philomena/lib/search/literal_parser.ex

59 lines
1.2 KiB
Elixir

defmodule Search.LiteralParser do
import NimbleParsec
defp to_number(input), do: Search.Helpers.to_number(input)
float =
ascii_string([?0..?9], min: 1)
|> optional(ascii_char('.') |> ascii_string([?0..?9], min: 1))
|> reduce({List, :to_string, []})
|> reduce(:to_number)
edit_distance =
ignore(string("~"))
|> concat(float)
|> unwrap_and_tag(:fuzz)
|> eos()
stopwords =
choice([
string("*"),
string("?"),
edit_distance
])
normal =
lookahead_not(stopwords)
|> choice([
ignore(string("\\")) |> utf8_char([]),
utf8_char([])
])
|> repeat()
|> reduce({List, :to_string, []})
|> unwrap_and_tag(:literal)
|> optional(edit_distance)
|> eos()
# Runs of Kleene stars are coalesced.
# Fuzzy search has no meaning in wildcard mode, so we ignore it.
wildcard =
lookahead_not(edit_distance)
|> choice([
ignore(string("\\")) |> utf8_char([]),
string("*") |> ignore(repeat(string("*"))),
utf8_char([])
])
|> repeat()
|> reduce({List, :to_string, []})
|> unwrap_and_tag(:wildcard)
|> ignore(optional(edit_distance))
|> eos()
literal =
choice([
normal,
wildcard
])
defparsec :parse, literal
end