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