diff --git a/.cargo/config.toml b/.cargo/config.toml index 32f73a85ed..808cf30806 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,7 +1,7 @@ [target.x86_64-pc-windows-msvc] # increase the default windows stack size # statically link the CRT so users don't have to install it -rustflags = ["-C", "link-args=-stack:10000000", "-C", "target-feature=+crt-static"] +rustflags = ["-C", "link-args=-stack:2147483648", "-C", "target-feature=+crt-static"] # keeping this but commentting out in case we need them in the future diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index dbafd9ecae..73b3b44ea0 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -801,6 +801,7 @@ pub fn eval_hook( name.as_bytes().to_vec(), val.span()?, Type::Any, + false, ); vars.push((var_id, val)); diff --git a/crates/nu-command/src/core_commands/mod.rs b/crates/nu-command/src/core_commands/mod.rs index 241672e65a..6c5d1048b9 100644 --- a/crates/nu-command/src/core_commands/mod.rs +++ b/crates/nu-command/src/core_commands/mod.rs @@ -24,6 +24,7 @@ mod ignore; mod let_; mod metadata; mod module; +mod mut_; pub(crate) mod overlay; mod use_; mod version; @@ -54,6 +55,7 @@ pub use ignore::Ignore; pub use let_::Let; pub use metadata::Metadata; pub use module::Module; +pub use mut_::Mut; pub use overlay::*; pub use use_::Use; pub use version::Version; diff --git a/crates/nu-command/src/core_commands/mut_.rs b/crates/nu-command/src/core_commands/mut_.rs new file mode 100644 index 0000000000..5c6dd5847d --- /dev/null +++ b/crates/nu-command/src/core_commands/mut_.rs @@ -0,0 +1,117 @@ +use nu_engine::eval_expression_with_input; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type}; + +#[derive(Clone)] +pub struct Mut; + +impl Command for Mut { + fn name(&self) -> &str { + "mut" + } + + fn usage(&self) -> &str { + "Create a mutable variable and give it a value." + } + + fn signature(&self) -> nu_protocol::Signature { + Signature::build("mut") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) + .allow_variants_without_examples(true) + .required("var_name", SyntaxShape::VarWithOptType, "variable name") + .required( + "initial_value", + SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)), + "equals sign followed by value", + ) + .category(Category::Core) + } + + fn extra_usage(&self) -> &str { + r#"This command is a parser keyword. For details, check: + https://www.nushell.sh/book/thinking_in_nu.html"# + } + + fn is_parser_keyword(&self) -> bool { + true + } + + fn search_terms(&self) -> Vec<&str> { + vec!["set", "mutable"] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let var_id = call + .positional_nth(0) + .expect("checked through parser") + .as_var() + .expect("internal error: missing variable"); + + let keyword_expr = call + .positional_nth(1) + .expect("checked through parser") + .as_keyword() + .expect("internal error: missing keyword"); + + let rhs = eval_expression_with_input( + engine_state, + stack, + keyword_expr, + input, + call.redirect_stdout, + call.redirect_stderr, + )? + .0; + + //println!("Adding: {:?} to {}", rhs, var_id); + + stack.add_var(var_id, rhs.into_value(call.head)); + Ok(PipelineData::new(call.head)) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Set a mutable variable to a value, then update it", + example: "mut x = 10; $x = 12", + result: None, + }, + Example { + description: "Set a mutable variable to the result of an expression", + example: "mut x = 10 + 100", + result: None, + }, + Example { + description: "Set a mutable variable based on the condition", + example: "mut x = if false { -1 } else { 1 }", + result: None, + }, + ] + } +} + +#[cfg(test)] +mod test { + use nu_protocol::engine::CommandType; + + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(Mut {}) + } + + #[test] + fn test_command_type() { + assert!(matches!(Mut.command_type(), CommandType::Keyword)); + } +} diff --git a/crates/nu-command/src/dataframe/values/nu_dataframe/between_values.rs b/crates/nu-command/src/dataframe/values/nu_dataframe/between_values.rs index 2c74d622fc..c81d38eabc 100644 --- a/crates/nu-command/src/dataframe/values/nu_dataframe/between_values.rs +++ b/crates/nu-command/src/dataframe/values/nu_dataframe/between_values.rs @@ -1,5 +1,8 @@ use super::{operations::Axis, NuDataFrame}; -use nu_protocol::{ast::Operator, span, ShellError, Span, Spanned, Value}; +use nu_protocol::{ + ast::{Boolean, Comparison, Math, Operator}, + span, ShellError, Span, Spanned, Value, +}; use num::Zero; use polars::prelude::{ BooleanType, ChunkCompare, ChunkedArray, DataType, Float64Type, Int64Type, IntoSeries, @@ -16,7 +19,7 @@ pub(super) fn between_dataframes( ) -> Result { let operation_span = span(&[left.span()?, right.span()?]); match operator.item { - Operator::Plus => match lhs.append_df(rhs, Axis::Row, operation_span) { + Operator::Math(Math::Plus) => match lhs.append_df(rhs, Axis::Row, operation_span) { Ok(df) => Ok(df.into_value(operation_span)), Err(e) => Err(e), }, @@ -39,25 +42,25 @@ pub(super) fn compute_between_series( ) -> Result { let operation_span = span(&[left.span()?, right.span()?]); match operator.item { - Operator::Plus => { + Operator::Math(Math::Plus) => { let mut res = lhs + rhs; let name = format!("sum_{}_{}", lhs.name(), rhs.name()); res.rename(&name); NuDataFrame::series_to_value(res, operation_span) } - Operator::Minus => { + Operator::Math(Math::Minus) => { let mut res = lhs - rhs; let name = format!("sub_{}_{}", lhs.name(), rhs.name()); res.rename(&name); NuDataFrame::series_to_value(res, operation_span) } - Operator::Multiply => { + Operator::Math(Math::Multiply) => { let mut res = lhs * rhs; let name = format!("mul_{}_{}", lhs.name(), rhs.name()); res.rename(&name); NuDataFrame::series_to_value(res, operation_span) } - Operator::Divide => { + Operator::Math(Math::Divide) => { let res = lhs.checked_div(rhs); match res { Ok(mut res) => { @@ -74,37 +77,37 @@ pub(super) fn compute_between_series( )), } } - Operator::Equal => { + Operator::Comparison(Comparison::Equal) => { let name = format!("eq_{}_{}", lhs.name(), rhs.name()); let res = compare_series(lhs, rhs, name.as_str(), right.span().ok(), Series::equal)?; NuDataFrame::series_to_value(res, operation_span) } - Operator::NotEqual => { + Operator::Comparison(Comparison::NotEqual) => { let name = format!("neq_{}_{}", lhs.name(), rhs.name()); let res = compare_series(lhs, rhs, name.as_str(), right.span().ok(), Series::equal)?; NuDataFrame::series_to_value(res, operation_span) } - Operator::LessThan => { + Operator::Comparison(Comparison::LessThan) => { let name = format!("lt_{}_{}", lhs.name(), rhs.name()); let res = compare_series(lhs, rhs, name.as_str(), right.span().ok(), Series::equal)?; NuDataFrame::series_to_value(res, operation_span) } - Operator::LessThanOrEqual => { + Operator::Comparison(Comparison::LessThanOrEqual) => { let name = format!("lte_{}_{}", lhs.name(), rhs.name()); let res = compare_series(lhs, rhs, name.as_str(), right.span().ok(), Series::equal)?; NuDataFrame::series_to_value(res, operation_span) } - Operator::GreaterThan => { + Operator::Comparison(Comparison::GreaterThan) => { let name = format!("gt_{}_{}", lhs.name(), rhs.name()); let res = compare_series(lhs, rhs, name.as_str(), right.span().ok(), Series::equal)?; NuDataFrame::series_to_value(res, operation_span) } - Operator::GreaterThanOrEqual => { + Operator::Comparison(Comparison::GreaterThanOrEqual) => { let name = format!("gte_{}_{}", lhs.name(), rhs.name()); let res = compare_series(lhs, rhs, name.as_str(), right.span().ok(), Series::equal)?; NuDataFrame::series_to_value(res, operation_span) } - Operator::And => match lhs.dtype() { + Operator::Boolean(Boolean::And) => match lhs.dtype() { DataType::Boolean => { let lhs_cast = lhs.bool(); let rhs_cast = rhs.bool(); @@ -133,7 +136,7 @@ pub(super) fn compute_between_series( operation_span, )), }, - Operator::Or => match lhs.dtype() { + Operator::Boolean(Boolean::Or) => match lhs.dtype() { DataType::Boolean => { let lhs_cast = lhs.bool(); let rhs_cast = rhs.bool(); @@ -218,7 +221,7 @@ pub(super) fn compute_series_single_value( let lhs = lhs.as_series(lhs_span)?; match operator.item { - Operator::Plus => match &right { + Operator::Math(Math::Plus) => match &right { Value::Int { val, .. } => { compute_series_i64(&lhs, *val, >::add, lhs_span) } @@ -234,7 +237,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::Minus => match &right { + Operator::Math(Math::Minus) => match &right { Value::Int { val, .. } => { compute_series_i64(&lhs, *val, >::sub, lhs_span) } @@ -249,7 +252,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::Multiply => match &right { + Operator::Math(Math::Multiply) => match &right { Value::Int { val, .. } => { compute_series_i64(&lhs, *val, >::mul, lhs_span) } @@ -264,7 +267,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::Divide => match &right { + Operator::Math(Math::Divide) => match &right { Value::Int { val, span } => { if *val == 0 { Err(ShellError::DivisionByZero(*span)) @@ -287,7 +290,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::Equal => match &right { + Operator::Comparison(Comparison::Equal) => match &right { Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::equal, lhs_span), Value::Float { val, .. } => { compare_series_decimal(&lhs, *val, ChunkedArray::equal, lhs_span) @@ -307,7 +310,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::NotEqual => match &right { + Operator::Comparison(Comparison::NotEqual) => match &right { Value::Int { val, .. } => { compare_series_i64(&lhs, *val, ChunkedArray::not_equal, lhs_span) } @@ -328,7 +331,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::LessThan => match &right { + Operator::Comparison(Comparison::LessThan) => match &right { Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::lt, lhs_span), Value::Float { val, .. } => { compare_series_decimal(&lhs, *val, ChunkedArray::lt, lhs_span) @@ -344,7 +347,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::LessThanOrEqual => match &right { + Operator::Comparison(Comparison::LessThanOrEqual) => match &right { Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::lt_eq, lhs_span), Value::Float { val, .. } => { compare_series_decimal(&lhs, *val, ChunkedArray::lt_eq, lhs_span) @@ -360,7 +363,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::GreaterThan => match &right { + Operator::Comparison(Comparison::GreaterThan) => match &right { Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::gt, lhs_span), Value::Float { val, .. } => { compare_series_decimal(&lhs, *val, ChunkedArray::gt, lhs_span) @@ -376,7 +379,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::GreaterThanOrEqual => match &right { + Operator::Comparison(Comparison::GreaterThanOrEqual) => match &right { Value::Int { val, .. } => compare_series_i64(&lhs, *val, ChunkedArray::gt_eq, lhs_span), Value::Float { val, .. } => { compare_series_decimal(&lhs, *val, ChunkedArray::gt_eq, lhs_span) @@ -393,7 +396,7 @@ pub(super) fn compute_series_single_value( }), }, // TODO: update this to do a regex match instead of a simple contains? - Operator::RegexMatch => match &right { + Operator::Comparison(Comparison::RegexMatch) => match &right { Value::String { val, .. } => contains_series_pat(&lhs, val, lhs_span), _ => Err(ShellError::OperatorMismatch { op_span: operator.span, @@ -403,7 +406,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::StartsWith => match &right { + Operator::Comparison(Comparison::StartsWith) => match &right { Value::String { val, .. } => { let starts_with_pattern = format!("^{}", fancy_regex::escape(val)); contains_series_pat(&lhs, &starts_with_pattern, lhs_span) @@ -416,7 +419,7 @@ pub(super) fn compute_series_single_value( rhs_span: right.span()?, }), }, - Operator::EndsWith => match &right { + Operator::Comparison(Comparison::EndsWith) => match &right { Value::String { val, .. } => { let ends_with_pattern = format!("{}$", fancy_regex::escape(val)); contains_series_pat(&lhs, &ends_with_pattern, lhs_span) diff --git a/crates/nu-command/src/dataframe/values/nu_expression/custom_value.rs b/crates/nu-command/src/dataframe/values/nu_expression/custom_value.rs index f856598e41..ee85756303 100644 --- a/crates/nu-command/src/dataframe/values/nu_expression/custom_value.rs +++ b/crates/nu-command/src/dataframe/values/nu_expression/custom_value.rs @@ -1,7 +1,10 @@ use std::ops::{Add, Div, Mul, Rem, Sub}; use super::NuExpression; -use nu_protocol::{ast::Operator, CustomValue, ShellError, Span, Type, Value}; +use nu_protocol::{ + ast::{Comparison, Math, Operator}, + CustomValue, ShellError, Span, Type, Value, +}; use polars::prelude::Expr; // CustomValue implementation for NuDataFrame @@ -95,33 +98,33 @@ fn with_operator( op_span: Span, ) -> Result { match operator { - Operator::Plus => apply_arithmetic(left, right, lhs_span, Add::add), - Operator::Minus => apply_arithmetic(left, right, lhs_span, Sub::sub), - Operator::Multiply => apply_arithmetic(left, right, lhs_span, Mul::mul), - Operator::Divide => apply_arithmetic(left, right, lhs_span, Div::div), - Operator::Modulo => apply_arithmetic(left, right, lhs_span, Rem::rem), - Operator::FloorDivision => apply_arithmetic(left, right, lhs_span, Div::div), - Operator::Equal => Ok(left + Operator::Math(Math::Plus) => apply_arithmetic(left, right, lhs_span, Add::add), + Operator::Math(Math::Minus) => apply_arithmetic(left, right, lhs_span, Sub::sub), + Operator::Math(Math::Multiply) => apply_arithmetic(left, right, lhs_span, Mul::mul), + Operator::Math(Math::Divide) => apply_arithmetic(left, right, lhs_span, Div::div), + Operator::Math(Math::Modulo) => apply_arithmetic(left, right, lhs_span, Rem::rem), + Operator::Math(Math::FloorDivision) => apply_arithmetic(left, right, lhs_span, Div::div), + Operator::Comparison(Comparison::Equal) => Ok(left .clone() .apply_with_expr(right.clone(), Expr::eq) .into_value(lhs_span)), - Operator::NotEqual => Ok(left + Operator::Comparison(Comparison::NotEqual) => Ok(left .clone() .apply_with_expr(right.clone(), Expr::neq) .into_value(lhs_span)), - Operator::GreaterThan => Ok(left + Operator::Comparison(Comparison::GreaterThan) => Ok(left .clone() .apply_with_expr(right.clone(), Expr::gt) .into_value(lhs_span)), - Operator::GreaterThanOrEqual => Ok(left + Operator::Comparison(Comparison::GreaterThanOrEqual) => Ok(left .clone() .apply_with_expr(right.clone(), Expr::gt_eq) .into_value(lhs_span)), - Operator::LessThan => Ok(left + Operator::Comparison(Comparison::LessThan) => Ok(left .clone() .apply_with_expr(right.clone(), Expr::lt) .into_value(lhs_span)), - Operator::LessThanOrEqual => Ok(left + Operator::Comparison(Comparison::LessThanOrEqual) => Ok(left .clone() .apply_with_expr(right.clone(), Expr::lt_eq) .into_value(lhs_span)), diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index b361048210..08fcea8525 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -59,6 +59,7 @@ pub fn create_default_context() -> EngineState { Let, Metadata, Module, + Mut, Use, Version, }; diff --git a/crates/nu-command/src/strings/format/command.rs b/crates/nu-command/src/strings/format/command.rs index 2d88988ff4..fe960e82ac 100644 --- a/crates/nu-command/src/strings/format/command.rs +++ b/crates/nu-command/src/strings/format/command.rs @@ -45,7 +45,7 @@ impl Command for Format { let specified_pattern: Result = call.req(engine_state, stack, 0); let input_val = input.into_value(call.head); // add '$it' variable to support format like this: $it.column1.column2. - let it_id = working_set.add_variable(b"$it".to_vec(), call.head, Type::Any); + let it_id = working_set.add_variable(b"$it".to_vec(), call.head, Type::Any, false); stack.add_var(it_id, input_val.clone()); match specified_pattern { diff --git a/crates/nu-command/tests/commands/let_.rs b/crates/nu-command/tests/commands/let_.rs index 4ee0614ad5..d2db3e5877 100644 --- a/crates/nu-command/tests/commands/let_.rs +++ b/crates/nu-command/tests/commands/let_.rs @@ -13,3 +13,15 @@ fn let_parse_error() { .err .contains("'in' is the name of a builtin Nushell variable")); } + +#[test] +fn let_doesnt_mutate() { + let actual = nu!( + cwd: ".", pipeline( + r#" + let i = 3; $i = 4 + "# + )); + + assert!(actual.err.contains("immutable")); +} diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs index 103c53ab1d..b0aa208d98 100644 --- a/crates/nu-command/tests/commands/mod.rs +++ b/crates/nu-command/tests/commands/mod.rs @@ -42,6 +42,7 @@ mod math; mod merge; mod mkdir; mod move_; +mod mut_; mod n; mod network; mod nu_check; diff --git a/crates/nu-command/tests/commands/mut_.rs b/crates/nu-command/tests/commands/mut_.rs new file mode 100644 index 0000000000..b6a72e7376 --- /dev/null +++ b/crates/nu-command/tests/commands/mut_.rs @@ -0,0 +1,49 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn mut_variable() { + let actual = nu!( + cwd: ".", pipeline( + r#" + mut x = 3; $x = $x + 1; $x + "# + )); + + assert_eq!(actual.out, "4"); +} + +#[test] +fn mut_variable_in_loop() { + let actual = nu!( + cwd: ".", pipeline( + r#" + mut x = 1; for i in 1..10 { $x = $x + $i}; $x + "# + )); + + assert_eq!(actual.out, "56"); +} + +#[test] +fn capture_of_mutable_var() { + let actual = nu!( + cwd: ".", pipeline( + r#" + mut x = 123; {|| $x } + "# + )); + + assert!(actual.err.contains("capture of mutable variable")); +} + +#[test] +fn mut_a_field() { + let actual = nu!( + cwd: ".", pipeline( + r#" + mut y = {abc: 123}; $y.abc = 456; $y.abc + "# + )); + + assert_eq!(actual.out, "456"); +} diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index b8e54ac158..2260f17fa3 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -1,7 +1,7 @@ use crate::{current_dir_str, get_full_help}; use nu_path::expand_path_with; use nu_protocol::{ - ast::{Block, Call, Expr, Expression, Operator}, + ast::{Assignment, Bits, Block, Boolean, Call, Comparison, Expr, Expression, Math, Operator}, engine::{EngineState, Stack, Visibility}, Config, HistoryFileFormat, IntoInterruptiblePipelineData, IntoPipelineData, ListStream, PipelineData, Range, RawStream, ShellError, Span, Spanned, SyntaxShape, Unit, Value, VarId, @@ -355,131 +355,112 @@ pub fn eval_expression( } Expr::BinaryOp(lhs, op, rhs) => { let op_span = op.span; - let lhs = eval_expression(engine_state, stack, lhs)?; let op = eval_operator(op)?; match op { - Operator::And => { - if lhs.is_false() { - Ok(Value::Bool { - val: false, - span: expr.span, - }) - } else { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.and(op_span, &rhs, expr.span) + Operator::Boolean(boolean) => { + let lhs = eval_expression(engine_state, stack, lhs)?; + match boolean { + Boolean::And => { + if lhs.is_false() { + Ok(Value::Bool { + val: false, + span: 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 { + val: true, + span: expr.span, + }) + } else { + let rhs = eval_expression(engine_state, stack, rhs)?; + lhs.or(op_span, &rhs, expr.span) + } + } } } - 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, 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::Plus => { + Operator::Comparison(comparison) => { + let lhs = eval_expression(engine_state, stack, lhs)?; let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.add(op_span, &rhs, expr.span) + 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(op_span, &rhs, false, expr.span), + Comparison::NotRegexMatch => { + lhs.regex_match(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::Append => { + Operator::Bits(bits) => { + let lhs = eval_expression(engine_state, stack, lhs)?; let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.append(op_span, &rhs, expr.span) + 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::Minus => { + Operator::Assignment(Assignment::Assign) => { let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.sub(op_span, &rhs, expr.span) - } - Operator::Multiply => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.mul(op_span, &rhs, expr.span) - } - Operator::Divide => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.div(op_span, &rhs, expr.span) - } - Operator::LessThan => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.lt(op_span, &rhs, expr.span) - } - Operator::LessThanOrEqual => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.lte(op_span, &rhs, expr.span) - } - Operator::GreaterThan => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.gt(op_span, &rhs, expr.span) - } - Operator::GreaterThanOrEqual => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.gte(op_span, &rhs, expr.span) - } - Operator::Equal => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.eq(op_span, &rhs, expr.span) - } - Operator::NotEqual => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.ne(op_span, &rhs, expr.span) - } - Operator::In => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.r#in(op_span, &rhs, expr.span) - } - Operator::NotIn => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.not_in(op_span, &rhs, expr.span) - } - Operator::RegexMatch => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.regex_match(op_span, &rhs, false, expr.span) - } - Operator::NotRegexMatch => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.regex_match(op_span, &rhs, true, expr.span) - } - Operator::Modulo => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.modulo(op_span, &rhs, expr.span) - } - Operator::FloorDivision => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.floor_div(op_span, &rhs, expr.span) - } - Operator::Pow => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.pow(op_span, &rhs, expr.span) - } - Operator::StartsWith => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.starts_with(op_span, &rhs, expr.span) - } - Operator::EndsWith => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.ends_with(op_span, &rhs, expr.span) - } - Operator::BitOr => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.bit_or(op_span, &rhs, expr.span) - } - Operator::BitXor => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.bit_xor(op_span, &rhs, expr.span) - } - Operator::BitAnd => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.bit_and(op_span, &rhs, expr.span) - } - Operator::ShiftRight => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.bit_shr(op_span, &rhs, expr.span) - } - Operator::ShiftLeft => { - let rhs = eval_expression(engine_state, stack, rhs)?; - lhs.bit_shl(op_span, &rhs, expr.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.vars.insert(*var_id, rhs); + Ok(Value::nothing(lhs.span)) + } else { + Err(ShellError::AssignmentRequiresMutableVar(lhs.span)) + } + } + Expr::FullCellPath(cell_path) => match &cell_path.head.expr { + Expr::Var(var_id) | Expr::VarDecl(var_id) => { + let var_info = engine_state.get_var(*var_id); + if var_info.mutable { + let mut lhs = + eval_expression(engine_state, stack, &cell_path.head)?; + lhs.update_data_at_cell_path(&cell_path.tail, rhs)?; + stack.vars.insert(*var_id, lhs); + Ok(Value::nothing(cell_path.head.span)) + } else { + Err(ShellError::AssignmentRequiresMutableVar(lhs.span)) + } + } + _ => Err(ShellError::AssignmentRequiresVar(lhs.span)), + }, + _ => Err(ShellError::AssignmentRequiresVar(lhs.span)), + } } } } diff --git a/crates/nu-parser/src/errors.rs b/crates/nu-parser/src/errors.rs index 5002b690b4..9abe911625 100644 --- a/crates/nu-parser/src/errors.rs +++ b/crates/nu-parser/src/errors.rs @@ -56,6 +56,10 @@ pub enum ParseError { Type, ), + #[error("Capture of mutable variable.")] + #[diagnostic(code(nu::parser::expected_keyword), url(docsrs))] + CaptureOfMutableVar(#[label("capture of mutable variable")] Span), + #[error("Expected keyword.")] #[diagnostic(code(nu::parser::expected_keyword), url(docsrs))] ExpectedKeyword(String, #[label("expected {0}")] Span), @@ -88,6 +92,16 @@ pub enum ParseError { )] LetInPipeline(String, String, #[label("let in pipeline")] Span), + #[error("Mut statement used in pipeline.")] + #[diagnostic( + code(nu::parser::unexpected_keyword), + url(docsrs), + help( + "Assigning '{0}' to '{1}' does not produce a value to be piped. If the pipeline result is meant to be assigned to '{1}', use 'mut {1} = ({0} | ...)'." + ) + )] + MutInPipeline(String, String, #[label("let in pipeline")] Span), + #[error("Let used with builtin variable name.")] #[diagnostic( code(nu::parser::let_builtin_var), @@ -96,6 +110,14 @@ pub enum ParseError { )] LetBuiltinVar(String, #[label("already a builtin variable")] Span), + #[error("Mut used with builtin variable name.")] + #[diagnostic( + code(nu::parser::let_builtin_var), + url(docsrs), + help("'{0}' is the name of a builtin Nushell variable. `mut` cannot assign to it.") + )] + MutBuiltinVar(String, #[label("already a builtin variable")] Span), + #[error("Incorrect value")] #[diagnostic(code(nu::parser::incorrect_value), url(docsrs), help("{2}"))] IncorrectValue(String, #[label("unexpected {0}")] Span, String), @@ -343,7 +365,10 @@ impl ParseError { ParseError::UnexpectedKeyword(_, s) => *s, ParseError::BuiltinCommandInPipeline(_, s) => *s, ParseError::LetInPipeline(_, _, s) => *s, + ParseError::MutInPipeline(_, _, s) => *s, ParseError::LetBuiltinVar(_, s) => *s, + ParseError::MutBuiltinVar(_, s) => *s, + ParseError::CaptureOfMutableVar(s) => *s, ParseError::IncorrectValue(_, s, _) => *s, ParseError::MultipleRestParams(s) => *s, ParseError::VariableNotFound(s) => *s, diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 57d31ebf19..65b6e4e8af 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -2781,8 +2781,12 @@ pub fn parse_let( } let mut idx = 0; - let (lvalue, err) = - parse_var_with_opt_type(working_set, &spans[1..(span.0)], &mut idx); + let (lvalue, err) = parse_var_with_opt_type( + working_set, + &spans[1..(span.0)], + &mut idx, + false, + ); error = error.or(err); let var_name = @@ -2858,6 +2862,129 @@ pub fn parse_let( ) } +pub fn parse_mut( + working_set: &mut StateWorkingSet, + spans: &[Span], + expand_aliases_denylist: &[usize], +) -> (Pipeline, Option) { + let name = working_set.get_span_contents(spans[0]); + + if name == b"mut" { + if let Some((span, err)) = check_name(working_set, spans) { + return (Pipeline::from_vec(vec![garbage(*span)]), Some(err)); + } + + if let Some(decl_id) = working_set.find_decl(b"mut", &Type::Any) { + let cmd = working_set.get_decl(decl_id); + let call_signature = cmd.signature().call_signature(); + + if spans.len() >= 4 { + // This is a bit of by-hand parsing to get around the issue where we want to parse in the reverse order + // so that the var-id created by the variable isn't visible in the expression that init it + for span in spans.iter().enumerate() { + let item = working_set.get_span_contents(*span.1); + if item == b"=" && spans.len() > (span.0 + 1) { + let mut error = None; + + let mut idx = span.0; + let (rvalue, err) = parse_multispan_value( + working_set, + spans, + &mut idx, + &SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)), + expand_aliases_denylist, + ); + error = error.or(err); + + if idx < (spans.len() - 1) { + error = error.or(Some(ParseError::ExtraPositional( + call_signature, + spans[idx + 1], + ))); + } + + let mut idx = 0; + let (lvalue, err) = parse_var_with_opt_type( + working_set, + &spans[1..(span.0)], + &mut idx, + true, + ); + error = error.or(err); + + let var_name = + String::from_utf8_lossy(working_set.get_span_contents(lvalue.span)) + .to_string(); + + if ["in", "nu", "env", "nothing"].contains(&var_name.as_str()) { + error = + error.or(Some(ParseError::MutBuiltinVar(var_name, lvalue.span))); + } + + let var_id = lvalue.as_var(); + let rhs_type = rvalue.ty.clone(); + + if let Some(var_id) = var_id { + working_set.set_variable_type(var_id, rhs_type); + } + + let call = Box::new(Call { + decl_id, + head: spans[0], + arguments: vec![ + Argument::Positional(lvalue), + Argument::Positional(rvalue), + ], + redirect_stdout: true, + redirect_stderr: false, + }); + + return ( + Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: nu_protocol::span(spans), + ty: Type::Any, + custom_completion: None, + }]), + error, + ); + } + } + } + let ParsedInternalCall { + call, + error: err, + output, + } = parse_internal_call( + working_set, + spans[0], + &spans[1..], + decl_id, + expand_aliases_denylist, + ); + + return ( + Pipeline { + expressions: vec![Expression { + expr: Expr::Call(call), + span: nu_protocol::span(spans), + ty: output, + custom_completion: None, + }], + }, + err, + ); + } + } + ( + garbage_pipeline(spans), + Some(ParseError::UnknownState( + "internal error: mut statement unparseable".into(), + span(spans), + )), + ) +} + pub fn parse_source( working_set: &mut StateWorkingSet, spans: &[Span], diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index c67b07e017..7f5ef20aaa 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -1,15 +1,16 @@ use crate::{ lex, lite_parse, lite_parse::LiteCommand, + parse_mut, type_check::{math_result_type, type_compatible}, LiteBlock, ParseError, Token, TokenContents, }; use nu_protocol::{ ast::{ - Argument, Block, Call, CellPath, Expr, Expression, FullCellPath, ImportPattern, - ImportPatternHead, ImportPatternMember, Operator, PathMember, Pipeline, RangeInclusion, - RangeOperator, + Argument, Assignment, Bits, Block, Boolean, Call, CellPath, Comparison, Expr, Expression, + FullCellPath, ImportPattern, ImportPatternHead, ImportPatternMember, Math, Operator, + PathMember, Pipeline, RangeInclusion, RangeOperator, }, engine::StateWorkingSet, span, BlockId, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId, @@ -633,7 +634,7 @@ pub fn parse_multispan_value( SyntaxShape::VarWithOptType => { trace!("parsing: var with opt type"); - let (arg, err) = parse_var_with_opt_type(working_set, spans, spans_idx); + let (arg, err) = parse_var_with_opt_type(working_set, spans, spans_idx, false); error = error.or(err); (arg, error) @@ -2919,6 +2920,7 @@ pub fn parse_var_with_opt_type( working_set: &mut StateWorkingSet, spans: &[Span], spans_idx: &mut usize, + mutable: bool, ) -> (Expression, Option) { let bytes = working_set.get_span_contents(spans[*spans_idx]).to_vec(); @@ -2953,7 +2955,7 @@ pub fn parse_var_with_opt_type( ); } - let id = working_set.add_variable(var_name, spans[*spans_idx - 1], ty.clone()); + let id = working_set.add_variable(var_name, spans[*spans_idx - 1], ty.clone(), mutable); ( Expression { @@ -2977,7 +2979,7 @@ pub fn parse_var_with_opt_type( ); } - let id = working_set.add_variable(var_name, spans[*spans_idx], Type::Any); + let id = working_set.add_variable(var_name, spans[*spans_idx], Type::Any, mutable); ( Expression { expr: Expr::VarDecl(id), @@ -3005,6 +3007,7 @@ pub fn parse_var_with_opt_type( var_name, span(&spans[*spans_idx..*spans_idx + 1]), Type::Any, + mutable, ); ( @@ -3044,7 +3047,7 @@ pub fn parse_row_condition( spans: &[Span], expand_aliases_denylist: &[usize], ) -> (Expression, Option) { - let var_id = working_set.add_variable(b"$it".to_vec(), span(spans), Type::Any); + let var_id = working_set.add_variable(b"$it".to_vec(), span(spans), Type::Any, false); let (expression, err) = parse_math_expression(working_set, spans, Some(var_id), expand_aliases_denylist); let span = span(spans); @@ -3232,7 +3235,7 @@ pub fn parse_signature_helper( } let var_id = - working_set.add_variable(variable_name, span, Type::Any); + working_set.add_variable(variable_name, span, Type::Any, false); if flags.len() == 1 { args.push(Arg::Flag(Flag { @@ -3282,8 +3285,12 @@ pub fn parse_signature_helper( }) } - let var_id = - working_set.add_variable(variable_name, span, Type::Any); + let var_id = working_set.add_variable( + variable_name, + span, + Type::Any, + false, + ); if chars.len() == 1 { args.push(Arg::Flag(Flag { @@ -3327,7 +3334,7 @@ pub fn parse_signature_helper( } let var_id = - working_set.add_variable(variable_name, span, Type::Any); + working_set.add_variable(variable_name, span, Type::Any, false); args.push(Arg::Flag(Flag { arg: None, @@ -3394,7 +3401,8 @@ pub fn parse_signature_helper( }) } - let var_id = working_set.add_variable(contents, span, Type::Any); + let var_id = + working_set.add_variable(contents, span, Type::Any, false); // Positional arg, optional args.push(Arg::Positional( @@ -3420,7 +3428,7 @@ pub fn parse_signature_helper( } let var_id = - working_set.add_variable(contents_vec, span, Type::Any); + working_set.add_variable(contents_vec, span, Type::Any, false); args.push(Arg::RestPositional(PositionalArg { desc: String::new(), @@ -3443,7 +3451,7 @@ pub fn parse_signature_helper( } let var_id = - working_set.add_variable(contents_vec, span, Type::Any); + working_set.add_variable(contents_vec, span, Type::Any, false); // Positional arg, required args.push(Arg::Positional( @@ -4411,33 +4419,34 @@ pub fn parse_operator( let contents = working_set.get_span_contents(span); let operator = match contents { - b"==" => Operator::Equal, - b"!=" => Operator::NotEqual, - b"<" => Operator::LessThan, - b"<=" => Operator::LessThanOrEqual, - b">" => Operator::GreaterThan, - b">=" => Operator::GreaterThanOrEqual, - b"=~" => Operator::RegexMatch, - b"!~" => Operator::NotRegexMatch, - b"+" => Operator::Plus, - b"++" => Operator::Append, - b"-" => Operator::Minus, - b"*" => Operator::Multiply, - b"/" => Operator::Divide, - b"//" => Operator::FloorDivision, - b"in" => Operator::In, - b"not-in" => Operator::NotIn, - b"mod" => Operator::Modulo, - b"bit-or" => Operator::BitOr, - b"bit-xor" => Operator::BitXor, - b"bit-and" => Operator::BitAnd, - b"bit-shl" => Operator::ShiftLeft, - b"bit-shr" => Operator::ShiftRight, - b"starts-with" => Operator::StartsWith, - b"ends-with" => Operator::EndsWith, - b"&&" | b"and" => Operator::And, - b"||" | b"or" => Operator::Or, - b"**" => Operator::Pow, + b"=" => Operator::Assignment(Assignment::Assign), + b"==" => Operator::Comparison(Comparison::Equal), + b"!=" => Operator::Comparison(Comparison::NotEqual), + b"<" => Operator::Comparison(Comparison::LessThan), + b"<=" => Operator::Comparison(Comparison::LessThanOrEqual), + b">" => Operator::Comparison(Comparison::GreaterThan), + b">=" => Operator::Comparison(Comparison::GreaterThanOrEqual), + b"=~" => Operator::Comparison(Comparison::RegexMatch), + b"!~" => Operator::Comparison(Comparison::NotRegexMatch), + b"+" => Operator::Math(Math::Plus), + b"++" => Operator::Math(Math::Append), + b"-" => Operator::Math(Math::Minus), + b"*" => Operator::Math(Math::Multiply), + b"/" => Operator::Math(Math::Divide), + b"//" => Operator::Math(Math::FloorDivision), + b"in" => Operator::Comparison(Comparison::In), + b"not-in" => Operator::Comparison(Comparison::NotIn), + b"mod" => Operator::Math(Math::Modulo), + b"bit-or" => Operator::Bits(Bits::BitOr), + b"bit-xor" => Operator::Bits(Bits::BitXor), + b"bit-and" => Operator::Bits(Bits::BitAnd), + b"bit-shl" => Operator::Bits(Bits::ShiftLeft), + b"bit-shr" => Operator::Bits(Bits::ShiftRight), + b"starts-with" => Operator::Comparison(Comparison::StartsWith), + b"ends-with" => Operator::Comparison(Comparison::EndsWith), + b"&&" | b"and" => Operator::Boolean(Boolean::And), + b"||" | b"or" => Operator::Boolean(Boolean::Or), + b"**" => Operator::Math(Math::Pow), _ => { return ( garbage(span), @@ -4764,6 +4773,28 @@ pub fn parse_expression( spans[0], )), ), + b"mut" => ( + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, + Some(ParseError::MutInPipeline( + String::from_utf8_lossy(match spans.len() { + 1 | 2 | 3 => b"value", + _ => working_set.get_span_contents(spans[3]), + }) + .to_string(), + String::from_utf8_lossy(match spans.len() { + 1 => b"variable", + _ => working_set.get_span_contents(spans[1]), + }) + .to_string(), + spans[0], + )), + ), b"alias" => ( parse_call( working_set, @@ -4977,6 +5008,7 @@ pub fn parse_builtin_commands( b"def" | b"def-env" => parse_def(working_set, lite_command, expand_aliases_denylist), b"extern" => parse_extern(working_set, lite_command, expand_aliases_denylist), b"let" => parse_let(working_set, &lite_command.parts, expand_aliases_denylist), + b"mut" => parse_mut(working_set, &lite_command.parts, expand_aliases_denylist), b"for" => { let (expr, err) = parse_for(working_set, &lite_command.parts, expand_aliases_denylist); (Pipeline::from_vec(vec![expr]), err) @@ -5229,8 +5261,8 @@ pub fn discover_captures_in_closure( working_set: &StateWorkingSet, block: &Block, seen: &mut Vec, - seen_blocks: &mut HashMap>, -) -> Vec { + seen_blocks: &mut HashMap>, +) -> Result, ParseError> { let mut output = vec![]; for flag in &block.signature.named { @@ -5256,57 +5288,71 @@ pub fn discover_captures_in_closure( } for pipeline in &block.pipelines { - let result = discover_captures_in_pipeline(working_set, pipeline, seen, seen_blocks); + let result = discover_captures_in_pipeline(working_set, pipeline, seen, seen_blocks)?; output.extend(&result); } - output + Ok(output) } fn discover_captures_in_pipeline( working_set: &StateWorkingSet, pipeline: &Pipeline, seen: &mut Vec, - seen_blocks: &mut HashMap>, -) -> Vec { + seen_blocks: &mut HashMap>, +) -> Result, ParseError> { let mut output = vec![]; for expr in &pipeline.expressions { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } - output + Ok(output) } +// Closes over captured variables pub fn discover_captures_in_expr( working_set: &StateWorkingSet, expr: &Expression, seen: &mut Vec, - seen_blocks: &mut HashMap>, -) -> Vec { - let mut output = vec![]; + seen_blocks: &mut HashMap>, +) -> Result, ParseError> { + let mut output: Vec<(VarId, Span)> = vec![]; match &expr.expr { Expr::BinaryOp(lhs, _, rhs) => { - let lhs_result = discover_captures_in_expr(working_set, lhs, seen, seen_blocks); - let rhs_result = discover_captures_in_expr(working_set, rhs, seen, seen_blocks); + let lhs_result = discover_captures_in_expr(working_set, lhs, seen, seen_blocks)?; + let rhs_result = discover_captures_in_expr(working_set, rhs, seen, seen_blocks)?; output.extend(&lhs_result); output.extend(&rhs_result); } Expr::UnaryNot(expr) => { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } Expr::Closure(block_id) => { let block = working_set.get_block(*block_id); let results = { let mut seen = vec![]; - discover_captures_in_closure(working_set, block, &mut seen, seen_blocks) + let results = + discover_captures_in_closure(working_set, block, &mut seen, seen_blocks)?; + + for (var_id, span) in results.iter() { + if !seen.contains(var_id) { + if let Some(variable) = working_set.get_variable_if_possible(*var_id) { + if variable.mutable { + return Err(ParseError::CaptureOfMutableVar(*span)); + } + } + } + } + + results }; seen_blocks.insert(*block_id, results.clone()); - for var_id in results.into_iter() { + for (var_id, span) in results.into_iter() { if !seen.contains(&var_id) { - output.push(var_id) + output.push((var_id, span)) } } } @@ -5315,12 +5361,12 @@ pub fn discover_captures_in_expr( // FIXME: is this correct? let results = { let mut seen = vec![]; - discover_captures_in_closure(working_set, block, &mut seen, seen_blocks) + discover_captures_in_closure(working_set, block, &mut seen, seen_blocks)? }; seen_blocks.insert(*block_id, results.clone()); - for var_id in results.into_iter() { + for (var_id, span) in results.into_iter() { if !seen.contains(&var_id) { - output.push(var_id) + output.push((var_id, span)) } } } @@ -5336,7 +5382,7 @@ pub fn discover_captures_in_expr( None => { let block = working_set.get_block(block_id); if !block.captures.is_empty() { - output.extend(&block.captures); + output.extend(block.captures.iter().map(|var_id| (*var_id, call.head))); } else { let mut seen = vec![]; seen_blocks.insert(block_id, output.clone()); @@ -5346,7 +5392,7 @@ pub fn discover_captures_in_expr( block, &mut seen, seen_blocks, - ); + )?; output.extend(&result); seen_blocks.insert(block_id, result); } @@ -5356,24 +5402,24 @@ pub fn discover_captures_in_expr( for named in call.named_iter() { if let Some(arg) = &named.2 { - let result = discover_captures_in_expr(working_set, arg, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, arg, seen, seen_blocks)?; output.extend(&result); } } for positional in call.positional_iter() { - let result = discover_captures_in_expr(working_set, positional, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, positional, seen, seen_blocks)?; output.extend(&result); } } Expr::CellPath(_) => {} Expr::DateTime(_) => {} Expr::ExternalCall(head, exprs) => { - let result = discover_captures_in_expr(working_set, head, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, head, seen, seen_blocks)?; output.extend(&result); for expr in exprs { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } } @@ -5381,7 +5427,8 @@ pub fn discover_captures_in_expr( Expr::Directory(_) => {} Expr::Float(_) => {} Expr::FullCellPath(cell_path) => { - let result = discover_captures_in_expr(working_set, &cell_path.head, seen, seen_blocks); + let result = + discover_captures_in_expr(working_set, &cell_path.head, seen, seen_blocks)?; output.extend(&result); } Expr::ImportPattern(_) => {} @@ -5391,27 +5438,27 @@ pub fn discover_captures_in_expr( Expr::GlobPattern(_) => {} Expr::Int(_) => {} Expr::Keyword(_, _, expr) => { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } Expr::List(exprs) => { for expr in exprs { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } } Expr::Operator(_) => {} Expr::Range(expr1, expr2, expr3, _) => { if let Some(expr) = expr1 { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } if let Some(expr) = expr2 { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } if let Some(expr) = expr3 { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } } @@ -5422,13 +5469,13 @@ pub fn discover_captures_in_expr( field_name, seen, seen_blocks, - )); + )?); output.extend(&discover_captures_in_expr( working_set, field_value, seen, seen_blocks, - )); + )?); } } Expr::Signature(sig) => { @@ -5457,7 +5504,7 @@ pub fn discover_captures_in_expr( Expr::String(_) => {} Expr::StringInterpolation(exprs) => { for expr in exprs { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } } @@ -5465,41 +5512,41 @@ pub fn discover_captures_in_expr( let block = working_set.get_block(*block_id); let results = { let mut seen = vec![]; - discover_captures_in_closure(working_set, block, &mut seen, seen_blocks) + discover_captures_in_closure(working_set, block, &mut seen, seen_blocks)? }; seen_blocks.insert(*block_id, results.clone()); - for var_id in results.into_iter() { + for (var_id, span) in results.into_iter() { if !seen.contains(&var_id) { - output.push(var_id) + output.push((var_id, span)) } } } Expr::Table(headers, values) => { for header in headers { - let result = discover_captures_in_expr(working_set, header, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, header, seen, seen_blocks)?; output.extend(&result); } for row in values { for cell in row { - let result = discover_captures_in_expr(working_set, cell, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, cell, seen, seen_blocks)?; output.extend(&result); } } } Expr::ValueWithUnit(expr, _) => { - let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks); + let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?; output.extend(&result); } Expr::Var(var_id) => { if (*var_id > ENV_VARIABLE_ID || *var_id == IN_VARIABLE_ID) && !seen.contains(var_id) { - output.push(*var_id); + output.push((*var_id, expr.span)); } } Expr::VarDecl(var_id) => { seen.push(*var_id); } } - output + Ok(output) } fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression) -> Expression { @@ -5604,7 +5651,10 @@ pub fn parse( let mut seen_blocks = HashMap::new(); let captures = discover_captures_in_closure(working_set, &output, &mut seen, &mut seen_blocks); - output.captures = captures; + match captures { + Ok(captures) => output.captures = captures.into_iter().map(|(var_id, _)| var_id).collect(), + Err(err) => error = Some(err), + } // Also check other blocks that might have been imported for (block_idx, block) in working_set.delta.blocks.iter().enumerate() { @@ -5613,7 +5663,12 @@ pub fn parse( if !seen_blocks.contains_key(&block_id) { let captures = discover_captures_in_closure(working_set, block, &mut seen, &mut seen_blocks); - seen_blocks.insert(block_id, captures); + match captures { + Ok(captures) => { + seen_blocks.insert(block_id, captures); + } + Err(err) => error = Some(err), + } } } @@ -5626,7 +5681,7 @@ pub fn parse( let block_captures_empty = block.captures.is_empty(); if !captures.is_empty() && block_captures_empty { let block = working_set.get_block_mut(block_id); - block.captures = captures; + block.captures = captures.into_iter().map(|(var_id, _)| var_id).collect(); } } diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index c8cdd7e27f..0daf0fa93c 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -1,6 +1,6 @@ use crate::ParseError; use nu_protocol::{ - ast::{Expr, Expression, Operator}, + ast::{Assignment, Bits, Boolean, Comparison, Expr, Expression, Math, Operator}, engine::StateWorkingSet, Type, }; @@ -26,7 +26,7 @@ pub fn math_result_type( //println!("checking: {:?} {:?} {:?}", lhs, op, rhs); match &op.expr { Expr::Operator(operator) => match operator { - Operator::Plus => match (&lhs.ty, &rhs.ty) { + Operator::Math(Math::Plus) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Int, None), (Type::Float, Type::Int) => (Type::Float, None), (Type::Int, Type::Float) => (Type::Float, None), @@ -69,7 +69,7 @@ pub fn math_result_type( ) } }, - Operator::Append => match (&lhs.ty, &rhs.ty) { + Operator::Math(Math::Append) => match (&lhs.ty, &rhs.ty) { (Type::List(a), Type::List(b)) => { if a == b { (Type::List(a.clone()), None) @@ -98,7 +98,7 @@ pub fn math_result_type( ) } }, - Operator::Minus => match (&lhs.ty, &rhs.ty) { + Operator::Math(Math::Minus) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Int, None), (Type::Float, Type::Int) => (Type::Float, None), (Type::Int, Type::Float) => (Type::Float, None), @@ -126,7 +126,7 @@ pub fn math_result_type( ) } }, - Operator::Multiply => match (&lhs.ty, &rhs.ty) { + Operator::Math(Math::Multiply) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Int, None), (Type::Float, Type::Int) => (Type::Float, None), (Type::Int, Type::Float) => (Type::Float, None), @@ -159,7 +159,7 @@ pub fn math_result_type( ) } }, - Operator::Pow => match (&lhs.ty, &rhs.ty) { + Operator::Math(Math::Pow) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Int, None), (Type::Float, Type::Int) => (Type::Float, None), (Type::Int, Type::Float) => (Type::Float, None), @@ -184,7 +184,8 @@ pub fn math_result_type( ) } }, - Operator::Divide | Operator::Modulo => match (&lhs.ty, &rhs.ty) { + Operator::Math(Math::Divide) | Operator::Math(Math::Modulo) => match (&lhs.ty, &rhs.ty) + { (Type::Int, Type::Int) => (Type::Int, None), (Type::Float, Type::Int) => (Type::Float, None), (Type::Int, Type::Float) => (Type::Float, None), @@ -215,7 +216,7 @@ pub fn math_result_type( ) } }, - Operator::FloorDivision => match (&lhs.ty, &rhs.ty) { + Operator::Math(Math::FloorDivision) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Int, None), (Type::Float, Type::Int) => (Type::Int, None), (Type::Int, Type::Float) => (Type::Int, None), @@ -243,33 +244,37 @@ pub fn math_result_type( ) } }, - Operator::And | Operator::Or => match (&lhs.ty, &rhs.ty) { - (Type::Bool, Type::Bool) => (Type::Bool, None), + Operator::Boolean(Boolean::And) | Operator::Boolean(Boolean::Or) => { + match (&lhs.ty, &rhs.ty) { + (Type::Bool, Type::Bool) => (Type::Bool, None), - (Type::Custom(a), Type::Custom(b)) if a == b => (Type::Custom(a.to_string()), None), - (Type::Custom(a), _) => (Type::Custom(a.to_string()), None), + (Type::Custom(a), Type::Custom(b)) if a == b => { + (Type::Custom(a.to_string()), None) + } + (Type::Custom(a), _) => (Type::Custom(a.to_string()), None), - (Type::Any, _) => (Type::Any, None), - (_, Type::Any) => (Type::Any, None), + (Type::Any, _) => (Type::Any, None), + (_, Type::Any) => (Type::Any, None), - // FIX ME. This is added because there is no type output for custom function - // definitions. As soon as that syntax is added this should be removed - (a, b) if a == b => (Type::Bool, None), - _ => { - *op = Expression::garbage(op.span); - ( - Type::Any, - Some(ParseError::UnsupportedOperation( - op.span, - lhs.span, - lhs.ty.clone(), - rhs.span, - rhs.ty.clone(), - )), - ) + // FIX ME. This is added because there is no type output for custom function + // definitions. As soon as that syntax is added this should be removed + (a, b) if a == b => (Type::Bool, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Any, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } } - }, - Operator::LessThan => match (&lhs.ty, &rhs.ty) { + } + Operator::Comparison(Comparison::LessThan) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Bool, None), (Type::Float, Type::Int) => (Type::Bool, None), (Type::Int, Type::Float) => (Type::Bool, None), @@ -296,7 +301,7 @@ pub fn math_result_type( ) } }, - Operator::LessThanOrEqual => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::LessThanOrEqual) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Bool, None), (Type::Float, Type::Int) => (Type::Bool, None), (Type::Int, Type::Float) => (Type::Bool, None), @@ -323,7 +328,7 @@ pub fn math_result_type( ) } }, - Operator::GreaterThan => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::GreaterThan) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Bool, None), (Type::Float, Type::Int) => (Type::Bool, None), (Type::Int, Type::Float) => (Type::Bool, None), @@ -350,7 +355,7 @@ pub fn math_result_type( ) } }, - Operator::GreaterThanOrEqual => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::GreaterThanOrEqual) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Bool, None), (Type::Float, Type::Int) => (Type::Bool, None), (Type::Int, Type::Float) => (Type::Bool, None), @@ -377,19 +382,19 @@ pub fn math_result_type( ) } }, - Operator::Equal => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::Equal) => match (&lhs.ty, &rhs.ty) { (Type::Custom(a), Type::Custom(b)) if a == b => (Type::Custom(a.to_string()), None), (Type::Custom(a), _) => (Type::Custom(a.to_string()), None), _ => (Type::Bool, None), }, - Operator::NotEqual => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::NotEqual) => match (&lhs.ty, &rhs.ty) { (Type::Custom(a), Type::Custom(b)) if a == b => (Type::Custom(a.to_string()), None), (Type::Custom(a), _) => (Type::Custom(a.to_string()), None), _ => (Type::Bool, None), }, - Operator::RegexMatch => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::RegexMatch) => match (&lhs.ty, &rhs.ty) { (Type::String, Type::String) => (Type::Bool, None), (Type::Any, _) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None), @@ -411,7 +416,7 @@ pub fn math_result_type( ) } }, - Operator::NotRegexMatch => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::NotRegexMatch) => match (&lhs.ty, &rhs.ty) { (Type::String, Type::String) => (Type::Bool, None), (Type::Any, _) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None), @@ -433,7 +438,7 @@ pub fn math_result_type( ) } }, - Operator::StartsWith => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::StartsWith) => match (&lhs.ty, &rhs.ty) { (Type::String, Type::String) => (Type::Bool, None), (Type::Any, _) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None), @@ -455,7 +460,7 @@ pub fn math_result_type( ) } }, - Operator::EndsWith => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::EndsWith) => match (&lhs.ty, &rhs.ty) { (Type::String, Type::String) => (Type::Bool, None), (Type::Any, _) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None), @@ -477,7 +482,7 @@ pub fn math_result_type( ) } }, - Operator::In => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::In) => match (&lhs.ty, &rhs.ty) { (t, Type::List(u)) if type_compatible(t, u) => (Type::Bool, None), (Type::Int | Type::Float, Type::Range) => (Type::Bool, None), (Type::String, Type::String) => (Type::Bool, None), @@ -502,7 +507,7 @@ pub fn math_result_type( ) } }, - Operator::NotIn => match (&lhs.ty, &rhs.ty) { + Operator::Comparison(Comparison::NotIn) => match (&lhs.ty, &rhs.ty) { (t, Type::List(u)) if type_compatible(t, u) => (Type::Bool, None), (Type::Int | Type::Float, Type::Range) => (Type::Bool, None), (Type::String, Type::String) => (Type::Bool, None), @@ -527,11 +532,11 @@ pub fn math_result_type( ) } }, - Operator::ShiftLeft - | Operator::ShiftRight - | Operator::BitOr - | Operator::BitXor - | Operator::BitAnd => match (&lhs.ty, &rhs.ty) { + Operator::Bits(Bits::ShiftLeft) + | Operator::Bits(Bits::ShiftRight) + | Operator::Bits(Bits::BitOr) + | Operator::Bits(Bits::BitXor) + | Operator::Bits(Bits::BitAnd) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Int, None), (Type::Any, _) => (Type::Any, None), @@ -550,6 +555,15 @@ pub fn math_result_type( ) } }, + Operator::Assignment(Assignment::Assign) => match (&lhs.ty, &rhs.ty) { + (x, y) if x == y => (Type::Nothing, None), + (Type::Any, _) => (Type::Nothing, None), + (_, Type::Any) => (Type::Nothing, None), + (x, y) => ( + Type::Nothing, + Some(ParseError::Mismatch(x.to_string(), y.to_string(), rhs.span)), + ), + }, }, _ => { *op = Expression::garbage(op.span); diff --git a/crates/nu-protocol/src/ast/expression.rs b/crates/nu-protocol/src/ast/expression.rs index f783972910..5e30d3a17b 100644 --- a/crates/nu-protocol/src/ast/expression.rs +++ b/crates/nu-protocol/src/ast/expression.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use super::{Expr, Operator}; +use super::Expr; use crate::ast::ImportPattern; use crate::DeclId; use crate::{engine::StateWorkingSet, BlockId, Signature, Span, Type, VarId, IN_VARIABLE_ID}; @@ -26,34 +26,36 @@ impl Expression { pub fn precedence(&self) -> usize { match &self.expr { Expr::Operator(operator) => { + use super::operator::*; // Higher precedence binds tighter match operator { - Operator::Pow => 100, - Operator::Multiply - | Operator::Divide - | Operator::Modulo - | Operator::FloorDivision => 95, - Operator::Plus | Operator::Minus => 90, - Operator::ShiftLeft | Operator::ShiftRight => 85, - Operator::NotRegexMatch - | Operator::RegexMatch - | Operator::StartsWith - | Operator::EndsWith - | Operator::LessThan - | Operator::LessThanOrEqual - | Operator::GreaterThan - | Operator::GreaterThanOrEqual - | Operator::Equal - | Operator::NotEqual - | Operator::In - | Operator::NotIn - | Operator::Append => 80, - Operator::BitAnd => 75, - Operator::BitXor => 70, - Operator::BitOr => 60, - Operator::And => 50, - Operator::Or => 40, + Operator::Math(Math::Pow) => 100, + Operator::Math(Math::Multiply) + | Operator::Math(Math::Divide) + | Operator::Math(Math::Modulo) + | Operator::Math(Math::FloorDivision) => 95, + Operator::Math(Math::Plus) | Operator::Math(Math::Minus) => 90, + Operator::Bits(Bits::ShiftLeft) | Operator::Bits(Bits::ShiftRight) => 85, + Operator::Comparison(Comparison::NotRegexMatch) + | Operator::Comparison(Comparison::RegexMatch) + | Operator::Comparison(Comparison::StartsWith) + | Operator::Comparison(Comparison::EndsWith) + | Operator::Comparison(Comparison::LessThan) + | Operator::Comparison(Comparison::LessThanOrEqual) + | Operator::Comparison(Comparison::GreaterThan) + | Operator::Comparison(Comparison::GreaterThanOrEqual) + | Operator::Comparison(Comparison::Equal) + | Operator::Comparison(Comparison::NotEqual) + | Operator::Comparison(Comparison::In) + | Operator::Comparison(Comparison::NotIn) + | Operator::Math(Math::Append) => 80, + Operator::Bits(Bits::BitAnd) => 75, + Operator::Bits(Bits::BitXor) => 70, + Operator::Bits(Bits::BitOr) => 60, + Operator::Boolean(Boolean::And) => 50, + Operator::Boolean(Boolean::Or) => 40, + Operator::Assignment(Assignment::Assign) => 10, } } _ => 0, diff --git a/crates/nu-protocol/src/ast/operator.rs b/crates/nu-protocol/src/ast/operator.rs index 2c29c1dbb5..644e7364fa 100644 --- a/crates/nu-protocol/src/ast/operator.rs +++ b/crates/nu-protocol/src/ast/operator.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use std::fmt::Display; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum Operator { +pub enum Comparison { Equal, NotEqual, LessThan, @@ -13,20 +13,32 @@ pub enum Operator { GreaterThanOrEqual, RegexMatch, NotRegexMatch, + In, + NotIn, + StartsWith, + EndsWith, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum Math { Plus, Append, Minus, Multiply, Divide, - In, - NotIn, Modulo, FloorDivision, + Pow, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum Boolean { And, Or, - Pow, - StartsWith, - EndsWith, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum Bits { BitOr, BitXor, BitAnd, @@ -34,36 +46,51 @@ pub enum Operator { ShiftRight, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum Assignment { + Assign, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum Operator { + Comparison(Comparison), + Math(Math), + Boolean(Boolean), + Bits(Bits), + Assignment(Assignment), +} + impl Display for Operator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Operator::Equal => write!(f, "=="), - Operator::NotEqual => write!(f, "!="), - Operator::LessThan => write!(f, "<"), - Operator::GreaterThan => write!(f, ">"), - Operator::RegexMatch => write!(f, "=~"), - Operator::NotRegexMatch => write!(f, "!~"), - Operator::Plus => write!(f, "+"), - Operator::Append => write!(f, "++"), - Operator::Minus => write!(f, "-"), - Operator::Multiply => write!(f, "*"), - Operator::Divide => write!(f, "/"), - Operator::In => write!(f, "in"), - Operator::NotIn => write!(f, "not-in"), - Operator::Modulo => write!(f, "mod"), - Operator::FloorDivision => write!(f, "fdiv"), - Operator::And => write!(f, "&&"), - Operator::Or => write!(f, "||"), - Operator::Pow => write!(f, "**"), - Operator::BitOr => write!(f, "bit-or"), - Operator::BitXor => write!(f, "bit-xor"), - Operator::BitAnd => write!(f, "bit-and"), - Operator::ShiftLeft => write!(f, "bit-shl"), - Operator::ShiftRight => write!(f, "bit-shr"), - Operator::LessThanOrEqual => write!(f, "<="), - Operator::GreaterThanOrEqual => write!(f, ">="), - Operator::StartsWith => write!(f, "starts-with"), - Operator::EndsWith => write!(f, "ends-with"), + Operator::Assignment(Assignment::Assign) => write!(f, "="), + Operator::Comparison(Comparison::Equal) => write!(f, "=="), + Operator::Comparison(Comparison::NotEqual) => write!(f, "!="), + Operator::Comparison(Comparison::LessThan) => write!(f, "<"), + Operator::Comparison(Comparison::GreaterThan) => write!(f, ">"), + Operator::Comparison(Comparison::RegexMatch) => write!(f, "=~"), + Operator::Comparison(Comparison::NotRegexMatch) => write!(f, "!~"), + Operator::Comparison(Comparison::LessThanOrEqual) => write!(f, "<="), + Operator::Comparison(Comparison::GreaterThanOrEqual) => write!(f, ">="), + Operator::Comparison(Comparison::StartsWith) => write!(f, "starts-with"), + Operator::Comparison(Comparison::EndsWith) => write!(f, "ends-with"), + Operator::Comparison(Comparison::In) => write!(f, "in"), + Operator::Comparison(Comparison::NotIn) => write!(f, "not-in"), + Operator::Math(Math::Plus) => write!(f, "+"), + Operator::Math(Math::Append) => write!(f, "++"), + Operator::Math(Math::Minus) => write!(f, "-"), + Operator::Math(Math::Multiply) => write!(f, "*"), + Operator::Math(Math::Divide) => write!(f, "/"), + Operator::Math(Math::Modulo) => write!(f, "mod"), + Operator::Math(Math::FloorDivision) => write!(f, "fdiv"), + Operator::Math(Math::Pow) => write!(f, "**"), + Operator::Boolean(Boolean::And) => write!(f, "&&"), + Operator::Boolean(Boolean::Or) => write!(f, "||"), + Operator::Bits(Bits::BitOr) => write!(f, "bit-or"), + Operator::Bits(Bits::BitXor) => write!(f, "bit-xor"), + Operator::Bits(Bits::BitAnd) => write!(f, "bit-and"), + Operator::Bits(Bits::ShiftLeft) => write!(f, "bit-shl"), + Operator::Bits(Bits::ShiftRight) => write!(f, "bit-shr"), } } } diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 7a5ceae8b9..0d59963e26 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -105,11 +105,11 @@ impl EngineState { files: vec![], file_contents: vec![], vars: vec![ - Variable::new(Span::new(0, 0), Type::Any), - Variable::new(Span::new(0, 0), Type::Any), - Variable::new(Span::new(0, 0), Type::Any), - Variable::new(Span::new(0, 0), Type::Any), - Variable::new(Span::new(0, 0), Type::Any), + Variable::new(Span::new(0, 0), Type::Any, false), + Variable::new(Span::new(0, 0), Type::Any, false), + Variable::new(Span::new(0, 0), Type::Any, false), + Variable::new(Span::new(0, 0), Type::Any, false), + Variable::new(Span::new(0, 0), Type::Any, false), ], decls: vec![], aliases: vec![], @@ -1571,7 +1571,13 @@ impl<'a> StateWorkingSet<'a> { None } - pub fn add_variable(&mut self, mut name: Vec, span: Span, ty: Type) -> VarId { + pub fn add_variable( + &mut self, + mut name: Vec, + span: Span, + ty: Type, + mutable: bool, + ) -> VarId { let next_id = self.next_var_id(); // correct name if necessary @@ -1581,7 +1587,7 @@ impl<'a> StateWorkingSet<'a> { self.last_overlay_mut().vars.insert(name, next_id); - self.delta.vars.push(Variable::new(span, ty)); + self.delta.vars.push(Variable::new(span, ty, mutable)); next_id } @@ -1643,6 +1649,15 @@ impl<'a> StateWorkingSet<'a> { } } + pub fn get_variable_if_possible(&self, var_id: VarId) -> Option<&Variable> { + let num_permanent_vars = self.permanent_state.num_vars(); + if var_id < num_permanent_vars { + Some(self.permanent_state.get_var(var_id)) + } else { + self.delta.vars.get(var_id - num_permanent_vars) + } + } + #[allow(clippy::borrowed_box)] pub fn get_decl(&self, decl_id: DeclId) -> &Box { let num_permanent_decls = self.permanent_state.num_decls(); diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index a77850da87..018aa91c71 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -83,6 +83,24 @@ pub enum ShellError { #[diagnostic(code(nu::shell::unsupported_operator), url(docsrs))] UnsupportedOperator(Operator, #[label = "unsupported operator"] Span), + /// This value cannot be used with this operator. + /// + /// ## Resolution + /// + /// Assignment requires that you assign to a variable or variable cell path. + #[error("Assignment operations require a variable.")] + #[diagnostic(code(nu::shell::assignment_requires_variable), url(docsrs))] + AssignmentRequiresVar(#[label = "needs to be a variable"] Span), + + /// This value cannot be used with this operator. + /// + /// ## Resolution + /// + /// Assignment requires that you assign to a mutable variable or cell path. + #[error("Assignment to an immutable variable.")] + #[diagnostic(code(nu::shell::assignment_requires_mutable_variable), url(docsrs))] + AssignmentRequiresMutableVar(#[label = "needs to be a mutable variable"] Span), + /// An operator was not recognized during evaluation. /// /// ## Resolution diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index 5b5c8a519a..cb879b87e9 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -5,8 +5,8 @@ mod range; mod stream; mod unit; -use crate::ast::Operator; -use crate::ast::{CellPath, PathMember}; +use crate::ast::{Bits, Boolean, CellPath, Comparison, PathMember}; +use crate::ast::{Math, Operator}; use crate::ShellError; use crate::{did_you_mean, BlockId, Config, Span, Spanned, Type, VarId}; use byte_unit::ByteUnit; @@ -1734,7 +1734,7 @@ impl Value { } (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::Plus, op, rhs) + lhs.operation(*span, Operator::Math(Math::Plus), op, rhs) } _ => Err(ShellError::OperatorMismatch { @@ -1841,7 +1841,7 @@ impl Value { } (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::Minus, op, rhs) + lhs.operation(*span, Operator::Math(Math::Minus), op, rhs) } _ => Err(ShellError::OperatorMismatch { @@ -1927,7 +1927,7 @@ impl Value { }) } (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::Multiply, op, rhs) + lhs.operation(*span, Operator::Math(Math::Multiply), op, rhs) } _ => Err(ShellError::OperatorMismatch { @@ -2064,7 +2064,7 @@ impl Value { } } (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::Divide, op, rhs) + lhs.operation(*span, Operator::Math(Math::Divide), op, rhs) } _ => Err(ShellError::OperatorMismatch { @@ -2210,7 +2210,7 @@ impl Value { } } (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::Divide, op, rhs) + lhs.operation(*span, Operator::Math(Math::Divide), op, rhs) } _ => Err(ShellError::OperatorMismatch { @@ -2225,7 +2225,7 @@ impl Value { pub fn lt(&self, op: Span, rhs: &Value, span: Span) -> Result { if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) { - return lhs.operation(*span, Operator::LessThan, op, rhs); + return lhs.operation(*span, Operator::Comparison(Comparison::LessThan), op, rhs); } if !type_compatible(self.get_type(), rhs.get_type()) @@ -2252,7 +2252,12 @@ impl Value { pub fn lte(&self, op: Span, rhs: &Value, span: Span) -> Result { if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) { - return lhs.operation(*span, Operator::LessThanOrEqual, op, rhs); + return lhs.operation( + *span, + Operator::Comparison(Comparison::LessThanOrEqual), + op, + rhs, + ); } if !type_compatible(self.get_type(), rhs.get_type()) @@ -2279,7 +2284,12 @@ impl Value { pub fn gt(&self, op: Span, rhs: &Value, span: Span) -> Result { if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) { - return lhs.operation(*span, Operator::GreaterThan, op, rhs); + return lhs.operation( + *span, + Operator::Comparison(Comparison::GreaterThan), + op, + rhs, + ); } if !type_compatible(self.get_type(), rhs.get_type()) @@ -2306,7 +2316,12 @@ impl Value { pub fn gte(&self, op: Span, rhs: &Value, span: Span) -> Result { if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) { - return lhs.operation(*span, Operator::GreaterThanOrEqual, op, rhs); + return lhs.operation( + *span, + Operator::Comparison(Comparison::GreaterThanOrEqual), + op, + rhs, + ); } if !type_compatible(self.get_type(), rhs.get_type()) @@ -2333,7 +2348,7 @@ impl Value { pub fn eq(&self, op: Span, rhs: &Value, span: Span) -> Result { if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) { - return lhs.operation(*span, Operator::Equal, op, rhs); + return lhs.operation(*span, Operator::Comparison(Comparison::Equal), op, rhs); } match self.partial_cmp(rhs) { @@ -2358,7 +2373,7 @@ impl Value { pub fn ne(&self, op: Span, rhs: &Value, span: Span) -> Result { if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) { - return lhs.operation(*span, Operator::NotEqual, op, rhs); + return lhs.operation(*span, Operator::Comparison(Comparison::NotEqual), op, rhs); } match self.partial_cmp(rhs) { @@ -2426,7 +2441,7 @@ impl Value { }) } (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::In, op, rhs) + lhs.operation(*span, Operator::Comparison(Comparison::In), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2483,7 +2498,7 @@ impl Value { }) } (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::NotIn, op, rhs) + lhs.operation(*span, Operator::Comparison(Comparison::NotIn), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2528,9 +2543,9 @@ impl Value { (Value::CustomValue { val: lhs, span }, rhs) => lhs.operation( *span, if invert { - Operator::NotRegexMatch + Operator::Comparison(Comparison::NotRegexMatch) } else { - Operator::RegexMatch + Operator::Comparison(Comparison::RegexMatch) }, op, rhs, @@ -2552,7 +2567,7 @@ impl Value { span, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::StartsWith, op, rhs) + lhs.operation(*span, Operator::Comparison(Comparison::StartsWith), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2571,7 +2586,7 @@ impl Value { span, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::EndsWith, op, rhs) + lhs.operation(*span, Operator::Comparison(Comparison::EndsWith), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2590,7 +2605,7 @@ impl Value { val: *lhs << rhs, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::ShiftLeft, op, rhs) + lhs.operation(*span, Operator::Bits(Bits::ShiftLeft), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2609,7 +2624,7 @@ impl Value { val: *lhs >> rhs, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::ShiftRight, op, rhs) + lhs.operation(*span, Operator::Bits(Bits::ShiftRight), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2628,7 +2643,7 @@ impl Value { val: *lhs | rhs, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::BitOr, op, rhs) + lhs.operation(*span, Operator::Bits(Bits::BitOr), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2647,7 +2662,7 @@ impl Value { val: *lhs ^ rhs, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::BitXor, op, rhs) + lhs.operation(*span, Operator::Bits(Bits::BitXor), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2666,7 +2681,7 @@ impl Value { val: *lhs & rhs, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::BitAnd, op, rhs) + lhs.operation(*span, Operator::Bits(Bits::BitAnd), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2721,7 +2736,7 @@ impl Value { } } (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::Modulo, op, rhs) + lhs.operation(*span, Operator::Math(Math::Modulo), op, rhs) } _ => Err(ShellError::OperatorMismatch { @@ -2741,7 +2756,7 @@ impl Value { span, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::And, op, rhs) + lhs.operation(*span, Operator::Boolean(Boolean::And), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2760,7 +2775,7 @@ impl Value { span, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::Or, op, rhs) + lhs.operation(*span, Operator::Boolean(Boolean::Or), op, rhs) } _ => Err(ShellError::OperatorMismatch { op_span: op, @@ -2797,7 +2812,7 @@ impl Value { span, }), (Value::CustomValue { val: lhs, span }, rhs) => { - lhs.operation(*span, Operator::Pow, op, rhs) + lhs.operation(*span, Operator::Math(Math::Pow), op, rhs) } _ => Err(ShellError::OperatorMismatch { diff --git a/crates/nu-protocol/src/variable.rs b/crates/nu-protocol/src/variable.rs index f6acaf6808..563a24248c 100644 --- a/crates/nu-protocol/src/variable.rs +++ b/crates/nu-protocol/src/variable.rs @@ -4,13 +4,15 @@ use crate::{Span, Type}; pub struct Variable { pub declaration_span: Span, pub ty: Type, + pub mutable: bool, } impl Variable { - pub fn new(declaration_span: Span, ty: Type) -> Variable { + pub fn new(declaration_span: Span, ty: Type, mutable: bool) -> Variable { Self { declaration_span, ty, + mutable, } } }