docs: automated documentation pipeline (per-symbol source → pages)
Some checks failed
docs / build-and-deploy (push) Failing after 38s

Replace the hardcoded landing page and minimal reference with a generated
documentation site driven by a single source of truth.

- docs/language/**: one file per symbol (93 keywords/types/builtins/namespace
  methods/operators/annotations), each with front-matter (id, kind, tokens,
  sig, tip) + description + a ```ludic example. Seeded by exploding the former
  inline SECTIONS list; these files are now the source of truth.
- docs/site/: site.json (editable hero/features/showcase/messaging, not
  hardcoded) + snippets/*.ludic (real programs shown on the landing page).
- tools/docgen/gen.py: generates index.html, api.html, ludic-highlight.js and
  symbols.json. The highlighter's symbol tables, hover tips and jump anchors
  are GENERATED from the per-symbol files — add a symbol and it is recognized,
  tipped and linked in every snippet automatically. Python stdlib only.
- tools/docgen/check.py: verifies the pages contract + that no snippet token
  links to a missing reference anchor.
- .forgejo/workflows/docs.yml: rebuilds and publishes to the pages branch on
  every push to main touching the docs sources.

Consumes the new Screen.*/Color.*/named-arg API and the 221-color palette.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-29 16:25:54 +03:00
parent a3a1e4d160
commit 51ddfa3ce9
121 changed files with 3827 additions and 0 deletions

View file

@ -0,0 +1,55 @@
name: docs
# Rebuild the documentation site and publish it to the `pages` branch on every
# push to main that touches the docs sources. The site is generated from the
# per-symbol source of truth in docs/language/** — nothing is hand-edited on the
# pages branch.
on:
push:
branches: [main]
paths:
- 'docs/**'
- 'tools/docgen/**'
- '.forgejo/workflows/docs.yml'
workflow_dispatch: {}
permissions:
contents: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Generate the documentation site
run: |
python3 --version
python3 tools/docgen/gen.py --out public
echo "--- generated files ---"
ls -la public
- name: Sanity-check the output (root has index.html + .nojekyll, no broken links)
run: python3 tools/docgen/check.py public
- name: Publish to the pages branch
run: |
set -eu
TOKEN="${PAGES_TOKEN:-${AUTO_TOKEN:-}}"
if [ -z "$TOKEN" ]; then
echo "::error::No deploy token. Add a repo secret PAGES_TOKEN (write access) or allow the automatic token to push."
exit 1
fi
cd public
git init -q -b pages
git config user.name "ludic-docs-bot"
git config user.email "docs@workshopsoft.io"
git add -A
git commit -q -m "docs: regenerate site from ${SOURCE_SHA}"
git push -f "https://ludic-docs-bot:${TOKEN}@git.workshopsoft.io/workshopsoft/ludic.git" pages
echo "published $(git rev-parse --short HEAD) to pages"
env:
PAGES_TOKEN: ${{ secrets.PAGES_TOKEN }}
AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SOURCE_SHA: ${{ github.sha }}

View file

@ -0,0 +1,7 @@
---
id: annotations
title: Annotations
order: 13
---
<code>@Name(...)</code> decorators attach compile-time behavior to a handler, property, or function.

View file

@ -0,0 +1,12 @@
---
id: annot-computed
name: @Computed
category: annotations
kind: annotation
tokens: @Computed
sig: @Computed on a property field
tip: A derived field: an expression expanded inline wherever it is read, never stored.
order: 1
---
A derived field: an expression expanded inline wherever it is read, never stored.

View file

@ -0,0 +1,12 @@
---
id: annot-export
name: @export
category: annotations
kind: annotation
tokens: @export
sig: @export fn name(…) -> R
tip: Expose a function as a C-ABI symbol from a module.
order: 3
---
Expose a function as a C-ABI symbol from a <code>module</code>.

View file

@ -0,0 +1,12 @@
---
id: annot-on
name: @OnEnable / @OnDisable / @OnDespawn
category: annotations
kind: annotation
tokens: @OnEnable @OnDisable @OnDespawn
sig: @OnDisable(Prop) handler … { … }
tip: Lifecycle hooks that fire when a property/model is toggled or an entity is torn down.
order: 2
---
Lifecycle hooks that fire when a property/model is toggled or an entity is torn down.

View file

@ -0,0 +1,12 @@
---
id: annot-queries
name: @Queries
category: annotations
kind: annotation
tokens: @Queries
sig: @Queries(these: [Pos, Vel])
tip: Declare the properties a handler touches, binding their fields by name in the body.
order: 0
---
Declare the properties a handler touches, binding their fields by name in the body.

View file

@ -0,0 +1,12 @@
---
id: annot-sync
name: @Sync / @Owned
category: annotations
kind: annotation
tokens: @Sync @Owned
sig: @Sync property … / @Owned
tip: Mark a property's fields as replicated, and entities as owned, for networking.
order: 4
---
Mark a property's fields as replicated, and entities as owned, for networking.

View file

@ -0,0 +1,7 @@
---
id: builtins
title: Builtin functions
order: 12
---
Global functions available anywhere, beyond the namespaced APIs above.

View file

@ -0,0 +1,12 @@
---
id: fn-abs
name: abs
category: builtins
kind: builtin
tokens: abs
sig: abs(a) -> int
tip: Absolute value.
order: 8
---
Absolute value.

View file

@ -0,0 +1,12 @@
---
id: fn-bytes
name: bytes / words
category: builtins
kind: builtin
tokens: bytes words
sig: bytes(n) -> ptr / words(n) -> words
tip: Allocate a raw buffer of n bytes / n 32-bit words.
order: 11
---
Allocate a raw buffer of n bytes / n 32-bit words.

View file

@ -0,0 +1,12 @@
---
id: fn-clamp
name: clamp
category: builtins
kind: builtin
tokens: clamp
sig: clamp(v, lo, hi) -> int
tip: Constrain v to [lo, hi].
order: 9
---
Constrain v to [lo, hi].

