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


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="mjadczak" data-post="61" 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/mjadczak/48/3537_2.png" class="avatar"> mjadczak:</div>
<blockquote>
<p>You have to supply a user-defined function (which needs to be a real <code>def</code> in the module) which takes the binary and has to return a success or error.</p>
</blockquote>
</aside>
<p>Huh, so you’d have to start the parser back up again, in another function, instead of just doing it in-position?  o.O?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="107615" 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/63">Post #62</a>
	                </div>
	            </div>
              <div id="likers-container-107615" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107615"
                     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 #62"></div>
  </section>
</div>
    <div class="postbit" id="107618" data-post-id="107618">
  <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">
								<aside class="quote no-group" data-username="mjadczak" data-post="56" 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/mjadczak/48/3537_2.png" class="avatar"> mjadczak:</div>
<blockquote>
<p>I do feel quite strongly that lookahead should be a fundamental combinator rather than a “call to user code” function</p>
</blockquote>
</aside>
<p>What would lookahead do in this case? Would it look if the combinator matches but without changing the accumulate state or the input and continue if so, otherwise it “denies”? And <code>lookahead_not</code> is the opposite? I think we can retire the current lookahead in favor of it. Especially as the current lookahead is really just a traverse.</p>
<aside class="quote no-group" data-username="mjadczak" data-post="56" 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/mjadczak/48/3537_2.png" class="avatar"> mjadczak:</div>
<blockquote>
<p>I also think that having this <code>:deny</code> form be a special case just for this lookahead usecase also hurts composability, which I understood was a core goal of the library</p>
</blockquote>
</aside>
<p>Indeed. The current patch would be the starting point, not the destination.</p>
<aside class="quote no-group" data-username="tmbb" data-post="58" data-topic="12860">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/77aa72/48.png" class="avatar"> tmbb:</div>
<blockquote>
<p>I very disagree very strongly with this idea. For a parser, it’s very important to decide ahead of time what it’s capabilities are. Adding features by accretion doesn’t seem like a good way to do this. (IMO) You shouldn’t add features one by one. You should pick a parsing style</p>
</blockquote>
</aside>
<p>I mean this is in the nicest way possible but don’t start this gate keeping non sense. <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"> I know there is a computer science background to all of this but catching up on it is not my priority as my CS reading time is usually focused elsewhere. I am doing it mostly for fun. If I had to do all of this research before, the idea would still be on paper, which would be a shame. It is open source, there are no warranties, warts and all.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="107618" 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/64">Post #63</a>
	                </div>
	            </div>
              <div id="likers-container-107618" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107618"
                     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 #63"></div>
  </section>
</div>
    <div class="postbit" id="107622" data-post-id="107622">
  <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">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="64" 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/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>What would lookahead do in this case? Would it look if the combinator matches but without changing the accumulate state or the input and continue if so, otherwise it “denies”? And <code>lookahead_not</code> is the opposite? I think we can retire the current lookahead in favor of it. Especially as the current lookahead is really just a traverse.</p>
</blockquote>
</aside>
<p>Yes, exactly that. In fact, <code>lookahead_not</code> is the more fundamental one, since you could write <code>def lookahead(c), do: c |&gt; lookahead_not() |&gt; lookahead_not() </code> (although when compiled naively, it would require an extra <code>case</code> statement to execute). And I agree that the current lookahead could be retired and easily implemented using <code>traverse</code> when needed.</p>
<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="64" 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/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>I know there is a computer science background to all of this but catching up on it is not my priority as my CS reading time is usually focused elsewhere.</p>
</blockquote>
</aside>
<p>I agree that it’s probably not a good idea to require an academic paper before you start writing some code for fun <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"> But, let me offer an alternative perspective—for fields such as parsing, where there are decades of research out there, sometimes it’s really helpful to be able to pick up some fundamental properties of the thing you’re working with. It can definitely save a lot of time! For me, I spent a few hours trying to get lookahead-style parsing to work with NimbleParsec, before I ended up realising that it was actually a PEG parser with one combinator missing, and then I could immediately prove that it wasn’t possible. Also, for a side-project like this, with no comprehensive documentation or tutorials, being able to say “this is a PEG parser” lets people access all the different resources written about this style of parser. FWIW, I think having a fast PEG parser is a great asset to Elixir, and I’m also pretty certain that once negative lookahead is properly supported, we can confidently assert that NimbleParsec is at least as powerful as a PEG parser <img src="https://forum.elixirforum.com/images/emoji/apple/partying_face.png?v=15" title=":partying_face:" class="emoji" alt=":partying_face:" 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="107622" 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/65">Post #64</a>
	                </div>
	            </div>
              <div id="likers-container-107622" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107622"
                     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 #64"></div>
  </section>
