Evaluator MVP (#39)

Evaluator, MVP
This commit is contained in:
Yehuda Katz 2019-05-27 23:45:18 -07:00 committed by GitHub
parent d45750617b
commit d5255f6dbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2258 additions and 654 deletions

12
Cargo.lock generated
View File

@ -786,6 +786,16 @@ dependencies = [
"futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "getset"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.3.0" version = "0.3.0"
@ -1000,6 +1010,7 @@ dependencies = [
"futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
"getset 0.0.7 (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)", "lalrpop-util 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2020,6 +2031,7 @@ dependencies = [
"checksum futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "49dcfdacd6b5974ca0b9b78bc38ffd1071da0206179735c3df82e279f5b784e4" "checksum futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "49dcfdacd6b5974ca0b9b78bc38ffd1071da0206179735c3df82e279f5b784e4"
"checksum futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f7a0451b9c5047c2b9ab93425ffd0793165511e93c04b977cd45fbd41c6e34b2" "checksum futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f7a0451b9c5047c2b9ab93425ffd0793165511e93c04b977cd45fbd41c6e34b2"
"checksum futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b60f48aa03e365df015d2fbf0b79f17b440350c268a5e20305da17b394adcc1e" "checksum futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b60f48aa03e365df015d2fbf0b79f17b440350c268a5e20305da17b394adcc1e"
"checksum getset 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "19fbde0fad0c1c1f9474694b1f5c9ba22b09f2f74f74e6d2bd19c43f6656e2cb"
"checksum hashbrown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "570178d5e4952010d138b0f1d581271ff3a02406d990f887d1e87e3d6e43b0ac" "checksum hashbrown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "570178d5e4952010d138b0f1d581271ff3a02406d990f887d1e87e3d6e43b0ac"
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" "checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"

View File

@ -40,6 +40,7 @@ regex = "1.1.6"
serde = "1.0.91" serde = "1.0.91"
serde_json = "1.0.39" serde_json = "1.0.39"
serde_derive = "1.0.91" serde_derive = "1.0.91"
getset = "0.0.7"
[dependencies.pancurses] [dependencies.pancurses]
version = "0.16" version = "0.16"

View File

@ -7,3 +7,8 @@ args = ["src/parser/parser.lalrpop"]
command = "cargo" command = "cargo"
args = ["build"] args = ["build"]
dependencies = ["lalrpop"] dependencies = ["lalrpop"]
[tasks.run]
command = "cargo"
args = ["run"]
dependencies = ["build"]

View File

@ -6,6 +6,7 @@ use crate::commands::classified::{
}; };
use crate::context::Context; use crate::context::Context;
crate use crate::errors::ShellError; crate use crate::errors::ShellError;
use crate::evaluate::Scope;
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::parser::{ParsedCommand, Pipeline};
@ -17,7 +18,6 @@ use rustyline::{self, ColorMode, Config, Editor};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::error::Error; use std::error::Error;
use std::iter::Iterator; use std::iter::Iterator;
use std::sync::Arc;
#[derive(Debug)] #[derive(Debug)]
pub enum MaybeOwned<'a, T> { pub enum MaybeOwned<'a, T> {
@ -41,23 +41,23 @@ pub async fn cli() -> Result<(), Box<Error>> {
use crate::commands::*; use crate::commands::*;
context.add_commands(vec![ context.add_commands(vec![
("format-list", Arc::new(format_list)), command("format-list", format_list),
("ps", Arc::new(ps::ps)), command("ps", ps::ps),
("ls", Arc::new(ls::ls)), command("ls", ls::ls),
("cd", Arc::new(cd::cd)), command("cd", cd::cd),
("view", Arc::new(view::view)), command("view", view::view),
("skip", Arc::new(skip::skip)), command("skip", skip::skip),
("first", Arc::new(take::take)), command("first", take::take),
("size", Arc::new(size::size)), command("size", size::size),
("from-json", Arc::new(from_json::from_json)), command("from-json", from_json::from_json),
("open", Arc::new(open::open)), command("open", open::open),
("column", Arc::new(column::column)), command("column", column::column),
("split", Arc::new(split::split)), command("split", split::split),
("reject", Arc::new(reject::reject)), command("reject", reject::reject),
("to-array", Arc::new(to_array::to_array)), command("to-array", to_array::to_array),
("to-json", Arc::new(to_json::to_json)), command("to-json", to_json::to_json),
("where", Arc::new(where_::r#where)), Arc::new(Where),
("sort-by", Arc::new(sort_by::sort_by)), command("sort-by", sort_by::sort_by),
]); ]);
} }
@ -196,9 +196,15 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
}, },
( (
Some(ClassifiedCommand::Internal(_)), Some(ClassifiedCommand::Internal(ref i)),
Some(ClassifiedCommand::External(_)), Some(ClassifiedCommand::External(ref e)),
) => return LineResult::Error(format!("Unimplemented Internal -> External",)), ) => {
return LineResult::Error(format!(
"Unimplemented Internal({}) -> External({})",
i.name(),
e.name()
))
}
( (
Some(ClassifiedCommand::External(left)), Some(ClassifiedCommand::External(left)),
@ -272,22 +278,28 @@ fn classify_command(
let command_name = &command.name[..]; let command_name = &command.name[..];
let args = &command.args; let args = &command.args;
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 { 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);
let config = command.config();
let scope = Scope::empty();
let args = config.evaluate_args(args.iter(), &scope)?;
Ok(ClassifiedCommand::Internal(InternalCommand { Ok(ClassifiedCommand::Internal(InternalCommand {
command, command,
args: arg_list, args,
}))
}
false => {
let arg_list_strings: Vec<String> = args.iter().map(|i| i.print()).collect();
Ok(ClassifiedCommand::External(ExternalCommand {
name: other.to_string(),
args: arg_list_strings,
})) }))
} }
false => Ok(ClassifiedCommand::External(ExternalCommand {
name: other.to_string(),
args: arg_list_strings,
})),
}, },
} }
} }

View File

@ -18,4 +18,6 @@ crate mod to_json;
crate mod view; crate mod view;
crate mod where_; crate mod where_;
crate use command::command;
crate use to_array::stream_to_array; crate use to_array::stream_to_array;
crate use where_::Where;

View File

@ -105,6 +105,10 @@ impl InternalCommand {
Ok(stream.boxed() as InputStream) Ok(stream.boxed() as InputStream)
} }
crate fn name(&self) -> &str {
self.command.name()
}
} }
crate struct ExternalCommand { crate struct ExternalCommand {
@ -163,4 +167,8 @@ impl ExternalCommand {
} }
} }
} }
crate fn name(&self) -> &str {
&self.name[..]
}
} }

View File

@ -1,5 +1,6 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::Value; use crate::object::Value;
use crate::parser::CommandConfig;
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
@ -44,13 +45,40 @@ impl ReturnValue {
pub trait Command { pub trait Command {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError>; fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError>;
} fn name(&self) -> &str;
impl<F> Command for F fn config(&self) -> CommandConfig {
where CommandConfig {
F: Fn(CommandArgs) -> Result<OutputStream, ShellError>, name: self.name().to_string(),
{ mandatory_positional: vec![],
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { optional_positional: vec![],
self(args) rest_positional: true,
named: indexmap::IndexMap::new(),
}
} }
} }
pub struct FnCommand {
name: String,
func: fn(CommandArgs) -> Result<OutputStream, ShellError>,
}
impl Command for FnCommand {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
(self.func)(args)
}
fn name(&self) -> &str {
&self.name
}
}
pub fn command(
name: &str,
func: fn(CommandArgs) -> Result<OutputStream, ShellError>,
) -> Arc<dyn Command> {
Arc::new(FnCommand {
name: name.to_string(),
func,
})
}

View File

