Current section
Files
Jump to
Current section
Files
lib/dantzig/solution/parser.ex
defmodule Dantzig.Solution.Parser do
@moduledoc false
import NimbleParsec
newline =
choice([
string("\r\n"),
string("\n")
])
skipping_newlines = fn combinators ->
Enum.reduce(combinators, empty(), fn comb, acc ->
acc
|> ignore(repeat(newline))
|> concat(comb)
end)
end
defp build_float(parts) do
{f, ""} = Float.parse(to_string(parts))
f
end
defp build_integer(parts) do
{i, ""} = Integer.parse(to_string(parts))
i
end
line = utf8_string([not: ?\n, not: ?\r], min: 0)
model_status =
ignore(string("Model status") |> concat(ignore(newline)))
|> concat(line)
|> unwrap_and_tag(:model_status)
float =
optional(string("-"))
|> ascii_string([?0..?9], min: 1)
|> string(".")
|> ascii_string([?0..?9], min: 1)
|> reduce(:build_float)
exponential_notation_float =
optional(string("-"))
|> ascii_string([?0..?9], min: 1)
|> optional(string(".") |> ascii_string([?0..?9], min: 1))
|> string("e")
|> optional(string("-"))
|> ascii_string([?0..?9], min: 1)
|> reduce(:build_float)
integer =
optional(string("-"))
|> ascii_string([?0..?9], min: 1)
|> reduce(:build_integer)
number = choice([exponential_notation_float, float, integer])
feasibility =
line |> unwrap_and_tag(:feasibility)
# Assume that the objective is separated from the value
# by only a single space
objective =
ignore(string("Objective "))
|> concat(number)
|> unwrap_and_tag(:objective)
variable = ascii_string([not: ?\s, not: ?\n, not: ?\r], min: 1)
defp build_variable([name, value]) do
{name, value}
end
defp build_variable_map(pairs) do
Enum.into(pairs, %{})
end
columns =
skipping_newlines.([
ignore(
string("# Columns ")
|> concat(integer)
),
repeat(
variable
|> ignore(string(" "))
|> concat(number)
|> concat(ignore(newline))
|> reduce(:build_variable)
)
])
|> reduce(:build_variable_map)
|> unwrap_and_tag(:variables)
rows =
skipping_newlines.([
ignore(
string("# Rows ")
|> concat(integer)
),
repeat(
variable
|> ignore(string(" "))
|> concat(number)
|> reduce(:build_variable)
)
])
|> reduce(:build_variable_map)
|> unwrap_and_tag(:constraints)
primal_solution_values =
skipping_newlines.([
ignore(
string("# Primal solution values")
|> concat(ignore(newline))
),
feasibility,
objective,
columns,
rows
])
solution_file =
skipping_newlines.([
model_status,
primal_solution_values
])
defparsec(:solution_file, solution_file)
def parse!(text) do
{:ok, parsed, _rest, _context, _, _} = solution_file(text)
parsed
end
def parse(text) do
case solution_file(text) do
{:ok, parsed, _rest, _context, _, _} ->
{:ok, parsed}
_other ->
{:error, text}
end
end
end