aboutsummaryrefslogtreecommitdiff
path: root/src/checker/statements.cpp
diff options
context:
space:
mode:
authorgingerBill <ginger.bill.22@gmail.com>2016-07-24 22:06:58 +0100
committergingerBill <ginger.bill.22@gmail.com>2016-07-24 22:06:58 +0100
commit9d8355d3612e65a5764640fb972bc4ef9f013088 (patch)
tree7d9d9a4c43ba7cf9c952d4f5c0d8e244612e36c0 /src/checker/statements.cpp
parent3fe7fc344d7d17a571a01e531db4a0e5ff057c9f (diff)
Branch Statements, if init statement, File parsing errors
Diffstat (limited to 'src/checker/statements.cpp')
-rw-r--r--src/checker/statements.cpp106
1 files changed, 75 insertions, 31 deletions
diff --git a/src/checker/statements.cpp b/src/checker/statements.cpp
index 55dba44fc..761fb03f0 100644
--- a/src/checker/statements.cpp
+++ b/src/checker/statements.cpp
@@ -1,43 +1,59 @@
// Statements and Declarations
-void check_statement(Checker *c, AstNode *node);
-
-void check_statement_list(Checker *c, AstNode *node) {
- for (; node != NULL; node = node->next)
- check_statement(c, node);
+enum StatementFlag : u32 {
+ Statement_BreakAllowed = GB_BIT(0),
+ Statement_ContinueAllowed = GB_BIT(1),
+ // Statement_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
+};
+
+void check_statement(Checker *c, AstNode *node, u32 flags);
+
+void check_statement_list(Checker *c, AstNode *node, u32 flags) {
+ for (; node != NULL; node = node->next) {
+ if (node->kind != AstNode_EmptyStatement) {
+ check_statement(c, node, flags);
+ }
+ }
}
+b32 check_is_terminating(Checker *c, AstNode *node);
+
+b32 check_is_terminating_list(Checker *c, AstNode *list) {
+ // Get to end of list
+ for (; list != NULL; list = list->next) {
+ if (list->next == NULL)
+ break;
+ }
+
+ // Iterate backwards
+ for (AstNode *n = list; n != NULL; n = n->prev) {
+ if (n->kind != AstNode_EmptyStatement)
+ return check_is_terminating(c, n);
+ }
+
+ return false;
+}
// NOTE(bill): The last expression has to be a `return` statement
// TODO(bill): This is a mild hack and should be probably handled properly
// TODO(bill): Warn/err against code after `return` that it won't be executed
b32 check_is_terminating(Checker *c, AstNode *node) {
switch (node->kind) {
- case AstNode_BlockStatement: {
- for (; node != NULL; node = node->next) {
- if (node->next == NULL)
- break;
- }
+ case AstNode_ReturnStatement:
+ return true;
- for (AstNode *n = node; node != NULL; n = n->prev) {
- if (n->kind == AstNode_EmptyStatement)
- continue;
- return check_is_terminating(c, n);
- }
- return false;
- }
+ case AstNode_BlockStatement:
+ return check_is_terminating_list(c, node->block_statement.list);
case AstNode_ExpressionStatement:
return check_is_terminating(c, node->expression_statement.expression);
- case AstNode_ReturnStatement:
- return true;
-
case AstNode_IfStatement:
if (node->if_statement.else_statement != NULL) {
if (check_is_terminating(c, node->if_statement.body) &&
- check_is_terminating(c, node->if_statement.else_statement))
+ check_is_terminating(c, node->if_statement.else_statement)) {
return true;
+ }
}
break;
@@ -353,7 +369,7 @@ void check_procedure_body(Checker *c, Token token, DeclarationInfo *decl, Type *
c->context.decl = decl;
push_procedure(c, type);
- check_statement_list(c, body->block_statement.list);
+ check_statement_list(c, body->block_statement.list, 0);
if (type->procedure.results_count > 0) {
if (!check_is_terminating(c, body)) {
error(&c->error_collector, body->block_statement.close, "Missing return statement at the end of the procedure");
@@ -492,7 +508,7 @@ void check_entity_declaration(Checker *c, Entity *e, Type *named_type) {
-void check_statement(Checker *c, AstNode *node) {
+void check_statement(Checker *c, AstNode *node, u32 flags) {
switch (node->kind) {
case AstNode_EmptyStatement: break;
case AstNode_BadStatement: break;
@@ -516,7 +532,7 @@ void check_statement(Checker *c, AstNode *node) {
case AstNode_TagStatement:
// TODO(bill): Tag Statements
error(&c->error_collector, ast_node_token(node), "Tag statements are not supported yet");
- check_statement(c, node->tag_statement.statement);
+ check_statement(c, node->tag_statement.statement, flags);
break;
case AstNode_IncDecStatement: {
@@ -631,11 +647,18 @@ void check_statement(Checker *c, AstNode *node) {
case AstNode_BlockStatement:
check_open_scope(c, node);
- check_statement_list(c, node->block_statement.list);
+ check_statement_list(c, node->block_statement.list, flags);
check_close_scope(c);
break;
case AstNode_IfStatement: {
+ check_open_scope(c, node);
+ defer (check_close_scope(c));
+ auto *is = &node->if_statement;
+
+ if (is->init != NULL)
+ check_statement(c, is->init, 0);
+
Operand operand = {Addressing_Invalid};
check_expression(c, &operand, node->if_statement.cond);
if (operand.mode != Addressing_Invalid &&
@@ -643,13 +666,14 @@ void check_statement(Checker *c, AstNode *node) {
error(&c->error_collector, ast_node_token(node->if_statement.cond),
"Non-boolean condition in `if` statement");
}
- check_statement(c, node->if_statement.body);
+
+ check_statement(c, node->if_statement.body, flags);
if (node->if_statement.else_statement) {
switch (node->if_statement.else_statement->kind) {
case AstNode_IfStatement:
case AstNode_BlockStatement:
- check_statement(c, node->if_statement.else_statement);
+ check_statement(c, node->if_statement.else_statement, flags);
break;
default:
error(&c->error_collector, ast_node_token(node->if_statement.else_statement),
@@ -686,10 +710,12 @@ void check_statement(Checker *c, AstNode *node) {
} break;
case AstNode_ForStatement: {
+ flags |= Statement_BreakAllowed | Statement_ContinueAllowed;
check_open_scope(c, node);
defer (check_close_scope(c));
- check_statement(c, node->for_statement.init);
+ if (node->for_statement.init != NULL)
+ check_statement(c, node->for_statement.init, 0);
if (node->for_statement.cond) {
Operand operand = {Addressing_Invalid};
check_expression(c, &operand, node->for_statement.cond);
@@ -699,8 +725,9 @@ void check_statement(Checker *c, AstNode *node) {
"Non-boolean condition in `for` statement");
}
}
- check_statement(c, node->for_statement.end);
- check_statement(c, node->for_statement.body);
+ if (node->for_statement.end != NULL)
+ check_statement(c, node->for_statement.end, 0);
+ check_statement(c, node->for_statement.body, flags);
} break;
case AstNode_DeferStatement: {
@@ -710,11 +737,28 @@ void check_statement(Checker *c, AstNode *node) {
} else {
b32 out_in_defer = c->in_defer;
c->in_defer = true;
- check_statement(c, ds->statement);
+ check_statement(c, ds->statement, 0);
c->in_defer = out_in_defer;
}
} break;
+ case AstNode_BranchStatement: {
+ Token token = node->branch_statement.token;
+ switch (token.kind) {
+ case Token_break:
+ if ((flags & Statement_BreakAllowed) == 0)
+ error(&c->error_collector, token, "`break` only allowed in `for` statement");
+ break;
+ case Token_continue:
+ if ((flags & Statement_ContinueAllowed) == 0)
+ error(&c->error_collector, token, "`continue` only allowed in `for` statement");
+ break;
+ default:
+ error(&c->error_collector, token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
+ break;
+ }
+ } break;
+
// Declarations
case AstNode_VariableDeclaration: {