diff options
| author | 2022-08-17 05:18:22 +0200 | |
|---|---|---|
| committer | 2022-08-17 05:18:22 +0200 | |
| commit | c50a02ec703c7c1c5f6823c8cbd07a424d604792 (patch) | |
| tree | ef80066da26b0b91c73fba6c6e3234ddc78d869c | |
| parent | 1ca77f60626666fba792db407dd11ea9b597d9cf (diff) | |
more exprs, warnings
| -rw-r--r-- | bootstrap/cgen.c | 2 | ||||
| -rw-r--r-- | examples/hello-world.cff | 2 | ||||
| -rw-r--r-- | src/cffc.hff | 22 | ||||
| -rw-r--r-- | src/fmt.cff | 17 | ||||
| -rw-r--r-- | src/main.cff | 3 | ||||
| -rw-r--r-- | src/parse.cff | 127 | ||||
| -rw-r--r-- | src/type.cff | 12 |
7 files changed, 172 insertions, 13 deletions
diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c index 0887ca7..d7b91b0 100644 --- a/bootstrap/cgen.c +++ b/bootstrap/cgen.c @@ -758,7 +758,9 @@ defctype(const struct type *ty, void *_) { return; } defctype(ty->child, NULL); + if (ty->_cname) break; *cname = xasprintf("__ty%d", id++); + pri("typedef struct { %t *ptr; size_t len; } %s;\n", ty->child, *cname); pri("_Static_assert(sizeof(%s) == %U, \"sizeof(%t) == %U\");\n", diff --git a/examples/hello-world.cff b/examples/hello-world.cff index 1c3c96f..a976450 100644 --- a/examples/hello-world.cff +++ b/examples/hello-world.cff @@ -2,8 +2,6 @@ extern fn printf(fmt *const u8, ...) int; extern fn cos(x f32) f32; extern fn main(argc int, argv *const *const u8) int { - #t ? 2 : 3; - 1 == 2 and argv != argv and #f; printf("hello world\n"); } diff --git a/src/cffc.hff b/src/cffc.hff index 8cdf655..ff9d3eb 100644 --- a/src/cffc.hff +++ b/src/cffc.hff @@ -120,7 +120,11 @@ struct Expr { BinOp struct { op TokT, lhs *Expr, rhs *Expr }, UnOp struct { op UnOp, ex *Expr }, Cond struct { test *Expr, t *Expr, f *Expr }, - Call struct { lhs *Expr, args [#]Expr } + Index struct { lhs *Expr, rhs *Expr }, + Slice struct { lhs *Expr, begin *Expr, end *Expr }, + Call struct { lhs *Expr, args [#]Expr }, + ZeroIni, + Ini struct { }, } } @@ -188,6 +192,7 @@ extern fn pfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ...) void; extern fn vefmt(fmt *const u8, ap va_list) void; extern fn efmt(fmt *const u8, ...) void; extern fn ssfmt(fmt *const u8, ...) *const u8; +extern fn warn(P *Parser, Loc, fmt *const u8, ...) void; extern fn err(P *Parser, Loc, fmt *const u8, ...) void; extern fn fatal(*Parser, Loc, fmt *const u8, ...) void; @@ -246,6 +251,21 @@ fn mkptrtype(child *const Type) *const Type { .u: :Ptr(child) }); } +fn mkslicetype(child *const Type) *const Type { + return interntype({ + g_targ.ptrsize, + .u: :Ptr(child) + }); +} +fn childtype(ty *const Type) *const Type { + switch ty.u { + case Ptr t; return t; + case Arr a; return a.child; + case Slice t; return t; + } + return #null; +} +extern fn completetype(ty *const Type) bool; extern fn isnumtype(ty *const Type) bool; extern fn typeof2(a *const Type, b *const Type) *const Type; diff --git a/src/fmt.cff b/src/fmt.cff index 10508b9..37b6dc9 100644 --- a/src/fmt.cff +++ b/src/fmt.cff @@ -242,6 +242,9 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list) ps("\e[1m"); pritype(proc, parg, ap->arg(*const Type)); ps("\e[0m"); + case 'l'; + let loc = ap->arg(Loc); + pfmt(proc, parg, "\e[1m%s:%d:%d\e[0m", fileid2path(loc.fileid), loc.line, loc.col); case else assert(#f, "bad fmt '%c'", c); } @@ -309,24 +312,30 @@ fn eprifileline(loc Loc) void { } extern fn vdiag(P *Parser, loc Loc, kind *const u8, fmt *const u8, ap va_list) void { - efmt("%s:%i:%i: %s: ", fileid2path(loc.fileid), loc.line, loc.col, kind); + efmt("\e[1m%s:%i:%i:\e[0m %s: ", fileid2path(loc.fileid), loc.line, loc.col, kind); vefmt(fmt, ap); efmt("\n"); eprifileline(loc); } +extern fn warn(P *Parser, loc Loc, fmt *const u8, ...) void { + let ap va_list #?; + ap->start(fmt); + vdiag(P, loc, "\e[33mwarning\e[0m", fmt, ap); + ap->end(); +} + extern fn err(P *Parser, loc Loc, fmt *const u8, ...) void { let ap va_list #?; ap->start(fmt); - vdiag(P, loc, "error", fmt, ap); + vdiag(P, loc, "\e[31merror\e[0m", fmt, ap); ap->end(); - exit(1); } extern fn fatal(P *Parser, loc Loc, fmt *const u8, ...) void { let ap va_list #?; ap->start(fmt); - vdiag(P, loc, "error", fmt, ap); + vdiag(P, loc, "\e[31merror\e[0m", fmt, ap); ap->end(); efmt("Aborting due to previous error.\n"); exit(1); diff --git a/src/main.cff b/src/main.cff index 7cc90a0..5246c60 100644 --- a/src/main.cff +++ b/src/main.cff @@ -9,6 +9,7 @@ extern fn main(argc int, argv **u8) int { let p = Parser {}; parser_init(&p, argv[1]); - parse(&p); + let decls = parse(&p); + free(decls.#ptr); } 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 = {}; diff --git a/src/type.cff b/src/type.cff index 133246d..a4a4e3d 100644 --- a/src/type.cff +++ b/src/type.cff @@ -89,6 +89,18 @@ extern fn interntype(ty0 Type) *const Type { return *set->intern(&ty0); } +extern fn completetype(ty *const Type) bool { + if ty.konst { + return completetype(unconstify(ty)); + } + switch ty.u { + case Void; return #f; + case Fn; return #f; + case Arr a; return a.length >= 0; + } + return #t; +} + extern static ty_void *const Type = {}, ty_bool *const Type = {}, ty_i8 *const Type = {}, |