Решение на Бонус задача: ArnoldC+- от Виктор Маринов

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

Към профила на Виктор Маринов

Резултати

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

Код

require_relative 'executor'
module PrintMethods
def printer
@printer
end
def printer=(printer)
@printer = printer
end
def print(variable)
@printer.print(get_value(variable))
end
end
module DefineVariableMethods
def declare_var(variable)
@executing_func.declare_var(variable)
@current_var = variable
end
def define_var(variable)
@executing_func.set_var @current_var, variable
end
def end_assignment
@current_var = nil
end
end
module ArithmeticOperations
def plus(number)
new_value = @executing_func.get_var(@current_var) + get_value(number)
@executing_func.set_var @current_var, new_value
end
def minus(number)
new_value = @executing_func.get_var(@current_var) - get_value(number)
@executing_func.set_var @current_var, new_value
end
def multiply(number)
new_value = @executing_func.get_var(@current_var) * get_value(number)
@executing_func.set_var @current_var, new_value
end
def divide(number)
new_value = @executing_func.get_var(@current_var) / get_value(number)
@executing_func.set_var @current_var, new_value
end
def mod(number)
new_value = @executing_func.get_var(@current_var) % get_value(number)
@executing_func.set_var @current_var, new_value
end
end
module BooleanOperations
def or(variable)
current_value = @executing_func.get_var(@current_var)
unless to_bool(current_value)
@executing_func.set_var @current_var, get_value(variable)
end
end
def and(variable)
current_value = @executing_func.get_var(@current_var)
if to_bool(current_value)
@executing_func.set_var @current_var, get_value(variable)
end
end
private
def to_bool(variable)
get_value(variable) == 0 ? false : true
end
end
module CompareOperations
def gt(variable)
current_value = @executing_func.get_var(@current_var)
bool_value = current_value > get_value(variable) ? 1 : 0
@executing_func.set_var @current_var, bool_value
end
def eq(variable)
current_value = @executing_func.get_var(@current_var)
bool_value = current_value == get_value(variable) ? 1 : 0
@executing_func.set_var @current_var, bool_value
end
end
module BranchingMethods
def arnold_if(condition)
end
def arnold_else
end
def end_if
end
end
module DefineFunctionMethods
def declare_func(name)
@currently_defined_func = Function.new(name)
if @current_func
@current_func.add_inner_function @currently_defined_func
else
@global_functions[name] = @currently_defined_func
end
end
def add_argument(name)
@currently_defined_func.add_arg name
end
def make_non_void
@currently_defined_func.is_void false
end
def end_function_def
@currently_defined_func = nil
end
def func_return
end
end
module ArnoldCPM
CONSTANTS = {i_lied: 0, no_problemo: 1}
KEYWORDS = {
get_up: :plus, get_down: :minus, youre_fired: :multiply,
he_had_to_split: :divide, i_let_him_go: :mod,
consider_that_a_divorce: :or, knock_knock: :and,
talk_to_the_hand: :print, get_to_the_chopper: :declare_var,
here_is_my_invitation: :define_var, enough_talk: :end_assignment,
ill_be_back: :func_return,
let_off_some_steam_bennet: :gt,
you_are_not_you_you_are_me: :eq,
because_im_going_to_say_please: :arnold_if,
bull_shit: :arnold_else,
you_have_no_respect_for_logic: :end_if,
listen_to_me_very_carefully: :declare_func,
i_need_your_clothes_your_boots_and_your_motorcycle: :add_argument,
give_these_people_air: :make_non_void,
hasta_la_vista_baby: :end_function_def
}
extend PrintMethods
extend DefineVariableMethods
extend ArithmeticOperations
extend BooleanOperations
extend CompareOperations
extend BranchingMethods
extend DefineFunctionMethods
class << self
def totally_recall(&block)
@executor = Executor.new
@global_functions = {}
instance_eval &block
end
def its_showtime
@main_func = Function.new(:main, @global_functions)
@current_func = @main_func
end
def you_have_been_terminated
execute_main
@main_func = nil
end
private
def execute_main
@executing_func = @main_func
execute_function(@executing_func)
end
def execute_function(function)
function.body.each do |method_name, args|
send KEYWORDS[method_name], *args
end
end
def method_missing(name, *args, &block)
if KEYWORDS.key? name
if @current_func
@current_func.add_to_body name, *args
else
send KEYWORDS[name], *args, &block
end
else
CONSTANTS[name] || @current_func.get_var(name) || name
end
end
def get_value(variable)
if variable.is_a? Numeric
variable
else
CONSTANTS[variable] || @executing_func.get_var(variable)
end
end
end
end
class Function
attr_accessor :name, :args, :is_void, :body, :scope, :inner_functions
def initialize(name, closure = {})
@name = name
@args = []
@is_void = true
@scope = closure.clone
@body = []
@inner_functions = []
end
def ==(other)
id == other.id
end
def add_arg(arg_name)
@args << arg_name
end
def add_to_body(method_name, *args)
@body << [method_name, args]
end
def declare_var(name)
@scope[name] = nil
end
def get_var(name)
@scope[name]
end
def set_var(name, value)
@scope[name] = value
end
def add_inner_function(function)
function.scope = scope.clone
@inner_functions << function
end
def get_inner_function(name)
@inner_functions.select { |f| f.name == name }.first
end
end

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

