plurimath-parslet 3.0.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.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.txt +284 -0
  3. data/LICENSE +23 -0
  4. data/README.adoc +454 -0
  5. data/Rakefile +71 -0
  6. data/lib/parslet/accelerator/application.rb +62 -0
  7. data/lib/parslet/accelerator/engine.rb +112 -0
  8. data/lib/parslet/accelerator.rb +162 -0
  9. data/lib/parslet/atoms/alternative.rb +53 -0
  10. data/lib/parslet/atoms/base.rb +157 -0
  11. data/lib/parslet/atoms/can_flatten.rb +137 -0
  12. data/lib/parslet/atoms/capture.rb +38 -0
  13. data/lib/parslet/atoms/context.rb +103 -0
  14. data/lib/parslet/atoms/dsl.rb +112 -0
  15. data/lib/parslet/atoms/dynamic.rb +32 -0
  16. data/lib/parslet/atoms/entity.rb +45 -0
  17. data/lib/parslet/atoms/ignored.rb +26 -0
  18. data/lib/parslet/atoms/infix.rb +115 -0
  19. data/lib/parslet/atoms/lookahead.rb +52 -0
  20. data/lib/parslet/atoms/named.rb +32 -0
  21. data/lib/parslet/atoms/re.rb +41 -0
  22. data/lib/parslet/atoms/repetition.rb +87 -0
  23. data/lib/parslet/atoms/scope.rb +26 -0
  24. data/lib/parslet/atoms/sequence.rb +48 -0
  25. data/lib/parslet/atoms/str.rb +42 -0
  26. data/lib/parslet/atoms/visitor.rb +89 -0
  27. data/lib/parslet/atoms.rb +34 -0
  28. data/lib/parslet/cause.rb +101 -0
  29. data/lib/parslet/context.rb +21 -0
  30. data/lib/parslet/convenience.rb +33 -0
  31. data/lib/parslet/error_reporter/contextual.rb +120 -0
  32. data/lib/parslet/error_reporter/deepest.rb +100 -0
  33. data/lib/parslet/error_reporter/tree.rb +63 -0
  34. data/lib/parslet/error_reporter.rb +8 -0
  35. data/lib/parslet/export.rb +163 -0
  36. data/lib/parslet/expression/treetop.rb +92 -0
  37. data/lib/parslet/expression.rb +51 -0
  38. data/lib/parslet/graphviz.rb +97 -0
  39. data/lib/parslet/parser.rb +68 -0
  40. data/lib/parslet/pattern/binding.rb +49 -0
  41. data/lib/parslet/pattern.rb +113 -0
  42. data/lib/parslet/position.rb +21 -0
  43. data/lib/parslet/rig/rspec.rb +52 -0
  44. data/lib/parslet/scope.rb +42 -0
  45. data/lib/parslet/slice.rb +105 -0
  46. data/lib/parslet/source/line_cache.rb +99 -0
  47. data/lib/parslet/source.rb +96 -0
  48. data/lib/parslet/transform.rb +265 -0
  49. data/lib/parslet/version.rb +5 -0
  50. data/lib/parslet.rb +314 -0
  51. data/plurimath-parslet.gemspec +42 -0
  52. data/spec/acceptance/infix_parser_spec.rb +145 -0
  53. data/spec/acceptance/mixing_parsers_spec.rb +74 -0
  54. data/spec/acceptance/regression_spec.rb +329 -0
  55. data/spec/acceptance/repetition_and_maybe_spec.rb +44 -0
  56. data/spec/acceptance/unconsumed_input_spec.rb +21 -0
  57. data/spec/examples/boolean_algebra_spec.rb +257 -0
  58. data/spec/examples/calc_spec.rb +278 -0
  59. data/spec/examples/capture_spec.rb +137 -0
  60. data/spec/examples/comments_spec.rb +186 -0
  61. data/spec/examples/deepest_errors_spec.rb +420 -0
  62. data/spec/examples/documentation_spec.rb +205 -0
  63. data/spec/examples/email_parser_spec.rb +275 -0
  64. data/spec/examples/empty_spec.rb +37 -0
  65. data/spec/examples/erb_spec.rb +482 -0
  66. data/spec/examples/ip_address_spec.rb +153 -0
  67. data/spec/examples/json_spec.rb +413 -0
  68. data/spec/examples/local_spec.rb +302 -0
  69. data/spec/examples/mathn_spec.rb +151 -0
  70. data/spec/examples/minilisp_spec.rb +492 -0
  71. data/spec/examples/modularity_spec.rb +340 -0
  72. data/spec/examples/nested_errors_spec.rb +322 -0
  73. data/spec/examples/optimized_erb_spec.rb +299 -0
  74. data/spec/examples/parens_spec.rb +239 -0
  75. data/spec/examples/prec_calc_spec.rb +525 -0
  76. data/spec/examples/readme_spec.rb +228 -0
  77. data/spec/examples/scopes_spec.rb +187 -0
  78. data/spec/examples/seasons_spec.rb +196 -0
  79. data/spec/examples/sentence_spec.rb +119 -0
  80. data/spec/examples/simple_xml_spec.rb +250 -0
  81. data/spec/examples/string_parser_spec.rb +407 -0
  82. data/spec/fixtures/examples/boolean_algebra.rb +62 -0
  83. data/spec/fixtures/examples/calc.rb +86 -0
  84. data/spec/fixtures/examples/capture.rb +36 -0
  85. data/spec/fixtures/examples/comments.rb +22 -0
  86. data/spec/fixtures/examples/deepest_errors.rb +99 -0
  87. data/spec/fixtures/examples/documentation.rb +32 -0
  88. data/spec/fixtures/examples/email_parser.rb +42 -0
  89. data/spec/fixtures/examples/empty.rb +10 -0
  90. data/spec/fixtures/examples/erb.rb +39 -0
  91. data/spec/fixtures/examples/ip_address.rb +103 -0
  92. data/spec/fixtures/examples/json.rb +107 -0
  93. data/spec/fixtures/examples/local.rb +60 -0
  94. data/spec/fixtures/examples/mathn.rb +47 -0
  95. data/spec/fixtures/examples/minilisp.rb +75 -0
  96. data/spec/fixtures/examples/modularity.rb +60 -0
  97. data/spec/fixtures/examples/nested_errors.rb +95 -0
  98. data/spec/fixtures/examples/optimized_erb.rb +105 -0
  99. data/spec/fixtures/examples/parens.rb +25 -0
  100. data/spec/fixtures/examples/prec_calc.rb +71 -0
  101. data/spec/fixtures/examples/readme.rb +59 -0
  102. data/spec/fixtures/examples/scopes.rb +43 -0
  103. data/spec/fixtures/examples/seasons.rb +40 -0
  104. data/spec/fixtures/examples/sentence.rb +18 -0
  105. data/spec/fixtures/examples/simple_xml.rb +51 -0
  106. data/spec/fixtures/examples/string_parser.rb +77 -0
  107. data/spec/parslet/atom_results_spec.rb +39 -0
  108. data/spec/parslet/atoms/alternative_spec.rb +26 -0
  109. data/spec/parslet/atoms/base_spec.rb +127 -0
  110. data/spec/parslet/atoms/capture_spec.rb +21 -0
  111. data/spec/parslet/atoms/combinations_spec.rb +5 -0
  112. data/spec/parslet/atoms/dsl_spec.rb +7 -0
  113. data/spec/parslet/atoms/entity_spec.rb +77 -0
  114. data/spec/parslet/atoms/ignored_spec.rb +15 -0
  115. data/spec/parslet/atoms/infix_spec.rb +5 -0
  116. data/spec/parslet/atoms/lookahead_spec.rb +22 -0
  117. data/spec/parslet/atoms/named_spec.rb +4 -0
  118. data/spec/parslet/atoms/re_spec.rb +14 -0
  119. data/spec/parslet/atoms/repetition_spec.rb +24 -0
  120. data/spec/parslet/atoms/scope_spec.rb +26 -0
  121. data/spec/parslet/atoms/sequence_spec.rb +28 -0
  122. data/spec/parslet/atoms/str_spec.rb +15 -0
  123. data/spec/parslet/atoms/visitor_spec.rb +101 -0
  124. data/spec/parslet/atoms_spec.rb +488 -0
  125. data/spec/parslet/convenience_spec.rb +54 -0
  126. data/spec/parslet/error_reporter/contextual_spec.rb +118 -0
  127. data/spec/parslet/error_reporter/deepest_spec.rb +82 -0
  128. data/spec/parslet/error_reporter/tree_spec.rb +7 -0
  129. data/spec/parslet/export_spec.rb +40 -0
  130. data/spec/parslet/expression/treetop_spec.rb +74 -0
  131. data/spec/parslet/minilisp.citrus +29 -0
  132. data/spec/parslet/minilisp.tt +29 -0
  133. data/spec/parslet/parser_spec.rb +36 -0
  134. data/spec/parslet/parslet_spec.rb +38 -0
  135. data/spec/parslet/pattern_spec.rb +272 -0
  136. data/spec/parslet/position_spec.rb +14 -0
  137. data/spec/parslet/rig/rspec_spec.rb +54 -0
  138. data/spec/parslet/scope_spec.rb +45 -0
  139. data/spec/parslet/slice_spec.rb +186 -0
  140. data/spec/parslet/source/line_cache_spec.rb +74 -0
  141. data/spec/parslet/source_spec.rb +210 -0
  142. data/spec/parslet/transform/context_spec.rb +56 -0
  143. data/spec/parslet/transform_spec.rb +183 -0
  144. data/spec/spec_helper.rb +74 -0
  145. data/spec/support/opal.rb +8 -0
  146. data/spec/support/opal.rb.erb +14 -0
  147. data/spec/support/parslet_matchers.rb +96 -0
  148. metadata +240 -0
