code-ruby 0.4.0 → 0.5.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 (204) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/Gemfile +2 -1
  4. data/Gemfile.lock +27 -54
  5. data/TODO +17 -0
  6. data/bin/code +56 -31
  7. data/bin/format +3 -0
  8. data/bin/template +62 -20
  9. data/bin/test +17 -0
  10. data/code-ruby.gemspec +1 -3
  11. data/docs/class.code +9 -0
  12. data/docs/euler/1.template +1 -5
  13. data/docs/euler/5.template +0 -1
  14. data/docs/meetup.code +12 -0
  15. data/docs/precedence.template +6 -39
  16. data/docs/rain.code +22 -0
  17. data/docs/slack.code +17 -0
  18. data/docs/stripe.code +7 -0
  19. data/docs/twitter.code +9 -0
  20. data/language-ruby.gemspec +18 -0
  21. data/lib/code/node/base_10.rb +29 -0
  22. data/lib/code/node/base_16.rb +13 -0
  23. data/lib/code/node/base_2.rb +13 -0
  24. data/lib/code/node/base_8.rb +13 -0
  25. data/lib/code/node/boolean.rb +7 -7
  26. data/lib/code/node/call.rb +32 -38
  27. data/lib/code/node/call_argument.rb +11 -27
  28. data/lib/code/node/chained_call.rb +10 -27
  29. data/lib/code/node/code.rb +4 -6
  30. data/lib/code/node/decimal.rb +26 -0
  31. data/lib/code/node/dictionnary.rb +20 -9
  32. data/lib/code/node/equal.rb +18 -20
  33. data/lib/code/node/function.rb +10 -7
  34. data/lib/code/node/function_parameter.rb +31 -0
  35. data/lib/code/node/if.rb +36 -32
  36. data/lib/code/node/if_modifier.rb +35 -36
  37. data/lib/code/node/list.rb +6 -8
  38. data/lib/code/node/negation.rb +5 -23
  39. data/lib/code/node/not.rb +15 -0
  40. data/lib/code/node/nothing.rb +1 -1
  41. data/lib/code/node/number.rb +14 -12
  42. data/lib/code/node/operation.rb +21 -16
  43. data/lib/code/node/power.rb +10 -6
  44. data/lib/code/node/rescue.rb +4 -3
  45. data/lib/code/node/splat.rb +15 -0
  46. data/lib/code/node/statement.rb +48 -70
  47. data/lib/code/node/string.rb +42 -16
  48. data/lib/code/node/ternary.rb +7 -9
  49. data/lib/code/node/unary_minus.rb +5 -12
  50. data/lib/code/node/while.rb +17 -24
  51. data/lib/code/node.rb +7 -8
  52. data/lib/code/object/argument.rb +2 -11
  53. data/lib/code/object/decimal.rb +45 -30
  54. data/lib/code/object/dictionnary.rb +6 -5
  55. data/lib/code/object/function.rb +20 -23
  56. data/lib/code/object/global.rb +11 -6
  57. data/lib/code/object/integer.rb +73 -30
  58. data/lib/code/object/list.rb +40 -34
  59. data/lib/code/object/range.rb +18 -17
  60. data/lib/code/object/ruby_function.rb +12 -6
  61. data/lib/code/object/string.rb +22 -12
  62. data/lib/code/object.rb +82 -24
  63. data/lib/code/parser/addition.rb +12 -20
  64. data/lib/code/parser/and_operator.rb +9 -20
  65. data/lib/code/parser/bitwise_and.rb +9 -20
  66. data/lib/code/parser/bitwise_or.rb +12 -20
  67. data/lib/code/parser/boolean.rb +10 -7
  68. data/lib/code/parser/call.rb +92 -60
  69. data/lib/code/parser/chained_call.rb +47 -0
  70. data/lib/code/parser/class.rb +45 -0
  71. data/lib/code/parser/code.rb +17 -10
  72. data/lib/code/parser/dictionnary.rb +56 -30
  73. data/lib/code/parser/equal.rb +87 -35
  74. data/lib/code/parser/equality.rb +23 -24
  75. data/lib/code/parser/equality_lower.rb +9 -0
  76. data/lib/code/parser/function.rb +67 -42
  77. data/lib/code/parser/greater.rb +25 -0
  78. data/lib/code/parser/group.rb +13 -8
  79. data/lib/code/parser/if.rb +51 -21
  80. data/lib/code/parser/if_modifier.rb +43 -16
  81. data/lib/code/parser/list.rb +32 -19
  82. data/lib/code/parser/multiplication.rb +15 -20
  83. data/lib/code/parser/name.rb +96 -84
  84. data/lib/code/parser/negation.rb +20 -9
  85. data/lib/code/parser/not_keyword.rb +14 -12
  86. data/lib/code/parser/nothing.rb +13 -8
  87. data/lib/code/parser/number.rb +124 -68
  88. data/lib/code/parser/operation.rb +35 -0
  89. data/lib/code/parser/or_keyword.rb +12 -20
  90. data/lib/code/parser/or_operator.rb +9 -20
  91. data/lib/code/parser/power.rb +32 -14
  92. data/lib/code/parser/range.rb +9 -17
  93. data/lib/code/parser/rescue.rb +29 -13
  94. data/lib/code/parser/shift.rb +11 -21
  95. data/lib/code/parser/splat.rb +31 -0
  96. data/lib/code/parser/statement.rb +4 -3
  97. data/lib/code/parser/string.rb +53 -62
  98. data/lib/code/parser/ternary.rb +36 -15
  99. data/lib/code/parser/unary_minus.rb +23 -5
  100. data/lib/code/parser/while.rb +26 -15
  101. data/lib/code/parser/whitespace.rb +49 -0
  102. data/lib/code/parser.rb +15 -0
  103. data/lib/code/ruby.rb +13 -12
  104. data/lib/code-ruby.rb +2 -11
  105. data/lib/code.rb +16 -13
  106. data/lib/language/atom.rb +343 -0
  107. data/lib/language/output.rb +130 -0
  108. data/lib/language/parser/absent/present.rb +8 -0
  109. data/lib/language/parser/absent.rb +6 -0
  110. data/lib/language/parser/end_of_input.rb +6 -0
  111. data/lib/language/parser/interuption.rb +38 -0
  112. data/lib/language/parser/not_end_of_input.rb +6 -0
  113. data/lib/language/parser/str/not_found.rb +16 -0
  114. data/lib/language/parser/str.rb +6 -0
  115. data/lib/language/parser.rb +53 -0
  116. data/lib/language-ruby.rb +10 -0
  117. data/lib/language.rb +80 -0
  118. data/lib/template/node/code_part.rb +1 -1
  119. data/lib/template/node/part.rb +1 -1
  120. data/lib/template/node/template.rb +1 -1
  121. data/lib/template/node/text_part.rb +1 -1
  122. data/lib/template/node.rb +1 -1
  123. data/lib/template/parser/template.rb +26 -17
  124. data/lib/template/parser.rb +15 -0
  125. data/lib/template/version.rb +1 -1
  126. data/lib/template-ruby.rb +2 -11
  127. data/lib/template.rb +6 -11
  128. data/spec/code/addition_spec.rb +13 -0
  129. data/spec/code/and_operator_spec.rb +13 -0
  130. data/spec/code/bitwise_and_spec.rb +13 -0
  131. data/spec/code/bitwise_or_spec.rb +13 -0
  132. data/spec/code/boolean_spec.rb +13 -0
  133. data/spec/code/call_spec.rb +21 -0
  134. data/spec/code/chained_call_spec.rb +16 -0
  135. data/spec/code/dictionnary_spec.rb +17 -0
  136. data/spec/code/equal_spec.rb +26 -0
  137. data/spec/code/equality_spec.rb +13 -0
  138. data/spec/code/function_spec.rb +18 -0
  139. data/spec/code/greater_spec.rb +18 -0
  140. data/spec/code/group_spec.rb +12 -0
  141. data/spec/code/if_modifier_spec.rb +20 -0
  142. data/spec/code/if_spec.rb +25 -0
  143. data/spec/code/list_spec.rb +17 -0
  144. data/spec/code/multiplication_spec.rb +18 -0
  145. data/spec/code/negation_spec.rb +20 -0
  146. data/spec/code/not_keyword_spec.rb +13 -0
  147. data/spec/code/nothing_spec.rb +17 -0
  148. data/spec/code/number_spec.rb +22 -0
  149. data/spec/code/or_keyword_spec.rb +17 -0
  150. data/spec/code/or_operator_spec.rb +16 -0
  151. data/spec/code/parser/boolean_spec.rb +5 -7
  152. data/spec/code/parser/call_spec.rb +16 -56
  153. data/spec/code/parser/chained_call.rb +17 -0
  154. data/spec/code/parser/dictionnary_spec.rb +8 -9
  155. data/spec/code/parser/function_spec.rb +5 -21
  156. data/spec/code/parser/group_spec.rb +18 -0
  157. data/spec/code/parser/list_spec.rb +9 -20
  158. data/spec/code/parser/number_spec.rb +4 -109
  159. data/spec/code/parser/string_spec.rb +9 -17
  160. data/spec/code/parser_spec.rb +23 -0
  161. data/spec/code/power_spec.rb +13 -0
  162. data/spec/code/range_spec.rb +16 -0
  163. data/spec/code/rescue_spec.rb +13 -0
  164. data/spec/code/shift_spec.rb +13 -0
  165. data/spec/code/splat_spec.rb +13 -0
  166. data/spec/code/string_spec.rb +25 -0
  167. data/spec/code/ternary_spec.rb +18 -0
  168. data/spec/code/unary_minus_spec.rb +13 -0
  169. data/spec/code/while_spec.rb +18 -0
  170. data/spec/spec_helper.rb +4 -1
  171. data/template-ruby.gemspec +2 -4
  172. metadata +110 -71
  173. data/lib/code/node/base_10_decimal.rb +0 -32
  174. data/lib/code/node/base_10_integer.rb +0 -32
  175. data/lib/code/node/base_10_number.rb +0 -19
  176. data/lib/code/node/base_16_number.rb +0 -19
  177. data/lib/code/node/base_2_number.rb +0 -19
  178. data/lib/code/node/base_8_number.rb +0 -19
  179. data/lib/code/node/block.rb +0 -17
  180. data/lib/code/node/defined.rb +0 -19
  181. data/lib/code/node/dictionnary_key_value.rb +0 -23
  182. data/lib/code/node/function_argument.rb +0 -45
  183. data/lib/code/node/group.rb +0 -13
  184. data/lib/code/node/keyword_call_argument.rb +0 -30
  185. data/lib/code/node/keyword_function_argument.rb +0 -33
  186. data/lib/code/node/name.rb +0 -28
  187. data/lib/code/node/not_keyword.rb +0 -13
  188. data/lib/code/node/or_keyword.rb +0 -34
  189. data/lib/code/node/range.rb +0 -31
  190. data/lib/code/node/regular_call_argument.rb +0 -34
  191. data/lib/code/node/regular_function_argument.rb +0 -36
  192. data/lib/code/node/string_characters.rb +0 -13
  193. data/lib/code/node/string_component.rb +0 -23
  194. data/lib/code/node/string_interpolation.rb +0 -13
  195. data/lib/code/parser/defined.rb +0 -20
  196. data/lib/code/parser/greater_than.rb +0 -33
  197. data/spec/call_spec.rb +0 -22
  198. data/spec/code/error/type_error_spec.rb +0 -63
  199. data/spec/code/parser/name_spec.rb +0 -15
  200. data/spec/code/parser/nothing_spec.rb +0 -19
  201. data/spec/code_spec.rb +0 -182
  202. data/spec/function_spec.rb +0 -26
  203. data/spec/template/parser/template_spec.rb +0 -19
  204. data/spec/template_spec.rb +0 -52
