\
NODE(CodeBlock) \
NODE(DirectiveInsert) \
+ NODE(Macro) \
\
NODE(Package)
Ast_Kind_Code_Block,
Ast_Kind_Directive_Insert,
+ Ast_Kind_Macro,
Ast_Kind_Note,
VA_Kind_Untyped,
} VarArgKind;
+typedef enum BlockRule {
+ Block_Rule_New_Scope = BH_BIT(1),
+ Block_Rule_New_Binding_Scope = BH_BIT(2), // Unused
+ Block_Rule_Clear_Defer = BH_BIT(3),
+
+ Block_Rule_Normal = Block_Rule_New_Scope | Block_Rule_New_Binding_Scope | Block_Rule_Clear_Defer,
+ Block_Rule_Macro = Block_Rule_New_Scope | Block_Rule_New_Binding_Scope,
+ Block_Rule_Code_Block = Block_Rule_New_Scope,
+} BlockRule;
+
typedef struct Arguments Arguments;
struct Arguments {
bh_arr(AstTyped *) values;
// Structure Nodes
struct AstBlock {
AstNode_base;
+
AstNode *body;
- Scope *scope;
+ Scope *scope;
Scope *binding_scope;
+ BlockRule rules;
};
struct AstDefer { AstNode_base; AstNode *stmt; };
struct AstFor {
AstTyped *code_expr;
};
+struct AstMacro {
+ AstTyped_base;
+
+ AstTyped* body;
+};
+
typedef enum EntityState {
Entity_State_Error,
Entity_Type_Memory_Reservation_Type,
Entity_Type_Use,
Entity_Type_Polymorphic_Proc,
+ Entity_Type_Macro,
Entity_Type_Foreign_Function_Header,
Entity_Type_Foreign_Global_Header,
Entity_Type_Function_Header,
AstEnumType *enum_type;
AstMemRes *mem_res;
AstPolyProc *poly_proc;
+ AstMacro *macro;
AstUse *use;
};
} Entity;
Token_Type_Keyword_Case,
Token_Type_Keyword_Switch,
Token_Type_Keyword_Fallthrough,
+ Token_Type_Keyword_Macro,
Token_Type_Right_Arrow,
Token_Type_Left_Arrow,
# strings in YAML. When using single quoted strings, only single quotes
# need to be escaped: this is done by using two single quotes next to each
# other.
- - match: '\b(package|struct|use|global|enum|if|elseif|else|for|while|do|break|continue|fallthrough|return|as|cast|sizeof|alignof|defer|switch|case)\b'
+ - match: '\b(package|struct|use|global|enum|if|elseif|else|for|while|do|break|continue|fallthrough|return|as|cast|sizeof|alignof|defer|switch|case|macro)\b'
scope: keyword.control.onyx
- match: '\b(bool|void|i8|u8|i16|u16|i32|u32|i64|u64|f32|f64|rawptr|str|cstr|range|type_expr|any)\b'
1: entity.name.type
2: keyword.control.onyx
+ - match: '([a-zA-Z_][a-zA-Z0-9_]*)\s*::\s*(macro)'
+ captures:
+ 1: entity.name.function
+ 2: keyword.control.onyx
+
- match: '([a-zA-Z_][a-zA-Z0-9_]*)\s*::\s*(enum)'
captures:
1: entity.name.enum
let s:cpo_save = &cpo
set cpo&vim
-syn keyword onyxKeyword package struct enum proc use global
+syn keyword onyxKeyword package struct enum proc use global macro
syn keyword onyxKeyword if elseif else
syn keyword onyxKeyword for while do
syn keyword onyxKeyword switch case
"CALL SITE",
"CODE BLOCK",
- "DIRECTIVE INSERT"
+ "DIRECTIVE INSERT",
+ "MACRO",
"NOTE",
"Memory Reservation Type",
"Use",
"Polymorphic Proc",
+ "Macro",
"Foreign_Function Header",
"Foreign_Global Header",
"Function Header",
CheckStatus check_while(AstIfWhile* whilenode);
CheckStatus check_for(AstFor* fornode);
CheckStatus check_switch(AstSwitch* switchnode);
-CheckStatus check_call(AstCall* call);
+CheckStatus check_call(AstCall** pcall);
CheckStatus check_binaryop(AstBinaryOp** pbinop);
CheckStatus check_unaryop(AstUnaryOp** punop);
CheckStatus check_struct_literal(AstStructLiteral* sl);
AS_Expecting_Untyped_VA,
} ArgState;
-CheckStatus check_call(AstCall* call) {
+CheckStatus check_call(AstCall** pcall) {
// All the things that need to be done when checking a call node.
// 1. Ensure the callee is not a symbol
// 2. Check the callee expression (since it could be a variable or a field access, etc)
// 7. Fill in arguments
// 8. If callee is an intrinsic, turn call into an Intrinsic_Call node
// 9. Check types of formal and actual params against each other, handling varargs
-
+ AstCall* call = *pcall;
+
if (call->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+ u32 current_checking_level_store = current_checking_level;
CHECK(expression, &call->callee);
CHECK(arguments, &call->args);
+ current_checking_level = current_checking_level_store;
// SPEED CLEANUP: Keeping an original copy for basically no reason except that sometimes you
// need to know the baked argument values in code generation.
while (call->callee->kind == Ast_Kind_Alias) call->callee = ((AstAlias *) call->callee)->alias;
- if (call->callee->kind == Ast_Kind_Overloaded_Function) {
+ AstTyped *effective_callee = call->callee;
+
+ // If we are "calling" a macro, the callee on the call node should not be replaced
+ // as then it would remove the macro from the callee, which would turn it into a
+ // normal function call.
+ b32 calling_a_macro = 0;
+
+ if (effective_callee->kind == Ast_Kind_Macro) {
+ calling_a_macro = 1;
+ if (current_checking_level == EXPRESSION_LEVEL) {
+ onyx_report_error(call->token->pos, "Macros calls are not allowed at the expression level yet.");
+ return Check_Error;
+ }
+
+ effective_callee = ((AstMacro * ) effective_callee)->body;
+ }
+
+ if (effective_callee->kind == Ast_Kind_Overloaded_Function) {
b32 should_yield = 0;
- AstTyped* new_callee = find_matching_overload_by_arguments(((AstOverloadedFunction *) call->callee)->overloads, &call->args, &should_yield);
+ AstTyped* new_callee = find_matching_overload_by_arguments(((AstOverloadedFunction *) effective_callee)->overloads, &call->args, &should_yield);
if (new_callee == NULL) {
- if (call->callee->entity->state > Entity_State_Check_Types && !should_yield) {
+ if (effective_callee->entity->state > Entity_State_Check_Types && !should_yield) {
report_unable_to_match_overload(call);
return Check_Error;
}
}
- call->callee = new_callee;
+ effective_callee = new_callee;
+ if (!calling_a_macro) call->callee = new_callee;
}
- if (call->callee->kind == Ast_Kind_Polymorphic_Proc) {
- AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstPolyProc *) call->callee, PPLM_By_Arguments, &call->args, call->token);
+ if (effective_callee->kind == Ast_Kind_Polymorphic_Proc) {
+ AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstPolyProc *) effective_callee, PPLM_By_Arguments, &call->args, call->token);
if (new_callee == (AstTyped *) &node_that_signals_a_yield) return Check_Yield_Macro;
if (new_callee == NULL) return Check_Error;
arguments_remove_baked(&call->args);
- call->callee = new_callee;
+ effective_callee = new_callee;
+ if (!calling_a_macro) call->callee = new_callee;
}
- AstFunction* callee = (AstFunction *) call->callee;
+ AstFunction* callee = (AstFunction *) effective_callee;
// NOTE: Build callee's type
fill_in_type((AstTyped *) callee);
return Check_Error;
}
-
// CLEANUP maybe make function_get_expected_arguments?
i32 non_vararg_param_count = (i32) callee->type->Function.param_count;
if (non_vararg_param_count > 0 &&
}
arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
- break;
+ break;CheckStatus check_compound(AstCompound* compound);
+
}
}
return Check_Error;
}
- callee->flags |= Ast_Flag_Function_Used;
+ if (!calling_a_macro) {
+ callee->flags |= Ast_Flag_Function_Used;
+
+ } else {
+ // Macro expansion
+ }
return Check_Success;
}
AstCall *implicit_call = binaryop_try_operator_overload(binop);
if (implicit_call != NULL) {
- CHECK(call, implicit_call);
+ CHECK(call, &implicit_call);
// NOTE: Not a binary op
*pbinop = (AstBinaryOp *) implicit_call;
AstCall *implicit_call = binaryop_try_operator_overload(binop);
if (implicit_call != NULL) {
- CHECK(call, implicit_call);
+ CHECK(call, &implicit_call);
// NOTE: Not an array access
*psub = (AstSubscript *) implicit_call;
}
(*mcall)->flags |= Ast_Flag_Has_Been_Checked;
- CHECK(call, call_node);
+ CHECK(call, &call_node);
call_node->next = (*mcall)->next;
*mcall = (AstBinaryOp *) call_node;
return Check_Success;
}
+ if (expr->kind == Ast_Kind_Macro) {
+ return Check_Success;
+ }
+
fill_in_type(expr);
current_checking_level = EXPRESSION_LEVEL;
case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp **) pexpr); break;
case Ast_Kind_Unary_Op: retval = check_unaryop((AstUnaryOp **) pexpr); break;
- case Ast_Kind_Call: retval = check_call((AstCall *) expr); break;
+ case Ast_Kind_Call: retval = check_call((AstCall **) pexpr); break;
case Ast_Kind_Argument: retval = check_argument((AstArgument **) pexpr); break;
case Ast_Kind_Block: retval = check_block((AstBlock *) expr); break;
AstNode* cloned_block = ast_clone(context.ast_alloc, code_block->code);
cloned_block->next = insert->next;
- if (cloned_block->kind == Ast_Kind_Block) {
+ /*if (cloned_block->kind == Ast_Kind_Block) {
AstNode* next = insert->next;
insert->next = (AstNode *) ((AstBlock *) cloned_block)->body;
last_stmt->next = next;
} else {
+ */
*(AstNode **) pinsert = cloned_block;
- }
+ //}
insert->flags |= Ast_Flag_Has_Been_Checked;
case Ast_Kind_Switch: return check_switch((AstSwitch *) stmt);
case Ast_Kind_Block: return check_block((AstBlock *) stmt);
case Ast_Kind_Defer: return check_statement(&((AstDefer *) stmt)->stmt);
+ case Ast_Kind_Call: {
+ CHECK(call, (AstCall **) pstmt);
+ stmt->flags |= Ast_Flag_Expr_Ignored;
+ return Check_Success;
+ }
case Ast_Kind_Binary_Op:
CHECK(binaryop, (AstBinaryOp **) pstmt);
return Check_Success;
}
+CheckStatus check_macro(AstMacro* macro) {
+ if (macro->body->kind == Ast_Kind_Function) {
+ CHECK(function_header, (AstFunction *) macro->body);
+ }
+
+ return Check_Success;
+}
+
CheckStatus check_node(AstNode* node) {
switch (node->kind) {
case Ast_Kind_Function: return check_function((AstFunction *) node);
case Ast_Kind_If: return check_if((AstIfWhile *) node);
case Ast_Kind_Static_If: return check_if((AstIfWhile *) node);
case Ast_Kind_While: return check_while((AstIfWhile *) node);
- case Ast_Kind_Call: return check_call((AstCall *) node);
+ case Ast_Kind_Call: return check_call((AstCall **) &node);
case Ast_Kind_Binary_Op: return check_binaryop((AstBinaryOp **) &node);
default: return check_expression((AstTyped **) &node);
}
case Entity_Type_Memory_Reservation_Type: cs = check_memres_type(ent->mem_res); break;
case Entity_Type_Memory_Reservation: cs = check_memres(ent->mem_res); break;
case Entity_Type_Static_If: cs = check_static_if(ent->static_if); break;
+ case Entity_Type_Macro: cs = check_macro(ent->macro);
case Entity_Type_Expression:
cs = check_expression(&ent->expr);
ENTITY_INSERT(ent);
break;
}
+
+ case Ast_Kind_Macro: {
+ ent.type = Entity_Type_Macro;
+ ent.macro = (AstMacro *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
case Ast_Kind_Polymorphic_Proc: {
ent.type = Entity_Type_Polymorphic_Proc;
}
void onyx_clear_errors() {
- bh_printf("ERRORS WERE CLEARED!!!\n");
bh_arr_set_length(errors.errors, 0);
}
"switch",
"case",
"fallthrough",
+ "macro",
"->",
"<-",
case 'i':
LITERAL_TOKEN("if", 1, Token_Type_Keyword_If);
break;
+ case 'm':
+ LITERAL_TOKEN("macro", 1, Token_Type_Keyword_Macro);
+ break;
case 'p':
LITERAL_TOKEN("package", 1, Token_Type_Keyword_Package);
LITERAL_TOKEN("proc", 1, Token_Type_Keyword_Proc);
break;
}
+ #if 0
case Token_Type_Keyword_Proc: {
OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc);
onyx_report_warning(proc_token->pos, "Warning: 'proc' is a deprecated keyword.");
retval = (AstTyped *) parse_function_definition(parser, proc_token);
break;
}
+ #endif
case Token_Type_Keyword_Package: {
retval = (AstTyped *) parse_package_expression(parser);
if (parser->curr->type == '{') {
code_block->code = (AstNode *) parse_block(parser, 1);
+ ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
} else {
code_block->code = (AstNode *) parse_expression(parser, 0);
static AstBlock* parse_block(OnyxParser* parser, b32 make_a_new_scope) {
AstBlock* block = make_node(AstBlock, Ast_Kind_Block);
+ block->rules = Block_Rule_Normal;
// NOTE: --- is for an empty block
if (parser->curr->type == Token_Type_Empty_Block) {
}
static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* token) {
- // :TemporaryForProcRemoval
- if (parser->curr->type == '{') {
- return (AstFunction *) parse_overloaded_function(parser, token);
- }
-
AstFunction* func_def = make_node(AstFunction, Ast_Kind_Function);
func_def->token = token;
pp->poly_params = polymorphic_vars;
pp->base_func = func_def;
- ENTITY_SUBMIT(pp);
return (AstFunction *) pp;
+
} else {
bh_arr_free(polymorphic_vars);
-
- ENTITY_SUBMIT(func_def);
return func_def;
}
}
static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
+ #if 0
if (parser->curr->type == Token_Type_Keyword_Proc) {
OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc);
onyx_report_warning(proc_token->pos, "Warning: 'proc' is a deprecated keyword.");
*ret = (AstTyped *) func_node;
return 1;
}
+ #endif
if (parser->curr->type == '(') {
OnyxToken* matching_paren = find_matching_paren(parser->curr);
OnyxToken* proc_token = parser->curr;
AstFunction* func_node = parse_function_definition(parser, proc_token);
+ ENTITY_SUBMIT(func_node);
*ret = (AstTyped *) func_node;
return 1;
}
return static_if_node;
}
+static AstMacro* parse_macro(OnyxParser* parser) {
+ AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
+ macro->token = expect_token(parser, Token_Type_Keyword_Macro);
+ macro->body = (AstTyped *) parse_function_definition(parser, macro->token);
+
+ ENTITY_SUBMIT(macro);
+ return macro;
+}
+
static AstTyped* parse_top_level_expression(OnyxParser* parser) {
+ #if 0
if (parser->curr->type == Token_Type_Keyword_Proc) {
OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc);
onyx_report_warning(proc_token->pos, "Warning: 'proc' is a deprecated keyword.");
return (AstTyped *) func_node;
}
+ #endif
if (parser->curr->type == Token_Type_Keyword_Global) return parse_global_declaration(parser);
if (parser->curr->type == Token_Type_Keyword_Struct) return (AstTyped *) parse_struct(parser);
if (parser->curr->type == Token_Type_Keyword_Enum) return (AstTyped *) parse_enum_declaration(parser);
+ if (parser->curr->type == Token_Type_Keyword_Macro) return (AstTyped *) parse_macro(parser);
if (parse_possible_directive(parser, "type")) {
AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
proc->base_func->name = symbol;
+ } else if (node->kind == Ast_Kind_Macro) {
+ AstMacro* macro = (AstMacro *) node;
+
+ AstFunction* func = (AstFunction *) macro->body;
+ if (func->kind == Ast_Kind_Polymorphic_Proc)
+ func = (AstFunction *) ((AstPolyProc *) func)->base_func;
+
+ func->name = symbol;
+
} else if (node->kind == Ast_Kind_Global) {
AstGlobal* global = (AstGlobal *) node;
// Variables used during the symbol resolution phase.
static Scope* curr_scope = NULL;
-bh_arr(AstBlock *) block_stack = NULL;
static b32 report_unresolved_symbols = 1;
// Everything related to waiting on is imcomplete at the moment.
static SymresStatus symres_memres(AstMemRes** memres);
static SymresStatus symres_struct_defaults(AstType* st);
static SymresStatus symres_static_if(AstIf* static_if);
+static SymresStatus symres_macro(AstMacro* macro);
static void scope_enter(Scope* new_scope) {
curr_scope = new_scope;
SYMRES(expression, &ifnode->cond);
+ // NOTE: These are statements because "elseif" means the `false_stmt` has an if node.
if (ifnode->true_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
}
static SymresStatus symres_block(AstBlock* block) {
- if (block->scope == NULL)
- block->scope = scope_create(context.ast_alloc, curr_scope, block->token->pos);
+ if (block->rules & Block_Rule_New_Scope) {
+ if (block->scope == NULL)
+ block->scope = scope_create(context.ast_alloc, curr_scope, block->token->pos);
- scope_enter(block->scope);
- bh_arr_push(block_stack, block);
+ scope_enter(block->scope);
+ }
if (block->binding_scope != NULL)
- scope_include(block->scope, block->binding_scope, block->token->pos);
+ scope_include(curr_scope, block->binding_scope, block->token->pos);
if (block->body)
SYMRES(statement_chain, &block->body);
- bh_arr_pop(block_stack);
- scope_leave();
+ if (block->rules & Block_Rule_New_Scope)
+ scope_leave();
+
return Symres_Success;
}
return Symres_Success;
}
-void symres_entity(Entity* ent) {
- if (block_stack == NULL) bh_arr_new(global_heap_allocator, block_stack, 16);
+static SymresStatus symres_macro(AstMacro* macro) {
+ if (macro->body->kind == Ast_Kind_Function) {
+ SYMRES(function_header, (AstFunction *) macro->body);
+ }
+ return Symres_Success;
+}
+
+void symres_entity(Entity* ent) {
Scope* old_scope = NULL;
if (ent->scope) {
old_scope = curr_scope;
case Entity_Type_Polymorphic_Proc: ss = symres_polyproc(ent->poly_proc); break;
case Entity_Type_String_Literal: ss = symres_expression(&ent->expr); break;
case Entity_Type_Struct_Member_Default: ss = symres_struct_defaults((AstType *) ent->type_alias); break;
- case Entity_Type_Process_Directive: ss = symres_process_directive((AstNode *) ent->expr);
- break;
+ case Entity_Type_Process_Directive: ss = symres_process_directive((AstNode *) ent->expr); break;
+ case Entity_Type_Macro: ss = symres_macro(ent->macro); break;
default: break;
}
AstPolyProc* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyProc), Ast_Kind_Polymorphic_Proc);
new_pp->token = pp->token; // TODO: Change this to be the solidify->token
new_pp->base_func = pp->base_func;
- new_pp->poly_scope = new_pp->poly_scope; // CLEANUP: This doesn't seem correct??
new_pp->flags = pp->flags;
new_pp->poly_params = pp->poly_params;
EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers) {
bh_arr(WasmInstruction) code = *pcode;
+ generate_block_headers = generate_block_headers && (block->rules & Block_Rule_New_Scope);
+
if (generate_block_headers)
emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
emit_statement(mod, &code, stmt);
}
- emit_deferred_stmts(mod, &code);
- emit_free_local_allocations(mod, &code);
+ if (block->rules & Block_Rule_Clear_Defer) emit_deferred_stmts(mod, &code);
+ if (block->rules & Block_Rule_New_Scope) emit_free_local_allocations(mod, &code);
if (generate_block_headers)
emit_leave_structured_block(mod, &code);
u64 depth = bh_arr_length(mod->structured_jump_target);
- while (bh_arr_length(mod->deferred_stmts) > 0 && bh_arr_last(mod->deferred_stmts).depth == depth) {
+ while (bh_arr_length(mod->deferred_stmts) > 0 && bh_arr_last(mod->deferred_stmts).depth >= depth) {
emit_deferred_stmt(mod, pcode, bh_arr_last(mod->deferred_stmts));
bh_arr_pop(mod->deferred_stmts);
}