Skip to content

Commit

Permalink
correct unparsing of Unicode binary operator expressions, getting pre…
Browse files Browse the repository at this point in the history
…cedences directly from julia-parser.scm rather than repeating them in show.jl
  • Loading branch information
stevengj committed Aug 9, 2014
1 parent fda850b commit 6ac206d
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 28 deletions.
41 changes: 13 additions & 28 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -240,26 +240,7 @@ show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex)

const indent_width = 4
const quoted_syms = Set{Symbol}([:(:),:(::),:(:=),:(=),:(==),:(===),:(=>)])
const uni_ops = Set{Symbol}([:(+), :(-), :(!), :(~), :(<:), :(>:)])
const bin_ops_by_prec = [
"= := += -= *= /= //= .//= .*= ./= \\= .\\= ^= .^= %= .%= |= &= \$= => <<= >>= >>>= ~ .+= .-=",
"?",
"||",
"&&",
"-- -->",
"> < >= <= == === != !== .> .< .>= .<= .== .!= .= .! <: >:",
"|> <|",
": ..",
"+ - .+ .- | \$",
"<< >> >>> .<< .>> .>>>",
"* / ./ % .% & .* \\ .\\",
"// .//",
"^ .^",
"::",
"."
]
const bin_op_precs = Dict{Symbol,Int}(merge([{symbol(op)=>i for op=split(bin_ops_by_prec[i])} for i=1:length(bin_ops_by_prec)]...))
const bin_ops = Set{Symbol}(keys(bin_op_precs))
const uni_ops = Set{Symbol}([:(+), :(-), :(!), :(¬), :(~), :(<:), :(>:), :(), :(), :()])
const expr_infix_wide = Set([:(=), :(+=), :(-=), :(*=), :(/=), :(\=), :(&=),
:(|=), :($=), :(>>>=), :(>>=), :(<<=), :(&&), :(||)])
const expr_infix = Set([:(:), :(<:), :(->), :(=>), symbol("::")])
Expand All @@ -282,8 +263,12 @@ function isidentifier(s::String)
end
return true
end
isoperator(s::ByteString) = ccall(:jl_is_operator, Cint, (Ptr{Uint8},), s) != 0
isoperator(s::String) = isoperator(bytestring(s))

isoperator(s::Symbol) = ccall(:jl_is_operator, Cint, (Ptr{Uint8},), s) != 0
operator_precedence(s::Symbol) = int(ccall(:jl_operator_precedence,
Cint, (Ptr{Uint8},), s))
operator_precedence(x::Any) = 0 # fallback for generic expression nodes
const prec_power = operator_precedence(:(^))

is_expr(ex, head::Symbol) = (isa(ex, Expr) && (ex.head == head))
is_expr(ex, head::Symbol, n::Int) = is_expr(ex, head) && length(ex.args) == n
Expand Down Expand Up @@ -375,7 +360,7 @@ show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int) =
function show_unquoted_quote_expr(io::IO, value, indent::Int, prec::Int)
if isa(value, Symbol) && !(value in quoted_syms)
s = string(value)
if (isidentifier(s) || isoperator(s)) && s != "end"
if (isidentifier(s) || isoperator(value)) && s != "end"
print(io, ":")
print(io, value)
else
Expand Down Expand Up @@ -413,7 +398,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int)
elseif (head in expr_infix && nargs==2) || (is(head,:(:)) && nargs==3)
show_list(io, args, head, indent)
elseif head in expr_infix_wide && nargs == 2
func_prec = get(bin_op_precs, head, 0)
func_prec = operator_precedence(head)
if func_prec < prec
show_enclosed_list(io, '(', args, " $head ", ')', indent, func_prec)
else
Expand All @@ -438,7 +423,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int)
# function call
elseif haskey(expr_calls, head) && nargs >= 1 # :call/:ref/:curly
func = args[1]
func_prec = get(bin_op_precs, func, 0)
func_prec = operator_precedence(func)
func_args = args[2:end]

# scalar multiplication (i.e. "100x")
Expand All @@ -459,9 +444,9 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int)
end

# binary operator (i.e. "x + y")
elseif func in bin_ops
elseif func_prec > 0 # is a binary operator
if length(func_args) > 1
sep = func_prec >= bin_op_precs[:(^)] ? "$func" : " $func "
sep = func_prec >= prec_power ? "$func" : " $func "
if func_prec <= prec
show_enclosed_list(io, '(', func_args, sep, ')', indent, func_prec)
else
Expand Down Expand Up @@ -494,7 +479,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int)

# comparison (i.e. "x < y < z")
elseif is(head, :comparison) && nargs >= 3 && (nargs&1==1)
comp_prec = minimum([get(bin_op_precs, comp, 0) for comp=args[2:2:end]])
comp_prec = minimum(operator_precedence, args[2:2:end])
if comp_prec <= prec
show_enclosed_list(io, '(', args, " ", ')', indent, comp_prec)
else
Expand Down
5 changes: 5 additions & 0 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,11 @@ DLLEXPORT int jl_is_operator(char *sym) {
== FL_T;
}

DLLEXPORT int jl_operator_precedence(char *sym) {
return numval(fl_applyn(1, symbol_value(symbol("operator-precedence")),
symbol(sym)));
}

#ifdef __cplusplus
}
#endif
11 changes: 11 additions & 0 deletions src/julia-parser.scm
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@
(eval `(define ,(symbol (string "is-" name "?")) (Set ,name))))
prec-names)

;; hash table of binary operators -> precedence
(define prec-table (let ((t (table)))
(define (pushprec L prec)
(if (not (null? L))
(begin
(for-each (lambda (x) (put! t x prec)) (car L))
(pushprec (cdr L) (+ prec 1)))))
(pushprec (map eval prec-names) 1)
t))
(define (operator-precedence op) (get prec-table op 0))

(define unary-ops '(+ - ! ¬ ~ |<:| |>:| √ ∛ ∜))

; operators that are both unary and binary
Expand Down
1 change: 1 addition & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,7 @@ DLLEXPORT jl_value_t *jl_compress_ast(jl_lambda_info_t *li, jl_value_t *ast);
DLLEXPORT jl_value_t *jl_uncompress_ast(jl_lambda_info_t *li, jl_value_t *data);

DLLEXPORT int jl_is_operator(char *sym);
DLLEXPORT int jl_operator_precedence(char *sym);

STATIC_INLINE int jl_vinfo_capt(jl_array_t *vi)
{
Expand Down
4 changes: 4 additions & 0 deletions test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,7 @@ end"""
@test_repr "foo.bar[1]"
@test_repr "foo.bar()"
@test_repr "(foo + bar)()"

# unicode operator printing
@test sprint(show, :(1 (2 3))) == ":(1 ⊕ 2 ⊗ 3)"
@test sprint(show, :((1 2) 3)) == ":((1 ⊕ 2) ⊗ 3)"

0 comments on commit 6ac206d

Please sign in to comment.