aboutsummaryrefslogtreecommitdiff
path: root/src/parse.cff
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-16 05:28:18 +0200
committerlemon <lsof@mailbox.org>2022-08-16 05:28:18 +0200
commit73f68a9c5ed4c8139cc1c4f7695da29e5a3fb4c8 (patch)
treeb2cebcf1f6b6073eeeb0595710d6fdf05fcb06fb /src/parse.cff
parentb3159bfd93c8bdce71f7437abdc521b5ccb72367 (diff)
stuff
Diffstat (limited to 'src/parse.cff')
-rw-r--r--src/parse.cff133
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);
}