mirror of
https://github.com/uutils/awk.git
synced 2026-06-10 16:15:04 -07:00
parser: refactor statements; delete support
This commit is contained in:
+13
-10
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user