parslet 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a03d43eaaf0b663727e308d79395c84019d63d0b
4
+ data.tar.gz: 2d39731312fe20e6640077b1cfb3b4a9c4ff34fd
5
+ SHA512:
6
+ metadata.gz: 5aacc9ac06b08230c6301ededf169dba75ea056c082531d62120a380a536df66e4bd310fd8ae609578cd0da8c74989c4c2d71d50ffa4acd394074e36166fd305
7
+ data.tar.gz: 8b7151423f165f532c24b6f5a3b3a0ce88e990c95749a7b5c918451ad5d2ed59368b528f66f5a39312495b7d33bb3ff1e3da69aadcc135a968881de0b84754ce
data/HISTORY.txt CHANGED
@@ -2,8 +2,29 @@
2
2
 
3
3
  - prsnt? and absnt? are now finally banned into oblivion. Wasting vocals for
4
4
  the win.
5
+
6
+ = 1.6 / ??
7
+
8
+ + EXPERIMENTAL: Parslet accelerators permit replacing parts of your parser
9
+ with optimized atoms using pattern matching. Look at
10
+ examples/optimized_erb.rb or the introduction to the feature in
11
+ qed/accelerators.md.
12
+
13
+ + infix_expression permits to declare an infix expression parser (think
14
+ calculator) directly. This will solve many of the problems we have
15
+ more elegantly.
16
+
17
+ + Rspec 3 syntax, though hideous, should now work.
18
+
19
+ - Drops 1.8.7 compatibility.
20
+
21
+ ! A performance anomaly when parsing multibyte characters has been detected
22
+ and fixed with the help of Zach Moazeni (@zmoazeni).
23
+
24
+ ! A few small bug fixes and optimisations have been introduced. API should
25
+ remain unchanged.
5
26
 
6
- = 1.5 / ??
27
+ = 1.5 / 27Dec2012
7
28
 
8
29
  + Handles unconsumed input at end of parse completely differently. Instead
9
30
  of generating a toplevel error, it now raises an error in every branch
data/LICENSE CHANGED
@@ -1,5 +1,4 @@
1
-
2
- Copyright (c) 2010 Kaspar Schiess
1
+ Copyright (c) 2010-2014 Kaspar Schiess
3
2
 
4
3
  Permission is hereby granted, free of charge, to any person
5
4
  obtaining a copy of this software and associated documentation
data/README CHANGED
@@ -51,20 +51,23 @@ SYNOPSIS
51
51
  # and then
52
52
  Smalltalk.new.parse('smalltalk')
53
53
 
54
- COMPATIBILITY
54
+ FEATURES
55
55
 
56
- This library should work with most rubies. I've tested it with MRI 1.8
57
- (except 1.8.6), 1.9, rbx-head, jruby. Please report as a bug if you encounter
58
- issues.
56
+ * Tools for every part of the parser chain
57
+ * Transformers generate Abstract Syntax Trees
58
+ * Accelerators transform parsers, making them quite a bit faster
59
+ * Pluggable error reporters
60
+ * Graphviz export for your parser
61
+ * Rspec testing support rig
62
+ * Simply Ruby, composable and hackable
59
63
 
60
- Note that due to Ruby 1.8 internals, Unicode parsing is not supported on that
61
- version.
64
+ COMPATIBILITY
62
65
 
63
- On Mac OS X Lion, ruby-1.8.7-p352 has been known to segfault. Use
64
- ruby-1.8.7-p334 for better results.
66
+ This library is intended to work with Ruby variants >= 1.9. I've tested it on
67
+ MRI 1.9, rbx-head, jruby. Please report as a bug if you encounter issues.
65
68
 
66
69
  STATUS
67
70
 
68
71
  Production worthy.
69
72
 
70
- (c) 2010, 2011, 2012 Kaspar Schiess
73
+ (c) 2010-2014 Kaspar Schiess
data/example/big.erb ADDED
@@ -0,0 +1,73 @@
1
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
2
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
3
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
4
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
5
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
6
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
7
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
8
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
9
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
10
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
11
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
12
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
13
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
14
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
15
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
16
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
17
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
18
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
19
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
20
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
21
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
22
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
23
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
24
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
25
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
26
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
27
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
28
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
29
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
30
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
31
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
32
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
33
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
34
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
35
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
36
+
37
+ <%= erb tag %>
38
+
39
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
40
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
41
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
42
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
43
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
44
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
45
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
46
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
47
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
48
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
49
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
50
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
51
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
52
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
53
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
54
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
55
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
56
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
57
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
58
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
59
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
60
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
61
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
62
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
63
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
64
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
65
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
66
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
67
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
68
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
69
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
70
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
71
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
72
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
73
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
@@ -44,9 +44,11 @@ end
44
44
  parser = EmailParser.new
