parslet 1.2.1 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,15 +1,16 @@
1
- # A sample Gemfile
2
1
  source "http://rubygems.org"
3
2
 
3
+ gem 'rake'
4
4
  gem 'blankslate', '~> 2'
5
5
 
6
6
  group :development do
7
7
  gem 'rspec'
8
8
  gem 'flexmock'
9
-
9
+
10
+ gem 'rdoc'
10
11
  gem 'sdoc'
11
12
 
12
13
  gem 'guard'
13
14
  gem 'guard-rspec'
14
15
  gem 'growl'
15
- end
16
+ end
@@ -3,9 +3,13 @@
3
3
  - prsnt? and absnt? are now finally banned into oblivion. Wasting vocals for
4
4
  the win.
5
5
 
6
- = 1.3.0 / ???
6
+ = 1.2.3 / 22Sep2011
7
7
 
8
- Next bigger release, features not clear yet. Probably heredoc-parsing.
8
+ + Transform#apply can now be called with a hash as second argument. This
9
+ provides bindings and a way to inject context.
10
+
11
+ ! Fixes a bug thar modified parslet atoms in place, defeating oop chaining.
12
+ (#50)
9
13
 
10
14
  = 1.2.1 / 6Jun2011
11
15
 
data/README CHANGED
@@ -2,13 +2,13 @@ INTRODUCTION
2
2
 
3
3
  Parslet makes developing complex parsers easy. It does so by
4
4
 
5
- * providing the best <b>error reporting</b> possible
6
- * <b>not generating</b> reams of code for you to debug
5
+ * providing the best error reporting possible
6
+ * not generating reams of code for you to debug
7
7
 
8
- Parslet takes the long way around to make <b>your job</b> easier. It allows
9
- for incremental language construction. Often, you start out small,
10
- implementing the atoms of your language first; _parslet_ takes pride in making
11
- this possible.
8
+ Parslet takes the long way around to make your job easier. It allows for
9
+ incremental language construction. Often, you start out small, implementing
10
+ the atoms of your language first; _parslet_ takes pride in making this
11
+ possible.
12
12
 
13
13
  Eager to try this out? Please see the associated web site:
14
14
  http://kschiess.github.com/parslet
@@ -48,8 +48,11 @@ issues.
48
48
  Note that due to Ruby 1.8 internals, Unicode parsing is not supported on that
49
49
  version.
50
50
 
51
+ On Mac OS X Lion, ruby-1.8.7-p352 has been known to segfault. Use
52
+ ruby-1.8.7-p334 for better results.
53
+
51
54
  STATUS
52
55
 
53
- At version 1.2.1 - See HISTORY.txt for changes.
56
+ At version 1.2.3 - See HISTORY.txt for changes.
54
57
 
55
58
  (c) 2010 Kaspar Schiess
data/Rakefile CHANGED
@@ -1,7 +1,8 @@
1
- require "rubygems"
2
- require "rake/rdoctask"
1
+ require 'rdoc/task'
2
+ require 'sdoc'
3
+
3
4
  require 'rspec/core/rake_task'
4
- require "rake/gempackagetask"
5
+ require "rubygems/package_task"
5
6
 
6
7
  desc "Run all tests: Exhaustive."
7
8
  RSpec::Core::RakeTask.new
@@ -15,10 +16,8 @@ end
15
16
 
16
17
  task :default => :spec
17
18
 
18
- require 'sdoc'
19
-
20
19
  # Generate documentation
21
- Rake::RDocTask.new do |rdoc|
20
+ RDoc::Task.new do |rdoc|
22
21
  rdoc.title = "parslet - construction of parsers made easy"
23
22
  rdoc.options << '--line-numbers'
24
23
  rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
@@ -36,7 +35,7 @@ task :gem => :spec
36
35
  spec = eval(File.read('parslet.gemspec'))
37
36
 
38
37
  desc "Generate the gem package."
39
- Rake::GemPackageTask.new(spec) do |pkg|
38
+ Gem::PackageTask.new(spec) do |pkg|
40
39
  pkg.gem_spec = spec
41
40
  end
42
41
 
@@ -0,0 +1,33 @@
1
+ # A small example on how to make parslet ignore parts of the parse tree.
2
+
3
+ $:.unshift File.dirname(__FILE__) + "/../lib"
4
+ require 'parslet'
5
+
6
+ class IgnoreParslet < Parslet::Atoms::Base
7
+ def initialize(parslet)
8
+ @parslet = parslet
9
+ end
10
+ def to_s_inner(prec)
11
+ @parslet.to_s(prec)
12
+ end
13
+ def try(source, context)
14
+ result = @parslet.try(source, context)
15
+
16
+ return success(nil) unless result.error?
17
+ return result
18
+ end
19
+
20
+ end
21
+ module IgnoreDSL
22
+ def ignore
23
+ IgnoreParslet.new(self)
24
+ end
25
+ end
26
+
27
+ class Parslet::Atoms::Base
28
+ include IgnoreDSL
29
+ end
30
+
31
+ include Parslet
32
+ p (str('a') >> str('b').ignore >> str('c')).
33
+ parse('abc')
@@ -0,0 +1 @@
1
+ "ac"@0
@@ -4,5 +4,5 @@
4
4
 
5
5
  ((((())))): {:l=>"("@0, :m=>{:l=>"("@1, :m=>{:l=>"("@2, :m=>{:l=>"("@3, :m=>{:l=>"("@4, :m=>nil, :r=>")"@5}, :r=>")"@6}, :r=>")"@7}, :r=>")"@8}, :r=>")"@9} (5 parens)
6
6
 
7
- ((()): Failed to match sequence (l:'(' m:(BALANCED?)) at line 1 char 6.
7
+ ((()): Failed to match sequence (l:'(' m:(BALANCED?) r:')') at line 1 char 6.
8
8
 
@@ -27,8 +27,7 @@ class Parslet::Atoms::Alternative < Parslet::Atoms::Base
27
27
  # all here. This reduces the number of objects created.
28
28
  #+++
29
29
  def |(parslet) # :nodoc:
30
- @alternatives << parslet
31
- self
30
+ self.class.new(*@alternatives + [parslet])
32
31
  end
33
32
 
34
33
  def try(source, context) # :nodoc:
@@ -97,6 +97,10 @@ class Parslet::Atoms::Base
97
97
  # Takes a mixed value coming out of a parslet and converts it to a return
98
98
  # value for the user by dropping things and merging hashes.
99
99
  #
100
+ # Named is set to true if this result will be embedded in a Hash result from
101
+ # naming something using <code>.as(...)</code>. It changes the folding
102
+ # semantics of repetition.
103
+ #
100
104
  def flatten(value, named=false) # :nodoc:
101
105
  # Passes through everything that isn't an array of things
102
106
  return value unless value.instance_of? Array
@@ -16,8 +16,7 @@ class Parslet::Atoms::Sequence < Parslet::Atoms::Base
16
16
  end
17
17
 
18
18
  def >>(parslet) # :nodoc:
19
- @parslets << parslet
20
- self
19
+ self.class.new(* @parslets+[parslet])
21
20
  end
22
21
 
23
22
  def try(source, context) # :nodoc:
@@ -18,37 +18,23 @@
18
18
  # Note that Parslet::Pattern only matches at a given subtree; it wont try
19
19
  # to match recursively. To do that, please use Parslet::Transform.
20
20
  #
21
- class Parslet::Pattern
22
- autoload :Context, 'parslet/pattern/context'
23
-
21
+ class Parslet::Pattern
24
22
  def initialize(pattern)
25
23
  @pattern = pattern
26
24
  end
27
25
 
28
26
  # Decides if the given subtree matches this pattern. Returns the bindings
29
- # made on a successful match or nil if the match fails.
27
+ # made on a successful match or nil if the match fails. If you specify
28
+ # bindings to be a hash, the mappings in it will be treated like bindings
29
+ # made during an attempted match.
30
30
  #
31
- def match(subtree)
32
- bindings = {}
33
- return bindings if element_match(subtree, @pattern, bindings)
34
- end
35
-
36
- # Executes the block on the bindings obtained by #match, if such a match
37
- # can be made. Contains the logic that will switch to instance variables
38
- # depending on the arity of the block.
31
+ # Example:
39
32
  #
40
- #---
41
- # TODO This method should be in Transform.
33
+ # Pattern.new('a').match('a', :foo => 'bar') # => { :foo => 'bar' }
42
34
  #
43
- def call_on_match(bindings, block)
44
- if block
45
- if block.arity == 1
46
- return block.call(bindings)
47
- else
48
- context = Context.new(bindings)
49
- return context.instance_eval(&block)
50
- end
51
- end
35
+ def match(subtree, bindings=nil)
36
+ bindings = bindings && bindings.dup || Hash.new
37
+ return bindings if element_match(subtree, @pattern, bindings)
52
38
  end
53
39
 
54
40
  # Returns true if the tree element given by +tree+ matches the expression
@@ -3,16 +3,6 @@
3
3
  # any other string, except that it remembers where it came from (offset in
4
4
  # original input).
5
5
  #
6
- # Some slices also know what parent slice they are a small part of. This
7
- # allows the slice to be concatenated to other slices from the same buffer by
8
- # reslicing it against that original buffer.
9
- #
10
- # Why the complexity? Slices allow retaining offset information. This will
11
- # allow to assign line and column to each small bit of output from the parslet
12
- # parser. Also, while we keep that information, we might as well try to do
13
- # something useful with it. Reslicing the same buffers should in theory keep
14
- # buffer copies and allocations down.
15
- #
16
6
  # == Extracting line and column
17
7
  #
18
8
  # Using the #line_and_column method, you can extract the line and column in
@@ -32,27 +22,16 @@
32
22
  # These omissions are somewhat intentional. Rather than maintaining a full
33
23
  # delegation, we opt for a partial emulation that gets the job done.
34
24
  #
35
- # Note also that there are some things that work with strings that will never
36
- # work when using slices. For instance, you cannot concatenate slices that
37
- # aren't from the same source or that don't join up:
38
- #
39
- # Example:
40
- # big_slice = 'abcdef'
41
- # a = big_slice.slice(0, 2) # => "ab"@0
42
- # b = big_slice.slice(4, 2) # => "ef"@4
43
- #
44
- # a + b # raises Parslet::InvalidSliceOperation
45
- #
46
- # This avoids creating slices with impossible offsets or that are
47
- # discontinous.
48
- #
49
25
  class Parslet::Slice
50
26
  attr_reader :str, :offset
51
- attr_reader :source
27
+ attr_reader :line_cache
52
28
 
53
- def initialize(string, offset, source=nil)
29
+ # Construct a slice using a string, an offset and an optional line cache.
30
+ # The line cache should be able to answer to the #line_and_column message.
31
+ #
32
+ def initialize(string, offset, line_cache=nil)
54
33
  @str, @offset = string, offset
55
- @source = source
34
+ @line_cache = line_cache
56
35
  end
57
36
 
58
37
  # Compares slices to other slices or strings.
@@ -78,16 +57,16 @@ class Parslet::Slice
78
57
  # as the one of this slice.
79
58
  #
80
59
  def +(other)
81
- self.class.new(str + other.to_s, offset, source)
60
+ self.class.new(str + other.to_s, offset, line_cache)
82
61
  end
83
62
 
84
63
  # Returns a <line, column> tuple referring to the original input.
85
64
  #
86
65
  def line_and_column
87
- raise ArgumentError, "No source was given, cannot infer line and column." \
88
- unless source
66
+ raise ArgumentError, "No line cache was given, cannot infer line and column." \
67
+ unless line_cache
89
68
 
90
- source.line_and_column(self.offset)
69
+ line_cache.line_and_column(self.offset)
91
70
  end
92
71
 
93
72
 
@@ -20,8 +20,7 @@ class Parslet::Source
20
20
  # Reads n chars from the input and returns a Range instance.
21
21
  #
22
22
  def read(n)
23
- raise ArgumentError, "Cannot read <= 1 characters at a time." \
24
- if n < 1
23
+ raise ArgumentError, "Cannot read < 1 characters at a time." if n < 1
25
24
  read_slice(n)
26
25
  end
27
26
 
@@ -51,7 +50,7 @@ private
51
50
  # cache line ends
52
51
  @line_cache.scan_for_line_endings(start, buf)
53
52
 
54
- Parslet::Slice.new(buf || '', start, self)
53
+ Parslet::Slice.new(buf || '', start, @line_cache)
55
54
  end
56
55
 
57
56
  if RUBY_VERSION !~ /^1.9/
@@ -62,7 +61,7 @@ private
62
61
  # cache line ends
63
62
  @line_cache.scan_for_line_endings(start, buf)
64
63
 
65
- Parslet::Slice.new(buf || '', start, self)
64
+ Parslet::Slice.new(buf || '', start, @line_cache)
66
65
  end
67
66
  end
68
- end
67
+ end
@@ -70,7 +70,6 @@ class Parslet::Source
70
70
  left = 0
71
71
  right = size - 1
72
72
 
73
- n = 10
74
73
  loop do
75
74
  mid = left + (right - left) / 2
76
75
 
@@ -87,4 +86,4 @@ class Parslet::Source
87
86
  end
88
87
  end
89
88
  end
90
- end
89
+ end
@@ -85,10 +85,38 @@ require 'parslet/pattern'
85
85
  # end
86
86
  # transform.apply(tree)
87
87
  #
88
+ # = Execution context
89
+ #
90
+ # The execution context of action blocks differs depending on the arity of
91
+ # said blocks. This can be confusing. It is however somewhat intentional. You
92
+ # should not create fat Transform descendants containing a lot of helper methods,
93
+ # instead keep your AST class construction in global scope or make it available
94
+ # through a factory. The following piece of code illustrates usage of global
95
+ # scope:
96
+ #
97
+ # transform = Parslet::Transform.new do
98
+ # rule(...) { AstNode.new(a_variable) }
99
+ # rule(...) { Ast.node(a_variable) } # modules are nice
100
+ # end
101
+ # transform.apply(tree)
102
+ #
103
+ # And here's how you would use a class builder (a factory):
104
+ #
105
+ # transform = Parslet::Transform.new do
106
+ # rule(...) { builder.add_node(a_variable) }
107
+ # rule(...) { |d| d[:builder].add_node(d[:a_variable]) }
108
+ # end
109
+ # transform.apply(tree, :builder => Builder.new)
110
+ #
111
+ # As you can see, Transform allows you to inject local context for your rule
112
+ # action blocks to use.
113
+ #
88
114
  class Parslet::Transform
89
115
  # FIXME: Maybe only part of it? Or maybe only include into constructor
90
116
  # context?
91
117
  include Parslet
118
+
119
+ autoload :Context, 'parslet/transform/context'
92
120
 
93
121
  class << self
94
122
  # FIXME: Only do this for subclasses?
@@ -133,19 +161,45 @@ class Parslet::Transform
133
161
  # or a simple parslet. Transformation will proceed down the tree, replacing
134
162
  # parts/all of it with new objects. The resulting object will be returned.
135
163
  #
136
- def apply(obj)
164
+ def apply(obj, context=nil)
137
165
  transform_elt(
138
166
  case obj
139
167
  when Hash
140
- recurse_hash(obj)
168
+ recurse_hash(obj, context)
141
169
  when Array
142
- recurse_array(obj)
170
+ recurse_array(obj, context)
143
171
  else
144
172
  obj
145
- end
173
+ end,
174
+ context
146
175
  )
147
176
  end
148
177
 
178
+ # Executes the block on the bindings obtained by Pattern#match, if such a match
179
+ # can be made. Depending on the arity of the given block, it is called in
180
+ # one of two environments: the current one or a clean toplevel environment.
181
+ #
182
+ # If you would like the current environment preserved, please use the
183
+ # arity 1 variant of the block. Alternatively, you can inject a context object
184
+ # and call methods on it (think :ctx => self).
185
+ #
186
+ # Example:
187
+ # # the local variable a is simulated
188
+ # t.call_on_match(:a => :b) { a }
189
+ # # no change of environment here
190
+ # t.call_on_match(:a => :b) { |d| d[:a] }
191
+ #
192
+ def call_on_match(bindings, block)
193
+ if block
194
+ if block.arity == 1
195
+ return block.call(bindings)
196
+ else
197
+ context = Context.new(bindings)
198
+ return context.instance_eval(&block)
199
+ end
200
+ end
201
+ end
202
+
149
203
  # Allow easy access to all rules, the ones defined in the instance and the
150
204
  # ones predefined in a subclass definition.
151
205
  #
@@ -153,24 +207,24 @@ class Parslet::Transform
153
207
  self.class.rules + @rules
154
208
  end
155
209
 
156
- def transform_elt(elt) # :nodoc:
210
+ def transform_elt(elt, context) # :nodoc:
157
211
  rules.each do |pattern, block|
158
- if bindings=pattern.match(elt)
212
+ if bindings=pattern.match(elt, context)
159
213
  # Produces transformed value
160
- return pattern.call_on_match(bindings, block)
214
+ return call_on_match(bindings, block)
161
215
  end
162
216
  end
163
217
 
164
218
  # No rule matched - element is not transformed
165
219
  return elt
166
220
  end
167
- def recurse_hash(hsh) # :nodoc:
221
+ def recurse_hash(hsh, ctx) # :nodoc:
168
222
  hsh.inject({}) do |new_hsh, (k,v)|
169
- new_hsh[k] = apply(v)
223
+ new_hsh[k] = apply(v, ctx)
170
224
  new_hsh
171
225
  end
172
226
  end
173
- def recurse_array(ary) # :nodoc:
174
- ary.map { |elt| apply(elt) }
227
+ def recurse_array(ary, ctx) # :nodoc:
228
+ ary.map { |elt| apply(elt, ctx) }
175
229
  end
176
230
  end
@@ -10,7 +10,7 @@ require 'blankslate'
10
10
  # a # => :b
11
11
  # end
12
12
  #
13
- class Parslet::Pattern::Context < BlankSlate # :nodoc:
13
+ class Parslet::Transform::Context < BlankSlate # :nodoc:
14
14
  def initialize(bindings)
15
15
  @bindings = bindings
16
16
  end
metadata CHANGED
@@ -1,70 +1,78 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: parslet
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.3
4
5
  prerelease:
5
- version: 1.2.1
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Kaspar Schiess
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-06-05 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2011-09-22 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: blankslate
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70142555622900 !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
18
+ requirements:
21
19
  - - ~>
22
- - !ruby/object:Gem::Version
23
- version: "2.0"
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
24
22
  type: :runtime
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: rspec
28
23
  prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *70142555622900
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70142555622120 !ruby/object:Gem::Requirement
30
28
  none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
35
33
  type: :development
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
34
+ prerelease: false
35
+ version_requirements: *70142555622120
36
+ - !ruby/object:Gem::Dependency
38
37
  name: flexmock
38
+ requirement: &70142555621000 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
39
45
  prerelease: false
40
- requirement: &id003 !ruby/object:Gem::Requirement
46
+ version_requirements: *70142555621000
47
+ - !ruby/object:Gem::Dependency
48
+ name: rdoc
49
+ requirement: &70142555620020 !ruby/object:Gem::Requirement
41
50
  none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- version: "0"
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
46
55
  type: :development
47
- version_requirements: *id003
48
- - !ruby/object:Gem::Dependency
49
- name: sdoc
50
56
  prerelease: false
51
- requirement: &id004 !ruby/object:Gem::Requirement
57
+ version_requirements: *70142555620020
58
+ - !ruby/object:Gem::Dependency
59
+ name: sdoc
60
+ requirement: &70142555618980 !ruby/object:Gem::Requirement
52
61
  none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: "0"
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
57
66
  type: :development
58
- version_requirements: *id004
67
+ prerelease: false
68
+ version_requirements: *70142555618980
59
69
  description:
60
70
  email: kaspar.schiess@absurd.li
61
71
  executables: []
62
-
63
72
  extensions: []
64
-
65
- extra_rdoc_files:
73
+ extra_rdoc_files:
66
74
  - README
67
- files:
75
+ files:
68
76
  - Gemfile
69
77
  - HISTORY.txt
70
78
  - LICENSE
@@ -91,12 +99,12 @@ files:
91
99
  - lib/parslet/expression.rb
92
100
  - lib/parslet/parser.rb
93
101
  - lib/parslet/pattern/binding.rb
94
- - lib/parslet/pattern/context.rb
95
102
  - lib/parslet/pattern.rb
96
103
  - lib/parslet/rig/rspec.rb
97
104
  - lib/parslet/slice.rb
98
105
  - lib/parslet/source/line_cache.rb
99
106
  - lib/parslet/source.rb
107
+ - lib/parslet/transform/context.rb
100
108
  - lib/parslet/transform.rb
101
109
  - lib/parslet.rb
102
110
  - example/boolean_algebra.rb
@@ -105,6 +113,7 @@ files:
105
113
  - example/email_parser.rb
106
114
  - example/empty.rb
107
115
  - example/erb.rb
116
+ - example/ignore.rb
108
117
  - example/ip_address.rb
109
118
  - example/json.rb
110
119
  - example/local.rb
@@ -116,6 +125,7 @@ files:
116
125
  - example/output/email_parser.out
117
126
  - example/output/empty.err
118
127
  - example/output/erb.out
128
+ - example/output/ignore.out
119
129
  - example/output/ip_address.out
120
130
  - example/output/json.out
121
131
  - example/output/local.out
@@ -136,31 +146,31 @@ files:
136
146
  - example/test.lit
137
147
  homepage: http://kschiess.github.com/parslet
138
148
  licenses: []
139
-
140
149
  post_install_message:
141
- rdoc_options:
150
+ rdoc_options:
142
151
  - --main
143
152
  - README
144
- require_paths:
153
+ require_paths:
145
154
  - lib
146
- required_ruby_version: !ruby/object:Gem::Requirement
155
+ required_ruby_version: !ruby/object:Gem::Requirement
147
156
  none: false
148
- requirements:
149
- - - ">="
150
- - !ruby/object:Gem::Version
151
- version: "0"
152
- required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ! '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ segments:
162
+ - 0
163
+ hash: 4497395910031021567
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
165
  none: false
154
- requirements:
155
- - - ">="
156
- - !ruby/object:Gem::Version
157
- version: "0"
166
+ requirements:
167
+ - - ! '>='
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
158
170
  requirements: []
159
-
160
171
  rubyforge_project:
161
- rubygems_version: 1.8.5
172
+ rubygems_version: 1.8.6
162
173
  signing_key:
163
174
  specification_version: 3
164
175
  summary: Parser construction library with great error reporting in Ruby.
165
176
  test_files: []
166
-