mirror of
https://github.com/nushell/nushell
synced 2024-12-26 04:53:09 +00:00
Limited mutable variables (#7089)
This adds support for (limited) mutable variables. Mutable variables are created with mut much the same way immutable variables are made with let. Mutable variables allow mutation via the assignment operator (=). ❯ mut x = 100 ❯ $x = 200 ❯ print $x 200 Mutable variables are limited in that they're only tended to be used in the local code block. Trying to capture a local variable will result in an error: ❯ mut x = 123; {|| $x } Error: nu::parser::expected_keyword (link) × Capture of mutable variable. The intent of this limitation is to reduce some of the issues with mutable variables in general: namely they make code that's harder to reason about. By reducing the scope that a mutable variable can be used it, we can help create local reasoning about them. Mutation can occur with fields as well, as in this case: ❯ mut y = {abc: 123} ❯ $y.abc = 456 ❯ $y On a historical note: mutable variables are something that we resisted for quite a long time, leaning as much as we could on the functional style of pipelines and dataflow. That said, we've watched folks struggle to work with reduce as an approximation for patterns that would be trivial to express with local mutation. With that in mind, we're leaning towards the happy path.
This commit is contained in:
parent
58d960d914
commit
13515c5eb0
22 changed files with 857 additions and 387 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -801,6 +801,7 @@ pub fn eval_hook(
|
|||
name.as_bytes().to_vec(),
|
||||
val.span()?,
|
||||
Type::Any,
|
||||
false,
|
||||
);
|
||||
|
||||
vars.push((var_id, val));
|
||||
|
|
|
@ -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;
|
||||
|
|
117
crates/nu-command/src/core_commands/mut_.rs
Normal file
117
crates/nu-command/src/core_commands/mut_.rs
Normal file
|
@ -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<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
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<Example> {
|
||||
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));
|
||||
}
|
||||
}
|
|
@ -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<Value, ShellError> {
|
||||
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<Value, ShellError> {
|
||||
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, <ChunkedArray<Int64Type>>::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, <ChunkedArray<Int64Type>>::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, <ChunkedArray<Int64Type>>::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)
|
||||
|
|
|
@ -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<Value, ShellError> {
|
||||
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)),
|
||||
|
|
|
@ -59,6 +59,7 @@ pub fn create_default_context() -> EngineState {
|
|||
Let,
|
||||
Metadata,
|
||||
Module,
|
||||
Mut,
|
||||
Use,
|
||||
Version,
|
||||
};
|
||||
|
|
|
@ -45,7 +45,7 @@ impl Command for Format {
|
|||
let specified_pattern: Result<Value, ShellError> = 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 {
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ mod math;
|
|||
mod merge;
|
||||
mod mkdir;
|
||||
mod move_;
|
||||
mod mut_;
|
||||
mod n;
|
||||
mod network;
|
||||
mod nu_check;
|
||||
|
|
49
crates/nu-command/tests/commands/mut_.rs
Normal file
49
crates/nu-command/tests/commands/mut_.rs
Normal file
|
@ -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");
|
||||
}
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<ParseError>) {
|
||||
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],
|
||||
|
|
|
@ -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<ParseError>) {
|
||||
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<ParseError>) {
|
||||
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<VarId>,
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<VarId>>,
|
||||
) -> Vec<VarId> {
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
|
||||
) -> Result<Vec<(VarId, Span)>, 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<VarId>,
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<VarId>>,
|
||||
) -> Vec<VarId> {
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
|
||||
) -> Result<Vec<(VarId, Span)>, 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<VarId>,
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<VarId>>,
|
||||
) -> Vec<VarId> {
|
||||
let mut output = vec![];
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
|
||||
) -> Result<Vec<(VarId, Span)>, 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<u8>, span: Span, ty: Type) -> VarId {
|
||||
pub fn add_variable(
|
||||
&mut self,
|
||||
mut name: Vec<u8>,
|
||||
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<dyn Command> {
|
||||
let num_permanent_decls = self.permanent_state.num_decls();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Value, ShellError> {
|
||||
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<Value, ShellError> {
|
||||
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<Value, ShellError> {
|
||||
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<Value, ShellError> {
|
||||
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<Value, ShellError> {
|
||||
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<Value, ShellError> {
|
||||
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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue