mirror of
https://github.com/nushell/nushell.git
synced 2025-05-30 22:57:07 +02:00
208 lines
6.5 KiB
Rust
208 lines
6.5 KiB
Rust
///All of the functions in this mod parse only 1 Token per invocation.
|
|
///Therefore they are primitives
|
|
use crate::lex::{lexer::Token, tokens::TokenContents};
|
|
use crate::parse::util::token_to_spanned_string;
|
|
use nu_errors::ParseError;
|
|
use nu_protocol::SyntaxShape;
|
|
use nu_source::{Span, Spanned, SpannedItem};
|
|
|
|
///Helper function
|
|
pub(crate) fn is_baseline_token_matching(token: &Token, string: &str) -> bool {
|
|
match &token.contents {
|
|
TokenContents::Baseline(base) => base == string,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_comma(tokens: &[Token]) -> (bool, usize) {
|
|
fn is_comma(token: &Token) -> bool {
|
|
is_baseline_token_matching(token, ",")
|
|
}
|
|
if !tokens.is_empty() && is_comma(&tokens[0]) {
|
|
(true, 1)
|
|
} else {
|
|
(false, 0)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_eol(tokens: &[Token]) -> (bool, usize) {
|
|
if !tokens.is_empty() && tokens[0].contents.is_eol() {
|
|
(true, 1)
|
|
} else {
|
|
(false, 0)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_optional_comment(tokens: &[Token]) -> (Option<String>, usize) {
|
|
let mut comment_text = None;
|
|
let mut i: usize = 0;
|
|
if i < tokens.len() {
|
|
if let TokenContents::Comment(comment) = &tokens[i].contents {
|
|
comment_text = Some(comment.trim().to_string());
|
|
i += 1;
|
|
}
|
|
}
|
|
(comment_text, i)
|
|
}
|
|
|
|
///Returns true if token is optional modifier
|
|
pub(crate) fn parse_optional_parameter_optional_modifier(token: &Token) -> (bool, usize) {
|
|
if is_baseline_token_matching(token, "?") {
|
|
(true, 1)
|
|
} else {
|
|
(false, 0)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_flag_optional_shortform(
|
|
tokens: &[Token],
|
|
) -> (Option<char>, usize, Option<ParseError>) {
|
|
if tokens.is_empty() {
|
|
return (None, 0, None);
|
|
}
|
|
|
|
let token = &tokens[0];
|
|
return if let TokenContents::Baseline(shortform) = &token.contents {
|
|
let mut chars = shortform.chars();
|
|
match (chars.next(), chars.next_back()) {
|
|
(Some('('), Some(')')) => {
|
|
let mut err = None;
|
|
|
|
let flag_span = Span::new(
|
|
token.span.start() + 1, //Skip '('
|
|
token.span.end() - 1, // Skip ')'
|
|
);
|
|
|
|
let c: String = chars.collect();
|
|
let dash_count = c.chars().take_while(|c| *c == '-').count();
|
|
err = err
|
|
.or_else(|| err_on_too_many_dashes(dash_count, c.clone().spanned(flag_span)));
|
|
let name = &c[dash_count..];
|
|
err = err.or_else(|| err_on_name_too_long(name, c.clone().spanned(flag_span)));
|
|
let c = name.chars().next();
|
|
|
|
(c, 1, err)
|
|
}
|
|
_ => (None, 0, None),
|
|
}
|
|
} else {
|
|
(None, 0, None)
|
|
};
|
|
|
|
fn err_on_too_many_dashes(dash_count: usize, actual: Spanned<String>) -> Option<ParseError> {
|
|
match dash_count {
|
|
0 => {
|
|
//If no starting -
|
|
Some(ParseError::mismatch("Shortflag starting with '-'", actual))
|
|
}
|
|
1 => None,
|
|
_ => {
|
|
//If --
|
|
Some(ParseError::mismatch(
|
|
"Shortflag starting with a single '-'",
|
|
actual,
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn err_on_name_too_long(name: &str, actual: Spanned<String>) -> Option<ParseError> {
|
|
if name.len() != 1 {
|
|
Some(ParseError::mismatch(
|
|
"Shortflag of exactly 1 character",
|
|
actual,
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_flag_name(token: &Token) -> (Spanned<String>, Option<ParseError>) {
|
|
if let TokenContents::Baseline(name) = &token.contents {
|
|
if !name.starts_with("--") {
|
|
(
|
|
name.clone().spanned(token.span),
|
|
Some(ParseError::mismatch(
|
|
"longform of a flag (Starting with --)",
|
|
token_to_spanned_string(token),
|
|
)),
|
|
)
|
|
} else {
|
|
//Discard preceding --
|
|
let name = name[2..].to_string();
|
|
(name.spanned(token.span), None)
|
|
}
|
|
} else {
|
|
(
|
|
"".to_string().spanned_unknown(),
|
|
Some(ParseError::mismatch(
|
|
"longform of a flag (Starting with --)",
|
|
token_to_spanned_string(token),
|
|
)),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_param_name(token: &Token) -> (Spanned<String>, Option<ParseError>) {
|
|
if let TokenContents::Baseline(name) = &token.contents {
|
|
let name = name.clone().spanned(token.span);
|
|
(name, None)
|
|
} else {
|
|
(
|
|
"InternalError".to_string().spanned(token.span),
|
|
Some(ParseError::mismatch(
|
|
"parameter name",
|
|
token_to_spanned_string(token),
|
|
)),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub fn parse_type_token(type_: &Token) -> (SyntaxShape, Option<ParseError>) {
|
|
match &type_.contents {
|
|
TokenContents::Baseline(type_str) => match type_str.as_str() {
|
|
"int" => (SyntaxShape::Int, None),
|
|
"string" => (SyntaxShape::String, None),
|
|
"path" => (SyntaxShape::FilePath, None),
|
|
"table" => (SyntaxShape::Table, None),
|
|
"duration" => (SyntaxShape::Duration, None),
|
|
"filesize" => (SyntaxShape::Filesize, None),
|
|
"number" => (SyntaxShape::Number, None),
|
|
"pattern" => (SyntaxShape::GlobPattern, None),
|
|
"range" => (SyntaxShape::Range, None),
|
|
"block" => (SyntaxShape::Block, None),
|
|
"any" => (SyntaxShape::Any, None),
|
|
_ => (
|
|
SyntaxShape::Any,
|
|
Some(ParseError::mismatch("type", token_to_spanned_string(type_))),
|
|
),
|
|
},
|
|
_ => (
|
|
SyntaxShape::Any,
|
|
Some(ParseError::mismatch("type", token_to_spanned_string(type_))),
|
|
),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn parse_rest_name(name_token: &Token) -> Option<ParseError> {
|
|
return if let TokenContents::Baseline(name) = &name_token.contents {
|
|
if !name.starts_with("...") {
|
|
Some(parse_rest_name_err(name_token))
|
|
} else if !name.starts_with("...rest") {
|
|
Some(ParseError::mismatch(
|
|
"rest argument name to be 'rest'",
|
|
token_to_spanned_string(name_token),
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
Some(parse_rest_name_err(name_token))
|
|
};
|
|
|
|
fn parse_rest_name_err(token: &Token) -> ParseError {
|
|
ParseError::mismatch("...rest", token_to_spanned_string(token))
|
|
}
|
|
}
|