Class: KaiserRuby::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/kaiser_ruby/parser.rb

Overview

Parser class goes through the Rockstar code provided and generates an intermediate tree from which we can output a proper Ruby program

Constant Summary collapse

POETIC_STRING_KEYWORDS =
%w[says].freeze
POETIC_NUMBER_KEYWORDS =
%w[is was were are 's 're].freeze
POETIC_NUMBER_CONTRACTIONS =
%w['s 're].freeze
POETIC_TYPE_KEYWORDS =
%w[is].freeze
%w[say whisper shout scream].freeze
LISTEN_TO_KEYWORDS =
['listen to'].freeze
LISTEN_KEYWORDS =
['listen'].freeze
BREAK_KEYWORDS =
['break', 'break it down'].freeze
CONTINUE_KEYWORDS =
['continue', 'take it to the top'].freeze
RETURN_KEYWORDS =
['give back'].freeze
INCREMENT_FIRST_KEYWORDS =
%w[build].freeze
INCREMENT_SECOND_KEYWORDS =
%w[up].freeze
DECREMENT_FIRST_KEYWORDS =
%w[knock].freeze
DECREMENT_SECOND_KEYWORDS =
%w[down].freeze
ASSIGNMENT_FIRST_KEYWORDS =
%w[put].freeze
ASSIGNMENT_SECOND_KEYWORDS =
%w[into].freeze
LET_ASSIGNMENT_FIRST_KEYWORDS =
%w[let].freeze
LET_ASSIGNMENT_SECOND_KEYWORDS =
%w[be].freeze
FUNCTION_KEYWORDS =
%w[takes].freeze
FUNCTION_SEPARATORS =
['and', ', and', "'n'", '&', ','].freeze
FUNCTION_CALL_KEYWORDS =
%w[taking].freeze
FUNCTION_CALL_SEPARATORS =
[', and', "'n'", '&', ','].freeze
IF_KEYWORDS =
%w[if].freeze
UNTIL_KEYWORDS =
%w[until].freeze
WHILE_KEYWORDS =
%w[while].freeze
ELSE_KEYWORDS =
%w[else].freeze
NULL_TYPE =
%w[null nothing nowhere nobody gone empty].freeze
TRUE_TYPE =
%w[true yes ok right].freeze
FALSE_TYPE =
%w[false no lies wrong].freeze
MYSTERIOUS_TYPE =
%w[mysterious].freeze
POETIC_TYPE_LITERALS =
MYSTERIOUS_TYPE + NULL_TYPE + TRUE_TYPE + FALSE_TYPE
COMMON_VARIABLE_KEYWORDS =
%w[a an the my your].freeze
PRONOUN_KEYWORDS =
%w[he him she her it its they them ze hir zie zir xe xem ve ver].freeze
ADDITION_KEYWORDS =
%w[plus with +].freeze
SUBTRACTION_KEYWORDS =
%w[minus without -].freeze
MULTIPLICATION_KEYWORDS =
%w[times of *].freeze
DIVISION_KEYWORDS =
%w[over /].freeze
MATH_OP_KEYWORDS =
ADDITION_KEYWORDS + SUBTRACTION_KEYWORDS + MULTIPLICATION_KEYWORDS + DIVISION_KEYWORDS
MATH_TOKENS =
%w[+ / * -].freeze
EQUALITY_KEYWORDS =
%w[is].freeze
INEQUALITY_KEYWORDS =
%w[isn't isnt ain't aint is\ not].freeze
GT_KEYWORDS =
['is higher than', 'is greater than', 'is bigger than', 'is stronger than'].freeze
GTE_KEYWORDS =
['is as high as', 'is as great as', 'is as big as', 'is as strong as'].freeze
LT_KEYWORDS =
['is lower than', 'is less than', 'is smaller than', 'is weaker than'].freeze
LTE_KEYWORDS =
['is as low as', 'is as little as', 'is as small as', 'is as weak as'].freeze
COMPARISON_KEYWORDS =
EQUALITY_KEYWORDS + INEQUALITY_KEYWORDS + GT_KEYWORDS + GTE_KEYWORDS + LT_KEYWORDS + LTE_KEYWORDS
FUNCTION_RESTRICTED_KEYWORDS =
MATH_OP_KEYWORDS + ['(?<!, )and', 'is', 'or', 'into', 'nor']
AND_KEYWORDS =
['(?<!, )and'].freeze
OR_KEYWORDS =
%w[or].freeze
NOR_KEYWORDS =
%w[nor].freeze
NOT_KEYWORDS =
['(?<!is )not'].freeze
LOGIC_KEYWORDS =
AND_KEYWORDS + OR_KEYWORDS + NOT_KEYWORDS + NOR_KEYWORDS
RESERVED_KEYWORDS =
LOGIC_KEYWORDS + MATH_OP_KEYWORDS + POETIC_TYPE_LITERALS

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input) ⇒ Parser

