Extract values from a parse tree

Parsing gives you a tree of Node objects. There are two common ways to pull data out of it: a quick manual traversal, or a NodeVisitor for anything structured.

Note

Treat a returned parse tree as read-only. Cache hits can cause trees from repeated parses of the same input to share descendant Node objects, so mutating one tree in place can affect another.

Quick traversal

For a one-off lookup, walk the tree yourself. A node’s name is its rule name and value is the source text it matched:

from abnf.grammars import rfc5322

def find_value(node, rule_name):
    """Breadth-first search for the first node with the given rule name."""
    queue = [node]
    while queue:
        n, queue = queue[0], queue[1:]
        if n.name == rule_name:
            return n.value
        queue.extend(n.children)
    return None

node = rfc5322.Rule("address").parse_all("John Doe <jdoe@example.com>")
find_value(node, "addr-spec")   # 'jdoe@example.com'

NodeVisitor

For anything beyond a single lookup, subclass NodeVisitor. Define a visit_<rule_name> method for each rule you care about (hyphens in rule names become underscores); dispatch is reflective, and visit(node) routes to the right method as you walk. This example pulls the scheme and token out of an HTTP Authorization header:

from abnf import NodeVisitor
from abnf.grammars import rfc7235

class AuthVisitor(NodeVisitor):
    def __init__(self):
        super().__init__()
        self.auth_scheme = None
        self.token = None

    def visit_authorization(self, node):
        for child in node.children:
            self.visit(child)

    def visit_credentials(self, node):
        for child in node.children:
            self.visit(child)

    def visit_auth_scheme(self, node):
        self.auth_scheme = node.value

    def visit_token68(self, node):
        self.token = node.value

node, _ = rfc7235.Rule("Authorization").parse("Basic YWxhZGRpbjpvcGVuc2VzYW1l", 0)
visitor = AuthVisitor()
visitor.visit(node)

visitor.auth_scheme   # 'Basic'
visitor.token         # 'YWxhZGRpbjpvcGVuc2VzYW1l'