@ -2,7 +2,7 @@ use crate::errors::ShellError;
use crate::prelude::*; use crate::prelude::*;
pub fn skip(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn skip(args: CommandArgs) -> Result<OutputStream, ShellError> {
let amount = args.args[0].as_int()?; let amount = args.args[0].as_i64()?;
let input = args.input; let input = args.input;

View File

@ -11,8 +11,8 @@ pub fn sort_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
vec.sort_by_key(|item| { vec.sort_by_key(|item| {
fields fields
.iter() .iter()
.map(|f| item.get_data_by_key(f).borrow().copy()) .map(|f| item.get_data_by_key(f).map(|i| i.copy()))
.collect::<Vec<Value>>() .collect::<Vec<Option<Value>>>()
}); });
vec.into_iter() vec.into_iter()

View File

@ -4,7 +4,7 @@ use crate::prelude::*;
// TODO: "Amount remaining" wrapper // TODO: "Amount remaining" wrapper
pub fn take(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn take(args: CommandArgs) -> Result<OutputStream, ShellError> {
let amount = args.args[0].as_int()?; let amount = args.args[0].as_i64()?;
let input = args.input; let input = args.input;

View File

@ -1,21 +1,48 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::object::base::find; use crate::parser::registry::PositionalType;
use crate::parser::CommandConfig;
use crate::prelude::*; use crate::prelude::*;
pub struct Where;
impl Command for Where {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
r#where(args)
}
fn name(&self) -> &str {
"where"
}
fn config(&self) -> CommandConfig {
CommandConfig {
name: self.name().to_string(),
mandatory_positional: vec![PositionalType::Block("condition".to_string())],
optional_positional: vec![],
rest_positional: false,
named: indexmap::IndexMap::new(),
}
}
}
pub fn r#where(args: CommandArgs) -> Result<OutputStream, ShellError> { pub fn r#where(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.args.is_empty() { if args.args.is_empty() {
return Err(ShellError::string("select requires a field")); return Err(ShellError::string("select requires a field"));
} }
let operation = args.args[0].as_operation()?; let block = args.args[0].as_block()?;
let field = operation.left.as_string()?;
let operator = operation.operator;
let right = operation.right;
let input = args.input; let input = args.input;
let objects = input let objects = input.filter_map(move |item| {
.filter(move |item| futures::future::ready(find(&item, &field, &operator, &right))) let result = block.invoke(&item);
.map(|item| ReturnValue::Value(item.copy()));
let return_value = match result {
Err(err) => Some(ReturnValue::Value(Value::Error(Box::new(err)))),
Ok(v) if v.is_true() => Some(ReturnValue::Value(item.copy())),
_ => None,
};
futures::future::ready(return_value)
});
Ok(objects.boxed()) Ok(objects.boxed())
} }

View File

@ -20,9 +20,9 @@ impl Context {
}) })
} }
pub fn add_commands(&mut self, commands: Vec<(&str, Arc<dyn Command>)>) { pub fn add_commands(&mut self, commands: Vec<Arc<dyn Command>>) {
for (name, command) in commands { for command in commands {
self.commands.insert(name.to_string(), command); self.commands.insert(command.name().to_string(), command);
} }
} }

93
src/evaluate/evaluator.rs Normal file
View File

@ -0,0 +1,93 @@
use crate::parser::ast;
use crate::prelude::*;
use derive_new::new;
#[derive(new)]
crate struct Scope {
it: Value,
}
impl Scope {
crate fn empty() -> Scope {
Scope {
it: Value::nothing(),
}
}
}
crate fn evaluate_expr(expr: &ast::Expression, scope: &Scope) -> Result<Value, ShellError> {
use ast::*;
match expr {
Expression::Leaf(l) => Ok(evaluate_leaf(l)),
Expression::Parenthesized(p) => evaluate_expr(&p.expr, scope),
Expression::Block(b) => evaluate_block(&b, scope),
Expression::Path(p) => evaluate_path(&p, scope),
Expression::Binary(b) => evaluate_binary(b, scope),
Expression::VariableReference(r) => evaluate_reference(r, scope),
}
}
fn evaluate_leaf(leaf: &ast::Leaf) -> Value {
use ast::*;
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),
}
}
fn evaluate_reference(r: &ast::Variable, scope: &Scope) -> Result<Value, ShellError> {
use ast::Variable::*;
match r {
It => Ok(scope.it.copy()),
True => Ok(Value::boolean(true)),
False => Ok(Value::boolean(false)),
Other(s) => Err(ShellError::string(&format!(
"Unimplemented variable reference: {}",
s
))),
}
}
fn evaluate_binary(binary: &ast::Binary, scope: &Scope) -> Result<Value, ShellError> {
let left = evaluate_expr(&binary.left, scope)?;
let right = evaluate_expr(&binary.right, scope)?;
match left.compare(binary.operator, &right) {
Some(v) => Ok(Value::boolean(v)),
None => Err(ShellError::string(&format!(
"Unimplemented evaluate_binary:\n{:#?}",
binary
))),
}
}
fn evaluate_block(block: &ast::Block, _scope: &Scope) -> Result<Value, ShellError> {
Ok(Value::block(block.expr.clone()))
}
fn evaluate_path(path: &ast::Path, scope: &Scope) -> Result<Value, ShellError> {
let head = path.head();
let mut value = &evaluate_expr(head, scope)?;
for name in path.tail() {
let next = value.get_data_by_key(&name);
match next {
None => {
return Err(ShellError::string(&format!(
"No key {} found in {}",
name,
path.print(),
)))
}
Some(v) => value = v,
}
}
Ok(value.copy())
}

