Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Round-trip Printing

Implement ToTokens to convert AST back to formatted output.

Basic Pattern

impl ToTokens for MyNode {
    fn write(&self, p: &mut Printer) {
        self.child1.value.write(p);
        p.space();
        self.child2.value.write(p);
    }
}

Token Printing

// Custom ToTokens implementations for tokens that need special handling.
// Other token ToTokens are auto-generated by parser_kit!

impl ToTokens for tokens::BasicStringToken {
    fn write(&self, p: &mut Printer) {
        // BasicString stores content without quotes, so we add them back for round-trip
        p.word("\"");
        p.word(&self.0);
        p.word("\"");
    }
}

impl ToTokens for tokens::NewlineToken {
    fn write(&self, p: &mut Printer) {
        p.newline();
    }
}

Note: BasicStringToken strips quotes during lexing, so we re-add them for output.

Trivia

Preserve newlines and comments:

impl ToTokens for Trivia {
    fn write(&self, p: &mut Printer) {
        match self {
            Trivia::Newline(nl) => nl.value.write(p),
            Trivia::Comment(c) => c.value.write(p),
        }
    }
}

Key-Value Pairs

impl ToTokens for KeyValue {
    fn write(&self, p: &mut Printer) {
        self.key.value.write(p);
        p.space();
        self.eq.value.write(p);
        p.space();
        self.value.value.write(p);
    }
}

Spacing around = is a style choice—adjust as needed.

Arrays

Handle items with optional trailing commas:

impl ToTokens for ArrayItem {
    fn write(&self, p: &mut Printer) {
        self.value.value.write(p);
        if let Some(comma) = &self.comma {
            comma.value.write(p);
            p.space();
        }
    }
}

impl ToTokens for Array {
    fn write(&self, p: &mut Printer) {
        self.lbracket.value.write(p);
        for item in &self.items {
            item.write(p);
        }
        self.rbracket.value.write(p);
    }
}

Tables

impl ToTokens for TableItem {
    fn write(&self, p: &mut Printer) {
        match self {
            TableItem::Trivia(trivia) => trivia.write(p),
            TableItem::KeyValue(kv) => kv.value.write(p),
        }
    }
}

impl ToTokens for Table {
    fn write(&self, p: &mut Printer) {
        self.lbracket.value.write(p);
        self.name.value.write(p);
        self.rbracket.value.write(p);
        for item in &self.items {
            item.write(p);
        }
    }
}

Documents

impl ToTokens for DocumentItem {
    fn write(&self, p: &mut Printer) {
        match self {
            DocumentItem::Trivia(trivia) => trivia.write(p),
            DocumentItem::KeyValue(kv) => kv.value.write(p),
            DocumentItem::Table(table) => table.value.write(p),
        }
    }
}

impl ToTokens for Document {
    fn write(&self, p: &mut Printer) {
        for item in &self.items {
            item.write(p);
        }
    }
}

Using the Output

// Parse
let mut stream = TokenStream::lex(input)?;
let doc: Spanned<Document> = stream.parse()?;

// Print using trait method
let output = doc.value.to_string_formatted();

// Or manual printer
let mut printer = Printer::new();
doc.value.write(&mut printer);
let output = printer.finish();

Printer Methods Reference

MethodEffect
word(s)Append string
token(&tok)Append token’s display
space()Single space
newline()Line break
open_block()Indent + newline
close_block()Dedent + newline
indent()Increase indent
dedent()Decrease indent
write_separated(&items, sep)Items with separator

Formatting Choices

The ToTokens implementation defines your output format:

// Compact: key=value
self.key.value.write(p);
self.eq.value.write(p);
self.value.value.write(p);

// Spaced: key = value
self.key.value.write(p);
p.space();
self.eq.value.write(p);
p.space();
self.value.value.write(p);

For exact round-trip, store original spacing as trivia. For normalized output, apply consistent rules in write().