aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-16 09:19:33 +0200
committerlemon <lsof@mailbox.org>2022-08-16 09:19:33 +0200
commit1ca77f60626666fba792db407dd11ea9b597d9cf (patch)
tree0e5ea52e457474899ad5f0076bbe658e67626925 /src
parent04c7892134d49f3b295a51cc741affe9f02e374d (diff)
binary operators and more stuff
Diffstat (limited to 'src')
-rw-r--r--src/cffc.hff3
-rw-r--r--src/fmt.cff1
-rw-r--r--src/libc-.c3
-rw-r--r--src/libc.hff8
-rw-r--r--src/map.hff6
-rw-r--r--src/parse.cff187
-rw-r--r--src/type.cff4
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 {