data/lib/code/ruby.rb CHANGED
@@ -31,15 +31,15 @@ class Code
31
31
  ::Code::Object::Decimal.new(raw)
32
32
  elsif hash?
33
33
  ::Code::Object::Dictionnary.new(
34
- raw.map do |key, value|
35
- [::Code::Ruby.to_code(key), ::Code::Ruby.to_code(value)]
36
- end.to_h
34
+ raw
35
+ .map do |key, value|
36
+ [::Code::Ruby.to_code(key), ::Code::Ruby.to_code(value)]
37
+ end
38
+ .to_h
37
39
  )
38
40
  elsif array?
39
41
  ::Code::Object::List.new(
40
- raw.map do |element|
41
- ::Code::Ruby.to_code(key)
42
- end
42
+ raw.map { |element| ::Code::Ruby.to_code(key) }
43
43
  )
44
44
  elsif proc?
45
45
  ::Code::Object::RubyFunction.new(raw)
@@ -63,13 +63,14 @@ class Code
63
63
  elsif code_string?
64
64
  raw.raw
65
65
  elsif code_dictionnary?
66
- raw.raw.map do |key, value|
67
- [::Code::Ruby.to_code(key), ::Code::Ruby.to_code(value)]
68
- end.to_h
66
+ raw
67
+ .raw
68
+ .map do |key, value|
69
+ [::Code::Ruby.to_code(key), ::Code::Ruby.to_code(value)]
70
+ end
71
+ .to_h
69
72
  elsif code_list?
