README.md

# Grammar

*One rule to ring them all*

Easily build parsers / transformers for structured data.

## Overview

An Elixir library that helps building parsers / transformers for LL(1) structured data. 

## Usage

> Please have a look at the examples for more advanced use cases

### API 

The following grammar extracts a list of integers from string of comma separated number, and enclosed by square brackets.

```elixir
  Grammar.new()
  |> Grammar.add_clause(:list, ["[", :content, "]"], fn ["[", list, "]"] -> list || [] end)
  |> Grammar.add_clause(:content, [:number, :list_tail], fn [number, list] -> [number | list || []] end, true)
  |> Grammar.add_clause(:list_tail, [",", :number, :list_tail], fn [",", number, list] -> [number | list || []] end, true)
  |> Grammar.add_clause(:number, [~r/[0-9]+/], fn [number] -> String.to_integer(number) end)
  |> Grammar.prepare!()
  |> Grammar.start(:list)
  |> Grammar.loop(Grammar.Tokenizer.new("[1, 2, 6, 12]"))

  {:ok, [1, 2, 6, 12]}
```

### DSL 

`use Grammar` and write "rule" functions that have direct access to parsing results.

You are fully in control of what is returned by each rule function.

Under the hood, the `rule/2` macro uses the regular API to build a `Grammar` at compile time, and bury it in the module you're defining. 

#### Example

This is the same grammar parser as in the [API section](#api), but under the form of a dedicated module.

```elixir
defmodule NumberListReader do
  use Grammar

  rule list("[", :list_tail, "]") do
    [_, list, _] = params
    case list do
      nil -> []
      _ -> list
    end
  end

  rule? list_tail(:number, :list_tail_tail) do
    [number, list] = params
    [number | (list || [])]
  end

  rule? list_tail_tail(",", :number, :list_tail_tail) do
    [_, number, list] = params
    [number | (list || [])]
  end

  rule number(~r/[0-9]+/) do
    [number] = params
    String.to_integer(number)
  end
end

$iex> NumberListReader.parse("[1, 2, 6, 12]")
{:ok, [1, 2, 6, 12]}
```