nushell/crates/nu-engine/src/eval.rs

1274 lines
42 KiB
Rust
Raw Normal View History

use std::cmp::Ordering;
use std::collections::HashMap;
use std::io::Write;
use nu_path::expand_path_with;
use nu_protocol::ast::{Block, Call, Expr, Expression, Operator};
2021-10-25 18:58:58 +02:00
use nu_protocol::engine::{EngineState, Stack};
2021-10-25 06:01:02 +02:00
use nu_protocol::{
IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Range, ShellError, Span,
Spanned, Unit, Value, VarId, ENV_VARIABLE_ID,
2021-10-25 06:01:02 +02:00
};
Use only $nu.env.PWD for getting the current directory (#587) * Use only $nu.env.PWD for getting current directory Because setting and reading to/from std::env changes the global state shich is problematic if we call `cd` from multiple threads (e.g., in a `par-each` block). With this change, when engine-q starts, it will either inherit existing PWD env var, or create a new one from `std::env::current_dir()`. Otherwise, everything that needs the current directory will get it from `$nu.env.PWD`. Each spawned external command will get its current directory per-process which should be thread-safe. One thing left to do is to patch nu-path for this as well since it uses `std::env::current_dir()` in its expansions. * Rename nu-path functions *_with is not *_relative which should be more descriptive and frees "with" for use in a followup commit. * Clone stack every each iter; Fix some commands Cloning the stack each iteration of `each` makes sure we're not reusing PWD between iterations. Some fixes in commands to make them use the new PWD. * Post-rebase cleanup, fmt, clippy * Change back _relative to _with in nu-path funcs Didn't use the idea I had for the new "_with". * Remove leftover current_dir from rebase * Add cwd sync at merge_delta() This makes sure the parser and completer always have up-to-date cwd. * Always pass absolute path to glob in ls * Do not allow PWD a relative path; Allow recovery Makes it possible to recover PWD by proceeding with the REPL cycle. * Clone stack in each also for byte/string stream * (WIP) Start moving env variables to engine state * (WIP) Move env vars to engine state (ugly) Quick and dirty code. * (WIP) Remove unused mut and args; Fmt * (WIP) Fix dataframe tests * (WIP) Fix missing args after rebase * (WIP) Clone only env vars, not the whole stack * (WIP) Add env var clone to `for` loop as well * Minor edits * Refactor merge_delta() to include stack merging. Less error-prone than doing it manually. * Clone env for each `update` command iteration * Mark env var hidden only when found in eng. state * Fix clippt warnings * Add TODO about env var reading * Do not clone empty environment in loops * Remove extra cwd collection * Split current_dir() into str and path; Fix autocd * Make completions respect PWD env var
2022-01-04 23:30:34 +01:00
use crate::{current_dir_str, get_full_help};
2021-10-13 19:53:27 +02:00
2021-08-16 00:33:34 +02:00
pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
2021-07-23 07:14:49 +02:00
match op {
Expression {
expr: Expr::Operator(operator),
..
} => Ok(operator.clone()),
2021-09-06 01:16:27 +02:00
Expression { span, expr, .. } => {
Err(ShellError::UnknownOperator(format!("{:?}", expr), *span))
}
}
2021-07-23 07:14:49 +02:00
}
2021-10-25 06:01:02 +02:00
fn eval_call(
2021-10-25 08:31:39 +02:00
engine_state: &EngineState,
caller_stack: &mut Stack,
2021-10-25 06:01:02 +02:00
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let decl = engine_state.get_decl(call.decl_id);
if !decl.is_known_external() && call.named.iter().any(|(flag, _)| flag.item == "help") {
let mut signature = decl.signature();
signature.usage = decl.usage().to_string();
signature.extra_usage = decl.extra_usage().to_string();
let full_help = get_full_help(&signature, &decl.examples(), engine_state, caller_stack);
2021-10-13 19:53:27 +02:00
Ok(Value::String {
val: full_help,
span: call.head,
2021-10-25 06:01:02 +02:00
}
.into_pipeline_data())
2021-10-13 19:53:27 +02:00
} else if let Some(block_id) = decl.get_block_id() {
2021-10-25 22:04:23 +02:00
let block = engine_state.get_block(block_id);
let mut callee_stack = caller_stack.gather_captures(&block.captures);
for (param_idx, param) in decl
.signature()
.required_positional
.iter()
.chain(decl.signature().optional_positional.iter())
.enumerate()
{
2021-07-23 23:19:30 +02:00
let var_id = param
.var_id
.expect("internal error: all custom parameters must have var_ids");
if let Some(arg) = call.positional.get(param_idx) {
let result = eval_expression(engine_state, caller_stack, arg)?;
callee_stack.add_var(var_id, result);
2022-03-07 21:08:56 +01:00
} else if let Some(arg) = &param.default_value {
let result = eval_expression(engine_state, caller_stack, arg)?;
callee_stack.add_var(var_id, result);
} else {
callee_stack.add_var(var_id, Value::nothing(call.head));
}
2021-07-23 23:19:30 +02:00
}
2021-09-07 05:37:02 +02:00
if let Some(rest_positional) = decl.signature().rest_positional {
let mut rest_items = vec![];
for arg in call.positional.iter().skip(
decl.signature().required_positional.len()
+ decl.signature().optional_positional.len(),
) {
let result = eval_expression(engine_state, caller_stack, arg)?;
2021-09-07 05:37:02 +02:00
rest_items.push(result);
}
let span = if let Some(rest_item) = rest_items.first() {
2021-10-11 20:45:31 +02:00
rest_item.span()?
2021-09-07 05:37:02 +02:00
} else {
2021-12-19 08:46:13 +01:00
call.head
2021-09-07 05:37:02 +02:00
};
callee_stack.add_var(
2021-09-07 05:37:02 +02:00
rest_positional
.var_id
.expect("Internal error: rest positional parameter lacks var_id"),
Value::List {
vals: rest_items,
2021-09-07 05:37:02 +02:00
span,
},
)
}
2021-10-11 23:17:45 +02:00
for named in decl.signature().named {
2021-10-13 19:53:27 +02:00
if let Some(var_id) = named.var_id {
let mut found = false;
for call_named in &call.named {
if call_named.0.item == named.long {
if let Some(arg) = &call_named.1 {
let result = eval_expression(engine_state, caller_stack, arg)?;
2021-10-12 06:49:17 +02:00
2022-03-07 21:08:56 +01:00
callee_stack.add_var(var_id, result);
} else if let Some(arg) = &named.default_value {
let result = eval_expression(engine_state, caller_stack, arg)?;
callee_stack.add_var(var_id, result);
2021-10-13 19:53:27 +02:00
} else {
callee_stack.add_var(
2021-10-13 19:53:27 +02:00
var_id,
Value::Bool {
val: true,
span: call.head,
},
)
}
found = true;
2021-10-11 23:17:45 +02:00
}
}
2021-10-12 06:49:17 +02:00
if !found {
if named.arg.is_none() {
callee_stack.add_var(
var_id,
Value::Bool {
val: false,
span: call.head,
},
)
2022-03-07 21:08:56 +01:00
} else if let Some(arg) = &named.default_value {
let result = eval_expression(engine_state, caller_stack, arg)?;
callee_stack.add_var(var_id, result);
} else {
callee_stack.add_var(var_id, Value::Nothing { span: call.head })
}
2021-10-13 19:53:27 +02:00
}
2021-10-12 06:49:17 +02:00
}
2021-10-11 23:17:45 +02:00
}
let result = eval_block(
engine_state,
&mut callee_stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
);
if block.redirect_env {
let caller_env_vars = caller_stack.get_env_var_names(engine_state);
// remove env vars that are present in the caller but not in the callee
// (the callee hid them)
for var in caller_env_vars.iter() {
if !callee_stack.has_env_var(engine_state, var) {
caller_stack.remove_env_var(engine_state, var);
}
}
// add new env vars from callee to caller
for env_vars in callee_stack.env_vars {
for (var, value) in env_vars {
caller_stack.add_env_var(var, value);
}
}
}
result
2021-07-30 00:56:51 +02:00
} else {
// We pass caller_stack here with the knowledge that internal commands
// are going to be specifically looking for global state in the stack
// rather than any local state.
decl.run(engine_state, caller_stack, call, input)
}
2021-07-23 07:14:49 +02:00
}
2021-09-19 23:48:33 +02:00
fn eval_external(
2021-10-25 08:31:39 +02:00
engine_state: &EngineState,
stack: &mut Stack,
head: &Expression,
2021-10-08 23:51:47 +02:00
args: &[Expression],
2021-10-25 06:01:02 +02:00
input: PipelineData,
redirect_stdout: bool,
redirect_stderr: bool,
2021-10-25 06:01:02 +02:00
) -> Result<PipelineData, ShellError> {
2021-10-25 08:31:39 +02:00
let decl_id = engine_state
.find_decl("run-external".as_bytes())
.ok_or(ShellError::ExternalNotSupported(head.span))?;
2021-09-19 23:48:33 +02:00
2021-10-25 08:31:39 +02:00
let command = engine_state.get_decl(decl_id);
2021-09-19 23:48:33 +02:00
let mut call = Call::new(head.span);
2021-10-08 23:51:47 +02:00
call.positional.push(head.clone());
2021-10-08 23:51:47 +02:00
for arg in args {
2021-10-09 00:30:10 +02:00
call.positional.push(arg.clone())
2021-10-08 23:51:47 +02:00
}
2021-09-19 23:48:33 +02:00
if redirect_stdout {
2021-10-11 23:17:45 +02:00
call.named.push((
Spanned {
item: "redirect-stdout".into(),
span: head.span,
},
None,
))
}
if redirect_stderr {
call.named.push((
Spanned {
item: "redirect-stderr".into(),
span: head.span,
2021-10-11 23:17:45 +02:00
},
None,
))
2021-09-23 18:42:03 +02:00
}
2021-10-25 08:31:39 +02:00
command.run(engine_state, stack, &call, input)
2021-09-19 23:48:33 +02:00
}
2021-09-03 00:58:15 +02:00
pub fn eval_expression(
2021-10-25 08:31:39 +02:00
engine_state: &EngineState,
stack: &mut Stack,
2021-09-03 00:58:15 +02:00
expr: &Expression,
) -> Result<Value, ShellError> {
2021-07-23 07:14:49 +02:00
match &expr.expr {
2021-07-24 07:57:17 +02:00
Expr::Bool(b) => Ok(Value::Bool {
val: *b,
span: expr.span,
}),
2021-07-23 07:14:49 +02:00
Expr::Int(i) => Ok(Value::Int {
val: *i,
span: expr.span,
}),
2021-08-08 22:21:21 +02:00
Expr::Float(f) => Ok(Value::Float {
val: *f,
span: expr.span,
}),
2022-03-01 00:31:53 +01:00
Expr::Binary(b) => Ok(Value::Binary {
val: b.clone(),
span: expr.span,
}),
2021-10-25 08:31:39 +02:00
Expr::ValueWithUnit(e, unit) => match eval_expression(engine_state, stack, e)? {
2021-10-05 04:27:39 +02:00
Value::Int { val, .. } => Ok(compute(val, unit.item, unit.span)),
x => Err(ShellError::CantConvert(
"unit value".into(),
x.get_type().to_string(),
e.span,
)),
2021-10-05 04:27:39 +02:00
},
Expr::Range(from, next, to, operator) => {
let from = if let Some(f) = from {
2021-10-25 08:31:39 +02:00
eval_expression(engine_state, stack, f)?
} else {
2021-12-19 08:46:13 +01:00
Value::Nothing { span: expr.span }
};
let next = if let Some(s) = next {
2021-10-25 08:31:39 +02:00
eval_expression(engine_state, stack, s)?
} else {
2021-12-19 08:46:13 +01:00
Value::Nothing { span: expr.span }
};
let to = if let Some(t) = to {
2021-10-25 08:31:39 +02:00
eval_expression(engine_state, stack, t)?
} else {
2021-12-19 08:46:13 +01:00
Value::Nothing { span: expr.span }
};
Ok(Value::Range {
val: Box::new(Range::new(expr.span, from, next, to, operator)?),
span: expr.span,
})
}
2021-10-29 20:15:17 +02:00
Expr::Var(var_id) => eval_variable(engine_state, stack, *var_id, expr.span),
2021-10-25 22:04:23 +02:00
Expr::VarDecl(_) => Ok(Value::Nothing { span: expr.span }),
2021-10-02 04:59:11 +02:00
Expr::CellPath(cell_path) => Ok(Value::CellPath {
val: cell_path.clone(),
span: expr.span,
}),
2021-09-26 20:39:19 +02:00
Expr::FullCellPath(cell_path) => {
2021-10-25 08:31:39 +02:00
let value = eval_expression(engine_state, stack, &cell_path.head)?;
2021-09-07 00:02:24 +02:00
2021-09-26 20:39:19 +02:00
value.follow_cell_path(&cell_path.tail)
2021-09-07 00:02:24 +02:00
}
2021-12-19 08:46:13 +01:00
Expr::ImportPattern(_) => Ok(Value::Nothing { span: expr.span }),
2021-10-25 06:01:02 +02:00
Expr::Call(call) => {
// FIXME: protect this collect with ctrl-c
Ok(
eval_call(engine_state, stack, call, PipelineData::new(call.head))?
.into_value(call.head),
)
2021-10-25 06:01:02 +02:00
}
Expr::ExternalCall(head, args) => {
let span = head.span;
2021-10-25 06:01:02 +02:00
// FIXME: protect this collect with ctrl-c
2021-10-25 23:14:21 +02:00
Ok(eval_external(
engine_state,
stack,
head,
2021-10-25 23:14:21 +02:00
args,
PipelineData::new(span),
false,
false,
2021-10-25 23:14:21 +02:00
)?
.into_value(span))
2021-09-23 18:42:03 +02:00
}
Expr::DateTime(dt) => Ok(Value::Date {
val: *dt,
span: expr.span,
}),
2021-08-08 23:02:47 +02:00
Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }),
2021-07-23 07:14:49 +02:00
Expr::BinaryOp(lhs, op, rhs) => {
2021-08-10 08:31:34 +02:00
let op_span = op.span;
2021-10-25 08:31:39 +02:00
let lhs = eval_expression(engine_state, stack, lhs)?;
2021-08-16 00:33:34 +02:00
let op = eval_operator(op)?;
2021-07-23 07:14:49 +02:00
match op {
Operator::And => {
if !lhs.is_true() {
Ok(Value::Bool {
val: false,
span: expr.span,
})
} else {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.and(op_span, &rhs)
}
}
Operator::Or => {
if lhs.is_true() {
Ok(Value::Bool {
val: true,
span: expr.span,
})
} else {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.or(op_span, &rhs)
}
}
Operator::Plus => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.add(op_span, &rhs)
}
Operator::Minus => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.sub(op_span, &rhs)
}
Operator::Multiply => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.mul(op_span, &rhs)
}
Operator::Divide => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.div(op_span, &rhs)
}
Operator::LessThan => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.lt(op_span, &rhs)
}
Operator::LessThanOrEqual => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.lte(op_span, &rhs)
}
Operator::GreaterThan => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.gt(op_span, &rhs)
}
Operator::GreaterThanOrEqual => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.gte(op_span, &rhs)
}
Operator::Equal => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.eq(op_span, &rhs)
}
Operator::NotEqual => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.ne(op_span, &rhs)
}
Operator::In => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.r#in(op_span, &rhs)
}
Operator::NotIn => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.not_in(op_span, &rhs)
}
Operator::Contains => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.contains(op_span, &rhs)
}
Operator::NotContains => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.not_contains(op_span, &rhs)
}
Operator::Modulo => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.modulo(op_span, &rhs)
}
Operator::Pow => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.pow(op_span, &rhs)
}
2021-07-23 07:14:49 +02:00
}
}
Expr::Subexpression(block_id) => {
2021-10-25 08:31:39 +02:00
let block = engine_state.get_block(*block_id);
2021-07-23 07:14:49 +02:00
2021-10-25 06:01:02 +02:00
// FIXME: protect this collect with ctrl-c
Ok(
eval_subexpression(engine_state, stack, block, PipelineData::new(expr.span))?
.into_value(expr.span),
)
2021-07-23 07:14:49 +02:00
}
Expr::RowCondition(block_id) | Expr::Block(block_id) => {
let mut captures = HashMap::new();
let block = engine_state.get_block(*block_id);
for var_id in &block.captures {
captures.insert(*var_id, stack.get_var(*var_id, expr.span)?);
}
Ok(Value::Block {
val: *block_id,
captures,
span: expr.span,
})
}
2021-07-23 23:19:30 +02:00
Expr::List(x) => {
let mut output = vec![];
for expr in x {
2021-10-25 08:31:39 +02:00
output.push(eval_expression(engine_state, stack, expr)?);
2021-07-23 23:19:30 +02:00
}
2021-08-08 23:02:47 +02:00
Ok(Value::List {
vals: output,
2021-08-08 23:02:47 +02:00
span: expr.span,
})
2021-07-23 23:19:30 +02:00
}
2021-11-11 00:14:00 +01:00
Expr::Record(fields) => {
let mut cols = vec![];
let mut vals = vec![];
for (col, val) in fields {
cols.push(eval_expression(engine_state, stack, col)?.as_string()?);
vals.push(eval_expression(engine_state, stack, val)?);
}
Ok(Value::Record {
cols,
vals,
span: expr.span,
})
}
2021-08-28 21:17:30 +02:00
Expr::Table(headers, vals) => {
let mut output_headers = vec![];
for expr in headers {
2021-10-25 08:31:39 +02:00
output_headers.push(eval_expression(engine_state, stack, expr)?.as_string()?);
2021-08-28 21:17:30 +02:00
}
let mut output_rows = vec![];
for val in vals {
let mut row = vec![];
for expr in val {
2021-10-25 08:31:39 +02:00
row.push(eval_expression(engine_state, stack, expr)?);
2021-08-28 21:17:30 +02:00
}
output_rows.push(Value::Record {
cols: output_headers.clone(),
vals: row,
span: expr.span,
});
2021-08-28 21:17:30 +02:00
}
Ok(Value::List {
vals: output_rows,
2021-08-28 21:17:30 +02:00
span: expr.span,
})
}
2021-10-25 08:31:39 +02:00
Expr::Keyword(_, _, expr) => eval_expression(engine_state, stack, expr),
Expr::StringInterpolation(exprs) => {
let mut parts = vec![];
for expr in exprs {
parts.push(eval_expression(engine_state, stack, expr)?);
}
let config = stack.get_config().unwrap_or_default();
parts
.into_iter()
.into_pipeline_data(None)
.collect_string("", &config)
.map(|x| Value::String {
val: x,
span: expr.span,
})
}
2021-07-23 23:19:30 +02:00
Expr::String(s) => Ok(Value::String {
val: s.clone(),
span: expr.span,
}),
Expr::Filepath(s) => {
let cwd = current_dir_str(engine_state, stack)?;
let path = expand_path_with(s, cwd);
Ok(Value::String {
val: path.to_string_lossy().to_string(),
span: expr.span,
})
}
Expr::GlobPattern(s) => {
let cwd = current_dir_str(engine_state, stack)?;
let path = expand_path_with(s, cwd);
Ok(Value::String {
val: path.to_string_lossy().to_string(),
span: expr.span,
})
}
2021-08-08 23:02:47 +02:00
Expr::Signature(_) => Ok(Value::Nothing { span: expr.span }),
Expr::Garbage => Ok(Value::Nothing { span: expr.span }),
Expr::Nothing => Ok(Value::Nothing { span: expr.span }),
}
2021-07-23 07:14:49 +02:00
}
/// Checks the expression to see if it's a internal or external call. If so, passes the input
/// into the call and gets out the result
/// Otherwise, invokes the expression
pub fn eval_expression_with_input(
engine_state: &EngineState,
stack: &mut Stack,
expr: &Expression,
mut input: PipelineData,
redirect_stdout: bool,
redirect_stderr: bool,
) -> Result<PipelineData, ShellError> {
match expr {
Expression {
expr: Expr::Call(call),
..
} => {
if !redirect_stdout || redirect_stderr {
// we're doing something different than the defaults
let mut call = call.clone();
call.redirect_stdout = redirect_stdout;
call.redirect_stderr = redirect_stderr;
input = eval_call(engine_state, stack, &call, input)?;
} else {
input = eval_call(engine_state, stack, call, input)?;
}
}
Expression {
expr: Expr::ExternalCall(head, args),
..
} => {
input = eval_external(
engine_state,
stack,
head,
args,
input,
redirect_stdout,
redirect_stderr,
)?;
}
Expression {
expr: Expr::Subexpression(block_id),
..
} => {
let block = engine_state.get_block(*block_id);
// FIXME: protect this collect with ctrl-c
input = eval_subexpression(engine_state, stack, block, input)?;
}
elem => {
input = eval_expression(engine_state, stack, elem)?.into_pipeline_data();
}
}
Ok(input)
}
2021-09-03 04:15:01 +02:00
pub fn eval_block(
2021-10-25 08:31:39 +02:00
engine_state: &EngineState,
stack: &mut Stack,
2021-09-03 04:15:01 +02:00
block: &Block,
2021-10-25 06:01:02 +02:00
mut input: PipelineData,
redirect_stdout: bool,
redirect_stderr: bool,
2021-10-25 06:01:02 +02:00
) -> Result<PipelineData, ShellError> {
let num_pipelines = block.len();
for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() {
for (i, elem) in pipeline.expressions.iter().enumerate() {
input = eval_expression_with_input(
engine_state,
stack,
elem,
input,
redirect_stdout || (i != pipeline.expressions.len() - 1),
redirect_stderr,
)?
}
if pipeline_idx < (num_pipelines) - 1 {
match input {
PipelineData::Value(Value::Nothing { .. }, ..) => {}
2022-03-04 23:46:18 +01:00
PipelineData::ExternalStream {
ref mut exit_code, ..
} => {
let exit_code = exit_code.take();
// Drain the input to the screen via tabular output
let config = stack.get_config().unwrap_or_default();
match engine_state.find_decl("table".as_bytes()) {
Some(decl_id) => {
let table = engine_state.get_decl(decl_id).run(
engine_state,
stack,
&Call::new(Span::new(0, 0)),
input,
)?;
for item in table {
let stdout = std::io::stdout();
if let Value::Error { error } = item {
return Err(error);
}
let mut out = item.into_string("\n", &config);
out.push('\n');
match stdout.lock().write_all(out.as_bytes()) {
Ok(_) => (),
Err(err) => eprintln!("{}", err),
};
}
}
None => {
for item in input {
let stdout = std::io::stdout();
if let Value::Error { error } = item {
return Err(error);
}
let mut out = item.into_string("\n", &config);
out.push('\n');
match stdout.lock().write_all(out.as_bytes()) {
Ok(_) => (),
Err(err) => eprintln!("{}", err),
};
}
}
};
if let Some(exit_code) = exit_code {
let mut v: Vec<_> = exit_code.collect();
if let Some(v) = v.pop() {
stack.add_env_var("LAST_EXIT_CODE".into(), v);
}
}
}
_ => {
// Drain the input to the screen via tabular output
let config = stack.get_config().unwrap_or_default();
match engine_state.find_decl("table".as_bytes()) {
Some(decl_id) => {
let table = engine_state.get_decl(decl_id).run(
engine_state,
stack,
&Call::new(Span::new(0, 0)),
input,
)?;
for item in table {
let stdout = std::io::stdout();
if let Value::Error { error } = item {
return Err(error);
}
let mut out = item.into_string("\n", &config);
out.push('\n');
match stdout.lock().write_all(out.as_bytes()) {
Ok(_) => (),
Err(err) => eprintln!("{}", err),
};
}
}
None => {
for item in input {
let stdout = std::io::stdout();
if let Value::Error { error } = item {
return Err(error);
}
let mut out = item.into_string("\n", &config);
out.push('\n');
match stdout.lock().write_all(out.as_bytes()) {
Ok(_) => (),
Err(err) => eprintln!("{}", err),
};
}
}
};
}
}
input = PipelineData::new(Span { start: 0, end: 0 })
}
}
Ok(input)
}
pub fn eval_subexpression(
engine_state: &EngineState,
stack: &mut Stack,
block: &Block,
mut input: PipelineData,
) -> Result<PipelineData, ShellError> {
for pipeline in block.pipelines.iter() {
for expr in pipeline.expressions.iter() {
input = eval_expression_with_input(engine_state, stack, expr, input, true, false)?
}
}
2021-07-23 07:14:49 +02:00
2021-09-03 04:15:01 +02:00
Ok(input)
}
2021-10-05 04:27:39 +02:00
pub fn create_scope(
engine_state: &EngineState,
stack: &Stack,
span: Span,
) -> Result<Value, ShellError> {
let mut output_cols = vec![];
let mut output_vals = vec![];
let mut vars = vec![];
let mut commands = vec![];
let mut aliases = vec![];
let mut overlays = vec![];
for frame in &engine_state.scope {
for var in &frame.vars {
let var_name = Value::string(String::from_utf8_lossy(var.0).to_string(), span);
let var_type = Value::string(engine_state.get_var(*var.1).to_string(), span);
let var_value = if let Ok(val) = stack.get_var(*var.1, span) {
val
} else {
Value::nothing(span)
};
vars.push(Value::Record {
cols: vec!["name".to_string(), "type".to_string(), "value".to_string()],
vals: vec![var_name, var_type, var_value],
span,
})
}
for command in &frame.decls {
let mut cols = vec![];
let mut vals = vec![];
cols.push("command".into());
vals.push(Value::String {
val: String::from_utf8_lossy(command.0).to_string(),
span,
});
let decl = engine_state.get_decl(*command.1);
let signature = decl.signature();
cols.push("category".to_string());
vals.push(Value::String {
val: signature.category.to_string(),
span,
});
// signature
let mut sig_records = vec![];
{
let sig_cols = vec![
"command".to_string(),
"parameter_name".to_string(),
"parameter_type".to_string(),
"syntax_shape".to_string(),
"is_optional".to_string(),
"short_flag".to_string(),
"description".to_string(),
];
// required_positional
for req in signature.required_positional {
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(req.name, span),
Value::string("positional", span),
Value::string(req.shape.to_string(), span),
Value::boolean(false, span),
Value::nothing(span),
Value::string(req.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
// optional_positional
for opt in signature.optional_positional {
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(opt.name, span),
Value::string("positional", span),
Value::string(opt.shape.to_string(), span),
Value::boolean(true, span),
Value::nothing(span),
Value::string(opt.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
{
// rest_positional
if let Some(rest) = signature.rest_positional {
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(rest.name, span),
Value::string("rest", span),
Value::string(rest.shape.to_string(), span),
Value::boolean(true, span),
Value::nothing(span),
Value::string(rest.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
}
// named flags
for named in signature.named {
let flag_type;
// Skip the help flag
if named.long == "help" {
continue;
}
let shape = if let Some(arg) = named.arg {
flag_type = Value::string("named", span);
Value::string(arg.to_string(), span)
} else {
flag_type = Value::string("switch", span);
Value::nothing(span)
};
let short_flag = if let Some(c) = named.short {
Value::string(c, span)
} else {
Value::nothing(span)
};
let sig_vals = vec![
Value::string(&signature.name, span),
Value::string(named.long, span),
flag_type,
shape,
Value::boolean(!named.required, span),
short_flag,
Value::string(named.desc, span),
];
sig_records.push(Value::Record {
cols: sig_cols.clone(),
vals: sig_vals,
span,
});
}
}
cols.push("signature".to_string());
vals.push(Value::List {
vals: sig_records,
span,
});
cols.push("usage".to_string());
vals.push(Value::String {
val: decl.usage().into(),
span,
});
cols.push("examples".to_string());
vals.push(Value::List {
vals: decl
.examples()
.into_iter()
.map(|x| Value::Record {
cols: vec!["description".into(), "example".into()],
vals: vec![
Value::String {
val: x.description.to_string(),
span,
},
Value::String {
val: x.example.to_string(),
span,
},
],
span,
})
.collect(),
span,
});
cols.push("is_binary".to_string());
vals.push(Value::Bool {
val: decl.is_binary(),
span,
});
cols.push("is_private".to_string());
vals.push(Value::Bool {
val: decl.is_private(),
span,
});
cols.push("is_builtin".to_string());
vals.push(Value::Bool {
val: decl.is_builtin(),
span,
});
cols.push("is_sub".to_string());
vals.push(Value::Bool {
val: decl.is_sub(),
span,
});
cols.push("is_plugin".to_string());
vals.push(Value::Bool {
val: decl.is_plugin().is_some(),
span,
});
cols.push("is_custom".to_string());
vals.push(Value::Bool {
val: decl.get_block_id().is_some(),
span,
});
cols.push("is_extern".to_string());
vals.push(Value::Bool {
val: decl.is_known_external(),
span,
});
cols.push("creates_scope".to_string());
vals.push(Value::Bool {
val: signature.creates_scope,
span,
});
cols.push("extra_usage".to_string());
vals.push(Value::String {
val: decl.extra_usage().into(),
span,
});
commands.push(Value::Record { cols, vals, span })
}
for (alias_name, alias_id) in &frame.aliases {
let alias = engine_state.get_alias(*alias_id);
let mut alias_text = String::new();
for span in alias {
let contents = engine_state.get_span_contents(span);
if !alias_text.is_empty() {
alias_text.push(' ');
}
alias_text.push_str(&String::from_utf8_lossy(contents).to_string());
}
aliases.push((
Value::String {
val: String::from_utf8_lossy(alias_name).to_string(),
span,
},
Value::string(alias_text, span),
));
}
for overlay in &frame.overlays {
overlays.push(Value::String {
val: String::from_utf8_lossy(overlay.0).to_string(),
span,
});
}
}
output_cols.push("vars".to_string());
output_vals.push(Value::List { vals: vars, span });
commands.sort_by(|a, b| match (a, b) {
(Value::Record { vals: rec_a, .. }, Value::Record { vals: rec_b, .. }) => {
// Comparing the first value from the record
// It is expected that the first value is the name of the column
// The names of the commands should be a value string
match (rec_a.get(0), rec_b.get(0)) {
(Some(val_a), Some(val_b)) => match (val_a, val_b) {
(Value::String { val: str_a, .. }, Value::String { val: str_b, .. }) => {
str_a.cmp(str_b)
}
_ => Ordering::Equal,
},
_ => Ordering::Equal,
}
}
_ => Ordering::Equal,
});
output_cols.push("commands".to_string());
output_vals.push(Value::List {
vals: commands,
span,
});
aliases.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
output_cols.push("aliases".to_string());
output_vals.push(Value::List {
vals: aliases
.into_iter()
.map(|(alias, value)| Value::Record {
cols: vec!["alias".into(), "expansion".into()],
vals: vec![alias, value],
span,
})
.collect(),
span,
});
overlays.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
output_cols.push("overlays".to_string());
output_vals.push(Value::List {
vals: overlays,
span,
});
Ok(Value::Record {
cols: output_cols,
vals: output_vals,
span,
})
}
2021-10-29 20:15:17 +02:00
pub fn eval_variable(
2021-11-02 04:08:05 +01:00
engine_state: &EngineState,
2021-10-29 20:15:17 +02:00
stack: &Stack,
var_id: VarId,
span: Span,
) -> Result<Value, ShellError> {
match var_id {
nu_protocol::NU_VARIABLE_ID => {
// $nu
let mut output_cols = vec![];
let mut output_vals = vec![];
2021-10-29 20:15:17 +02:00
if let Some(mut config_path) = nu_path::config_dir() {
config_path.push("nushell");
2021-10-29 20:15:17 +02:00
let mut history_path = config_path.clone();
2021-10-29 20:15:17 +02:00
history_path.push("history.txt");
2021-10-29 20:15:17 +02:00
output_cols.push("history-path".into());
output_vals.push(Value::String {
val: history_path.to_string_lossy().to_string(),
span,
});
2021-10-29 20:15:17 +02:00
config_path.push("config.nu");
2021-12-11 20:29:56 +01:00
output_cols.push("config-path".into());
2021-12-02 07:35:32 +01:00
output_vals.push(Value::String {
val: config_path.to_string_lossy().to_string(),
2021-12-02 07:35:32 +01:00
span,
});
}
#[cfg(feature = "plugin")]
if let Some(path) = &engine_state.plugin_signatures {
if let Some(path_str) = path.to_str() {
output_cols.push("plugin-path".into());
output_vals.push(Value::String {
val: path_str.into(),
span,
});
}
2021-12-02 07:35:32 +01:00
}
// since the env var PWD doesn't exist on all platforms
// lets just get the current directory
let cwd = current_dir_str(engine_state, stack)?;
output_cols.push("cwd".into());
output_vals.push(Value::String { val: cwd, span });
output_cols.push("scope".into());
output_vals.push(create_scope(engine_state, stack, span)?);
if let Some(home_path) = nu_path::home_dir() {
if let Some(home_path_str) = home_path.to_str() {
output_cols.push("home-path".into());
output_vals.push(Value::String {
val: home_path_str.into(),
span,
})
}
}
let temp = std::env::temp_dir();
if let Some(temp_path) = temp.to_str() {
output_cols.push("temp-path".into());
2021-12-11 20:12:30 +01:00
output_vals.push(Value::String {
val: temp_path.into(),
2021-12-11 20:12:30 +01:00
span,
})
}
Ok(Value::Record {
cols: output_cols,
vals: output_vals,
2021-12-11 21:00:29 +01:00
span,
})
}
ENV_VARIABLE_ID => {
let env_vars = stack.get_env_vars(engine_state);
let env_columns = env_vars.keys();
let env_values = env_vars.values();
let mut pairs = env_columns
.map(|x| x.to_string())
.zip(env_values.cloned())
.collect::<Vec<(String, Value)>>();
pairs.sort_by(|a, b| a.0.cmp(&b.0));
let (env_columns, env_values) = pairs.into_iter().unzip();
Ok(Value::Record {
cols: env_columns,
vals: env_values,
span,
})
}
var_id => stack.get_var(var_id, span),
2021-10-29 20:15:17 +02:00
}
}
fn compute(size: i64, unit: Unit, span: Span) -> Value {
2021-10-05 04:27:39 +02:00
match unit {
Unit::Byte => Value::Filesize { val: size, span },
Unit::Kilobyte => Value::Filesize {
val: size * 1000,
span,
},
Unit::Megabyte => Value::Filesize {
val: size * 1000 * 1000,
span,
},
Unit::Gigabyte => Value::Filesize {
val: size * 1000 * 1000 * 1000,
span,
},
Unit::Terabyte => Value::Filesize {
val: size * 1000 * 1000 * 1000 * 1000,
span,
},
Unit::Petabyte => Value::Filesize {
val: size * 1000 * 1000 * 1000 * 1000 * 1000,
span,
},
Unit::Kibibyte => Value::Filesize {
val: size * 1024,
span,
},
Unit::Mebibyte => Value::Filesize {
val: size * 1024 * 1024,
span,
},
Unit::Gibibyte => Value::Filesize {
val: size * 1024 * 1024 * 1024,
span,
},
Unit::Tebibyte => Value::Filesize {
val: size * 1024 * 1024 * 1024 * 1024,
span,
},
Unit::Pebibyte => Value::Filesize {
val: size * 1024 * 1024 * 1024 * 1024 * 1024,
span,
},
Unit::Nanosecond => Value::Duration { val: size, span },
Unit::Microsecond => Value::Duration {
val: size * 1000,
span,
},
Unit::Millisecond => Value::Duration {
val: size * 1000 * 1000,
span,
},
Unit::Second => Value::Duration {
val: size * 1000 * 1000 * 1000,
span,
},
Unit::Minute => Value::Duration {
val: size * 1000 * 1000 * 1000 * 60,
span,
},
Unit::Hour => Value::Duration {
val: size * 1000 * 1000 * 1000 * 60 * 60,
span,
},
Unit::Day => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24) {
Some(val) => Value::Duration { val, span },
None => Value::Error {
error: ShellError::SpannedLabeledError(
"duration too large".into(),
"duration too large".into(),
span,
),
},
2021-10-05 04:27:39 +02:00
},
Unit::Week => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 7) {
Some(val) => Value::Duration { val, span },
None => Value::Error {
error: ShellError::SpannedLabeledError(
"duration too large".into(),
"duration too large".into(),
span,
),
},
2021-10-05 04:27:39 +02:00
},
}
}