Skip to content

Commit 7a56e06

Browse files
yashrajguptadpsanders
authored andcommitted
Add string parsing (#289)
* Add extreme cases in string parsing * Add parsing for uncertain forms * add tests
1 parent fd7bee9 commit 7a56e06

File tree

2 files changed

+220
-4
lines changed

2 files changed

+220
-4
lines changed

src/parsing.jl

Lines changed: 179 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ function parse(::Type{DecoratedInterval{T}}, s::AbstractString) where T
1010
m = match(r"(\[.*\])(\_.*)?", s)
1111

1212
if m == nothing # matched
13-
throw(ArgumentError("Unable to process string $x as decorated interval"))
13+
14+
m = match(r"(.*\?[a-z0-9]*)(\_.*)?", s)
15+
16+
if m == nothing
17+
throw(ArgumentError("Unable to process string $x as decorated interval"))
18+
end
1419

1520
end
1621

@@ -49,32 +54,196 @@ function parse(::Type{Interval{T}}, s::AbstractString) where T
4954
b = parse(T, strip(m.captures[2]))
5055

5156
return a ± b
52-
end
5357

5458
a = parse(T, s, RoundDown)
5559
b = parse(T, s, RoundUp)
5660

5761
return Interval(a, b)
5862

63+
end
64+
65+
if m == nothing
66+
67+
m = match(r"(\-?\d*\.?\d*)\?\?([ud]?)", s) # match with string of form "34??"
68+
69+
if m!= nothing
70+
if m.captures[2] == "" # strings of the form "10??"
71+
return entireinterval(T)
72+
end
73+
if m.captures[2] == "u" # strings of the form "10??u"
74+
lo = parse(Float64, m.captures[1])
75+
hi = Inf
76+
interval = eval(make_interval(T, lo, [hi]))
77+
return interval
78+
end
79+
if m.captures[2] == "d" # strings of the form "10??d"
80+
lo = -Inf
81+
hi = parse(Float64, m.captures[1])
82+
interval = eval(make_interval(T, lo, [hi]))
83+
return interval
84+
end
85+
end
86+
87+
m = match(r"(\-?\d*)\.?(\d*)\?(\d*)([ud]?)(e-?\d*)?", s) # match with strings like "3.56?1u" or "3.56?1e2"
88+
89+
if m != nothing # matched
90+
if m.captures[3] != "" && m.captures[4] == "" && m.captures[5] == nothing # string of form "3.4?1" or "10?2"
91+
d = length(m.captures[2])
92+
x = parse(Float64, m.captures[3] * "e-$d")
93+
n = parse(Float64, m.captures[1]*"."*m.captures[2])
94+
lo = n - x
95+
hi = n + x
96+
interval = eval(make_interval(T, lo, [hi]))
97+
return interval
98+
end
99+
100+
if m.captures[3] == "" && m.captures[4] == "" && m.captures[5] == nothing # strings of the form "3.46?"
101+
d = length(m.captures[2])
102+
x = parse(Float64, "0.5" * "e-$d")
103+
n = parse(Float64, m.captures[1]*"." * m.captures[2])
104+
lo = n - x
105+
hi = n + x
106+
interval = eval(make_interval(T, lo, [hi]))
107+
return interval
108+
end
109+
110+
if m.captures[3] == "" && m.captures[4] == "" && m.captures[5] != nothing # strings of the form "3.46?e2"
111+
d = length(m.captures[2])
112+
x = parse(Float64, "0.5" * "e-$d")
113+
n = parse(Float64, m.captures[1]*"." * m.captures[2])
114+
lo = parse(Float64, string(n-x) * m.captures[5])
115+
hi = parse(Float64, string(n+x) * m.captures[5])
116+
interval = eval(make_interval(T, lo, [hi]))
117+
return interval
118+
end
119+
120+
if m.captures[3] == "" && m.captures[4] == "u" && m.captures[5] == nothing # strings of the form "3.4?u"
121+
d = length(m.captures[2])
122+
x = parse(Float64, "0.5" * "e-$d")
123+
n = parse(Float64, m.captures[1]*"."*m.captures[2])
124+
lo = n
125+
hi = n+x
126+
interval = eval(make_interval(T, lo, [hi]))
127+
return interval
128+
end
129+
130+
if m.captures[3] == "" && m.captures[4] == "d" && m.captures[5] == nothing # strings of the form "3.4?d"
131+
d = length(m.captures[2])
132+
x = parse(Float64, "0.5" * "e-$d")
133+
n = parse(Float64, m.captures[1]*"."*m.captures[2])
134+
lo = n - x
135+
hi = n
136+
interval = eval(make_interval(T, lo, [hi]))
137+
return interval
138+
end
139+
140+
if m.captures[3] != ""
141+
if m.captures[4] == "u" && m.captures[5] != nothing #strings of the form "3.46?1u"
142+
d = length(m.captures[2])
143+
x = parse(Float64, m.captures[3] * "e-$d")
144+
n = parse(Float64, m.captures[1]*"."*m.captures[2])
145+
lo = parse(Float64, string(n) * m.captures[5])
146+
hi = parse(Float64, string(n+x) * m.captures[5])
147+
interval = eval(make_interval(T, lo, [hi]))
148+
return interval
149+
end
150+
151+
if m.captures[4] == "u" && m.captures[5] == nothing #strings of the form "3.46?1u"
152+
d = length(m.captures[2])
153+
x = parse(Float64, m.captures[3] * "e-$d")
154+
n = parse(Float64, m.captures[1]*"."*m.captures[2])
155+
lo = n
156+
hi = n+x
157+
interval = eval(make_interval(T, lo, [hi]))
158+
return interval
159+
end
160+
161+
if m.captures[4] == "d" && m.captures[5] != nothing #strings of the form "3.46?1d"
162+
d = length(m.captures[2])
163+
x = parse(Float64, m.captures[3] * "e-$d")
164+
n = parse(Float64, m.captures[1]*"."*m.captures[2])
165+
lo = parse(Float64, string(n-x) * m.captures[5])
166+
hi = parse(Float64, string(n) * m.captures[5])
167+
interval = eval(make_interval(T, lo, [hi]))
168+
return interval
169+
end
170+
171+
if m.captures[4] == "d" && m.captures[5] == nothing #strings of the form "3.46?1d"
172+
d = length(m.captures[2])
173+
x = parse(Float64, m.captures[3] * "e-$d")
174+
n = parse(Float64, m.captures[1]*"."*m.captures[2])
175+
lo = n-x
176+
hi = n
177+
interval = eval(make_interval(T, lo, [hi]))
178+
return interval
179+
end
180+
181+
if m.captures[4] == "" && m.captures[5] != nothing # strings of the form "3.56?1e2"
182+
d = length(m.captures[2])
183+
x = parse(Float64, m.captures[3] * "e-$d")
184+
n = parse(Float64, m.captures[1]*"."*m.captures[2])
185+
lo = parse(Float64, string(n-x)*m.captures[5])
186+
hi = parse(Float64, string(n+x)*m.captures[5])
187+
interval = eval(make_interval(T, lo, [hi]))
188+
return interval
189+
end
190+
end
191+
end
192+
193+
m = match(r"(-?\d*\.?\d*)", s) # match strings of form "1" and "2.4"
194+
195+
if m != nothing
196+
lo = parse(Float64, m.captures[1])
197+
hi = lo
198+
end
199+
200+
if m == nothing
201+
throw(ArgumentError("Unable to process string $s as interval"))
202+
end
203+
204+
interval = eval(make_interval(T, lo, [hi]))
205+
return interval
206+
end
59207
end
60208

61209
# match string of form [a, b]_dec:
62210
m = match(r"\[(.*),(.*)\]", s)
63211

64212
if m != nothing # matched
213+
214+
m.captures[1] = strip(m.captures[1], [' '])
215+
m.captures[2] = strip(m.captures[2], [' '])
65216
lo, hi = m.captures
66217

67-
else
218+
if m.captures[2] == "+infinity" || m.captures[2] == ""
219+
hi = "Inf"
220+
end
221+
222+
if m.captures[1] == "-infinity" || m.captures[1] == ""
223+
lo = "-Inf"
224+
end
225+
226+
end
227+
228+
if m == nothing
68229

69230
m = match(r"\[(.*)\]", s) # string like "[1]"
70231

71232
if m == nothing
72233
throw(ArgumentError("Unable to process string $s as interval"))
73234
end
74235

75-
if m.captures[1] == "Empty"
236+
m.captures[1] = strip(m.captures[1], [' '])
237+
238+
if m.captures[1] == "Empty" || m.captures[1] == ""
76239
return emptyinterval(T)
77240
end
241+
if m.captures[1] == "entire"
242+
return entireinterval(T)
243+
end
244+
if m.captures[1] == "nai" || m.captures[1] == "Nai"
245+
return nai(T)
246+
end
78247

79248
lo = m.captures[1]
80249
hi = lo
@@ -83,6 +252,12 @@ function parse(::Type{Interval{T}}, s::AbstractString) where T
83252

84253
expr1 = Meta.parse(lo)
85254
expr2 = Meta.parse(hi)
255+
if lo == "-Inf"
256+
expr1 = parse(Float64, lo)
257+
end
258+
if hi == "Inf"
259+
expr2 = parse(Float64, hi)
260+
end
86261

87262
interval = eval(make_interval(T, expr1, [expr2]))
88263

test/interval_tests/parsing.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,27 @@ setprecision(128)
99
@test parse(Interval{Float64}, "1") == Interval(1, 1)
1010
@test parse(Interval{Float64}, "[1, 2]") == Interval(1, 2)
1111
@test parse(Interval{Float64}, "[-0x1.3p-1, 2/3]") == @interval(-0x1.3p-1, 2/3)
12+
@test parse(Interval{Float64}, "[1,+infinity]") == Interval(1.0, Inf)
13+
@test parse(Interval{Float64}, "[1.234e5,Inf]") == Interval(123400.0, Inf)
14+
@test parse(Interval{Float64}, "[]") ==
15+
@test parse(Interval{Float64}, "[,]") == entireinterval(Float64)
16+
@test parse(Interval{Float64}, "[ entire ]") == entireinterval(Float64)
17+
@test parse(Interval{Float64}, "3.56?1") == Interval(0x3.8ccccccccccccp+0, 0x3.91eb851eb8520p+0)
18+
@test parse(Interval{Float64}, "3.56?1e2") == Interval(355.0, 357.0)
19+
@test parse(Interval{Float64}, "3.560?2") == Interval(0x3.8ed916872b020p+0, 0x3.8fdf3b645a1ccp+0)
20+
@test parse(Interval{Float64}, "3.56?") == Interval(0x3.8e147ae147ae0p+0, 0x3.90a3d70a3d70cp+0)
21+
@test parse(Interval{Float64}, "3.560?2u") == Interval(0x3.8f5c28f5c28f4p+0, 0x3.8fdf3b645a1ccp+0)
22+
@test parse(Interval{Float64}, "-10?") == Interval(-10.5, -9.5)
23+
@test parse(Interval{Float64}, "-10?u") == Interval(-10.0, -9.5)
24+
@test parse(Interval{Float64}, "-10?12") == Interval(-22.0, 2.0)
25+
@test parse(Interval{Float64}, "0.0?d") == Interval(-0.05, 0.0)
26+
@test parse(Interval{Float64}, "2.5?d") == Interval(0x1.3999999999999p+1, 2.5)
27+
@test parse(Interval{Float64}, "0.000?5d") == Interval(-0.005, 0.0)
28+
@test parse(Interval{Float64}, "2.500?5d") == Interval(0x1.3f5c28f5c28f5p+1, 2.5)
29+
@test parse(Interval{Float64}, "2.5??d") == Interval(-Inf, 2.5)
30+
@test parse(Interval{Float64}, "2.500?5ue4") == Interval(0x1.86ap+14, 0x1.8768p+14)
31+
@test parse(Interval{Float64}, "2.500?5de-5") == Interval(0x1.a2976f1cee4d5p-16, 0x1.a36e2eb1c432dp-16)
32+
@test parse(Interval{Float64}, "3.1416?1") == Interval(0x3.24395810624dcp+0, 0x3.24467381d7dc0p+0)
1233

1334
@test parse(Interval{BigFloat}, "1") == @biginterval(1)
1435
@test parse(Interval{BigFloat}, "[-0x1.3p-1, 2/3]") == @biginterval(-0x1.3p-1, 2/3)
@@ -27,4 +48,24 @@ end
2748
@test parse(DecoratedInterval{Float64}, "[3, 4]") == DecoratedInterval(3, 4)
2849

2950
@test parse(DecoratedInterval{Float64}, "[3, 4]_dac") == DecoratedInterval(3, 4, dac)
51+
52+
@test parse(DecoratedInterval{Float64}, "[ ]") == DecoratedInterval(∅, trv)
53+
54+
@test parse(DecoratedInterval{Float64}, "[entire]") == DecoratedInterval(Interval(-Inf, Inf), dac)
55+
56+
@test parse(DecoratedInterval{Float64}, "[,]") == DecoratedInterval(entireinterval(Float64), dac)
57+
@test parse(DecoratedInterval{Float64}, "3.56?1") == DecoratedInterval(Interval(0x3.8ccccccccccccp+0, 0x3.91eb851eb8520p+0), com)
58+
@test parse(DecoratedInterval{Float64}, "3.56?1e2") == DecoratedInterval(Interval(355.0, 357.0), com)
59+
@test parse(DecoratedInterval{Float64}, "3.560?2") == DecoratedInterval(Interval(0x3.8ed916872b020p+0, 0x3.8fdf3b645a1ccp+0), com)
60+
@test parse(DecoratedInterval{Float64}, "3.56?") == DecoratedInterval(Interval(0x3.8e147ae147ae0p+0, 0x3.90a3d70a3d70cp+0), com)
61+
@test parse(DecoratedInterval{Float64}, "3.56?e2") == DecoratedInterval(Interval(3.555e2, 3.565e2), com)
62+
@test parse(DecoratedInterval{Float64}, "3.560?2u") == DecoratedInterval(Interval(0x3.8f5c28f5c28f4p+0, 0x3.8fdf3b645a1ccp+0), com)
63+
@test parse(DecoratedInterval{Float64}, "-10?") == DecoratedInterval(Interval(-10.5, -9.5), com)
64+
@test parse(DecoratedInterval{Float64}, "-10?e2") == DecoratedInterval(Interval(-10.5e2, -9.5e2), com)
65+
@test parse(DecoratedInterval{Float64}, "-10?u") == DecoratedInterval(Interval(-10.0, -9.5), com)
66+
@test parse(DecoratedInterval{Float64}, "-10?12") == DecoratedInterval(Interval(-22.0, 2.0), com)
67+
@test parse(DecoratedInterval{Float64}, "-10??u") == DecoratedInterval(Interval(-10.0, Inf), dac)
68+
@test parse(DecoratedInterval{Float64}, "-10??") == DecoratedInterval(Interval(-Inf, Inf), dac)
69+
@test isnai(parse(DecoratedInterval{Float64}, "[nai]"))
70+
@test parse(DecoratedInterval{Float64}, "3.56?1_def") == DecoratedInterval(Interval(0x3.8ccccccccccccp+0, 0x3.91eb851eb8520p+0), def)
3071
end

0 commit comments

Comments
 (0)