myco 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/lib/myco/bootstrap/add_method.rb +38 -0
  3. data/lib/myco/bootstrap/component.rb +43 -21
  4. data/lib/myco/bootstrap/find_constant.rb +73 -25
  5. data/lib/myco/bootstrap/instance.rb +96 -25
  6. data/lib/myco/bootstrap/meme.rb +18 -12
  7. data/lib/myco/bootstrap/tuple.rb +13 -0
  8. data/lib/myco/bootstrap/undefined.rb +9 -0
  9. data/lib/myco/bootstrap/void.rb +5 -4
  10. data/lib/myco/bootstrap.my +24 -13
  11. data/lib/myco/bootstrap.my.rb +41 -4
  12. data/lib/myco/bootstrap.rb +4 -0
  13. data/lib/myco/code_loader.rb +11 -9
  14. data/lib/myco/code_tools/AST/Block.my.rb +2 -2
  15. data/lib/myco/code_tools/AST/ConstantAccess.my.rb +4 -4
  16. data/lib/myco/code_tools/AST/ConstantAssignment.my.rb +4 -4
  17. data/lib/myco/code_tools/AST/Invoke.my +1 -1
  18. data/lib/myco/code_tools/AST/Invoke.my.rb +1 -1
  19. data/lib/myco/code_tools/AST/Node.my +2 -4
  20. data/lib/myco/code_tools/AST/Node.my.rb +3 -3
  21. data/lib/myco/code_tools/AST/PipeOperator.my.rb +1 -1
  22. data/lib/myco/code_tools/AST/ToRuby.my.rb +8 -8
  23. data/lib/myco/code_tools/AST/misc.my.rb +1 -1
  24. data/lib/myco/code_tools/Parser.my +8 -15
  25. data/lib/myco/code_tools/Parser.my.rb +8 -14
  26. data/lib/myco/code_tools/parser/MycoBuilder.my +3 -4
  27. data/lib/myco/code_tools/parser/MycoBuilder.my.rb +5 -6
  28. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeHelpers.my +8 -4
  29. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeHelpers.my.rb +5 -5
  30. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeInstructions.my +2 -2
  31. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeInstructions.my.rb +12 -12
  32. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeParser.my +54 -44
  33. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/BytecodeParser.my.rb +69 -83
  34. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Grammar.my +18 -8
  35. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Grammar.my.rb +24 -10
  36. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Machine.my.rb +1 -1
  37. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Parser.my +1 -1
  38. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Parser.my.rb +1 -2
  39. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Patterns.my +1 -1
  40. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Patterns.my.rb +1 -1
  41. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Processor.my +3 -3
  42. data/lib/myco/code_tools/parser/pegleromyces/lib/pegleromyces/Processor.my.rb +5 -6
  43. data/lib/myco/code_tools/parser/pegleromyces/spec/BasicSpec.my +35 -0
  44. data/lib/myco/code_tools/parser/pegleromyces/spec/BasicSpec.my.rb +35 -0
  45. data/lib/myco/code_tools/parser/pegleromyces/spec/Builder.test.my +10 -0
  46. data/lib/myco/code_tools/parser/pegleromyces/spec/Builder.test.my.rb +9 -0
  47. data/lib/myco/code_tools/parser/pegleromyces/spec/BytecodeInstructions.test.my +10 -0
  48. data/lib/myco/code_tools/parser/pegleromyces/spec/BytecodeInstructions.test.my.rb +9 -0
  49. data/lib/myco/code_tools/parser/pegleromyces/spec/BytecodeParser.test.my +81 -0
  50. data/lib/myco/code_tools/parser/pegleromyces/spec/BytecodeParser.test.my.rb +209 -0
  51. data/lib/myco/code_tools/parser/pegleromyces/spec/Constructions.test.my +229 -0
  52. data/lib/myco/code_tools/parser/pegleromyces/spec/Constructions.test.my.rb +663 -0
  53. data/lib/myco/code_tools/parser/pegleromyces/spec/Grammar.test.my +10 -0
  54. data/lib/myco/code_tools/parser/pegleromyces/spec/Grammar.test.my.rb +9 -0
  55. data/lib/myco/code_tools/parser/pegleromyces/spec/Instructions.test.my +10 -0
  56. data/lib/myco/code_tools/parser/pegleromyces/spec/Instructions.test.my.rb +9 -0
  57. data/lib/myco/code_tools/parser/pegleromyces/spec/Machine.test.my +13 -0
  58. data/lib/myco/code_tools/parser/pegleromyces/spec/Machine.test.my.rb +20 -0
  59. data/lib/myco/code_tools/parser/pegleromyces/spec/Parser.test.my +54 -0
  60. data/lib/myco/code_tools/parser/pegleromyces/spec/Parser.test.my.rb +215 -0
  61. data/lib/myco/code_tools/parser/pegleromyces/spec/Patterns.test.my +156 -0
  62. data/lib/myco/code_tools/parser/pegleromyces/spec/Patterns.test.my.rb +334 -0
  63. data/lib/myco/code_tools/parser/pegleromyces/spec/Processor.test.my +10 -0
  64. data/lib/myco/code_tools/parser/pegleromyces/spec/Processor.test.my.rb +9 -0
  65. data/lib/myco/code_tools/parser/pegleromyces/spec/run.my +20 -0
  66. data/lib/myco/code_tools/parser/pegleromyces/spec/run.my.rb +16 -0
  67. data/lib/myco/core/BasicDecorators.my +19 -11
  68. data/lib/myco/core/BasicDecorators.my.rb +24 -20
  69. data/lib/myco/core/BasicObject.my +12 -7
  70. data/lib/myco/core/BasicObject.my.rb +50 -44
  71. data/lib/myco/core/Category.my +12 -2
  72. data/lib/myco/core/Category.my.rb +15 -7
  73. data/lib/myco/core/Decorator.my +1 -1
  74. data/lib/myco/core/Decorator.my.rb +8 -10
  75. data/lib/myco/core/FileToplevel.my +3 -3
  76. data/lib/myco/core/FileToplevel.my.rb +4 -6
  77. data/lib/myco/core/Object.my +7 -10
  78. data/lib/myco/core/Object.my.rb +11 -17
  79. data/lib/myco/core/Ruby.my +6 -0
  80. data/lib/myco/core/Ruby.my.rb +16 -0
  81. data/lib/myco/core/Switch.my +1 -1
  82. data/lib/myco/core/Switch.my.rb +1 -1
  83. data/lib/myco/core.my +4 -0
  84. data/lib/myco/core.my.rb +7 -0
  85. data/lib/myco/dev/call_sites.rb +39 -0
  86. data/lib/myco/dev/counter.rb +26 -0
  87. data/lib/myco/dev.rb +3 -0
  88. data/lib/myco/eval.rb +1 -1
  89. data/lib/myco/tools/BasicCommand.my.rb +1 -1
  90. data/lib/myco/version.rb +1 -1
  91. data/lib/myco.rb +2 -3
  92. metadata +53 -20
  93. data/lib/myco/bootstrap/evaluator.rb +0 -58