70
- raw.raw.map do |element|
71
- ::Code::Ruby.to_code(element)
72
- end
73
+ raw.raw.map { |element| ::Code::Ruby.to_code(element) }
73
74
  else
74
75
  raise "Unsupported class #{raw.class} for Code to Ruby conversion"
75
76
  end
data/lib/code-ruby.rb CHANGED
@@ -1,19 +1,10 @@
1
- require "parslet"
2
- require "zeitwerk"
3
1
  require "bigdecimal"
4
- require "active_support"
5
- require "active_support/core_ext/object/blank"
6
- require "active_support/core_ext/object/deep_dup"
7
2
  require "stringio"
8
3
  require "timeout"
4
+ require "zeitwerk"
9
5
 
10
6
  loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
11
7
  loader.ignore("#{__dir__}/template-ruby.rb")
12
8
  loader.ignore("#{__dir__}/code-ruby.rb")
9
+ loader.ignore("#{__dir__}/language-ruby.rb")
13
10
  loader.setup
14
-
15
- class Hash
16
- def multi_fetch(*keys)
17
- keys.map { |key| [key, fetch(key)] }.to_h
18
- end
19
- end
data/lib/code.rb CHANGED
@@ -1,35 +1,38 @@
1
1
  class Code
2
- GLOBALS = [:io, :context, :object]
2
+ EMPTY_STRING = ""
3
+ GLOBALS = %i[io context object]
3
4
  DEFAULT_TIMEOUT = Template::DEFAULT_TIMEOUT
