use crate::hir::syntax_shape::{ AnyExpressionShape, BareShape, ExpandSyntax, FlatShape, IntShape, ParseError, StringShape, WhitespaceShape, }; use crate::hir::{Expression, SpannedExpression, TokensIterator}; use crate::parse::token_tree::{CompareOperatorType, DotDotType, DotType, ItVarType, VarType}; use crate::{hir, CompareOperator}; use nu_protocol::{PathMember, ShellTypeName}; use nu_source::{ b, DebugDocBuilder, HasSpan, PrettyDebug, PrettyDebugWithSource, Span, Spanned, SpannedItem, Tag, Tagged, TaggedItem, Text, }; use num_bigint::BigInt; use serde::{Deserialize, Serialize}; use std::str::FromStr; #[derive(Debug, Copy, Clone)] pub struct VariablePathShape; impl ExpandSyntax for VariablePathShape { type Output = Result; fn name(&self) -> &'static str { "variable path" } fn expand<'a, 'b>( &self, token_nodes: &mut TokensIterator<'_>, ) -> Result { // 1. let the head be the first token, expecting a variable // 2. let the tail be an empty list of members // 2. while the next token (excluding ws) is a dot: // 1. consume the dot // 2. consume the next token as a member and push it onto tail let head = token_nodes.expand_syntax(VariableShape)?; let start = head.span; let mut end = start; let mut tail: Vec = vec![]; loop { if token_nodes.expand_syntax(DotShape).is_err() { break; } let member = token_nodes.expand_syntax(MemberShape)?; let member = member.to_path_member(&token_nodes.source()); end = member.span; tail.push(member); } Ok(Expression::path(head, tail).into_expr(start.until(end))) } } #[derive(Debug, Copy, Clone)] pub struct PathTailShape; #[derive(Debug, Clone)] pub struct PathTailSyntax { pub tail: Vec, pub span: Span, } impl HasSpan for PathTailSyntax { fn span(&self) -> Span { self.span } } impl PrettyDebug for PathTailSyntax { fn pretty(&self) -> DebugDocBuilder { b::typed("tail", b::intersperse(self.tail.iter(), b::space())) } } impl ExpandSyntax for PathTailShape { type Output = Result; fn name(&self) -> &'static str { "path continuation" } fn expand<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, ) -> Result { let mut end: Option = None; let mut tail: Vec = vec![]; loop { if token_nodes.expand_syntax(DotShape).is_err() { break; } let member = token_nodes.expand_syntax(MemberShape)?; let member = member.to_path_member(&token_nodes.source()); end = Some(member.span); tail.push(member); } match end { None => Err(token_nodes.err_next_token("path continuation")), Some(end) => Ok(PathTailSyntax { tail, span: end }), } } } #[derive(Debug, Clone)] pub struct ContinuationSyntax { kind: ContinuationSyntaxKind, span: Span, } impl ContinuationSyntax { pub fn append_to(self, expr: SpannedExpression) -> SpannedExpression { match self.kind { ContinuationSyntaxKind::Infix(op, right) => { let span = expr.span.until(right.span); Expression::infix(expr, op, right).into_expr(span) } ContinuationSyntaxKind::Dot(_, member) => { let span = expr.span.until(member.span); Expression::dot_member(expr, member).into_expr(span) } ContinuationSyntaxKind::DotDot(_, right) => { let span = expr.span.until(right.span); Expression::range(expr, span, right).into_expr(span) } } } } impl HasSpan for ContinuationSyntax { fn span(&self) -> Span { self.span } } impl PrettyDebugWithSource for ContinuationSyntax { fn pretty_debug(&self, source: &str) -> DebugDocBuilder { b::typed("continuation", self.kind.pretty_debug(source)) } } #[derive(Debug, Clone)] pub enum ContinuationSyntaxKind { Infix(Spanned, SpannedExpression), Dot(Span, PathMember), DotDot(Span, SpannedExpression), } impl PrettyDebugWithSource for ContinuationSyntaxKind { fn pretty_debug(&self, source: &str) -> DebugDocBuilder { match self { ContinuationSyntaxKind::Infix(op, expr) => { b::operator(op.span.slice(source)) + expr.pretty_debug(source) } ContinuationSyntaxKind::Dot(span, member) => { b::operator(span.slice(source)) + member.pretty_debug(source) } ContinuationSyntaxKind::DotDot(span, expr) => { b::operator(span.slice(source)) + expr.pretty_debug(source) } } } } /// An expression continuation #[derive(Debug, Copy, Clone)] pub struct ExpressionContinuationShape; impl ExpandSyntax for ExpressionContinuationShape { type Output = Result; fn name(&self) -> &'static str { "expression continuation" } fn expand<'a, 'b>( &self, token_nodes: &mut TokensIterator<'_>, ) -> Result { token_nodes.atomic_parse(|token_nodes| { // Try to expand a `.` let dot = token_nodes.expand_syntax(DotShape); if let Ok(dot) = dot { // If a `.` was matched, it's a `Path`, and we expect a `Member` next let syntax = token_nodes.expand_syntax(MemberShape)?; let member = syntax.to_path_member(&token_nodes.source()); let member_span = member.span; return Ok(ContinuationSyntax { kind: ContinuationSyntaxKind::Dot(dot, member), span: dot.until(member_span), }); } // Try to expand a `..` let dot = token_nodes.expand_syntax(DotDotShape); if let Ok(dotdot) = dot { // If a `..` was matched, it's a `Range`, and we expect an `Expression` next let expr = token_nodes.expand_syntax(AnyExpressionShape)?; let expr_span = expr.span; return Ok(ContinuationSyntax { kind: ContinuationSyntaxKind::DotDot(dotdot, expr), span: dotdot.until(expr_span), }); } // Otherwise, we expect an infix operator and an expression next let (_, op, _) = token_nodes.expand_syntax(InfixShape)?.infix.item; let next = token_nodes.expand_syntax(AnyExpressionShape)?; let next_span = next.span; Ok(ContinuationSyntax { kind: ContinuationSyntaxKind::Infix(op.operator, next), span: op.operator.span.until(next_span), }) }) } } #[derive(Debug, Copy, Clone)] pub struct VariableShape; impl ExpandSyntax for VariableShape { type Output = Result; fn name(&self) -> &'static str { "variable" } fn expand<'a, 'b>( &self, token_nodes: &mut TokensIterator<'_>, ) -> Result { token_nodes .expand_token(ItVarType, |(inner, outer)| { Ok(( FlatShape::ItVariable, Expression::it_variable(inner).into_expr(outer), )) }) .or_else(|_| { token_nodes.expand_token(VarType, |(inner, outer)| { Ok(( FlatShape::Variable, Expression::variable(inner).into_expr(outer), )) }) }) } } #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Member { String(/* outer */ Span, /* inner */ Span), Int(BigInt, Span), Bare(Span), } impl ShellTypeName for Member { fn type_name(&self) -> &'static str { match self { Member::String(_, _) => "string", Member::Int(_, _) => "integer", Member::Bare(_) => "word", } } } impl Member { pub fn int(span: Span, source: &Text) -> Member { if let Ok(big_int) = BigInt::from_str(span.slice(source)) { Member::Int(big_int, span) } else { unreachable!("Internal error: could not convert text to BigInt as expected") } } pub fn to_path_member(&self, source: &Text) -> PathMember { match self { Member::String(outer, inner) => PathMember::string(inner.slice(source), *outer), Member::Int(int, span) => PathMember::int(int.clone(), *span), Member::Bare(span) => PathMember::string(span.slice(source), *span), } } } impl PrettyDebugWithSource for Member { fn pretty_debug(&self, source: &str) -> DebugDocBuilder { match self { Member::String(outer, _) => b::value(outer.slice(source)), Member::Int(int, _) => b::value(format!("{}", int)), Member::Bare(span) => b::value(span.slice(source)), } } } impl HasSpan for Member { fn span(&self) -> Span { match self { Member::String(outer, ..) => *outer, Member::Int(_, int) => *int, Member::Bare(name) => *name, } } } impl Member { pub fn to_expr(&self) -> hir::SpannedExpression { match self { Member::String(outer, inner) => Expression::string(*inner).into_expr(outer), Member::Int(number, span) => Expression::number(number.clone()).into_expr(span), Member::Bare(span) => Expression::string(*span).into_expr(span), } } pub(crate) fn span(&self) -> Span { match self { Member::String(outer, _inner) => *outer, Member::Int(_, span) => *span, Member::Bare(span) => *span, } } } enum ColumnPathState { Initial, LeadingDot(Span), Dot(Span, Vec, Span), Member(Span, Vec), Error(ParseError), } impl ColumnPathState { pub fn dot(self, dot: Span) -> ColumnPathState { match self { ColumnPathState::Initial => ColumnPathState::LeadingDot(dot), ColumnPathState::LeadingDot(_) => { ColumnPathState::Error(ParseError::mismatch("column", "dot".spanned(dot))) } ColumnPathState::Dot(..) => { ColumnPathState::Error(ParseError::mismatch("column", "dot".spanned(dot))) } ColumnPathState::Member(tag, members) => ColumnPathState::Dot(tag, members, dot), ColumnPathState::Error(err) => ColumnPathState::Error(err), } } pub fn member(self, member: Member) -> ColumnPathState { match self { ColumnPathState::Initial => ColumnPathState::Member(member.span(), vec![member]), ColumnPathState::LeadingDot(tag) => { ColumnPathState::Member(tag.until(member.span()), vec![member]) } ColumnPathState::Dot(tag, mut tags, _) => { ColumnPathState::Member(tag.until(member.span()), { tags.push(member); tags }) } ColumnPathState::Member(..) => ColumnPathState::Error(ParseError::mismatch( "column", member.type_name().spanned(member.span()), )), ColumnPathState::Error(err) => ColumnPathState::Error(err), } } pub fn into_path(self, err: ParseError) -> Result>, ParseError> { match self { ColumnPathState::Initial => Err(err), ColumnPathState::LeadingDot(dot) => { Err(ParseError::mismatch("column", "dot".spanned(dot))) } ColumnPathState::Dot(_tag, _members, dot) => { Err(ParseError::mismatch("column", "dot".spanned(dot))) } ColumnPathState::Member(tag, tags) => Ok(tags.tagged(tag)), ColumnPathState::Error(err) => Err(err), } } } #[derive(Debug, Copy, Clone)] pub struct ColumnPathShape; impl ExpandSyntax for ColumnPathShape { type Output = Result; fn name(&self) -> &'static str { "column path" } fn expand<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, ) -> Result { let mut state = ColumnPathState::Initial; loop { let member = token_nodes.expand_syntax(MemberShape); match member { Err(_) => break, Ok(member) => state = state.member(member), } let dot = token_nodes.expand_syntax(DotShape); match dot { Err(_) => break, Ok(dot) => state = state.dot(dot), } } let path = state.into_path(token_nodes.err_next_token("column path"))?; Ok(ColumnPathSyntax { path: path.item, tag: path.tag, }) } } #[derive(Debug, Clone)] pub struct ColumnPathSyntax { pub path: Vec, pub tag: Tag, } impl HasSpan for ColumnPathSyntax { fn span(&self) -> Span { self.tag.span } } impl PrettyDebugWithSource for ColumnPathSyntax { fn pretty_debug(&self, source: &str) -> DebugDocBuilder { b::typed( "column path", b::intersperse( self.path.iter().map(|member| member.pretty_debug(source)), b::space(), ), ) } } #[derive(Debug, Copy, Clone)] pub struct MemberShape; impl ExpandSyntax for MemberShape { type Output = Result; fn name(&self) -> &'static str { "column" } fn expand<'a, 'b>(&self, token_nodes: &mut TokensIterator<'_>) -> Result { if let Ok(int) = token_nodes.expand_syntax(IntMemberShape) { return Ok(int); } let bare = token_nodes.expand_syntax(BareShape); if let Ok(bare) = bare { return Ok(Member::Bare(bare.span())); } let string = token_nodes.expand_syntax(StringShape); if let Ok(syntax) = string { return Ok(Member::String(syntax.span, syntax.inner)); } Err(token_nodes.peek().type_error("column")) } } #[derive(Debug, Copy, Clone)] struct IntMemberShape; impl ExpandSyntax for IntMemberShape { type Output = Result; fn name(&self) -> &'static str { "integer member" } fn expand<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, ) -> Result { token_nodes .expand_syntax(IntShape) .map(|int| Member::int(int.span(), &token_nodes.source())) .or_else(|_| Err(token_nodes.err_next_token("integer member"))) } } #[derive(Debug, Copy, Clone)] pub struct DotShape; #[derive(Debug, Copy, Clone)] pub struct ColorableDotShape; impl ExpandSyntax for DotShape { type Output = Result; fn name(&self) -> &'static str { "dot" } fn expand<'a, 'b>(&self, token_nodes: &'b mut TokensIterator<'a>) -> Result { token_nodes.expand_token(DotType, |token| Ok((FlatShape::Dot, token.span()))) } } #[derive(Debug, Copy, Clone)] struct DotDotShape; impl ExpandSyntax for DotDotShape { type Output = Result; fn name(&self) -> &'static str { "dotdot" } fn expand<'a, 'b>(&self, token_nodes: &'b mut TokensIterator<'a>) -> Result { token_nodes.expand_token(DotDotType, |token| Ok((FlatShape::DotDot, token.span()))) } } #[derive(Debug, Copy, Clone)] pub struct InfixShape; #[derive(Debug, Clone)] pub struct InfixSyntax { infix: Spanned<(Span, InfixInnerSyntax, Span)>, } impl HasSpan for InfixSyntax { fn span(&self) -> Span { self.infix.span } } impl PrettyDebugWithSource for InfixSyntax { fn pretty_debug(&self, source: &str) -> DebugDocBuilder { self.infix.1.pretty_debug(source) } } impl ExpandSyntax for InfixShape { type Output = Result; fn name(&self) -> &'static str { "infix operator" } fn expand<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, ) -> Result { token_nodes.atomic_parse(|token_nodes| { // An infix operator must be prefixed by whitespace let start = token_nodes.expand_syntax(WhitespaceShape)?; // Parse the next TokenNode after the whitespace let operator = token_nodes.expand_syntax(InfixInnerShape)?; // An infix operator must be followed by whitespace let end = token_nodes.expand_syntax(WhitespaceShape)?; Ok(InfixSyntax { infix: (start, operator, end).spanned(start.until(end)), }) }) } } #[derive(Debug, Clone)] pub struct InfixInnerSyntax { pub operator: Spanned, } impl HasSpan for InfixInnerSyntax { fn span(&self) -> Span { self.operator.span } } impl PrettyDebug for InfixInnerSyntax { fn pretty(&self) -> DebugDocBuilder { self.operator.pretty() } } #[derive(Debug, Copy, Clone)] pub struct InfixInnerShape; impl ExpandSyntax for InfixInnerShape { type Output = Result; fn name(&self) -> &'static str { "infix inner" } fn expand<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, ) -> Result { token_nodes.expand_token(CompareOperatorType, |(span, operator)| { Ok(( FlatShape::CompareOperator, InfixInnerSyntax { operator: operator.spanned(span), }, )) }) } }