mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 14:57:18 +00:00
more
This commit is contained in:
@@ -46,6 +46,9 @@ const op_aget = u8(33)
|
||||
const op_aset = u8(34)
|
||||
const op_alen = u8(35)
|
||||
const op_apush = u8(36)
|
||||
const op_mkstruct = u8(37)
|
||||
const op_sget = u8(38)
|
||||
const op_sset = u8(39)
|
||||
|
||||
// compile parses and compiles VuurRaaf source into an object file.
|
||||
pub fn compile(src string) !obj.Obj {
|
||||
@@ -171,6 +174,13 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
||||
g.gen_expr(st.expr)!
|
||||
g.code << op_aset
|
||||
}
|
||||
.field_assign {
|
||||
// a.b = v → a, v, "b" sset (field name on top of the stack)
|
||||
g.gen_expr(st.base)!
|
||||
g.gen_expr(st.expr)!
|
||||
g.emit_field_name(st.target)
|
||||
g.code << op_sset
|
||||
}
|
||||
.if_stmt {
|
||||
else_l := g.new_label()
|
||||
end_l := g.new_label()
|
||||
@@ -384,6 +394,21 @@ fn (mut g Gen) gen_expr(e Expr) ! {
|
||||
g.code << op_mkarray
|
||||
g.code << obj.encode_i64(i64(e.elems.len))
|
||||
}
|
||||
.struct_lit {
|
||||
// for each field: push the name string then the value; mkstruct n
|
||||
// pops the (name, value) pairs and builds the record
|
||||
for f in e.fields {
|
||||
g.emit_field_name(f.name)
|
||||
g.gen_expr(f.val)!
|
||||
}
|
||||
g.code << op_mkstruct
|
||||
g.code << obj.encode_i64(i64(e.fields.len))
|
||||
}
|
||||
.field {
|
||||
g.gen_expr(*e.left)!
|
||||
g.emit_field_name(e.name)
|
||||
g.code << op_sget
|
||||
}
|
||||
.index {
|
||||
g.gen_expr(*e.left)!
|
||||
g.gen_expr(*e.right)!
|
||||
@@ -522,6 +547,14 @@ fn (mut g Gen) gen_binary(e Expr) ! {
|
||||
}
|
||||
}
|
||||
|
||||
// emit_field_name pushes a field name as a string constant. Like string
|
||||
// literals it goes through a kind-1 relocation so multi-file links rebase it.
|
||||
fn (mut g Gen) emit_field_name(name string) {
|
||||
g.code << op_push_s
|
||||
g.code << obj.encode_i64(0)
|
||||
g.relocs << obj.Reloc{ offset: u32(g.code.len) - 8, name: name, kind: 1 }
|
||||
}
|
||||
|
||||
fn (mut g Gen) intern(s string) int {
|
||||
if s in g.str_map {
|
||||
return g.str_map[s]
|
||||
|
||||
+7
-1
@@ -13,6 +13,8 @@ pub enum TokKind {
|
||||
lbracket
|
||||
rbracket
|
||||
comma
|
||||
dot
|
||||
colon
|
||||
plus
|
||||
minus
|
||||
star
|
||||
@@ -154,12 +156,16 @@ fn (mut l Lexer) next() !Tok {
|
||||
}
|
||||
return Tok{ kind: .dotdot, lit: '..', line: line }
|
||||
}
|
||||
return error('unexpected character "." at line ${line}')
|
||||
return Tok{ kind: .dot, lit: '.', line: line }
|
||||
}
|
||||
`,` {
|
||||
l.advance()
|
||||
return Tok{ kind: .comma, lit: ',', line: line }
|
||||
}
|
||||
`:` {
|
||||
l.advance()
|
||||
return Tok{ kind: .colon, lit: ':', line: line }
|
||||
}
|
||||
`+` {
|
||||
l.advance()
|
||||
return Tok{ kind: .plus, lit: '+', line: line }
|
||||
|
||||
+98
-41
@@ -6,7 +6,7 @@
|
||||
// block := '{' stmt* '}'
|
||||
// stmt := 'let' IDENT '=' expr
|
||||
// | IDENT '=' expr
|
||||
// | postfix '=' expr (a[i] = v)
|
||||
// | postfix '=' expr (a[i] = v, a.b = v)
|
||||
// | 'if' cond block ['else' ('if' ... | block)]
|
||||
// | 'match' expr '{' (expr block | 'else' block)* '}'
|
||||
// | 'while' cond block
|
||||
@@ -25,6 +25,8 @@
|
||||
// add := mul (('*'|'/'|'%') mul)*
|
||||
// mul := ('not'|'-') mul | primary
|
||||
// primary := INT | STR | 'true' | 'false' | IDENT ['(' args ')'] | '(' expr ')'
|
||||
// | '{' [IDENT ':' expr (',' IDENT ':' expr)*] '}' (struct literal)
|
||||
// postfix := primary ('.' IDENT | '[' expr ']')* (field access, indexing)
|
||||
module compiler
|
||||
|
||||
pub enum ExprKind {
|
||||
@@ -33,24 +35,34 @@ pub enum ExprKind {
|
||||
bool_lit
|
||||
ident
|
||||
array_lit
|
||||
struct_lit
|
||||
index
|
||||
field
|
||||
unary
|
||||
binary
|
||||
call
|
||||
}
|
||||
|
||||
// StructField is one `name: value` entry of a struct literal.
|
||||
pub struct StructField {
|
||||
pub mut:
|
||||
name string
|
||||
val Expr
|
||||
}
|
||||
|
||||
pub struct Expr {
|
||||
pub mut:
|
||||
kind ExprKind
|
||||
int_v i64
|
||||
str_v string
|
||||
name string
|
||||
op TokKind
|
||||
left &Expr = unsafe { nil }
|
||||
right &Expr = unsafe { nil }
|
||||
elems []Expr
|
||||
args []Expr
|
||||
line int
|
||||
kind ExprKind
|
||||
int_v i64
|
||||
str_v string
|
||||
name string // ident/call name, or the field name of a `.field` access
|
||||
op TokKind
|
||||
left &Expr = unsafe { nil }
|
||||
right &Expr = unsafe { nil }
|
||||
elems []Expr
|
||||
fields []StructField // struct_lit: the named fields
|
||||
args []Expr
|
||||
line int
|
||||
}
|
||||
|
||||
pub enum StmtKind {
|
||||
@@ -58,6 +70,7 @@ pub enum StmtKind {
|
||||
let_stmt
|
||||
assign_stmt
|
||||
index_assign
|
||||
field_assign
|
||||
if_stmt
|
||||
match_stmt
|
||||
while_stmt
|
||||
@@ -283,22 +296,33 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
||||
}
|
||||
.ident {
|
||||
p.advance()
|
||||
mut e := Expr{}
|
||||
if p.cur().kind == .lparen {
|
||||
// a call statement: foo(args), optionally chained foo().x
|
||||
e = p.parse_call(t)!
|
||||
e = p.parse_postfix_tail(e)!
|
||||
} else {
|
||||
e = p.parse_postfix_tail(Expr{ kind: .ident, name: t.lit, line: t.line })!
|
||||
}
|
||||
if p.cur().kind == .assign {
|
||||
// assignment to an ident, an index, or a field
|
||||
p.advance()
|
||||
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 }
|
||||
rhs := p.parse_expr()!
|
||||
match e.kind {
|
||||
.ident {
|
||||
return Stmt{ kind: .assign_stmt, target: e.name, expr: rhs, line: t.line }
|
||||
}
|
||||
.index {
|
||||
return Stmt{ kind: .index_assign, base: *e.left, idx: *e.right, expr: rhs, line: t.line }
|
||||
}
|
||||
.field {
|
||||
return Stmt{ kind: .field_assign, base: *e.left, target: e.name, expr: rhs, line: t.line }
|
||||
}
|
||||
else {
|
||||
return error('cannot assign to this expression (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 }
|
||||
}
|
||||
.kw_print, .kw_println {
|
||||
@@ -352,6 +376,12 @@ fn index_node(base Expr, idx Expr, line int) Expr {
|
||||
return Expr{ kind: .index, left: &b, right: &i, line: line }
|
||||
}
|
||||
|
||||
// field_node builds `base.name`.
|
||||
fn field_node(base Expr, name string, line int) Expr {
|
||||
mut b := base
|
||||
return Expr{ kind: .field, left: &b, name: name, line: line }
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_expr() !Expr {
|
||||
return p.parse_or()!
|
||||
}
|
||||
@@ -426,29 +456,36 @@ fn (mut p Parser) parse_unary() !Expr {
|
||||
return p.parse_postfix()!
|
||||
}
|
||||
|
||||
// parse_postfix handles indexing: `base[expr]`, possibly chained `a[i][j]`.
|
||||
// parse_postfix handles postfix operators after a primary: indexing
|
||||
// `a[i]` (chainable `a[i][j]`) and field access `a.b` (chainable `a.b.c`),
|
||||
// in any mix: `a[i].b`, `a.b[i]`, ...
|
||||
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
|
||||
return p.parse_postfix_tail(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)
|
||||
// parse_postfix_tail continues a postfix chain from an already-parsed base.
|
||||
// It takes the base by value (Expr only holds pointers to heap-allocated
|
||||
// child nodes) and returns the extended chain.
|
||||
fn (mut p Parser) parse_postfix_tail(e Expr) !Expr {
|
||||
mut cur := e
|
||||
for {
|
||||
if p.cur().kind == .lbracket {
|
||||
p.advance()
|
||||
idx := p.parse_expr()!
|
||||
p.expect(.rbracket, "']'")!
|
||||
cur = index_node(cur, idx, cur.line)
|
||||
continue
|
||||
}
|
||||
if p.cur().kind == .dot {
|
||||
p.advance()
|
||||
name := p.expect(.ident, 'field name')!
|
||||
cur = field_node(cur, name.lit, cur.line)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return e
|
||||
return cur
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_primary() !Expr {
|
||||
@@ -492,6 +529,26 @@ fn (mut p Parser) parse_primary() !Expr {
|
||||
p.expect(.rbracket, "']'")!
|
||||
return Expr{ kind: .array_lit, elems: elems, line: t.line }
|
||||
}
|
||||
.lbrace {
|
||||
// struct literal: { name: expr, ... }
|
||||
p.advance()
|
||||
mut fields := []StructField{}
|
||||
if p.cur().kind != .rbrace {
|
||||
for {
|
||||
name := p.expect(.ident, 'field name')!
|
||||
p.expect(.colon, "':'")!
|
||||
val := p.parse_expr()!
|
||||
fields << StructField{ name: name.lit, val: val }
|
||||
if p.cur().kind == .comma {
|
||||
p.advance()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
p.expect(.rbrace, "'}'")!
|
||||
return Expr{ kind: .struct_lit, fields: fields, line: t.line }
|
||||
}
|
||||
.ident {
|
||||
p.advance()
|
||||
return p.parse_call_or_ident(t)!
|
||||
|
||||
Reference in New Issue
Block a user