<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="95853" data-post-id="95853">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I appreciate the suggestion - and I’ve tried all manner of ordering without success.  I can’t seen a way to avoid needing some kind of forcing backtracking (<code>fail</code>ing a parser in my post above).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="95853" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/43">Post #42</a>
	                </div>
	            </div>
              <div id="likers-container-95853" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="95853"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #42"></div>
  </section>
</div>
    <div class="postbit" id="95901" data-post-id="95901">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>A very quick suggestion is to unfold the ABNF grammar into complete combinators and then refactor it. IIRC ABNF is able of tracking back multiple levels but nimble isn’t, so you need to restructure the problem. The <a href="https://github.com/plataformatec/nimble_parsec/blob/master/examples/simple_math.exs" rel="nofollow">math example</a> shows how the solutions end-up a bit different.</p>
<p>The other thing I noticed is that you are excessively relying on <code>parsec</code>. You should compose on the helpers as much as possible and fallback to combinators only if compilation times are high.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="95901" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/44">Post #43</a>
	                </div>
	            </div>
              <div id="likers-container-95901" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="95901"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #43"></div>
  </section>
</div>
    <div class="postbit" id="95951" data-post-id="95951">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I second <a class="mention" href="/u/josevalim" rel="nofollow">@josevalim</a> here. If you use regular combinators, the parser will be measurably faster, although it will be slower to compile. By using <code>parsec</code> excessively you WILL decrease runtime performance (although it might be worh it if compile times are too high).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="95951" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/45">Post #44</a>
	                </div>
	            </div>
              <div id="likers-container-95951" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="95951"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #44"></div>
  </section>
</div>
    <div class="postbit" id="96083" data-post-id="96083">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Appreciate the feedback <a class="mention" href="/u/josevalim" rel="nofollow">@josevalim</a> and <a class="mention" href="/u/tmbb" rel="nofollow">@tmbb</a>. Moved everything to helpers except the entry point.  Shows about 30% performance improvement (in a non scientific timer test).  I’ll try some grammar unrolling this weekend to see if I can work around my original issue.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96083" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/46">Post #45</a>
	                </div>
	            </div>
              <div id="likers-container-96083" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96083"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #45"></div>
  </section>
</div>
    <div class="postbit" id="96086" data-post-id="96086">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="kip" data-post="46" data-topic="12860">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/48/1440_2.png" class="avatar"> kip:</div>
<blockquote>
<p>Shows about 30% performance improvement (in a non scientific timer test)</p>
</blockquote>
</aside>
<p>I have written a small library to make such tests as scientific as possible and as easy as possible. It’s called schism, and it’s available on Github here (no hex package yet): <a href="https://github.com/tmbb/schism" class="inline-onebox" rel="noopener nofollow ugc">GitHub - tmbb/schism: A library that makes it easy to have alternative implementation of functions for benchmarks · GitHub</a></p>
<p>There is a post discussing it here in the forum: <a href="https://forum.elixirforum.com/t/schism-a-library-to-make-benchmarking-easy-and-heretical/13844" class="inline-onebox" rel="nofollow">Schism - a library to make benchmarking easy (and heretical!)</a></p>
<p>Basically, it uses some macros to define alternative versions of code fragments (function definitions, parts of function definitions, etc). It then provides some utilities to make it easier to benchmark the changes with Benchee.</p>
<p>You use it like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule YourModule do
  # Some code, don't need to touch here
  # The code here is the same for both versions
  schism "inline vs parsec" do
    dogma "parsec" do
      # single combinator, coded in such a way that it uses parsec
    end

    heresy "inline" do
      # same combinator, but avoiding parsec
    end
  end
  # some more code..
  # The code here is the same for both versions
end
</code></pre>
<p>With benchee you can then compare the dogma and the heresy and determine objectively what is faster. This is obviously something you can do yourself, but the beauty of it is that it provides a very simple API with almost no friction to test the impact of small changes in single functions on the performance of the whole application, without having to maintain two copies of the application.</p>
<p>EDIT: are you afraid the two implementations of the function are not compatible? Don’t worry, the <code>defsnippet</code> macro in the Schism package handles that situation pretty well. For what it’s worth, Schism was originally written to benchmark alternative implementations of nimble_parsec parsers.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96086" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/47">Post #46</a>
	                </div>
	            </div>
              <div id="likers-container-96086" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96086"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #46"></div>
  </section>
