mirror of
https://github.com/nushell/nushell.git
synced 2025-02-08 22:51:42 +01:00
parent
84a6010f71
commit
6f69ae8707
@ -27,6 +27,15 @@ impl<'s> Flatten<'s> {
|
|||||||
Expression::Block(block) => self.completion_locations(block),
|
Expression::Block(block) => self.completion_locations(block),
|
||||||
Expression::Invocation(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::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::Command => vec![LocationType::Command.spanned(e.span)],
|
||||||
Expression::Path(path) => self.expression(&path.head),
|
Expression::Path(path) => self.expression(&path.head),
|
||||||
Expression::Variable(_) => vec![LocationType::Variable.spanned(e.span)],
|
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))
|
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) => {
|
Expression::List(list) => {
|
||||||
let mut exprs = vec![];
|
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
|
/// Parses the given argument using the shape as a guide for how to correctly parse the argument
|
||||||
fn parse_arg(
|
fn parse_arg(
|
||||||
expected_type: SyntaxShape,
|
expected_type: SyntaxShape,
|
||||||
@ -644,7 +767,6 @@ fn parse_arg(
|
|||||||
(Some('['), Some(']')) => {
|
(Some('['), Some(']')) => {
|
||||||
// We have a literal row
|
// We have a literal row
|
||||||
let string: String = chars.collect();
|
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
|
// 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) {
|
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() {
|
if lite_block.block.is_empty() {
|
||||||
return (
|
return (
|
||||||
SpannedExpression::new(Expression::List(vec![]), lite_arg.span),
|
SpannedExpression::new(Expression::List(vec![]), lite_arg.span),
|
||||||
error,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if lite_block.block.len() > 1 {
|
if lite_block.block.len() == 1 {
|
||||||
return (
|
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),
|
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),
|
garbage(lite_arg.span),
|
||||||
|
@ -16,6 +16,18 @@ pub fn expression_to_flat_shape(e: &SpannedExpression) -> Vec<Spanned<FlatShape>
|
|||||||
}
|
}
|
||||||
output
|
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) => {
|
Expression::Path(exprs) => {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
output.append(&mut expression_to_flat_shape(&exprs.head));
|
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::Path(path) => path.pretty_debug(source),
|
||||||
Expression::FilePath(path) => b::typed("path", b::primitive(path.display())),
|
Expression::FilePath(path) => b::typed("path", b::primitive(path.display())),
|
||||||
Expression::ExternalCommand(external) => {
|
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::Path(path) => path.pretty_debug(source),
|
||||||
Expression::FilePath(path) => b::typed("path", b::primitive(path.display())),
|
Expression::FilePath(path) => b::typed("path", b::primitive(path.display())),
|
||||||
Expression::ExternalCommand(external) => b::typed(
|
Expression::ExternalCommand(external) => b::typed(
|
||||||
@ -960,6 +985,7 @@ pub enum Expression {
|
|||||||
Range(Box<Range>),
|
Range(Box<Range>),
|
||||||
Block(hir::Block),
|
Block(hir::Block),
|
||||||
List(Vec<SpannedExpression>),
|
List(Vec<SpannedExpression>),
|
||||||
|
Table(Vec<SpannedExpression>, Vec<Vec<SpannedExpression>>),
|
||||||
Path(Box<Path>),
|
Path(Box<Path>),
|
||||||
|
|
||||||
FilePath(PathBuf),
|
FilePath(PathBuf),
|
||||||
@ -985,6 +1011,7 @@ impl ShellTypeName for Expression {
|
|||||||
Expression::FilePath(..) => "file path",
|
Expression::FilePath(..) => "file path",
|
||||||
Expression::Variable(..) => "variable",
|
Expression::Variable(..) => "variable",
|
||||||
Expression::List(..) => "list",
|
Expression::List(..) => "list",
|
||||||
|
Expression::Table(..) => "table",
|
||||||
Expression::Binary(..) => "binary",
|
Expression::Binary(..) => "binary",
|
||||||
Expression::Range(..) => "range",
|
Expression::Range(..) => "range",
|
||||||
Expression::Block(..) => "block",
|
Expression::Block(..) => "block",
|
||||||
|
@ -418,6 +418,30 @@ fn echoing_ranges() {
|
|||||||
assert_eq!(actual.out, "6");
|
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 {
|
mod parse {
|
||||||
use nu_test_support::nu;
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user