Class: Forthic::Interpreter
- Inherits:
-
Object
- Object
- Forthic::Interpreter
- Defined in:
- lib/forthic/interpreter.rb
Constant Summary collapse
- TOKEN_HANDLERS =
Token handler lookup table
{ TokenType::STRING => :handle_string_token, TokenType::COMMENT => :handle_comment_token, TokenType::START_ARRAY => :handle_start_array_token, TokenType::END_ARRAY => :handle_end_array_token, TokenType::START_MODULE => :handle_start_module_token, TokenType::END_MODULE => :handle_end_module_token, TokenType::START_DEF => :handle_start_definition_token, TokenType::START_MEMO => :handle_start_memo_token, TokenType::END_DEF => :handle_end_definition_token, TokenType::WORD => :handle_word_token }.freeze
Instance Attribute Summary collapse
-
#app_module ⇒ Object
readonly
Core interpreter components.
-
#default_module_flags ⇒ Object
Screen and module management.
-
#execution_state ⇒ Object
readonly
State objects.
-
#global_module ⇒ Object
readonly
Core interpreter components.
-
#module_flags ⇒ Object
Screen and module management.
-
#profiling_state ⇒ Object
readonly
State objects.
-
#registered_modules ⇒ Object
readonly
Core interpreter components.
-
#screens ⇒ Object
Screen and module management.
Instance Method Summary collapse
- #add_timestamp(label) ⇒ Object
- #count_word(word) ⇒ Object
- #cur_definition ⇒ Object
- #cur_module ⇒ ForthicModule
- #find_module(name) ⇒ ForthicModule
- #find_word(name) ⇒ Word?
- #get_app_module ⇒ ForthicModule
- #get_flags(module_id) ⇒ Hash
- #get_screen_forthic(screen_name) ⇒ String
- #get_string_location ⇒ CodeLocation?
- #halt ⇒ Object
- #handle_comment_token(_token) ⇒ Object
- #handle_end_array_token(_token) ⇒ Object
- #handle_end_definition_token(token) ⇒ Object
- #handle_end_module_token(_token) ⇒ Object
- #handle_start_array_token(token) ⇒ Object
- #handle_start_definition_token(token) ⇒ Object
- #handle_start_memo_token(token) ⇒ Object
- #handle_start_module_token(token) ⇒ Object
- #handle_string_token(token) ⇒ Object
- #handle_token(token) ⇒ Object
- #handle_word(word, location = nil) ⇒ Object
- #handle_word_token(token) ⇒ Object
- #import_module(mod, prefix = "") ⇒ Object
-
#initialize ⇒ Interpreter
constructor
A new instance of Interpreter.
- #is_compiling ⇒ Object
- #modify_flags(module_id, flags) ⇒ Object
- #module_stack ⇒ Object
- #module_stack_pop ⇒ Object
- #module_stack_push(mod) ⇒ Object
- #profile_timestamps ⇒ Object
- #register_module(mod) ⇒ Object
- #reset ⇒ Object
- #run(string, reference_location = nil) ⇒ Boolean
- #run_module_code(mod) ⇒ Object
- #run_with_tokenizer(tokenizer) ⇒ Boolean
- #set_flags(module_id, flags) ⇒ Object
-
#stack ⇒ Object
Delegation methods for execution state.
- #stack=(new_stack) ⇒ Object
- #stack_pop ⇒ Object
- #stack_push(val) ⇒ Object
-
#start_profiling ⇒ Object
Delegation methods for profiling.
- #stop_profiling ⇒ Object
- #word_histogram ⇒ Object
Constructor Details
#initialize ⇒ Interpreter
Returns a new instance of Interpreter.
120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/forthic/interpreter.rb', line 120 def initialize @registered_modules = {} @screens = {} @default_module_flags = {} @module_flags = {} @global_module = GlobalModule.new(self) @app_module = ForthicModule.new("", self) @execution_state = ExecutionState.new(@app_module) @profiling_state = ProfilingState.new end |
Instance Attribute Details
#app_module ⇒ Object (readonly)
Core interpreter components
100 101 102 |
# File 'lib/forthic/interpreter.rb', line 100 def app_module @app_module end |
#default_module_flags ⇒ Object
Screen and module management
102 103 104 |
# File 'lib/forthic/interpreter.rb', line 102 def default_module_flags @default_module_flags end |
#execution_state ⇒ Object (readonly)
State objects
104 105 106 |
# File 'lib/forthic/interpreter.rb', line 104 def execution_state @execution_state end |
#global_module ⇒ Object (readonly)
Core interpreter components
100 101 102 |
# File 'lib/forthic/interpreter.rb', line 100 def global_module @global_module end |
#module_flags ⇒ Object
Screen and module management
102 103 104 |
# File 'lib/forthic/interpreter.rb', line 102 def module_flags @module_flags end |
#profiling_state ⇒ Object (readonly)
State objects
104 105 106 |
# File 'lib/forthic/interpreter.rb', line 104 def profiling_state @profiling_state end |
#registered_modules ⇒ Object (readonly)
Core interpreter components
100 101 102 |
# File 'lib/forthic/interpreter.rb', line 100 def registered_modules @registered_modules end |
#screens ⇒ Object
Screen and module management
102 103 104 |
# File 'lib/forthic/interpreter.rb', line 102 def screens @screens end |
Instance Method Details
#add_timestamp(label) ⇒ Object
320 321 322 |
# File 'lib/forthic/interpreter.rb', line 320 def (label) @profiling_state.(label) end |
#count_word(word) ⇒ Object
316 317 318 |
# File 'lib/forthic/interpreter.rb', line 316 def count_word(word) @profiling_state.count_word(word) end |
#cur_definition ⇒ Object
164 165 166 |
# File 'lib/forthic/interpreter.rb', line 164 def cur_definition @execution_state.cur_definition end |
#cur_module ⇒ ForthicModule
230 231 232 |
# File 'lib/forthic/interpreter.rb', line 230 def cur_module @execution_state.module_stack.last end |
#find_module(name) ⇒ ForthicModule
236 237 238 239 240 241 242 243 |
# File 'lib/forthic/interpreter.rb', line 236 def find_module(name) raise ArgumentError, "Module name cannot be nil" if name.nil? raise ArgumentError, "Module name cannot be empty" if name.empty? result = @registered_modules[name] raise ForthicError.new(ErrorCodes::MODULE_NOT_FOUND, "Module '#{name}' not found", "Check the module name for typos and ensure it has been properly registered.") unless result result end |
#find_word(name) ⇒ Word?
297 298 299 300 301 302 303 304 305 |
# File 'lib/forthic/interpreter.rb', line 297 def find_word(name) result = nil @execution_state.module_stack.reverse_each do |m| result = m.find_word(name) break if result end result ||= @global_module.find_word(name) result end |
#get_app_module ⇒ ForthicModule
138 139 140 |
# File 'lib/forthic/interpreter.rb', line 138 def get_app_module @app_module end |
#get_flags(module_id) ⇒ Hash
177 178 179 180 181 182 |
# File 'lib/forthic/interpreter.rb', line 177 def get_flags(module_id) module_flags = @module_flags[module_id] || {} result = module_flags.dup @module_flags[module_id] = @default_module_flags[module_id].dup result end |
#get_screen_forthic(screen_name) ⇒ String
198 199 200 201 202 |
# File 'lib/forthic/interpreter.rb', line 198 def get_screen_forthic(screen_name) screen = @screens[screen_name] raise ForthicError.new(ErrorCodes::SCREEN_NOT_FOUND, "Unable to find screen \"#{screen_name}\"", "Screen not found. Check the screen name for typos or ensure it has been properly registered.") unless screen screen end |
#get_string_location ⇒ CodeLocation?
143 144 145 |
# File 'lib/forthic/interpreter.rb', line 143 def get_string_location @execution_state.string_location end |
#halt ⇒ Object
133 134 135 |
# File 'lib/forthic/interpreter.rb', line 133 def halt @execution_state.should_stop = true end |
#handle_comment_token(_token) ⇒ Object
377 378 379 |
# File 'lib/forthic/interpreter.rb', line 377 def handle_comment_token(_token) # Handle comment token (no-op) end |
#handle_end_array_token(_token) ⇒ Object
372 373 374 |
# File 'lib/forthic/interpreter.rb', line 372 def handle_end_array_token(_token) handle_word(EndArrayWord.new) end |
#handle_end_definition_token(token) ⇒ Object
398 399 400 401 402 403 404 405 406 407 |
# File 'lib/forthic/interpreter.rb', line 398 def handle_end_definition_token(token) raise ForthicError.new(ErrorCodes::DEFINITION_WITHOUT_START, "Definition ended without start", "A definition was ended when none was active. Check for extra semicolons.", token.location) unless @execution_state.is_compiling raise ForthicError.new(ErrorCodes::MISSING_DEFINITION, "No current definition to end", "Internal error: definition state is inconsistent.", token.location) unless @execution_state.cur_definition if @execution_state.is_memo_definition cur_module.add_memo_words(@execution_state.cur_definition) else cur_module.add_word(@execution_state.cur_definition) end @execution_state.is_compiling = false end |
#handle_end_module_token(_token) ⇒ Object
359 360 361 362 363 364 |
# File 'lib/forthic/interpreter.rb', line 359 def handle_end_module_token(_token) word = EndModuleWord.new @execution_state.cur_definition.add_word(word) if @execution_state.is_compiling count_word(word) word.execute(self) end |
#handle_start_array_token(token) ⇒ Object
367 368 369 |
# File 'lib/forthic/interpreter.rb', line 367 def handle_start_array_token(token) handle_word(PushValueWord.new("<start_array_token>", token)) end |
#handle_start_definition_token(token) ⇒ Object
382 383 384 385 386 387 |
# File 'lib/forthic/interpreter.rb', line 382 def handle_start_definition_token(token) raise ForthicError.new(ErrorCodes::NESTED_DEFINITION, "Nested definition not allowed", "A definition was started while another definition is active. Ensure all definitions end with semicolons.", token.location) if @execution_state.is_compiling @execution_state.cur_definition = DefinitionWord.new(token.string) @execution_state.is_compiling = true @execution_state.is_memo_definition = false end |
#handle_start_memo_token(token) ⇒ Object
390 391 392 393 394 395 |
# File 'lib/forthic/interpreter.rb', line 390 def handle_start_memo_token(token) raise ForthicError.new(ErrorCodes::NESTED_MEMO_DEFINITION, "Nested memo definition not allowed", "A memo definition was started while another definition is active. Ensure all definitions end with semicolons.", token.location) if @execution_state.is_compiling @execution_state.cur_definition = DefinitionWord.new(token.string) @execution_state.is_compiling = true @execution_state.is_memo_definition = true end |
#handle_start_module_token(token) ⇒ Object
351 352 353 354 355 356 |
# File 'lib/forthic/interpreter.rb', line 351 def handle_start_module_token(token) word = StartModuleWord.new(token.string) @execution_state.cur_definition.add_word(word) if @execution_state.is_compiling count_word(word) word.execute(self) end |
#handle_string_token(token) ⇒ Object
345 346 347 348 |
# File 'lib/forthic/interpreter.rb', line 345 def handle_string_token(token) value = PositionedString.new(token.string, token.location) handle_word(PushValueWord.new("<string>", value)) end |
#handle_token(token) ⇒ Object
333 334 335 336 337 338 339 340 341 342 |
# File 'lib/forthic/interpreter.rb', line 333 def handle_token(token) return if token.type == TokenType::EOS handler = TOKEN_HANDLERS[token.type] if handler send(handler, token) else raise ForthicError.new(ErrorCodes::UNKNOWN_TOKEN, "Unknown token type '#{token.string}'", "This token type is not recognized. Check for typos or unsupported syntax.", token.location) end end |
#handle_word(word, location = nil) ⇒ Object
418 419 420 421 422 423 424 425 426 |
# File 'lib/forthic/interpreter.rb', line 418 def handle_word(word, location = nil) if @execution_state.is_compiling word.set_location(location) @execution_state.cur_definition.add_word(word) else count_word(word) word.execute(self) end end |
#handle_word_token(token) ⇒ Object
410 411 412 413 414 |
# File 'lib/forthic/interpreter.rb', line 410 def handle_word_token(token) word = find_word(token.string) raise ForthicError.new(ErrorCodes::WORD_NOT_FOUND, "Word '#{token.string}' not found", "Check for typos in the word name or ensure the word has been defined.", token.location) unless word handle_word(word, token.location) end |
#import_module(mod, prefix = "") ⇒ Object
277 278 279 280 281 |
# File 'lib/forthic/interpreter.rb', line 277 def import_module(mod, prefix = "") raise ArgumentError, "Module cannot be nil" if mod.nil? register_module(mod) @app_module.import_module(prefix, mod, self) end |
#is_compiling ⇒ Object
160 161 162 |
# File 'lib/forthic/interpreter.rb', line 160 def is_compiling @execution_state.is_compiling end |
#modify_flags(module_id, flags) ⇒ Object
186 187 188 189 |
# File 'lib/forthic/interpreter.rb', line 186 def modify_flags(module_id, flags) module_flags = @module_flags[module_id] || {} @module_flags[module_id] = module_flags.merge(flags) end |
#module_stack ⇒ Object
156 157 158 |
# File 'lib/forthic/interpreter.rb', line 156 def module_stack @execution_state.module_stack end |
#module_stack_pop ⇒ Object
264 265 266 |
# File 'lib/forthic/interpreter.rb', line 264 def module_stack_pop @execution_state.module_stack.pop end |
#module_stack_push(mod) ⇒ Object
259 260 261 262 |
# File 'lib/forthic/interpreter.rb', line 259 def module_stack_push(mod) raise ArgumentError, "Module cannot be nil" if mod.nil? @execution_state.module_stack.push(mod) end |
#profile_timestamps ⇒ Object
328 329 330 |
# File 'lib/forthic/interpreter.rb', line 328 def @profiling_state. end |
#register_module(mod) ⇒ Object
269 270 271 272 273 |
# File 'lib/forthic/interpreter.rb', line 269 def register_module(mod) raise ArgumentError, "Module cannot be nil" if mod.nil? raise ArgumentError, "Module must respond to :name" unless mod.respond_to?(:name) @registered_modules[mod.name] = mod end |
#reset ⇒ Object
191 192 193 194 |
# File 'lib/forthic/interpreter.rb', line 191 def reset @app_module.variables = {} @execution_state.reset(@app_module) end |
#run(string, reference_location = nil) ⇒ Boolean
207 208 209 210 |
# File 'lib/forthic/interpreter.rb', line 207 def run(string, reference_location = nil) tokenizer = Tokenizer.new(string, reference_location) run_with_tokenizer(tokenizer) end |
#run_module_code(mod) ⇒ Object
284 285 286 287 288 289 290 291 292 293 |
# File 'lib/forthic/interpreter.rb', line 284 def run_module_code(mod) raise ArgumentError, "Module cannot be nil" if mod.nil? module_stack_push(mod) run(mod.forthic_code) module_stack_pop rescue => e error = ForthicError.new(ErrorCodes::MODULE_EXECUTION_ERROR, "Error executing module '#{mod.name}'", "An error occurred while running the module code. Check the module implementation for syntax errors.") error.set_caught_error(e) raise error end |
#run_with_tokenizer(tokenizer) ⇒ Boolean
214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/forthic/interpreter.rb', line 214 def run_with_tokenizer(tokenizer) token = nil loop do token = tokenizer.next_token handle_token(token) break if token.type == TokenType::EOS || @execution_state.should_stop next if [TokenType::START_DEF, TokenType::END_DEF, TokenType::COMMENT].include?(token.type) || @execution_state.is_compiling end true rescue => e error = ForthicError.new(ErrorCodes::EXECUTION_ERROR, "Error executing token '#{token&.string}'", "An unexpected error occurred during execution. Check the token syntax and try again.", token&.location) error.set_caught_error(e) raise error end |
#set_flags(module_id, flags) ⇒ Object
170 171 172 173 |
# File 'lib/forthic/interpreter.rb', line 170 def set_flags(module_id, flags) @default_module_flags[module_id] = flags @module_flags[module_id] = flags end |
#stack ⇒ Object
Delegation methods for execution state
148 149 150 |
# File 'lib/forthic/interpreter.rb', line 148 def stack @execution_state.stack end |
#stack=(new_stack) ⇒ Object
152 153 154 |
# File 'lib/forthic/interpreter.rb', line 152 def stack=(new_stack) @execution_state.stack = new_stack end |
#stack_pop ⇒ Object
251 252 253 254 255 256 |
# File 'lib/forthic/interpreter.rb', line 251 def stack_pop raise ForthicError.new(ErrorCodes::STACK_UNDERFLOW, "Stack underflow", "Attempted to pop from an empty stack. This indicates a logical error in the Forthic code.") if @execution_state.stack.empty? result = @execution_state.stack.pop @execution_state.string_location = result.is_a?(PositionedString) ? result.location : nil result.is_a?(PositionedString) ? result.value_of : result end |
#stack_push(val) ⇒ Object
246 247 248 |
# File 'lib/forthic/interpreter.rb', line 246 def stack_push(val) @execution_state.stack.push(val) end |
#start_profiling ⇒ Object
Delegation methods for profiling
308 309 310 |
# File 'lib/forthic/interpreter.rb', line 308 def start_profiling @profiling_state.start_profiling end |
#stop_profiling ⇒ Object
312 313 314 |
# File 'lib/forthic/interpreter.rb', line 312 def stop_profiling @profiling_state.stop_profiling end |
#word_histogram ⇒ Object
324 325 326 |
# File 'lib/forthic/interpreter.rb', line 324 def word_histogram @profiling_state.word_histogram end |