Калоян обнови решението на 11.01.2014 20:10 (преди почти 11 години)
+module Asm
+ class Evaluator
+ def initialize
+ @instructions = []
+ @labels = {}
+ @registers = {ax: 0, bx: 0, cx: 0, dx: 0}
+ @cmp_flag = 0
+ @instruction_pointer = 0
+ end
+
+ def run
+ while @instruction_pointer < @instructions.size
+
+ name = @instructions[@instruction_pointer].name
+
+ operand_1 = @instructions[@instruction_pointer].operand_1
+ operand_2 = @instructions[@instruction_pointer].operand_2
+
+ if [:jmp, :je, :jne, :jl, :jle, :jg, :jge].include? name
+ execute_jump name, operand_1
+ else
+ execute_instruction name, operand_1, operand_2
+ @instruction_pointer += 1
+ end
+ end
+ end
+
+ def state
+ @registers.values
+ end
+
+ private
+
+ Instruction = Struct.new(:name, :operand_1, :operand_2)
+
+ INSTRUCTIONS = [:mov, :inc, :dec, :cmp, :jmp, :je, :jne, :jl, :jle, :jg, :jge]
+
+ INSTRUCTIONS.each do |instruction_name|
+ define_method instruction_name do |*arguments|
+ @instructions << Instruction.new(instruction_name, *arguments)
+ end
+ end
+
+ def method_missing(name)
+ name.to_sym
+ end
+
+ def label(label_name)
+ @labels[label_name] = @instructions.size
+ end
+
+ def get_value(value)
+ return value unless value.instance_of? Symbol
+
+ if @registers.has_key? value
+ @registers[value]
+ else
+ @labels[value]
+ end
+ end
+
+ def execute_instruction(type, operand_1, operand_2)
+ if type == :mov
+ @registers[operand_1] = get_value(operand_2)
+ elsif type == :inc
+ @registers[operand_1] += operand_2 ? get_value(operand_2) : 1
+ elsif type == :dec
+ @registers[operand_1] -= operand_2 ? get_value(operand_2) : 1
+ elsif type == :cmp
+ @cmp_flag = get_value(operand_1) <=> get_value(operand_2)
+ end
+ end
+
+ JUMP_CONDITIONS = { je: :==,
+ jne: :!=,
+ jl: :<,
+ jle: :<=,
+ jg: :>,
+ jge: :>=
+ }.freeze
+
+ def execute_jump(type, where)
+ if type != :jmp and not @cmp_flag.public_send JUMP_CONDITIONS[type], 0
+ return @instruction_pointer += 1
+ end
+
+ @instruction_pointer = get_value(where)
+ end
+ end
+
+ def self.asm(&block)
+ evaluator = Evaluator.new
+ evaluator.instance_eval(&block)
+ evaluator.run
+ evaluator.state
+ end
+end
Дотук добре. Има какво да се пипне, обаче:
- На няколко места повтаряш имената на инструкциите: в
Evaluator#run
, вINSTRUCTIONS
, вexecute_instruction
и вJUMP_CONDITIONS
. Това е в разрез с DRY принципа. Помисли как можеш да избегнеш повторенията. -
JUMP_CONDITIONS
изглежда по-добре така:JUMP_CONDITIONS = { je: :==, # etc. }
- Може би
execute_instruction
ще изглежда по-добре сcase
. -
if
-а вget_value
може да се замести сHash#fetch
. - Празен ред над последния ред в
Asm.asm
, понеже връщаме стойност и конвенцията повелява.
Като цяло най-важното нещо ми си струва премахването на повторенията. Гарантирано ще научиш още неща, ако се опиташ да ги премахнеш.