| """AST node classes shared by the parser, semantic validator, and interpreter. | |
| Each class represents one language construct after parsing, for example a | |
| VariableDeclarationNode, BinaryOpNode, FunctionCallNode, or WhileLoopNode. | |
| """ | |
| # AUTO: Defines class `ASTNode`. | |
| class ASTNode: | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, node_type, value=None, line=None): | |
| # LINE: node_type tells semantic/interpreter what this AST node represents. | |
| self.node_type = node_type | |
| # LINE: value stores the node's main text/value, like an operator or name. | |
| self.value = value | |
| # LINE: children stores nested AST nodes under this node. | |
| self.children = [] | |
| # LINE: parent points back to the surrounding AST node. | |
| self.parent = None | |
| # LINE: line stores the original source line for error messages. | |
| self.line = line | |
| # AUTO: Defines function `add_child`. | |
| def add_child(self, child): | |
| # GUIDE: Parent links let later stages know the surrounding construct, | |
| # for example water() can see whether it belongs to an assignment. | |
| # AUTO: Sets `child.parent`. | |
| child.parent = self | |
| # AUTO: Appends a value to a list. | |
| self.children.append(child) | |
| # AUTO: Defines function `print_tree`. | |
| def print_tree(self, level=0): | |
| # AUTO: Sets `indent`. | |
| indent = ' ' * (level * 3) | |
| # AUTO: Calls `print`. | |
| print(f"{indent}╚═{self.node_type}: {self.value if self.value else ''}") | |
| # AUTO: Starts a loop over these values. | |
| for child in self.children: | |
| # AUTO: Calls `child.print_tree`. | |
| child.print_tree(level + 1) | |
| # AUTO: Defines class `ProgramNode`. | |
| class ProgramNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, line=None): | |
| # LINE: ProgramNode is the root container for the whole AST. | |
| super().__init__("Program", line=line) | |
| # AUTO: Defines class `VariableDeclarationNode`. | |
| class VariableDeclarationNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, var_type, var_name, value=None, line=None): | |
| # LINE: VariableDeclaration represents code like seed x = 10; | |
| super().__init__("VariableDeclaration", line=line) | |
| # LINE: First child stores declared type. | |
| self.add_child(ASTNode("Type", var_type, line=line)) | |
| # LINE: Second child stores variable name. | |
| self.add_child(ASTNode("Identifier", var_name, line=line)) | |
| # AUTO: Checks this condition. | |
| if value: | |
| # LINE: Optional third child stores initializer expression. | |
| self.add_child(value) | |
| # AUTO: Defines class `AssignmentNode`. | |
| class AssignmentNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, target, value, line=None): | |
| # LINE: Assignment represents code like x = y + 1; | |
| super().__init__("Assignment", line=line) | |
| # AUTO: Checks this condition. | |
| if isinstance(target, str): | |
| # LINE: Simple target names are wrapped as Identifier nodes. | |
| self.add_child(ASTNode("Identifier", target, line=line)) | |
| # AUTO: Runs when previous condition did not pass. | |
| else: | |
| # LINE: Complex targets are already AST nodes, like array/member access. | |
| self.add_child(target) | |
| # LINE: Second child stores the RHS expression/value. | |
| self.add_child(value) | |
| # AUTO: Defines class `BinaryOpNode`. | |
| class BinaryOpNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, left, operator, right, line=None): | |
| # LINE: BinaryOp stores operator in value with left/right children. | |
| super().__init__("BinaryOp", operator, line=line) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(left) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(right) | |
| # AUTO: Defines class `FunctionDeclarationNode`. | |
| class FunctionDeclarationNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, return_type, name, params, line=None): | |
| # LINE: FunctionDeclaration stores function name in value. | |
| super().__init__("FunctionDeclaration", name, line=line) | |
| # LINE: First child stores return type. | |
| self.add_child(ASTNode("ReturnType", return_type, line=line)) | |
| # LINE: Second child stores parameters. | |
| self.add_child(params) | |
| # AUTO: Defines class `FunctionCallNode`. | |
| class FunctionCallNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, name, args, line=None): | |
| # LINE: FunctionCall stores called function name in value. | |
| super().__init__("FunctionCall", name, line=line) | |
| # LINE: Each argument becomes a child. | |
| for arg in args: | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(arg) | |
| # AUTO: Defines class `IfStatementNode`. | |
| class IfStatementNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, condition, line=None): | |
| # AUTO: Sets `super().__init__("IfStatement", line`. | |
| super().__init__("IfStatement", line=line) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(condition) | |
| # AUTO: Defines class `ForLoopNode`. | |
| class ForLoopNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, initialization, condition, update, line=None): | |
| # AUTO: Sets `super().__init__("ForLoop", line`. | |
| super().__init__("ForLoop", line=line) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(initialization) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(condition) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(update) | |
| # AUTO: Defines class `WhileLoopNode`. | |
| class WhileLoopNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, condition, line=None): | |
| # AUTO: Sets `super().__init__("WhileLoop", line`. | |
| super().__init__("WhileLoop", line=line) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(condition) | |
| # AUTO: Defines class `DoWhileLoopNode`. | |
| class DoWhileLoopNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, condition, line=None): | |
| # AUTO: Sets `super().__init__("DoWhileLoop", line`. | |
| super().__init__("DoWhileLoop", line=line) | |
| # AUTO: Defines class `PrintNode`. | |
| class PrintNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, args, line=None): | |
| # AUTO: Sets `super().__init__("PrintStatement", line`. | |
| super().__init__("PrintStatement", line=line) | |
| # AUTO: Starts a loop over these values. | |
| for arg in args: | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(arg) | |
| # AUTO: Defines class `UnaryOpNode`. | |
| class UnaryOpNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, operator, operand, position="pre", line=None): | |
| # AUTO: Sets `super().__init__("UnaryOp", operator, line`. | |
| super().__init__("UnaryOp", operator, line=line) | |
| # AUTO: Sets `self.position`. | |
| self.position = position | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(operand) | |
| # AUTO: Defines class `FertileDeclarationNode`. | |
| class FertileDeclarationNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, var_type, var_name, value, line=None): | |
| # AUTO: Sets `super().__init__("SturdyDeclaration", line`. | |
| super().__init__("SturdyDeclaration", line=line) | |
| # AUTO: Sets `self.add_child(ASTNode("Type", var_type, line`. | |
| self.add_child(ASTNode("Type", var_type, line=line)) | |
| # AUTO: Sets `self.add_child(ASTNode("Identifier", var_name, line`. | |
| self.add_child(ASTNode("Identifier", var_name, line=line)) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(value) | |
| # AUTO: Defines class `ReturnNode`. | |
| class ReturnNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, return_value=None, line=None): | |
| # AUTO: Sets `super().__init__("Return", line`. | |
| super().__init__("Return", line=line) | |
| # AUTO: Checks this condition. | |
| if return_value: | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(return_value) | |
| # AUTO: Defines class `UpdateNode`. | |
| class UpdateNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, operator, operand, prefix = True, line=None): | |
| # AUTO: Sets `super().__init__("Update", line`. | |
| super().__init__("Update", line=line) | |
| # AUTO: Sets `self.prefix`. | |
| self.prefix = prefix | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(operand) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(operator) | |
| # AUTO: Defines class `SwitchNode`. | |
| class SwitchNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, expression, cases, default_case, line=None): | |
| # AUTO: Sets `super().__init__("Switch", line`. | |
| super().__init__("Switch", line=line) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(expression) | |
| # AUTO: Starts a loop over these values. | |
| for case in cases: | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(case) | |
| # AUTO: Checks this condition. | |
| if default_case is not None: | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(default_case) | |
| # AUTO: Defines class `ContinueNode`. | |
| class ContinueNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, line=None): | |
| # AUTO: Sets `super().__init__("Continue", line`. | |
| super().__init__("Continue", line=line) | |
| # AUTO: Defines class `BreakNode`. | |
| class BreakNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, line=None): | |
| # AUTO: Sets `super().__init__("Break", line`. | |
| super().__init__("Break", line=line) | |
| # AUTO: Defines class `ListNode`. | |
| class ListNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, line=None, elements=None): | |
| # AUTO: Sets `super().__init__("List", line`. | |
| super().__init__("List", line=line) | |
| # AUTO: Sets `self.elements`. | |
| self.elements = elements or [] | |
| # AUTO: Starts a loop over these values. | |
| for element in self.elements: | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(element) | |
| # AUTO: Defines class `SoilNode`. | |
| class SoilNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, variable_name, line=None): | |
| # AUTO: Sets `super().__init__("SoilFunction", line`. | |
| super().__init__("SoilFunction", line=line) | |
| # AUTO: Sets `self.add_child(ASTNode("Identifier", variable_name, line`. | |
| self.add_child(ASTNode("Identifier", variable_name, line=line)) | |
| # AUTO: Defines class `BloomNode`. | |
| class BloomNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, variable_name, line=None): | |
| # AUTO: Sets `super().__init__("BloomFunction", line`. | |
| super().__init__("BloomFunction", line=line) | |
| # AUTO: Sets `self.add_child(ASTNode("Identifier", variable_name, line`. | |
| self.add_child(ASTNode("Identifier", variable_name, line=line)) | |
| # AUTO: Defines class `AppendNode`. | |
| class AppendNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, elements, line=None): | |
| # AUTO: Sets `super().__init__("Append", line`. | |
| super().__init__("Append", line=line) | |
| # AUTO: Starts a loop over these values. | |
| for elem in elements: | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(elem) | |
| # AUTO: Defines class `InsertNode`. | |
| class InsertNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, index, elements, line=None): | |
| # AUTO: Sets `super().__init__("Insert", line`. | |
| super().__init__("Insert", line=line) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(index) | |
| # AUTO: Starts a loop over these values. | |
| for elem in elements: | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(elem) | |
| # AUTO: Defines class `RemoveNode`. | |
| class RemoveNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, value, index, line=None): | |
| # AUTO: Sets `super().__init__("Remove", line`. | |
| super().__init__("Remove", line=line) | |
| # AUTO: Sets `self.add_child(ASTNode("Identifier", value, line`. | |
| self.add_child(ASTNode("Identifier", value, line=line)) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(index) | |
| # AUTO: Defines class `CastNode`. | |
| class CastNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, target_type, expression, line=None): | |
| # AUTO: Sets `super().__init__("TypeCast", line`. | |
| super().__init__("TypeCast", line=line) | |
| # AUTO: Sets `self.add_child(ASTNode("TargetType", target_type, line`. | |
| self.add_child(ASTNode("TargetType", target_type, line=line)) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(expression) | |
| # AUTO: Defines class `ListAccessNode`. | |
| class ListAccessNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, list_name, index_expr, line=None): | |
| # AUTO: Sets `super().__init__("ListAccess", line`. | |
| super().__init__("ListAccess", line=line) | |
| # AUTO: Sets `self.add_child(ASTNode("ListName", list_name, line`. | |
| self.add_child(ASTNode("ListName", list_name, line=line)) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(index_expr) | |
| # AUTO: Defines class `MemberAccessNode`. | |
| class MemberAccessNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, object_name, member_name, line=None): | |
| # AUTO: Sets `super().__init__("MemberAccess", line`. | |
| super().__init__("MemberAccess", line=line) | |
| # AUTO: Checks this condition. | |
| if isinstance(object_name, ASTNode): | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(object_name) | |
| # AUTO: Runs when previous condition did not pass. | |
| else: | |
| # AUTO: Sets `self.add_child(ASTNode("Object", object_name, line`. | |
| self.add_child(ASTNode("Object", object_name, line=line)) | |
| # AUTO: Sets `self.add_child(ASTNode("Member", member_name, line`. | |
| self.add_child(ASTNode("Member", member_name, line=line)) | |
| # AUTO: Defines class `ArrayMemberAccessNode`. | |
| class ArrayMemberAccessNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, list_access_node, member_name, line=None): | |
| # AUTO: Sets `super().__init__("ArrayMemberAccess", line`. | |
| super().__init__("ArrayMemberAccess", line=line) | |
| # AUTO: Calls `self.add_child`. | |
| self.add_child(list_access_node) | |
| # AUTO: Sets `self.add_child(ASTNode("Member", member_name, line`. | |
| self.add_child(ASTNode("Member", member_name, line=line)) | |
| # AUTO: Defines class `BundleDefinitionNode`. | |
| class BundleDefinitionNode(ASTNode): | |
| # AUTO: Defines function `__init__`. | |
| def __init__(self, bundle_name, members, line=None): | |
| # AUTO: Sets `super().__init__("BundleDefinition", line`. | |
| super().__init__("BundleDefinition", line=line) | |
| # AUTO: Sets `self.bundle_name`. | |
| self.bundle_name = bundle_name | |
| # AUTO: Sets `self.members`. | |
| self.members = members | |