Решение на Четвърта задача от Емануела Моллова

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

Към профила на Емануела Моллова

Резултати

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

Код

module Asm
module Operations
def mov(destination_register, source)
@registers[destination_register] = find_value(source)
end
def inc(destination_register, value = 1)
@registers[destination_register] += find_value(value)
end
def dec(destination_register, value = 1)
@registers[destination_register] -= find_value(value)
end
def cmp(register, value)
@last_comparison = (@registers[register] <=> find_value(value))
end
def find_value(value)
@registers[value] or value
end
end
module Jumps
def jmp(where)
@pc = (@labels[where] or where)
end
jumps = {
je: :'==',
jne: :'!=',
jl: :'<',
jle: :'<=',
jg: :'>',
jge: :'>=',
}
jumps.each do |jump_name, comparison|
define_method jump_name do |where|
@last_comparison.public_send(comparison, 0) ? (return jmp(where)) : @pc = @pc.succ
end
end
end
class Evaluator
include Operations
include Jumps
class Storage
attr_reader :labels, :methods_to_call
def initialize(&block)
@methods_to_call = []
@labels = {}
instance_eval(&block)
end
def method_missing(method_name, *args)
if (Operations.instance_methods + Jumps.instance_methods).include? method_name
@methods_to_call << [method_name, args]
else
method_name.to_sym
end
end
def label(label_name)
@labels[label_name] = @methods_to_call.size
end
end
def initialize(&block)
@registers = {ax: 0, bx: 0, cx: 0, dx: 0}
@last_comparison = 0
@pc = 0
storage = Storage.new(&block)
@methods_to_call = storage.methods_to_call
@labels = storage.labels
end
def evaluate
while @pc < @methods_to_call.size
method_name = @methods_to_call[@pc].first
args = @methods_to_call[@pc].last
public_send(method_name, *args)
@pc = @pc.succ if !Jumps.instance_methods.include? method_name
end
@registers.values
end
end
def self.asm(&block)
Evaluator.new(&block).evaluate
end
end

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

........

Finished in 0.00889 seconds
8 examples, 0 failures

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

Емануела обнови решението на 13.01.2014 17:36 (преди почти 11 години)

+module Asm
+ module Operations
+ def mov(destination_register, source)
+ @registers[destination_register] = find_value(source)
+ end
+
+ def inc(destination_register, value = 1)
+ @registers[destination_register] += find_value(value)
+ end
+
+ def dec(destination_register, value = 1)
+ @registers[destination_register] -= find_value(value)
+ end
+
+ def cmp(register, value)
+ @last_comparison = (@registers[register] <=> find_value(value))
+ end
+
+ def find_value(value)
+ @registers[value] or value
+ end
+ end
+
+ module Jumps
+ def jmp(where)
+ where = (@labels[where] or where)
+ end
+
+ jumps = {
+ je: :'==',
+ jne: :'!=',
+ jl: :'<',
+ jle: :'<=',
+ jg: :'>',
+ jge: :'>=',
+ }
+
+ jumps.each do |jump_name, comparison|
+ define_method jump_name do |where|
+ @last_comparison.send(comparison, 0) ? (return jmp(where)) : @current_method + 1
+ end
+ end
+ end
+
+ class Evaluator
+ include Operations
+ include Jumps
+
+ class Storage
+ attr_reader :labels, :methods_to_call
+
+ def initialize(&block)
+ @methods_to_call = []
+ @labels = {}
+ instance_eval(&block)
+ end
+
+ def method_missing(method_name, *args)
+ if (Operations.instance_methods + Jumps.instance_methods).include? method_name
+ @methods_to_call << [method_name, args]
+ else
+ method_name.to_s
+ end
+ end
+
+ def label(label_name)
+ @labels[label_name] = @methods_to_call.size
+ end
+ end
+
+ def initialize(&block)
+ @registers = {"ax" => 0, "bx" => 0, "cx" => 0, "dx" => 0}
+ @last_comparison = 0
+ storage = Storage.new(&block)
+ @methods_to_call = storage.methods_to_call
+ @labels = storage.labels
+ end
+
+ def evaluate(start = 0)
+ @methods_to_call.each_with_index do |method, index|
+ @current_method = index
+ if index >= start
+ method_name, args = method.first, method.last
+ if Jumps.instance_methods.include? method_name
+ return evaluate(send(method_name, *args))
+ end
+ send(method_name, *args)
+ end
+ end
+ @registers.values
+ end
+ end
+
+ def self.asm(&block)
+ Evaluator.new(&block).evaluate
+ end
+end

