Current section
Files
Jump to
Current section
Files
lib/rclex/parsers/helpers.ex
defmodule Rclex.Parsers.Helpers do
@moduledoc false
# The ROS 2 .msg specification URL is as follows,
# https://docs.ros.org/en/humble/Concepts/Basic/About-Interfaces.html
import NimbleParsec
def parse_type(combinator \\ empty()) do
combinator
|> choice([
builtin_type_array(),
msg_type_array(),
builtin_type(),
msg_type()
])
end
def parse_message(combinator \\ empty()) do
combinator
|> choice([
field_line(),
ignore(constant_line()),
ignore(comment_line()),
ignore(empty_line())
])
|> wrap()
|> times(min: 0)
end
def field_line(combinator \\ empty()) do
combinator
|> optional(whitespace())
|> field_type()
|> ignore(whitespace())
|> field_name()
|> ignore(optional(whitespace()))
|> optional(field_default_value())
|> ignore(optional(whitespace()))
|> ignore(optional(comment()))
|> ignore(new_line())
end
def constant_line(combinator \\ empty()) do
combinator
|> optional(whitespace())
|> field_type()
|> ignore(whitespace())
|> ascii_string([?A..?Z, ?_, ?0..?9], min: 1)
|> ignore(optional(whitespace()))
|> string("=")
|> ignore(optional(whitespace()))
|> choice([value_float(), value_integer(), value_string()])
|> ignore(optional(whitespace()))
|> ignore(optional(comment()))
|> ignore(new_line())
end
defp comment_line(combinator \\ empty()) do
combinator
|> optional(whitespace())
|> comment()
|> new_line()
end
defp empty_line(combinator \\ empty()) do
combinator
|> optional(whitespace())
|> new_line()
end
defp field_type(combinator) do
combinator
|> choice([
builtin_type_array() |> reduce({Enum, :join, []}) |> unwrap_and_tag(:builtin_type_array),
msg_type_array() |> reduce({Enum, :join, []}) |> unwrap_and_tag(:msg_type_array),
builtin_type() |> unwrap_and_tag(:builtin_type),
msg_type() |> unwrap_and_tag(:msg_type)
])
end
defp field_name(combinator) do
combinator |> ascii_string([?a..?z, ?0..?9, ?_], min: 1)
end
defp field_default_value(combinator \\ empty()) do
combinator
|> choice([
value_float(),
value_integer(),
value_string(),
value_array()
])
end
defp bounded_string(combinator \\ empty()) do
combinator
|> string("string<=")
|> integer(min: 1)
|> reduce({Enum, :join, []})
end
defp builtin_type(combinator \\ empty()) do
combinator
|> choice([
bounded_string(),
string("bool"),
string("byte"),
string("char"),
string("float32"),
string("float64"),
string("int8"),
string("uint8"),
string("int16"),
string("uint16"),
string("int32"),
string("uint32"),
string("int64"),
string("uint64"),
string("string"),
string("wstring")
])
end
defp msg_type(combinator \\ empty()) do
combinator
|> ascii_string([?a..?z, ?A..?Z, ?0..?9, ?_, ?/], min: 1)
end
defp builtin_type_array(combinator \\ empty()) do
combinator
|> builtin_type()
|> choice([
static_array(),
unbounded_dynamic_array(),
bounded_dynamic_array()
])
end
defp msg_type_array(combinator \\ empty()) do
combinator
|> msg_type()
|> choice([
static_array(),
unbounded_dynamic_array(),
bounded_dynamic_array()
])
end
defp static_array(combinator \\ empty()) do
combinator
|> string("[")
|> integer(min: 1)
|> string("]")
end
defp unbounded_dynamic_array(combinator \\ empty()) do
combinator
|> string("[]")
end
defp bounded_dynamic_array(combinator \\ empty()) do
combinator
|> string("[<=")
|> integer(min: 1)
|> string("]")
end
defp value_integer(combinator \\ empty()) do
combinator
|> optional(ascii_string([?-, ?+], 1))
|> ascii_string([?0..?9], min: 1)
|> reduce({Enum, :join, []})
|> map({String, :to_integer, []})
end
defp value_float(combinator \\ empty()) do
combinator
|> optional(ascii_string([?-, ?+], 1))
|> ascii_string([?0..?9], min: 1)
|> string(".")
|> ascii_string([?0..?9], min: 1)
|> reduce({Enum, :join, []})
|> map({String, :to_float, []})
end
defp value_string(combinator \\ empty()) do
combinator
|> ignore(ascii_string([?", ?'], 1))
|> ascii_string([?\s, ?!, ?#..?&, ?(..?~], min: 1)
|> ignore(ascii_string([?", ?'], 1))
end
defp value_array(combinator \\ empty()) do
combinator
|> ignore(string("["))
|> times(
choice([value_float(), value_integer(), value_string()])
|> ignore(optional(whitespace()))
|> ignore(optional(string(",")))
|> ignore(optional(whitespace())),
min: 1
)
|> ignore(string("]"))
|> wrap()
end
defp comment(combinator \\ empty()) do
combinator
|> string("#")
|> optional(ascii_string([?\s..?~, ?\t], min: 1))
end
defp whitespace(combinator \\ empty()) do
combinator |> ascii_string([?\s, ?\t], min: 1)
end
defp new_line(combinator \\ empty()) do
combinator |> choice([string("\n"), string("\r\n")])
end
end