Baseline: Ludic compiler + toolchain, Phase 1 syntax fixes complete
Self-hosted compiler (selfhost/*.ludic), runtime, examples, editor tooling, and docs. Phase 1 of the syntax-redesign cohesion pass has landed: edge-system fix, signature-query, when-alias, and the documentation truth-pass. Suite green (14/14), C-free bootstrap fixpoint holds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
985f9ad8f2
418 changed files with 39065 additions and 0 deletions
139
selfhost/emit_core.ludic
Normal file
139
selfhost/emit_core.ludic
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# emit_core.ludic — emitter state, type mapping, struct/slice helpers, and the
|
||||
# module header. Mirrors the pieces of compiler/back/ that this subset needs.
|
||||
# structs and slices are references, so every non-scalar type lowers to `ptr`.
|
||||
|
||||
struct Val { code: ptr = ptr_null(), ty: ptr = ptr_null() }
|
||||
fn val(code: ptr, ty: ptr) -> Val { let v = new Val v.code = code v.ty = ty return v }
|
||||
|
||||
var head: Buf # module-level: types, globals, string constants
|
||||
var code: Buf # function bodies
|
||||
var falloc: Buf # entry-block allocas for the current function
|
||||
var ll_t: int = 0 # temp register counter (reset per function)
|
||||
var ll_lbl: int = 0 # label counter
|
||||
var ll_str: int = 0 # string-constant counter
|
||||
|
||||
# local environment (parallel slices), reset per function
|
||||
var loc_name: []ptr
|
||||
var loc_reg: []ptr
|
||||
var loc_ty: []ptr
|
||||
var nloc: int = 0
|
||||
|
||||
# loop targets for break/continue (innermost last)
|
||||
var brk_lbl: []ptr
|
||||
var cnt_lbl: []ptr
|
||||
var nloop: int = 0
|
||||
|
||||
var ret_ty: ptr # current function's return type
|
||||
var g_term: bool = false # did the current block end in a terminator?
|
||||
var self_stk: []ptr # entity-index slot (ip) per enclosing query, for self()
|
||||
var nself: int = 0
|
||||
var mach_stk: []Node # enclosing `machine` nodes, so `become` finds its register
|
||||
var nmach: int = 0
|
||||
|
||||
fn emit(s: ptr) -> void { buf_puts(code, s) }
|
||||
fn emith(s: ptr) -> void { buf_puts(head, s) }
|
||||
|
||||
# stack slots MUST live in the entry block (an alloca in a loop walks the stack
|
||||
# off its end), so they go into a per-function buffer spliced in at entry.
|
||||
fn emit_alloca(llt: ptr) -> ptr {
|
||||
let r = sconcat("%t", itoa(ll_t)) ll_t = ll_t + 1
|
||||
buf_puts(falloc, " ") buf_puts(falloc, r) buf_puts(falloc, " = alloca ") buf_puts(falloc, llt) buf_puts(falloc, "\n")
|
||||
return r
|
||||
}
|
||||
|
||||
# "%t<n>" fresh register
|
||||
fn sconcat(a: ptr, b: ptr) -> ptr {
|
||||
let la = slen(a) let lb = slen(b)
|
||||
let out = mem_alloc(la + lb + 1)
|
||||
let i = 0
|
||||
while i < la { poke8(out, i, peek8(a, i)) i = i + 1 }
|
||||
let j = 0
|
||||
while j < lb { poke8(out, la + j, peek8(b, j)) j = j + 1 }
|
||||
poke8(out, la + lb, 0)
|
||||
return out
|
||||
}
|
||||
fn nreg() -> ptr { let r = sconcat("%t", itoa(ll_t)) ll_t = ll_t + 1 return r }
|
||||
fn lbl(pfx: ptr) -> ptr { let r = sconcat(pfx, itoa(ll_lbl)) ll_lbl = ll_lbl + 1 return r }
|
||||
|
||||
# Ludic type -> LLVM type. int/bool are i32; everything else (ptr/str/struct/
|
||||
# slice) is a pointer; void is void.
|
||||
fn llty(t: ptr) -> ptr {
|
||||
if streq(t, "int") or streq(t, "bool") or streq(t, "fixed") { return "i32" }
|
||||
if streq(t, "void") { return "void" }
|
||||
return "ptr"
|
||||
}
|
||||
|
||||
fn is_slice_ty(t: ptr) -> bool { return peek8(t, 0) == 91 and peek8(t, 1) == 93 } # "[]"
|
||||
fn slice_elem(t: ptr) -> ptr { return substr(t, 2, slen(t) - 2) }
|
||||
|
||||
fn find_struct(name: ptr) -> Node {
|
||||
let i = 0
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
if d.kind == N_STRUCT and streq(d.s, name) { return d }
|
||||
i = i + 1
|
||||
}
|
||||
return ptr_null()
|
||||
}
|
||||
fn is_struct_ty(t: ptr) -> bool { return not ptr_is_null(find_struct(t)) }
|
||||
|
||||
fn find_arch(name: ptr) -> Node {
|
||||
let i = 0
|
||||
while i < len(prog) { let d = prog[i] if d.kind == N_ARCH and streq(d.s, name) { return d } i = i + 1 }
|
||||
return ptr_null()
|
||||
}
|
||||
fn find_comp(name: ptr) -> Node {
|
||||
let i = 0
|
||||
while i < len(prog) { let d = prog[i] if d.kind == N_COMP and streq(d.s, name) { return d } i = i + 1 }
|
||||
return ptr_null()
|
||||
}
|
||||
# a struct or a component — both have %Str_/%Cmp_ layouts with named fields
|
||||
fn layout_node(name: ptr) -> Node {
|
||||
let s = find_struct(name) if not ptr_is_null(s) { return s }
|
||||
return find_comp(name)
|
||||
}
|
||||
fn layout_ty(name: ptr) -> ptr {
|
||||
if not ptr_is_null(find_struct(name)) { return sconcat("%Str_", name) }
|
||||
return sconcat("%Cmp_", name)
|
||||
}
|
||||
|
||||
fn field_index(s: Node, fname: ptr) -> int {
|
||||
let i = 0
|
||||
while i < len(s.kids) { if streq(s.kids[i].s, fname) { return i } i = i + 1 }
|
||||
return 0 - 1
|
||||
}
|
||||
fn field_type(s: Node, fname: ptr) -> ptr {
|
||||
let i = 0
|
||||
while i < len(s.kids) { if streq(s.kids[i].s, fname) { return s.kids[i].ty } i = i + 1 }
|
||||
return "int"
|
||||
}
|
||||
|
||||
# find a global var/const by name
|
||||
fn find_global(name: ptr) -> Node {
|
||||
let i = 0
|
||||
while i < len(prog) {
|
||||
let d = prog[i]
|
||||
if d.kind == N_VAR and streq(d.s, name) { return d }
|
||||
if d.kind == N_CONST and streq(d.s, name) { return d }
|
||||
i = i + 1
|
||||
}
|
||||
return ptr_null()
|
||||
}
|
||||
fn find_fn(name: ptr) -> Node {
|
||||
let i = 0
|
||||
while i < len(prog) { let d = prog[i] if d.kind == N_FN and streq(d.s, name) { return d } i = i + 1 }
|
||||
return ptr_null()
|
||||
}
|
||||
|
||||
# local variable environment
|
||||
fn loc_reset() -> void { nloc = 0 }
|
||||
fn loc_push(name: ptr, r: ptr, ty: ptr) -> void {
|
||||
if nloc < len(loc_name) { loc_name[nloc] = name loc_reg[nloc] = r loc_ty[nloc] = ty }
|
||||
else { push(loc_name, name) push(loc_reg, r) push(loc_ty, ty) }
|
||||
nloc = nloc + 1
|
||||
}
|
||||
fn loc_find(name: ptr) -> int {
|
||||
let i = nloc - 1
|
||||
while i >= 0 { if streq(loc_name[i], name) { return i } i = i - 1 }
|
||||
return 0 - 1
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue