Merge lang/foundations (9391695) into lang/editortools: no conflicts, selfhost/ identical to lang/foundations (the seeds unchanged; bootstrap-cfree fixpoint holds at 289532 lines), the help lists R10's --stdin-file, R8's syntax and R9's fmt / remove lines together
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
50333571d7
76 changed files with 96896 additions and 89729 deletions
12
docs/language/annotations/annot-alloc_ok.md
Normal file
12
docs/language/annotations/annot-alloc_ok.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-alloc_ok
|
||||
name: @alloc_ok
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @alloc_ok
|
||||
sig: @alloc_ok("why") function f() / @alloc_ok("why") statement
|
||||
tip: Allocates in a frame on purpose; the reason is required.
|
||||
order: 80
|
||||
---
|
||||
|
||||
<code>@alloc_ok("a memo miss, bounded by MM_CAP")</code> on a function or a single statement marks an allocation reachable from a frame as deliberate: the fence lets it through and <code>ludic deps</code> does not count it. The reason is required and greppable.
|
||||
18
docs/language/annotations/annot-appendonly.md
Normal file
18
docs/language/annotations/annot-appendonly.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
id: annot-appendonly
|
||||
name: @AppendOnly
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @AppendOnly @ByKey
|
||||
sig: @AppendOnly @ByKey registry Name of Record ...
|
||||
tip: How a registry's entries may change: only appended (their index is saved), or saved by key.
|
||||
order: 74
|
||||
---
|
||||
|
||||
<code>@AppendOnly</code> on a registry says an entry's index is stored somewhere that outlives the build, so entries are only ever appended; <code>@ByKey</code> says entries are saved by key, so their order is free. Editors keep to what each says.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — the file it names is beside the example
|
||||
@AppendOnly @ByKey
|
||||
registry Tools of Tool as TL from "data/tools.lres"
|
||||
```
|
||||
12
docs/language/annotations/annot-asset.md
Normal file
12
docs/language/annotations/annot-asset.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-asset
|
||||
name: @Asset
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Asset
|
||||
sig: @Asset("kind"[, map[, optional]]) field: string = ""
|
||||
tip: A path to a file of that kind; with map, under each map's directory.
|
||||
order: 67
|
||||
---
|
||||
|
||||
<code>@Asset("gltf")</code> says a string field names a file of that kind. With <code>map</code> the path is under each map's directory and <code>ludicc --check</code> looks for it in every map, refusing one that lacks it unless the field says <code>optional</code>.
|
||||
|
|
@ -3,7 +3,7 @@ id: annot-clearcolor
|
|||
name: "@ClearColor"
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: "@ClearColor"
|
||||
tokens: @ClearColor
|
||||
sig: "@ClearColor(0xRRGGBB) handler Draw phase Render { … }"
|
||||
tip: Declare a clear colour so the Render phase auto-clears + auto-presents for you.
|
||||
order: 62
|
||||
|
|
|
|||
12
docs/language/annotations/annot-color.md
Normal file
12
docs/language/annotations/annot-color.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-color
|
||||
name: @Color
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Color
|
||||
sig: @Color field: int = 0
|
||||
tip: The field is a colour.
|
||||
order: 68
|
||||
---
|
||||
|
||||
<code>@Color</code> marks an <code>int</code> field as a colour so an editor shows a swatch and a picker; with <code>@Tint(SLOT)</code> beside it the colour is for that tint slot.
|
||||
12
docs/language/annotations/annot-derived.md
Normal file
12
docs/language/annotations/annot-derived.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-derived
|
||||
name: @Derived
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Derived
|
||||
sig: @Derived field: float = 0.0
|
||||
tip: Worked out at boot: anything written in the data is overwritten.
|
||||
order: 71
|
||||
---
|
||||
|
||||
<code>@Derived</code> tells an editor a field is computed when the game starts, so a value typed into the data would be overwritten and is not offered for editing.
|
||||
12
docs/language/annotations/annot-deterministic.md
Normal file
12
docs/language/annotations/annot-deterministic.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-deterministic
|
||||
name: @deterministic
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @deterministic
|
||||
sig: @deterministic function f() { }
|
||||
tip: No floating point inside: it must replay the same everywhere.
|
||||
order: 79
|
||||
---
|
||||
|
||||
<code>@deterministic</code> on a function or a handler refuses floating point inside it, so it computes the same bits on every machine - what lockstep and replays need.
|
||||
12
docs/language/annotations/annot-frame.md
Normal file
12
docs/language/annotations/annot-frame.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-frame
|
||||
name: @frame
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @frame
|
||||
sig: @frame field: fn() -> void
|
||||
tip: A fn stored here runs every frame.
|
||||
order: 76
|
||||
---
|
||||
|
||||
<code>@frame</code> on a function-typed field says whatever function it holds runs every frame, so <code>ludic deps</code> counts what it can allocate among the frame's allocations.
|
||||
12
docs/language/annotations/annot-key.md
Normal file
12
docs/language/annotations/annot-key.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-key
|
||||
name: @Key
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Key
|
||||
sig: @Key field: int = 0
|
||||
tip: The field is a key code.
|
||||
order: 73
|
||||
---
|
||||
|
||||
<code>@Key</code> marks an <code>int</code> field as a key code, so an editor offers a key to press rather than a number.
|
||||
18
docs/language/annotations/annot-max.md
Normal file
18
docs/language/annotations/annot-max.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
id: annot-max
|
||||
name: @max
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @max
|
||||
sig: @max(64) rows: []Row
|
||||
tip: The most a list may hold; growing past it fails the run.
|
||||
order: 77
|
||||
---
|
||||
|
||||
<code>@max(n)</code> bounds a list field: it never holds more than <code>n</code>, and a push past it fails the run (exit 87) rather than growing a list that should not grow.
|
||||
|
||||
```ludic
|
||||
property Log {
|
||||
@max(64) lines: []int
|
||||
}
|
||||
```
|
||||
12
docs/language/annotations/annot-node.md
Normal file
12
docs/language/annotations/annot-node.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-node
|
||||
name: @Node
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Node @Clip @Material
|
||||
sig: @Node(model) / @Clip(model) / @Material(model) field: string = ""
|
||||
tip: A node, an animation clip or a material inside the glTF another field names.
|
||||
order: 69
|
||||
---
|
||||
|
||||
<code>@Node(model)</code>, <code>@Clip(model)</code> and <code>@Material(model)</code> say a string field names a node, a clip or a material inside the glTF that field <code>model</code> of the same record names (an <code>@Asset("gltf")</code> field, or an <code>@Ref</code> to a registry whose record has exactly one). Naming anything else is an error.
|
||||
12
docs/language/annotations/annot-oneof.md
Normal file
12
docs/language/annotations/annot-oneof.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-oneof
|
||||
name: @OneOf
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @OneOf
|
||||
sig: @OneOf(PREFIX_) / @OneOf(A, B) / @OneOf("word", ...)
|
||||
tip: The field holds one of these constants or words.
|
||||
order: 64
|
||||
---
|
||||
|
||||
<code>@OneOf(GR_)</code> says a field holds one of the constants whose names start <code>GR_</code>; <code>@OneOf(GR_GOLD, GR_SILVER)</code> one of those (each must exist); on a <code>string</code> field, <code>@OneOf("box", "hull")</code> one of those words, and every registry row is checked against them.
|
||||
12
docs/language/annotations/annot-owns.md
Normal file
12
docs/language/annotations/annot-owns.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-owns
|
||||
name: @owns
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @owns @creates @releases
|
||||
sig: @owns(Kind) field / @creates(Kind) function / @releases(Kind) function
|
||||
tip: Resource handles: a field that owns one, a function that makes one, a function that lets one go.
|
||||
order: 78
|
||||
---
|
||||
|
||||
<code>@creates(PhysShape)</code> on a function says it returns a handle the caller must release, <code>@releases(PhysShape)</code> that it releases one, and <code>@owns(PhysShape)</code> on a field that its record owns the handle it holds. <code>ludic deps --resources</code> counts a created handle never released, and an owned one lost when its record is released.
|
||||
12
docs/language/annotations/annot-permap.md
Normal file
12
docs/language/annotations/annot-permap.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-permap
|
||||
name: @PerMap
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @PerMap @Chunked
|
||||
sig: @PerMap registry Name of Row from "rows.lres" / @PerMap @Chunked(n) registry ...
|
||||
tip: A table whose rows are read per map from that map's directory; @Chunked(n) reads it a chunk at a time.
|
||||
order: 75
|
||||
---
|
||||
|
||||
A <code>@PerMap</code> registry holds what is on a map rather than in the game: its rows are read when a map loads, from that map's directory, into a state the compiler writes with its verbs (<code>_load</code>, <code>_find</code>, ...). <code>@Chunked(n)</code> reads it in n-by-n-metre chunks from files named by <code>{cx}</code> and <code>{cz}</code>.
|
||||
12
docs/language/annotations/annot-range.md
Normal file
12
docs/language/annotations/annot-range.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-range
|
||||
name: @Range
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Range
|
||||
sig: @Range(lo, hi) field: float = 0.0
|
||||
tip: The least and the most the value may be.
|
||||
order: 65
|
||||
---
|
||||
|
||||
<code>@Range(0, 20.5)</code> gives an editor the bounds of a number field, and <code>ludicc --check</code> holds every map row's value to them. Both arguments are numbers.
|
||||
19
docs/language/annotations/annot-ref.md
Normal file
19
docs/language/annotations/annot-ref.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: annot-ref
|
||||
name: @Ref
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Ref
|
||||
sig: @Ref(Registry) field: int = 0
|
||||
tip: The field is an entry of that registry: an index, or a key for a @PerMap one.
|
||||
order: 63
|
||||
---
|
||||
|
||||
<code>@Ref(Items)</code> on a field says its value is an entry of the registry <code>Items</code> - its index on an <code>int</code> field, its key on a <code>string</code> field of a <code>@PerMap</code> table - so an editor offers the entries. It changes nothing the program does; a registry the program does not declare is a warning and <code>"unresolved"</code> in the schema, anything else of that name is an error.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — the registry is declared elsewhere
|
||||
property Tool {
|
||||
@Ref(Vendors) seller: int = 0
|
||||
}
|
||||
```
|
||||
12
docs/language/annotations/annot-text.md
Normal file
12
docs/language/annotations/annot-text.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-text
|
||||
name: @Text
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Text @Multiline
|
||||
sig: @Text name: Key = null / @Text @Multiline blurb: string = ""
|
||||
tip: Read by the player, so translated; and prose, edited over several lines.
|
||||
order: 72
|
||||
---
|
||||
|
||||
<code>@Text</code> marks what the player reads, so it is translated. On a <code>Key</code> field a row that gives no value gets its derived key, <code><registry>.<row>.<field></code> (see <code>@TextKey</code>); English still written in a <code>@Text</code> row is counted as <code>english_left</code>. <code>@Multiline</code> says it is prose, edited as several lines rather than one.
|
||||
12
docs/language/annotations/annot-textkey.md
Normal file
12
docs/language/annotations/annot-textkey.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-textkey
|
||||
name: @TextKey
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @TextKey
|
||||
sig: @TextKey("steps") registry StepKinds of StepKind from "steps.lres"
|
||||
tip: The prefix of a registry's derived text keys, in place of its name in snake case.
|
||||
order: 73
|
||||
---
|
||||
|
||||
A registry's <code>@Text</code> fields are keyed <code><registry>.<row>.<field></code>, the registry's name in snake case (<code>GearKinds</code> is <code>gear_kinds</code>). <code>@TextKey("steps")</code> names the prefix instead, so its rows' keys are <code>steps.<row>.<field></code>. Every derived key is checked against the source language's <code>.po</code> (LANGUAGE.md, Text keys).
|
||||
12
docs/language/annotations/annot-tint.md
Normal file
12
docs/language/annotations/annot-tint.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-tint
|
||||
name: @Tint
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Tint
|
||||
sig: @Color @Tint(SLOT) field: int = 0
|
||||
tip: A colour for that tint slot.
|
||||
order: 70
|
||||
---
|
||||
|
||||
<code>@Tint(TSLOT_HAIR)</code> names the tint slot a colour field paints - a constant, which must exist (or, when the program lacks it, is a warning and unresolved).
|
||||
12
docs/language/annotations/annot-unit.md
Normal file
12
docs/language/annotations/annot-unit.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: annot-unit
|
||||
name: @Unit
|
||||
category: annotations
|
||||
kind: annotation
|
||||
tokens: @Unit
|
||||
sig: @Unit("m/s") field: float = 0.0
|
||||
tip: The value's unit, in one canonical ASCII spelling.
|
||||
order: 66
|
||||
---
|
||||
|
||||
<code>@Unit</code> takes one of <code>m</code>, <code>m/s</code>, <code>m/s2</code>, <code>s</code>, <code>min</code>, <code>h</code>, <code>d</code>, <code>deg</code>, <code>rad</code>, <code>rad/s</code>, <code>kg</code>, <code>N</code>, <code>N.m</code>, <code>%</code>, <code>px</code>, so one word means one unit to an editor; another spelling is a warning naming the canonical one.
|
||||
17
docs/language/control/kw-dispatch.md
Normal file
17
docs/language/control/kw-dispatch.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
id: kw-dispatch
|
||||
name: dispatch
|
||||
category: control
|
||||
kind: keyword
|
||||
tokens: dispatch
|
||||
sig: dispatch Action(field: value, ...)
|
||||
tip: Queues an action for the reducers declared on it.
|
||||
order: 13
|
||||
---
|
||||
|
||||
<code>dispatch Move(dx: 1)</code> queues an action; at the end of the frame phase every <code>reducer</code> on <code>Move</code> applies it to its own state, in the order of the states' names. Input code and a screen's buttons dispatch; they never write another module's state.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — the action is declared elsewhere
|
||||
dispatch Move(dx: 1)
|
||||
```
|
||||
12
docs/language/ecs/kw-system.md
Normal file
12
docs/language/ecs/kw-system.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-system
|
||||
name: system
|
||||
category: ecs
|
||||
kind: keyword
|
||||
tokens: system
|
||||
sig: disable system SystemFunction
|
||||
tip: Leaves an engine system out at compile time.
|
||||
order: 51
|
||||
---
|
||||
|
||||
<code>disable system S</code> names a package's engine system (declared with <code>@EngineSystem</code>) and leaves it out of the program at compile time, for a game that drives that component itself.
|
||||
12
docs/language/operators/kw-true.md
Normal file
12
docs/language/operators/kw-true.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-true
|
||||
name: true
|
||||
category: operators
|
||||
kind: keyword
|
||||
tokens: true false null
|
||||
sig: true false null
|
||||
tip: The boolean literals, and the absent record, slice or string.
|
||||
order: 13
|
||||
---
|
||||
|
||||
<code>true</code> and <code>false</code> are the two <code>bool</code> values; <code>null</code> is no record, no slice and no string - what an unset reference field holds and what a lookup that finds nothing returns.
|
||||
12
docs/language/scenes/kw-lasts.md
Normal file
12
docs/language/scenes/kw-lasts.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-lasts
|
||||
name: lasts
|
||||
category: scenes
|
||||
kind: keyword
|
||||
tokens: lasts
|
||||
sig: scene Name lasts N then Next { }
|
||||
tip: A timed scene: after N seconds it moves on.
|
||||
order: 52
|
||||
---
|
||||
|
||||
<code>lasts N then Next</code> makes a scene timed - a banner, a splash - that moves to <code>Next</code> by itself once <code>N</code> seconds have passed.
|
||||
12
docs/language/scenes/kw-loads.md
Normal file
12
docs/language/scenes/kw-loads.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-loads
|
||||
name: loads
|
||||
category: scenes
|
||||
kind: keyword
|
||||
tokens: loads
|
||||
sig: scene Name loads then Next { }
|
||||
tip: A loading scene: pumps the asset queue with a bar, then moves on.
|
||||
order: 54
|
||||
---
|
||||
|
||||
<code>loads then Next</code> makes a scene the loading screen: the engine pumps the asset queue, draws a progress bar and enters <code>Next</code> when everything queued is ready.
|
||||
12
docs/language/scenes/kw-shows.md
Normal file
12
docs/language/scenes/kw-shows.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-shows
|
||||
name: shows
|
||||
category: scenes
|
||||
kind: keyword
|
||||
tokens: shows
|
||||
sig: scene Name shows Menu { }
|
||||
tip: The ui this scene opens, renders and closes by itself.
|
||||
order: 51
|
||||
---
|
||||
|
||||
<code>shows Menu</code> in a scene's header hands a <code>ui</code> to the engine: it is opened when the scene is entered, drawn every frame and closed when it is left, so the scene needs no handler for it.
|
||||
12
docs/language/scenes/kw-then.md
Normal file
12
docs/language/scenes/kw-then.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-then
|
||||
name: then
|
||||
category: scenes
|
||||
kind: keyword
|
||||
tokens: then
|
||||
sig: scene Name lasts N then Next / scene Name loads then Next
|
||||
tip: The scene a timed or loading scene moves on to.
|
||||
order: 53
|
||||
---
|
||||
|
||||
<code>then</code> names where a scene goes when it is done: after the time of a <code>lasts</code> scene, or once a <code>loads</code> scene's assets are ready.
|
||||
16
docs/language/structure/kw-action.md
Normal file
16
docs/language/structure/kw-action.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
id: kw-action
|
||||
name: action
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: action
|
||||
sig: action Name { field: Type, ... }
|
||||
tip: What the player asked for, dispatched and applied by each state's reducer.
|
||||
order: 61
|
||||
---
|
||||
|
||||
An <code>action</code> is a record that says what was asked for in the game's words (<code>Move</code>, <code>OpenPack</code>). Input code <code>dispatch</code>es it, and every <code>reducer</code> declared on it changes its own state. The queue drains at the end of every frame phase, each action's reducers in the order of their states' names.
|
||||
|
||||
```ludic
|
||||
action Move { dx: int = 0, dy: int = 0 }
|
||||
```
|
||||
19
docs/language/structure/kw-alias.md
Normal file
19
docs/language/structure/kw-alias.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: kw-alias
|
||||
name: alias
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: alias
|
||||
sig: namespace Name { alias method = function_name }
|
||||
tip: A namespace method that is another function.
|
||||
order: 69
|
||||
---
|
||||
|
||||
Inside a <code>namespace</code> block, <code>alias m = f</code> makes <code>Name.m(...)</code> a call of <code>f</code>, checked against its parameters. It is how an engine namespace declared in Ludic forwards to the functions that implement it.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — the aliased function lives elsewhere
|
||||
namespace Trail {
|
||||
alias length = trail_length
|
||||
}
|
||||
```
|
||||
12
docs/language/structure/kw-as.md
Normal file
12
docs/language/structure/kw-as.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-as
|
||||
name: as
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: as
|
||||
sig: registry Name of Record as PREFIX
|
||||
tip: The prefix of a registry's generated constants.
|
||||
order: 65
|
||||
---
|
||||
|
||||
<code>as P</code> after a registry's record makes the compiler write a constant for each entry, <code>P_KEY</code> in upper case, holding the entry's index - so code reads <code>IT_ROPE</code> rather than looking the key up.
|
||||
17
docs/language/structure/kw-bind.md
Normal file
17
docs/language/structure/kw-bind.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
id: kw-bind
|
||||
name: bind
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: bind
|
||||
sig: bind Port { member: function_name, ... }
|
||||
tip: The program's answers to a port: one function per member.
|
||||
order: 60
|
||||
---
|
||||
|
||||
<code>bind</code> answers a <code>port</code>: each member names a function of the member's type. It is written once, where the program is put together, and it is the only code that knows both the asking module and the one that answers.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — a module spans files
|
||||
bind ClockWorld { rest_scale: party_rest_scale }
|
||||
```
|
||||
21
docs/language/structure/kw-component.md
Normal file
21
docs/language/structure/kw-component.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: kw-component
|
||||
name: component
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: component
|
||||
sig: component Name[(states)] { prop p: T = v, state s: T = v, function ..., on e(...) { } }
|
||||
tip: A UI component: its props, state, functions and events, beside Name.xml and Name.lss.
|
||||
order: 70
|
||||
---
|
||||
|
||||
A <code>component</code> is a piece of interface: the code file declares its <code>prop</code>s (handed in by its parent), its own <code>state</code>, the functions its template calls and the events it handles with <code>on</code>; <code>Name.xml</code> beside it is its template and <code>Name.lss</code> its styles. The states named in its header are supplied by the runtime and never seen by the template.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — a component needs its template beside it
|
||||
component Counter {
|
||||
prop step: int = 1
|
||||
state count: int = 0
|
||||
on add() { count = count + step }
|
||||
}
|
||||
```
|
||||
17
docs/language/structure/kw-def.md
Normal file
17
docs/language/structure/kw-def.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
id: kw-def
|
||||
name: def
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: def
|
||||
sig: def Registry key { field: value, ... } / def Registry from "file.lres"
|
||||
tip: Entries of a registry, written in code or read from a resource file.
|
||||
order: 67
|
||||
---
|
||||
|
||||
<code>def</code> adds entries to a registry: one written inline, or every entry of a resource file. An <code>open</code> registry takes <code>def</code>s from other modules, which is how a package's table gets a game's rows.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — the registry is declared elsewhere
|
||||
def Tools lantern { weight: 1.5 }
|
||||
```
|
||||
17
docs/language/structure/kw-export.md
Normal file
17
docs/language/structure/kw-export.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
id: kw-export
|
||||
name: export
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: export
|
||||
sig: export function / property / state / registry ...
|
||||
tip: Makes a declaration visible outside its module.
|
||||
order: 54
|
||||
---
|
||||
|
||||
<code>export</code> in front of a declaration makes it reachable from other modules; without it a module's names are its own. It works on every declaration - functions, records, states, events, actions, ports, registries, views and components - and <code>@export</code> is the same thing written as an attribute. Export deliberately: a name nobody else asks for stays private.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — a module spans files
|
||||
export function balance(b: Bank) -> int { return b.total }
|
||||
```
|
||||
17
docs/language/structure/kw-friend.md
Normal file
17
docs/language/structure/kw-friend.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
id: kw-friend
|
||||
name: friend
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: friend
|
||||
sig: friend module name [of a, b]
|
||||
tip: A module that sees other modules' private names - the lab, the tests.
|
||||
order: 53
|
||||
---
|
||||
|
||||
<code>friend module lab</code> declares a module that may name every private name of every module; <code>friend module lab of bank, sky</code> limits it to those. It is for test and staging code that must reach inside a system without the system exporting its internals.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — a module spans files
|
||||
friend module lab of bank, sky
|
||||
```
|
||||
12
docs/language/structure/kw-from.md
Normal file
12
docs/language/structure/kw-from.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-from
|
||||
name: from
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: from
|
||||
sig: registry ... from "file.lres" / def Registry from "file.lres"
|
||||
tip: The resource file a registry's entries are read from.
|
||||
order: 66
|
||||
---
|
||||
|
||||
<code>from</code> names the <code>.lres</code> file the compiler reads a registry's entries from, relative to the declaring file (or, in a <code>@PerMap</code> registry, to each map's directory). The entries are checked against the record at build time, and an entry's place in the file is its index.
|
||||
19
docs/language/structure/kw-internal.md
Normal file
19
docs/language/structure/kw-internal.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: kw-internal
|
||||
name: internal
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: internal
|
||||
sig: namespace Name { internal function helper() { } }
|
||||
tip: Inside a namespace: a function kept out of the Name.* surface.
|
||||
order: 55
|
||||
---
|
||||
|
||||
In a <code>namespace</code> block every function is part of the <code>Name.*</code> surface unless it says <code>internal</code>: then it is emitted as an ordinary helper the namespace's own methods can call, and <code>Name.helper</code> is not a method.
|
||||
|
||||
```ludic
|
||||
namespace Trail {
|
||||
internal function step(n: int) -> int { return n + 1 }
|
||||
function next(n: int) -> int { return step(n) }
|
||||
}
|
||||
```
|
||||
19
docs/language/structure/kw-module.md
Normal file
19
docs/language/structure/kw-module.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: kw-module
|
||||
name: module
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: module
|
||||
sig: module name [in layer L] [uses a, b]
|
||||
tip: Names the module a directory's files belong to; only what it exports is reachable from outside.
|
||||
order: 51
|
||||
---
|
||||
|
||||
<code>module</code> opens a directory's barrel (<code>index.ludic</code>) and says which module every file under it belongs to. A name a module does not <code>export</code> is private to it: another module that names it is refused at compile time, which is what makes a private name safe to change. The line may place the module in a layer (<code>in layer L</code>) and say what it may reach (<code>uses</code>).
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — a module spans files
|
||||
# bank/index.ludic
|
||||
module bank uses base
|
||||
import "ledger.ludic"
|
||||
```
|
||||
17
docs/language/structure/kw-mut.md
Normal file
17
docs/language/structure/kw-mut.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
id: kw-mut
|
||||
name: mut
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: mut
|
||||
sig: function f(st: mut State)
|
||||
tip: A parameter the function may change - how a state is written.
|
||||
order: 58
|
||||
---
|
||||
|
||||
A state reaches a function only as a parameter: <code>h: HikerState</code> to read it, <code>h: mut HikerState</code> to change it, so a function's signature is everything it touches. The compiler refuses a write through a parameter that is not <code>mut</code>, and <code>ludic migrate state --tighten</code> takes <code>mut</code> off every one nothing down the chain writes.
|
||||
|
||||
```ludic
|
||||
state Counter { n: int = 0 }
|
||||
function bump(c: mut Counter) -> void { c.n = c.n + 1 }
|
||||
```
|
||||
18
docs/language/structure/kw-numbers.md
Normal file
18
docs/language/structure/kw-numbers.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
id: kw-numbers
|
||||
name: numbers
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: numbers
|
||||
sig: numbers float
|
||||
tip: This file's bare decimals are floats.
|
||||
order: 56
|
||||
---
|
||||
|
||||
<code>numbers float</code> at the top of a file makes a bare decimal such as <code>1.5</code> a <code>float</code> rather than a <code>fixed</code>. Such a file also refuses to promote a computed <code>int</code> silently: write <code>float(n)</code> where a count becomes a number.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — a file-level line
|
||||
numbers float
|
||||
const GRAVITY: float = 9.81
|
||||
```
|
||||
12
docs/language/structure/kw-of.md
Normal file
12
docs/language/structure/kw-of.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-of
|
||||
name: of
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: of
|
||||
sig: registry Name of Record / friend module m of a, b
|
||||
tip: Says what a registry holds, or whose private names a friend module sees.
|
||||
order: 64
|
||||
---
|
||||
|
||||
<code>of</code> names the record a <code>registry</code>'s entries are (<code>registry Tools of Tool</code>), and, on a <code>friend module</code> line, the modules whose private names it may see.
|
||||
12
docs/language/structure/kw-open.md
Normal file
12
docs/language/structure/kw-open.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-open
|
||||
name: open
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: open
|
||||
sig: open registry Name of Record
|
||||
tip: A registry other modules may add entries to with def.
|
||||
order: 68
|
||||
---
|
||||
|
||||
An <code>open registry</code> is one whose entries may come from other modules' <code>def</code>s, merged in a deterministic order; a closed one takes entries only from its own module. A package declares its table open so the game can fill it.
|
||||
19
docs/language/structure/kw-port.md
Normal file
19
docs/language/structure/kw-port.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: kw-port
|
||||
name: port
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: port
|
||||
sig: port Name { member: fn(T) -> R [= default], ... }
|
||||
tip: The questions a module asks the world, bound once by the program.
|
||||
order: 59
|
||||
---
|
||||
|
||||
A <code>port</code> is what a module needs to ask the world, as named function members in primitive types. The program answers it once with <code>bind</code>; a member left unbound without a default is a compile error. Ports let a package ask a question without reaching into the module that knows the answer.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — a module spans files
|
||||
export port ClockWorld {
|
||||
rest_scale: fn() -> float
|
||||
}
|
||||
```
|
||||
12
docs/language/structure/kw-prop.md
Normal file
12
docs/language/structure/kw-prop.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
id: kw-prop
|
||||
name: prop
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: prop
|
||||
sig: component Name { prop name: Type = default }
|
||||
tip: A component's value handed in by its parent.
|
||||
order: 71
|
||||
---
|
||||
|
||||
<code>prop</code> declares a component member its parent sets from the template (<code><Counter step="2"/></code>); <code>state</code> declares one the component keeps for itself. Both are fields of the instance, listed with their types and defaults in <code>ludic schema</code>.
|
||||
20
docs/language/structure/kw-reducer.md
Normal file
20
docs/language/structure/kw-reducer.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: kw-reducer
|
||||
name: reducer
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: reducer
|
||||
sig: reducer State on Action(st: mut State, reads..., a: Action) { ... }
|
||||
tip: Applies an action to one state; it may read others.
|
||||
order: 62
|
||||
---
|
||||
|
||||
A <code>reducer</code> writes exactly one state when its action is dispatched: the first parameter is that state, <code>mut</code>, the last is the action, and any states between are read-only. A change that must touch several states in a set order is a chain: a reducer dispatches the next action.
|
||||
|
||||
```ludic
|
||||
state Pos { x: int = 0 }
|
||||
action Move { dx: int = 0 }
|
||||
reducer Pos on Move(p: mut Pos, a: Move) {
|
||||
p.x = p.x + a.dx
|
||||
}
|
||||
```
|
||||
18
docs/language/structure/kw-registry.md
Normal file
18
docs/language/structure/kw-registry.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
id: kw-registry
|
||||
name: registry
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: registry
|
||||
sig: [open] registry Name of Record [as PREFIX] [from "file.lres"]
|
||||
tip: A table of named entries of one record type, with a constant per entry.
|
||||
order: 63
|
||||
---
|
||||
|
||||
A <code>registry</code> is a table of named entries of one record: its entries come from <code>def</code> declarations or a resource file named by <code>from</code>, and <code>as P</code> generates a constant <code>P_KEY</code> per entry holding its index. Editors read it through <code>ludic schema</code>, and attributes such as <code>@AppendOnly</code> and <code>@PerMap</code> say how its entries may change and where they live.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — the file it names is beside the example
|
||||
property Tool { key: string = "", weight: float = 0.0 }
|
||||
registry Tools of Tool as TL from "data/tools.lres"
|
||||
```
|
||||
17
docs/language/structure/kw-unsafe.md
Normal file
17
docs/language/structure/kw-unsafe.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
id: kw-unsafe
|
||||
name: unsafe
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: unsafe
|
||||
sig: unsafe function f() { } / unsafe { ... }
|
||||
tip: Raw memory is allowed inside: bytes(), free, Memory.*, indexing a pointer, calling C.
|
||||
order: 57
|
||||
---
|
||||
|
||||
Ludic's memory is safe unless code says <code>unsafe</code>: raw allocation, freeing, pointer indexing and foreign calls are compile errors elsewhere. An <code>unsafe function</code> or an <code>unsafe { ... }</code> block allows them inside, and only packages and the runtime are trusted to write it; a program's own files need <code>--unsafe</code>.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — needs --unsafe
|
||||
unsafe function raw(n: int) -> pointer { return bytes(n) }
|
||||
```
|
||||
17
docs/language/structure/kw-uses.md
Normal file
17
docs/language/structure/kw-uses.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
id: kw-uses
|
||||
name: uses
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: uses
|
||||
sig: module name uses a, b, c
|
||||
tip: The modules a module may reach; a reach not on the line is a compile error that names the fix.
|
||||
order: 52
|
||||
---
|
||||
|
||||
<code>uses</code> ends a <code>module</code> line with the modules its files may name. The compiler holds the module to that list and refuses a list that goes round: when adding a module would make a cycle, the question goes through a <code>port</code> the asker declares and the program binds instead.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — a module spans files
|
||||
module sky uses base, events, clock
|
||||
```
|
||||
20
docs/language/structure/kw-view.md
Normal file
20
docs/language/structure/kw-view.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
id: kw-view
|
||||
name: view
|
||||
category: structure
|
||||
kind: keyword
|
||||
tokens: view
|
||||
sig: view Name[(states)] { field = expr, function q(...), on e(...) { } }
|
||||
tip: The bridge to a template: what it may read and what it may do.
|
||||
order: 72
|
||||
---
|
||||
|
||||
A <code>view</code> says what a template may read (its fields, each an expression) and what it may do (its queries and its <code>on</code> events), and nothing else crosses. The compiler writes <code>view_name()</code>, whose model is a value object of every field and whose call runs a query or an event by name.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — the functions it names are elsewhere
|
||||
view Yard {
|
||||
spots: int = yard_spots()
|
||||
on buy(hf: int) { yard_buy(hf) }
|
||||
}
|
||||
```
|
||||
19
docs/language/types/type-key.md
Normal file
19
docs/language/types/type-key.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
id: type-key
|
||||
name: Key
|
||||
category: types
|
||||
kind: type
|
||||
tokens: Key
|
||||
sig: k"module.purpose" / kn"module.purpose"
|
||||
tip: A text key: it names a text and is not one - tr(key) makes the text.
|
||||
order: 4
|
||||
---
|
||||
|
||||
A `Key` names a text the player reads; its English lives in the source language's `.po`, like every other language's text. It is written `k"module.purpose"`, or `kn"..."` for a text with plural forms, against its quote. A `Key` is not a `string` and a `string` is not a `Key`: text is made with `tr(key)`, `trf(key, ...)` or, for a plural, `trn(kn"...", n, ...)`, and a key in a template literal's hole is an error. Keys compare with `==`, and with a `lang` line in `package.ludic` the compiler checks every one against the `.po`.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — tr / trf / trn are ludic.i18n's
|
||||
let title = tr(k"pause.resume")
|
||||
let day = trf(k"hud.day", txt_num(n))
|
||||
if key == k"pause.resume" { Screen.status(title) }
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue