CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-tatsu

TatSu takes a grammar in a variation of EBNF as input, and outputs a memoizing PEG/Packrat parser in Python.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

tree-walking.mddocs/

Tree Walking

Traverse and transform parse trees using visitor patterns with pre-order, depth-first, and context-aware walking strategies. TatSu provides a comprehensive tree walking framework for AST analysis, transformation, and code generation.

Capabilities

Base Walker Classes

Foundation classes for implementing tree traversal patterns with automatic method dispatch and flexible visiting strategies.

class NodeWalker:
    """
    Base tree walker with method dispatch.
    
    Features:
    - Automatic method dispatch based on node types
    - Flexible visiting patterns
    - Extensible for custom node types
    - Exception handling during traversal
    """
    
    def walk(self, node):
        """
        Walk a parse tree starting from the given node.
        
        Parameters:
        - node: Root node to start walking from
        
        Returns:
        Result of walking the tree (implementation dependent)
        """
    
    def walk_children(self, node):
        """
        Walk all children of the current node.
        
        Parameters:
        - node: Parent node whose children to walk
        
        Returns:
        List of results from walking each child
        """
    
    def _find_walker(self, node):
        """
        Find appropriate walker method for a node type.
        
        Parameters:
        - node: Node to find walker for
        
        Returns:
        Method: Walker method for the node type
        """

Usage example:

import tatsu
from tatsu.walkers import NodeWalker
from tatsu.semantics import ModelBuilderSemantics

grammar = '''
    expr = term ("+" term)*;
    term = factor ("*" factor)*;
    factor = "(" expr ")" | number;
    number = /\d+/;
'''

class ExpressionWalker(NodeWalker):
    """Walk expression trees and collect information."""
    
    def walk_expr(self, node):
        """Handle expression nodes."""
        print(f"Found expression: {node}")
        return self.walk_children(node)
    
    def walk_term(self, node):
        """Handle term nodes."""
        print(f"Found term: {node}")
        return self.walk_children(node)
    
    def walk_number(self, node):
        """Handle number nodes."""
        print(f"Found number: {node}")
        return int(str(node))

# Usage
model = tatsu.compile(grammar)
ast = model.parse("2 + 3 * 4", semantics=ModelBuilderSemantics())

walker = ExpressionWalker()
result = walker.walk(ast)

Pre-Order Walker

Traverse trees in pre-order (parent before children), suitable for top-down analysis and code generation.

class PreOrderWalker(NodeWalker):
    """
    Pre-order tree traversal walker.
    
    Visits parent nodes before their children, making it suitable for:
    - Top-down analysis
    - Code generation with forward declarations
    - Dependency resolution
    - Symbol table construction
    """

Usage example:

from tatsu.walkers import PreOrderWalker

class CodeGenerator(PreOrderWalker):
    """Generate code using pre-order traversal."""
    
    def __init__(self):
        self.code = []
        self.indent_level = 0
    
    def walk_function_def(self, node):
        """Generate function definition."""
        self.emit(f"def {node.name}({', '.join(node.params)}):")
        self.indent_level += 1
        
        # Walk children (function body)
        self.walk_children(node)
        
        self.indent_level -= 1
        return node
    
    def walk_assignment(self, node):
        """Generate assignment statement.""" 
        self.emit(f"{node.target} = {self.evaluate_expr(node.value)}")
        return node
    
    def emit(self, code):
        """Emit code with proper indentation."""
        indent = "    " * self.indent_level
        self.code.append(f"{indent}{code}")
    
    def get_code(self):
        """Get generated code as string."""
        return "\n".join(self.code)

# Usage
generator = CodeGenerator()
generator.walk(ast)
print(generator.get_code())

Depth-First Walker

Traverse trees depth-first (children before parent), ideal for bottom-up analysis and evaluation.

class DepthFirstWalker(NodeWalker):
    """
    Depth-first tree traversal walker.
    
    Visits child nodes before their parents, making it suitable for:
    - Bottom-up evaluation
    - Type inference
    - Optimization passes
    - Dependency analysis
    - Post-order processing
    """

Usage example:

from tatsu.walkers import DepthFirstWalker

class ExpressionEvaluator(DepthFirstWalker):
    """Evaluate arithmetic expressions using depth-first traversal."""
    
    def walk_number(self, node):
        """Evaluate number literals."""
        return int(str(node))
    
    def walk_binary_op(self, node):
        """Evaluate binary operations."""
        # Children are evaluated first (depth-first)
        left = self.walk(node.left)
        right = self.walk(node.right)
        
        if node.operator == '+':
            return left + right
        elif node.operator == '*':
            return left * right
        elif node.operator == '-':
            return left - right
        elif node.operator == '/':
            return left / right
        else:
            raise ValueError(f"Unknown operator: {node.operator}")
    
    def walk_expr(self, node):
        """Handle expression nodes."""
        if hasattr(node, 'terms'):
            result = self.walk(node.terms[0]) 
            for i in range(1, len(node.terms), 2):
                op = node.terms[i]
                operand = self.walk(node.terms[i + 1])
                if op == '+':
                    result += operand
                elif op == '-':
                    result -= operand
            return result
        return self.walk_children(node)

# Usage
evaluator = ExpressionEvaluator()
result = evaluator.walk(ast)
print(f"Expression evaluates to: {result}")

Context-Aware Walker

Maintain context and state during tree traversal for advanced analysis requiring scope and environment tracking.

class ContextWalker(NodeWalker):
    """
    Context-aware tree walking with stack management.
    
    Features:
    - Context stack management
    - Scope tracking
    - State preservation across traversal
    - Environment and symbol table support
    """
    
    def get_node_context(self, node):
        """
        Get context information for a specific node.
        
        Parameters:
        - node: Node to get context for
        
        Returns:
        Context information for the node
        """
    
    def enter_context(self, context):
        """
        Enter a new context scope.
        
        Parameters:
        - context: Context object to enter
        """
    
    def leave_context(self):
        """
        Leave the current context scope.
        
        Returns:
        Previous context that was left
        """
    
    def push_context(self, context):
        """
        Push context onto the context stack.
        
        Parameters:
        - context: Context to push
        """
    
    def pop_context(self):
        """
        Pop context from the context stack.
        
        Returns:
        Context that was popped
        """

Usage example:

from tatsu.walkers import ContextWalker

class SymbolTableWalker(ContextWalker):
    """Build symbol table with scope awareness."""
    
    def __init__(self):
        super().__init__()
        self.scopes = [{}]  # Stack of symbol tables
        self.current_scope = self.scopes[-1]
    
    def enter_scope(self):
        """Enter a new lexical scope."""
        new_scope = {}
        self.scopes.append(new_scope)
        self.current_scope = new_scope
    
    def leave_scope(self):
        """Leave current lexical scope."""
        if len(self.scopes) > 1:
            self.scopes.pop()
            self.current_scope = self.scopes[-1]
    
    def walk_function_def(self, node):
        """Handle function definitions with new scope."""
        # Add function to current scope
        self.current_scope[node.name] = {
            'type': 'function',
            'params': node.params,
            'node': node
        }
        
        # Enter function scope for parameters and body
        self.enter_scope()
        
        # Add parameters to function scope
        for param in node.params:
            self.current_scope[param] = {
                'type': 'parameter', 
                'node': node
            }
        
        # Walk function body
        self.walk_children(node.body)
        
        # Leave function scope
        self.leave_scope()
        
        return node
    
    def walk_variable_ref(self, node):
        """Handle variable references with scope resolution."""
        var_name = str(node)
        
        # Search scopes from innermost to outermost
        for scope in reversed(self.scopes):
            if var_name in scope:
                return scope[var_name]
        
        # Variable not found in any scope
        raise NameError(f"Undefined variable: {var_name}")
    
    def walk_assignment(self, node):
        """Handle variable assignments."""
        var_name = str(node.target)
        
        # Walk the value expression first
        value_info = self.walk(node.value)
        
        # Add variable to current scope
        self.current_scope[var_name] = {
            'type': 'variable',
            'value': value_info,
            'node': node
        }
        
        return node

# Usage
symbol_walker = SymbolTableWalker()
symbol_walker.walk(ast)

