#WRT https://github.com/JuliaLang/julia/issues/5571#issuecomment-157608127
Copyright (c) 2015 Lyndon White aka oxinabox aka Frames
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
function terse_lambda(l_args::Vector{Symbol}, new_ff::Expr)
if length(l_args)==0
return new_ff
elseif length(l_args)==1
Expr(:->,
l_args[1],
new_ff
)
else
Expr(:->,
Expr(:tuple,l_args...),
new_ff
)
end
end
terse_lambda (generic function with 1 method)
function terse_treechange(ff::Expr)
#Need to search the tree from left most node, to right
#This is depth first I think (CHKLOGIC)
function rec(path::Vector{Int},gg::Expr)
new_tail = Any[]
l_args = Symbol[]
for (child_ii, hh) in enumerate(gg.args)
if typeof(hh)==Expr
c_path=[path...,child_ii]
new_hh, c_l_args = rec(c_path,hh)
if length(c_l_args)>0
push!(l_args, c_l_args...)
end
push!(new_tail, new_hh)
elseif hh==:_
path_str = join(path,"b")
#There may be a way to use the existing hygine mechnanism's to avoid a nameclash here
#For now I describe them with the path
c_l_arg = Symbol("xx$(path_str)t$(child_ii)")
push!(l_args, c_l_arg)
push!(new_tail, c_l_arg)
else
push!(new_tail, hh)
end
end
new_gg = Expr(gg.head, new_tail...)
(new_gg, l_args)
end
new_ff, ol_args = rec(Int[], ff)
terse_lambda(ol_args, new_ff)
end
terse_treechange (generic function with 1 method)
function terse!(ff)
ff
end
function terse!(ff::Expr)
const prec_other = Set(map(Symbol,["->", "if"]))
const prec_assignment = Set(map(Symbol,["=", ":=", "+=", "-=", "*=", "/=", "//=", ".//=", ".*=", "./=", "|\\=|", "|.\\=|", "^=", ".^=", "÷=", ".÷=", "%=", ".%=", "|\|=|", "&=", "\$=", "=>", "<<=", ">>=", ">>>=", "~", "|.+=|", "|.-=|"]))
const prec_conditional = Set([:?])
const prec_arrow = Set(map(Symbol,["", "'(--", "-->", "←", "→", "↔", "↚", "↛", "↠", "↣", "↦", "↮", "⇎", "⇏", "⇒", "⇔", "⇴", "⇶", "⇷", "⇸", "⇹", "⇺", "⇻", "⇼", "⇽", "⇾", "⇿", "⟵", "⟶", "⟷", "⟷", "⟹", "⟺", "⟻", "⟼", "⟽", "⟾", "⟿", "⤀", "⤁", "⤂", "⤃", "⤄", "⤅", "⤆", "⤇", "⤌", "⤍", "⤎", "⤏", "⤐", "⤑", "⤔", "⤕", "⤖", "⤗", "⤘", "⤝", "⤞", "⤟", "⤠", "⥄", "⥅", "⥆", "⥇", "⥈", "⥊", "⥋", "⥎", "⥐", "⥒", "⥓", "⥖", "⥗", "⥚", "⥛", "⥞", "⥟", "⥢", "⥤", "⥦", "⥧", "⥨", "⥩", "⥪", "⥫", "⥬", "⥭", "⥰", "⧴", "⬱", "⬰", "⬲", "⬳", "⬴", "⬵", "⬶", "⬷", "⬸", "⬹", "⬺", "⬻", "⬼", "⬽", "⬾", "⬿", "⭀", "⭁", "⭂", "⭃", "⭄", "⭇", "⭈", "⭉", "⭊", "⭋", "⭌", "←", "→))"]))
const prec_above = union(prec_other, prec_assignment,prec_conditional, prec_arrow)
function high_prec_node(gg::Expr)
gg.head in prec_above
end
const prec_below = Set([:*,:+,:-,:/,:÷]) #Their many be others
function low_prec_node(gg::Expr)
#Certain nodes should not be transformed inside of, even if they are the top node
gg.head == :call && gg.args[1] in prec_below
end
#######################
rewrite_nodes = Expr[]
function need_rewrite!(gg)
gg==:_ #My parent is a rewrite candiated if I am a blank node
end
function need_rewrite!(gg::Expr, top=false)
at_peak = ((top && !low_prec_node(gg)) #Don't internally change within low_prec_node, even if at top
|| high_prec_node(gg)) #Do internally change with nodes of precences to high to propergate upwards
need_rewrite_at_parent = false
for child in gg.args
child_need_rewrite = need_rewrite!(child)
this_child_demand_granparent_rewrite = child_need_rewrite && ((at_peak && child==:_) || !at_peak)
if this_child_demand_granparent_rewrite
need_rewrite_at_parent=true
break
end
end
if need_rewrite_at_parent
return true
end
#I can be rewritten as I am not demanding my parents be rewritten
for ii in 1:length(gg.args)
child = gg.args[ii]
child_need_rewrite = need_rewrite!(child)
if child_need_rewrite && at_peak
gg.args[ii]=terse_treechange(child)
end
end
return false
end
rewrite_top = need_rewrite!(ff,true)
#println(rewrite_top)
if rewrite_top
terse_treechange(ff)
else
ff
end
end
terse! (generic function with 2 methods)
macro terse(ff)
terse!(ff)
end
#Failing,
#I'm not sure this failure is not infact the prefered behavour
:(_ ? "true" : "false") |> terse! #→ (x -> x) ? "true" : "false")
:(xxt1->if xxt1 "true" else "false" end)
:(_ ? _ : 0) |> terse! #→ (x -> x) ? (y -> y) : 0
:((xxt1,xxt2)->if xxt1 xxt2 else 0 end)
#Failing, Can't Fix
#As (_) is interpretted as the same as _
:(map((_), v)) |> terse! #→ map(x -> x, v)
:(xxt2->map(xxt2,v))
#Won't Fix
#Only standalone _ are broken, iuf they form part of even a simple expression eg `1_` they work
#code only expressions are being rewritten, not symbols
:(_)|>terse! #→ x -> x
:_
:(f = _)|>terse! #→ f = x -> x
:(xxt2->f = xxt2)
#Passing
terse!(:(map(_ + 2, v) ))
:(map((xxt2->xxt2 + 2),v))
terse!(:(_ + 2))
:(xxt2->xxt2 + 2)
terse!(:(f=_+2) )
:(f = (xxt2->xxt2 + 2))
terse!(:(x->_+2) )
:(x->(xx2t2->begin # In[18], line 1: xx2t2 + 2 end))
terse!(:(map(_ + 2, v) ))
:(map((xxt2->xxt2 + 2),v))
terse!(:(f(_,b)))
:(xxt2->f(xxt2,b))
terse!(:(f(a,_)))
:(xxt3->f(a,xxt3))
terse!(:(f(_,_)))
:((xxt2,xxt3)->f(xxt2,xxt3))
terse!(:(2_^2))
:(xx3t2->2 * xx3t2 ^ 2)
terse!(:(2_^_))
:((xx3t2,xx3t3)->2 * xx3t2 ^ xx3t3)
terse!(:(map(2_ + 2, v)))
:(map((xx2t3->2xx2t3 + 2),v))
terse!(:(map(abs,_)))
:(xxt3->map(abs,xxt3))
terse!(:(map(2_ + 2, _)))
:((xx2b2t3,xxt3)->map(2xx2b2t3 + 2,xxt3))
terse!(:(a[_]))
:(xxt2->a[xxt2])
terse!(:(a[_,_]))
:((xxt2,xxt3)->a[xxt2,xxt3])
terse!(:(a[_,1_]))
:((xxt2,xx3t3)->a[xxt2,1xx3t3])
:(f(_,2_)) |> terse!
:((xxt2,xx3t3)->f(xxt2,2xx3t3))
:(map(2_ + 2, _)) |> terse! #→ x -> map(y -> 2y + 2, x)
:((xx2b2t3,xxt3)->map(2xx2b2t3 + 2,xxt3))
:(map(2_ - _, v, w)) |> terse! #→ map((x, y) -> 2x - y, v, w)
:(map(((xx2t3,xxt3)->2xx2t3 - xxt3),v,w))
:(map(2_ - _, v, _)) |> terse! #→ x -> map((y, z) -> 2y - z, v, x)
:((xx2b2t3,xx2t3,xxt4)->map(2xx2b2t3 - xx2t3,v,xxt4))
:(map(2_ - _, _, _)) |> terse! #→ (x, y) -> map((z, w) -> 2z - w, x, y)
:((xx2b2t3,xx2t3,xxt3,xxt4)->map(2xx2b2t3 - xx2t3,xxt3,xxt4))
:(map(_, v))|>terse! #→ x -> map(x, v)
:(xxt2->map(xxt2,v))
:(f = 2_)|>terse! #→ f = x -> 2x
:(f = (xxt3->2xxt3))
:(x -> x^_)|>terse! #→ x -> y -> x^y
:(x->(xx2t3->begin # In[38], line 1: x ^ xx2t3 end))
:(_ && _)|>terse! #→ (x, y) -> x && y
:((xxt1,xxt2)->xxt1 && xxt2)
:(!_ && _)|>terse! #→ (x, y) -> !x && y
:((xx1t2,xxt2)->!xx1t2 && xxt2)
:(2v[_])|> terse! #→ x -> 2v[x] (good)
:(xx3t2->2 * v[xx3t2])
:(2f(_)) |> terse! #→ 2*(x -> f(x)) (not so good)
:(xx3t2->2 * f(xx3t2))