pub fn render(program: &Program) -> StringExpand 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.