A real parser (lalrpop)

This commit is contained in:
Yehuda Katz 2019-05-25 23:54:41 -07:00
parent cd92f579c9
commit b74daa2e60
24 changed files with 2599 additions and 216 deletions

0
.cargo/config Normal file
View File

53
Cargo.lock generated
View File

@ -639,6 +639,18 @@ dependencies = [
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "error-chain" name = "error-chain"
version = "0.12.1" version = "0.12.1"
@ -828,6 +840,11 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.3.0" version = "1.3.0"
@ -985,11 +1002,15 @@ dependencies = [
"futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "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)", "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)", "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)", "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)", "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)", "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)", "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)", "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)", "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]] [[package]]
name = "prettyprint" name = "prettyprint"
version = "0.6.0" version = "0.6.0"
@ -1656,6 +1687,14 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "termion" name = "termion"
version = "1.5.2" version = "1.5.2"
@ -1854,6 +1893,15 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "winreg" name = "winreg"
version = "0.5.1" 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 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 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 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 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 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" "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 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 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 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 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 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" "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 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 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 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 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 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" "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 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 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 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 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 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" "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-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-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 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 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 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" "checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5"

View File

@ -33,6 +33,10 @@ tokio-fs = "0.1.6"
futures_codec = "0.2.2" futures_codec = "0.2.2"
term = "0.5.2" term = "0.5.2"
bytes = "0.4.12" 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] [dependencies.pancurses]
version = "0.16" version = "0.16"

9
Makefile.toml Normal file
View 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"]

View File

