Tzefa / language /createdpython.py
WARAJA's picture
Update Tzefa Space (full pipeline)
35febfb verified
import sys
from typing import Dict, List, Any, Callable, Optional
# --- Comparison operator tables ---
COMPARE_OP_INDEX: Dict[str, int] = {"EQUALS": 0, "BIGEQUALS": 1, "BIGGER": 2}
COMPARE_OPS: List[Callable[[Any, Any], bool]] = [
(lambda x, y: x == y),
(lambda x, y: x >= y),
(lambda x, y: x > y)
]
# --- VM execution counters and limits ---
line_count: int = 0
current_line: int = 0
line_limit: int = 1000
function_limit: int = 25
function_count: int = 0
printed_output: str = ""
# --- Forward declarations for types ---
class Value: pass
class VmList: pass
class Condition: pass
# --- Condition registries ---
cond_registry: Dict[str, Condition] = {}
saved_conds: Dict[str, Condition] = {}
# --- Local variable save slots ---
saved_locals: Dict[str, Any] = {}
saved_globals: Dict[str, Dict[str, Any]] = {"INT": {}, "STR": {}, "LIST": {}, "BOOLEAN": {}}
# ---------------------------------------------------------------------------
# Line tracking
# ---------------------------------------------------------------------------
def set_current_line(line_num: int) -> bool:
"""Record the current executing line number for error reporting."""
global current_line
current_line = line_num
return True
# ---------------------------------------------------------------------------
# Node / Stack
# ---------------------------------------------------------------------------
class Node:
"""Singly-linked list node used internally by Stack."""
def __init__(self, value: Any):
self.value = value
self.next: Optional['Node'] = None
def set_next(self, next_value: Any) -> None:
"""Create and link a new node with the given value."""
self.next = Node(value=next_value)
def set_next_node(self, next_node: 'Node') -> None:
"""Directly link to an existing node."""
self.next = next_node
def get_next(self) -> Optional['Node']:
"""Return the next node."""
return self.next
def get_value(self) -> Any:
"""Return the value stored in this node."""
return self.value
class Stack:
"""Simple LIFO stack backed by a Python list."""
def __init__(self):
self.top: Optional[Node] = None
self.list: List[Any] = []
def is_empty(self) -> bool:
"""Return True if the stack holds no elements."""
return len(self.list) == 0
def push(self, value: Any) -> None:
"""Push a value onto the top of the stack."""
self.list.append(value)
def pop(self) -> Any:
"""Pop and return the top value of the stack."""
return self.list.pop()
function_call_stack = Stack()
# ---------------------------------------------------------------------------
# Error Handler
# ---------------------------------------------------------------------------
class ErrorHandler(Exception):
"""Centralised VM error reporter: prints diagnostics and terminates execution."""
def name_error(self, var_type: str, name: str) -> None:
"""Report that a variable of the given type and name does not exist."""
print(f"Error Line: {current_line}")
print(f"Var of name {name} doesn't exist as type {var_type}")
print(" ")
print_vars()
sys.exit(0)
def index_error(self, list_name: str, bad_index: int, list_size: int) -> None:
"""Report an out-of-bounds list index access."""
print(f"Error Line: {current_line}")
print(f"Tried to change index of list {list_name} with size of {list_size} to out-of-bounds index {bad_index}")
print(" ")
print_vars()
sys.exit(0)
def div_zero_error(self, var_name: str) -> None:
"""Report a division-by-zero attempt."""
print(f"Error Line: {current_line}")
print(f"Cant divide by zero and var {var_name} has value of zero")
print(" ")
print_vars()
sys.exit(0)
def doesnt_exist_error(self, name: str) -> None:
"""Report that no object with the given name exists."""
print(f"Error Line: {current_line}")
print(f"No object with name {name} exists")
print_vars()
sys.exit(0)
def write_error(self, name: str, value: Any) -> None:
"""Report an attempt to write to an unwritable variable."""
print(f"Error Line: {current_line}")
print(f"Tried to write value of {value} to unwritable variable {name}")
print(" ")
print_vars()
sys.exit(0)
def type_to_int_error(self, value: str) -> None:
"""Report that a string does not correspond to a known type name."""
print(f"Error Line: {current_line}")
print(f"No type such as {value}")
print(" ")
print_vars()
sys.exit(0)
def read_error(self, name: str) -> None:
"""Report an attempt to read from an unreadable variable."""
print(f"Error Line: {current_line}")
print(f"Tried to read from unreadable variable {name}")
print(" ")
print_vars()
sys.exit(0)
def line_limit_error(self) -> None:
"""Report that the program exceeded the maximum allowed line count."""
print(f"Error Line: {current_line}")
print("Program ran for too long")
print(" ")
print_vars()
sys.exit(0)
def overflow_error(self, call_stack: Stack) -> None:
"""Report a function call stack overflow."""
print(f"Error Line: {current_line}")
print("Executing too many function calls")
print(" ")
print_vars()
sys.exit(0)
def cant_change_index_error(self, name: str, added: int) -> None:
"""Report an attempt to resize an unwritable list."""
print(f"Error Line: {current_line}")
print(f"Tried to change indexes of list {name} and add size {added} but list is unwritable")
def var_exists_error(self, name: str) -> None:
"""Report an attempt to create a variable whose name is already taken."""
print(f"Error Line: {current_line}")
print(f"Tried to create object with name {name} but var already exists")
print(" ")
print_vars()
sys.exit(0)
def type_error(self, name: str, type1: str, type2: str) -> None:
"""Report a type mismatch during a variable assignment."""
print(f"Error Line: {current_line}")
print(f"Mismatch of types {type1} and {type2} in variable {name}")
print(" ")
print_vars()
sys.exit(0)
# ---------------------------------------------------------------------------
# Value
# ---------------------------------------------------------------------------
class Value:
"""A single typed, named VM variable with read/write permission flags."""
def __init__(self, name: str, value: Any, readable: bool, writable: bool, type_name: str):
self.name = name
self.value = value
self.readable = readable
self.writable = writable
self.type = type_name
def write(self, value: Any) -> None:
"""Write a value if writable, otherwise raise a write error."""
if self.writable:
self.value = value
else:
error_handler.write_error(self.name, value)
def force_write(self, value: Any) -> None:
"""Write a value unconditionally, bypassing the writable flag."""
self.value = value
def read(self) -> Any:
"""Read the value if readable, otherwise raise a read error."""
if self.readable:
return self.value
else:
error_handler.read_error(self.name)
def force_read(self) -> Any:
"""Read the value unconditionally, bypassing the readable flag."""
return self.value
def set_readable(self, readable: bool) -> None:
"""Set the readable permission flag."""
self.readable = readable
def set_writable(self, writable: bool) -> None:
"""Set the writable permission flag."""
self.writable = writable
def get_name(self) -> str:
"""Return the variable's name."""
return self.name
def is_writable(self) -> bool:
"""Return True if the variable is writable."""
return self.writable
def is_readable(self) -> bool:
"""Return True if the variable is readable."""
return self.readable
def to_string(self) -> str:
"""Return a string representation of the stored value."""
return str(self.value)
def give_type(self) -> str:
"""Return the type string of this variable."""
return self.type
def override(self, value: Any) -> None:
"""Directly overwrite the stored value, bypassing all checks."""
self.value = value
def make_copy(self) -> 'Value':
"""Return a readable, writable copy of this variable."""
return Value(self.name, self.value, self.readable, True, self.type)
def copy_var(self, source: 'Value') -> None:
"""Copy the value from source into this variable, with type and read checks."""
if self.type != source.type:
error_handler.type_error(self.name, self.type, source.type)
else:
if not source.is_readable():
error_handler.read_error(source.get_name())
else:
self.value = source.value
# ---------------------------------------------------------------------------
# VmList
# ---------------------------------------------------------------------------
class VmList:
"""A fixed-size, typed VM list whose elements are Value objects."""
def __init__(self, name: str, size: int, readable: bool, writable: bool, type_name: str):
self.size = size
self.index = 0
self.values: List[Value] = [
Value(name=(str(name) + " " + str(i)), value=0, writable=True, readable=True, type_name="INT")
for i in range(size)
]
self.types: List[str] = ["INT" for _ in range(size)]
self.readable = readable
self.writable = writable
self.name = name
self.type = type_name
def add_size(self, added: int) -> None:
"""Grow the list by `added` elements if writable."""
if self.writable:
old_size = self.size
self.size = self.size + added
# Recreate values list with previous values preserved or new slots added
# Note: The logic here mirrors the original, which re-checks 'compare_ops' in a
# way that copies old indices and creates new ones.
self.values = [
self.values[i] if i < old_size
else Value(name=(str(self.name) + " " + str(i)), value=0, writable=True, readable=True, type_name="INT")
for i in range(self.size)
]
self.types = self.types + ["INT" for _ in range(added)]
else:
error_handler.cant_change_index_error(self.name, added)
def force_add_size(self, added: int) -> None:
"""Grow the list by `added` elements unconditionally."""
old_size = self.size
self.size = self.size + added
self.values = [
self.values[i] if i < old_size
else Value(name=(str(self.name) + " " + str(i)), value=0, writable=True, readable=True, type_name="INT")
for i in range(self.size)
]
self.types = self.types + ["INT" for _ in range(added)]
def change_index(self, new_index: int) -> None:
"""Set the active index if readable and in bounds."""
if self.readable:
if new_index >= self.size:
error_handler.index_error(self.name, new_index, self.size)
else:
self.index = new_index
else:
error_handler.read_error(self.name)
def force_change_index(self, new_index: int) -> None:
"""Set the active index unconditionally, still checking bounds."""
if new_index >= self.size:
error_handler.index_error(self.name, new_index, self.size)
else:
self.index = new_index
def place_value(self, name: str, var_type: str) -> None:
"""Copy a variable into the current index slot if writable."""
if self.writable:
source = get_var(var_type, name)
if source.is_readable():
self.types[self.index] = var_type
self.values[self.index] = source.make_copy()
else:
error_handler.read_error(name)
else:
error_handler.write_error(self.name, name)
def force_place_value(self, name: str, var_type: str) -> None:
"""Copy a variable into the current index slot unconditionally."""
source = get_var(var_type, name)
if source.is_readable():
self.types[self.index] = var_type
self.values[self.index] = source.make_copy()
else:
error_handler.read_error(name)
def read_value(self) -> Value:
"""Return the Value at the current index if readable."""
if self.readable:
return self.values[self.index]
else:
error_handler.read_error(self.name)
return self.values[0] # Should be unreachable due to sys.exit
def read(self) -> Value:
"""Return the Value at the current index if readable (alias for read_value)."""
return self.read_value()
def force_read_value(self) -> Value:
"""Return the Value at the current index unconditionally."""
return self.values[self.index]
def copy_element_to(self, dest_value: Value) -> None:
"""Write the current element's value into dest_value, with type checking."""
if self.types[self.index] == dest_value.give_type():
dest_value.write(self.values[self.index].read())
else:
error_handler.type_error(
name=self.name,
type1=self.types[self.index],
type2=dest_value.give_type()
)
def read_type(self) -> str:
"""Return the type string of the element at the current index if readable."""
if self.readable:
return self.types[self.index]
else:
error_handler.read_error(self.name)
return ""
def force_read_type(self) -> str:
"""Return the type string of the element at the current index unconditionally."""
return self.types[self.index]
def to_string(self) -> str:
"""Return a bracketed string of all element values."""
parts = ""
for val in self.values:
parts = parts + str(val.to_string()) + " "
return "[ " + parts + " ]"
def to_type_string(self) -> str:
"""Return a string of the first-character type codes for all elements if readable."""
if self.readable:
return "".join(t[0] for t in self.types)
else:
error_handler.read_error(self.name)
return ""
def force_to_type_string(self) -> str:
"""Return a string of the first-character type codes for all elements unconditionally."""
return "".join(t[0] for t in self.types)
def set_readable(self, readable: bool) -> None:
"""Set the readable permission flag."""
self.readable = readable
def set_writable(self, writable: bool) -> None:
"""Set the writable permission flag."""
self.writable = writable
def get_name(self) -> str:
"""Return the list's name."""
return self.name
def is_writable(self) -> bool:
"""Return True if the list is writable."""
return self.writable
def is_readable(self) -> bool:
"""Return True if the list is readable."""
return self.readable
def get_values(self) -> List[Value]:
"""Return the raw list of Value elements."""
return self.values
def get_types(self) -> List[str]:
"""Return the list of type strings for each element."""
return self.types
def get_size(self) -> int:
"""Return the current size of the list."""
return self.size
def make_copy(self) -> 'VmList':
"""Return a full deep copy of this list as a writable instance."""
copy = VmList(self.name, self.size, self.readable, True, self.type)
copy.types = self.types.copy()
copy.values = [val.make_copy() for val in self.values]
return copy
def override(self, values: List[Value], types: List[str], size: int) -> None:
"""Directly replace the list's contents, bypassing all checks."""
self.values = values
self.types = types
self.size = size
def give_type(self) -> str:
"""Return the type string of this list object."""
return self.type
def copy_var(self, source_list: 'VmList') -> None:
"""Copy all content from source_list into this list, with type and read checks."""
if self.type != source_list.type:
error_handler.type_error(self.name, self.type, source_list.type)
else:
if not source_list.is_readable():
error_handler.read_error(source_list.get_name())
else:
self.type = 'LIST'
self.types = source_list.types.copy()
self.size = source_list.size
self.values = [var.make_copy() for var in source_list.values]
# ---------------------------------------------------------------------------
# Condition
# ---------------------------------------------------------------------------
class Condition:
"""A named conditional that compares two Value objects using a stored operator."""
def __init__(self, compare: str):
self.compare_index = COMPARE_OP_INDEX[compare]
self.left = Value("0", 0, False, False, "INT")
self.right = Value("0", 0, False, False, "INT")
self.type = "COND"
def set_compare(self, compare: str) -> None:
"""Change the comparison operator."""
self.compare_index = COMPARE_OP_INDEX[compare]
def set_left(self, left: Value) -> None:
"""Set the left operand Value."""
self.left = left
def set_right(self, right: Value) -> None:
"""Set the right operand Value."""
self.right = right
def evaluate(self) -> bool:
"""Evaluate the condition and return the boolean result."""
return COMPARE_OPS[self.compare_index](self.left.read(), self.right.read())
def give_type(self) -> str:
"""Return the type string of this object."""
return self.type
# ---------------------------------------------------------------------------
# Condition helpers
# ---------------------------------------------------------------------------
def add_cond(name, compare):
"""Create a new global condition with the given comparison operator."""
global cond_registry
if name in cond_registry:
error_handler.var_exists_error(name)
else:
cond_registry[name] = Condition(compare)
def add_local_cond(name, compare):
"""Create a new condition scoped to the current function call."""
global cond_registry, current_local_conds
if name in cond_registry:
error_handler.var_exists_error(name)
else:
cond_registry[name] = Condition(compare)
current_local_conds[name] = cond_registry[name]
return current_local_conds
def push_local_conds(local_conds):
"""Save the current local conditions onto the stack and remove them from the registry."""
global local_conds_stack, cond_registry, saved_conds
for name in local_conds:
del cond_registry[name]
local_conds_stack.push(local_conds)
def pop_local_conds():
"""Restore the previous function's local conditions from the stack."""
global local_conds_stack, cond_registry, saved_conds
popped = local_conds_stack.pop()
for name in saved_conds:
if name in cond_registry:
del cond_registry[name]
saved_conds = {}
for name in popped:
cond_registry[name] = popped[name]
saved_conds[name] = popped[name]
return popped
def get_cond(name):
"""Look up and return a condition by name, raising an error if absent."""
global cond_registry
if name in cond_registry:
return cond_registry[name]
else:
error_handler.doesnt_exist_error(name)
# ---------------------------------------------------------------------------
# Debug / print helpers
# ---------------------------------------------------------------------------
def print_vars():
"""Print all current variable values and the accumulated output buffer."""
global var_registry, printed_output
print("END OF PROGRAM")
print()
for var_type in var_registry:
print("All the vars used from type " + var_type)
for var_name in var_registry[var_type]:
if var_registry[var_type][var_name].is_writable():
print(var_name + " : " + var_registry[var_type][var_name].to_string())
print("")
print("All that was printed during the program")
print(printed_output)
# ---------------------------------------------------------------------------
# Variable helpers
# ---------------------------------------------------------------------------
def add_var(var_type, name, value):
"""Add a new global variable of the given type and initial value."""
global var_registry
if name in var_registry[var_type]:
error_handler.var_exists_error(name)
if var_type == "LIST":
var_registry[var_type][name] = VmList(name, value, True, True, var_type)
else:
var_registry[var_type][name] = Value(name, value, True, True, var_type)
def get_var(var_type, name):
"""Return the variable object for the given type and name."""
global var_registry
if name in var_registry[var_type]:
return var_registry[var_type][name]
else:
error_handler.doesnt_exist_error(name)
def add_local_var(var_type, name, value):
"""Add a new variable scoped to the current function call."""
global current_local_vars, var_registry
if name in var_registry[var_type]:
error_handler.var_exists_error(name)
if var_type == "LIST":
var_registry[var_type][name] = VmList(name, value, True, True, var_type)
current_local_vars[var_type][name] = var_registry[var_type][name]
else:
var_registry[var_type][name] = Value(name, value, True, True, var_type)
current_local_vars[var_type][name] = var_registry[var_type][name]
def push_local_vars(var_registry_ref: dict, local_vars: dict, stack: Stack):
"""Save current local variables to the stack and clear them from the registry."""
for var_type in local_vars:
for name in local_vars[var_type]:
del var_registry_ref[var_type][name]
stack.push(local_vars)
return {"INT": {}, "STR": {}, "LIST": {}, "BOOLEAN": {}}
def pop_local_vars(var_registry_ref: dict):
"""Restore the previous call's local variables from the stack."""
stack = local_vars_stack
last_call = stack.pop()
global saved_locals
for var_type in saved_locals:
for name in saved_locals[var_type]:
if name in var_registry_ref[var_type]:
del var_registry_ref[var_type][name]
saved_locals = {"INT": {}, "STR": {}, "LIST": {}, "BOOLEAN": {}}
for var_type in last_call:
for name in last_call[var_type]:
var_registry_ref[var_type][name] = last_call[var_type][name]
saved_locals[var_type][name] = last_call[var_type][name]
return last_call
# ---------------------------------------------------------------------------
# VM print
# ---------------------------------------------------------------------------
def vm_print(var, newline):
"""Print a VM variable's value and append it to the output buffer."""
global printed_output
text = var.to_string() + newline * '\n' + ' ' * (1 - newline)
print(text, end='')
printed_output = printed_output + text
# ---------------------------------------------------------------------------
# Arithmetic / string operations
# ---------------------------------------------------------------------------
def vm_add(var1, var2):
"""Add two INT variables and store the result in TEMPORARY."""
get_var("INT", "TEMPORARY").force_write(get_var("INT", var1).read() + get_var("INT", var2).read())
def vm_sub(var1, var2):
"""Subtract var2 from var1 and store the result in TEMPORARY."""
get_var("INT", "TEMPORARY").force_write(get_var("INT", var1).read() - get_var("INT", var2).read())
def vm_mul(var1, var2):
"""Multiply two INT variables and store the result in TEMPORARY."""
get_var("INT", "TEMPORARY").force_write(get_var("INT", var1).read() * get_var("INT", var2).read())
def vm_div(var1, var2):
"""Integer-divide var1 by var2 and store the result in TEMPORARY."""
if get_var("INT", var2).read() == 0:
error_handler.div_zero_error(var2)
get_var("INT", "TEMPORARY").force_write(get_var("INT", var1).read() // get_var("INT", var2).read())
def vm_float_div(var1, var2):
"""Float-divide var1 by var2 and store the result in TEMPORARY."""
if get_var("INT", var2).read() == 0:
error_handler.div_zero_error(var2)
get_var("INT", "TEMPORARY").force_write(get_var("INT", var1).read() / get_var("INT", var2).read())
def vm_pow(var1, var2):
"""Raise var1 to the power of var2 and store the result in TEMPORARY."""
get_var("INT", "TEMPORARY").force_write(get_var("INT", var1).read() ** get_var("INT", var2).read())
def vm_mod(var1, var2):
"""Compute var1 modulo var2 and store the result in TEMPORARY."""
if get_var("INT", var2).read() == 0:
error_handler.div_zero_error(var2)
get_var("INT", "TEMPORARY").force_write(int(get_var("INT", var1).read() % get_var("INT", var2).read()))
def vm_concat(var1, var2):
"""Concatenate two STR variables and store the result in TEMPSTRING."""
get_var("STR", "TEMPSTRING").force_write(get_var("STR", var1).read() + get_var("STR", var2).read())
# ---------------------------------------------------------------------------
# Explicit-destination ALU operations (4-word dialect)
# ---------------------------------------------------------------------------
def vm_add_to(dest: str, var1: str, var2: str) -> None:
"""Add two INT variables and store the result in *dest*."""
get_var("INT", dest).force_write(get_var("INT", var1).read() + get_var("INT", var2).read())
def vm_sub_to(dest: str, var1: str, var2: str) -> None:
"""Subtract var2 from var1 and store the result in *dest*."""
get_var("INT", dest).force_write(get_var("INT", var1).read() - get_var("INT", var2).read())
def vm_mul_to(dest: str, var1: str, var2: str) -> None:
"""Multiply two INT variables and store the result in *dest*."""
get_var("INT", dest).force_write(get_var("INT", var1).read() * get_var("INT", var2).read())
def vm_div_to(dest: str, var1: str, var2: str) -> None:
"""Integer-divide var1 by var2 and store the result in *dest*."""
if get_var("INT", var2).read() == 0:
error_handler.div_zero_error(var2)
get_var("INT", dest).force_write(get_var("INT", var1).read() // get_var("INT", var2).read())
def vm_float_div_to(dest: str, var1: str, var2: str) -> None:
"""Float-divide var1 by var2 and store the result in *dest*."""
if get_var("INT", var2).read() == 0:
error_handler.div_zero_error(var2)
get_var("INT", dest).force_write(get_var("INT", var1).read() / get_var("INT", var2).read())
def vm_pow_to(dest: str, var1: str, var2: str) -> None:
"""Raise var1 to the power of var2 and store the result in *dest*."""
get_var("INT", dest).force_write(get_var("INT", var1).read() ** get_var("INT", var2).read())
def vm_mod_to(dest: str, var1: str, var2: str) -> None:
"""Compute var1 modulo var2 and store the result in *dest*."""
if get_var("INT", var2).read() == 0:
error_handler.div_zero_error(var2)
get_var("INT", dest).force_write(int(get_var("INT", var1).read() % get_var("INT", var2).read()))
def vm_concat_to(dest: str, var1: str, var2: str) -> None:
"""Concatenate two STR variables and store the result in *dest*."""
get_var("STR", dest).force_write(get_var("STR", var1).read() + get_var("STR", var2).read())
def vm_list_grow(list_name, size_var):
"""Increase the size of a LIST variable by the value of an INT variable."""
get_var("LIST", list_name).add_size(get_var("INT", size_var).read())
def vm_assign_list(dest, src):
"""Copy a LIST variable from src into dest."""
get_var("LIST", dest).copy_var(get_var("LIST", src))
def vm_assign_str(dest, src):
"""Copy a STR variable from src into dest."""
get_var("STR", dest).copy_var(get_var("STR", src))
def vm_assign_int(dest, src):
"""Copy an INT variable from src into dest."""
get_var("INT", dest).copy_var(get_var("INT", src))
def vm_pad_str(var_name, num_spaces):
"""Append a fixed number of blank spaces to a STR variable."""
get_var("STR", var_name).write(get_var("STR", var_name).read() + ' ' * num_spaces)
def vm_type_to_int(type_str_var, dest_int_var):
"""Write the integer index of a type-name string variable into an INT variable."""
lookup = {"INT": 0, "STR": 1, "BOOLEAN": 2, "LIST": 3}
type_str = get_var('STR', type_str_var).read()
if type_str in lookup:
get_var('INT', dest_int_var).write(lookup[type_str])
else:
error_handler.type_to_int_error(type_str)
# ---------------------------------------------------------------------------
# Program-locals save/restore
# ---------------------------------------------------------------------------
def restore_program_locals(global_vars: dict, saved_program_locals: dict):
"""Restore program-level local variables into the global registry after a function call."""
global saved_globals
for var_type in saved_globals:
for name in saved_globals[var_type]:
del global_vars[var_type][name]
saved_globals = {"INT": {}, "STR": {}, "LIST": {}}
for var_type in saved_program_locals:
for name in saved_program_locals[var_type]:
global_vars[var_type][name] = saved_program_locals[var_type][name]
saved_globals[var_type][name] = saved_program_locals[var_type][name]
# ---------------------------------------------------------------------------
# Line tick
# ---------------------------------------------------------------------------
def tick_line():
"""Increment the execution line counter and abort if the line limit is exceeded."""
global line_count
line_count += 1
if line_count == line_limit:
error_handler.line_limit_error()
else:
return True
# ---------------------------------------------------------------------------
# Function call enter / exit
# ---------------------------------------------------------------------------
def enter_function_call(input_type, input_var_name, function, output_type, output_var_name, call_line):
"""Push a new function call frame, execute the function, and copy its return value."""
set_current_line(call_line)
global var_registry, function_count, program_locals_stack, current_program_locals
global program_local_names, current_local_vars, function_limit, current_local_conds
program_locals_stack.push(current_program_locals)
var_input = get_var(input_type, input_var_name)
# Reset program-local slots to unreadable/unwritable defaults
for slot_type in program_local_names:
slot_name = program_local_names[slot_type]
if slot_type == 'STR':
var_registry[slot_type][slot_name] = Value(slot_name, '', False, False, 'STR')
elif slot_type == 'INT':
var_registry[slot_type][slot_name] = Value(slot_name, 0, False, False, 'INT')
else:
var_registry[slot_type][slot_name] = VmList(slot_name, 8, False, False, 'LIST')
current_program_locals = {
"INT": {"LOCALINT": var_registry["INT"]["LOCALINT"]},
"STR": {"LOCALSTR": var_registry["STR"]["LOCALSTR"]},
"LIST": {"LOCALLIST": var_registry["LIST"]["LOCALLIST"]},
}
var_registry[input_type][program_local_names[input_type]].copy_var(var_input)
var_to_send = var_registry[input_type][program_local_names[input_type]]
function_count += 1
if function_count == function_limit:
error_handler.overflow_error(function_call_stack)
else:
current_local_vars = push_local_vars(var_registry, current_local_vars, local_vars_stack)
var_to_send.set_readable(True)
var_to_send.set_writable(True)
push_local_conds(current_local_conds)
current_local_conds = {}
output = get_var(output_type, output_var_name)
result = function()
output.copy_var(result)
tick_line()
def exit_function_call(return_type, return_var_name):
"""Pop the current function call frame and return the named output variable."""
global var_registry, program_locals_stack, function_count, current_local_conds
global current_local_vars, local_vars_stack
return_var = get_var(return_type, return_var_name)
function_count -= 1
saved_program_locals = program_locals_stack.pop()
# Restore program-level globals
restore_program_locals(var_registry, saved_program_locals)
# Restore call-level locals
current_local_vars = pop_local_vars(var_registry)
# Restore conditions
current_local_conds = pop_local_conds()
tick_line()
return return_var
# ---------------------------------------------------------------------------
# VM initialisation
# ---------------------------------------------------------------------------
local_vars_stack = Stack() # stack of local variable dicts per function call
local_vars_stack.push({"INT": {}, "STR": {}, "LIST": {}})
program_locals_stack = Stack() # stack of program-local variable dicts
LOOP_INTEGER = Value(name="LOOPINTEGER", value=0, readable=True, writable=False, type_name="INT")
LOOP_STRING = Value(name="LOOPSTRING", value="", readable=True, writable=False, type_name="STR")
LOOP_BOOL = Value(name="LOOPBOOL", value=True, readable=True, writable=False, type_name="BOOL")
LOOP_LIST = VmList( name="LOOPLIST", size=8, readable=True, writable=False, type_name="LIST")
TEMPORARY = Value(name="TEMPORARY", value=0, readable=True, writable=True, type_name="INT")
LOCAL_INT = Value(name="LOCALINT", value=0, readable=False, writable=False, type_name="INT")
TEMP_STRING = Value(name="TEMPSTRING", value="", readable=True, writable=False, type_name="STR")
LOCAL_STR = Value(name="LOCALSTR", value="", readable=False, writable=False, type_name="STR")
LOCAL_LIST = VmList( name="LOCALLIST", size=8, readable=False, writable=False, type_name="LIST")
TYPE_INT_VAL = Value(name="INTEGER", value="INT", readable=True, writable=False, type_name="STR")
TYPE_STR_VAL = Value(name="STRING", value="STR", readable=True, writable=False, type_name="STR")
TYPE_LIST_VAL = Value(name="LIST", value="LIST", readable=True, writable=False, type_name="STR")
TYPE_BOOLEAN_VAL = Value(name="BOOLEAN", value="BOOLEAN", readable=True, writable=False, type_name="STR")
loop_var_registry = {
"INT": {"LOOPINTEGER": LOOP_INTEGER},
"STR": {"LOOPSTRING": LOOP_STRING},
"LIST": {"LOOPLIST": LOOP_LIST},
"BOOLEAN": {"LOOPBOOL": LOOP_BOOL},
}
loop_var_by_type = {
"INT": LOOP_INTEGER,
"STR": LOOP_STRING,
"LIST": LOOP_LIST,
"BOOLEAN": LOOP_BOOL,
}
THE_TRUTH = Condition('EQUALS')
THE_TRUTH.set_left(TEMPORARY)
THE_TRUTH.set_right(TEMPORARY)
var_registry = {
"INT": {
"LOOPINTEGER": LOOP_INTEGER,
"TEMPORARY": TEMPORARY,
"LOCALINT": LOCAL_INT,
},
"STR": {
"LOOPSTRING": LOOP_STRING,
"TEMPSTRING": TEMP_STRING,
"LOCALSTR": LOCAL_STR,
"INTEGER": TYPE_INT_VAL,
"STRING": TYPE_STR_VAL,
"LIST": TYPE_LIST_VAL,
"BOOLEAN": TYPE_BOOLEAN_VAL,
},
"LIST": {
"LOOPLIST": LOOP_LIST,
"LOCALLIST": LOCAL_LIST,
},
"BOOLEAN": {
"LOOPBOOL": LOOP_BOOL,
},
}
# Number names (ZERO..ONEHUNDRED) are compile-time constants only.
# They are resolved to plain integer literals by the compiler (toline/word_to_num)
# and must NOT live in var_registry["INT"] — that would prevent users from naming
# their own variables ONE, ZERO, etc.
empty_local_vars = {"INT": {}, "STR": {}, "LIST": {}, "BOOLEAN": {}}
current_local_vars = empty_local_vars.copy()
current_program_locals = {
"INT": {"LOCALINT": var_registry["INT"]["LOCALINT"]},
"STR": {"LOCALSTR": var_registry["STR"]["LOCALSTR"]},
"LIST": {"LOCALLIST": var_registry["LIST"]["LOCALLIST"]},
}
program_local_names = {"INT": "LOCALINT", "STR": "LOCALSTR", "LIST": "LOCALLIST"}
local_conds_stack = Stack()
current_local_conds = {}
program_locals_stack.push(current_program_locals)
error_handler = ErrorHandler()
cond_registry['THETRUTH'] = THE_TRUTH