zevv

zevv

Hereby I’d like to announce a new library I’ve been working on over the last few months: XPeg.

XPeg is a pure Elixir pattern matching library. It provides macros to compile PEG grammars to an Elixir function which will parse a string and capture selected parts of the input. PEGs are not unlike regular expressions, but offer more power and flexibility, and have less ambiguities. More about PEGs on wikipedia.

Some use cases where XPeg is useful are configuration or data file parsers, robust protocol implementations, input validation, lexing of programming languages or domain specific languages.

XPeg allows mixing of the grammar definition with Elixir functions that act on the matched data, allowing for powerful and concise AST generating parsers.

Example

Below is a simple grammar definition that parses a comma separated list of key/value pairs into a list of tuples:

p = Xpeg.peg :dict do
  :dict <- :pair * star("," * :pair) * !1
  :pair <- :word * "=" * :number * fn [a,b | cs] -> [{b,a} | cs] end
  :word <- cap(+{'a'..'z'})
  :number <- cap(+{'0'..'9'}) * fn [v | cs] -> [String.to_integer(v) | cs] end
end

This grammar consists of the following rules:

  • The top level rule :dict matches one :pair, followed by zero-or-more instances of a , followed by a :pair
  • The :pair rule matches a :word followed by an = and a :number
  • The :word rule matches one-or-more characters from the set {'a'..'z'}
  • The :number rule matches one-or-more characters from the set {'0'..'9'}

Some rules are followed by elixir functions that convert or transform the captured data at parse time, resulting in the required AST syntax.

The grammar can be matched against the subject string using the Xpeg.match() function:

Xpeg.match(p, "grass=4,horse=1,star=2")

resulting in the following output:

[{"star", 2}, {"horse", 1}, {"grass", 4}]

Below are some links to more elaborate examples from the GitHub repository:

Showing Posts 1 to 8

the_wildgoose

the_wildgoose

This looks really nice! I’m mostly just asking for conversation, but can you compare this with NimbleParsec? When would one prefer one over the other? Performance?

Good luck!

zevv

zevv OP

Honestly, I’m not at all acquainted with NimbleParsec, or any other available parsers for Elixir - I would have to dive into the alternatives to see how they would compare. One of the strengths of XPeg would be the concise way to build ASTs directly from the grammar, but other parsers might as well offer the same functionality. Performance is currently not great, as there is a number of possible optimizations that I have not yet implemented.

I initially wrote XPeg as an exercise to acquainted with Elixir metaprogramming, the implementation itself is basically a Elixir port of a (pretty much mature) Nim parser I made a a few years ago: NPeg, which is itself based on Lua’s LPeg.

zevv

zevv OP

Apologies for bumping my own thread, but I’d like to mention that Xpeg has learned some nice new tricks over the last few weeks. Most important changes:

  • Performance has been improved drastically; my typical benchmark is parsing JSON into Elixir maps and lists, which now runs at about half as fast as the highly optimized and blazing fast Poison parser.
  • Xpeg can now draw cool railroad graphs for the grammar that it is compiling, which is nice and helpful for understanding and debugging your grammars. for example:

This grammar fragment:

  Obj_pair <- S * String * S * ":" * Value                                                       
  Object <- "{" * (Obj_pair * star("," * Obj_pair) | S) * "}" 

Will be dumped like this railroad diagram at compile time:

                               ╭───────────»──────────╮                                            
Object o──'{'─»─┬─[Obj_pair]─»─┴─┬─","─»─[Obj_pair]─┬─┴──┬─»─"}"──o                                
                │                ╰─────────«────────╯    │                                         
                ╰─[S]────────────────────────────────────╯  

For more info, check the README on the Xpeg github repo

CharlesIrvine

CharlesIrvine

It looks like the latest release in hex has errors and warning with Elixir 1.16. It looks like the code is fixed in GitHub but not released in to hex yet.

zevv

zevv OP

Thanks for the heads up, 0.9.0 has just been released.

CharlesIrvine

CharlesIrvine

Nice! And thanks. Xpeg looks pretty cool. I think I’m going to try it for my library Mozart - a somewhat new take on BPM. I want to create a very readable textual syntax for my current struct based syntax. It might take me a while.

The last time I did any serious parsing was using Bison and Flex. Since Flex took care of tokenization, you didn’t have to account for spaces in your parse rules. It seems like this is not the case for Xpeg, NimbleParsec, etc. Do I have that right?

CharlesIrvine

CharlesIrvine

Hello @zevv

I just tried to take 0.9.0 for a spin, but ran into a problem. I submitted an issue:

https://github.com/zevv/xpeg/issues/10

zevv

zevv OP

True. When parsing a document at the character level with a PEG parser, you need to explicitly handle the white space in your grammar. For some grammars this might sound like a bad thing (more work), but the advantage is that you can handle white space any way you want when it makes sense, for example when parsing indent-based languages.

In practice, I usually define a short symbol like S that matches one or more spaces, tabs or newlines and sprinkle that through my grammar.

That said: PEGs can be more flexible, if the implementation allows. XPeg is basically a port of my original nPeg parser for the Nim language [GitHub - zevv/npeg: PEGs for Nim, another take · GitHub]. nPeg is able to parse grammars for sequences of any types, not only strings. I use this to create more classical lexer/parser stages: a first PEG grammar consumes characters and delivers a sequence of tokens, a second PEG grammar parses the tokens into an AST. I think it should be possible to extend XPeg to do something similar, if the need arises.

— All posts loaded —

Where Next? Top

Trending in Announcing Top

bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
387 15136 120
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
restlessronin
The repo is at GitHub - cyberchitta/openai_ex: Community maintained Elixir library for OpenAI API · GitHub. Docs are at OpenaiEx User Gu...
152 11030 135
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
shahryarjb
The Chelekom project is a library of Phoenix and LiveView components generated via Mix tasks to fit developer needs seamlessly. One of i...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
woylie
Phoenix components for pagination, sortable tables and filter forms with Flop and (optionally) Ecto. pagination cursor pagination sorta...
New

Other Trending Topics Top

mudasobwa
I am seeing a lot of aplications of Argumentum ad Vericundiam in software discussions. They do link some piece of writing and point us to...
New
bartblast
Hey folks, I just published a post about Hologram’s funding and where the project goes next - the short version: Curiosum as Main Spons...
New
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New
Herve37
We’re evaluating API mocking tools for OpenAPI-based projects and would love to hear what other teams are using. We’re particularly inte...
New
sorenone
Today we’re releasing Oban for Python. Not an Oban client in Python. Not a pythonx wrapper embedded in Elixir. Nope, it’s a fully operati...
New
lawik
I was thinking since Goatmire Elixir turned out pretty good I should maybe do another one. 30th of Sep - 2nd of Oct this year./ The firs...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews