Skip to content

Commit ee1cb14

Browse files
committed
UPdate
1 parent 5c28ebe commit ee1cb14

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

src/FileFormats/LP/read.jl

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ end
2323
Read `io` in the LP file format and store the result in `model`.
2424
2525
This reader attempts to follow the CPLEX LP format, because others like the
26-
lpsolve version are very...flexible...in how they accept input. Read more about
27-
them here: http://lpsolve.sourceforge.net
26+
lpsolve version are very...flexible...in how they accept input.
27+
28+
Read more about the format here:
29+
* http://lpsolve.sourceforge.net
30+
* https://web.mit.edu/lpsolve/doc/CPLEX-format.htm
2831
"""
2932
function Base.read!(io::IO, model::Model{T}) where {T}
3033
if !MOI.is_empty(model)
@@ -98,6 +101,7 @@ const _KEYWORDS = Dict(
98101
"such that" => :CONSTRAINTS,
99102
"st" => :CONSTRAINTS,
100103
"s.t." => :CONSTRAINTS,
104+
"st." => :CONSTRAINTS,
101105
# BOUNDS
102106
"bounds" => :BOUNDS,
103107
"bound" => :BOUNDS,
@@ -244,7 +248,16 @@ function Base.read(state::LexerState, ::Type{Token}, kind::_TokenKind)
244248
return _expect(token, kind)
245249
end
246250

247-
_is_idenfifier(c::Char) = !(isspace(c) || c in ('+', '-', '*', '^', ':'))
251+
# We're a bit more relaxed than typical, allowing any letter or digit, not just
252+
# ASCII.
253+
function _is_identifier(c::Char)
254+
return isletter(c) || isdigit(c) || c in "!\"#\$%&()/,.;?@_`'{}|~"
255+
end
256+
257+
function _is_starting_identifier(c::Char)
258+
return isletter(c) || c in "!\"#\$%&(),;?@_`'{}|~"
259+
end
260+
248261
_is_number(c::Char) = isdigit(c) || c in ('.', 'e', 'E', '+', '-')
249262

250263
function Base.peek(state::LexerState, ::Type{Token}, n::Int = 1)
@@ -276,9 +289,9 @@ function _peek_inner(state::LexerState)
276289
read(state, Char)
277290
end
278291
return Token(_TOKEN_NUMBER, String(take!(buf)))
279-
elseif isletter(c) || c == '_' # Identifier / keyword
292+
elseif _is_starting_identifier(c) # Identifier / keyword
280293
buf = IOBuffer()
281-
while (c = peek(state, Char)) !== nothing && _is_idenfifier(c)
294+
while (c = peek(state, Char)) !== nothing && _is_identifier(c)
282295
write(buf, c)
283296
read(state, Char)
284297
end
@@ -382,7 +395,11 @@ function _parse_number(state::LexerState, cache::Cache{T})::T where {T}
382395
end
383396
end
384397
_expect(token, _TOKEN_NUMBER)
385-
return parse(T, token.value)
398+
ret = tryparse(T, token.value)
399+
if ret === nothing
400+
throw(UnexpectedToken(token))
401+
end
402+
return ret
386403
end
387404

388405
# QUAD_TERM :=

test/FileFormats/LP/LP.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,7 @@ function test_subject_to_name()
12101210
"Such That" => false,
12111211
"st" => false,
12121212
"s.t." => false,
1213+
"st." => false,
12131214
"subject that" => true,
12141215
"subject\nto" => true,
12151216
"s. t." => true,
@@ -1232,6 +1233,33 @@ function test_subject_to_name()
12321233
return
12331234
end
12341235

1236+
function test_parse_variable()
1237+
cache = LP.Cache(LP.Model{Float64}())
1238+
for input in [
1239+
"x",
1240+
"X",
1241+
"e",
1242+
"abc!\"D",
1243+
"π",
1244+
"𝔼1π!~a",
1245+
"x!\"#\$%&()/,.;?@_`'{}|~",
1246+
"aAc2",
1247+
]
1248+
io = IOBuffer(input)
1249+
seekstart(io)
1250+
state = LP.LexerState(io)
1251+
x = LP._parse_variable(state, cache)
1252+
@test cache.variable_name_to_index[input] == x
1253+
end
1254+
for input in ["2", "2x", ".x"]
1255+
io = IOBuffer(input)
1256+
seekstart(io)
1257+
state = LP.LexerState(io)
1258+
@test_throws LP.UnexpectedToken LP._parse_variable(state, cache)
1259+
end
1260+
return
1261+
end
1262+
12351263
function test_parse_number()
12361264
cache = LP.Cache(LP.Model{Float64}())
12371265
for (input, result) in [
@@ -1338,6 +1366,7 @@ function test_parse_term()
13381366
"- x" => -1.0,
13391367
"- -x" => 1.0,
13401368
"+ -x" => -1.0,
1369+
"2x" => 2.0,
13411370
"2.0 x" => 2.0,
13421371
"3.0 x" => 3.0,
13431372
"2.0 * x" => 2.0,

0 commit comments

Comments
 (0)