diff options
author | Leo Tenenbaum <pommicket@gmail.com> | 2019-09-22 15:30:27 -0400 |
---|---|---|
committer | Leo Tenenbaum <pommicket@gmail.com> | 2019-09-22 15:30:27 -0400 |
commit | 378208b54b0c5ac52bfbb67e4c3bff5d9b747cd7 (patch) | |
tree | 29f53b776a4eefc78110a2315678c086238c0ae2 | |
parent | 7e6069c7a137334fed51d1ef8ae2942d941818ad (diff) |
added if
-rw-r--r-- | eval.c | 8 | ||||
-rw-r--r-- | main.c | 1 | ||||
-rw-r--r-- | parse.c | 76 | ||||
-rw-r--r-- | test.toc | 12 | ||||
-rw-r--r-- | tokenizer.c | 7 | ||||
-rw-r--r-- | types.c | 55 |
6 files changed, 133 insertions, 26 deletions
@@ -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; @@ -1,6 +1,5 @@ /* TODO: -don't allow nested functions to capture outer variables (constants are allowed though) if, else re-do cgen */ @@ -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); @@ -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"}; @@ -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) { |