aboutsummaryrefslogtreecommitdiff
path: root/src/parser.c
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2016-11-29 22:08:48 +0000
committerGinger Bill <bill@gingerbill.org>2016-11-29 22:08:48 +0000
commitb232b9d5ea23fdd4d53f8e93cdfeb1f962811331 (patch)
tree6b4fbe56bf1fc7e7929104790cfb05b42b5f4071 /src/parser.c
parent348bcc3f9a1375ddf24b952fad537b5c84e84053 (diff)
Basic `when` statement - Compile time if statement
This is similar to an #if in C but handled during the semantic checking stage.
Diffstat (limited to 'src/parser.c')
-rw-r--r--src/parser.c315
1 files changed, 209 insertions, 106 deletions
diff --git a/src/parser.c b/src/parser.c
index 79753c832..e2b046a69 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -162,6 +162,12 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
AstNode *body; \
AstNode *else_stmt; \
}) \
+ AST_NODE_KIND(WhenStmt, "when statement", struct { \
+ Token token; \
+ AstNode *cond; \
+ AstNode *body; \
+ AstNode *else_stmt; \
+ }) \
AST_NODE_KIND(ReturnStmt, "return statement", struct { \
Token token; \
AstNodeArray results; \
@@ -256,6 +262,7 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
}) \
AST_NODE_KIND(ForeignLibrary, "foreign library", struct { \
Token token, filepath; \
+ String base_dir; \
bool is_system; \
}) \
AST_NODE_KIND(_DeclEnd, "", i32) \
@@ -365,6 +372,9 @@ gb_inline bool is_ast_node_decl(AstNode *node) {
gb_inline bool is_ast_node_type(AstNode *node) {
return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1);
}
+gb_inline bool is_ast_node_when_stmt(AstNode *node) {
+ return node->kind == AstNode_WhenStmt;
+}
Token ast_node_token(AstNode *node) {
@@ -424,6 +434,8 @@ Token ast_node_token(AstNode *node) {
return node->BlockStmt.open;
case AstNode_IfStmt:
return node->IfStmt.token;
+ case AstNode_WhenStmt:
+ return node->WhenStmt.token;
case AstNode_ReturnStmt:
return node->ReturnStmt.token;
case AstNode_ForStmt:
@@ -717,6 +729,16 @@ AstNode *make_if_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, Ast
return result;
}
+AstNode *make_when_stmt(AstFile *f, Token token, AstNode *cond, AstNode *body, AstNode *else_stmt) {
+ AstNode *result = make_node(f, AstNode_WhenStmt);
+ result->WhenStmt.token = token;
+ result->WhenStmt.cond = cond;
+ result->WhenStmt.body = body;
+ result->WhenStmt.else_stmt = else_stmt;
+ return result;
+}
+
+
AstNode *make_return_stmt(AstFile *f, Token token, AstNodeArray results) {
AstNode *result = make_node(f, AstNode_ReturnStmt);
result->ReturnStmt.token = token;
@@ -1351,12 +1373,39 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
case Token_Integer:
case Token_Float:
- case Token_String:
case Token_Rune:
operand = make_basic_lit(f, f->curr_token);
next_token(f);
return operand;
+ case Token_String: {
+ Token token = f->curr_token;
+ next_token(f);
+ if (f->curr_token.kind == Token_String) {
+ // NOTE(bill): Allow neighbouring string literals to be merge together to
+ // become one big string
+ String s = f->curr_token.string;
+ Array(u8) data;
+ array_init_reserve(&data, heap_allocator(), token.string.len+s.len);
+ gb_memmove(data.e, token.string.text, token.string.len);
+ data.count += token.string.len;
+
+ while (f->curr_token.kind == Token_String) {
+ String s = f->curr_token.string;
+ isize old_count = data.count;
+ array_resize(&data, data.count + s.len);
+ gb_memmove(data.e+old_count, s.text, s.len);
+ next_token(f);
+ }
+
+ token.string = make_string(data.e, data.count);
+ array_add(&f->tokenizer.allocated_strings, token.string);
+ }
+
+ return make_basic_lit(f, token);
+ }
+
+
case Token_OpenParen: {
Token open, close;
// NOTE(bill): Skip the Paren Expression
@@ -1614,11 +1663,9 @@ AstNode *parse_unary_expr(AstFile *f, bool lhs) {
case Token_Sub:
case Token_Not:
case Token_Xor: {
- AstNode *operand;
Token op = f->curr_token;
next_token(f);
- operand = parse_unary_expr(f, lhs);
- return make_unary_expr(f, op, operand);
+ return make_unary_expr(f, op, parse_unary_expr(f, lhs));
} break;
}
@@ -1658,7 +1705,6 @@ i32 token_precedence(Token t) {
case Token_union_cast:
return 6;
}
-
return 0;
}
@@ -1669,8 +1715,9 @@ AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
AstNode *right;
Token op = f->curr_token;
i32 op_prec = token_precedence(op);
- if (op_prec != prec)
+ if (op_prec != prec) {
break;
+ }
expect_operator(f); // NOTE(bill): error checks too
if (lhs) {
// TODO(bill): error checking
@@ -1791,21 +1838,22 @@ AstNode *parse_simple_stmt(AstFile *f) {
-AstNode *parse_block_stmt(AstFile *f) {
- if (f->curr_proc == NULL) {
+AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
+ if (!is_when && f->curr_proc == NULL) {
syntax_error(f->curr_token, "You cannot use a block statement in the file scope");
return make_bad_stmt(f, f->curr_token, f->curr_token);
}
- AstNode *block_stmt = parse_body(f);
- return block_stmt;
+ return parse_body(f);
}
AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) {
- if (statement == NULL)
+ if (statement == NULL) {
return NULL;
+ }
- if (statement->kind == AstNode_ExprStmt)
+ if (statement->kind == AstNode_ExprStmt) {
return statement->ExprStmt.expr;
+ }
syntax_error(f->curr_token, "Expected `%.*s`, found a simple statement.", LIT(kind));
return make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]);
@@ -2008,27 +2056,34 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
f->expr_level++;
Token token = expect_token(f, Token_OpenBracket);
AstNode *count_expr = NULL;
+ bool is_vector = false;
if (f->curr_token.kind == Token_Ellipsis) {
count_expr = make_ellipsis(f, f->curr_token, NULL);
next_token(f);
+ } else if (f->curr_token.kind == Token_vector) {
+ next_token(f);
+ count_expr = parse_expr(f, false);
+ is_vector = true;
} else if (f->curr_token.kind != Token_CloseBracket) {
count_expr = parse_expr(f, false);
}
expect_token(f, Token_CloseBracket);
f->expr_level--;
- AstNode *e = make_array_type(f, token, count_expr, parse_type(f));
- return e;
+ if (is_vector) {
+ return make_vector_type(f, token, count_expr, parse_type(f));
+ }
+ return make_array_type(f, token, count_expr, parse_type(f));
}
- case Token_OpenBrace: {
- f->expr_level++;
- Token token = expect_token(f, Token_OpenBrace);
- AstNode *count_expr = parse_expr(f, false);
- expect_token(f, Token_CloseBrace);
- f->expr_level--;
- return make_vector_type(f, token, count_expr, parse_type(f));
- }
+ // case Token_OpenBrace: {
+ // f->expr_level++;
+ // Token token = expect_token(f, Token_OpenBrace);
+ // AstNode *count_expr = parse_expr(f, false);
+ // expect_token(f, Token_CloseBrace);
+ // f->expr_level--;
+ // return make_vector_type(f, token, count_expr, parse_type(f));
+ // }
case Token_struct: {
Token token = expect_token(f, Token_struct);
@@ -2359,7 +2414,7 @@ AstNode *parse_if_stmt(AstFile *f) {
syntax_error(f->curr_token, "Expected condition for if statement");
}
- body = parse_block_stmt(f);
+ body = parse_block_stmt(f, false);
if (allow_token(f, Token_else)) {
switch (f->curr_token.kind) {
@@ -2367,7 +2422,7 @@ AstNode *parse_if_stmt(AstFile *f) {
else_stmt = parse_if_stmt(f);
break;
case Token_OpenBrace:
- else_stmt = parse_block_stmt(f);
+ else_stmt = parse_block_stmt(f, false);
break;
default:
syntax_error(f->curr_token, "Expected if statement block statement");
@@ -2379,6 +2434,44 @@ AstNode *parse_if_stmt(AstFile *f) {
return make_if_stmt(f, token, init, cond, body, else_stmt);
}
+AstNode *parse_when_stmt(AstFile *f) {
+ Token token = expect_token(f, Token_when);
+ AstNode *cond = NULL;
+ AstNode *body = NULL;
+ AstNode *else_stmt = NULL;
+
+ isize prev_level = f->expr_level;
+ f->expr_level = -1;
+
+ cond = parse_expr(f, false);
+
+ f->expr_level = prev_level;
+
+ if (cond == NULL) {
+ syntax_error(f->curr_token, "Expected condition for when statement");
+ }
+
+ body = parse_block_stmt(f, true);
+
+ if (allow_token(f, Token_else)) {
+ switch (f->curr_token.kind) {
+ case Token_when:
+ else_stmt = parse_when_stmt(f);
+ break;
+ case Token_OpenBrace:
+ else_stmt = parse_block_stmt(f, true);
+ break;
+ default:
+ syntax_error(f->curr_token, "Expected when statement block statement");
+ else_stmt = make_bad_stmt(f, f->curr_token, f->tokens.e[f->curr_token_index+1]);
+ break;
+ }
+ }
+
+ return make_when_stmt(f, token, cond, body, else_stmt);
+}
+
+
AstNode *parse_return_stmt(AstFile *f) {
if (f->curr_proc == NULL) {
syntax_error(f->curr_token, "You cannot use a return statement in the file scope");
@@ -2436,7 +2529,7 @@ AstNode *parse_for_stmt(AstFile *f) {
}
f->expr_level = prev_level;
}
- body = parse_block_stmt(f);
+ body = parse_block_stmt(f, false);
cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
@@ -2624,6 +2717,7 @@ AstNode *parse_stmt(AstFile *f) {
// TODO(bill): other keywords
case Token_if: return parse_if_stmt(f);
+ case Token_when: return parse_when_stmt(f);
case Token_return: return parse_return_stmt(f);
case Token_for: return parse_for_stmt(f);
case Token_match: return parse_match_stmt(f);
@@ -2678,7 +2772,7 @@ AstNode *parse_stmt(AstFile *f) {
AstNode *expr = parse_expr(f, false);
f->expr_level = prev_level;
- AstNode *body = parse_block_stmt(f);
+ AstNode *body = parse_block_stmt(f, false);
return make_push_allocator(f, token, expr, body);
} break;
@@ -2689,7 +2783,7 @@ AstNode *parse_stmt(AstFile *f) {
AstNode *expr = parse_expr(f, false);
f->expr_level = prev_level;
- AstNode *body = parse_block_stmt(f);
+ AstNode *body = parse_block_stmt(f, false);
return make_push_context(f, token, expr, body);
} break;
@@ -2798,7 +2892,7 @@ AstNode *parse_stmt(AstFile *f) {
} break;
case Token_OpenBrace:
- return parse_block_stmt(f);
+ return parse_block_stmt(f, false);
case Token_Semicolon:
s = make_empty_stmt(f, token);
@@ -2965,20 +3059,20 @@ String get_fullpath_core(gbAllocator a, String path) {
return res;
}
-// NOTE(bill): Returns true if it's added
-bool try_add_foreign_library_path(Parser *p, String import_file) {
- gb_mutex_lock(&p->mutex);
+// // NOTE(bill): Returns true if it's added
+// bool try_add_foreign_library_path(Parser *p, String import_file) {
+// gb_mutex_lock(&p->mutex);
- for_array(i, p->foreign_libraries) {
- String import = p->foreign_libraries.e[i];
- if (str_eq(import, import_file)) {
- return false;
- }
- }
- array_add(&p->foreign_libraries, import_file);
- gb_mutex_unlock(&p->mutex);
- return true;
-}
+// for_array(i, p->foreign_libraries) {
+// String import = p->foreign_libraries.e[i];
+// if (str_eq(import, import_file)) {
+// return false;
+// }
+// }
+// array_add(&p->foreign_libraries, import_file);
+// gb_mutex_unlock(&p->mutex);
+// return true;
+// }
gb_global Rune illegal_import_runes[] = {
'"', '\'', '`', ' ', '\t', '\r', '\n', '\v', '\f',
@@ -3040,93 +3134,102 @@ String get_filepath_extension(String path) {
return make_string(path.text, dot);
}
-void parse_file(Parser *p, AstFile *f) {
- String filepath = f->tokenizer.fullpath;
- String base_dir = filepath;
- for (isize i = filepath.len-1; i >= 0; i--) {
- if (base_dir.text[i] == '\\' ||
- base_dir.text[i] == '/') {
+void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, AstNodeArray decls);
+
+void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstNodeWhenStmt *ws) {
+ if (ws->body != NULL && ws->body->kind == AstNode_BlockStmt) {
+ parse_setup_file_decls(p, f, base_dir, ws->body->BlockStmt.stmts);
+ }
+ if (ws->else_stmt) {
+ switch (ws->else_stmt->kind) {
+ case AstNode_BlockStmt:
+ parse_setup_file_decls(p, f, base_dir, ws->else_stmt->BlockStmt.stmts);
+ break;
+ case AstNode_WhenStmt:
+ parse_setup_file_when_stmt(p, f, base_dir, &ws->else_stmt->WhenStmt);
break;
}
- base_dir.len--;
- }
-
- while (f->curr_token.kind == Token_Comment) {
- next_token(f);
}
+}
- f->decls = parse_stmt_list(f);
+void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, AstNodeArray decls) {
+ for_array(i, decls) {
+ AstNode *node = decls.e[i];
- for_array(i, f->decls) {
- AstNode *node = f->decls.e[i];
if (!is_ast_node_decl(node) &&
+ !is_ast_node_when_stmt(node) &&
node->kind != AstNode_BadStmt &&
node->kind != AstNode_EmptyStmt) {
// NOTE(bill): Sanity check
syntax_error(ast_node_token(node), "Only declarations are allowed at file scope");
- } else {
- if (node->kind == AstNode_ImportDecl) {
- AstNodeImportDecl *id = &node->ImportDecl;
- String file_str = id->relpath.string;
-
- if (!is_import_path_valid(file_str)) {
- if (id->is_load) {
- syntax_error(ast_node_token(node), "Invalid #load path: `%.*s`", LIT(file_str));
- } else {
- syntax_error(ast_node_token(node), "Invalid #import path: `%.*s`", LIT(file_str));
- }
- // NOTE(bill): It's a naughty name
- f->decls.e[i] = make_bad_decl(f, id->token, id->token);
- continue;
+ } else if (node->kind == AstNode_WhenStmt) {
+ parse_setup_file_when_stmt(p, f, base_dir, &node->WhenStmt);
+ } else if (node->kind == AstNode_ImportDecl) {
+ AstNodeImportDecl *id = &node->ImportDecl;
+ String file_str = id->relpath.string;
+
+ if (!is_import_path_valid(file_str)) {
+ if (id->is_load) {
+ syntax_error(ast_node_token(node), "Invalid #load path: `%.*s`", LIT(file_str));
+ } else {
+ syntax_error(ast_node_token(node), "Invalid #import path: `%.*s`", LIT(file_str));
}
+ // NOTE(bill): It's a naughty name
+ decls.e[i] = make_bad_decl(f, id->token, id->token);
+ continue;
+ }
- gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator
+ gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator
- String rel_path = get_fullpath_relative(allocator, base_dir, file_str);
- String import_file = rel_path;
- if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
- String abs_path = get_fullpath_core(allocator, file_str);
- if (gb_file_exists(cast(char *)abs_path.text)) {
- import_file = abs_path;
- }
+ String rel_path = get_fullpath_relative(allocator, base_dir, file_str);
+ String import_file = rel_path;
+ if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
+ String abs_path = get_fullpath_core(allocator, file_str);
+ if (gb_file_exists(cast(char *)abs_path.text)) {
+ import_file = abs_path;
}
+ }
- id->fullpath = import_file;
- try_add_import_path(p, import_file, file_str, ast_node_token(node).pos);
+ id->fullpath = import_file;
+ try_add_import_path(p, import_file, file_str, ast_node_token(node).pos);
- } else if (node->kind == AstNode_ForeignLibrary) {
- AstNodeForeignLibrary *id = &node->ForeignLibrary;
- String file_str = id->filepath.string;
+ } else if (node->kind == AstNode_ForeignLibrary) {
+ AstNodeForeignLibrary *fl = &node->ForeignLibrary;
+ String file_str = fl->filepath.string;
- if (!is_import_path_valid(file_str)) {
- if (id->is_system) {
- syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path");
- } else {
- syntax_error(ast_node_token(node), "Invalid `foreign_library` path");
- }
- // NOTE(bill): It's a naughty name
- f->decls.e[i] = make_bad_decl(f, id->token, id->token);
- continue;
+ if (!is_import_path_valid(file_str)) {
+ if (fl->is_system) {
+ syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path");
+ } else {
+ syntax_error(ast_node_token(node), "Invalid `foreign_library` path");
}
+ // NOTE(bill): It's a naughty name
+ f->decls.e[i] = make_bad_decl(f, fl->token, fl->token);
+ continue;
+ }
- if (!id->is_system) {
- gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator
-
- String rel_path = get_fullpath_relative(allocator, base_dir, file_str);
- String import_file = rel_path;
- if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
- String abs_path = get_fullpath_core(allocator, file_str);
- if (gb_file_exists(cast(char *)abs_path.text)) {
- import_file = abs_path;
- }
- }
- file_str = import_file;
- }
+ fl->base_dir = base_dir;
+ }
+ }
+}
- try_add_foreign_library_path(p, file_str);
- }
+void parse_file(Parser *p, AstFile *f) {
+ String filepath = f->tokenizer.fullpath;
+ String base_dir = filepath;
+ for (isize i = filepath.len-1; i >= 0; i--) {
+ if (base_dir.text[i] == '\\' ||
+ base_dir.text[i] == '/') {
+ break;
}
+ base_dir.len--;
}
+
+ while (f->curr_token.kind == Token_Comment) {
+ next_token(f);
+ }
+
+ f->decls = parse_stmt_list(f);
+ parse_setup_file_decls(p, f, base_dir, f->decls);
}