nu_parser/
parse_keywords.rs

1use crate::{
2    exportable::Exportable,
3    parse_block,
4    parser::{parse_attribute, parse_redirection, redirecting_builtin_error},
5    type_check::{check_block_input_output, type_compatible},
6};
7use itertools::Itertools;
8use log::trace;
9use nu_path::canonicalize_with;
10use nu_protocol::{
11    ast::{
12        Argument, AttributeBlock, Block, Call, Expr, Expression, ImportPattern, ImportPatternHead,
13        ImportPatternMember, Pipeline, PipelineElement,
14    },
15    category_from_string,
16    engine::{StateWorkingSet, DEFAULT_OVERLAY_NAME},
17    eval_const::eval_constant,
18    parser_path::ParserPath,
19    Alias, BlockId, CustomExample, DeclId, FromValue, Module, ModuleId, ParseError, PositionalArg,
20    ResolvedImportPattern, ShellError, Span, Spanned, SyntaxShape, Type, Value, VarId,
21};
22use std::{
23    collections::{HashMap, HashSet},
24    path::{Path, PathBuf},
25    sync::Arc,
26};
27
28pub const LIB_DIRS_VAR: &str = "NU_LIB_DIRS";
29#[cfg(feature = "plugin")]
30pub const PLUGIN_DIRS_VAR: &str = "NU_PLUGIN_DIRS";
31
32use crate::{
33    is_math_expression_like,
34    known_external::KnownExternal,
35    lex,
36    lite_parser::{lite_parse, LiteCommand},
37    parser::{
38        check_call, garbage, garbage_pipeline, parse, parse_call, parse_expression,
39        parse_full_signature, parse_import_pattern, parse_internal_call, parse_string, parse_value,
40        parse_var_with_opt_type, trim_quotes, ParsedInternalCall,
41    },
42    unescape_unquote_string, Token, TokenContents,
43};
44
45/// These parser keywords can be aliased
46pub const ALIASABLE_PARSER_KEYWORDS: &[&[u8]] = &[
47    b"if",
48    b"match",
49    b"try",
50    b"overlay",
51    b"overlay hide",
52    b"overlay new",
53    b"overlay use",
54];
55
56pub const RESERVED_VARIABLE_NAMES: [&str; 3] = ["in", "nu", "env"];
57
58/// These parser keywords cannot be aliased (either not possible, or support not yet added)
59pub const UNALIASABLE_PARSER_KEYWORDS: &[&[u8]] = &[
60    b"alias",
61    b"const",
62    b"def",
63    b"extern",
64    b"module",
65    b"use",
66    b"export",
67    b"export alias",
68    b"export const",
69    b"export def",
70    b"export extern",
71    b"export module",
72    b"export use",
73    b"for",
74    b"loop",
75    b"while",
76    b"return",
77    b"break",
78    b"continue",
79    b"let",
80    b"mut",
81    b"hide",
82    b"export-env",
83    b"source-env",
84    b"source",
85    b"where",
86    b"plugin use",
87];
88
89/// Check whether spans start with a parser keyword that can be aliased
90pub fn is_unaliasable_parser_keyword(working_set: &StateWorkingSet, spans: &[Span]) -> bool {
91    // try two words
92    if let (Some(&span1), Some(&span2)) = (spans.first(), spans.get(1)) {
93        let cmd_name = working_set.get_span_contents(Span::append(span1, span2));
94        return UNALIASABLE_PARSER_KEYWORDS.contains(&cmd_name);
95    }
96
97    // try one word
98    if let Some(&span1) = spans.first() {
99        let cmd_name = working_set.get_span_contents(span1);
100        UNALIASABLE_PARSER_KEYWORDS.contains(&cmd_name)
101    } else {
102        false
103    }
104}
105
106/// This is a new more compact method of calling parse_xxx() functions without repeating the
107/// parse_call() in each function. Remaining keywords can be moved here.
108pub fn parse_keyword(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
109    let orig_parse_errors_len = working_set.parse_errors.len();
110
111    let call_expr = parse_call(working_set, &lite_command.parts, lite_command.parts[0]);
112
113    // If an error occurred, don't invoke the keyword-specific functionality
114    if working_set.parse_errors.len() > orig_parse_errors_len {
115        return Pipeline::from_vec(vec![call_expr]);
116    }
117
118    if let Expression {
119        expr: Expr::Call(call),
120        ..
121    } = call_expr.clone()
122    {
123        // Apply parse keyword side effects
124        let cmd = working_set.get_decl(call.decl_id);
125        // check help flag first.
126        if call.named_iter().any(|(flag, _, _)| flag.item == "help") {
127            let call_span = call.span();
128            return Pipeline::from_vec(vec![Expression::new(
129                working_set,
130                Expr::Call(call),
131                call_span,
132                Type::Any,
133            )]);
134        }
135
136        match cmd.name() {
137            "overlay hide" => parse_overlay_hide(working_set, call),
138            "overlay new" => parse_overlay_new(working_set, call),
139            "overlay use" => parse_overlay_use(working_set, call),
140            #[cfg(feature = "plugin")]
141            "plugin use" => parse_plugin_use(working_set, call),
142            _ => Pipeline::from_vec(vec![call_expr]),
143        }
144    } else {
145        Pipeline::from_vec(vec![call_expr])
146    }
147}
148
149pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
150    let mut pos = 0;
151
152    let def_type_name = if spans.len() >= 3 {
153        // definition can't have only two spans, minimum is 3, e.g., 'extern spam []'
154        let first_word = working_set.get_span_contents(spans[0]);
155
156        if first_word == b"export" {
157            pos += 2;
158        } else {
159            pos += 1;
160        }
161
162        working_set.get_span_contents(spans[pos - 1]).to_vec()
163    } else {
164        return;
165    };
166
167    if def_type_name != b"def" && def_type_name != b"extern" {
168        return;
169    }
170
171    // Now, pos should point at the next span after the def-like call.
172    // Skip all potential flags, like --env, --wrapped or --help:
173    while pos < spans.len() && working_set.get_span_contents(spans[pos]).starts_with(b"-") {
174        pos += 1;
175    }
176
177    if pos >= spans.len() {
178        // This can happen if the call ends with a flag, e.g., 'def --help'
179        return;
180    }
181
182    // Now, pos should point at the command name.
183    let name_pos = pos;
184
185    let Some(name) = parse_string(working_set, spans[name_pos]).as_string() else {
186        return;
187    };
188
189    if name.contains('#')
190        || name.contains('^')
191        || name.parse::<bytesize::ByteSize>().is_ok()
192        || name.parse::<f64>().is_ok()
193    {
194        working_set.error(ParseError::CommandDefNotValid(spans[name_pos]));
195        return;
196    }
197
198    // Find signature
199    let mut signature_pos = None;
200
201    while pos < spans.len() {
202        if working_set.get_span_contents(spans[pos]).starts_with(b"[")
203            || working_set.get_span_contents(spans[pos]).starts_with(b"(")
204        {
205            signature_pos = Some(pos);
206            break;
207        }
208
209        pos += 1;
210    }
211
212    let Some(signature_pos) = signature_pos else {
213        return;
214    };
215
216    let mut allow_unknown_args = false;
217
218    for span in spans {
219        if working_set.get_span_contents(*span) == b"--wrapped" && def_type_name == b"def" {
220            allow_unknown_args = true;
221        }
222    }
223
224    let starting_error_count = working_set.parse_errors.len();
225
226    working_set.enter_scope();
227    // FIXME: because parse_signature will update the scope with the variables it sees
228    // we end up parsing the signature twice per def. The first time is during the predecl
229    // so that we can see the types that are part of the signature, which we need for parsing.
230    // The second time is when we actually parse the body itworking_set.
231    // We can't reuse the first time because the variables that are created during parse_signature
232    // are lost when we exit the scope below.
233    let sig = parse_full_signature(working_set, &spans[signature_pos..]);
234    working_set.parse_errors.truncate(starting_error_count);
235    working_set.exit_scope();
236
237    let Some(mut signature) = sig.as_signature() else {
238        return;
239    };
240
241    signature.name = name;
242
243    if allow_unknown_args {
244        signature.allows_unknown_args = true;
245    }
246
247    let decl = signature.predeclare();
248
249    if working_set.add_predecl(decl).is_some() {
250        working_set.error(ParseError::DuplicateCommandDef(spans[name_pos]));
251    }
252}
253
254pub fn parse_for(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Expression {
255    let spans = &lite_command.parts;
256    // Checking that the function is used with the correct name
257    // Maybe this is not necessary but it is a sanity check
258    if working_set.get_span_contents(spans[0]) != b"for" {
259        working_set.error(ParseError::UnknownState(
260            "internal error: Wrong call name for 'for' function".into(),
261            Span::concat(spans),
262        ));
263        return garbage(working_set, spans[0]);
264    }
265    if let Some(redirection) = lite_command.redirection.as_ref() {
266        working_set.error(redirecting_builtin_error("for", redirection));
267        return garbage(working_set, spans[0]);
268    }
269
270    // Parsing the spans and checking that they match the register signature
271    // Using a parsed call makes more sense than checking for how many spans are in the call
272    // Also, by creating a call, it can be checked if it matches the declaration signature
273    let (call, call_span) = match working_set.find_decl(b"for") {
274        None => {
275            working_set.error(ParseError::UnknownState(
276                "internal error: for declaration not found".into(),
277                Span::concat(spans),
278            ));
279            return garbage(working_set, spans[0]);
280        }
281        Some(decl_id) => {
282            working_set.enter_scope();
283            let ParsedInternalCall { call, output } =
284                parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
285
286            working_set.exit_scope();
287
288            let call_span = Span::concat(spans);
289            let decl = working_set.get_decl(decl_id);
290            let sig = decl.signature();
291
292            let starting_error_count = working_set.parse_errors.len();
293            check_call(working_set, call_span, &sig, &call);
294
295            let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
296                return garbage(working_set, spans[0]);
297            };
298
299            if starting_error_count != working_set.parse_errors.len() || is_help {
300                return Expression::new(working_set, Expr::Call(call), call_span, output);
301            }
302
303            // Let's get our block and make sure it has the right signature
304            if let Some(
305                Expression {
306                    expr: Expr::Block(block_id),
307                    ..
308                }
309                | Expression {
310                    expr: Expr::RowCondition(block_id),
311                    ..
312                },
313            ) = call.positional_nth(2)
314            {
315                {
316                    let block = working_set.get_block_mut(*block_id);
317
318                    block.signature = Box::new(sig);
319                }
320            }
321
322            (call, call_span)
323        }
324    };
325
326    // All positional arguments must be in the call positional vector by this point
327    let var_decl = call.positional_nth(0).expect("for call already checked");
328    let iteration_expr = call.positional_nth(1).expect("for call already checked");
329    let block = call.positional_nth(2).expect("for call already checked");
330
331    let iteration_expr_ty = iteration_expr.ty.clone();
332
333    // Figure out the type of the variable the `for` uses for iteration
334    let var_type = match iteration_expr_ty {
335        Type::List(x) => *x,
336        Type::Table(x) => Type::Record(x),
337        x => x,
338    };
339
340    if let (Some(var_id), Some(block_id)) = (&var_decl.as_var(), block.as_block()) {
341        working_set.set_variable_type(*var_id, var_type.clone());
342
343        let block = working_set.get_block_mut(block_id);
344
345        block.signature.required_positional.insert(
346            0,
347            PositionalArg {
348                name: String::new(),
349                desc: String::new(),
350                shape: var_type.to_shape(),
351                var_id: Some(*var_id),
352                default_value: None,
353            },
354        );
355    }
356
357    Expression::new(working_set, Expr::Call(call), call_span, Type::Nothing)
358}
359
360/// If `name` is a keyword, emit an error.
361fn verify_not_reserved_variable_name(working_set: &mut StateWorkingSet, name: &str, span: Span) {
362    if RESERVED_VARIABLE_NAMES.contains(&name) {
363        working_set.error(ParseError::NameIsBuiltinVar(name.to_string(), span))
364    }
365}
366
367// This is meant for parsing attribute blocks without an accompanying `def` or `extern`. It's
368// necessary to provide consistent syntax highlighting, completions, and helpful errors
369//
370// There is no need to run the const evaluation here
371pub fn parse_attribute_block(
372    working_set: &mut StateWorkingSet,
373    lite_command: &LiteCommand,
374) -> Pipeline {
375    let attributes = lite_command
376        .attribute_commands()
377        .map(|cmd| parse_attribute(working_set, &cmd).0)
378        .collect::<Vec<_>>();
379
380    let last_attr_span = attributes
381        .last()
382        .expect("Attribute block must contain at least one attribute")
383        .expr
384        .span;
385
386    working_set.error(ParseError::AttributeRequiresDefinition(last_attr_span));
387    let cmd_span = if lite_command.command_parts().is_empty() {
388        last_attr_span.past()
389    } else {
390        Span::concat(lite_command.command_parts())
391    };
392    let cmd_expr = garbage(working_set, cmd_span);
393    let ty = cmd_expr.ty.clone();
394
395    let attr_block_span = Span::merge_many(
396        attributes
397            .first()
398            .map(|x| x.expr.span)
399            .into_iter()
400            .chain(Some(cmd_span)),
401    );
402
403    Pipeline::from_vec(vec![Expression::new(
404        working_set,
405        Expr::AttributeBlock(AttributeBlock {
406            attributes,
407            item: Box::new(cmd_expr),
408        }),
409        attr_block_span,
410        ty,
411    )])
412}
413
414// Returns also the parsed command name and ID
415pub fn parse_def(
416    working_set: &mut StateWorkingSet,
417    lite_command: &LiteCommand,
418    module_name: Option<&[u8]>,
419) -> (Pipeline, Option<(Vec<u8>, DeclId)>) {
420    let mut attributes = vec![];
421    let mut attribute_vals = vec![];
422
423    for attr_cmd in lite_command.attribute_commands() {
424        let (attr, name) = parse_attribute(working_set, &attr_cmd);
425        if let Some(name) = name {
426            let val = eval_constant(working_set, &attr.expr);
427            match val {
428                Ok(val) => attribute_vals.push((name, val)),
429                Err(e) => working_set.error(e.wrap(working_set, attr.expr.span)),
430            }
431        }
432        attributes.push(attr);
433    }
434
435    let (expr, decl) = parse_def_inner(working_set, attribute_vals, lite_command, module_name);
436
437    let ty = expr.ty.clone();
438
439    let attr_block_span = Span::merge_many(
440        attributes
441            .first()
442            .map(|x| x.expr.span)
443            .into_iter()
444            .chain(Some(expr.span)),
445    );
446
447    let expr = if attributes.is_empty() {
448        expr
449    } else {
450        Expression::new(
451            working_set,
452            Expr::AttributeBlock(AttributeBlock {
453                attributes,
454                item: Box::new(expr),
455            }),
456            attr_block_span,
457            ty,
458        )
459    };
460
461    (Pipeline::from_vec(vec![expr]), decl)
462}
463
464pub fn parse_extern(
465    working_set: &mut StateWorkingSet,
466    lite_command: &LiteCommand,
467    module_name: Option<&[u8]>,
468) -> Pipeline {
469    let mut attributes = vec![];
470    let mut attribute_vals = vec![];
471
472    for attr_cmd in lite_command.attribute_commands() {
473        let (attr, name) = parse_attribute(working_set, &attr_cmd);
474        if let Some(name) = name {
475            let val = eval_constant(working_set, &attr.expr);
476            match val {
477                Ok(val) => attribute_vals.push((name, val)),
478                Err(e) => working_set.error(e.wrap(working_set, attr.expr.span)),
479            }
480        }
481        attributes.push(attr);
482    }
483
484    let expr = parse_extern_inner(working_set, attribute_vals, lite_command, module_name);
485
486    let ty = expr.ty.clone();
487
488    let attr_block_span = Span::merge_many(
489        attributes
490            .first()
491            .map(|x| x.expr.span)
492            .into_iter()
493            .chain(Some(expr.span)),
494    );
495
496    let expr = if attributes.is_empty() {
497        expr
498    } else {
499        Expression::new(
500            working_set,
501            Expr::AttributeBlock(AttributeBlock {
502                attributes,
503                item: Box::new(expr),
504            }),
505            attr_block_span,
506            ty,
507        )
508    };
509
510    Pipeline::from_vec(vec![expr])
511}
512
513// Returns also the parsed command name and ID
514fn parse_def_inner(
515    working_set: &mut StateWorkingSet,
516    attributes: Vec<(String, Value)>,
517    lite_command: &LiteCommand,
518    module_name: Option<&[u8]>,
519) -> (Expression, Option<(Vec<u8>, DeclId)>) {
520    let spans = lite_command.command_parts();
521
522    let (desc, extra_desc) = working_set.build_desc(&lite_command.comments);
523
524    let (attribute_vals, examples, search_terms, category) =
525        handle_special_attributes(attributes, working_set);
526
527    // Checking that the function is used with the correct name
528    // Maybe this is not necessary but it is a sanity check
529    // Note: "export def" is treated the same as "def"
530
531    let (name_span, split_id) =
532        if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
533            (spans[1], 2)
534        } else {
535            (spans[0], 1)
536        };
537
538    let def_call = working_set.get_span_contents(name_span).to_vec();
539    if def_call != b"def" {
540        working_set.error(ParseError::UnknownState(
541            "internal error: Wrong call name for def function".into(),
542            Span::concat(spans),
543        ));
544        return (garbage(working_set, Span::concat(spans)), None);
545    }
546    if let Some(redirection) = lite_command.redirection.as_ref() {
547        working_set.error(redirecting_builtin_error("def", redirection));
548        return (garbage(working_set, Span::concat(spans)), None);
549    }
550
551    // Parsing the spans and checking that they match the register signature
552    // Using a parsed call makes more sense than checking for how many spans are in the call
553    // Also, by creating a call, it can be checked if it matches the declaration signature
554    let (call, call_span) = match working_set.find_decl(&def_call) {
555        None => {
556            working_set.error(ParseError::UnknownState(
557                "internal error: def declaration not found".into(),
558                Span::concat(spans),
559            ));
560            return (garbage(working_set, Span::concat(spans)), None);
561        }
562        Some(decl_id) => {
563            working_set.enter_scope();
564            let (command_spans, rest_spans) = spans.split_at(split_id);
565
566            // Find the first span that is not a flag
567            let mut decl_name_span = None;
568
569            for span in rest_spans {
570                if !working_set.get_span_contents(*span).starts_with(b"-") {
571                    decl_name_span = Some(*span);
572                    break;
573                }
574            }
575
576            if let Some(name_span) = decl_name_span {
577                // Check whether name contains [] or () -- possible missing space error
578                if let Some(err) = detect_params_in_name(
579                    working_set,
580                    name_span,
581                    String::from_utf8_lossy(&def_call).as_ref(),
582                ) {
583                    working_set.error(err);
584                    return (garbage(working_set, Span::concat(spans)), None);
585                }
586            }
587
588            let starting_error_count = working_set.parse_errors.len();
589            let ParsedInternalCall { call, output } = parse_internal_call(
590                working_set,
591                Span::concat(command_spans),
592                rest_spans,
593                decl_id,
594            );
595            // This is to preserve the order of the errors so that
596            // the check errors below come first
597            let mut new_errors = working_set.parse_errors[starting_error_count..].to_vec();
598            working_set.parse_errors.truncate(starting_error_count);
599
600            working_set.exit_scope();
601
602            let call_span = Span::concat(spans);
603            let decl = working_set.get_decl(decl_id);
604            let sig = decl.signature();
605
606            // Let's get our block and make sure it has the right signature
607            if let Some(arg) = call.positional_nth(2) {
608                match arg {
609                    Expression {
610                        expr: Expr::Closure(block_id),
611                        ..
612                    } => {
613                        let block = working_set.get_block_mut(*block_id);
614                        block.signature = Box::new(sig.clone());
615                    }
616                    _ => working_set.error(ParseError::Expected(
617                        "definition body closure { ... }",
618                        arg.span,
619                    )),
620                }
621            }
622
623            let starting_error_count = working_set.parse_errors.len();
624            check_call(working_set, call_span, &sig, &call);
625            working_set.parse_errors.append(&mut new_errors);
626
627            let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
628                return (garbage(working_set, Span::concat(spans)), None);
629            };
630
631            if starting_error_count != working_set.parse_errors.len() || is_help {
632                return (
633                    Expression::new(working_set, Expr::Call(call), call_span, output),
634                    None,
635                );
636            }
637
638            (call, call_span)
639        }
640    };
641
642    let Ok(has_env) = has_flag_const(working_set, &call, "env") else {
643        return (garbage(working_set, Span::concat(spans)), None);
644    };
645    let Ok(has_wrapped) = has_flag_const(working_set, &call, "wrapped") else {
646        return (garbage(working_set, Span::concat(spans)), None);
647    };
648
649    // All positional arguments must be in the call positional vector by this point
650    let name_expr = call.positional_nth(0).expect("def call already checked");
651    let sig = call.positional_nth(1).expect("def call already checked");
652    let block = call.positional_nth(2).expect("def call already checked");
653
654    let name = if let Some(name) = name_expr.as_string() {
655        if let Some(mod_name) = module_name {
656            if name.as_bytes() == mod_name {
657                let name_expr_span = name_expr.span;
658
659                working_set.error(ParseError::NamedAsModule(
660                    "command".to_string(),
661                    name,
662                    "main".to_string(),
663                    name_expr_span,
664                ));
665                return (
666                    Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
667                    None,
668                );
669            }
670        }
671
672        name
673    } else {
674        working_set.error(ParseError::UnknownState(
675            "Could not get string from string expression".into(),
676            name_expr.span,
677        ));
678        return (garbage(working_set, Span::concat(spans)), None);
679    };
680
681    let mut result = None;
682
683    if let (Some(mut signature), Some(block_id)) = (sig.as_signature(), block.as_block()) {
684        for arg_name in &signature.required_positional {
685            verify_not_reserved_variable_name(working_set, &arg_name.name, sig.span);
686        }
687        for arg_name in &signature.optional_positional {
688            verify_not_reserved_variable_name(working_set, &arg_name.name, sig.span);
689        }
690        if let Some(arg_name) = &signature.rest_positional {
691            verify_not_reserved_variable_name(working_set, &arg_name.name, sig.span);
692        }
693        for flag_name in &signature.get_names() {
694            verify_not_reserved_variable_name(working_set, flag_name, sig.span);
695        }
696
697        if has_wrapped {
698            if let Some(rest) = &signature.rest_positional {
699                if let Some(var_id) = rest.var_id {
700                    let rest_var = &working_set.get_variable(var_id);
701
702                    if rest_var.ty != Type::Any && rest_var.ty != Type::List(Box::new(Type::String))
703                    {
704                        working_set.error(ParseError::TypeMismatchHelp(
705                            Type::List(Box::new(Type::String)),
706                            rest_var.ty.clone(),
707                            rest_var.declaration_span,
708                            format!("...rest-like positional argument used in 'def --wrapped' supports only strings. Change the type annotation of ...{} to 'string'.", &rest.name)));
709
710                        return (
711                            Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
712                            result,
713                        );
714                    }
715                }
716            } else {
717                working_set.error(ParseError::MissingPositional("...rest-like positional argument".to_string(), name_expr.span, "def --wrapped must have a ...rest-like positional argument. Add '...rest: string' to the command's signature.".to_string()));
718
719                return (
720                    Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
721                    result,
722                );
723            }
724        }
725
726        if let Some(decl_id) = working_set.find_predecl(name.as_bytes()) {
727            let declaration = working_set.get_decl_mut(decl_id);
728
729            signature.name.clone_from(&name);
730            if !has_wrapped {
731                *signature = signature.add_help();
732            }
733            signature.description = desc;
734            signature.extra_description = extra_desc;
735            signature.allows_unknown_args = has_wrapped;
736            signature.search_terms = search_terms;
737            signature.category = category_from_string(&category);
738
739            *declaration = signature
740                .clone()
741                .into_block_command(block_id, attribute_vals, examples);
742
743            let block = working_set.get_block_mut(block_id);
744            block.signature = signature;
745            block.redirect_env = has_env;
746
747            if block.signature.input_output_types.is_empty() {
748                block
749                    .signature
750                    .input_output_types
751                    .push((Type::Any, Type::Any));
752            }
753
754            let block = working_set.get_block(block_id);
755
756            let typecheck_errors = check_block_input_output(working_set, block);
757
758            working_set
759                .parse_errors
760                .extend_from_slice(&typecheck_errors);
761
762            result = Some((name.as_bytes().to_vec(), decl_id));
763        } else {
764            working_set.error(ParseError::InternalError(
765                "Predeclaration failed to add declaration".into(),
766                name_expr.span,
767            ));
768        };
769    }
770
771    // It's OK if it returns None: The decl was already merged in previous parse pass.
772    working_set.merge_predecl(name.as_bytes());
773
774    (
775        Expression::new(working_set, Expr::Call(call), call_span, Type::Any),
776        result,
777    )
778}
779
780fn parse_extern_inner(
781    working_set: &mut StateWorkingSet,
782    attributes: Vec<(String, Value)>,
783    lite_command: &LiteCommand,
784    module_name: Option<&[u8]>,
785) -> Expression {
786    let spans = lite_command.command_parts();
787    let concat_span = Span::concat(spans);
788
789    let (description, extra_description) = working_set.build_desc(&lite_command.comments);
790
791    let (attribute_vals, examples, search_terms, category) =
792        handle_special_attributes(attributes, working_set);
793
794    // Checking that the function is used with the correct name
795    // Maybe this is not necessary but it is a sanity check
796
797    let (name_span, split_id) =
798        if spans.len() > 1 && (working_set.get_span_contents(spans[0]) == b"export") {
799            (spans[1], 2)
800        } else {
801            (spans[0], 1)
802        };
803
804    let extern_call = working_set.get_span_contents(name_span).to_vec();
805    if extern_call != b"extern" {
806        working_set.error(ParseError::UnknownState(
807            "internal error: Wrong call name for extern command".into(),
808            Span::concat(spans),
809        ));
810        return garbage(working_set, concat_span);
811    }
812    if let Some(redirection) = lite_command.redirection.as_ref() {
813        working_set.error(redirecting_builtin_error("extern", redirection));
814        return garbage(working_set, concat_span);
815    }
816
817    // Parsing the spans and checking that they match the register signature
818    // Using a parsed call makes more sense than checking for how many spans are in the call
819    // Also, by creating a call, it can be checked if it matches the declaration signature
820    let (call, call_span) = match working_set.find_decl(&extern_call) {
821        None => {
822            working_set.error(ParseError::UnknownState(
823                "internal error: def declaration not found".into(),
824                Span::concat(spans),
825            ));
826            return garbage(working_set, concat_span);
827        }
828        Some(decl_id) => {
829            working_set.enter_scope();
830
831            let (command_spans, rest_spans) = spans.split_at(split_id);
832
833            if let Some(name_span) = rest_spans.first() {
834                if let Some(err) = detect_params_in_name(
835                    working_set,
836                    *name_span,
837                    String::from_utf8_lossy(&extern_call).as_ref(),
838                ) {
839                    working_set.error(err);
840                    return garbage(working_set, concat_span);
841                }
842            }
843
844            let ParsedInternalCall { call, .. } = parse_internal_call(
845                working_set,
846                Span::concat(command_spans),
847                rest_spans,
848                decl_id,
849            );
850            working_set.exit_scope();
851
852            let call_span = Span::concat(spans);
853            //let decl = working_set.get_decl(decl_id);
854            //let sig = decl.signature();
855
856            (call, call_span)
857        }
858    };
859    let name_expr = call.positional_nth(0);
860    let sig = call.positional_nth(1);
861    let body = call.positional_nth(2);
862
863    if let (Some(name_expr), Some(sig)) = (name_expr, sig) {
864        if let (Some(name), Some(mut signature)) = (&name_expr.as_string(), sig.as_signature()) {
865            if let Some(mod_name) = module_name {
866                if name.as_bytes() == mod_name {
867                    let name_expr_span = name_expr.span;
868                    working_set.error(ParseError::NamedAsModule(
869                        "known external".to_string(),
870                        name.clone(),
871                        "main".to_string(),
872                        name_expr_span,
873                    ));
874                    return Expression::new(working_set, Expr::Call(call), call_span, Type::Any);
875                }
876            }
877
878            if let Some(decl_id) = working_set.find_predecl(name.as_bytes()) {
879                let declaration = working_set.get_decl_mut(decl_id);
880
881                let external_name = if let Some(mod_name) = module_name {
882                    if name.as_bytes() == b"main" {
883                        String::from_utf8_lossy(mod_name).to_string()
884                    } else {
885                        name.clone()
886                    }
887                } else {
888                    name.clone()
889                };
890
891                signature.name = external_name;
892                signature.description = description;
893                signature.extra_description = extra_description;
894                signature.search_terms = search_terms;
895                signature.allows_unknown_args = true;
896                signature.category = category_from_string(&category);
897
898                if let Some(block_id) = body.and_then(|x| x.as_block()) {
899                    if signature.rest_positional.is_none() {
900                        working_set.error(ParseError::InternalError(
901                            "Extern block must have a rest positional argument".into(),
902                            name_expr.span,
903                        ));
904                    } else {
905                        *declaration = signature.clone().into_block_command(
906                            block_id,
907                            attribute_vals,
908                            examples,
909                        );
910
911                        working_set.get_block_mut(block_id).signature = signature;
912                    }
913                } else {
914                    if signature.rest_positional.is_none() {
915                        // Make sure that a known external takes rest args with ExternalArgument
916                        // shape
917                        *signature = signature.rest(
918                            "args",
919                            SyntaxShape::ExternalArgument,
920                            "all other arguments to the command",
921                        );
922                    }
923
924                    let decl = KnownExternal {
925                        signature,
926                        attributes: attribute_vals,
927                        examples,
928                    };
929
930                    *declaration = Box::new(decl);
931                }
932            } else {
933                working_set.error(ParseError::InternalError(
934                    "Predeclaration failed to add declaration".into(),
935                    spans[split_id],
936                ));
937            };
938        }
939        if let Some(name) = name_expr.as_string() {
940            // It's OK if it returns None: The decl was already merged in previous parse pass.
941            working_set.merge_predecl(name.as_bytes());
942        } else {
943            working_set.error(ParseError::UnknownState(
944                "Could not get string from string expression".into(),
945                name_expr.span,
946            ));
947        }
948    }
949
950    Expression::new(working_set, Expr::Call(call), call_span, Type::Any)
951}
952
953#[allow(clippy::type_complexity)]
954fn handle_special_attributes(
955    attributes: Vec<(String, Value)>,
956    working_set: &mut StateWorkingSet<'_>,
957) -> (
958    Vec<(String, Value)>,
959    Vec<CustomExample>,
960    Vec<String>,
961    String,
962) {
963    let mut attribute_vals = vec![];
964    let mut examples = vec![];
965    let mut search_terms = vec![];
966    let mut category = String::new();
967
968    for (name, value) in attributes {
969        let val_span = value.span();
970        match name.as_str() {
971            "example" => match CustomExample::from_value(value) {
972                Ok(example) => examples.push(example),
973                Err(_) => {
974                    let e = ShellError::GenericError {
975                        error: "nu::shell::invalid_example".into(),
976                        msg: "Value couldn't be converted to an example".into(),
977                        span: Some(val_span),
978                        help: Some("Is `attr example` shadowed?".into()),
979                        inner: vec![],
980                    };
981                    working_set.error(e.wrap(working_set, val_span));
982                }
983            },
984            "search-terms" => match <Vec<String>>::from_value(value) {
985                Ok(mut terms) => {
986                    search_terms.append(&mut terms);
987                }
988                Err(_) => {
989                    let e = ShellError::GenericError {
990                        error: "nu::shell::invalid_search_terms".into(),
991                        msg: "Value couldn't be converted to search-terms".into(),
992                        span: Some(val_span),
993                        help: Some("Is `attr search-terms` shadowed?".into()),
994                        inner: vec![],
995                    };
996                    working_set.error(e.wrap(working_set, val_span));
997                }
998            },
999            "category" => match <String>::from_value(value) {
1000                Ok(term) => {
1001                    category.push_str(&term);
1002                }
1003                Err(_) => {
1004                    let e = ShellError::GenericError {
1005                        error: "nu::shell::invalid_category".into(),
1006                        msg: "Value couldn't be converted to category".into(),
1007                        span: Some(val_span),
1008                        help: Some("Is `attr category` shadowed?".into()),
1009                        inner: vec![],
1010                    };
1011                    working_set.error(e.wrap(working_set, val_span));
1012                }
1013            },
1014            _ => {
1015                attribute_vals.push((name, value));
1016            }
1017        }
1018    }
1019    (attribute_vals, examples, search_terms, category)
1020}
1021
1022fn check_alias_name<'a>(working_set: &mut StateWorkingSet, spans: &'a [Span]) -> Option<&'a Span> {
1023    let command_len = if !spans.is_empty() {
1024        if working_set.get_span_contents(spans[0]) == b"export" {
1025            2
1026        } else {
1027            1
1028        }
1029    } else {
1030        return None;
1031    };
1032
1033    if spans.len() == 1 {
1034        None
1035    } else if spans.len() < command_len + 3 {
1036        if working_set.get_span_contents(spans[command_len]) == b"=" {
1037            let name = String::from_utf8_lossy(
1038                working_set.get_span_contents(Span::concat(&spans[..command_len])),
1039            );
1040            working_set.error(ParseError::AssignmentMismatch(
1041                format!("{name} missing name"),
1042                "missing name".into(),
1043                spans[command_len],
1044            ));
1045            Some(&spans[command_len])
1046        } else {
1047            None
1048        }
1049    } else if working_set.get_span_contents(spans[command_len + 1]) != b"=" {
1050        let name = String::from_utf8_lossy(
1051            working_set.get_span_contents(Span::concat(&spans[..command_len])),
1052        );
1053        working_set.error(ParseError::AssignmentMismatch(
1054            format!("{name} missing sign"),
1055            "missing equal sign".into(),
1056            spans[command_len + 1],
1057        ));
1058        Some(&spans[command_len + 1])
1059    } else {
1060        None
1061    }
1062}
1063
1064pub fn parse_alias(
1065    working_set: &mut StateWorkingSet,
1066    lite_command: &LiteCommand,
1067    module_name: Option<&[u8]>,
1068) -> Pipeline {
1069    let spans = &lite_command.parts;
1070
1071    let (name_span, split_id) =
1072        if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
1073            (spans[1], 2)
1074        } else {
1075            (spans[0], 1)
1076        };
1077
1078    let name = working_set.get_span_contents(name_span);
1079
1080    if name != b"alias" {
1081        working_set.error(ParseError::InternalError(
1082            "Alias statement unparsable".into(),
1083            Span::concat(spans),
1084        ));
1085        return garbage_pipeline(working_set, spans);
1086    }
1087    if let Some(redirection) = lite_command.redirection.as_ref() {
1088        working_set.error(redirecting_builtin_error("alias", redirection));
1089        return garbage_pipeline(working_set, spans);
1090    }
1091
1092    if let Some(span) = check_alias_name(working_set, spans) {
1093        return Pipeline::from_vec(vec![garbage(working_set, *span)]);
1094    }
1095
1096    if let Some(decl_id) = working_set.find_decl(b"alias") {
1097        let (command_spans, rest_spans) = spans.split_at(split_id);
1098
1099        let original_starting_error_count = working_set.parse_errors.len();
1100
1101        let ParsedInternalCall {
1102            call: alias_call,
1103            output,
1104            ..
1105        } = parse_internal_call(
1106            working_set,
1107            Span::concat(command_spans),
1108            rest_spans,
1109            decl_id,
1110        );
1111
1112        working_set
1113            .parse_errors
1114            .truncate(original_starting_error_count);
1115
1116        let Ok(has_help_flag) = has_flag_const(working_set, &alias_call, "help") else {
1117            return garbage_pipeline(working_set, spans);
1118        };
1119
1120        let alias_pipeline = Pipeline::from_vec(vec![Expression::new(
1121            working_set,
1122            Expr::Call(alias_call.clone()),
1123            Span::concat(spans),
1124            output,
1125        )]);
1126
1127        if has_help_flag {
1128            return alias_pipeline;
1129        }
1130
1131        let Some(alias_name_expr) = alias_call.positional_nth(0) else {
1132            working_set.error(ParseError::UnknownState(
1133                "Missing positional after call check".to_string(),
1134                Span::concat(spans),
1135            ));
1136            return garbage_pipeline(working_set, spans);
1137        };
1138
1139        let alias_name = if let Some(name) = alias_name_expr.as_string() {
1140            if name.contains('#')
1141                || name.contains('^')
1142                || name.parse::<bytesize::ByteSize>().is_ok()
1143                || name.parse::<f64>().is_ok()
1144            {
1145                working_set.error(ParseError::AliasNotValid(alias_name_expr.span));
1146                return garbage_pipeline(working_set, spans);
1147            } else {
1148                name
1149            }
1150        } else {
1151            working_set.error(ParseError::AliasNotValid(alias_name_expr.span));
1152            return garbage_pipeline(working_set, spans);
1153        };
1154
1155        if spans.len() >= split_id + 3 {
1156            if let Some(mod_name) = module_name {
1157                if alias_name.as_bytes() == mod_name {
1158                    working_set.error(ParseError::NamedAsModule(
1159                        "alias".to_string(),
1160                        alias_name,
1161                        "main".to_string(),
1162                        spans[split_id],
1163                    ));
1164
1165                    return alias_pipeline;
1166                }
1167
1168                if alias_name == "main" {
1169                    working_set.error(ParseError::ExportMainAliasNotAllowed(spans[split_id]));
1170                    return alias_pipeline;
1171                }
1172            }
1173
1174            let _equals = working_set.get_span_contents(spans[split_id + 1]);
1175
1176            let replacement_spans = &spans[(split_id + 2)..];
1177            let first_bytes = working_set.get_span_contents(replacement_spans[0]);
1178
1179            if first_bytes != b"if"
1180                && first_bytes != b"match"
1181                && is_math_expression_like(working_set, replacement_spans[0])
1182            {
1183                // TODO: Maybe we need to implement a Display trait for Expression?
1184                let starting_error_count = working_set.parse_errors.len();
1185                let expr = parse_expression(working_set, replacement_spans);
1186                working_set.parse_errors.truncate(starting_error_count);
1187
1188                let msg = format!("{:?}", expr.expr);
1189                let msg_parts: Vec<&str> = msg.split('(').collect();
1190
1191                working_set.error(ParseError::CantAliasExpression(
1192                    msg_parts[0].to_string(),
1193                    replacement_spans[0],
1194                ));
1195                return alias_pipeline;
1196            }
1197
1198            let starting_error_count = working_set.parse_errors.len();
1199            working_set.search_predecls = false;
1200
1201            let expr = parse_call(working_set, replacement_spans, replacement_spans[0]);
1202
1203            working_set.search_predecls = true;
1204
1205            if starting_error_count != working_set.parse_errors.len() {
1206                if let Some(e) = working_set.parse_errors.get(starting_error_count) {
1207                    if let ParseError::MissingPositional(..) = e {
1208                        working_set
1209                            .parse_errors
1210                            .truncate(original_starting_error_count);
1211                        // ignore missing required positional
1212                    } else {
1213                        return garbage_pipeline(working_set, replacement_spans);
1214                    }
1215                }
1216            }
1217
1218            let (command, wrapped_call) = match expr {
1219                Expression {
1220                    expr: Expr::Call(ref rhs_call),
1221                    ..
1222                } => {
1223                    let cmd = working_set.get_decl(rhs_call.decl_id);
1224
1225                    if cmd.is_keyword()
1226                        && !ALIASABLE_PARSER_KEYWORDS.contains(&cmd.name().as_bytes())
1227                    {
1228                        working_set.error(ParseError::CantAliasKeyword(
1229                            ALIASABLE_PARSER_KEYWORDS
1230                                .iter()
1231                                .map(|bytes| String::from_utf8_lossy(bytes).to_string())
1232                                .collect::<Vec<String>>()
1233                                .join(", "),
1234                            rhs_call.head,
1235                        ));
1236                        return alias_pipeline;
1237                    }
1238
1239                    (Some(cmd.clone_box()), expr)
1240                }
1241                Expression {
1242                    expr: Expr::ExternalCall(..),
1243                    ..
1244                } => (None, expr),
1245                _ => {
1246                    working_set.error(ParseError::InternalError(
1247                        "Parsed call not a call".into(),
1248                        expr.span,
1249                    ));
1250                    return alias_pipeline;
1251                }
1252            };
1253
1254            // Tries to build a useful description string
1255            let (description, extra_description) = match lite_command.comments.is_empty() {
1256                // First from comments, if any are present
1257                false => working_set.build_desc(&lite_command.comments),
1258                // Then from the command itself
1259                true => match alias_call.arguments.get(1) {
1260                    Some(Argument::Positional(Expression {
1261                        expr: Expr::Keyword(kw),
1262                        ..
1263                    })) => {
1264                        let aliased = working_set.get_span_contents(kw.expr.span);
1265                        (
1266                            format!("Alias for `{}`", String::from_utf8_lossy(aliased)),
1267                            String::new(),
1268                        )
1269                    }
1270                    // Then with a default.
1271                    _ => ("User declared alias".into(), String::new()),
1272                },
1273            };
1274
1275            let decl = Alias {
1276                name: alias_name,
1277                command,
1278                wrapped_call,
1279                description,
1280                extra_description,
1281            };
1282
1283            working_set.add_decl(Box::new(decl));
1284        }
1285
1286        // special case for `alias foo=bar`
1287        if spans.len() == 2 && working_set.get_span_contents(spans[1]).contains(&b'=') {
1288            let arg = String::from_utf8_lossy(working_set.get_span_contents(spans[1]));
1289
1290            // split at '='.  Note that the output must never be None, the
1291            // `unwrap` is just to avoid the possibility of panic, if the
1292            // invariant is broken.
1293            let (name, initial_value) = arg.split_once('=').unwrap_or((&arg, ""));
1294
1295            let name = if name.is_empty() { "{name}" } else { name };
1296            let initial_value = if initial_value.is_empty() {
1297                "{initial_value}"
1298            } else {
1299                initial_value
1300            };
1301
1302            working_set.error(ParseError::IncorrectValue(
1303                "alias argument".into(),
1304                spans[1],
1305                format!("Make sure to put spaces around '=': alias {name} = {initial_value}"),
1306            ))
1307        } else if spans.len() < 4 {
1308            working_set.error(ParseError::IncorrectValue(
1309                "Incomplete alias".into(),
1310                Span::concat(&spans[..split_id]),
1311                "incomplete alias".into(),
1312            ));
1313        }
1314
1315        return alias_pipeline;
1316    }
1317
1318    working_set.error(ParseError::InternalError(
1319        "Alias statement unparsable".into(),
1320        Span::concat(spans),
1321    ));
1322
1323    garbage_pipeline(working_set, spans)
1324}
1325
1326// This one will trigger if `export` appears during eval, e.g., in a script
1327pub fn parse_export_in_block(
1328    working_set: &mut StateWorkingSet,
1329    lite_command: &LiteCommand,
1330) -> Pipeline {
1331    let call_span = Span::concat(&lite_command.parts);
1332
1333    let parts = lite_command.command_parts();
1334    let full_name = if parts.len() > 1 {
1335        let sub = working_set.get_span_contents(parts[1]);
1336        match sub {
1337            b"alias" => "export alias",
1338            b"def" => "export def",
1339            b"extern" => "export extern",
1340            b"use" => "export use",
1341            b"module" => "export module",
1342            b"const" => "export const",
1343            _ => "export",
1344        }
1345    } else {
1346        "export"
1347    };
1348
1349    if let Some(redirection) = lite_command.redirection.as_ref() {
1350        working_set.error(redirecting_builtin_error(full_name, redirection));
1351        return garbage_pipeline(working_set, &lite_command.parts);
1352    }
1353
1354    // No need to care for this when attributes are present, parse_attribute_block will throw the
1355    // necessary error
1356    if !lite_command.has_attributes() {
1357        if let Some(decl_id) = working_set.find_decl(full_name.as_bytes()) {
1358            let starting_error_count = working_set.parse_errors.len();
1359            let ParsedInternalCall { call, output, .. } = parse_internal_call(
1360                working_set,
1361                if full_name == "export" {
1362                    parts[0]
1363                } else {
1364                    Span::concat(&parts[0..2])
1365                },
1366                if full_name == "export" {
1367                    &parts[1..]
1368                } else {
1369                    &parts[2..]
1370                },
1371                decl_id,
1372            );
1373            // don't need errors generated by parse_internal_call
1374            // further error will be generated by detail `parse_xxx` function.
1375            working_set.parse_errors.truncate(starting_error_count);
1376
1377            let decl = working_set.get_decl(decl_id);
1378            check_call(working_set, call_span, &decl.signature(), &call);
1379            let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
1380                return garbage_pipeline(working_set, &lite_command.parts);
1381            };
1382
1383            if starting_error_count != working_set.parse_errors.len() || is_help {
1384                return Pipeline::from_vec(vec![Expression::new(
1385                    working_set,
1386                    Expr::Call(call),
1387                    call_span,
1388                    output,
1389                )]);
1390            }
1391        } else {
1392            working_set.error(ParseError::UnknownState(
1393                format!("internal error: '{full_name}' declaration not found",),
1394                Span::concat(&lite_command.parts),
1395            ));
1396            return garbage_pipeline(working_set, &lite_command.parts);
1397        };
1398    }
1399
1400    match full_name {
1401        // `parse_def` and `parse_extern` work both with and without attributes
1402        "export def" => parse_def(working_set, lite_command, None).0,
1403        "export extern" => parse_extern(working_set, lite_command, None),
1404        // Other definitions can't have attributes, so we handle attributes here with parse_attribute_block
1405        _ if lite_command.has_attributes() => parse_attribute_block(working_set, lite_command),
1406        "export alias" => parse_alias(working_set, lite_command, None),
1407        "export const" => parse_const(working_set, &lite_command.parts[1..]).0,
1408        "export use" => parse_use(working_set, lite_command, None).0,
1409        "export module" => parse_module(working_set, lite_command, None).0,
1410        _ => {
1411            working_set.error(ParseError::UnexpectedKeyword(
1412                full_name.into(),
1413                lite_command.parts[0],
1414            ));
1415
1416            garbage_pipeline(working_set, &lite_command.parts)
1417        }
1418    }
1419}
1420
1421// This one will trigger only in a module
1422pub fn parse_export_in_module(
1423    working_set: &mut StateWorkingSet,
1424    lite_command: &LiteCommand,
1425    module_name: &[u8],
1426    parent_module: &mut Module,
1427) -> (Pipeline, Vec<Exportable>) {
1428    let spans = lite_command.command_parts();
1429
1430    let export_span = if let Some(sp) = spans.first() {
1431        if working_set.get_span_contents(*sp) != b"export" {
1432            working_set.error(ParseError::UnknownState(
1433                "expected export statement".into(),
1434                Span::concat(spans),
1435            ));
1436            return (garbage_pipeline(working_set, spans), vec![]);
1437        }
1438
1439        *sp
1440    } else {
1441        working_set.error(ParseError::UnknownState(
1442            "got empty input for parsing export statement".into(),
1443            Span::concat(spans),
1444        ));
1445        return (garbage_pipeline(working_set, spans), vec![]);
1446    };
1447
1448    let export_decl_id = if let Some(id) = working_set.find_decl(b"export") {
1449        id
1450    } else {
1451        working_set.error(ParseError::InternalError(
1452            "missing export command".into(),
1453            export_span,
1454        ));
1455        return (garbage_pipeline(working_set, spans), vec![]);
1456    };
1457
1458    let mut call = Box::new(Call {
1459        head: spans[0],
1460        decl_id: export_decl_id,
1461        arguments: vec![],
1462        parser_info: HashMap::new(),
1463    });
1464
1465    let mut out_pipeline = None;
1466
1467    let exportables = if let Some(kw_span) = spans.get(1) {
1468        let kw_name = working_set.get_span_contents(*kw_span);
1469        match kw_name {
1470            // `parse_def` and `parse_extern` work both with and without attributes
1471            b"def" => {
1472                let (mut pipeline, cmd_result) =
1473                    parse_def(working_set, lite_command, Some(module_name));
1474
1475                let mut result = vec![];
1476
1477                if let Some((decl_name, decl_id)) = cmd_result {
1478                    result.push(Exportable::Decl {
1479                        name: decl_name.to_vec(),
1480                        id: decl_id,
1481                    });
1482                }
1483
1484                let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export def") {
1485                    id
1486                } else {
1487                    working_set.error(ParseError::InternalError(
1488                        "missing 'export def' command".into(),
1489                        export_span,
1490                    ));
1491                    return (garbage_pipeline(working_set, spans), vec![]);
1492                };
1493
1494                // Trying to warp the 'def' call into the 'export def' in a very clumsy way
1495                // TODO: Rather than this, handle `export def` correctly in `parse_def`
1496                'warp: {
1497                    match pipeline.elements.first_mut().map(|e| &mut e.expr.expr) {
1498                        Some(Expr::Call(def_call)) => {
1499                            def_call.head = Span::concat(&spans[0..=1]);
1500                            def_call.decl_id = export_def_decl_id;
1501                            break 'warp;
1502                        }
1503                        Some(Expr::AttributeBlock(ab)) => {
1504                            if let Expr::Call(def_call) = &mut ab.item.expr {
1505                                def_call.head = Span::concat(&spans[0..=1]);
1506                                def_call.decl_id = export_def_decl_id;
1507                                break 'warp;
1508                            }
1509                        }
1510                        _ => {}
1511                    };
1512
1513                    working_set.error(ParseError::InternalError(
1514                        "unexpected output from parsing a definition".into(),
1515                        Span::concat(&spans[1..]),
1516                    ));
1517                }
1518
1519                out_pipeline = Some(pipeline);
1520                result
1521            }
1522            b"extern" => {
1523                let extern_name = [b"export ", kw_name].concat();
1524
1525                let mut pipeline = parse_extern(working_set, lite_command, Some(module_name));
1526
1527                let export_def_decl_id = if let Some(id) = working_set.find_decl(&extern_name) {
1528                    id
1529                } else {
1530                    working_set.error(ParseError::InternalError(
1531                        "missing 'export extern' command".into(),
1532                        export_span,
1533                    ));
1534                    return (garbage_pipeline(working_set, spans), vec![]);
1535                };
1536
1537                // Trying to warp the 'extern' call into the 'export extern' in a very clumsy way
1538                // TODO: Rather than this, handle `export extern` correctly in `parse_extern`
1539                'warp: {
1540                    match pipeline.elements.first_mut().map(|e| &mut e.expr.expr) {
1541                        Some(Expr::Call(def_call)) => {
1542                            def_call.head = Span::concat(&spans[0..=1]);
1543                            def_call.decl_id = export_def_decl_id;
1544                            break 'warp;
1545                        }
1546                        Some(Expr::AttributeBlock(ab)) => {
1547                            if let Expr::Call(def_call) = &mut ab.item.expr {
1548                                def_call.head = Span::concat(&spans[0..=1]);
1549                                def_call.decl_id = export_def_decl_id;
1550                                break 'warp;
1551                            }
1552                        }
1553                        _ => {}
1554                    };
1555
1556                    working_set.error(ParseError::InternalError(
1557                        "unexpected output from parsing a definition".into(),
1558                        Span::concat(&spans[1..]),
1559                    ));
1560                }
1561
1562                let mut result = vec![];
1563
1564                let decl_name = match spans.get(2) {
1565                    Some(span) => working_set.get_span_contents(*span),
1566                    None => &[],
1567                };
1568                let decl_name = trim_quotes(decl_name);
1569
1570                if let Some(decl_id) = working_set.find_decl(decl_name) {
1571                    result.push(Exportable::Decl {
1572                        name: decl_name.to_vec(),
1573                        id: decl_id,
1574                    });
1575                } else {
1576                    working_set.error(ParseError::InternalError(
1577                        "failed to find added declaration".into(),
1578                        Span::concat(&spans[1..]),
1579                    ));
1580                }
1581
1582                out_pipeline = Some(pipeline);
1583                result
1584            }
1585            // Other definitions can't have attributes, so we handle attributes here with parse_attribute_block
1586            _ if lite_command.has_attributes() => {
1587                out_pipeline = Some(parse_attribute_block(working_set, lite_command));
1588                vec![]
1589            }
1590            b"alias" => {
1591                let lite_command = LiteCommand {
1592                    comments: lite_command.comments.clone(),
1593                    parts: spans[1..].to_vec(),
1594                    pipe: lite_command.pipe,
1595                    redirection: lite_command.redirection.clone(),
1596                    attribute_idx: vec![],
1597                };
1598                let pipeline = parse_alias(working_set, &lite_command, Some(module_name));
1599
1600                let export_alias_decl_id = if let Some(id) = working_set.find_decl(b"export alias")
1601                {
1602                    id
1603                } else {
1604                    working_set.error(ParseError::InternalError(
1605                        "missing 'export alias' command".into(),
1606                        export_span,
1607                    ));
1608                    return (garbage_pipeline(working_set, spans), vec![]);
1609                };
1610
1611                // Trying to warp the 'alias' call into the 'export alias' in a very clumsy way
1612                if let Some(Expr::Call(alias_call)) =
1613                    pipeline.elements.first().map(|e| &e.expr.expr)
1614                {
1615                    call.clone_from(alias_call);
1616
1617                    call.head = Span::concat(&spans[0..=1]);
1618                    call.decl_id = export_alias_decl_id;
1619                } else {
1620                    working_set.error(ParseError::InternalError(
1621                        "unexpected output from parsing a definition".into(),
1622                        Span::concat(&spans[1..]),
1623                    ));
1624                };
1625
1626                let mut result = vec![];
1627
1628                let alias_name = match spans.get(2) {
1629                    Some(span) => working_set.get_span_contents(*span),
1630                    None => &[],
1631                };
1632                let alias_name = trim_quotes(alias_name);
1633
1634                if let Some(alias_id) = working_set.find_decl(alias_name) {
1635                    result.push(Exportable::Decl {
1636                        name: alias_name.to_vec(),
1637                        id: alias_id,
1638                    });
1639                } else {
1640                    working_set.error(ParseError::InternalError(
1641                        "failed to find added alias".into(),
1642                        Span::concat(&spans[1..]),
1643                    ));
1644                }
1645
1646                result
1647            }
1648            b"use" => {
1649                let lite_command = LiteCommand {
1650                    comments: lite_command.comments.clone(),
1651                    parts: spans[1..].to_vec(),
1652                    pipe: lite_command.pipe,
1653                    redirection: lite_command.redirection.clone(),
1654                    attribute_idx: vec![],
1655                };
1656                let (pipeline, exportables) =
1657                    parse_use(working_set, &lite_command, Some(parent_module));
1658
1659                let export_use_decl_id = if let Some(id) = working_set.find_decl(b"export use") {
1660                    id
1661                } else {
1662                    working_set.error(ParseError::InternalError(
1663                        "missing 'export use' command".into(),
1664                        export_span,
1665                    ));
1666                    return (garbage_pipeline(working_set, spans), vec![]);
1667                };
1668
1669                // Trying to warp the 'use' call into the 'export use' in a very clumsy way
1670                if let Some(Expr::Call(use_call)) = pipeline.elements.first().map(|e| &e.expr.expr)
1671                {
1672                    call.clone_from(use_call);
1673
1674                    call.head = Span::concat(&spans[0..=1]);
1675                    call.decl_id = export_use_decl_id;
1676                } else {
1677                    working_set.error(ParseError::InternalError(
1678                        "unexpected output from parsing a definition".into(),
1679                        Span::concat(&spans[1..]),
1680                    ));
1681                };
1682
1683                exportables
1684            }
1685            b"module" => {
1686                let (pipeline, maybe_module_id) =
1687                    parse_module(working_set, lite_command, Some(module_name));
1688
1689                let export_module_decl_id =
1690                    if let Some(id) = working_set.find_decl(b"export module") {
1691                        id
1692                    } else {
1693                        working_set.error(ParseError::InternalError(
1694                            "missing 'export module' command".into(),
1695                            export_span,
1696                        ));
1697                        return (garbage_pipeline(working_set, spans), vec![]);
1698                    };
1699
1700                // Trying to warp the 'module' call into the 'export module' in a very clumsy way
1701                if let Some(Expr::Call(module_call)) =
1702                    pipeline.elements.first().map(|e| &e.expr.expr)
1703                {
1704                    call.clone_from(module_call);
1705
1706                    call.head = Span::concat(&spans[0..=1]);
1707                    call.decl_id = export_module_decl_id;
1708                } else {
1709                    working_set.error(ParseError::InternalError(
1710                        "unexpected output from parsing a definition".into(),
1711                        Span::concat(&spans[1..]),
1712                    ));
1713                };
1714
1715                let mut result = vec![];
1716
1717                if let Some(module_name_span) = spans.get(2) {
1718                    let module_name = working_set.get_span_contents(*module_name_span);
1719                    let module_name = trim_quotes(module_name);
1720
1721                    if let Some(module_id) = maybe_module_id {
1722                        result.push(Exportable::Module {
1723                            name: working_set.get_module(module_id).name(),
1724                            id: module_id,
1725                        });
1726                    } else {
1727                        working_set.error(ParseError::InternalError(
1728                            format!(
1729                                "failed to find added module '{}'",
1730                                String::from_utf8_lossy(module_name)
1731                            ),
1732                            Span::concat(&spans[1..]),
1733                        ));
1734                    }
1735                }
1736
1737                result
1738            }
1739            b"const" => {
1740                let (pipeline, var_name_span) = parse_const(working_set, &spans[1..]);
1741                let export_const_decl_id = if let Some(id) = working_set.find_decl(b"export const")
1742                {
1743                    id
1744                } else {
1745                    working_set.error(ParseError::InternalError(
1746                        "missing 'export const' command".into(),
1747                        export_span,
1748                    ));
1749                    return (garbage_pipeline(working_set, spans), vec![]);
1750                };
1751
1752                // Trying to warp the 'const' call into the 'export const' in a very clumsy way
1753                if let Some(Expr::Call(def_call)) = pipeline.elements.first().map(|e| &e.expr.expr)
1754                {
1755                    call.clone_from(def_call);
1756
1757                    call.head = Span::concat(&spans[0..=1]);
1758                    call.decl_id = export_const_decl_id;
1759                } else {
1760                    working_set.error(ParseError::InternalError(
1761                        "unexpected output from parsing a definition".into(),
1762                        Span::concat(&spans[1..]),
1763                    ));
1764                };
1765
1766                let mut result = vec![];
1767
1768                if let Some(var_name_span) = var_name_span {
1769                    let var_name = working_set.get_span_contents(var_name_span);
1770                    let var_name = trim_quotes(var_name);
1771
1772                    if let Some(var_id) = working_set.find_variable(var_name) {
1773                        if let Err(err) = working_set.get_constant(var_id) {
1774                            working_set.error(err);
1775                        } else {
1776                            result.push(Exportable::VarDecl {
1777                                name: var_name.to_vec(),
1778                                id: var_id,
1779                            });
1780                        }
1781                    } else {
1782                        working_set.error(ParseError::InternalError(
1783                            "failed to find added variable".into(),
1784                            Span::concat(&spans[1..]),
1785                        ));
1786                    }
1787                }
1788
1789                result
1790            }
1791            _ => {
1792                working_set.error(ParseError::Expected(
1793                    "def, alias, use, module, const or extern keyword",
1794                    spans[1],
1795                ));
1796
1797                vec![]
1798            }
1799        }
1800    } else {
1801        working_set.error(ParseError::MissingPositional(
1802            "def, alias, use, module, const or extern keyword".to_string(),
1803            Span::new(export_span.end, export_span.end),
1804            "def, alias, use, module, const or extern keyword".to_string(),
1805        ));
1806
1807        vec![]
1808    };
1809
1810    let out_pipeline = out_pipeline.unwrap_or(Pipeline::from_vec(vec![Expression::new(
1811        working_set,
1812        Expr::Call(call),
1813        Span::concat(spans),
1814        Type::Any,
1815    )]));
1816    (out_pipeline, exportables)
1817}
1818
1819pub fn parse_export_env(
1820    working_set: &mut StateWorkingSet,
1821    spans: &[Span],
1822) -> (Pipeline, Option<BlockId>) {
1823    if !spans.is_empty() && working_set.get_span_contents(spans[0]) != b"export-env" {
1824        working_set.error(ParseError::UnknownState(
1825            "internal error: Wrong call name for 'export-env' command".into(),
1826            Span::concat(spans),
1827        ));
1828        return (garbage_pipeline(working_set, spans), None);
1829    }
1830
1831    if spans.len() < 2 {
1832        working_set.error(ParseError::MissingPositional(
1833            "block".into(),
1834            Span::concat(spans),
1835            "export-env <block>".into(),
1836        ));
1837        return (garbage_pipeline(working_set, spans), None);
1838    }
1839
1840    let call = match working_set.find_decl(b"export-env") {
1841        Some(decl_id) => {
1842            let ParsedInternalCall { call, output } =
1843                parse_internal_call(working_set, spans[0], &[spans[1]], decl_id);
1844            let decl = working_set.get_decl(decl_id);
1845
1846            let call_span = Span::concat(spans);
1847
1848            let starting_error_count = working_set.parse_errors.len();
1849            check_call(working_set, call_span, &decl.signature(), &call);
1850
1851            let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
1852                return (garbage_pipeline(working_set, spans), None);
1853            };
1854
1855            if starting_error_count != working_set.parse_errors.len() || is_help {
1856                return (
1857                    Pipeline::from_vec(vec![Expression::new(
1858                        working_set,
1859                        Expr::Call(call),
1860                        call_span,
1861                        output,
1862                    )]),
1863                    None,
1864                );
1865            }
1866
1867            call
1868        }
1869        None => {
1870            working_set.error(ParseError::UnknownState(
1871                "internal error: 'export-env' declaration not found".into(),
1872                Span::concat(spans),
1873            ));
1874            return (garbage_pipeline(working_set, spans), None);
1875        }
1876    };
1877
1878    let block_id = if let Some(block) = call.positional_nth(0) {
1879        if let Some(block_id) = block.as_block() {
1880            block_id
1881        } else {
1882            working_set.error(ParseError::UnknownState(
1883                "internal error: 'export-env' block is not a block".into(),
1884                block.span,
1885            ));
1886            return (garbage_pipeline(working_set, spans), None);
1887        }
1888    } else {
1889        working_set.error(ParseError::UnknownState(
1890            "internal error: 'export-env' block is missing".into(),
1891            Span::concat(spans),
1892        ));
1893        return (garbage_pipeline(working_set, spans), None);
1894    };
1895
1896    let pipeline = Pipeline::from_vec(vec![Expression::new(
1897        working_set,
1898        Expr::Call(call),
1899        Span::concat(spans),
1900        Type::Any,
1901    )]);
1902
1903    (pipeline, Some(block_id))
1904}
1905
1906fn collect_first_comments(tokens: &[Token]) -> Vec<Span> {
1907    let mut comments = vec![];
1908
1909    let mut tokens_iter = tokens.iter().peekable();
1910    while let Some(token) = tokens_iter.next() {
1911        match token.contents {
1912            TokenContents::Comment => {
1913                comments.push(token.span);
1914            }
1915            TokenContents::Eol => {
1916                if let Some(Token {
1917                    contents: TokenContents::Eol,
1918                    ..
1919                }) = tokens_iter.peek()
1920                {
1921                    if !comments.is_empty() {
1922                        break;
1923                    }
1924                }
1925            }
1926            _ => {
1927                comments.clear();
1928                break;
1929            }
1930        }
1931    }
1932
1933    comments
1934}
1935
1936pub fn parse_module_block(
1937    working_set: &mut StateWorkingSet,
1938    span: Span,
1939    module_name: &[u8],
1940) -> (Block, Module, Vec<Span>) {
1941    working_set.enter_scope();
1942
1943    let source = working_set.get_span_contents(span);
1944
1945    let (output, err) = lex(source, span.start, &[], &[], false);
1946    if let Some(err) = err {
1947        working_set.error(err)
1948    }
1949
1950    let module_comments = collect_first_comments(&output);
1951
1952    let (output, err) = lite_parse(&output, working_set);
1953    if let Some(err) = err {
1954        working_set.error(err)
1955    }
1956
1957    for pipeline in &output.block {
1958        if pipeline.commands.len() == 1 {
1959            parse_def_predecl(working_set, pipeline.commands[0].command_parts());
1960        }
1961    }
1962
1963    let mut module = Module::from_span(module_name.to_vec(), span);
1964
1965    let mut block = Block::new_with_capacity(output.block.len());
1966    block.span = Some(span);
1967
1968    for pipeline in output.block.iter() {
1969        if pipeline.commands.len() == 1 {
1970            let command = &pipeline.commands[0];
1971
1972            let name = command
1973                .command_parts()
1974                .first()
1975                .map(|s| working_set.get_span_contents(*s))
1976                .unwrap_or(b"");
1977
1978            match name {
1979                // `parse_def` and `parse_extern` work both with and without attributes
1980                b"def" => {
1981                    block.pipelines.push(
1982                        parse_def(
1983                            working_set,
1984                            command,
1985                            None, // using commands named as the module locally is OK
1986                        )
1987                        .0,
1988                    )
1989                }
1990                b"extern" => block
1991                    .pipelines
1992                    .push(parse_extern(working_set, command, None)),
1993                // `parse_export_in_module` also handles attributes by itself
1994                b"export" => {
1995                    let (pipe, exportables) =
1996                        parse_export_in_module(working_set, command, module_name, &mut module);
1997
1998                    for exportable in exportables {
1999                        match exportable {
2000                            Exportable::Decl { name, id } => {
2001                                if &name == b"main" {
2002                                    if module.main.is_some() {
2003                                        let err_span = if !pipe.elements.is_empty() {
2004                                            if let Expr::Call(call) = &pipe.elements[0].expr.expr {
2005                                                call.head
2006                                            } else {
2007                                                pipe.elements[0].expr.span
2008                                            }
2009                                        } else {
2010                                            span
2011                                        };
2012                                        working_set.error(ParseError::ModuleDoubleMain(
2013                                            String::from_utf8_lossy(module_name).to_string(),
2014                                            err_span,
2015                                        ));
2016                                    } else {
2017                                        module.main = Some(id);
2018                                    }
2019                                } else {
2020                                    module.add_decl(name, id);
2021                                }
2022                            }
2023                            Exportable::Module { name, id } => {
2024                                if &name == b"mod" {
2025                                    let (submodule_main, submodule_decls, submodule_submodules) = {
2026                                        let submodule = working_set.get_module(id);
2027                                        (submodule.main, submodule.decls(), submodule.submodules())
2028                                    };
2029
2030                                    // Add submodule's decls to the parent module
2031                                    for (decl_name, decl_id) in submodule_decls {
2032                                        module.add_decl(decl_name, decl_id);
2033                                    }
2034
2035                                    // Add submodule's main command to the parent module
2036                                    if let Some(main_decl_id) = submodule_main {
2037                                        if module.main.is_some() {
2038                                            let err_span = if !pipe.elements.is_empty() {
2039                                                if let Expr::Call(call) =
2040                                                    &pipe.elements[0].expr.expr
2041                                                {
2042                                                    call.head
2043                                                } else {
2044                                                    pipe.elements[0].expr.span
2045                                                }
2046                                            } else {
2047                                                span
2048                                            };
2049                                            working_set.error(ParseError::ModuleDoubleMain(
2050                                                String::from_utf8_lossy(module_name).to_string(),
2051                                                err_span,
2052                                            ));
2053                                        } else {
2054                                            module.main = Some(main_decl_id);
2055                                        }
2056                                    }
2057
2058                                    // Add submodule's submodules to the parent module
2059                                    for (submodule_name, submodule_id) in submodule_submodules {
2060                                        module.add_submodule(submodule_name, submodule_id);
2061                                    }
2062                                } else {
2063                                    module.add_submodule(name, id);
2064                                }
2065                            }
2066                            Exportable::VarDecl { name, id } => {
2067                                module.add_variable(name, id);
2068                            }
2069                        }
2070                    }
2071
2072                    block.pipelines.push(pipe)
2073                }
2074                // Other definitions can't have attributes, so we handle attributes here with parse_attribute_block
2075                _ if command.has_attributes() => block
2076                    .pipelines
2077                    .push(parse_attribute_block(working_set, command)),
2078                b"const" => block
2079                    .pipelines
2080                    .push(parse_const(working_set, &command.parts).0),
2081                b"alias" => {
2082                    block.pipelines.push(parse_alias(
2083                        working_set,
2084                        command,
2085                        None, // using aliases named as the module locally is OK
2086                    ))
2087                }
2088                b"use" => {
2089                    let (pipeline, _) = parse_use(working_set, command, Some(&mut module));
2090
2091                    block.pipelines.push(pipeline)
2092                }
2093                b"module" => {
2094                    let (pipeline, _) = parse_module(
2095                        working_set,
2096                        command,
2097                        None, // using modules named as the module locally is OK
2098                    );
2099
2100                    block.pipelines.push(pipeline)
2101                }
2102                b"export-env" => {
2103                    let (pipe, maybe_env_block) = parse_export_env(working_set, &command.parts);
2104
2105                    if let Some(block_id) = maybe_env_block {
2106                        module.add_env_block(block_id);
2107                    }
2108
2109                    block.pipelines.push(pipe)
2110                }
2111                _ => {
2112                    working_set.error(ParseError::ExpectedKeyword(
2113                        "def, const, extern, alias, use, module, export or export-env keyword"
2114                            .into(),
2115                        command.parts[0],
2116                    ));
2117
2118                    block
2119                        .pipelines
2120                        .push(garbage_pipeline(working_set, &command.parts))
2121                }
2122            }
2123        } else {
2124            working_set.error(ParseError::Expected("not a pipeline", span));
2125            block.pipelines.push(garbage_pipeline(working_set, &[span]))
2126        }
2127    }
2128
2129    working_set.exit_scope();
2130
2131    (block, module, module_comments)
2132}
2133
2134fn module_needs_reloading(working_set: &StateWorkingSet, module_id: ModuleId) -> bool {
2135    let module = working_set.get_module(module_id);
2136
2137    fn submodule_need_reloading(working_set: &StateWorkingSet, submodule_id: ModuleId) -> bool {
2138        let submodule = working_set.get_module(submodule_id);
2139        let submodule_changed = if let Some((file_path, file_id)) = &submodule.file {
2140            let existing_contents = working_set.get_contents_of_file(*file_id);
2141            let file_contents = file_path.read(working_set);
2142
2143            if let (Some(existing), Some(new)) = (existing_contents, file_contents) {
2144                existing != new
2145            } else {
2146                false
2147            }
2148        } else {
2149            false
2150        };
2151
2152        if submodule_changed {
2153            true
2154        } else {
2155            module_needs_reloading(working_set, submodule_id)
2156        }
2157    }
2158
2159    let export_submodule_changed = module
2160        .submodules
2161        .iter()
2162        .any(|(_, submodule_id)| submodule_need_reloading(working_set, *submodule_id));
2163
2164    if export_submodule_changed {
2165        return true;
2166    }
2167
2168    let private_submodule_changed = module
2169        .imported_modules
2170        .iter()
2171        .any(|submodule_id| submodule_need_reloading(working_set, *submodule_id));
2172
2173    private_submodule_changed
2174}
2175
2176/// Parse a module from a file.
2177///
2178/// The module name is inferred from the stem of the file, unless specified in `name_override`.
2179fn parse_module_file(
2180    working_set: &mut StateWorkingSet,
2181    path: ParserPath,
2182    path_span: Span,
2183    name_override: Option<String>,
2184) -> Option<ModuleId> {
2185    // Infer the module name from the stem of the file, unless overridden.
2186    let module_name = if let Some(name) = name_override {
2187        name
2188    } else if let Some(stem) = path.file_stem() {
2189        stem.to_string_lossy().to_string()
2190    } else {
2191        working_set.error(ParseError::ModuleNotFound(
2192            path_span,
2193            path.path().to_string_lossy().to_string(),
2194        ));
2195        return None;
2196    };
2197
2198    // Read the content of the module.
2199    let contents = if let Some(contents) = path.read(working_set) {
2200        contents
2201    } else {
2202        working_set.error(ParseError::ModuleNotFound(
2203            path_span,
2204            path.path().to_string_lossy().to_string(),
2205        ));
2206        return None;
2207    };
2208
2209    let file_id = working_set.add_file(path.path().to_string_lossy().to_string(), &contents);
2210    let new_span = working_set.get_span_for_file(file_id);
2211
2212    // Check if we've parsed the module before.
2213    if let Some(module_id) = working_set.find_module_by_span(new_span) {
2214        if !module_needs_reloading(working_set, module_id) {
2215            return Some(module_id);
2216        }
2217    }
2218
2219    // Add the file to the stack of files being processed.
2220    if let Err(e) = working_set.files.push(path.clone().path_buf(), path_span) {
2221        working_set.error(e);
2222        return None;
2223    }
2224
2225    // Parse the module
2226    let (block, mut module, module_comments) =
2227        parse_module_block(working_set, new_span, module_name.as_bytes());
2228
2229    // Remove the file from the stack of files being processed.
2230    working_set.files.pop();
2231
2232    let _ = working_set.add_block(Arc::new(block));
2233    module.file = Some((path, file_id));
2234    let module_id = working_set.add_module(&module_name, module, module_comments);
2235
2236    Some(module_id)
2237}
2238
2239pub fn parse_module_file_or_dir(
2240    working_set: &mut StateWorkingSet,
2241    path: &[u8],
2242    path_span: Span,
2243    name_override: Option<String>,
2244) -> Option<ModuleId> {
2245    let (module_path_str, err) = unescape_unquote_string(path, path_span);
2246    if let Some(err) = err {
2247        working_set.error(err);
2248        return None;
2249    }
2250
2251    #[allow(deprecated)]
2252    let cwd = working_set.get_cwd();
2253
2254    let module_path =
2255        if let Some(path) = find_in_dirs(&module_path_str, working_set, &cwd, Some(LIB_DIRS_VAR)) {
2256            path
2257        } else {
2258            working_set.error(ParseError::ModuleNotFound(path_span, module_path_str));
2259            return None;
2260        };
2261
2262    if module_path.is_dir() {
2263        if module_path.read_dir().is_none() {
2264            working_set.error(ParseError::ModuleNotFound(
2265                path_span,
2266                module_path.path().to_string_lossy().to_string(),
2267            ));
2268            return None;
2269        };
2270
2271        let module_name = if let Some(stem) = module_path.file_stem() {
2272            stem.to_string_lossy().to_string()
2273        } else {
2274            working_set.error(ParseError::ModuleNotFound(
2275                path_span,
2276                module_path.path().to_string_lossy().to_string(),
2277            ));
2278            return None;
2279        };
2280
2281        let mod_nu_path = module_path.clone().join("mod.nu");
2282
2283        if !(mod_nu_path.exists() && mod_nu_path.is_file()) {
2284            working_set.error(ParseError::ModuleMissingModNuFile(
2285                module_path.path().to_string_lossy().to_string(),
2286                path_span,
2287            ));
2288            return None;
2289        }
2290
2291        if let Some(module_id) = parse_module_file(
2292            working_set,
2293            mod_nu_path,
2294            path_span,
2295            name_override.or(Some(module_name)),
2296        ) {
2297            let module = working_set.get_module(module_id).clone();
2298
2299            let module_name = String::from_utf8_lossy(&module.name).to_string();
2300
2301            let module_comments = if let Some(comments) = working_set.get_module_comments(module_id)
2302            {
2303                comments.to_vec()
2304            } else {
2305                vec![]
2306            };
2307
2308            let new_module_id = working_set.add_module(&module_name, module, module_comments);
2309
2310            Some(new_module_id)
2311        } else {
2312            None
2313        }
2314    } else if module_path.is_file() {
2315        parse_module_file(working_set, module_path, path_span, name_override)
2316    } else {
2317        working_set.error(ParseError::ModuleNotFound(
2318            path_span,
2319            module_path.path().to_string_lossy().to_string(),
2320        ));
2321        None
2322    }
2323}
2324
2325pub fn parse_module(
2326    working_set: &mut StateWorkingSet,
2327    lite_command: &LiteCommand,
2328    module_name: Option<&[u8]>,
2329) -> (Pipeline, Option<ModuleId>) {
2330    // TODO: Currently, module is closing over its parent scope (i.e., defs in the parent scope are
2331    // visible and usable in this module's scope). We want to disable that for files.
2332
2333    let spans = &lite_command.parts;
2334
2335    if let Some(redirection) = lite_command.redirection.as_ref() {
2336        working_set.error(redirecting_builtin_error("module", redirection));
2337        return (garbage_pipeline(working_set, spans), None);
2338    }
2339
2340    let mut module_comments = lite_command.comments.clone();
2341
2342    let split_id = if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
2343        2
2344    } else {
2345        1
2346    };
2347
2348    let (call, call_span) = match working_set.find_decl(b"module") {
2349        Some(decl_id) => {
2350            let (command_spans, rest_spans) = spans.split_at(split_id);
2351
2352            let ParsedInternalCall { call, output } = parse_internal_call(
2353                working_set,
2354                Span::concat(command_spans),
2355                rest_spans,
2356                decl_id,
2357            );
2358            let decl = working_set.get_decl(decl_id);
2359
2360            let call_span = Span::concat(spans);
2361
2362            let starting_error_count = working_set.parse_errors.len();
2363            check_call(working_set, call_span, &decl.signature(), &call);
2364
2365            let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
2366                return (garbage_pipeline(working_set, spans), None);
2367            };
2368
2369            if starting_error_count != working_set.parse_errors.len() || is_help {
2370                return (
2371                    Pipeline::from_vec(vec![Expression::new(
2372                        working_set,
2373                        Expr::Call(call),
2374                        call_span,
2375                        output,
2376                    )]),
2377                    None,
2378                );
2379            }
2380
2381            (call, call_span)
2382        }
2383        None => {
2384            working_set.error(ParseError::UnknownState(
2385                "internal error: 'module' or 'export module' declaration not found".into(),
2386                Span::concat(spans),
2387            ));
2388            return (garbage_pipeline(working_set, spans), None);
2389        }
2390    };
2391
2392    let (module_name_or_path, module_name_or_path_span, module_name_or_path_expr) =
2393        if let Some(name) = call.positional_nth(0) {
2394            if let Some(s) = name.as_string() {
2395                if let Some(mod_name) = module_name {
2396                    if s.as_bytes() == mod_name {
2397                        working_set.error(ParseError::NamedAsModule(
2398                            "module".to_string(),
2399                            s,
2400                            "mod".to_string(),
2401                            name.span,
2402                        ));
2403                        return (
2404                            Pipeline::from_vec(vec![Expression::new(
2405                                working_set,
2406                                Expr::Call(call),
2407                                call_span,
2408                                Type::Any,
2409                            )]),
2410                            None,
2411                        );
2412                    }
2413                }
2414                (s, name.span, name.clone())
2415            } else {
2416                working_set.error(ParseError::UnknownState(
2417                    "internal error: name not a string".into(),
2418                    Span::concat(spans),
2419                ));
2420                return (garbage_pipeline(working_set, spans), None);
2421            }
2422        } else {
2423            working_set.error(ParseError::UnknownState(
2424                "internal error: missing positional".into(),
2425                Span::concat(spans),
2426            ));
2427            return (garbage_pipeline(working_set, spans), None);
2428        };
2429
2430    let pipeline = Pipeline::from_vec(vec![Expression::new(
2431        working_set,
2432        Expr::Call(call),
2433        call_span,
2434        Type::Any,
2435    )]);
2436
2437    if spans.len() == split_id + 1 {
2438        if let Some(module_id) = parse_module_file_or_dir(
2439            working_set,
2440            module_name_or_path.as_bytes(),
2441            module_name_or_path_span,
2442            None,
2443        ) {
2444            return (pipeline, Some(module_id));
2445        } else {
2446            working_set.error(ParseError::ModuleNotFound(
2447                module_name_or_path_span,
2448                module_name_or_path,
2449            ));
2450            return (pipeline, None);
2451        }
2452    }
2453
2454    if spans.len() < split_id + 2 {
2455        working_set.error(ParseError::UnknownState(
2456            "Expected structure: module <name> or module <name> <block>".into(),
2457            Span::concat(spans),
2458        ));
2459
2460        return (garbage_pipeline(working_set, spans), None);
2461    }
2462
2463    let module_name = module_name_or_path;
2464
2465    let block_expr_span = spans[split_id + 1];
2466    let block_bytes = working_set.get_span_contents(block_expr_span);
2467    let mut start = block_expr_span.start;
2468    let mut end = block_expr_span.end;
2469
2470    if block_bytes.starts_with(b"{") {
2471        start += 1;
2472    } else {
2473        working_set.error(ParseError::Expected("block", block_expr_span));
2474        return (garbage_pipeline(working_set, spans), None);
2475    }
2476
2477    if block_bytes.ends_with(b"}") {
2478        end -= 1;
2479    } else {
2480        working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end)));
2481    }
2482
2483    let block_content_span = Span::new(start, end);
2484
2485    let (block, module, inner_comments) =
2486        parse_module_block(working_set, block_content_span, module_name.as_bytes());
2487
2488    let block_id = working_set.add_block(Arc::new(block));
2489
2490    module_comments.extend(inner_comments);
2491    let module_id = working_set.add_module(&module_name, module, module_comments);
2492
2493    let block_expr = Expression::new(
2494        working_set,
2495        Expr::Block(block_id),
2496        block_expr_span,
2497        Type::Any,
2498    );
2499
2500    let module_decl_id = working_set
2501        .find_decl(b"module")
2502        .expect("internal error: missing module command");
2503
2504    let call = Box::new(Call {
2505        head: Span::concat(&spans[..split_id]),
2506        decl_id: module_decl_id,
2507        arguments: vec![
2508            Argument::Positional(module_name_or_path_expr),
2509            Argument::Positional(block_expr),
2510        ],
2511        parser_info: HashMap::new(),
2512    });
2513
2514    (
2515        Pipeline::from_vec(vec![Expression::new(
2516            working_set,
2517            Expr::Call(call),
2518            Span::concat(spans),
2519            Type::Any,
2520        )]),
2521        Some(module_id),
2522    )
2523}
2524
2525pub fn parse_use(
2526    working_set: &mut StateWorkingSet,
2527    lite_command: &LiteCommand,
2528    parent_module: Option<&mut Module>,
2529) -> (Pipeline, Vec<Exportable>) {
2530    let spans = &lite_command.parts;
2531
2532    let (name_span, split_id) =
2533        if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
2534            (spans[1], 2)
2535        } else {
2536            (spans[0], 1)
2537        };
2538
2539    let use_call = working_set.get_span_contents(name_span).to_vec();
2540    if use_call != b"use" {
2541        working_set.error(ParseError::UnknownState(
2542            "internal error: Wrong call name for 'use' command".into(),
2543            Span::concat(spans),
2544        ));
2545        return (garbage_pipeline(working_set, spans), vec![]);
2546    }
2547
2548    if working_set.get_span_contents(name_span) != b"use" {
2549        working_set.error(ParseError::UnknownState(
2550            "internal error: Wrong call name for 'use' command".into(),
2551            Span::concat(spans),
2552        ));
2553        return (garbage_pipeline(working_set, spans), vec![]);
2554    }
2555
2556    if let Some(redirection) = lite_command.redirection.as_ref() {
2557        working_set.error(redirecting_builtin_error("use", redirection));
2558        return (garbage_pipeline(working_set, spans), vec![]);
2559    }
2560
2561    let (call, call_span, args_spans) = match working_set.find_decl(b"use") {
2562        Some(decl_id) => {
2563            let (command_spans, rest_spans) = spans.split_at(split_id);
2564
2565            let ParsedInternalCall { call, output } = parse_internal_call(
2566                working_set,
2567                Span::concat(command_spans),
2568                rest_spans,
2569                decl_id,
2570            );
2571            let decl = working_set.get_decl(decl_id);
2572
2573            let call_span = Span::concat(spans);
2574
2575            let starting_error_count = working_set.parse_errors.len();
2576            check_call(working_set, call_span, &decl.signature(), &call);
2577
2578            let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
2579                return (garbage_pipeline(working_set, spans), vec![]);
2580            };
2581
2582            if starting_error_count != working_set.parse_errors.len() || is_help {
2583                return (
2584                    Pipeline::from_vec(vec![Expression::new(
2585                        working_set,
2586                        Expr::Call(call),
2587                        call_span,
2588                        output,
2589                    )]),
2590                    vec![],
2591                );
2592            }
2593
2594            (call, call_span, rest_spans)
2595        }
2596        None => {
2597            working_set.error(ParseError::UnknownState(
2598                "internal error: 'use' declaration not found".into(),
2599                Span::concat(spans),
2600            ));
2601            return (garbage_pipeline(working_set, spans), vec![]);
2602        }
2603    };
2604
2605    let import_pattern_expr = parse_import_pattern(working_set, args_spans);
2606
2607    let import_pattern = match &import_pattern_expr {
2608        Expression {
2609            expr: Expr::Nothing,
2610            ..
2611        } => {
2612            let mut call = call;
2613            call.set_parser_info(
2614                "noop".to_string(),
2615                Expression::new_unknown(Expr::Nothing, Span::unknown(), Type::Nothing),
2616            );
2617            return (
2618                Pipeline::from_vec(vec![Expression::new(
2619                    working_set,
2620                    Expr::Call(call),
2621                    Span::concat(spans),
2622                    Type::Any,
2623                )]),
2624                vec![],
2625            );
2626        }
2627        Expression {
2628            expr: Expr::ImportPattern(import_pattern),
2629            ..
2630        } => import_pattern.clone(),
2631        _ => {
2632            working_set.error(ParseError::UnknownState(
2633                "internal error: Import pattern positional is not import pattern".into(),
2634                import_pattern_expr.span,
2635            ));
2636            return (garbage_pipeline(working_set, spans), vec![]);
2637        }
2638    };
2639
2640    let (mut import_pattern, module, module_id) = if let Some(module_id) = import_pattern.head.id {
2641        let module = working_set.get_module(module_id).clone();
2642        (
2643            ImportPattern {
2644                head: ImportPatternHead {
2645                    name: module.name.clone(),
2646                    id: Some(module_id),
2647                    span: import_pattern.head.span,
2648                },
2649                members: import_pattern.members,
2650                hidden: HashSet::new(),
2651                constants: vec![],
2652            },
2653            module,
2654            module_id,
2655        )
2656    } else if let Some(module_id) = parse_module_file_or_dir(
2657        working_set,
2658        &import_pattern.head.name,
2659        import_pattern.head.span,
2660        None,
2661    ) {
2662        let module = working_set.get_module(module_id).clone();
2663        (
2664            ImportPattern {
2665                head: ImportPatternHead {
2666                    name: module.name.clone(),
2667                    id: Some(module_id),
2668                    span: import_pattern.head.span,
2669                },
2670                members: import_pattern.members,
2671                hidden: HashSet::new(),
2672                constants: vec![],
2673            },
2674            module,
2675            module_id,
2676        )
2677    } else {
2678        working_set.error(ParseError::ModuleNotFound(
2679            import_pattern.head.span,
2680            String::from_utf8_lossy(&import_pattern.head.name).to_string(),
2681        ));
2682        return (
2683            Pipeline::from_vec(vec![Expression::new(
2684                working_set,
2685                Expr::Call(call),
2686                call_span,
2687                Type::Any,
2688            )]),
2689            vec![],
2690        );
2691    };
2692
2693    let mut imported_modules = vec![];
2694    let (definitions, errors) = module.resolve_import_pattern(
2695        working_set,
2696        module_id,
2697        &import_pattern.members,
2698        None,
2699        name_span,
2700        &mut imported_modules,
2701    );
2702
2703    working_set.parse_errors.extend(errors);
2704
2705    let mut constants = vec![];
2706
2707    for (name, const_vid) in definitions.constants {
2708        constants.push((name, const_vid));
2709    }
2710
2711    for (name, const_val) in definitions.constant_values {
2712        let const_var_id =
2713            working_set.add_variable(name.clone(), name_span, const_val.get_type(), false);
2714        working_set.set_variable_const_val(const_var_id, const_val);
2715        constants.push((name, const_var_id));
2716    }
2717
2718    let exportables = definitions
2719        .decls
2720        .iter()
2721        .map(|(name, decl_id)| Exportable::Decl {
2722            name: name.clone(),
2723            id: *decl_id,
2724        })
2725        .chain(
2726            definitions
2727                .modules
2728                .iter()
2729                .map(|(name, module_id)| Exportable::Module {
2730                    name: name.clone(),
2731                    id: *module_id,
2732                }),
2733        )
2734        .chain(
2735            constants
2736                .iter()
2737                .map(|(name, variable_id)| Exportable::VarDecl {
2738                    name: name.clone(),
2739                    id: *variable_id,
2740                }),
2741        )
2742        .collect();
2743
2744    import_pattern.constants = constants.iter().map(|(_, id)| *id).collect();
2745
2746    if let Some(m) = parent_module {
2747        m.track_imported_modules(&imported_modules)
2748    }
2749    // Extend the current scope with the module's exportables
2750    working_set.use_decls(definitions.decls);
2751    working_set.use_modules(definitions.modules);
2752    working_set.use_variables(constants);
2753
2754    // Create a new Use command call to pass the import pattern as parser info
2755    let import_pattern_expr = Expression::new(
2756        working_set,
2757        Expr::ImportPattern(Box::new(import_pattern)),
2758        Span::concat(args_spans),
2759        Type::Any,
2760    );
2761
2762    let mut call = call;
2763    call.set_parser_info("import_pattern".to_string(), import_pattern_expr);
2764
2765    (
2766        Pipeline::from_vec(vec![Expression::new(
2767            working_set,
2768            Expr::Call(call),
2769            Span::concat(spans),
2770            Type::Any,
2771        )]),
2772        exportables,
2773    )
2774}
2775
2776pub fn parse_hide(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
2777    let spans = &lite_command.parts;
2778
2779    if working_set.get_span_contents(spans[0]) != b"hide" {
2780        working_set.error(ParseError::UnknownState(
2781            "internal error: Wrong call name for 'hide' command".into(),
2782            Span::concat(spans),
2783        ));
2784        return garbage_pipeline(working_set, spans);
2785    }
2786    if let Some(redirection) = lite_command.redirection.as_ref() {
2787        working_set.error(redirecting_builtin_error("hide", redirection));
2788        return garbage_pipeline(working_set, spans);
2789    }
2790
2791    let (call, args_spans) = match working_set.find_decl(b"hide") {
2792        Some(decl_id) => {
2793            let ParsedInternalCall { call, output } =
2794                parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
2795            let decl = working_set.get_decl(decl_id);
2796
2797            let call_span = Span::concat(spans);
2798
2799            let starting_error_count = working_set.parse_errors.len();
2800            check_call(working_set, call_span, &decl.signature(), &call);
2801
2802            let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
2803                return garbage_pipeline(working_set, spans);
2804            };
2805
2806            if starting_error_count != working_set.parse_errors.len() || is_help {
2807                return Pipeline::from_vec(vec![Expression::new(
2808                    working_set,
2809                    Expr::Call(call),
2810                    call_span,
2811                    output,
2812                )]);
2813            }
2814
2815            (call, &spans[1..])
2816        }
2817        None => {
2818            working_set.error(ParseError::UnknownState(
2819                "internal error: 'hide' declaration not found".into(),
2820                Span::concat(spans),
2821            ));
2822            return garbage_pipeline(working_set, spans);
2823        }
2824    };
2825
2826    let import_pattern_expr = parse_import_pattern(working_set, args_spans);
2827
2828    let import_pattern = if let Expression {
2829        expr: Expr::ImportPattern(import_pattern),
2830        ..
2831    } = &import_pattern_expr
2832    {
2833        import_pattern.clone()
2834    } else {
2835        working_set.error(ParseError::UnknownState(
2836            "internal error: Import pattern positional is not import pattern".into(),
2837            import_pattern_expr.span,
2838        ));
2839        return garbage_pipeline(working_set, spans);
2840    };
2841
2842    let bytes = working_set.get_span_contents(spans[0]);
2843
2844    if bytes == b"hide" && spans.len() >= 2 {
2845        for span in spans[1..].iter() {
2846            parse_string(working_set, *span);
2847        }
2848
2849        // module used only internally, not saved anywhere
2850        let (is_module, module) =
2851            if let Some(module_id) = working_set.find_module(&import_pattern.head.name) {
2852                (true, working_set.get_module(module_id).clone())
2853            } else if import_pattern.members.is_empty() {
2854                // The pattern head can be:
2855                if let Some(id) = working_set.find_decl(&import_pattern.head.name) {
2856                    // a custom command,
2857                    let mut module = Module::new(b"tmp".to_vec());
2858                    module.add_decl(import_pattern.head.name.clone(), id);
2859
2860                    (false, module)
2861                } else {
2862                    // , or it could be an env var (handled by the engine)
2863                    (false, Module::new(b"tmp".to_vec()))
2864                }
2865            } else {
2866                working_set.error(ParseError::ModuleNotFound(
2867                    spans[1],
2868                    String::from_utf8_lossy(&import_pattern.head.name).to_string(),
2869                ));
2870                return garbage_pipeline(working_set, spans);
2871            };
2872
2873        // This kind of inverts the import pattern matching found in parse_use()
2874        let decls_to_hide = if import_pattern.members.is_empty() {
2875            if is_module {
2876                module.decl_names_with_head(&import_pattern.head.name)
2877            } else {
2878                module.decl_names()
2879            }
2880        } else {
2881            match &import_pattern.members[0] {
2882                ImportPatternMember::Glob { .. } => module.decl_names(),
2883                ImportPatternMember::Name { name, span } => {
2884                    let mut decls = vec![];
2885
2886                    if name == b"main" {
2887                        if module.main.is_some() {
2888                            decls.push(import_pattern.head.name.clone());
2889                        } else {
2890                            working_set.error(ParseError::ExportNotFound(*span));
2891                        }
2892                    } else if let Some(item) =
2893                        module.decl_name_with_head(name, &import_pattern.head.name)
2894                    {
2895                        decls.push(item);
2896                    } else {
2897                        working_set.error(ParseError::ExportNotFound(*span));
2898                    }
2899
2900                    decls
2901                }
2902                ImportPatternMember::List { names } => {
2903                    let mut decls = vec![];
2904
2905                    for (name, span) in names {
2906                        if name == b"main" {
2907                            if module.main.is_some() {
2908                                decls.push(import_pattern.head.name.clone());
2909                            } else {
2910                                working_set.error(ParseError::ExportNotFound(*span));
2911                                break;
2912                            }
2913                        } else if let Some(item) =
2914                            module.decl_name_with_head(name, &import_pattern.head.name)
2915                        {
2916                            decls.push(item);
2917                        } else {
2918                            working_set.error(ParseError::ExportNotFound(*span));
2919                            break;
2920                        }
2921                    }
2922
2923                    decls
2924                }
2925            }
2926        };
2927
2928        let import_pattern = {
2929            let decls: HashSet<Vec<u8>> = decls_to_hide.iter().cloned().collect();
2930
2931            import_pattern.with_hidden(decls)
2932        };
2933
2934        // TODO: `use spam; use spam foo; hide foo` will hide both `foo` and `spam foo` since
2935        // they point to the same DeclId. Do we want to keep it that way?
2936        working_set.hide_decls(&decls_to_hide);
2937
2938        // Create a new Use command call to pass the new import pattern
2939        let import_pattern_expr = Expression::new(
2940            working_set,
2941            Expr::ImportPattern(Box::new(import_pattern)),
2942            Span::concat(args_spans),
2943            Type::Any,
2944        );
2945
2946        let mut call = call;
2947        call.set_parser_info("import_pattern".to_string(), import_pattern_expr);
2948
2949        Pipeline::from_vec(vec![Expression::new(
2950            working_set,
2951            Expr::Call(call),
2952            Span::concat(spans),
2953            Type::Any,
2954        )])
2955    } else {
2956        working_set.error(ParseError::UnknownState(
2957            "Expected structure: hide <name>".into(),
2958            Span::concat(spans),
2959        ));
2960        garbage_pipeline(working_set, spans)
2961    }
2962}
2963
2964pub fn parse_overlay_new(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
2965    let call_span = call.span();
2966
2967    let (overlay_name, _) = if let Some(expr) = call.positional_nth(0) {
2968        match eval_constant(working_set, expr) {
2969            Ok(val) => match val.coerce_into_string() {
2970                Ok(s) => (s, expr.span),
2971                Err(err) => {
2972                    working_set.error(err.wrap(working_set, call_span));
2973                    return garbage_pipeline(working_set, &[call_span]);
2974                }
2975            },
2976            Err(err) => {
2977                working_set.error(err.wrap(working_set, call_span));
2978                return garbage_pipeline(working_set, &[call_span]);
2979            }
2980        }
2981    } else {
2982        working_set.error(ParseError::UnknownState(
2983            "internal error: Missing required positional after call parsing".into(),
2984            call_span,
2985        ));
2986        return garbage_pipeline(working_set, &[call_span]);
2987    };
2988
2989    let pipeline = Pipeline::from_vec(vec![Expression::new(
2990        working_set,
2991        Expr::Call(call),
2992        call_span,
2993        Type::Any,
2994    )]);
2995
2996    let module_id = working_set.add_module(
2997        &overlay_name,
2998        Module::new(overlay_name.as_bytes().to_vec()),
2999        vec![],
3000    );
3001
3002    working_set.add_overlay(
3003        overlay_name.as_bytes().to_vec(),
3004        module_id,
3005        ResolvedImportPattern::new(vec![], vec![], vec![], vec![]),
3006        false,
3007    );
3008
3009    pipeline
3010}
3011
3012pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
3013    let call_span = call.span();
3014
3015    let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
3016        match eval_constant(working_set, expr) {
3017            Ok(Value::Nothing { .. }) => {
3018                let mut call = call;
3019                call.set_parser_info(
3020                    "noop".to_string(),
3021                    Expression::new_unknown(Expr::Bool(true), Span::unknown(), Type::Bool),
3022                );
3023                return Pipeline::from_vec(vec![Expression::new(
3024                    working_set,
3025                    Expr::Call(call),
3026                    call_span,
3027                    Type::Any,
3028                )]);
3029            }
3030            Ok(val) => match val.coerce_into_string() {
3031                Ok(s) => (s, expr.span),
3032                Err(err) => {
3033                    working_set.error(err.wrap(working_set, call_span));
3034                    return garbage_pipeline(working_set, &[call_span]);
3035                }
3036            },
3037            Err(err) => {
3038                working_set.error(err.wrap(working_set, call_span));
3039                return garbage_pipeline(working_set, &[call_span]);
3040            }
3041        }
3042    } else {
3043        working_set.error(ParseError::UnknownState(
3044            "internal error: Missing required positional after call parsing".into(),
3045            call_span,
3046        ));
3047        return garbage_pipeline(working_set, &[call_span]);
3048    };
3049
3050    let new_name = if let Some(kw_expression) = call.positional_nth(1) {
3051        if let Some(new_name_expression) = kw_expression.as_keyword() {
3052            match eval_constant(working_set, new_name_expression) {
3053                Ok(val) => match val.coerce_into_string() {
3054                    Ok(s) => Some(Spanned {
3055                        item: s,
3056                        span: new_name_expression.span,
3057                    }),
3058                    Err(err) => {
3059                        working_set.error(err.wrap(working_set, call_span));
3060                        return garbage_pipeline(working_set, &[call_span]);
3061                    }
3062                },
3063                Err(err) => {
3064                    working_set.error(err.wrap(working_set, call_span));
3065                    return garbage_pipeline(working_set, &[call_span]);
3066                }
3067            }
3068        } else {
3069            working_set.error(ParseError::ExpectedKeyword(
3070                "as keyword".to_string(),
3071                kw_expression.span,
3072            ));
3073            return garbage_pipeline(working_set, &[call_span]);
3074        }
3075    } else {
3076        None
3077    };
3078
3079    let Ok(has_prefix) = has_flag_const(working_set, &call, "prefix") else {
3080        return garbage_pipeline(working_set, &[call_span]);
3081    };
3082    let Ok(do_reload) = has_flag_const(working_set, &call, "reload") else {
3083        return garbage_pipeline(working_set, &[call_span]);
3084    };
3085
3086    let pipeline = Pipeline::from_vec(vec![Expression::new(
3087        working_set,
3088        Expr::Call(call.clone()),
3089        call_span,
3090        Type::Any,
3091    )]);
3092
3093    let (final_overlay_name, origin_module, origin_module_id, is_module_updated) =
3094        if let Some(overlay_frame) = working_set.find_overlay(overlay_name.as_bytes()) {
3095            // Activate existing overlay
3096
3097            // First, check for errors
3098            if has_prefix && !overlay_frame.prefixed {
3099                working_set.error(ParseError::OverlayPrefixMismatch(
3100                    overlay_name,
3101                    "without".to_string(),
3102                    overlay_name_span,
3103                ));
3104                return pipeline;
3105            }
3106
3107            if !has_prefix && overlay_frame.prefixed {
3108                working_set.error(ParseError::OverlayPrefixMismatch(
3109                    overlay_name,
3110                    "with".to_string(),
3111                    overlay_name_span,
3112                ));
3113                return pipeline;
3114            }
3115
3116            if let Some(new_name) = new_name {
3117                if new_name.item != overlay_name {
3118                    working_set.error(ParseError::CantAddOverlayHelp(
3119                        format!(
3120                        "Cannot add overlay as '{}' because it already exists under the name '{}'",
3121                        new_name.item, overlay_name
3122                    ),
3123                        new_name.span,
3124                    ));
3125                    return pipeline;
3126                }
3127            }
3128
3129            let module_id = overlay_frame.origin;
3130
3131            if let Some(new_module_id) = working_set.find_module(overlay_name.as_bytes()) {
3132                if !do_reload && (module_id == new_module_id) {
3133                    (
3134                        overlay_name,
3135                        Module::new(working_set.get_module(module_id).name.clone()),
3136                        module_id,
3137                        false,
3138                    )
3139                } else {
3140                    // The origin module of an overlay changed => update it
3141                    (
3142                        overlay_name,
3143                        working_set.get_module(new_module_id).clone(),
3144                        new_module_id,
3145                        true,
3146                    )
3147                }
3148            } else {
3149                let module_name = overlay_name.as_bytes().to_vec();
3150                (overlay_name, Module::new(module_name), module_id, true)
3151            }
3152        } else {
3153            // Create a new overlay
3154            if let Some(module_id) =
3155                // the name is a module
3156                working_set.find_module(overlay_name.as_bytes())
3157            {
3158                (
3159                    new_name.map(|spanned| spanned.item).unwrap_or(overlay_name),
3160                    working_set.get_module(module_id).clone(),
3161                    module_id,
3162                    true,
3163                )
3164            } else if let Some(module_id) = parse_module_file_or_dir(
3165                working_set,
3166                overlay_name.as_bytes(),
3167                overlay_name_span,
3168                new_name.as_ref().map(|spanned| spanned.item.clone()),
3169            ) {
3170                // try file or directory
3171                let new_module = working_set.get_module(module_id).clone();
3172                (
3173                    new_name
3174                        .map(|spanned| spanned.item)
3175                        .unwrap_or_else(|| String::from_utf8_lossy(&new_module.name).to_string()),
3176                    new_module,
3177                    module_id,
3178                    true,
3179                )
3180            } else {
3181                working_set.error(ParseError::ModuleOrOverlayNotFound(overlay_name_span));
3182                return pipeline;
3183            }
3184        };
3185
3186    let (definitions, errors) = if is_module_updated {
3187        if has_prefix {
3188            origin_module.resolve_import_pattern(
3189                working_set,
3190                origin_module_id,
3191                &[],
3192                Some(final_overlay_name.as_bytes()),
3193                call.head,
3194                &mut vec![],
3195            )
3196        } else {
3197            origin_module.resolve_import_pattern(
3198                working_set,
3199                origin_module_id,
3200                &[ImportPatternMember::Glob {
3201                    span: overlay_name_span,
3202                }],
3203                Some(final_overlay_name.as_bytes()),
3204                call.head,
3205                &mut vec![],
3206            )
3207        }
3208    } else {
3209        (
3210            ResolvedImportPattern::new(vec![], vec![], vec![], vec![]),
3211            vec![],
3212        )
3213    };
3214
3215    if errors.is_empty() {
3216        working_set.add_overlay(
3217            final_overlay_name.as_bytes().to_vec(),
3218            origin_module_id,
3219            definitions,
3220            has_prefix,
3221        );
3222    } else {
3223        working_set.parse_errors.extend(errors);
3224    }
3225
3226    // Change the call argument to include the Overlay expression with the module ID
3227    let mut call = call;
3228    call.set_parser_info(
3229        "overlay_expr".to_string(),
3230        Expression::new(
3231            working_set,
3232            Expr::Overlay(if is_module_updated {
3233                Some(origin_module_id)
3234            } else {
3235                None
3236            }),
3237            overlay_name_span,
3238            Type::Any,
3239        ),
3240    );
3241
3242    Pipeline::from_vec(vec![Expression::new(
3243        working_set,
3244        Expr::Call(call),
3245        call_span,
3246        Type::Any,
3247    )])
3248}
3249
3250pub fn parse_overlay_hide(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
3251    let call_span = call.span();
3252
3253    let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
3254        match eval_constant(working_set, expr) {
3255            Ok(val) => match val.coerce_into_string() {
3256                Ok(s) => (s, expr.span),
3257                Err(err) => {
3258                    working_set.error(err.wrap(working_set, call_span));
3259                    return garbage_pipeline(working_set, &[call_span]);
3260                }
3261            },
3262            Err(err) => {
3263                working_set.error(err.wrap(working_set, call_span));
3264                return garbage_pipeline(working_set, &[call_span]);
3265            }
3266        }
3267    } else {
3268        (
3269            String::from_utf8_lossy(working_set.last_overlay_name()).to_string(),
3270            call_span,
3271        )
3272    };
3273
3274    let Ok(keep_custom) = has_flag_const(working_set, &call, "keep-custom") else {
3275        return garbage_pipeline(working_set, &[call_span]);
3276    };
3277
3278    let pipeline = Pipeline::from_vec(vec![Expression::new(
3279        working_set,
3280        Expr::Call(call),
3281        call_span,
3282        Type::Any,
3283    )]);
3284
3285    if overlay_name == DEFAULT_OVERLAY_NAME {
3286        working_set.error(ParseError::CantHideDefaultOverlay(
3287            overlay_name,
3288            overlay_name_span,
3289        ));
3290
3291        return pipeline;
3292    }
3293
3294    if !working_set
3295        .unique_overlay_names()
3296        .contains(&overlay_name.as_bytes())
3297    {
3298        working_set.error(ParseError::ActiveOverlayNotFound(overlay_name_span));
3299        return pipeline;
3300    }
3301
3302    if working_set.num_overlays() < 2 {
3303        working_set.error(ParseError::CantRemoveLastOverlay(overlay_name_span));
3304        return pipeline;
3305    }
3306
3307    working_set.remove_overlay(overlay_name.as_bytes(), keep_custom);
3308
3309    pipeline
3310}
3311
3312pub fn parse_let(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline {
3313    trace!("parsing: let");
3314
3315    // JT: Disabling check_name because it doesn't work with optional types in the declaration
3316    // if let Some(span) = check_name(working_set, spans) {
3317    //     return Pipeline::from_vec(vec![garbage(*span)]);
3318    // }
3319
3320    if let Some(decl_id) = working_set.find_decl(b"let") {
3321        if spans.len() >= 4 {
3322            // This is a bit of by-hand parsing to get around the issue where we want to parse in the reverse order
3323            // so that the var-id created by the variable isn't visible in the expression that init it
3324            for span in spans.iter().enumerate() {
3325                let item = working_set.get_span_contents(*span.1);
3326                // https://github.com/nushell/nushell/issues/9596, let = if $
3327                // let x = 'f', = at least start from index 2
3328                if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
3329                    let (tokens, parse_error) = lex(
3330                        working_set.get_span_contents(Span::concat(&spans[(span.0 + 1)..])),
3331                        spans[span.0 + 1].start,
3332                        &[],
3333                        &[],
3334                        true,
3335                    );
3336
3337                    if let Some(parse_error) = parse_error {
3338                        working_set.error(parse_error)
3339                    }
3340
3341                    let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
3342                    let rvalue_block = parse_block(working_set, &tokens, rvalue_span, false, true);
3343
3344                    let output_type = rvalue_block.output_type();
3345
3346                    let block_id = working_set.add_block(Arc::new(rvalue_block));
3347
3348                    let rvalue = Expression::new(
3349                        working_set,
3350                        Expr::Block(block_id),
3351                        rvalue_span,
3352                        output_type,
3353                    );
3354
3355                    let mut idx = 0;
3356                    let (lvalue, explicit_type) =
3357                        parse_var_with_opt_type(working_set, &spans[1..(span.0)], &mut idx, false);
3358                    // check for extra tokens after the identifier
3359                    if idx + 1 < span.0 - 1 {
3360                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
3361                    }
3362
3363                    let var_name =
3364                        String::from_utf8_lossy(working_set.get_span_contents(lvalue.span))
3365                            .trim_start_matches('$')
3366                            .to_string();
3367
3368                    if RESERVED_VARIABLE_NAMES.contains(&var_name.as_str()) {
3369                        working_set.error(ParseError::NameIsBuiltinVar(var_name, lvalue.span))
3370                    }
3371
3372                    let var_id = lvalue.as_var();
3373                    let rhs_type = rvalue.ty.clone();
3374
3375                    if let Some(explicit_type) = &explicit_type {
3376                        if !type_compatible(explicit_type, &rhs_type) {
3377                            working_set.error(ParseError::TypeMismatch(
3378                                explicit_type.clone(),
3379                                rhs_type.clone(),
3380                                Span::concat(&spans[(span.0 + 1)..]),
3381                            ));
3382                        }
3383                    }
3384
3385                    if let Some(var_id) = var_id {
3386                        if explicit_type.is_none() {
3387                            working_set.set_variable_type(var_id, rhs_type);
3388                        }
3389                    }
3390
3391                    let call = Box::new(Call {
3392                        decl_id,
3393                        head: spans[0],
3394                        arguments: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
3395                        parser_info: HashMap::new(),
3396                    });
3397
3398                    return Pipeline::from_vec(vec![Expression::new(
3399                        working_set,
3400                        Expr::Call(call),
3401                        Span::concat(spans),
3402                        Type::Any,
3403                    )]);
3404                }
3405            }
3406        }
3407        let ParsedInternalCall { call, output } =
3408            parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
3409
3410        return Pipeline::from_vec(vec![Expression::new(
3411            working_set,
3412            Expr::Call(call),
3413            Span::concat(spans),
3414            output,
3415        )]);
3416    } else {
3417        working_set.error(ParseError::UnknownState(
3418            "internal error: let or const statements not found in core language".into(),
3419            Span::concat(spans),
3420        ))
3421    }
3422
3423    working_set.error(ParseError::UnknownState(
3424        "internal error: let or const statement unparsable".into(),
3425        Span::concat(spans),
3426    ));
3427
3428    garbage_pipeline(working_set, spans)
3429}
3430
3431/// Additionally returns a span encompassing the variable name, if successful.
3432pub fn parse_const(working_set: &mut StateWorkingSet, spans: &[Span]) -> (Pipeline, Option<Span>) {
3433    trace!("parsing: const");
3434
3435    // JT: Disabling check_name because it doesn't work with optional types in the declaration
3436    // if let Some(span) = check_name(working_set, spans) {
3437    //     return Pipeline::from_vec(vec![garbage(working_set, *span)]);
3438    // }
3439
3440    if let Some(decl_id) = working_set.find_decl(b"const") {
3441        if spans.len() >= 4 {
3442            // This is a bit of by-hand parsing to get around the issue where we want to parse in the reverse order
3443            // so that the var-id created by the variable isn't visible in the expression that init it
3444            for span in spans.iter().enumerate() {
3445                let item = working_set.get_span_contents(*span.1);
3446                // const x = 'f', = at least start from index 2
3447                if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
3448                    // Parse the rvalue as a subexpression
3449                    let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
3450
3451                    let (rvalue_tokens, rvalue_error) = lex(
3452                        working_set.get_span_contents(rvalue_span),
3453                        rvalue_span.start,
3454                        &[],
3455                        &[],
3456                        false,
3457                    );
3458                    working_set.parse_errors.extend(rvalue_error);
3459
3460                    trace!("parsing: const right-hand side subexpression");
3461                    let rvalue_block =
3462                        parse_block(working_set, &rvalue_tokens, rvalue_span, false, true);
3463                    let rvalue_ty = rvalue_block.output_type();
3464                    let rvalue_block_id = working_set.add_block(Arc::new(rvalue_block));
3465                    let rvalue = Expression::new(
3466                        working_set,
3467                        Expr::Subexpression(rvalue_block_id),
3468                        rvalue_span,
3469                        rvalue_ty,
3470                    );
3471
3472                    let mut idx = 0;
3473
3474                    let (lvalue, explicit_type) =
3475                        parse_var_with_opt_type(working_set, &spans[1..(span.0)], &mut idx, false);
3476                    // check for extra tokens after the identifier
3477                    if idx + 1 < span.0 - 1 {
3478                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
3479                    }
3480
3481                    let var_name =
3482                        String::from_utf8_lossy(working_set.get_span_contents(lvalue.span))
3483                            .trim_start_matches('$')
3484                            .to_string();
3485
3486                    if RESERVED_VARIABLE_NAMES.contains(&var_name.as_str()) {
3487                        working_set.error(ParseError::NameIsBuiltinVar(var_name, lvalue.span))
3488                    }
3489
3490                    let var_id = lvalue.as_var();
3491                    let rhs_type = rvalue.ty.clone();
3492
3493                    if let Some(explicit_type) = &explicit_type {
3494                        if !type_compatible(explicit_type, &rhs_type) {
3495                            working_set.error(ParseError::TypeMismatch(
3496                                explicit_type.clone(),
3497                                rhs_type.clone(),
3498                                Span::concat(&spans[(span.0 + 1)..]),
3499                            ));
3500                        }
3501                    }
3502
3503                    if let Some(var_id) = var_id {
3504                        if explicit_type.is_none() {
3505                            working_set.set_variable_type(var_id, rhs_type);
3506                        }
3507
3508                        match eval_constant(working_set, &rvalue) {
3509                            Ok(mut value) => {
3510                                // In case rhs is parsed as 'any' but is evaluated to a concrete
3511                                // type:
3512                                let mut const_type = value.get_type();
3513
3514                                if let Some(explicit_type) = &explicit_type {
3515                                    if !type_compatible(explicit_type, &const_type) {
3516                                        working_set.error(ParseError::TypeMismatch(
3517                                            explicit_type.clone(),
3518                                            const_type.clone(),
3519                                            Span::concat(&spans[(span.0 + 1)..]),
3520                                        ));
3521                                    }
3522                                    let val_span = value.span();
3523
3524                                    // need to convert to Value::glob if rhs is string, and
3525                                    // the const variable is annotated with glob type.
3526                                    match value {
3527                                        Value::String { val, .. }
3528                                            if explicit_type == &Type::Glob =>
3529                                        {
3530                                            value = Value::glob(val, false, val_span);
3531                                            const_type = value.get_type();
3532                                        }
3533                                        _ => {}
3534                                    }
3535                                }
3536
3537                                working_set.set_variable_type(var_id, const_type);
3538
3539                                // Assign the constant value to the variable
3540                                working_set.set_variable_const_val(var_id, value);
3541                            }
3542                            Err(err) => working_set.error(err.wrap(working_set, rvalue.span)),
3543                        }
3544                    }
3545
3546                    let call = Box::new(Call {
3547                        decl_id,
3548                        head: spans[0],
3549                        arguments: vec![
3550                            Argument::Positional(lvalue.clone()),
3551                            Argument::Positional(rvalue),
3552                        ],
3553                        parser_info: HashMap::new(),
3554                    });
3555
3556                    return (
3557                        Pipeline::from_vec(vec![Expression::new(
3558                            working_set,
3559                            Expr::Call(call),
3560                            Span::concat(spans),
3561                            Type::Any,
3562                        )]),
3563                        Some(lvalue.span),
3564                    );
3565                }
3566            }
3567        }
3568        let ParsedInternalCall { call, output } =
3569            parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
3570
3571        return (
3572            Pipeline::from_vec(vec![Expression::new(
3573                working_set,
3574                Expr::Call(call),
3575                Span::concat(spans),
3576                output,
3577            )]),
3578            None,
3579        );
3580    } else {
3581        working_set.error(ParseError::UnknownState(
3582            "internal error: let or const statements not found in core language".into(),
3583            Span::concat(spans),
3584        ))
3585    }
3586
3587    working_set.error(ParseError::UnknownState(
3588        "internal error: let or const statement unparsable".into(),
3589        Span::concat(spans),
3590    ));
3591
3592    (garbage_pipeline(working_set, spans), None)
3593}
3594
3595pub fn parse_mut(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline {
3596    trace!("parsing: mut");
3597
3598    // JT: Disabling check_name because it doesn't work with optional types in the declaration
3599    // if let Some(span) = check_name(working_set, spans) {
3600    //     return Pipeline::from_vec(vec![garbage(working_set, *span)]);
3601    // }
3602
3603    if let Some(decl_id) = working_set.find_decl(b"mut") {
3604        if spans.len() >= 4 {
3605            // This is a bit of by-hand parsing to get around the issue where we want to parse in the reverse order
3606            // so that the var-id created by the variable isn't visible in the expression that init it
3607            for span in spans.iter().enumerate() {
3608                let item = working_set.get_span_contents(*span.1);
3609                // mut x = 'f', = at least start from index 2
3610                if item == b"=" && spans.len() > (span.0 + 1) && span.0 > 1 {
3611                    let (tokens, parse_error) = lex(
3612                        working_set.get_span_contents(Span::concat(&spans[(span.0 + 1)..])),
3613                        spans[span.0 + 1].start,
3614                        &[],
3615                        &[],
3616                        true,
3617                    );
3618
3619                    if let Some(parse_error) = parse_error {
3620                        working_set.error(parse_error);
3621                    }
3622
3623                    let rvalue_span = Span::concat(&spans[(span.0 + 1)..]);
3624                    let rvalue_block = parse_block(working_set, &tokens, rvalue_span, false, true);
3625
3626                    let output_type = rvalue_block.output_type();
3627
3628                    let block_id = working_set.add_block(Arc::new(rvalue_block));
3629
3630                    let rvalue = Expression::new(
3631                        working_set,
3632                        Expr::Block(block_id),
3633                        rvalue_span,
3634                        output_type,
3635                    );
3636
3637                    let mut idx = 0;
3638
3639                    let (lvalue, explicit_type) =
3640                        parse_var_with_opt_type(working_set, &spans[1..(span.0)], &mut idx, true);
3641                    // check for extra tokens after the identifier
3642                    if idx + 1 < span.0 - 1 {
3643                        working_set.error(ParseError::ExtraTokens(spans[idx + 2]));
3644                    }
3645
3646                    let var_name =
3647                        String::from_utf8_lossy(working_set.get_span_contents(lvalue.span))
3648                            .trim_start_matches('$')
3649                            .to_string();
3650
3651                    if RESERVED_VARIABLE_NAMES.contains(&var_name.as_str()) {
3652                        working_set.error(ParseError::NameIsBuiltinVar(var_name, lvalue.span))
3653                    }
3654
3655                    let var_id = lvalue.as_var();
3656                    let rhs_type = rvalue.ty.clone();
3657
3658                    if let Some(explicit_type) = &explicit_type {
3659                        if !type_compatible(explicit_type, &rhs_type) {
3660                            working_set.error(ParseError::TypeMismatch(
3661                                explicit_type.clone(),
3662                                rhs_type.clone(),
3663                                Span::concat(&spans[(span.0 + 1)..]),
3664                            ));
3665                        }
3666                    }
3667
3668                    if let Some(var_id) = var_id {
3669                        if explicit_type.is_none() {
3670                            working_set.set_variable_type(var_id, rhs_type);
3671                        }
3672                    }
3673
3674                    let call = Box::new(Call {
3675                        decl_id,
3676                        head: spans[0],
3677                        arguments: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
3678                        parser_info: HashMap::new(),
3679                    });
3680
3681                    return Pipeline::from_vec(vec![Expression::new(
3682                        working_set,
3683                        Expr::Call(call),
3684                        Span::concat(spans),
3685                        Type::Any,
3686                    )]);
3687                }
3688            }
3689        }
3690        let ParsedInternalCall { call, output } =
3691            parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
3692
3693        return Pipeline::from_vec(vec![Expression::new(
3694            working_set,
3695            Expr::Call(call),
3696            Span::concat(spans),
3697            output,
3698        )]);
3699    } else {
3700        working_set.error(ParseError::UnknownState(
3701            "internal error: let or const statements not found in core language".into(),
3702            Span::concat(spans),
3703        ))
3704    }
3705
3706    working_set.error(ParseError::UnknownState(
3707        "internal error: let or const statement unparsable".into(),
3708        Span::concat(spans),
3709    ));
3710
3711    garbage_pipeline(working_set, spans)
3712}
3713
3714pub fn parse_source(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
3715    trace!("parsing source");
3716    let spans = &lite_command.parts;
3717    let name = working_set.get_span_contents(spans[0]);
3718
3719    if name == b"source" || name == b"source-env" {
3720        if let Some(redirection) = lite_command.redirection.as_ref() {
3721            let name = if name == b"source" {
3722                "source"
3723            } else {
3724                "source-env"
3725            };
3726            working_set.error(redirecting_builtin_error(name, redirection));
3727            return garbage_pipeline(working_set, spans);
3728        }
3729
3730        let scoped = name == b"source-env";
3731
3732        if let Some(decl_id) = working_set.find_decl(name) {
3733            #[allow(deprecated)]
3734            let cwd = working_set.get_cwd();
3735
3736            // Is this the right call to be using here?
3737            // Some of the others (`parse_let`) use it, some of them (`parse_hide`) don't.
3738            let ParsedInternalCall { call, output } =
3739                parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
3740
3741            let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
3742                return garbage_pipeline(working_set, spans);
3743            };
3744
3745            if is_help {
3746                return Pipeline::from_vec(vec![Expression::new(
3747                    working_set,
3748                    Expr::Call(call),
3749                    Span::concat(spans),
3750                    output,
3751                )]);
3752            }
3753
3754            // Command and one file name
3755            if spans.len() >= 2 {
3756                let expr = parse_value(working_set, spans[1], &SyntaxShape::Any);
3757
3758                let val = match eval_constant(working_set, &expr) {
3759                    Ok(val) => val,
3760                    Err(err) => {
3761                        working_set.error(err.wrap(working_set, Span::concat(&spans[1..])));
3762                        return Pipeline::from_vec(vec![Expression::new(
3763                            working_set,
3764                            Expr::Call(call),
3765                            Span::concat(&spans[1..]),
3766                            Type::Any,
3767                        )]);
3768                    }
3769                };
3770
3771                if val.is_nothing() {
3772                    let mut call = call;
3773                    call.set_parser_info(
3774                        "noop".to_string(),
3775                        Expression::new_unknown(Expr::Nothing, Span::unknown(), Type::Nothing),
3776                    );
3777                    return Pipeline::from_vec(vec![Expression::new(
3778                        working_set,
3779                        Expr::Call(call),
3780                        Span::concat(spans),
3781                        Type::Any,
3782                    )]);
3783                }
3784
3785                let filename = match val.coerce_into_string() {
3786                    Ok(s) => s,
3787                    Err(err) => {
3788                        working_set.error(err.wrap(working_set, Span::concat(&spans[1..])));
3789                        return Pipeline::from_vec(vec![Expression::new(
3790                            working_set,
3791                            Expr::Call(call),
3792                            Span::concat(&spans[1..]),
3793                            Type::Any,
3794                        )]);
3795                    }
3796                };
3797
3798                if let Some(path) = find_in_dirs(&filename, working_set, &cwd, Some(LIB_DIRS_VAR)) {
3799                    if let Some(contents) = path.read(working_set) {
3800                        // Add the file to the stack of files being processed.
3801                        if let Err(e) = working_set.files.push(path.clone().path_buf(), spans[1]) {
3802                            working_set.error(e);
3803                            return garbage_pipeline(working_set, spans);
3804                        }
3805
3806                        // This will load the defs from the file into the
3807                        // working set, if it was a successful parse.
3808                        let block = parse(
3809                            working_set,
3810                            Some(&path.path().to_string_lossy()),
3811                            &contents,
3812                            scoped,
3813                        );
3814
3815                        // Remove the file from the stack of files being processed.
3816                        working_set.files.pop();
3817
3818                        // Save the block into the working set
3819                        let block_id = working_set.add_block(block);
3820
3821                        let mut call_with_block = call;
3822
3823                        // FIXME: Adding this expression to the positional creates a syntax highlighting error
3824                        // after writing `source example.nu`
3825                        call_with_block.set_parser_info(
3826                            "block_id".to_string(),
3827                            Expression::new(
3828                                working_set,
3829                                Expr::Int(block_id.get() as i64),
3830                                spans[1],
3831                                Type::Any,
3832                            ),
3833                        );
3834
3835                        // store the file path as a string to be gathered later
3836                        call_with_block.set_parser_info(
3837                            "block_id_name".to_string(),
3838                            Expression::new(
3839                                working_set,
3840                                Expr::Filepath(path.path_buf().display().to_string(), false),
3841                                spans[1],
3842                                Type::String,
3843                            ),
3844                        );
3845
3846                        return Pipeline::from_vec(vec![Expression::new(
3847                            working_set,
3848                            Expr::Call(call_with_block),
3849                            Span::concat(spans),
3850                            Type::Any,
3851                        )]);
3852                    }
3853                } else {
3854                    working_set.error(ParseError::SourcedFileNotFound(filename, spans[1]));
3855                }
3856            }
3857            return Pipeline::from_vec(vec![Expression::new(
3858                working_set,
3859                Expr::Call(call),
3860                Span::concat(spans),
3861                Type::Any,
3862            )]);
3863        }
3864    }
3865    working_set.error(ParseError::UnknownState(
3866        "internal error: source statement unparsable".into(),
3867        Span::concat(spans),
3868    ));
3869    garbage_pipeline(working_set, spans)
3870}
3871
3872pub fn parse_where_expr(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expression {
3873    trace!("parsing: where");
3874
3875    if !spans.is_empty() && working_set.get_span_contents(spans[0]) != b"where" {
3876        working_set.error(ParseError::UnknownState(
3877            "internal error: Wrong call name for 'where' command".into(),
3878            Span::concat(spans),
3879        ));
3880        return garbage(working_set, Span::concat(spans));
3881    }
3882
3883    if spans.len() < 2 {
3884        working_set.error(ParseError::MissingPositional(
3885            "row condition".into(),
3886            Span::concat(spans),
3887            "where <row_condition>".into(),
3888        ));
3889        return garbage(working_set, Span::concat(spans));
3890    }
3891
3892    let call = match working_set.find_decl(b"where") {
3893        Some(decl_id) => {
3894            let ParsedInternalCall { call, output } =
3895                parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
3896            let decl = working_set.get_decl(decl_id);
3897
3898            let call_span = Span::concat(spans);
3899
3900            let starting_error_count = working_set.parse_errors.len();
3901            check_call(working_set, call_span, &decl.signature(), &call);
3902
3903            let Ok(is_help) = has_flag_const(working_set, &call, "help") else {
3904                return garbage(working_set, Span::concat(spans));
3905            };
3906
3907            if starting_error_count != working_set.parse_errors.len() || is_help {
3908                return Expression::new(working_set, Expr::Call(call), call_span, output);
3909            }
3910
3911            call
3912        }
3913        None => {
3914            working_set.error(ParseError::UnknownState(
3915                "internal error: 'where' declaration not found".into(),
3916                Span::concat(spans),
3917            ));
3918            return garbage(working_set, Span::concat(spans));
3919        }
3920    };
3921
3922    Expression::new(
3923        working_set,
3924        Expr::Call(call),
3925        Span::concat(spans),
3926        Type::Any,
3927    )
3928}
3929
3930pub fn parse_where(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
3931    let expr = parse_where_expr(working_set, &lite_command.parts);
3932    let redirection = lite_command
3933        .redirection
3934        .as_ref()
3935        .map(|r| parse_redirection(working_set, r));
3936
3937    let element = PipelineElement {
3938        pipe: None,
3939        expr,
3940        redirection,
3941    };
3942
3943    Pipeline {
3944        elements: vec![element],
3945    }
3946}
3947
3948#[cfg(feature = "plugin")]
3949pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
3950    use nu_protocol::{FromValue, PluginRegistryFile};
3951
3952    #[allow(deprecated)]
3953    let cwd = working_set.get_cwd();
3954
3955    if let Err(err) = (|| {
3956        let name = call
3957            .positional_nth(0)
3958            .map(|expr| {
3959                eval_constant(working_set, expr)
3960                    .and_then(Spanned::<String>::from_value)
3961                    .map_err(|err| err.wrap(working_set, call.head))
3962            })
3963            .expect("required positional should have been checked")?;
3964
3965        let plugin_config = call
3966            .named_iter()
3967            .find(|(arg_name, _, _)| arg_name.item == "plugin-config")
3968            .map(|(_, _, expr)| {
3969                let expr = expr
3970                    .as_ref()
3971                    .expect("--plugin-config arg should have been checked already");
3972                eval_constant(working_set, expr)
3973                    .and_then(Spanned::<String>::from_value)
3974                    .map_err(|err| err.wrap(working_set, call.head))
3975            })
3976            .transpose()?;
3977
3978        // The name could also be a filename, so try our best to expand it for that match.
3979        let filename_query = {
3980            let path = nu_path::expand_path_with(&name.item, &cwd, true);
3981            path.to_str()
3982                .and_then(|path_str| {
3983                    find_in_dirs(path_str, working_set, &cwd, Some("NU_PLUGIN_DIRS"))
3984                })
3985                .map(|parser_path| parser_path.path_buf())
3986                .unwrap_or(path)
3987        };
3988
3989        // Find the actual plugin config path location. We don't have a const/env variable for this,
3990        // it either lives in the current working directory or in the script's directory
3991        let plugin_config_path = if let Some(custom_path) = &plugin_config {
3992            find_in_dirs(&custom_path.item, working_set, &cwd, None).ok_or_else(|| {
3993                ParseError::FileNotFound(custom_path.item.clone(), custom_path.span)
3994            })?
3995        } else {
3996            ParserPath::RealPath(
3997                working_set
3998                    .permanent_state
3999                    .plugin_path
4000                    .as_ref()
4001                    .ok_or_else(|| ParseError::LabeledErrorWithHelp {
4002                        error: "Plugin registry file not set".into(),
4003                        label: "can't load plugin without registry file".into(),
4004                        span: call.head,
4005                        help:
4006                            "pass --plugin-config to `plugin use` when $nu.plugin-path is not set"
4007                                .into(),
4008                    })?
4009                    .to_owned(),
4010            )
4011        };
4012
4013        let file = plugin_config_path.open(working_set).map_err(|err| {
4014            ParseError::LabeledError(
4015                "Plugin registry file can't be opened".into(),
4016                err.to_string(),
4017                plugin_config.as_ref().map(|p| p.span).unwrap_or(call.head),
4018            )
4019        })?;
4020
4021        // The file is now open, so we just have to parse the contents and find the plugin
4022        let contents = PluginRegistryFile::read_from(file, Some(call.head))
4023            .map_err(|err| err.wrap(working_set, call.head))?;
4024
4025        let plugin_item = contents
4026            .plugins
4027            .iter()
4028            .find(|plugin| plugin.name == name.item || plugin.filename == filename_query)
4029            .ok_or_else(|| ParseError::PluginNotFound {
4030                name: name.item.clone(),
4031                name_span: name.span,
4032                plugin_config_span: plugin_config.as_ref().map(|p| p.span),
4033            })?;
4034
4035        // Now add the signatures to the working set
4036        nu_plugin_engine::load_plugin_registry_item(working_set, plugin_item, Some(call.head))
4037            .map_err(|err| err.wrap(working_set, call.head))?;
4038
4039        Ok(())
4040    })() {
4041        working_set.error(err);
4042    }
4043
4044    let call_span = call.span();
4045
4046    Pipeline::from_vec(vec![Expression::new(
4047        working_set,
4048        Expr::Call(call),
4049        call_span,
4050        Type::Nothing,
4051    )])
4052}
4053
4054pub fn find_dirs_var(working_set: &StateWorkingSet, var_name: &str) -> Option<VarId> {
4055    working_set
4056        .find_variable(format!("${}", var_name).as_bytes())
4057        .filter(|var_id| working_set.get_variable(*var_id).const_val.is_some())
4058}
4059
4060/// This helper function is used to find files during parsing
4061///
4062/// First, the actual current working directory is selected as
4063///   a) the directory of a file currently being parsed
4064///   b) current working directory (PWD)
4065///
4066/// Then, if the file is not found in the actual cwd, dirs_var is checked.
4067/// For now, we first check for a const with the name of `dirs_var_name`,
4068/// and if that's not found, then we try to look for an environment variable of the same name.
4069/// If there is a relative path in dirs_var, it is assumed to be relative to the actual cwd
4070/// determined in the first step.
4071///
4072/// Always returns an absolute path
4073pub fn find_in_dirs(
4074    filename: &str,
4075    working_set: &StateWorkingSet,
4076    cwd: &str,
4077    dirs_var_name: Option<&str>,
4078) -> Option<ParserPath> {
4079    pub fn find_in_dirs_with_id(
4080        filename: &str,
4081        working_set: &StateWorkingSet,
4082        cwd: &str,
4083        dirs_var_name: Option<&str>,
4084    ) -> Option<ParserPath> {
4085        // Choose whether to use file-relative or PWD-relative path
4086        let actual_cwd = working_set
4087            .files
4088            .current_working_directory()
4089            .unwrap_or(Path::new(cwd));
4090
4091        // Try if we have an existing virtual path
4092        if let Some(virtual_path) = working_set.find_virtual_path(filename) {
4093            return Some(ParserPath::from_virtual_path(
4094                working_set,
4095                filename,
4096                virtual_path,
4097            ));
4098        } else {
4099            let abs_virtual_filename = actual_cwd.join(filename);
4100            let abs_virtual_filename = abs_virtual_filename.to_string_lossy();
4101
4102            if let Some(virtual_path) = working_set.find_virtual_path(&abs_virtual_filename) {
4103                return Some(ParserPath::from_virtual_path(
4104                    working_set,
4105                    &abs_virtual_filename,
4106                    virtual_path,
4107                ));
4108            }
4109        }
4110
4111        // Try if we have an existing physical path
4112        if let Ok(p) = canonicalize_with(filename, actual_cwd) {
4113            return Some(ParserPath::RealPath(p));
4114        }
4115
4116        // Early-exit if path is non-existent absolute path
4117        let path = Path::new(filename);
4118        if !path.is_relative() {
4119            return None;
4120        }
4121
4122        // Look up relative path from NU_LIB_DIRS
4123        dirs_var_name
4124            .as_ref()
4125            .and_then(|dirs_var_name| find_dirs_var(working_set, dirs_var_name))
4126            .map(|var_id| working_set.get_variable(var_id))?
4127            .const_val
4128            .as_ref()?
4129            .as_list()
4130            .ok()?
4131            .iter()
4132            .map(|lib_dir| -> Option<PathBuf> {
4133                let dir = lib_dir.to_path().ok()?;
4134                let dir_abs = canonicalize_with(dir, actual_cwd).ok()?;
4135                canonicalize_with(filename, dir_abs).ok()
4136            })
4137            .find(Option::is_some)
4138            .flatten()
4139            .map(ParserPath::RealPath)
4140    }
4141
4142    // TODO: remove (see #8310)
4143    // Same as find_in_dirs_with_id but using $env.NU_LIB_DIRS instead of constant
4144    pub fn find_in_dirs_old(
4145        filename: &str,
4146        working_set: &StateWorkingSet,
4147        cwd: &str,
4148        dirs_env: Option<&str>,
4149    ) -> Option<PathBuf> {
4150        // Choose whether to use file-relative or PWD-relative path
4151        let actual_cwd = working_set
4152            .files
4153            .current_working_directory()
4154            .unwrap_or(Path::new(cwd));
4155
4156        if let Ok(p) = canonicalize_with(filename, actual_cwd) {
4157            Some(p)
4158        } else {
4159            let path = Path::new(filename);
4160
4161            if path.is_relative() {
4162                if let Some(lib_dirs) =
4163                    dirs_env.and_then(|dirs_env| working_set.get_env_var(dirs_env))
4164                {
4165                    if let Ok(dirs) = lib_dirs.as_list() {
4166                        for lib_dir in dirs {
4167                            if let Ok(dir) = lib_dir.to_path() {
4168                                // make sure the dir is absolute path
4169                                if let Ok(dir_abs) = canonicalize_with(dir, actual_cwd) {
4170                                    if let Ok(path) = canonicalize_with(filename, dir_abs) {
4171                                        return Some(path);
4172                                    }
4173                                }
4174                            }
4175                        }
4176
4177                        None
4178                    } else {
4179                        None
4180                    }
4181                } else {
4182                    None
4183                }
4184            } else {
4185                None
4186            }
4187        }
4188    }
4189
4190    find_in_dirs_with_id(filename, working_set, cwd, dirs_var_name).or_else(|| {
4191        find_in_dirs_old(filename, working_set, cwd, dirs_var_name).map(ParserPath::RealPath)
4192    })
4193}
4194
4195fn detect_params_in_name(
4196    working_set: &StateWorkingSet,
4197    name_span: Span,
4198    decl_name: &str,
4199) -> Option<ParseError> {
4200    let name = working_set.get_span_contents(name_span);
4201
4202    let extract_span = |delim: u8| {
4203        // it is okay to unwrap because we know the slice contains the byte
4204        let (idx, _) = name
4205            .iter()
4206            .find_position(|c| **c == delim)
4207            .unwrap_or((name.len(), &b' '));
4208        let param_span = Span::new(name_span.start + idx - 1, name_span.start + idx - 1);
4209        let error = ParseError::LabeledErrorWithHelp{
4210            error: "no space between name and parameters".into(),
4211            label: "expected space".into(),
4212            help: format!("consider adding a space between the `{decl_name}` command's name and its parameters"),
4213            span: param_span,
4214            };
4215        Some(error)
4216    };
4217
4218    if name.contains(&b'[') {
4219        extract_span(b'[')
4220    } else if name.contains(&b'(') {
4221        extract_span(b'(')
4222    } else {
4223        None
4224    }
4225}
4226
4227/// Run has_flag_const and push possible error to working_set
4228fn has_flag_const(working_set: &mut StateWorkingSet, call: &Call, name: &str) -> Result<bool, ()> {
4229    call.has_flag_const(working_set, name).map_err(|err| {
4230        working_set.error(err.wrap(working_set, call.span()));
4231    })
4232}