parslet 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 48adca502f4d3fa08eeaa93ee6c2349866ec2028
4
- data.tar.gz: d74a18ffbf0fa05cc3b9c6ba42d112ce7fcb218e
3
+ metadata.gz: d212114155ea2fce120ce17157dcd10a9bb81ebf
4
+ data.tar.gz: 07ad0296a6bea037a87594ffc565429b39e6258b
5
5
  SHA512:
6
- metadata.gz: 0271bfb7536639b396e5203b52fa3d148f09534d08bd67d54c58efb4be4454be35761203b02270719bf32f4ab54313b63b3bb4321e4b993094248f36f7b7f88d
7
- data.tar.gz: 56a0e3845d0e0dd7a553c6d901c7ed37863c89d5140bd460285dea2e81d63aeb917190f80a7f29f7a914ac2cb17f301874db2fbb7d4f2bcfc1e6715645541b64
6
+ metadata.gz: 704d66fde31caf3741fa202d2ac56eaf410ec3ed0cf9050bc834af92a115d847a14e72d61cd97b44c60ba8bc1e50bf792cfadff3257e2b789a7bc9550d0b944a
7
+ data.tar.gz: 6ae7e679bfe78d0de4f6c66f4d096f2985b23ece311d5705deb5fe9098ce516ba4d6a7ecb2df7f34d942c7c2370dac3b075d0a6c168e887ac1bdbbdc020a21b2
@@ -3,9 +3,11 @@
3
3
  - prsnt? and absnt? are now finally banned into oblivion. Wasting vocals for
4
4
  the win.
5
5
 
6
- = 1.7 / ???
6
+ = 1.7 / 12Mar2015
7
7
 
8
8
  ! Small speed gains from improvements on the hot spots.
9
+ + Contextual error reporter, a flavor of error reporting that emphasizes
10
+ context.
9
11
 
10
12
  = 1.6 / 1May2014, 13Okt14
11
13
 
data/README CHANGED
@@ -11,7 +11,7 @@ the atoms of your language first; _parslet_ takes pride in making this
11
11
  possible.
12
12
 
13
13
  Eager to try this out? Please see the associated web site:
14
- http://kschiess.github.com/parslet
14
+ http://kschiess.github.io/parslet
15
15
 
16
16
  SYNOPSIS
17
17
 
@@ -99,7 +99,7 @@ module Parslet
99
99
  # root :twobar
100
100
  # end
101
101
  #
102
- def rule(name, &definition)
102
+ def rule(name, opts={}, &definition)
103
103
  define_method(name) do
104
104
  @rules ||= {} # <name, rule> memoization
105
105
  return @rules[name] if @rules.has_key?(name)
@@ -109,7 +109,7 @@ module Parslet
109
109
  self.instance_eval(&definition)
110
110
  }
111
111
 
112
- @rules[name] = Atoms::Entity.new(name, &definition_closure)
112
+ @rules[name] = Atoms::Entity.new(name, opts[:label], &definition_closure)
113
113
  end
114
114
  end
115
115
  end
@@ -7,7 +7,10 @@ class Parslet::Atoms::Base
7
7
  include Parslet::Atoms::Precedence
8
8
  include Parslet::Atoms::DSL
9
9
  include Parslet::Atoms::CanFlatten
10
-
10
+
11
+ # Parslet label as provided in grammar
12
+ attr_accessor :label
13
+
11
14
  # Given a string or an IO object, this will attempt a parse of its contents
12
15
  # and return a result. If the parse fails, a Parslet::ParseFailed exception
13
16
  # will be thrown.
@@ -83,6 +86,8 @@ class Parslet::Atoms::Base
83
86
  success, value = result = context.try_with_cache(self, source, consume_all)
84
87
 
85
88
  if success
89
+ # Notify context
90
+ context.succ(source)
86
91
  # If a consume_all parse was made and doesn't result in the consumption
87
92
  # of all the input, that is considered an error.
88
93
  if consume_all && source.chars_left>0
@@ -132,10 +137,11 @@ class Parslet::Atoms::Base
132
137
  end
133
138
  precedence BASE
134
139
  def to_s(outer_prec=OUTER)
140
+ str = @label || to_s_inner(precedence)
135
141
  if outer_prec < precedence
136
- "("+to_s_inner(precedence)+")"
142
+ "(#{str})"
137
143
  else
138
- to_s_inner(precedence)
144
+ str
139
145
  end
140
146
  end
141
147
  def inspect
