Ралица обнови решението на 29.12.2016 17:12 (преди почти 9 години)
+require "method_source"
+
+module ArnoldCPM
+  Nothing = Module.new
+  extend self
+
+  def printer=(object)
+    @object = object
+  end
+
+  def printer
+    @object
+  end
+
+  def make_call(call)
+    if call.match /,/
+      call = call.split(",", 2)
+      call.join
+    else
+      call
+    end
+  end
+
+  def change_words_in_logical_expressions(line)
+    exeptions = [">", "==", "if", "else", "(", ")", "=", "then", "end", "talk_to_the_hand"]
+    words = line.scan(/(\S+)/).map { |x| x[0] }
+
+    index = words.index("=")
+    index = 0 if index == nil
+    words = words.drop(index).reject { |x| exeptions.include? x }
+    words.each do |word|
+      line.gsub! /\b#{word}\b/, "( #{word} && #{word} != 0 )"
+    end
+  end
+
+  def handle_logical_constants(code)
+    code = code.split(/\n/)
+    code.each do |line|
+      if line.scan(/(\n| )(or |and |if |else )/) != []
+        change_words_in_logical_expressions(line)
+      end
+    end
+    code = code.join("\n")
+    handle_print_bool(code)
+  end
+
+  def logical_expressions_changes(code)
+    code.gsub! 'i_lied', '0'
+    code.gsub! 'no_problemo', '1'
+    code.gsub! /\n[[:blank:]]*consider_that_a_divorce/, ' or'
+    code.gsub! /\n[[:blank:]]*knock_knock/, ' and'
+    code.gsub! /\n[[:blank:]]*let_off_some_steam_bennet/, ' >'
+    code.gsub! /\n[[:blank:]]*you_are_not_you_you_are_me/, ' =='
+    code.gsub! 'because_im_going_to_say_please', 'if'
+    code.gsub! 'bull_shit', 'else'
+    code.gsub! 'you_have_no_respect_for_logic', 'end'
+    handle_logical_constants(code)
+  end
+
+  def handle_print_bool(code)
+    pr = code.scan /(\n[[:blank:]]*talk_to_the_hand\s*(\S*))/
+    pr.each do |statement|
+      code.gsub! statement[0].to_s, "\n@object.print ( if !!#{statement[1]} == #{statement[1]}"\
+      " then ( if #{statement[1]} == true then 1 else 0 end ) else #{statement[1]} end )"
+    end
+    code
+  end
+
+  def basic_changes(code)
+    code.gsub! /\n[[:blank:]]*#(.*)/, ""
+    code.gsub! /\n[[:blank:]]*here_is_my_invitation/, ' ='
+    code.gsub! 'get_to_the_chopper', '( '
+    code.gsub! /\n[[:blank:]]*enough_talk/, ' )'
+    code.gsub! /\n[[:blank:]]*youre_fired/, ' *'
+    code.gsub! /\n[[:blank:]]*get_up/, ' +'
+    code.gsub! /\n[[:blank:]]*get_down/, ' -'
+    code.gsub! /\n[[:blank:]]*he_had_to_split/, ' /'
+    code.gsub! /\n[[:blank:]]*i_let_him_go/, ' %'
+    code
+  end
+
+  def call_functions(code)
+    calls = code.scan(/(do_it_now(.*))/)
+    calls.each do |call|
+      code.gsub! call[0], make_call(call[1])
+    end
+    return_calls = code.scan /(get_your_ass_to_mars[[:blank:]]*(\w*)[[:blank:]]*\n(.*))/
+    return_calls.each do |call|
+      code.gsub! call[0], "if (#{call[2].strip}) != ArnoldCPM::Nothing then #{call[1]} = "\
+    "(#{call[2].strip}) else raise RuntimeError, 'Assigning to a void function' end"
+    end
+    code
+  end
+
+  def handle_definition(func)
+    args = func.scan(/i_need_your_clothes_your_boots_and_your_motorcycle(.*)/)
+    args = args.map { |x| x[0].strip }
+    args = args.join(", ")
+    name = func.scan(/listen_to_me_very_carefully(.*)/)
+    func.gsub! /listen_to_me_very_carefully(.*)/, "def #{name[0][0].strip}(#{args})"
+    func.gsub! /i_need_your_clothes_your_boots_and_your_motorcycle(.*)\n/, ""
+    func.gsub! "hasta_la_vista_baby", "end"
+  end
+
+  def handle_nonvoid_function(func)
+    func.gsub! /ill_be_back([[:blank:]]*)\n/, "return 0\n"
+    returns = func.scan(/(ill_be_back(.*))/)
+    returns.each do |return_statement|
+      func.gsub! return_statement[0], "return #{return_statement[1]}"
+    end
+    func = func.split(/\n/)
+    func.delete_at(1)
+    func
+  end
+
+  def handle_return_statements(func)
+    if func.match /def(.*)\n[[:blank:]]*give_these_people_air[[:blank:]]*\n/
+      func = handle_nonvoid_function(func)
+    else
+      func.gsub! /ill_be_back(.*)\n/, "return Nothing\n"
+      func = func.split(/\n/)
+    end
+    func.insert(-2, "return Nothing")
+    func.join("\n")
+  end
+
+  def define_functions(functions)
+    list = functions.scan(/(listen_to_me_very_carefully.*?hasta_la_vista_baby)/m)
+    list = list.map { |x| x[0] }
+    list.each do |func|
+      handle_definition(func)
+      func = handle_return_statements(func)
+      instance_eval(func)
+    end
+  end
+
+  def totally_recall(&block)
+    code = block.source
+    code = basic_changes(code)
+    code = logical_expressions_changes(code)
+    code = call_functions(code)
+    main = code.split(/[[:blank:]]*its_showtime[[:blank:]]*\n/, 2)[1]
+    main = main.split(/[[:blank:]]*you_have_been_terminated[[:blank:]]*\n/, 2)[0]
+    functions = code.split(/[[:blank:]]*its_showtime[[:blank:]]*\n/, 2)[0]
+    define_functions(functions)
+    instance_eval(main)
+  end
+end
Лог от изпълнението при ръчна проверка:
......FFF..F.....F.FFFFFF..F.
Failures:
  1) ArnoldCPM can take functions as arguments
     Failure/Error:
           ArnoldCPM.totally_recall do
             listen_to_me_very_carefully _forty_two_printer
             give_these_people_air
               talk_to_the_hand 42
             hasta_la_vista_baby
             listen_to_me_very_carefully _ingredients_printer
             give_these_people_air
               talk_to_the_hand 6
               talk_to_the_hand 9
       (Double "printer").print(6)
           expected: 0 times with arguments: (6)
           received: 1 time with arguments: (6)
     # ./solution.rb:130:in `instance_eval'
     # (eval):2:in `_ingredients_printer'
     # (eval):2:in `totally_recall'
     # ./solution.rb:130:in `instance_eval'
     # ./solution.rb:130:in `totally_recall'
     # ./spec.rb:556:in `block (2 levels) in <top (required)>'
  2) ArnoldCPM can return functions
     Failure/Error:
           ArnoldCPM.totally_recall do
             listen_to_me_very_carefully _function_generator
             give_these_people_air
               listen_to_me_very_carefully _generated_function
               give_these_people_air
                 talk_to_the_hand 42
               hasta_la_vista_baby
               ill_be_back _generated_function
             hasta_la_vista_baby
     SyntaxError:
       (eval):6: syntax error, unexpected end-of-input, expecting keyword_end
               end
                  ^
     # ./solution.rb:118:in `instance_eval'
     # ./solution.rb:118:in `block in define_functions'
     # ./solution.rb:115:in `each'
     # ./solution.rb:115:in `define_functions'
     # ./solution.rb:129:in `totally_recall'
     # ./spec.rb:593:in `block (2 levels) in <top (required)>'
  3) ArnoldCPM defines new inner functions for each function invocation
     Failure/Error:
           ArnoldCPM.totally_recall do
             listen_to_me_very_carefully _define_function
             give_these_people_air
               listen_to_me_very_carefully _inner_function
               hasta_la_vista_baby
               ill_be_back _inner_function
             hasta_la_vista_baby
             its_showtime
     SyntaxError:
       (eval):4: syntax error, unexpected end-of-input, expecting keyword_end
               end
                  ^
     # ./solution.rb:118:in `instance_eval'
     # ./solution.rb:118:in `block in define_functions'
     # ./solution.rb:115:in `each'
     # ./solution.rb:115:in `define_functions'
     # ./solution.rb:129:in `totally_recall'
     # ./spec.rb:625:in `block (2 levels) in <top (required)>'
  4) ArnoldCPM can use closures in very convoluted ways
     Failure/Error:
           ArnoldCPM.totally_recall do
             listen_to_me_very_carefully _null
             hasta_la_vista_baby
             listen_to_me_very_carefully _cons
             i_need_your_clothes_your_boots_and_your_motorcycle _head
             i_need_your_clothes_your_boots_and_your_motorcycle _tail
             give_these_people_air
               listen_to_me_very_carefully _anon
               i_need_your_clothes_your_boots_and_your_motorcycle _func
     SyntaxError:
       (eval):8: syntax error, unexpected end-of-input, expecting keyword_end
               end
                  ^
     # ./solution.rb:118:in `instance_eval'
     # ./solution.rb:118:in `block in define_functions'
     # ./solution.rb:115:in `each'
     # ./solution.rb:115:in `define_functions'
     # ./solution.rb:129:in `totally_recall'
     # ./spec.rb:765:in `block (2 levels) in <top (required)>'
  5) ArnoldCPM has algebra that can chain multiple operations
     Failure/Error:
       ArnoldCPM.totally_recall do
         its_showtime
           get_to_the_chopper _result
           here_is_my_invitation 7
           i_let_him_go 4
           youre_fired 5
           get_up 2
           youre_fired 22
           he_had_to_split 11
           get_down 2
       #<Double "printer"> received :print with unexpected arguments
         expected: (42)
              got: (27)
     # ./solution.rb:130:in `instance_eval'
     # (eval):3:in `totally_recall'
     # ./solution.rb:130:in `instance_eval'
     # ./solution.rb:130:in `totally_recall'
     # ./spec.rb:136:in `block (3 levels) in <top (required)>'
  6) ArnoldCPM has boolean arithmetic that given *or* returns truthy values if one of the operands is truthy
     Failure/Error:
             ArnoldCPM.totally_recall do
               its_showtime
                 get_to_the_chopper _result
                 here_is_my_invitation no_problemo
                 consider_that_a_divorce i_lied
                 enough_talk
                 talk_to_the_hand _result
                 get_to_the_chopper _result
     SyntaxError:
       (eval):1: syntax error, unexpected '(', expecting ')'
       ...( 1 && 1 != ( 0 && 0 != 0 ) ) ( or && or != ( 0 && 0 != 0 ) ...
       ...                               ^
       (eval):1: syntax error, unexpected '(', expecting end-of-input
       ...or && or != ( 0 && 0 != 0 ) ) ( 0 && 0 != 0 ) )
       ...                               ^
     # ./solution.rb:130:in `instance_eval'
     # ./solution.rb:130:in `totally_recall'
     # ./spec.rb:192:in `block (3 levels) in <top (required)>'
  7) ArnoldCPM has boolean arithmetic that given *or* returns falsy values if both operands are falsy
     Failure/Error:
             ArnoldCPM.totally_recall do
               its_showtime
                 get_to_the_chopper _result
                 here_is_my_invitation i_lied
                 consider_that_a_divorce i_lied
                 enough_talk
                 talk_to_the_hand _result
               you_have_been_terminated
             end
     SyntaxError:
       (eval):1: syntax error, unexpected '(', expecting ')'
       ...0 != 0 ) != ( 0 && 0 != 0 ) ) ( or && or != ( 0 && 0 != 0 ) ...
       ...                               ^
       (eval):1: syntax error, unexpected '(', expecting end-of-input
       ...or && or != ( 0 && 0 != 0 ) ) ( ( 0 && 0 != 0 ) && ( 0 && 0 ...
       ...                               ^
     # ./solution.rb:130:in `instance_eval'
     # ./solution.rb:130:in `totally_recall'
     # ./spec.rb:223:in `block (3 levels) in <top (required)>'
  8) ArnoldCPM has boolean arithmetic that given *or* between two truthy values returns the first one
     Failure/Error:
             ArnoldCPM.totally_recall do
               its_showtime
                 get_to_the_chopper _result
                 here_is_my_invitation 11
                 consider_that_a_divorce 22
                 enough_talk
                 talk_to_the_hand _result
               you_have_been_terminated
             end
     SyntaxError:
       (eval):1: syntax error, unexpected '(', expecting ')'
       ...  _result = ( 11 && 11 != 0 ) ( or && or != 0 ) ( 22 && 22 !...
       ...                               ^
     # ./solution.rb:130:in `instance_eval'
     # ./solution.rb:130:in `totally_recall'
     # ./spec.rb:240:in `block (3 levels) in <top (required)>'
  9) ArnoldCPM has boolean arithmetic that given *and* returns falsy values if either operand is falsy
     Failure/Error:
             ArnoldCPM.totally_recall do
               its_showtime
                 get_to_the_chopper _result
                 here_is_my_invitation no_problemo
                 knock_knock i_lied
                 enough_talk
                 talk_to_the_hand _result
                 get_to_the_chopper _result
     SyntaxError:
       (eval):1: syntax error, unexpected '(', expecting ')'
       ...( 1 && 1 != ( 0 && 0 != 0 ) ) ( and && and != ( 0 && 0 != 0 ...
       ...                               ^
       (eval):1: syntax error, unexpected '(', expecting end-of-input
       ...d && and != ( 0 && 0 != 0 ) ) ( 0 && 0 != 0 ) )
       ...                               ^
     # ./solution.rb:130:in `instance_eval'
     # ./solution.rb:130:in `totally_recall'
     # ./spec.rb:261:in `block (3 levels) in <top (required)>'
  10) ArnoldCPM has boolean arithmetic that given *and* between two truthy values returns the second one
      Failure/Error:
              ArnoldCPM.totally_recall do
                its_showtime
                  get_to_the_chopper _result
                  here_is_my_invitation 11
                  knock_knock 22
                  enough_talk
                  talk_to_the_hand _result
                you_have_been_terminated
              end
      SyntaxError:
        (eval):1: syntax error, unexpected '(', expecting ')'
        ...  _result = ( 11 && 11 != 0 ) ( and && and != 0 ) ( 22 && 22...
        ...                               ^
      # ./solution.rb:130:in `instance_eval'
      # ./solution.rb:130:in `totally_recall'
      # ./spec.rb:292:in `block (3 levels) in <top (required)>'
  11) ArnoldCPM has boolean arithmetic that has the same precedence of *or* and *and* operations
      Failure/Error:
              ArnoldCPM.totally_recall do
                its_showtime
                  get_to_the_chopper _result
                  here_is_my_invitation no_problemo
                  consider_that_a_divorce no_problemo
                  knock_knock i_lied
                  enough_talk
                  talk_to_the_hand _result
                you_have_been_terminated
      SyntaxError:
        (eval):1: syntax error, unexpected '(', expecting ')'
        ...!= 0 ) ) != ( 0 && 0 != 0 ) ) ( or && or != ( 0 && 0 != 0 ) ...
        ...                               ^
        (eval):1: syntax error, unexpected '(', expecting end-of-input
        ...or && or != ( 0 && 0 != 0 ) ) ( ( 1 && 1 != ( 0 && 0 != 0 ) ...
        ...                               ^
      # ./solution.rb:130:in `instance_eval'
      # ./solution.rb:130:in `totally_recall'
      # ./spec.rb:309:in `block (3 levels) in <top (required)>'
  12) ArnoldCPM has branching mechanism that can nest if-else statements
      Failure/Error:
        ArnoldCPM.totally_recall do
          its_showtime
            because_im_going_to_say_please no_problemo
              because_im_going_to_say_please i_lied
                talk_to_the_hand 11
              bull_shit
                talk_to_the_hand 22
              you_have_no_respect_for_logic
            bull_shit
              because_im_going_to_say_please i_lied
      SyntaxError:
        (eval):17: syntax error, unexpected tINTEGER, expecting keyword_end
        (eval):19: syntax error, unexpected tINTEGER, expecting keyword_end
        (eval):23: syntax error, unexpected tINTEGER, expecting keyword_end
        (eval):25: syntax error, unexpected tINTEGER, expecting keyword_end
      # ./solution.rb:130:in `instance_eval'
      # ./solution.rb:130:in `totally_recall'
      # ./spec.rb:408:in `block (3 levels) in <top (required)>'
Finished in 1 minute 28.73 seconds (files took 0.13512 seconds to load)
29 examples, 12 failures
Failed examples:
rspec ./spec.rb:540 # ArnoldCPM can take functions as arguments
rspec ./spec.rb:583 # ArnoldCPM can return functions
rspec ./spec.rb:613 # ArnoldCPM defines new inner functions for each function invocation
rspec ./spec.rb:760 # ArnoldCPM can use closures in very convoluted ways
rspec ./spec.rb:130 # ArnoldCPM has algebra that can chain multiple operations
rspec ./spec.rb:183 # ArnoldCPM has boolean arithmetic that given *or* returns truthy values if one of the operands is truthy
rspec ./spec.rb:218 # ArnoldCPM has boolean arithmetic that given *or* returns falsy values if both operands are falsy
rspec ./spec.rb:235 # ArnoldCPM has boolean arithmetic that given *or* between two truthy values returns the first one
rspec ./spec.rb:252 # ArnoldCPM has boolean arithmetic that given *and* returns falsy values if either operand is falsy
rspec ./spec.rb:287 # ArnoldCPM has boolean arithmetic that given *and* between two truthy values returns the second one
rspec ./spec.rb:304 # ArnoldCPM has boolean arithmetic that has the same precedence of *or* and *and* operations
rspec ./spec.rb:378 # ArnoldCPM has branching mechanism that can nest if-else statements
Basically, цялото ти решение е един голям eval(to_s.gsub(/foo/, 'bar')) с няколко нагаждания. Което обезсмисля ArnoldC+- да е internal DSL и е изключително податливо на грешки (както забелязваш).
Все пак се радвам, че си си поиграла малко с регулярни изрази. (:
Може да погледнеш нашето решение ако ти е интересно как сме подходили към задачата. Надявам се да ти е била интересна и полезна.
Ами да...осъзнавам, че е калпаво решението и на мен хич не ми харесва. Мерси за съветите :)
