Skip to content

Commit

Permalink
Merge pull request #14 from JuliaPluto/remove-configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
fonsp authored Nov 16, 2023
2 parents 0f95499 + 5d46397 commit a43b31c
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 336 deletions.
49 changes: 15 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,50 +168,31 @@ SymbolsState(

```

## Configuration
## Macro calls

It is possible to tweak the behaviour of ExpressionExplorer for specific expression types. For example, you can choose to never look inside a `for` expression (not sure why you would want that). In Pluto, we use this to tweak the way `macrocall` expressions are explored, in a way that we did not want to include in this more general package.
ExpressionExplorer ignores the arguments of macro calls. Macros can transform an expression into anything, so the output of ExpressionExplorer for expressions with a macro call is ambiguous. For example, the expression `@time x` contains a *reference* to `x`, while `@gensym x` contains a *definition* of `x`.

You can configure ExpressionExplorer by creating a new subtype:
In this example, notice that the assignment to `x` and reference to `y` are detected, but `AAA` and `BBB` are ignored, because they happen inside a macro call argument.

```julia
struct IgnoreForLoops <: ExpressionExplorer.AbstractExpressionExplorerConfiguration
end
julia> ExpressionExplorer.compute_reactive_node(quote
x = y
@time AAA = BBB
end)
ReactiveNode(Set([Symbol("@time"), :y]), Set([:x]), Set{Symbol}(), Set{FunctionNameSignaturePair}(), Set{Symbol}(), Set([Symbol("@time")]))
```

Choose an `explore_...` method that you want to overload, and add an overload with `IgnoreForLoops`. You will need to read ExpressionExplorer's source code for this.
To solve this, you can **macroexpand expressions before giving them to ExpressionExplorer**. For example:

```julia
function ExpressionExplorer.explore_inner_scoped(ex::Expr, scopestate::ExpressionExplorer.ScopeState{IgnoreForLoops})::SymbolsState
if ex.head === :for
# ignore everything: report that nothing was found here:
return SymbolsState()
else
# the original code:
innerscopestate = deepcopy(scopestate)
innerscopestate.inglobalscope = false

return mapfoldl(a -> explore!(a, innerscopestate), ex.args)
end
end
julia> ExpressionExplorer.compute_reactive_node(macroexpand(Main, quote
x = y
@time AAA = BBB
end))
ReactiveNode(Set([:first, :GC_Diff, :isnothing, :gc_alloc_count, :-, :gc_num, :cumulative_compile_time_ns, :time_ns, :y, :BBB, :print, :cumulative_compile_timing, :time_print, :last, :!]), Set([:AAA, :x]), Set{Symbol}(), Set{FunctionNameSignaturePair}(), Set{Symbol}(), Set{Symbol}())
```

Then, use the `configuration` keyword argument when using ExpressionExplorer:

```julia
julia> my_expr = quote
a = b
for x in z
w = rrr
end
end;

julia> ExpressionExplorer.compute_reactive_node(my_expr; configuration=IgnoreForLoops())
ReactiveNode(Set([:b]), Set([:a]), Set{Symbol}(), Set{FunctionNameSignaturePair}(), Set{Symbol}(), Set{Symbol}())

julia> ExpressionExplorer.compute_reactive_node(my_expr)
ReactiveNode(Set([:b, :rrr, :z]), Set([:a]), Set{Symbol}(), Set{FunctionNameSignaturePair}(), Set{Symbol}(), Set{Symbol}())
```
Notice that now, `AAA` and `BBB` are detected, along with functions used inside the `@time` expression.

## Utility functions

Expand Down
20 changes: 7 additions & 13 deletions src/explore.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,14 @@ Base.@kwdef mutable struct SymbolsState
macrocalls::Set{FunctionName} = Set{FunctionName}()
end


abstract type AbstractExpressionExplorerConfiguration end

struct DefaultConfiguration <: AbstractExpressionExplorerConfiguration end

"ScopeState moves _up_ the ASTree: it carries scope information up towards the endpoints."
mutable struct ScopeState{T <: AbstractExpressionExplorerConfiguration}
mutable struct ScopeState
inglobalscope::Bool
exposedglobals::Set{Symbol}
hiddenglobals::Set{Symbol}
definedfuncs::Set{Symbol}
configuration::T
end
ScopeState(configuration::AbstractExpressionExplorerConfiguration=DefaultConfiguration()) = ScopeState(true, Set{Symbol}(), Set{Symbol}(), Set{Symbol}(), configuration)
ScopeState() = ScopeState(true, Set{Symbol}(), Set{Symbol}(), Set{Symbol}())

# The `union` and `union!` overloads define how two `SymbolsState`s or two `ScopeState`s are combined.

Expand Down Expand Up @@ -443,7 +437,7 @@ function explore_macrocall!(ex::Expr, scopestate::ScopeState)
symstate = SymbolsState(macrocalls = Set{FunctionName}([macro_name]))

for arg in ex.args[begin+1:end]
macro_symstate = explore!(arg, ScopeState(scopestate.configuration))
macro_symstate = explore!(arg, ScopeState())
union!(symstate, SymbolsState(macrocalls = macro_symstate.macrocalls))
end

Expand Down Expand Up @@ -1155,9 +1149,9 @@ compute_symbols_state(ex::Any)::SymbolsState
Return the global references, assignment, function calls and function definitions inside an arbitrary expression, in a `SymbolsState` object.
"""
function compute_symbols_state(ex::Any; configuration::AbstractExpressionExplorerConfiguration=DefaultConfiguration())::SymbolsState
function compute_symbols_state(ex::Any)::SymbolsState
try
compute_symbolreferences(ex; configuration)
compute_symbolreferences(ex)
catch e
if e isa InterruptException
rethrow(e)
Expand All @@ -1168,8 +1162,8 @@ function compute_symbols_state(ex::Any; configuration::AbstractExpressionExplore
end
end

function compute_symbolreferences(ex::Any; configuration::AbstractExpressionExplorerConfiguration=DefaultConfiguration())::SymbolsState
symstate = explore!(ex, ScopeState(configuration))
function compute_symbolreferences(ex::Any)::SymbolsState
symstate = explore!(ex, ScopeState())
handle_recursive_functions!(symstate)
return symstate
end
Expand Down
143 changes: 0 additions & 143 deletions test/Configuration.jl

This file was deleted.

26 changes: 26 additions & 0 deletions test/ExpressionExplorer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -736,3 +736,29 @@ end
]
)
end

@testset "Macros and heuristics w/o Pluto" begin
@test test_expression_explorer(;
expr=:(@macro import Pkg),
macrocalls=[Symbol("@macro")],
definitions=[],
)
@test test_expression_explorer(;
expr=:(@macro Pkg.activate("..")),
macrocalls=[Symbol("@macro")],
references=[],
funccalls=[],
)
@test test_expression_explorer(;
expr=:(@macro Pkg.add("Pluto.jl")),
macrocalls=[Symbol("@macro")],
references=[],
funccalls=[],
)
@test test_expression_explorer(;
expr=:(@macro include("Firebasey.jl")),
macrocalls=[Symbol("@macro")],
funccalls=[],
)
end

Loading

0 comments on commit a43b31c

Please sign in to comment.