feat(lang): 0.S - components take states in their header; migrate writes them, never inside a name, never outside the programs given, never into a package's state

- component Name (a: mut A, b: B) { ... }: every getter, default, function and event takes the
  header's states before the instance; a member's call to another passes them on; the glue is
  supplied them; the template never sees them; a read-only one is read-only in every member
- ludic migrate state: a component's members' needs go into its header (added to an existing one,
  mut added where now changed); a field read or a member call inside a component is the compiler's,
  so nothing is written inside a name and no ', )' is left; an entry point that declares states
  already gets the rest after them
- a program's module named like a package gets <Name>AppState; a program's own file its own state;
  a friend module's files go by directory; a package's settable var stays state
- it writes only under the programs and directories given (and runtime/ with --runtime), and
  refuses the whole run naming any other file that would have to change
- a name a package already moved into its state is rewritten through it; a read of the runtime's
  var through the runtime function that answers it (gl_w: gl_width())
- a state's instance supplied by the runtime is not a uses reference
- tests: state/component, rejected/state_component_ro, rendering/ui_render3d, migrate component
  and foreign cases

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 17:11:15 +03:00
parent c167ecc714
commit ce80e64b24
23 changed files with 67967 additions and 61444 deletions

View file

@ -0,0 +1,7 @@
# 0.S: a component's states are read-only unless its header says mut
import "ludic.ui"
program StateComponentRo {
state Look { size: int = 1 }
import "state_component_ro/Shelf.ludic"
entry { print(1) }
}

View file

@ -0,0 +1,3 @@
component Shelf (look: Look) {
on grow() { look.size += 1 }
}

View file

@ -0,0 +1 @@
<div><button on-click="grow()">+</button></div>

View file

@ -0,0 +1,13 @@
# ui_render3d.ludic - ludic.ui drawn by render3d's overlay: importing its backend is enough to
# draw templates (the suite builds it, so the backend always compiles against the renderer)
program UiRender3d {
numbers float
import "ludic.render3d/r3d.ludic"
import "ludic.ui"
import "ludic.ui/render3d.ludic"
entry (ui_st: mut UiState) {
ui_render3d(ui_st)
ui_render3d_scale(ui_st, 1.5)
print("ok")
}
}

View file

@ -0,0 +1,33 @@
# component.ludic - 0.S: a component's members take the states its header names; the runtime
# supplies them when the template asks (Tally.ludic)
import "ludic.ui"
program StateComponent {
numbers float
state Score {
points: int = 0
bigs: int = 0
}
state Look {
prefix: string = "#"
big: int = 7
}
import "component_parts/Tally.ludic"
function all_text(n: UiNode) -> string {
var out = n.text
for i in 0 .. len(n.children) { out = out + all_text(n.children[i]) }
return out
}
function frame(ui_st: mut UiState) -> UiNode {
let root: UiNode = ui_nodes(ui_st, "Tally", null)
ui_place(ui_st, root, 0.0, 0.0, 300.0, 200.0)
return root
}
entry (ui_st: mut UiState, score: Score) {
var root = frame(ui_st)
ui_press(ui_st, root.children[0].children[1])
root = frame(ui_st)
ui_press(ui_st, root.children[0].children[1])
root = frame(ui_st)
print(`{all_text(root.children[0].children[0])} {score.points} {score.bigs}`)
}
}

View file

@ -0,0 +1,12 @@
# Tally.ludic - a component that reads and changes states: its header names them, every member
# takes them, and the template never sees them
component Tally (score: mut Score, look: Look) {
prop label: string
total: int = score.points
shown: string = `{look.prefix}{total}`
function big() -> bool { return total > look.big }
on add(n: int) {
score.points += n
if big() { score.bigs += 1 }
}
}

View file

@ -0,0 +1,4 @@
<div>
<p>{label} {shown}{big() ? ' big' : ''}</p>
<button on-click="add(5)">+5</button>
</div>