mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 08:06:03 +02:00
Pipeline blocks (#1579)
* Making Commands match what UntaggedValue needs * WIP * WIP * WIP * Moved to expressions for conditions * Add 'each' command to use command blocks * More cleanup * Add test for 'each' * Instead use an expression block
This commit is contained in:
@ -1,945 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use derive_new::new;
|
||||
use nu_protocol::{PathMember, ShellTypeName};
|
||||
use nu_protocol::{Primitive, UntaggedValue};
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
use nu_source::{
|
||||
b, DebugDocBuilder, HasSpan, PrettyDebug, PrettyDebugRefineKind, PrettyDebugWithSource,
|
||||
};
|
||||
use nu_source::{IntoSpanned, Span, Spanned, SpannedItem};
|
||||
|
||||
use bigdecimal::BigDecimal;
|
||||
use indexmap::IndexMap;
|
||||
use log::trace;
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::identities::Zero;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||
pub struct ExternalCommand {
|
||||
pub name: Spanned<String>,
|
||||
pub args: Vec<Spanned<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Copy, Deserialize, Serialize)]
|
||||
pub enum Unit {
|
||||
// Filesize units
|
||||
Byte,
|
||||
Kilobyte,
|
||||
Megabyte,
|
||||
Gigabyte,
|
||||
Terabyte,
|
||||
Petabyte,
|
||||
|
||||
// Duration units
|
||||
Second,
|
||||
Minute,
|
||||
Hour,
|
||||
Day,
|
||||
Week,
|
||||
Month,
|
||||
Year,
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||
pub enum Member {
|
||||
String(/* outer */ Span, /* inner */ Span),
|
||||
Int(BigInt, Span),
|
||||
Bare(Spanned<String>),
|
||||
}
|
||||
|
||||
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) -> PathMember {
|
||||
match self {
|
||||
//Member::String(outer, inner) => PathMember::string(inner.slice(source), *outer),
|
||||
Member::Int(int, span) => PathMember::int(int.clone(), *span),
|
||||
Member::Bare(spanned_string) => {
|
||||
PathMember::string(spanned_string.item.clone(), spanned_string.span)
|
||||
}
|
||||
_ => unimplemented!("Need to finish to_path_member"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.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.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||
pub enum Number {
|
||||
Int(BigInt),
|
||||
Decimal(BigDecimal),
|
||||
}
|
||||
|
||||
impl PrettyDebug for Number {
|
||||
fn pretty(&self) -> DebugDocBuilder {
|
||||
match self {
|
||||
Number::Int(int) => b::primitive(int),
|
||||
Number::Decimal(decimal) => b::primitive(decimal),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! primitive_int {
|
||||
($($ty:ty)*) => {
|
||||
$(
|
||||
impl From<$ty> for Number {
|
||||
fn from(int: $ty) -> Number {
|
||||
Number::Int(BigInt::zero() + int)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&$ty> for Number {
|
||||
fn from(int: &$ty) -> Number {
|
||||
Number::Int(BigInt::zero() + *int)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
primitive_int!(i8 u8 i16 u16 i32 u32 i64 u64 i128 u128);
|
||||
|
||||
macro_rules! primitive_decimal {
|
||||
($($ty:tt -> $from:tt),*) => {
|
||||
$(
|
||||
impl From<$ty> for Number {
|
||||
fn from(decimal: $ty) -> Number {
|
||||
if let Some(num) = BigDecimal::$from(decimal) {
|
||||
Number::Decimal(num)
|
||||
} else {
|
||||
unreachable!("Internal error: BigDecimal 'from' failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&$ty> for Number {
|
||||
fn from(decimal: &$ty) -> Number {
|
||||
if let Some(num) = BigDecimal::$from(*decimal) {
|
||||
Number::Decimal(num)
|
||||
} else {
|
||||
unreachable!("Internal error: BigDecimal 'from' failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
primitive_decimal!(f32 -> from_f32, f64 -> from_f64);
|
||||
|
||||
impl std::ops::Mul for Number {
|
||||
type Output = Number;
|
||||
|
||||
fn mul(self, other: Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::Int(a), Number::Int(b)) => Number::Int(a * b),
|
||||
(Number::Int(a), Number::Decimal(b)) => Number::Decimal(BigDecimal::from(a) * b),
|
||||
(Number::Decimal(a), Number::Int(b)) => Number::Decimal(a * BigDecimal::from(b)),
|
||||
(Number::Decimal(a), Number::Decimal(b)) => Number::Decimal(a * b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For literals
|
||||
impl std::ops::Mul<u32> for Number {
|
||||
type Output = Number;
|
||||
|
||||
fn mul(self, other: u32) -> Number {
|
||||
match self {
|
||||
Number::Int(left) => Number::Int(left * (other as i64)),
|
||||
Number::Decimal(left) => Number::Decimal(left * BigDecimal::from(other)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebug for Unit {
|
||||
fn pretty(&self) -> DebugDocBuilder {
|
||||
b::keyword(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_number_to_u64(number: &Number) -> u64 {
|
||||
match number {
|
||||
Number::Int(big_int) => {
|
||||
if let Some(x) = big_int.to_u64() {
|
||||
x
|
||||
} else {
|
||||
unreachable!("Internal error: convert_number_to_u64 given incompatible number")
|
||||
}
|
||||
}
|
||||
Number::Decimal(big_decimal) => {
|
||||
if let Some(x) = big_decimal.to_u64() {
|
||||
x
|
||||
} else {
|
||||
unreachable!("Internal error: convert_number_to_u64 given incompatible number")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Unit::Byte => "B",
|
||||
Unit::Kilobyte => "KB",
|
||||
Unit::Megabyte => "MB",
|
||||
Unit::Gigabyte => "GB",
|
||||
Unit::Terabyte => "TB",
|
||||
Unit::Petabyte => "PB",
|
||||
Unit::Second => "s",
|
||||
Unit::Minute => "m",
|
||||
Unit::Hour => "h",
|
||||
Unit::Day => "d",
|
||||
Unit::Week => "w",
|
||||
Unit::Month => "M",
|
||||
Unit::Year => "y",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute(self, size: &Number) -> UntaggedValue {
|
||||
let size = size.clone();
|
||||
|
||||
match self {
|
||||
Unit::Byte => number(size),
|
||||
Unit::Kilobyte => number(size * 1024),
|
||||
Unit::Megabyte => number(size * 1024 * 1024),
|
||||
Unit::Gigabyte => number(size * 1024 * 1024 * 1024),
|
||||
Unit::Terabyte => number(size * 1024 * 1024 * 1024 * 1024),
|
||||
Unit::Petabyte => number(size * 1024 * 1024 * 1024 * 1024 * 1024),
|
||||
Unit::Second => duration(convert_number_to_u64(&size)),
|
||||
Unit::Minute => duration(60 * convert_number_to_u64(&size)),
|
||||
Unit::Hour => duration(60 * 60 * convert_number_to_u64(&size)),
|
||||
Unit::Day => duration(24 * 60 * 60 * convert_number_to_u64(&size)),
|
||||
Unit::Week => duration(7 * 24 * 60 * 60 * convert_number_to_u64(&size)),
|
||||
Unit::Month => duration(30 * 24 * 60 * 60 * convert_number_to_u64(&size)),
|
||||
Unit::Year => duration(365 * 24 * 60 * 60 * convert_number_to_u64(&size)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn number(number: impl Into<Number>) -> UntaggedValue {
|
||||
let number = number.into();
|
||||
|
||||
match number {
|
||||
Number::Int(int) => UntaggedValue::Primitive(Primitive::Int(int)),
|
||||
Number::Decimal(decimal) => UntaggedValue::Primitive(Primitive::Decimal(decimal)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duration(secs: u64) -> UntaggedValue {
|
||||
UntaggedValue::Primitive(Primitive::Duration(secs))
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||
pub struct SpannedExpression {
|
||||
pub expr: Expression,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl SpannedExpression {
|
||||
pub fn new(expr: Expression, span: Span) -> SpannedExpression {
|
||||
SpannedExpression { expr, span }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for SpannedExpression {
|
||||
type Target = Expression;
|
||||
|
||||
fn deref(&self) -> &Expression {
|
||||
&self.expr
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSpan for SpannedExpression {
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
|
||||
impl ShellTypeName for SpannedExpression {
|
||||
fn type_name(&self) -> &'static str {
|
||||
self.expr.type_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebugWithSource for SpannedExpression {
|
||||
fn refined_pretty_debug(&self, refine: PrettyDebugRefineKind, source: &str) -> DebugDocBuilder {
|
||||
match refine {
|
||||
PrettyDebugRefineKind::ContextFree => self.refined_pretty_debug(refine, source),
|
||||
PrettyDebugRefineKind::WithContext => match &self.expr {
|
||||
Expression::Literal(literal) => literal
|
||||
.clone()
|
||||
.into_spanned(self.span)
|
||||
.refined_pretty_debug(refine, source),
|
||||
Expression::ExternalWord => {
|
||||
b::delimit("e\"", b::primitive(self.span.slice(source)), "\"").group()
|
||||
}
|
||||
Expression::Synthetic(s) => match s {
|
||||
Synthetic::String(_) => {
|
||||
b::delimit("s\"", b::primitive(self.span.slice(source)), "\"").group()
|
||||
}
|
||||
},
|
||||
Expression::Variable(Variable::Other(_, _)) => b::keyword(self.span.slice(source)),
|
||||
Expression::Variable(Variable::It(_)) => b::keyword("$it"),
|
||||
Expression::Binary(binary) => binary.pretty_debug(source),
|
||||
Expression::Range(range) => range.pretty_debug(source),
|
||||
Expression::Block(_) => b::opaque("block"),
|
||||
Expression::Garbage => b::opaque("garbage"),
|
||||
Expression::List(list) => b::delimit(
|
||||
"[",
|
||||
b::intersperse(
|
||||
list.iter()
|
||||
.map(|item| item.refined_pretty_debug(refine, source)),
|
||||
b::space(),
|
||||
),
|
||||
"]",
|
||||
),
|
||||
Expression::Path(path) => path.pretty_debug(source),
|
||||
Expression::FilePath(path) => b::typed("path", b::primitive(path.display())),
|
||||
Expression::ExternalCommand(external) => {
|
||||
b::keyword("^") + b::keyword(external.name.span.slice(source))
|
||||
}
|
||||
Expression::Command(command) => b::keyword(command.slice(source)),
|
||||
Expression::Boolean(boolean) => match boolean {
|
||||
true => b::primitive("$yes"),
|
||||
false => b::primitive("$no"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
match &self.expr {
|
||||
Expression::Literal(literal) => {
|
||||
literal.clone().into_spanned(self.span).pretty_debug(source)
|
||||
}
|
||||
Expression::ExternalWord => {
|
||||
b::typed("external word", b::primitive(self.span.slice(source)))
|
||||
}
|
||||
Expression::Synthetic(s) => match s {
|
||||
Synthetic::String(s) => b::typed("synthetic", b::primitive(format!("{:?}", s))),
|
||||
},
|
||||
Expression::Variable(Variable::Other(_, _)) => b::keyword(self.span.slice(source)),
|
||||
Expression::Variable(Variable::It(_)) => b::keyword("$it"),
|
||||
Expression::Binary(binary) => binary.pretty_debug(source),
|
||||
Expression::Range(range) => range.pretty_debug(source),
|
||||
Expression::Block(_) => b::opaque("block"),
|
||||
Expression::Garbage => b::opaque("garbage"),
|
||||
Expression::List(list) => b::delimit(
|
||||
"[",
|
||||
b::intersperse(
|
||||
list.iter().map(|item| item.pretty_debug(source)),
|
||||
b::space(),
|
||||
),
|
||||
"]",
|
||||
),
|
||||
Expression::Path(path) => path.pretty_debug(source),
|
||||
Expression::FilePath(path) => b::typed("path", b::primitive(path.display())),
|
||||
Expression::ExternalCommand(external) => b::typed(
|
||||
"command",
|
||||
b::keyword("^") + b::primitive(external.name.span.slice(source)),
|
||||
),
|
||||
Expression::Command(command) => {
|
||||
b::typed("command", b::primitive(command.slice(source)))
|
||||
}
|
||||
Expression::Boolean(boolean) => match boolean {
|
||||
true => b::primitive("$yes"),
|
||||
false => b::primitive("$no"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||
pub enum Variable {
|
||||
It(Span),
|
||||
Other(String, Span),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialOrd, Ord, Eq, Hash, PartialEq, Deserialize, Serialize)]
|
||||
pub enum CompareOperator {
|
||||
Equal,
|
||||
NotEqual,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
LessThanOrEqual,
|
||||
GreaterThanOrEqual,
|
||||
Contains,
|
||||
NotContains,
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize, new)]
|
||||
pub struct Binary {
|
||||
pub left: SpannedExpression,
|
||||
pub op: SpannedExpression, //Spanned<CompareOperator>,
|
||||
pub right: SpannedExpression,
|
||||
}
|
||||
|
||||
impl PrettyDebugWithSource for Binary {
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
b::delimit(
|
||||
"<",
|
||||
self.left.pretty_debug(source)
|
||||
+ b::space()
|
||||
+ b::keyword(self.op.span.slice(source))
|
||||
+ b::space()
|
||||
+ self.right.pretty_debug(source),
|
||||
">",
|
||||
)
|
||||
.group()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||
pub enum Synthetic {
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl ShellTypeName for Synthetic {
|
||||
fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Synthetic::String(_) => "string",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||
pub struct Range {
|
||||
pub left: SpannedExpression,
|
||||
pub dotdot: Span,
|
||||
pub right: SpannedExpression,
|
||||
}
|
||||
|
||||
impl PrettyDebugWithSource for Range {
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
b::delimit(
|
||||
"<",
|
||||
self.left.pretty_debug(source)
|
||||
+ b::space()
|
||||
+ b::keyword(self.dotdot.slice(source))
|
||||
+ b::space()
|
||||
+ self.right.pretty_debug(source),
|
||||
">",
|
||||
)
|
||||
.group()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||
pub enum Literal {
|
||||
Number(Number),
|
||||
Size(Spanned<Number>, Spanned<Unit>),
|
||||
Operator(CompareOperator),
|
||||
String(String),
|
||||
GlobPattern(String),
|
||||
ColumnPath(Vec<Member>),
|
||||
Bare(String),
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
pub fn into_spanned(self, span: impl Into<Span>) -> SpannedLiteral {
|
||||
SpannedLiteral {
|
||||
literal: self,
|
||||
span: span.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpannedLiteral {
|
||||
pub literal: Literal,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl ShellTypeName for Literal {
|
||||
fn type_name(&self) -> &'static str {
|
||||
match &self {
|
||||
Literal::Number(..) => "number",
|
||||
Literal::Size(..) => "size",
|
||||
Literal::String(..) => "string",
|
||||
Literal::ColumnPath(..) => "column path",
|
||||
Literal::Bare(_) => "string",
|
||||
Literal::GlobPattern(_) => "pattern",
|
||||
Literal::Operator(_) => "operator",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebugWithSource for SpannedLiteral {
|
||||
fn refined_pretty_debug(&self, refine: PrettyDebugRefineKind, source: &str) -> DebugDocBuilder {
|
||||
match refine {
|
||||
PrettyDebugRefineKind::ContextFree => self.pretty_debug(source),
|
||||
PrettyDebugRefineKind::WithContext => match &self.literal {
|
||||
Literal::Number(number) => number.pretty(),
|
||||
Literal::Size(number, unit) => (number.pretty() + unit.pretty()).group(),
|
||||
Literal::String(string) => b::primitive(format!("{:?}", string)), //string.slice(source))),
|
||||
Literal::GlobPattern(pattern) => b::primitive(pattern),
|
||||
Literal::ColumnPath(path) => {
|
||||
b::intersperse_with_source(path.iter(), b::space(), source)
|
||||
}
|
||||
Literal::Bare(bare) => b::delimit("b\"", b::primitive(bare), "\""),
|
||||
Literal::Operator(operator) => b::primitive(format!("{:?}", operator)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
match &self.literal {
|
||||
Literal::Number(number) => number.pretty(),
|
||||
Literal::Size(number, unit) => {
|
||||
b::typed("size", (number.pretty() + unit.pretty()).group())
|
||||
}
|
||||
Literal::String(string) => b::typed(
|
||||
"string",
|
||||
b::primitive(format!("{:?}", string)), //string.slice(source))),
|
||||
),
|
||||
Literal::GlobPattern(pattern) => b::typed("pattern", b::primitive(pattern)),
|
||||
Literal::ColumnPath(path) => b::typed(
|
||||
"column path",
|
||||
b::intersperse_with_source(path.iter(), b::space(), source),
|
||||
),
|
||||
Literal::Bare(bare) => b::typed("bare", b::primitive(bare)),
|
||||
Literal::Operator(operator) => {
|
||||
b::typed("operator", b::primitive(format!("{:?}", operator)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, new, Deserialize, Serialize)]
|
||||
pub struct Path {
|
||||
pub head: SpannedExpression,
|
||||
pub tail: Vec<PathMember>,
|
||||
}
|
||||
|
||||
impl PrettyDebugWithSource for Path {
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
self.head.pretty_debug(source)
|
||||
+ b::operator(".")
|
||||
+ b::intersperse(self.tail.iter().map(|m| m.pretty()), b::operator("."))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
|
||||
pub enum Expression {
|
||||
Literal(Literal),
|
||||
ExternalWord,
|
||||
Synthetic(Synthetic),
|
||||
Variable(Variable),
|
||||
Binary(Box<Binary>),
|
||||
Range(Box<Range>),
|
||||
Block(Vec<SpannedExpression>),
|
||||
List(Vec<SpannedExpression>),
|
||||
Path(Box<Path>),
|
||||
|
||||
FilePath(PathBuf),
|
||||
ExternalCommand(ExternalCommand),
|
||||
Command(Span),
|
||||
|
||||
Boolean(bool),
|
||||
|
||||
// Trying this approach out: if we let parsing always be infallible
|
||||
// we can use the same parse and just place bad token markers in the output
|
||||
// We can later throw an error if we try to process them further.
|
||||
Garbage,
|
||||
}
|
||||
|
||||
impl ShellTypeName for Expression {
|
||||
fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Expression::Literal(literal) => literal.type_name(),
|
||||
Expression::Synthetic(synthetic) => synthetic.type_name(),
|
||||
Expression::Command(..) => "command",
|
||||
Expression::ExternalWord => "external word",
|
||||
Expression::FilePath(..) => "file path",
|
||||
Expression::Variable(..) => "variable",
|
||||
Expression::List(..) => "list",
|
||||
Expression::Binary(..) => "binary",
|
||||
Expression::Range(..) => "range",
|
||||
Expression::Block(..) => "block",
|
||||
Expression::Path(..) => "variable path",
|
||||
Expression::Boolean(..) => "boolean",
|
||||
Expression::ExternalCommand(..) => "external",
|
||||
Expression::Garbage => "garbage",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoSpanned for Expression {
|
||||
type Output = SpannedExpression;
|
||||
|
||||
fn into_spanned(self, span: impl Into<Span>) -> Self::Output {
|
||||
SpannedExpression {
|
||||
expr: self,
|
||||
span: span.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
pub fn integer(i: i64) -> Expression {
|
||||
Expression::Literal(Literal::Number(Number::Int(BigInt::from(i))))
|
||||
}
|
||||
|
||||
pub fn decimal(f: f64) -> Expression {
|
||||
Expression::Literal(Literal::Number(Number::Decimal(BigDecimal::from(f))))
|
||||
}
|
||||
|
||||
pub fn string(s: String) -> Expression {
|
||||
Expression::Literal(Literal::String(s))
|
||||
}
|
||||
|
||||
pub fn operator(operator: CompareOperator) -> Expression {
|
||||
Expression::Literal(Literal::Operator(operator))
|
||||
}
|
||||
|
||||
pub fn range(left: SpannedExpression, dotdot: Span, right: SpannedExpression) -> Expression {
|
||||
Expression::Range(Box::new(Range {
|
||||
left,
|
||||
dotdot,
|
||||
right,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn pattern(p: String) -> Expression {
|
||||
Expression::Literal(Literal::GlobPattern(p))
|
||||
}
|
||||
|
||||
pub fn file_path(file_path: PathBuf) -> Expression {
|
||||
Expression::FilePath(file_path)
|
||||
}
|
||||
|
||||
pub fn simple_column_path(members: Vec<Member>) -> Expression {
|
||||
Expression::Literal(Literal::ColumnPath(members))
|
||||
}
|
||||
|
||||
pub fn path(head: SpannedExpression, tail: Vec<impl Into<PathMember>>) -> Expression {
|
||||
let tail = tail.into_iter().map(|t| t.into()).collect();
|
||||
Expression::Path(Box::new(Path::new(head, tail)))
|
||||
}
|
||||
|
||||
pub fn unit(i: Spanned<i64>, unit: Spanned<Unit>) -> Expression {
|
||||
Expression::Literal(Literal::Size(
|
||||
Number::Int(BigInt::from(i.item)).spanned(i.span),
|
||||
unit,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn variable(v: String, span: Span) -> Expression {
|
||||
if v == "$it" {
|
||||
Expression::Variable(Variable::It(span))
|
||||
} else {
|
||||
Expression::Variable(Variable::Other(v, span))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum NamedValue {
|
||||
AbsentSwitch,
|
||||
PresentSwitch(Span),
|
||||
AbsentValue,
|
||||
Value(Span, SpannedExpression),
|
||||
}
|
||||
|
||||
impl PrettyDebugWithSource for NamedValue {
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
match self {
|
||||
NamedValue::AbsentSwitch => b::typed("switch", b::description("absent")),
|
||||
NamedValue::PresentSwitch(_) => b::typed("switch", b::description("present")),
|
||||
NamedValue::AbsentValue => b::description("absent"),
|
||||
NamedValue::Value(_, value) => value.pretty_debug(source),
|
||||
}
|
||||
}
|
||||
|
||||
fn refined_pretty_debug(&self, refine: PrettyDebugRefineKind, source: &str) -> DebugDocBuilder {
|
||||
match refine {
|
||||
PrettyDebugRefineKind::ContextFree => self.pretty_debug(source),
|
||||
PrettyDebugRefineKind::WithContext => match self {
|
||||
NamedValue::AbsentSwitch => b::value("absent"),
|
||||
NamedValue::PresentSwitch(_) => b::value("present"),
|
||||
NamedValue::AbsentValue => b::value("absent"),
|
||||
NamedValue::Value(_, value) => value.refined_pretty_debug(refine, source),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Call {
|
||||
pub head: Box<SpannedExpression>,
|
||||
pub positional: Option<Vec<SpannedExpression>>,
|
||||
pub named: Option<NamedArguments>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl Call {
|
||||
pub fn switch_preset(&self, switch: &str) -> bool {
|
||||
self.named
|
||||
.as_ref()
|
||||
.map(|n| n.switch_present(switch))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn set_initial_flags(&mut self, signature: &nu_protocol::Signature) {
|
||||
for (named, value) in signature.named.iter() {
|
||||
if self.named.is_none() {
|
||||
self.named = Some(NamedArguments::new());
|
||||
}
|
||||
|
||||
if let Some(ref mut args) = self.named {
|
||||
match value.0 {
|
||||
nu_protocol::NamedType::Switch(_) => args.insert_switch(named, None),
|
||||
_ => args.insert_optional(named, Span::new(0, 0), None),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebugWithSource for Call {
|
||||
fn refined_pretty_debug(&self, refine: PrettyDebugRefineKind, source: &str) -> DebugDocBuilder {
|
||||
match refine {
|
||||
PrettyDebugRefineKind::ContextFree => self.pretty_debug(source),
|
||||
PrettyDebugRefineKind::WithContext => {
|
||||
self.head
|
||||
.refined_pretty_debug(PrettyDebugRefineKind::WithContext, source)
|
||||
+ b::preceded_option(
|
||||
Some(b::space()),
|
||||
self.positional.as_ref().map(|pos| {
|
||||
b::intersperse(
|
||||
pos.iter().map(|expr| {
|
||||
expr.refined_pretty_debug(
|
||||
PrettyDebugRefineKind::WithContext,
|
||||
source,
|
||||
)
|
||||
}),
|
||||
b::space(),
|
||||
)
|
||||
}),
|
||||
)
|
||||
+ b::preceded_option(
|
||||
Some(b::space()),
|
||||
self.named.as_ref().map(|named| {
|
||||
named.refined_pretty_debug(PrettyDebugRefineKind::WithContext, source)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
b::typed(
|
||||
"call",
|
||||
self.refined_pretty_debug(PrettyDebugRefineKind::WithContext, source),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Call {
|
||||
pub fn new(head: Box<SpannedExpression>, span: Span) -> Call {
|
||||
Call {
|
||||
head,
|
||||
positional: None,
|
||||
named: None,
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum Delimiter {
|
||||
Paren,
|
||||
Brace,
|
||||
Square,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum FlatShape {
|
||||
OpenDelimiter(Delimiter),
|
||||
CloseDelimiter(Delimiter),
|
||||
Type,
|
||||
Identifier,
|
||||
ItVariable,
|
||||
Variable,
|
||||
CompareOperator,
|
||||
Dot,
|
||||
DotDot,
|
||||
InternalCommand,
|
||||
ExternalCommand,
|
||||
ExternalWord,
|
||||
BareMember,
|
||||
StringMember,
|
||||
String,
|
||||
Path,
|
||||
Word,
|
||||
Keyword,
|
||||
Pipe,
|
||||
GlobPattern,
|
||||
Flag,
|
||||
ShorthandFlag,
|
||||
Int,
|
||||
Decimal,
|
||||
Garbage,
|
||||
Whitespace,
|
||||
Separator,
|
||||
Comment,
|
||||
Size { number: Span, unit: Span },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct NamedArguments {
|
||||
pub named: IndexMap<String, NamedValue>,
|
||||
}
|
||||
|
||||
impl NamedArguments {
|
||||
pub fn new() -> NamedArguments {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&String, &NamedValue)> {
|
||||
self.named.iter()
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Option<&NamedValue> {
|
||||
self.named.get(name)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.named.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl NamedArguments {
|
||||
pub fn insert_switch(&mut self, name: impl Into<String>, switch: Option<Flag>) {
|
||||
let name = name.into();
|
||||
trace!("Inserting switch -- {} = {:?}", name, switch);
|
||||
|
||||
match switch {
|
||||
None => self.named.insert(name, NamedValue::AbsentSwitch),
|
||||
Some(flag) => self
|
||||
.named
|
||||
.insert(name, NamedValue::PresentSwitch(flag.name)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn insert_optional(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
flag_span: Span,
|
||||
expr: Option<SpannedExpression>,
|
||||
) {
|
||||
match expr {
|
||||
None => self.named.insert(name.into(), NamedValue::AbsentValue),
|
||||
Some(expr) => self
|
||||
.named
|
||||
.insert(name.into(), NamedValue::Value(flag_span, expr)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn insert_mandatory(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
flag_span: Span,
|
||||
expr: SpannedExpression,
|
||||
) {
|
||||
self.named
|
||||
.insert(name.into(), NamedValue::Value(flag_span, expr));
|
||||
}
|
||||
|
||||
pub fn switch_present(&self, switch: &str) -> bool {
|
||||
self.named
|
||||
.get(switch)
|
||||
.map(|t| match t {
|
||||
NamedValue::PresentSwitch(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebugWithSource for NamedArguments {
|
||||
fn refined_pretty_debug(&self, refine: PrettyDebugRefineKind, source: &str) -> DebugDocBuilder {
|
||||
match refine {
|
||||
PrettyDebugRefineKind::ContextFree => self.pretty_debug(source),
|
||||
PrettyDebugRefineKind::WithContext => b::intersperse(
|
||||
self.named.iter().map(|(key, value)| {
|
||||
b::key(key)
|
||||
+ b::equals()
|
||||
+ value.refined_pretty_debug(PrettyDebugRefineKind::WithContext, source)
|
||||
}),
|
||||
b::space(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
b::delimit(
|
||||
"(",
|
||||
self.refined_pretty_debug(PrettyDebugRefineKind::WithContext, source),
|
||||
")",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum FlagKind {
|
||||
Shorthand,
|
||||
Longhand,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, new)]
|
||||
pub struct Flag {
|
||||
pub(crate) kind: FlagKind,
|
||||
pub(crate) name: Span,
|
||||
}
|
||||
|
||||
impl PrettyDebugWithSource for Flag {
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
let prefix = match self.kind {
|
||||
FlagKind::Longhand => b::description("--"),
|
||||
FlagKind::Shorthand => b::description("-"),
|
||||
};
|
||||
|
||||
prefix + b::description(self.name.slice(source))
|
||||
}
|
||||
}
|
||||
|
||||
impl Flag {
|
||||
pub fn color(&self, span: impl Into<Span>) -> Spanned<FlatShape> {
|
||||
match self.kind {
|
||||
FlagKind::Longhand => FlatShape::Flag.spanned(span.into()),
|
||||
FlagKind::Shorthand => FlatShape::ShorthandFlag.spanned(span.into()),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
mod files;
|
||||
pub mod hir;
|
||||
mod lite_parse;
|
||||
mod parse;
|
||||
mod shapes;
|
||||
@ -7,10 +6,6 @@ mod signature;
|
||||
|
||||
pub use crate::files::Files;
|
||||
pub use crate::lite_parse::{lite_parse, LitePipeline};
|
||||
pub use crate::parse::{
|
||||
classify_pipeline, garbage, ClassifiedCommand, ClassifiedPipeline, Commands, InternalCommand,
|
||||
};
|
||||
pub use crate::parse::{classify_pipeline, garbage};
|
||||
pub use crate::shapes::shapes;
|
||||
pub use crate::signature::{
|
||||
ExternalArg, ExternalArgs, ExternalCommand, Signature, SignatureRegistry,
|
||||
};
|
||||
pub use crate::signature::{Signature, SignatureRegistry};
|
||||
|
@ -1,83 +1,17 @@
|
||||
use std::path::Path;
|
||||
|
||||
use nu_errors::{ArgumentError, ParseError};
|
||||
//use crate::hir::*;
|
||||
use crate::hir::{
|
||||
Binary, CompareOperator, Expression, Flag, FlagKind, Member, NamedArguments, SpannedExpression,
|
||||
Unit,
|
||||
};
|
||||
use crate::lite_parse::{lite_parse, LiteCommand, LitePipeline};
|
||||
use crate::signature::SignatureRegistry;
|
||||
use crate::{ExternalArg, ExternalArgs, ExternalCommand};
|
||||
use nu_errors::{ArgumentError, ParseError};
|
||||
use nu_protocol::hir::{
|
||||
self, Binary, ClassifiedCommand, ClassifiedPipeline, Commands, CompareOperator, Expression,
|
||||
ExternalArg, ExternalArgs, ExternalCommand, Flag, FlagKind, InternalCommand, Member,
|
||||
NamedArguments, SpannedExpression, Unit,
|
||||
};
|
||||
use nu_protocol::{NamedType, PositionalType, Signature, SyntaxShape, UnspannedPathMember};
|
||||
use nu_source::{Span, Spanned, SpannedItem, Tag};
|
||||
use num_bigint::BigInt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InternalCommand {
|
||||
pub name: String,
|
||||
pub name_span: Span,
|
||||
pub args: crate::hir::Call,
|
||||
}
|
||||
|
||||
impl InternalCommand {
|
||||
pub fn new(name: String, name_span: Span, full_span: Span) -> InternalCommand {
|
||||
InternalCommand {
|
||||
name: name.clone(),
|
||||
name_span,
|
||||
args: crate::hir::Call::new(
|
||||
Box::new(SpannedExpression::new(Expression::string(name), name_span)),
|
||||
full_span,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ClassifiedCommand {
|
||||
#[allow(unused)]
|
||||
Comparison(
|
||||
Box<SpannedExpression>,
|
||||
Box<SpannedExpression>,
|
||||
Box<SpannedExpression>,
|
||||
),
|
||||
#[allow(unused)]
|
||||
Dynamic(crate::hir::Call),
|
||||
Internal(InternalCommand),
|
||||
External(crate::ExternalCommand),
|
||||
Error(ParseError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Commands {
|
||||
pub list: Vec<ClassifiedCommand>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl Commands {
|
||||
pub fn new(span: Span) -> Commands {
|
||||
Commands { list: vec![], span }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, command: ClassifiedCommand) {
|
||||
self.list.push(command);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClassifiedPipeline {
|
||||
pub commands: Commands,
|
||||
// this is not a Result to make it crystal clear that these shapes
|
||||
// aren't intended to be used directly with `?`
|
||||
pub failed: Option<ParseError>,
|
||||
}
|
||||
|
||||
impl ClassifiedPipeline {
|
||||
pub fn new(commands: Commands, failed: Option<ParseError>) -> ClassifiedPipeline {
|
||||
ClassifiedPipeline { commands, failed }
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a simple column path, one without a variable (implied or explicit) at the head
|
||||
fn parse_simple_column_path(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
|
||||
let mut delimiter = '.';
|
||||
@ -534,53 +468,23 @@ fn parse_arg(
|
||||
(Some('{'), Some('}')) => {
|
||||
// We have a literal block
|
||||
let string: String = chars.collect();
|
||||
let mut error = None;
|
||||
|
||||
// We haven't done much with the inner string, so let's go ahead and work with it
|
||||
let lite_pipeline = match lite_parse(&string, lite_arg.span.start() + 1) {
|
||||
Ok(lp) => lp,
|
||||
Err(e) => return (garbage(lite_arg.span), Some(e)),
|
||||
};
|
||||
//let pipeline = parse(&lite_pipeline, definitions)?;
|
||||
|
||||
// For now, just take the first command
|
||||
if let Some(lite_cmd) = lite_pipeline.commands.first() {
|
||||
if lite_cmd.args.len() != 2 {
|
||||
return (
|
||||
garbage(lite_arg.span),
|
||||
Some(ParseError::mismatch("block", lite_arg.clone())),
|
||||
);
|
||||
}
|
||||
let (lhs, err) =
|
||||
parse_arg(SyntaxShape::FullColumnPath, registry, &lite_cmd.name);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
let (op, err) =
|
||||
parse_arg(SyntaxShape::Operator, registry, &lite_cmd.args[0]);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
let (rhs, err) = parse_arg(SyntaxShape::Any, registry, &lite_cmd.args[1]);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
let classified_block = classify_pipeline(&lite_pipeline, registry);
|
||||
let error = classified_block.failed;
|
||||
|
||||
let span = Span::new(lhs.span.start(), rhs.span.end());
|
||||
let binary = SpannedExpression::new(
|
||||
Expression::Binary(Box::new(Binary::new(lhs, op, rhs))),
|
||||
span,
|
||||
);
|
||||
(
|
||||
SpannedExpression::new(Expression::Block(vec![binary]), span),
|
||||
error,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
garbage(lite_arg.span),
|
||||
Some(ParseError::mismatch("block", lite_arg.clone())),
|
||||
)
|
||||
}
|
||||
(
|
||||
SpannedExpression::new(
|
||||
Expression::Block(classified_block.commands),
|
||||
lite_arg.span,
|
||||
),
|
||||
error,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// We have an implied block, but we can't parse this here
|
||||
@ -592,6 +496,14 @@ fn parse_arg(
|
||||
}
|
||||
}
|
||||
}
|
||||
SyntaxShape::Condition => {
|
||||
// We have an implied condition, but we can't parse this here
|
||||
// it needed to have been parsed up higher where we have control over more than one arg
|
||||
(
|
||||
garbage(lite_arg.span),
|
||||
Some(ParseError::mismatch("condition", lite_arg.clone())),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -664,13 +576,10 @@ fn classify_positional_arg(
|
||||
let mut idx = idx;
|
||||
let mut error = None;
|
||||
let arg = match positional_type {
|
||||
PositionalType::Mandatory(_, SyntaxShape::Block)
|
||||
| PositionalType::Optional(_, SyntaxShape::Block) => {
|
||||
// We may have an implied block, so let's try to parse it here
|
||||
// The only implied block format we currently support is <shorthand path> <operator> <any>, though
|
||||
// we may want to revisit this in the future
|
||||
|
||||
// TODO: only do this step if it's not a literal block
|
||||
PositionalType::Mandatory(_, SyntaxShape::Condition)
|
||||
| PositionalType::Optional(_, SyntaxShape::Condition) => {
|
||||
// A condition can take up multiple arguments, as we build the operation as <arg> <operator> <arg>
|
||||
// We need to do this here because in parse_arg, we have access to only one arg at a time
|
||||
if (idx + 2) < lite_cmd.args.len() {
|
||||
let (lhs, err) =
|
||||
parse_arg(SyntaxShape::FullColumnPath, registry, &lite_cmd.args[idx]);
|
||||
@ -691,9 +600,19 @@ fn classify_positional_arg(
|
||||
Expression::Binary(Box::new(Binary::new(lhs, op, rhs))),
|
||||
span,
|
||||
);
|
||||
SpannedExpression::new(Expression::Block(vec![binary]), span)
|
||||
let mut commands = hir::Commands::new(span);
|
||||
commands.push(ClassifiedCommand::Expr(Box::new(binary)));
|
||||
SpannedExpression::new(Expression::Block(commands), span)
|
||||
} else if idx < lite_cmd.args.len() {
|
||||
let (arg, err) =
|
||||
parse_arg(SyntaxShape::FullColumnPath, registry, &lite_cmd.args[idx]);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
arg
|
||||
} else {
|
||||
let (arg, err) = parse_arg(SyntaxShape::Block, registry, &lite_cmd.args[idx]);
|
||||
// TODO - this needs to get fixed and return an actual error
|
||||
let (arg, err) = parse_arg(SyntaxShape::Condition, registry, &lite_cmd.args[idx]);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
|
@ -1,18 +1,11 @@
|
||||
use crate::hir::*;
|
||||
use crate::parse::{ClassifiedCommand, Commands};
|
||||
use nu_protocol::hir::*;
|
||||
use nu_protocol::UnspannedPathMember;
|
||||
use nu_source::{Spanned, SpannedItem};
|
||||
|
||||
/// Converts a SpannedExpression into a spanned shape(s) ready for color-highlighting
|
||||
pub fn expression_to_flat_shape(e: &SpannedExpression) -> Vec<Spanned<FlatShape>> {
|
||||
match &e.expr {
|
||||
Expression::Block(exprs) => {
|
||||
let mut output = vec![];
|
||||
for expr in exprs.iter() {
|
||||
output.append(&mut expression_to_flat_shape(expr));
|
||||
}
|
||||
output
|
||||
}
|
||||
Expression::Block(exprs) => shapes(exprs),
|
||||
Expression::FilePath(_) => vec![FlatShape::Path.spanned(e.span)],
|
||||
Expression::Garbage => vec![FlatShape::Garbage.spanned(e.span)],
|
||||
Expression::List(exprs) => {
|
||||
@ -106,6 +99,7 @@ pub fn shapes(commands: &Commands) -> Vec<Spanned<FlatShape>> {
|
||||
output.push(FlatShape::ExternalWord.spanned(arg.tag.span));
|
||||
}
|
||||
}
|
||||
ClassifiedCommand::Expr(expr) => output.append(&mut expression_to_flat_shape(expr)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use nu_source::{b, DebugDocBuilder, HasSpan, PrettyDebug, PrettyDebugWithSource, Span, Tag};
|
||||
use nu_source::{DebugDocBuilder, HasSpan, PrettyDebugWithSource, Span};
|
||||
|
||||
pub trait SignatureRegistry: Debug {
|
||||
fn has(&self, name: &str) -> bool;
|
||||
@ -46,99 +46,3 @@ impl PrettyDebugWithSource for Signature {
|
||||
self.unspanned.pretty_debug(source)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ExternalArg {
|
||||
pub arg: String,
|
||||
pub tag: Tag,
|
||||
}
|
||||
|
||||
impl ExternalArg {
|
||||
pub fn has(&self, name: &str) -> bool {
|
||||
self.arg == name
|
||||
}
|
||||
|
||||
pub fn is_it(&self) -> bool {
|
||||
self.has("$it")
|
||||
}
|
||||
|
||||
pub fn is_nu(&self) -> bool {
|
||||
self.has("$nu")
|
||||
}
|
||||
|
||||
pub fn looks_like_it(&self) -> bool {
|
||||
self.arg.starts_with("$it") && (self.arg.starts_with("$it.") || self.is_it())
|
||||
}
|
||||
|
||||
pub fn looks_like_nu(&self) -> bool {
|
||||
self.arg.starts_with("$nu") && (self.arg.starts_with("$nu.") || self.is_nu())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for ExternalArg {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &str {
|
||||
&self.arg
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ExternalArgs {
|
||||
pub list: Vec<ExternalArg>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl ExternalArgs {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &ExternalArg> {
|
||||
self.list.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for ExternalArgs {
|
||||
type Target = [ExternalArg];
|
||||
|
||||
fn deref(&self) -> &[ExternalArg] {
|
||||
&self.list
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ExternalCommand {
|
||||
pub name: String,
|
||||
|
||||
pub name_tag: Tag,
|
||||
pub args: ExternalArgs,
|
||||
}
|
||||
|
||||
impl ExternalCommand {
|
||||
pub fn has_it_argument(&self) -> bool {
|
||||
self.args.iter().any(|arg| arg.looks_like_it())
|
||||
}
|
||||
|
||||
pub fn has_nu_argument(&self) -> bool {
|
||||
self.args.iter().any(|arg| arg.looks_like_nu())
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyDebug for ExternalCommand {
|
||||
fn pretty(&self) -> DebugDocBuilder {
|
||||
b::typed(
|
||||
"external command",
|
||||
b::description(&self.name)
|
||||
+ b::preceded(
|
||||
b::space(),
|
||||
b::intersperse(
|
||||
self.args.iter().map(|a| b::primitive(a.arg.to_string())),
|
||||
b::space(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSpan for ExternalCommand {
|
||||
fn span(&self) -> Span {
|
||||
self.name_tag.span.until(self.args.span)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user