@@ -62,7 +62,15 @@ module Parslet::Atoms
62
62
  return [false, @reporter.err(*args)] if @reporter
63
63
  return [false, nil]
64
64
  end
65
-
65
+
66
+ # Report a successful parse.
67
+ # @see ErrorReporter::Contextual
68
+ #
69
+ def succ(*args)
70
+ return [true, @reporter.succ(*args)] if @reporter
71
+ return [true, nil]
72
+ end
73
+
66
74
  # Returns the current captures made on the input (see
67
75
  # Parslet::Atoms::Base#capture). Use as follows:
68
76
  #
@@ -5,15 +5,16 @@
5
5
  # * Be able to print things by their name, not by their sometimes
6
6
  # complicated content.
7
7
  #
8
- # You don't normally use this directly, instead you should generated it by
8
+ # You don't normally use this directly, instead you should generate it by
9
9
  # using the structuring method Parslet.rule.
10
10
  #
11
11
  class Parslet::Atoms::Entity < Parslet::Atoms::Base
12
12
  attr_reader :name, :block
13
- def initialize(name, &block)
13
+ def initialize(name, label=nil, &block)
14
14
  super()
15
15
 
16
16
  @name = name
17
+ @label = label
17
18
  @block = block
18
19
  end
19
20
 
@@ -22,9 +23,11 @@ class Parslet::Atoms::Entity < Parslet::Atoms::Base
22
23
  end
23
24
 
24
25
  def parslet
25
- @parslet ||= @block.call.tap { |p|
26
- raise_not_implemented unless p
27
- }
26
+ return @parslet unless @parslet.nil?
27
+ @parslet = @block.call
28
+ raise_not_implemented if @parslet.nil?
29
+ @parslet.label = @label
30
+ @parslet
28
31
  end
29
32
 
30
33
  def to_s_inner(prec)
@@ -31,7 +31,7 @@ class Parslet::Atoms::Sequence < Parslet::Atoms::Base
31
31
  unless success
32
32
  return context.err(self, source, @error_msgs[:failed], [value])
33
33
  end
34
-
34
+
35
35
  result[idx+1] = value
36
36
  end
37
37
 
@@ -44,15 +44,22 @@ module Parslet
44
44
  def self.format(source, pos, str, children=[])
45
45
  self.new(str, source, pos, children)
46
46
  end
47
-
47
+
48
+ # Update error message to include context provided by label
49
+ # Update all child causes too (the same context applies to all causes)
50
+ def set_label(l)
51
+ @context = " when parsing #{l}"
52
+ children.each { |c| c.set_label(l) }
53
+ end
54
+
48
55
  def to_s
49
56
  line, column = source.line_and_column(pos)
50
57
  # Allow message to be a list of objects. Join them here, since we now
51
- # really need it.
52
- Array(message).map { |o|
53
- o.respond_to?(:to_slice) ?
54
- o.str.inspect :
55
- o.to_s }.join + " at line #{line} char #{column}."
58
+ # really need it.
59
+ Array(message).map { |o|
60
+ o.respond_to?(:to_slice) ?
61
+ o.str.inspect :
62
+ o.to_s }.join + " at line #{line} char #{column}#{@context}."
56
63
  end
57
64
 
58
65
  # Signals to the outside that the parse has failed. Use this in
@@ -4,4 +4,5 @@ module Parslet::ErrorReporter
4
4
  end
5
5
 
6
6
  require 'parslet/error_reporter/tree'
