Home

Awesome

Build Status

About

A parser and interpreter written in Elixir for ABNF grammars.

ABNF is defined in the RFC2234, which is obsoleted by RFC4234, which in turn is obsoleted by the RFC5234. There's also an update in the RFC7405.

This library implements the latest definition (RFC5234) (with erratas #3076, and #2968), and RFC7405.

Use example

iex(1)> grammar = ABNF.load_file "test/resources/ipv4.abnf"
iex(2)> initial_state = %{}
iex(2)> ABNF.apply grammar, "ipv4address", '250.246.192.34', initial_state
%ABNF.CaptureResult{
  input: '250.246.192.34',
  rest: '',
  state: %{ipv4address: '250.246.192.34'},
  string_text: '250.246.192.34',
  string_tokens: ['250', '.', '246', '.', '192', '.', '34'],
  values: ["Your ip address is: 250.246.192.34"]
}

The result can be read as an %ABNF.CaptureResult{} where:

More complex examples

How it works

This is not a parser generator, but an interpreter. It will load up an ABNF grammar, and generate an (kind of) AST for it. Then you can apply any of the rules to an input and the interpreter will parse the input according to the rule.

Using it with Mix

To use it in your Mix projects, first add it as a dependency:

def deps do
  [{:ex_abnf, "~> 0.2.8"}]
end

Then run mix deps.get to install it.

Adding custom code to reduce rules

After a rule, you can add your own code, for example:

userinfo      = *( unreserved / pct-encoded / sub-delims / ":" ) !!!
  state = Map.put state, :userinfo, rule
  {:ok, state}
!!!

The code in question will be packed together into a module that is created in runtime to speed up execution later on.

Your code can return:

And your code will be called with the following bindings:

Adding helper code

You can also start your grammar with code to write your own helper functions and module additions. For example:

!!!
require Logger
def return_value(ip) do
  Logger.debug "Hello world"
  "Your ip address is: #{ip}"
end
!!!

IPv4address =
  dec-octet "."
  dec-octet "."
  dec-octet "."
  dec-octet !!!
    state = Map.put state, :ipv4address, rule
    {:ok, state, return_value(rule)}
  !!!

dec-octet = DIGIT         ; 0-9
  / %x31-39 DIGIT         ; 10-99
  / "1" 2DIGIT            ; 100-199
  / "2" %x30-34 DIGIT     ; 200-249
  / "25" %x30-35          ; 250-255

DIGIT = %x30-39

Note how the result of the IPv4address rule is the result of a call to the function return_value.

Changes from 0.1.x to 0.2.x

License

The source code is released under Apache 2 License.

Check LICENSE file for more information.