Skip to main content

nu_protocol/ast/
expr.rs

1use chrono::FixedOffset;
2use serde::{Deserialize, Serialize};
3
4use super::{
5    AttributeBlock, Call, CellPath, Expression, ExternalArgument, FullCellPath, Keyword,
6    MatchPattern, Operator, Range, Table, ValueWithUnit,
7};
8use crate::{
9    BlockId, ModuleId, OutDest, Signature, Span, VarId, ast::ImportPattern, engine::StateWorkingSet,
10};
11
12/// An [`Expression`] AST node
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub enum Expr {
15    AttributeBlock(AttributeBlock),
16    Bool(bool),
17    Int(i64),
18    Float(f64),
19    Binary(Vec<u8>),
20    Range(Box<Range>),
21    Var(VarId),
22    VarDecl(VarId),
23    Call(Box<Call>),
24    ExternalCall(Box<Expression>, Box<[ExternalArgument]>), // head, args
25    Operator(Operator),
26    RowCondition(BlockId),
27    UnaryNot(Box<Expression>),
28    BinaryOp(Box<Expression>, Box<Expression>, Box<Expression>), //lhs, op, rhs
29    Collect(VarId, Box<Expression>),
30    Subexpression(BlockId),
31    Block(BlockId),
32    Closure(BlockId),
33    MatchBlock(Vec<(MatchPattern, Expression)>),
34    List(Vec<ListItem>),
35    Table(Table),
36    Record(Vec<RecordItem>),
37    Keyword(Box<Keyword>),
38    ValueWithUnit(Box<ValueWithUnit>),
39    DateTime(chrono::DateTime<FixedOffset>),
40    /// The boolean is `true` if the string is quoted.
41    Filepath(String, bool),
42    /// The boolean is `true` if the string is quoted.
43    Directory(String, bool),
44    /// The boolean is `true` if the string is quoted.
45    GlobPattern(String, bool),
46    String(String),
47    RawString(String),
48    CellPath(CellPath),
49    FullCellPath(Box<FullCellPath>),
50    ImportPattern(Box<ImportPattern>),
51    Overlay(Option<ModuleId>),
52    Signature(Box<Signature>),
53    StringInterpolation(Vec<Expression>),
54    /// The boolean is `true` if the string is quoted.
55    GlobInterpolation(Vec<Expression>, bool),
56    Nothing,
57    Garbage,
58}
59
60// This is to document/enforce the size of `Expr` in bytes.
61// We should try to avoid increasing the size of `Expr`,
62// and PRs that do so will have to change the number below so that it's noted in review.
63const _: () = assert!(std::mem::size_of::<Expr>() <= 40);
64
65impl Expr {
66    /// Returns a user-friendly description of the expression variant.
67    ///
68    /// This is used in error messages to avoid exposing internal Rust type names
69    /// (e.g. "FullCellPath") to users.
70    pub fn description(&self) -> &str {
71        match self {
72            Expr::AttributeBlock(_) => "an attribute block",
73            Expr::Bool(_) => "a boolean",
74            Expr::Int(_) => "an integer",
75            Expr::Float(_) => "a float",
76            Expr::Binary(_) => "a binary value",
77            Expr::Range(_) => "a range",
78            Expr::Var(_) => "a variable",
79            Expr::VarDecl(_) => "a variable declaration",
80            Expr::Call(_) => "a command call",
81            Expr::ExternalCall(_, _) => "an external command call",
82            Expr::Operator(_) => "an operator",
83            Expr::RowCondition(_) => "a row condition",
84            Expr::UnaryNot(_) => "a negation",
85            Expr::BinaryOp(_, _, _) => "a binary operation",
86            Expr::Collect(_, _) => "a collect expression",
87            Expr::Subexpression(_) => "a subexpression",
88            Expr::Block(_) => "a block",
89            Expr::Closure(_) => "a closure",
90            Expr::MatchBlock(_) => "a match block",
91            Expr::List(_) => "a list",
92            Expr::Table(_) => "a table",
93            Expr::Record(_) => "a record",
94            Expr::Keyword(_) => "a keyword",
95            Expr::ValueWithUnit(_) => "a value with unit",
96            Expr::DateTime(_) => "a datetime",
97            Expr::Filepath(_, _) => "a filepath",
98            Expr::Directory(_, _) => "a directory",
99            Expr::GlobPattern(_, _) => "a glob pattern",
100            Expr::String(_) => "a string",
101            Expr::RawString(_) => "a raw string",
102            Expr::CellPath(_) => "a cell path",
103            Expr::FullCellPath(_) => "a cell path expression",
104            Expr::ImportPattern(_) => "an import pattern",
105            Expr::Overlay(_) => "an overlay",
106            Expr::Signature(_) => "a signature",
107            Expr::StringInterpolation(_) => "a string interpolation",
108            Expr::GlobInterpolation(_, _) => "a glob interpolation",
109            Expr::Nothing => "a nothing",
110            Expr::Garbage => "a garbage expression",
111        }
112    }
113
114    pub fn pipe_redirection(
115        &self,
116        working_set: &StateWorkingSet,
117    ) -> (Option<OutDest>, Option<OutDest>) {
118        match self {
119            Expr::AttributeBlock(ab) => ab.item.expr.pipe_redirection(working_set),
120            Expr::Call(call) => working_set.get_decl(call.decl_id).pipe_redirection(),
121            Expr::Collect(_, _) => {
122                // A collect expression always has default redirection, it's just going to collect
123                // stdout unless another type of redirection is specified
124                (None, None)
125            },
126            Expr::Subexpression(block_id) | Expr::Block(block_id) => working_set
127                .get_block(*block_id)
128                .pipe_redirection(working_set),
129            Expr::FullCellPath(cell_path) => cell_path.head.expr.pipe_redirection(working_set),
130            Expr::Bool(_)
131            | Expr::Int(_)
132            | Expr::Float(_)
133            | Expr::Binary(_)
134            | Expr::Range(_)
135            | Expr::Var(_)
136            | Expr::UnaryNot(_)
137            | Expr::BinaryOp(_, _, _)
138            | Expr::Closure(_) // piping into a closure value, not into a closure call
139            | Expr::List(_)
140            | Expr::Table(_)
141            | Expr::Record(_)
142            | Expr::ValueWithUnit(_)
143            | Expr::DateTime(_)
144            | Expr::String(_)
145            | Expr::RawString(_)
146            | Expr::CellPath(_)
147            | Expr::StringInterpolation(_)
148            | Expr::GlobInterpolation(_, _)
149            | Expr::Nothing => {
150                // These expressions do not use the output of the pipeline in any meaningful way,
151                // but we still need to use the pipeline output, so the previous command
152                // can be stopped with SIGPIPE(in unix).
153                (None, None)
154            }
155            Expr::VarDecl(_)
156            | Expr::Operator(_)
157            | Expr::Filepath(_, _)
158            | Expr::Directory(_, _)
159            | Expr::GlobPattern(_, _)
160            | Expr::ImportPattern(_)
161            | Expr::Overlay(_)
162            | Expr::Signature(_)
163            | Expr::Garbage => {
164                // These should be impossible to pipe to,
165                // but even it is, the pipeline output is not used in any way.
166                (Some(OutDest::Null), None)
167            }
168            Expr::RowCondition(_) | Expr::MatchBlock(_) => {
169                // These should be impossible to pipe to,
170                // but if they are, then the pipeline output could be used.
171                (None, None)
172            }
173            Expr::ExternalCall(_, _) => {
174                // No override necessary, pipes will always be created in eval
175                (None, None)
176            }
177            Expr::Keyword(_) => {
178                // Not sure about this; let's return no redirection override for now.
179                (None, None)
180            }
181        }
182    }
183}
184
185/// Expressions permitted inside a record expression/literal
186#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
187pub enum RecordItem {
188    /// A key: val mapping
189    Pair(Expression, Expression),
190    /// Span for the "..." and the expression that's being spread
191    Spread(Span, Expression),
192}
193
194/// Expressions permitted inside a list expression/literal
195#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
196pub enum ListItem {
197    /// A normal expression
198    Item(Expression),
199    /// Span for the "..." and the expression that's being spread
200    Spread(Span, Expression),
201}
202
203impl ListItem {
204    pub fn expr(&self) -> &Expression {
205        let (ListItem::Item(expr) | ListItem::Spread(_, expr)) = self;
206        expr
207    }
208
209    pub fn expr_mut(&mut self) -> &mut Expression {
210        let (ListItem::Item(expr) | ListItem::Spread(_, expr)) = self;
211        expr
212    }
213}