From e2528c1b10fdd189da21d75714f0aa90c50d99f9 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Tue, 29 Sep 2026 23:59:47 +0300 Subject: [PATCH] docs/language: a page for every keyword and attribute the vocabulary has Keywords: module uses friend export internal numbers unsafe mut port bind action reducer registry of as from def open alias component prop view (structure), shows lasts then loads (scenes), system (ecs), dispatch (control), true false null (operators). Attributes: @Ref @OneOf @Range @Unit @Asset @Color, @Node / @Clip / @Material, @Tint @Derived, @Text / @Multiline, @Key, @AppendOnly / @ByKey, @PerMap / @Chunked, @frame @max, @owns / @creates / @releases, @deterministic @alloc_ok. Each is in tools/docgen/inventory.json; every fence that is not marked skip parses (ludicc --fmt). annot-clearcolor's token loses its quotes, which no reader strips. Co-Authored-By: Claude Opus 5.5 --- docs/language/annotations/annot-alloc_ok.md | 12 ++++ docs/language/annotations/annot-appendonly.md | 18 ++++++ docs/language/annotations/annot-asset.md | 12 ++++ docs/language/annotations/annot-clearcolor.md | 2 +- docs/language/annotations/annot-color.md | 12 ++++ docs/language/annotations/annot-derived.md | 12 ++++ .../annotations/annot-deterministic.md | 12 ++++ docs/language/annotations/annot-frame.md | 12 ++++ docs/language/annotations/annot-key.md | 12 ++++ docs/language/annotations/annot-max.md | 18 ++++++ docs/language/annotations/annot-node.md | 12 ++++ docs/language/annotations/annot-oneof.md | 12 ++++ docs/language/annotations/annot-owns.md | 12 ++++ docs/language/annotations/annot-permap.md | 12 ++++ docs/language/annotations/annot-range.md | 12 ++++ docs/language/annotations/annot-ref.md | 19 ++++++ docs/language/annotations/annot-text.md | 12 ++++ docs/language/annotations/annot-tint.md | 12 ++++ docs/language/annotations/annot-unit.md | 12 ++++ docs/language/control/kw-dispatch.md | 17 ++++++ docs/language/ecs/kw-system.md | 12 ++++ docs/language/operators/kw-true.md | 12 ++++ docs/language/scenes/kw-lasts.md | 12 ++++ docs/language/scenes/kw-loads.md | 12 ++++ docs/language/scenes/kw-shows.md | 12 ++++ docs/language/scenes/kw-then.md | 12 ++++ docs/language/structure/kw-action.md | 16 +++++ docs/language/structure/kw-alias.md | 19 ++++++ docs/language/structure/kw-as.md | 12 ++++ docs/language/structure/kw-bind.md | 17 ++++++ docs/language/structure/kw-component.md | 21 +++++++ docs/language/structure/kw-def.md | 17 ++++++ docs/language/structure/kw-export.md | 17 ++++++ docs/language/structure/kw-friend.md | 17 ++++++ docs/language/structure/kw-from.md | 12 ++++ docs/language/structure/kw-internal.md | 19 ++++++ docs/language/structure/kw-module.md | 19 ++++++ docs/language/structure/kw-mut.md | 17 ++++++ docs/language/structure/kw-numbers.md | 18 ++++++ docs/language/structure/kw-of.md | 12 ++++ docs/language/structure/kw-open.md | 12 ++++ docs/language/structure/kw-port.md | 19 ++++++ docs/language/structure/kw-prop.md | 12 ++++ docs/language/structure/kw-reducer.md | 20 +++++++ docs/language/structure/kw-registry.md | 18 ++++++ docs/language/structure/kw-unsafe.md | 17 ++++++ docs/language/structure/kw-uses.md | 17 ++++++ docs/language/structure/kw-view.md | 20 +++++++ tools/docgen/inventory.json | 59 +++++++++++++++++-- 49 files changed, 746 insertions(+), 7 deletions(-) create mode 100644 docs/language/annotations/annot-alloc_ok.md create mode 100644 docs/language/annotations/annot-appendonly.md create mode 100644 docs/language/annotations/annot-asset.md create mode 100644 docs/language/annotations/annot-color.md create mode 100644 docs/language/annotations/annot-derived.md create mode 100644 docs/language/annotations/annot-deterministic.md create mode 100644 docs/language/annotations/annot-frame.md create mode 100644 docs/language/annotations/annot-key.md create mode 100644 docs/language/annotations/annot-max.md create mode 100644 docs/language/annotations/annot-node.md create mode 100644 docs/language/annotations/annot-oneof.md create mode 100644 docs/language/annotations/annot-owns.md create mode 100644 docs/language/annotations/annot-permap.md create mode 100644 docs/language/annotations/annot-range.md create mode 100644 docs/language/annotations/annot-ref.md create mode 100644 docs/language/annotations/annot-text.md create mode 100644 docs/language/annotations/annot-tint.md create mode 100644 docs/language/annotations/annot-unit.md create mode 100644 docs/language/control/kw-dispatch.md create mode 100644 docs/language/ecs/kw-system.md create mode 100644 docs/language/operators/kw-true.md create mode 100644 docs/language/scenes/kw-lasts.md create mode 100644 docs/language/scenes/kw-loads.md create mode 100644 docs/language/scenes/kw-shows.md create mode 100644 docs/language/scenes/kw-then.md create mode 100644 docs/language/structure/kw-action.md create mode 100644 docs/language/structure/kw-alias.md create mode 100644 docs/language/structure/kw-as.md create mode 100644 docs/language/structure/kw-bind.md create mode 100644 docs/language/structure/kw-component.md create mode 100644 docs/language/structure/kw-def.md create mode 100644 docs/language/structure/kw-export.md create mode 100644 docs/language/structure/kw-friend.md create mode 100644 docs/language/structure/kw-from.md create mode 100644 docs/language/structure/kw-internal.md create mode 100644 docs/language/structure/kw-module.md create mode 100644 docs/language/structure/kw-mut.md create mode 100644 docs/language/structure/kw-numbers.md create mode 100644 docs/language/structure/kw-of.md create mode 100644 docs/language/structure/kw-open.md create mode 100644 docs/language/structure/kw-port.md create mode 100644 docs/language/structure/kw-prop.md create mode 100644 docs/language/structure/kw-reducer.md create mode 100644 docs/language/structure/kw-registry.md create mode 100644 docs/language/structure/kw-unsafe.md create mode 100644 docs/language/structure/kw-uses.md create mode 100644 docs/language/structure/kw-view.md diff --git a/docs/language/annotations/annot-alloc_ok.md b/docs/language/annotations/annot-alloc_ok.md new file mode 100644 index 00000000..f6e3e4de --- /dev/null +++ b/docs/language/annotations/annot-alloc_ok.md @@ -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 +--- + +@alloc_ok("a memo miss, bounded by MM_CAP") on a function or a single statement marks an allocation reachable from a frame as deliberate: the fence lets it through and ludic deps does not count it. The reason is required and greppable. diff --git a/docs/language/annotations/annot-appendonly.md b/docs/language/annotations/annot-appendonly.md new file mode 100644 index 00000000..d3ef03b3 --- /dev/null +++ b/docs/language/annotations/annot-appendonly.md @@ -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 +--- + +@AppendOnly on a registry says an entry's index is stored somewhere that outlives the build, so entries are only ever appended; @ByKey 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" +``` diff --git a/docs/language/annotations/annot-asset.md b/docs/language/annotations/annot-asset.md new file mode 100644 index 00000000..14ed7fa9 --- /dev/null +++ b/docs/language/annotations/annot-asset.md @@ -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 +--- + +@Asset("gltf") says a string field names a file of that kind. With map the path is under each map's directory and ludicc --check looks for it in every map, refusing one that lacks it unless the field says optional. diff --git a/docs/language/annotations/annot-clearcolor.md b/docs/language/annotations/annot-clearcolor.md index 1dc2e451..fe2a0785 100644 --- a/docs/language/annotations/annot-clearcolor.md +++ b/docs/language/annotations/annot-clearcolor.md @@ -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 diff --git a/docs/language/annotations/annot-color.md b/docs/language/annotations/annot-color.md new file mode 100644 index 00000000..3552b2ab --- /dev/null +++ b/docs/language/annotations/annot-color.md @@ -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 +--- + +@Color marks an int field as a colour so an editor shows a swatch and a picker; with @Tint(SLOT) beside it the colour is for that tint slot. diff --git a/docs/language/annotations/annot-derived.md b/docs/language/annotations/annot-derived.md new file mode 100644 index 00000000..fb5422c6 --- /dev/null +++ b/docs/language/annotations/annot-derived.md @@ -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 +--- + +@Derived 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. diff --git a/docs/language/annotations/annot-deterministic.md b/docs/language/annotations/annot-deterministic.md new file mode 100644 index 00000000..2b235a72 --- /dev/null +++ b/docs/language/annotations/annot-deterministic.md @@ -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 +--- + +@deterministic 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. diff --git a/docs/language/annotations/annot-frame.md b/docs/language/annotations/annot-frame.md new file mode 100644 index 00000000..6d312d00 --- /dev/null +++ b/docs/language/annotations/annot-frame.md @@ -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 +--- + +@frame on a function-typed field says whatever function it holds runs every frame, so ludic deps counts what it can allocate among the frame's allocations. diff --git a/docs/language/annotations/annot-key.md b/docs/language/annotations/annot-key.md new file mode 100644 index 00000000..f73cd0b8 --- /dev/null +++ b/docs/language/annotations/annot-key.md @@ -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 +--- + +@Key marks an int field as a key code, so an editor offers a key to press rather than a number. diff --git a/docs/language/annotations/annot-max.md b/docs/language/annotations/annot-max.md new file mode 100644 index 00000000..6a322616 --- /dev/null +++ b/docs/language/annotations/annot-max.md @@ -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 +--- + +@max(n) bounds a list field: it never holds more than n, 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 +} +``` diff --git a/docs/language/annotations/annot-node.md b/docs/language/annotations/annot-node.md new file mode 100644 index 00000000..55f2c3be --- /dev/null +++ b/docs/language/annotations/annot-node.md @@ -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 +--- + +@Node(model), @Clip(model) and @Material(model) say a string field names a node, a clip or a material inside the glTF that field model of the same record names (an @Asset("gltf") field, or an @Ref to a registry whose record has exactly one). Naming anything else is an error. diff --git a/docs/language/annotations/annot-oneof.md b/docs/language/annotations/annot-oneof.md new file mode 100644 index 00000000..e77be25c --- /dev/null +++ b/docs/language/annotations/annot-oneof.md @@ -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 +--- + +@OneOf(GR_) says a field holds one of the constants whose names start GR_; @OneOf(GR_GOLD, GR_SILVER) one of those (each must exist); on a string field, @OneOf("box", "hull") one of those words, and every registry row is checked against them. diff --git a/docs/language/annotations/annot-owns.md b/docs/language/annotations/annot-owns.md new file mode 100644 index 00000000..4ffcf950 --- /dev/null +++ b/docs/language/annotations/annot-owns.md @@ -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 +--- + +@creates(PhysShape) on a function says it returns a handle the caller must release, @releases(PhysShape) that it releases one, and @owns(PhysShape) on a field that its record owns the handle it holds. ludic deps --resources counts a created handle never released, and an owned one lost when its record is released. diff --git a/docs/language/annotations/annot-permap.md b/docs/language/annotations/annot-permap.md new file mode 100644 index 00000000..3f754ee2 --- /dev/null +++ b/docs/language/annotations/annot-permap.md @@ -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 @PerMap 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 (_load, _find, ...). @Chunked(n) reads it in n-by-n-metre chunks from files named by {cx} and {cz}. diff --git a/docs/language/annotations/annot-range.md b/docs/language/annotations/annot-range.md new file mode 100644 index 00000000..e18b9066 --- /dev/null +++ b/docs/language/annotations/annot-range.md @@ -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 +--- + +@Range(0, 20.5) gives an editor the bounds of a number field, and ludicc --check holds every map row's value to them. Both arguments are numbers. diff --git a/docs/language/annotations/annot-ref.md b/docs/language/annotations/annot-ref.md new file mode 100644 index 00000000..65381bbe --- /dev/null +++ b/docs/language/annotations/annot-ref.md @@ -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 +--- + +@Ref(Items) on a field says its value is an entry of the registry Items - its index on an int field, its key on a string field of a @PerMap table - so an editor offers the entries. It changes nothing the program does; a registry the program does not declare is a warning and "unresolved" 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 +} +``` diff --git a/docs/language/annotations/annot-text.md b/docs/language/annotations/annot-text.md new file mode 100644 index 00000000..e9461c87 --- /dev/null +++ b/docs/language/annotations/annot-text.md @@ -0,0 +1,12 @@ +--- +id: annot-text +name: @Text +category: annotations +kind: annotation +tokens: @Text @Multiline +sig: @Text @Multiline field: string = "" +tip: Read by the player, so translated; and prose, edited over several lines. +order: 72 +--- + +@Text marks a string the player reads, so it is translated; @Multiline says it is prose, edited as several lines rather than one. diff --git a/docs/language/annotations/annot-tint.md b/docs/language/annotations/annot-tint.md new file mode 100644 index 00000000..d7559487 --- /dev/null +++ b/docs/language/annotations/annot-tint.md @@ -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 +--- + +@Tint(TSLOT_HAIR) names the tint slot a colour field paints - a constant, which must exist (or, when the program lacks it, is a warning and unresolved). diff --git a/docs/language/annotations/annot-unit.md b/docs/language/annotations/annot-unit.md new file mode 100644 index 00000000..fc23f483 --- /dev/null +++ b/docs/language/annotations/annot-unit.md @@ -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 +--- + +@Unit takes one of m, m/s, m/s2, s, min, h, d, deg, rad, rad/s, kg, N, N.m, %, px, so one word means one unit to an editor; another spelling is a warning naming the canonical one. diff --git a/docs/language/control/kw-dispatch.md b/docs/language/control/kw-dispatch.md new file mode 100644 index 00000000..478b57cd --- /dev/null +++ b/docs/language/control/kw-dispatch.md @@ -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 +--- + +dispatch Move(dx: 1) queues an action; at the end of the frame phase every reducer on Move 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) +``` diff --git a/docs/language/ecs/kw-system.md b/docs/language/ecs/kw-system.md new file mode 100644 index 00000000..99d0e303 --- /dev/null +++ b/docs/language/ecs/kw-system.md @@ -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 +--- + +disable system S names a package's engine system (declared with @EngineSystem) and leaves it out of the program at compile time, for a game that drives that component itself. diff --git a/docs/language/operators/kw-true.md b/docs/language/operators/kw-true.md new file mode 100644 index 00000000..a7da264d --- /dev/null +++ b/docs/language/operators/kw-true.md @@ -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 +--- + +true and false are the two bool values; null is no record, no slice and no string - what an unset reference field holds and what a lookup that finds nothing returns. diff --git a/docs/language/scenes/kw-lasts.md b/docs/language/scenes/kw-lasts.md new file mode 100644 index 00000000..5ab42ccc --- /dev/null +++ b/docs/language/scenes/kw-lasts.md @@ -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 +--- + +lasts N then Next makes a scene timed - a banner, a splash - that moves to Next by itself once N seconds have passed. diff --git a/docs/language/scenes/kw-loads.md b/docs/language/scenes/kw-loads.md new file mode 100644 index 00000000..57714188 --- /dev/null +++ b/docs/language/scenes/kw-loads.md @@ -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 +--- + +loads then Next makes a scene the loading screen: the engine pumps the asset queue, draws a progress bar and enters Next when everything queued is ready. diff --git a/docs/language/scenes/kw-shows.md b/docs/language/scenes/kw-shows.md new file mode 100644 index 00000000..7aec662f --- /dev/null +++ b/docs/language/scenes/kw-shows.md @@ -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 +--- + +shows Menu in a scene's header hands a ui 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. diff --git a/docs/language/scenes/kw-then.md b/docs/language/scenes/kw-then.md new file mode 100644 index 00000000..bb4802b9 --- /dev/null +++ b/docs/language/scenes/kw-then.md @@ -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 +--- + +then names where a scene goes when it is done: after the time of a lasts scene, or once a loads scene's assets are ready. diff --git a/docs/language/structure/kw-action.md b/docs/language/structure/kw-action.md new file mode 100644 index 00000000..d932e6cc --- /dev/null +++ b/docs/language/structure/kw-action.md @@ -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 action is a record that says what was asked for in the game's words (Move, OpenPack). Input code dispatches it, and every reducer 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 } +``` diff --git a/docs/language/structure/kw-alias.md b/docs/language/structure/kw-alias.md new file mode 100644 index 00000000..e0e7ed07 --- /dev/null +++ b/docs/language/structure/kw-alias.md @@ -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 namespace block, alias m = f makes Name.m(...) a call of f, 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 +} +``` diff --git a/docs/language/structure/kw-as.md b/docs/language/structure/kw-as.md new file mode 100644 index 00000000..a2cb8b2d --- /dev/null +++ b/docs/language/structure/kw-as.md @@ -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 +--- + +as P after a registry's record makes the compiler write a constant for each entry, P_KEY in upper case, holding the entry's index - so code reads IT_ROPE rather than looking the key up. diff --git a/docs/language/structure/kw-bind.md b/docs/language/structure/kw-bind.md new file mode 100644 index 00000000..1d2928b2 --- /dev/null +++ b/docs/language/structure/kw-bind.md @@ -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 +--- + +bind answers a port: 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 } +``` diff --git a/docs/language/structure/kw-component.md b/docs/language/structure/kw-component.md new file mode 100644 index 00000000..62aba77f --- /dev/null +++ b/docs/language/structure/kw-component.md @@ -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 component is a piece of interface: the code file declares its props (handed in by its parent), its own state, the functions its template calls and the events it handles with on; Name.xml beside it is its template and Name.lss 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 } +} +``` diff --git a/docs/language/structure/kw-def.md b/docs/language/structure/kw-def.md new file mode 100644 index 00000000..ab607759 --- /dev/null +++ b/docs/language/structure/kw-def.md @@ -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 +--- + +def adds entries to a registry: one written inline, or every entry of a resource file. An open registry takes defs 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 } +``` diff --git a/docs/language/structure/kw-export.md b/docs/language/structure/kw-export.md new file mode 100644 index 00000000..4fd6d54d --- /dev/null +++ b/docs/language/structure/kw-export.md @@ -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 +--- + +export 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 @export 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 } +``` diff --git a/docs/language/structure/kw-friend.md b/docs/language/structure/kw-friend.md new file mode 100644 index 00000000..2a9ba874 --- /dev/null +++ b/docs/language/structure/kw-friend.md @@ -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 +--- + +friend module lab declares a module that may name every private name of every module; friend module lab of bank, sky 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 +``` diff --git a/docs/language/structure/kw-from.md b/docs/language/structure/kw-from.md new file mode 100644 index 00000000..e2966983 --- /dev/null +++ b/docs/language/structure/kw-from.md @@ -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 +--- + +from names the .lres file the compiler reads a registry's entries from, relative to the declaring file (or, in a @PerMap 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. diff --git a/docs/language/structure/kw-internal.md b/docs/language/structure/kw-internal.md new file mode 100644 index 00000000..e017ed49 --- /dev/null +++ b/docs/language/structure/kw-internal.md @@ -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 namespace block every function is part of the Name.* surface unless it says internal: then it is emitted as an ordinary helper the namespace's own methods can call, and Name.helper is not a method. + +```ludic +namespace Trail { + internal function step(n: int) -> int { return n + 1 } + function next(n: int) -> int { return step(n) } +} +``` diff --git a/docs/language/structure/kw-module.md b/docs/language/structure/kw-module.md new file mode 100644 index 00000000..c1752267 --- /dev/null +++ b/docs/language/structure/kw-module.md @@ -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 +--- + +module opens a directory's barrel (index.ludic) and says which module every file under it belongs to. A name a module does not export 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 (in layer L) and say what it may reach (uses). + +```ludic +# doc-check: skip — a module spans files +# bank/index.ludic +module bank uses base +import "ledger.ludic" +``` diff --git a/docs/language/structure/kw-mut.md b/docs/language/structure/kw-mut.md new file mode 100644 index 00000000..0b29891e --- /dev/null +++ b/docs/language/structure/kw-mut.md @@ -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: h: HikerState to read it, h: mut HikerState to change it, so a function's signature is everything it touches. The compiler refuses a write through a parameter that is not mut, and ludic migrate state --tighten takes mut 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 } +``` diff --git a/docs/language/structure/kw-numbers.md b/docs/language/structure/kw-numbers.md new file mode 100644 index 00000000..c7f16825 --- /dev/null +++ b/docs/language/structure/kw-numbers.md @@ -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 +--- + +numbers float at the top of a file makes a bare decimal such as 1.5 a float rather than a fixed. Such a file also refuses to promote a computed int silently: write float(n) where a count becomes a number. + +```ludic +# doc-check: skip — a file-level line +numbers float +const GRAVITY: float = 9.81 +``` diff --git a/docs/language/structure/kw-of.md b/docs/language/structure/kw-of.md new file mode 100644 index 00000000..403a7d46 --- /dev/null +++ b/docs/language/structure/kw-of.md @@ -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 +--- + +of names the record a registry's entries are (registry Tools of Tool), and, on a friend module line, the modules whose private names it may see. diff --git a/docs/language/structure/kw-open.md b/docs/language/structure/kw-open.md new file mode 100644 index 00000000..3a969e15 --- /dev/null +++ b/docs/language/structure/kw-open.md @@ -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 open registry is one whose entries may come from other modules' defs, 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. diff --git a/docs/language/structure/kw-port.md b/docs/language/structure/kw-port.md new file mode 100644 index 00000000..26b84812 --- /dev/null +++ b/docs/language/structure/kw-port.md @@ -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 port is what a module needs to ask the world, as named function members in primitive types. The program answers it once with bind; 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 +} +``` diff --git a/docs/language/structure/kw-prop.md b/docs/language/structure/kw-prop.md new file mode 100644 index 00000000..ab454940 --- /dev/null +++ b/docs/language/structure/kw-prop.md @@ -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 +--- + +prop declares a component member its parent sets from the template (<Counter step="2"/>); state declares one the component keeps for itself. Both are fields of the instance, listed with their types and defaults in ludic schema. diff --git a/docs/language/structure/kw-reducer.md b/docs/language/structure/kw-reducer.md new file mode 100644 index 00000000..567732ae --- /dev/null +++ b/docs/language/structure/kw-reducer.md @@ -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 reducer writes exactly one state when its action is dispatched: the first parameter is that state, mut, 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 +} +``` diff --git a/docs/language/structure/kw-registry.md b/docs/language/structure/kw-registry.md new file mode 100644 index 00000000..09c85e28 --- /dev/null +++ b/docs/language/structure/kw-registry.md @@ -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 registry is a table of named entries of one record: its entries come from def declarations or a resource file named by from, and as P generates a constant P_KEY per entry holding its index. Editors read it through ludic schema, and attributes such as @AppendOnly and @PerMap 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" +``` diff --git a/docs/language/structure/kw-unsafe.md b/docs/language/structure/kw-unsafe.md new file mode 100644 index 00000000..443d2e9f --- /dev/null +++ b/docs/language/structure/kw-unsafe.md @@ -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 unsafe: raw allocation, freeing, pointer indexing and foreign calls are compile errors elsewhere. An unsafe function or an unsafe { ... } block allows them inside, and only packages and the runtime are trusted to write it; a program's own files need --unsafe. + +```ludic +# doc-check: skip — needs --unsafe +unsafe function raw(n: int) -> pointer { return bytes(n) } +``` diff --git a/docs/language/structure/kw-uses.md b/docs/language/structure/kw-uses.md new file mode 100644 index 00000000..41eec827 --- /dev/null +++ b/docs/language/structure/kw-uses.md @@ -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 +--- + +uses ends a module 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 port the asker declares and the program binds instead. + +```ludic +# doc-check: skip — a module spans files +module sky uses base, events, clock +``` diff --git a/docs/language/structure/kw-view.md b/docs/language/structure/kw-view.md new file mode 100644 index 00000000..817b813f --- /dev/null +++ b/docs/language/structure/kw-view.md @@ -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 view says what a template may read (its fields, each an expression) and what it may do (its queries and its on events), and nothing else crosses. The compiler writes view_name(), 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) } +} +``` diff --git a/tools/docgen/inventory.json b/tools/docgen/inventory.json index a1aa6fd4..bd355942 100644 --- a/tools/docgen/inventory.json +++ b/tools/docgen/inventory.json @@ -25,7 +25,25 @@ "annot-system", "annot-enginesystem", "annot-namespace", - "annot-clearcolor" + "annot-clearcolor", + "annot-alloc_ok", + "annot-appendonly", + "annot-asset", + "annot-color", + "annot-derived", + "annot-deterministic", + "annot-frame", + "annot-key", + "annot-max", + "annot-node", + "annot-oneof", + "annot-owns", + "annot-permap", + "annot-range", + "annot-ref", + "annot-text", + "annot-tint", + "annot-unit" ], "builtins": [ "fn-print", @@ -86,7 +104,8 @@ "kw-machine", "kw-state", "kw-become", - "kw-try" + "kw-try", + "kw-dispatch" ], "ease": [ "ease-in", @@ -119,7 +138,8 @@ "fn-world_register_prop", "fn-world_attach_dyn", "fn-world_detach_dyn", - "fn-world_query_next" + "fn-world_query_next", + "kw-system" ], "events": [ "kw-event", @@ -211,7 +231,8 @@ "op-access", "op-interp", "op-literals", - "op-comment" + "op-comment", + "kw-true" ], "phases": [ "phase-fixedupdate", @@ -240,7 +261,11 @@ "kw-on", "kw-enter", "kw-exit", - "kw-start" + "kw-start", + "kw-lasts", + "kw-loads", + "kw-shows", + "kw-then" ], "screen": [ "screen-clear", @@ -276,7 +301,29 @@ "kw-extern", "kw-ui", "kw-enum", - "kw-namespace" + "kw-namespace", + "kw-action", + "kw-alias", + "kw-as", + "kw-bind", + "kw-component", + "kw-def", + "kw-export", + "kw-friend", + "kw-from", + "kw-internal", + "kw-module", + "kw-mut", + "kw-numbers", + "kw-of", + "kw-open", + "kw-port", + "kw-prop", + "kw-reducer", + "kw-registry", + "kw-unsafe", + "kw-uses", + "kw-view" ], "system": [ "system-run",