diff --git a/src/JuliaLowering.jl b/src/JuliaLowering.jl index b24c5687..4d37b90f 100644 --- a/src/JuliaLowering.jl +++ b/src/JuliaLowering.jl @@ -33,6 +33,7 @@ _include("syntax_macros.jl") _include("eval.jl") _include("compat.jl") +_include("hooks.jl") function __init__() _register_kinds() diff --git a/src/hooks.jl b/src/hooks.jl new file mode 100644 index 00000000..c150fce9 --- /dev/null +++ b/src/hooks.jl @@ -0,0 +1,51 @@ +""" +Becomes `Core._lower()` upon activating JuliaLowering. + +Returns an svec with the lowered code (usually expr) as its first element, and +(until integration is less experimental) whatever we want after it +""" +function core_lowering_hook(@nospecialize(code), mod::Module, + file="none", line=0, world=typemax(Csize_t), warn=false) + if !(code isa SyntaxTree || code isa Expr) + # e.g. LineNumberNode, integer... + return Core.svec(code) + end + + # TODO: fix in base + file = file isa Ptr{UInt8} ? unsafe_string(file) : file + line = !(line isa Int64) ? Int64(line) : line + + st0 = code isa Expr ? expr_to_syntaxtree(code, LineNumberNode(line, file)) : code + try + ctx1, st1 = expand_forms_1( mod, st0) + ctx2, st2 = expand_forms_2( ctx1, st1) + ctx3, st3 = resolve_scopes( ctx2, st2) + ctx4, st4 = convert_closures(ctx3, st3) + ctx5, st5 = linearize_ir( ctx4, st4) + ex = to_lowered_expr(mod, st5) + return Core.svec(ex, st5, ctx5) + catch exc + @error("JuliaLowering failed — falling back to flisp!", + exception=(exc,catch_backtrace()), + code=code, file=file, line=line, mod=mod) + return Base.fl_lower(code, mod, file, line, world, warn) + end +end + +# TODO: Write a parser hook here. The input to `core_lowering_hook` should +# eventually be a (convertible to) SyntaxTree, but we need to make updates to +# the parsing API to include a parameter for AST type. + +const _has_v1_13_hooks = isdefined(Core, :_lower) + +function activate!(enable=true) + if !_has_v1_13_hooks + error("Cannot use JuliaLowering without `Core._lower` binding or in $VERSION < 1.13") + end + + if enable + Core._setlowerer!(core_lowering_hook) + else + Core._setlowerer!(Base.fl_lower) + end +end diff --git a/test/hooks.jl b/test/hooks.jl new file mode 100644 index 00000000..39a3d883 --- /dev/null +++ b/test/hooks.jl @@ -0,0 +1,38 @@ +const JL = JuliaLowering + +@testset "hooks" begin + test_mod = Module() + + @testset "`core_lowering_hook`" begin + # Non-AST types are often sent through lowering + stuff = Any[LineNumberNode(1), 123, 123.123, true, "foo", test_mod] + for s in stuff + @test JL.core_lowering_hook(s, test_mod) == Core.svec(s) + end + + for ast_type in (Expr, JL.SyntaxTree) + ex = parsestmt(ast_type, "[1,2,3] .+= 1") + out = JL.core_lowering_hook(ex, test_mod) + @test out isa Core.SimpleVector && out[1] isa Expr + val = Core.eval(test_mod, out[1]) + @test val == [2,3,4] + end + end + + @testset "integration: `JuliaLowering.activate!`" begin + prog = parseall(Expr, "global asdf = 1") + JL.activate!() + out = Core.eval(test_mod, prog) + JL.activate!(false) + @test out === 1 + @test isdefined(test_mod, :asdf) + + prog = parseall(Expr, "module M; x = 1; end") + JL.activate!() + out = Core.eval(test_mod, prog) + JL.activate!(false) + @test out isa Module + @test isdefined(test_mod, :M) + @test isdefined(test_mod.M, :x) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index dc24047f..5225f7da 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -26,4 +26,5 @@ include("utils.jl") include("scopes.jl") include("typedefs.jl") include("compat.jl") + include("hooks.jl") end