</div>
    <div class="postbit" id="107625" data-post-id="107625">
  <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">
								<aside class="quote no-group" data-username="mjadczak" data-post="65" 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/mjadczak/48/3537_2.png" class="avatar"> mjadczak:</div>
<blockquote>
<p>I agree that it’s probably not a good idea to require an academic paper before you start writing some code for fun <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"> But, let me offer an alternative perspective</p>
</blockquote>
</aside>
<p>I totally agree. My point is that because I did not start by wanting to write a complete parser, but rather something that can convert to pattern matching, it never crossed my mind to write a complete parser. And even though now I am aware it could be a complete parser, I don’t have the time to pour into studying what a complete parser is, so I either continue evolving it piece by piece with fantastic input like yours or I don’t evolve it at all.</p>
<p>I completely understand this is not ideal and I completely understand that by saying this some people may not use nimble_parsec as they would prefer a parsing library built on proper foundations. That’s fine by me because that was never my concern. <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>
<p>In any case, I have <code>lookahead</code> ready for <code>choice</code> and <code>lookahead_not</code> should be an invert of that, I will push soon, but I need to understand how it works with things like <code>traverse</code> and <code>repeat</code> before I consider it done.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="107625" data-batch-url="/posts/batch_likers">
                        2
                      </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/66">Post #65</a>
	                </div>
	            </div>
              <div id="likers-container-107625" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107625"
                     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 #65"></div>
  </section>
</div>
    <div class="postbit" id="107627" data-post-id="107627">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote group-livebook_core_team quote-modified" data-username="josevalim" data-post="66" 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/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>but I need to understand how it works with things like choice, traverse and so on before I consider it done.</p>
</blockquote>
</aside>
<p>What do you need to know?  I’ve worked on (both building and using) Full PEG parsers for nearing 20 years at this point.  ^.^</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="107627" 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/67">Post #66</a>
	                </div>
	            </div>
              <div id="likers-container-107627" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107627"
                     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 #66"></div>
  </section>
</div>
    <div class="postbit" id="107635" data-post-id="107635">
  <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 class="mention" href="/u/overminddl1" rel="nofollow">@OvermindDL1</a> <a class="mention" href="/u/mjadczak" rel="nofollow">@mjadczak</a> is the behaviour of my repeat correct?</p>
<p>Take the example below:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defparsecp :lookahead_not_with_repeat,
               repeat(ascii_char([]) |&gt; lookahead_not(ascii_char([?0..?9])))
</code></pre>
<p>I would expect it to return this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">assert lookahead_not_with_repeat("aa0") ==
               {:ok, 'a', "a0", %{}, {1, 0}, 1}
</code></pre>
<p>But it returns:</p>
<pre><code>{:ok, '', "aa0", %{}, {1, 0}, 1}
</code></pre>
<p>I.e. it is happy to parse nothing if it halts anywhere. However, if I use <code>times(min: 1)</code>, then it works as expected but then it always aborts at the minimum. So it seems they are not greedy.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="107635" 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/68">Post #67</a>
	                </div>
	            </div>
              <div id="likers-container-107635" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107635"
                     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 #67"></div>
  </section>
</div>
    <div class="postbit" id="107639" data-post-id="107639">
  <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 class="mention" href="/u/mjadczak" rel="nofollow">@mjadczak</a> here is a commit with lookahead and lookahead_not: <a href="https://github.com/plataformatec/nimble_parsec/commit/71663d1096bb9e556965fe7013cb2e462bf405c8" class="inline-onebox" rel="nofollow">Add lookahead and negative lookahead · dashbitco/nimble_parsec@71663d1 · GitHub</a></p>
<p>I will wait for your feedback then I will tidy it up with docs and what not.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="107639" 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/69">Post #68</a>
	                </div>
	            </div>
              <div id="likers-container-107639" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107639"
                     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 #68"></div>
  </section>
</div>
    <div class="postbit" id="107641" data-post-id="107641">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>PEG is absolutely greedy, it has no non-greedy constructs, you have to be explicit.</p>
<p>So let’s see:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">repeat(ascii_char([]) |&gt; lookahead_not(ascii_char([?0..?9])))
</code></pre>
<p>Which repeats just</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">ascii_char([]) |&gt; lookahead_not(ascii_char([?0..?9]))
</code></pre>
<p>Which will match <code>a</code>, <code>aa</code>, <code>aaaaa</code>, but not <code>a0</code>, thus the first loop matches out the <code>aa0</code> and extracts the first <code>a</code> and returns <code>97</code>/<code>?a</code>, leaving <code>a0</code>, it repeats again and fails to match <code>a0</code> thus ending, so repeat returns <code>[97]</code>/<code>[?a]</code>/<code>'a'</code>, thus the successful return value should return <code>[97]</code> with a leftover of <code>"a0"</code>, and this is indeed what ExSpirit does:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex(1)&gt; defmodule Testing do
...(1)&gt;   use ExSpirit.Parser, text: true
...(1)&gt;   defrule lookahead_not_with_repeat(repeat(char()|&gt;lookahead_not(char(?0..?9))))
...(1)&gt; end
{:module, Testing,
 &lt;&lt;70, 79, 82, 49, 0, 0, 111, 28, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 3, 5,
   0, 0, 0, 77, 14, 69, 108, 105, 120, 105, 114, 46, 84, 101, 115, 116, 105,
   110, 103, 8, 95, 95, 105, 110, 102, 111, 95, ...&gt;&gt;,
 {:lookahead_not_with_repeat, 1}}
iex(2)&gt; import ExSpirit.Parser
ExSpirit.Parser
iex(3)&gt; parse("aa0", Testing.lookahead_not_with_repeat)
%ExSpirit.Parser.Context{
  column: 2,
  error: nil,
  filename: "&lt;unknown&gt;",
  line: 1,
  position: 1,
  rest: "a0",
  result: 'a',
  rulestack: [],
  skipper: nil,
  state: %{},
  userdata: nil
}
</code></pre>
<p>In addition to other useful data like the matching position/column/line and so forth.  Making it able to parse out any length of chars that don’t contain numbers after the starting position could be enforced via an expectation as well:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">iex(15)&gt; defmodule Testing do
...(15)&gt;   use ExSpirit.Parser, text: true
...(15)&gt;   defrule lookahead_not_with_repeat(repeat(char()|&gt;expect(lookahead_not(char(?0..?9)))))
...(15)&gt; end
warning: redefining module Testing (current version defined in memory)
  iex:15

{:module, Testing,
 &lt;&lt;70, 79, 82, 49, 0, 0, 112, 12, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 3, 23,                                                       
   0, 0, 0, 78, 14, 69, 108, 105, 120, 105, 114, 46, 84, 101, 115, 116, 105,                                                          
   110, 103, 8, 95, 95, 105, 110, 102, 111, 95, ...&gt;&gt;,
 {:lookahead_not_with_repeat, 1}}
iex(16)&gt; import ExSpirit.Parser
ExSpirit.Parser
iex(17)&gt; parse("aaaaa", Testing.lookahead_not_with_repeat)
%ExSpirit.Parser.Context{
  column: 6,
  error: nil,
  filename: "&lt;unknown&gt;",
  line: 1,
  position: 5,
  rest: "",
  result: 'aaaaa',
  rulestack: [],
  skipper: nil,
  state: %{},
  userdata: nil
}
iex(18)&gt; parse("aa0", Testing.lookahead_not_with_repeat)
%ExSpirit.Parser.Context{
  column: 3,
  error: %ExSpirit.Parser.ExpectationFailureException{
    context: %ExSpirit.Parser.Context{
      column: 3,
      error: nil,
      filename: "&lt;unknown&gt;",
      line: 1,
      position: 2,
      rest: "0",
      result: 97,
      rulestack: [:lookahead_not_with_repeat],
      skipper: nil,
      state: %{},
      userdata: nil
    },
    extradata: %ExSpirit.Parser.Context{
      column: 4,
      error: nil,
      filename: "&lt;unknown&gt;",
      line: 1,
      position: 3,
      rest: "",
      result: 48,
      rulestack: [:lookahead_not_with_repeat],                                                                                        
      skipper: nil,
      state: %{},                                                                                                                     
      userdata: nil
    },
    message: "Lookahead_not failed"
  },
  filename: "&lt;unknown&gt;",
  line: 1,
  position: 2,
  rest: "0",
  result: nil,
  rulestack: [],
  skipper: nil,
  state: %{},
  userdata: nil
}
iex(19)&gt; parse("aa0aaa", Testing.lookahead_not_with_repeat)
%ExSpirit.Parser.Context{
  column: 3,
  error: %ExSpirit.Parser.ExpectationFailureException{
    context: %ExSpirit.Parser.Context{
      column: 3,
      error: nil,
      filename: "&lt;unknown&gt;",
      line: 1,
      position: 2,
      rest: "0aaa",
      result: 97,
      rulestack: [:lookahead_not_with_repeat],
      skipper: nil,
      state: %{},
      userdata: nil
    },
    extradata: %ExSpirit.Parser.Context{
      column: 4,
      error: nil,
      filename: "&lt;unknown&gt;",
      line: 1,
      position: 3,
      rest: "aaa",
      result: 48,
      rulestack: [:lookahead_not_with_repeat],
      skipper: nil,
      state: %{},
      userdata: nil
    },
    message: "Lookahead_not failed"
  },
  filename: "&lt;unknown&gt;",
  line: 1,
  position: 2,
  rest: "0aaa",
  result: nil,
  rulestack: [],
  skipper: nil,
  state: %{},
  userdata: nil
}
</code></pre>
<p>The reason I point out <code>except</code> is it is also a very standard PEG operator in PEG libraries that allows you to ‘early kill’ something like an <code>alt</code> or so, like say you are parsing <code>if</code>, well if you put the rest of the parsing after if in an <code>except(...)</code> then if it fails to parse inside then you know the code is bad, you don’t need to test other alternatives like identifiers and so forth or you will just end up getting an error in another unrelated place.  That should be added to NimbleParsec as well.  <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="107641" 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/70">Post #69</a>
	                </div>
	            </div>
              <div id="likers-container-107641" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107641"
                     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 #69"></div>
  </section>
</div>
    <div class="postbit" id="107649" data-post-id="107649">
  <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">
								<aside class="quote no-group" data-username="OvermindDL1" data-post="70" 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/overminddl1/48/2677_2.png" class="avatar"> OvermindDL1:</div>
<blockquote>
<p>Which will match <code>a</code> , <code>aa</code> , <code>aaaaa</code> , but not <code>a0</code> , thus the first loop matches out the <code>aa0</code> and extracts the first <code>a</code> and returns <code>97</code> / <code>?a</code> , leaving <code>a0</code> , it repeats again and fails to match <code>a0</code> thus ending, so repeat returns <code>[97]</code> / <code>[?a]</code> / <code>'a'</code> , thus the successful return value should return <code>[97]</code> with a leftover of <code>"a0"</code> , and this is indeed what ExSpirit does:</p>
</blockquote>
</aside>
<p>Perfect. NimbleParsec is behaving the same way now. <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 a lot!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="107649" 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/71">Post #70</a>
	                </div>
	            </div>
              <div id="likers-container-107649" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107649"
                     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 #70"></div>
  </section>
