Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment: For-loops as macro expansions #182

Merged
merged 20 commits into from
Jan 19, 2025
Merged

Conversation

brendanfh
Copy link
Collaborator

@brendanfh brendanfh commented Jan 10, 2025

This experimental pull-request make a large change to how for loops work in the language.

Previously, for loops had many special cases baked into the compiler so that they could support a variety of different iterable types, including i32, i64, range, range64, [N] T, [] T, [..] T, and Iterator(T). While this worked, and provided a way to have custom, user-defined iterators, it was limited to only have one iterable value at a time, and bloated the compiler by about 1000 lines to handle all the special code generation cases.

In this pull-request, for loops are changed to simply be a function call to __for_expansion, a special builtin overloaded function. This function has overloads for all the previously supported type, as well other types like Map(K, V) and Set(T). A for loop is converted to the function call in the following way.

for k, v in some_map {
    // For-loop body
}

__for_expansion(some_map, 0, [k, v] {
    // For-loop body
})

As you can see, the iterator of the loop is placed as the first argument unmodified. The body of the for-loop is converted into a code block that takes binding parameters with the specified names and given as the last argument. The second argument is a set of flags -- bit 1 is set if the for loop was by pointer (for &), and bit 2 is set if the for loop was specified with #no_close.

The overloads for __for_expansion all take on about the same shape. A while loop with an #unquote directive inside of it, with a special specifier called #skip_scope(N) (also introduced in this pull-request) that tell the unquoted block to start looking for symbols N scopes up, effectively hiding the symbols defined in the macro. Here is the definition of __for_expansion for [] T by value. Notice the compile-time check that the first bit of flags is not set, ensuring this is not by pointer. This overload is also written in a way that provides 2 values to the code block, allowing for using an iterating index at the same time.

#overload
__for_expansion :: macro (_s: [] $T, $flags: i32, $body: Code) where !(flags & .BY_POINTER) {
    s     := _s
    data  := s.data
    count := s.count

    i := 0
    while i < count {
        defer i += 1

        v := data[i]
        #unquote body(v, ~~i) #skip_scope(2)
    }
}

@brendanfh brendanfh added the enhancement New feature or request label Jan 10, 2025
@brendanfh brendanfh added this to the v0.1.14 milestone Jan 10, 2025
@brendanfh brendanfh self-assigned this Jan 10, 2025
@brendanfh brendanfh merged commit 7ef6f27 into master Jan 19, 2025
15 checks passed
@brendanfh brendanfh deleted the experiment/for-macros branch January 19, 2025 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant