feat(parser): Rework S-expr printing

This commit is contained in:
Guillem L. Jara
2026-04-30 03:31:08 +02:00
parent 70e5ec08f1
commit 8d2d4b4f0d
4 changed files with 287 additions and 139 deletions
+12 -2
View File
@@ -2,7 +2,11 @@
mod tests;
use core::str;
use std::{borrow::Cow, fmt::Debug, slice::SliceIndex};
use std::{
borrow::Cow,
fmt::{Debug, Display},
slice::SliceIndex,
};
use logos::Skip;
pub use logos::{Logos, Span, SpannedIter};
@@ -396,9 +400,15 @@ fn accept_operator(lex: &mut Lexer<'_>) {
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Slice<'a>(Cow<'a, [u8]>);
impl Display for Slice<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&self.0).as_ref())
}
}
impl Debug for Slice<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\"{}\"", String::from_utf8_lossy(&self.0).as_ref())
write!(f, "\"{self}\"")
}
}
+11 -128
View File
@@ -25,7 +25,7 @@ pub struct Rule<'a> {
pub actions: Option<Body<'a>>,
}
#[derive(Debug, Clone)]
#[derive(Clone)]
pub enum Atom<'a> {
Variable(Variable<'a>),
String(Slice<'a>),
@@ -55,7 +55,7 @@ pub struct Identifier<'a> {
pub literal: &'a str,
}
#[derive(Debug, Clone, Copy)]
#[derive(Clone, Copy)]
pub enum Variable<'a> {
User(Identifier<'a>),
Nr,
@@ -82,7 +82,8 @@ pub enum Expr<'a> {
Node(&'a ExprNode<'a>),
}
pub type Body<'a> = Vec<'a, Statement<'a>>;
#[derive(Clone)]
pub struct Body<'a>(pub Vec<'a, Statement<'a>>);
pub type Pattern<'a> = Either<RulePattern<'a>, SpecialPattern>;
#[derive(Debug, Clone)]
@@ -436,13 +437,19 @@ impl BindingPower for Ternary {
}
}
impl<'a> From<Vec<'a, Statement<'a>>> for Body<'a> {
fn from(value: Vec<'a, Statement<'a>>) -> Self {
Self(value)
}
}
impl Debug for Expr<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Leaf(atom) => write!(f, "{atom:?}"),
Self::Node(expr) => match expr {
ExprNode::FunctionCall(ident, args) => {
write!(f, "(call Identifier({ident:?})")?;
write!(f, "({ident:?}")?;
for arg in args {
write!(f, " {arg:?}")?;
}
@@ -456,127 +463,3 @@ impl Debug for Expr<'_> {
}
}
}
struct ListLispFmt<'a, T: Debug>(&'a [T]);
struct ListLispCasesFmt<'a, I: Debug, T: Debug>(&'a [(I, Vec<'a, T>)]);
impl Debug for Statement<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Statement::Expression(expr) => write!(f, "{expr:?}"),
Self::Command {
name,
args,
redirection,
} => {
if let Some(rx) = redirection {
write!(f, "(redir {rx:?} ({name:?}{:?}))", ListLispFmt(args))
} else {
write!(f, "({name:?}{:?})", ListLispFmt(args))
}
}
Self::If {
condition,
then_body,
else_body,
} => {
if let Some(else_body) = else_body {
write!(
f,
"(if {condition:?} (body{:?}) Some(body{:?}))",
ListLispFmt(then_body),
ListLispFmt(else_body)
)
} else {
write!(
f,
"(if {condition:?} (body{:?}) None)",
ListLispFmt(then_body)
)
}
}
Self::While {
condition,
then_body,
} => write!(
f,
"(while {condition:?} (body{:?}))",
ListLispFmt(then_body)
),
Self::DoWhile {
then_body,
condition,
} => write!(
f,
"(do-while (body{:?}) {condition:?})",
ListLispFmt(then_body)
),
Self::For {
init,
condition,
update,
body,
} => {
write!(
f,
"(for {init:?} {condition:?} {update:?} (body{:?}))",
ListLispFmt(body)
)
}
Self::ForEach { place, array, body } => {
write!(
f,
"(for-each {place:?} {array:?} (body{:?}))",
ListLispFmt(body)
)
}
Self::Switch {
scrutinee,
branches,
default,
} => {
if let Some((dx, i)) = default {
write!(
f,
"(switch {scrutinee:?} (cases{:?}) (body{:?}) {i})",
ListLispCasesFmt(branches),
ListLispFmt(dx)
)
} else {
write!(
f,
"(switch {scrutinee:?} (cases{:?}))",
ListLispCasesFmt(branches)
)
}
}
Self::Continue => write!(f, "(continue)"),
Self::Break => write!(f, "(break)"),
Self::Return(expr) => write!(f, "(return {expr:?})"),
}
}
}
impl<T: Debug> Debug for ListLispFmt<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for e in self.0 {
write!(f, " {e:?}")?;
}
Ok(())
}
}
impl<I: Debug, T: Debug> Debug for ListLispCasesFmt<'_, I, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (i, e) in self.0 {
write!(f, " (case {i:?} (eval{:?})", ListLispFmt(e))?;
}
Ok(())
}
}
impl Debug for Identifier<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}::{}", self.namespace, self.literal)
}
}
+15 -9
View File
@@ -1,5 +1,6 @@
mod ast;
mod lex;
mod sexpr;
use std::{fmt::Debug, mem::replace};
@@ -173,7 +174,7 @@ impl<'a> Parser<'a> {
match lex.expect_next()? {
Token::ClosedBrace => {
if depth == 0 {
break Ok(body);
break Ok(Body(body));
}
depth -= 1;
}
@@ -238,7 +239,7 @@ impl<'a> Parser<'a> {
return Err(ParsingError::UnexpectedToken);
};
lex.expect(&Token::ClosedParent)?;
let body: Vec<'_, Statement<'_>> = self.parse_statement_body(lex)?;
let body = self.parse_statement_body(lex)?;
Statement::ForEach {
place: *place,
array: *array,
@@ -259,12 +260,14 @@ impl<'a> Parser<'a> {
match case.take() {
Some(Right(())) => {
default = Some((
replace(&mut body, Vec::new_in(self.arena)),
replace(&mut body, Vec::new_in(self.arena)).into(),
branches.len(),
));
}
Some(Left(atom)) => branches
.push((atom, replace(&mut body, Vec::new_in(self.arena)))),
Some(Left(atom)) => branches.push((
atom,
replace(&mut body, Vec::new_in(self.arena)).into(),
)),
_ => {}
}
case = Some(Left(self.parse_case(lex)?));
@@ -273,7 +276,10 @@ impl<'a> Parser<'a> {
if default.is_some() || matches!(case, Some(Right(()))) {
return Err(ParsingError::UnexpectedToken);
} else if let Some(Left(atom)) = case {
branches.push((atom, replace(&mut body, Vec::new_in(self.arena))));
branches.push((
atom,
replace(&mut body, Vec::new_in(self.arena)).into(),
));
}
case = Some(Right(()));
} else {
@@ -285,8 +291,8 @@ impl<'a> Parser<'a> {
}
}
match case.take() {
Some(Right(())) => default = Some((body, branches.len())),
Some(Left(atom)) => branches.push((atom, body)),
Some(Right(())) => default = Some((body.into(), branches.len())),
Some(Left(atom)) => branches.push((atom, body.into())),
_ => {}
}
@@ -360,7 +366,7 @@ impl<'a> Parser<'a> {
if lex.peek_is(&Token::OpenBrace) {
self.parse_body(lex)
} else {
Ok(vec![in self.arena; self.parse_statement(lex)?])
Ok(vec![in self.arena; self.parse_statement(lex)?].into())
}
}
+249
View File
@@ -0,0 +1,249 @@
use std::fmt::{Debug, Formatter, Result};
use crate::ast::{Atom, Body, Identifier, Statement, Variable};
const PRETTY_PRINT_INDENT: usize = 2;
fn fmt_vars(f: &mut Formatter<'_>) -> (bool, usize, String) {
let ni = f.width().unwrap_or(0) + PRETTY_PRINT_INDENT;
(f.alternate(), ni, " ".repeat(ni))
}
impl Debug for Statement<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (alt, ni, pad) = fmt_vars(f);
match self {
Statement::Expression(expr) => {
if alt {
write!(f, "{expr:#ni$?}")
} else {
write!(f, "{expr:?}")
}
}
Self::Command {
name,
args,
redirection,
} => {
if let Some(rx) = redirection {
if alt {
write!(
f,
"(redir {rx:?}\n{pad}({name:?}{:#width$?}))",
ListLispFmt(args),
width = ni
)
} else {
write!(f, "(redir {rx:?} ({name:?}{:?}))", ListLispFmt(args))
}
} else {
if alt {
write!(f, "({name:?}{:#width$?})", ListLispFmt(args), width = ni)
} else {
write!(f, "({name:?}{:?}", ListLispFmt(args))
}
}
}
Self::If {
condition,
then_body,
else_body,
} => {
if alt {
write!(f, "(if {condition:?}\n{pad}")?;
write!(f, "{then_body:#ni$?}")?;
if let Some(else_body) = else_body {
write!(f, "\n{pad}Some({else_body:#ni$?})")
} else {
write!(f, "\n{pad}None)")
}
} else if let Some(else_body) = else_body {
write!(f, "(if {condition:?} {then_body:?} Some({else_body:?})")
} else {
write!(f, "(if {condition:?} {then_body:?} None)")
}
}
Self::While {
condition,
then_body,
} => {
if alt {
write!(f, "(while {condition:?}\n{pad}{then_body:#ni$?})")
} else {
write!(f, "(while {condition:?} {then_body:?})")
}
}
Self::DoWhile {
then_body,
condition,
} => {
if alt {
write!(f, "(do-while\n{pad}{then_body:#ni$?}\n{pad}{condition:?})")
} else {
write!(f, "(do-while {then_body:?} {condition:?})")
}
}
Self::For {
init,
condition,
update,
body,
} => {
if alt {
write!(
f,
"(for\n{pad}{init:?}\n{pad}{condition:?}\n{pad}{update:?}\n{pad}{body:#ni$?})"
)
} else {
write!(f, "(for {init:?} {condition:?} {update:?} {body:?})")
}
}
Self::ForEach { place, array, body } => {
if alt {
write!(f, "(for-each {place:?} {array:?}\n{pad}{body:#ni$?})")
} else {
write!(f, "(for-each {place:?} {array:?} {body:?})")
}
}
Self::Switch {
scrutinee,
branches,
default,
} => {
if alt {
if let Some((dx, i)) = default {
write!(
f,
"(switch {scrutinee:?}\n{pad}(cases{:#width$?})\n{pad}Some({dx:?}) {i})",
ListLispCasesFmt(branches.as_slice()),
width = ni
)
} else {
write!(
f,
"(switch {scrutinee:?}\n{pad}(cases{:#width$?}))",
ListLispCasesFmt(branches),
width = ni
)
}
} else {
if let Some((dx, i)) = default {
write!(
f,
"(switch {scrutinee:?} (cases{:?}) Some({dx:?}) {i})",
ListLispCasesFmt(branches.as_slice())
)
} else {
write!(
f,
"(switch {scrutinee:?} (cases{:?}))",
ListLispCasesFmt(branches)
)
}
}
}
Self::Continue => write!(f, "(continue)"),
Self::Break => write!(f, "(break)"),
Self::Return(expr) => {
if alt {
write!(f, "(return {expr:#ni$?})")
} else {
write!(f, "(return {expr:?})")
}
}
}
}
}
struct ListLispFmt<'a, T: Debug>(&'a [T]);
impl<T: Debug> Debug for ListLispFmt<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (alt, ni, pad) = fmt_vars(f);
for e in self.0 {
if alt {
write!(f, "\n{pad}{e:#ni$?}")?;
} else {
write!(f, " {e:?}")?;
}
}
Ok(())
}
}
struct ListLispCasesFmt<'a, T: Debug>(&'a [(T, Body<'a>)]);
impl<T: Debug> Debug for ListLispCasesFmt<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (alt, ni, pad) = fmt_vars(f);
for (i, e) in self.0 {
if alt {
write!(
f,
"\n{pad}(case {i:?}\n{pad} {:#width$?})",
e,
width = ni + PRETTY_PRINT_INDENT
)?;
} else {
write!(f, " (case {i:?} {e:?}")?;
}
}
Ok(())
}
}
impl Debug for Identifier<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{}::{}", self.namespace, self.literal)
}
}
impl Debug for Body<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (alt, ni, pad) = fmt_vars(f);
write!(f, "(body")?;
for e in &self.0 {
if alt {
write!(f, "\n{pad}{e:#ni$?}")?;
} else {
write!(f, " {e:?}")?;
}
}
write!(f, ")")
}
}
impl Debug for Atom<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::Variable(var) => write!(f, "{var:?}"),
Self::String(str) => write!(f, "{str:?}"),
Self::Number(num) => write!(f, "{num}"),
Self::Regex(rgx) => write!(f, "/{rgx}/"),
Self::BigInt() | Self::BigFloat() => unimplemented!(),
}
}
}
impl Debug for Variable<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::User(ident) => ident.fmt(f),
Self::Nr => write!(f, "NR"),
Self::Nf => write!(f, "NF"),
Self::Fs => write!(f, "FS"),
Self::Rs => write!(f, "RS"),
Self::Ofs => write!(f, "OFS"),
Self::Ors => write!(f, "ORS"),
Self::Filename => write!(f, "FILENAME"),
Self::Argc => write!(f, "ARGC"),
Self::Argv => write!(f, "ARGV"),
Self::Subsep => write!(f, "SUBSEP"),
Self::Fnr => write!(f, "FNR"),
Self::Argind => write!(f, "ARGIND"),
Self::Ofmt => write!(f, "OFMT"),
Self::Rstart => write!(f, "RSTART"),
Self::Rlength => write!(f, "RLENGTH"),
Self::Environ => write!(f, "ENVIRON"),
}
}
}