Skip to content

Commit fa36c58

Browse files
committed
Add extern def which allows raw arguments
1 parent 24b4ac6 commit fa36c58

3 files changed

Lines changed: 71 additions & 41 deletions

File tree

crates/nu-cmd-lang/src/core_commands/extern_.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ impl Command for Extern {
1919
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
2020
.required("def_name", SyntaxShape::String, "definition name")
2121
.required("params", SyntaxShape::Signature, "parameters")
22+
.optional("body", SyntaxShape::Block, "wrapper function block")
2223
.category(Category::Core)
2324
}
2425

crates/nu-command/tests/commands/def.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,11 @@ fn def_with_paren_params() {
141141

142142
assert_eq!(actual.out, "3");
143143
}
144+
145+
#[test]
146+
fn extern_with_block() {
147+
let actual =
148+
nu!("extern foo [...rest] { print ($rest | str join ',' ) }; foo --bar baz -- -q -u -x");
149+
150+
assert_eq!(actual.out, "--bar,baz,--,-q,-u,-x");
151+
}

crates/nu-parser/src/parse_keywords.rs

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
159159
working_set.error(ParseError::DuplicateCommandDef(spans[1]));
160160
}
161161
}
162-
} else if name == b"extern" && spans.len() == 3 {
162+
} else if name == b"extern" && spans.len() >= 3 {
163163
let name_expr = parse_string(working_set, spans[1]);
164164
let name = name_expr.as_string();
165165

@@ -430,40 +430,7 @@ pub fn parse_def(
430430
*declaration = signature.clone().into_block_command(block_id);
431431

432432
let mut block = working_set.get_block_mut(block_id);
433-
let calls_itself = block.pipelines.iter().any(|pipeline| {
434-
pipeline
435-
.elements
436-
.iter()
437-
.any(|pipe_element| match pipe_element {
438-
PipelineElement::Expression(
439-
_,
440-
Expression {
441-
expr: Expr::Call(call_expr),
442-
..
443-
},
444-
) => {
445-
if call_expr.decl_id == decl_id {
446-
return true;
447-
}
448-
call_expr.arguments.iter().any(|arg| match arg {
449-
Argument::Positional(Expression { expr, .. }) => match expr {
450-
Expr::Keyword(.., expr) => {
451-
let expr = expr.as_ref();
452-
let Expression { expr, .. } = expr;
453-
match expr {
454-
Expr::Call(call_expr2) => call_expr2.decl_id == decl_id,
455-
_ => false,
456-
}
457-
}
458-
Expr::Call(call_expr2) => call_expr2.decl_id == decl_id,
459-
_ => false,
460-
},
461-
_ => false,
462-
})
463-
}
464-
_ => false,
465-
})
466-
});
433+
let calls_itself = block_calls_itself(block, decl_id);
467434
block.recursive = Some(calls_itself);
468435
block.signature = signature;
469436
block.redirect_env = def_call == b"def-env";
@@ -543,6 +510,7 @@ pub fn parse_extern(
543510
};
544511
let name_expr = call.positional_nth(0);
545512
let sig = call.positional_nth(1);
513+
let body = call.positional_nth(2);
546514

547515
if let (Some(name_expr), Some(sig)) = (name_expr, sig) {
548516
if let (Some(name), Some(mut signature)) = (&name_expr.as_string(), sig.as_signature()) {
@@ -581,13 +549,29 @@ pub fn parse_extern(
581549
signature.extra_usage = extra_usage.clone();
582550
signature.allows_unknown_args = true;
583551

584-
let decl = KnownExternal {
585-
name: external_name,
586-
usage: [usage, extra_usage].join("\n"),
587-
signature,
588-
};
552+
if let Some(block_id) = body.and_then(|x| x.as_block()) {
553+
if signature.rest_positional.is_none() {
554+
working_set.error(ParseError::InternalError(
555+
"Extern block must have a rest positional argument".into(),
556+
name_expr.span,
557+
));
558+
} else {
559+
*declaration = signature.clone().into_block_command(block_id);
560+
561+
let block = working_set.get_block_mut(block_id);
562+
let calls_itself = block_calls_itself(block, decl_id);
563+
block.recursive = Some(calls_itself);
564+
block.signature = signature;
565+
}
566+
} else {
567+
let decl = KnownExternal {
568+
name: external_name,
569+
usage: [usage, extra_usage].join("\n"),
570+
signature,
571+
};
589572

590-
*declaration = Box::new(decl);
573+
*declaration = Box::new(decl);
574+
}
591575
} else {
592576
working_set.error(ParseError::InternalError(
593577
"Predeclaration failed to add declaration".into(),
@@ -614,6 +598,43 @@ pub fn parse_extern(
614598
}])
615599
}
616600

601+
fn block_calls_itself(block: &Block, decl_id: usize) -> bool {
602+
block.pipelines.iter().any(|pipeline| {
603+
pipeline
604+
.elements
605+
.iter()
606+
.any(|pipe_element| match pipe_element {
607+
PipelineElement::Expression(
608+
_,
609+
Expression {
610+
expr: Expr::Call(call_expr),
611+
..
612+
},
613+
) => {
614+
if call_expr.decl_id == decl_id {
615+
return true;
616+
}
617+
call_expr.arguments.iter().any(|arg| match arg {
618+
Argument::Positional(Expression { expr, .. }) => match expr {
619+
Expr::Keyword(.., expr) => {
620+
let expr = expr.as_ref();
621+
let Expression { expr, .. } = expr;
622+
match expr {
623+
Expr::Call(call_expr2) => call_expr2.decl_id == decl_id,
624+
_ => false,
625+
}
626+
}
627+
Expr::Call(call_expr2) => call_expr2.decl_id == decl_id,
628+
_ => false,
629+
},
630+
_ => false,
631+
})
632+
}
633+
_ => false,
634+
})
635+
})
636+
}
637+
617638
pub fn parse_alias(
618639
working_set: &mut StateWorkingSet,
619640
lite_command: &LiteCommand,

0 commit comments

Comments
 (0)