From 9bbd2b4a8895b16f2e8d4eb5e4cc2216f1becb04 Mon Sep 17 00:00:00 2001 From: Tim Fennis Date: Fri, 27 Feb 2026 14:12:09 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20op-assign=20not=20using=20?= =?UTF-8?q?default=20value=20on=20maps=20with=20defaults?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ndc_lib/src/interpreter/evaluate/index.rs | 43 +++++++++++++++---- ndc_lib/src/interpreter/evaluate/mod.rs | 25 +---------- .../024_op_assign_default_value.ndc | 7 +++ 3 files changed, 44 insertions(+), 31 deletions(-) create mode 100644 tests/programs/007_map_and_set/024_op_assign_default_value.ndc diff --git a/ndc_lib/src/interpreter/evaluate/index.rs b/ndc_lib/src/interpreter/evaluate/index.rs index c9c186fe..5f904153 100644 --- a/ndc_lib/src/interpreter/evaluate/index.rs +++ b/ndc_lib/src/interpreter/evaluate/index.rs @@ -9,7 +9,7 @@ //! | Backward index | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 | //! +----------------+-----+----+----+----+----+----+----+----+----+----+ -use super::{EvaluationError, IntoEvaluationResult, evaluate_expression}; +use super::{EvaluationError, EvaluationResult, IntoEvaluationResult, evaluate_expression}; use crate::interpreter::environment::Environment; use crate::{ ast::{Expression, ExpressionLocation}, @@ -174,6 +174,7 @@ pub fn get_at_index( lhs: &Value, index: EvaluatedIndex, span: Span, + environment: &Rc>, ) -> Result { let Some(size) = lhs.sequence_length() else { return Err(EvaluationError::new( @@ -210,9 +211,7 @@ pub fn get_at_index( .collect::(), })) } - Value::Sequence(Sequence::Map(map, _)) => { - let map = map.try_borrow().into_evaluation_result(span)?; - + Value::Sequence(Sequence::Map(map, default)) => { let key = match index { EvaluatedIndex::Index(idx) => idx, EvaluatedIndex::Slice { .. } => { @@ -224,10 +223,17 @@ pub fn get_at_index( } }; - Ok(map - .get(&key) - .ok_or_else(|| EvaluationError::key_not_found(&key, span))? - .clone()) + let value = map.try_borrow().into_evaluation_result(span)?.get(&key).cloned(); + + if let Some(value) = value { + Ok(value) + } else if let Some(default) = default { + let default_value = produce_default_value(default, environment, span)?; + map.try_borrow_mut().into_evaluation_result(span)?.insert(key, default_value.clone()); + Ok(default_value) + } else { + Err(EvaluationError::key_not_found(&key, span).into()) + } } _ => Err(EvaluationError::syntax_error( format!("cannot insert into {} at index", lhs.static_type()), @@ -236,6 +242,27 @@ pub fn get_at_index( .into()), } } + +pub(super) fn produce_default_value( + default: &Value, + environment: &Rc>, + span: Span, +) -> EvaluationResult { + match default { + Value::Function(function) => { + match function.call_checked(&mut [], environment) { + Err(FunctionCarrier::FunctionTypeMismatch) => { + Err(FunctionCarrier::EvaluationError(EvaluationError::new( + "default function is not callable without arguments".to_string(), + span, + ))) + } + a => a, + } + } + value => Ok(value.clone()), + } +} pub fn set_at_index( lhs: &mut Value, rhs: Value, diff --git a/ndc_lib/src/interpreter/evaluate/mod.rs b/ndc_lib/src/interpreter/evaluate/mod.rs index b93a1ab0..90253e0b 100644 --- a/ndc_lib/src/interpreter/evaluate/mod.rs +++ b/ndc_lib/src/interpreter/evaluate/mod.rs @@ -165,7 +165,7 @@ pub(crate) fn evaluate_expression( } => { let mut lhs_value = evaluate_expression(lhs_expression, environment)?; let index = evaluate_as_index(index_expression, environment)?; - let value_at_index = get_at_index(&lhs_value, index.clone(), span)?; + let value_at_index = get_at_index(&lhs_value, index.clone(), span, environment)?; let right_value = evaluate_expression(r_value, environment)?; @@ -565,7 +565,7 @@ pub(crate) fn evaluate_expression( return if let Some(value) = value { Ok(value) } else if let Some(default) = default { - let default_value = produce_default_value( + let default_value = index::produce_default_value( &default, environment, // NOTE: this span points at the entire expression instead of the @@ -647,27 +647,6 @@ pub(crate) fn evaluate_expression( Ok(literal) } -fn produce_default_value( - default: &Value, - environment: &Rc>, - span: Span, -) -> EvaluationResult { - match default { - Value::Function(function) => { - match function.call_checked(&mut [], environment) { - Err(FunctionCarrier::FunctionTypeMismatch) => { - Err(FunctionCarrier::EvaluationError(EvaluationError::new( - "default function is not callable without arguments".to_string(), - span, - ))) - } - a => a, - } - // test - } - value => Ok(value.clone()), - } -} fn declare_or_assign_variable( l_value: &Lvalue, diff --git a/tests/programs/007_map_and_set/024_op_assign_default_value.ndc b/tests/programs/007_map_and_set/024_op_assign_default_value.ndc new file mode 100644 index 00000000..43f90d9b --- /dev/null +++ b/tests/programs/007_map_and_set/024_op_assign_default_value.ndc @@ -0,0 +1,7 @@ +let map = %{:0}; // map with default 0 +map["foo"] += 3; +map[0] += 10; +map[0] += 10; + +assert_eq(map["foo"], 3); +assert_eq(map[0], 20); \ No newline at end of file