parser: refactor statements; delete support

This commit is contained in:
Guillem L. Jara
2026-05-06 16:54:06 +02:00
parent 48c2c6abc4
commit 7b3ea1b91c
5 changed files with 223 additions and 124 deletions
+13 -10
View File
@@ -184,12 +184,7 @@ pub enum Getline<'a> {
}
pub enum Statement<'a> {
Expression(Expr<'a>),
Command {
name: Command,
args: Vec<'a, Expr<'a>>,
redirection: Option<Redirection<'a>>,
},
Simple(SimpleStatement<'a>),
If {
condition: Expr<'a>,
then_body: Body<'a>,
@@ -203,12 +198,10 @@ pub enum Statement<'a> {
then_body: Body<'a>,
condition: Expr<'a>,
},
// TODO: Maybe Expr is too restrictive for init & update.
// Maybe a subset of Statement?
For {
init: Option<Expr<'a>>,
init: Option<SimpleStatement<'a>>,
condition: Option<Expr<'a>>,
update: Option<Expr<'a>>,
update: Option<SimpleStatement<'a>>,
body: Body<'a>,
},
ForEach {
@@ -229,6 +222,16 @@ pub enum Statement<'a> {
Exit(Option<Expr<'a>>),
}
pub enum SimpleStatement<'a> {
Expression(Expr<'a>),
Command {
name: Command,
args: Vec<'a, Expr<'a>>,
redirection: Option<Redirection<'a>>,
},
Delete(Variable<'a>, Option<Expr<'a>>),
}
#[derive(Debug)]
pub struct Function<'a> {
pub args: Vec<'a, Identifier<'a>>,
+28 -15
View File
@@ -4,8 +4,8 @@ use crate::{
Ast, Function,
ast::{
Atom, BinaryOperator, BinaryPlaceOperator, BindingPower, Body, Command, Expr, ExprNode,
Getline, Place, Rule, RulePattern, Statement, Ternary, UnaryOperator, UnaryPlaceOperator,
Variable,
Getline, Place, Rule, RulePattern, SimpleStatement, Statement, Ternary, UnaryOperator,
UnaryPlaceOperator, Variable,
},
};
@@ -77,19 +77,7 @@ impl Display for Statement<'_> {
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::Simple(simple) => <_ as Display>::fmt(simple, f),
Self::If {
condition,
then_body,
@@ -186,6 +174,31 @@ impl Display for Statement<'_> {
}
}
impl Display for SimpleStatement<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (indent, _) = decode(f);
let ew = encode(indent, 0);
match self {
SimpleStatement::Expression(expr) => write!(f, "{expr:ew$}"),
SimpleStatement::Command {
name,
args,
redirection,
} => {
write!(f, "{name} ")?;
write_args(f, args, indent)?;
if let Some(_redir) = redirection {
write!(f, "TODO")?;
}
Ok(())
}
SimpleStatement::Delete(array, Some(index)) => write!(f, "delete {array}[{index}]"),
SimpleStatement::Delete(array, None) => write!(f, "delete {array}"),
}
}
}
impl Display for Rule<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match (&self.pattern, &self.actions) {
+112 -52
View File
@@ -25,7 +25,7 @@ pub use crate::lex::Lexer;
use crate::{
ast::{
Atom, BinaryPlaceOperator, Body, Command, Expr, ExprNode, Function, Identifier, Pattern,
Rule, RulePattern, SpecialPattern, Statement, Variable,
Rule, RulePattern, SimpleStatement, SpecialPattern, Statement, Variable,
},
diagnostics::{ParsingError, report_error},
lex::TokenExt,
@@ -213,14 +213,35 @@ impl<'a> Parser<'a> {
}
}
}
#[tracing::instrument]
fn parse_simple_statement(
&mut self,
lex: &mut Lexer<'a>,
) -> Option<Result<SimpleStatement<'a>>> {
let peek = lex.expect_peek().ok()?;
if peek.is_expr_start() {
Some(self.parse_expression(lex).map(SimpleStatement::Expression))
} else {
match peek {
token if let Some(name) = token.maps_to_command() => {
lex.next();
Some(self.parse_command(lex, name))
}
Token::Delete => {
lex.next();
Some(self.parse_delete(lex))
}
_ => None,
}
}
}
#[tracing::instrument]
fn parse_statement(&mut self, lex: &mut Lexer<'a>) -> Result<Statement<'a>> {
let statement = if lex.expect_peek()?.is_expr_start() {
self.parse_expression(lex).map(Statement::Expression)?
let statement = if let Some(statement) = self.parse_simple_statement(lex) {
Statement::Simple(statement?)
} else {
match lex.expect_next()? {
tok if let Some(name) = tok.maps_to_command() => self.parse_command(lex, name)?,
Token::If => {
let condition = self.parse_parenthesized_expr(lex)?;
let then_body = self.parse_statement_body(lex)?;
@@ -239,42 +260,19 @@ impl<'a> Parser<'a> {
// for (ident in ident; expr; expr) as a syntax error.
// It seems like a bug to me.
lex.expect(&Token::OpenParent, ParsingError::ExpectedOpeningParenthesis)?;
let init = (!lex.consume(&Token::Semicolon))
.then(|| self.parse_expression(lex))
.transpose()?;
if init.is_none() || lex.consume(&Token::Semicolon) {
let condition = self.parse_for_fragment::<false>(lex)?;
let update = self.parse_for_fragment::<true>(lex)?;
let body = self.parse_statement_body(lex)?;
Statement::For {
init,
condition,
update,
body,
}
let init = if lex.consume(&Token::Semicolon) {
None
} else {
let Some(Expr::Node(node)) = init else {
let Some(stmnt) = self.parse_simple_statement(lex) else {
return Err(ParsingError::InvalidForLoop(lex.span()));
};
let ExprNode::BinaryPlaceOperation(
BinaryPlaceOperator::InArray,
ast::Place::Variable(variable),
Expr::Leaf(Atom::Variable(array)),
) = Box::into_inner(node)
else {
return Err(ParsingError::InvalidForLoop(lex.span()));
};
lex.expect(
&Token::ClosedParent,
ParsingError::UnclosedParenthesisInStatement,
)?;
let body = self.parse_statement_body(lex)?;
Statement::ForEach {
variable,
array,
body,
}
}
Some(stmnt?)
};
if init.is_none() || lex.consume(&Token::Semicolon) {
self.parse_for_loop(lex, init)
} else {
self.parse_for_each(lex, init)
}?
}
Token::Switch => {
let scrutinee = self.parse_parenthesized_expr(lex)?;
@@ -386,22 +384,65 @@ impl<'a> Parser<'a> {
}
#[tracing::instrument]
fn parse_for_fragment<const END: bool>(
fn parse_for_loop(
&mut self,
lex: &mut Lexer<'a>,
) -> Result<Option<Expr<'a>>> {
let next = if END {
Token::ClosedParent
init: Option<SimpleStatement<'a>>,
) -> Result<Statement<'a>> {
lex.consume(&Token::Newline);
let condition = (!lex.peek_is(&Token::Semicolon))
.then(|| self.parse_expression(lex))
.transpose()?;
lex.expect(&Token::Semicolon, ParsingError::InvalidForLoop)?;
lex.consume(&Token::Newline);
let update = if lex.peek_is(&Token::ClosedParent) {
None
} else {
Token::Semicolon
let Some(stmnt) = self.parse_simple_statement(lex) else {
return Err(ParsingError::InvalidForLoop(lex.span()));
};
Some(stmnt?)
};
if lex.consume(&next) {
Ok(None)
} else {
let expr = self.parse_expression(lex)?;
lex.expect(&next, ParsingError::InvalidForLoop)?;
Ok(Some(expr))
}
lex.expect(&Token::ClosedParent, ParsingError::InvalidForLoop)?;
let body = self.parse_statement_body(lex)?;
Ok(Statement::For {
init,
condition,
update,
body,
})
}
#[tracing::instrument]
fn parse_for_each(
&mut self,
lex: &mut Lexer<'a>,
expr: Option<SimpleStatement<'a>>,
) -> Result<Statement<'a>> {
let Some(SimpleStatement::Expression(Expr::Node(node))) = expr else {
return Err(ParsingError::InvalidForLoop(lex.span()));
};
let ExprNode::BinaryPlaceOperation(
BinaryPlaceOperator::InArray,
ast::Place::Variable(variable),
Expr::Leaf(Atom::Variable(array)),
) = Box::into_inner(node)
else {
return Err(ParsingError::InvalidForLoop(lex.span()));
};
lex.expect(
&Token::ClosedParent,
ParsingError::UnclosedParenthesisInStatement,
)?;
let body = self.parse_statement_body(lex)?;
Ok(Statement::ForEach {
variable,
array,
body,
})
}
#[tracing::instrument]
@@ -426,9 +467,13 @@ impl<'a> Parser<'a> {
}
#[tracing::instrument]
fn parse_command(&mut self, lex: &mut Lexer<'a>, command: Command) -> Result<Statement<'a>> {
fn parse_command(
&mut self,
lex: &mut Lexer<'a>,
command: Command,
) -> Result<SimpleStatement<'a>> {
let parent = lex.consume(&Token::OpenParent);
let command = Statement::Command {
let command = SimpleStatement::Command {
name: command,
args: self.parse_arguments(lex, |t| {
if parent {
@@ -467,6 +512,21 @@ impl<'a> Parser<'a> {
Ok(arguments)
}
fn parse_delete(&mut self, lex: &mut Lexer<'a>) -> Result<SimpleStatement<'a>> {
let next = lex.expect_next()?;
let Some(var) = self.get_place(lex, next) else {
return Err(ParsingError::OperatorExpectsVariable(lex.span()));
};
let index = if lex.consume(&Token::OpenBracket) {
let mut pratt = Pratt::new(self);
let first = pratt.parse(lex)?;
Some(pratt.parse_array_index(lex, first)?)
} else {
None
};
Ok(SimpleStatement::Delete(var, index))
}
#[tracing::instrument]
fn parse_function(&mut self, lex: &mut Lexer<'a>) -> Result<()> {
let name = lex.expect_identifier()?.qualify(self.namespace);
@@ -517,7 +577,7 @@ impl<'a> Parser<'a> {
#[tracing::instrument]
fn parse_expression(&mut self, lex: &mut Lexer<'a>) -> Result<Expr<'a>> {
Pratt::parse(self, lex)
Pratt::new(self).parse(lex)
}
#[tracing::instrument]
+26 -16
View File
@@ -15,8 +15,12 @@ pub struct Pratt<'a, 'b> {
}
impl<'a, 'b> Pratt<'a, 'b> {
pub fn parse(parser: &'b mut Parser<'a>, lex: &mut Lexer<'a>) -> Result<Expr<'a>> {
Self { parser }.parse_expression(lex, 0)
pub fn new(parser: &'b mut Parser<'a>) -> Self {
Self { parser }
}
pub fn parse(&mut self, lex: &mut Lexer<'a>) -> Result<Expr<'a>> {
self.parse_expression(lex, 0)
}
fn parse_lhs(&mut self, lex: &mut Lexer<'a>) -> Result<Expr<'a>> {
@@ -168,24 +172,30 @@ impl<'a, 'b> Pratt<'a, 'b> {
if !matches!(place, Place::Variable(_)) {
return Err(ParsingError::OperatorExpectsVariable(lex.span()));
}
while lex.consume(&Token::Comma) {
rhs = Expr::node(
BinaryOperator::Concat.expr(
rhs,
Expr::node(
BinaryOperator::Concat
.expr(Expr::leaf(Variable::Subsep), self.parse_expression(lex, 0)?),
self.parser.arena,
),
),
self.parser.arena,
);
}
lex.expect(&Token::ClosedBracket, ParsingError::UnclosedArrayAccess)?;
rhs = self.parse_array_index(lex, rhs)?;
}
Ok(Expr::node(op.expr(place, rhs), self.parser.arena))
}
pub fn parse_array_index(&mut self, lex: &mut Lexer<'a>, lhs: Expr<'a>) -> Result<Expr<'a>> {
let mut rhs = lhs;
while lex.consume(&Token::Comma) {
rhs = Expr::node(
BinaryOperator::Concat.expr(
rhs,
Expr::node(
BinaryOperator::Concat
.expr(Expr::leaf(Variable::Subsep), self.parse_expression(lex, 0)?),
self.parser.arena,
),
),
self.parser.arena,
);
}
lex.expect(&Token::ClosedBracket, ParsingError::UnclosedArrayAccess)?;
Ok(rhs)
}
fn parse_ternary(&mut self, lex: &mut Lexer<'a>, lhs: Expr<'a>) -> Result<Expr<'a>> {
let right_bp = Ternary.binding_power().1;
lex.next();
+44 -31
View File
@@ -5,7 +5,7 @@
use std::fmt::{Debug, Display, Formatter, Result};
use crate::ast::{Atom, Body, Expr, Identifier, Place, Statement, Variable};
use crate::ast::{Atom, Body, Identifier, Place, SimpleStatement, Statement, Variable};
const PRETTY_PRINT_INDENT: usize = 2;
@@ -19,32 +19,7 @@ impl Debug for Statement<'_> {
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:?}{:#ni$?}))",
ListLispFmt(args),
)
} else {
write!(f, "(redir {rx:?} ({name:?}{:?}))", ListLispFmt(args))
}
} else {
write!(f, "({name:?}{:?})", ListLispFmt(args))
}
}
Statement::Simple(simple) => <_ as Debug>::fmt(simple, f),
Self::If {
condition,
then_body,
@@ -96,16 +71,16 @@ impl Debug for Statement<'_> {
} => {
if alt {
write!(f, "(for")?;
let write_fragment = |f: &mut Formatter, x: &Option<Expr>| {
let write_fragment = |f: &mut Formatter, x: Option<&dyn Debug>| {
if let Some(x) = x {
write!(f, "\n{pad}{x:?}")
} else {
write!(f, "\n{pad}None")
}
};
write_fragment(f, init)?;
write_fragment(f, condition)?;
write_fragment(f, update)?;
write_fragment(f, init.as_ref().map(|x| x as _))?;
write_fragment(f, condition.as_ref().map(|x| x as _))?;
write_fragment(f, update.as_ref().map(|x| x as _))?;
write!(f, "\n{pad}{body:#ni$?})")
} else {
write!(f, "(for {init:?} {condition:?} {update:?} {body:?})")
@@ -167,6 +142,44 @@ impl Debug for Statement<'_> {
}
}
impl Debug for SimpleStatement<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (alt, ni, pad) = fmt_vars(f);
match self {
Self::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:?}{:#ni$?}))",
ListLispFmt(args),
)
} else {
write!(f, "(redir {rx:?} ({name:?}{:?}))", ListLispFmt(args))
}
} else {
write!(f, "({name:?}{:?})", ListLispFmt(args))
}
}
Self::Delete(array, Some(index)) => {
write!(f, "(delete (index {array:?} {index:?}))")
}
Self::Delete(array, None) => write!(f, "(delete {array:?})"),
}
}
}
struct ListLispFmt<'a, T: Debug>(&'a [T]);
impl<T: Debug> Debug for ListLispFmt<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {