mirror of
https://github.com/liljencrantz/crush.git
synced 2024-10-04 06:17:17 +03:00
Fix various context bugs
This commit is contained in:
parent
21c5395679
commit
11e09d09c5
@ -122,25 +122,25 @@ impl ArgumentVecCompiler for Vec<ArgumentDefinition> {
|
||||
let mut res = Vec::new();
|
||||
for a in self {
|
||||
if a.argument_type.is_this() {
|
||||
this = Some(a.value.compile_bound(context)?);
|
||||
this = Some(a.value.eval_and_bind(context)?);
|
||||
} else {
|
||||
match &a.argument_type {
|
||||
ArgumentType::Some(name) => {
|
||||
res.push(Argument::named(
|
||||
&name.string,
|
||||
a.value.compile_bound(context)?,
|
||||
a.value.eval_and_bind(context)?,
|
||||
a.location,
|
||||
))
|
||||
}
|
||||
|
||||
ArgumentType::None => {
|
||||
res.push(Argument::unnamed(
|
||||
a.value.compile_bound(context)?,
|
||||
a.value.eval_and_bind(context)?,
|
||||
a.location,
|
||||
))
|
||||
}
|
||||
|
||||
ArgumentType::ArgumentList => match a.value.compile_bound(context)? {
|
||||
ArgumentType::ArgumentList => match a.value.eval_and_bind(context)? {
|
||||
Value::List(l) => {
|
||||
let mut copy = l.dump();
|
||||
for v in copy.drain(..) {
|
||||
@ -153,7 +153,7 @@ impl ArgumentVecCompiler for Vec<ArgumentDefinition> {
|
||||
_ => return argument_error_legacy("Argument list must be of type list"),
|
||||
},
|
||||
|
||||
ArgumentType::ArgumentDict => match a.value.compile_bound(context)? {
|
||||
ArgumentType::ArgumentDict => match a.value.eval_and_bind(context)? {
|
||||
Value::Dict(d) => {
|
||||
let mut copy = d.elements();
|
||||
for (key, value) in copy.drain(..) {
|
||||
|
@ -16,6 +16,8 @@ use crate::lang::pipe::{black_hole, empty_channel};
|
||||
use crate::lang::value::{Value, ValueDefinition, ValueType};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use crate::lang::ast::{TrackedString, Location};
|
||||
|
||||
pub struct Closure {
|
||||
@ -201,7 +203,6 @@ impl<'a> ClosureSerializer<'a> {
|
||||
model::parameter::Parameter::Named(n.serialize(self.elements, self.state)? as u64),
|
||||
Parameter::Parameter(n, t, d) => {
|
||||
model::parameter::Parameter::Normal(model::NormalParameter {
|
||||
|
||||
name: n.serialize(self.elements, self.state)? as u64,
|
||||
|
||||
r#type: Some(self.value_definition(t)?),
|
||||
@ -531,13 +532,14 @@ impl Help for Closure {
|
||||
}
|
||||
}
|
||||
|
||||
/** Extracts the help message from a closure definition */
|
||||
fn extract_help(jobs: &mut Vec<Job>) -> String {
|
||||
if jobs.is_empty() {
|
||||
return "".to_string();
|
||||
}
|
||||
|
||||
let j = &jobs[0];
|
||||
match j.as_string() {
|
||||
match j.extract_help_message() {
|
||||
Some(help) => {
|
||||
if jobs.len() > 1 {
|
||||
jobs.remove(0);
|
||||
@ -570,80 +572,89 @@ impl Closure {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_arguments_to_env_with_signature(
|
||||
signature: &Vec<Parameter>,
|
||||
mut arguments: Vec<Argument>,
|
||||
context: &mut CompileContext,
|
||||
) -> CrushResult<()> {
|
||||
let mut named = HashMap::new();
|
||||
let mut unnamed = Vec::new();
|
||||
for arg in arguments.drain(..) {
|
||||
match arg.argument_type {
|
||||
Some(name) => {
|
||||
named.insert(name.clone(), arg.value);
|
||||
}
|
||||
None => unnamed.push(arg.value),
|
||||
};
|
||||
}
|
||||
let mut unnamed_name = None;
|
||||
let mut named_name = None;
|
||||
|
||||
for param in signature {
|
||||
match param {
|
||||
Parameter::Parameter(name, value_type, default) => {
|
||||
if let Value::Type(value_type) = value_type.eval_and_bind(context)? {
|
||||
if named.contains_key(&name.string) {
|
||||
let value = named.remove(&name.string).unwrap();
|
||||
if !value_type.is(&value) {
|
||||
return argument_error_legacy("Wrong parameter type");
|
||||
}
|
||||
context.env.redeclare(&name.string, value)?;
|
||||
} else if !unnamed.is_empty() {
|
||||
context.env.redeclare(&name.string, unnamed.remove(0))?;
|
||||
} else if let Some(default) = default {
|
||||
let env = context.env.clone();
|
||||
env.redeclare(&name.string, default.eval_and_bind(context)?)?;
|
||||
} else {
|
||||
return argument_error_legacy("Missing variable!!!");
|
||||
}
|
||||
} else {
|
||||
return argument_error_legacy("Not a type");
|
||||
}
|
||||
}
|
||||
Parameter::Named(name) => {
|
||||
if named_name.is_some() {
|
||||
return argument_error_legacy("Multiple named argument maps specified");
|
||||
}
|
||||
named_name = Some(name);
|
||||
}
|
||||
Parameter::Unnamed(name) => {
|
||||
if unnamed_name.is_some() {
|
||||
return argument_error_legacy("Multiple named argument maps specified");
|
||||
}
|
||||
unnamed_name = Some(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(unnamed_name) = unnamed_name {
|
||||
context.env.redeclare(
|
||||
unnamed_name.string.as_ref(),
|
||||
Value::List(List::new(ValueType::Any, unnamed)),
|
||||
)?;
|
||||
} else if !unnamed.is_empty() {
|
||||
return argument_error_legacy("No target for unnamed arguments");
|
||||
}
|
||||
|
||||
if let Some(named_name) = named_name {
|
||||
let d = Dict::new(ValueType::String, ValueType::Any);
|
||||
for (k, v) in named {
|
||||
d.insert(Value::string(&k), v)?;
|
||||
}
|
||||
context.env.redeclare(named_name.string.as_ref(), Value::Dict(d))?;
|
||||
} else if !named.is_empty() {
|
||||
return argument_error_legacy("No target for extra named arguments");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_arguments_to_env(
|
||||
signature: &Option<Vec<Parameter>>,
|
||||
mut arguments: Vec<Argument>,
|
||||
context: &mut CompileContext,
|
||||
) -> CrushResult<()> {
|
||||
if let Some(signature) = signature {
|
||||
let mut named = HashMap::new();
|
||||
let mut unnamed = Vec::new();
|
||||
for arg in arguments.drain(..) {
|
||||
match arg.argument_type {
|
||||
Some(name) => {
|
||||
named.insert(name.clone(), arg.value);
|
||||
}
|
||||
None => unnamed.push(arg.value),
|
||||
};
|
||||
}
|
||||
let mut unnamed_name = None;
|
||||
let mut named_name = None;
|
||||
|
||||
for param in signature {
|
||||
match param {
|
||||
Parameter::Parameter(name, value_type, default) => {
|
||||
if let Value::Type(value_type) = value_type.compile_bound(context)? {
|
||||
if named.contains_key(&name.string) {
|
||||
let value = named.remove(&name.string).unwrap();
|
||||
if !value_type.is(&value) {
|
||||
return argument_error_legacy("Wrong parameter type");
|
||||
}
|
||||
context.env.redeclare(&name.string, value)?;
|
||||
} else if !unnamed.is_empty() {
|
||||
context.env.redeclare(&name.string, unnamed.remove(0))?;
|
||||
} else if let Some(default) = default {
|
||||
let env = context.env.clone();
|
||||
env.redeclare(&name.string, default.compile_bound(context)?)?;
|
||||
} else {
|
||||
return argument_error_legacy("Missing variable!!!");
|
||||
}
|
||||
} else {
|
||||
return argument_error_legacy("Not a type");
|
||||
}
|
||||
}
|
||||
Parameter::Named(name) => {
|
||||
if named_name.is_some() {
|
||||
return argument_error_legacy("Multiple named argument maps specified");
|
||||
}
|
||||
named_name = Some(name);
|
||||
}
|
||||
Parameter::Unnamed(name) => {
|
||||
if unnamed_name.is_some() {
|
||||
return argument_error_legacy("Multiple named argument maps specified");
|
||||
}
|
||||
unnamed_name = Some(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(unnamed_name) = unnamed_name {
|
||||
context.env.redeclare(
|
||||
unnamed_name.string.as_ref(),
|
||||
Value::List(List::new(ValueType::Any, unnamed)),
|
||||
)?;
|
||||
} else if !unnamed.is_empty() {
|
||||
return argument_error_legacy("No target for unnamed arguments");
|
||||
}
|
||||
|
||||
if let Some(named_name) = named_name {
|
||||
let d = Dict::new(ValueType::String, ValueType::Any);
|
||||
for (k, v) in named {
|
||||
d.insert(Value::string(&k), v)?;
|
||||
}
|
||||
context.env.redeclare(named_name.string.as_ref(), Value::Dict(d))?;
|
||||
} else if !named.is_empty() {
|
||||
return argument_error_legacy("No target for extra named arguments");
|
||||
}
|
||||
Self::push_arguments_to_env_with_signature(signature, arguments, context)
|
||||
} else {
|
||||
for arg in arguments.drain(..) {
|
||||
match arg.argument_type {
|
||||
@ -655,8 +666,8 @@ impl Closure {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn deserialize(
|
||||
|
@ -4,11 +4,12 @@ use crate::lang::data::scope::Scope;
|
||||
use crate::lang::{argument::ArgumentDefinition, argument::ArgumentVecCompiler, value::Value};
|
||||
use crate::lang::command::Command;
|
||||
use crate::lang::execution_context::CommandContext;
|
||||
use crate::lang::value::ValueDefinition;
|
||||
use crate::lang::value::{ValueDefinition, ValueType};
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::thread::ThreadId;
|
||||
use crate::data::r#struct::Struct;
|
||||
use crate::lang::ast::Location;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -38,7 +39,6 @@ fn resolve_external_command(name: &str, env: &Scope) -> CrushResult<Option<PathB
|
||||
fn arg_can_block(local_arguments: &Vec<ArgumentDefinition>, context: &mut CompileContext) -> bool {
|
||||
for arg in local_arguments {
|
||||
if arg.value.can_block(local_arguments, context) {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -50,7 +50,8 @@ impl CommandInvocation {
|
||||
CommandInvocation { command, arguments }
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> Option<String> {
|
||||
/** Extracts the help message from a closure definition */
|
||||
pub fn extract_help_message(&self) -> Option<String> {
|
||||
if self.arguments.len() != 0 {
|
||||
return None;
|
||||
}
|
||||
@ -69,34 +70,6 @@ impl CommandInvocation {
|
||||
&self.command
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn spawn_stream(
|
||||
&self,
|
||||
env: &Scope,
|
||||
mut argument_stream: InputStream,
|
||||
output: ValueSender,
|
||||
) -> CrushResult<JobJoinHandle> {
|
||||
let cmd = env.get(&self.name);
|
||||
match cmd {
|
||||
Some(Value::Command(command)) => {
|
||||
let c = command.call;
|
||||
Ok(handle(build(format_name(&self.name)).spawn(
|
||||
move || {
|
||||
loop {
|
||||
match argument_stream.recv() {
|
||||
Ok(mut row) => {}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})))
|
||||
}
|
||||
_ => {
|
||||
error("Can't stream call")
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
fn execution_context(
|
||||
local_arguments: Vec<ArgumentDefinition>,
|
||||
mut this: Option<Value>,
|
||||
@ -112,7 +85,7 @@ impl CommandInvocation {
|
||||
}
|
||||
|
||||
pub fn can_block(&self, arg: &[ArgumentDefinition], context: &mut CompileContext) -> bool {
|
||||
let cmd = self.command.compile_internal(context, false);
|
||||
let cmd = self.command.eval(context, false);
|
||||
match cmd {
|
||||
Ok((_, Value::Command(command))) => {
|
||||
command.can_block(arg, context) || arg_can_block(&self.arguments, context)
|
||||
@ -121,12 +94,10 @@ impl CommandInvocation {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke(&self, context: JobContext) -> CrushResult<Option<ThreadId>> {
|
||||
match self
|
||||
.command
|
||||
.compile_internal(&mut CompileContext::from(&context), false)
|
||||
pub fn eval(&self, context: JobContext) -> CrushResult<Option<ThreadId>> {
|
||||
match self.command.eval(&mut CompileContext::from(&context), false)
|
||||
{
|
||||
Ok((this, value)) => invoke_value(this, value, self.arguments.clone(), context, self.command.location()),
|
||||
Ok((this, value)) => eval_internal(this, value, self.arguments.clone(), context, self.command.location()),
|
||||
Err(err) => {
|
||||
if err.is(CrushErrorType::BlockError) {
|
||||
let cmd = self.command.clone();
|
||||
@ -136,8 +107,8 @@ impl CommandInvocation {
|
||||
Ok(Some(t.spawn(
|
||||
&self.command.to_string(),
|
||||
move || {
|
||||
match cmd.clone().compile_unbound(&mut CompileContext::from(&context)) {
|
||||
Ok((this, value)) => context.global_state.printer().handle_error(invoke_value(
|
||||
match cmd.clone().eval(&mut CompileContext::from(&context), true) {
|
||||
Ok((this, value)) => context.global_state.printer().handle_error(eval_internal(
|
||||
this,
|
||||
value,
|
||||
arguments,
|
||||
@ -162,7 +133,7 @@ impl CommandInvocation {
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke_value(
|
||||
fn eval_internal(
|
||||
this: Option<Value>,
|
||||
value: Value,
|
||||
local_arguments: Vec<ArgumentDefinition>,
|
||||
@ -171,92 +142,9 @@ fn invoke_value(
|
||||
) -> CrushResult<Option<ThreadId>> {
|
||||
match value {
|
||||
Value::Command(command) => invoke_command(command, this, local_arguments, context),
|
||||
Value::File(f) => {
|
||||
if local_arguments.len() == 0 {
|
||||
let meta = f.metadata();
|
||||
if meta.is_ok() && meta.unwrap().is_dir() {
|
||||
invoke_command(
|
||||
context
|
||||
.scope
|
||||
.global_static_cmd(vec!["global", "fs", "cd"])?,
|
||||
None,
|
||||
vec![ArgumentDefinition::unnamed(ValueDefinition::Value(
|
||||
Value::File(f),
|
||||
location,
|
||||
))],
|
||||
context,
|
||||
)
|
||||
} else {
|
||||
invoke_command(
|
||||
context.scope.global_static_cmd(vec!["global", "io", "val"])?,
|
||||
None,
|
||||
vec![ArgumentDefinition::unnamed(ValueDefinition::Value(
|
||||
Value::File(f),
|
||||
location,
|
||||
))],
|
||||
context,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
error(
|
||||
format!(
|
||||
"Not a command {}",
|
||||
f.to_str().unwrap_or("<invalid filename>")
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Value::Type(t) => match t.fields().get("__call__") {
|
||||
None => invoke_command(
|
||||
context.scope.global_static_cmd(vec!["global", "io", "val"])?,
|
||||
None,
|
||||
vec![ArgumentDefinition::unnamed(ValueDefinition::Value(
|
||||
Value::Type(t),
|
||||
location,
|
||||
))],
|
||||
context,
|
||||
),
|
||||
Some(call) => invoke_command(
|
||||
call.as_ref().copy(),
|
||||
Some(Value::Type(t)),
|
||||
local_arguments,
|
||||
context,
|
||||
),
|
||||
},
|
||||
Value::Struct(s) => match s.get("__call__") {
|
||||
Some(Value::Command(call)) => {
|
||||
invoke_command(call, Some(Value::Struct(s)), local_arguments, context)
|
||||
}
|
||||
Some(v) => error(
|
||||
format!(
|
||||
"__call__ should be a command, was of type {}",
|
||||
v.value_type().to_string()
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
_ => {
|
||||
if local_arguments.len() == 0 {
|
||||
invoke_command(
|
||||
context.scope.global_static_cmd(vec!["global", "io", "val"])?,
|
||||
None,
|
||||
vec![ArgumentDefinition::unnamed(ValueDefinition::Value(
|
||||
Value::Struct(s),
|
||||
location,
|
||||
))],
|
||||
context,
|
||||
)
|
||||
} else {
|
||||
error(
|
||||
format!(
|
||||
"Struct must have a member __call__ to be used as a command {}",
|
||||
s.to_string()
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
Value::File(f) => eval_file(f, local_arguments, context, location),
|
||||
Value::Type(t) => eval_type(t, local_arguments, context, location),
|
||||
Value::Struct(s) => invoke_struct(s, local_arguments, context, location),
|
||||
_ => {
|
||||
if local_arguments.len() == 0 {
|
||||
invoke_command(
|
||||
@ -272,27 +160,135 @@ fn invoke_value(
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_file(
|
||||
file: PathBuf,
|
||||
local_arguments: Vec<ArgumentDefinition>,
|
||||
context: JobContext,
|
||||
location: Location,
|
||||
) -> CrushResult<Option<ThreadId>> {
|
||||
if local_arguments.len() == 0 {
|
||||
let meta = file.metadata();
|
||||
if meta.is_ok() && meta.unwrap().is_dir() {
|
||||
invoke_command(
|
||||
context
|
||||
.scope
|
||||
.global_static_cmd(vec!["global", "fs", "cd"])?,
|
||||
None,
|
||||
vec![ArgumentDefinition::unnamed(ValueDefinition::Value(
|
||||
Value::File(file),
|
||||
location,
|
||||
))],
|
||||
context,
|
||||
)
|
||||
} else {
|
||||
invoke_command(
|
||||
context.scope.global_static_cmd(vec!["global", "io", "val"])?,
|
||||
None,
|
||||
vec![ArgumentDefinition::unnamed(ValueDefinition::Value(
|
||||
Value::File(file),
|
||||
location,
|
||||
))],
|
||||
context,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
error(
|
||||
format!(
|
||||
"Not a command {}",
|
||||
file.to_str().unwrap_or("<invalid filename>")
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_type(
|
||||
value_type: ValueType,
|
||||
local_arguments: Vec<ArgumentDefinition>,
|
||||
context: JobContext,
|
||||
location: Location,
|
||||
) -> CrushResult<Option<ThreadId>> {
|
||||
match value_type.fields().get("__call__") {
|
||||
None => invoke_command(
|
||||
context.scope.global_static_cmd(vec!["global", "io", "val"])?,
|
||||
None,
|
||||
vec![ArgumentDefinition::unnamed(ValueDefinition::Value(
|
||||
Value::Type(value_type),
|
||||
location,
|
||||
))],
|
||||
context,
|
||||
),
|
||||
Some(call) => invoke_command(
|
||||
call.as_ref().copy(),
|
||||
Some(Value::Type(value_type)),
|
||||
local_arguments,
|
||||
context,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke_struct(
|
||||
struct_value: Struct,
|
||||
local_arguments: Vec<ArgumentDefinition>,
|
||||
context: JobContext,
|
||||
location: Location,
|
||||
) -> CrushResult<Option<ThreadId>> {
|
||||
match struct_value.get("__call__") {
|
||||
Some(Value::Command(call)) => {
|
||||
invoke_command(call, Some(Value::Struct(struct_value)), local_arguments, context)
|
||||
}
|
||||
Some(v) => error(
|
||||
format!(
|
||||
"__call__ should be a command, was of type {}",
|
||||
v.value_type().to_string()
|
||||
)
|
||||
.as_str(),
|
||||
),
|
||||
_ => {
|
||||
if local_arguments.len() == 0 {
|
||||
invoke_command(
|
||||
context.scope.global_static_cmd(vec!["global", "io", "val"])?,
|
||||
None,
|
||||
vec![ArgumentDefinition::unnamed(ValueDefinition::Value(
|
||||
Value::Struct(struct_value),
|
||||
location,
|
||||
))],
|
||||
context,
|
||||
)
|
||||
} else {
|
||||
error(
|
||||
format!(
|
||||
"Struct must have a member __call__ to be used as a command {}",
|
||||
struct_value.to_string()
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke_command(
|
||||
action: Command,
|
||||
command: Command,
|
||||
this: Option<Value>,
|
||||
local_arguments: Vec<ArgumentDefinition>,
|
||||
context: JobContext,
|
||||
) -> CrushResult<Option<ThreadId>> {
|
||||
if !action.can_block(&local_arguments, &mut CompileContext::from(&context))
|
||||
if !command.can_block(&local_arguments, &mut CompileContext::from(&context))
|
||||
&& !arg_can_block(&local_arguments, &mut CompileContext::from(&context))
|
||||
{
|
||||
let new_context =
|
||||
CommandInvocation::execution_context(local_arguments, this, context.clone())?;
|
||||
context.global_state.printer().handle_error(action.invoke(new_context));
|
||||
context.global_state.printer().handle_error(command.invoke(new_context));
|
||||
Ok(None)
|
||||
} else {
|
||||
let t = context.global_state.threads().clone();
|
||||
let name = action.name().to_string();
|
||||
let name = command.name().to_string();
|
||||
Ok(Some(t.spawn(
|
||||
&name,
|
||||
move || {
|
||||
let res = CommandInvocation::execution_context(local_arguments, this, context.clone())?;
|
||||
action.invoke(res)
|
||||
command.invoke(res)
|
||||
},
|
||||
)?))
|
||||
}
|
||||
@ -341,7 +337,7 @@ fn try_external_command(
|
||||
),
|
||||
arguments,
|
||||
};
|
||||
call.invoke(context)
|
||||
call.eval(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ impl CommandContext {
|
||||
arguments,
|
||||
this,
|
||||
global_state: self.global_state,
|
||||
handle: self.handle.clone(),
|
||||
handle: self.handle,
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,7 +347,22 @@ impl CommandContext {
|
||||
arguments: self.arguments,
|
||||
this: self.this,
|
||||
global_state: self.global_state,
|
||||
handle: self.handle.clone(),
|
||||
handle: self.handle,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Return a new Command context that is identical to this one but with a different output sender.
|
||||
*/
|
||||
pub fn with_scope(self, scope : Scope) -> CommandContext {
|
||||
CommandContext {
|
||||
input: self.input,
|
||||
output: self.output,
|
||||
scope,
|
||||
arguments: self.arguments,
|
||||
this: self.this,
|
||||
global_state: self.global_state,
|
||||
handle: self.handle,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ pub fn execute_prompt(
|
||||
ValueDefinition::Value(Value::Command(prompt), Location::new(0, 0)),
|
||||
vec![]);
|
||||
let (snd, recv) = pipe();
|
||||
cmd.invoke(JobContext::new(
|
||||
cmd.eval(JobContext::new(
|
||||
empty_channel(),
|
||||
snd,
|
||||
env.clone(),
|
||||
|
@ -39,7 +39,7 @@ impl Job {
|
||||
let last_job_idx = self.commands.len() - 1;
|
||||
for call_def in &self.commands[..last_job_idx] {
|
||||
let (output, next_input) = pipe();
|
||||
call_def.invoke(context.with_io(input, output))?;
|
||||
call_def.eval(context.with_io(input, output))?;
|
||||
input = next_input;
|
||||
|
||||
if context.scope.is_stopped() {
|
||||
@ -52,15 +52,16 @@ impl Job {
|
||||
}
|
||||
|
||||
let last_call_def = &self.commands[last_job_idx];
|
||||
last_call_def.invoke(context.with_io(input, context.output.clone())).map_err(|e| e.with_location(self.location))
|
||||
last_call_def.eval(context.with_io(input, context.output.clone())).map_err(|e| e.with_location(self.location))
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> Option<String> {
|
||||
/** Extracts the help message from a closure definition */
|
||||
pub fn extract_help_message(&self) -> Option<String> {
|
||||
if self.commands.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.commands[0].as_string()
|
||||
self.commands[0].extract_help_message()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,6 +174,17 @@ pub fn pipe() -> (ValueSender, ValueReceiver) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
A Sender/Receiver pair that is bounded to only one Value on the wire before blocking.
|
||||
*/
|
||||
pub fn printer_pipe() -> (ValueSender, ValueReceiver) {
|
||||
let (send, recv) = bounded(1);
|
||||
(
|
||||
ValueSender { sender: send, is_pipeline: false },
|
||||
ValueReceiver { receiver: recv, is_pipeline: false },
|
||||
)
|
||||
}
|
||||
|
||||
pub fn streams(signature: Vec<ColumnType>) -> (OutputStream, InputStream) {
|
||||
let (output, input) = bounded(128);
|
||||
(
|
||||
|
@ -6,7 +6,7 @@ use crate::lang::data::binary::BinaryReader;
|
||||
use crate::lang::errors::to_crush_error;
|
||||
use crate::lang::data::list::ListReader;
|
||||
use crate::lang::printer::Printer;
|
||||
use crate::lang::pipe::{CrushStream, InputStream, ValueSender, pipe};
|
||||
use crate::lang::pipe::{CrushStream, InputStream, ValueSender, pipe, printer_pipe};
|
||||
use crate::lang::data::table::ColumnType;
|
||||
use crate::lang::data::table::Row;
|
||||
use crate::lang::data::table::Table;
|
||||
@ -44,7 +44,7 @@ pub fn create_pretty_printer(
|
||||
global_state: &GlobalState,
|
||||
) -> ValueSender {
|
||||
let global_state = global_state.clone();
|
||||
let (o, i) = pipe();
|
||||
let (o, i) = printer_pipe();
|
||||
let printer_clone = printer.clone();
|
||||
printer_clone.handle_error(to_crush_error(
|
||||
thread::Builder::new()
|
||||
|
@ -49,20 +49,12 @@ impl ValueDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_unbound(
|
||||
&self,
|
||||
context: &mut CompileContext,
|
||||
) -> CrushResult<(Option<Value>, Value)> {
|
||||
self.compile_internal(context, true)
|
||||
}
|
||||
|
||||
pub fn compile_bound(&self, context: &mut CompileContext) -> CrushResult<Value> {
|
||||
let (t, v) = self.compile_internal(context, true)?;
|
||||
|
||||
pub fn eval_and_bind(&self, context: &mut CompileContext) -> CrushResult<Value> {
|
||||
let (t, v) = self.eval(context, true)?;
|
||||
Ok(t.map(|tt| v.clone().bind(tt)).unwrap_or(v))
|
||||
}
|
||||
|
||||
pub fn compile_internal(
|
||||
pub fn eval(
|
||||
&self,
|
||||
context: &mut CompileContext,
|
||||
can_block: bool,
|
||||
@ -97,7 +89,7 @@ impl ValueDefinition {
|
||||
),
|
||||
|
||||
ValueDefinition::GetAttr(parent_def, entry) => {
|
||||
let (grand_parent, mut parent) = parent_def.compile_internal(context, can_block)?;
|
||||
let (grand_parent, mut parent) = parent_def.eval(context, can_block)?;
|
||||
parent = if let Value::Command(parent_cmd) = &parent {
|
||||
if !can_block {
|
||||
return block_error();
|
||||
@ -125,7 +117,7 @@ impl ValueDefinition {
|
||||
}
|
||||
|
||||
ValueDefinition::Path(parent_def, entry) => {
|
||||
let parent = parent_def.compile_internal(context, can_block)?.1;
|
||||
let parent = parent_def.eval(context, can_block)?.1;
|
||||
let val = mandate(
|
||||
parent.path(&entry.string),
|
||||
&format!("Missing path entry {} in {}", entry, parent_def),
|
||||
|
@ -38,7 +38,7 @@ pub fn r#for(mut context: CommandContext) -> CrushResult<()> {
|
||||
}
|
||||
}
|
||||
};
|
||||
body.invoke(context.empty())?;
|
||||
body.invoke(context.empty().with_scope(env.clone()).with_args(arguments, None))?;
|
||||
if env.is_stopped() {
|
||||
break;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ fn r#loop(context: CommandContext) -> CrushResult<()> {
|
||||
context.output.initialize(vec![])?;
|
||||
loop {
|
||||
let env = context.scope.create_child(&context.scope, true);
|
||||
cfg.body.invoke(context.empty())?;
|
||||
cfg.body.invoke(context.empty().with_scope(env.clone()))?;
|
||||
if env.is_stopped() {
|
||||
break;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ fn r#while(mut context: CommandContext) -> CrushResult<()> {
|
||||
let (sender, receiver) = pipe();
|
||||
|
||||
let cond_env = context.scope.create_child(&context.scope, true);
|
||||
cfg.condition.invoke(context.empty().with_output(sender))?;
|
||||
cfg.condition.invoke(context.empty().with_scope(cond_env.clone()).with_output(sender))?;
|
||||
if cond_env.is_stopped() {
|
||||
break;
|
||||
}
|
||||
|
@ -214,8 +214,8 @@ mod macos {
|
||||
Value::Integer(i128::from(curr_task.ptinfo.pti_virtual_size)),
|
||||
Value::Duration(Duration::nanoseconds(
|
||||
i64::try_from(curr_task.ptinfo.pti_total_user + curr_task.ptinfo.pti_total_system)? *
|
||||
i64::try_from(info.numer)? /
|
||||
i64::try_from(info.denom)?)),
|
||||
i64::from(info.numer) /
|
||||
i64::from(info.denom))),
|
||||
Value::String(name)
|
||||
]));
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ pub fn run(config: Config, mut input: Stream, context: CommandContext) -> CrushR
|
||||
Argument::named(cell_type.name.as_ref(), cell.clone(), config.location)
|
||||
})
|
||||
.collect();
|
||||
closure.invoke(context.empty().with_output(sender))?;
|
||||
closure.invoke(context.empty().with_args(arguments, None).with_output(sender))?;
|
||||
receiver.recv()?
|
||||
}
|
||||
Source::Argument(idx) => row.cells()[*idx].clone(),
|
||||
@ -98,7 +98,7 @@ pub fn run(config: Config, mut input: Stream, context: CommandContext) -> CrushR
|
||||
.map(|(cell, cell_type)| Argument::named(&cell_type.name, cell.clone(), config.location))
|
||||
.collect();
|
||||
let (sender, receiver) = pipe();
|
||||
closure.invoke(context.empty().with_output(sender))?;
|
||||
closure.invoke(context.empty().with_args(arguments, None).with_output(sender))?;
|
||||
receiver.recv()?
|
||||
}
|
||||
Source::Argument(idx) => row.cells()[*idx].clone(),
|
||||
|
@ -1,4 +1,7 @@
|
||||
touch ./foo
|
||||
touch ./foo
|
||||
touch ./foo
|
||||
touch ./foo
|
||||
foo:chmod "a=" "o+xr" "u+w" "g-r"
|
||||
find ./foo | select ^permissions
|
||||
rm ./foo
|
||||
#rm ./foo
|
||||
|
@ -1,6 +1,6 @@
|
||||
rm ./.test_file
|
||||
# Pipe output of find into a file
|
||||
find example_data/tree|select ^file ^type | pup:to ./.test_file
|
||||
find example_data/tree|select ^file ^type | sort ^file | pup:to ./.test_file
|
||||
# And read it back out again
|
||||
pup:from ./.test_file
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user