Support recursive functions in capture (#797)

This commit is contained in:
JT 2022-01-21 11:39:55 -05:00 committed by GitHub
parent f44954da68
commit 939745ad67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 35 deletions

View File

@ -155,7 +155,8 @@ pub fn parse_for(
// will come into scope as params. Because of this, we need to recalculated what // will come into scope as params. Because of this, we need to recalculated what
// variables this block will capture from the outside. // variables this block will capture from the outside.
let mut seen = vec![]; let mut seen = vec![];
let captures = find_captures_in_block(working_set, block, &mut seen); let mut seen_decls = vec![];
let captures = find_captures_in_block(working_set, block, &mut seen, &mut seen_decls);
let mut block = working_set.get_block_mut(block_id); let mut block = working_set.get_block_mut(block_id);
block.captures = captures; block.captures = captures;
@ -271,7 +272,8 @@ pub fn parse_def(
// will come into scope as params. Because of this, we need to recalculated what // will come into scope as params. Because of this, we need to recalculated what
// variables this block will capture from the outside. // variables this block will capture from the outside.
let mut seen = vec![]; let mut seen = vec![];
let captures = find_captures_in_block(working_set, block, &mut seen); let mut seen_decls = vec![];
let captures = find_captures_in_block(working_set, block, &mut seen, &mut seen_decls);
let mut block = working_set.get_block_mut(block_id); let mut block = working_set.get_block_mut(block_id);
block.captures = captures; block.captures = captures;

View File

@ -12,7 +12,7 @@ use nu_protocol::{
Statement, Statement,
}, },
engine::StateWorkingSet, engine::StateWorkingSet,
span, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId, span, DeclId, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId,
CONFIG_VARIABLE_ID, ENV_VARIABLE_ID, IN_VARIABLE_ID, CONFIG_VARIABLE_ID, ENV_VARIABLE_ID, IN_VARIABLE_ID,
}; };
@ -2234,7 +2234,8 @@ pub fn parse_row_condition(
}); });
let mut seen = vec![]; let mut seen = vec![];
let captures = find_captures_in_block(working_set, &block, &mut seen); let mut seen_decls = vec![];
let captures = find_captures_in_block(working_set, &block, &mut seen, &mut seen_decls);
block.captures = captures; block.captures = captures;
@ -2913,7 +2914,8 @@ pub fn parse_block_expression(
} }
let mut seen = vec![]; let mut seen = vec![];
let captures = find_captures_in_block(working_set, &output, &mut seen); let mut seen_decls = vec![];
let captures = find_captures_in_block(working_set, &output, &mut seen, &mut seen_decls);
output.captures = captures; output.captures = captures;
@ -3404,7 +3406,8 @@ pub fn parse_expression(
})]; })];
let mut seen = vec![]; let mut seen = vec![];
let captures = find_captures_in_block(working_set, &block, &mut seen); let mut seen_decls = vec![];
let captures = find_captures_in_block(working_set, &block, &mut seen, &mut seen_decls);
block.captures = captures; block.captures = captures;
let block_id = working_set.add_block(block); let block_id = working_set.add_block(block);
@ -3651,11 +3654,10 @@ pub fn find_captures_in_block(
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
block: &Block, block: &Block,
seen: &mut Vec<VarId>, seen: &mut Vec<VarId>,
seen_decls: &mut Vec<DeclId>,
) -> Vec<VarId> { ) -> Vec<VarId> {
let mut output = vec![]; let mut output = vec![];
// println!("sig: {:#?}", block.signature);
for flag in &block.signature.named { for flag in &block.signature.named {
if let Some(var_id) = flag.var_id { if let Some(var_id) = flag.var_id {
seen.push(var_id); seen.push(var_id);
@ -3681,7 +3683,7 @@ pub fn find_captures_in_block(
for stmt in &block.stmts { for stmt in &block.stmts {
match stmt { match stmt {
Statement::Pipeline(pipeline) => { Statement::Pipeline(pipeline) => {
let result = find_captures_in_pipeline(working_set, pipeline, seen); let result = find_captures_in_pipeline(working_set, pipeline, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
Statement::Declaration(_) => {} Statement::Declaration(_) => {}
@ -3695,10 +3697,11 @@ fn find_captures_in_pipeline(
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
pipeline: &Pipeline, pipeline: &Pipeline,
seen: &mut Vec<VarId>, seen: &mut Vec<VarId>,
seen_decls: &mut Vec<DeclId>,
) -> Vec<VarId> { ) -> Vec<VarId> {
let mut output = vec![]; let mut output = vec![];
for expr in &pipeline.expressions { for expr in &pipeline.expressions {
let result = find_captures_in_expr(working_set, expr, seen); let result = find_captures_in_expr(working_set, expr, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
@ -3709,56 +3712,64 @@ pub fn find_captures_in_expr(
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
expr: &Expression, expr: &Expression,
seen: &mut Vec<VarId>, seen: &mut Vec<VarId>,
seen_decls: &mut Vec<DeclId>,
) -> Vec<VarId> { ) -> Vec<VarId> {
let mut output = vec![]; let mut output = vec![];
match &expr.expr { match &expr.expr {
Expr::BinaryOp(lhs, _, rhs) => { Expr::BinaryOp(lhs, _, rhs) => {
let lhs_result = find_captures_in_expr(working_set, lhs, seen); let lhs_result = find_captures_in_expr(working_set, lhs, seen, seen_decls);
let rhs_result = find_captures_in_expr(working_set, rhs, seen); let rhs_result = find_captures_in_expr(working_set, rhs, seen, seen_decls);
output.extend(&lhs_result); output.extend(&lhs_result);
output.extend(&rhs_result); output.extend(&rhs_result);
} }
Expr::Block(block_id) => { Expr::Block(block_id) => {
let block = working_set.get_block(*block_id); let block = working_set.get_block(*block_id);
let result = find_captures_in_block(working_set, block, seen); let result = find_captures_in_block(working_set, block, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
Expr::Bool(_) => {} Expr::Bool(_) => {}
Expr::Call(call) => { Expr::Call(call) => {
let decl = working_set.get_decl(call.decl_id); let decl = working_set.get_decl(call.decl_id);
if !seen_decls.contains(&call.decl_id) {
if let Some(block_id) = decl.get_block_id() { if let Some(block_id) = decl.get_block_id() {
let block = working_set.get_block(block_id); let block = working_set.get_block(block_id);
let result = find_captures_in_block(working_set, block, seen); if !block.captures.is_empty() {
output.extend(&block.captures)
} else {
seen_decls.push(call.decl_id);
let result = find_captures_in_block(working_set, block, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
}
}
for named in &call.named { for named in &call.named {
if let Some(arg) = &named.1 { if let Some(arg) = &named.1 {
let result = find_captures_in_expr(working_set, arg, seen); let result = find_captures_in_expr(working_set, arg, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
} }
for positional in &call.positional { for positional in &call.positional {
let result = find_captures_in_expr(working_set, positional, seen); let result = find_captures_in_expr(working_set, positional, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
} }
Expr::CellPath(_) => {} Expr::CellPath(_) => {}
Expr::ExternalCall(head, exprs) => { Expr::ExternalCall(head, exprs) => {
let result = find_captures_in_expr(working_set, head, seen); let result = find_captures_in_expr(working_set, head, seen, seen_decls);
output.extend(&result); output.extend(&result);
for expr in exprs { for expr in exprs {
let result = find_captures_in_expr(working_set, expr, seen); let result = find_captures_in_expr(working_set, expr, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
} }
Expr::Filepath(_) => {} Expr::Filepath(_) => {}
Expr::Float(_) => {} Expr::Float(_) => {}
Expr::FullCellPath(cell_path) => { Expr::FullCellPath(cell_path) => {
let result = find_captures_in_expr(working_set, &cell_path.head, seen); let result = find_captures_in_expr(working_set, &cell_path.head, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
Expr::ImportPattern(_) => {} Expr::ImportPattern(_) => {}
@ -3767,34 +3778,44 @@ pub fn find_captures_in_expr(
Expr::GlobPattern(_) => {} Expr::GlobPattern(_) => {}
Expr::Int(_) => {} Expr::Int(_) => {}
Expr::Keyword(_, _, expr) => { Expr::Keyword(_, _, expr) => {
let result = find_captures_in_expr(working_set, expr, seen); let result = find_captures_in_expr(working_set, expr, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
Expr::List(exprs) => { Expr::List(exprs) => {
for expr in exprs { for expr in exprs {
let result = find_captures_in_expr(working_set, expr, seen); let result = find_captures_in_expr(working_set, expr, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
} }
Expr::Operator(_) => {} Expr::Operator(_) => {}
Expr::Range(expr1, expr2, expr3, _) => { Expr::Range(expr1, expr2, expr3, _) => {
if let Some(expr) = expr1 { if let Some(expr) = expr1 {
let result = find_captures_in_expr(working_set, expr, seen); let result = find_captures_in_expr(working_set, expr, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
if let Some(expr) = expr2 { if let Some(expr) = expr2 {
let result = find_captures_in_expr(working_set, expr, seen); let result = find_captures_in_expr(working_set, expr, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
if let Some(expr) = expr3 { if let Some(expr) = expr3 {
let result = find_captures_in_expr(working_set, expr, seen); let result = find_captures_in_expr(working_set, expr, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
} }
Expr::Record(fields) => { Expr::Record(fields) => {
for (field_name, field_value) in fields { for (field_name, field_value) in fields {
output.extend(&find_captures_in_expr(working_set, field_name, seen)); output.extend(&find_captures_in_expr(
output.extend(&find_captures_in_expr(working_set, field_value, seen)); working_set,
field_name,
seen,
seen_decls,
));
output.extend(&find_captures_in_expr(
working_set,
field_value,
seen,
seen_decls,
));
} }
} }
Expr::Signature(sig) => { Expr::Signature(sig) => {
@ -3824,29 +3845,29 @@ pub fn find_captures_in_expr(
Expr::String(_) => {} Expr::String(_) => {}
Expr::StringInterpolation(exprs) => { Expr::StringInterpolation(exprs) => {
for expr in exprs { for expr in exprs {
let result = find_captures_in_expr(working_set, expr, seen); let result = find_captures_in_expr(working_set, expr, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
} }
Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => { Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
let block = working_set.get_block(*block_id); let block = working_set.get_block(*block_id);
let result = find_captures_in_block(working_set, block, seen); let result = find_captures_in_block(working_set, block, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
Expr::Table(headers, values) => { Expr::Table(headers, values) => {
for header in headers { for header in headers {
let result = find_captures_in_expr(working_set, header, seen); let result = find_captures_in_expr(working_set, header, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
for row in values { for row in values {
for cell in row { for cell in row {
let result = find_captures_in_expr(working_set, cell, seen); let result = find_captures_in_expr(working_set, cell, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
} }
} }
Expr::ValueWithUnit(expr, _) => { Expr::ValueWithUnit(expr, _) => {
let result = find_captures_in_expr(working_set, expr, seen); let result = find_captures_in_expr(working_set, expr, seen, seen_decls);
output.extend(&result); output.extend(&result);
} }
Expr::Var(var_id) => { Expr::Var(var_id) => {
@ -3888,7 +3909,8 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
}; };
let mut seen = vec![]; let mut seen = vec![];
let captures = find_captures_in_block(working_set, &block, &mut seen); let mut seen_decls = vec![];
let captures = find_captures_in_block(working_set, &block, &mut seen, &mut seen_decls);
block.captures = captures; block.captures = captures;

View File

@ -164,3 +164,8 @@ fn string_interp_with_equals() -> TestResult {
"https://api.github.com/search/issues?q=repo:nushell/", "https://api.github.com/search/issues?q=repo:nushell/",
) )
} }
#[test]
fn recursive_parse() -> TestResult {
run_test(r#"def c [] { c }; echo done"#, "done")
}