Skip to main content

nu_protocol/
syntax_shape.rs

1use crate::Type;
2use serde::{Deserialize, Serialize};
3use std::fmt::Display;
4
5/// The syntactic shapes that describe how a sequence should be parsed.
6///
7/// This extends beyond [`Type`] which describes how [`Value`](crate::Value)s are represented.
8/// `SyntaxShape`s can describe the parsing rules for arguments to a command.
9/// e.g. [`SyntaxShape::GlobPattern`]/[`SyntaxShape::Filepath`] serve the completer,
10/// but don't have an associated [`Value`](crate::Value)
11/// There are additional `SyntaxShape`s that only make sense in particular expressions or keywords
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub enum SyntaxShape {
14    /// Any syntactic form is allowed
15    Any,
16
17    /// A binary literal
18    Binary,
19
20    /// A block is allowed, eg `{start this thing}`
21    Block,
22
23    /// A boolean value, eg `true` or `false`
24    Boolean,
25
26    /// A dotted path to navigate the table
27    CellPath,
28
29    /// A closure is allowed, eg `{|| start this thing}`
30    Closure(Option<Vec<SyntaxShape>>),
31
32    /// A datetime value, eg `2022-02-02` or `2019-10-12T07:20:50.52+00:00`
33    DateTime,
34
35    /// A directory is allowed
36    Directory,
37
38    /// A duration value is allowed, eg `19day`
39    Duration,
40
41    /// An error value
42    Error,
43
44    /// A general expression, eg `1 + 2` or `foo --bar`
45    Expression,
46
47    /// A (typically) string argument that follows external command argument parsing rules.
48    ///
49    /// Filepaths are expanded if unquoted, globs are allowed, and quotes embedded within unknown
50    /// args are unquoted.
51    ExternalArgument,
52
53    /// A filepath is allowed
54    Filepath,
55
56    /// A filesize value is allowed, eg `10kb`
57    Filesize,
58
59    /// A floating point value, eg `1.0`
60    Float,
61
62    /// A dotted path including the variable to access items
63    ///
64    /// Fully qualified
65    FullCellPath,
66
67    /// A glob pattern is allowed, eg `foo*`
68    GlobPattern,
69
70    /// Only an integer value is allowed
71    Int,
72
73    /// A module path pattern used for imports
74    ImportPattern,
75
76    /// A specific match to a word or symbol
77    Keyword(Vec<u8>, Box<SyntaxShape>),
78
79    /// A list is allowed, eg `[first second]`
80    List(Box<SyntaxShape>),
81
82    /// A general math expression, eg `1 + 2`
83    MathExpression,
84
85    /// A block of matches, used by `match`
86    MatchBlock,
87
88    /// Nothing
89    Nothing,
90
91    /// Only a numeric (integer or float) value is allowed
92    Number,
93
94    /// One of a list of possible items, checked in order
95    OneOf(Vec<SyntaxShape>),
96
97    /// An operator, eg `+`
98    Operator,
99
100    /// A range is allowed (eg, `1..3`)
101    Range,
102
103    /// A record value, eg `{x: 1, y: 2}`
104    Record(Vec<(String, SyntaxShape)>),
105
106    /// A math expression which expands shorthand forms on the lefthand side, eg `foo > 1`
107    /// The shorthand allows us to more easily reach columns inside of the row being passed in
108    RowCondition,
109
110    /// A signature for a definition, `[x:int, --foo]`
111    Signature,
112
113    /// A signature for command `extern`, which allows some reserved variable names, such as `[--env(-e), --in]`
114    ExternalSignature,
115
116    /// Strings and string-like bare words are allowed
117    String,
118
119    /// A table is allowed, eg `[[first, second]; [1, 2]]`
120    Table(Vec<(String, SyntaxShape)>),
121
122    /// A variable with optional type, `x` or `x: int`
123    VarWithOptType,
124}
125
126impl SyntaxShape {
127    /// If possible provide the associated concrete [`Type`]
128    ///
129    /// Note: Some [`SyntaxShape`]s don't have a corresponding [`Value`](crate::Value)
130    /// Here we currently return [`Type::Any`]
131    ///
132    /// ```rust
133    /// use nu_protocol::{SyntaxShape, Type};
134    /// let non_value = SyntaxShape::ImportPattern;
135    /// assert_eq!(non_value.to_type(), Type::Any);
136    /// ```
137    pub fn to_type(&self) -> Type {
138        let mk_ty = |tys: &[(String, SyntaxShape)]| {
139            tys.iter()
140                .map(|(key, val)| (key.clone(), val.to_type()))
141                .collect()
142        };
143
144        match self {
145            SyntaxShape::Any => Type::Any,
146            SyntaxShape::Block => Type::Block,
147            SyntaxShape::Closure(_) => Type::Closure,
148            SyntaxShape::Binary => Type::Binary,
149            SyntaxShape::CellPath => Type::CellPath,
150            SyntaxShape::DateTime => Type::Date,
151            SyntaxShape::Duration => Type::Duration,
152            SyntaxShape::Expression => Type::Any,
153            SyntaxShape::ExternalArgument => Type::Any,
154            SyntaxShape::Filepath => Type::String,
155            SyntaxShape::Directory => Type::String,
156            SyntaxShape::Float => Type::Float,
157            SyntaxShape::Filesize => Type::Filesize,
158            SyntaxShape::FullCellPath => Type::Any,
159            SyntaxShape::GlobPattern => Type::Glob,
160            SyntaxShape::Error => Type::Error,
161            SyntaxShape::ImportPattern => Type::Any,
162            SyntaxShape::Int => Type::Int,
163            SyntaxShape::List(x) => {
164                let contents = x.to_type();
165                Type::List(Box::new(contents))
166            }
167            SyntaxShape::Keyword(_, expr) => expr.to_type(),
168            SyntaxShape::MatchBlock => Type::Any,
169            SyntaxShape::MathExpression => Type::Any,
170            SyntaxShape::Nothing => Type::Nothing,
171            SyntaxShape::Number => Type::Number,
172            SyntaxShape::OneOf(types) => Type::one_of(types.iter().map(SyntaxShape::to_type)),
173            SyntaxShape::Operator => Type::Any,
174            SyntaxShape::Range => Type::Range,
175            SyntaxShape::Record(entries) => Type::Record(mk_ty(entries)),
176            SyntaxShape::RowCondition => Type::Bool,
177            SyntaxShape::Boolean => Type::Bool,
178            SyntaxShape::Signature | SyntaxShape::ExternalSignature => Type::Any,
179            SyntaxShape::String => Type::String,
180            SyntaxShape::Table(columns) => Type::Table(mk_ty(columns)),
181            SyntaxShape::VarWithOptType => Type::Any,
182        }
183    }
184}
185
186impl Display for SyntaxShape {
187    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188        let mk_fmt = |tys: &[(String, SyntaxShape)]| -> String {
189            tys.iter()
190                .map(|(x, y)| format!("{x}: {y}"))
191                .collect::<Vec<String>>()
192                .join(", ")
193        };
194
195        match self {
196            SyntaxShape::Keyword(kw, shape) => {
197                write!(f, "\"{}\" {}", String::from_utf8_lossy(kw), shape)
198            }
199            SyntaxShape::Any => write!(f, "any"),
200            SyntaxShape::String => write!(f, "string"),
201            SyntaxShape::CellPath => write!(f, "cell-path"),
202            SyntaxShape::FullCellPath => write!(f, "cell-path"),
203            SyntaxShape::Number => write!(f, "number"),
204            SyntaxShape::Range => write!(f, "range"),
205            SyntaxShape::Int => write!(f, "int"),
206            SyntaxShape::Float => write!(f, "float"),
207            SyntaxShape::Filepath => write!(f, "path"),
208            SyntaxShape::Directory => write!(f, "directory"),
209            SyntaxShape::GlobPattern => write!(f, "glob"),
210            SyntaxShape::ImportPattern => write!(f, "import"),
211            SyntaxShape::Block => write!(f, "block"),
212            SyntaxShape::Closure(args) => {
213                if let Some(args) = args {
214                    let arg_vec: Vec<_> = args.iter().map(|x| x.to_string()).collect();
215                    let arg_string = arg_vec.join(", ");
216                    write!(f, "closure({arg_string})")
217                } else {
218                    write!(f, "closure()")
219                }
220            }
221            SyntaxShape::Binary => write!(f, "binary"),
222            SyntaxShape::List(x) => write!(f, "list<{x}>"),
223            SyntaxShape::Table(columns) => {
224                if columns.is_empty() {
225                    write!(f, "table")
226                } else {
227                    write!(f, "table<{}>", mk_fmt(columns))
228                }
229            }
230            SyntaxShape::Record(entries) => {
231                if entries.is_empty() {
232                    write!(f, "record")
233                } else {
234                    write!(f, "record<{}>", mk_fmt(entries))
235                }
236            }
237            SyntaxShape::Filesize => write!(f, "filesize"),
238            SyntaxShape::Duration => write!(f, "duration"),
239            SyntaxShape::DateTime => write!(f, "datetime"),
240            SyntaxShape::Operator => write!(f, "operator"),
241            SyntaxShape::RowCondition => write!(
242                f,
243                "oneof<condition, {}>",
244                SyntaxShape::Closure(Some(vec![SyntaxShape::Any]))
245            ),
246            SyntaxShape::MathExpression => write!(f, "variable"),
247            SyntaxShape::VarWithOptType => write!(f, "vardecl"),
248            SyntaxShape::Signature => write!(f, "signature"),
249            SyntaxShape::ExternalSignature => write!(f, "external-signature"),
250            SyntaxShape::MatchBlock => write!(f, "match-block"),
251            SyntaxShape::Expression => write!(f, "expression"),
252            SyntaxShape::ExternalArgument => write!(f, "external-argument"),
253            SyntaxShape::Boolean => write!(f, "bool"),
254            SyntaxShape::Error => write!(f, "error"),
255            SyntaxShape::OneOf(list) => {
256                write!(f, "oneof")?;
257                let [first, rest @ ..] = &**list else {
258                    return Ok(());
259                };
260                write!(f, "<{first}")?;
261                for t in rest {
262                    write!(f, ", {t}")?;
263                }
264                f.write_str(">")
265            }
266            SyntaxShape::Nothing => write!(f, "nothing"),
267        }
268    }
269}