mirror of
https://github.com/nushell/nushell.git
synced 2025-02-02 03:30:16 +01:00
parent
84a6010f71
commit
6f69ae8707
@ -27,6 +27,15 @@ impl<'s> Flatten<'s> {
|
||||
Expression::Block(block) => self.completion_locations(block),
|
||||
Expression::Invocation(block) => self.completion_locations(block),
|
||||
Expression::List(exprs) => exprs.iter().flat_map(|v| self.expression(v)).collect(),
|
||||
Expression::Table(headers, cells) => headers
|
||||
.iter()
|
||||
.flat_map(|v| self.expression(v))
|
||||
.chain(
|
||||
cells
|
||||
.iter()
|
||||
.flat_map(|v| v.iter().flat_map(|v| self.expression(v))),
|
||||
)
|
||||
.collect(),
|
||||
Expression::Command => vec![LocationType::Command.spanned(e.span)],
|
||||
Expression::Path(path) => self.expression(&path.head),
|
||||
Expression::Variable(_) => vec![LocationType::Variable.spanned(e.span)],
|
||||
|
@ -80,6 +80,46 @@ pub(crate) async fn evaluate_baseline_expr(
|
||||
|
||||
Ok(UntaggedValue::range(left, right).into_value(tag))
|
||||
}
|
||||
Expression::Table(headers, cells) => {
|
||||
let mut output_headers = vec![];
|
||||
|
||||
for expr in headers {
|
||||
let val = evaluate_baseline_expr(&expr, registry, it, vars, env).await?;
|
||||
|
||||
let header = val.as_string()?;
|
||||
output_headers.push(header);
|
||||
}
|
||||
|
||||
let mut output_table = vec![];
|
||||
|
||||
for row in cells {
|
||||
if row.len() != headers.len() {
|
||||
match (row.first(), row.last()) {
|
||||
(Some(first), Some(last)) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Cell count doesn't match header count",
|
||||
format!("expected {} columns", headers.len()),
|
||||
Span::new(first.span.start(), last.span.end()),
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
"Cell count doesn't match header count",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut row_output = IndexMap::new();
|
||||
for cell in output_headers.iter().zip(row.iter()) {
|
||||
let val = evaluate_baseline_expr(&cell.1, registry, it, vars, env).await?;
|
||||
row_output.insert(cell.0.clone(), val);
|
||||
}
|
||||
output_table.push(UntaggedValue::row(row_output).into_value(tag.clone()));
|
||||
}
|
||||
|
||||
Ok(UntaggedValue::Table(output_table).into_value(tag))
|
||||
}
|
||||
Expression::List(list) => {
|
||||
let mut exprs = vec![];
|
||||
|
||||
|
@ -537,6 +537,129 @@ fn parse_external_arg(
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_list(
|
||||
lite_block: &LiteBlock,
|
||||
registry: &dyn SignatureRegistry,
|
||||
) -> (Vec<SpannedExpression>, Option<ParseError>) {
|
||||
let mut error = None;
|
||||
|
||||
if lite_block.block.is_empty() {
|
||||
return (vec![], None);
|
||||
}
|
||||
let lite_pipeline = &lite_block.block[0];
|
||||
let mut output = vec![];
|
||||
for lite_inner in &lite_pipeline.commands {
|
||||
let (arg, err) = parse_arg(SyntaxShape::Any, registry, &lite_inner.name);
|
||||
|
||||
output.push(arg);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
|
||||
for arg in &lite_inner.args {
|
||||
let (arg, err) = parse_arg(SyntaxShape::Any, registry, &arg);
|
||||
output.push(arg);
|
||||
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(output, error)
|
||||
}
|
||||
|
||||
fn verify_and_strip(
|
||||
contents: &Spanned<String>,
|
||||
left: char,
|
||||
right: char,
|
||||
) -> (String, Option<ParseError>) {
|
||||
let mut chars = contents.item.chars();
|
||||
|
||||
match (chars.next(), chars.next_back()) {
|
||||
(Some(l), Some(r)) if l == left && r == right => {
|
||||
let output: String = chars.collect();
|
||||
(output, None)
|
||||
}
|
||||
_ => (
|
||||
String::new(),
|
||||
Some(ParseError::mismatch(
|
||||
format!("value in {} {}", left, right),
|
||||
contents.clone(),
|
||||
)),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_table(
|
||||
lite_block: &LiteBlock,
|
||||
registry: &dyn SignatureRegistry,
|
||||
span: Span,
|
||||
) -> (SpannedExpression, Option<ParseError>) {
|
||||
let mut error = None;
|
||||
let mut output = vec![];
|
||||
|
||||
// Header
|
||||
let lite_pipeline = &lite_block.block[0];
|
||||
let lite_inner = &lite_pipeline.commands[0];
|
||||
|
||||
let (string, err) = verify_and_strip(&lite_inner.name, '[', ']');
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
|
||||
let lite_header = match lite_parse(&string, lite_inner.name.span.start() + 1) {
|
||||
Ok(lb) => lb,
|
||||
Err(e) => return (garbage(lite_inner.name.span), Some(e.cause)),
|
||||
};
|
||||
|
||||
let (headers, err) = parse_list(&lite_header, registry);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
|
||||
// Cells
|
||||
let lite_rows = &lite_block.block[1];
|
||||
let lite_cells = &lite_rows.commands[0];
|
||||
|
||||
let (string, err) = verify_and_strip(&lite_cells.name, '[', ']');
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
|
||||
let lite_cell = match lite_parse(&string, lite_cells.name.span.start() + 1) {
|
||||
Ok(lb) => lb,
|
||||
Err(e) => return (garbage(lite_cells.name.span), Some(e.cause)),
|
||||
};
|
||||
|
||||
let (inner_cell, err) = parse_list(&lite_cell, registry);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
output.push(inner_cell);
|
||||
|
||||
for arg in &lite_cells.args {
|
||||
let (string, err) = verify_and_strip(&arg, '[', ']');
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
let lite_cell = match lite_parse(&string, arg.span.start() + 1) {
|
||||
Ok(lb) => lb,
|
||||
Err(e) => return (garbage(arg.span), Some(e.cause)),
|
||||
};
|
||||
let (inner_cell, err) = parse_list(&lite_cell, registry);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
output.push(inner_cell);
|
||||
}
|
||||
|
||||
(
|
||||
SpannedExpression::new(Expression::Table(headers, output), span),
|
||||
error,
|
||||
)
|
||||
}
|
||||
|
||||
/// Parses the given argument using the shape as a guide for how to correctly parse the argument
|
||||
fn parse_arg(
|
||||
expected_type: SyntaxShape,
|
||||
@ -644,7 +767,6 @@ fn parse_arg(
|
||||
(Some('['), Some(']')) => {
|
||||
// We have a literal row
|
||||
let string: String = chars.collect();
|
||||
let mut error = None;
|
||||
|
||||
// We haven't done much with the inner string, so let's go ahead and work with it
|
||||
let lite_block = match lite_parse(&string, lite_arg.span.start() + 1) {
|
||||
@ -655,40 +777,26 @@ fn parse_arg(
|
||||
if lite_block.block.is_empty() {
|
||||
return (
|
||||
SpannedExpression::new(Expression::List(vec![]), lite_arg.span),
|
||||
error,
|
||||
None,
|
||||
);
|
||||
}
|
||||
if lite_block.block.len() > 1 {
|
||||
return (
|
||||
if lite_block.block.len() == 1 {
|
||||
let (items, err) = parse_list(&lite_block, registry);
|
||||
(
|
||||
SpannedExpression::new(Expression::List(items), lite_arg.span),
|
||||
err,
|
||||
)
|
||||
} else if lite_block.block.len() == 2 {
|
||||
parse_table(&lite_block, registry, lite_arg.span)
|
||||
} else {
|
||||
(
|
||||
garbage(lite_arg.span),
|
||||
Some(ParseError::mismatch("table", lite_arg.clone())),
|
||||
);
|
||||
Some(ParseError::mismatch(
|
||||
"list or table",
|
||||
"unknown".to_string().spanned(lite_arg.span),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
let lite_pipeline = lite_block.block[0].clone();
|
||||
let mut output = vec![];
|
||||
for lite_inner in &lite_pipeline.commands {
|
||||
let (arg, err) = parse_arg(SyntaxShape::Any, registry, &lite_inner.name);
|
||||
|
||||
output.push(arg);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
|
||||
for arg in &lite_inner.args {
|
||||
let (arg, err) = parse_arg(SyntaxShape::Any, registry, &arg);
|
||||
output.push(arg);
|
||||
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
SpannedExpression::new(Expression::List(output), lite_arg.span),
|
||||
error,
|
||||
)
|
||||
}
|
||||
_ => (
|
||||
garbage(lite_arg.span),
|
||||
|
@ -16,6 +16,18 @@ pub fn expression_to_flat_shape(e: &SpannedExpression) -> Vec<Spanned<FlatShape>
|
||||
}
|
||||
output
|
||||
}
|
||||
Expression::Table(headers, cells) => {
|
||||
let mut output = vec![];
|
||||
for header in headers.iter() {
|
||||
output.append(&mut expression_to_flat_shape(header));
|
||||
}
|
||||
for row in cells {
|
||||
for cell in row {
|
||||
output.append(&mut expression_to_flat_shape(&cell));
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
Expression::Path(exprs) => {
|
||||
let mut output = vec![];
|
||||
output.append(&mut expression_to_flat_shape(&exprs.head));
|
||||
|
@ -716,6 +716,20 @@ impl PrettyDebugWithSource for SpannedExpression {
|
||||
),
|
||||
"]",
|
||||
),
|
||||
Expression::Table(_headers, cells) => b::delimit(
|
||||
"[",
|
||||
b::intersperse(
|
||||
cells
|
||||
.iter()
|
||||
.map(|row| {
|
||||
row.iter()
|
||||
.map(|item| item.refined_pretty_debug(refine, source))
|
||||
})
|
||||
.flatten(),
|
||||
b::space(),
|
||||
),
|
||||
"]",
|
||||
),
|
||||
Expression::Path(path) => path.pretty_debug(source),
|
||||
Expression::FilePath(path) => b::typed("path", b::primitive(path.display())),
|
||||
Expression::ExternalCommand(external) => {
|
||||
@ -756,6 +770,17 @@ impl PrettyDebugWithSource for SpannedExpression {
|
||||
),
|
||||
"]",
|
||||
),
|
||||
Expression::Table(_headers, cells) => b::delimit(
|
||||
"[",
|
||||
b::intersperse(
|
||||
cells
|
||||
.iter()
|
||||
.map(|row| row.iter().map(|item| item.pretty_debug(source)))
|
||||
.flatten(),
|
||||
b::space(),
|
||||
),
|
||||
"]",
|
||||
),
|
||||
Expression::Path(path) => path.pretty_debug(source),
|
||||
Expression::FilePath(path) => b::typed("path", b::primitive(path.display())),
|
||||
Expression::ExternalCommand(external) => b::typed(
|
||||
@ -960,6 +985,7 @@ pub enum Expression {
|
||||
Range(Box<Range>),
|
||||
Block(hir::Block),
|
||||
List(Vec<SpannedExpression>),
|
||||
Table(Vec<SpannedExpression>, Vec<Vec<SpannedExpression>>),
|
||||
Path(Box<Path>),
|
||||
|
||||
FilePath(PathBuf),
|
||||
@ -985,6 +1011,7 @@ impl ShellTypeName for Expression {
|
||||
Expression::FilePath(..) => "file path",
|
||||
Expression::Variable(..) => "variable",
|
||||
Expression::List(..) => "list",
|
||||
Expression::Table(..) => "table",
|
||||
Expression::Binary(..) => "binary",
|
||||
Expression::Range(..) => "range",
|
||||
Expression::Block(..) => "block",
|
||||
|
@ -418,6 +418,30 @@ fn echoing_ranges() {
|
||||
assert_eq!(actual.out, "6");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_literals1() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
echo [[name age]; [foo 13]] | get age
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "13");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_literals2() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
echo [[name age] ; [bob 13] [sally 20]] | get age | math sum
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "33");
|
||||
}
|
||||
|
||||
mod parse {
|
||||
use nu_test_support::nu;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user