mirror of
https://github.com/nushell/nushell.git
synced 2025-08-06 12:10:16 +02:00
* Clippy fixes * Finish converting to use clippy * fix warnings in new master * fix windows * fix windows Co-authored-by: Artem Vorotnikov <artem@vorotnikov.me>
448 lines
13 KiB
Rust
448 lines
13 KiB
Rust
use crate::parse::{call_node::*, comment::*, flag::*, operator::*, pipeline::*, tokens::*};
|
|
use derive_new::new;
|
|
use getset::Getters;
|
|
use nu_errors::{ParseError, ShellError};
|
|
use nu_protocol::ShellTypeName;
|
|
use nu_source::{
|
|
b, DebugDocBuilder, HasSpan, PrettyDebugWithSource, Span, Spanned, SpannedItem, Tagged,
|
|
TaggedItem, Text,
|
|
};
|
|
use std::fmt;
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
|
pub enum TokenNode {
|
|
Token(Token),
|
|
|
|
Call(Spanned<CallNode>),
|
|
Nodes(Spanned<Vec<TokenNode>>),
|
|
Delimited(Spanned<DelimitedNode>),
|
|
Pipeline(Pipeline),
|
|
Flag(Flag),
|
|
Comment(Comment),
|
|
Whitespace(Span),
|
|
Separator(Span),
|
|
|
|
Error(Spanned<ShellError>),
|
|
}
|
|
|
|
impl PrettyDebugWithSource for TokenNode {
|
|
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
|
match self {
|
|
TokenNode::Token(token) => token.pretty_debug(source),
|
|
TokenNode::Call(call) => call.pretty_debug(source),
|
|
TokenNode::Nodes(nodes) => b::intersperse(
|
|
nodes.iter().map(|node| node.pretty_debug(source)),
|
|
b::space(),
|
|
),
|
|
TokenNode::Delimited(delimited) => delimited.pretty_debug(source),
|
|
TokenNode::Pipeline(pipeline) => pipeline.pretty_debug(source),
|
|
TokenNode::Flag(flag) => flag.pretty_debug(source),
|
|
TokenNode::Whitespace(space) => b::typed(
|
|
"whitespace",
|
|
b::description(format!("{:?}", space.slice(source))),
|
|
),
|
|
TokenNode::Separator(span) => b::typed(
|
|
"separator",
|
|
b::description(format!("{:?}", span.slice(source))),
|
|
),
|
|
TokenNode::Comment(comment) => {
|
|
b::typed("comment", b::description(comment.text.slice(source)))
|
|
}
|
|
TokenNode::Error(_) => b::error("error"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ShellTypeName for TokenNode {
|
|
fn type_name(&self) -> &'static str {
|
|
match self {
|
|
TokenNode::Token(t) => t.type_name(),
|
|
TokenNode::Nodes(_) => "nodes",
|
|
TokenNode::Call(_) => "command",
|
|
TokenNode::Delimited(d) => d.type_name(),
|
|
TokenNode::Pipeline(_) => "pipeline",
|
|
TokenNode::Flag(_) => "flag",
|
|
TokenNode::Whitespace(_) => "whitespace",
|
|
TokenNode::Separator(_) => "separator",
|
|
TokenNode::Comment(_) => "comment",
|
|
TokenNode::Error(_) => "error",
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct DebugTokenNode<'a> {
|
|
node: &'a TokenNode,
|
|
source: &'a Text,
|
|
}
|
|
|
|
impl fmt::Debug for DebugTokenNode<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match self.node {
|
|
TokenNode::Token(t) => write!(f, "{:?}", t.debug(self.source)),
|
|
TokenNode::Call(s) => {
|
|
write!(f, "(")?;
|
|
|
|
write!(f, "{}", s.head().debug(self.source))?;
|
|
|
|
if let Some(children) = s.children() {
|
|
for child in children {
|
|
write!(f, "{}", child.debug(self.source))?;
|
|
}
|
|
}
|
|
|
|
write!(f, ")")
|
|
}
|
|
|
|
TokenNode::Delimited(d) => {
|
|
write!(
|
|
f,
|
|
"{}",
|
|
match d.delimiter {
|
|
Delimiter::Brace => "{",
|
|
Delimiter::Paren => "(",
|
|
Delimiter::Square => "[",
|
|
}
|
|
)?;
|
|
|
|
for child in d.children() {
|
|
write!(f, "{:?}", child.old_debug(self.source))?;
|
|
}
|
|
|
|
write!(
|
|
f,
|
|
"{}",
|
|
match d.delimiter {
|
|
Delimiter::Brace => "}",
|
|
Delimiter::Paren => ")",
|
|
Delimiter::Square => "]",
|
|
}
|
|
)
|
|
}
|
|
TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)),
|
|
TokenNode::Error(_) => write!(f, "<error>"),
|
|
rest => write!(f, "{}", rest.span().slice(self.source)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&TokenNode> for Span {
|
|
fn from(token: &TokenNode) -> Span {
|
|
token.span()
|
|
}
|
|
}
|
|
|
|
impl HasSpan for TokenNode {
|
|
fn span(&self) -> Span {
|
|
match self {
|
|
TokenNode::Token(t) => t.span,
|
|
TokenNode::Nodes(t) => t.span,
|
|
TokenNode::Call(s) => s.span,
|
|
TokenNode::Delimited(s) => s.span,
|
|
TokenNode::Pipeline(s) => s.span,
|
|
TokenNode::Flag(s) => s.span,
|
|
TokenNode::Whitespace(s) => *s,
|
|
TokenNode::Separator(s) => *s,
|
|
TokenNode::Comment(c) => c.span(),
|
|
TokenNode::Error(s) => s.span,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TokenNode {
|
|
pub fn tagged_type_name(&self) -> Tagged<&'static str> {
|
|
self.type_name().tagged(self.span())
|
|
}
|
|
|
|
pub fn old_debug<'a>(&'a self, source: &'a Text) -> DebugTokenNode<'a> {
|
|
DebugTokenNode { node: self, source }
|
|
}
|
|
|
|
pub fn as_external_arg(&self, source: &Text) -> String {
|
|
self.span().slice(source).to_string()
|
|
}
|
|
|
|
pub fn source<'a>(&self, source: &'a Text) -> &'a str {
|
|
self.span().slice(source)
|
|
}
|
|
|
|
pub fn get_variable(&self) -> Result<(Span, Span), ShellError> {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::Variable(inner_span),
|
|
span: outer_span,
|
|
}) => Ok((*outer_span, *inner_span)),
|
|
_ => Err(ShellError::type_error(
|
|
"variable",
|
|
self.type_name().spanned(self.span()),
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub fn is_bare(&self) -> bool {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::Bare,
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_string(&self) -> bool {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::String(_),
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_number(&self) -> bool {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::Number(_),
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn as_string(&self) -> Option<(Span, Span)> {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::String(inner_span),
|
|
span: outer_span,
|
|
}) => Some((*outer_span, *inner_span)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn is_pattern(&self) -> bool {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::GlobPattern,
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_word(&self) -> bool {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::Bare,
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_int(&self) -> bool {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::Number(RawNumber::Int(_)),
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn is_dot(&self) -> bool {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::EvaluationOperator(EvaluationOperator::Dot),
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn as_block(&self) -> Option<(Spanned<&[TokenNode]>, (Span, Span))> {
|
|
match self {
|
|
TokenNode::Delimited(Spanned {
|
|
item:
|
|
DelimitedNode {
|
|
delimiter,
|
|
children,
|
|
spans,
|
|
},
|
|
span,
|
|
}) if *delimiter == Delimiter::Brace => Some(((&children[..]).spanned(*span), *spans)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn is_external(&self) -> bool {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::ExternalCommand(..),
|
|
..
|
|
}) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn as_flag(&self, value: &str, source: &Text) -> Option<Flag> {
|
|
match self {
|
|
TokenNode::Flag(flag @ Flag { .. }) if value == flag.name().slice(source) => {
|
|
Some(*flag)
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn as_pipeline(&self) -> Result<Pipeline, ParseError> {
|
|
match self {
|
|
TokenNode::Pipeline(pipeline) => Ok(pipeline.clone()),
|
|
other => Err(ParseError::mismatch(
|
|
"pipeline",
|
|
other.type_name().spanned(other.span()),
|
|
)),
|
|
}
|
|
}
|
|
|
|
pub fn is_whitespace(&self) -> bool {
|
|
match self {
|
|
TokenNode::Whitespace(_) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)]
|
|
#[get = "pub(crate)"]
|
|
pub struct DelimitedNode {
|
|
pub(crate) delimiter: Delimiter,
|
|
pub(crate) spans: (Span, Span),
|
|
pub(crate) children: Vec<TokenNode>,
|
|
}
|
|
|
|
impl PrettyDebugWithSource for DelimitedNode {
|
|
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
|
b::delimit(
|
|
self.delimiter.open(),
|
|
b::intersperse(
|
|
self.children.iter().map(|child| child.pretty_debug(source)),
|
|
b::space(),
|
|
),
|
|
self.delimiter.close(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl DelimitedNode {
|
|
pub fn type_name(&self) -> &'static str {
|
|
match self.delimiter {
|
|
Delimiter::Brace => "braced expression",
|
|
Delimiter::Paren => "parenthesized expression",
|
|
Delimiter::Square => "array literal or index operator",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
|
pub enum Delimiter {
|
|
Paren,
|
|
Brace,
|
|
Square,
|
|
}
|
|
|
|
impl Delimiter {
|
|
pub(crate) fn open(self) -> &'static str {
|
|
match self {
|
|
Delimiter::Paren => "(",
|
|
Delimiter::Brace => "{",
|
|
Delimiter::Square => "[",
|
|
}
|
|
}
|
|
|
|
pub(crate) fn close(self) -> &'static str {
|
|
match self {
|
|
Delimiter::Paren => ")",
|
|
Delimiter::Brace => "}",
|
|
Delimiter::Square => "]",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)]
|
|
#[get = "pub(crate)"]
|
|
pub struct PathNode {
|
|
head: Box<TokenNode>,
|
|
tail: Vec<TokenNode>,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
impl TokenNode {
|
|
pub fn expect_external(&self) -> Span {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::ExternalCommand(span),
|
|
..
|
|
}) => *span,
|
|
other => panic!(
|
|
"Only call expect_external if you checked is_external first, found {:?}",
|
|
other
|
|
),
|
|
}
|
|
}
|
|
|
|
pub fn expect_string(&self) -> (Span, Span) {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::String(inner_span),
|
|
span: outer_span,
|
|
}) => (*outer_span, *inner_span),
|
|
other => panic!("Expected string, found {:?}", other),
|
|
}
|
|
}
|
|
|
|
pub fn expect_list(&self) -> Spanned<&[TokenNode]> {
|
|
match self {
|
|
TokenNode::Nodes(token_nodes) => token_nodes[..].spanned(token_nodes.span),
|
|
other => panic!("Expected list, found {:?}", other),
|
|
}
|
|
}
|
|
|
|
pub fn expect_pattern(&self) -> Span {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::GlobPattern,
|
|
span: outer_span,
|
|
}) => *outer_span,
|
|
other => panic!("Expected pattern, found {:?}", other),
|
|
}
|
|
}
|
|
|
|
pub fn expect_var(&self) -> (Span, Span) {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::Variable(inner_span),
|
|
span: outer_span,
|
|
}) => (*outer_span, *inner_span),
|
|
other => panic!("Expected var, found {:?}", other),
|
|
}
|
|
}
|
|
|
|
pub fn expect_dot(&self) -> Span {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::EvaluationOperator(EvaluationOperator::Dot),
|
|
span,
|
|
}) => *span,
|
|
other => panic!("Expected dot, found {:?}", other),
|
|
}
|
|
}
|
|
|
|
pub fn expect_bare(&self) -> Span {
|
|
match self {
|
|
TokenNode::Token(Token {
|
|
unspanned: UnspannedToken::Bare,
|
|
span,
|
|
}) => *span,
|
|
other => panic!("Expected bare, found {:?}", other),
|
|
}
|
|
}
|
|
}
|