</div>
    <div class="postbit" id="96087" data-post-id="96087">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thats very cool and I can’t believe I missed it when you posted in April.  Thanks for the assist!  And now back to grammar unrolling … <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96087" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/48">Post #47</a>
	                </div>
	            </div>
              <div id="likers-container-96087" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96087"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #47"></div>
  </section>
</div>
    <div class="postbit" id="96295" data-post-id="96295">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I ran a benchee example of my old parser and the new one using <code>nimble_parsec</code> with only one <code>parsec/1</code> call.  The old one used an ABNF parser (and the even older one used <code>leex</code> and <code>yecc</code>).  Really positive results:</p>
<p>Old version: Average of 163.87 μs<br>
Nimble_parsec version:  Average of 28.40 μs</p>
<p>I think ~6 times faster was worth the evenings work <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"> .  Thanks again <a class="mention" href="/u/josevalim" rel="nofollow">@josevalim</a> and <a class="mention" href="/u/tmbb" rel="nofollow">@tmbb</a></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96295" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/49">Post #48</a>
	                </div>
	            </div>
              <div id="likers-container-96295" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96295"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #48"></div>
  </section>
</div>
    <div class="postbit" id="96298" data-post-id="96298">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tmbb" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tmbb
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="kip" data-post="49" data-topic="12860">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/48/1440_2.png" class="avatar"> kip:</div>
<blockquote>
<p>I think ~6 times faster was worth the evenings work</p>
</blockquote>
</aside>
<p>By the way, did you end up using Schism?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96298" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/50">Post #49</a>
	                </div>
	            </div>
              <div id="likers-container-96298" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96298"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #49"></div>
  </section>
</div>
    <div class="postbit" id="96312" data-post-id="96312">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kip" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/kip/120/1440_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kip
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>ex_cldr Core Team</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Not this time - the dependencies are different (abnf2 which I maintain and nimble_parsec).  But for sure its going to be a standard part of my performance improvement activities from now on.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="96312" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/51">Post #50</a>
	                </div>
	            </div>
              <div id="likers-container-96312" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="96312"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #50"></div>
  </section>
