Files
nushell/crates/nu-parser/src/hir/syntax_shape/expression/variable_path.rs
Andrés N. Robalino b36d21e76f Infer types from regular delimited plain text unstructured files. (#1494)
* Infer types from regular delimited plain text unstructured files.

* Nothing resolves to an empty string.
2020-03-16 15:50:45 -05:00

633 lines
18 KiB
Rust

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<SpannedExpression, ParseError>;
fn name(&self) -> &'static str {
"variable path"
}
fn expand<'a, 'b>(
&self,
token_nodes: &mut TokensIterator<'_>,
) -> Result<SpannedExpression, ParseError> {
// 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<PathMember> = 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<PathMember>,
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<PathTailSyntax, ParseError>;
fn name(&self) -> &'static str {
"path continuation"
}
fn expand<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
) -> Result<PathTailSyntax, ParseError> {
let mut end: Option<Span> = None;
let mut tail: Vec<PathMember> = 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<CompareOperator>, 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<ContinuationSyntax, ParseError>;
fn name(&self) -> &'static str {
"expression continuation"
}
fn expand<'a, 'b>(
&self,
token_nodes: &mut TokensIterator<'_>,
) -> Result<ContinuationSyntax, ParseError> {
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<SpannedExpression, ParseError>;
fn name(&self) -> &'static str {
"variable"
}
fn expand<'a, 'b>(
&self,
token_nodes: &mut TokensIterator<'_>,
) -> Result<SpannedExpression, ParseError> {
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<Member>, Span),
Member(Span, Vec<Member>),
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<Tagged<Vec<Member>>, 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<ColumnPathSyntax, ParseError>;
fn name(&self) -> &'static str {
"column path"
}
fn expand<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
) -> Result<ColumnPathSyntax, ParseError> {
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<Member>,
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<Member, ParseError>;
fn name(&self) -> &'static str {
"column"
}
fn expand<'a, 'b>(&self, token_nodes: &mut TokensIterator<'_>) -> Result<Member, ParseError> {
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<Member, ParseError>;
fn name(&self) -> &'static str {
"integer member"
}
fn expand<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
) -> Result<Member, ParseError> {
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<Span, ParseError>;
fn name(&self) -> &'static str {
"dot"
}
fn expand<'a, 'b>(&self, token_nodes: &'b mut TokensIterator<'a>) -> Result<Span, ParseError> {
token_nodes.expand_token(DotType, |token| Ok((FlatShape::Dot, token.span())))
}
}
#[derive(Debug, Copy, Clone)]
struct DotDotShape;
impl ExpandSyntax for DotDotShape {
type Output = Result<Span, ParseError>;
fn name(&self) -> &'static str {
"dotdot"
}
fn expand<'a, 'b>(&self, token_nodes: &'b mut TokensIterator<'a>) -> Result<Span, ParseError> {
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<InfixSyntax, ParseError>;
fn name(&self) -> &'static str {
"infix operator"
}
fn expand<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
) -> Result<InfixSyntax, ParseError> {
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<CompareOperator>,
}
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<InfixInnerSyntax, ParseError>;
fn name(&self) -> &'static str {
"infix inner"
}
fn expand<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
) -> Result<InfixInnerSyntax, ParseError> {
token_nodes.expand_token(CompareOperatorType, |(span, operator)| {
Ok((
FlatShape::CompareOperator,
InfixInnerSyntax {
operator: operator.spanned(span),
},
))
})
}
}