mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
batch A
This commit is contained in:
@@ -68,6 +68,7 @@ pub enum StmtKind {
|
||||
assert_stmt
|
||||
try_stmt
|
||||
throw_stmt
|
||||
defer_stmt
|
||||
}
|
||||
|
||||
// MatchArm is a single `value { body }` arm of a match statement.
|
||||
|
||||
+8
-1
@@ -292,6 +292,11 @@ fn (mut c Checker) check_stmt(st Stmt) ! {
|
||||
.throw_stmt {
|
||||
_ = c.check_expr(st.expr)!
|
||||
}
|
||||
.defer_stmt {
|
||||
for s in st.body {
|
||||
c.check_stmt(s)!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +496,7 @@ fn (mut c Checker) check_binary(e Expr) !TypeInfo {
|
||||
}
|
||||
TypeInfo{ kind: .bool_t }
|
||||
}
|
||||
.kw_and, .kw_or {
|
||||
.kw_and, .kw_or, .kw_in {
|
||||
TypeInfo{ kind: .bool_t }
|
||||
}
|
||||
.amp, .pipe, .caret, .lt_lt, .gt_gt {
|
||||
@@ -635,6 +640,8 @@ fn builtin_result_type(name string) TypeInfo {
|
||||
'flag_val' { TypeInfo{ kind: .string_t } }
|
||||
'flag_has' { TypeInfo{ kind: .int_t } }
|
||||
'flag_positional' { TypeInfo{ kind: .array_t } }
|
||||
'type_info' { TypeInfo{ kind: .struct_t } }
|
||||
'range' { TypeInfo{ kind: .array_t } }
|
||||
'build_is_dir' { TypeInfo{ kind: .int_t } }
|
||||
// build-module builtins (.vrmm)
|
||||
'build_compile', 'build_assemble', 'build_link', 'build_exec', 'build_base',
|
||||
|
||||
+110
-5
@@ -47,6 +47,12 @@ mut:
|
||||
modules map[string]bool // imported module names (bare `import os`)
|
||||
fn_names map[string]bool // top-level function names usable as closure values
|
||||
captures []string // enclosing locals captured by the closure being compiled
|
||||
defers []Stmt // deferred statements of the current function (in order)
|
||||
has_defers bool // current function registers deferred cleanup
|
||||
defer_ret_slot int // hidden local holding the return value while defers run
|
||||
defer_value_seen bool // a `return expr` (value) appeared, so restore+retv after defers
|
||||
cur_defer_label string // label of the current function's deferred-cleanup block
|
||||
namer_id int // unique-id source for generated label names
|
||||
}
|
||||
|
||||
fn gen(prog Program) !obj.Obj {
|
||||
@@ -226,10 +232,45 @@ fn (mut g Gen) gen_fn(fd FnDecl) ! {
|
||||
g.code << obj.encode_i64(i64(g.argc))
|
||||
g.code << obj.encode_i64(i64(vidx))
|
||||
}
|
||||
// one-pass defer handling: collect every `defer` statement in the body first
|
||||
// (so all returns can be redirected to the deferred-cleanup block), then
|
||||
// reserve a hidden slot to hold the return value across that block.
|
||||
g.defers = []Stmt{}
|
||||
g.has_defers = false
|
||||
g.defer_value_seen = false
|
||||
g.collect_defers(fd.body, mut g.defers)
|
||||
if g.defers.len > 0 {
|
||||
g.has_defers = true
|
||||
g.defer_ret_slot = g.local_cnt
|
||||
g.local_cnt++
|
||||
}
|
||||
g.cur_defer_label = g.new_label()
|
||||
for st in fd.body {
|
||||
g.gen_stmt(st)!
|
||||
}
|
||||
g.code << op_ret // trailing return for fall-through
|
||||
|
||||
if g.has_defers {
|
||||
g.code << op_jmp
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: g.cur_defer_label, off: u32(g.code.len) - 8 }
|
||||
} else {
|
||||
g.code << op_ret // trailing return for fall-through
|
||||
}
|
||||
// emit the deferred-cleanup block (LIFO) then the real return here, so a
|
||||
// `return` in the body jumps into this block and lands back on the return
|
||||
if g.has_defers {
|
||||
g.emit_label(g.cur_defer_label)
|
||||
for i := g.defers.len - 1; i >= 0; i-- {
|
||||
g.gen_stmt(g.defers[i])!
|
||||
}
|
||||
if g.defer_value_seen {
|
||||
// restore the saved return value and return it
|
||||
g.emit_load(g.defer_ret_slot)
|
||||
g.code << op_retv
|
||||
} else {
|
||||
g.code << op_ret
|
||||
}
|
||||
}
|
||||
// reserve all local slots: the callee may be called with fewer arguments
|
||||
// than declared (default parameters) or more (variadic), so the frame must
|
||||
// always cover slots 0..local_cnt-1
|
||||
@@ -245,6 +286,8 @@ fn (mut g Gen) gen_fn(fd FnDecl) ! {
|
||||
}
|
||||
g.fixups.clear()
|
||||
g.labels.clear()
|
||||
g.defers = []Stmt{} // reset for the next (possibly enclosing) function
|
||||
g.has_defers = false
|
||||
// snapshot the live locals as debug info for the debugger: name -> slot
|
||||
for name, slot in g.locals {
|
||||
g.dbg_locals << obj.DbgLocal{ fn: sym, name: name, slot: slot }
|
||||
@@ -519,13 +562,34 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
g.emit_label(end_l)
|
||||
}
|
||||
.ret_stmt {
|
||||
if st.has_val {
|
||||
g.gen_expr(st.expr)!
|
||||
g.code << op_retv
|
||||
if g.has_defers {
|
||||
// save any return value in the hidden slot, jump into the
|
||||
// deferred-cleanup block, which restores and returns it
|
||||
if st.has_val {
|
||||
g.defer_value_seen = true
|
||||
g.gen_expr(st.expr)!
|
||||
g.emit_store(g.defer_ret_slot)
|
||||
} else {
|
||||
g.code << op_push_i
|
||||
g.code << obj.encode_i64(0)
|
||||
g.emit_store(g.defer_ret_slot)
|
||||
}
|
||||
g.code << op_jmp
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: g.cur_defer_label, off: u32(g.code.len) - 8 }
|
||||
} else {
|
||||
g.code << op_ret
|
||||
if st.has_val {
|
||||
g.gen_expr(st.expr)!
|
||||
g.code << op_retv
|
||||
} else {
|
||||
g.code << op_ret
|
||||
}
|
||||
}
|
||||
}
|
||||
.defer_stmt {
|
||||
// collected in gen_fn's pass and emitted in the cleanup block;
|
||||
// nothing to place at the declaration site
|
||||
}
|
||||
.assert_stmt {
|
||||
g.gen_expr(st.expr)!
|
||||
g.code << op_assert
|
||||
@@ -1047,6 +1111,9 @@ fn builtin_spec(name string) (int, int) {
|
||||
'flag_val' { native_flag_val, 1 }
|
||||
'flag_has' { native_flag_has, 1 }
|
||||
'flag_positional' { native_flag_positional, 0 }
|
||||
// structured reflection + sequence helper
|
||||
'type_info' { native_type_info, 1 }
|
||||
'range' { native_range, 2 }
|
||||
'build_is_dir' { native_build_is_dir, 1 }
|
||||
'cwd' { native_cwd, 0 }
|
||||
'json_pretty' { native_json_pretty, 1 }
|
||||
@@ -1257,6 +1324,7 @@ fn (mut g Gen) gen_binary(e Expr) ! {
|
||||
.le { op_le }
|
||||
.gt { op_gt }
|
||||
.ge { op_ge }
|
||||
.kw_in { op_in }
|
||||
.amp { op_and_b }
|
||||
.pipe { op_or_b }
|
||||
.caret { op_xor }
|
||||
@@ -1510,6 +1578,38 @@ fn (mut g Gen) maybe_capture(name string, bound map[string]bool, mut caps []stri
|
||||
}
|
||||
}
|
||||
|
||||
// collect_defers walks a statement list, gathering the inner statements of
|
||||
// every `defer` into `out` in source order. Called once per function before
|
||||
// code generation so all return sites can be redirected to the cleanup block.
|
||||
fn (mut g Gen) collect_defers(stmts []Stmt, mut out []Stmt) {
|
||||
for st in stmts {
|
||||
match st.kind {
|
||||
.defer_stmt {
|
||||
for d in st.body {
|
||||
out << d
|
||||
}
|
||||
}
|
||||
.if_stmt {
|
||||
g.collect_defers(st.body, mut out)
|
||||
g.collect_defers(st.els, mut out)
|
||||
}
|
||||
.match_stmt {
|
||||
for a in st.arms {
|
||||
g.collect_defers(a.body, mut out)
|
||||
}
|
||||
g.collect_defers(st.els_body, mut out)
|
||||
}
|
||||
.while_stmt, .for_range_stmt, .for_in_stmt, .try_stmt {
|
||||
g.collect_defers(st.body, mut out)
|
||||
if st.kind == .try_stmt {
|
||||
g.collect_defers(st.els, mut out)
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) scan_stmt(st Stmt, mut bound map[string]bool, mut caps []string, mut seen map[string]bool) {
|
||||
match st.kind {
|
||||
.expr_stmt {
|
||||
@@ -1623,6 +1723,11 @@ fn (mut g Gen) scan_stmt(st Stmt, mut bound map[string]bool, mut caps []string,
|
||||
.throw_stmt {
|
||||
g.scan_expr(st.expr, mut bound, mut caps, mut seen)
|
||||
}
|
||||
.defer_stmt {
|
||||
for s in st.body {
|
||||
g.scan_stmt(s, mut bound, mut caps, mut seen)
|
||||
}
|
||||
}
|
||||
.break_stmt, .continue_stmt {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,6 +310,7 @@ fn (mut l Lexer) lex_ident(line int, col int) Tok {
|
||||
'interface' { TokKind.kw_interface }
|
||||
'try' { TokKind.kw_try }
|
||||
'catch' { TokKind.kw_catch }
|
||||
'defer' { TokKind.kw_defer }
|
||||
'throw' { TokKind.kw_throw }
|
||||
else { TokKind.ident }
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ const op_load_dyn = u8(59) // pop idx, push stack[bp + idx]
|
||||
const op_varargs = u8(60) // <named:i64> <dst:i64> — collect args[named..argc-1] into an array at local dst
|
||||
const op_str_method = u8(61) // <name:str> <argc:i64> — call a string method (s.len(), s.contains(x), ...)
|
||||
const op_push_none = u8(62) // push the `none` sentinel
|
||||
const op_in = u8(63) // membership: x in col -> 0 or 1
|
||||
|
||||
// native builtin ids (keep in sync with vm/opcodes.v)
|
||||
const native_abs = 100
|
||||
@@ -191,3 +192,7 @@ const native_input = 196
|
||||
const native_flag_val = 197
|
||||
const native_flag_has = 198
|
||||
const native_flag_positional = 199
|
||||
|
||||
// structured reflection + sequence helper
|
||||
const native_type_info = 200
|
||||
const native_range = 201
|
||||
|
||||
+6
-1
@@ -403,6 +403,11 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
||||
body := p.parse_block()!
|
||||
return Stmt{ kind: .for_in_stmt, target: val_name, idx_target: idx_name, expr: first, body: body, line: t.line }
|
||||
}
|
||||
.kw_defer {
|
||||
p.advance()
|
||||
inner := p.parse_stmt()!
|
||||
return Stmt{ kind: .defer_stmt, body: [inner], line: t.line }
|
||||
}
|
||||
.kw_return {
|
||||
p.advance()
|
||||
mut e := Expr{}
|
||||
@@ -664,7 +669,7 @@ fn (mut p Parser) parse_eq() !Expr {
|
||||
|
||||
fn (mut p Parser) parse_rel() !Expr {
|
||||
mut e := p.parse_shift()!
|
||||
for p.cur().kind == .lt || p.cur().kind == .le || p.cur().kind == .gt || p.cur().kind == .ge {
|
||||
for p.cur().kind == .lt || p.cur().kind == .le || p.cur().kind == .gt || p.cur().kind == .ge || p.cur().kind == .kw_in {
|
||||
op := p.advance()
|
||||
rhs := p.parse_shift()!
|
||||
e = bin_node(op.kind, e, rhs, op.line)
|
||||
|
||||
@@ -69,6 +69,7 @@ pub enum TokKind {
|
||||
kw_interface
|
||||
kw_try
|
||||
kw_catch
|
||||
kw_defer
|
||||
kw_throw
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user