</div>
    <div class="postbit" id="107590" data-post-id="107590">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="mjadczak" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mjadczak/120/3537_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  mjadczak
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p><em>Note: I initially wrote this as an issue on the repo, but I thought it might be better served by being a forum post, as it turned out quite long, and may invite discussion.</em></p>
<p><a class="mention" href="/u/josevalim" rel="nofollow">@josevalim</a>, let me first start by saying thanks for making this library. I really like the combination of just creating a data structure describing the parser using functions (thus avoiding excessive macro magic and making things as composable as possible), but then—instead of interpreting that structure at runtime—compiling it into efficient code.</p>
<p>While this isn’t explicitly mentioned anywhere in the project docs, the overall style of the library plus the reference to Parsec suggests that the underlying idea (whether originally intentional or not) is to do PEG-style parsing. Even if it’s not an exact implementation of PEG, it would be reasonable to expect that NimbleParsec should be equal in “parsing power”, if you will, to a PEG parser—that is, admit any languages which a PEG parser does.</p>
<p>If you look, for instance, at <a href="https://en.wikipedia.org/wiki/Parsing_expression_grammar#Syntax" rel="noopener nofollow ugc">Wikipedia’s definition of a PEG</a>, you will see the following combinators mentioned as fundamental operators (you can technically get away with fewer, but it starts to get impractical to implement), and most of these do resemble constructs NimbleParsec has:</p>
<ul>
<li><strong>Sequence</strong>—either  <code>concat/2</code> or the first argument of a combinator</li>
<li><strong>Ordered choice</strong>—<code>choice/2</code> implements this</li>
<li><strong>Zero-or-more</strong>—<code>repeat/2</code></li>
<li><strong>One-or-more</strong>—<code>times/3</code> with <code>min: 1</code></li>
</ul>
<p>However, then you come to these:</p>
<ul>
<li><strong>And-predicate</strong> (<code>&amp;</code>)</li>
<li><strong>Not-predicate</strong> (<code>!</code>)</li>
</ul>
<p>These are the two predicates which denote positive and negative lookahead. Put simply, <code>&amp;</code> asserts that the parser it wraps matches, and <code>!</code> asserts that the parser it wraps does not match. Crucially, <em>they don’t consume any input</em>. Say that we call them <code>require</code> and <code>deny</code> for the purposes of NimbleParsec. That means that <code>require(string("foo")) |&gt; utf8_string([], min: 3)</code> would parse in any string starting in “foo”, and <code>deny(utf8_char([?0..?9])) |&gt; utf8_string([], min: 1)</code> would parse any string <strong>not</strong> starting with a digit.</p>
<p><em>N.B. technically you only need <code>!</code>, since for any parser <code>p</code>, <code>!!p == &amp;p</code> in behaviour, but explicitly implementing <code>&amp;</code> is likely to be more efficient.</em></p>
<h2><a name="p-107590-why-is-proper-lookahead-support-needed-1" class="anchor" href="#p-107590-why-is-proper-lookahead-support-needed-1" aria-label="Heading link" rel="nofollow"></a>Why is proper lookahead support needed?</h2>
<p>I know most requests of the form “we should implement a new combinator in NimbleParsec” are met with the response of “this library is supposed to be barebones, and you should make your own combinators”, as well as “what is the particular usecase”? Therefore, I aim to show a couple of grammars which I believe NimbleParsec cannot currently parse properly, but which would be useful to support, and how they could be supported if <code>require</code> and <code>unless</code> are implemented.</p>
<p><em>N.B. I am not tied to the names <code>require/2</code> and <code>deny/2</code>—they were the first I though of which seemed to fit in with the existing naming theme.</em></p>
<h3><a name="p-107590-keywords-and-identifiers-2" class="anchor" href="#p-107590-keywords-and-identifiers-2" aria-label="Heading link" rel="nofollow"></a>Keywords and identifiers</h3>
<p>Let’s imagine we’re parsing a super simple programming language. For now, we only need to care about keywords (let’s say <code>if</code> and <code>while</code>) and identifiers (let’s say any string at all in the range of <code>[a-zA-Z0-9]</code>).</p>
<p>Great, let’s get started! We go ahead and write some code like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule NPL.Helpers do
  import NimbleParsec

  def keyword(c \\ empty()),
    do:
      c
      |&gt; choice([
        string("if") |&gt; replace(:if),
        string("while") |&gt; replace(:while)
      ])
      |&gt; tag(:kw)

  def identifier(c \\ empty()), do: c |&gt; utf8_string([?a..?z, ?A..?Z, ?0..?9], min: 1) |&gt; tag(:id)

  def expr_(),
    do:
      choice([
        keyword(),
        identifier()
      ])
      |&gt; optional(string(" ") |&gt; ignore() |&gt; expr())

  def expr(c \\ empty()), do: c |&gt; parsec(:expr)
end

defmodule NPL do
  import NimbleParsec
  import NPL.Helpers

  defparsec(
    :expr,
    expr_()
  )
end
</code></pre>
<p>Let’s test it:</p>
<pre data-code-wrap="iex"><code class="lang-iex">iex(1)&gt; import NPL
NPL
iex(2)&gt; expr "if"
{:ok, [kw: [:if]], "", %{}, {1, 0}, 2}
iex(3)&gt; expr "foo"
{:ok, [id: ["foo"]], "", %{}, {1, 0}, 3}
iex(4)&gt; expr "foo if"
{:ok, [id: ["foo"], kw: [:if]], "", %{}, {1, 0}, 6}
</code></pre>
<p>Seems like it works fine!</p>
<pre data-code-wrap="iex"><code class="lang-iex">iex(5)&gt; expr "iffy"  
{:ok, [kw: [:if]], "fy", %{}, {1, 0}, 2}
</code></pre>
<p>Uh… no,  we wanted that to just be identifier. Now we have a keyboard and a failed parse for the rest of the input. Maybe if we switch the order of the <code>choice</code> around?</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def expr_(),
    do:
      choice([
        identifier(),
        keyword()
      ])
      |&gt; optional(string(" ") |&gt; ignore() |&gt; expr())
