mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Restructuring the parser & split parser into parts. Parsing more constructs now, like enums with extended syntax. Corrected handling of function block to be an expression. Added expression block codegen.
This commit is contained in:
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
static void fprint_asts_recursive(FILE *file, Ast **asts, int indent);
|
||||
|
||||
Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility)
|
||||
{
|
||||
assert(name.string);
|
||||
@@ -515,6 +517,17 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent)
|
||||
fprintf_indented(file, indent, "(ident %s\n", expr->identifier_expr.identifier);
|
||||
fprint_expr_common(file, expr, indent + 1);
|
||||
break;
|
||||
case EXPR_EXPR_BLOCK:
|
||||
if (!expr->expr_block.stmts)
|
||||
{
|
||||
fprintf(file, "(expr_block)\n");
|
||||
return;
|
||||
}
|
||||
fprintf(file, "(expr_block\n");
|
||||
{
|
||||
fprint_asts_recursive(file, expr->expr_block.stmts, indent + 1);
|
||||
}
|
||||
break;
|
||||
case EXPR_CONST:
|
||||
fprintf_indented(file, indent, "(const ");
|
||||
switch (expr->const_expr.type)
|
||||
@@ -858,17 +871,6 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
|
||||
fprint_indent(file, indent);
|
||||
switch (ast->ast_kind)
|
||||
{
|
||||
case AST_FUNCTION_BLOCK_STMT:
|
||||
if (!ast->compound_stmt.stmts)
|
||||
{
|
||||
fprintf(file, "(function_block)\n");
|
||||
return;
|
||||
}
|
||||
fprintf(file, "(function_block\n");
|
||||
{
|
||||
fprint_asts_recursive(file, ast->compound_stmt.stmts, indent + 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case AST_COMPOUND_STMT:
|
||||
if (!ast->compound_stmt.stmts)
|
||||
|
||||
@@ -81,15 +81,20 @@ void compiler_compile(BuildTarget *target)
|
||||
{
|
||||
sema_analysis_pass_process_imports(contexts[i]);
|
||||
}
|
||||
if (diagnostics.errors > 0) exit(EXIT_FAILURE);
|
||||
|
||||
VECEACH(contexts, i)
|
||||
{
|
||||
sema_analysis_pass_conditional_compilation(contexts[i]);
|
||||
}
|
||||
if (diagnostics.errors > 0) exit(EXIT_FAILURE);
|
||||
|
||||
VECEACH(contexts, i)
|
||||
{
|
||||
sema_analysis_pass_decls(contexts[i]);
|
||||
}
|
||||
if (diagnostics.errors > 0) exit(EXIT_FAILURE);
|
||||
|
||||
VECEACH(contexts, i)
|
||||
{
|
||||
Context *context = contexts[i];
|
||||
|
||||
@@ -219,6 +219,7 @@ typedef struct
|
||||
{
|
||||
Decl *parent;
|
||||
Expr *expr;
|
||||
Expr **args;
|
||||
uint64_t ordinal;
|
||||
} EnumConstantDecl;
|
||||
|
||||
@@ -231,6 +232,7 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
Decl** values;
|
||||
Decl** parameters;
|
||||
TypeInfo *type_info;
|
||||
} EnumDecl;
|
||||
|
||||
@@ -473,6 +475,11 @@ typedef struct
|
||||
DeferList defers;
|
||||
} ExprScope;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Ast **stmts;
|
||||
} ExprFuncBlock;
|
||||
|
||||
struct _Expr
|
||||
{
|
||||
ExprKind expr_kind : 8;
|
||||
@@ -498,6 +505,7 @@ struct _Expr
|
||||
Expr** initializer_expr;
|
||||
Expr** expression_list;
|
||||
ExprScope expr_scope;
|
||||
ExprFuncBlock expr_block;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -669,6 +677,25 @@ typedef struct
|
||||
DeferList defers;
|
||||
} AstThrowStmt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Token name;
|
||||
Token constraints;
|
||||
Token token;
|
||||
} AsmOperand;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool is_volatile : 1;
|
||||
bool is_inline : 1;
|
||||
bool is_goto : 1;
|
||||
Expr *asm_template;
|
||||
AsmOperand** output_operands;
|
||||
AsmOperand** input_operands;
|
||||
AsmOperand** clobbers;
|
||||
Token* labels;
|
||||
} AstAsmStmt;
|
||||
|
||||
typedef struct _Ast
|
||||
{
|
||||
SourceRange span;
|
||||
@@ -677,8 +704,8 @@ typedef struct _Ast
|
||||
union
|
||||
{
|
||||
AstAttribute attribute;
|
||||
AstAsmStmt asm_stmt;
|
||||
AstCompoundStmt compound_stmt;
|
||||
AstCompoundStmt function_block_stmt;
|
||||
Decl *declare_stmt;
|
||||
Expr *expr_stmt;
|
||||
AstThrowStmt throw_stmt;
|
||||
@@ -781,6 +808,13 @@ typedef struct _Context
|
||||
Token *trailing_comment;
|
||||
Token *next_lead_comment;
|
||||
DynamicScope *current_scope;
|
||||
struct
|
||||
{
|
||||
Type *expected_block_type;
|
||||
Ast **returns;
|
||||
// Reusable returns cache.
|
||||
Ast **returns_cache;
|
||||
};
|
||||
Decl *evaluating_macro;
|
||||
// Error handling
|
||||
struct
|
||||
@@ -882,6 +916,7 @@ static inline Ast *extend_ast_with_prev_token(Context *context, Ast *ast)
|
||||
}
|
||||
|
||||
|
||||
|
||||
void builtin_setup(Target *target);
|
||||
|
||||
static inline bool builtin_may_negate(Type *canonical)
|
||||
@@ -957,6 +992,8 @@ bool context_set_module_from_filename(Context *context);
|
||||
bool context_set_module(Context *context, Path *path, Token *generic_parameters);
|
||||
void context_print_ast(Context *context, FILE *file);
|
||||
|
||||
#pragma mark --- Decl functions
|
||||
|
||||
Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility);
|
||||
Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility);
|
||||
Decl *decl_new_var(Token name, TypeInfo *type, VarDeclKind kind, Visibility visibility);
|
||||
@@ -973,6 +1010,8 @@ static inline DeclKind decl_from_token(TokenType type)
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
#pragma mark --- Diag functions
|
||||
|
||||
void diag_reset(void);
|
||||
void diag_error_range(SourceRange span, const char *message, ...);
|
||||
void diag_verror_range(SourceRange span, const char *message, va_list args);
|
||||
@@ -1000,6 +1039,7 @@ static inline bool func_has_error_return(FunctionSignature *func_sig)
|
||||
return func_sig->throws || func_sig->throw_any;
|
||||
}
|
||||
|
||||
#pragma mark --- Lexer functions
|
||||
|
||||
Token lexer_scan_token(Lexer *lexer);
|
||||
Token lexer_scan_ident_test(Lexer *lexer, const char *scan);
|
||||
@@ -1050,6 +1090,8 @@ void source_file_append_line_end(File *file, SourceLoc loc);
|
||||
SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc);
|
||||
SourcePosition source_file_find_position(SourceLoc loc);
|
||||
SourceRange source_range_from_ranges(SourceRange first, SourceRange last);
|
||||
#define RANGE_EXTEND_PREV(x) (x)->span.end_loc = context->prev_tok_end
|
||||
|
||||
static inline unsigned source_range_len(SourceRange range) { return range.end_loc - range.loc; }
|
||||
|
||||
void stable_init(STable *table, uint32_t initial_size);
|
||||
@@ -1193,4 +1235,10 @@ static inline const char* struct_union_name_from_token(TokenType type)
|
||||
#define DEBUG_TYPE(type) gencontext_get_debug_type(context, type)
|
||||
|
||||
void advance(Context *context);
|
||||
void advance_and_verify(Context *context, TokenType token_type);
|
||||
|
||||
// Useful sanity check function.
|
||||
static inline void advance_and_verify(Context *context, TokenType token_type)
|
||||
{
|
||||
assert(context->tok.type == token_type);
|
||||
advance(context);
|
||||
}
|
||||
@@ -89,7 +89,6 @@ typedef enum
|
||||
AST_DO_STMT,
|
||||
AST_EXPR_STMT,
|
||||
AST_FOR_STMT,
|
||||
AST_FUNCTION_BLOCK_STMT,
|
||||
AST_GENERIC_CASE_STMT,
|
||||
AST_GENERIC_DEFAULT_STMT,
|
||||
AST_GOTO_STMT,
|
||||
@@ -246,6 +245,7 @@ typedef enum
|
||||
EXPR_CAST,
|
||||
EXPR_SCOPED_EXPR,
|
||||
EXPR_MACRO_EXPR,
|
||||
EXPR_EXPR_BLOCK,
|
||||
} ExprKind;
|
||||
|
||||
|
||||
@@ -281,6 +281,7 @@ typedef enum
|
||||
SCOPE_CONTINUE = 1 << 1,
|
||||
SCOPE_NEXT = 1 << 2,
|
||||
SCOPE_DEFER = 1 << 3,
|
||||
SCOPE_EXPR_BLOCK = 1 << 4,
|
||||
} ScopeFlags;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -496,9 +496,9 @@ Token lexer_scan_token(Lexer *lexer)
|
||||
case '{':
|
||||
return make_token(lexer, TOKEN_LBRACE, "{");
|
||||
case '}':
|
||||
return match(lexer, ')') ? make_token(lexer, TOKEN_RPARBRA, "})") : make_token(lexer, TOKEN_RBRACE, "})");
|
||||
return match(lexer, ')') ? make_token(lexer, TOKEN_RPARBRA, "})") : make_token(lexer, TOKEN_RBRACE, "}");
|
||||
case '(':
|
||||
return match(lexer, '{') ? make_token(lexer, TOKEN_LPARBRA, "({") : make_token(lexer, TOKEN_LPAREN, ")");
|
||||
return match(lexer, '{') ? make_token(lexer, TOKEN_LPARBRA, "({") : make_token(lexer, TOKEN_LPAREN, "(");
|
||||
case ')':
|
||||
return make_token(lexer, TOKEN_RPAREN, ")");
|
||||
case '[':
|
||||
|
||||
@@ -77,6 +77,8 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_EXPR_BLOCK:
|
||||
TODO
|
||||
case EXPR_IDENTIFIER:
|
||||
return expr->identifier_expr.decl->var.backend_ref;
|
||||
case EXPR_UNARY:
|
||||
@@ -637,12 +639,45 @@ static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *c
|
||||
TODO
|
||||
}
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMValueRef old_ret_out = context->return_out;
|
||||
LLVMBasicBlockRef saved_expr_block = context->expr_block_exit;
|
||||
|
||||
LLVMBasicBlockRef expr_block = gencontext_create_free_block(context, "expr_block.exit");
|
||||
context->expr_block_exit = expr_block;
|
||||
|
||||
LLVMValueRef return_out = NULL;
|
||||
if (expr->type != type_void)
|
||||
{
|
||||
return_out = gencontext_emit_alloca(context, llvm_type(expr->type), "blockret");
|
||||
}
|
||||
context->return_out = return_out;
|
||||
|
||||
Ast **stmts = expr->expr_block.stmts;
|
||||
VECEACH(stmts, i)
|
||||
{
|
||||
gencontext_emit_stmt(context, stmts[i]);
|
||||
}
|
||||
gencontext_emit_br(context, expr_block);
|
||||
|
||||
// Emit the exit block.
|
||||
gencontext_emit_block(context, expr_block);
|
||||
|
||||
context->return_out = old_ret_out;
|
||||
context->expr_block_exit = saved_expr_block;
|
||||
|
||||
return return_out;
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_POISONED:
|
||||
UNREACHABLE
|
||||
case EXPR_EXPR_BLOCK:
|
||||
return gencontext_emit_expr_block(context, expr);
|
||||
case EXPR_SCOPED_EXPR:
|
||||
return gencontext_emit_scoped_expr(context, expr);
|
||||
case EXPR_UNARY:
|
||||
|
||||
@@ -107,6 +107,7 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(context->context, context->function, "entry");
|
||||
context->current_block = entry;
|
||||
context->current_block_is_target = true;
|
||||
context->expr_block_exit = NULL;
|
||||
context->builder = LLVMCreateBuilder();
|
||||
LLVMPositionBuilderAtEnd(context->builder, entry);
|
||||
|
||||
|
||||
@@ -48,10 +48,8 @@ typedef struct
|
||||
LLVMValueRef alloca_point;
|
||||
LLVMBuilderRef builder;
|
||||
LLVMBasicBlockRef current_block;
|
||||
bool current_block_is_target;
|
||||
Decl *cur_code_decl;
|
||||
Decl *cur_func_decl;
|
||||
bool did_call_stack_save;
|
||||
TypeInfo *current_return_type;
|
||||
int block_global_unique_count;
|
||||
int ast_alloca_addr_space;
|
||||
@@ -65,6 +63,9 @@ typedef struct
|
||||
BreakContinue break_continue_stack[BREAK_STACK_MAX];
|
||||
size_t break_continue_stack_index;
|
||||
LLVMValueRef return_out;
|
||||
LLVMBasicBlockRef expr_block_exit;
|
||||
bool current_block_is_target : 1;
|
||||
bool did_call_stack_save : 1;
|
||||
} GenContext;
|
||||
|
||||
|
||||
|
||||
@@ -105,6 +105,12 @@ LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Ast *ast, bool
|
||||
return result;
|
||||
}
|
||||
|
||||
void gencontext_emit_jmp(GenContext *context, LLVMBasicBlockRef block)
|
||||
{
|
||||
gencontext_emit_br(context, block);
|
||||
LLVMBasicBlockRef post_jump_block = gencontext_create_free_block(context, "jmp");
|
||||
gencontext_emit_block(context, post_jump_block);
|
||||
}
|
||||
|
||||
static inline void gencontext_emit_return(GenContext *context, Ast *ast)
|
||||
{
|
||||
@@ -113,6 +119,18 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast)
|
||||
|
||||
LLVMValueRef ret_value = ast->return_stmt.expr ? gencontext_emit_expr(context, ast->return_stmt.expr) : NULL;
|
||||
gencontext_emit_defer(context, ast->return_stmt.defer, NULL);
|
||||
|
||||
// Are we in an expression block?
|
||||
if (context->expr_block_exit)
|
||||
{
|
||||
if (context->return_out)
|
||||
{
|
||||
LLVMBuildStore(context->builder, ret_value, context->return_out);
|
||||
}
|
||||
gencontext_emit_jmp(context, context->expr_block_exit);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ret_value)
|
||||
{
|
||||
gencontext_emit_implicit_return(context);
|
||||
@@ -393,12 +411,6 @@ void gencontext_emit_do_stmt(GenContext *context, Ast *ast)
|
||||
gencontext_pop_break_continue(context);
|
||||
}
|
||||
|
||||
void gencontext_emit_jmp(GenContext *context, LLVMBasicBlockRef block)
|
||||
{
|
||||
gencontext_emit_br(context, block);
|
||||
LLVMBasicBlockRef post_jump_block = gencontext_create_free_block(context, "jmp");
|
||||
gencontext_emit_block(context, post_jump_block);
|
||||
}
|
||||
|
||||
void gencontext_emit_label(GenContext *context, Ast *ast)
|
||||
{
|
||||
@@ -591,8 +603,6 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
switch (ast->ast_kind)
|
||||
{
|
||||
case AST_FUNCTION_BLOCK_STMT:
|
||||
TODO
|
||||
case AST_POISONED:
|
||||
UNREACHABLE
|
||||
case AST_SCOPED_STMT:
|
||||
|
||||
785
src/compiler/parse_expr.c
Normal file
785
src/compiler/parse_expr.c
Normal file
@@ -0,0 +1,785 @@
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
#include "parser_internal.h"
|
||||
|
||||
|
||||
typedef Expr *(*ParseFn)(Context *context, Expr *);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ParseFn prefix;
|
||||
ParseFn infix;
|
||||
Precedence precedence;
|
||||
} ParseRule;
|
||||
|
||||
extern ParseRule rules[TOKEN_EOF + 1];
|
||||
|
||||
inline Expr *parse_precedence_with_left_side(Context *context, Expr *left_side, Precedence precedence)
|
||||
{
|
||||
while (precedence <= rules[context->tok.type].precedence)
|
||||
{
|
||||
if (!expr_ok(left_side)) return left_side;
|
||||
ParseFn infix_rule = rules[context->tok.type].infix;
|
||||
left_side = infix_rule(context, left_side);
|
||||
}
|
||||
return left_side;
|
||||
}
|
||||
|
||||
|
||||
static Expr *parse_precedence(Context *context, Precedence precedence)
|
||||
{
|
||||
// Get the rule for the previous token.
|
||||
ParseFn prefix_rule = rules[context->tok.type].prefix;
|
||||
if (prefix_rule == NULL)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "An expression was expected.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
|
||||
Expr *left_side = prefix_rule(context, NULL);
|
||||
if (!expr_ok(left_side)) return left_side;
|
||||
return parse_precedence_with_left_side(context, left_side, precedence);
|
||||
}
|
||||
|
||||
inline Expr* parse_expr(Context *context)
|
||||
{
|
||||
return parse_precedence(context, PREC_ASSIGNMENT);
|
||||
}
|
||||
|
||||
inline Expr* parse_constant_expr(Context *context)
|
||||
{
|
||||
return parse_precedence(context, PREC_TERNARY);
|
||||
}
|
||||
|
||||
/**
|
||||
* param_list
|
||||
* : parameter
|
||||
* | parameter ',' parameters
|
||||
* ;
|
||||
*
|
||||
* parameter
|
||||
* : type
|
||||
* | expr
|
||||
* ;
|
||||
*
|
||||
*/
|
||||
bool parse_param_list(Context *context, Expr ***result, bool allow_type)
|
||||
{
|
||||
*result = NULL;
|
||||
while (1)
|
||||
{
|
||||
TypeInfo *type = NULL;
|
||||
Expr *expr = NULL;
|
||||
SourceRange start = context->tok.span;
|
||||
if (!parse_type_or_expr(context, &expr, &type)) return false;
|
||||
if (!expr)
|
||||
{
|
||||
if (!allow_type)
|
||||
{
|
||||
sema_error_range(start, "Did not expect a type here, only expressions.");
|
||||
return false;
|
||||
}
|
||||
expr = expr_new(EXPR_TYPE, start);
|
||||
RANGE_EXTEND_PREV(expr);
|
||||
expr->type_expr.type = type;
|
||||
}
|
||||
vec_add(*result, expr);
|
||||
if (!try_consume(context, TOKEN_COMMA))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Expr *parse_macro_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *macro_expr = EXPR_NEW_TOKEN(EXPR_MACRO_EXPR, context->tok);
|
||||
advance_and_verify(context, TOKEN_AT);
|
||||
macro_expr->macro_expr = TRY_EXPR_OR(parse_precedence(context, PREC_UNARY + 1), &poisoned_expr);
|
||||
return macro_expr;
|
||||
}
|
||||
|
||||
|
||||
static inline Expr* parse_non_assign_expr(Context *context)
|
||||
{
|
||||
return parse_precedence(context, PREC_ASSIGNMENT + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* expression_list
|
||||
* : expression
|
||||
* | expression_list ',' expression
|
||||
* ;
|
||||
* @return Ast *
|
||||
*/
|
||||
Expr *parse_expression_list(Context *context)
|
||||
{
|
||||
Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok);
|
||||
if (!parse_param_list(context, &expr_list->expression_list, false)) return &poisoned_expr;
|
||||
return expr_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param left must be null.
|
||||
* @return Expr*
|
||||
*/
|
||||
static Expr *parse_type_identifier(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
return parse_type_identifier_with_path(context, NULL);
|
||||
}
|
||||
static Expr *parse_cast_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *expr = EXPR_NEW_TOKEN(EXPR_CAST, context->tok);
|
||||
advance_and_verify(context, TOKEN_CAST);
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_expr);
|
||||
expr->cast_expr.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr);
|
||||
CONSUME_OR(TOKEN_COMMA, &poisoned_expr);
|
||||
expr->cast_expr.type_info = TRY_TYPE_OR(parse_type_expression(context), &poisoned_expr);
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_expr);
|
||||
return expr;
|
||||
}
|
||||
|
||||
|
||||
static Expr *parse_unary_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Did not expect a left hand side!");
|
||||
|
||||
TokenType operator_type = context->tok.type;
|
||||
|
||||
Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY, context->tok);
|
||||
unary->unary_expr.operator = unaryop_from_token(operator_type);
|
||||
Precedence rule_precedence = rules[operator_type].precedence;
|
||||
advance(context);
|
||||
Expr *right_side = parse_precedence(context, rule_precedence);
|
||||
|
||||
CHECK_EXPR(right_side);
|
||||
|
||||
unary->unary_expr.expr = right_side;
|
||||
return unary;
|
||||
}
|
||||
|
||||
static Expr *parse_post_unary(Context *context, Expr *left)
|
||||
{
|
||||
assert(expr_ok(left));
|
||||
Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, context->tok);
|
||||
unary->post_expr.expr = left;
|
||||
unary->post_expr.operator = post_unaryop_from_token(context->tok.type);
|
||||
advance(context);
|
||||
return unary;
|
||||
}
|
||||
|
||||
static Expr *parse_ternary_expr(Context *context, Expr *left_side)
|
||||
{
|
||||
assert(expr_ok(left_side));
|
||||
Expr *expr_ternary = EXPR_NEW_EXPR(EXPR_TERNARY, left_side);
|
||||
expr_ternary->ternary_expr.cond = left_side;
|
||||
|
||||
// Check for elvis
|
||||
if (try_consume(context, TOKEN_ELVIS))
|
||||
{
|
||||
expr_ternary->ternary_expr.then_expr = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
advance_and_verify(context, TOKEN_QUESTION);
|
||||
Expr *true_expr = TRY_EXPR_OR(parse_precedence(context, PREC_TERNARY + 1), &poisoned_expr);
|
||||
expr_ternary->ternary_expr.then_expr = true_expr;
|
||||
CONSUME_OR(TOKEN_COLON, &poisoned_expr);
|
||||
}
|
||||
|
||||
Expr *false_expr = TRY_EXPR_OR(parse_precedence(context, PREC_TERNARY + 1), &poisoned_expr);
|
||||
expr_ternary->ternary_expr.else_expr = false_expr;
|
||||
return expr_ternary;
|
||||
}
|
||||
|
||||
/**
|
||||
* grouping_expr
|
||||
* : '(' expression ')'
|
||||
* ;
|
||||
*/
|
||||
static Expr *parse_grouping_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
advance_and_verify(context, TOKEN_LPAREN);
|
||||
Expr *right = TRY_EXPR_OR(parse_expr(context), &poisoned_expr);
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_expr);
|
||||
return right;
|
||||
}
|
||||
|
||||
/**
|
||||
* initializer
|
||||
* : initializer_list
|
||||
* | expr
|
||||
* ;
|
||||
*
|
||||
* @param context
|
||||
* @return the parsed expression
|
||||
*/
|
||||
Expr *parse_initializer(Context *context)
|
||||
{
|
||||
if (context->tok.type == TOKEN_LBRACE)
|
||||
{
|
||||
return parse_initializer_list(context);
|
||||
}
|
||||
return parse_expr(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* initializer_list
|
||||
* : '{' initializer_values '}'
|
||||
* | '{' initializer_values ',' '}'
|
||||
* ;
|
||||
*
|
||||
* initializer_values
|
||||
* : initializer
|
||||
* | initializer_values ',' initializer
|
||||
* ;
|
||||
*
|
||||
* @param elements
|
||||
* @return
|
||||
*/
|
||||
Expr *parse_initializer_list(Context *context)
|
||||
{
|
||||
Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, context->tok);
|
||||
CONSUME_OR(TOKEN_LBRACE, &poisoned_expr);
|
||||
if (!parse_param_list(context, &initializer_list->initializer_expr, false)) return &poisoned_expr;
|
||||
CONSUME_OR(TOKEN_RBRACE, &poisoned_expr);
|
||||
return initializer_list;
|
||||
}
|
||||
|
||||
static Expr *parse_binary(Context *context, Expr *left_side)
|
||||
{
|
||||
assert(left_side && expr_ok(left_side));
|
||||
|
||||
// Remember the operator.
|
||||
TokenType operator_type = context->tok.type;
|
||||
|
||||
advance(context);
|
||||
|
||||
Expr *right_side;
|
||||
if (context->tok.type == TOKEN_LBRACE && operator_type == TOKEN_EQ)
|
||||
{
|
||||
right_side = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr);
|
||||
}
|
||||
else
|
||||
{
|
||||
right_side = TRY_EXPR_OR(parse_precedence(context, rules[operator_type].precedence + 1), &poisoned_expr);
|
||||
}
|
||||
|
||||
Expr *expr = EXPR_NEW_EXPR(EXPR_BINARY, left_side);
|
||||
expr->binary_expr.operator = binaryop_from_token(operator_type);
|
||||
expr->binary_expr.left = left_side;
|
||||
expr->binary_expr.right = right_side;
|
||||
return expr;
|
||||
}
|
||||
|
||||
static Expr *parse_call_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(left && expr_ok(left));
|
||||
|
||||
Expr **params = NULL;
|
||||
advance_and_verify(context, TOKEN_LPAREN);
|
||||
if (context->tok.type != TOKEN_RPAREN)
|
||||
{
|
||||
if (!parse_param_list(context, ¶ms, 0)) return &poisoned_expr;
|
||||
}
|
||||
TRY_CONSUME_OR(TOKEN_RPAREN, "Expected the ending ')' here", &poisoned_expr);
|
||||
|
||||
Expr *call = EXPR_NEW_EXPR(EXPR_CALL, left);
|
||||
call->call_expr.function = left;
|
||||
call->call_expr.arguments = params;
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
|
||||
static Expr *parse_subscript_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(left && expr_ok(left));
|
||||
|
||||
advance_and_verify(context, TOKEN_LBRACKET);
|
||||
Expr *index = TRY_EXPR_OR(parse_expr(context), &poisoned_expr);
|
||||
CONSUME_OR(TOKEN_RBRACKET, &poisoned_expr);
|
||||
Expr *subscript_ast = EXPR_NEW_EXPR(EXPR_SUBSCRIPT, left);
|
||||
subscript_ast->subscript_expr.expr = left;
|
||||
subscript_ast->subscript_expr.index = index;
|
||||
return subscript_ast;
|
||||
}
|
||||
|
||||
|
||||
static Expr *parse_access_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(left && expr_ok(left));
|
||||
advance_and_verify(context, TOKEN_DOT);
|
||||
Expr *access_expr = EXPR_NEW_EXPR(EXPR_ACCESS, left);
|
||||
access_expr->access_expr.parent = left;
|
||||
access_expr->access_expr.sub_element = context->tok;
|
||||
TRY_CONSUME_OR(TOKEN_IDENT, "Expected identifier", &poisoned_expr);
|
||||
return access_expr;
|
||||
}
|
||||
|
||||
|
||||
static Expr *parse_identifier_with_path(Context *context, Path *path)
|
||||
{
|
||||
Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER, context->tok);
|
||||
expr->identifier_expr.identifier = context->tok.string;
|
||||
expr->identifier_expr.path = path;
|
||||
advance(context);
|
||||
return expr;
|
||||
}
|
||||
|
||||
static Expr *parse_identifier(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
return parse_identifier_with_path(context, NULL);
|
||||
}
|
||||
|
||||
static Expr *parse_type_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE, context->tok);
|
||||
advance_and_verify(context, TOKEN_TYPE);
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_expr);
|
||||
TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_expr);
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_expr);
|
||||
expr->type_expr.type = type;
|
||||
return expr;
|
||||
}
|
||||
|
||||
static Expr *parse_maybe_scope(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Path *path = parse_path_prefix(context);
|
||||
switch (context->tok.type)
|
||||
{
|
||||
case TOKEN_IDENT:
|
||||
case TOKEN_CT_IDENT:
|
||||
case TOKEN_CONST_IDENT:
|
||||
return parse_identifier_with_path(context, path);
|
||||
case TOKEN_TYPE_IDENT:
|
||||
return parse_type_identifier_with_path(context, path);
|
||||
default:
|
||||
SEMA_TOKEN_ERROR(context->tok, "Expected a type, function or constant.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
}
|
||||
|
||||
static Expr *parse_try_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok);
|
||||
advance_and_verify(context, TOKEN_TRY);
|
||||
try_expr->try_expr.expr = TRY_EXPR_OR(parse_precedence(context, PREC_TRY + 1), &poisoned_expr);
|
||||
if (try_consume(context, TOKEN_ELSE))
|
||||
{
|
||||
try_expr->try_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), &poisoned_expr);
|
||||
}
|
||||
return try_expr;
|
||||
}
|
||||
|
||||
static Expr *parse_integer(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Had left hand side");
|
||||
Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
|
||||
const char *string = context->tok.start;
|
||||
const char *end = string + source_range_len(context->tok.span);
|
||||
uint64_t i = 0;
|
||||
if (string[0] == '\'')
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8_t u8;
|
||||
uint16_t u16;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
uint8_t b[8];
|
||||
} bytes;
|
||||
int pos = 0;
|
||||
while (++string < end - 1)
|
||||
{
|
||||
if (*string == '\\')
|
||||
{
|
||||
if (*(++string) == 'x')
|
||||
{
|
||||
int hex = 0;
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
hex <<= 4U;
|
||||
char c = *(++string);
|
||||
if (c < 'A')
|
||||
{
|
||||
hex += c - '0';
|
||||
}
|
||||
else if (c < 'a')
|
||||
{
|
||||
hex += c - 'A' + 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
hex += c - 'a' + 10;
|
||||
}
|
||||
}
|
||||
bytes.b[pos++] = hex;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
bytes.b[pos++] = (unsigned)*string;
|
||||
}
|
||||
switch (pos)
|
||||
{
|
||||
case 1:
|
||||
expr_int->const_expr.i = bytes.u8;
|
||||
break;
|
||||
case 2:
|
||||
expr_int->const_expr.i = bytes.u16;
|
||||
break;
|
||||
case 4:
|
||||
expr_int->const_expr.i = bytes.u32;
|
||||
break;
|
||||
case 8:
|
||||
expr_int->const_expr.i = bytes.u64;
|
||||
break;
|
||||
}
|
||||
expr_int->const_expr.type = CONST_INT;
|
||||
expr_int->type = i > INT64_MAX ? type_compuint : type_compint;
|
||||
expr_int->resolve_status = RESOLVE_DONE;
|
||||
advance(context);
|
||||
return expr_int;
|
||||
}
|
||||
switch (source_range_len(context->tok.span) > 2 ? string[1] : '0')
|
||||
{
|
||||
case 'x':
|
||||
string += 2;
|
||||
while (string < end)
|
||||
{
|
||||
char c = *(string++);
|
||||
if (c == '_') continue;
|
||||
if (i > (UINT64_MAX >> 4U))
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
i <<= 4U;
|
||||
if (c < 'A')
|
||||
{
|
||||
i += c - '0';
|
||||
}
|
||||
else if (c < 'a')
|
||||
{
|
||||
i += c - 'A' + 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
i += c - 'a' + 10;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
string += 2;
|
||||
while (string < end)
|
||||
{
|
||||
char c = *(string++);
|
||||
if (c == '_') continue;
|
||||
if (i > (UINT64_MAX >> 3U))
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
i <<= (unsigned) 3;
|
||||
i += c - '0';
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
string += 2;
|
||||
while (string < end)
|
||||
{
|
||||
char c = *(string++);
|
||||
if (c == '_') continue;
|
||||
if (i > (UINT64_MAX >> 1U))
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
i <<= (unsigned) 1;
|
||||
i += c - '0';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
while (string < end)
|
||||
{
|
||||
char c = *(string++);
|
||||
if (c == '_') continue;
|
||||
if (i > (UINT64_MAX / 10))
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number.");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
i *= 10;
|
||||
i += c - '0';
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
expr_int->const_expr.i = i;
|
||||
expr_int->const_expr.type = CONST_INT;
|
||||
expr_int->type = i > INT64_MAX ? type_compuint : type_compint;
|
||||
advance(context);
|
||||
return expr_int;
|
||||
}
|
||||
|
||||
|
||||
static Expr *parse_double(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Had left hand side");
|
||||
Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
|
||||
char *end = NULL;
|
||||
// IMPROVE
|
||||
long double fval = strtold(context->tok.start, &end);
|
||||
if (end != source_range_len(context->tok.span) + context->tok.start)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Invalid float value");
|
||||
return &poisoned_expr;
|
||||
}
|
||||
advance(context);
|
||||
number->const_expr.f = fval;
|
||||
number->type = type_compfloat;
|
||||
number->const_expr.type = CONST_FLOAT;
|
||||
return number;
|
||||
}
|
||||
|
||||
static int append_esc_string_token(char *restrict dest, const char *restrict src, size_t *pos)
|
||||
{
|
||||
int scanned = 0;
|
||||
uint64_t unicode_char = 0;
|
||||
switch (src[0])
|
||||
{
|
||||
case 'a':
|
||||
dest[(*pos)++] = '\a';
|
||||
return 1;
|
||||
case 'b':
|
||||
dest[(*pos)++] = '\b';
|
||||
return 1;
|
||||
case 'e':
|
||||
dest[(*pos)++] = 0x1b;
|
||||
return 1;
|
||||
case 'f':
|
||||
dest[(*pos)++] = '\f';
|
||||
return 1;
|
||||
case 'n':
|
||||
dest[(*pos)++] = '\n';
|
||||
return 1;
|
||||
case 'r':
|
||||
dest[(*pos)++] = '\r';
|
||||
return 1;
|
||||
case 't':
|
||||
dest[(*pos)++] = '\t';
|
||||
return 1;
|
||||
case 'x':
|
||||
{
|
||||
int h = char_to_nibble(src[1]);
|
||||
int l = char_to_nibble(src[2]);
|
||||
if (h < 0 || l < 0) return -1;
|
||||
unicode_char = ((unsigned) h << 4U) + l;
|
||||
scanned = 3;
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
int x1 = char_to_nibble(src[1]);
|
||||
int x2 = char_to_nibble(src[2]);
|
||||
int x3 = char_to_nibble(src[3]);
|
||||
int x4 = char_to_nibble(src[4]);
|
||||
if (x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0) return -1;
|
||||
unicode_char = ((unsigned) x1 << 12U) + ((unsigned) x2 << 8U) + ((unsigned) x3 << 4U) + x4;
|
||||
scanned = 5;
|
||||
break;
|
||||
}
|
||||
case 'U':
|
||||
{
|
||||
int x1 = char_to_nibble(src[1]);
|
||||
int x2 = char_to_nibble(src[2]);
|
||||
int x3 = char_to_nibble(src[3]);
|
||||
int x4 = char_to_nibble(src[4]);
|
||||
int x5 = char_to_nibble(src[5]);
|
||||
int x6 = char_to_nibble(src[6]);
|
||||
int x7 = char_to_nibble(src[7]);
|
||||
int x8 = char_to_nibble(src[8]);
|
||||
if (x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 || x5 < 0 || x6 < 0 || x7 < 0 || x8 < 0) return -1;
|
||||
unicode_char = ((unsigned) x1 << 28U) + ((unsigned) x2 << 24U) + ((unsigned) x3 << 20U) + ((unsigned) x4 << 16U) +
|
||||
((unsigned) x5 << 12U) + ((unsigned) x6 << 8U) + ((unsigned) x7 << 4U) + x8;
|
||||
scanned = 9;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dest[(*pos)++] = src[0];
|
||||
return 1;
|
||||
}
|
||||
if (unicode_char < 0x80U)
|
||||
{
|
||||
dest[(*pos)++] = (char)unicode_char;
|
||||
}
|
||||
else if (unicode_char < 0x800U)
|
||||
{
|
||||
dest[(*pos)++] = (char)(0xC0U | (unicode_char >> 6U));
|
||||
dest[(*pos)++] = (char)(0x80U | (unicode_char & 0x3FU));
|
||||
}
|
||||
else if (unicode_char < 0x10000U)
|
||||
{
|
||||
dest[(*pos)++] = (char)(0xE0U | (unicode_char >> 12U));
|
||||
dest[(*pos)++] = (char)(0x80U | ((unicode_char >> 6U) & 0x3FU));
|
||||
dest[(*pos)++] = (char)(0x80U | (unicode_char & 0x3FU));
|
||||
}
|
||||
else
|
||||
{
|
||||
dest[(*pos)++] = (char)(0xF0U | (unicode_char >> 18U));
|
||||
dest[(*pos)++] = (char)(0x80U | ((unicode_char >> 12U) & 0x3FU));
|
||||
dest[(*pos)++] = (char)(0x80U | ((unicode_char >> 6U) & 0x3FU));
|
||||
dest[(*pos)++] = (char)(0x80U | (unicode_char & 0x3FU));
|
||||
}
|
||||
return scanned;
|
||||
}
|
||||
|
||||
static Expr *parse_string_literal(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Had left hand side");
|
||||
Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
|
||||
expr_string->resolve_status = RESOLVE_DONE;
|
||||
expr_string->type = type_string;
|
||||
|
||||
char *str = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
while (context->tok.type == TOKEN_STRING)
|
||||
{
|
||||
char *new_string = malloc_arena(len + source_range_len(context->tok.span));
|
||||
if (str) memcpy(new_string, str, len);
|
||||
str = new_string;
|
||||
for (unsigned i = 1; i < source_range_len(context->tok.span) - 1; i++)
|
||||
{
|
||||
if (context->tok.string[i] == '\\')
|
||||
{
|
||||
i++;
|
||||
i += append_esc_string_token(str, context->tok.string + i, &len) - 1;
|
||||
continue;
|
||||
}
|
||||
str[len++] = context->tok.string[i];
|
||||
}
|
||||
advance_and_verify(context, TOKEN_STRING);
|
||||
}
|
||||
|
||||
assert(str);
|
||||
str[len] = '\0';
|
||||
expr_string->const_expr.string.chars = str;
|
||||
expr_string->const_expr.string.len = len;
|
||||
expr_string->type = type_string;
|
||||
expr_string->const_expr.type = CONST_STRING;
|
||||
return expr_string;
|
||||
}
|
||||
|
||||
static Expr *parse_bool(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Had left hand side");
|
||||
Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
|
||||
number->const_expr = (ExprConst) { .b = context->tok.type == TOKEN_TRUE, .type = CONST_BOOL };
|
||||
number->type = type_bool;
|
||||
number->resolve_status = RESOLVE_DONE;
|
||||
advance(context);
|
||||
return number;
|
||||
}
|
||||
|
||||
static Expr *parse_nil(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Had left hand side");
|
||||
Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
|
||||
number->const_expr.type = CONST_NIL;
|
||||
number->type = type_voidptr;
|
||||
advance(context);
|
||||
return number;
|
||||
}
|
||||
|
||||
/**
|
||||
* function_block
|
||||
* : '({' stmt_list '})'
|
||||
*/
|
||||
static Expr* parse_expr_block(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Had left hand side");
|
||||
Expr *expr = EXPR_NEW_TOKEN(EXPR_EXPR_BLOCK, context->tok);
|
||||
advance_and_verify(context, TOKEN_LPARBRA);
|
||||
while (!try_consume(context, TOKEN_RPARBRA))
|
||||
{
|
||||
Ast *stmt = parse_stmt(context);
|
||||
if (!ast_ok(stmt)) return &poisoned_expr;
|
||||
vec_add(expr->expr_block.stmts, stmt);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
ParseRule rules[TOKEN_EOF + 1] = {
|
||||
[TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY },
|
||||
[TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY },
|
||||
[TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL },
|
||||
[TOKEN_MINUSMINUS] = { parse_unary_expr, parse_post_unary, PREC_CALL },
|
||||
[TOKEN_LPAREN] = { parse_grouping_expr, parse_call_expr, PREC_CALL },
|
||||
[TOKEN_LPARBRA] = { parse_expr_block, NULL, PREC_NONE },
|
||||
[TOKEN_TYPE] = { parse_type_expr, NULL, PREC_NONE },
|
||||
[TOKEN_CAST] = { parse_cast_expr, NULL, PREC_NONE },
|
||||
[TOKEN_TRY] = { parse_try_expr, NULL, PREC_TRY },
|
||||
//[TOKEN_SIZEOF] = { parse_sizeof, NULL, PREC_NONE },
|
||||
[TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL },
|
||||
[TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE },
|
||||
[TOKEN_MINUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE },
|
||||
[TOKEN_PLUS] = { NULL, parse_binary, PREC_ADDITIVE },
|
||||
[TOKEN_PLUS_MOD] = { NULL, parse_binary, PREC_ADDITIVE },
|
||||
[TOKEN_DIV] = { NULL, parse_binary, PREC_MULTIPLICATIVE },
|
||||
[TOKEN_MOD] = { NULL, parse_binary, PREC_MULTIPLICATIVE },
|
||||
[TOKEN_STAR] = { parse_unary_expr, parse_binary, PREC_MULTIPLICATIVE },
|
||||
[TOKEN_MULT_MOD] = { NULL, parse_binary, PREC_MULTIPLICATIVE },
|
||||
[TOKEN_DOT] = { NULL, parse_access_expr, PREC_CALL },
|
||||
[TOKEN_NOT] = { parse_unary_expr, NULL, PREC_UNARY },
|
||||
[TOKEN_BIT_NOT] = { parse_unary_expr, NULL, PREC_UNARY },
|
||||
[TOKEN_BIT_XOR] = { NULL, parse_binary, PREC_BIT },
|
||||
[TOKEN_BIT_OR] = { NULL, parse_binary, PREC_BIT },
|
||||
[TOKEN_AMP] = { parse_unary_expr, parse_binary, PREC_BIT },
|
||||
[TOKEN_EQEQ] = { NULL, parse_binary, PREC_RELATIONAL },
|
||||
[TOKEN_NOT_EQUAL] = { NULL, parse_binary, PREC_RELATIONAL },
|
||||
[TOKEN_GREATER] = { NULL, parse_binary, PREC_RELATIONAL },
|
||||
[TOKEN_GREATER_EQ] = { NULL, parse_binary, PREC_RELATIONAL },
|
||||
[TOKEN_LESS] = { NULL, parse_binary, PREC_RELATIONAL },
|
||||
[TOKEN_LESS_EQ] = { NULL, parse_binary, PREC_RELATIONAL },
|
||||
[TOKEN_SHL] = { NULL, parse_binary, PREC_SHIFT },
|
||||
[TOKEN_SHR] = { NULL, parse_binary, PREC_SHIFT },
|
||||
[TOKEN_TRUE] = { parse_bool, NULL, PREC_NONE },
|
||||
[TOKEN_FALSE] = { parse_bool, NULL, PREC_NONE },
|
||||
[TOKEN_NIL] = { parse_nil, NULL, PREC_NONE },
|
||||
[TOKEN_INTEGER] = { parse_integer, NULL, PREC_NONE },
|
||||
[TOKEN_IDENT] = { parse_maybe_scope, NULL, PREC_NONE },
|
||||
[TOKEN_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_CT_IDENT] = { parse_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_AT] = { parse_macro_expr, NULL, PREC_UNARY },
|
||||
[TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE },
|
||||
[TOKEN_FLOAT] = { parse_double, NULL, PREC_NONE },
|
||||
[TOKEN_OR] = { NULL, parse_binary, PREC_LOGICAL },
|
||||
[TOKEN_AND] = { NULL, parse_binary, PREC_LOGICAL },
|
||||
[TOKEN_EQ] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_PLUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_PLUS_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_MINUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_MINUS_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_MULT_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_MULT_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_DIV_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_BIT_XOR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_BIT_AND_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_BIT_OR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_SHR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
[TOKEN_SHL_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT },
|
||||
};
|
||||
865
src/compiler/parse_stmt.c
Normal file
865
src/compiler/parse_stmt.c
Normal file
@@ -0,0 +1,865 @@
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
#include "parser_internal.h"
|
||||
|
||||
|
||||
#pragma mark --- Internal functions
|
||||
|
||||
|
||||
/**
|
||||
* declaration_stmt
|
||||
* : declaration ';'
|
||||
*/
|
||||
static inline Ast *parse_declaration_stmt(Context *context)
|
||||
{
|
||||
Ast *decl_stmt = AST_NEW_TOKEN(AST_DECLARE_STMT, context->tok);
|
||||
decl_stmt->declare_stmt = TRY_DECL_OR(parse_decl(context), &poisoned_ast);
|
||||
CONSUME_OR(TOKEN_EOS, &poisoned_ast);
|
||||
return decl_stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* control_expression
|
||||
* : decl_or_expr_list
|
||||
* | declaration_list ';' decl_or_expr_list
|
||||
* ;
|
||||
*/
|
||||
static inline bool parse_control_expression(Context *context, Ast **decls, Ast **exprs)
|
||||
{
|
||||
*exprs = TRY_AST_OR(parse_decl_expr_list(context), false);
|
||||
|
||||
if (!try_consume(context, TOKEN_EOS))
|
||||
{
|
||||
*decls = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
*decls = *exprs;
|
||||
|
||||
*exprs = TRY_AST_OR(parse_decl_expr_list(context), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline Ast* parse_asm_stmt(Context *context)
|
||||
{
|
||||
// TODO
|
||||
SEMA_TOKEN_ERROR(context->tok, "ASM not supported yet.");
|
||||
return &poisoned_ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_stmt
|
||||
* : DO statement WHILE '(' expression ')' ';'
|
||||
*/
|
||||
static inline Ast* parse_do_stmt(Context *context)
|
||||
{
|
||||
Ast *do_ast = AST_NEW_TOKEN(AST_DO_STMT, context->tok);
|
||||
|
||||
advance_and_verify(context, TOKEN_DO);
|
||||
|
||||
do_ast->do_stmt.body = TRY_AST(parse_stmt(context));
|
||||
|
||||
CONSUME_OR(TOKEN_WHILE, &poisoned_ast);
|
||||
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_ast);
|
||||
do_ast->do_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast);
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_ast);
|
||||
|
||||
CONSUME_OR(TOKEN_EOS, &poisoned_ast);
|
||||
|
||||
return do_ast;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* catch_stmt
|
||||
* : CATCH '(' ERROR ident ')' statement
|
||||
* | CATCH '(' type_expression ident ')' statement
|
||||
* ;
|
||||
*/
|
||||
static inline Ast* parse_catch_stmt(Context *context)
|
||||
{
|
||||
Ast *catch_stmt = AST_NEW_TOKEN(AST_CATCH_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_CATCH);
|
||||
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_ast);
|
||||
|
||||
TypeInfo *type = NULL;
|
||||
if (!try_consume(context, TOKEN_ERROR_TYPE))
|
||||
{
|
||||
type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_ast);
|
||||
}
|
||||
EXPECT_IDENT_FOR_OR("error parameter", &poisoned_ast);
|
||||
Decl *decl = decl_new_var(context->tok, type, VARDECL_PARAM, VISIBLE_LOCAL);
|
||||
catch_stmt->catch_stmt.error_param = decl;
|
||||
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_ast);
|
||||
catch_stmt->catch_stmt.body = TRY_AST(parse_stmt(context));
|
||||
return catch_stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* defer_stmt
|
||||
* : DEFER statement
|
||||
*/
|
||||
static inline Ast* parse_defer_stmt(Context *context)
|
||||
{
|
||||
advance_and_verify(context, TOKEN_DEFER);
|
||||
Ast *defer_stmt = AST_NEW_TOKEN(AST_DEFER_STMT, context->tok);
|
||||
defer_stmt->defer_stmt.body = TRY_AST(parse_stmt(context));
|
||||
return defer_stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* while_stmt
|
||||
* : WHILE '(' control_expression ')' statement
|
||||
*/
|
||||
static inline Ast* parse_while_stmt(Context *context)
|
||||
{
|
||||
Ast *while_ast = AST_NEW_TOKEN(AST_WHILE_STMT, context->tok);
|
||||
|
||||
advance_and_verify(context, TOKEN_WHILE);
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_ast);
|
||||
if (!parse_control_expression(context, &while_ast->while_stmt.decl, &while_ast->while_stmt.cond)) return &poisoned_ast;
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_ast);
|
||||
while_ast->while_stmt.body = TRY_AST(parse_stmt(context));
|
||||
return while_ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* if_stmt
|
||||
* : IF '(' control_expression ')' statement
|
||||
* | IF '(' control_expression ')' compound_statement ELSE compound_statement
|
||||
* ;
|
||||
*/
|
||||
static inline Ast* parse_if_stmt(Context *context)
|
||||
{
|
||||
Ast *if_ast = AST_NEW_TOKEN(AST_IF_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_IF);
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_ast);
|
||||
if (!parse_control_expression(context, &if_ast->if_stmt.decl, &if_ast->if_stmt.cond)) return &poisoned_ast;
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_ast);
|
||||
Ast *stmt = TRY_AST(parse_stmt(context));
|
||||
if_ast->if_stmt.then_body = stmt;
|
||||
if (!try_consume(context, TOKEN_ELSE))
|
||||
{
|
||||
return if_ast;
|
||||
}
|
||||
if_ast->if_stmt.else_body = TRY_AST(parse_stmt(context));
|
||||
return if_ast;
|
||||
}
|
||||
|
||||
static inline bool token_type_ends_case(TokenType type)
|
||||
{
|
||||
return type == TOKEN_CASE || type == TOKEN_DEFAULT || type == TOKEN_RBRACE;
|
||||
}
|
||||
|
||||
static inline Ast *parse_case_stmts(Context *context)
|
||||
{
|
||||
Ast *compound = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok);
|
||||
while (!token_type_ends_case(context->tok.type))
|
||||
{
|
||||
Ast *stmt = TRY_AST(parse_stmt(context));
|
||||
vec_add(compound->compound_stmt.stmts, stmt);
|
||||
}
|
||||
return compound;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* case_stmt
|
||||
* : CASE constant_expression ':' case_stmts
|
||||
*/
|
||||
static inline Ast* parse_case_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok);
|
||||
advance(context);
|
||||
Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), &poisoned_ast);
|
||||
ast->case_stmt.expr = expr;
|
||||
TRY_CONSUME(TOKEN_COLON, "Missing ':' after case");
|
||||
extend_ast_with_prev_token(context, ast);
|
||||
ast->case_stmt.body = TRY_AST(parse_case_stmts(context));
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* default_stmt
|
||||
* : DEFAULT ':' case_stmts
|
||||
*/
|
||||
static inline Ast *parse_default_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_DEFAULT_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_DEFAULT);
|
||||
TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", &poisoned_ast);
|
||||
extend_ast_with_prev_token(context, ast);
|
||||
ast->case_stmt.body = TRY_AST(parse_case_stmts(context));
|
||||
ast->case_stmt.value_type = CASE_VALUE_DEFAULT;
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* switch_body
|
||||
* : case_stmt
|
||||
* | default_stmt
|
||||
* | case_stmt switch_body
|
||||
* | default_stmt switch body
|
||||
* ;
|
||||
*/
|
||||
static inline bool parse_switch_body(Context *context, Ast *switch_ast)
|
||||
{
|
||||
Ast *result;
|
||||
switch (context->tok.type)
|
||||
{
|
||||
case TOKEN_CASE:
|
||||
result = TRY_AST_OR(parse_case_stmt(context), false);
|
||||
break;
|
||||
case TOKEN_DEFAULT:
|
||||
result = TRY_AST_OR(parse_default_stmt(context), false);
|
||||
break;
|
||||
default:
|
||||
SEMA_TOKEN_ERROR(context->tok, "A 'case' or 'default' would be needed here, '%.*s' is not allowed.", source_range_len(context->tok.span), context->tok.start);
|
||||
return false;
|
||||
}
|
||||
vec_add(switch_ast->switch_stmt.cases, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* switch
|
||||
* : SWITCH '(' control_expression ')' '{' switch_body '}'
|
||||
*/
|
||||
static inline Ast* parse_switch_stmt(Context *context)
|
||||
{
|
||||
Ast *switch_ast = AST_NEW_TOKEN(AST_SWITCH_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_SWITCH);
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_ast);
|
||||
if (!parse_control_expression(context, &switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return &poisoned_ast;
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_ast);
|
||||
CONSUME_OR(TOKEN_LBRACE, &poisoned_ast);
|
||||
while (!try_consume(context, TOKEN_RBRACE))
|
||||
{
|
||||
if (!parse_switch_body(context, switch_ast)) return &poisoned_ast;
|
||||
}
|
||||
return switch_ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* for_statement
|
||||
* : FOR '(' decl_expr_list ';' expression ';' ')' statement
|
||||
* | FOR '(' decl_expr_list ';' ';' expression_list ')' statement
|
||||
* | FOR '(' decl_expr_list ';' expression ';' expression_list ')' statement
|
||||
* | FOR '(' ';' expression ';' ')' statement
|
||||
* | FOR '(' ';' ';' expression_list ')' statement
|
||||
* | FOR '(' ';' expression ';' expression_list ')' statement
|
||||
* ;
|
||||
*/
|
||||
static inline Ast* parse_for_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_FOR_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_FOR);
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_ast);
|
||||
|
||||
if (context->tok.type != TOKEN_EOS)
|
||||
{
|
||||
ast->for_stmt.init = TRY_AST(parse_decl_expr_list(context));
|
||||
}
|
||||
else
|
||||
{
|
||||
ast->for_stmt.init = NULL;
|
||||
}
|
||||
|
||||
CONSUME_OR(TOKEN_EOS, &poisoned_ast);
|
||||
|
||||
if (context->tok.type != TOKEN_EOS)
|
||||
{
|
||||
ast->for_stmt.cond = TRY_EXPR_OR(parse_expr(context), &poisoned_ast);
|
||||
}
|
||||
|
||||
CONSUME_OR(TOKEN_EOS, &poisoned_ast);
|
||||
|
||||
if (context->tok.type != TOKEN_RPAREN)
|
||||
{
|
||||
ast->for_stmt.incr = parse_expression_list(context);
|
||||
}
|
||||
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_ast);
|
||||
|
||||
extend_ast_with_prev_token(context, ast);
|
||||
ast->for_stmt.body = TRY_AST(parse_stmt(context));
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* goto_stmt
|
||||
* : GOTO ct_ident EOS
|
||||
* ;
|
||||
*/
|
||||
static inline Ast* parse_goto_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_GOTO_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_GOTO);
|
||||
ast->goto_stmt.label_name = context->tok.string;
|
||||
if (!consume_const_name(context, "label")) return &poisoned_ast;
|
||||
RETURN_AFTER_EOS(ast);
|
||||
}
|
||||
|
||||
/**
|
||||
* continue_stmt
|
||||
* : CONTINUE EOS
|
||||
*/
|
||||
static inline Ast* parse_continue_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_CONTINUE_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_CONTINUE);
|
||||
RETURN_AFTER_EOS(ast);
|
||||
}
|
||||
|
||||
/**
|
||||
* next_stmt
|
||||
* : NEXT EOS
|
||||
*/
|
||||
static inline Ast* parse_next_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_NEXT_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_NEXT);
|
||||
RETURN_AFTER_EOS(ast);
|
||||
}
|
||||
|
||||
/**
|
||||
* break_stmt
|
||||
* : BREAK EOS
|
||||
*/
|
||||
static inline Ast* parse_break_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_BREAK_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_BREAK);
|
||||
RETURN_AFTER_EOS(ast);
|
||||
}
|
||||
|
||||
/**
|
||||
* expr_stmt
|
||||
* : expression EOS
|
||||
*/
|
||||
static inline Ast *parse_expr_stmt(Context *context)
|
||||
{
|
||||
Ast *stmt = AST_NEW_TOKEN(AST_EXPR_STMT, context->tok);
|
||||
stmt->expr_stmt = TRY_EXPR_OR(parse_expr(context), &poisoned_ast);
|
||||
TRY_CONSUME_EOS();
|
||||
return stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* ct_else_stmt
|
||||
* : CT_ELSE compound_stmt
|
||||
*/
|
||||
static inline Ast* parse_ct_else_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_CT_ELSE_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_CT_ELSE);
|
||||
ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context));
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* ct_elif_stmt
|
||||
* : CT_ELIF '(' expression ')' compound_statement
|
||||
*/
|
||||
static inline Ast *parse_ct_elif_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_CT_ELIF_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_CT_ELIF);
|
||||
|
||||
ast->ct_elif_stmt.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast);
|
||||
|
||||
ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context));
|
||||
|
||||
if (context->tok.type == TOKEN_CT_ELIF)
|
||||
{
|
||||
ast->ct_elif_stmt.elif = TRY_AST(parse_ct_elif_stmt(context));
|
||||
}
|
||||
else if (context->tok.type == TOKEN_CT_ELSE)
|
||||
{
|
||||
ast->ct_elif_stmt.elif = TRY_AST(parse_ct_else_stmt(context));
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
/**
|
||||
* ct_if_stmt
|
||||
* : CT_IF '(' expression ')' compound_stmt
|
||||
* | CT_IF '(' expression ')' compound_stmt elif_stmt
|
||||
* | CT_IF '(' expression ')' compound_stmt else_stmt
|
||||
* ;
|
||||
*/
|
||||
static inline Ast* parse_ct_if_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_CT_IF_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_CT_IF);
|
||||
ast->ct_if_stmt.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast);
|
||||
ast->ct_if_stmt.then = TRY_AST(parse_compound_stmt(context));
|
||||
if (context->tok.type == TOKEN_CT_ELIF)
|
||||
{
|
||||
ast->ct_if_stmt.elif = TRY_AST(parse_ct_elif_stmt(context));
|
||||
}
|
||||
else if (context->tok.type == TOKEN_CT_ELSE)
|
||||
{
|
||||
ast->ct_if_stmt.elif = TRY_AST(parse_ct_else_stmt(context));
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* label_stmt
|
||||
* : ct_label ':'
|
||||
*/
|
||||
static inline Ast *parse_label_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_LABEL, context->tok);
|
||||
ast->label_stmt.name = context->tok.string;
|
||||
advance_and_verify(context, TOKEN_CONST_IDENT);
|
||||
advance_and_verify(context, TOKEN_COLON);
|
||||
return extend_ast_with_prev_token(context, ast);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return_stmt
|
||||
* : RETURN expression EOS
|
||||
* | RETURN EOS
|
||||
* ;
|
||||
*/
|
||||
static inline Ast *parse_return_stmt(Context *context)
|
||||
{
|
||||
advance_and_verify(context, TOKEN_RETURN);
|
||||
Ast *ast = AST_NEW_TOKEN(AST_RETURN_STMT, context->tok);
|
||||
ast->exit = EXIT_RETURN;
|
||||
ast->return_stmt.defer = NULL;
|
||||
if (try_consume(context, TOKEN_EOS))
|
||||
{
|
||||
ast->return_stmt.expr = NULL;
|
||||
return ast;
|
||||
}
|
||||
ast->return_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast);
|
||||
RETURN_AFTER_EOS(ast);
|
||||
}
|
||||
|
||||
/**
|
||||
* throw_stmt
|
||||
* : THROW expr EOS
|
||||
*/
|
||||
static inline Ast *parse_throw_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_THROW_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_THROW);
|
||||
ast->throw_stmt.throw_value = TRY_EXPR_OR(parse_expr(context), &poisoned_ast);
|
||||
RETURN_AFTER_EOS(ast);
|
||||
}
|
||||
|
||||
/**
|
||||
* volatile_stmt
|
||||
* : VOLATILE compound_stmt
|
||||
*/
|
||||
static Ast *parse_volatile_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_VOLATILE_STMT, context->tok);
|
||||
ast->volatile_stmt = TRY_AST_OR(parse_compound_stmt(context), &poisoned_ast);
|
||||
return ast;
|
||||
}
|
||||
|
||||
static inline bool is_valid_try_statement(TokenType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TOKEN_SWITCH:
|
||||
case TOKEN_IF:
|
||||
case TOKEN_FOR:
|
||||
case TOKEN_WHILE:
|
||||
case TOKEN_DO:
|
||||
case TOKEN_RETURN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline Ast *parse_decl_or_expr_stmt(Context *context)
|
||||
{
|
||||
Expr *expr = NULL;
|
||||
TypeInfo *type = NULL;
|
||||
|
||||
if (!parse_type_or_expr(context, &expr, &type)) return &poisoned_ast;
|
||||
|
||||
Ast *ast;
|
||||
if (expr)
|
||||
{
|
||||
ast = AST_NEW(AST_EXPR_STMT, expr->span);
|
||||
ast->expr_stmt = expr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, false, type), &poisoned_ast);
|
||||
ast = AST_NEW(AST_DECLARE_STMT, decl->span);
|
||||
ast->declare_stmt = decl;
|
||||
}
|
||||
CONSUME_OR(TOKEN_EOS, &poisoned_ast);
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* ct_for_stmt
|
||||
* : CT_FOR '(' CT_IDENT IN expression ')' statement
|
||||
* | CT_FOR '(' CT_IDENT, CT_IDENT IN expression ')' statement
|
||||
* ;
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static inline Ast* parse_ct_for_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_CT_FOR_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_CT_FOR);
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_ast);
|
||||
if (context->next_tok.type == TOKEN_COMMA)
|
||||
{
|
||||
ast->ct_for_stmt.index = context->tok;
|
||||
TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time index variable", &poisoned_ast);
|
||||
advance_and_verify(context, TOKEN_COMMA);
|
||||
}
|
||||
ast->ct_for_stmt.value = context->tok;
|
||||
TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time variable", &poisoned_ast);
|
||||
TRY_CONSUME_OR(TOKEN_IN, "Expected 'in'.", &poisoned_ast);
|
||||
ast->ct_for_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast);
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_ast);
|
||||
ast->ct_for_stmt.body = TRY_AST(parse_stmt(context));
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* CTSWITCH '(' expression ')' '{' ct_switch_body '}'
|
||||
*
|
||||
* ct_switch_body
|
||||
* : ct_case_statement
|
||||
* | ct_switch_body ct_case_statement
|
||||
* ;
|
||||
*
|
||||
* ct_case_statement
|
||||
* : CTCASE type_list ':' statement
|
||||
* | CTDEFAULT ':' statement
|
||||
* ;
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static inline Ast* parse_ct_switch_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_CT_SWITCH_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_CT_SWITCH);
|
||||
ast->ct_switch_stmt.cond = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast);
|
||||
CONSUME_OR(TOKEN_LBRACE, &poisoned_ast);
|
||||
Ast **switch_statements = NULL;
|
||||
Ast *stmt = &poisoned_ast;
|
||||
while (stmt)
|
||||
{
|
||||
switch (context->tok.type)
|
||||
{
|
||||
case TOKEN_CT_CASE:
|
||||
stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok);
|
||||
advance(context);
|
||||
while (1)
|
||||
{
|
||||
TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_ast);
|
||||
vec_add(stmt->ct_case_stmt.types, type);
|
||||
if (!try_consume(context, TOKEN_COMMA)) break;
|
||||
}
|
||||
CONSUME_OR(TOKEN_COLON, &poisoned_ast);
|
||||
stmt->ct_case_stmt.body = TRY_AST_OR(parse_stmt(context), &poisoned_ast);
|
||||
vec_add(switch_statements, stmt);
|
||||
break;
|
||||
case TOKEN_CT_DEFAULT:
|
||||
stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok);
|
||||
advance(context);
|
||||
CONSUME_OR(TOKEN_COLON, &poisoned_ast);
|
||||
stmt->ct_default_stmt = TRY_AST_OR(parse_stmt(context), &poisoned_ast);
|
||||
vec_add(switch_statements, stmt);
|
||||
break;
|
||||
case TOKEN_RBRACE:
|
||||
stmt = NULL;
|
||||
break;
|
||||
default:
|
||||
SEMA_TOKEN_ERROR(context->tok, "Expected $case or $default.");
|
||||
return &poisoned_ast;
|
||||
}
|
||||
}
|
||||
CONSUME_OR(TOKEN_RBRACE, &poisoned_ast);
|
||||
ast->ct_switch_stmt.body = switch_statements;
|
||||
return ast;
|
||||
}
|
||||
|
||||
#pragma mark --- External functions
|
||||
|
||||
Ast *parse_stmt(Context *context)
|
||||
{
|
||||
switch (context->tok.type)
|
||||
{
|
||||
case TOKEN_LBRACE:
|
||||
return parse_compound_stmt(context);
|
||||
case TOKEN_HALF:
|
||||
case TOKEN_QUAD:
|
||||
SEMA_TOKEN_ERROR(context->next_tok, "Type is unsupported by platform.");
|
||||
advance(context);
|
||||
return &poisoned_ast;
|
||||
case TOKEN_VOID:
|
||||
case TOKEN_BYTE:
|
||||
case TOKEN_BOOL:
|
||||
case TOKEN_CHAR:
|
||||
case TOKEN_DOUBLE:
|
||||
case TOKEN_FLOAT:
|
||||
case TOKEN_INT:
|
||||
case TOKEN_ISIZE:
|
||||
case TOKEN_LONG:
|
||||
case TOKEN_SHORT:
|
||||
case TOKEN_UINT:
|
||||
case TOKEN_ULONG:
|
||||
case TOKEN_USHORT:
|
||||
case TOKEN_USIZE:
|
||||
case TOKEN_C_SHORT:
|
||||
case TOKEN_C_INT:
|
||||
case TOKEN_C_LONG:
|
||||
case TOKEN_C_LONGLONG:
|
||||
case TOKEN_C_USHORT:
|
||||
case TOKEN_C_UINT:
|
||||
case TOKEN_C_ULONG:
|
||||
case TOKEN_C_ULONGLONG:
|
||||
case TOKEN_TYPE_IDENT:
|
||||
if (context->next_tok.type == TOKEN_DOT || context->next_tok.type == TOKEN_LBRACE)
|
||||
{
|
||||
return parse_expr_stmt(context);
|
||||
}
|
||||
return parse_declaration_stmt(context);
|
||||
case TOKEN_LOCAL: // Local means declaration!
|
||||
case TOKEN_CONST: // Const means declaration!
|
||||
return parse_declaration_stmt(context);
|
||||
case TOKEN_TYPE:
|
||||
return parse_decl_or_expr_stmt(context);
|
||||
case TOKEN_CONST_IDENT:
|
||||
if (context->next_tok.type == TOKEN_COLON)
|
||||
{
|
||||
return parse_label_stmt(context);
|
||||
}
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_AT:
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_IDENT:
|
||||
if (context->next_tok.type == TOKEN_SCOPE)
|
||||
{
|
||||
return parse_decl_or_expr_stmt(context);
|
||||
}
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_RETURN:
|
||||
return parse_return_stmt(context);
|
||||
case TOKEN_IF:
|
||||
return parse_if_stmt(context);
|
||||
case TOKEN_WHILE:
|
||||
return parse_while_stmt(context);
|
||||
case TOKEN_DEFER:
|
||||
return parse_defer_stmt(context);
|
||||
case TOKEN_SWITCH:
|
||||
return parse_switch_stmt(context);
|
||||
case TOKEN_GOTO:
|
||||
return parse_goto_stmt(context);
|
||||
case TOKEN_DO:
|
||||
return parse_do_stmt(context);
|
||||
case TOKEN_FOR:
|
||||
return parse_for_stmt(context);
|
||||
case TOKEN_CATCH:
|
||||
return parse_catch_stmt(context);
|
||||
case TOKEN_TRY:
|
||||
if (is_valid_try_statement(context->next_tok.type))
|
||||
{
|
||||
Token token = context->tok;
|
||||
advance(context);
|
||||
Ast *stmt = TRY_AST(parse_stmt(context));
|
||||
Ast *try_ast = AST_NEW_TOKEN(AST_TRY_STMT, token);
|
||||
try_ast->try_stmt = stmt;
|
||||
return try_ast;
|
||||
}
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_CONTINUE:
|
||||
return parse_continue_stmt(context);
|
||||
case TOKEN_CASE:
|
||||
SEMA_TOKEN_ERROR(context->tok, "'case' was found outside of 'switch', did you mismatch a '{ }' pair?");
|
||||
advance(context);
|
||||
return &poisoned_ast;
|
||||
case TOKEN_BREAK:
|
||||
return parse_break_stmt(context);
|
||||
case TOKEN_NEXT:
|
||||
return parse_next_stmt(context);
|
||||
case TOKEN_ASM:
|
||||
return parse_asm_stmt(context);
|
||||
case TOKEN_DEFAULT:
|
||||
SEMA_TOKEN_ERROR(context->tok, "'default' was found outside of 'switch', did you mismatch a '{ }' pair?");
|
||||
advance(context);
|
||||
return &poisoned_ast;
|
||||
case TOKEN_CT_IF:
|
||||
return parse_ct_if_stmt(context);
|
||||
case TOKEN_CT_SWITCH:
|
||||
return parse_ct_switch_stmt(context);
|
||||
case TOKEN_CT_FOR:
|
||||
return parse_ct_for_stmt(context);
|
||||
case TOKEN_THROW:
|
||||
return parse_throw_stmt(context);
|
||||
case TOKEN_VOLATILE:
|
||||
return parse_volatile_stmt(context);
|
||||
case TOKEN_STAR:
|
||||
case TOKEN_AMP:
|
||||
case TOKEN_INTEGER:
|
||||
case TOKEN_BIT_NOT:
|
||||
case TOKEN_BIT_OR:
|
||||
case TOKEN_BIT_XOR:
|
||||
case TOKEN_LPAREN:
|
||||
case TOKEN_MINUS:
|
||||
case TOKEN_NOT:
|
||||
case TOKEN_OR:
|
||||
case TOKEN_PLUS:
|
||||
case TOKEN_MINUSMINUS:
|
||||
case TOKEN_PLUSPLUS:
|
||||
case TOKEN_HASH_IDENT:
|
||||
case TOKEN_CT_IDENT:
|
||||
case TOKEN_STRING:
|
||||
case TOKEN_REAL:
|
||||
case TOKEN_CAST:
|
||||
case TOKEN_FALSE:
|
||||
case TOKEN_NIL:
|
||||
case TOKEN_TRUE:
|
||||
case TOKEN_LPARBRA:
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_INVALID_TOKEN:
|
||||
advance(context);
|
||||
return &poisoned_ast;
|
||||
case TOKEN_COLON:
|
||||
case TOKEN_COMMA:
|
||||
case TOKEN_EQ:
|
||||
case TOKEN_GREATER:
|
||||
case TOKEN_DIV:
|
||||
case TOKEN_DOLLAR:
|
||||
case TOKEN_DOT:
|
||||
case TOKEN_HASH:
|
||||
case TOKEN_LESS:
|
||||
case TOKEN_LBRACKET:
|
||||
case TOKEN_MOD:
|
||||
case TOKEN_QUESTION:
|
||||
case TOKEN_AND:
|
||||
case TOKEN_ARROW:
|
||||
case TOKEN_BIT_AND_ASSIGN:
|
||||
case TOKEN_BIT_OR_ASSIGN:
|
||||
case TOKEN_BIT_XOR_ASSIGN:
|
||||
case TOKEN_PLUS_MOD:
|
||||
case TOKEN_MINUS_MOD:
|
||||
case TOKEN_MULT_MOD:
|
||||
case TOKEN_DIV_ASSIGN:
|
||||
case TOKEN_DOTDOT:
|
||||
case TOKEN_ELVIS:
|
||||
case TOKEN_EQEQ:
|
||||
case TOKEN_GREATER_EQ:
|
||||
case TOKEN_LESS_EQ:
|
||||
case TOKEN_MINUS_ASSIGN:
|
||||
case TOKEN_MOD_ASSIGN:
|
||||
case TOKEN_MULT_ASSIGN:
|
||||
case TOKEN_NOT_EQUAL:
|
||||
case TOKEN_PLUS_ASSIGN:
|
||||
case TOKEN_ELIPSIS:
|
||||
case TOKEN_SCOPE:
|
||||
case TOKEN_SHR:
|
||||
case TOKEN_SHL:
|
||||
case TOKEN_MULT_MOD_ASSIGN:
|
||||
case TOKEN_PLUS_MOD_ASSIGN:
|
||||
case TOKEN_MINUS_MOD_ASSIGN:
|
||||
case TOKEN_SHR_ASSIGN:
|
||||
case TOKEN_SHL_ASSIGN:
|
||||
case TOKEN_ALIAS:
|
||||
case TOKEN_AS:
|
||||
case TOKEN_ELSE:
|
||||
case TOKEN_ENUM:
|
||||
case TOKEN_ERROR_TYPE:
|
||||
case TOKEN_FUNC:
|
||||
case TOKEN_GENERIC:
|
||||
case TOKEN_IMPORT:
|
||||
case TOKEN_MACRO:
|
||||
case TOKEN_MODULE:
|
||||
case TOKEN_PUBLIC:
|
||||
case TOKEN_EXTERN:
|
||||
case TOKEN_STRUCT:
|
||||
case TOKEN_THROWS:
|
||||
case TOKEN_TYPEDEF:
|
||||
case TOKEN_UNION:
|
||||
case TOKEN_UNTIL:
|
||||
case TOKEN_ATTRIBUTE:
|
||||
case TOKEN_VAR:
|
||||
case TOKEN_AT_PARAM:
|
||||
case TOKEN_AT_THROWS:
|
||||
case TOKEN_AT_RETURN:
|
||||
case TOKEN_AT_ENSURE:
|
||||
case TOKEN_AT_REQUIRE:
|
||||
case TOKEN_AT_PURE:
|
||||
case TOKEN_AT_CONST:
|
||||
case TOKEN_AT_REQPARSE:
|
||||
case TOKEN_AT_DEPRECATED:
|
||||
case TOKEN_DOCS_START:
|
||||
case TOKEN_DOCS_END:
|
||||
case TOKEN_DOCS_EOL:
|
||||
case TOKEN_DOC_COMMENT:
|
||||
case TOKEN_COMMENT:
|
||||
case TOKEN_DOCS_LINE:
|
||||
case TOKEN_CT_CASE:
|
||||
case TOKEN_CT_ELIF:
|
||||
case TOKEN_CT_ELSE:
|
||||
case TOKEN_CT_DEFAULT:
|
||||
case TOKEN_RPARBRA:
|
||||
case TOKEN_IN:
|
||||
SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type));
|
||||
advance(context);
|
||||
return &poisoned_ast;
|
||||
break;
|
||||
case TOKEN_RPAREN:
|
||||
case TOKEN_RBRACE:
|
||||
case TOKEN_RBRACKET:
|
||||
SEMA_TOKEN_ERROR(context->tok, "Mismatched '%s' found.", token_type_to_string(context->tok.type));
|
||||
advance(context);
|
||||
return &poisoned_ast;
|
||||
case TOKEN_EOS:
|
||||
advance(context);
|
||||
return AST_NEW_TOKEN(AST_NOP_STMT, context->tok);
|
||||
case TOKEN_EOF:
|
||||
sema_error_at(context->tok.span.loc - 1, "Reached the end of the file when expecting a statement.");
|
||||
return &poisoned_ast;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* compound_stmt
|
||||
* : '{' stmt_list '}'
|
||||
* ;
|
||||
*
|
||||
* stmt_list
|
||||
* : stmt
|
||||
* | stmt stmt_list
|
||||
* ;
|
||||
*
|
||||
* @param context
|
||||
* @return a compound statement
|
||||
*/
|
||||
Ast* parse_compound_stmt(Context *context)
|
||||
{
|
||||
CONSUME_OR(TOKEN_LBRACE, &poisoned_ast);
|
||||
Ast *ast = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok);
|
||||
while (!try_consume(context, TOKEN_RBRACE))
|
||||
{
|
||||
Ast *stmt = TRY_AST(parse_stmt(context));
|
||||
ast->compound_stmt.stmts = VECADD(ast->compound_stmt.stmts, stmt);
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
85
src/compiler/parser_internal.h
Normal file
85
src/compiler/parser_internal.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#define EXPECT_IDENT_FOR_OR(_name, _res) do { if (!expect_ident(context, _name)) return _res; } while(0)
|
||||
#define EXPECT_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; } while(0)
|
||||
#define CONSUME_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; advance(context); } while(0)
|
||||
#define TRY_EXPECT_OR(_tok, _message, _type) do { if (context->tok.type != _tok) { SEMA_TOKEN_ERROR(context->tok, _message); return _type; } } while(0)
|
||||
#define TRY_CONSUME_OR(_tok, _message, _type) do { if (!consume(context, _tok, _message)) return _type; } while(0)
|
||||
#define TRY_CONSUME(_tok, _message) TRY_CONSUME_OR(_tok, _message, &poisoned_ast)
|
||||
#define TRY_CONSUME_EOS_OR(_res) TRY_CONSUME_OR(TOKEN_EOS, "Expected ';'", _res)
|
||||
#define TRY_CONSUME_EOS() TRY_CONSUME_EOS_OR(&poisoned_ast)
|
||||
#define RETURN_AFTER_EOS(_ast) extend_ast_with_prev_token(context, ast); TRY_CONSUME_EOS_OR(&poisoned_ast); return _ast
|
||||
#define TRY_CONSUME_LBRACE() TRY_CONSUME(TOKEN_LBRACE, "Expected '{'")
|
||||
|
||||
#define TRY_AST_OR(_ast_stmt, _res) ({ Ast* _ast = (_ast_stmt); if (!ast_ok(_ast)) return _res; _ast; })
|
||||
#define TRY_AST(_ast_stmt) TRY_AST_OR(_ast_stmt, &poisoned_ast)
|
||||
#define TRY_EXPR_OR(_expr_stmt, _res) ({ Expr* _expr = (_expr_stmt); if (!expr_ok(_expr)) return _res; _expr; })
|
||||
#define TRY_TYPE_OR(_type_stmt, _res) ({ TypeInfo* _type = (_type_stmt); if (!type_info_ok(_type)) return _res; _type; })
|
||||
#define TRY_DECL_OR(_decl_stmt, _res) ({ Decl* _decl = (_decl_stmt); if (!decl_ok(_decl)) return _res; _decl; })
|
||||
|
||||
#define CHECK_EXPR(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0)
|
||||
|
||||
|
||||
#define COMMA_RPAREN_OR(_res) \
|
||||
do { if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_RPAREN) { \
|
||||
SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ')'"); return _res; } } while(0)
|
||||
|
||||
|
||||
Ast *parse_stmt(Context *context);
|
||||
Path *parse_path_prefix(Context *context);
|
||||
Expr *parse_type_identifier_with_path(Context *context, Path *path);
|
||||
Expr *parse_expr(Context *context);
|
||||
TypeInfo *parse_type_expression(Context *context);
|
||||
Expr* parse_constant_expr(Context *context);
|
||||
Expr *parse_initializer_list(Context *context);
|
||||
Expr *parse_initializer(Context *context);
|
||||
Decl *parse_decl(Context *context);
|
||||
Ast *parse_decl_expr_list(Context *context);
|
||||
Ast* parse_compound_stmt(Context *context);
|
||||
Expr *parse_expression_list(Context *context);
|
||||
bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr);
|
||||
Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type);
|
||||
bool parse_param_list(Context *context, Expr ***result, bool allow_type);
|
||||
|
||||
void error_at_current(Context *context, const char* message, ...);
|
||||
bool try_consume(Context *context, TokenType type);
|
||||
bool consume(Context *context, TokenType type, const char *message, ...);
|
||||
bool consume_const_name(Context *context, const char* type);
|
||||
Expr *parse_precedence_with_left_side(Context *context, Expr *left_side, Precedence precedence);
|
||||
|
||||
|
||||
static inline bool expect(Context *context, TokenType token_type)
|
||||
{
|
||||
if (token_type == context->tok.type) return true;
|
||||
|
||||
SEMA_TOKEN_ERROR(context->tok, "Expected '%s'.", token_type_to_string(token_type));
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool expect_ident(Context *context, const char* name)
|
||||
{
|
||||
switch (context->tok.type)
|
||||
{
|
||||
case TOKEN_IDENT:
|
||||
return true;
|
||||
case TOKEN_TYPE_IDENT:
|
||||
case TOKEN_CONST_IDENT:
|
||||
SEMA_TOKEN_ERROR(context->tok, "A %s cannot start with a capital letter.", name);
|
||||
return false;
|
||||
default:
|
||||
SEMA_TOKEN_ERROR(context->tok, "A %s was expected.", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline Expr *parse_paren_expr(Context *context)
|
||||
{
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_expr);
|
||||
Expr *expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr);
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_expr);
|
||||
return expr;
|
||||
}
|
||||
458
src/compiler/sema_decls.c
Normal file
458
src/compiler/sema_decls.c
Normal file
@@ -0,0 +1,458 @@
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "sema_internal.h"
|
||||
|
||||
|
||||
static inline bool sema_analyse_error(Context *context __unused, Decl *decl)
|
||||
{
|
||||
Decl **constants = decl->error.error_constants;
|
||||
unsigned size = vec_size(constants);
|
||||
if (size > MAX_ERRORS)
|
||||
{
|
||||
SEMA_ERROR(decl, "More than %d errors declared in a single error type.", MAX_ERRORS);
|
||||
return false;
|
||||
}
|
||||
bool success = true;
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
{
|
||||
Decl *constant = constants[i];
|
||||
for (unsigned j = 0; j < i; j++)
|
||||
{
|
||||
if (constant->name == constants[j]->name)
|
||||
{
|
||||
SEMA_ERROR(constant, "Duplicate error names, please remove one of them.");
|
||||
SEMA_PREV(constants[j], "The previous declaration was here.");
|
||||
decl_poison(constant);
|
||||
decl_poison(constants[j]);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
constant->error_constant.value = i;
|
||||
constant->resolve_status = RESOLVE_DONE;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_struct_member(Context *context, Decl *decl)
|
||||
{
|
||||
if (decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION)
|
||||
{
|
||||
DEBUG_LOG("Beginning analysis of inner struct/union");
|
||||
VECEACH(decl->strukt.members, i)
|
||||
{
|
||||
Decl *member = decl->strukt.members[i];
|
||||
if (!decl_ok(member))
|
||||
{
|
||||
decl_poison(decl);
|
||||
continue;
|
||||
}
|
||||
if (!sema_analyse_struct_member(context, decl->strukt.members[i]))
|
||||
{
|
||||
if (decl_ok(decl))
|
||||
{
|
||||
decl_poison(decl);
|
||||
continue;
|
||||
}
|
||||
decl_poison(decl);
|
||||
}
|
||||
}
|
||||
DEBUG_LOG("Analysis complete.");
|
||||
return decl_ok(decl);
|
||||
}
|
||||
assert(decl->decl_kind == DECL_VAR);
|
||||
assert(decl->var.kind == VARDECL_MEMBER);
|
||||
assert(!decl->var.init_expr);
|
||||
if (!sema_resolve_type_info(context, decl->var.type_info))
|
||||
{
|
||||
decl_poison(decl);
|
||||
return false;
|
||||
}
|
||||
decl->type = decl->var.type_info->type;
|
||||
assert(decl->var.type_info->type);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_struct_union(Context *context, Decl *decl)
|
||||
{
|
||||
DEBUG_LOG("Beginning analysis of %s.", decl->name);
|
||||
assert(decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION);
|
||||
VECEACH(decl->strukt.members, i)
|
||||
{
|
||||
Decl *member = decl->strukt.members[i];
|
||||
if (!decl_ok(member))
|
||||
{
|
||||
decl_poison(decl);
|
||||
continue;
|
||||
}
|
||||
if (!sema_analyse_struct_member(context, decl->strukt.members[i]))
|
||||
{
|
||||
if (decl_ok(decl))
|
||||
{
|
||||
decl_poison(decl);
|
||||
continue;
|
||||
}
|
||||
decl_poison(decl);
|
||||
}
|
||||
}
|
||||
DEBUG_LOG("Analysis complete.");
|
||||
return decl_ok(decl);
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_function_param(Context *context, Decl *param, bool is_function)
|
||||
{
|
||||
assert(param->decl_kind == DECL_VAR);
|
||||
assert(param->var.kind == VARDECL_PARAM);
|
||||
if (!sema_resolve_type_info(context, param->var.type_info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
param->type = param->var.type_info->type;
|
||||
if (param->var.init_expr && !is_function)
|
||||
{
|
||||
SEMA_ERROR(param->var.init_expr, "Function types may not have default arguments.");
|
||||
return false;
|
||||
}
|
||||
if (param->var.init_expr)
|
||||
{
|
||||
Expr *expr = param->var.init_expr;
|
||||
if (!sema_analyse_expr(context, param->type, expr)) return false;
|
||||
if (expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(expr, "Only constant expressions may be used as default values.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function)
|
||||
{
|
||||
char buffer[MAX_FUNCTION_SIGNATURE_SIZE + 200];
|
||||
size_t buffer_write_offset = 0;
|
||||
bool all_ok = true;
|
||||
all_ok = sema_resolve_type_info(context, signature->rtype) && all_ok;
|
||||
if (all_ok)
|
||||
{
|
||||
type_append_signature_name(signature->rtype->type, buffer, &buffer_write_offset);
|
||||
buffer[buffer_write_offset++] = '(';
|
||||
}
|
||||
if (vec_size(signature->params) > MAX_PARAMS)
|
||||
{
|
||||
SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS);
|
||||
return false;
|
||||
}
|
||||
STable *names = &context->scratch_table;
|
||||
stable_clear(names);
|
||||
|
||||
VECEACH(signature->params, i)
|
||||
{
|
||||
Decl *param = signature->params[i];
|
||||
assert(param->resolve_status == RESOLVE_NOT_DONE);
|
||||
param->resolve_status = RESOLVE_RUNNING;
|
||||
if (!sema_analyse_function_param(context, param, is_function))
|
||||
{
|
||||
decl_poison(param);
|
||||
all_ok = false;
|
||||
continue;
|
||||
}
|
||||
param->resolve_status = RESOLVE_DONE;
|
||||
if (i > 0 && all_ok)
|
||||
{
|
||||
buffer[buffer_write_offset++] = ',';
|
||||
}
|
||||
type_append_signature_name(param->var.type_info->type, buffer, &buffer_write_offset);
|
||||
if (param->name)
|
||||
{
|
||||
Decl *prev = stable_set(names, param->name, param);
|
||||
if (prev)
|
||||
{
|
||||
SEMA_ERROR(param, "Duplicate parameter name %s.", param->name);
|
||||
SEMA_PREV(prev, "Previous use of the name was here.");
|
||||
decl_poison(prev);
|
||||
decl_poison(param);
|
||||
all_ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (signature->variadic)
|
||||
{
|
||||
buffer[buffer_write_offset++] = ',';
|
||||
buffer[buffer_write_offset++] = '.';
|
||||
buffer[buffer_write_offset++] = '.';
|
||||
buffer[buffer_write_offset++] = '.';
|
||||
}
|
||||
buffer[buffer_write_offset++] = ')';
|
||||
if (signature->throw_any)
|
||||
{
|
||||
assert(!signature->throws);
|
||||
buffer[buffer_write_offset++] = '!';
|
||||
}
|
||||
if (vec_size(signature->throws))
|
||||
{
|
||||
buffer[buffer_write_offset++] = '!';
|
||||
VECEACH(signature->throws, i)
|
||||
{
|
||||
Decl *err_decl = signature->throws[i];
|
||||
if (!sema_analyse_decl(context, err_decl))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (i > 0 && all_ok)
|
||||
{
|
||||
buffer[buffer_write_offset++] = '|';
|
||||
}
|
||||
type_append_signature_name(err_decl->type, buffer, &buffer_write_offset);
|
||||
}
|
||||
}
|
||||
if (!all_ok) return NULL;
|
||||
TokenType type = TOKEN_INVALID_TOKEN;
|
||||
signature->mangled_signature = symtab_add(buffer, buffer_write_offset, fnv1a(buffer, buffer_write_offset), &type);
|
||||
Type *func_type = stable_get(&context->local_symbols, signature->mangled_signature);
|
||||
if (!func_type)
|
||||
{
|
||||
func_type = type_new(TYPE_FUNC, signature->mangled_signature);
|
||||
func_type->canonical = func_type;
|
||||
func_type->func.signature = signature;
|
||||
stable_set(&context->local_symbols, signature->mangled_signature, func_type);
|
||||
}
|
||||
return func_type;
|
||||
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_typedef(Context *context, Decl *decl)
|
||||
{
|
||||
if (decl->typedef_decl.is_func)
|
||||
{
|
||||
Type *func_type = sema_analyse_function_signature(context, &decl->typedef_decl.function_signature, false);
|
||||
if (!func_type) return false;
|
||||
decl->type->canonical = func_type;
|
||||
return true;
|
||||
}
|
||||
if (!sema_resolve_type_info(context, decl->typedef_decl.type_info)) return false;
|
||||
decl->type->canonical = decl->typedef_decl.type_info->type;
|
||||
// Do we need anything else?
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_enum(Context *context, Decl *decl)
|
||||
{
|
||||
if (!sema_resolve_type_info(context, decl->enums.type_info)) return false;
|
||||
uint64_t value = 0;
|
||||
Type *type = decl->enums.type_info->type;
|
||||
bool success = true;
|
||||
VECEACH(decl->enums.values, i)
|
||||
{
|
||||
Decl *enum_value = decl->enums.values[i];
|
||||
enum_value->enum_constant.ordinal = i;
|
||||
assert(enum_value->resolve_status == RESOLVE_NOT_DONE);
|
||||
assert(enum_value->decl_kind == DECL_ENUM_CONSTANT);
|
||||
enum_value->resolve_status = RESOLVE_RUNNING;
|
||||
Expr *expr = enum_value->enum_constant.expr;
|
||||
if (!expr)
|
||||
{
|
||||
expr = expr_new(EXPR_CONST, INVALID_RANGE);
|
||||
expr->type = type;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
expr->const_expr.type = CONST_INT;
|
||||
expr->const_expr.i = value;
|
||||
enum_value->enum_constant.expr = expr;
|
||||
}
|
||||
if (!sema_analyse_expr(context, type, expr))
|
||||
{
|
||||
success = false;
|
||||
enum_value->resolve_status = RESOLVE_DONE;
|
||||
continue;
|
||||
}
|
||||
assert(type_is_integer(expr->type->canonical));
|
||||
if (expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(expr, "Expected a constant expression for enum");
|
||||
success = false;
|
||||
}
|
||||
enum_value->resolve_status = RESOLVE_DONE;
|
||||
enum_value->type = decl->type;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_throws(Context *context, Decl *decl)
|
||||
{
|
||||
if (!sema_resolve_type_info(context, decl->throws)) return false;
|
||||
decl->type = decl->throws->type;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_method_function(Context *context, Decl *decl)
|
||||
{
|
||||
TypeInfo *parent_type = decl->func.type_parent;
|
||||
if (!sema_resolve_type_info(context, parent_type)) return false;
|
||||
if (!type_may_have_method_functions(parent_type->type))
|
||||
{
|
||||
SEMA_ERROR(decl,
|
||||
"Method functions can not be associated with '%s'",
|
||||
type_to_error_string(decl->func.type_parent->type));
|
||||
return false;
|
||||
}
|
||||
Decl *parent = parent_type->type->decl;
|
||||
VECEACH(parent->method_functions, i)
|
||||
{
|
||||
Decl *function = parent->method_functions[i];
|
||||
if (function->name == decl->name)
|
||||
{
|
||||
SEMA_ERROR(decl, "Duplicate name '%s' for method function.", function->name);
|
||||
SEMA_PREV(function, "Previous definition here.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DEBUG_LOG("Method function '%s.%s' analysed.", parent->name, decl->name);
|
||||
vec_add(parent->method_functions, decl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline bool sema_analyse_func(Context *context, Decl *decl)
|
||||
{
|
||||
DEBUG_LOG("----Analysing function %s", decl->name);
|
||||
Type *func_type = sema_analyse_function_signature(context, &decl->func.function_signature, true);
|
||||
decl->type = func_type;
|
||||
if (!func_type) return decl_poison(decl);
|
||||
if (decl->func.type_parent)
|
||||
{
|
||||
if (!sema_analyse_method_function(context, decl)) return decl_poison(decl);
|
||||
}
|
||||
if (decl->func.body && !sema_analyse_function_body(context, decl)) return decl_poison(decl);
|
||||
if (decl->name == main_name)
|
||||
{
|
||||
if (decl->visibility == VISIBLE_LOCAL)
|
||||
{
|
||||
SEMA_ERROR(decl, "'main' cannot have local visibility.");
|
||||
return false;
|
||||
}
|
||||
decl->visibility = VISIBLE_EXTERN;
|
||||
}
|
||||
DEBUG_LOG("Function analysis done.");
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_macro(Context *context, Decl *decl)
|
||||
{
|
||||
TypeInfo *rtype = decl->macro_decl.rtype;
|
||||
if (decl->macro_decl.rtype && !sema_resolve_type_info(context, rtype)) return false;
|
||||
VECEACH(decl->macro_decl.parameters, i)
|
||||
{
|
||||
Decl *param = decl->macro_decl.parameters[i];
|
||||
assert(param->decl_kind == DECL_VAR);
|
||||
assert(param->var.kind == VARDECL_PARAM);
|
||||
if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static inline bool sema_analyse_global(Context *context, Decl *decl)
|
||||
{
|
||||
if (!sema_resolve_type_info(context, decl->var.type_info)) return false;
|
||||
if (decl->var.init_expr)
|
||||
{
|
||||
if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return false;
|
||||
if (decl->var.init_expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
case VARDECL_CONST:
|
||||
assert(decl->var.init_expr);
|
||||
return true;
|
||||
case VARDECL_GLOBAL:
|
||||
return true;
|
||||
default:
|
||||
UNREACHABLE
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_generic(Context *context, Decl *decl)
|
||||
{
|
||||
TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
{
|
||||
if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl);
|
||||
|
||||
DEBUG_LOG("Analyse %s", decl->name);
|
||||
if (decl->resolve_status == RESOLVE_RUNNING)
|
||||
{
|
||||
SEMA_ERROR(decl, "Recursive dependency on %s.", decl->name);
|
||||
decl_poison(decl);
|
||||
return false;
|
||||
}
|
||||
|
||||
decl->resolve_status = RESOLVE_RUNNING;
|
||||
decl->module = context->module;
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_THROWS:
|
||||
if (!sema_analyse_throws(context, decl)) return decl_poison(decl);
|
||||
break;
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl);
|
||||
llvm_set_struct_size_alignment(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_FUNC:
|
||||
if (!sema_analyse_func(context, decl)) return decl_poison(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_MACRO:
|
||||
if (!sema_analyse_macro(context, decl)) return decl_poison(decl);
|
||||
break;
|
||||
case DECL_VAR:
|
||||
if (!sema_analyse_global(context, decl)) return decl_poison(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_TYPEDEF:
|
||||
if (!sema_analyse_typedef(context, decl)) return decl_poison(decl);
|
||||
break;
|
||||
case DECL_ENUM:
|
||||
if (!sema_analyse_enum(context, decl)) return decl_poison(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_ERROR:
|
||||
if (!sema_analyse_error(context, decl)) return decl_poison(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_GENERIC:
|
||||
if (!sema_analyse_generic(context, decl)) return decl_poison(decl);
|
||||
break;
|
||||
case DECL_ATTRIBUTE:
|
||||
TODO
|
||||
case DECL_POISONED:
|
||||
case DECL_IMPORT:
|
||||
case DECL_ENUM_CONSTANT:
|
||||
case DECL_ERROR_CONSTANT:
|
||||
case DECL_ARRAY_VALUE:
|
||||
case DECL_CT_ELSE:
|
||||
case DECL_CT_ELIF:
|
||||
UNREACHABLE
|
||||
case DECL_CT_IF:
|
||||
// Handled elsewhere
|
||||
UNREACHABLE
|
||||
}
|
||||
decl->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
@@ -2,8 +2,14 @@
|
||||
// Use of this source code is governed by the GNU LGPLv3.0 license
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
#include "sema_internal.h"
|
||||
|
||||
/*
|
||||
* TODOs
|
||||
* - Check all returns correctly
|
||||
* - Disallow jumping in and out of an expression block.
|
||||
* - Handle IXX FXX and UXX in a sane way.
|
||||
*/
|
||||
|
||||
#define CONST_PROCESS(_op) \
|
||||
if (both_const(left, right)) { \
|
||||
@@ -15,6 +21,17 @@ expr->expr_kind = EXPR_CONST; \
|
||||
expr->const_expr.type = left->const_expr.type; \
|
||||
}
|
||||
|
||||
int sema_check_comp_time_bool(Context *context, Expr *expr)
|
||||
{
|
||||
if (!sema_analyse_expr(context, type_bool, expr)) return -1;
|
||||
if (expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(expr, "$if requires a compile time constant value.");
|
||||
return -1;
|
||||
}
|
||||
return expr->const_expr.b;
|
||||
}
|
||||
|
||||
static bool expr_is_ltype(Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
@@ -34,6 +51,7 @@ static bool expr_is_ltype(Expr *expr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_type_error_on_binop(Expr *expr)
|
||||
{
|
||||
const char *c = token_type_to_string(binaryop_to_token(expr->binary_expr.operator));
|
||||
@@ -1590,6 +1608,17 @@ static TypeInfo *type_info_copy_from_macro(Context *context, Expr *macro, TypeIn
|
||||
}
|
||||
|
||||
|
||||
static void ast_copy_list_from_macro(Context *context, Expr *macro, Ast ***to_convert)
|
||||
{
|
||||
Ast **result = NULL;
|
||||
Ast **list = *to_convert;
|
||||
VECEACH(list, i)
|
||||
{
|
||||
vec_add(result, ast_copy_from_macro(context, macro, list[i]));
|
||||
}
|
||||
*to_convert = result;
|
||||
}
|
||||
|
||||
|
||||
static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr)
|
||||
{
|
||||
@@ -1598,6 +1627,9 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex
|
||||
Expr *expr = expr_shallow_copy(source_expr);
|
||||
switch (source_expr->expr_kind)
|
||||
{
|
||||
case EXPR_EXPR_BLOCK:
|
||||
ast_copy_list_from_macro(context, macro, &expr->expr_block.stmts);
|
||||
return expr;
|
||||
case EXPR_POISONED:
|
||||
return source_expr;
|
||||
case EXPR_TRY:
|
||||
@@ -1681,16 +1713,6 @@ static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ast_copy_list_from_macro(Context *context, Expr *macro, Ast ***to_convert)
|
||||
{
|
||||
Ast **result = NULL;
|
||||
Ast **list = *to_convert;
|
||||
VECEACH(list, i)
|
||||
{
|
||||
vec_add(result, ast_copy_from_macro(context, macro, list[i]));
|
||||
}
|
||||
*to_convert = result;
|
||||
}
|
||||
|
||||
static void type_info_copy_list_from_macro(Context *context, Expr *macro, TypeInfo ***to_convert)
|
||||
{
|
||||
@@ -1782,9 +1804,6 @@ static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source)
|
||||
AST_COPY(ast->for_stmt.body);
|
||||
AST_COPY(ast->for_stmt.init);
|
||||
return ast;
|
||||
case AST_FUNCTION_BLOCK_STMT:
|
||||
ast_copy_list_from_macro(context, macro, &ast->function_block_stmt.stmts);
|
||||
return ast;
|
||||
case AST_GENERIC_CASE_STMT:
|
||||
AST_COPY(ast->generic_case_stmt.body);
|
||||
// ast->generic_case_stmt.types = ...
|
||||
@@ -1934,8 +1953,130 @@ static inline bool sema_expr_analyse_type(Context *context, Type *to, Expr *expr
|
||||
|
||||
|
||||
|
||||
static inline Ast **context_push_returns(Context *context)
|
||||
{
|
||||
Ast** old_returns = context->returns;
|
||||
if (context->returns_cache)
|
||||
{
|
||||
context->returns = context->returns_cache;
|
||||
context->returns_cache = NULL;
|
||||
vec_resize(context->returns, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
context->returns = NULL;
|
||||
}
|
||||
return old_returns;
|
||||
}
|
||||
|
||||
static inline void context_pop_returns(Context *context, Ast **restore)
|
||||
{
|
||||
if (!context->returns_cache && context->returns)
|
||||
{
|
||||
context->returns_cache = context->returns;
|
||||
}
|
||||
context->returns = restore;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
bool success = true;
|
||||
expr->type = type_void;
|
||||
Type *prev_expected_block_type = context->expected_block_type;
|
||||
Ast **saved_returns = context_push_returns(context);
|
||||
context->expected_block_type = to;
|
||||
context_push_scope_with_flags(context, SCOPE_EXPR_BLOCK);
|
||||
|
||||
VECEACH(expr->expr_block.stmts, i)
|
||||
{
|
||||
if (!sema_analyse_statement(context, expr->expr_block.stmts[i]))
|
||||
{
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vec_size(context->returns))
|
||||
{
|
||||
if (to)
|
||||
{
|
||||
SEMA_ERROR(expr, "Expected at least one return out of expression block to return a value of type %s.", type_to_error_string(to));
|
||||
success = false;
|
||||
}
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
Expr *first_return_expr = context->returns[0]->return_stmt.expr;
|
||||
Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void;
|
||||
bool all_returns_needs_casts = false;
|
||||
// Let's unify the return statements.
|
||||
VECEACH(context->returns, i)
|
||||
{
|
||||
Ast *return_stmt = context->returns[i];
|
||||
Expr *ret_expr = return_stmt->return_stmt.expr;
|
||||
bool last_expr_was_void = left_canonical == type_void;
|
||||
Type *right_canonical = ret_expr ? ret_expr->type->canonical : type_void;
|
||||
bool current_expr_was_void = right_canonical == type_void;
|
||||
if (i > 0 && last_expr_was_void != current_expr_was_void)
|
||||
{
|
||||
SEMA_ERROR(return_stmt, "You can't combine empty returns with value returns.");
|
||||
SEMA_PREV(context->returns[i - 1], "Previous return was here.");
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
if (to)
|
||||
{
|
||||
if (current_expr_was_void)
|
||||
{
|
||||
SEMA_ERROR(return_stmt, "The return must be a value of type '%s'.", type_to_error_string(to));
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
if (!cast_implicit(ret_expr, to))
|
||||
{
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// The simple case.
|
||||
if (left_canonical == right_canonical) continue;
|
||||
|
||||
// Try to find a common type:
|
||||
Type *max = type_find_max_type(left_canonical, right_canonical);
|
||||
if (!max)
|
||||
{
|
||||
SEMA_ERROR(return_stmt, "Cannot find a common parent type of '%s' and '%s'",
|
||||
type_to_error_string(left_canonical), type_to_error_string(right_canonical));
|
||||
SEMA_PREV(context->returns[i - 1], "The previous return was here.");
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
left_canonical = max;
|
||||
all_returns_needs_casts = true;
|
||||
}
|
||||
if (all_returns_needs_casts)
|
||||
{
|
||||
VECEACH(context->returns, i)
|
||||
{
|
||||
Ast *return_stmt = context->returns[i];
|
||||
Expr *ret_expr = return_stmt->return_stmt.expr;
|
||||
if (!cast_implicit(ret_expr, left_canonical))
|
||||
{
|
||||
success = false;
|
||||
goto EXIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
expr->type = left_canonical;
|
||||
EXIT:
|
||||
context_pop_scope(context);
|
||||
context_pop_returns(context, saved_returns);
|
||||
context->expected_block_type = prev_expected_block_type;
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
@@ -1945,6 +2086,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
return false;
|
||||
case EXPR_SCOPED_EXPR:
|
||||
UNREACHABLE
|
||||
case EXPR_EXPR_BLOCK:
|
||||
return sema_expr_analyse_expr_block(context, to, expr);
|
||||
case EXPR_MACRO_EXPR:
|
||||
return sema_expr_analyse_macro_expr(context, to, expr);
|
||||
case EXPR_TRY:
|
||||
13
src/compiler/sema_internal.h
Normal file
13
src/compiler/sema_internal.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
int sema_check_comp_time_bool(Context *context, Expr *expr);
|
||||
bool sema_analyse_function_body(Context *context, Decl *func);
|
||||
void context_pop_scope(Context *context);
|
||||
void context_push_scope_with_flags(Context *context, ScopeFlags flags);
|
||||
123
src/compiler/sema_passes.c
Normal file
123
src/compiler/sema_passes.c
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "sema_internal.h"
|
||||
|
||||
void sema_analysis_pass_process_imports(Context *context)
|
||||
{
|
||||
DEBUG_LOG("Pass: Importing dependencies for %s", context->file->name);
|
||||
unsigned imports = vec_size(context->imports);
|
||||
for (unsigned i = 0; i < imports; i++)
|
||||
{
|
||||
Decl *import = context->imports[i];
|
||||
import->resolve_status = RESOLVE_RUNNING;
|
||||
Path *path = import->import.path;
|
||||
Module *module = stable_get(&compiler.modules, path->module);
|
||||
DEBUG_LOG("- Import of %s.", path->module);
|
||||
if (!module)
|
||||
{
|
||||
SEMA_ERROR(import, "No module named '%s' could be found.", path->module);
|
||||
decl_poison(import);
|
||||
continue;
|
||||
}
|
||||
import->module = module;
|
||||
for (unsigned j = 0; j < i; j++)
|
||||
{
|
||||
if (import->module == context->imports[j]->module)
|
||||
{
|
||||
SEMA_ERROR(import, "Module '%s' imported more than once.", path->module);
|
||||
SEMA_PREV(context->imports[i], "Previous import was here");
|
||||
decl_poison(import);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG_LOG("Pass finished with %d error(s).", diagnostics.errors);
|
||||
}
|
||||
|
||||
|
||||
static inline void sema_append_decls(Context *context, Decl **decls)
|
||||
{
|
||||
VECEACH(decls, i)
|
||||
{
|
||||
context_register_global_decl(context, decls[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_top_level_if(Context *context, Decl *ct_if)
|
||||
{
|
||||
int res = sema_check_comp_time_bool(context, ct_if->ct_if_decl.expr);
|
||||
if (res == -1) return false;
|
||||
if (res)
|
||||
{
|
||||
// Append declarations
|
||||
sema_append_decls(context, ct_if->ct_if_decl.then);
|
||||
return true;
|
||||
}
|
||||
|
||||
// False, so check elifs
|
||||
Decl *ct_elif = ct_if->ct_if_decl.elif;
|
||||
while (ct_elif)
|
||||
{
|
||||
if (ct_elif->decl_kind == DECL_CT_ELIF)
|
||||
{
|
||||
res = sema_check_comp_time_bool(context, ct_elif->ct_elif_decl.expr);
|
||||
if (res == -1) return false;
|
||||
if (res)
|
||||
{
|
||||
sema_append_decls(context, ct_elif->ct_elif_decl.then);
|
||||
return true;
|
||||
}
|
||||
ct_elif = ct_elif->ct_elif_decl.elif;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(ct_elif->decl_kind == DECL_CT_ELSE);
|
||||
sema_append_decls(context, ct_elif->ct_elif_decl.then);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void sema_analysis_pass_conditional_compilation(Context *context)
|
||||
{
|
||||
DEBUG_LOG("Pass: Top level conditionals %s", context->file->name);
|
||||
for (unsigned i = 0; i < vec_size(context->ct_ifs); i++)
|
||||
{
|
||||
sema_analyse_top_level_if(context, context->ct_ifs[i]);
|
||||
}
|
||||
DEBUG_LOG("Pass finished with %d error(s).", diagnostics.errors);
|
||||
}
|
||||
|
||||
void sema_analysis_pass_decls(Context *context)
|
||||
{
|
||||
DEBUG_LOG("Pass: Decl analysis %s", context->file->name);
|
||||
VECEACH(context->enums, i)
|
||||
{
|
||||
sema_analyse_decl(context, context->enums[i]);
|
||||
}
|
||||
VECEACH(context->types, i)
|
||||
{
|
||||
sema_analyse_decl(context, context->types[i]);
|
||||
}
|
||||
VECEACH(context->error_types, i)
|
||||
{
|
||||
sema_analyse_decl(context, context->error_types[i]);
|
||||
}
|
||||
VECEACH(context->struct_functions, i)
|
||||
{
|
||||
sema_analyse_decl(context, context->struct_functions[i]);
|
||||
}
|
||||
VECEACH(context->vars, i)
|
||||
{
|
||||
sema_analyse_decl(context, context->vars[i]);
|
||||
}
|
||||
VECEACH(context->functions, i)
|
||||
{
|
||||
sema_analyse_decl(context, context->functions[i]);
|
||||
}
|
||||
DEBUG_LOG("Pass finished with %d error(s).", diagnostics.errors);
|
||||
}
|
||||
921
src/compiler/sema_stmts.c
Normal file
921
src/compiler/sema_stmts.c
Normal file
@@ -0,0 +1,921 @@
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "sema_internal.h"
|
||||
|
||||
#pragma mark --- Context help functions
|
||||
|
||||
void context_push_scope_with_flags(Context *context, ScopeFlags flags)
|
||||
{
|
||||
if (context->current_scope == &context->scopes[MAX_SCOPE_DEPTH - 1])
|
||||
{
|
||||
FATAL_ERROR("Too deeply nested scopes.");
|
||||
}
|
||||
ScopeFlags previous_flags = context->current_scope->flags;
|
||||
Ast *parent_defer = context->current_scope->defers.start;
|
||||
context->current_scope++;
|
||||
context->current_scope->exit = EXIT_NONE;
|
||||
context->current_scope->local_decl_start = context->last_local;
|
||||
context->current_scope->defers.start = parent_defer;
|
||||
context->current_scope->defers.end = parent_defer;
|
||||
if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK))
|
||||
{
|
||||
context->current_scope->flags = flags;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->current_scope->flags = previous_flags | flags;
|
||||
}
|
||||
context->current_scope->flags_created = flags;
|
||||
}
|
||||
|
||||
static inline void context_push_scope(Context *context)
|
||||
{
|
||||
context_push_scope_with_flags(context, SCOPE_NONE);
|
||||
}
|
||||
|
||||
static inline void context_pop_defers(Context *context)
|
||||
{
|
||||
context->current_scope->defers.start = context->current_scope->defers.end;
|
||||
}
|
||||
|
||||
static inline void context_pop_defers_to(Context *context, DeferList *list)
|
||||
{
|
||||
*list = context->current_scope->defers;
|
||||
context_pop_defers(context);
|
||||
}
|
||||
|
||||
static inline void context_add_exit(Context *context, ExitType exit)
|
||||
{
|
||||
if (context->current_scope->exit < exit)
|
||||
{
|
||||
context->current_scope->exit = exit;
|
||||
}
|
||||
}
|
||||
|
||||
void context_pop_scope(Context *context)
|
||||
{
|
||||
assert(context->current_scope != &context->scopes[0]);
|
||||
context->last_local = context->current_scope->local_decl_start;
|
||||
ExitType exit_type = context->current_scope->exit;
|
||||
assert (context->current_scope->defers.end == context->current_scope->defers.start);
|
||||
context->current_scope--;
|
||||
if (context->current_scope->exit < exit_type)
|
||||
{
|
||||
context->current_scope->exit = exit_type;
|
||||
}
|
||||
}
|
||||
|
||||
static Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr)
|
||||
{
|
||||
DeferList defers = { NULL, NULL };
|
||||
context_pop_defers_to(context, &defers);
|
||||
if (defers.end == defers.start) return expr;
|
||||
Expr *wrap = expr_new(EXPR_SCOPED_EXPR, expr->span);
|
||||
wrap->type = expr->type;
|
||||
wrap->resolve_status = RESOLVE_DONE;
|
||||
wrap->expr_scope.expr = expr;
|
||||
wrap->expr_scope.defers = defers;
|
||||
return expr;
|
||||
}
|
||||
|
||||
static void context_pop_defers_and_replace_ast(Context *context, Ast *ast)
|
||||
{
|
||||
DeferList defers = { NULL, NULL };
|
||||
context_pop_defers_to(context, &defers);
|
||||
if (defers.end == defers.start) return;
|
||||
if (ast->ast_kind == AST_DEFER_STMT)
|
||||
{
|
||||
assert(defers.start == ast);
|
||||
*ast = *ast->defer_stmt.body;
|
||||
return;
|
||||
}
|
||||
assert(ast->ast_kind != AST_COMPOUND_STMT);
|
||||
Ast *replacement = malloc_arena(sizeof(Ast));
|
||||
*replacement = *ast;
|
||||
ast->ast_kind = AST_SCOPED_STMT;
|
||||
ast->scoped_stmt.stmt = replacement;
|
||||
ast->scoped_stmt.defers = defers;
|
||||
}
|
||||
|
||||
#pragma mark --- Helper functions
|
||||
|
||||
#define UPDATE_EXIT(exit_type) \
|
||||
do { if (!context->current_scope->exit) context->current_scope->exit = exit_type; } while(0)
|
||||
|
||||
#pragma mark --- Sema analyse stmts
|
||||
|
||||
|
||||
static inline bool sema_analyse_block_return_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
assert(context->current_scope->flags & SCOPE_EXPR_BLOCK);
|
||||
UPDATE_EXIT(EXIT_RETURN);
|
||||
if (statement->return_stmt.expr && !sema_analyse_expr(context, context->expected_block_type, statement->return_stmt.expr)) return false;
|
||||
vec_add(context->returns, statement);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_return_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
// This might be a return in a function block or a macro which must be treated differently.
|
||||
if (context->current_scope->flags & SCOPE_EXPR_BLOCK)
|
||||
{
|
||||
return sema_analyse_block_return_stmt(context, statement);
|
||||
}
|
||||
|
||||
UPDATE_EXIT(EXIT_RETURN);
|
||||
context->current_scope->exit = EXIT_RETURN;
|
||||
|
||||
Type *expected_rtype = context->rtype;
|
||||
Expr *return_expr = statement->return_stmt.expr;
|
||||
statement->return_stmt.defer = context->current_scope->defers.start;
|
||||
if (return_expr == NULL)
|
||||
{
|
||||
if (!expected_rtype)
|
||||
{
|
||||
assert(context->evaluating_macro);
|
||||
context->rtype = type_void;
|
||||
return true;
|
||||
}
|
||||
if (expected_rtype->canonical != type_void)
|
||||
{
|
||||
SEMA_ERROR(statement, "Expected to return a result of type %s.", type_to_error_string(expected_rtype));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!sema_analyse_expr(context, expected_rtype, return_expr)) return false;
|
||||
if (!expected_rtype)
|
||||
{
|
||||
assert(context->evaluating_macro);
|
||||
context->rtype = type_void;
|
||||
context->active_function_for_analysis->func.function_signature.rtype->type->canonical = statement->return_stmt.expr->type->canonical;
|
||||
return true;
|
||||
}
|
||||
assert(statement->return_stmt.expr->type->canonical == expected_rtype->canonical);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_var_decl(Context *context, Decl *decl)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_VAR);
|
||||
if (!sema_resolve_type_info(context, decl->var.type_info)) return decl_poison(decl);
|
||||
decl->type = decl->var.type_info->type;
|
||||
if (decl->var.init_expr)
|
||||
{
|
||||
if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return decl_poison(decl);
|
||||
}
|
||||
if (!sema_add_local(context, decl)) return decl_poison(decl);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline Ast *convert_expr_to_ast(Expr *expr)
|
||||
{
|
||||
Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span);
|
||||
ast->expr_stmt = expr;
|
||||
return ast;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline bool sema_analyse_decl_expr_list(Context *context, Ast *stmt)
|
||||
{
|
||||
assert(stmt->ast_kind == AST_DECL_EXPR_LIST);
|
||||
|
||||
VECEACH(stmt->decl_expr_stmt, i)
|
||||
{
|
||||
if (!sema_analyse_statement(context, stmt->decl_expr_stmt[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_bool)
|
||||
{
|
||||
assert(stmt->ast_kind == AST_DECL_EXPR_LIST);
|
||||
|
||||
size_t size = vec_size(stmt->decl_expr_stmt);
|
||||
if (!size)
|
||||
{
|
||||
SEMA_ERROR(stmt, "Expected a boolean expression");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sema_analyse_decl_expr_list(context, stmt)) return false;
|
||||
|
||||
Ast *last = stmt->decl_expr_stmt[size - 1];
|
||||
switch (last->ast_kind)
|
||||
{
|
||||
case AST_EXPR_STMT:
|
||||
if (cast_to_bool)
|
||||
{
|
||||
if (!cast_implicit(last->expr_stmt, type_bool)) return false;
|
||||
}
|
||||
return true;
|
||||
case AST_DECLARE_STMT:
|
||||
{
|
||||
Expr *init = last->declare_stmt->var.init_expr;
|
||||
if (!init)
|
||||
{
|
||||
SEMA_ERROR(last, "Expected a declaration with initializer.");
|
||||
return false;
|
||||
}
|
||||
if (cast_to_bool && init->type->type_kind != TYPE_BOOL &&
|
||||
cast_to_bool_kind(last->declare_stmt->var.type_info->type) == CAST_ERROR)
|
||||
{
|
||||
SEMA_ERROR(last->declare_stmt->var.init_expr, "The expression needs to be convertible to a boolean.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_while_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
Ast *decl = statement->while_stmt.decl;
|
||||
Ast *cond = statement->while_stmt.cond;
|
||||
Ast *body = statement->while_stmt.body;
|
||||
context_push_scope(context);
|
||||
bool success = !decl || sema_analyse_statement(context, decl);
|
||||
context_push_scope(context);
|
||||
|
||||
success = success && sema_analyse_cond(context, cond, true);
|
||||
context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise)
|
||||
success = success && sema_analyse_statement(context, body);
|
||||
context_pop_defers_and_replace_ast(context, body);
|
||||
context_pop_scope(context);
|
||||
context_pop_defers_and_replace_ast(context, cond);
|
||||
context_pop_scope(context);
|
||||
context_pop_defers_and_replace_ast(context, statement);
|
||||
context_pop_scope(context);
|
||||
if (!success) return false;
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_do_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
Expr *expr = statement->do_stmt.expr;
|
||||
Ast *body = statement->do_stmt.body;
|
||||
bool success;
|
||||
context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise)
|
||||
success = sema_analyse_statement(context, body);
|
||||
context_pop_defers_and_replace_ast(context, body);
|
||||
context_pop_scope(context);
|
||||
if (!success) return false;
|
||||
context_push_scope(context);
|
||||
success = sema_analyse_expr(context, type_bool, expr);
|
||||
statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr);
|
||||
context_pop_scope(context);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
Decl *decl = statement->declare_stmt;
|
||||
return sema_analyse_var_decl(context, decl);
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_expr_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
if (!sema_analyse_expr(context, NULL, statement->expr_stmt)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
// TODO special parsing of "catch"
|
||||
context_push_scope_with_flags(context, SCOPE_DEFER | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise)
|
||||
// Only ones allowed.
|
||||
context->current_scope->flags &= SCOPE_DEFER | SCOPE_CONTINUE; // NOLINT(hicpp-signed-bitwise)
|
||||
|
||||
bool success = sema_analyse_statement(context, statement->defer_stmt.body);
|
||||
|
||||
context_pop_scope(context);
|
||||
|
||||
if (!success) return false;
|
||||
|
||||
statement->defer_stmt.prev_defer = context->current_scope->defers.start;
|
||||
context->current_scope->defers.start = statement;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_default_stmt(Context *context __unused, Ast *statement)
|
||||
{
|
||||
SEMA_ERROR(statement, "Unexpected 'default' outside of switch");
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_for_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
// Enter for scope
|
||||
context_push_scope(context);
|
||||
if (statement->for_stmt.init)
|
||||
{
|
||||
success = sema_analyse_statement(context, statement->for_stmt.init);
|
||||
}
|
||||
if (success && statement->for_stmt.cond)
|
||||
{
|
||||
// Conditional scope start
|
||||
context_push_scope(context);
|
||||
Expr *cond = statement->for_stmt.cond;
|
||||
success = sema_analyse_expr(context, type_bool, cond);
|
||||
statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond);
|
||||
// Conditional scope end
|
||||
context_pop_scope(context);
|
||||
}
|
||||
if (success && statement->for_stmt.incr)
|
||||
{
|
||||
// Incr scope start
|
||||
context_push_scope(context);
|
||||
Expr *incr = statement->for_stmt.incr;
|
||||
success = sema_analyse_expr(context, NULL, incr);
|
||||
statement->for_stmt.incr = context_pop_defers_and_wrap_expr(context, incr);
|
||||
// Incr scope end
|
||||
context_pop_scope(context);
|
||||
}
|
||||
if (!success) return false;
|
||||
|
||||
// Create the for body scope.
|
||||
context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise)
|
||||
success = sema_analyse_statement(context, statement->for_stmt.body);
|
||||
// End for body scope
|
||||
context_pop_defers_and_replace_ast(context, statement->for_stmt.body);
|
||||
context_pop_scope(context);
|
||||
context_pop_defers_and_replace_ast(context, statement);
|
||||
// End for scope
|
||||
context_pop_scope(context);
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_goto_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
|
||||
statement->goto_stmt.defer = context->current_scope->defers;
|
||||
VECEACH(context->labels, i)
|
||||
{
|
||||
Ast *label = context->labels[i];
|
||||
if (statement->goto_stmt.label_name == label->label_stmt.name)
|
||||
{
|
||||
label->label_stmt.is_used = true;
|
||||
statement->goto_stmt.label = label;
|
||||
}
|
||||
}
|
||||
vec_add(context->gotos, statement);
|
||||
context_add_exit(context, EXIT_GOTO);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
// IMPROVE
|
||||
// convert
|
||||
// if (!x) A(); else B();
|
||||
// into
|
||||
// if (x) B(); else A();
|
||||
Ast *cond = statement->if_stmt.cond;
|
||||
context_push_scope(context);
|
||||
bool success = sema_analyse_cond(context, cond, true);
|
||||
if (statement->if_stmt.else_body)
|
||||
{
|
||||
if (statement->if_stmt.then_body->ast_kind != AST_COMPOUND_STMT)
|
||||
{
|
||||
SEMA_ERROR(statement->if_stmt.then_body,
|
||||
"if-statements with an 'else' must use '{ }' even around a single statement.");
|
||||
success = false;
|
||||
}
|
||||
if (success && statement->if_stmt.else_body->ast_kind != AST_COMPOUND_STMT)
|
||||
{
|
||||
SEMA_ERROR(statement->if_stmt.else_body,
|
||||
"An 'else' must use '{ }' even around a single statement.");
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
success = success && sema_analyse_statement(context, statement->if_stmt.then_body);
|
||||
if (statement->if_stmt.else_body)
|
||||
{
|
||||
success = success && sema_analyse_statement(context, statement->if_stmt.else_body);
|
||||
}
|
||||
context_pop_defers_and_replace_ast(context, statement);
|
||||
context_pop_scope(context);
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_label(Context *context, Ast *statement)
|
||||
{
|
||||
statement->label_stmt.defer = context->current_scope->defers.start;
|
||||
VECEACH(context->labels, i)
|
||||
{
|
||||
Ast *label = context->labels[i];
|
||||
if (label->label_stmt.name == statement->label_stmt.name)
|
||||
{
|
||||
SEMA_ERROR(statement, "This duplicate label '%s'.", statement->label_stmt.name);
|
||||
sema_prev_at_range(label->span, "The previous declaration was here.");
|
||||
ast_poison(label);
|
||||
ast_poison(statement);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
vec_add(context->labels, statement);
|
||||
VECEACH(context->gotos, i)
|
||||
{
|
||||
Ast *the_goto = context->gotos[i];
|
||||
if (the_goto->goto_stmt.label_name == statement->label_stmt.name)
|
||||
{
|
||||
the_goto->goto_stmt.label = statement;
|
||||
statement->label_stmt.is_used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool sema_analyse_catch_stmt(Context *context __unused, Ast *statement __unused)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
static bool sema_analyse_asm_stmt(Context *context __unused, Ast *statement __unused)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
|
||||
static bool sema_analyse_break_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
if (!(context->current_scope->flags & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise)
|
||||
{
|
||||
SEMA_ERROR(statement, "'break' is not allowed here.");
|
||||
return false;
|
||||
}
|
||||
DynamicScope *scope = context->current_scope;
|
||||
statement->break_stmt.defers.start = scope->defers.start;
|
||||
while (!(scope->flags_created & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise)
|
||||
{
|
||||
scope--;
|
||||
}
|
||||
statement->break_stmt.defers.end = scope->defers.end;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sema_analyse_next_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
if (!(context->current_scope->flags & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise)
|
||||
{
|
||||
SEMA_ERROR(statement, "'next' is not allowed here.");
|
||||
return false;
|
||||
}
|
||||
DynamicScope *scope = context->current_scope;
|
||||
statement->next_stmt.defers.start = scope->defers.start;
|
||||
while (!(scope->flags_created & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise)
|
||||
{
|
||||
scope--;
|
||||
}
|
||||
statement->next_stmt.defers.end = scope->defers.end;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sema_analyse_case_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
SEMA_ERROR(statement, "Unexpected 'case' outside of switch");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sema_analyse_continue_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
if (!(context->current_scope->flags & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise)
|
||||
{
|
||||
SEMA_ERROR(statement, "'continue' is not allowed here.");
|
||||
return false;
|
||||
}
|
||||
DynamicScope *scope = context->current_scope;
|
||||
statement->continue_stmt.defers.start = scope->defers.start;
|
||||
while (!(scope->flags_created & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise)
|
||||
{
|
||||
scope--;
|
||||
}
|
||||
statement->continue_stmt.defers.end = scope->defers.end;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_then_overwrite(Context *context, Ast *statement, Ast *replacement)
|
||||
{
|
||||
if (!sema_analyse_statement(context, replacement)) return false;
|
||||
// Overwrite
|
||||
*statement = *replacement;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool sema_analyse_ct_if_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
int res = sema_check_comp_time_bool(context, statement->ct_if_stmt.expr);
|
||||
if (res == -1) return false;
|
||||
if (res)
|
||||
{
|
||||
return sema_analyse_then_overwrite(context, statement, statement->ct_if_stmt.then);
|
||||
}
|
||||
|
||||
Ast *elif = statement->ct_if_stmt.elif;
|
||||
while (1)
|
||||
{
|
||||
if (!elif)
|
||||
{
|
||||
// Turn into NOP!
|
||||
statement->ast_kind = AST_NOP_STMT;
|
||||
return true;
|
||||
}
|
||||
// We found else, then just replace with that.
|
||||
if (elif->ast_kind == AST_CT_ELSE_STMT)
|
||||
{
|
||||
return sema_analyse_then_overwrite(context, statement, elif->ct_else_stmt);
|
||||
}
|
||||
assert(elif->ast_kind == AST_CT_ELIF_STMT);
|
||||
|
||||
res = sema_check_comp_time_bool(context, elif->ct_elif_stmt.expr);
|
||||
if (res == -1) return false;
|
||||
if (res)
|
||||
{
|
||||
return sema_analyse_then_overwrite(context, statement, elif->ct_elif_stmt.then);
|
||||
}
|
||||
elif = elif->ct_elif_stmt.elif;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool sema_analyse_case_expr(Context *context, Ast *case_stmt)
|
||||
{
|
||||
Expr *case_expr = case_stmt->case_stmt.expr;
|
||||
// TODO handle enums
|
||||
if (!sema_analyse_expr(context, NULL, case_expr)) return false;
|
||||
if (case_expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(case_expr, "This must be a constant expression.");
|
||||
return false;
|
||||
}
|
||||
if (case_expr->const_expr.type != CONST_INT)
|
||||
{
|
||||
SEMA_ERROR(case_expr, "The 'case' value must be an integer constant.");
|
||||
return false;
|
||||
}
|
||||
assert(case_expr->const_expr.type == CONST_INT);
|
||||
case_stmt->case_stmt.value_type = type_is_signed(case_expr->type->canonical) ? CASE_VALUE_INT : CASE_VALUE_UINT;
|
||||
uint64_t val = case_expr->const_expr.i;
|
||||
case_stmt->case_stmt.val = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_compound_statement_no_scope(Context *context, Ast *compound_statement)
|
||||
{
|
||||
bool all_ok = ast_ok(compound_statement);
|
||||
VECEACH(compound_statement->compound_stmt.stmts, i)
|
||||
{
|
||||
if (!sema_analyse_statement(context, compound_statement->compound_stmt.stmts[i]))
|
||||
{
|
||||
ast_poison(compound_statement->compound_stmt.stmts[i]);
|
||||
all_ok = false;
|
||||
}
|
||||
}
|
||||
context_pop_defers_to(context, &compound_statement->compound_stmt.defer_list);
|
||||
|
||||
/*
|
||||
if (parent->exit < compound_statement->exit)
|
||||
{
|
||||
parent->exit = compound_statement->exit;
|
||||
}*/
|
||||
return all_ok;
|
||||
}
|
||||
|
||||
static inline Type *ast_cond_type(Ast *ast)
|
||||
{
|
||||
assert(ast->ast_kind == AST_DECL_EXPR_LIST);
|
||||
Ast *last = VECLAST(ast->decl_expr_stmt);
|
||||
switch (last->ast_kind)
|
||||
{
|
||||
case AST_EXPR_STMT:
|
||||
return last->expr_stmt->type;
|
||||
case AST_DECLARE_STMT:
|
||||
return last->declare_stmt->var.type_info->type;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
context_push_scope(context);
|
||||
Ast *cond = statement->switch_stmt.cond;
|
||||
bool success = sema_analyse_cond(context, cond, false);
|
||||
|
||||
Type *switch_type = ast_cond_type(cond)->canonical;
|
||||
if (!type_is_integer(switch_type))
|
||||
{
|
||||
SEMA_ERROR(cond, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type));
|
||||
return false;
|
||||
}
|
||||
Ast *default_case = NULL;
|
||||
assert(context->current_scope->defers.start == context->current_scope->defers.end);
|
||||
VECEACH(statement->switch_stmt.cases, i)
|
||||
{
|
||||
Ast *stmt = statement->switch_stmt.cases[i];
|
||||
switch (stmt->ast_kind)
|
||||
{
|
||||
case AST_CASE_STMT:
|
||||
if (!sema_analyse_case_expr(context, stmt))
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
for (unsigned j = 0; j < i; j++)
|
||||
{
|
||||
Ast *other = statement->switch_stmt.cases[j];
|
||||
if (other->ast_kind == AST_CASE_STMT && other->case_stmt.val == stmt->case_stmt.val)
|
||||
{
|
||||
SEMA_ERROR(stmt, "The same case value appears more than once.");
|
||||
SEMA_PREV(other, "Here is the previous use of that value.");
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AST_DEFAULT_STMT:
|
||||
if (default_case)
|
||||
{
|
||||
SEMA_ERROR(stmt, "'default' may only appear once in a single 'switch', please remove one.");
|
||||
SEMA_PREV(default_case, "Here is the previous use.");
|
||||
success = false;
|
||||
}
|
||||
default_case = stmt;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_NEXT);
|
||||
success = success && sema_analyse_compound_statement_no_scope(context, stmt->case_stmt.body);
|
||||
context_pop_scope(context);
|
||||
}
|
||||
context_pop_defers_and_replace_ast(context, statement);
|
||||
context_pop_scope(context);
|
||||
if (!success) return false;
|
||||
// Is this a typeless switch value?
|
||||
if (switch_type->type_kind == TYPE_UXX || switch_type->type_kind == TYPE_IXX)
|
||||
{
|
||||
|
||||
TODO
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool sema_analyse_try_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
context->try_nesting++;
|
||||
unsigned errors = vec_size(context->errors);
|
||||
if (!sema_analyse_statement(context, statement->try_stmt))
|
||||
{
|
||||
context->try_nesting--;
|
||||
return false;
|
||||
}
|
||||
unsigned new_errors = vec_size(context->errors);
|
||||
if (new_errors == errors)
|
||||
{
|
||||
SEMA_ERROR(statement, "No error to 'try' in the statement that follows, please remove the 'try'.");
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = errors; i < new_errors; i++)
|
||||
{
|
||||
// At least one uncaught error found!
|
||||
if (context->errors[i]) return true;
|
||||
}
|
||||
SEMA_ERROR(statement, "All errors in the following statement was caught, please remove the 'try'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sema_analyse_throw_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
Expr *throw_value = statement->throw_stmt.throw_value;
|
||||
if (!sema_analyse_expr(context, NULL, throw_value)) return false;
|
||||
Type *type = throw_value->type->canonical;
|
||||
if (type->type_kind != TYPE_ERROR)
|
||||
{
|
||||
SEMA_ERROR(throw_value, "Only 'error' types can be thrown, this is a '%s'.", type->name);
|
||||
return false;
|
||||
}
|
||||
if (!context->try_nesting && !func_has_error_return(&context->active_function_for_analysis->func.function_signature))
|
||||
{
|
||||
SEMA_ERROR(statement, "This 'throw' is not handled, please add a 'throws %s' clause to the function signature or use try-catch.", type->name);
|
||||
return false;
|
||||
}
|
||||
VECADD(context->errors, type->decl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool sema_analyse_volatile_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
context->in_volatile_section++;
|
||||
bool result = sema_analyse_statement(context, statement->volatile_stmt);
|
||||
context->in_volatile_section--;
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool sema_analyse_compound_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
context_push_scope(context);
|
||||
bool success = sema_analyse_compound_statement_no_scope(context, statement);
|
||||
context_pop_scope(context);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_statement_inner(Context *context, Ast *statement)
|
||||
{
|
||||
switch (statement->ast_kind)
|
||||
{
|
||||
case AST_POISONED:
|
||||
return false;
|
||||
case AST_ATTRIBUTE:
|
||||
case AST_SCOPED_STMT:
|
||||
UNREACHABLE
|
||||
case AST_ASM_STMT:
|
||||
return sema_analyse_asm_stmt(context, statement);
|
||||
case AST_BREAK_STMT:
|
||||
return sema_analyse_break_stmt(context, statement);
|
||||
case AST_CASE_STMT:
|
||||
return sema_analyse_case_stmt(context, statement);
|
||||
case AST_CATCH_STMT:
|
||||
return sema_analyse_catch_stmt(context, statement);
|
||||
case AST_COMPOUND_STMT:
|
||||
return sema_analyse_compound_stmt(context, statement);
|
||||
case AST_CONTINUE_STMT:
|
||||
return sema_analyse_continue_stmt(context, statement);
|
||||
case AST_CT_IF_STMT:
|
||||
return sema_analyse_ct_if_stmt(context, statement);
|
||||
case AST_DECLARE_STMT:
|
||||
return sema_analyse_declare_stmt(context, statement);
|
||||
case AST_DEFAULT_STMT:
|
||||
return sema_analyse_default_stmt(context, statement);
|
||||
case AST_DEFER_STMT:
|
||||
return sema_analyse_defer_stmt(context, statement);
|
||||
case AST_DO_STMT:
|
||||
return sema_analyse_do_stmt(context, statement);
|
||||
case AST_EXPR_STMT:
|
||||
return sema_analyse_expr_stmt(context, statement);
|
||||
case AST_FOR_STMT:
|
||||
return sema_analyse_for_stmt(context, statement);
|
||||
case AST_GOTO_STMT:
|
||||
return sema_analyse_goto_stmt(context, statement);
|
||||
case AST_IF_STMT:
|
||||
return sema_analyse_if_stmt(context, statement);
|
||||
case AST_LABEL:
|
||||
return sema_analyse_label(context, statement);
|
||||
case AST_NOP_STMT:
|
||||
return true;
|
||||
case AST_RETURN_STMT:
|
||||
return sema_analyse_return_stmt(context, statement);
|
||||
case AST_SWITCH_STMT:
|
||||
return sema_analyse_switch_stmt(context, statement);
|
||||
case AST_THROW_STMT:
|
||||
return sema_analyse_throw_stmt(context, statement);
|
||||
case AST_TRY_STMT:
|
||||
return sema_analyse_try_stmt(context, statement);
|
||||
case AST_NEXT_STMT:
|
||||
return sema_analyse_next_stmt(context, statement);
|
||||
case AST_VOLATILE_STMT:
|
||||
return sema_analyse_volatile_stmt(context, statement);
|
||||
case AST_WHILE_STMT:
|
||||
return sema_analyse_while_stmt(context, statement);
|
||||
case AST_DECL_EXPR_LIST:
|
||||
return sema_analyse_decl_expr_list(context, statement);
|
||||
case AST_CT_ELIF_STMT:
|
||||
case AST_CT_ELSE_STMT:
|
||||
UNREACHABLE
|
||||
case AST_CT_FOR_STMT:
|
||||
case AST_CT_SWITCH_STMT:
|
||||
case AST_CT_DEFAULT_STMT:
|
||||
case AST_CT_CASE_STMT:
|
||||
case AST_GENERIC_CASE_STMT:
|
||||
case AST_GENERIC_DEFAULT_STMT:
|
||||
TODO
|
||||
}
|
||||
}
|
||||
|
||||
bool sema_analyse_statement(Context *context, Ast *statement)
|
||||
{
|
||||
if (sema_analyse_statement_inner(context, statement)) return true;
|
||||
return ast_poison(statement);
|
||||
}
|
||||
|
||||
static inline int defer_depth(Ast *defer_stmt)
|
||||
{
|
||||
int depth = 0;
|
||||
while (defer_stmt)
|
||||
{
|
||||
defer_stmt = defer_stmt->defer_stmt.prev_defer;
|
||||
depth++;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
static inline void defer_list_walk_to_common_depth(Ast **defer_stmt, int this_depth, int other_depth)
|
||||
{
|
||||
int steps = this_depth - other_depth;
|
||||
for (int i = 0; i < steps; i++)
|
||||
{
|
||||
*defer_stmt = (*defer_stmt)->defer_stmt.prev_defer;
|
||||
}
|
||||
}
|
||||
|
||||
bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
{
|
||||
context->active_function_for_analysis = func;
|
||||
context->rtype = func->func.function_signature.rtype->type;
|
||||
context->current_scope = &context->scopes[0];
|
||||
// Clean out the current scope.
|
||||
memset(context->current_scope, 0, sizeof(*context->current_scope));
|
||||
|
||||
// Clear try handling
|
||||
vec_resize(context->errors, 0);
|
||||
// Clear returns
|
||||
vec_resize(context->returns, 0);
|
||||
context->try_nesting = 0;
|
||||
context->labels = NULL;
|
||||
context->gotos = NULL;
|
||||
context->returns = NULL;
|
||||
context->expected_block_type = NULL;
|
||||
context->last_local = &context->locals[0];
|
||||
context->in_volatile_section = 0;
|
||||
func->func.annotations = CALLOCS(*func->func.annotations);
|
||||
context_push_scope(context);
|
||||
Decl **params = func->func.function_signature.params;
|
||||
VECEACH(params, i)
|
||||
{
|
||||
if (!sema_add_local(context, params[i])) return false;
|
||||
}
|
||||
if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false;
|
||||
if (context->current_scope->exit != EXIT_RETURN)
|
||||
{
|
||||
if (func->func.function_signature.rtype->type->canonical != type_void)
|
||||
{
|
||||
SEMA_ERROR(func, "Missing return statement at the end of the function.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VECEACH(context->gotos, i)
|
||||
{
|
||||
Ast *goto_stmt = context->gotos[i];
|
||||
Ast *label_target = goto_stmt->goto_stmt.label;
|
||||
if (!label_target)
|
||||
{
|
||||
SEMA_ERROR(goto_stmt, "Goto to a missing label %s.", goto_stmt->goto_stmt.label_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there are no defers, then that's fine.
|
||||
if (!goto_stmt->goto_stmt.defer.start && !label_target->label_stmt.defer) continue;
|
||||
|
||||
Ast *common_depth_label = label_target->label_stmt.defer;
|
||||
Ast *common_depth_goto = goto_stmt->goto_stmt.defer.start;
|
||||
|
||||
// First we need to search for the common depth.
|
||||
int label_depth = defer_depth(common_depth_label);
|
||||
int goto_depth = defer_depth(common_depth_goto);
|
||||
|
||||
|
||||
// Now walk up to the common depth.
|
||||
defer_list_walk_to_common_depth(&common_depth_label, label_depth, goto_depth);
|
||||
defer_list_walk_to_common_depth(&common_depth_goto, goto_depth, label_depth);
|
||||
|
||||
// We might still not match, so walk upwards until we have a match:
|
||||
while (common_depth_goto != common_depth_label)
|
||||
{
|
||||
assert(common_depth_goto && common_depth_label);
|
||||
common_depth_goto = common_depth_goto->defer_stmt.prev_defer;
|
||||
common_depth_label = common_depth_label->defer_stmt.prev_defer;
|
||||
}
|
||||
|
||||
// We now know the top defer (which we won't actually generate)
|
||||
goto_stmt->goto_stmt.defer.end = common_depth_goto;
|
||||
|
||||
// Mark all defers that occur on the way "up" to the common depth conditional.
|
||||
Ast *current = label_target->label_stmt.defer;
|
||||
while (current != common_depth_goto)
|
||||
{
|
||||
current->defer_stmt.emit_boolean = true;
|
||||
current = current->defer_stmt.prev_defer;
|
||||
}
|
||||
}
|
||||
func->func.labels = context->labels;
|
||||
context_pop_scope(context);
|
||||
context->current_scope = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
151
src/compiler/sema_types.c
Normal file
151
src/compiler/sema_types.c
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "sema_internal.h"
|
||||
|
||||
|
||||
static inline bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info)
|
||||
{
|
||||
if (!sema_resolve_type_shallow(context, type_info->pointer))
|
||||
{
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
type_info->type = type_get_ptr(type_info->pointer->type);
|
||||
type_info->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_resolve_array_type(Context *context, TypeInfo *type)
|
||||
{
|
||||
if (!sema_resolve_type_info(context, type->array.base))
|
||||
{
|
||||
return type_info_poison(type);
|
||||
}
|
||||
if (type->array.len)
|
||||
{
|
||||
if (!sema_analyse_expr(context, type_usize, type->array.len)) return type_info_poison(type);
|
||||
if (type->array.len->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(type->array.len, "Expected a constant value as array size.");
|
||||
return type_info_poison(type);
|
||||
}
|
||||
}
|
||||
assert(!type->array.len || type->array.len->expr_kind == EXPR_CONST);
|
||||
type->type = type_get_array(type->array.base->type, type->array.len ? type->array.len->const_expr.i : 0);
|
||||
type->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
||||
{
|
||||
Decl *ambiguous_decl;
|
||||
Decl *decl = sema_resolve_symbol(context,
|
||||
type_info->unresolved.name_loc.string,
|
||||
type_info->unresolved.path,
|
||||
&ambiguous_decl);
|
||||
|
||||
if (!decl)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", type_info->unresolved.name_loc.string);
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
|
||||
// Already handled
|
||||
if (!decl_ok(decl))
|
||||
{
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
|
||||
|
||||
if (ambiguous_decl)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(type_info->unresolved.name_loc,
|
||||
"Ambiguous type '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity",
|
||||
type_info->unresolved.name_loc.string,
|
||||
decl->module->name->module,
|
||||
ambiguous_decl->module->name->module);
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_THROWS:
|
||||
TODO
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
case DECL_ERROR:
|
||||
case DECL_ENUM:
|
||||
type_info->type = decl->type;
|
||||
type_info->resolve_status = RESOLVE_DONE;
|
||||
DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string);
|
||||
return true;
|
||||
case DECL_TYPEDEF:
|
||||
// TODO func
|
||||
if (!sema_resolve_type_info(context, decl->typedef_decl.type_info))
|
||||
{
|
||||
decl_poison(decl);
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
DEBUG_LOG("Resolved %s.", type_info->unresolved.name_loc.string);
|
||||
type_info->type = decl->type;
|
||||
type_info->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
case DECL_POISONED:
|
||||
return type_info_poison(type_info);
|
||||
case DECL_FUNC:
|
||||
case DECL_VAR:
|
||||
case DECL_ENUM_CONSTANT:
|
||||
case DECL_ERROR_CONSTANT:
|
||||
case DECL_ARRAY_VALUE:
|
||||
case DECL_IMPORT:
|
||||
case DECL_MACRO:
|
||||
case DECL_GENERIC:
|
||||
SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "This is not a type.");
|
||||
return type_info_poison(type_info);
|
||||
case DECL_CT_ELSE:
|
||||
case DECL_CT_IF:
|
||||
case DECL_CT_ELIF:
|
||||
case DECL_ATTRIBUTE:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
|
||||
}
|
||||
|
||||
bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info)
|
||||
{
|
||||
if (type_info->resolve_status == RESOLVE_DONE) return type_info_ok(type_info);
|
||||
|
||||
if (type_info->resolve_status == RESOLVE_RUNNING)
|
||||
{
|
||||
// TODO this is incorrect for unresolved expressions
|
||||
SEMA_TOKEN_ERROR(type_info->unresolved.name_loc,
|
||||
"Circular dependency resolving type '%s'.",
|
||||
type_info->unresolved.name_loc.string);
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
|
||||
type_info->resolve_status = RESOLVE_RUNNING;
|
||||
|
||||
switch (type_info->kind)
|
||||
{
|
||||
case TYPE_INFO_POISON:
|
||||
case TYPE_INFO_INC_ARRAY:
|
||||
UNREACHABLE
|
||||
case TYPE_INFO_IDENTIFIER:
|
||||
return sema_resolve_type_identifier(context, type_info);
|
||||
case TYPE_INFO_EXPRESSION:
|
||||
if (!sema_analyse_expr(context, NULL, type_info->unresolved_type_expr))
|
||||
{
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
TODO
|
||||
case TYPE_INFO_ARRAY:
|
||||
return sema_resolve_array_type(context, type_info);
|
||||
case TYPE_INFO_POINTER:
|
||||
return sema_resolve_ptr_type(context, type_info);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -172,11 +172,12 @@ size_t type_size(Type *canonical)
|
||||
switch (canonical->type_kind)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_TYPEDEF:
|
||||
UNREACHABLE;
|
||||
case TYPE_META_TYPE:
|
||||
return 0;
|
||||
case TYPE_ENUM:
|
||||
case TYPE_TYPEDEF:
|
||||
return type_size(canonical->decl->enums.type_info->type->canonical);
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
case TYPE_ERROR:
|
||||
|
||||
Reference in New Issue
Block a user