myco 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/myco/bootstrap/find_constant.rb +4 -11
  3. data/lib/myco/code_loader.rb +2 -1
  4. data/lib/myco/code_tools/AST/ConstantAccess.my +47 -3
  5. data/lib/myco/code_tools/AST/ConstantAccess.my.rb +13 -9
  6. data/lib/myco/code_tools/AST/ConstantAssignment.my +1 -5
  7. data/lib/myco/code_tools/AST/ConstantAssignment.my.rb +3 -9
  8. data/lib/myco/code_tools/AST/ToRuby.my +5 -2
  9. data/lib/myco/code_tools/AST/ToRuby.my.rb +7 -3
  10. data/lib/myco/code_tools/AST.my +1 -0
  11. data/lib/myco/code_tools/AST.my.rb +9 -1
  12. data/lib/myco/code_tools/Parser.my +24 -0
  13. data/lib/myco/code_tools/Parser.my.rb +25 -0
  14. data/lib/myco/code_tools/parser/MycoBuilder.my +67 -0
  15. data/lib/myco/code_tools/parser/MycoBuilder.my.rb +99 -0
  16. data/lib/myco/code_tools/parser/MycoCharacterClasses.my +20 -0
  17. data/lib/myco/code_tools/parser/MycoCharacterClasses.my.rb +56 -0
  18. data/lib/myco/code_tools/parser/MycoGrammar.my +564 -0
  19. data/lib/myco/code_tools/parser/MycoGrammar.my.rb +1851 -0
  20. data/lib/myco/code_tools/parser/MycoTokens.my +78 -0
  21. data/lib/myco/code_tools/parser/MycoTokens.my.rb +170 -0
  22. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Builder.my +4 -0
  23. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Builder.my.rb +5 -0
  24. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeHelpers.my +142 -0
  25. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeHelpers.my.rb +181 -0
  26. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeInstructions.my +420 -0
  27. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeInstructions.my.rb +415 -0
  28. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeParser.my +137 -0
  29. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeParser.my.rb +237 -0
  30. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Constructions.my +183 -0
  31. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Constructions.my.rb +370 -0
  32. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Grammar.my +65 -0
  33. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Grammar.my.rb +83 -0
  34. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Instructions.my +139 -0
  35. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Instructions.my.rb +284 -0
  36. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Machine.my +37 -0
  37. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Machine.my.rb +24 -0
  38. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Parser.my +42 -0
  39. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Parser.my.rb +52 -0
  40. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Patterns.my +123 -0
  41. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Patterns.my.rb +164 -0
  42. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Processor.my +236 -0
  43. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Processor.my.rb +339 -0
  44. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces.my +15 -0
  45. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces.my.rb +14 -0
  46. data/lib/myco/code_tools.rb +1 -1
  47. data/lib/myco/version.rb +1 -1
  48. data/lib/myco.rb +2 -0
  49. metadata +44 -25
  50. data/lib/myco/code_tools/parser/peg_parser.rb +0 -7182
  51. data/lib/myco/code_tools/parser.rb +0 -39