45
45
  sanitizer = EmailSanitizer.new
46
46
 
47
- unless ARGV[0]
47
+ input = ARGV[0] || begin
48
+ default = "a.b.c.d@gmail.com"
48
49
  STDERR.puts "usage: #{$0} \"EMAIL_ADDR\""
49
- STDOUT.puts "since you haven't specified any EMAIL_ADDR, for testing purposes we're using a.b.c.d@gmail.com"
50
+ STDOUT.puts "since you haven't specified any EMAIL_ADDR, for testing purposes we're using #{default}"
51
+ default
50
52
  end
51
53
 
52
- p sanitizer.apply(parser.parse_with_debug(ARGV[0] || 'a.b.c.d@gmail.com'))
54
+ p sanitizer.apply(parser.parse_with_debug(input))
@@ -0,0 +1,42 @@
1
+ # Please also look at the more naive 'erb.rb'. This shows how to optimize an
2
+ # ERB like parser using parslet.
3
+
4
+ $:.unshift File.join(File.dirname(__FILE__), "/../lib")
5
+
6
+ require 'parslet'
7
+ require './qed/applique/gobbleup'
8
+ require 'parslet/accelerator'
9
+
10
+ class ErbParser < Parslet::Parser
11
+ rule(:ruby) { (str('%>').absent? >> any).repeat.as(:ruby) }
12
+
13
+ rule(:expression) { (str('=') >> ruby).as(:expression) }
14
+ rule(:comment) { (str('#') >> ruby).as(:comment) }
15
+ rule(:code) { ruby.as(:code) }
16
+ rule(:erb) { expression | comment | code }
17
+
18
+ rule(:erb_with_tags) { str('<%') >> erb >> str('%>') }
19
+ rule(:text) { (str('<%').absent? >> any).repeat(1) }
20
+
21
+ rule(:text_with_ruby) { (text.as(:text) | erb_with_tags).repeat.as(:text) }
22
+ root(:text_with_ruby)
23
+ end
24
+
25
+ parser = ErbParser.new
26
+
27
+ A = Parslet::Accelerator
28
+ optimized = A.apply(parser,
29
+ A.rule((A.str(:x).absent? >> A.any).repeat(1)) { GobbleUp.new(x, 1) },
30
+ A.rule((A.str(:x).absent? >> A.any).repeat(0)) { GobbleUp.new(x, 0) })
31
+
32
+ input = File.read(File.dirname(__FILE__) + "/big.erb")
33
+
34
+ # Remove the comment marks here to see what difference the optimisation makes.
35
+ # Commented out for the acceptance tests to run.
36
+ #
37
+ # require 'benchmark'
38
+ # Benchmark.bm(7) do |bm|
39
+ # bm.report('original') { parser.parse(input) }
40
+ # bm.report('gobble') { optimized.parse(input) }
41
+ # end
42
+ p optimized.parse(input)
@@ -0,0 +1 @@
1
+ {:text=>[{:text=>"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\n"@0}, {:expression=>{:ruby=>" erb tag "@2685}}, {:text=>"\n\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n"@2696}]}
@@ -0,0 +1,5 @@
1
+ a = 1
2
+ b = 2
3
+ c = 3 * 25
4
+ d = 100 + 3*4
5
+ {:a=>1, :b=>2, :c=>75, :d=>112}
@@ -0,0 +1,71 @@
1
+
2
+ # A demonstration of the new precedence climbing infix expression parser.
3
+
4
+ $:.unshift File.dirname(__FILE__) + "/../lib"
5
+
6
+ require 'pp'
7
+ require 'rspec'
8
+ require 'parslet'
9
+ require 'parslet/rig/rspec'
10
+ require 'parslet/convenience'
11
+
12
+ class InfixExpressionParser < Parslet::Parser
13
+ root :variable_assignment_list
14
+
15
+ rule(:space) { match[' '] }
16
+
17
+ def cts atom
18
+ atom >> space.repeat
19
+ end
20
+ def infix *args
21
+ Infix.new(*args)
22
+ end
23
+
24
+ # This is the heart of the infix expression parser: real simple definitions
25
+ # for all the pieces we need.
26
+ rule(:mul_op) { cts match['*/'] }
27
+ rule(:add_op) { cts match['+-'] }
28
+ rule(:digit) { match['0-9'] }
29
+ rule(:integer) { cts digit.repeat(1).as(:int) }
30
+
31
+ rule(:expression) { infix_expression(integer,
32
+ [mul_op, 2, :left],
33
+ [add_op, 1, :right]) }
34
+
35
+ # And now adding variable assignments to that, just to a) demonstrate this
36
+ # embedded in a bigger parser, and b) make the example interesting.
37
+ rule(:variable_assignment_list) {
38
+ variable_assignment.repeat(1) }
39
+ rule(:variable_assignment) {
40
+ identifier.as(:ident) >> equal_sign >> expression.as(:exp) >> eol }
41
+ rule(:identifier) {
42
+ cts (match['a-z'] >> match['a-zA-Z0-9'].repeat) }
43
+ rule(:equal_sign) {
44
+ cts str('=') }
45
+ rule(:eol) {
46
+ cts(str("\n")) | any.absent? }
47
+ end
48
+
49
+ class InfixInterpreter < Parslet::Transform
50
+ rule(int: simple(:int)) { Integer(int) }
51
+ rule(ident: simple(:ident), exp: simple(:result)) { |d|
52
+ d[:doc][d[:ident].to_s.strip.to_sym] = d[:result] }
53
+
54
+ rule(l: simple(:l), o: /^\*/, r: simple(:r)) { l * r }
55
+ rule(l: simple(:l), o: /^\+/, r: simple(:r)) { l + r }
56
+ end
57
+
58
+ input = <<ASSIGNMENTS
59
+ a = 1
60
+ b = 2
61
+ c = 3 * 25
62
+ d = 100 + 3*4
63
+ ASSIGNMENTS
64
+
65
+ puts input
66
+
67
+ int_tree = InfixExpressionParser.new.parse_with_debug(input)
68
+ bindings = {}
69
+ result = InfixInterpreter.new.apply(int_tree, doc: bindings)
70
+
71
+ pp bindings
data/lib/parslet.rb CHANGED
@@ -199,6 +199,38 @@ module Parslet
199
199
  Parslet::Atoms::Dynamic.new(block)
