mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
Loops
This commit is contained in:
@@ -41,6 +41,11 @@ const op_print = u8(28)
|
||||
const op_println = u8(29)
|
||||
const op_assert = u8(30)
|
||||
const op_enter = u8(31)
|
||||
const op_mkarray = u8(32)
|
||||
const op_aget = u8(33)
|
||||
const op_aset = u8(34)
|
||||
const op_alen = u8(35)
|
||||
const op_apush = u8(36)
|
||||
|
||||
// compile parses and compiles VuurRaaf source into an object file.
|
||||
pub fn compile(src string) !obj.Obj {
|
||||
@@ -151,6 +156,12 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
g.code << op_store
|
||||
g.code << obj.encode_i64(i64(idx))
|
||||
}
|
||||
.index_assign {
|
||||
g.gen_expr(st.base)!
|
||||
g.gen_expr(st.idx)!
|
||||
g.gen_expr(st.expr)!
|
||||
g.code << op_aset
|
||||
}
|
||||
.if_stmt {
|
||||
else_l := g.new_label()
|
||||
end_l := g.new_label()
|
||||
@@ -186,6 +197,87 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
g.fixups << Fixup{ name: loop_l, off: u32(g.code.len) - 8 }
|
||||
g.emit_label(end_l)
|
||||
}
|
||||
.for_range_stmt {
|
||||
// for i in a..b / for i in a...b → i := a; while i <(<=) b { body; i++ }
|
||||
var_idx := g.new_local()
|
||||
bound_idx := g.new_local()
|
||||
loop_l := g.new_label()
|
||||
end_l := g.new_label()
|
||||
g.gen_expr(st.expr)!
|
||||
g.gen_expr(st.cond)!
|
||||
g.emit_store(bound_idx)
|
||||
g.emit_store(var_idx)
|
||||
g.emit_label(loop_l)
|
||||
g.emit_load(var_idx)
|
||||
g.emit_load(bound_idx)
|
||||
g.code << if st.inclusive { op_le } else { op_lt }
|
||||
g.code << op_jz
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
||||
prev := g.locals[st.target] or { -1 }
|
||||
g.locals[st.target] = var_idx
|
||||
for s in st.body {
|
||||
g.gen_stmt(s)!
|
||||
}
|
||||
if prev >= 0 {
|
||||
g.locals[st.target] = prev
|
||||
} else {
|
||||
g.locals.delete(st.target)
|
||||
}
|
||||
g.emit_load(var_idx)
|
||||
g.code << op_push_i
|
||||
g.code << obj.encode_i64(1)
|
||||
g.code << op_add
|
||||
g.emit_store(var_idx)
|
||||
g.code << op_jmp
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: loop_l, off: u32(g.code.len) - 8 }
|
||||
g.emit_label(end_l)
|
||||
}
|
||||
.for_in_stmt {
|
||||
// for x in arr → idx := 0; while idx < len(arr) { x := arr[idx]; body; idx++ }
|
||||
arr_idx := g.new_local()
|
||||
idx_idx := g.new_local()
|
||||
elem_idx := g.new_local()
|
||||
loop_l := g.new_label()
|
||||
end_l := g.new_label()
|
||||
g.gen_expr(st.expr)!
|
||||
g.emit_store(arr_idx)
|
||||
g.code << op_push_i
|
||||
g.code << obj.encode_i64(0)
|
||||
g.emit_store(idx_idx)
|
||||
g.emit_label(loop_l)
|
||||
g.emit_load(idx_idx)
|
||||
g.emit_load(arr_idx)
|
||||
g.code << op_alen
|
||||
g.code << op_lt
|
||||
g.code << op_jz
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
||||
g.emit_load(arr_idx)
|
||||
g.emit_load(idx_idx)
|
||||
g.code << op_aget
|
||||
g.emit_store(elem_idx)
|
||||
prev := g.locals[st.target] or { -1 }
|
||||
g.locals[st.target] = elem_idx
|
||||
for s in st.body {
|
||||
g.gen_stmt(s)!
|
||||
}
|
||||
if prev >= 0 {
|
||||
g.locals[st.target] = prev
|
||||
} else {
|
||||
g.locals.delete(st.target)
|
||||
}
|
||||
g.emit_load(idx_idx)
|
||||
g.code << op_push_i
|
||||
g.code << obj.encode_i64(1)
|
||||
g.code << op_add
|
||||
g.emit_store(idx_idx)
|
||||
g.code << op_jmp
|
||||
g.code << obj.encode_i64(0)
|
||||
g.fixups << Fixup{ name: loop_l, off: u32(g.code.len) - 8 }
|
||||
g.emit_label(end_l)
|
||||
}
|
||||
.ret_stmt {
|
||||
if st.has_val {
|
||||
g.gen_expr(st.expr)!
|
||||
@@ -214,6 +306,18 @@ fn (mut g Gen) gen_expr(e Expr) ! {
|
||||
g.code << obj.encode_i64(0)
|
||||
g.relocs << obj.Reloc{ offset: u32(g.code.len) - 8, name: e.str_v, kind: 1 }
|
||||
}
|
||||
.array_lit {
|
||||
for el in e.elems {
|
||||
g.gen_expr(el)!
|
||||
}
|
||||
g.code << op_mkarray
|
||||
g.code << obj.encode_i64(i64(e.elems.len))
|
||||
}
|
||||
.index {
|
||||
g.gen_expr(*e.left)!
|
||||
g.gen_expr(*e.right)!
|
||||
g.code << op_aget
|
||||
}
|
||||
.bool_lit {
|
||||
g.code << op_push_i
|
||||
g.code << obj.encode_i64(e.int_v)
|
||||
@@ -251,6 +355,23 @@ fn (mut g Gen) gen_call(e Expr) ! {
|
||||
g.code << if e.name == 'print' { op_print } else { op_println }
|
||||
return
|
||||
}
|
||||
if e.name == 'len' {
|
||||
if e.args.len != 1 {
|
||||
return error('len() takes exactly one argument (line ${e.line})')
|
||||
}
|
||||
g.gen_expr(e.args[0])!
|
||||
g.code << op_alen
|
||||
return
|
||||
}
|
||||
if e.name == 'push' {
|
||||
if e.args.len != 2 {
|
||||
return error('push() takes exactly two arguments (line ${e.line})')
|
||||
}
|
||||
g.gen_expr(e.args[0])!
|
||||
g.gen_expr(e.args[1])!
|
||||
g.code << op_apush
|
||||
return
|
||||
}
|
||||
for a in e.args {
|
||||
g.gen_expr(a)!
|
||||
}
|
||||
@@ -340,6 +461,24 @@ fn (mut g Gen) intern(s string) int {
|
||||
return idx
|
||||
}
|
||||
|
||||
fn (mut g Gen) new_local() int {
|
||||
idx := g.local_cnt
|
||||
g.local_cnt++
|
||||
return idx
|
||||
}
|
||||
|
||||
fn (mut g Gen) emit_load(idx int) {
|
||||
g.code << op_load
|
||||
g.code << obj.encode_i64(i64(idx))
|
||||
}
|
||||
|
||||
fn (mut g Gen) emit_store(idx int) {
|
||||
g.code << op_store
|
||||
g.code << obj.encode_i64(i64(idx))
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn (mut g Gen) new_label() string {
|
||||
g.next_lbl++
|
||||
return 'L${g.next_lbl}'
|
||||
|
||||
@@ -10,6 +10,8 @@ pub enum TokKind {
|
||||
rparen
|
||||
lbrace
|
||||
rbrace
|
||||
lbracket
|
||||
rbracket
|
||||
comma
|
||||
plus
|
||||
minus
|
||||
@@ -23,11 +25,15 @@ pub enum TokKind {
|
||||
gt
|
||||
ge
|
||||
assign
|
||||
dotdot
|
||||
dotdotdot
|
||||
kw_fn
|
||||
kw_let
|
||||
kw_if
|
||||
kw_else
|
||||
kw_while
|
||||
kw_for
|
||||
kw_in
|
||||
kw_return
|
||||
kw_true
|
||||
kw_false
|
||||
@@ -127,6 +133,26 @@ fn (mut l Lexer) next() !Tok {
|
||||
l.advance()
|
||||
return Tok{ kind: .rbrace, lit: '}', line: line }
|
||||
}
|
||||
`[` {
|
||||
l.advance()
|
||||
return Tok{ kind: .lbracket, lit: '[', line: line }
|
||||
}
|
||||
`]` {
|
||||
l.advance()
|
||||
return Tok{ kind: .rbracket, lit: ']', line: line }
|
||||
}
|
||||
`.` {
|
||||
l.advance()
|
||||
if l.peek() == `.` {
|
||||
l.advance()
|
||||
if l.peek() == `.` {
|
||||
l.advance()
|
||||
return Tok{ kind: .dotdotdot, lit: '...', line: line }
|
||||
}
|
||||
return Tok{ kind: .dotdot, lit: '..', line: line }
|
||||
}
|
||||
return error('unexpected character "." at line ${line}')
|
||||
}
|
||||
`,` {
|
||||
l.advance()
|
||||
return Tok{ kind: .comma, lit: ',', line: line }
|
||||
@@ -223,6 +249,8 @@ fn (mut l Lexer) lex_ident(line int) Tok {
|
||||
'if' { TokKind.kw_if }
|
||||
'else' { TokKind.kw_else }
|
||||
'while' { TokKind.kw_while }
|
||||
'for' { TokKind.kw_for }
|
||||
'in' { TokKind.kw_in }
|
||||
'return' { TokKind.kw_return }
|
||||
'true' { TokKind.kw_true }
|
||||
'false' { TokKind.kw_false }
|
||||
|
||||
+94
-9
@@ -6,8 +6,11 @@
|
||||
// block := '{' stmt* '}'
|
||||
// stmt := 'let' IDENT '=' expr
|
||||
// | IDENT '=' expr
|
||||
// | postfix '=' expr (a[i] = v)
|
||||
// | 'if' cond block ['else' block]
|
||||
// | 'while' cond block
|
||||
// | 'for' IDENT 'in' range block (range := expr '..' expr | expr '...' expr)
|
||||
// | 'for' IDENT 'in' expr block (iterate an array)
|
||||
// | 'return' [expr]
|
||||
// | 'assert' expr
|
||||
// | expr
|
||||
@@ -27,6 +30,8 @@ pub enum ExprKind {
|
||||
str_lit
|
||||
bool_lit
|
||||
ident
|
||||
array_lit
|
||||
index
|
||||
unary
|
||||
binary
|
||||
call
|
||||
@@ -41,6 +46,7 @@ pub mut:
|
||||
op TokKind
|
||||
left &Expr = unsafe { nil }
|
||||
right &Expr = unsafe { nil }
|
||||
elems []Expr
|
||||
args []Expr
|
||||
line int
|
||||
}
|
||||
@@ -49,22 +55,28 @@ pub enum StmtKind {
|
||||
expr_stmt
|
||||
let_stmt
|
||||
assign_stmt
|
||||
index_assign
|
||||
if_stmt
|
||||
while_stmt
|
||||
for_range_stmt
|
||||
for_in_stmt
|
||||
ret_stmt
|
||||
assert_stmt
|
||||
}
|
||||
|
||||
pub struct Stmt {
|
||||
pub mut:
|
||||
kind StmtKind
|
||||
target string
|
||||
expr Expr
|
||||
cond Expr
|
||||
body []Stmt
|
||||
els []Stmt
|
||||
has_val bool
|
||||
line int
|
||||
kind StmtKind
|
||||
target string
|
||||
expr Expr
|
||||
cond Expr
|
||||
base Expr // index_assign: the indexed expression
|
||||
idx Expr // index_assign: the index expression
|
||||
body []Stmt
|
||||
els []Stmt
|
||||
has_val bool
|
||||
inclusive bool // for_range_stmt: `..` (false) vs `...` (true)
|
||||
line int
|
||||
}
|
||||
|
||||
pub struct FnDecl {
|
||||
@@ -195,6 +207,21 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
||||
body := p.parse_block()!
|
||||
return Stmt{ kind: .while_stmt, cond: cond, body: body, line: t.line }
|
||||
}
|
||||
.kw_for {
|
||||
p.advance()
|
||||
name := p.expect(.ident, 'loop variable')!
|
||||
p.expect(.kw_in, "'in'")!
|
||||
first := p.parse_expr()!
|
||||
if p.cur().kind == .dotdot || p.cur().kind == .dotdotdot {
|
||||
inclusive := p.cur().kind == .dotdotdot
|
||||
p.advance()
|
||||
end := p.parse_expr()!
|
||||
body := p.parse_block()!
|
||||
return Stmt{ kind: .for_range_stmt, target: name.lit, expr: first, cond: end, inclusive: inclusive, body: body, line: t.line }
|
||||
}
|
||||
body := p.parse_block()!
|
||||
return Stmt{ kind: .for_in_stmt, target: name.lit, expr: first, body: body, line: t.line }
|
||||
}
|
||||
.kw_return {
|
||||
p.advance()
|
||||
mut e := Expr{}
|
||||
@@ -222,6 +249,16 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
||||
e := p.parse_expr()!
|
||||
return Stmt{ kind: .assign_stmt, target: t.lit, expr: e, line: t.line }
|
||||
}
|
||||
if p.cur().kind == .lbracket {
|
||||
// a[i] = v or a[i] (expression statement)
|
||||
e := p.parse_index_chain(t)!
|
||||
if p.cur().kind == .assign {
|
||||
p.advance()
|
||||
rhs := p.parse_expr()!
|
||||
return Stmt{ kind: .index_assign, base: *e.left, idx: *e.right, expr: rhs, line: t.line }
|
||||
}
|
||||
return Stmt{ kind: .expr_stmt, expr: e, line: t.line }
|
||||
}
|
||||
e := p.parse_call_or_ident(t)!
|
||||
return Stmt{ kind: .expr_stmt, expr: e, line: t.line }
|
||||
}
|
||||
@@ -250,6 +287,13 @@ fn unary_node(op TokKind, operand Expr, line int) Expr {
|
||||
return Expr{ kind: .unary, op: op, right: &o, line: line }
|
||||
}
|
||||
|
||||
// index_node builds `base[idx]`.
|
||||
fn index_node(base Expr, idx Expr, line int) Expr {
|
||||
mut b := base
|
||||
mut i := idx
|
||||
return Expr{ kind: .index, left: &b, right: &i, line: line }
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_expr() !Expr {
|
||||
return p.parse_or()!
|
||||
}
|
||||
@@ -321,7 +365,32 @@ fn (mut p Parser) parse_unary() !Expr {
|
||||
e := p.parse_unary()!
|
||||
return unary_node(t.kind, e, t.line)
|
||||
}
|
||||
return p.parse_primary()!
|
||||
return p.parse_postfix()!
|
||||
}
|
||||
|
||||
// parse_postfix handles indexing: `base[expr]`, possibly chained `a[i][j]`.
|
||||
fn (mut p Parser) parse_postfix() !Expr {
|
||||
mut e := p.parse_primary()!
|
||||
for p.cur().kind == .lbracket {
|
||||
p.advance()
|
||||
idx := p.parse_expr()!
|
||||
p.expect(.rbracket, "']'")!
|
||||
e = index_node(e, idx, e.line)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// parse_index_chain is like parse_postfix but starts from an already-consumed
|
||||
// identifier token (used for statements like `a[i] = v`).
|
||||
fn (mut p Parser) parse_index_chain(t Tok) !Expr {
|
||||
mut e := Expr{ kind: .ident, name: t.lit, line: t.line }
|
||||
for p.cur().kind == .lbracket {
|
||||
p.advance()
|
||||
idx := p.parse_expr()!
|
||||
p.expect(.rbracket, "']'")!
|
||||
e = index_node(e, idx, t.line)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_primary() !Expr {
|
||||
@@ -349,6 +418,22 @@ fn (mut p Parser) parse_primary() !Expr {
|
||||
p.expect(.rparen, "')'")!
|
||||
return e
|
||||
}
|
||||
.lbracket {
|
||||
p.advance()
|
||||
mut elems := []Expr{}
|
||||
if p.cur().kind != .rbracket {
|
||||
for {
|
||||
elems << p.parse_expr()!
|
||||
if p.cur().kind == .comma {
|
||||
p.advance()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
p.expect(.rbracket, "']'")!
|
||||
return Expr{ kind: .array_lit, elems: elems, line: t.line }
|
||||
}
|
||||
.ident {
|
||||
p.advance()
|
||||
return p.parse_call_or_ident(t)!
|
||||
|
||||
Reference in New Issue
Block a user