Merge branch 'master' of github.com:nushell/nushell

This commit is contained in:
Yehuda Katz 2019-11-01 13:35:20 -07:00
commit 0f67569cc3
55 changed files with 2903 additions and 1136 deletions

View File

@ -0,0 +1,3 @@
[build]
rustflags = "--cfg coloring_in_tokens"

30
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,30 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1.
2.
3.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Configuration (please complete the following information):**
- OS: [e.g. Windows]
- Version [e.g. 0.4.0]
- Optional features (if any)
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

9
Cargo.lock generated
View File

@ -1498,6 +1498,7 @@ dependencies = [
"bson 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "bson 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-unit 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "byte-unit 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1540,7 +1541,7 @@ dependencies = [
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"roxmltree 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustyline 5.0.3 (git+https://github.com/kkawakam/rustyline.git)", "rustyline 5.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2077,8 +2078,8 @@ dependencies = [
[[package]] [[package]]
name = "rustyline" name = "rustyline"
version = "5.0.3" version = "5.0.4"
source = "git+https://github.com/kkawakam/rustyline.git#449c811998f630102bb2d9fb0b59b890d9eabac5" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
@ -3056,7 +3057,7 @@ dependencies = [
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum rustyline 5.0.3 (git+https://github.com/kkawakam/rustyline.git)" = "<none>" "checksum rustyline 5.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e9d8eb9912bc492db051324d36f5cea56984fc2afeaa5c6fa84e0b0e3cde550f"
"checksum ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19d2271fa48eaf61e53cc88b4ad9adcbafa2d512c531e7fadb6dc11a4d3656c5" "checksum ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19d2271fa48eaf61e53cc88b4ad9adcbafa2d512c531e7fadb6dc11a4d3656c5"
"checksum safemem 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b08423011dae9a5ca23f07cf57dac3857f5c885d352b76f6d95f4aea9434d0" "checksum safemem 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b08423011dae9a5ca23f07cf57dac3857f5c885d352b76f6d95f4aea9434d0"
"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"

View File

@ -75,6 +75,7 @@ serde_urlencoded = "0.6.1"
sublime_fuzzy = "0.5" sublime_fuzzy = "0.5"
trash = "1.0.0" trash = "1.0.0"
regex = "1" regex = "1"
cfg-if = "0.1"
neso = { version = "0.5.0", optional = true } neso = { version = "0.5.0", optional = true }
crossterm = { version = "0.10.2", optional = true } crossterm = { version = "0.10.2", optional = true }

47
docs/commands/tags.md Normal file
View File

@ -0,0 +1,47 @@
# tags
The tags commands allows users to access the metadata of the previous value in
the pipeline. This command may be run on multiple values of input as well.
As of writing this, the only metadata returned includes:
- `span`: the start and end indices of the previous value's substring location
- `anchor`: the source where data was loaded from; this may not appear if the
previous pipeline value didn't actually have a source (like trying to `open` a
dir, or running `ls` on a dir)
## Examples
```shell
> open README.md | tags
━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
span │ anchor
────────────────┼──────────────────────────────────────────────────
[table: 1 row] │ /Users/danielh/Projects/github/nushell/README.md
━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
```shell
> open README.md | tags | get span
━━━━━━━┯━━━━━
start │ end
───────┼─────
5 │ 14
━━━━━━━┷━━━━━
```
```shell
> ls | tags | first 3 | get span
━━━┯━━━━━━━┯━━━━━
# │ start │ end
───┼───────┼─────
0 │ 0 │ 2
1 │ 0 │ 2
2 │ 0 │ 2
━━━┷━━━━━━━┷━━━━━
```
## Reference
More useful information on the `tags` command can be found by referencing [The
Nu Book's entry on Metadata](https://book.nushell.sh/en/metadata)

View File

@ -14,13 +14,13 @@ use crate::git::current_branch;
use crate::parser::registry::Signature; use crate::parser::registry::Signature;
use crate::parser::{ use crate::parser::{
hir, hir,
hir::syntax_shape::{expand_syntax, PipelineShape}, hir::syntax_shape::{expand_syntax, ExpandContext, PipelineShape},
hir::{expand_external_tokens::expand_external_tokens, tokens_iterator::TokensIterator}, hir::{expand_external_tokens::ExternalTokensShape, tokens_iterator::TokensIterator},
TokenNode, TokenNode,
}; };
use crate::prelude::*; use crate::prelude::*;
use log::{debug, trace}; use log::{debug, log_enabled, trace};
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::{self, config::Configurer, config::EditMode, ColorMode, Config, Editor}; use rustyline::{self, config::Configurer, config::EditMode, ColorMode, Config, Editor};
use std::env; use std::env;
@ -506,6 +506,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Some(ClassifiedCommand::External(_)) => {} Some(ClassifiedCommand::External(_)) => {}
_ => pipeline _ => pipeline
.commands .commands
.item
.push(ClassifiedCommand::Internal(InternalCommand { .push(ClassifiedCommand::Internal(InternalCommand {
name: "autoview".to_string(), name: "autoview".to_string(),
name_tag: Tag::unknown(), name_tag: Tag::unknown(),
@ -513,13 +514,14 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Box::new(hir::Expression::synthetic_string("autoview")), Box::new(hir::Expression::synthetic_string("autoview")),
None, None,
None, None,
), )
.spanned_unknown(),
})), })),
} }
let mut input = ClassifiedInputStream::new(); let mut input = ClassifiedInputStream::new();
let mut iter = pipeline.commands.into_iter().peekable(); let mut iter = pipeline.commands.item.into_iter().peekable();
let mut is_first_command = true; let mut is_first_command = true;
// Check the config to see if we need to update the path // Check the config to see if we need to update the path
@ -679,11 +681,20 @@ fn classify_pipeline(
let mut pipeline_list = vec![pipeline.clone()]; let mut pipeline_list = vec![pipeline.clone()];
let mut iterator = TokensIterator::all(&mut pipeline_list, pipeline.span()); let mut iterator = TokensIterator::all(&mut pipeline_list, pipeline.span());
expand_syntax( let result = expand_syntax(
&PipelineShape, &PipelineShape,
&mut iterator, &mut iterator,
&context.expand_context(source, pipeline.span()), &context.expand_context(source),
) )
.map_err(|err| err.into());
if log_enabled!(target: "nu::expand_syntax", log::Level::Debug) {
println!("");
ptree::print_tree(&iterator.expand_tracer().print(source.clone())).unwrap();
println!("");
}
result
} }
// Classify this command as an external command, which doesn't give special meaning // Classify this command as an external command, which doesn't give special meaning
@ -691,21 +702,22 @@ fn classify_pipeline(
// strings. // strings.
pub(crate) fn external_command( pub(crate) fn external_command(
tokens: &mut TokensIterator, tokens: &mut TokensIterator,
source: &Text, context: &ExpandContext,
name: Tagged<&str>, name: Tagged<&str>,
) -> Result<ClassifiedCommand, ShellError> { ) -> Result<ClassifiedCommand, ParseError> {
let arg_list_strings = expand_external_tokens(tokens, source)?; let Spanned { item, span } = expand_syntax(&ExternalTokensShape, tokens, context)?;
Ok(ClassifiedCommand::External(ExternalCommand { Ok(ClassifiedCommand::External(ExternalCommand {
name: name.to_string(), name: name.to_string(),
name_tag: name.tag(), name_tag: name.tag(),
args: arg_list_strings args: item
.iter() .iter()
.map(|x| Tagged { .map(|x| Tagged {
tag: x.span.into(), tag: x.span.into(),
item: x.item.clone(), item: x.item.clone(),
}) })
.collect(), .collect::<Vec<_>>()
.spanned(span),
})) }))
} }

View File

@ -4,7 +4,9 @@ use bytes::{BufMut, BytesMut};
use derive_new::new; use derive_new::new;
use futures::stream::StreamExt; use futures::stream::StreamExt;
use futures_codec::{Decoder, Encoder, Framed}; use futures_codec::{Decoder, Encoder, Framed};
use itertools::Itertools;
use log::{log_enabled, trace}; use log::{log_enabled, trace};
use std::fmt;
use std::io::{Error, ErrorKind}; use std::io::{Error, ErrorKind};
use subprocess::Exec; use subprocess::Exec;
@ -72,26 +74,77 @@ impl ClassifiedInputStream {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub(crate) struct ClassifiedPipeline { pub(crate) struct ClassifiedPipeline {
pub(crate) commands: Vec<ClassifiedCommand>, pub(crate) commands: Spanned<Vec<ClassifiedCommand>>,
} }
#[derive(Debug, Eq, PartialEq)] impl FormatDebug for ClassifiedPipeline {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
f.say_str(
"classified pipeline",
self.commands.iter().map(|c| c.debug(source)).join(" | "),
)
}
}
impl HasSpan for ClassifiedPipeline {
fn span(&self) -> Span {
self.commands.span
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum ClassifiedCommand { pub(crate) enum ClassifiedCommand {
#[allow(unused)] #[allow(unused)]
Expr(TokenNode), Expr(TokenNode),
Internal(InternalCommand), Internal(InternalCommand),
#[allow(unused)] #[allow(unused)]
Dynamic(hir::Call), Dynamic(Spanned<hir::Call>),
External(ExternalCommand), External(ExternalCommand),
} }
#[derive(new, Debug, Eq, PartialEq)] impl FormatDebug for ClassifiedCommand {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self {
ClassifiedCommand::Expr(expr) => expr.fmt_debug(f, source),
ClassifiedCommand::Internal(internal) => internal.fmt_debug(f, source),
ClassifiedCommand::Dynamic(dynamic) => dynamic.fmt_debug(f, source),
ClassifiedCommand::External(external) => external.fmt_debug(f, source),
}
}
}
impl HasSpan for ClassifiedCommand {
fn span(&self) -> Span {
match self {
ClassifiedCommand::Expr(node) => node.span(),
ClassifiedCommand::Internal(command) => command.span(),
ClassifiedCommand::Dynamic(call) => call.span,
ClassifiedCommand::External(command) => command.span(),
}
}
}
#[derive(new, Debug, Clone, Eq, PartialEq)]
pub(crate) struct InternalCommand { pub(crate) struct InternalCommand {
pub(crate) name: String, pub(crate) name: String,
pub(crate) name_tag: Tag, pub(crate) name_tag: Tag,
pub(crate) args: hir::Call, pub(crate) args: Spanned<hir::Call>,
}
impl HasSpan for InternalCommand {
fn span(&self) -> Span {
let start = self.name_tag.span;
start.until(self.args.span)
}
}
impl FormatDebug for InternalCommand {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
f.say("internal", self.args.debug(source))
}
} }
#[derive(new, Debug, Eq, PartialEq)] #[derive(new, Debug, Eq, PartialEq)]
@ -122,7 +175,7 @@ impl InternalCommand {
context.run_command( context.run_command(
command, command,
self.name_tag.clone(), self.name_tag.clone(),
self.args, self.args.item,
&source, &source,
objects, objects,
is_first_command, is_first_command,
@ -201,12 +254,31 @@ impl InternalCommand {
} }
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct ExternalCommand { pub(crate) struct ExternalCommand {
pub(crate) name: String, pub(crate) name: String,
pub(crate) name_tag: Tag, pub(crate) name_tag: Tag,
pub(crate) args: Vec<Tagged<String>>, pub(crate) args: Spanned<Vec<Tagged<String>>>,
}
impl FormatDebug for ExternalCommand {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "{}", self.name)?;
if self.args.item.len() > 0 {
write!(f, " ")?;
write!(f, "{}", self.args.iter().map(|i| i.debug(source)).join(" "))?;
}
Ok(())
}
}
impl HasSpan for ExternalCommand {
fn span(&self) -> Span {
self.name_tag.span.until(self.args.span)
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -230,7 +302,7 @@ impl ExternalCommand {
trace!(target: "nu::run::external", "inputs = {:?}", inputs); trace!(target: "nu::run::external", "inputs = {:?}", inputs);
let mut arg_string = format!("{}", self.name); let mut arg_string = format!("{}", self.name);
for arg in &self.args { for arg in &self.args.item {
arg_string.push_str(&arg); arg_string.push_str(&arg);
} }
@ -275,7 +347,7 @@ impl ExternalCommand {
process = Exec::shell(itertools::join(commands, " && ")) process = Exec::shell(itertools::join(commands, " && "))
} else { } else {
process = Exec::cmd(&self.name); process = Exec::cmd(&self.name);
for arg in &self.args { for arg in &self.args.item {
let arg_chars: Vec<_> = arg.chars().collect(); let arg_chars: Vec<_> = arg.chars().collect();
if arg_chars.len() > 1 if arg_chars.len() > 1
&& arg_chars[0] == '"' && arg_chars[0] == '"'

View File

@ -19,8 +19,8 @@ pub struct UnevaluatedCallInfo {
pub name_tag: Tag, pub name_tag: Tag,
} }
impl ToDebug for UnevaluatedCallInfo { impl FormatDebug for UnevaluatedCallInfo {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
self.args.fmt_debug(f, source) self.args.fmt_debug(f, source)
} }
} }
@ -96,8 +96,14 @@ impl RawCommandArgs {
} }
} }
impl ToDebug for CommandArgs { impl std::fmt::Debug for CommandArgs {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.call_info.fmt(f)
}
}
impl FormatDebug for CommandArgs {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
self.call_info.fmt_debug(f, source) self.call_info.fmt_debug(f, source)
} }
} }
@ -377,7 +383,7 @@ impl EvaluatedCommandArgs {
} }
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CommandAction { pub enum CommandAction {
ChangePath(String), ChangePath(String),
Exit, Exit,
@ -389,8 +395,8 @@ pub enum CommandAction {
LeaveShell, LeaveShell,
} }
impl ToDebug for CommandAction { impl FormatDebug for CommandAction {
fn fmt_debug(&self, f: &mut fmt::Formatter, _source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, _source: &str) -> fmt::Result {
match self { match self {
CommandAction::ChangePath(s) => write!(f, "action:change-path={}", s), CommandAction::ChangePath(s) => write!(f, "action:change-path={}", s),
CommandAction::Exit => write!(f, "action:exit"), CommandAction::Exit => write!(f, "action:exit"),
@ -408,7 +414,7 @@ impl ToDebug for CommandAction {
} }
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ReturnSuccess { pub enum ReturnSuccess {
Value(Tagged<Value>), Value(Tagged<Value>),
Action(CommandAction), Action(CommandAction),
@ -416,8 +422,8 @@ pub enum ReturnSuccess {
pub type ReturnValue = Result<ReturnSuccess, ShellError>; pub type ReturnValue = Result<ReturnSuccess, ShellError>;
impl ToDebug for ReturnValue { impl FormatDebug for ReturnValue {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self { match self {
Err(err) => write!(f, "{}", err.debug(source)), Err(err) => write!(f, "{}", err.debug(source)),
Ok(ReturnSuccess::Value(v)) => write!(f, "{:?}", v.debug()), Ok(ReturnSuccess::Value(v)) => write!(f, "{:?}", v.debug()),

View File

@ -35,37 +35,34 @@ fn run(
_registry: &CommandRegistry, _registry: &CommandRegistry,
_raw_args: &RawCommandArgs, _raw_args: &RawCommandArgs,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let name = call_info.name_tag.clone(); let mut output = vec![];
let mut output = String::new();
let mut first = true;
if let Some(ref positional) = call_info.args.positional { if let Some(ref positional) = call_info.args.positional {
for i in positional { for i in positional {
match i.as_string() { match i.as_string() {
Ok(s) => { Ok(s) => {
if !first { output.push(Ok(ReturnSuccess::Value(
output.push_str(" "); Value::string(s).tagged(i.tag.clone()),
} else { )));
first = false; }
_ => match i {
Tagged {
item: Value::Table(table),
..
} => {
for item in table {
output.push(Ok(ReturnSuccess::Value(item.clone())));
} }
output.push_str(&s);
} }
_ => { _ => {
return Err(ShellError::type_error( output.push(Ok(ReturnSuccess::Value(i.clone())));
"a string-compatible value",
i.tagged_type_name(),
))
} }
},
} }
} }
} }
let stream = VecDeque::from(vec![Ok(ReturnSuccess::Value( let stream = VecDeque::from(output);
Value::string(output).tagged(name),
))]);
Ok(stream.to_output_stream()) Ok(stream.to_output_stream())
} }

View File

@ -1,8 +1,8 @@
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::data::meta::tag_for_tagged_list;
use crate::data::Value; use crate::data::Value;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::prelude::*; use crate::prelude::*;
use crate::utils::did_you_mean;
use log::trace; use log::trace;
pub struct Get; pub struct Get;
@ -50,43 +50,54 @@ pub fn get_column_path(
path: &ColumnPath, path: &ColumnPath,
obj: &Tagged<Value>, obj: &Tagged<Value>,
) -> Result<Tagged<Value>, ShellError> { ) -> Result<Tagged<Value>, ShellError> {
let mut current = Some(obj); let fields = path.clone();
for p in path.iter() {
if let Some(obj) = current { let value = obj.get_data_by_column_path(
current = match obj.get_data_by_key(&p) { obj.tag(),
Some(v) => Some(v), path,
None => Box::new(move |(obj_source, column_path_tried)| {
// Before we give up, see if they gave us a path that matches a field name by itself match obj_source {
Value::Table(rows) => {
let total = rows.len();
let end_tag = match fields.iter().nth_back(if fields.len() > 2 { 1 } else { 0 })
{ {
let possibilities = obj.data_descriptors(); Some(last_field) => last_field.tag(),
None => column_path_tried.tag(),
};
let mut possible_matches: Vec<_> = possibilities return ShellError::labeled_error_with_secondary(
.iter() "Row not found",
.map(|x| (natural::distance::levenshtein_distance(x, &p), x)) format!("There isn't a row indexed at '{}'", **column_path_tried),
.collect(); column_path_tried.tag(),
format!("The table only has {} rows (0..{})", total, total - 1),
end_tag,
);
}
_ => {}
}
possible_matches.sort(); match did_you_mean(&obj_source, &column_path_tried) {
Some(suggestions) => {
if possible_matches.len() > 0 { return ShellError::labeled_error(
return Err(ShellError::labeled_error(
"Unknown column", "Unknown column",
format!("did you mean '{}'?", possible_matches[0].1), format!("did you mean '{}'?", suggestions[0].1),
tag_for_tagged_list(path.iter().map(|p| p.tag())), tag_for_tagged_list(fields.iter().map(|p| p.tag())),
)); )
} else { }
return Err(ShellError::labeled_error( None => {
return ShellError::labeled_error(
"Unknown column", "Unknown column",
"row does not contain this column", "row does not contain this column",
tag_for_tagged_list(path.iter().map(|p| p.tag())), tag_for_tagged_list(fields.iter().map(|p| p.tag())),
)); )
}
}
}
} }
} }
}),
);
match current { let res = match value {
Some(v) => Ok(v.clone()), Ok(fetched) => match fetched {
Some(Tagged { item: v, tag }) => Ok((v.clone()).tagged(&tag)),
None => match obj { None => match obj {
// If its None check for certain values. // If its None check for certain values.
Tagged { Tagged {
@ -99,7 +110,11 @@ pub fn get_column_path(
} => Ok(obj.clone()), } => Ok(obj.clone()),
_ => Ok(Value::nothing().tagged(&obj.tag)), _ => Ok(Value::nothing().tagged(&obj.tag)),
}, },
} },
Err(reason) => Err(reason),
};
res
} }
pub fn get( pub fn get(
@ -118,26 +133,30 @@ pub fn get(
let member = vec![member.clone()]; let member = vec![member.clone()];
let fields = vec![&member, &fields] let column_paths = vec![&member, &fields]
.into_iter() .into_iter()
.flatten() .flatten()
.collect::<Vec<&ColumnPath>>(); .collect::<Vec<&ColumnPath>>();
for column_path in &fields { for path in column_paths {
match get_column_path(column_path, &item) { let res = get_column_path(&path, &item);
Ok(Tagged {
item: Value::Table(l), match res {
Ok(got) => match got {
Tagged {
item: Value::Table(rows),
.. ..
}) => { } => {
for item in l { for item in rows {
result.push_back(ReturnSuccess::value(item.clone())); result.push_back(ReturnSuccess::value(item.clone()));
} }
} }
Ok(x) => result.push_back(ReturnSuccess::value(x.clone())), other => result
Err(x) => result.push_back(Err(x)), .push_back(ReturnSuccess::value((*other).clone().tagged(&item.tag))),
},
Err(reason) => result.push_back(Err(reason)),
} }
} }
result result
}) })
.flatten(); .flatten();

View File

@ -71,9 +71,8 @@ impl Context {
pub(crate) fn expand_context<'context>( pub(crate) fn expand_context<'context>(
&'context self, &'context self,
source: &'context Text, source: &'context Text,
span: Span,
) -> ExpandContext<'context> { ) -> ExpandContext<'context> {
ExpandContext::new(&self.registry, span, source, self.shell_manager.homedir()) ExpandContext::new(&self.registry, source, self.shell_manager.homedir())
} }
pub(crate) fn basic() -> Result<Context, Box<dyn Error>> { pub(crate) fn basic() -> Result<Context, Box<dyn Error>> {

View File

@ -459,11 +459,10 @@ impl Value {
} }
} }
// TODO: This is basically a legacy construct, I think
pub fn data_descriptors(&self) -> Vec<String> { pub fn data_descriptors(&self) -> Vec<String> {
match self { match self {
Value::Primitive(_) => vec![], Value::Primitive(_) => vec![],
Value::Row(o) => o Value::Row(columns) => columns
.entries .entries
.keys() .keys()
.into_iter() .into_iter()
@ -475,6 +474,13 @@ impl Value {
} }
} }
pub(crate) fn get_data_by_index(&self, idx: usize) -> Option<&Tagged<Value>> {
match self {
Value::Table(value_set) => value_set.get(idx),
_ => None,
}
}
pub(crate) fn get_data_by_key(&self, name: &str) -> Option<&Tagged<Value>> { pub(crate) fn get_data_by_key(&self, name: &str) -> Option<&Tagged<Value>> {
match self { match self {
Value::Row(o) => o.get_data_by_key(name), Value::Row(o) => o.get_data_by_key(name),
@ -523,16 +529,33 @@ impl Value {
&self, &self,
tag: Tag, tag: Tag,
path: &Vec<Tagged<String>>, path: &Vec<Tagged<String>>,
) -> Option<Tagged<&Value>> { callback: Box<dyn FnOnce((&Value, &Tagged<String>)) -> ShellError>,
) -> Result<Option<Tagged<&Value>>, ShellError> {
let mut current = self; let mut current = self;
for p in path { for p in path {
match current.get_data_by_key(p) { // note:
// This will eventually be refactored once we are able
// to parse correctly column_paths and get them deserialized
// to values for us.
let value = match p.item().parse::<usize>() {
Ok(number) => match current {
Value::Table(_) => current.get_data_by_index(number),
Value::Row(_) => current.get_data_by_key(p),
_ => None,
},
Err(_) => match self {
Value::Table(_) | Value::Row(_) => current.get_data_by_key(p),
_ => None,
},
}; // end
match value {
Some(v) => current = v, Some(v) => current = v,
None => return None, None => return Err(callback((&current.clone(), &p.clone()))),
} }
} }
Some(current.tagged(tag)) Ok(Some(current.tagged(tag)))
} }
pub fn insert_data_at_path( pub fn insert_data_at_path(
@ -912,6 +935,7 @@ fn coerce_compare_primitive(
mod tests { mod tests {
use crate::data::meta::*; use crate::data::meta::*;
use crate::ShellError;
use crate::Value; use crate::Value;
use indexmap::IndexMap; use indexmap::IndexMap;
@ -927,6 +951,10 @@ mod tests {
Value::table(list).tagged_unknown() Value::table(list).tagged_unknown()
} }
fn error_callback() -> impl FnOnce((&Value, &Tagged<String>)) -> ShellError {
move |(_obj_source, _column_path_tried)| ShellError::unimplemented("will never be called.")
}
fn column_path(paths: &Vec<Tagged<Value>>) -> Tagged<Vec<Tagged<String>>> { fn column_path(paths: &Vec<Tagged<Value>>) -> Tagged<Vec<Tagged<String>>> {
table( table(
&paths &paths
@ -960,7 +988,7 @@ mod tests {
let (version, tag) = string("0.4.0").into_parts(); let (version, tag) = string("0.4.0").into_parts();
let row = Value::row(indexmap! { let value = Value::row(indexmap! {
"package".into() => "package".into() =>
row(indexmap! { row(indexmap! {
"name".into() => string("nu"), "name".into() => string("nu"),
@ -969,7 +997,10 @@ mod tests {
}); });
assert_eq!( assert_eq!(
**row.get_data_by_column_path(tag, &field_path).unwrap(), **value
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
.unwrap()
.unwrap(),
version version
) )
} }
@ -980,7 +1011,7 @@ mod tests {
let (name, tag) = string("Andrés N. Robalino").into_parts(); let (name, tag) = string("Andrés N. Robalino").into_parts();
let row = Value::row(indexmap! { let value = Value::row(indexmap! {
"package".into() => row(indexmap! { "package".into() => row(indexmap! {
"name".into() => string("nu"), "name".into() => string("nu"),
"version".into() => string("0.4.0"), "version".into() => string("0.4.0"),
@ -993,11 +1024,72 @@ mod tests {
}); });
assert_eq!( assert_eq!(
**row.get_data_by_column_path(tag, &field_path).unwrap(), **value
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
.unwrap()
.unwrap(),
name name
) )
} }
#[test]
fn column_path_that_contains_just_a_number_gets_a_row_from_a_table() {
let field_path = column_path(&vec![string("package"), string("authors"), string("0")]);
let (_, tag) = string("Andrés N. Robalino").into_parts();
let value = Value::row(indexmap! {
"package".into() => row(indexmap! {
"name".into() => string("nu"),
"version".into() => string("0.4.0"),
"authors".into() => table(&vec![
row(indexmap!{"name".into() => string("Andrés N. Robalino")}),
row(indexmap!{"name".into() => string("Jonathan Turner")}),
row(indexmap!{"name".into() => string("Yehuda Katz")})
])
})
});
assert_eq!(
**value
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
.unwrap()
.unwrap(),
Value::row(indexmap! {
"name".into() => string("Andrés N. Robalino")
})
);
}
#[test]
fn column_path_that_contains_just_a_number_gets_a_row_from_a_row() {
let field_path = column_path(&vec![string("package"), string("authors"), string("0")]);
let (_, tag) = string("Andrés N. Robalino").into_parts();
let value = Value::row(indexmap! {
"package".into() => row(indexmap! {
"name".into() => string("nu"),
"version".into() => string("0.4.0"),
"authors".into() => row(indexmap! {
"0".into() => row(indexmap!{"name".into() => string("Andrés N. Robalino")}),
"1".into() => row(indexmap!{"name".into() => string("Jonathan Turner")}),
"2".into() => row(indexmap!{"name".into() => string("Yehuda Katz")}),
})
})
});
assert_eq!(
**value
.get_data_by_column_path(tag, &field_path, Box::new(error_callback()))
.unwrap()
.unwrap(),
Value::row(indexmap! {
"name".into() => string("Andrés N. Robalino")
})
);
}
#[test] #[test]
fn replaces_matching_field_from_a_row() { fn replaces_matching_field_from_a_row() {
let field_path = column_path(&vec![string("amigos")]); let field_path = column_path(&vec![string("amigos")]);

View File

@ -5,6 +5,7 @@ use derive_new::new;
use getset::Getters; use getset::Getters;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use std::fmt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] #[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
@ -461,3 +462,134 @@ impl language_reporting::ReportingSpan for Span {
self.end self.end
} }
} }
pub trait HasSpan: ToDebug {
fn span(&self) -> Span;
}
pub trait HasFallibleSpan: ToDebug {
fn maybe_span(&self) -> Option<Span>;
}
impl<T: HasSpan> HasFallibleSpan for T {
fn maybe_span(&self) -> Option<Span> {
Some(HasSpan::span(self))
}
}
impl<T> HasSpan for Spanned<T>
where
Spanned<T>: ToDebug,
{
fn span(&self) -> Span {
self.span
}
}
impl HasFallibleSpan for Option<Span> {
fn maybe_span(&self) -> Option<Span> {
*self
}
}
impl FormatDebug for Option<Span> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self {
Option::None => write!(f, "no span"),
Option::Some(span) => FormatDebug::fmt_debug(span, f, source),
}
}
}
impl FormatDebug for Span {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "{:?}", self.slice(source))
}
}
impl HasSpan for Span {
fn span(&self) -> Span {
*self
}
}
impl<T> FormatDebug for Option<Spanned<T>>
where
Spanned<T>: ToDebug,
{
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self {
Option::None => write!(f, "nothing"),
Option::Some(spanned) => FormatDebug::fmt_debug(spanned, f, source),
}
}
}
impl<T> HasFallibleSpan for Option<Spanned<T>>
where
Spanned<T>: ToDebug,
{
fn maybe_span(&self) -> Option<Span> {
match self {
None => None,
Some(value) => Some(value.span),
}
}
}
impl<T> FormatDebug for Option<Tagged<T>>
where
Tagged<T>: ToDebug,
{
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self {
Option::None => write!(f, "nothing"),
Option::Some(item) => FormatDebug::fmt_debug(item, f, source),
}
}
}
impl<T> HasFallibleSpan for Option<Tagged<T>>
where
Tagged<T>: ToDebug,
{
fn maybe_span(&self) -> Option<Span> {
match self {
None => None,
Some(value) => Some(value.tag.span),
}
}
}
impl<T> HasSpan for Tagged<T>
where
Tagged<T>: ToDebug,
{
fn span(&self) -> Span {
self.tag.span
}
}
impl<T: ToDebug> FormatDebug for Vec<T> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "[ ")?;
write!(
f,
"{}",
self.iter().map(|item| item.debug(source)).join(" ")
)?;
write!(f, " ]")
}
}
impl FormatDebug for String {
fn fmt_debug(&self, f: &mut DebugFormatter, _source: &str) -> fmt::Result {
write!(f, "{}", self)
}
}
impl FormatDebug for Spanned<String> {
fn fmt_debug(&self, f: &mut DebugFormatter, _source: &str) -> fmt::Result {
write!(f, "{}", self.item)
}
}

View File

@ -30,6 +30,82 @@ impl Description {
} }
} }
#[derive(Debug, Clone)]
pub enum ParseErrorReason {
Eof {
expected: &'static str,
},
Mismatch {
expected: &'static str,
actual: Tagged<String>,
},
ArgumentError {
command: String,
error: ArgumentError,
tag: Tag,
},
}
#[derive(Debug, Clone)]
pub struct ParseError {
reason: ParseErrorReason,
tag: Tag,
}
impl ParseError {
pub fn unexpected_eof(expected: &'static str, span: Span) -> ParseError {
ParseError {
reason: ParseErrorReason::Eof { expected },
tag: span.into(),
}
}
pub fn mismatch(expected: &'static str, actual: Tagged<impl Into<String>>) -> ParseError {
let Tagged { tag, item } = actual;
ParseError {
reason: ParseErrorReason::Mismatch {
expected,
actual: item.into().tagged(tag.clone()),
},
tag,
}
}
pub fn argument_error(
command: impl Into<String>,
kind: ArgumentError,
tag: impl Into<Tag>,
) -> ParseError {
let tag = tag.into();
ParseError {
reason: ParseErrorReason::ArgumentError {
command: command.into(),
error: kind,
tag: tag.clone(),
},
tag: tag.clone(),
}
}
}
impl From<ParseError> for ShellError {
fn from(error: ParseError) -> ShellError {
match error.reason {
ParseErrorReason::Eof { expected } => ShellError::unexpected_eof(expected, error.tag),
ParseErrorReason::Mismatch { actual, expected } => {
ShellError::type_error(expected, actual.clone())
}
ParseErrorReason::ArgumentError {
command,
error,
tag,
} => ShellError::argument_error(command, error, tag),
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
pub enum ArgumentError { pub enum ArgumentError {
MissingMandatoryFlag(String), MissingMandatoryFlag(String),
@ -51,8 +127,8 @@ impl ShellError {
} }
} }
impl ToDebug for ShellError { impl FormatDebug for ShellError {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
self.error.fmt_debug(f, source) self.error.fmt_debug(f, source)
} }
} }
@ -153,16 +229,6 @@ impl ShellError {
.start() .start()
} }
pub(crate) fn invalid_external_word(tag: impl Into<Tag>) -> ShellError {
ProximateShellError::ArgumentError {
command: "Invalid argument to Nu command (did you mean to call an external command?)"
.into(),
error: ArgumentError::InvalidExternalWord,
tag: tag.into(),
}
.start()
}
pub(crate) fn parse_error( pub(crate) fn parse_error(
error: nom::Err<( error: nom::Err<(
nom_locate::LocatedSpanEx<&str, TracableContext>, nom_locate::LocatedSpanEx<&str, TracableContext>,
@ -490,8 +556,8 @@ impl ProximateShellError {
} }
} }
impl ToDebug for ProximateShellError { impl FormatDebug for ProximateShellError {
fn fmt_debug(&self, f: &mut fmt::Formatter, _source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, _source: &str) -> fmt::Result {
// TODO: Custom debug for inner spans // TODO: Custom debug for inner spans
write!(f, "{:?}", self) write!(f, "{:?}", self)
} }

View File

@ -30,12 +30,16 @@ pub use crate::env::host::BasicHost;
pub use crate::parser::hir::SyntaxShape; pub use crate::parser::hir::SyntaxShape;
pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder; pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder;
pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::plugin::{serve_plugin, Plugin};
pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; pub use crate::traits::{DebugFormatter, FormatDebug, ToDebug};
pub use crate::utils::{did_you_mean, AbsoluteFile, AbsolutePath, RelativePath};
pub use cli::cli; pub use cli::cli;
pub use data::base::{Primitive, Value}; pub use data::base::{Primitive, Value};
pub use data::config::{config_path, APP_INFO}; pub use data::config::{config_path, APP_INFO};
pub use data::dict::{Dictionary, TaggedDictBuilder}; pub use data::dict::{Dictionary, TaggedDictBuilder};
pub use data::meta::{Span, Spanned, SpannedItem, Tag, Tagged, TaggedItem}; pub use data::meta::{
tag_for_tagged_list, HasFallibleSpan, HasSpan, Span, Spanned, SpannedItem, Tag, Tagged,
TaggedItem,
};
pub use errors::{CoerceInto, ShellError}; pub use errors::{CoerceInto, ShellError};
pub use num_traits::cast::ToPrimitive; pub use num_traits::cast::ToPrimitive;
pub use parser::parse::text::Text; pub use parser::parse::text::Text;

View File

@ -19,6 +19,12 @@ fn main() -> Result<(), Box<dyn Error>> {
.multiple(true) .multiple(true)
.takes_value(true), .takes_value(true),
) )
.arg(
Arg::with_name("debug")
.long("debug")
.multiple(true)
.takes_value(true),
)
.get_matches(); .get_matches();
let loglevel = match matches.value_of("loglevel") { let loglevel = match matches.value_of("loglevel") {
@ -48,6 +54,15 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
} }
match matches.values_of("debug") {
None => {}
Some(values) => {
for item in values {
builder.filter_module(&format!("nu::{}", item), LevelFilter::Debug);
}
}
}
builder.try_init()?; builder.try_init()?;
futures::executor::block_on(nu::cli())?; futures::executor::block_on(nu::cli())?;

View File

@ -24,7 +24,6 @@ pub(crate) use self::external_command::ExternalCommand;
pub(crate) use self::named::NamedArguments; pub(crate) use self::named::NamedArguments;
pub(crate) use self::path::Path; pub(crate) use self::path::Path;
pub(crate) use self::syntax_shape::ExpandContext; pub(crate) use self::syntax_shape::ExpandContext;
pub(crate) use self::tokens_iterator::debug::debug_tokens;
pub(crate) use self::tokens_iterator::TokensIterator; pub(crate) use self::tokens_iterator::TokensIterator;
pub use self::syntax_shape::SyntaxShape; pub use self::syntax_shape::SyntaxShape;
@ -50,8 +49,8 @@ impl Call {
} }
} }
impl ToDebug for Call { impl FormatDebug for Call {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "({}", self.head.debug(source))?; write!(f, "({}", self.head.debug(source))?;
if let Some(positional) = &self.positional { if let Some(positional) = &self.positional {
@ -242,10 +241,14 @@ impl Expression {
pub(crate) fn it_variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression { pub(crate) fn it_variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
RawExpression::Variable(Variable::It(inner.into())).spanned(outer) RawExpression::Variable(Variable::It(inner.into())).spanned(outer)
} }
pub(crate) fn tagged_type_name(&self) -> Tagged<&'static str> {
self.item.type_name().tagged(self.span)
}
} }
impl ToDebug for Expression { impl FormatDebug for Spanned<RawExpression> {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match &self.item { match &self.item {
RawExpression::Literal(l) => l.spanned(self.span).fmt_debug(f, source), RawExpression::Literal(l) => l.spanned(self.span).fmt_debug(f, source),
RawExpression::FilePath(p) => write!(f, "{}", p.display()), RawExpression::FilePath(p) => write!(f, "{}", p.display()),
@ -256,7 +259,7 @@ impl ToDebug for Expression {
RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)), RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)),
RawExpression::Binary(b) => write!(f, "{}", b.debug(source)), RawExpression::Binary(b) => write!(f, "{}", b.debug(source)),
RawExpression::ExternalCommand(c) => write!(f, "^{}", c.name().slice(source)), RawExpression::ExternalCommand(c) => write!(f, "^{}", c.name().slice(source)),
RawExpression::Block(exprs) => { RawExpression::Block(exprs) => f.say_block("block", |f| {
write!(f, "{{ ")?; write!(f, "{{ ")?;
for expr in exprs { for expr in exprs {
@ -264,8 +267,8 @@ impl ToDebug for Expression {
} }
write!(f, "}}") write!(f, "}}")
} }),
RawExpression::List(exprs) => { RawExpression::List(exprs) => f.say_block("list", |f| {
write!(f, "[ ")?; write!(f, "[ ")?;
for expr in exprs { for expr in exprs {
@ -273,7 +276,7 @@ impl ToDebug for Expression {
} }
write!(f, "]") write!(f, "]")
} }),
RawExpression::Path(p) => write!(f, "{}", p.debug(source)), RawExpression::Path(p) => write!(f, "{}", p.debug(source)),
RawExpression::Boolean(true) => write!(f, "$yes"), RawExpression::Boolean(true) => write!(f, "$yes"),
RawExpression::Boolean(false) => write!(f, "$no"), RawExpression::Boolean(false) => write!(f, "$no"),
@ -321,14 +324,14 @@ impl std::fmt::Display for Tagged<&Literal> {
} }
} }
impl ToDebug for Spanned<&Literal> { impl FormatDebug for Spanned<&Literal> {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self.item { match self.item {
Literal::Number(number) => write!(f, "{:?}", number), Literal::Number(..) => f.say_str("number", self.span.slice(source)),
Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), Literal::Size(..) => f.say_str("size", self.span.slice(source)),
Literal::String(tag) => write!(f, "{}", tag.slice(source)), Literal::String(..) => f.say_str("string", self.span.slice(source)),
Literal::GlobPattern(_) => write!(f, "{}", self.span.slice(source)), Literal::GlobPattern(..) => f.say_str("glob", self.span.slice(source)),
Literal::Bare => write!(f, "{}", self.span.slice(source)), Literal::Bare => f.say_str("word", self.span.slice(source)),
} }
} }
} }
@ -359,3 +362,9 @@ impl std::fmt::Display for Variable {
} }
} }
} }
impl FormatDebug for Spanned<Variable> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "{}", self.span.slice(source))
}
}

View File

@ -6,7 +6,7 @@ use crate::parser::hir::syntax_shape::*;
use crate::parser::hir::TokensIterator; use crate::parser::hir::TokensIterator;
use crate::parser::parse::token_tree_builder::{CurriedToken, TokenTreeBuilder as b}; use crate::parser::parse::token_tree_builder::{CurriedToken, TokenTreeBuilder as b};
use crate::parser::TokenNode; use crate::parser::TokenNode;
use crate::{Span, SpannedItem, Tag, Text}; use crate::{HasSpan, Span, SpannedItem, Tag, Text};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use std::fmt::Debug; use std::fmt::Debug;
@ -63,7 +63,9 @@ fn test_parse_command() {
vec![b::bare("ls"), b::sp(), b::pattern("*.txt")], vec![b::bare("ls"), b::sp(), b::pattern("*.txt")],
|tokens| { |tokens| {
let bare = tokens[0].expect_bare(); let bare = tokens[0].expect_bare();
let pattern = tokens[2].expect_pattern(); let pat = tokens[2].expect_pattern();
eprintln!("{:?} {:?} {:?}", bare, pat, bare.until(pat));
ClassifiedCommand::Internal(InternalCommand::new( ClassifiedCommand::Internal(InternalCommand::new(
"ls".to_string(), "ls".to_string(),
@ -73,9 +75,10 @@ fn test_parse_command() {
}, },
hir::Call { hir::Call {
head: Box::new(hir::RawExpression::Command(bare).spanned(bare)), head: Box::new(hir::RawExpression::Command(bare).spanned(bare)),
positional: Some(vec![hir::Expression::pattern("*.txt", pattern)]), positional: Some(vec![hir::Expression::pattern("*.txt", pat)]),
named: None, named: None,
}, }
.spanned(bare.until(pat)),
)) ))
// hir::Expression::path( // hir::Expression::path(
// hir::Expression::variable(inner_var, outer_var), // hir::Expression::variable(inner_var, outer_var),
@ -86,7 +89,7 @@ fn test_parse_command() {
); );
} }
fn parse_tokens<T: Eq + Debug>( fn parse_tokens<T: Eq + HasSpan + Clone + Debug + 'static>(
shape: impl ExpandSyntax<Output = T>, shape: impl ExpandSyntax<Output = T>,
tokens: Vec<CurriedToken>, tokens: Vec<CurriedToken>,
expected: impl FnOnce(&[TokenNode]) -> T, expected: impl FnOnce(&[TokenNode]) -> T,
@ -96,19 +99,19 @@ fn parse_tokens<T: Eq + Debug>(
ExpandContext::with_empty(&Text::from(source), |context| { ExpandContext::with_empty(&Text::from(source), |context| {
let tokens = tokens.expect_list(); let tokens = tokens.expect_list();
let mut iterator = TokensIterator::all(tokens, *context.span()); let mut iterator = TokensIterator::all(tokens.item, tokens.span);
let expr = expand_syntax(&shape, &mut iterator, &context); let expr = expand_syntax(&shape, &mut iterator, &context);
let expr = match expr { let expr = match expr {
Ok(expr) => expr, Ok(expr) => expr,
Err(err) => { Err(err) => {
crate::cli::print_err(err, &BasicHost, context.source().clone()); crate::cli::print_err(err.into(), &BasicHost, context.source().clone());
panic!("Parse failed"); panic!("Parse failed");
} }
}; };
assert_eq!(expr, expected(tokens)); assert_eq!(expr, expected(tokens.item));
}) })
} }

View File

@ -22,8 +22,8 @@ impl fmt::Display for Binary {
} }
} }
impl ToDebug for Binary { impl FormatDebug for Binary {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "{}", self.left.debug(source))?; write!(f, "{}", self.left.debug(source))?;
write!(f, " {} ", self.op.debug(source))?; write!(f, " {} ", self.op.debug(source))?;
write!(f, "{}", self.right.debug(source))?; write!(f, "{}", self.right.debug(source))?;

View File

@ -1,35 +1,55 @@
use crate::errors::ShellError; use crate::errors::ParseError;
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
use crate::parser::hir::syntax_shape::FlatShape; use crate::parser::hir::syntax_shape::FlatShape;
use crate::parser::{ use crate::parser::{
hir::syntax_shape::{ hir::syntax_shape::{
color_syntax, expand_atom, AtomicToken, ColorSyntax, ExpandContext, ExpansionRule, color_syntax, expand_atom, expand_expr, expand_syntax, AtomicToken, ColorSyntax,
MaybeSpaceShape, ExpandContext, ExpandExpression, ExpandSyntax, ExpansionRule, MaybeSpaceShape,
}, },
TokenNode, TokensIterator, hir::Expression,
TokensIterator,
}; };
use crate::{Span, Spanned, Text}; use crate::{DebugFormatter, FormatDebug, Span, Spanned, SpannedItem};
use std::fmt;
pub fn expand_external_tokens(
token_nodes: &mut TokensIterator<'_>,
source: &Text,
) -> Result<Vec<Spanned<String>>, ShellError> {
let mut out: Vec<Spanned<String>> = vec![];
loop {
if let Some(span) = expand_next_expression(token_nodes)? {
out.push(span.spanned_string(source));
} else {
break;
}
}
Ok(out)
}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct ExternalTokensShape; pub struct ExternalTokensShape;
impl FormatDebug for Spanned<Vec<Spanned<String>>> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
FormatDebug::fmt_debug(&self.item, f, source)
}
}
impl ExpandSyntax for ExternalTokensShape {
type Output = Spanned<Vec<Spanned<String>>>;
fn name(&self) -> &'static str {
"external command"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
let mut out: Vec<Spanned<String>> = vec![];
let start = token_nodes.span_at_cursor();
loop {
match expand_syntax(&ExternalExpressionShape, token_nodes, context) {
Err(_) | Ok(None) => break,
Ok(Some(span)) => out.push(span.spanned_string(context.source())),
}
}
let end = token_nodes.span_at_cursor();
Ok(out.spanned(start.until(end)))
}
}
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
impl ColorSyntax for ExternalTokensShape { impl ColorSyntax for ExternalTokensShape {
type Info = (); type Info = ();
@ -85,109 +105,200 @@ impl ColorSyntax for ExternalTokensShape {
} }
} }
pub fn expand_next_expression( #[derive(Debug, Copy, Clone)]
token_nodes: &mut TokensIterator<'_>, pub struct ExternalExpressionShape;
) -> Result<Option<Span>, ShellError> {
let first = token_nodes.next_non_ws();
let first = match first { impl ExpandSyntax for ExternalExpressionShape {
None => return Ok(None), type Output = Option<Span>;
Some(v) => v,
}; fn name(&self) -> &'static str {
"external expression"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
expand_syntax(&MaybeSpaceShape, token_nodes, context)?;
let first = expand_atom(
token_nodes,
"external command",
context,
ExpansionRule::new().allow_external_command(),
)?
.span;
let first = triage_external_head(first)?;
let mut last = first; let mut last = first;
loop { loop {
let continuation = triage_continuation(token_nodes)?; let continuation = expand_expr(&ExternalContinuationShape, token_nodes, context);
if let Some(continuation) = continuation { if let Ok(continuation) = continuation {
last = continuation; last = continuation.span;
} else { } else {
break; break;
} }
} }
Ok(Some(first.until(last))) Ok(Some(first.until(last)))
}
fn triage_external_head(node: &TokenNode) -> Result<Span, ShellError> {
Ok(match node {
TokenNode::Token(token) => token.span,
TokenNode::Call(_call) => unimplemented!("TODO: OMG"),
TokenNode::Nodes(_nodes) => unimplemented!("TODO: OMG"),
TokenNode::Delimited(_delimited) => unimplemented!("TODO: OMG"),
TokenNode::Pipeline(_pipeline) => unimplemented!("TODO: OMG"),
TokenNode::Flag(flag) => flag.span,
TokenNode::Whitespace(_whitespace) => {
unreachable!("This function should be called after next_non_ws()")
} }
TokenNode::Error(_error) => unimplemented!("TODO: OMG"),
})
}
fn triage_continuation<'a, 'b>(
nodes: &'a mut TokensIterator<'b>,
) -> Result<Option<Span>, ShellError> {
let mut peeked = nodes.peek_any();
let node = match peeked.node {
None => return Ok(None),
Some(node) => node,
};
match &node {
node if node.is_whitespace() => return Ok(None),
TokenNode::Token(..) | TokenNode::Flag(..) => {}
TokenNode::Call(..) => unimplemented!("call"),
TokenNode::Nodes(..) => unimplemented!("nodes"),
TokenNode::Delimited(..) => unimplemented!("delimited"),
TokenNode::Pipeline(..) => unimplemented!("pipeline"),
TokenNode::Whitespace(..) => unimplemented!("whitespace"),
TokenNode::Error(..) => unimplemented!("error"),
}
peeked.commit();
Ok(Some(node.span()))
}
#[must_use]
enum ExternalExpressionResult {
Eof,
Processed,
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
struct ExternalExpression; struct ExternalExpression;
#[cfg(not(coloring_in_tokens))] impl ExpandSyntax for ExternalExpression {
impl ColorSyntax for ExternalExpression { type Output = Option<Span>;
type Info = ExternalExpressionResult;
type Input = ();
fn color_syntax<'a, 'b>( fn name(&self) -> &'static str {
"external expression"
}
fn expand_syntax<'a, 'b>(
&self, &self,
_input: &(),
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
shapes: &mut Vec<Spanned<FlatShape>>, ) -> Result<Self::Output, ParseError> {
) -> ExternalExpressionResult { expand_syntax(&MaybeSpaceShape, token_nodes, context)?;
let atom = match expand_atom(
token_nodes,
"external word",
context,
ExpansionRule::permissive(),
) {
Err(_) => unreachable!("TODO: separate infallible expand_atom"),
Ok(Spanned {
item: AtomicToken::Eof { .. },
..
}) => return ExternalExpressionResult::Eof,
Ok(atom) => atom,
};
atom.color_tokens(shapes); let first = expand_syntax(&ExternalHeadShape, token_nodes, context)?.span;
return ExternalExpressionResult::Processed; let mut last = first;
loop {
let continuation = expand_syntax(&ExternalContinuationShape, token_nodes, context);
if let Ok(continuation) = continuation {
last = continuation.span;
} else {
break;
}
}
Ok(Some(first.until(last)))
}
}
#[derive(Debug, Copy, Clone)]
struct ExternalHeadShape;
impl ExpandExpression for ExternalHeadShape {
fn name(&self) -> &'static str {
"external argument"
}
fn expand_expr<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<Expression, ParseError> {
match expand_atom(
token_nodes,
"external argument",
context,
ExpansionRule::new()
.allow_external_word()
.treat_size_as_word(),
)? {
atom => match &atom {
Spanned { item, span } => Ok(match item {
AtomicToken::Eof { .. } => unreachable!("ExpansionRule doesn't allow EOF"),
AtomicToken::Error { .. } => unreachable!("ExpansionRule doesn't allow Error"),
AtomicToken::Size { .. } => unreachable!("ExpansionRule treats size as word"),
AtomicToken::Whitespace { .. } => {
unreachable!("ExpansionRule doesn't allow Whitespace")
}
AtomicToken::ShorthandFlag { .. }
| AtomicToken::LonghandFlag { .. }
| AtomicToken::SquareDelimited { .. }
| AtomicToken::ParenDelimited { .. }
| AtomicToken::BraceDelimited { .. }
| AtomicToken::Pipeline { .. } => {
return Err(ParseError::mismatch(
"external command name",
atom.tagged_type_name(),
))
}
AtomicToken::ExternalCommand { command } => {
Expression::external_command(*command, *span)
}
AtomicToken::Number { number } => {
Expression::number(number.to_number(context.source()), *span)
}
AtomicToken::String { body } => Expression::string(*body, *span),
AtomicToken::ItVariable { name } => Expression::it_variable(*name, *span),
AtomicToken::Variable { name } => Expression::variable(*name, *span),
AtomicToken::ExternalWord { .. }
| AtomicToken::GlobPattern { .. }
| AtomicToken::FilePath { .. }
| AtomicToken::Word { .. }
| AtomicToken::Dot { .. }
| AtomicToken::Operator { .. } => Expression::external_command(*span, *span),
}),
},
}
}
}
#[derive(Debug, Copy, Clone)]
struct ExternalContinuationShape;
impl ExpandExpression for ExternalContinuationShape {
fn name(&self) -> &'static str {
"external argument"
}
fn expand_expr<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<Expression, ParseError> {
match expand_atom(
token_nodes,
"external argument",
context,
ExpansionRule::new()
.allow_external_word()
.treat_size_as_word(),
)? {
atom => match &atom {
Spanned { item, span } => Ok(match item {
AtomicToken::Eof { .. } => unreachable!("ExpansionRule doesn't allow EOF"),
AtomicToken::Error { .. } => unreachable!("ExpansionRule doesn't allow Error"),
AtomicToken::Number { number } => {
Expression::number(number.to_number(context.source()), *span)
}
AtomicToken::Size { .. } => unreachable!("ExpansionRule treats size as word"),
AtomicToken::ExternalCommand { .. } => {
unreachable!("ExpansionRule doesn't allow ExternalCommand")
}
AtomicToken::Whitespace { .. } => {
unreachable!("ExpansionRule doesn't allow Whitespace")
}
AtomicToken::String { body } => Expression::string(*body, *span),
AtomicToken::ItVariable { name } => Expression::it_variable(*name, *span),
AtomicToken::Variable { name } => Expression::variable(*name, *span),
AtomicToken::ExternalWord { .. }
| AtomicToken::GlobPattern { .. }
| AtomicToken::FilePath { .. }
| AtomicToken::Word { .. }
| AtomicToken::ShorthandFlag { .. }
| AtomicToken::LonghandFlag { .. }
| AtomicToken::Dot { .. }
| AtomicToken::Operator { .. } => Expression::bare(*span),
AtomicToken::SquareDelimited { .. }
| AtomicToken::ParenDelimited { .. }
| AtomicToken::BraceDelimited { .. }
| AtomicToken::Pipeline { .. } => {
return Err(ParseError::mismatch(
"external argument",
atom.tagged_type_name(),
))
}
}),
},
}
} }
} }
@ -224,3 +335,40 @@ impl ColorSyntax for ExternalExpression {
return ExternalExpressionResult::Processed; return ExternalExpressionResult::Processed;
} }
} }
#[must_use]
enum ExternalExpressionResult {
Eof,
Processed,
}
#[cfg(not(coloring_in_tokens))]
impl ColorSyntax for ExternalExpression {
type Info = ExternalExpressionResult;
type Input = ();
fn color_syntax<'a, 'b>(
&self,
_input: &(),
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
shapes: &mut Vec<Spanned<FlatShape>>,
) -> ExternalExpressionResult {
let atom = match expand_atom(
token_nodes,
"external word",
context,
ExpansionRule::permissive(),
) {
Err(_) => unreachable!("TODO: separate infallible expand_atom"),
Ok(Spanned {
item: AtomicToken::Eof { .. },
..
}) => return ExternalExpressionResult::Eof,
Ok(atom) => atom,
};
atom.color_tokens(shapes);
return ExternalExpressionResult::Processed;
}
}

View File

@ -12,8 +12,8 @@ pub struct ExternalCommand {
pub(crate) name: Span, pub(crate) name: Span,
} }
impl ToDebug for ExternalCommand { impl FormatDebug for ExternalCommand {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "{}", self.name.slice(source))?; write!(f, "{}", self.name.slice(source))?;
Ok(()) Ok(())

View File

@ -21,8 +21,8 @@ pub struct NamedArguments {
pub(crate) named: IndexMap<String, NamedValue>, pub(crate) named: IndexMap<String, NamedValue>,
} }
impl ToDebug for NamedArguments { impl FormatDebug for NamedArguments {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
for (name, value) in &self.named { for (name, value) in &self.named {
match value { match value {
NamedValue::AbsentSwitch => continue, NamedValue::AbsentSwitch => continue,

View File

@ -44,8 +44,8 @@ impl Path {
} }
} }
impl ToDebug for Path { impl FormatDebug for Path {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "{}", self.head.debug(source))?; write!(f, "{}", self.head.debug(source))?;
for part in &self.tail { for part in &self.tail {

View File

@ -11,16 +11,12 @@ use crate::parser::hir::expand_external_tokens::ExternalTokensShape;
use crate::parser::hir::syntax_shape::block::AnyBlockShape; use crate::parser::hir::syntax_shape::block::AnyBlockShape;
use crate::parser::hir::tokens_iterator::Peeked; use crate::parser::hir::tokens_iterator::Peeked;
use crate::parser::parse_command::{parse_command_tail, CommandTailShape}; use crate::parser::parse_command::{parse_command_tail, CommandTailShape};
use crate::parser::{ use crate::parser::{hir, hir::TokensIterator, Operator, RawToken, TokenNode};
hir,
hir::{debug_tokens, TokensIterator},
Operator, RawToken, TokenNode,
};
use crate::prelude::*; use crate::prelude::*;
use derive_new::new; use derive_new::new;
use getset::Getters; use getset::Getters;
use log::{self, trace};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub(crate) use self::expression::atom::{expand_atom, AtomicToken, ExpansionRule}; pub(crate) use self::expression::atom::{expand_atom, AtomicToken, ExpansionRule};
@ -40,15 +36,16 @@ pub(crate) use self::expression::variable_path::{
pub(crate) use self::expression::{continue_expression, AnyExpressionShape}; pub(crate) use self::expression::{continue_expression, AnyExpressionShape};
pub(crate) use self::flat_shape::FlatShape; pub(crate) use self::flat_shape::FlatShape;
#[cfg(not(coloring_in_tokens))]
use crate::parser::hir::tokens_iterator::debug::debug_tokens;
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
use crate::parser::parse::pipeline::Pipeline; use crate::parser::parse::pipeline::Pipeline;
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
use log::log_enabled; use log::{log_enabled, trace};
#[derive(Debug, Copy, Clone, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum SyntaxShape { pub enum SyntaxShape {
Any, Any,
List,
String, String,
Member, Member,
ColumnPath, ColumnPath,
@ -75,10 +72,6 @@ impl FallibleColorSyntax for SyntaxShape {
SyntaxShape::Any => { SyntaxShape::Any => {
color_fallible_syntax(&AnyExpressionShape, token_nodes, context, shapes) color_fallible_syntax(&AnyExpressionShape, token_nodes, context, shapes)
} }
SyntaxShape::List => {
color_syntax(&ExpressionListShape, token_nodes, context, shapes);
Ok(())
}
SyntaxShape::Int => color_fallible_syntax(&IntShape, token_nodes, context, shapes), SyntaxShape::Int => color_fallible_syntax(&IntShape, token_nodes, context, shapes),
SyntaxShape::String => color_fallible_syntax_with( SyntaxShape::String => color_fallible_syntax_with(
&StringShape, &StringShape,
@ -126,10 +119,6 @@ impl FallibleColorSyntax for SyntaxShape {
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
match self { match self {
SyntaxShape::Any => color_fallible_syntax(&AnyExpressionShape, token_nodes, context), SyntaxShape::Any => color_fallible_syntax(&AnyExpressionShape, token_nodes, context),
SyntaxShape::List => {
color_syntax(&ExpressionListShape, token_nodes, context);
Ok(())
}
SyntaxShape::Int => color_fallible_syntax(&IntShape, token_nodes, context), SyntaxShape::Int => color_fallible_syntax(&IntShape, token_nodes, context),
SyntaxShape::String => { SyntaxShape::String => {
color_fallible_syntax_with(&StringShape, &FlatShape::String, token_nodes, context) color_fallible_syntax_with(&StringShape, &FlatShape::String, token_nodes, context)
@ -147,14 +136,27 @@ impl FallibleColorSyntax for SyntaxShape {
} }
impl ExpandExpression for SyntaxShape { impl ExpandExpression for SyntaxShape {
fn name(&self) -> &'static str {
match self {
SyntaxShape::Any => "any",
SyntaxShape::Int => "integer",
SyntaxShape::String => "string",
SyntaxShape::Member => "column name",
SyntaxShape::ColumnPath => "column path",
SyntaxShape::Number => "number",
SyntaxShape::Path => "file path",
SyntaxShape::Pattern => "glob pattern",
SyntaxShape::Block => "block",
}
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
match self { match self {
SyntaxShape::Any => expand_expr(&AnyExpressionShape, token_nodes, context), SyntaxShape::Any => expand_expr(&AnyExpressionShape, token_nodes, context),
SyntaxShape::List => Err(ShellError::unimplemented("SyntaxShape:List")),
SyntaxShape::Int => expand_expr(&IntShape, token_nodes, context), SyntaxShape::Int => expand_expr(&IntShape, token_nodes, context),
SyntaxShape::String => expand_expr(&StringShape, token_nodes, context), SyntaxShape::String => expand_expr(&StringShape, token_nodes, context),
SyntaxShape::Member => { SyntaxShape::Member => {
@ -162,8 +164,9 @@ impl ExpandExpression for SyntaxShape {
Ok(syntax.to_expr()) Ok(syntax.to_expr())
} }
SyntaxShape::ColumnPath => { SyntaxShape::ColumnPath => {
let Tagged { item: members, tag } = let column_path = expand_syntax(&ColumnPathShape, token_nodes, context)?;
expand_syntax(&ColumnPathShape, token_nodes, context)?;
let Tagged { item: members, tag } = column_path.path();
Ok(hir::Expression::list( Ok(hir::Expression::list(
members.into_iter().map(|s| s.to_expr()).collect(), members.into_iter().map(|s| s.to_expr()).collect(),
@ -182,7 +185,6 @@ impl std::fmt::Display for SyntaxShape {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
SyntaxShape::Any => write!(f, "Any"), SyntaxShape::Any => write!(f, "Any"),
SyntaxShape::List => write!(f, "List"),
SyntaxShape::String => write!(f, "String"), SyntaxShape::String => write!(f, "String"),
SyntaxShape::Int => write!(f, "Integer"), SyntaxShape::Int => write!(f, "Integer"),
SyntaxShape::Member => write!(f, "Member"), SyntaxShape::Member => write!(f, "Member"),
@ -200,8 +202,6 @@ pub struct ExpandContext<'context> {
#[get = "pub(crate)"] #[get = "pub(crate)"]
registry: &'context CommandRegistry, registry: &'context CommandRegistry,
#[get = "pub(crate)"] #[get = "pub(crate)"]
span: Span,
#[get = "pub(crate)"]
source: &'context Text, source: &'context Text,
homedir: Option<PathBuf>, homedir: Option<PathBuf>,
} }
@ -221,7 +221,6 @@ impl<'context> ExpandContext<'context> {
callback(ExpandContext { callback(ExpandContext {
registry: &registry, registry: &registry,
span: Span::unknown(),
source, source,
homedir: None, homedir: None,
}) })
@ -237,11 +236,13 @@ pub trait TestSyntax: std::fmt::Debug + Copy {
} }
pub trait ExpandExpression: std::fmt::Debug + Copy { pub trait ExpandExpression: std::fmt::Debug + Copy {
fn name(&self) -> &'static str;
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError>; ) -> Result<hir::Expression, ParseError>;
} }
#[cfg(coloring_in_tokens)] #[cfg(coloring_in_tokens)]
@ -303,35 +304,49 @@ pub trait ColorSyntax: std::fmt::Debug + Copy {
} }
pub(crate) trait ExpandSyntax: std::fmt::Debug + Copy { pub(crate) trait ExpandSyntax: std::fmt::Debug + Copy {
type Output: std::fmt::Debug; type Output: HasFallibleSpan + Clone + std::fmt::Debug + 'static;
fn name(&self) -> &'static str;
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Self::Output, ShellError>; ) -> Result<Self::Output, ParseError>;
} }
pub(crate) fn expand_syntax<'a, 'b, T: ExpandSyntax>( pub(crate) fn expand_syntax<'a, 'b, T: ExpandSyntax>(
shape: &T, shape: &T,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<T::Output, ShellError> { ) -> Result<T::Output, ParseError> {
trace!(target: "nu::expand_syntax", "before {} :: {:?}", std::any::type_name::<T>(), debug_tokens(token_nodes.state(), context.source)); token_nodes.expand_frame(shape.name(), |token_nodes| {
shape.expand_syntax(token_nodes, context)
})
}
let result = shape.expand_syntax(token_nodes, context); pub(crate) fn expand_expr<'a, 'b, T: ExpandExpression>(
shape: &T,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<hir::Expression, ParseError> {
token_nodes.expand_expr_frame(shape.name(), |token_nodes| {
shape.expand_expr(token_nodes, context)
})
}
match result { #[cfg(coloring_in_tokens)]
Err(err) => { pub fn color_syntax<'a, 'b, T: ColorSyntax<Info = U, Input = ()>, U>(
trace!(target: "nu::expand_syntax", "error :: {} :: {:?}", err, debug_tokens(token_nodes.state(), context.source)); shape: &T,
Err(err) token_nodes: &'b mut TokensIterator<'a>,
} context: &ExpandContext,
) -> ((), U) {
Ok(result) => { (
trace!(target: "nu::expand_syntax", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes.state(), context.source)); (),
Ok(result) token_nodes.color_frame(shape.name(), |token_nodes| {
} shape.color_syntax(&(), token_nodes, context)
} }),
)
} }
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
@ -363,20 +378,6 @@ pub fn color_syntax<'a, 'b, T: ColorSyntax<Info = U, Input = ()>, U>(
((), result) ((), result)
} }
#[cfg(coloring_in_tokens)]
pub fn color_syntax<'a, 'b, T: ColorSyntax<Info = U, Input = ()>, U>(
shape: &T,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> ((), U) {
(
(),
token_nodes.color_frame(shape.name(), |token_nodes| {
shape.color_syntax(&(), token_nodes, context)
}),
)
}
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
pub fn color_fallible_syntax<'a, 'b, T: FallibleColorSyntax<Info = U, Input = ()>, U>( pub fn color_fallible_syntax<'a, 'b, T: FallibleColorSyntax<Info = U, Input = ()>, U>(
shape: &T, shape: &T,
@ -492,36 +493,18 @@ pub fn color_fallible_syntax_with<'a, 'b, T: FallibleColorSyntax<Info = U, Input
}) })
} }
pub(crate) fn expand_expr<'a, 'b, T: ExpandExpression>(
shape: &T,
token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext,
) -> Result<hir::Expression, ShellError> {
trace!(target: "nu::expand_expression", "before {} :: {:?}", std::any::type_name::<T>(), debug_tokens(token_nodes.state(), context.source));
let result = shape.expand_expr(token_nodes, context);
match result {
Err(err) => {
trace!(target: "nu::expand_expression", "error :: {} :: {:?}", err, debug_tokens(token_nodes.state(), context.source));
Err(err)
}
Ok(result) => {
trace!(target: "nu::expand_expression", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes.state(), context.source));
Ok(result)
}
}
}
impl<T: ExpandExpression> ExpandSyntax for T { impl<T: ExpandExpression> ExpandSyntax for T {
type Output = hir::Expression; type Output = hir::Expression;
fn name(&self) -> &'static str {
ExpandExpression::name(self)
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
ExpandExpression::expand_expr(self, token_nodes, context) ExpandExpression::expand_expr(self, token_nodes, context)
} }
} }
@ -537,7 +520,7 @@ pub trait SkipSyntax: std::fmt::Debug + Copy {
enum BarePathState { enum BarePathState {
Initial, Initial,
Seen(Span, Span), Seen(Span, Span),
Error(ShellError), Error(ParseError),
} }
impl BarePathState { impl BarePathState {
@ -549,7 +532,7 @@ impl BarePathState {
} }
} }
pub fn end(self, peeked: Peeked, reason: impl Into<String>) -> BarePathState { pub fn end(self, peeked: Peeked, reason: &'static str) -> BarePathState {
match self { match self {
BarePathState::Initial => BarePathState::Error(peeked.type_error(reason)), BarePathState::Initial => BarePathState::Error(peeked.type_error(reason)),
BarePathState::Seen(start, end) => BarePathState::Seen(start, end), BarePathState::Seen(start, end) => BarePathState::Seen(start, end),
@ -557,7 +540,7 @@ impl BarePathState {
} }
} }
pub fn into_bare(self) -> Result<Span, ShellError> { pub fn into_bare(self) -> Result<Span, ParseError> {
match self { match self {
BarePathState::Initial => unreachable!("into_bare in initial state"), BarePathState::Initial => unreachable!("into_bare in initial state"),
BarePathState::Seen(start, end) => Ok(start.until(end)), BarePathState::Seen(start, end) => Ok(start.until(end)),
@ -570,7 +553,7 @@ pub fn expand_bare<'a, 'b>(
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
_context: &ExpandContext, _context: &ExpandContext,
predicate: impl Fn(&TokenNode) -> bool, predicate: impl Fn(&TokenNode) -> bool,
) -> Result<Span, ShellError> { ) -> Result<Span, ParseError> {
let mut state = BarePathState::Initial; let mut state = BarePathState::Initial;
loop { loop {
@ -603,11 +586,15 @@ pub struct BarePathShape;
impl ExpandSyntax for BarePathShape { impl ExpandSyntax for BarePathShape {
type Output = Span; type Output = Span;
fn name(&self) -> &'static str {
"shorthand path"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Span, ShellError> { ) -> Result<Span, ParseError> {
expand_bare(token_nodes, context, |token| match token { expand_bare(token_nodes, context, |token| match token {
TokenNode::Token(Spanned { TokenNode::Token(Spanned {
item: RawToken::Bare, item: RawToken::Bare,
@ -638,7 +625,8 @@ impl FallibleColorSyntax for BareShape {
_context: &ExpandContext, _context: &ExpandContext,
shapes: &mut Vec<Spanned<FlatShape>>, shapes: &mut Vec<Spanned<FlatShape>>,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
token_nodes.peek_any_token("word", |token| match token { token_nodes
.peek_any_token("word", |token| match token {
// If it's a bare token, color it // If it's a bare token, color it
TokenNode::Token(Spanned { TokenNode::Token(Spanned {
item: RawToken::Bare, item: RawToken::Bare,
@ -649,8 +637,9 @@ impl FallibleColorSyntax for BareShape {
} }
// otherwise, fail // otherwise, fail
other => Err(ShellError::type_error("word", other.tagged_type_name())), other => Err(ParseError::mismatch("word", other.tagged_type_name())),
}) })
.map_err(|err| err.into())
} }
} }
@ -677,7 +666,7 @@ impl FallibleColorSyntax for BareShape {
}) => Ok(span), }) => Ok(span),
// otherwise, fail // otherwise, fail
other => Err(ShellError::type_error("word", other.tagged_type_name())), other => Err(ParseError::mismatch("word", other.tagged_type_name())),
})?; })?;
token_nodes.color_shape((*input).spanned(*span)); token_nodes.color_shape((*input).spanned(*span));
@ -689,11 +678,15 @@ impl FallibleColorSyntax for BareShape {
impl ExpandSyntax for BareShape { impl ExpandSyntax for BareShape {
type Output = Spanned<String>; type Output = Spanned<String>;
fn name(&self) -> &'static str {
"word"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
let peeked = token_nodes.peek_any().not_eof("word")?; let peeked = token_nodes.peek_any().not_eof("word")?;
match peeked.node { match peeked.node {
@ -705,7 +698,7 @@ impl ExpandSyntax for BareShape {
Ok(span.spanned_string(context.source)) Ok(span.spanned_string(context.source))
} }
other => Err(ShellError::type_error("word", other.tagged_type_name())), other => Err(ParseError::mismatch("word", other.tagged_type_name())),
} }
} }
} }
@ -725,7 +718,7 @@ impl TestSyntax for BareShape {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum CommandSignature { pub enum CommandSignature {
Internal(Spanned<Arc<Command>>), Internal(Spanned<Arc<Command>>),
LiteralExternal { outer: Span, inner: Span }, LiteralExternal { outer: Span, inner: Span },
@ -733,6 +726,34 @@ pub enum CommandSignature {
Expression(hir::Expression), Expression(hir::Expression),
} }
impl FormatDebug for CommandSignature {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self {
CommandSignature::Internal(internal) => {
f.say_str("internal", internal.span.slice(source))
}
CommandSignature::LiteralExternal { outer, .. } => {
f.say_str("external", outer.slice(source))
}
CommandSignature::External(external) => {
write!(f, "external:{}", external.slice(source))
}
CommandSignature::Expression(expr) => expr.fmt_debug(f, source),
}
}
}
impl HasSpan for CommandSignature {
fn span(&self) -> Span {
match self {
CommandSignature::Internal(spanned) => spanned.span,
CommandSignature::LiteralExternal { outer, .. } => *outer,
CommandSignature::External(span) => *span,
CommandSignature::Expression(expr) => expr.span,
}
}
}
impl CommandSignature { impl CommandSignature {
pub fn to_expression(&self) -> hir::Expression { pub fn to_expression(&self) -> hir::Expression {
match self { match self {
@ -833,12 +854,17 @@ impl FallibleColorSyntax for PipelineShape {
#[cfg(coloring_in_tokens)] #[cfg(coloring_in_tokens)]
impl ExpandSyntax for PipelineShape { impl ExpandSyntax for PipelineShape {
type Output = ClassifiedPipeline; type Output = ClassifiedPipeline;
fn name(&self) -> &'static str {
"pipeline"
}
fn expand_syntax<'content, 'me>( fn expand_syntax<'content, 'me>(
&self, &self,
iterator: &'me mut TokensIterator<'content>, iterator: &'me mut TokensIterator<'content>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
let source = context.source; let start = iterator.span_at_cursor();
let peeked = iterator.peek_any().not_eof("pipeline")?; let peeked = iterator.peek_any().not_eof("pipeline")?;
let pipeline = peeked.commit().as_pipeline()?; let pipeline = peeked.commit().as_pipeline()?;
@ -851,25 +877,34 @@ impl ExpandSyntax for PipelineShape {
let tokens: Spanned<&[TokenNode]> = (&part.item.tokens[..]).spanned(part.span); let tokens: Spanned<&[TokenNode]> = (&part.item.tokens[..]).spanned(part.span);
let classified = iterator.child(tokens, move |token_nodes| { let classified = iterator.child(tokens, move |token_nodes| {
classify_command(token_nodes, context, &source) expand_syntax(&ClassifiedCommandShape, token_nodes, context)
})?; })?;
out.push(classified); out.push(classified);
} }
Ok(ClassifiedPipeline { commands: out }) let end = iterator.span_at_cursor();
Ok(ClassifiedPipeline {
commands: out.spanned(start.until(end)),
})
} }
} }
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
impl ExpandSyntax for PipelineShape { impl ExpandSyntax for PipelineShape {
type Output = ClassifiedPipeline; type Output = ClassifiedPipeline;
fn name(&self) -> &'static str {
"pipeline"
}
fn expand_syntax<'content, 'me>( fn expand_syntax<'content, 'me>(
&self, &self,
iterator: &'me mut TokensIterator<'content>, iterator: &'me mut TokensIterator<'content>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
let source = context.source; let start = iterator.span_at_cursor();
let peeked = iterator.peek_any().not_eof("pipeline")?; let peeked = iterator.peek_any().not_eof("pipeline")?;
let pipeline = peeked.commit().as_pipeline()?; let pipeline = peeked.commit().as_pipeline()?;
@ -882,13 +917,17 @@ impl ExpandSyntax for PipelineShape {
let tokens: Spanned<&[TokenNode]> = (&part.item.tokens[..]).spanned(part.span); let tokens: Spanned<&[TokenNode]> = (&part.item.tokens[..]).spanned(part.span);
let classified = iterator.child(tokens, move |token_nodes| { let classified = iterator.child(tokens, move |token_nodes| {
classify_command(token_nodes, context, &source) expand_syntax(&ClassifiedCommandShape, token_nodes, context)
})?; })?;
out.push(classified); out.push(classified);
} }
Ok(ClassifiedPipeline { commands: out }) let end = iterator.span_at_cursor();
Ok(ClassifiedPipeline {
commands: out.spanned(start.until(end)),
})
} }
} }
@ -1014,11 +1053,15 @@ impl FallibleColorSyntax for CommandHeadShape {
impl ExpandSyntax for CommandHeadShape { impl ExpandSyntax for CommandHeadShape {
type Output = CommandSignature; type Output = CommandSignature;
fn name(&self) -> &'static str {
"command head"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<CommandSignature, ShellError> { ) -> Result<CommandSignature, ParseError> {
let node = let node =
parse_single_node_skipping_ws(token_nodes, "command head1", |token, token_span, _| { parse_single_node_skipping_ws(token_nodes, "command head1", |token, token_span, _| {
Ok(match token { Ok(match token {
@ -1060,29 +1103,34 @@ pub struct ClassifiedCommandShape;
impl ExpandSyntax for ClassifiedCommandShape { impl ExpandSyntax for ClassifiedCommandShape {
type Output = ClassifiedCommand; type Output = ClassifiedCommand;
fn name(&self) -> &'static str {
"classified command"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
iterator: &'b mut TokensIterator<'a>, iterator: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
let start = iterator.span_at_cursor();
let head = expand_syntax(&CommandHeadShape, iterator, context)?; let head = expand_syntax(&CommandHeadShape, iterator, context)?;
match &head { match &head {
CommandSignature::Expression(expr) => Err(ShellError::syntax_error( CommandSignature::Expression(expr) => {
"Unexpected expression in command position".tagged(expr.span), Err(ParseError::mismatch("command", expr.tagged_type_name()))
)), }
// If the command starts with `^`, treat it as an external command no matter what // If the command starts with `^`, treat it as an external command no matter what
CommandSignature::External(name) => { CommandSignature::External(name) => {
let name_str = name.slice(&context.source); let name_str = name.slice(&context.source);
external_command(iterator, &context.source, name_str.tagged(name)) external_command(iterator, context, name_str.tagged(name))
} }
CommandSignature::LiteralExternal { outer, inner } => { CommandSignature::LiteralExternal { outer, inner } => {
let name_str = inner.slice(&context.source); let name_str = inner.slice(&context.source);
external_command(iterator, &context.source, name_str.tagged(outer)) external_command(iterator, context, name_str.tagged(outer))
} }
CommandSignature::Internal(command) => { CommandSignature::Internal(command) => {
@ -1094,11 +1142,14 @@ impl ExpandSyntax for ClassifiedCommandShape {
Some((positional, named)) => (positional, named), Some((positional, named)) => (positional, named),
}; };
let end = iterator.span_at_cursor();
let call = hir::Call { let call = hir::Call {
head: Box::new(head.to_expression()), head: Box::new(head.to_expression()),
positional, positional,
named, named,
}; }
.spanned(start.until(end));
Ok(ClassifiedCommand::Internal(InternalCommand::new( Ok(ClassifiedCommand::Internal(InternalCommand::new(
command.item.name().to_string(), command.item.name().to_string(),
@ -1198,12 +1249,16 @@ impl FallibleColorSyntax for InternalCommandHeadShape {
} }
impl ExpandExpression for InternalCommandHeadShape { impl ExpandExpression for InternalCommandHeadShape {
fn name(&self) -> &'static str {
"internal command head"
}
fn expand_expr( fn expand_expr(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
_context: &ExpandContext, _context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
let peeked_head = token_nodes.peek_non_ws().not_eof("command head4")?; let peeked_head = token_nodes.peek_non_ws().not_eof("command head")?;
let expr = match peeked_head.node { let expr = match peeked_head.node {
TokenNode::Token( TokenNode::Token(
@ -1219,8 +1274,8 @@ impl ExpandExpression for InternalCommandHeadShape {
}) => hir::RawExpression::Literal(hir::Literal::String(*inner_span)).spanned(*span), }) => hir::RawExpression::Literal(hir::Literal::String(*inner_span)).spanned(*span),
node => { node => {
return Err(ShellError::type_error( return Err(ParseError::mismatch(
"command head5", "command head",
node.tagged_type_name(), node.tagged_type_name(),
)) ))
} }
@ -1238,16 +1293,16 @@ pub(crate) struct SingleError<'token> {
} }
impl<'token> SingleError<'token> { impl<'token> SingleError<'token> {
pub(crate) fn error(&self) -> ShellError { pub(crate) fn error(&self) -> ParseError {
ShellError::type_error(self.expected, self.node.type_name().tagged(self.node.span)) ParseError::mismatch(self.expected, self.node.type_name().tagged(self.node.span))
} }
} }
fn parse_single_node<'a, 'b, T>( fn parse_single_node<'a, 'b, T>(
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
expected: &'static str, expected: &'static str,
callback: impl FnOnce(RawToken, Span, SingleError) -> Result<T, ShellError>, callback: impl FnOnce(RawToken, Span, SingleError) -> Result<T, ParseError>,
) -> Result<T, ShellError> { ) -> Result<T, ParseError> {
token_nodes.peek_any_token(expected, |node| match node { token_nodes.peek_any_token(expected, |node| match node {
TokenNode::Token(token) => callback( TokenNode::Token(token) => callback(
token.item, token.item,
@ -1258,7 +1313,7 @@ fn parse_single_node<'a, 'b, T>(
}, },
), ),
other => Err(ShellError::type_error(expected, other.tagged_type_name())), other => Err(ParseError::mismatch(expected, other.tagged_type_name())),
}) })
} }
@ -1360,22 +1415,21 @@ impl FallibleColorSyntax for WhitespaceShape {
impl ExpandSyntax for WhitespaceShape { impl ExpandSyntax for WhitespaceShape {
type Output = Span; type Output = Span;
fn name(&self) -> &'static str {
"whitespace"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
_context: &ExpandContext, _context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
let peeked = token_nodes.peek_any().not_eof("whitespace")?; let peeked = token_nodes.peek_any().not_eof("whitespace")?;
let span = match peeked.node { let span = match peeked.node {
TokenNode::Whitespace(tag) => *tag, TokenNode::Whitespace(tag) => *tag,
other => { other => return Err(ParseError::mismatch("whitespace", other.tagged_type_name())),
return Err(ShellError::type_error(
"whitespace",
other.tagged_type_name(),
))
}
}; };
peeked.commit(); peeked.commit();
@ -1390,11 +1444,15 @@ pub struct SpacedExpression<T: ExpandExpression> {
} }
impl<T: ExpandExpression> ExpandExpression for SpacedExpression<T> { impl<T: ExpandExpression> ExpandExpression for SpacedExpression<T> {
fn name(&self) -> &'static str {
"spaced expression"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
// TODO: Make the name part of the trait // TODO: Make the name part of the trait
let peeked = token_nodes.peek_any().not_eof("whitespace")?; let peeked = token_nodes.peek_any().not_eof("whitespace")?;
@ -1404,10 +1462,7 @@ impl<T: ExpandExpression> ExpandExpression for SpacedExpression<T> {
expand_expr(&self.inner, token_nodes, context) expand_expr(&self.inner, token_nodes, context)
} }
other => Err(ShellError::type_error( other => Err(ParseError::mismatch("whitespace", other.tagged_type_name())),
"whitespace",
other.tagged_type_name(),
)),
} }
} }
} }
@ -1424,6 +1479,36 @@ pub struct MaybeSpacedExpression<T: ExpandExpression> {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct MaybeSpaceShape; pub struct MaybeSpaceShape;
impl ExpandSyntax for MaybeSpaceShape {
type Output = Option<Span>;
fn name(&self) -> &'static str {
"maybe space"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
_context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
let peeked = token_nodes.peek_any().not_eof("whitespace");
let span = match peeked {
Err(_) => None,
Ok(peeked) => {
if let TokenNode::Whitespace(..) = peeked.node {
let node = peeked.commit();
Some(node.span())
} else {
None
}
}
};
Ok(span)
}
}
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
impl ColorSyntax for MaybeSpaceShape { impl ColorSyntax for MaybeSpaceShape {
type Info = (); type Info = ();
@ -1544,11 +1629,15 @@ impl FallibleColorSyntax for SpaceShape {
} }
impl<T: ExpandExpression> ExpandExpression for MaybeSpacedExpression<T> { impl<T: ExpandExpression> ExpandExpression for MaybeSpacedExpression<T> {
fn name(&self) -> &'static str {
"maybe space"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
// TODO: Make the name part of the trait // TODO: Make the name part of the trait
let peeked = token_nodes.peek_any().not_eof("whitespace")?; let peeked = token_nodes.peek_any().not_eof("whitespace")?;
@ -1578,58 +1667,6 @@ fn expand_variable(span: Span, token_span: Span, source: &Text) -> hir::Expressi
} }
} }
fn classify_command(
mut iterator: &mut TokensIterator,
context: &ExpandContext,
source: &Text,
) -> Result<ClassifiedCommand, ShellError> {
let head = CommandHeadShape.expand_syntax(&mut iterator, &context)?;
match &head {
CommandSignature::Expression(_) => Err(ShellError::syntax_error(
"Unexpected expression in command position".tagged(iterator.whole_span()),
)),
// If the command starts with `^`, treat it as an external command no matter what
CommandSignature::External(name) => {
let name_str = name.slice(source);
external_command(&mut iterator, source, name_str.tagged(name))
}
CommandSignature::LiteralExternal { outer, inner } => {
let name_str = inner.slice(source);
external_command(&mut iterator, source, name_str.tagged(outer))
}
CommandSignature::Internal(command) => {
let tail =
parse_command_tail(&command.signature(), &context, &mut iterator, command.span)?;
let (positional, named) = match tail {
None => (None, None),
Some((positional, named)) => (positional, named),
};
let call = hir::Call {
head: Box::new(head.to_expression()),
positional,
named,
};
Ok(ClassifiedCommand::Internal(InternalCommand::new(
command.name().to_string(),
Tag {
span: command.span,
anchor: None,
},
call,
)))
}
}
}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct CommandShape; pub struct CommandShape;

View File

@ -6,7 +6,8 @@ use crate::parser::{
hir::syntax_shape::{ hir::syntax_shape::{
color_fallible_syntax, color_syntax_with, continue_expression, expand_expr, expand_syntax, color_fallible_syntax, color_syntax_with, continue_expression, expand_expr, expand_syntax,
DelimitedShape, ExpandContext, ExpandExpression, ExpressionContinuationShape, DelimitedShape, ExpandContext, ExpandExpression, ExpressionContinuationShape,
ExpressionListShape, FallibleColorSyntax, MemberShape, PathTailShape, VariablePathShape, ExpressionListShape, FallibleColorSyntax, MemberShape, ParseError, PathTailShape,
VariablePathShape,
}, },
hir::tokens_iterator::TokensIterator, hir::tokens_iterator::TokensIterator,
parse::token_tree::Delimiter, parse::token_tree::Delimiter,
@ -42,7 +43,7 @@ impl FallibleColorSyntax for AnyBlockShape {
match block { match block {
// If so, color it as a block // If so, color it as a block
Some((children, spans)) => { Some((children, spans)) => {
let mut token_nodes = TokensIterator::new(children.item, context.span, false); let mut token_nodes = TokensIterator::new(children.item, children.span, false);
color_syntax_with( color_syntax_with(
&DelimitedShape, &DelimitedShape,
&(Delimiter::Brace, spans.0, spans.1), &(Delimiter::Brace, spans.0, spans.1),
@ -109,11 +110,15 @@ impl FallibleColorSyntax for AnyBlockShape {
} }
impl ExpandExpression for AnyBlockShape { impl ExpandExpression for AnyBlockShape {
fn name(&self) -> &'static str {
"any block"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
let block = token_nodes.peek_non_ws().not_eof("block")?; let block = token_nodes.peek_non_ws().not_eof("block")?;
// is it just a block? // is it just a block?
@ -121,11 +126,11 @@ impl ExpandExpression for AnyBlockShape {
match block { match block {
Some((block, _tags)) => { Some((block, _tags)) => {
let mut iterator = TokensIterator::new(&block.item, context.span, false); let mut iterator = TokensIterator::new(&block.item, block.span, false);
let exprs = expand_syntax(&ExpressionListShape, &mut iterator, context)?; let exprs = expand_syntax(&ExpressionListShape, &mut iterator, context)?;
return Ok(hir::RawExpression::Block(exprs).spanned(block.span)); return Ok(hir::RawExpression::Block(exprs.item).spanned(block.span));
} }
_ => {} _ => {}
} }
@ -204,14 +209,18 @@ impl FallibleColorSyntax for ShorthandBlock {
} }
impl ExpandExpression for ShorthandBlock { impl ExpandExpression for ShorthandBlock {
fn name(&self) -> &'static str {
"shorthand block"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
let path = expand_expr(&ShorthandPath, token_nodes, context)?; let path = expand_expr(&ShorthandPath, token_nodes, context)?;
let start = path.span; let start = path.span;
let expr = continue_expression(path, token_nodes, context)?; let expr = continue_expression(path, token_nodes, context);
let end = expr.span; let end = expr.span;
let block = hir::RawExpression::Block(vec![expr]).spanned(start.until(end)); let block = hir::RawExpression::Block(vec![expr]).spanned(start.until(end));
@ -317,11 +326,15 @@ impl FallibleColorSyntax for ShorthandPath {
} }
impl ExpandExpression for ShorthandPath { impl ExpandExpression for ShorthandPath {
fn name(&self) -> &'static str {
"shorthand path"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
// if it's a variable path, that's the head part // if it's a variable path, that's the head part
let path = expand_expr(&VariablePathShape, token_nodes, context); let path = expand_expr(&VariablePathShape, token_nodes, context);
@ -339,7 +352,7 @@ impl ExpandExpression for ShorthandPath {
match tail { match tail {
Err(_) => return Ok(head), Err(_) => return Ok(head),
Ok((tail, _)) => { Ok(Spanned { item: tail, .. }) => {
// For each member that `PathTailShape` expanded, join it onto the existing expression // For each member that `PathTailShape` expanded, join it onto the existing expression
// to form a new path // to form a new path
for member in tail { for member in tail {
@ -446,11 +459,15 @@ impl FallibleColorSyntax for ShorthandHeadShape {
} }
impl ExpandExpression for ShorthandHeadShape { impl ExpandExpression for ShorthandHeadShape {
fn name(&self) -> &'static str {
"shorthand head"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
// A shorthand path must not be at EOF // A shorthand path must not be at EOF
let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?; let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?;
@ -495,7 +512,7 @@ impl ExpandExpression for ShorthandHeadShape {
// Any other token is not a valid bare head // Any other token is not a valid bare head
other => { other => {
return Err(ShellError::type_error( return Err(ParseError::mismatch(
"shorthand path", "shorthand path",
other.tagged_type_name(), other.tagged_type_name(),
)) ))

View File

@ -12,7 +12,7 @@ use crate::parser::hir::syntax_shape::{
color_delimited_square, color_fallible_syntax, color_fallible_syntax_with, expand_atom, color_delimited_square, color_fallible_syntax, color_fallible_syntax_with, expand_atom,
expand_delimited_square, expand_expr, expand_syntax, AtomicToken, BareShape, ColorableDotShape, expand_delimited_square, expand_expr, expand_syntax, AtomicToken, BareShape, ColorableDotShape,
DotShape, ExpandContext, ExpandExpression, ExpandSyntax, ExpansionRule, ExpressionContinuation, DotShape, ExpandContext, ExpandExpression, ExpandSyntax, ExpansionRule, ExpressionContinuation,
ExpressionContinuationShape, FallibleColorSyntax, FlatShape, ExpressionContinuationShape, FallibleColorSyntax, FlatShape, ParseError,
}; };
use crate::parser::{ use crate::parser::{
hir, hir,
@ -25,15 +25,19 @@ use std::path::PathBuf;
pub struct AnyExpressionShape; pub struct AnyExpressionShape;
impl ExpandExpression for AnyExpressionShape { impl ExpandExpression for AnyExpressionShape {
fn name(&self) -> &'static str {
"any expression"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
// Look for an expression at the cursor // Look for an expression at the cursor
let head = expand_expr(&AnyExpressionStartShape, token_nodes, context)?; let head = expand_expr(&AnyExpressionStartShape, token_nodes, context)?;
continue_expression(head, token_nodes, context) Ok(continue_expression(head, token_nodes, context))
} }
} }
@ -98,14 +102,14 @@ pub(crate) fn continue_expression(
mut head: hir::Expression, mut head: hir::Expression,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> hir::Expression {
loop { loop {
// Check to see whether there's any continuation after the head expression // Check to see whether there's any continuation after the head expression
let continuation = expand_syntax(&ExpressionContinuationShape, token_nodes, context); let continuation = expand_syntax(&ExpressionContinuationShape, token_nodes, context);
match continuation { match continuation {
// If there's no continuation, return the head // If there's no continuation, return the head
Err(_) => return Ok(head), Err(_) => return head,
// Otherwise, form a new expression by combining the head with the continuation // Otherwise, form a new expression by combining the head with the continuation
Ok(continuation) => match continuation { Ok(continuation) => match continuation {
// If the continuation is a `.member`, form a path with the new member // If the continuation is a `.member`, form a path with the new member
@ -174,11 +178,15 @@ pub(crate) fn continue_coloring_expression(
pub struct AnyExpressionStartShape; pub struct AnyExpressionStartShape;
impl ExpandExpression for AnyExpressionStartShape { impl ExpandExpression for AnyExpressionStartShape {
fn name(&self) -> &'static str {
"any expression start"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
let atom = expand_atom(token_nodes, "expression", context, ExpansionRule::new())?; let atom = expand_atom(token_nodes, "expression", context, ExpansionRule::new())?;
match atom.item { match atom.item {
@ -445,13 +453,17 @@ impl FallibleColorSyntax for BareTailShape {
} }
impl ExpandSyntax for BareTailShape { impl ExpandSyntax for BareTailShape {
fn name(&self) -> &'static str {
"word continuation"
}
type Output = Option<Span>; type Output = Option<Span>;
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Option<Span>, ShellError> { ) -> Result<Option<Span>, ParseError> {
let mut end: Option<Span> = None; let mut end: Option<Span> = None;
loop { loop {

View File

@ -90,40 +90,40 @@ impl<'tokens> SpannedAtomicToken<'tokens> {
&self, &self,
context: &ExpandContext, context: &ExpandContext,
expected: &'static str, expected: &'static str,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
Ok(match &self.item { Ok(match &self.item {
AtomicToken::Eof { .. } => { AtomicToken::Eof { .. } => {
return Err(ShellError::type_error( return Err(ParseError::mismatch(
expected, expected,
"eof atomic token".tagged(self.span), "eof atomic token".tagged(self.span),
)) ))
} }
AtomicToken::Error { .. } => { AtomicToken::Error { .. } => {
return Err(ShellError::type_error( return Err(ParseError::mismatch(
expected, expected,
"eof atomic token".tagged(self.span), "eof atomic token".tagged(self.span),
)) ))
} }
AtomicToken::Operator { .. } => { AtomicToken::Operator { .. } => {
return Err(ShellError::type_error( return Err(ParseError::mismatch(expected, "operator".tagged(self.span)))
expected,
"operator".tagged(self.span),
))
} }
AtomicToken::ShorthandFlag { .. } => { AtomicToken::ShorthandFlag { .. } => {
return Err(ShellError::type_error( return Err(ParseError::mismatch(
expected, expected,
"shorthand flag".tagged(self.span), "shorthand flag".tagged(self.span),
)) ))
} }
AtomicToken::LonghandFlag { .. } => { AtomicToken::LonghandFlag { .. } => {
return Err(ShellError::type_error(expected, "flag".tagged(self.span))) return Err(ParseError::mismatch(expected, "flag".tagged(self.span)))
} }
AtomicToken::Whitespace { .. } => { AtomicToken::Whitespace { .. } => {
return Err(ShellError::unimplemented("whitespace in AtomicToken")) return Err(ParseError::mismatch(
expected,
"whitespace".tagged(self.span),
))
} }
AtomicToken::Dot { .. } => { AtomicToken::Dot { .. } => {
return Err(ShellError::type_error(expected, "dot".tagged(self.span))) return Err(ParseError::mismatch(expected, "dot".tagged(self.span)))
} }
AtomicToken::Number { number } => { AtomicToken::Number { number } => {
Expression::number(number.to_number(context.source), self.span) Expression::number(number.to_number(context.source), self.span)
@ -381,7 +381,7 @@ pub fn expand_atom<'me, 'content>(
expected: &'static str, expected: &'static str,
context: &ExpandContext, context: &ExpandContext,
rule: ExpansionRule, rule: ExpansionRule,
) -> Result<SpannedAtomicToken<'content>, ShellError> { ) -> Result<SpannedAtomicToken<'content>, ParseError> {
if token_nodes.at_end() { if token_nodes.at_end() {
match rule.allow_eof { match rule.allow_eof {
true => { true => {
@ -390,7 +390,7 @@ pub fn expand_atom<'me, 'content>(
} }
.spanned(Span::unknown())) .spanned(Span::unknown()))
} }
false => return Err(ShellError::unexpected_eof("anything", Tag::unknown())), false => return Err(ParseError::unexpected_eof("anything", Span::unknown())),
} }
} }
@ -515,12 +515,13 @@ pub fn expand_atom<'me, 'content>(
// if whitespace is disallowed, return an error // if whitespace is disallowed, return an error
WhitespaceHandling::RejectWhitespace => { WhitespaceHandling::RejectWhitespace => {
return Err(ShellError::syntax_error("Unexpected whitespace".tagged( return Err(ParseError::mismatch(
Tag { expected,
"whitespace".tagged(Tag {
span: *span, span: *span,
anchor: None, anchor: None,
}, }),
))) ))
} }
}, },
@ -544,7 +545,7 @@ pub fn expand_atom<'me, 'content>(
RawToken::Operator(_) if !rule.allow_operator => return Err(err.error()), RawToken::Operator(_) if !rule.allow_operator => return Err(err.error()),
// rule.allow_external_command // rule.allow_external_command
RawToken::ExternalCommand(_) if !rule.allow_external_command => { RawToken::ExternalCommand(_) if !rule.allow_external_command => {
return Err(ShellError::type_error( return Err(ParseError::mismatch(
expected, expected,
token.type_name().tagged(Tag { token.type_name().tagged(Tag {
span: token_span, span: token_span,
@ -554,10 +555,13 @@ pub fn expand_atom<'me, 'content>(
} }
// rule.allow_external_word // rule.allow_external_word
RawToken::ExternalWord if !rule.allow_external_word => { RawToken::ExternalWord if !rule.allow_external_word => {
return Err(ShellError::invalid_external_word(Tag { return Err(ParseError::mismatch(
expected,
"external word".tagged(Tag {
span: token_span, span: token_span,
anchor: None, anchor: None,
})) }),
))
} }
RawToken::Number(number) => AtomicToken::Number { number }.spanned(token_span), RawToken::Number(number) => AtomicToken::Number { number }.spanned(token_span),

View File

@ -8,12 +8,15 @@ pub fn expand_delimited_square(
children: &Vec<TokenNode>, children: &Vec<TokenNode>,
span: Span, span: Span,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
let mut tokens = TokensIterator::new(&children, span, false); let mut tokens = TokensIterator::new(&children, span, false);
let list = expand_syntax(&ExpressionListShape, &mut tokens, context); let list = expand_syntax(&ExpressionListShape, &mut tokens, context);
Ok(hir::Expression::list(list?, Tag { span, anchor: None })) Ok(hir::Expression::list(
list?.item,
Tag { span, anchor: None },
))
} }
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]

View File

@ -1,6 +1,7 @@
use crate::parser::hir::syntax_shape::expression::atom::{expand_atom, AtomicToken, ExpansionRule}; use crate::parser::hir::syntax_shape::expression::atom::{expand_atom, AtomicToken, ExpansionRule};
use crate::parser::hir::syntax_shape::{ use crate::parser::hir::syntax_shape::{
expression::expand_file_path, ExpandContext, ExpandExpression, FallibleColorSyntax, FlatShape, expression::expand_file_path, ExpandContext, ExpandExpression, FallibleColorSyntax, FlatShape,
ParseError,
}; };
use crate::parser::{hir, hir::TokensIterator}; use crate::parser::{hir, hir::TokensIterator};
use crate::prelude::*; use crate::prelude::*;
@ -90,11 +91,15 @@ impl FallibleColorSyntax for FilePathShape {
} }
impl ExpandExpression for FilePathShape { impl ExpandExpression for FilePathShape {
fn name(&self) -> &'static str {
"file path"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
let atom = expand_atom(token_nodes, "file path", context, ExpansionRule::new())?; let atom = expand_atom(token_nodes, "file path", context, ExpansionRule::new())?;
match atom.item { match atom.item {

View File

@ -1,4 +1,4 @@
use crate::errors::ShellError; use crate::errors::ParseError;
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
use crate::parser::hir::syntax_shape::FlatShape; use crate::parser::hir::syntax_shape::FlatShape;
use crate::parser::{ use crate::parser::{
@ -10,24 +10,36 @@ use crate::parser::{
}, },
hir::TokensIterator, hir::TokensIterator,
}; };
#[cfg(not(coloring_in_tokens))] use crate::{DebugFormatter, FormatDebug, Spanned, SpannedItem};
use crate::Spanned; use std::fmt;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct ExpressionListShape; pub struct ExpressionListShape;
impl FormatDebug for Spanned<Vec<hir::Expression>> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
FormatDebug::fmt_debug(&self.item, f, source)
}
}
impl ExpandSyntax for ExpressionListShape { impl ExpandSyntax for ExpressionListShape {
type Output = Vec<hir::Expression>; type Output = Spanned<Vec<hir::Expression>>;
fn name(&self) -> &'static str {
"expression list"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Vec<hir::Expression>, ShellError> { ) -> Result<Spanned<Vec<hir::Expression>>, ParseError> {
let mut exprs = vec![]; let mut exprs = vec![];
let start = token_nodes.span_at_cursor();
if token_nodes.at_end_possible_ws() { if token_nodes.at_end_possible_ws() {
return Ok(exprs); return Ok(exprs.spanned(start));
} }
let expr = expand_expr(&maybe_spaced(AnyExpressionShape), token_nodes, context)?; let expr = expand_expr(&maybe_spaced(AnyExpressionShape), token_nodes, context)?;
@ -36,7 +48,8 @@ impl ExpandSyntax for ExpressionListShape {
loop { loop {
if token_nodes.at_end_possible_ws() { if token_nodes.at_end_possible_ws() {
return Ok(exprs); let end = token_nodes.span_at_cursor();
return Ok(exprs.spanned(start.until(end)));
} }
let expr = expand_expr(&spaced(AnyExpressionShape), token_nodes, context)?; let expr = expand_expr(&spaced(AnyExpressionShape), token_nodes, context)?;

View File

@ -1,6 +1,6 @@
use crate::parser::hir::syntax_shape::{ use crate::parser::hir::syntax_shape::{
expand_atom, parse_single_node, ExpandContext, ExpandExpression, ExpansionRule, expand_atom, parse_single_node, ExpandContext, ExpandExpression, ExpansionRule,
FallibleColorSyntax, FlatShape, FallibleColorSyntax, FlatShape, ParseError,
}; };
use crate::parser::{ use crate::parser::{
hir, hir,
@ -13,11 +13,15 @@ use crate::prelude::*;
pub struct NumberShape; pub struct NumberShape;
impl ExpandExpression for NumberShape { impl ExpandExpression for NumberShape {
fn name(&self) -> &'static str {
"number"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
parse_single_node(token_nodes, "Number", |token, token_span, err| { parse_single_node(token_nodes, "Number", |token, token_span, err| {
Ok(match token { Ok(match token {
RawToken::GlobPattern | RawToken::Operator(..) => return Err(err.error()), RawToken::GlobPattern | RawToken::Operator(..) => return Err(err.error()),
@ -28,10 +32,13 @@ impl ExpandExpression for NumberShape {
hir::Expression::external_command(tag, token_span) hir::Expression::external_command(tag, token_span)
} }
RawToken::ExternalWord => { RawToken::ExternalWord => {
return Err(ShellError::invalid_external_word(Tag { return Err(ParseError::mismatch(
"number",
"syntax error".tagged(Tag {
span: token_span, span: token_span,
anchor: None, anchor: None,
})) }),
))
} }
RawToken::Variable(tag) => hir::Expression::variable(tag, token_span), RawToken::Variable(tag) => hir::Expression::variable(tag, token_span),
RawToken::Number(number) => { RawToken::Number(number) => {
@ -111,16 +118,19 @@ impl FallibleColorSyntax for NumberShape {
pub struct IntShape; pub struct IntShape;
impl ExpandExpression for IntShape { impl ExpandExpression for IntShape {
fn name(&self) -> &'static str {
"integer"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
parse_single_node(token_nodes, "Integer", |token, token_span, err| { parse_single_node(token_nodes, "Integer", |token, token_span, err| {
Ok(match token { Ok(match token {
RawToken::GlobPattern | RawToken::Operator(..) => return Err(err.error()), RawToken::GlobPattern | RawToken::Operator(..) | RawToken::ExternalWord => {
RawToken::ExternalWord => { return Err(err.error())
return Err(ShellError::invalid_external_word(token_span))
} }
RawToken::Variable(span) if span.slice(context.source) == "it" => { RawToken::Variable(span) if span.slice(context.source) == "it" => {
hir::Expression::it_variable(span, token_span) hir::Expression::it_variable(span, token_span)

View File

@ -1,6 +1,6 @@
use crate::parser::hir::syntax_shape::{ use crate::parser::hir::syntax_shape::{
expand_atom, expand_bare, expression::expand_file_path, AtomicToken, ExpandContext, expand_atom, expand_bare, expression::expand_file_path, AtomicToken, ExpandContext,
ExpandExpression, ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, ExpandExpression, ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, ParseError,
}; };
use crate::parser::{hir, hir::TokensIterator, Operator, RawToken, TokenNode}; use crate::parser::{hir, hir::TokensIterator, Operator, RawToken, TokenNode};
use crate::prelude::*; use crate::prelude::*;
@ -66,11 +66,15 @@ impl FallibleColorSyntax for PatternShape {
} }
impl ExpandExpression for PatternShape { impl ExpandExpression for PatternShape {
fn name(&self) -> &'static str {
"glob pattern"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
let atom = expand_atom(token_nodes, "pattern", context, ExpansionRule::new())?; let atom = expand_atom(token_nodes, "pattern", context, ExpansionRule::new())?;
match atom.item { match atom.item {
@ -91,11 +95,15 @@ pub struct BarePatternShape;
impl ExpandSyntax for BarePatternShape { impl ExpandSyntax for BarePatternShape {
type Output = Span; type Output = Span;
fn name(&self) -> &'static str {
"bare pattern"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Span, ShellError> { ) -> Result<Span, ParseError> {
expand_bare(token_nodes, context, |token| match token { expand_bare(token_nodes, context, |token| match token {
TokenNode::Token(Spanned { TokenNode::Token(Spanned {
item: RawToken::Bare, item: RawToken::Bare,

View File

@ -1,6 +1,6 @@
use crate::parser::hir::syntax_shape::{ use crate::parser::hir::syntax_shape::{
expand_atom, expand_variable, parse_single_node, AtomicToken, ExpandContext, ExpandExpression, expand_atom, expand_variable, parse_single_node, AtomicToken, ExpandContext, ExpandExpression,
ExpansionRule, FallibleColorSyntax, FlatShape, TestSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, ParseError, TestSyntax,
}; };
use crate::parser::hir::tokens_iterator::Peeked; use crate::parser::hir::tokens_iterator::Peeked;
use crate::parser::{hir, hir::TokensIterator, RawToken}; use crate::parser::{hir, hir::TokensIterator, RawToken};
@ -75,32 +75,24 @@ impl FallibleColorSyntax for StringShape {
} }
impl ExpandExpression for StringShape { impl ExpandExpression for StringShape {
fn name(&self) -> &'static str {
"string"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
parse_single_node(token_nodes, "String", |token, token_span, _| { parse_single_node(token_nodes, "String", |token, token_span, err| {
Ok(match token { Ok(match token {
RawToken::GlobPattern => { RawToken::GlobPattern | RawToken::Operator(..) | RawToken::ExternalWord => {
return Err(ShellError::type_error( return Err(err.error())
"String",
"glob pattern".tagged(token_span),
))
}
RawToken::Operator(..) => {
return Err(ShellError::type_error(
"String",
"operator".tagged(token_span),
))
} }
RawToken::Variable(span) => expand_variable(span, token_span, &context.source), RawToken::Variable(span) => expand_variable(span, token_span, &context.source),
RawToken::ExternalCommand(span) => { RawToken::ExternalCommand(span) => {
hir::Expression::external_command(span, token_span) hir::Expression::external_command(span, token_span)
} }
RawToken::ExternalWord => {
return Err(ShellError::invalid_external_word(token_span))
}
RawToken::Number(_) => hir::Expression::bare(token_span), RawToken::Number(_) => hir::Expression::bare(token_span),
RawToken::Bare => hir::Expression::bare(token_span), RawToken::Bare => hir::Expression::bare(token_span),
RawToken::String(span) => hir::Expression::string(span, token_span), RawToken::String(span) => hir::Expression::string(span, token_span),

View File

@ -1,5 +1,5 @@
use crate::data::meta::Span; use crate::data::meta::Span;
use crate::parser::hir::syntax_shape::{ExpandContext, ExpandSyntax}; use crate::parser::hir::syntax_shape::{ExpandContext, ExpandSyntax, ParseError};
use crate::parser::parse::tokens::RawNumber; use crate::parser::parse::tokens::RawNumber;
use crate::parser::parse::unit::Unit; use crate::parser::parse::unit::Unit;
use crate::parser::{hir::TokensIterator, RawToken, TokenNode}; use crate::parser::{hir::TokensIterator, RawToken, TokenNode};
@ -9,18 +9,34 @@ use nom::bytes::complete::tag;
use nom::character::complete::digit1; use nom::character::complete::digit1;
use nom::combinator::{all_consuming, opt, value}; use nom::combinator::{all_consuming, opt, value};
use nom::IResult; use nom::IResult;
use std::fmt;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct UnitShape; pub struct UnitShape;
impl FormatDebug for Spanned<(Spanned<RawNumber>, Spanned<Unit>)> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
let dict = indexmap::indexmap! {
"number" => format!("{}", self.item.0.item.debug(source)),
"unit" => format!("{}", self.item.1.debug(source)),
};
f.say_dict("unit", dict)
}
}
impl ExpandSyntax for UnitShape { impl ExpandSyntax for UnitShape {
type Output = Spanned<(Spanned<RawNumber>, Spanned<Unit>)>; type Output = Spanned<(Spanned<RawNumber>, Spanned<Unit>)>;
fn name(&self) -> &'static str {
"unit"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Spanned<(Spanned<RawNumber>, Spanned<Unit>)>, ShellError> { ) -> Result<Spanned<(Spanned<RawNumber>, Spanned<Unit>)>, ParseError> {
let peeked = token_nodes.peek_any().not_eof("unit")?; let peeked = token_nodes.peek_any().not_eof("unit")?;
let span = match peeked.node { let span = match peeked.node {
@ -34,12 +50,7 @@ impl ExpandSyntax for UnitShape {
let unit = unit_size(span.slice(context.source), *span); let unit = unit_size(span.slice(context.source), *span);
let (_, (number, unit)) = match unit { let (_, (number, unit)) = match unit {
Err(_) => { Err(_) => return Err(ParseError::mismatch("unit", "word".tagged(Tag::unknown()))),
return Err(ShellError::type_error(
"unit",
"word".tagged(Tag::unknown()),
))
}
Ok((number, unit)) => (number, unit), Ok((number, unit)) => (number, unit),
}; };

View File

@ -1,21 +1,28 @@
use crate::parser::hir::syntax_shape::{ use crate::parser::hir::syntax_shape::{
color_fallible_syntax, color_fallible_syntax_with, expand_atom, expand_expr, expand_syntax, color_fallible_syntax, color_fallible_syntax_with, expand_atom, expand_expr, expand_syntax,
parse_single_node, AnyExpressionShape, AtomicToken, BareShape, ExpandContext, ExpandExpression, parse_single_node, AnyExpressionShape, AtomicToken, BareShape, ExpandContext, ExpandExpression,
ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, Peeked, SkipSyntax, StringShape, ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, ParseError, Peeked, SkipSyntax,
TestSyntax, WhitespaceShape, StringShape, TestSyntax, WhitespaceShape,
}; };
use crate::parser::{hir, hir::Expression, hir::TokensIterator, Operator, RawToken}; use crate::parser::{hir, hir::Expression, hir::TokensIterator, Operator, RawToken};
use crate::prelude::*; use crate::prelude::*;
use derive_new::new;
use getset::Getters;
use std::fmt;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct VariablePathShape; pub struct VariablePathShape;
impl ExpandExpression for VariablePathShape { impl ExpandExpression for VariablePathShape {
fn name(&self) -> &'static str {
"variable path"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
// 1. let the head be the first token, expecting a variable // 1. let the head be the first token, expecting a variable
// 2. let the tail be an empty list of members // 2. let the tail be an empty list of members
// 2. while the next token (excluding ws) is a dot: // 2. while the next token (excluding ws) is a dot:
@ -200,12 +207,17 @@ impl FallibleColorSyntax for PathTailShape {
} }
impl ExpandSyntax for PathTailShape { impl ExpandSyntax for PathTailShape {
type Output = (Vec<Spanned<String>>, Span); type Output = Spanned<Vec<Spanned<String>>>;
fn name(&self) -> &'static str {
"path continuation"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
let mut end: Option<Span> = None; let mut end: Option<Span> = None;
let mut tail = vec![]; let mut tail = vec![];
@ -223,7 +235,7 @@ impl ExpandSyntax for PathTailShape {
match end { match end {
None => { None => {
return Err(ShellError::type_error("path tail", { return Err(ParseError::mismatch("path tail", {
let typed_span = token_nodes.typed_span_at_cursor(); let typed_span = token_nodes.typed_span_at_cursor();
Tagged { Tagged {
@ -233,17 +245,41 @@ impl ExpandSyntax for PathTailShape {
})) }))
} }
Some(end) => Ok((tail, end)), Some(end) => Ok(tail.spanned(end)),
} }
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum ExpressionContinuation { pub enum ExpressionContinuation {
DotSuffix(Span, Spanned<String>), DotSuffix(Span, Spanned<String>),
InfixSuffix(Spanned<Operator>, Expression), InfixSuffix(Spanned<Operator>, Expression),
} }
impl FormatDebug for ExpressionContinuation {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self {
ExpressionContinuation::DotSuffix(dot, rest) => {
f.say_str("dot suffix", dot.until(rest.span).slice(source))
}
ExpressionContinuation::InfixSuffix(operator, expr) => {
f.say_str("infix suffix", operator.span.until(expr.span).slice(source))
}
}
}
}
impl HasSpan for ExpressionContinuation {
fn span(&self) -> Span {
match self {
ExpressionContinuation::DotSuffix(dot, column) => dot.until(column.span),
ExpressionContinuation::InfixSuffix(operator, expression) => {
operator.span.until(expression.span)
}
}
}
}
/// An expression continuation /// An expression continuation
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct ExpressionContinuationShape; pub struct ExpressionContinuationShape;
@ -251,11 +287,15 @@ pub struct ExpressionContinuationShape;
impl ExpandSyntax for ExpressionContinuationShape { impl ExpandSyntax for ExpressionContinuationShape {
type Output = ExpressionContinuation; type Output = ExpressionContinuation;
fn name(&self) -> &'static str {
"expression continuation"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<ExpressionContinuation, ShellError> { ) -> Result<ExpressionContinuation, ParseError> {
// Try to expand a `.` // Try to expand a `.`
let dot = expand_syntax(&DotShape, token_nodes, context); let dot = expand_syntax(&DotShape, token_nodes, context);
@ -270,7 +310,7 @@ impl ExpandSyntax for ExpressionContinuationShape {
// Otherwise, we expect an infix operator and an expression next // Otherwise, we expect an infix operator and an expression next
Err(_) => { Err(_) => {
let (_, op, _) = expand_syntax(&InfixShape, token_nodes, context)?; let (_, op, _) = expand_syntax(&InfixShape, token_nodes, context)?.item;
let next = expand_expr(&AnyExpressionShape, token_nodes, context)?; let next = expand_expr(&AnyExpressionShape, token_nodes, context)?;
Ok(ExpressionContinuation::InfixSuffix(op, next)) Ok(ExpressionContinuation::InfixSuffix(op, next))
@ -390,12 +430,16 @@ impl FallibleColorSyntax for ExpressionContinuationShape {
pub struct VariableShape; pub struct VariableShape;
impl ExpandExpression for VariableShape { impl ExpandExpression for VariableShape {
fn name(&self) -> &'static str {
"variable"
}
fn expand_expr<'a, 'b>( fn expand_expr<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ParseError> {
parse_single_node(token_nodes, "variable", |token, token_tag, _| { parse_single_node(token_nodes, "variable", |token, token_tag, err| {
Ok(match token { Ok(match token {
RawToken::Variable(tag) => { RawToken::Variable(tag) => {
if tag.slice(context.source) == "it" { if tag.slice(context.source) == "it" {
@ -404,12 +448,7 @@ impl ExpandExpression for VariableShape {
hir::Expression::variable(tag, token_tag) hir::Expression::variable(tag, token_tag)
} }
} }
_ => { _ => return Err(err.error()),
return Err(ShellError::type_error(
"variable",
token.type_name().tagged(token_tag),
))
}
}) })
}) })
} }
@ -435,7 +474,7 @@ impl FallibleColorSyntax for VariableShape {
); );
let atom = match atom { let atom = match atom {
Err(err) => return Err(err), Err(err) => return Err(err.into()),
Ok(atom) => atom, Ok(atom) => atom,
}; };
@ -476,7 +515,7 @@ impl FallibleColorSyntax for VariableShape {
); );
let atom = match atom { let atom = match atom {
Err(err) => return Err(err), Err(err) => return Err(err.into()),
Ok(atom) => atom, Ok(atom) => atom,
}; };
@ -489,7 +528,7 @@ impl FallibleColorSyntax for VariableShape {
token_nodes.color_shape(FlatShape::ItVariable.spanned(atom.span)); token_nodes.color_shape(FlatShape::ItVariable.spanned(atom.span));
Ok(()) Ok(())
} }
_ => Err(ShellError::type_error("variable", atom.tagged_type_name())), _ => Err(ParseError::mismatch("variable", atom.tagged_type_name()).into()),
} }
} }
} }
@ -500,6 +539,24 @@ pub enum Member {
Bare(Span), Bare(Span),
} }
impl FormatDebug for Member {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self {
Member::String(outer, _) => write!(f, "member ({})", outer.slice(source)),
Member::Bare(bare) => write!(f, "member ({})", bare.slice(source)),
}
}
}
impl HasSpan for Member {
fn span(&self) -> Span {
match self {
Member::String(outer, ..) => *outer,
Member::Bare(name) => *name,
}
}
}
impl Member { impl Member {
pub(crate) fn to_expr(&self) -> hir::Expression { pub(crate) fn to_expr(&self) -> hir::Expression {
match self { match self {
@ -538,7 +595,7 @@ enum ColumnPathState {
LeadingDot(Span), LeadingDot(Span),
Dot(Span, Vec<Member>, Span), Dot(Span, Vec<Member>, Span),
Member(Span, Vec<Member>), Member(Span, Vec<Member>),
Error(ShellError), Error(ParseError),
} }
impl ColumnPathState { impl ColumnPathState {
@ -546,10 +603,10 @@ impl ColumnPathState {
match self { match self {
ColumnPathState::Initial => ColumnPathState::LeadingDot(dot), ColumnPathState::Initial => ColumnPathState::LeadingDot(dot),
ColumnPathState::LeadingDot(_) => { ColumnPathState::LeadingDot(_) => {
ColumnPathState::Error(ShellError::type_error("column", "dot".tagged(dot))) ColumnPathState::Error(ParseError::mismatch("column", "dot".tagged(dot)))
} }
ColumnPathState::Dot(..) => { ColumnPathState::Dot(..) => {
ColumnPathState::Error(ShellError::type_error("column", "dot".tagged(dot))) ColumnPathState::Error(ParseError::mismatch("column", "dot".tagged(dot)))
} }
ColumnPathState::Member(tag, members) => ColumnPathState::Dot(tag, members, dot), ColumnPathState::Member(tag, members) => ColumnPathState::Dot(tag, members, dot),
ColumnPathState::Error(err) => ColumnPathState::Error(err), ColumnPathState::Error(err) => ColumnPathState::Error(err),
@ -570,20 +627,20 @@ impl ColumnPathState {
}) })
} }
ColumnPathState::Member(..) => { ColumnPathState::Member(..) => {
ColumnPathState::Error(ShellError::type_error("column", member.tagged_type_name())) ColumnPathState::Error(ParseError::mismatch("column", member.tagged_type_name()))
} }
ColumnPathState::Error(err) => ColumnPathState::Error(err), ColumnPathState::Error(err) => ColumnPathState::Error(err),
} }
} }
pub fn into_path(self, next: Peeked) -> Result<Tagged<Vec<Member>>, ShellError> { pub fn into_path(self, next: Peeked) -> Result<Tagged<Vec<Member>>, ParseError> {
match self { match self {
ColumnPathState::Initial => Err(next.type_error("column path")), ColumnPathState::Initial => Err(next.type_error("column path")),
ColumnPathState::LeadingDot(dot) => { ColumnPathState::LeadingDot(dot) => {
Err(ShellError::type_error("column", "dot".tagged(dot))) Err(ParseError::mismatch("column", "dot".tagged(dot)))
} }
ColumnPathState::Dot(_tag, _members, dot) => { ColumnPathState::Dot(_tag, _members, dot) => {
Err(ShellError::type_error("column", "dot".tagged(dot))) Err(ParseError::mismatch("column", "dot".tagged(dot)))
} }
ColumnPathState::Member(tag, tags) => Ok(tags.tagged(tag)), ColumnPathState::Member(tag, tags) => Ok(tags.tagged(tag)),
ColumnPathState::Error(err) => Err(err), ColumnPathState::Error(err) => Err(err),
@ -594,7 +651,7 @@ impl ColumnPathState {
pub fn expand_column_path<'a, 'b>( pub fn expand_column_path<'a, 'b>(
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Tagged<Vec<Member>>, ShellError> { ) -> Result<Tagged<Vec<Member>>, ParseError> {
let mut state = ColumnPathState::Initial; let mut state = ColumnPathState::Initial;
loop { loop {
@ -720,15 +777,43 @@ impl FallibleColorSyntax for ColumnPathShape {
} }
} }
impl FormatDebug for Tagged<Vec<Member>> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
self.item.fmt_debug(f, source)
}
}
#[derive(Debug, Clone, Getters, new)]
pub struct ColumnPath {
#[get = "pub"]
path: Tagged<Vec<Member>>,
}
impl HasSpan for ColumnPath {
fn span(&self) -> Span {
self.path.tag.span
}
}
impl FormatDebug for ColumnPath {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
f.say("column path", self.path.item.debug(source))
}
}
impl ExpandSyntax for ColumnPathShape { impl ExpandSyntax for ColumnPathShape {
type Output = Tagged<Vec<Member>>; type Output = ColumnPath;
fn name(&self) -> &'static str {
"column path"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
expand_column_path(token_nodes, context) Ok(ColumnPath::new(expand_column_path(token_nodes, context)?))
} }
} }
@ -806,11 +891,15 @@ impl FallibleColorSyntax for MemberShape {
impl ExpandSyntax for MemberShape { impl ExpandSyntax for MemberShape {
type Output = Member; type Output = Member;
fn name(&self) -> &'static str {
"column"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Member, ShellError> { ) -> Result<Member, ParseError> {
let bare = BareShape.test(token_nodes, context); let bare = BareShape.test(token_nodes, context);
if let Some(peeked) = bare { if let Some(peeked) = bare {
let node = peeked.not_eof("column")?.commit(); let node = peeked.not_eof("column")?.commit();
@ -906,16 +995,20 @@ impl SkipSyntax for DotShape {
impl ExpandSyntax for DotShape { impl ExpandSyntax for DotShape {
type Output = Span; type Output = Span;
fn name(&self) -> &'static str {
"dot"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
_context: &ExpandContext, _context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
parse_single_node(token_nodes, "dot", |token, token_span, _| { parse_single_node(token_nodes, "dot", |token, token_span, _| {
Ok(match token { Ok(match token {
RawToken::Operator(Operator::Dot) => token_span, RawToken::Operator(Operator::Dot) => token_span,
_ => { _ => {
return Err(ShellError::type_error( return Err(ParseError::mismatch(
"dot", "dot",
token.type_name().tagged(token_span), token.type_name().tagged(token_span),
)) ))
@ -950,7 +1043,7 @@ impl FallibleColorSyntax for InfixShape {
parse_single_node( parse_single_node(
checkpoint.iterator, checkpoint.iterator,
"infix operator", "infix operator",
|token, token_span, _| { |token, token_span, err| {
match token { match token {
// If it's an operator (and not `.`), it's a match // If it's an operator (and not `.`), it's a match
RawToken::Operator(operator) if operator != Operator::Dot => { RawToken::Operator(operator) if operator != Operator::Dot => {
@ -959,10 +1052,7 @@ impl FallibleColorSyntax for InfixShape {
} }
// Otherwise, it's not a match // Otherwise, it's not a match
_ => Err(ShellError::type_error( _ => Err(err.error()),
"infix operator",
token.type_name().tagged(token_span),
)),
} }
}, },
)?; )?;
@ -1006,7 +1096,7 @@ impl FallibleColorSyntax for InfixShape {
RawToken::Operator(operator) if operator != Operator::Dot => Ok(token_span), RawToken::Operator(operator) if operator != Operator::Dot => Ok(token_span),
// Otherwise, it's not a match // Otherwise, it's not a match
_ => Err(ShellError::type_error( _ => Err(ParseError::mismatch(
"infix operator", "infix operator",
token.type_name().tagged(token_span), token.type_name().tagged(token_span),
)), )),
@ -1026,24 +1116,63 @@ impl FallibleColorSyntax for InfixShape {
} }
} }
impl FormatDebug for Spanned<(Span, Spanned<Operator>, Span)> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
f.say_str("operator", self.item.1.span.slice(source))
}
}
impl ExpandSyntax for InfixShape { impl ExpandSyntax for InfixShape {
type Output = (Span, Spanned<Operator>, Span); type Output = Spanned<(Span, Spanned<Operator>, Span)>;
fn name(&self) -> &'static str {
"infix operator"
}
fn expand_syntax<'a, 'b>( fn expand_syntax<'a, 'b>(
&self, &self,
token_nodes: &'b mut TokensIterator<'a>, token_nodes: &'b mut TokensIterator<'a>,
context: &ExpandContext, context: &ExpandContext,
) -> Result<Self::Output, ShellError> { ) -> Result<Self::Output, ParseError> {
let checkpoint = token_nodes.checkpoint(); let mut checkpoint = token_nodes.checkpoint();
// An infix operator must be prefixed by whitespace // An infix operator must be prefixed by whitespace
let start = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?; let start = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?;
// Parse the next TokenNode after the whitespace // Parse the next TokenNode after the whitespace
let operator = parse_single_node( let operator = expand_syntax(&InfixInnerShape, &mut checkpoint.iterator, context)?;
checkpoint.iterator,
"infix operator", // An infix operator must be followed by whitespace
|token, token_span, _| { let end = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?;
checkpoint.commit();
Ok((start, operator, end).spanned(start.until(end)))
}
}
#[derive(Debug, Copy, Clone)]
pub struct InfixInnerShape;
impl FormatDebug for Spanned<Operator> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
f.say_str("operator", self.span.slice(source))
}
}
impl ExpandSyntax for InfixInnerShape {
type Output = Spanned<Operator>;
fn name(&self) -> &'static str {
"infix inner"
}
fn expand_syntax<'a, 'b>(
&self,
token_nodes: &'b mut TokensIterator<'a>,
_context: &ExpandContext,
) -> Result<Self::Output, ParseError> {
parse_single_node(token_nodes, "infix operator", |token, token_span, err| {
Ok(match token { Ok(match token {
// If it's an operator (and not `.`), it's a match // If it's an operator (and not `.`), it's a match
RawToken::Operator(operator) if operator != Operator::Dot => { RawToken::Operator(operator) if operator != Operator::Dot => {
@ -1051,21 +1180,8 @@ impl ExpandSyntax for InfixShape {
} }
// Otherwise, it's not a match // Otherwise, it's not a match
_ => { _ => return Err(err.error()),
return Err(ShellError::type_error( })
"infix operator",
token.type_name().tagged(token_span),
))
}
}) })
},
)?;
// An infix operator must be followed by whitespace
let end = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?;
checkpoint.commit();
Ok((start, operator, end))
} }
} }

View File

@ -1,5 +1,5 @@
use crate::parser::{Delimiter, Flag, FlagKind, Operator, RawNumber, RawToken, TokenNode}; use crate::parser::{Delimiter, Flag, FlagKind, Operator, RawNumber, RawToken, TokenNode};
use crate::{Span, Spanned, SpannedItem, Text}; use crate::{HasSpan, Span, Spanned, SpannedItem, Text};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum FlatShape { pub enum FlatShape {

View File

@ -1,25 +1,38 @@
pub(crate) mod debug; pub(crate) mod debug;
use self::debug::Tracer; use self::debug::{ColorTracer, ExpandTracer};
use crate::errors::ShellError; use crate::errors::ShellError;
#[cfg(coloring_in_tokens)] #[cfg(coloring_in_tokens)]
use crate::parser::hir::syntax_shape::FlatShape; use crate::parser::hir::syntax_shape::FlatShape;
use crate::parser::hir::Expression;
use crate::parser::TokenNode; use crate::parser::TokenNode;
use crate::prelude::*; use crate::prelude::*;
use crate::{Span, Spanned, SpannedItem}; use crate::{Span, Spanned, SpannedItem};
#[allow(unused)] #[allow(unused)]
use getset::{Getters, MutGetters}; use getset::{Getters, MutGetters};
#[derive(Getters, Debug)] cfg_if::cfg_if! {
pub struct TokensIteratorState<'content> { if #[cfg(coloring_in_tokens)] {
#[derive(Getters, Debug)]
pub struct TokensIteratorState<'content> {
tokens: &'content [TokenNode], tokens: &'content [TokenNode],
span: Span, span: Span,
skip_ws: bool, skip_ws: bool,
index: usize, index: usize,
seen: indexmap::IndexSet<usize>, seen: indexmap::IndexSet<usize>,
#[cfg(coloring_in_tokens)] #[get = "pub"]
#[cfg_attr(coloring_in_tokens, get = "pub")]
shapes: Vec<Spanned<FlatShape>>, shapes: Vec<Spanned<FlatShape>>,
}
} else {
#[derive(Getters, Debug)]
pub struct TokensIteratorState<'content> {
tokens: &'content [TokenNode],
span: Span,
skip_ws: bool,
index: usize,
seen: indexmap::IndexSet<usize>,
}
}
} }
#[derive(Getters, MutGetters, Debug)] #[derive(Getters, MutGetters, Debug)]
@ -29,7 +42,10 @@ pub struct TokensIterator<'content> {
state: TokensIteratorState<'content>, state: TokensIteratorState<'content>,
#[get = "pub"] #[get = "pub"]
#[get_mut = "pub"] #[get_mut = "pub"]
tracer: Tracer, color_tracer: ColorTracer,
#[get = "pub"]
#[get_mut = "pub"]
expand_tracer: ExpandTracer,
} }
#[derive(Debug)] #[derive(Debug)]
@ -83,12 +99,9 @@ impl<'content, 'me> Peeked<'content, 'me> {
Some(node) Some(node)
} }
pub fn not_eof( pub fn not_eof(self, expected: &'static str) -> Result<PeekedNode<'content, 'me>, ParseError> {
self,
expected: impl Into<String>,
) -> Result<PeekedNode<'content, 'me>, ShellError> {
match self.node { match self.node {
None => Err(ShellError::unexpected_eof( None => Err(ParseError::unexpected_eof(
expected, expected,
self.iterator.eof_span(), self.iterator.eof_span(),
)), )),
@ -101,7 +114,7 @@ impl<'content, 'me> Peeked<'content, 'me> {
} }
} }
pub fn type_error(&self, expected: impl Into<String>) -> ShellError { pub fn type_error(&self, expected: &'static str) -> ParseError {
peek_error(&self.node, self.iterator.eof_span(), expected) peek_error(&self.node, self.iterator.eof_span(), expected)
} }
} }
@ -129,19 +142,15 @@ impl<'content, 'me> PeekedNode<'content, 'me> {
pub fn rollback(self) {} pub fn rollback(self) {}
pub fn type_error(&self, expected: impl Into<String>) -> ShellError { pub fn type_error(&self, expected: &'static str) -> ParseError {
peek_error(&Some(self.node), self.iterator.eof_span(), expected) peek_error(&Some(self.node), self.iterator.eof_span(), expected)
} }
} }
pub fn peek_error( pub fn peek_error(node: &Option<&TokenNode>, eof_span: Span, expected: &'static str) -> ParseError {
node: &Option<&TokenNode>,
eof_span: Span,
expected: impl Into<String>,
) -> ShellError {
match node { match node {
None => ShellError::unexpected_eof(expected, eof_span), None => ParseError::unexpected_eof(expected, eof_span),
Some(node) => ShellError::type_error(expected, node.tagged_type_name()), Some(node) => ParseError::mismatch(expected, node.tagged_type_name()),
} }
} }
@ -161,7 +170,8 @@ impl<'content> TokensIterator<'content> {
#[cfg(coloring_in_tokens)] #[cfg(coloring_in_tokens)]
shapes: vec![], shapes: vec![],
}, },
tracer: Tracer::new(), color_tracer: ColorTracer::new(),
expand_tracer: ExpandTracer::new(),
} }
} }
@ -188,7 +198,7 @@ impl<'content> TokensIterator<'content> {
#[cfg(coloring_in_tokens)] #[cfg(coloring_in_tokens)]
pub fn color_shape(&mut self, shape: Spanned<FlatShape>) { pub fn color_shape(&mut self, shape: Spanned<FlatShape>) {
self.with_tracer(|_, tracer| tracer.add_shape(shape)); self.with_color_tracer(|_, tracer| tracer.add_shape(shape));
self.state.shapes.push(shape); self.state.shapes.push(shape);
} }
@ -201,7 +211,7 @@ impl<'content> TokensIterator<'content> {
(len..(shapes.len())).map(|i| shapes[i]).collect() (len..(shapes.len())).map(|i| shapes[i]).collect()
}; };
self.with_tracer(|_, tracer| { self.with_color_tracer(|_, tracer| {
for shape in new_shapes { for shape in new_shapes {
tracer.add_shape(shape) tracer.add_shape(shape)
} }
@ -233,8 +243,11 @@ impl<'content> TokensIterator<'content> {
let mut shapes = vec![]; let mut shapes = vec![];
std::mem::swap(&mut shapes, &mut self.state.shapes); std::mem::swap(&mut shapes, &mut self.state.shapes);
let mut tracer = Tracer::new(); let mut color_tracer = ColorTracer::new();
std::mem::swap(&mut tracer, &mut self.tracer); std::mem::swap(&mut color_tracer, &mut self.color_tracer);
let mut expand_tracer = ExpandTracer::new();
std::mem::swap(&mut expand_tracer, &mut self.expand_tracer);
let mut iterator = TokensIterator { let mut iterator = TokensIterator {
state: TokensIteratorState { state: TokensIteratorState {
@ -245,13 +258,15 @@ impl<'content> TokensIterator<'content> {
seen: indexmap::IndexSet::new(), seen: indexmap::IndexSet::new(),
shapes, shapes,
}, },
tracer, color_tracer,
expand_tracer,
}; };
let result = block(&mut iterator); let result = block(&mut iterator);
std::mem::swap(&mut iterator.state.shapes, &mut self.state.shapes); std::mem::swap(&mut iterator.state.shapes, &mut self.state.shapes);
std::mem::swap(&mut iterator.tracer, &mut self.tracer); std::mem::swap(&mut iterator.color_tracer, &mut self.color_tracer);
std::mem::swap(&mut iterator.expand_tracer, &mut self.expand_tracer);
result result
} }
@ -262,8 +277,11 @@ impl<'content> TokensIterator<'content> {
tokens: Spanned<&'me [TokenNode]>, tokens: Spanned<&'me [TokenNode]>,
block: impl FnOnce(&mut TokensIterator<'me>) -> T, block: impl FnOnce(&mut TokensIterator<'me>) -> T,
) -> T { ) -> T {
let mut tracer = Tracer::new(); let mut color_tracer = ColorTracer::new();
std::mem::swap(&mut tracer, &mut self.tracer); std::mem::swap(&mut color_tracer, &mut self.color_tracer);
let mut expand_tracer = ExpandTracer::new();
std::mem::swap(&mut expand_tracer, &mut self.expand_tracer);
let mut iterator = TokensIterator { let mut iterator = TokensIterator {
state: TokensIteratorState { state: TokensIteratorState {
@ -273,19 +291,34 @@ impl<'content> TokensIterator<'content> {
index: 0, index: 0,
seen: indexmap::IndexSet::new(), seen: indexmap::IndexSet::new(),
}, },
tracer, color_tracer,
expand_tracer,
}; };
let result = block(&mut iterator); let result = block(&mut iterator);
std::mem::swap(&mut iterator.tracer, &mut self.tracer); std::mem::swap(&mut iterator.color_tracer, &mut self.color_tracer);
std::mem::swap(&mut iterator.expand_tracer, &mut self.expand_tracer);
result result
} }
pub fn with_tracer(&mut self, block: impl FnOnce(&mut TokensIteratorState, &mut Tracer)) { pub fn with_color_tracer(
&mut self,
block: impl FnOnce(&mut TokensIteratorState, &mut ColorTracer),
) {
let state = &mut self.state; let state = &mut self.state;
let tracer = &mut self.tracer; let color_tracer = &mut self.color_tracer;
block(state, color_tracer)
}
pub fn with_expand_tracer(
&mut self,
block: impl FnOnce(&mut TokensIteratorState, &mut ExpandTracer),
) {
let state = &mut self.state;
let tracer = &mut self.expand_tracer;
block(state, tracer) block(state, tracer)
} }
@ -296,32 +329,77 @@ impl<'content> TokensIterator<'content> {
desc: &'static str, desc: &'static str,
block: impl FnOnce(&mut TokensIterator) -> T, block: impl FnOnce(&mut TokensIterator) -> T,
) -> T { ) -> T {
self.with_tracer(|_, tracer| tracer.start(desc)); self.with_color_tracer(|_, tracer| tracer.start(desc));
let result = block(self); let result = block(self);
self.with_tracer(|_, tracer| { self.with_color_tracer(|_, tracer| {
tracer.success(); tracer.success();
}); });
result result
} }
pub fn expand_frame<T>(
&mut self,
desc: &'static str,
block: impl FnOnce(&mut TokensIterator) -> Result<T, ParseError>,
) -> Result<T, ParseError>
where
T: std::fmt::Debug + FormatDebug + Clone + HasFallibleSpan + 'static,
{
self.with_expand_tracer(|_, tracer| tracer.start(desc));
let result = block(self);
self.with_expand_tracer(|_, tracer| match &result {
Ok(result) => {
tracer.add_result(Box::new(result.clone()));
tracer.success();
}
Err(err) => tracer.failed(err),
});
result
}
pub fn expand_expr_frame(
&mut self,
desc: &'static str,
block: impl FnOnce(&mut TokensIterator) -> Result<Expression, ParseError>,
) -> Result<Expression, ParseError> {
self.with_expand_tracer(|_, tracer| tracer.start(desc));
let result = block(self);
self.with_expand_tracer(|_, tracer| match &result {
Ok(expr) => {
tracer.add_expr(expr.clone());
tracer.success()
}
Err(err) => tracer.failed(err),
});
result
}
pub fn color_fallible_frame<T>( pub fn color_fallible_frame<T>(
&mut self, &mut self,
desc: &'static str, desc: &'static str,
block: impl FnOnce(&mut TokensIterator) -> Result<T, ShellError>, block: impl FnOnce(&mut TokensIterator) -> Result<T, ShellError>,
) -> Result<T, ShellError> { ) -> Result<T, ShellError> {
self.with_tracer(|_, tracer| tracer.start(desc)); self.with_color_tracer(|_, tracer| tracer.start(desc));
if self.at_end() { if self.at_end() {
self.with_tracer(|_, tracer| tracer.eof_frame()); self.with_color_tracer(|_, tracer| tracer.eof_frame());
return Err(ShellError::unexpected_eof("coloring", Tag::unknown())); return Err(ShellError::unexpected_eof("coloring", Tag::unknown()));
} }
let result = block(self); let result = block(self);
self.with_tracer(|_, tracer| match &result { self.with_color_tracer(|_, tracer| match &result {
Ok(_) => { Ok(_) => {
tracer.success(); tracer.success();
} }
@ -431,10 +509,6 @@ impl<'content> TokensIterator<'content> {
} }
} }
pub fn whole_span(&self) -> Span {
self.state.span
}
pub fn span_at_cursor(&mut self) -> Span { pub fn span_at_cursor(&mut self) -> Span {
let next = self.peek_any(); let next = self.peek_any();
@ -491,27 +565,22 @@ impl<'content> TokensIterator<'content> {
self.state.index = 0; self.state.index = 0;
} }
pub fn clone(&self) -> TokensIterator<'content> { // pub fn clone(&self) -> TokensIterator<'content> {
let state = &self.state; // let state = &self.state;
TokensIterator { // TokensIterator {
state: TokensIteratorState { // state: TokensIteratorState {
tokens: state.tokens, // tokens: state.tokens,
span: state.span, // span: state.span,
index: state.index, // index: state.index,
seen: state.seen.clone(), // seen: state.seen.clone(),
skip_ws: state.skip_ws, // skip_ws: state.skip_ws,
#[cfg(coloring_in_tokens)] // #[cfg(coloring_in_tokens)]
shapes: state.shapes.clone(), // shapes: state.shapes.clone(),
}, // },
tracer: self.tracer.clone(), // color_tracer: self.color_tracer.clone(),
} // expand_tracer: self.expand_tracer.clone(),
} // }
// }
// Get the next token, not including whitespace
pub fn next_non_ws(&mut self) -> Option<&TokenNode> {
let mut peeked = start_next(self, true);
peeked.commit()
}
// Peek the next token, not including whitespace // Peek the next token, not including whitespace
pub fn peek_non_ws<'me>(&'me mut self) -> Peeked<'content, 'me> { pub fn peek_non_ws<'me>(&'me mut self) -> Peeked<'content, 'me> {
@ -527,8 +596,8 @@ impl<'content> TokensIterator<'content> {
pub fn peek_any_token<'me, T>( pub fn peek_any_token<'me, T>(
&'me mut self, &'me mut self,
expected: &'static str, expected: &'static str,
block: impl FnOnce(&'content TokenNode) -> Result<T, ShellError>, block: impl FnOnce(&'content TokenNode) -> Result<T, ParseError>,
) -> Result<T, ShellError> { ) -> Result<T, ParseError> {
let peeked = start_next(self, false); let peeked = start_next(self, false);
let peeked = peeked.not_eof(expected); let peeked = peeked.not_eof(expected);
@ -557,9 +626,11 @@ impl<'content> TokensIterator<'content> {
} }
pub fn debug_remaining(&self) -> Vec<TokenNode> { pub fn debug_remaining(&self) -> Vec<TokenNode> {
let mut tokens = self.clone(); // TODO: TODO: TODO: Clean up
tokens.restart(); vec![]
tokens.cloned().collect() // let mut tokens = self.clone();
// tokens.restart();
// tokens.cloned().collect()
} }
} }

View File

@ -1,13 +1,13 @@
use crate::errors::ShellError; #![allow(unused)]
use crate::parser::hir::syntax_shape::FlatShape;
pub(crate) mod color_trace;
pub(crate) mod expand_trace;
pub(crate) use self::color_trace::*;
pub(crate) use self::expand_trace::*;
use crate::parser::hir::tokens_iterator::TokensIteratorState; use crate::parser::hir::tokens_iterator::TokensIteratorState;
use crate::prelude::*;
use crate::traits::ToDebug; use crate::traits::ToDebug;
use ansi_term::Color;
use log::trace;
use ptree::*;
use std::borrow::Cow;
use std::io;
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum DebugIteratorToken { pub(crate) enum DebugIteratorToken {
@ -36,344 +36,3 @@ pub(crate) fn debug_tokens(state: &TokensIteratorState, source: &str) -> Vec<Deb
out out
} }
#[derive(Debug, Clone)]
pub enum FrameChild {
#[allow(unused)]
Shape(Spanned<FlatShape>),
Frame(ColorFrame),
}
impl FrameChild {
fn colored_leaf_description(&self, text: &Text, f: &mut impl io::Write) -> io::Result<()> {
match self {
FrameChild::Shape(shape) => write!(
f,
"{} {:?}",
Color::White
.bold()
.on(Color::Green)
.paint(format!("{:?}", shape.item)),
shape.span.slice(text)
),
FrameChild::Frame(frame) => frame.colored_leaf_description(f),
}
}
fn into_tree_child(self, text: &Text) -> TreeChild {
match self {
FrameChild::Shape(shape) => TreeChild::Shape(shape, text.clone()),
FrameChild::Frame(frame) => TreeChild::Frame(frame, text.clone()),
}
}
}
#[derive(Debug, Clone)]
pub struct ColorFrame {
description: &'static str,
children: Vec<FrameChild>,
error: Option<ShellError>,
}
impl ColorFrame {
fn colored_leaf_description(&self, f: &mut impl io::Write) -> io::Result<()> {
if self.has_only_error_descendents() {
if self.children.len() == 0 {
write!(
f,
"{}",
Color::White.bold().on(Color::Red).paint(self.description)
)
} else {
write!(f, "{}", Color::Red.normal().paint(self.description))
}
} else if self.has_descendent_shapes() {
write!(f, "{}", Color::Green.normal().paint(self.description))
} else {
write!(f, "{}", Color::Yellow.bold().paint(self.description))
}
}
fn colored_description(&self, text: &Text, f: &mut impl io::Write) -> io::Result<()> {
if self.children.len() == 1 {
let child = &self.children[0];
self.colored_leaf_description(f)?;
write!(f, " -> ")?;
child.colored_leaf_description(text, f)
} else {
self.colored_leaf_description(f)
}
}
fn children_for_formatting(&self, text: &Text) -> Vec<TreeChild> {
if self.children.len() == 1 {
let child = &self.children[0];
match child {
FrameChild::Shape(_) => vec![],
FrameChild::Frame(frame) => frame.tree_children(text),
}
} else {
self.tree_children(text)
}
}
fn tree_children(&self, text: &Text) -> Vec<TreeChild> {
self.children
.clone()
.into_iter()
.map(|c| c.into_tree_child(text))
.collect()
}
#[allow(unused)]
fn add_shape(&mut self, shape: Spanned<FlatShape>) {
self.children.push(FrameChild::Shape(shape))
}
fn has_child_shapes(&self) -> bool {
self.any_child_shape(|_| true)
}
fn any_child_shape(&self, predicate: impl Fn(Spanned<FlatShape>) -> bool) -> bool {
for item in &self.children {
match item {
FrameChild::Shape(shape) => {
if predicate(*shape) {
return true;
}
}
_ => {}
}
}
false
}
fn any_child_frame(&self, predicate: impl Fn(&ColorFrame) -> bool) -> bool {
for item in &self.children {
match item {
FrameChild::Frame(frame) => {
if predicate(frame) {
return true;
}
}
_ => {}
}
}
false
}
fn has_descendent_shapes(&self) -> bool {
if self.has_child_shapes() {
true
} else {
self.any_child_frame(|frame| frame.has_descendent_shapes())
}
}
fn has_only_error_descendents(&self) -> bool {
if self.children.len() == 0 {
// if this frame has no children at all, it has only error descendents if this frame
// is an error
self.error.is_some()
} else {
// otherwise, it has only error descendents if all of its children terminate in an
// error (transitively)
let mut seen_error = false;
for child in &self.children {
match child {
// if this frame has at least one child shape, this frame has non-error descendents
FrameChild::Shape(_) => return false,
FrameChild::Frame(frame) => {
// if the chi
if frame.has_only_error_descendents() {
seen_error = true;
} else {
return false;
}
}
}
}
seen_error
}
}
}
#[derive(Debug, Clone)]
pub enum TreeChild {
Shape(Spanned<FlatShape>, Text),
Frame(ColorFrame, Text),
}
impl TreeChild {
fn colored_leaf_description(&self, f: &mut impl io::Write) -> io::Result<()> {
match self {
TreeChild::Shape(shape, text) => write!(
f,
"{} {:?}",
Color::White
.bold()
.on(Color::Green)
.paint(format!("{:?}", shape.item)),
shape.span.slice(text)
),
TreeChild::Frame(frame, _) => frame.colored_leaf_description(f),
}
}
}
impl TreeItem for TreeChild {
type Child = TreeChild;
fn write_self<W: io::Write>(&self, f: &mut W, _style: &Style) -> io::Result<()> {
match self {
shape @ TreeChild::Shape(..) => shape.colored_leaf_description(f),
TreeChild::Frame(frame, text) => frame.colored_description(text, f),
}
}
fn children(&self) -> Cow<[Self::Child]> {
match self {
TreeChild::Shape(..) => Cow::Borrowed(&[]),
TreeChild::Frame(frame, text) => Cow::Owned(frame.children_for_formatting(text)),
}
}
}
#[derive(Debug, Clone)]
pub struct Tracer {
frame_stack: Vec<ColorFrame>,
}
impl Tracer {
pub fn print(self, source: Text) -> PrintTracer {
PrintTracer {
tracer: self,
source,
}
}
pub fn new() -> Tracer {
let root = ColorFrame {
description: "Trace",
children: vec![],
error: None,
};
Tracer {
frame_stack: vec![root],
}
}
fn current_frame(&mut self) -> &mut ColorFrame {
let frames = &mut self.frame_stack;
let last = frames.len() - 1;
&mut frames[last]
}
fn pop_frame(&mut self) -> ColorFrame {
let result = self.frame_stack.pop().expect("Can't pop root tracer frame");
if self.frame_stack.len() == 0 {
panic!("Can't pop root tracer frame");
}
self.debug();
result
}
pub fn start(&mut self, description: &'static str) {
let frame = ColorFrame {
description,
children: vec![],
error: None,
};
self.frame_stack.push(frame);
self.debug();
}
pub fn eof_frame(&mut self) {
let current = self.pop_frame();
self.current_frame()
.children
.push(FrameChild::Frame(current));
}
#[allow(unused)]
pub fn finish(&mut self) {
loop {
if self.frame_stack.len() == 1 {
break;
}
let frame = self.pop_frame();
self.current_frame().children.push(FrameChild::Frame(frame));
}
}
#[allow(unused)]
pub fn add_shape(&mut self, shape: Spanned<FlatShape>) {
self.current_frame().add_shape(shape);
}
pub fn success(&mut self) {
let current = self.pop_frame();
self.current_frame()
.children
.push(FrameChild::Frame(current));
}
pub fn failed(&mut self, error: &ShellError) {
let mut current = self.pop_frame();
current.error = Some(error.clone());
self.current_frame()
.children
.push(FrameChild::Frame(current));
}
fn debug(&self) {
trace!(target: "nu::color_syntax",
"frames = {:?}",
self.frame_stack
.iter()
.map(|f| f.description)
.collect::<Vec<_>>()
);
trace!(target: "nu::color_syntax", "{:#?}", self);
}
}
#[derive(Debug, Clone)]
pub struct PrintTracer {
tracer: Tracer,
source: Text,
}
impl TreeItem for PrintTracer {
type Child = TreeChild;
fn write_self<W: io::Write>(&self, f: &mut W, style: &Style) -> io::Result<()> {
write!(f, "{}", style.paint("Color Trace"))
}
fn children(&self) -> Cow<[Self::Child]> {
Cow::Owned(vec![TreeChild::Frame(
self.tracer.frame_stack[0].clone(),
self.source.clone(),
)])
}
}

View File

@ -0,0 +1,351 @@
use crate::errors::ShellError;
use crate::parser::hir::syntax_shape::FlatShape;
use crate::prelude::*;
use ansi_term::Color;
use log::trace;
use ptree::*;
use std::borrow::Cow;
use std::io;
#[derive(Debug, Clone)]
pub enum FrameChild {
#[allow(unused)]
Shape(Spanned<FlatShape>),
Frame(ColorFrame),
}
impl FrameChild {
fn colored_leaf_description(&self, text: &Text, f: &mut impl io::Write) -> io::Result<()> {
match self {
FrameChild::Shape(shape) => write!(
f,
"{} {:?}",
Color::White
.bold()
.on(Color::Green)
.paint(format!("{:?}", shape.item)),
shape.span.slice(text)
),
FrameChild::Frame(frame) => frame.colored_leaf_description(f),
}
}
fn into_tree_child(self, text: &Text) -> TreeChild {
match self {
FrameChild::Shape(shape) => TreeChild::Shape(shape, text.clone()),
FrameChild::Frame(frame) => TreeChild::Frame(frame, text.clone()),
}
}
}
#[derive(Debug, Clone)]
pub struct ColorFrame {
description: &'static str,
children: Vec<FrameChild>,
error: Option<ShellError>,
}
impl ColorFrame {
fn colored_leaf_description(&self, f: &mut impl io::Write) -> io::Result<()> {
if self.has_only_error_descendents() {
if self.children.len() == 0 {
write!(
f,
"{}",
Color::White.bold().on(Color::Red).paint(self.description)
)
} else {
write!(f, "{}", Color::Red.normal().paint(self.description))
}
} else if self.has_descendent_shapes() {
write!(f, "{}", Color::Green.normal().paint(self.description))
} else {
write!(f, "{}", Color::Yellow.bold().paint(self.description))
}
}
fn colored_description(&self, text: &Text, f: &mut impl io::Write) -> io::Result<()> {
if self.children.len() == 1 {
let child = &self.children[0];
self.colored_leaf_description(f)?;
write!(f, " -> ")?;
child.colored_leaf_description(text, f)
} else {
self.colored_leaf_description(f)
}
}
fn children_for_formatting(&self, text: &Text) -> Vec<TreeChild> {
if self.children.len() == 1 {
let child = &self.children[0];
match child {
FrameChild::Shape(_) => vec![],
FrameChild::Frame(frame) => frame.tree_children(text),
}
} else {
self.tree_children(text)
}
}
fn tree_children(&self, text: &Text) -> Vec<TreeChild> {
self.children
.clone()
.into_iter()
.map(|c| c.into_tree_child(text))
.collect()
}
#[allow(unused)]
fn add_shape(&mut self, shape: Spanned<FlatShape>) {
self.children.push(FrameChild::Shape(shape))
}
fn has_child_shapes(&self) -> bool {
self.any_child_shape(|_| true)
}
fn any_child_shape(&self, predicate: impl Fn(Spanned<FlatShape>) -> bool) -> bool {
for item in &self.children {
match item {
FrameChild::Shape(shape) => {
if predicate(*shape) {
return true;
}
}
_ => {}
}
}
false
}
fn any_child_frame(&self, predicate: impl Fn(&ColorFrame) -> bool) -> bool {
for item in &self.children {
match item {
FrameChild::Frame(frame) => {
if predicate(frame) {
return true;
}
}
_ => {}
}
}
false
}
fn has_descendent_shapes(&self) -> bool {
if self.has_child_shapes() {
true
} else {
self.any_child_frame(|frame| frame.has_descendent_shapes())
}
}
fn has_only_error_descendents(&self) -> bool {
if self.children.len() == 0 {
// if this frame has no children at all, it has only error descendents if this frame
// is an error
self.error.is_some()
} else {
// otherwise, it has only error descendents if all of its children terminate in an
// error (transitively)
let mut seen_error = false;
for child in &self.children {
match child {
// if this frame has at least one child shape, this frame has non-error descendents
FrameChild::Shape(_) => return false,
FrameChild::Frame(frame) => {
// if the chi
if frame.has_only_error_descendents() {
seen_error = true;
} else {
return false;
}
}
}
}
seen_error
}
}
}
#[derive(Debug, Clone)]
pub enum TreeChild {
Shape(Spanned<FlatShape>, Text),
Frame(ColorFrame, Text),
}
impl TreeChild {
fn colored_leaf_description(&self, f: &mut impl io::Write) -> io::Result<()> {
match self {
TreeChild::Shape(shape, text) => write!(
f,
"{} {:?}",
Color::White
.bold()
.on(Color::Green)
.paint(format!("{:?}", shape.item)),
shape.span.slice(text)
),
TreeChild::Frame(frame, _) => frame.colored_leaf_description(f),
}
}
}
impl TreeItem for TreeChild {
type Child = TreeChild;
fn write_self<W: io::Write>(&self, f: &mut W, _style: &Style) -> io::Result<()> {
match self {
shape @ TreeChild::Shape(..) => shape.colored_leaf_description(f),
TreeChild::Frame(frame, text) => frame.colored_description(text, f),
}
}
fn children(&self) -> Cow<[Self::Child]> {
match self {
TreeChild::Shape(..) => Cow::Borrowed(&[]),
TreeChild::Frame(frame, text) => Cow::Owned(frame.children_for_formatting(text)),
}
}
}
#[derive(Debug, Clone)]
pub struct ColorTracer {
frame_stack: Vec<ColorFrame>,
}
impl ColorTracer {
pub fn print(self, source: Text) -> PrintTracer {
PrintTracer {
tracer: self,
source,
}
}
pub fn new() -> ColorTracer {
let root = ColorFrame {
description: "Trace",
children: vec![],
error: None,
};
ColorTracer {
frame_stack: vec![root],
}
}
fn current_frame(&mut self) -> &mut ColorFrame {
let frames = &mut self.frame_stack;
let last = frames.len() - 1;
&mut frames[last]
}
fn pop_frame(&mut self) -> ColorFrame {
trace!(target: "nu::color_syntax", "Popping {:#?}", self);
let result = self.frame_stack.pop().expect("Can't pop root tracer frame");
if self.frame_stack.len() == 0 {
panic!("Can't pop root tracer frame {:#?}", self);
}
self.debug();
result
}
pub fn start(&mut self, description: &'static str) {
let frame = ColorFrame {
description,
children: vec![],
error: None,
};
self.frame_stack.push(frame);
self.debug();
}
pub fn eof_frame(&mut self) {
let current = self.pop_frame();
self.current_frame()
.children
.push(FrameChild::Frame(current));
}
#[allow(unused)]
pub fn finish(&mut self) {
loop {
if self.frame_stack.len() == 1 {
break;
}
let frame = self.pop_frame();
self.current_frame().children.push(FrameChild::Frame(frame));
}
}
#[allow(unused)]
pub fn add_shape(&mut self, shape: Spanned<FlatShape>) {
self.current_frame().add_shape(shape);
}
pub fn success(&mut self) {
let current = self.pop_frame();
self.current_frame()
.children
.push(FrameChild::Frame(current));
}
pub fn failed(&mut self, error: &ShellError) {
let mut current = self.pop_frame();
current.error = Some(error.clone());
self.current_frame()
.children
.push(FrameChild::Frame(current));
}
fn debug(&self) {
trace!(target: "nu::color_syntax",
"frames = {:?}",
self.frame_stack
.iter()
.map(|f| f.description)
.collect::<Vec<_>>()
);
trace!(target: "nu::color_syntax", "{:#?}", self);
}
}
#[derive(Debug, Clone)]
pub struct PrintTracer {
tracer: ColorTracer,
source: Text,
}
impl TreeItem for PrintTracer {
type Child = TreeChild;
fn write_self<W: io::Write>(&self, f: &mut W, style: &Style) -> io::Result<()> {
write!(f, "{}", style.paint("Color Trace"))
}
fn children(&self) -> Cow<[Self::Child]> {
Cow::Owned(vec![TreeChild::Frame(
self.tracer.frame_stack[0].clone(),
self.source.clone(),
)])
}
}

View File

@ -0,0 +1,365 @@
use crate::parser::hir::Expression;
use crate::prelude::*;
use ansi_term::Color;
use log::trace;
use ptree::*;
use std::borrow::Cow;
use std::io;
#[derive(Debug)]
pub enum FrameChild {
Expr(Expression),
Frame(ExprFrame),
Result(Box<dyn FormatDebug>),
}
impl FrameChild {
fn get_error_leaf(&self) -> Option<&'static str> {
match self {
FrameChild::Frame(frame) if frame.error.is_some() => {
if frame.children.len() == 0 {
Some(frame.description)
} else {
None
}
}
_ => None,
}
}
fn to_tree_child(&self, text: &Text) -> TreeChild {
match self {
FrameChild::Expr(expr) => TreeChild::OkExpr(expr.clone(), text.clone()),
FrameChild::Result(result) => {
let result = format!("{}", result.debug(text));
TreeChild::OkNonExpr(result)
}
FrameChild::Frame(frame) => {
if frame.error.is_some() {
if frame.children.len() == 0 {
TreeChild::ErrorLeaf(vec![frame.description])
} else {
TreeChild::ErrorFrame(frame.to_tree_frame(text), text.clone())
}
} else {
TreeChild::OkFrame(frame.to_tree_frame(text), text.clone())
}
}
}
}
}
#[derive(Debug)]
pub struct ExprFrame {
description: &'static str,
children: Vec<FrameChild>,
error: Option<ParseError>,
}
impl ExprFrame {
fn to_tree_frame(&self, text: &Text) -> TreeFrame {
let mut children = vec![];
let mut errors = vec![];
for child in &self.children {
if let Some(error_leaf) = child.get_error_leaf() {
errors.push(error_leaf);
continue;
} else if errors.len() > 0 {
children.push(TreeChild::ErrorLeaf(errors));
errors = vec![];
}
children.push(child.to_tree_child(text));
}
if errors.len() > 0 {
children.push(TreeChild::ErrorLeaf(errors));
}
TreeFrame {
description: self.description,
children,
error: self.error.clone(),
}
}
fn add_expr(&mut self, expr: Expression) {
self.children.push(FrameChild::Expr(expr))
}
fn add_result(&mut self, result: Box<dyn FormatDebug>) {
self.children.push(FrameChild::Result(result))
}
}
#[derive(Debug, Clone)]
pub struct TreeFrame {
description: &'static str,
children: Vec<TreeChild>,
error: Option<ParseError>,
}
impl TreeFrame {
fn leaf_description(&self, f: &mut impl io::Write) -> io::Result<()> {
if self.children.len() == 1 {
if self.error.is_some() {
write!(f, "{}", Color::Red.normal().paint(self.description))?;
} else if self.has_descendent_green() {
write!(f, "{}", Color::Green.normal().paint(self.description))?;
} else {
write!(f, "{}", Color::Yellow.bold().paint(self.description))?;
}
write!(f, " -> ")?;
self.children[0].leaf_description(f)
} else {
if self.error.is_some() {
if self.children.len() == 0 {
write!(
f,
"{}",
Color::White.bold().on(Color::Red).paint(self.description)
)
} else {
write!(f, "{}", Color::Red.normal().paint(self.description))
}
} else if self.has_descendent_green() {
write!(f, "{}", Color::Green.normal().paint(self.description))
} else {
write!(f, "{}", Color::Yellow.bold().paint(self.description))
}
}
}
fn has_child_green(&self) -> bool {
self.children.iter().any(|item| match item {
TreeChild::OkFrame(..) | TreeChild::ErrorFrame(..) | TreeChild::ErrorLeaf(..) => false,
TreeChild::OkExpr(..) | TreeChild::OkNonExpr(..) => true,
})
}
fn any_child_frame(&self, predicate: impl Fn(&TreeFrame) -> bool) -> bool {
for item in &self.children {
match item {
TreeChild::OkFrame(frame, ..) => {
if predicate(frame) {
return true;
}
}
_ => {}
}
}
false
}
fn has_descendent_green(&self) -> bool {
if self.has_child_green() {
true
} else {
self.any_child_frame(|frame| frame.has_child_green())
}
}
fn children_for_formatting(&self, text: &Text) -> Vec<TreeChild> {
if self.children.len() == 1 {
let child: &TreeChild = &self.children[0];
match child {
TreeChild::OkExpr(..) | TreeChild::OkNonExpr(..) | TreeChild::ErrorLeaf(..) => {
vec![]
}
TreeChild::OkFrame(frame, _) | TreeChild::ErrorFrame(frame, _) => {
frame.children_for_formatting(text)
}
}
} else {
self.children.clone()
}
}
}
#[derive(Debug, Clone)]
pub enum TreeChild {
OkNonExpr(String),
OkExpr(Expression, Text),
OkFrame(TreeFrame, Text),
ErrorFrame(TreeFrame, Text),
ErrorLeaf(Vec<&'static str>),
}
impl TreeChild {
fn leaf_description(&self, f: &mut impl io::Write) -> io::Result<()> {
match self {
TreeChild::OkExpr(expr, text) => write!(
f,
"{} {} {}",
Color::Cyan.normal().paint("returns"),
Color::White.bold().on(Color::Green).paint(expr.type_name()),
expr.span.slice(text)
),
TreeChild::OkNonExpr(result) => write!(
f,
"{} {}",
Color::Cyan.normal().paint("returns"),
Color::White
.bold()
.on(Color::Green)
.paint(format!("{}", result))
),
TreeChild::ErrorLeaf(desc) => {
let last = desc.len() - 1;
for (i, item) in desc.iter().enumerate() {
write!(f, "{}", Color::White.bold().on(Color::Red).paint(*item))?;
if i != last {
write!(f, "{}", Color::White.normal().paint(", "))?;
}
}
Ok(())
}
TreeChild::ErrorFrame(frame, _) | TreeChild::OkFrame(frame, _) => {
frame.leaf_description(f)
}
}
}
}
impl TreeItem for TreeChild {
type Child = TreeChild;
fn write_self<W: io::Write>(&self, f: &mut W, _style: &Style) -> io::Result<()> {
self.leaf_description(f)
}
fn children(&self) -> Cow<[Self::Child]> {
match self {
TreeChild::OkExpr(..) | TreeChild::OkNonExpr(..) | TreeChild::ErrorLeaf(..) => {
Cow::Borrowed(&[])
}
TreeChild::OkFrame(frame, text) | TreeChild::ErrorFrame(frame, text) => {
Cow::Owned(frame.children_for_formatting(text))
}
}
}
}
#[derive(Debug)]
pub struct ExpandTracer {
frame_stack: Vec<ExprFrame>,
}
impl ExpandTracer {
pub fn print(&self, source: Text) -> PrintTracer {
let root = self
.frame_stack
.iter()
.nth(0)
.unwrap()
.to_tree_frame(&source);
PrintTracer { root, source }
}
pub fn new() -> ExpandTracer {
let root = ExprFrame {
description: "Trace",
children: vec![],
error: None,
};
ExpandTracer {
frame_stack: vec![root],
}
}
fn current_frame(&mut self) -> &mut ExprFrame {
let frames = &mut self.frame_stack;
let last = frames.len() - 1;
&mut frames[last]
}
fn pop_frame(&mut self) -> ExprFrame {
let result = self.frame_stack.pop().expect("Can't pop root tracer frame");
if self.frame_stack.len() == 0 {
panic!("Can't pop root tracer frame");
}
self.debug();
result
}
pub fn start(&mut self, description: &'static str) {
let frame = ExprFrame {
description,
children: vec![],
error: None,
};
self.frame_stack.push(frame);
self.debug();
}
pub fn add_expr(&mut self, shape: Expression) {
self.current_frame().add_expr(shape);
}
pub fn add_result(&mut self, result: Box<dyn FormatDebug>) {
self.current_frame().add_result(result);
}
pub fn success(&mut self) {
trace!(target: "parser::expand_syntax", "success {:#?}", self);
let current = self.pop_frame();
self.current_frame()
.children
.push(FrameChild::Frame(current));
}
pub fn failed(&mut self, error: &ParseError) {
let mut current = self.pop_frame();
current.error = Some(error.clone());
self.current_frame()
.children
.push(FrameChild::Frame(current));
}
fn debug(&self) {
trace!(target: "nu::parser::expand",
"frames = {:?}",
self.frame_stack
.iter()
.map(|f| f.description)
.collect::<Vec<_>>()
);
trace!(target: "nu::parser::expand", "{:#?}", self);
}
}
#[derive(Debug, Clone)]
pub struct PrintTracer {
root: TreeFrame,
source: Text,
}
impl TreeItem for PrintTracer {
type Child = TreeChild;
fn write_self<W: io::Write>(&self, f: &mut W, style: &Style) -> io::Result<()> {
write!(f, "{}", style.paint("Expansion Trace"))
}
fn children(&self) -> Cow<[Self::Child]> {
Cow::Borrowed(&self.root.children)
}
}

View File

@ -1,7 +1,7 @@
use crate::parser::TokenNode; use crate::parser::TokenNode;
use crate::traits::ToDebug; use crate::traits::{DebugFormatter, FormatDebug, ToDebug};
use getset::Getters; use getset::Getters;
use std::fmt; use std::fmt::{self, Write};
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters)]
pub struct CallNode { pub struct CallNode {
@ -27,8 +27,8 @@ impl CallNode {
} }
} }
impl ToDebug for CallNode { impl FormatDebug for CallNode {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "{}", self.head.debug(source))?; write!(f, "{}", self.head.debug(source))?;
if let Some(children) = &self.children { if let Some(children) = &self.children {

View File

@ -14,8 +14,8 @@ pub enum Operator {
Dot, Dot,
} }
impl ToDebug for Operator { impl FormatDebug for Operator {
fn fmt_debug(&self, f: &mut fmt::Formatter, _source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, _source: &str) -> fmt::Result {
write!(f, "{}", self.as_str()) write!(f, "{}", self.as_str())
} }
} }

View File

@ -1,24 +1,22 @@
use crate::parser::TokenNode; use crate::parser::TokenNode;
use crate::traits::ToDebug; use crate::{DebugFormatter, FormatDebug, Span, Spanned, ToDebug};
use crate::{Span, Spanned};
use derive_new::new; use derive_new::new;
use getset::Getters; use getset::Getters;
use std::fmt; use itertools::Itertools;
use std::fmt::{self, Write};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Getters, new)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Getters, new)]
pub struct Pipeline { pub struct Pipeline {
#[get = "pub"] #[get = "pub"]
pub(crate) parts: Vec<Spanned<PipelineElement>>, pub(crate) parts: Vec<Spanned<PipelineElement>>,
// pub(crate) post_ws: Option<Tag>,
} }
impl ToDebug for Pipeline { impl FormatDebug for Pipeline {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
for part in self.parts.iter() { f.say_str(
write!(f, "{}", part.debug(source))?; "pipeline",
} self.parts.iter().map(|p| p.debug(source)).join(" "),
)
Ok(())
} }
} }
@ -29,8 +27,8 @@ pub struct PipelineElement {
pub tokens: Spanned<Vec<TokenNode>>, pub tokens: Spanned<Vec<TokenNode>>,
} }
impl ToDebug for PipelineElement { impl FormatDebug for PipelineElement {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
if let Some(pipe) = self.pipe { if let Some(pipe) = self.pipe {
write!(f, "{}", pipe.slice(source))?; write!(f, "{}", pipe.slice(source))?;
} }

View File

@ -1,4 +1,4 @@
use crate::errors::ShellError; use crate::errors::{ParseError, ShellError};
use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*}; use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*};
use crate::prelude::*; use crate::prelude::*;
use crate::traits::ToDebug; use crate::traits::ToDebug;
@ -21,8 +21,14 @@ pub enum TokenNode {
Error(Spanned<ShellError>), Error(Spanned<ShellError>),
} }
impl ToDebug for TokenNode { impl HasSpan for TokenNode {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn span(&self) -> Span {
self.get_span()
}
}
impl FormatDebug for TokenNode {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "{:?}", self.old_debug(&Text::from(source))) write!(f, "{:?}", self.old_debug(&Text::from(source)))
} }
} }
@ -84,12 +90,12 @@ impl fmt::Debug for DebugTokenNode<'_> {
impl From<&TokenNode> for Span { impl From<&TokenNode> for Span {
fn from(token: &TokenNode) -> Span { fn from(token: &TokenNode) -> Span {
token.span() token.get_span()
} }
} }
impl TokenNode { impl TokenNode {
pub fn span(&self) -> Span { pub fn get_span(&self) -> Span {
match self { match self {
TokenNode::Token(t) => t.span, TokenNode::Token(t) => t.span,
TokenNode::Nodes(t) => t.span, TokenNode::Nodes(t) => t.span,
@ -231,10 +237,10 @@ impl TokenNode {
} }
} }
pub fn as_pipeline(&self) -> Result<Pipeline, ShellError> { pub fn as_pipeline(&self) -> Result<Pipeline, ParseError> {
match self { match self {
TokenNode::Pipeline(Spanned { item, .. }) => Ok(item.clone()), TokenNode::Pipeline(Spanned { item, .. }) => Ok(item.clone()),
_ => Err(ShellError::type_error("pipeline", self.tagged_type_name())), other => Err(ParseError::mismatch("pipeline", other.tagged_type_name())),
} }
} }
@ -321,9 +327,9 @@ impl TokenNode {
} }
} }
pub fn expect_list(&self) -> &[TokenNode] { pub fn expect_list(&self) -> Spanned<&[TokenNode]> {
match self { match self {
TokenNode::Nodes(token_nodes) => &token_nodes[..], TokenNode::Nodes(token_nodes) => token_nodes[..].spanned(token_nodes.span),
other => panic!("Expected list, found {:?}", other), other => panic!("Expected list, found {:?}", other),
} }
} }

View File

@ -23,8 +23,8 @@ impl RawToken {
RawToken::Operator(..) => "operator", RawToken::Operator(..) => "operator",
RawToken::String(_) => "string", RawToken::String(_) => "string",
RawToken::Variable(_) => "variable", RawToken::Variable(_) => "variable",
RawToken::ExternalCommand(_) => "external command", RawToken::ExternalCommand(_) => "syntax error",
RawToken::ExternalWord => "external word", RawToken::ExternalWord => "syntax error",
RawToken::GlobPattern => "glob pattern", RawToken::GlobPattern => "glob pattern",
RawToken::Bare => "string", RawToken::Bare => "string",
} }
@ -37,6 +37,15 @@ pub enum RawNumber {
Decimal(Span), Decimal(Span),
} }
impl FormatDebug for RawNumber {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
match self {
RawNumber::Int(span) => f.say_str("int", span.slice(source)),
RawNumber::Decimal(span) => f.say_str("decimal", span.slice(source)),
}
}
}
impl RawNumber { impl RawNumber {
pub fn int(span: impl Into<Span>) -> Spanned<RawNumber> { pub fn int(span: impl Into<Span>) -> Spanned<RawNumber> {
let span = span.into(); let span = span.into();

View File

@ -1,6 +1,7 @@
use crate::data::base::Value; use crate::data::base::Value;
use crate::prelude::*; use crate::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
@ -13,6 +14,12 @@ pub enum Unit {
PB, PB,
} }
impl FormatDebug for Spanned<Unit> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
write!(f, "{}", self.span.slice(source))
}
}
impl Unit { impl Unit {
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
match *self { match *self {

View File

@ -1,4 +1,4 @@
use crate::errors::{ArgumentError, ShellError}; use crate::errors::{ArgumentError, ParseError};
use crate::parser::hir::syntax_shape::{ use crate::parser::hir::syntax_shape::{
color_fallible_syntax, color_syntax, expand_expr, flat_shape::FlatShape, spaced, color_fallible_syntax, color_syntax, expand_expr, flat_shape::FlatShape, spaced,
BackoffColoringMode, ColorSyntax, MaybeSpaceShape, BackoffColoringMode, ColorSyntax, MaybeSpaceShape,
@ -18,9 +18,9 @@ pub fn parse_command_tail(
context: &ExpandContext, context: &ExpandContext,
tail: &mut TokensIterator, tail: &mut TokensIterator,
command_span: Span, command_span: Span,
) -> Result<Option<(Option<Vec<hir::Expression>>, Option<NamedArguments>)>, ShellError> { ) -> Result<Option<(Option<Vec<hir::Expression>>, Option<NamedArguments>)>, ParseError> {
let mut named = NamedArguments::new(); let mut named = NamedArguments::new();
trace_remaining("nodes", tail.clone(), context.source()); trace_remaining("nodes", &tail, context.source());
for (name, kind) in &config.named { for (name, kind) in &config.named {
trace!(target: "nu::parse", "looking for {} : {:?}", name, kind); trace!(target: "nu::parse", "looking for {} : {:?}", name, kind);
@ -38,7 +38,7 @@ pub fn parse_command_tail(
tail.move_to(pos); tail.move_to(pos);
if tail.at_end() { if tail.at_end() {
return Err(ShellError::argument_error( return Err(ParseError::argument_error(
config.name.clone(), config.name.clone(),
ArgumentError::MissingValueForName(name.to_string()), ArgumentError::MissingValueForName(name.to_string()),
flag.span, flag.span,
@ -59,7 +59,7 @@ pub fn parse_command_tail(
tail.move_to(pos); tail.move_to(pos);
if tail.at_end() { if tail.at_end() {
return Err(ShellError::argument_error( return Err(ParseError::argument_error(
config.name.clone(), config.name.clone(),
ArgumentError::MissingValueForName(name.to_string()), ArgumentError::MissingValueForName(name.to_string()),
flag.span, flag.span,
@ -85,7 +85,7 @@ pub fn parse_command_tail(
}; };
} }
trace_remaining("after named", tail.clone(), context.source()); trace_remaining("after named", &tail, context.source());
let mut positional = vec![]; let mut positional = vec![];
@ -95,7 +95,7 @@ pub fn parse_command_tail(
match &arg.0 { match &arg.0 {
PositionalType::Mandatory(..) => { PositionalType::Mandatory(..) => {
if tail.at_end_possible_ws() { if tail.at_end_possible_ws() {
return Err(ShellError::argument_error( return Err(ParseError::argument_error(
config.name.clone(), config.name.clone(),
ArgumentError::MissingMandatoryPositional(arg.0.name().to_string()), ArgumentError::MissingMandatoryPositional(arg.0.name().to_string()),
Tag { Tag {
@ -118,7 +118,7 @@ pub fn parse_command_tail(
positional.push(result); positional.push(result);
} }
trace_remaining("after positional", tail.clone(), context.source()); trace_remaining("after positional", &tail, context.source());
if let Some((syntax_type, _)) = config.rest_positional { if let Some((syntax_type, _)) = config.rest_positional {
let mut out = vec![]; let mut out = vec![];
@ -136,7 +136,7 @@ pub fn parse_command_tail(
positional.extend(out); positional.extend(out);
} }
trace_remaining("after rest", tail.clone(), context.source()); trace_remaining("after rest", &tail, context.source());
trace!(target: "nu::parse", "Constructed positional={:?} named={:?}", positional, named); trace!(target: "nu::parse", "Constructed positional={:?} named={:?}", positional, named);
@ -202,8 +202,6 @@ impl ColorSyntax for CommandTailShape {
shapes: &mut Vec<Spanned<FlatShape>>, shapes: &mut Vec<Spanned<FlatShape>>,
) -> Self::Info { ) -> Self::Info {
let mut args = ColoringArgs::new(token_nodes.len()); let mut args = ColoringArgs::new(token_nodes.len());
trace_remaining("nodes", token_nodes.clone(), context.source());
for (name, kind) in &signature.named { for (name, kind) in &signature.named {
trace!(target: "nu::color_syntax", "looking for {} : {:?}", name, kind); trace!(target: "nu::color_syntax", "looking for {} : {:?}", name, kind);
@ -295,8 +293,6 @@ impl ColorSyntax for CommandTailShape {
}; };
} }
trace_remaining("after named", token_nodes.clone(), context.source());
for arg in &signature.positional { for arg in &signature.positional {
trace!("Processing positional {:?}", arg); trace!("Processing positional {:?}", arg);
@ -341,8 +337,6 @@ impl ColorSyntax for CommandTailShape {
} }
} }
trace_remaining("after positional", token_nodes.clone(), context.source());
if let Some((syntax_type, _)) = signature.rest_positional { if let Some((syntax_type, _)) = signature.rest_positional {
loop { loop {
if token_nodes.at_end_possible_ws() { if token_nodes.at_end_possible_ws() {
@ -402,7 +396,7 @@ impl ColorSyntax for CommandTailShape {
context: &ExpandContext, context: &ExpandContext,
) -> Self::Info { ) -> Self::Info {
let mut args = ColoringArgs::new(token_nodes.len()); let mut args = ColoringArgs::new(token_nodes.len());
trace_remaining("nodes", token_nodes.clone(), context.source()); trace_remaining("nodes", &token_nodes, context.source());
for (name, kind) in &signature.named { for (name, kind) in &signature.named {
trace!(target: "nu::color_syntax", "looking for {} : {:?}", name, kind); trace!(target: "nu::color_syntax", "looking for {} : {:?}", name, kind);
@ -497,7 +491,7 @@ impl ColorSyntax for CommandTailShape {
}; };
} }
trace_remaining("after named", token_nodes.clone(), context.source()); trace_remaining("after named", &token_nodes, context.source());
for arg in &signature.positional { for arg in &signature.positional {
trace!("Processing positional {:?}", arg); trace!("Processing positional {:?}", arg);
@ -537,7 +531,7 @@ impl ColorSyntax for CommandTailShape {
} }
} }
trace_remaining("after positional", token_nodes.clone(), context.source()); trace_remaining("after positional", &token_nodes, context.source());
if let Some((syntax_type, _)) = signature.rest_positional { if let Some((syntax_type, _)) = signature.rest_positional {
loop { loop {
@ -594,11 +588,11 @@ fn extract_mandatory(
tokens: &mut hir::TokensIterator<'_>, tokens: &mut hir::TokensIterator<'_>,
source: &Text, source: &Text,
span: Span, span: Span,
) -> Result<(usize, Spanned<Flag>), ShellError> { ) -> Result<(usize, Spanned<Flag>), ParseError> {
let flag = tokens.extract(|t| t.as_flag(name, source)); let flag = tokens.extract(|t| t.as_flag(name, source));
match flag { match flag {
None => Err(ShellError::argument_error( None => Err(ParseError::argument_error(
config.name.clone(), config.name.clone(),
ArgumentError::MissingMandatoryFlag(name.to_string()), ArgumentError::MissingMandatoryFlag(name.to_string()),
span, span,
@ -615,7 +609,7 @@ fn extract_optional(
name: &str, name: &str,
tokens: &mut hir::TokensIterator<'_>, tokens: &mut hir::TokensIterator<'_>,
source: &Text, source: &Text,
) -> Result<(Option<(usize, Spanned<Flag>)>), ShellError> { ) -> Result<(Option<(usize, Spanned<Flag>)>), ParseError> {
let flag = tokens.extract(|t| t.as_flag(name, source)); let flag = tokens.extract(|t| t.as_flag(name, source));
match flag { match flag {
@ -627,7 +621,7 @@ fn extract_optional(
} }
} }
pub fn trace_remaining(desc: &'static str, tail: hir::TokensIterator<'_>, source: &Text) { pub fn trace_remaining(desc: &'static str, tail: &hir::TokensIterator<'_>, source: &Text) {
trace!( trace!(
target: "nu::parse", target: "nu::parse",
"{} = {:?}", "{} = {:?}",

View File

@ -1,6 +1,6 @@
use nu::{ use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, did_you_mean, serve_plugin, tag_for_tagged_list, CallInfo, Plugin, Primitive, ReturnSuccess,
SyntaxShape, Tagged, TaggedItem, Value, ReturnValue, ShellError, Signature, SyntaxShape, Tagged, TaggedItem, Value,
}; };
enum Action { enum Action {
@ -93,9 +93,36 @@ impl Inc {
)); ));
} }
} }
Value::Row(_) => match self.field { Value::Row(_) => match self.field {
Some(ref f) => { Some(ref f) => {
let replacement = match value.item.get_data_by_column_path(value.tag(), f) { let fields = f.clone();
let replace_for = value.item.get_data_by_column_path(
value.tag(),
&f,
Box::new(move |(obj_source, column_path_tried)| {
match did_you_mean(&obj_source, &column_path_tried) {
Some(suggestions) => {
return ShellError::labeled_error(
"Unknown column",
format!("did you mean '{}'?", suggestions[0].1),
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
)
}
None => {
return ShellError::labeled_error(
"Unknown column",
"row does not contain this column",
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
)
}
}
}),
);
let replacement = match replace_for {
Ok(got) => match got {
Some(result) => self.inc(result.map(|x| x.clone()))?, Some(result) => self.inc(result.map(|x| x.clone()))?,
None => { None => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
@ -104,11 +131,13 @@ impl Inc {
value.tag(), value.tag(),
)) ))
} }
},
Err(reason) => return Err(reason),
}; };
match value.item.replace_data_at_column_path( match value.item.replace_data_at_column_path(
value.tag(), value.tag(),
f, &f,
replacement.item.clone(), replacement.item.clone(),
) { ) {
Some(v) => return Ok(v), Some(v) => return Ok(v),

View File

@ -1,6 +1,6 @@
use nu::{ use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, did_you_mean, serve_plugin, tag_for_tagged_list, CallInfo, Plugin, Primitive, ReturnSuccess,
SyntaxShape, Tagged, TaggedItem, Value, ReturnValue, ShellError, Signature, SyntaxShape, Tagged, TaggedItem, Value,
}; };
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
@ -92,13 +92,48 @@ impl Str {
Value::Primitive(Primitive::String(ref s)) => Ok(self.apply(&s)?.tagged(value.tag())), Value::Primitive(Primitive::String(ref s)) => Ok(self.apply(&s)?.tagged(value.tag())),
Value::Row(_) => match self.field { Value::Row(_) => match self.field {
Some(ref f) => { Some(ref f) => {
let replacement = match value.item.get_data_by_column_path(value.tag(), f) { let fields = f.clone();
let replace_for = value.item.get_data_by_column_path(
value.tag(),
&f,
Box::new(move |(obj_source, column_path_tried)| {
match did_you_mean(&obj_source, &column_path_tried) {
Some(suggestions) => {
return ShellError::labeled_error(
"Unknown column",
format!("did you mean '{}'?", suggestions[0].1),
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
)
}
None => {
return ShellError::labeled_error(
"Unknown column",
"row does not contain this column",
tag_for_tagged_list(fields.iter().map(|p| p.tag())),
)
}
}
}),
);
let replacement = match replace_for {
Ok(got) => match got {
Some(result) => self.strutils(result.map(|x| x.clone()))?, Some(result) => self.strutils(result.map(|x| x.clone()))?,
None => return Ok(Value::nothing().tagged(value.tag)), None => {
return Err(ShellError::labeled_error(
"inc could not find field to replace",
"column name",
value.tag(),
))
}
},
Err(reason) => return Err(reason),
}; };
match value.item.replace_data_at_column_path( match value.item.replace_data_at_column_path(
value.tag(), value.tag(),
f, &f,
replacement.item.clone(), replacement.item.clone(),
) { ) {
Some(v) => return Ok(v), Some(v) => return Ok(v),

View File

@ -66,12 +66,15 @@ pub(crate) use crate::commands::RawCommandArgs;
pub(crate) use crate::context::CommandRegistry; pub(crate) use crate::context::CommandRegistry;
pub(crate) use crate::context::{AnchorLocation, Context}; pub(crate) use crate::context::{AnchorLocation, Context};
pub(crate) use crate::data::base as value; pub(crate) use crate::data::base as value;
pub(crate) use crate::data::meta::{Span, Spanned, SpannedItem, Tag, Tagged, TaggedItem}; pub(crate) use crate::data::meta::{
tag_for_tagged_list, HasFallibleSpan, HasSpan, Span, Spanned, SpannedItem, Tag, Tagged,
TaggedItem,
};
pub(crate) use crate::data::types::ExtractType; pub(crate) use crate::data::types::ExtractType;
pub(crate) use crate::data::{Primitive, Value}; pub(crate) use crate::data::{Primitive, Value};
pub(crate) use crate::env::host::handle_unexpected; pub(crate) use crate::env::host::handle_unexpected;
pub(crate) use crate::env::Host; pub(crate) use crate::env::Host;
pub(crate) use crate::errors::{CoerceInto, ShellError}; pub(crate) use crate::errors::{CoerceInto, ParseError, ShellError};
pub(crate) use crate::parser::hir::SyntaxShape; pub(crate) use crate::parser::hir::SyntaxShape;
pub(crate) use crate::parser::parse::parser::Number; pub(crate) use crate::parser::parse::parser::Number;
pub(crate) use crate::parser::registry::Signature; pub(crate) use crate::parser::registry::Signature;
@ -80,7 +83,7 @@ pub(crate) use crate::shell::help_shell::HelpShell;
pub(crate) use crate::shell::shell_manager::ShellManager; pub(crate) use crate::shell::shell_manager::ShellManager;
pub(crate) use crate::shell::value_shell::ValueShell; pub(crate) use crate::shell::value_shell::ValueShell;
pub(crate) use crate::stream::{InputStream, OutputStream}; pub(crate) use crate::stream::{InputStream, OutputStream};
pub(crate) use crate::traits::{HasTag, ToDebug}; pub(crate) use crate::traits::{DebugFormatter, FormatDebug, HasTag, ToDebug};
pub(crate) use crate::Text; pub(crate) use crate::Text;
pub(crate) use async_stream::stream as async_stream; pub(crate) use async_stream::stream as async_stream;
pub(crate) use bigdecimal::BigDecimal; pub(crate) use bigdecimal::BigDecimal;
@ -91,9 +94,12 @@ pub(crate) use num_traits::cast::{FromPrimitive, ToPrimitive};
pub(crate) use num_traits::identities::Zero; pub(crate) use num_traits::identities::Zero;
pub(crate) use serde::Deserialize; pub(crate) use serde::Deserialize;
pub(crate) use std::collections::VecDeque; pub(crate) use std::collections::VecDeque;
pub(crate) use std::fmt::Write;
pub(crate) use std::future::Future; pub(crate) use std::future::Future;
pub(crate) use std::sync::{Arc, Mutex}; pub(crate) use std::sync::{Arc, Mutex};
pub(crate) use itertools::Itertools;
pub trait FromInputStream { pub trait FromInputStream {
fn from_input_stream(self) -> OutputStream; fn from_input_stream(self) -> OutputStream;
} }

View File

@ -3,7 +3,7 @@ use crate::parser::hir::syntax_shape::{color_fallible_syntax, FlatShape, Pipelin
use crate::parser::hir::TokensIterator; use crate::parser::hir::TokensIterator;
use crate::parser::nom_input; use crate::parser::nom_input;
use crate::parser::parse::token_tree::TokenNode; use crate::parser::parse::token_tree::TokenNode;
use crate::{Span, Spanned, SpannedItem, Tag, Tagged, Text}; use crate::{HasSpan, Spanned, SpannedItem, Tag, Tagged, Text};
use ansi_term::Color; use ansi_term::Color;
use log::{log_enabled, trace}; use log::{log_enabled, trace};
use rustyline::completion::Completer; use rustyline::completion::Completer;
@ -65,9 +65,7 @@ impl Highlighter for Helper {
let mut tokens = TokensIterator::all(&tokens[..], v.span()); let mut tokens = TokensIterator::all(&tokens[..], v.span());
let text = Text::from(line); let text = Text::from(line);
let expand_context = self let expand_context = self.context.expand_context(&text);
.context
.expand_context(&text, Span::new(0, line.len() - 1));
#[cfg(not(coloring_in_tokens))] #[cfg(not(coloring_in_tokens))]
let shapes = { let shapes = {
@ -86,16 +84,17 @@ impl Highlighter for Helper {
let shapes = { let shapes = {
// We just constructed a token list that only contains a pipeline, so it can't fail // We just constructed a token list that only contains a pipeline, so it can't fail
color_fallible_syntax(&PipelineShape, &mut tokens, &expand_context).unwrap(); color_fallible_syntax(&PipelineShape, &mut tokens, &expand_context).unwrap();
tokens.with_tracer(|_, tracer| tracer.finish()); tokens.with_color_tracer(|_, tracer| tracer.finish());
tokens.state().shapes() tokens.state().shapes()
}; };
trace!(target: "nu::color_syntax", "{:#?}", tokens.tracer()); trace!(target: "nu::color_syntax", "{:#?}", tokens.color_tracer());
if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { if log_enabled!(target: "nu::color_syntax", log::Level::Debug) {
println!(""); println!("");
ptree::print_tree(&tokens.tracer().clone().print(Text::from(line))).unwrap(); ptree::print_tree(&tokens.color_tracer().clone().print(Text::from(line)))
.unwrap();
println!(""); println!("");
} }

View File

@ -1,14 +1,28 @@
use crate::prelude::*; use crate::prelude::*;
use std::fmt; use derive_new::new;
use std::fmt::{self, Write};
pub struct Debuggable<'a, T: ToDebug> { pub struct Debuggable<'a, T: FormatDebug> {
inner: &'a T, inner: &'a T,
source: &'a str, source: &'a str,
} }
impl FormatDebug for str {
fn fmt_debug(&self, f: &mut DebugFormatter, _source: &str) -> fmt::Result {
write!(f, "{}", self)
}
}
impl<T: ToDebug> fmt::Display for Debuggable<'_, T> { impl<T: ToDebug> fmt::Display for Debuggable<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt_debug(f, self.source) self.inner.fmt_debug(
&mut DebugFormatter::new(
f,
ansi_term::Color::White.bold(),
ansi_term::Color::Black.bold(),
),
self.source,
)
} }
} }
@ -16,13 +30,109 @@ pub trait HasTag {
fn tag(&self) -> Tag; fn tag(&self) -> Tag;
} }
pub trait ToDebug: Sized { #[derive(new)]
pub struct DebugFormatter<'me, 'args> {
formatter: &'me mut std::fmt::Formatter<'args>,
style: ansi_term::Style,
default_style: ansi_term::Style,
}
impl<'me, 'args> DebugFormatter<'me, 'args> {
pub fn say<'debuggable>(
&mut self,
kind: &str,
debuggable: Debuggable<'debuggable, impl FormatDebug>,
) -> std::fmt::Result {
write!(self, "{}", self.style.paint(kind))?;
write!(self, "{}", self.default_style.paint(" "))?;
write!(
self,
"{}",
self.default_style.paint(format!("{}", debuggable))
)
}
pub fn say_str<'debuggable>(
&mut self,
kind: &str,
string: impl AsRef<str>,
) -> std::fmt::Result {
write!(self, "{}", self.style.paint(kind))?;
write!(self, "{}", self.default_style.paint(" "))?;
write!(self, "{}", self.default_style.paint(string.as_ref()))
}
pub fn say_block(
&mut self,
kind: &str,
block: impl FnOnce(&mut Self) -> std::fmt::Result,
) -> std::fmt::Result {
write!(self, "{}", self.style.paint(kind))?;
write!(self, "{}", self.default_style.paint(" "))?;
block(self)
}
pub fn say_dict<'debuggable>(
&mut self,
kind: &str,
dict: indexmap::IndexMap<&str, String>,
) -> std::fmt::Result {
write!(self, "{}", self.style.paint(kind))?;
write!(self, "{}", self.default_style.paint(" "))?;
let last = dict.len() - 1;
for (i, (key, value)) in dict.into_iter().enumerate() {
write!(self, "{}", self.default_style.paint(key))?;
write!(self, "{}", self.default_style.paint("=["))?;
write!(self, "{}", self.style.paint(value))?;
write!(self, "{}", self.default_style.paint("]"))?;
if i != last {
write!(self, "{}", self.default_style.paint(" "))?;
}
}
Ok(())
}
}
impl<'a, 'b> std::fmt::Write for DebugFormatter<'a, 'b> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.formatter.write_str(s)
}
fn write_char(&mut self, c: char) -> std::fmt::Result {
self.formatter.write_char(c)
}
fn write_fmt(self: &mut Self, args: std::fmt::Arguments<'_>) -> std::fmt::Result {
self.formatter.write_fmt(args)
}
}
pub trait FormatDebug: std::fmt::Debug {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result;
}
pub trait ToDebug: Sized + FormatDebug {
fn debug<'a>(&'a self, source: &'a str) -> Debuggable<'a, Self>;
}
impl FormatDebug for Box<dyn FormatDebug> {
fn fmt_debug(&self, f: &mut DebugFormatter, source: &str) -> fmt::Result {
(&**self).fmt_debug(f, source)
}
}
impl<T> ToDebug for T
where
T: FormatDebug + Sized,
{
fn debug<'a>(&'a self, source: &'a str) -> Debuggable<'a, Self> { fn debug<'a>(&'a self, source: &'a str) -> Debuggable<'a, Self> {
Debuggable { Debuggable {
inner: self, inner: self,
source, source,
} }
} }
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result;
} }

View File

@ -5,6 +5,30 @@ use std::fmt;
use std::ops::Div; use std::ops::Div;
use std::path::{Component, Path, PathBuf}; use std::path::{Component, Path, PathBuf};
pub fn did_you_mean(
obj_source: &Value,
field_tried: &Tagged<String>,
) -> Option<Vec<(usize, String)>> {
let possibilities = obj_source.data_descriptors();
let mut possible_matches: Vec<_> = possibilities
.into_iter()
.map(|x| {
let word = x.clone();
let distance = natural::distance::levenshtein_distance(&word, &field_tried);
(distance, word)
})
.collect();
if possible_matches.len() > 0 {
possible_matches.sort();
return Some(possible_matches);
}
None
}
pub struct AbsoluteFile { pub struct AbsoluteFile {
inner: PathBuf, inner: PathBuf,
} }

200
tests/command_get_tests.rs Normal file
View File

@ -0,0 +1,200 @@
mod helpers;
use helpers as h;
use helpers::{Playground, Stub::*};
#[test]
fn get() {
Playground::setup("get_test_1", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"sample.toml",
r#"
nu_party_venue = "zion"
"#,
)]);
let actual = nu!(
cwd: dirs.test(), h::pipeline(
r#"
open sample.toml
| get nu_party_venue
| echo $it
"#
));
assert_eq!(actual, "zion");
})
}
#[test]
fn fetches_by_index_from_a_given_table() {
Playground::setup("get_test_2", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"sample.toml",
r#"
[package]
name = "nu"
version = "0.4.1"
authors = ["Yehuda Katz <wycats@gmail.com>", "Jonathan Turner <jonathan.d.turner@gmail.com>", "Andrés N. Robalino <andres@androbtech.com>"]
description = "When arepas shells are tasty and fun."
"#,
)]);
let actual = nu!(
cwd: dirs.test(), h::pipeline(
r#"
open sample.toml
| get package.authors.2
| echo $it
"#
));
assert_eq!(actual, "Andrés N. Robalino <andres@androbtech.com>");
})
}
#[test]
fn supports_fetching_rows_from_tables_using_columns_named_as_numbers() {
Playground::setup("get_test_3", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"sample.toml",
r#"
[package]
0 = "nu"
1 = "0.4.1"
"#,
)]);
let actual = nu!(
cwd: dirs.test(), h::pipeline(
r#"
open sample.toml
| get package.1
| echo $it
"#
));
assert_eq!(actual, "0.4.1");
})
}
#[test]
fn can_fetch_tables_or_rows_using_numbers_in_column_path() {
Playground::setup("get_test_4", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"sample.toml",
r#"
[package]
0 = "nu"
1 = "0.4.1"
2 = ["Yehuda Katz <wycats@gmail.com>", "Jonathan Turner <jonathan.d.turner@gmail.com>", "Andrés N. Robalino <andres@androbtech.com>"]
description = "When arepas shells are tasty and fun."
"#,
)]);
let actual = nu!(
cwd: dirs.test(), h::pipeline(
r#"
open sample.toml
| get package.2.1
| echo $it
"#
));
assert_eq!(actual, "Jonathan Turner <jonathan.d.turner@gmail.com>");
})
}
#[test]
fn fetches_more_than_one_column_member_path() {
Playground::setup("get_test_5", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"sample.toml",
r#"
[[fortune_tellers]]
name = "Andrés N. Robalino"
arepas = 1
[[fortune_tellers]]
name = "Jonathan Turner"
arepas = 1
[[fortune_tellers]]
name = "Yehuda Katz"
arepas = 1
"#,
)]);
let actual = nu!(
cwd: dirs.test(), h::pipeline(
r#"
open sample.toml
| get fortune_tellers.2.name fortune_tellers.0.name fortune_tellers.1.name
| nth 2
| echo $it
"#
));
assert_eq!(actual, "Jonathan Turner");
})
}
#[test]
fn errors_fetching_by_column_not_present() {
Playground::setup("get_test_6", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"sample.toml",
r#"
[taconushell]
sentence_words = ["Yo", "quiero", "taconushell"]
"#,
)]);
let actual = nu_error!(
cwd: dirs.test(), h::pipeline(
r#"
open sample.toml
| get taco
"#
));
assert!(actual.contains("Unknown column"));
assert!(actual.contains("did you mean 'taconushell'?"));
})
}
#[test]
fn errors_fetching_by_index_out_of_bounds_from_table() {
Playground::setup("get_test_7", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContent(
"sample.toml",
r#"
[spanish_lesson]
sentence_words = ["Yo", "quiero", "taconushell"]
"#,
)]);
let actual = nu_error!(
cwd: dirs.test(), h::pipeline(
r#"
open sample.toml
| get spanish_lesson.sentence_words.3
"#
));
assert!(actual.contains("Row not found"));
assert!(actual.contains("There isn't a row indexed at '3'"));
assert!(actual.contains("The table only has 3 rows (0..2)"))
})
}
#[test]
fn requires_at_least_one_column_member_path() {
Playground::setup("get_test_8", |dirs, sandbox| {
sandbox.with_files(vec![EmptyFile("andres.txt")]);
let actual = nu_error!(
cwd: dirs.test(), "ls | get"
);
assert!(actual.contains("requires member parameter"));
})
}