diff --git a/examples/other.odin b/examples/other.odin index e69de29bb..1e2db5d7d 100644 --- a/examples/other.odin +++ b/examples/other.odin @@ -0,0 +1 @@ +TAU :: 6.28; diff --git a/examples/test.odin b/examples/test.odin index 8d23dbaa3..e69de29bb 100644 --- a/examples/test.odin +++ b/examples/test.odin @@ -1,10 +0,0 @@ -main :: proc() { - type Vec2: struct { x, y: f32; } - v := Vec2{1, 1}; - a := [2]int{1, 2}; // Array 2 of int - s := []int{1, 2}; // Slice of int - _, _ = a, s; - // Equivalent to - // sa := [2]int{1, 2}; s := sa[:]; - v.x = 1; -} diff --git a/run.bat b/run.bat index 7868aeaa4..ba78f765e 100644 --- a/run.bat +++ b/run.bat @@ -3,10 +3,6 @@ rem del "..\examples\test.bc" call ..\bin\odin.exe ..\examples/test.odin -rem clang -S -emit-llvm ..\examples/test.c -o ..\examples/test.ll -call llvm-as < ..\examples/test.ll rem call lli ..\examples/test.ll -rem call lli ..\examples/test.bc rem JIT -rem llc ..\examples/test.bc -march=x86-64 -o ..\examples/test.exe 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); +enum StatementFlag : u32 { + Statement_BreakAllowed = GB_BIT(0), + Statement_ContinueAllowed = GB_BIT(1), + // Statement_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough +}; -void check_statement_list(Checker *c, AstNode *node) { - for (; node != NULL; node = node->next) - check_statement(c, node); +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: { diff --git a/src/checker/type.cpp b/src/checker/type.cpp index 291d6fd17..e767a6d07 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -104,8 +104,9 @@ struct Type { }; Type *get_base_type(Type *t) { - while (t->kind == Type_Named) + while (t->kind == Type_Named) { t = t->named.base; + } return t; } diff --git a/src/main.cpp b/src/main.cpp index a41538f1b..1eed15dbc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,6 @@ #include "checker/checker.cpp" // #include "codegen/codegen.cpp" - int main(int argc, char **argv) { if (argc < 2) { gb_printf_err("Please specify a .odin file\n"); @@ -22,24 +21,23 @@ int main(int argc, char **argv) { if (init_parser(&parser)) { defer (destroy_parser(&parser)); - parse_files(&parser, init_filename); + if (parse_files(&parser, init_filename) == ParseFile_None) { + // print_ast(parser.files[0].declarations, 0); - // print_ast(parser.files[0].declarations, 0); - - Checker checker = {}; - init_checker(&checker, &parser); - defer (destroy_checker(&checker)); - - check_parsed_files(&checker); + Checker checker = {}; + init_checker(&checker, &parser); + defer (destroy_checker(&checker)); + check_parsed_files(&checker); #if 0 - Codegen codegen = {}; - if (init_codegen(&codegen, &checker)) { - defer (destroy_codegen(&codegen)); + Codegen codegen = {}; + if (init_codegen(&codegen, &checker)) { + defer (destroy_codegen(&codegen)); - generate_code(&codegen, file_node); - } + generate_code(&codegen, file_node); + } #endif + } } } diff --git a/src/parser.cpp b/src/parser.cpp index 5ff69589c..3ac0977c3 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3,6 +3,19 @@ struct Type; struct AstScope; +enum ParseFileError { + ParseFile_None, + + ParseFile_WrongExtension, + ParseFile_InvalidFile, + ParseFile_EmptyFile, + ParseFile_Permission, + ParseFile_NotFound, + ParseFile_InvalidToken, + + ParseFile_Count, +}; + struct AstFile { gbArena arena; @@ -10,6 +23,11 @@ struct AstFile { gbArray(Token) tokens; Token * cursor; // NOTE(bill): Current token, easy to peek forward and backwards if needed + // >= 0: In Expression + // < 0: In Control Clause + // NOTE(bill): Used to prevent type literals in control clauses + isize expression_level; + AstNode *declarations; isize declaration_count; @@ -19,6 +37,11 @@ struct AstFile { isize error_count; TokenPos error_prev_pos; + + // NOTE(bill): Error recovery +#define PARSER_MAX_FIX_COUNT 6 + isize fix_count; + TokenPos fix_prev_pos; }; @@ -78,6 +101,8 @@ AstNode__ComplexStatementBegin, AstNode_ReturnStatement, AstNode_ForStatement, AstNode_DeferStatement, + AstNode_BranchStatement, + AstNode__ComplexStatementEnd, AstNode__StatementEnd, @@ -180,7 +205,10 @@ struct AstNode { } block_statement; struct { Token token; - AstNode *cond, *body, *else_statement; + AstNode *init; + AstNode *cond; + AstNode *body; + AstNode *else_statement; } if_statement; struct { Token token; @@ -196,6 +224,9 @@ struct AstNode { Token token; AstNode *statement; } defer_statement; + struct { + Token token; + } branch_statement; struct { Token begin, end; } bad_declaration; struct { @@ -333,6 +364,8 @@ Token ast_node_token(AstNode *node) { return node->for_statement.token; case AstNode_DeferStatement: return node->defer_statement.token; + case AstNode_BranchStatement: + return node->branch_statement.token; case AstNode_BadDeclaration: return node->bad_declaration.begin; case AstNode_VariableDeclaration: @@ -359,8 +392,7 @@ Token ast_node_token(AstNode *node) { return node->struct_type.token; } - Token null_token = {}; - return null_token; + return empty_token; ;} gb_inline void destroy_ast_scope(AstScope *scope) { @@ -637,9 +669,10 @@ gb_inline AstNode *make_block_statement(AstFile *f, AstNode *list, isize list_co return result; } -gb_inline AstNode *make_if_statement(AstFile *f, Token token, AstNode *cond, AstNode *body, AstNode *else_statement) { +gb_inline AstNode *make_if_statement(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *body, AstNode *else_statement) { AstNode *result = make_node(f, AstNode_IfStatement); result->if_statement.token = token; + result->if_statement.init = init; result->if_statement.cond = cond; result->if_statement.body = body; result->if_statement.else_statement = else_statement; @@ -670,6 +703,13 @@ gb_inline AstNode *make_defer_statement(AstFile *f, Token token, AstNode *statem return result; } +gb_inline AstNode *make_branch_statement(AstFile *f, Token token) { + AstNode *result = make_node(f, AstNode_BranchStatement); + result->branch_statement.token = token; + return result; +} + + gb_inline AstNode *make_bad_declaration(AstFile *f, Token begin, Token end) { AstNode *result = make_node(f, AstNode_BadDeclaration); result->bad_declaration.begin = begin; @@ -770,8 +810,8 @@ gb_inline Token expect_token(AstFile *f, TokenKind kind) { Token prev = f->cursor[0]; if (prev.kind != kind) { ast_file_err(f, f->cursor[0], "Expected `%s`, got `%s`", - token_kind_to_string(kind), - token_kind_to_string(prev.kind)); + token_kind_to_string(kind), + token_kind_to_string(prev.kind)); } next_token(f); return prev; @@ -781,7 +821,7 @@ gb_inline Token expect_operator(AstFile *f) { Token prev = f->cursor[0]; if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) { ast_file_err(f, f->cursor[0], "Expected an operator, got `%s`", - token_kind_to_string(prev.kind)); + token_kind_to_string(prev.kind)); } next_token(f); return prev; @@ -791,7 +831,7 @@ gb_inline Token expect_keyword(AstFile *f) { Token prev = f->cursor[0]; if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) { ast_file_err(f, f->cursor[0], "Expected a keyword, got `%s`", - token_kind_to_string(prev.kind)); + token_kind_to_string(prev.kind)); } next_token(f); return prev; @@ -833,6 +873,39 @@ gb_internal void add_ast_entity(AstFile *f, AstScope *scope, AstNode *declaratio +void fix_advance_to_next_statement(AstFile *f) { +#if 0 + for (;;) { + Token t = f->cursor[0]; + switch (t.kind) { + case Token_EOF: + return; + + case Token_type: + case Token_break: + case Token_continue: + case Token_fallthrough: + case Token_if: + case Token_for: + case Token_defer: + case Token_return: + if (token_pos_are_equal(t.pos, f->fix_prev_pos) && + f->fix_count < PARSER_MAX_FIX_COUNT) { + f->fix_count++; + return; + } + if (token_pos_cmp(f->fix_prev_pos, t.pos) < 0) { + f->fix_prev_pos = t.pos; + f->fix_count = 0; // NOTE(bill): Reset + return; + } + + } + next_token(f); + } +#endif +} + AstNode *parse_expression(AstFile *f, b32 lhs); @@ -903,8 +976,10 @@ AstNode *parse_literal_value(AstFile *f, AstNode *type_expression) { AstNode *element_list = NULL; isize element_count = 0; Token open = expect_token(f, Token_OpenBrace); + f->expression_level++; if (f->cursor[0].kind != Token_CloseBrace) element_list = parse_element_list(f, &element_count); + f->expression_level--; Token close = expect_token(f, Token_CloseBrace); return make_compound_literal(f, type_expression, element_list, element_count, open, close); @@ -942,29 +1017,36 @@ AstNode *parse_operand(AstFile *f, b32 lhs) { Token open, close; // NOTE(bill): Skip the Paren Expression open = expect_token(f, Token_OpenParen); + f->expression_level++; operand = parse_expression(f, false); + f->expression_level--; close = expect_token(f, Token_CloseParen); - operand = make_paren_expression(f, operand, open, close); - return operand; - } break; + return make_paren_expression(f, operand, open, close); + } case Token_Hash: { operand = parse_tag_expression(f, NULL); operand->tag_expression.expression = parse_expression(f, false); return operand; - } break; + } // Parse Procedure Type or Literal case Token_proc: { AstScope *scope = NULL; AstNode *type = parse_procedure_type(f, &scope); + if (f->cursor[0].kind != Token_OpenBrace) { return type; - } + } else { + AstNode *body; - AstNode *body = parse_body(f, scope); - return make_procedure_literal(f, type, body); - } break; + f->expression_level++; + body = parse_body(f, scope); + f->expression_level--; + + return make_procedure_literal(f, type, body); + } + } default: { AstNode *type = parse_identifier_or_type(f); @@ -973,17 +1055,30 @@ AstNode *parse_operand(AstFile *f, b32 lhs) { GB_ASSERT_MSG(type->kind != AstNode_Identifier, "Type Cannot be identifier"); return type; } - } break; + } } - ast_file_err(f, f->cursor[0], "Expected an operand"); - return make_bad_expression(f, f->cursor[0], f->cursor[0]); + Token begin = f->cursor[0]; + ast_file_err(f, begin, "Expected an operand"); + fix_advance_to_next_statement(f); + return make_bad_expression(f, begin, f->cursor[0]); +} + +b32 is_literal_type(AstNode *node) { + switch (node->kind) { + case AstNode_BadExpression: + case AstNode_Identifier: + case AstNode_ArrayType: + case AstNode_StructType: + return true; + } + return false; } AstNode *parse_atom_expression(AstFile *f, b32 lhs) { AstNode *operand = parse_operand(f, lhs); - b32 loop = true; + b32 loop = true; while (loop) { switch (f->cursor[0].kind) { case Token_OpenParen: { @@ -995,6 +1090,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { isize arg_list_count = 0; Token open_paren, close_paren; + f->expression_level++; open_paren = expect_token(f, Token_OpenParen); while (f->cursor[0].kind != Token_CloseParen && @@ -1013,6 +1109,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { next_token(f); } + f->expression_level--; close_paren = expect_token(f, Token_CloseParen); operand = make_call_expression(f, operand, arg_list, arg_list_count, open_paren, close_paren); @@ -1043,7 +1140,9 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { Token open, close; AstNode *indices[3] = {}; + f->expression_level++; open = expect_token(f, Token_OpenBracket); + if (f->cursor[0].kind != Token_Colon) indices[0] = parse_expression(f, false); isize colon_count = 0; @@ -1058,6 +1157,8 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { indices[colon_count] = parse_expression(f, false); } } + + f->expression_level--; close = expect_token(f, Token_CloseBracket); if (colon_count == 0) { @@ -1084,20 +1185,14 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { break; case Token_OpenBrace: { - switch (operand->kind) { - case AstNode_BadExpression: - case AstNode_Identifier: - case AstNode_ArrayType: - case AstNode_StructType: { + if (is_literal_type(operand) && f->expression_level >= 0) { + gb_printf_err("here\n"); if (lhs) { // TODO(bill): Handle this } operand = parse_literal_value(f, operand); - } break; - - default: + } else { loop = false; - break; } } break; @@ -1275,14 +1370,14 @@ AstNode *parse_block_statement(AstFile *f) { return block_statement; } -AstNode *convert_statement_to_expression(AstFile *f, AstNode *statement, char *kind) { +AstNode *convert_statement_to_expression(AstFile *f, AstNode *statement, String kind) { if (statement == NULL) return NULL; if (statement->kind == AstNode_ExpressionStatement) return statement->expression_statement.expression; - ast_file_err(f, f->cursor[0], "Expected `%s`, found a simple statement.", kind); + ast_file_err(f, f->cursor[0], "Expected `%.*s`, found a simple statement.", LIT(kind)); return make_bad_expression(f, f->cursor[0], f->cursor[1]); } @@ -1371,12 +1466,14 @@ AstNode *parse_identifier_or_type(AstFile *f) { return make_pointer_type(f, expect_token(f, Token_Pointer), parse_type(f)); case Token_OpenBracket: { + f->expression_level++; Token token = expect_token(f, Token_OpenBracket); AstNode *count_expression = NULL; if (f->cursor[0].kind != Token_CloseBracket) count_expression = parse_expression(f, false); expect_token(f, Token_CloseBracket); + f->expression_level--; return make_array_type(f, token, count_expression, parse_type(f)); } @@ -1417,12 +1514,6 @@ AstNode *parse_identifier_or_type(AstFile *f) { return make_paren_expression(f, type_expression, open, close); } -#if 0 - // TODO(bill): Why did I add this? - case Token_Colon: - case Token_Eq: - break; -#endif case Token_Colon: break; @@ -1595,8 +1686,10 @@ AstNode *parse_declaration(AstFile *f, AstNode *name_list, isize name_list_count return make_bad_declaration(f, f->cursor[0], f->cursor[0]); } } else { - ast_file_err(f, f->cursor[0], "Unknown type of variable declaration"); - return make_bad_declaration(f, f->cursor[0], f->cursor[0]); + Token begin = f->cursor[0]; + ast_file_err(f, begin, "Unknown type of variable declaration"); + fix_advance_to_next_statement(f); + return make_bad_declaration(f, begin, f->cursor[0]); } AstNode *variable_declaration = make_variable_declaration(f, declaration_kind, name_list, name_list_count, type_expression, value_list, value_list_count); @@ -1612,18 +1705,41 @@ AstNode *parse_if_statement(AstFile *f) { } Token token = expect_token(f, Token_if); - AstNode *cond, *body, *else_statement; + AstNode *init = NULL; + AstNode *cond = NULL; + AstNode *body = NULL; + AstNode *else_statement = NULL; open_ast_scope(f); + defer (close_ast_scope(f)); + + isize prev_level = f->expression_level; + f->expression_level = -1; + + + if (allow_token(f, Token_Semicolon)) { + cond = parse_expression(f, false); + } else { + init = parse_simple_statement(f); + if (allow_token(f, Token_Semicolon)) { + cond = parse_expression(f, false); + } else { + cond = convert_statement_to_expression(f, init, make_string("boolean expression")); + init = NULL; + } + } + + + f->expression_level = prev_level; + + - cond = convert_statement_to_expression(f, parse_simple_statement(f), "boolean expression"); if (cond == NULL) { ast_file_err(f, f->cursor[0], "Expected condition for if statement"); } body = parse_block_statement(f); - else_statement = NULL; if (allow_token(f, Token_else)) { switch (f->cursor[0].kind) { case Token_if: @@ -1639,8 +1755,7 @@ AstNode *parse_if_statement(AstFile *f) { } } - close_ast_scope(f); - return make_if_statement(f, token, cond, body, else_statement); + return make_if_statement(f, token, init, cond, body, else_statement); } AstNode *parse_return_statement(AstFile *f) { @@ -1667,32 +1782,42 @@ AstNode *parse_for_statement(AstFile *f) { Token token = expect_token(f, Token_for); open_ast_scope(f); - AstNode *init_statement = NULL, *cond = NULL, *end_statement = NULL, *body = NULL; + defer (close_ast_scope(f)); + + AstNode *init = NULL; + AstNode *cond = NULL; + AstNode *end = NULL; + AstNode *body = NULL; if (f->cursor[0].kind != Token_OpenBrace) { - cond = parse_simple_statement(f); - if (is_ast_node_complex_statement(cond)) { - ast_file_err(f, f->cursor[0], - "You are not allowed that type of statement in a for statement, it's too complex!"); + isize prev_level = f->expression_level; + f->expression_level = -1; + if (f->cursor[0].kind != Token_Semicolon) { + cond = parse_simple_statement(f); + if (is_ast_node_complex_statement(cond)) { + ast_file_err(f, f->cursor[0], + "You are not allowed that type of statement in a for statement, it is too complex!"); + } } if (allow_token(f, Token_Semicolon)) { - init_statement = cond; + init = cond; cond = NULL; if (f->cursor[0].kind != Token_Semicolon) { cond = parse_simple_statement(f); } expect_token(f, Token_Semicolon); if (f->cursor[0].kind != Token_OpenBrace) { - end_statement = parse_simple_statement(f); + end = parse_simple_statement(f); } } + f->expression_level = prev_level; } body = parse_block_statement(f); - close_ast_scope(f); + cond = convert_statement_to_expression(f, cond, make_string("boolean expression")); - return make_for_statement(f, token, init_statement, cond, end_statement, body); + return make_for_statement(f, token, init, cond, end, body); } AstNode *parse_defer_statement(AstFile *f) { @@ -1775,8 +1900,15 @@ AstNode *parse_statement(AstFile *f) { case Token_return: return parse_return_statement(f); case Token_for: return parse_for_statement(f); case Token_defer: return parse_defer_statement(f); - // case Token_match: - // case Token_case: + // case Token_match: return NULL; // TODO(bill): Token_match + // case Token_case: return NULL; // TODO(bill): Token_case + + case Token_break: + case Token_continue: + case Token_fallthrough: + next_token(f); + expect_token(f, Token_Semicolon); + return make_branch_statement(f, token); case Token_Hash: s = parse_tag_statement(f, NULL); @@ -1789,9 +1921,12 @@ AstNode *parse_statement(AstFile *f) { s = make_empty_statement(f, token); next_token(f); return s; + + } ast_file_err(f, token, "Expected a statement, got `%s`", token_kind_to_string(token.kind)); + fix_advance_to_next_statement(f); return make_bad_statement(f, token, f->cursor[0]); } @@ -1814,17 +1949,18 @@ AstNode *parse_statement_list(AstFile *f, isize *list_count_) { -b32 init_ast_file(AstFile *f, String fullpath) { +ParseFileError init_ast_file(AstFile *f, String fullpath) { if (!string_has_extension(fullpath, make_string("odin"))) { gb_printf_err("Only `.odin` files are allowed\n"); - return false; + return ParseFile_WrongExtension; } - if (init_tokenizer(&f->tokenizer, fullpath)) { + TokenizerInitError err = init_tokenizer(&f->tokenizer, fullpath); + if (err == TokenizerInit_None) { gb_array_init(f->tokens, gb_heap_allocator()); for (;;) { Token token = tokenizer_get_token(&f->tokenizer); if (token.kind == Token_Invalid) - return false; + return ParseFile_InvalidToken; gb_array_append(f->tokens, token); if (token.kind == Token_EOF) @@ -1841,9 +1977,19 @@ b32 init_ast_file(AstFile *f, String fullpath) { open_ast_scope(f); f->file_scope = f->curr_scope; - return true; + return ParseFile_None; } - return false; + + switch (err) { + case TokenizerInit_NotExists: + return ParseFile_NotFound; + case TokenizerInit_Permission: + return ParseFile_Permission; + case TokenizerInit_Empty: + return ParseFile_EmptyFile; + } + + return ParseFile_InvalidFile; } void destroy_ast_file(AstFile *f) { @@ -1928,8 +2074,6 @@ b32 is_import_path_valid(String path) { void parse_file(Parser *p, AstFile *f) { - f->declarations = parse_statement_list(f, &f->declaration_count); - String filepath = f->tokenizer.fullpath; String base_dir = filepath; for (isize i = filepath.len-1; i >= 0; i--) { @@ -1938,6 +2082,8 @@ void parse_file(Parser *p, AstFile *f) { base_dir.len--; } + f->declarations = parse_statement_list(f, &f->declaration_count); + for (AstNode *node = f->declarations; node != NULL; node = node->next) { if (!is_ast_node_declaration(node) && node->kind != AstNode_BadStatement && @@ -1987,7 +2133,7 @@ void parse_file(Parser *p, AstFile *f) { } -void parse_files(Parser *p, char *init_filename) { +ParseFileError parse_files(Parser *p, char *init_filename) { char *fullpath_str = gb_path_get_full_name(gb_heap_allocator(), init_filename); String init_fullpath = make_string(fullpath_str); gb_array_append(p->imports, init_fullpath); @@ -1995,14 +2141,36 @@ void parse_files(Parser *p, char *init_filename) { for (isize i = 0; i < gb_array_count(p->imports); i++) { String import_path = p->imports[i]; AstFile file = {}; - b32 ok = init_ast_file(&file, import_path); - if (!ok) { + ParseFileError err = init_ast_file(&file, import_path); + if (err != ParseFile_None) { gb_printf_err("Failed to parse file: %.*s\n", LIT(import_path)); - return; + switch (err) { + case ParseFile_WrongExtension: + gb_printf_err("\tInvalid file extension\n"); + break; + case ParseFile_InvalidFile: + gb_printf_err("\tInvalid file\n"); + break; + case ParseFile_EmptyFile: + gb_printf_err("\tFile is empty\n"); + break; + case ParseFile_Permission: + gb_printf_err("\tFile permissions problem\n"); + break; + case ParseFile_NotFound: + gb_printf_err("\tFile cannot be found\n"); + break; + case ParseFile_InvalidToken: + gb_printf_err("\tInvalid token found in file\n"); + break; + } + return err; } parse_file(p, &file); gb_array_append(p->files, file); } + + return ParseFile_None; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index fd3683d91..4263f7f52 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -214,14 +214,20 @@ struct TokenPos { isize line, column; }; -b32 token_pos_are_equal(TokenPos a, TokenPos b) { +i32 token_pos_cmp(TokenPos a, TokenPos b) { if (a.line == b.line) { if (a.column == b.column) { - return are_strings_equal(a.file, b.file); + isize min_len = gb_min(a.file.len, b.file.len); + return gb_memcompare(a.file.text, b.file.text, min_len); } + return (a.column < b.column) ? -1 : +1; } - return false; + return (a.line < b.line) ? -1 : +1; +} + +b32 token_pos_are_equal(TokenPos a, TokenPos b) { + return token_pos_cmp(a, b) == 0; } // NOTE(bill): Text is UTF-8, thus why u8 and not char @@ -318,7 +324,19 @@ gb_inline b32 token_is_comparison(Token t) { gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); } -typedef struct Tokenizer Tokenizer; + +enum TokenizerInitError { + TokenizerInit_None, + + TokenizerInit_Invalid, + TokenizerInit_NotExists, + TokenizerInit_Permission, + TokenizerInit_Empty, + + TokenizerInit_Count, +}; + + struct Tokenizer { String fullpath; u8 *start; @@ -387,7 +405,7 @@ void advance_to_next_rune(Tokenizer *t) { } } -b32 init_tokenizer(Tokenizer *t, String fullpath) { +TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) { char *c_str = gb_alloc_array(gb_heap_allocator(), char, fullpath.len+1); memcpy(c_str, fullpath.text, fullpath.len); c_str[fullpath.len] = '\0'; @@ -395,7 +413,7 @@ b32 init_tokenizer(Tokenizer *t, String fullpath) { gbFileContents fc = gb_file_read_contents(gb_heap_allocator(), true, c_str); gb_zero_item(t); - if (fc.data) { + if (fc.data != NULL) { t->start = cast(u8 *)fc.data; t->line = t->read_curr = t->curr = t->start; t->end = t->start + fc.size; @@ -407,13 +425,30 @@ b32 init_tokenizer(Tokenizer *t, String fullpath) { advance_to_next_rune(t); if (t->curr_rune == GB_RUNE_BOM) advance_to_next_rune(t); // Ignore BOM at file beginning - return true; + return TokenizerInit_None; } - return false; + + gbFile f = {}; + gbFileError err = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + + switch (err) { + case gbFileError_Invalid: + return TokenizerInit_Invalid; + case gbFileError_NotExists: + return TokenizerInit_NotExists; + case gbFileError_Permission: + return TokenizerInit_Permission; + } + + if (gb_file_size(&f) == 0) + return TokenizerInit_Empty; + return TokenizerInit_None; } gb_inline void destroy_tokenizer(Tokenizer *t) { - gb_free(gb_heap_allocator(), t->start); + if (t->start != NULL) + gb_free(gb_heap_allocator(), t->start); } void tokenizer_skip_whitespace(Tokenizer *t) { diff --git a/todo.md b/todo.md index c7caf08cf..ecf45ffc1 100644 --- a/todo.md +++ b/todo.md @@ -8,7 +8,8 @@ ## Parser * Extra checking here rather than in the checker -* Mulitple files +* Mulitple files (done) + - Namespaces ## Checker * Cyclic Type Checking @@ -18,10 +19,10 @@ - integer - rational - real -* Multiple files +* Multiple files (done) + - Namespaces ## Codegen -* Begin!!! * Emit LLVM-IR using custom library * Debug info @@ -29,3 +30,9 @@ * Begin!!! * Choose/determine architecture + + + +## Language + +* should `if/for` statements init statement be of the same scope as the block scope or not? (currently not)