Current section
Files
Jump to
Current section
Files
lib/expr.ex
defmodule CommonParser.Expr do
@moduledoc """
Documentation for Parser.
"""
import NimbleParsec
import CommonParser.Helper
# tag := ascii_tag_with_space([?a..?z])
# single_value := string_with_quote | integer | atom_with_space
# list_value := [ single_value | single_value , single_value ]
# value := single_value | list_value
# op1 := = | in | not in
# op2 := < | <= | > | >=
# op3 := and | or
# cond1 := ( tag op1 value )
# cond2 := ( tag op2 integer )
# sub_expr := ( cond1 | cond2 )
# expr := sub_expr op3 expr | sub_expr
tag = parse_tag() |> reduce({:parser_result_to_atom, []})
single_value = choice([parse_string(), parse_integer(), parse_atom()])
defcombinatorp :list_entries,
choice([
single_value
|> concat(ignore_space())
|> concat(ignore_sep(","))
|> concat(ignore_space())
|> concat(parsec(:list_entries)),
single_value
])
list_value =
ignore(string("["))
|> concat(ignore_space())
|> parsec(:list_entries)
|> concat(ignore_space())
|> ignore(string("]"))
|> reduce({Enum, :uniq, []})
value = choice([single_value, list_value]) |> unwrap_and_tag(:v)
op1 = parse_ops(["=", "!=", "in", "not in"]) |> reduce({:parser_result_to_atom, []})
op2 = parse_ops(["<", "<=", ">", ">="]) |> reduce({:parser_result_to_atom, []})
op3 = parse_ops(["and", "or"]) |> reduce({:parser_result_to_atom, []})
cond1 = tag |> concat(ignore_space()) |> concat(op1) |> concat(ignore_space()) |> concat(value)
cond2 =
tag
|> concat(ignore_space())
|> concat(op2)
|> concat(ignore_space())
|> concat(parse_integer())
sub_expr = ignore_bracket(?\(, choice([cond1, cond2]), ?\))
defcombinatorp :expr,
choice([
sub_expr
|> concat(ignore_space())
|> concat(op3)
|> concat(ignore_space())
|> concat(ignore_bracket(?\(, parsec(:expr), ?\)))
|> tag(:expr),
sub_expr
])
@doc """
Parse to an atom. For testing purpose. Please use `parse/2` instead.
iex> CommonParser.Expr.parse_atom(":h")
{:ok, [:h], "", %{}, {1, 0}, 2}
iex> CommonParser.Expr.parse_atom(":hello_world")
{:ok, [:hello_world], "", %{}, {1, 0}, 12}
iex> CommonParser.Expr.parse_atom(":he2llo_world1")
{:ok, [:he], "2llo_world1", %{}, {1, 0}, 3}
"""
defparsec :parse_atom, parse_atom()
@doc ~S"""
Parse to a string. For testing purpose. Please use `parse/2` instead.
iex> CommonParser.Expr.parse_quoted_string(~S("hello world"))
{:ok, ["hello world"], "", %{}, {1, 0}, 13}
iex> CommonParser.Expr.parse_quoted_string(~S(hello world))
{:error, "expected byte equal to ?\"", "hello world", %{}, {1, 0}, 0}
iex> CommonParser.Expr.parse_quoted_string(~S("hello \"world\""))
{:ok, ["hello \"world\""], "", %{}, {1, 0}, 17}
"""
defparsec :parse_quoted_string, parse_string()
@doc """
Parse a value. For testing purpose. Please use `parse/2` instead.
iex> CommonParser.Expr.parse_value("10")
{:ok, [v: 10], "", %{}, {1, 0}, 2}
iex> CommonParser.Expr.parse_value(~S(["a", :a, 1]))
{:ok, [v: ["a", :a, 1]], "", %{}, {1, 0}, 12}
"""
defparsec :parse_value, value
@doc """
Parse a sub expr. For testing purpose. Please use `parse/2` instead.
iex> CommonParser.Expr.parse_expr("a != 1")
{:ok, [:a, :!=, {:v, 1}], "", %{}, {1, 0}, 6}
iex> CommonParser.Expr.parse_expr(~S(a in ["hello", :world, 2]))
{:ok, [:a, :in, {:v, ["hello", :world, 2]}], "", %{}, {1, 0}, 25}
"""
defparsec :parse_expr, sub_expr
@doc ~S"""
Parse an expression.
iex> CommonParser.Expr.parse("a=1 and b = 2")
{:ok, [expr: [:a, :==, {:v, 1}, :and, :b, :==, {:v, 2}]], "", %{}, {1, 0}, 13}
iex> CommonParser.Expr.parse("a=1 and b in [\"abc\", :abc, 123]")
{:ok, [expr: [:a, :==, {:v, 1}, :and, :b, :in, {:v, ["abc", :abc, 123]}]], "", %{}, {1, 0}, 31}
iex> CommonParser.Expr.parse("a=1 and (b in [\"abc\", :abc, 123] or c != [1,2,3])")
{:ok, [expr: [:a, :==, {:v, 1}, :and, {:expr, [:b, :in, {:v, ["abc", :abc, 123]}, :or, :c, :!=, {:v, [1, 2, 3]}]}]], "", %{}, {1, 0}, 49}
"""
defparsec :parse, parsec(:expr)
end