treetop 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/Rakefile +1 -1
  2. data/doc/contributing_and_planned_features.markdown +3 -11
  3. data/doc/index.markdown +65 -4
  4. data/doc/semantic_interpretation.markdown +3 -1
  5. data/doc/site.rb +79 -10
  6. data/doc/site/contribute.html +118 -0
  7. data/doc/{images/middle_backgound.png → site/images/bottom_background.png} +0 -0
  8. data/doc/{images → site/images}/middle_background.png +0 -0
  9. data/doc/{images → site/images}/paren_language_output.png +0 -0
  10. data/doc/site/images/pivotal.gif +0 -0
  11. data/doc/site/images/top_background.png +0 -0
  12. data/doc/site/index.html +102 -0
  13. data/doc/site/pitfalls_and_advanced_techniques.html +68 -0
  14. data/doc/site/screen.css +129 -0
  15. data/doc/site/semantic_interpretation.html +214 -0
  16. data/doc/site/syntactic_recognition.html +142 -0
  17. data/doc/site/using_in_ruby.html +34 -0
  18. data/doc/sitegen.rb +60 -0
  19. data/doc/syntactic_recognition.markdown +11 -14
  20. data/doc/using_in_ruby.markdown +7 -3
  21. data/lib/treetop/compiler/metagrammar.rb +2 -2
  22. data/lib/treetop/compiler/metagrammar.treetop +3 -3
  23. data/lib/treetop/compiler/node_classes.rb +1 -0
  24. data/lib/treetop/compiler/node_classes/character_class.rb +5 -1
  25. data/lib/treetop/compiler/node_classes/predicate.rb +1 -1
  26. data/lib/treetop/compiler/node_classes/transient_prefix.rb +9 -0
  27. data/lib/treetop/runtime.rb +2 -1
  28. data/lib/treetop/runtime/interval_skip_list.rb +4 -0
  29. data/lib/treetop/runtime/interval_skip_list/head_node.rb +15 -0
  30. data/lib/treetop/runtime/interval_skip_list/interval_skip_list.rb +200 -0
  31. data/lib/treetop/runtime/interval_skip_list/node.rb +164 -0
  32. data/lib/treetop/runtime/syntax_node.rb +40 -40
  33. metadata +23 -10
  34. data/doc/images/bottom_background.png +0 -0
  35. data/doc/images/top_background.png +0 -0
  36. data/doc/screen.css +0 -52
  37. data/doc/site.html +0 -34
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ end
15
15
 
16
16
  gemspec = Gem::Specification.new do |s|
17
17
  s.name = "treetop"
18
- s.version = "1.2.0"
18
+ s.version = "1.2.1"
19
19
  s.author = "Nathan Sobo"
20
20
  s.email = "nathansobo@gmail.com"
21
21
  s.homepage = "http://functionalform.blogspot.com"
@@ -1,31 +1,23 @@
1
1
  #Contributing
2
2
  I like to try Rubinius's policy regarding commit rights. If you submit one patch worth integrating, I'll give you commit rights. We'll see how this goes, but I think it's a good policy.
3
3
 
4
+ The source code is currently stored in a git repository at <a href="http://repo.or.cz/w/treetop.git">http://repo.or.cz/w/treetop.git</a>
5
+
4
6
  ##Getting Started with the Code
5
7
  Treetop compiler is interesting in that it is implemented in itself. Its functionality revolves around `metagrammar.treetop`, which specifies the grammar for Treetop grammars. I took a hybrid approach with regard to definition of methods on syntax nodes in the metagrammar. Methods that are more syntactic in nature, like those that provide access to elements of the syntax tree, are often defined inline, directly in the grammar. More semantic methods are defined in custom node classes.
6
8
 
7
- Iterating on the metagrammar is tricky. The current testing strategy uses the last stable version of the metagrammar to parse the version under test. Then the version under test is used to parse and functionally test the various pieces of syntax it should recognize and translate to Ruby. As you change `metagrammar.treetop` and its associated node classes, note that the node classes you are changing are also used to support the previous stable version of the metagrammar, so must be kept backward compatible until such time as a new stable version can be produced to replace it. This became an issue fairly recently when I closed the loop on the bootstrap. Serious iteration on the metagrammar will probably necessitate a more robust testing strategy, perhaps one that relies on the Treetop gem for compiling the metagrammar under test. I haven't done this because my changes since closing the metacircular loop have been minor enough to deal with the issue, but let me know if you need help on this front.
9
+ Iterating on the metagrammar is tricky. The current testing strategy uses the last stable version of Treetop to parse the version under test. Then the version under test is used to parse and functionally test the various pieces of syntax it should recognize and translate to Ruby. As you change `metagrammar.treetop` and its associated node classes, note that the node classes you are changing are also used to support the previous stable version of the metagrammar, so must be kept backward compatible until such time as a new stable version can be produced to replace it.
8
10
 
9
11
  ##Tests
10
12
  Most of the compiler's tests are functional in nature. The grammar under test is used to parse and compile piece of sample code. Then I attempt to parse input with the compiled output and test its results.
11
13
 
12
- Due to shortcomings in Ruby's semantics that scope constant definitions in a block's lexical environment rather than the environment in which it is module evaluated, I was unable to use Rspec without polluting a global namespace with const definitions. Rspec has recently improved to allow specs to reside within standard Ruby classes, but I have not yet migrated the tests back. Instead, they are built on a modified version of Test::Unit that allows tests to be defined as strings. It's not ideal but it worked at the time.
13
-
14
14
  #What Needs to be Done
15
15
  ##Small Stuff
16
- * Migrate the tests back to RSpec.
17
16
  * Improve the `tt` command line tool to allow `.treetop` extensions to be elided in its arguments.
18
17
  * Generate and load temp files with `Treetop.load` rather than evaluating strings to improve stack trace readability.
19
18
  * Allow `do/end` style blocks as well as curly brace blocks. This was originally omitted because I thought it would be confusing. It probably isn't.
20
- * Allow the root of a grammar to be dynamically set for testing purposes.
21
19
 
22
20
  ##Big Stuff
23
- ###Avoiding Excessive Object Instantiation
24
- Based on some preliminary profiling work, it is pretty apparent that a large percentage of a typical parse's time is spent instantiating objects. This needs to be avoided if parsing is to be more performant.
25
-
26
- ####Avoiding Failure Result Instantiation
27
- Currently, every parse failure instantiates a failure object. Both success and failure objects propagate an array of the furthest-advanced terminal failures encountered during the parse. These are used to give feedback to the user in the event of a parse failure as to where the most likely source of the error was located. Rather than propagate them upward in the failure objects, it would be faster to just return false in the event of failure and instead write terminal failures to a mutable data structure that is global to the parse. Even this can be done only in the event that the index of the failure is greater than or equal to the current maximal failure index. In addition to minimizing failure object instantiation, this will probably reduce the time spent sorting propagated failures.
28
-
29
21
  ####Transient Expressions
30
22
  Currently, every parsing expression instantiates a syntax node. This includes even very simple parsing expressions, like single characters. It is probably unnecessary for every single expression in the parse to correspond to its own syntax node, so much savings could be garnered from a transient declaration that instructs the parser only to attempt a match without instantiating nodes.
31
23
 
data/doc/index.markdown CHANGED
@@ -4,18 +4,79 @@ Treetop is a language for describing languages. Combining the elegance of Ruby w
4
4
 
5
5
  </p>
6
6
 
7
+ sudo gem install treetop
7
8
 
8
9
  #Intuitive Grammar Specifications
9
- Treetop's packrat parsers use _memoization_ to make the backtracking possible in linear time. This cuts the gordian knot of grammar design. There's no need to look ahead and no need to lex. Worry about the structure of the language, not the idiosyncrasies of the parser.
10
+ Parsing expression grammars (PEGs) are simple to write and easy to maintain. They are a simple but powerful generalization of regular expressions that are easier to work with than the LALR or LR-1 grammars of traditional parser generators. There's no need for a tokenization phase, and _lookahead assertions_ can be used for a limited degree of context-sensitivity. Here's an extremely simple Treetop grammar that matches a subset of arithmetic, respecting operator precedence:
11
+
12
+ grammar Arithmetic
13
+ rule additive
14
+ multitive '+' additive / multitive
15
+ end
16
+
17
+ rule multitive
18
+ primary '*' multitive / primary
19
+ end
20
+
21
+ rule primary
22
+ '(' additive ')' / number
23
+ end
24
+
25
+ rule number
26
+ [1-9] [0-9]*
27
+ end
28
+ end
29
+
10
30
 
11
31
  #Syntax-Oriented Programming
