diff --git a/Project.toml b/Project.toml index 8c61b44..6d8c13e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,18 +1,20 @@ name = "GeneralizedGenerated" uuid = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" authors = ["thautwarm"] -version = "0.1.4" +version = "0.2.0" [deps] CanonicalTraits = "a603d957-0e48-4f86-8fbd-0b7bc66df689" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" JuliaVariables = "b14d175d-62b4-44ba-8fb7-3064adc8c3ec" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" -NameResolution = "71a1bf82-56d0-4bbc-8a3c-48b961074391" [compat] +CanonicalTraits = "^0.1" +DataStructures = "^0.17" +JuliaVariables = "^0.2" +MLStyle = "^0.3.1" julia = "1" -JuliaVariables = "0.1.3" [extras] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" diff --git a/README.md b/README.md index 3e936df..5e8e423 100644 --- a/README.md +++ b/README.md @@ -54,28 +54,54 @@ end f(1)(2) # => 3 +@gg function h(x, c) + quote + d = x + 10 + function g(x, y=c) + x + y + d + end + end +end + +h(1, 2)(1) # => 14 ``` -P.S: We can figure out a pure Julia way to resolve symbols, thus free variables and -other stuffs can be resolved automatically. +Note there're some restrictions to the generalized generated functions yet: -Note there're some restrictions to the closures of generated functions yet: +- Multiple dispatch is not allowed, and `f(x) = ...` is equivalent to `f = x -> ...`. This will never gets supported for it needs a thorough implementation of multuple dispatch in GG. +- Comprehensions for generated functions are not implemented yet. It won't cost a long time for being supported. -- Multiple dispatch is not allowed, and `f(x) = ...` is equivalent to `f = x -> ...`. -- Comprehensions for generated functions are not implemented yet. -- Default arguments doesn't work unless they're constants. You can just splice variables into the AST to achieve the same functionlity. The following code works. +The evaluation module can be specified in this way: - ```julia - @gg function f(x) - k = 10 - quote - d = k + 10 - function g(x, y=$k) - x + y + d - end - end - end - ``` +```julia +julia> module S + run(y) = y + 1 + end +Main.S + +julia> @gg m function g(m::Module, y) :(run(y)) end +# the global variable `run` is from the module `m` +g (generic function with 1 method) + +julia> g(S, 1) +2 +``` + +Of course you can use structures to imitate modules: + +```julia +julia> struct S + run :: Function + end +Main.S + +julia> @gg m function g(m::S, y) :(run(y)) end +# the global variable `run` is from the datum `m` +g (generic function with 1 method) + +julia> g(S(x -> x + 1), 1) +2 +``` ## No `eval`/`invokelatest`! diff --git a/src/GeneralizedGenerated.jl b/src/GeneralizedGenerated.jl index 9b9204c..c1a97c1 100644 --- a/src/GeneralizedGenerated.jl +++ b/src/GeneralizedGenerated.jl @@ -1,19 +1,14 @@ module GeneralizedGenerated using MLStyle using JuliaVariables -using NameResolution using CanonicalTraits using DataStructures List = LinkedList -export gg, @gg, top_level_closure_conv, expr2typelevel, interpret -export RuntimeFn, mk_function +export NGG +export gg, @gg, closure_conv, interpret +export RuntimeFn, mk_function, mkngg export to_type, to_typelist, types_to_typelist, from_type, runtime_eval - -include("utils.jl") -include("typeable.jl") -include("runtime_funcs.jl") -include("closure.jl") include("closure_conv.jl") @@ -23,9 +18,11 @@ end function mk_function(mod::Module, ex) ex = macroexpand(mod, ex) - fn = top_level_closure_conv(mod, solve(ex)) + ex = simplify_ex(ex) + ex = solve(ex) + fn = closure_conv(mod, ex) if !(fn isa RuntimeFn) - error("Expect a function expression") + error("Expect an unnamed function expression. ") end fn end @@ -35,7 +32,7 @@ function mk_function(mod::Module, args, kwargs, body) end function mk_function(args, kwargs, body) - mk_function(@__MODULE__, args, kwargs, body) + mk_function(Main, args, kwargs, body) end function runtime_eval(mod::Module, ex) @@ -44,7 +41,7 @@ function runtime_eval(mod::Module, ex) end function runtime_eval(ex) - runtime_eval(@__MODULE__, ex) + runtime_eval(Main, ex) end end # module diff --git a/src/closure_conv.jl b/src/closure_conv.jl index 46a23f1..939fb12 100644 --- a/src/closure_conv.jl +++ b/src/closure_conv.jl @@ -1,180 +1,142 @@ -# This file implements closure conversions. +using JuliaVariables +using MLStyle +include("ngg/ngg.jl") +include("lens.jl") +include("closure.jl") +using .NGG +include("func_arg_decs.jl") + +"""`ex` should be a scoped expression +""" +function closure_conv(top::Any, ex::Any) + function conv(ex::Expr) + @when Expr(:scoped, scope, inner) = ex begin + block = Any[] + for var in scope.bounds + if var.is_mutable && var.is_shared + name = var.name + if var.name in scope.bound_inits + push!(block, :($name = Core.Box($name))) + else + push!(block, :($name = Core.Box())) + end + end + end + push!(block, conv(inner)) + Expr(:block, block...) + @when Expr(:function, head, inner&&Expr(:scoped, scope, _)) = ex + + freenames = Symbol[f.name for f in scope.freevars] + # If the evaluation module is a symbol and not in arguments + if top isa Symbol && all(scope.bounds) do e; e.name !== top end + push!(freenames, top) + end -function top_level_closure_conv(def_module::Module, ex) + head = conv(head) + fh = func_header(head) + lambda_n = Symbol(:function) + name = fh.name === unset ? lambda_n : fh.name + fh = @with fh.args = FuncArg[map(func_arg, freenames)..., fh.args...] - function build_argument(arg) :: Argument - @when Expr(:kw, _...) = arg begin - arg.head = :(=) - end - default = Unset() - @when :($a = $b) = arg begin - default = closure_conv(b) - arg = a - end - type = nothing - @when :($a :: $b) = arg begin - arg = a - type = closure_conv(b) - @when :(:: $b) = arg - arg = :_ - type = closure_conv(b) - end + if fh.fresh !== unset || fh.ret !== unset + error("GG doesn't support type parameters or return type annotations.") + end - @when ::ScopedVar = arg begin - arg = arg.sym + args = of_args(fh.args) + kwargs = of_args(fh.kwargs) + inner = conv(inner) + + fn = mkngg(Symbol(name), args, kwargs, inner) + if !isempty(freenames) + closure_vars = Expr(:tuple, freenames...) + fn = quote + let freevars = $closure_vars + $Closure{$fn, Base.typeof(freevars)}(freevars) + end + end + end - @when ::Symbol = arg - nothing - @otherwise - error("not supported argument name $arg.") + if name !== lambda_n + fn = Expr(:block, :($(fh.name) = $fn)) + end + fn + @when Expr(hd, args...) = ex + Expr(hd, map(conv, args)...) end - - Argument(arg, type, default) end - function build_arguments(args, kwargs) - Arguments(map(build_argument, args), map(build_argument, kwargs)) + function conv(s::Var) + name = s.name + s.is_global && return :($top.$name) + s.is_mutable && s.is_shared && return begin + :($name.contents) + end + name end + conv(s) = s - """ - also canonicalize the arguments - """ - inject_freesyms_as_arg!(freesyms::Vector{Symbol}, call, ref::Ref{Union{Nothing, ScopedVar}}) = - @when :($f($(args...))) = call begin - (args, kwargs) = split_args_kwargs(args) - ref.x = f - build_arguments([freesyms..., args...], kwargs) - - @when Expr(:tuple, args...) = call - (args, kwargs) = split_args_kwargs(args) - build_arguments([freesyms..., args...], kwargs) - - - @when f :: Union{Symbol, ScopedVar} = call - build_arguments([freesyms..., f], []) + conv(ex.args[2]) +end - @when :($f :: $t) = call - error("'annotation' not implemented") - # f = inject_freesyms_as_arg!(freesyms, f, ref) - # :($f :: $t) +function _get_body(::RuntimeFn{Args, Kwargs, Body}) where {Args, Kwargs, Body} + Body +end - @when :($f where {$(ts...)}) = call - error("'where' not implemented") - # f = inject_freesyms_as_arg!(freesyms, f, ref) - # :($f where {$(ts...)}) +function _get_body(ex) + error(ex) +end - @otherwise - error("Malformed function signature $call.") +function gg(compmod::Module, runmod::Any, source::Union{Nothing, LineNumberNode}, ex) + (head, body) = @match ex begin + Expr(:(=), head, body) => (head, body) + Expr(:function, head, body) => (head, body) + Expr(:->, head, body) => (head, body) + _ => error("Malformed generated function at $source.") + end + fh = func_header(head) + locals = Any[] + if fh.args !== unset + for arg in fh.args + push!(locals, arg.name) end - - function process_mutable_cells!(argnames :: Vector{Symbol}, bounds::Vector{LocalVar}, body) - # mutable free variables stored in Core.Box - stmts = Expr[] - for bound in bounds - if bound.is_mutable[] && bound.is_shared[] - sym = bound.sym - if sym in argnames - push!(stmts, :($sym = $Core.Box($sym))) - else - push!(stmts, :($sym = $Core.Box())) - end - end + end + if fh.kwargs !== unset + for arg in fh.kwargs + push!(locals, arg.name) end - Expr(:let, Expr(:block, stmts...), body) end - - function closure_conv(ex::ScopedFunc) - scope = ex.scope - frees = LocalVar[values(scope.freevars)...] - bounds = LocalVar[values(scope.bounds)...] - freesyms = Symbol[x.sym for x in frees] - @when Expr(hd, sig, body) = ex.func begin - check_fun_mut = Ref{Union{Nothing, ScopedVar}}(nothing) - sig = inject_freesyms_as_arg!(freesyms, sig, check_fun_mut) - argnames = Symbol[[arg.name for arg in sig.args];[arg.name for arg in sig.kwargs]] - body = process_mutable_cells!(argnames, bounds, closure_conv(body)) - Args = to_typelist(sig.args) - Kwargs = to_typelist(sig.kwargs) - fn = RuntimeFn{Args, Kwargs, to_type(body)}() - if !isempty(freesyms) - tp = Expr(:tuple, freesyms...) - fn = :(let _free = $tp; $Closure{$fn, $typeof(_free)}(_free) end) - end - if check_fun_mut[] !== nothing - sym = check_fun_mut[] |> closure_conv - fn = :($sym = $fn) - end - fn - @otherwise - error("impossible") + if fh.fresh !== unset + for name in map(extract_tvar, fh.fresh) + push!(locals, name) end end - - function closure_conv(ex::ScopedGenerator) - error("Not implemented") + if fh.name !== unset + push!(locals, fh.name) end - function closure_conv(ex::ScopedVar) - var = ex.scope[ex.sym] - var isa GlobalVar && return :($def_module.$var) - if var.is_shared[] && var.is_mutable[] - return :($(var.sym).contents) - end - return var.sym - end + pseudo_head = Expr(:tuple, locals...) - function closure_conv(ex) - @match ex begin - Expr(hd, args...) => Expr(hd, map(closure_conv, args)...) - a => a + genbody = quote + let ast = Base.macroexpand($compmod, $body), + fake_ast = Base.Expr(:function, $(QuoteNode(pseudo_head)), ast), + fake_ast = $simplify_ex(fake_ast), + fake_ast = $solve(fake_ast), + fake_fn = $closure_conv($(QuoteNode(runmod)), fake_ast) + $from_type($_get_body(fake_fn)) end end - closure_conv(ex) + generator = Expr(:function, head, genbody) + Expr(:macrocall, Symbol("@generated"), source, generator) end -function destruct_rt_fn(::RuntimeFn{Args, Kwargs, Body}) where {Args, Kwargs, Body} - (Args, Kwargs, Body) -end - -function destruct_rt_fn(expr::Expr) - @when :($lhs = $rhs) = expr begin - destruct_rt_fn(rhs) - @otherwise - error("Malformed input $expr") - end -end - -function gg(mod::Module, source::Union{Nothing, LineNumberNode}, ex) - @when Expr(hd, func_sig, body) = ex begin - # a fake function to get all arguments of the generated function - quote_hd = QuoteNode(hd) - quote_sig = QuoteNode(deepcopy(func_sig)) - body = quote - let ast = $macroexpand($mod, $body), -# get all arguments of the generated functions - fake_ast_for_args = $Expr($quote_hd, $quote_sig, $Expr(:block)), - fn_for_args :: $ScopedFunc = $solve(fake_ast_for_args), - args = [keys(fn_for_args.scope.bounds)...], -# generate a fake function and extract out its function body - fake_ast_for_fn_body = $Expr($quote_hd, Expr(:tuple, args...), ast), # to support generator's arguments as closures - fn_for_fn_body :: $ScopedFunc = $solve(fake_ast_for_fn_body), - fn = $top_level_closure_conv($mod, fn_for_fn_body), - (_, _, Body) = $destruct_rt_fn(fn) -# return the real function body - ex = from_type(Body) - ex - end - end - generator = Expr(hd, func_sig, body) - Expr(:macrocall, Symbol("@generated"), source, generator) - @otherwise - error("Malformed generated function at $source.") - end +macro gg(modname, ex) + ex = gg(__module__, modname, __source__, ex) + esc(ex) end macro gg(ex) - ex = gg(__module__, __source__, ex) + ex = gg(__module__, __module__, __source__, ex) esc(ex) -end +end \ No newline at end of file diff --git a/src/func_arg_decs.jl b/src/func_arg_decs.jl new file mode 100644 index 0000000..4a3c349 --- /dev/null +++ b/src/func_arg_decs.jl @@ -0,0 +1,74 @@ +unset = Unset() + +struct FuncArg + name + type + default +end + +struct FuncHeader + name :: Any + args :: Any + kwargs :: Any + ret :: Any + fresh :: Any +end + +FuncHeader() = FuncHeader(unset, unset, unset, unset, unset) + +is_func_header(a::FuncHeader) = a.args != unset + +function func_arg(@nospecialize(ex))::FuncArg + @match ex begin + :($var :: $ty) => @with func_arg(var).type = ty + Expr(:kw, var, default) => @with func_arg(var).default = default + Expr(:(=), var, default) => @with func_arg(var).default = default + var => FuncArg(var, unset, unset) + end +end + +function func_header(@nospecialize(ex))::FuncHeader + @match ex begin + :($hd::$ret) => @with func_header(hd).ret = ret + + :($f($(args...); $(kwargs...))) => + begin inter = @with func_header(f).args = map(func_arg, args) + @with inter.kwargs = map(func_arg, kwargs) + end + :($f($(args...))) => @with func_header(f).args = map(func_arg, args) + + :($f where {$(args...)}) => @with func_header(f).fresh = args + + Expr(:tuple, Expr(:parameters, kwargs...), args...) => + begin inter = @with FuncHeader().args = map(func_arg, args) + @with inter.kwargs = map(func_arg, kwargs) + end + Expr(:tuple, args...) => @with FuncHeader().args = map(func_arg, args) + + f => @with FuncHeader().name = f + end +end + +function of_args(::Unset) + Argument[] +end + +function of_args(args::AbstractArray{FuncArg}) + ret = Argument[] + for (i, each) in enumerate(args) + name = each.name === unset ? gensym("_$i") : each.name + type = each.type === unset ? nothing : each.type + arg = Argument(name, type, each.default) + push!(ret, arg) + end + ret +end + +extract_tvar(var :: Union{Symbol, Expr})::Symbol = + @match var begin + :($a <: $_) => a + :($a >: $_) => a + :($_ >: $a >: $_) => a + :($_ <: $a <: $_) => a + a::Symbol => a + end \ No newline at end of file diff --git a/src/lens.jl b/src/lens.jl new file mode 100644 index 0000000..3819f38 --- /dev/null +++ b/src/lens.jl @@ -0,0 +1,32 @@ +@generated function field_update(main :: T, field::Val{Field}, value) where {T, Field} + fields = fieldnames(T) + quote + $T($([field !== Field ? :(main.$field) : :value for field in fields]...)) + end +end + +function lens_compile(ex, cache, value) + @when :($a.$(b::Symbol).$(c::Symbol) = $d) = ex begin + updated = + Expr(:let, + Expr(:block, :($cache = $cache.$b), :($value = $d)), + :($field_update($cache, $(Val(c)), $value))) + lens_compile(:($a.$b = $updated), cache, value) + @when :($a.$(b::Symbol) = $c) = ex + Expr(:let, + Expr(:block, :($cache = $a), :($value=$c)), + :($field_update($cache, $(Val(b)), $value))) + @otherwise + error("Malformed update notation $ex, expect the form like 'a.b = c'.") + end +end + +function with(ex) + cache = gensym("cache") + value = gensym("value") + lens_compile(ex, cache, value) +end + +macro with(ex) + with(ex) |> esc +end \ No newline at end of file diff --git a/src/ngg/ngg.jl b/src/ngg/ngg.jl new file mode 100644 index 0000000..a1aa576 --- /dev/null +++ b/src/ngg/ngg.jl @@ -0,0 +1,59 @@ +module NGG +export to_type, from_type, show_repr, TypeLevel, TVal, TApp, TCons, TNil +export RuntimeFn, Unset, Argument, mkngg, rmlines +using MLStyle +using CanonicalTraits +using DataStructures +List = LinkedList + +rmlines(ex::Expr) = begin + hd = ex.head + tl = map(rmlines, filter(!islinenumbernode, ex.args)) + Expr(hd, tl...) +end +rmlines(@nospecialize(a)) = a +islinenumbernode(@nospecialize(x)) = x isa LineNumberNode + +include("typeable.jl") +include("runtime_fns.jl") + + +function vectolist(x::Vector{T}) where T + foldr(x, init=nil(T)) do e, last + Cons{T}(e, last) + end +end + +""" +julia> using .NGG +julia> mkngg( + :f, #fname + [ + Argument(:a, nothing, Unset()), + Argument(:b, nothing, Unset()) + ], # args + Argument[], # kwargs + :(a + b) #expression + ) +f = (a, b;) -> a + b + +julia> ans(1, 2) +3 +""" +function mkngg( + name::Symbol, + args::Vector{Argument}, + kwargs::Vector{Argument}, + @nospecialize(ex) +) + arglist = vectolist(args) + Args = to_type(arglist) + + kwarglist = vectolist(kwargs) + Kwargs = to_type(kwarglist) + Ex = to_type(ex) + RuntimeFn{Args, Kwargs, Ex, name}() +end + + +end diff --git a/src/runtime_funcs.jl b/src/ngg/runtime_fns.jl similarity index 85% rename from src/runtime_funcs.jl rename to src/ngg/runtime_fns.jl index 97cd4cd..131896a 100644 --- a/src/runtime_funcs.jl +++ b/src/ngg/runtime_fns.jl @@ -1,31 +1,26 @@ -struct RuntimeFn{Args, Kwargs, Body} end +# A generalized generated function implementation, but not that generalized. + +struct RuntimeFn{Args, Kwargs, Body, Name} end struct Unset end -@implement Typeable{RuntimeFn{Args, Kwargs, Body}} where {Args, Kwargs, Body} +@implement Typeable{RuntimeFn{Args, Kwargs, Body, Name}} where {Args, Kwargs, Body, Name} -Base.show(io::IO, rtfn::RuntimeFn{Args, Kwargs, Body}) where {Args, Kwargs, Body} = begin +Base.show(io::IO, rtfn::RuntimeFn{Args, Kwargs, Body, Name}) where {Args, Kwargs, Body, Name} = begin args = interpret(Args) kwargs = interpret(Kwargs) - args = join(args, ", ") - kwargs = join(kwargs, ", ") + args = join(map(string, args), ", ") + kwargs = join(map(string, kwargs), ", ") body = interpret(Body) |> rmlines - repr = "($args;$kwargs) -> $body" + repr = "$Name = ($args;$kwargs) -> $body" print(io, repr) end - - struct Argument name :: Symbol type :: Union{Nothing, Any} default :: Union{Unset, Any} end -struct Arguments - args :: Vector{Argument} - kwargs :: Vector{Argument} -end - Base.show(io::IO, arg::Argument) = begin print(io, arg.name) if arg.type !== nothing @@ -39,7 +34,7 @@ end @implement Typeable{Unset} @implement Typeable{Argument} begin - to_type(arg) = + to_type(@nospecialize(arg)) = let f = Argument args = [arg.name, arg.type, arg.default] |> to_typelist TApp{Argument, f, args} @@ -113,4 +108,4 @@ end $body end end -end \ No newline at end of file +end diff --git a/src/typeable.jl b/src/ngg/typeable.jl similarity index 88% rename from src/typeable.jl rename to src/ngg/typeable.jl index 4496356..af9e0a1 100644 --- a/src/typeable.jl +++ b/src/ngg/typeable.jl @@ -23,7 +23,7 @@ function interpret(t::Type{TApp{Ret, Fn, Args}}) where {Fn, Args, Ret} end Base.show(io::IO, t::Type{<:TypeLevel}) = show_t(io, t) -show_t(io::IO, t) = begin +show_t(io::IO, @nospecialize(t)) = begin @match t begin ::Type{TypeLevel{L}} where L => print(io, "TypeLevel{", L, "}") ::Type{TypeLevel} => print(io, "TypeLevel") @@ -38,35 +38,32 @@ end @trait Typeable{T} begin to_type :: T => Type{<:TypeLevel{T}} - to_type(x) = TVal{T, x} + to_type(@nospecialize(x)) = TVal{T, x} from_type :: Type{<:TypeLevel{T}} => T - from_type(t) = interpret(t) + from_type(@nospecialize(t)) = interpret(t) show_repr :: [IO, Type{<:TypeLevel{T}}] => Nothing - show_repr(io, t) = begin + show_repr(io::IO, @nospecialize(t)) = begin print(io, from_type(t)) end end -to_typelist(many) = +to_typelist(@nospecialize(many)) = let T = eltype(many) foldr(many, init=TNil{T}) do each, prev TCons{T, to_type(each), prev} end end -types_to_typelist(many) = +types_to_typelist(@nospecialize(many)) = let T = eltype(many) foldr(many, init=TNil{T}) do each, prev TCons{T, each, prev} end end -# compat -expr2typelevel = to_type - @implement Typeable{L} where {T, L <: List{T}} begin - to_type(x) = to_typelist(T[x...]) + to_type(@nospecialize(x)) = to_typelist(T[x...]) end @implement Typeable{Expr} begin @@ -82,7 +79,7 @@ end end @implement Typeable{LineNumberNode} begin - function to_type(ln) + function to_type(@nospecialize(ln)) f = LineNumberNode args = Any[ln.line, ln.file] |> to_typelist TApp{LineNumberNode, f, args} @@ -90,7 +87,7 @@ end end @implement Typeable{QuoteNode} begin - function to_type(x) + function to_type(@nospecialize(x)) f = QuoteNode args = [x.value] |> to_typelist TApp{QuoteNode, f, args} @@ -98,7 +95,7 @@ end end @implement Typeable{Tp} where Tp <: Tuple begin - function to_type(x) + function to_type(@nospecialize(x)) args = collect(x) |> to_typelist TApp{Tp, tuple, args} end @@ -107,7 +104,7 @@ end const named_tuple_maker(p...) = (;p...) @implement Typeable{NamedTuple{Ks, Ts}} where {Ks, Ts} begin - function to_type(x) + function to_type(@nospecialize(x)) f = named_tuple_maker args = [kv for kv in zip(Ks, values(x))] |> to_typelist TApp{NamedTuple{Ks, Ts}, f, args} @@ -165,4 +162,4 @@ end mod, v = from_type(V) GlobalRef(mod, v) end -end \ No newline at end of file +end diff --git a/src/utils.jl b/src/utils.jl deleted file mode 100644 index 57f3b6a..0000000 --- a/src/utils.jl +++ /dev/null @@ -1,16 +0,0 @@ -function split_args_kwargs(args) - @match args begin - [Expr(:parameters, kwargs...), args...] => (args, kwargs) - _ => (args, []) - end -end - -rmlines(ex::Expr) = begin - hd = ex.head - tl = map(rmlines, filter(!islinenumbernode, ex.args)) - Expr(hd, tl...) -end - -rmlines(a) = a - -islinenumbernode(x) = x isa LineNumberNode \ No newline at end of file diff --git a/test/debug.jl b/test/debug.jl new file mode 100644 index 0000000..129f82b --- /dev/null +++ b/test/debug.jl @@ -0,0 +1,25 @@ +begin + begin + g = Core.Box() + begin + #= /home/redbq/Desktop/stable-dev/julia-community/GeneralizedGenerated.jl/src/GeneralizedGenerated.jl:41 =# + begin + #= /home/redbq/Desktop/stable-dev/julia-community/GeneralizedGenerated.jl/test/runtests.jl:168 =# + g.contents = (x, r = 0)->begin + begin + begin + if (Main).:(===)(x, 0) + r + else + g.contents = g.contents + g.contents((Main).:-(x, 1), (Main).:+(r, x)) + end + end + end + end + #= /home/redbq/Desktop/stable-dev/julia-community/GeneralizedGenerated.jl/test/runtests.jl:172 =# + println(g.contents(10)) + end + end + end +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index b49bf53..ae24848 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,20 +5,14 @@ using BenchmarkTools using DataStructures -rmlines(ex::Expr) = begin - hd = ex.head - tl = map(rmlines, filter(!islinenumbernode, ex.args)) - Expr(hd, tl...) -end -rmlines(a) = a -islinenumbernode(x) = x isa LineNumberNode +rmlines = NGG.rmlines @testset "no kwargs" begin @gg function f1(a) quote x -> a + x - end + end |> rmlines end @test f1(1)(2) == 3 @@ -29,7 +23,7 @@ end a += 2 x + a end - end + end |> rmlines end @test f2(1)(2) == 5 @@ -208,6 +202,78 @@ end end end @test bar(2)() == 2 + 20 + + @gg function foobar(x::T, y::A) where {T <: Number, A <: AbstractArray{T}} + quote + g = x + 20 + x = 10 + () -> begin + x = g + (A, x + y[1]) + end + end + end + @test foobar(2, [3])() == (Vector{Int}, 2 + 20 + 3) +end + +@testset "support default arguments" begin + @gg function h(x, c) + quote + d = x + 10 + function g(x, y=c) + x + y + d + end + end + end + @test h(1, 2)(3) == 16 +end + +module S + run(y) = y + 1 +end + +struct K + f1::Function + f2::Function +end +@testset "specifying evaluation modules" begin + @gg m function g(m::Module, y) :(run(y)) end + @test g(S, 1) == 2 + + @gg m function h(m, y) + quote + c = m.f1(y) + () -> begin c = m.f2(c) end + end + end + k = K(x -> x + 1, x -> x * 9) + next = h(k, 1) + @test next() == 18 + @test next() == 18 * 9 +end + +@testset "test free variables of let bindings" begin + @gg function test_free_of_let() + quote + let x = 1 + f = () -> begin + x * 3 + end + x = 2 + f + end + end + end + @test test_free_of_let()() == 6 +end + +@testset "show something" begin + f1 = mk_function(:(x -> x + 1)) + f2 = mk_function(:((x :: Int = 2, ) -> x + 1)) + @test f1(1) == 2 + @test f2() == 3 + println(f1) + println(f2) end # # From Chris Rackauckas: https://github.com/JuliaLang/julia/pull/32737