README.md
1## Combine
2
3A parser combinator library for Elixir projects.
4
5[![Master](https://travis-ci.org/bitwalker/combine.svg?branch=master)](https://travis-ci.org/bitwalker/combine)
6[![Hex.pm Version](http://img.shields.io/hexpm/v/combine.svg?style=flat)](https://hex.pm/packages/combine)
7
8## How to Use
9
10First add it to your dependency list like so:
11
12```elixir
13def deps do
14 [{:combine, "~> x.x.x"}, ...]
15end
16```
17
18Documentation is [located here](http://hexdocs.pm/combine).
19
20From there the API is fairly straightforward, the docs cover what
21parser combinators are available, but here's a quick taste of how you
22use it:
23
24```elixir
25defmodule DateTimeParser do
26 use Combine
27
28 def parse(datetime), do: Combine.parse(datetime, parser)
29
30 defp parser, do: date |> ignore(char("T")) |> time
31
32 defp date do
33 label(integer, "year")
34 |> ignore(char("-"))
35 |> label(integer, "month")
36 |> ignore(char("-"))
37 |> label(integer, "day")
38 end
39
40 defp time(previous) do
41 previous
42 |> label(integer, "hour")
43 |> ignore(char(":"))
44 |> label(integer, "minute")
45 |> ignore(char(":"))
46 |> label(float, "seconds")
47 |> either(
48 map(char("Z"), fn _ -> "UTC" end),
49 pipe([either(char("-"), char("+")), word], &(Enum.join(&1)))
50 )
51 end
52end
53
54...> datetime = "2014-07-22T12:30:05.0002Z"
55...> datetime_zoned = "2014-07-22T12:30:05.0002+0200"
56...> DateTimeParser.parse(datetime)
57[2014, 7, 22, 12, 30, 5.0002, "UTC"]
58...> DateTimeParser.parse(datetime_zoned)
59[2014, 7, 22, 12, 30, 5.0002, "+0200"]
60
61```
62
63## Why Combine vs ExParsec?
64
65Combine is a superset of ExParsec's API for the most part and it's performance is significantly
66better in the one benchmark I've run with a very simple parser. Benchfella was used to run the
67benchmarks, and the benchmarks used for comparison are present in both Combine and ExParsec's
68`bench` directories with the exception of the datetime parsing one, which is easily replicated
69in ExParsec if you wish to double check yourself. For reference, here's what I'm seeing on my machine:
70
71```
72# ExParsec
73
74Settings:
75 duration: 1.0 s
76
77## Bench.ExParsec.Binary
78[19:01:54] 1/2: many bits
79## Bench.ExParsec.Text
80[19:01:56] 2/2: many any_char
81
82Finished in 5.67 seconds
83
84## Bench.ExParsec.Binary
85many bits 1000 1731.83 µs/op
86
87## Bench.ExParsec.Text
88many any_char 5000 616.02 µs/op
89parse ISO 8601 datetime 2000 964.48 µs/op
90
91# Combine
92
93Settings:
94 duration: 1.0 s
95
96## Combine.Bench
97[15:21:21] 1/5: parse ISO 8601 datetime
98[15:21:22] 2/5: many bits
99[15:21:25] 3/5: many any_char
100[15:21:27] 4/5: large set of choices (one_of/word)
101[15:21:30] 5/5: large set of choices (choice/parsers)
102
103Finished in 12.08 seconds
104
105## Combine.Bench
106large set of choices (one_of/word) 100000 25.60 µs/op
107many any_char 50000 32.98 µs/op
108many bits 50000 43.90 µs/op
109parse ISO 8601 datetime 10000 139.45 µs/op
110large set of choices (choice/parsers) 10000 265.04 µs/op
111```
112
113ExParsec also appears to be falling behind on maintenace, even with PRs being submitted,
114so rather than forking I decided to write my own from scratch that met my needs.
115
116## Parsers
117
118You should look at the docs for usage on each parser combinator, but the following
119lists which ones are available in each module.
120
121### Combine.Parsers.Base
122--------
123```
124between both
125choice either
126eof fail
127fatal ignore
128label many
129map none_of
130one_of option
131pair_both pair_left
132pair_right pipe
133satisfy sep_by
134sep_by1 sequence
135skip skip_many
136skip_many1 times
137zero
138```
139
140### Combine.Parsers.Text
141--------
142```
143alphanumeric bin_digit
144char digit
145float fixed_integer
146hex_digit integer
147letter lower
148newline octal_digit
149space spaces
150string tab
151upper word
152word_of
153```
154
155### Combine.Parsers.Binary
156--------
157```
158bits bytes
159float int
160uint
161```
162
163## Roadmap
164
165- Streaming parsers
166
167## License
168
169MIT
170