View file

@ -0,0 +1,12 @@
---
id: fn-font_load
name: font_load
category: builtins
kind: builtin
tokens: font_load
sig: font_load(path) -> int
tip: Load a TrueType font and return a handle for the UI.
order: 12
---
Load a TrueType font and return a handle for the UI.

View file

@ -0,0 +1,12 @@
---
id: fn-fx
name: fx / flr
category: builtins
kind: builtin
tokens: fx flr
sig: fx(n) -> fixed / flr(x) -> int
tip: Convert between int and fixed-point.
order: 10
---
Convert between int and fixed-point.

View file

@ -0,0 +1,12 @@
---
id: fn-len
name: len
category: builtins
kind: builtin
tokens: len
sig: len(x) -> int
tip: Length of a slice or string.
order: 5
---
Length of a slice or string.

View file

@ -0,0 +1,12 @@
---
id: fn-load
name: load
category: builtins
kind: builtin
tokens: load
sig: load() -> bool
tip: Restore a snapshot written by save().
order: 4
---
Restore a snapshot written by <code>save()</code>.

View file

@ -0,0 +1,12 @@
---
id: fn-min
name: min / max
category: builtins
kind: builtin
tokens: min max
sig: min(a, b) / max(a, b) -> int
tip: The smaller / larger of two ints.
order: 7
---
The smaller / larger of two ints.

View file

@ -0,0 +1,12 @@
---
id: fn-print
name: print
category: builtins
kind: builtin
tokens: print
sig: print(x)
tip: Print an int or string, followed by a newline — for headless tests and debugging.
order: 0
---
Print an int or string, followed by a newline — for headless tests and debugging.

View file

@ -0,0 +1,12 @@
---
id: fn-push
name: push
category: builtins
kind: builtin
tokens: push
sig: push(slice, x)
tip: Append to a slice.
order: 6
---
Append to a slice.

View file

@ -0,0 +1,12 @@
---
id: fn-quit
name: quit
category: builtins
kind: builtin
tokens: quit
sig: quit()
tip: Stop the game loop after this frame.
order: 2
---
Stop the game loop after this frame.

View file

@ -0,0 +1,12 @@
---
id: fn-save
name: save
category: builtins
kind: builtin
tokens: save
sig: save()
tip: Serialize the entire world — every entity, property and program var — to a snapshot in one call.
order: 3
---
Serialize the entire world — every entity, property and program <code>var</code> — to a snapshot in one call.

View file

@ -0,0 +1,12 @@
---
id: fn-str
name: str
category: builtins
kind: builtin
tokens: str
sig: str(x) -> str
tip: Convert an int/bool/fixed to text; a string passes through.
order: 1
---
Convert an int/bool/fixed to text; a string passes through. Also used by <code>`{…}`</code> interpolation.

View file

@ -0,0 +1,7 @@
---
id: colors
title: Color — the named palette
order: 9
---
<code>Color.Name</code> lowers to a plain <code>0xRRGGBB</code> integer at compile time — no runtime cost, identical to writing the hex by hand, but readable. 221 names are built in; a custom shade is any hex literal or a named <code>const</code>. The full palette:

View file

@ -0,0 +1,944 @@
{
"count": 221,
"groups": [
{
"name": "Whites",
"colors": [
{
"name": "White",
"hex": "FFFFFF"
},
{
"name": "Snow",
"hex": "FFFAFA"
},
{
"name": "Ivory",
"hex": "FFFFF0"
},
{
"name": "EggShellWhite",
"hex": "F0EAD6"
},
{
"name": "FloralWhite",
"hex": "FFFAF0"
},
{
"name": "SeaShell",
"hex": "FFF5EE"
},
{
"name": "Linen",
"hex": "FAF0E6"
},
{
"name": "AntiqueWhite",
"hex": "FAEBD7"
},
{
"name": "OldLace",
"hex": "FDF5E6"
},
{
"name": "Beige",
"hex": "F5F5DC"
},
{
"name": "Cream",
"hex": "FFFDD0"
},
{
"name": "Honeydew",
"hex": "F0FFF0"
},
{
"name": "MintCream",
"hex": "F5FFFA"
},
{
"name": "Azure",
"hex": "F0FFFF"
},
{
"name": "AliceBlue",
"hex": "F0F8FF"
},
{
"name": "GhostWhite",
"hex": "F8F8FF"
},
{
"name": "WhiteSmoke",
"hex": "F5F5F5"
},
{
"name": "Lavender",
"hex": "E6E6FA"
},
{
"name": "Bone",
"hex": "E3DAC9"
},
{
"name": "Parchment",
"hex": "F1E9D2"
}
]
},
{
"name": "Grays",
"colors": [
{
"name": "Gainsboro",
"hex": "DCDCDC"
},
{
"name": "LightGray",
"hex": "D3D3D3"
},
{
"name": "Silver",
"hex": "C0C0C0"
},
{
"name": "Ash",
"hex": "B2BEB5"
},
{
"name": "DarkGray",
"hex": "A9A9A9"
},
{
"name": "Gray",
"hex": "808080"
},
{
"name": "DimGray",
"hex": "696969"
},
{
"name": "Nickel",
"hex": "727472"
},
{
"name": "Slate",
"hex": "708090"
},
{
"name": "SlateGray",
"hex": "708090"
},
{
"name": "LightSlateGray",
"hex": "778899"
},
{
"name": "Gunmetal",
"hex": "2A3439"
},
{
"name": "Charcoal",
"hex": "36454F"
},
{
"name": "Graphite",
"hex": "1C1C1C"
},
{
"name": "Onyx",
"hex": "353839"
},
{
"name": "Jet",
"hex": "343434"
},
{
"name": "Black",
"hex": "000000"
},
{
"name": "EerieBlack",
"hex": "1B1B1B"
},
{
"name": "RaisinBlack",
"hex": "242124"
},
{
"name": "Ebony",
"hex": "555D50"
}
]
},
{
"name": "Reds",
"colors": [
{
"name": "Red",
"hex": "FF0000"
},
{
"name": "Crimson",
"hex": "DC143C"
},
{
"name": "Scarlet",
"hex": "FF2400"
},
{
"name": "Vermilion",
"hex": "E34234"
},
{
"name": "FireBrick",
"hex": "B22222"
},
{
"name": "Cinnabar",
"hex": "E44D2E"
},
{
"name": "DarkRed",
"hex": "8B0000"
},
{
"name": "Maroon",
"hex": "800000"
},
{
"name": "Ruby",
"hex": "E0115F"
},
{
"name": "Cardinal",
"hex": "C41E3A"
},
{
"name": "IndianRed",
"hex": "CD5C5C"
},
{
"name": "Rust",
"hex": "B7410E"
},
{
"name": "Sangria",
"hex": "92000A"
},
{
"name": "Redwood",
"hex": "A45A52"
},
{
"name": "Cerise",
"hex": "DE3163"
},
{
"name": "Amaranth",
"hex": "E52B50"
},
{
"name": "Carmine",
"hex": "960018"
},
{
"name": "Chestnut",
"hex": "954535"
},
{
"name": "Brick",
"hex": "CB4154"
},
{
"name": "TerraCotta",
"hex": "E2725B"
}
]
},
{
"name": "Pinks",
"colors": [
{
"name": "Pink",
"hex": "FFC0CB"
},
{
"name": "LightPink",
"hex": "FFB6C1"
},
{
"name": "HotPink",
"hex": "FF69B4"
},
{
"name": "DeepPink",
"hex": "FF1493"
},
{
"name": "PaleVioletRed",
"hex": "DB7093"
},
{
"name": "Rose",
"hex": "FF007F"
},
{
"name": "Blush",
"hex": "DE5D83"
},
{
"name": "Salmon",
"hex": "FA8072"
},
{
"name": "LightSalmon",
"hex": "FFA07A"
},
{
"name": "DarkSalmon",
"hex": "E9967A"
},
{
"name": "Coral",
"hex": "FF7F50"
},
{
"name": "Watermelon",
"hex": "FC6C85"
},
{
"name": "Flamingo",
"hex": "FC8EAC"
},
{
"name": "Bubblegum",
"hex": "FFC1CC"
},
{
"name": "Fuchsia",
"hex": "FF00FF"
},
{
"name": "Magenta",
"hex": "FF00FF"
},
{
"name": "Mauve",
"hex": "E0B0FF"
},
{
"name": "Puce",
"hex": "CC8899"
},
{
"name": "Thistle",
"hex": "D8BFD8"
},
{
"name": "Orchid",
"hex": "DA70D6"
}
]
},
{
"name": "Oranges",
"colors": [
{
"name": "Orange",
"hex": "FFA500"
},
{
"name": "DarkOrange",
"hex": "FF8C00"
},
{
"name": "Tangerine",
"hex": "F28500"
},
{
"name": "Pumpkin",
"hex": "FF7518"
},
{
"name": "Apricot",
"hex": "FBCEB1"
},
{
"name": "Peach",
"hex": "FFE5B4"
},
{
"name": "Cantaloupe",
"hex": "FFA62B"
},
{
"name": "Amber",
"hex": "FFBF00"
},
{
"name": "Bronze",
"hex": "CD7F32"
},
{
"name": "Copper",
"hex": "B87333"
},
{
"name": "Marigold",
"hex": "EAA221"
},
{
"name": "Carrot",
"hex": "ED9121"
},
{
"name": "Persimmon",
"hex": "EC5800"
},
{
"name": "Papaya",
"hex": "FF9E2C"
},
{
"name": "Sunset",
"hex": "FAD6A5"
}
]
},
{
"name": "Yellows",
"colors": [
{
"name": "Yellow",
"hex": "FFFF00"
},
{
"name": "LightYellow",
"hex": "FFFFE0"
},
{
"name": "Gold",
"hex": "FFD700"
},
{
"name": "Goldenrod",
"hex": "DAA520"
},
{
"name": "Lemon",
"hex": "FFF700"
},
{
"name": "Canary",
"hex": "FFEF00"
},
{
"name": "Mustard",
"hex": "FFDB58"
},
{
"name": "Flax",
"hex": "EEDC82"
},
{
"name": "Wheat",
"hex": "F5DEB3"
},
{
"name": "Corn",
"hex": "FBEC5D"
},
{
"name": "Dandelion",
"hex": "F0E130"
},
{
"name": "Saffron",
"hex": "F4C430"
},
{
"name": "Khaki",
"hex": "F0E68C"
},
{
"name": "DarkKhaki",
"hex": "BDB76B"
},
{
"name": "Straw",
"hex": "E4D96F"
}
]
},
{
"name": "Browns",
"colors": [
{
"name": "Brown",
"hex": "8B4513"
},
{
"name": "SaddleBrown",
"hex": "8B4513"
},
{
"name": "Sienna",
"hex": "A0522D"
},
{
"name": "Chocolate",
"hex": "D2691E"
},
{
"name": "Peru",
"hex": "CD853F"
},
{
"name": "Tan",
"hex": "D2B48C"
},
{
"name": "BurlyWood",
"hex": "DEB887"
},
{
"name": "Sand",
"hex": "C2B280"
},
{
"name": "Coffee",
"hex": "6F4E37"
},
{
"name": "Espresso",
"hex": "4B3621"
},
{
"name": "Mahogany",
"hex": "C04000"
},
{
"name": "Walnut",
"hex": "773F1A"
},
{
"name": "Umber",
"hex": "635147"
},
{
"name": "Sepia",
"hex": "704214"
},
{
"name": "Taupe",
"hex": "483C32"
},
{
"name": "Fawn",
"hex": "E5AA70"
},
{
"name": "Caramel",
"hex": "C68E17"
},
{
"name": "Cocoa",
"hex": "D2691E"
},
{
"name": "Hazel",
"hex": "8E7618"
},
{
"name": "Wenge",
"hex": "645452"
}
]
},
{
"name": "Greens",
"colors": [
{
"name": "Green",
"hex": "008000"
},
{
"name": "Lime",
"hex": "00FF00"
},
{
"name": "LimeGreen",
"hex": "32CD32"
},
{
"name": "LawnGreen",
"hex": "7CFC00"
},
{
"name": "Chartreuse",
"hex": "7FFF00"
},
{
"name": "GreenYellow",
"hex": "ADFF2F"
},
{
"name": "SpringGreen",
"hex": "00FF7F"
},
{
"name": "MintGreen",
"hex": "98FF98"
},
{
"name": "SeaGreen",
"hex": "2E8B57"
},
{
"name": "MediumSeaGreen",
"hex": "3CB371"
},
{
"name": "ForestGreen",
"hex": "228B22"
},
{
"name": "DarkGreen",
"hex": "006400"
},
{
"name": "OliveDrab",
"hex": "6B8E23"
},
{
"name": "Olive",
"hex": "808000"
},
{
"name": "Moss",
"hex": "8A9A5B"
},
{
"name": "Fern",
"hex": "4F7942"
},
{
"name": "Emerald",
"hex": "50C878"
},
{
"name": "Jade",
"hex": "00A86B"
},
{
"name": "Malachite",
"hex": "0BDA51"
},
{
"name": "Shamrock",
"hex": "009E60"
},
{
"name": "Pistachio",
"hex": "93C572"
},
{
"name": "Avocado",
"hex": "568203"
},
{
"name": "Pine",
"hex": "01796F"
},
{
"name": "Sage",
"hex": "9CAF88"
},
{
"name": "Kelly",
"hex": "4CBB17"
},
{
"name": "Hunter",
"hex": "355E3B"
},
{
"name": "Basil",
"hex": "579229"
},
{
"name": "Clover",
"hex": "2E8B57"
},
{
"name": "Juniper",
"hex": "6D9A79"
},
{
"name": "Neon",
"hex": "39FF14"
}
]
},
{
"name": "Cyans",
"colors": [
{
"name": "Cyan",
"hex": "00FFFF"
},
{
"name": "Aqua",
"hex": "00FFFF"
},
{
"name": "LightCyan",
"hex": "E0FFFF"
},
{
"name": "PaleTurquoise",
"hex": "AFEEEE"
},
{
"name": "Aquamarine",
"hex": "7FFFD4"
},
{
"name": "Turquoise",
"hex": "40E0D0"
},
{
"name": "MediumTurquoise",
"hex": "48D1CC"
},
{
"name": "DarkTurquoise",
"hex": "00CED1"
},
{
"name": "Teal",
"hex": "008080"
},
{
"name": "DarkCyan",
"hex": "008B8B"
},
{
"name": "CadetBlue",
"hex": "5F9EA0"
},
{
"name": "Lagoon",
"hex": "018E8E"
},
{
"name": "Seafoam",
"hex": "93E9BE"
},
{
"name": "Cerulean",
"hex": "007BA7"
},
{
"name": "SkyBlueLight",
"hex": "80DAEB"
},
{
"name": "Robin",
"hex": "00CCCC"
},
{
"name": "Verdigris",
"hex": "43B3AE"
},
{
"name": "Celadon",
"hex": "ACE1AF"
}
]
},
{
"name": "Blues",
"colors": [
{
"name": "Blue",
"hex": "0000FF"
},
{
"name": "LightBlue",
"hex": "ADD8E6"
},
{
"name": "PowderBlue",
"hex": "B0E0E6"
},
{
"name": "SkyBlue",
"hex": "87CEEB"
},
{
"name": "LightSkyBlue",
"hex": "87CEFA"
},
{
"name": "DeepSkyBlue",
"hex": "00BFFF"
},
{
"name": "DodgerBlue",
"hex": "1E90FF"
},
{
"name": "CornflowerBlue",
"hex": "6495ED"
},
{
"name": "SteelBlue",
"hex": "4682B4"
},
{
"name": "RoyalBlue",
"hex": "4169E1"
},
{
"name": "MediumBlue",
"hex": "0000CD"
},
{
"name": "DarkBlue",
"hex": "00008B"
},
{
"name": "Navy",
"hex": "000080"
},
{
"name": "MidnightBlue",
"hex": "191970"
},
{
"name": "Cobalt",
"hex": "0047AB"
},
{
"name": "Sapphire",
"hex": "0F52BA"
},
{
"name": "Denim",
"hex": "1560BD"
},
{
"name": "Indigo",
"hex": "4B0082"
},
{
"name": "Prussian",
"hex": "003153"
},
{
"name": "Ultramarine",
"hex": "3F00FF"
},
{
"name": "Periwinkle",
"hex": "CCCCFF"
},
{
"name": "Iris",
"hex": "5A4FCF"
},
{
"name": "Glaucous",
"hex": "6082B6"
},
{
"name": "Zaffre",
"hex": "0014A8"
},
{
"name": "Berry",
"hex": "2E2D88"
}
]
},
{
"name": "Purples",
"colors": [
{
"name": "Purple",
"hex": "800080"
},
{
"name": "Violet",
"hex": "EE82EE"
},
{
"name": "DarkViolet",
"hex": "9400D3"
},
{
"name": "BlueViolet",
"hex": "8A2BE2"
},
{
"name": "MediumPurple",
"hex": "9370DB"
},
{
"name": "Amethyst",
"hex": "9966CC"
},
{
"name": "Plum",
"hex": "8E4585"
},
{
"name": "Eggplant",
"hex": "614051"
},
{
"name": "Grape",
"hex": "6F2DA8"
},
{
"name": "Wine",
"hex": "722F37"
},
{
"name": "Mulberry",
"hex": "C54B8C"
},
{
"name": "Lilac",
"hex": "C8A2C8"
},
{
"name": "Wisteria",
"hex": "C9A0DC"
},
{
"name": "Heliotrope",
"hex": "DF73FF"
},
{
"name": "Byzantium",
"hex": "702963"
},
{
"name": "Tyrian",
"hex": "66023C"
},
{
"name": "RebeccaPurple",
"hex": "663399"
},
{
"name": "Orchid2",
"hex": "AF69EF"
}
]
}
]
}

View file

@ -0,0 +1,7 @@
---
id: control
title: Control flow
order: 2
---
Branches, loops, and state machines.

View file

@ -0,0 +1,12 @@
---
id: kw-become
name: become
category: control
kind: keyword
tokens: become
sig: become Name
tip: Transition: to another state of the enclosing machine, or to another scene.
order: 8
---
Transition: to another <code>state</code> of the enclosing machine, or to another <code>scene</code>.

View file

@ -0,0 +1,16 @@
---
id: kw-for
name: for … in
category: control
kind: keyword
tokens: for
sig: for name in a .. b { … }
tip: Range loop.
order: 2
---
Range loop. <b>Each pass binds a fresh, immutable <code>name</code></b> — it is not a variable you reuse or reassign; the range <code>a .. b</code> runs from <code>a</code> up to but not including <code>b</code>.
```ludic
for gy in 0 .. GRID_H { … }
```

View file

@ -0,0 +1,12 @@
---
id: kw-if
name: if / else
category: control
kind: keyword
tokens: if else
sig: if cond { … } else { … }
tip: A branch.
order: 0
---
A branch. Conditions are plain expressions; no parentheses required.

View file

@ -0,0 +1,12 @@
---
id: kw-in
name: in
category: control
kind: keyword
tokens: in
sig: for x in range | query
tip: Binds the loop name to each value of a range or query.
order: 3
---
Binds the loop name to each value of a range or query.