Доста добро, но има един проблем и една дреболийка:

Дреболийката е, че ключовете в @registers могат да са символи.

Проблемът е, че Evaluator#evaluate е рекурсивен метод и достатъчно продължителна програма ще доведе до препълване на стека. Чак се чудя дали да не добавя следното към тестовете:

Asm.asm do
  mov ax, 100000
  dec ax
  cmp ax, 0
  jne 1
end

:-)

Помисли как всъщност процесорите оценяват кода и какво представляват програмите на ниско ниво. Рекурсията е абстракция от по-високо ниво. На по-ниско ниво има само jump-ове :) Ако си посещавала курс по компютърни архитектури, вероятно си чувала за неща като стек, регистри, PC (program counter, hint, hint), указател към текуща инструкция, списък с инструкции (програма). Процесорите са итеративни. Можеш да емулираш това с код.

Емануела обнови решението на 15.01.2014 13:55 (преди почти 11 години)

module Asm
module Operations
def mov(destination_register, source)
@registers[destination_register] = find_value(source)
end
def inc(destination_register, value = 1)
@registers[destination_register] += find_value(value)
end
def dec(destination_register, value = 1)
@registers[destination_register] -= find_value(value)
end
def cmp(register, value)
@last_comparison = (@registers[register] <=> find_value(value))
end
def find_value(value)
@registers[value] or value
end
end
module Jumps
def jmp(where)
- where = (@labels[where] or where)
+ @pc = (@labels[where] or where)
end
jumps = {
je: :'==',
jne: :'!=',
jl: :'<',
jle: :'<=',
jg: :'>',
jge: :'>=',
}
jumps.each do |jump_name, comparison|
define_method jump_name do |where|
- @last_comparison.send(comparison, 0) ? (return jmp(where)) : @current_method + 1
+ @last_comparison.public_send(comparison, 0) ? (return jmp(where)) : @pc = @pc.succ
end
end
end
class Evaluator
include Operations
include Jumps
class Storage
attr_reader :labels, :methods_to_call
def initialize(&block)
@methods_to_call = []
@labels = {}
instance_eval(&block)
end
def method_missing(method_name, *args)
if (Operations.instance_methods + Jumps.instance_methods).include? method_name
@methods_to_call << [method_name, args]
else
- method_name.to_s
+ method_name.to_sym
end
end
def label(label_name)
@labels[label_name] = @methods_to_call.size
end
end
def initialize(&block)
- @registers = {"ax" => 0, "bx" => 0, "cx" => 0, "dx" => 0}
+ @registers = {ax: 0, bx: 0, cx: 0, dx: 0}
@last_comparison = 0
+ @pc = 0
storage = Storage.new(&block)
@methods_to_call = storage.methods_to_call
@labels = storage.labels
end
- def evaluate(start = 0)
- @methods_to_call.each_with_index do |method, index|
- @current_method = index
- if index >= start
- method_name, args = method.first, method.last
- if Jumps.instance_methods.include? method_name
- return evaluate(send(method_name, *args))
- end
- send(method_name, *args)
- end
+ def evaluate
+ while @pc < @methods_to_call.size
+ method_name = @methods_to_call[@pc].first
+ args = @methods_to_call[@pc].last
+ public_send(method_name, *args)
+ @pc = @pc.succ if !Jumps.instance_methods.include? method_name
end
@registers.values
end
end
def self.asm(&block)
Evaluator.new(&block).evaluate
end
-end
+end