mirror of
https://github.com/nushell/nushell.git
synced 2025-07-08 02:17:22 +02:00
* Infer types from regular delimited plain text unstructured files. * Nothing resolves to an empty string.
633 lines
18 KiB
Rust
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),
|
|
},
|
|
))
|
|
})
|
|
}
|
|
}
|