Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: More printing overhaul #169

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Intervals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@ export Bound,
less_than_disjoint,
greater_than_disjoint,
superset,
.., ≪, ≫, ⊆, ⊇, ⊈, ⊉
.., ≪, ≫, ⊆, ⊇, ⊈, ⊉,
@i_cmd, @interval_cmd
end
104 changes: 86 additions & 18 deletions src/interval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -258,35 +258,103 @@ function Base.convert(::Type{T}, interval::Interval{T}) where T
end
end

##### Interval Macro #####

macro interval_cmd(str)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason you used *_cmd over *_str?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be using parse(Interval, ...) under the hood and unify the logic

Would probably need to expand the existing functionality with:

function Base.parse(::Type{Interval}, str::AbstractString; element_parser=(T, el) -> eval(Meta.parse(el))::T)
    x = invoke(parse, Tuple{Type{Interval{Any}}, AbstractString}, Interval{Any}, str; element_parser)
    T = promote_type(typeof(x.first), typeof(x.last))
    return convert(Interval{T}, x)
end

function Base.convert(::Type{Interval{T}}, interval::Interval) where T
    L, R = bounds_types(interval)
    return Interval{T,L,R}(first(interval), last(interval))
end

!isempty(str) || throw("syntax: Interval must not be empty")
f, l = first(str), last(str)
L = (f in ('[', '[')) ? Closed :
(f in ('(', '(')) ? Open :
Unbounded
R = (l in (']', ']')) ? Closed :
(l in (')', ')')) ? Open :
Unbounded
if L === R === Unbounded
if str !== ','
throw("syntax: Interval specified as unbounded (left delimiter '$f' was not '[','[','(' or '('; right delimiter '$l' was not ']',']',')' or ')'")
end
return :(Interval{$L, $R}(nothing, nothing))
end
if L === Unbounded
if f !== ','
throw("syntax: Left unbounded interval (left delimiter '$f' was not '[','[','(' or '(') must not have a lower bound")
end
expr = Meta.parse(str)
return :(Interval{$L, $R}(nothing, $(esc(expr))))
else
str = str[(ncodeunits(f)+1):end]
end
if R === Unbounded
if l !== ','
throw("syntax: Left unbounded interval (right delimiter '$l' was not '[','[','(' or '(') must not have an upper bound")
end
expr = Meta.parse(str)
return :(Interval{$L, $R}($(esc(expr)), nothing))
else
str = str[1:(end-1)]
end
# Add parentheses around the string to parse this as a tuple
str = string('(', str, ')')
expr = Meta.parse(str)
:(Interval{$L, $R}(($(esc(expr))...)))
end
const var"@i_cmd" = var"@interval_cmd"

##### DISPLAY #####

function print_interval(io, printer, interval::Interval{T,L,R}) where {T,L,R}
print(io, L === Closed ? "[" : "(")
if L !== Unbounded
printer(io, first(interval))
end
print(io, get(io, :compact, false) ? "," : ", ")
if R !== Unbounded
printer(io, last(interval))
end
print(io, R === Closed ? "]" : ")")
end

function Base.show(io::IO, interval::Interval{T,L,R}) where {T,L,R}
if get(io, :compact, false)
print(io, interval)
else
print(io, "$(typeof(interval))(")
L === Unbounded ? print(io, "nothing") : show(io, interval.first)
if L === Closed && R === Closed
print(io, "..(")
show(io, first(interval))
print(io, ", ")
R === Unbounded ? print(io, "nothing") : show(io, interval.last)
show(io, last(interval))
print(io, ")")
else
if get(io, :compact, false)
buf = IOBuffer()
ioctx = IOContext(buf, io)
print_interval(ioctx, show, interval)
str = String(take!(buf))
use_triple = any(==('`'), str)

print(io, 'i', use_triple ? "```" : '`')
print(io, str)
print(io, use_triple ? "```" : '`')
else
Base.show_type_name(io, Base.typename(Interval))
print(io, '{', L, ",", R, "}(")
show(io, L === Unbounded ? nothing : first(interval))
print(io, ", ")
show(io, R === Unbounded ? nothing : first(interval))
print(io, ")")
end
end
end

function Base.print(io::IO, interval::AbstractInterval{T,L,R}) where {T,L,R}
# Print to io in order to keep properties like :limit and :compact
if get(io, :compact, false)
io = IOContext(io, :limit=>true)
function Base.show(io::IO, mime::MIME"text/plain", interval::Interval{T,L,R}) where {T,L,R}
if L === Closed && R === Closed
show(io, mime, first(interval))
print(io, get(io, :compact, false) ? ".." : " .. ")
show(io, mime, last(interval))
else
print_interval(io, (io, x)->show(io, mime, x), interval)
end
end

print(
io,
L === Closed ? "[" : "(",
L === Unbounded ? "" : first(interval),
" .. ",
R === Unbounded ? "" : last(interval),
R === Closed ? "]" : ")",
)
function Base.print(io::IO, interval::AbstractInterval{T,L,R}) where {T,L,R}
print_interval(io, print, interval)
end

##### ARITHMETIC #####
Expand Down