12
- Rather than implementing semantic actions that construct parse trees, define methods on the trees that Treetop automatically constructs–and write this code directly inside the grammar.
32
+ Rather than implementing semantic actions that construct parse trees, Treetop lets you define methods on trees that it constructs for you automatically. You can define these methods directly within the grammar...
33
+
34
+ grammar Arithmetic
35
+ rule additive
36
+ multitive '+' additive {
37
+ def value
38
+ multitive.value + additive.value
39
+ end
40
+ }
41
+ /
42
+ multitive
43
+ end
44
+
45
+ # other rules below ...
46
+ end
47
+
48
+ ...or associate rules with classes of nodes you wish your parsers to instantiate upon matching a rule.
49
+
50
+ grammar Arithmetic
51
+ rule additive
52
+ multitive '+' additive <AdditiveNode>
53
+ /
54
+ multitive
55
+ end
56
+
57
+ # other rules below ...
58
+ end
59
+
13
60
 
14
61
  #Reusable, Composable Language Descriptions
15
- Break grammars into modules and compose them via Ruby's mixin semantics. Or combine grammars written by others in novel ways. Or extend existing grammars with your own syntactic constructs by overriding rules with access to a `super` keyword. Compositionally means your investment of time into grammar writing is secure–you can always extend and reuse your code.
62
+ Because PEGs are closed under composition, Treetop grammars can be treated like Ruby modules. You can mix them into one another and override rules with access to the `super` keyword. You can break large grammars down into coherent units or make your language's syntax modular. This is especially useful if you want other programmers to be able to reuse your work.
63
+
64
+ grammar RubyWithEmbeddedSQL
65
+ include SQL
66
+
67
+ rule string
68
+ quote sql_expression quote / super
69
+ end
70
+ end
71
+
16
72
 
17
73
  #Acknowledgements
18
- First, thank you to my employer Rob Mee of Pivotal Labs for funding a substantial portion of Treetop's development. He gets it.
74
+
75
+
76
+ <a href="http://pivotallabs.com"><img id="pivotal_logo" src="./images/pivotal.gif"></a>
77
+
78
+ First, thank you to my employer Rob Mee of <a href="http://pivotallabs.com"/>Pivotal Labs</a> for funding a substantial portion of Treetop's development. He gets it.
79
+
19
80
 
20
81
  I'd also like to thank:
21
82
 
@@ -128,7 +128,9 @@ Subexpressions can be given an explicit label to have an element accessor method
128
128
  rule labels
129
129
  first_letter:[a-z] rest_letters:(', ' letter:[a-z])* {
130
130
  def letters
131
- [first_letter] + rest_letters.map { |comma_and_letter| comma_and_letter.letter }
131
+ [first_letter] + rest_letters.map do |comma_and_letter|
132
+ comma_and_letter.letter
133
+ end
132
134
  end
133
135
  }
134
136
  end
data/doc/site.rb CHANGED
@@ -1,17 +1,30 @@
1
1
  require 'rubygems'
2
2
  require 'erector'
3
+ require "#{File.dirname(__FILE__)}/sitegen"
3
4
 
4
5
  class Layout < Erector::Widget
5
6
  def render
6
7
  html do
7
8
  head do
8
9
  link :rel => "stylesheet",
9
- :type => "text/css",
10
- :href => "./screen.css"
10
+ :type => "text/css",
11
+ :href => "./screen.css"
12
+
13
+ text %(
14
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
15
+ </script>
16
+ <script type="text/javascript">
17
+ _uacct = "UA-3418876-1";
18
+ urchinTracker();
19
+ </script>
20
+ )
11
21
  end
12
-
22
+
13
23
  body do
14
24
  div :id => 'top' do
25
+ div :id => 'main_navigation' do
26
+ main_navigation
27
+ end
15
28
  end
16
29
  div :id => 'middle' do
17
30
  div :id => 'content' do
@@ -19,17 +32,22 @@ class Layout < Erector::Widget
19
32
  end
20
33
  end
21
34
  div :id => 'bottom' do
22
-
35
+
23
36
  end
24
37
  end
25
- end
38
+ end
26
39
  end
27
-
28
- def bluecloth(path)
29
- File.open(path) do |file|
30
- text BlueCloth.new(file.read).to_html
40
+
41
+ def main_navigation
42
+ ul do
43
+ li { link_to "Documentation", SyntacticRecognition, Documentation }
44
+ li { link_to "Contribute", Contribute }
45
+ li { link_to "Home", Index }
31
46
  end
