diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 3291ddc..907ff1d 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -5,14 +5,14 @@ use std::fmt::Debug; -use bumpalo::{Bump, collections::Vec}; +use bumpalo::{Bump, boxed::Box, collections::Vec}; use either::Either; use hashbrown::{DefaultHashBuilder, HashMap}; use lexer::{Slice, Span, Token}; use crate::{ParsingError, Result, lex::TokenExt}; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Ast<'a> { pub loads: Vec<'a, Slice<'a>>, pub begin: Vec<'a, Body<'a>>, @@ -24,7 +24,7 @@ pub struct Ast<'a> { pub functions: HashMap, Function<'a>, DefaultHashBuilder, &'a Bump>, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Rule<'a> { pub pattern: Option>, pub actions: Option>, @@ -40,7 +40,7 @@ pub enum Atom<'a> { Regex(Slice<'a>), } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum RulePattern<'a> { Expression(Expr<'a>), Range(Expr<'a>, Expr<'a>), @@ -81,22 +81,21 @@ pub enum Variable<'a> { Environ, } -#[derive(Clone)] pub enum Expr<'a> { Leaf(Atom<'a>), - Node(&'a ExprNode<'a>), + Node(Box<'a, ExprNode<'a>>), } -#[derive(Clone)] pub struct Body<'a>(pub Vec<'a, Statement<'a>>); pub type Pattern<'a> = Either, SpecialPattern>; -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum ExprNode<'a> { FunctionCall(Identifier<'a>, Vec<'a, Expr<'a>>), UnaryOperation(UnaryOperator, Expr<'a>), BinaryOperation(BinaryOperator, Expr<'a>, Expr<'a>), - PlaceOperation(PlaceOperator, Variable<'a>, Expr<'a>), + UnaryPlaceOperation(UnaryPlaceOperator, Place<'a>), + BinaryPlaceOperation(BinaryPlaceOperator, Place<'a>, Expr<'a>), Ternary(Expr<'a>, Expr<'a>, Expr<'a>), Getline(Getline<'a>), } @@ -131,12 +130,32 @@ pub enum BinaryOperator { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum PlaceOperator { +pub enum UnaryPlaceOperator { + IncrementL, + DecrementL, + IncrementR, + DecrementR, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum BinaryPlaceOperator { Assignment, + AddAssign, + SubAssign, + MulAssign, + DivAssign, + PowAssign, + ModAssign, ArrayAccess, InArray, } +pub enum Place<'a> { + Record(Expr<'a>), + Variable(Variable<'a>), + ArrayElement(Variable<'a>, Expr<'a>), +} + /// GNU docs: https://www.gnu.org/software/gawk/manual/html_node/Redirection.html #[derive(Debug, Clone)] pub enum Redirection<'a> { @@ -152,19 +171,18 @@ pub enum WriteKind { Coprocess, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum Getline<'a> { // getline (var)? - FromInput(Option>), + FromInput(Option>), // getline (var)? < (file) - FromFile(Option>, Expr<'a>), + FromFile(Option>, Expr<'a>), // (expr) | getline (var)? - PipeOut(Option>, Expr<'a>), + PipeOut(Option>, Expr<'a>), // (expr) |& getline (var)? - CoprocessOut(Option>, Expr<'a>), + CoprocessOut(Option>, Expr<'a>), } -#[derive(Clone)] pub enum Statement<'a> { Expression(Expr<'a>), Command { @@ -194,7 +212,7 @@ pub enum Statement<'a> { body: Body<'a>, }, ForEach { - place: Variable<'a>, + variable: Variable<'a>, array: Variable<'a>, body: Body<'a>, }, @@ -211,13 +229,13 @@ pub enum Statement<'a> { Exit(Option>), } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Function<'a> { pub args: Vec<'a, Identifier<'a>>, pub body: Body<'a>, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum Command { Print, Printf, @@ -229,7 +247,7 @@ impl<'a> Expr<'a> { } pub fn node(op: impl Into>, arena: &'a Bump) -> Self { - Self::Node(arena.alloc(op.into())) + Self::Node(Box::new_in(op.into(), arena)) } } @@ -245,9 +263,15 @@ impl BinaryOperator { } } -impl PlaceOperator { - pub fn expr<'a>(self, a: Variable<'a>, b: Expr<'a>) -> ExprNode<'a> { - ExprNode::PlaceOperation(self, a, b) +impl UnaryPlaceOperator { + pub fn expr(self, a: Place<'_>) -> ExprNode<'_> { + ExprNode::UnaryPlaceOperation(self, a) + } +} + +impl BinaryPlaceOperator { + pub fn expr<'a>(self, a: Place<'a>, b: Expr<'a>) -> ExprNode<'a> { + ExprNode::BinaryPlaceOperation(self, a, b) } } @@ -275,6 +299,12 @@ impl<'a> From> for Atom<'a> { } } +impl<'a> From> for Place<'a> { + fn from(value: Variable<'a>) -> Self { + Self::Variable(value) + } +} + impl From for Atom<'_> { fn from(value: f64) -> Self { Self::Number(value) @@ -323,16 +353,34 @@ impl<'a> BinaryOperator { } } -impl<'a> PlaceOperator { +impl UnaryPlaceOperator { + pub fn parse_prefix(value: &Token<'_>, span: &Span) -> Result { + match value { + Token::Increment => Ok(Self::IncrementL), + Token::Decrement => Ok(Self::DecrementL), + _ => Err(ParsingError::OperatorExpectsVariable(span.clone())), + } + } + + pub fn parse_suffix(value: &Token<'_>, span: &Span) -> Result { + match value { + Token::Increment => Ok(Self::IncrementR), + Token::Decrement => Ok(Self::DecrementR), + _ => Err(ParsingError::OperatorExpectsVariable(span.clone())), + } + } +} + +impl<'a> BinaryPlaceOperator { pub fn parse(value: &Token<'a>, span: &Span) -> Result { match value { - Token::Assignment - | Token::PlusAssign - | Token::MinusAssign - | Token::StarAssign - | Token::SlashAssign - | Token::CaretAssign - | Token::PercentAssign => Ok(Self::Assignment), + Token::Assignment => Ok(Self::Assignment), + Token::PlusAssign => Ok(Self::AddAssign), + Token::MinusAssign => Ok(Self::SubAssign), + Token::StarAssign => Ok(Self::MulAssign), + Token::SlashAssign => Ok(Self::DivAssign), + Token::CaretAssign => Ok(Self::PowAssign), + Token::PercentAssign => Ok(Self::ModAssign), Token::OpenBracket => Ok(Self::ArrayAccess), Token::In => Ok(Self::InArray), _ => Err(ParsingError::UnexpectedToken( @@ -343,6 +391,34 @@ impl<'a> PlaceOperator { } } +impl<'a> Place<'a> { + pub fn promote_from(expr: Expr<'a>, span: Span) -> Result, ParsingError)> { + match expr { + Expr::Leaf(Atom::Variable(var)) => Ok(Self::Variable(var)), + Expr::Node(node) + if matches!( + &*node, + &ExprNode::UnaryOperation(UnaryOperator::Record, _) + | &ExprNode::BinaryPlaceOperation( + BinaryPlaceOperator::ArrayAccess, + Place::Variable(_), + _ + ) + ) => + { + match Box::into_inner(node) { + ExprNode::UnaryOperation(_, index) => Ok(Self::Record(index)), + ExprNode::BinaryPlaceOperation(_, Place::Variable(var), index) => { + Ok(Self::ArrayElement(var, index)) + } + _ => unreachable!("Box is magic; handled awkwardly in the match guard."), + } + } + _ => Err((expr, ParsingError::OperatorExpectsVariable(span))), + } + } +} + impl WriteKind { pub fn parse(value: &Token) -> Option { match value { @@ -352,7 +428,7 @@ impl WriteKind { } } - pub fn expr_getline<'a>(self, var: Option>, expr: Expr<'a>) -> Getline<'a> { + pub fn expr_getline<'a>(self, var: Option>, expr: Expr<'a>) -> Getline<'a> { match self { Self::Pipe => Getline::PipeOut(var, expr), Self::Coprocess => Getline::CoprocessOut(var, expr), @@ -360,35 +436,6 @@ impl WriteKind { } } -impl BinaryOperator { - pub fn unfold(token: &Token) -> Option { - match token { - Token::PlusAssign => Some(Self::Add), - Token::MinusAssign => Some(Self::Subtract), - Token::StarAssign => Some(Self::Multiply), - Token::SlashAssign => Some(Self::Divide), - Token::PercentAssign => Some(Self::Modulo), - Token::CaretAssign => Some(Self::Raise), - _ => None, - } - } - - pub fn unfold_prefix(token: &Token<'_>) -> Option<(Self, u8)> { - match token { - Token::Increment => Some((Self::Add, binding_powers::BP_INC_DEC)), - Token::Decrement => Some((Self::Subtract, binding_powers::BP_INC_DEC)), - _ => None, - } - } - pub fn unfold_suffix(token: &Token<'_>) -> Option<(Self, Self, u8)> { - match token { - Token::Increment => Some((Self::Add, Self::Subtract, binding_powers::BP_INC_DEC)), - Token::Decrement => Some((Self::Subtract, Self::Add, binding_powers::BP_INC_DEC)), - _ => None, - } - } -} - pub struct Ternary; mod binding_powers { @@ -435,13 +482,20 @@ impl BindingPower for BinaryOperator { } } -impl BindingPower for PlaceOperator { +impl BindingPower for UnaryPlaceOperator { + type Bp = u8; + fn binding_power(&self) -> Self::Bp { + binding_powers::BP_INC_DEC + } +} + +impl BindingPower for BinaryPlaceOperator { type Bp = (u8, u8); fn binding_power(&self) -> Self::Bp { match self { - Self::Assignment => binding_powers::BP_ASSIGN, Self::ArrayAccess => (binding_powers::BP_GROUPING, 0), Self::InArray => binding_powers::BP_IN, + _ => binding_powers::BP_ASSIGN, } } } @@ -460,7 +514,6 @@ impl BindingPower for UnaryOperator { impl BindingPower for Ternary { type Bp = (u8, u8); - fn binding_power(&self) -> Self::Bp { binding_powers::BP_TERNARY } @@ -476,7 +529,7 @@ impl Debug for Expr<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Leaf(atom) => write!(f, "{atom:?}"), - Self::Node(expr) => match expr { + Self::Node(expr) => match expr.as_ref() { ExprNode::FunctionCall(ident, args) => { write!(f, "({ident:?}")?; for arg in args { @@ -486,7 +539,8 @@ impl Debug for Expr<'_> { } ExprNode::UnaryOperation(op, a) => write!(f, "({op:?} {a:?})"), ExprNode::BinaryOperation(op, a, b) => write!(f, "({op:?} {a:?} {b:?})"), - ExprNode::PlaceOperation(op, a, b) => write!(f, "({op:?} {a:?} {b:?})"), + ExprNode::BinaryPlaceOperation(op, a, b) => write!(f, "({op:?} {a:?} {b:?})"), + ExprNode::UnaryPlaceOperation(op, a) => write!(f, "({op:?} {a:?})"), ExprNode::Ternary(a, b, c) => write!(f, "(?: {a:?} {b:?} {c:?})"), ExprNode::Getline(getline) => match getline { Getline::FromInput(Some(a)) => write!(f, "(getline {a:?})"), diff --git a/parser/src/diagnostics.rs b/parser/src/diagnostics.rs index 5c98c54..51fe014 100644 --- a/parser/src/diagnostics.rs +++ b/parser/src/diagnostics.rs @@ -150,3 +150,9 @@ pub fn report_error<'a>( } (Box::new(report.finish()), Source::from(source)) } + +impl From<(T, Self)> for ParsingError { + fn from(value: (T, Self)) -> Self { + value.1 + } +} diff --git a/parser/src/idempotency.rs b/parser/src/idempotency.rs index 9625753..81da784 100644 --- a/parser/src/idempotency.rs +++ b/parser/src/idempotency.rs @@ -3,8 +3,9 @@ use std::fmt::{Debug, Display, Formatter, Result, Write}; use crate::{ Ast, Function, ast::{ - Atom, BinaryOperator, BindingPower, Body, Command, Expr, ExprNode, Getline, PlaceOperator, - Rule, RulePattern, Statement, Ternary, UnaryOperator, + Atom, BinaryOperator, BinaryPlaceOperator, BindingPower, Body, Command, Expr, ExprNode, + Getline, Place, Rule, RulePattern, Statement, Ternary, UnaryOperator, UnaryPlaceOperator, + Variable, }, }; @@ -138,8 +139,12 @@ impl Display for Statement<'_> { write!(f, ") ")?; write_body(f, body, indent) } - Self::ForEach { place, array, body } => { - write!(f, "for ({place:?} in {array:?}) ")?; + Self::ForEach { + variable, + array, + body, + } => { + write!(f, "for ({variable} in {array}) ")?; write_body(f, body, indent) } Self::Switch { @@ -220,6 +225,23 @@ impl Display for Atom<'_> { } } +impl Display for Variable<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + ::fmt(self, f) + } +} + +impl Display for Place<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Self::Variable(var) => <_ as Display>::fmt(var, f), + Self::Record(Expr::Leaf(leaf)) => write!(f, "${leaf}"), + Self::Record(Expr::Node(node)) => write!(f, "$({node})"), + Self::ArrayElement(var, expr) => write!(f, "{var}[{expr}]"), + } + } +} + impl Display for ExprNode<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> Result { let (indent, parent_bp) = decode(f); @@ -249,21 +271,27 @@ impl Display for ExprNode<'_> { write!(f, "{a:left_w$}{op}{b:right_w$}") } } - Self::PlaceOperation(op, place, idx) => { + Self::UnaryPlaceOperation(op, place) => match op { + UnaryPlaceOperator::IncrementL => write!(f, "++{place}"), + UnaryPlaceOperator::DecrementL => write!(f, "--{place}"), + UnaryPlaceOperator::DecrementR => write!(f, "{place}--"), + UnaryPlaceOperator::IncrementR => write!(f, "{place}++"), + }, + Self::BinaryPlaceOperation(op, place, idx) => { let (left_bp, right_bp) = op.binding_power(); - if op == &PlaceOperator::ArrayAccess { + if op == &BinaryPlaceOperator::ArrayAccess { let idx_w = encode(indent, 0); if left_bp < parent_bp { - write!(f, "({place:?}[{idx:idx_w$}])") + write!(f, "({place}[{idx:idx_w$}])") } else { - write!(f, "{place:?}[{idx:idx_w$}]") + write!(f, "{place}[{idx:idx_w$}]") } } else { let right_w = encode(indent, right_bp); if left_bp < parent_bp { - write!(f, "({place:?}{op}{idx:right_w$})") + write!(f, "({place}{op}{idx:right_w$})") } else { - write!(f, "{place:?}{op}{idx:right_w$}") + write!(f, "{place}{op}{idx:right_w$}") } } } @@ -283,11 +311,14 @@ impl Display for ExprNode<'_> { } } Self::Getline(getline) => match getline { - Getline::FromInput(Some(var)) => write!(f, "getline {var:?}"), + Getline::FromInput(Some(var)) => write!(f, "getline {var}"), Getline::FromInput(None) => write!(f, "getline"), - Getline::FromFile(Some(var), file) => write!(f, "getline {var:?} < {file}"), + Getline::FromFile(Some(var), file) => write!(f, "getline {var} < {file}"), Getline::FromFile(None, file) => write!(f, "getline < {file}"), - _ => todo!(), + Getline::PipeOut(Some(place), e) => write!(f, "{e} | getline {place}"), + Getline::PipeOut(None, e) => write!(f, "{e} | getline"), + Getline::CoprocessOut(Some(place), e) => write!(f, "{e} |& getline {place}"), + Getline::CoprocessOut(None, e) => write!(f, "{e} |& getline"), }, } } @@ -328,10 +359,16 @@ impl Display for BinaryOperator { } } -impl Display for PlaceOperator { +impl Display for BinaryPlaceOperator { fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self { Self::Assignment => write!(f, " = "), + Self::AddAssign => write!(f, " += "), + Self::SubAssign => write!(f, " -= "), + Self::MulAssign => write!(f, " *= "), + Self::DivAssign => write!(f, " /= "), + Self::PowAssign => write!(f, " ^= "), + Self::ModAssign => write!(f, " %= "), Self::InArray => write!(f, " in "), Self::ArrayAccess => unreachable!("Array access needs special handling."), } diff --git a/parser/src/lex.rs b/parser/src/lex.rs index c7e5be1..a32f4b3 100644 --- a/parser/src/lex.rs +++ b/parser/src/lex.rs @@ -189,7 +189,8 @@ impl TokenExt for Token<'_> { ) } fn is_atom(&self) -> bool { - matches!(self, Token::Number(_) | Token::String(_) | Token::Regex(_)) || self.is_place() + matches!(self, Token::Number(_) | Token::String(_) | Token::Regex(_)) + || self.is_place() && self != &Token::Record } fn is_expr_start(&self) -> bool { self.is_atom() @@ -216,6 +217,7 @@ impl TokenExt for Token<'_> { | Token::RlengthVariable | Token::EnvironVariable | Token::Identifier(_) + | Token::Record ) } fn is_pattern_start(&self) -> bool { diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 1baca84..18d3609 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -15,7 +15,7 @@ mod tests; use std::{fmt::Debug, mem::replace}; -use bumpalo::{Bump, collections::Vec, vec}; +use bumpalo::{Bump, boxed::Box, collections::Vec, vec}; use either::Either::{Left, Right}; use hashbrown::HashMap; use lexer::{LexingError, Span, Token}; @@ -24,8 +24,8 @@ pub use crate::ast::Ast; pub use crate::lex::Lexer; use crate::{ ast::{ - Atom, Body, Command, Expr, ExprNode, Function, Identifier, Pattern, PlaceOperator, Rule, - RulePattern, SpecialPattern, Statement, Variable, + Atom, BinaryPlaceOperator, Body, Command, Expr, ExprNode, Function, Identifier, Pattern, + Rule, RulePattern, SpecialPattern, Statement, Variable, }, diagnostics::{ParsingError, report_error}, lex::TokenExt, @@ -50,7 +50,7 @@ impl From for ParsingError { } type AriadneErr<'a> = ( - Box>, + std::boxed::Box>, ariadne::Source<&'a str>, ); @@ -242,7 +242,7 @@ impl<'a> Parser<'a> { let init = (!lex.consume(&Token::Semicolon)) .then(|| self.parse_expression(lex)) .transpose()?; - if lex.consume(&Token::Semicolon) || init.is_none() { + 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)?; @@ -253,11 +253,14 @@ impl<'a> Parser<'a> { body, } } else { - let Some(Expr::Node(ExprNode::PlaceOperation( - PlaceOperator::InArray, - place, + let Some(Expr::Node(node)) = init else { + return Err(ParsingError::InvalidForLoop(lex.span())); + }; + let ExprNode::BinaryPlaceOperation( + BinaryPlaceOperator::InArray, + ast::Place::Variable(variable), Expr::Leaf(Atom::Variable(array)), - ))) = init + ) = Box::into_inner(node) else { return Err(ParsingError::InvalidForLoop(lex.span())); }; @@ -267,8 +270,8 @@ impl<'a> Parser<'a> { )?; let body = self.parse_statement_body(lex)?; Statement::ForEach { - place: *place, - array: *array, + variable, + array, body, } } diff --git a/parser/src/pratt.rs b/parser/src/pratt.rs index 6c04573..048011f 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::{ - Atom, BinaryOperator, BindingPower, Expr, ExprNode, Getline, PlaceOperator, Ternary, - UnaryOperator, Variable, WriteKind, + BinaryOperator, BinaryPlaceOperator, BindingPower, Expr, ExprNode, Getline, Place, Ternary, + UnaryOperator, UnaryPlaceOperator, Variable, WriteKind, }, diagnostics::ParsingError, lex::TokenExt, @@ -39,35 +39,35 @@ impl<'a, 'b> Pratt<'a, 'b> { fn fold_rhs(&mut self, lex: &mut Lexer<'a>, mut lhs: Expr<'a>, min_bp: u8) -> Result> { while let Some((next, span)) = lex.peek_with_span() { let next = next?; - lhs = if let Ok(op) = BinaryOperator::parse(next, &span) + lhs = if let Ok(op) = UnaryPlaceOperator::parse_suffix(next, &span) { + if op.binding_power() < min_bp { + break; + } + lex.next(); + let place = Place::promote_from(lhs.take(), lex.span())?; + Expr::node(op.expr(place), self.parser.arena) + } else if let Ok(op) = BinaryPlaceOperator::parse(next, &span) { + if op.binding_power().0 < min_bp { + break; + } + let place = Place::promote_from(lhs.take(), lex.span())?; + self.parse_place_op(lex, op, place)? + } else if let Ok(op) = BinaryOperator::parse(next, &span) && !matches!(next, Token::Increment | Token::Decrement) { if op.binding_power().0 < min_bp { break; } self.parse_infix_op(lex, op, lhs)? - } else if let Ok(op) = PlaceOperator::parse(next, &span) { - let Expr::Leaf(Atom::Variable(var)) = lhs.take() else { - return Err(ParsingError::OperatorExpectsVariable(lex.span())); - }; - if op.binding_power().0 < min_bp { - break; - } - self.parse_place_op(lex, op, var)? } else if next == &Token::QuestionMark { if Ternary.binding_power().0 < min_bp { break; } self.parse_ternary(lex, lhs)? - } else if let Some((op, reciprocal, bp)) = BinaryOperator::unfold_suffix(next) { - let Expr::Leaf(Atom::Variable(rhs)) = lhs else { - return Err(ParsingError::OperatorExpectsVariable(lex.span())); - }; - if bp < min_bp { + } else if let Some(op) = WriteKind::parse(next) { + if BinaryOperator::Concat.binding_power().0 < min_bp { break; } - self.unfolded_suffix_op(lex, op, reciprocal, rhs) - } else if let Some(op) = WriteKind::parse(next) { self.parse_getline_pipe(lex, op, lhs)? } else { break; @@ -87,15 +87,10 @@ impl<'a, 'b> Pratt<'a, 'b> { fn parse_prefix(&mut self, lex: &mut Lexer<'a>) -> Result> { let next = lex.expect_next()?; - if let Some((op, bp)) = BinaryOperator::unfold_prefix(&next) { - let Expr::Leaf(Atom::Variable(rhs)) = self.parse_expression(lex, bp)? else { - return Err(ParsingError::OperatorExpectsVariable(lex.span())); - }; + if let Ok(op) = UnaryPlaceOperator::parse_prefix(&next, &lex.span()) { + let rhs = self.parse_expression(lex, op.binding_power())?; Ok(Expr::node( - PlaceOperator::Assignment.expr( - rhs, - Expr::node(op.expr(Expr::leaf(rhs), Expr::leaf(1.)), self.parser.arena), - ), + op.expr(Place::promote_from(rhs, lex.span())?), self.parser.arena, )) } else if let Ok(op) = UnaryOperator::parse(&next, &lex.peeked_span()?) { @@ -107,22 +102,32 @@ impl<'a, 'b> Pratt<'a, 'b> { } fn parse_prefix_getline(&mut self, lex: &mut Lexer<'a>) -> Result> { - // Consumes with maximum precedence the following ident and/or + // Consumes with maximum precedence the following place and/or // redirection reading from file. - // TODO: move to specialized rhs loop to disambiguate call/variable. - let var = if lex.peek_with(Token::is_place) { - let next = lex.expect_next()?; - self.parser.get_place(lex, next) - } else { - None - }; - if lex.consume(&Token::LesserThan) { - Ok(Expr::node( - Getline::FromFile(var, self.parse_expression(lex, 0)?), - self.parser.arena, + let place = if lex.peek_with(Token::is_place) { + Some(Place::promote_from( + self.parse_expression(lex, BinaryOperator::Concat.binding_power().1)?, + lex.span(), )) } else { - Ok(Expr::node(Getline::FromInput(var), self.parser.arena)) + None + } + .transpose(); + + let getline = |gl| Expr::node(ExprNode::Getline(gl), self.parser.arena); + match place { + Err((expr, _)) => Ok(Expr::node( + BinaryOperator::Concat.expr(getline(Getline::FromInput(None)), expr), + self.parser.arena, + )), + Ok(place) => { + if lex.consume(&Token::LesserThan) { + let file = self.parse_expression(lex, BinaryOperator::Lt.binding_power().1)?; + Ok(getline(Getline::FromFile(place, file))) + } else { + Ok(getline(Getline::FromInput(place))) + } + } } } @@ -154,15 +159,15 @@ impl<'a, 'b> Pratt<'a, 'b> { fn parse_place_op( &mut self, lex: &mut Lexer<'a>, - op: PlaceOperator, - var: Variable<'a>, + op: BinaryPlaceOperator, + place: Place<'a>, ) -> Result> { - let token_op = lex.expect_next()?; - + lex.next(); let mut rhs = self.parse_expression(lex, op.binding_power().1)?; - if let Some(op) = BinaryOperator::unfold(&token_op) { - rhs = Expr::node(op.expr(Expr::leaf(var), rhs), self.parser.arena); - } else if op == PlaceOperator::ArrayAccess { + if op == BinaryPlaceOperator::ArrayAccess { + if !matches!(place, Place::Variable(_)) { + return Err(ParsingError::OperatorExpectsVariable(lex.span())); + } while lex.consume(&Token::Comma) { rhs = Expr::node( BinaryOperator::Concat.expr( @@ -178,7 +183,7 @@ impl<'a, 'b> Pratt<'a, 'b> { } lex.expect(&Token::ClosedBracket, ParsingError::UnclosedArrayAccess)?; } - Ok(Expr::node(op.expr(var, rhs), self.parser.arena)) + Ok(Expr::node(op.expr(place, rhs), self.parser.arena)) } fn parse_ternary(&mut self, lex: &mut Lexer<'a>, lhs: Expr<'a>) -> Result> { @@ -193,42 +198,32 @@ impl<'a, 'b> Pratt<'a, 'b> { )) } - fn unfolded_suffix_op( - &mut self, - lex: &mut Lexer<'a>, - op: BinaryOperator, - reciprocal: BinaryOperator, - rhs: Variable<'a>, - ) -> Expr<'a> { - lex.next(); - Expr::node( - reciprocal.expr( - Expr::node( - PlaceOperator::Assignment.expr( - rhs, - Expr::node(op.expr(Expr::leaf(rhs), Expr::leaf(1.)), self.parser.arena), - ), - self.parser.arena, - ), - Expr::leaf(1.), - ), - self.parser.arena, - ) - } - fn parse_getline_pipe( &mut self, lex: &mut Lexer<'a>, op: WriteKind, lhs: Expr<'a>, ) -> Result> { + lex.next(); lex.expect(&Token::Getline, |span| { ParsingError::UnexpectedToken( span, "operand must precede `getline` in an expression.".into(), ) })?; - // TODO: move to specialized rhs loop to disambiguate call/variable. - Ok(Expr::node(op.expr_getline(None, lhs), self.parser.arena)) + + 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)?; + match Place::promote_from(expr, lex.span()) { + Ok(place) => Ok(pipe(Some(place))), + Err((expr, _)) => Ok(Expr::node( + BinaryOperator::Concat.expr(pipe(None), expr), + self.parser.arena, + )), + } + } else { + Ok(pipe(None)) + } } } diff --git a/parser/src/sexpr.rs b/parser/src/sexpr.rs index 5ddcbd2..a802a3d 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, Statement, Variable}; +use crate::ast::{Atom, Body, Expr, Identifier, Place, Statement, Variable}; const PRETTY_PRINT_INDENT: usize = 2; @@ -111,11 +111,15 @@ impl Debug for Statement<'_> { write!(f, "(for {init:?} {condition:?} {update:?} {body:?})") } } - Self::ForEach { place, array, body } => { + Self::ForEach { + variable, + array, + body, + } => { if alt { - write!(f, "(for-each {place:?} {array:?}\n{pad}{body:#ni$?})") + write!(f, "(for-each {variable:?} {array:?}\n{pad}{body:#ni$?})") } else { - write!(f, "(for-each {place:?} {array:?} {body:?})") + write!(f, "(for-each {variable:?} {array:?} {body:?})") } } Self::Switch { @@ -237,6 +241,16 @@ impl Debug for Atom<'_> { } } +impl Debug for Place<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + Self::Record(expr) => write!(f, "($ {expr:?})"), + Self::Variable(var) => <_ as Debug>::fmt(var, f), + Self::ArrayElement(var, index) => write!(f, "(index {var:?} {index:?})"), + } + } +} + impl Debug for Variable<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self {