README.md (5520B)
1 # NimbleParsec 2 3 [Online Documentation](https://hexdocs.pm/nimble_parsec). 4 5 <!-- MDOC !--> 6 7 `NimbleParsec` is a simple and fast library for text-based parser 8 combinators. 9 10 Combinators are built during runtime and compiled into multiple 11 clauses with binary matching. This provides the following benefits: 12 13 * Performance: since it compiles to binary matching, it leverages 14 many Erlang VM optimizations to generate extremely fast parser 15 code with low memory usage 16 17 * Composable: this library does not rely on macros for building and 18 composing parsers, therefore they are fully composable. The only 19 macros are `defparsec/3` and `defparsecp/3` which emit the compiled 20 clauses with binary matching 21 22 * No runtime dependency: after compilation, the generated parser 23 clauses have no runtime dependency on `NimbleParsec`. This opens up 24 the possibility to compile parsers and do not impose a dependency on 25 users of your library 26 27 * No footprints: `NimbleParsec` only needs to be imported in your modules. 28 There is no need for `use NimbleParsec`, leaving no footprints on your 29 modules 30 31 The goal of this library is to focus on a set of primitives for writing 32 efficient parser combinators. The composition aspect means you should be 33 able to use those primitives to implement higher level combinators. 34 35 Note this library does not handle low-level binary parsing. In such cases, 36 we recommend using [Elixir's bitstring syntax](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#%3C%3C%3E%3E/1). 37 38 ## Examples 39 40 ```elixir 41 defmodule MyParser do 42 import NimbleParsec 43 44 date = 45 integer(4) 46 |> ignore(string("-")) 47 |> integer(2) 48 |> ignore(string("-")) 49 |> integer(2) 50 51 time = 52 integer(2) 53 |> ignore(string(":")) 54 |> integer(2) 55 |> ignore(string(":")) 56 |> integer(2) 57 |> optional(string("Z")) 58 59 defparsec :datetime, date |> ignore(string("T")) |> concat(time), debug: true 60 end 61 62 MyParser.datetime("2010-04-17T14:12:34Z") 63 #=> {:ok, [2010, 4, 17, 14, 12, 34, "Z"], "", %{}, 1, 21} 64 ``` 65 66 If you add `debug: true` to `defparsec/3`, it will print the generated 67 clauses, which are shown below: 68 69 ```elixir 70 defp datetime__0(<<x0, x1, x2, x3, "-", x4, x5, "-", x6, x7, "T", 71 x8, x9, ":", x10, x11, ":", x12, x13, rest::binary>>, 72 acc, stack, comb__context, comb__line, comb__column) 73 when x0 >= 48 and x0 <= 57 and (x1 >= 48 and x1 <= 57) and 74 (x2 >= 48 and x2 <= 57) and (x3 >= 48 and x3 <= 57) and 75 (x4 >= 48 and x4 <= 57) and (x5 >= 48 and x5 <= 57) and 76 (x6 >= 48 and x6 <= 57) and (x7 >= 48 and x7 <= 57) and 77 (x8 >= 48 and x8 <= 57) and (x9 >= 48 and x9 <= 57) and 78 (x10 >= 48 and x10 <= 57) and (x11 >= 48 and x11 <= 57) and 79 (x12 >= 48 and x12 <= 57) and (x13 >= 48 and x13 <= 57) do 80 datetime__1( 81 rest, 82 [(x13 - 48) * 1 + (x12 - 48) * 10, (x11 - 48) * 1 + (x10 - 48) * 10, 83 (x9 - 48) * 1 + (x8 - 48) * 10, (x7 - 48) * 1 + (x6 - 48) * 10, (x5 - 48) * 1 + (x4 - 48) * 10, 84 (x3 - 48) * 1 + (x2 - 48) * 10 + (x1 - 48) * 100 + (x0 - 48) * 1000] ++ acc, 85 stack, 86 comb__context, 87 comb__line, 88 comb__column + 19 89 ) 90 end 91 92 defp datetime__0(rest, acc, _stack, context, line, column) do 93 {:error, "...", rest, context, line, column} 94 end 95 96 defp datetime__1(<<"Z", rest::binary>>, acc, stack, comb__context, comb__line, comb__column) do 97 datetime__2(rest, ["Z"] ++ acc, stack, comb__context, comb__line, comb__column + 1) 98 end 99 100 defp datetime__1(rest, acc, stack, context, line, column) do 101 datetime__2(rest, acc, stack, context, line, column) 102 end 103 104 defp datetime__2(rest, acc, _stack, context, line, column) do 105 {:ok, acc, rest, context, line, column} 106 end 107 ``` 108 109 As you can see, it generates highly inlined code, comparable to 110 hand-written parsers. This gives `NimbleParsec` an order of magnitude 111 performance gains compared to other parser combinators. Further performance 112 can be gained by giving the `inline: true` option to `defparsec/3`. 113 114 <!-- MDOC !--> 115 116 ## Installation 117 118 Add `nimble_parsec` to your list of dependencies in `mix.exs`: 119 120 ```elixir 121 def deps do 122 [ 123 {:nimble_parsec, "~> 1.0"} 124 ] 125 end 126 ``` 127 128 ## Nimble* 129 130 All nimble libraries by Dashbit: 131 132 * [NimbleCSV](https://github.com/dashbitco/nimble_csv) - simple and fast CSV parsing 133 * [NimbleOptions](https://github.com/dashbitco/nimble_options) - tiny library for validating and documenting high-level options 134 * [NimbleParsec](https://github.com/dashbitco/nimble_parsec) - simple and fast parser combinators 135 * [NimblePool](https://github.com/dashbitco/nimble_pool) - tiny resource-pool implementation 136 * [NimblePublisher](https://github.com/dashbitco/nimble_publisher) - a minimal filesystem-based publishing engine with Markdown support and code highlighting 137 * [NimbleTOTP](https://github.com/dashbitco/nimble_totp) - tiny library for generating time-based one time passwords (TOTP) 138 139 ## License 140 141 Copyright 2018 Plataformatec \ 142 Copyright 2020 Dashbit 143 144 Licensed under the Apache License, Version 2.0 (the "License"); 145 you may not use this file except in compliance with the License. 146 You may obtain a copy of the License at 147 148 http://www.apache.org/licenses/LICENSE-2.0 149 150 Unless required by applicable law or agreed to in writing, software 151 distributed under the License is distributed on an "AS IS" BASIS, 152 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 153 See the License for the specific language governing permissions and 154 limitations under the License.