Skip to content

Instantly share code, notes, and snippets.

Last active August 21, 2018 19:52
Show Gist options
  • Save stevengj/255cb778efcc72a84dbf97ecbbf221fe to your computer and use it in GitHub Desktop.
Save stevengj/255cb778efcc72a84dbf97ecbbf221fe to your computer and use it in GitHub Desktop.
draft julia function to make ASTs use soft global scope
using Base.Meta: isexpr
const assignments = Set((:(=), :(+=), :(-=), :(*=), :(/=), :(//=), :(\=), :(^=), :(÷=), :(%=), :(<<=), :(>>=), :(>>>=), :(|=), :(&=), :(⊻=), :($=)))
localvar(ex::Expr) = isexpr(ex, :(=)) ? ex.args[1] : nothing
localvar(ex) = nothing
function soft_globals(ex::Expr, globals, insertglobal=false)
if isexpr(ex, :for) || isexpr(ex, :while)
return Expr(ex.head, ex.args[1], soft_globals(ex.args[2], globals, true))
elseif isexpr(ex, :try)
try_clause = soft_globals(ex.args[1], globals, true)
catch_clause = soft_globals(ex.args[1], ex.args[2] isa Symbol ? setdiff(globals, ex.args[2:2]) : globals, true)
finally_clause = soft_globals(ex.args[4], globals, true)
return Expr(:try, try_clause, ex.args[2], catch_clause, finally_clause)
elseif isexpr(ex, :let)
letglobals = setdiff(globals, isexpr(ex.args[1], :(=)) ? [ex.args[1].args[1]] : [localvar(ex) for ex in ex.args[1].args])
return Expr(ex.head, soft_globals(ex.args[1], globals, insertglobal),
soft_globals(ex.args[2], letglobals, true))
elseif isexpr(ex, :block) || isexpr(ex, :if)
return Expr(ex.head, soft_globals.(ex.args, Ref(globals), insertglobal)...)
elseif insertglobal && ex.head in assignments && ex.args[1] in globals
return Expr(:global, Expr(ex.head, ex.args[1], soft_globals(ex.args[2], globals, insertglobal)))
return ex
soft_globals(ex, globals, insertglobal=false) = ex
globalize(m::Module, ast) = soft_globals(ast, Set(names(m)), false)
function globalize_include_string(m::Module, code::AbstractString, filename::AbstractString="string")
# use the undocumented parse_input_line function so that we preserve
# the filename and line-number information.
expr = Base.parse_input_line("begin; "*code*"\nend\n", filename=filename)
retval = nothing
# expr.args should consist of LineNumberNodes followed by expressions to evaluate
for i = 2:2:length(expr.args)
retval = Core.eval(m, globalize(m, Expr(:block, expr.args[i-1:i]...)))
return retval
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment