aboutsummaryrefslogtreecommitdiff
path: root/src/parse.cff
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse.cff')
-rw-r--r--src/parse.cff127
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 = {};