diff options
| author | 2022-08-16 09:19:33 +0200 | |
|---|---|---|
| committer | 2022-08-16 09:19:33 +0200 | |
| commit | 1ca77f60626666fba792db407dd11ea9b597d9cf (patch) | |
| tree | 0e5ea52e457474899ad5f0076bbe658e67626925 /src | |
| parent | 04c7892134d49f3b295a51cc741affe9f02e374d (diff) | |
binary operators and more stuff
Diffstat (limited to 'src')
| -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 |
7 files changed, 183 insertions, 29 deletions
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 { |