diff options
Diffstat (limited to 'src/parse.cff')
| -rw-r--r-- | src/parse.cff | 127 |
1 files changed, 122 insertions, 5 deletions
diff --git a/src/parse.cff b/src/parse.cff index b67cea9..0479b0a 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -560,7 +560,7 @@ fn putdecl(P *Parser, decl Decl) *Decl { let d0 *const Decl; let d = envput(P.curenv, decl, &d0); if d == #null { - fatal(P, decl.loc, "attempt to redefine `%s'", decl.name); + fatal(P, decl.loc, "attempt to redefine `%s' (previously defined at %l)", decl.name, d0.loc); } return d; } @@ -675,6 +675,50 @@ fn pexpostfix(P *Parser) Expr { 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 lexmatch(P, &tok, '['); + let lhs = exprdup(P.alloc, ex), + rhs = exprdup(P.alloc, parseexpr(P)); + if !lhs.ty->is(:Ptr) and !lhs.ty->is(:Arr) and !lhs.ty->is(:Slice) { + fatal(P, lhs.loc, "indexee is not array or pointer type (%t)", lhs.ty); + } + if !rhs.ty->is(:Int) { + err(P, lhs.loc, "index expression type is not integral (%t)", rhs.ty); + } + + if lexmatch(P, #null, '::') { + let end *Expr #?; + if !lexmatch(P, #null, ']') { + end = exprdup(P.alloc, parseexpr(P)); + if !end.ty->is(:Int) { + err(P, lhs.loc, "slice range end expression type is not integral (%t)", end.ty); + } + lexexpect(P, ']'); + } else if lhs.ty->is(:Arr) { + end = exprdup(P.alloc, { tok.loc, ty_usize, .u: :IntLit{lhs.ty.u.Arr.length} }); + } else { + fatal(P, tok.loc, "missing end of slice range"); + } + ex = { tok.loc, mkslicetype(childtype(lhs.ty)), .u: :Slice { lhs, rhs, end }}; + } else { + lexexpect(P, ']'); + ex = { tok.loc, childtype(lhs.ty), .u: :Index { lhs, rhs }}; + } + case lexmatch(P, &tok, '++') or lexmatch(P, &tok, '--'); + if !isnumtype(ex.ty) and !ex.ty->is(:Ptr) { + fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); + } + if !islvalue(ex) { + err(P, ex.loc, "left operand to prefix %qT operator is not lvalue", tok); + } + if ex.ty.konst { + err(P, ex.loc, "left operand to prefix %qT operator is const", tok); + } + ex = { + tok.loc, ex.ty, .u: :UnOp { + tok.t == '++' ? :postinc : :postdec, exprdup(P.alloc, ex) + } + }; case else; break; } @@ -683,6 +727,65 @@ fn pexpostfix(P *Parser) Expr { } fn pexprefix(P *Parser) Expr { + let tok Tok #?; + switch { + case lexmatch(P, &tok, '-') or lexmatch(P, &tok, '~'); + let ex = pexprefix(P); + let ty = typeof2(ex.ty, ty_int); + if ty == #null { + fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); + } + if !ty->is(:Int) and tok.t == '~' { + fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); + } + return { + tok.loc, ty, .u: :UnOp { + tok.t == '-' ? :neg : :compl, exprdup(P.alloc, ex) + } + }; + case lexmatch(P, &tok, '++') or lexmatch(P, &tok, '--'); + let ex = pexprefix(P); + if !isnumtype(ex.ty) and !ex.ty->is(:Ptr) { + fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); + } + if !islvalue(ex) { + err(P, ex.loc, "left operand to prefix %qT operator is not lvalue", tok); + } + if ex.ty.konst { + err(P, ex.loc, "left operand to prefix %qT operator is const", tok); + } + return { + tok.loc, ex.ty, .u: :UnOp { + tok.t == '++' ? :preinc : :predec, exprdup(P.alloc, ex) + } + }; + case lexmatch(P, &tok, '!'); + let ex = pexprefix(P); + if !ex.ty->is(:Bool) { + err(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty); + } + return { tok.loc, ty_bool, .u: :UnOp { :not, exprdup(P.alloc, ex) }}; + + case lexmatch(P, &tok, '*'); + let ex = pexprefix(P); + if !ex.ty->is(:Ptr) { + fatal(P, ex.loc, "invalid operand to dereference, not pointer (%t)", ex.ty); + } + let child = ex.ty.u.Ptr; + if !completetype(child) and !child->is(:Fn) { + fatal(P, ex.loc, "invalid operand to dereference, incomplete (%t)", ex.ty); + } + return { tok.loc, child, .u: :UnOp { :deref, exprdup(P.alloc, ex) }}; + + case lexmatch(P, &tok, '&'); + let ex = pexprefix(P); + let ty2 = interntype({ g_targ.ptrsize, .u: :Ptr(ex.ty) }); + if !islvalue(ex) and !(ex.u.#tag == :Symbol and ex.u.Symbol.u.#tag == :Fn) + and !(ex.u.#tag == :ZeroIni or ex.u.#tag == :Ini) { + err(P, ex.loc, "invalid operand to `&': not an lvalue"); + } + return { tok.loc, ty2, .u: :UnOp { :addrof, exprdup(P.alloc, ex) }}; + } return pexpostfix(P); } @@ -754,10 +857,17 @@ fn pexcmp(P *Parser) Expr { fatal(P, tok.loc, "incompatible operands %t and %t to binary operator %qT", ex.ty, rhs.ty, tok); } - if tok.t != '==' and tok.t != '!=' and !isnumtype(typeof2(ex.ty, rhs.ty)) { - fatal(P, tok.loc, "invalid non-numeric operands %t and %t to relational operator %qT", - ex.ty, rhs.ty, tok); + let ty = typeof2(ex.ty, rhs.ty); + if tok.t != '==' and tok.t != '!=' { + if !isnumtype(ty) and !ty->is(:Ptr) { + fatal(P, tok.loc, "invalid non-numeric operands %t and %t to relational operator %qT", + ex.ty, rhs.ty, tok); + } + if ty->is(:Int) and ty != ty_int and ex.ty.u.Int.sgn != rhs.ty.u.Int.sgn { + warn(P, tok.loc, "comparing integers of different signedness (%t and %t)", ex.ty, rhs.ty); + } } + ex = { tok.loc, ty_bool, .u: :BinOp { tok.t, exprdup(P.alloc, ex), exprdup(P.alloc, rhs) @@ -879,10 +989,16 @@ fn parseexpr(P *Parser) Expr { typedef DeclYielder *fn(*Decl, *void) void; typedef StmtYielder *fn(Stmt, *void) void; +fn parseblock(P *Parser) [#]Stmt; fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { let tok Tok = {}; switch { - case lexmatch(P, &tok, :kw_if); + case lexmatch(P, &tok, '{'); + yield({ + tok.loc, .u: :Block(parseblock(P)) + }, yarg); + case lexmatch(P, &tok, :kw_let); + // pstlet(P, yield, yarg); case else; let ex = parseexpr(P); lexexpect(P, ';'); @@ -979,6 +1095,7 @@ fn doimport(P *Parser, path *const u8) [#]Decl { fn hash(s *const u8) u32 { return fnv1a_s(FNV1A_INI, s); } fn eq(a *const u8, b *const u8) bool { return streq(a, b); } }> = {}; + // imports are cached based on their realpath fn getbasedir(path *const u8) *const u8 { static res [PATH_MAX]u8 = {}; |