parser: idempotency for fuzzing, --pretty-print

This commit is contained in:
Guillem L. Jara
2026-05-03 04:12:18 +02:00
parent 9670302115
commit 08b4ead128
4 changed files with 378 additions and 3 deletions
-2
View File
@@ -127,7 +127,6 @@ pub enum BinaryOperator {
Divide,
Raise,
Modulo,
InArray,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -409,7 +408,6 @@ impl BindingPower for BinaryOperator {
Self::Divide => binding_powers::BP_MULTI,
Self::Raise => binding_powers::BP_RAISE,
Self::Modulo => binding_powers::BP_RAISE,
Self::InArray => binding_powers::BP_IN,
}
}
}
+376
View File
@@ -0,0 +1,376 @@
use std::fmt::{Debug, Display, Formatter, Result, Write};
use crate::{
Ast,
ast::{
Atom, BinaryOperator, BindingPower, Body, Command, Expr, ExprNode, PlaceOperator, Rule,
RulePattern, Statement, Ternary, UnaryOperator,
},
};
/// Bit trick to pack `{ indent: u8, bp: u8 }` inside `Formatter::width`.
fn encode(indent: u8, prec: u8) -> usize {
((indent as usize) << 8) | (prec as usize)
}
/// Bit trick to unpack the `{ indent: u8, bp: u8 }` inside `Formatter::width`.
fn decode(f: &Formatter<'_>) -> (u8, u8) {
let w = f.width().unwrap_or(0);
((w >> 8) as u8, (w & 0xFF) as u8)
}
impl Display for Ast<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
for load in &self.loads {
writeln!(f, "@load \"{load}\"")?;
}
for body in &self.begin {
write!(f, "BEGIN ")?;
write_body_ln(f, body, 0)?;
writeln!(f)?;
}
for body in &self.begin_file {
write!(f, "BEGINFILE ")?;
write_body_ln(f, body, 0)?;
writeln!(f)?;
}
for rule in &self.rules {
writeln!(f, "{rule}")?;
}
for body in &self.end_file {
write!(f, "ENDFILE ")?;
write_body_ln(f, body, 0)?;
writeln!(f)?;
}
for body in &self.end {
write!(f, "END ")?;
write_body_ln(f, body, 0)?;
writeln!(f)?;
}
Ok(())
}
}
impl Display for Body<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (indent, _) = decode(f);
for stmnt in &self.0 {
write!(f, "{stmnt:width$}", width = encode(indent, 0))?;
writeln!(f)?;
}
Ok(())
}
}
impl Display for Statement<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (indent, _) = decode(f);
let ew = encode(indent, 0);
match self {
Self::Expression(expr) => write!(f, "{expr:ew$}"),
Self::Command {
name,
args,
redirection,
} => {
write!(f, "{name} ")?;
write_args(f, args, indent)?;
if let Some(_redir) = redirection {
write!(f, "TODO")?;
}
Ok(())
}
Self::If {
condition,
then_body,
else_body,
} => {
write!(f, "if ({condition:ew$}) ")?;
write_body(f, then_body, indent)?;
if let Some(else_body) = else_body {
write!(f, " else ")?;
write_body(f, else_body, indent)?;
}
Ok(())
}
Self::While {
condition,
then_body,
} => {
write!(f, "while ({condition:ew$}) ")?;
write_body(f, then_body, indent)
}
Self::DoWhile {
then_body,
condition,
} => {
write!(f, "do ")?;
write_body(f, then_body, indent)?;
write!(f, " while ({condition:ew$})")
}
Self::For {
init,
condition,
update,
body,
} => {
write!(f, "for (")?;
if let Some(e) = init {
write!(f, "{e:ew$}")?;
}
write!(f, ";")?;
if let Some(e) = condition {
write!(f, " {e:ew$}")?;
}
write!(f, ";")?;
if let Some(e) = update {
write!(f, " {e:ew$}")?;
}
write!(f, ") ")?;
write_body(f, body, indent)
}
Self::ForEach { place, array, body } => {
write!(f, "for ({place:?} in {array:?}) ")?;
write_body(f, body, indent)
}
Self::Switch {
scrutinee,
branches,
default,
} => {
writeln!(f, "switch ({scrutinee:ew$}) {{")?;
let default_pos = default.as_ref().map_or(branches.len(), |x| x.1);
for i in 0..default_pos {
let (case, branch) = &branches[i];
tabs(f, indent)?;
writeln!(f, "case {case}:")?;
write_stmnts(f, branch, indent + 1)?;
}
if let Some((body, pos)) = default {
tabs(f, indent)?;
writeln!(f, "default:")?;
write_stmnts(f, body, indent + 1)?;
for i in *pos..branches.len() {
let (case, branch) = &branches[i];
tabs(f, indent)?;
writeln!(f, "case {case}:")?;
write_stmnts(f, branch, indent + 1)?;
}
}
tabs(f, indent)?;
f.write_char('}')
}
Self::Break => write!(f, "break"),
Self::Continue => write!(f, "continue"),
Self::Return(Some(expr)) => write!(f, "return {expr:ew$}"),
Self::Return(None) => write!(f, "return"),
}
}
}
impl Display for Rule<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match (&self.pattern, &self.actions) {
(None, None) => Ok(()),
(None, Some(body)) => write_body_ln(f, body, 0),
(Some(pat), None) => writeln!(f, "{pat};"),
(Some(pat), Some(body)) => {
write!(f, "{pat} ")?;
write_body_ln(f, body, 0)
}
}
}
}
impl Display for RulePattern<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::Range(a, b) => write!(f, "{a}, {b}"),
Self::Expression(x) => write!(f, "{x}"),
}
}
}
impl Display for Expr<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (indent, prec) = decode(f);
match self {
Expr::Leaf(atom) => write!(f, "{atom}"),
Expr::Node(node) => write!(f, "{node:width$}", width = encode(indent, prec)),
}
}
}
impl Display for Atom<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
<Self as Debug>::fmt(self, f)
}
}
impl Display for ExprNode<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (indent, parent_bp) = decode(f);
match self {
Self::FunctionCall(fun, args) => {
write!(f, "{fun}(")?;
write_args(f, args, indent)?;
write!(f, ")")
}
Self::UnaryOperation(op, x) => {
let bp = op.binding_power();
let child_w = encode(indent, bp.saturating_add(1));
if bp < parent_bp {
write!(f, "({op}{x:child_w$})")
} else {
write!(f, "{op}{x:child_w$}")
}
}
Self::BinaryOperation(op, a, b) => {
let (left_bp, right_bp) = op.binding_power();
let left_w = encode(indent, left_bp);
let right_w = encode(indent, right_bp);
if left_bp < parent_bp {
write!(f, "({a:left_w$}{op}{b:right_w$})")
} else {
write!(f, "{a:left_w$}{op}{b:right_w$}")
}
}
Self::PlaceOperation(op, place, idx) => {
let (left_bp, right_bp) = op.binding_power();
if op == &PlaceOperator::ArrayAccess {
let idx_w = encode(indent, 0);
if left_bp < parent_bp {
write!(f, "({place:?}[{idx:idx_w$}])")
} else {
write!(f, "{place:?}[{idx:idx_w$}]")
}
} else {
let right_w = encode(indent, right_bp);
if left_bp < parent_bp {
write!(f, "({place:?}{op}{idx:right_w$})")
} else {
write!(f, "{place:?}{op}{idx:right_w$}")
}
}
}
Self::Ternary(cond, then_expr, else_expr) => {
let ternary_bp = Ternary.binding_power().0;
let inner_w = encode(indent, 0);
if ternary_bp < parent_bp {
write!(
f,
"({cond:inner_w$} ? {then_expr:inner_w$} : {else_expr:inner_w$})"
)
} else {
write!(
f,
"{cond:inner_w$} ? {then_expr:inner_w$} : {else_expr:inner_w$}"
)
}
}
}
}
}
impl Display for UnaryOperator {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::Record => write!(f, "$"),
Self::Negation => write!(f, "!"),
Self::ToInt => write!(f, "+"),
Self::Negative => write!(f, "-"),
}
}
}
impl Display for BinaryOperator {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::Concat => write!(f, " "),
Self::Eq => write!(f, " == "),
Self::NEq => write!(f, " != "),
Self::Gt => write!(f, " > "),
Self::Lt => write!(f, " < "),
Self::LtE => write!(f, " <= "),
Self::GtE => write!(f, " >= "),
Self::And => write!(f, " && "),
Self::Or => write!(f, " || "),
Self::MatchRegex => write!(f, " ~ "),
Self::NotMatchRegex => write!(f, " !~ "),
Self::Add => write!(f, " + "),
Self::Subtract => write!(f, " - "),
Self::Multiply => write!(f, " * "),
Self::Divide => write!(f, " / "),
Self::Raise => write!(f, " ^ "),
Self::Modulo => write!(f, " % "),
}
}
}
impl Display for PlaceOperator {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::Assignment => write!(f, " = "),
Self::InArray => write!(f, " in "),
Self::ArrayAccess => unreachable!("Array access needs special handling."),
}
}
}
impl Display for Command {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::Print => write!(f, "print"),
Self::Printf => write!(f, "printf"),
Self::Getline => write!(f, "getline"),
Self::Next => write!(f, "next"),
Self::NextFile => write!(f, "nextfile"),
Self::Exit => write!(f, "exit"),
}
}
}
fn write_args(f: &mut Formatter<'_>, args: &[Expr<'_>], indent: u8) -> Result {
let ew = encode(indent, 0);
for (i, arg) in args.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "{arg:ew$}")?;
}
Ok(())
}
fn write_stmnts(f: &mut Formatter<'_>, body: &Body, indent: u8) -> Result {
let sw = encode(indent, 0);
for stmnt in &body.0 {
tabs(f, indent)?;
writeln!(f, "{stmnt:sw$}")?;
}
Ok(())
}
fn write_body(f: &mut Formatter<'_>, body: &Body, indent: u8) -> Result {
writeln!(f, "{{")?;
write_stmnts(f, body, indent + 1)?;
tabs(f, indent)?;
f.write_char('}')
}
fn write_body_ln(f: &mut Formatter<'_>, body: &Body, indent: u8) -> Result {
write_body(f, body, indent)?;
writeln!(f)
}
fn tabs(f: &mut Formatter<'_>, indent: u8) -> Result {
for _ in 0..indent {
f.write_char('\t')?;
}
Ok(())
}
+1
View File
@@ -6,6 +6,7 @@
mod ast;
mod diagnostics;
mod idempotency;
mod lex;
mod sexpr;
#[cfg(test)]
+1 -1
View File
@@ -38,7 +38,7 @@ fn uu_main() -> Result<()> {
return Ok(());
}
};
println!("{:?}", ast.rules);
println!("{ast}");
dbg!(arena.chunk_capacity());
// for token in lex {