summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2019-09-22 18:58:36 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2019-09-22 18:58:36 -0400
commit8afb5378033e60a745d83142dbde4ef32ec30fbe (patch)
tree3424ed4200720f8af51140be275328c88d83a03e
parent378208b54b0c5ac52bfbb67e4c3bff5d9b747cd7 (diff)
elif, else
-rw-r--r--parse.c76
-rw-r--r--test.toc11
-rw-r--r--types.c16
3 files changed, 84 insertions, 19 deletions
diff --git a/parse.c b/parse.c
index 7749ddc..f4408eb 100644
--- a/parse.c
+++ b/parse.c
@@ -49,13 +49,10 @@ typedef struct Type {
};
} Type;
-typedef enum {
- BLOCK_FN,
- BLOCK_EXPR
-} BlockKind;
+#define BLOCK_FLAG_FN 0x01
typedef struct Block {
- BlockKind kind;
+ uint16_t flags;
Location start;
Location end;
Array stmts;
@@ -103,7 +100,14 @@ typedef struct {
Array args; /* of Expression */
} CallExpr;
+typedef enum {
+ IFEXPR_IF,
+ IFEXPR_ELIF,
+ IFEXPR_ELSE
+} IfExprKind;
+
typedef struct {
+ IfExprKind kind;
struct Expression *cond; /* NULL = this is an else */
struct Expression *next_elif; /* next elif/else of this statement */
Block body;
@@ -424,7 +428,9 @@ static Token *expr_find_end(Parser *p, uint16_t flags, bool *is_vbs) {
brace_level--;
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 */
+ /* if there's an else/elif, the expr must continue */
+ if (!(token_is_kw(token + 1, KW_ELSE) || token_is_kw(token + 1, KW_ELIF)))
+ return token + 1; /* token afer } is end */
}
if (brace_level < 0)
return token;
@@ -571,6 +577,7 @@ static bool parse_type(Parser *p, Type *type) {
static bool parse_stmt(Parser *p, Statement *s);
static bool parse_block(Parser *p, Block *b) {
+ b->flags = 0;
Tokenizer *t = p->tokr;
Block *prev_block = p->block;
b->parent = prev_block;
@@ -648,8 +655,10 @@ static bool parse_fn_expr(Parser *p, FnExpr *f) {
return false;
}
}
- f->body.kind = BLOCK_FN;
- return parse_block(p, &f->body);
+ if (!parse_block(p, &f->body))
+ return false;
+ f->body.flags |= BLOCK_FLAG_FN;
+ return true;
}
/* parses, e.g. "(3, 5, foo)" */
@@ -735,8 +744,7 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) {
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 */
+ tokr_err(t, "Direct function calling in an expression is not supported.\nYou can wrap the function in parentheses.");
return false;
}
return true;
@@ -744,8 +752,10 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) {
case KW_IF: {
IfExpr *i = &e->if_;
e->kind = EXPR_IF;
+ i->kind = IFEXPR_IF;
t->token++;
Token *cond_end = expr_find_end(p, EXPR_CAN_END_WITH_LBRACE, NULL);
+ if (!cond_end) return false;
if (!token_is_kw(cond_end, KW_LBRACE)) {
t->token = cond_end;
tokr_err(t, "Expected { to open if body.");
@@ -754,8 +764,48 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) {
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 */
+ IfExpr *curr = i;
+ while (1) {
+ bool is_else = token_is_kw(t->token, KW_ELSE);
+ bool is_elif = token_is_kw(t->token, KW_ELIF);
+ if (!is_else && !is_elif) {
+ curr->next_elif = NULL;
+ break;
+ }
+ if (curr->kind == IFEXPR_ELSE) {
+ tokr_err(t, "You can't have more elif/elses after an else.");
+ return false;
+ }
+ Expression *next = parser_new_expr(p);
+ next->flags = 0;
+ next->kind = EXPR_IF;
+ next->where = t->token->where;
+ curr->next_elif = next;
+ IfExpr *nexti = &next->if_;
+ if (is_else) {
+ t->token++;
+ nexti->cond = NULL;
+ nexti->kind = IFEXPR_ELSE;
+ if (!parse_block(p, &nexti->body)) return false;
+ } else {
+ /* elif */
+ t->token++;
+ cond_end = expr_find_end(p, EXPR_CAN_END_WITH_LBRACE, NULL);
+ if (!cond_end) return false;
+ if (!token_is_kw(cond_end, KW_LBRACE)) {
+ t->token = cond_end;
+ tokr_err(t, "Expected { to open elif body.");
+ return false;
+ }
+ Expression *cond = parser_new_expr(p);
+ if (!parse_expr(p, cond, cond_end))
+ return false;
+ nexti->cond = cond;
+ nexti->kind = IFEXPR_ELIF;
+ if (!parse_block(p, &nexti->body)) return false;
+ }
+ curr = nexti;
+ }
return true;
}
default: break;
@@ -963,8 +1013,6 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) {
if (token_is_kw(t->token, KW_LBRACE)) {
/* it's a block */
- e->kind = EXPR_BLOCK;
- e->block.kind = BLOCK_EXPR;
if (!parse_block(p, &e->block)) return false;
if (t->token != end) {
tokr_err(t, "Expression continues after end of block."); /* TODO: improve this err message */
diff --git a/test.toc b/test.toc
index a74d6fc..31d337a 100644
--- a/test.toc
+++ b/test.toc
@@ -1,8 +1,11 @@
main @= fn() {
x := 1;
- if x {
- foo := 5;
- };
- foo := if 1 { 5 };
+ foo := if x {
+ 9
+ } elif 7-3 {
+ 5
+ } elif main {
+ 3
+ } else {7 };
};
diff --git a/types.c b/types.c
index cbcd37f..51a801f 100644
--- a/types.c
+++ b/types.c
@@ -164,7 +164,7 @@ static bool type_of_ident(Typer *tr, Location where, Identifier i, Type *t, bool
bool captured = false;
if (decl->scope != NULL)
for (Block *block = tr->block; block != decl->scope; block = block->parent) {
- if (block->kind == BLOCK_FN) {
+ if (block->flags & BLOCK_FLAG_FN) {
captured = true;
break;
}
@@ -360,6 +360,20 @@ static bool type_of_expr(Typer *tr, Expression *e) {
return false;
}
}
+ if (i->kind == IFEXPR_IF && t->kind != TYPE_VOID) {
+ /* make sure there's an else at the end of this chain */
+ bool has_else = false;
+ IfExpr *curr = i;
+ do {
+ curr = &curr->next_elif->if_;
+ if (curr->kind == IFEXPR_ELSE)
+ has_else = true;
+ } while (curr->next_elif);
+ if (!has_else) {
+ err_print(e->where, "non-void if block with no else");
+ return false;
+ }
+ }
} break;
case EXPR_CALL: {
CallExpr *c = &e->call;