aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/fib.fwd4
-rw-r--r--examples/vec.fwd51
-rw-r--r--include/fwd/ast.h163
-rw-r--r--include/fwd/debug.h5
-rw-r--r--include/fwd/mod.h4
-rw-r--r--include/fwd/rewrite.h9
-rw-r--r--include/fwd/scope.h7
-rw-r--r--mod/Makefile8
-rw-r--r--mod/io.c8
-rw-r--r--mod/mem.c38
-rw-r--r--mod/util.c20
-rw-r--r--src/analyze.c524
-rw-r--r--src/ast.c51
-rw-r--r--src/debug.c39
-rw-r--r--src/lexer.l19
-rw-r--r--src/parser.y185
-rw-r--r--src/rewrite.c96
-rw-r--r--src/scope.c87
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
diff --git a/mod/io.c b/mod/io.c
index 380566f..d376d1c 100644
--- a/mod/io.c
+++ b/mod/io.c
@@ -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();
}
diff --git a/src/ast.c b/src/ast.c
index 73599cb..f8b3447 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -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);