mirror of
https://github.com/nushell/nushell.git
synced 2024-11-07 09:04:18 +01:00
correctly parse table literals as lists (#14226)
# User-Facing Changes Table literal arguments to list parameters are now correctly parsed: ```diff def a [l: list<any>] { $l | to nuon }; a [[a]; [2]] -[[a]] +[[a]; [2]] ```
This commit is contained in:
parent
3182adb6a0
commit
23fba6d2ea
@ -2312,7 +2312,7 @@ pub fn parse_full_cell_path(
|
|||||||
} else if bytes.starts_with(b"[") {
|
} else if bytes.starts_with(b"[") {
|
||||||
trace!("parsing: table head of full cell path");
|
trace!("parsing: table head of full cell path");
|
||||||
|
|
||||||
let output = parse_table_expression(working_set, head.span);
|
let output = parse_table_expression(working_set, head.span, &SyntaxShape::Any);
|
||||||
|
|
||||||
tokens.next();
|
tokens.next();
|
||||||
|
|
||||||
@ -4120,7 +4120,11 @@ fn parse_table_row(
|
|||||||
.map(|exprs| (exprs, span))
|
.map(|exprs| (exprs, span))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_table_expression(working_set: &mut StateWorkingSet, span: Span) -> Expression {
|
fn parse_table_expression(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
span: Span,
|
||||||
|
list_element_shape: &SyntaxShape,
|
||||||
|
) -> Expression {
|
||||||
let bytes = working_set.get_span_contents(span);
|
let bytes = working_set.get_span_contents(span);
|
||||||
let inner_span = {
|
let inner_span = {
|
||||||
let start = if bytes.starts_with(b"[") {
|
let start = if bytes.starts_with(b"[") {
|
||||||
@ -4149,13 +4153,13 @@ fn parse_table_expression(working_set: &mut StateWorkingSet, span: Span) -> Expr
|
|||||||
// Check that we have all arguments first, before trying to parse the first
|
// Check that we have all arguments first, before trying to parse the first
|
||||||
// in order to avoid exponential parsing time
|
// in order to avoid exponential parsing time
|
||||||
let [first, second, rest @ ..] = &tokens[..] else {
|
let [first, second, rest @ ..] = &tokens[..] else {
|
||||||
return parse_list_expression(working_set, span, &SyntaxShape::Any);
|
return parse_list_expression(working_set, span, list_element_shape);
|
||||||
};
|
};
|
||||||
if !working_set.get_span_contents(first.span).starts_with(b"[")
|
if !working_set.get_span_contents(first.span).starts_with(b"[")
|
||||||
|| second.contents != TokenContents::Semicolon
|
|| second.contents != TokenContents::Semicolon
|
||||||
|| rest.is_empty()
|
|| rest.is_empty()
|
||||||
{
|
{
|
||||||
return parse_list_expression(working_set, span, &SyntaxShape::Any);
|
return parse_list_expression(working_set, span, list_element_shape);
|
||||||
};
|
};
|
||||||
let head = parse_table_row(working_set, first.span);
|
let head = parse_table_row(working_set, first.span);
|
||||||
|
|
||||||
@ -4771,7 +4775,7 @@ pub fn parse_value(
|
|||||||
}
|
}
|
||||||
SyntaxShape::List(elem) => {
|
SyntaxShape::List(elem) => {
|
||||||
if bytes.starts_with(b"[") {
|
if bytes.starts_with(b"[") {
|
||||||
parse_list_expression(working_set, span, elem)
|
parse_table_expression(working_set, span, elem)
|
||||||
} else {
|
} else {
|
||||||
working_set.error(ParseError::Expected("list", span));
|
working_set.error(ParseError::Expected("list", span));
|
||||||
|
|
||||||
@ -4780,7 +4784,7 @@ pub fn parse_value(
|
|||||||
}
|
}
|
||||||
SyntaxShape::Table(_) => {
|
SyntaxShape::Table(_) => {
|
||||||
if bytes.starts_with(b"[") {
|
if bytes.starts_with(b"[") {
|
||||||
parse_table_expression(working_set, span)
|
parse_table_expression(working_set, span, &SyntaxShape::Any)
|
||||||
} else {
|
} else {
|
||||||
working_set.error(ParseError::Expected("table", span));
|
working_set.error(ParseError::Expected("table", span));
|
||||||
|
|
||||||
|
@ -271,68 +271,30 @@ fn table_annotations_none() -> TestResult {
|
|||||||
run_test(input, expected)
|
run_test(input, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[rstest]
|
||||||
fn table_annotations() -> TestResult {
|
fn table_annotations(
|
||||||
let input = "def run [t: table<age: int>] { $t }; run [[age]; [3]] | describe";
|
#[values(true, false)] list_annotation: bool,
|
||||||
let expected = "table<age: int>";
|
#[values(
|
||||||
run_test(input, expected)
|
("age: int", "age: int", "[[age]; [3]]" ),
|
||||||
}
|
("name: string age: int", "name: string, age: int", "[[name, age]; [nushell, 3]]" ),
|
||||||
|
("name: string, age: int", "name: string, age: int", "[[name, age]; [nushell, 3]]" ),
|
||||||
|
("name", "name: string", "[[name]; [nushell]]"),
|
||||||
|
("name: string, age", "name: string, age: int", "[[name, age]; [nushell, 3]]"),
|
||||||
|
("name, age", "name: string, age: int", "[[name, age]; [nushell, 3]]"),
|
||||||
|
("age: any", "age: duration", "[[age]; [2wk]]"),
|
||||||
|
("size", "size: filesize", "[[size]; [2mb]]")
|
||||||
|
)]
|
||||||
|
record_annotation_data: (&str, &str, &str),
|
||||||
|
) -> TestResult {
|
||||||
|
let (record_annotation, inferred_type, data) = record_annotation_data;
|
||||||
|
|
||||||
#[test]
|
let type_annotation = match list_annotation {
|
||||||
fn table_annotations_two_types() -> TestResult {
|
true => format!("list<record<{record_annotation}>>"),
|
||||||
let input = "\
|
false => format!("table<{record_annotation}>"),
|
||||||
def run [t: table<name: string age: int>] { $t };
|
};
|
||||||
run [[name, age]; [nushell, 3]] | describe";
|
let input = format!("def run [t: {type_annotation}] {{ $t }}; run {data} | describe");
|
||||||
let expected = "table<name: string, age: int>";
|
let expected = format!("table<{inferred_type}>");
|
||||||
run_test(input, expected)
|
run_test(&input, &expected)
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_annotations_two_types_comma_sep() -> TestResult {
|
|
||||||
let input = "\
|
|
||||||
def run [t: table<name: string, age: int>] { $t };
|
|
||||||
run [[name, age]; [nushell, 3]] | describe";
|
|
||||||
let expected = "table<name: string, age: int>";
|
|
||||||
run_test(input, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_annotations_key_with_no_type() -> TestResult {
|
|
||||||
let input = "def run [t: table<name>] { $t }; run [[name]; [nushell]] | describe";
|
|
||||||
let expected = "table<name: string>";
|
|
||||||
run_test(input, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_annotations_two_types_one_with_no_type() -> TestResult {
|
|
||||||
let input = "\
|
|
||||||
def run [t: table<name: string, age>] { $t };
|
|
||||||
run [[name, age]; [nushell, 3]] | describe";
|
|
||||||
let expected = "table<name: string, age: int>";
|
|
||||||
run_test(input, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_annotations_two_types_both_with_no_types() -> TestResult {
|
|
||||||
let input = "\
|
|
||||||
def run [t: table<name, age>] { $t };
|
|
||||||
run [[name, age]; [nushell, 3]] | describe";
|
|
||||||
let expected = "table<name: string, age: int>";
|
|
||||||
run_test(input, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_annotations_type_inference_1() -> TestResult {
|
|
||||||
let input = "def run [t: table<age: any>] { $t }; run [[age]; [2wk]] | describe";
|
|
||||||
let expected = "table<age: duration>";
|
|
||||||
run_test(input, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_annotations_type_inference_2() -> TestResult {
|
|
||||||
let input = "def run [t: table<size>] { $t }; run [[size]; [2mb]] | describe";
|
|
||||||
let expected = "table<size: filesize>";
|
|
||||||
run_test(input, expected)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user