///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, 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, usize, Option) { 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) -> Option { 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) -> Option { if name.len() != 1 { Some(ParseError::mismatch( "Shortflag of exactly 1 character", actual, )) } else { None } } } pub(crate) fn parse_flag_name(token: &Token) -> (Spanned, Option) { 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, Option) { 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) { 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 { 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)) } }