3
src/evaluate/mod.rs Normal file
View File

@ -0,0 +1,3 @@
crate mod evaluator;
crate use evaluator::{evaluate_expr, Scope};

View File

@ -21,20 +21,6 @@ impl RenderView for GenericView<'value> {
} }
Ok(()) Ok(())
// let mut list: Vec<String> = vec![];
// for item in l {
// match item {
// Value::Primitive(p) => list.push(p.format()),
// Value::List(l) => list.push(format!("{:?}", l)),
// Value::Object(o) => {
// let view = o.to_entries_view();
// let out = view.render_view(host);
// list.extend(out);
// }
// }
// list.push("\n".to_string());
// }
// list
} }
o @ Value::Object(_) => { o @ Value::Object(_) => {
@ -43,11 +29,10 @@ impl RenderView for GenericView<'value> {
Ok(()) Ok(())
} }
Value::Operation(o) => { b @ Value::Block(_) => {
host.stdout(&format!( let printed = b.format_leaf(None);
"Unexpectedly trying to print an operation: {:?}", let view = EntriesView::from_value(&Value::string(&printed));
o view.render_view(host)?;
));
Ok(()) Ok(())
} }

View File

@ -9,6 +9,7 @@ mod commands;
mod context; mod context;
mod env; mod env;
mod errors; mod errors;
mod evaluate;
mod format; mod format;
mod object; mod object;
mod parser; mod parser;

View File

