From b232b9d5ea23fdd4d53f8e93cdfeb1f962811331 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 29 Nov 2016 22:08:48 +0000 Subject: Basic `when` statement - Compile time if statement This is similar to an #if in C but handled during the semantic checking stage. --- src/parser.c | 315 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 209 insertions(+), 106 deletions(-) (limited to 'src/parser.c') 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); } -- cgit v1.2.3