@ -1,14 +1,17 @@
use crate::prelude::*; use crate::prelude::*;
use crate::commands::classified::{ use crate::commands::classified::{
ClassifiedCommand, ClassifiedInputStream, ExternalCommand, InternalCommand, StreamNext, ClassifiedCommand, ClassifiedInputStream, ClassifiedPipeline, ExternalCommand, InternalCommand,
StreamNext,
}; };
use crate::context::Context; use crate::context::Context;
crate use crate::errors::ShellError; crate use crate::errors::ShellError;
crate use crate::format::{EntriesListView, GenericView}; crate use crate::format::{EntriesListView, GenericView};
use crate::object::Value; use crate::object::Value;
use crate::parser::{ParsedCommand, Pipeline};
use crate::stream::empty_stream; use crate::stream::empty_stream;
use log::debug;
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::{self, ColorMode, Config, Editor}; use rustyline::{self, ColorMode, Config, Editor};
use std::collections::VecDeque; use std::collections::VecDeque;
@ -32,20 +35,6 @@ impl<T> MaybeOwned<'a, T> {
} }
pub async fn cli() -> Result<(), Box<Error>> { 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()?; 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 { loop {
let readline = rl.readline(&format!( 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) if line.trim() == "" => LineResult::Success(line.clone()),
Ok(line) => { Ok(line) => {
let result = match crate::parser::shell_parser(&line) { let result = match crate::parser::parse(&line, &ctx.registry()) {
Err(err) => { Err(err) => {
return LineResult::Error(format!("{:?}", err)); return LineResult::Error(format!("{:?}", err));
} }
@ -149,17 +152,14 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Ok(val) => val, Ok(val) => val,
}; };
let parsed: Result<Vec<_>, _> = result debug!("=== Parsed ===");
.1 debug!("{:#?}", result);
.into_iter()
.map(|item| classify_command(&item, ctx))
.collect();
let parsed = parsed?; let pipeline = classify_pipeline(&result, ctx)?;
let mut input = ClassifiedInputStream::new(); let mut input = ClassifiedInputStream::new();
let mut iter = parsed.into_iter().peekable(); let mut iter = pipeline.commands.into_iter().peekable();
loop { loop {
let item: Option<ClassifiedCommand> = iter.next(); 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::Internal(_)),
Some(ClassifiedCommand::External(_)), Some(ClassifiedCommand::External(_)),
) => unimplemented!(), ) => return LineResult::Error(format!("Unimplemented Internal -> External",)),
( (
Some(ClassifiedCommand::External(left)), 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( fn classify_command(
command: &[crate::parser::Item], command: &ParsedCommand,
context: &Context, context: &Context,
) -> Result<ClassifiedCommand, ShellError> { ) -> 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: Vec<Value> = args.iter().map(|i| Value::from_expr(i)).collect();
let arg_list_strings: Vec<String> = command[1..].iter().map(|i| i.print()).collect(); let arg_list_strings: Vec<String> = args.iter().map(|i| i.print()).collect();
match *command_name { match command_name {
other => match context.has_command(*command_name) { other => match context.has_command(command_name) {
true => { true => {
let command = context.get_command(command_name); let command = context.get_command(command_name);
Ok(ClassifiedCommand::Internal(InternalCommand { Ok(ClassifiedCommand::Internal(InternalCommand {

View File

@ -1,10 +1,9 @@
use crate::prelude::*; use crate::prelude::*;
use futures::TryStreamExt; use bytes::{BufMut, BytesMut};
use futures_codec::{Encoder, Decoder, Framed}; use futures_codec::{Decoder, Encoder, Framed};
use std::io::{Error, ErrorKind};
use std::sync::Arc; use std::sync::Arc;
use subprocess::Exec; use subprocess::Exec;
use std::io::{Error, ErrorKind};
use bytes::{BufMut, BytesMut};
/// A simple `Codec` implementation that splits up data into lines. /// A simple `Codec` implementation that splits up data into lines.
pub struct LinesCodec {} pub struct LinesCodec {}
@ -37,7 +36,7 @@ impl Decoder for LinesCodec {
.map(Some) .map(Some)
.map_err(|e| Error::new(ErrorKind::InvalidData, e)) .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 { crate enum ClassifiedCommand {
Internal(InternalCommand), Internal(InternalCommand),
External(ExternalCommand), External(ExternalCommand),
@ -127,8 +130,7 @@ impl ExternalCommand {
cmd.push_str(" "); cmd.push_str(" ");
cmd.push_str(&arg); cmd.push_str(&arg);
} }
let process = Exec::shell(&cmd) let process = Exec::shell(&cmd).cwd(context.env.lock().unwrap().cwd());
.cwd(context.env.lock().unwrap().cwd());
let mut process = match stream_next { let mut process = match stream_next {
StreamNext::Last => process, StreamNext::Last => process,

View File

@ -1,6 +1,7 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::Value; use crate::object::{Primitive, Value};
use crate::prelude::*; use crate::prelude::*;
use log::debug;
// TODO: "Amount remaining" wrapper // TODO: "Amount remaining" wrapper
@ -13,18 +14,27 @@ pub fn split(args: CommandArgs) -> Result<OutputStream, ShellError> {
.map(move |v| match v { .map(move |v| match v {
Value::Primitive(Primitive::String(s)) => { Value::Primitive(Primitive::String(s)) => {
let splitter = args[0].as_string().unwrap(); let splitter = args[0].as_string().unwrap();
debug!("splitting with {:?}", splitter);
let split_result: Vec<_> = s.split(&splitter).filter(|s| s.trim() != "").collect(); let split_result: Vec<_> = s.split(&splitter).filter(|s| s.trim() != "").collect();
debug!("split result = {:?}", split_result);
if split_result.len() == (args.len() - 1) { if split_result.len() == (args.len() - 1) {
let mut dict = crate::object::Dictionary::default(); let mut dict = crate::object::Dictionary::default();
for (k, v) in split_result.iter().zip(args.iter().skip(1)) { 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)) ReturnValue::Value(Value::Object(dict))
} else { } else {
let mut dict = crate::object::Dictionary::default(); let mut dict = crate::object::Dictionary::default();
for k in args.iter().skip(1) { 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)) ReturnValue::Value(Value::Object(dict))
} }

View File

@ -7,24 +7,15 @@ pub fn r#where(args: CommandArgs) -> Result<OutputStream, ShellError> {
return Err(ShellError::string("select requires a field")); return Err(ShellError::string("select requires a field"));
} }
let field: Result<String, _> = args.args[0].as_string(); let operation = args.args[0].as_operation()?;
let field = field?; let field = operation.left.as_string()?;
let operator = operation.operator;
let right = operation.right;
let input = args.input; let input = args.input;
let operator = args.args[1].copy();
match operator { let objects = input
Value::Primitive(Primitive::Operator(operator)) => { .filter(move |item| futures::future::ready(find(&item, &field, &operator, &right)))
let right = args.args[2].copy(); .map(|item| ReturnValue::Value(item.copy()));
let objects = input Ok(objects.boxed())
.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"))
}
}
} }

View File

@ -1,10 +1,12 @@
use crate::parser::{CommandConfig, CommandRegistry};
use crate::prelude::*; use crate::prelude::*;
use indexmap::IndexMap;
use std::error::Error; use std::error::Error;
use std::sync::Arc; use std::sync::Arc;
pub struct Context { pub struct Context {
commands: indexmap::IndexMap<String, Arc<dyn Command>>, commands: IndexMap<String, Arc<dyn Command>>,
crate host: Arc<Mutex<dyn Host + Send>>, crate host: Arc<Mutex<dyn Host + Send>>,
crate env: Arc<Mutex<Environment>>, 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 { crate fn has_command(&self, name: &str) -> bool {
self.commands.contains_key(name) self.commands.contains_key(name)
} }
@ -48,3 +60,20 @@ impl Context {
command.run(command_args) 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(),
}
}
}

View File

@ -3,7 +3,7 @@ use crate::prelude::*;
use derive_new::new; use derive_new::new;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new)] #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new, Clone)]
pub struct ShellError { pub struct ShellError {
title: String, title: String,
error: Value, 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(),
}
}
}

View File

@ -43,6 +43,14 @@ impl RenderView for GenericView<'value> {
Ok(()) Ok(())
} }
Value::Operation(o) => {
host.stdout(&format!(
"Unexpectedly trying to print an operation: {:?}",
o
));
Ok(())
}
Value::Error(e) => { Value::Error(e) => {
host.stdout(&format!("{:?}", e)); host.stdout(&format!("{:?}", e));
Ok(()) Ok(())

View File

@ -19,6 +19,7 @@ mod stream;
use std::error::Error; use std::error::Error;
fn main() -> Result<(), Box<Error>> { fn main() -> Result<(), Box<Error>> {
pretty_env_logger::init();
futures::executor::block_on(crate::cli::cli())?; futures::executor::block_on(crate::cli::cli())?;
Ok(()) Ok(())
} }

View File

@ -1,10 +1,11 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::DataDescriptor; use crate::object::DataDescriptor;
use crate::parser::parse::Operator; use crate::parser::tokens::{self, Operator};
use crate::prelude::*; use crate::prelude::*;
use ansi_term::Color; use ansi_term::Color;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use chrono_humanize::Humanize; use chrono_humanize::Humanize;
use derive_new::new;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use std::time::SystemTime; use std::time::SystemTime;
@ -20,7 +21,6 @@ pub enum Primitive {
String(String), String(String),
Boolean(bool), Boolean(bool),
Date(DateTime<Utc>), Date(DateTime<Utc>),
Operator(Operator),
} }
impl Primitive { impl Primitive {
@ -51,27 +51,64 @@ impl Primitive {
(false, Some(_)) => format!(""), (false, Some(_)) => format!(""),
}, },
Primitive::Date(d) => format!("{}", d.humanize()), 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 { pub enum Value {
Primitive(Primitive), Primitive(Primitive),
Object(crate::object::Dictionary), Object(crate::object::Dictionary),
List(Vec<Value>), List(Vec<Value>),
Operation(Box<Operation>),
#[allow(unused)] #[allow(unused)]
Error(Box<ShellError>), Error(Box<ShellError>),
} }
impl Value { 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> { crate fn data_descriptors(&self) -> Vec<DataDescriptor> {
match self { match self {
Value::Primitive(_) => vec![DataDescriptor::value_of()], Value::Primitive(_) => vec![DataDescriptor::value_of()],
Value::Object(o) => o.data_descriptors(), Value::Object(o) => o.data_descriptors(),
Value::List(_) => vec![], Value::List(_) => vec![],
Value::Operation(_) => vec![],
Value::Error(_) => vec![], Value::Error(_) => vec![],
} }
} }
@ -81,6 +118,7 @@ impl Value {
Value::Primitive(_) => MaybeOwned::Owned(Value::nothing()), Value::Primitive(_) => MaybeOwned::Owned(Value::nothing()),
Value::Object(o) => o.get_data_by_key(name), Value::Object(o) => o.get_data_by_key(name),
Value::List(_) => MaybeOwned::Owned(Value::nothing()), Value::List(_) => MaybeOwned::Owned(Value::nothing()),
Value::Operation(_) => MaybeOwned::Owned(Value::nothing()),
Value::Error(_) => MaybeOwned::Owned(Value::nothing()), Value::Error(_) => MaybeOwned::Owned(Value::nothing()),
} }
} }
@ -90,6 +128,7 @@ impl Value {
p @ Value::Primitive(_) => MaybeOwned::Borrowed(p), p @ Value::Primitive(_) => MaybeOwned::Borrowed(p),
Value::Object(o) => o.get_data(desc), Value::Object(o) => o.get_data(desc),
Value::List(_) => MaybeOwned::Owned(Value::nothing()), Value::List(_) => MaybeOwned::Owned(Value::nothing()),
Value::Operation(_) => MaybeOwned::Owned(Value::nothing()),
Value::Error(_) => 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(); let list = l.iter().map(|i| i.copy()).collect();
Value::List(list) Value::List(list)
} }
Value::Operation(o) => Value::Operation(o.clone()),
Value::Error(e) => Value::Error(Box::new(e.copy_error())), Value::Error(e) => Value::Error(Box::new(e.copy_error())),
} }
} }
@ -111,6 +151,7 @@ impl Value {
Value::Primitive(p) => p.format(field_name), Value::Primitive(p) => p.format(field_name),
Value::Object(_) => format!("[object Object]"), Value::Object(_) => format!("[object Object]"),
Value::List(_) => format!("[list List]"), Value::List(_) => format!("[list List]"),
Value::Operation(_) => format!("[operation Operation]"),
Value::Error(e) => format!("{}", e), 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> { crate fn as_int(&self) -> Result<i64, ShellError> {
match self { match self {
Value::Primitive(Primitive::Int(i)) => Ok(*i), Value::Primitive(Primitive::Int(i)) => Ok(*i),

View File

@ -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 { impl DataDescriptor {
crate fn value_of() -> DataDescriptor { crate fn value_of() -> DataDescriptor {
DataDescriptor { DataDescriptor {

View File

@ -5,7 +5,7 @@ use crate::object::{Primitive, Value};
use indexmap::IndexMap; use indexmap::IndexMap;
use std::cmp::{Ordering, PartialOrd}; use std::cmp::{Ordering, PartialOrd};
#[derive(Debug, Default, Eq, PartialEq)] #[derive(Debug, Default, Eq, PartialEq, Clone)]
pub struct Dictionary { pub struct Dictionary {
entries: IndexMap<DataDescriptor, Value>, entries: IndexMap<DataDescriptor, Value>,
} }

View File

@ -1,4 +1,18 @@
crate mod completer; 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)))
}

View File

@ -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
View 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

File diff suppressed because it is too large Load Diff

21
src/parser/registry.rs Normal file
View 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
View 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 }
}
}

View File

@ -4,7 +4,7 @@ crate use crate::context::Context;
crate use crate::env::host::handle_unexpected; crate use crate::env::host::handle_unexpected;
crate use crate::env::{Environment, Host}; crate use crate::env::{Environment, Host};
crate use crate::errors::ShellError; 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 crate::stream::{single_output, InputStream, OutputStream};
crate use futures::{FutureExt, SinkExt, StreamExt}; crate use futures::{FutureExt, SinkExt, StreamExt};
crate use std::collections::VecDeque; crate use std::collections::VecDeque;

View File

@ -1,9 +1,13 @@
use crate::prelude::*;
use derive_new::new;
use rustyline::completion::Completer; use rustyline::completion::Completer;
use rustyline::completion::{self, FilenameCompleter}; use rustyline::completion::{self, FilenameCompleter};
use rustyline::line_buffer::LineBuffer; use rustyline::line_buffer::LineBuffer;
#[derive(new)]
crate struct NuCompleter { crate struct NuCompleter {
pub file_completer: FilenameCompleter, pub file_completer: FilenameCompleter,
pub commands: indexmap::IndexMap<String, Arc<dyn Command>>,
} }
impl Completer for NuCompleter { impl Completer for NuCompleter {
@ -15,10 +19,7 @@ impl Completer for NuCompleter {
pos: usize, pos: usize,
context: &rustyline::Context, context: &rustyline::Context,
) -> rustyline::Result<(usize, Vec<completion::Pair>)> { ) -> rustyline::Result<(usize, Vec<completion::Pair>)> {
let commands = [ let commands: Vec<String> = self.commands.keys().cloned().collect();
"ps", "ls", "cd", "view", "skip", "take", "select", "reject", "to-array", "where",
"sort-by",
];
let mut completions = self.file_completer.complete(line, pos, context)?.1; let mut completions = self.file_completer.complete(line, pos, context)?.1;

View File

@ -1,5 +1,6 @@
use crate::shell::completer::NuCompleter; use crate::shell::completer::NuCompleter;
use crate::prelude::*;
use rustyline::completion::{self, Completer, FilenameCompleter}; use rustyline::completion::{self, Completer, FilenameCompleter};
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter}; use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
@ -13,10 +14,11 @@ crate struct Helper {
} }
impl Helper { impl Helper {
crate fn new() -> Helper { crate fn new(commands: indexmap::IndexMap<String, Arc<dyn Command>>) -> Helper {
Helper { Helper {
completer: NuCompleter { completer: NuCompleter {
file_completer: FilenameCompleter::new(), file_completer: FilenameCompleter::new(),
commands,
}, },
highlighter: MatchingBracketHighlighter::new(), highlighter: MatchingBracketHighlighter::new(),
hinter: HistoryHinter {}, hinter: HistoryHinter {},