diff options
| author | 2022-08-16 05:28:18 +0200 | |
|---|---|---|
| committer | 2022-08-16 05:28:18 +0200 | |
| commit | 73f68a9c5ed4c8139cc1c4f7695da29e5a3fb4c8 (patch) | |
| tree | b2cebcf1f6b6073eeeb0595710d6fdf05fcb06fb | |
| parent | b3159bfd93c8bdce71f7437abdc521b5ccb72367 (diff) | |
stuff
| -rw-r--r-- | bootstrap/parse.c | 16 | ||||
| -rw-r--r-- | examples/hello-world.cff | 5 | ||||
| -rw-r--r-- | src/cffc.hff | 40 | ||||
| -rw-r--r-- | src/common.hff | 2 | ||||
| -rw-r--r-- | src/env.cff | 11 | ||||
| -rw-r--r-- | src/fmt.cff | 20 | ||||
| -rw-r--r-- | src/libc.hff | 2 | ||||
| -rw-r--r-- | src/parse.cff | 133 | ||||
| -rw-r--r-- | src/targ.hff | 12 | ||||
| -rw-r--r-- | src/type.cff | 47 | ||||
| -rw-r--r-- | src/vec.hff | 4 |
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]; } |