From 9ef17207d5f99c7a0019cbbe0e58f77e7c4c1d21 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sun, 15 Feb 2015 16:28:40 -0500 Subject: [PATCH] fix bug in nested quote and nested $ with splatting In general, the outermost quote evaluates the innermost $: ``` julia> y = 2 2 julia> x = :y :y julia> :(:(f($$x))) :($(Expr(:quote, :(f($(Expr(:$, :y))))))) julia> eval(ans) :(f(2)) julia> :(:(f($x))) :($(Expr(:quote, :(f($(Expr(:$, :x))))))) julia> eval(ans) :(f(y)) ``` I triple-checked that this is how lisp works. For splatting, you generally want to put extra `$`s inside the `...`. E.g. `:(:(f($(($x)...))))` will cause the elements of `x` to be splatted after two evaluations: ``` julia> x = [:a,:b]; julia> :(:(f($(($x)...)))) :($(Expr(:quote, :(f($(Expr(:$, :(([:a,:b]...,))))))))) julia> eval(ans) :(f(a,b)) ``` This commit also fixes printing expressions with nested quote. The problem is that printing works by surrounding an expression with `:( )` and using `$` inside for Exprs that don't have surface syntax. However, if the expression contains quotes, those quotes "capture" any `$` expressions inside, preventing them from being evaluated by the printer's outermost `:( )`. For now I fixed this by reserving `quote`, `:( )`, and `$` only for the outermost expression, and using `Expr` for inner quotes. This works but leads to really ugly output. I believe the only way to improve it is to introduce surface syntax for Exprs with arbitrary head symbols, and for non-interpolating quote. --- base/show.jl | 27 +++++++++++++++++++-------- src/julia-syntax.scm | 5 +++-- test/core.jl | 5 ++++- test/show.jl | 6 ++++++ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/base/show.jl b/base/show.jl index 5b78f1f0e986d..5488c9d3fb3ae 100644 --- a/base/show.jl +++ b/base/show.jl @@ -199,7 +199,7 @@ end show_comma_array(io::IO, itr, o, c) = show_delim_array(io, itr, o, ',', c, false) show(io::IO, t::Tuple) = show_delim_array(io, t, '(', ',', ')', true) -show(io::IO, s::Symbol) = show_unquoted(io, QuoteNode(s)) +show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0) ## Abstract Syntax Tree (AST) printing ## @@ -228,7 +228,7 @@ show(io::IO, s::Symbol) = show_unquoted(io, QuoteNode(s)) typealias ExprNode Union(Expr, QuoteNode, SymbolNode, LineNumberNode, LabelNode, GotoNode, TopNode) print (io::IO, ex::ExprNode) = (show_unquoted(io, ex); nothing) -show (io::IO, ex::ExprNode) = show_unquoted(io, QuoteNode(ex)) +show (io::IO, ex::ExprNode) = show_unquoted_quote_expr(io, ex, 0, 0) show_unquoted(io::IO, ex) = show_unquoted(io, ex, 0, 0) show_unquoted(io::IO, ex, indent::Int) = show_unquoted(io, ex, indent, 0) show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex) @@ -385,8 +385,15 @@ function show_unquoted(io::IO, ex::SymbolNode, ::Int, ::Int) show_expr_type(io, ex.typ) end -show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int) = - show_unquoted_quote_expr(io, ex.value, indent, prec) +function show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int) + if isa(ex.value, Symbol) + show_unquoted_quote_expr(io, ex.value, indent, prec) + else + print(io, "\$(QuoteNode(") + show(io, ex.value) + print(io, "))") + end +end function show_unquoted_quote_expr(io::IO, value, indent::Int, prec::Int) if isa(value, Symbol) && !(value in quoted_syms) @@ -611,7 +618,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) elseif is(head, :block) || is(head, :body) show_block(io, "begin", ex, indent); print(io, "end") - elseif is(head, :quote) && nargs == 1 + elseif is(head, :quote) && nargs == 1 && isa(args[1],Symbol) show_unquoted_quote_expr(io, args[1], indent, 0) elseif is(head, :gotoifnot) && nargs == 2 @@ -643,9 +650,13 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) end print(io, '"', a..., '"') - elseif is(head, :&) && length(args) == 1 - print(io, '&') - show_unquoted(io, args[1]) + elseif (is(head, :&)#= || is(head, :$)=#) && length(args) == 1 + print(io, head) + a1 = args[1] + parens = (isa(a1,Expr) && a1.head !== :tuple) || (isa(a1,Symbol) && isoperator(a1)) + parens && print(io, "(") + show_unquoted(io, a1) + parens && print(io, ")") # transpose elseif (head === symbol('\'') || head === symbol(".'")) && length(args) == 1 diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 06249b1c8b7c0..573f63b05e4fc 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -3278,11 +3278,12 @@ So far only the second case can actually occur. (if (and (= d 0) (length= x 2)) (cadr x) (if (splice-expr? (cadr x)) - (wrap-with-splice (julia-bq-expand (cadr (cadr (cadr (cadr x)))) (- d 1))) + `(call (top splicedexpr) (inert $) + (call (top append_any) ,(julia-bq-bracket (cadr x) (- d 1)))) `(call (top _expr) (inert $) ,(julia-bq-expand (cadr x) (- d 1)))))) ((not (contains (lambda (e) (and (pair? e) (eq? (car e) '$))) x)) `(copyast (inert ,x))) - ((or (> d 0) (not (any splice-expr? x))) + ((not (any splice-expr? x)) `(call (top _expr) ,.(map (lambda (ex) (julia-bq-expand ex d)) x))) (else (let loop ((p (cdr x)) (q '())) diff --git a/test/core.jl b/test/core.jl index 8c10e4f76d75c..80dddd9c435d1 100644 --- a/test/core.jl +++ b/test/core.jl @@ -2164,4 +2164,7 @@ f8631{T}(::Type{(T...)}, x::Tuple) = 2 # issue caused by 8d0037cb377257fc4232c8526b12337dd7bdf0a7 args8d003 = (:x, :y) -@test eval(:(:(f($($(args8d003...)))))) == :(f(x,y)) +@test eval(:(:(f($(($args8d003)...))))) == :(f(x,y)) +x8d003 = Any[:y8d003] +y8d003 = 777 +@test eval(:(string(:(f($($(x8d003...))))))) == "f(777)" diff --git a/test/show.jl b/test/show.jl index 1d29d85ba8566..6c27af2586879 100644 --- a/test/show.jl +++ b/test/show.jl @@ -212,3 +212,9 @@ let q1 = parse(repr(:("$(a)b"))), @test q2.args[1].head == :string @test q2.args[1].args == [:ab,] end + +x8d003 = 2 +let a = Expr(:quote,Expr(:$,:x8d003)) + @test eval(parse(repr(a))) == a + @test eval(eval(parse(repr(a)))) == 2 +end