fix(lang): the action drain is the program's; --check runs every check a build makes; a registry key named count is refused; name lookups are tables
- the function that calls every reducer (and the action queue) is written in the program's own file: in the first action's file it belonged to that module, depended on every module with a reducer, and joined a game's modules into one 69-module cycle (examples/actions/modules and a ludic deps case hold it); the state instances, the queue and the reducers make no deps edges - ludicc --check / ludic build --check lower the program too and write nothing, so the code writer's refusals are in it: a bind to a function that is gone, an unknown name (and the checker now refuses fn <missing> itself); rejects bind_missing_fn, unknown_name, registry_count_key - def R count is refused: its constant would be PREFIX_COUNT, the registry's size - a file's module, package, trust and numbers-float are tables, and from the check on the lookups of functions, enums, records, globals and externs are too (tagged enums kept as a list): Maroon Lake's check-only build went from about 20 s to 7 s including lowering, its IR from about 2 minutes to under 10 s; duplicate declarations are found by table, not a pair of loops - threads.ludic's pool check gives each call a little work, so a busy machine cannot run them all on the caller before a worker wakes (it failed one run in three under load) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2fdefa6040
commit
8cf4b3fed6
24 changed files with 67169 additions and 64398 deletions
|
|
@ -151,7 +151,94 @@ function find_arch(name: pointer) -> Node {
|
|||
while i < len(prog) { let d = prog[i]; if d.kind == N_ARCH and (d.s == name) { return d }; i += 1 }
|
||||
return null
|
||||
}
|
||||
# ---- the emitter's lookups by name: tables, once the program is whole ---------------------
|
||||
# find_fn / find_enum / find_comp / find_global / find_extern are asked for every name the emitter
|
||||
# lowers; walking prog for each was most of a big program's build. From emit_program on, each kind
|
||||
# is a table, kept up with what is appended to prog; a hit whose node has since been renamed falls
|
||||
# back to the walk.
|
||||
const IX_SLOTS: int = 131072
|
||||
var g_ix_on: bool = false
|
||||
var g_ix_tagged: []Node = new []Node
|
||||
var g_ix_upto: int = 0
|
||||
var g_ix_keys: [][]pointer = new [][]pointer # per kind: 0 fn, 1 enum, 2 comp, 3 global, 4 extern
|
||||
var g_ix_vals: [][]pointer = new [][]pointer
|
||||
function ix_start() -> void {
|
||||
g_ix_keys = new [][]pointer
|
||||
g_ix_vals = new [][]pointer
|
||||
var t = 0
|
||||
while t < 5 {
|
||||
let k = new []pointer
|
||||
let v = new []pointer
|
||||
var i = 0
|
||||
while i < IX_SLOTS {
|
||||
push(k, null)
|
||||
push(v, null)
|
||||
i += 1
|
||||
}
|
||||
push(g_ix_keys, k)
|
||||
push(g_ix_vals, v)
|
||||
t += 1
|
||||
}
|
||||
g_ix_upto = 0
|
||||
g_ix_tagged = new []Node
|
||||
g_ix_on = true
|
||||
}
|
||||
function ix_kind(d: Node) -> int {
|
||||
if d.kind == N_FN { return 0 }
|
||||
if d.kind == N_ENUM { return 1 }
|
||||
if d.kind == N_COMP { return 2 }
|
||||
if d.kind == N_VAR or d.kind == N_CONST { return 3 }
|
||||
if d.kind == N_EXTERN { return 4 }
|
||||
return -1
|
||||
}
|
||||
function ix_slot(t: int, name: pointer) -> int {
|
||||
var h = 5381
|
||||
var i = 0
|
||||
let n = len(name)
|
||||
while i < n {
|
||||
h = (h * 33 + name[i]) % 1000003
|
||||
i += 1
|
||||
}
|
||||
h = h % IX_SLOTS
|
||||
let ks = g_ix_keys[t]
|
||||
while ks[h] != null and not (ks[h] == name) { h = (h + 1) % IX_SLOTS }
|
||||
return h
|
||||
}
|
||||
function ix_sync() -> void {
|
||||
while g_ix_upto < len(prog) {
|
||||
let d = prog[g_ix_upto]
|
||||
let t = ix_kind(d)
|
||||
if d.kind == N_ENUM and enum_is_tagged(d) { push(g_ix_tagged, d) }
|
||||
if t >= 0 and d.s != null {
|
||||
let h = ix_slot(t, d.s)
|
||||
if g_ix_keys[t][h] == null { # the first of a name wins, as the walk had it
|
||||
g_ix_keys[t][h] = d.s
|
||||
g_ix_vals[t][h] = d
|
||||
}
|
||||
}
|
||||
g_ix_upto += 1
|
||||
}
|
||||
}
|
||||
# the node of kind t called name, or null; -1 when the table cannot say (off, or a stale hit)
|
||||
var g_ix_miss: bool = false
|
||||
function ix_find(t: int, name: pointer) -> Node {
|
||||
g_ix_miss = false
|
||||
if not g_ix_on or name == null {
|
||||
g_ix_miss = true
|
||||
return null
|
||||
}
|
||||
ix_sync()
|
||||
let h = ix_slot(t, name)
|
||||
let p = g_ix_vals[t][h]
|
||||
if p == null { return null }
|
||||
let d: Node = p
|
||||
if (d.s == name) and ix_kind(d) == t { return d }
|
||||
g_ix_miss = true
|
||||
return null
|
||||
}
|
||||
function find_comp(name: pointer) -> Node {
|
||||
let x = ix_find(2, name)
|
||||
if not g_ix_miss { return x }
|
||||
var i = 0
|
||||
while i < len(prog) { let d = prog[i]; if d.kind == N_COMP and (d.s == name) { return d }; i += 1 }
|
||||
return null
|
||||
|
|
@ -174,6 +261,8 @@ function field_type(s: Node, fname: pointer) -> pointer {
|
|||
|
||||
# find a global var/const by name
|
||||
function find_global(name: pointer) -> Node {
|
||||
let x = ix_find(3, name)
|
||||
if not g_ix_miss { return x }
|
||||
var i = 0
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
|
|
@ -188,6 +277,14 @@ function find_global(name: pointer) -> Node {
|
|||
# enum with that variant. Enum names live in `prog` like any other declaration.
|
||||
function enum_ordinal(ename: pointer, vname: pointer) -> int {
|
||||
var i = 0
|
||||
if g_ix_on { # the table knows the one enum of that name
|
||||
let d = find_enum(ename)
|
||||
if d != null {
|
||||
var j = 0
|
||||
while j < len(d.kids) { if (d.kids[j].s == vname) { return j }; j += 1 }
|
||||
}
|
||||
i = len(prog)
|
||||
}
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
if d.kind == N_ENUM and (d.s == ename) {
|
||||
|
|
@ -234,6 +331,8 @@ function has_countdowns() -> bool {
|
|||
return false
|
||||
}
|
||||
function find_fn(name: pointer) -> Node {
|
||||
let x = ix_find(0, name)
|
||||
if not g_ix_miss { return x }
|
||||
var i = 0
|
||||
while i < len(prog) { let d = prog[i]; if d.kind == N_FN and (d.s == name) { return d }; i += 1 }
|
||||
return null
|
||||
|
|
@ -248,6 +347,8 @@ function find_fn(name: pointer) -> Node {
|
|||
var g_var_ord: int = 0
|
||||
|
||||
function find_enum(name: pointer) -> Node {
|
||||
let x = ix_find(1, name)
|
||||
if not g_ix_miss { return x }
|
||||
var i = 0
|
||||
while i < len(prog) { let d = prog[i]; if d.kind == N_ENUM and (d.s == name) { return d }; i += 1 }
|
||||
return null
|
||||
|
|
@ -276,6 +377,16 @@ function variant_ordinal(en: Node, vname: pointer) -> int {
|
|||
# Only tagged enums participate, so a plain enum's `Enum.Variant` member form and
|
||||
# any like-named function are left alone.
|
||||
function variant_enum(vname: pointer) -> Node {
|
||||
if g_ix_on { # the tagged enums, kept as prog grows
|
||||
ix_sync()
|
||||
var t = 0
|
||||
while t < len(g_ix_tagged) {
|
||||
let ord = variant_ordinal(g_ix_tagged[t], vname)
|
||||
if ord >= 0 { g_var_ord = ord; return g_ix_tagged[t] }
|
||||
t += 1
|
||||
}
|
||||
return null
|
||||
}
|
||||
var i = 0
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
|
|
@ -306,6 +417,8 @@ function enum_box_size(en: Node) -> int { return 8 * (1 + enum_max_arity(en)) }
|
|||
# is the transport seam (net_send/net_poll), the windowing/socket FFI, and any
|
||||
# C/Rust/Zig library binding — the same seam NETWORKING-DESIGN §5 names.
|
||||
function find_extern(name: pointer) -> Node {
|
||||
let x = ix_find(4, name)
|
||||
if not g_ix_miss { return x }
|
||||
var i = 0
|
||||
while i < len(prog) { let d = prog[i]; if d.kind == N_EXTERN and (d.s == name) { return d }; i += 1 }
|
||||
return null
|
||||
|
|
|
|||
|
|
@ -213,19 +213,19 @@ function emit_test_runner() -> void {
|
|||
# two `function`s with one name would collide in the object file; say so in
|
||||
# source terms (and name both files) instead of leaving it to the IR assembler.
|
||||
function check_duplicate_fns() -> void {
|
||||
let k = new []pointer # by name, in a table: a pair of loops was quadratic
|
||||
let v = new []Node
|
||||
ck_tab_init(k, v)
|
||||
var i = 0
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
if d.kind == N_FN {
|
||||
var j = i + 1
|
||||
while j < len(prog) {
|
||||
let o = prog[j]
|
||||
if o.kind == N_FN and (o.s == d.s) {
|
||||
g_err_file = o.file; g_err_line = o.line
|
||||
perr(`function '{d.s}' is defined twice (first in {d.file}:{itoa(d.line)})`)
|
||||
}
|
||||
j += 1
|
||||
let first = ck_tab_get(k, v, d.s)
|
||||
if first != null {
|
||||
g_err_file = d.file; g_err_line = d.line
|
||||
perr(`function '{d.s}' is defined twice (first in {first.file}:{itoa(first.line)})`)
|
||||
}
|
||||
ck_tab_put(k, v, d.s, d)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
|
@ -240,22 +240,23 @@ function decl_group(k: int) -> int {
|
|||
return 0
|
||||
}
|
||||
function check_duplicate_decls() -> void {
|
||||
let k = new []pointer
|
||||
let v = new []Node
|
||||
ck_tab_init(k, v)
|
||||
let n = g_prog_user_end
|
||||
var i = 0
|
||||
while i < n and i < len(prog) {
|
||||
let d = prog[i]
|
||||
let g = decl_group(d.kind)
|
||||
if g > 0 {
|
||||
var j = i + 1
|
||||
while j < n and j < len(prog) {
|
||||
let o = prog[j]
|
||||
if decl_group(o.kind) == g and (o.s == d.s) {
|
||||
g_err_file = o.file
|
||||
g_err_line = o.line
|
||||
perr(`'{d.s}' is defined twice (first in {d.file}:{itoa(d.line)})`)
|
||||
}
|
||||
j += 1
|
||||
let key = `{itoa(g)}:{d.s}`
|
||||
let first = ck_tab_get(k, v, key)
|
||||
if first != null {
|
||||
g_err_file = d.file
|
||||
g_err_line = d.line
|
||||
perr(`'{d.s}' is defined twice (first in {first.file}:{itoa(first.line)})`)
|
||||
}
|
||||
ck_tab_put(k, v, key, d)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
|
@ -264,6 +265,7 @@ function check_duplicate_decls() -> void {
|
|||
function emit_program() -> void {
|
||||
check_duplicate_fns()
|
||||
check_duplicate_decls()
|
||||
ix_start() # the lookups by name, as tables from here on
|
||||
head = buf_new()
|
||||
code = buf_new()
|
||||
g_uses_str = false
|
||||
|
|
|
|||
|
|
@ -29,17 +29,29 @@ function vis_plain(what: pointer) -> pointer {
|
|||
function vis_say(m: pointer) -> void {
|
||||
file_write(file_stderr(), m, len(m))
|
||||
}
|
||||
# does s start with p - without making the slice a comparison of s[0 .. n] would (asked for every
|
||||
# reference)
|
||||
function str_prefix(s: pointer, p: pointer) -> bool {
|
||||
if s == null { return false }
|
||||
let n = len(p)
|
||||
if len(s) < n { return false }
|
||||
var i = 0
|
||||
while i < n {
|
||||
if s[i] != p[i] { return false }
|
||||
i += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
function vis_check(d: Node, what0: pointer) -> void {
|
||||
if d == null { return }
|
||||
port_check_bound(d)
|
||||
# 0.S/0.R: a state's instance, the action queue and a reducer are the runtime's to supply and to
|
||||
# call - the code that names them depends on no module for it
|
||||
if d.kind == N_VAR and d.uns == 1 and str_prefix(d.s, "state$") and is_state_ty(d.ty) { return }
|
||||
if d.kind == N_FN and (str_prefix(d.s, "ludic_reduce__") or is_action_builtin(d.s)) { return }
|
||||
deps_edge(d, what0) # LUDIC_DEPS: the graph ludic deps reads (emit_deps.ludic)
|
||||
if g_vis_off { return }
|
||||
if d.file == null { return }
|
||||
# 0.S: a state's instance, supplied by the runtime (a component's or a view's glue, an entry's
|
||||
# parameters): the code that names it only passes it on
|
||||
if d.kind == N_VAR and d.uns == 1 and is_state_ty(d.ty) and len(d.s) > 6 and (d.s[0 .. 6] == "state$") { return }
|
||||
# 0.R: the action queue and the reducers are the runtime's to call, from any module
|
||||
if d.kind == N_FN and (is_action_builtin(d.s) or (len(d.s) > 14 and (d.s[0 .. 14] == "ludic_reduce__"))) { return }
|
||||
let what = vis_plain(what0)
|
||||
var here = g_err_file
|
||||
if here == null { return }
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ function ck_expr(e: Node) -> pointer {
|
|||
let f = ck_fn(e.s)
|
||||
ck_vis(f, e.s, e)
|
||||
if f != null { return fn_sig_of(f) }
|
||||
# the emitter's own refusal, here too, so a check-only build finds it (a bind to a function
|
||||
# that is gone)
|
||||
if find_fn(e.s) == null { ck_err("fnref", e, `fn {e.s}: no function called {e.s}`) }
|
||||
return "?"
|
||||
}
|
||||
if k == E_NEW { return ck_new(e) }
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ var g_red_action: []pointer = new []pointer
|
|||
var g_dsp_ids: []Node = new []Node # each dispatch's action number, filled at the end
|
||||
var g_dsp_names: []pointer = new []pointer
|
||||
var g_dsp_at: []Node = new []Node
|
||||
var g_act_main: pointer = null # the program's own file, where the drain is written
|
||||
const ACTION_PASSES: int = 64
|
||||
|
||||
# action NAME { fields } - a record
|
||||
|
|
@ -101,12 +102,11 @@ function actions_finish() -> void {
|
|||
g_dsp_ids[i].ival = k
|
||||
i += 1
|
||||
}
|
||||
# the queue and the drain are the program's, written in its own file: in the file of the first
|
||||
# action they would belong to its module, and it would call every module's reducers
|
||||
var file = g_parse_file
|
||||
var line = 1
|
||||
if len(g_act_nodes) > 0 {
|
||||
file = g_act_nodes[0].file
|
||||
line = g_act_nodes[0].line
|
||||
}
|
||||
if g_act_main != null { file = g_act_main }
|
||||
let line = 1
|
||||
# no actions: drain_actions() is still there (a ludic.base runner calls it), and does nothing
|
||||
if len(g_act_names) == 0 {
|
||||
vw_parse("export function drain_actions() -> void {\n}\n", file, line, 1)
|
||||
|
|
|
|||
|
|
@ -128,15 +128,9 @@ function pkg_name_of(rel: pointer) -> pointer {
|
|||
function pkg_file_set(f: pointer, name: pointer) -> void {
|
||||
push(g_pk_file, f)
|
||||
push(g_pk_name, name)
|
||||
fm_put(g_fm_pkg, f, name)
|
||||
}
|
||||
function pkg_of_file(f: pointer) -> pointer {
|
||||
var i = len(g_pk_file) - 1
|
||||
while i >= 0 {
|
||||
if (g_pk_file[i] == f) { return g_pk_name[i] }
|
||||
i -= 1
|
||||
}
|
||||
return ""
|
||||
}
|
||||
function pkg_of_file(f: pointer) -> pointer { return fm_get(g_fm_pkg, f) }
|
||||
# the module a declaration is in, for the uses rule: its own, else its package's
|
||||
function module_for_uses(f: pointer) -> pointer {
|
||||
let m = module_of(f)
|
||||
|
|
|
|||
|
|
@ -25,36 +25,77 @@ var g_trusted_files: []pointer = new []pointer
|
|||
var g_unsafe_all: bool = false
|
||||
function unsafe_trusted(f: pointer) -> bool {
|
||||
if g_unsafe_all { return true }
|
||||
return not (fm_get(g_fm_trusted, f) == "")
|
||||
}
|
||||
# a file's facts - its module, its package, whether it is trusted - asked of every reference the
|
||||
# checker and the emitter resolve: a table by file name, not a list walked each time (with a game's
|
||||
# nine hundred files that walk was most of a check-only build)
|
||||
const FM_SLOTS: int = 16384
|
||||
property FileMap {
|
||||
keys: []pointer = null
|
||||
vals: []pointer = null
|
||||
}
|
||||
function fm_new() -> FileMap {
|
||||
let m = new FileMap
|
||||
m.keys = new []pointer
|
||||
m.vals = new []pointer
|
||||
var i = 0
|
||||
while i < len(g_trusted_files) {
|
||||
if (g_trusted_files[i] == f) { return true }
|
||||
while i < FM_SLOTS {
|
||||
push(m.keys, null)
|
||||
push(m.vals, null)
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
return m
|
||||
}
|
||||
function fm_slot(m: FileMap, f: pointer) -> int {
|
||||
var h = 5381
|
||||
var i = 0
|
||||
let n = len(f)
|
||||
while i < n {
|
||||
h = (h * 33 + f[i]) % 1000003
|
||||
i += 1
|
||||
}
|
||||
h = h % FM_SLOTS
|
||||
while m.keys[h] != null and not (m.keys[h] == f) { h = (h + 1) % FM_SLOTS }
|
||||
return h
|
||||
}
|
||||
# the latest value set for f wins, as the lists' walk from the end had it
|
||||
function fm_put(m: FileMap, f: pointer, v: pointer) -> void {
|
||||
if f == null { return }
|
||||
let h = fm_slot(m, f)
|
||||
m.keys[h] = f
|
||||
m.vals[h] = v
|
||||
}
|
||||
function fm_get(m: FileMap, f: pointer) -> pointer {
|
||||
if f == null { return "" }
|
||||
let h = fm_slot(m, f)
|
||||
if m.keys[h] == null { return "" }
|
||||
return m.vals[h]
|
||||
}
|
||||
var g_fm_mod: FileMap = fm_new()
|
||||
var g_fm_pkg: FileMap = fm_new()
|
||||
var g_fm_trusted: FileMap = fm_new()
|
||||
function trust_file(f: pointer) -> void {
|
||||
push(g_trusted_files, f)
|
||||
fm_put(g_fm_trusted, f, "1")
|
||||
}
|
||||
var g_mod_file: []pointer = new []pointer
|
||||
var g_mod_name: []pointer = new []pointer
|
||||
var g_mod_friends: []pointer = new []pointer
|
||||
function module_of(f: pointer) -> pointer {
|
||||
var i = len(g_mod_file) - 1
|
||||
while i >= 0 {
|
||||
if (g_mod_file[i] == f) { return g_mod_name[i] }
|
||||
i -= 1
|
||||
}
|
||||
return ""
|
||||
}
|
||||
function module_of(f: pointer) -> pointer { return fm_get(g_fm_mod, f) }
|
||||
function module_set(f: pointer, name: pointer) -> void {
|
||||
push(g_mod_file, f)
|
||||
push(g_mod_name, name)
|
||||
fm_put(g_fm_mod, f, name)
|
||||
}
|
||||
function is_float_file(f: pointer) -> bool {
|
||||
if (f == null) { return false }
|
||||
var i = 0
|
||||
while i < len(g_float_files) {
|
||||
if (g_float_files[i] == f) { return true }
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
return not (fm_get(g_fm_float, f) == "")
|
||||
}
|
||||
var g_fm_float: FileMap = fm_new()
|
||||
function float_file_add(f: pointer) -> void {
|
||||
push(g_float_files, f)
|
||||
fm_put(g_fm_float, f, "1")
|
||||
}
|
||||
var g_parsing: bool = true # false once lowering starts
|
||||
var g_err_file: pointer = "" # the statement being lowered
|
||||
|
|
@ -952,7 +993,7 @@ function parse_one_decl() -> void {
|
|||
}
|
||||
if is_id("numbers") and (toks[pi + 1].text == "float") {
|
||||
pi += 2
|
||||
if not is_float_file(g_parse_file) { push(g_float_files, g_parse_file) }
|
||||
if not is_float_file(g_parse_file) { float_file_add(g_parse_file) }
|
||||
return
|
||||
}
|
||||
if is_id("import") { pi += 1
|
||||
|
|
@ -1180,11 +1221,11 @@ function do_import(rel: pointer) -> void {
|
|||
}
|
||||
if already_loaded(full) { return }
|
||||
push(loaded_paths, full)
|
||||
if is_float_file(g_parse_file) and not is_runtime_path(rel) and not is_float_file(full) { push(g_float_files, full) }
|
||||
if is_float_file(g_parse_file) and not is_runtime_path(rel) and not is_float_file(full) { float_file_add(full) }
|
||||
# a module reaches as far as its own files: a package found through $LUDIC_HOME or
|
||||
# ludic_modules is not beside its importer and keeps its own module (or none)
|
||||
let beside = full == join_path(cur_dir, rel)
|
||||
if is_runtime_path(rel) or not beside or (beside and unsafe_trusted(g_parse_file)) { push(g_trusted_files, full) }
|
||||
if is_runtime_path(rel) or not beside or (beside and unsafe_trusted(g_parse_file)) { trust_file(full) }
|
||||
if beside and not is_runtime_path(rel) and not (module_of(g_parse_file) == "") { module_set(full, module_of(g_parse_file)) }
|
||||
if not beside and not is_runtime_path(rel) { pkg_file_set(full, pkg_name_of(rel)) } # modules.ludic
|
||||
if beside and not is_runtime_path(rel) and not (pkg_of_file(g_parse_file) == "") { pkg_file_set(full, pkg_of_file(g_parse_file)) }
|
||||
|
|
|
|||
|
|
@ -44,6 +44,12 @@ function reg_fill(r: int) -> void {
|
|||
j += 1
|
||||
}
|
||||
push(seen, key)
|
||||
if (reg_upper(key) == "COUNT") {
|
||||
g_err_file = rec.file
|
||||
g_err_line = rec.line
|
||||
g_parsing = false
|
||||
perr(`def {g_rg_name[r]} {key}: its constant would be {g_rg_prefix[r]}_COUNT, which is the registry's size; give the entry another key`)
|
||||
}
|
||||
if keyed and not reg_rec_has(rec, "key") {
|
||||
let fi = node(E_FINIT)
|
||||
fi.s = "key"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ function res_read_into(reg: pointer, from: pointer, who: pointer) -> void {
|
|||
let saved_file = g_parse_file
|
||||
let saved_parsing = g_parsing
|
||||
if not (module_of(saved_file) == "") { module_set(path, module_of(saved_file)) }
|
||||
if is_float_file(saved_file) and not is_float_file(path) { push(g_float_files, path) }
|
||||
if is_float_file(saved_file) and not is_float_file(path) { float_file_add(path) }
|
||||
g_parse_file = path
|
||||
g_parsing = true
|
||||
g_res_mode = true
|
||||
|
|
|
|||
65588
selfhost/ludicc.seed.ll
65588
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -188,6 +188,7 @@ entry {
|
|||
cur_dir = dir_of(path)
|
||||
g_src_name = base_name(path) # for panic/expect file:line messages
|
||||
g_parse_file = path # for the compiler's own file:line diagnostics
|
||||
g_act_main = path # 0.R: where the action drain is written
|
||||
lex(src)
|
||||
parse_program()
|
||||
g_prog_user_end = len(prog)
|
||||
|
|
@ -217,9 +218,15 @@ entry {
|
|||
check_duplicate_decls()
|
||||
g_mg_entry = path
|
||||
if g_migrate { mg_collect() } # 0.S2: the vars that move (migrate.ludic)
|
||||
ix_start() # the lookups by name, as tables (emit_core.ludic)
|
||||
check_program() # L4: the types agree before anything is emitted
|
||||
if g_migrate { mg_finish() }
|
||||
if g_check_only { exit(0) } # --check: the checks are all there is
|
||||
# --check: every check a build makes - the emitter refuses things too (a bind to a function that is
|
||||
# gone, an unknown name) - and nothing written
|
||||
if g_check_only {
|
||||
emit_program()
|
||||
exit(0)
|
||||
}
|
||||
emit_program()
|
||||
deps_flush() # LUDIC_DEPS=<file>: the module graph (ludic deps)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue