Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

+ Tricks reformatted

  • Loading branch information...
commit b8b4fa72e973723963358e8e701808fb0e7f34bf 1 parent 98a3286
@kschiess authored
View
3  website/Gemfile
@@ -8,4 +8,5 @@ gem 'cod'
gem 'case'
gem 'text-highlight'
-gem 'parslet'
+gem 'parslet'
+gem 'rspec'
View
10 website/Gemfile.lock
@@ -19,6 +19,7 @@ GEM
fssm (>= 0.2.7)
sass (~> 3.1)
daemons (1.1.8)
+ diff-lcs (1.1.3)
em-websocket (0.3.6)
addressable (>= 2.1.1)
eventmachine (>= 0.12.9)
@@ -80,6 +81,14 @@ GEM
rack-test (0.6.1)
rack (>= 1.0)
rb-fsevent (0.9.1)
+ rspec (2.10.0)
+ rspec-core (~> 2.10.0)
+ rspec-expectations (~> 2.10.0)
+ rspec-mocks (~> 2.10.0)
+ rspec-core (2.10.1)
+ rspec-expectations (2.10.0)
+ diff-lcs (~> 1.1.3)
+ rspec-mocks (2.10.1)
sass (3.1.15)
sinatra (1.3.2)
rack (~> 1.3, >= 1.3.6)
@@ -117,4 +126,5 @@ DEPENDENCIES
middleman
parslet
rb-fsevent
+ rspec
text-highlight
View
189 website/source/tricks.html.textile
@@ -6,9 +6,9 @@ h2. Matching EOF (End Of File)
Ahh Sir, you'll be needin what us parsers call _epsilon_:
-{% highlight ruby %}
-rule(:eof) { any.absent? }
-{% endhighlight %}
+<pre class="sh_ruby"><code>
+ rule(:eof) { any.absent? }
+</code></pre>
Of course, most of us don't use this at all, since any parser has EOF as
implicit last input.
@@ -18,17 +18,17 @@ h2. Matching Strings Case Insensitive
Parslet is fully hackable: You can use code to create parsers easily. Here's
how I would match a string in case insensitive manner:
-{% highlight ruby %}
-def stri(str)
- key_chars = str.split(//)
- key_chars.
- collect! { |char| match["#{char.upcase}#{char.downcase}"] }.
- reduce(:>>)
-end
+<pre class="sh_ruby"><code title="case insensitive match">
+ def stri(str)
+ key_chars = str.split(//)
+ key_chars.
+ collect! { |char| match["#{char.upcase}#{char.downcase}"] }.
+ reduce(:>>)
+ end
-# Constructs a parser using a Parser Expression Grammar
-stri('keyword').parse "kEyWoRd" # => "kEyWoRd"
-{% endhighlight %}
+ # Constructs a parser using a Parser Expression Grammar
+ stri('keyword').parse "kEyWoRd" # => "kEyWoRd"@0
+</code></pre>
h2. Testing
@@ -36,16 +36,16 @@ Parslet helps you to create parsers that are in turn created out of many small
parsers. It is really turtles all the way down. Imagine you have a complex
parser:
-{% highlight ruby %}
-class ComplexParser < Parslet::Parser
- root :lots_of_stuff
+<pre class="sh_ruby"><code>
+ class ComplexParser < Parslet::Parser
+ root :lots_of_stuff
- rule(:lots_of_stuff) { ... }
+ rule(:lots_of_stuff) { ... }
- # and many lines later:
- rule(:simple_rule) { str('a') }
-end
-{% endhighlight %}
+ # and many lines later:
+ rule(:simple_rule) { str('a') }
+ end
+</code></pre>
Also imagine that the parser (as a whole) fails to consume the 'a' that
<code>simple_rule</code> is talking about.
@@ -59,18 +59,36 @@ problems. Either:
I find it very useful in this situation to eliminate 2. from our options:
-{% highlight ruby %}
-require 'parslet/rig/rspec'
+<pre class="sh_ruby"><code title="rspec">
+ require 'rspec'
+ require 'parslet/rig/rspec'
+
+ class ComplexParser < Parslet::Parser
+ rule(:simple_rule) { str('a') }
+ end
-describe ComplexParser do
- let(:parser) { ComplexParser.new }
- context "simple_rule" do
- it "should consume 'a'" do
- parser.simple_rule.should parse('a')
- end
+ describe ComplexParser do
+ let(:parser) { ComplexParser.new }
+ context "simple_rule" do
+ it "should consume 'a'" do
+ parser.simple_rule.should parse('a')
+ end
+ end
end
-end
-{% endhighlight %}
+
+ RSpec::Core::Runner.run([])
+</code></pre>
+
+Output is:
+<pre class="output">
+
+Example::ComplexParser
+ simple_rule
+ should consume 'a'
+
+Finished in 0.00115 seconds
+1 example, 0 failures
+</pre>
Parslet parsers have one method per rule. These methods return valid parsers
for a subset of your grammar.
@@ -80,27 +98,20 @@ h2. Error reports
If your grammar fails and you're aching to know why, here's a bit of exception
handling code that will help you out:
-{% highlight ruby %}
-begin
- parser.parse(some_input)
-rescue Parslet::ParseFailed => error
- puts error, parser.root.error_tree
-end
-{% endhighlight %}
+<pre class="sh_ruby"><code title="exception handling">
+ parser = str('foo')
+ begin
+ parser.parse('bar')
+ rescue Parslet::ParseFailed => error
+ puts error.cause.ascii_tree
+ end
+</code></pre>
This should print something akin to:
-{% highlight text %}
-Parsing 1++2: Don't know what to do with ++2 at line 1 char 2.
-`- Unknown error in SUM / INTEGER
- |- Failed to match sequence (INTEGER OPERATOR EXPRESSION) at line 1 char 3.
- | `- Unknown error in [0-9]{1, } SPACE?
- | `- Expected at least 1 of \\s at line 1 char 2.
- | `- Failed to match \\s at line 1 char 3.
- `- Unknown error in [0-9]{1, } SPACE?
- `- Expected at least 1 of \\s at line 1 char 2.
- `- Failed to match \\s at line 1 char 3.
-{% endhighlight %}
+<pre class="output">
+Expected "foo", but got "bar" at line 1 char 1.
+</pre>
These error reports are probably the fastest way to know exactly where you
went wrong (or where your input is wrong, which is aequivalent).
@@ -108,10 +119,76 @@ went wrong (or where your input is wrong, which is aequivalent).
And since this is such a common idiom, we provide you with a shortcut: to
get the above, just:
-{% highlight ruby %}
+<pre class="sh_ruby"><code>
require 'parslet/convenience'
parser.parse_with_debug(input)
-{% endhighlight %}
+</code></pre>
+
+
+h3. Reporter engines
+
+Note that there is currently not one, but two error reporting engines! The
+default engine will report errors in a structure that looks exactly like the
+grammar structure:
+
+<pre class="sh_ruby"><code title="error reporter 1">
+ class P < Parslet::Parser
+ root(:body)
+ rule(:body) { elements }
+ rule(:elements) { (call | element).repeat(2) }
+ rule(:element) { str('bar') }
+ rule(:call) { str('baz') >> str('()') }
+ end
+
+ begin
+ P.new.parse('barbaz')
+ rescue Parslet::ParseFailed => error
+ puts error.cause.ascii_tree
+ end
+</code></pre>
+
+Outputs:
+
+<pre class="output">
+Expected at least 2 of CALL / ELEMENT at line 1 char 1.
+`- Expected one of [CALL, ELEMENT] at line 1 char 4.
+ |- Failed to match sequence ('baz' '()') at line 1 char 7.
+ | `- Premature end of input at line 1 char 7.
+ `- Expected "bar", but got "baz" at line 1 char 4.
+</pre>
+
+Let's switch out the 'grammar structure' engine (called '<code>Tree</code>')
+with the 'deepest error position' engine:
+
+<pre class="sh_ruby"><code title="error reporter 2">
+ class P < Parslet::Parser
+ root(:body)
+ rule(:body) { elements }
+ rule(:elements) { (call | element).repeat(2) }
+ rule(:element) { str('bar') }
+ rule(:call) { str('baz') >> str('()') }
+ end
+
+ begin
+ P.new.parse('barbaz', reporter: Parslet::ErrorReporter::Deepest.new)
+ rescue Parslet::ParseFailed => error
+ puts error.cause.ascii_tree
+ end
+</code></pre>
+
+Outputs:
+
+<pre class="output">
+Expected at least 2 of CALL / ELEMENT at line 1 char 1.
+`- Expected one of [CALL, ELEMENT] at line 1 char 4.
+ |- Failed to match sequence ('baz' '()') at line 1 char 7.
+ | `- Premature end of input at line 1 char 7.
+ `- Premature end of input at line 1 char 7.
+</pre>
+
+The <code>'Deepest'</code> position engine will store errors that are the
+farthest into the input. In some examples, this produces more readable output
+for the end user.
h2. Line numbers from parser output
@@ -125,10 +202,10 @@ Parslet gives you slices (Parslet::Slice) of input as part of your tree. These
are essentially strings with line numbers. Here's how to print that error
message:
-{% highlight ruby %}
-# assume that type == "int"@0 - a piece from your parser output
-line, col = type.line_and_column
-puts "Sorry. Can't have #{type} at #{line}:#{col}!"
-{% endhighlight %}
+<pre class="sh_ruby"><code>
+ # assume that type == "int"@0 - a piece from your parser output
+ line, col = type.line_and_column
+ puts "Sorry. Can't have #{type} at #{line}:#{col}!"
+</code></pre>
Please sign in to comment.
Something went wrong with that request. Please try again.