mirror of
https://github.com/nushell/nushell.git
synced 2025-05-30 14:50:02 +02:00
feat(cell-path): add insensitive column syntax and parsing
This commit is contained in:
parent
d411a1e9e6
commit
e0812f6bc8
@ -1797,7 +1797,7 @@ pub fn parse_range(working_set: &mut StateWorkingSet, span: Span) -> Option<Expr
|
|||||||
&contents[..dotdot_pos[0]],
|
&contents[..dotdot_pos[0]],
|
||||||
span.start,
|
span.start,
|
||||||
&[],
|
&[],
|
||||||
&[b'.', b'?'],
|
&[b'.', b'?', b'!'],
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
if let Some(_err) = err {
|
if let Some(_err) = err {
|
||||||
@ -2297,9 +2297,55 @@ pub fn parse_cell_path(
|
|||||||
expect_dot: bool,
|
expect_dot: bool,
|
||||||
) -> Vec<PathMember> {
|
) -> Vec<PathMember> {
|
||||||
enum TokenType {
|
enum TokenType {
|
||||||
Dot, // .
|
Dot, // .
|
||||||
QuestionOrDot, // ? or .
|
DotOrSign, // . or ? or !
|
||||||
PathMember, // an int or string, like `1` or `foo`
|
DotOrExclamation, // . or !
|
||||||
|
DotOrQuestion, // . or ?
|
||||||
|
PathMember, // an int or string, like `1` or `foo`
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ModifyMember {
|
||||||
|
No,
|
||||||
|
Optional,
|
||||||
|
Insensitive,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenType {
|
||||||
|
fn expect(&mut self, byte: u8) -> Result<ModifyMember, &'static str> {
|
||||||
|
match (&*self, byte) {
|
||||||
|
(Self::PathMember, _) => {
|
||||||
|
*self = Self::DotOrSign;
|
||||||
|
Ok(ModifyMember::No)
|
||||||
|
}
|
||||||
|
(
|
||||||
|
Self::Dot | Self::DotOrSign | Self::DotOrExclamation | Self::DotOrQuestion,
|
||||||
|
b'.',
|
||||||
|
) => {
|
||||||
|
*self = Self::PathMember;
|
||||||
|
Ok(ModifyMember::No)
|
||||||
|
}
|
||||||
|
(Self::DotOrSign, b'!') => {
|
||||||
|
*self = Self::DotOrQuestion;
|
||||||
|
Ok(ModifyMember::Insensitive)
|
||||||
|
}
|
||||||
|
(Self::DotOrSign, b'?') => {
|
||||||
|
*self = Self::DotOrExclamation;
|
||||||
|
Ok(ModifyMember::Optional)
|
||||||
|
}
|
||||||
|
(Self::DotOrSign, _) => Err(". or ! or ?"),
|
||||||
|
(Self::DotOrExclamation, b'!') => {
|
||||||
|
*self = Self::Dot;
|
||||||
|
Ok(ModifyMember::Insensitive)
|
||||||
|
}
|
||||||
|
(Self::DotOrExclamation, _) => Err(". or !"),
|
||||||
|
(Self::DotOrQuestion, b'?') => {
|
||||||
|
*self = Self::Dot;
|
||||||
|
Ok(ModifyMember::Optional)
|
||||||
|
}
|
||||||
|
(Self::DotOrQuestion, _) => Err(". or ?"),
|
||||||
|
(Self::Dot, _) => Err("."),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parsing a cell path is essentially a state machine, and this is the state
|
// Parsing a cell path is essentially a state machine, and this is the state
|
||||||
@ -2314,73 +2360,68 @@ pub fn parse_cell_path(
|
|||||||
for path_element in tokens {
|
for path_element in tokens {
|
||||||
let bytes = working_set.get_span_contents(path_element.span);
|
let bytes = working_set.get_span_contents(path_element.span);
|
||||||
|
|
||||||
match expected_token {
|
// both parse_int and parse_string require their source to be non-empty
|
||||||
TokenType::Dot => {
|
// all cases where `bytes` is empty is an error
|
||||||
if bytes.len() != 1 || bytes[0] != b'.' {
|
let Some((&first, rest)) = bytes.split_first() else {
|
||||||
working_set.error(ParseError::Expected(".", path_element.span));
|
working_set.error(ParseError::Expected("string", path_element.span));
|
||||||
return tail;
|
return tail;
|
||||||
}
|
};
|
||||||
expected_token = TokenType::PathMember;
|
let single_char = rest.is_empty();
|
||||||
}
|
|
||||||
TokenType::QuestionOrDot => {
|
|
||||||
if bytes.len() == 1 && bytes[0] == b'.' {
|
|
||||||
expected_token = TokenType::PathMember;
|
|
||||||
} else if bytes.len() == 1 && bytes[0] == b'?' {
|
|
||||||
if let Some(last) = tail.last_mut() {
|
|
||||||
match last {
|
|
||||||
PathMember::String {
|
|
||||||
ref mut optional, ..
|
|
||||||
} => *optional = true,
|
|
||||||
PathMember::Int {
|
|
||||||
ref mut optional, ..
|
|
||||||
} => *optional = true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expected_token = TokenType::Dot;
|
|
||||||
} else {
|
|
||||||
working_set.error(ParseError::Expected(". or ?", path_element.span));
|
|
||||||
return tail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TokenType::PathMember => {
|
|
||||||
let starting_error_count = working_set.parse_errors.len();
|
|
||||||
|
|
||||||
let expr = parse_int(working_set, path_element.span);
|
if let TokenType::PathMember = expected_token {
|
||||||
working_set.parse_errors.truncate(starting_error_count);
|
let starting_error_count = working_set.parse_errors.len();
|
||||||
|
|
||||||
match expr {
|
let expr = parse_int(working_set, path_element.span);
|
||||||
Expression {
|
working_set.parse_errors.truncate(starting_error_count);
|
||||||
expr: Expr::Int(val),
|
|
||||||
span,
|
match expr {
|
||||||
..
|
Expression {
|
||||||
} => tail.push(PathMember::Int {
|
expr: Expr::Int(val),
|
||||||
val: val as usize,
|
span,
|
||||||
span,
|
..
|
||||||
optional: false,
|
} => tail.push(PathMember::Int {
|
||||||
}),
|
val: val as usize,
|
||||||
_ => {
|
span,
|
||||||
let result = parse_string(working_set, path_element.span);
|
optional: false,
|
||||||
match result {
|
}),
|
||||||
Expression {
|
_ => {
|
||||||
expr: Expr::String(string),
|
let result = parse_string(working_set, path_element.span);
|
||||||
|
match result {
|
||||||
|
Expression {
|
||||||
|
expr: Expr::String(string),
|
||||||
|
span,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
tail.push(PathMember::String {
|
||||||
|
val: string,
|
||||||
span,
|
span,
|
||||||
..
|
optional: false,
|
||||||
} => {
|
insensitive: false,
|
||||||
tail.push(PathMember::String {
|
});
|
||||||
val: string,
|
}
|
||||||
span,
|
_ => {
|
||||||
optional: false,
|
working_set.error(ParseError::Expected("string", path_element.span));
|
||||||
});
|
return tail;
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
working_set
|
|
||||||
.error(ParseError::Expected("string", path_element.span));
|
|
||||||
return tail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expected_token = TokenType::QuestionOrDot;
|
}
|
||||||
|
expected_token = TokenType::DotOrSign;
|
||||||
|
} else {
|
||||||
|
match expected_token.expect(if single_char { first } else { b' ' }) {
|
||||||
|
Ok(modify) => {
|
||||||
|
if let Some(last) = tail.last_mut() {
|
||||||
|
match modify {
|
||||||
|
ModifyMember::No => {}
|
||||||
|
ModifyMember::Optional => last.make_optional(),
|
||||||
|
ModifyMember::Insensitive => last.make_insensitive(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(expected) => {
|
||||||
|
working_set.error(ParseError::Expected(expected, path_element.span));
|
||||||
|
return tail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2391,7 +2432,13 @@ pub fn parse_cell_path(
|
|||||||
pub fn parse_simple_cell_path(working_set: &mut StateWorkingSet, span: Span) -> Expression {
|
pub fn parse_simple_cell_path(working_set: &mut StateWorkingSet, span: Span) -> Expression {
|
||||||
let source = working_set.get_span_contents(span);
|
let source = working_set.get_span_contents(span);
|
||||||
|
|
||||||
let (tokens, err) = lex(source, span.start, &[b'\n', b'\r'], &[b'.', b'?'], true);
|
let (tokens, err) = lex(
|
||||||
|
source,
|
||||||
|
span.start,
|
||||||
|
&[b'\n', b'\r'],
|
||||||
|
&[b'.', b'?', b'!'],
|
||||||
|
true,
|
||||||
|
);
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
working_set.error(err)
|
working_set.error(err)
|
||||||
}
|
}
|
||||||
@ -2417,7 +2464,13 @@ pub fn parse_full_cell_path(
|
|||||||
let full_cell_span = span;
|
let full_cell_span = span;
|
||||||
let source = working_set.get_span_contents(span);
|
let source = working_set.get_span_contents(span);
|
||||||
|
|
||||||
let (tokens, err) = lex(source, span.start, &[b'\n', b'\r'], &[b'.', b'?'], true);
|
let (tokens, err) = lex(
|
||||||
|
source,
|
||||||
|
span.start,
|
||||||
|
&[b'\n', b'\r'],
|
||||||
|
&[b'.', b'?', b'!'],
|
||||||
|
true,
|
||||||
|
);
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
working_set.error(err)
|
working_set.error(err)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user