Add explicit block params (#3444)

* Add explicit block params

* Add explicit block params
This commit is contained in:
JT 2021-05-19 20:23:45 +12:00 committed by GitHub
parent 0ff08bb63a
commit e2973d2176
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 153 additions and 57 deletions

View File

@ -37,7 +37,7 @@ impl<'s> Flatten<'s> {
)
.collect(),
Expression::Command => vec![LocationType::Command.spanned(e.span)],
Expression::Path(path) => self.expression(&path.head),
Expression::FullColumnPath(path) => self.expression(&path.head),
Expression::Variable(_, _) => vec![LocationType::Variable.spanned(e.span)],
Expression::Boolean(_)

View File

@ -296,7 +296,7 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
Expression::List(_) => Some(SyntaxShape::Table),
Expression::Boolean(_) => Some(SyntaxShape::String),
Expression::Path(_) => Some(SyntaxShape::ColumnPath),
Expression::FullColumnPath(_) => Some(SyntaxShape::ColumnPath),
Expression::FilePath(_) => Some(SyntaxShape::FilePath),
Expression::Block(_) => Some(SyntaxShape::Block),
Expression::ExternalCommand(_) => Some(SyntaxShape::String),
@ -539,7 +539,7 @@ impl VarSyntaxShapeDeductor {
trace!("Inferring vars in block");
self.infer_shape(&b, scope)?;
}
Expression::Path(path) => {
Expression::FullColumnPath(path) => {
trace!("Inferring vars in path");
match &path.head.expr {
//PathMember can't be var yet (?)
@ -790,7 +790,7 @@ impl VarSyntaxShapeDeductor {
| Expression::Binary(_)
| Expression::Range(_)
| Expression::Block(_)
| Expression::Path(_)
| Expression::FullColumnPath(_)
| Expression::FilePath(_)
| Expression::ExternalCommand(_)
| Expression::Command

View File

@ -55,6 +55,15 @@ impl WholeStreamCommand for Each {
"echo ['bob' 'fred'] | each --numbered { echo $\"{$it.index} is {$it.item}\" }",
result: Some(vec![Value::from("0 is bob"), Value::from("1 is fred")]),
},
Example {
description: "Name the block variable that each uses",
example: "[1, 2, 3] | each {|x| $x + 100}",
result: Some(vec![
UntaggedValue::int(101).into(),
UntaggedValue::int(102).into(),
UntaggedValue::int(103).into(),
]),
},
]
}
}

View File

@ -159,7 +159,7 @@ pub fn evaluate_baseline_expr(
.into_value(&tag),
)
}
Expression::Path(path) => {
Expression::FullColumnPath(path) => {
let value = evaluate_baseline_expr(&path.head, ctx)?;
let mut item = value;

View File

@ -13,12 +13,18 @@ use nu_protocol::{NamedType, PositionalType, Signature, SyntaxShape, UnspannedPa
use nu_source::{HasSpan, Span, Spanned, SpannedItem};
use num_bigint::BigInt;
use crate::lex::tokens::{LiteBlock, LiteCommand, LitePipeline};
use crate::path::expand_path;
use crate::{
lex::lexer::{lex, parse_block},
ParserScope,
};
use crate::{
lex::{
lexer::Token,
tokens::{LiteBlock, LiteCommand, LitePipeline, TokenContents},
},
parse::def::lex_split_baseline_tokens_on,
};
use crate::{parse::def::parse_parameter, path::expand_path};
use self::{
def::{parse_definition, parse_definition_prototype},
@ -919,20 +925,78 @@ fn parse_arg(
let string: String = chars.collect();
// We haven't done much with the inner string, so let's go ahead and work with it
let (tokens, err) = lex(&string, lite_arg.span.start() + 1);
let (mut tokens, err) = lex(&string, lite_arg.span.start() + 1);
if err.is_some() {
return (garbage(lite_arg.span), err);
}
// Check to see if we have parameters
let params = if matches!(
tokens.first(),
Some(Token {
contents: TokenContents::Pipe,
..
})
) {
// We've found a parameter list
let mut param_tokens = vec![];
let mut token_iter = tokens.into_iter().skip(1);
while let Some(token) = token_iter.next() {
if matches!(
token,
Token {
contents: TokenContents::Pipe,
..
}
) {
break;
} else {
param_tokens.push(token);
}
}
let split_tokens =
lex_split_baseline_tokens_on(param_tokens, &[',', ':', '?']);
let mut i = 0;
let mut params = vec![];
while i < split_tokens.len() {
let (parameter, advance_by, error) =
parse_parameter(&split_tokens[i..], split_tokens[i].span);
if error.is_some() {
return (garbage(lite_arg.span), error);
}
i += advance_by;
params.push(parameter);
}
tokens = token_iter.collect();
params
} else {
vec![]
};
let (lite_block, err) = parse_block(tokens);
if err.is_some() {
return (garbage(lite_arg.span), err);
}
scope.enter_scope();
let (classified_block, err) = classify_block(&lite_block, scope);
let (mut classified_block, err) = classify_block(&lite_block, scope);
scope.exit_scope();
if !params.is_empty() && classified_block.params.positional.is_empty() {
if let Some(classified_block) = Arc::get_mut(&mut classified_block) {
for param in params {
classified_block
.params
.positional
.push((param.pos_type, param.desc.unwrap_or_default()));
}
}
}
(
SpannedExpression::new(Expression::Block(classified_block), lite_arg.span),
err,
@ -1600,16 +1664,11 @@ fn parse_call(
})),
error,
);
// } else if lite_cmd.parts[0].item.starts_with('(') {
// let (expr, err) = parse_simple_invocation(&lite_cmd.parts[0], scope);
// error = error.or(err);
// return (Some(ClassifiedCommand::Expr(Box::new(expr))), error);
} else if lite_cmd.parts[0].item.starts_with('{') {
return parse_value_call(lite_cmd, scope);
} else if lite_cmd.parts[0].item.starts_with('$')
|| lite_cmd.parts[0].item.starts_with('\"')
|| lite_cmd.parts[0].item.starts_with('\'')
|| lite_cmd.parts[0].item.starts_with('`')
|| lite_cmd.parts[0].item.starts_with('-')
|| lite_cmd.parts[0].item.starts_with('0')
|| lite_cmd.parts[0].item.starts_with('1')

View File

@ -16,6 +16,7 @@ use crate::lex::lexer::{lex, parse_block};
use crate::ParserScope;
use self::signature::parse_signature;
pub use self::signature::{lex_split_baseline_tokens_on, parse_parameter};
mod data_structs;
mod primitives;

View File

@ -87,7 +87,7 @@ pub fn parse_signature(
i += advanced_by;
rest = rest_;
} else {
let (parameter, advanced_by, error) = parse_parameter(&tokens[i..], signature_vec);
let (parameter, advanced_by, error) = parse_parameter(&tokens[i..], signature_vec.span);
err = err.or(error);
i += advanced_by;
parameters.push(parameter);
@ -100,16 +100,13 @@ pub fn parse_signature(
(signature, err)
}
fn parse_parameter(
tokens: &[Token],
tokens_as_str: &Spanned<String>,
) -> (Parameter, usize, Option<ParseError>) {
pub fn parse_parameter(tokens: &[Token], span: Span) -> (Parameter, usize, Option<ParseError>) {
if tokens.is_empty() {
//TODO fix span
return (
Parameter::error(),
0,
Some(ParseError::unexpected_eof("parameter", tokens_as_str.span)),
Some(ParseError::unexpected_eof("parameter", span)),
);
}
@ -145,9 +142,15 @@ fn parse_parameter(
}
let pos_type = if optional {
PositionalType::optional(&name.item, type_)
} else {
if name.item.starts_with('$') {
PositionalType::optional(&name.item, type_)
} else {
PositionalType::optional(&format!("${}", name.item), type_)
}
} else if name.item.starts_with('$') {
PositionalType::mandatory(&name.item, type_)
} else {
PositionalType::mandatory(&format!("${}", name.item), type_)
};
let parameter = Parameter::new(pos_type, comment, name.span);
@ -402,7 +405,7 @@ fn lex_split_shortflag_from_longflag(tokens: Vec<Token>) -> Vec<Token> {
}
//Currently the lexer does not split baselines on ',' ':' '?'
//The parameter list requires this. Therefore here is a hacky method doing this.
fn lex_split_baseline_tokens_on(
pub fn lex_split_baseline_tokens_on(
tokens: Vec<Token>,
extra_baseline_terminal_tokens: &[char],
) -> Vec<Token> {

View File

@ -19,11 +19,11 @@ fn simple_def_with_params() {
sign.positional,
vec![
(
PositionalType::Optional("param1".into(), SyntaxShape::Int),
PositionalType::Optional("$param1".into(), SyntaxShape::Int),
"".into()
),
(
PositionalType::Mandatory("param2".into(), SyntaxShape::String),
PositionalType::Mandatory("$param2".into(), SyntaxShape::String),
"".into()
),
]
@ -40,11 +40,11 @@ fn simple_def_with_optional_param_without_type() {
sign.positional,
vec![
(
PositionalType::Optional("param1".into(), SyntaxShape::Any),
PositionalType::Optional("$param1".into(), SyntaxShape::Any),
"".into()
),
(
PositionalType::Optional("param2".into(), SyntaxShape::Any),
PositionalType::Optional("$param2".into(), SyntaxShape::Any),
"".into()
),
]
@ -64,11 +64,11 @@ fn simple_def_with_params_with_comment() {
sign.positional,
vec![
(
PositionalType::Mandatory("param1".into(), SyntaxShape::FilePath),
PositionalType::Mandatory("$param1".into(), SyntaxShape::FilePath),
"My first param".into()
),
(
PositionalType::Mandatory("param2".into(), SyntaxShape::Number),
PositionalType::Mandatory("$param2".into(), SyntaxShape::Number),
"My second param".into()
),
]
@ -88,11 +88,11 @@ fn simple_def_with_params_without_type() {
sign.positional,
vec![
(
PositionalType::Mandatory("param1".into(), SyntaxShape::Any),
PositionalType::Mandatory("$param1".into(), SyntaxShape::Any),
"My first param".into()
),
(
PositionalType::Mandatory("param2".into(), SyntaxShape::Number),
PositionalType::Mandatory("$param2".into(), SyntaxShape::Number),
"My second param".into()
),
]
@ -116,23 +116,23 @@ fn oddly_but_correct_written_params() {
sign.positional,
vec![
(
PositionalType::Mandatory("param1".into(), SyntaxShape::Int),
PositionalType::Mandatory("$param1".into(), SyntaxShape::Int),
"param1".into()
),
(
PositionalType::Mandatory("param2".into(), SyntaxShape::Number),
PositionalType::Mandatory("$param2".into(), SyntaxShape::Number),
"My second param".into()
),
(
PositionalType::Mandatory("param4".into(), SyntaxShape::Any),
PositionalType::Mandatory("$param4".into(), SyntaxShape::Any),
"".into()
),
(
PositionalType::Mandatory("param5".into(), SyntaxShape::FilePath),
PositionalType::Mandatory("$param5".into(), SyntaxShape::FilePath),
"".into()
),
(
PositionalType::Mandatory("param6".into(), SyntaxShape::Any),
PositionalType::Mandatory("$param6".into(), SyntaxShape::Any),
"param6".into()
),
]
@ -225,19 +225,19 @@ fn simple_def_with_params_and_flags() {
sign.positional,
vec![
(
PositionalType::Mandatory("param1".into(), SyntaxShape::Any),
PositionalType::Mandatory("$param1".into(), SyntaxShape::Any),
"".into()
),
(
PositionalType::Mandatory("param2".into(), SyntaxShape::Table),
PositionalType::Mandatory("$param2".into(), SyntaxShape::Table),
"Param2 Doc".into()
),
(
PositionalType::Mandatory("param3".into(), SyntaxShape::Number),
PositionalType::Mandatory("$param3".into(), SyntaxShape::Number),
"".into()
),
(
PositionalType::Optional("param4".into(), SyntaxShape::Table),
PositionalType::Optional("$param4".into(), SyntaxShape::Table),
"Optional Param".into()
),
]
@ -262,15 +262,15 @@ fn simple_def_with_parameters_and_flags_no_delimiter() {
// --flag3 # Third flag
vec![
(
PositionalType::Mandatory("param1".into(), SyntaxShape::Int),
PositionalType::Mandatory("$param1".into(), SyntaxShape::Int),
"".into()
),
(
PositionalType::Mandatory("param2".into(), SyntaxShape::Any),
PositionalType::Mandatory("$param2".into(), SyntaxShape::Any),
"".into()
),
(
PositionalType::Mandatory("param3".into(), SyntaxShape::Any),
PositionalType::Mandatory("$param3".into(), SyntaxShape::Any),
"Param3".into()
),
]
@ -302,7 +302,7 @@ fn simple_example_signature() {
assert_eq!(
sign.positional,
vec![(
PositionalType::Mandatory("d".into(), SyntaxShape::Int),
PositionalType::Mandatory("$d".into(), SyntaxShape::Int),
"The required d parameter".into()
)]
);
@ -374,7 +374,7 @@ fn simple_def_with_param_flag_and_rest() {
assert_eq!(
sign.positional,
vec![(
PositionalType::Mandatory("d".into(), SyntaxShape::String),
PositionalType::Mandatory("$d".into(), SyntaxShape::String),
"The required d parameter".into()
)]
);

View File

@ -28,7 +28,7 @@ pub fn expression_to_flat_shape(e: &SpannedExpression) -> Vec<Spanned<FlatShape>
}
output
}
Expression::Path(exprs) => {
Expression::FullColumnPath(exprs) => {
let mut output = vec![];
output.append(&mut expression_to_flat_shape(&exprs.head));
for member in exprs.tail.iter() {
@ -119,6 +119,30 @@ pub fn shapes(commands: &Block) -> Vec<Spanned<FlatShape>> {
ClassifiedCommand::Expr(expr) => {
output.append(&mut expression_to_flat_shape(expr))
}
ClassifiedCommand::Dynamic(call) => {
output.append(&mut expression_to_flat_shape(&call.head));
if let Some(positionals) = &call.positional {
for positional_arg in positionals {
output.append(&mut expression_to_flat_shape(positional_arg));
}
}
if let Some(named) = &call.named {
for (_, named_arg) in named.iter() {
match named_arg {
NamedValue::PresentSwitch(span) => {
output.push(FlatShape::Flag.spanned(*span));
}
NamedValue::Value(span, expr) => {
output.push(FlatShape::Flag.spanned(*span));
output.append(&mut expression_to_flat_shape(expr));
}
_ => {}
}
}
}
}
_ => {}
}
}

View File

@ -315,10 +315,10 @@ impl ExternalCommand {
pub fn has_it_usage(&self) -> bool {
self.args.iter().any(|arg| match arg {
SpannedExpression {
expr: Expression::Path(path),
expr: Expression::FullColumnPath(path),
..
} => {
let Path { head, .. } = &**path;
let FullColumnPath { head, .. } = &**path;
matches!(head, SpannedExpression{expr: Expression::Variable(x, ..), ..} if x == "$it")
}
_ => false,
@ -753,7 +753,7 @@ impl PrettyDebugWithSource for SpannedExpression {
),
"]",
),
Expression::Path(path) => path.pretty_debug(source),
Expression::FullColumnPath(path) => path.pretty_debug(source),
Expression::FilePath(path) => {
DbgDocBldr::typed("path", DbgDocBldr::primitive(path.display()))
}
@ -808,7 +808,7 @@ impl PrettyDebugWithSource for SpannedExpression {
),
"]",
),
Expression::Path(path) => path.pretty_debug(source),
Expression::FullColumnPath(path) => path.pretty_debug(source),
Expression::FilePath(path) => {
DbgDocBldr::typed("path", DbgDocBldr::primitive(path.display()))
}
@ -1006,12 +1006,12 @@ impl PrettyDebugWithSource for SpannedLiteral {
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, new, Deserialize, Serialize)]
pub struct Path {
pub struct FullColumnPath {
pub head: SpannedExpression,
pub tail: Vec<PathMember>,
}
impl PrettyDebugWithSource for Path {
impl PrettyDebugWithSource for FullColumnPath {
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
self.head.pretty_debug(source)
+ DbgDocBldr::operator(".")
@ -1033,7 +1033,7 @@ pub enum Expression {
Block(Arc<hir::Block>),
List(Vec<SpannedExpression>),
Table(Vec<SpannedExpression>, Vec<Vec<SpannedExpression>>),
Path(Box<Path>),
FullColumnPath(Box<FullColumnPath>),
FilePath(PathBuf),
ExternalCommand(ExternalStringCommand),
@ -1063,7 +1063,7 @@ impl ShellTypeName for Expression {
Expression::Range(..) => "range",
Expression::Block(..) => "block",
Expression::Invocation(..) => "command invocation",
Expression::Path(..) => "variable path",
Expression::FullColumnPath(..) => "variable path",
Expression::Boolean(..) => "boolean",
Expression::ExternalCommand(..) => "external",
Expression::Garbage => "garbage",
@ -1129,7 +1129,7 @@ impl Expression {
pub fn path(head: SpannedExpression, tail: Vec<impl Into<PathMember>>) -> Expression {
let tail = tail.into_iter().map(|t| t.into()).collect();
Expression::Path(Box::new(Path::new(head, tail)))
Expression::FullColumnPath(Box::new(FullColumnPath::new(head, tail)))
}
pub fn unit(i: Spanned<i64>, unit: Spanned<Unit>) -> Expression {
@ -1157,7 +1157,7 @@ impl Expression {
Expression::List(list) => list.iter().any(|se| se.has_it_usage()),
Expression::Invocation(block) => block.has_it_usage(),
Expression::Binary(binary) => binary.left.has_it_usage() || binary.right.has_it_usage(),
Expression::Path(path) => path.head.has_it_usage(),
Expression::FullColumnPath(path) => path.head.has_it_usage(),
Expression::Range(range) => {
(if let Some(left) = &range.left {
left.has_it_usage()
@ -1203,7 +1203,7 @@ impl Expression {
output.extend(binary.left.get_free_variables(known_variables));
output.extend(binary.right.get_free_variables(known_variables));
}
Expression::Path(path) => {
Expression::FullColumnPath(path) => {
output.extend(path.head.get_free_variables(known_variables));
}
Expression::Range(range) => {