Skip to content

Commit 87df76e

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 764597a commit 87df76e

File tree

4 files changed

+71
-2
lines changed

4 files changed

+71
-2
lines changed

src/expr.jl

Lines changed: 41 additions & 1 deletion
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
@@ -131,7 +147,7 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
131147
args[2] = _to_expr(node_args[2])
132148
else
133149
eq_to_kw_in_call =
134-
((headsym == :call || headsym == :dotcall) && is_prefix_call(node)) ||
150+
((headsym == :call || headsym == :dotcall || headsym == Symbol("/>")) && is_prefix_call(node)) ||
135151
headsym == :ref
136152
eq_to_kw_all = headsym == :parameters && !map_kw_in_params
137153
in_vcbr = headsym == :vect || headsym == :curly || headsym == :braces || headsym == :ref
@@ -169,6 +185,7 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
169185
headsym = Symbol("'")
170186
end
171187
# Move parameters blocks to args[2]
188+
g = lower_underscores(args, 1)
172189
reorder_parameters!(args, 2)
173190
if headsym === :dotcall
174191
if is_prefix_call(node)
@@ -179,9 +196,17 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
179196
args[1] = Symbol(".", args[1])
180197
end
181198
end
199+
if !isnothing(g)
200+
return Expr(:->, g, Expr(:call, args...))
201+
end
182202
elseif headsym in (:ref, :curly)
183203
# Move parameters blocks to args[2]
184204
reorder_parameters!(args, 2)
205+
elseif headsym == :.
206+
g = lower_underscores(args)
207+
if !isnothing(g)
208+
return Expr(:->, g, Expr(:., args...))
209+
end
185210
elseif headsym in (:tuple, :vect, :braces)
186211
# Move parameters blocks to args[1]
187212
reorder_parameters!(args, 1)
@@ -299,10 +324,25 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
299324
args[1] = Expr(headsym, args[1].args...)
300325
headsym = :const
301326
end
327+
elseif headsym == Symbol("/>")
328+
freearg = gensym()
329+
cargs = [args[1], freearg, args[2:end]...]
330+
reorder_parameters!(cargs, 2)
331+
return Expr(:->, freearg, Expr(:call, cargs...))
332+
elseif headsym == Symbol("\\>")
333+
freearg = gensym()
334+
cargs = [args[1], args[2:end]..., freearg]
335+
reorder_parameters!(cargs, 2)
336+
return Expr(:->, freearg, Expr(:call, cargs...))
337+
elseif headsym == :chain
338+
return Expr(:call, :(JuliaSyntax.chain), args...)
302339
end
303340
return Expr(headsym, args...)
304341
end
305342

343+
chain(x, f, fs...) = chain(f(x), fs...)
344+
chain(x) = x
345+
306346
Base.Expr(node::SyntaxNode) = _to_expr(node)
307347

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

src/kinds.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,8 @@ const _kind_names =
823823
"'"
824824
".'"
825825
"->"
826+
"/>"
827+
"\\>"
826828

827829
"BEGIN_UNICODE_OPS"
828830
"¬"
@@ -878,6 +880,7 @@ const _kind_names =
878880
"block"
879881
"call"
880882
"dotcall"
883+
"chain"
881884
"comparison"
882885
"curly"
883886
"inert" # QuoteNode; not quasiquote

src/parser.jl

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,29 @@ end
820820
# x .|> y ==> (dotcall-i x |> y)
821821
# flisp: parse-pipe>
822822
function parse_pipe_gt(ps::ParseState)
823-
parse_LtoR(ps, parse_range, is_prec_pipe_gt)
823+
parse_LtoR(ps, parse_curry_chain, is_prec_pipe_gt)
824+
end
825+
826+
# x /> f(y) /> g(z) ==> (chain x (/> f y) (/> g z))
827+
# x /> A.f(y) ==> (chain x (/> (. A (quote f)) y))
828+
function parse_curry_chain(ps::ParseState)
829+
mark = position(ps)
830+
parse_range(ps)
831+
has_chain = false
832+
while (k = peek(ps); k == K"/>" || k == K"\>")
833+
bump(ps, TRIVIA_FLAG)
834+
m = position(ps)
835+
parse_range(ps)
836+
if peek_behind(ps).kind == K"call"
837+
has_chain = true
838+
reset_node!(ps, position(ps), kind=k)
839+
else
840+
emit(ps, m, K"error", error="Expected call to the right of />")
841+
end
842+
end
843+
if has_chain
844+
emit(ps, mark, K"chain")
845+
end
824846
end
825847

826848
# parse ranges and postfix ...

src/tokenize.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,8 @@ function lex_forwardslash(l::Lexer)
932932
end
933933
elseif accept(l, '=')
934934
return emit(l, K"/=")
935+
elseif accept(l, '>')
936+
return emit(l, K"/>")
935937
else
936938
return emit(l, K"/")
937939
end
@@ -940,6 +942,8 @@ end
940942
function lex_backslash(l::Lexer)
941943
if accept(l, '=')
942944
return emit(l, K"\=")
945+
elseif accept(l, '>')
946+
return emit(l, K"\>")
943947
end
944948
return emit(l, K"\\")
945949
end

0 commit comments

Comments
 (0)