4
5
 
5
- def initialize(input, io: $stdout, timeout: DEFAULT_TIMEOUT, ruby: {})
6
+ def initialize(input, io: StringIO.new, timeout: DEFAULT_TIMEOUT, ruby: {})
6
7
  @input = input
7
- @parsed =
8
- Timeout.timeout(timeout) { ::Code::Parser::Code.new.parse(@input) }
8
+ @parsed = Timeout.timeout(timeout) { ::Code::Parser.parse(@input).to_raw }
9
9
  @io = io
10
10
  @timeout = timeout || DEFAULT_TIMEOUT
11
11
  @ruby = ::Code::Ruby.to_code(ruby || {})
12
12
  end
13
13
 
14
- def self.evaluate(input, context = "", io: $stdout, timeout: DEFAULT_TIMEOUT, ruby: {})
14
+ def self.evaluate(
15
+ input,
16
+ context = "",
17
+ io: StringIO.new,
18
+ timeout: DEFAULT_TIMEOUT,
19
+ ruby: {}
20
+ )
15
21
  new(input, io: io, timeout: timeout, ruby: ruby).evaluate(context)
16
22
  end
17
23
 
18
24
  def evaluate(context = "")
19
25
  Timeout.timeout(timeout) do
20
- if context.present?
21
- context = ::Code.evaluate(
22
- context,
23
- timeout: timeout,
24
- io: io,
25
- ruby: ruby
26
- )
26
+ if context != EMPTY_STRING
27
+ context = ::Code.evaluate(context, timeout: timeout, io: io, ruby: ruby)
27
28
  else