Returns a new instance of Parser.



72
73
74
75
# File 'lib/kaiser_ruby/parser.rb', line 72

def initialize(input)
  @raw_input = input
  @lines = input.gsub(/\(\n.*?\)/m, "\n").split(/\n/) # eat multiline comments
end

Instance Attribute Details

#linesObject (readonly)

Returns the value of attribute lines.



7
8
9
# File 'lib/kaiser_ruby/parser.rb', line 7

def lines
  @lines
end

#raw_inputObject (readonly)

Returns the value of attribute raw_input.



7
8
9
# File 'lib/kaiser_ruby/parser.rb', line 7

def raw_input
  @raw_input
end

#treeObject (readonly)

Returns the value of attribute tree.



7
8
9
# File 'lib/kaiser_ruby/parser.rb', line 7

def tree
  @tree
end

Instance Method Details

#add_to_tree(object) ⇒ Object

private



699
700
701
702
703
704
705
706
# File 'lib/kaiser_ruby/parser.rb', line 699

def add_to_tree(object)
  object.extend(Hashie::Extensions::DeepLocate)

  object[:current_scope] = @current_scope.last
  object[:nesting] = @nesting

  @tree << object
end

#consume_function_calls(string) ⇒ Object



400
401
402
403
404
405
406
407
408
409
# File 'lib/kaiser_ruby/parser.rb', line 400

def consume_function_calls(string)
  if string =~ prepared_regexp(FUNCTION_CALL_KEYWORDS)
    words = string.split prepared_regexp(FUNCTION_RESTRICTED_KEYWORDS)
    found_string = words.select { |w| w =~ /\btaking\b/ }.first
    @function_temp << found_string
    string = string.gsub(found_string, " func_#{@function_temp.count - 1} ")
  end

  string
end

#matches_all?(words, rxp) ⇒ Boolean

Returns:

  • (Boolean)


724
725
726
727
# File 'lib/kaiser_ruby/parser.rb', line 724

def matches_all?(words, rxp)
  regexp = rxp.is_a?(Regexp) ? rxp : prepared_regexp(rxp)
  words.all? { |w| w =~ regexp }
end

#matches_any?(words, rxp) ⇒ Boolean

Returns:

  • (Boolean)


719
720
721
722
# File 'lib/kaiser_ruby/parser.rb', line 719

def matches_any?(words, rxp)
  regexp = rxp.is_a?(Regexp) ? rxp : prepared_regexp(rxp)
  words.any? { |w| w =~ regexp }
end

#matches_consecutive?(words, first_rxp, second_rxp) ⇒ Boolean

Returns:

  • (Boolean)


729
730
731
732
733
734
# File 'lib/kaiser_ruby/parser.rb', line 729

def matches_consecutive?(words, first_rxp, second_rxp)
  first_idx = words.index { |w| w =~ prepared_regexp(first_rxp) }
  second_idx = words.index { |w| w =~ prepared_regexp(second_rxp) }

  !second_idx.nil? && !first_idx.nil? && second_idx.to_i - first_idx.to_i == 1
end

#matches_first?(words, rxp) ⇒ Boolean

Returns:

  • (Boolean)


736
737
738
# File 'lib/kaiser_ruby/parser.rb', line 736

def matches_first?(words, rxp)
  words.index { |w| w =~ prepared_regexp(rxp) }&.zero?
end

#matches_separate?(words, first_rxp, second_rxp) ⇒ Boolean

Returns:

  • (Boolean)


744
745
746
747
748
749
# File 'lib/kaiser_ruby/parser.rb', line 744

def matches_separate?(words, first_rxp, second_rxp)
  first_idx = words.index { |w| w =~ prepared_regexp(first_rxp) }
  second_idx = words.index { |w| w =~ prepared_regexp(second_rxp) }

  !second_idx.nil? && !first_idx.nil? && second_idx.to_i > first_idx.to_i
