aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-17 05:18:22 +0200
committerlemon <lsof@mailbox.org>2022-08-17 05:18:22 +0200
commitc50a02ec703c7c1c5f6823c8cbd07a424d604792 (patch)
treeef80066da26b0b91c73fba6c6e3234ddc78d869c
parent1ca77f60626666fba792db407dd11ea9b597d9cf (diff)
more exprs, warnings
-rw-r--r--bootstrap/cgen.c2
-rw-r--r--examples/hello-world.cff2
-rw-r--r--src/cffc.hff22
-rw-r--r--src/fmt.cff17
-rw-r--r--src/main.cff3
-rw-r--r--src/parse.cff127
-rw-r--r--src/type.cff12
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 = {},