28
29
  context = ::Code::Object::Dictionnary.new
29
30
  end
30
31
 
31
32
  if !context.is_a?(::Code::Object::Dictionnary)
32
- raise ::Code::Error::IncompatibleContext.new("context must be a dictionnary")
33
+ raise ::Code::Error::IncompatibleContext.new(
34
+ "context must be a dictionnary"
35
+ )
33
36
  end
34
37
 
35
38
  context = context.merge(ruby)
@@ -0,0 +1,343 @@
1
+ class Language
2
+ class Atom
3
+ class Rule < Atom
4
+ def initialize(name:)
5
+ @name = name
6
+ end
7
+
8
+ def parse(parser)
9
+ parser.find_rule!(@name).parse(parser)
10
+ end
11
+
12
+ def to_s
13
+ "rule(#{@name.inspect})"
14
+ end
15
+ end
16
+
17
+ class Any < Atom
18
+ def parse(parser)
19
+ parser.consume(1)
20
+ end
21
+
22
+ def to_s
23
+ "any"
24
+ end
25
+ end
26
+
27
+ class Then < Atom
28
+ def initialize(parent:, block:)
29
+ @parent = parent
30
+ @block = block
31
+ end
32
+
33
+ def parse(parser)
34
+ @parent.parse(parser)
35
+ parser.output = Output.from_raw(@block.call(parser.output.to_raw))
36
+ end
37
+
38
+ def to_s
39
+ "(#{@parent}).then {}"
40
+ end
41
+ end
42
+
43
+ class Repeat < Atom
44
+ def initialize(parent: nil, min: 0, max: nil)
45
+ @parent = parent
46
+ @min = min
47
+ @max = max
48
+ end
49
+
50
+ def parse(parser)
51
+ return unless @parent
52
+
53
+ if @max.nil?
54
+ @min.times { match(parser) }
55
+
56
+ begin
57
+ loop { match(parser) }
58
+ rescue Parser::Interuption
59
+ end
60
+ else
61
+ @min.times { match(parser) }
62
+
63
+ begin
64
+ (@max - @min).times { match(parser) }
65
+ rescue Parser::Interuption
66
+ end
67
+ end
68
+ end
69
+
70
+ def to_s
71
+ min = @min.zero? ? "" : @min.to_s
72
+ max = @max.nil? ? "" : ", #{@max}"
73
+ parenthesis = min.empty? && max.empty? ? "" : "(#{min}#{max})"
74
+
75
+ @parent ? "(#{@parent}).repeat#{parenthesis}" : "repeat#{parenthesis}"
76
+ end
77
+
78
+ private
79
+
80
+ def match(parser)
81
+ clone =
82
+ Parser.new(
83
+ root: self,
84
+ input: parser.input,
85
+ cursor: parser.cursor,
86
+ buffer: parser.buffer
87
+ )
88
+
89
+ @parent.parse(clone)
90
+
91
+ parser.cursor = clone.cursor
92
+ parser.buffer = clone.buffer
93
+ parser.output << clone.output
94
+ end
95
+ end
96
+
97
+ class Str < Atom
98
+ def initialize(string:)
99
+ @string = string
100
+ end
101
+
102
+ def parse(parser)
103
+ if parser.next?(@string)
104
+ parser.consume(@string.size)
105
+ else
106
+ raise Parser::Str::NotFound.new(parser, @string)
107
+ end
108
+ end
109
+
110
+ def to_s
111
+ "str(#{@string.inspect})"
112
+ end
113
+ end
114
+
115
+ class Absent < Atom
116
+ def initialize(parent: nil)
117
+ @parent = parent
118
+ end
119
+
120
+ def parse(parser)
121
+ clone =
122
+ Parser.new(
123
+ root: self,
124
+ input: parser.input,
125
+ cursor: parser.cursor,
126
+ buffer: parser.buffer
127
+ )
128
+ @parent.parse(clone) if @parent
129
+ rescue Parser::Interuption
130
+ else
131
+ raise Parser::Interuption.new(parser, self)
132
+ end
133
+
134
+ def to_s
135
+ @parent ? "(#{@parent}).absent" : "absent"
136
+ end
137
+ end
138
+
139
+ class Ignore < Atom
140
+ def initialize(parent: nil)
141
+ @parent = parent
142
+ end
143
+
144
+ def parse(parser)
145
+ clone =
146
+ Parser.new(
147
+ root: self,
148
+ input: parser.input,
149
+ cursor: parser.cursor,
150
+ buffer: parser.buffer
151
+ )
152
+ @parent.parse(clone) if @parent
153
+ parser.cursor = clone.cursor
154
+ end
155
+
156
+ def to_s
157
+ @parent ? "#{@parent}.ignore" : "ignore"
158
+ end
159
+ end
160
+
161
+ class Maybe < Atom
162
+ def initialize(parent:)
163
+ @parent = parent
164
+ end
165
+
166
+ def parse(parser)
167
+ clone =
168
+ Parser.new(
169
+ root: self,
170
+ input: parser.input,
171
+ cursor: parser.cursor,
172
+ buffer: parser.buffer
173
+ )
174
+
175
+ @parent.parse(clone)
176
+ rescue Parser::Interuption
177
+ else
178
+ parser.cursor = clone.cursor
179
+ parser.output = clone.output
180
+ end
181
+
182
+ def to_s
183
+ @parent ? "#{@parent}.maybe" : "maybe"
184
+ end
185
+ end
186
+
187
+ class Aka < Atom
188
+ def initialize(name:, parent:)
189
+ @name = name
190
+ @parent = parent
191
+ end
192
+
193
+ def parse(parser)
194
+ clone =
195
+ Parser.new(root: self, input: parser.input, cursor: parser.cursor)
196
+
197
+ require "colorize"
198
+ @parent.parse(clone)
199
+
200
+ if clone.output?
201
+ parser.output = Output.new(@name => clone.output)
202
+ else
203
+ parser.output[@name] = Output.new(clone.buffer)
204
+ parser.buffer = ""
205
+ end
206
+
207
+ parser.cursor = clone.cursor
208
+ end
209
+
210
+ def to_s
211
+ @parent ? "#{@parent}.aka(#{@name.inspect})" : "aka(#{@name.inspect})"
212
+ end
213
+ end
214
+
215
+ class Or < Atom
216
+ def initialize(left:, right:)
217
+ @left = left
218
+ @right = right
219
+ end
220
+
221
+ def parse(parser)
222
+ left_clone =
223
+ Parser.new(
224
+ root: self,
225
+ input: parser.input,
226
+ cursor: parser.cursor,
227
+ buffer: parser.buffer
228
+ )
229
+
230
+ right_clone =
231
+ Parser.new(
232
+ root: self,
233
+ input: parser.input,
234
+ cursor: parser.cursor,
235
+ buffer: parser.buffer
236
+ )
237
+
238
+ begin
239
+ @left.parse(left_clone)
240
+ parser.cursor = left_clone.cursor
241
+ parser.buffer = left_clone.buffer
242
+ parser.output.merge(left_clone.output)
243
+ rescue Parser::Interuption
244
+ @right.parse(right_clone)
245
+ parser.cursor = right_clone.cursor
246
+ parser.buffer = right_clone.buffer
247
+ parser.output.merge(right_clone.output)
248
+ end
249
+ end
250
+
251
+ def to_s
252
+ "((#{@left}) | (#{@right}))"
253
+ end
254
+ end
255
+
256
+ class And < Atom
257
+ def initialize(left:, right:)
258
+ @left = left
259
+ @right = right
260
+ end
261
+
262
+ def parse(parser)
263
+ @left.parse(parser)
264
+ right_clone =
265
+ Parser.new(
266
+ root: self,
267
+ input: parser.input,
268
+ cursor: parser.cursor,
269
+ buffer: parser.buffer
270
+ )
271
+ @right.parse(right_clone)
272
+ parser.cursor = right_clone.cursor
273
+ parser.buffer = right_clone.buffer
274
+
275
+ parser.output.merge(right_clone.output)
276
+ end
277
+
278
+ def to_s
279
+ "#{@left} >> #{@right}".to_s
280
+ end
281
+ end
282
+
283
+ def any
284
+ Any.new
285
+ end
286
+
287
+ def str(string)
288
+ Str.new(string: string)
289
+ end
290
+
291
+ def absent
292
+ Absent.new(parent: self)
293
+ end
294
+
295
+ def ignore
296
+ Ignore.new(parent: self)
297
+ end
298
+
299
+ def maybe
300
+ Maybe.new(parent: self)
301
+ end
302
+
303
+ def repeat(min = 0, max = nil)
304
+ Repeat.new(parent: self, min: min, max: max)
305
+ end
306
+
307
+ def aka(name)
308
+ Aka.new(parent: self, name: name)
309
+ end
310
+
311
+ def |(other)
312
+ Or.new(left: self, right: other)
313
+ end
314
+
315
+ def >>(other)
316
+ And.new(left: self, right: other)
317
+ end
318
+
319
+ def <<(other)
320
+ And.new(left: self, right: other)
321
+ end
322
+
323
+ def then(&block)
324
+ Then.new(parent: self, block: block)
325
+ end
326
+
327
+ def rule(name)
328
+ Rule.new(name: name)
329
+ end
330
+
331
+ def parse(parser)
332
+ raise NotImplementedError.new("#{self.class}#parse")
333
+ end
334
+
335
+ def to_s
336
+ ""
337
+ end
338
+
339
+ def inspect
340
+ to_s
341
+ end
342
+ end
343
+ end
@@ -0,0 +1,130 @@
1
+ class Language
2
+ class Output
3
+ attr_reader :raw
4
+
5
+ def initialize(raw = nil)
6
+ @raw = raw
7
+ end
8
+
9
+ def self.from_raw(raw)
10
+ if raw.is_a?(Array)
11
+ new(raw.map { |element| from_raw(element) })
12
+ elsif raw.is_a?(Hash)
13
+ new(raw.map { |key, value| [key, from_raw(value)] }.to_h)
14
+ else
15
+ new(raw)
16
+ end
17
+ end
18
+
19
+ def to_raw
20
+ if raw.is_a?(Array)
21
+ raw.map(&:to_raw)
22
+ elsif raw.is_a?(Hash)
23
+ raw.map { |key, value| [key, value.to_raw] }.to_h
24
+ else
25
+ raw
26
+ end
27
+ end
28
+
29
+ def clone
30
+ Output.new(@raw.clone)
31
+ end
32
+
33
+ def present?
34
+ @raw != nil
35
+ end
36
+
37
+ def ==(other)
38
+ other.is_a?(Output) ? raw == other.raw : raw == other
39
+ end
40
+
41
+ def to_s
42
+ raw.to_s
43
+ end
44
+
45
+ def inspect
46
+ raw.inspect
47
+ end
48
+
49
+ def []=(key, value)
50
+ case @raw
51
+ when NilClass
52
+ @raw = { key => value }
53
+ when String
54
+ @raw = { key => value }
55
+ when Array
56
+ @raw << Output.new({ key => value })
57
+ when Hash
58
+ @raw[key] = value
59
+ end
60
+ end
61
+
62
+ def merge(other)
63
+ case @raw
64
+ when NilClass
65
+ @raw = other.raw
66
+ when String
67
+ case other.raw
68
+ when String
69
+ @raw = @raw + other.raw
70
+ when Array
71
+ @raw = other.raw
72
+ when Hash
73
+ @raw = other.raw
74
+ end
75
+ when Array
76
+ case other.raw
77
+ when String
78
+ return
79
+ when Array
80
+ @raw = @raw + other.raw
81
+ when Hash
82
+ @raw << other
83
+ end
84
+ when Hash
85
+ case other.raw
86
+ when String
87
+ return
88
+ when Array
89
+ return
90
+ when Hash
91
+ @raw.merge!(other.raw)
92
+ end
93
+ end
94
+ end
95
+
96
+ def <<(other)
97
+ case @raw
98
+ when NilClass
99
+ case other.raw
100
+ when String
101
+ @raw = [other]
102
+ when Array
103
+ @raw = [other]
104
+ when Hash
105
+ @raw = [other]
106
+ end
107
+ when String
108
+ case other.raw
109
+ when String
110
+ @raw = @raw + other.raw
111
+ when Array
112
+ @raw = [other]
113
+ when Hash
114
+ @raw = [other]
115
+ end
116
+ when Array
117
+ @raw << other
118
+ when Hash
119
+ case other.raw
120
+ when String
121
+ return
122
+ when Array
123
+ return
124
+ when Hash
125
+ @raw.merge!(other.raw)
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,8 @@
1
+ class Language
2
+ class Parser
3
+ class Absent
4
+ class Present < Interuption
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ class Language
2
+ class Parser
3
+ class Absent
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class Language
2
+ class Parser
3
+ class EndOfInput < Interuption
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,38 @@
1
+ class Language
2
+ class Parser
3
+ class Interuption < StandardError
4
+ def initialize(parser, atom = Atom.new)
5
+ @parser = parser
6
+ @atom = atom
7
+ end
8
+
9
+ def message
10
+ "\n#{line}#{" " * column_index}^\n"
11
+ end
12
+
13
+ def line
14
+ l = input.lines[line_index]
15
+ l += "\n" if l[-1] != "\n"
16
+ l
17
+ end
18
+
19
+ def line_index
20
+ input[...cursor].count("\n")
21
+ end
22
+
23
+ def column_index
24
+ cursor - input.lines[...line_index].map(&:size).sum
25
+ end
26
+
27
+ private
28
+
29
+ def cursor
30
+ @parser.cursor
31
+ end
32
+
33
+ def input
34
+ @parser.input
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ class Language
2
+ class Parser
3
+ class NotEndOfInput < Interuption
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ class Language
2
+ class Parser
3
+ class Str
4
+ class NotFound < Interuption
5
+ def initialize(parser, string)
6
+ @parser = parser
7
+ @string = string
8
+ end
9
+
10
+ def message
11
+ "#{@string} not found\n#{super}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ class Language
2
+ class Parser
3
+ class Str
4
+ end
5
+ end
6
+ end