</code></pre>
<pre data-code-wrap="iex"><code class="lang-iex">iex(2)&gt; expr "iffy"
{:ok, [id: ["iffy"]], "", %{}, {1, 0}, 4}
iex(3)&gt; expr "if"  
{:ok, [id: ["if"]], "", %{}, {1, 0}, 2}
</code></pre>
<p>Now identifiers which have a keyword prefix are parsed fine, but we no longer recognise keywords as keywords!</p>
<h3><a name="p-107590-units-3" class="anchor" href="#p-107590-units-3" aria-label="Heading link" rel="nofollow"></a>Units</h3>
<p>You might say “NimbleParsec was really designed for much simpler parsing—language codes, CSV maybe, simple formats, not full on languages!” Fine, let’s tackle a much simpler problem: parsing measurement units. Let’s define a parser like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule NPT.Helpers do
  import NimbleParsec

  def metric(c \\ empty()),
    do:
      c
      |&gt; choice([
        string("mol"),
        string("l"),
        string("m"),
        string("g")
      ])
      |&gt; label("metric base unit")

  def datetime(c \\ empty()),
    do:
      c
      |&gt; choice([
        string("yr"),
        string("mo"),
        string("wk"),
        string("d"),
        string("h")
      ])
      |&gt; label("temporal unit")

  def prefix(c \\ empty()),
    do:
      c
      |&gt; choice([
        string("m"),
        string("k"),
        string("c")
      ])
      |&gt; label("metric prefix")

  def unit(c \\ empty()),
    do:
      choice([
        datetime() |&gt; tag(:datetime),
        metric() |&gt; tag(:metric),
        prefix() |&gt; tag(:prefix) |&gt; metric() |&gt; tag(:metric)
      ])
end

defmodule NPT do
  import NimbleParsec
  import NPT.Helpers

  defparsec(
    :parse_unit,
    unit()
  )
end
</code></pre>
<pre data-code-wrap="iex"><code class="lang-iex">iex(1)&gt; parse_unit "m"
{:ok, [metric: ["m"]], "", %{}, {1, 0}, 1}
iex(2)&gt; parse_unit "mol"
{:ok, [datetime: ["mo"]], "l", %{}, {1, 0}, 2}
iex(3)&gt; parse_unit "ml" 
{:ok, [metric: ["m"]], "l", %{}, {1, 0}, 1}
</code></pre>
<p>It’s the same issue!</p>
<p>It turns out that PEG parsers (which is how NimbleParsec is acting), in contrast to, for instance, regular expressions, are greedy and don’t backtrack. If we have a choice between several underlying parsers, and one of them parses a prefix of the other, we will always have issues. PEG gives us a great deal of control over how exactly parsing takes place, but this also requires us to be very specific where the parser needs to be careful—that is, where it needs to check ahead without consuming any input, so it can make a decision on how to consume further input (PEGs are linear, so once the output is consumed, there’s no going back! In NimbleParsec, once the output is consumed, we have tail-recursed and there is no way to go back!)</p>
<h2><a name="p-107590-why-is-lookahead2-not-enough-4" class="anchor" href="#p-107590-why-is-lookahead2-not-enough-4" aria-label="Heading link" rel="nofollow"></a>Why is <code>lookahead/2</code> not enough?</h2>
<p>This issue <a href="https://github.com/plataformatec/nimble_parsec/issues/5" rel="noopener nofollow ugc">has been mentioned</a> on the repo, and as a response, the <code>lookahead/2</code> function has been implemented. Let’s try fixing our keyword parser using the function, and let’s add a couple more parsecs just for the individual types of objects we have in our grammar, to help with debugging.</p>
<p>Our goal here is to try to say “when we’re trying to parse an identifier, first check if the next token up to a space is a keyword. If so, fail the parse (with the expectation that when in a choice block, this would cause the next choice to be taken).”</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">...
  def identifier(c \\ empty()),
    do:
      c
      |&gt; lookahead(:fail_on_keyword)
      |&gt; utf8_string([?a..?z, ?A..?Z, ?0..?9], min: 1)
      |&gt; tag(:id)
...
  def fail_on_keyword("if " &lt;&gt; _, _, _, _), do: {:error, "if found"}
  def fail_on_keyword("while " &lt;&gt; _, _, _, _), do: {:error, "if found"}
  def fail_on_keyword(_, ctx, _, _), do: {[], ctx}
...
  defparsec(
    :keyword,
    keyword()
  )

  defparsec(
    :identifier,
    identifier()
  )
