|
| 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