View file

@ -0,0 +1,16 @@
---
id: kw-machine
name: machine
category: control
kind: keyword
tokens: machine
sig: machine store { state Name { … } }
tip: A state machine over an int var (or register).
order: 6
---
A state machine over an int <code>var</code> (or register). It dispatches on the store's value.
```ludic
machine turn_phase { state KnightMenu { … } }
```

View file

@ -0,0 +1,12 @@
---
id: kw-match
name: match / when
category: control
kind: keyword
tokens: match
sig: match x { when a, b => … }
tip: Multi-way branch on a value, matching one or more literals per arm.
order: 4
---
Multi-way branch on a value, matching one or more literals per arm.

View file

@ -0,0 +1,12 @@
---
id: kw-state
name: state
category: control
kind: keyword
tokens: state
sig: state Name { … }
tip: One state of a machine.
order: 7
---
One state of a <code>machine</code>.

View file

@ -0,0 +1,12 @@
---
id: kw-when
name: when
category: control
kind: keyword
tokens: when
sig: when value => result
tip: One arm of a match.
order: 5
---
One arm of a <code>match</code>.

View file

@ -0,0 +1,12 @@
---
id: kw-while
name: while
category: control
kind: keyword
tokens: while
sig: while cond { … }
tip: Loop while the condition holds.
order: 1
---
Loop while the condition holds.

View file

@ -0,0 +1,7 @@
---
id: ecs
title: Entities & the ECS
order: 1
---
Entities are ids; properties are their data; queries walk them.

View file

@ -0,0 +1,12 @@
---
id: fn-world_get
name: world_get / world_set / world_has …
category: ecs
kind: builtin
tokens: world_get world_set world_has world_count world_spawn
sig: world_get(entity, prop, field) -> int
tip: The reflection ABI: read and write the world by numeric id, for tools and mods.
order: 8
---
The reflection ABI: read and write the world by numeric id, for tools and mods. See also world_count, world_spawn.

View file

@ -0,0 +1,12 @@
---
id: kw-attach
name: attach
category: ecs
kind: keyword
tokens: attach
sig: attach Prop on entity { overrides }
tip: Add a property to a live entity.
order: 6
---
Add a property to a live entity.

View file

@ -0,0 +1,12 @@
---
id: kw-despawn
name: despawn
category: ecs
kind: keyword
tokens: despawn
sig: despawn entity
tip: Remove an entity.
order: 1
---
Remove an entity. Inside a query, <code>despawn self()</code> removes the current match.

View file

@ -0,0 +1,12 @@
---
id: kw-detach
name: detach
category: ecs
kind: keyword
tokens: detach
sig: detach Prop on entity
tip: Remove a property from a live entity.
order: 7
---
Remove a property from a live entity.

View file

@ -0,0 +1,12 @@
---
id: kw-disable
name: disable
category: ecs
kind: keyword
tokens: disable
sig: disable Prop on entity
tip: Deactivate a property without destroying its data.
order: 5
---
Deactivate a property without destroying its data. Also <code>disable Model</code> / a whole handler.

View file

@ -0,0 +1,12 @@
---
id: kw-enable
name: enable
category: ecs
kind: keyword
tokens: enable
sig: enable Prop on entity
tip: Re-activate a disabled property; its stored values are intact.
order: 4
---
Re-activate a disabled property; its stored values are intact.

View file

@ -0,0 +1,16 @@
---
id: kw-query
name: query
category: ecs
kind: keyword
tokens: query
sig: query [PropA, PropB, {Tag}]
tip: Match every entity that carries all listed properties.
order: 2
---
Match every entity that carries all listed properties. Use it in <code>for (a, b) in query […]</code>; a <code>{Tag}</code> filters without binding.
```ludic
for (p, s) in query [Pos, Seg] { … }
```

View file

@ -0,0 +1,12 @@
---
id: kw-self
name: self
category: ecs
kind: keyword
tokens: self
sig: self()
tip: The entity currently bound by the enclosing query.
order: 3
---
The entity currently bound by the enclosing query.