32
47
  end
48
+
49
+ def content
50
+ end
33
51
  end
34
52
 
35
53
  class Index < Layout
@@ -38,4 +56,55 @@ class Index < Layout
38
56
  end
39
57
  end
40
58
 
41
- puts Index.new.to_s
59
+ class Documentation < Layout
60
+ abstract
61
+
62
+ def content
63
+ div :id => 'secondary_navigation' do
64
+ ul do
65
+ li { link_to 'Syntax', SyntacticRecognition }
66
+ li { link_to 'Semantics', SemanticInterpretation }
67
+ li { link_to 'Using In Ruby', UsingInRuby }
68
+ li { link_to 'Advanced Techniques', PitfallsAndAdvancedTechniques }
69
+ end
70
+ end
71
+
72
+ div :id => 'documentation_content' do
73
+ documentation_content
74
+ end
75
+ end
76
+ end
77
+
78
+ class SyntacticRecognition < Documentation
79
+ def documentation_content
80
+ bluecloth "syntactic_recognition.markdown"
81
+ end
82
+ end
83
+
84
+ class SemanticInterpretation < Documentation
85
+ def documentation_content
86
+ bluecloth "semantic_interpretation.markdown"
87
+ end
88
+ end
89
+
90
+ class UsingInRuby < Documentation
91
+ def documentation_content
92
+ bluecloth "using_in_ruby.markdown"
93
+ end
94
+ end
95
+
96
+ class PitfallsAndAdvancedTechniques < Documentation
97
+ def documentation_content
98
+ bluecloth "pitfalls_and_advanced_techniques.markdown"
99
+ end
100
+ end
101
+
102
+
103
+ class Contribute < Layout
104
+ def content
105
+ bluecloth "contributing_and_planned_features.markdown"
106
+ end
107
+ end
108
+
109
+
110
+ Layout.generate_site
@@ -0,0 +1,118 @@
1
+ <html><head><link rel="stylesheet" href="./screen.css" type="text/css"></link>
2
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
3
+ </script>
4
+ <script type="text/javascript">
5
+ _uacct = "UA-3418876-1";
6
+ urchinTracker();
7
+ </script>
8
+ </head><body><div id="top"><div id="main_navigation"><ul><li><a href="syntactic_recognition.html">Documentation</a></li><li>Contribute</li><li><a href="index.html">Home</a></li></ul></div></div><div id="middle"><div id="content"><h1>Contributing</h1>
9
+
10
+ <p>I like to try Rubinius's policy regarding commit rights. If you submit one patch worth integrating, I'll give you commit rights. We'll see how this goes, but I think it's a good policy.</p>
11
+
12
+ <p>The source code is currently stored in a git repository at <a href="http://repo.or.cz/w/treetop.git">http://repo.or.cz/w/treetop.git</a></p>
13
+
14
+ <h2>Getting Started with the Code</h2>
15
+
16
+ <p>Treetop compiler is interesting in that it is implemented in itself. Its functionality revolves around <code>metagrammar.treetop</code>, which specifies the grammar for Treetop grammars. I took a hybrid approach with regard to definition of methods on syntax nodes in the metagrammar. Methods that are more syntactic in nature, like those that provide access to elements of the syntax tree, are often defined inline, directly in the grammar. More semantic methods are defined in custom node classes.</p>
17
+
18
+ <p>Iterating on the metagrammar is tricky. The current testing strategy uses the last stable version of Treetop to parse the version under test. Then the version under test is used to parse and functionally test the various pieces of syntax it should recognize and translate to Ruby. As you change <code>metagrammar.treetop</code> and its associated node classes, note that the node classes you are changing are also used to support the previous stable version of the metagrammar, so must be kept backward compatible until such time as a new stable version can be produced to replace it.</p>
19
+
20
+ <h2>Tests</h2>
21
+
22
+ <p>Most of the compiler's tests are functional in nature. The grammar under test is used to parse and compile piece of sample code. Then I attempt to parse input with the compiled output and test its results.</p>
23
+
24
+ <h1>What Needs to be Done</h1>
25
+
26
+ <h2>Small Stuff</h2>
27
+
28
+ <ul>
29
+ <li>Improve the <code>tt</code> command line tool to allow <code>.treetop</code> extensions to be elided in its arguments.</li>
30
+ <li>Generate and load temp files with <code>Treetop.load</code> rather than evaluating strings to improve stack trace readability.</li>
31
+ <li>Allow <code>do/end</code> style blocks as well as curly brace blocks. This was originally omitted because I thought it would be confusing. It probably isn't.</li>
32
+ </ul>
33
+
34
+ <h2>Big Stuff</h2>
35
+
36
+ <h4>Transient Expressions</h4>
37
+
38
+ <p>Currently, every parsing expression instantiates a syntax node. This includes even very simple parsing expressions, like single characters. It is probably unnecessary for every single expression in the parse to correspond to its own syntax node, so much savings could be garnered from a transient declaration that instructs the parser only to attempt a match without instantiating nodes.</p>
39
+
40
+ <h3>Generate Rule Implementations in C</h3>
41
+
42
+ <p>Parsing expressions are currently compiled into simple Ruby source code that comprises the body of parsing rules, which are translated into Ruby methods. The generator could produce C instead of Ruby in the body of these method implementations.</p>
43
+
44
+ <h3>Global Parsing State and Semantic Backtrack Triggering</h3>
45
+
46
+ <p>Some programming language grammars are not entirely context-free, requiring that global state dictate the behavior of the parser in certain circumstances. Treetop does not currently expose explicit parser control to the grammar writer, and instead automatically constructs the syntax tree for them. A means of semantic parser control compatible with this approach would involve callback methods defined on parsing nodes. Each time a node is successfully parsed it will be given an opportunity to set global state and optionally trigger a parse failure on <em>extrasyntactic</em> grounds. Nodes will probably need to define an additional method that undoes their changes to global state when there is a parse failure and they are backtracked.</p>
47
+
48
+ <p>Here is a sketch of the potential utility of such mechanisms. Consider the structure of YAML, which uses indentation to indicate block structure.</p>
49
+
50
+ <pre><code>level_1:
51
+ level_2a:
52
+ level_2b:
53
+ level_3a:
54
+ level_2c:
55
+ </code></pre>
56
+
57
+ <p>Imagine a grammar like the following:</p>
58
+
59
+ <pre><code>rule yaml_element
60
+ name ':' block
61
+ /
62
+ name ':' value
63
+ end
64
+
65
+ rule block
66
+ indent yaml_elements outdent
67
+ end
68
+
69
+ rule yaml_elements
70
+ yaml_element (samedent yaml_element)*
71
+ end
72
+
73
+ rule samedent
74
+ newline spaces {
75
+ def after_success(parser_state)
76
+ spaces.length == parser_state.indent_level
77
+ end
78
+ }
79
+ end
80
+
81
+ rule indent
82
+ newline spaces {
83
+ def after_success(parser_state)
84
+ if spaces.length == parser_state.indent_level + 2
85
+ parser_state.indent_level += 2
86
+ true
87
+ else
88
+ false # fail the parse on extrasyntactic grounds
89
+ end
90
+ end
91
+
92
+ def undo_success(parser_state)
93
+ parser_state.indent_level -= 2
94
+ end
95
+ }
96
+ end
97
+
98
+ rule outdent
99
+ newline spaces {
100
+ def after_success(parser_state)
101
+ if spaces.length == parser_state.indent_level - 2
102
+ parser_state.indent_level -= 2
103
+ true
104
+ else
105
+ false # fail the parse on extrasyntactic grounds
106
+ end
107
+ end
108
+
109
+ def undo_success(parser_state)
110
+ parser_state.indent_level += 2
111
+ end
112
+ }
113
+ end
114
+ </code></pre>
115
+
116
+ <p>In this case a block will be detected only if a change in indentation warrants it. Note that this change in the state of indentation must be undone if a subsequent failure causes this node not to ultimately be incorporated into a successful result.</p>
117
+
118
+ <p>I am by no means sure that the above sketch is free of problems, or even that this overall strategy is sound, but it seems like a promising path.</p></div></div><div id="bottom"></div></body></html>
Binary file
Binary file
@@ -0,0 +1,102 @@
1
+ <html><head><link rel="stylesheet" href="./screen.css" type="text/css"></link>
2
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
3
+ </script>
4
+ <script type="text/javascript">
5
+ _uacct = "UA-3418876-1";
6
+ urchinTracker();
7
+ </script>
8
+ </head><body><div id="top"><div id="main_navigation"><ul><li><a href="syntactic_recognition.html">Documentation</a></li><li><a href="contribute.html">Contribute</a></li><li>Home</li></ul></div></div><div id="middle"><div id="content"><p class="intro_text">
9
+
10
+ Treetop is a language for describing languages. Combining the elegance of Ruby with cutting-edge <em>parsing expression grammars</em>, it helps you analyze syntax with revolutionarily ease.
11
+
12
+ </p>
13
+
14
+ <pre><code>sudo gem install treetop
15
+ </code></pre>
16
+
17
+ <h1>Intuitive Grammar Specifications</h1>
18
+
19
+ <p>Parsing expression grammars (PEGs) are simple to write and easy to maintain. They are a simple but powerful generalization of regular expressions that are easier to work with than the LALR or LR-1 grammars of traditional parser generators. There's no need for a tokenization phase, and <em>lookahead assertions</em> can be used for a limited degree of context-sensitivity. Here's an extremely simple Treetop grammar that matches a subset of arithmetic, respecting operator precedence:</p>
20
+
21
+ <pre><code>grammar Arithmetic
22
+ rule additive
23
+ multitive '+' additive / multitive
24
+ end
25
+
26
+ rule multitive
27
+ primary '*' multitive / primary
28
+ end
29
+
30
+ rule primary
31
+ '(' additive ')' / number
32
+ end
33
+
34
+ rule number
35
+ [1-9] [0-9]*
36
+ end
37
+ end
38
+ </code></pre>
39
+
40
+ <h1>Syntax-Oriented Programming</h1>
41
+
42
+ <p>Rather than implementing semantic actions that construct parse trees, Treetop lets you define methods on trees that it constructs for you automatically. You can define these methods directly within the grammar...</p>
43
+
44
+ <pre><code>grammar Arithmetic
45
+ rule additive
46
+ multitive '+' additive {
47
+ def value
48
+ multitive.value + additive.value
49
+ end
50
+ }
51
+ /
52
+ multitive
53
+ end
54
+
55
+ # other rules below ...
56
+ end
57
+ </code></pre>
58
+
59
+ <p>...or associate rules with classes of nodes you wish your parsers to instantiate upon matching a rule.</p>
60
+
61
+ <pre><code>grammar Arithmetic
62
+ rule additive
63
+ multitive '+' additive &lt;AdditiveNode&gt;
64
+ /
65
+ multitive
66
+ end
67
+
68
+ # other rules below ...
69
+ end
70
+ </code></pre>
71
+
72
+ <h1>Reusable, Composable Language Descriptions</h1>
73
+
74
+ <p>Because PEGs are closed under composition, Treetop grammars can be treated like Ruby modules. You can mix them into one another and override rules with access to the <code>super</code> keyword. You can break large grammars down into coherent units or make your language's syntax modular. This is especially useful if you want other programmers to be able to reuse your work.</p>
75
+
76
+ <pre><code>grammar RubyWithEmbeddedSQL
77
+ include SQL
78
+
79
+ rule string
80
+ quote sql_expression quote / super
81
+ end
82
+ end
83
+ </code></pre>
84
+
85
+ <h1>Acknowledgements</h1>
86
+
87
+ <p><a href="http://pivotallabs.com"><img id="pivotal_logo" src="./images/pivotal.gif"></a></p>
88
+
89
+ <p>First, thank you to my employer Rob Mee of <a href="http://pivotallabs.com"/>Pivotal Labs</a> for funding a substantial portion of Treetop's development. He gets it.</p>
90
+
91
+ <p>I'd also like to thank:</p>
92
+
93
+ <ul>
94
+ <li>Damon McCormick for several hours of pair programming.</li>
95
+ <li>Nick Kallen for lots of well-considered feedback and a few afternoons of programming.</li>
96
+ <li>Brian Takita for a night of pair programming.</li>
97
+ <li>Eliot Miranda for urging me rewrite as a compiler right away rather than putting it off.</li>
98
+ <li>Ryan Davis and Eric Hodel for hurting my code.</li>
99
+ <li>Dav Yaginuma for kicking me into action on my idea.</li>
100
+ <li>Bryan Ford for his seminal work on Packrat Parsers.</li>
101
+ <li>The editors of Lambda the Ultimate, where I discovered parsing expression grammars.</li>
102
+ </ul></div></div><div id="bottom"></div></body></html>