7
- require 'parslet/error_reporter/deepest'
7
+ require 'parslet/error_reporter/deepest'
8
+ require 'parslet/error_reporter/contextual'
@@ -0,0 +1,120 @@
1
+ module Parslet
2
+ module ErrorReporter
3
+
4
+ # A reporter that tries to improve on the deepest error reporter by
5
+ # using heuristics to find the most relevant error and provide more
6
+ # context.
7
+ # The heuristic chooses the deepest error when parsing a sequence for which
8
+ # no alternative parsed successfully.
9
+ #
10
+ # Given the following parser:
11
+ #
12
+ # root(:call)
13
+ #
14
+ # rule(:call, label: 'call') {
15
+ # identifier >> str('.') >> method
16
+ # }
17
+ #
18
+ # rule(:method, label: 'method call') {
19
+ # identifier >> str('(') >> arguments.maybe >> str(')')
20
+ # }
21
+ #
22
+ # rule(:identifier, label: 'identifier') {
23
+ # match['[:alnum:]'].repeat(1)
24
+ # }
25
+ #
26
+ # rule(:arguments, label: 'method call arguments') {
27
+ # argument >> str(',') >> arguments | argument
28
+ # }
29
+ #
30
+ # rule(:argument) {
31
+ # call | identifier
32
+ # }
33
+ #
34
+ # and the following source:
35
+ #
36
+ # foo.bar(a,goo.baz(),c,)
37
+ #
38
+ # The contextual reporter returns the following causes:
39
+ #
40
+ # 0: Failed to match sequence (identifier '.' method call) at line 1 char 5
41
+ # when parsing method call arguments.
42
+ # 1: Failed to match sequence (identifier '(' method call arguments? ')') at
43
+ # line 1 char 22 when parsing method call arguments.
44
+ # 2: Failed to match [[:alnum:]] at line 1 char 23 when parsing method call
45
+ # arguments.
46
+ #
47
+ # (where 2 is a child cause of 1 and 1 a child cause of 0)
48
+ #
49
+ # The last piece used by the reporter is the (newly introduced) ability
50
+ # to attach a label to rules that describe a sequence in the grammar. The
51
+ # labels are used in two places:
52
+ # - In the "to_s" of Atom::Base so that any error message uses labels to
53
+ # refer to atoms
54
+ # - In the cause error messages to give information about which expression
55
+ # failed to parse
56
+ #
57
+ class Contextual < Deepest
58
+
59
+ def initialize
60
+ @last_reset_pos = 0
61
+ reset
62
+ end
63
+
64
+ # A sequence expression successfully parsed, reset all errors reported
65
+ # for previous expressions in the sequence (an alternative matched)
66
+ # Only reset errors if the position of the source that matched is higher
67
+ # than the position of the source that was last successful (so we keep
68
+ # errors that are the "deepest" but for which no alternative succeeded)
69
+ #
70
+ def succ(source)
71
+ source_pos = source.pos.bytepos
72
+ return if source_pos < @last_reset_pos
73
+ @last_reset_pos = source_pos
74
+ reset
75
+ end
76
+
77
+ # Reset deepest error and its position and sequence index
78
+ #
79
+ def reset
80
+ @deepest_cause = nil
81
+ @label_pos = -1
82
+ end
83
+
84
+ # Produces an error cause that combines the message at the current level
85
+ # with the errors that happened at a level below (children).
86
+ # Compute and set label used by Cause to produce error message.
87
+ #
88
+ # @param atom [Parslet::Atoms::Base] parslet that failed
89
+ # @param source [Source] Source that we're using for this parse. (line
90
+ # number information...)
91
+ # @param message [String, Array] Error message at this level.
92
+ # @param children [Array] A list of errors from a deeper level (or nil).
93
+ # @return [Cause] An error tree combining children with message.
94
+ #
95
+ def err(atom, source, message, children=nil)
96
+ cause = super(atom, source, message, children)
97
+ if (label = atom.respond_to?(:label) && atom.label)
98
+ update_label(label, source.pos.bytepos)
99
+ cause.set_label(@label)
100
+ end
101
+ cause
102
+ end
103
+
104
+ # Update error message label if given label is more relevant.
105
+ # A label is more relevant if the position of the matched source is
106
+ # bigger.
107
+ #
108
+ # @param label [String] label to apply if more relevant
109
+ # @param bytepos [Integer] position in source code of matched source
110
+ #
111
+ def update_label(label, bytepos)
112
+ if bytepos >= @label_pos
113
+ @label_pos = bytepos
114
+ @label = label
115
+ end
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -50,6 +50,11 @@ module Parslet
50
50
  end
51
51
 
52
52
  # Returns the cause that is currently deepest. Mainly for specs.
53
+
54
+ # Notification that an expression successfully parsed
55
+ # not used, see ErrorReporter::Contextual
56
+ def succ(source)
57
+ end
53
58
  #
54
59
  attr_reader :deepest_cause
55
60
 
@@ -52,6 +52,12 @@ module Parslet
52
52
  position = pos
53
53
  Cause.format(source, position, message, children)
54
54
  end
55
+
56
+ # Notification that an expression successfully parsed
57
+ # not used, see ErrorReporter::Contextual
58
+ def succ(source)
59
+ end
60
+
55
61
  end
56
62
  end
57
- end
63
+ end
@@ -57,6 +57,8 @@ class Parslet::Slice
57
57
  def size
58
58
  str.size
59
59
  end
60
+
61
+ alias length size
60
62
 
61
63
  # Concatenate two slices; it is assumed that the second slice begins
62
64
  # where the first one ends. The offset of the resulting slice is the same
