Решение на Четвърта задача от Мария Митева

Обратно към всички решения

Към профила на Мария Митева

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 8 успешни тест(а)
  • 0 неуспешни тест(а)

Код

module Asm
def self.asm(&block)
parser = Parser.new
parser.instance_eval &block
parser.operations
evaluator = Evaluator.new parser.labels
evaluator.evaluate parser.operations
evaluator.registers.map(&:value)
end
class Parser
attr_accessor :ax, :bx, :cx, :dx
attr_reader :operations, :labels
def initialize
@ax = :ax
@bx = :bx
@cx = :cx
@dx = :dx
@operations = []
@labels = []
@instruction_names = [:mov, :cmp, :inc, :dec, :jmp, :je, :jne, :jl, :jle, :jg, :jge]
end
def label(label)
label.position = @operations.size
@labels << label
end
def method_missing(name, *args)
if @instruction_names.include? name
@operations << [name, args]
else
if @labels.select { |label| label.name == name }.empty?
Label.new name
else
@labels.select { |label| label.name == name }.first
end
end
end
end
class Register
attr_accessor :value, :name
def initialize(name)
@value = 0
@name = name
end
end
class Label
attr_accessor :position, :name
def initialize(name="", position=-1)
@position = position
@name = name
end
end
class Evaluator
attr_reader :next
attr_accessor :ax, :bx, :cx, :dx
def initialize(labels)
@next = 0
@ax = Register.new :ax
@bx = Register.new :bx
@cx = Register.new :cx
@dx = Register.new :dx
@labels = labels
@last_cmp = 0
end
def evaluate(operations)
while @next < operations.size
operation, arg1, arg2 = operations[@next].flatten
arg1 = self.public_send arg1 if arg1.is_a? Symbol
arg2 = (self.public_send arg2).value if arg2.is_a? Symbol
if arg2
self.public_send operation, arg1, arg2
else
self.public_send operation, arg1
end
end
end
def registers
[@ax, @bx, @cx, @dx]
end
def mov(register, source)
register.value = source
@next += 1
end
def inc(register, value=1)
register.value += value
@next += 1
end
def dec(register, value=1)
register.value -= value
@next += 1
end
def cmp(register, source)
@last_cmp = register.value <=> source
@next += 1
end
def jmp(where)
if where.is_a? Label
where = @labels.select { |label| label.name == where.name }.first.position
end
@next = where
end
jumps = {
:je => :==,
:jne => :!=,
:jl => :<,
:jle => :<=,
:jg => :>,
:jge => :>=,
}
jumps.each do |jump, operation|
define_method jump.to_s do |where|
if @last_cmp.public_send operation, 0
return jmp where
end
@next += 1
end
end
end
end

Лог от изпълнението

........

Finished in 0.0076 seconds
8 examples, 0 failures

История (1 версия и 1 коментар)

Мария обнови решението на 14.01.2014 20:21 (преди почти 11 години)

+module Asm
+ def self.asm(&block)
+ parser = Parser.new
+ parser.instance_eval &block
+ parser.operations
+ evaluator = Evaluator.new parser.labels
+ evaluator.evaluate parser.operations
+ evaluator.registers.map(&:value)
+ end
+
+ class Parser
+ attr_accessor :ax, :bx, :cx, :dx
+ attr_reader :operations, :labels
+
+ def initialize
+ @ax = :ax
+ @bx = :bx
+ @cx = :cx
+ @dx = :dx
+ @operations = []
+ @labels = []
+ @instruction_names = [:mov, :cmp, :inc, :dec, :jmp, :je, :jne, :jl, :jle, :jg, :jge]
+ end
+
+ def label(label)
+ label.position = @operations.size
+ @labels << label
+ end
+
+ def method_missing(name, *args)
+ if @instruction_names.include? name
+ @operations << [name, args]
+ else
+ if @labels.select { |label| label.name == name }.empty?
+ Label.new name
+ else
+ @labels.select { |label| label.name == name }.first
+ end
+ end
+ end
+ end
+
+ class Register
+ attr_accessor :value, :name
+
+ def initialize(name)
+ @value = 0
+ @name = name
+ end
+ end
+
+ class Label
+ attr_accessor :position, :name
+
+ def initialize(name="", position=-1)
+ @position = position
+ @name = name
+ end
+ end
+
+ class Evaluator
+ attr_reader :next
+ attr_accessor :ax, :bx, :cx, :dx
+
+ def initialize(labels)
+ @next = 0
+ @ax = Register.new :ax
+ @bx = Register.new :bx
+ @cx = Register.new :cx
+ @dx = Register.new :dx
+ @labels = labels
+ @last_cmp = 0
+ end
+
+ def evaluate(operations)
+ while @next < operations.size
+ operation, arg1, arg2 = operations[@next].flatten
+ arg1 = self.public_send arg1 if arg1.is_a? Symbol
+ arg2 = (self.public_send arg2).value if arg2.is_a? Symbol
+ if arg2
+ self.public_send operation, arg1, arg2
+ else
+ self.public_send operation, arg1
+ end
+ end
+ end
+
+ def registers
+ [@ax, @bx, @cx, @dx]
+ end
+
+ def mov(register, source)
+ register.value = source
+ @next += 1
+ end
+
+ def inc(register, value=1)
+ register.value += value
+ @next += 1
+ end
+
+ def dec(register, value=1)
+ register.value -= value
+ @next += 1
+ end
+
+ def cmp(register, source)
+ @last_cmp = register.value <=> source
+ @next += 1
+ end
+
+ def jmp(where)
+ if where.is_a? Label
+ where = @labels.select { |label| label.name == where.name }.first.position
+ end
+ @next = where
+ end
+
+ jumps = {
+ :je => :==,
+ :jne => :!=,
+ :jl => :<,
+ :jle => :<=,
+ :jg => :>,
+ :jge => :>=,
+ }
+
+ jumps.each do |jump, operation|
+ define_method jump.to_s do |where|
+ if @last_cmp.public_send operation, 0
+ return jmp where
+ end
+ @next += 1
+ end
+ end
+ end
+end

Чудесно! Все пак можеш да подобриш едно-две неща.

  • Аз бих freeze-нал jumps.
  • В Asm.asm има едно самотно parser.operations, което трябва да се махне.
  • Имплементацията ти на Asm.asm нарушава Law of Demeter, по-конкретно ей този ред: evaluator.registers.map(&:value). Asm.asm знае, че регистрите се представят чрез някакъв обект, който има метод value, което е повече знание, отколкото ни трябва. По този начин имплементацията на метода е обвързана с 3 класа. Можеш ли да промениш кода си така, че имплементацията на Asm.asm да изглежда горе-долу по следния начин:

    def self.asm(&block)
      Evaluator.evaluate(Parser.parse &block)
    end