GAL / Backend /parser /builder.py
Clarkoer's picture
add comments ehehe
e6b283b
"""AST builder and early semantic checks for GAL.
parser.py proves token order first. This file then builds AST nodes, fills the
symbol table, and catches declaration/type errors that need context.
"""
# AUTO: Imports a module used by this file.
import copy
# AUTO: Imports a module used by this file.
import re
# AUTO: Imports names from another module.
from shared.tokens import * # noqa: F401,F403 - TT_* constants used throughout parse_*
# AUTO: Imports names from another module.
from semantic.errors import SemanticError
# AUTO: Imports names from another module.
from shared.ast_nodes import * # noqa: F401,F403 - all AST node classes
# AUTO: Imports names from another module.
from semantic.symbol_table import SymbolTable
# AUTO: Defines class `SemanticAnalyzer`.
class SemanticAnalyzer:
# AUTO: Defines function `__init__`.
def __init__(self, symbol_table):
# AUTO: Sets `self.symbol_table`.
self.symbol_table = symbol_table
# AUTO: Sets `self.visited_nodes`.
self.visited_nodes = set()
# AUTO: Sets `symbol_table`.
symbol_table = SymbolTable()
# AUTO: Sets `semantic_analyzer`.
semantic_analyzer = SemanticAnalyzer(symbol_table)
# AUTO: Sets `context_stack`.
context_stack = []
# AUTO: Defines function `build_ast`.
def build_ast(tokens):
# GUIDE: Entry point after syntax success; reset compiler state, then build the
# ProgramNode from globals, pollinate functions, and root().
# root is the top AST node. Every global declaration/function/root becomes
# a child of this ProgramNode.
# LINE: Create the main AST container that will hold the whole program.
root = ProgramNode()
# Reset symbol table state so each compile/run starts clean.
# LINE: Clear global variables from any previous run.
symbol_table.variables = {}
# LINE: Clear stored functions from any previous run.
symbol_table.functions = {}
# LINE: Reset scope stack to one global scope.
symbol_table.scopes = [{}]
# LINE: Clear per-function variable records.
symbol_table.function_variables = {}
# LINE: Clear bundle/struct type definitions.
symbol_table.bundle_types = {}
# LINE: Reset builder context tracking.
context_stack = []
# LINE: index points to the current token being converted to AST.
index = 0
# LINE: No function is active before parsing top-level code.
symbol_table.current_func_name = None
# LINE: Walk through all tokens until EOF/global parsing is finished.
while index < len(tokens):
# token is the current token being converted into an AST construct.
# index moves forward as each parse_* helper consumes tokens.
# LINE: Read the current token at this index.
token = tokens[index]
# LINE: Ignore extra semicolons at global level.
if token.type == ";":
# AUTO: Adds into `index`.
index += 1
# AUTO: Skips to the next loop iteration.
continue
# LINE: Top-level data type means global variable declaration.
if tokens[index].value in {"seed", "tree", "vine", "leaf", "branch"}:
# Global variable declaration such as: seed x = 10;
# LINE: Save declared type, such as seed or vine.
id_type = token.value
# LINE: Move to the variable name token.
index += 1
# LINE: Variable declaration must be followed by an identifier.
if tokens[index].type != "id":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid variable declaration.", token.line)
# LINE: Save the variable name.
id_name = tokens[index].value
# LINE: Move after the identifier before parsing initializer/array tail.
index += 1
# LINE: Build a VariableDeclarationNode and update index to the next token.
node, index = parse_variable(tokens, index, id_name, id_type)
# LINE: Add the global declaration node under ProgramNode.
if node:
# AUTO: Calls `root.add_child`.
root.add_child(node)
# LINE: empty starts an empty-return function declaration.
elif tokens[index].value == "empty":
# Empty-return function declaration without pollinate prefix.
# AUTO: Adds into `index`.
index += 1
# LINE: Function name must come after empty.
if tokens[index].type == "id":
# AUTO: Sets `func_name`.
func_name = tokens[index].value
# AUTO: Sets `func_type`.
func_type = "empty"
# LINE: Build the function AST node.
node, index = parse_function(tokens, index, func_name, func_type)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid function declaration.", tokens[index].line)
# LINE: Store function declaration under ProgramNode.
if node:
# AUTO: Calls `root.add_child`.
root.add_child(node)
# LINE: pollinate starts a typed function declaration.
elif tokens[index].value in {"pollinate"}:
# Function declaration such as: pollinate seed add(seed a, seed b) { ... }
# LINE: Move from pollinate to the return type.
index += 1
# LINE: Built-in return type path.
if tokens[index].value in {"seed", "tree", "vine", "leaf", "branch", "empty"}:
# AUTO: Sets `id_type`.
id_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# LINE: Function return type must be followed by function name.
if tokens[index].type != "id":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid function declaration.", tokens[index].line)
# AUTO: Sets `id_name`.
id_name = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# LINE: Parse parameters and function body.
node, index = parse_function(tokens, index, id_name, id_type)
# LINE: Store function declaration under ProgramNode.
if node:
# AUTO: Calls `root.add_child`.
root.add_child(node)
# LINE: Bundle return type path, like pollinate Student make().
elif tokens[index].type == "id" and tokens[index].value in symbol_table.bundle_types:
# AUTO: Sets `id_type`.
id_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "id":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid function declaration.", tokens[index].line)
# AUTO: Sets `id_name`.
id_name = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `node, index`.
node, index = parse_function(tokens, index, id_name, id_type)
# AUTO: Checks this condition.
if node:
# AUTO: Calls `root.add_child`.
root.add_child(node)
# AUTO: Runs when previous condition did not pass.
else:
# LINE: pollinate must be followed by a valid return type.
raise SemanticError(f"Semantic Error: Expected data type for function declaration after 'pollinate'.", tokens[index].line)
# LINE: fertile starts a constant declaration.
elif token.value == "fertile":
# Constant/global fertile declaration.
# AUTO: Sets `node, index`.
node, index = parse_fertile(tokens, index)
# AUTO: Checks this condition.
if node:
# AUTO: Calls `root.add_child`.
root.add_child(node)
# LINE: Raw identifier at global level is invalid unless handled by another rule.
elif token.value == "identifier":
# AUTO: Checks this condition.
if isinstance(symbol_table.lookup_variable(token.value), str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{token.value}' used before declaration.", token.line)
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid global statement.", token.line)
# LINE: root token starts the required main function.
elif token.value in {"root"}:
# AUTO: Sets `func_name`.
func_name = token.value
# AUTO: Sets `func_type`.
func_type = "empty"
# LINE: Build root as an empty-return FunctionDeclarationNode.
node, index = parse_function(tokens, index, func_name, func_type)
# AUTO: Checks this condition.
if node:
# AUTO: Calls `root.add_child`.
root.add_child(node)
# LINE: bundle either defines a bundle type or declares a bundle variable.
elif token.value == "bundle":
# AUTO: Sets `bundle_name`.
bundle_name = tokens[index + 1].value
# AUTO: Adds into `index`.
index += 2
# LINE: bundle Name { ... } defines a new bundle type.
if tokens[index].type == "{":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `members`.
members = {}
# LINE: Collect bundle members until closing brace.
while tokens[index].type != "}":
# AUTO: Checks this condition.
if tokens[index].value in {"seed", "tree", "vine", "leaf", "branch"}:
# AUTO: Sets `member_type`.
member_type = tokens[index].value
# AUTO: Sets `member_name`.
member_name = tokens[index + 1].value
# AUTO: Checks this condition.
if member_name in members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Duplicate member '{member_name}' in bundle '{bundle_name}'.", tokens[index].line)
# AUTO: Sets `members[member_name]`.
members[member_name] = member_type
# AUTO: Adds into `index`.
index += 2
# AUTO: Checks this condition.
if tokens[index].type == ";":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id" and tokens[index].value in symbol_table.bundle_types:
# AUTO: Sets `member_type`.
member_type = tokens[index].value
# AUTO: Sets `member_name`.
member_name = tokens[index + 1].value
# AUTO: Checks this condition.
if member_name in members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Duplicate member '{member_name}' in bundle '{bundle_name}'.", tokens[index].line)
# AUTO: Sets `members[member_name]`.
members[member_name] = member_type
# AUTO: Adds into `index`.
index += 2
# AUTO: Checks this condition.
if tokens[index].type == ";":
# AUTO: Adds into `index`.
index += 1
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid member type '{tokens[index].value}' in bundle definition.", tokens[index].line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if bundle_name in symbol_table.bundle_types:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{bundle_name}' already defined.", token.line)
# LINE: Save the bundle type into the symbol table.
symbol_table.bundle_types[bundle_name] = members
# LINE: Create AST node for the bundle definition.
node = BundleDefinitionNode(bundle_name, members, line=token.line)
# AUTO: Calls `root.add_child`.
root.add_child(node)
# AUTO: Runs when previous condition did not pass.
else:
# LINE: bundle Name var; declares a variable of bundle type Name.
var_name = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if bundle_name not in symbol_table.bundle_types:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{bundle_name}' is not defined.", token.line)
# AUTO: Sets `members`.
members = symbol_table.bundle_types[bundle_name]
# AUTO: Sets `_defaults`.
_defaults = {"seed": 0, "tree": 0.0, "leaf": '', "vine": "", "branch": False}
# AUTO: Sets `bundle_value`.
bundle_value = {name: _defaults.get(typ, None) for name, typ in members.items()}
# AUTO: Sets `error`.
error = symbol_table.declare_variable(var_name, bundle_name, value=bundle_value)
# AUTO: Checks this condition.
if isinstance(error, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, token.line)
# AUTO: Sets `node`.
node = VariableDeclarationNode(bundle_name, var_name, line=token.line)
# AUTO: Calls `root.add_child`.
root.add_child(node)
# AUTO: Runs when previous condition did not pass.
else:
# LINE: Any other top-level token is invalid except EOF.
if token.type not in {"EOF"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid token '{token.value}' used in global statement.", token.line)
# AUTO: Stops the nearest loop.
break
# LINE: Return the complete ProgramNode AST to parser.py.
return root
# AUTO: Defines function `parse_functionOrVariable`.
def parse_functionOrVariable(tokens, index):
# AUTO: Sets `id_type`.
id_type = tokens[index].value
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if tokens[index + 1].type == "id" or tokens[index + 1].value == "root":
# AUTO: Sets `id_name`.
id_name = tokens[index + 1].value
# AUTO: Adds into `index`.
index += 2
# AUTO: Checks this condition.
if tokens[index].type == "(":
# AUTO: Sets `node, index`.
node, index = parse_function(tokens, index, id_name, id_type)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "=":
# AUTO: Sets `node, index`.
node, index = parse_variable(tokens, index, id_name, id_type)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `error`.
error = f"Semantic Error: Invalid function or variable declaration."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Sets `node.line`.
node.line = line
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Returns this result to the caller.
return None, index
# AUTO: Defines function `parse_function`.
def parse_function(tokens, index, func_name, func_type):
# LINE: Start parsing a function/root declaration from the current index.
line = tokens[index].line
# LINE: Remember which function is being built for declaration checks.
symbol_table.current_func_name = func_name
# LINE: Function names cannot be duplicated.
if func_name in symbol_table.functions:
# AUTO: Sets `error`.
error = f"Semantic Error: '{func_name}' already declared."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, tokens[index].line)
# AUTO: Checks the next alternate condition.
elif func_name in symbol_table.variables:
# AUTO: Sets `error`.
error = f"Semantic Error: '{func_name}' already declared."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, tokens[index].line)
# LINE: root has special rules: no parameters and empty return.
if func_name in {"root"}:
# LINE: Create a new function scope for declarations inside root.
symbol_table.enter_scope()
# AUTO: Adds into `index`.
index += 1
# LINE: root must have parentheses.
if tokens[index].type == "(":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: {func_name}() should not have parameters.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks the next alternate condition.
elif func_name == "root":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Semantic Error: Missing () for root function declaration.", line)
# LINE: root has an empty parameter container.
params_node = ASTNode("Parameters")
# LINE: Create FunctionDeclarationNode for root.
func_node = FunctionDeclarationNode(func_type, func_name, params_node)
# AUTO: Runs when previous condition did not pass.
else:
# LINE: Normal pollinate functions must start with '(' after the name.
if tokens[index].type != "(":
# AUTO: Sets `error`.
error = f"Semantic Error: Missing () for function declaration."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Sets `params_node`.
params_node = ASTNode("Parameters")
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Calls `symbol_table.enter_scope`.
symbol_table.enter_scope()
# AUTO: Repeats while this condition is true.
while tokens[index].type != ")":
# AUTO: Checks this condition.
if tokens[index].value in {"seed", "tree", "vine", "leaf", "branch", "empty"}:
# AUTO: Sets `param_type`.
param_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `param_name`.
param_name = tokens[index].value
# AUTO: Sets `param_node`.
param_node = ASTNode("Parameter")
# AUTO: Calls `param_node.add_child`.
param_node.add_child(ASTNode("Type", param_type))
# AUTO: Calls `param_node.add_child`.
param_node.add_child(ASTNode("Identifier", param_name))
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `is_list`.
is_list = False
# AUTO: Checks this condition.
if tokens[index].type == "[":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected ']' after '[' in array parameter.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `is_list`.
is_list = True
# AUTO: Calls `param_node.add_child`.
param_node.add_child(ASTNode("ArrayParam", "true"))
# AUTO: Calls `params_node.add_child`.
params_node.add_child(param_node)
# AUTO: Sets `error`.
error = symbol_table.declare_variable(param_name, param_type, is_list=is_list)
# AUTO: Checks this condition.
if error:
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `error`.
error = f"Semantic Error: Invalid parameter declaration."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id" and tokens[index].value in symbol_table.bundle_types:
# AUTO: Sets `param_type`.
param_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `param_name`.
param_name = tokens[index].value
# AUTO: Sets `param_node`.
param_node = ASTNode("Parameter")
# AUTO: Calls `param_node.add_child`.
param_node.add_child(ASTNode("Type", param_type))
# AUTO: Calls `param_node.add_child`.
param_node.add_child(ASTNode("Identifier", param_name))
# AUTO: Calls `params_node.add_child`.
params_node.add_child(param_node)
# AUTO: Sets `error`.
error = symbol_table.declare_variable(param_name, param_type)
# AUTO: Checks this condition.
if error:
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `error`.
error = f"Semantic Error: Invalid parameter declaration."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.declare_function`.
symbol_table.declare_function(func_name, func_type, params_node.children)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `func_node`.
func_node = FunctionDeclarationNode(func_type, func_name, params_node)
# AUTO: Checks this condition.
if tokens[index].type == "{":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `block_node`.
block_node = ASTNode("Block")
# AUTO: Repeats while this condition is true.
while tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `block_node.add_child`.
block_node.add_child(stmt)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `func_node.add_child`.
func_node.add_child(block_node)
# AUTO: Calls `symbol_table.exit_scope`.
symbol_table.exit_scope()
# AUTO: Sets `symbol_table.current_func_name`.
symbol_table.current_func_name = None
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `error`.
error = f"Semantic Error: Function body must be enclosed in curly braces."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Returns this result to the caller.
return func_node, index
# AUTO: Defines function `parse_variable`.
def parse_variable(tokens, index, var_name, var_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Sets `var_nodes`.
var_nodes = []
# AUTO: Repeats while this condition is true.
while True:
# AUTO: Sets `global_var`.
global_var = symbol_table.variables.get(var_name)
# AUTO: Checks this condition.
if global_var and global_var.get("is_fertile"):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' is declared as fertile and cannot be re-declared.", line)
# AUTO: Sets `is_list`.
is_list = False
# AUTO: Sets `var_node`.
var_node = VariableDeclarationNode(var_type, var_name, line=line)
# AUTO: Checks this condition.
if tokens[index].type == "=":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "[":
# AUTO: Sets `is_list`.
is_list = True
# AUTO: Sets `value_node, index`.
value_node, index = parse_list(tokens, index, var_type)
# AUTO: Calls `var_node.add_child`.
var_node.add_child(value_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].value == "water":
# AUTO: Sets `water_line`.
water_line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected '(' after water.", water_line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `water_type`.
water_type = None
# AUTO: Checks this condition.
if tokens[index].value in {"seed", "tree", "leaf", "branch", "vine"}:
# AUTO: Sets `water_type`.
water_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: water() accepts only an optional type parameter (e.g., water(seed)).", water_line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if water_type and not _types_compatible(var_type, water_type):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Type mismatch — cannot assign water({water_type}) to '{var_type}' variable.", water_line)
# AUTO: Sets `value_node`.
value_node = ASTNode("Input", f"water({water_type})" if water_type else "water()", line=water_line)
# AUTO: Calls `var_node.add_child`.
var_node.add_child(value_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `value_node, index`.
value_node, index = parse_expression_type(tokens, index, var_type)
# AUTO: Calls `var_node.add_child`.
var_node.add_child(value_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "[":
# AUTO: Sets `is_list`.
is_list = True
# AUTO: Sets `dimensions`.
dimensions = []
# AUTO: Repeats while this condition is true.
while tokens[index].type == "[":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `dim_size`.
dim_size = 0
# AUTO: Checks this condition.
if tokens[index].type == "dblit":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Array size must be of type 'seed' (integer), got 'tree' (float) '{tokens[index].value}'.", line)
# AUTO: Checks this condition.
if tokens[index].type == "intlit":
# AUTO: Sets `dim_size`.
dim_size = int(tokens[index].value)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ']' after list size.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Appends a value to a list.
dimensions.append(dim_size)
# AUTO: Sets `default_literals`.
default_literals = {"seed": "0", "tree": "0.0", "leaf": "''", "vine": '""', "branch": "false"}
# AUTO: Defines function `build_list_node`.
def build_list_node(dims):
# AUTO: Sets `node`.
node = ASTNode("List", line=line)
# AUTO: Checks this condition.
if len(dims) == 1:
# AUTO: Starts a loop over these values.
for _ in range(dims[0]):
# AUTO: Sets `node.add_child(ASTNode("Value", default_literals.get(var_type, "0"), line`.
node.add_child(ASTNode("Value", default_literals.get(var_type, "0"), line=line))
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Starts a loop over these values.
for _ in range(dims[0]):
# AUTO: Calls `node.add_child`.
node.add_child(build_list_node(dims[1:]))
# AUTO: Returns this result to the caller.
return node
# AUTO: Sets `list_node`.
list_node = build_list_node(dimensions)
# AUTO: Calls `var_node.add_child`.
var_node.add_child(list_node)
# AUTO: Checks this condition.
if tokens[index].type == "=":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "{":
# AUTO: Defines function `parse_init_braces`.
def parse_init_braces(idx):
# AUTO: Checks this condition.
if tokens[idx].type != "{":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '{{' in array initialization.", tokens[idx].line)
# AUTO: Adds into `idx`.
idx += 1
# AUTO: Sets `items`.
items = []
# AUTO: Repeats while this condition is true.
while tokens[idx].type != "}":
# AUTO: Checks this condition.
if tokens[idx].type == "{":
# AUTO: Sets `inner_node, idx`.
inner_node, idx = parse_init_braces(idx)
# AUTO: Appends a value to a list.
items.append(inner_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `expr, idx`.
expr, idx = parse_expression_type(tokens, idx, var_type)
# AUTO: Appends a value to a list.
items.append(expr)
# AUTO: Checks this condition.
if tokens[idx].type == ",":
# AUTO: Adds into `idx`.
idx += 1
# AUTO: Adds into `idx`.
idx += 1
# AUTO: Returns this result to the caller.
return ListNode(elements=items, line=line), idx
# AUTO: Sets `value_node, index`.
value_node, index = parse_init_braces(index)
# AUTO: Sets `var_node.children[-1]`.
var_node.children[-1] = value_node
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '{{' after '=' in array initialization.", line)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Does nothing for this required block.
pass
# AUTO: Sets `error`.
error = symbol_table.declare_variable(var_name, var_type, is_list = is_list)
# AUTO: Checks this condition.
if isinstance(error, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Appends a value to a list.
var_nodes.append(var_node)
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops the nearest loop.
break
# AUTO: Checks this condition.
if len(var_nodes) == 1:
# AUTO: Returns this result to the caller.
return var_nodes[0], index
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `var_list_node`.
var_list_node = ASTNode("VariableDeclarationList")
# AUTO: Starts a loop over these values.
for node in var_nodes:
# AUTO: Calls `var_list_node.add_child`.
var_list_node.add_child(node)
# AUTO: Returns this result to the caller.
return var_list_node, index
# AUTO: Defines function `_skip_semicolons`.
def _skip_semicolons(tokens, index):
# AUTO: Repeats while this condition is true.
while index < len(tokens) and tokens[index].type == ";":
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return index
# AUTO: Defines function `parse_statement`.
def parse_statement(tokens, index, func_type = None):
# GUIDE: Central dispatcher for executable statements inside function/root blocks.
# It routes by the current token: assignment, plant/water, loop, branch, etc.
# This function receives the current token index and returns:
# (AST node for that statement, next token index after the statement).
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Checks this condition.
if token.type == ";":
# Empty statement; consume only the semicolon.
# AUTO: Returns this result to the caller.
return None, index + 1
# AUTO: Sets `line`.
line = token.line
# AUTO: Checks this condition.
if token.value in {"seed", "tree", "vine", "leaf", "branch"}:
# Local variable declaration such as: seed count = 0;
# AUTO: Sets `var_type`.
var_type = token.value
# AUTO: Sets `var_name`.
var_name = tokens[index + 1].value
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `node, index`.
node, index = parse_variable(tokens, index, var_name, var_type)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value == "fertile":
# Constant declaration; parse_fertile also records it as non-reassignable.
# AUTO: Sets `node, index`.
node, index = parse_fertile(tokens, index)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value == "bundle":
# Bundle variable declaration such as: bundle Student s;
# AUTO: Sets `bundle_type_name`.
bundle_type_name = tokens[index + 1].value
# AUTO: Checks this condition.
if bundle_type_name not in symbol_table.bundle_types:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{bundle_type_name}' is not defined.", token.line)
# AUTO: Sets `var_name`.
var_name = tokens[index + 2].value
# AUTO: Adds into `index`.
index += 3
# AUTO: Sets `members`.
members = symbol_table.bundle_types[bundle_type_name]
# AUTO: Sets `_defaults`.
_defaults = {"seed": 0, "tree": 0.0, "leaf": '', "vine": "", "branch": False}
# AUTO: Checks this condition.
if tokens[index].type == "[":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "dblit":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Array size must be of type 'seed' (integer), got 'tree' (float) '{tokens[index].value}'.", token.line)
# AUTO: Sets `array_size`.
array_size = 0
# AUTO: Checks this condition.
if tokens[index].type == "intlit":
# AUTO: Sets `array_size`.
array_size = int(tokens[index].value)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ']' after array size.", token.line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `list_node`.
list_node = ASTNode("List", line=token.line)
# AUTO: Starts a loop over these values.
for _ in range(array_size):
# AUTO: Sets `bundle_val_node`.
bundle_val_node = ASTNode("BundleDefault", line=token.line)
# AUTO: Calls `list_node.add_child`.
list_node.add_child(bundle_val_node)
# AUTO: Sets `error`.
error = symbol_table.declare_variable(var_name, bundle_type_name, is_list=True)
# AUTO: Checks this condition.
if isinstance(error, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, token.line)
# AUTO: Sets `node`.
node = VariableDeclarationNode(bundle_type_name, var_name, line=token.line)
# AUTO: Calls `node.add_child`.
node.add_child(list_node)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `bundle_value`.
bundle_value = {name: _defaults.get(typ, None) for name, typ in members.items()}
# AUTO: Sets `error`.
error = symbol_table.declare_variable(var_name, bundle_type_name, value=bundle_value)
# AUTO: Checks this condition.
if isinstance(error, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, token.line)
# AUTO: Sets `node`.
node = VariableDeclarationNode(bundle_type_name, var_name, line=token.line)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.type == "id" and tokens[index + 1].type == "(":
# Identifier followed by '(' is a function call statement.
# AUTO: Checks this condition.
if tokens[index + 1].type == "(":
# AUTO: Sets `func_name`.
func_name = token.value
# AUTO: Sets `error`.
error = symbol_table.lookup_function(func_name)
# AUTO: Checks this condition.
if isinstance(error, str):
# AUTO: Sets `error`.
error = symbol_table.lookup_function(func_name)
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, token.line)
# AUTO: Sets `func_type`.
func_type = symbol_table.lookup_function(func_name)["return_type"] # type: ignore
# AUTO: Sets `func_params`.
func_params = symbol_table.lookup_function(func_name)["params"] # type: ignore
# AUTO: Sets `func_call_node, index`.
func_call_node, index = parse_function_call(tokens, index, func_name, func_type, func_params)
# AUTO: Returns this result to the caller.
return func_call_node, index
# AUTO: Checks the next alternate condition.
elif token.type == "id" or tokens[index].type in {"++", "--"}:
# Identifier-start statements include assignments, array/member access,
# function-like list operations, and prefix/postfix ++/--.
# AUTO: Sets `assignments_node`.
assignments_node = ASTNode("AssignmentList")
# AUTO: Repeats while this condition is true.
while True:
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(tokens[index].value)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, line)
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_type`.
var_type = var_info["type"]
# AUTO: Sets `is_list`.
is_list = var_info.get("is_list", False)
# AUTO: Sets `is_fertile`.
is_fertile = var_info.get("is_fertile", False)
# AUTO: Checks this condition.
if is_fertile:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' is declared as fertile and cannot be re-assigned a value.", line)
# AUTO: Checks this condition.
if is_list or (var_type == "vine" and tokens[index + 1].type == "["):
# AUTO: Checks this condition.
if tokens[index + 1].type == "=":
# AUTO: Sets `node, index`.
node, index = parse_list_assignment(tokens, index)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(node)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type == "[":
# AUTO: Sets `list_access_node, index`.
list_access_node, index = parse_list_access(tokens, index)
# AUTO: Checks this condition.
if tokens[index + 1].type == "." and var_type in symbol_table.bundle_types:
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `member_name`.
member_name = tokens[index].value
# AUTO: Sets `bundle_members`.
bundle_members = symbol_table.bundle_types[var_type]
# AUTO: Checks this condition.
if member_name not in bundle_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{var_type}' has no member '{member_name}'.", line)
# AUTO: Sets `member_type`.
member_type = bundle_members[member_name]
# AUTO: Sets `target`.
target = ArrayMemberAccessNode(list_access_node, member_name, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Repeats while this condition is true.
while tokens[index].type == "." and member_type in symbol_table.bundle_types:
# AUTO: Sets `next_member`.
next_member = tokens[index + 1].value
# AUTO: Sets `nested_members`.
nested_members = symbol_table.bundle_types[member_type]
# AUTO: Checks this condition.
if next_member not in nested_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{member_type}' has no member '{next_member}'.", line)
# AUTO: Sets `member_type`.
member_type = nested_members[next_member]
# AUTO: Sets `target`.
target = MemberAccessNode(target, next_member, line=line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Checks this condition.
if tokens[index].type == "=":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `value_node, index`.
value_node, index = parse_expression_type(tokens, index, member_type)
# AUTO: Sets `assign_node`.
assign_node = AssignmentNode(target, value_node, line=line)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(assign_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"+=", "-=", "*=", "/=", "%=", "**="}:
# AUTO: Checks this condition.
if member_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Compound assignment '{tokens[index].value}' is not valid for '{member_type}' member '{member_name}'.", line)
# AUTO: Sets `compound_op`.
compound_op = tokens[index].value
# AUTO: Sets `base_op`.
base_op = compound_op[:-1]
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `rhs_node, index`.
rhs_node, index = parse_expression_type(tokens, index, member_type)
# AUTO: Sets `value_node`.
value_node = BinaryOpNode(target, base_op, rhs_node, line=line)
# AUTO: Sets `assign_node`.
assign_node = AssignmentNode(target, value_node, line=line)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(assign_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected '=' after '{var_name}[...].{member_name}'.", line)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type == "=":
# AUTO: Adds into `index`.
index += 2
# AUTO: Checks this condition.
if tokens[index].value == "water":
# AUTO: Sets `water_line`.
water_line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after water.", water_line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `water_type`.
water_type = None
# AUTO: Checks this condition.
if tokens[index].value in {"seed", "tree", "leaf", "branch", "vine"}:
# AUTO: Sets `water_type`.
water_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: water() accepts only an optional type parameter.", water_line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if water_type and not _types_compatible(var_type, water_type):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Type mismatch — cannot assign water({water_type}) to '{var_type}' list element.", water_line)
# AUTO: Sets `value_node`.
value_node = ASTNode("Input", f"water({water_type})" if water_type else "water()", line=water_line)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `value_node, index`.
value_node, index = parse_expression_type(tokens, index, var_type)
# AUTO: Sets `assign_node`.
assign_node = AssignmentNode(list_access_node, value_node, line=tokens[index].line)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(assign_node)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type in {"++", "--"}:
# AUTO: Checks this condition.
if var_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{var_name}' of type {var_type} in expression.", line)
# AUTO: Sets `operator`.
operator = tokens[index + 1].value
# AUTO: Sets `unary_node`.
unary_node = UnaryOpNode(operator, list_access_node, "post", line=line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(unary_node)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type == ".":
# AUTO: Sets `obj_name`.
obj_name = tokens[index].value
# AUTO: Sets `member_name`.
member_name = tokens[index + 2].value
# AUTO: Checks this condition.
if var_type not in symbol_table.bundle_types:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{obj_name}' is not a bundle type.", line)
# AUTO: Sets `bundle_members`.
bundle_members = symbol_table.bundle_types[var_type]
# AUTO: Checks this condition.
if member_name not in bundle_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{var_type}' has no member '{member_name}'.", line)
# AUTO: Sets `member_type`.
member_type = bundle_members[member_name]
# AUTO: Adds into `index`.
index += 3
# AUTO: Sets `target`.
target = MemberAccessNode(obj_name, member_name, line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type == "." and member_type in symbol_table.bundle_types:
# AUTO: Sets `next_member`.
next_member = tokens[index + 1].value
# AUTO: Sets `nested_members`.
nested_members = symbol_table.bundle_types[member_type]
# AUTO: Checks this condition.
if next_member not in nested_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{member_type}' has no member '{next_member}'.", line)
# AUTO: Sets `member_type`.
member_type = nested_members[next_member]
# AUTO: Sets `target`.
target = MemberAccessNode(target, next_member, line=line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Checks this condition.
if tokens[index].type == "=":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].value == "water":
# AUTO: Sets `water_line`.
water_line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after water.", water_line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `water_type`.
water_type = None
# AUTO: Checks this condition.
if tokens[index].value in {"seed", "tree", "leaf", "branch", "vine"}:
# AUTO: Sets `water_type`.
water_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: water() accepts only an optional type parameter (e.g., water(seed)).", water_line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if water_type and not _types_compatible(member_type, water_type):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Type mismatch — cannot assign water({water_type}) to '{member_type}' member '{member_name}'.", water_line)
# AUTO: Sets `value_node`.
value_node = ASTNode("Input", f"water({water_type})" if water_type else "water()", line=water_line)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `value_node, index`.
value_node, index = parse_expression_type(tokens, index, member_type)
# AUTO: Sets `assign_node`.
assign_node = AssignmentNode(target, value_node, line=line)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(assign_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"+=", "-=", "*=", "/=", "%=", "**="}:
# AUTO: Checks this condition.
if member_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Compound assignment '{tokens[index].value}' is not valid for '{member_type}' member '{member_name}'.", line)
# AUTO: Sets `compound_op`.
compound_op = tokens[index].value
# AUTO: Sets `base_op`.
base_op = compound_op[:-1]
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `rhs_node, index`.
rhs_node, index = parse_expression_type(tokens, index, member_type)
# AUTO: Sets `value_node`.
value_node = BinaryOpNode(target, base_op, rhs_node, line=line)
# AUTO: Sets `assign_node`.
assign_node = AssignmentNode(target, value_node, line=line)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(assign_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"++", "--"}:
# AUTO: Checks this condition.
if member_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot apply '{tokens[index].value}' to member '{member_name}' of type '{member_type}'.", line)
# AUTO: Sets `operator`.
operator = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, target, "post", line`.
assignments_node.add_child(UnaryOpNode(operator, target, "post", line=line))
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected '=' after '{obj_name}.{member_name}'.", line)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type == "=":
# AUTO: Sets `var_name`.
var_name = token.value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, token.line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `node, index`.
node, index = parse_assignment(tokens, index, token.value, var_info["type"])
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(node)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type in {"++", "--"}:
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(tokens[index].value)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, line)
# AUTO: Checks this condition.
if var_info["type"] not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{token.value}' of type {var_info['type']} in expression.", line)
# AUTO: Sets `operand`.
operand = ASTNode("Identifier", token.value, line=line)
# AUTO: Sets `operator`.
operator = tokens[index + 1].value
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, operand, "post", line`.
assignments_node.add_child(UnaryOpNode(operator, operand, "post", line=line))
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type in {"+=", "-=", "*=", "/=", "%=", "**="}:
# AUTO: Sets `compound_op`.
compound_op = tokens[index + 1].value
# AUTO: Sets `base_op`.
base_op = compound_op[:-1]
# AUTO: Sets `cur_var_name`.
cur_var_name = tokens[index].value
# AUTO: Sets `cur_var_info`.
cur_var_info = symbol_table.lookup_variable(cur_var_name)
# AUTO: Checks this condition.
if isinstance(cur_var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(cur_var_info, line)
# AUTO: Checks this condition.
if cur_var_info.get("is_fertile", False):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{cur_var_name}' is declared as fertile and cannot be re-assigned a value.", line)
# AUTO: Sets `cur_var_type`.
cur_var_type = cur_var_info["type"]
# AUTO: Checks this condition.
if cur_var_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use compound assignment on '{cur_var_name}' of type '{cur_var_type}'.", line)
# AUTO: Checks this condition.
if base_op == "%" and cur_var_type != "seed":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Modulo operator '%' requires 'seed' (integer) operands, "
# AUTO: Executes this statement.
f"but '{cur_var_name}' is of type 'tree'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `rhs_node, index, rhs_type`.
rhs_node, index, rhs_type = parse_expression(tokens, index)
# AUTO: Checks this condition.
if rhs_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Sets `f"Semantic Error: Cannot use '{base_op}`.
f"Semantic Error: Cannot use '{base_op}=' with right-hand side of type '{rhs_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `lhs_node`.
lhs_node = ASTNode("Identifier", cur_var_name, line=line)
# AUTO: Sets `value_node`.
value_node = BinaryOpNode(lhs_node, base_op, rhs_node, line=line)
# AUTO: Sets `assign_node`.
assign_node = AssignmentNode(cur_var_name, value_node, line=line)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(assign_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Unexpected token '{tokens[index].value}' in statement.", line)
# AUTO: Checks the next alternate condition.
elif tokens[index].value in {"++", "--"}:
# AUTO: Sets `operator`.
operator = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' used before declaration.", line)
# AUTO: Checks this condition.
if tokens[index + 1].type == "[":
# AUTO: Checks this condition.
if var_info["type"] not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{var_name}' of type {var_info['type']} in expression.", line)
# AUTO: Sets `list_access_node, index`.
list_access_node, index = parse_list_access(tokens, index)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, list_access_node, "pre", line`.
assignments_node.add_child(UnaryOpNode(operator, list_access_node, "pre", line=line))
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type == ".":
# AUTO: Sets `obj_name`.
obj_name = tokens[index].value
# AUTO: Checks this condition.
if var_info["type"] not in symbol_table.bundle_types:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{obj_name}' is not a bundle type.", line)
# AUTO: Sets `member_name`.
member_name = tokens[index + 2].value
# AUTO: Sets `bundle_members`.
bundle_members = symbol_table.bundle_types[var_info["type"]]
# AUTO: Checks this condition.
if member_name not in bundle_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{var_info['type']}' has no member '{member_name}'.", line)
# AUTO: Sets `member_type`.
member_type = bundle_members[member_name]
# AUTO: Adds into `index`.
index += 3
# AUTO: Sets `target`.
target = MemberAccessNode(obj_name, member_name, line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type == "." and member_type in symbol_table.bundle_types:
# AUTO: Sets `next_member`.
next_member = tokens[index + 1].value
# AUTO: Sets `nested_members`.
nested_members = symbol_table.bundle_types[member_type]
# AUTO: Checks this condition.
if next_member not in nested_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{member_type}' has no member '{next_member}'.", line)
# AUTO: Sets `member_type`.
member_type = nested_members[next_member]
# AUTO: Sets `target`.
target = MemberAccessNode(target, next_member, line=line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Checks this condition.
if member_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot apply '{operator}' to member '{member_name}' of type '{member_type}'.", line)
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, target, "pre", line`.
assignments_node.add_child(UnaryOpNode(operator, target, "pre", line=line))
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Checks this condition.
if var_info["type"] not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{var_name}' of type {var_info['type']} in expression.", line)
# AUTO: Sets `operand`.
operand = ASTNode("Identifier", tokens[index].value, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, operand, "pre", line`.
assignments_node.add_child(UnaryOpNode(operator, operand, "pre", line=line))
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected identifier after '{operator}'.", line)
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Skips to the next loop iteration.
continue
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops the nearest loop.
break
# AUTO: Checks this condition.
if len(assignments_node.children) > 1:
# AUTO: Returns this result to the caller.
return assignments_node, index
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Returns this result to the caller.
return assignments_node.children[0], index
# AUTO: Checks the next alternate condition.
elif token.value in {"plant"}:
# plant(...) output statement.
# AUTO: Sets `node, index`.
node, index = parse_print(tokens, index)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value == "water":
# water(...) input statement.
# AUTO: Sets `node, index`.
node, index = parse_water_statement(tokens, index)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value == "spring":
# spring/bud/wither conditional chain.
# AUTO: Sets `node, index`.
node, index = parse_if(tokens, index, func_type)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value in {"reclaim"}:
# reclaim; or reclaim expression; returns from the current function.
# AUTO: Sets `node, index`.
node, index = parse_return(tokens, index, func_type)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value == "cultivate":
# cultivate loop.
# AUTO: Sets `node, index`.
node, index = parse_for(tokens, index, func_type)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value in {"grow"}:
# grow while-loop.
# AUTO: Sets `node, index`.
node, index = parse_while(tokens, index, func_type)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value in {"tend"}:
# tend do-while loop.
# AUTO: Sets `node, index`.
node, index = parse_do(tokens, index, func_type)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value in {"harvest"}:
# harvest switch-like statement.
# AUTO: Sets `node, index`.
node, index = parse_switch(tokens, index, func_type)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value in {"prune"}:
# AUTO: Checks this condition.
if not is_inside_loop_or_switch_stack():
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: 'prune' statement used outside a loop or switch statement.", line)
# AUTO: Sets `node`.
node = BreakNode(line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value in {"skip"}:
# AUTO: Checks this condition.
if not is_inside_loop_or_switch_stack():
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: 'skip' statement used outside a loop or switch statement.", line)
# AUTO: Sets `node`.
node = ContinueNode(line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif token.value in {"variety", "soil"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: '{token.value}' statement used outside a 'harvest' block.", line)
# AUTO: Checks the next alternate condition.
elif token.type == "{":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `block_node`.
block_node = ASTNode("Block", line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `block_node.add_child`.
block_node.add_child(stmt)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return block_node, index
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Unexpected token '{token.value}' in statement.", line)
# AUTO: Defines function `parse_list_access`.
def parse_list_access(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Sets `list_name`.
list_name = tokens[index].value
# AUTO: Sets `list_info`.
list_info = symbol_table.lookup_variable(list_name)
# AUTO: Checks this condition.
if isinstance(list_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(list_info, line)
# AUTO: Checks this condition.
if not list_info.get("is_list", False) and list_info.get("type") != "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{list_name}' is not a list.", line)
# AUTO: Sets `list_type`.
list_type = list_info["type"]
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `index_node, index, idx_type`.
index_node, index, idx_type = parse_equality(tokens, index)
# AUTO: Checks this condition.
if idx_type is not None and idx_type != "seed":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: List index must be of type 'seed', got '{idx_type}'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ']' after list index.", line)
# AUTO: Sets `index_wrapper`.
index_wrapper = ASTNode("Index", line=line)
# AUTO: Calls `index_wrapper.add_child`.
index_wrapper.add_child(index_node)
# AUTO: Sets `node`.
node = ListAccessNode(list_name, index_wrapper, line=line)
# AUTO: Repeats while this condition is true.
while tokens[index + 1].type == "[":
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `inner_index_node, index, inner_idx_type`.
inner_index_node, index, inner_idx_type = parse_equality(tokens, index)
# AUTO: Checks this condition.
if inner_idx_type is not None and inner_idx_type != "seed":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: List index must be of type 'seed', got '{inner_idx_type}'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ']' after list index.", line)
# AUTO: Sets `inner_wrapper`.
inner_wrapper = ASTNode("Index", line=line)
# AUTO: Calls `inner_wrapper.add_child`.
inner_wrapper.add_child(inner_index_node)
# AUTO: Sets `node`.
node = ListAccessNode(node, inner_wrapper, line=line)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Defines function `parse_list_assignment`.
def parse_list_assignment(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, line)
# AUTO: Checks this condition.
if not var_info.get("is_list", False):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: '{var_name}' is not a list.", line)
# AUTO: Sets `var_type`.
var_type = var_info["type"]
# AUTO: Adds into `index`.
index += 2
# AUTO: Checks this condition.
if var_info.get("is_fertile", False):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' is declared as fertile.", line)
# AUTO: Checks this condition.
if tokens[index].value == "append":
# AUTO: Sets `value_node, index`.
value_node, index = parse_append(tokens, index, var_name, var_type)
# AUTO: Checks the next alternate condition.
elif tokens[index].value == "insert":
# AUTO: Sets `value_node, index`.
value_node, index = parse_insert(tokens, index, var_name, var_type)
# AUTO: Checks the next alternate condition.
elif tokens[index].value == "remove":
# AUTO: Sets `value_node, index`.
value_node, index = parse_remove(tokens, index, var_name, var_type)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id":
# AUTO: Sets `source_var`.
source_var = tokens[index].value
# AUTO: Sets `source_info`.
source_info = symbol_table.lookup_variable(source_var)
# AUTO: Checks this condition.
if isinstance(source_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{source_var}' used before declaration.", line)
# AUTO: Checks this condition.
if source_info["is_list"] == False and tokens[index + 1].type != ".":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot assign non-list '{source_var}' to list '{var_name}'.", line)
# AUTO: Sets `source_type`.
source_type = source_info["type"]
# AUTO: Checks this condition.
if source_type == "leaf" and tokens[index + 1].type == ".":
# AUTO: Sets `member_name`.
member_name = tokens[index + 2].value
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Unsupported member access '{source_var}.{member_name}'.", line)
# AUTO: Checks this condition.
if source_type == "leaf" and tokens[index + 1].type != ".":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot assign non-list '{source_var}' to list '{var_name}'.", line)
# AUTO: Checks this condition.
if var_type != source_type:
# AUTO: Checks this condition.
if not (var_type in {"seed", "tree"} and source_type in {"seed", "tree"}):
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot assign list of '{source_type}' type to list of '{var_type}' type.", line
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `value_node`.
value_node = ASTNode("Value", source_var, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "[":
# AUTO: Sets `value_node, index`.
value_node, index = parse_list(tokens, index, var_type)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid list assignment.", line)
# AUTO: Returns this result to the caller.
return AssignmentNode(var_name, value_node, line=line), index
# AUTO: Defines function `_types_compatible`.
def _types_compatible(declared, inferred):
# AUTO: Checks this condition.
if declared == inferred:
# AUTO: Returns this result to the caller.
return True
# AUTO: Checks this condition.
if declared in {"seed", "tree"} and inferred in {"seed", "tree"}:
# AUTO: Returns this result to the caller.
return True
# AUTO: Returns this result to the caller.
return False
# AUTO: Defines function `parse_expression_type`.
def parse_expression_type(tokens, index, var_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if var_type not in {"seed", "tree", "vine", "leaf", "branch"} and var_type not in symbol_table.bundle_types:
# AUTO: Stops this flow by raising an error.
raise SemanticError("Semantic Error: Invalid type for assignment.", line)
# AUTO: Sets `node, index, expr_type`.
node, index, expr_type = parse_assignment_expression(tokens, index)
# AUTO: Checks this condition.
if expr_type is None:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
"Semantic Error: Could not determine the type of the expression.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if not _types_compatible(var_type, expr_type):
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Type mismatch — cannot assign '{expr_type}' value to '{var_type}' variable.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Defines function `parse_expression_vine`.
def parse_expression_vine(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Checks this condition.
if tokens[index].type == "id" and tokens[index + 1].type == "(":
# AUTO: Sets `func_name`.
func_name = tokens[index].value
# AUTO: Sets `func_info`.
func_info = symbol_table.lookup_function(func_name)
# AUTO: Sets `func_return_type`.
func_return_type = func_info["return_type"] # type: ignore
# AUTO: Sets `func_params`.
func_params = func_info["params"] # type: ignore
# AUTO: Checks this condition.
if func_return_type not in {"vine"}:
# AUTO: Sets `error`.
error = f"Semantic Error: Cannot use function '{func_name}' of type {func_return_type}. Expected valid vine value."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return parse_function_call(tokens, index, func_name, func_return_type, func_params)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id":
# AUTO: Sets `variable_info`.
variable_info = symbol_table.lookup_variable(tokens[index].value)
# AUTO: Checks this condition.
if isinstance(variable_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(variable_info, line)
# AUTO: Sets `is_list`.
is_list = variable_info.get("is_list", False)
# AUTO: Checks this condition.
if is_list and tokens[index + 1].type != "[":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{token.value}' must be indexed with '[]'.", line)
# AUTO: Checks this condition.
if variable_info["type"] != "vine":
# AUTO: Sets `error`.
error = f"Semantic Error: Cannot use '{tokens[index].value}' of type {variable_info['type']}. Expected valid vine value."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Sets `node`.
node = ASTNode("Value", tokens[index].value)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "stringlit":
# AUTO: Sets `node`.
node = ASTNode("Value", tokens[index].value)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return node, index
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `error`.
error = f"Semantic Error: Expected valid vine value."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Defines function `parse_expression_leaf`.
def parse_expression_leaf(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Checks this condition.
if tokens[index].type not in {"chrlit", "id", "stringlit"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected valid leaf value.", line)
# AUTO: Checks this condition.
if tokens[index].type == "id" and tokens[index + 1].type == "(":
# AUTO: Sets `func_name`.
func_name = tokens[index].value
# AUTO: Sets `func_info`.
func_info = symbol_table.lookup_function(func_name)
# AUTO: Checks this condition.
if isinstance(func_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{func_name}' is not defined.", line)
# AUTO: Sets `func_return_type`.
func_return_type = func_info["return_type"]
# AUTO: Checks this condition.
if func_return_type not in {"vine", "leaf"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use function '{func_name}' of type '{func_return_type}'. Expected valid leaf value.", line)
# AUTO: Sets `node, index`.
node, index = parse_function_call(tokens, index, func_name, func_return_type, func_info["params"])
# AUTO: Checks the next alternate condition.
elif token.type == "id" and tokens[index + 1].type == "[":
# AUTO: Sets `list_name`.
list_name = token.value
# AUTO: Sets `list_info`.
list_info = symbol_table.lookup_variable(list_name)
# AUTO: Checks this condition.
if isinstance(list_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{list_name}' used before declaration.", token.line)
# AUTO: Checks this condition.
if not list_info["is_list"] and list_info.get("type") != "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: '{list_name}' is not a list.", token.line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Syntax Error: Missing closing bracket.", token.line)
# AUTO: Sets `index_node`.
index_node = ASTNode("Index", line=token.line)
# AUTO: Calls `index_node.add_child`.
index_node.add_child(expr_node)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `node`.
node = ListAccessNode(list_name, index_node, line=token.line)
# AUTO: Repeats while this condition is true.
while index < len(tokens) and tokens[index].type == "[":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `inner_expr, index, _`.
inner_expr, index, _ = parse_expression(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Syntax Error: Missing closing bracket.", token.line)
# AUTO: Sets `inner_index`.
inner_index = ASTNode("Index", line=token.line)
# AUTO: Calls `inner_index.add_child`.
inner_index.add_child(inner_expr)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `node`.
node = ListAccessNode(node, inner_index, line=token.line)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id":
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, line)
# AUTO: Sets `is_list`.
is_list = var_info.get("is_list", False)
# AUTO: Checks this condition.
if is_list and tokens[index + 1].type != "[":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{tokens[index].value}' must be indexed with '[]'.", line)
# AUTO: Checks this condition.
if var_info["type"] not in {"vine", "leaf"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{var_name}' of type {var_info['type']}. Expected valid leaf value.", line)
# AUTO: Sets `node`.
node = ASTNode("Value", var_name, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"chrlit", "stringlit"}:
# AUTO: Sets `node`.
node = ASTNode("Value", tokens[index].value, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `left_node`.
left_node = node
# AUTO: Checks this condition.
if tokens[index].type in {"+", "-", "*", "/", "%", "<", ">", "<=", ">=", "==", "!="}:
# AUTO: Repeats while this condition is true.
while tokens[index].type in {"+", "-", "*", "/", "%"}:
# AUTO: Sets `op`.
op = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "id" and tokens[index + 1].type == "(":
# AUTO: Sets `func_name`.
func_name = tokens[index].value
# AUTO: Sets `func_info`.
func_info = symbol_table.lookup_function(func_name)
# AUTO: Checks this condition.
if isinstance(func_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{func_name}' is not defined.", line)
# AUTO: Sets `func_return_type`.
func_return_type = func_info["return_type"]
# AUTO: Checks this condition.
if func_return_type not in {"vine", "leaf"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use function '{func_name}' of type '{func_return_type}'. Expected valid leaf value", line)
# AUTO: Sets `right_node, index`.
right_node, index = parse_function_call(tokens, index, func_name, func_return_type, func_info["params"])
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id" and tokens[index + 1].type == "[":
# AUTO: Sets `list_name`.
list_name = tokens[index].value
# AUTO: Sets `list_info`.
list_info = symbol_table.lookup_variable(list_name)
# AUTO: Checks this condition.
if isinstance(list_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{list_name}' used before declaration.", token.line)
# AUTO: Checks this condition.
if not list_info["is_list"] and list_info.get("type") != "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: '{list_name}' is not a list.", token.line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Syntax Error: Missing closing bracket.", token.line)
# AUTO: Sets `index_node`.
index_node = ASTNode("Index", line=token.line)
# AUTO: Calls `index_node.add_child`.
index_node.add_child(expr_node)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `right_node`.
right_node = ListAccessNode(list_name, index_node, line=token.line)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id":
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, line)
# AUTO: Sets `is_list`.
is_list = var_info.get("is_list", False)
# AUTO: Checks this condition.
if is_list and tokens[index + 1].type != "[":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{token.value}' must be indexed with '[]' in expressions.", line)
# AUTO: Checks this condition.
if var_info["type"] not in {"vine", "leaf"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{var_name}' of type {var_info['type']} in this expression.", line)
# AUTO: Sets `right_node`.
right_node = ASTNode("Value", var_name, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `right_node`.
right_node = ASTNode("Value", tokens[index].value, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `left_node`.
left_node = BinaryOpNode(left_node, op, right_node, line=line)
# AUTO: Returns this result to the caller.
return left_node, index
# AUTO: Defines function `parse_expression`.
def parse_expression(tokens, index):
# GUIDE: Arithmetic/string expression level. Lower-level functions handle
# multiplication, exponent, unary operators, literals, calls, and grouping.
# AUTO: Sets `left_node, index, left_type`.
left_node, index, left_type = parse_term(tokens, index)
# AUTO: Repeats while this condition is true.
while tokens[index].type in {"+", "-", "`"}:
# AUTO: Sets `op`.
op = tokens[index].value
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Checks this condition.
if op == "`":
# AUTO: Checks this condition.
if left_type not in {"vine", "leaf"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot concatenate - left operand is of type '{left_type}', expected 'vine' or 'leaf'.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `right_node, index, right_type`.
right_node, index, right_type = parse_term(tokens, index)
# AUTO: Checks this condition.
if right_type not in {"vine", "leaf"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot concatenate - right operand is of type '{right_type}', expected 'vine' or 'leaf'.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `left_node`.
left_node = BinaryOpNode(left_node, op, right_node)
# AUTO: Sets `left_type`.
left_type = "vine"
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Checks this condition.
if left_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot use '{op}' on type '{left_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `right_node, index, right_type`.
right_node, index, right_type = parse_term(tokens, index)
# AUTO: Checks this condition.
if right_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot use '{op}' on type '{right_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `left_node`.
left_node = BinaryOpNode(left_node, op, right_node)
# AUTO: Checks this condition.
if left_type == "tree" or right_type == "tree":
# AUTO: Sets `left_type`.
left_type = "tree"
# AUTO: Returns this result to the caller.
return left_node, index, left_type
# AUTO: Defines function `parse_term`.
def parse_term(tokens, index):
# AUTO: Sets `left_node, index, left_type`.
left_node, index, left_type = parse_power(tokens, index)
# AUTO: Repeats while this condition is true.
while tokens[index].type in {"*", "/", "%"}:
# AUTO: Sets `op`.
op = tokens[index].value
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Checks this condition.
if left_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot use '{op}' on type '{left_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if op == "%":
# AUTO: Checks this condition.
if left_type == "tree":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
"Semantic Error: Modulo operator '%' requires 'seed' (integer) operands, "
# AUTO: Executes this statement.
"but found 'tree' (decimal) value.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `right_node, index, right_type`.
right_node, index, right_type = parse_power(tokens, index)
# AUTO: Checks this condition.
if right_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot use '{op}' on type '{right_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if op == "%" and right_type == "tree":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
"Semantic Error: Modulo operator '%' requires 'seed' (integer) operands, "
# AUTO: Executes this statement.
"but found 'tree' (decimal) value.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if op in {"/", "%"} and isinstance(right_node, ASTNode) and right_node.node_type == "Value":
# AUTO: Starts protected code that can catch errors.
try:
# AUTO: Checks this condition.
if float(right_node.value) == 0: # type: ignore
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Division or modulus by zero is undefined.", token.line)
# AUTO: Handles the matching error case.
except ValueError:
# AUTO: Does nothing for this required block.
pass
# AUTO: Sets `left_node`.
left_node = BinaryOpNode(left_node, op, right_node)
# AUTO: Checks this condition.
if left_type == "tree" or right_type == "tree":
# AUTO: Sets `left_type`.
left_type = "tree"
# AUTO: Returns this result to the caller.
return left_node, index, left_type
# AUTO: Defines function `parse_power`.
def parse_power(tokens, index):
# AUTO: Sets `left_node, index, left_type`.
left_node, index, left_type = parse_unary(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type == "**":
# AUTO: Sets `op`.
op = tokens[index].value
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Checks this condition.
if left_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot use '{op}' on type '{left_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `right_node, index, right_type`.
right_node, index, right_type = parse_power(tokens, index)
# AUTO: Checks this condition.
if right_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot use '{op}' on type '{right_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
token.line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `left_node`.
left_node = BinaryOpNode(left_node, op, right_node, line=token.line)
# AUTO: Checks this condition.
if left_type == "tree" or right_type == "tree":
# AUTO: Sets `left_type`.
left_type = "tree"
# AUTO: Returns this result to the caller.
return left_node, index, left_type
# AUTO: Defines function `parse_unary`.
def parse_unary(tokens, index):
# AUTO: Checks this condition.
if tokens[index].type in {"++", "--", "-", "~"}:
# AUTO: Sets `op`.
op = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `operand, index, operand_type`.
operand, index, operand_type = parse_unary(tokens, index)
# AUTO: Checks this condition.
if op in {"++", "--", "-"} and operand_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot use '{op}' on type '{operand_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
tokens[index - 1].line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if op == "~" and operand_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Arithmetic negation '~' requires a numeric 'seed' or 'tree' operand, got '{operand_type}'.",
# AUTO: Executes this statement.
tokens[index - 1].line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Returns this result to the caller.
return UnaryOpNode(op, operand, position="pre", line=tokens[index].line), index, operand_type
# AUTO: Sets `node, index, factor_type`.
node, index, factor_type = parse_factor(tokens, index)
# AUTO: Checks this condition.
if index < len(tokens) and tokens[index].type in {"++", "--"} and tokens[index + 1].type != "id":
# AUTO: Sets `op`.
op = tokens[index].value
# AUTO: Checks this condition.
if factor_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot use '{op}' on type '{factor_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
tokens[index].line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `node`.
node = UnaryOpNode(op, node, position="post", line=tokens[index].line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return node, index, factor_type
# AUTO: Defines function `parse_cast`.
def parse_cast(tokens, index):
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Sets `cast_types`.
cast_types = {"seed", "tree", "leaf", "branch", "vine"}
# AUTO: Checks this condition.
if token.type == "(" and tokens[index + 1].value in cast_types:
# AUTO: Sets `target_type`.
target_type = tokens[index + 1].value
# AUTO: Checks this condition.
if tokens[index + 2].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Syntax Error: Missing closing parenthesis.", token.line)
# AUTO: Adds into `index`.
index += 3
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Sets `cast_node`.
cast_node = CastNode(target_type, expr_node, line=token.line)
# AUTO: Returns this result to the caller.
return cast_node, index, target_type
# AUTO: Returns this result to the caller.
return parse_factor(tokens, index)
# AUTO: Defines function `parse_factor`.
def parse_factor(tokens, index):
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Checks this condition.
if token.type == "(" and tokens[index + 1].value in {"seed", "tree", "leaf", "branch", "vine"}:
# AUTO: Sets `node, index, cast_type`.
node, index, cast_type = parse_cast(tokens, index)
# AUTO: Returns this result to the caller.
return node, index, cast_type
# AUTO: Checks this condition.
if token.type == "(":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `node, index, inner_type`.
node, index, inner_type = parse_expression_branch(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Syntax Error: Missing closing parenthesis.", token.line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return node, index, inner_type
# AUTO: Checks this condition.
if token.type in {"intlit", "dblit", "chrlit", "stringlit", "sunshine", "frost"}:
# AUTO: Sets `node`.
node = ASTNode("Value", token.value)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return node, index, infer_literal_type(token.type)
# AUTO: Checks this condition.
if token.value == "water":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: water() is an I/O statement, not an expression. It cannot be used inside an expression.", token.line)
# AUTO: Checks this condition.
if token.type == "id" and tokens[index + 1].type == "(":
# AUTO: Sets `func_name`.
func_name = token.value
# AUTO: Sets `func_info`.
func_info = symbol_table.lookup_function(func_name)
# AUTO: Checks this condition.
if isinstance(func_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{func_name}' is not declared.", token.line)
# AUTO: Sets `func_return_type`.
func_return_type = func_info["return_type"]
# AUTO: Sets `func_params`.
func_params = func_info["params"]
# AUTO: Sets `node, index`.
node, index = parse_function_call(tokens, index, func_name, func_return_type, func_params)
# AUTO: Returns this result to the caller.
return node, index, func_return_type
# AUTO: Checks the next alternate condition.
elif (
# AUTO: Executes this statement.
tokens[index].type == "id" and
# AUTO: Executes this statement.
tokens[index + 1].type == "." and
# AUTO: Calls `in`.
tokens[index + 2].value in ("wilt", "bloom")
# AUTO: Closes the current grouped code/data.
):
# AUTO: Sets `func_name`.
func_name = tokens[index + 2].value
# AUTO: Sets `identifier`.
identifier = tokens[index].value
# AUTO: Sets `identifier_info`.
identifier_info = symbol_table.lookup_variable(identifier)
# AUTO: Checks this condition.
if isinstance(identifier_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{identifier}' used before declaration.", token.line)
# AUTO: Checks this condition.
if identifier_info["type"] != "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: {func_name}() can only be used on vine (string) variables, but '{identifier}' is of type {identifier_info['type']}.", token.line)
# AUTO: Adds into `index`.
index += 3
# AUTO: Checks this condition.
if func_name == "wilt":
# AUTO: Sets `node`.
node = SoilNode(identifier, line=token.line)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `node`.
node = BloomNode(identifier, line=token.line)
# AUTO: Returns this result to the caller.
return node, index, "vine"
# AUTO: Checks the next alternate condition.
elif (
# AUTO: Executes this statement.
tokens[index].type == "id" and
# AUTO: Executes this statement.
tokens[index + 1].type == "."
# AUTO: Closes the current grouped code/data.
):
# AUTO: Sets `obj_name`.
obj_name = tokens[index].value
# AUTO: Sets `member_name`.
member_name = tokens[index + 2].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(obj_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{obj_name}' used before declaration.", token.line)
# AUTO: Sets `var_type`.
var_type = var_info["type"]
# AUTO: Checks this condition.
if var_type not in symbol_table.bundle_types:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{obj_name}' is not a bundle type.", token.line)
# AUTO: Sets `bundle_members`.
bundle_members = symbol_table.bundle_types[var_type]
# AUTO: Checks this condition.
if member_name not in bundle_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{var_type}' has no member '{member_name}'.", token.line)
# AUTO: Sets `member_type`.
member_type = bundle_members[member_name]
# AUTO: Adds into `index`.
index += 3
# AUTO: Sets `node`.
node = MemberAccessNode(obj_name, member_name, line=token.line)
# AUTO: Repeats while this condition is true.
while index < len(tokens) and tokens[index].type == "." and member_type in symbol_table.bundle_types:
# AUTO: Sets `next_member`.
next_member = tokens[index + 1].value
# AUTO: Sets `nested_members`.
nested_members = symbol_table.bundle_types[member_type]
# AUTO: Checks this condition.
if next_member not in nested_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{member_type}' has no member '{next_member}'.", token.line)
# AUTO: Sets `member_type`.
member_type = nested_members[next_member]
# AUTO: Sets `node`.
node = MemberAccessNode(node, next_member, line=token.line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Returns this result to the caller.
return node, index, member_type
# AUTO: Checks the next alternate condition.
elif token.type == "id" and tokens[index + 1].type == "[":
# AUTO: Sets `list_name`.
list_name = token.value
# AUTO: Sets `list_info`.
list_info = symbol_table.lookup_variable(list_name)
# AUTO: Checks this condition.
if isinstance(list_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{list_name}' used before declaration.", token.line)
# AUTO: Checks this condition.
if not list_info["is_list"] and list_info.get("type") != "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: '{list_name}' is not a list.", token.line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Syntax Error: Missing closing bracket.", token.line)
# AUTO: Sets `index_node`.
index_node = ASTNode("Index", line=token.line)
# AUTO: Calls `index_node.add_child`.
index_node.add_child(expr_node)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `list_access_node`.
list_access_node = ListAccessNode(list_name, index_node, line=token.line)
# AUTO: Repeats while this condition is true.
while index < len(tokens) and tokens[index].type == "[":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `inner_expr, index, _`.
inner_expr, index, _ = parse_expression(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Syntax Error: Missing closing bracket.", token.line)
# AUTO: Sets `inner_index`.
inner_index = ASTNode("Index", line=token.line)
# AUTO: Calls `inner_index.add_child`.
inner_index.add_child(inner_expr)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `list_access_node`.
list_access_node = ListAccessNode(list_access_node, inner_index, line=token.line)
# AUTO: Checks this condition.
if index < len(tokens) and tokens[index].type == "." and list_info["type"] in symbol_table.bundle_types:
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `member_name`.
member_name = tokens[index].value
# AUTO: Sets `bundle_members`.
bundle_members = symbol_table.bundle_types[list_info["type"]]
# AUTO: Checks this condition.
if member_name not in bundle_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{list_info['type']}' has no member '{member_name}'.", token.line)
# AUTO: Sets `member_type`.
member_type = bundle_members[member_name]
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `node`.
node = ArrayMemberAccessNode(list_access_node, member_name, line=token.line)
# AUTO: Repeats while this condition is true.
while index < len(tokens) and tokens[index].type == "." and member_type in symbol_table.bundle_types:
# AUTO: Sets `next_member`.
next_member = tokens[index + 1].value
# AUTO: Sets `nested_members`.
nested_members = symbol_table.bundle_types[member_type]
# AUTO: Checks this condition.
if next_member not in nested_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{member_type}' has no member '{next_member}'.", token.line)
# AUTO: Sets `member_type`.
member_type = nested_members[next_member]
# AUTO: Sets `node`.
node = MemberAccessNode(node, next_member, line=token.line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Returns this result to the caller.
return node, index, member_type
# AUTO: Returns this result to the caller.
return list_access_node, index, list_info["type"]
# AUTO: Checks the next alternate condition.
elif token.type == "id":
# AUTO: Sets `variable_info`.
variable_info = symbol_table.lookup_variable(token.value)
# AUTO: Checks this condition.
if isinstance(variable_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(variable_info, token.line)
# AUTO: Sets `is_list`.
is_list = variable_info.get("is_list", False)
# AUTO: Checks this condition.
if is_list and tokens[index + 1].type != "[":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{token.value}' must be indexed with '[]' in expressions.", token.line)
# AUTO: Sets `var_type`.
var_type = variable_info["type"]
# AUTO: Sets `node`.
node = ASTNode("Value", token.value)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if index < len(tokens) and tokens[index].type in {"++", "--"}:
# AUTO: Sets `op`.
op = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return UnaryOpNode(op, node, position="post", line=token.line), index, var_type
# AUTO: Returns this result to the caller.
return node, index, var_type
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Checks this condition.
if token.type in {";", "}", ")", ","}:
# AUTO: Sets `error`.
error = f"Semantic Error: Expected an expression before '{token.value}'."
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `error`.
error = f"Semantic Error: Cannot use '{token.value}' in this expression."
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, token.line)
# AUTO: Defines function `_assignment_root_name`.
def _assignment_root_name(node):
# AUTO: Checks this condition.
if node.node_type in {"Identifier", "Value", "Object", "ListName"}:
# AUTO: Checks this condition.
if isinstance(node.value, ASTNode):
# AUTO: Returns this result to the caller.
return _assignment_root_name(node.value)
# AUTO: Returns this result to the caller.
return node.value
# AUTO: Checks this condition.
if node.node_type in {"ListAccess", "MemberAccess", "ArrayMemberAccess"}:
# AUTO: Returns this result to the caller.
return _assignment_root_name(node.children[0])
# AUTO: Returns this result to the caller.
return None
# AUTO: Defines function `_assignment_target`.
def _assignment_target(node, line):
# AUTO: Sets `root_name`.
root_name = _assignment_root_name(node)
# AUTO: Sets `valid_node_types`.
valid_node_types = {"Value", "Identifier", "ListAccess", "MemberAccess", "ArrayMemberAccess"}
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(root_name) if root_name is not None else None
# AUTO: Checks this condition.
if node.node_type not in valid_node_types or isinstance(var_info, str) or var_info is None:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
"Semantic Error: Left-hand side of assignment expression must be a modifiable variable, list element, or bundle member.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if var_info.get("is_fertile", False):
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Variable '{root_name}' is declared as fertile and cannot be re-assigned a value.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if node.node_type == "Value":
# AUTO: Sets `node`.
node = ASTNode("Identifier", node.value, line=line)
# AUTO: Returns this result to the caller.
return node
# AUTO: Defines function `parse_assignment_expression`.
def parse_assignment_expression(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Sets `left_node, index, left_type`.
left_node, index, left_type = parse_logical_expression(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type not in {"=", "+=", "-=", "*=", "/=", "%="}:
# AUTO: Returns this result to the caller.
return left_node, index, left_type
# AUTO: Sets `operator`.
operator = tokens[index].type
# AUTO: Sets `target`.
target = _assignment_target(left_node, line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `right_node, index, right_type`.
right_node, index, right_type = parse_assignment_expression(tokens, index)
# AUTO: Checks this condition.
if operator == "=":
# AUTO: Checks this condition.
if not _types_compatible(left_type, right_type):
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Type mismatch - cannot assign '{right_type}' value to '{left_type}' variable.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `value_node`.
value_node = right_node
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Checks this condition.
if left_type not in {"seed", "tree"} or right_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Compound assignment '{operator}' requires numeric 'seed' or 'tree' operands.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if operator == "%=" and left_type != "seed":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Sets `"Semantic Error: Modulo assignment '%`.
"Semantic Error: Modulo assignment '%=' requires a 'seed' (integer) left-hand side.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `value_node`.
value_node = BinaryOpNode(copy.deepcopy(target), operator[0], right_node, line=line)
# AUTO: Returns this result to the caller.
return AssignmentNode(target, value_node, line=line), index, left_type
# AUTO: Defines function `parse_expression_branch`.
def parse_expression_branch(tokens, index):
# AUTO: Returns this result to the caller.
return parse_assignment_expression(tokens, index)
# AUTO: Defines function `parse_logical_expression`.
def parse_logical_expression(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Sets `left_node, index, left_type`.
left_node, index, left_type = parse_equality(tokens, index)
# AUTO: Repeats while this condition is true.
while tokens[index].type in {"&&", "||"}:
# AUTO: Sets `operator`.
operator = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `right_node, index, right_type`.
right_node, index, right_type = parse_equality(tokens, index)
# AUTO: Checks this condition.
if left_type in {"vine", "leaf"} or right_type in {"vine", "leaf"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Logical operator '{operator}' is not valid for string or leaf operands.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `left_node`.
left_node = BinaryOpNode(left_node, operator, right_node, line=line)
# AUTO: Sets `left_type`.
left_type = "branch"
# AUTO: Returns this result to the caller.
return left_node, index, left_type
# AUTO: Defines function `parse_equality`.
def parse_equality(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Sets `left_node, index, left_type`.
left_node, index, left_type = parse_relational(tokens, index)
# AUTO: Repeats while this condition is true.
while tokens[index].type in {"==", "!="}:
# AUTO: Sets `operator`.
operator = tokens[index].type
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `right_node, index, right_type`.
right_node, index, right_type = parse_relational(tokens, index)
# AUTO: Checks this condition.
if left_type is None or right_type is None:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
"Semantic Error: Could not determine the type of an operand in equality check.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `numeric`.
numeric = {"seed", "tree"}
# AUTO: Checks this condition.
if left_type != right_type and not (left_type in numeric and right_type in numeric):
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot compare '{left_type}' with '{right_type}' using '{operator}'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `left_node`.
left_node = BinaryOpNode(left_node, operator, right_node, line=line)
# AUTO: Sets `left_type`.
left_type = "branch"
# AUTO: Returns this result to the caller.
return left_node, index, left_type
# AUTO: Defines function `parse_relational`.
def parse_relational(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if tokens[index].type == "!":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `operand_node, index, operand_type`.
operand_node, index, operand_type = parse_relational(tokens, index)
# AUTO: Checks this condition.
if operand_type != "branch":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: ! operator can only apply to 'branch' value.", line)
# AUTO: Returns this result to the caller.
return UnaryOpNode("!", operand_node, line=line), index, "branch"
# AUTO: Sets `left_node, index, left_type`.
left_node, index, left_type = parse_expression(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type in {"<", "<=", ">", ">="}:
# AUTO: Sets `operator`.
operator = tokens[index].type
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `right_node, index, right_type`.
right_node, index, right_type = parse_expression(tokens, index)
# AUTO: Checks this condition.
if left_type == "vine" or right_type == "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Relational operator '{operator}' is not valid for string operands. Use '==' or '!='.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if left_type and right_type:
# AUTO: Sets `numeric`.
numeric = {"seed", "tree"}
# AUTO: Checks this condition.
if left_type in numeric and right_type not in numeric:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot compare '{left_type}' with '{right_type}' using '{operator}'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if left_type not in numeric and right_type in numeric:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot compare '{left_type}' with '{right_type}' using '{operator}'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Checks this condition.
if left_type != right_type and not (left_type in numeric and right_type in numeric):
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot compare '{left_type}' with '{right_type}' using '{operator}'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `left_node`.
left_node = BinaryOpNode(left_node, operator, right_node, line=line)
# AUTO: Sets `left_type`.
left_type = "branch"
# AUTO: Returns this result to the caller.
return left_node, index, left_type
# AUTO: Returns this result to the caller.
return left_node, index, left_type
# AUTO: Defines function `check_lwk`.
def check_lwk(tokens, index):
# AUTO: Sets `start_index`.
start_index = index
# AUTO: Sets `op_found`.
op_found = False
# AUTO: Repeats while this condition is true.
while tokens[index].type != ")":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type in {"<", "<=", ">", ">=", "==", "!=", "&&", "||", "sunshine", "frost"}:
# AUTO: Sets `op_found`.
op_found = True
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(tokens[index].value)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{tokens[index].value}' used before declaration.", tokens[index].line)
# AUTO: Checks this condition.
if var_info["type"] == "branch":
# AUTO: Sets `op_found`.
op_found = True
# AUTO: Checks this condition.
if tokens[index].type in {"<", "<=", ">", ">=", "==", "!=", "&&", "||"}:
# AUTO: Sets `op_found`.
op_found = True
# AUTO: Returns this result to the caller.
return op_found, start_index
# AUTO: Defines function `parse_operand`.
def parse_operand(tokens, index):
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Sets `line`.
line = token.line
# AUTO: Checks this condition.
if token.value == "water":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: water() is an I/O statement, not an expression. It cannot be used inside an expression.", token.line)
# AUTO: Checks this condition.
if token.type == "(":
# AUTO: Checks this condition.
if tokens[index+1].value in {"seed", "tree"}:
# AUTO: Sets `expr_type`.
expr_type = tokens[index+1].value
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, expr_type
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type == "id":
# AUTO: Sets `var_name`.
var_name = tokens[index +1].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Sets `var_type`.
var_type = None
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `var_type`.
var_type = var_info["type"]
# AUTO: Sets `is_lwk, index`.
is_lwk, index = check_lwk(tokens, index)
# AUTO: Checks this condition.
if not is_lwk:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Subtracts from `index`.
index -= 1
# AUTO: Sets `expr_type`.
expr_type = "seed"
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Sets `expr_type`.
expr_type = "branch"
# AUTO: Sets `is_lwk, index`.
is_lwk, index = check_lwk(tokens, index)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return expr_node, index, expr_type
# AUTO: Checks this condition.
if token.type in {"intlit", "dblit"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, infer_literal_type(token.type)
# AUTO: Checks this condition.
if token.type in {"chrlit", "stringlit"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, infer_literal_type(token.type)
# AUTO: Checks this condition.
if token.type in {"sunshine", "frost"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, infer_literal_type(token.type)
# AUTO: Checks this condition.
if token.type == "id" and tokens[index + 1].type == "(":
# AUTO: Sets `func_name`.
func_name = tokens[index].value
# AUTO: Sets `func_info`.
func_info = symbol_table.lookup_function(func_name)
# AUTO: Checks this condition.
if isinstance(func_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{func_name}' is not declared.", line)
# AUTO: Sets `func_return_type`.
func_return_type = func_info["return_type"]
# AUTO: Sets `func_params`.
func_params = func_info["params"]
# AUTO: Sets `func_node, index`.
func_node, index = parse_function_call(tokens, index, func_name, func_return_type, func_params)
# AUTO: Returns this result to the caller.
return func_node, index, func_return_type
# AUTO: Checks this condition.
if token.type == "id" and tokens[index + 1].type == "[":
# AUTO: Sets `list_name`.
list_name = token.value
# AUTO: Sets `list_info`.
list_info = symbol_table.lookup_variable(list_name)
# AUTO: Checks this condition.
if isinstance(list_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{list_name}' used before declaration.", token.line)
# AUTO: Checks this condition.
if not list_info["is_list"] and list_info.get("type") != "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: '{list_name}' is not a list.", token.line)
# AUTO: Sets `expr_node, index, expr_type`.
expr_node, index, expr_type = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, expr_type
# AUTO: Checks this condition.
if token.type in {"intlit", "dblit"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, infer_literal_type(token.type)
# AUTO: Checks this condition.
if token.type == "chrlit":
# AUTO: Sets `expr_node, index`.
expr_node, index = parse_expression_leaf(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, infer_literal_type(token.type)
# AUTO: Checks this condition.
if token.type == "stringlit":
# AUTO: Returns this result to the caller.
return ASTNode("Value", token.value, line=line), index + 1, "vine"
# AUTO: Checks this condition.
if token.type in {"sunshine", "frost"}:
# AUTO: Returns this result to the caller.
return ASTNode("Value", token.value, line=line), index + 1, "branch"
# AUTO: Checks this condition.
if token.type == "id" and tokens[index + 1].type == ".":
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(token.value)
# AUTO: Checks this condition.
if not isinstance(var_info, str) and var_info["type"] in symbol_table.bundle_types:
# AUTO: Sets `expr_node, index, expr_type`.
expr_node, index, expr_type = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, expr_type
# AUTO: Checks this condition.
if token.type == "id":
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(token.value)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{token.value}' used before declaration.", line)
# AUTO: Sets `var_type`.
var_type = var_info["type"]
# AUTO: Sets `is_list`.
is_list = var_info.get("is_list", False)
# AUTO: Checks this condition.
if is_list and tokens[index + 1].type != "[":
# AUTO: Checks this condition.
if tokens[index + 1].type in {"+", "-", "*", "/", "%", "**", "==", "!=", ">", "<", ">=", "<="}:
# AUTO: Sets `op_token`.
op_token = tokens[index + 1]
# AUTO: Checks this condition.
if index + 2 < len(tokens) and tokens[index + 2].type == "id":
# AUTO: Sets `rhs_info`.
rhs_info = symbol_table.lookup_variable(tokens[index + 2].value)
# AUTO: Checks this condition.
if not isinstance(rhs_info, str) and rhs_info.get("is_list", False):
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Cannot use '{op_token.value}' operator on arrays '{token.value}' and '{tokens[index + 2].value}'. Arrays must be indexed with '[]'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{token.value}' must be indexed with '[]' in expressions.", line)
# AUTO: Checks this condition.
if var_type in {"seed", "tree", "branch"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, var_type
# AUTO: Checks the next alternate condition.
elif var_type == "leaf":
# AUTO: Sets `expr_node, index`.
expr_node, index = parse_expression_leaf(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, var_type
# AUTO: Checks the next alternate condition.
elif var_type == "vine":
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, "vine"
# AUTO: Checks the next alternate condition.
elif var_type == "branch":
# AUTO: Returns this result to the caller.
return ASTNode("Value", token.value, line=line), index + 1, var_type
# AUTO: Checks the next alternate condition.
elif var_type in symbol_table.bundle_types:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Returns this result to the caller.
return expr_node, index, var_type
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Unsupported type '{var_type}'.", line)
# AUTO: Checks this condition.
if token.type in {";", "}", ")", ","}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected an expression before '{token.value}'.", line)
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{token.value}' in this expression.", line)
# AUTO: Defines function `infer_literal_type`.
def infer_literal_type(token_type):
# AUTO: Checks this condition.
if token_type == "intlit":
# AUTO: Returns this result to the caller.
return "seed"
# AUTO: Checks this condition.
if token_type == "dblit":
# AUTO: Returns this result to the caller.
return "tree"
# AUTO: Checks this condition.
if token_type == "stringlit":
# AUTO: Returns this result to the caller.
return "vine"
# AUTO: Checks this condition.
if token_type == "chrlit":
# AUTO: Returns this result to the caller.
return "leaf"
# AUTO: Checks this condition.
if token_type in {"sunshine", "frost"}:
# AUTO: Returns this result to the caller.
return "branch"
# AUTO: Returns this result to the caller.
return None
# AUTO: Defines function `parse_assignment`.
def parse_assignment(tokens, index, var_name, var_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if var_info and var_info["is_fertile"]:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' is declared as fertile.", line)
# AUTO: Checks this condition.
if tokens[index].value == "water":
# AUTO: Sets `water_line`.
water_line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after water.", water_line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `water_type`.
water_type = None
# AUTO: Checks this condition.
if tokens[index].value in {"seed", "tree", "leaf", "branch", "vine"}:
# AUTO: Sets `water_type`.
water_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: water() accepts only an optional type parameter (e.g., water(seed)).", water_line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if water_type and not _types_compatible(var_type, water_type):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Type mismatch — cannot assign water({water_type}) to '{var_type}' variable.", water_line)
# AUTO: Sets `value_node`.
value_node = ASTNode("Input", f"water({water_type})" if water_type else "water()", line=water_line)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `value_node, index`.
value_node, index = parse_expression_type(tokens, index, var_type)
# AUTO: Sets `assignment_node`.
assignment_node = AssignmentNode(var_name, value_node, line=line)
# AUTO: Returns this result to the caller.
return assignment_node, index
# AUTO: Defines function `parse_function_call`.
def parse_function_call(tokens, index, func_name, func_type, func_params):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `args_node`.
args_node = ASTNode("Arguments")
# AUTO: Sets `provided_args`.
provided_args = []
# AUTO: Sets `expected_params`.
expected_params = func_params
# AUTO: Repeats while this condition is true.
while tokens[index].type != ")":
# AUTO: Checks this condition.
if len(provided_args) >= len(expected_params):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Too many arguments in function call '{func_name}'.", line)
# AUTO: Sets `expected_type`.
expected_type = expected_params[len(provided_args)].children[0].value
# AUTO: Sets `expected_param`.
expected_param = expected_params[len(provided_args)]
# AUTO: Calls `any`.
is_array_param = any(child.node_type == "ArrayParam" for child in expected_param.children)
# AUTO: Checks this condition.
if is_array_param:
# AUTO: Checks this condition.
if tokens[index].type != "id":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected array variable for parameter {len(provided_args) + 1} of '{func_name}'.", line)
# AUTO: Sets `arg_name`.
arg_name = tokens[index].value
# AUTO: Sets `arg_info`.
arg_info = symbol_table.lookup_variable(arg_name)
# AUTO: Checks this condition.
if isinstance(arg_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(arg_info, line)
# AUTO: Checks this condition.
if not arg_info.get("is_list", False):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Argument '{arg_name}' is not an array. Parameter {len(provided_args) + 1} of '{func_name}' expects an array.", line)
# AUTO: Checks this condition.
if arg_info["type"] != expected_type:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Array argument '{arg_name}' is of type '{arg_info['type']}', but parameter expects '{expected_type}'.", line)
# AUTO: Sets `expr_node`.
expr_node = ASTNode("Identifier", arg_name, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `expr_node, index`.
expr_node, index = parse_expression_type(tokens, index, expected_type)
# AUTO: Sets `arg_node`.
arg_node = ASTNode("Argument")
# AUTO: Calls `arg_node.add_child`.
arg_node.add_child(expr_node)
# AUTO: Calls `args_node.add_child`.
args_node.add_child(arg_node)
# AUTO: Appends a value to a list.
provided_args.append((arg_node, expected_type))
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type in {"++", "--"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Unary operators cannot be applied to function calls.", line)
# AUTO: Checks this condition.
if len(provided_args) != len(expected_params):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{func_name}' expects {len(expected_params)} arguments, but {len(provided_args)} were provided.", line)
# AUTO: Starts a loop over these values.
for i, (arg_node, arg_type) in enumerate(provided_args):
# AUTO: Sets `expected_type`.
expected_type = expected_params[i].children[0].value
# AUTO: Checks this condition.
if expected_type in {"seed", "tree"} and arg_type == "seed":
# AUTO: Skips to the next loop iteration.
continue
# AUTO: Checks this condition.
if arg_type != expected_type:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Argument {i+1} of '{func_name}' should be '{expected_type}', but got '{arg_type}'.", line)
# AUTO: Returns this result to the caller.
return FunctionCallNode(func_name, args_node.children, line=line), index
# AUTO: Defines function `parse_water_statement`.
def parse_water_statement(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after water.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].value in {"seed", "tree", "leaf", "branch", "vine"}:
# AUTO: Sets `water_type`.
water_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: water() accepts only an optional type parameter or a variable name.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ";":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ';' after water statement.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `input_node`.
input_node = ASTNode("Input", f"water({water_type})", line=line)
# AUTO: Returns this result to the caller.
return input_node, index
# AUTO: Checks the next alternate condition.
elif tokens[index].type == ")":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ";":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ';' after water statement.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `input_node`.
input_node = ASTNode("Input", "water()", line=line)
# AUTO: Returns this result to the caller.
return input_node, index
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id":
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, line)
# AUTO: Checks this condition.
if var_info.get("is_fertile", False):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' is declared as fertile and cannot be re-assigned a value.", line)
# AUTO: Sets `var_type`.
var_type = var_info["type"]
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "[":
# AUTO: Checks this condition.
if not var_info.get("is_list", False) and var_info.get("type") != "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' is not a list.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `index_expr, index, idx_type`.
index_expr, index, idx_type = parse_equality(tokens, index)
# AUTO: Checks this condition.
if idx_type is not None and idx_type != "seed":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List index must be of type 'seed', got '{idx_type}'.", line)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ']' after list index.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `index_wrapper`.
index_wrapper = ASTNode("Index", line=line)
# AUTO: Calls `index_wrapper.add_child`.
index_wrapper.add_child(index_expr)
# AUTO: Sets `list_access_node`.
list_access_node = ListAccessNode(var_name, index_wrapper, line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type == "[":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `inner_expr, index, inner_type`.
inner_expr, index, inner_type = parse_equality(tokens, index)
# AUTO: Checks this condition.
if inner_type is not None and inner_type != "seed":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List index must be of type 'seed', got '{inner_type}'.", line)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ']' after list index.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `inner_wrapper`.
inner_wrapper = ASTNode("Index", line=line)
# AUTO: Calls `inner_wrapper.add_child`.
inner_wrapper.add_child(inner_expr)
# AUTO: Sets `list_access_node`.
list_access_node = ListAccessNode(list_access_node, inner_wrapper, line=line)
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected ')' after water(arr[i]).", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ";":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ';' after water statement.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `input_node`.
input_node = ASTNode("Input", f"water({var_type})", line=line)
# AUTO: Sets `assignment_node`.
assignment_node = AssignmentNode(list_access_node, input_node, line=line)
# AUTO: Returns this result to the caller.
return assignment_node, index
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: water() accepts only a single variable name or type parameter.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ";":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ';' after water statement.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `input_node`.
input_node = ASTNode("Input", f"water({var_type})", line=line)
# AUTO: Sets `value_ident`.
value_ident = ASTNode("Identifier", var_name, line=line)
# AUTO: Sets `assignment_node`.
assignment_node = AssignmentNode(var_name, input_node, line=line)
# AUTO: Returns this result to the caller.
return assignment_node, index
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid argument to water(). Expected a variable name or type.", line)
# AUTO: Defines function `parse_print`.
def parse_print(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after plant statement.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Sets `args`.
args = []
# AUTO: Sets `placeholder_count`.
placeholder_count = 0
# AUTO: Checks this condition.
if tokens[index].type == "stringlit":
# AUTO: Sets `format_node, index, placeholder_count`.
format_node, index, placeholder_count = parse_string_concatenation(tokens, index)
# AUTO: Appends a value to a list.
args.append(format_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id":
# AUTO: Sets `identif_name`.
identif_name = tokens[index].value
# AUTO: Checks this condition.
if tokens[index + 1].type == "(":
# AUTO: Sets `func_name`.
func_name = identif_name
# AUTO: Sets `func_info`.
func_info = symbol_table.lookup_function(func_name)
# AUTO: Checks this condition.
if isinstance(func_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{func_name}' is not defined.", line)
# AUTO: Checks this condition.
if func_info["return_type"] in {"seed", "tree"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif func_info["return_type"] in {"vine"}:
# AUTO: Sets `expr_node, index`.
expr_node, index = parse_expression_vine(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif func_info["return_type"] in {"leaf"}:
# AUTO: Sets `expr_node, index`.
expr_node, index = parse_expression_leaf(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif func_info["return_type"] in {"branch"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{func_name}' returns invalid type '{func_info['return_type']}'.", line)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id" and tokens[index + 1].type == "[":
# AUTO: Sets `list_name`.
list_name = token.value
# AUTO: Sets `list_info`.
list_info = symbol_table.lookup_variable(list_name)
# AUTO: Checks this condition.
if isinstance(list_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{list_name}' used before declaration.", tokens[index].line)
# AUTO: Sets `list_type`.
list_type = list_info["type"]
# AUTO: Sets `start_index`.
start_index = index
# AUTO: Checks this condition.
if not list_info["is_list"] and list_info.get("type") != "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: '{list_name}' is not a list.", tokens[index].line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Syntax Error: Missing closing bracket.", tokens[index].line)
# AUTO: Sets `index_node`.
index_node = ASTNode("Index", line=tokens[index].line)
# AUTO: Calls `index_node.add_child`.
index_node.add_child(expr_node)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `list_access_node`.
list_access_node = ListAccessNode(list_name, index_node, line=tokens[index].line)
# AUTO: Checks this condition.
if list_type in {"seed", "tree"} or list_type in symbol_table.bundle_types:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, start_index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif list_info["is_list"]:
# AUTO: Appends a value to a list.
args.append(list_access_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `arg_info`.
arg_info = symbol_table.lookup_variable(identif_name)
# AUTO: Checks this condition.
if isinstance(arg_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{identif_name}' used before declaration.", line)
# AUTO: Checks this condition.
if tokens[index + 1].type == "." and arg_info["type"] in symbol_table.bundle_types:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif arg_info["type"] in {"vine", "leaf"} and tokens[index + 1].type == "`":
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif arg_info["type"] in {"seed", "tree"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `args.append(ASTNode("Value", identif_name, line`.
args.append(ASTNode("Value", identif_name, line=line))
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"intlit", "dblit"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"sunshine", "frost", "!"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"chrlit"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"("}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"++", "--", "-"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index)
# AUTO: Appends a value to a list.
args.append(expr_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected valid argument in plant statement.", line)
# AUTO: Sets `actual_args`.
actual_args = []
# AUTO: Repeats while this condition is true.
while tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type in {"intlit", "dblit", "-"}:
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id" and tokens[index + 1].type == "[":
# AUTO: Sets `start_index`.
start_index = index
# AUTO: Sets `list_name`.
list_name = tokens[index].value
# AUTO: Sets `list_info`.
list_info = symbol_table.lookup_variable(list_name)
# AUTO: Sets `list_type`.
list_type = list_info["type"]
# AUTO: Sets `is_list`.
is_list = list_info["is_list"]
# AUTO: Checks this condition.
if isinstance(list_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{list_name}' used before declaration.", tokens[index].line)
# AUTO: Checks this condition.
if not list_info["is_list"] and list_info.get("type") != "vine":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: '{list_name}' is not a list.", tokens[index].line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError("Syntax Error: Missing closing bracket.", tokens[index].line)
# AUTO: Sets `index_node`.
index_node = ASTNode("Index", line=tokens[index].line)
# AUTO: Calls `index_node.add_child`.
index_node.add_child(expr_node)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `list_access_node`.
list_access_node = ListAccessNode(list_name, index_node, line=tokens[index].line)
# AUTO: Checks this condition.
if list_type in {"seed", "tree"}:
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_expression(tokens, start_index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Checks the next alternate condition.
elif is_list:
# AUTO: Appends a value to a list.
actual_args.append(list_access_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id" and tokens[index+1].type == "(":
# AUTO: Sets `func_name`.
func_name = tokens[index].value
# AUTO: Sets `func_info`.
func_info = symbol_table.lookup_function(func_name)
# AUTO: Sets `index_start`.
index_start = index
# AUTO: Checks this condition.
if isinstance(func_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{func_name}' is not declared.", line)
# AUTO: Sets `func_return_type`.
func_return_type = func_info["return_type"]
# AUTO: Sets `func_params`.
func_params = func_info["params"]
# AUTO: Sets `func_node, index`.
func_node, index = parse_function_call(tokens, index, func_name, func_return_type, func_params)
# AUTO: Checks this condition.
if func_return_type in {"seed", "tree"}:
# AUTO: Sets `expr_node, index, _`.
expr_node, index, _ = parse_expression(tokens, index_start)
# AUTO: Appends a value to a list.
actual_args.append(expr_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Appends a value to a list.
actual_args.append(func_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id":
# AUTO: Sets `arg_name`.
arg_name = tokens[index].value
# AUTO: Sets `arg_info`.
arg_info = symbol_table.lookup_variable(arg_name)
# AUTO: Checks this condition.
if isinstance(arg_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{arg_name}' used before declaration.", line)
# AUTO: Checks this condition.
if arg_info["is_list"]:
# AUTO: Checks this condition.
if tokens[index + 1].type != "[":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: List '{arg_name}' must be indexed with '[]' in expressions.", line)
# AUTO: Checks this condition.
if tokens[index + 1].type == "." and arg_info["type"] in symbol_table.bundle_types:
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_expression(tokens, index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Checks the next alternate condition.
elif arg_info["type"] in {"seed", "tree"}:
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Checks the next alternate condition.
elif arg_info["type"] in {"vine", "leaf"} and tokens[index + 1].type == "`":
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `actual_args.append(ASTNode("Value", arg_name, line`.
actual_args.append(ASTNode("Value", arg_name, line=line))
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"("}:
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "stringlit":
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_string_concatenation(tokens, index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"chrlit"}:
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"sunshine", "frost", "!"}:
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_expression_branch(tokens, index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"++", "--"}:
# AUTO: Sets `arg_node, index, _`.
arg_node, index, _ = parse_expression(tokens, index)
# AUTO: Appends a value to a list.
actual_args.append(arg_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected valid argument after ',' in plant statement.", line)
# AUTO: Checks this condition.
if placeholder_count > 15:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Exceeded maximum amount of 15 arguments in plant statement.", line)
# AUTO: Checks this condition.
if placeholder_count > 0 and placeholder_count != len(actual_args):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Found {len(actual_args)} argument(s). Expected {placeholder_count} argument(s).", line)
# AUTO: Calls `args.extend`.
args.extend(actual_args)
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected ')' after {tokens[index-1].value} in plant statement.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return PrintNode(args, line=line), index
# AUTO: Defines function `parse_string_concatenation`.
def parse_string_concatenation(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if tokens[index].type != "stringlit":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: String concatenation must start with a string literal.", line)
# AUTO: Sets `format_string`.
format_string = tokens[index].value
# AUTO: Sets `raw_string`.
raw_string = format_string.replace("\\{", "").replace("\\}", "")
# AUTO: Checks this condition.
if "{" in raw_string or "}" in raw_string:
# AUTO: Checks this condition.
if raw_string.count("{") != raw_string.count("}"):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid string literal '{format_string}' in plant().", line)
# AUTO: Sets `matches`.
matches = re.findall(r"\{[^}]*\}", raw_string)
# AUTO: Starts a loop over these values.
for match in matches:
# AUTO: Checks this condition.
if match != "{}":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Placeholders {{}} must be adjacent to each other within the string literal.", line)
# AUTO: Sets `placeholder_count`.
placeholder_count = raw_string.count("{}")
# AUTO: Sets `left_node`.
left_node = ASTNode("FormattedString", tokens[index].value, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Repeats while this condition is true.
while index < len(tokens) and tokens[index].type == "`":
# AUTO: Sets `concat_op`.
concat_op = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type not in {"stringlit", "chrlit", "id"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Only values of type vine or leaf can be concatenated in plant().", line)
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' used before declaration.", line)
# AUTO: Checks this condition.
if var_info["type"] not in {"vine", "leaf"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' with type {var_info['type']} cannot be concatenated in plant().", line)
# AUTO: Sets `format_string`.
format_string = tokens[index].value
# AUTO: Sets `raw_string`.
raw_string = format_string.replace("\\{", "").replace("\\}", "")
# AUTO: Checks this condition.
if "{" in raw_string or "}" in raw_string:
# AUTO: Checks this condition.
if raw_string.count("{") != raw_string.count("}"):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid string literal '{format_string}' in plant().", line)
# AUTO: Checks this condition.
if "{}" not in raw_string:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Placeholders {{}} must be adjacent within the string literal.", line)
# AUTO: Adds into `placeholder_count`.
placeholder_count += raw_string.count("{}")
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `right_node`.
right_node = ASTNode("Identifier", tokens[index].value, line=line)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "chrlit":
# AUTO: Sets `right_node`.
right_node = ASTNode("Value", tokens[index].value, line=line)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `right_node`.
right_node = ASTNode("FormattedString", tokens[index].value, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `left_node`.
left_node = BinaryOpNode(left_node, concat_op, right_node, line=line)
# AUTO: Returns this result to the caller.
return left_node, index, placeholder_count
# AUTO: Defines function `parse_fertile`.
def parse_fertile(tokens, index):
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Sets `line`.
line = token.line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].value not in {"seed", "tree", "vine", "leaf", "branch"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid fertile variable type '{tokens[index].value}'.", line)
# AUTO: Sets `var_type`.
var_type = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "id":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected identifier after '{var_type}'.", line)
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "=":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Fertile variables must be initialized.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `expected_literals`.
expected_literals = {
# AUTO: Executes this statement.
"seed": {"intlit"},
# AUTO: Executes this statement.
"tree": {"dblit"},
# AUTO: Executes this statement.
"vine": {"stringlit"},
# AUTO: Executes this statement.
"leaf": {"chrlit"},
# AUTO: Executes this statement.
"branch": {"sunshine", "frost"}
# AUTO: Closes the current grouped code/data.
}
# AUTO: Checks this condition.
if tokens[index].type not in expected_literals[var_type]:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: '{var_name}' must be initialized with a {var_type} literal.", line)
# AUTO: Sets `value_node`.
value_node = ASTNode("Value", tokens[index].value, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Multiple fertile declaration is not allowed.", line)
# AUTO: Sets `error`.
error = symbol_table.declare_variable(var_name, var_type, value=value_node, is_list=False, is_fertile=True)
# AUTO: Checks this condition.
if isinstance(error, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(error, line)
# AUTO: Returns this result to the caller.
return FertileDeclarationNode(var_type, var_name, value_node, line=line), index
# AUTO: Defines function `parse_if`.
def parse_if(tokens, index, func_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after 'spring'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `condition_expr, index, cond_type`.
condition_expr, index, cond_type = parse_expression_branch(tokens, index)
# AUTO: Checks this condition.
if cond_type != "branch":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: spring condition must be branch, got {cond_type}.", line)
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ')' after 'spring' condition.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.enter_scope`.
symbol_table.enter_scope()
# AUTO: Sets `condition_node`.
condition_node = ASTNode("Condition", line=line)
# AUTO: Calls `condition_node.add_child`.
condition_node.add_child(condition_expr)
# AUTO: Sets `if_node`.
if_node = IfStatementNode(condition_node, line=line)
# AUTO: Checks this condition.
if tokens[index].type == "{":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `block_node`.
block_node = ASTNode("Block", line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `block_node.add_child`.
block_node.add_child(stmt)
# AUTO: Checks this condition.
if tokens[index].type != "}":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '}}' after 'spring' block.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.exit_scope`.
symbol_table.exit_scope()
# AUTO: Calls `if_node.add_child`.
if_node.add_child(block_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '{{' after 'spring' condition.", line)
# AUTO: Repeats while this condition is true.
while tokens[index].value == "bud":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after else-if.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `elseif_node`.
elseif_node = ASTNode("ElseIfStatement", line=line)
# AUTO: Sets `elseif_condition_expr, index, elseif_cond_type`.
elseif_condition_expr, index, elseif_cond_type = parse_expression_branch(tokens, index)
# AUTO: Checks this condition.
if elseif_cond_type != "branch":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: bud condition must be branch, got {elseif_cond_type}.", line)
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ')' after else-if condition.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.enter_scope`.
symbol_table.enter_scope()
# AUTO: Sets `elseif_condition_node`.
elseif_condition_node = ASTNode("Condition", line=line)
# AUTO: Calls `elseif_condition_node.add_child`.
elseif_condition_node.add_child(elseif_condition_expr)
# AUTO: Calls `elseif_node.add_child`.
elseif_node.add_child(elseif_condition_node)
# AUTO: Checks this condition.
if tokens[index].type == "{":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `elseif_block_node`.
elseif_block_node = ASTNode("Block", line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `elseif_block_node.add_child`.
elseif_block_node.add_child(stmt)
# AUTO: Calls `elseif_node.add_child`.
elseif_node.add_child(elseif_block_node)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.exit_scope`.
symbol_table.exit_scope()
# AUTO: Calls `if_node.add_child`.
if_node.add_child(elseif_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '{{' after else-if condition.", line)
# AUTO: Checks this condition.
if tokens[index].value == "wither":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "{":
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.enter_scope`.
symbol_table.enter_scope()
# AUTO: Sets `else_node`.
else_node = ASTNode("ElseStatement", line=line)
# AUTO: Sets `else_block_node`.
else_block_node = ASTNode("Block", line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `else_block_node.add_child`.
else_block_node.add_child(stmt)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.exit_scope`.
symbol_table.exit_scope()
# AUTO: Calls `else_node.add_child`.
else_node.add_child(else_block_node)
# AUTO: Calls `if_node.add_child`.
if_node.add_child(else_node)
# AUTO: Returns this result to the caller.
return if_node, index
# AUTO: Defines function `parse_return`.
def parse_return(tokens, index, func_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if func_type == "empty":
# AUTO: Checks this condition.
if tokens[index].type not in {"}", ";"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: empty function must not return any value.", line)
# AUTO: Returns this result to the caller.
return ReturnNode(None, line=line), index
# AUTO: Checks this condition.
if tokens[index].type in {";", "}"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function expects to return a '{func_type}' value, but 'reclaim' has no return expression.", line)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id":
# AUTO: Sets `identifier`.
identifier = tokens[index].value
# AUTO: Checks this condition.
if tokens[index+1].type == "(":
# AUTO: Sets `func_info`.
func_info = symbol_table.lookup_function(identifier)
# AUTO: Checks this condition.
if isinstance(func_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{identifier}' is not defined.", line)
# AUTO: Returns this result to the caller.
return_type = func_info["return_type"]
# AUTO: Checks this condition.
if return_type != func_type:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Function '{identifier}' returns '{return_type}', but expected '{func_type}'.", line)
# AUTO: Returns this result to the caller.
return_expr, index = parse_expression_type(tokens, index, func_type)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(identifier)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{identifier}' used before declaration.", line)
# AUTO: Executes this statement.
is_member_access = var_info["type"] in symbol_table.bundle_types and tokens[index+1].type == "."
# AUTO: Checks this condition.
if not is_member_access:
# AUTO: Checks this condition.
if var_info["type"] not in [func_type, "seed", "tree"] and var_info["type"] != "seed" and var_info["type"] != "tree":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{identifier}' is of type '{var_info['type']}'. Expected return value: '{func_type}'.", line)
# AUTO: Returns this result to the caller.
return_expr, index = parse_expression_type(tokens, index, func_type)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Returns this result to the caller.
return_expr, index = parse_expression_type(tokens, index, func_type)
# AUTO: Returns this result to the caller.
return ReturnNode(return_expr, line=line), index
# AUTO: Defines function `parse_for`.
def parse_for(tokens, index, func_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Appends a value to a list.
context_stack.append("ForNode")
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after 'cultivate'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].value in {"seed", "tree", "vine", "leaf", "branch", "seed", "tree", "vine", "leaf", "branch"}:
# AUTO: Sets `var_type`.
var_type = tokens[index].value
# AUTO: Sets `var_name`.
var_name = tokens[index + 1].value
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `initialization, index`.
initialization, index = parse_variable(tokens, index, var_name, var_type)
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "id":
# AUTO: Sets `identifier_name`.
identifier_name = tokens[index].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(identifier_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{identifier_name}' used before declaration.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "=":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '=' after for loop identifier.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `initialization, index`.
initialization, index = parse_assignment(tokens, index, identifier_name, var_info["type"])
# AUTO: Checks this condition.
if tokens[index].type != ";":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ';' after for loop initialization.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `condition, index, cond_type`.
condition, index, cond_type = parse_expression_branch(tokens, index)
# AUTO: Checks this condition.
if cond_type != "branch":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: cultivate condition must be branch, got {cond_type}.", line)
# AUTO: Sets `condition_node`.
condition_node = ASTNode("Condition", line=line)
# AUTO: Calls `condition_node.add_child`.
condition_node.add_child(condition)
# AUTO: Checks this condition.
if tokens[index].type != ";":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ';' after for loop condition.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `update_node`.
update_node = ASTNode("Update", line=line)
# AUTO: Repeats while this condition is true.
while True:
# AUTO: Sets `update, index`.
update, index = parse_update(tokens, index)
# AUTO: Calls `update_node.add_child`.
update_node.add_child(update)
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Skips to the next loop iteration.
continue
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops the nearest loop.
break
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ')' after for loop update.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.enter_scope`.
symbol_table.enter_scope()
# AUTO: Sets `for_node`.
for_node = ForLoopNode(initialization, condition_node, update_node, line=line)
# AUTO: Checks this condition.
if tokens[index].type == "{":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `block_node`.
block_node = ASTNode("Block", line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `block_node.add_child`.
block_node.add_child(stmt)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.exit_scope`.
symbol_table.exit_scope()
# AUTO: Removes and returns an item.
context_stack.pop()
# AUTO: Calls `for_node.add_child`.
for_node.add_child(block_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '{{' after for loop condition.", line)
# AUTO: Returns this result to the caller.
return for_node, index
# AUTO: Defines function `parse_update`.
def parse_update(tokens, index):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if tokens[index].type == "id" or tokens[index].type in {"++", "--"}:
# AUTO: Sets `assignments_node`.
assignments_node = ASTNode("AssignmentList")
# AUTO: Repeats while this condition is true.
while True:
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(tokens[index].value)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, line)
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_type`.
var_type = var_info["type"]
# AUTO: Sets `is_list`.
is_list = var_info.get("is_list", False)
# AUTO: Sets `is_fertile`.
is_fertile = var_info.get("is_fertile", False)
# AUTO: Checks this condition.
if is_fertile:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' is declared as fertile and cannot be re-assigned a value.", line)
# AUTO: Checks this condition.
if is_list or (var_type == "vine" and tokens[index + 1].type == "["):
# AUTO: Checks this condition.
if tokens[index + 1].type == "=":
# AUTO: Sets `node, index`.
node, index = parse_list_assignment(tokens, index)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(node)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type == "[":
# AUTO: Sets `list_access_node, index`.
list_access_node, index = parse_list_access(tokens, index)
# AUTO: Checks this condition.
if tokens[index + 1].type == "=":
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `value_node, index`.
value_node, index = parse_expression_type(tokens, index, var_type)
# AUTO: Sets `assign_node`.
assign_node = AssignmentNode(list_access_node, value_node, line=tokens[index].line)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(assign_node)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type in {"++", "--"}:
# AUTO: Checks this condition.
if var_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{var_name}' of type {var_type} in expression.", line)
# AUTO: Sets `operator`.
operator = tokens[index + 1].value
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, list_access_node, "post", line`.
assignments_node.add_child(UnaryOpNode(operator, list_access_node, "post", line=line))
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError("Semantic Error: Expected '=' or '++'/'--' after list access.", tokens[index + 1].line)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type in {"++", "--"}:
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(tokens[index].value)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, line)
# AUTO: Checks this condition.
if var_info["type"] not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{tokens[index].value}' of type {var_info['type']} in expression.", line)
# AUTO: Sets `operand`.
operand = ASTNode("Identifier", tokens[index].value, line=line)
# AUTO: Sets `operator`.
operator = tokens[index + 1].value
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, operand, "post", line`.
assignments_node.add_child(UnaryOpNode(operator, operand, "post", line=line))
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type == "=":
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(var_info, tokens[index].line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `node, index`.
node, index = parse_assignment(tokens, index, var_name, var_info["type"])
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(node)
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type in {"+=", "-=", "*=", "/=", "%=", "**="}:
# AUTO: Sets `compound_op`.
compound_op = tokens[index + 1].value
# AUTO: Sets `base_op`.
base_op = compound_op[:-1]
# AUTO: Sets `cur_var_name`.
cur_var_name = tokens[index].value
# AUTO: Sets `cur_var_info`.
cur_var_info = symbol_table.lookup_variable(cur_var_name)
# AUTO: Checks this condition.
if isinstance(cur_var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(cur_var_info, line)
# AUTO: Checks this condition.
if cur_var_info.get("is_fertile", False):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{cur_var_name}' is declared as fertile and cannot be re-assigned a value.", line)
# AUTO: Sets `cur_var_type`.
cur_var_type = cur_var_info["type"]
# AUTO: Checks this condition.
if cur_var_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use compound assignment on '{cur_var_name}' of type '{cur_var_type}'.", line)
# AUTO: Checks this condition.
if base_op == "%" and cur_var_type != "seed":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Modulo operator '%' requires 'seed' (integer) operands, "
# AUTO: Executes this statement.
f"but '{cur_var_name}' is of type 'tree'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Adds into `index`.
index += 2
# AUTO: Sets `rhs_node, index, rhs_type`.
rhs_node, index, rhs_type = parse_expression(tokens, index)
# AUTO: Checks this condition.
if rhs_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Sets `f"Semantic Error: Cannot use '{base_op}`.
f"Semantic Error: Cannot use '{base_op}=' with right-hand side of type '{rhs_type}'. Expected 'seed' or 'tree'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `lhs_node`.
lhs_node = ASTNode("Identifier", cur_var_name, line=line)
# AUTO: Sets `value_node`.
value_node = BinaryOpNode(lhs_node, base_op, rhs_node, line=line)
# AUTO: Sets `assign_node`.
assign_node = AssignmentNode(cur_var_name, value_node, line=line)
# AUTO: Calls `assignments_node.add_child`.
assignments_node.add_child(assign_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Unexpected token '{tokens[index].value}' in statement.", line)
# AUTO: Checks the next alternate condition.
elif tokens[index].value in {"++", "--"}:
# AUTO: Sets `operator`.
operator = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `var_name`.
var_name = tokens[index].value
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(var_name)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' used before declaration.", line)
# AUTO: Checks this condition.
if tokens[index + 1].type == "[":
# AUTO: Checks this condition.
if var_info["type"] not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{var_name}' of type {var_info['type']} in expression.", line)
# AUTO: Sets `list_access_node, index`.
list_access_node, index = parse_list_access(tokens, index)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, list_access_node, "pre", line`.
assignments_node.add_child(UnaryOpNode(operator, list_access_node, "pre", line=line))
# AUTO: Checks the next alternate condition.
elif tokens[index + 1].type == ".":
# AUTO: Sets `obj_name`.
obj_name = tokens[index].value
# AUTO: Checks this condition.
if var_info["type"] not in symbol_table.bundle_types:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{obj_name}' is not a bundle type.", line)
# AUTO: Sets `member_name`.
member_name = tokens[index + 2].value
# AUTO: Sets `bundle_members`.
bundle_members = symbol_table.bundle_types[var_info["type"]]
# AUTO: Checks this condition.
if member_name not in bundle_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{var_info['type']}' has no member '{member_name}'.", line)
# AUTO: Sets `member_type`.
member_type = bundle_members[member_name]
# AUTO: Adds into `index`.
index += 3
# AUTO: Sets `target`.
target = MemberAccessNode(obj_name, member_name, line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type == "." and member_type in symbol_table.bundle_types:
# AUTO: Sets `next_member`.
next_member = tokens[index + 1].value
# AUTO: Sets `nested_members`.
nested_members = symbol_table.bundle_types[member_type]
# AUTO: Checks this condition.
if next_member not in nested_members:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Bundle type '{member_type}' has no member '{next_member}'.", line)
# AUTO: Sets `member_type`.
member_type = nested_members[next_member]
# AUTO: Sets `target`.
target = MemberAccessNode(target, next_member, line=line)
# AUTO: Adds into `index`.
index += 2
# AUTO: Checks this condition.
if member_type not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot apply '{operator}' to member '{member_name}' of type '{member_type}'.", line)
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, target, "pre", line`.
assignments_node.add_child(UnaryOpNode(operator, target, "pre", line=line))
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Checks this condition.
if var_info["type"] not in {"seed", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Cannot use '{var_name}' of type {var_info['type']} in expression.", line)
# AUTO: Sets `operand`.
operand = ASTNode("Identifier", tokens[index].value, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `assignments_node.add_child(UnaryOpNode(operator, operand, "pre", line`.
assignments_node.add_child(UnaryOpNode(operator, operand, "pre", line=line))
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected identifier after '{operator}'.", line)
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `token`.
token = tokens[index]
# AUTO: Skips to the next loop iteration.
continue
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops the nearest loop.
break
# AUTO: Checks this condition.
if len(assignments_node.children) > 1:
# AUTO: Returns this result to the caller.
return assignments_node, index
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Returns this result to the caller.
return assignments_node.children[0], index
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"++", "--"}:
# AUTO: Sets `operator`.
operator = tokens[index].value
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `var_name`.
var_name = symbol_table.lookup_variable(tokens[index].value)
# AUTO: Checks this condition.
if isinstance(var_name, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{var_name}' used before declaration.", line)
# AUTO: Sets `operand`.
operand = ASTNode("Identifier", tokens[index].value, line=line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return UnaryOpNode(operator, operand, "pre", line=line), index
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid update statement.", line)
# AUTO: Defines function `parse_while`.
def parse_while(tokens, index, func_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Appends a value to a list.
context_stack.append("WhileNode")
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after 'grow'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `condition, index, cond_type`.
condition, index, cond_type = parse_expression_branch(tokens, index)
# AUTO: Checks this condition.
if cond_type != "branch":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: grow condition must be branch, got {cond_type}.", line)
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ')' after 'grow' condition.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.enter_scope`.
symbol_table.enter_scope()
# AUTO: Sets `condition_node`.
condition_node = ASTNode("Condition", line=line)
# AUTO: Calls `condition_node.add_child`.
condition_node.add_child(condition)
# AUTO: Sets `while_node`.
while_node = WhileLoopNode(condition_node, line=line)
# AUTO: Checks this condition.
if tokens[index].type == "{":
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `block_node`.
block_node = ASTNode("Block", line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `block_node.add_child`.
block_node.add_child(stmt)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.exit_scope`.
symbol_table.exit_scope()
# AUTO: Removes and returns an item.
context_stack.pop()
# AUTO: Calls `while_node.add_child`.
while_node.add_child(block_node)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '{{' after 'grow' condition.", line)
# AUTO: Returns this result to the caller.
return while_node, index
# AUTO: Defines function `parse_do`.
def parse_do(tokens, index, func_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.enter_scope`.
symbol_table.enter_scope()
# AUTO: Appends a value to a list.
context_stack.append("DoWhileNode")
# AUTO: Checks this condition.
if tokens[index].type != "{":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '{{' after 'tend'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `block_node`.
block_node = ASTNode("Block", line=line)
# AUTO: Repeats while this condition is true.
while tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `block_node.add_child`.
block_node.add_child(stmt)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].value not in {"grow"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected 'grow' after 'tend' block.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after 'grow'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `condition, index, cond_type`.
condition, index, cond_type = parse_expression_branch(tokens, index)
# AUTO: Checks this condition.
if cond_type != "branch":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: tend condition must be branch, got {cond_type}.", line)
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ')' after 'grow' condition.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `do_node`.
do_node = DoWhileLoopNode(condition, line=line)
# AUTO: Sets `condition_node`.
condition_node = ASTNode("Condition", line=line)
# AUTO: Calls `condition_node.add_child`.
condition_node.add_child(condition)
# AUTO: Calls `do_node.add_child`.
do_node.add_child(block_node)
# AUTO: Calls `do_node.add_child`.
do_node.add_child(condition_node)
# AUTO: Calls `symbol_table.exit_scope`.
symbol_table.exit_scope()
# AUTO: Removes and returns an item.
context_stack.pop()
# AUTO: Returns this result to the caller.
return do_node, index
# AUTO: Defines function `parse_switch`.
def parse_switch(tokens, index, func_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Appends a value to a list.
context_stack.append("SwitchNode")
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after 'harvest'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `switch_type`.
switch_type = None
# AUTO: Checks this condition.
if tokens[index].type == "id":
# AUTO: Sets `var_info`.
var_info = symbol_table.lookup_variable(tokens[index].value)
# AUTO: Checks this condition.
if isinstance(var_info, str):
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Variable '{tokens[index].value}' used before declaration.", line)
# AUTO: Sets `var_type`.
var_type = var_info["type"]
# AUTO: Checks this condition.
if var_type in {"vine", "tree"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: 'harvest' expression must be 'seed'/'leaf'/'branch', not '{var_type}'.", line)
# AUTO: Sets `switch_type`.
switch_type = var_type
# AUTO: Sets `switch_expr, index`.
switch_expr, index = parse_expression_type(tokens, index, var_type)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"intlit", "chrlit", "sunshine", "frost"} or tokens[index].type in {"--", "++", "-", "("}:
# AUTO: Checks this condition.
if tokens[index].type == "intlit" or tokens[index].type in {"--", "++", "-"}:
# AUTO: Sets `switch_type`.
switch_type = "seed"
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "chrlit":
# AUTO: Sets `switch_type`.
switch_type = "leaf"
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"sunshine", "frost"}:
# AUTO: Sets `switch_type`.
switch_type = "branch"
# AUTO: Checks the next alternate condition.
elif tokens[index].type == "(":
# AUTO: Sets `switch_type`.
switch_type = "seed"
# AUTO: Sets `switch_expr, index, _`.
switch_expr, index, _ = parse_expression(tokens, index)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"stringlit"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: 'harvest' expression must be 'seed'/'leaf'/'branch', not 'vine'.", line)
# AUTO: Checks the next alternate condition.
elif tokens[index].type in {"dblit"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: 'harvest' expression must be 'seed'/'leaf'/'branch', not 'tree'.", line)
# AUTO: Runs when previous condition did not pass.
else:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Invalid token '{tokens[index].value}' used in expression.", line)
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ')' after 'harvest' expression.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "{":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '{{' after 'harvest' expression.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.enter_scope`.
symbol_table.enter_scope()
# AUTO: Sets `case_nodes`.
case_nodes = []
# AUTO: Sets `default_case`.
default_case = None
# AUTO: Sets `seen_case_values`.
seen_case_values = set()
# AUTO: Repeats while this condition is true.
while tokens[index].value in {"variety"}:
# AUTO: Sets `case_line`.
case_line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if tokens[index].type not in {"chrlit", "stringlit", "sunshine", "frost", "intlit", "dblit"}:
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected valid literal value after 'variety'.", line)
# AUTO: Sets `lit_type_map`.
lit_type_map = {
# AUTO: Executes this statement.
"intlit": "seed",
# AUTO: Executes this statement.
"dblit": "tree",
# AUTO: Executes this statement.
"stringlit": "vine",
# AUTO: Executes this statement.
"chrlit": "leaf",
# AUTO: Executes this statement.
"sunshine": "branch",
# AUTO: Executes this statement.
"frost": "branch",
# AUTO: Closes the current grouped code/data.
}
# AUTO: Sets `lit_type`.
lit_type = lit_type_map.get(tokens[index].type)
# AUTO: Checks this condition.
if switch_type and lit_type and lit_type != switch_type:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: 'variety' value type mismatch — expected '{switch_type}' but got '{lit_type}' ('{tokens[index].value}').",
# AUTO: Executes this statement.
tokens[index].line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `case_val_key`.
case_val_key = tokens[index].value
# AUTO: Checks this condition.
if case_val_key in seen_case_values:
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: Duplicate 'variety' value '{case_val_key}' in 'harvest'.",
# AUTO: Executes this statement.
tokens[index].line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Calls `seen_case_values.add`.
seen_case_values.add(case_val_key)
# AUTO: Sets `case_value`.
case_value = ASTNode("Value", tokens[index].value, line=case_line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ":":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ':' after 'variety' value.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `case_block`.
case_block = ASTNode("Block", line=case_line)
# AUTO: Sets `getout_node`.
getout_node = None
# AUTO: Repeats while this condition is true.
while tokens[index].value not in {"variety", "soil"} and tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `case_block.add_child`.
case_block.add_child(stmt)
# AUTO: Checks this condition.
if getout_node:
# AUTO: Checks this condition.
if tokens[index].value not in {"variety", "soil"} and tokens[index].type != "}":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Unexpected statement after 'prune' in case block.", tokens[index].line)
# AUTO: Calls `case_block.add_child`.
case_block.add_child(getout_node)
# AUTO: Sets `case_node`.
case_node = ASTNode("Case", line=case_line)
# AUTO: Calls `case_node.add_child`.
case_node.add_child(case_value)
# AUTO: Calls `case_node.add_child`.
case_node.add_child(case_block)
# AUTO: Appends a value to a list.
case_nodes.append(case_node)
# AUTO: Checks this condition.
if tokens[index].value in {"soil"}:
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ":":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ':' after 'soil'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `default_block`.
default_block = ASTNode("Block", line=line)
# AUTO: Sets `getout_node`.
getout_node = None
# AUTO: Repeats while this condition is true.
while tokens[index].type != "}":
# AUTO: Sets `stmt, index`.
stmt, index = parse_statement(tokens, index, func_type)
# AUTO: Checks this condition.
if stmt:
# AUTO: Calls `default_block.add_child`.
default_block.add_child(stmt)
# AUTO: Checks this condition.
if getout_node:
# AUTO: Checks this condition.
if tokens[index].type != "}":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Unexpected statement after 'prune' in default block.", tokens[index].line)
# AUTO: Calls `default_block.add_child`.
default_block.add_child(getout_node)
# AUTO: Sets `default_case`.
default_case = ASTNode("Default", line=line)
# AUTO: Calls `default_case.add_child`.
default_case.add_child(default_block)
# AUTO: Checks this condition.
if tokens[index].type != "}":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '}}' after 'harvest' statement.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Calls `symbol_table.exit_scope`.
symbol_table.exit_scope()
# AUTO: Removes and returns an item.
context_stack.pop()
# AUTO: Returns this result to the caller.
return SwitchNode(switch_expr, case_nodes, default_case, line=line), index
# AUTO: Defines function `parse_list`.
def parse_list(tokens, index, expected_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if tokens[index].type != "[":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected '[' for list declaration.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `elements`.
elements = []
# AUTO: Checks this condition.
if tokens[index].type == "]":
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return ListNode(elements=[], line=line), index
# AUTO: Repeats while this condition is true.
while tokens[index].type != "]":
# AUTO: Sets `expr, index`.
expr, index = parse_expression_type(tokens, index, expected_type)
# AUTO: Appends a value to a list.
elements.append(expr)
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "]":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ']' after list elements.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return ListNode(elements = elements, line = line), index
# AUTO: Defines function `parse_append`.
def parse_append(tokens, index, var_name, expected_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if tokens[index].value != "append":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected 'append'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after 'append'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `elements`.
elements = []
# AUTO: Repeats while this condition is true.
while tokens[index].type != ")":
# AUTO: Sets `elem, index`.
elem, index = parse_expression_type(tokens, index, expected_type)
# AUTO: Appends a value to a list.
elements.append(elem)
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ')' after append arguments.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return AppendNode(elements, line=line), index
# AUTO: Defines function `parse_insert`.
def parse_insert(tokens, index, var_name, expected_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if tokens[index].value != "insert":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected 'insert'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after 'insert'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `expr_node, index, idx_type`.
expr_node, index, idx_type = parse_equality(tokens, index)
# AUTO: Checks this condition.
if idx_type is not None and idx_type != "seed":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: List index must be of type 'seed', got '{idx_type}'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `index_value`.
index_value = ASTNode("Index", line=tokens[index].line)
# AUTO: Calls `index_value.add_child`.
index_value.add_child(expr_node)
# AUTO: Checks this condition.
if tokens[index].type != ",":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ',' after index in 'insert'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `elements`.
elements = []
# AUTO: Repeats while this condition is true.
while tokens[index].type != ")":
# AUTO: Sets `elem, index`.
elem, index = parse_expression_type(tokens, index, expected_type)
# AUTO: Appends a value to a list.
elements.append(elem)
# AUTO: Checks this condition.
if tokens[index].type == ",":
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ')' after insert arguments.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return InsertNode(index_value, elements, line=line), index
# AUTO: Defines function `parse_remove`.
def parse_remove(tokens, index, var_name, expected_type):
# AUTO: Sets `line`.
line = tokens[index].line
# AUTO: Checks this condition.
if tokens[index].value != "remove":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Semantic Error: Expected 'remove'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Checks this condition.
if tokens[index].type != "(":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected '(' after 'remove'.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Sets `expr_node, index, idx_type`.
expr_node, index, idx_type = parse_equality(tokens, index)
# AUTO: Checks this condition.
if idx_type is not None and idx_type != "seed":
# AUTO: Stops this flow by raising an error.
raise SemanticError(
# AUTO: Executes this statement.
f"Semantic Error: List index must be of type 'seed', got '{idx_type}'.",
# AUTO: Executes this statement.
line,
# AUTO: Closes the current grouped code/data.
)
# AUTO: Sets `index_value`.
index_value = ASTNode("Index", line=tokens[index].line)
# AUTO: Calls `index_value.add_child`.
index_value.add_child(expr_node)
# AUTO: Checks this condition.
if tokens[index].type != ")":
# AUTO: Stops this flow by raising an error.
raise SemanticError(f"Syntax Error: Expected ')' after remove argument.", line)
# AUTO: Adds into `index`.
index += 1
# AUTO: Returns this result to the caller.
return RemoveNode(var_name, index_value, line=line), index
# AUTO: Defines function `is_inside_loop_or_switch_stack`.
def is_inside_loop_or_switch_stack():
# AUTO: Returns this result to the caller.
return any(ctx in {"WhileNode", "DoWhileNode", "SwitchNode", "ForNode"} for ctx in context_stack)
# AUTO: Defines function `analyze_semantics`.
def analyze_semantics(tokens):
# GUIDE: Compatibility wrapper kept for older server paths; current server flow
# normally uses LL1Parser.parse_and_build() then semantic.validate_ast().
# AUTO: Starts protected code that can catch errors.
try:
# AUTO: Sets `filtered`.
filtered = [t for t in tokens if t.type not in ('\n', 'comment', 'mcommentlit')]
# AUTO: Sets `ast`.
ast = build_ast(filtered)
# AUTO: Sets `st`.
st = {
# AUTO: Executes this statement.
"variables": [
# AUTO: Executes this statement.
{
# AUTO: Executes this statement.
"name": name,
# AUTO: Executes this statement.
"type": info["type"],
# AUTO: Executes this statement.
"scope": "global",
# AUTO: Calls `info.get`.
"is_list": info.get("is_list", False),
# AUTO: Calls `info.get`.
"is_constant": info.get("is_fertile", False),
# AUTO: Closes the current grouped code/data.
}
# AUTO: Starts a loop over these values.
for name, info in symbol_table.variables.items()
# AUTO: Closes the current grouped code/data.
],
# AUTO: Executes this statement.
"functions": {
# AUTO: Executes this statement.
name: {
# AUTO: Executes this statement.
"return_type": info["return_type"],
# AUTO: Executes this statement.
"params": [
# AUTO: Executes this statement.
{
# AUTO: Executes this statement.
"type": p.children[0].value if p.children else "unknown",
# AUTO: Executes this statement.
"name": p.children[1].value if len(p.children) > 1 else "unknown",
# AUTO: Closes the current grouped code/data.
}
# AUTO: Starts a loop over these values.
for p in info["params"]
# AUTO: Closes the current grouped code/data.
] if info["params"] else [],
# AUTO: Closes the current grouped code/data.
}
# AUTO: Starts a loop over these values.
for name, info in symbol_table.functions.items()
# AUTO: Closes the current grouped code/data.
},
# AUTO: Closes the current grouped code/data.
}
# AUTO: Returns this result to the caller.
return {
# AUTO: Executes this statement.
"success": True,
# AUTO: Executes this statement.
"errors": [],
# AUTO: Executes this statement.
"warnings": [],
# AUTO: Executes this statement.
"symbol_table": st,
# AUTO: Executes this statement.
"ast": ast,
# AUTO: Closes the current grouped code/data.
}
# AUTO: Handles the matching error case.
except SemanticError as e:
# AUTO: Returns this result to the caller.
return {
# AUTO: Executes this statement.
"success": False,
# AUTO: Executes this statement.
"errors": [str(e)],
# AUTO: Executes this statement.
"warnings": [],
# AUTO: Executes this statement.
"symbol_table": {},
# AUTO: Executes this statement.
"ast": None,
# AUTO: Closes the current grouped code/data.
}