@ -1,6 +1,7 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::evaluate::{evaluate_expr, Scope};
use crate::object::DataDescriptor; use crate::object::DataDescriptor;
use crate::parser::tokens::{self, Operator}; use crate::parser::ast::{self, Operator};
use crate::prelude::*; use crate::prelude::*;
use ansi_term::Color; use ansi_term::Color;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -82,12 +83,33 @@ pub struct Operation {
crate right: Value, crate right: Value,
} }
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)]
pub struct Block {
crate expression: ast::Expression,
}
impl Serialize for Block {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.expression.print())
}
}
impl Block {
pub fn invoke(&self, value: &Value) -> Result<Value, ShellError> {
let scope = Scope::new(value.copy());
evaluate_expr(&self.expression, &scope)
}
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] #[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>), Block(Block),
#[allow(unused)] #[allow(unused)]
Error(Box<ShellError>), Error(Box<ShellError>),
@ -102,59 +124,27 @@ impl Serialize for Value {
Value::Primitive(p) => p.serialize(serializer), Value::Primitive(p) => p.serialize(serializer),
Value::Object(o) => o.serialize(serializer), Value::Object(o) => o.serialize(serializer),
Value::List(l) => l.serialize(serializer), Value::List(l) => l.serialize(serializer),
Value::Operation(o) => o.serialize(serializer), Value::Block(b) => b.serialize(serializer),
Value::Error(e) => e.serialize(serializer), Value::Error(e) => e.serialize(serializer),
} }
} }
} }
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::Block(_) => vec![DataDescriptor::value_of()],
Value::List(_) => vec![], Value::List(_) => vec![],
Value::Operation(_) => vec![], Value::Error(_) => vec![DataDescriptor::value_of()],
Value::Error(_) => vec![],
} }
} }
crate fn get_data_by_key(&'a self, name: &str) -> MaybeOwned<'a, Value> { crate fn get_data_by_key(&'a self, name: &str) -> Option<&Value> {
match self { match self {
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()), _ => None,
Value::Operation(_) => MaybeOwned::Owned(Value::nothing()),
Value::Error(_) => MaybeOwned::Owned(Value::nothing()),
} }
} }
@ -162,9 +152,9 @@ impl Value {
match self { match self {
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::Block(_) => MaybeOwned::Owned(Value::nothing()),
Value::List(_) => MaybeOwned::Owned(Value::nothing()), Value::List(_) => MaybeOwned::Owned(Value::nothing()),
Value::Operation(_) => MaybeOwned::Owned(Value::nothing()), Value::Error(e) => MaybeOwned::Owned(Value::string(&format!("{:#?}", e))),
Value::Error(_) => MaybeOwned::Owned(Value::nothing()),
} }
} }
@ -172,11 +162,11 @@ impl Value {
match self { match self {
Value::Primitive(p) => Value::Primitive(p.clone()), Value::Primitive(p) => Value::Primitive(p.clone()),
Value::Object(o) => Value::Object(o.copy_dict()), Value::Object(o) => Value::Object(o.copy_dict()),
Value::Block(b) => Value::Block(b.clone()),
Value::List(l) => { Value::List(l) => {
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())),
} }
} }
@ -184,17 +174,41 @@ impl Value {
crate fn format_leaf(&self, field_name: Option<&str>) -> String { crate fn format_leaf(&self, field_name: Option<&str>) -> String {
match self { match self {
Value::Primitive(p) => p.format(field_name), Value::Primitive(p) => p.format(field_name),
Value::Block(b) => b.expression.print(),
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),
} }
} }
crate fn compare(&self, operator: ast::Operator, other: &Value) -> Option<bool> {
match operator {
ast::Operator::Equal | ast::Operator::NotEqual => unimplemented!(),
_ => {
let coerced = coerce_compare(self, other)?;
let ordering = coerced.compare();
use std::cmp::Ordering;
let result = match (operator, ordering) {
(Operator::Equal, Ordering::Equal) => true,
(Operator::LessThan, Ordering::Less) => true,
(Operator::GreaterThan, Ordering::Greater) => true,
(Operator::GreaterThanOrEqual, Ordering::Greater)
| (Operator::GreaterThanOrEqual, Ordering::Equal) => true,
(Operator::LessThanOrEqual, Ordering::Less)
| (Operator::LessThanOrEqual, Ordering::Equal) => true,
_ => false,
};
Some(result)
}
}
}
crate fn as_string(&self) -> Result<String, ShellError> { crate fn as_string(&self) -> Result<String, ShellError> {
match self { match self {
Value::Primitive(Primitive::String(s)) => Ok(s.to_string()), Value::Primitive(Primitive::String(s)) => Ok(s.to_string()),
// TODO: this should definitely be more general with better errors // TODO: this should definitely be more general with better errors
other => Err(ShellError::string(format!( other => Err(ShellError::string(format!(
"Expected string, got {:?}", "Expected string, got {:?}",
@ -203,24 +217,24 @@ impl Value {
} }
} }
crate fn as_operation(&self) -> Result<Operation, ShellError> { crate fn as_i64(&self) -> Result<i64, ShellError> {
match self { match self {
Value::Operation(o) => Ok(*o.clone()), Value::Primitive(Primitive::Int(i)) => Ok(*i),
Value::Primitive(Primitive::Bytes(b)) if *b <= std::i64::MAX as u128 => Ok(*b as i64),
// TODO: this should definitely be more general with better errors // TODO: this should definitely be more general with better errors
other => Err(ShellError::string(format!( other => Err(ShellError::string(format!(
"Expected operation, got {:?}", "Expected integer, got {:?}",
other other
))), ))),
} }
} }
crate fn as_int(&self) -> Result<i64, ShellError> { crate fn as_block(&self) -> Result<Block, ShellError> {
match self { match self {
Value::Primitive(Primitive::Int(i)) => Ok(*i), Value::Block(block) => Ok(block.clone()),
// TODO: this should definitely be more general with better errors // TODO: this should definitely be more general with better errors
other => Err(ShellError::string(format!( other => Err(ShellError::string(format!(
"Expected integer, got {:?}", "Expected block, got {:?}",
other other
))), ))),
} }
@ -238,6 +252,17 @@ impl Value {
} }
} }
crate fn is_true(&self) -> bool {
match self {
Value::Primitive(Primitive::Boolean(true)) => true,
_ => false,
}
}
crate fn block(e: ast::Expression) -> Value {
Value::Block(Block::new(e))
}
crate fn string(s: impl Into<String>) -> Value { crate fn string(s: impl Into<String>) -> Value {
Value::Primitive(Primitive::String(s.into())) Value::Primitive(Primitive::String(s.into()))
} }
@ -316,13 +341,13 @@ crate fn reject_fields(obj: &Value, fields: &[String]) -> crate::object::Diction
out out
} }
#[allow(unused)]
crate fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool { crate fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool {
let descs = obj.data_descriptors(); let descs = obj.data_descriptors();
match descs.iter().find(|d| d.name.is_string(field)) { match descs.iter().find(|d| d.name.is_string(field)) {
None => false, None => false,
Some(desc) => { Some(desc) => {
let v = obj.get_data(desc).borrow().copy(); let v = obj.get_data(desc).borrow().copy();
//println!("'{:?}' '{:?}' '{:?}'", v, op, rhs);
match v { match v {
Value::Primitive(Primitive::Boolean(b)) => match (op, rhs) { Value::Primitive(Primitive::Boolean(b)) => match (op, rhs) {
@ -398,3 +423,39 @@ crate fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool {
} }
} }
} }
enum CompareValues {
Ints(i64, i64),
Bytes(i128, i128),
String(String, String),
}
impl CompareValues {
fn compare(&self) -> std::cmp::Ordering {
match self {
CompareValues::Ints(left, right) => left.cmp(right),
CompareValues::Bytes(left, right) => left.cmp(right),
CompareValues::String(left, right) => left.cmp(right),
}
}
}
fn coerce_compare(left: &Value, right: &Value) -> Option<CompareValues> {
match (left, right) {
(Value::Primitive(left), Value::Primitive(right)) => coerce_compare_primitive(left, right),
_ => None,
}
}
fn coerce_compare_primitive(left: &Primitive, right: &Primitive) -> Option<CompareValues> {
use Primitive::*;
match (left, right) {
(Int(left), Int(right)) => Some(CompareValues::Ints(*left, *right)),
(Int(left), Bytes(right)) => Some(CompareValues::Bytes(*left as i128, *right as i128)),
(Bytes(left), Int(right)) => Some(CompareValues::Bytes(*left as i128, *right as i128)),
(String(left), String(right)) => Some(CompareValues::String(left.clone(), right.clone())),
_ => None,
}
}

View File

@ -80,14 +80,14 @@ impl Dictionary {
} }
} }
crate fn get_data_by_key(&self, name: &str) -> MaybeOwned<'_, Value> { crate fn get_data_by_key(&self, name: &str) -> Option<&Value> {
match self match self
.entries .entries
.iter() .iter()
.find(|(desc_name, _)| desc_name.name.is_string(name)) .find(|(desc_name, _)| desc_name.name.is_string(name))
{ {
Some((_, v)) => MaybeOwned::Borrowed(v), Some((_, v)) => Some(v),
None => MaybeOwned::Owned(Value::Primitive(Primitive::Nothing)), None => None,
} }
} }
} }

View File

@ -1,10 +1,10 @@
crate mod ast;
crate mod completer; crate mod completer;
crate mod parser; crate mod parser;
crate mod registry; crate mod registry;
crate mod tokens;
crate use ast::{ParsedCommand, Pipeline};
crate use registry::{CommandConfig, CommandRegistry}; crate use registry::{CommandConfig, CommandRegistry};
crate use tokens::{ParsedCommand, Pipeline};
use crate::errors::ShellError; use crate::errors::ShellError;
use parser::PipelineParser; use parser::PipelineParser;

View File

@ -1,4 +1,5 @@
use derive_new::new; use derive_new::new;
use getset::Getters;
use std::str::FromStr; use std::str::FromStr;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -40,25 +41,106 @@ impl FromStr for Operator {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Expression { pub enum Expression {
Leaf(Leaf), Leaf(Leaf),
Binary(Binary), Parenthesized(Box<Parenthesized>),
Block(Box<Block>),
Binary(Box<Binary>),
Path(Box<Path>),
VariableReference(Variable),
} }
impl Expression { impl Expression {
crate fn print(&self) -> String { crate fn print(&self) -> String {
match self { match self {
Expression::Leaf(l) => l.print(), Expression::Leaf(l) => l.print(),
Expression::Parenthesized(p) => p.print(),
Expression::Block(b) => b.print(),
Expression::VariableReference(r) => r.print(),
Expression::Path(p) => p.print(),
Expression::Binary(b) => b.print(), Expression::Binary(b) => b.print(),
} }
} }
crate fn as_string(&self) -> Option<String> {
match self {
Expression::Leaf(Leaf::String(s)) | Expression::Leaf(Leaf::Bare(s)) => {
Some(s.to_string())
}
_ => None,
}
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, new)]
pub struct Block {
crate expr: Expression,
}
impl Block {
fn print(&self) -> String {
format!("{{ {} }}", self.expr.print())
}
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, new)]
pub struct Parenthesized {
crate expr: Expression,
}
impl Parenthesized {
fn print(&self) -> String {
format!("({})", self.expr.print())
}
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Getters, new)]
pub struct Path {
#[get = "crate"]
head: Expression,
#[get = "crate"]
tail: Vec<String>,
}
impl Path {
crate fn print(&self) -> String {
let mut out = self.head.print();
for item in self.tail.iter() {
out.push_str(&format!(".{}", item));
}
out
}
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Variable {
It,
True,
False,
Other(String),
}
impl Variable {
fn print(&self) -> String {
match self {
Variable::It => format!("$it"),
Variable::True => format!("$true"),
Variable::False => format!("$false"),
Variable::Other(s) => format!("${}", s),
}
}
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Leaf { pub enum Leaf {
String(String), String(String),
Bare(String), Bare(String),
#[allow(unused)]
Boolean(bool), Boolean(bool),
Int(i64), Int(i64),
} }
@ -74,11 +156,11 @@ impl Leaf {
} }
} }
#[derive(Debug, Clone, new)] #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, new)]
pub struct Binary { pub struct Binary {
crate left: Leaf, crate left: Expression,
crate operator: Operator, crate operator: Operator,
crate right: Leaf, crate right: Expression,
} }
impl Binary { impl Binary {

View File

@ -1,6 +1,6 @@
use std::str::FromStr; use std::str::FromStr;
use crate::parser::tokens::*;
use byte_unit::Byte; use byte_unit::Byte;
use crate::parser::ast::*;
grammar; grammar;
@ -10,12 +10,59 @@ pub Pipeline: Pipeline = {
} }
Command: ParsedCommand = { Command: ParsedCommand = {
<command:RawBareWord> <expr:Expr*> => ParsedCommand::new(command, expr) <command:RawBareWord> <expr:Expr*> => ParsedCommand::new(command, expr),
<command:RawBareWord> <expr:BinaryExpression> => ParsedCommand::new(command, vec![expr]),
}
Leaf: Expression = {
<String> => Expression::Leaf(Leaf::String(<>)),
<Num> => Expression::Leaf(Leaf::Int(<>)),
<Variable> => Expression::VariableReference(<>),
}
BinaryExpression: Expression = {
<left:Expr> <op:Operator> <right:Leaf> => Expression::Binary(Box::new(Binary::new(left, op, right))),
}
Parenthesized: Expression = {
"(" <Leaf> ")" => Expression::Parenthesized(Box::new(Parenthesized::new(<>))),
"(" <BinaryExpression> ")" => Expression::Parenthesized(Box::new(Parenthesized::new(<>))),
}
AtomicExpression: Expression = {
<Parenthesized>,
<Leaf>,
}
Block: Expression = {
"{" <AtomicExpression> "}" => Expression::Block(Box::new(Block::new(<>))),
"{" <BinaryExpression> "}" => Expression::Block(Box::new(Block::new(<>))),
}
WholeExpression: Expression = {
<AtomicExpression>,
<Block>,
}
PathExpression: Expression = {
<head:WholeExpression> <tail: ( "." <Member> )*> => Expression::Path(Box::new(Path::new(head, tail)))
} }
Expr: Expression = { Expr: Expression = {
<Leaf> => Expression::Leaf(<>), <RawBareWord> => Expression::Leaf(Leaf::Bare(<>)),
<Binary> => Expression::Binary(<>), <PathExpression>
}
Variable: Variable = {
"$true" => Variable::True,
"$false" => Variable::False,
"$it" => Variable::It,
"$" <RawBareWord> => Variable::Other(<>.to_string()),
}
Member: String = {
<RawBareWord>,
<String>
} }
Operator: Operator = { Operator: Operator = {
@ -27,10 +74,6 @@ Operator: Operator = {
">=" => Operator::GreaterThanOrEqual ">=" => Operator::GreaterThanOrEqual
} }
Binary: Binary = {
<left:Leaf> <op:Operator> <right:Leaf> => Binary::new(left, op, right),
}
Flag: Flag = { Flag: Flag = {
"-" <RawBareWord> => Flag::Shorthand(<>.to_string()), "-" <RawBareWord> => Flag::Shorthand(<>.to_string()),
"--" <RawBareWord> => Flag::Longhand(<>.to_string()), "--" <RawBareWord> => Flag::Longhand(<>.to_string()),
@ -41,18 +84,7 @@ String: String = {
DQString, DQString,
} }
Leaf: Leaf = { RawBareWord: String = <s:r#"[^0-9"'\-][^\s"']*"#> => <>.to_string();
<String> => Leaf::String(<>),
<Size> => Leaf::Int(<>),
<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(); DQString: String = <s:r#""([^"]|\\")*""#> => s[1..s.len() - 1].to_string();
SQString: 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(); Num: i64 = <s:r"-?[0-9]+"> => i64::from_str(s).unwrap();

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,110 @@
use crate::evaluate::{evaluate_expr, Scope};
use crate::prelude::*;
use indexmap::IndexMap; use indexmap::IndexMap;
#[allow(unused)] #[allow(unused)]
pub enum CommandType { #[derive(Debug)]
Switch, pub enum NamedType {
Single, Switch(String),
Array, Single(String),
Array(String),
Block(String),
} }
#[allow(unused)] #[allow(unused)]
#[derive(Debug, Clone)]
pub enum PositionalType {
Value(String),
Block(String),
}
impl PositionalType {
crate fn name(&self) -> String {
match self {
PositionalType::Value(s) => s.clone(),
PositionalType::Block(s) => s.clone(),
}
}
crate fn evaluate(&self, arg: ast::Expression, scope: &Scope) -> Result<Value, ShellError> {
match self {
PositionalType::Value(_) => evaluate_expr(&arg, scope),
PositionalType::Block(_) => match arg {
ast::Expression::Block(b) => Ok(Value::block(b.expr)),
ast::Expression::Binary(b) => {
if let Some(s) = b.left.as_string() {
Ok(Value::block(ast::Expression::Binary(Box::new(
ast::Binary::new(
ast::Expression::Path(Box::new(ast::Path::new(
ast::Expression::VariableReference(ast::Variable::It),
vec![s],
))),
b.operator,
b.right,
),
))))
} else {
Ok(Value::block(ast::Expression::Binary(b)))
}
}
other => Ok(Value::block(other)), // other =>
},
}
}
}
#[derive(Debug)]
pub struct CommandConfig { pub struct CommandConfig {
crate name: String, crate name: String,
crate mandatory_positional: Vec<String>, crate mandatory_positional: Vec<PositionalType>,
crate optional_positional: Vec<String>, crate optional_positional: Vec<PositionalType>,
crate rest_positional: bool, crate rest_positional: bool,
crate named: IndexMap<String, CommandType>, crate named: IndexMap<String, NamedType>,
}
impl CommandConfig {
crate fn evaluate_args(
&self,
mut args: impl Iterator<Item = &'expr ast::Expression>,
scope: &Scope,
) -> Result<Vec<Value>, ShellError> {
let mut results: Vec<Value> = vec![];
for param in &self.mandatory_positional {
let arg = args.next();
let value = match arg {
None => {
return Err(ShellError::string(format!(
"expected mandatory positional argument {}",
param.name()
)))
}
Some(arg) => param.evaluate(arg.clone(), scope)?,
};
results.push(value);
}
if self.rest_positional {
let rest: Result<Vec<Value>, _> =
args.map(|i| evaluate_expr(i, &Scope::empty())).collect();
results.extend(rest?);
} else {
match args.next() {
None => {}
Some(_) => return Err(ShellError::string("Too many arguments")),
}
}
Ok(results)
}
#[allow(unused)]
crate fn signature(&self) -> String {
format!("TODO")
}
} }
pub trait CommandRegistry { pub trait CommandRegistry {

View File

@ -5,6 +5,7 @@ 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::Value; crate use crate::object::Value;
crate use crate::parser::ast;
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;