diff options
Diffstat (limited to 'src/parse.cff')
| -rw-r--r-- | src/parse.cff | 133 |
1 files changed, 126 insertions, 7 deletions
diff --git a/src/parse.cff b/src/parse.cff index f77c30f..2aed2da 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -591,6 +591,16 @@ fn exprdup(alloc *Allocator, ex Expr) *Expr { return memcpy(alloc->alloc(sizeof(ex)), &ex, sizeof(ex)); } +fn islvalue(ex Expr) bool { + switch ex.u { + case Symbol decl; + return decl.u.#tag == :Let or decl.u.#tag == :Static; + case UnOp u; + return u.op == :deref; + } + return #f; +} + fn parseexpr(P *Parser) Expr; fn pexprimary(P *Parser) Expr { @@ -616,7 +626,15 @@ fn pexprimary(P *Parser) Expr { switch decl.u { case Fn f; return { tok.loc, f.ty, .u: :Symbol decl }; - } + case Let var; + return { tok.loc, var.ty, .u: :Symbol decl }; + case Static var; + return { tok.loc, var.ty, .u: :Symbol decl }; + } + case '('; + let ex = parseexpr(P); + lexexpect(P, ')'); + return ex; case else; fatal(P, tok.loc, "expected expression (near %qT)", tok); } @@ -629,20 +647,28 @@ fn pexpostfix(P *Parser) Expr { switch { case lexmatch(P, &tok, '('); let lhs = exprdup(P.alloc, ex), - ty = lhs.ty.u.#tag == :Ptr ? lhs.ty.u.Ptr : lhs.ty; - if ty.u.#tag != :Fn { + ty = lhs.ty->is(:Ptr) ? lhs.ty.u.Ptr : lhs.ty; + if not ty->is(:Fn) { fatal(P, lhs.loc, "not callable (%t)", lhs.ty); } + let Fn = &ty.u.Fn; let args Vec<Expr> = {}; while not lexmatch(P, #null, ')') { args->push(parseexpr(P)); + if args.len > Fn.params.#len and not Fn.variadic { + fatal(P, args->last().loc, "too many args (%z, expected %z)", args.len, Fn.params.#len); + } if not lexmatch(P, #null, ',') { lexexpect(P, ')'); break; } } - ex = { tok.loc, .ty: ty.u.Fn.ret, .u: :Call { lhs, args->move(P.alloc) }}; - case else; break; + if args.len < Fn.params.#len { + err(P, lhs.loc, "too few args (%z, expected %z)", args.len, Fn.params.#len); + } + ex = { tok.loc, .ty: Fn.ret, .u: :Call { lhs, args->move(P.alloc) }}; + case else; + break; } } return ex; @@ -653,7 +679,47 @@ fn pexprefix(P *Parser) Expr { } fn pexbitarith(P *Parser) Expr { + enum Kind { None, IntFlo, Int, StrLit } + fn peeksop(P *Parser, tokp *Tok) Kind { + let tok = lexpeek(P); + if tokp { *tokp = tok; } + switch tok.t { + case '+', '-', '*', '/'; + return :IntFlo; + case '%', '&', '|', '^', '<<', '>>'; + return :Int; + case '##'; + return :StrLit; + } + return :None; + } + let ex = pexprefix(P); + let tok Tok = {}; + let okind = peeksop(P, &tok); + if okind == :None { + return ex; + } + let tokt = tok.t; + while lexmatch(P, &tok, tokt) { + let rhs = pexprefix(P); + let ty = typeof2(ex.ty, rhs.ty); + switch { + case ty == #null + and (tokt == '+' or tokt == '-') + and ((ex.ty->is(:Int) and rhs.ty->is(:Ptr)) or (rhs.ty->is(:Int) and ex.ty->is(:Ptr))); + ty = ex.ty->is(:Ptr) ? ex.ty : rhs.ty; + case ty == #null; + fatal(P, tok.loc, "invalid operands %t and %t to binary operator %k", ex.ty, rhs.ty, tokt); + case tokt == '-' and ex.ty->is(:Ptr) and rhs.ty->is(:Ptr); + ty = ty_isize; + case okind == :Int and ty->is(:Flo); + err(P, tok.loc, "invalid operands %t and %t to binary operator %k", ex.ty, rhs.ty, tokt); + case okind != :StrLit and not isnumtype(ty); + fatal(P, tok.loc, "invalid operands %t and %t to binary operator %k", ex.ty, rhs.ty, tokt); + } + ex = { tok.loc, ty, .u: :BinOp { tokt, exprdup(P.alloc, ex), exprdup(P.alloc, rhs) }}; + } return ex; } @@ -673,8 +739,47 @@ fn pexcond(P *Parser) Expr { } fn pexassign(P *Parser) Expr { + enum Kind { None, Set, IntFlo, Int } + fn matchop(P *Parser, tokp *Tok) Kind { + let tok = lexpeek(P); + if tokp { + *tokp = tok; + } + switch tok.t { + case '='; + lex(P); + return :Set; + case '+=', '-=', '*=', '/='; + lex(P); + return :IntFlo; + case '%=', '&=', '|=', '^=', '<<=', '>>='; + lex(P); + return :Int; + } + return :None; + } + + let tok Tok = {}; let ex = pexcond(P); - return ex; + let okind = matchop(P, &tok); + if okind == :None { + return ex; + } + let rhs = pexcond(P); + switch { + case not islvalue(ex); + err(P, ex.loc, "left operand to assignment is not lvalue"); + case (typeof2(ex.ty, rhs.ty) == #null) + and not ((ex.ty->is(:Ptr) and rhs.ty->is(:Int) + and (tok.t == '+=' or tok.t == '-='))); + err(P, ex.loc, + "operands %t and %t to assignment operator %qT have incompatible types", + ex.ty, rhs.ty, tok); + case ex.ty.konst; + err(P, ex.loc, "left operand to assignment is const"); + } + + return { tok.loc, ex.ty, .u: :BinOp { tok.t, exprdup(P.alloc, ex), exprdup(P.alloc, rhs) }}; } fn parseexpr(P *Parser) Expr { @@ -693,7 +798,9 @@ fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { switch { case lexmatch(P, &tok, :kw_if); case else; - return yield({ tok.loc, .u: :Expr parseexpr(P) }, yarg); + let ex = parseexpr(P); + lexexpect(P, ';'); + return yield({ tok.loc, .u: :Expr ex }, yarg); } } @@ -761,7 +868,18 @@ fn parsefn(P *Parser, decl *Decl, externp bool, name *const u8) void { } with_tmpchange(P.alloc, &Allocator { &Arena {}, &Arena:allocf }, + let env = mkenv(P.curenv, P.alloc); + P.curenv = env; + foreach(name, i, Fn.paramnames, + if name { + putdecl(P, { name, tok.loc, .u: :Let { + Fn.ty.u.Fn.params[i], :None, Fn.id, + }}); + } + ) Fn.body = :Some parseblock(P); + P.curenv = envparent(env); + envfree(env); (as(*Arena)P.alloc.a)->destroy(); ); } @@ -781,6 +899,7 @@ fn parsedecls(P *Parser, yield DeclYielder, yarg *void) void { let name = lexmatch(P, &tok, :ident) ? tok.u.ident : #null; parsefn(P, &decl, externp, name); decl.loc = tok.loc; + decl.externp = externp; case else; fatal(P, tok.loc, "expected declaration (near %qT)", tok); } |