Files
corelang/parse.c
2022-05-13 16:04:39 +02:00

1184 lines
32 KiB
C

typedef struct Parser_Error Parser_Error;
struct Parser_Error{
Parser_Error *next;
String message;
Token *token;
};
typedef struct Parser{
Token_Array tokens;
Arena *arena;
Parser_Error *first_error;
Parser_Error *last_error;
}Parser;
__thread Parser *global_parser;
__thread Arena global_scratch;
__thread Arena_Checkpoint global_scratch_checkpoint;
function Arena *
arena_begin_scratch(){
global_scratch_checkpoint = arena_checkpoint(&global_scratch);
return &global_scratch;
}
function void
arena_end_scratch(){
arena_restore(global_scratch_checkpoint);
}
function void
parser_push_error(Parser *p, Token *token, char *str, ...){
String string;
{
va_list args1, args2;
va_start(args1, str);
va_copy(args2, args1);
string.len = vsnprintf(0, 0, str, args2);
va_end(args2);
string.str = arena_push_size(p->arena, string.len + 1);
vsnprintf((char*)string.str, string.len + 1, str, args1);
va_end(args1);
}
// @Note(Krzosa): Print nice error message
printf("\nError: %s", string.str);
if(token){
printf(" %s:%d\n", token->file.str, (S32)token->line);
// @Note(Krzosa): Print error line
{
int i = 0;
while(token->line_begin[i]!='\n' && token->line_begin[i]!=0) i++;
printf("%.*s\n", i, token->line_begin);
// @Note(Krzosa): Print error marker
int token_i = token->str - token->line_begin;
for(int i = 0; i < token_i-2; i++) printf(" ");
printf("^^^^^^\n");
}
}
Parser_Error *error = arena_push_struct(p->arena, Parser_Error);
error->message = string;
error->next = 0;
error->token = token;
SLLQueuePush(p->first_error, p->last_error, error);
__debugbreak();
}
//-----------------------------------------------------------------------------
// Parsing helpers
//-----------------------------------------------------------------------------
function Token *
token_get(Parser *p){
Token *result = token_array_iter_peek(&p->tokens, 0);
return result;
}
function Token *
token_peek(Parser *p, S64 i){
Token *result = token_array_iter_peek(&p->tokens, i);
return result;
}
function Token *
token_peek_is(Parser *p, Token_Kind kind, S64 i){
Token *result = token_peek(p, i);
if(result->kind == kind)
return result;
return 0;
}
function Token *
token_peek_is_keyword(Parser *p, Intern_String keyword, S64 i){
assert(lex_is_keyword(keyword));
Token *result = token_peek(p, i);
if(result->kind == TK_Keyword && intern_compare(result->intern_val, keyword))
return result;
return 0;
}
function Token *
token_is(Parser *p, Token_Kind kind){
Token *result = token_get(p);
if(result->kind == kind)
return result;
return 0;
}
function Token *
token_next(Parser *p){
Token *result = token_array_iter_next(&p->tokens);
return result;
}
function Token *
token_match(Parser *p, Token_Kind kind){
Token *token = token_get(p);
if(token->kind == kind){
token = token_next(p);
return token;
}
return 0;
}
function Token *
token_match_keyword(Parser *p, Intern_String string){
Token *token = token_get(p);
if(token->kind == TK_Keyword){
if(intern_compare(token->intern_val, string)){
token = token_next(p);
return token;
}
}
return 0;
}
function Token *
token_expect(Parser *p, Token_Kind kind){
Token *token = token_get(p);
if(token->kind == kind){
token = token_next(p);
return token;
}
parser_push_error(p, token, "Expected token of kind: %s, got instead token of kind: %s.", token_kind_string[kind], token_kind_string[token->kind]);
return 0;
}
function Typespec *parse_typespec(Parser *p);
function Expr *parse_expr(Parser *p);
//-----------------------------------------------------------------------------
// Expression parsing
//-----------------------------------------------------------------------------
/*
add = [+-]
mul = [/%*]
compare = == | != | >= | > | <= | <
logical = [&|^] | && | ||
unary = [&*-!~+] | ++ | --
atom_expr = Int
| Float
| String
| Identifier
| 'cast' '(' typespec ',' expr ')'
| 'size_type' '(' typespec ')'
| 'size_expr' '(' expr ')'
| '{' compound_expr '}'
| '(' expr ')'
| '(' ':' typespec ')' '{' compound_expr '}'
postfix_expr = atom_expr ('[' expr ']' | '.' Identifier | ++ | -- | '(' expr_list ')')*
unary_expr = unary ? unary_expr : atom_expr
mul_expr = atom_expr (mul atom_expr)*
add_expr = mul_expr (add mul_expr)*
logical_expr = add_expr (logical add_expr)*
compare_expr = logical_expr (compare logical_expr)*
ternary_expr = compare_expr ('?' ternary_expr ':' ternary_expr)?
expr = logical_expr
Compound literals
- (:[23]*Type){}
- Type{}
- { }
*/
function Expr_Compound_Field *
parse_expr_compound_field(Parser *p){
Token *token = token_get(p);
Expr_Compound_Field *result = 0;
if(token_match(p, TK_OpenBracket)){
Expr *index = parse_expr(p);
token_expect(p, TK_CloseBracket);
token_expect(p, TK_Assign);
Expr *expr = parse_expr(p);
result = expr_compound_index(p->arena, token, index, expr);
}
else{
Expr *expr = parse_expr(p);
if((token = token_match(p, TK_Assign))){
if(expr->kind != EK_Identifier){
parser_push_error(p, token, "Failed to parse compound literal, required identifier as left value");
}
result = expr_compound_named(p->arena, token, expr->intern_val, parse_expr(p));
}
else{
result = expr_compound_default(p->arena, token, expr);
}
}
return result;
}
function Expr *
parse_expr_compound(Parser *p, Typespec *typespec){
Token *token = token_expect(p, TK_OpenBrace);
Expr *expr = expr_compound(p->arena, token, typespec);
while(!token_is(p, TK_CloseBrace)){
Expr_Compound_Field *field = parse_expr_compound_field(p);
expr_compound_push(expr, field);
if(!token_match(p, TK_Comma)){
break;
}
}
token_expect(p, TK_CloseBrace);
return expr;
}
function Expr *
parse_expr_atom(Parser *p){
Expr *result = 0;
Token *token = token_get(p);
if(token_match(p, TK_StringLit)){
result = expr_str(p->arena, token);
}
else if(token_match(p, TK_Identifier)){
if(token_is(p, TK_OpenBrace)){
Typespec *typespec = typespec_name(p->arena, token, token->intern_val);
result = parse_expr_compound(p, typespec);
}
else{
result = expr_identifier(p->arena, token);
}
}
else if(token_match(p, TK_Integer)){
result = expr_int(p->arena, token);
}
else if(token_is(p, TK_OpenBrace)){
result = parse_expr_compound(p, 0);
}
else if(token_match(p, TK_OpenParen)){
if(token_match(p, TK_Colon)){
Typespec *typespec = parse_typespec(p);
token_expect(p, TK_CloseParen);
result = parse_expr_compound(p, typespec);
}
else{
Expr *expr = parse_expr(p);
token_expect(p, TK_CloseParen);
result = expr_paren(p->arena, token, expr);
}
}
else if(token_match_keyword(p, keyword_cast)){
token_expect(p, TK_OpenParen);
Typespec *typespec = parse_typespec(p);
token_expect(p, TK_Comma);
Expr *expr = parse_expr(p);
token_expect(p, TK_CloseParen);
result = expr_cast(p->arena, token, typespec, expr);
}
else{
parser_push_error(p, token, "Failed to parse expression");
}
return result;
}
function B32
token_is_postfix(Parser *p){
Token *token = token_get(p);
B32 result = token->kind == TK_OpenBracket
|| token->kind == TK_OpenParen
|| token->kind == TK_Dot
|| token->kind == TK_Increment
|| token->kind == TK_Decrement;
return result;
}
function Expr_Compound_Field *
parse_expr_function_argument(Parser *p){
Token *token = token_get(p);
Expr_Compound_Field *result = 0;
Expr *expr1 = parse_expr(p);
if(token_match(p, TK_Assign)){
if(expr1->kind != EK_Identifier){
parser_push_error(p, token, "Failed to parse named function argument, required identifier as left value");
}
Expr *expr2 = parse_expr(p);
result = expr_compound_named(p->arena, token, expr1->intern_val, expr2);
}
else{
result = expr_compound_default(p->arena, token, expr1);
}
return result;
}
function Expr *
parse_expr_postfix(Parser *p){
Expr *left = parse_expr_atom(p);
while(token_is_postfix(p)){
Token *token = 0;
if((token = token_match(p, TK_OpenBracket))){
Expr *size = parse_expr(p);
token_expect(p, TK_CloseBracket);
left = expr_index(p->arena, token, left, size);
}
else if((token = token_match(p, TK_OpenParen))){
left = expr_call(p->arena, token, left);
if(!token_is(p, TK_CloseParen)){
do {
Expr_Compound_Field *field = parse_expr_function_argument(p);
expr_call_push(left, field);
} while(token_match(p, TK_Comma));
}
token_expect(p, TK_CloseParen);
}
else if(token_match(p, TK_Dot)){
token = token_expect(p, TK_Identifier);
left = expr_field(p->arena, token, left);
}
else{
token = token_next(p);
assert(token->kind == TK_Increment || token->kind == TK_Decrement);
left = expr_postfix_unary(p->arena, token, left);
}
}
return left;
}
function B32
token_is_unary(Parser *p){
Token *token = token_get(p);
B32 result = token->kind == TK_Add
|| token->kind == TK_Increment
|| token->kind == TK_Decrement
|| token->kind == TK_Sub
|| token->kind == TK_Mul
|| token->kind == TK_BitAnd
|| token->kind == TK_Neg
|| token->kind == TK_Not;
return result;
}
function Expr *
parse_expr_unary(Parser *p){
if(token_is_unary(p)){
Token *op = token_next(p);
Expr *right = parse_expr_unary(p);
Expr *result = expr_unary(p->arena, op, right);
return result;
}
else{
return parse_expr_postfix(p);
}
}
function B32
token_is_mul(Parser *p){
Token *token = token_get(p);
B32 result = token->kind >= TK_FirstMul && token->kind <= TK_LastMul;
return result;
}
function Expr *
parse_expr_mul(Parser *p){
Expr *left = parse_expr_unary(p);
while(token_is_mul(p)){
Token *op = token_next(p);
Expr *right = parse_expr_unary(p);
left = expr_binary(p->arena, op, left, right);
}
return left;
}
function B32
token_is_add(Parser *p){
Token *token = token_get(p);
B32 result = token->kind >= TK_FirstAdd && token->kind <= TK_LastAdd;
return result;
}
function Expr *
parse_expr_add(Parser *p){
Expr *left = parse_expr_mul(p);
while(token_is_add(p)){
Token *op = token_next(p);
Expr *right = parse_expr_mul(p);
left = expr_binary(p->arena, op, left, right);
}
return left;
}
function B32
token_is_logical(Parser *p){
Token *token = token_get(p);
B32 result = token->kind >= TK_FirstLogical && token->kind <= TK_LastLogical;
return result;
}
function Expr *
parse_expr_logical(Parser *p){
Expr *left = parse_expr_add(p);
while(token_is_logical(p)){
Token *op = token_next(p);
Expr *right = parse_expr_add(p);
left = expr_binary(p->arena, op, left, right);
}
return left;
}
function B32
token_is_compare(Parser *p){
Token *token = token_get(p);
B32 result = token->kind >= TK_FirstCompare && token->kind <= TK_LastCompare;
return result;
}
function Expr *
parse_expr_compare(Parser *p){
Expr *left = parse_expr_logical(p);
while(token_is_compare(p)){
Token *op = token_next(p);
Expr *right = parse_expr_logical(p);
left = expr_binary(p->arena, op, left, right);
}
return left;
}
function Expr *
parse_expr_ternary(Parser *p){
Expr *cond = parse_expr_compare(p);
Token *token = 0;
if((token = token_match(p, TK_Question))){
Expr *on_true = parse_expr_ternary(p);
token_expect(p, TK_Colon);
Expr *on_false = parse_expr_ternary(p);
Expr *result = expr_ternary(p->arena, token, cond, on_true, on_false);
return result;
}
return cond;
}
function Expr *
parse_expr(Parser *p){
return parse_expr_ternary(p);
}
//-----------------------------------------------------------------------------
// Type specifier parsing
//-----------------------------------------------------------------------------
/*
base_type = NAME
| '(' type_list? ')' type?
type = ('*' | '[' expr ']')* base_type
Examples:
[32]*U32 - Array of 32 pointers to U32
**CustomDataType - Pointer to pointer of CustomDataType
(*U32, S64) **S64 - Function pointer
(CoolType: optional, S32) - Implicit void return value
*/
function Typespec *
parse_optional_type(Parser *p, Intern_String type){
Typespec *result = 0;
if(token_match(p, TK_Colon)){
result = parse_typespec(p);
}
else{
result = typespec_name(p->arena, token_get(p), type);
}
return result;
}
function Typespec *
parse_typespec_function(Parser *p, Token *token){
Typespec *result = typespec_function(p->arena, token, 0);
if(!token_is(p, TK_CloseParen))
for(;;) {
// Optional name
Token *name = 0;
if((token = token_is(p, TK_Identifier))){
if(token_peek_is(p, TK_Colon, 1)){
token_next(p);
token_next(p);
name = token;
}
}
// Parse type
Typespec *arg = parse_typespec(p);
if(name)
arg = typespec_named_argument(p->arena, name, arg, name->intern_val);
typespec_function_push(result, arg);
if(!token_match(p, TK_Comma)){
break;
}
}
token_expect(p, TK_CloseParen);
result->func.ret = parse_optional_type(p, intern_void);
return result;
}
// [10]*int - Array of 10 pointers to ints
function Typespec *
parse_typespec_recurse(Parser *p){
Token *token = token_get(p);
if(token_match(p, TK_Mul)){
Typespec *result = parse_typespec_recurse(p);
result = typespec_pointer(p->arena, token, result);
return result;
}
else if(token_match(p, TK_OpenBracket)){
Expr *expr = parse_expr(p);
token_expect(p, TK_CloseBracket);
Typespec *result = parse_typespec_recurse(p);
result = typespec_array(p->arena, token, result, expr);
return result;
}
else if(token_match(p, TK_OpenParen)){
Typespec *result = parse_typespec_function(p, token);
return result;
}
else if(token_match(p, TK_Identifier)){
Typespec *result = typespec_name(p->arena, token, token->intern_val);
return result;
}
else{
parser_push_error(p, token, "Failed to parse type, unexpected token");
return 0;
}
}
function Typespec *
parse_typespec(Parser *p){
Typespec *result = parse_typespec_recurse(p);
return result;
}
//-----------------------------------------------------------------------------
// Parsing decls
//-----------------------------------------------------------------------------
/*
name::(param:U32)*U32{}
name::struct{}
name::union{}
name::enum{}
name::typedef = name2;
name::const = 4254;
// Thing::struct{
// var1, var2: U32;
// union {
// thing: *S64;
// }
// named: union {
// data: [24]U32;
// }
// }
*/
function void
parse_note_list(Parser *p, AST_Parent *parent) {
if(token_match(p, TK_OpenParen)) {
if(token_match(p, TK_CloseParen)){
return;
}
do {
Token *name = token_expect(p, TK_Identifier);
Note *current = note_push_new(p->arena, parent, name, name->intern_val, 0);
parse_note_list(p, (AST_Parent *)current);
if(token_match(p, TK_Assign)) {
current->expr = parse_expr(p);
}
} while(token_match(p, TK_Comma));
token_expect(p, TK_CloseParen);
}
}
function void
parse__notes(Parser *p, AST_Parent *result) {
while(token_match(p, TK_At)) {
Token *name = token_expect(p, TK_Identifier);
Note *current = note_push_new(p->arena, result, name, name->intern_val, 0);
parse_note_list(p, (AST_Parent *)current);
if(token_match(p, TK_Assign)) {
current->expr = parse_expr(p);
}
token_match(p, TK_Semicolon);
}
}
function Note_List *
parse_notes(Parser *p){
Note_List result = (Note_List){.pos=token_get(p), .kind=AST_Note_List};
parse__notes(p, &result);
if(result.first){
Note_List *ast = (Note_List *)ast_shallow_copy(p->arena, (AST *)&result);
return ast;
}
return 0;
}
function Expr *
parse_assign_expr(Parser *p){
if(token_match(p, TK_Assign))
return parse_expr(p);
return 0;
}
function Decl_Enum *
parse_enum(Parser *p, Token *name){
Typespec *typespec = parse_optional_type(p, intern_int);
Decl_Enum *result = decl_enum(p->arena, name, name->intern_val, typespec);
token_expect(p, TK_OpenBrace);
do {
Note_List *notes = parse_notes(p);
Token *val = token_expect(p, TK_Identifier);
Expr *expr = parse_assign_expr(p);
decl_enum_push(p->arena, result, val, val->intern_val, expr, notes);
if(!token_match(p, TK_Comma)){
break;
}
}while(!token_is(p, TK_CloseBrace));
token_expect(p, TK_CloseBrace);
return result;
}
function void
parse_var_decl(Parser *p, Decl_Struct *parent, Token *name, Note_List *notes){
Token *name_stack[64];
S64 name_stack_len = 0;
name_stack[name_stack_len++] = name;
while(token_match(p, TK_Comma))
name_stack[name_stack_len++] = token_expect(p, TK_Identifier);
token_expect(p, TK_Colon);
Typespec *typespec = parse_typespec(p);
token_expect(p, TK_Semicolon);
for(S64 i = 0; i < name_stack_len; i++){
Decl_Var *decl = decl_variable(p->arena, name_stack[i], name_stack[i]->intern_val, 0, typespec, notes);
decl_struct_push(parent, (AST *)decl);
}
}
function Decl_Struct *
parse_struct(Parser *p, Token *name, AST_Kind struct_kind){
Intern_String intern_name = name ? name->intern_val : (Intern_String){0};
Token *token = name ? name : token_get(p);
Decl_Struct *result = decl_struct(p->arena, struct_kind, token, intern_name);
token_expect(p, TK_OpenBrace);
do {
Note_List *notes = parse_notes(p);
Token *token = token_get(p);
if(token_match_keyword(p, keyword_union)){
Decl_Struct *decl = parse_struct(p, 0, AST_Decl_SubUnion);
decl->notes = notes;
decl_struct_push(result, (AST *)decl);
}
else if(token_match_keyword(p, keyword_struct)){
Decl_Struct *decl = parse_struct(p, 0, AST_Decl_SubStruct);
decl->notes = notes;
decl_struct_push(result, (AST *)decl);
}
else if(token_match(p, TK_Identifier)){
if(token_is(p, TK_Colon)){
if(token_peek_is_keyword(p, keyword_union, 1)){
token_next(p); token_next(p);
Decl_Struct *decl = parse_struct(p, token, AST_Decl_SubUnion);
decl->notes = notes;
decl_struct_push(result, (AST *)decl);
}
else if(token_peek_is_keyword(p, keyword_struct, 1)){
token_next(p); token_next(p);
Decl_Struct *decl = parse_struct(p, token, AST_Decl_SubStruct);
decl->notes = notes;
decl_struct_push(result, (AST *)decl);
}
else{
parse_var_decl(p, result, token, notes);
}
}
else{
parse_var_decl(p, result, token, notes);
}
}
else{
parser_push_error(p, token, "Failed to parse struct, unexpected token of kind '%s'", token_kind_string[token->kind]);
}
} while(!token_match(p, TK_CloseBrace));
return result;
}
function Decl_Typedef *
parse_typedef(Parser *p, Token *name){
token_expect(p, TK_Assign);
Typespec *typespec = parse_typespec(p);
token_expect(p, TK_Semicolon);
Decl_Typedef *result = decl_typedef(p->arena, name, name->intern_val, typespec);
return result;
}
function Decl_Const *
parse_const(Parser *p, Token *name){
Typespec *typespec = 0;
if(token_match(p, TK_Colon))
typespec = parse_typespec(p);
token_expect(p, TK_Assign);
Expr *expr = parse_expr(p);
token_expect(p, TK_Semicolon);
Decl_Const *result = decl_const(p->arena, name, name->intern_val, expr, typespec);
return result;
}
function void parse_stmt_block(Parser *p, AST_Parent *parent);
function Decl_Func *
parse_function(Parser *p, Token *name){
Decl_Func *result = decl_function(p->arena, name, name->intern_val, 0);
if(!token_is(p, TK_CloseParen)){
do{
name = token_expect(p, TK_Identifier);
token_expect(p, TK_Colon);
Typespec *typespec = parse_typespec(p);
decl_func_push(p->arena, result, name, name->intern_val, typespec);
} while(token_match(p, TK_Comma));
}
token_expect(p, TK_CloseParen);
result->ret = parse_optional_type(p, intern_void);
if(token_is(p, TK_OpenBrace)){
result->body = stmt_block(p->arena, token_get(p));
parse_stmt_block(p, (AST_Parent *)result->body);
}
else if(token_match(p, TK_Semicolon)){
result->is_incomplete = true;
}
return result;
}
function AST *
parse_decl(Parser *p, AST_Parent *parent){
Token *name = 0;
AST *result = 0;
Note_List *note = parse_notes(p);
if((name = token_match(p, TK_Identifier))){
if(token_match(p, TK_DoubleColon)){
Token *token = 0;
if((token = token_match_keyword(p, keyword_enum))){
result = (AST *)parse_enum(p, name);
}
else if((token = token_match_keyword(p, keyword_union))){
result = (AST *)parse_struct(p, name, AST_Decl_Union);
}
else if((token = token_match_keyword(p, keyword_struct))){
result = (AST *)parse_struct(p, name, AST_Decl_Struct);
}
else if((token = token_match_keyword(p, keyword_typedef))){
result = (AST *)parse_typedef(p, name);
}
else if((token = token_match_keyword(p, keyword_const))){
result = (AST *)parse_const(p, name);
}
else if((token = token_match(p, TK_OpenParen))){
result = (AST *)parse_function(p, name);
}
else{
token = token_get(p);
parser_push_error(p, token, "Expected a declaration keyword. Got instead: %s", token_kind_string[token->kind]);
}
}
else{
Token *token = token_get(p);
parser_push_error(p, token, "Expected a declaration which starts with token '::' got instead"
"token '%s'", token_kind_string[token->kind]);
}
}
if(result){
result->notes = note;
ast_push_last(parent, result);
return result;
}
return 0;
}
//-----------------------------------------------------------------------------
// Statement parsing
//-----------------------------------------------------------------------------
function AST *parse_stmt(Parser *p);
/*
stmt_list = '{' stmt* '}'
stmt =
| stmt_list
| 'return' expr ';'
| 'if' expr stmt_list
| 'for' simple_stmt_list ';' expr ';' simple_stmt_list stmt_list
| 'for' expr stmt_list
| expr assign? ';'
//| 'while' expr stmt_or_stmt_list
*/
function void
parse_stmt_block(Parser *p, AST_Parent *parent){
token_expect(p, TK_OpenBrace);
while(!token_is(p, TK_End) && !token_is(p, TK_CloseBrace)){
AST *stmt = parse_stmt(p);
ast_push_last(parent, stmt);
}
token_expect(p, TK_CloseBrace);
}
function AST *
parse_stmt_if(Parser *p){
Expr *expr = parse_expr(p);
Stmt_If *result = stmt_if(p->arena, token_get(p), expr);
parse_stmt_block(p, (AST_Parent *)result->body);
Token *token = 0;
while((token = token_match_keyword(p, keyword_else))){
if(!token_match_keyword(p, keyword_if)){
Stmt_Else *stmt_else = stmt_else_push(p->arena, result, token);
parse_stmt_block(p, (AST_Parent *)stmt_else->body);
break;
}
Expr *expr = parse_expr(p);
Stmt_ElseIf *stmt_else_if = stmt_else_if_push(p->arena, result, token, expr);
parse_stmt_block(p, (AST_Parent *)stmt_else_if->body);
}
return (AST *)result;
}
function Stmt_Init *
parse_stmt_init(Parser *p, Expr *left){
Stmt_Init *result = 0;
if(token_match(p, TK_ColonAssign)){
if(left->kind != EK_Identifier){
parser_push_error(p, token_get(p), "Expected an identifier before ':='");
}
Expr *expr = parse_expr(p);
result = stmt_init(p->arena, left->token, left->token->intern_val, 0, expr);
}
else if(token_match(p, TK_Colon)){
if(left->kind != EK_Identifier){
parser_push_error(p, token_get(p), "Expected an identifier before ':'");
}
Typespec *typespec = parse_typespec(p);
Expr *expr = parse_assign_expr(p);
result = stmt_init(p->arena, left->token, left->token->intern_val, typespec, expr);
}
return result;
}
function Token *
token_is_assignment(Parser *p){
Token *t = token_get(p);
if(t->kind >= TK_FirstAssign && t->kind <= TK_LastAssign)
return t;
return 0;
}
function AST *
parse_simple_stmt(Parser *p){
Token *token = token_get(p);
Expr *left = parse_expr(p);
Stmt_Init *init = parse_stmt_init(p, left);
if(init){
return (AST *)init;
}
if(token_is_assignment(p)){
token = token_next(p);
Expr *right = parse_expr(p);
Stmt_Assign *result = stmt_assign(p->arena, token, token->kind, left, right);
return (AST *)result;
}
else{
Stmt_Expr *result = stmt_expr(p->arena, token, left);
return (AST *)result;
}
}
function AST *
parse_stmt_return(Parser *p){
Token *token = token_get(p);
Expr *expr = parse_expr(p);
token_expect(p, TK_Semicolon);
Stmt_Return *result = stmt_return(p->arena, token, expr);
return (AST *)result;
}
function AST *
parse_stmt_defer(Parser *p){
Token *token = token_get(p);
Stmt_Defer *result = stmt_defer(p->arena, token);
parse_stmt_block(p, result->body);
return (AST *)result;
}
function AST *
parse_stmt_for(Parser *p){
Token *token = token_get(p);
AST *on_begin = 0;
AST *on_iter = 0;
Expr*condition = 0;
if(!token_is(p, TK_OpenBrace)){
if(!token_is(p, TK_Semicolon)){
on_begin = parse_simple_stmt(p);
}
if(token_match(p, TK_Semicolon)){
if(!token_is(p, TK_Semicolon)){
condition = parse_expr(p);
}
if(token_match(p, TK_Semicolon)){
if(!token_is(p, TK_OpenBrace)){
on_iter = parse_simple_stmt(p);
if(on_iter->kind == AST_Stmt_Init){
parser_push_error(p, token_get(p), "Init statements are not allowed in for on_iter");
}
}
}
}
}
Stmt_For *result = stmt_for(p->arena, token, token_get(p), on_begin, condition, on_iter);
parse_stmt_block(p, (AST_Parent *)result->body);
return (AST*)result;
}
function AST *
parse_stmt(Parser *p){
Note_List *notes = parse_notes(p);
AST *result = 0;
if(token_match_keyword(p, keyword_if)){
result = (AST *)parse_stmt_if(p);
}
else if(token_match_keyword(p, keyword_for)){
result = parse_stmt_for(p);
}
else if(token_match_keyword(p, keyword_while)){
not_implemented;
}
else if(token_match_keyword(p, keyword_defer)){
result = parse_stmt_defer(p);
}
else if(token_match_keyword(p, keyword_return)){
result = parse_stmt_return(p);
}
else if(token_is(p, TK_OpenBrace)){
Stmt_Block *block = stmt_block(p->arena, token_get(p));
parse_stmt_block(p, block);
result = (AST *)block;
}
else{
result = parse_simple_stmt(p);
token_expect(p, TK_Semicolon);
}
if(result){
result->notes = notes;
}
return result;
}
//-----------------------------------------------------------------------------
// Test code
//-----------------------------------------------------------------------------
function S64
eval_expr(Expr *expr){
switch(expr->kind){
case EK_Int: return expr->int_val; break;
case EK_Unary:{
S64 left = eval_expr(expr->unary.expr);
switch(expr->unary.op){
case TK_Not: return !left; break;
case TK_Neg: return ~left; break;
case TK_Sub: return -left; break;
case TK_Add: return +left; break;
default: invalid_codepath;
}
} break;
case EK_Ternary:{
S64 cond = eval_expr(expr->ternary.cond);
if(cond) return eval_expr(expr->ternary.on_true);
else return eval_expr(expr->ternary.on_false);
} break;
case EK_Paren: return eval_expr(expr->paren.expr); break;
case EK_Binary: {
S64 left = eval_expr(expr->binary.left);
S64 right = eval_expr(expr->binary.right);
switch(expr->binary.op){
case TK_Add: return left + right; break;
case TK_Sub: return left - right; break;
case TK_Mul: return left * right; break;
case TK_Div: return left / right; break;
case TK_Mod: return left % right; break;
case TK_Equals: return left == right; break;
case TK_NotEquals: return left != right; break;
case TK_GreaterThenOrEqual: return left >= right; break;
case TK_LesserThenOrEqual: return left <= right; break;
case TK_GreaterThen: return left > right; break;
case TK_LesserThen: return left < right; break;
case TK_BitAnd: return left & right; break;
case TK_BitOr: return left | right; break;
case TK_BitXor: return left ^ right; break;
case TK_And: return left && right; break;
case TK_Or: return left || right; break;
case TK_LeftShift: return left << right; break;
case TK_RightShift: return left >> right; break;
default: invalid_codepath;
}
} break;
default: invalid_codepath;
}
return 0;
}
function Parser
parser_make(Arena *arena){
Parser result = {
.tokens = lex_make_token_array(arena),
.arena = arena,
};
global_parser = &result;
return result;
}
function void
parser_restream(Parser *p, String stream, String file){
lex_restream(&p->tokens, stream, file);
}
function Parser
parser_make_stream(Arena *arena, String stream, String file){
Parser parser = parser_make(arena);
lex_restream(&parser.tokens, stream, file);
return parser;
}
function void
parser_add_stream(Parser *p, String string, String file){
lex_add_stream(&p->tokens, string, file);
}
function Program *
test_parse_decls(Parser *p){
Program *decl_list = ast_program(p->arena, token_get(p));
while(!token_is(p, TK_End)){
AST *success = parse_decl(p, decl_list);
if(!success){
parser_push_error(p, token_get(p), "Failed to parse decls, unexpected token!");
break;
}
}
return decl_list;
}
function void
parse_test_expr(){
Arena *scratch = arena_begin_scratch();
String test_case = lit("32+52-242*2/424%5-23"
" 1<<5>>6<<2 "
" 5*(4/3)*(2+5) "
" 0&1 == 1&0 "
" 1&&5*3 "
" 1&&5||0 "
" 1>5>=5==0 "
" 1>5 ? 1 : 2 "
" !!!!!1 "
" ~~1 + -!2 "
" 1 + ++Thing[12]++ + ++Thing[12].expr +"
);
Parser parser = parser_make_stream(scratch, test_case, lit("Big_Expr"));
Parser *p = &parser;
S64 t = 5;
S64 test_val[] = {
(32+52-242*2/424%5-23),
(((1<<5)>>6)<<2),
5*(4/3)*(2+5),
(0&1) == (1&0),
1&&(t*3),
(1&&t)||0,
1>t>=t==0,
1>t ? 1 : 2,
!!!!!1,
~~1 + -!2,
};
for(int i = 0; i < buff_cap(test_val); i++){
Expr *expr = parse_expr(p);
S64 val = eval_expr(expr);
assert(val == test_val[i]);
}
String exprs[] = {
lit("cast([12](thing: U32, qwe: *U32): [32]Result, (123+234))"),
lit("cast((thing: U32, qwe: *U32), (123+234))"),
lit("(:(U32,U32)){Thing=10}"),
lit("--Not_Thing[156](Thing) + test_func(asd=func1, af=func2, gg=func3)"),
lit("(:[23]*Type){Thing=10}"),
lit("cast(**Data,{Thing=10})"),
lit("(:[64]S64){1,2,3,4,5}"),
lit("Data_Type{1,2,3,4,5}"),
};
for(SizeU i = 0; i < buff_cap(exprs); i++){
parser_restream(p, exprs[i], lit("Test_Exprs"));
Expr *expr = parse_expr(p);
expr_print(expr);
printf("\n");
}
arena_end_scratch();
}
function void
parse_test_decls(){
Arena *scratch = arena_begin_scratch();
Parser p = parser_make(scratch);
String decls[] = {
lit("Thing::enum: U64{ Thing_1 = 1<<1, Thing_2 = 2 }"),
lit("Struct::struct{ thing, thing2: *S64; struct { thing:U32; thing2: [64]Vec2; } inner: struct {i:U32;} }"),
};
for(SizeU i = 0; i < buff_cap(decls); i++){
parser_restream(&p, decls[i], lit("Decl_Test"));
Program *decl = test_parse_decls(&p);
assert(decl->first);
ast_print((AST *)decl);
}
arena_end_scratch();
}
function void
parse_test_from_file(){
const String FILENAME = lit("test.cc");
Arena *scratch = arena_begin_scratch();
String file = os_read_file(scratch, FILENAME);
Parser p = parser_make_stream(scratch, file, FILENAME);
Program *d = test_parse_decls(&p);
ast_print((AST *)d);
arena_end_scratch();
}
function void
parse_test_stmt(){
Arena *scratch = arena_begin_scratch();
Parser p = parser_make(scratch);
String stmts[] = {
lit("if thing > 10 { thing++; }"),
lit("thing := 23;"),
lit("thing+=245;"),
lit("thing++;"),
lit("return thing;"),
};
for(SizeU i = 0; i < buff_cap(stmts); i++){
parser_restream(&p, stmts[i], lit("Stmt_Test"));
AST *stmt = parse_stmt(&p);
ast_print(stmt);
}
arena_end_scratch();
}
function void
parse_test(){
parse_test_expr();
parse_test_decls();
parse_test_from_file();
parse_test_stmt();
}