</div>
    <div class="postbit" id="107651" data-post-id="107651">
  <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">
								<aside class="quote group-livebook_core_team" data-username="josevalim" data-post="71" 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/josevalim/48/1787_2.png" class="avatar"> josevalim:</div>
<blockquote>
<p>NimbleParsec is behaving the same way now. <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>
</blockquote>
</aside>
<p>Nice! And yes, I agree with how <a class="mention" href="/u/overminddl1" rel="nofollow">@OvermindDL1</a> describes it.</p>
<p>I think something to keep in mind, which may be useful in working out how something “should” behave, is a standard model of whether a parser “fails” or “succeeds”. Once every single parser can be said to either “fail” or “succeed” given some input, potentially consuming some input if it succeeds, then it becomes clear—<code>repeat(p)</code> tries to let <code>p</code> consume as much input as possible, while it succeeds, and once it fails (necessarily not consuming any input), it goes to the next thing in the sequence. <code>choice([a, b, c])</code> tries to apply <code>a</code> to the input, and if it succeeds, proceeds to the next parser in the sequence. If <code>a</code> fails (not consuming input), then <code>b</code> is tried instead (and then <code>c</code>, and if all choices fail, the whole <code>choice</code> combinator fails).</p>
<p><em>N.B. Typically these things are expressed in terms of “consuming input”, but in NimbleParsec/Elixir terms “input consumed” == “the next combinator in the chain is called with a binary which no longer includes the consumed input”.</em></p>
<p>Once you have a unified way in which all parsers either succeed or fail, you can then e.g. express <code>repeat_until</code> like I said: <code>repeat(lookahead_not(until) |&gt; to_repeat())</code>. The inner parser <code>p</code> in this case is <code>lookahead_not(until) |&gt; to_repeat()</code> (where we’ve assumed that <code>to_repeat()</code> will consume some input, like a string or whatever). The outer <code>repeat</code> passes its current input to this <code>p</code>. The <code>lookahead_not</code> checks whether the immediate input (i.e. whatever’s at the start of the current binary) matches <code>until</code>. Let’s say at first it doesn’t. So <code>lookahead_not</code> succeeds, but does not consume any input—it lets <code>to_repeat()</code> consume it instead. Now the binary has moved along, and <code>repeat</code> gives it to <code>p</code> again. <code>lookahead_not</code> passes this input to <code>until</code>, and it matches! <code>lookahead_not</code> discards the results (accumulator) and also pretends the input was never consumed, but fails. Now <code>repeat</code> knows to succeed (it always succeeds, because it’s a 0 or more repetition) instead of to keep repeating.</p>
<p>(I hope I got the above all right, <a class="mention" href="/u/overminddl1" rel="nofollow">@OvermindDL1</a> please correct me otherwise)</p>
<p>Now, obviously if you actually implemented it like this, you would no longer have a super-efficient pattern-matching parser, but just yet-another-PEG parser which needs to interpret all of this information at runtime. Hence, I’m not necessarily saying that each parser/combinator should actually explicitly return an <code>:ok</code> or <code>:error</code> which is matched on. Rather, I’m saying that this is a good mental model of what is happening, and should help to answer questions like “how should this combination of parsers behave”?</p>
<p>Regardless, the commit looks good, especially now you’ve got the behaviour with <code>repeat</code> worked out. I’m looking forward to trying it out! I’m currently tinkering with trying to implement a full-featured (spec-compliant) XML parser using NimbleParsec <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="107651" 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/72">Post #71</a>
	                </div>
	            </div>
              <div id="likers-container-107651" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="107651"
                     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 #71"></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=8">Load more posts (71 remaining)</a>
</div></template></turbo-stream>