Виктор обнови решението на 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 "хавата". Разгледах вашето решение подробно, понеже не бях сигурен дали съм тръгнал в правилната посока. Благодаря за обратната връзка. Задачата определено ми беше интересна и полезна.