View file

@ -0,0 +1,16 @@
---
id: kw-spawn
name: spawn
category: ecs
kind: keyword
tokens: spawn
sig: spawn Label { Prop { field: v }; … }
tip: Create an entity carrying the listed properties (or a model).
order: 0
---
Create an entity carrying the listed properties (or a model). The label is for readability.
```ludic
spawn Body { Seg { order: 1 }; Pos { x: 9, y: 7 } }
```

View file

@ -0,0 +1,7 @@
---
id: events
title: Events
order: 4
---
Decoupled, named messages between handlers.

View file

@ -0,0 +1,12 @@
---
id: kw-cancel
name: cancel
category: events
kind: keyword
tokens: cancel
sig: cancel
tip: Inside a listener, veto a cancellable event.
order: 2
---
Inside a listener, veto a cancellable event.

View file

@ -0,0 +1,12 @@
---
id: kw-emit
name: emit
category: events
kind: keyword
tokens: emit
sig: emit Name(field: v)
tip: Fire an event, invoking its listeners.
order: 1
---
Fire an event, invoking its listeners. As an expression it yields a cancellable event's cancelled flag.

View file

@ -0,0 +1,12 @@
---
id: kw-event
name: event
category: events
kind: keyword
tokens: event
sig: event Name { field: T = default }
tip: Declare a public event payload.
order: 0
---
Declare a public event payload.

View file

@ -0,0 +1,7 @@
---
id: input
title: Input
order: 6
---
Reading the keyboard.

View file

@ -0,0 +1,17 @@
---
id: input-key
name: Input.key
category: input
kind: namespace-method
tokens: Input.key
sig: Input.key() -> int
tip: The key pressed this frame, as a character code (0 when nothing is pressed).
order: 0
---
The key pressed this frame, as a character code (0 when nothing is pressed). Compare against character literals like <code>'w'</code>.
```ludic
let k = Input.key()
if k == 'w' { … }
```

View file

@ -0,0 +1,7 @@
---
id: map
title: Map — tilemap
order: 8
---
A character grid the game paints and reads.

View file

@ -0,0 +1,16 @@
---
id: map-row
name: Map.row
category: map
kind: namespace-method
tokens: Map.row
sig: Map.row(y, cells)
tip: Fill one row of the tilemap from a string of tile characters.
order: 1
---
Fill one row of the tilemap from a string of tile characters.
```ludic
Map.row(y: 0, cells: "####......####")
```

View file

@ -0,0 +1,12 @@
---
id: map-size
name: Map.size
category: map
kind: namespace-method
tokens: Map.size
sig: Map.size(width, height)
tip: Set the tilemap dimensions in cells.
order: 0
---
Set the tilemap dimensions in cells.

View file

@ -0,0 +1,12 @@
---
id: map-tile
name: Map.tile
category: map
kind: namespace-method
tokens: Map.tile
sig: Map.tile(x, y) -> int
tip: Read the tile character at a cell.
order: 2
---
Read the tile character at a cell. Out-of-bounds reads answer '#', so the edge of the world is a wall for free.

View file

@ -0,0 +1,7 @@
---
id: operators
title: Operators & tokens
order: 11
---
The symbols the grammar recognizes.

View file

@ -0,0 +1,11 @@
---
id: op-access
name: Member & index
category: operators
kind: operator
sig: x.field buf[i] s[a..b]
tip: Field/method access, element index, and string slice (a fresh substring).
order: 6
---
Field/method access, element index, and string slice (a fresh substring).

View file

@ -0,0 +1,11 @@
---
id: op-arith
name: Arithmetic
category: operators
kind: operator
sig: + - * / %
tip: Add, subtract, multiply, integer-divide, remainder.
order: 0
---
Add, subtract, multiply, integer-divide, remainder. On <code>fixed</code> values the same symbols do fixed-point math.

View file

@ -0,0 +1,11 @@
---
id: op-assign
name: Assignment
category: operators
kind: operator
sig: name = value
tip: Assign to a var, a field, or an element.
order: 5
---
Assign to a <code>var</code>, a field, or an element. Not an expression.

View file

@ -0,0 +1,11 @@
---
id: op-bitwise
name: Bitwise
category: operators
kind: operator
sig: & | ^ ~ << >>
tip: And, or, xor, not, shift left/right.
order: 3
---
And, or, xor, not, shift left/right. Shifts and <code>&amp;</code> bind like <code>*</code>; <code>|</code>/<code>^</code> bind like <code>+</code> — tighter than comparison, so <code>flags &amp; MASK == 0</code> needs no parentheses.

View file

@ -0,0 +1,11 @@
---
id: op-comment
name: Comment
category: operators
kind: operator
sig: # to end of line
tip: Everything after # on a line is a comment.
order: 9
---
Everything after <code>#</code> on a line is a comment.

View file

@ -0,0 +1,11 @@
---
id: op-compare
name: Comparison
category: operators
kind: operator
sig: == != < <= > >=
tip: Yield a bool.
order: 1
---
Yield a <code>bool</code>. On strings, <code>==</code> compares contents.

View file

@ -0,0 +1,15 @@
---
id: op-interp
name: String interpolation
category: operators
kind: operator
sig: `text {expr} more`
tip: A backtick string with {expr} holes, each stringified and concatenated.
order: 7
---
A backtick string with <code>{expr}</code> holes, each stringified and concatenated. <code>{{</code> and <code>}}</code> are literal braces.
```ludic
print(`score: {score}`)
```

View file

@ -0,0 +1,11 @@
---
id: op-literals
name: Literals
category: operators
kind: operator
sig: 42 0x1E90FF 'w' "text" true null
tip: Decimal and hex ints (hex is how colors are written), a character code in single quotes, a string in double quotes, booleans, and the null pointer.
order: 8
---
Decimal and hex ints (hex is how colors are written), a character code in single quotes, a string in double quotes, booleans, and the null pointer.

