mirror of
https://github.com/nushell/nushell
synced 2024-12-25 12:33:17 +00:00
A real parser (lalrpop)
This commit is contained in:
parent
cd92f579c9
commit
b74daa2e60
24 changed files with 2599 additions and 216 deletions
0
.cargo/config
Normal file
0
.cargo/config
Normal file
53
Cargo.lock
generated
53
Cargo.lock
generated
|
@ -639,6 +639,18 @@ dependencies = [
|
|||
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.12.1"
|
||||
|
@ -828,6 +840,11 @@ dependencies = [
|
|||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop-util"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.3.0"
|
||||
|
@ -985,11 +1002,15 @@ dependencies = [
|
|||
"futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lalrpop-util 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 5.0.0-beta1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pancurses 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prettyprint 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sysinfo 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1179,6 +1200,16 @@ dependencies = [
|
|||
"xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyprint"
|
||||
version = "0.6.0"
|
||||
|
@ -1656,6 +1687,14 @@ dependencies = [
|
|||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.2"
|
||||
|
@ -1854,6 +1893,15 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wincolor"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.5.1"
|
||||
|
@ -1953,6 +2001,7 @@ dependencies = [
|
|||
"checksum enum-map-internals 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "38b0bacf3ea7aba18ce84032efc3f0fa29f5c814048b742ab3e64d07d83ac3e8"
|
||||
"checksum enumset 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac0a22e173f6570a7d69a2ab9e3fe79cf0dcdd0fdb162bfc932b97158f2b2a7"
|
||||
"checksum enumset_derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01d93b926a992a4a526c2a14e2faf734fdef5bf9d0a52ba69a2ca7d4494c284b"
|
||||
"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a"
|
||||
"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9"
|
||||
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
||||
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
||||
|
@ -1976,6 +2025,7 @@ dependencies = [
|
|||
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
|
||||
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum lalrpop-util 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9768f55211206d3c17181108d8facb80bdffc1f1e674a67b1dddb2743529ca19"
|
||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
||||
"checksum lexical-core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e82e023e062f1d25f807ad182008fba1b46538e999f908a08cc0c29e084462e"
|
||||
|
@ -2014,6 +2064,7 @@ dependencies = [
|
|||
"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
|
||||
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
|
||||
"checksum plist 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f4739851c08dd9a62a78beff2edf1a438517268b2c563c42fc6d9d3139e42d2a"
|
||||
"checksum pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df8b3f4e0475def7d9c2e5de8e5a1306949849761e107b360d03e98eafaffd61"
|
||||
"checksum prettyprint 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2705417f8aa07cb6308db42e55623479c1c9667942a4d5e4174c684e5da5590d"
|
||||
"checksum prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
|
@ -2069,6 +2120,7 @@ dependencies = [
|
|||
"checksum sysinfo 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65f0e28a49b7bf142cee89befd7077b40627d7cc70aa8a8acfe03afc26016c33"
|
||||
"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
|
||||
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
|
||||
"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
|
||||
"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea"
|
||||
"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
|
@ -2096,6 +2148,7 @@ dependencies = [
|
|||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
|
||||
"checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a"
|
||||
"checksum xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12ea8eda4b1eb72f02d148402e23832d56a33f55d8c1b2d5bcdde91d79d47cb1"
|
||||
"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5"
|
||||
|
|
|
@ -33,6 +33,10 @@ tokio-fs = "0.1.6"
|
|||
futures_codec = "0.2.2"
|
||||
term = "0.5.2"
|
||||
bytes = "0.4.12"
|
||||
log = "0.4.6"
|
||||
pretty_env_logger = "0.3.0"
|
||||
lalrpop-util = "0.17.0"
|
||||
regex = "1.1.6"
|
||||
|
||||
[dependencies.pancurses]
|
||||
version = "0.16"
|
||||
|
|
9
Makefile.toml
Normal file
9
Makefile.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[tasks.lalrpop]
|
||||
install_crate = { crate_name = "lalrpop", binary = "lalrpop", test_arg = "--help" }
|
||||
command = "lalrpop"
|
||||
args = ["src/parser/parser.lalrpop"]
|
||||
|
||||
[tasks.build]
|
||||
command = "cargo"
|
||||
args = ["build"]
|
||||
dependencies = ["lalrpop"]
|
77
src/cli.rs
77
src/cli.rs
|
@ -1,14 +1,17 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
use crate::commands::classified::{
|
||||
ClassifiedCommand, ClassifiedInputStream, ExternalCommand, InternalCommand, StreamNext,
|
||||
ClassifiedCommand, ClassifiedInputStream, ClassifiedPipeline, ExternalCommand, InternalCommand,
|
||||
StreamNext,
|
||||
};
|
||||
use crate::context::Context;
|
||||
crate use crate::errors::ShellError;
|
||||
crate use crate::format::{EntriesListView, GenericView};
|
||||
use crate::object::Value;
|
||||
use crate::parser::{ParsedCommand, Pipeline};
|
||||
use crate::stream::empty_stream;
|
||||
|
||||
use log::debug;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::{self, ColorMode, Config, Editor};
|
||||
use std::collections::VecDeque;
|
||||
|
@ -32,20 +35,6 @@ impl<T> MaybeOwned<'a, T> {
|
|||
}
|
||||
|
||||
pub async fn cli() -> Result<(), Box<Error>> {
|
||||
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
||||
let h = crate::shell::Helper::new();
|
||||
let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config);
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let _ = ansi_term::enable_ansi_support();
|
||||
}
|
||||
|
||||
rl.set_helper(Some(h));
|
||||
if rl.load_history("history.txt").is_err() {
|
||||
println!("No previous history.");
|
||||
}
|
||||
|
||||
let mut context = Context::basic()?;
|
||||
|
||||
{
|
||||
|
@ -68,6 +57,20 @@ pub async fn cli() -> Result<(), Box<Error>> {
|
|||
]);
|
||||
}
|
||||
|
||||
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
||||
let h = crate::shell::Helper::new(context.clone_commands());
|
||||
let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config);
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let _ = ansi_term::enable_ansi_support();
|
||||
}
|
||||
|
||||
rl.set_helper(Some(h));
|
||||
if rl.load_history("history.txt").is_err() {
|
||||
println!("No previous history.");
|
||||
}
|
||||
|
||||
loop {
|
||||
let readline = rl.readline(&format!(
|
||||
"{}> ",
|
||||
|
@ -141,7 +144,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
|||
Ok(line) if line.trim() == "" => LineResult::Success(line.clone()),
|
||||
|
||||
Ok(line) => {
|
||||
let result = match crate::parser::shell_parser(&line) {
|
||||
let result = match crate::parser::parse(&line, &ctx.registry()) {
|
||||
Err(err) => {
|
||||
return LineResult::Error(format!("{:?}", err));
|
||||
}
|
||||
|
@ -149,17 +152,14 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
|||
Ok(val) => val,
|
||||
};
|
||||
|
||||
let parsed: Result<Vec<_>, _> = result
|
||||
.1
|
||||
.into_iter()
|
||||
.map(|item| classify_command(&item, ctx))
|
||||
.collect();
|
||||
debug!("=== Parsed ===");
|
||||
debug!("{:#?}", result);
|
||||
|
||||
let parsed = parsed?;
|
||||
let pipeline = classify_pipeline(&result, ctx)?;
|
||||
|
||||
let mut input = ClassifiedInputStream::new();
|
||||
|
||||
let mut iter = parsed.into_iter().peekable();
|
||||
let mut iter = pipeline.commands.into_iter().peekable();
|
||||
|
||||
loop {
|
||||
let item: Option<ClassifiedCommand> = iter.next();
|
||||
|
@ -194,7 +194,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
|||
(
|
||||
Some(ClassifiedCommand::Internal(_)),
|
||||
Some(ClassifiedCommand::External(_)),
|
||||
) => unimplemented!(),
|
||||
) => return LineResult::Error(format!("Unimplemented Internal -> External",)),
|
||||
|
||||
(
|
||||
Some(ClassifiedCommand::External(left)),
|
||||
|
@ -245,17 +245,34 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
|||
}
|
||||
}
|
||||
|
||||
fn classify_pipeline(
|
||||
pipeline: &Pipeline,
|
||||
context: &Context,
|
||||
) -> Result<ClassifiedPipeline, ShellError> {
|
||||
let commands: Result<Vec<_>, _> = pipeline
|
||||
.commands
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|item| classify_command(&item, context))
|
||||
.collect();
|
||||
|
||||
Ok(ClassifiedPipeline {
|
||||
commands: commands?,
|
||||
})
|
||||
}
|
||||
|
||||
fn classify_command(
|
||||
command: &[crate::parser::Item],
|
||||
command: &ParsedCommand,
|
||||
context: &Context,
|
||||
) -> Result<ClassifiedCommand, ShellError> {
|
||||
let command_name = &command[0].name()?;
|
||||
let command_name = &command.name[..];
|
||||
let args = &command.args;
|
||||
|
||||
let arg_list: Vec<Value> = command[1..].iter().map(|i| i.as_value()).collect();
|
||||
let arg_list_strings: Vec<String> = command[1..].iter().map(|i| i.print()).collect();
|
||||
let arg_list: Vec<Value> = args.iter().map(|i| Value::from_expr(i)).collect();
|
||||
let arg_list_strings: Vec<String> = args.iter().map(|i| i.print()).collect();
|
||||
|
||||
match *command_name {
|
||||
other => match context.has_command(*command_name) {
|
||||
match command_name {
|
||||
other => match context.has_command(command_name) {
|
||||
true => {
|
||||
let command = context.get_command(command_name);
|
||||
Ok(ClassifiedCommand::Internal(InternalCommand {
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use crate::prelude::*;
|
||||
use futures::TryStreamExt;
|
||||
use futures_codec::{Encoder, Decoder, Framed};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures_codec::{Decoder, Encoder, Framed};
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::sync::Arc;
|
||||
use subprocess::Exec;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
|
||||
/// A simple `Codec` implementation that splits up data into lines.
|
||||
pub struct LinesCodec {}
|
||||
|
@ -37,7 +36,7 @@ impl Decoder for LinesCodec {
|
|||
.map(Some)
|
||||
.map_err(|e| Error::new(ErrorKind::InvalidData, e))
|
||||
}
|
||||
_ => Ok(None)
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +69,10 @@ impl ClassifiedInputStream {
|
|||
}
|
||||
}
|
||||
|
||||
crate struct ClassifiedPipeline {
|
||||
crate commands: Vec<ClassifiedCommand>,
|
||||
}
|
||||
|
||||
crate enum ClassifiedCommand {
|
||||
Internal(InternalCommand),
|
||||
External(ExternalCommand),
|
||||
|
@ -127,8 +130,7 @@ impl ExternalCommand {
|
|||
cmd.push_str(" ");
|
||||
cmd.push_str(&arg);
|
||||
}
|
||||
let process = Exec::shell(&cmd)
|
||||
.cwd(context.env.lock().unwrap().cwd());
|
||||
let process = Exec::shell(&cmd).cwd(context.env.lock().unwrap().cwd());
|
||||
|
||||
let mut process = match stream_next {
|
||||
StreamNext::Last => process,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::errors::ShellError;
|
||||
use crate::object::Value;
|
||||
use crate::object::{Primitive, Value};
|
||||
use crate::prelude::*;
|
||||
use log::debug;
|
||||
|
||||
// TODO: "Amount remaining" wrapper
|
||||
|
||||
|
@ -13,18 +14,27 @@ pub fn split(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
.map(move |v| match v {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
let splitter = args[0].as_string().unwrap();
|
||||
debug!("splitting with {:?}", splitter);
|
||||
let split_result: Vec<_> = s.split(&splitter).filter(|s| s.trim() != "").collect();
|
||||
|
||||
debug!("split result = {:?}", split_result);
|
||||
|
||||
if split_result.len() == (args.len() - 1) {
|
||||
let mut dict = crate::object::Dictionary::default();
|
||||
for (k, v) in split_result.iter().zip(args.iter().skip(1)) {
|
||||
dict.add(v.as_string().unwrap(), Value::Primitive(Primitive::String(k.to_string())));
|
||||
dict.add(
|
||||
v.as_string().unwrap(),
|
||||
Value::Primitive(Primitive::String(k.to_string())),
|
||||
);
|
||||
}
|
||||
ReturnValue::Value(Value::Object(dict))
|
||||
} else {
|
||||
let mut dict = crate::object::Dictionary::default();
|
||||
for k in args.iter().skip(1) {
|
||||
dict.add(k.as_string().unwrap().trim(), Value::Primitive(Primitive::String("".to_string())));
|
||||
dict.add(
|
||||
k.as_string().unwrap().trim(),
|
||||
Value::Primitive(Primitive::String("".to_string())),
|
||||
);
|
||||
}
|
||||
ReturnValue::Value(Value::Object(dict))
|
||||
}
|
||||
|
|
|
@ -7,24 +7,15 @@ pub fn r#where(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
return Err(ShellError::string("select requires a field"));
|
||||
}
|
||||
|
||||
let field: Result<String, _> = args.args[0].as_string();
|
||||
let field = field?;
|
||||
let operation = args.args[0].as_operation()?;
|
||||
let field = operation.left.as_string()?;
|
||||
let operator = operation.operator;
|
||||
let right = operation.right;
|
||||
let input = args.input;
|
||||
let operator = args.args[1].copy();
|
||||
|
||||
match operator {
|
||||
Value::Primitive(Primitive::Operator(operator)) => {
|
||||
let right = args.args[2].copy();
|
||||
let objects = input
|
||||
.filter(move |item| futures::future::ready(find(&item, &field, &operator, &right)))
|
||||
.map(|item| ReturnValue::Value(item.copy()));
|
||||
|
||||
let objects = input
|
||||
.filter(move |item| futures::future::ready(find(&item, &field, &operator, &right)))
|
||||
.map(|item| ReturnValue::Value(item.copy()));
|
||||
|
||||
Ok(objects.boxed())
|
||||
}
|
||||
x => {
|
||||
println!("{:?}", x);
|
||||
Err(ShellError::string("expected a comparison operator"))
|
||||
}
|
||||
}
|
||||
Ok(objects.boxed())
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use crate::parser::{CommandConfig, CommandRegistry};
|
||||
use crate::prelude::*;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Context {
|
||||
commands: indexmap::IndexMap<String, Arc<dyn Command>>,
|
||||
commands: IndexMap<String, Arc<dyn Command>>,
|
||||
crate host: Arc<Mutex<dyn Host + Send>>,
|
||||
crate env: Arc<Mutex<Environment>>,
|
||||
}
|
||||
|
@ -24,6 +26,16 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn clone_commands(&self) -> indexmap::IndexMap<String, Arc<dyn Command>> {
|
||||
self.commands.clone()
|
||||
}
|
||||
|
||||
pub fn registry(&self) -> CommandMap {
|
||||
CommandMap {
|
||||
commands: self.clone_commands(),
|
||||
}
|
||||
}
|
||||
|
||||
crate fn has_command(&self, name: &str) -> bool {
|
||||
self.commands.contains_key(name)
|
||||
}
|
||||
|
@ -48,3 +60,20 @@ impl Context {
|
|||
command.run(command_args)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandMap {
|
||||
#[allow(unused)]
|
||||
commands: IndexMap<String, Arc<dyn Command>>,
|
||||
}
|
||||
|
||||
impl CommandRegistry for CommandMap {
|
||||
fn get(&self, name: &str) -> CommandConfig {
|
||||
CommandConfig {
|
||||
name: name.to_string(),
|
||||
mandatory_positional: vec![],
|
||||
optional_positional: vec![],
|
||||
rest_positional: true,
|
||||
named: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::prelude::*;
|
|||
|
||||
use derive_new::new;
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new)]
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new, Clone)]
|
||||
pub struct ShellError {
|
||||
title: String,
|
||||
error: Value,
|
||||
|
@ -60,3 +60,12 @@ impl std::convert::From<subprocess::PopenError> for ShellError {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<nom::Err<(&str, nom::error::ErrorKind)>> for ShellError {
|
||||
fn from(input: nom::Err<(&str, nom::error::ErrorKind)>) -> ShellError {
|
||||
ShellError {
|
||||
title: format!("{:?}", input),
|
||||
error: Value::nothing(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,14 @@ impl RenderView for GenericView<'value> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
Value::Operation(o) => {
|
||||
host.stdout(&format!(
|
||||
"Unexpectedly trying to print an operation: {:?}",
|
||||
o
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Value::Error(e) => {
|
||||
host.stdout(&format!("{:?}", e));
|
||||
Ok(())
|
||||
|
|
|
@ -19,6 +19,7 @@ mod stream;
|
|||
use std::error::Error;
|
||||
|
||||
fn main() -> Result<(), Box<Error>> {
|
||||
pretty_env_logger::init();
|
||||
futures::executor::block_on(crate::cli::cli())?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::errors::ShellError;
|
||||
use crate::object::DataDescriptor;
|
||||
use crate::parser::parse::Operator;
|
||||
use crate::parser::tokens::{self, Operator};
|
||||
use crate::prelude::*;
|
||||
use ansi_term::Color;
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono_humanize::Humanize;
|
||||
use derive_new::new;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::time::SystemTime;
|
||||
|
||||
|
@ -20,7 +21,6 @@ pub enum Primitive {
|
|||
String(String),
|
||||
Boolean(bool),
|
||||
Date(DateTime<Utc>),
|
||||
Operator(Operator),
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
|
@ -51,27 +51,64 @@ impl Primitive {
|
|||
(false, Some(_)) => format!(""),
|
||||
},
|
||||
Primitive::Date(d) => format!("{}", d.humanize()),
|
||||
Primitive::Operator(o) => o.print(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)]
|
||||
pub struct Operation {
|
||||
crate left: Value,
|
||||
crate operator: Operator,
|
||||
crate right: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
|
||||
pub enum Value {
|
||||
Primitive(Primitive),
|
||||
Object(crate::object::Dictionary),
|
||||
List(Vec<Value>),
|
||||
Operation(Box<Operation>),
|
||||
|
||||
#[allow(unused)]
|
||||
Error(Box<ShellError>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
crate fn from_leaf(leaf: &tokens::Leaf) -> Value {
|
||||
use tokens::*;
|
||||
|
||||
match leaf {
|
||||
Leaf::String(s) => Value::string(s),
|
||||
Leaf::Bare(s) => Value::string(s),
|
||||
Leaf::Boolean(b) => Value::boolean(*b),
|
||||
Leaf::Int(i) => Value::int(*i),
|
||||
}
|
||||
}
|
||||
|
||||
crate fn from_expr(expr: &tokens::Expression) -> Value {
|
||||
use tokens::*;
|
||||
|
||||
match expr {
|
||||
Expression::Leaf(leaf) => Value::from_leaf(leaf),
|
||||
|
||||
Expression::Binary(Binary {
|
||||
left,
|
||||
operator,
|
||||
right,
|
||||
}) => Value::Operation(Box::new(Operation::new(
|
||||
Value::from_leaf(left),
|
||||
*operator,
|
||||
Value::from_leaf(right),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
crate fn data_descriptors(&self) -> Vec<DataDescriptor> {
|
||||
match self {
|
||||
Value::Primitive(_) => vec![DataDescriptor::value_of()],
|
||||
Value::Object(o) => o.data_descriptors(),
|
||||
Value::List(_) => vec![],
|
||||
Value::Operation(_) => vec![],
|
||||
Value::Error(_) => vec![],
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +118,7 @@ impl Value {
|
|||
Value::Primitive(_) => MaybeOwned::Owned(Value::nothing()),
|
||||
Value::Object(o) => o.get_data_by_key(name),
|
||||
Value::List(_) => MaybeOwned::Owned(Value::nothing()),
|
||||
Value::Operation(_) => MaybeOwned::Owned(Value::nothing()),
|
||||
Value::Error(_) => MaybeOwned::Owned(Value::nothing()),
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +128,7 @@ impl Value {
|
|||
p @ Value::Primitive(_) => MaybeOwned::Borrowed(p),
|
||||
Value::Object(o) => o.get_data(desc),
|
||||
Value::List(_) => MaybeOwned::Owned(Value::nothing()),
|
||||
Value::Operation(_) => MaybeOwned::Owned(Value::nothing()),
|
||||
Value::Error(_) => MaybeOwned::Owned(Value::nothing()),
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +141,7 @@ impl Value {
|
|||
let list = l.iter().map(|i| i.copy()).collect();
|
||||
Value::List(list)
|
||||
}
|
||||
Value::Operation(o) => Value::Operation(o.clone()),
|
||||
Value::Error(e) => Value::Error(Box::new(e.copy_error())),
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +151,7 @@ impl Value {
|
|||
Value::Primitive(p) => p.format(field_name),
|
||||
Value::Object(_) => format!("[object Object]"),
|
||||
Value::List(_) => format!("[list List]"),
|
||||
Value::Operation(_) => format!("[operation Operation]"),
|
||||
Value::Error(e) => format!("{}", e),
|
||||
}
|
||||
}
|
||||
|
@ -127,6 +168,18 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
crate fn as_operation(&self) -> Result<Operation, ShellError> {
|
||||
match self {
|
||||
Value::Operation(o) => Ok(*o.clone()),
|
||||
|
||||
// TODO: this should definitely be more general with better errors
|
||||
other => Err(ShellError::string(format!(
|
||||
"Expected operation, got {:?}",
|
||||
other
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
crate fn as_int(&self) -> Result<i64, ShellError> {
|
||||
match self {
|
||||
Value::Primitive(Primitive::Int(i)) => Ok(*i),
|
||||
|
|
|
@ -79,6 +79,16 @@ impl DescriptorName {
|
|||
}
|
||||
}
|
||||
|
||||
impl Clone for DataDescriptor {
|
||||
fn clone(&self) -> DataDescriptor {
|
||||
DataDescriptor {
|
||||
name: self.name.clone(),
|
||||
readonly: self.readonly,
|
||||
ty: self.ty.copy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DataDescriptor {
|
||||
crate fn value_of() -> DataDescriptor {
|
||||
DataDescriptor {
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::object::{Primitive, Value};
|
|||
use indexmap::IndexMap;
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq)]
|
||||
#[derive(Debug, Default, Eq, PartialEq, Clone)]
|
||||
pub struct Dictionary {
|
||||
entries: IndexMap<DataDescriptor, Value>,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,18 @@
|
|||
crate mod completer;
|
||||
crate mod parse;
|
||||
crate mod parser;
|
||||
crate mod registry;
|
||||
crate mod tokens;
|
||||
|
||||
crate use self::parse::{shell_parser, Item};
|
||||
crate use registry::{CommandConfig, CommandRegistry};
|
||||
crate use tokens::{ParsedCommand, Pipeline};
|
||||
|
||||
use crate::errors::ShellError;
|
||||
use parser::PipelineParser;
|
||||
|
||||
pub fn parse(input: &str, _registry: &dyn CommandRegistry) -> Result<Pipeline, ShellError> {
|
||||
let parser = PipelineParser::new();
|
||||
|
||||
parser
|
||||
.parse(input)
|
||||
.map_err(|e| ShellError::string(format!("{:?}", e)))
|
||||
}
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::{escaped, is_a, is_not, tag};
|
||||
use nom::character::complete::one_of;
|
||||
use nom::multi::separated_list;
|
||||
use nom::sequence::{preceded, terminated};
|
||||
use nom::IResult;
|
||||
use nom::{complete, named, ws};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Item {
|
||||
Quoted(String),
|
||||
Bare(String),
|
||||
Int(i64),
|
||||
Boolean(bool),
|
||||
Operator(Operator),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Operator {
|
||||
Equal,
|
||||
NotEqual,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
LessThanOrEqual,
|
||||
GreaterThanOrEqual,
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
pub fn print(&self) -> String {
|
||||
match *self {
|
||||
Operator::Equal => "==".to_string(),
|
||||
Operator::NotEqual => "!=".to_string(),
|
||||
Operator::LessThan => "<".to_string(),
|
||||
Operator::GreaterThan => ">".to_string(),
|
||||
Operator::LessThanOrEqual => "<=".to_string(),
|
||||
Operator::GreaterThanOrEqual => ">=".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Operator {
|
||||
type Err = ();
|
||||
fn from_str(input: &str) -> Result<Self, <Self as std::str::FromStr>::Err> {
|
||||
match input {
|
||||
"==" => Ok(Operator::Equal),
|
||||
"!=" => Ok(Operator::NotEqual),
|
||||
"<" => Ok(Operator::LessThan),
|
||||
">" => Ok(Operator::GreaterThan),
|
||||
"<=" => Ok(Operator::LessThanOrEqual),
|
||||
">=" => Ok(Operator::GreaterThanOrEqual),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
crate fn as_value(&self) -> Value {
|
||||
match self {
|
||||
Item::Quoted(s) => Value::Primitive(Primitive::String(s.clone())),
|
||||
Item::Bare(s) => Value::Primitive(Primitive::String(s.clone())),
|
||||
Item::Int(i) => Value::Primitive(Primitive::Int(*i)),
|
||||
Item::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)),
|
||||
Item::Operator(o) => Value::Primitive(Primitive::Operator(o.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self) -> String {
|
||||
match self {
|
||||
Item::Bare(s) => format!("{}", s),
|
||||
Item::Quoted(s) => format!("{}", s),
|
||||
Item::Int(i) => format!("{:?}", i),
|
||||
Item::Boolean(b) => format!("{:?}", b),
|
||||
Item::Operator(o) => o.print(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
crate fn name(&self) -> Result<&str, ShellError> {
|
||||
match self {
|
||||
Item::Quoted(s) => Ok(s),
|
||||
Item::Bare(s) => Ok(s),
|
||||
Item::Boolean(i) => Err(ShellError::string(format!("{} is not a valid command", i))),
|
||||
Item::Int(i) => Err(ShellError::string(format!("{} is not a valid command", i))),
|
||||
Item::Operator(x) => Err(ShellError::string(format!(
|
||||
"{:?} is not a valid command",
|
||||
x
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn esc(s: &str) -> IResult<&str, &str> {
|
||||
escaped(is_not("\\\""), '\\', one_of("\"n\\"))(s)
|
||||
}
|
||||
|
||||
fn quoted(s: &str) -> IResult<&str, Item> {
|
||||
terminated(preceded(tag("\""), esc), tag("\""))(s)
|
||||
.map(|(a, b)| (a, Item::Quoted(b.to_string())))
|
||||
}
|
||||
|
||||
fn unquoted(s: &str) -> IResult<&str, Item> {
|
||||
is_not(" |")(s).map(|(a, b)| (a, Item::Bare(b.to_string())))
|
||||
}
|
||||
|
||||
fn operator(s: &str) -> IResult<&str, Item> {
|
||||
alt((
|
||||
tag("=="),
|
||||
tag("!="),
|
||||
tag("<"),
|
||||
tag(">"),
|
||||
tag("<="),
|
||||
tag(">="),
|
||||
))(s)
|
||||
.map(|(a, b)| (a, Item::Operator(FromStr::from_str(b).unwrap())))
|
||||
}
|
||||
|
||||
fn int(s: &str) -> IResult<&str, Item> {
|
||||
is_a("1234567890")(s).map(|(a, b)| (a, Item::Int(FromStr::from_str(b).unwrap())))
|
||||
}
|
||||
|
||||
fn boolean(s: &str) -> IResult<&str, Item> {
|
||||
alt((tag("true"), tag("false")))(s)
|
||||
.map(|(a, b)| (a, Item::Boolean(FromStr::from_str(b).unwrap())))
|
||||
}
|
||||
|
||||
fn command_token(s: &str) -> IResult<&str, Item> {
|
||||
alt((boolean, int, operator, quoted, unquoted))(s)
|
||||
}
|
||||
|
||||
fn command_args(s: &str) -> IResult<&str, Vec<Item>> {
|
||||
separated_list(tag(" "), command_token)(s)
|
||||
}
|
||||
|
||||
named!(
|
||||
pub shell_parser(&str) -> Vec<Vec<Item>>,
|
||||
complete!(
|
||||
ws!(
|
||||
separated_list!(tag("|"), command_args)
|
||||
)
|
||||
)
|
||||
);
|
56
src/parser/parser.lalrpop
Normal file
56
src/parser/parser.lalrpop
Normal file
|
@ -0,0 +1,56 @@
|
|||
use std::str::FromStr;
|
||||
use crate::parser::tokens::*;
|
||||
|
||||
grammar;
|
||||
|
||||
pub Pipeline: Pipeline = {
|
||||
<first:Command> => Pipeline::new(vec![first]),
|
||||
<first:Command> <rest: ( "|" <Command> )+> => Pipeline::from_parts(first, rest),
|
||||
}
|
||||
|
||||
Command: ParsedCommand = {
|
||||
<command:RawBareWord> <expr:Expr*> => ParsedCommand::new(command, expr)
|
||||
}
|
||||
|
||||
Expr: Expression = {
|
||||
<Leaf> => Expression::Leaf(<>),
|
||||
<Binary> => Expression::Binary(<>),
|
||||
}
|
||||
|
||||
Operator: Operator = {
|
||||
"==" => Operator::Equal,
|
||||
"!=" => Operator::NotEqual,
|
||||
"<" => Operator::LessThan,
|
||||
">" => Operator::GreaterThan,
|
||||
"<=" => Operator::LessThanOrEqual,
|
||||
">=" => Operator::GreaterThanOrEqual
|
||||
}
|
||||
|
||||
Binary: Binary = {
|
||||
<left:Leaf> <op:Operator> <right:Leaf> => Binary::new(left, op, right),
|
||||
}
|
||||
|
||||
Flag: Flag = {
|
||||
"-" <RawBareWord> => Flag::Shorthand(<>.to_string()),
|
||||
"--" <RawBareWord> => Flag::Longhand(<>.to_string()),
|
||||
}
|
||||
|
||||
String: String = {
|
||||
SQString,
|
||||
DQString,
|
||||
}
|
||||
|
||||
Leaf: Leaf = {
|
||||
<String> => Leaf::String(<>),
|
||||
<Num> => Leaf::Int(<>),
|
||||
<RawBareWord> => match <>.as_ref() {
|
||||
"true" => Leaf::Boolean(true),
|
||||
"false" => Leaf::Boolean(false),
|
||||
_ => Leaf::Bare(<>),
|
||||
}
|
||||
}
|
||||
|
||||
RawBareWord: String = <s:r#"[^0-9"'\-][^\s]*"#> => <>.to_string();
|
||||
DQString: String = <s:r#""([^"]|\\")*""#> => s[1..s.len() - 1].to_string();
|
||||
SQString: String = <s:r#"'([^']|\\')*'"#> => s[1..s.len() - 1].to_string();
|
||||
Num: i64 = <s:r"-?[0-9]+"> => i64::from_str(s).unwrap();
|
2109
src/parser/parser.rs
Normal file
2109
src/parser/parser.rs
Normal file
File diff suppressed because it is too large
Load diff
21
src/parser/registry.rs
Normal file
21
src/parser/registry.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use indexmap::IndexMap;
|
||||
|
||||
#[allow(unused)]
|
||||
pub enum CommandType {
|
||||
Switch,
|
||||
Single,
|
||||
Array,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct CommandConfig {
|
||||
crate name: String,
|
||||
crate mandatory_positional: Vec<String>,
|
||||
crate optional_positional: Vec<String>,
|
||||
crate rest_positional: bool,
|
||||
crate named: IndexMap<String, CommandType>,
|
||||
}
|
||||
|
||||
pub trait CommandRegistry {
|
||||
fn get(&self, name: &str) -> CommandConfig;
|
||||
}
|
128
src/parser/tokens.rs
Normal file
128
src/parser/tokens.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
use derive_new::new;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Operator {
|
||||
Equal,
|
||||
NotEqual,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
LessThanOrEqual,
|
||||
GreaterThanOrEqual,
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
pub fn print(&self) -> String {
|
||||
match *self {
|
||||
Operator::Equal => "==".to_string(),
|
||||
Operator::NotEqual => "!=".to_string(),
|
||||
Operator::LessThan => "<".to_string(),
|
||||
Operator::GreaterThan => ">".to_string(),
|
||||
Operator::LessThanOrEqual => "<=".to_string(),
|
||||
Operator::GreaterThanOrEqual => ">=".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Operator {
|
||||
type Err = ();
|
||||
fn from_str(input: &str) -> Result<Self, <Self as std::str::FromStr>::Err> {
|
||||
match input {
|
||||
"==" => Ok(Operator::Equal),
|
||||
"!=" => Ok(Operator::NotEqual),
|
||||
"<" => Ok(Operator::LessThan),
|
||||
">" => Ok(Operator::GreaterThan),
|
||||
"<=" => Ok(Operator::LessThanOrEqual),
|
||||
">=" => Ok(Operator::GreaterThanOrEqual),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expression {
|
||||
Leaf(Leaf),
|
||||
Binary(Binary),
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
crate fn print(&self) -> String {
|
||||
match self {
|
||||
Expression::Leaf(l) => l.print(),
|
||||
Expression::Binary(b) => b.print(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Leaf {
|
||||
String(String),
|
||||
Bare(String),
|
||||
Boolean(bool),
|
||||
Int(i64),
|
||||
}
|
||||
|
||||
impl Leaf {
|
||||
fn print(&self) -> String {
|
||||
match self {
|
||||
Leaf::String(s) => format!("{:?}", s),
|
||||
Leaf::Bare(s) => format!("{}", s),
|
||||
Leaf::Boolean(b) => format!("{}", b),
|
||||
Leaf::Int(i) => format!("{}", i),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, new)]
|
||||
pub struct Binary {
|
||||
crate left: Leaf,
|
||||
crate operator: Operator,
|
||||
crate right: Leaf,
|
||||
}
|
||||
|
||||
impl Binary {
|
||||
fn print(&self) -> String {
|
||||
format!(
|
||||
"{} {} {}",
|
||||
self.left.print(),
|
||||
self.operator.print(),
|
||||
self.right.print()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Flag {
|
||||
Shorthand(String),
|
||||
Longhand(String),
|
||||
}
|
||||
|
||||
impl Flag {
|
||||
#[allow(unused)]
|
||||
fn print(&self) -> String {
|
||||
match self {
|
||||
Flag::Shorthand(s) => format!("-{}", s),
|
||||
Flag::Longhand(s) => format!("--{}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(new, Debug, Clone)]
|
||||
pub struct ParsedCommand {
|
||||
crate name: String,
|
||||
crate args: Vec<Expression>,
|
||||
}
|
||||
|
||||
#[derive(new, Debug)]
|
||||
pub struct Pipeline {
|
||||
crate commands: Vec<ParsedCommand>,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
crate fn from_parts(command: ParsedCommand, rest: Vec<ParsedCommand>) -> Pipeline {
|
||||
let mut commands = vec![command];
|
||||
commands.extend(rest);
|
||||
|
||||
Pipeline { commands }
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ crate use crate::context::Context;
|
|||
crate use crate::env::host::handle_unexpected;
|
||||
crate use crate::env::{Environment, Host};
|
||||
crate use crate::errors::ShellError;
|
||||
crate use crate::object::{Primitive, Value};
|
||||
crate use crate::object::Value;
|
||||
crate use crate::stream::{single_output, InputStream, OutputStream};
|
||||
crate use futures::{FutureExt, SinkExt, StreamExt};
|
||||
crate use std::collections::VecDeque;
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
use crate::prelude::*;
|
||||
use derive_new::new;
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::completion::{self, FilenameCompleter};
|
||||
use rustyline::line_buffer::LineBuffer;
|
||||
|
||||
#[derive(new)]
|
||||
crate struct NuCompleter {
|
||||
pub file_completer: FilenameCompleter,
|
||||
pub commands: indexmap::IndexMap<String, Arc<dyn Command>>,
|
||||
}
|
||||
|
||||
impl Completer for NuCompleter {
|
||||
|
@ -15,10 +19,7 @@ impl Completer for NuCompleter {
|
|||
pos: usize,
|
||||
context: &rustyline::Context,
|
||||
) -> rustyline::Result<(usize, Vec<completion::Pair>)> {
|
||||
let commands = [
|
||||
"ps", "ls", "cd", "view", "skip", "take", "select", "reject", "to-array", "where",
|
||||
"sort-by",
|
||||
];
|
||||
let commands: Vec<String> = self.commands.keys().cloned().collect();
|
||||
|
||||
let mut completions = self.file_completer.complete(line, pos, context)?.1;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::shell::completer::NuCompleter;
|
||||
|
||||
use crate::prelude::*;
|
||||
use rustyline::completion::{self, Completer, FilenameCompleter};
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
|
||||
|
@ -13,10 +14,11 @@ crate struct Helper {
|
|||
}
|
||||
|
||||
impl Helper {
|
||||
crate fn new() -> Helper {
|
||||
crate fn new(commands: indexmap::IndexMap<String, Arc<dyn Command>>) -> Helper {
|
||||
Helper {
|
||||
completer: NuCompleter {
|
||||
file_completer: FilenameCompleter::new(),
|
||||
commands,
|
||||
},
|
||||
highlighter: MatchingBracketHighlighter::new(),
|
||||
hinter: HistoryHinter {},
|
||||
|
|
Loading…
Reference in a new issue