end

#matches_several_first?(line, rxp) ⇒ Boolean

Returns:

  • (Boolean)


740
741
742
# File 'lib/kaiser_ruby/parser.rb', line 740

def matches_several_first?(line, rxp)
  (line =~ prepared_regexp(rxp))&.zero?
end

#parseObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/kaiser_ruby/parser.rb', line 77

def parse
  @tree = []

  @tree.extend(Hashie::Extensions::DeepLocate)
  @function_temp = []
  @nesting = 0
  @nesting_has_else = false
  @current_scope = [nil]
  @lnum = 0

  # parse through lines to get the general structure (statements/flow control/functions/etc) out of it
  @lines.each_with_index do |line, lnum|
    @lnum = lnum
    parse_line(line)
  end

  func_calls = @tree.deep_locate(:passed_function_call)
  func_calls.each do |func|
    str = func[:passed_function_call]
    num = Integer(str.split('_').last)

    func[:passed_function_call] = parse_function_call(@function_temp[num])
  end

  @tree
end

#parse_addition(string) ⇒ Object



652
653
654
655
656
657
658
# File 'lib/kaiser_ruby/parser.rb', line 652

def parse_addition(string)
  words = string.rpartition prepared_regexp(ADDITION_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { addition: { left: left, right: right } }
end

#parse_and(string) ⇒ Object



479
480
481
482
483
484
485
# File 'lib/kaiser_ruby/parser.rb', line 479

def parse_and(string)
  words = string.rpartition prepared_regexp(AND_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { and: { left: left, right: right } }
end

#parse_argument(string) ⇒ Object



417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# File 'lib/kaiser_ruby/parser.rb', line 417

def parse_argument(string)
  str = parse_literal_string(string)
  return str if str

  exp = parse_logic_operation(string)
  return exp if exp

  cmp = parse_comparison(string)
  return cmp if cmp

  math = parse_math_operations(string)
  return math if math

  fcl2 = pass_function_calls(string)
  return fcl2 if fcl2

  fcl = parse_function_call(string)
  return fcl if fcl

  vals = parse_value_or_variable(string)
  return vals if vals
end

#parse_assignment(line) ⇒ Object



341
342
343
344
345
346
347
348
# File 'lib/kaiser_ruby/parser.rb', line 341

def parse_assignment(line)
  match_rxp = prepared_capture(ASSIGNMENT_FIRST_KEYWORDS, ASSIGNMENT_SECOND_KEYWORDS)
  right = parse_argument(line.match(match_rxp).captures.first.strip)
  left = parse_variables(line.match(match_rxp).captures.last.strip)
  left[:type] = :assignment

  { assignment: { left: left, right: right } }
end

#parse_breakObject



239
240
241
# File 'lib/kaiser_ruby/parser.rb', line 239

def parse_break
  { break: nil }
end

#parse_common_variable(string) ⇒ Object



606
607
608
609
610
611
# File 'lib/kaiser_ruby/parser.rb', line 606

def parse_common_variable(string)
  words = string.split(/\s/)

  words = words.map { |e| e.chars.select { |c| c =~ /[[:alpha:]]/ }.join }
  { variable_name: words.map(&:downcase).join('_') }
end

#parse_comparison(string) ⇒ Object



514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
# File 'lib/kaiser_ruby/parser.rb', line 514

def parse_comparison(string)
  return false if string.strip.start_with?('"') && string.strip.strip.end_with?('"') && string.count('"') == 2

  if string =~ prepared_regexp(GT_KEYWORDS)
    return parse_gt(string)
  elsif string =~ prepared_regexp(GTE_KEYWORDS)
    return parse_gte(string)
  elsif string =~ prepared_regexp(LT_KEYWORDS)
    return parse_lt(string)
  elsif string =~ prepared_regexp(LTE_KEYWORDS)
    return parse_lte(string)
  elsif string =~ prepared_regexp(INEQUALITY_KEYWORDS)
    return parse_inequality(string)
  elsif string =~ prepared_regexp(EQUALITY_KEYWORDS)
    return parse_equality(string)
  end

  false
end

#parse_continueObject



243
244
245
# File 'lib/kaiser_ruby/parser.rb', line 243

def parse_continue
  { continue: nil }
end

#parse_decrement(line) ⇒ Object



214
215
216
217
218
219
220
221
# File 'lib/kaiser_ruby/parser.rb', line 214

def parse_decrement(line)
  match_rxp = prepared_capture(DECREMENT_FIRST_KEYWORDS, DECREMENT_SECOND_KEYWORDS)
  var = line.match(match_rxp).captures.first.strip
  capture = parse_variables(var)
  capture[:amount] = line.split(var).last.scan(/\bdown\b/i).count

  { decrement: capture }
end

#parse_division(string) ⇒ Object



676
677
678
679
680
681
682
# File 'lib/kaiser_ruby/parser.rb', line 676

def parse_division(string)
  words = string.rpartition prepared_regexp(DIVISION_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { division: { left: left, right: right } }
end

#parse_elseObject



223
224
225
226
227
228
229
230
231
232
233
# File 'lib/kaiser_ruby/parser.rb', line 223

def parse_else
  if @nesting_has_else
    @nesting -= 1
    @nesting_has_else = false
    add_to_tree parse_empty_line
  else
    @nesting_has_else = true
  end

  { else: nil }
end

#parse_empty_lineObject



235
236
237
# File 'lib/kaiser_ruby/parser.rb', line 235

def parse_empty_line
  { empty_line: nil }
end

#parse_equality(string) ⇒ Object



534
535
536
537
538
539
540
# File 'lib/kaiser_ruby/parser.rb', line 534

def parse_equality(string)
  words = string.rpartition prepared_regexp(EQUALITY_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { equality: { left: left, right: right } }
end

#parse_function(line) ⇒ Object



392
393
394
395
396
397
398
# File 'lib/kaiser_ruby/parser.rb', line 392

def parse_function(line)
  words = line.split prepared_regexp(FUNCTION_KEYWORDS)
  funcname = parse_function_name(words.first.strip)
  argument = parse_function_definition_arguments(words.last.strip)

  { function: { name: funcname, argument: argument } }
end

#parse_function_call(line) ⇒ Object



269
270
271
272
273
274
275
276
277
278
# File 'lib/kaiser_ruby/parser.rb', line 269

def parse_function_call(line)
  words = line.split(/\s/)
  return false unless matches_any?(words, FUNCTION_CALL_KEYWORDS)

  words = line.split prepared_regexp(FUNCTION_CALL_KEYWORDS)
  left = parse_function_name(words.first.strip)
  right = parse_function_call_arguments(words.last.strip)

  { function_call: { left: left, right: right } }
end

#parse_function_call_arguments(string) ⇒ Object



259
260
261
262
263
264
265
266
267
# File 'lib/kaiser_ruby/parser.rb', line 259

def parse_function_call_arguments(string)
  words = string.split(Regexp.new(FUNCTION_CALL_SEPARATORS.join('|')))
  arguments = []
  words.each do |w|
    arguments << parse_value_or_variable(w.strip)
  end

  { argument_list: arguments }
end

#parse_function_definition_arguments(string) ⇒ Object



247
248
249
250
251
252
253
254
255
256
257
# File 'lib/kaiser_ruby/parser.rb', line 247

def parse_function_definition_arguments(string)
  words = string.split Regexp.new(FUNCTION_SEPARATORS.join('|'))
  arguments = []
  words.each do |w|
    arg = parse_value_or_variable(w.strip)
    arg[:local_variable_name] = arg.delete(:variable_name)
    arguments << arg
  end

  { argument_list: arguments }
end

#parse_function_name(string) ⇒ Object



600
601
602
603
604
# File 'lib/kaiser_ruby/parser.rb', line 600

def parse_function_name(string)
  fname = parse_variables(string)
  fname[:function_name] = fname.delete(:variable_name)
  fname
end

#parse_gt(string) ⇒ Object



550
551
552
553
554
555
556
# File 'lib/kaiser_ruby/parser.rb', line 550

def parse_gt(string)
  words = string.rpartition prepared_regexp(GT_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { gt: { left: left, right: right } }
end

#parse_gte(string) ⇒ Object



558
559
560
561
562
563
564
# File 'lib/kaiser_ruby/parser.rb', line 558

def parse_gte(string)
  words = string.rpartition prepared_regexp(GTE_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { gte: { left: left, right: right } }
end

#parse_if(line) ⇒ Object



368
369
370
371
372
373
374
# File 'lib/kaiser_ruby/parser.rb', line 368

def parse_if(line)
  words = line.split prepared_regexp(IF_KEYWORDS)
  arg = consume_function_calls(words.last.strip)
  argument = parse_argument(arg)

  { if: { argument: argument } }
end

#parse_increment(line) ⇒ Object



205
206
207
208
209
210
211
212
# File 'lib/kaiser_ruby/parser.rb', line 205

def parse_increment(line)
  match_rxp = prepared_capture(INCREMENT_FIRST_KEYWORDS, INCREMENT_SECOND_KEYWORDS)
  var = line.match(match_rxp).captures.first.strip
  capture = parse_variables(var)
  capture[:amount] = line.split(var).last.scan(/\bup\b/i).count

  { increment: capture }
end

#parse_inequality(string) ⇒ Object



542
543
544
545
546
547
548
# File 'lib/kaiser_ruby/parser.rb', line 542

def parse_inequality(string)
  words = string.rpartition prepared_regexp(INEQUALITY_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { inequality: { left: left, right: right } }
end

#parse_let_assignment(line) ⇒ Object



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/kaiser_ruby/parser.rb', line 350

def parse_let_assignment(line)
  match_rxp = prepared_capture(LET_ASSIGNMENT_FIRST_KEYWORDS, LET_ASSIGNMENT_SECOND_KEYWORDS)
  right = parse_argument(line.match(match_rxp).captures.last.strip)
  left = parse_variables(line.match(match_rxp).captures.first.strip)
  left[:type] = :assignment

  # if the right is an expression and its variable name is empty
  # then it's a compound assignment which we translate back to an explicit one
  right.extend(Hashie::Extensions::DeepLocate)
  unless right.deep_locate(:variable_name).empty?
    if right.deep_locate(:variable_name).first[:variable_name].empty?
      right.deep_locate(:variable_name).first[:variable_name] = left[:variable_name]
    end
  end

  { assignment: { left: left, right: right } }
end

#parse_line(line) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/kaiser_ruby/parser.rb', line 104

def parse_line(line)
  # consume comments and extra spaces
  line = line.gsub(/\(.*?\)/, '').strip

  if line.empty?
    if @nesting.positive?
      @current_scope.pop unless @current_scope[@nesting].nil?
      @nesting_has_else = false
      @nesting -= 1
    end

    @current_scope.pop if @nesting.zero?

    add_to_tree(parse_empty_line)
  else
    obj = parse_line_content(line)
    add_to_tree obj
    update_nesting obj
  end
end

#parse_line_content(line) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/kaiser_ruby/parser.rb', line 136

def parse_line_content(line)
  words = line.split(/\s/)

  if matches_first?(words, IF_KEYWORDS)
    return parse_if(line)
  elsif matches_first?(words, ELSE_KEYWORDS)
    return parse_else
  elsif matches_first?(words, WHILE_KEYWORDS)
    return parse_while(line)
  elsif matches_first?(words, UNTIL_KEYWORDS)
    return parse_until(line)
  elsif matches_separate?(words, ASSIGNMENT_FIRST_KEYWORDS, ASSIGNMENT_SECOND_KEYWORDS)
    return parse_assignment(line)
  elsif matches_separate?(words, LET_ASSIGNMENT_FIRST_KEYWORDS, LET_ASSIGNMENT_SECOND_KEYWORDS)
    return parse_let_assignment(line)
  elsif matches_several_first?(line, RETURN_KEYWORDS)
    return parse_return(line)
  elsif matches_first?(words, PRINT_KEYWORDS)
    return parse_print(line)
  else
    if matches_any?(words, POETIC_STRING_KEYWORDS)
      return parse_poetic_string(line)
    elsif matches_any?(words, POETIC_NUMBER_KEYWORDS)
      return parse_poetic_type_all(line)
    else
      return(parse_listen_to(line)) if matches_several_first?(line, LISTEN_TO_KEYWORDS)
      return(parse_listen) if matches_first?(words, LISTEN_KEYWORDS)
      return(parse_break) if matches_several_first?(line, BREAK_KEYWORDS)
      return(parse_continue) if matches_several_first?(line, CONTINUE_KEYWORDS)
      return(parse_function_call(line)) if matches_any?(words, FUNCTION_CALL_KEYWORDS)

      return parse_increment(line) if matches_separate?(words, INCREMENT_FIRST_KEYWORDS, INCREMENT_SECOND_KEYWORDS)
      return parse_decrement(line) if matches_separate?(words, DECREMENT_FIRST_KEYWORDS, DECREMENT_SECOND_KEYWORDS)
      return(parse_function(line)) if matches_any?(words, FUNCTION_KEYWORDS)
    end
  end

  raise KaiserRuby::RockstarSyntaxError, "couldn't parse line: #{line}:#{@lnum + 1}"
end

#parse_listenObject



193
194
195
# File 'lib/kaiser_ruby/parser.rb', line 193

def parse_listen
  { listen: nil }
end

#parse_listen_to(line) ⇒ Object



185
186
187
188
189
190
191
# File 'lib/kaiser_ruby/parser.rb', line 185

def parse_listen_to(line)
  words = line.split prepared_regexp(LISTEN_TO_KEYWORDS)
  arg = parse_variables(words.last.strip)
  arg[:type] = :assignment

  { listen_to: arg }
end

#parse_literal_number(string) ⇒ Object



690
691
692
693
694
695
# File 'lib/kaiser_ruby/parser.rb', line 690

def parse_literal_number(string)
  num = Float(string) rescue string
  return false unless num.is_a?(Float)

  { number: num }
end

#parse_literal_string(string) ⇒ Object



684
685
686
687
688
# File 'lib/kaiser_ruby/parser.rb', line 684

def parse_literal_string(string)
  return false unless string.strip.start_with?('"') && string.strip.end_with?('"') && string.count('"') == 2

  { string: string }
end

#parse_logic_operation(string) ⇒ Object



464
465
466
467
468
469
470
471
472
473
474
475
476
477
# File 'lib/kaiser_ruby/parser.rb', line 464

def parse_logic_operation(string)
  testable = string.partition(prepared_regexp(LOGIC_KEYWORDS))
  return false if testable.first.count('"').odd? || testable.last.count('"').odd?

  if string =~ prepared_regexp(AND_KEYWORDS)
    return parse_and(string)
  elsif string =~ prepared_regexp(OR_KEYWORDS)
    return parse_or(string)
  elsif string =~ prepared_regexp(NOR_KEYWORDS)
    return parse_nor(string)
  end

  false
end

#parse_lt(string) ⇒ Object



566
567
568
569
570
571
572
# File 'lib/kaiser_ruby/parser.rb', line 566

def parse_lt(string)
  words = string.rpartition prepared_regexp(LT_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { lt: { left: left, right: right } }
end

#parse_lte(string) ⇒ Object



574
575
576
577
578
579
580
# File 'lib/kaiser_ruby/parser.rb', line 574

def parse_lte(string)
  words = string.rpartition prepared_regexp(LTE_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { lte: { left: left, right: right } }
end

#parse_math_operations(string) ⇒ Object



634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
# File 'lib/kaiser_ruby/parser.rb', line 634

def parse_math_operations(string)
  return false if string.strip.start_with?('"') && string.strip.end_with?('"') && string.count('"') == 2

  words = string.split(/\s/)

  if matches_any?(words, MULTIPLICATION_KEYWORDS)
    return parse_multiplication(string)
  elsif matches_any?(words, DIVISION_KEYWORDS)
    return parse_division(string)
  elsif matches_any?(words, ADDITION_KEYWORDS)
    return parse_addition(string)
  elsif matches_any?(words, SUBTRACTION_KEYWORDS)
    return parse_subtraction(string)
  end

  false
end

#parse_multiplication(string) ⇒ Object



668
669
670
671
672
673
674
# File 'lib/kaiser_ruby/parser.rb', line 668

def parse_multiplication(string)
  words = string.rpartition prepared_regexp(MULTIPLICATION_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { multiplication: { left: left, right: right } }
end

#parse_nor(string) ⇒ Object



496
497
498
499
500
501
502
503
# File 'lib/kaiser_ruby/parser.rb', line 496

def parse_nor(string)
  words = string.rpartition prepared_regexp(NOR_KEYWORDS)

  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { nor: { left: left, right: right } }
end

#parse_not(string) ⇒ Object



505
506
507
508
509
510
511
512
# File 'lib/kaiser_ruby/parser.rb', line 505

def parse_not(string)
  return false if string !~ /(?<!is )\bnot\b/i

  words = string.split prepared_regexp(NOT_KEYWORDS)
  argument = parse_argument(words.last.strip)

  { not: argument }
end

#parse_or(string) ⇒ Object



487
488
489
490
491
492
493
494
# File 'lib/kaiser_ruby/parser.rb', line 487

def parse_or(string)
  words = string.rpartition prepared_regexp(OR_KEYWORDS)

  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { or: { left: left, right: right } }
end

#parse_poetic_number_value(string) ⇒ Object



457
458
459
460
461
462
# File 'lib/kaiser_ruby/parser.rb', line 457

def parse_poetic_number_value(string)
  num = parse_literal_number(string)
  return num if num

  { number_literal: string.strip }
end

#parse_poetic_string(line) ⇒ Object



280
281
282
283
284
285
286
287
# File 'lib/kaiser_ruby/parser.rb', line 280

def parse_poetic_string(line)
  words = line.partition prepared_regexp(POETIC_STRING_KEYWORDS)
  left = parse_variables(words.first.strip)
  right = { string: "\"#{words.last.strip}\"" }
  left[:type] = :assignment

  { poetic_string: { left: left, right: right } }
end

#parse_poetic_type_all(line) ⇒ Object



289
290
291
292
293
294
295
296
# File 'lib/kaiser_ruby/parser.rb', line 289

def parse_poetic_type_all(line)
  words = line.partition prepared_regexp(POETIC_NUMBER_KEYWORDS)
  left = parse_variables(words.first.strip)
  right = parse_type_value(words.last.strip)
  left[:type] = :assignment

  { poetic_type: { left: left, right: right } }
end

#parse_print(line) ⇒ Object

statements



177
178
179
180
181
182
183
# File 'lib/kaiser_ruby/parser.rb', line 177

def parse_print(line)
  words = line.partition prepared_regexp(PRINT_KEYWORDS)
  arg = consume_function_calls(words.last.strip)
  argument = parse_argument(arg)

  { print: argument }
end

#parse_pronounObject



626
627
628
# File 'lib/kaiser_ruby/parser.rb', line 626

def parse_pronoun
  { pronoun: nil }
end

#parse_proper_variable(string) ⇒ Object



613
614
615
616
617
618
619
620
621
622
623
624
# File 'lib/kaiser_ruby/parser.rb', line 613

def parse_proper_variable(string)
  words = string.split(/\s/)

  copied = words.dup
  copied.shift
  copied.each do |w|
    raise SyntaxError, "invalid proper variable name: #{string}:#{@lnum + 1}" unless w =~ /\A[[:upper:]]/
  end

  words = words.map { |e| e.chars.select { |c| c =~ /[[:alpha:]]/ }.join }
  { variable_name: words.map(&:downcase).join('_') }
end

#parse_return(line) ⇒ Object



197
198
199
200
201
202
203
# File 'lib/kaiser_ruby/parser.rb', line 197

def parse_return(line)
  words = line.split prepared_regexp(RETURN_KEYWORDS)
  arg = consume_function_calls(words.last.strip)
  argument = parse_argument(arg)

  { return: argument }
end

#parse_subtraction(string) ⇒ Object



660
661
662
663
664
665
666
# File 'lib/kaiser_ruby/parser.rb', line 660

def parse_subtraction(string)
  words = string.rpartition prepared_regexp(SUBTRACTION_KEYWORDS)
  left = parse_argument(words.first.strip)
  right = parse_argument(words.last.strip)

  { subtraction: { left: left, right: right } }
end

#parse_type_literal(string) ⇒ Object

Raises:

  • (SyntaxError)


324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/kaiser_ruby/parser.rb', line 324

def parse_type_literal(string)
  words = string.split(/\s/)
  raise SyntaxError, "too many words in poetic type literal: #{string}:#{@lnum + 1}" if words.size > 1

  if matches_first?(words, MYSTERIOUS_TYPE)
    { type: 'mysterious' }
  elsif matches_first?(words, NULL_TYPE)
    { type: 'null' }
  elsif matches_first?(words, TRUE_TYPE)
    { type: 'true' }
  elsif matches_first?(words, FALSE_TYPE)
    { type: 'false' }
  else
    raise SyntaxError, "unknown poetic type literal: #{string}:#{@lnum + 1}"
  end
end

#parse_type_value(string) ⇒ Object



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/kaiser_ruby/parser.rb', line 298

def parse_type_value(string)
  words = string.split(/\s/)

  if matches_first?(words, MYSTERIOUS_TYPE)
    raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum + 1}" if words.count > 1

    { type: 'mysterious' }
  elsif matches_first?(words, NULL_TYPE)
    raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum + 1}" if words.count > 1

    { type: 'null' }
  elsif matches_first?(words, TRUE_TYPE)
    raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum + 1}" if words.count > 1

    { type: 'true' }
  elsif matches_first?(words, FALSE_TYPE)
    raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum + 1}" if words.count > 1

    { type: 'false' }
  elsif string.strip.start_with?('"') && string.strip.end_with?('"')
    parse_literal_string(string)
  else
    parse_poetic_number_value(string)
  end
end

#parse_until(line) ⇒ Object



376
377
378
379
380
381
382
# File 'lib/kaiser_ruby/parser.rb', line 376

def parse_until(line)
  words = line.split prepared_regexp(UNTIL_KEYWORDS)
  arg = consume_function_calls(words.last.strip)
  argument = parse_argument(arg)

  { until: { argument: argument } }
end

#parse_value_or_variable(string) ⇒ Object



440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/kaiser_ruby/parser.rb', line 440

def parse_value_or_variable(string)
  nt = parse_not(string)
  return nt if nt

  str = parse_literal_string(string)
  return str if str

  num = parse_literal_number(string)
  return num if num

  vars = parse_variables(string)
  return vars if vars

  tpl = parse_type_literal(string)
  return tpl if tpl
end

#parse_variables(string) ⇒ Object



582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/kaiser_ruby/parser.rb', line 582

def parse_variables(string)
  words = string.split(/\s/)
  words = words.map { |e| e.chars.select { |c| c =~ /[[:alnum:]]|\./ }.join }
  string = words.join(' ')

  if string =~ prepared_regexp(PRONOUN_KEYWORDS)
    return parse_pronoun
  elsif matches_first?(words, COMMON_VARIABLE_KEYWORDS)
    return parse_common_variable(string)
  elsif matches_all?(words, /\A[[:upper:]]/) && string !~ prepared_regexp(RESERVED_KEYWORDS)
    return parse_proper_variable(string)
  elsif words.count == 1 && string !~ prepared_regexp(RESERVED_KEYWORDS)
    return prase_simple_variable(string)
  end

  false
end

#parse_while(line) ⇒ Object



384
385
386
387
388
389
390
# File 'lib/kaiser_ruby/parser.rb', line 384

def parse_while(line)
  words = line.split prepared_regexp(WHILE_KEYWORDS)
  arg = consume_function_calls(words.last.strip)
  argument = parse_argument(arg)

  { while: { argument: argument } }
end

#pass_function_calls(string) ⇒ Object



411
412
413
414
415
# File 'lib/kaiser_ruby/parser.rb', line 411

def pass_function_calls(string)
  return false unless string.strip =~ /func_\d+\Z/

  { passed_function_call: string }
end

#prase_simple_variable(string) ⇒ Object



630
631
632
# File 'lib/kaiser_ruby/parser.rb', line 630

def prase_simple_variable(string)
  { variable_name: string }
end

#prepared_capture(farr, sarr) ⇒ Object



713
714
715
716
717
# File 'lib/kaiser_ruby/parser.rb', line 713

def prepared_capture(farr, sarr)
  frxp = farr.map { |a| tokenize_word(a) }.join('|')
  srxp = sarr.map { |a| tokenize_word(a) }.join('|')
  Regexp.new(frxp + '(.*?)' + srxp + '(.*)', Regexp::IGNORECASE)
end

#prepared_regexp(array) ⇒ Object



708
709
710
711
# File 'lib/kaiser_ruby/parser.rb', line 708

def prepared_regexp(array)
  rxp = array.map { |a| tokenize_word(a) }.join('|')
  Regexp.new(rxp, Regexp::IGNORECASE)
end

#tokenize_word(word) ⇒ Object



751
752
753
754
755
# File 'lib/kaiser_ruby/parser.rb', line 751

def tokenize_word(word)
  return '\B' + Regexp.escape(word) + '\B' if MATH_TOKENS.include?(word) # apparently ' + ' is not a word so word boundaries don't work

  '\b' + word + '\b'
end

#update_nesting(object) ⇒ Object



125
126
127
128
129
130
131
132
133
134
# File 'lib/kaiser_ruby/parser.rb', line 125

def update_nesting(object)
  if %i[if function until while].include? object.keys.first
    @nesting += 1
    @nesting_has_else = false
  end

  if object.keys.first == :function
    @current_scope.push object.deep_locate(:function_name).first.dig(:function_name)
  end
end