View file

@ -0,0 +1,15 @@
---
id: op-logical
name: Logical
category: operators
kind: operator
sig: and or not
tip: Boolean combinators — words, not symbols.
order: 2
---
Boolean combinators — words, not symbols.
```ludic
if k != 0 and mode == 0 { … }
```

View file

@ -0,0 +1,11 @@
---
id: op-range
name: Range
category: operators
kind: operator
sig: a .. b
tip: A half-open range for for loops: a up to but not including b.
order: 4
---
A half-open range for <code>for</code> loops: <code>a</code> up to but not including <code>b</code>.

View file

@ -0,0 +1,7 @@
---
id: random
title: Random
order: 7
---
A seeded, deterministic RNG — same seed, same sequence, every run and every platform.

View file

@ -0,0 +1,12 @@
---
id: random-chance
name: Random.chance
category: random
kind: namespace-method
tokens: Random.chance
sig: Random.chance(percent) -> bool
tip: True with the given percent probability.
order: 1
---
True with the given percent probability.

View file

@ -0,0 +1,16 @@
---
id: random-range
name: Random.range
category: random
kind: namespace-method
tokens: Random.range
sig: Random.range(low, high) -> int
tip: A random integer in the inclusive range [low, high].
order: 0
---
A random integer in the inclusive range [low, high].
```ludic
food_x = Random.range(low: 0, high: GRID_W - 1)
```

View file

@ -0,0 +1,12 @@
---
id: random-seed
name: Random.seed
category: random
kind: namespace-method
tokens: Random.seed
sig: Random.seed(value)
tip: Seed the RNG.
order: 2
---
Seed the RNG. Seeding with the same value makes runs reproducible.

View file

@ -0,0 +1,7 @@
---
id: scenes
title: Scenes & layers
order: 3
---
One active scene at a time, each grouping handlers into layers.

View file

@ -0,0 +1,12 @@
---
id: kw-enter
name: enter
category: scenes
kind: keyword
tokens: enter
sig: on enter { … }
tip: The scene-entry hook.
order: 3
---
The scene-entry hook.

View file

@ -0,0 +1,12 @@
---
id: kw-exit
name: exit
category: scenes
kind: keyword
tokens: exit
sig: on exit { … }
tip: The scene-exit hook.
order: 4
---
The scene-exit hook.

View file

@ -0,0 +1,12 @@
---
id: kw-layer
name: layer
category: scenes
kind: keyword
tokens: layer
sig: layer Name { handlers }
tip: A group of handlers inside a scene; layers render in declaration order.
order: 1
---
A group of handlers inside a scene; layers render in declaration order.

View file

@ -0,0 +1,12 @@
---
id: kw-on
name: on enter / on exit
category: scenes
kind: keyword
tokens: on
sig: on enter { … } on exit { … }
tip: Hooks that fire when a scene becomes active or is left.
order: 2
---
Hooks that fire when a scene becomes active or is left.

View file

@ -0,0 +1,16 @@
---
id: kw-scene
name: scene
category: scenes
kind: keyword
tokens: scene
sig: scene Name [start] { on enter{} on exit{} layer … }
tip: A mutually-exclusive game state.
order: 0
---
A mutually-exclusive game state. Mark one <code>start</code>.
```ludic
scene Title start { on enter { … } layer Main { … } }
```

View file

@ -0,0 +1,7 @@
---
id: screen
title: Screen — drawing
order: 5
---
The 2D drawing surface. Every call takes named arguments; draw during the <code>Render</code> phase, then <code>Screen.show()</code>.

View file

@ -0,0 +1,16 @@
---
id: screen-clear
name: Screen.clear
category: screen
kind: namespace-method
tokens: Screen.clear
sig: Screen.clear(color)
tip: Fill the entire screen with one color.
order: 0
---
Fill the entire screen with one color.
```ludic
Screen.clear(Color.MidnightBlue)
```

View file

@ -0,0 +1,16 @@
---
id: screen-draw_number
name: Screen.draw_number
category: screen
kind: namespace-method
tokens: Screen.draw_number
sig: Screen.draw_number(x, y, value, color, scale)
tip: Draw an integer — no allocation, no string conversion.
order: 5
---
Draw an integer — no allocation, no string conversion.
```ludic
Screen.draw_number(x: 52, y: 4, value: score, color: Color.Gold, scale: 1)
```

View file

@ -0,0 +1,12 @@
---
id: screen-draw_rectangle
name: Screen.draw_rectangle
category: screen
kind: namespace-method
tokens: Screen.draw_rectangle
sig: Screen.draw_rectangle(x, y, width, height, color)
tip: Draw a one-pixel rectangle outline.
order: 2
---
Draw a one-pixel rectangle outline.

View file

@ -0,0 +1,16 @@
---
id: screen-draw_text
name: Screen.draw_text
category: screen
kind: namespace-method
tokens: Screen.draw_text
sig: Screen.draw_text(x, y, text, color, scale)
tip: Draw a string with the built-in font at an integer scale.
order: 4
---
Draw a string with the built-in font at an integer scale.
```ludic
Screen.draw_text(x: 6, y: 4, text: "SCORE", color: Color.White, scale: 1)
```

View file

@ -0,0 +1,16 @@
---
id: screen-fill_rectangle
name: Screen.fill_rectangle
category: screen
kind: namespace-method
tokens: Screen.fill_rectangle
sig: Screen.fill_rectangle(x, y, width, height, color)
tip: Draw a filled rectangle.
order: 1
---
Draw a filled rectangle.
```ludic
Screen.fill_rectangle(x: 8, y: 8, width: 16, height: 16, color: Color.Crimson)
```

View file

@ -0,0 +1,12 @@
---
id: screen-height
name: Screen.height
category: screen
kind: namespace-method
tokens: Screen.height
sig: Screen.height() -> int
tip: Framebuffer height in pixels.
order: 8
---
Framebuffer height in pixels.

