aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2023-11-19 18:41:55 +0200
committerKimplul <kimi.h.kuparinen@gmail.com>2023-11-19 18:41:55 +0200
commit8f754f8b13e55e4bb92d72f105c5aaaba1754b7d (patch)
treeb70b813c091691d6645f8cb82295f41f24f3be93
parent5304dbcf4df1fc211b5099f4d6eb7f23dfa8c454 (diff)
downloadek-8f754f8b13e55e4bb92d72f105c5aaaba1754b7d.tar.gz
ek-8f754f8b13e55e4bb92d72f105c5aaaba1754b7d.zip
start working on instruction lowering
-rw-r--r--include/ek/ast.h13
-rw-r--r--include/ek/compiler.h2
-rw-r--r--include/ek/ops.h14
-rw-r--r--include/ek/scope.h3
-rw-r--r--src/actualize.c296
-rw-r--r--src/ast.c18
-rw-r--r--src/compiler.c33
-rw-r--r--src/debug.c7
-rw-r--r--src/main.c36
-rw-r--r--src/ops.c395
-rw-r--r--src/parser.y19
-rw-r--r--src/scope.c42
-rw-r--r--tests/blocks.ek8
-rw-r--r--tests/pointer_literal.ek5
-rw-r--r--tests/rvalue.ek5
15 files changed, 628 insertions, 268 deletions
diff --git a/include/ek/ast.h b/include/ek/ast.h
index 66acc08..d36860f 100644
--- a/include/ek/ast.h
+++ b/include/ek/ast.h
@@ -4,6 +4,8 @@
#ifndef AST_H
#define AST_H
+#include <stddef.h>
+
/**
* @file ast.h
*
@@ -11,15 +13,20 @@
*/
#define AST_ID(x) x->_id
+#define AST_AS(x) x->_as
+#define AST_DOT(x) x->_dot
+#define AST_UNOP(x) x->_unop
#define AST_IMPORT(x) x->_import
#define AST_ALIAS(x) x->_alias
#define AST_TRAIT(x) x->_trait
+#define AST_CAST(x) x->_cast
#define AST_PROC(x) x->_proc
#define AST_VAR(x) x->_var
#define AST_STRUCT(x) x->_struct
#define AST_ENUM(x) x->_enum
#define AST_CALL(x) x->_call
#define AST_CONST(x) x->_const
+#define AST_ASSIGN(x) x->_assign
#define AST_BLOCK(x) x->_block
#define AST_ARR_ACCESS(x) x->_arr_access
#define AST_MACRO_CONSTRUCT(x) x->_macro_construct
@@ -694,8 +701,6 @@ struct ast_const {
long long integer;
/** String. */
const char *str;
- /** Float. */
- double dbl;
};
};
@@ -742,6 +747,8 @@ struct ast_node {
/** Ek type. */
struct ast_node *type;
+ size_t reg;
+
/** Data relevant to kind. */
union {
struct ast_arr_access _arr_access;
@@ -1052,7 +1059,7 @@ struct ast_node *gen_lambda(struct ast_node *captures,
* @return Corresponding AST node.
*/
struct ast_node *gen_proc(struct ast_node *id, struct ast_node *type,
- struct ast_node *body);
+ struct ast_node *body, struct src_loc loc);
/**
* Generate dot operation.
diff --git a/include/ek/compiler.h b/include/ek/compiler.h
index 32fc218..95bd836 100644
--- a/include/ek/compiler.h
+++ b/include/ek/compiler.h
@@ -20,7 +20,7 @@
* @param file Root file to compile.
* @return \c 0 if compilation was succesful, otherwise some non-zero value.
*/
-int compile(const char *file);
+int compile(const char *input, const char *output);
/**
* Process a file, i.e. lex, parse and generate raw AST.
diff --git a/include/ek/ops.h b/include/ek/ops.h
new file mode 100644
index 0000000..330de0c
--- /dev/null
+++ b/include/ek/ops.h
@@ -0,0 +1,14 @@
+#ifndef EK_OPS_H
+#define EK_OPS_H
+
+#include <ek/ast.h>
+
+struct ops;
+
+struct ops *create_ops();
+void destroy_ops(struct ops *ops);
+int lower_ops(struct scope *root, struct ops *ops);
+int analyze_lifetime(struct ops *ops);
+int print_asm(struct ops *ops, const char *output);
+
+#endif /* EK_OPS_H */
diff --git a/include/ek/scope.h b/include/ek/scope.h
index 156788a..10b0aad 100644
--- a/include/ek/scope.h
+++ b/include/ek/scope.h
@@ -614,6 +614,9 @@ struct ast_node *scope_resolve_call(struct scope *scope, struct ast_node *call);
struct ast_node *file_scope_resolve_call(struct scope *scope,
struct ast_node *call);
+struct ast_node *file_scope_resolve_macro(struct scope *scope,
+ struct ast_node *macro);
+
/**
* Check if \p arg_type implements \p param_type.
*
diff --git a/src/actualize.c b/src/actualize.c
index aa4d409..ab72589 100644
--- a/src/actualize.c
+++ b/src/actualize.c
@@ -26,9 +26,7 @@ struct act_stack {
enum act_flags {
ACT_IN_LOOP = (1 << 0),
ACT_IN_SWITCH = (1 << 1),
- ACT_ONLY_TYPES = (1 << 2),
- ACT_HAS_RETURN = (1 << 3),
- ACT_REQUIRE_FULLY_QUALIFIED = (1 << 4),
+ ACT_HAS_RETURN = (1 << 2),
};
struct act_state {
@@ -42,6 +40,21 @@ struct act_state {
struct act_stack *label_stack;
};
+static int is_lvalue(struct ast_node *n)
+{
+ if (n->node_type == AST_ARR_ACCESS)
+ return 1;
+
+ if (n->node_type == AST_ID)
+ return 1;
+
+ if (n->node_type == AST_UNOP && AST_UNOP(n).op == AST_DEREF)
+ return 1;
+
+ return 0;
+}
+
+
static void act_set_flags(struct act_state *state, enum act_flags flags)
{
state->flags |= flags;
@@ -65,7 +78,7 @@ static int is_void(struct ast_node *type)
static struct ast_node *void_type()
{
struct ast_node *v = gen_primitive(AST_VOID, NULL_LOC());
- ast_set_flags(v, AST_FLAG_ACTUAL);
+ ast_set_flags(v, AST_FLAG_INIT | AST_FLAG_ACTUAL);
v->type = v;
return v;
}
@@ -73,7 +86,7 @@ static struct ast_node *void_type()
static struct ast_node *i27_type()
{
struct ast_node *a = gen_primitive(AST_I27, NULL_LOC());
- ast_set_flags(a, AST_FLAG_ACTUAL);
+ ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL);
a->type = a;
return a;
}
@@ -189,7 +202,6 @@ static void clear_labels(struct act_state *state, struct act_stack *to)
static void destroy_act_state(struct act_state *state)
{
- /* clears all defers */
clear_defers(state, NULL);
clear_labels(state, NULL);
clear_gotos(state, NULL);
@@ -442,11 +454,7 @@ static int _replace_id(struct ast_node *node, void *data)
clone->next = node->next;
clone->scope = node->scope;
- /* we know this is an ID node, so we know exactly what to free */
- free((void *)node->_id.id);
-
*node = *clone;
- free(clone);
/* a succesful replacement needs no futher replacements, I think */
return 0;
@@ -504,50 +512,6 @@ static int actualize_proc_call(struct act_state *state,
struct scope *scope, struct ast_node *call,
struct ast_node *proc)
{
- /* proc has already been actualized, so we don't have to do anything */
- if (ast_flags(proc, AST_FLAG_ACTUAL)) {
- /* call node type is the return type */
- struct ast_node *sign = proc->type;
- assert(sign->node_type == AST_TYPE);
-
- call->type = AST_SIGN_TYPE(sign).ret;
- return 0;
- }
-
- if (act_flags(state, ACT_ONLY_TYPES)) {
- /* TODO: better cleanup */
- /* at this point we're really only interested in the return
- * type, but if it's a typeof expression we need to actualize
- * the arguments as well, unfortunately */
- struct ast_node *sign = clone_ast_node(proc->_proc.sign);
- if (!sign) {
- internal_error("failed cloning proc signature");
- return -1;
- }
-
- struct scope *tmp = create_scope();
- if (!tmp) {
- internal_error(
- "failed creating temporary signature scope");
- return -1;
- }
-
- /* if the function is in some completely different file for
- * example, we want to use that file's scope */
- scope_add_scope(proc->scope, tmp);
-
- struct ast_node *params = AST_SIGN_TYPE(sign).params;
- struct ast_node *args = call->_call.args;
- /* fuck, analyze_proc gobbles up the return type typeof */
- actualize_trait_types(params, args);
-
- if (actualize(state, tmp, sign))
- return -1;
-
- call->type = AST_SIGN_TYPE(sign).ret;
- return 0;
- }
-
/* clone procedure definition to
* replace trait types with actual types and actualize it */
struct ast_node *def = clone_ast_node(proc);
@@ -557,9 +521,9 @@ static int actualize_proc_call(struct act_state *state,
return -1;
}
- struct ast_node *sign = def->_proc.sign;
+ struct ast_node *sign = AST_PROC(def).sign;
struct ast_node *params = AST_SIGN_TYPE(sign).params;
- struct ast_node *args = call->_call.args;
+ struct ast_node *args = AST_CALL(call).args;
actualize_trait_types(params, args);
if (actualize(state, def->scope, def))
@@ -569,11 +533,18 @@ static int actualize_proc_call(struct act_state *state,
return 0;
}
-static int actualize_macro_call(struct act_state *state,
- struct scope *scope, struct ast_node *call,
- struct ast_node *macro)
+static int actualize_macro_expand(struct act_state *state,
+ struct scope *scope, struct ast_node *macro_expand)
{
- assert(call->node_type == AST_CALL && macro->node_type == AST_MACRO_CONSTRUCT);
+ assert(macro_expand->node_type == AST_MACRO_EXPAND);
+ struct ast_node *id = AST_MACRO_EXPAND(macro_expand).id;
+ struct ast_node *macro = file_scope_resolve_macro(scope, id);
+ if (!macro) {
+ semantic_error(scope->fctx, macro_expand, "no such macro");
+ return -1;
+ }
+
+ assert(macro->node_type == AST_MACRO_CONSTRUCT);
if (ast_flags(macro, AST_FLAG_VARIADIC)) {
semantic_error(scope->fctx, macro,
"variadic macros not yet implemented");
@@ -586,11 +557,12 @@ static int actualize_macro_call(struct act_state *state,
return -1;
}
- body->scope = call->scope;
- body->next = call->next;
+ /** @todo update all macro IDs to the correct scope */
+ body->scope = macro_expand->scope;
+ body->next = macro_expand->next;
struct ast_node *param = AST_MACRO_CONSTRUCT(macro).params;
- struct ast_node *arg = AST_CALL(call).args;
+ struct ast_node *arg = AST_MACRO_EXPAND(macro_expand).args;
/* TODO: actual replacements */
while (param && arg) {
@@ -602,7 +574,7 @@ static int actualize_macro_call(struct act_state *state,
arg->next = NULL;
if (replace_id(body, param, arg)) {
- semantic_error(scope->fctx, call,
+ semantic_error(scope->fctx, macro_expand,
"failed replacing params with args");
arg->next = next_arg;
return -1;
@@ -612,18 +584,9 @@ static int actualize_macro_call(struct act_state *state,
arg = arg->next = next_arg;
}
- /* variadic macros could just replace this bit with with replacing the
- * rest of the list with the parameter? */
- if (param || arg) {
- /* TODO: internal error more like */
- semantic_error(scope->fctx, call, "uneven number of arguments");
- return -1;
- }
-
- *call = *body;
- free(body);
+ *macro_expand = *body;
/* actualize the new content */
- return actualize(state, scope, call);
+ return actualize(state, scope, macro_expand);
}
static int actualize_call(struct act_state *state,
@@ -637,12 +600,6 @@ static int actualize_call(struct act_state *state,
if (ret)
return ret;
- /* this label implements a kind of on demand signature actualization,
- * might work? */
- /* if it does, I might flesh this out into its own procedure that
- * reinserts the found proc after actualization, see if maybe that way
- * we get rid of duplicates? */
-get_callable:
struct ast_node *callable = file_scope_resolve_call(scope, call);
if (!callable) {
char *str = call_str(call);
@@ -651,56 +608,41 @@ get_callable:
return -1;
}
- if (act_flags(state, ACT_ONLY_TYPES)) {
- if (callable->node_type == AST_PROC
- && !ast_flags(callable->_proc.sign, AST_FLAG_ACTUAL)) {
- /* since we're in types only mode, this should be fine */
- if (actualize(state, callable->scope, callable))
- return -1;
-
- goto get_callable;
- }
- }
-
- if (callable->node_type == AST_PROC)
- return actualize_proc_call(state, scope, call, callable);
-
- if (callable->node_type == AST_MACRO_EXPAND)
- return actualize_macro_call(state, scope, call, callable);
-
- /* TODO: add lambdas and arrays */
- semantic_error(scope->fctx, call,
- "currently only procedures and macros are callable");
- return -1;
+ assert(callable->node_type == AST_PROC);
+ return actualize_proc_call(state, scope, call, callable);
}
static void warn_unused_labels(struct act_state *state, struct scope *scope)
{
struct act_stack *labels = state->label_stack;
- if (labels)
- do {
- struct ast_node *label = labels->node;
- if (!ast_flags(label, AST_FLAG_ACTUAL))
- semantic_warn(scope->fctx, label,
- "unused label");
+ if (!labels)
+ return;
+
+ do {
+ struct ast_node *label = labels->node;
+ if (!ast_flags(label, AST_FLAG_ACTUAL))
+ semantic_warn(scope->fctx, label,
+ "unused label");
- } while ((labels = labels->next));
+ } while ((labels = labels->next));
}
static int undefined_gotos(struct act_state *state, struct scope *scope)
{
int ret = 0;
struct act_stack *gotos = state->goto_stack;
- if (gotos)
- do {
- struct ast_node *got = gotos->node;
- if (!ast_flags(got, AST_FLAG_ACTUAL)) {
- semantic_warn(scope->fctx, got,
- "undefined label");
- ret = -1;
- }
+ if (!gotos)
+ return ret;
- } while ((gotos = gotos->next));
+ do {
+ struct ast_node *got = gotos->node;
+ if (!ast_flags(got, AST_FLAG_ACTUAL)) {
+ semantic_warn(scope->fctx, got,
+ "undefined label");
+ ret = -1;
+ }
+
+ } while ((gotos = gotos->next));
return ret;
}
@@ -714,41 +656,27 @@ static int actualize_proc(struct act_state *state,
* that's fine? */
assert(proc && proc->node_type == AST_PROC);
int ret = 0;
- /* mark the function as initialized, assume previously allocated for us */
- struct ast_node *actual = proc;
- ast_set_flags(actual, AST_FLAG_INIT);
- if (!act_flags(state, ACT_ONLY_TYPES)) {
- ret = scope_add_actual(scope, actual);
- if (ret) {
- return ret;
- }
- }
- struct ast_node *sign = actual->_proc.sign;
+ struct ast_node *actual = proc;
+ struct ast_node *sign = AST_PROC(actual).sign;
struct scope *param_scope = create_scope();
scope_add_scope(scope, param_scope);
/* procedure type is the signature */
sign->scope = param_scope;
- actual->type = sign;
-
- /* TODO: if we're actualizing the types here, how can we avoid
- * duplicates in the scope proc list? Or can we at all? */
/* actualize types in signature */
if (actualize(state, param_scope, sign))
return -1;
- if (act_flags(state, ACT_ONLY_TYPES))
- return 0;
+ actual->type = sign;
/* we don't have to maintain the same flags as the parent state, I don't
* think */
struct act_state new_state = {0};
new_state.cur_proc = actual;
- act_set_flags(&new_state, ACT_REQUIRE_FULLY_QUALIFIED);
/* actualize body */
- ret |= actualize(&new_state, sign->scope, actual->_proc.body);
+ ret |= actualize(&new_state, sign->scope, AST_PROC(actual).body);
if (!act_flags(&new_state, ACT_HAS_RETURN)) {
if (!is_void(AST_SIGN_TYPE(sign).ret)) {
semantic_error(scope->fctx, actual,
@@ -772,8 +700,8 @@ static int actualize_proc(struct act_state *state,
printf("Actualized func:\n");
dump_ast(0, actual);
+ scope_add_actual(scope, actual);
- ast_set_flags(actual, AST_FLAG_ACTUAL);
/* we have successfully actualized the procedure */
return 0;
}
@@ -964,18 +892,8 @@ static int actualize_type(struct act_state *state,
* etc. */
assert(type->node_type == AST_TYPE);
ENTER_ACT();
- act_set_flags(state, ACT_ONLY_TYPES);
- if (ast_flags(type,
- AST_FLAG_INIT) && !ast_flags(type, AST_FLAG_ACTUAL)) {
- semantic_error(scope->fctx, type, "detected type loop");
- EXIT_ACT(-1);
- }
-
- /* mark type initialize. We can detect type loops if this is set but
- * AST_FLAG_ACTUAL isn't. */
- ast_set_flags(type, AST_FLAG_INIT);
- if (actualize(state, scope, type->_type.next)) {
+ if (actualize(state, scope, AST_TYPE(type).next)) {
EXIT_ACT(-1);
}
@@ -1000,11 +918,8 @@ static int actualize_type(struct act_state *state,
break;
}
- /* actualize whatever type we have on demand, either alias or
- * trait */
- if (!ast_flags(exists, AST_FLAG_ACTUAL))
- if (actualize(state, exists->scope, exists))
- EXIT_ACT(-1);
+ if (actualize(state, exists->scope, exists))
+ EXIT_ACT(-1);
assert(AST_TYPE(type).next == NULL);
if (exists->node_type == AST_ALIAS) {
@@ -1047,8 +962,9 @@ static int actualize_type(struct act_state *state,
}
case AST_TYPE_POINTER:
- assert(type->_type.next);
- type->loc = type->_type.next->loc;
+ assert(AST_PTR_TYPE(type).base);
+ if (actualize(state, scope, AST_PTR_TYPE(type).base))
+ EXIT_ACT(-1);
break;
case AST_TYPE_SIGN: {
@@ -1082,7 +998,6 @@ static int actualize_type(struct act_state *state,
EXIT_ACT(-1);
}
- ast_set_flags(type, AST_FLAG_ACTUAL);
/* generally speaking */
EXIT_ACT(0);
}
@@ -1136,13 +1051,11 @@ static int pointer_conversion(struct ast_node *a, struct ast_node *b)
assert(a->node_type == AST_TYPE);
assert(b->node_type == AST_TYPE);
if (pointer_type(a)) {
- if (b->_type.kind != AST_TYPE_ID)
+ if (AST_TYPE(b).kind != AST_TYPE_PRIMITIVE)
return 0;
- struct ast_node *id = AST_ID_TYPE(b).id;
- /* there's gotta be a better way */
- if (strcmp(id->_id.id, "usize") == 0)
- return 1;
+ /* for now */
+ return AST_PRIMITIVE_TYPE(b).type == AST_I27;
}
return 0;
@@ -1387,7 +1300,7 @@ static int actualize_cast(struct act_state *state,
return 0;
}
- /* TODO: arrays? lambdas? */
+ /* TODO: arrays? */
char *left_type = type_str(expr);
char *right_type = type_str(type);
@@ -1420,12 +1333,6 @@ static int actualize_alias(struct act_state *state, struct scope *scope,
* aliases might be a bit cumbersome to work with. Still, this works
* well enough I suppose. */
assert(alias->node_type == AST_ALIAS);
- if (ast_flags(alias, AST_FLAG_INIT)) {
- semantic_error(scope->fctx, alias, "alias loop");
- return -1;
- }
-
- ast_set_flags(alias, AST_FLAG_INIT);
if (actualize(state, scope, AST_ALIAS(alias).type)) {
/* usually we don't want to output errors upon errors, but this
* is likely a useful message as it might show where a loop is
@@ -1449,7 +1356,6 @@ static int actualize_trait(struct act_state *state, struct scope *scope,
/* will still have to figure out how I want to inform the actualizer
* that a some_type in a some_type just means whichever type we're
* testing for */
- act_set_flags(state, ACT_ONLY_TYPES);
if (actualize(state, trait->scope, trait->_trait.body)) {
semantic_error(scope->fctx, trait,
"failed actualizing trait");
@@ -1641,14 +1547,14 @@ static int actualize_unop(struct act_state *state,
switch (node->_unop.op) {
case AST_DEREF: {
struct ast_node *type = expr->type;
- if (type->_type.kind != AST_TYPE_POINTER) {
+ if (AST_TYPE(type).kind != AST_TYPE_POINTER) {
/* TODO: or array */
semantic_error(scope->fctx, expr,
"trying to dereference something that's not a pointer");
return -1;
}
- node->type = type->_type.next;
+ node->type = AST_PTR_TYPE(type).base;
assert(node->type);
break;
}
@@ -1671,13 +1577,7 @@ static int actualize_as(struct act_state *state,
struct scope *scope, struct ast_node *as)
{
assert(as->node_type == AST_AS);
- if (!act_flags(state, ACT_ONLY_TYPES)) {
- semantic_error(scope->fctx, as,
- "as used outside of type context");
- return -1;
- }
-
- struct ast_node *type = as->_as.type;
+ struct ast_node *type = AST_AS(as).type;
if (actualize(state, scope, type))
return -1;
@@ -1689,7 +1589,6 @@ static int actualize_struct(struct act_state *state,
struct scope *scope, struct ast_node *node)
{
assert(node->node_type == AST_STRUCT);
- ast_set_flags(node, AST_FLAG_INIT);
struct ast_node *generics = node->_struct.generics;
struct scope *struct_scope = create_scope();
if (!struct_scope)
@@ -1749,7 +1648,7 @@ static int actualize_dot(struct act_state *state,
if (actualize(state, scope, expr))
return -1;
- struct ast_node *id = node->_dot.id;
+ struct ast_node *id = AST_DOT(node).id;
if (!has_members(expr->type)) {
char *tstr = type_str(expr->type);
semantic_error(scope->fctx, node, "%s does not have member %s",
@@ -1759,7 +1658,6 @@ static int actualize_dot(struct act_state *state,
}
/* TODO: actually look through stuff */
- /* TODO: figure out how to match traitd structures to actual */
return 0;
}
@@ -1771,7 +1669,6 @@ static int actualize_init(struct act_state *state,
/* TODO: how to make sure all members are initialized? */
int ret = 0;
enum act_flags old_flags = state->flags;
- act_set_flags(state, ACT_ONLY_TYPES);
ret = actualize(state, scope, node->_init.body);
state->flags = old_flags;
return ret;
@@ -1804,6 +1701,12 @@ static int actualize_assign(struct act_state *state, struct scope *scope,
return -1;
}
+ /** @todo rvalue vs lvalue? */
+ if (!is_lvalue(to)) {
+ semantic_error(scope->fctx, node, "rvalue used where lvalue required");
+ return -1;
+ }
+
node->type = to->type;
return 0;
}
@@ -1898,6 +1801,14 @@ static int actualize(struct act_state *state, struct scope *scope,
if (!node->scope)
node->scope = scope;
+ if (ast_flags(node, AST_FLAG_INIT) && !ast_flags(node, AST_FLAG_ACTUAL)) {
+ semantic_error(scope->fctx, node, "detected dependency loop");
+ return -1;
+ }
+
+ /* actualization started */
+ ast_set_flags(node, AST_FLAG_INIT);
+
switch (node->node_type) {
case AST_PROC:
/* procedure definitions only allowed at file scope */
@@ -1908,6 +1819,7 @@ static int actualize(struct act_state *state, struct scope *scope,
case AST_TRAIT: ret |= actualize_trait(state, scope, node); break;
case AST_ALIAS: ret |= actualize_alias(state, scope, node); break;
case AST_MACRO_CONSTRUCT: ret |= actualize_macro_construct(state, scope, node); break;
+ case AST_MACRO_EXPAND: ret |= actualize_macro_expand(state, scope, node); break;
case AST_CALL: ret |= actualize_call(state, scope, node); break;
case AST_BINOP: ret |= actualize_binop(state, scope, node); break;
case AST_BLOCK: ret |= actualize_block(state, scope, node); break;
@@ -1937,6 +1849,9 @@ static int actualize(struct act_state *state, struct scope *scope,
break;
}
+ /* actualization done for this node */
+ ast_set_flags(node, AST_FLAG_ACTUAL);
+
/* should it automatically handle next nodes or should it be saved to
* actualize_block or something? */
ret |= actualize(state, scope, node->next);
@@ -1957,7 +1872,7 @@ int actualize_main(struct scope *root)
error("no main");
return -1;
}
- int ret = actualize(&state, root, clone_ast_node(main));
+ int ret = actualize(&state, root, main);
destroy_act_state(&state);
return ret;
}
@@ -1972,12 +1887,10 @@ void replace_type(struct ast_node *type, struct ast_node *from,
assert(from->node_type == AST_TYPE);
assert(to->node_type == AST_TYPE);
- /* TODO: cleanup? */
if (types_match(type, from)) {
assert(type->_type.next == NULL);
struct ast_node *clone = clone_ast_node(to);
*type = *clone;
- free(clone);
return;
}
@@ -1997,16 +1910,3 @@ void replace_param_types(struct ast_node *param, struct ast_node *param_type,
param = param->next;
}
}
-
-int actualize_temp_type(struct scope *scope, struct ast_node *type)
-{
- struct act_state state;
- act_set_flags(&state, ACT_ONLY_TYPES);
- struct ast_node *next = type->next;
-
- type->next = NULL;
- int ret = actualize(&state, scope, type);
- type->next = next;
-
- return ret;
-}
diff --git a/src/ast.c b/src/ast.c
index 5187066..0fc7c8c 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -453,14 +453,14 @@ struct ast_node *gen_var(struct ast_node *id, struct ast_node *type,
}
struct ast_node *gen_proc(struct ast_node *id, struct ast_node *sign,
- struct ast_node *body)
+ struct ast_node *body, struct src_loc loc)
{
ALLOC_NODE(n, "proc");
n->node_type = AST_PROC;
- n->_proc.id = id;
- n->_proc.sign = sign;
- n->_proc.body = body;
- n->loc = id->loc;
+ AST_PROC(n).id = id;
+ AST_PROC(n).sign = sign;
+ AST_PROC(n).body = body;
+ n->loc = loc;
return n;
}
@@ -1215,7 +1215,8 @@ struct ast_node *clone_ast_node(struct ast_node *node)
case AST_PROC: new = gen_proc(clone_ast_node(node->_proc.id),
clone_ast_node(node->_proc.sign),
- clone_ast_node(node->_proc.body));
+ clone_ast_node(node->_proc.body),
+ node->loc);
break;
case AST_VAR: {
@@ -1307,9 +1308,10 @@ struct ast_node *clone_ast_node(struct ast_node *node)
break;
case AST_TYPE_SIGN:
- new = gen_type(AST_TYPE_SIGN, NULL,
+ new = gen_type(AST_TYPE_SIGN,
clone_ast_node(AST_SIGN_TYPE(node).params),
- clone_ast_node(AST_SIGN_TYPE(node).ret));
+ clone_ast_node(AST_SIGN_TYPE(node).ret),
+ NULL);
break;
}
diff --git a/src/compiler.c b/src/compiler.c
index 5ad67c3..8801c7f 100644
--- a/src/compiler.c
+++ b/src/compiler.c
@@ -22,6 +22,7 @@
#include <ek/debug.h>
#include <ek/scope.h>
#include <ek/path.h>
+#include <ek/ops.h>
#include <ek/res.h>
/**
@@ -163,18 +164,40 @@ out:
return res;
}
-int compile(const char *file) {
+int compile(const char *input, const char *output) {
int ret = -1;
struct scope *root = NULL;
- if (process_file(&root, 0, file)) {
+ if (process_file(&root, 0, input)) {
destroy_scope(root);
- error("compilation of %s stopped due to errors", file);
+ error("compilation of %s stopped due to errors", input);
return ret;
}
ret = actualize_main(root);
- /** @todo backend */
+ if (ret) {
+ destroy_scope(root);
+ destroy_ast_nodes();
+ error("compilation of %s stopped due to errors", input);
+ return ret;
+ }
+
+ struct ops *ops = create_ops();
+ ret = lower_ops(root, ops);
destroy_scope(root);
destroy_ast_nodes();
- return ret;
+
+ if (ret) {
+ destroy_ops(ops);
+ error("compilation of %s stopped due to errors", input);
+ return ret;
+ }
+
+ ret = analyze_lifetime(ops);
+ if (ret) {
+ destroy_ops(ops);
+ error("compilation of %s stopped due to errors", input);
+ return ret;
+ }
+
+ return print_asm(ops, output);
}
diff --git a/src/debug.c b/src/debug.c
index e6e2b60..b110b9b 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -192,9 +192,10 @@ static void _type_str(FILE *fp, struct ast_node *type)
assert(type->node_type == AST_TYPE);
- switch (type->_type.kind) {
+ switch (AST_TYPE(type).kind) {
case AST_TYPE_POINTER:
- fputc('\'', fp);
+ fputc('*', fp);
+ _type_str(fp, AST_PTR_TYPE(type).base);
break;
case AST_TYPE_ID: {
@@ -238,7 +239,7 @@ static void _type_str(FILE *fp, struct ast_node *type)
fprintf(fp, "NOT YET IMPLEMENTED");
}
- _type_str(fp, type->_type.next);
+ _type_str(fp, AST_TYPE(type).next);
}
char *type_str(struct ast_node *node)
diff --git a/src/main.c b/src/main.c
index 3faf5d5..1e8901a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -23,12 +23,12 @@
*/
static const char *cmdline_usage =
"ek compiler usage:\n"
- " ek [-I <dir>...] [-D <var>...] infile...\n"
- " -h Show usage (this)\n"
- " -I <dir> Add directory to import path\n"
- " -D <var> Add predefined variable\n"
- " -o Name of output\n"
- " infile Top file(s) to compile\n"
+ " ek [-I <dir>...] [-o <outfile>] infile\n"
+ " -h Show usage (this)\n"
+ " -I <dir> Add directory to import path\n"
+ " infile Top file(s) to compile\n"
+ " -o <outfile> Name of output assembly file\n"
+ " (infile minus file extension if not given)\n"
;
/** Print usage of compiler. */
@@ -50,24 +50,21 @@ static void usage()
int main(int argc, char *argv[])
{
int opt;
- while ((opt = getopt(argc, argv, "hI:D:o:")) != -1) {
+ const char *output = "e.out";
+ while ((opt = getopt(argc, argv, "hI:o:")) != -1) {
switch (opt) {
case 'o':
- error("not yet implemented");
+ output = optarg;
break;
case 'I':
add_import_path(optarg);
break;
- case 'D':
- /* TODO */
- error("not yet implemented");
- break;
-
case 'h':
usage();
exit(EXIT_SUCCESS);
+
default:
usage();
exit(EXIT_FAILURE);
@@ -75,16 +72,17 @@ int main(int argc, char *argv[])
}
if (optind >= argc) {
- error("no input files");
+ error("no input file");
usage();
exit(EXIT_FAILURE);
}
- for (int i = optind; i < argc; ++i) {
- debug("starting compilation of '%s'", argv[i]);
- if (compile(argv[i]))
- return -1;
+ if (optind != argc - 1) {
+ error("too many arguments");
+ usage();
+ exit(EXIT_FAILURE);
}
- return 0;
+ const char *input = argv[optind];
+ return compile(input, output);
}
diff --git a/src/ops.c b/src/ops.c
new file mode 100644
index 0000000..d237ae0
--- /dev/null
+++ b/src/ops.c
@@ -0,0 +1,395 @@
+#include <ek/ops.h>
+#include <ek/scope.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/* hopefully not too difficult to follow what's goind on, but to start with we
+ * assume we have an effectively infinite amount of virtual registers and we
+ * move everything down into them. Typically the top output is used as input in
+ * some other step. */
+
+enum loc_kind {
+ LOC_NONE, LOC_REG, LOC_MEM
+};
+
+struct loc {
+ enum loc_kind kind;
+ struct loc *next;
+ size_t start;
+ size_t end;
+ size_t reg;
+ long long off;
+ size_t width;
+};
+
+static void set_reg(struct loc *loc, size_t reg)
+{
+ loc->kind = LOC_REG;
+ loc->reg = reg;
+}
+
+static void set_mem(struct loc *loc, size_t reg, long long off, size_t width)
+{
+ loc->kind = LOC_MEM;
+ loc->reg = reg;
+ loc->off = off;
+ loc->width = width;
+}
+
+static size_t trivial_type_width(struct ast_node *type)
+{
+ switch (AST_TYPE(type).kind) {
+ case AST_TYPE_POINTER: return 3;
+ case AST_TYPE_PRIMITIVE: {
+ if (AST_PRIMITIVE_TYPE(type).type == AST_I27)
+ return 3;
+
+ if (AST_PRIMITIVE_TYPE(type).type == AST_I9)
+ return 1;
+
+ abort();
+ break;
+ }
+ default: abort();
+ }
+ return 3;
+}
+
+enum opcode {
+ /* small subset for now */
+ OP_LI,
+ OP_LA,
+ OP_ADD,
+ OP_ADDI,
+ OP_STT,
+ OP_LDT,
+ OP_STW,
+ OP_LDW,
+ OP_MV, /* meta op, will be realized as either load/store or register move */
+ OP_LABEL,
+ OP_COMMENT,
+};
+
+struct op {
+ enum opcode opcode;
+ struct loc inputs;
+ struct loc outputs;
+ size_t loc;
+ union {
+ long long constant;
+ const char *string;
+ };
+ struct op *next;
+};
+
+struct ops {
+ struct op *base;
+ struct op *head;
+};
+
+struct ops *create_ops()
+{
+ struct ops *ops = calloc(1, sizeof(struct ops));
+
+ struct op *op = calloc(1, sizeof(struct op));
+ op->opcode = OP_COMMENT;
+ op->string = strdup("start");
+
+ ops->base = op;
+ ops->head = op;
+ return ops;
+}
+
+void destroy_ops(struct ops *ops)
+{
+ if (!ops)
+ return;
+
+ struct op *base = ops->base;
+ while (base) {
+ struct op *prev = base;
+ base = base->next;
+ free(prev);
+ }
+
+ free(ops);
+}
+
+static size_t next_virtual_reg()
+{
+ static size_t reg = 1;
+ return reg++;
+}
+
+#define HEAD_OUTPUTS(ops) ops->head->outputs
+
+static int lower_op(struct ast_node *n, struct ops *ops);
+static struct op *op_head(struct ops *ops)
+{
+ return ops->head;
+}
+
+static struct op *append_op(struct ops *ops, enum opcode opcode)
+{
+ static size_t i = 1;
+ struct op *op = op_head(ops);
+ struct op *n = calloc(1, sizeof(struct op));
+ n->opcode = opcode;
+ op->next = n;
+ op->loc = i++;
+ ops->head = n;
+ return n;
+}
+
+static int lower_proc(struct ast_node *n, struct ops *ops)
+{
+ struct op *op = append_op(ops, OP_LABEL);
+ /** @todo name mangling */
+ struct ast_node *id = AST_PROC(n).id;
+ op->string = strdup(AST_ID(id).id);
+ return lower_op(AST_PROC(n).body, ops);
+}
+
+static int lower_block(struct ast_node *n, struct ops *ops)
+{
+ struct ast_node *b = AST_BLOCK(n).body;
+ while (b) {
+ int ret = lower_op(b, ops);
+ if (ret)
+ return ret;
+
+ b = b->next;
+ }
+
+ return 0;
+}
+
+static int lower_var(struct ast_node *n, struct ops *ops)
+{
+ struct ast_node *d = scope_find_var(n->scope, AST_VAR(n).id);
+ /* structs should be handled as well, would it be better to try and fit
+ * them into regs or just dump them on the stack to make sure we don't
+ * immediately run out of registers? */
+ struct ast_node *type = d->type;
+ if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE
+ && AST_TYPE(type).kind != AST_TYPE_POINTER) {
+ semantic_error(n->scope->fctx, n,
+ "only trivial type lowering implemented");
+ return -1;
+ }
+
+ d->reg = next_virtual_reg();
+
+ if (AST_VAR(n).init) {
+ int ret = lower_op(AST_VAR(n).init, ops);
+ if (ret)
+ return ret;
+
+ struct loc inputs = HEAD_OUTPUTS(ops);
+ /* work with only primitive types for now */
+ assert(inputs.next == NULL);
+
+ struct op *op = append_op(ops, OP_MV);
+ op->inputs = inputs;
+ set_reg(&op->outputs, d->reg);
+ }
+
+ return 0;
+}
+
+static int lower_cast(struct ast_node *n, struct ops *ops)
+{
+ /** @todo lob off extra trits or something? */
+ return lower_op(AST_CAST(n).expr, ops);
+}
+
+static int lower_const(struct ast_node *n, struct ops *ops)
+{
+ if (AST_CONST(n).kind != AST_CONST_INTEGER) {
+ semantic_error(n->scope->fctx, n, "only integer constant lowering implemented");
+ return -1;
+ }
+
+ struct op *op = append_op(ops, OP_LI);
+ op->constant = AST_CONST(n).integer;
+ set_reg(&op->outputs, next_virtual_reg());
+ return 0;
+}
+
+static int lower_assign(struct ast_node *n, struct ops *ops)
+{
+ int ret = lower_op(AST_ASSIGN(n).from, ops);
+ if (ret)
+ return ret;
+
+ struct loc from = HEAD_OUTPUTS(ops);
+
+ ret = lower_op(AST_ASSIGN(n).to, ops);
+ if (ret)
+ return ret;
+
+ struct loc to = HEAD_OUTPUTS(ops);
+ struct op *op = append_op(ops, OP_MV);
+ op->inputs = from;
+ op->outputs = to;
+ return 0;
+}
+
+static int lower_unop(struct ast_node *n, struct ops *ops)
+{
+ if (AST_UNOP(n).op != AST_DEREF) {
+ semantic_error(n->scope->fctx, n, "unop lowering not implemented");
+ return -1;
+ }
+
+ if (AST_UNOP(n).op == AST_DEREF) {
+ int ret = lower_op(AST_UNOP(n).expr, ops);
+ if (ret)
+ return ret;
+
+ struct loc *l = &HEAD_OUTPUTS(ops);
+ if (l->kind == LOC_MEM) {
+ /* load value from memory and use it as the next step
+ * location */
+ struct op *op = append_op(ops, OP_LDW);
+ op->inputs = *l;
+ /* hard coded 3 for now, pointer is three trytes */
+ set_mem(&op->outputs, next_virtual_reg(), 0, 3);
+ return 0;
+ }
+
+ /* use the value as if it was a memory location */
+ size_t w = trivial_type_width(n->type);
+ set_mem(l, l->reg, 0, w);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int lower_id(struct ast_node *n, struct ops *ops)
+{
+ struct ast_node *d = scope_find_var(n->scope, n);
+ assert(d->reg);
+
+ /* feels like a massive hack, but gives fairly readable debug output so
+ * I'll keep it for now. Eventually once I figure out what I'm doing
+ * this whole system will probably have to be rewritten anyway. */
+ struct op *op = append_op(ops, OP_COMMENT);
+ op->string = strdup(AST_ID(n).id);
+ set_reg(&op->outputs, d->reg);
+ return 0;
+}
+
+static int lower_op(struct ast_node *n, struct ops *ops)
+{
+ int ret = 0;
+ switch (n->node_type) {
+ case AST_PROC: ret = lower_proc(n, ops); break;
+ case AST_BLOCK: ret = lower_block(n, ops); break;
+ case AST_VAR: ret = lower_var(n, ops); break;
+ case AST_CAST: ret = lower_cast(n, ops); break;
+ case AST_CONST: ret = lower_const(n, ops); break;
+ case AST_ASSIGN: ret = lower_assign(n, ops); break;
+ case AST_UNOP: ret = lower_unop(n, ops); break;
+ case AST_ID: ret = lower_id(n, ops); break;
+ default:
+ semantic_error(n->scope->fctx, n, "unimplemented lowering");
+ return -1;
+ }
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void print_locs(struct loc *locs)
+{
+ if (locs->kind == LOC_NONE)
+ return;
+
+ printf(" ( ");
+
+ while (locs) {
+ if (locs->kind == LOC_MEM)
+ printf("%lld(", locs->off);
+
+ printf("r%zd", locs->reg);
+
+ if (locs->kind == LOC_MEM)
+ printf(", %zd)", locs->width);
+
+ printf(" ");
+ locs = locs->next;
+ }
+
+ printf(")");
+}
+
+static void print_op(struct op *op)
+{
+ if (!op)
+ return;
+
+ print_locs(&op->outputs);
+ putchar(' ');
+ switch (op->opcode) {
+ case OP_LABEL: printf("%s:", op->string); break;
+ case OP_COMMENT: printf("/* %s */", op->string); break;
+ case OP_LI: printf("li %lld", op->constant); break;
+ case OP_LA: printf("la %s", op->string); break;
+ case OP_ADD: printf("add"); break;
+ case OP_ADDI: printf("addi %lld", op->constant); break;
+ case OP_STT: printf("stt"); break;
+ case OP_LDT: printf("ldt"); break;
+ case OP_STW: printf("stw"); break;
+ case OP_LDW: printf("ldw"); break;
+ case OP_MV: printf("mv"); break;
+ default: printf("unimp"); break;
+ }
+ putchar(' ');
+ print_locs(&op->inputs);
+ printf("\n");
+}
+
+static void print_ops(struct ops *ops)
+{
+ if (!ops)
+ return;
+
+ struct op *base = ops->base;
+ while (base) {
+ print_op(base);
+ base = base->next;
+ }
+}
+
+int lower_ops(struct scope *root, struct ops *ops)
+{
+ for (struct actual *a = root->actuals; a; a = a->next) {
+ assert(a->node);
+ int ret = lower_op(a->node, ops);
+ if (ret)
+ return ret;
+ }
+
+ printf("Lowered ops before lifetime analysis:\n");
+ print_ops(ops);
+
+ return 0;
+}
+
+int analyze_lifetime(struct ops *ops)
+{
+ return 0;
+}
+
+/* this is probably going to balloon up, should probably move this into another
+ * file */
+int print_asm(struct ops *ops, const char *output)
+{
+ return 0;
+}
diff --git a/src/parser.y b/src/parser.y
index 433a5ab..4def5fd 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -446,10 +446,11 @@ statement
statements
: statement statements { $$ = $1; $1->next = $2; }
| statement
+ | statelet
+
body
: "{" statements "}" { $$ = gen_block($2); }
- | "{" statelet "}" { $$ = gen_block($2); }
| "{" "}" { $$ = gen_block(gen_empty()); }
references
@@ -549,11 +550,11 @@ const
func_sign
: "(" decls "=>" type ")" {
- $$ = gen_type(AST_TYPE_SIGN, NULL, $2, $4);
+ $$ = gen_type(AST_TYPE_SIGN, $2, $4, NULL);
}
- | "(" decls ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, $2, NULL); }
- | "(" decls "=>" ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, $2, NULL); }
- | "(" "=>" type ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, NULL, $3); }
+ | "(" decls ")" { $$ = gen_type(AST_TYPE_SIGN, $2, NULL, NULL); }
+ | "(" decls "=>" ")" { $$ = gen_type(AST_TYPE_SIGN, $2, NULL, NULL); }
+ | "(" "=>" type ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, $3, NULL); }
| "(" "=>" ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, NULL, NULL); }
| "(" ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, NULL, NULL); }
@@ -571,7 +572,7 @@ type
$$ = gen_type(AST_TYPE_ARR, $2, $4, NULL);
}
| "typeof" expr {
- $$ = gen_type(AST_TYPE_TYPEOF, NULL, $2, NULL);
+ $$ = gen_type(AST_TYPE_TYPEOF, $2, NULL, NULL);
}
| "const" type {
$$ = $2;
@@ -608,13 +609,13 @@ var_init
proc
: id func_sign body {
- $$ = gen_proc($1, $2, $3);
+ $$ = gen_proc($1, $2, $3, src_loc(@$));
ast_set_flags($$, $2->flags);
ast_set_flags($3, AST_FLAG_UNHYGIENIC);
}
| "extern" id func_sign {
/* todo check that we don't have a variadic function */
- $$ = gen_proc($2, $3, NULL);
+ $$ = gen_proc($2, $3, NULL, src_loc(@$));
ast_set_flags($$, AST_FLAG_EXTERN);
}
@@ -650,7 +651,7 @@ anon_struct
trait_elem
: id /* trait */
- | id func_sign { $$ = gen_proc($1, $2, NULL); } /* proc */
+ | id func_sign { $$ = gen_proc($1, $2, NULL, src_loc(@$)); } /* proc */
| var_decl /* member */
| type_expand /* type construction */
diff --git a/src/scope.c b/src/scope.c
index 369a89b..6c94aec 100644
--- a/src/scope.c
+++ b/src/scope.c
@@ -40,14 +40,9 @@ static struct param_node *find_matching_param(struct resolve_node *node,
static int traits_resolve(struct ast_node *arg_type, struct ast_node *param_type)
{
- /** @todo are more checks required? */
- return AST_TYPE(arg_type).as == AST_TRAIT_TYPE(param_type).def;
-}
-
-static int typeofs_resolve(struct ast_node *arg_type, struct ast_node *param_type)
-{
- internal_error("typeof resolve unimplemented");
- return 0;
+ /** @todo are more checks required? arg_type should already be in
+ * `as`-form*/
+ return AST_TRAIT_TYPE(arg_type).def == AST_TRAIT_TYPE(param_type).def;
}
static int types_resolve(struct ast_node *arg_type, struct ast_node *param_type)
@@ -56,11 +51,16 @@ static int types_resolve(struct ast_node *arg_type, struct ast_node *param_type)
if (!param_type)
return 1;
+ /* if arg is specifying to be matches as `as`, then do it */
+ if (AST_TYPE(arg_type).as)
+ return types_resolve(AST_TYPE(arg_type).as, param_type);
+
if (AST_TYPE(param_type).kind == AST_TYPE_TRAIT)
return traits_resolve(arg_type, param_type);
+ /* typeof is matched later */
if (AST_TYPE(param_type).kind == AST_TYPE_TYPEOF)
- return typeofs_resolve(arg_type, param_type);
+ return 1;
return types_match(arg_type, param_type);
}
@@ -110,8 +110,6 @@ static int add_next_resolve(struct scope *scope, struct ast_node *resolve,
struct resolve_node *node, struct ast_node *params)
{
assert(node);
- if (params && actualize_temp_type(scope, params))
- return -1;
/* TODO: variadics in macros? */
/* we've run out of params, check if this is a suitable node */
@@ -855,7 +853,13 @@ void scope_add_scope(struct scope *parent, struct scope *child)
static int add_actual(struct actual *actuals, struct ast_node *node)
{
- /* TODO: check that there isn't already an actual like ours */
+ if (!actuals->node) {
+ /* fill empty first element */
+ actuals->node = node;
+ return 0;
+ }
+
+ /* TODO: check that there isn't already an actual like ours? */
struct actual *actual = calloc(1, sizeof(struct actual));
if (!actual)
return -1;
@@ -876,13 +880,15 @@ static struct ast_node *find_actual(struct actual *actuals,
{
assert(node->node_type == AST_ID);
- if (actuals)
- do {
- struct ast_node *actual = actuals->node;
- if (identical_ast_nodes(0, actual->_proc.id, node))
- return actual;
+ if (!actuals)
+ return NULL;
+
+ do {
+ struct ast_node *actual = actuals->node;
+ if (identical_ast_nodes(0, actual->_proc.id, node))
+ return actual;
- } while ((actuals = actuals->next));
+ } while ((actuals = actuals->next));
return NULL;
}
diff --git a/tests/blocks.ek b/tests/blocks.ek
index bc210a2..3e598f5 100644
--- a/tests/blocks.ek
+++ b/tests/blocks.ek
@@ -1,10 +1,10 @@
main()
{
mut a = switch 1 {
- case 2: 2
- case 1: 1
- default: 20; 20
+ case 2: 2;
+ case 1: 1;
+ default: 20; 20;
};
- mut c = ({2 + 2; 4 + 4});
+ mut c = {2 + 2; 4 + 4};
}
diff --git a/tests/pointer_literal.ek b/tests/pointer_literal.ek
new file mode 100644
index 0000000..09889d2
--- /dev/null
+++ b/tests/pointer_literal.ek
@@ -0,0 +1,5 @@
+main()
+{
+ *i9 p = 19683 as *i9;
+ *p = 'A' as i9;
+}
diff --git a/tests/rvalue.ek b/tests/rvalue.ek
new file mode 100644
index 0000000..c55f14b
--- /dev/null
+++ b/tests/rvalue.ek
@@ -0,0 +1,5 @@
+main()
+{
+ /* should parse, but give an error about lvalue vs rvalue */
+ 20 = 20;
+}