Решение на Четвърта задача от Цани Проданов

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

Към профила на Цани Проданов

Резултати

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

Код

module Asm
# This is my CPU
# there are many like it, but this one is mine!
class X86CPU
@registers = {
ax: 0,
bx: 0,
cx: 0,
dx: 0,
cmpx: 0,
ip: 0,
}
@instruction_set = { # A simple hash holding lambdas for our instructions
mov: ->(reg,value) {@registers[reg] = parse(value)},
inc: ->(reg,value=1) {@registers[reg] = @registers[reg] + parse(value)},
dec: ->(reg,value=1) {@registers[reg] = @registers[reg] - parse(value)},
cmp: ->(reg,value) {@registers[:cmpx] = @registers[reg] <=> parse(value)},
jmp: ->(pos) {@registers[:ip] = pos - 1},# pos-1 is a shitty fix, but ez
je: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] == 0},
jne: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] != 0},
jl: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] < 0},
jle: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] <= 0},
jg: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] > 0},
jge: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] >= 0}
}
def self.parse (val) # since we don't know if we are getting a symbol
val.class == Symbol ? @registers[val] : val # or a Fixnum, parse it beforehand
end
def self.ip # We need access to our instruction pointer for our executable
@registers[:ip]
end
def self.instruction_set # We need access to our instruction set for our builder
@instruction_set
end
def self.registers
[@registers[:ax], @registers[:bx],
@registers[:cx], @registers[:dx]]
end
def self.reset_registers
@registers.each_key {|key| @registers[key] = 0}
end
def self.execute_instruction (instruction,*args) # This is where the magic happens
@instruction_set[instruction][*args]
@instruction_set[:inc][:ip]
end
end
class X86Executable
def initialize(instr) # our executable is just an array of instructions
@instructions = instr
end
def run # here we execute the instruction at :ip until we reach the end
X86CPU.reset_registers
while X86CPU.ip < @instructions.length do
X86CPU.execute_instruction(*@instructions[X86CPU.ip])
end
X86CPU.registers
end
end
# This class will take the shitty assembler DSL
# and convert it to arrays of beautiful symbol butterflies
# Also it will parse the jumps to labels
# into jumps to the corresponding instruction
class X86Builder
def initialize # no instructions, no labels, no problem
@instructions = []
@labels = {}
end
def method_missing(m,*args) # This is (almost) all we need to parse the DSL
if X86CPU.instruction_set.has_key? m # if this is an instruction, add it
@instructions << [m,*args]
else
m # otherwise it's an argument, return it as a symbol
end
end
def label (lbl) # Only labels get the special treatment
@labels[lbl] = @instructions.length
end
def parse_labels # we replace the labels with their respective instruction number
@instructions.each do |instruction|
instruction.collect! do |val|
if @labels.has_key? val then
@labels[val]
else
val
end
end
end
end
def build (&block)
instance_eval &block
parse_labels # compile the labels, no need to keep them
X86Executable.new @instructions
end
end
def self.asm(&block)
exe = X86Builder.new.build &block
exe.run
end
end

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

........

Finished in 0.00749 seconds
8 examples, 0 failures

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

Цани обнови решението на 13.01.2014 18:01 (преди почти 11 години)

+module Asm
+ class X86CPU
+ class Register
+ include Comparable
+ attr_reader :val
+ def initialize
+ @val = 0
+ end
+
+ def set (value)
+ @val = value
+ end
+
+ #Let's make our register work with operations on Fixnum
+ def + (value)
+ @val + value
+ end
+
+ def - (value)
+ @val - value
+ end
+
+ def <=> (value)
+ @val <=> value
+ end
+
+ def coerce (something) #Coerce into Fixnum
+ [something,@val]
+ end
+ end
+ @@registers =
+ {
+ :ax => Register.new,
+ :bx => Register.new,
+ :cx => Register.new,
+ :dx => Register.new,
+ :cmpx => Register.new,
+ :ip => Register.new, #This should be taken from each executable
+ #and we should pass it to the CPU so it can
+ #manipulate it. But let's do it here for ezness
+ }
+ @@instruction_set = #A simple hash holding lambdas for our instructions
+ {
+ :mov => ->(reg,value) {@@registers[reg].set(parse(value))},
+ :inc => ->(reg,value=1) {@@registers[reg].set(@@registers[reg] + parse(value))},
+ :dec => ->(reg,value=1) {@@registers[reg].set(@@registers[reg] - parse(value))},
+ :cmp => ->(reg,value) {@@registers[:cmpx].set(@@registers[reg]<=>parse(value))},
+ :jmp => ->(pos) {@@registers[:ip].set(pos-1)},#pos-1 is a shitty fix, but ez
+ :je => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] == 0},
+ :jne => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] != 0},
+ :jl => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] < 0},
+ :jle => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] <= 0},
+ :jg => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] > 0},
+ :jge => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] >= 0}
+ }
+
+ def self.parse (val) #since we don't know if we are getting a symbol
+ case val #or a Fixnum, parse it beforehand
+ when Symbol
+ @@registers[val] #Here we return the register itself, because we can coerce it
+ else
+ val
+ end
+ end
+
+ def self.ip #We need access to our instruction pointer for our executable
+ @@registers[:ip].val
+ end
+
+ def self.instruction_set #We need access to our instruction set for our builder
+ @@instruction_set
+ end
+
+ def self.registers
+ [@@registers[:ax].val,@@registers[:bx].val,
+ @@registers[:cx].val,@@registers[:dx].val]
+ end
+
+ def self.reset_registers
+ @@registers.each_value {|register| register.set(0)}
+ end
+
+ def self.execute_instruction (instruction,*args) #This is where the magic happens
+ @@instruction_set[instruction][*args]
+ @@instruction_set[:inc][:ip]
+ end
+ end
+
+ class X86Executable
+ def initialize(instr) #our executable is just an array of instructions
+ @instructions = instr
+ end
+
+ def run #here we just execute the instruction at :ip until we reach the end
+ p "Running executable"
+ X86CPU.reset_registers
+ while X86CPU.ip < @instructions.length do
+ p "Executing (IP:#{X86CPU.ip}) #{@instructions[X86CPU.ip]}"
+ X86CPU.execute_instruction(*@instructions[X86CPU.ip])
+ end
+ X86CPU.registers
+ end
+ end
+
+ class X86Builder
+ attr_accessor :instructions, :labels
+ def initialize
+ @instructions = []
+ @labels = {}
+ end
+
+ def method_missing(m,*args)
+ if X86CPU.instruction_set.has_key? m #if this is an instruction, add it
+ @instructions << [m,*args]
+ else
+ m #otherwise it's an argument, return it as a symbol
+ end
+ end
+
+ def label (lbl)
+ labels[lbl] = @instructions.length
+ end
+
+ def parse_labels #we replace the labels with their respective instructions
+ @instructions.each do |instruction|
+ instruction.collect! do |val|
+ if @labels.has_key? val then
+ @labels[val]
+ else
+ val
+ end
+ end
+ end
+ end
+
+ def build (&block)
+ instance_eval &block
+ parse_labels #compile the labels, no need to keep them
+ X86Executable.new @instructions
+ end
+ end
+
+ def self.asm(&block)
+ exe = X86Builder.new.build &block
+ exe.run
+ end
+end

Цани обнови решението на 13.01.2014 18:25 (преди почти 11 години)

module Asm
+ #This is my CPU
+ #there are many like it, but this one is mine!
class X86CPU
- class Register
- include Comparable
- attr_reader :val
- def initialize
- @val = 0
- end
-
- def set (value)
- @val = value
- end
-
- #Let's make our register work with operations on Fixnum
- def + (value)
- @val + value
- end
-
- def - (value)
- @val - value
- end
-
- def <=> (value)
- @val <=> value
- end
-
- def coerce (something) #Coerce into Fixnum
- [something,@val]
- end
- end
@@registers =
{
- :ax => Register.new,
- :bx => Register.new,
- :cx => Register.new,
- :dx => Register.new,
- :cmpx => Register.new,
- :ip => Register.new, #This should be taken from each executable
- #and we should pass it to the CPU so it can
- #manipulate it. But let's do it here for ezness
+ :ax => 0,
+ :bx => 0,
+ :cx => 0,
+ :dx => 0,
+ :cmpx => 0,
+ :ip => 0,
}
@@instruction_set = #A simple hash holding lambdas for our instructions
{
- :mov => ->(reg,value) {@@registers[reg].set(parse(value))},
- :inc => ->(reg,value=1) {@@registers[reg].set(@@registers[reg] + parse(value))},
- :dec => ->(reg,value=1) {@@registers[reg].set(@@registers[reg] - parse(value))},
- :cmp => ->(reg,value) {@@registers[:cmpx].set(@@registers[reg]<=>parse(value))},
- :jmp => ->(pos) {@@registers[:ip].set(pos-1)},#pos-1 is a shitty fix, but ez
- :je => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] == 0},
- :jne => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] != 0},
- :jl => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] < 0},
- :jle => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] <= 0},
- :jg => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] > 0},
- :jge => ->(pos) {@@registers[:ip].set(pos-1) if @@registers[:cmpx] >= 0}
+ :mov => ->(reg,value) {@@registers[reg] = parse(value)},
+ :inc => ->(reg,value=1) {@@registers[reg] = @@registers[reg] + parse(value)},
+ :dec => ->(reg,value=1) {@@registers[reg] = @@registers[reg] - parse(value)},
+ :cmp => ->(reg,value) {@@registers[:cmpx] = @@registers[reg]<=>parse(value)},
+ :jmp => ->(pos) {@@registers[:ip] = pos-1},#pos-1 is a shitty fix, but ez
+ :je => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] == 0},
+ :jne => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] != 0},
+ :jl => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] < 0},
+ :jle => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] <= 0},
+ :jg => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] > 0},
+ :jge => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] >= 0}
}
def self.parse (val) #since we don't know if we are getting a symbol
case val #or a Fixnum, parse it beforehand
when Symbol
- @@registers[val] #Here we return the register itself, because we can coerce it
+ @@registers[val]
else
val
end
end
def self.ip #We need access to our instruction pointer for our executable
- @@registers[:ip].val
+ @@registers[:ip]
end
def self.instruction_set #We need access to our instruction set for our builder
@@instruction_set
end
def self.registers
- [@@registers[:ax].val,@@registers[:bx].val,
- @@registers[:cx].val,@@registers[:dx].val]
+ [@@registers[:ax],@@registers[:bx],
+ @@registers[:cx],@@registers[:dx]]
end
def self.reset_registers
- @@registers.each_value {|register| register.set(0)}
+ @@registers.each_key {|key| @@registers[key] = 0}
end
def self.execute_instruction (instruction,*args) #This is where the magic happens
@@instruction_set[instruction][*args]
@@instruction_set[:inc][:ip]
end
end
class X86Executable
def initialize(instr) #our executable is just an array of instructions
@instructions = instr
end
- def run #here we just execute the instruction at :ip until we reach the end
+ def run #here we execute the instruction at :ip until we reach the end
p "Running executable"
X86CPU.reset_registers
while X86CPU.ip < @instructions.length do
p "Executing (IP:#{X86CPU.ip}) #{@instructions[X86CPU.ip]}"
X86CPU.execute_instruction(*@instructions[X86CPU.ip])
end
X86CPU.registers
end
end
+ #This class will take the shitty assembler DSL
+ #and convert it to arrays of beautiful symbol butterflies
+ #Also it will parse the jumps to labels
+ #into jumps to the corresponding instruction
class X86Builder
- attr_accessor :instructions, :labels
- def initialize
+ def initialize #no instructions, no labels, no problem
@instructions = []
@labels = {}
end
- def method_missing(m,*args)
+ def method_missing(m,*args) #This is (almost) all we need to parse the DSL
if X86CPU.instruction_set.has_key? m #if this is an instruction, add it
@instructions << [m,*args]
else
m #otherwise it's an argument, return it as a symbol
end
end
- def label (lbl)
- labels[lbl] = @instructions.length
+ def label (lbl) #Only labels get the special treatment
+ @labels[lbl] = @instructions.length
end
def parse_labels #we replace the labels with their respective instructions
@instructions.each do |instruction|
instruction.collect! do |val|
if @labels.has_key? val then
@labels[val]
else
val
end
end
end
end
def build (&block)
instance_eval &block
parse_labels #compile the labels, no need to keep them
X86Executable.new @instructions
end
end
def self.asm(&block)
exe = X86Builder.new.build &block
exe.run
end
end

"...shitty assembler DSL...", а? :-)

Дотук добре, ето какво може да се подобри:

  • Махни принтенето. Изглежда приятно, но ще пускаме автоматизирани тестове и твоите p "Running executable" ще се смесят с изхода на rspec. Ще изглежда грозно и най-вероятно ще ти вземем точка, понеже сме казвали за това на лекции. Не си заслужава.
  • Използването на class променливи не се смята за добра практика. Заради особеното поведение на class променливите при наследяване, се предпочитат class instance променливи, т.е. вместо @@registers е по-добре да използваш X86CPU.registers.
  • case-а в X86CPU.parse може да е едноредов тернарен оператор.
  • if-а в X86Builder#parse_labels може да се замести с Hash#fetch.
  • Използвай новия синтаксис за хешове в @@instruction_set и @@registers, например ax: 0.
  • Whitespace-а може да се пипне малко, например виж къде няма интервали в @@instruction_set. Също така, добре е при писане на коментари да се оставя интервал след #, за по-лесно четене.
  • Отварящите къдрави скоби на хешовете следва да се преместят на горния ред.

Цани обнови решението на 15.01.2014 10:59 (преди почти 11 години)

module Asm
- #This is my CPU
- #there are many like it, but this one is mine!
+ # This is my CPU
+ # there are many like it, but this one is mine!
class X86CPU
- @@registers =
- {
- :ax => 0,
- :bx => 0,
- :cx => 0,
- :dx => 0,
- :cmpx => 0,
- :ip => 0,
+ @registers = {
+ ax: 0,
+ bx: 0,
+ cx: 0,
+ dx: 0,
+ cmpx: 0,
+ ip: 0,
}
- @@instruction_set = #A simple hash holding lambdas for our instructions
- {
- :mov => ->(reg,value) {@@registers[reg] = parse(value)},
- :inc => ->(reg,value=1) {@@registers[reg] = @@registers[reg] + parse(value)},
- :dec => ->(reg,value=1) {@@registers[reg] = @@registers[reg] - parse(value)},
- :cmp => ->(reg,value) {@@registers[:cmpx] = @@registers[reg]<=>parse(value)},
- :jmp => ->(pos) {@@registers[:ip] = pos-1},#pos-1 is a shitty fix, but ez
- :je => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] == 0},
- :jne => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] != 0},
- :jl => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] < 0},
- :jle => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] <= 0},
- :jg => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] > 0},
- :jge => ->(pos) {@@registers[:ip] = pos-1 if @@registers[:cmpx] >= 0}
+ @instruction_set = { # A simple hash holding lambdas for our instructions
+ mov: ->(reg,value) {@registers[reg] = parse(value)},
+ inc: ->(reg,value=1) {@registers[reg] = @registers[reg] + parse(value)},
+ dec: ->(reg,value=1) {@registers[reg] = @registers[reg] - parse(value)},
+ cmp: ->(reg,value) {@registers[:cmpx] = @registers[reg] <=> parse(value)},
+ jmp: ->(pos) {@registers[:ip] = pos - 1},# pos-1 is a shitty fix, but ez
+ je: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] == 0},
+ jne: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] != 0},
+ jl: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] < 0},
+ jle: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] <= 0},
+ jg: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] > 0},
+ jge: ->(pos) {@registers[:ip] = pos - 1 if @registers[:cmpx] >= 0}
}
- def self.parse (val) #since we don't know if we are getting a symbol
- case val #or a Fixnum, parse it beforehand
- when Symbol
- @@registers[val]
- else
- val
- end
+ def self.parse (val) # since we don't know if we are getting a symbol
+ val.class == Symbol ? @registers[val] : val # or a Fixnum, parse it beforehand
end
- def self.ip #We need access to our instruction pointer for our executable
- @@registers[:ip]
+ def self.ip # We need access to our instruction pointer for our executable
+ @registers[:ip]
end
- def self.instruction_set #We need access to our instruction set for our builder
- @@instruction_set
+ def self.instruction_set # We need access to our instruction set for our builder
+ @instruction_set
end
def self.registers
- [@@registers[:ax],@@registers[:bx],
- @@registers[:cx],@@registers[:dx]]
+ [@registers[:ax], @registers[:bx],
+ @registers[:cx], @registers[:dx]]
end
def self.reset_registers
- @@registers.each_key {|key| @@registers[key] = 0}
+ @registers.each_key {|key| @registers[key] = 0}
end
- def self.execute_instruction (instruction,*args) #This is where the magic happens
- @@instruction_set[instruction][*args]
- @@instruction_set[:inc][:ip]
+ def self.execute_instruction (instruction,*args) # This is where the magic happens
+ @instruction_set[instruction][*args]
+ @instruction_set[:inc][:ip]
end
end
class X86Executable
- def initialize(instr) #our executable is just an array of instructions
+ def initialize(instr) # our executable is just an array of instructions
@instructions = instr
end
- def run #here we execute the instruction at :ip until we reach the end
- p "Running executable"
+ def run # here we execute the instruction at :ip until we reach the end
X86CPU.reset_registers
while X86CPU.ip < @instructions.length do
- p "Executing (IP:#{X86CPU.ip}) #{@instructions[X86CPU.ip]}"
X86CPU.execute_instruction(*@instructions[X86CPU.ip])
end
X86CPU.registers
end
end
- #This class will take the shitty assembler DSL
- #and convert it to arrays of beautiful symbol butterflies
- #Also it will parse the jumps to labels
- #into jumps to the corresponding instruction
+ # This class will take the shitty assembler DSL
+ # and convert it to arrays of beautiful symbol butterflies
+ # Also it will parse the jumps to labels
+ # into jumps to the corresponding instruction
class X86Builder
- def initialize #no instructions, no labels, no problem
+ def initialize # no instructions, no labels, no problem
@instructions = []
@labels = {}
end
- def method_missing(m,*args) #This is (almost) all we need to parse the DSL
- if X86CPU.instruction_set.has_key? m #if this is an instruction, add it
+ def method_missing(m,*args) # This is (almost) all we need to parse the DSL
+ if X86CPU.instruction_set.has_key? m # if this is an instruction, add it
@instructions << [m,*args]
else
- m #otherwise it's an argument, return it as a symbol
+ m # otherwise it's an argument, return it as a symbol
end
end
- def label (lbl) #Only labels get the special treatment
+ def label (lbl) # Only labels get the special treatment
@labels[lbl] = @instructions.length
end
- def parse_labels #we replace the labels with their respective instructions
+ def parse_labels # we replace the labels with their respective instruction number
@instructions.each do |instruction|
instruction.collect! do |val|
if @labels.has_key? val then
@labels[val]
else
val
end
end
end
end
def build (&block)
instance_eval &block
- parse_labels #compile the labels, no need to keep them
+ parse_labels # compile the labels, no need to keep them
X86Executable.new @instructions
end
end
def self.asm(&block)
exe = X86Builder.new.build &block
exe.run
end
end