200
200
  end
201
201
  module_function :dynamic
202
+
203
+ # Returns a parslet atom that parses infix expressions. Operations are
204
+ # specified as a list of <atom, precedence, associativity> tuples, where
205
+ # atom is simply the parslet atom that matches an operator, precedence is
206
+ # a number and associativity is either :left or :right.
207
+ #
208
+ # Higher precedence indicates that the operation should bind tighter than
209
+ # other operations with lower precedence. In common algebra, '+' has
210
+ # lower precedence than '*'. So you would have a precedence of 1 for '+' and
211
+ # a precedence of 2 for '*'. Only the order relation between these two
212
+ # counts, so any number would work.
213
+ #
214
+ # Associativity is what decides what interpretation to take for strings that
215
+ # are ambiguous like '1 + 2 + 3'. If '+' is specified as left associative,
216
+ # the expression would be interpreted as '(1 + 2) + 3'. If right
217
+ # associativity is chosen, it would be interpreted as '1 + (2 + 3)'. Note
218
+ # that the hash trees output reflect that choice as well.
219
+ #
220
+ # Example:
221
+ # infix_expression(integer, [add_op, 1, :left])
222
+ # # would parse things like '1 + 2'
223
+ #
224
+ # @param element [Parslet::Atoms::Base] elements that take the NUMBER position
225
+ # in the expression
226
+ # @param operations [Array<(Parslet::Atoms::Base, Integer, {:left, :right})>]
227
+ #
228
+ # @see Parslet::Atoms::Infix
229
+ #
230
+ def infix_expression(element, *operations)
231
+ Parslet::Atoms::Infix.new(element, operations)
232
+ end
233
+ module_function :infix_expression
202
234
 
203
235
  # A special kind of atom that allows embedding whole treetop expressions
204
236
  # into parslet construction.
@@ -254,7 +286,7 @@ module Parslet
254
286
  Pattern::SubtreeBind.new(symbol)
255
287
  end
256
288
  module_function :subtree
257
-
289
+
258
290
  autoload :Expression, 'parslet/expression'
259
291
  end
260
292
 
