class Spitewaste::Assembler

Attributes

parser[R]

Public Class Methods

new(program, **options) click to toggle source
# File lib/spitewaste/assembler.rb, line 5
def initialize program, **options
  format = options[:format]
  format ||= Spitewaste.guess_format program
  unless %i[whitespace wsassembly assembly spitewaste].include? format
    raise ArgumentError, "unknown format for parse: #{format}"
  end

  parser =
    case format
    when :whitespace
      WhitespaceParser
    when :assembly
      AssemblyParser
    else # used for both Spitewaste and Whitespace assembly
      SpitewasteParser
    end

  @parser = parser.new(program, **options).tap &:parse
  @src = @parser.src if format == :spitewaste
  @instructions = @parser.instructions
end

Public Instance Methods

assemble!(format:, io: STDOUT, **options) click to toggle source
# File lib/spitewaste/assembler.rb, line 27
def assemble! format:, io: STDOUT, **options
  unless %i[whitespace wsassembly assembly codegen image].include? format
    raise ArgumentError, "unknown format for emit: #{format}"
  end

  emitter =
    case format
    when :whitespace
      WhitespaceEmitter
    when :wsassembly
      WSAssemblyEmitter
    when :codegen
      CodegenEmitter
    when :image
      ImageEmitter
    else
      AssemblyEmitter
    end

  # Worthwhile optimizations can only be done if we have a symbol table.
  optimize! if parser.respond_to? :symbol_table

  src = format == :wsassembly ? @src : @instructions
  emitter.new(src, **options).emit io: io
end
optimize!() click to toggle source
# File lib/spitewaste/assembler.rb, line 84
def optimize!
  @ip = 0
  while @instructions[@ip]
    next @ip -= 1 if optimize_constant_pow
    # next if optimize_constant_strpack
    @ip += 1
  end
end
optimize_constant_pow() click to toggle source
# File lib/spitewaste/assembler.rb, line 53
def optimize_constant_pow
  pow = parser.symbol_table['pow']

  case @instructions[@ip - 2, 3]
  in [[:push, base], [:push, exp], [:call, ^pow]]
    @instructions[@ip - 2, 3] = [[:push, base ** exp]]

  in [[:push, base], [:dup, _], [:call, ^pow]]
    @instructions[@ip - 2, 3] = [[:push, base ** base]]

  else nil
  end
end
optimize_constant_strpack() click to toggle source
# File lib/spitewaste/assembler.rb, line 67
def optimize_constant_strpack
  strpack = parser.symbol_table['strpack']
  return if @instructions[@ip] != [:call, strpack]

  # grab all the instructions between `push 0` and this `call strpack`
  return unless start = @instructions[0, @ip].rindex([:push, 0])
  between = @instructions[start + 1...@ip]

  bytes = []
  # optimization only applies if all of the intervening ops are pushes
  return unless between.all? { |op, arg| op == :push && bytes << arg }

  packed = bytes.reverse.zip(0..).sum { |b, e| b * 128 ** e }
  @instructions[start..@ip] = [[:push, packed]]
  @ip = start + 1
end
try_elide_main() click to toggle source
# File lib/spitewaste/assembler.rb, line 93
def try_elide_main
  if @instructions.first == [:label, 0]
    @instructions.shift unless @instructions.any? { |op, arg|
      arg == 0 && %i[jump jz jn call].include?(op)
    }
  end
end