summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2019-09-22 15:30:27 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2019-09-22 15:30:27 -0400
commit378208b54b0c5ac52bfbb67e4c3bff5d9b747cd7 (patch)
tree29f53b776a4eefc78110a2315678c086238c0ae2
parent7e6069c7a137334fed51d1ef8ae2942d941818ad (diff)
added if
-rw-r--r--eval.c8
-rw-r--r--main.c1
-rw-r--r--parse.c76
-rw-r--r--test.toc12
-rw-r--r--tokenizer.c7
-rw-r--r--types.c55
6 files changed, 133 insertions, 26 deletions
diff --git a/eval.c b/eval.c
index 8a78dd0..071ca3f 100644
--- a/eval.c
+++ b/eval.c
@@ -7,6 +7,11 @@ typedef struct Value {
};
} Value;
+/* static bool eval_truthiness(Expression *e, Value *v) { */
+/* /\* TODO *\/ */
+/* return true; */
+/* } */
+
/* NOTE: expr must be typed before it can be evaluated */
static bool eval_expr(Expression *e, Value *v) {
/* TODO: cache eval'd expression values (probably only needed for declarations) */
@@ -131,6 +136,9 @@ static bool eval_expr(Expression *e, Value *v) {
case EXPR_FN:
v->fn = *e->fn;
return true;
+ case EXPR_IF: {
+ err_print(e->where, "compile time if not supported yet."); /* TODO */
+ } break;
case EXPR_CALL:
err_print(e->where, "Compile time function calling not supported yet."); /* TODO */
break;
diff --git a/main.c b/main.c
index a963d0c..9e6fdbc 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,5 @@
/*
TODO:
-don't allow nested functions to capture outer variables (constants are allowed though)
if, else
re-do cgen
*/
diff --git a/parse.c b/parse.c
index e0f6cdc..7749ddc 100644
--- a/parse.c
+++ b/parse.c
@@ -70,6 +70,7 @@ typedef enum {
EXPR_IDENT, /* variable or constant */
EXPR_BINARY_OP,
EXPR_UNARY_OP,
+ EXPR_IF,
EXPR_FN,
EXPR_CALL,
EXPR_BLOCK,
@@ -102,6 +103,12 @@ typedef struct {
Array args; /* of Expression */
} CallExpr;
+typedef struct {
+ struct Expression *cond; /* NULL = this is an else */
+ struct Expression *next_elif; /* next elif/else of this statement */
+ Block body;
+} IfExpr;
+
#define EXPR_FLAG_FOUND_TYPE 0x01
typedef struct Expression {
@@ -125,6 +132,7 @@ typedef struct Expression {
CallExpr call;
DirectExpr direct;
Identifier ident;
+ IfExpr if_;
struct FnExpr *fn;
Block block;
};
@@ -372,6 +380,7 @@ static int op_precedence(Keyword op) {
/* TODO: check that we check which thing ends it everywhere */
#define EXPR_CAN_END_WITH_COMMA 0x01 /* a comma could end the expression */
+#define EXPR_CAN_END_WITH_LBRACE 0x02
static Token *expr_find_end(Parser *p, uint16_t flags, bool *is_vbs) {
Tokenizer *t = p->tokr;
@@ -406,12 +415,14 @@ static Token *expr_find_end(Parser *p, uint16_t flags, bool *is_vbs) {
return token;
break;
case KW_LBRACE:
+ if ((flags & EXPR_CAN_END_WITH_LBRACE) && square_level == 0 && paren_level == 0)
+ return token;
brace_level++;
could_be_vbs = true;
break;
case KW_RBRACE:
brace_level--;
- if (brace_level == 0 && could_be_vbs) {
+ if (brace_level == 0 && could_be_vbs && !token_is_kw(token + 1, KW_RPAREN)) {
if (is_vbs) *is_vbs = true;
return token + 1; /* token afer } is end */
}
@@ -714,21 +725,41 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) {
}
Token *start = t->token;
+
+ if (t->token->kind == TOKEN_KW) switch (t->token->kw) {
+ case KW_FN: {
+ /* this is a function */
+ e->kind = EXPR_FN;
+ e->fn = err_malloc(sizeof *e->fn);
+ if (!parse_fn_expr(p, e->fn))
+ return false;
- if (token_is_kw(t->token, KW_FN)) {
- /* this is a function */
- e->kind = EXPR_FN;
- e->fn = err_malloc(sizeof *e->fn);
- if (!parse_fn_expr(p, e->fn))
- return false;
-
- if (t->token != end) {
- tokr_err(t, "Direct function calling in an expression is not supported yet.\nYou can wrap the function in parentheses.");
- /* TODO */
- return false;
+ if (t->token != end) {
+ tokr_err(t, "Direct function calling in an expression is not supported yet.\nYou can wrap the function in parentheses.");
+ /* TODO */
+ return false;
+ }
+ return true;
+ }
+ case KW_IF: {
+ IfExpr *i = &e->if_;
+ e->kind = EXPR_IF;
+ t->token++;
+ Token *cond_end = expr_find_end(p, EXPR_CAN_END_WITH_LBRACE, NULL);
+ if (!token_is_kw(cond_end, KW_LBRACE)) {
+ t->token = cond_end;
+ tokr_err(t, "Expected { to open if body.");
+ return false;
+ }
+ i->cond = parser_new_expr(p);
+ if (!parse_expr(p, i->cond, cond_end)) return false;
+ if (!parse_block(p, &i->body)) return false;
+ i->next_elif = NULL;
+ /* TODO: elif/else */
+ return true;
+ }
+ default: break;
}
- return true;
- }
/* Find the lowest-precedence operator not in parentheses/braces/square brackets */
int paren_level = 0;
@@ -808,6 +839,12 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) {
if (entirely_within_parentheses) {
t->token++; /* move past opening ( */
+ if (token_is_kw(t->token, KW_RPAREN)) {
+ /* ()foo */
+ t->token--;
+ tokr_err(t, "Stray () (maybe try wrapping the stuff before this in parentheses)");
+ return false;
+ }
Token *new_end = end - 1; /* parse to ending ) */
if (!parse_expr(p, e, new_end))
return false;
@@ -1338,6 +1375,17 @@ static void fprint_expr(FILE *out, Expression *e) {
case EXPR_FN:
fprint_fn_expr(out, e->fn);
break;
+ case EXPR_IF:
+ if (e->if_.cond) {
+ fprintf(out, "(else)? if ");
+ fprint_expr(out, e->if_.cond);
+ } else {
+ fprintf(out, "else");
+ }
+ fprint_block(out, &e->if_.body);
+ if (e->if_.next_elif)
+ fprint_expr(out, e->if_.next_elif);
+ break;
case EXPR_CALL:
fprint_expr(out, e->call.fn);
fprint_args(out, &e->call.args);
diff --git a/test.toc b/test.toc
index 720c87e..a74d6fc 100644
--- a/test.toc
+++ b/test.toc
@@ -1,12 +1,8 @@
-x := 3;
-
main @= fn() {
- y := 5;
- z @= 10;
- foo @= fn() {
- bar := y;
- asdf := z;
- gfdij := x;
+ x := 1;
+ if x {
+ foo := 5;
};
+ foo := if 1 { 5 };
};
diff --git a/tokenizer.c b/tokenizer.c
index 5dd9886..87a6a95 100644
--- a/tokenizer.c
+++ b/tokenizer.c
@@ -34,6 +34,9 @@ typedef enum {
KW_AMPERSAND,
KW_SLASH,
KW_LAST_SYMBOL = KW_SLASH, /* last one entirely consisting of symbols */
+ KW_IF,
+ KW_ELIF,
+ KW_ELSE,
KW_FN,
KW_INT,
KW_I8,
@@ -52,8 +55,8 @@ typedef enum {
static const char *keywords[KW_COUNT] =
{";", "=", ":", "@", ",", "(", ")", "{", "}", "[", "]", "==", "<", "<=", "+", "-", "*",
- "&", "/", "fn", "int", "i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "float",
- "f32", "f64"};
+ "&", "/", "if", "elif", "else", "fn", "int", "i8", "i16", "i32", "i64", "u8", "u16",
+ "u32", "u64", "float", "f32", "f64"};
static const char *directives[DIRECT_COUNT] =
{"C"};
diff --git a/types.c b/types.c
index 472401d..cbcd37f 100644
--- a/types.c
+++ b/types.c
@@ -267,12 +267,35 @@ static bool type_resolve(Typer *tr, Type *t) {
return true;
}
+
+static bool type_can_be_truthy(Type *t) {
+ switch (t->kind) {
+ case TYPE_VOID:
+ return false;
+ case TYPE_UNKNOWN:
+ return true;
+ case TYPE_BUILTIN:
+ return true;
+ case TYPE_FN:
+ return true;
+ case TYPE_TUPLE:
+ return false;
+ case TYPE_ARR:
+ return false;
+ case TYPE_PTR:
+ return true;
+ }
+ assert(0);
+ return false;
+}
+
/* NOTE: this does descend into un/binary ops, calls, etc. but NOT into any functions */
static bool type_of_expr(Typer *tr, Expression *e) {
if (e->flags & EXPR_FLAG_FOUND_TYPE) return true;
Type *t = &e->type;
t->flags = 0;
t->kind = TYPE_UNKNOWN; /* default to unknown type (in the case of an error) */
+ e->flags |= EXPR_FLAG_FOUND_TYPE; /* even if failed, pretend we found the type */
switch (e->kind) {
case EXPR_FN: {
FnExpr *f = e->fn;
@@ -308,6 +331,36 @@ static bool type_of_expr(Typer *tr, Expression *e) {
case EXPR_IDENT: {
if (!type_of_ident(tr, e->where, e->ident, t, false)) return false;
} break;
+ case EXPR_IF: {
+ IfExpr *i = &e->if_;
+ if (i->cond) {
+ if (!type_of_expr(tr, i->cond))
+ return false;
+ if (!type_can_be_truthy(&i->cond->type)) {
+ char *s = type_to_str(&i->cond->type);
+ err_print(i->cond->where, "Type %s cannot be the condition of an if statement.", s);
+ free(s);
+ return false;
+ }
+ }
+ if (!types_block(tr, &i->body))
+ return false;
+ if (i->body.ret_expr)
+ *t = i->body.ret_expr->type;
+ else
+ t->kind = TYPE_VOID;
+ if (i->next_elif) {
+ if (!type_of_expr(tr, i->next_elif))
+ return false;
+ if (!type_eq(t, &i->next_elif->type)) {
+ char *this_type = type_to_str(t);
+ char *that_type = type_to_str(&i->next_elif->type);
+
+ err_print(e->where, "elif/else block of an if statement has a different type. Expected %s, but got %s.", that_type, this_type);
+ return false;
+ }
+ }
+ } break;
case EXPR_CALL: {
CallExpr *c = &e->call;
Expression *f = c->fn;
@@ -508,7 +561,6 @@ static bool type_of_expr(Typer *tr, Expression *e) {
}
} break;
}
- e->flags |= EXPR_FLAG_FOUND_TYPE;
return true;
}
@@ -529,6 +581,7 @@ static bool types_block(Typer *tr, Block *b) {
}
/* does descend into blocks, unlike type_of_expr. */
+/* TODO: you still need to descend into other things */
static bool types_expr(Typer *tr, Expression *e) {
if (!type_of_expr(tr, e)) return false;
switch (e->kind) {