parser: an attribute before export is kept - @ToClients export event E was a local event (the attributes read in front of export were dropped when the declaration was parsed afresh one call down), as were @Sync / @Owned / @Server / @On / @Public ... before export; and export @ToClients event was refused. Attributes and export now read in either order into one declaration, and a wrapper marks what it added exported (g_at_keep goes); examples/networking/net_export and three schema cases (added, not run); reseeded, bootstrap-cfree fixpoint holds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-30 01:03:44 +03:00
parent 64adb052b4
commit 26b986de5b
7 changed files with 89034 additions and 88841 deletions

View file

@ -36,7 +36,6 @@ var g_at_nargs: []int = new []int
var g_at_unres: []bool = new []bool # its target is not in this program (a warning; the schema says so)
var g_at_open: int = -1 # the group the attributes being read go into
var g_at_decl: []pointer = new []pointer # @AppendOnly / @ByKey read before a declaration
var g_at_keep: bool = false # `export` parses its declaration one call down
# a new group, empty; its index
function at_group() -> int {
@ -165,10 +164,6 @@ function at_decl_with(a: pointer, args: pointer) -> void {
push(g_at_decl_args, args)
}
function at_decl_reset() -> void {
if g_at_keep {
g_at_keep = false
return
}
if len(g_at_decl) > 0 { at_decl_clear() }
}
function at_decl_clear() -> void {

View file

@ -982,7 +982,36 @@ function decl_hook_target(ev: pointer, sp: pointer, dsp: pointer, att: pointer,
# parse one top-level declaration (or resolve an import) into `prog`.
# Modifiers are `@annotations` in front of the declaration: `@export`, `@edge`,
# `@pure`, `@deterministic`, … — one channel, not a zoo of prefix keywords.
var g_decl_exported: bool = false # `export` read in front of the declaration being parsed
function parse_one_decl() -> void {
let p0 = len(prog)
let e0 = len(g_events)
let r0 = len(g_rg_var)
let outer = g_decl_exported
g_decl_exported = false
parse_one_decl_in()
if g_decl_exported { decl_mark_exported(p0, e0, r0) }
g_decl_exported = outer
}
# `export function f`, `export var v`, ...: what the declaration added is visible from other modules (L3)
function decl_mark_exported(p0: int, e0: int, r0: int) -> void {
var k = p0
while k < len(prog) {
prog[k].vis = 1
k += 1
}
k = r0
while k < len(g_rg_var) { # a @PerMap registry's own node is not in prog
g_rg_var[k].vis = 1
k += 1
}
k = e0
while k < len(g_events) {
g_events[k].vis = 1
k += 1
}
}
function parse_one_decl_in() -> void {
at_decl_reset() # attrs.ludic: @AppendOnly / @ByKey belong to one declaration
var is_export = false
var qspec: Node = null
@ -1005,7 +1034,14 @@ function parse_one_decl() -> void {
var esys_phase: pointer = null
var ns_name: pointer = null # @Namespace(Name) — a package-provided Foo.* namespace (#62)
var is_det = false # @deterministic — no floating point inside (emit_float.ludic)
while is_op("@") {
# attributes and `export` in any order: `@ToClients export event E` keeps its @ToClients
while is_op("@") or (is_id("export") and (toks[pi + 1].kind == TK_ID or toks[pi + 1].text == "@")) {
if is_id("export") {
pi += 1
g_decl_exported = true
skipnl()
continue
}
pi += 1; let a = eat_id() # collect a leading @annotation
if a == "export" { is_export = true }
else if a == "Public" { is_public = true } # @Public hook promotion
@ -1045,7 +1081,7 @@ function parse_one_decl() -> void {
let why = expr()
if why.kind != E_STR or len(why.s) == 0 { perr("@alloc_ok needs its reason: @alloc_ok(\"a memo miss, bounded by MM_CAP\")") }
eat_op(")")
g_aok_pending = true # a global: `export` parses the declaration one call down
g_aok_pending = true # a global: the declaration after the attributes reads it
}
else if a == "creates" or a == "releases" { # 25.5e: @creates(PhysShape) / @releases(PhysShape)
eat_op("(")
@ -1073,31 +1109,6 @@ function parse_one_decl() -> void {
mod_parse_line() # module NAME [uses A, B]
return
}
# `export function f`, `export var v`, ...: visible from other modules (L3)
if is_id("export") and (toks[pi + 1].kind == TK_ID) {
pi += 1
g_at_keep = true
let p0 = len(prog)
let e0 = len(g_events)
let r0 = len(g_rg_var)
parse_one_decl()
var k = p0
while k < len(prog) {
prog[k].vis = 1
k += 1
}
k = r0
while k < len(g_rg_var) { # a @PerMap registry's own node is not in prog
g_rg_var[k].vis = 1
k += 1
}
k = e0
while k < len(g_events) {
g_events[k].vis = 1
k += 1
}
return
}
if is_id("numbers") and (toks[pi + 1].text == "float") {
pi += 2
if not is_float_file(g_parse_file) { float_file_add(g_parse_file) }