Skip to main content

nu_parser/
parse_bindings.rs

1use crate::{
2    lex, parse_block,
3    parse_helpers::garbage_pipeline,
4    parser::{
5        ArgumentParsingLevel, ParsedInternalCall, parse_internal_call, parse_var_with_opt_type,
6    },
7    type_check::{check_pipeline_type, type_compatible},
8};
9
10use log::trace;
11use nu_protocol::{
12    ParseError, Span, Type,
13    ast::{Argument, Call, Expr, Expression, Pipeline},
14    engine::StateWorkingSet,
15    eval_const::eval_constant,
16};
17use std::{collections::HashMap, sync::Arc};
18
19// TODO: handle pipeline input type based inference
20pub fn parse_let(
21    working_set: &mut StateWorkingSet,
22    spans: &[Span],
23    input_type: Option<&Type>,
24) -> Pipeline {
25    trace!("parsing: let");
26
27    if let Some(decl_id) = working_set.find_decl(b"let") {
28        if spans.len() >= 4 {
29            for span in spans.iter().enumerate() {
30                let item = working_set.get_span_contents(*span.1);
31                if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
32                    let (tokens, parse_error) = lex(
33                        working_set.get_span_contents(Span::concat(&spans[(span.0 + 1)..])),
34                        spans[span.0 + 1].start,
35                        &[],
36                        &[],
37                        false,
38                    );
39
40                    if let Some(parse_error) = parse_error {
41                        working_set.error(parse_error)
42                    }
43
44                    let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
45                    let rvalue_block =
46                        parse_block(working_set, &tokens, rvalue_span, false, true, input_type);
47
48                    let output_type = match rvalue_block.pipelines.as_slice() {
49                        [pipeline] if let Some(input) = input_type => {
50                            match check_pipeline_type(working_set, pipeline, input) {
51                                Ok(output_ty) => output_ty,
52                                Err((output_ty, errors)) => {
53                                    working_set.parse_errors.extend(errors);
54                                    output_ty
55                                }
56                            }
57                        }
58                        _ => rvalue_block.output_type(),
59                    };
60
61                    let block_id = working_set.add_block(Arc::new(rvalue_block));
62
63                    let rvalue = Expression::new(
64                        working_set,
65                        Expr::Block(block_id),
66                        rvalue_span,
67                        output_type,
68                    );
69
70                    let mut idx = 0;
71                    let (lvalue, explicit_type) = parse_var_with_opt_type(
72                        working_set,
73                        &spans[1..(span.0)],
74                        &mut idx,
75                        false,
76                        input_type,
77                    );
78                    if idx + 1 < span.0 - 1 {
79                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
80                    }
81
82                    let var_id = lvalue.as_var();
83                    let rhs_type = rvalue.ty.clone();
84
85                    if let Some(explicit_type) = &explicit_type
86                        && !type_compatible(explicit_type, &rhs_type)
87                    {
88                        working_set.error(ParseError::TypeMismatch(
89                            explicit_type.clone(),
90                            rhs_type.clone(),
91                            Span::concat(&spans[(span.0 + 1)..]),
92                        ));
93                    }
94
95                    if let Some(var_id) = var_id
96                        && explicit_type.is_none()
97                    {
98                        working_set.set_variable_type(var_id, rhs_type);
99                    }
100
101                    let call = Box::new(Call {
102                        decl_id,
103                        head: spans[0],
104                        arguments: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
105                        parser_info: HashMap::new(),
106                    });
107
108                    return Pipeline::from_vec(vec![Expression::new(
109                        working_set,
110                        Expr::Call(call),
111                        Span::concat(spans),
112                        Type::Any,
113                    )]);
114                }
115            }
116        }
117        let ParsedInternalCall { call, output, .. } = parse_internal_call(
118            working_set,
119            spans[0],
120            &spans[1..],
121            decl_id,
122            ArgumentParsingLevel::Full,
123            input_type,
124        );
125
126        return Pipeline::from_vec(vec![Expression::new(
127            working_set,
128            Expr::Call(call),
129            Span::concat(spans),
130            output,
131        )]);
132    } else {
133        working_set.error(ParseError::UnknownState(
134            "internal error: let or const statements not found in core language".into(),
135            Span::concat(spans),
136        ))
137    }
138
139    working_set.error(ParseError::UnknownState(
140        "internal error: let or const statement unparsable".into(),
141        Span::concat(spans),
142    ));
143
144    garbage_pipeline(working_set, spans)
145}
146
147/// Additionally returns a span encompassing the variable name, if successful.
148pub fn parse_const(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeline, Option<Span>) {
149    trace!("parsing: const");
150
151    if let Some(decl_id) = working_set.find_decl(b"const") {
152        if spans.len() >= 4 {
153            for span in spans.iter().enumerate() {
154                let item = working_set.get_span_contents(*span.1);
155                if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
156                    let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
157
158                    let (rvalue_tokens, rvalue_error) = lex(
159                        working_set.get_span_contents(rvalue_span),
160                        rvalue_span.start,
161                        &[],
162                        &[],
163                        false,
164                    );
165                    working_set.parse_errors.extend(rvalue_error);
166
167                    trace!("parsing: const right-hand side subexpression");
168                    let rvalue_block =
169                        parse_block(working_set, &rvalue_tokens, rvalue_span, false, true, None);
170                    let rvalue_ty = rvalue_block.output_type();
171                    let rvalue_block_id = working_set.add_block(Arc::new(rvalue_block));
172                    let rvalue = Expression::new(
173                        working_set,
174                        Expr::Subexpression(rvalue_block_id),
175                        rvalue_span,
176                        rvalue_ty,
177                    );
178
179                    let mut idx = 0;
180
181                    let (lvalue, explicit_type) = parse_var_with_opt_type(
182                        working_set,
183                        &spans[1..(span.0)],
184                        &mut idx,
185                        false,
186                        None,
187                    );
188                    if idx + 1 < span.0 - 1 {
189                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
190                    }
191
192                    let var_id = lvalue.as_var();
193                    let rhs_type = rvalue.ty.clone();
194
195                    if let Some(explicit_type) = &explicit_type
196                        && !type_compatible(explicit_type, &rhs_type)
197                    {
198                        working_set.error(ParseError::TypeMismatch(
199                            explicit_type.clone(),
200                            rhs_type.clone(),
201                            Span::concat(&spans[(span.0 + 1)..]),
202                        ));
203                    }
204
205                    if let Some(var_id) = var_id {
206                        if explicit_type.is_none() {
207                            working_set.set_variable_type(var_id, rhs_type);
208                        }
209
210                        match eval_constant(working_set, &rvalue) {
211                            Ok(mut value) => {
212                                let mut const_type = value.get_type();
213
214                                if let Some(explicit_type) = &explicit_type {
215                                    if !type_compatible(explicit_type, &const_type) {
216                                        working_set.error(ParseError::TypeMismatch(
217                                            explicit_type.clone(),
218                                            const_type.clone(),
219                                            Span::concat(&spans[(span.0 + 1)..]),
220                                        ));
221                                    }
222                                    let val_span = value.span();
223
224                                    match value {
225                                        nu_protocol::Value::String { val, .. }
226                                            if explicit_type == &nu_protocol::Type::Glob =>
227                                        {
228                                            value = nu_protocol::Value::glob(val, false, val_span);
229                                            const_type = value.get_type();
230                                        }
231                                        _ => {}
232                                    }
233                                }
234
235                                working_set.set_variable_type(var_id, const_type);
236
237                                working_set.set_variable_const_val(var_id, value);
238                            }
239                            Err(err) => working_set.error(err.wrap(working_set, rvalue.span)),
240                        }
241                    }
242
243                    let call = Box::new(Call {
244                        decl_id,
245                        head: spans[0],
246                        arguments: vec![
247                            Argument::Positional(lvalue.clone()),
248                            Argument::Positional(rvalue),
249                        ],
250                        parser_info: HashMap::new(),
251                    });
252
253                    return (
254                        Pipeline::from_vec(vec![Expression::new(
255                            working_set,
256                            Expr::Call(call),
257                            Span::concat(spans),
258                            Type::Any,
259                        )]),
260                        Some(lvalue.span),
261                    );
262                }
263            }
264        }
265        let ParsedInternalCall { call, output, .. } = parse_internal_call(
266            working_set,
267            spans[0],
268            &spans[1..],
269            decl_id,
270            ArgumentParsingLevel::Full,
271            None,
272        );
273
274        return (
275            Pipeline::from_vec(vec![Expression::new(
276                working_set,
277                Expr::Call(call),
278                Span::concat(spans),
279                output,
280            )]),
281            None,
282        );
283    } else {
284        working_set.error(ParseError::UnknownState(
285            "internal error: let or const statements not found in core language".into(),
286            Span::concat(spans),
287        ))
288    }
289
290    working_set.error(ParseError::UnknownState(
291        "internal error: let or const statement unparsable".into(),
292        Span::concat(spans),
293    ));
294
295    (garbage_pipeline(working_set, spans), None)
296}
297
298pub fn parse_mut(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline {
299    trace!("parsing: mut");
300
301    if let Some(decl_id) = working_set.find_decl(b"mut") {
302        if spans.len() >= 4 {
303            for span in spans.iter().enumerate() {
304                let item = working_set.get_span_contents(*span.1);
305                if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
306                    let (tokens, parse_error) = lex(
307                        working_set.get_span_contents(Span::concat(&spans[(span.0 + 1)..])),
308                        spans[span.0 + 1].start,
309                        &[],
310                        &[],
311                        false,
312                    );
313
314                    if let Some(parse_error) = parse_error {
315                        working_set.error(parse_error);
316                    }
317
318                    let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
319                    let rvalue_block =
320                        parse_block(working_set, &tokens, rvalue_span, false, true, None);
321
322                    let output_type = rvalue_block.output_type();
323
324                    let block_id = working_set.add_block(Arc::new(rvalue_block));
325
326                    let rvalue = Expression::new(
327                        working_set,
328                        Expr::Block(block_id),
329                        rvalue_span,
330                        output_type,
331                    );
332
333                    let mut idx = 0;
334
335                    let (lvalue, explicit_type) = parse_var_with_opt_type(
336                        working_set,
337                        &spans[1..(span.0)],
338                        &mut idx,
339                        true,
340                        None,
341                    );
342                    if idx + 1 < span.0 - 1 {
343                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
344                    }
345
346                    let var_id = lvalue.as_var();
347                    let rhs_type = rvalue.ty.clone();
348
349                    if let Some(explicit_type) = &explicit_type
350                        && !type_compatible(explicit_type, &rhs_type)
351                    {
352                        working_set.error(ParseError::TypeMismatch(
353                            explicit_type.clone(),
354                            rhs_type.clone(),
355                            Span::concat(&spans[(span.0 + 1)..]),
356                        ));
357                    }
358
359                    if let Some(var_id) = var_id
360                        && explicit_type.is_none()
361                    {
362                        working_set.set_variable_type(var_id, rhs_type);
363                    }
364
365                    let call = Box::new(Call {
366                        decl_id,
367                        head: spans[0],
368                        arguments: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
369                        parser_info: HashMap::new(),
370                    });
371
372                    return Pipeline::from_vec(vec![Expression::new(
373                        working_set,
374                        Expr::Call(call),
375                        Span::concat(spans),
376                        Type::Any,
377                    )]);
378                }
379            }
380        }
381        let ParsedInternalCall { call, output, .. } = parse_internal_call(
382            working_set,
383            spans[0],
384            &spans[1..],
385            decl_id,
386            ArgumentParsingLevel::Full,
387            None,
388        );
389
390        return Pipeline::from_vec(vec![Expression::new(
391            working_set,
392            Expr::Call(call),
393            Span::concat(spans),
394            output,
395        )]);
396    } else {
397        working_set.error(ParseError::UnknownState(
398            "internal error: let or const statements not found in core language".into(),
399            Span::concat(spans),
400        ))
401    }
402
403    working_set.error(ParseError::UnknownState(
404        "internal error: let or const statement unparsable".into(),
405        Span::concat(spans),
406    ));
407
408    garbage_pipeline(working_set, spans)
409}