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)",
]
[[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]]
name = "hashbrown"
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-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)",
"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)",
"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)",
@ -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-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 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 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"

View File

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

View File

@ -6,4 +6,9 @@ args = ["src/parser/parser.lalrpop"]
[tasks.build]
command = "cargo"
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;
crate use crate::errors::ShellError;
use crate::evaluate::Scope;
crate use crate::format::{EntriesListView, GenericView};
use crate::object::Value;
use crate::parser::{ParsedCommand, Pipeline};
@ -17,7 +18,6 @@ use rustyline::{self, ColorMode, Config, Editor};
use std::collections::VecDeque;
use std::error::Error;
use std::iter::Iterator;
use std::sync::Arc;
#[derive(Debug)]
pub enum MaybeOwned<'a, T> {
@ -41,23 +41,23 @@ pub async fn cli() -> Result<(), Box<Error>> {
use crate::commands::*;
context.add_commands(vec![
("format-list", Arc::new(format_list)),
("ps", Arc::new(ps::ps)),
("ls", Arc::new(ls::ls)),
("cd", Arc::new(cd::cd)),
("view", Arc::new(view::view)),
("skip", Arc::new(skip::skip)),
("first", Arc::new(take::take)),
("size", Arc::new(size::size)),
("from-json", Arc::new(from_json::from_json)),
("open", Arc::new(open::open)),
("column", Arc::new(column::column)),
("split", Arc::new(split::split)),
("reject", Arc::new(reject::reject)),
("to-array", Arc::new(to_array::to_array)),
("to-json", Arc::new(to_json::to_json)),
("where", Arc::new(where_::r#where)),
("sort-by", Arc::new(sort_by::sort_by)),
command("format-list", format_list),
command("ps", ps::ps),
command("ls", ls::ls),
command("cd", cd::cd),
command("view", view::view),
command("skip", skip::skip),
command("first", take::take),
command("size", size::size),
command("from-json", from_json::from_json),
command("open", open::open),
command("column", column::column),
command("split", split::split),
command("reject", reject::reject),
command("to-array", to_array::to_array),
command("to-json", to_json::to_json),
Arc::new(Where),
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::External(_)),
) => return LineResult::Error(format!("Unimplemented Internal -> External",)),
Some(ClassifiedCommand::Internal(ref i)),
Some(ClassifiedCommand::External(ref e)),
) => {
return LineResult::Error(format!(
"Unimplemented Internal({}) -> External({})",
i.name(),
e.name()
))
}
(
Some(ClassifiedCommand::External(left)),
@ -272,22 +278,28 @@ fn classify_command(
let command_name = &command.name[..];
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 {
other => match context.has_command(command_name) {
true => {
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 {
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 where_;
crate use command::command;
crate use to_array::stream_to_array;
crate use where_::Where;

View File

@ -105,6 +105,10 @@ impl InternalCommand {
Ok(stream.boxed() as InputStream)
}
crate fn name(&self) -> &str {
self.command.name()
}
}
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::object::Value;
use crate::parser::CommandConfig;
use crate::prelude::*;
use std::path::PathBuf;
@ -44,13 +45,40 @@ impl ReturnValue {
pub trait Command {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError>;
}
fn name(&self) -> &str;
impl<F> Command for F
where
F: Fn(CommandArgs) -> Result<OutputStream, ShellError>,
{
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
self(args)
fn config(&self) -> CommandConfig {
CommandConfig {
name: self.name().to_string(),
mandatory_positional: vec![],
optional_positional: vec![],
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::*;
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;

View File

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

View File

@ -4,7 +4,7 @@ use crate::prelude::*;
// TODO: "Amount remaining" wrapper
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;

View File

@ -1,21 +1,48 @@
use crate::errors::ShellError;
use crate::object::base::find;
use crate::parser::registry::PositionalType;
use crate::parser::CommandConfig;
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> {
if args.args.is_empty() {
return Err(ShellError::string("select requires a field"));
}
let operation = args.args[0].as_operation()?;
let field = operation.left.as_string()?;
let operator = operation.operator;
let right = operation.right;
let block = args.args[0].as_block()?;
let input = args.input;
let objects = input
.filter(move |item| futures::future::ready(find(&item, &field, &operator, &right)))
.map(|item| ReturnValue::Value(item.copy()));
let objects = input.filter_map(move |item| {
let result = block.invoke(&item);
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())
}

View File

@ -20,9 +20,9 @@ impl Context {
})
}
pub fn add_commands(&mut self, commands: Vec<(&str, Arc<dyn Command>)>) {
for (name, command) in commands {
self.commands.insert(name.to_string(), command);
pub fn add_commands(&mut self, commands: Vec<Arc<dyn Command>>) {
for command in commands {
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(())
// 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(_) => {
@ -43,11 +29,10 @@ impl RenderView for GenericView<'value> {
Ok(())
}
Value::Operation(o) => {
host.stdout(&format!(
"Unexpectedly trying to print an operation: {:?}",
o
));
b @ Value::Block(_) => {
let printed = b.format_leaf(None);
let view = EntriesView::from_value(&Value::string(&printed));
view.render_view(host)?;
Ok(())
}

View File

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

View File

@ -1,6 +1,7 @@
use crate::errors::ShellError;
use crate::evaluate::{evaluate_expr, Scope};
use crate::object::DataDescriptor;
use crate::parser::tokens::{self, Operator};
use crate::parser::ast::{self, Operator};
use crate::prelude::*;
use ansi_term::Color;
use chrono::{DateTime, Utc};
@ -82,12 +83,33 @@ pub struct Operation {
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)]
pub enum Value {
Primitive(Primitive),
Object(crate::object::Dictionary),
List(Vec<Value>),
Operation(Box<Operation>),
Block(Block),
#[allow(unused)]
Error(Box<ShellError>),
@ -102,59 +124,27 @@ impl Serialize for Value {
Value::Primitive(p) => p.serialize(serializer),
Value::Object(o) => o.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),
}
}
}
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::Block(_) => vec![DataDescriptor::value_of()],
Value::List(_) => vec![],
Value::Operation(_) => vec![],
Value::Error(_) => vec![],
Value::Error(_) => vec![DataDescriptor::value_of()],
}
}
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 {
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()),
_ => None,
}
}
@ -162,9 +152,9 @@ impl Value {
match self {
p @ Value::Primitive(_) => MaybeOwned::Borrowed(p),
Value::Object(o) => o.get_data(desc),
Value::Block(_) => MaybeOwned::Owned(Value::nothing()),
Value::List(_) => MaybeOwned::Owned(Value::nothing()),
Value::Operation(_) => MaybeOwned::Owned(Value::nothing()),
Value::Error(_) => MaybeOwned::Owned(Value::nothing()),
Value::Error(e) => MaybeOwned::Owned(Value::string(&format!("{:#?}", e))),
}
}
@ -172,11 +162,11 @@ impl Value {
match self {
Value::Primitive(p) => Value::Primitive(p.clone()),
Value::Object(o) => Value::Object(o.copy_dict()),
Value::Block(b) => Value::Block(b.clone()),
Value::List(l) => {
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())),
}
}
@ -184,17 +174,41 @@ impl Value {
crate fn format_leaf(&self, field_name: Option<&str>) -> String {
match self {
Value::Primitive(p) => p.format(field_name),
Value::Block(b) => b.expression.print(),
Value::Object(_) => format!("[object Object]"),
Value::List(_) => format!("[list List]"),
Value::Operation(_) => format!("[operation Operation]"),
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> {
match self {
Value::Primitive(Primitive::String(s)) => Ok(s.to_string()),
// TODO: this should definitely be more general with better errors
other => Err(ShellError::string(format!(
"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 {
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
other => Err(ShellError::string(format!(
"Expected operation, got {:?}",
"Expected integer, got {:?}",
other
))),
}
}
crate fn as_int(&self) -> Result<i64, ShellError> {
crate fn as_block(&self) -> Result<Block, ShellError> {
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
other => Err(ShellError::string(format!(
"Expected integer, got {:?}",
"Expected block, got {:?}",
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 {
Value::Primitive(Primitive::String(s.into()))
}
@ -316,13 +341,13 @@ crate fn reject_fields(obj: &Value, fields: &[String]) -> crate::object::Diction
out
}
#[allow(unused)]
crate fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool {
let descs = obj.data_descriptors();
match descs.iter().find(|d| d.name.is_string(field)) {
None => false,
Some(desc) => {
let v = obj.get_data(desc).borrow().copy();
//println!("'{:?}' '{:?}' '{:?}'", v, op, rhs);
match v {
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
.entries
.iter()
.find(|(desc_name, _)| desc_name.name.is_string(name))
{
Some((_, v)) => MaybeOwned::Borrowed(v),
None => MaybeOwned::Owned(Value::Primitive(Primitive::Nothing)),
Some((_, v)) => Some(v),
None => None,
}
}
}

View File

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

View File

@ -1,4 +1,5 @@
use derive_new::new;
use getset::Getters;
use std::str::FromStr;
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 {
Leaf(Leaf),
Binary(Binary),
Parenthesized(Box<Parenthesized>),
Block(Box<Block>),
Binary(Box<Binary>),
Path(Box<Path>),
VariableReference(Variable),
}
impl Expression {
crate fn print(&self) -> String {
match self {
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(),
}
}
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 {
String(String),
Bare(String),
#[allow(unused)]
Boolean(bool),
Int(i64),
}
@ -74,11 +156,11 @@ impl Leaf {
}
}
#[derive(Debug, Clone, new)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, new)]
pub struct Binary {
crate left: Leaf,
crate left: Expression,
crate operator: Operator,
crate right: Leaf,
crate right: Expression,
}
impl Binary {

View File

@ -1,6 +1,6 @@
use std::str::FromStr;
use crate::parser::tokens::*;
use byte_unit::Byte;
use crate::parser::ast::*;
grammar;
@ -10,12 +10,59 @@ pub Pipeline: Pipeline = {
}
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 = {
<Leaf> => Expression::Leaf(<>),
<Binary> => Expression::Binary(<>),
<RawBareWord> => Expression::Leaf(Leaf::Bare(<>)),
<PathExpression>
}
Variable: Variable = {
"$true" => Variable::True,
"$false" => Variable::False,
"$it" => Variable::It,
"$" <RawBareWord> => Variable::Other(<>.to_string()),
}
Member: String = {
<RawBareWord>,
<String>
}
Operator: Operator = {
@ -27,10 +74,6 @@ Operator: Operator = {
">=" => 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()),
@ -41,18 +84,7 @@ String: String = {
DQString,
}
Leaf: Leaf = {
<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();
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();

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;
#[allow(unused)]
pub enum CommandType {
Switch,
Single,
Array,
#[derive(Debug)]
pub enum NamedType {
Switch(String),
Single(String),
Array(String),
Block(String),
}
#[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 {
crate name: String,
crate mandatory_positional: Vec<String>,
crate optional_positional: Vec<String>,
crate mandatory_positional: Vec<PositionalType>,
crate optional_positional: Vec<PositionalType>,
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 {

View File

@ -5,6 +5,7 @@ crate use crate::env::host::handle_unexpected;
crate use crate::env::{Environment, Host};
crate use crate::errors::ShellError;
crate use crate::object::Value;
crate use crate::parser::ast;
crate use crate::stream::{single_output, InputStream, OutputStream};
crate use futures::{FutureExt, SinkExt, StreamExt};
crate use std::collections::VecDeque;