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