@@ -105,4 +107,4 @@ class Parslet::Slice
105
107
 
106
108
  str.inspect << "@#{offset}"
107
109
  end
108
- end
110
+ end
@@ -124,7 +124,8 @@ class Parslet::Transform
124
124
  #
125
125
  def rule(expression, &block)
126
126
  @__transform_rules ||= []
127
- @__transform_rules << [Parslet::Pattern.new(expression), block]
127
+ # Prepend new rules so they have higher precedence than older rules
128
+ @__transform_rules.unshift([Parslet::Pattern.new(expression), block])
128
129
  end
129
130
 
130
131
  # Allows accessing the class' rules
@@ -132,6 +133,11 @@ class Parslet::Transform
132
133
  def rules
133
134
  @__transform_rules || []
134
135
  end
136
+
137
+ def inherited(subclass)
138
+ super
139
+ subclass.instance_variable_set(:@__transform_rules, rules.dup)
140
+ end
135
141
  end
136
142
 
137
143
  def initialize(&block)
@@ -149,16 +155,31 @@ class Parslet::Transform
149
155
  # * a *transformation block*
150
156
  #
151
157
  def rule(expression, &block)
152
- @rules << [
153
- Parslet::Pattern.new(expression),
154
- block
155
- ]
158
+ # Prepend new rules so they have higher precedence than older rules
159
+ @rules.unshift([Parslet::Pattern.new(expression), block])
156
160
  end
157
161
 
158
162
  # Applies the transformation to a tree that is generated by Parslet::Parser
159
163
  # or a simple parslet. Transformation will proceed down the tree, replacing
160
164
  # parts/all of it with new objects. The resulting object will be returned.
161
165
  #
166
+ # Using the context parameter, you can inject bindings for the transformation.
167
+ # This can be used to allow access to the outside world from transform blocks,
168
+ # like so:
169
+ #
170
+ # document = # some class that you act on
171
+ # transform.apply(tree, document: document)
172
+ #
173
+ # The above will make document available to all your action blocks:
174
+ #
175
+ # # Variant A
176
+ # rule(...) { document.foo(bar) }
177
+ # # Variant B
178
+ # rule(...) { |d| d[:document].foo(d[:bar]) }
179
+ #
180
+ # @param obj PORO ast to transform
181
+ # @param context start context to inject into the bindings.
182
+ #
162
183
  def apply(obj, context=nil)
