-
-
Save inoas/e9f338f6900b227ad6f423c104a5c7e1 to your computer and use it in GitHub Desktop.
Parsing a simple expression grammar with maybe quoted strings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule ParseHelpers do | |
import NimbleParsec | |
def whitespace() do | |
ascii_char([?\s]) | |
|> repeat | |
end | |
def quote_mark(combinator \\ empty()) do | |
combinator | |
|> ignore(optional(whitespace())) | |
|> ascii_char([?"]) | |
end | |
def and_(combinator \\ empty()) do | |
combinator | |
|> ignore(optional(whitespace())) | |
|> ascii_char([?&]) | |
end | |
def or_(combinator \\ empty()) do | |
combinator | |
|> ignore(optional(whitespace())) | |
|> ascii_char([?|]) | |
end | |
def left_paren(combinator \\ empty()) do | |
combinator | |
|> ignore(optional(whitespace())) | |
|> ascii_char([?(]) | |
end | |
def right_paren(combinator \\ empty()) do | |
combinator | |
|> ignore(optional(whitespace())) | |
|> ascii_char([?)]) | |
end | |
def input_string(combinator \\ empty()) do | |
combinator | |
|> choice([ | |
quoted_string(), | |
unquoted_string() | |
]) | |
end | |
@syntax Enum.map([?", ?(, ?), ?&, ?|, ?\s], &{:not, &1}) | |
def unquoted_string(combinator \\ empty()) do | |
combinator | |
|> ignore(optional(whitespace())) | |
|> repeat(ascii_char(@syntax)) | |
|> reduce({List, :to_string, []}) | |
end | |
def quoted_string(combinator \\ empty()) do | |
combinator | |
|> ignore(optional(whitespace())) | |
|> ignore(quote_mark()) | |
|> repeat(maybe_escaped_char()) | |
|> ignore(quote_mark()) | |
|> reduce({List, :to_string, []}) | |
end | |
def maybe_escaped_char(combinator \\ empty()) do | |
combinator | |
|> choice([ | |
ignore(string("\\")) |> ascii_char([]), | |
ascii_char([{:not, ?"}]) | |
]) | |
end | |
def to_postfix([left, ?&, right]) do | |
[:and, left, right] | |
end | |
def to_postfix([left, ?|, right]) do | |
[:or, left, right] | |
end | |
end | |
defmodule ParserGrammar do | |
import NimbleParsec | |
import ParseHelpers | |
# quotation_mark ::= ?" | |
# left_bracket ::= ?( | |
# right_bracket ::= ?) | |
# or_ ::= ?| | |
# and_ ::= ?& | |
# Based upon knowledge @ https://www.youtube.com/watch?v=dDtZLm7HIJs#t=15m54s | |
# expression ::= term or_ expression | term | |
# term ::= factor and_ term | factor | |
# factor ::= left_bracket expression right_bracket | input_string | |
defparsec :expression, | |
choice([ | |
parsec(:term) |> or_() |> parsec(:expression) |> reduce(:to_postfix), | |
parsec(:term) | |
]) | |
defparsec :term, | |
ignore(optional(whitespace())) | |
|> choice([ | |
parsec(:factor) |> and_() |> parsec(:term) |> reduce(:to_postfix), | |
parsec(:factor) | |
]) | |
defparsec :factor, | |
ignore(optional(whitespace())) | |
|> choice([ | |
ignore(left_paren()) |> parsec(:expression) |> ignore(right_paren()) |> wrap(), | |
input_string() | |
]) | |
defparsec :parse, | |
parsec(:expression) | |
|> ignore(optional(whitespace())) | |
|> eos() | |
end | |
defmodule ParseTest do | |
@inputs [ | |
"\"1 quote\"", | |
"\"2 quote\" | \"quote\"", | |
"\"3 quote\" & foo | \"quote\"", | |
"\"4 quote\" & (foo | bar) & \"quote\"", | |
"5 & (foo | (bar | \"qu( & )ux\" & batz))", | |
"6&(foo|(bar|\"qu( & )ux\"&batz))", | |
"7bar&\"qu( & )ux\"", | |
"8 | foo & (\"b a a r\" & quux) | batz", | |
"99 | ab & \"c d\"", | |
"10aa & bb | cc", | |
"11aa | bb & cc" | |
] | |
def test do | |
for string <- @inputs do | |
{:ok, result, "", _, _, _} = ParserGrammar.parse(string) | |
result | |
end | |
end | |
end | |
defmodule Evaluator do | |
def evaluate(term) when is_binary(term) do | |
inspect(term) | |
end | |
def evaluate([:or, left, right]) do | |
"#{evaluate(left)} OR #{evaluate(right)}" | |
end | |
def evaluate([:and, left, right]) do | |
"#{evaluate(left)} AND #{evaluate(right)}" | |
end | |
def evaluate([expression]) do | |
"( #{evaluate(expression)} )" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment