Archive Rust bootstrap — El compiler is now self-hosting
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
[package]
|
||||
name = "el-compiler"
|
||||
version = "0.1.0"
|
||||
description = "el self-hosting compiler — lexer, parser, codegen"
|
||||
edition = "2026"
|
||||
|
||||
[build]
|
||||
entry = "src/compiler.el"
|
||||
@@ -0,0 +1,9 @@
|
||||
package "el-compiler" {
|
||||
version "0.1.0"
|
||||
description "el self-hosting compiler — lexer, parser, codegen"
|
||||
edition "2026"
|
||||
}
|
||||
|
||||
build {
|
||||
entry "src/compiler.el"
|
||||
}
|
||||
+308
-309
@@ -7,6 +7,11 @@
|
||||
// See the el-compiler/src/bytecode.rs for the canonical enum.
|
||||
//
|
||||
// Entry point: fn codegen(stmts: [Map<String, Any>], source: String) -> String
|
||||
//
|
||||
// Performance: instruction accumulation uses the VM-native global buffer
|
||||
// (native_instr_push / native_instr_len / native_instr_patch / native_instr_all)
|
||||
// instead of passing a growing list through function arguments. This avoids O(n²)
|
||||
// cloning and keeps compilation time linear in the number of instructions.
|
||||
|
||||
// ── JSON helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -59,265 +64,237 @@ fn json_bool(b: Bool) -> String {
|
||||
|
||||
// ── Codegen state ─────────────────────────────────────────────────────────────
|
||||
//
|
||||
// el has no mutable globals. We thread a "ctx" map through all codegen
|
||||
// functions. The context holds:
|
||||
// "instrs" -> [String] — JSON strings for each emitted instruction
|
||||
// "patches" -> [Map<String,Any>] — pending forward-jump patches
|
||||
// each patch: { "idx": Int, "kind": String }
|
||||
// (kind is "JumpIfNot" / "Jump" — we store the instruction index and
|
||||
// patch it at the end of the construct)
|
||||
|
||||
fn ctx_new() -> Map<String, Any> {
|
||||
{ "instrs": native_list_empty(), "patches": native_list_empty() }
|
||||
}
|
||||
|
||||
fn ctx_emit(ctx: Map<String, Any>, instr_json: String) -> Map<String, Any> {
|
||||
let instrs = ctx["instrs"]
|
||||
let instrs = native_list_append(instrs, instr_json)
|
||||
{ "instrs": instrs, "patches": ctx["patches"] }
|
||||
}
|
||||
|
||||
fn ctx_len(ctx: Map<String, Any>) -> Int {
|
||||
let instrs = ctx["instrs"]
|
||||
native_list_len(instrs)
|
||||
}
|
||||
|
||||
// Patch a previously-emitted placeholder instruction at index idx.
|
||||
// For Jump/JumpIf/JumpIfNot, we need to replace the entry in instrs.
|
||||
// We rebuild the list with the patched value at position idx.
|
||||
fn ctx_patch(ctx: Map<String, Any>, idx: Int, dest: Int) -> Map<String, Any> {
|
||||
// offset = dest - (idx + 1)
|
||||
let offset = dest - (idx + 1)
|
||||
let instrs = ctx["instrs"]
|
||||
let total = native_list_len(instrs)
|
||||
let new_instrs: [String] = native_list_empty()
|
||||
let i = 0
|
||||
while i < total {
|
||||
let instr: String = native_list_get(instrs, i)
|
||||
if i == idx {
|
||||
// Replace: determine kind from original instruction string
|
||||
if native_string_contains(instr, "JumpIfNot") {
|
||||
let new_instrs = native_list_append(new_instrs, "{\"JumpIfNot\":" + json_int(offset) + "}")
|
||||
} else {
|
||||
if native_string_contains(instr, "JumpIf") {
|
||||
let new_instrs = native_list_append(new_instrs, "{\"JumpIf\":" + json_int(offset) + "}")
|
||||
} else {
|
||||
let new_instrs = native_list_append(new_instrs, "{\"Jump\":" + json_int(offset) + "}")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let new_instrs = native_list_append(new_instrs, instr)
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
{ "instrs": new_instrs, "patches": ctx["patches"] }
|
||||
}
|
||||
// The instruction accumulator is stored in the VM's global instr_buf.
|
||||
// native_instr_push(s) — append JSON instruction string
|
||||
// native_instr_len() — current instruction count
|
||||
// native_instr_get(i) — get instruction at index i
|
||||
// native_instr_patch(i,s) — replace instruction at index i
|
||||
// native_instr_all() — return full list as [String]
|
||||
|
||||
// ── Instruction emitters ──────────────────────────────────────────────────────
|
||||
|
||||
fn emit_halt(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Halt\"")
|
||||
fn emit_halt() -> Void {
|
||||
native_instr_push("\"Halt\"")
|
||||
}
|
||||
|
||||
fn emit_nop(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Nop\"")
|
||||
fn emit_pop() -> Void {
|
||||
native_instr_push("\"Pop\"")
|
||||
}
|
||||
|
||||
fn emit_pop(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Pop\"")
|
||||
fn emit_return() -> Void {
|
||||
native_instr_push("\"Return\"")
|
||||
}
|
||||
|
||||
fn emit_return(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Return\"")
|
||||
fn emit_add() -> Void {
|
||||
native_instr_push("\"Add\"")
|
||||
}
|
||||
|
||||
fn emit_add(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Add\"")
|
||||
fn emit_sub() -> Void {
|
||||
native_instr_push("\"Sub\"")
|
||||
}
|
||||
|
||||
fn emit_sub(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Sub\"")
|
||||
fn emit_mul() -> Void {
|
||||
native_instr_push("\"Mul\"")
|
||||
}
|
||||
|
||||
fn emit_mul(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Mul\"")
|
||||
fn emit_div() -> Void {
|
||||
native_instr_push("\"Div\"")
|
||||
}
|
||||
|
||||
fn emit_div(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Div\"")
|
||||
fn emit_eq() -> Void {
|
||||
native_instr_push("\"Eq\"")
|
||||
}
|
||||
|
||||
fn emit_eq(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Eq\"")
|
||||
fn emit_not_eq() -> Void {
|
||||
native_instr_push("\"NotEq\"")
|
||||
}
|
||||
|
||||
fn emit_not_eq(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"NotEq\"")
|
||||
fn emit_lt() -> Void {
|
||||
native_instr_push("\"Lt\"")
|
||||
}
|
||||
|
||||
fn emit_lt(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Lt\"")
|
||||
fn emit_gt() -> Void {
|
||||
native_instr_push("\"Gt\"")
|
||||
}
|
||||
|
||||
fn emit_gt(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Gt\"")
|
||||
fn emit_lt_eq() -> Void {
|
||||
native_instr_push("\"LtEq\"")
|
||||
}
|
||||
|
||||
fn emit_lt_eq(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"LtEq\"")
|
||||
fn emit_gt_eq() -> Void {
|
||||
native_instr_push("\"GtEq\"")
|
||||
}
|
||||
|
||||
fn emit_gt_eq(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"GtEq\"")
|
||||
fn emit_and() -> Void {
|
||||
native_instr_push("\"And\"")
|
||||
}
|
||||
|
||||
fn emit_and(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"And\"")
|
||||
fn emit_or() -> Void {
|
||||
native_instr_push("\"Or\"")
|
||||
}
|
||||
|
||||
fn emit_or(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Or\"")
|
||||
fn emit_not() -> Void {
|
||||
native_instr_push("\"Not\"")
|
||||
}
|
||||
|
||||
fn emit_not(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"Not\"")
|
||||
fn emit_get_index() -> Void {
|
||||
native_instr_push("\"GetIndex\"")
|
||||
}
|
||||
|
||||
fn emit_get_index(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "\"GetIndex\"")
|
||||
fn emit_push_nil() -> Void {
|
||||
native_instr_push("{\"Push\":\"Nil\"}")
|
||||
}
|
||||
|
||||
fn emit_push_nil(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"Push\":\"Nil\"}")
|
||||
fn emit_push_int(n: Int) -> Void {
|
||||
native_instr_push("{\"Push\":{\"Int\":" + json_int(n) + "}}")
|
||||
}
|
||||
|
||||
fn emit_push_int(ctx: Map<String, Any>, n: Int) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"Push\":{\"Int\":" + json_int(n) + "}}")
|
||||
fn emit_push_bool(b: Bool) -> Void {
|
||||
native_instr_push("{\"Push\":{\"Bool\":" + json_bool(b) + "}}")
|
||||
}
|
||||
|
||||
fn emit_push_bool(ctx: Map<String, Any>, b: Bool) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"Push\":{\"Bool\":" + json_bool(b) + "}}")
|
||||
fn emit_push_str(s: String) -> Void {
|
||||
native_instr_push("{\"Push\":{\"Str\":" + json_str(s) + "}}")
|
||||
}
|
||||
|
||||
fn emit_push_str(ctx: Map<String, Any>, s: String) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"Push\":{\"Str\":" + json_str(s) + "}}")
|
||||
fn emit_load(name: String) -> Void {
|
||||
native_instr_push("{\"LoadLocal\":" + json_str(name) + "}")
|
||||
}
|
||||
|
||||
fn emit_load(ctx: Map<String, Any>, name: String) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"LoadLocal\":" + json_str(name) + "}")
|
||||
fn emit_store(name: String) -> Void {
|
||||
native_instr_push("{\"StoreLocal\":" + json_str(name) + "}")
|
||||
}
|
||||
|
||||
fn emit_store(ctx: Map<String, Any>, name: String) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"StoreLocal\":" + json_str(name) + "}")
|
||||
fn emit_call(name: String, arity: Int) -> Void {
|
||||
native_instr_push("{\"Call\":{\"name\":" + json_str(name) + ",\"arity\":" + json_int(arity) + "}}")
|
||||
}
|
||||
|
||||
fn emit_call(ctx: Map<String, Any>, name: String, arity: Int) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"Call\":{\"name\":" + json_str(name) + ",\"arity\":" + json_int(arity) + "}}")
|
||||
fn emit_get_field(field: String) -> Void {
|
||||
native_instr_push("{\"GetField\":" + json_str(field) + "}")
|
||||
}
|
||||
|
||||
fn emit_get_field(ctx: Map<String, Any>, field: String) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"GetField\":" + json_str(field) + "}")
|
||||
fn emit_build_map(n: Int) -> Void {
|
||||
native_instr_push("{\"BuildMap\":" + json_int(n) + "}")
|
||||
}
|
||||
|
||||
fn emit_build_map(ctx: Map<String, Any>, n: Int) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"BuildMap\":" + json_int(n) + "}")
|
||||
fn emit_build_list(n: Int) -> Void {
|
||||
native_instr_push("{\"BuildList\":" + json_int(n) + "}")
|
||||
}
|
||||
|
||||
fn emit_build_list(ctx: Map<String, Any>, n: Int) -> Map<String, Any> {
|
||||
ctx_emit(ctx, "{\"BuildList\":" + json_int(n) + "}")
|
||||
// Emit a placeholder Jump and return its index (for later patching).
|
||||
fn emit_jump_placeholder() -> Int {
|
||||
let idx: Int = native_instr_len()
|
||||
native_instr_push("{\"Jump\":0}")
|
||||
idx
|
||||
}
|
||||
|
||||
// Emit a placeholder Jump and return its index (for later patching)
|
||||
fn emit_jump_placeholder(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
let idx = ctx_len(ctx)
|
||||
let ctx = ctx_emit(ctx, "{\"Jump\":0}")
|
||||
{ "ctx": ctx, "idx": idx }
|
||||
fn emit_jump_if_not_placeholder() -> Int {
|
||||
let idx: Int = native_instr_len()
|
||||
native_instr_push("{\"JumpIfNot\":0}")
|
||||
idx
|
||||
}
|
||||
|
||||
fn emit_jump_if_not_placeholder(ctx: Map<String, Any>) -> Map<String, Any> {
|
||||
let idx = ctx_len(ctx)
|
||||
let ctx = ctx_emit(ctx, "{\"JumpIfNot\":0}")
|
||||
{ "ctx": ctx, "idx": idx }
|
||||
fn emit_jump_to(dest: Int) -> Void {
|
||||
let here: Int = native_instr_len()
|
||||
let offset: Int = dest - (here + 1)
|
||||
native_instr_push("{\"Jump\":" + json_int(offset) + "}")
|
||||
}
|
||||
|
||||
fn emit_jump_to(ctx: Map<String, Any>, dest: Int) -> Map<String, Any> {
|
||||
let here = ctx_len(ctx)
|
||||
let offset = dest - (here + 1)
|
||||
ctx_emit(ctx, "{\"Jump\":" + json_int(offset) + "}")
|
||||
// Patch a previously-emitted placeholder jump instruction.
|
||||
fn patch_instr(idx: Int, dest: Int) -> Void {
|
||||
let offset: Int = dest - (idx + 1)
|
||||
let original: String = native_instr_get(idx)
|
||||
if native_string_contains(original, "JumpIfNot") {
|
||||
native_instr_patch(idx, "{\"JumpIfNot\":" + json_int(offset) + "}")
|
||||
} else {
|
||||
if native_string_contains(original, "JumpIf") {
|
||||
native_instr_patch(idx, "{\"JumpIf\":" + json_int(offset) + "}")
|
||||
} else {
|
||||
native_instr_patch(idx, "{\"Jump\":" + json_int(offset) + "}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Expression codegen ────────────────────────────────────────────────────────
|
||||
|
||||
fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
|
||||
fn cg_expr(expr: Map<String, Any>) -> Void {
|
||||
let kind: String = expr["expr"]
|
||||
|
||||
if kind == "Int" {
|
||||
let v: String = expr["value"]
|
||||
let n: Int = native_str_to_int(v)
|
||||
return emit_push_int(ctx, n)
|
||||
emit_push_int(n)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Float" {
|
||||
let v: String = expr["value"]
|
||||
// Push as string for now — VM handles float parsing from Str
|
||||
return emit_push_str(ctx, v)
|
||||
emit_push_str(v)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Str" {
|
||||
let v: String = expr["value"]
|
||||
return emit_push_str(ctx, v)
|
||||
emit_push_str(v)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Bool" {
|
||||
let v: String = expr["value"]
|
||||
if v == "true" {
|
||||
return emit_push_bool(ctx, true)
|
||||
emit_push_bool(true)
|
||||
} else {
|
||||
emit_push_bool(false)
|
||||
}
|
||||
return emit_push_bool(ctx, false)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Nil" {
|
||||
return emit_push_nil(ctx)
|
||||
emit_push_nil()
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Ident" {
|
||||
let name: String = expr["name"]
|
||||
return emit_load(ctx, name)
|
||||
emit_load(name)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Not" {
|
||||
let inner = expr["inner"]
|
||||
let ctx = cg_expr(ctx, inner)
|
||||
return emit_not(ctx)
|
||||
cg_expr(inner)
|
||||
emit_not()
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Neg" {
|
||||
// unary minus: push 0, push inner, sub
|
||||
let ctx = emit_push_int(ctx, 0)
|
||||
emit_push_int(0)
|
||||
let inner = expr["inner"]
|
||||
let ctx = cg_expr(ctx, inner)
|
||||
return emit_sub(ctx)
|
||||
cg_expr(inner)
|
||||
emit_sub()
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "BinOp" {
|
||||
let op: String = expr["op"]
|
||||
let left = expr["left"]
|
||||
let right = expr["right"]
|
||||
let ctx = cg_expr(ctx, left)
|
||||
let ctx = cg_expr(ctx, right)
|
||||
if op == "Plus" { return emit_add(ctx) }
|
||||
if op == "Minus" { return emit_sub(ctx) }
|
||||
if op == "Star" { return emit_mul(ctx) }
|
||||
if op == "Slash" { return emit_div(ctx) }
|
||||
if op == "EqEq" { return emit_eq(ctx) }
|
||||
if op == "NotEq" { return emit_not_eq(ctx) }
|
||||
if op == "Lt" { return emit_lt(ctx) }
|
||||
if op == "Gt" { return emit_gt(ctx) }
|
||||
if op == "LtEq" { return emit_lt_eq(ctx) }
|
||||
if op == "GtEq" { return emit_gt_eq(ctx) }
|
||||
if op == "And" { return emit_and(ctx) }
|
||||
if op == "Or" { return emit_or(ctx) }
|
||||
return ctx
|
||||
cg_expr(left)
|
||||
cg_expr(right)
|
||||
if op == "Plus" { emit_add() }
|
||||
if op == "Minus" { emit_sub() }
|
||||
if op == "Star" { emit_mul() }
|
||||
if op == "Slash" { emit_div() }
|
||||
if op == "EqEq" { emit_eq() }
|
||||
if op == "NotEq" { emit_not_eq() }
|
||||
if op == "Lt" { emit_lt() }
|
||||
if op == "Gt" { emit_gt() }
|
||||
if op == "LtEq" { emit_lt_eq() }
|
||||
if op == "GtEq" { emit_gt_eq() }
|
||||
if op == "And" { emit_and() }
|
||||
if op == "Or" { emit_or() }
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Call" {
|
||||
@@ -328,37 +305,42 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
|
||||
let i = 0
|
||||
while i < arity {
|
||||
let arg = native_list_get(args, i)
|
||||
let ctx = cg_expr(ctx, arg)
|
||||
cg_expr(arg)
|
||||
let i = i + 1
|
||||
}
|
||||
// get function name from func expr
|
||||
let func_kind: String = func["expr"]
|
||||
if func_kind == "Ident" {
|
||||
let fn_name: String = func["name"]
|
||||
return emit_call(ctx, fn_name, arity)
|
||||
emit_call(fn_name, arity)
|
||||
return
|
||||
}
|
||||
if func_kind == "Field" {
|
||||
let obj = func["object"]
|
||||
let field: String = func["field"]
|
||||
let ctx = cg_expr(ctx, obj)
|
||||
return emit_call(ctx, field, arity + 1)
|
||||
cg_expr(obj)
|
||||
emit_call(field, arity + 1)
|
||||
return
|
||||
}
|
||||
return emit_call(ctx, "__dynamic__", arity)
|
||||
emit_call("__dynamic__", arity)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Field" {
|
||||
let obj = expr["object"]
|
||||
let field: String = expr["field"]
|
||||
let ctx = cg_expr(ctx, obj)
|
||||
return emit_get_field(ctx, field)
|
||||
cg_expr(obj)
|
||||
emit_get_field(field)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Index" {
|
||||
let obj = expr["object"]
|
||||
let idx = expr["index"]
|
||||
let ctx = cg_expr(ctx, obj)
|
||||
let ctx = cg_expr(ctx, idx)
|
||||
return emit_get_index(ctx)
|
||||
cg_expr(obj)
|
||||
cg_expr(idx)
|
||||
emit_get_index()
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Array" {
|
||||
@@ -367,10 +349,11 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
|
||||
let i = 0
|
||||
while i < n {
|
||||
let elem = native_list_get(elems, i)
|
||||
let ctx = cg_expr(ctx, elem)
|
||||
cg_expr(elem)
|
||||
let i = i + 1
|
||||
}
|
||||
return emit_build_list(ctx, n)
|
||||
emit_build_list(n)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Map" {
|
||||
@@ -381,11 +364,12 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
|
||||
let pair = native_list_get(pairs, i)
|
||||
let key: String = pair["key"]
|
||||
let val = pair["value"]
|
||||
let ctx = emit_push_str(ctx, key)
|
||||
let ctx = cg_expr(ctx, val)
|
||||
emit_push_str(key)
|
||||
cg_expr(val)
|
||||
let i = i + 1
|
||||
}
|
||||
return emit_build_map(ctx, n)
|
||||
emit_build_map(n)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "If" {
|
||||
@@ -394,41 +378,36 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
|
||||
let else_stmts = expr["else"]
|
||||
let has_else: Bool = expr["has_else"]
|
||||
// cond
|
||||
let ctx = cg_expr(ctx, cond)
|
||||
cg_expr(cond)
|
||||
// JumpIfNot placeholder
|
||||
let r = emit_jump_if_not_placeholder(ctx)
|
||||
let ctx = r["ctx"]
|
||||
let jump_false_idx: Int = r["idx"]
|
||||
let jump_false_idx: Int = emit_jump_if_not_placeholder()
|
||||
// then body
|
||||
let ctx = cg_stmts(ctx, then_stmts)
|
||||
cg_stmts(then_stmts)
|
||||
if has_else {
|
||||
// jump over else
|
||||
let r2 = emit_jump_placeholder(ctx)
|
||||
let ctx = r2["ctx"]
|
||||
let jump_end_idx: Int = r2["idx"]
|
||||
let jump_end_idx: Int = emit_jump_placeholder()
|
||||
// patch jump_false to here
|
||||
let else_start = ctx_len(ctx)
|
||||
let ctx = ctx_patch(ctx, jump_false_idx, else_start)
|
||||
let else_start: Int = native_instr_len()
|
||||
patch_instr(jump_false_idx, else_start)
|
||||
// else body
|
||||
let ctx = cg_stmts(ctx, else_stmts)
|
||||
cg_stmts(else_stmts)
|
||||
// patch jump_end to here
|
||||
let after_else = ctx_len(ctx)
|
||||
let ctx = ctx_patch(ctx, jump_end_idx, after_else)
|
||||
return ctx
|
||||
let after_else: Int = native_instr_len()
|
||||
patch_instr(jump_end_idx, after_else)
|
||||
} else {
|
||||
let after_then = ctx_len(ctx)
|
||||
let ctx = ctx_patch(ctx, jump_false_idx, after_then)
|
||||
return ctx
|
||||
let after_then: Int = native_instr_len()
|
||||
patch_instr(jump_false_idx, after_then)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Match" {
|
||||
let subject = expr["subject"]
|
||||
let arms = expr["arms"]
|
||||
let n_arms: Int = native_list_len(arms)
|
||||
let ctx = cg_expr(ctx, subject)
|
||||
cg_expr(subject)
|
||||
// store subject in temp var
|
||||
let ctx = emit_store(ctx, "__match_subj__")
|
||||
emit_store("__match_subj__")
|
||||
let end_jump_idxs: [Int] = native_list_empty()
|
||||
let i = 0
|
||||
while i < n_arms {
|
||||
@@ -438,155 +417,187 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
|
||||
let pat_kind: String = pattern["pattern"]
|
||||
if pat_kind == "Wildcard" {
|
||||
// always matches — just emit body
|
||||
let ctx = cg_expr(ctx, body)
|
||||
let r = emit_jump_placeholder(ctx)
|
||||
let ctx = r["ctx"]
|
||||
let jidx: Int = r["idx"]
|
||||
cg_expr(body)
|
||||
let jidx: Int = emit_jump_placeholder()
|
||||
let end_jump_idxs = native_list_append(end_jump_idxs, jidx)
|
||||
} else {
|
||||
if pat_kind == "Binding" {
|
||||
let bind_name: String = pattern["name"]
|
||||
let ctx = emit_load(ctx, "__match_subj__")
|
||||
let ctx = emit_store(ctx, bind_name)
|
||||
let ctx = cg_expr(ctx, body)
|
||||
let r = emit_jump_placeholder(ctx)
|
||||
let ctx = r["ctx"]
|
||||
let jidx: Int = r["idx"]
|
||||
emit_load("__match_subj__")
|
||||
emit_store(bind_name)
|
||||
cg_expr(body)
|
||||
let jidx: Int = emit_jump_placeholder()
|
||||
let end_jump_idxs = native_list_append(end_jump_idxs, jidx)
|
||||
} else {
|
||||
// literal pattern: compare subject to literal
|
||||
let ctx = emit_load(ctx, "__match_subj__")
|
||||
emit_load("__match_subj__")
|
||||
if pat_kind == "LitInt" {
|
||||
let v: String = pattern["value"]
|
||||
let n: Int = native_str_to_int(v)
|
||||
let ctx = emit_push_int(ctx, n)
|
||||
emit_push_int(n)
|
||||
} else {
|
||||
if pat_kind == "LitStr" {
|
||||
let v: String = pattern["value"]
|
||||
let ctx = emit_push_str(ctx, v)
|
||||
emit_push_str(v)
|
||||
} else {
|
||||
if pat_kind == "LitBool" {
|
||||
let v: String = pattern["value"]
|
||||
if v == "true" {
|
||||
let ctx = emit_push_bool(ctx, true)
|
||||
emit_push_bool(true)
|
||||
} else {
|
||||
let ctx = emit_push_bool(ctx, false)
|
||||
emit_push_bool(false)
|
||||
}
|
||||
} else {
|
||||
let ctx = emit_push_nil(ctx)
|
||||
emit_push_nil()
|
||||
}
|
||||
}
|
||||
}
|
||||
let ctx = emit_eq(ctx)
|
||||
let r = emit_jump_if_not_placeholder(ctx)
|
||||
let ctx = r["ctx"]
|
||||
let no_match_idx: Int = r["idx"]
|
||||
let ctx = cg_expr(ctx, body)
|
||||
let r2 = emit_jump_placeholder(ctx)
|
||||
let ctx = r2["ctx"]
|
||||
let jidx: Int = r2["idx"]
|
||||
emit_eq()
|
||||
let no_match_idx: Int = emit_jump_if_not_placeholder()
|
||||
cg_expr(body)
|
||||
let jidx: Int = emit_jump_placeholder()
|
||||
let end_jump_idxs = native_list_append(end_jump_idxs, jidx)
|
||||
let next_arm = ctx_len(ctx)
|
||||
let ctx = ctx_patch(ctx, no_match_idx, next_arm)
|
||||
let next_arm: Int = native_instr_len()
|
||||
patch_instr(no_match_idx, next_arm)
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
// default: push nil
|
||||
let ctx = emit_push_nil(ctx)
|
||||
let end_pos = ctx_len(ctx)
|
||||
emit_push_nil()
|
||||
let end_pos: Int = native_instr_len()
|
||||
// patch all end jumps
|
||||
let n_end: Int = native_list_len(end_jump_idxs)
|
||||
let j = 0
|
||||
while j < n_end {
|
||||
let jidx: Int = native_list_get(end_jump_idxs, j)
|
||||
let ctx = ctx_patch(ctx, jidx, end_pos)
|
||||
patch_instr(jidx, end_pos)
|
||||
let j = j + 1
|
||||
}
|
||||
return ctx
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "For" {
|
||||
// for item in list { body }
|
||||
// Implementation:
|
||||
// __for_list__ = list
|
||||
// __for_len__ = len(list)
|
||||
// __for_i__ = 0
|
||||
// loop_start:
|
||||
// if __for_i__ >= __for_len__ goto done
|
||||
// item = __for_list__[__for_i__]
|
||||
// body
|
||||
// __for_i__ = __for_i__ + 1
|
||||
// goto loop_start
|
||||
// done:
|
||||
let item: String = expr["item"]
|
||||
let list_expr = expr["list"]
|
||||
let body = expr["body"]
|
||||
// emit list, store it
|
||||
let ctx = cg_expr(ctx, list_expr)
|
||||
let ctx = emit_store(ctx, "__for_list__")
|
||||
cg_expr(list_expr)
|
||||
emit_store("__for_list__")
|
||||
// compute length
|
||||
let ctx = emit_load(ctx, "__for_list__")
|
||||
let ctx = emit_call(ctx, "native_list_len", 1)
|
||||
let ctx = emit_store(ctx, "__for_len__")
|
||||
emit_load("__for_list__")
|
||||
emit_call("native_list_len", 1)
|
||||
emit_store("__for_len__")
|
||||
// init counter
|
||||
let ctx = emit_push_int(ctx, 0)
|
||||
let ctx = emit_store(ctx, "__for_i__")
|
||||
emit_push_int(0)
|
||||
emit_store("__for_i__")
|
||||
// loop start
|
||||
let loop_start = ctx_len(ctx)
|
||||
let loop_start: Int = native_instr_len()
|
||||
// condition: __for_i__ < __for_len__
|
||||
let ctx = emit_load(ctx, "__for_i__")
|
||||
let ctx = emit_load(ctx, "__for_len__")
|
||||
let ctx = emit_lt(ctx)
|
||||
let r = emit_jump_if_not_placeholder(ctx)
|
||||
let ctx = r["ctx"]
|
||||
let to_done_idx: Int = r["idx"]
|
||||
emit_load("__for_i__")
|
||||
emit_load("__for_len__")
|
||||
emit_lt()
|
||||
let to_done_idx: Int = emit_jump_if_not_placeholder()
|
||||
// get current element
|
||||
let ctx = emit_load(ctx, "__for_list__")
|
||||
let ctx = emit_load(ctx, "__for_i__")
|
||||
let ctx = emit_get_index(ctx)
|
||||
let ctx = emit_store(ctx, item)
|
||||
emit_load("__for_list__")
|
||||
emit_load("__for_i__")
|
||||
emit_get_index()
|
||||
emit_store(item)
|
||||
// body
|
||||
let ctx = cg_stmts(ctx, body)
|
||||
cg_stmts(body)
|
||||
// increment counter
|
||||
let ctx = emit_load(ctx, "__for_i__")
|
||||
let ctx = emit_push_int(ctx, 1)
|
||||
let ctx = emit_add(ctx)
|
||||
let ctx = emit_store(ctx, "__for_i__")
|
||||
emit_load("__for_i__")
|
||||
emit_push_int(1)
|
||||
emit_add()
|
||||
emit_store("__for_i__")
|
||||
// jump back
|
||||
let ctx = emit_jump_to(ctx, loop_start)
|
||||
emit_jump_to(loop_start)
|
||||
// patch done
|
||||
let done_pos = ctx_len(ctx)
|
||||
let ctx = ctx_patch(ctx, to_done_idx, done_pos)
|
||||
return ctx
|
||||
let done_pos: Int = native_instr_len()
|
||||
patch_instr(to_done_idx, done_pos)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Try" {
|
||||
// Just emit the inner expression — error propagation handled at runtime
|
||||
// Just emit the inner expression
|
||||
let inner = expr["inner"]
|
||||
return cg_expr(ctx, inner)
|
||||
cg_expr(inner)
|
||||
return
|
||||
}
|
||||
|
||||
// Fallback
|
||||
emit_push_nil(ctx)
|
||||
emit_push_nil()
|
||||
}
|
||||
|
||||
// ── Statement codegen ─────────────────────────────────────────────────────────
|
||||
|
||||
fn cg_stmt(ctx: Map<String, Any>, stmt: Map<String, Any>) -> Map<String, Any> {
|
||||
// cg_stmt_tail — emit a statement in tail position (last stmt of fn body).
|
||||
// For Expr statements, the result is left on the stack (not popped).
|
||||
// For all other statement kinds, we delegate to cg_stmt and push Nil.
|
||||
fn cg_stmt_tail(stmt: Map<String, Any>) -> Void {
|
||||
let kind: String = stmt["stmt"]
|
||||
|
||||
if kind == "Expr" {
|
||||
let val = stmt["value"]
|
||||
// Emit the expression — leave result on stack (tail position).
|
||||
cg_expr(val)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Return" {
|
||||
// Explicit return — same as cg_stmt
|
||||
let val = stmt["value"]
|
||||
cg_expr(val)
|
||||
emit_return()
|
||||
return
|
||||
}
|
||||
|
||||
// All other statement kinds (Let, FnDef, While, etc.) don't produce
|
||||
// a natural return value — fall through to cg_stmt then push Nil.
|
||||
cg_stmt(stmt)
|
||||
emit_push_nil()
|
||||
}
|
||||
|
||||
// cg_stmts_body — emit function body statements.
|
||||
// All statements except the last use cg_stmt (pops results).
|
||||
// The last statement uses cg_stmt_tail (leaves value on stack).
|
||||
fn cg_stmts_body(stmts: [Map<String, Any>]) -> Void {
|
||||
let n: Int = native_list_len(stmts)
|
||||
if n == 0 {
|
||||
// Empty body — push Nil as return value.
|
||||
emit_push_nil()
|
||||
return
|
||||
}
|
||||
let i = 0
|
||||
while i < n {
|
||||
let stmt = native_list_get(stmts, i)
|
||||
let is_last: Bool = (i == n - 1)
|
||||
if is_last {
|
||||
cg_stmt_tail(stmt)
|
||||
} else {
|
||||
cg_stmt(stmt)
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
fn cg_stmt(stmt: Map<String, Any>) -> Void {
|
||||
let kind: String = stmt["stmt"]
|
||||
|
||||
if kind == "Let" {
|
||||
let name: String = stmt["name"]
|
||||
let val = stmt["value"]
|
||||
let ctx = cg_expr(ctx, val)
|
||||
return emit_store(ctx, name)
|
||||
cg_expr(val)
|
||||
emit_store(name)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Return" {
|
||||
let val = stmt["value"]
|
||||
let ctx = cg_expr(ctx, val)
|
||||
return emit_return(ctx)
|
||||
cg_expr(val)
|
||||
emit_return()
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "FnDef" {
|
||||
@@ -595,45 +606,40 @@ fn cg_stmt(ctx: Map<String, Any>, stmt: Map<String, Any>) -> Map<String, Any> {
|
||||
let body = stmt["body"]
|
||||
let n_params: Int = native_list_len(params)
|
||||
// emit a Jump to skip over the function body
|
||||
let r = emit_jump_placeholder(ctx)
|
||||
let ctx = r["ctx"]
|
||||
let skip_jump_idx: Int = r["idx"]
|
||||
let skip_jump_idx: Int = emit_jump_placeholder()
|
||||
// function body entry: store params in reverse order (caller pushes L→R, so pop R→L)
|
||||
let pi = n_params - 1
|
||||
while pi >= 0 {
|
||||
let param = native_list_get(params, pi)
|
||||
let pname: String = param["name"]
|
||||
let ctx = emit_store(ctx, pname)
|
||||
emit_store(pname)
|
||||
let pi = pi - 1
|
||||
}
|
||||
// emit body statements
|
||||
let ctx = cg_stmts(ctx, body)
|
||||
// implicit nil return
|
||||
let ctx = emit_push_nil(ctx)
|
||||
let ctx = emit_return(ctx)
|
||||
// emit body statements using tail-aware emit (last stmt leaves value on stack)
|
||||
cg_stmts_body(body)
|
||||
// return the top-of-stack value
|
||||
emit_return()
|
||||
// patch skip jump
|
||||
let after_fn = ctx_len(ctx)
|
||||
let ctx = ctx_patch(ctx, skip_jump_idx, after_fn)
|
||||
let after_fn: Int = native_instr_len()
|
||||
patch_instr(skip_jump_idx, after_fn)
|
||||
// register function entry point
|
||||
let entry_ip = skip_jump_idx + 1
|
||||
let ctx = emit_push_int(ctx, entry_ip)
|
||||
let ctx = emit_store(ctx, "__fn_" + fn_name)
|
||||
return ctx
|
||||
let entry_ip: Int = skip_jump_idx + 1
|
||||
emit_push_int(entry_ip)
|
||||
emit_store("__fn_" + fn_name)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "While" {
|
||||
let cond = stmt["cond"]
|
||||
let body = stmt["body"]
|
||||
let loop_start = ctx_len(ctx)
|
||||
let ctx = cg_expr(ctx, cond)
|
||||
let r = emit_jump_if_not_placeholder(ctx)
|
||||
let ctx = r["ctx"]
|
||||
let to_done_idx: Int = r["idx"]
|
||||
let ctx = cg_stmts(ctx, body)
|
||||
let ctx = emit_jump_to(ctx, loop_start)
|
||||
let done_pos = ctx_len(ctx)
|
||||
let ctx = ctx_patch(ctx, to_done_idx, done_pos)
|
||||
return ctx
|
||||
let loop_start: Int = native_instr_len()
|
||||
cg_expr(cond)
|
||||
let to_done_idx: Int = emit_jump_if_not_placeholder()
|
||||
cg_stmts(body)
|
||||
emit_jump_to(loop_start)
|
||||
let done_pos: Int = native_instr_len()
|
||||
patch_instr(to_done_idx, done_pos)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "For" {
|
||||
@@ -642,53 +648,46 @@ fn cg_stmt(ctx: Map<String, Any>, stmt: Map<String, Any>) -> Map<String, Any> {
|
||||
let list_expr = stmt["list"]
|
||||
let body = stmt["body"]
|
||||
let for_expr = { "expr": "For", "item": item, "list": list_expr, "body": body }
|
||||
return cg_expr(ctx, for_expr)
|
||||
cg_expr(for_expr)
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Expr" {
|
||||
let val = stmt["value"]
|
||||
let val_kind: String = val["expr"]
|
||||
let ctx = cg_expr(ctx, val)
|
||||
// Discard result unless it's a control-flow expression
|
||||
if val_kind == "If" {
|
||||
return ctx
|
||||
}
|
||||
cg_expr(val)
|
||||
// Discard result unless it's a control-flow expression that doesn't push a value
|
||||
if val_kind == "For" {
|
||||
return ctx
|
||||
return
|
||||
}
|
||||
if val_kind == "Match" {
|
||||
return ctx
|
||||
}
|
||||
return emit_pop(ctx)
|
||||
emit_pop()
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "TypeDef" {
|
||||
// compile-time only; no runtime code
|
||||
return ctx
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "EnumDef" {
|
||||
// compile-time only; no runtime code
|
||||
return ctx
|
||||
return
|
||||
}
|
||||
|
||||
if kind == "Import" {
|
||||
// handled at a higher level; skip
|
||||
return ctx
|
||||
return
|
||||
}
|
||||
|
||||
ctx
|
||||
}
|
||||
|
||||
fn cg_stmts(ctx: Map<String, Any>, stmts: [Map<String, Any>]) -> Map<String, Any> {
|
||||
fn cg_stmts(stmts: [Map<String, Any>]) -> Void {
|
||||
let n: Int = native_list_len(stmts)
|
||||
let i = 0
|
||||
while i < n {
|
||||
let stmt = native_list_get(stmts, i)
|
||||
let ctx = cg_stmt(ctx, stmt)
|
||||
cg_stmt(stmt)
|
||||
let i = i + 1
|
||||
}
|
||||
ctx
|
||||
}
|
||||
|
||||
// ── JSON serialisation ────────────────────────────────────────────────────────
|
||||
@@ -711,9 +710,9 @@ fn instrs_to_json(instrs: [String]) -> String {
|
||||
// ── Entry point ───────────────────────────────────────────────────────────────
|
||||
|
||||
fn codegen(stmts: [Map<String, Any>], source: String) -> String {
|
||||
let ctx = ctx_new()
|
||||
let ctx = cg_stmts(ctx, stmts)
|
||||
let ctx = emit_halt(ctx)
|
||||
let instrs = ctx["instrs"]
|
||||
native_instr_reset()
|
||||
cg_stmts(stmts)
|
||||
emit_halt()
|
||||
let instrs: [String] = native_instr_all()
|
||||
instrs_to_json(instrs)
|
||||
}
|
||||
|
||||
@@ -17,3 +17,30 @@ fn compile(source: String) -> String {
|
||||
let stmts: [Map<String, Any>] = parse(tokens)
|
||||
codegen(stmts, source)
|
||||
}
|
||||
|
||||
// main — CLI entry point for self-hosted compilation.
|
||||
//
|
||||
// Called by: elvm el-compiler.elc <source.el> <output.elc>
|
||||
//
|
||||
// Reads pre-resolved El source from args()[0], compiles it to JSON bytecode,
|
||||
// and writes the result to args()[1]. The output is raw JSON (accepted by
|
||||
// both elvm and el exec without an ELVM container header).
|
||||
fn main() -> Void {
|
||||
let argv: [String] = args()
|
||||
let argc: Int = native_list_len(argv)
|
||||
if argc < 2 {
|
||||
println("el-compiler: usage: elvm el-compiler.elc <source.el> <output.elc>")
|
||||
exit(1)
|
||||
}
|
||||
let src_path: String = native_list_get(argv, 0)
|
||||
let out_path: String = native_list_get(argv, 1)
|
||||
let source: String = fs_read(src_path)
|
||||
let bytecode_json: String = compile(source)
|
||||
let ok: Bool = fs_write(out_path, bytecode_json)
|
||||
if ok {
|
||||
exit(0)
|
||||
} else {
|
||||
println("el-compiler: failed to write output")
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
+26
-21
@@ -6,6 +6,9 @@
|
||||
// "value" -> String (the raw text of the token)
|
||||
//
|
||||
// Entry point: fn lex(source: String) -> [Map<String, Any>]
|
||||
//
|
||||
// Uses global char_buf via native_chars_init / native_char_at / native_char_len
|
||||
// to avoid O(N²) cloning of the chars list.
|
||||
|
||||
// ── Character helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -141,9 +144,11 @@ fn keyword_kind(word: String) -> String {
|
||||
}
|
||||
|
||||
// ── Scan helpers ──────────────────────────────────────────────────────────────
|
||||
// All scan helpers use the global char_buf (native_char_at / native_char_len).
|
||||
// No chars parameter — avoids O(N²) cloning.
|
||||
|
||||
// scan_digits — advance i while chars[i] is a digit, return { "text": ..., "pos": i }
|
||||
fn scan_digits(chars: [String], start: Int, total: Int) -> Map<String, Any> {
|
||||
// scan_digits — advance i while char_buf[i] is a digit, return { "text": ..., "pos": i }
|
||||
fn scan_digits(start: Int, total: Int) -> Map<String, Any> {
|
||||
let i = start
|
||||
let text = ""
|
||||
let running = true
|
||||
@@ -151,7 +156,7 @@ fn scan_digits(chars: [String], start: Int, total: Int) -> Map<String, Any> {
|
||||
if i >= total {
|
||||
let running = false
|
||||
} else {
|
||||
let ch = native_list_get(chars, i)
|
||||
let ch = native_char_at(i)
|
||||
if is_digit(ch) {
|
||||
let text = text + ch
|
||||
let i = i + 1
|
||||
@@ -163,8 +168,8 @@ fn scan_digits(chars: [String], start: Int, total: Int) -> Map<String, Any> {
|
||||
{ "text": text, "pos": i }
|
||||
}
|
||||
|
||||
// scan_ident — advance i while chars[i] is alphanumeric or underscore
|
||||
fn scan_ident(chars: [String], start: Int, total: Int) -> Map<String, Any> {
|
||||
// scan_ident — advance i while char_buf[i] is alphanumeric or underscore
|
||||
fn scan_ident(start: Int, total: Int) -> Map<String, Any> {
|
||||
let i = start
|
||||
let text = ""
|
||||
let running = true
|
||||
@@ -172,7 +177,7 @@ fn scan_ident(chars: [String], start: Int, total: Int) -> Map<String, Any> {
|
||||
if i >= total {
|
||||
let running = false
|
||||
} else {
|
||||
let ch = native_list_get(chars, i)
|
||||
let ch = native_char_at(i)
|
||||
if is_alnum_or_underscore(ch) {
|
||||
let text = text + ch
|
||||
let i = i + 1
|
||||
@@ -186,7 +191,7 @@ fn scan_ident(chars: [String], start: Int, total: Int) -> Map<String, Any> {
|
||||
|
||||
// scan_string — scan a quoted string literal, handling \" escapes.
|
||||
// Starts AFTER the opening quote. Returns { "text": content, "pos": i_after_close }
|
||||
fn scan_string(chars: [String], start: Int, total: Int) -> Map<String, Any> {
|
||||
fn scan_string(start: Int, total: Int) -> Map<String, Any> {
|
||||
let i = start
|
||||
let text = ""
|
||||
let closed = false
|
||||
@@ -195,12 +200,12 @@ fn scan_string(chars: [String], start: Int, total: Int) -> Map<String, Any> {
|
||||
if i >= total {
|
||||
let running = false
|
||||
} else {
|
||||
let ch = native_list_get(chars, i)
|
||||
let ch = native_char_at(i)
|
||||
if ch == "\\" {
|
||||
// escape: peek next char
|
||||
let next_i = i + 1
|
||||
if next_i < total {
|
||||
let next_ch = native_list_get(chars, next_i)
|
||||
let next_ch = native_char_at(next_i)
|
||||
if next_ch == "\"" {
|
||||
let text = text + "\""
|
||||
let i = next_i + 1
|
||||
@@ -244,13 +249,13 @@ fn scan_string(chars: [String], start: Int, total: Int) -> Map<String, Any> {
|
||||
// ── Main lexer ────────────────────────────────────────────────────────────────
|
||||
|
||||
fn lex(source: String) -> [Map<String, Any>] {
|
||||
let chars: [String] = native_string_chars(source)
|
||||
let total: Int = native_list_len(chars)
|
||||
native_chars_init(source)
|
||||
let total: Int = native_char_len()
|
||||
let tokens: [Map<String, Any>] = native_list_empty()
|
||||
let i: Int = 0
|
||||
|
||||
while i < total {
|
||||
let ch: String = native_list_get(chars, i)
|
||||
let ch: String = native_char_at(i)
|
||||
|
||||
// Skip whitespace
|
||||
if is_whitespace(ch) {
|
||||
@@ -260,7 +265,7 @@ fn lex(source: String) -> [Map<String, Any>] {
|
||||
if ch == "/" {
|
||||
let next_i = i + 1
|
||||
if next_i < total {
|
||||
let next_ch: String = native_list_get(chars, next_i)
|
||||
let next_ch: String = native_char_at(next_i)
|
||||
if next_ch == "/" {
|
||||
// skip to end of line
|
||||
let i = i + 2
|
||||
@@ -269,7 +274,7 @@ fn lex(source: String) -> [Map<String, Any>] {
|
||||
if i >= total {
|
||||
let running2 = false
|
||||
} else {
|
||||
let lch: String = native_list_get(chars, i)
|
||||
let lch: String = native_char_at(i)
|
||||
if lch == "\n" {
|
||||
let running2 = false
|
||||
} else {
|
||||
@@ -288,7 +293,7 @@ fn lex(source: String) -> [Map<String, Any>] {
|
||||
} else {
|
||||
// String literal
|
||||
if ch == "\"" {
|
||||
let result = scan_string(chars, i + 1, total)
|
||||
let result = scan_string(i + 1, total)
|
||||
let str_text: String = result["text"]
|
||||
let new_pos: Int = result["pos"]
|
||||
let tokens = native_list_append(tokens, make_tok("Str", str_text))
|
||||
@@ -296,18 +301,18 @@ fn lex(source: String) -> [Map<String, Any>] {
|
||||
} else {
|
||||
// Number literal
|
||||
if is_digit(ch) {
|
||||
let result = scan_digits(chars, i, total)
|
||||
let result = scan_digits(i, total)
|
||||
let num_text: String = result["text"]
|
||||
let new_pos: Int = result["pos"]
|
||||
// check for float (dot followed by digit)
|
||||
if new_pos < total {
|
||||
let dot_ch: String = native_list_get(chars, new_pos)
|
||||
let dot_ch: String = native_char_at(new_pos)
|
||||
if dot_ch == "." {
|
||||
let after_dot = new_pos + 1
|
||||
if after_dot < total {
|
||||
let after_dot_ch: String = native_list_get(chars, after_dot)
|
||||
let after_dot_ch: String = native_char_at(after_dot)
|
||||
if is_digit(after_dot_ch) {
|
||||
let frac_result = scan_digits(chars, after_dot, total)
|
||||
let frac_result = scan_digits(after_dot, total)
|
||||
let frac_text: String = frac_result["text"]
|
||||
let frac_pos: Int = frac_result["pos"]
|
||||
let tokens = native_list_append(tokens, make_tok("Float", num_text + "." + frac_text))
|
||||
@@ -331,7 +336,7 @@ fn lex(source: String) -> [Map<String, Any>] {
|
||||
} else {
|
||||
// Identifier or keyword
|
||||
if is_alpha(ch) || ch == "_" {
|
||||
let result = scan_ident(chars, i, total)
|
||||
let result = scan_ident(i, total)
|
||||
let word: String = result["text"]
|
||||
let new_pos: Int = result["pos"]
|
||||
let kw = keyword_kind(word)
|
||||
@@ -346,7 +351,7 @@ fn lex(source: String) -> [Map<String, Any>] {
|
||||
let peek_i = i + 1
|
||||
let peek_ch = ""
|
||||
if peek_i < total {
|
||||
let peek_ch = native_list_get(chars, peek_i)
|
||||
let peek_ch = native_char_at(peek_i)
|
||||
}
|
||||
|
||||
if ch == "=" {
|
||||
|
||||
+146
-140
@@ -6,26 +6,30 @@
|
||||
// The cursor (integer position into the token list) is threaded through every
|
||||
// parse function. Functions return { "node": <map>, "pos": <int> }.
|
||||
//
|
||||
// The token list is stored in the VM's global token buffer via
|
||||
// native_tokens_init / native_token_at / native_token_len. This avoids
|
||||
// O(n²) cloning that occurs when passing the list as a function argument.
|
||||
//
|
||||
// Entry point: fn parse(tokens: [Map<String, Any>]) -> [Map<String, Any>]
|
||||
|
||||
// ── Token access helpers ──────────────────────────────────────────────────────
|
||||
|
||||
fn tok_at(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
native_list_get(tokens, pos)
|
||||
fn tok_at(pos: Int) -> Map<String, Any> {
|
||||
native_token_at(pos)
|
||||
}
|
||||
|
||||
fn tok_kind(tokens: [Map<String, Any>], pos: Int) -> String {
|
||||
let t = tok_at(tokens, pos)
|
||||
fn tok_kind(pos: Int) -> String {
|
||||
let t = native_token_at(pos)
|
||||
t["kind"]
|
||||
}
|
||||
|
||||
fn tok_value(tokens: [Map<String, Any>], pos: Int) -> String {
|
||||
let t = tok_at(tokens, pos)
|
||||
fn tok_value(pos: Int) -> String {
|
||||
let t = native_token_at(pos)
|
||||
t["value"]
|
||||
}
|
||||
|
||||
fn expect(tokens: [Map<String, Any>], pos: Int, kind: String) -> Int {
|
||||
let k = tok_kind(tokens, pos)
|
||||
fn expect(pos: Int, kind: String) -> Int {
|
||||
let k = tok_kind(pos)
|
||||
if k == kind {
|
||||
return pos + 1
|
||||
}
|
||||
@@ -43,26 +47,26 @@ fn make_result(node: Map<String, Any>, pos: Int) -> Map<String, Any> {
|
||||
// Skips over a type annotation, returning the new position.
|
||||
// Types can be: Ident, [Type], Map<K,V>, Type?, Type<Type,...>
|
||||
|
||||
fn skip_type(tokens: [Map<String, Any>], pos: Int) -> Int {
|
||||
let k = tok_kind(tokens, pos)
|
||||
fn skip_type(pos: Int) -> Int {
|
||||
let k = tok_kind(pos)
|
||||
// Array type: [Type]
|
||||
if k == "LBracket" {
|
||||
let p = pos + 1
|
||||
let p = skip_type(tokens, p)
|
||||
let p = expect(tokens, p, "RBracket")
|
||||
let p = skip_type(p)
|
||||
let p = expect(p, "RBracket")
|
||||
return p
|
||||
}
|
||||
// Named type (possibly generic)
|
||||
if k == "Ident" {
|
||||
let p = pos + 1
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "Lt" {
|
||||
// Generic params: skip until matching >
|
||||
let p = p + 1
|
||||
let depth = 1
|
||||
let running = true
|
||||
while running {
|
||||
let kk = tok_kind(tokens, p)
|
||||
let kk = tok_kind(p)
|
||||
if kk == "Eof" {
|
||||
let running = false
|
||||
} else {
|
||||
@@ -82,7 +86,7 @@ fn skip_type(tokens: [Map<String, Any>], pos: Int) -> Int {
|
||||
}
|
||||
}
|
||||
}
|
||||
let k3 = tok_kind(tokens, p)
|
||||
let k3 = tok_kind(p)
|
||||
if k3 == "QuestionMark" {
|
||||
let p = p + 1
|
||||
}
|
||||
@@ -100,12 +104,12 @@ fn skip_type(tokens: [Map<String, Any>], pos: Int) -> Int {
|
||||
// ── Parameter list ────────────────────────────────────────────────────────────
|
||||
// Parses (name: Type, name: Type, ...) — returns { "params": [...], "pos": ... }
|
||||
|
||||
fn parse_params(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let p = expect(tokens, pos, "LParen")
|
||||
fn parse_params(pos: Int) -> Map<String, Any> {
|
||||
let p = expect(pos, "LParen")
|
||||
let params: [Map<String, Any>] = native_list_empty()
|
||||
let running = true
|
||||
while running {
|
||||
let k = tok_kind(tokens, p)
|
||||
let k = tok_kind(p)
|
||||
if k == "RParen" {
|
||||
let running = false
|
||||
} else {
|
||||
@@ -113,28 +117,28 @@ fn parse_params(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let running = false
|
||||
} else {
|
||||
// param name
|
||||
let pname = tok_value(tokens, p)
|
||||
let pname = tok_value(p)
|
||||
let p = p + 1
|
||||
let p = expect(tokens, p, "Colon")
|
||||
let p = skip_type(tokens, p)
|
||||
let p = expect(p, "Colon")
|
||||
let p = skip_type(p)
|
||||
let param = { "name": pname }
|
||||
let params = native_list_append(params, param)
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "Comma" {
|
||||
let p = p + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let p = expect(tokens, p, "RParen")
|
||||
let p = expect(p, "RParen")
|
||||
{ "params": params, "pos": p }
|
||||
}
|
||||
|
||||
// ── Expression parsing ────────────────────────────────────────────────────────
|
||||
|
||||
fn parse_primary(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let k = tok_kind(tokens, pos)
|
||||
let v = tok_value(tokens, pos)
|
||||
fn parse_primary(pos: Int) -> Map<String, Any> {
|
||||
let k = tok_kind(pos)
|
||||
let v = tok_value(pos)
|
||||
|
||||
// Integer literal
|
||||
if k == "Int" {
|
||||
@@ -163,10 +167,10 @@ fn parse_primary(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
|
||||
// Grouped expression
|
||||
if k == "LParen" {
|
||||
let r = parse_expr(tokens, pos + 1)
|
||||
let r = parse_expr(pos + 1)
|
||||
let node = r["node"]
|
||||
let p = r["pos"]
|
||||
let p = expect(tokens, p, "RParen")
|
||||
let p = expect(p, "RParen")
|
||||
return make_result(node, p)
|
||||
}
|
||||
|
||||
@@ -176,25 +180,25 @@ fn parse_primary(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let elems: [Map<String, Any>] = native_list_empty()
|
||||
let running = true
|
||||
while running {
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "RBracket" {
|
||||
let running = false
|
||||
} else {
|
||||
if k2 == "Eof" {
|
||||
let running = false
|
||||
} else {
|
||||
let r = parse_expr(tokens, p)
|
||||
let r = parse_expr(p)
|
||||
let elem = r["node"]
|
||||
let p = r["pos"]
|
||||
let elems = native_list_append(elems, elem)
|
||||
let k3 = tok_kind(tokens, p)
|
||||
let k3 = tok_kind(p)
|
||||
if k3 == "Comma" {
|
||||
let p = p + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let p = expect(tokens, p, "RBracket")
|
||||
let p = expect(p, "RBracket")
|
||||
return make_result({ "expr": "Array", "elems": elems }, p)
|
||||
}
|
||||
|
||||
@@ -204,7 +208,7 @@ fn parse_primary(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let pairs: [Map<String, Any>] = native_list_empty()
|
||||
let running = true
|
||||
while running {
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "RBrace" {
|
||||
let running = false
|
||||
} else {
|
||||
@@ -212,46 +216,46 @@ fn parse_primary(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let running = false
|
||||
} else {
|
||||
// key: Str token
|
||||
let key = tok_value(tokens, p)
|
||||
let key = tok_value(p)
|
||||
let p = p + 1
|
||||
let p = expect(tokens, p, "Colon")
|
||||
let r = parse_expr(tokens, p)
|
||||
let p = expect(p, "Colon")
|
||||
let r = parse_expr(p)
|
||||
let val_node = r["node"]
|
||||
let p = r["pos"]
|
||||
let pair = { "key": key, "value": val_node }
|
||||
let pairs = native_list_append(pairs, pair)
|
||||
let k3 = tok_kind(tokens, p)
|
||||
let k3 = tok_kind(p)
|
||||
if k3 == "Comma" {
|
||||
let p = p + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let p = expect(tokens, p, "RBrace")
|
||||
let p = expect(p, "RBrace")
|
||||
return make_result({ "expr": "Map", "pairs": pairs }, p)
|
||||
}
|
||||
|
||||
// if expression
|
||||
if k == "If" {
|
||||
let r = parse_if(tokens, pos)
|
||||
let r = parse_if(pos)
|
||||
return r
|
||||
}
|
||||
|
||||
// match expression
|
||||
if k == "Match" {
|
||||
let r = parse_match(tokens, pos)
|
||||
let r = parse_match(pos)
|
||||
return r
|
||||
}
|
||||
|
||||
// for expression (used as statement)
|
||||
if k == "For" {
|
||||
let r = parse_for_expr(tokens, pos)
|
||||
let r = parse_for_expr(pos)
|
||||
return r
|
||||
}
|
||||
|
||||
// Unary not
|
||||
if k == "Not" {
|
||||
let r = parse_primary(tokens, pos + 1)
|
||||
let r = parse_primary(pos + 1)
|
||||
let inner = r["node"]
|
||||
let p = r["pos"]
|
||||
return make_result({ "expr": "Not", "inner": inner }, p)
|
||||
@@ -259,7 +263,7 @@ fn parse_primary(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
|
||||
// Unary minus
|
||||
if k == "Minus" {
|
||||
let r = parse_primary(tokens, pos + 1)
|
||||
let r = parse_primary(pos + 1)
|
||||
let inner = r["node"]
|
||||
let p = r["pos"]
|
||||
return make_result({ "expr": "Neg", "inner": inner }, p)
|
||||
@@ -269,29 +273,29 @@ fn parse_primary(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
make_result({ "expr": "Nil" }, pos + 1)
|
||||
}
|
||||
|
||||
fn parse_if(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let p = expect(tokens, pos, "If")
|
||||
let r = parse_expr(tokens, p)
|
||||
fn parse_if(pos: Int) -> Map<String, Any> {
|
||||
let p = expect(pos, "If")
|
||||
let r = parse_expr(p)
|
||||
let cond = r["node"]
|
||||
let p = r["pos"]
|
||||
let r2 = parse_block(tokens, p)
|
||||
let r2 = parse_block(p)
|
||||
let then_stmts = r2["stmts"]
|
||||
let p = r2["pos"]
|
||||
let has_else = false
|
||||
let else_stmts: [Map<String, Any>] = native_list_empty()
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "Else" {
|
||||
let p = p + 1
|
||||
let k3 = tok_kind(tokens, p)
|
||||
let k3 = tok_kind(p)
|
||||
if k3 == "If" {
|
||||
// else-if chain: parse as nested if
|
||||
let r3 = parse_if(tokens, p)
|
||||
let r3 = parse_if(p)
|
||||
let nested = r3["node"]
|
||||
let p = r3["pos"]
|
||||
let else_stmts = native_list_append(else_stmts, { "stmt": "Expr", "value": nested })
|
||||
let has_else = true
|
||||
} else {
|
||||
let r3 = parse_block(tokens, p)
|
||||
let r3 = parse_block(p)
|
||||
let else_stmts = r3["stmts"]
|
||||
let p = r3["pos"]
|
||||
let has_else = true
|
||||
@@ -300,16 +304,16 @@ fn parse_if(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
make_result({ "expr": "If", "cond": cond, "then": then_stmts, "else": else_stmts, "has_else": has_else }, p)
|
||||
}
|
||||
|
||||
fn parse_match(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let p = expect(tokens, pos, "Match")
|
||||
let r = parse_expr(tokens, p)
|
||||
fn parse_match(pos: Int) -> Map<String, Any> {
|
||||
let p = expect(pos, "Match")
|
||||
let r = parse_expr(p)
|
||||
let subject = r["node"]
|
||||
let p = r["pos"]
|
||||
let p = expect(tokens, p, "LBrace")
|
||||
let p = expect(p, "LBrace")
|
||||
let arms: [Map<String, Any>] = native_list_empty()
|
||||
let running = true
|
||||
while running {
|
||||
let k = tok_kind(tokens, p)
|
||||
let k = tok_kind(p)
|
||||
if k == "RBrace" {
|
||||
let running = false
|
||||
} else {
|
||||
@@ -317,131 +321,131 @@ fn parse_match(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let running = false
|
||||
} else {
|
||||
// parse pattern => body
|
||||
let r2 = parse_pattern(tokens, p)
|
||||
let r2 = parse_pattern(p)
|
||||
let pattern = r2["node"]
|
||||
let p = r2["pos"]
|
||||
let p = expect(tokens, p, "FatArrow")
|
||||
let r3 = parse_expr(tokens, p)
|
||||
let p = expect(p, "FatArrow")
|
||||
let r3 = parse_expr(p)
|
||||
let body = r3["node"]
|
||||
let p = r3["pos"]
|
||||
let arm = { "pattern": pattern, "body": body }
|
||||
let arms = native_list_append(arms, arm)
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "Comma" {
|
||||
let p = p + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let p = expect(tokens, p, "RBrace")
|
||||
let p = expect(p, "RBrace")
|
||||
make_result({ "expr": "Match", "subject": subject, "arms": arms }, p)
|
||||
}
|
||||
|
||||
fn parse_pattern(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let k = tok_kind(tokens, pos)
|
||||
fn parse_pattern(pos: Int) -> Map<String, Any> {
|
||||
let k = tok_kind(pos)
|
||||
if k == "Ident" {
|
||||
let v = tok_value(tokens, pos)
|
||||
let v = tok_value(pos)
|
||||
if v == "_" {
|
||||
return make_result({ "pattern": "Wildcard" }, pos + 1)
|
||||
}
|
||||
return make_result({ "pattern": "Binding", "name": v }, pos + 1)
|
||||
}
|
||||
if k == "Int" {
|
||||
return make_result({ "pattern": "LitInt", "value": tok_value(tokens, pos) }, pos + 1)
|
||||
return make_result({ "pattern": "LitInt", "value": tok_value(pos) }, pos + 1)
|
||||
}
|
||||
if k == "Str" {
|
||||
return make_result({ "pattern": "LitStr", "value": tok_value(tokens, pos) }, pos + 1)
|
||||
return make_result({ "pattern": "LitStr", "value": tok_value(pos) }, pos + 1)
|
||||
}
|
||||
if k == "Bool" {
|
||||
return make_result({ "pattern": "LitBool", "value": tok_value(tokens, pos) }, pos + 1)
|
||||
return make_result({ "pattern": "LitBool", "value": tok_value(pos) }, pos + 1)
|
||||
}
|
||||
// Wildcard _
|
||||
make_result({ "pattern": "Wildcard" }, pos + 1)
|
||||
}
|
||||
|
||||
fn parse_for_expr(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let p = expect(tokens, pos, "For")
|
||||
let item_name = tok_value(tokens, p)
|
||||
fn parse_for_expr(pos: Int) -> Map<String, Any> {
|
||||
let p = expect(pos, "For")
|
||||
let item_name = tok_value(p)
|
||||
let p = p + 1
|
||||
let p = expect(tokens, p, "In")
|
||||
let r = parse_expr(tokens, p)
|
||||
let p = expect(p, "In")
|
||||
let r = parse_expr(p)
|
||||
let list_expr = r["node"]
|
||||
let p = r["pos"]
|
||||
let r2 = parse_block(tokens, p)
|
||||
let r2 = parse_block(p)
|
||||
let body = r2["stmts"]
|
||||
let p = r2["pos"]
|
||||
make_result({ "expr": "For", "item": item_name, "list": list_expr, "body": body }, p)
|
||||
}
|
||||
|
||||
fn parse_block(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let p = expect(tokens, pos, "LBrace")
|
||||
fn parse_block(pos: Int) -> Map<String, Any> {
|
||||
let p = expect(pos, "LBrace")
|
||||
let stmts: [Map<String, Any>] = native_list_empty()
|
||||
let running = true
|
||||
while running {
|
||||
let k = tok_kind(tokens, p)
|
||||
let k = tok_kind(p)
|
||||
if k == "RBrace" {
|
||||
let running = false
|
||||
} else {
|
||||
if k == "Eof" {
|
||||
let running = false
|
||||
} else {
|
||||
let r = parse_stmt(tokens, p)
|
||||
let r = parse_stmt(p)
|
||||
let stmt = r["node"]
|
||||
let p = r["pos"]
|
||||
let stmts = native_list_append(stmts, stmt)
|
||||
}
|
||||
}
|
||||
}
|
||||
let p = expect(tokens, p, "RBrace")
|
||||
let p = expect(p, "RBrace")
|
||||
{ "stmts": stmts, "pos": p }
|
||||
}
|
||||
|
||||
// ── Postfix expressions (calls, field access, index) ─────────────────────────
|
||||
|
||||
fn parse_postfix(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let r = parse_primary(tokens, pos)
|
||||
fn parse_postfix(pos: Int) -> Map<String, Any> {
|
||||
let r = parse_primary(pos)
|
||||
let node = r["node"]
|
||||
let p = r["pos"]
|
||||
let running = true
|
||||
while running {
|
||||
let k = tok_kind(tokens, p)
|
||||
let k = tok_kind(p)
|
||||
if k == "LParen" {
|
||||
// function call
|
||||
let p = p + 1
|
||||
let args: [Map<String, Any>] = native_list_empty()
|
||||
let run2 = true
|
||||
while run2 {
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "RParen" {
|
||||
let run2 = false
|
||||
} else {
|
||||
if k2 == "Eof" {
|
||||
let run2 = false
|
||||
} else {
|
||||
let r2 = parse_expr(tokens, p)
|
||||
let r2 = parse_expr(p)
|
||||
let arg = r2["node"]
|
||||
let p = r2["pos"]
|
||||
let args = native_list_append(args, arg)
|
||||
let k3 = tok_kind(tokens, p)
|
||||
let k3 = tok_kind(p)
|
||||
if k3 == "Comma" {
|
||||
let p = p + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let p = expect(tokens, p, "RParen")
|
||||
let p = expect(p, "RParen")
|
||||
let node = { "expr": "Call", "func": node, "args": args }
|
||||
} else {
|
||||
if k == "Dot" {
|
||||
let field = tok_value(tokens, p + 1)
|
||||
let field = tok_value(p + 1)
|
||||
let p = p + 2
|
||||
let node = { "expr": "Field", "object": node, "field": field }
|
||||
} else {
|
||||
if k == "LBracket" {
|
||||
let r2 = parse_expr(tokens, p + 1)
|
||||
let r2 = parse_expr(p + 1)
|
||||
let idx = r2["node"]
|
||||
let p = r2["pos"]
|
||||
let p = expect(tokens, p, "RBracket")
|
||||
let p = expect(p, "RBracket")
|
||||
let node = { "expr": "Index", "object": node, "index": idx }
|
||||
} else {
|
||||
if k == "QuestionMark" {
|
||||
@@ -491,18 +495,18 @@ fn is_binop(kind: String) -> Bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn parse_binop(tokens: [Map<String, Any>], pos: Int, min_prec: Int) -> Map<String, Any> {
|
||||
let r = parse_postfix(tokens, pos)
|
||||
fn parse_binop(pos: Int, min_prec: Int) -> Map<String, Any> {
|
||||
let r = parse_postfix(pos)
|
||||
let left = r["node"]
|
||||
let p = r["pos"]
|
||||
let running = true
|
||||
while running {
|
||||
let k = tok_kind(tokens, p)
|
||||
let k = tok_kind(p)
|
||||
let prec = op_precedence(k)
|
||||
if is_binop(k) {
|
||||
if prec >= min_prec {
|
||||
let op = k
|
||||
let r2 = parse_binop(tokens, p + 1, prec + 1)
|
||||
let r2 = parse_binop(p + 1, prec + 1)
|
||||
let right = r2["node"]
|
||||
let p = r2["pos"]
|
||||
let left = { "expr": "BinOp", "op": op, "left": left, "right": right }
|
||||
@@ -516,28 +520,28 @@ fn parse_binop(tokens: [Map<String, Any>], pos: Int, min_prec: Int) -> Map<Strin
|
||||
make_result(left, p)
|
||||
}
|
||||
|
||||
fn parse_expr(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
parse_binop(tokens, pos, 1)
|
||||
fn parse_expr(pos: Int) -> Map<String, Any> {
|
||||
parse_binop(pos, 1)
|
||||
}
|
||||
|
||||
// ── Statement parsing ─────────────────────────────────────────────────────────
|
||||
|
||||
fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let k = tok_kind(tokens, pos)
|
||||
fn parse_stmt(pos: Int) -> Map<String, Any> {
|
||||
let k = tok_kind(pos)
|
||||
|
||||
// let binding
|
||||
if k == "Let" {
|
||||
let p = pos + 1
|
||||
let name = tok_value(tokens, p)
|
||||
let name = tok_value(p)
|
||||
let p = p + 1
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
// optional type annotation: name: Type
|
||||
if k2 == "Colon" {
|
||||
let p = p + 1
|
||||
let p = skip_type(tokens, p)
|
||||
let p = skip_type(p)
|
||||
}
|
||||
let p = expect(tokens, p, "Eq")
|
||||
let r = parse_expr(tokens, p)
|
||||
let p = expect(p, "Eq")
|
||||
let r = parse_expr(p)
|
||||
let val = r["node"]
|
||||
let p = r["pos"]
|
||||
return make_result({ "stmt": "Let", "name": name, "value": val }, p)
|
||||
@@ -546,14 +550,14 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
// return statement
|
||||
if k == "Return" {
|
||||
let p = pos + 1
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "RBrace" {
|
||||
return make_result({ "stmt": "Return", "value": { "expr": "Nil" } }, p)
|
||||
}
|
||||
if k2 == "Eof" {
|
||||
return make_result({ "stmt": "Return", "value": { "expr": "Nil" } }, p)
|
||||
}
|
||||
let r = parse_expr(tokens, p)
|
||||
let r = parse_expr(p)
|
||||
let val = r["node"]
|
||||
let p = r["pos"]
|
||||
return make_result({ "stmt": "Return", "value": val }, p)
|
||||
@@ -562,18 +566,18 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
// fn definition
|
||||
if k == "Fn" {
|
||||
let p = pos + 1
|
||||
let name = tok_value(tokens, p)
|
||||
let name = tok_value(p)
|
||||
let p = p + 1
|
||||
let r = parse_params(tokens, p)
|
||||
let r = parse_params(p)
|
||||
let params = r["params"]
|
||||
let p = r["pos"]
|
||||
// return type annotation: -> Type
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "Arrow" {
|
||||
let p = p + 1
|
||||
let p = skip_type(tokens, p)
|
||||
let p = skip_type(p)
|
||||
}
|
||||
let r2 = parse_block(tokens, p)
|
||||
let r2 = parse_block(p)
|
||||
let body = r2["stmts"]
|
||||
let p = r2["pos"]
|
||||
return make_result({ "stmt": "FnDef", "name": name, "params": params, "body": body }, p)
|
||||
@@ -582,69 +586,69 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
// type definition
|
||||
if k == "Type" {
|
||||
let p = pos + 1
|
||||
let name = tok_value(tokens, p)
|
||||
let name = tok_value(p)
|
||||
let p = p + 1
|
||||
let p = expect(tokens, p, "LBrace")
|
||||
let p = expect(p, "LBrace")
|
||||
let fields: [Map<String, Any>] = native_list_empty()
|
||||
let running = true
|
||||
while running {
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "RBrace" {
|
||||
let running = false
|
||||
} else {
|
||||
if k2 == "Eof" {
|
||||
let running = false
|
||||
} else {
|
||||
let fname = tok_value(tokens, p)
|
||||
let fname = tok_value(p)
|
||||
let p = p + 1
|
||||
let p = expect(tokens, p, "Colon")
|
||||
let p = skip_type(tokens, p)
|
||||
let p = expect(p, "Colon")
|
||||
let p = skip_type(p)
|
||||
let fields = native_list_append(fields, { "name": fname })
|
||||
let k3 = tok_kind(tokens, p)
|
||||
let k3 = tok_kind(p)
|
||||
if k3 == "Comma" {
|
||||
let p = p + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let p = expect(tokens, p, "RBrace")
|
||||
let p = expect(p, "RBrace")
|
||||
return make_result({ "stmt": "TypeDef", "name": name, "fields": fields }, p)
|
||||
}
|
||||
|
||||
// enum definition
|
||||
if k == "Enum" {
|
||||
let p = pos + 1
|
||||
let name = tok_value(tokens, p)
|
||||
let name = tok_value(p)
|
||||
let p = p + 1
|
||||
let p = expect(tokens, p, "LBrace")
|
||||
let p = expect(p, "LBrace")
|
||||
let variants: [Map<String, Any>] = native_list_empty()
|
||||
let running = true
|
||||
while running {
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "RBrace" {
|
||||
let running = false
|
||||
} else {
|
||||
if k2 == "Eof" {
|
||||
let running = false
|
||||
} else {
|
||||
let vname = tok_value(tokens, p)
|
||||
let vname = tok_value(p)
|
||||
let p = p + 1
|
||||
let variants = native_list_append(variants, { "name": vname })
|
||||
let k3 = tok_kind(tokens, p)
|
||||
let k3 = tok_kind(p)
|
||||
if k3 == "Comma" {
|
||||
let p = p + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let p = expect(tokens, p, "RBrace")
|
||||
let p = expect(p, "RBrace")
|
||||
return make_result({ "stmt": "EnumDef", "name": name, "variants": variants }, p)
|
||||
}
|
||||
|
||||
// import statement
|
||||
if k == "Import" {
|
||||
let p = pos + 1
|
||||
let path = tok_value(tokens, p)
|
||||
let path = tok_value(p)
|
||||
let p = p + 1
|
||||
return make_result({ "stmt": "Import", "path": path }, p)
|
||||
}
|
||||
@@ -652,20 +656,20 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
// from ... import { ... }
|
||||
if k == "From" {
|
||||
let p = pos + 1
|
||||
let module_name = tok_value(tokens, p)
|
||||
let module_name = tok_value(p)
|
||||
let p = p + 1
|
||||
// skip "import" keyword
|
||||
let k2 = tok_kind(tokens, p)
|
||||
let k2 = tok_kind(p)
|
||||
if k2 == "Import" {
|
||||
let p = p + 1
|
||||
}
|
||||
// skip { Name, ... }
|
||||
let k3 = tok_kind(tokens, p)
|
||||
let k3 = tok_kind(p)
|
||||
if k3 == "LBrace" {
|
||||
let p = p + 1
|
||||
let running = true
|
||||
while running {
|
||||
let k4 = tok_kind(tokens, p)
|
||||
let k4 = tok_kind(p)
|
||||
if k4 == "RBrace" {
|
||||
let running = false
|
||||
} else {
|
||||
@@ -673,14 +677,14 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let running = false
|
||||
} else {
|
||||
let p = p + 1
|
||||
let k5 = tok_kind(tokens, p)
|
||||
let k5 = tok_kind(p)
|
||||
if k5 == "Comma" {
|
||||
let p = p + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let p = expect(tokens, p, "RBrace")
|
||||
let p = expect(p, "RBrace")
|
||||
}
|
||||
return make_result({ "stmt": "Import", "path": module_name }, p)
|
||||
}
|
||||
@@ -688,10 +692,10 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
// while loop
|
||||
if k == "While" {
|
||||
let p = pos + 1
|
||||
let r = parse_expr(tokens, p)
|
||||
let r = parse_expr(p)
|
||||
let cond = r["node"]
|
||||
let p = r["pos"]
|
||||
let r2 = parse_block(tokens, p)
|
||||
let r2 = parse_block(p)
|
||||
let body = r2["stmts"]
|
||||
let p = r2["pos"]
|
||||
return make_result({ "stmt": "While", "cond": cond, "body": body }, p)
|
||||
@@ -700,13 +704,13 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
// for loop
|
||||
if k == "For" {
|
||||
let p = pos + 1
|
||||
let item_name = tok_value(tokens, p)
|
||||
let item_name = tok_value(p)
|
||||
let p = p + 1
|
||||
let p = expect(tokens, p, "In")
|
||||
let r = parse_expr(tokens, p)
|
||||
let p = expect(p, "In")
|
||||
let r = parse_expr(p)
|
||||
let list_expr = r["node"]
|
||||
let p = r["pos"]
|
||||
let r2 = parse_block(tokens, p)
|
||||
let r2 = parse_block(p)
|
||||
let body = r2["stmts"]
|
||||
let p = r2["pos"]
|
||||
return make_result({ "stmt": "For", "item": item_name, "list": list_expr, "body": body }, p)
|
||||
@@ -717,11 +721,11 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
let p = pos + 1
|
||||
// skip decorator name
|
||||
let p = p + 1
|
||||
return parse_stmt(tokens, p)
|
||||
return parse_stmt(p)
|
||||
}
|
||||
|
||||
// bare expression or if/match statement
|
||||
let r = parse_expr(tokens, pos)
|
||||
let r = parse_expr(pos)
|
||||
let val = r["node"]
|
||||
let p = r["pos"]
|
||||
make_result({ "stmt": "Expr", "value": val }, p)
|
||||
@@ -730,7 +734,9 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
|
||||
// ── Top-level parse ────────────────────────────────────────────────────────────
|
||||
|
||||
fn parse(tokens: [Map<String, Any>]) -> [Map<String, Any>] {
|
||||
let total: Int = native_list_len(tokens)
|
||||
// Store tokens in global buffer to avoid O(n²) cloning on every recursive call.
|
||||
native_tokens_init(tokens)
|
||||
let total: Int = native_token_len()
|
||||
let stmts: [Map<String, Any>] = native_list_empty()
|
||||
let pos: Int = 0
|
||||
let running = true
|
||||
@@ -738,11 +744,11 @@ fn parse(tokens: [Map<String, Any>]) -> [Map<String, Any>] {
|
||||
if pos >= total {
|
||||
let running = false
|
||||
} else {
|
||||
let k = tok_kind(tokens, pos)
|
||||
let k = tok_kind(pos)
|
||||
if k == "Eof" {
|
||||
let running = false
|
||||
} else {
|
||||
let r = parse_stmt(tokens, pos)
|
||||
let r = parse_stmt(pos)
|
||||
let stmt = r["node"]
|
||||
let new_pos: Int = r["pos"]
|
||||
let stmts = native_list_append(stmts, stmt)
|
||||
|
||||
Reference in New Issue
Block a user