dunyakirkali

dunyakirkali

Idiomatic nimble_parsec

I am trying to implement a parser.

I want to parse a string that has the following format:

I did the following:

  sample =
    empty()
    |> ascii_string([?A..?Z, ?0..?9], 2)
    |> ascii_string([0..127], 4)
    |> ascii_string([0..127], 4)
    |> ignore(string("$"))

  defparsec :parse, sample

As much as I like it, something tells me that it can be cleaner / more consice.

Any ideas?

Marked As Solved

kip

kip

ex_cldr Core Team

Using nimble_parsec I tend to prefer using a helper module for combinators rather than inline. I also tend to create semantic functions because sooner or later debugging a nimble_parsec combinator - and providing reasonable messages back to a user on parse errors - becomes a challenge.

Example parser

This is a pattern I usually follow, here just parsing your first example.

defmodule Parse.Helpers do
  import NimbleParsec

  def version() do
    head()
    |> concat(hex_integer(4))
    |> label("version")
    |> tag(:version)
  end

  def hex_integer(digits) do
    ascii_string([?a..?f, ?A..?F, ?0..?9], digits)
    |> reduce(:hex_to_integer)
  end

  def count() do
    hex_integer(4)
    |> label("count")
    |> unwrap_and_tag(:count)
  end

  def head() do
    ascii_string([?A..?Z, ?0..?9], 2)
    |> label("head")
  end

  def tail() do
    string("$")
    |> label("tail")
    |> unwrap_and_tag(:tail)
  end

  def hex_to_integer([string]) do
    String.to_integer(string, 16)
  end

end

defmodule Parser do
  import NimbleParsec
  import Parse.Helpers

  defparsec :parse,
    version()
    |> concat(count())
    |> concat(tail())

end

Example usage

iex> Parser.parse "AB98761234$"
{:ok, [version: ["AB", 39030], count: 4660, tail: "$"], "", %{}, {1, 0}, 11}

iex> Parser.parse "AB98761234" 
{:error, "expected version, followed by count, followed by tail", "AB98761234",
 %{}, {1, 0}, 0}

Also Liked

kip

kip

ex_cldr Core Team

Noting BTW that you can use binary pattern matching to parse variable length content. This article has a really good example for parsing PNG files.

kip

kip

ex_cldr Core Team

Given you’re heading down the nimble_parsec route I would stay in that domain and do something like:

defparsec :parse,
  choice([
    string("+ACK:GTHBD") |> parsec(:parse_ack),
    string("+SACK:GTHBD") |> parsec(:parse_sack)
  ])
end
kip

kip

ex_cldr Core Team

OK, revised version. Not sure its any more “beautiful” than your version but may be food for thought:

case some_string do
  <<h1::integer-8, h2::integer-8, version::binary-4, count::binary-4, "$">> 
      when (h1 in ?A..?Z or h1 in ?0..?9) and (h2 in ?A..?Z or h2 in ?0..?9)->
    header = List.to_string [h1, h2]
    version = String.to_integer(version, 16)
    count = String.to_integer(count, 16)
    {:ok, header, version, count}
  _other ->
    {:error, :invalid}
end

Where Next?

Popular in Questions Top

LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Other popular topics Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New

We're in Beta

About us Mission Statement