Gray Soft / My Projects / Ghost Wheel Exampletag:graysoftinc.com,2014-03-20:/posts/412014-04-05T14:56:38ZJames Edward Gray IIThe 7th Comment on "Ghost Wheel Example"tag:graysoftinc.com,2009-01-16:/comments/2562014-04-05T14:56:38ZThanks a lot!<p>Thanks a lot!</p>Robert DoberThe 6th Comment on "Ghost Wheel Example"tag:graysoftinc.com,2009-01-03:/comments/2492014-04-05T14:56:38Z[Done](http://ghostwheel.rubyforge.org/svn/trunk/README). I know it's not much yet, but it at least guides you to two full examples.<p><a href="http://ghostwheel.rubyforge.org/svn/trunk/README">Done</a>. I know it's not much yet, but it at least guides you to two full examples.</p>James Edward Gray IIThe 5th Comment on "Ghost Wheel Example"tag:graysoftinc.com,2009-01-01:/comments/2482014-04-05T14:56:38ZJames
I had some troubles of finding a starting point for what seems a hack of an interesting project. Actually it would be *very* helpful if you could just added this example into a README of the ghost_wheel package and or on the Rubyforge Proje...<p>James<br>
I had some troubles of finding a starting point for what seems a hack of an interesting project. Actually it would be <em>very</em> helpful if you could just added this example into a README of the ghost_wheel package and or on the Rubyforge Project Homepage.<br>
Thanx<br>
Robert</p>Robert DoberThe 4th Comment on "Ghost Wheel Example"tag:graysoftinc.com,2008-01-17:/comments/1712014-04-05T14:55:38ZIt was intentional, yes. Ghost Wheel is a parser generator designed to build on your existing regular expression knowledge, so the related names seemed like a good fit to me.<p>It was intentional, yes. Ghost Wheel is a parser generator designed to build on your existing regular expression knowledge, so the related names seemed like a good fit to me.</p>James Edward Gray IIThe 3rd Comment on "Ghost Wheel Example"tag:graysoftinc.com,2008-01-17:/comments/1702014-04-05T14:55:38ZI wonder why you chose the name "Ghost Wheel" as Ruby 1.9's Regexp engine has the same name (in Japanese). Seems like it could cause potential confusion. Was it intentional?<p>I wonder why you chose the name "Ghost Wheel" as Ruby 1.9's Regexp engine has the same name (in Japanese). Seems like it could cause potential confusion. Was it intentional?</p>PhilThe 2nd Comment on "Ghost Wheel Example"tag:graysoftinc.com,2007-11-30:/comments/1432014-04-05T14:54:49ZI'm sure you can put something together as long as you are willing to spend the effort, yes. Here's a trivial example that passes your tests, just to get you started:
```ruby
#!/usr/bin/env ruby -wKU
require "rubygems"
require "ghost_whee...<p>I'm sure you can put something together as long as you are willing to spend the effort, yes. Here's a trivial example that passes your tests, just to get you started:</p>
<div class="highlight highlight-ruby"><pre><span class="c1">#!/usr/bin/env ruby -wKU</span>
<span class="nb">require</span> <span class="s2">"rubygems"</span>
<span class="nb">require</span> <span class="s2">"ghost_wheel"</span>
<span class="no">English</span> <span class="o">=</span> <span class="no">GhostWheel</span><span class="o">.</span><span class="n">build_parser</span> <span class="sx">%q{</span>
<span class="sx"> space = /\s+/</span>
<span class="sx"> noun = 'Bob' | 'John' | 'work' | 'home'</span>
<span class="sx"> nouns = noun space 'and' space noun | noun</span>
<span class="sx"> verb = 'go' | 'come'</span>
<span class="sx"> verb_plural = 'goes' | 'comes'</span>
<span class="sx"> preposition = 'to' space</span>
<span class="sx"> statement = noun space verb_plural space preposition? noun</span>
<span class="sx"> | nouns space verb space preposition? noun</span>
<span class="sx"> sentence := statement EOF</span>
<span class="sx">}</span>
<span class="k">if</span> <span class="bp">__FILE__</span> <span class="o">==</span> <span class="vg">$PROGRAM_NAME</span>
<span class="nb">require</span> <span class="s2">"test/unit"</span>
<span class="k">class</span> <span class="nc">TestEnglishGrammar</span> <span class="o"><</span> <span class="no">Test</span><span class="o">::</span><span class="no">Unit</span><span class="o">::</span><span class="no">TestCase</span>
<span class="k">def</span> <span class="nf">test_good_grammar</span>
<span class="n">assert_parses</span> <span class="s2">"Bob and John go to work"</span>
<span class="n">assert_parses</span> <span class="s2">"John goes to work"</span>
<span class="n">assert_parses</span> <span class="s2">"Bob goes home"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">test_bad_grammar</span>
<span class="n">assert_does_not_parse</span> <span class="s2">"Bob and John goes to work"</span>
<span class="n">assert_does_not_parse</span> <span class="s2">"John goes to"</span>
<span class="n">assert_does_not_parse</span> <span class="s2">"Bob John work"</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">assert_parses</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="n">assert_nothing_raised</span><span class="p">(</span><span class="no">GhostWheel</span><span class="o">::</span><span class="no">FailedParseError</span><span class="p">)</span> <span class="k">do</span>
<span class="no">English</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">assert_does_not_parse</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="n">assert_raises</span><span class="p">(</span><span class="no">GhostWheel</span><span class="o">::</span><span class="no">FailedParseError</span><span class="p">)</span> <span class="p">{</span> <span class="no">English</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">input</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>James Edward Gray IIThe 1st Comment on "Ghost Wheel Example"tag:graysoftinc.com,2007-11-29:/comments/1422014-04-05T14:54:49ZJames,
Could this parser be used to check a very limited English vocabulary given a full superset of nouns, verbs and prepositions?
For illustration, I'd greatly appreciate if you could show how this might be used to generate a parser which ...<p>James,</p>
<p>Could this parser be used to check a very limited English vocabulary given a full superset of nouns, verbs and prepositions?</p>
<p>For illustration, I'd greatly appreciate if you could show how this might be used to generate a parser which verifies that</p>
<p>"Bob and John go to work" is accepted and so are "John goes to work" and "Bob goes to home" but the following are rejected: "Bob and John goes to work", "John go to work", "John goes to", "Bob John work".</p>
<p>The superset being-</p>
<p>[Prepositions: to, and], [Nouns: Bob, John, work, home], [Verbs: go, come, goes, comes]</p>
<p>I'm not expecting semantic/colloquial correctness so "Bob and work go to John" is acceptable and "Bob goes to home" is acceptable in place of "Bob goes home". (Of course it would be a plus to have that too.)</p>
<p>My question is motivated by the fact that many DSLs have a very limited vocabulary.</p>
<p>Thanks in advance</p>VishGhost Wheel Exampletag:graysoftinc.com,2007-11-18:/posts/412014-04-05T14:56:38ZShowing the Ghost Wheel approach to the recently popular Treetop example.<p>There has been a fair bit of buzz around the <a href="https://github.com/nathansobo/treetop">Treetop parser</a> in the Ruby community lately. Part of that is fueled by the nice <a href="http://www.pivotalblabs.com/files/treetop-arithmetic-example.mov">screencast</a> that shows off how to use the parser generator.</p>
<p>It doesn't get talked about as much, but I wrote a parser generator too, called <a href="http://rubygems.org/gems/ghostwheel">Ghost Wheel</a>. Probably the main reason Ghost Wheel doesn't receive much attention yet is that I have been slow in getting the documentation written. Given that, I thought I would show how the code built in the Treetop screencast translates to Ghost Wheel:</p>
<div class="highlight highlight-ruby"><pre><span class="c1">#!/usr/bin/env ruby -wKU</span>
<span class="nb">require</span> <span class="s2">"rubygems"</span>
<span class="nb">require</span> <span class="s2">"ghost_wheel"</span>
<span class="c1"># define a parser using Ghost Wheel's Ruby DSL</span>
<span class="no">RubyParser</span> <span class="o">=</span> <span class="no">GhostWheel</span><span class="o">.</span><span class="n">build_parser</span> <span class="k">do</span>
<span class="n">rule</span><span class="p">(</span> <span class="ss">:additive</span><span class="p">,</span>
<span class="n">alt</span><span class="p">(</span> <span class="n">seq</span><span class="p">(</span> <span class="ss">:multiplicative</span><span class="p">,</span>
<span class="ss">:space</span><span class="p">,</span>
<span class="ss">:additive_op</span><span class="p">,</span>
<span class="ss">:space</span><span class="p">,</span>
<span class="ss">:additive</span> <span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">add</span><span class="o">|</span> <span class="n">add</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">send</span><span class="p">(</span><span class="n">add</span><span class="o">[</span><span class="mi">2</span><span class="o">]</span><span class="p">,</span> <span class="n">add</span><span class="o">[-</span><span class="mi">1</span><span class="o">]</span><span class="p">)},</span>
<span class="ss">:multiplicative</span> <span class="p">)</span> <span class="p">)</span>
<span class="n">rule</span><span class="p">(</span><span class="ss">:additive_op</span><span class="p">,</span> <span class="n">alt</span><span class="p">(</span><span class="s2">"+"</span><span class="p">,</span> <span class="s2">"-"</span><span class="p">))</span>
<span class="n">rule</span><span class="p">(</span> <span class="ss">:multiplicative</span><span class="p">,</span>
<span class="n">alt</span><span class="p">(</span> <span class="n">seq</span><span class="p">(</span> <span class="ss">:primary</span><span class="p">,</span>
<span class="ss">:space</span><span class="p">,</span>
<span class="ss">:multiplicative_op</span><span class="p">,</span>
<span class="ss">:space</span><span class="p">,</span>
<span class="ss">:multiplicative</span> <span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">mul</span><span class="o">|</span> <span class="n">mul</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="n">send</span><span class="p">(</span><span class="n">mul</span><span class="o">[</span><span class="mi">2</span><span class="o">]</span><span class="p">,</span> <span class="n">mul</span><span class="o">[-</span><span class="mi">1</span><span class="o">]</span><span class="p">)},</span>
<span class="ss">:primary</span> <span class="p">)</span> <span class="p">)</span>
<span class="n">rule</span><span class="p">(</span><span class="ss">:multiplicative_op</span><span class="p">,</span> <span class="n">alt</span><span class="p">(</span><span class="s2">"*"</span><span class="p">,</span> <span class="s2">"/"</span><span class="p">))</span>
<span class="n">rule</span><span class="p">(</span><span class="ss">:primary</span><span class="p">,</span> <span class="n">alt</span><span class="p">(</span><span class="ss">:parenthized_additive</span><span class="p">,</span> <span class="ss">:number</span><span class="p">))</span>
<span class="n">rule</span><span class="p">(</span> <span class="ss">:parenthized_additive</span><span class="p">,</span>
<span class="n">seq</span><span class="p">(</span><span class="s2">"("</span><span class="p">,</span> <span class="ss">:space</span><span class="p">,</span> <span class="ss">:additive</span><span class="p">,</span> <span class="ss">:space</span><span class="p">,</span> <span class="s2">")"</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">par</span><span class="o">|</span> <span class="n">par</span><span class="o">[</span><span class="mi">2</span><span class="o">]</span> <span class="p">}</span> <span class="p">)</span>
<span class="n">rule</span><span class="p">(</span><span class="ss">:number</span><span class="p">,</span> <span class="sr">/[1-9][0-9]*|0/</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span> <span class="nb">Integer</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">}</span>
<span class="n">rule</span><span class="p">(</span><span class="ss">:space</span><span class="p">,</span> <span class="sr">/\s*/</span><span class="p">)</span>
<span class="n">parser</span><span class="p">(</span><span class="ss">:exp</span><span class="p">,</span> <span class="n">seq</span><span class="p">(</span><span class="ss">:additive</span><span class="p">,</span> <span class="n">eof</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">e</span><span class="o">|</span> <span class="n">e</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="p">})</span>
<span class="k">end</span>
<span class="c1"># define a parser using Ghost Wheel's grammar syntax</span>
<span class="no">GrammarParser</span> <span class="o">=</span> <span class="no">GhostWheel</span><span class="o">.</span><span class="n">build_parser</span> <span class="sx">%q{</span>
<span class="sx"> additive = multiplicative space additive_op space additive</span>
<span class="sx"> { ast[0].send(ast[2], ast[-1]) }</span>
<span class="sx"> | multiplicative</span>
<span class="sx"> additive_op = "+" | "-"</span>
<span class="sx"> multiplicative = primary space multiplicative_op space multiplicative</span>
<span class="sx"> { ast[0].send(ast[2], ast[-1])}</span>
<span class="sx"> | primary</span>
<span class="sx"> multiplicative_op = "*" | "/"</span>
<span class="sx"> primary = parenthized_additive | number</span>
<span class="sx"> parenthized_additive = "(" space additive space ")" { ast[2] }</span>
<span class="sx"> number = /[1-9][0-9]*|0/ { Integer(ast) }</span>
<span class="sx"> space = /\s*/</span>
<span class="sx"> exp := additive EOF { ast[0] }</span>
<span class="sx">}</span>
<span class="k">if</span> <span class="bp">__FILE__</span> <span class="o">==</span> <span class="vg">$PROGRAM_NAME</span>
<span class="nb">require</span> <span class="s2">"test/unit"</span>
<span class="k">class</span> <span class="nc">TestArithmetic</span> <span class="o"><</span> <span class="no">Test</span><span class="o">::</span><span class="no">Unit</span><span class="o">::</span><span class="no">TestCase</span>
<span class="k">def</span> <span class="nf">test_paring_numbers</span>
<span class="n">assert_parses</span> <span class="s2">"0"</span>
<span class="n">assert_parses</span> <span class="s2">"1"</span>
<span class="n">assert_parses</span> <span class="s2">"123"</span>
<span class="n">assert_does_not_parse</span> <span class="s2">"01"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">test_parsing_multiplicative</span>
<span class="n">assert_parses</span> <span class="s2">"1*2"</span>
<span class="n">assert_parses</span> <span class="s2">"1 * 2"</span>
<span class="n">assert_parses</span> <span class="s2">"1/2"</span>
<span class="n">assert_parses</span> <span class="s2">"1 / 2"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">test_parsing_additive</span>
<span class="n">assert_parses</span> <span class="s2">"1+2"</span>
<span class="n">assert_parses</span> <span class="s2">"1 + 2"</span>
<span class="n">assert_parses</span> <span class="s2">"1-2"</span>
<span class="n">assert_parses</span> <span class="s2">"1 - 2"</span>
<span class="n">assert_parses</span> <span class="s2">"1*2 + 3 * 4"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">test_parsing_parenthized_expressions</span>
<span class="n">assert_parses</span> <span class="s2">"1 * (2 + 3) * 4"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">test_parse_results</span>
<span class="n">assert_correct_result</span> <span class="s2">"0"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1"</span>
<span class="n">assert_correct_result</span> <span class="s2">"123"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1*2"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1 * 2"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1/2"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1 / 2"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1+2"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1 + 2"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1-2"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1 - 2"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1*2 + 3 * 4"</span>
<span class="n">assert_correct_result</span> <span class="s2">"1 * (2 + 3) * 4"</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="no">PARSERS</span> <span class="o">=</span> <span class="o">[</span><span class="no">RubyParser</span><span class="p">,</span> <span class="no">GrammarParser</span><span class="o">]</span>
<span class="k">def</span> <span class="nf">assert_parses</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="no">PARSERS</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">parser</span><span class="o">|</span>
<span class="n">assert_nothing_raised</span><span class="p">(</span><span class="no">GhostWheel</span><span class="o">::</span><span class="no">FailedParseError</span><span class="p">)</span> <span class="k">do</span>
<span class="n">parser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">assert_does_not_parse</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="no">PARSERS</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">parser</span><span class="o">|</span>
<span class="n">assert_raises</span><span class="p">(</span><span class="no">GhostWheel</span><span class="o">::</span><span class="no">FailedParseError</span><span class="p">)</span> <span class="p">{</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">input</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">assert_correct_result</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
<span class="no">PARSERS</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">parser</span><span class="o">|</span> <span class="n">assert_equal</span><span class="p">(</span><span class="nb">eval</span><span class="p">(</span><span class="n">input</span><span class="p">),</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">input</span><span class="p">))</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></div>
<p>The primary differences you should note from the above code and Treetop are:</p>
<ul>
<li>I show two different ways to build the parser using Ghost Wheel: using a Ruby DSL and using a grammar syntax. I prefer the grammar syntax in this and, in fact, most cases. The Ruby DSL can be handy when you want the AST transformations to be true closures though.</li>
<li>Ghost Wheel builds on your regular expression knowledge. Note that the grammar syntax is regex-like and you can even match <code>Regexp</code> literals.</li>
<li>Ghost Wheel's AST transformations are more Lispish compared to Treetop's very object oriented syntax. I think they both have strengths in certain scenarios.</li>
</ul><p>There's still plenty I want to do with Ghost Wheel, but maybe this will begin the process of getting the word out about it. Feel free to post questions here.</p>James Edward Gray II