Skip to content

Commit 7ef6f27

Browse files
authored
Merge pull request #182 from onyx-lang/experiment/for-macros
Experiment: For-loops as macro expansions
2 parents 48f09c7 + c51efc5 commit 7ef6f27

21 files changed

+696
-706
lines changed

compiler/include/astnodes.h

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -866,9 +866,7 @@ struct AstFor {
866866
// NOTE: Stores the iteration variable
867867
Scope *scope;
868868

869-
// NOTE: Local defining the iteration variable
870-
AstLocal* var;
871-
AstLocal* index_var;
869+
bh_arr(AstLocal *) indexing_variables;
872870

873871
// NOTE: This can be any expression, but it is checked that
874872
// it is of a type that we know how to iterate over.
@@ -877,16 +875,18 @@ struct AstFor {
877875

878876
AstBlock *stmt;
879877

878+
// NOTE: This is set when a for-loop isn't over a primitive type
879+
// and instead is over a custom type, such as `Iterator` or `Map`.
880+
// To properly invoke `__for_expansion`, we need to store the prepared
881+
// call, as there could be overloads we have to wait for by yielding.
882+
AstCall *intermediate_macro_expansion;
883+
880884
// ROBUSTNESS: This should be able to be set by a compile time variable at some point.
881885
// But for now, this will do.
882886
b32 by_pointer : 1;
883887
b32 no_close : 1;
884-
b32 has_first : 1;
885-
886-
// NOTE: This is used by the AstDirectiveFirst node for this
887-
// for node to know which local variable to use.
888-
u64 first_local;
889888
};
889+
890890
struct AstIfWhile {
891891
AstNode_base;
892892

@@ -907,7 +907,14 @@ struct AstIfWhile {
907907
};
908908

909909
// Used by While
910-
b32 bottom_test;
910+
struct {
911+
// NOTE: This is used by the AstDirectiveFirst node for this
912+
// for node to know which local variable to use.
913+
u64 first_local;
914+
b32 has_first;
915+
916+
b32 bottom_test;
917+
};
911918
};
912919
};
913920
typedef struct AstIfWhile AstIf;
@@ -1572,7 +1579,8 @@ struct AstDirectiveRemove {
15721579

15731580
struct AstDirectiveFirst {
15741581
AstTyped_base;
1575-
AstFor *for_node;
1582+
1583+
AstIfWhile *while_node;
15761584
};
15771585

15781586
struct AstDirectiveExportName {
@@ -1607,12 +1615,17 @@ struct AstCallSite {
16071615
b32 collapsed : 1;
16081616
};
16091617

1618+
typedef struct CodeBlockBindingSymbol {
1619+
OnyxToken *symbol;
1620+
AstType *type_node; // This can be NULL if no type was given.
1621+
} CodeBlockBindingSymbol;
1622+
16101623
// Represents a "pastable" block of code.
16111624
struct AstCodeBlock {
16121625
AstTyped_base;
16131626

16141627
AstNode *code;
1615-
bh_arr(OnyxToken *) binding_symbols;
1628+
bh_arr(CodeBlockBindingSymbol) binding_symbols;
16161629

16171630
b32 is_expression: 1;
16181631
};
@@ -1622,6 +1635,9 @@ struct AstDirectiveInsert {
16221635

16231636
AstTyped *code_expr;
16241637
bh_arr(AstTyped *) binding_exprs;
1638+
1639+
// Set when using #skip_scope
1640+
AstTyped *skip_scope_index;
16251641
};
16261642

16271643
struct AstDirectiveInit {
@@ -1921,8 +1937,7 @@ typedef enum CheckerMode {
19211937
typedef struct CheckerData {
19221938
b32 expression_types_must_be_known;
19231939
b32 all_checks_are_final;
1924-
b32 inside_for_iterator;
1925-
bh_arr(AstFor *) for_node_stack;
1940+
bh_arr(AstIfWhile *) while_node_stack;
19261941
bh_imap __binop_impossible_cache[Binary_Op_Count];
19271942
AstCall __op_maybe_overloaded;
19281943
Entity *current_entity;
@@ -2109,6 +2124,9 @@ struct CompilerBuiltins {
21092124
bh_arr(AstFunction *) init_procedures;
21102125
AstOverloadedFunction *implicit_bool_cast;
21112126
AstOverloadedFunction *dispose_used_local;
2127+
2128+
AstType *for_expansion_flag_type;
2129+
AstOverloadedFunction *for_expansion;
21122130
};
21132131

21142132
typedef struct TypeStore TypeStore;
@@ -2361,6 +2379,8 @@ AstPolyCallType* convert_call_to_polycall(Context *context, AstCall* call);
23612379

23622380
void insert_auto_dispose_call(Context *context, AstLocal *local);
23632381

2382+
AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode);
2383+
23642384
typedef struct OverloadReturnTypeCheck {
23652385
Type *expected_type;
23662386
AstTyped *node;

compiler/src/astnodes.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1948,6 +1948,61 @@ void insert_auto_dispose_call(Context *context, AstLocal *local) {
19481948
}
19491949

19501950

1951+
AstCall * create_implicit_for_expansion_call(Context *context, AstFor *fornode) {
1952+
AstCall *call = onyx_ast_node_new(context->ast_alloc, sizeof(AstCall), Ast_Kind_Call);
1953+
call->token = fornode->token;
1954+
call->callee = (AstTyped *) context->builtins.for_expansion;
1955+
call->next = fornode->next;
1956+
1957+
arguments_initialize(context, &call->args);
1958+
1959+
//
1960+
// Create the code block that will represent the body of the for-loop.
1961+
// This code block is given up to 2 inputs, depending on if the index variable
1962+
// was set in the for-loop. The implementer of a __for_expansion overload must
1963+
// always provide 2 values when `#unquote`-ing, as they cannot currently know if
1964+
// the index variable was asked for.
1965+
//
1966+
// Maybe we could pass `void` as the `index_type` if the index variable is not necessary?
1967+
// I would like to keep the implementations of __for_expansion simple and easy
1968+
// to read, but sometimes there are extra complications you cannot avoid...
1969+
//
1970+
AstCodeBlock *body_code_block = onyx_ast_node_new(context->ast_alloc, sizeof(AstCodeBlock), Ast_Kind_Code_Block);
1971+
body_code_block->token = fornode->token;
1972+
body_code_block->type_node = context->builtins.code_type;
1973+
body_code_block->code = (AstNode *) fornode->stmt;
1974+
((AstBlock *) body_code_block->code)->rules = Block_Rule_Code_Block;
1975+
1976+
bh_arr_new(context->ast_alloc, body_code_block->binding_symbols, bh_arr_length(fornode->indexing_variables));
1977+
bh_arr_each(AstLocal *, indexing_variable, fornode->indexing_variables) {
1978+
CodeBlockBindingSymbol sym;
1979+
sym.symbol = (*indexing_variable)->token;
1980+
sym.type_node = (*indexing_variable)->type_node;
1981+
bh_arr_push(body_code_block->binding_symbols, sym);
1982+
}
1983+
1984+
i32 flags = 0;
1985+
if (fornode->by_pointer) flags |= 1; // BY_POINTER
1986+
if (fornode->no_close) flags |= 2; // NO_CLOSE
1987+
1988+
AstNumLit *flag_node = make_int_literal(context, flags);
1989+
flag_node->type_node = context->builtins.for_expansion_flag_type;
1990+
// flag_node->type = type_build_from_ast(context, context->builtins.for_expansion_flag_type);
1991+
// assert(flag_node->type);
1992+
1993+
// Arguments are:
1994+
// Iterator
1995+
// Code block with 2 inputs (value, index)
1996+
// Flags
1997+
bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) fornode->iter));
1998+
bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) flag_node));
1999+
bh_arr_push(call->args.values, (AstTyped *) make_argument(context, (AstTyped *) body_code_block));
2000+
2001+
return call;
2002+
}
2003+
2004+
2005+
19512006
b32 resolve_intrinsic_interface_constraint(Context *context, AstConstraint *constraint) {
19522007
AstInterface *interface = constraint->interface;
19532008
Type* type = type_build_from_ast(context, (AstType *) constraint->args[0]);

compiler/src/builtins.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,18 @@ void initialize_builtins(Context *context) {
511511
return;
512512
}
513513

514+
context->builtins.for_expansion = (AstOverloadedFunction *) symbol_raw_resolve(context, p->scope, "__for_expansion");
515+
if (context->builtins.for_expansion == NULL || context->builtins.for_expansion->kind != Ast_Kind_Overloaded_Function) {
516+
ONYX_ERROR((OnyxFilePos) { 0 }, Error_Critical, "'__for_expansion' #match procedure not found.");
517+
return;
518+
}
519+
520+
context->builtins.for_expansion_flag_type = (AstType *) symbol_raw_resolve(context, p->scope, "__For_Expansion_Flags");
521+
if (context->builtins.for_expansion_flag_type == NULL || context->builtins.for_expansion_flag_type->kind != Ast_Kind_Enum_Type) {
522+
ONYX_ERROR((OnyxFilePos) { 0 }, Error_Critical, "'__For_Expansion_Flags' enum procedure not found.");
523+
return;
524+
}
525+
514526
context->builtins.closure_block_allocate = (AstFunction *) symbol_raw_resolve(context, p->scope, "__closure_block_allocate");
515527
if (context->builtins.closure_block_allocate == NULL || context->builtins.closure_block_allocate->kind != Ast_Kind_Function) {
516528
ONYX_ERROR((OnyxFilePos) { 0 }, Error_Critical, "'__closure_block_allocate' procedure not found.");

0 commit comments

Comments
 (0)