Skip to content

Commit ed04c29

Browse files
committed
Playing with currying, chaining and underscores
A super hacky, quick implementation of some ideas from https://discourse.julialang.org/t/fixing-the-piping-chaining-issue Parse chains of `/>` and `\>` at the rough same precedence as `|>`, but treat them as a "frontfix/backfix operator" for function calls such that the succeeding function call becomes curried in first or last argument. Thus, the following x /> f(y) \> g(z) is parsed as (chain x (/> f y) (\> g z)) and lowered to the equivalent of chain(x, a->f(a, y), b->g(z, b)) Also add lowering of underscore as strictly tight-binding placeholder syntax. (Super hacky - more forms should be allowed! This is just for experimentation).
1 parent 6a12fff commit ed04c29

File tree

4 files changed

+71
-2
lines changed

4 files changed

+71
-2
lines changed

src/expr.jl

+41-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,22 @@ function is_stringchunk(node)
1111
return k == K"String" || k == K"CmdString"
1212
end
1313

14+
function lower_underscores(args, skiparg=-1)
15+
g = nothing
16+
for i in 1:length(args)
17+
if i == skiparg
18+
continue
19+
end
20+
if args[i] == :_
21+
if isnothing(g)
22+
g = gensym()
23+
end
24+
args[i] = g
25+
end
26+
end
27+
return g
28+
end
29+
1430
function reorder_parameters!(args, params_pos)
1531
p = 0
1632
for i = length(args):-1:1
@@ -125,7 +141,7 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
125141
args[2] = _to_expr(node_args[2])
126142
else
127143
eq_to_kw_in_call =
128-
headsym == :call && is_prefix_call(node) ||
144+
(headsym == :call || headsym == Symbol("/>")) && is_prefix_call(node) ||
129145
headsym == :ref
130146
eq_to_kw_all = headsym == :parameters && !inside_vect_or_braces ||
131147
(headsym == :tuple && inside_dot_expr)
@@ -168,7 +184,16 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
168184
headsym = Symbol("'")
169185
end
170186
# Move parameters blocks to args[2]
187+
g = lower_underscores(args, 1)
171188
reorder_parameters!(args, 2)
189+
if !isnothing(g)
190+
return Expr(:->, g, Expr(:call, args...))
191+
end
192+
elseif headsym == :.
193+
g = lower_underscores(args)
194+
if !isnothing(g)
195+
return Expr(:->, g, Expr(:., args...))
196+
end
172197
elseif headsym in (:tuple, :vect, :braces)
173198
# Move parameters blocks to args[1]
174199
reorder_parameters!(args, 1)
@@ -278,10 +303,25 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
278303
args[1] = Expr(headsym, args[1].args...)
279304
headsym = :const
280305
end
306+
elseif headsym == Symbol("/>")
307+
freearg = gensym()
308+
cargs = [args[1], freearg, args[2:end]...]
309+
reorder_parameters!(cargs, 2)
310+
return Expr(:->, freearg, Expr(:call, cargs...))
311+
elseif headsym == Symbol("\\>")
312+
freearg = gensym()
313+
cargs = [args[1], args[2:end]..., freearg]
314+
reorder_parameters!(cargs, 2)
315+
return Expr(:->, freearg, Expr(:call, cargs...))
316+
elseif headsym == :chain
317+
return Expr(:call, :(JuliaSyntax.chain), args...)
281318
end
282319
return Expr(headsym, args...)
283320
end
284321

322+
chain(x, f, fs...) = chain(f(x), fs...)
323+
chain(x) = x
324+
285325
Base.Expr(node::SyntaxNode) = _to_expr(node)
286326

287327
function build_tree(::Type{Expr}, stream::ParseStream; kws...)

src/kinds.jl

+3
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,8 @@ const _kind_names =
813813
"'"
814814
".'"
815815
"->"
816+
"/>"
817+
"\\>"
816818

817819
"BEGIN_UNICODE_OPS"
818820
"¬"
@@ -867,6 +869,7 @@ const _kind_names =
867869
"BEGIN_SYNTAX_KINDS"
868870
"block"
869871
"call"
872+
"chain"
870873
"comparison"
871874
"curly"
872875
"inert" # QuoteNode; not quasiquote

src/parser.jl

+23-1
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,29 @@ end
793793
# x |> y |> z ==> (call-i (call-i x |> y) |> z)
794794
# flisp: parse-pipe>
795795
function parse_pipe_gt(ps::ParseState)
796-
parse_LtoR(ps, parse_range, is_prec_pipe_gt)
796+
parse_LtoR(ps, parse_curry_chain, is_prec_pipe_gt)
797+
end
798+
799+
# x /> f(y) /> g(z) ==> (chain x (/> f y) (/> g z))
800+
# x /> A.f(y) ==> (chain x (/> (. A (quote f)) y))
801+
function parse_curry_chain(ps::ParseState)
802+
mark = position(ps)
803+
parse_range(ps)
804+
has_chain = false
805+
while (k = peek(ps); k == K"/>" || k == K"\>")
806+
bump(ps, TRIVIA_FLAG)
807+
m = position(ps)
808+
parse_range(ps)
809+
if peek_behind(ps).kind == K"call"
810+
has_chain = true
811+
reset_node!(ps, position(ps), kind=k)
812+
else
813+
emit(ps, m, K"error", error="Expected call to the right of />")
814+
end
815+
end
816+
if has_chain
817+
emit(ps, mark, K"chain")
818+
end
797819
end
798820

799821
# parse ranges and postfix ...

src/tokenize.jl

+4
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,8 @@ function lex_forwardslash(l::Lexer)
937937
end
938938
elseif accept(l, '=')
939939
return emit(l, K"/=")
940+
elseif accept(l, '>')
941+
return emit(l, K"/>")
940942
else
941943
return emit(l, K"/")
942944
end
@@ -945,6 +947,8 @@ end
945947
function lex_backslash(l::Lexer)
946948
if accept(l, '=')
947949
return emit(l, K"\=")
950+
elseif accept(l, '>')
951+
return emit(l, K"\>")
948952
end
949953
return emit(l, K"\\")
950954
end

0 commit comments

Comments
 (0)