@@ -0,0 +1,89 @@
1
+ # Augments all parslet atoms with an accept method that will call back
2
+ # to the visitor given.
3
+
4
+ #
5
+ module Parslet::Atoms
6
+ class Base
7
+ def accept(visitor)
8
+ raise NotImplementedError, "No #accept method on #{self.class.name}."
9
+ end
10
+ end
11
+
12
+ class Str
13
+ # Call back visitors #visit_str method. See parslet/export for an example.
14
+ #
15
+ def accept(visitor)
16
+ visitor.visit_str(str)
17
+ end
18
+ end
19
+
20
+ class Entity
21
+ # Call back visitors #visit_entity method. See parslet/export for an
22
+ # example.
23
+ #
24
+ def accept(visitor)
25
+ visitor.visit_entity(name, block)
26
+ end
27
+ end
28
+
29
+ class Named
30
+ # Call back visitors #visit_named method. See parslet/export for an
31
+ # example.
32
+ #
33
+ def accept(visitor)
34
+ visitor.visit_named(name, parslet)
35
+ end
36
+ end
37
+
38
+ class Sequence
39
+ # Call back visitors #visit_sequence method. See parslet/export for an
40
+ # example.
41
+ #
42
+ def accept(visitor)
43
+ visitor.visit_sequence(parslets)
44
+ end
45
+ end
46
+
47
+ class Repetition
48
+ # Call back visitors #visit_repetition method. See parslet/export for an
49
+ # example.
50
+ #
51
+ def accept(visitor)
52
+ visitor.visit_repetition(@tag, min, max, parslet)
53
+ end
54
+ end
55
+
56
+ class Alternative
57
+ # Call back visitors #visit_alternative method. See parslet/export for an
58
+ # example.
59
+ #
60
+ def accept(visitor)
61
+ visitor.visit_alternative(alternatives)
62
+ end
63
+ end
64
+
65
+ class Lookahead
66
+ # Call back visitors #visit_lookahead method. See parslet/export for an
67
+ # example.
68
+ #
69
+ def accept(visitor)
70
+ visitor.visit_lookahead(positive, bound_parslet)
71
+ end
72
+ end
73
+
74
+ class Re
75
+ # Call back visitors #visit_re method. See parslet/export for an example.
76
+ #
77
+ def accept(visitor)
78
+ visitor.visit_re(match)
79
+ end
80
+ end
81
+ end
82
+
83
+ class Parslet::Parser
84
+ # Call back visitors #visit_parser method.
85
+ #
86
+ def accept(visitor)
87
+ visitor.visit_parser(root)
88
+ end
89
+ end
@@ -0,0 +1,34 @@
1
+
2
+ # This is where parslets name comes from: Small parser atoms.
3
+ #
4
+ module Parslet::Atoms
5
+ # The precedence module controls parenthesis during the #inspect printing
6
+ # of parslets. It is not relevant to other aspects of the parsing.
7
+ #
8
+ module Precedence
9
+ BASE = 1 # everything else
10
+ LOOKAHEAD = 2 # &SOMETHING
11
+ REPETITION = 3 # 'a'+, 'a'?
12
+ SEQUENCE = 4 # 'a' 'b'
13
+ ALTERNATE = 5 # 'a' | 'b'
14
+ OUTER = 6 # printing is done here.
15
+ end
16
+
17
+ require 'parslet/atoms/can_flatten'
18
+ require 'parslet/atoms/context'
19
+ require 'parslet/atoms/dsl'
20
+ require 'parslet/atoms/base'
21
+ require 'parslet/atoms/ignored'
22
+ require 'parslet/atoms/named'
23
+ require 'parslet/atoms/lookahead'
24
+ require 'parslet/atoms/alternative'
25
+ require 'parslet/atoms/sequence'
26
+ require 'parslet/atoms/repetition'
27
+ require 'parslet/atoms/re'
28
+ require 'parslet/atoms/str'
29
+ require 'parslet/atoms/entity'
30
+ require 'parslet/atoms/capture'
31
+ require 'parslet/atoms/dynamic'
32
+ require 'parslet/atoms/scope'
33
+ require 'parslet/atoms/infix'
34
+ end
@@ -0,0 +1,101 @@
1
+ module Parslet
2
+ # Represents a cause why a parse did fail. A lot of these objects are
3
+ # constructed - not all of the causes turn out to be failures for the whole
4
+ # parse.
5
+ #
6
+ class Cause
7
+ def initialize(message, source, pos, children)
8
+ @message, @source, @pos, @children, @context =
9
+ message, source, pos, children, nil
10
+ end
11
+
12
+ # @return [String, Array] A string or an array of message pieces that
13
+ # provide failure information. Use #to_s to get a formatted string.
14
+ attr_reader :message
15
+
16
+ # @return [Parslet::Source] Source that was parsed when this error
17
+ # happend. Mainly used for line number information.
18
+ attr_reader :source
19
+
20
+ # Location of the error.
21
+ #
22
+ # @return [Fixnum] Position where the error happened. (character offset)
23
+ attr_reader :pos
24
+
25
+ # When this cause is part of a tree of error causes: child nodes for this
26
+ # node. Very often carries the reasons for this cause.
27
+ #
28
+ # @return [Array<Parslet::Cause>] A list of reasons for this cause.
29
+ def children
30
+ @children ||= []
31
+ end
32
+
33
+ # Appends 'at line LINE char CHAR' to the string given. Use +pos+ to
34
+ # override the position of the +source+. This method returns an object
35
+ # that can be turned into a string using #to_s.
36
+ #
37
+ # @param source [Parslet::Source] source that was parsed when this error
38
+ # happened
39
+ # @param pos [Fixnum] position of error
40
+ # @param str [String, Array<String>] message parts
41
+ # @param children [Array<Parslet::Cause>] child nodes for this error tree
42
+ # @return [Parslet::Cause] a new instance of {Parslet::Cause}
43
+ #
44
+ def self.format(source, pos, str, children=[])
45
+ self.new(str, source, pos, children)
46
+ end
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
+
55
+ def to_s
56
+ line, column = source.line_and_column(pos)
57
+ # Allow message to be a list of objects. Join them here, since we now
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}."
63
+ end
64
+
65
+ # Signals to the outside that the parse has failed. Use this in
66
+ # conjunction with .format for nice error messages.
67
+ #
68
+ def raise(exception_klass=Parslet::ParseFailed)
69
+ exception = exception_klass.new(self.to_s, self)
70
+ Kernel.raise exception
71
+ end
72
+
73
+ # Returns an ascii tree representation of the causes of this node and its
74
+ # children.
75
+ #
76
+ def ascii_tree
77
+ StringIO.new.tap { |io|
78
+ recursive_ascii_tree(self, io, [true]) }.
79
+ string
80
+ end
81
+
82
+ private
83
+ def recursive_ascii_tree(node, stream, curved)
84
+ append_prefix(stream, curved)
85
+ stream.puts node.to_s
86
+
87
+ node.children.each do |child|
88
+ last_child = (node.children.last == child)
89
+
90
+ recursive_ascii_tree(child, stream, curved + [last_child])
91
+ end
92
+ end
93
+ def append_prefix(stream, curved)
94
+ return if curved.size < 2
95
+ curved[1..-2].each do |c|
96
+ stream.print c ? " " : "| "
97
+ end
98
+ stream.print curved.last ? "`- " : "|- "
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,21 @@
1
+ # Provides a context for tree transformations to run in. The context allows
2
+ # accessing each of the bindings in the bindings hash as local method.
3
+ #
4
+ # Example:
5
+ #
6
+ # ctx = Context.new(:a => :b)
7
+ # ctx.instance_eval do
8
+ # a # => :b
9
+ # end
10
+ #
11
+ # @api private
12
+ class Parslet::Context
13
+ include Parslet
14
+
15
+ def initialize(bindings)
16
+ bindings.each do |key, value|
17
+ singleton_class.send(:define_method, key) { value }
18
+ instance_variable_set("@#{key}", value)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ class Parslet::Atoms::Base
2
+
3
+ # Packages the common idiom
4
+ #
5
+ # begin
6
+ # tree = parser.parse('something')
7
+ # rescue Parslet::ParseFailed => error
8
+ # puts parser.parse_failure_cause.ascii_tree
9
+ # end
10
+ #
11
+ # into a convenient method.
12
+ #
13
+ # Usage:
14
+ #
15
+ # require 'parslet'
16
+ # require 'parslet/convenience'
17
+ #
18
+ # class FooParser < Parslet::Parser
19
+ # rule(:foo) { str('foo') }
20
+ # root(:foo)
21
+ # end
22
+ #
23
+ # FooParser.new.parse_with_debug('bar')
24
+ #
25
+ # @see Parslet::Atoms::Base#parse
26
+ #
27
+ def parse_with_debug str, opts={}
28
+ parse str, opts
29
+ rescue Parslet::ParseFailed => error
30
+ puts error.parse_failure_cause.ascii_tree
31
+ end
32
+
33
+ end
@@ -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
@@ -0,0 +1,100 @@
1
+ module Parslet
2
+ module ErrorReporter
3
+ # Instead of reporting the latest error that happens like {Tree} does,
4
+ # this class reports the deepest error. Depth is defined here as how
5
+ # advanced into the input an error happens. The errors close to the
6
+ # greatest depth tend to be more relevant to the end user, since they
7
+ # specify what could be done to make them go away.
8
+ #
9
+ # More specifically, errors produced by this reporter won't be related to
10
+ # the structure of the grammar at all. The positions of the errors will
11
+ # be advanced and convey at every grammar level what the deepest rule
12
+ # was to fail.
13
+ #
14
+ class Deepest
15
+ def initialize
16
+ @deepest_cause = nil
17
+ end
18
+
19
+ # Produces an error cause that combines the message at the current level
20
+ # with the errors that happened at a level below (children).
21
+ #
22
+ # @param atom [Parslet::Atoms::Base] parslet that failed
23
+ # @param source [Source] Source that we're using for this parse. (line
24
+ # number information...)
25
+ # @param message [String, Array] Error message at this level.
26
+ # @param children [Array] A list of errors from a deeper level (or nil).
27
+ # @return [Cause] An error tree combining children with message.
28
+ #
29
+ def err(atom, source, message, children=nil)
30
+ position = source.pos
31
+ cause = Cause.format(source, position, message, children)
32
+ return deepest(cause)
33
+ end
34
+
35
+ # Produces an error cause that combines the message at the current level
36
+ # with the errors that happened at a level below (children).
37
+ #
38
+ # @param atom [Parslet::Atoms::Base] parslet that failed
39
+ # @param source [Source] Source that we're using for this parse. (line
40
+ # number information...)
41
+ # @param message [String, Array] Error message at this level.
42
+ # @param pos [Fixnum] The real position of the error.
43
+ # @param children [Array] A list of errors from a deeper level (or nil).
44
+ # @return [Cause] An error tree combining children with message.
45
+ #
46
+ def err_at(atom, source, message, pos, children=nil)
47
+ position = pos
48
+ cause = Cause.format(source, position, message, children)
49
+ return deepest(cause)
50
+ end
51
+
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
58
+ #
59
+ attr_reader :deepest_cause
60
+
61
+ # Checks to see if the lineage of the cause given includes a cause with
62
+ # an error position deeper than the current deepest cause stored. If
63
+ # yes, it passes the cause through to the caller. If no, it returns the
64
+ # current deepest error that was saved as a reference.
65
+ #
66
+ def deepest(cause)
67
+ _, leaf = deepest_child(cause)
68
+
69
+ if !deepest_cause || leaf.pos >= deepest_cause.pos
70
+ # This error reaches deeper into the input, save it as reference.
71
+ @deepest_cause = leaf
72
+ return cause
73
+ end
74
+
75
+ return deepest_cause
76
+ end
77
+
78
+ private
79
+ # Returns the leaf from a given error tree with the biggest rank.
80
+ #
81
+ def deepest_child(cause, rank=0)
82
+ max_child = cause
83
+ max_rank = rank
84
+
85
+ if cause.children && !cause.children.empty?
86
+ cause.children.each do |child|
87
+ c_rank, c_cause = deepest_child(child, rank+1)
88
+
89
+ if c_rank > max_rank
90
+ max_rank = c_rank
91
+ max_child = c_cause
92
+ end
93
+ end
94
+ end
95
+
96
+ return max_rank, max_child
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,63 @@
1
+ module Parslet
2
+ module ErrorReporter
3
+ # An error reporter has two central methods, one for reporting errors at
4
+ # the current parse position (#err) and one for reporting errors at a
5
+ # given parse position (#err_at). The reporter can return an object (a
6
+ # 'cause') that will be returned to the caller along with the information
7
+ # that the parse failed.
8
+ #
9
+ # When reporting errors on the outer levels of your parser, these methods
10
+ # get passed a list of error objects ('causes') from the inner levels. In
11
+ # this default implementation, the inner levels are considered error
12
+ # subtrees and are appended to the generated tree node at each level,
13
+ # thereby constructing an error tree.
14
+ #
15
+ # This error tree will report in parallel with the grammar structure that
16
+ # failed. A one-to-one correspondence exists between each error in the
17
+ # tree and the parslet atom that produced that error.
18
+ #
19
+ # The implementor is really free to use these return values as he sees
20
+ # fit. One example would be to return an error state object from these
21
+ # methods that is then updated as errors cascade up the parse derivation
22
+ # tree.
23
+ #
24
+ class Tree
25
+ # Produces an error cause that combines the message at the current level
26
+ # with the errors that happened at a level below (children).
27
+ #
28
+ # @param atom [Parslet::Atoms::Base] parslet that failed
29
+ # @param source [Source] Source that we're using for this parse. (line
30
+ # number information...)
31
+ # @param message [String, Array] Error message at this level.
32
+ # @param children [Array] A list of errors from a deeper level (or nil).
33
+ # @return [Cause] An error tree combining children with message.
34
+ #
35
+ def err(atom, source, message, children=nil)
36
+ position = source.pos
37
+ Cause.format(source, position, message, children)
38
+ end
39
+
40
+ # Produces an error cause that combines the message at the current level
41
+ # with the errors that happened at a level below (children).
42
+ #
43
+ # @param atom [Parslet::Atoms::Base] parslet that failed
44
+ # @param source [Source] Source that we're using for this parse. (line
45
+ # number information...)
46
+ # @param message [String, Array] Error message at this level.
47
+ # @param pos [Fixnum] The real position of the error.
48
+ # @param children [Array] A list of errors from a deeper level (or nil).
49
+ # @return [Cause] An error tree combining children with message.
50
+ #
51
+ def err_at(atom, source, message, pos, children=nil)
52
+ position = pos
53
+ Cause.format(source, position, message, children)
54
+ end
55
+
56
+ # Notification that an expression successfully parsed
57
+ # not used, see ErrorReporter::Contextual
58
+ def succ(source)
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,8 @@
1
+ # A namespace for all error reporters.
2
+ #
3
+ module Parslet::ErrorReporter
4
+ end
5
+
6
+ require 'parslet/error_reporter/tree'
7
+ require 'parslet/error_reporter/deepest'
8
+ require 'parslet/error_reporter/contextual'