Ралица обнови решението на 29.12.2016 17:12 (преди почти 8 години)
+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 и е изключително податливо на грешки (както забелязваш).
Все пак се радвам, че си си поиграла малко с регулярни изрази. (:
Може да погледнеш нашето решение ако ти е интересно как сме подходили към задачата. Надявам се да ти е била интересна и полезна.
Ами да...осъзнавам, че е калпаво решението и на мен хич не ми харесва. Мерси за съветите :)