docs(api): per-symbol pages, fuzzy search, deep token linking, hover cards
All checks were successful
docs / build-and-deploy (push) Successful in 2s
All checks were successful
docs / build-and-deploy (push) Successful in 2s
Rebuild the API Reference around one page per symbol and richer, verified content.
Pages & navigation
- One HTML page per symbol (kw-*, type-*, phase-*, screen-*, fn-*, annot-*, op-*)
instead of a single scrolling page; namespace overview pages (ns-screen …
ns-color) and a searchable index (api.html) with client-side fuzzy search.
- Sticky-header scroll offset (scroll-margin) so a jumped-to entry/param/color is
never hidden, plus a flash highlight on the scrolled-to target.
Deep linking in every snippet & example
- Namespace members split: `Screen`→namespace page, `fill_rectangle`→method page;
`Color`→palette page, `Charcoal`→its swatch — separately.
- Named arguments (`width:`) link to that parameter's anchor on the method page.
- Hover any token for a summary card built from the real API data (symbols.json).
Content & coverage
- Full authoritative surface documented from the compiler: every keyword, type,
the 6 phases (Start/Input/FixedUpdate/Update/LateUpdate/Render, each its own
page), all 22 annotations, namespace methods with parameter docs, builtins,
the world_* reflection ABI, networking, operators — 155 symbols.
- Longer, clearer explanations; "model"/"model instance" terminology, not "entity";
descriptive identifiers in every example (Position{column,row}, Velocity{delta_x,
delta_y}, Health{current,maximum}, Player/Enemy) — no Pos/Seg/x/dx.
- Accuracy fixes from compiler ground-truth: world_count() takes no arg,
world_query_next(property, cursor) arg order, event fields bind by name; dropped
`when` and `module` (not in the self-hosted parser).
Tooling
- inventory.json + check.py: coverage guard (every symbol has a page), duplicate-
token guard, and broken-link guard — fail CI so docs can't drift.
- validate.py: compiles every ```ludic example against bin/ludicc (158 compile).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
25f987e30d
commit
3c7ec9b016
172 changed files with 5240 additions and 895 deletions
|
|
@ -3,9 +3,15 @@ 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).
|
||||
sig: x.field buffer[i] text[a..b]
|
||||
tip: Field access, element index, and string slice — usable as value or target.
|
||||
order: 6
|
||||
---
|
||||
|
||||
Field/method access, element index, and string slice (a fresh substring).
|
||||
The access operators reach into a compound value. `value.field` reads or writes a property field and chains freely (`route.next.column`); `buffer[index]` reads or writes one element of a slice or raw buffer; and `text[start..end]` produces a fresh substring of the bytes in the half-open range, while `text[index]` reads a single byte as an `int` code point. Each form works both as a <b>value</b> and as an <b>assignment target</b>, and they compose — `route[index].column = 0` is one address computation. Field access also binds method-style calls like `Screen.fill_rectangle`.
|
||||
|
||||
```ludic
|
||||
let head_column: int = segments[0].column
|
||||
segments[0].column = head_column + 1
|
||||
let extension: str = file_name[len(file_name) - 4 .. len(file_name)]
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,8 +4,14 @@ name: Arithmetic
|
|||
category: operators
|
||||
kind: operator
|
||||
sig: + - * / %
|
||||
tip: Add, subtract, multiply, integer-divide, remainder.
|
||||
tip: Add, subtract, multiply, integer-divide, and remainder — on int and fixed.
|
||||
order: 0
|
||||
---
|
||||
|
||||
Add, subtract, multiply, integer-divide, remainder. On <code>fixed</code> values the same symbols do fixed-point math.
|
||||
The arithmetic operators are `+` (add), `-` (subtract), `*` (multiply), `/` (divide), and `%` (remainder). On `int` values `/` is integer division that truncates and `%` gives the remainder — handy for wrapping a value or testing a period, as in `elapsed_frames % 30 == 0`. On `fixed` values the same symbols do fixed-point math (multiply and divide are scaled), and mixing an `int` with a `fixed` promotes the `int`. `*`, `/`, and `%` bind tighter than `+` and `-`, so `column * TILE_SIZE + 1` groups as expected.
|
||||
|
||||
```ludic
|
||||
let pixel_x: int = column * TILE_SIZE + 1
|
||||
let is_even_row: bool = row % 2 == 0
|
||||
let average: int = (current_health + maximum_health) / 2
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,8 +4,16 @@ name: Assignment
|
|||
category: operators
|
||||
kind: operator
|
||||
sig: name = value
|
||||
tip: Assign to a var, a field, or an element.
|
||||
tip: Store into a var, a field, or an element — a statement, not an expression.
|
||||
order: 5
|
||||
---
|
||||
|
||||
Assign to a <code>var</code>, a field, or an element. Not an expression.
|
||||
Assignment stores a value into a target — a `var` binding, a property field, or a buffer/slice element — with `target = value`. The compound forms `+=`, `-=`, `*=`, and `/=` update in place, so `score += 1` means `score = score + 1`. Assignment is a <b>statement, not an expression</b>: it produces no value, so you cannot write `if x = 0` (use `==` for the test). Only a mutable target accepts it — assigning to a `let` or `const` binding is a compile error — though you may still mutate <b>through</b> an immutable binding that holds a record or slice.
|
||||
|
||||
```ludic
|
||||
var score: int = 0
|
||||
score = 10
|
||||
score += 5
|
||||
let current_position = new_position()
|
||||
current_position.column = current_position.column + 1
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@ name: Bitwise
|
|||
category: operators
|
||||
kind: operator
|
||||
sig: & | ^ ~ << >>
|
||||
tip: And, or, xor, not, shift left/right.
|
||||
tip: Bit-level and, or, xor, not, and shifts — with Go-style precedence.
|
||||
order: 3
|
||||
---
|
||||
|
||||
And, or, xor, not, shift left/right. Shifts and <code>&</code> bind like <code>*</code>; <code>|</code>/<code>^</code> bind like <code>+</code> — tighter than comparison, so <code>flags & MASK == 0</code> needs no parentheses.
|
||||
The bitwise operators work on the bits of an `int`: `&` (and), `|` (or), `^` (xor), `~` (not), `<<` (shift left), and `>>` (a logical/unsigned shift right). They are the tools for flag sets, packing several small values into one integer, and fast multiply/divide by powers of two. Precedence is Go-style: `<<`, `>>`, and `&` bind like `*` (tightly), while `|` and `^` bind like `+`, and all of them bind <b>tighter than comparison</b> — so `flags & MASK == 0` means `(flags & MASK) == 0` with no parentheses.
|
||||
|
||||
```ludic
|
||||
const FLAG_POISONED: int = 1
|
||||
const FLAG_SHIELDED: int = 2
|
||||
var status_flags: int = FLAG_POISONED | FLAG_SHIELDED
|
||||
var is_shielded: bool = status_flags & FLAG_SHIELDED != 0
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,8 +4,14 @@ name: Comment
|
|||
category: operators
|
||||
kind: operator
|
||||
sig: # to end of line
|
||||
tip: Everything after # on a line is a comment.
|
||||
tip: Everything after # on a line is a comment, ignored by the compiler.
|
||||
order: 9
|
||||
---
|
||||
|
||||
Everything after <code>#</code> on a line is a comment.
|
||||
A comment begins with `#` and runs to the end of the line; the compiler ignores everything after it. Ludic has only this one line-comment form — there is no block-comment syntax — so to comment out several lines put a `#` on each. Use comments to explain intent next to a `const`, to label a section of a handler, or to note a gotcha. The source formatter (`ludic-fmt`) works on tokens, so your comments and blank lines survive a reformat.
|
||||
|
||||
```ludic
|
||||
const TILE_SIZE: int = 16 # pixels per grid cell
|
||||
var score: int = 0 # reset in the Start phase
|
||||
# the head is drawn brighter than the body
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,8 +4,13 @@ name: Comparison
|
|||
category: operators
|
||||
kind: operator
|
||||
sig: == != < <= > >=
|
||||
tip: Yield a bool.
|
||||
tip: Compare two values and yield a bool; on strings == compares contents.
|
||||
order: 1
|
||||
---
|
||||
|
||||
Yield a <code>bool</code>. On strings, <code>==</code> compares contents.
|
||||
The comparison operators — `==` (equal), `!=` (not equal), `<`, `<=`, `>`, `>=` — take two values and yield a `bool`, the kind of test an `if` or `where` clause wants. On `str` values `==` and `!=` compare <b>by content</b>, so `direction == "left"` checks the characters, not the pointer (a comparison against `null` stays a pointer test). Comparison binds looser than arithmetic and looser than the bitwise operators, so `flags & MASK == 0` reads as `(flags & MASK) == 0` with no parentheses needed. Chain several conditions together with `and` / `or`.
|
||||
|
||||
```ludic
|
||||
if current_health <= 0 { become GameOver }
|
||||
if pressed_key == 'w' and elapsed_frames > 0 { row = row - 1 }
|
||||
```
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ 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.
|
||||
A backtick string `` `…` `` is an interpolated string: any `{expr}` hole inside it is evaluated, converted to text, and concatenated with the surrounding literal parts. Numbers, `bool`s, and `fixed` values are stringified automatically and `str` values pass through, so `` `score: {score}` `` desugars to `"score: " + str(score)`. It is the readable way to build a message from mixed pieces without hand-writing a `+` chain. Write a literal brace with `{{` or `}}`.
|
||||
|
||||
```ludic
|
||||
print(`score: {score}`)
|
||||
let status_line: str = `score {score} — health {current_health}/{maximum_health}`
|
||||
Screen.status(status_line)
|
||||
print(`wave {wave_number} incoming`)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@ 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.
|
||||
tip: Integer, hex, character, string, boolean, and null-pointer literals.
|
||||
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.
|
||||
Literals are the fixed values you write directly in source. `42` is a decimal `int` and `0x1E90FF` is a hex `int` — hex is how colors are written, so a raw color is just an integer. A number with a decimal point (`1.5`) is a `fixed`. `'w'` is a character literal, an `int` code point handy for comparing against `Input.key()`. `"text"` is a `str`, `true` / `false` are `bool`s, and `null` is the null-pointer literal used to test an unset record, slice, or `ptr` field.
|
||||
|
||||
```ludic
|
||||
let sky_color: int = 0x1E90FF
|
||||
let banner: str = "GAME OVER"
|
||||
if Input.key() == 'w' { row = row - 1 }
|
||||
if next_segment == null { is_tail = true }
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ name: Logical
|
|||
category: operators
|
||||
kind: operator
|
||||
sig: and or not
|
||||
tip: Boolean combinators — words, not symbols.
|
||||
tip: The boolean combinators, spelled as words — never && or || or a bare !.
|
||||
order: 2
|
||||
---
|
||||
|
||||
Boolean combinators — words, not symbols.
|
||||
The boolean combinators are `and`, `or`, and `not`. They combine `bool` values — usually the results of comparisons — into the compound conditions an `if`, `while`, or `where` clause tests. Ludic spells them as <b>words, not symbols</b>: `&&` and `||` are not operators, and a bare `!` is rejected with a diagnostic pointing you to `not` (`!=` is unaffected). `and` binds tighter than `or`, so `a and b or c` groups as `(a and b) or c`; add parentheses when you mean otherwise.
|
||||
|
||||
```ludic
|
||||
if k != 0 and mode == 0 { … }
|
||||
if pressed_key != 0 and not is_paused { apply_input() }
|
||||
if current_health <= 0 or elapsed_frames > time_limit { become GameOver }
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,8 +4,16 @@ name: Range
|
|||
category: operators
|
||||
kind: operator
|
||||
sig: a .. b
|
||||
tip: A half-open range for for loops: a up to but not including b.
|
||||
tip: A half-open range for numeric for loops — from 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>.
|
||||
`a .. b` is a half-open numeric range used by the counting `for` loop: `for index in a .. b` walks `index` from `a` up to but <b>not including</b> `b`. This "up to but not including" convention makes lengths line up naturally — `0 .. len(route)` visits every element index of a slice, and `0 .. GRID_WIDTH` visits every column with no off-by-one. Both endpoints are `int` expressions, so a bound can be a `const`, a `var`, or any computed value. It is distinct from the string slice `text[start..end]`, which uses the same half-open idea for bytes.
|
||||
|
||||
```ludic
|
||||
for row in 0 .. GRID_HEIGHT {
|
||||
for column in 0 .. GRID_WIDTH {
|
||||
Screen.put_pixel(x: column, y: row, color: Color.MidnightBlue)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue