Skip to content

Commit 33a4a4f

Browse files
committed
Proper support for dots
1 parent 37ee380 commit 33a4a4f

File tree

3 files changed

+53
-3
lines changed

3 files changed

+53
-3
lines changed

src/Lists/Lists.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ unparse(s::String) = repr(s)
6969
unparse(i::BigInt) = string(i)
7070
unparse(::Void) = "#<void>"
7171

72+
Base.convert(::Type{List}, xs::List) = xs
7273
Base.convert(::Type{List}, xs) = List(xs...)
7374
append(::Nil, β::List) = β
7475
append::Cons, β::List) = Cons(car(α), append(cdr(α), β))

src/Parser/Parser.jl

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,48 @@ function readsymbol(io::IO)
7474
String(take!(buf))
7575
end
7676

77+
"""
78+
A dot as in (a . b).
79+
"""
80+
struct Dot end
81+
82+
"""
83+
Build an improper list (a b . c).
84+
"""
85+
function improperlist(prefix, terminal)
86+
if isempty(prefix)
87+
terminal
88+
else
89+
Cons(prefix[1], improperlist(prefix[2:end], terminal))
90+
end
91+
end
92+
93+
"""
94+
Check that there are no . in objs, and return the list if so. Otherwise process the dots as
95+
they should be.
96+
"""
97+
function listify(objs) :: List
98+
objs_ = collect(objs)
99+
dots = count(equalto(Dot()), objs_)
100+
if dots == 0
101+
objs_
102+
elseif dots == 1
103+
if length(objs_) 2 || objs_[end - 1] !== Dot()
104+
error("invalid use of .")
105+
else
106+
improperlist(objs_[1:end-2], objs_[end])
107+
end
108+
elseif dots == 2
109+
if length(objs_) == 5 && objs_[2] === objs_[4] === Dot()
110+
[objs_[3], objs_[1], objs_[5]]
111+
else
112+
error("invalid use of .")
113+
end
114+
else
115+
error("invalid use of .")
116+
end
117+
end
118+
77119
"""
78120
nextobject(io::IO)
79121
@@ -94,7 +136,7 @@ function nextobject(io::IO)
94136
read(io, Char)
95137
cl = closer(c)
96138
objects = readobjectsuntil(io, cl)
97-
Nullable(convert(List, objects))
139+
Nullable(listify(objects))
98140
elseif c in ")]}"
99141
error("mismatched superfluous $c")
100142
elseif c in keys(_READER_MACROS)
@@ -109,7 +151,7 @@ function nextobject(io::IO)
109151
else
110152
str = readsymbol(io)
111153
if str == "."
112-
error(". currently unsupported")
154+
Nullable(Dot())
113155
else
114156
asnumber = tryparse(Number, str)
115157
Nullable(get(asnumber, Symbol(str)))

test/parser.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,14 @@ end
6868

6969
@testset "lists" begin
7070
@test sx("(1 2 3)") == List(1, 2, 3)
71-
@test_broken sx("(1 . 2)") == Cons(1, 2)
71+
@test sx("(1 . 2)") == Cons(1, 2)
72+
@test sx("(1 . (2))") == List(1, 2)
73+
@test sx("(1 . + . 2)") == List(:+, 1, 2)
74+
@test sx("(1 2 . 3)") == Cons(1, Cons(2, 3))
75+
@test_throws ErrorException sx("(.)")
76+
@test_throws ErrorException sx("(. x)")
77+
@test_throws ErrorException sx("(1 . 2 3)")
78+
@test_throws ErrorException sx("(1 . 2 . 3 . 4)")
7279
end
7380

7481
@testset "reader macros" begin

0 commit comments

Comments
 (0)