mirror of
https://github.com/uutils/awk.git
synced 2026-06-10 16:15:04 -07:00
parser: redirection support
This commit is contained in:
+18
-6
@@ -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<'a>>,
|
||||
redirection: Option<(Redirection, Expr<'a>)>,
|
||||
},
|
||||
Delete(Variable<'a>, Option<Expr<'a>>),
|
||||
}
|
||||
@@ -422,6 +422,18 @@ impl<'a> Place<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Redirection {
|
||||
pub fn parse(value: &Token) -> Option<Self> {
|
||||
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<Self> {
|
||||
match value {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+48
-30
@@ -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<SimpleStatement<'a>> {
|
||||
fn parse_command(&mut self, lex: &mut Lexer<'a>, name: Command) -> Result<SimpleStatement<'a>> {
|
||||
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<Vec<'a, Expr<'a>>> {
|
||||
fn parse_function_args(&mut self, lex: &mut Lexer<'a>) -> Result<Vec<'a, Expr<'a>>> {
|
||||
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<Vec<'a, Expr<'a>>> {
|
||||
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<Option<(Redirection, Expr<'a>)>> {
|
||||
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<SimpleStatement<'a>> {
|
||||
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())
|
||||
})?;
|
||||
|
||||
+26
-9
@@ -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<Expr<'a>> {
|
||||
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<Expr<'a>> {
|
||||
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<Expr<'a>> {
|
||||
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<Expr<'a>> {
|
||||
fn fold_rhs(
|
||||
&mut self,
|
||||
lex: &mut Lexer<'a>,
|
||||
mut lhs: Expr<'a>,
|
||||
min_bp: u8,
|
||||
delimiter: impl Fn(&Token<'a>) -> bool,
|
||||
) -> Result<Expr<'a>> {
|
||||
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<Expr<'a>> {
|
||||
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<Expr<'a>> {
|
||||
self.parse_expression(lex, BinaryOperator::Concat.binding_power().1 - 1)
|
||||
}
|
||||
}
|
||||
|
||||
+31
-13
@@ -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, "|&"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user