/tmp/d20170103-15620-3l13fz/solution.rb:1:in `require_relative': cannot load such file -- /tmp/d20170103-15620-3l13fz/executor (LoadError)
	from /tmp/d20170103-15620-3l13fz/solution.rb:1:in `<top (required)>'
	from /data/rails/evans-2016/shared/bundle/ruby/2.3.0/gems/rspec-core-2.99.2/lib/rspec/core/configuration.rb:1036:in `require'
	from /data/rails/evans-2016/shared/bundle/ruby/2.3.0/gems/rspec-core-2.99.2/lib/rspec/core/configuration.rb:1036:in `block in setup_load_path_and_require'
	from /data/rails/evans-2016/shared/bundle/ruby/2.3.0/gems/rspec-core-2.99.2/lib/rspec/core/configuration.rb:1036:in `each'
	from /data/rails/evans-2016/shared/bundle/ruby/2.3.0/gems/rspec-core-2.99.2/lib/rspec/core/configuration.rb:1036:in `setup_load_path_and_require'
	from /data/rails/evans-2016/shared/bundle/ruby/2.3.0/gems/rspec-core-2.99.2/lib/rspec/core/configuration_options.rb:25:in `configure'
	from /data/rails/evans-2016/shared/bundle/ruby/2.3.0/gems/rspec-core-2.99.2/lib/rspec/core/command_line.rb:17:in `run'
	from /data/rails/evans-2016/shared/bundle/ruby/2.3.0/gems/rspec-core-2.99.2/lib/rspec/core/runner.rb:103:in `run'
	from /data/rails/evans-2016/shared/bundle/ruby/2.3.0/gems/rspec-core-2.99.2/lib/rspec/core/runner.rb:17:in `block in autorun'

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

Виктор обнови решението на 29.12.2016 11:36 (преди около 8 години)

+require_relative 'executor'
+
+module PrintMethods
+ def printer
+ @printer
+ end
+
+ def printer=(printer)
+ @printer = printer
+ end
+
+ def print(variable)
+ @printer.print(get_value(variable))
+ end
+end
+
+module DefineVariableMethods
+ def declare_var(variable)
+ @executing_func.declare_var(variable)
+ @current_var = variable
+ end
+
+ def define_var(variable)
+ @executing_func.set_var @current_var, variable
+ end
+
+ def end_assignment
+ @current_var = nil
+ end
+end
+
+module ArithmeticOperations
+ def plus(number)
+ new_value = @executing_func.get_var(@current_var) + get_value(number)
+ @executing_func.set_var @current_var, new_value
+ end
+
+ def minus(number)
+ new_value = @executing_func.get_var(@current_var) - get_value(number)
+ @executing_func.set_var @current_var, new_value
+ end
+
+ def multiply(number)
+ new_value = @executing_func.get_var(@current_var) * get_value(number)
+ @executing_func.set_var @current_var, new_value
+ end
+
+ def divide(number)
+ new_value = @executing_func.get_var(@current_var) / get_value(number)
+ @executing_func.set_var @current_var, new_value
+ end
+
+ def mod(number)
+ new_value = @executing_func.get_var(@current_var) % get_value(number)
+ @executing_func.set_var @current_var, new_value
+ end
+end
+
+module BooleanOperations
+ def or(variable)
+ current_value = @executing_func.get_var(@current_var)
+ unless to_bool(current_value)
+ @executing_func.set_var @current_var, get_value(variable)
+ end
+ end
+
+ def and(variable)
+ current_value = @executing_func.get_var(@current_var)
+ if to_bool(current_value)
+ @executing_func.set_var @current_var, get_value(variable)
+ end
+ end
+
+ private
+
+ def to_bool(variable)
+ get_value(variable) == 0 ? false : true
+ end
+end
+
+module CompareOperations
+ def gt(variable)
+ current_value = @executing_func.get_var(@current_var)
+ bool_value = current_value > get_value(variable) ? 1 : 0
+ @executing_func.set_var @current_var, bool_value
+ end
+
+ def eq(variable)
+ current_value = @executing_func.get_var(@current_var)
+ bool_value = current_value == get_value(variable) ? 1 : 0
+ @executing_func.set_var @current_var, bool_value
+ end
+end
+
+module BranchingMethods
+ def arnold_if(condition)
+
+ end
+
+ def arnold_else
+
+ end
+
+ def end_if
+
+ end
+end
+
+module DefineFunctionMethods
+ def declare_func(name)
+ @currently_defined_func = Function.new(name)
+ if @current_func
+ @current_func.add_inner_function @currently_defined_func
+ else
+ @global_functions[name] = @currently_defined_func
+ end
+ end
+
+ def add_argument(name)
+ @currently_defined_func.add_arg name
+ end
+
+ def make_non_void
+ @currently_defined_func.is_void false
+ end
+
+ def end_function_def
+ @currently_defined_func = nil
+ end
+
+ def func_return
+
+ end
+end
+
+module ArnoldCPM
+ CONSTANTS = {i_lied: 0, no_problemo: 1}
+ KEYWORDS = {
+ get_up: :plus, get_down: :minus, youre_fired: :multiply,
+ he_had_to_split: :divide, i_let_him_go: :mod,
+ consider_that_a_divorce: :or, knock_knock: :and,
+
+ talk_to_the_hand: :print, get_to_the_chopper: :declare_var,
+ here_is_my_invitation: :define_var, enough_talk: :end_assignment,
+ ill_be_back: :func_return,
+
+ let_off_some_steam_bennet: :gt,
+ you_are_not_you_you_are_me: :eq,
+
+ because_im_going_to_say_please: :arnold_if,
+ bull_shit: :arnold_else,
+ you_have_no_respect_for_logic: :end_if,
+
+ listen_to_me_very_carefully: :declare_func,
+ i_need_your_clothes_your_boots_and_your_motorcycle: :add_argument,
+ give_these_people_air: :make_non_void,
+ hasta_la_vista_baby: :end_function_def
+ }
+ extend PrintMethods
+ extend DefineVariableMethods
+ extend ArithmeticOperations
+ extend BooleanOperations
+ extend CompareOperations
+ extend BranchingMethods
+ extend DefineFunctionMethods
+
+ class << self
+ def totally_recall(&block)
+ @executor = Executor.new
+ @global_functions = {}
+ instance_eval &block
+ end
+
+ def its_showtime
+ @main_func = Function.new(:main, @global_functions)
+ @current_func = @main_func
+ end
+
+ def you_have_been_terminated
+ execute_main
+ @main_func = nil
+ end
+
+ private
+
+ def execute_main
+ @executing_func = @main_func
+ execute_function(@executing_func)
+ end
+
+ def execute_function(function)
+ function.body.each do |method_name, args|
+ send KEYWORDS[method_name], *args
+ end
+ end
+
+ def method_missing(name, *args, &block)
+ if KEYWORDS.key? name
+ if @current_func
+ @current_func.add_to_body name, *args
+ else
+ send KEYWORDS[name], *args, &block
+ end
+ else
+ CONSTANTS[name] || @current_func.get_var(name) || name
+ end
+ end
+
+ def get_value(variable)
+ if variable.is_a? Numeric
+ variable
+ else
+ CONSTANTS[variable] || @executing_func.get_var(variable)
+ end
+ end
+ end
+end
+
+class Function
+ attr_accessor :name, :args, :is_void, :body, :scope, :inner_functions
+
+ def initialize(name, closure = {})
+ @name = name
+ @args = []
+ @is_void = true
+ @scope = closure.clone
+ @body = []
+ @inner_functions = []
+ end
+
+ def ==(other)
+ id == other.id
+ end
+
+ def add_arg(arg_name)
+ @args << arg_name
+ end
+
+ def add_to_body(method_name, *args)
+ @body << [method_name, args]
+ end
+
+ def declare_var(name)
+ @scope[name] = nil
+ end
+
+ def get_var(name)
+ @scope[name]
+ end
+
+ def set_var(name, value)
+ @scope[name] = value
+ end
+
+ def add_inner_function(function)
+ function.scope = scope.clone
+ @inner_functions << function
+ end
+
+ def get_inner_function(name)
+ @inner_functions.select { |f| f.name == name }.first
+ end
+end

Не можа ли да влeзеш за 1 минута да изтриеш executor хавата като видя, че няма да я довършиш? :/

Няколко забележки:

  • Няма много полза от това да изваждаш сходно звучащи методи в модул и после да го include-ваш само на едно място. Изглежда все едно разделя логиката, но всъщност единствено кара читателя да търси едно и също нещо на две места (например повтаряш имената на методите в KEYWORDS и самите методи в различните модули). Ако ти е интересно на какъв принцип сме решили да разделим отговорностите в този случай, може да погледнеш нашето решение.
  • ArnoldCPM модулът не трябваше да пази друго състояние освен printer. Какво ще стане ако пусна две ArnoldC+- програми една след друга? Или в две нишки едновременно?

Все пак ми харесва, че си решил да "преведеш" езика с по-смислени имена.


Надявам се задачата да ти е била забавна и полезна.

Забравих за executor "хавата". Разгледах вашето решение подробно, понеже не бях сигурен дали съм тръгнал в правилната посока. Благодаря за обратната връзка. Задачата определено ми беше интересна и полезна.