@@ -0,0 +1,9 @@
1
+
2
+ ::Myco::Component.new([::Myco::FileToplevel], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
3
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(::Myco::Component.new([::Myco.find_constant(:BasicSpec)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
4
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(
5
+ declare_meme(:name, [], nil, ::Myco.cscope.dup) { |*| ("Builder")}
6
+ declare_meme(:new_builder, [], nil, ::Myco.cscope.dup) { |*| (::Myco::Component.new([::Myco.find_constant(:Builder)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
7
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {nil}}.instance)}
8
+ __category__(:tests).component_eval {(declare_meme(:exists, [[:it, []]], nil, ::Myco.cscope.dup) { |*| (self.assert(self.new_builder))})}
9
+ )}}.instance)}}.instance
@@ -0,0 +1,10 @@
1
+
2
+ BasicSpec {
3
+ name: "BytecodeInstructions"
4
+
5
+ instructions: BytecodeInstructions { }
6
+
7
+ [tests]
8
+
9
+ it "exists": assert(instructions)
10
+ }
@@ -0,0 +1,9 @@
1
+
2
+ ::Myco::Component.new([::Myco::FileToplevel], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
3
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(::Myco::Component.new([::Myco.find_constant(:BasicSpec)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
4
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(
5
+ declare_meme(:name, [], nil, ::Myco.cscope.dup) { |*| ("BytecodeInstructions")}
6
+ declare_meme(:instructions, [], nil, ::Myco.cscope.dup) { |*| (::Myco::Component.new([::Myco.find_constant(:BytecodeInstructions)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
7
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {nil}}.instance)}
8
+ __category__(:tests).component_eval {(declare_meme(:exists, [[:it, []]], nil, ::Myco.cscope.dup) { |*| (self.assert(self.instructions))})}
9
+ )}}.instance)}}.instance
@@ -0,0 +1,81 @@
1
+
2
+ BasicSpec {
3
+ name: "BytecodeParser"
4
+
5
+ new_parser: BytecodeParser { }
6
+
7
+ [tests]
8
+
9
+ it "can match from a tiny grammar": {
10
+ parser = new_parser
11
+ parser.grammar = Grammar {
12
+ [rules]
13
+ rule root:
14
+ str('abc') / str('x') + set('Yy') + !str('y') + !!range('a','z') + any
15
+ }
16
+ state = parser.parse('xyz')
17
+ assert_equal(state.end_idx, 3)
18
+ }
19
+
20
+ it "can match from a grammar with multiplicit patterns": {
21
+ parser = new_parser
22
+ parser.grammar = Grammar {
23
+ [rules]
24
+ rule root: str('x').+ + str('y').* + str('z').-
25
+ }
26
+ state = parser.parse('xxz')
27
+ assert_equal(state.end_idx, 3)
28
+ }
29
+
30
+ it "can match from a grammar with rule calls": {
31
+ parser = new_parser
32
+ parser.grammar = Grammar {
33
+ [rules]
34
+ rule root: unintended / intended
35
+ rule unintended: abc
36
+ rule intended: x + any_y + not_y + any_lower
37
+
38
+ rule abc: str('abc')
39
+ rule x: str('x')
40
+ rule any_y: set('Yy')
41
+ rule not_y: !str('y')
42
+ rule lower: range('a','z')
43
+ rule any_lower: !!lower + any
44
+ }
45
+ state = parser.parse('xyz')
46
+ assert_equal(state.end_idx, 3)
47
+ }
48
+
49
+ it "skips memoized rule calls without result corruption": {
50
+ parser = new_parser
51
+ parser.grammar = Grammar {
52
+ [rules]
53
+ rule root: moot
54
+ rule moot:
55
+ (array[:root] + str('NO')) / (array[:root] + str('NOPE')) / array[:root]
56
+ rule array:
57
+ r(str('[').token(:foo)[:t0] + (!str(']') + any).*[:list] + str(']'))
58
+ { [:array, t0.line, *list.map(&:to_sym)] }
59
+ }
60
+ state = parser.parse('[abc]')
61
+ assert(state.end_idx)
62
+ assert_equal(state.result[:root], [:array, 1, :a, :b, :c])
63
+ }
64
+
65
+ it "can save a parser to a file to load and run it later": {
66
+ parser = new_parser
67
+ parser.grammar = Grammar {
68
+ [rules]
69
+ rule root: (r(character.+[:clist]) { clist })[:root]
70
+ rule character: r(any[:text]) { text.to_sym }
71
+ }
72
+ state = parser.parse('xyz')
73
+ assert_equal(state.result.fetch(:root), [:x, :y, :z])
74
+ parser.save_prototype("/tmp/pegleromyces_output.my.rbc")
75
+
76
+ parser = new_parser
77
+ parser.load_prototype("/tmp/pegleromyces_output.my.rbc")
78
+ state = parser.parse('xyz')
79
+ assert_equal(state.result.fetch(:root), [:x, :y, :z])
80
+ }
81
+ }
@@ -0,0 +1,209 @@
1
+
2
+ ::Myco::Component.new([::Myco::FileToplevel], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
3
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(::Myco::Component.new([::Myco.find_constant(:BasicSpec)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
4
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(
5
+ declare_meme(:name, [], nil, ::Myco.cscope.dup) { |*| ("BytecodeParser")}
6
+ declare_meme(:new_parser, [], nil, ::Myco.cscope.dup) { |*| (::Myco::Component.new([::Myco.find_constant(:BytecodeParser)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
7
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {nil}}.instance)}
8
+ __category__(:tests).component_eval {(
9
+ declare_meme(:"can match from a tiny grammar", [[:it, []]], nil, ::Myco.cscope.dup) { |*| (
10
+ parser = self.new_parser
11
+ parser.__send__(
12
+ :grammar=,
13
+ ::Myco::Component.new([::Myco.find_constant(:Grammar)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
14
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(__category__(:rules).component_eval {(declare_meme(:root, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.str("abc").__send__(
15
+ :/,
16
+ self.str("x")
17
+ ).__send__(
18
+ :+,
19
+ self.set("Yy")
20
+ ).__send__(
21
+ :+,
22
+ self.str("y").__send__(:!)
23
+ ).__send__(
24
+ :+,
25
+ self.range(
26
+ "a",
27
+ "z"
28
+ ).__send__(:!).__send__(:!)
29
+ ).__send__(
30
+ :+,
31
+ self.any
32
+ ))})})}}.instance
33
+ )
34
+ state = parser.parse("xyz")
35
+ self.assert_equal(
36
+ state.end_idx,
37
+ 3
38
+ )
39
+ )}
40
+ declare_meme(:"can match from a grammar with multiplicit patterns", [[:it, []]], nil, ::Myco.cscope.dup) { |*| (
41
+ parser = self.new_parser
42
+ parser.__send__(
43
+ :grammar=,
44
+ ::Myco::Component.new([::Myco.find_constant(:Grammar)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
45
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(__category__(:rules).component_eval {(declare_meme(:root, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.str("x").__send__(:+).__send__(
46
+ :+,
47
+ self.str("y").__send__(:*)
48
+ ).__send__(
49
+ :+,
50
+ self.str("z").__send__(:-)
51
+ ))})})}}.instance
52
+ )
53
+ state = parser.parse("xxz")
54
+ self.assert_equal(
55
+ state.end_idx,
56
+ 3
57
+ )
58
+ )}
59
+ declare_meme(:"can match from a grammar with rule calls", [[:it, []]], nil, ::Myco.cscope.dup) { |*| (
60
+ parser = self.new_parser
61
+ parser.__send__(
62
+ :grammar=,
63
+ ::Myco::Component.new([::Myco.find_constant(:Grammar)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
64
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(__category__(:rules).component_eval {(
65
+ declare_meme(:root, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.unintended.__send__(
66
+ :/,
67
+ self.intended
68
+ ))}
69
+ declare_meme(:unintended, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.abc)}
70
+ declare_meme(:intended, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.x.__send__(
71
+ :+,
72
+ self.any_y
73
+ ).__send__(
74
+ :+,
75
+ self.not_y
76
+ ).__send__(
77
+ :+,
78
+ self.any_lower
79
+ ))}
80
+ declare_meme(:abc, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.str("abc"))}
81
+ declare_meme(:x, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.str("x"))}
82
+ declare_meme(:any_y, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.set("Yy"))}
83
+ declare_meme(:not_y, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.str("y").__send__(:!))}
84
+ declare_meme(:lower, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.range(
85
+ "a",
86
+ "z"
87
+ ))}
88
+ declare_meme(:any_lower, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.lower.__send__(:!).__send__(:!).__send__(
89
+ :+,
90
+ self.any
91
+ ))}
92
+ )})}}.instance
93
+ )
94
+ state = parser.parse("xyz")
95
+ self.assert_equal(
96
+ state.end_idx,
97
+ 3
98
+ )
99
+ )}
100
+ declare_meme(:"skips memoized rule calls without result corruption", [[:it, []]], nil, ::Myco.cscope.dup) { |*| (
101
+ parser = self.new_parser
102
+ parser.__send__(
103
+ :grammar=,
104
+ ::Myco::Component.new([::Myco.find_constant(:Grammar)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
105
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(__category__(:rules).component_eval {(
106
+ declare_meme(:root, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.moot)}
107
+ declare_meme(:moot, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.array.__send__(
108
+ :[],
109
+ :root
110
+ ).__send__(
111
+ :+,
112
+ self.str("NO")
113
+ ).__send__(
114
+ :/,
115
+ self.array.__send__(
116
+ :[],
117
+ :root
118
+ ).__send__(
119
+ :+,
120
+ self.str("NOPE")
121
+ )
122
+ ).__send__(
123
+ :/,
124
+ self.array.__send__(
125
+ :[],
126
+ :root
127
+ )
128
+ ))}
129
+ declare_meme(:array, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.r(self.str("[").token(:foo).__send__(
130
+ :[],
131
+ :t0
132
+ ).__send__(
133
+ :+,
134
+ self.str("]").__send__(:!).__send__(
135
+ :+,
136
+ self.any
137
+ ).__send__(:*).__send__(
138
+ :[],
139
+ :list
140
+ )
141
+ ).__send__(
142
+ :+,
143
+ self.str("]")
144
+ )) { || ([
145
+ :array,
146
+ self.__send__(:t0).line,
147
+ *self.list.map(&:to_sym)
148
+ ])})}
149
+ )})}}.instance
150
+ )
151
+ state = parser.parse("[abc]")
152
+ self.assert(state.end_idx)
153
+ self.assert_equal(
154
+ state.result.__send__(
155
+ :[],
156
+ :root
157
+ ),
158
+ [
159
+ :array,
160
+ 1,
161
+ :a,
162
+ :b,
163
+ :c
164
+ ]
165
+ )
166
+ )}
167
+ declare_meme(:"can save a parser to a file to load and run it later", [[:it, []]], nil, ::Myco.cscope.dup) { |*| (
168
+ parser = self.new_parser
169
+ parser.__send__(
170
+ :grammar=,
171
+ ::Myco::Component.new([::Myco.find_constant(:Grammar)], ::Myco.cscope.for_method_definition, __FILE__, __LINE__)
172
+ .tap { |__c__| __c__.__last__ = __c__.component_eval {(__category__(:rules).component_eval {(
173
+ declare_meme(:root, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.r(self.character.__send__(:+).__send__(
174
+ :[],
175
+ :clist
176
+ )) { || (self.clist)}.__send__(
177
+ :[],
178
+ :root
179
+ ))}
180
+ declare_meme(:character, [[:rule, []]], nil, ::Myco.cscope.dup) { |*| (self.r(self.any.__send__(
181
+ :[],
182
+ :text
183
+ )) { || (self.text.to_sym)})}
184
+ )})}}.instance
185
+ )
186
+ state = parser.parse("xyz")
187
+ self.assert_equal(
188
+ state.result.fetch(:root),
189
+ [
190
+ :x,
191
+ :y,
192
+ :z
193
+ ]
194
+ )
195
+ parser.save_prototype("/tmp/pegleromyces_output.my.rbc")
196
+ parser = self.new_parser
197
+ parser.load_prototype("/tmp/pegleromyces_output.my.rbc")
198
+ state = parser.parse("xyz")
199
+ self.assert_equal(
200
+ state.result.fetch(:root),
201
+ [
202
+ :x,
203
+ :y,
204
+ :z
205
+ ]
206
+ )
207
+ )}
208
+ )}
209
+ )}}.instance)}}.instance
@@ -0,0 +1,229 @@
1
+
2
+ BasicSpec {
3
+ name: "Constructions"
4
+
5
+ [tests]
6
+
7
+ specify "AnyCharacter creates a single any instruction": {
8
+ construct = Constructions::AnyCharacter.new
9
+ assert_equal(construct.sequence, [
10
+ [:any, 1]
11
+ ])
12
+ }
13
+
14
+ specify "Character creates a single char instruction": {
15
+ construct = Constructions::Character.new(code:120)
16
+ assert_equal(construct.sequence, [
17
+ [:char, 120]
18
+ ])
19
+ }
20
+
21
+ specify "CharacterString creates a string of char instructions": {
22
+ construct = Constructions::CharacterString.new(codes:[120,121,122])
23
+ assert_equal(construct.sequence, [
24
+ [:char, 120]
25
+ [:char, 121]
26
+ [:char, 122]
27
+ ])
28
+ }
29
+
30
+ specify "CharacterSet creates a single charset instruction": {
31
+ construct = Constructions::CharacterSet.new(codes:[120,121,122])
32
+ expected_table = [120,121,122].map |code| { [code,true] }.to_h
33
+ assert_equal(construct.sequence, [
34
+ [:charset, expected_table]
35
+ ])
36
+ }
37
+
38
+ specify "CharacterRange creates a single charrange instruction": {
39
+ construct = Constructions::CharacterRange.new(start:120, stop:122)
40
+ assert_equal(construct.sequence, [
41
+ [:charrange, 120, 122]
42
+ ])
43
+ }
44
+
45
+ specify "NegativePredicate assembles choice, fail_twice instructions": {
46
+ a = Constructions::CharacterString.new(codes:[120,121,122])
47
+ construct = Constructions::NegativePredicate.new(inner:a)
48
+ assert_equal(construct.sequence, [
49
+ [:choice, 5] # ip+5 == L1
50
+ [:char, 120]
51
+ [:char, 121]
52
+ [:char, 122]
53
+ [:fail_twice]
54
+ ]) # (L1)
55
+ }
56
+
57
+ specify "PositivePredicate assembles choice, commit, fail instructions": {
58
+ a = Constructions::CharacterString.new(codes:[120,121,122])
59
+ construct = Constructions::PositivePredicate.new(inner:a)
60
+ assert_equal(construct.sequence, [
61
+ [:choice, 7] # ip+7 == L1
62
+ [:choice, 4] # ip+4 == L2
63
+ [:char, 120]
64
+ [:char, 121]
65
+ [:char, 122]
66
+ [:commit, 1] # ip+1 == L3 (L2)
67
+ [:fail] # (L3)
68
+ ]) # (L1)
69
+ }
70
+
71
+ specify "OneOrMore assembles UNOPTIMIZED instructions": {
72
+ a = Constructions::CharacterString.new(codes:[120,121,122])
73
+ construct = Constructions::OneOrMore.new(inner:a)
74
+ assert_equal(construct.sequence, [
75
+ [:char, 120] # TODO: optimize and eliminate the double specification here
76
+ [:char, 121]
77
+ [:char, 122]
78
+ [:choice, 5] # ip+5 == L2
79
+ [:char, 120] # (L1)
80
+ [:char, 121]
81
+ [:char, 122]
82
+ [:partial_commit, -3] # ip-3 == L1
83
+ ]) # (L2)
84
+ }
85
+
86
+ specify "ZeroOrOne assembles choice and commit instructions": {
87
+ a = Constructions::CharacterString.new(codes:[120,121,122])
88
+ construct = Constructions::ZeroOrOne.new(inner:a)
89
+ assert_equal(construct.sequence, [
90
+ [:choice, 5] # ip+5 == L1
91
+ [:char, 120]
92
+ [:char, 121]
93
+ [:char, 122]
94
+ [:commit, 1] # ip+1 == L1
95
+ ]) # (L1)
96
+ }
97
+
98
+ specify "ZeroOrMore assembles choice and partial_commit instructions": {
99
+ a = Constructions::CharacterString.new(codes:[120,121,122])
100
+ construct = Constructions::ZeroOrMore.new(inner:a)
101
+ assert_equal(construct.sequence, [
102
+ [:choice, 5] # ip+5 == L2
103
+ [:char, 120] # (L1)
104
+ [:char, 121]
105
+ [:char, 122]
106
+ [:partial_commit, -3] # ip-3 == L1
107
+ ]) # (L2)
108
+ }
109
+
110
+ specify "ZeroOrMore<CharacterSet> creates a single span instruction": {
111
+ a = Constructions::CharacterSet.new(codes:[120,121,122])
112
+ construct = Constructions::ZeroOrMore.new(inner:a)
113
+ expected_table = [120,121,122].map |code| { [code,true] }.to_h
114
+ assert_equal(construct.sequence, [
115
+ [:span, expected_table]
116
+ ])
117
+ }
118
+
119
+ specify "OrderedChoice assembles choice and commit instructions": {
120
+ a = Constructions::CharacterString.new(codes:[110,111,112])
121
+ b = Constructions::CharacterString.new(codes:[120,121,122])
122
+ construct = Constructions::OrderedChoice.new(first:a, second:b)
123
+ assert_equal(construct.sequence, [
124
+ [:choice, 5] # ip+5 == L1
125
+ [:char, 110]
126
+ [:char, 111]
127
+ [:char, 112]
128
+ [:commit, 4] # ip+4 == L2
129
+ [:char, 120] # (L1)
130
+ [:char, 121]
131
+ [:char, 122]
132
+ ]) # (L2)
133
+
134
+ a = Constructions::CharacterString.new(codes:[110,111,112])
135
+ b = Constructions::CharacterString.new(codes:[120,121,122])
136
+ c = Constructions::CharacterString.new(codes:[130,131,132])
137
+ construct = Constructions::OrderedChoice.new(first:b, second:c)
138
+ construct = Constructions::OrderedChoice.new(first:a, second:construct)
139
+ assert_equal(construct.sequence, [
140
+ [:choice, 5] # ip+5 == L1
141
+ [:char, 110]
142
+ [:char, 111]
143
+ [:char, 112]
144
+ [:commit, 9] # ip+9 == L2
145
+ [:choice, 5] # ip+5 == L3; (L1)
146
+ [:char, 120]
147
+ [:char, 121]
148
+ [:char, 122]
149
+ [:commit, 4] # ip+4 == L2
150
+ [:char, 130] # (L3)
151
+ [:char, 131]
152
+ [:char, 132]
153
+ ]) # (L2)
154
+ }
155
+
156
+ specify "Concatenation simply concatenates the operands": {
157
+ a = Constructions::CharacterString.new(codes:[110,111,112])
158
+ b = Constructions::CharacterString.new(codes:[120,121,122])
159
+ construct = Constructions::Concatenation.new(first:a, second:b)
160
+ assert_equal(construct.sequence, [
161
+ [:char, 110]
162
+ [:char, 111]
163
+ [:char, 112]
164
+ [:char, 120]
165
+ [:char, 121]
166
+ [:char, 122]
167
+ ])
168
+ }
169
+
170
+ specify "NamedCapture assembles capture instructions": {
171
+ a = Constructions::CharacterString.new(codes:[120,121,122])
172
+ construct = Constructions::NamedCapture.new(inner:a)
173
+ assert_equal(construct.sequence, [
174
+ [:capture, [:c_start]]
175
+ [:char, 120]
176
+ [:char, 121]
177
+ [:char, 122]
178
+ [:capture, [:c_end, construct.captargs]]
179
+ ])
180
+ }
181
+
182
+ specify "NamedCapture of ZeroOrMore has a special multiplicit strategy": {
183
+ a = Constructions::CharacterString.new(codes:[120,121,122])
184
+ b = Constructions::ZeroOrMore.new(inner:a)
185
+ construct = Constructions::NamedCapture.new(inner:b)
186
+ assert_equal(construct.sequence, [
187
+ [:capture, [:m_start]]
188
+ [:choice, 6]
189
+ [:char, 120]
190
+ [:char, 121]
191
+ [:char, 122]
192
+ [:capture, [:m_split, null]]
193
+ [:partial_commit, -4]
194
+ [:capture, [:m_end, null]]
195
+ ])
196
+ }
197
+
198
+ specify "NamedCapture of OneOrMore has a special multiplicit strategy": {
199
+ a = Constructions::CharacterString.new(codes:[120,121,122])
200
+ b = Constructions::OneOrMore.new(inner:a)
201
+ construct = Constructions::NamedCapture.new(inner:b)
202
+ assert_equal(construct.sequence, [
203
+ [:capture, [:m_start]]
204
+ [:char, 120]
205
+ [:char, 121]
206
+ [:char, 122]
207
+ [:capture, [:m_split, null]]
208
+ [:choice, 6]
209
+ [:char, 120]
210
+ [:char, 121]
211
+ [:char, 122]
212
+ [:capture, [:m_split, null]]
213
+ [:partial_commit, -4]
214
+ [:capture, [:m_end, null]]
215
+ ])
216
+ }
217
+
218
+ specify "Reduction assembles capture instructions": {
219
+ a = Constructions::CharacterString.new(codes:[120,121,122])
220
+ construct = Constructions::Reduction.new(inner:a)
221
+ assert_equal(construct.sequence, [
222
+ [:capture, [:r_start]]
223
+ [:char, 120]
224
+ [:char, 121]
225
+ [:char, 122]
226
+ [:capture, [:r_end, construct.captargs]]
227
+ ])
228
+ }
229
+ }