mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-27 16:57:01 +00:00
Enhacnementsand toolchain
This commit is contained in:
@@ -0,0 +1,388 @@
|
||||
// parser.v — recursive-descent parser for the VuurRaaf language.
|
||||
//
|
||||
// Grammar (informal):
|
||||
// program := fn*
|
||||
// fn := 'fn' IDENT '(' [IDENT (',' IDENT)*] ')' block
|
||||
// block := '{' stmt* '}'
|
||||
// stmt := 'let' IDENT '=' expr
|
||||
// | IDENT '=' expr
|
||||
// | 'if' cond block ['else' block]
|
||||
// | 'while' cond block
|
||||
// | 'return' [expr]
|
||||
// | 'assert' expr
|
||||
// | expr
|
||||
// cond := ['('] expr [')'] // parens optional
|
||||
// expr := or ('or' or)*
|
||||
// or := and ('and' and)*
|
||||
// and := eq (('=='|'!=') eq)*
|
||||
// eq := rel (('<'|'<='|'>'|'>=') rel)*
|
||||
// rel := add (('+'|'-') add)*
|
||||
// add := mul (('*'|'/'|'%') mul)*
|
||||
// mul := ('not'|'-') mul | primary
|
||||
// primary := INT | STR | 'true' | 'false' | IDENT ['(' args ')'] | '(' expr ')'
|
||||
module compiler
|
||||
|
||||
pub enum ExprKind {
|
||||
int_lit
|
||||
str_lit
|
||||
bool_lit
|
||||
ident
|
||||
unary
|
||||
binary
|
||||
call
|
||||
}
|
||||
|
||||
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 }
|
||||
args []Expr
|
||||
line int
|
||||
}
|
||||
|
||||
pub enum StmtKind {
|
||||
expr_stmt
|
||||
let_stmt
|
||||
assign_stmt
|
||||
if_stmt
|
||||
while_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
|
||||
}
|
||||
|
||||
pub struct FnDecl {
|
||||
pub mut:
|
||||
name string
|
||||
params []string
|
||||
body []Stmt
|
||||
line int
|
||||
}
|
||||
|
||||
pub struct Program {
|
||||
pub mut:
|
||||
fns []FnDecl
|
||||
}
|
||||
|
||||
pub fn parse(toks []Tok) !Program {
|
||||
mut p := Parser{ toks: toks }
|
||||
return p.parse_program()
|
||||
}
|
||||
|
||||
struct Parser {
|
||||
mut:
|
||||
toks []Tok
|
||||
pos int
|
||||
}
|
||||
|
||||
fn (mut p Parser) cur() Tok {
|
||||
if p.pos < p.toks.len {
|
||||
return p.toks[p.pos]
|
||||
}
|
||||
return p.toks[p.toks.len - 1]
|
||||
}
|
||||
|
||||
fn (mut p Parser) advance() Tok {
|
||||
t := p.cur()
|
||||
if p.pos < p.toks.len - 1 {
|
||||
p.pos++
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
fn (mut p Parser) expect(k TokKind, what string) !Tok {
|
||||
t := p.cur()
|
||||
if t.kind != k {
|
||||
return error('expected ${what}, got "${t.lit}" at line ${t.line}')
|
||||
}
|
||||
return p.advance()
|
||||
}
|
||||
|
||||
// parse_cond parses a condition, accepting either `if cond {` or `if (cond) {`.
|
||||
fn (mut p Parser) parse_cond() !Expr {
|
||||
if p.cur().kind == .lparen {
|
||||
p.advance()
|
||||
e := p.parse_expr()!
|
||||
p.expect(.rparen, "')'")!
|
||||
return e
|
||||
}
|
||||
return p.parse_expr()!
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_program() !Program {
|
||||
mut prog := Program{}
|
||||
for p.cur().kind != .eof {
|
||||
prog.fns << p.parse_fn()!
|
||||
}
|
||||
if prog.fns.len == 0 {
|
||||
return error('no functions found in source')
|
||||
}
|
||||
return prog
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_fn() !FnDecl {
|
||||
fn_tok := p.expect(.kw_fn, "'fn'")!
|
||||
name := p.expect(.ident, 'function name')!
|
||||
p.expect(.lparen, "'('")!
|
||||
mut params := []string{}
|
||||
if p.cur().kind != .rparen {
|
||||
for {
|
||||
params << p.expect(.ident, 'parameter name')!.lit
|
||||
if p.cur().kind == .comma {
|
||||
p.advance()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
p.expect(.rparen, "')'")!
|
||||
body := p.parse_block()!
|
||||
return FnDecl{ name: name.lit, params: params, body: body, line: fn_tok.line }
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_block() ![]Stmt {
|
||||
p.expect(.lbrace, "'{'")!
|
||||
mut stmts := []Stmt{}
|
||||
for p.cur().kind != .rbrace {
|
||||
if p.cur().kind == .eof {
|
||||
return error('unexpected end of file inside block (missing "}")')
|
||||
}
|
||||
stmts << p.parse_stmt()!
|
||||
}
|
||||
p.expect(.rbrace, "'}'")!
|
||||
return stmts
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_stmt() !Stmt {
|
||||
t := p.cur()
|
||||
match t.kind {
|
||||
.kw_let {
|
||||
p.advance()
|
||||
name := p.expect(.ident, 'variable name')!
|
||||
p.expect(.assign, "'='")!
|
||||
e := p.parse_expr()!
|
||||
return Stmt{ kind: .let_stmt, target: name.lit, expr: e, line: t.line }
|
||||
} .kw_if {
|
||||
p.advance()
|
||||
cond := p.parse_cond()!
|
||||
body := p.parse_block()!
|
||||
mut els := []Stmt{}
|
||||
if p.cur().kind == .kw_else {
|
||||
p.advance()
|
||||
els = p.parse_block()!
|
||||
}
|
||||
return Stmt{ kind: .if_stmt, cond: cond, body: body, els: els, line: t.line }
|
||||
}
|
||||
.kw_while {
|
||||
p.advance()
|
||||
cond := p.parse_cond()!
|
||||
body := p.parse_block()!
|
||||
return Stmt{ kind: .while_stmt, cond: cond, body: body, line: t.line }
|
||||
}
|
||||
.kw_return {
|
||||
p.advance()
|
||||
mut e := Expr{}
|
||||
has_val := p.cur().kind != .rbrace
|
||||
if has_val {
|
||||
e = p.parse_expr()!
|
||||
}
|
||||
return Stmt{ kind: .ret_stmt, expr: e, has_val: has_val, line: t.line }
|
||||
} .kw_assert {
|
||||
p.advance()
|
||||
mut e := Expr{}
|
||||
if p.cur().kind == .lparen {
|
||||
p.advance()
|
||||
e = p.parse_expr()!
|
||||
p.expect(.rparen, "')'")!
|
||||
} else {
|
||||
e = p.parse_expr()!
|
||||
}
|
||||
return Stmt{ kind: .assert_stmt, expr: e, line: t.line }
|
||||
}
|
||||
.ident {
|
||||
p.advance()
|
||||
if p.cur().kind == .assign {
|
||||
p.advance()
|
||||
e := p.parse_expr()!
|
||||
return Stmt{ kind: .assign_stmt, target: t.lit, 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 {
|
||||
p.advance()
|
||||
e := p.parse_call(t)!
|
||||
return Stmt{ kind: .expr_stmt, expr: e, line: t.line }
|
||||
}
|
||||
else {
|
||||
return error('unexpected token "${t.lit}" at line ${t.line}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bin_node allocates a binary-operator node. It takes copies of the operands
|
||||
// so that `&l`/`&r` target fresh heap objects (taking the address of a local
|
||||
// that is later reassigned would create a self-referential node).
|
||||
fn bin_node(op TokKind, left Expr, right Expr, line int) Expr {
|
||||
mut l := left
|
||||
mut r := right
|
||||
return Expr{ kind: .binary, op: op, left: &l, right: &r, line: line }
|
||||
}
|
||||
|
||||
fn unary_node(op TokKind, operand Expr, line int) Expr {
|
||||
mut o := operand
|
||||
return Expr{ kind: .unary, op: op, right: &o, line: line }
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_expr() !Expr {
|
||||
return p.parse_or()!
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_or() !Expr {
|
||||
mut e := p.parse_and()!
|
||||
for p.cur().kind == .kw_or {
|
||||
op := p.advance()
|
||||
rhs := p.parse_and()!
|
||||
e = bin_node(op.kind, e, rhs, op.line)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_and() !Expr {
|
||||
mut e := p.parse_eq()!
|
||||
for p.cur().kind == .kw_and {
|
||||
op := p.advance()
|
||||
rhs := p.parse_eq()!
|
||||
e = bin_node(op.kind, e, rhs, op.line)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_eq() !Expr {
|
||||
mut e := p.parse_rel()!
|
||||
for p.cur().kind == .eq_eq || p.cur().kind == .not_eq {
|
||||
op := p.advance()
|
||||
rhs := p.parse_rel()!
|
||||
e = bin_node(op.kind, e, rhs, op.line)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_rel() !Expr {
|
||||
mut e := p.parse_add()!
|
||||
for p.cur().kind == .lt || p.cur().kind == .le || p.cur().kind == .gt || p.cur().kind == .ge {
|
||||
op := p.advance()
|
||||
rhs := p.parse_add()!
|
||||
e = bin_node(op.kind, e, rhs, op.line)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_add() !Expr {
|
||||
mut e := p.parse_mul()!
|
||||
for p.cur().kind == .plus || p.cur().kind == .minus {
|
||||
op := p.advance()
|
||||
rhs := p.parse_mul()!
|
||||
e = bin_node(op.kind, e, rhs, op.line)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_mul() !Expr {
|
||||
mut e := p.parse_unary()!
|
||||
for p.cur().kind == .star || p.cur().kind == .slash || p.cur().kind == .percent {
|
||||
op := p.advance()
|
||||
rhs := p.parse_unary()!
|
||||
e = bin_node(op.kind, e, rhs, op.line)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_unary() !Expr {
|
||||
t := p.cur()
|
||||
if t.kind == .kw_not || t.kind == .minus {
|
||||
p.advance()
|
||||
e := p.parse_unary()!
|
||||
return unary_node(t.kind, e, t.line)
|
||||
}
|
||||
return p.parse_primary()!
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_primary() !Expr {
|
||||
t := p.cur()
|
||||
match t.kind {
|
||||
.int_lit {
|
||||
p.advance()
|
||||
return Expr{ kind: .int_lit, int_v: t.lit.i64(), line: t.line }
|
||||
}
|
||||
.str_lit {
|
||||
p.advance()
|
||||
return Expr{ kind: .str_lit, str_v: t.lit, line: t.line }
|
||||
}
|
||||
.kw_true {
|
||||
p.advance()
|
||||
return Expr{ kind: .bool_lit, int_v: 1, line: t.line }
|
||||
}
|
||||
.kw_false {
|
||||
p.advance()
|
||||
return Expr{ kind: .bool_lit, int_v: 0, line: t.line }
|
||||
}
|
||||
.lparen {
|
||||
p.advance()
|
||||
e := p.parse_expr()!
|
||||
p.expect(.rparen, "')'")!
|
||||
return e
|
||||
}
|
||||
.ident {
|
||||
p.advance()
|
||||
return p.parse_call_or_ident(t)!
|
||||
}
|
||||
.kw_print, .kw_println {
|
||||
p.advance()
|
||||
return p.parse_call(t)!
|
||||
}
|
||||
else {
|
||||
return error('unexpected token "${t.lit}" at line ${t.line}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_call_or_ident(t Tok) !Expr {
|
||||
if p.cur().kind == .lparen {
|
||||
return p.parse_call(t)!
|
||||
}
|
||||
return Expr{ kind: .ident, name: t.lit, line: t.line }
|
||||
}
|
||||
|
||||
fn (mut p Parser) parse_call(name Tok) !Expr {
|
||||
p.expect(.lparen, "'('")!
|
||||
mut args := []Expr{}
|
||||
if p.cur().kind != .rparen {
|
||||
for {
|
||||
args << p.parse_expr()!
|
||||
if p.cur().kind == .comma {
|
||||
p.advance()
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
p.expect(.rparen, "')'")!
|
||||
return Expr{ kind: .call, name: name.lit, args: args, line: name.line }
|
||||
}
|
||||
Reference in New Issue
Block a user