class KaiserRuby::Parser

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

Constants

ADDITION_KEYWORDS
AND_KEYWORDS
ASSIGNMENT_FIRST_KEYWORDS
ASSIGNMENT_SECOND_KEYWORDS
BREAK_KEYWORDS
COMMON_VARIABLE_KEYWORDS
COMPARISON_KEYWORDS
CONTINUE_KEYWORDS
DECREMENT_FIRST_KEYWORDS
DECREMENT_SECOND_KEYWORDS
DIVISION_KEYWORDS
ELSE_KEYWORDS
EQUALITY_KEYWORDS
FALSE_TYPE
FUNCTION_CALL_KEYWORDS
FUNCTION_CALL_SEPARATORS
FUNCTION_KEYWORDS
FUNCTION_RESTRICTED_KEYWORDS
FUNCTION_SEPARATORS
GTE_KEYWORDS
GT_KEYWORDS
IF_KEYWORDS
INCREMENT_FIRST_KEYWORDS
INCREMENT_SECOND_KEYWORDS
INEQUALITY_KEYWORDS
LET_ASSIGNMENT_FIRST_KEYWORDS
LET_ASSIGNMENT_SECOND_KEYWORDS
LISTEN_KEYWORDS
LISTEN_TO_KEYWORDS
LOGIC_KEYWORDS
LTE_KEYWORDS
LT_KEYWORDS
MATH_OP_KEYWORDS
MATH_TOKENS
MULTIPLICATION_KEYWORDS
MYSTERIOUS_TYPE
NOR_KEYWORDS
NOT_KEYWORDS
NULL_TYPE
OR_KEYWORDS
POETIC_NUMBER_CONTRACTIONS
POETIC_NUMBER_KEYWORDS
POETIC_STRING_KEYWORDS
POETIC_TYPE_KEYWORDS
POETIC_TYPE_LITERALS
PRONOUN_KEYWORDS
RESERVED_KEYWORDS
RETURN_KEYWORDS
SUBTRACTION_KEYWORDS
TRUE_TYPE
UNTIL_KEYWORDS
WHILE_KEYWORDS

Attributes

lines[R]
raw_input[R]
tree[R]

Public Class Methods

new(input) click to toggle source
# 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

Public Instance Methods

add_to_tree(object) click to toggle source

private

# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# File lib/kaiser_ruby/parser.rb, line 740
def matches_several_first?(line, rxp)
  (line =~ prepared_regexp(rxp))&.zero?
end
parse() click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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_break() click to toggle source
# File lib/kaiser_ruby/parser.rb, line 239
def parse_break
  { break: nil }
end
parse_common_variable(string) click to toggle source
# 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) click to toggle source
# 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_continue() click to toggle source
# File lib/kaiser_ruby/parser.rb, line 243
def parse_continue
  { continue: nil }
end
parse_decrement(line) click to toggle source
# 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) click to toggle source
# 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_else() click to toggle source
# 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_line() click to toggle source
# File lib/kaiser_ruby/parser.rb, line 235
def parse_empty_line
  { empty_line: nil }
end
parse_equality(string) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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_listen() click to toggle source
# File lib/kaiser_ruby/parser.rb, line 193
def parse_listen
  { listen: nil }
end
parse_listen_to(line) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source

statements

# 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_pronoun() click to toggle source
# File lib/kaiser_ruby/parser.rb, line 626
def parse_pronoun
  { pronoun: nil }
end
parse_proper_variable(string) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# File lib/kaiser_ruby/parser.rb, line 630
def prase_simple_variable(string)
  { variable_name: string }
end
prepared_capture(farr, sarr) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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