kaiser-ruby 0.5.1 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,710 @@
1
+ module KaiserRuby
2
+ class Parser
3
+ attr_reader :lines, :raw_input, :tree
4
+
5
+ POETIC_STRING_KEYWORDS = %w(says)
6
+ POETIC_NUMBER_KEYWORDS = %w(is was were are 's 're)
7
+ POETIC_NUMBER_CONTRACTIONS = %w('s 're)
8
+ POETIC_TYPE_KEYWORDS = %w(is)
9
+ PRINT_KEYWORDS = %w(say whisper shout scream)
10
+ LISTEN_TO_KEYWORDS = ['listen to']
11
+ LISTEN_KEYWORDS = ['listen']
12
+ BREAK_KEYWORDS = ['break', 'break it down']
13
+ CONTINUE_KEYWORDS = ['continue', 'take it to the top']
14
+ RETURN_KEYWORDS = ['give back']
15
+
16
+ INCREMENT_FIRST_KEYWORDS = %w(build)
17
+ INCREMENT_SECOND_KEYWORDS = %w(up)
18
+ DECREMENT_FIRST_KEYWORDS = %w(knock)
19
+ DECREMENT_SECOND_KEYWORDS = %w(down)
20
+ ASSIGNMENT_FIRST_KEYWORDS = %w(put)
21
+ ASSIGNMENT_SECOND_KEYWORDS = %w(into)
22
+
23
+ FUNCTION_KEYWORDS = %w(takes)
24
+ FUNCTION_SEPARATORS = ['and', ', and', "'n'", '&', ',']
25
+ FUNCTION_CALL_KEYWORDS = %w(taking)
26
+ FUNCTION_CALL_SEPARATORS = [', and', "'n'", '&', ',']
27
+ IF_KEYWORDS = %w(if)
28
+ UNTIL_KEYWORDS = %w(until)
29
+ WHILE_KEYWORDS = %w(while)
30
+ ELSE_KEYWORDS = %w(else)
31
+
32
+ NULL_TYPE = %w(null nothing nowhere nobody gone empty)
33
+ TRUE_TYPE = %w(true yes ok right)
34
+ FALSE_TYPE = %w(false no lies wrong)
35
+ NIL_TYPE = %w(mysterious)
36
+ POETIC_TYPE_LITERALS = NIL_TYPE + NULL_TYPE + TRUE_TYPE + FALSE_TYPE
37
+
38
+ COMMON_VARIABLE_KEYWORDS = %w(a an the my your)
39
+ PRONOUN_KEYWORDS = %w(he him she her it its they them)
40
+
41
+ ADDITION_KEYWORDS = %w(plus with)
42
+ SUBTRACTION_KEYWORDS = %w(minus without)
43
+ MULTIPLICATION_KEYWORDS = %w(times of)
44
+ DIVISION_KEYWORDS = %w(over)
45
+ MATH_OP_KEYWORDS = ADDITION_KEYWORDS + SUBTRACTION_KEYWORDS + MULTIPLICATION_KEYWORDS + DIVISION_KEYWORDS
46
+
47
+ EQUALITY_KEYWORDS = %w(is)
48
+ INEQUALITY_KEYWORDS = %w(isn't isnt ain't aint is\ not)
49
+ GT_KEYWORDS = ['is higher than', 'is greater than', 'is bigger than', 'is stronger than']
50
+ GTE_KEYWORDS = ['is as high as', 'is as great as', 'is as big as', 'is as strong as']
51
+ LT_KEYWORDS = ['is lower than', 'is less than', 'is smaller than', 'is weaker than']
52
+ LTE_KEYWORDS = ['is as low as', 'is as little as', 'is as small as', 'is as weak as']
53
+ COMPARISON_KEYWORDS = EQUALITY_KEYWORDS + INEQUALITY_KEYWORDS + GT_KEYWORDS + GTE_KEYWORDS + LT_KEYWORDS + LTE_KEYWORDS
54
+
55
+ FUNCTION_RESTRICTED_KEYWORDS = MATH_OP_KEYWORDS + ['(?<!, )and', 'is', 'or', 'into', 'nor']
56
+
57
+ AND_KEYWORDS = %w(and)
58
+ OR_KEYWORDS = %w(or)
59
+ NOR_KEYWORDS = %w(nor)
60
+ NOT_KEYWORDS = ['(?<!is )not']
61
+ LOGIC_KEYWORDS = AND_KEYWORDS + OR_KEYWORDS + NOT_KEYWORDS + NOR_KEYWORDS
62
+
63
+ RESERVED_KEYWORDS = LOGIC_KEYWORDS + MATH_OP_KEYWORDS + POETIC_TYPE_LITERALS
64
+
65
+ def initialize(input)
66
+ @raw_input = input
67
+ @lines = input.split /\n/
68
+ end
69
+
70
+ def parse
71
+ @tree = []
72
+
73
+ @tree.extend(Hashie::Extensions::DeepLocate)
74
+ @function_temp = []
75
+ @nesting = 0
76
+ @nesting_start_line = 0
77
+ @lnum = 0
78
+
79
+ # parse through lines to get the general structure (statements/flow control/functions/etc) out of it
80
+ @lines.each_with_index do |line, lnum|
81
+ @lnum = lnum
82
+ parse_line(line)
83
+ end
84
+
85
+ func_calls = @tree.deep_locate(:passed_function_call)
86
+ func_calls.each do |func|
87
+ str = func[:passed_function_call]
88
+ num = Integer(str.split('_').last)
89
+
90
+ func[:passed_function_call] = parse_function_call(@function_temp[num])
91
+ end
92
+
93
+ @tree
94
+ end
95
+
96
+ def parse_line(line)
97
+ if line.strip.empty?
98
+ if @nesting > 0
99
+ @nesting -= 1
100
+ @nesting_start_line = nil
101
+ end
102
+
103
+ add_to_tree(parse_empty_line)
104
+ else
105
+ obj = parse_line_content(line)
106
+ add_to_tree obj
107
+ update_nesting obj
108
+ end
109
+ end
110
+
111
+ def update_nesting(object)
112
+ if %i(if function until while).include? object.keys.first
113
+ @nesting += 1
114
+ @nesting_start_line = @lnum
115
+ end
116
+ end
117
+
118
+ def parse_line_content(line)
119
+ words = line.split /\s/
120
+
121
+ if matches_first?(words, IF_KEYWORDS)
122
+ return parse_if(line)
123
+ elsif matches_first?(words, ELSE_KEYWORDS)
124
+ return parse_else
125
+ elsif matches_first?(words, WHILE_KEYWORDS)
126
+ return parse_while(line)
127
+ elsif matches_first?(words, UNTIL_KEYWORDS)
128
+ return parse_until(line)
129
+ elsif matches_separate?(words, ASSIGNMENT_FIRST_KEYWORDS, ASSIGNMENT_SECOND_KEYWORDS)
130
+ return parse_assignment(line)
131
+ elsif matches_several_first?(line, RETURN_KEYWORDS)
132
+ return parse_return(line)
133
+ elsif matches_first?(words, PRINT_KEYWORDS)
134
+ return parse_print(line)
135
+ else
136
+ if matches_any?(words, POETIC_STRING_KEYWORDS)
137
+ return parse_poetic_string(line)
138
+ elsif matches_any?(words, POETIC_NUMBER_KEYWORDS)
139
+ return parse_poetic_type_all(line)
140
+ else
141
+ return(parse_listen_to(line)) if matches_several_first?(line, LISTEN_TO_KEYWORDS)
142
+ return(parse_listen) if matches_first?(words, LISTEN_KEYWORDS)
143
+ return(parse_break) if matches_several_first?(line, BREAK_KEYWORDS)
144
+ return(parse_continue) if matches_several_first?(line, CONTINUE_KEYWORDS)
145
+ return(parse_function_call(line)) if matches_any?(words, FUNCTION_CALL_KEYWORDS)
146
+
147
+ if matches_separate?(words, INCREMENT_FIRST_KEYWORDS, INCREMENT_SECOND_KEYWORDS)
148
+ return parse_increment(line)
149
+ end
150
+
151
+ if matches_separate?(words, DECREMENT_FIRST_KEYWORDS, DECREMENT_SECOND_KEYWORDS)
152
+ return parse_decrement(line)
153
+ end
154
+
155
+ if matches_any?(words, FUNCTION_KEYWORDS)
156
+ return(parse_function(line))
157
+ end
158
+ end
159
+ end
160
+
161
+ raise KaiserRuby::RockstarSyntaxError, "couldn't parse line: #{line}"
162
+ end
163
+
164
+ # statements
165
+ def parse_print(line)
166
+ words = line.partition prepared_regexp(PRINT_KEYWORDS)
167
+ arg = consume_function_calls(words.last.strip)
168
+ argument = parse_argument(arg)
169
+
170
+ { print: argument }
171
+ end
172
+
173
+ def parse_listen_to(line)
174
+ words = line.split prepared_regexp(LISTEN_TO_KEYWORDS)
175
+ arg = parse_variables(words.last.strip)
176
+ arg[:type] = :assignment
177
+ { listen_to: arg }
178
+ end
179
+
180
+ def parse_listen
181
+ { listen: nil }
182
+ end
183
+
184
+ def parse_return(line)
185
+ words = line.split prepared_regexp(RETURN_KEYWORDS)
186
+ arg = consume_function_calls(words.last.strip)
187
+ argument = parse_argument(arg)
188
+ { return: argument }
189
+ end
190
+
191
+ def parse_increment(line)
192
+ match_rxp = prepared_capture(INCREMENT_FIRST_KEYWORDS, INCREMENT_SECOND_KEYWORDS)
193
+ var = line.match(match_rxp).captures.first.strip
194
+ capture = parse_variables(var)
195
+
196
+ capture[:amount] = line.split(var).last.scan(/\bup\b/i).count
197
+ { increment: capture }
198
+ end
199
+
200
+ def parse_decrement(line)
201
+ match_rxp = prepared_capture(DECREMENT_FIRST_KEYWORDS, DECREMENT_SECOND_KEYWORDS)
202
+ var = line.match(match_rxp).captures.first.strip
203
+ capture = parse_variables(var)
204
+
205
+ capture[:amount] = line.split(var).last.scan(/\bdown\b/i).count
206
+ { decrement: capture }
207
+ end
208
+
209
+ def parse_else
210
+ { else: nil }
211
+ end
212
+
213
+ def parse_empty_line
214
+ { empty_line: nil }
215
+ end
216
+
217
+ def parse_break
218
+ { break: nil }
219
+ end
220
+
221
+ def parse_continue
222
+ { continue: nil }
223
+ end
224
+
225
+ def parse_function_definition_arguments(string)
226
+ words = string.split Regexp.new(FUNCTION_SEPARATORS.join('|'))
227
+ arguments = []
228
+ words.each do |w|
229
+ arg = parse_value_or_variable(w.strip)
230
+ arg[:local_variable_name] = arg.delete(:variable_name)
231
+ arguments << arg
232
+ end
233
+
234
+ { argument_list: arguments }
235
+ end
236
+
237
+ def parse_function_call_arguments(string)
238
+ words = string.split(Regexp.new(FUNCTION_CALL_SEPARATORS.join('|')))
239
+ arguments = []
240
+ words.each do |w|
241
+ arguments << parse_value_or_variable(w.strip)
242
+ end
243
+
244
+ { argument_list: arguments }
245
+ end
246
+
247
+ def parse_function_call(line)
248
+ words = line.split /\s/
249
+ if matches_any?(words, FUNCTION_CALL_KEYWORDS)
250
+ words = line.split prepared_regexp(FUNCTION_CALL_KEYWORDS)
251
+ left = parse_function_name(words.first.strip)
252
+ right = parse_function_call_arguments(words.last.strip)
253
+ { function_call: { left: left, right: right } }
254
+ else
255
+ return false
256
+ end
257
+ end
258
+
259
+ def parse_poetic_string(line)
260
+ words = line.partition prepared_regexp(POETIC_STRING_KEYWORDS)
261
+ left = parse_variables(words.first.strip)
262
+ right = { string: "\"#{words.last.strip}\"" }
263
+ left[:type] = :assignment
264
+ { poetic_string: { left: left, right: right } }
265
+ end
266
+
267
+ def parse_poetic_type_all(line)
268
+ words = line.partition prepared_regexp(POETIC_NUMBER_KEYWORDS)
269
+ left = parse_variables(words.first.strip)
270
+ right = parse_type_value(words.last.strip)
271
+ left[:type] = :assignment
272
+ { poetic_type: { left: left, right: right } }
273
+ end
274
+
275
+ def parse_type_value(string)
276
+ words = string.split /\s/
277
+
278
+ if matches_first?(words, NIL_TYPE)
279
+ raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword" if words.count > 1
280
+ { type: 'nil' }
281
+ elsif matches_first?(words, NULL_TYPE)
282
+ raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword" if words.count > 1
283
+ { type: 'null' }
284
+ elsif matches_first?(words, TRUE_TYPE)
285
+ raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword" if words.count > 1
286
+ { type: 'true' }
287
+ elsif matches_first?(words, FALSE_TYPE)
288
+ raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword" if words.count > 1
289
+ { type: 'false' }
290
+ elsif string.strip.start_with?('"') && string.strip.end_with?('"')
291
+ parse_literal_string(string)
292
+ else
293
+ parse_poetic_number_value(string)
294
+ end
295
+ end
296
+
297
+ def parse_type_literal(string)
298
+ words = string.split /\s/
299
+ raise SyntaxError, "too many words in poetic type literal: #{string}" if words.size > 1
300
+
301
+ if matches_first?(words, NIL_TYPE)
302
+ { type: 'nil' }
303
+ elsif matches_first?(words, NULL_TYPE)
304
+ { type: 'null' }
305
+ elsif matches_first?(words, TRUE_TYPE)
306
+ { type: 'true' }
307
+ elsif matches_first?(words, FALSE_TYPE)
308
+ { type: 'false' }
309
+ else
310
+ raise SyntaxError, "unknown poetic type literal: #{string}"
311
+ end
312
+ end
313
+
314
+ def parse_assignment(line)
315
+ match_rxp = prepared_capture(ASSIGNMENT_FIRST_KEYWORDS, ASSIGNMENT_SECOND_KEYWORDS)
316
+ right = parse_argument(line.match(match_rxp).captures.first.strip)
317
+ left = parse_variables(line.match(match_rxp).captures.last.strip)
318
+ left[:type] = :assignment
319
+ { assignment: { left: left, right: right } }
320
+ end
321
+
322
+ def parse_if(line)
323
+ words = line.split prepared_regexp(IF_KEYWORDS)
324
+
325
+ arg = consume_function_calls(words.last.strip)
326
+ argument = parse_argument(arg)
327
+ { if: { argument: argument } }
328
+ end
329
+
330
+ def parse_until(line)
331
+ words = line.split prepared_regexp(UNTIL_KEYWORDS)
332
+ arg = consume_function_calls(words.last.strip)
333
+ argument = parse_argument(arg)
334
+ { until: { argument: argument } }
335
+ end
336
+
337
+ def parse_while(line)
338
+ words = line.split prepared_regexp(WHILE_KEYWORDS)
339
+ arg = consume_function_calls(words.last.strip)
340
+ argument = parse_argument(arg)
341
+ { while: { argument: argument } }
342
+ end
343
+
344
+ def parse_function(line)
345
+ words = line.split prepared_regexp(FUNCTION_KEYWORDS)
346
+ funcname = parse_function_name(words.first.strip)
347
+ argument = parse_function_definition_arguments(words.last.strip)
348
+ { function: { name: funcname, argument: argument } }
349
+ end
350
+
351
+ def consume_function_calls(string)
352
+ if string =~ prepared_regexp(FUNCTION_CALL_KEYWORDS)
353
+ words = string.split prepared_regexp(FUNCTION_RESTRICTED_KEYWORDS)
354
+ found_string = words.select { |w| w =~ (/\btaking\b/) }.first
355
+ @function_temp << found_string
356
+ string = string.gsub(found_string, " func_#{@function_temp.count - 1} ")
357
+ end
358
+
359
+ string
360
+ end
361
+
362
+ def pass_function_calls(string)
363
+ if string.strip =~ /func_\d+\Z/
364
+ { passed_function_call: string }
365
+ else
366
+ return false
367
+ end
368
+ end
369
+
370
+ def parse_argument(string)
371
+ str = parse_literal_string(string)
372
+ return str if str
373
+
374
+ exp = parse_logic_operation(string)
375
+ return exp if exp
376
+
377
+ cmp = parse_comparison(string)
378
+ return cmp if cmp
379
+
380
+ math = parse_math_operations(string)
381
+ return math if math
382
+
383
+ fcl2 = pass_function_calls(string)
384
+ return fcl2 if fcl2
385
+
386
+ fcl = parse_function_call(string)
387
+ return fcl if fcl
388
+
389
+ vals = parse_value_or_variable(string)
390
+ return vals if vals
391
+ end
392
+
393
+ def parse_value_or_variable(string)
394
+ nt = parse_not(string)
395
+ return nt if nt
396
+
397
+ str = parse_literal_string(string)
398
+ return str if str
399
+
400
+ num = parse_literal_number(string)
401
+ return num if num
402
+
403
+ vars = parse_variables(string)
404
+ return vars if vars
405
+
406
+ tpl = parse_type_literal(string)
407
+ return tpl if tpl
408
+ end
409
+
410
+ def parse_poetic_number_value(string)
411
+ num = parse_literal_number(string)
412
+ if num
413
+ return num
414
+ else
415
+ return { number_literal: string.strip }
416
+ end
417
+ end
418
+
419
+ def parse_logic_operation(string)
420
+ testable = string.partition(prepared_regexp(LOGIC_KEYWORDS))
421
+ return false if testable.first.count('"').odd? || testable.last.count('"').odd?
422
+
423
+ if string =~ prepared_regexp(AND_KEYWORDS)
424
+ return parse_and(string)
425
+ elsif string =~ prepared_regexp(OR_KEYWORDS)
426
+ return parse_or(string)
427
+ elsif string =~ prepared_regexp(NOR_KEYWORDS)
428
+ return parse_nor(string)
429
+ end
430
+
431
+ return false
432
+ end
433
+
434
+ def parse_and(string)
435
+ words = string.rpartition prepared_regexp(AND_KEYWORDS)
436
+ left = parse_argument(words.first.strip)
437
+ right = parse_argument(words.last.strip)
438
+
439
+ { and: { left: left, right: right } }
440
+ end
441
+
442
+ def parse_or(string)
443
+ words = string.rpartition prepared_regexp(OR_KEYWORDS)
444
+
445
+ left = parse_argument(words.first.strip)
446
+ right = parse_argument(words.last.strip)
447
+
448
+ { or: { left: left, right: right } }
449
+ end
450
+
451
+ def parse_nor(string)
452
+ words = string.rpartition prepared_regexp(NOR_KEYWORDS)
453
+
454
+ left = parse_argument(words.first.strip)
455
+ right = parse_argument(words.last.strip)
456
+
457
+ { nor: { left: left, right: right } }
458
+ end
459
+
460
+ def parse_not(string)
461
+ return false if string !~ /(?<!is )\bnot\b/i
462
+ words = string.split prepared_regexp(NOT_KEYWORDS)
463
+ argument = parse_argument(words.last.strip)
464
+
465
+ { not: argument }
466
+ end
467
+
468
+ def parse_comparison(string)
469
+ return false if string.strip.start_with?('"') && string.strip.strip.end_with?('"') && string.count('"') == 2
470
+ words = string.split(/\s/)
471
+
472
+ if string =~ prepared_regexp(GT_KEYWORDS)
473
+ return parse_gt(string)
474
+ elsif string =~ prepared_regexp(GTE_KEYWORDS)
475
+ return parse_gte(string)
476
+ elsif string =~ prepared_regexp(LT_KEYWORDS)
477
+ return parse_lt(string)
478
+ elsif string =~ prepared_regexp(LTE_KEYWORDS)
479
+ return parse_lte(string)
480
+ elsif string =~ prepared_regexp(INEQUALITY_KEYWORDS)
481
+ return parse_inequality(string)
482
+ elsif string =~ prepared_regexp(EQUALITY_KEYWORDS)
483
+ return parse_equality(string)
484
+ end
485
+
486
+ return false
487
+ end
488
+
489
+ def parse_equality(string)
490
+ words = string.rpartition prepared_regexp(EQUALITY_KEYWORDS)
491
+ left = parse_argument(words.first.strip)
492
+ right = parse_argument(words.last.strip)
493
+
494
+ { equality: { left: left, right: right } }
495
+ end
496
+
497
+ def parse_inequality(string)
498
+ words = string.rpartition prepared_regexp(INEQUALITY_KEYWORDS)
499
+ left = parse_argument(words.first.strip)
500
+ right = parse_argument(words.last.strip)
501
+
502
+ { inequality: { left: left, right: right } }
503
+ end
504
+
505
+ def parse_gt(string)
506
+ words = string.rpartition prepared_regexp(GT_KEYWORDS)
507
+ left = parse_argument(words.first.strip)
508
+ right = parse_argument(words.last.strip)
509
+
510
+ { gt: { left: left, right: right } }
511
+ end
512
+
513
+ def parse_gte(string)
514
+ words = string.rpartition prepared_regexp(GTE_KEYWORDS)
515
+ left = parse_argument(words.first.strip)
516
+ right = parse_argument(words.last.strip)
517
+
518
+ { gte: { left: left, right: right } }
519
+ end
520
+
521
+ def parse_lt(string)
522
+ words = string.rpartition prepared_regexp(LT_KEYWORDS)
523
+ left = parse_argument(words.first.strip)
524
+ right = parse_argument(words.last.strip)
525
+
526
+ { lt: { left: left, right: right } }
527
+ end
528
+
529
+ def parse_lte(string)
530
+ words = string.rpartition prepared_regexp(LTE_KEYWORDS)
531
+ left = parse_argument(words.first.strip)
532
+ right = parse_argument(words.last.strip)
533
+
534
+ { lte: { left: left, right: right } }
535
+ end
536
+
537
+ def parse_variables(string)
538
+ words = string.split /\s/
539
+ words = words.map { |e| e.chars.select { |c| c =~ /[[:alnum:]]|\./ }.join }
540
+ string = words.join(' ')
541
+
542
+ if string =~ prepared_regexp(PRONOUN_KEYWORDS)
543
+ return parse_pronoun
544
+ elsif matches_first?(words, COMMON_VARIABLE_KEYWORDS)
545
+ return parse_common_variable(string)
546
+ elsif matches_all?(words, /\A[[:upper:]]/) && string !~ prepared_regexp(RESERVED_KEYWORDS)
547
+ return parse_proper_variable(string)
548
+ end
549
+
550
+ return false
551
+ end
552
+
553
+ def parse_function_name(string)
554
+ fname = parse_variables(string)
555
+ fname[:function_name] = fname.delete(:variable_name)
556
+ fname
557
+ end
558
+
559
+ def parse_common_variable(string)
560
+ words = string.split(/\s/)
561
+
562
+ copied = words.dup
563
+ copied.shift
564
+ copied.each do |w|
565
+ raise SyntaxError, "invalid common variable name: #{string}" if w =~ /[[:upper:]]/
566
+ end
567
+
568
+ words = words.map { |e| e.chars.select { |c| c =~ /[[:alpha:]]/ }.join }
569
+ { variable_name: words.map { |w| w.downcase }.join('_') }
570
+ end
571
+
572
+ def parse_proper_variable(string)
573
+ words = string.split(/\s/)
574
+
575
+ copied = words.dup
576
+ copied.shift
577
+ copied.each do |w|
578
+ raise SyntaxError, "invalid proper variable name: #{string}" unless w =~ /\A[[:upper:]]/
579
+ end
580
+
581
+ words = words.map { |e| e.chars.select { |c| c =~ /[[:alpha:]]/ }.join }
582
+ { variable_name: words.map { |w| w.downcase }.join('_') }
583
+ end
584
+
585
+ def parse_pronoun
586
+ { pronoun: nil }
587
+ end
588
+
589
+ def parse_math_operations(string)
590
+ return false if string.strip.start_with?('"') && string.strip.end_with?('"') && string.count('"') == 2
591
+ words = string.split(/\s/)
592
+
593
+ if matches_any?(words, MULTIPLICATION_KEYWORDS)
594
+ return parse_multiplication(string)
595
+ elsif matches_any?(words, DIVISION_KEYWORDS)
596
+ return parse_division(string)
597
+ elsif matches_any?(words, ADDITION_KEYWORDS)
598
+ return parse_addition(string)
599
+ elsif matches_any?(words, SUBTRACTION_KEYWORDS)
600
+ return parse_subtraction(string)
601
+ end
602
+
603
+ return false
604
+ end
605
+
606
+ def parse_addition(string)
607
+ words = string.rpartition prepared_regexp(ADDITION_KEYWORDS)
608
+ left = parse_argument(words.first.strip)
609
+ right = parse_argument(words.last.strip)
610
+
611
+ { addition: { left: left, right: right } }
612
+ end
613
+
614
+ def parse_subtraction(string)
615
+ words = string.rpartition prepared_regexp(SUBTRACTION_KEYWORDS)
616
+ left = parse_argument(words.first.strip)
617
+ right = parse_argument(words.last.strip)
618
+
619
+ { subtraction: { left: left, right: right } }
620
+ end
621
+
622
+ def parse_multiplication(string)
623
+ words = string.rpartition prepared_regexp(MULTIPLICATION_KEYWORDS)
624
+ left = parse_argument(words.first.strip)
625
+ right = parse_argument(words.last.strip)
626
+
627
+ { multiplication: { left: left, right: right } }
628
+ end
629
+
630
+ def parse_division(string)
631
+ words = string.rpartition prepared_regexp(DIVISION_KEYWORDS)
632
+ left = parse_argument(words.first.strip)
633
+ right = parse_argument(words.last.strip)
634
+
635
+ { division: { left: left, right: right } }
636
+ end
637
+
638
+ def parse_literal_string(string)
639
+ if string.strip.start_with?('"') && string.strip.end_with?('"') && string.count('"') == 2
640
+ { string: string }
641
+ else
642
+ return false
643
+ end
644
+ end
645
+
646
+ def parse_literal_number(string)
647
+ num = Float(string) rescue string
648
+ if num.is_a?(Float)
649
+ { number: num }
650
+ else
651
+ return false
652
+ end
653
+ end
654
+
655
+ #private
656
+
657
+ def add_to_tree(object)
658
+ object.extend(Hashie::Extensions::DeepLocate)
659
+
660
+ if @nesting > 0
661
+ object[:nesting_start_line] = @nesting_start_line
662
+ object[:nesting] = @nesting
663
+ end
664
+ @tree << object
665
+ end
666
+
667
+ def prepared_regexp(array)
668
+ rxp = array.map { |a| '\b' + a + '\b' }.join('|')
669
+ Regexp.new(rxp, Regexp::IGNORECASE)
670
+ end
671
+
672
+ def prepared_capture(farr, sarr)
673
+ frxp = farr.map { |a| '\b' + a + '\b' }.join('|')
674
+ srxp = sarr.map { |a| '\b' + a + '\b' }.join('|')
675
+ Regexp.new(frxp + '(.*?)' + srxp + '(.*)', Regexp::IGNORECASE)
676
+ end
677
+
678
+ def matches_any?(words, rxp)
679
+ regexp = rxp.is_a?(Regexp) ? rxp : prepared_regexp(rxp)
680
+ words.any? { |w| w =~ regexp }
681
+ end
682
+
683
+ def matches_all?(words, rxp)
684
+ regexp = rxp.is_a?(Regexp) ? rxp : prepared_regexp(rxp)
685
+ words.all? { |w| w =~ regexp }
686
+ end
687
+
688
+ def matches_consecutive?(words, first_rxp, second_rxp)
689
+ first_idx = words.index { |w| w =~ prepared_regexp(first_rxp) }
690
+ second_idx = words.index { |w| w =~ prepared_regexp(second_rxp) }
691
+
692
+ second_idx != nil && first_idx != nil && second_idx.to_i - first_idx.to_i == 1
693
+ end
694
+
695
+ def matches_first?(words, rxp)
696
+ words.index { |w| w =~ prepared_regexp(rxp) } == 0
697
+ end
698
+
699
+ def matches_several_first?(line, rxp)
700
+ (line =~ prepared_regexp(rxp)) == 0
701
+ end
702
+
703
+ def matches_separate?(words, first_rxp, second_rxp)
704
+ first_idx = words.index { |w| w =~ prepared_regexp(first_rxp) }
705
+ second_idx = words.index { |w| w =~ prepared_regexp(second_rxp) }
706
+
707
+ second_idx != nil && first_idx != nil && second_idx.to_i > first_idx.to_i
708
+ end
709
+ end
710
+ end