View file

@ -0,0 +1,12 @@
---
id: screen-put_pixel
name: Screen.put_pixel
category: screen
kind: namespace-method
tokens: Screen.put_pixel
sig: Screen.put_pixel(x, y, color)
tip: Set a single pixel.
order: 3
---
Set a single pixel.

View file

@ -0,0 +1,16 @@
---
id: screen-show
name: Screen.show
category: screen
kind: namespace-method
tokens: Screen.show
sig: Screen.show()
tip: Present the finished frame — copy everything you have drawn to the window.
order: 6
---
Present the finished frame — copy everything you have drawn to the window. Call it once, last, in your Render handler.
```ludic
Screen.show()
```

View file

@ -0,0 +1,12 @@
---
id: screen-status
name: Screen.status
category: screen
kind: namespace-method
tokens: Screen.status
sig: Screen.status(text)
tip: Set the persistent one-line status/HUD string.
order: 9
---
Set the persistent one-line status/HUD string.

View file

@ -0,0 +1,12 @@
---
id: screen-width
name: Screen.width
category: screen
kind: namespace-method
tokens: Screen.width
sig: Screen.width() -> int
tip: Framebuffer width in pixels.
order: 7
---
Framebuffer width in pixels.

View file

@ -0,0 +1,7 @@
---
id: structure
title: Program structure
order: 0
---
The shape of a Ludic program: one <code>program</code> block holding declarations.

View file

@ -0,0 +1,12 @@
---
id: kw-const
name: const
category: structure
kind: keyword
tokens: const
sig: const NAME: T = value
tip: A compile-time constant.
order: 5
---
A compile-time constant. Folds directly into the code — no storage, no cost.

View file

@ -0,0 +1,12 @@
---
id: kw-extern
name: extern
category: structure
kind: keyword
tokens: extern
sig: extern fn name(a: T) -> R = "symbol"
tip: Bind a name to an external C-ABI symbol — the seam for platform and library calls.
order: 12
---
Bind a name to an external C-ABI symbol — the seam for platform and library calls.

View file

@ -0,0 +1,12 @@
---
id: kw-fn
name: fn
category: structure
kind: keyword
tokens: fn
sig: fn name(a: T, b: T) -> R { … }
tip: A function.
order: 8
---
A function. Call it positionally or with named arguments: <code>name(a: 1, b: 2)</code>.

View file

@ -0,0 +1,16 @@
---
id: kw-handler
name: handler
category: structure
kind: keyword
tokens: handler
sig: handler Name phase P { … }
tip: A block of code the engine runs every frame during phase P.
order: 3
---
A block of code the engine runs every frame during phase <code>P</code>. With a query, the body runs once per matching entity.
```ludic
handler Move phase Update { … }
```

View file

@ -0,0 +1,12 @@
---
id: kw-import
name: import
category: structure
kind: keyword
tokens: import
sig: import "file.ludic"
tip: Splice another Ludic file into this program.
order: 10
---
Splice another Ludic file into this program. Paths resolve relative to the importer; re-imports are free.

View file

@ -0,0 +1,12 @@
---
id: kw-let
name: let
category: structure
kind: keyword
tokens: let
sig: let name = value
tip: An immutable binding, scoped to the block it appears in.
order: 7
---
An immutable binding, scoped to the block it appears in.

View file

@ -0,0 +1,16 @@
---
id: kw-model
name: model
category: structure
kind: keyword
tokens: model
sig: model Name { PropA, PropB, … }
tip: A named bundle of properties, so an entity that always travels together is spawned by one name.
order: 2
---
A named bundle of properties, so an entity that always travels together is spawned by one name.
```ludic
model Player { Health, Shield }
```

View file

@ -0,0 +1,12 @@
---
id: kw-module
name: module
category: structure
kind: keyword
tokens: module
sig: module Name { @export fn … }
tip: Build a shared library of plain C-ABI symbols instead of an executable.
order: 11
---
Build a shared library of plain C-ABI symbols instead of an executable.

View file

@ -0,0 +1,12 @@
---
id: kw-phase
name: phase
category: structure
kind: phase
tokens: Start Input Update FixedUpdate Render
sig: phase Start | Input | Update | FixedUpdate | Render
tip: When a handler runs.
order: 4
---
When a handler runs. <b>Start</b> once at boot; <b>Input</b> reads the keyboard; <b>Update</b> is the per-frame step; <b>FixedUpdate</b> is the deterministic fixed-step; <b>Render</b> draws the frame.

View file

@ -0,0 +1,12 @@
---
id: kw-program
name: program
category: structure
kind: keyword
tokens: program
sig: program Name { … }
tip: The top-level unit.
order: 0
---
The top-level unit. A program compiles to one native game; everything else lives inside it.

View file

@ -0,0 +1,16 @@
---
id: kw-property
name: property
category: structure
kind: keyword
tokens: property
sig: property Name { field: T = default, … }
tip: A component: a named record of fields an entity can carry.
order: 1
---
A component: a named record of fields an entity can carry. Fields have a type and a default.
```ludic
property Pos { x: int = 0, y: int = 0 }
```

View file

@ -0,0 +1,12 @@
---
id: kw-return
name: return
category: structure
kind: keyword
tokens: return
sig: return value
tip: Return from a function.
order: 9
---
Return from a function.

View file

@ -0,0 +1,16 @@
---
id: kw-var
name: var
category: structure
kind: keyword
tokens: var
sig: var name: T = value
tip: A mutable binding.
order: 6
---
A mutable binding. At program scope it is your game's persistent, named state — the modern replacement for numeric registers.
```ludic
var score: int = 0
```

View file

@ -0,0 +1,7 @@
---
id: types
title: Types
order: 10
---
Ludic is statically typed; most code uses just <code>int</code>.

Some files were not shown because too many files have changed in this diff Show more