mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
Branch Statements, if init statement, File parsing errors
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
TAU :: 6.28;
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,10 +3,6 @@
|
|||||||
|
|
||||||
rem del "..\examples\test.bc"
|
rem del "..\examples\test.bc"
|
||||||
call ..\bin\odin.exe ..\examples/test.odin
|
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.ll
|
||||||
|
|
||||||
rem call lli ..\examples/test.bc rem JIT
|
|
||||||
rem llc ..\examples/test.bc -march=x86-64 -o ..\examples/test.exe
|
|
||||||
|
|
||||||
|
|||||||
+74
-30
@@ -1,43 +1,59 @@
|
|||||||
// Statements and Declarations
|
// 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) {
|
void check_statement(Checker *c, AstNode *node, u32 flags);
|
||||||
for (; node != NULL; node = node->next)
|
|
||||||
check_statement(c, node);
|
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
|
// 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): 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
|
// TODO(bill): Warn/err against code after `return` that it won't be executed
|
||||||
b32 check_is_terminating(Checker *c, AstNode *node) {
|
b32 check_is_terminating(Checker *c, AstNode *node) {
|
||||||
switch (node->kind) {
|
switch (node->kind) {
|
||||||
case AstNode_BlockStatement: {
|
case AstNode_ReturnStatement:
|
||||||
for (; node != NULL; node = node->next) {
|
return true;
|
||||||
if (node->next == NULL)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (AstNode *n = node; node != NULL; n = n->prev) {
|
case AstNode_BlockStatement:
|
||||||
if (n->kind == AstNode_EmptyStatement)
|
return check_is_terminating_list(c, node->block_statement.list);
|
||||||
continue;
|
|
||||||
return check_is_terminating(c, n);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstNode_ExpressionStatement:
|
case AstNode_ExpressionStatement:
|
||||||
return check_is_terminating(c, node->expression_statement.expression);
|
return check_is_terminating(c, node->expression_statement.expression);
|
||||||
|
|
||||||
case AstNode_ReturnStatement:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case AstNode_IfStatement:
|
case AstNode_IfStatement:
|
||||||
if (node->if_statement.else_statement != NULL) {
|
if (node->if_statement.else_statement != NULL) {
|
||||||
if (check_is_terminating(c, node->if_statement.body) &&
|
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;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -353,7 +369,7 @@ void check_procedure_body(Checker *c, Token token, DeclarationInfo *decl, Type *
|
|||||||
c->context.decl = decl;
|
c->context.decl = decl;
|
||||||
|
|
||||||
push_procedure(c, type);
|
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 (type->procedure.results_count > 0) {
|
||||||
if (!check_is_terminating(c, body)) {
|
if (!check_is_terminating(c, body)) {
|
||||||
error(&c->error_collector, body->block_statement.close, "Missing return statement at the end of the procedure");
|
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) {
|
switch (node->kind) {
|
||||||
case AstNode_EmptyStatement: break;
|
case AstNode_EmptyStatement: break;
|
||||||
case AstNode_BadStatement: break;
|
case AstNode_BadStatement: break;
|
||||||
@@ -516,7 +532,7 @@ void check_statement(Checker *c, AstNode *node) {
|
|||||||
case AstNode_TagStatement:
|
case AstNode_TagStatement:
|
||||||
// TODO(bill): Tag Statements
|
// TODO(bill): Tag Statements
|
||||||
error(&c->error_collector, ast_node_token(node), "Tag statements are not supported yet");
|
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;
|
break;
|
||||||
|
|
||||||
case AstNode_IncDecStatement: {
|
case AstNode_IncDecStatement: {
|
||||||
@@ -631,11 +647,18 @@ void check_statement(Checker *c, AstNode *node) {
|
|||||||
|
|
||||||
case AstNode_BlockStatement:
|
case AstNode_BlockStatement:
|
||||||
check_open_scope(c, node);
|
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);
|
check_close_scope(c);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AstNode_IfStatement: {
|
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};
|
Operand operand = {Addressing_Invalid};
|
||||||
check_expression(c, &operand, node->if_statement.cond);
|
check_expression(c, &operand, node->if_statement.cond);
|
||||||
if (operand.mode != Addressing_Invalid &&
|
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),
|
error(&c->error_collector, ast_node_token(node->if_statement.cond),
|
||||||
"Non-boolean condition in `if` statement");
|
"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) {
|
if (node->if_statement.else_statement) {
|
||||||
switch (node->if_statement.else_statement->kind) {
|
switch (node->if_statement.else_statement->kind) {
|
||||||
case AstNode_IfStatement:
|
case AstNode_IfStatement:
|
||||||
case AstNode_BlockStatement:
|
case AstNode_BlockStatement:
|
||||||
check_statement(c, node->if_statement.else_statement);
|
check_statement(c, node->if_statement.else_statement, flags);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error(&c->error_collector, ast_node_token(node->if_statement.else_statement),
|
error(&c->error_collector, ast_node_token(node->if_statement.else_statement),
|
||||||
@@ -686,10 +710,12 @@ void check_statement(Checker *c, AstNode *node) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case AstNode_ForStatement: {
|
case AstNode_ForStatement: {
|
||||||
|
flags |= Statement_BreakAllowed | Statement_ContinueAllowed;
|
||||||
check_open_scope(c, node);
|
check_open_scope(c, node);
|
||||||
defer (check_close_scope(c));
|
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) {
|
if (node->for_statement.cond) {
|
||||||
Operand operand = {Addressing_Invalid};
|
Operand operand = {Addressing_Invalid};
|
||||||
check_expression(c, &operand, node->for_statement.cond);
|
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");
|
"Non-boolean condition in `for` statement");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check_statement(c, node->for_statement.end);
|
if (node->for_statement.end != NULL)
|
||||||
check_statement(c, node->for_statement.body);
|
check_statement(c, node->for_statement.end, 0);
|
||||||
|
check_statement(c, node->for_statement.body, flags);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case AstNode_DeferStatement: {
|
case AstNode_DeferStatement: {
|
||||||
@@ -710,11 +737,28 @@ void check_statement(Checker *c, AstNode *node) {
|
|||||||
} else {
|
} else {
|
||||||
b32 out_in_defer = c->in_defer;
|
b32 out_in_defer = c->in_defer;
|
||||||
c->in_defer = true;
|
c->in_defer = true;
|
||||||
check_statement(c, ds->statement);
|
check_statement(c, ds->statement, 0);
|
||||||
c->in_defer = out_in_defer;
|
c->in_defer = out_in_defer;
|
||||||
}
|
}
|
||||||
} break;
|
} 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
|
// Declarations
|
||||||
case AstNode_VariableDeclaration: {
|
case AstNode_VariableDeclaration: {
|
||||||
|
|||||||
@@ -104,8 +104,9 @@ struct Type {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Type *get_base_type(Type *t) {
|
Type *get_base_type(Type *t) {
|
||||||
while (t->kind == Type_Named)
|
while (t->kind == Type_Named) {
|
||||||
t = t->named.base;
|
t = t->named.base;
|
||||||
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-14
@@ -5,7 +5,6 @@
|
|||||||
#include "checker/checker.cpp"
|
#include "checker/checker.cpp"
|
||||||
// #include "codegen/codegen.cpp"
|
// #include "codegen/codegen.cpp"
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
gb_printf_err("Please specify a .odin file\n");
|
gb_printf_err("Please specify a .odin file\n");
|
||||||
@@ -22,24 +21,23 @@ int main(int argc, char **argv) {
|
|||||||
if (init_parser(&parser)) {
|
if (init_parser(&parser)) {
|
||||||
defer (destroy_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);
|
||||||
Checker checker = {};
|
defer (destroy_checker(&checker));
|
||||||
init_checker(&checker, &parser);
|
|
||||||
defer (destroy_checker(&checker));
|
|
||||||
|
|
||||||
check_parsed_files(&checker);
|
|
||||||
|
|
||||||
|
check_parsed_files(&checker);
|
||||||
#if 0
|
#if 0
|
||||||
Codegen codegen = {};
|
Codegen codegen = {};
|
||||||
if (init_codegen(&codegen, &checker)) {
|
if (init_codegen(&codegen, &checker)) {
|
||||||
defer (destroy_codegen(&codegen));
|
defer (destroy_codegen(&codegen));
|
||||||
|
|
||||||
generate_code(&codegen, file_node);
|
generate_code(&codegen, file_node);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+235
-67
@@ -3,6 +3,19 @@ struct Type;
|
|||||||
struct AstScope;
|
struct AstScope;
|
||||||
|
|
||||||
|
|
||||||
|
enum ParseFileError {
|
||||||
|
ParseFile_None,
|
||||||
|
|
||||||
|
ParseFile_WrongExtension,
|
||||||
|
ParseFile_InvalidFile,
|
||||||
|
ParseFile_EmptyFile,
|
||||||
|
ParseFile_Permission,
|
||||||
|
ParseFile_NotFound,
|
||||||
|
ParseFile_InvalidToken,
|
||||||
|
|
||||||
|
ParseFile_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct AstFile {
|
struct AstFile {
|
||||||
gbArena arena;
|
gbArena arena;
|
||||||
@@ -10,6 +23,11 @@ struct AstFile {
|
|||||||
gbArray(Token) tokens;
|
gbArray(Token) tokens;
|
||||||
Token * cursor; // NOTE(bill): Current token, easy to peek forward and backwards if needed
|
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;
|
AstNode *declarations;
|
||||||
isize declaration_count;
|
isize declaration_count;
|
||||||
|
|
||||||
@@ -19,6 +37,11 @@ struct AstFile {
|
|||||||
|
|
||||||
isize error_count;
|
isize error_count;
|
||||||
TokenPos error_prev_pos;
|
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_ReturnStatement,
|
||||||
AstNode_ForStatement,
|
AstNode_ForStatement,
|
||||||
AstNode_DeferStatement,
|
AstNode_DeferStatement,
|
||||||
|
AstNode_BranchStatement,
|
||||||
|
|
||||||
AstNode__ComplexStatementEnd,
|
AstNode__ComplexStatementEnd,
|
||||||
|
|
||||||
AstNode__StatementEnd,
|
AstNode__StatementEnd,
|
||||||
@@ -180,7 +205,10 @@ struct AstNode {
|
|||||||
} block_statement;
|
} block_statement;
|
||||||
struct {
|
struct {
|
||||||
Token token;
|
Token token;
|
||||||
AstNode *cond, *body, *else_statement;
|
AstNode *init;
|
||||||
|
AstNode *cond;
|
||||||
|
AstNode *body;
|
||||||
|
AstNode *else_statement;
|
||||||
} if_statement;
|
} if_statement;
|
||||||
struct {
|
struct {
|
||||||
Token token;
|
Token token;
|
||||||
@@ -196,6 +224,9 @@ struct AstNode {
|
|||||||
Token token;
|
Token token;
|
||||||
AstNode *statement;
|
AstNode *statement;
|
||||||
} defer_statement;
|
} defer_statement;
|
||||||
|
struct {
|
||||||
|
Token token;
|
||||||
|
} branch_statement;
|
||||||
|
|
||||||
struct { Token begin, end; } bad_declaration;
|
struct { Token begin, end; } bad_declaration;
|
||||||
struct {
|
struct {
|
||||||
@@ -333,6 +364,8 @@ Token ast_node_token(AstNode *node) {
|
|||||||
return node->for_statement.token;
|
return node->for_statement.token;
|
||||||
case AstNode_DeferStatement:
|
case AstNode_DeferStatement:
|
||||||
return node->defer_statement.token;
|
return node->defer_statement.token;
|
||||||
|
case AstNode_BranchStatement:
|
||||||
|
return node->branch_statement.token;
|
||||||
case AstNode_BadDeclaration:
|
case AstNode_BadDeclaration:
|
||||||
return node->bad_declaration.begin;
|
return node->bad_declaration.begin;
|
||||||
case AstNode_VariableDeclaration:
|
case AstNode_VariableDeclaration:
|
||||||
@@ -359,8 +392,7 @@ Token ast_node_token(AstNode *node) {
|
|||||||
return node->struct_type.token;
|
return node->struct_type.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
Token null_token = {};
|
return empty_token;
|
||||||
return null_token;
|
|
||||||
;}
|
;}
|
||||||
|
|
||||||
gb_inline void destroy_ast_scope(AstScope *scope) {
|
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;
|
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);
|
AstNode *result = make_node(f, AstNode_IfStatement);
|
||||||
result->if_statement.token = token;
|
result->if_statement.token = token;
|
||||||
|
result->if_statement.init = init;
|
||||||
result->if_statement.cond = cond;
|
result->if_statement.cond = cond;
|
||||||
result->if_statement.body = body;
|
result->if_statement.body = body;
|
||||||
result->if_statement.else_statement = else_statement;
|
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;
|
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) {
|
gb_inline AstNode *make_bad_declaration(AstFile *f, Token begin, Token end) {
|
||||||
AstNode *result = make_node(f, AstNode_BadDeclaration);
|
AstNode *result = make_node(f, AstNode_BadDeclaration);
|
||||||
result->bad_declaration.begin = begin;
|
result->bad_declaration.begin = begin;
|
||||||
@@ -770,8 +810,8 @@ gb_inline Token expect_token(AstFile *f, TokenKind kind) {
|
|||||||
Token prev = f->cursor[0];
|
Token prev = f->cursor[0];
|
||||||
if (prev.kind != kind) {
|
if (prev.kind != kind) {
|
||||||
ast_file_err(f, f->cursor[0], "Expected `%s`, got `%s`",
|
ast_file_err(f, f->cursor[0], "Expected `%s`, got `%s`",
|
||||||
token_kind_to_string(kind),
|
token_kind_to_string(kind),
|
||||||
token_kind_to_string(prev.kind));
|
token_kind_to_string(prev.kind));
|
||||||
}
|
}
|
||||||
next_token(f);
|
next_token(f);
|
||||||
return prev;
|
return prev;
|
||||||
@@ -781,7 +821,7 @@ gb_inline Token expect_operator(AstFile *f) {
|
|||||||
Token prev = f->cursor[0];
|
Token prev = f->cursor[0];
|
||||||
if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
|
if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
|
||||||
ast_file_err(f, f->cursor[0], "Expected an operator, got `%s`",
|
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);
|
next_token(f);
|
||||||
return prev;
|
return prev;
|
||||||
@@ -791,7 +831,7 @@ gb_inline Token expect_keyword(AstFile *f) {
|
|||||||
Token prev = f->cursor[0];
|
Token prev = f->cursor[0];
|
||||||
if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) {
|
if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) {
|
||||||
ast_file_err(f, f->cursor[0], "Expected a keyword, got `%s`",
|
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);
|
next_token(f);
|
||||||
return prev;
|
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);
|
AstNode *parse_expression(AstFile *f, b32 lhs);
|
||||||
@@ -903,8 +976,10 @@ AstNode *parse_literal_value(AstFile *f, AstNode *type_expression) {
|
|||||||
AstNode *element_list = NULL;
|
AstNode *element_list = NULL;
|
||||||
isize element_count = 0;
|
isize element_count = 0;
|
||||||
Token open = expect_token(f, Token_OpenBrace);
|
Token open = expect_token(f, Token_OpenBrace);
|
||||||
|
f->expression_level++;
|
||||||
if (f->cursor[0].kind != Token_CloseBrace)
|
if (f->cursor[0].kind != Token_CloseBrace)
|
||||||
element_list = parse_element_list(f, &element_count);
|
element_list = parse_element_list(f, &element_count);
|
||||||
|
f->expression_level--;
|
||||||
Token close = expect_token(f, Token_CloseBrace);
|
Token close = expect_token(f, Token_CloseBrace);
|
||||||
|
|
||||||
return make_compound_literal(f, type_expression, element_list, element_count, open, close);
|
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;
|
Token open, close;
|
||||||
// NOTE(bill): Skip the Paren Expression
|
// NOTE(bill): Skip the Paren Expression
|
||||||
open = expect_token(f, Token_OpenParen);
|
open = expect_token(f, Token_OpenParen);
|
||||||
|
f->expression_level++;
|
||||||
operand = parse_expression(f, false);
|
operand = parse_expression(f, false);
|
||||||
|
f->expression_level--;
|
||||||
close = expect_token(f, Token_CloseParen);
|
close = expect_token(f, Token_CloseParen);
|
||||||
operand = make_paren_expression(f, operand, open, close);
|
return make_paren_expression(f, operand, open, close);
|
||||||
return operand;
|
}
|
||||||
} break;
|
|
||||||
|
|
||||||
case Token_Hash: {
|
case Token_Hash: {
|
||||||
operand = parse_tag_expression(f, NULL);
|
operand = parse_tag_expression(f, NULL);
|
||||||
operand->tag_expression.expression = parse_expression(f, false);
|
operand->tag_expression.expression = parse_expression(f, false);
|
||||||
return operand;
|
return operand;
|
||||||
} break;
|
}
|
||||||
|
|
||||||
// Parse Procedure Type or Literal
|
// Parse Procedure Type or Literal
|
||||||
case Token_proc: {
|
case Token_proc: {
|
||||||
AstScope *scope = NULL;
|
AstScope *scope = NULL;
|
||||||
AstNode *type = parse_procedure_type(f, &scope);
|
AstNode *type = parse_procedure_type(f, &scope);
|
||||||
|
|
||||||
if (f->cursor[0].kind != Token_OpenBrace) {
|
if (f->cursor[0].kind != Token_OpenBrace) {
|
||||||
return type;
|
return type;
|
||||||
}
|
} else {
|
||||||
|
AstNode *body;
|
||||||
|
|
||||||
AstNode *body = parse_body(f, scope);
|
f->expression_level++;
|
||||||
return make_procedure_literal(f, type, body);
|
body = parse_body(f, scope);
|
||||||
} break;
|
f->expression_level--;
|
||||||
|
|
||||||
|
return make_procedure_literal(f, type, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
AstNode *type = parse_identifier_or_type(f);
|
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");
|
GB_ASSERT_MSG(type->kind != AstNode_Identifier, "Type Cannot be identifier");
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
} break;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_file_err(f, f->cursor[0], "Expected an operand");
|
Token begin = f->cursor[0];
|
||||||
return make_bad_expression(f, f->cursor[0], 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 *parse_atom_expression(AstFile *f, b32 lhs) {
|
||||||
AstNode *operand = parse_operand(f, lhs);
|
AstNode *operand = parse_operand(f, lhs);
|
||||||
b32 loop = true;
|
|
||||||
|
|
||||||
|
b32 loop = true;
|
||||||
while (loop) {
|
while (loop) {
|
||||||
switch (f->cursor[0].kind) {
|
switch (f->cursor[0].kind) {
|
||||||
case Token_OpenParen: {
|
case Token_OpenParen: {
|
||||||
@@ -995,6 +1090,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
|||||||
isize arg_list_count = 0;
|
isize arg_list_count = 0;
|
||||||
Token open_paren, close_paren;
|
Token open_paren, close_paren;
|
||||||
|
|
||||||
|
f->expression_level++;
|
||||||
open_paren = expect_token(f, Token_OpenParen);
|
open_paren = expect_token(f, Token_OpenParen);
|
||||||
|
|
||||||
while (f->cursor[0].kind != Token_CloseParen &&
|
while (f->cursor[0].kind != Token_CloseParen &&
|
||||||
@@ -1013,6 +1109,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
|||||||
next_token(f);
|
next_token(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f->expression_level--;
|
||||||
close_paren = expect_token(f, Token_CloseParen);
|
close_paren = expect_token(f, Token_CloseParen);
|
||||||
|
|
||||||
operand = make_call_expression(f, operand, arg_list, arg_list_count, open_paren, close_paren);
|
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;
|
Token open, close;
|
||||||
AstNode *indices[3] = {};
|
AstNode *indices[3] = {};
|
||||||
|
|
||||||
|
f->expression_level++;
|
||||||
open = expect_token(f, Token_OpenBracket);
|
open = expect_token(f, Token_OpenBracket);
|
||||||
|
|
||||||
if (f->cursor[0].kind != Token_Colon)
|
if (f->cursor[0].kind != Token_Colon)
|
||||||
indices[0] = parse_expression(f, false);
|
indices[0] = parse_expression(f, false);
|
||||||
isize colon_count = 0;
|
isize colon_count = 0;
|
||||||
@@ -1058,6 +1157,8 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
|||||||
indices[colon_count] = parse_expression(f, false);
|
indices[colon_count] = parse_expression(f, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f->expression_level--;
|
||||||
close = expect_token(f, Token_CloseBracket);
|
close = expect_token(f, Token_CloseBracket);
|
||||||
|
|
||||||
if (colon_count == 0) {
|
if (colon_count == 0) {
|
||||||
@@ -1084,20 +1185,14 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Token_OpenBrace: {
|
case Token_OpenBrace: {
|
||||||
switch (operand->kind) {
|
if (is_literal_type(operand) && f->expression_level >= 0) {
|
||||||
case AstNode_BadExpression:
|
gb_printf_err("here\n");
|
||||||
case AstNode_Identifier:
|
|
||||||
case AstNode_ArrayType:
|
|
||||||
case AstNode_StructType: {
|
|
||||||
if (lhs) {
|
if (lhs) {
|
||||||
// TODO(bill): Handle this
|
// TODO(bill): Handle this
|
||||||
}
|
}
|
||||||
operand = parse_literal_value(f, operand);
|
operand = parse_literal_value(f, operand);
|
||||||
} break;
|
} else {
|
||||||
|
|
||||||
default:
|
|
||||||
loop = false;
|
loop = false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@@ -1275,14 +1370,14 @@ AstNode *parse_block_statement(AstFile *f) {
|
|||||||
return block_statement;
|
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)
|
if (statement == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (statement->kind == AstNode_ExpressionStatement)
|
if (statement->kind == AstNode_ExpressionStatement)
|
||||||
return statement->expression_statement.expression;
|
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]);
|
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));
|
return make_pointer_type(f, expect_token(f, Token_Pointer), parse_type(f));
|
||||||
|
|
||||||
case Token_OpenBracket: {
|
case Token_OpenBracket: {
|
||||||
|
f->expression_level++;
|
||||||
Token token = expect_token(f, Token_OpenBracket);
|
Token token = expect_token(f, Token_OpenBracket);
|
||||||
AstNode *count_expression = NULL;
|
AstNode *count_expression = NULL;
|
||||||
|
|
||||||
if (f->cursor[0].kind != Token_CloseBracket)
|
if (f->cursor[0].kind != Token_CloseBracket)
|
||||||
count_expression = parse_expression(f, false);
|
count_expression = parse_expression(f, false);
|
||||||
expect_token(f, Token_CloseBracket);
|
expect_token(f, Token_CloseBracket);
|
||||||
|
f->expression_level--;
|
||||||
return make_array_type(f, token, count_expression, parse_type(f));
|
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);
|
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:
|
case Token_Colon:
|
||||||
break;
|
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]);
|
return make_bad_declaration(f, f->cursor[0], f->cursor[0]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ast_file_err(f, f->cursor[0], "Unknown type of variable declaration");
|
Token begin = f->cursor[0];
|
||||||
return make_bad_declaration(f, f->cursor[0], 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);
|
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);
|
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);
|
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) {
|
if (cond == NULL) {
|
||||||
ast_file_err(f, f->cursor[0], "Expected condition for if statement");
|
ast_file_err(f, f->cursor[0], "Expected condition for if statement");
|
||||||
}
|
}
|
||||||
|
|
||||||
body = parse_block_statement(f);
|
body = parse_block_statement(f);
|
||||||
else_statement = NULL;
|
|
||||||
if (allow_token(f, Token_else)) {
|
if (allow_token(f, Token_else)) {
|
||||||
switch (f->cursor[0].kind) {
|
switch (f->cursor[0].kind) {
|
||||||
case Token_if:
|
case Token_if:
|
||||||
@@ -1639,8 +1755,7 @@ AstNode *parse_if_statement(AstFile *f) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close_ast_scope(f);
|
return make_if_statement(f, token, init, cond, body, else_statement);
|
||||||
return make_if_statement(f, token, cond, body, else_statement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode *parse_return_statement(AstFile *f) {
|
AstNode *parse_return_statement(AstFile *f) {
|
||||||
@@ -1667,32 +1782,42 @@ AstNode *parse_for_statement(AstFile *f) {
|
|||||||
|
|
||||||
Token token = expect_token(f, Token_for);
|
Token token = expect_token(f, Token_for);
|
||||||
open_ast_scope(f);
|
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) {
|
if (f->cursor[0].kind != Token_OpenBrace) {
|
||||||
cond = parse_simple_statement(f);
|
isize prev_level = f->expression_level;
|
||||||
if (is_ast_node_complex_statement(cond)) {
|
f->expression_level = -1;
|
||||||
ast_file_err(f, f->cursor[0],
|
if (f->cursor[0].kind != Token_Semicolon) {
|
||||||
"You are not allowed that type of statement in a for statement, it's too complex!");
|
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)) {
|
if (allow_token(f, Token_Semicolon)) {
|
||||||
init_statement = cond;
|
init = cond;
|
||||||
cond = NULL;
|
cond = NULL;
|
||||||
if (f->cursor[0].kind != Token_Semicolon) {
|
if (f->cursor[0].kind != Token_Semicolon) {
|
||||||
cond = parse_simple_statement(f);
|
cond = parse_simple_statement(f);
|
||||||
}
|
}
|
||||||
expect_token(f, Token_Semicolon);
|
expect_token(f, Token_Semicolon);
|
||||||
if (f->cursor[0].kind != Token_OpenBrace) {
|
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);
|
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) {
|
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_return: return parse_return_statement(f);
|
||||||
case Token_for: return parse_for_statement(f);
|
case Token_for: return parse_for_statement(f);
|
||||||
case Token_defer: return parse_defer_statement(f);
|
case Token_defer: return parse_defer_statement(f);
|
||||||
// case Token_match:
|
// case Token_match: return NULL; // TODO(bill): Token_match
|
||||||
// case Token_case:
|
// 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:
|
case Token_Hash:
|
||||||
s = parse_tag_statement(f, NULL);
|
s = parse_tag_statement(f, NULL);
|
||||||
@@ -1789,9 +1921,12 @@ AstNode *parse_statement(AstFile *f) {
|
|||||||
s = make_empty_statement(f, token);
|
s = make_empty_statement(f, token);
|
||||||
next_token(f);
|
next_token(f);
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_file_err(f, token, "Expected a statement, got `%s`", token_kind_to_string(token.kind));
|
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]);
|
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"))) {
|
if (!string_has_extension(fullpath, make_string("odin"))) {
|
||||||
gb_printf_err("Only `.odin` files are allowed\n");
|
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());
|
gb_array_init(f->tokens, gb_heap_allocator());
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Token token = tokenizer_get_token(&f->tokenizer);
|
Token token = tokenizer_get_token(&f->tokenizer);
|
||||||
if (token.kind == Token_Invalid)
|
if (token.kind == Token_Invalid)
|
||||||
return false;
|
return ParseFile_InvalidToken;
|
||||||
gb_array_append(f->tokens, token);
|
gb_array_append(f->tokens, token);
|
||||||
|
|
||||||
if (token.kind == Token_EOF)
|
if (token.kind == Token_EOF)
|
||||||
@@ -1841,9 +1977,19 @@ b32 init_ast_file(AstFile *f, String fullpath) {
|
|||||||
open_ast_scope(f);
|
open_ast_scope(f);
|
||||||
f->file_scope = f->curr_scope;
|
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) {
|
void destroy_ast_file(AstFile *f) {
|
||||||
@@ -1928,8 +2074,6 @@ b32 is_import_path_valid(String path) {
|
|||||||
|
|
||||||
|
|
||||||
void parse_file(Parser *p, AstFile *f) {
|
void parse_file(Parser *p, AstFile *f) {
|
||||||
f->declarations = parse_statement_list(f, &f->declaration_count);
|
|
||||||
|
|
||||||
String filepath = f->tokenizer.fullpath;
|
String filepath = f->tokenizer.fullpath;
|
||||||
String base_dir = filepath;
|
String base_dir = filepath;
|
||||||
for (isize i = filepath.len-1; i >= 0; i--) {
|
for (isize i = filepath.len-1; i >= 0; i--) {
|
||||||
@@ -1938,6 +2082,8 @@ void parse_file(Parser *p, AstFile *f) {
|
|||||||
base_dir.len--;
|
base_dir.len--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f->declarations = parse_statement_list(f, &f->declaration_count);
|
||||||
|
|
||||||
for (AstNode *node = f->declarations; node != NULL; node = node->next) {
|
for (AstNode *node = f->declarations; node != NULL; node = node->next) {
|
||||||
if (!is_ast_node_declaration(node) &&
|
if (!is_ast_node_declaration(node) &&
|
||||||
node->kind != AstNode_BadStatement &&
|
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);
|
char *fullpath_str = gb_path_get_full_name(gb_heap_allocator(), init_filename);
|
||||||
String init_fullpath = make_string(fullpath_str);
|
String init_fullpath = make_string(fullpath_str);
|
||||||
gb_array_append(p->imports, init_fullpath);
|
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++) {
|
for (isize i = 0; i < gb_array_count(p->imports); i++) {
|
||||||
String import_path = p->imports[i];
|
String import_path = p->imports[i];
|
||||||
AstFile file = {};
|
AstFile file = {};
|
||||||
b32 ok = init_ast_file(&file, import_path);
|
ParseFileError err = init_ast_file(&file, import_path);
|
||||||
if (!ok) {
|
if (err != ParseFile_None) {
|
||||||
gb_printf_err("Failed to parse file: %.*s\n", LIT(import_path));
|
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);
|
parse_file(p, &file);
|
||||||
gb_array_append(p->files, file);
|
gb_array_append(p->files, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ParseFile_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+44
-9
@@ -214,14 +214,20 @@ struct TokenPos {
|
|||||||
isize line, column;
|
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.line == b.line) {
|
||||||
if (a.column == b.column) {
|
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
|
// 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)); }
|
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 {
|
struct Tokenizer {
|
||||||
String fullpath;
|
String fullpath;
|
||||||
u8 *start;
|
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);
|
char *c_str = gb_alloc_array(gb_heap_allocator(), char, fullpath.len+1);
|
||||||
memcpy(c_str, fullpath.text, fullpath.len);
|
memcpy(c_str, fullpath.text, fullpath.len);
|
||||||
c_str[fullpath.len] = '\0';
|
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);
|
gbFileContents fc = gb_file_read_contents(gb_heap_allocator(), true, c_str);
|
||||||
gb_zero_item(t);
|
gb_zero_item(t);
|
||||||
if (fc.data) {
|
if (fc.data != NULL) {
|
||||||
t->start = cast(u8 *)fc.data;
|
t->start = cast(u8 *)fc.data;
|
||||||
t->line = t->read_curr = t->curr = t->start;
|
t->line = t->read_curr = t->curr = t->start;
|
||||||
t->end = t->start + fc.size;
|
t->end = t->start + fc.size;
|
||||||
@@ -407,13 +425,30 @@ b32 init_tokenizer(Tokenizer *t, String fullpath) {
|
|||||||
advance_to_next_rune(t);
|
advance_to_next_rune(t);
|
||||||
if (t->curr_rune == GB_RUNE_BOM)
|
if (t->curr_rune == GB_RUNE_BOM)
|
||||||
advance_to_next_rune(t); // Ignore BOM at file beginning
|
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_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) {
|
void tokenizer_skip_whitespace(Tokenizer *t) {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
## Parser
|
## Parser
|
||||||
* Extra checking here rather than in the checker
|
* Extra checking here rather than in the checker
|
||||||
* Mulitple files
|
* Mulitple files (done)
|
||||||
|
- Namespaces
|
||||||
|
|
||||||
## Checker
|
## Checker
|
||||||
* Cyclic Type Checking
|
* Cyclic Type Checking
|
||||||
@@ -18,10 +19,10 @@
|
|||||||
- integer
|
- integer
|
||||||
- rational
|
- rational
|
||||||
- real
|
- real
|
||||||
* Multiple files
|
* Multiple files (done)
|
||||||
|
- Namespaces
|
||||||
|
|
||||||
## Codegen
|
## Codegen
|
||||||
* Begin!!!
|
|
||||||
* Emit LLVM-IR using custom library
|
* Emit LLVM-IR using custom library
|
||||||
* Debug info
|
* Debug info
|
||||||
|
|
||||||
@@ -29,3 +30,9 @@
|
|||||||
* Begin!!!
|
* Begin!!!
|
||||||
* Choose/determine architecture
|
* Choose/determine architecture
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Language
|
||||||
|
|
||||||
|
* should `if/for` statements init statement be of the same scope as the block scope or not? (currently not)
|
||||||
|
|||||||
Reference in New Issue
Block a user