@@ -0,0 +1,284 @@
1
+
2
+ ::Myco::Component.new([::Myco::FileToplevel], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
3
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(::Myco.cscope.for_method_definition.const_set(:Instructions, ::Myco::Component.new([::Myco.find_constant(:EmptyObject)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
4
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(
5
+ declare_meme(:char, [], nil, ::Myco.cscope.dup) { |code| (::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", self.subject.at(self.idx).__send__(
6
+ :==,
7
+ code
8
+ )) {(
9
+ self.__send__(
10
+ :ip=,
11
+ self.ip.__send__(
12
+ :+,
13
+ 1
14
+ )
15
+ )
16
+ self.__send__(
17
+ :idx=,
18
+ self.idx.__send__(
19
+ :+,
20
+ 1
21
+ )
22
+ )
23
+ )}) {self.__send__(
24
+ :ip=,
25
+ nil
26
+ )})}
27
+ declare_meme(:jump, [], nil, ::Myco.cscope.dup) { |label| (self.__send__(
28
+ :ip=,
29
+ self.ip.__send__(
30
+ :+,
31
+ label
32
+ )
33
+ ))}
34
+ declare_meme(:choice, [], nil, ::Myco.cscope.dup) { |label| (
35
+ self.stack.push([
36
+ self.ip.__send__(
37
+ :+,
38
+ label
39
+ ),
40
+ self.idx,
41
+ self.captures.dup
42
+ ])
43
+ self.__send__(
44
+ :ip=,
45
+ self.ip.__send__(
46
+ :+,
47
+ 1
48
+ )
49
+ )
50
+ )}
51
+ declare_meme(:call, [], nil, ::Myco.cscope.dup) { |new_ipk| (
52
+ self.stack.push([
53
+ self.ip.__send__(
54
+ :+,
55
+ 1
56
+ ),
57
+ self.ipk
58
+ ])
59
+ self.__send__(
60
+ :ip=,
61
+ 0
62
+ )
63
+ self.__send__(
64
+ :ipk=,
65
+ new_ipk
66
+ )
67
+ )}
68
+ declare_meme(:return, [], nil, ::Myco.cscope.dup) { |*| (
69
+ addr = self.stack.pop
70
+ ::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", addr) {(
71
+ self.__send__(
72
+ :ipk=,
73
+ addr.pop
74
+ )
75
+ self.__send__(
76
+ :ip=,
77
+ addr.pop
78
+ )
79
+ true
80
+ )}) {self.finish}
81
+ )}
82
+ declare_meme(:commit, [], nil, ::Myco.cscope.dup) { |label| (
83
+ self.stack.pop
84
+ self.__send__(
85
+ :ip=,
86
+ self.ip.__send__(
87
+ :+,
88
+ label
89
+ )
90
+ )
91
+ )}
92
+ declare_meme(:capture, [], nil, ::Myco.cscope.dup) { |metadata| (
93
+ self.captures.push([
94
+ self.idx,
95
+ metadata
96
+ ])
97
+ self.__send__(
98
+ :ip=,
99
+ self.ip.__send__(
100
+ :+,
101
+ 1
102
+ )
103
+ )
104
+ )}
105
+ declare_meme(:handle_fail, [], nil, ::Myco.cscope.dup) { |*| (::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", self.stack.__send__(:empty?)) {::Myco.find_constant(:ErrorCondition).new}) {(
106
+ entry = self.stack.pop
107
+ size = entry.size
108
+ ::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", size.__send__(
109
+ :==,
110
+ 3
111
+ )) {(
112
+ self.__send__(
113
+ :captures=,
114
+ entry.pop
115
+ )
116
+ self.__send__(
117
+ :idx=,
118
+ entry.pop
119
+ )
120
+ self.__send__(
121
+ :ip=,
122
+ entry.pop
123
+ )
124
+ true
125
+ )}) {::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", size.__send__(
126
+ :==,
127
+ 2
128
+ )) {(
129
+ self.__send__(
130
+ :ipk=,
131
+ entry.pop
132
+ )
133
+ self.__send__(
134
+ :ip=,
135
+ nil
136
+ )
137
+ )}) {::Myco.find_constant(:ErrorCondition).new}}
138
+ )})}
139
+ declare_meme(:finish, [], nil, ::Myco.cscope.dup) { |*| (self.__send__(
140
+ :ip=,
141
+ true
142
+ ))}
143
+ declare_meme(:charset, [], nil, ::Myco.cscope.dup) { |table| (::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", (
144
+ code = self.subject.at(self.idx)
145
+ ::Myco.branch_op(:"&&", code) {table.__send__(
146
+ :key?,
147
+ code
148
+ )}
149
+ )) {(
150
+ self.__send__(
151
+ :ip=,
152
+ self.ip.__send__(
153
+ :+,
154
+ 1
155
+ )
156
+ )
157
+ self.__send__(
158
+ :idx=,
159
+ self.idx.__send__(
160
+ :+,
161
+ 1
162
+ )
163
+ )
164
+ )}) {self.__send__(
165
+ :ip=,
166
+ nil
167
+ )})}
168
+ declare_meme(:charrange, [], nil, ::Myco.cscope.dup) { |start, stop| (::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", (
169
+ code = self.subject.at(self.idx)
170
+ ::Myco.branch_op(:"&&", ::Myco.branch_op(:"&&", code) {code.__send__(
171
+ :>=,
172
+ start
173
+ )}) {code.__send__(
174
+ :<=,
175
+ stop
176
+ )}
177
+ )) {(
178
+ self.__send__(
179
+ :ip=,
180
+ self.ip.__send__(
181
+ :+,
182
+ 1
183
+ )
184
+ )
185
+ self.__send__(
186
+ :idx=,
187
+ self.idx.__send__(
188
+ :+,
189
+ 1
190
+ )
191
+ )
192
+ )}) {self.__send__(
193
+ :ip=,
194
+ nil
195
+ )})}
196
+ declare_meme(:span, [], nil, ::Myco.cscope.dup) { |table| (self.loop { || (::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", (
197
+ code = self.subject.at(self.idx)
198
+ ::Myco.branch_op(:"&&", code) {table.__send__(
199
+ :key?,
200
+ code
201
+ )}
202
+ )) {self.__send__(
203
+ :idx=,
204
+ self.idx.__send__(
205
+ :+,
206
+ 1
207
+ )
208
+ )}) {(
209
+ self.__send__(
210
+ :ip=,
211
+ self.ip.__send__(
212
+ :+,
213
+ 1
214
+ )
215
+ )
216
+ self.__send__(:break)
217
+ )})})}
218
+ declare_meme(:any, [], nil, ::Myco.cscope.dup) { |count| (::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", self.idx.__send__(
219
+ :+,
220
+ count
221
+ ).__send__(
222
+ :>,
223
+ self.subject.size
224
+ )) {(
225
+ self.__send__(
226
+ :ip=,
227
+ nil
228
+ )
229
+ true
230
+ )}) {(
231
+ self.__send__(
232
+ :ip=,
233
+ self.ip.__send__(
234
+ :+,
235
+ 1
236
+ )
237
+ )
238
+ self.__send__(
239
+ :idx=,
240
+ self.idx.__send__(
241
+ :+,
242
+ count
243
+ )
244
+ )
245
+ )})}
246
+ declare_meme(:partial_commit, [], nil, ::Myco.cscope.dup) { |label| (
247
+ entry = self.stack.pop
248
+ self.stack.push([
249
+ entry.first,
250
+ self.idx,
251
+ self.captures.dup
252
+ ])
253
+ self.__send__(
254
+ :ip=,
255
+ self.ip.__send__(
256
+ :+,
257
+ label
258
+ )
259
+ )
260
+ )}
261
+ declare_meme(:back_commit, [], nil, ::Myco.cscope.dup) { |label| (
262
+ entry = self.stack.pop
263
+ self.__send__(
264
+ :captures=,
265
+ entry.pop
266
+ )
267
+ self.__send__(
268
+ :idx=,
269
+ entry.pop
270
+ )
271
+ self.__send__(
272
+ :ip=,
273
+ self.ip.__send__(
274
+ :+,
275
+ label
276
+ )
277
+ )
278
+ )}
279
+ declare_meme(:fail_twice, [], nil, ::Myco.cscope.dup) { |*| (
280
+ ::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", self.stack.__send__(:empty?)) {::Myco.find_constant(:ErrorCondition).new}) {self.stack.pop}
281
+ self.handle_fail
282
+ )}
283
+ )}})
284
+ .tap { |__c__| __c__.__name__ = :Instructions })}}.instance
@@ -0,0 +1,37 @@
1
+
2
+ Machine < BasicObject, Instructions {
3
+ # The index of the next program instruction, or null indicating fail condition
4
+ var ip: 0
5
+
6
+ # The key name in the rule table of the next program instruction
7
+ var ipk: :root
8
+
9
+ # The current index into the subject string
10
+ var idx: 0
11
+
12
+ # A list of stack entries, each of which is either a:
13
+ # return_address (the instruction index to return to)
14
+ # or a list containing:
15
+ # [fail_address, current_idx, current_capture_list] (a pending alternative)
16
+ var stack: []
17
+
18
+ # A list of tuples, each containing:
19
+ # [idx, captor] (the index into the subject and the captor to notify)
20
+ var captures: []
21
+
22
+ # The subject string as an array of character codes
23
+ var subject: []
24
+
25
+ # The sequence of instructions to execute for each rule name
26
+ var sequence_table: Hash.new
27
+
28
+
29
+ # Execute the root rule instruction sequence over the given subject
30
+ execute: loop {
31
+ ip |? handle_fail ?? (
32
+ (ip==true) &? break ?? send(*sequence_table.fetch(ipk)[ip])
33
+ )
34
+ }
35
+
36
+ had_failure: !ip
37
+ }
@@ -0,0 +1,24 @@
1
+
2
+ ::Myco::Component.new([::Myco::FileToplevel], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
3
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(::Myco.cscope.for_method_definition.const_set(:Machine, ::Myco::Component.new([
4
+ ::Myco.find_constant(:BasicObject),
5
+ ::Myco.find_constant(:Instructions)
6
+ ], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
7
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(
8
+ declare_meme(:ip, [[:var, []]], nil, ::Myco.cscope.dup) { |*| (0)}
9
+ declare_meme(:ipk, [[:var, []]], nil, ::Myco.cscope.dup) { |*| (:root)}
10
+ declare_meme(:idx, [[:var, []]], nil, ::Myco.cscope.dup) { |*| (0)}
11
+ declare_meme(:stack, [[:var, []]], nil, ::Myco.cscope.dup) { |*| ([])}
12
+ declare_meme(:captures, [[:var, []]], nil, ::Myco.cscope.dup) { |*| ([])}
13
+ declare_meme(:subject, [[:var, []]], nil, ::Myco.cscope.dup) { |*| ([])}
14
+ declare_meme(:sequence_table, [[:var, []]], nil, ::Myco.cscope.dup) { |*| (::Myco.find_constant(:Hash).new)}
15
+ declare_meme(:execute, [], nil, ::Myco.cscope.dup) { |*| (self.loop { || (::Myco.branch_op(:"??", ::Myco.branch_op(:"|?", self.ip) {self.handle_fail}) {::Myco.branch_op(:"??", ::Myco.branch_op(:"&?", self.ip.__send__(
16
+ :==,
17
+ true
18
+ )) {self.__send__(:break)}) {self.send(*self.sequence_table.fetch(self.ipk).__send__(
19
+ :[],
20
+ self.ip
21
+ ))}})})}
22
+ declare_meme(:had_failure, [], nil, ::Myco.cscope.dup) { |*| (self.ip.__send__(:!))}
23
+ )}})
24
+ .tap { |__c__| __c__.__name__ = :Machine })}}.instance
@@ -0,0 +1,42 @@
1
+
2
+ Parser < BasicObject {
3
+
4
+ var grammar: Grammar { }
5
+ var string: ""
6
+
7
+ var result: null
8
+
9
+ memoize make_sequence_table: |grammar| {
10
+ grammar.construct_all_rules
11
+ grammar.rule_table.map |k,v| {
12
+ [k, v.construct_rule.sequence + [[:return]]]
13
+ }.to_h
14
+ }
15
+
16
+ new_machine: Machine.new(
17
+ sequence_table: make_sequence_table(grammar)
18
+ subject: string.each_char.map(&:ord)
19
+ )
20
+
21
+ new_processor: Processor.new(
22
+ builder: new_builder
23
+ string: string
24
+ )
25
+
26
+ new_builder: Builder.new
27
+
28
+ parse: |string=null| {
29
+ string && (self.string = string)
30
+ machine = new_machine
31
+
32
+ machine.execute
33
+
34
+ unless(machine.had_failure) {
35
+ processor = new_processor
36
+ processor.capture_items = machine.captures
37
+ self.result = processor.process
38
+ }
39
+
40
+ self
41
+ }
42
+ }
@@ -0,0 +1,52 @@
1
+
2
+ ::Myco::Component.new([::Myco::FileToplevel], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
3
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(::Myco.cscope.for_method_definition.const_set(:Parser, ::Myco::Component.new([::Myco.find_constant(:BasicObject)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
4
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(
5
+ declare_meme(:grammar, [[:var, []]], nil, ::Myco.cscope.dup) { |*| (::Myco::Component.new([::Myco.find_constant(:Grammar)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
6
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {nil}}.instance)}
7
+ declare_meme(:string, [[:var, []]], nil, ::Myco.cscope.dup) { |*| ("")}
8
+ declare_meme(:result, [[:var, []]], nil, ::Myco.cscope.dup) { |*| (nil)}
9
+ declare_meme(:make_sequence_table, [[:memoize, []]], nil, ::Myco.cscope.dup) { |grammar| (
10
+ grammar.construct_all_rules
11
+ grammar.rule_table.map { |k, v| ([
12
+ k,
13
+ v.construct_rule.sequence.__send__(
14
+ :+,
15
+ [[:return]]
16
+ )
17
+ ])}.to_h
18
+ )}
19
+ declare_meme(:new_machine, [], nil, ::Myco.cscope.dup) { |*| (::Myco.find_constant(:Machine).new({
20
+ :sequence_table => self.make_sequence_table(self.grammar),
21
+ :subject => self.string.each_char.map(&:ord)
22
+ }))}
23
+ declare_meme(:new_processor, [], nil, ::Myco.cscope.dup) { |*| (::Myco.find_constant(:Processor).new({
24
+ :builder => self.new_builder,
25
+ :string => self.string
26
+ }))}
27
+ declare_meme(:new_builder, [], nil, ::Myco.cscope.dup) { |*| (::Myco.find_constant(:Builder).new)}
28
+ declare_meme(:parse, [], nil, ::Myco.cscope.dup) { |string=nil| (
29
+ ::Myco.branch_op(:"&&", string) {self.__send__(
30
+ :string=,
31
+ string
32
+ )}
33
+ machine = self.new_machine
34
+ machine.execute
35
+ self.__send__(
36
+ :unless,
37
+ machine.had_failure
38
+ ) { || (
39
+ processor = self.new_processor
40
+ processor.__send__(
41
+ :capture_items=,
42
+ machine.captures
43
+ )
44
+ self.__send__(
45
+ :result=,
46
+ processor.process
47
+ )
48
+ )}
49
+ self
50
+ )}
51
+ )}})
52
+ .tap { |__c__| __c__.__name__ = :Parser })}}.instance
@@ -0,0 +1,123 @@
1
+
2
+ Patterns < EmptyObject {
3
+
4
+ ##
5
+ # Basic types of patterns.
6
+
7
+ Base < BasicObject { var name: parent_meme.name }
8
+ UnaryBase < Base { var inner }
9
+ BinaryBase < Base { var first, var second }
10
+
11
+ AnyCharacter < Base { }
12
+ Character < Base { var code }
13
+ CharacterString < Base { var codes } # Concatenation for Characters
14
+ CharacterSet < Base { var codes } # OrderedChoice for Characters
15
+ CharacterRange < Base { var start, var stop }
16
+
17
+ NegativePredicate < UnaryBase { }
18
+ PositivePredicate < UnaryBase { }
19
+ OneOrMore < UnaryBase { }
20
+ ZeroOrOne < UnaryBase { }
21
+ ZeroOrMore < UnaryBase { }
22
+ OrderedChoice < BinaryBase { }
23
+ Concatenation < BinaryBase { }
24
+
25
+ NamedCapture < UnaryBase { }
26
+ NamedTextCapture < UnaryBase { }
27
+ NamedTokenCapture < UnaryBase { }
28
+ Reduction < UnaryBase { var block, var args: [] }
29
+
30
+ ##
31
+ # Shorthand methods for creating primitive patterns
32
+
33
+ ShorthandMethods < EmptyObject {
34
+ any: AnyCharacter.new
35
+ char: |character| Character.new(code:character.ord)
36
+ str: |string| CharacterString.new(codes:string.each_char.map(&:ord))
37
+ set: |string| CharacterSet.new(codes:string.each_char.map(&:ord))
38
+ range: |start, stop| CharacterRange.new(start:start.ord, stop:stop.ord)
39
+ r: |pattern, &block| Reduction.new(inner:pattern, block:block)
40
+ }
41
+
42
+ ##
43
+ # Operator overloading for combining and modifying patterns.
44
+ #
45
+ # Each operation returns a new Pattern object composed of the operands
46
+ # so that complex grammars can be specified with relatively succint syntax.
47
+
48
+ Base << {
49
+ "!": NegativePredicate .new(inner:self)
50
+ "/": |other| OrderedChoice .new(first:self, second:other)
51
+ "+": |other=false|
52
+ other &? Concatenation .new(first:self, second:other)
53
+ ?? OneOrMore .new(inner:self)
54
+ "-": |other=false|
55
+ other &? ErrorCondition .new() # TODO: add exception handling to Myco
56
+ ?? ZeroOrOne .new(inner:self)
57
+ "*": |other=false|
58
+ other &? ErrorCondition .new()
59
+ ?? ZeroOrMore .new(inner:self)
60
+
61
+ "[]": |name| NamedCapture .new(inner:self, name:name)
62
+ text: |name=:text|
63
+ NamedTextCapture .new(inner:self, name:name)
64
+ token: |name|
65
+ NamedTokenCapture .new(inner:self, name:name)
66
+
67
+ [private]
68
+
69
+ # Add |other| to |orig| in a right-associative tree of type |type|
70
+ binary_right_assoc: |orig, other, type| {
71
+ cursor = orig
72
+ loop {
73
+ unless(cursor.second.is_a?(type)) { break }
74
+ cursor = cursor.second
75
+ }
76
+ cursor.second = type.new(first:cursor.second, second:other)
77
+ orig
78
+ }
79
+ }
80
+
81
+ NegativePredicate << {
82
+ # Invert the polarity of the predicate instead of nesting the predicate
83
+ "!": PositivePredicate.new(inner:inner)
84
+ }
85
+
86
+ PositivePredicate << {
87
+ # Invert the polarity of the predicate instead of nesting the predicate
88
+ "!": NegativePredicate.new(inner:inner)
89
+ }
90
+
91
+ OrderedChoice << {
92
+ # Build a right-associative tree if the first operand of another "/"
93
+ "/": |other|
94
+ private.binary_right_assoc(self, other, OrderedChoice)
95
+ }
96
+
97
+ Concatenation << {
98
+ # Build a right-associative tree if the first operand of another "+"
99
+ "+": |other=false|
100
+ other
101
+ &? private.binary_right_assoc(self, other, Concatenation)
102
+ ?? OneOrMore.new(inner:self)
103
+ }
104
+
105
+ ##
106
+ # Reduction and associated named captures implementation
107
+
108
+ Reduction << {
109
+ Environment < EmptyObject, BasicDecorators {
110
+ var captures # A Hash of capture names to values
111
+ var builder # The fallback delegate for methods that are not capture names
112
+
113
+ method_missing: |name, *args, &block|
114
+ captures.fetch(name) { builder.send(name, *args, &block) }
115
+
116
+ # Implement the action for the reduction in the '!' method.
117
+ # It will have access to the captures via method_missing.
118
+ "!": null
119
+ }
120
+
121
+ code: block.block.compiled_code
122
+ }
123
+ }