How to parse strings with NimbleParsec?

Hello.
i would like to parse strings (with nimble parsec i assume) that look something like this:

x(some.dot.split.values) → [x: [“some”, “dot”, “split”, “value”]]
y(some.x(dot).split.values) → [y: [“some”, [x: [“dot”]], “split”, “value”]]

maybe if it is easier to implement

x some.dot.split.values → [x: [“some”, “dot”, “split”, “value”]]
y some.(x dot).split.values → [y: [“some”, [x: [“dot”]], “split”, “value”]]

Idea is to have nested patterns, and dot split strings that should represent array

I found this, but i do not understand how perentacies are implemented there How do you write nested and recursive NimbleParsec parsers? - #4 by jakemorrison

Maybe someone knows. Thanks.

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)")
6 Likes

Here is a library that supports parsing nested lists with parentheses: GitHub - cogini/http_structured_field: Elixir library to parse HTTP Structured Fields (RFC 8941)

1 Like