diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 53d2afd..0d43528 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -157,11 +157,11 @@ pub enum Place<'a> { } /// GNU docs: https://www.gnu.org/software/gawk/manual/html_node/Redirection.html -#[derive(Debug, Clone)] -pub enum Redirection<'a> { - Write(WriteKind, Slice<'a>), - WriteFile(&'a str), - AppendFile(&'a str), +pub enum Redirection { + WriteFile, + AppendFile, + PipeIn, + CoprocessIn, } #[derive(Debug, Clone, Copy)] @@ -227,7 +227,7 @@ pub enum SimpleStatement<'a> { Command { name: Command, args: Vec<'a, Expr<'a>>, - redirection: Option>, + redirection: Option<(Redirection, Expr<'a>)>, }, Delete(Variable<'a>, Option>), } @@ -422,6 +422,18 @@ impl<'a> Place<'a> { } } +impl Redirection { + pub fn parse(value: &Token) -> Option { + match value { + Token::GreaterThan => Some(Self::WriteFile), + Token::AppendPipe => Some(Self::AppendFile), + Token::Pipe => Some(Self::PipeIn), + Token::DoublePipe => Some(Self::CoprocessIn), + _ => None, + } + } +} + impl WriteKind { pub fn parse(value: &Token) -> Option { match value { diff --git a/parser/src/idempotency.rs b/parser/src/idempotency.rs index 3299259..56d0c2a 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, SimpleStatement, Statement, Ternary, UnaryOperator, - UnaryPlaceOperator, Variable, + Getline, Place, Redirection, Rule, RulePattern, SimpleStatement, Statement, Ternary, + UnaryOperator, UnaryPlaceOperator, Variable, }, }; @@ -184,14 +184,20 @@ impl Display for SimpleStatement<'_> { SimpleStatement::Command { name, args, - redirection, + redirection: Some((rx, expr)), + } => { + write!(f, "{name}(")?; + write_args(f, args, indent)?; + write!(f, "){rx}{expr}")?; + Ok(()) + } + SimpleStatement::Command { + name, + args, + redirection: None, } => { write!(f, "{name} ")?; - write_args(f, args, indent)?; - if let Some(_redir) = redirection { - write!(f, "TODO")?; - } - Ok(()) + write_args(f, args, indent) } SimpleStatement::Delete(array, Some(index)) => write!(f, "delete {array}[{index}]"), SimpleStatement::Delete(array, None) => write!(f, "delete {array}"), @@ -397,6 +403,17 @@ impl Display for Command { } } +impl Display for Redirection { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Self::WriteFile => write!(f, " > "), + Self::AppendFile => write!(f, " >> "), + Self::PipeIn => write!(f, " | "), + Self::CoprocessIn => write!(f, " |& "), + } + } +} + fn write_args(f: &mut Formatter<'_>, args: &[impl Display], indent: u8) -> Result { let ew = encode(indent, 0); for (i, arg) in args.iter().enumerate() { diff --git a/parser/src/lib.rs b/parser/src/lib.rs index f09525d..6b72709 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, SimpleStatement, SpecialPattern, Statement, Variable, + Redirection, Rule, RulePattern, SimpleStatement, SpecialPattern, Statement, Variable, }, diagnostics::{ParsingError, report_error}, lex::TokenExt, @@ -467,41 +467,31 @@ 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>, name: Command) -> Result> { let parent = lex.consume(&Token::OpenParent); - let command = SimpleStatement::Command { - name: command, - args: self.parse_arguments(lex, |t| { - if parent { - t == &Token::ClosedParent - } else { - t.is_stmnt_end() || t == &Token::ClosedBrace - } - })?, - redirection: None, - }; - if parent { + let args = if parent { + let expr = self.parse_function_args(lex)?; lex.expect( &Token::ClosedParent, ParsingError::UnclosedParenthesisInStatement, )?; - } - Ok(command) + expr + } else { + self.parse_command_args(lex)? + }; + let redirection = self.parse_command_redirection(lex)?; + Ok(SimpleStatement::Command { + name, + args, + redirection, + }) } /// Parses arguments to command or function calls; consumes to the end of /// the argument list or short-circuits with `delimiter` if empty. - fn parse_arguments( - &mut self, - lex: &mut Lexer<'a>, - delimiter: impl Fn(&Token<'a>) -> bool, - ) -> Result>> { + fn parse_function_args(&mut self, lex: &mut Lexer<'a>) -> Result>> { let mut arguments = Vec::new_in(self.arena); - if lex.peek_with(&delimiter) { + if lex.peek_is(&Token::ClosedParent) { return Ok(arguments); } @@ -512,6 +502,37 @@ impl<'a> Parser<'a> { Ok(arguments) } + fn parse_command_args(&mut self, lex: &mut Lexer<'a>) -> Result>> { + let mut arguments = Vec::new_in(self.arena); + let mut pratt = Pratt::new(self); + if !lex.peek_with(Token::is_expr_start) { + return Ok(arguments); + } + + arguments.push(pratt.parse_command_argument(lex)?); + while lex.consume(&Token::Comma) { + arguments.push(pratt.parse_command_argument(lex)?); + } + Ok(arguments) + } + + fn parse_command_redirection( + &mut self, + lex: &mut Lexer<'a>, + ) -> Result)>> { + if let Some(Ok(token)) = lex.peek() + && let Some(redirection) = Redirection::parse(token) + { + lex.next(); + Ok(Some(( + redirection, + Pratt::new(self).parse_redirection(lex)?, + ))) + } else { + Ok(None) + } + } + fn parse_delete(&mut self, lex: &mut Lexer<'a>) -> Result> { let next = lex.expect_next()?; let Some(var) = self.get_place(lex, next) else { @@ -602,10 +623,7 @@ impl<'a> Parser<'a> { if lex.span().start != span.end { return Err(ParsingError::FunctionCallSeparatedIdent(span)); } - let expr = ExprNode::FunctionCall( - name, - self.parse_arguments(lex, |t| t == &Token::ClosedParent)?, - ); + let expr = ExprNode::FunctionCall(name, self.parse_function_args(lex)?); lex.expect(&Token::ClosedParent, |s| { ParsingError::FunctionCallMissingParenthesis(s, name.to_string()) })?; diff --git a/parser/src/pratt.rs b/parser/src/pratt.rs index f116e18..50661ec 100644 --- a/parser/src/pratt.rs +++ b/parser/src/pratt.rs @@ -3,8 +3,8 @@ use lexer::Token; use crate::{ IdentifierExt, Lexer, Parser, Result, ast::{ - BinaryOperator, BinaryPlaceOperator, BindingPower, Expr, ExprNode, Getline, Place, Ternary, - UnaryOperator, UnaryPlaceOperator, Variable, WriteKind, + BinaryOperator, BinaryPlaceOperator, BindingPower, Expr, ExprNode, Getline, Place, + Redirection, Ternary, UnaryOperator, UnaryPlaceOperator, Variable, WriteKind, }, diagnostics::ParsingError, lex::TokenExt, @@ -23,6 +23,11 @@ impl<'a, 'b> Pratt<'a, 'b> { self.parse_expression(lex, 0) } + pub fn parse_command_argument(&mut self, lex: &mut Lexer<'a>) -> Result> { + let lhs = self.parse_lhs(lex)?; + self.fold_rhs(lex, lhs, 0, |t| Redirection::parse(t).is_some()) + } + fn parse_lhs(&mut self, lex: &mut Lexer<'a>) -> Result> { if lex.consume(&Token::OpenParent) { self.parse_parenthesized(lex) @@ -37,12 +42,21 @@ impl<'a, 'b> Pratt<'a, 'b> { fn parse_expression(&mut self, lex: &mut Lexer<'a>, min_bp: u8) -> Result> { let lhs = self.parse_lhs(lex)?; - self.fold_rhs(lex, lhs, min_bp) + self.fold_rhs(lex, lhs, min_bp, |_| false) } - fn fold_rhs(&mut self, lex: &mut Lexer<'a>, mut lhs: Expr<'a>, min_bp: u8) -> Result> { + fn fold_rhs( + &mut self, + lex: &mut Lexer<'a>, + mut lhs: Expr<'a>, + min_bp: u8, + delimiter: impl Fn(&Token<'a>) -> bool, + ) -> Result> { while let Some((next, span)) = lex.peek_with_span() { let next = next?; + if delimiter(next) { + break; + } lhs = if let Ok(op) = UnaryPlaceOperator::parse_suffix(next, &span) { if op.binding_power() < min_bp { break; @@ -81,7 +95,7 @@ impl<'a, 'b> Pratt<'a, 'b> { } fn parse_parenthesized(&mut self, lex: &mut Lexer<'a>) -> Result> { - let inner = self.parse_expression(lex, 0); + let inner = self.parse(lex); lex.expect( &Token::ClosedParent, ParsingError::UnclosedParenthesisExpression, @@ -110,7 +124,7 @@ impl<'a, 'b> Pratt<'a, 'b> { // redirection reading from file. let place = if lex.peek_with(Token::is_place) { Some(Place::promote_from( - self.parse_expression(lex, BinaryOperator::Concat.binding_power().1)?, + self.parse_redirection(lex)?, lex.span(), )) } else { @@ -184,8 +198,7 @@ impl<'a, 'b> Pratt<'a, 'b> { BinaryOperator::Concat.expr( rhs, Expr::node( - BinaryOperator::Concat - .expr(Expr::leaf(Variable::Subsep), self.parse_expression(lex, 0)?), + BinaryOperator::Concat.expr(Expr::leaf(Variable::Subsep), self.parse(lex)?), self.parser.arena, ), ), @@ -224,7 +237,7 @@ impl<'a, 'b> Pratt<'a, 'b> { let pipe = |place| Expr::node(op.expr_getline(place, lhs), self.parser.arena); if lex.peek_with(Token::is_place) { - let expr = self.parse_expression(lex, BinaryOperator::Concat.binding_power().1)?; + let expr = self.parse_redirection(lex)?; match Place::promote_from(expr, lex.span()) { Ok(place) => Ok(pipe(Some(place))), Err((expr, _)) => Ok(Expr::node( @@ -236,4 +249,8 @@ impl<'a, 'b> Pratt<'a, 'b> { Ok(pipe(None)) } } + + pub fn parse_redirection(&mut self, lex: &mut Lexer<'a>) -> Result> { + self.parse_expression(lex, BinaryOperator::Concat.binding_power().1 - 1) + } } diff --git a/parser/src/sexpr.rs b/parser/src/sexpr.rs index 66487ea..cc087d6 100644 --- a/parser/src/sexpr.rs +++ b/parser/src/sexpr.rs @@ -5,7 +5,9 @@ use std::fmt::{Debug, Display, Formatter, Result}; -use crate::ast::{Atom, Body, Identifier, Place, SimpleStatement, Statement, Variable}; +use crate::ast::{ + Atom, Body, Identifier, Place, Redirection, SimpleStatement, Statement, Variable, +}; const PRETTY_PRINT_INDENT: usize = 2; @@ -156,22 +158,27 @@ impl Debug for SimpleStatement<'_> { Self::Command { name, args, - redirection, + redirection: Some((rx, expr)), } => { - 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)) - } + if alt { + write!( + f, + "({name:?}{:#width$?}\n{pad}({rx:?} {expr:?}))", + ListLispFmt(args), + width = ni - PRETTY_PRINT_INDENT + ) } else { - write!(f, "({name:?}{:?})", ListLispFmt(args)) + write!(f, "({name:?}{:?} ({rx:?} {expr:?}))", ListLispFmt(args)) } } + Self::Command { + name, + args, + redirection: None, + } => { + write!(f, "({name:?}{:?})", ListLispFmt(args)) + } + Self::Delete(array, Some(index)) => { write!(f, "(delete (index {array:?} {index:?}))") } @@ -287,3 +294,14 @@ impl Debug for Variable<'_> { } } } + +impl Debug for Redirection { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Self::WriteFile => write!(f, ">"), + Self::AppendFile => write!(f, ">>"), + Self::PipeIn => write!(f, "|"), + Self::CoprocessIn => write!(f, "|&"), + } + } +}