class MicroCisc::Compile::Statement

Constants

FUNCTION_REGEX
IMM_REGEX
SUGAR_REGEX

Attributes

minimal[R]
original[R]

Public Class Methods

new(label_generator, statement, indexed_vars, equivalents) click to toggle source
# File lib/micro_cisc/compile/statement.rb, line 9
def initialize(label_generator, statement, indexed_vars, equivalents)
  @label_generator = label_generator
  @original = statement
  @minimal = filter_comments(statement)
  @indexed_vars = indexed_vars
  @equivalents = equivalents
end

Public Instance Methods

create_equivalent(name, arg, delta) click to toggle source
# File lib/micro_cisc/compile/statement.rb, line 54
def create_equivalent(name, arg, delta)
  arg = normalize(arg, delta)
  if arg.first.include?("mem") || arg.first.include?("reg")
    imm_val = arg.last
    # remove mem/reg part
    arg_num = arg[0][0..-4]
    name = name[1..-1]

    mem_name = "$#{name}"
    ref_name = "&#{name}"
    @indexed_vars.delete(mem_name)
    @indexed_vars.delete(ref_name)

    imm_str = " #{imm_val}.imm" if imm_val > 0
    @equivalents[mem_name] = "#{arg_num}mem#{imm_str}"
    @equivalents[ref_name] = "#{arg_num}reg#{imm_str}"
  else
    @indexed_vars.delete(name)
    @equivalents[name] = arg.first
  end
end
create_variable(name, arg, delta) click to toggle source
# File lib/micro_cisc/compile/statement.rb, line 35
def create_variable(name, arg, delta)
  unless arg.include?("mem") || arg.include?("reg")
    raise ArgumentError, "Indexed variable reference is not allowed for non-register arguments, use 'as' instead"
  end

  arg = normalize(arg, delta)
  imm_val = arg.last
  # remove mem/reg part
  arg_num = arg.first[0..-4]
  name = name[1..-1]

  mem_name = "$#{name}"
  ref_name = "&#{name}"
  @equivalents.delete(mem_name)
  @equivalents.delete(ref_name)
  @indexed_vars[mem_name] = ["#{arg_num}mem", imm_val]
  @indexed_vars[ref_name] = ["#{arg_num}reg", imm_val]
end
filter_comments(instruction) click to toggle source
# File lib/micro_cisc/compile/statement.rb, line 17
def filter_comments(instruction)
  # Remove all inline comments
  instruction = instruction.to_s.strip.gsub(/\/[^\/]*\//, '')
  # Remove all word comments
  instruction = instruction.gsub(/'[^\s]*/, '')
  # Remove all line comments
  instruction = instruction.gsub(/#.*/, '')
  # Single space
  instruction.gsub(/\s+/, ' ')
end
get_var(name) click to toggle source
# File lib/micro_cisc/compile/statement.rb, line 86
def get_var(name)
  val = @equivalents[name]
  val ||=
    begin
      pair = @indexed_vars[name]
      "#{pair.first} #{pair.last}.imm" if pair
    end
  val || name
end
normalize(arg, delta) click to toggle source
# File lib/micro_cisc/compile/statement.rb, line 28
def normalize(arg, delta)
  imm_matches = arg.scan(IMM_REGEX).flatten
  imm_val = imm_matches.map(&:to_i).sum + delta
  arg = arg.gsub(IMM_REGEX, '').strip
  [arg, imm_val]
end
parse() click to toggle source
# File lib/micro_cisc/compile/statement.rb, line 96
def parse
  instruction = nil
  names = []
  op = nil
  if FUNCTION_REGEX =~ @minimal
    return parse_function_call
  elsif SUGAR_REGEX =~ @minimal
    match = SUGAR_REGEX.match(@minimal)
    op = match['op']
    names = match['names'].split(',').map(&:strip)

    @minimal = match['param']
    if minimal.start_with?('copy') || minimal.start_with?('compute')
      instruction = Instruction.new(@label_generator, @minimal, original, self)
    else
      var = match['param']
      imm_match = IMM_REGEX.match(var)
      imm_val = imm_match ? imm_match['imm_val'].to_i : 0
      var = var.to_s.gsub(IMM_REGEX, '').strip
      @minimal = "#{get_var(var)}#{" #{imm_val}.imm" if imm_val != 0}"
    end
  else
    instruction = Instruction.new(@label_generator, @minimal, original, self)
  end
  if instruction && instruction.instruction?
    dest = instruction.dest
    if instruction.inc && instruction.inc > 0
      if dest > 4
        # pop
        update_variable("#{dest - 4}.mem", -1)
      else
        # push
        update_variable("#{dest}.mem", 1)
      end
    end
    if minimal.start_with?('copy') && dest > 4 && instruction.src == dest
      # Manually modifying a register
      update_variable("#{dest - 4}.mem", -1 * instruction.immediates.first)
    end
    dest -= 4 if dest > 4
    @minimal = "#{dest}.mem"
  end
  names.each_with_index do |name, index|
    if op == 'as'
      create_equivalent(name, @minimal, index)
    elsif op == '='
      create_variable(name, @minimal, index)
    end
  end
  [instruction].compact
end
parse_function_call() click to toggle source
# File lib/micro_cisc/compile/statement.rb, line 148
def parse_function_call
  match = FUNCTION_REGEX.match(@minimal)
  label = match['label']

  stack = match['stack']
  stack = get_var(stack)
  raise ArgumentError, "Invalid stack param, mem register expected: #{stack}" unless stack =~ /^[1-3]\.mem$/
  stackp = stack.sub('mem', 'reg')

  return_words = match['words'].to_i
  args = match['args'].split(',').map(&:strip)

  instructions = []
  if return_words > 0
    instruction = "copy #{stackp} -#{return_words}.imm #{stackp}"
    instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # return vars - #{original}", self)
  end

  instruction = "copy 0.reg #{args.size + 2}.imm #{stack} push"
  instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # return addr - #{original}", self)

  stack_delta = 1 + return_words
  args = args.each do |arg|
    arg = arg.split(' ').map { |a| get_var(a) || a }.join(' ')
    is_stack = arg.start_with?(stack)
    if is_stack
      offset = stack_delta
      if arg_imm = IMM_REGEX.match(arg)
        arg_imm = arg_imm['imm_val'].to_i(16)
        arg = arg.sub(IMM_REGEX, '')
      else
        arg_imm = 0
      end
      offset_immediate = (offset + arg_imm) > 0 ? " #{(offset + arg_imm)}.imm" : ''
      arg = "#{arg}#{offset_immediate}"
    end
    instruction = "copy #{arg} #{stack} push"
    stack_delta += 1
    instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # push arg - #{original}", self)
  end
  instruction = "copy 0.reg #{label}.disp 0.reg"
  instructions << Instruction.new(@label_generator, instruction, "  #{instruction} # call - #{original}", self)
  if return_words > 0
    update_variable(stack, return_words)
  end
  instructions
end
update_variable(arg, delta) click to toggle source
# File lib/micro_cisc/compile/statement.rb, line 76
def update_variable(arg, delta)
  arg = arg.sub('reg', 'mem')
  ['mem', 'reg'].each do |type|
    variable = arg.sub('mem', type)
    @indexed_vars.each do |name, value|
      value[1] += delta if value.first == variable
    end
  end
end