From c1a30ac60f6f6cd1e5cac85606fd5dc42d475df5 Mon Sep 17 00:00:00 2001 From: Yash Thakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:13:47 -0500 Subject: [PATCH] Reduce code duplication in eval.rs and eval_const.rs (#11192) --- crates/nu-engine/src/eval.rs | 688 ++++++++++--------------- crates/nu-parser/src/parse_keywords.rs | 17 +- crates/nu-parser/src/parser.rs | 4 +- crates/nu-protocol/src/eval_base.rs | 379 ++++++++++++++ crates/nu-protocol/src/eval_const.rs | 390 +++++--------- crates/nu-protocol/src/lib.rs | 1 + 6 files changed, 783 insertions(+), 696 deletions(-) create mode 100644 crates/nu-protocol/src/eval_base.rs diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 46a8c50953..307a08eb73 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -2,12 +2,13 @@ use crate::{current_dir_str, get_full_help}; use nu_path::expand_path_with; use nu_protocol::{ ast::{ - eval_operator, Argument, Assignment, Bits, Block, Boolean, Call, Comparison, Expr, - Expression, Math, Operator, PathMember, PipelineElement, RecordItem, Redirection, + Argument, Assignment, Block, Call, Expr, Expression, PathMember, PipelineElement, + Redirection, }, engine::{Closure, EngineState, Stack}, - DeclId, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Range, Record, - ShellError, Span, Spanned, Type, Unit, Value, VarId, ENV_VARIABLE_ID, + eval_base::Eval, + DeclId, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Span, + Spanned, Type, Value, VarId, ENV_VARIABLE_ID, }; use std::collections::HashMap; use std::thread::{self, JoinHandle}; @@ -253,421 +254,7 @@ pub fn eval_expression( stack: &mut Stack, expr: &Expression, ) -> Result { - match &expr.expr { - Expr::Bool(b) => Ok(Value::bool(*b, expr.span)), - Expr::Int(i) => Ok(Value::int(*i, expr.span)), - Expr::Float(f) => Ok(Value::float(*f, expr.span)), - Expr::Binary(b) => Ok(Value::binary(b.clone(), expr.span)), - Expr::ValueWithUnit(e, unit) => match eval_expression(engine_state, stack, e)? { - Value::Int { val, .. } => compute(val, unit.item, unit.span), - x => Err(ShellError::CantConvert { - to_type: "unit value".into(), - from_type: x.get_type().to_string(), - span: e.span, - help: None, - }), - }, - Expr::Range(from, next, to, operator) => { - let from = if let Some(f) = from { - eval_expression(engine_state, stack, f)? - } else { - Value::nothing(expr.span) - }; - - let next = if let Some(s) = next { - eval_expression(engine_state, stack, s)? - } else { - Value::nothing(expr.span) - }; - - let to = if let Some(t) = to { - eval_expression(engine_state, stack, t)? - } else { - Value::nothing(expr.span) - }; - - Ok(Value::range( - Range::new(expr.span, from, next, to, operator)?, - expr.span, - )) - } - Expr::Var(var_id) => eval_variable(engine_state, stack, *var_id, expr.span), - Expr::VarDecl(_) => Ok(Value::nothing(expr.span)), - Expr::CellPath(cell_path) => Ok(Value::cell_path(cell_path.clone(), expr.span)), - Expr::FullCellPath(cell_path) => { - let value = eval_expression(engine_state, stack, &cell_path.head)?; - - value.follow_cell_path(&cell_path.tail, false) - } - Expr::ImportPattern(_) => Ok(Value::nothing(expr.span)), - Expr::Overlay(_) => { - let name = - String::from_utf8_lossy(engine_state.get_span_contents(expr.span)).to_string(); - - Ok(Value::string(name, expr.span)) - } - Expr::Call(call) => { - // FIXME: protect this collect with ctrl-c - Ok(eval_call(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)) - } - Expr::ExternalCall(head, args, is_subexpression) => { - let span = head.span; - // FIXME: protect this collect with ctrl-c - Ok(eval_external( - engine_state, - stack, - head, - args, - PipelineData::empty(), - RedirectTarget::Piped(false, false), - *is_subexpression, - )? - .into_value(span)) - } - Expr::DateTime(dt) => Ok(Value::date(*dt, expr.span)), - Expr::Operator(_) => Ok(Value::nothing(expr.span)), - Expr::MatchPattern(pattern) => Ok(Value::match_pattern(*pattern.clone(), expr.span)), - Expr::MatchBlock(_) => Ok(Value::nothing(expr.span)), // match blocks are handled by `match` - Expr::UnaryNot(expr) => { - let lhs = eval_expression(engine_state, stack, expr)?; - match lhs { - Value::Bool { val, .. } => Ok(Value::bool(!val, expr.span)), - other => Err(ShellError::TypeMismatch { - err_message: format!("expected bool, found {}", other.get_type()), - span: expr.span, - }), - } - } - Expr::BinaryOp(lhs, op, rhs) => { - let op_span = op.span; - let op = eval_operator(op)?; - - match op { - Operator::Boolean(boolean) => { - let lhs = eval_expression(engine_state, stack, lhs)?; - match boolean { - Boolean::And => { - if lhs.is_false() { - Ok(Value::bool(false, expr.span)) - } else { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.and(op_span, &rhs, expr.span) - } - } - Boolean::Or => { - if lhs.is_true() { - Ok(Value::bool(true, expr.span)) - } else { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.or(op_span, &rhs, expr.span) - } - } - Boolean::Xor => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.xor(op_span, &rhs, expr.span) - } - } - } - Operator::Math(math) => { - let lhs = eval_expression(engine_state, stack, lhs)?; - let rhs = eval_expression(engine_state, stack, rhs)?; - - match math { - Math::Plus => lhs.add(op_span, &rhs, expr.span), - Math::Minus => lhs.sub(op_span, &rhs, expr.span), - Math::Multiply => lhs.mul(op_span, &rhs, expr.span), - Math::Divide => lhs.div(op_span, &rhs, expr.span), - Math::Append => lhs.append(op_span, &rhs, expr.span), - Math::Modulo => lhs.modulo(op_span, &rhs, expr.span), - Math::FloorDivision => lhs.floor_div(op_span, &rhs, expr.span), - Math::Pow => lhs.pow(op_span, &rhs, expr.span), - } - } - Operator::Comparison(comparison) => { - let lhs = eval_expression(engine_state, stack, lhs)?; - let rhs = eval_expression(engine_state, stack, rhs)?; - match comparison { - Comparison::LessThan => lhs.lt(op_span, &rhs, expr.span), - Comparison::LessThanOrEqual => lhs.lte(op_span, &rhs, expr.span), - Comparison::GreaterThan => lhs.gt(op_span, &rhs, expr.span), - Comparison::GreaterThanOrEqual => lhs.gte(op_span, &rhs, expr.span), - Comparison::Equal => lhs.eq(op_span, &rhs, expr.span), - Comparison::NotEqual => lhs.ne(op_span, &rhs, expr.span), - Comparison::In => lhs.r#in(op_span, &rhs, expr.span), - Comparison::NotIn => lhs.not_in(op_span, &rhs, expr.span), - Comparison::RegexMatch => { - lhs.regex_match(engine_state, op_span, &rhs, false, expr.span) - } - Comparison::NotRegexMatch => { - lhs.regex_match(engine_state, op_span, &rhs, true, expr.span) - } - Comparison::StartsWith => lhs.starts_with(op_span, &rhs, expr.span), - Comparison::EndsWith => lhs.ends_with(op_span, &rhs, expr.span), - } - } - Operator::Bits(bits) => { - let lhs = eval_expression(engine_state, stack, lhs)?; - let rhs = eval_expression(engine_state, stack, rhs)?; - match bits { - Bits::BitAnd => lhs.bit_and(op_span, &rhs, expr.span), - Bits::BitOr => lhs.bit_or(op_span, &rhs, expr.span), - Bits::BitXor => lhs.bit_xor(op_span, &rhs, expr.span), - Bits::ShiftLeft => lhs.bit_shl(op_span, &rhs, expr.span), - Bits::ShiftRight => lhs.bit_shr(op_span, &rhs, expr.span), - } - } - Operator::Assignment(assignment) => { - let rhs = eval_expression(engine_state, stack, rhs)?; - - let rhs = match assignment { - Assignment::Assign => rhs, - Assignment::PlusAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; - lhs.add(op_span, &rhs, op_span)? - } - Assignment::MinusAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; - lhs.sub(op_span, &rhs, op_span)? - } - Assignment::MultiplyAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; - lhs.mul(op_span, &rhs, op_span)? - } - Assignment::DivideAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; - lhs.div(op_span, &rhs, op_span)? - } - Assignment::AppendAssign => { - let lhs = eval_expression(engine_state, stack, lhs)?; - lhs.append(op_span, &rhs, op_span)? - } - }; - - match &lhs.expr { - Expr::Var(var_id) | Expr::VarDecl(var_id) => { - let var_info = engine_state.get_var(*var_id); - if var_info.mutable { - stack.add_var(*var_id, rhs); - Ok(Value::nothing(lhs.span)) - } else { - Err(ShellError::AssignmentRequiresMutableVar { lhs_span: lhs.span }) - } - } - Expr::FullCellPath(cell_path) => { - match &cell_path.head.expr { - Expr::Var(var_id) | Expr::VarDecl(var_id) => { - // The $env variable is considered "mutable" in Nushell. - // As such, give it special treatment here. - let is_env = var_id == &ENV_VARIABLE_ID; - if is_env || engine_state.get_var(*var_id).mutable { - let mut lhs = - eval_expression(engine_state, stack, &cell_path.head)?; - - lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?; - if is_env { - if cell_path.tail.is_empty() { - return Err(ShellError::CannotReplaceEnv { - span: cell_path.head.span, - }); - } - - // The special $env treatment: for something like $env.config.history.max_size = 2000, - // get $env.config (or whichever one it is) AFTER the above mutation, and set it - // as the "config" environment variable. - let vardata = lhs.follow_cell_path( - &[cell_path.tail[0].clone()], - false, - )?; - match &cell_path.tail[0] { - PathMember::String { val, span, .. } => { - if val == "FILE_PWD" - || val == "CURRENT_FILE" - || val == "PWD" - { - return Err(ShellError::AutomaticEnvVarSetManually { - envvar_name: val.to_string(), - span: *span, - }); - } else { - stack.add_env_var(val.to_string(), vardata); - } - } - // In case someone really wants an integer env-var - PathMember::Int { val, .. } => { - stack.add_env_var(val.to_string(), vardata); - } - } - } else { - stack.add_var(*var_id, lhs); - } - Ok(Value::nothing(cell_path.head.span)) - } else { - Err(ShellError::AssignmentRequiresMutableVar { - lhs_span: lhs.span, - }) - } - } - _ => Err(ShellError::AssignmentRequiresVar { lhs_span: lhs.span }), - } - } - _ => Err(ShellError::AssignmentRequiresVar { lhs_span: lhs.span }), - } - } - } - } - Expr::Subexpression(block_id) => { - let block = engine_state.get_block(*block_id); - - // FIXME: protect this collect with ctrl-c - Ok( - eval_subexpression(engine_state, stack, block, PipelineData::empty())? - .into_value(expr.span), - ) - } - Expr::RowCondition(block_id) | Expr::Closure(block_id) => { - let block_id = *block_id; - let captures = engine_state - .get_block(block_id) - .captures - .iter() - .map(|&id| stack.get_var(id, expr.span).map(|var| (id, var))) - .collect::>()?; - - Ok(Value::closure(Closure { block_id, captures }, expr.span)) - } - Expr::Block(block_id) => Ok(Value::block(*block_id, expr.span)), - Expr::List(x) => { - let mut output = vec![]; - for expr in x { - match &expr.expr { - Expr::Spread(expr) => match eval_expression(engine_state, stack, expr)? { - Value::List { mut vals, .. } => output.append(&mut vals), - _ => return Err(ShellError::CannotSpreadAsList { span: expr.span }), - }, - _ => output.push(eval_expression(engine_state, stack, expr)?), - } - } - Ok(Value::list(output, expr.span)) - } - Expr::Record(items) => { - let mut record = Record::new(); - - let mut col_names = HashMap::new(); - - for item in items { - match item { - RecordItem::Pair(col, val) => { - // avoid duplicate cols - let col_name = eval_expression(engine_state, stack, col)?.as_string()?; - if let Some(orig_span) = col_names.get(&col_name) { - return Err(ShellError::ColumnDefinedTwice { - col_name, - second_use: col.span, - first_use: *orig_span, - }); - } else { - col_names.insert(col_name.clone(), col.span); - record.push(col_name, eval_expression(engine_state, stack, val)?); - } - } - RecordItem::Spread(_, inner) => { - match eval_expression(engine_state, stack, inner)? { - Value::Record { val: inner_val, .. } => { - for (col_name, val) in inner_val { - if let Some(orig_span) = col_names.get(&col_name) { - return Err(ShellError::ColumnDefinedTwice { - col_name, - second_use: inner.span, - first_use: *orig_span, - }); - } else { - col_names.insert(col_name.clone(), inner.span); - record.push(col_name, val); - } - } - } - _ => return Err(ShellError::CannotSpreadAsRecord { span: inner.span }), - } - } - } - } - - Ok(Value::record(record, expr.span)) - } - Expr::Table(headers, vals) => { - let mut output_headers = vec![]; - for expr in headers { - let header = eval_expression(engine_state, stack, expr)?.as_string()?; - if let Some(idx) = output_headers - .iter() - .position(|existing| existing == &header) - { - return Err(ShellError::ColumnDefinedTwice { - col_name: header, - second_use: expr.span, - first_use: headers[idx].span, - }); - } else { - output_headers.push(header); - } - } - - let mut output_rows = vec![]; - for val in vals { - let mut row = vec![]; - for expr in val { - row.push(eval_expression(engine_state, stack, expr)?); - } - output_rows.push(Value::record( - Record::from_raw_cols_vals(output_headers.clone(), row), - expr.span, - )); - } - Ok(Value::list(output_rows, expr.span)) - } - 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 = engine_state.get_config(); - - parts - .into_iter() - .into_pipeline_data(None) - .collect_string("", config) - .map(|x| Value::string(x, expr.span)) - } - Expr::String(s) => Ok(Value::string(s.clone(), expr.span)), - Expr::Filepath(s) => { - let cwd = current_dir_str(engine_state, stack)?; - let path = expand_path_with(s, cwd); - - Ok(Value::string(path.to_string_lossy(), expr.span)) - } - Expr::Directory(s) => { - if s == "-" { - Ok(Value::string("-", expr.span)) - } else { - let cwd = current_dir_str(engine_state, stack)?; - let path = expand_path_with(s, cwd); - - Ok(Value::string(path.to_string_lossy(), expr.span)) - } - } - Expr::GlobPattern(s) => { - let cwd = current_dir_str(engine_state, stack)?; - let path = expand_path_with(s, cwd); - - Ok(Value::string(path.to_string_lossy(), expr.span)) - } - Expr::Signature(_) => Ok(Value::nothing(expr.span)), - Expr::Garbage => Ok(Value::nothing(expr.span)), - Expr::Nothing => Ok(Value::nothing(expr.span)), - Expr::Spread(_) => Ok(Value::nothing(expr.span)), // Spread operator only occurs in lists - } + ::eval(engine_state, stack, expr) } /// Checks the expression to see if it's a internal or external call. If so, passes the input @@ -1215,10 +802,6 @@ pub fn eval_variable( } } -fn compute(size: i64, unit: Unit, span: Span) -> Result { - unit.to_value(size, span) -} - fn gen_save_call( save_decl_id: DeclId, out_info: (Span, Expression, bool), @@ -1319,3 +902,262 @@ impl DataSaveJob { self.inner.join() } } + +struct EvalRuntime; + +impl Eval for EvalRuntime { + type State<'a> = &'a EngineState; + + type MutState = Stack; + + fn eval_filepath( + engine_state: Self::State<'_>, + stack: &mut Self::MutState, + path: String, + span: Span, + ) -> Result { + let cwd = current_dir_str(engine_state, stack)?; + let path = expand_path_with(path, cwd); + + Ok(Value::string(path.to_string_lossy(), span)) + } + + fn eval_directory( + engine_state: Self::State<'_>, + stack: &mut Self::MutState, + path: String, + span: Span, + ) -> Result { + if path == "-" { + Ok(Value::string("-", span)) + } else { + let cwd = current_dir_str(engine_state, stack)?; + let path = expand_path_with(path, cwd); + + Ok(Value::string(path.to_string_lossy(), span)) + } + } + + fn eval_var( + engine_state: &EngineState, + stack: &mut Stack, + var_id: VarId, + span: Span, + ) -> Result { + eval_variable(engine_state, stack, var_id, span) + } + + fn eval_call( + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _: Span, + ) -> Result { + // FIXME: protect this collect with ctrl-c + Ok(eval_call(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)) + } + + fn eval_external_call( + engine_state: &EngineState, + stack: &mut Stack, + head: &Expression, + args: &[Expression], + is_subexpression: bool, + _: Span, + ) -> Result { + let span = head.span; + // FIXME: protect this collect with ctrl-c + Ok(eval_external( + engine_state, + stack, + head, + args, + PipelineData::empty(), + RedirectTarget::Piped(false, false), + is_subexpression, + )? + .into_value(span)) + } + + fn eval_subexpression( + engine_state: &EngineState, + stack: &mut Stack, + block_id: usize, + span: Span, + ) -> Result { + let block = engine_state.get_block(block_id); + + // FIXME: protect this collect with ctrl-c + Ok(eval_subexpression(engine_state, stack, block, PipelineData::empty())?.into_value(span)) + } + + fn regex_match( + engine_state: &EngineState, + op_span: Span, + lhs: &Value, + rhs: &Value, + invert: bool, + expr_span: Span, + ) -> Result { + lhs.regex_match(engine_state, op_span, rhs, invert, expr_span) + } + + fn eval_assignment( + engine_state: &EngineState, + stack: &mut Stack, + lhs: &Expression, + rhs: &Expression, + assignment: Assignment, + op_span: Span, + _expr_span: Span, + ) -> Result { + let rhs = eval_expression(engine_state, stack, rhs)?; + + let rhs = match assignment { + Assignment::Assign => rhs, + Assignment::PlusAssign => { + let lhs = eval_expression(engine_state, stack, lhs)?; + lhs.add(op_span, &rhs, op_span)? + } + Assignment::MinusAssign => { + let lhs = eval_expression(engine_state, stack, lhs)?; + lhs.sub(op_span, &rhs, op_span)? + } + Assignment::MultiplyAssign => { + let lhs = eval_expression(engine_state, stack, lhs)?; + lhs.mul(op_span, &rhs, op_span)? + } + Assignment::DivideAssign => { + let lhs = eval_expression(engine_state, stack, lhs)?; + lhs.div(op_span, &rhs, op_span)? + } + Assignment::AppendAssign => { + let lhs = eval_expression(engine_state, stack, lhs)?; + lhs.append(op_span, &rhs, op_span)? + } + }; + + match &lhs.expr { + Expr::Var(var_id) | Expr::VarDecl(var_id) => { + let var_info = engine_state.get_var(*var_id); + if var_info.mutable { + stack.add_var(*var_id, rhs); + Ok(Value::nothing(lhs.span)) + } else { + Err(ShellError::AssignmentRequiresMutableVar { lhs_span: lhs.span }) + } + } + Expr::FullCellPath(cell_path) => { + match &cell_path.head.expr { + Expr::Var(var_id) | Expr::VarDecl(var_id) => { + // The $env variable is considered "mutable" in Nushell. + // As such, give it special treatment here. + let is_env = var_id == &ENV_VARIABLE_ID; + if is_env || engine_state.get_var(*var_id).mutable { + let mut lhs = eval_expression(engine_state, stack, &cell_path.head)?; + + lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?; + if is_env { + if cell_path.tail.is_empty() { + return Err(ShellError::CannotReplaceEnv { + span: cell_path.head.span, + }); + } + + // The special $env treatment: for something like $env.config.history.max_size = 2000, + // get $env.config (or whichever one it is) AFTER the above mutation, and set it + // as the "config" environment variable. + let vardata = + lhs.follow_cell_path(&[cell_path.tail[0].clone()], false)?; + match &cell_path.tail[0] { + PathMember::String { val, span, .. } => { + if val == "FILE_PWD" + || val == "CURRENT_FILE" + || val == "PWD" + { + return Err(ShellError::AutomaticEnvVarSetManually { + envvar_name: val.to_string(), + span: *span, + }); + } else { + stack.add_env_var(val.to_string(), vardata); + } + } + // In case someone really wants an integer env-var + PathMember::Int { val, .. } => { + stack.add_env_var(val.to_string(), vardata); + } + } + } else { + stack.add_var(*var_id, lhs); + } + Ok(Value::nothing(cell_path.head.span)) + } else { + Err(ShellError::AssignmentRequiresMutableVar { lhs_span: lhs.span }) + } + } + _ => Err(ShellError::AssignmentRequiresVar { lhs_span: lhs.span }), + } + } + _ => Err(ShellError::AssignmentRequiresVar { lhs_span: lhs.span }), + } + } + + fn eval_row_condition_or_closure( + engine_state: &EngineState, + stack: &mut Stack, + block_id: usize, + span: Span, + ) -> Result { + let captures = engine_state + .get_block(block_id) + .captures + .iter() + .map(|&id| stack.get_var(id, span).map(|var| (id, var))) + .collect::>()?; + + Ok(Value::closure(Closure { block_id, captures }, span)) + } + + fn eval_string_interpolation( + engine_state: &EngineState, + stack: &mut Stack, + exprs: &[Expression], + span: Span, + ) -> Result { + let mut parts = vec![]; + for expr in exprs { + parts.push(eval_expression(engine_state, stack, expr)?); + } + + let config = engine_state.get_config(); + + parts + .into_iter() + .into_pipeline_data(None) + .collect_string("", config) + .map(|x| Value::string(x, span)) + } + + fn eval_overlay(engine_state: &EngineState, span: Span) -> Result { + let name = String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string(); + + Ok(Value::string(name, span)) + } + + fn eval_glob_pattern( + engine_state: Self::State<'_>, + stack: &mut Self::MutState, + pattern: String, + span: Span, + ) -> Result { + let cwd = current_dir_str(engine_state, stack)?; + let path = expand_path_with(pattern, cwd); + + Ok(Value::string(path.to_string_lossy(), span)) + } + + fn unreachable(expr: &Expression) -> Result { + Ok(Value::nothing(expr.span)) + } +} diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index ec70292a03..cf35059c1d 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -12,7 +12,7 @@ use nu_protocol::{ ImportPatternMember, Pipeline, PipelineElement, }, engine::{StateWorkingSet, DEFAULT_OVERLAY_NAME}, - eval_const::{eval_constant, value_as_string}, + eval_const::eval_constant, span, Alias, BlockId, DeclId, Exportable, Module, ModuleId, ParseError, PositionalArg, ResolvedImportPattern, Span, Spanned, SyntaxShape, Type, VarId, }; @@ -2666,7 +2666,7 @@ pub fn parse_overlay_new(working_set: &mut StateWorkingSet, call: Box) -> let (overlay_name, _) = if let Some(expr) = call.positional_nth(0) { match eval_constant(working_set, expr) { - Ok(val) => match value_as_string(val, expr.span) { + Ok(val) => match val.as_string() { Ok(s) => (s, expr.span), Err(err) => { working_set.error(err.wrap(working_set, call_span)); @@ -2715,7 +2715,7 @@ pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box) -> let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) { match eval_constant(working_set, expr) { - Ok(val) => match value_as_string(val, expr.span) { + Ok(val) => match val.as_string() { Ok(s) => (s, expr.span), Err(err) => { working_set.error(err.wrap(working_set, call_span)); @@ -2738,7 +2738,7 @@ pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box) -> let new_name = if let Some(kw_expression) = call.positional_nth(1) { if let Some(new_name_expression) = kw_expression.as_keyword() { match eval_constant(working_set, new_name_expression) { - Ok(val) => match value_as_string(val, new_name_expression.span) { + Ok(val) => match val.as_string() { Ok(s) => Some(Spanned { item: s, span: new_name_expression.span, @@ -2932,7 +2932,7 @@ pub fn parse_overlay_hide(working_set: &mut StateWorkingSet, call: Box) -> let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) { match eval_constant(working_set, expr) { - Ok(val) => match value_as_string(val, expr.span) { + Ok(val) => match val.as_string() { Ok(s) => (s, expr.span), Err(err) => { working_set.error(err.wrap(working_set, call_span)); @@ -3393,7 +3393,7 @@ pub fn parse_source(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeli } }; - let filename = match value_as_string(val, spans[1]) { + let filename = match val.as_string() { Ok(s) => s, Err(err) => { working_set.error(err.wrap(working_set, span(&spans[1..]))); @@ -3589,8 +3589,9 @@ pub fn parse_register(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipe .map(|expr| { let val = eval_constant(working_set, expr).map_err(|err| err.wrap(working_set, call.head))?; - let filename = - value_as_string(val, expr.span).map_err(|err| err.wrap(working_set, call.head))?; + let filename = val + .as_string() + .map_err(|err| err.wrap(working_set, call.head))?; let Some(path) = find_in_dirs(&filename, working_set, &cwd, PLUGIN_DIRS_VAR) else { return Err(ParseError::RegisteredFileNotFound(filename, expr.span)); diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 7d685c318f..e5ec8578d7 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -17,7 +17,7 @@ use nu_protocol::{ RecordItem, }, engine::StateWorkingSet, - eval_const::{eval_constant, value_as_string}, + eval_const::eval_constant, span, BlockId, DidYouMean, Flag, ParseError, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID, }; @@ -2703,7 +2703,7 @@ pub fn parse_import_pattern(working_set: &mut StateWorkingSet, spans: &[Span]) - let head_expr = parse_value(working_set, *head_span, &SyntaxShape::Any); let (maybe_module_id, head_name) = match eval_constant(working_set, &head_expr) { - Ok(val) => match value_as_string(val, head_expr.span) { + Ok(val) => match val.as_string() { Ok(s) => (working_set.find_module(s.as_bytes()), s.into_bytes()), Err(err) => { working_set.error(err.wrap(working_set, span(spans))); diff --git a/crates/nu-protocol/src/eval_base.rs b/crates/nu-protocol/src/eval_base.rs new file mode 100644 index 0000000000..7ffcc25e6b --- /dev/null +++ b/crates/nu-protocol/src/eval_base.rs @@ -0,0 +1,379 @@ +use crate::{ + ast::{ + eval_operator, Assignment, Bits, Boolean, Call, Comparison, Expr, Expression, Math, + Operator, RecordItem, + }, + Range, Record, ShellError, Span, Value, VarId, +}; +use std::collections::HashMap; + +/// To share implementations for regular eval and const eval +pub trait Eval { + /// State that doesn't need to be mutated. + /// EngineState for regular eval and StateWorkingSet for const eval + type State<'a>: Copy; + + /// State that needs to be mutated. + /// This is the stack for regular eval, and unused by const eval + type MutState; + + fn eval( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + expr: &Expression, + ) -> Result { + match &expr.expr { + Expr::Bool(b) => Ok(Value::bool(*b, expr.span)), + Expr::Int(i) => Ok(Value::int(*i, expr.span)), + Expr::Float(f) => Ok(Value::float(*f, expr.span)), + Expr::Binary(b) => Ok(Value::binary(b.clone(), expr.span)), + Expr::Filepath(path) => Self::eval_filepath(state, mut_state, path.clone(), expr.span), + Expr::Directory(path) => { + Self::eval_directory(state, mut_state, path.clone(), expr.span) + } + Expr::Var(var_id) => Self::eval_var(state, mut_state, *var_id, expr.span), + Expr::CellPath(cell_path) => Ok(Value::cell_path(cell_path.clone(), expr.span)), + Expr::FullCellPath(cell_path) => { + let value = Self::eval(state, mut_state, &cell_path.head)?; + + value.follow_cell_path(&cell_path.tail, false) + } + Expr::DateTime(dt) => Ok(Value::date(*dt, expr.span)), + Expr::List(x) => { + let mut output = vec![]; + for expr in x { + match &expr.expr { + Expr::Spread(expr) => match Self::eval(state, mut_state, expr)? { + Value::List { mut vals, .. } => output.append(&mut vals), + _ => return Err(ShellError::CannotSpreadAsList { span: expr.span }), + }, + _ => output.push(Self::eval(state, mut_state, expr)?), + } + } + Ok(Value::list(output, expr.span)) + } + Expr::Record(items) => { + let mut record = Record::new(); + let mut col_names = HashMap::new(); + for item in items { + match item { + RecordItem::Pair(col, val) => { + // avoid duplicate cols + let col_name = Self::eval(state, mut_state, col)?.as_string()?; + if let Some(orig_span) = col_names.get(&col_name) { + return Err(ShellError::ColumnDefinedTwice { + col_name, + second_use: col.span, + first_use: *orig_span, + }); + } else { + col_names.insert(col_name.clone(), col.span); + record.push(col_name, Self::eval(state, mut_state, val)?); + } + } + RecordItem::Spread(_, inner) => { + match Self::eval(state, mut_state, inner)? { + Value::Record { val: inner_val, .. } => { + for (col_name, val) in inner_val { + if let Some(orig_span) = col_names.get(&col_name) { + return Err(ShellError::ColumnDefinedTwice { + col_name, + second_use: inner.span, + first_use: *orig_span, + }); + } else { + col_names.insert(col_name.clone(), inner.span); + record.push(col_name, val); + } + } + } + _ => { + return Err(ShellError::CannotSpreadAsRecord { + span: inner.span, + }) + } + } + } + } + } + + Ok(Value::record(record, expr.span)) + } + Expr::Table(headers, vals) => { + let mut output_headers = vec![]; + for expr in headers { + let header = Self::eval(state, mut_state, expr)?.as_string()?; + if let Some(idx) = output_headers + .iter() + .position(|existing| existing == &header) + { + return Err(ShellError::ColumnDefinedTwice { + col_name: header, + second_use: expr.span, + first_use: headers[idx].span, + }); + } else { + output_headers.push(header); + } + } + + let mut output_rows = vec![]; + for val in vals { + let mut row = vec![]; + for expr in val { + row.push(Self::eval(state, mut_state, expr)?); + } + // length equality already ensured in parser + output_rows.push(Value::record( + Record::from_raw_cols_vals(output_headers.clone(), row), + expr.span, + )); + } + Ok(Value::list(output_rows, expr.span)) + } + Expr::Keyword(_, _, expr) => Self::eval(state, mut_state, expr), + Expr::String(s) => Ok(Value::string(s.clone(), expr.span)), + Expr::Nothing => Ok(Value::nothing(expr.span)), + Expr::ValueWithUnit(e, unit) => match Self::eval(state, mut_state, e)? { + Value::Int { val, .. } => unit.item.to_value(val, unit.span), + x => Err(ShellError::CantConvert { + to_type: "unit value".into(), + from_type: x.get_type().to_string(), + span: e.span, + help: None, + }), + }, + Expr::Call(call) => Self::eval_call(state, mut_state, call, expr.span), + Expr::ExternalCall(head, args, is_subexpression) => { + Self::eval_external_call(state, mut_state, head, args, *is_subexpression, expr.span) + } + Expr::Subexpression(block_id) => { + Self::eval_subexpression(state, mut_state, *block_id, expr.span) + } + Expr::Range(from, next, to, operator) => { + let from = if let Some(f) = from { + Self::eval(state, mut_state, f)? + } else { + Value::nothing(expr.span) + }; + + let next = if let Some(s) = next { + Self::eval(state, mut_state, s)? + } else { + Value::nothing(expr.span) + }; + + let to = if let Some(t) = to { + Self::eval(state, mut_state, t)? + } else { + Value::nothing(expr.span) + }; + Ok(Value::range( + Range::new(expr.span, from, next, to, operator)?, + expr.span, + )) + } + Expr::UnaryNot(expr) => { + let lhs = Self::eval(state, mut_state, expr)?; + match lhs { + Value::Bool { val, .. } => Ok(Value::bool(!val, expr.span)), + other => Err(ShellError::TypeMismatch { + err_message: format!("expected bool, found {}", other.get_type()), + span: expr.span, + }), + } + } + Expr::BinaryOp(lhs, op, rhs) => { + let op_span = op.span; + let op = eval_operator(op)?; + + match op { + Operator::Boolean(boolean) => { + let lhs = Self::eval(state, mut_state, lhs)?; + match boolean { + Boolean::And => { + if lhs.is_false() { + Ok(Value::bool(false, expr.span)) + } else { + let rhs = Self::eval(state, mut_state, rhs)?; + lhs.and(op_span, &rhs, expr.span) + } + } + Boolean::Or => { + if lhs.is_true() { + Ok(Value::bool(true, expr.span)) + } else { + let rhs = Self::eval(state, mut_state, rhs)?; + lhs.or(op_span, &rhs, expr.span) + } + } + Boolean::Xor => { + let rhs = Self::eval(state, mut_state, rhs)?; + lhs.xor(op_span, &rhs, expr.span) + } + } + } + Operator::Math(math) => { + let lhs = Self::eval(state, mut_state, lhs)?; + let rhs = Self::eval(state, mut_state, rhs)?; + + match math { + Math::Plus => lhs.add(op_span, &rhs, expr.span), + Math::Minus => lhs.sub(op_span, &rhs, expr.span), + Math::Multiply => lhs.mul(op_span, &rhs, expr.span), + Math::Divide => lhs.div(op_span, &rhs, expr.span), + Math::Append => lhs.append(op_span, &rhs, expr.span), + Math::Modulo => lhs.modulo(op_span, &rhs, expr.span), + Math::FloorDivision => lhs.floor_div(op_span, &rhs, expr.span), + Math::Pow => lhs.pow(op_span, &rhs, expr.span), + } + } + Operator::Comparison(comparison) => { + let lhs = Self::eval(state, mut_state, lhs)?; + let rhs = Self::eval(state, mut_state, rhs)?; + match comparison { + Comparison::LessThan => lhs.lt(op_span, &rhs, expr.span), + Comparison::LessThanOrEqual => lhs.lte(op_span, &rhs, expr.span), + Comparison::GreaterThan => lhs.gt(op_span, &rhs, expr.span), + Comparison::GreaterThanOrEqual => lhs.gte(op_span, &rhs, expr.span), + Comparison::Equal => lhs.eq(op_span, &rhs, expr.span), + Comparison::NotEqual => lhs.ne(op_span, &rhs, expr.span), + Comparison::In => lhs.r#in(op_span, &rhs, expr.span), + Comparison::NotIn => lhs.not_in(op_span, &rhs, expr.span), + Comparison::StartsWith => lhs.starts_with(op_span, &rhs, expr.span), + Comparison::EndsWith => lhs.ends_with(op_span, &rhs, expr.span), + Comparison::RegexMatch => { + Self::regex_match(state, op_span, &lhs, &rhs, false, expr.span) + } + Comparison::NotRegexMatch => { + Self::regex_match(state, op_span, &lhs, &rhs, true, expr.span) + } + } + } + Operator::Bits(bits) => { + let lhs = Self::eval(state, mut_state, lhs)?; + let rhs = Self::eval(state, mut_state, rhs)?; + match bits { + Bits::BitAnd => lhs.bit_and(op_span, &rhs, expr.span), + Bits::BitOr => lhs.bit_or(op_span, &rhs, expr.span), + Bits::BitXor => lhs.bit_xor(op_span, &rhs, expr.span), + Bits::ShiftLeft => lhs.bit_shl(op_span, &rhs, expr.span), + Bits::ShiftRight => lhs.bit_shr(op_span, &rhs, expr.span), + } + } + Operator::Assignment(assignment) => Self::eval_assignment( + state, mut_state, lhs, rhs, assignment, op_span, expr.span, + ), + } + } + Expr::Block(block_id) => Ok(Value::block(*block_id, expr.span)), + Expr::RowCondition(block_id) | Expr::Closure(block_id) => { + Self::eval_row_condition_or_closure(state, mut_state, *block_id, expr.span) + } + Expr::StringInterpolation(exprs) => { + Self::eval_string_interpolation(state, mut_state, exprs, expr.span) + } + Expr::Overlay(_) => Self::eval_overlay(state, expr.span), + Expr::GlobPattern(pattern) => { + Self::eval_glob_pattern(state, mut_state, pattern.clone(), expr.span) + } + Expr::MatchPattern(pattern) => Ok(Value::match_pattern(*pattern.clone(), expr.span)), + Expr::MatchBlock(_) // match blocks are handled by `match` + | Expr::VarDecl(_) + | Expr::ImportPattern(_) + | Expr::Signature(_) + | Expr::Spread(_) + | Expr::Operator(_) + | Expr::Garbage => Self::unreachable(expr), + } + } + + fn eval_filepath( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + path: String, + span: Span, + ) -> Result; + + fn eval_directory( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + path: String, + span: Span, + ) -> Result; + + fn eval_var( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + var_id: VarId, + span: Span, + ) -> Result; + + fn eval_call( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + call: &Call, + span: Span, + ) -> Result; + + fn eval_external_call( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + head: &Expression, + args: &[Expression], + is_subexpression: bool, + span: Span, + ) -> Result; + + fn eval_subexpression( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + block_id: usize, + span: Span, + ) -> Result; + + fn regex_match( + state: Self::State<'_>, + op_span: Span, + lhs: &Value, + rhs: &Value, + invert: bool, + expr_span: Span, + ) -> Result; + + fn eval_assignment( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + lhs: &Expression, + rhs: &Expression, + assignment: Assignment, + op_span: Span, + expr_span: Span, + ) -> Result; + + fn eval_row_condition_or_closure( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + block_id: usize, + span: Span, + ) -> Result; + + fn eval_string_interpolation( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + exprs: &[Expression], + span: Span, + ) -> Result; + + fn eval_overlay(state: Self::State<'_>, span: Span) -> Result; + + fn eval_glob_pattern( + state: Self::State<'_>, + mut_state: &mut Self::MutState, + pattern: String, + span: Span, + ) -> Result; + + /// For expressions that should never actually be evaluated + fn unreachable(expr: &Expression) -> Result; +} diff --git a/crates/nu-protocol/src/eval_const.rs b/crates/nu-protocol/src/eval_const.rs index 4b880a1d75..6eff45ce94 100644 --- a/crates/nu-protocol/src/eval_const.rs +++ b/crates/nu-protocol/src/eval_const.rs @@ -1,16 +1,11 @@ use crate::{ - ast::{ - eval_operator, Bits, Block, Boolean, Call, Comparison, Expr, Expression, Math, Operator, - PipelineElement, RecordItem, - }, + ast::{Assignment, Block, Call, Expr, Expression, PipelineElement}, engine::{EngineState, StateWorkingSet}, - record, HistoryFileFormat, PipelineData, Range, Record, ShellError, Span, Value, + eval_base::Eval, + record, HistoryFileFormat, PipelineData, Record, ShellError, Span, Value, VarId, }; use nu_system::os_info::{get_kernel_version, get_os_arch, get_os_family, get_os_name}; -use std::{ - collections::HashMap, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; pub fn create_nu_constant(engine_state: &EngineState, span: Span) -> Result { fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf { @@ -264,271 +259,140 @@ pub fn eval_constant_with_input( } /// Evaluate a constant value at parse time -/// -/// Based off eval_expression() in the engine pub fn eval_constant( working_set: &StateWorkingSet, expr: &Expression, ) -> Result { - match &expr.expr { - Expr::Bool(b) => Ok(Value::bool(*b, expr.span)), - Expr::Int(i) => Ok(Value::int(*i, expr.span)), - Expr::Float(f) => Ok(Value::float(*f, expr.span)), - Expr::Binary(b) => Ok(Value::binary(b.clone(), expr.span)), - Expr::Filepath(path) => Ok(Value::string(path.clone(), expr.span)), - Expr::Var(var_id) => match working_set.get_variable(*var_id).const_val.as_ref() { + ::eval(working_set, &mut (), expr) +} + +struct EvalConst; + +impl Eval for EvalConst { + type State<'a> = &'a StateWorkingSet<'a>; + + type MutState = (); + + fn eval_filepath( + _: &StateWorkingSet, + _: &mut (), + path: String, + span: Span, + ) -> Result { + Ok(Value::string(path, span)) + } + + fn eval_directory( + _: &StateWorkingSet, + _: &mut (), + _: String, + span: Span, + ) -> Result { + Err(ShellError::NotAConstant(span)) + } + + fn eval_var( + working_set: &StateWorkingSet, + _: &mut (), + var_id: VarId, + span: Span, + ) -> Result { + match working_set.get_variable(var_id).const_val.as_ref() { Some(val) => Ok(val.clone()), - None => Err(ShellError::NotAConstant(expr.span)), - }, - Expr::CellPath(cell_path) => Ok(Value::cell_path(cell_path.clone(), expr.span)), - Expr::FullCellPath(cell_path) => { - let value = eval_constant(working_set, &cell_path.head)?; + None => Err(ShellError::NotAConstant(span)), + } + } - match value.follow_cell_path(&cell_path.tail, false) { - Ok(val) => Ok(val), - // TODO: Better error conversion - Err(shell_error) => Err(ShellError::GenericError( - "Error when following cell path".to_string(), - format!("{shell_error:?}"), - Some(expr.span), - None, - vec![], - )), - } - } - Expr::DateTime(dt) => Ok(Value::date(*dt, expr.span)), - Expr::List(x) => { - let mut output = vec![]; - for expr in x { - match &expr.expr { - Expr::Spread(expr) => match eval_constant(working_set, expr)? { - Value::List { mut vals, .. } => output.append(&mut vals), - _ => return Err(ShellError::CannotSpreadAsList { span: expr.span }), - }, - _ => output.push(eval_constant(working_set, expr)?), - } - } - Ok(Value::list(output, expr.span)) - } - Expr::Record(items) => { - let mut record = Record::new(); - let mut col_names = HashMap::new(); - for item in items { - match item { - RecordItem::Pair(col, val) => { - // avoid duplicate cols - let col_name = - value_as_string(eval_constant(working_set, col)?, expr.span)?; - if let Some(orig_span) = col_names.get(&col_name) { - return Err(ShellError::ColumnDefinedTwice { - col_name, - second_use: col.span, - first_use: *orig_span, - }); - } else { - col_names.insert(col_name.clone(), col.span); - record.push(col_name, eval_constant(working_set, val)?); - } - } - RecordItem::Spread(_, inner) => match eval_constant(working_set, inner)? { - Value::Record { val: inner_val, .. } => { - for (col_name, val) in inner_val { - if let Some(orig_span) = col_names.get(&col_name) { - return Err(ShellError::ColumnDefinedTwice { - col_name, - second_use: inner.span, - first_use: *orig_span, - }); - } else { - col_names.insert(col_name.clone(), inner.span); - record.push(col_name, val); - } - } - } - _ => return Err(ShellError::CannotSpreadAsRecord { span: inner.span }), - }, - } - } + fn eval_call( + working_set: &StateWorkingSet, + _: &mut (), + call: &Call, + span: Span, + ) -> Result { + // TODO: eval.rs uses call.head for the span rather than expr.span + Ok(eval_const_call(working_set, call, PipelineData::empty())?.into_value(span)) + } - Ok(Value::record(record, expr.span)) - } - Expr::Table(headers, vals) => { - let mut output_headers = vec![]; - for expr in headers { - let header = value_as_string(eval_constant(working_set, expr)?, expr.span)?; - if let Some(idx) = output_headers - .iter() - .position(|existing| existing == &header) - { - return Err(ShellError::ColumnDefinedTwice { - col_name: header, - second_use: expr.span, - first_use: headers[idx].span, - }); - } else { - output_headers.push(header); - } - } + fn eval_external_call( + _: &StateWorkingSet, + _: &mut (), + _: &Expression, + _: &[Expression], + _: bool, + span: Span, + ) -> Result { + // TODO: It may be more helpful to give not_a_const_command error + Err(ShellError::NotAConstant(span)) + } - let mut output_rows = vec![]; - for val in vals { - let mut row = vec![]; - for expr in val { - row.push(eval_constant(working_set, expr)?); - } - // length equality already ensured in parser - output_rows.push(Value::record( - Record::from_raw_cols_vals(output_headers.clone(), row), - expr.span, - )); - } - Ok(Value::list(output_rows, expr.span)) - } - Expr::Keyword(_, _, expr) => eval_constant(working_set, expr), - Expr::String(s) => Ok(Value::string(s.clone(), expr.span)), - Expr::Nothing => Ok(Value::nothing(expr.span)), - Expr::ValueWithUnit(expr, unit) => { - if let Ok(Value::Int { val, .. }) = eval_constant(working_set, expr) { - unit.item.to_value(val, unit.span) - } else { - Err(ShellError::NotAConstant(expr.span)) - } - } - Expr::Call(call) => { - Ok(eval_const_call(working_set, call, PipelineData::empty())?.into_value(expr.span)) - } - Expr::Subexpression(block_id) => { - let block = working_set.get_block(*block_id); - Ok( - eval_const_subexpression(working_set, block, PipelineData::empty(), expr.span)? - .into_value(expr.span), - ) - } - Expr::Range(from, next, to, operator) => { - let from = if let Some(f) = from { - eval_constant(working_set, f)? - } else { - Value::Nothing { - internal_span: expr.span, - } - }; + fn eval_subexpression( + working_set: &StateWorkingSet, + _: &mut (), + block_id: usize, + span: Span, + ) -> Result { + let block = working_set.get_block(block_id); + Ok( + eval_const_subexpression(working_set, block, PipelineData::empty(), span)? + .into_value(span), + ) + } - let next = if let Some(s) = next { - eval_constant(working_set, s)? - } else { - Value::Nothing { - internal_span: expr.span, - } - }; + fn regex_match( + _: &StateWorkingSet, + _op_span: Span, + _: &Value, + _: &Value, + _: bool, + expr_span: Span, + ) -> Result { + Err(ShellError::NotAConstant(expr_span)) + } - let to = if let Some(t) = to { - eval_constant(working_set, t)? - } else { - Value::Nothing { - internal_span: expr.span, - } - }; - Ok(Value::Range { - val: Box::new(Range::new(expr.span, from, next, to, operator)?), - internal_span: expr.span, - }) - } - Expr::UnaryNot(expr) => { - let lhs = eval_constant(working_set, expr)?; - match lhs { - Value::Bool { val, .. } => Ok(Value::bool(!val, expr.span)), - _ => Err(ShellError::TypeMismatch { - err_message: "bool".to_string(), - span: expr.span, - }), - } - } - Expr::BinaryOp(lhs, op, rhs) => { - let op_span = op.span; - let op = eval_operator(op)?; + fn eval_assignment( + _: &StateWorkingSet, + _: &mut (), + _: &Expression, + _: &Expression, + _: Assignment, + _op_span: Span, + expr_span: Span, + ) -> Result { + Err(ShellError::NotAConstant(expr_span)) + } - match op { - Operator::Boolean(boolean) => { - let lhs = eval_constant(working_set, lhs)?; - match boolean { - Boolean::And => { - if lhs.is_false() { - Ok(Value::bool(false, expr.span)) - } else { - let rhs = eval_constant(working_set, rhs)?; - lhs.and(op_span, &rhs, expr.span) - } - } - Boolean::Or => { - if lhs.is_true() { - Ok(Value::bool(true, expr.span)) - } else { - let rhs = eval_constant(working_set, rhs)?; - lhs.or(op_span, &rhs, expr.span) - } - } - Boolean::Xor => { - let rhs = eval_constant(working_set, rhs)?; - lhs.xor(op_span, &rhs, expr.span) - } - } - } - Operator::Math(math) => { - let lhs = eval_constant(working_set, lhs)?; - let rhs = eval_constant(working_set, rhs)?; + fn eval_row_condition_or_closure( + _: &StateWorkingSet, + _: &mut (), + _: usize, + span: Span, + ) -> Result { + Err(ShellError::NotAConstant(span)) + } - match math { - Math::Plus => lhs.add(op_span, &rhs, expr.span), - Math::Minus => lhs.sub(op_span, &rhs, expr.span), - Math::Multiply => lhs.mul(op_span, &rhs, expr.span), - Math::Divide => lhs.div(op_span, &rhs, expr.span), - Math::Append => lhs.append(op_span, &rhs, expr.span), - Math::Modulo => lhs.modulo(op_span, &rhs, expr.span), - Math::FloorDivision => lhs.floor_div(op_span, &rhs, expr.span), - Math::Pow => lhs.pow(op_span, &rhs, expr.span), - } - } - Operator::Comparison(comparison) => { - let lhs = eval_constant(working_set, lhs)?; - let rhs = eval_constant(working_set, rhs)?; - match comparison { - Comparison::LessThan => lhs.lt(op_span, &rhs, expr.span), - Comparison::LessThanOrEqual => lhs.lte(op_span, &rhs, expr.span), - Comparison::GreaterThan => lhs.gt(op_span, &rhs, expr.span), - Comparison::GreaterThanOrEqual => lhs.gte(op_span, &rhs, expr.span), - Comparison::Equal => lhs.eq(op_span, &rhs, expr.span), - Comparison::NotEqual => lhs.ne(op_span, &rhs, expr.span), - Comparison::In => lhs.r#in(op_span, &rhs, expr.span), - Comparison::NotIn => lhs.not_in(op_span, &rhs, expr.span), - Comparison::StartsWith => lhs.starts_with(op_span, &rhs, expr.span), - Comparison::EndsWith => lhs.ends_with(op_span, &rhs, expr.span), - // RegEx comparison is not a constant - _ => Err(ShellError::NotAConstant(expr.span)), - } - } - Operator::Bits(bits) => { - let lhs = eval_constant(working_set, lhs)?; - let rhs = eval_constant(working_set, rhs)?; - match bits { - Bits::BitAnd => lhs.bit_and(op_span, &rhs, expr.span), - Bits::BitOr => lhs.bit_or(op_span, &rhs, expr.span), - Bits::BitXor => lhs.bit_xor(op_span, &rhs, expr.span), - Bits::ShiftLeft => lhs.bit_shl(op_span, &rhs, expr.span), - Bits::ShiftRight => lhs.bit_shr(op_span, &rhs, expr.span), - } - } - Operator::Assignment(_) => Err(ShellError::NotAConstant(expr.span)), - } - } - Expr::Block(block_id) => Ok(Value::block(*block_id, expr.span)), - _ => Err(ShellError::NotAConstant(expr.span)), - } -} - -/// Get the value as a string -pub fn value_as_string(value: Value, span: Span) -> Result { - match value { - Value::String { val, .. } => Ok(val), - _ => Err(ShellError::NotAConstant(span)), + fn eval_string_interpolation( + _: &StateWorkingSet, + _: &mut (), + _: &[Expression], + span: Span, + ) -> Result { + Err(ShellError::NotAConstant(span)) + } + + fn eval_overlay(_: &StateWorkingSet, span: Span) -> Result { + Err(ShellError::NotAConstant(span)) + } + + fn eval_glob_pattern( + _: &StateWorkingSet, + _: &mut (), + _: String, + span: Span, + ) -> Result { + Err(ShellError::NotAConstant(span)) + } + + fn unreachable(expr: &Expression) -> Result { + Err(ShellError::NotAConstant(expr.span)) } } diff --git a/crates/nu-protocol/src/lib.rs b/crates/nu-protocol/src/lib.rs index 854f5aa30a..f9d4879240 100644 --- a/crates/nu-protocol/src/lib.rs +++ b/crates/nu-protocol/src/lib.rs @@ -4,6 +4,7 @@ pub mod cli_error; pub mod config; mod did_you_mean; pub mod engine; +pub mod eval_base; pub mod eval_const; mod example; mod exportable;