diff options
40 files changed, 164 insertions, 800 deletions
@@ -50,3 +50,8 @@ Then continue just goes to the bottom of the loop and break goes out of the loop, pretty much what we're currently doing but just with the top loop in the stack. We'd probably just have to check the loop stack in lower.c, should be fairly straightforward? + ++ Function callbacks currently aren't treated as pointers, even though they +really are under the hood. Unsure if I want to do something about it, one option +could be to treat callbacks as their own thing but allow them to be casted to +pointers? diff --git a/examples/alloc.ct b/examples/alloc.ct deleted file mode 100644 index 11eed78..0000000 --- a/examples/alloc.ct +++ /dev/null @@ -1,18 +0,0 @@ -/* from c bindings */ - -pub alloc(s: usize) *void -{ - p: *u8 = malloc(s); - assert(p, "memory allocation failed\n"); - return p; -} - -pub dealloc(p *void) -{ - free(p); -} - -pub realloc(p *, s usize) *void -{ - realloc(p, s); -} diff --git a/examples/arc.ct b/examples/arc.ct deleted file mode 100644 index 8869b42..0000000 --- a/examples/arc.ct +++ /dev/null @@ -1,53 +0,0 @@ -/* could be non-null instead of raw pointer? */ -typedef atomic_rc [t any] { - count *usize; - data *t; -} - -/* macros must be 'called' with () */ -pub define arc(t) { struct (atomic_rc[t]) }; - -pub dereference(a *arc) *#a.data -{ - return a.t; -} - -pub init(mut a *arc, e #a.data) -{ - new(a.count, 0); - a.data = e; -} - -pub init(mut a *arc, b *arc<#a.data>) -{ - copy(a, b); -} - -pub deinit(mut a *arc) -{ - if atomic_fetch_add(a.count, -1) == 1 { - dealloc(a.data); - dealloc(a.count); - } - - a.count = null; - a.data = null; -} - -pub copy(mut a *arc, b *arc<#a.data>) -{ - deinit(a); - a.count = b.count; - a.data = b.data; - atomic_fetch_add(b.count, 1); -} - -pub move(mut a *arc, mut b *arc<#a.data>) -{ - deinit(a); - a.count = b.count; - a.data = b.data; - /* reference count stays the same */ - b.count = null; - a.count = null; -} diff --git a/examples/example.ct b/examples/example.ct deleted file mode 100644 index b5d752f..0000000 --- a/examples/example.ct +++ /dev/null @@ -1,150 +0,0 @@ -/* String example */ - -pub struct string { - len usize; - buf *u8; -} - -pub init(mut s *string) -{ - s.len = 0; - s.buf = null; -} - -pub deinit(mut s *string) -{ - dealloc(s.buf); -} - -pub init(mut s *string, u *u8) -{ - len usize = 0; - r any = u; - while r++ {len++;} - - s.len = len; - s.buf = alloc(s.len); - memcpy(s.buf, u); -} - -pub copy(mut r *string, s *string) *string -{ - dealloc(r.buf); - r.len = s.len; - r.buf = alloc(r.len); - memcpy(r.buf, s.buf); -} - -pub move(mut r *string, mut s *string) *string -{ - dealloc(r.buf); - r.len = s.len; - r.buf = s.buf; - s.buf = null; - s.len = 0; -} - -pub length(s *string) usize -{ - return s.len; -} - -pub index(s *string, i usize) u8 -{ - assert(i < len, "index %zu out of bounds\n", i); - return s.buf[i]; -} - -pub add(r *string, a *string, b *string) -{ - len any = length(a) + length(b); - buf any = alloc(l); - - if r.buf == a.buf { - dealloc(a.buf); - } else if r.buf == b.buf { - dealloc(b.buf); - } - - r.buf = buf; - r.len = len; -} - -/* example of hygienic and recursive macros. - In macros, the last statement of the block is taken as the "return" value, - so the following two definitions would be enough to get the smallest - value of any number of parameters, without affecting any variables in - the parent context. If recursive blocks, the last of the blocks is taken. - - In this case, the same thing could be done with functions of course. - - Internally, if one of these macros is passed as a function argument, all - statements except the last (which has to be a certain type, - have to check exactly which rules I want to apply to it) will be moved - into the nearest "regular" context, i.e. not a function call in a function - call or something like that. The last variable's context is kept as the block - in which it was defined, but accessed "anonymously". In C, something like - - // some counter to make sure we don't overlap some other variable - int min_res_12345; - { - const int _a = a; - const int _b = b; - - const int _res = _a < _b ? _a : _b; - min_res_12345 = _res; - } - call(min_res_12345); - - */ -define min(a, b) {{ - _a const = a; - _b const = b; - - _res const = if _a < _b { - _a - } else { - _b - }; - - _res; -}} - -define min(a, b ...args) {{ - _a const = a; - _b const = min(b, ...args); - - _res const = if _a < _b { - _a - } else { - _b - }; - - _res; -}} - -pub define new(x ...args) -{ - x = alloc(sizeof(x)); - init(@ ...args); -} - -pub define ctx(x ...args) -{ - init(&x ...args); - defer(deinit(@)); -} - -main(argc i32, argv [argc] * u8) -{ - something = argv[10]; // run type bounds checking for ranged array? - // will not be automatically freed - new(a *string); - new(b *string, "hello"); - c string; // undefined state - // will be automatically 'freed' - ctx(c string); - ctx(v vec<string>); - append(v, a); - append(v, c); -} diff --git a/examples/import.ct b/examples/import.ct deleted file mode 100644 index 36e7300..0000000 --- a/examples/import.ct +++ /dev/null @@ -1 +0,0 @@ -import vec.ct; diff --git a/examples/main.ct b/examples/main.ct deleted file mode 100644 index 4ebe8b7..0000000 --- a/examples/main.ct +++ /dev/null @@ -1,3 +0,0 @@ -main() i32 { - return 0; -} diff --git a/examples/numeral.ct b/examples/numeral.ct deleted file mode 100644 index 38936e9..0000000 --- a/examples/numeral.ct +++ /dev/null @@ -1,35 +0,0 @@ -/* good optimisation target: make these built in */ -type numeral { - u8; - u16; - u32; - u64; - i8; - i16; - i32; - i64; - isize; - usize; - float; - double; -} - -pub init(mut a *numeral) -{ - *a = 0; -} - -pub deinit(mut a *numeral) -{ - *a = 0; -} - -pub copy(mut a *numeral, b *numeral) -{ - *a = *b; -} - -pub move(mut a *numeral, mut b *numeral) -{ - *a = *b; -} diff --git a/examples/option.ct b/examples/option.ct deleted file mode 100644 index 483a9ee..0000000 --- a/examples/option.ct +++ /dev/null @@ -1,6 +0,0 @@ -type t {any}; - -struct option <t> { - u8 res; - t val; -}; diff --git a/examples/res.ek b/examples/res.ek index f7c96df..a1854ae 100644 --- a/examples/res.ek +++ b/examples/res.ek @@ -1,6 +1,7 @@ /* simple example with C-like strings. A real implementation would likely want * to use a string wrapper or something. */ +typedef ptr {} typedef i27 {} typedef i9 {} diff --git a/examples/std.ct b/examples/std.ct deleted file mode 100644 index 339459f..0000000 --- a/examples/std.ct +++ /dev/null @@ -1,35 +0,0 @@ -type std { - init(mut * any); - deinit(mut * any); - copy(mut * any, * any); - move(mut * any, mut * any); -} - -/* these as well could be way optimized for builtin types */ -type comparable { - equal(* any, * any) bool; - less_than(* any, * any) bool; -} - -/* I'm imagining that if someone provides a specific function for these, it will - * be used rather than these ones, but that the minimum to implement comparable - * is as small as possible this should be fine */ -pub not_equal(a * comparable, b * comparable) bool -{ - return !equal(a, b); -} - -pub greater_than(a * comparable. b * comparable) bool -{ - return !equal(a, b) && !less_than(a, b); -} - -pub less_or_equal(a * comparable, b * comparable) bool -{ - return !greater_than(a, b); -} - -pub greater_or_equal(a * comparable, b * comparable) bool -{ - return !less_than(a, b); -} diff --git a/examples/std.ek b/examples/std.ek deleted file mode 100644 index fda6d23..0000000 --- a/examples/std.ek +++ /dev/null @@ -1,259 +0,0 @@ -/* comparison traits */ -pub define cmp[] { - eq(*cmp a, *cmp b => bool); - lt(*cmp a, *cmp b => bool); - bt(*cmp a, *cmp b => bool); - - ne(*cmp a, *cmp b => bool) {return !a.eq(b)} - - le(*cmp a, *cmp b => bool) {return !a.bt(b)} - ge(*cmp a, *cmp b => bool) {return !a.lt(b)} -} - -/* hash traits */ -pub define hash[] { - hash(*hash h => i27); -} - -/* builtin type 'implementations' */ -/* as a special case, builtin types are allowed to be typedef'd to implement - * interfaces */ -pub typedef i27 {} -pub typedef i9 { - fmt![]; - fmt(*i9 i, string args => result![string]) { - /* hmm, this static string would have to be deinitialized - * somewhere else with possibly bad results. Should free() or - * whatever I choose to use check if the pointer is in static - * memory or something? Or should it be the user's - * responsibility to ensure static strings are cloned? */ - return ok!(string!("123")); - } - - cmp![]; - eq(*i9 i, *i9 o => bool) { - return i* == o*; - } - - cmp(*i9 i, *i9 o => bool) { - return i* - o*; - } - - hash![]; - hash(*i9 i => i27) { - return i; - } -} - -pub typedef bool { -} - -/* special case of special case, 'str' means *i9 but pointers aren't allowed in - * the parser stage. Is this an ugly solution? Feels kind of ugly. */ -pub typedef str { - fmt![]; - fmt(*i9 s, string args => result![string]) { - /* here we should probably copy s in case it is statically - * defined */ - return ok!(string!("cp")); - } - - cmp![]; - eq(*i9 s, *i9 o => bool) { - if s == o {return true;} - /* iterate over stuff I guess */ - } - - lt(*i9 s, *i9 o => bool) { - if s == o {return false;} - /* iterate over stuff */ - } - - hash![]; - hash(*i9 s => i27) { - /* iterate over all characters and hash them I guess */ - } -} - -/* 'continue typedef' to implement more traits and stuff, can be postponed a bit - * as I don't think it's an essential feature yet. Might be in the future, - * though, just requires some extra finangling to figure out how each thing - * should work. - * Particularly template continuations might be a bit interesting, should we - * force the user to replicate the type arguments or should it be done - * automatically? - -pub continue str { - /* implement some other traits */ -}; -*/ - -/* any import */ -pub typedef any {} - -/* string import */ -pub typedef string { - usize len; - *i9 buf; -} - -pub define string(s) { - string!{.len = sizeof(s), .buf = s} -} - -/* result import */ -pub typedef result[any T] { - *i9 err; - T val; - - err(*result r => bool) { - return r*.err != null; - } -} - -pub define ok(v) { - result!{.err = null, .val = v}; -} - -pub define err(e) { - /* for this to work properly, I'll probably need some pretty decent type - * decuction...*/ - result!{.err = e}; -} - -pub define errv(e, v) { - result!{.err = e, .val = v}; -} - -/* fmt import */ -pub define fmt[] { - fmt(*fmt p, string args => result![string]); - str(*fmt p => string) { - /* is this a loop? allowed? */ - const r = p*.fmt(string!("")); - if r*.err() { - abort("error converting to string"); - } - - return r*.v; - } -} - -/* file import */ -pub typedef file { - /* file could also just be a memory region, kind of like memstream? */ -} - -/* here would be useful if macros could take type arguments as well, for example - * f must be a file and fmt must be a string, but I guess this is a quirk I can - * live with... */ -pub define fprint(f, fmt, ...args) { - /* possible name clash, hmmm */ - i27 __ek_reserved_pos = 0; - const for __ek_reserved_a : args { - __ek_reserved_pos += f.output_fmt_string(fmt, pos); - if __ek_reserved_pos < 0 { - abort("too many print arguments"); - } - - /* pos should be at a {}, with an unknown string of arguments - * within */ - /* find matching '}' */ - i27 __ek_reserved_prev_pos = __ek_reserved_pos; - do { - if fmt.at(__ek_reserved_pos) == '}' {break;} - __ek_reserved_pos += 1; - } while 1; - - /* skip leading '{' */ - __ek_reserved_prev_pos += 1; - - /* copy arguments to a separate string (probably pretty slow, a - * string_view or something could be beneficial here)*/ - const p = fmt.dup(__ek_reserved_prev_pos, __ek_reserved_pos); - const string r = a.fmt(p); - - /* better abort messages could be useful, provide some - * "stringify" operator? #a or something? I guess we have - * src_loc that could lift the appropriate code, maybe? not - * high priority for now anyway */ - if r.err() {abort("failed to format argument");} - f.output_raw_string(r.v); - - /* skip over trailing '}' */ - __ek_reserved_pos += 1; - } - - /* if output_fmt_string() still wants to continue, we have extra - * brackets that can't be handled as we ran out of args */ - if f.output_fmt_string(fmt, pos) > 0 { - abort("too few print arguments"); - } -} - -/* vec import */ -pub typedef vec[any T] { - // we want our vector to be formattable, used by print etc. - fmt![]; - fmt(*vec v, string args => result![string]) { - return ok!(string!("test")); - } - - usize len; - *T buf; - ^(usize) alloc; - - init(*vec v, ^(usize) alloc) { - v*.len = 0; - v*.buf = null; - v*.alloc = alloc; - } - - init(*vec v) { - v*.init(alloc); - } - - length(*vec v => usize) { return v.len; } - index(*vec v, usize i => T) - { - assert(i < v*.len, "index %zu out of bounds\n", i); - return &v*.buf[i]; - } - - index(*vec v, isize i => T) - { - if i < 0 { - assert(-i < v*.len, "reverse index %zi out of bounds\n", i); - return &v*.buf[v*.len + i]; - } - - /* v.whatever() is effectively syntactic sugar for - * whatever::typeof(v)(&v), but since I don't allow typeof() - * it's built-in. */ - return v*.index(i as usize); - } - - prepend(*vec v, T e) { v*.insert(e, 0); } - append(*vec v, T e) { v*.insert(e, v*.len); } - - preplace(*vec v, T e) { v*.place(e, 0); } - applace(*vec v, T e) { v*.place(e, v*.len); } - - place(*vec v, T e) {} - insert(*vec v, T e) {} - - deinit(*vec v) - { - for i27 i = 0; i < v.len; i += 1 { - deinit(v[i]); - v[i] = null; - } - - dealloc(v.buf); - } -} - -main() { - vec![i9] what; - what.length(); -} diff --git a/examples/v.ct b/examples/v.ct deleted file mode 100644 index 2026d55..0000000 --- a/examples/v.ct +++ /dev/null @@ -1,12 +0,0 @@ -main(argc u32, argv[argc] *u8) i32 -{ - ctx(vec(u32) a); - // equivalent to - ctx(struct (vec[u32]) a); - - // All entities defined with struct [vector<u32>] are seen as the same - // structure, i.e. mangled the same. Something like vector_u32 - // If someone adds a name, i.e. structure lol [vector(u32)] the mangling - // is lol_vector_u32 and considered a different type. All identical - // definitions of structs are considered the same struct, in short. -} diff --git a/examples/vec.ct b/examples/vec.ct deleted file mode 100644 index dee50ca..0000000 --- a/examples/vec.ct +++ /dev/null @@ -1,62 +0,0 @@ -type vectorable { - std; - /* this would be needed for comparisons like find() or something */ - comparable; -} - -type vec {t: vectorable} { - std_vector: void; - len: usize; - buf: 't; -} - -pub define vec(t) { struct (vec{t}) } - -pub init(v: 'vector) -{ - v.len = 0; - v.buf = null; -} - -pub length(v: *vec => usize) { return v.len; } - -pub index(v: *vec, i: usize => '#v.buf) -{ - assert(i < v.len, "index %zu out of bounds\n", i); - return &v.buf[i]; -} - -pub index(v: mut *vec, i: const isize => '#v.buf) -{ - if i < 0 { - assert(-i < v.len, "reverse index %zi out of bounds\n", i); - return &v.buf[v.len + i]; - } - - assert(i < v.len, "index %zi out of bounds\n", i); - return &v.buf[i]; -} - -pub prepend(v: mut 'vec, e: '#v.buf) { insert(v, e, 0); } -pub append(v: mut 'vec, e: '#v.buf) { insert(v, e, v.len); } - -pub preplace(v: mut 'vec, e: '#v.buf) { place(v, e, 0); } -pub applace(v: mut 'vec, e: '#v.buf) { place(v, e, v.len - 1); } - -pub place(v: mut *vec, e: '#v.buf, i: usize) -{ -} - -pub insert(v: mut *vec, e^ '#v.buf, i: usize) -{ -} - -pub deinit(mut v *vec) -{ - for i usize : v { - deinit(v[i]); - v[i] = null; - } - - dealloc(v.buf); -} diff --git a/include/ek/ast.h b/include/ek/ast.h index 3d839ad..53bc8d6 100644 --- a/include/ek/ast.h +++ b/include/ek/ast.h @@ -158,6 +158,7 @@ enum ast_kind { AST_CONST_CHAR, AST_CONST_BOOL, AST_CONST_STR, + AST_UNPACK, }; /** Flags an AST node can have. */ @@ -275,6 +276,9 @@ size_t type_offsetof(struct type *t, char *m); #define tgen_trait(id, def, loc) \ tgen_type(TYPE_TRAIT, NULL, NULL, def, NULL, id, loc) +#define tgen_enum(id, def, loc) \ + tgen_type(TYPE_ENUM, NULL, NULL, def, NULL, id, loc) + static inline bool is_binop(struct ast *x) { switch (x->k) { @@ -625,6 +629,9 @@ static inline bool is_primitive(struct type *t) #define gen_id(id, loc) \ gen_str(AST_ID, id, loc) +#define gen_unpack(id, loc) \ + gen_str(AST_UNPACK, id, loc) + #define gen_empty(loc) \ gen1(AST_EMPTY, NULL, loc) diff --git a/src/actualize.c b/src/actualize.c index 2cf4c13..ba5475e 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -815,6 +815,14 @@ static int actualize_macro_def(struct act_state *state, /* macro bodies, arguments, etc aren't expanded upon until the macro is * called, so just try to add it to the local scope */ assert(n && n->k == AST_MACRO_DEF); + + n->t = void_type(); + + /* analysis should've added us to the file scope already */ + if (scope_flags(scope, SCOPE_FILE)) + return 0; + + /* otherwise, add us to whatever scope we're in */ return scope_add_macro(scope, n); } @@ -1443,7 +1451,8 @@ static int actualize_tconstruct(struct act_state *state, struct type *t) { assert(t->k == TYPE_CONSTRUCT); - struct ast *d = actualized_file_scope_find_type(state, scope, construct_id(t)); + struct ast *d = actualized_file_scope_find_type(state, scope, + construct_id(t)); if (!d) { type_error(scope->fctx, t, "no such type"); return -1; @@ -1602,8 +1611,7 @@ static int actualize_cast(struct act_state *state, char *left_type = type_str(expr->t); char *right_type = type_str(type); - semantic_error(scope->fctx, cast, "illegal cast: %s vs %s", - left_type, right_type); + type_mismatch(scope, "illegal cast", cast, expr->t, type); free(left_type); free(right_type); return -1; @@ -1690,7 +1698,7 @@ static int actualize_return(struct act_state *state, struct scope *scope, struct type *rtype = callable_rtype(cur_proc->t); if (!types_match(node->t, rtype)) { type_mismatch(scope, "return type mismatch", node, - rtype, node->t); + rtype, node->t); return -1; } @@ -2099,7 +2107,8 @@ static int actualize_struct(struct act_state *state, else if (same_id(id, "i9")) node->t = tgen_primitive(TYPE_I9, strdup(id), node, node->loc); else if (same_id(id, "bool")) - node->t = tgen_primitive(TYPE_BOOL, strdup(id), node, node->loc); + node->t = tgen_primitive(TYPE_BOOL, strdup(id), node, + node->loc); else if (same_id(id, "ptr")) { node->t = tgen_ptr(void_type(), node->loc); /* we know we're the definition for pointers */ @@ -2145,7 +2154,7 @@ static int actualize_struct(struct act_state *state, * table */ if (n->k == AST_VAR_DEF && node->t->k != TYPE_STRUCT) { semantic_error(scope->fctx, n, - "variables not allowed in primitive struct"); + "variables not allowed in primitive struct"); return -1; } @@ -2275,8 +2284,17 @@ static int actualize_dot(struct act_state *state, } } + if (def->k == AST_ENUM_DEF) + def = (enum_type(def))->d; + + if (!def) { + semantic_error(scope->fctx, node, + "no such type"); + return -1; + } + struct ast *exists = actualized_scope_find_symbol(state, - def->scope, + def->scope, id); if (exists) { assert(exists->t); @@ -2284,8 +2302,11 @@ static int actualize_dot(struct act_state *state, return 0; } + char *tstr = type_str(type); semantic_error(scope->fctx, node, - "does not have member"); + "%s does not have have member", + tstr); + free(tstr); return -1; } @@ -2430,6 +2451,8 @@ static int actualize_enum_fetch(struct act_state *state, struct scope *scope, return -1; } + /* this should be safe since we currently don't allow enums to be part + * of any generic structures. */ replace_slice_ast(fetch, val_val(member)); set_type(fetch, def->t); return 0; @@ -2443,7 +2466,8 @@ static int actualize_fetch(struct act_state *state, struct scope *scope, if (actualize_type(state, scope, type)) return -1; - if (type->k == TYPE_ENUM) + assert(type->d); + if (type->d->k == AST_ENUM_DEF) return actualize_enum_fetch(state, scope, fetch); if (type->k != TYPE_STRUCT && type->k != TYPE_TRAIT && @@ -2473,41 +2497,45 @@ static int actualize_enum(struct act_state *state, struct scope *scope, struct ast *node) { assert(node->k == AST_ENUM_DEF); - struct type *type = enum_type(node); struct scope *enum_scope = node->scope; /* TODO: here we could save space by choosing the smallest type that * fits */ - if (!type) { - type = i27_type(scope); - node->t = type; - } else if (actualize_type(state, enum_scope, type)) + if (!enum_type(node)) + enum_type(node) = i27_type(scope); + + /* enum are at least currently limited to top scope, so we don't have to + * worry about them being part of some generic structure. Might be a + * future improvement, though. */ + struct type *type = enum_type(node); + if (actualize_type(state, enum_scope, type)) return -1; + /* overwrite type definition to point to us. + * This is maybe something of a hack, but effectively 'pretend' to be a + * regular i27 or whatever to make casts etc. work like in C. */ + node->t = clone_type(type); + node->t->d = node; + long long counter = 0; - node->t = type; struct ast *members = enum_body(node); while (members) { set_type(members, type); - if (val_val(members)) { - struct ast *val = val_val(members); - if (actualize(state, enum_scope, val)) - return -1; + if (!val_val(members)) + val_val(members) = gen_const_int(counter, NULL_LOC()); - if (val->k != AST_CONST_INT) { - semantic_error(scope->fctx, members, - "unable to process nonconstant expression"); - return -1; - } + struct ast *val = val_val(members); + if (actualize(state, enum_scope, val)) + return -1; - counter = int_val(val); - } - else { - val_val(members) = gen_const_int(counter, NULL_LOC()); + if (val->k != AST_CONST_INT) { + semantic_error(scope->fctx, members, + "unable to process nonconstant expression"); + return -1; } + counter = int_val(val) + 1; members = members->n; - counter++; } /* TODO: check that we don't go outside the limits of the type */ @@ -2681,6 +2709,7 @@ static int actualize(struct act_state *state, struct scope *scope, } switch (node->k) { + case AST_IMPORT: ret = 0; node->t = void_type(); break; case AST_PROC_DEF: ret = actualize_proc(state, scope, node); break; case AST_TRAIT_DEF: ret = actualize_trait(state, scope, node); break; case AST_ALIAS_DEF: ret = actualize_alias(state, scope, node); break; @@ -296,6 +296,7 @@ void ast_dump(int depth, struct ast *n) DUMP(AST_CONST_CHAR); DUMP(AST_CONST_BOOL); DUMP(AST_CONST_STR); + DUMP(AST_UNPACK); } #undef DUMP diff --git a/src/debug.c b/src/debug.c index e90f1ee..32c74a2 100644 --- a/src/debug.c +++ b/src/debug.c @@ -280,10 +280,18 @@ static void _type_str(FILE *fp, struct type *type) break; } + case TYPE_ENUM: { + struct ast *def = type->d; + if (enum_id(def)) { + fprintf(fp, "%s", enum_id(def)); + } + break; + } + case TYPE_TRAIT: { struct ast *def = type->d; if (trait_id(def)) { - fprintf(fp, "%s ", trait_id(def)); + fprintf(fp, "%s", trait_id(def)); } if (struct_params(def)) { diff --git a/src/lexer.l b/src/lexer.l index b38833d..3d5a413 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -165,7 +165,7 @@ STRING \"(\\.|[^"\\])*\" {STRING} { /* seems risky, I know, but letting the parser choose when to allocate a * new string seems to help with syntax error cleanup */ - yylval->str = yytext; + yylval->str = strdup(yytext); return STRING; } diff --git a/src/lower.c b/src/lower.c index 02d28e4..a01dd40 100644 --- a/src/lower.c +++ b/src/lower.c @@ -279,11 +279,15 @@ static int lower_param(struct lower_state *s, struct ast *p, struct vec *fixups) assert(p->k == AST_VAR_DEF); assert(var_init(p) == NULL); - if (is_primitive(p->t) || p->t->k == TYPE_PTR) { + if (is_primitive(p->t) || p->t->k == TYPE_PTR || p->t->k == TYPE_CALLABLE) { return lower_simple_param(s, p); } - assert(p->t->k == TYPE_STRUCT); + if (p->t->k != TYPE_STRUCT) { + semantic_error(p->scope->fctx, p, + "illegal type"); + return -1; + } char *name = NULL; if (var_id(p)) name = mangle(p); @@ -496,13 +500,19 @@ static int lower_id(struct lower_state *s, struct ast *id, * them but if we did have them we might have to run * file_scope_find_symbol and add it to the state as we run into * them */ - struct ast *def = file_scope_find_proc(id->scope, id->s); + struct ast *def = file_scope_find_symbol(id->scope, id->s); assert(def); - add_proc(s, def); - char *o = m; - m = build_str("&%s", m); - free(o); + if (def->k == AST_PROC_DEF) { + add_proc(s, def); + + char *o = m; + /* we don't want to take the address of a variable + * (pointer to function, whatever), + * just the regular functions */ + m = build_str("&%s", m); + free(o); + } } *retval = build_retval(kind, m); @@ -960,6 +970,18 @@ static int lower_init(struct lower_state *s, struct ast *init, return 0; } +static struct ast *maybe_fetch_enum_type(struct ast *node) +{ + if (node->k == AST_ENUM_DEF) { + struct type *t = enum_type(node); + assert(t && (t->k == TYPE_I9 || t->k == TYPE_I27)); + + return file_scope_find_type(node->scope, t->id); + } + + return node; +} + static int lower_fetch(struct lower_state *s, struct ast *f, struct retval *retval) { @@ -972,6 +994,11 @@ static int lower_fetch(struct lower_state *s, struct ast *f, struct ast *def = (fetch_type(f))->d; assert(def); + /* enums are kind of sneaky as they masquerade as their underlying type + * but their definition differs */ + def = maybe_fetch_enum_type(def); + assert(def); + struct ast *proc = scope_find_proc(def->scope, fetch_id(f)); assert(proc); diff --git a/src/parser.y b/src/parser.y index 62afcac..c7a3a63 100644 --- a/src/parser.y +++ b/src/parser.y @@ -253,7 +253,7 @@ var : var_init embed - : "embed" "(" STRING ")" { $$ = gen_embed(strip($3), src_loc(@$)); } + : "embed" STRING { $$ = gen_embed(strip($2), src_loc(@$)); } import : "import" STRING { $$ = gen_import(strip($2), src_loc(@$)); } @@ -423,6 +423,7 @@ expr | "sizeof" expr { $$ = gen_sizeof($2, src_loc(@$)); } | expr "as" type { $$ = gen_cast($1, $3, src_loc(@$)); } | ID "::" type { $$ = gen_fetch($1, $3, src_loc(@$)); } + | "..." ID { $$ = gen_unpack($2, src_loc(@$)); } | macro_expand | construct | assign @@ -477,7 +478,6 @@ statement | for | if | const - | enum | macro | ID ":" { $$ = gen_label($[ID], NULL, src_loc(@$)); } diff --git a/tests/arr.ek b/tests/arr.ek deleted file mode 100644 index c206615..0000000 --- a/tests/arr.ek +++ /dev/null @@ -1,5 +0,0 @@ -main() -{ - [20][20]*type![u32, u32] a = [20, => 20 ... 200 = 240, [200, 200]]; - a[20] = 200; -} diff --git a/tests/blocks.ek b/tests/blocks.ek deleted file mode 100644 index 3e598f5..0000000 --- a/tests/blocks.ek +++ /dev/null @@ -1,10 +0,0 @@ -main() -{ - mut a = switch 1 { - case 2: 2; - case 1: 1; - default: 20; 20; - }; - - mut c = {2 + 2; 4 + 4}; -} diff --git a/tests/callbacks.ek b/tests/callbacks.ek index 6e753e0..927c122 100644 --- a/tests/callbacks.ek +++ b/tests/callbacks.ek @@ -1,17 +1,16 @@ -do_stuff(^(u32) proc) -{ - proc(); -} +typedef i27 {} -other_proc(u32) +do_stuff(^(i27 => i27) proc => i27) { + return proc(20); } -other_proc() +other_proc(i27 a => i27) { + return a + 10; } main() { - do_stuff(other_proc as ^(u32)); + do_stuff(other_proc); } diff --git a/tests/d.ek b/tests/d.ek deleted file mode 100644 index 1ee313d..0000000 --- a/tests/d.ek +++ /dev/null @@ -1,35 +0,0 @@ -struct vec[any T] -{ - u27 size; - u27 cap; - *T buf; -} - -append(*vec v, T::typeof(v) e) -{ - if v.size == v.cap { - v.cap *= 3; - v.buf = realloc(v.buf, v.cap); - } - u27 a = { - 2 - }; - - { - here(); - } - - u27 a = { - .what = 200, - .wow = 200 - }; - - u27 a = switch (b) { - default: 20; - }; - - label: while a { - } - - v.buf[v.size] = e; -} diff --git a/tests/enums.ek b/tests/enums.ek index 234bbd1..ef7850c 100644 --- a/tests/enums.ek +++ b/tests/enums.ek @@ -1,9 +1,17 @@ +typedef i27 { + add(i27 a, i27 b => i27) { + return a + b; + } +}; + +typedef i9 {}; + enum A { a = 200, b } -enum B: u8 { +enum B: i9 { b, c } @@ -11,4 +19,5 @@ main() { const a = a::A; const b = b::B; + const c = a.add(b as i27); } diff --git a/tests/if.ek b/tests/if.ek index cd4341d..e3e08d7 100644 --- a/tests/if.ek +++ b/tests/if.ek @@ -1,5 +1,6 @@ typedef i9 {} typedef i27 {} +typedef ptr {} putchar(i9 c) { diff --git a/tests/if2.ek b/tests/if2.ek index c990ca5..0932155 100644 --- a/tests/if2.ek +++ b/tests/if2.ek @@ -1,6 +1,7 @@ typedef i9 {} typedef i27 {} typedef bool {} +typedef ptr {} putchar(i9 c) { diff --git a/tests/loop.ek b/tests/loop.ek index 265d09f..6ecf439 100644 --- a/tests/loop.ek +++ b/tests/loop.ek @@ -1,6 +1,7 @@ typedef i9 {} typedef i27 {} typedef bool {} +typedef ptr {} putchar(i9 c) { diff --git a/tests/pointer_literal.ek b/tests/pointer_literal.ek index dbb2bef..ade7988 100644 --- a/tests/pointer_literal.ek +++ b/tests/pointer_literal.ek @@ -1,5 +1,6 @@ typedef i9 {} typedef i27 {} +typedef ptr {} main() { diff --git a/tests/refderef.ek b/tests/refderef.ek index 1420d2c..ff020e7 100644 --- a/tests/refderef.ek +++ b/tests/refderef.ek @@ -1,5 +1,6 @@ typedef i9 {} typedef i27 {} +typedef ptr {} main() { diff --git a/tests/resolve.ek b/tests/resolve.ek deleted file mode 100644 index f417fde..0000000 --- a/tests/resolve.ek +++ /dev/null @@ -1,35 +0,0 @@ -typedef A {} - -struct generic (T1 A, T2 A) { - T1 a; - T2 b; -} - -struct other_generic(T1 A) { - T1 a; - T2 b; -} - -/* wow this works pretty good */ -//some_func(a A, b typeof a, c i64){1;} -//some_func(a A, b typeof a, c typeof b){2;} -//some_func(a A, b typeof a, c A){3;} -//some_func(a generic(u32, u32)) {5;} -//some_func(a other_generic){6;} -// TODO: partial templates shouldn't be allowed -//some_func(a generic(u32)){6;} -//some_func(a generic) {4;} -// illegal -// TODO: traits shouldn't be allowed in template instantiation -//some_func(a generic(A, A)){6;} -//some_func(generic(generic, generic)){1;} -//some_func(generic(generic, i64)){2;} -//some_func(generic(i64, generic)){3;} -//some_func(generic(i64, i64)){4;} -some_func(generic){5;} - -main(){ - // TODO: not fully qualified types in bodies should cause an error - [20]generic![i64, generic] a; - some_func(a); -} diff --git a/tests/rvalue.ek b/tests/rvalue.ek index c55f14b..4864fae 100644 --- a/tests/rvalue.ek +++ b/tests/rvalue.ek @@ -1,3 +1,5 @@ +typedef i27 {} + main() { /* should parse, but give an error about lvalue vs rvalue */ diff --git a/tests/struct_call_ref.ek b/tests/struct_call_ref.ek index a4d38bc..e1ec9d9 100644 --- a/tests/struct_call_ref.ek +++ b/tests/struct_call_ref.ek @@ -1,3 +1,4 @@ +typedef ptr {} typedef i27 {} typedef i9 {} diff --git a/tests/structs.ek b/tests/structs.ek index 3e64b94..13399b3 100644 --- a/tests/structs.ek +++ b/tests/structs.ek @@ -1,3 +1,4 @@ +typedef ptr {} typedef i27 {} typedef i9 {} diff --git a/tests/trait_expand.ek b/tests/trait_expand.ek index 8ee413e..c5f0f1c 100644 --- a/tests/trait_expand.ek +++ b/tests/trait_expand.ek @@ -1,3 +1,5 @@ +typedef ptr {} + define b[] { a![]; } diff --git a/tests/trait_multiple_expand.ek b/tests/trait_multiple_expand.ek index 285a6ea..04e2b82 100644 --- a/tests/trait_multiple_expand.ek +++ b/tests/trait_multiple_expand.ek @@ -1,3 +1,5 @@ +typedef ptr {} + define b[] { c![]; a![]; @@ -16,3 +18,9 @@ typedef d { b![]; c(*d d) {} } + +main() +{ + d d = d!{}; + d.c(); +} diff --git a/tests/type_expand.ek b/tests/type_expand.ek deleted file mode 100644 index e58d0a0..0000000 --- a/tests/type_expand.ek +++ /dev/null @@ -1,11 +0,0 @@ -define trait[] { - trait_func(*trait a, *trait b); -} - -typedef a[] { - trait![]; -} - -main() { - a![] some_var; -} diff --git a/tests/ufcs.ek b/tests/ufcs.ek index 85d241b..efa7701 100644 --- a/tests/ufcs.ek +++ b/tests/ufcs.ek @@ -1,3 +1,4 @@ +typedef ptr {} typedef i27 { add(*i27 self, i27 other => i27) { diff --git a/tests/unions.ek b/tests/unions.ek deleted file mode 100644 index b493d5a..0000000 --- a/tests/unions.ek +++ /dev/null @@ -1,21 +0,0 @@ -typedef any {} - -union basic_union { - u32 a; - i64 b; - f32 c; -} - -union complex_union(any A, any B, any C) { - A a; - B b; - C c; -} - -main() -{ - const simple_named = {.b = 1} as basic_union; - - // TODO: unions should be fully actualized - const complex_named = {.b = 1} as complex_union![u32, i64, f32]; -} diff --git a/tests/variadic.ek b/tests/variadic.ek index ae3c2c2..0e9120c 100644 --- a/tests/variadic.ek +++ b/tests/variadic.ek @@ -1,7 +1,16 @@ +typedef i27 {} + define macro(a, b, ... c) { + mut sum = 0; const for i : a, b, ... c { + sum += i; } + + sum; } -proc(u32 a, u32 b, ... c) {} +main() +{ + macro!(1, 2, 3, 4); +} |
