Skip to content

Commit 1e5e437

Browse files
committed
Add task 06 solution and spec
1 parent 1b09aa7 commit 1e5e437

File tree

2 files changed

+1158
-0
lines changed

2 files changed

+1158
-0
lines changed

tasks/06/solution.rb

+273
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
module ArnoldCPM
2+
KEYPHRASES = {
3+
i_lied: :false_,
4+
no_problemo: :true_,
5+
because_im_going_to_say_please: :if_,
6+
bull_shit: :else_,
7+
you_have_no_respect_for_logic: :end_if,
8+
get_up: :increment,
9+
get_down: :decrement,
10+
youre_fired: :multiply,
11+
he_had_to_split: :divide,
12+
i_let_him_go: :modulo,
13+
you_are_not_you_you_are_me: :equal_to,
14+
let_off_some_steam_bennet: :greater_than,
15+
consider_that_a_divorce: :or_,
16+
knock_knock: :and_,
17+
listen_to_me_very_carefully: :declare_function,
18+
i_need_your_clothes_your_boots_and_your_motorcycle: :function_parameter,
19+
give_these_people_air: :non_void_function,
20+
ill_be_back: :return_,
21+
hasta_la_vista_baby: :end_function_declaration,
22+
do_it_now: :call_function,
23+
get_your_ass_to_mars: :assign_invocation_result,
24+
its_showtime: :begin_main,
25+
you_have_been_terminated: :end_main,
26+
talk_to_the_hand: :print_,
27+
get_to_the_chopper: :assign_variable,
28+
here_is_my_invitation: :value_set,
29+
enough_talk: :end_assign_variable,
30+
}
31+
32+
class Parser < BasicObject
33+
attr_reader :parsed_body
34+
35+
def initialize(&program)
36+
@parsed_body = []
37+
instance_eval(&program)
38+
end
39+
40+
def method_missing(command, *arguments)
41+
if (KEYPHRASES.keys - %i(i_lied no_problemo)).include? command
42+
@parsed_body << [command, *arguments].map { |term| KEYPHRASES[term] || term }
43+
end
44+
45+
command
46+
end
47+
end
48+
49+
class Scope
50+
attr_reader :parent_scope
51+
52+
def initialize(parent_scope = {true_: 1, false_: 0})
53+
@declared_entities = {}
54+
@parent_scope = parent_scope
55+
end
56+
57+
def [](resolvee)
58+
if reference?(resolvee)
59+
@declared_entities[resolvee] || @parent_scope[resolvee]
60+
else
61+
resolvee
62+
end
63+
end
64+
65+
def []=(name, value)
66+
@declared_entities[name] = self[value]
67+
end
68+
69+
private
70+
71+
def reference?(value)
72+
value.is_a? Symbol
73+
end
74+
end
75+
76+
class Function
77+
attr_accessor :void
78+
alias_method :void?, :void
79+
80+
def initialize(outer_scope)
81+
@definition_scope = Scope.new(outer_scope)
82+
@void = true
83+
@parameters = []
84+
@body = []
85+
end
86+
87+
def add_parameter(parameter)
88+
@parameters.push(parameter)
89+
end
90+
91+
def add_line(*line)
92+
@body.push(line)
93+
end
94+
95+
def invoke(*arguments)
96+
execution_scope = Scope.new(@definition_scope)
97+
@parameters.zip(arguments).each do |parameter, argument|
98+
execution_scope[parameter] = argument
99+
end
100+
101+
catch(:return) do
102+
Executor.new(@body, execution_scope).execute
103+
end
104+
end
105+
end
106+
107+
class Executor
108+
def initialize(body, scope = Scope.new)
109+
@body = body
110+
@scope = scope
111+
@should_skip = []
112+
@nested_function_definition_balance = 0
113+
end
114+
115+
def execute
116+
@body.each do |command, *arguments|
117+
if part_of_ongoing_function_declaration?(command)
118+
@nested_function_definition_balance += 1 if command == :declare_function
119+
@nested_function_definition_balance -= 1 if command == :end_function_declaration
120+
@current_function_definition.add_line(command, *arguments)
121+
elsif @should_skip.none? || control_flow?(command)
122+
send(command, *arguments)
123+
end
124+
end
125+
126+
self
127+
end
128+
129+
def if_(condition)
130+
@scope = Scope.new(@scope)
131+
@should_skip.push(!as_boolean(@scope[condition]))
132+
end
133+
134+
def else_
135+
@should_skip[-1] = !@should_skip.last
136+
end
137+
138+
def end_if
139+
@scope = @scope.parent_scope
140+
@should_skip.pop
141+
end
142+
143+
def increment(amount)
144+
@entity_value += @scope[amount]
145+
end
146+
147+
def decrement(amount)
148+
@entity_value -= @scope[amount]
149+
end
150+
151+
def multiply(multiplicand)
152+
@entity_value *= @scope[multiplicand]
153+
end
154+
155+
def divide(divisor)
156+
@entity_value /= @scope[divisor]
157+
end
158+
159+
def modulo(modulus)
160+
@entity_value %= @scope[modulus]
161+
end
162+
163+
def equal_to(other)
164+
@entity_value = as_integer(@entity_value == @scope[other])
165+
end
166+
167+
def greater_than(other)
168+
@entity_value = as_integer(@entity_value > @scope[other])
169+
end
170+
171+
def or_(other)
172+
@entity_value = as_boolean(@entity_value) ? @entity_value : @scope[other]
173+
end
174+
175+
def and_(other)
176+
@entity_value = as_boolean(@entity_value) ? @scope[other] : @entity_value
177+
end
178+
179+
def assign_variable(name)
180+
@entity_name = name
181+
end
182+
alias_method :assign_invocation_result, :assign_variable
183+
184+
def value_set(value)
185+
@entity_value = @scope[value]
186+
end
187+
188+
def end_assign_variable
189+
@scope[@entity_name] = @entity_value
190+
end
191+
192+
def end_function_declaration
193+
@scope[@entity_name] = @current_function_definition
194+
@current_function_definition = nil
195+
end
196+
alias_method :end_main, :end_function_declaration
197+
198+
def declare_function(name)
199+
@entity_name = name
200+
@current_function_definition = Function.new(@scope)
201+
end
202+
203+
def function_parameter(name)
204+
@current_function_definition.add_parameter(name)
205+
end
206+
207+
def non_void_function
208+
@current_function_definition.void = false
209+
end
210+
211+
def return_(value = 0)
212+
throw :return, @scope[value]
213+
end
214+
215+
def begin_main
216+
declare_function :main
217+
end
218+
219+
def call_function(name, *arguments)
220+
function = @scope[name]
221+
resolved_arguments = arguments.map { |argument| @scope[argument] }
222+
invocation_result = function.invoke(*resolved_arguments)
223+
224+
@scope[@entity_name] = invocation_result unless function.void?
225+
end
226+
227+
def print_(value)
228+
ArnoldCPM.printer.print(@scope[value])
229+
end
230+
231+
private
232+
233+
def as_integer(boolean)
234+
boolean ? 1 : 0
235+
end
236+
237+
def as_boolean(entity)
238+
entity != 0
239+
end
240+
241+
def part_of_ongoing_function_declaration?(command)
242+
@current_function_definition &&
243+
(!function_meta?(command) || belongs_to_nested_function?)
244+
end
245+
246+
def function_meta?(command)
247+
[
248+
:function_parameter,
249+
:non_void_function,
250+
:end_function_declaration,
251+
:end_main,
252+
].include?(command)
253+
end
254+
255+
def belongs_to_nested_function?
256+
@nested_function_definition_balance.nonzero?
257+
end
258+
259+
def control_flow?(command)
260+
[:if_, :else_, :end_if].include?(command)
261+
end
262+
end
263+
264+
class << self
265+
attr_accessor :printer
266+
267+
def totally_recall(&program)
268+
parser = Parser.new(&program)
269+
270+
Executor.new(parser.parsed_body).execute.call_function(:main)
271+
end
272+
end
273+
end

0 commit comments

Comments
 (0)