Add ListItem type for Expr::List (#12529)

# Description
This PR adds a `ListItem` enum to our set of AST types. It encodes the
two possible expressions inside of list expression: a singular item or a
spread. This is similar to the existing `RecordItem` enum. Adding
`ListItem` allows us to remove the existing `Expr::Spread` case which
was previously used for list spreads. As a consequence, this guarantees
(via the type system) that spreads can only ever occur inside lists,
records, or as command args.

This PR also does a little bit of cleanup in relevant parser code.
This commit is contained in:
Ian Manske
2024-04-18 11:21:05 +00:00
committed by GitHub
parent 57b0c722c6
commit 6ccd547d81
8 changed files with 179 additions and 133 deletions

View File

@ -34,7 +34,7 @@ pub enum Expr {
Block(BlockId),
Closure(BlockId),
MatchBlock(Vec<(MatchPattern, Expression)>),
List(Vec<Expression>),
List(Vec<ListItem>),
Table(Vec<Expression>, Vec<Vec<Expression>>),
Record(Vec<RecordItem>),
Keyword(Vec<u8>, Span, Box<Expression>),
@ -50,7 +50,6 @@ pub enum Expr {
Overlay(Option<BlockId>), // block ID of the overlay's origin module
Signature(Box<Signature>),
StringInterpolation(Vec<Expression>),
Spread(Box<Expression>),
Nothing,
Garbage,
}
@ -99,7 +98,6 @@ impl Expr {
| Expr::ImportPattern(_)
| Expr::Overlay(_)
| Expr::Signature(_)
| Expr::Spread(_)
| Expr::Garbage => {
// These should be impossible to pipe to,
// but even it is, the pipeline output is not used in any way.
@ -129,3 +127,23 @@ pub enum RecordItem {
/// Span for the "..." and the expression that's being spread
Spread(Span, Expression),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ListItem {
/// A normal expression
Item(Expression),
/// Span for the "..." and the expression that's being spread
Spread(Span, Expression),
}
impl ListItem {
pub fn expr(&self) -> &Expression {
let (ListItem::Item(expr) | ListItem::Spread(_, expr)) = self;
expr
}
pub fn expr_mut(&mut self) -> &mut Expression {
let (ListItem::Item(expr) | ListItem::Spread(_, expr)) = self;
expr
}
}

View File

@ -86,13 +86,6 @@ impl Expression {
}
}
pub fn as_list(&self) -> Option<Vec<Expression>> {
match &self.expr {
Expr::List(list) => Some(list.clone()),
_ => None,
}
}
pub fn as_keyword(&self) -> Option<&Expression> {
match &self.expr {
Expr::Keyword(_, _, expr) => Some(expr),
@ -213,8 +206,8 @@ impl Expression {
Expr::Int(_) => false,
Expr::Keyword(_, _, expr) => expr.has_in_variable(working_set),
Expr::List(list) => {
for l in list {
if l.has_in_variable(working_set) {
for item in list {
if item.expr().has_in_variable(working_set) {
return true;
}
}
@ -304,7 +297,6 @@ impl Expression {
Expr::ValueWithUnit(expr, _) => expr.has_in_variable(working_set),
Expr::Var(var_id) => *var_id == IN_VARIABLE_ID,
Expr::VarDecl(_) => false,
Expr::Spread(expr) => expr.has_in_variable(working_set),
}
}
@ -394,8 +386,9 @@ impl Expression {
Expr::Int(_) => {}
Expr::Keyword(_, _, expr) => expr.replace_span(working_set, replaced, new_span),
Expr::List(list) => {
for l in list {
l.replace_span(working_set, replaced, new_span)
for item in list {
item.expr_mut()
.replace_span(working_set, replaced, new_span);
}
}
Expr::Operator(_) => {}
@ -456,7 +449,6 @@ impl Expression {
Expr::ValueWithUnit(expr, _) => expr.replace_span(working_set, replaced, new_span),
Expr::Var(_) => {}
Expr::VarDecl(_) => {}
Expr::Spread(expr) => expr.replace_span(working_set, replaced, new_span),
}
}
}

View File

@ -253,7 +253,6 @@ fn expr_to_string(engine_state: &EngineState, expr: &Expr) -> String {
Expr::Record(_) => "record".to_string(),
Expr::RowCondition(_) => "row condition".to_string(),
Expr::Signature(_) => "signature".to_string(),
Expr::Spread(_) => "spread".to_string(),
Expr::String(_) => "string".to_string(),
Expr::StringInterpolation(_) => "string interpolation".to_string(),
Expr::Subexpression(_) => "subexpression".to_string(),

View File

@ -1,7 +1,7 @@
use crate::{
ast::{
eval_operator, Assignment, Bits, Boolean, Call, Comparison, Expr, Expression,
ExternalArgument, Math, Operator, RecordItem,
ExternalArgument, ListItem, Math, Operator, RecordItem,
},
debugger::DebugContext,
Config, IntoInterruptiblePipelineData, Range, Record, ShellError, Span, Value, VarId,
@ -40,15 +40,15 @@ pub trait Eval {
value.follow_cell_path(&cell_path.tail, false)
}
Expr::DateTime(dt) => Ok(Value::date(*dt, expr.span)),
Expr::List(x) => {
Expr::List(list) => {
let mut output = vec![];
for expr in x {
match &expr.expr {
Expr::Spread(expr) => match Self::eval::<D>(state, mut_state, expr)? {
Value::List { mut vals, .. } => output.append(&mut vals),
for item in list {
match item {
ListItem::Item(expr) => output.push(Self::eval::<D>(state, mut_state, expr)?),
ListItem::Spread(_, expr) => match Self::eval::<D>(state, mut_state, expr)? {
Value::List { vals, .. } => output.extend(vals),
_ => return Err(ShellError::CannotSpreadAsList { span: expr.span }),
},
_ => output.push(Self::eval::<D>(state, mut_state, expr)?),
}
}
Ok(Value::list(output, expr.span))
@ -295,7 +295,6 @@ pub trait Eval {
| Expr::VarDecl(_)
| Expr::ImportPattern(_)
| Expr::Signature(_)
| Expr::Spread(_)
| Expr::Operator(_)
| Expr::Garbage => Self::unreachable(expr),
}