summaryrefslogtreecommitdiff
path: root/crates/nu-protocol/src/ast/expr.rs
blob: d6386f92dce0594e55a4c961d0f1d5670723841b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use chrono::FixedOffset;
use serde::{Deserialize, Serialize};

use super::{
    Call, CellPath, Expression, ExternalArgument, FullCellPath, Keyword, MatchPattern, Operator,
    Range, Table, ValueWithUnit,
};
use crate::{
    ast::ImportPattern, engine::StateWorkingSet, BlockId, OutDest, Signature, Span, VarId,
};

/// An [`Expression`] AST node
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Expr {
    Bool(bool),
    Int(i64),
    Float(f64),
    Binary(Vec<u8>),
    Range(Box<Range>),
    Var(VarId),
    VarDecl(VarId),
    Call(Box<Call>),
    ExternalCall(Box<Expression>, Box<[ExternalArgument]>), // head, args
    Operator(Operator),
    RowCondition(BlockId),
    UnaryNot(Box<Expression>),
    BinaryOp(Box<Expression>, Box<Expression>, Box<Expression>), //lhs, op, rhs
    Collect(VarId, Box<Expression>),
    Subexpression(BlockId),
    Block(BlockId),
    Closure(BlockId),
    MatchBlock(Vec<(MatchPattern, Expression)>),
    List(Vec<ListItem>),
    Table(Table),
    Record(Vec<RecordItem>),
    Keyword(Box<Keyword>),
    ValueWithUnit(Box<ValueWithUnit>),
    DateTime(chrono::DateTime<FixedOffset>),
    /// The boolean is `true` if the string is quoted.
    Filepath(String, bool),
    /// The boolean is `true` if the string is quoted.
    Directory(String, bool),
    /// The boolean is `true` if the string is quoted.
    GlobPattern(String, bool),
    String(String),
    RawString(String),
    CellPath(CellPath),
    FullCellPath(Box<FullCellPath>),
    ImportPattern(Box<ImportPattern>),
    Overlay(Option<BlockId>), // block ID of the overlay's origin module
    Signature(Box<Signature>),
    StringInterpolation(Vec<Expression>),
    /// The boolean is `true` if the string is quoted.
    GlobInterpolation(Vec<Expression>, bool),
    Nothing,
    Garbage,
}

// This is to document/enforce the size of `Expr` in bytes.
// We should try to avoid increasing the size of `Expr`,
// and PRs that do so will have to change the number below so that it's noted in review.
const _: () = assert!(std::mem::size_of::<Expr>() <= 40);

impl Expr {
    pub fn pipe_redirection(
        &self,
        working_set: &StateWorkingSet,
    ) -> (Option<OutDest>, Option<OutDest>) {
        match self {
            Expr::Call(call) => working_set.get_decl(call.decl_id).pipe_redirection(),
            Expr::Collect(_, _) => {
                // A collect expression always has default redirection, it's just going to collect
                // stdout unless another type of redirection is specified
                (None, None)
            },
            Expr::Subexpression(block_id) | Expr::Block(block_id) => working_set
                .get_block(*block_id)
                .pipe_redirection(working_set),
            Expr::FullCellPath(cell_path) => cell_path.head.expr.pipe_redirection(working_set),
            Expr::Bool(_)
            | Expr::Int(_)
            | Expr::Float(_)
            | Expr::Binary(_)
            | Expr::Range(_)
            | Expr::Var(_)
            | Expr::UnaryNot(_)
            | Expr::BinaryOp(_, _, _)
            | Expr::Closure(_) // piping into a closure value, not into a closure call
            | Expr::List(_)
            | Expr::Table(_)
            | Expr::Record(_)
            | Expr::ValueWithUnit(_)
            | Expr::DateTime(_)
            | Expr::String(_)
            | Expr::RawString(_)
            | Expr::CellPath(_)
            | Expr::StringInterpolation(_)
            | Expr::GlobInterpolation(_, _)
            | Expr::Nothing => {
                // These expressions do not use the output of the pipeline in any meaningful way,
                // so we can discard the previous output by redirecting it to `Null`.
                (Some(OutDest::Null), None)
            }
            Expr::VarDecl(_)
            | Expr::Operator(_)
            | Expr::Filepath(_, _)
            | Expr::Directory(_, _)
            | Expr::GlobPattern(_, _)
            | Expr::ImportPattern(_)
            | Expr::Overlay(_)
            | Expr::Signature(_)
            | Expr::Garbage => {
                // These should be impossible to pipe to,
                // but even it is, the pipeline output is not used in any way.
                (Some(OutDest::Null), None)
            }
            Expr::RowCondition(_) | Expr::MatchBlock(_) => {
                // These should be impossible to pipe to,
                // but if they are, then the pipeline output could be used.
                (None, None)
            }
            Expr::ExternalCall(_, _) => {
                // No override necessary, pipes will always be created in eval
                (None, None)
            }
            Expr::Keyword(_) => {
                // Not sure about this; let's return no redirection override for now.
                (None, None)
            }
        }
    }
}

/// Expressions permitted inside a record expression/literal
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum RecordItem {
    /// A key: val mapping
    Pair(Expression, Expression),
    /// Span for the "..." and the expression that's being spread
    Spread(Span, Expression),
}

/// Expressions permitted inside a list expression/literal
#[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
    }
}