diff options
author | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-05-07 21:22:38 +0300 |
---|---|---|
committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-05-07 21:22:38 +0300 |
commit | 0e0c41af58a0f4ec5a39ce77822de71e5523fcba (patch) | |
tree | 4a02b2f93e61411cbfc6084b0855dba51b326cd9 | |
parent | 1fadcec6d7b26d34edf3b5b3a293deea0edb4139 (diff) | |
download | fwd-gnc.tar.gz fwd-gnc.zip |
implement enough type analysis for vector examplegnc
+ Big commit, scary
+ Some details still a bit up in the air, mainly about move checking
structure member access ('register' types are freely copied I guess,
same as in rust? How about user types?)
-rw-r--r-- | examples/fib.fwd | 4 | ||||
-rw-r--r-- | examples/vec.fwd | 51 | ||||
-rw-r--r-- | include/fwd/ast.h | 163 | ||||
-rw-r--r-- | include/fwd/debug.h | 5 | ||||
-rw-r--r-- | include/fwd/mod.h | 4 | ||||
-rw-r--r-- | include/fwd/rewrite.h | 9 | ||||
-rw-r--r-- | include/fwd/scope.h | 7 | ||||
-rw-r--r-- | mod/Makefile | 8 | ||||
-rw-r--r-- | mod/io.c | 8 | ||||
-rw-r--r-- | mod/mem.c | 38 | ||||
-rw-r--r-- | mod/util.c | 20 | ||||
-rw-r--r-- | src/analyze.c | 524 | ||||
-rw-r--r-- | src/ast.c | 51 | ||||
-rw-r--r-- | src/debug.c | 39 | ||||
-rw-r--r-- | src/lexer.l | 19 | ||||
-rw-r--r-- | src/parser.y | 185 | ||||
-rw-r--r-- | src/rewrite.c | 96 | ||||
-rw-r--r-- | src/scope.c | 87 |
18 files changed, 1127 insertions, 191 deletions
diff --git a/examples/fib.fwd b/examples/fib.fwd index e6d6ee6..4583a26 100644 --- a/examples/fib.fwd +++ b/examples/fib.fwd @@ -25,6 +25,6 @@ fib(i64 n, (i64) res) main() { fib(42) => i64 n; - print_i64(n); - print_nl(); + fwdprint_i64(n); + fwdprint_nl(); } diff --git a/examples/vec.fwd b/examples/vec.fwd new file mode 100644 index 0000000..1cc10b3 --- /dev/null +++ b/examples/vec.fwd @@ -0,0 +1,51 @@ +import "../mod/libfwdmem.so" +import "../mod/libfwdutil.so" + +/* trait */ +any = {} + +/* template */ +vec[any type]() { + <> { + i64 s; + i64 n; + *[]type buf; + } + + <>create((<>) next) + { + next((0 => s, 0 => n, * => buf)<>); + } + + <>append(<> v, type n, (<>) next) + { + v.n = v.n + 1; + if v.s == 0 { + v.s = 1; + } + + if v.n > v.s { + if v.s == 0 { + v.s = 1; + } + + v.s = v.s * 2; + fwdrealloc(v.buf, v.s as u64 * sizeof(type)) => ptr; + v.buf = ptr; + } + + /* in a real implementation this would be moved to after + * fwdrealloc I guess, but this is fine for now */ + nil v.buf {fwdpanic("no buf for vector");} => buf; + buf*[v.n - 1] = n; + next(v); + } + + <>destroy(<> v) + { + fwdfree(v.buf); + nil v; + } +} + +u8vec = vec[u8]() diff --git a/include/fwd/ast.h b/include/fwd/ast.h index 69f7bf1..044a27e 100644 --- a/include/fwd/ast.h +++ b/include/fwd/ast.h @@ -32,12 +32,46 @@ struct src_loc { struct ast; enum type_kind { - TYPE_ID = 1, TYPE_CONSTRUCT, TYPE_REF, TYPE_PTR, TYPE_CLOSURE, - TYPE_PURE_CLOSURE, TYPE_FUNC_PTR, TYPE_VOID, + TYPE_ID = 1, TYPE_REF, TYPE_PTR, TYPE_ARR, TYPE_CLOSURE, + TYPE_PURE_CLOSURE, TYPE_FUNC_PTR, TYPE_VOID, TYPE_NIL, TYPE_I8, TYPE_I16, TYPE_I32, TYPE_I64, TYPE_U8, TYPE_U16, TYPE_U32, TYPE_U64 }; +static inline bool is_int_type(enum type_kind k) +{ + switch (k) { + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + case TYPE_U8: + case TYPE_U16: + case TYPE_U32: + case TYPE_U64: + return true; + + default: + return false; + } + + return false; +} + +static inline bool is_ptr_type(enum type_kind k) +{ + switch (k) { + case TYPE_PTR: + case TYPE_FUNC_PTR: + return true; + + default: + return false; + } + + return false; +} + struct type { enum type_kind k; @@ -73,6 +107,8 @@ enum ast_kind { /** If statement */ AST_IF, /** Nil check */ + AST_NIL_CHECK, + /** Nil */ AST_NIL, /** Call procedure. */ AST_CALL, @@ -124,12 +160,29 @@ enum ast_kind { AST_EQ, /** Negation, \c - */ AST_NEG, + /** Bitwise negation, \c ~ */ + AST_BNEG, /** Logical negation, \c ! */ AST_LNOT, /** Bitwise negation, \c ~ */ AST_NOT, /** Reference (note, not addrof!) */ AST_REF, + /** Sizeof */ + AST_SIZEOF, + AST_AS, + AST_ARR, + AST_ASSIGN, + AST_CONSTRUCTION, + AST_CONSTRUCT, + AST_SELF, + AST_FORGET, + AST_PASTE, + AST_IMPLEMENTS, + AST_TEMPLATE, + AST_INSTANCE, + AST_SUPERTEMPLATE, + AST_INSTANCE_CONT, /** Dereferencing. * @todo should references and pointers both use this? */ @@ -270,11 +323,14 @@ static inline bool is_const(struct ast *x) static inline bool is_lvalue(struct ast *node) { switch (node->k) { + case AST_ARR: + case AST_DEREF: + case AST_DOT: case AST_ID: - /** @todo others */ return true; - default: return false; + default: + return false; } return false; @@ -295,13 +351,8 @@ static inline bool is_trivially_copyable(struct type *type) case TYPE_PTR: case TYPE_FUNC_PTR: case TYPE_PURE_CLOSURE: - /** @todo primitive types */ return true; - case TYPE_ID: - /* very special, bad bad bad */ - return strcmp(type->id, "int") == 0; - default: return false; } @@ -318,11 +369,11 @@ static inline bool is_trivially_copyable(struct type *type) #define gen_str(k, s, loc) gen_ast(k, NULL, NULL, NULL, NULL, NULL, s, -1, 0., \ loc) - #define gen4(k, a, b, c, d, loc) gen_ast(k, a, b, c, d, NULL, NULL, -1, 0., loc) #define gen3(k, a, b, c, loc) gen4(k, a, b, c, NULL, loc) #define gen2(k, a, b, loc) gen3(k, a, b, NULL, loc) #define gen1(k, a, loc) gen2(k, a, NULL, loc) +#define gen0(k, loc) gen1(k, NULL, loc) /* kind of hacky but I guess it works, and allows us to check that the type is @@ -340,17 +391,16 @@ static inline bool is_trivially_copyable(struct type *type) #define tgen_void(loc) \ tgen_type(TYPE_VOID, NULL, NULL, loc) -#define tgen_err(loc) \ - tgen_type(TYPE_ERR, NULL, NULL, loc) - #define tid_str(x) return_id(x, TYPE_ID) #define tgen_id(id, loc) \ tgen_type(TYPE_ID, NULL, id, loc) -#define tconstruct_id(x) return_id(x, TYPE_CONSTRUCT) -#define tconstruct_args(x) return_t0(x, TYPE_CONSTRUCT) -#define tgen_construct(id, args, loc) \ - tgen_type(TYPE_CONSTRUCT, args, id, loc) +#define tgen_nil(loc) \ + tgen_type(TYPE_NIL, NULL, NULL, loc) + +#define tarr_base(x) return_t0(x, TYPE_ARR) +#define tgen_arr(base, len, loc) \ + tgen_type(TYPE_ARR, base, len, loc) #define tptr_base(x) return_t0(x, TYPE_PTR) #define tgen_ptr(base, loc) \ @@ -387,6 +437,60 @@ static inline bool is_trivially_copyable(struct type *type) #define gen_comparison(op, left, right, loc) \ gen2(op, left, right, loc) +#define construction_id(x) return_s(x, AST_CONSTRUCTION) +#define construction_expr(x) return_a0(x, AST_CONSTRUCTION) +#define gen_construction(id, expr, loc) \ + gen_str1(AST_CONSTRUCTION, id, expr, loc) + +#define construct_id(x) return_s(x, AST_CONSTRUCT) +#define construct_members(x) return_a0(x, AST_CONSTRUCT) +#define gen_construct(id, members, loc) \ + gen_str1(AST_CONSTRUCT, id, members, loc) + +#define gen_paste(l, r, loc) \ + gen2(AST_PASTE, l, r, loc) + +#define forget_id(x) return_s(x, AST_FORGET) +#define gen_forget(id, loc) \ + gen_str(AST_FORGET, id, loc) + +#define gen_implements(id, loc) \ + gen_str(AST_IMPLEMENTS, id, loc) + +#define template_type_params(x) return_a0(x, AST_TEMPLATE) +#define template_body(x) return_a2(x, AST_TEMPLATE) +#define gen_template(id, types, params, body, loc) \ + gen_str3(AST_TEMPLATE, id, types, params, body, loc) + +#define gen_supertemplate(id, types, params, inst, body, loc) \ + gen_ast(AST_SUPERTEMPLATE, types, params, inst, body, NULL, id, 0, 0., loc) + +#define gen_self(loc) \ + gen0(AST_SELF, loc) + +#define instance_id(x) return_s(x, AST_INSTANCE) +#define instance_templ(x) return_a1(x, AST_INSTANCE) +#define instance_type_args(x) return_t2(x, AST_INSTANCE) +#define gen_instance(name, templ, types, exprs, loc) \ + gen_ast(AST_INSTANCE, exprs, templ, NULL, NULL, types, name, 0, 0., loc) + +#define gen_instance_cont(name, body, loc) \ + gen_str1(AST_INSTANCE_CONT, name, body, loc) + +#define sizeof_type(x) return_t2(x, AST_SIZEOF) +#define gen_sizeof(type, loc) \ + gen_ast(AST_SIZEOF, NULL, NULL, NULL, NULL, type, NULL, 0, 0., loc) + +#define assign_expr(x) return_a1(x, AST_ASSIGN) +#define assign_target(x) return_a0(x, AST_ASSIGN) +#define gen_assign(l, r, loc) \ + gen2(AST_ASSIGN, l, r, loc) + +#define arr_base(x) return_a0(x, AST_ARR) +#define arr_idx(x) return_a1(x, AST_ARR) +#define gen_arr(base, idx, loc) \ + gen2(AST_ARR, base, idx, loc) + #define unop_expr(x) ({assert(is_unop(x)); x->a0;}) #define gen_unop(op, expr, loc) \ gen1(op, expr, loc) @@ -427,10 +531,9 @@ static inline bool is_trivially_copyable(struct type *type) gen_str(AST_TRAIT_DEF, id, loc) #define struct_id(x) return_s(x, AST_STRUCT_DEF) -#define struct_params(x) return_a0(x, AST_STRUCT_DEF) -#define struct_body(x) return_a1(x, AST_STRUCT_DEF) -#define gen_struct(id, params, body, loc) \ - gen_str2(AST_STRUCT_DEF, id, params, body, loc) +#define struct_body(x) return_a0(x, AST_STRUCT_DEF) +#define gen_struct(id, body, loc) \ + gen_str1(AST_STRUCT_DEF, id, body, loc) #define import_file(x) return_s(x, AST_IMPORT) #define gen_import(file, loc) \ @@ -479,11 +582,19 @@ static inline bool is_trivially_copyable(struct type *type) #define gen_if(cond, body, els, loc) \ gen3(AST_IF, cond, body, els, loc) -#define nil_var(x) return_s(x, AST_NIL) -#define nil_body(x) return_a1(x, AST_NIL) -#define nil_ref(x) return_a2(x, AST_NIL) -#define gen_nil(var, body, ref, loc) \ - gen_ast(AST_NIL, NULL, body, ref, NULL, NULL, var, 0, 0., loc) +#define nil_check_expr(x) return_a0(x, AST_NIL_CHECK) +#define nil_check_body(x) return_a1(x, AST_NIL_CHECK) +#define nil_check_ref(x) return_a2(x, AST_NIL_CHECK) +#define gen_nil_check(expr, body, ref, loc) \ + gen3(AST_NIL_CHECK, expr, body, ref, loc) + +#define gen_nil(loc) \ + gen0(AST_NIL, loc) + +#define as_expr(x) return_a0(x, AST_AS) +#define as_type(x) return_t2(x, AST_AS) +#define gen_as(expr, type, loc) \ + gen_ast(AST_AS, expr, NULL, NULL, NULL, type, NULL, 0, 0., loc) #define own_id(x) return_s(x, AST_OWN) #define own_body(x) return_a1(x, AST_OWN) diff --git a/include/fwd/debug.h b/include/fwd/debug.h index c3dce63..ac4dfdf 100644 --- a/include/fwd/debug.h +++ b/include/fwd/debug.h @@ -96,6 +96,9 @@ void semantic_warn(struct scope *scope, struct ast *node, const char *fmt, ...); void semantic_error(struct scope *scope, struct ast *node, const char *fmt, ...); +void type_error(struct scope *scope, struct type *type, const char *fmt, + ...); + void loc_error(struct scope *scope, struct src_loc loc, const char *fmt, ...); /** @@ -141,6 +144,6 @@ void type_mismatch(struct scope *scope, struct ast *node, void move_error(struct ast *new_use, struct ast *prev_use); void reference_error(struct ast *new_use, struct ast *prev_use); -const char *type_str(struct type *t); +char *type_str(struct type *t); #endif /* FWD_DEBUG_H */ diff --git a/include/fwd/mod.h b/include/fwd/mod.h index 6031c92..51aa9c4 100644 --- a/include/fwd/mod.h +++ b/include/fwd/mod.h @@ -61,6 +61,10 @@ static inline void *fwd_arg(fwd_extern_args_t args, size_t idx, fwd_type_t id) #define FWD_ARG_T(args, idx, type) \ FWD_ARG(args, idx, type, FWD_T(type)) +/* unimplemented as of yet */ +#define FWD_RET(id, x) \ + abort() + static inline fwd_type_t fwd_t_signed(size_t s) { switch (s) { diff --git a/include/fwd/rewrite.h b/include/fwd/rewrite.h new file mode 100644 index 0000000..d9c4129 --- /dev/null +++ b/include/fwd/rewrite.h @@ -0,0 +1,9 @@ +#ifndef FWD_REWRITE_H +#define FWD_REWRITE_H + +#include <fwd/ast.h> + +int rewrite_types(struct ast *node, char *orig, char *new); +int rewrite_holes(struct ast *node, char *new); + +#endif /* FWD_REWRITE_H */ diff --git a/include/fwd/scope.h b/include/fwd/scope.h index ab268bc..c3bafa0 100644 --- a/include/fwd/scope.h +++ b/include/fwd/scope.h @@ -55,6 +55,7 @@ struct scope { /** List of child scopes. */ struct scope *children; + struct visible templates; struct visible symbols; struct visible traits; struct visible types; @@ -113,13 +114,11 @@ int scope_add_scratch(struct scope *scope, struct ast *scratch); */ void scope_add_scope(struct scope *parent, struct scope *child); - int scope_add_symbol(struct scope *scope, struct ast *symbol); struct ast *scope_find_symbol(struct scope *scope, char *id); struct ast *file_scope_find_symbol(struct scope *scope, char *id); int scope_add_type(struct scope *scope, struct ast *type); -int scope_add_chain(struct scope *scope, struct ast *type); struct ast *scope_find_type(struct scope *scope, char *id); struct ast *file_scope_find_type(struct scope *scope, char *id); @@ -127,4 +126,8 @@ int scope_add_trait(struct scope *scope, struct ast *trait); struct ast *scope_find_trait(struct scope *scope, char *id); struct ast *file_scope_find_trait(struct scope *scope, char *id); +int scope_add_template(struct scope *scope, struct ast *trait); +struct ast *scope_find_template(struct scope *scope, char *id); +struct ast *file_scope_find_template(struct scope *scope, char *id); + #endif /* SCOPE_H */ diff --git a/mod/Makefile b/mod/Makefile index 0a0c045..9121afd 100644 --- a/mod/Makefile +++ b/mod/Makefile @@ -1,4 +1,10 @@ -all: libfwdio.so +all: libfwdio.so libfwdmem.so libfwdutil.so libfwdio.so: io.c ../include/fwd/mod.h $(CC) -I../include -fPIC -O2 -g -Wall -Wextra -shared io.c -o libfwdio.so + +libfwdmem.so: mem.c ../include/fwd/mod.h + $(CC) -I../include -fPIC -O2 -g -Wall -Wextra -shared mem.c -o libfwdmem.so + +libfwdutil.so: util.c ../include/fwd/mod.h + $(CC) -I../include -fPIC -O2 -g -Wall -Wextra -shared util.c -o libfwdutil.so @@ -4,14 +4,14 @@ #include <fwd/mod.h> -long print_nl(fwd_extern_args_t args) +long fwdprint_nl(fwd_extern_args_t args) { assert(args.argc == 0); putchar('\n'); return 0; } -long print_i64(fwd_extern_args_t args) +long fwdprint_i64(fwd_extern_args_t args) { assert(args.argc == 1); int64_t n = FWD_ARG_T(args, 0, int64_t); @@ -21,10 +21,10 @@ long print_i64(fwd_extern_args_t args) int fwdopen(fwd_state_t *state) { - FWD_REGISTER(state, print_nl, + FWD_REGISTER(state, fwdprint_nl, FWD_VOID); - FWD_REGISTER(state, print_i64, + FWD_REGISTER(state, fwdprint_i64, FWD_VOID, FWD_T(int64_t)); return 0; diff --git a/mod/mem.c b/mod/mem.c new file mode 100644 index 0000000..5303ffa --- /dev/null +++ b/mod/mem.c @@ -0,0 +1,38 @@ +#include <fwd/mod.h> + +long fwdmalloc(fwd_extern_args_t args) +{ + assert(args.argc == 1); + size_t n = FWD_ARG_T(args, 0, size_t); + FWD_RET(FWD_PTR, malloc(n)); + return 0; +} + +long fwdrealloc(fwd_extern_args_t args) +{ + assert(args.argc == 2); + void *ptr = FWD_ARG(args, 0, void *, FWD_PTR); + size_t n = FWD_ARG_T(args, 1, size_t); + FWD_RET(FWD_PTR, realloc(ptr, n)); + return 0; +} + +long fwdfree(fwd_extern_args_t args) +{ + assert(args.argc == 1); + void *ptr = FWD_ARG(args, 0, void *, FWD_PTR); + return 0; +} + +int fwdopen(fwd_state_t *state) +{ + FWD_REGISTER(state, fwdmalloc, + FWD_PTR, FWD_T(size_t)); + + FWD_REGISTER(state, fwdrealloc, + FWD_PTR, FWD_PTR, FWD_T(size_t)); + + FWD_REGISTER(state, fwdfree, + FWD_VOID, FWD_PTR); + return 0; +} diff --git a/mod/util.c b/mod/util.c new file mode 100644 index 0000000..c967349 --- /dev/null +++ b/mod/util.c @@ -0,0 +1,20 @@ +#include <fwd/mod.h> +#include <stdio.h> + +long fwdpanic(fwd_extern_args_t args) +{ + assert(args.argc == 1); + char *str = FWD_ARG_T(args, 0, char *); + fprintf(stderr, "%s", str); + exit(1); + return 0; +} + +int fwdopen(fwd_state_t *state) +{ + /** @todo passing around strings might be common enough to warrant its + * own type? */ + FWD_REGISTER(state, fwdpanic, + FWD_VOID, FWD_PTR); + return 0; +} diff --git a/src/analyze.c b/src/analyze.c index be52bfb..4f98ac6 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -3,7 +3,9 @@ #include <fwd/compiler.h> #include <fwd/analyze.h> +#include <fwd/rewrite.h> #include <fwd/mod.h> + #include <stdlib.h> #include <stdarg.h> #include <string.h> @@ -214,16 +216,36 @@ static int analyze_init(struct state *state, struct scope *scope, if (analyze(state, scope, init_body(node))) return -1; - /** @todo check that all parameters match, though that requires that the - * type is defined in fwd and not in C++, so this might have to wait a - * bit */ - node->t = tgen_construct(strdup(init_id(node)), init_args(node), - node->loc); - if (!node->t) { - internal_error("failed allocating type for construct"); + internal_error("init unimplemented"); + return -1; +} + +static int deduce_closure_types(struct scope *scope, struct ast *node, struct type *type) +{ + /* a bit of checking duplication here, hmm */ + if (node->k != AST_CLOSURE) + return 0; + + if (type->k != TYPE_CLOSURE && type->k != TYPE_PURE_CLOSURE) { + semantic_error(scope, node, "surprise closure"); + return -1; + } + + struct ast *param = closure_bindings(node); + struct type *t = tclosure_args(type); + if (ast_list_len(param) != type_list_len(t)) { + semantic_error(scope, node, "expected %zu closure parameters, got %zu", + type_list_len(t), ast_list_len(param)); return -1; } + for (; param && t; param = param->n, t = t->n) { + if (var_type(param)) + continue; + + var_type(param) = t; + } + return 0; } @@ -241,8 +263,6 @@ static int analyze_call(struct state *state, struct scope *scope, struct type *types = callable_types(expr->t); struct ast *args = call_args(node); - if (analyze_list(state, scope, args)) - return -1; size_t expected = type_list_len(types); size_t got = ast_list_len(args); @@ -253,18 +273,24 @@ static int analyze_call(struct state *state, struct scope *scope, } struct type *type = types; - foreach_node(arg, args) { + struct ast *arg = args; + for (; arg && type; type = type->n, arg = arg->n) { + if (deduce_closure_types(scope, arg, type)) + return -1; + + if (analyze(state, scope, arg)) + return -1; + if (arg->k == AST_CLOSURE) state->flags |= STATE_PROC_REQ_FRAME; if (!types_match(type, arg->t)) { - type_mismatch(scope, node, type, arg->t); + type_mismatch(scope, arg, type, arg->t); return -1; } /* clone group info */ arg->t->group = type->group; - type = type->n; } node->t = tgen_void(node->loc); @@ -309,7 +335,7 @@ static int analyze_deref(struct state *state, struct scope *scope, semantic_error(node->scope, node, "deref of raw ptr not allowed"); semantic_info(node->scope, node, - "use fwd_null() to convert to ref"); + "use a nil check to convert to ref"); return -1; } @@ -319,7 +345,7 @@ static int analyze_deref(struct state *state, struct scope *scope, return -1; } - node->t = tptr_base(expr->t); + node->t = tref_base(expr->t); return 0; } @@ -432,6 +458,126 @@ static int analyze_str(struct state *state, struct scope *scope, return 0; } +static int analyze_template(struct state *state, struct scope *scope, struct ast *node) +{ + struct scope *template_scope = create_scope(); + scope_add_scope(scope, template_scope); + + /* enough for vec example since traits are as of yet unimplemented */ + + node->t = tgen_void(node->loc); + return 0; +} + +static int analyze_instance(struct state *state, struct scope *scope, struct ast *node) +{ + (void)state; + + struct ast *id = instance_templ(node); + struct ast *template = file_scope_find_template(scope, id_str(id)); + if (!template) { + semantic_error(scope, node, "no such template: %s\n", id_str(id)); + return -1; + } + + /** @todo check that type implements trait */ + struct ast *params = template_type_params(template); + struct type *args = instance_type_args(node); + if (ast_list_len(params) != type_list_len(args)) { + semantic_error(scope, node, "expected %zu types, got %zu\n", + ast_list_len(params), type_list_len(args)); + return -1; + } + + /* make a working copy that we modify */ + struct ast *copy = clone_ast(template); + if (!copy) { + internal_error("failed copying template body"); + return -1; + } + + if (rewrite_holes(copy, instance_id(node))) + return -1; + + /* replace type args */ + struct ast *p = params; + struct type *a = args; + for (; p && a; p = p->n, a = a->n) { + if (rewrite_types(copy, var_id(p), tid_str(a))) + return -1; + } + + printf("// --- instance %s ---\n", instance_id(node)); + ast_dump(0, copy); + + /* analyze newly initialized things (might not work for supertemplates?) */ + if (analyze_root(scope, template_body(copy))) + return -1; + + node->t = tgen_void(node->loc); + return 0; +} + +static int analyze_struct(struct state *state, struct scope *scope, struct ast *node) +{ + struct scope *struct_scope = create_scope(); + scope_add_scope(scope, struct_scope); + node->scope = struct_scope; + + if (analyze_list(state, struct_scope, struct_body(node))) + return -1; + + node->t = tgen_void(node->loc); + return 0; +} + +static int analyze_construct(struct state *state, struct scope *scope, struct ast *node) +{ + struct ast *def = file_scope_find_type(scope, construct_id(node)); + if (!def) { + semantic_error(scope, node, "no such type"); + return -1; + } + + if (def->k != AST_STRUCT_DEF) { + semantic_error(scope, node, "type is not a struct"); + return -1; + } + + struct ast *params = struct_body(def); + struct ast *args = construct_members(node); + + if (ast_list_len(params) != ast_list_len(args)) { + semantic_error(scope, node, "expected %zu args, got %zu\n", + ast_list_len(params), ast_list_len(args)); + return -1; + } + + foreach_node(arg, args) { + struct ast *exists = scope_find_symbol(def->scope, construction_id(arg)); + if (!exists) { + semantic_error(scope, arg, "no member %s in %s\n", + construction_id(arg), construct_id(node)); + return -1; + } + + struct ast *expr = construction_expr(arg); + if (analyze(state, scope, expr)) + return -1; + + if (!types_match(exists->t, expr->t)) { + type_mismatch(scope, arg, exists->t, expr->t); + return -1; + } + + /** @todo check for duplicates, preferably not in O(n^2) or with + * a memory allocation? */ + } + + node->t = tgen_id(strdup(construct_id(node)), node->loc); + return 0; +} + static int analyze_if(struct state *state, struct scope *scope, struct ast *node) { @@ -453,6 +599,212 @@ static int analyze_if(struct state *state, struct scope *scope, return 0; } +static int analyze_nil(struct state *state, struct scope *scope, struct ast *node) +{ + (void)state; + (void)scope; + + node->t = tgen_nil(node->loc); + return 0; +} + +static bool castable_type(struct type *type) +{ + switch (type->k) { + case TYPE_PTR: + case TYPE_FUNC_PTR: + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + case TYPE_U8: + case TYPE_U16: + case TYPE_U32: + case TYPE_U64: + return true; + + default: + return false; + } + + return false; +} + +static int analyze_as(struct state *state, struct scope *scope, struct ast *node) +{ + if (analyze(state, scope, as_expr(node))) + return -1; + + if (analyze_type(state, scope, as_type(node))) + return -1; + + /* for now, any 'register' size type can be cast to any other. Might + * restrict this, like signed to unsigned might be weird if its negative + * etc and should be done via a helper function */ + struct type *expr_type = (as_expr(node))->t; + struct type *type = as_type(node); + if (!castable_type(expr_type)) { + char *s = type_str(expr_type); + semantic_error(scope, as_expr(node), "can't cast from %s\n", s); + free(s); + return -1; + } + + if (!castable_type(type)) { + char *s = type_str(type); + type_error(scope, type, "can't cast to %s\n", s); + free(s); + return -1; + } + + node->t = type; + return 0; +} + +static int analyze_assign(struct state *state, struct scope *scope, struct ast *node) +{ + struct ast *expr = assign_expr(node); + if (analyze(state, scope, expr)) + return -1; + + struct ast *target = assign_target(node); + if (analyze(state, scope, target)) + return -1; + + + if (!types_match(expr->t, target->t)) { + type_mismatch(scope, node, target->t, expr->t); + return -1; + } + + if (!is_lvalue(target)) { + semantic_error(scope, target, "not an lvalue"); + return -1; + } + + node->t = target->t; + return 0; +} + +static int analyze_dot(struct state *state, struct scope *scope, struct ast *node) +{ + struct ast *expr = dot_expr(node); + if (analyze(state, scope, expr)) + return -1; + + struct type *type = expr->t; + if (type->k != TYPE_ID) { + char *s = type_str(type); + semantic_error(scope, node, "type %s does not have any members", s); + free(s); + return -1; + } + + struct ast *def = file_scope_find_type(scope, tid_str(type)); + if (!def) { + semantic_error(scope, node, "no such type: %s", tid_str(type)); + return -1; + } + + if (def->k != AST_STRUCT_DEF) { + semantic_error(scope, node, "not a struct type: %s", tid_str(type)); + return -1; + } + + struct ast *member = scope_find_symbol(def->scope, dot_id(node)); + if (!member) { + semantic_error(scope, node, "no such member: %s", dot_id(node)); + return -1; + } + + node->t = member->t; + return 0; +} + +static int analyze_sizeof(struct state *state, struct scope *scope, struct ast *node) +{ + if (analyze_type(state, scope, sizeof_type(node))) + return -1; + + /* hmm, no built-in size_t */ + node->t = tgen_type(TYPE_U64, NULL, NULL, node->loc); + return 0; +} + +static int analyze_arr(struct state *state, struct scope *scope, struct ast *node) +{ + struct ast *base = arr_base(node); + if (analyze(state, scope, base)) + return -1; + + if (base->t->k != TYPE_ARR) { + char *s = type_str(base->t); + semantic_error(scope, base, "expected array type, got %s", s); + free(s); + return -1; + } + + struct ast *idx = arr_idx(node); + if (analyze(state, scope, idx)) + return -1; + + if (!is_int_type(idx->t->k)) { + char *s = type_str(idx->t); + semantic_error(scope, idx, "expected int type, got %s", s); + free(s); + return -1; + } + + node->t = tarr_base(base->t); + return 0; +} + +static int analyze_nil_check(struct state *state, struct scope *scope, struct ast *node) +{ + struct ast *expr = nil_check_expr(node); + if (analyze(state, scope, expr)) + return -1; + + if (!is_ptr_type(expr->t->k)) { + char *s = type_str(expr->t); + semantic_error(scope, expr, "expected ptr type, got %s", s); + free(s); + return -1; + } + + if (analyze(state, scope, nil_check_body(node))) + return -1; + + struct ast *var = nil_check_ref(node); + struct type *base = tptr_base(expr->t); + struct type *ref = tgen_ref(clone_type(base), node->loc); + if (!var_type(var)) + var_type(var) = ref; + + if (analyze(state, scope, var)) + return -1; + + if (!types_match(ref, var->t)) { + type_mismatch(scope, var, ref, var->t); + return -1; + } + + node->t = tgen_void(node->loc); + return 0; +} + +static int analyze_forget(struct state *state, struct scope *scope, struct ast *node) +{ + struct ast *def = file_scope_find_symbol(scope, forget_id(node)); + if (!def) { + semantic_error(scope, node, "no such symbol"); + return -1; + } + + node->t = tgen_void(node->loc); + return 0; +} + static int analyze(struct state *state, struct scope *scope, struct ast *node) { if (!node) @@ -502,9 +854,32 @@ static int analyze(struct state *state, struct scope *scope, struct ast *node) case AST_ID: ret = analyze_id(state, scope, node); break; case AST_IF: ret = analyze_if(state, scope, node); break; + case AST_NIL_CHECK: ret = analyze_nil_check(state, scope, node); break; + case AST_CONST_INT: ret = analyze_int(state, scope, node); break; case AST_CONST_STR: ret = analyze_str(state, scope, node); break; + case AST_INSTANCE: ret = analyze_instance(state, scope, node); break; + case AST_TEMPLATE: ret = analyze_template(state, scope, node); break; + + case AST_STRUCT_DEF: ret = analyze_struct(state, scope, node); break; + case AST_CONSTRUCT: ret = analyze_construct(state, scope, node); break; + + case AST_SIZEOF: ret = analyze_sizeof(state, scope, node); break; + case AST_NIL: ret = analyze_nil(state, scope, node); break; + case AST_AS: ret = analyze_as(state, scope, node); break; + + case AST_ASSIGN: ret = analyze_assign(state, scope, node); break; + case AST_ARR: ret = analyze_arr(state, scope, node); break; + case AST_DOT: ret = analyze_dot(state, scope, node); break; + + case AST_FORGET: ret = analyze_forget(state, scope, node); break; + + case AST_TRAIT_DEF: + node->t = tgen_void(node->loc); + ret = 0; + break; + case AST_EMPTY: node->t = tgen_void(node->loc); ret = 0; @@ -548,39 +923,61 @@ static int analyze_type(struct state *state, struct scope *scope, return -1; /* temporary */ - if (type->k != TYPE_ID) + if (!type->id) return 0; char *id = type->id; - if (strcmp(id, "i8") == 0) + /* handle primitives */ + if (strcmp(id, "i8") == 0) { type->k = TYPE_I8; + return 0; + } - else if (strcmp(id, "u8") == 0) + if (strcmp(id, "u8") == 0) { type->k = TYPE_U8; + return 0; + } - else if (strcmp(id, "i16") == 0) + if (strcmp(id, "i16") == 0) { type->k = TYPE_I16; + return 0; + } - else if (strcmp(id, "u16") == 0) + if (strcmp(id, "u16") == 0) { type->k = TYPE_U16; + return 0; + } - else if (strcmp(id, "i32") == 0) + if (strcmp(id, "i32") == 0) { type->k = TYPE_I32; + return 0; + } - else if (strcmp(id, "u32") == 0) + if (strcmp(id, "u32") == 0) { type->k = TYPE_U32; + return 0; + } - else if (strcmp(id, "i64") == 0) + if (strcmp(id, "i64") == 0) { type->k = TYPE_I64; + return 0; + } - else if (strcmp(id, "u64") == 0) + if (strcmp(id, "u64") == 0) { type->k = TYPE_U64; - else { - internal_error("unhandled type id: %s", id); - abort(); + return 0; } + /* check for user-defined types */ + struct ast *def = file_scope_find_type(scope, id); + if (!def) { + type_error(scope, type, "no such type: %s", id); + return -1; + } + + /* don't change type->k since we might want to check up on this again + * later */ return 0; } @@ -609,27 +1006,39 @@ static int copy_scope(bool reexport, struct scope *to, struct scope *from) return -1; } - foreach(visible, symbol, &from->types) { - struct ast *def = symbol->data; + foreach(visible, type, &from->types) { + struct ast *def = type->data; if (!reexport && def->scope != from) continue; if (!ast_flags(def, AST_FLAG_PUBLIC)) continue; - if (scope_add_symbol(to, def)) + if (scope_add_type(to, def)) return -1; } - foreach(visible, symbol, &from->traits) { - struct ast *def = symbol->data; + foreach(visible, trait, &from->traits) { + struct ast *def = trait->data; if (!reexport && def->scope != from) continue; if (!ast_flags(def, AST_FLAG_PUBLIC)) continue; - if (scope_add_symbol(to, def)) + if (scope_add_trait(to, def)) + return -1; + } + + foreach(visible, template, &from->templates) { + struct ast *def = template->data; + if (!reexport && def->scope != from) + continue; + + if (!ast_flags(def, AST_FLAG_PUBLIC)) + continue; + + if (scope_add_template(to, def)) return -1; } @@ -677,9 +1086,10 @@ static struct type *fwd_type_kind(fwd_type_t type) switch (type) { case FWD_VOID: return tgen_void(NULL_LOC()); case FWD_I64: return tgen_type(TYPE_I64, NULL, NULL, NULL_LOC()); + case FWD_U64: return tgen_type(TYPE_U64, NULL, NULL, NULL_LOC()); case FWD_PTR: { - struct type *base = tgen_void(NULL_LOC()); - return tgen_ptr(base, NULL_LOC()); + /* hack? */ + return tgen_nil(NULL_LOC()); } default: @@ -697,20 +1107,42 @@ int fwd_register(struct fwd_state *state, const char *name, fwd_extern_t func, f va_list args; va_start(args, rtype); + + size_t idx = 0; while (1) { fwd_type_t type = va_arg(args, enum fwd_type); if (type == FWD_END) break; - struct ast *new = gen_var(strdup("arg"), fwd_type_kind(type), NULL_LOC()); + /* eight bytes is likely more than enough, since first three are + * var, then we have four bytes for indexes and one trailing + * NULL */ + char *name = calloc(8, sizeof(char)); + assert(name); + + sprintf(name, "%s%zu", "var", idx); + struct ast *new = gen_var(name, fwd_type_kind(type), NULL_LOC()); if (vars) - vars->n = new; - else - vars = new; + new->n = vars; + + vars = new; + idx++; } va_end(args); + /* append 'return' as a continuation */ + if (rtype != FWD_VOID) { + char *name = strdup("ret"); + struct ast *new = gen_var(name, + tgen_closure(fwd_type_kind(rtype), NULL_LOC()), + NULL_LOC()); + if (vars) + new->n = vars; + + vars = new; + } + vars = reverse_ast_list(vars); struct ast *def = gen_proc(strdup(name), vars, @@ -737,8 +1169,7 @@ int analyze_root(struct scope *scope, struct ast *root) break; case AST_STRUCT_CONT: - if (scope_add_chain(scope, node)) - return -1; + abort(); break; case AST_TRAIT_DEF: @@ -756,6 +1187,19 @@ int analyze_root(struct scope *scope, struct ast *root) return -1; } + case AST_TEMPLATE: + if (scope_add_template(scope, node)) + return -1; + break; + + case AST_SUPERTEMPLATE: + if (scope_add_template(scope, node)) + return -1; + break; + + case AST_INSTANCE: + break; + default: abort(); } @@ -243,6 +243,22 @@ void ast_dump(int depth, struct ast *n) DUMP(AST_NOT); DUMP(AST_REF); DUMP(AST_DEREF); + DUMP(AST_NIL_CHECK); + DUMP(AST_BNEG); + DUMP(AST_SIZEOF); + DUMP(AST_AS); + DUMP(AST_ARR); + DUMP(AST_ASSIGN); + DUMP(AST_CONSTRUCTION); + DUMP(AST_CONSTRUCT); + DUMP(AST_SELF); + DUMP(AST_FORGET); + DUMP(AST_PASTE); + DUMP(AST_IMPLEMENTS); + DUMP(AST_TEMPLATE); + DUMP(AST_INSTANCE); + DUMP(AST_SUPERTEMPLATE); + DUMP(AST_INSTANCE_CONT); DUMP(AST_CONST_INT); DUMP(AST_CONST_CHAR); DUMP(AST_CONST_BOOL); @@ -320,6 +336,9 @@ struct ast *clone_ast(struct ast *n) if (n->a3) new->a3 = clone_ast_list(n->a3); + if (n->t2) + new->t2 = clone_type_list(n->t2); + /** @todo rebuild opt group? */ return new; @@ -351,13 +370,14 @@ struct type *clone_type(struct type *type) return NULL; new->k = type->k; - if (type->id && !(new->id = strdup(type->id))) - return NULL; + if (type->id) + new->id = strdup(type->id); - if (type->t0 && !(new->t0 = clone_type_list(type->t0))) - return NULL; + if (type->t0) + new->t0 = clone_type_list(type->t0); new->group = type->group; + new->loc = type->loc; return new; } @@ -589,6 +609,13 @@ bool types_match(struct type *a, struct type *b) if (!a && b) return false; + /* nil matches any kind of pointer */ + if (is_ptr_type(a->k) && b->k == TYPE_NIL) + return true; + + if (a->k == TYPE_NIL && is_ptr_type(b->k)) + return true; + if (a->k != b->k) return false; @@ -662,6 +689,22 @@ const char *ast_str(enum ast_kind k) CASE(AST_NOT); CASE(AST_REF); CASE(AST_DEREF); + CASE(AST_NIL_CHECK); + CASE(AST_BNEG); + CASE(AST_SIZEOF); + CASE(AST_AS); + CASE(AST_ARR); + CASE(AST_ASSIGN); + CASE(AST_CONSTRUCTION); + CASE(AST_CONSTRUCT); + CASE(AST_SELF); + CASE(AST_FORGET); + CASE(AST_PASTE); + CASE(AST_IMPLEMENTS); + CASE(AST_TEMPLATE); + CASE(AST_INSTANCE); + CASE(AST_SUPERTEMPLATE); + CASE(AST_INSTANCE_CONT); CASE(AST_CONST_INT); CASE(AST_CONST_CHAR); CASE(AST_CONST_BOOL); diff --git a/src/debug.c b/src/debug.c index f3540f0..8ef4293 100644 --- a/src/debug.c +++ b/src/debug.c @@ -134,14 +134,27 @@ void semantic_error(struct scope *scope, struct ast *node, va_end(args); } +void type_error(struct scope *scope, struct type *type, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + struct src_issue issue; + issue.level = SRC_ERROR; + issue.loc = type->loc; + issue.fctx = scope->fctx; + _issue(issue, fmt, args); + va_end(args); +} + void type_mismatch(struct scope *scope, struct ast *node, struct type *l, struct type *r) { - const char *ls = type_str(l); - const char *rs = type_str(r); + char *ls = type_str(l); + char *rs = type_str(r); semantic_error(scope, node, "type mismatch: %s vs %s\n", ls, rs); - free((void *)ls); - free((void *)rs); + free(ls); + free(rs); } void move_error(struct ast *new_use, struct ast *prev_use) @@ -213,6 +226,16 @@ static void _type_str(FILE *f, struct type *type) fprintf(f, "void"); break; + case TYPE_NIL: + fprintf(f, "nil"); + break; + + case TYPE_ARR: + /** @todo length? */ + fprintf(f, "[]"); + _type_list_str(f, tarr_base(type)); + break; + case TYPE_PTR: fprintf(f, "*"); _type_list_str(f, tptr_base(type)); @@ -244,16 +267,10 @@ static void _type_str(FILE *f, struct type *type) _type_list_str(f, type->t0); fprintf(f, ")"); break; - - case TYPE_CONSTRUCT: - fprintf(f, "%s![", type->id); - _type_list_str(f, type->t0); - fprintf(f, "]"); - break; } } -const char *type_str(struct type *t) +char *type_str(struct type *t) { if (!t) return strdup("NULL"); diff --git a/src/lexer.l b/src/lexer.l index 24bda93..4f6598a 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -38,8 +38,7 @@ INT {HEX}|{DEC}|{OCT}|{BIN} HEXF [+-]?0[xX][0-9a-fA-F]+([pP][+-]?[0-9]+) DECF [+-]?[0-9]+[.]([eE]?[+-]?[0-9]+)?[fF]? -ID [_a-zA-Z][_a-zA-Z0-9]* -APPLY {ID}! +ID ((<>)|[_a-zA-Z])[_a-zA-Z0-9]* STRING \"(\\.|[^"\\])*\" @@ -64,7 +63,6 @@ STRING \"(\\.|[^"\\])*\" \n {} } -"::" {return SCOPE;} "(" {return LPAREN;} ")" {return RPAREN;} "{" {return LBRACE;} @@ -126,6 +124,7 @@ STRING \"(\\.|[^"\\])*\" "<<" {return LSHIFT;} ">>" {return RSHIFT;} +"as" {return AS;} "if" {return IF;} "else" {return ELSE;} "nil" {return NIL;} @@ -138,9 +137,9 @@ STRING \"(\\.|[^"\\])*\" "mut" {return MUT;} +"sizeof" {return SIZEOF;} + {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 = strdup(yytext); return STRING; } @@ -155,16 +154,6 @@ STRING \"(\\.|[^"\\])*\" return ID; } -{APPLY} { - /* strip trailing '!' */ - char *s = yytext + strlen(yytext); - s[-1] = '\0'; - - yylval->str = strdup(yytext); - return APPLY; -} - - [[:space:]]+ {/* skip whitespace */} . { diff --git a/src/parser.y b/src/parser.y index 6ec2eb2..023c10a 100644 --- a/src/parser.y +++ b/src/parser.y @@ -36,7 +36,6 @@ %token <dbl> FLOAT %token <str> STRING %token <str> ID -%token <str> APPLY %token QUESTION "?" %token SQUOTE "'" @@ -70,6 +69,7 @@ %token RBRACE "}" %token LBRACKET "[" %token RBRACKET "]" +%token AS "as" %token IF "if" %token ELSE "else" %token NIL "nil" @@ -79,7 +79,6 @@ %token IMPORT "import" %token ERROR "error" %token DOT "." -%token SCOPE "::" %token FATARROW "=>" %right "[" "]" @@ -95,19 +94,26 @@ %left "*" "/" "%" %left "as" "sizeof" %right "'" "!" "~" +%right ".." %left "." "=>" "(" ")" -%left "::" -%nterm <node> top unit proc proc_decl call closure var expr statement body -%nterm <node> vars exprs statements closures trailing_closure -%nterm <node> opt_vars opt_exprs opt_statements opt_trailing_closure -%nterm <node> rev_vars rev_exprs rev_closures rev_statements +%nterm <node> top unit proc proc_decl call closure expr statement body +%nterm <node> exprs statements closures trailing_closure +%nterm <node> opt_exprs opt_statements opt_trailing_closure +%nterm <node> rev_exprs rev_closures rev_statements %nterm <node> let if nil unop binop construct import +%nterm <node> opt_vars vars rev_vars var +%nterm <node> opt_params params rev_params param + %nterm <node> struct struct_cont trait %nterm <node> type_param type_params opt_type_params %nterm <node> behaviour behaviours opt_behaviours %nterm <node> member members opt_members +%nterm <node> opt_elements elements rev_elements element +%nterm <node> instance template supertemplate +%nterm <node> opt_constructions constructions construction +%nterm <node> id impl forget assign %nterm <type> type rev_types types opt_types @@ -189,12 +195,29 @@ static char *strip(const char *s); %start input; %% -var +param : type ID { $$ = gen_var($2, $1, src_loc(@$)); } +rev_params + : rev_params "," param { $$ = $3; $$->n = $1; } + | rev_params "|" param { $$ = $3; $$->n = $1; opt_group($1, $3); } + | param + +params + : rev_params { $$ = reverse_ast_list($1); } + | rev_params "," { $$ = reverse_ast_list($1); } + +opt_params + : params + | {$$ = NULL;} + + +var + : param + | ID { $$ = gen_var($1, NULL, src_loc(@$)); } + rev_vars : rev_vars "," var { $$ = $3; $$->n = $1; } - | rev_vars "|" var { $$ = $3; $$->n = $1; opt_group($1, $3); } | var vars @@ -206,13 +229,13 @@ opt_vars | {$$ = NULL;} proc - : ID "(" opt_vars ")" body { - $$ = gen_proc($[ID], $[opt_vars], NULL, $[body], src_loc(@$)); + : ID "(" opt_params ")" body { + $$ = gen_proc($[ID], $[opt_params], NULL, $[body], src_loc(@$)); } proc_decl - : ID "(" opt_vars ")" ";" { - $$ = gen_proc($[ID], $[opt_vars], NULL, NULL, src_loc(@$)); + : ID "(" opt_params ")" ";" { + $$ = gen_proc($[ID], $[opt_params], NULL, NULL, src_loc(@$)); } binop @@ -229,17 +252,20 @@ binop | expr ">=" expr { $$ = gen_comparison(AST_GE, $1, $3, src_loc(@$)); } | expr "!=" expr { $$ = gen_comparison(AST_NE, $1, $3, src_loc(@$)); } | expr "==" expr { $$ = gen_comparison(AST_EQ, $1, $3, src_loc(@$)); } + | id "~" id { $$ = gen_paste($1, $3, src_loc(@$)); } unop : "-" expr { $$ = gen_unop(AST_NEG, $2, src_loc(@$)); } - | "!" expr { $$ = gen_unop(AST_LNOT, $2, src_loc(@$)); } + | "!" expr { $$ = gen_unop(AST_LNOT, $2, src_loc(@$)); } + | "~" expr { $$ = gen_unop(AST_BNEG, $2, src_loc(@$)); } type - : ID { $$ = tgen_id($[ID], src_loc(@$)); } - | APPLY "[" opt_types "]" { - $$ = tgen_construct($[APPLY], $[opt_types], src_loc(@$)); - } + : ID { $$ = tgen_id($1, src_loc(@$)); } | "(" opt_types ")" { $$ = tgen_closure($2, src_loc(@$)); } + | "[" "]" type { + /* TODO static sizes? */ + $$ = tgen_arr($3, NULL, src_loc(@$)); + } | "&&" type { if ($2->k == TYPE_CLOSURE) { $$ = $2; $$->k = TYPE_PURE_CLOSURE; @@ -276,26 +302,43 @@ opt_types : types | { $$ = NULL; } -construct - : APPLY "{" opt_exprs "}" { - $$ = gen_init($[APPLY], NULL, $[opt_exprs], src_loc(@$)); +construction + : expr "=>" ID { + $$ = gen_construction($3, $1, src_loc(@$)); } - | APPLY "[" opt_types "]" "{" opt_exprs "}" { - $$ = gen_init($[APPLY], $[opt_types], $[opt_exprs], src_loc(@$)); + +constructions + : constructions "," construction { $$ = $3; $$->n = $1; } + | construction + +opt_constructions + : constructions + | { $$ = NULL; } + +construct + : "(" opt_constructions ")" ID { + $$ = gen_construct($4, $2, src_loc(@$)); } +id + : ID { $$ = gen_id($1, src_loc(@$)); } + expr - : expr "." ID { $$ = gen_dot($3, $1, src_loc(@$)); } + : "sizeof" "(" type ")" { $$ = gen_sizeof($3, src_loc(@$)); } + | expr "." ID { $$ = gen_dot($3, $1, src_loc(@$)); } + | expr "[" expr "]" { $$ = gen_arr($1, $3, src_loc(@$)); } | STRING { $$ = gen_const_str(strip($1), src_loc(@$)); } | FLOAT { $$ = gen_const_float($1, src_loc(@$)); } | BOOL { $$ = gen_const_bool($1, src_loc(@$)); } | CHAR { $$ = gen_const_char($1, src_loc(@$)); } | INT { $$ = gen_const_int($1, src_loc(@$)); } - | ID { $$ = gen_id($1, src_loc(@$)); } + | "*" { $$ = gen_nil(src_loc(@$)); } | "(" expr ")" { $$ = $2; } | expr "&" { $$ = gen_ref($1, src_loc(@$)); } | expr "*" { $$ = gen_deref($1, src_loc(@$)); } + | expr "as" type { $$ = gen_as($1, $3, src_loc(@$)); } | construct + | id | binop | unop @@ -369,12 +412,24 @@ if | "if" expr body "else" if { $$ = gen_if($2, $3, $5, src_loc(@$)); } nil - : "nil" ID body "=>" var { - $$ = gen_nil($2, $3, $5, src_loc(@$)); + : "nil" expr body "=>" var ";" { + $$ = gen_nil_check($2, $3, $5, src_loc(@$)); + } + +forget + : "nil" ID ";" { + $$ = gen_forget($2, src_loc(@$)); + } + +assign + : expr "=" expr ";" { + $$ = gen_assign($1, $3, src_loc(@$)); } statement : call + | forget + | assign | body | let | nil @@ -398,9 +453,9 @@ body } behaviour - : APPLY ";" { $$ = gen_trait_apply($1, src_loc(@$)); } - | proc_decl ";" + : proc_decl | proc + | impl behaviours : behaviours behaviour { $$ = $1; $1->n = $2; } @@ -410,16 +465,18 @@ opt_behaviours : behaviours | { $$ = NULL; } +impl + : ID "!" { $$ = gen_implements($1, src_loc(@$)); } + member - : var ";" - | behaviour + : param members - : members member { $$ = $1; $1->n = $2; } + : members ";" member { $$ = $3; $$->n = $1; } | member opt_members - : members + : members ";" | { $$ = NULL; } type_param @@ -438,19 +495,53 @@ opt_type_params : type_params | { $$ = NULL; } +element + : proc + | struct + | impl + | var ";" + +rev_elements + : rev_elements element { $$ = $2; $$->n = $1; } + | element + +elements + : rev_elements { $$ = reverse_ast_list($1); } + +opt_elements + : elements + | { $$ = NULL; } + +template + : ID "[" opt_type_params "]" "(" opt_vars ")" "{" opt_elements "}" { + $$ = gen_template($1, $3, $6, $9, src_loc(@$)); + } + +supertemplate + : ID "[" opt_type_params "]" "(" opt_vars ")" "=" + id "[" opt_types "]" "(" opt_exprs ")" "{" opt_elements "}" { + struct ast *pseudoinst = gen_instance(NULL, $9, $11, $14, src_loc(@$)); + $$ = gen_supertemplate($1, $3, $6, pseudoinst, $17, src_loc(@$)); + } + struct - : ID "[" opt_type_params "]" "{" opt_members "}" { - $$ = gen_struct($1, $3, $6, src_loc(@$)); + : ID "{" opt_members "}" { + $$ = gen_struct($1, $3, src_loc(@$)); } struct_cont - : "continue" ID "[" opt_type_params "]" "{" opt_behaviours "}" { - $$ = gen_struct_cont($2, $4, $7, src_loc(@$)); + : "continue" ID "{" opt_behaviours "}" { + $$ = gen_instance_cont($2, $4, src_loc(@$)); } trait - : ID "{" opt_behaviours "}" { - $$ = gen_trait($1, $3, src_loc(@$)); + : ID "=" "{" opt_behaviours "}" { + $$ = gen_trait($1, $4, src_loc(@$)); + } + +instance + : ID "=" id "[" opt_types "]" "(" opt_exprs ")" { + $$ = gen_instance($1, $3, $5, $8, src_loc(@$)); } import @@ -464,11 +555,17 @@ top | struct_cont | import | trait - | "pub" proc { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } - | "pub" struct { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } - | "pub" struct_cont { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } - | "pub" import { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } - | "pub" trait { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } + | instance + | template + | supertemplate + | "pub" proc { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } + | "pub" struct { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } + | "pub" struct_cont { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } + | "pub" import { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } + | "pub" trait { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } + | "pub" instance { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } + | "pub" template { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } + | "pub" supertemplate { $$ = $2; ast_set_flags($$, AST_FLAG_PUBLIC); } | error { $$ = gen_empty(src_loc(@$)); parser->failed = true; diff --git a/src/rewrite.c b/src/rewrite.c new file mode 100644 index 0000000..e3cea01 --- /dev/null +++ b/src/rewrite.c @@ -0,0 +1,96 @@ +#include <fwd/rewrite.h> +#include <stddef.h> +#include <stdlib.h> + +struct type_helper { + char *orig; + char *new; +}; + +/* types are simple enough (for now) that this works fine */ +static int rewrite_type_ids(struct type *type, char *orig, char *new) +{ + if (type->k == TYPE_ID && strcmp(type->id, orig) == 0) { + char *r = strdup(new); + free(type->id); + type->id = r; + } + + if (type->t0 && rewrite_type_ids(type->t0, orig, new)) + return -1; + + if (type->n && rewrite_type_ids(type->n, orig, new)) + return -1; + + return 0; +} + +static int rewrite_type_visit(struct ast *node, struct type_helper *helper) +{ + if (node->t2) + return rewrite_type_ids(node->t2, helper->orig, helper->new); + + return 0; +} + +int rewrite_types(struct ast *node, char *orig, char *new) +{ + struct type_helper helper = {.orig = orig, .new = new}; + return ast_visit((ast_callback_t)rewrite_type_visit, NULL, node, &helper); +} + +/* not the fastest thing in the world but should work well enough for now */ +static char *rewrite_hole(char *old, char *new) +{ + /* skip "<>" */ + size_t on = strlen(old) - 2; + size_t nn = strlen(new); + + /* +1 for null terminator */ + char *r = malloc(on + nn + 1); + + memcpy(r, new, nn); + + /* +2 to skip "<>", +1 for null terminator */ + memcpy(r + nn, old + 2, on + 1); + return r; +} + +static int rewrite_type_holes(struct type *type, char *new) +{ + if (type->id && strncmp(type->id, "<>", 2) == 0) { + char *r = rewrite_hole(type->id, new); + free(type->id); + type->id = r; + } + + if (type->t0 && rewrite_type_holes(type->t0, new)) + return -1; + + if (type->n && rewrite_type_holes(type->n, new)) + return -1; + + return 0; +} + +static int rewrite_holes_visit(struct ast *node, char *new) +{ + if (node->k == AST_CONST_STR) + return 0; + + if (node->s && strncmp(node->s, "<>", 2) == 0) { + char *r = rewrite_hole(node->s, new); + free(node->s); + node->s = r; + } + + if (node->t2 && rewrite_type_holes(node->t2, new)) + return -1; + + return 0; +} + +int rewrite_holes(struct ast *node, char *new) +{ + return ast_visit((ast_callback_t)rewrite_holes_visit, NULL, node, new); +} diff --git a/src/scope.c b/src/scope.c index 485b3a6..e49b828 100644 --- a/src/scope.c +++ b/src/scope.c @@ -30,8 +30,9 @@ struct scope *create_scope() scope->number = counter++; scope->symbols = visible_create(16); /* arbitrary number */ - scope->traits = visible_create(1); - scope->types = visible_create(1); + scope->templates = visible_create(0); + scope->traits = visible_create(0); + scope->types = visible_create(0); scope->mods = mod_vec_create(0); return scope; } @@ -47,12 +48,13 @@ void destroy_scope(struct scope *scope) } foreach(mod_vec, m, &scope->mods) { - dlclose(m); + dlclose(*m); } mod_vec_destroy(&scope->mods); visible_destroy(&scope->symbols); + visible_destroy(&scope->templates); visible_destroy(&scope->traits); visible_destroy(&scope->types); @@ -108,7 +110,7 @@ struct ast *file_scope_find_symbol(struct scope *scope, char *id) int scope_add_type(struct scope *scope, struct ast *type) { assert(type->k == AST_STRUCT_DEF); - struct ast **exists = visible_insert(&scope->symbols, type->s, type); + struct ast **exists = visible_insert(&scope->types, type->s, type); if (!exists) { internal_error("failed inserting type into scope"); return -1; @@ -123,42 +125,6 @@ int scope_add_type(struct scope *scope, struct ast *type) return 0; } -int scope_add_chain(struct scope *scope, struct ast *type) -{ - assert(type->k == AST_STRUCT_CONT); - struct ast *exists = file_scope_find_type(scope, type->s); - if (!exists) { - semantic_error(scope, type, "no previous definition"); - return -1; - } - - assert(exists->k == AST_STRUCT_DEF || exists->k == AST_STRUCT_CONT); - if (ast_flags(exists, AST_FLAG_PUBLIC) || !ast_flags(type, AST_FLAG_PUBLIC)) { - type->chain = exists; - struct ast **v = visible_insert(&scope->types, type->s, type); - if (!v) { - internal_error("failed inserting type into chain"); - return -1; - } - - /* overwrite root */ - if (*v != type) - *v = type; - - return 0; - } - - struct ast *next = exists->chain; - while (next->k == AST_STRUCT_CONT && !ast_flags(next, AST_FLAG_PUBLIC)) { - exists = next; - next = next->chain; - } - - exists->chain = type; - type->chain = exists; - return 0; -} - struct ast *scope_find_type(struct scope *scope, char *id) { struct ast **v = visible_find(&scope->types, id); @@ -183,7 +149,7 @@ struct ast *file_scope_find_type(struct scope *scope, char *id) int scope_add_trait(struct scope *scope, struct ast *trait) { assert(trait->k == AST_TRAIT_DEF); - struct ast **exists = visible_insert(&scope->symbols, trait->s, trait); + struct ast **exists = visible_insert(&scope->traits, trait->s, trait); if (!exists) { internal_error("failed inserting trait into scope"); return -1; @@ -197,6 +163,7 @@ int scope_add_trait(struct scope *scope, struct ast *trait) return 0; } + struct ast *scope_find_trait(struct scope *scope, char *id) { struct ast **v = visible_find(&scope->traits, id); @@ -218,6 +185,44 @@ struct ast *file_scope_find_trait(struct scope *scope, char *id) return file_scope_find_trait(scope->parent, id); } +int scope_add_template(struct scope *scope, struct ast *template) +{ + struct ast **exists = visible_insert(&scope->templates, template->s, template); + if (!exists) { + internal_error("failed inserting template into scope"); + return -1; + } + + if (*exists != template) { + semantic_error(scope, template, "template redefined"); + semantic_info(scope, *exists, "previously here"); + return -1; + } + + return 0; +} + +struct ast *scope_find_template(struct scope *scope, char *id) +{ + struct ast **v = visible_find(&scope->templates, id); + if (!v) + return NULL; + + return *v; +} + +struct ast *file_scope_find_template(struct scope *scope, char *id) +{ + if (!scope) + return NULL; + + struct ast *found = scope_find_template(scope, id); + if (found) + return found; + + return file_scope_find_template(scope->parent, id); +} + void scope_add_scope(struct scope *parent, struct scope *child) { assert(parent); |