@@ -0,0 +1,161 @@
1
+
2
+
3
+ # Optimizes the parsers by pattern matching on the parser atoms and replacing
4
+ # matches with better versions. See the file qed/accelerators.md for a more
5
+ # in-depth description.
6
+ #
7
+ # Example:
8
+ # quote = str('"')
9
+ # parser = quote >> (quote.absent? >> any).repeat >> quote
10
+ #
11
+ # A = Accelerator # for making what follows a bit shorter
12
+ # optimized_parser = A.apply(parser,
13
+ # A.rule( (A.str(:x).absent? >> A.any).repeat ) { GobbleUp.new(x) })
14
+ #
15
+ # optimized_parser.parse('"Parsing is now fully optimized! (tm)"')
16
+ #
17
+ module Parslet::Accelerator
18
+
19
+ # An expression to match against a tree of parser atoms. Normally, an
20
+ # expression is produced by Parslet::Accelerator.any,
21
+ # Parslet::Accelerator.str or Parslet::Accelerator.re.
22
+ #
23
+ # Expressions can be chained much like parslet atoms can be:
24
+ #
25
+ # expr.repeat(1) # matching repetition
26
+ # expr.absent? # matching absent?
27
+ # expr.present? # matching present?
28
+ # expr1 >> expr2 # matching a sequence
29
+ # expr1 | expr2 # matching an alternation
30
+ #
31
+ # @see Parslet::Accelerator.str
32
+ # @see Parslet::Accelerator.re
33
+ # @see Parslet::Accelerator.any
34
+ #
35
+ # @see Parslet::Accelerator
36
+ #
37
+ class Expression
38
+ attr_reader :type
39
+ attr_reader :args
40
+
41
+ def initialize(type, *args)
42
+ @type = type
43
+ @args = args
44
+ end
45
+
46
+ # @return [Expression]
47
+ def >> other_expr
48
+ join_or_new :seq, other_expr
49
+ end
50
+
51
+ # @return [Expression]
52
+ def | other_expr
53
+ join_or_new :alt, other_expr
54
+ end
55
+
56
+ # @return [Expression]
57
+ def absent?
58
+ Expression.new(:absent, self)
59
+ end
60
+ # @return [Expression]
61
+ def present?
62
+ Expression.new(:present, self)
63
+ end
64
+
65
+ # @return [Expression]
66
+ def repeat min=0, max=nil
67
+ Expression.new(:rep, min, max, self)
68
+ end
69
+
70
+ # @return [Expression]
71
+ def as name
72
+ Expression.new(:as, name)
73
+ end
74
+
75
+ # @api private
76
+ # @return [Expression]
77
+ def join_or_new tag, other_expr
78
+ if type == tag
79
+ @args << other_expr
80
+ else
81
+ Expression.new(tag, self, other_expr)
82
+ end
83
+ end
84
+ end
85
+
86
+ module_function
87
+ # Returns a match expression that will match `str` parslet atoms.
88
+ #
89
+ # @return [Parslet::Accelerator::Expression]
90
+ #
91
+ def str variable, *constraints
92
+ Expression.new(:str, variable, *constraints)
93
+ end
94
+
95
+ # Returns a match expression that will match `match` parslet atoms.
96
+ #
97
+ # @return [Parslet::Accelerator::Expression]
98
+ #
99
+ def re variable, *constraints
100
+ Expression.new(:re, variable, *constraints)
101
+ end
102
+
103
+ # Returns a match expression that will match `any` parslet atoms.
104
+ #
105
+ # @return [Parslet::Accelerator::Expression]
106
+ #
107
+ def any
108
+ Expression.new(:re, ".")
109
+ end
110
+
111
+ # Given a parslet atom and an expression, will determine if the expression
112
+ # matches the atom. If successful, returns the bindings into the pattern
113
+ # that were made. If no bindings had to be made to make the match successful,
114
+ # the empty hash is returned.
115
+ #
116
+ # @param atom [Parslet::Atoms::Base] parslet atom to match against
117
+ # @param expr [Parslet::Accelerator::Expression] expression to match
118
+ # @return [nil, Hash] bindings for the match, nil on failure
119
+ #
120
+ def match atom, expr
121
+ engine = Engine.new
122
+
123
+ return engine.bindings if engine.match(atom, expr)
124
+ end
125
+
126
+ # Constructs an accelerator rule. A rule is a matching expression and the
127
+ # code that should be executed once the expression could be bound to a
128
+ # parser.
129
+ #
130
+ # Example:
131
+ # Accelerator.rule(Accelerator.any) { Parslet.match('.') }
132
+ #
133
+ def rule expression, &action
134
+ [expression, action]
135
+ end
136
+
137
+ # Given a parslet atom and a set of rules, tries to match the rules
138
+ # recursively through the parslet atom. Once a rule could be matched,
139
+ # its action block will be called.
140
+ #
141
+ # Example:
142
+ # quote = str('"')
143
+ # parser = quote >> (quote.absent? >> any).repeat >> quote
144
+ #
145
+ # A = Accelerator # for making what follows a bit shorter
146
+ # optimized_parser = A.apply(parser,
147
+ # A.rule( (A.str(:x).absent? >> A.any).repeat ) { GobbleUp.new(x) })
148
+ #
149
+ # optimized_parser.parse('"Parsing is now fully optimized! (tm)"')
150
+ #
151
+ # @param atom [Parslet::Atoms::Base] a parser to optimize
152
+ # @param *rules [Parslet::Accelerator::Rule] rules produced by .rule
153
+ # @return [Parslet::Atoms::Base] optimized parser
154
+ #
155
+ def apply atom, *rules
156
+ Application.new(atom, rules).call
157
+ end
158
+ end
159
+
160
+ require 'parslet/accelerator/engine'
161
+ require 'parslet/accelerator/application'