diff options
| -rw-r--r-- | bootstrap/all.h | 1 | ||||
| -rwxr-xr-x | bootstrap/bootstrap.sh | 13 | ||||
| -rw-r--r-- | bootstrap/cgen.c | 7 | ||||
| -rw-r--r-- | bootstrap/parse.c | 42 | ||||
| -rw-r--r-- | examples/hello-world.cff | 4 | ||||
| -rw-r--r-- | src/cffc.hff | 3 | ||||
| -rw-r--r-- | src/fmt.cff | 1 | ||||
| -rw-r--r-- | src/libc-.c | 3 | ||||
| -rw-r--r-- | src/libc.hff | 8 | ||||
| -rw-r--r-- | src/map.hff | 6 | ||||
| -rw-r--r-- | src/parse.cff | 187 | ||||
| -rw-r--r-- | src/type.cff | 4 |
12 files changed, 241 insertions, 38 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h index 932fbe0..720c7ea 100644 --- a/bootstrap/all.h +++ b/bootstrap/all.h @@ -327,6 +327,7 @@ enum exprtype { Emcall, Eslice, Elen, + Eptr, Eeuini, Eeutag, Evastart, diff --git a/bootstrap/bootstrap.sh b/bootstrap/bootstrap.sh index c2fd41e..f221ce2 100755 --- a/bootstrap/bootstrap.sh +++ b/bootstrap/bootstrap.sh @@ -2,11 +2,16 @@ cd $(dirname "$0") -./build.sh || exit 1 - if [ x"$CC" == x ]; then export CC=cc fi + +if command -v ccache > /dev/null; then + export CC="ccache $CC" +fi + +./build.sh || exit 1 + export CFLAGS="-Wno-builtin-declaration-mismatch -Wno-discarded-qualifiers -g" set -euo pipefail @@ -16,5 +21,9 @@ for f in $(find ../src/ -name '*.cff'); do out=obj1/$(basename "$f").o (set -x ; ./cff0 "$f" | $CC $CFLAGS -xc - -c -o $out) done +for f in $(find ../src/ -name '*.c'); do + out=obj1/$(basename "$f").o + (set -x ; $CC $CFLAGS "$f" -c -o $out) +done set -x $CC $CFLAGS -ocff1 obj1/*.o diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c index 15fab93..0887ca7 100644 --- a/bootstrap/cgen.c +++ b/bootstrap/cgen.c @@ -288,7 +288,10 @@ genexpr(struct expr *ex) { ++id; break; case Elen: - pri("%e.len", ex->child); + pri("%e%slen", ex->child, ex->child->ty->t == TYptr ? "->" : "."); + break; + case Eptr: + pri("%e%sptr", ex->child, ex->child->ty->t == TYptr ? "->" : "."); break; case Eeutag: pri("%e%st", ex->child, ex->child->ty->t == TYptr ? "->" : "."); @@ -518,7 +521,7 @@ liftnestedex(struct expr *ex) { case Eblock: liftnested(blocktostmt(ex->block)); break; - case Eas: case Elen: case Eeutag: + case Eas: case Elen: case Eeutag: case Eptr: liftnestedex(ex->child); break; case Eini: diff --git a/bootstrap/parse.c b/bootstrap/parse.c index 2eda662..c76445f 100644 --- a/bootstrap/parse.c +++ b/bootstrap/parse.c @@ -395,6 +395,8 @@ lex(struct parser *P) { tok.t = '##'; } else if (!strcmp(s, "#len")) { tok.t = '#len'; + } else if (!strcmp(s, "#ptr")) { + tok.t = '#ptr'; } else if (!strcmp(s, "#when")) { tok.t = TKhwhen; } else if (!strcmp(s, "#tag")) { @@ -1197,9 +1199,14 @@ pexprimary(struct parser *P) { ex.euini.fnam = fld->name; ex.euini.tag = ex.ty->agg.enumty->enu.vals.d[i].i; P->targty = fld->ty; - if (fld->ty) + if (lexmatch(P, &tok, '(') || lexpeek(P).t == '{') { ex.euini.ini = exprdup(parseexpr(P)); - } + if (tok.t == '(') + lexexpect(P, ')'); + } + if (!!ex.euini.ini != !!fld->ty) + fatal(P, tok.span, "invalid tagged union initializer"); + } } else if (lexmatch(P, &tok, '{')) { aggini: P->used_targty = 1; @@ -1367,6 +1374,34 @@ pexpostfix(struct parser *P) { } else { fatal(P, ex.span, "invalid operand to `.#len' (%t)", ex.ty); } + } else if (lexmatch(P, &tok, '#ptr')) { + const struct type *ty = ex.ty; + const struct expr lhs = ex; + bool ptr = 0; + if (ty->t == TYptr) { + ty = ty->child; + ptr = 1; + } + if (ty->t == TYarr) { + ex.ty = interntype((struct type) { + TYptr, g_targ.ptrsize, .child = ty->child + }); + ex.span = tok.span; + if (ptr) { + ex.t = Eprefix; + ex.unop.op = '*'; + ex.unop.child = exprdup(lhs); + } + } else if (ty->t == TYslice) { + ex.ty = interntype((struct type) { + TYptr, g_targ.ptrsize, .child = ty->child + }); + ex = (struct expr) { + Eptr, tok.span, ex.ty, .child = exprdup(lhs) + }; + } else { + fatal(P, ex.span, "invalid operand to `.#ptr' (%t)", ex.ty); + } } else if (lexmatch (P, &tok, '#tag')) { const struct type *ty = ex.ty; if (ty->t == TYptr) @@ -3096,7 +3131,7 @@ parsedecl(decl_yielder_t yield, void *yarg, struct parser *P, bool toplevel) { struct attr attr = {0}; decl.container = P->container; - while (lexmatch(P, &tok, TKhwhen)) { + if (lexmatch(P, &tok, TKhwhen)) { struct expr test = parseexpr(P); if (!fold(&test) || test.ty->t != TYbool) fatal(P, test.span, "#when test is not a constant bool expression"); @@ -3119,6 +3154,7 @@ parsedecl(decl_yielder_t yield, void *yarg, struct parser *P, bool toplevel) { } } } + return; } if (lexmatch(P, &tok, '#')) { diff --git a/examples/hello-world.cff b/examples/hello-world.cff index 4eb9352..1c3c96f 100644 --- a/examples/hello-world.cff +++ b/examples/hello-world.cff @@ -2,8 +2,8 @@ extern fn printf(fmt *const u8, ...) int; extern fn cos(x f32) f32; extern fn main(argc int, argv *const *const u8) int { - 1 - (1 + argv); - argc *= 7; + #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 3ff78c4..8cdf655 100644 --- a/src/cffc.hff +++ b/src/cffc.hff @@ -90,6 +90,7 @@ struct Parser { tokloc Loc, curloc Loc, eof bool, + is_header bool, peekchr Option<int>, peektok Option<Tok>, } @@ -242,7 +243,7 @@ fn mkarrtype(len i64, konst bool, child *const Type) *const Type { fn mkptrtype(child *const Type) *const Type { return interntype({ g_targ.ptrsize, - .u: :Ptr child + .u: :Ptr(child) }); } extern fn isnumtype(ty *const Type) bool; diff --git a/src/fmt.cff b/src/fmt.cff index 53eeb56..10508b9 100644 --- a/src/fmt.cff +++ b/src/fmt.cff @@ -132,6 +132,7 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list) } fn pritype(proc typeof(proc), parg *void, ty *const Type) void { + assert(ty != #null, "pritype"); if ty.konst { ps("const "); } diff --git a/src/libc-.c b/src/libc-.c new file mode 100644 index 0000000..077b355 --- /dev/null +++ b/src/libc-.c @@ -0,0 +1,3 @@ +#include <errno.h> + +int c_errno() { return errno; } diff --git a/src/libc.hff b/src/libc.hff index 6446a33..6fb32f7 100644 --- a/src/libc.hff +++ b/src/libc.hff @@ -23,6 +23,8 @@ extern fn malloc(n usize) *void; extern fn calloc(n usize, m usize) *void; extern fn realloc(p *void, n usize) *void; extern fn free(p *void) void; +extern fn realpath(path *const u8, resolved *u8) *u8; +def PATH_MAX = 4096; // string.h extern fn strlen(s *const u8) usize; @@ -30,6 +32,10 @@ extern fn strcmp(a *const u8, b *const u8) int; extern fn strcasecmp(a *const u8, b *const u8) int; extern fn memcpy(*void, *const void, usize) *void; extern fn strcpy(*u8, *const u8) *u8; +extern fn strerror(int) *const u8; -//ctype.h +// ctype.h extern fn tolower(int) int; + +// errno.h +extern fn c_errno() int; diff --git a/src/map.hff b/src/map.hff index f97ea7e..700c553 100644 --- a/src/map.hff +++ b/src/map.hff @@ -100,8 +100,10 @@ struct Map<K, V, KTraits> { } - fn put(self *Map, key K, val V) void { - *self->get_slot(key) = val; + fn put(self *Map, key K, val V) *V { + let slot = self->get_slot(key); + *slot = val; + return slot; } fn clear(self *Map) void { diff --git a/src/parse.cff b/src/parse.cff index 87ca850..b67cea9 100644 --- a/src/parse.cff +++ b/src/parse.cff @@ -1,6 +1,7 @@ import "mem.hff"; import "util.hff"; import "vec.hff"; +import "map.hff"; import "cffc.hff"; /////////// @@ -37,7 +38,7 @@ fn chrpeek(P *Parser) int { if c == EOF { P.eof = #t; } - P.peekchr = :Some c; + P.peekchr = :Some(c); return c; } @@ -226,7 +227,7 @@ fn readnumber(s *const u8) Option<Tok> { case else; return :None; } - return :Some tok; + return :Some(tok); } else { tok.t = :int; tok.u.uint = acc; @@ -272,7 +273,7 @@ fn readnumber(s *const u8) Option<Tok> { case else; return :None; } - return :Some tok; + return :Some(tok); } } @@ -504,7 +505,7 @@ fn lexpeek(P *Parser) Tok { case Some t; return t; } let tok = lex(P); - P.peektok = :Some tok; + P.peektok = :Some(tok); return tok; } @@ -549,7 +550,10 @@ fn finddecl(P *Parser, name *const u8) *Decl { } fn putdecl(P *Parser, decl Decl) *Decl { - assert(primenv != #null, "primenv"); + if primenv == #null { + primenv = mkenv(#null, P.alloc); + putprimtypes(primenv); + } if envfind(primenv, decl.name) { fatal(P, decl.loc, "cannot shadow primitive `%s'", decl.name); } @@ -613,14 +617,14 @@ fn pexprimary(P *Parser) Expr { case :int, :chr; return { tok.loc, tok.ty, .u: :IntLit { tok.u.int }}; case :flo; - return { tok.loc, tok.ty, .u: :FloLit tok.u.flo }; + return { tok.loc, tok.ty, .u: :FloLit(tok.u.flo) }; case :bool; - return { tok.loc, ty_bool, .u: :BoolLit tok.u.bool }; + return { tok.loc, ty_bool, .u: :BoolLit(tok.u.bool) }; case :null; return { tok.loc, ty_voidptr, .u: :NullLit }; case :str; let ty = mkarrtype(tok.u.str.#len + 1, #t, ty_u8); - return { tok.loc, ty, .u: :StrLit tok.u.str }; + return { tok.loc, ty, .u: :StrLit(tok.u.str) }; case :ident; let ident = tok.u.ident; let decl = finddecl(P, ident); @@ -629,11 +633,11 @@ fn pexprimary(P *Parser) Expr { } switch decl.u { case Fn f; - return { tok.loc, f.ty, .u: :Symbol decl }; + return { tok.loc, f.ty, .u: :Symbol(decl) }; case Let var; - return { tok.loc, var.ty, .u: :Symbol decl }; + return { tok.loc, var.ty, .u: :Symbol(decl) }; case Static var; - return { tok.loc, var.ty, .u: :Symbol decl }; + return { tok.loc, var.ty, .u: :Symbol(decl) }; } case '('; let ex = parseexpr(P); @@ -709,10 +713,11 @@ fn pexbitarith(P *Parser) Expr { let rhs = pexprefix(P); let ty = typeof2(ex.ty, rhs.ty); switch { - case ty == #null - and (tokt == '+' or tokt == '-') + case ty == #null and 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 and tokt == '-' and 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); @@ -728,17 +733,94 @@ fn pexbitarith(P *Parser) Expr { } fn pexcmp(P *Parser) Expr { + fn matchcmpop(P *Parser, tokp *Tok) bool { + let tok = lexpeek(P); + if tokp { + *tokp = tok; + } + switch tok.t { + case '==', '<=', '>=', '<', '>', '!='; + lex(P); + return #t; + } + return #f; + } + let ex = pexbitarith(P); + let tok Tok = {}; + if matchcmpop(P, &tok) { + let rhs = pexbitarith(P); + if typeof2(ex.ty, rhs.ty) == #null { + 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); + } + ex = { + tok.loc, ty_bool, .u: :BinOp { + tok.t, exprdup(P.alloc, ex), exprdup(P.alloc, rhs) + } + }; + } return ex; } fn pexlog(P *Parser) Expr { let ex = pexcmp(P); + let tok Tok = lexpeek(P); + let tokt = lexpeek(P).t; + if tokt != :kw_and and tokt != :kw_or and tokt != '??' { + return ex; + } + while lexmatch(P, &tok, tokt) { + let rhs = pexcmp(P); + if tokt == '??' { + let ty = typeof2(ex.ty, rhs.ty); + if !ex.ty->is(:Ptr) or !rhs.ty->is(:Ptr) or ty == #null { + fatal(P, tok.loc, "invalid operands %t and %t to binary operator %k", + ex.ty, rhs.ty, tok.t); + } + ex = { + tok.loc, ty, .u: :BinOp { '??', exprdup(P.alloc, ex), exprdup(P.alloc, rhs) } + }; + } else { + if !ex.ty->is(:Bool) or !rhs.ty->is(:Bool) { + fatal(P, tok.loc, "invalid operands %t and %t to binary operator %k", + ex.ty, rhs.ty, tok.t); + } + ex = { + tok.loc, ty_bool, .u: :BinOp { + tokt == :kw_and ? 'and' : 'or' , exprdup(P.alloc, ex), exprdup(P.alloc, rhs) + } + }; + } + } return ex; } fn pexcond(P *Parser) Expr { + let tok Tok = {}; let ex = pexlog(P); + if (lexmatch(P, &tok, '?')) { + let ex2 = parseexpr(P); + if !ex.ty->is(:Bool) and !ex.ty->is(:Ptr) { + fatal(P, ex.loc, "invalid test operand to conditional operator (%t)", ex.ty); + } + lexexpect(P, ':'); + let ex3 = pexcond(P); + let ty = typeof2(ex2.ty, ex3.ty); + if ty == #null { + fatal(P, tok.loc, "conditional operator branches have incompatible types %t and %t", + ex2.ty, ex3.ty); + } + ex = { + tok.loc, ty, .u: :Cond { + exprdup(P.alloc, ex), exprdup(P.alloc, ex2), exprdup(P.alloc, ex3) + } + }; + } return ex; } @@ -804,7 +886,7 @@ fn parsestmts(P *Parser, yield StmtYielder, yarg *void) void { case else; let ex = parseexpr(P); lexexpect(P, ';'); - return yield({ tok.loc, .u: :Expr ex }, yarg); + return yield({ tok.loc, .u: :Expr(ex) }, yarg); } } @@ -881,14 +963,57 @@ fn parsefn(P *Parser, decl *Decl, externp bool, name *const u8) void { }}); } ) - Fn.body = :Some parseblock(P); + Fn.body = :Some(parseblock(P)); P.curenv = envparent(env); envfree(env); (as(*Arena)P.alloc.a)->destroy(); ); } -fn parsedecls(P *Parser, yield DeclYielder, yarg *void) void { +fn doimport(P *Parser, path *const u8) [#]Decl { + struct Entry { + decls [#]Decl, + wip bool, + } + static seen Map<*const u8, Entry, struct { + 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); } + }> = {}; + + fn getbasedir(path *const u8) *const u8 { + static res [PATH_MAX]u8 = {}; + let i isize #?; + for i = strlen(path); i > 0 and path[i] != '/'; --i { } + if i == 0 { + return "."; + } + memcpy(res, path, i); + res[i] = 0; + return res; + } + let basedir = getbasedir(P.curfile); + let path2 [PATH_MAX]u8 #?; + let _rpath [PATH_MAX]u8 #?; + snprintf(path2, sizeof(path2) - 1, "%s/%s", basedir, path); + let rpath *const u8 = realpath(path2, _rpath); + if rpath == #null { + fatal(P, P.tokloc, "import %qs: %s", path, strerror(c_errno())); + } + let d0 = seen->get(rpath); + if d0 != #null { + return d0.decls; + } + rpath = strcpy(malloc(strlen(rpath) + 1), rpath); + let P2 Parser #?; + parser_init(&P2, rpath); + P2.is_header = #t; + let e = seen->put(rpath, { {}, #t }); + let decls = parse(&P2); + *e = { decls, #f }; + return decls; +} + +fn parsedecls(P *Parser, yield DeclYielder, yarg *void, toplevel bool) void { let tok = Tok { .loc: P.tokloc }, decl = Decl {}, externp = #f, @@ -896,6 +1021,9 @@ fn parsedecls(P *Parser, yield DeclYielder, yarg *void) void { if lexmatch(P, &tok, :kw_extern) { externp = #t; + if (tok = lexpeek(P)).t != :kw_fn and tok.t != :kw_static { + err(P, tok.loc, "expected `fn' or `static' after `extern'"); + } } switch { @@ -904,26 +1032,39 @@ fn parsedecls(P *Parser, yield DeclYielder, yarg *void) void { parsefn(P, &decl, externp, name); decl.loc = tok.loc; decl.externp = externp; + case lexmatch(P, &tok, :kw_import); + let path = (tok = lexexpects(P, :str, "import path")).u.str.#ptr; + let decls = doimport(P, path); + foreach(decl, _, decls, + let decl = putdecl(P, decl); + if yield { + yield(decl, yarg); + } + ) + lexexpect(P, ';'); + return; case else; fatal(P, tok.loc, "expected declaration (near %qT)", tok); } - - yield(putdecl(P, decl), yarg); + + if yield { + yield(putdecl(P, decl), yarg); + } } extern fn parse(P *Parser) [#]Decl { let aralloc = Arena {}; let alloc = Allocator { &aralloc, &Arena:allocf, #null }; - let decls Vec<*Decl> = {}; + let decls Vec<Decl> = {}; P.alloc = &alloc; P.curenv = mkenv(#null, P.alloc); while !P.eof { fn yield(decl *Decl, arg *void) void { - let decls *Vec<*Decl> = arg; - decls->push(decl); + let decls *Vec<Decl> = arg; + decls->push(*decl); } - parsedecls(P, &yield, &decls); + parsedecls(P, &yield, &decls, #{toplevel} #t); if lexmatch(P, #null, :eof) { break; } @@ -931,7 +1072,7 @@ extern fn parse(P *Parser) [#]Decl { envfree(P.curenv); aralloc->destroy(); - decls->clear(); + return decls.dat[0::decls.len]; } extern fn parser_init(P *Parser, path *const u8) void { diff --git a/src/type.cff b/src/type.cff index ecd8d77..133246d 100644 --- a/src/type.cff +++ b/src/type.cff @@ -132,9 +132,9 @@ extern fn putprimtypes(env *Env) void { }; foreach(type, _, types[0::], - envput(env, { type.name, .u: :Ty (*type.gty = interntype(type.ty)) }, #null); + envput(env, { type.name, .u: :Ty(*type.gty = interntype(type.ty)) }, #null); ) - ty_voidptr = interntype({ .size: g_targ.ptrsize, .u: :Ptr ty_void }); + ty_voidptr = interntype({ .size: g_targ.ptrsize, .u: :Ptr(ty_void) }); } extern fn isnumtype(ty *const Type) bool { |