nu_parser/
parse_keywords.rs

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