mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 04:45:04 +02:00
Merge branch 'main' of https://github.com/nushell/engine-q into plugins
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use crate::{DeclId, Signature};
|
||||
use crate::{DeclId, Signature, VarId};
|
||||
|
||||
use super::Statement;
|
||||
|
||||
@ -9,6 +9,7 @@ pub struct Block {
|
||||
pub signature: Box<Signature>,
|
||||
pub stmts: Vec<Statement>,
|
||||
pub exports: Vec<(Vec<u8>, DeclId)>, // Assuming just defs for now
|
||||
pub captures: Vec<VarId>,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
@ -47,6 +48,7 @@ impl Block {
|
||||
signature: Box::new(Signature::new("")),
|
||||
stmts: vec![],
|
||||
exports: vec![],
|
||||
captures: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +57,7 @@ impl Block {
|
||||
signature: self.signature,
|
||||
stmts: self.stmts,
|
||||
exports,
|
||||
captures: self.captures,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,6 +71,7 @@ where
|
||||
signature: Box::new(Signature::new("")),
|
||||
stmts: stmts.collect(),
|
||||
exports: vec![],
|
||||
captures: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ pub enum Expr {
|
||||
RangeOperator,
|
||||
),
|
||||
Var(VarId),
|
||||
VarDecl(VarId),
|
||||
Call(Box<Call>),
|
||||
ExternalCall(String, Span, Vec<Expression>),
|
||||
Operator(Operator),
|
||||
|
@ -77,6 +77,7 @@ impl Expression {
|
||||
pub fn as_var(&self) -> Option<VarId> {
|
||||
match self.expr {
|
||||
Expr::Var(var_id) => Some(var_id),
|
||||
Expr::VarDecl(var_id) => Some(var_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::{ast::Call, value::Value, BlockId, Example, ShellError, Signature};
|
||||
use crate::{ast::Call, BlockId, Example, PipelineData, ShellError, Signature};
|
||||
|
||||
use super::EvaluationContext;
|
||||
use super::{EngineState, Stack};
|
||||
|
||||
pub trait Command: Send + Sync {
|
||||
pub trait Command: Send + Sync + CommandClone {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
@ -17,10 +17,11 @@ pub trait Command: Send + Sync {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
context: &EvaluationContext,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: Value,
|
||||
) -> Result<Value, ShellError>;
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError>;
|
||||
|
||||
fn is_binary(&self) -> bool {
|
||||
false
|
||||
@ -55,3 +56,22 @@ pub trait Command: Send + Sync {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CommandClone {
|
||||
fn clone_box(&self) -> Box<dyn Command>;
|
||||
}
|
||||
|
||||
impl<T> CommandClone for T
|
||||
where
|
||||
T: 'static + Command + Clone,
|
||||
{
|
||||
fn clone_box(&self) -> Box<dyn Command> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Box<dyn Command> {
|
||||
fn clone(&self) -> Box<dyn Command> {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,20 @@
|
||||
use super::Command;
|
||||
use crate::{ast::Block, BlockId, DeclId, Example, Signature, Span, Type, VarId};
|
||||
use core::panic;
|
||||
use std::{collections::HashMap, slice::Iter};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EngineState {
|
||||
files: Vec<(String, usize, usize)>,
|
||||
file_contents: Vec<u8>,
|
||||
vars: Vec<Type>,
|
||||
decls: Vec<Box<dyn Command>>,
|
||||
blocks: Vec<Block>,
|
||||
pub scope: Vec<ScopeFrame>,
|
||||
files: im::Vector<(String, usize, usize)>,
|
||||
file_contents: im::Vector<(Vec<u8>, usize, usize)>,
|
||||
vars: im::Vector<Type>,
|
||||
decls: im::Vector<Box<dyn Command + 'static>>,
|
||||
blocks: im::Vector<Block>,
|
||||
pub scope: im::Vector<ScopeFrame>,
|
||||
pub ctrlc: Option<Arc<AtomicBool>>,
|
||||
}
|
||||
|
||||
// Tells whether a decl etc. is visible or not
|
||||
@ -53,7 +58,7 @@ impl Visibility {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScopeFrame {
|
||||
pub vars: HashMap<Vec<u8>, VarId>,
|
||||
predecls: HashMap<Vec<u8>, DeclId>, // temporary storage for predeclarations
|
||||
@ -95,12 +100,13 @@ impl Default for EngineState {
|
||||
impl EngineState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
files: vec![],
|
||||
file_contents: vec![],
|
||||
vars: vec![],
|
||||
decls: vec![],
|
||||
blocks: vec![],
|
||||
scope: vec![ScopeFrame::new()],
|
||||
files: im::vector![],
|
||||
file_contents: im::vector![],
|
||||
vars: im::vector![],
|
||||
decls: im::vector![],
|
||||
blocks: im::vector![],
|
||||
scope: im::vector![ScopeFrame::new()],
|
||||
ctrlc: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +118,7 @@ impl EngineState {
|
||||
this.vars.extend(delta.vars);
|
||||
this.blocks.extend(delta.blocks);
|
||||
|
||||
if let Some(last) = this.scope.last_mut() {
|
||||
if let Some(last) = this.scope.back_mut() {
|
||||
let first = delta.scope.remove(0);
|
||||
for item in first.decls.into_iter() {
|
||||
last.decls.insert(item.0, item.1);
|
||||
@ -165,8 +171,10 @@ impl EngineState {
|
||||
}
|
||||
|
||||
pub fn print_contents(&self) {
|
||||
let string = String::from_utf8_lossy(&self.file_contents);
|
||||
println!("{}", string);
|
||||
for (contents, _, _) in self.file_contents.iter() {
|
||||
let string = String::from_utf8_lossy(contents);
|
||||
println!("{}", string);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
|
||||
@ -200,7 +208,13 @@ impl EngineState {
|
||||
}
|
||||
|
||||
pub fn get_span_contents(&self, span: &Span) -> &[u8] {
|
||||
&self.file_contents[span.start..span.end]
|
||||
for (contents, start, finish) in &self.file_contents {
|
||||
if span.start >= *start && span.end <= *finish {
|
||||
return &contents[(span.start - start)..(span.end - start)];
|
||||
}
|
||||
}
|
||||
|
||||
panic!("internal error: span missing in file contents cache")
|
||||
}
|
||||
|
||||
pub fn get_var(&self, var_id: VarId) -> &Type {
|
||||
@ -253,10 +267,14 @@ impl EngineState {
|
||||
}
|
||||
|
||||
pub fn next_span_start(&self) -> usize {
|
||||
self.file_contents.len()
|
||||
if let Some((_, _, last)) = self.file_contents.last() {
|
||||
*last
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn files(&self) -> Iter<(String, usize, usize)> {
|
||||
pub fn files(&self) -> impl Iterator<Item = &(String, usize, usize)> {
|
||||
self.files.iter()
|
||||
}
|
||||
|
||||
@ -273,8 +291,11 @@ impl EngineState {
|
||||
pub fn get_file_source(&self, file_id: usize) -> String {
|
||||
for file in self.files.iter().enumerate() {
|
||||
if file.0 == file_id {
|
||||
let output =
|
||||
String::from_utf8_lossy(&self.file_contents[file.1 .1..file.1 .2]).to_string();
|
||||
let contents = self.get_span_contents(&Span {
|
||||
start: file.1 .1,
|
||||
end: file.1 .2,
|
||||
});
|
||||
let output = String::from_utf8_lossy(contents).to_string();
|
||||
|
||||
return output;
|
||||
}
|
||||
@ -286,12 +307,13 @@ impl EngineState {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn add_file(&mut self, filename: String, contents: Vec<u8>) -> usize {
|
||||
let next_span_start = self.next_span_start();
|
||||
let next_span_end = next_span_start + contents.len();
|
||||
|
||||
self.file_contents.extend(&contents);
|
||||
self.file_contents
|
||||
.push_back((contents, next_span_start, next_span_end));
|
||||
|
||||
let next_span_end = self.next_span_start();
|
||||
|
||||
self.files.push((filename, next_span_start, next_span_end));
|
||||
self.files
|
||||
.push_back((filename, next_span_start, next_span_end));
|
||||
|
||||
self.num_files() - 1
|
||||
}
|
||||
@ -304,7 +326,7 @@ pub struct StateWorkingSet<'a> {
|
||||
|
||||
pub struct StateDelta {
|
||||
files: Vec<(String, usize, usize)>,
|
||||
pub(crate) file_contents: Vec<u8>,
|
||||
pub(crate) file_contents: Vec<(Vec<u8>, usize, usize)>,
|
||||
vars: Vec<Type>, // indexed by VarId
|
||||
decls: Vec<Box<dyn Command>>, // indexed by DeclId
|
||||
blocks: Vec<Block>, // indexed by BlockId
|
||||
@ -481,7 +503,13 @@ impl<'a> StateWorkingSet<'a> {
|
||||
}
|
||||
|
||||
pub fn next_span_start(&self) -> usize {
|
||||
self.permanent_state.next_span_start() + self.delta.file_contents.len()
|
||||
let permanent_span_start = self.permanent_state.next_span_start();
|
||||
|
||||
if let Some((_, _, last)) = self.delta.file_contents.last() {
|
||||
permanent_span_start + *last
|
||||
} else {
|
||||
permanent_span_start
|
||||
}
|
||||
}
|
||||
|
||||
pub fn global_span_offset(&self) -> usize {
|
||||
@ -520,10 +548,11 @@ impl<'a> StateWorkingSet<'a> {
|
||||
|
||||
pub fn add_file(&mut self, filename: String, contents: &[u8]) -> usize {
|
||||
let next_span_start = self.next_span_start();
|
||||
let next_span_end = next_span_start + contents.len();
|
||||
|
||||
self.delta.file_contents.extend(contents);
|
||||
|
||||
let next_span_end = self.next_span_start();
|
||||
self.delta
|
||||
.file_contents
|
||||
.push((contents.to_vec(), next_span_start, next_span_end));
|
||||
|
||||
self.delta
|
||||
.files
|
||||
@ -535,10 +564,16 @@ impl<'a> StateWorkingSet<'a> {
|
||||
pub fn get_span_contents(&self, span: Span) -> &[u8] {
|
||||
let permanent_end = self.permanent_state.next_span_start();
|
||||
if permanent_end <= span.start {
|
||||
&self.delta.file_contents[(span.start - permanent_end)..(span.end - permanent_end)]
|
||||
for (contents, start, finish) in &self.delta.file_contents {
|
||||
if (span.start >= *start) && (span.end <= *finish) {
|
||||
return &contents[(span.start - permanent_end)..(span.end - permanent_end)];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
&self.permanent_state.file_contents[span.start..span.end]
|
||||
return self.permanent_state.get_span_contents(&span);
|
||||
}
|
||||
|
||||
panic!("internal error: missing span contents in file cache")
|
||||
}
|
||||
|
||||
pub fn enter_scope(&mut self) {
|
||||
|
@ -1,137 +0,0 @@
|
||||
use super::EngineState;
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::{Example, ShellError, Signature, Value, VarId};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EvaluationContext {
|
||||
pub engine_state: Rc<RefCell<EngineState>>,
|
||||
pub stack: Stack,
|
||||
}
|
||||
|
||||
impl EvaluationContext {
|
||||
pub fn get_var(&self, var_id: VarId) -> Result<Value, ShellError> {
|
||||
self.stack.get_var(var_id)
|
||||
}
|
||||
|
||||
pub fn enter_scope(&self) -> EvaluationContext {
|
||||
Self {
|
||||
engine_state: self.engine_state.clone(),
|
||||
stack: self.stack.clone().enter_scope(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_var(&self, var_id: VarId, value: Value) {
|
||||
// We need to make values concreate before we assign them to variables, as stream values
|
||||
// will drain and remain drained.
|
||||
//
|
||||
// TODO: find a good home for converting a stream->list when storing into a variable
|
||||
// TODO: add ctrl-c support when setting a var
|
||||
|
||||
let value = match value {
|
||||
Value::Stream { stream, span } => Value::List {
|
||||
vals: stream.collect(),
|
||||
span,
|
||||
},
|
||||
x => x,
|
||||
};
|
||||
|
||||
self.stack.add_var(var_id, value);
|
||||
}
|
||||
|
||||
pub fn add_env_var(&self, var: String, value: String) {
|
||||
self.stack.add_env_var(var, value);
|
||||
}
|
||||
|
||||
pub fn print_stack(&self) {
|
||||
self.stack.print_stack();
|
||||
}
|
||||
|
||||
pub fn get_signatures(&self) -> Vec<Signature> {
|
||||
self.engine_state.borrow().get_signatures()
|
||||
}
|
||||
|
||||
pub fn get_signatures_with_examples(&self) -> Vec<(Signature, Vec<Example>)> {
|
||||
self.engine_state.borrow().get_signatures_with_examples()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StackFrame {
|
||||
pub vars: HashMap<VarId, Value>,
|
||||
pub env_vars: HashMap<String, String>,
|
||||
pub parent: Option<Stack>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Stack(Rc<RefCell<StackFrame>>);
|
||||
|
||||
impl Default for Stack {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
pub fn new() -> Stack {
|
||||
Stack(Rc::new(RefCell::new(StackFrame {
|
||||
vars: HashMap::new(),
|
||||
env_vars: HashMap::new(),
|
||||
parent: None,
|
||||
})))
|
||||
}
|
||||
pub fn get_var(&self, var_id: VarId) -> Result<Value, ShellError> {
|
||||
let this = self.0.borrow();
|
||||
match this.vars.get(&var_id) {
|
||||
Some(v) => Ok(v.clone()),
|
||||
_ => {
|
||||
if let Some(parent) = &this.parent {
|
||||
parent.get_var(var_id)
|
||||
} else {
|
||||
Err(ShellError::InternalError("variable not found".into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_var(&self, var_id: VarId, value: Value) {
|
||||
let mut this = self.0.borrow_mut();
|
||||
this.vars.insert(var_id, value);
|
||||
}
|
||||
|
||||
pub fn add_env_var(&self, var: String, value: String) {
|
||||
let mut this = self.0.borrow_mut();
|
||||
this.env_vars.insert(var, value);
|
||||
}
|
||||
|
||||
pub fn enter_scope(self) -> Stack {
|
||||
Stack(Rc::new(RefCell::new(StackFrame {
|
||||
vars: HashMap::new(),
|
||||
env_vars: HashMap::new(),
|
||||
parent: Some(self),
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn get_env_vars(&self) -> HashMap<String, String> {
|
||||
self.0.borrow().env_vars.clone()
|
||||
}
|
||||
|
||||
pub fn get_env_var(&self, name: &str) -> Option<String> {
|
||||
self.0.borrow().env_vars.get(name).cloned()
|
||||
}
|
||||
|
||||
pub fn print_stack(&self) {
|
||||
println!("===frame===");
|
||||
println!("vars:");
|
||||
for (var, val) in &self.0.borrow().vars {
|
||||
println!(" {}: {:?}", var, val);
|
||||
}
|
||||
println!("env vars:");
|
||||
for (var, val) in &self.0.borrow().env_vars {
|
||||
println!(" {}: {:?}", var, val);
|
||||
}
|
||||
if let Some(parent) = &self.0.borrow().parent {
|
||||
parent.print_stack()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
mod call_info;
|
||||
mod command;
|
||||
mod engine_state;
|
||||
mod evaluation_context;
|
||||
mod stack;
|
||||
|
||||
pub use call_info::*;
|
||||
pub use command::*;
|
||||
pub use engine_state::*;
|
||||
pub use evaluation_context::*;
|
||||
pub use stack::*;
|
||||
|
97
crates/nu-protocol/src/engine/stack.rs
Normal file
97
crates/nu-protocol/src/engine/stack.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{ShellError, Value, VarId};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Stack {
|
||||
pub vars: HashMap<VarId, Value>,
|
||||
pub env_vars: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Default for Stack {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
pub fn new() -> Stack {
|
||||
Stack {
|
||||
vars: HashMap::new(),
|
||||
env_vars: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn get_var(&self, var_id: VarId) -> Result<Value, ShellError> {
|
||||
if let Some(v) = self.vars.get(&var_id) {
|
||||
return Ok(v.clone());
|
||||
}
|
||||
Err(ShellError::InternalError("variable not found".into()))
|
||||
}
|
||||
|
||||
pub fn add_var(&mut self, var_id: VarId, value: Value) {
|
||||
self.vars.insert(var_id, value);
|
||||
}
|
||||
|
||||
pub fn add_env_var(&mut self, var: String, value: String) {
|
||||
self.env_vars.insert(var, value);
|
||||
}
|
||||
|
||||
pub fn collect_captures(&self, captures: &[VarId]) -> Stack {
|
||||
let mut output = Stack::new();
|
||||
|
||||
for capture in captures {
|
||||
// Note: this assumes we have calculated captures correctly and that commands
|
||||
// that take in a var decl will manually set this into scope when running the blocks
|
||||
if let Ok(value) = self.get_var(*capture) {
|
||||
output.vars.insert(*capture, value);
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
// pub fn enter_scope(&self) -> Stack {
|
||||
// // FIXME: VERY EXPENSIVE to clone entire stack
|
||||
// let mut output = self.clone();
|
||||
// output.0.push(StackFrame {
|
||||
// vars: HashMap::new(),
|
||||
// env_vars: HashMap::new(),
|
||||
// });
|
||||
|
||||
// output
|
||||
// }
|
||||
|
||||
pub fn get_env_vars(&self) -> HashMap<String, String> {
|
||||
// let mut output = HashMap::new();
|
||||
|
||||
// for frame in &self.0 {
|
||||
// output.extend(frame.env_vars.clone().into_iter());
|
||||
// }
|
||||
|
||||
// output
|
||||
self.env_vars.clone()
|
||||
}
|
||||
|
||||
pub fn get_env_var(&self, name: &str) -> Option<String> {
|
||||
// for frame in self.0.iter().rev() {
|
||||
if let Some(v) = self.env_vars.get(name) {
|
||||
return Some(v.to_string());
|
||||
}
|
||||
// }
|
||||
None
|
||||
}
|
||||
|
||||
pub fn print_stack(&self) {
|
||||
// for frame in self.0.iter().rev() {
|
||||
// println!("===frame===");
|
||||
println!("vars:");
|
||||
for (var, val) in &self.vars {
|
||||
println!(" {}: {:?}", var, val);
|
||||
}
|
||||
println!("env vars:");
|
||||
for (var, val) in &self.env_vars {
|
||||
println!(" {}: {:?}", var, val);
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ pub mod ast;
|
||||
pub mod engine;
|
||||
mod example;
|
||||
mod id;
|
||||
mod pipeline_data;
|
||||
mod shell_error;
|
||||
mod signature;
|
||||
mod span;
|
||||
@ -12,6 +13,7 @@ pub use value::Value;
|
||||
|
||||
pub use example::*;
|
||||
pub use id::*;
|
||||
pub use pipeline_data::*;
|
||||
pub use shell_error::*;
|
||||
pub use signature::*;
|
||||
pub use span::*;
|
||||
|
163
crates/nu-protocol/src/pipeline_data.rs
Normal file
163
crates/nu-protocol/src/pipeline_data.rs
Normal file
@ -0,0 +1,163 @@
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
use crate::{ast::PathMember, ShellError, Span, Value, ValueStream};
|
||||
|
||||
pub enum PipelineData {
|
||||
Value(Value),
|
||||
Stream(ValueStream),
|
||||
}
|
||||
|
||||
impl PipelineData {
|
||||
pub fn new() -> PipelineData {
|
||||
PipelineData::Value(Value::nothing())
|
||||
}
|
||||
|
||||
pub fn into_value(self) -> Value {
|
||||
match self {
|
||||
PipelineData::Value(v) => v,
|
||||
PipelineData::Stream(s) => Value::List {
|
||||
vals: s.collect(),
|
||||
span: Span::unknown(), // FIXME?
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_string(self) -> String {
|
||||
match self {
|
||||
PipelineData::Value(v) => v.collect_string(),
|
||||
PipelineData::Stream(s) => s.collect_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn follow_cell_path(self, cell_path: &[PathMember]) -> Result<Value, ShellError> {
|
||||
match self {
|
||||
// FIXME: there are probably better ways of doing this
|
||||
PipelineData::Stream(stream) => Value::List {
|
||||
vals: stream.collect(),
|
||||
span: Span::unknown(),
|
||||
}
|
||||
.follow_cell_path(cell_path),
|
||||
PipelineData::Value(v) => v.follow_cell_path(cell_path),
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplified mapper to help with simple values also. For full iterator support use `.into_iter()` instead
|
||||
pub fn map<F>(
|
||||
self,
|
||||
mut f: F,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> Result<PipelineData, ShellError>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Value) -> Value + 'static + Send,
|
||||
{
|
||||
match self {
|
||||
PipelineData::Value(Value::List { vals, .. }) => {
|
||||
Ok(vals.into_iter().map(f).into_pipeline_data(ctrlc))
|
||||
}
|
||||
PipelineData::Stream(stream) => Ok(stream.map(f).into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(Value::Range { val, .. }) => {
|
||||
Ok(val.into_range_iter()?.map(f).into_pipeline_data(ctrlc))
|
||||
}
|
||||
PipelineData::Value(v) => {
|
||||
let output = f(v);
|
||||
match output {
|
||||
Value::Error { error } => Err(error),
|
||||
v => Ok(v.into_pipeline_data()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplified flatmapper. For full iterator support use `.into_iter()` instead
|
||||
pub fn flat_map<U, F>(
|
||||
self,
|
||||
mut f: F,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> Result<PipelineData, ShellError>
|
||||
where
|
||||
Self: Sized,
|
||||
U: IntoIterator<Item = Value>,
|
||||
<U as IntoIterator>::IntoIter: 'static + Send,
|
||||
F: FnMut(Value) -> U + 'static + Send,
|
||||
{
|
||||
match self {
|
||||
PipelineData::Value(Value::List { vals, .. }) => {
|
||||
Ok(vals.into_iter().map(f).flatten().into_pipeline_data(ctrlc))
|
||||
}
|
||||
PipelineData::Stream(stream) => Ok(stream.map(f).flatten().into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() {
|
||||
Ok(iter) => Ok(iter.map(f).flatten().into_pipeline_data(ctrlc)),
|
||||
Err(error) => Err(error),
|
||||
},
|
||||
PipelineData::Value(v) => Ok(f(v).into_iter().into_pipeline_data(ctrlc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PipelineData {
|
||||
fn default() -> Self {
|
||||
PipelineData::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PipelineIterator(PipelineData);
|
||||
|
||||
impl IntoIterator for PipelineData {
|
||||
type Item = Value;
|
||||
|
||||
type IntoIter = PipelineIterator;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
PipelineData::Value(Value::List { vals, .. }) => {
|
||||
PipelineIterator(PipelineData::Stream(ValueStream {
|
||||
stream: Box::new(vals.into_iter()),
|
||||
ctrlc: None,
|
||||
}))
|
||||
}
|
||||
x => PipelineIterator(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PipelineIterator {
|
||||
type Item = Value;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match &mut self.0 {
|
||||
PipelineData::Value(Value::Nothing { .. }) => None,
|
||||
PipelineData::Value(v) => {
|
||||
let prev = std::mem::take(v);
|
||||
Some(prev)
|
||||
}
|
||||
PipelineData::Stream(stream) => stream.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoPipelineData {
|
||||
fn into_pipeline_data(self) -> PipelineData;
|
||||
}
|
||||
|
||||
impl IntoPipelineData for Value {
|
||||
fn into_pipeline_data(self) -> PipelineData {
|
||||
PipelineData::Value(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoInterruptiblePipelineData {
|
||||
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData;
|
||||
}
|
||||
|
||||
impl<T> IntoInterruptiblePipelineData for T
|
||||
where
|
||||
T: Iterator<Item = Value> + Send + 'static,
|
||||
{
|
||||
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData {
|
||||
PipelineData::Stream(ValueStream {
|
||||
stream: Box::new(self),
|
||||
ctrlc,
|
||||
})
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
use crate::ast::Call;
|
||||
use crate::engine::Command;
|
||||
use crate::engine::EvaluationContext;
|
||||
use crate::engine::EngineState;
|
||||
use crate::engine::Stack;
|
||||
use crate::BlockId;
|
||||
use crate::PipelineData;
|
||||
use crate::SyntaxShape;
|
||||
use crate::Value;
|
||||
use crate::VarId;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -335,6 +336,7 @@ impl Signature {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Predeclaration {
|
||||
signature: Signature,
|
||||
}
|
||||
@ -354,14 +356,16 @@ impl Command for Predeclaration {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_context: &EvaluationContext,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
_input: Value,
|
||||
) -> Result<crate::Value, crate::ShellError> {
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, crate::ShellError> {
|
||||
panic!("Internal error: can't run a predeclaration without a body")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct BlockCommand {
|
||||
signature: Signature,
|
||||
block_id: BlockId,
|
||||
@ -382,10 +386,11 @@ impl Command for BlockCommand {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_context: &EvaluationContext,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
_input: Value,
|
||||
) -> Result<crate::Value, crate::ShellError> {
|
||||
_input: PipelineData,
|
||||
) -> Result<crate::PipelineData, crate::ShellError> {
|
||||
panic!("Internal error: can't run custom command with 'run', use block_id");
|
||||
}
|
||||
|
||||
|
@ -59,10 +59,6 @@ pub enum Value {
|
||||
vals: Vec<Value>,
|
||||
span: Span,
|
||||
},
|
||||
Stream {
|
||||
stream: ValueStream,
|
||||
span: Span,
|
||||
},
|
||||
List {
|
||||
vals: Vec<Value>,
|
||||
span: Span,
|
||||
@ -110,7 +106,6 @@ impl Value {
|
||||
Value::Record { span, .. } => Ok(*span),
|
||||
Value::List { span, .. } => Ok(*span),
|
||||
Value::Block { span, .. } => Ok(*span),
|
||||
Value::Stream { span, .. } => Ok(*span),
|
||||
Value::Nothing { span, .. } => Ok(*span),
|
||||
Value::Binary { span, .. } => Ok(*span),
|
||||
Value::CellPath { span, .. } => Ok(*span),
|
||||
@ -129,7 +124,6 @@ impl Value {
|
||||
Value::Range { span, .. } => *span = new_span,
|
||||
Value::String { span, .. } => *span = new_span,
|
||||
Value::Record { span, .. } => *span = new_span,
|
||||
Value::Stream { span, .. } => *span = new_span,
|
||||
Value::List { span, .. } => *span = new_span,
|
||||
Value::Block { span, .. } => *span = new_span,
|
||||
Value::Nothing { span, .. } => *span = new_span,
|
||||
@ -158,7 +152,6 @@ impl Value {
|
||||
Value::List { .. } => Type::List(Box::new(Type::Unknown)), // FIXME
|
||||
Value::Nothing { .. } => Type::Nothing,
|
||||
Value::Block { .. } => Type::Block,
|
||||
Value::Stream { .. } => Type::ValueStream,
|
||||
Value::Error { .. } => Type::Error,
|
||||
Value::Binary { .. } => Type::Binary,
|
||||
Value::CellPath { .. } => Type::CellPath,
|
||||
@ -174,19 +167,10 @@ impl Value {
|
||||
Value::Filesize { val, .. } => format_filesize(val),
|
||||
Value::Duration { val, .. } => format_duration(val),
|
||||
Value::Date { val, .. } => HumanTime::from(val).to_string(),
|
||||
Value::Range { val, .. } => match val.into_range_iter() {
|
||||
Ok(iter) => {
|
||||
format!(
|
||||
"range: [{}]",
|
||||
iter.map(|x| x.into_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
)
|
||||
}
|
||||
Err(error) => format!("{:?}", error),
|
||||
},
|
||||
Value::Range { val, .. } => {
|
||||
format!("{}..{}", val.from.into_string(), val.to.into_string())
|
||||
}
|
||||
Value::String { val, .. } => val,
|
||||
Value::Stream { stream, .. } => stream.into_string(),
|
||||
Value::List { vals: val, .. } => format!(
|
||||
"[{}]",
|
||||
val.into_iter()
|
||||
@ -218,17 +202,10 @@ impl Value {
|
||||
Value::Filesize { val, .. } => format!("{} bytes", val),
|
||||
Value::Duration { val, .. } => format!("{} ns", val),
|
||||
Value::Date { val, .. } => format!("{:?}", val),
|
||||
Value::Range { val, .. } => match val.into_range_iter() {
|
||||
Ok(iter) => iter
|
||||
.map(|x| x.into_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
Err(error) => {
|
||||
format!("{:?}", error)
|
||||
}
|
||||
},
|
||||
Value::Range { val, .. } => {
|
||||
format!("{}..{}", val.from.into_string(), val.to.into_string())
|
||||
}
|
||||
Value::String { val, .. } => val,
|
||||
Value::Stream { stream, .. } => stream.collect_string(),
|
||||
Value::List { vals: val, .. } => val
|
||||
.into_iter()
|
||||
.map(|x| x.collect_string())
|
||||
@ -274,13 +251,6 @@ impl Value {
|
||||
return Err(ShellError::AccessBeyondEnd(val.len(), *origin_span));
|
||||
}
|
||||
}
|
||||
Value::Stream { stream, .. } => {
|
||||
if let Some(item) = stream.nth(*count) {
|
||||
current = item;
|
||||
} else {
|
||||
return Err(ShellError::AccessBeyondEndOfStream(*origin_span));
|
||||
}
|
||||
}
|
||||
x => {
|
||||
return Err(ShellError::IncompatiblePathAccess(
|
||||
format!("{}", x.get_type()),
|
||||
@ -329,27 +299,6 @@ impl Value {
|
||||
span: *span,
|
||||
};
|
||||
}
|
||||
Value::Stream { stream, span } => {
|
||||
let mut output = vec![];
|
||||
for val in stream {
|
||||
output.push(val.clone().follow_cell_path(&[PathMember::String {
|
||||
val: column_name.clone(),
|
||||
span: *origin_span,
|
||||
}])?);
|
||||
// if let Value::Record { cols, vals, .. } = val {
|
||||
// for col in cols.iter().enumerate() {
|
||||
// if col.1 == column_name {
|
||||
// output.push(vals[col.0].clone());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
current = Value::List {
|
||||
vals: output,
|
||||
span: *span,
|
||||
};
|
||||
}
|
||||
x => {
|
||||
return Err(ShellError::IncompatiblePathAccess(
|
||||
format!("{}", x.get_type()),
|
||||
@ -374,64 +323,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map<F>(self, span: Span, mut f: F) -> Result<Value, ShellError>
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(Self) -> Value + 'static,
|
||||
{
|
||||
match self {
|
||||
Value::List { vals, .. } => Ok(Value::Stream {
|
||||
stream: vals.into_iter().map(f).into_value_stream(),
|
||||
span,
|
||||
}),
|
||||
Value::Stream { stream, .. } => Ok(Value::Stream {
|
||||
stream: stream.map(f).into_value_stream(),
|
||||
span,
|
||||
}),
|
||||
Value::Range { val, .. } => Ok(Value::Stream {
|
||||
stream: val.into_range_iter()?.map(f).into_value_stream(),
|
||||
span,
|
||||
}),
|
||||
v => {
|
||||
let output = f(v);
|
||||
match output {
|
||||
Value::Error { error } => Err(error),
|
||||
v => Ok(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flat_map<U, F>(self, span: Span, mut f: F) -> Value
|
||||
where
|
||||
Self: Sized,
|
||||
U: IntoIterator<Item = Value>,
|
||||
<U as IntoIterator>::IntoIter: 'static,
|
||||
F: FnMut(Self) -> U + 'static,
|
||||
{
|
||||
match self {
|
||||
Value::List { vals, .. } => Value::Stream {
|
||||
stream: vals.into_iter().map(f).flatten().into_value_stream(),
|
||||
span,
|
||||
},
|
||||
Value::Stream { stream, .. } => Value::Stream {
|
||||
stream: stream.map(f).flatten().into_value_stream(),
|
||||
span,
|
||||
},
|
||||
Value::Range { val, .. } => match val.into_range_iter() {
|
||||
Ok(iter) => Value::Stream {
|
||||
stream: iter.map(f).flatten().into_value_stream(),
|
||||
span,
|
||||
},
|
||||
Err(error) => Value::Error { error },
|
||||
},
|
||||
v => Value::Stream {
|
||||
stream: f(v).into_iter().into_value_stream(),
|
||||
span,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string(val: impl Into<String>, span: Span) -> Value {
|
||||
Value::String {
|
||||
val: val.into(),
|
||||
@ -460,6 +351,12 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
fn default() -> Self {
|
||||
Value::nothing()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Value {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
// Compare two floating point numbers. The decision interval for equality is dynamically
|
||||
@ -511,24 +408,6 @@ impl PartialOrd for Value {
|
||||
..
|
||||
},
|
||||
) if lhs_headers == rhs_headers && lhs == rhs => Some(Ordering::Equal),
|
||||
(Value::Stream { stream: lhs, .. }, Value::Stream { stream: rhs, .. }) => {
|
||||
lhs.clone().partial_cmp(rhs.clone())
|
||||
}
|
||||
(Value::Stream { stream: lhs, .. }, Value::String { val: rhs, .. }) => {
|
||||
lhs.clone().collect_string().partial_cmp(rhs)
|
||||
}
|
||||
(Value::String { val: lhs, .. }, Value::Stream { stream: rhs, .. }) => {
|
||||
lhs.partial_cmp(&rhs.clone().collect_string())
|
||||
}
|
||||
// NOTE: This may look a bit strange, but a `Stream` is still just a `List`, it just
|
||||
// happens to be in an iterator form instead of a concrete form. The contained values
|
||||
// can be compared.
|
||||
(Value::Stream { stream: lhs, .. }, Value::List { vals: rhs, .. }) => {
|
||||
lhs.clone().collect::<Vec<Value>>().partial_cmp(rhs)
|
||||
}
|
||||
(Value::List { vals: lhs, .. }, Value::Stream { stream: rhs, .. }) => {
|
||||
lhs.partial_cmp(&rhs.clone().collect::<Vec<Value>>())
|
||||
}
|
||||
(Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => {
|
||||
lhs.partial_cmp(rhs)
|
||||
}
|
||||
@ -852,10 +731,6 @@ impl Value {
|
||||
val: rhs.contains(lhs),
|
||||
span,
|
||||
}),
|
||||
(lhs, Value::Stream { stream: rhs, .. }) => Ok(Value::Bool {
|
||||
val: rhs.clone().any(|x| lhs == &x),
|
||||
span,
|
||||
}),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
@ -886,10 +761,6 @@ impl Value {
|
||||
val: !rhs.contains(lhs),
|
||||
span,
|
||||
}),
|
||||
(lhs, Value::Stream { stream: rhs, .. }) => Ok(Value::Bool {
|
||||
val: rhs.clone().all(|x| lhs != &x),
|
||||
span,
|
||||
}),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type(),
|
||||
|
@ -1,10 +1,16 @@
|
||||
use crate::*;
|
||||
use std::{cell::RefCell, fmt::Debug, rc::Rc};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use serde::{ser::SerializeSeq, Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ValueStream(pub Rc<RefCell<dyn Iterator<Item = Value>>>);
|
||||
pub struct ValueStream {
|
||||
pub stream: Box<dyn Iterator<Item = Value> + Send + 'static>,
|
||||
pub ctrlc: Option<Arc<AtomicBool>>,
|
||||
}
|
||||
|
||||
impl ValueStream {
|
||||
pub fn into_string(self) -> String {
|
||||
@ -22,8 +28,14 @@ impl ValueStream {
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
pub fn from_stream(input: impl Iterator<Item = Value> + 'static) -> ValueStream {
|
||||
ValueStream(Rc::new(RefCell::new(input)))
|
||||
pub fn from_stream(
|
||||
input: impl Iterator<Item = Value> + Send + 'static,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> ValueStream {
|
||||
ValueStream {
|
||||
stream: Box::new(input),
|
||||
ctrlc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,67 +49,73 @@ impl Iterator for ValueStream {
|
||||
type Item = Value;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
{
|
||||
self.0.borrow_mut().next()
|
||||
if let Some(ctrlc) = &self.ctrlc {
|
||||
if ctrlc.load(Ordering::SeqCst) {
|
||||
None
|
||||
} else {
|
||||
self.stream.next()
|
||||
}
|
||||
} else {
|
||||
self.stream.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ValueStream {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(None)?;
|
||||
// impl Serialize for ValueStream {
|
||||
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
// where
|
||||
// S: serde::Serializer,
|
||||
// {
|
||||
// let mut seq = serializer.serialize_seq(None)?;
|
||||
|
||||
for element in self.0.borrow_mut().into_iter() {
|
||||
seq.serialize_element(&element)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
// for element in self.0.borrow_mut().into_iter() {
|
||||
// seq.serialize_element(&element)?;
|
||||
// }
|
||||
// seq.end()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<'de> Deserialize<'de> for ValueStream {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_seq(MySeqVisitor)
|
||||
}
|
||||
}
|
||||
// impl<'de> Deserialize<'de> for ValueStream {
|
||||
// fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
// where
|
||||
// D: serde::Deserializer<'de>,
|
||||
// {
|
||||
// deserializer.deserialize_seq(MySeqVisitor)
|
||||
// }
|
||||
// }
|
||||
|
||||
struct MySeqVisitor;
|
||||
// struct MySeqVisitor;
|
||||
|
||||
impl<'a> serde::de::Visitor<'a> for MySeqVisitor {
|
||||
type Value = ValueStream;
|
||||
// impl<'a> serde::de::Visitor<'a> for MySeqVisitor {
|
||||
// type Value = ValueStream;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a value stream")
|
||||
}
|
||||
// fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
// formatter.write_str("a value stream")
|
||||
// }
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: serde::de::SeqAccess<'a>,
|
||||
{
|
||||
let mut output: Vec<Value> = vec![];
|
||||
// fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
// where
|
||||
// A: serde::de::SeqAccess<'a>,
|
||||
// {
|
||||
// let mut output: Vec<Value> = vec![];
|
||||
|
||||
while let Some(value) = seq.next_element()? {
|
||||
output.push(value);
|
||||
}
|
||||
// while let Some(value) = seq.next_element()? {
|
||||
// output.push(value);
|
||||
// }
|
||||
|
||||
Ok(ValueStream(Rc::new(RefCell::new(output.into_iter()))))
|
||||
}
|
||||
}
|
||||
// Ok(ValueStream(Rc::new(RefCell::new(output.into_iter()))))
|
||||
// }
|
||||
// }
|
||||
|
||||
pub trait IntoValueStream {
|
||||
fn into_value_stream(self) -> ValueStream;
|
||||
}
|
||||
// pub trait IntoValueStream {
|
||||
// fn into_value_stream(self) -> ValueStream;
|
||||
// }
|
||||
|
||||
impl<T> IntoValueStream for T
|
||||
where
|
||||
T: Iterator<Item = Value> + 'static,
|
||||
{
|
||||
fn into_value_stream(self) -> ValueStream {
|
||||
ValueStream::from_stream(self)
|
||||
}
|
||||
}
|
||||
// impl<T> IntoValueStream for T
|
||||
// where
|
||||
// T: Iterator<Item = Value> + 'static,
|
||||
// {
|
||||
// fn into_value_stream(self) -> ValueStream {
|
||||
// ValueStream::from_stream(self)
|
||||
// }
|
||||
// }
|
||||
|
Reference in New Issue
Block a user