aboutsummaryrefslogtreecommitdiff
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
parentb3159bfd93c8bdce71f7437abdc521b5ccb72367 (diff)
stuff
-rw-r--r--bootstrap/parse.c16
-rw-r--r--examples/hello-world.cff5
-rw-r--r--src/cffc.hff40
-rw-r--r--src/common.hff2
-rw-r--r--src/env.cff11
-rw-r--r--src/fmt.cff20
-rw-r--r--src/libc.hff2
-rw-r--r--src/parse.cff133
-rw-r--r--src/targ.hff12
-rw-r--r--src/type.cff47
-rw-r--r--src/vec.hff4
11 files changed, 256 insertions, 36 deletions
diff --git a/bootstrap/parse.c b/bootstrap/parse.c
index aeb8124..b113618 100644
--- a/bootstrap/parse.c
+++ b/bootstrap/parse.c
@@ -273,6 +273,7 @@ readstrlit(struct parser *P, u8 delim, char *s, size_t n) {
case 't': s[i++] = '\t'; break;
case 'v': s[i++] = '\v'; break;
case 'f': s[i++] = '\f'; break;
+ case 'e': s[i++] = '\x1b'; break;
case '0': s[i++] = '\0'; break;
default:
fatal(P, P->tokspan, "unknown escape sequence '\\%c'", c);
@@ -952,11 +953,11 @@ parsestructini(struct parser *P, const struct type *ty) {
}
fld = structidx2fld(ty, idx++);
- P->targty = fld->ty;
- e = parseexpr(P);
if (!fld)
- fatal(P, e.span,
+ fatal(P, P->tokspan,
"excess elements in struct initializer");
+ P->targty = fld->ty;
+ e = parseexpr(P);
if (!typematchestarg(fld->ty, e.ty))
fatal(P, e.span,
"incompatible element `%s` type in %s initializer (%t, expected %t)",
@@ -1157,6 +1158,7 @@ pexprimary(struct parser *P) {
}
} else {
ex = parseexpr(P);
+ P->used_targty = 0;
}
lexexpect(P, ')');
} else if (lexmatch(P, &tok, ':')) {
@@ -1404,7 +1406,7 @@ pexpostfix(struct parser *P) {
if ((exptr = ty->t == TYptr))
ty = ty->child;
if (ty->t == TYstruct || ty->t == TYunion) {
- struct decl *decl = findaggdecl(ty, fnam);
+ struct decl *decl = findaggdecl(unconstify(ty), fnam);
if (!decl)
fatal(P, tok.span, "%t has no such method `%s'", ty, fnam);
if (decl->t != Dfn)
@@ -1771,7 +1773,7 @@ pexcond(struct parser *P) {
return ex;
}
-static bool
+static int
matchassignop(struct parser *P, struct tok *tokp) {
struct tok tok = lexpeek(P);
if (tokp)
@@ -1780,10 +1782,10 @@ matchassignop(struct parser *P, struct tok *tokp) {
case '=':
lex(P);
return 1;
- case '+=': case '-=': case '*=': case '/=': case '%=':
+ case '+=': case '-=': case '*=': case '/=':
lex(P);
return 2;
- case '&=': case '|=': case '^=': case '<<=': case '>>=':
+ case '%=': case '&=': case '|=': case '^=': case '<<=': case '>>=':
lex(P);
return 3;
}
diff --git a/examples/hello-world.cff b/examples/hello-world.cff
index 9764ac2..4eb9352 100644
--- a/examples/hello-world.cff
+++ b/examples/hello-world.cff
@@ -1,6 +1,9 @@
extern fn printf(fmt *const u8, ...) int;
+extern fn cos(x f32) f32;
-extern fn main(argc int, argc *const *const u8) int {
+extern fn main(argc int, argv *const *const u8) int {
+ 1 - (1 + argv);
+ argc *= 7;
printf("hello world\n");
}
diff --git a/src/cffc.hff b/src/cffc.hff
index 2b6faaf..3824a14 100644
--- a/src/cffc.hff
+++ b/src/cffc.hff
@@ -75,6 +75,10 @@ struct Type {
variadic bool,
ret *const Type,
},
+ },
+
+ fn is(ty *const Type, tag typeof((Type{}).u.#tag)) bool {
+ return ty.u.#tag == tag;
}
}
@@ -90,6 +94,18 @@ struct Parser {
peektok Option<Tok>,
}
+enum UnOp {
+ neg,
+ lnot,
+ compl,
+ deref,
+ addrof,
+ postinc,
+ preinc,
+ postdec,
+ predec,
+}
+
struct Expr {
loc Loc,
ty *const Type,
@@ -100,6 +116,9 @@ struct Expr {
BoolLit bool,
NullLit,
Symbol *Decl,
+ 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 }
}
}
@@ -120,10 +139,19 @@ struct Fn {
body Option<[#]Stmt>
}
+struct Var {
+ ty *const Type,
+ ini Option<Expr>,
+ fnid int,
+}
+
struct Decl {
name *const u8,
loc Loc,
+ externp bool,
u enum union {
+ Let Var,
+ Static Var,
Fn Fn,
Ty *const Type,
},
@@ -159,6 +187,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 err(P *Parser, Loc, fmt *const u8, ...) void;
extern fn fatal(*Parser, Loc, fmt *const u8, ...) void;
// targ.cff
@@ -195,6 +224,14 @@ fn constify(ty *const Type) *const Type {
ty2.konst = #t;
return interntype(ty2);
}
+fn unconstify(ty *const Type) *const Type {
+ if not ty.konst {
+ return ty;
+ }
+ let ty2 = *ty;
+ ty2.konst = #f;
+ return interntype(ty2);
+}
fn mkarrtype(len i64, konst bool, child *const Type) *const Type {
return interntype({
len * child.size,
@@ -208,9 +245,12 @@ fn mkptrtype(child *const Type) *const Type {
.u: :Ptr child
});
}
+extern fn isnumtype(ty *const Type) bool;
+extern fn typeof2(a *const Type, b *const Type) *const Type;
// env.cff
extern fn mkenv(parent *Env, alloc *Allocator) *Env;
+extern fn envparent(*Env) *Env;
extern fn envput(*Env, Decl, **const Decl) *Decl;
extern fn envfind(*Env, *const u8) *Decl;
extern fn envfree(*Env) void;
diff --git a/src/common.hff b/src/common.hff
index fe90e09..02859f8 100644
--- a/src/common.hff
+++ b/src/common.hff
@@ -40,6 +40,8 @@ defmacro with_tmpchange(var,x,...body) [
(var) = $tmp; }
]
+defmacro MAX(a,b) [((a) > (b) ? (a) : (b))]
+
// Inline functions
fn bswap32(x u32) u32 {
return (x >> 24)
diff --git a/src/env.cff b/src/env.cff
index 60b485b..001e92c 100644
--- a/src/env.cff
+++ b/src/env.cff
@@ -20,6 +20,10 @@ extern fn mkenv(parent *Env, alloc *Allocator) *Env {
return env;
}
+extern fn envparent(env *Env) *Env {
+ return env.parent;
+}
+
extern fn envput(env *Env, decl Decl, old **const Decl) *Decl {
let l **DeclList = env.decls->get_slot(decl.name);
let n *DeclList = anew(env.alloc, DeclList);
@@ -35,7 +39,12 @@ extern fn envput(env *Env, decl Decl, old **const Decl) *Decl {
extern fn envfind(env *Env, name *const u8) *Decl {
let l **DeclList = env.decls->get(name);
- if l == #null { return #null; }
+ if l == #null {
+ if env.parent == #null {
+ return #null;
+ }
+ return envfind(env.parent, name);
+ }
let l = *l;
assert(l != #null, "l?");
return &l.decl;
diff --git a/src/fmt.cff b/src/fmt.cff
index 90b6423..53eeb56 100644
--- a/src/fmt.cff
+++ b/src/fmt.cff
@@ -155,7 +155,7 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
case Fn f;
ps("fn (");
foreach(ty, i, f.params,
- pritype(proc, parg, ty);
+ pfmt(proc, parg, "%t", ty);
if f.variadic or i < f.params.#len - 1 {
ps(", ");
}
@@ -238,7 +238,9 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
let tokt = ap->arg(TokT);
pritokt(proc, parg, tokt);
case 't';
+ ps("\e[1m");
pritype(proc, parg, ap->arg(*const Type));
+ ps("\e[0m");
case else
assert(#f, "bad fmt '%c'", c);
}
@@ -293,13 +295,13 @@ fn eprifileline(loc Loc) void {
}
}
fseek(fp, line_begin, SEEK_SET);
- fprintf(stderr, "%4d | ", loc.line);
- for let c u8 = fgetc(fp); c != EOF and c != '\n'; c = fgetc(fp) {
+ let ln = fprintf(stderr, "%4d | ", loc.line);
+ for let c = fgetc(fp); c != EOF and c != '\n'; c = fgetc(fp) {
fputc(c, stderr);
}
fputc('\n', stderr);
fclose(fp);
- for let i = 0; i < loc.col; ++i {
+ for let i = 0; i < ln + (loc.col - 1); ++i {
fputc(' ', stderr);
}
fprintf(stderr, "^\n");
@@ -312,7 +314,7 @@ extern fn vdiag(P *Parser, loc Loc, kind *const u8, fmt *const u8, ap va_list) v
eprifileline(loc);
}
-extern fn fatal(P *Parser, loc Loc, fmt *const u8, ...) void {
+extern fn err(P *Parser, loc Loc, fmt *const u8, ...) void {
let ap va_list #?;
ap->start(fmt);
vdiag(P, loc, "error", fmt, ap);
@@ -320,3 +322,11 @@ extern fn fatal(P *Parser, loc Loc, fmt *const u8, ...) void {
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);
+ ap->end();
+ efmt("Aborting due to previous error.\n");
+ exit(1);
+}
diff --git a/src/libc.hff b/src/libc.hff
index a269f42..6446a33 100644
--- a/src/libc.hff
+++ b/src/libc.hff
@@ -11,6 +11,8 @@ extern fn fopen(path *const u8, mode *const u8) *FILE;
extern fn fclose(*FILE) int;
extern fn fgetc(*FILE) int;
extern fn fputc(int, *FILE) int;
+def SEEK_SET = 0;
+extern fn fseek(*FILE, off c_long, whence int) int;
def EOF = -1;
// stdlib.h
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);
}
diff --git a/src/targ.hff b/src/targ.hff
deleted file mode 100644
index 9df8ee5..0000000
--- a/src/targ.hff
+++ /dev/null
@@ -1,12 +0,0 @@
-struct Targ {
- name *const u8,
- ptrsize u8,
- intsize u8,
- longsize u8, longalign u8,
- llongsize u8, llongalign u8,
- sizesize u8,
- f64align u8,
- valistsize u8, valistalign u8,
- charsigned bool,
- shortenum bool,
-}
diff --git a/src/type.cff b/src/type.cff
index 33d78e6..97fc44a 100644
--- a/src/type.cff
+++ b/src/type.cff
@@ -136,3 +136,50 @@ extern fn putprimtypes(env *Env) void {
)
ty_voidptr = interntype({ .size: g_targ.ptrsize, .u: :Ptr ty_void });
}
+
+extern fn isnumtype(ty *const Type) bool {
+ return ty->is(:Int) or ty->is(:Flo);
+}
+
+fn numtype2rank(ty *const Type) int {
+ ty = unconstify(ty);
+ switch {
+ case ty->is(:Int) and (ty == ty_int or ty.size < ty_int.size);
+ return 0;
+ case ty == ty_uint; return 1;
+ case ty == ty_i32; return 2;
+ case ty == ty_u32; return 3;
+ case ty == ty_i64; return 4;
+ case ty == ty_u64; return 5;
+ case ty == ty_f32; return 6;
+ case ty == ty_f64; return 7;
+ }
+ return -1;
+}
+
+fn rank2numtype(r int) *const Type {
+ static const types []**const Type = {
+ &ty_int, &ty_uint, &ty_i32, &ty_u32,
+ &ty_i64, &ty_u64, &ty_f32, &ty_f64,
+ };
+ assert(r >= 0 and r < types.#len, "rank");
+ return *types[r];
+}
+
+extern fn typeof2(a *const Type, b *const Type) *const Type {
+ if a == b and not a->is(:Int) {
+ return a;
+ }
+ if isnumtype(a) and isnumtype(b) {
+ let ra = numtype2rank(a),
+ rb = numtype2rank(b);
+ return rank2numtype(MAX(ra, rb));
+ }
+ if unconstify(a) == b {
+ return a;
+ }
+ if a == unconstify(b) {
+ return b;
+ }
+ return #null;
+}
diff --git a/src/vec.hff b/src/vec.hff
index 7f012aa..9933038 100644
--- a/src/vec.hff
+++ b/src/vec.hff
@@ -12,10 +12,8 @@ struct Vec<T> {
vec.cap = (vec.len + 1) > 8 ? (vec.len + 1) * 2 : 8;
if vec.dat == #null {
vec.dat = xmalloc(vec.cap * sizeof T);
- assert(vec.dat != #null, "malloc");
} else {
vec.dat = xrealloc(vec.dat, vec.cap * sizeof T);
- assert(vec.dat != #null, "realloc");
}
}
vec.dat[vec.len++] = x;
@@ -29,7 +27,7 @@ struct Vec<T> {
fn compact(vec *Vec) [#]T {
if vec.dat {
vec.cap = vec.len;
- vec.dat = realloc(vec.dat, vec.cap * sizeof T);
+ vec.dat = xrealloc(vec.dat, vec.cap * sizeof T);
}
return vec.dat[0::vec.len];
}