| | import sys
|
| | from typing import Dict, List, Any, Callable, Optional
|
| |
|
| |
|
| | 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)
|
| | ]
|
| |
|
| |
|
| | line_count: int = 0
|
| | current_line: int = 0
|
| | line_limit: int = 1000
|
| | function_limit: int = 25
|
| | function_count: int = 0
|
| | printed_output: str = ""
|
| |
|
| |
|
| | class Value: pass
|
| | class VmList: pass
|
| | class Condition: pass
|
| |
|
| |
|
| | cond_registry: Dict[str, Condition] = {}
|
| | saved_conds: Dict[str, Condition] = {}
|
| |
|
| |
|
| | saved_locals: Dict[str, Any] = {}
|
| | saved_globals: Dict[str, Dict[str, Any]] = {"INT": {}, "STR": {}, "LIST": {}, "BOOLEAN": {}}
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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()
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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)
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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
|
| |
|
| |
|
| |
|
| | 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]
|
| |
|
| | 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]
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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)
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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)
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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())
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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)
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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]
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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)
|
| |
|
| |
|
| | 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_locals(var_registry, saved_program_locals)
|
| |
|
| | current_local_vars = pop_local_vars(var_registry)
|
| |
|
| | current_local_conds = pop_local_conds()
|
| | tick_line()
|
| | return return_var
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | local_vars_stack = Stack()
|
| | local_vars_stack.push({"INT": {}, "STR": {}, "LIST": {}})
|
| | program_locals_stack = Stack()
|
| |
|
| | 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,
|
| | },
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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
|
| |
|