# Access symbol table
print("Global scope:", symbol_walker.scopes[0])

Custom Walker Patterns

Advanced patterns for specialized tree walking needs.

class MultiPassWalker(NodeWalker):
    """Walker that makes multiple passes over the tree."""
    
    def __init__(self, passes):
        """
        Initialize multi-pass walker.
        
        Parameters:
        - passes: List of pass functions to execute
        """
        self.passes = passes
        self.current_pass = 0
    
    def walk_multi_pass(self, node):
        """Execute all passes on the tree."""
        results = []
        for i, pass_func in enumerate(self.passes):
            self.current_pass = i
            result = pass_func(node)
            results.append(result)
        return results

class CollectingWalker(NodeWalker):
    """Walker that collects nodes matching specific criteria."""
    
    def __init__(self, predicate):
        """
        Initialize collecting walker.
        
        Parameters:
        - predicate: Function that returns True for nodes to collect
        """
        self.predicate = predicate
        self.collected = []
    
    def walk(self, node):
        """Walk tree and collect matching nodes."""
        if self.predicate(node):
            self.collected.append(node)
        
        return self.walk_children(node)
    
    def get_collected(self):
        """Get list of collected nodes."""
        return self.collected

class TransformingWalker(NodeWalker):
    """Walker that transforms the tree structure."""
    
    def walk(self, node):
        """Walk and transform nodes."""
        # Transform current node
        transformed = self.transform_node(node)
        
        # Transform children
        if hasattr(transformed, 'children'):
            new_children = []
            for child in transformed.children():
                new_child = self.walk(child)
                new_children.append(new_child)
            transformed.set_children(new_children)
        
        return transformed
    
    def transform_node(self, node):
        """Override to implement node transformation."""
        return node

# Usage examples
def is_number_node(node):
    return hasattr(node, '__class__') and 'number' in node.__class__.__name__.lower()

# Collect all number nodes
collector = CollectingWalker(is_number_node)
collector.walk(ast)
numbers = collector.get_collected()
print(f"Found {len(numbers)} number nodes")

# Multi-pass analysis
def pass1_collect_functions(ast):
    """First pass: collect function definitions."""
    # Implementation here
    pass

def pass2_resolve_calls(ast):
    """Second pass: resolve function calls."""
    # Implementation here  
    pass

multi_walker = MultiPassWalker([pass1_collect_functions, pass2_resolve_calls])
results = multi_walker.walk_multi_pass(ast)

Error Handling in Walkers

class SafeWalker(NodeWalker):
    """Walker with comprehensive error handling."""
    
    def __init__(self):
        self.errors = []
        self.warnings = []
    
    def walk(self, node):
        """Walk with error handling."""
        try:
            return super().walk(node)
        except Exception as e:
            self.errors.append({
                'node': node,
                'error': e,
                'message': str(e)
            })
            return None  # Continue walking despite errors
    
    def walk_children(self, node):
        """Walk children with individual error handling."""
        results = []
        for child in getattr(node, 'children', lambda: [])():
            try:
                result = self.walk(child)
                results.append(result)
            except Exception as e:
                self.warnings.append({
                    'parent': node,
                    'child': child,
                    'error': e
                })
                # Continue with other children
        return results
    
    def has_errors(self):
        """Check if any errors occurred during walking."""
        return len(self.errors) > 0
    
    def get_error_report(self):
        """Get detailed error report."""
        report = []
        for error in self.errors:
            report.append(f"Error at {error['node']}: {error['message']}")
        for warning in self.warnings:
            report.append(f"Warning at {warning['parent']}: {warning['error']}")
        return "\n".join(report)

# Usage with error handling
safe_walker = SafeWalker()
result = safe_walker.walk(ast)

if safe_walker.has_errors():
    print("Errors occurred during walking:")
    print(safe_walker.get_error_report())
else:
    print("Walking completed successfully")

Install with Tessl CLI

npx tessl i tessl/pypi-tatsu

docs

ast-models.md

code-generation.md

configuration.md

core-parsing.md

exceptions.md

index.md

semantic-actions.md

tree-walking.md

tile.json