Download language/createdpython.py from WARAJA/Tzefa: direct link, hf CLI and curl.
- Browser
- Download file 38.3 kB
-
https://huggingface.co/spaces/WARAJA/Tzefa/resolve/main/language/createdpython.py
- Command line
-
hf download hf://spaces/WARAJA/Tzefa/language/createdpython.py
-
curl -L -o createdpython.py https://huggingface.co/spaces/WARAJA/Tzefa/resolve/main/language/createdpython.py
38.3 kB
| 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 | |