</code></pre>
<p>Right off the bat, we see one immediate issue with <code>lookahead/2</code>: it’s not a composable combinator, but rather some sort of halfway measure, where we need to define a separate function for each little thing we want to check. We also need to ensure this extra function is accessible in the module which defines the parsec! We can’t just easily re-use our existing combinators, we’d need to have a separate function call and explicitly call another parsec within it, and return <code>:error</code> or <code>:ok</code> based on whether it succeeds. That’s quite unergonomic, but perhaps could be solved to some degree with a <code>quoted_lookahead</code>. Nevertheless, let’s try our new solution out:</p>
<pre data-code-wrap="iex"><code class="lang-iex">iex(2)&gt; keyword "if"
{:ok, [kw: [:if]], "", %{}, {1, 0}, 2}
iex(3)&gt; identifier "iffy"
{:ok, [id: ["iffy"]], "", %{}, {1, 0}, 4}
iex(4)&gt; identifier "if " 
{:error, "if found", "if ", %{}, {1, 0}, 0}
</code></pre>
<p>Seems like our individual parsers are working ok. Now, let’s try their combination:</p>
<pre data-code-wrap="iex"><code class="lang-iex">iex(6)&gt; expr "if foo"
{:error, "if found", "if foo", %{}, {1, 0}, 0}
</code></pre>
<p>And no, this doesn’t work. Even if you ignore the ergonomic issues around <code>lookahead</code>, its fundamental shortcoming is the fact that it doesn’t compose—when it fails inside a <code>choice</code> or <code>repeat</code> block, the failure leaks to the top level of the parser, instead of causing the parent parser to stop looking at that branch and continue.</p>
<h2><a name="p-107590-proposed-solution-5" class="anchor" href="#p-107590-proposed-solution-5" aria-label="Heading link" rel="nofollow"></a>Proposed solution</h2>
<p>Revisiting the keyword example one more time, I think that you should be able to write something like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  def id_char(c \\ empty()), do: c |&gt; utf8_char([?a..?z, ?A..?Z, ?0..?9])
  
  def keyword(c \\ empty()),
    do:
      c
      |&gt; choice([
        string("if") |&gt; replace(:if),
        string("while") |&gt; replace(:while)
      ])
      |&gt; deny(id_char())
      |&gt; tag(:kw)

  # ignore things like actually joining this into a string
  def identifier(c \\ empty()),
    do:
      c
      |&gt; times(id_char(), min: 1)
      |&gt; tag(:id)
</code></pre>
<p>In fact, adding <code>deny</code> (and having <code>repeat</code> handle it properly) would allow <code>repeat_until</code> to be removed, since it could be written like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  def repeat_until(c \\ empty(), to_repeat, until),
    do:
      c
      |&gt; repeat(
        deny(until())
        |&gt; to_repeat()
      )
</code></pre>
<p>As for implementation, it seems to me reading through the compiler code that failed parsers are already specially handled in some cases, but not all (I’m referring to the <code>catch_all</code> piece of config which is passed around. I believe that for all parsers (bound and unbound), both <code>repeat</code> and <code>choice</code> should respect the failure or success of them. Implementing <code>require</code> and <code>deny</code> will require making a stack frame at the point they are invoked (since we need to be able to get back to the state of the binary, accumulator etc right before we called them), which is unavoidable, but we still retain great efficiency and tail recursion for the majority of the code (we only make extra stack frames in specific places, unlike a full backtracking parser, which needs to keep the full stack around).</p>
<p>Please let me know what you think about this—I’m pretty certain that what I described cannot currently be achieved generally with NimbleParsec, but if I misunderstood how it works somewhere, please let me know. I think implementing this feature would greatly aid the library on its way to be a powerful set of primitives with which fully-featured parsers can be built.</p>
<h2><a name="p-107590-side-note-failures-vs-errors-6" class="anchor" href="#p-107590-side-note-failures-vs-errors-6" aria-label="Heading link" rel="nofollow"></a>Side note: failures vs errors</h2>
<p>While not necessary for more parsing power, it would be useful to distinguish between failures (this parser failed, but it it’s in a <code>choice</code> or <code>repeat</code> then try the next choice / stop the repeat) and errors (something unrecoverable was seen, fail right away and show the user an error) in order to allow better error messages to be reported back to the users. Of course, this would need to come after all errors are actually handled consistently as proposed above, and is probably a separate discussion. <a href="https://arxiv.org/abs/1405.6646" rel="noopener nofollow ugc">This paper</a> gives a good overview of how some other parsers handle this and what advantages it has.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="107590" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/nimbleparsec-a-simple-and-fast-parser-combinator-for-elixir/12860/52">Post #51</a>
	                </div>
	            </div>
              <div id="likers-container-107590" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107590"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #51"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/12860/load_more?page=6">Load more posts (91 remaining)</a>
</div></template></turbo-stream>