Skip to main content

render

Function render 

Source
pub fn render(program: &Program) -> String
Expand description

Render a program as canonical STIX pattern text.

The output is canonical rather than a reproduction of any original source: whitespace is normalized, != is the canonical spelling of not-equal, and parentheses appear only where operator precedence requires them. (The grammar’s other spelling, <>, is accepted by the lexer and rendered as !=.)

For a program lowered from parse, reparsing this output recovers the same AST up to spans, including non-ASCII string literals:

use stix_pattern::{parse, ir};

let ast = parse("[file:size>1024] OR [file:name='a']").unwrap();
let text = ir::render(&ir::lower(&ast));
assert_eq!(text, "[file:size > 1024] OR [file:name = 'a']");
assert_eq!(parse(&text).unwrap().without_spans(), ast.without_spans());

The guarantee assumes the AST came from the parser: a hand-built Comparison using EXISTS with an operand other than Literal::Boolean(true) — the placeholder the parser writes — loses that operand, since the IR records EXISTS as having none.

Hand-built programs that share one value between two consumers render that subexpression once per use, since pattern text has no way to name a shared value. Program::validate rejects those, so check any program that did not come from lower before rendering it.

Rendering walks the program with an explicit work stack rather than by recursion, so nesting depth is limited only by memory, never by the call stack. It also terminates on a cyclic program, which validate rejects, by skipping any operand that names one of its own ancestors.