mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Improved defer functionality, handling goto, conditional defers, next, break, continue, fixed bugs with module path detection. Improved switch parsing. Better errors on unknown module.
This commit is contained in:
38
.clang-tidy
Normal file
38
.clang-tidy
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
# Configure clang-tidy for this project.
|
||||
|
||||
# Disabled:
|
||||
# -google-readability-namespace-comments the *_CLIENT_NS is a macro, and
|
||||
# clang-tidy fails to match it against the initial value.
|
||||
Checks: >
|
||||
-*,
|
||||
bugprone-*,
|
||||
google-*,
|
||||
misc-*,
|
||||
modernize-*,
|
||||
performance-*,
|
||||
portability-*,
|
||||
readability-*,
|
||||
-google-readability-namespace-comments,
|
||||
-google-runtime-int,
|
||||
-google-runtime-references,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-readability-named-parameter,
|
||||
-readability-braces-around-statements,
|
||||
-readability-magic-numbers
|
||||
|
||||
# Turn all the warnings from the checks above into errors.
|
||||
WarningsAsErrors: "*"
|
||||
|
||||
CheckOptions:
|
||||
- { key: readability-identifier-naming.StructCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.FunctionCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.VariableCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE }
|
||||
- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
|
||||
- { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.ConstexprVariablePrefix, value: k }
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.GlobalConstantPrefix, value: k }
|
||||
- { key: readability-identifier-naming.StaticConstantCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.StaticConstantPrefix, value: k }
|
||||
@@ -27,11 +27,12 @@ There are some small work being done on the parser here, but most of the structu
|
||||
- `asm` sections.
|
||||
- Macro parameter lists to imports.
|
||||
- auxiliary data for enums.
|
||||
- Docs not correctly linked to statements/functions/declarations.
|
||||
- Docs not linked to statements/functions/declarations.
|
||||
|
||||
#### What's missing in the semantic analyser
|
||||
|
||||
- No handling imports.
|
||||
- Incomplete handling of imports.
|
||||
- `next` not correct
|
||||
- Function signatures incomplete.
|
||||
- Function typedef not done.
|
||||
- `asm` not done.
|
||||
@@ -51,10 +52,6 @@ There are some small work being done on the parser here, but most of the structu
|
||||
|
||||
#### What's missing overall
|
||||
|
||||
- Exactly how the module system should work together with the import is still
|
||||
under discussion.
|
||||
- Exactly how the library imports should work is not 100% decided.
|
||||
- The compiler currently only works in single file mode.
|
||||
- Integration with C.
|
||||
|
||||
#### What's working?
|
||||
|
||||
@@ -120,20 +120,113 @@ func void bob()
|
||||
long deee = (eok ? a : b) + c;
|
||||
}
|
||||
|
||||
func int if_test(int x)
|
||||
{
|
||||
switch (x)
|
||||
{
|
||||
case 1:
|
||||
x += 1;
|
||||
if (x < 10)
|
||||
{
|
||||
defer x += 5;
|
||||
if (x < 7)
|
||||
{
|
||||
defer x += 100;
|
||||
next;
|
||||
}
|
||||
x += 99;
|
||||
}
|
||||
next;
|
||||
default:
|
||||
x += 2;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
func int yyyy(int x)
|
||||
{
|
||||
defer printf("A");
|
||||
if (x > 0) return 2;
|
||||
defer printf("B");
|
||||
printf("C");
|
||||
return 1;
|
||||
}
|
||||
|
||||
func void zzzz()
|
||||
{
|
||||
int x = 0;
|
||||
defer
|
||||
{
|
||||
x += 1;
|
||||
printf("A");
|
||||
}
|
||||
defer
|
||||
{
|
||||
x += 2;
|
||||
printf("B");
|
||||
}
|
||||
printf("C");
|
||||
x += 3;
|
||||
}
|
||||
|
||||
func int jumpback(int x)
|
||||
{
|
||||
{
|
||||
defer x += 1;
|
||||
{
|
||||
defer x += 2;
|
||||
LABELX:
|
||||
x += 3;
|
||||
}
|
||||
}
|
||||
if (x < 100) goto LABELX;
|
||||
return x + 1;
|
||||
}
|
||||
func int xxxx(int x)
|
||||
{
|
||||
{
|
||||
x += 10;
|
||||
defer printf("XXX");
|
||||
if (x < 100) goto LABELD;
|
||||
defer printf("EODfe");
|
||||
}
|
||||
{
|
||||
defer printf("Defer says hello!\n");
|
||||
LABEL:
|
||||
LABELD:
|
||||
x--;
|
||||
}
|
||||
if (x > 0) goto LABEL;
|
||||
if (x > 0) goto LABELD;
|
||||
return 1;
|
||||
}
|
||||
|
||||
func int main(int x)
|
||||
{
|
||||
int efd = 9;
|
||||
int okfe = 1;
|
||||
switch (int bobe = okfe > 0 ? 1 : 0)
|
||||
{
|
||||
case 0:
|
||||
defer printf("case0-\n");
|
||||
case 1:
|
||||
printf("case 1\n");
|
||||
defer printf("case1-\n");
|
||||
if (efd < 10)
|
||||
{
|
||||
{
|
||||
defer printf("ef < 10\n");
|
||||
if (efd < 7)
|
||||
{
|
||||
defer printf("ef < 7\n");
|
||||
next;
|
||||
}
|
||||
}
|
||||
}
|
||||
next;
|
||||
default:
|
||||
printf("default\n");
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
int aa = x++;
|
||||
int bb = x--;
|
||||
int cc = ++x;
|
||||
|
||||
@@ -6,6 +6,9 @@ import testbaz;
|
||||
public func void test()
|
||||
{
|
||||
Foo x;
|
||||
baz::foo::test();
|
||||
foo::test();
|
||||
baz::foo::Foo y;
|
||||
foo::Foo d;
|
||||
Zab z;
|
||||
}
|
||||
@@ -893,6 +893,10 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
|
||||
fprint_ast_recursive(file, ast->while_stmt.cond, indent + 1);
|
||||
fprint_ast_recursive(file, ast->while_stmt.body, indent + 1);
|
||||
break;
|
||||
case AST_SCOPED_STMT:
|
||||
fprintf(file, "(scoped\n");
|
||||
fprint_ast_recursive(file, ast->scoped_stmt.stmt, indent + 1);
|
||||
break;
|
||||
case AST_CT_FOR_STMT:
|
||||
if (ast->ct_for_stmt.index.string)
|
||||
{
|
||||
@@ -946,7 +950,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
|
||||
}
|
||||
if (ast->for_stmt.incr)
|
||||
{
|
||||
fprint_ast_recursive(file, ast->for_stmt.incr, indent + 1);
|
||||
fprint_expr_recursive(file, ast->for_stmt.incr, indent + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -967,7 +971,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
|
||||
case AST_SWITCH_STMT:
|
||||
fprintf(file, "(switchstmt\n");
|
||||
fprint_ast_recursive(file, ast->switch_stmt.cond, indent + 1);
|
||||
fprint_ast_recursive(file, ast->switch_stmt.body, indent + 1);
|
||||
fprint_asts_recursive(file, ast->switch_stmt.cases, indent + 1);
|
||||
break;
|
||||
case AST_CASE_STMT:
|
||||
fprintf(file, "(case\n");
|
||||
|
||||
@@ -26,7 +26,6 @@ typedef struct _TypeInfo TypeInfo;
|
||||
typedef struct _Expr Expr;
|
||||
typedef struct _Module Module;
|
||||
typedef struct _Type Type;
|
||||
typedef uint16_t DeferId;
|
||||
|
||||
typedef bool(*CastFunc)(Expr *, Type *, Type *, Type *, CastType cast_type);
|
||||
|
||||
@@ -38,8 +37,8 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DeferId start;
|
||||
DeferId end;
|
||||
Ast *start;
|
||||
Ast *end;
|
||||
} DeferList;
|
||||
|
||||
typedef struct
|
||||
@@ -458,6 +457,11 @@ typedef struct
|
||||
};
|
||||
} ExprCast;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Expr *expr;
|
||||
DeferList defers;
|
||||
} ExprScope;
|
||||
|
||||
struct _Expr
|
||||
{
|
||||
@@ -482,6 +486,7 @@ struct _Expr
|
||||
ExprType type_expr;
|
||||
Expr** initializer_expr;
|
||||
Expr** expression_list;
|
||||
ExprScope expr_scope;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -549,15 +554,15 @@ typedef struct
|
||||
bool has_next;
|
||||
};
|
||||
};
|
||||
Ast *block;
|
||||
Ast *body;
|
||||
void *backend_value;
|
||||
} AstCaseStmt;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Ast *decl;
|
||||
Ast *cond;
|
||||
Ast *body;
|
||||
Ast **cases;
|
||||
} AstSwitchStmt;
|
||||
|
||||
@@ -565,32 +570,29 @@ typedef struct
|
||||
{
|
||||
Ast *init;
|
||||
Expr *cond;
|
||||
Ast *incr;
|
||||
Expr *incr;
|
||||
Ast *body;
|
||||
DeferList cond_defer;
|
||||
} AstForStmt;
|
||||
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GotoType type : 2;
|
||||
const char *label_name;
|
||||
Ast *label;
|
||||
Ast *defer;
|
||||
DeferList defer;
|
||||
union
|
||||
{
|
||||
struct _Ast *in_defer;
|
||||
struct _Ast *defer_end;
|
||||
};
|
||||
} AstGotoStmt;
|
||||
|
||||
typedef struct _AstDeferStmt
|
||||
{
|
||||
bool emit_boolean : 1;
|
||||
struct _Ast *body; // Compound statement
|
||||
struct _Ast *prev_defer;
|
||||
DeferId id;
|
||||
Ast *body; // Compound statement
|
||||
Ast *prev_defer;
|
||||
void *bool_var;
|
||||
} AstDeferStmt;
|
||||
|
||||
typedef struct _AstCatchStmt
|
||||
@@ -613,6 +615,12 @@ typedef struct _AstGenericCaseStmt
|
||||
struct _Ast *body;
|
||||
} AstGenericCaseStmt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Ast *stmt;
|
||||
DeferList defers;
|
||||
} AstScopedStmt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Expr *cond;
|
||||
@@ -635,8 +643,14 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Ast **defers;
|
||||
} AstContinueStmt;
|
||||
DeferList defers;
|
||||
} AstContinueBreakStmt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Ast *prev;
|
||||
DeferList defers;
|
||||
} AstNextStmt;
|
||||
|
||||
typedef struct _Ast
|
||||
{
|
||||
@@ -663,9 +677,10 @@ typedef struct _Ast
|
||||
AstCaseStmt case_stmt;
|
||||
AstCtSwitchStmt ct_switch_stmt;
|
||||
AstCtCaseStmt ct_case_stmt;
|
||||
AstContinueStmt continue_stmt;
|
||||
AstContinueBreakStmt continue_stmt;
|
||||
AstContinueBreakStmt break_stmt;
|
||||
Ast* ct_default_stmt;
|
||||
Ast* next_stmt;
|
||||
AstNextStmt next_stmt;
|
||||
AstCatchStmt catch_stmt;
|
||||
AstGotoStmt goto_stmt;
|
||||
AstForStmt for_stmt;
|
||||
@@ -676,6 +691,7 @@ typedef struct _Ast
|
||||
AstGenericCaseStmt generic_case_stmt;
|
||||
Ast *generic_default_stmt;
|
||||
Ast** decl_expr_stmt;
|
||||
AstScopedStmt scoped_stmt;
|
||||
};
|
||||
} Ast;
|
||||
|
||||
@@ -704,8 +720,7 @@ typedef struct _DynamicScope
|
||||
ScopeFlags flags_created;
|
||||
unsigned errors;
|
||||
Decl **local_decl_start;
|
||||
DeferId defer_top;
|
||||
DeferId defer_last;
|
||||
DeferList defers;
|
||||
ExitType exit;
|
||||
} DynamicScope;
|
||||
|
||||
@@ -766,10 +781,15 @@ typedef struct _Context
|
||||
Token next_tok;
|
||||
struct
|
||||
{
|
||||
bool has_stored;
|
||||
const char *current;
|
||||
const char *start;
|
||||
Token tok;
|
||||
Token next_tok;
|
||||
Token *lead_comment;
|
||||
Token *trailing_comment;
|
||||
Token *next_lead_comment;
|
||||
unsigned comments;
|
||||
} stored;
|
||||
} Context;
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ typedef enum
|
||||
AST_NEXT_STMT,
|
||||
AST_VOLATILE_STMT,
|
||||
AST_WHILE_STMT,
|
||||
AST_SCOPED_STMT,
|
||||
} AstKind;
|
||||
|
||||
typedef enum
|
||||
@@ -241,14 +242,9 @@ typedef enum
|
||||
EXPR_INITIALIZER_LIST,
|
||||
EXPR_EXPRESSION_LIST,
|
||||
EXPR_CAST,
|
||||
EXPR_SCOPED_EXPR,
|
||||
} ExprKind;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GOTO_NOT_ANALYSED,
|
||||
GOTO_JUMP_FORWARD,
|
||||
GOTO_JUMP_BACK
|
||||
} GotoType;
|
||||
|
||||
|
||||
typedef enum
|
||||
@@ -263,7 +259,7 @@ typedef enum
|
||||
{
|
||||
PREC_NONE,
|
||||
PREC_ASSIGNMENT, // =, *=, /=, %=, ...
|
||||
PREC_TERNARY, // ?:
|
||||
PREC_TERNARY, // ?:
|
||||
PREC_LOGICAL, // && ||
|
||||
PREC_RELATIONAL, // < > <= >= == !=
|
||||
PREC_ADDITIVE, // + -
|
||||
|
||||
@@ -87,7 +87,6 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e
|
||||
}
|
||||
|
||||
expr->type = left->type;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -103,6 +102,9 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr
|
||||
return false;
|
||||
}
|
||||
|
||||
// Already handled
|
||||
if (!decl_ok(decl)) return false;
|
||||
|
||||
if (ambiguous_decl)
|
||||
{
|
||||
SEMA_ERROR(expr,
|
||||
@@ -1472,6 +1474,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
{
|
||||
case EXPR_POISONED:
|
||||
return false;
|
||||
case EXPR_SCOPED_EXPR:
|
||||
UNREACHABLE
|
||||
case EXPR_TRY:
|
||||
return sema_expr_analyse_try(context, to, expr);
|
||||
case EXPR_CONST:
|
||||
|
||||
@@ -59,6 +59,20 @@ static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr
|
||||
return LLVMBuildStructGEP2(context->builder, BACKEND_TYPE(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, "");
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_scoped_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMValueRef value = gencontext_emit_expr(context, expr->expr_scope.expr);
|
||||
gencontext_emit_defer(context, expr->expr_scope.defers.start, expr->expr_scope.defers.end);
|
||||
return value;
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_scoped_expr_address(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMValueRef value = gencontext_emit_address(context, expr->expr_scope.expr);
|
||||
gencontext_emit_defer(context, expr->expr_scope.defers.start, expr->expr_scope.defers.end);
|
||||
return value;
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
@@ -72,6 +86,8 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
return gencontext_emit_access_addr(context, expr);
|
||||
case EXPR_SUBSCRIPT:
|
||||
return gencontext_emit_subscript_addr(context, expr);
|
||||
case EXPR_SCOPED_EXPR:
|
||||
return gencontext_emit_scoped_expr_address(context, expr);
|
||||
case EXPR_CONST:
|
||||
case EXPR_TYPE:
|
||||
case EXPR_POISONED:
|
||||
@@ -614,20 +630,14 @@ static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *c
|
||||
TODO
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr)
|
||||
{
|
||||
assert(expr->ast_kind == AST_EXPR_STMT);
|
||||
LLVMValueRef value = gencontext_emit_expr(context, expr->expr_stmt);
|
||||
// gencontext_emit_defer(context, expr);
|
||||
return value;
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_POISONED:
|
||||
UNREACHABLE
|
||||
case EXPR_SCOPED_EXPR:
|
||||
return gencontext_emit_scoped_expr(context, expr);
|
||||
case EXPR_UNARY:
|
||||
return gencontext_emit_unary_expr(context, expr);
|
||||
case EXPR_CONST:
|
||||
|
||||
@@ -112,8 +112,10 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
label->label_stmt.backend_value = gencontext_create_free_block(context, label->label_stmt.name);
|
||||
}
|
||||
|
||||
gencontext_emit_compound_stmt(context, decl->func.body);
|
||||
|
||||
VECEACH(decl->func.body->compound_stmt.stmts, i)
|
||||
{
|
||||
gencontext_emit_stmt(context, decl->func.body->compound_stmt.stmts[i]);
|
||||
}
|
||||
|
||||
if (!LLVMGetFirstInstruction(context->current_block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block)))
|
||||
{
|
||||
@@ -122,10 +124,12 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
context->current_block = prev_block;
|
||||
LLVMPositionBuilderAtEnd(context->builder, context->current_block);
|
||||
}
|
||||
// Insert a return if needed.
|
||||
// Insert a return (and defer) if needed.
|
||||
if (!LLVMGetBasicBlockTerminator(context->current_block))
|
||||
{
|
||||
assert(decl->func.function_signature.rtype->type->type_kind == TYPE_VOID);
|
||||
assert(decl->func.body->compound_stmt.defer_list.end == NULL);
|
||||
gencontext_emit_defer(context, decl->func.body->compound_stmt.defer_list.start, NULL);
|
||||
LLVMBuildRetVoid(context->builder);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ typedef struct
|
||||
void gencontext_begin_module(GenContext *context);
|
||||
void gencontext_end_module(GenContext *context);
|
||||
void gencontext_emit_stmt(GenContext *context, Ast *ast);
|
||||
void gencontext_emit_defer(GenContext *context, DeferList defer_list);
|
||||
void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end);
|
||||
LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr);
|
||||
LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr);
|
||||
LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type);
|
||||
|
||||
@@ -15,6 +15,7 @@ void gencontext_emit_compound_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
gencontext_emit_stmt(context, ast->compound_stmt.stmts[i]);
|
||||
}
|
||||
gencontext_emit_defer(context, ast->compound_stmt.defer_list.start, ast->compound_stmt.defer_list.end);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +44,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
|
||||
{
|
||||
LLVMValueRef value = gencontext_emit_expr(context, decl->var.init_expr);
|
||||
LLVMBuildStore(context->builder, value, decl->var.backend_ref);
|
||||
return value;
|
||||
return decl->var.backend_ref;
|
||||
}
|
||||
return decl->var.backend_ref;
|
||||
}
|
||||
@@ -99,20 +100,14 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast)
|
||||
{
|
||||
// Ensure we are on a branch that is non empty.
|
||||
if (!gencontext_check_block_branch_emit(context)) return;
|
||||
Ast *defer = ast->return_stmt.defer;
|
||||
while (defer)
|
||||
{
|
||||
gencontext_emit_stmt(context, defer->defer_stmt.body);
|
||||
// TODO boolean
|
||||
defer = defer->defer_stmt.prev_defer;
|
||||
}
|
||||
if (!ast->return_stmt.expr)
|
||||
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);
|
||||
if (!ret_value)
|
||||
{
|
||||
LLVMBuildRetVoid(context->builder);
|
||||
return;
|
||||
}
|
||||
LLVMValueRef returnValue = gencontext_emit_expr(context, ast->return_stmt.expr);
|
||||
LLVMBuildRet(context->builder, returnValue);
|
||||
LLVMBuildRet(context->builder, ret_value);
|
||||
context->current_block = NULL;
|
||||
LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret");
|
||||
gencontext_emit_block(context, post_ret_block);
|
||||
@@ -225,7 +220,6 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast)
|
||||
gencontext_emit_br(context, cond_block);
|
||||
gencontext_emit_block(context, cond_block);
|
||||
value = gencontext_emit_expr(context, ast->for_stmt.cond);
|
||||
gencontext_emit_defer(context, ast->for_stmt.cond_defer);
|
||||
// If we have a body, conditionally jump to it.
|
||||
if (body_block)
|
||||
{
|
||||
@@ -258,7 +252,7 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
// Emit the block
|
||||
gencontext_emit_block(context, inc_block);
|
||||
gencontext_emit_expr(context, ast->for_stmt.incr->expr_stmt);
|
||||
gencontext_emit_expr(context, ast->for_stmt.incr);
|
||||
}
|
||||
|
||||
// Loop back.
|
||||
@@ -285,7 +279,14 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast)
|
||||
// Emit cond
|
||||
gencontext_emit_br(context, begin_block);
|
||||
gencontext_emit_block(context, begin_block);
|
||||
LLVMValueRef value = gencontext_emit_decl_expr_list(context, ast->while_stmt.cond, true);
|
||||
DeferList defers = { NULL, NULL };
|
||||
Ast *cond = ast->while_stmt.cond;
|
||||
if (cond->ast_kind == AST_SCOPED_STMT)
|
||||
{
|
||||
defers = cond->scoped_stmt.defers;
|
||||
cond = cond->scoped_stmt.stmt;
|
||||
}
|
||||
LLVMValueRef value = gencontext_emit_decl_expr_list(context, cond, true);
|
||||
// If we have a body, conditionally jump to it.
|
||||
if (body_block)
|
||||
{
|
||||
@@ -293,6 +294,9 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Emit defers
|
||||
gencontext_emit_defer(context, defers.start, defers.end);
|
||||
|
||||
// Otherwise jump to inc or cond depending on what's available.
|
||||
gencontext_emit_cond_br(context, value, begin_block, exit_block);
|
||||
}
|
||||
@@ -301,6 +305,9 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
gencontext_emit_block(context, body_block);
|
||||
gencontext_emit_stmt(context, ast->while_stmt.body);
|
||||
|
||||
// Emit defers
|
||||
gencontext_emit_defer(context, defers.start, defers.end);
|
||||
}
|
||||
|
||||
// Loop back.
|
||||
@@ -308,6 +315,9 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast)
|
||||
|
||||
// And insert exit block
|
||||
gencontext_emit_block(context, exit_block);
|
||||
|
||||
// Emit defers
|
||||
gencontext_emit_defer(context, defers.start, defers.end);
|
||||
gencontext_pop_break_continue(context);
|
||||
}
|
||||
|
||||
@@ -369,6 +379,7 @@ void gencontext_emit_label(GenContext *context, Ast *ast)
|
||||
|
||||
void gencontext_emit_switch(GenContext *context, Ast *ast)
|
||||
{
|
||||
// TODO check defer correctness
|
||||
if (ast->switch_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->switch_stmt.decl);
|
||||
LLVMValueRef switch_value = gencontext_emit_decl_expr_list(context, ast->switch_stmt.cond, false);
|
||||
|
||||
@@ -385,13 +396,13 @@ void gencontext_emit_switch(GenContext *context, Ast *ast)
|
||||
Ast *case_stmt = ast->switch_stmt.cases[i];
|
||||
if (case_stmt->case_stmt.value_type == CASE_VALUE_DEFAULT)
|
||||
{
|
||||
if (case_stmt->case_stmt.block)
|
||||
if (case_stmt->case_stmt.body)
|
||||
{
|
||||
case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.default");
|
||||
}
|
||||
default_case = case_stmt;
|
||||
}
|
||||
else if (case_stmt->case_stmt.block)
|
||||
else if (case_stmt->case_stmt.body)
|
||||
{
|
||||
case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.case");
|
||||
}
|
||||
@@ -444,13 +455,13 @@ void gencontext_emit_switch(GenContext *context, Ast *ast)
|
||||
}
|
||||
|
||||
// Skip fallthroughs.
|
||||
if (!case_stmt->case_stmt.block) continue;
|
||||
if (!case_stmt->case_stmt.body) continue;
|
||||
|
||||
gencontext_emit_block(context, block);
|
||||
// IMPORTANT!
|
||||
context->current_block_is_target = true;
|
||||
gencontext_push_break_continue(context, NULL, NULL, i < cases - 1 ? ast->switch_stmt.cases[i + 1]->case_stmt.backend_value : exit_block);
|
||||
gencontext_emit_stmt(context, case_stmt->case_stmt.block);
|
||||
gencontext_emit_stmt(context, case_stmt->case_stmt.body);
|
||||
gencontext_pop_break_continue(context);
|
||||
gencontext_emit_br(context, exit_block);
|
||||
}
|
||||
@@ -458,15 +469,92 @@ void gencontext_emit_switch(GenContext *context, Ast *ast)
|
||||
gencontext_emit_block(context, exit_block);
|
||||
}
|
||||
|
||||
void gencontext_emit_defer(GenContext *context, DeferList defer_list)
|
||||
LLVMValueRef gencontext_get_defer_bool(GenContext *context, Ast *defer)
|
||||
{
|
||||
if (defer_list.start == defer_list.end) return;
|
||||
Ast *defer = context->ast_context->defers[defer_list.start - 1];
|
||||
while (defer && defer->defer_stmt.id != defer_list.end)
|
||||
assert(defer->ast_kind == AST_DEFER_STMT && defer->defer_stmt.emit_boolean);
|
||||
if (!defer->defer_stmt.bool_var)
|
||||
{
|
||||
gencontext_emit_stmt(context, defer->defer_stmt.body);
|
||||
// TODO boolean
|
||||
defer->defer_stmt.bool_var = gencontext_emit_alloca(context, BACKEND_TYPE(type_bool), "defer");
|
||||
}
|
||||
return defer->defer_stmt.bool_var;
|
||||
}
|
||||
|
||||
void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end)
|
||||
{
|
||||
if (defer_start == defer_end) return;
|
||||
Ast *defer = defer_start;
|
||||
while (defer && defer != defer_end)
|
||||
{
|
||||
if (defer->defer_stmt.emit_boolean)
|
||||
{
|
||||
|
||||
// We need at least the exit block and the "then" block.
|
||||
LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "skip.defer");
|
||||
LLVMBasicBlockRef defer_block = LLVMCreateBasicBlockInContext(context->context, "do.defer");
|
||||
|
||||
LLVMValueRef value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(type_bool), gencontext_get_defer_bool(context, defer), "will.defer");
|
||||
|
||||
gencontext_emit_cond_br(context, value, defer_block, exit_block);
|
||||
|
||||
// Emit the defer.
|
||||
gencontext_emit_block(context, defer_block);
|
||||
gencontext_emit_stmt(context, defer->defer_stmt.body);
|
||||
|
||||
// Jump to exit.
|
||||
gencontext_emit_br(context, exit_block);
|
||||
|
||||
// And now we just emit the exit block.
|
||||
gencontext_emit_block(context, exit_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
gencontext_emit_stmt(context, defer->defer_stmt.body);
|
||||
}
|
||||
defer = defer->defer_stmt.prev_defer;
|
||||
}
|
||||
}
|
||||
|
||||
void gencontext_emit_goto(GenContext *context, Ast *ast)
|
||||
{
|
||||
gencontext_emit_defer(context, ast->goto_stmt.defer.start, ast->goto_stmt.defer.end);
|
||||
Ast *defer = ast->goto_stmt.label->label_stmt.defer;
|
||||
while (defer != ast->goto_stmt.defer.end)
|
||||
{
|
||||
LLVMBuildStore(context->builder, LLVMConstInt(BACKEND_TYPE(type_bool), 0, false),
|
||||
gencontext_get_defer_bool(context, defer));
|
||||
defer = defer->defer_stmt.prev_defer;
|
||||
}
|
||||
gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value);
|
||||
}
|
||||
|
||||
void gencontext_emit_break(GenContext *context, Ast *ast)
|
||||
{
|
||||
gencontext_emit_defer(context, ast->break_stmt.defers.start, ast->break_stmt.defers.end);
|
||||
|
||||
assert(context->break_continue_stack_index);
|
||||
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].break_block);
|
||||
}
|
||||
|
||||
void gencontext_emit_continue(GenContext *context, Ast *ast)
|
||||
{
|
||||
gencontext_emit_defer(context, ast->continue_stmt.defers.start, ast->continue_stmt.defers.end);
|
||||
|
||||
assert(context->break_continue_stack_index);
|
||||
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].continue_block);
|
||||
}
|
||||
|
||||
void gencontext_emit_next_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
gencontext_emit_defer(context, ast->next_stmt.defers.start, ast->next_stmt.defers.end);
|
||||
|
||||
assert(context->break_continue_stack_index);
|
||||
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block);
|
||||
}
|
||||
|
||||
void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
gencontext_emit_stmt(context, ast->scoped_stmt.stmt);
|
||||
gencontext_emit_defer(context, ast->scoped_stmt.defers.start, ast->scoped_stmt.defers.end);
|
||||
}
|
||||
|
||||
void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||
@@ -477,6 +565,9 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||
TODO
|
||||
case AST_POISONED:
|
||||
UNREACHABLE
|
||||
case AST_SCOPED_STMT:
|
||||
gencontext_emit_scoped_stmt(context, ast);
|
||||
break;
|
||||
case AST_EXPR_STMT:
|
||||
gencontext_emit_expr(context, ast->expr_stmt);
|
||||
break;
|
||||
@@ -484,12 +575,10 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||
gencontext_emit_decl(context, ast);
|
||||
break;
|
||||
case AST_BREAK_STMT:
|
||||
assert(context->break_continue_stack_index);
|
||||
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].break_block);
|
||||
gencontext_emit_break(context, ast);
|
||||
break;
|
||||
case AST_CONTINUE_STMT:
|
||||
assert(context->break_continue_stack_index);
|
||||
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].continue_block);
|
||||
gencontext_emit_continue(context, ast);
|
||||
break;
|
||||
case AST_IF_STMT:
|
||||
gencontext_emit_if(context, ast);
|
||||
@@ -510,11 +599,16 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||
gencontext_emit_do_stmt(context, ast);
|
||||
break;
|
||||
case AST_NEXT_STMT:
|
||||
assert(context->break_continue_stack_index);
|
||||
gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block);
|
||||
gencontext_emit_next_stmt(context, ast);
|
||||
break;
|
||||
case AST_DEFER_STMT:
|
||||
if (ast->defer_stmt.emit_boolean)
|
||||
{
|
||||
LLVMBuildStore(context->builder, LLVMConstInt(BACKEND_TYPE(type_bool), 1, false),
|
||||
gencontext_get_defer_bool(context, ast));
|
||||
}
|
||||
break;
|
||||
case AST_NOP_STMT:
|
||||
case AST_DEFER_STMT:
|
||||
break;
|
||||
case AST_CATCH_STMT:
|
||||
case AST_TRY_STMT:
|
||||
@@ -539,7 +633,7 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||
gencontext_emit_label(context, ast);
|
||||
break;
|
||||
case AST_GOTO_STMT:
|
||||
gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value);
|
||||
gencontext_emit_goto(context, ast);
|
||||
break;
|
||||
case AST_SWITCH_STMT:
|
||||
gencontext_emit_switch(context, ast);
|
||||
|
||||
@@ -13,7 +13,7 @@ static Expr *parse_paren_expr(Context *context);
|
||||
static Expr *parse_precedence(Context *context, Precedence precedence);
|
||||
static Expr *parse_initializer_list(Context *context);
|
||||
static Expr *parse_initializer(Context *context);
|
||||
static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **typePtr);
|
||||
static bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr);
|
||||
static Decl *parse_top_level(Context *context);
|
||||
|
||||
typedef Expr *(*ParseFn)(Context *context, Expr *);
|
||||
@@ -29,18 +29,31 @@ extern ParseRule rules[TOKEN_EOF + 1];
|
||||
|
||||
void context_store_lexer_state(Context *context)
|
||||
{
|
||||
assert(!context->stored.has_stored && "Nested lexer store is forbidden");
|
||||
context->stored.has_stored = true;
|
||||
context->stored.current = context->lexer.current;
|
||||
context->stored.start = context->lexer.lexing_start;
|
||||
context->stored.tok = context->tok;
|
||||
context->stored.next_tok = context->next_tok;
|
||||
context->stored.lead_comment = context->lead_comment;
|
||||
context->stored.trailing_comment = context->trailing_comment;
|
||||
context->stored.next_lead_comment = context->next_lead_comment;
|
||||
context->stored.comments = vec_size(context->comments);
|
||||
}
|
||||
|
||||
void context_restore_lexer_state(Context *context)
|
||||
{
|
||||
assert(context->stored.has_stored && "Tried to restore missing stored state.");
|
||||
context->stored.has_stored = false;
|
||||
context->lexer.current = context->stored.current;
|
||||
context->lexer.lexing_start = context->stored.start;
|
||||
context->tok = context->stored.tok;
|
||||
context->next_tok = context->stored.next_tok;
|
||||
context->lead_comment = context->stored.lead_comment;
|
||||
context->next_lead_comment = context->stored.next_lead_comment;
|
||||
context->trailing_comment = context->stored.trailing_comment;
|
||||
context->prev_tok_end = context->tok.span.end_loc;
|
||||
vec_resize(context->comments, context->stored.comments);
|
||||
}
|
||||
|
||||
inline void advance(Context *context)
|
||||
@@ -347,13 +360,12 @@ static Path *parse_path_prefix(Context *context)
|
||||
while (context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE)
|
||||
{
|
||||
last_range = context->tok.span;
|
||||
advance(context);
|
||||
advance(context);
|
||||
scratch_ptr[offset++] = ':';
|
||||
scratch_ptr[offset++] = ':';
|
||||
len = context->tok.span.end_loc - context->tok.span.loc;
|
||||
memcpy(scratch_ptr + offset, context->tok.start, len);
|
||||
offset += len;
|
||||
advance(context); advance(context);
|
||||
}
|
||||
|
||||
TokenType type = TOKEN_IDENT;
|
||||
@@ -768,7 +780,6 @@ static inline Ast* parse_if_stmt(Context *context)
|
||||
{
|
||||
return if_ast;
|
||||
}
|
||||
advance_and_verify(context, TOKEN_ELSE);
|
||||
if_ast->if_stmt.else_body = TRY_AST(parse_stmt(context));
|
||||
return if_ast;
|
||||
}
|
||||
@@ -857,22 +868,7 @@ static inline Ast* parse_do_stmt(Context *context)
|
||||
return do_ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* switch
|
||||
* : SWITCH '(' control_expression ')' compound_statement
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
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);
|
||||
switch_ast->switch_stmt.body = TRY_AST(parse_compound_stmt(context));
|
||||
return switch_ast;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* for_statement
|
||||
@@ -912,7 +908,7 @@ static inline Ast* parse_for_stmt(Context *context)
|
||||
|
||||
if (context->tok.type != TOKEN_RPAREN)
|
||||
{
|
||||
ast->for_stmt.incr = TRY_AST(ast_from_expr(parse_expression_list(context)));
|
||||
ast->for_stmt.incr = parse_expression_list(context);
|
||||
}
|
||||
|
||||
CONSUME_OR(TOKEN_RPAREN, &poisoned_ast);
|
||||
@@ -926,21 +922,6 @@ static inline Expr* parse_constant_expr(Context *context)
|
||||
{
|
||||
return parse_precedence(context, PREC_TERNARY);
|
||||
}
|
||||
/**
|
||||
* case_stmt
|
||||
* : CASE constant_expression ':'
|
||||
*
|
||||
* @return Ast*
|
||||
*/
|
||||
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");
|
||||
return extend_ast_with_prev_token(context, ast);
|
||||
}
|
||||
|
||||
static inline Ast* parse_goto_stmt(Context *context)
|
||||
{
|
||||
@@ -1136,7 +1117,7 @@ static 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 = 0;
|
||||
ast->return_stmt.defer = NULL;
|
||||
if (try_consume(context, TOKEN_EOS))
|
||||
{
|
||||
ast->return_stmt.expr = NULL;
|
||||
@@ -1161,13 +1142,6 @@ static Ast *parse_volatile_stmt(Context *context)
|
||||
return ast;
|
||||
}
|
||||
|
||||
static 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);
|
||||
return ast;
|
||||
}
|
||||
|
||||
|
||||
bool is_valid_try_statement(TokenType type)
|
||||
@@ -1236,7 +1210,10 @@ static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **type
|
||||
{
|
||||
// We need a little lookahead to see if this is type or expression.
|
||||
context_store_lexer_state(context);
|
||||
advance(context); advance(context);
|
||||
do
|
||||
{
|
||||
advance(context); advance(context);
|
||||
} while (context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE);
|
||||
if (context->tok.type == TOKEN_TYPE_IDENT && !is_expr_after_type_ident(context))
|
||||
{
|
||||
context_restore_lexer_state(context);
|
||||
@@ -1282,21 +1259,106 @@ static inline Ast *parse_decl_or_expr_stmt(Context *context)
|
||||
|
||||
if (!parse_type_or_expr(context, &expr, &type)) return &poisoned_ast;
|
||||
|
||||
Ast *ast;
|
||||
if (expr)
|
||||
{
|
||||
CONSUME_OR(TOKEN_EOS, &poisoned_ast);
|
||||
Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span);
|
||||
ast = AST_NEW(AST_EXPR_STMT, expr->span);
|
||||
ast->expr_stmt = expr;
|
||||
return ast;
|
||||
}
|
||||
else
|
||||
{
|
||||
Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, false, type), &poisoned_ast);
|
||||
Ast *ast = AST_NEW(AST_DECLARE_STMT, decl->span);
|
||||
ast = AST_NEW(AST_DECLARE_STMT, decl->span);
|
||||
ast->declare_stmt = decl;
|
||||
CONSUME_OR(TOKEN_EOS, &poisoned_ast);
|
||||
return ast;
|
||||
}
|
||||
CONSUME_OR(TOKEN_EOS, &poisoned_ast);
|
||||
return 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 ':'
|
||||
*
|
||||
* @return Ast*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
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 ')' compound_statement
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
static Ast *parse_stmt(Context *context)
|
||||
@@ -1389,7 +1451,9 @@ static Ast *parse_stmt(Context *context)
|
||||
case TOKEN_CONTINUE:
|
||||
return parse_continue_stmt(context);
|
||||
case TOKEN_CASE:
|
||||
return parse_case_stmt(context);
|
||||
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:
|
||||
@@ -1397,7 +1461,9 @@ static Ast *parse_stmt(Context *context)
|
||||
case TOKEN_ASM:
|
||||
return parse_asm_stmt(context);
|
||||
case TOKEN_DEFAULT:
|
||||
return parse_default_stmt(context);
|
||||
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:
|
||||
|
||||
@@ -31,6 +31,7 @@ static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path
|
||||
assert(path && "Expected path.");
|
||||
*ambiguous_other_decl = NULL;
|
||||
Decl *decl = NULL;
|
||||
bool path_found = false;
|
||||
VECEACH(context->imports, i)
|
||||
{
|
||||
Decl *import = context->imports[i];
|
||||
@@ -39,6 +40,7 @@ static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path
|
||||
// Full import, first match the subpath.
|
||||
if (path->len > import->import.path->len) continue;
|
||||
if (!matches_subpath(import->import.path, path)) continue;
|
||||
path_found = true;
|
||||
Decl *found = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL);
|
||||
if (!found) continue;
|
||||
if (decl)
|
||||
@@ -48,6 +50,15 @@ static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path
|
||||
}
|
||||
decl = found;
|
||||
}
|
||||
if (!decl)
|
||||
{
|
||||
if (!path_found)
|
||||
{
|
||||
SEMA_ERROR(path, "Unknown module %.*s.", path->len, path->module);
|
||||
return &poisoned_decl;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
context_register_external_symbol(context, decl);
|
||||
return decl;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,6 @@
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
typedef bool(*AstAnalysis)(Context *, Ast*);
|
||||
|
||||
static inline DeferId defer_id_from_ast(Ast *ast)
|
||||
{
|
||||
if (!ast) return 0;
|
||||
assert(ast->ast_kind == AST_DEFER_STMT);
|
||||
return ast->defer_stmt.id;
|
||||
}
|
||||
static inline Type *ast_cond_type(Ast *ast)
|
||||
{
|
||||
assert(ast->ast_kind == AST_DECL_EXPR_LIST);
|
||||
@@ -42,12 +34,12 @@ static inline void context_push_scope_with_flags(Context *context, ScopeFlags fl
|
||||
FATAL_ERROR("Too deeply nested scopes.");
|
||||
}
|
||||
ScopeFlags previous_flags = context->current_scope->flags;
|
||||
DeferId parent_defer = context->current_scope->defer_last;
|
||||
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->defer_top = parent_defer;
|
||||
context->current_scope->defer_last = parent_defer;
|
||||
context->current_scope->defers.start = parent_defer;
|
||||
context->current_scope->defers.end = parent_defer;
|
||||
context->current_scope->flags = previous_flags | flags;
|
||||
context->current_scope->flags_created = flags;
|
||||
}
|
||||
@@ -57,15 +49,23 @@ static inline void context_push_scope(Context *context)
|
||||
context_push_scope_with_flags(context, SCOPE_NONE);
|
||||
}
|
||||
|
||||
static inline void context_pop_scope(Context *context, Ast **ast, Expr **expr)
|
||||
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_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;
|
||||
if (context->current_scope->defer_top != context->current_scope->defer_last)
|
||||
{
|
||||
TODO;
|
||||
}
|
||||
assert (context->current_scope->defers.end == context->current_scope->defers.start);
|
||||
context->current_scope--;
|
||||
if (context->current_scope->exit < exit_type)
|
||||
{
|
||||
@@ -73,6 +73,37 @@ static inline void context_pop_scope(Context *context, Ast **ast, Expr **expr)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info)
|
||||
@@ -107,6 +138,7 @@ static bool sema_resolve_array_type(Context *context, TypeInfo *type)
|
||||
type->resolve_status = RESOLVE_DONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_struct_member(Context *context, Decl *decl)
|
||||
{
|
||||
if (decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION)
|
||||
@@ -174,7 +206,6 @@ static inline bool sema_analyse_struct_union(Context *context, Decl *decl)
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline bool sema_analyse_function_param(Context *context, Decl *param, bool is_function)
|
||||
{
|
||||
assert(param->decl_kind == DECL_VAR);
|
||||
@@ -294,6 +325,8 @@ static inline bool sema_analyse_compound_statement_no_scope(Context *context, As
|
||||
all_ok = false;
|
||||
}
|
||||
}
|
||||
context_pop_defers_to(context, &compound_statement->compound_stmt.defer_list);
|
||||
|
||||
/*
|
||||
if (parent->exit < compound_statement->exit)
|
||||
{
|
||||
@@ -307,7 +340,7 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement)
|
||||
context->current_scope->exit = EXIT_RETURN;
|
||||
Type *expected_rtype = context->rtype;
|
||||
Expr *return_expr = statement->return_stmt.expr;
|
||||
statement->return_stmt.defer = VECLAST(context->defers);
|
||||
statement->return_stmt.defer = context->current_scope->defers.start;
|
||||
if (return_expr == NULL)
|
||||
{
|
||||
if (!expected_rtype)
|
||||
@@ -359,50 +392,6 @@ static inline Ast *convert_expr_to_ast(Expr *expr)
|
||||
ast->expr_stmt = expr;
|
||||
return ast;
|
||||
}
|
||||
static inline Expr *convert_decl_to_expr(Context *context, Decl *decl)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_VAR);
|
||||
assert(decl->decl_kind == VARDECL_LOCAL);
|
||||
if (!decl->var.init_expr) return NULL;
|
||||
Expr *assign_expr = expr_new(EXPR_BINARY, decl->span);
|
||||
assign_expr->resolve_status = RESOLVE_DONE;
|
||||
Expr *identifier = expr_new(EXPR_IDENTIFIER, decl->span);
|
||||
identifier->resolve_status = RESOLVE_DONE;
|
||||
identifier->identifier_expr.identifier = decl->name;
|
||||
identifier->identifier_expr.decl = decl;
|
||||
assign_expr->binary_expr.left = identifier;
|
||||
assign_expr->binary_expr.right = decl->var.init_expr;
|
||||
assign_expr->binary_expr.operator = BINARYOP_ASSIGN;
|
||||
// Possibly not right v TODO
|
||||
identifier->type = decl->var.type_info->type;
|
||||
assign_expr->type = decl->var.init_expr->type;
|
||||
return assign_expr;
|
||||
}
|
||||
|
||||
static inline bool convert_decl_for_cond(Context *context, Decl *decl, Ast*** stmt_list, Expr** last, bool is_last)
|
||||
{
|
||||
Expr *expr = convert_decl_to_expr(context, decl);
|
||||
if (!expr)
|
||||
{
|
||||
if (is_last)
|
||||
{
|
||||
SEMA_ERROR(decl, "Expected an initializer for '%s'.", decl->name);
|
||||
return false;
|
||||
}
|
||||
// Simply skip declarations if they don't have an initializer, since they're already registered anyway.
|
||||
return true;
|
||||
}
|
||||
if (is_last)
|
||||
{
|
||||
*last = expr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ast *stmt = convert_expr_to_ast(expr);
|
||||
*stmt_list = VECADD(*stmt_list, stmt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_analyse_function_block_stmt(Context *context, Ast *stmt)
|
||||
@@ -453,7 +442,7 @@ static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_b
|
||||
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)
|
||||
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;
|
||||
@@ -473,12 +462,16 @@ static inline bool sema_analyse_while_stmt(Context *context, Ast *statement)
|
||||
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_scope(context, &body, NULL);
|
||||
context_pop_scope(context, &cond, NULL);
|
||||
context_pop_scope(context, &decl, NULL);
|
||||
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;
|
||||
}
|
||||
@@ -490,16 +483,17 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement)
|
||||
bool success;
|
||||
context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise)
|
||||
success = sema_analyse_statement(context, body);
|
||||
context_pop_scope(context, &body, NULL);
|
||||
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);
|
||||
context_pop_scope(context, NULL, &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;
|
||||
@@ -520,17 +514,16 @@ static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement)
|
||||
|
||||
bool success = sema_analyse_statement(context, statement->defer_stmt.body);
|
||||
|
||||
context_pop_scope(context, &statement->defer_stmt.body, NULL);
|
||||
context_pop_scope(context);
|
||||
|
||||
if (!success) return false;
|
||||
|
||||
statement->defer_stmt.prev_defer = VECLAST(context->defers);
|
||||
vec_add(context->defers, statement);
|
||||
statement->defer_stmt.id = vec_size(context->defers);
|
||||
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, Ast *statement)
|
||||
static inline bool sema_analyse_default_stmt(Context *context __unused, Ast *statement)
|
||||
{
|
||||
SEMA_ERROR(statement, "Unexpected 'default' outside of switch");
|
||||
return false;
|
||||
@@ -550,17 +543,21 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
// Conditional scope start
|
||||
context_push_scope(context);
|
||||
success = sema_analyse_expr(context, type_bool, statement->for_stmt.cond);
|
||||
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, NULL, &statement->for_stmt.cond);
|
||||
context_pop_scope(context);
|
||||
}
|
||||
if (success && statement->for_stmt.incr)
|
||||
{
|
||||
// Incr scope start
|
||||
context_push_scope(context);
|
||||
success = sema_analyse_statement(context, statement->for_stmt.incr);
|
||||
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, &statement->for_stmt.incr, NULL);
|
||||
context_pop_scope(context);
|
||||
}
|
||||
if (!success) return false;
|
||||
|
||||
@@ -568,20 +565,23 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement)
|
||||
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_scope(context, &statement->for_stmt.body, NULL);
|
||||
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, &statement, NULL);
|
||||
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)
|
||||
{
|
||||
statement->goto_stmt.type = GOTO_JUMP_BACK;
|
||||
label->label_stmt.is_used = true;
|
||||
statement->goto_stmt.label = label;
|
||||
}
|
||||
@@ -604,7 +604,8 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
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.");
|
||||
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)
|
||||
@@ -614,22 +615,19 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
context_push_scope(context);
|
||||
success = success && sema_analyse_statement(context, statement->if_stmt.then_body);
|
||||
context_pop_scope(context, &statement->if_stmt.then_body, NULL);
|
||||
// TODO null flowcheck
|
||||
if (statement->if_stmt.else_body)
|
||||
{
|
||||
context_push_scope(context);
|
||||
success = success && sema_analyse_statement(context, statement->if_stmt.else_body);
|
||||
context_pop_scope(context, &statement->if_stmt.else_body, NULL);
|
||||
}
|
||||
context_pop_scope(context, &statement, NULL);
|
||||
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];
|
||||
@@ -648,7 +646,6 @@ static inline bool sema_analyse_label(Context *context, Ast *statement)
|
||||
Ast *the_goto = context->gotos[i];
|
||||
if (the_goto->goto_stmt.label_name == statement->label_stmt.name)
|
||||
{
|
||||
the_goto->goto_stmt.type = GOTO_JUMP_FORWARD;
|
||||
the_goto->goto_stmt.label = statement;
|
||||
statement->label_stmt.is_used = true;
|
||||
break;
|
||||
@@ -657,18 +654,13 @@ static inline bool sema_analyse_label(Context *context, Ast *statement)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_nop_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool sema_analyse_catch_stmt(Context *context, Ast *statement)
|
||||
static bool sema_analyse_catch_stmt(Context *context __unused, Ast *statement __unused)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
static bool sema_analyse_asm_stmt(Context *context, Ast *statement)
|
||||
static bool sema_analyse_asm_stmt(Context *context __unused, Ast *statement __unused)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
@@ -681,6 +673,30 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -697,6 +713,13 @@ static bool sema_analyse_continue_stmt(Context *context, Ast *statement)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -754,112 +777,99 @@ static bool sema_analyse_ct_if_stmt(Context *context, Ast *statement)
|
||||
}
|
||||
}
|
||||
|
||||
static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *case_stmt, Type *switch_type, Ast **prev_case)
|
||||
|
||||
static bool sema_analyse_case_expr(Context *context, Ast *case_stmt)
|
||||
{
|
||||
if (case_stmt->ast_kind == 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)
|
||||
{
|
||||
if (*prev_case)
|
||||
{
|
||||
// sema_build_defer_chain(context, prev_cases);
|
||||
context_pop_scope(context, prev_case, NULL);
|
||||
*prev_case = NULL;
|
||||
}
|
||||
Expr *case_expr = case_stmt->case_stmt.expr;
|
||||
if (!sema_analyse_expr(context, switch_type, case_expr)) return false;
|
||||
if (case_expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(case_expr, "This must be a constant expression.");
|
||||
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;
|
||||
*prev_case = case_stmt;
|
||||
VECEACH(*prev_cases, i)
|
||||
{
|
||||
if ((*prev_cases)[i]->case_stmt.val == val)
|
||||
{
|
||||
SEMA_ERROR(case_stmt, "Duplicate case value.");
|
||||
sema_prev_at_range((*prev_cases)[i]->span, "Previous use was here.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
context_push_scope_with_flags(context, SCOPE_BREAK);
|
||||
vec_add(*prev_cases, case_stmt);
|
||||
return true;
|
||||
}
|
||||
if (case_stmt->ast_kind == AST_DEFAULT_STMT)
|
||||
{
|
||||
case_stmt->ast_kind = AST_CASE_STMT;
|
||||
case_stmt->case_stmt.value_type = CASE_VALUE_DEFAULT;
|
||||
case_stmt->case_stmt.block = NULL;
|
||||
if (*prev_case)
|
||||
{
|
||||
context_pop_scope(context, prev_case, NULL);
|
||||
}
|
||||
context_push_scope(context);
|
||||
*prev_case = case_stmt;
|
||||
vec_add(*prev_cases, case_stmt);
|
||||
return true;
|
||||
}
|
||||
if (!*prev_case)
|
||||
{
|
||||
SEMA_ERROR(case_stmt, "Expected a 'case' or 'default' statement.");
|
||||
SEMA_ERROR(case_expr, "This must be a constant expression.");
|
||||
return false;
|
||||
}
|
||||
if (case_stmt->ast_kind == AST_NEXT_STMT)
|
||||
if (case_expr->const_expr.type != CONST_INT)
|
||||
{
|
||||
case_stmt->next_stmt = *prev_case;
|
||||
(*prev_case)->case_stmt.has_next = true;
|
||||
SEMA_ERROR(case_expr, "The 'case' value must be an integer constant.");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sema_analyse_statement(context, case_stmt)) return false;
|
||||
}
|
||||
if (!(*prev_case)->case_stmt.block)
|
||||
{
|
||||
(*prev_case)->case_stmt.block = AST_NEW(AST_COMPOUND_STMT, (*prev_case)->span);
|
||||
}
|
||||
vec_add((*prev_case)->case_stmt.block->compound_stmt.stmts, case_stmt);
|
||||
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 bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
context_push_scope(context);
|
||||
bool success = sema_analyse_statement(context, statement->switch_stmt.cond);
|
||||
Ast *cond = statement->switch_stmt.cond;
|
||||
success = success && sema_analyse_cond(context, cond, false);
|
||||
context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_NEXT); // NOLINT(hicpp-signed-bitwise)
|
||||
Ast *body = statement->switch_stmt.body;
|
||||
assert(body->ast_kind == AST_COMPOUND_STMT);
|
||||
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 *in_case = NULL;
|
||||
VECEACH(body->compound_stmt.stmts, i)
|
||||
Ast *default_case = NULL;
|
||||
assert(context->current_scope->defers.start == context->current_scope->defers.end);
|
||||
VECEACH(statement->switch_stmt.cases, i)
|
||||
{
|
||||
success = success && sema_analyse_switch_case(context, &statement->switch_stmt.cases, body->compound_stmt.stmts[i], switch_type, &in_case);
|
||||
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);
|
||||
}
|
||||
if (in_case)
|
||||
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)
|
||||
{
|
||||
context_pop_scope(context, &in_case, NULL);
|
||||
|
||||
TODO
|
||||
}
|
||||
context_pop_scope(context, &cond, NULL);
|
||||
context_pop_scope(context, &statement, NULL);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool sema_analyse_try_stmt(Context *context, Ast *statement)
|
||||
static bool sema_analyse_try_stmt(Context *context __unused, Ast *statement __unused)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
static bool sema_analyse_throw_stmt(Context *context, Ast *statement)
|
||||
static bool sema_analyse_throw_stmt(Context *context __unused, Ast *statement __unused)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
@@ -877,7 +887,7 @@ 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, &statement, NULL);
|
||||
context_pop_scope(context);
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -888,6 +898,7 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement
|
||||
case AST_POISONED:
|
||||
return false;
|
||||
case AST_ATTRIBUTE:
|
||||
case AST_SCOPED_STMT:
|
||||
UNREACHABLE
|
||||
case AST_ASM_STMT:
|
||||
return sema_analyse_asm_stmt(context, statement);
|
||||
@@ -922,7 +933,7 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement
|
||||
case AST_LABEL:
|
||||
return sema_analyse_label(context, statement);
|
||||
case AST_NOP_STMT:
|
||||
return sema_analyse_nop_stmt(context, statement);
|
||||
return true;
|
||||
case AST_RETURN_STMT:
|
||||
return sema_analyse_return_stmt(context, statement);
|
||||
case AST_SWITCH_STMT:
|
||||
@@ -932,7 +943,7 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement
|
||||
case AST_TRY_STMT:
|
||||
return sema_analyse_try_stmt(context, statement);
|
||||
case AST_NEXT_STMT:
|
||||
UNREACHABLE
|
||||
return sema_analyse_next_stmt(context, statement);
|
||||
case AST_VOLATILE_STMT:
|
||||
return sema_analyse_volatile_stmt(context, statement);
|
||||
case AST_WHILE_STMT:
|
||||
@@ -1007,6 +1018,7 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VECEACH(context->gotos, i)
|
||||
{
|
||||
Ast *goto_stmt = context->gotos[i];
|
||||
@@ -1018,14 +1030,15 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
}
|
||||
|
||||
// If there are no defers, then that's fine.
|
||||
if (!goto_stmt->goto_stmt.defer && !label_target->label_stmt.defer) continue;
|
||||
|
||||
// First we need to search for the common depth.
|
||||
int label_depth = defer_depth(label_target->label_stmt.defer);
|
||||
int goto_depth = defer_depth(goto_stmt->goto_stmt.defer);
|
||||
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;
|
||||
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);
|
||||
@@ -1040,17 +1053,18 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
}
|
||||
|
||||
// We now know the top defer (which we won't actually generate)
|
||||
goto_stmt->goto_stmt.defer_end = common_depth_goto;
|
||||
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, &func->func.body, NULL);
|
||||
context_pop_scope(context);
|
||||
context->current_scope = NULL;
|
||||
return true;
|
||||
}
|
||||
@@ -1062,7 +1076,9 @@ static inline bool sema_analyse_method_function(Context *context, Decl *decl)
|
||||
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));
|
||||
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;
|
||||
@@ -1101,7 +1117,7 @@ static inline bool sema_analyse_func(Context *context, Decl *decl)
|
||||
}
|
||||
decl->visibility = VISIBLE_EXTERN;
|
||||
}
|
||||
DEBUG_LOG("Function analysis done.")
|
||||
DEBUG_LOG("Function analysis done.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1279,7 +1295,6 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void append_decls(Context *context, Decl **decls)
|
||||
{
|
||||
VECEACH(decls, i)
|
||||
@@ -1287,6 +1302,7 @@ static void append_decls(Context *context, Decl **decls)
|
||||
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);
|
||||
@@ -1387,7 +1403,16 @@ bool sema_resolve_type_info(Context *context, TypeInfo *type_info)
|
||||
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);
|
||||
Decl *decl = sema_resolve_symbol(context,
|
||||
type_info->unresolved.name_loc.string,
|
||||
type_info->unresolved.path,
|
||||
&ambiguous_decl);
|
||||
|
||||
// Already handled
|
||||
if (!decl_ok(decl))
|
||||
{
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
|
||||
if (!decl)
|
||||
{
|
||||
@@ -1397,8 +1422,11 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *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);
|
||||
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)
|
||||
@@ -1453,7 +1481,9 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,3 +16,6 @@
|
||||
|
||||
#define MAX_IDENTIFIER_LENGTH 31
|
||||
#define PROJECT_TOML "project.toml"
|
||||
#ifndef __unused
|
||||
#define __unused
|
||||
#endif
|
||||
@@ -27,7 +27,7 @@ void error_exit(const char *format, ...) __attribute__((noreturn));
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DEBUG_LOG(_string, ...) eprintf("-- DEBUG: "); eprintf(_string, ##__VA_ARGS__); eprintf("\n");
|
||||
#define DEBUG_LOG(_string, ...) eprintf("-- DEBUG: "); eprintf(_string, ##__VA_ARGS__); eprintf("\n")
|
||||
#else
|
||||
#define DEBUG_LOG(_string, ...)
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user