defmodule Parser do
import NimbleParsec
# Step 1: Define your smallest units
# I've assumed here that the a word is a list of
# more than one lowercase character
#
# Match abc
defparsecp(
:word,
times(ascii_char([?a..?z]), min: 1)
|> reduce({List, :to_string, []})
)
# Step 2: Define a function as a a thing
# that has a word followed by:
# - an opening bracket
# - a "chain of stuff"
# - a closing bracket
#
# Match abc(___)
defparsec(
:function,
parsec(:word)
|> map({String, :to_atom, []})
|> concat(
ignore(string("("))
|> concat(parsec(:chain))
|> concat(ignore(string(")")))
# Put the function arguments in a list
|> wrap()
)
)
# Match abc or abc(___)
defparsecp(
:function_or_word,
choice([
parsec(:function),
parsec(:word)
])
)
# Step 3: Define the "chain" as a
# word or function followed by a "."
# delimited list of words or functions
#
# Match abc.def or abc(___).def or abc(___).def
defparsecp(
:chain,
parsec(:function_or_word)
|> times(
ignore(string("."))
|> concat(parsec(:function_or_word)),
min: 0
)
)
def parse!(str) do
# Only parse successfully if the entire string is consumed
{:ok, result, "" = _unconsumed, %{}, {_, _}, _} =
function(str)
result
end
end
Parser.parse!("x(some.dot.split.values)")
Parser.parse!("y(some.x(dot).split.values)")