163
184
  transform_elt(
164
185
  case obj
metadata CHANGED
@@ -1,33 +1,33 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parslet
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.2
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaspar Schiess
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-13 00:00:00.000000000 Z
11
+ date: 2015-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: blankslate
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '2.0'
20
- - - <=
20
+ - - "<="
21
21
  - !ruby/object:Gem::Version
22
22
  version: '4.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - '>='
27
+ - - ">="
28
28
  - !ruby/object:Gem::Version
29
29
  version: '2.0'
30
- - - <=
30
+ - - "<="
31
31
  - !ruby/object:Gem::Version
32
32
  version: '4.0'
33
33
  description:
@@ -39,50 +39,8 @@ extra_rdoc_files:
39
39
  files:
40
40
  - HISTORY.txt
41
41
  - LICENSE
42
- - Rakefile
43
42
  - README
44
- - lib/parslet/accelerator/application.rb
45
- - lib/parslet/accelerator/engine.rb
46
- - lib/parslet/accelerator.rb
47
- - lib/parslet/atoms/alternative.rb
48
- - lib/parslet/atoms/base.rb
49
- - lib/parslet/atoms/can_flatten.rb
50
- - lib/parslet/atoms/capture.rb
51
- - lib/parslet/atoms/context.rb
52
- - lib/parslet/atoms/dsl.rb
53
- - lib/parslet/atoms/dynamic.rb
54
- - lib/parslet/atoms/entity.rb
55
- - lib/parslet/atoms/infix.rb
56
- - lib/parslet/atoms/lookahead.rb
57
- - lib/parslet/atoms/named.rb
58
- - lib/parslet/atoms/re.rb
59
- - lib/parslet/atoms/repetition.rb
60
- - lib/parslet/atoms/scope.rb
61
- - lib/parslet/atoms/sequence.rb
62
- - lib/parslet/atoms/str.rb
63
- - lib/parslet/atoms/visitor.rb
64
- - lib/parslet/atoms.rb
65
- - lib/parslet/cause.rb
66
- - lib/parslet/context.rb
67
- - lib/parslet/convenience.rb
68
- - lib/parslet/error_reporter/deepest.rb
69
- - lib/parslet/error_reporter/tree.rb
70
- - lib/parslet/error_reporter.rb
71
- - lib/parslet/export.rb
72
- - lib/parslet/expression/treetop.rb
73
- - lib/parslet/expression.rb
74
- - lib/parslet/graphviz.rb
75
- - lib/parslet/parser.rb
76
- - lib/parslet/pattern/binding.rb
77
- - lib/parslet/pattern.rb
78
- - lib/parslet/position.rb
79
- - lib/parslet/rig/rspec.rb
80
- - lib/parslet/scope.rb
81
- - lib/parslet/slice.rb
82
- - lib/parslet/source/line_cache.rb
83
- - lib/parslet/source.rb
84
- - lib/parslet/transform.rb
85
- - lib/parslet.rb
43
+ - Rakefile
86
44
  - example/big.erb
87
45
  - example/boolean_algebra.rb
88
46
  - example/calc.rb
@@ -140,30 +98,74 @@ files:
140
98
  - example/simple_xml.rb
141
99
  - example/string_parser.rb
142
100
  - example/test.lit
143
- homepage: http://kschiess.github.com/parslet
101
+ - lib/parslet.rb
102
+ - lib/parslet/accelerator.rb
103
+ - lib/parslet/accelerator/application.rb
104
+ - lib/parslet/accelerator/engine.rb
105
+ - lib/parslet/atoms.rb
106
+ - lib/parslet/atoms/alternative.rb
107
+ - lib/parslet/atoms/base.rb
108
+ - lib/parslet/atoms/can_flatten.rb
109
+ - lib/parslet/atoms/capture.rb
110
+ - lib/parslet/atoms/context.rb
111
+ - lib/parslet/atoms/dsl.rb
112
+ - lib/parslet/atoms/dynamic.rb
113
+ - lib/parslet/atoms/entity.rb
114
+ - lib/parslet/atoms/infix.rb
115
+ - lib/parslet/atoms/lookahead.rb
116
+ - lib/parslet/atoms/named.rb
117
+ - lib/parslet/atoms/re.rb
118
+ - lib/parslet/atoms/repetition.rb
119
+ - lib/parslet/atoms/scope.rb
120
+ - lib/parslet/atoms/sequence.rb
121
+ - lib/parslet/atoms/str.rb
122
+ - lib/parslet/atoms/visitor.rb
123
+ - lib/parslet/cause.rb
124
+ - lib/parslet/context.rb
125
+ - lib/parslet/convenience.rb
126
+ - lib/parslet/error_reporter.rb
127
+ - lib/parslet/error_reporter/contextual.rb
128
+ - lib/parslet/error_reporter/deepest.rb
129
+ - lib/parslet/error_reporter/tree.rb
130
+ - lib/parslet/export.rb
131
+ - lib/parslet/expression.rb
132
+ - lib/parslet/expression/treetop.rb
133
+ - lib/parslet/graphviz.rb
134
+ - lib/parslet/parser.rb
135
+ - lib/parslet/pattern.rb
136
+ - lib/parslet/pattern/binding.rb
137
+ - lib/parslet/position.rb
138
+ - lib/parslet/rig/rspec.rb
139
+ - lib/parslet/scope.rb
140
+ - lib/parslet/slice.rb
141
+ - lib/parslet/source.rb
142
+ - lib/parslet/source/line_cache.rb
143
+ - lib/parslet/transform.rb
144
+ homepage: http://kschiess.github.io/parslet
144
145
  licenses:
145
146
  - MIT
146
147
  metadata: {}
147
148
  post_install_message:
148
149
  rdoc_options:
149
- - --main
150
+ - "--main"
150
151
  - README
151
152
  require_paths:
152
153
  - lib
153
154
  required_ruby_version: !ruby/object:Gem::Requirement
154
155
  requirements:
155
- - - '>='
156
+ - - ">="
156
157
  - !ruby/object:Gem::Version
157
158
  version: '0'
158
159
  required_rubygems_version: !ruby/object:Gem::Requirement
159
160
  requirements:
160
- - - '>='
161
+ - - ">="
161
162
  - !ruby/object:Gem::Version
162
163
  version: '0'
163
164
  requirements: []
164
165
  rubyforge_project:
165
- rubygems_version: 2.0.14
166
+ rubygems_version: 2.2.2
166
167
  signing_key:
167
168
  specification_version: 4
168
169
  summary: Parser construction library with great error reporting in Ruby.
169
170
  test_files: []
171
+ has_rdoc: