diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 907ff1d..53d2afd 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -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>, - }, + 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>, + init: Option>, condition: Option>, - update: Option>, + update: Option>, body: Body<'a>, }, ForEach { @@ -229,6 +222,16 @@ pub enum Statement<'a> { Exit(Option>), } +pub enum SimpleStatement<'a> { + Expression(Expr<'a>), + Command { + name: Command, + args: Vec<'a, Expr<'a>>, + redirection: Option>, + }, + Delete(Variable<'a>, Option>), +} + #[derive(Debug)] pub struct Function<'a> { pub args: Vec<'a, Identifier<'a>>, diff --git a/parser/src/idempotency.rs b/parser/src/idempotency.rs index 81da784..3299259 100644 --- a/parser/src/idempotency.rs +++ b/parser/src/idempotency.rs @@ -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) { diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 18d3609..f09525d 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -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>> { + 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> { - 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::(lex)?; - let update = self.parse_for_fragment::(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( + fn parse_for_loop( &mut self, lex: &mut Lexer<'a>, - ) -> Result>> { - let next = if END { - Token::ClosedParent + init: Option>, + ) -> Result> { + 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>, + ) -> Result> { + 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> { + fn parse_command( + &mut self, + lex: &mut Lexer<'a>, + command: Command, + ) -> Result> { 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> { + 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> { - Pratt::parse(self, lex) + Pratt::new(self).parse(lex) } #[tracing::instrument] diff --git a/parser/src/pratt.rs b/parser/src/pratt.rs index 048011f..f116e18 100644 --- a/parser/src/pratt.rs +++ b/parser/src/pratt.rs @@ -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> { - 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> { + self.parse_expression(lex, 0) } fn parse_lhs(&mut self, lex: &mut Lexer<'a>) -> Result> { @@ -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> { + 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> { let right_bp = Ternary.binding_power().1; lex.next(); diff --git a/parser/src/sexpr.rs b/parser/src/sexpr.rs index a802a3d..66487ea 100644 --- a/parser/src/sexpr.rs +++ b/parser/src/sexpr.rs @@ -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| { + 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 Debug for ListLispFmt<'_, T> { fn fmt(&self, f: &mut Formatter<'_>) -> Result {