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.
- checksums.yaml +7 -0
- data/HISTORY.txt +284 -0
- data/LICENSE +23 -0
- data/README.adoc +454 -0
- data/Rakefile +71 -0
- data/lib/parslet/accelerator/application.rb +62 -0
- data/lib/parslet/accelerator/engine.rb +112 -0
- data/lib/parslet/accelerator.rb +162 -0
- data/lib/parslet/atoms/alternative.rb +53 -0
- data/lib/parslet/atoms/base.rb +157 -0
- data/lib/parslet/atoms/can_flatten.rb +137 -0
- data/lib/parslet/atoms/capture.rb +38 -0
- data/lib/parslet/atoms/context.rb +103 -0
- data/lib/parslet/atoms/dsl.rb +112 -0
- data/lib/parslet/atoms/dynamic.rb +32 -0
- data/lib/parslet/atoms/entity.rb +45 -0
- data/lib/parslet/atoms/ignored.rb +26 -0
- data/lib/parslet/atoms/infix.rb +115 -0
- data/lib/parslet/atoms/lookahead.rb +52 -0
- data/lib/parslet/atoms/named.rb +32 -0
- data/lib/parslet/atoms/re.rb +41 -0
- data/lib/parslet/atoms/repetition.rb +87 -0
- data/lib/parslet/atoms/scope.rb +26 -0
- data/lib/parslet/atoms/sequence.rb +48 -0
- data/lib/parslet/atoms/str.rb +42 -0
- data/lib/parslet/atoms/visitor.rb +89 -0
- data/lib/parslet/atoms.rb +34 -0
- data/lib/parslet/cause.rb +101 -0
- data/lib/parslet/context.rb +21 -0
- data/lib/parslet/convenience.rb +33 -0
- data/lib/parslet/error_reporter/contextual.rb +120 -0
- data/lib/parslet/error_reporter/deepest.rb +100 -0
- data/lib/parslet/error_reporter/tree.rb +63 -0
- data/lib/parslet/error_reporter.rb +8 -0
- data/lib/parslet/export.rb +163 -0
- data/lib/parslet/expression/treetop.rb +92 -0
- data/lib/parslet/expression.rb +51 -0
- data/lib/parslet/graphviz.rb +97 -0
- data/lib/parslet/parser.rb +68 -0
- data/lib/parslet/pattern/binding.rb +49 -0
- data/lib/parslet/pattern.rb +113 -0
- data/lib/parslet/position.rb +21 -0
- data/lib/parslet/rig/rspec.rb +52 -0
- data/lib/parslet/scope.rb +42 -0
- data/lib/parslet/slice.rb +105 -0
- data/lib/parslet/source/line_cache.rb +99 -0
- data/lib/parslet/source.rb +96 -0
- data/lib/parslet/transform.rb +265 -0
- data/lib/parslet/version.rb +5 -0
- data/lib/parslet.rb +314 -0
- data/plurimath-parslet.gemspec +42 -0
- data/spec/acceptance/infix_parser_spec.rb +145 -0
- data/spec/acceptance/mixing_parsers_spec.rb +74 -0
- data/spec/acceptance/regression_spec.rb +329 -0
- data/spec/acceptance/repetition_and_maybe_spec.rb +44 -0
- data/spec/acceptance/unconsumed_input_spec.rb +21 -0
- data/spec/examples/boolean_algebra_spec.rb +257 -0
- data/spec/examples/calc_spec.rb +278 -0
- data/spec/examples/capture_spec.rb +137 -0
- data/spec/examples/comments_spec.rb +186 -0
- data/spec/examples/deepest_errors_spec.rb +420 -0
- data/spec/examples/documentation_spec.rb +205 -0
- data/spec/examples/email_parser_spec.rb +275 -0
- data/spec/examples/empty_spec.rb +37 -0
- data/spec/examples/erb_spec.rb +482 -0
- data/spec/examples/ip_address_spec.rb +153 -0
- data/spec/examples/json_spec.rb +413 -0
- data/spec/examples/local_spec.rb +302 -0
- data/spec/examples/mathn_spec.rb +151 -0
- data/spec/examples/minilisp_spec.rb +492 -0
- data/spec/examples/modularity_spec.rb +340 -0
- data/spec/examples/nested_errors_spec.rb +322 -0
- data/spec/examples/optimized_erb_spec.rb +299 -0
- data/spec/examples/parens_spec.rb +239 -0
- data/spec/examples/prec_calc_spec.rb +525 -0
- data/spec/examples/readme_spec.rb +228 -0
- data/spec/examples/scopes_spec.rb +187 -0
- data/spec/examples/seasons_spec.rb +196 -0
- data/spec/examples/sentence_spec.rb +119 -0
- data/spec/examples/simple_xml_spec.rb +250 -0
- data/spec/examples/string_parser_spec.rb +407 -0
- data/spec/fixtures/examples/boolean_algebra.rb +62 -0
- data/spec/fixtures/examples/calc.rb +86 -0
- data/spec/fixtures/examples/capture.rb +36 -0
- data/spec/fixtures/examples/comments.rb +22 -0
- data/spec/fixtures/examples/deepest_errors.rb +99 -0
- data/spec/fixtures/examples/documentation.rb +32 -0
- data/spec/fixtures/examples/email_parser.rb +42 -0
- data/spec/fixtures/examples/empty.rb +10 -0
- data/spec/fixtures/examples/erb.rb +39 -0
- data/spec/fixtures/examples/ip_address.rb +103 -0
- data/spec/fixtures/examples/json.rb +107 -0
- data/spec/fixtures/examples/local.rb +60 -0
- data/spec/fixtures/examples/mathn.rb +47 -0
- data/spec/fixtures/examples/minilisp.rb +75 -0
- data/spec/fixtures/examples/modularity.rb +60 -0
- data/spec/fixtures/examples/nested_errors.rb +95 -0
- data/spec/fixtures/examples/optimized_erb.rb +105 -0
- data/spec/fixtures/examples/parens.rb +25 -0
- data/spec/fixtures/examples/prec_calc.rb +71 -0
- data/spec/fixtures/examples/readme.rb +59 -0
- data/spec/fixtures/examples/scopes.rb +43 -0
- data/spec/fixtures/examples/seasons.rb +40 -0
- data/spec/fixtures/examples/sentence.rb +18 -0
- data/spec/fixtures/examples/simple_xml.rb +51 -0
- data/spec/fixtures/examples/string_parser.rb +77 -0
- data/spec/parslet/atom_results_spec.rb +39 -0
- data/spec/parslet/atoms/alternative_spec.rb +26 -0
- data/spec/parslet/atoms/base_spec.rb +127 -0
- data/spec/parslet/atoms/capture_spec.rb +21 -0
- data/spec/parslet/atoms/combinations_spec.rb +5 -0
- data/spec/parslet/atoms/dsl_spec.rb +7 -0
- data/spec/parslet/atoms/entity_spec.rb +77 -0
- data/spec/parslet/atoms/ignored_spec.rb +15 -0
- data/spec/parslet/atoms/infix_spec.rb +5 -0
- data/spec/parslet/atoms/lookahead_spec.rb +22 -0
- data/spec/parslet/atoms/named_spec.rb +4 -0
- data/spec/parslet/atoms/re_spec.rb +14 -0
- data/spec/parslet/atoms/repetition_spec.rb +24 -0
- data/spec/parslet/atoms/scope_spec.rb +26 -0
- data/spec/parslet/atoms/sequence_spec.rb +28 -0
- data/spec/parslet/atoms/str_spec.rb +15 -0
- data/spec/parslet/atoms/visitor_spec.rb +101 -0
- data/spec/parslet/atoms_spec.rb +488 -0
- data/spec/parslet/convenience_spec.rb +54 -0
- data/spec/parslet/error_reporter/contextual_spec.rb +118 -0
- data/spec/parslet/error_reporter/deepest_spec.rb +82 -0
- data/spec/parslet/error_reporter/tree_spec.rb +7 -0
- data/spec/parslet/export_spec.rb +40 -0
- data/spec/parslet/expression/treetop_spec.rb +74 -0
- data/spec/parslet/minilisp.citrus +29 -0
- data/spec/parslet/minilisp.tt +29 -0
- data/spec/parslet/parser_spec.rb +36 -0
- data/spec/parslet/parslet_spec.rb +38 -0
- data/spec/parslet/pattern_spec.rb +272 -0
- data/spec/parslet/position_spec.rb +14 -0
- data/spec/parslet/rig/rspec_spec.rb +54 -0
- data/spec/parslet/scope_spec.rb +45 -0
- data/spec/parslet/slice_spec.rb +186 -0
- data/spec/parslet/source/line_cache_spec.rb +74 -0
- data/spec/parslet/source_spec.rb +210 -0
- data/spec/parslet/transform/context_spec.rb +56 -0
- data/spec/parslet/transform_spec.rb +183 -0
- data/spec/spec_helper.rb +74 -0
- data/spec/support/opal.rb +8 -0
- data/spec/support/opal.rb.erb +14 -0
- data/spec/support/parslet_matchers.rb +96 -0
- metadata +240 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
require 'parslet/convenience'
|
3
|
+
|
4
|
+
# This example demonstrates tree error reporting in a real life example.
|
5
|
+
# The parser code has been contributed by John Mettraux.
|
6
|
+
|
7
|
+
module NestedErrorsExample
|
8
|
+
def self.prettify(str)
|
9
|
+
lines = []
|
10
|
+
lines << " "*3 + " "*4 + "." + " "*4 + "10" + " "*3 + "." + " "*4 + "20"
|
11
|
+
str.lines.each_with_index do |line, index|
|
12
|
+
lines << sprintf("%02d %s", index+1, line.chomp)
|
13
|
+
end
|
14
|
+
lines.join("\n")
|
15
|
+
end
|
16
|
+
|
17
|
+
class Parser < Parslet::Parser
|
18
|
+
# commons
|
19
|
+
rule(:space) { match('[ \t]').repeat(1) }
|
20
|
+
rule(:space?) { space.maybe }
|
21
|
+
|
22
|
+
rule(:newline) { match('[\r\n]') }
|
23
|
+
|
24
|
+
rule(:comment) { str('#') >> match('[^\r\n]').repeat }
|
25
|
+
|
26
|
+
rule(:line_separator) {
|
27
|
+
(space? >> ((comment.maybe >> newline) | str(';')) >> space?).repeat(1)
|
28
|
+
}
|
29
|
+
|
30
|
+
rule(:blank) { line_separator | space }
|
31
|
+
rule(:blank?) { blank.maybe }
|
32
|
+
|
33
|
+
rule(:identifier) { match('[a-zA-Z0-9_]').repeat(1) }
|
34
|
+
|
35
|
+
# res_statement
|
36
|
+
rule(:reference) {
|
37
|
+
(str('@').repeat(1,2) >> identifier).as(:reference)
|
38
|
+
}
|
39
|
+
|
40
|
+
rule(:res_action_or_link) {
|
41
|
+
str('.').as(:dot) >> (identifier >> str('?').maybe ).as(:name) >> str('()')
|
42
|
+
}
|
43
|
+
|
44
|
+
rule(:res_actions) {
|
45
|
+
(
|
46
|
+
reference
|
47
|
+
).as(:resources) >>
|
48
|
+
(
|
49
|
+
res_action_or_link.as(:res_action)
|
50
|
+
).repeat(0).as(:res_actions)
|
51
|
+
}
|
52
|
+
|
53
|
+
rule(:res_statement) {
|
54
|
+
res_actions >>
|
55
|
+
(str(':') >> identifier.as(:name)).maybe.as(:res_field)
|
56
|
+
}
|
57
|
+
|
58
|
+
# expression
|
59
|
+
rule(:expression) {
|
60
|
+
res_statement
|
61
|
+
}
|
62
|
+
|
63
|
+
# body
|
64
|
+
rule(:body) {
|
65
|
+
(line_separator >> (block | expression)).repeat(1).as(:body) >>
|
66
|
+
line_separator
|
67
|
+
}
|
68
|
+
|
69
|
+
# blocks
|
70
|
+
rule(:begin_block) {
|
71
|
+
(str('concurrent').as(:type) >> space).maybe.as(:pre) >>
|
72
|
+
str('begin').as(:begin) >>
|
73
|
+
body >>
|
74
|
+
str('end')
|
75
|
+
}
|
76
|
+
|
77
|
+
rule(:define_block) {
|
78
|
+
str('define').as(:define) >> space >>
|
79
|
+
identifier.as(:name) >> str('()') >>
|
80
|
+
body >>
|
81
|
+
str('end')
|
82
|
+
}
|
83
|
+
|
84
|
+
rule(:block) {
|
85
|
+
define_block | begin_block
|
86
|
+
}
|
87
|
+
|
88
|
+
# root
|
89
|
+
rule(:radix) {
|
90
|
+
line_separator.maybe >> block >> line_separator.maybe
|
91
|
+
}
|
92
|
+
|
93
|
+
root(:radix)
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
# This example shows how to optimize an ERB like parser using parslet.
|
4
|
+
# Please also look at the more naive 'erb.rb' for comparison.
|
5
|
+
|
6
|
+
module OptimizedErbExample
|
7
|
+
class ErbParser < Parslet::Parser
|
8
|
+
rule(:ruby) { (str('%>').absent? >> any).repeat.as(:ruby) }
|
9
|
+
|
10
|
+
rule(:expression) { (str('=') >> ruby).as(:expression) }
|
11
|
+
rule(:comment) { (str('#') >> ruby).as(:comment) }
|
12
|
+
rule(:code) { ruby.as(:code) }
|
13
|
+
rule(:erb) { expression | comment | code }
|
14
|
+
|
15
|
+
rule(:erb_with_tags) { str('<%') >> erb >> str('%>') }
|
16
|
+
rule(:text) { (str('<%').absent? >> any).repeat(1) }
|
17
|
+
|
18
|
+
rule(:text_with_ruby) { (text.as(:text) | erb_with_tags).repeat.as(:text) }
|
19
|
+
root(:text_with_ruby)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.parse_erb_content(content)
|
23
|
+
parser = ErbParser.new
|
24
|
+
parser.parse(content)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.parse_big_erb_file
|
28
|
+
content = <<~ERB_CONTENT
|
29
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
30
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
31
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
32
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
33
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
34
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
35
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
36
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
37
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
38
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
39
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
40
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
41
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
42
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
43
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
44
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
45
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
46
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
47
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
48
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
49
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
50
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
51
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
52
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
53
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
54
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
55
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
56
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
57
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
58
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
59
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
60
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
61
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
62
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
63
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
64
|
+
|
65
|
+
<%= erb tag %>
|
66
|
+
|
67
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
68
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
69
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
70
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
71
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
72
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
73
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
74
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
75
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
76
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
77
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
78
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
79
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
80
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
81
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
82
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
83
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
84
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
85
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
86
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
87
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
88
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
89
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
90
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
91
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
92
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
93
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
94
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
95
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
96
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
97
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
98
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
99
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
100
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
101
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
102
|
+
ERB_CONTENT
|
103
|
+
parse_erb_content(content)
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# A small example that demonstrates the power of tree pattern matching. Also
|
2
|
+
# uses '.as(:name)' to construct a tree that can reliably be matched
|
3
|
+
# afterwards.
|
4
|
+
|
5
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
6
|
+
|
7
|
+
require 'pp'
|
8
|
+
require 'parslet'
|
9
|
+
|
10
|
+
module LISP # as in 'lots of insipid and stupid parenthesis'
|
11
|
+
class Parser < Parslet::Parser
|
12
|
+
rule(:balanced) {
|
13
|
+
str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r)
|
14
|
+
}
|
15
|
+
|
16
|
+
root(:balanced)
|
17
|
+
end
|
18
|
+
|
19
|
+
class Transform < Parslet::Transform
|
20
|
+
rule(:l => '(', :m => simple(:x), :r => ')') {
|
21
|
+
# innermost :m will contain nil
|
22
|
+
x.nil? ? 1 : x+1
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
@@ -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
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
module ReadmeExample
|
4
|
+
include Parslet
|
5
|
+
|
6
|
+
# Basic parslet examples from the readme
|
7
|
+
class SimpleStringParser < Parslet::Parser
|
8
|
+
root :simple_string
|
9
|
+
|
10
|
+
rule(:simple_string) { quote >> content >> quote }
|
11
|
+
rule(:quote) { str('"') }
|
12
|
+
rule(:content) { (quote.absent? >> any).repeat }
|
13
|
+
end
|
14
|
+
|
15
|
+
class SmalltalkParser < Parslet::Parser
|
16
|
+
root :smalltalk
|
17
|
+
|
18
|
+
rule(:smalltalk) { statements }
|
19
|
+
rule(:statements) {
|
20
|
+
# Simple implementation for demo purposes
|
21
|
+
str('smalltalk')
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
# Demonstrate basic parslet functionality
|
26
|
+
def self.demo_basic_parsing
|
27
|
+
# String parsing
|
28
|
+
foo_parser = Parslet.str('foo')
|
29
|
+
foo_result = foo_parser.parse('foo')
|
30
|
+
|
31
|
+
# Character set matching
|
32
|
+
abc_parser = Parslet.match('[abc]')
|
33
|
+
a_result = abc_parser.parse('a')
|
34
|
+
b_result = abc_parser.parse('b')
|
35
|
+
c_result = abc_parser.parse('c')
|
36
|
+
|
37
|
+
# Annotation
|
38
|
+
annotated_parser = Parslet.str('foo').as(:important_bit)
|
39
|
+
annotated_result = annotated_parser.parse('foo')
|
40
|
+
|
41
|
+
{
|
42
|
+
foo: foo_result,
|
43
|
+
a: a_result,
|
44
|
+
b: b_result,
|
45
|
+
c: c_result,
|
46
|
+
annotated: annotated_result
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.demo_simple_string
|
51
|
+
quote = Parslet.str('"')
|
52
|
+
simple_string = quote >> (quote.absent? >> Parslet.any).repeat >> quote
|
53
|
+
simple_string.parse('"Simple Simple Simple"')
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.demo_smalltalk
|
57
|
+
SmalltalkParser.new.parse('smalltalk')
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
# Demonstrates scope handling in parslet - how captures can be scoped
|
4
|
+
# and how dynamic parsing can access captured values.
|
5
|
+
|
6
|
+
module ScopesExample
|
7
|
+
def self.create_parser
|
8
|
+
Parslet.str('a').capture(:a) >> Parslet.scope { Parslet.str('b').capture(:a) } >>
|
9
|
+
Parslet.dynamic { |s,c| Parslet.str(c.captures[:a]) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.parse_scoped_input(input = 'aba')
|
13
|
+
parser = create_parser
|
14
|
+
parser.parse(input)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.demonstrate_scope_success
|
18
|
+
parse_scoped_input('aba')
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.demonstrate_scope_failure
|
22
|
+
begin
|
23
|
+
parse_scoped_input('abc')
|
24
|
+
rescue Parslet::ParseFailed => e
|
25
|
+
e
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Simpler nested scope example that actually works
|
30
|
+
def self.create_nested_scope_parser
|
31
|
+
Parslet.str('x').capture(:outer) >>
|
32
|
+
Parslet.scope {
|
33
|
+
Parslet.str('y').capture(:outer) >>
|
34
|
+
Parslet.dynamic { |s,c| Parslet.str(c.captures[:outer]) }
|
35
|
+
} >>
|
36
|
+
Parslet.dynamic { |s,c| Parslet.str(c.captures[:outer]) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.parse_nested_scoped_input(input = 'xyyx')
|
40
|
+
parser = create_nested_scope_parser
|
41
|
+
parser.parse(input)
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
module SeasonsExample
|
4
|
+
class Spring < Parslet::Transform
|
5
|
+
rule(:stem => sequence(:branches)) {
|
6
|
+
{:stem => (branches + [{:branch => :leaf}])}
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
class Summer < Parslet::Transform
|
11
|
+
rule(:stem => subtree(:branches)) {
|
12
|
+
new_branches = branches.map { |b| {:branch => [:leaf, :flower]} }
|
13
|
+
{:stem => new_branches}
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
class Fall < Parslet::Transform
|
18
|
+
rule(:branch => sequence(:x)) {
|
19
|
+
x.each { |e| puts "Fruit!" if e==:flower }
|
20
|
+
x.each { |e| puts "Falling Leaves!" if e==:leaf }
|
21
|
+
{:branch => []}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
class Winter < Parslet::Transform
|
26
|
+
rule(:stem => subtree(:x)) {
|
27
|
+
{:stem => []}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.do_seasons(tree)
|
32
|
+
[Spring, Summer, Fall, Winter].each do |season|
|
33
|
+
p "And when #{season} comes"
|
34
|
+
tree = season.new.apply(tree)
|
35
|
+
pp tree
|
36
|
+
puts
|
37
|
+
end
|
38
|
+
tree
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# A small example contributed by John Mettraux (jmettraux) that demonstrates
|
4
|
+
# working with Unicode. This only works on Ruby 1.9.
|
5
|
+
|
6
|
+
require 'parslet'
|
7
|
+
|
8
|
+
module SentenceExample
|
9
|
+
class MyParser < Parslet::Parser
|
10
|
+
rule(:sentence) { (match('[^。]').repeat(1) >> str("。")).as(:sentence) }
|
11
|
+
rule(:sentences) { sentence.repeat }
|
12
|
+
root(:sentences)
|
13
|
+
end
|
14
|
+
|
15
|
+
class Transformer < Parslet::Transform
|
16
|
+
rule(:sentence => simple(:sen)) { sen.to_s }
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# A simple xml parser. It is simple in the respect as that it doesn't address
|
2
|
+
# any of the complexities of XML. This is ruby 1.9.
|
3
|
+
|
4
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
5
|
+
|
6
|
+
require 'pp'
|
7
|
+
require 'parslet'
|
8
|
+
|
9
|
+
class XML < Parslet::Parser
|
10
|
+
root :document
|
11
|
+
|
12
|
+
rule(:document) {
|
13
|
+
tag(close: false).as(:o) >> document.as(:i) >> tag(close: true).as(:c) |
|
14
|
+
text
|
15
|
+
}
|
16
|
+
|
17
|
+
# Perhaps we could have some syntax sugar to make this more easy?
|
18
|
+
#
|
19
|
+
def tag(opts={})
|
20
|
+
close = opts[:close] || false
|
21
|
+
|
22
|
+
parslet = str('<')
|
23
|
+
parslet = parslet >> str('/') if close
|
24
|
+
parslet = parslet >> (str('>').absent? >> match("[a-zA-Z]")).repeat(1).as(:name)
|
25
|
+
parslet = parslet >> str('>')
|
26
|
+
|
27
|
+
parslet
|
28
|
+
end
|
29
|
+
|
30
|
+
rule(:text) {
|
31
|
+
match('[^<>]').repeat(0)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def check(xml)
|
36
|
+
r = XML.new.parse(xml)
|
37
|
+
|
38
|
+
# We'll validate the tree by reducing valid pairs of tags into simply the
|
39
|
+
# string "verified". If the transformation ends on a string, then the
|
40
|
+
# document was 'valid'.
|
41
|
+
#
|
42
|
+
t = Parslet::Transform.new do
|
43
|
+
rule(
|
44
|
+
o: {name: simple(:tag)},
|
45
|
+
c: {name: simple(:tag)},
|
46
|
+
i: simple(:t)
|
47
|
+
) { 'verified' }
|
48
|
+
end
|
49
|
+
|
50
|
+
t.apply(r)
|
51
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# A more complex parser that illustrates how a compiler might be constructed.
|
2
|
+
# The parser recognizes strings and integer literals and constructs almost a
|
3
|
+
# useful AST from the file contents.
|
4
|
+
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
8
|
+
require 'parslet'
|
9
|
+
|
10
|
+
include Parslet
|
11
|
+
|
12
|
+
class LiteralsParser < Parslet::Parser
|
13
|
+
rule :space do
|
14
|
+
(match '[ ]').repeat(1)
|
15
|
+
end
|
16
|
+
|
17
|
+
rule :literals do
|
18
|
+
(literal >> eol).repeat
|
19
|
+
end
|
20
|
+
|
21
|
+
rule :literal do
|
22
|
+
(integer | string).as(:literal) >> space.maybe
|
23
|
+
end
|
24
|
+
|
25
|
+
rule :string do
|
26
|
+
str('"') >>
|
27
|
+
(
|
28
|
+
(str('\\') >> any) |
|
29
|
+
(str('"').absent? >> any)
|
30
|
+
).repeat.as(:string) >>
|
31
|
+
str('"')
|
32
|
+
end
|
33
|
+
|
34
|
+
rule :integer do
|
35
|
+
match('[0-9]').repeat(1).as(:integer)
|
36
|
+
end
|
37
|
+
|
38
|
+
rule :eol do
|
39
|
+
line_end.repeat(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
rule :line_end do
|
43
|
+
crlf >> space.maybe
|
44
|
+
end
|
45
|
+
|
46
|
+
rule :crlf do
|
47
|
+
match('[\r\n]').repeat(1)
|
48
|
+
end
|
49
|
+
|
50
|
+
root :literals
|
51
|
+
end
|
52
|
+
|
53
|
+
input_name = File.join(File.dirname(__FILE__), 'simple.lit')
|
54
|
+
file = File.read(input_name)
|
55
|
+
|
56
|
+
parsetree = LiteralsParser.new.parse(file)
|
57
|
+
|
58
|
+
class Lit < Struct.new(:text)
|
59
|
+
def to_s
|
60
|
+
text.inspect
|
61
|
+
end
|
62
|
+
end
|
63
|
+
class StringLit < Lit
|
64
|
+
end
|
65
|
+
class IntLit < Lit
|
66
|
+
def to_s
|
67
|
+
text
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
transform = Parslet::Transform.new do
|
72
|
+
rule(:literal => {:integer => simple(:x)}) { IntLit.new(x) }
|
73
|
+
rule(:literal => {:string => simple(:s)}) { StringLit.new(s) }
|
74
|
+
end
|
75
|
+
|
76
|
+
ast = transform.apply(parsetree)
|
77
|
+
pp ast
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Result of a Parslet#parse' do
|
4
|
+
include Parslet; extend Parslet
|
5
|
+
|
6
|
+
describe 'regression' do
|
7
|
+
[
|
8
|
+
# Behaviour with maybe-nil
|
9
|
+
[str('foo').maybe >> str('bar'), 'bar', 'bar'],
|
10
|
+
[str('bar') >> str('foo').maybe, 'bar', 'bar'],
|
11
|
+
|
12
|
+
# These might be hard to understand; look at the result of
|
13
|
+
# str.maybe >> str
|
14
|
+
# and
|
15
|
+
# str.maybe >> str first.
|
16
|
+
[(str('f').maybe >> str('b')).repeat, 'bb', 'bb'],
|
17
|
+
[(str('b') >> str('f').maybe).repeat, 'bb', 'bb'],
|
18
|
+
|
19
|
+
[str('a').as(:a) >> (str('b') >> str('c').as(:a)).repeat, 'abc',
|
20
|
+
[{ a: 'a' }, { a: 'c' }]],
|
21
|
+
|
22
|
+
[str('a').as(:a).repeat >> str('b').as(:b).repeat, 'ab', [{ a: 'a' }, { b: 'b' }]],
|
23
|
+
|
24
|
+
# Repetition behaviour / named vs. unnamed
|
25
|
+
[str('f').repeat, '', ''],
|
26
|
+
[str('f').repeat.as(:f), '', { f: [] }],
|
27
|
+
|
28
|
+
# Maybe behaviour / named vs. unnamed
|
29
|
+
[str('f').maybe, '', ''],
|
30
|
+
[str('f').maybe.as(:f), '', { f: nil }],
|
31
|
+
].each do |parslet, input, result|
|
32
|
+
context parslet.inspect.to_s do
|
33
|
+
it "parses \"#{input}\" into \"#{result}\"" do
|
34
|
+
expect(parslet.parse(input)).to eq(result)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parslet::Atoms::Alternative do
|
4
|
+
include Parslet
|
5
|
+
|
6
|
+
describe '| shortcut' do
|
7
|
+
let(:alternative) { str('a') | str('b') }
|
8
|
+
|
9
|
+
context "when chained with different atoms" do
|
10
|
+
before(:each) {
|
11
|
+
# Chain something else to the alternative parslet. If it modifies the
|
12
|
+
# parslet atom in place, we'll notice:
|
13
|
+
|
14
|
+
alternative | str('d')
|
15
|
+
}
|
16
|
+
let!(:chained) { alternative | str('c') }
|
17
|
+
|
18
|
+
|
19
|
+
it "is side-effect free" do
|
20
|
+
chained.should parse('c')
|
21
|
+
chained.should parse('a')
|
22
|
+
chained.should_not parse('d')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|