diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7740c6a5..f0672f0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,14 +41,14 @@ jobs: path: Cargo.lock msrv: - name: MSRV (1.68) Compiles + name: MSRV (1.71) Compiles runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.68 + toolchain: 1.71 - run: cargo check style-check: diff --git a/Cargo.toml b/Cargo.toml index 94855c4f..b1c35f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,9 @@ categories = ["development-tools", "compilers"] # MSRV 1.61.0 is the old MSRV of syn # MRSV 1.63.0 is needed to support RefMut::filter_map # MRSV 1.68.0 is the new latest MSRV of syn (as of syn 2.0.107 released on 19th October 2025) +# MRSV 1.71.0 is the new latest MSRV of unicode-ident (released on 8th February 2026) # If changing this, also update the local-check-msrv.sh script and ci.yml -rust-version = "1.68" +rust-version = "1.71" [lib] proc-macro = true diff --git a/local-check-msrv.sh b/local-check-msrv.sh index ac001e6f..60909ae6 100755 --- a/local-check-msrv.sh +++ b/local-check-msrv.sh @@ -4,5 +4,5 @@ set -e cd "$(dirname "$0")" -rustup install 1.68 -rm Cargo.lock && rustup run 1.68 cargo check +rustup install 1.71 +rm Cargo.lock && rustup run 1.71 cargo check diff --git a/plans/TODO.md b/plans/TODO.md index 411f2cb4..94fb0b8b 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -237,26 +237,25 @@ Moved to [2026-01-types-and-forms.md](./2026-01-types-and-forms.md). - [x] Allow shared / mutable arguments - [x] Add tests for shared/mutable, including conversions working and not - [ ] Iterable methods: - - [ ] Unpick `feat/iterable-map` and work our what we want to keep: - * Half-baked `FunctionValue` changes to allow invocation - * Separation of `ExecutionInterrupt` and `FunctionError` - do we actually want / need it? - - Perhaps `Result` -> `core::Result` - - `ExecutionResult` -> `ExpressionResult` - - And `FunctionResult` -> `Result` ? - * `Iterator` returns `Result` change -> do we want it? - * `Iterator` comparison can return errors -> maybe we just have `iterator != iterator` unless `std::ptr::eq`? + - [x] Unpick `feat/iterable-map` and work our what we want to keep: + - [x] Separation of `ExecutionInterrupt` and `FunctionError` - incorporated below + - [x] `Iterator` returns `Result` change -> replaced with below * To implement `.map()`, we have a few things we need first: - - An iterator trait where Interpreter is passed at next time. - - Possibly - not require `Clone` on iterators: + -[x] An iterator trait where Interpreter is passed at next time. + -[ ] Possibly - not require `Clone` on iterators: - Make `TryClone -> Result` - Make `to_string` for iterator return `Iterator[?]` - - [ ] Create new iterator trait `PreinterpretIterator` and `IntoPreinterpretIterator` with `.next(&mut Interpreter)` - - [ ] Blanket implement it for iterator - - [ ] Then replace e.g. for loop with it. - - [ ] Create `Map` and `Filter` types on top of it, to be able to - implement `map` and `filter` + - [x] Create new iterator trait `PreinterpretIterator` and `IntoPreinterpretIterator` with `.next(&mut Interpreter)` + - [x] Blanket implement it for `Iterator + Clone` + - [x] Then replace e.g. for loop impl with it. + - [x] Create `Map` and `Filter` types on top of it, to be able to implement `map` and `filter` + - [ ] See if new iterators on iterator value can be fixed to be lazy + - [ ] Salvage half-baked `FunctionValue` changes to allow invocation + - [ ] Consider if IteratorValue should have `Item = ReturnedValue` - [ ] Add `iterable.map`, `iterable.filter`, `iterable.flatten`, `iterable.flatmap` + - [ ] Add tests for iterable methods - [ ] Add `array.sort`, `array.sort_by` +- [ ] Look into if a closure like `|| { }()` can evade `attempt` block statue mutation checks. Maybe lean into it as a way to avoid htem, and mention it in the error message - [ ] Resolve all `TODO[functions]` Possible punted: @@ -458,8 +457,9 @@ preinterpret::run! { - [ ] Look at benchmarks and if anything should be sped up - [ ] Speeding up stream literal processing - - [ ] When interpreting a stream literal, we can avoid having to go through error handling pathways to get an `output` from the intepreter by storing a `OutputInterpreter<'a>` which wraps an `&mut OutputStream` and a pointer to an Intepreter, and can be converted back into/from an `Interpreter` easily - - [ ] Possibly similarly for an `InputInterpreter<'a>` when processing a `ConsumeStream` + - [ ] Avoid a little overhead by having `OutputInterpreter<'a>` store a pointer to interpreter and output stream and a `PhantomData<&'a Interpreter>` / `PhantomData<&'a OutputStream>`, and recreate output stream after a call to with_interpreter. + ... and get rid of `output_stack_height`, `current_output_unchecked`, `current_output_mut_unchecked` + - [ ] Create an `InputInterpreter<'a>` when processing a `ConsumeStream` - [ ] Speeding up scopes at runtime: - [ ] In the interpreter, store a flattened stack of variable values - [ ] `no_mutation_above` can be a stack offset diff --git a/src/expressions/closures.rs b/src/expressions/closures.rs index faa522f9..5c1da71a 100644 --- a/src/expressions/closures.rs +++ b/src/expressions/closures.rs @@ -19,7 +19,7 @@ impl ClosureExpression { &self, interpreter: &mut Interpreter, ownership: RequestedOwnership, - ) -> ExecutionResult> { + ) -> FunctionResult> { let span_range = self.0.span_range; let value = ClosureValue { definition: Rc::clone(&self.0), @@ -61,14 +61,17 @@ impl ClosureValue { self, arguments: Vec>, context: &mut FunctionCallContext, - ) -> ExecutionResult> { + ) -> FunctionResult> { let definition = &*self.definition; - context.interpreter.enter_function_boundary_scope( - definition.scope_id, - definition.frame_id, - context.output_span_range, - )?; + context + .interpreter + .enter_function_boundary_scope( + definition.scope_id, + definition.frame_id, + context.output_span_range, + ) + .expect_no_interrupts()?; for (definition, content) in self.closed_references { context.interpreter.define_variable(definition, content); } @@ -78,7 +81,9 @@ impl ClosureValue { { match (pattern, arg) { (pattern, ArgumentValue::Owned(owned)) => { - pattern.handle_destructure(context.interpreter, owned)?; + pattern + .handle_destructure(context.interpreter, owned) + .expect_no_interrupts()?; } (Pattern::Discarded(_), _) => {} (Pattern::Variable(variable), ArgumentValue::Shared(shared)) => { @@ -111,10 +116,13 @@ impl ClosureValue { } } - let Spanned(output, body_span) = definition.body.evaluate( - context.interpreter, - RequestedOwnership::Concrete(ArgumentOwnership::AsIs), - )?; + let Spanned(output, body_span) = definition + .body + .evaluate( + context.interpreter, + RequestedOwnership::Concrete(ArgumentOwnership::AsIs), + ) + .expect_no_interrupts()?; let returned_value = match output { RequestedValue::Owned(any_value) => ReturnedValue::Owned(any_value), diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs index d33e97c7..641cbcd0 100644 --- a/src/expressions/concepts/content.rs +++ b/src/expressions/concepts/content.rs @@ -92,7 +92,7 @@ where pub(crate) fn downcast_resolve>( self, resolution_target: &str, - ) -> ExecutionResult + ) -> FunctionResult where ::Type: DowncastFrom, { @@ -107,11 +107,11 @@ where } // TODO[concepts]: Change to use a FromSpannedDynContent trait, - // so that it can return a ExecutionResult and avoid needing to specify D. + // so that it can return a FunctionResult and avoid needing to specify D. pub(crate) fn dyn_resolve( self, resolution_target: &str, - ) -> ExecutionResult> + ) -> FunctionResult> where ::Type: DynResolveFrom, C::Form: IsDynCompatibleForm, @@ -225,7 +225,7 @@ impl< { type ValueType = T; const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; - fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + fn from_argument(Spanned(value, span_range): Spanned) -> FunctionResult { let ownership_mapped = F::from_argument_value(value)?; let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?; Ok(X::from_content(type_mapped)) @@ -240,7 +240,7 @@ impl< T: UpcastTo, > IsReturnable for X { - fn to_returned_value(self) -> ExecutionResult { + fn to_returned_value(self) -> FunctionResult { let type_mapped = self.into_content().upcast::(); BeOwned::into_returned_value(type_mapped) } diff --git a/src/expressions/concepts/form.rs b/src/expressions/concepts/form.rs index 6e86fb15..6337ce19 100644 --- a/src/expressions/concepts/form.rs +++ b/src/expressions/concepts/form.rs @@ -65,13 +65,11 @@ pub(crate) trait IsDynCompatibleForm: IsHierarchicalForm { pub(crate) trait MapFromArgument: IsHierarchicalForm { const ARGUMENT_OWNERSHIP: ArgumentOwnership; - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult>; + fn from_argument_value(value: ArgumentValue) + -> FunctionResult>; } pub(crate) trait MapIntoReturned: IsHierarchicalForm { - fn into_returned_value( - value: Content<'static, AnyType, Self>, - ) -> ExecutionResult; + fn into_returned_value(value: Content<'static, AnyType, Self>) + -> FunctionResult; } diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs index 5bbaf984..43f623e9 100644 --- a/src/expressions/concepts/forms/any_mut.rs +++ b/src/expressions/concepts/forms/any_mut.rs @@ -57,7 +57,7 @@ impl MapFromArgument for BeAnyMut { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(value .expect_mutable() .0 diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs index 3e9220cd..b813264b 100644 --- a/src/expressions/concepts/forms/any_ref.rs +++ b/src/expressions/concepts/forms/any_ref.rs @@ -52,7 +52,7 @@ impl MapFromArgument for BeAnyRef { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(value .expect_shared() .0 diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs index a2ac0e4d..f7cafdfc 100644 --- a/src/expressions/concepts/forms/argument.rs +++ b/src/expressions/concepts/forms/argument.rs @@ -38,7 +38,7 @@ impl MapFromArgument for BeArgument { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> FunctionResult> { todo!("Argument") } } diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs index 1a20eef6..489bb712 100644 --- a/src/expressions/concepts/forms/assignee.rs +++ b/src/expressions/concepts/forms/assignee.rs @@ -62,7 +62,7 @@ impl MapFromArgument for BeAssignee { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(value.expect_assignee().into_content()) } } diff --git a/src/expressions/concepts/forms/copy_on_write.rs b/src/expressions/concepts/forms/copy_on_write.rs index 2e6ea9a0..8a644ddb 100644 --- a/src/expressions/concepts/forms/copy_on_write.rs +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -76,7 +76,7 @@ impl MapFromArgument for BeCopyOnWrite { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> FunctionResult> { match value.expect_copy_on_write().inner { CopyOnWriteInner::Owned(owned) => Ok(BeCopyOnWrite::new_owned(owned)), CopyOnWriteInner::SharedWithInfallibleCloning(shared) => { diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs index b17e1c43..d8c79110 100644 --- a/src/expressions/concepts/forms/mutable.rs +++ b/src/expressions/concepts/forms/mutable.rs @@ -60,7 +60,7 @@ impl MapFromArgument for BeMutable { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(value.expect_mutable().into_content()) } } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs index 5095ac42..09873161 100644 --- a/src/expressions/concepts/forms/owned.rs +++ b/src/expressions/concepts/forms/owned.rs @@ -50,7 +50,7 @@ impl MapFromArgument for BeOwned { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(value.expect_owned()) } } @@ -58,7 +58,7 @@ impl MapFromArgument for BeOwned { impl MapIntoReturned for BeOwned { fn into_returned_value( content: Content<'static, AnyType, Self>, - ) -> ExecutionResult { + ) -> FunctionResult { Ok(ReturnedValue::Owned(content)) } } diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs index 106cf736..e9a21302 100644 --- a/src/expressions/concepts/forms/shared.rs +++ b/src/expressions/concepts/forms/shared.rs @@ -54,7 +54,7 @@ impl MapFromArgument for BeShared { fn from_argument_value( value: ArgumentValue, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(value.expect_shared().into_content()) } } @@ -62,7 +62,7 @@ impl MapFromArgument for BeShared { // impl MapIntoReturned for BeShared { // fn into_returned_value( // content: Content<'static, AnyType, Self>, -// ) -> ExecutionResult { +// ) -> FunctionResult { // todo!("Return shared") // } // } diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs index d5111b6d..d267a660 100644 --- a/src/expressions/concepts/type_traits.rs +++ b/src/expressions/concepts/type_traits.rs @@ -86,7 +86,7 @@ pub(crate) trait DowncastFrom: IsHierarchicalType { content: Content<'a, T, F>, span_range: SpanRange, resolution_target: &str, - ) -> ExecutionResult> { + ) -> FunctionResult> { let content = match Self::downcast_from(content) { Ok(c) => c, Err(existing) => { @@ -112,7 +112,7 @@ pub(crate) trait DynResolveFrom: IsDynType { content: Content<'a, T, F>, span_range: SpanRange, resolution_target: &str, - ) -> ExecutionResult> { + ) -> FunctionResult> { let content = match Self::downcast_from(content) { Ok(c) => c, Err(existing) => { @@ -816,7 +816,7 @@ macro_rules! define_dyn_type { impl IsArgument for Box<$dyn_type> { type ValueType = $type_def; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + fn from_argument(Spanned(value, span_range): Spanned) -> FunctionResult { let form_mapped = BeOwned::from_argument_value(value)?; <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") } @@ -825,7 +825,7 @@ macro_rules! define_dyn_type { impl<'a> IsArgument for AnyRef<'a, $dyn_type> { type ValueType = $type_def; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; - fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + fn from_argument(Spanned(value, span_range): Spanned) -> FunctionResult { let form_mapped = BeAnyRef::from_argument_value(value)?; <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") } @@ -834,7 +834,7 @@ macro_rules! define_dyn_type { impl<'a> IsArgument for AnyMut<'a, $dyn_type> { type ValueType = $type_def; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; - fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + fn from_argument(Spanned(value, span_range): Spanned) -> FunctionResult { let form_mapped = BeAnyMut::from_argument_value(value)?; <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") } diff --git a/src/expressions/control_flow.rs b/src/expressions/control_flow.rs index 3e941f1d..ee1f22d1 100644 --- a/src/expressions/control_flow.rs +++ b/src/expressions/control_flow.rs @@ -125,9 +125,7 @@ impl Evaluate for IfExpression { return else_code.evaluate(interpreter, requested_ownership); } - requested_ownership - .map_from_owned(Spanned(().into_any_value(), self.span_range())) - .map(|spanned| spanned.0) + Ok(requested_ownership.map_none(self.span_range())?.0) } } @@ -216,9 +214,7 @@ impl Evaluate for WhileExpression { } } } - ownership - .map_none(self.span_range()) - .map(|spanned| spanned.0) + Ok(ownership.map_none(self.span_range())?.0) } } @@ -379,7 +375,12 @@ impl Evaluate for ForExpression { let scope = interpreter.current_scope_id(); let mut iteration_counter = interpreter.start_iteration_counter(&span); - for item in iterable.into_iterator()? { + let mut iterator = iterable.into_iterator()?; + loop { + let item = match iterator.do_next(interpreter)? { + Some(item) => item, + None => break, + }; iteration_counter.increment_and_check()?; interpreter.enter_child_scope(self.iteration_scope)?; @@ -407,9 +408,7 @@ impl Evaluate for ForExpression { } interpreter.exit_scope(self.iteration_scope); } - ownership - .map_none(self.span_range()) - .map(|spanned| spanned.0) + Ok(ownership.map_none(self.span_range())?.0) } } @@ -527,7 +526,8 @@ impl Evaluate for AttemptExpression { move |interpreter: &mut Interpreter| -> ExecutionResult { guard_expression .evaluate_owned(interpreter)? - .resolve_as("The guard condition of an attempt arm") + .downcast_resolve::("The guard condition of an attempt arm") + .into_execution_result() } }) } @@ -538,7 +538,10 @@ impl Evaluate for AttemptExpression { |interpreter| -> ExecutionResult<()> { arm.lhs .evaluate_owned(interpreter)? - .resolve_as("The returned value from the left half of an attempt arm") + .downcast_resolve::<()>( + "The returned value from the left half of an attempt arm", + ) + .into_execution_result() }, guard_clause(arm.guard.as_ref()), MutationBlockReason::AttemptRevertibleSegment, diff --git a/src/expressions/equality.rs b/src/expressions/equality.rs index 59662f54..5b095550 100644 --- a/src/expressions/equality.rs +++ b/src/expressions/equality.rs @@ -9,7 +9,6 @@ use super::*; pub(crate) enum PathSegment { ArrayIndex(usize), ObjectKey(String), - IteratorIndex(usize), RangeStart, RangeEnd, } @@ -27,7 +26,6 @@ impl PathSegment { result.push_str(&format!("[{:?}]", k)) } } - PathSegment::IteratorIndex(i) => result.push_str(&format!("[{}]", i)), PathSegment::RangeStart => result.push_str(".start"), PathSegment::RangeEnd => result.push_str(".end"), } @@ -68,20 +66,12 @@ pub(crate) trait EqualityContext { /// Object is missing a key that the other has. fn missing_key(&mut self, key: &str, missing_on: MissingSide) -> Self::Result; - fn iteration_limit_exceeded(&mut self, limit: usize) -> Self::Result { - let message = format!("iteration limit {} exceeded", limit); - self.leaf_values_not_equal(&message, &message) - } - /// Wrap a comparison within an array index context. fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R; /// Wrap a comparison within an object key context. fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R; - /// Wrap a comparison within an iterator index context. - fn with_iterator_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R; - /// Wrap a comparison within a range start context. fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R; @@ -140,11 +130,6 @@ impl EqualityContext for SimpleEquality { f(self) } - #[inline] - fn with_iterator_index(&mut self, _index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - f(self) - } - #[inline] fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { f(self) @@ -177,10 +162,10 @@ impl TypedEquality { } impl EqualityContext for TypedEquality { - type Result = ExecutionResult; + type Result = FunctionResult; #[inline] - fn values_equal(&mut self) -> ExecutionResult { + fn values_equal(&mut self) -> FunctionResult { Ok(true) } @@ -189,7 +174,7 @@ impl EqualityContext for TypedEquality { &mut self, _lhs: &T, _rhs: &T, - ) -> ExecutionResult { + ) -> FunctionResult { Ok(false) } @@ -197,7 +182,7 @@ impl EqualityContext for TypedEquality { &mut self, lhs: &L, rhs: &R, - ) -> ExecutionResult { + ) -> FunctionResult { let path_str = PathSegment::fmt_path(&self.path); Err(self.error_span.type_error(format!( "lhs{} is {}, but rhs{} is {}", @@ -228,12 +213,12 @@ impl EqualityContext for TypedEquality { &mut self, _lhs_len: Option, _rhs_len: Option, - ) -> ExecutionResult { + ) -> FunctionResult { Ok(false) } #[inline] - fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> ExecutionResult { + fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> FunctionResult { Ok(false) } @@ -253,14 +238,6 @@ impl EqualityContext for TypedEquality { result } - #[inline] - fn with_iterator_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::IteratorIndex(index)); - let result = f(self); - self.path.pop(); - result - } - #[inline] fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { self.path.push(PathSegment::RangeStart); @@ -278,7 +255,7 @@ impl EqualityContext for TypedEquality { } #[inline] - fn should_short_circuit(&self, result: &ExecutionResult) -> bool { + fn should_short_circuit(&self, result: &FunctionResult) -> bool { // Short-circuit on Ok(false) or Err(_) !matches!(result, Ok(true)) } @@ -513,14 +490,6 @@ impl EqualityContext for DebugEquality { result } - #[inline] - fn with_iterator_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::IteratorIndex(index)); - let result = f(self); - self.path.pop(); - result - } - #[inline] fn with_range_start(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { self.path.push(PathSegment::RangeStart); @@ -570,7 +539,7 @@ pub(crate) trait ValuesEqual: Sized + HasLeafKind { } /// Strict equality check that errors on incompatible types. - fn typed_eq(&self, other: &Self, error_span: SpanRange) -> ExecutionResult { + fn typed_eq(&self, other: &Self, error_span: SpanRange) -> FunctionResult { self.test_equality(other, &mut TypedEquality::new(error_span)) } diff --git a/src/expressions/evaluation/evaluator.rs b/src/expressions/evaluation/evaluator.rs index db95133e..1576b1ad 100644 --- a/src/expressions/evaluation/evaluator.rs +++ b/src/expressions/evaluation/evaluator.rs @@ -237,13 +237,12 @@ impl RequestedValue { pub(crate) fn expect_any_value_and_map( self, - map_shared: impl FnOnce(AnyValueShared) -> ExecutionResult, + map_shared: impl FnOnce(AnyValueShared) -> FunctionResult, map_mutable: impl FnOnce( AnyValueMutable, - ) - -> Result, - map_owned: impl FnOnce(AnyValueOwned) -> ExecutionResult, - ) -> ExecutionResult { + ) -> Result, + map_owned: impl FnOnce(AnyValueOwned) -> FunctionResult, + ) -> FunctionResult { Ok(match self { RequestedValue::LateBound(late_bound) => { RequestedValue::LateBound(late_bound.map_any(map_shared, map_mutable, map_owned)?) diff --git a/src/expressions/evaluation/node_conversion.rs b/src/expressions/evaluation/node_conversion.rs index 443f9e33..68333d37 100644 --- a/src/expressions/evaluation/node_conversion.rs +++ b/src/expressions/evaluation/node_conversion.rs @@ -23,9 +23,8 @@ impl ExpressionNode { .return_argument_value(Spanned(resolved, variable.span_range()))? } }, - Leaf::TypeProperty(type_property) => { - context.evaluate(|_, ownership| type_property.resolve_spanned(ownership))? - } + Leaf::TypeProperty(type_property) => context + .evaluate(|_, ownership| Ok(type_property.resolve_spanned(ownership)?))?, Leaf::Block(block) => context.evaluate(|interpreter, ownership| { block.evaluate_spanned(interpreter, ownership) })?, @@ -43,7 +42,7 @@ impl ExpressionNode { let span = stream_literal.span_range(); let value = context .interpreter() - .capture_output(|interpreter| stream_literal.interpret(interpreter))?; + .capture_output(|output| stream_literal.output_to_stream(output))?; context.return_value(Spanned(value, span))? } Leaf::ParseTemplateLiteral(consume_literal) => { @@ -83,7 +82,7 @@ impl ExpressionNode { } Leaf::ClosureExpression(closure_expression) => { context.evaluate(|interpreter, ownership| { - closure_expression.evaluate_spanned(interpreter, ownership) + Ok(closure_expression.evaluate_spanned(interpreter, ownership)?) })? } } diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index 66a6c82c..f32972a8 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -95,7 +95,7 @@ impl Clone for DisabledArgumentValue { impl DisabledArgumentValue { /// Re-enables this disabled argument value by re-acquiring any borrow. - pub(crate) fn enable(self, span: SpanRange) -> ExecutionResult { + pub(crate) fn enable(self, span: SpanRange) -> FunctionResult { match self { DisabledArgumentValue::Owned(owned) => Ok(ArgumentValue::Owned(owned)), DisabledArgumentValue::CopyOnWrite(copy_on_write) => { @@ -206,14 +206,14 @@ impl RequestedOwnership { ) } - pub(crate) fn map_none(self, span: SpanRange) -> ExecutionResult> { + pub(crate) fn map_none(self, span: SpanRange) -> FunctionResult> { self.map_from_owned(Spanned(().into_any_value(), span)) } pub(crate) fn map_from_late_bound( &self, Spanned(late_bound, span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(match self { RequestedOwnership::LateBound => RequestedValue::LateBound(late_bound), RequestedOwnership::Concrete(argument_ownership) => { @@ -227,7 +227,7 @@ impl RequestedOwnership { pub(crate) fn map_from_argument( &self, Spanned(value, span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { match value { ArgumentValue::Owned(owned) => self.map_from_owned(Spanned(owned, span)), ArgumentValue::Mutable(mutable) => self.map_from_mutable(Spanned(mutable, span)), @@ -242,7 +242,7 @@ impl RequestedOwnership { pub(crate) fn map_from_returned( &self, Spanned(value, span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { match value { ReturnedValue::Owned(owned) => self.map_from_owned(Spanned(owned, span)), ReturnedValue::Mutable(mutable) => self.map_from_mutable(Spanned(mutable, span)), @@ -257,7 +257,7 @@ impl RequestedOwnership { pub(crate) fn map_from_requested( &self, Spanned(requested, span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { match requested { RequestedValue::Owned(owned) => self.map_from_owned(Spanned(owned, span)), RequestedValue::Shared(shared) => self.map_from_shared(Spanned(shared, span)), @@ -278,7 +278,7 @@ impl RequestedOwnership { pub(crate) fn map_from_owned( &self, Spanned(value, span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(Spanned( match self { RequestedOwnership::LateBound => { @@ -298,7 +298,7 @@ impl RequestedOwnership { pub(crate) fn map_from_copy_on_write( &self, Spanned(cow, span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(Spanned( match self { RequestedOwnership::LateBound => { @@ -315,7 +315,7 @@ impl RequestedOwnership { pub(crate) fn map_from_mutable( &self, Spanned(mutable, span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(Spanned( match self { RequestedOwnership::LateBound => { @@ -332,7 +332,7 @@ impl RequestedOwnership { pub(crate) fn map_from_assignee( &self, Spanned(assignee, span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(Spanned( match self { RequestedOwnership::LateBound => { @@ -349,7 +349,7 @@ impl RequestedOwnership { pub(crate) fn map_from_shared( &self, Spanned(shared, span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(Spanned( match self { RequestedOwnership::LateBound => RequestedValue::LateBound( @@ -421,7 +421,7 @@ impl ArgumentOwnership { pub(crate) fn map_from_late_bound( &self, Spanned(late_bound, span): Spanned, - ) -> ExecutionResult { + ) -> FunctionResult { match late_bound { LateBoundValue::Owned(owned) => self.map_from_owned_with_is_last_use( Spanned(owned.owned, span), @@ -433,17 +433,21 @@ impl ArgumentOwnership { LateBoundValue::Mutable(mutable) => { self.map_from_mutable_inner(Spanned(mutable, span), true) } - LateBoundValue::Shared(late_bound_shared) => self - .map_from_shared_with_error_reason(Spanned(late_bound_shared.shared, span), |_| { - ExecutionInterrupt::ownership_error(late_bound_shared.reason_not_mutable) - }), + LateBoundValue::Shared(late_bound_shared) => self.map_from_shared_with_error_reason( + Spanned(late_bound_shared.shared, span), + |_| { + FunctionError::new(ExecutionInterrupt::ownership_error( + late_bound_shared.reason_not_mutable, + )) + }, + ), } } pub(crate) fn map_from_copy_on_write( &self, Spanned(copy_on_write, span): Spanned, - ) -> ExecutionResult { + ) -> FunctionResult { match self { ArgumentOwnership::Owned => Ok(ArgumentValue::Owned( copy_on_write.clone_to_owned_transparently(span)?, @@ -474,7 +478,7 @@ impl ArgumentOwnership { pub(crate) fn map_from_shared( &self, shared: Spanned, - ) -> ExecutionResult { + ) -> FunctionResult { self.map_from_shared_with_error_reason( shared, |span| span.ownership_error("A mutable reference is required, but a shared reference was received, this indicates a possible bug as the updated value won't be accessible. To proceed regardless, use `.clone().as_mut()` to get a mutable reference."), @@ -484,8 +488,8 @@ impl ArgumentOwnership { fn map_from_shared_with_error_reason( &self, Spanned(shared, span): Spanned, - mutable_error: impl FnOnce(SpanRange) -> ExecutionInterrupt, - ) -> ExecutionResult { + mutable_error: impl FnOnce(SpanRange) -> FunctionError, + ) -> FunctionResult { match self { ArgumentOwnership::Owned => Ok(ArgumentValue::Owned( Spanned(shared, span).transparent_clone()?, @@ -504,14 +508,14 @@ impl ArgumentOwnership { pub(crate) fn map_from_mutable( &self, spanned_mutable: Spanned, - ) -> ExecutionResult { + ) -> FunctionResult { self.map_from_mutable_inner(spanned_mutable, false) } pub(crate) fn map_from_assignee( &self, Spanned(assignee, span): Spanned, - ) -> ExecutionResult { + ) -> FunctionResult { self.map_from_mutable_inner(Spanned(assignee.0, span), false) } @@ -519,7 +523,7 @@ impl ArgumentOwnership { &self, Spanned(mutable, span): Spanned, is_late_bound: bool, - ) -> ExecutionResult { + ) -> FunctionResult { match self { ArgumentOwnership::Owned => { if is_late_bound { @@ -541,10 +545,7 @@ impl ArgumentOwnership { } } - pub(crate) fn map_from_owned( - &self, - owned: Spanned, - ) -> ExecutionResult { + pub(crate) fn map_from_owned(&self, owned: Spanned) -> FunctionResult { self.map_from_owned_with_is_last_use(owned, false) } @@ -552,7 +553,7 @@ impl ArgumentOwnership { &self, Spanned(owned, span): Spanned, is_from_last_use: bool, - ) -> ExecutionResult { + ) -> FunctionResult { match self { ArgumentOwnership::Owned | ArgumentOwnership::AsIs => Ok(ArgumentValue::Owned(owned)), ArgumentOwnership::CopyOnWrite => { @@ -831,7 +832,7 @@ impl EvaluationFrame for UnaryOperationBuilder { fn handle_next( self, - context: ValueContext, + mut context: ValueContext, operand: Spanned, ) -> ExecutionResult { let operand = operand.expect_late_bound(); @@ -844,8 +845,11 @@ impl EvaluationFrame for UnaryOperationBuilder { { let operand_span = operand.span_range(); let resolved_value = operand.resolve(interface.argument_ownership())?; - let result = - interface.execute(Spanned(resolved_value, operand_span), &self.operation)?; + let result = interface.execute( + Spanned(resolved_value, operand_span), + &self.operation, + context.interpreter(), + )?; return context.return_returned_value(result); } self.operation.type_err(format!( @@ -1488,7 +1492,7 @@ impl EvaluationFrame for InvocationBuilder { let span = arg.1; arg.try_map(|v| v.enable(span)) }) - .collect::>>()?; + .collect::>>()?; (arguments, invokable, function_span) } }; diff --git a/src/expressions/expression_block.rs b/src/expressions/expression_block.rs index 91ee3c9c..a46c22e7 100644 --- a/src/expressions/expression_block.rs +++ b/src/expressions/expression_block.rs @@ -29,13 +29,16 @@ impl HasSpanRange for EmbeddedExpression { } } -impl Interpret for EmbeddedExpression { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { - let value = self.content.evaluate_shared(interpreter)?; - value.as_ref_value().output_to( - Grouping::Flattened, - &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), - ) +impl OutputToStream for EmbeddedExpression { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { + let value = output.with_interpreter(|i| self.content.evaluate_shared(i))?; + value + .as_ref_value() + .output_to( + Grouping::Flattened, + &mut ToStreamContext::new(output, self.span_range()), + ) + .into_execution_result() } } @@ -68,17 +71,22 @@ impl HasSpanRange for EmbeddedStatements { } } -impl Interpret for EmbeddedStatements { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { - let value = self - .content - .evaluate_spanned(interpreter, self.span_range(), RequestedOwnership::shared())? +impl OutputToStream for EmbeddedStatements { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { + let value = output + .with_interpreter(|i| { + self.content + .evaluate_spanned(i, self.span_range(), RequestedOwnership::shared()) + })? .0 .expect_shared(); - value.as_ref_value().output_to( - Grouping::Flattened, - &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), - ) + value + .as_ref_value() + .output_to( + Grouping::Flattened, + &mut ToStreamContext::new(output, self.span_range()), + ) + .into_execution_result() } } @@ -332,6 +340,6 @@ impl ExpressionBlockContent { statement.evaluate_as_statement(interpreter)?; } } - ownership.map_from_owned(Spanned(().into_any_value(), output_span)) + Ok(ownership.map_none(output_span)?) } } diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index 13c30884..3c7433e6 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -85,13 +85,14 @@ impl UnaryOperation { pub(super) fn evaluate( &self, Spanned(input, input_span): Spanned, - ) -> ExecutionResult> { + interpreter: &mut Interpreter, + ) -> FunctionResult> { let input = input.into_any_value(); let method = input .kind() .feature_resolver() .resolve_unary_operation(self) - .ok_or_else(|| { + .ok_or_else(|| -> FunctionError { self.type_error(format!( "The {} operator is not supported for {} operand", self, @@ -101,7 +102,7 @@ impl UnaryOperation { let input = method .argument_ownership .map_from_owned(Spanned(input, input_span))?; - method.execute(Spanned(input, input_span), self) + method.execute(Spanned(input, input_span), self, interpreter) } } @@ -305,7 +306,7 @@ impl BinaryOperation { &self, Spanned(left, left_span): Spanned, Spanned(right, right_span): Spanned, - ) -> ExecutionResult> { + ) -> FunctionResult> { let left = left.into_any_value(); let right = right.into_any_value(); match left @@ -418,7 +419,7 @@ pub(super) trait HandleBinaryOperation: Sized + std::fmt::Display + Copy { context: BinaryOperationCallContext, lhs: Self, rhs: impl std::fmt::Display, - ) -> ExecutionInterrupt { + ) -> FunctionError { context.error(format!( "The {} operation {} {} {} overflowed", Self::type_name(), @@ -433,7 +434,7 @@ pub(super) trait HandleBinaryOperation: Sized + std::fmt::Display + Copy { rhs: impl ResolveAs>, context: BinaryOperationCallContext, perform_fn: fn(Self, Self) -> Option, - ) -> ExecutionResult { + ) -> FunctionResult { let lhs = self; let rhs = rhs.resolve_as("This operand")?; perform_fn(lhs, rhs.0) @@ -445,7 +446,7 @@ pub(super) trait HandleBinaryOperation: Sized + std::fmt::Display + Copy { self, rhs: impl ResolveAs>, perform_fn: fn(Self, Self) -> Self, - ) -> ExecutionResult { + ) -> FunctionResult { let lhs = self; let rhs = rhs.resolve_as("This operand")?; Ok(perform_fn(lhs, rhs.0).into()) @@ -455,7 +456,7 @@ pub(super) trait HandleBinaryOperation: Sized + std::fmt::Display + Copy { self, rhs: impl ResolveAs>, compare_fn: fn(Self, Self) -> bool, - ) -> ExecutionResult { + ) -> FunctionResult { let lhs = self; let rhs = rhs.resolve_as("This operand")?; Ok(compare_fn(lhs, rhs.0)) @@ -466,7 +467,7 @@ pub(super) trait HandleBinaryOperation: Sized + std::fmt::Display + Copy { rhs: u32, context: BinaryOperationCallContext, perform_fn: impl FnOnce(Self, u32) -> Option, - ) -> ExecutionResult { + ) -> FunctionResult { let lhs = self; perform_fn(lhs, rhs) .map(|r| r.into()) diff --git a/src/expressions/statements.rs b/src/expressions/statements.rs index e0d4fca3..55187bcf 100644 --- a/src/expressions/statements.rs +++ b/src/expressions/statements.rs @@ -369,9 +369,13 @@ impl EmitStatement { interpreter: &mut Interpreter, ) -> ExecutionResult<()> { let Spanned(value, span) = self.expression.evaluate_owned(interpreter)?; - value.as_ref_value().output_to( - Grouping::Flattened, - &mut ToStreamContext::new(interpreter.output(&self.emit)?, span), - ) + let mut output = OutputInterpreter::new_checked(interpreter, &self.emit)?; + value + .as_ref_value() + .output_to( + Grouping::Flattened, + &mut ToStreamContext::new(&mut output, span), + ) + .into_execution_result() } } diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index ff442295..57f97a28 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -33,7 +33,7 @@ impl<'a> ResolutionContext<'a> { &self, articled_expected_value_kind: &str, value: V, - ) -> ExecutionResult { + ) -> FunctionResult { self.span_range.type_err(format!( "{} is expected to be {}, but it is {}", self.resolution_target, @@ -46,14 +46,14 @@ impl<'a> ResolutionContext<'a> { pub(crate) trait IsArgument: Sized { type ValueType: TypeData; const OWNERSHIP: ArgumentOwnership; - fn from_argument(value: Spanned) -> ExecutionResult; + fn from_argument(value: Spanned) -> FunctionResult; } impl IsArgument for ArgumentValue { type ValueType = AnyType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; - fn from_argument(Spanned(value, _): Spanned) -> ExecutionResult { + fn from_argument(Spanned(value, _): Spanned) -> FunctionResult { Ok(value) } } @@ -62,7 +62,7 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgume type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; - fn from_argument(argument: Spanned) -> ExecutionResult { + fn from_argument(argument: Spanned) -> FunctionResult { T::resolve_shared(argument.expect_shared(), "This argument") } } @@ -85,7 +85,7 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgum type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Assignee { auto_create: false }; - fn from_argument(argument: Spanned) -> ExecutionResult { + fn from_argument(argument: Spanned) -> FunctionResult { T::resolve_assignee(argument.expect_assignee(), "This argument") } } @@ -94,7 +94,7 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgum type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; - fn from_argument(argument: Spanned) -> ExecutionResult { + fn from_argument(argument: Spanned) -> FunctionResult { T::resolve_mutable(argument.expect_mutable(), "This argument") } } @@ -128,7 +128,7 @@ where type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; - fn from_argument(Spanned(value, span): Spanned) -> ExecutionResult { + fn from_argument(Spanned(value, span): Spanned) -> FunctionResult { value.expect_copy_on_write().map( |v| T::resolve_shared(v.spanned(span), "This argument"), |v| { @@ -145,7 +145,7 @@ impl IsArgument for Spanned { type ValueType = ::ValueType; const OWNERSHIP: ArgumentOwnership = ::OWNERSHIP; - fn from_argument(argument: Spanned) -> ExecutionResult { + fn from_argument(argument: Spanned) -> FunctionResult { let span = argument.1; Ok(Spanned(T::from_argument(argument)?, span)) } @@ -153,26 +153,26 @@ impl IsArgument for Spanned { pub(crate) trait ResolveAs { /// The `resolution_target` should be capitalized, e.g. "This argument" or "The value destructed with an object pattern" - fn resolve_as(self, resolution_target: &str) -> ExecutionResult; + fn resolve_as(self, resolution_target: &str) -> FunctionResult; } // Sadly this can't be changed Value => V because of spurious issues with // https://github.com/rust-lang/rust/issues/48869 // Instead, we could introduce a different trait ResolveAs2 if needed. impl> ResolveAs for Spanned { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult { + fn resolve_as(self, resolution_target: &str) -> FunctionResult { T::resolve_value(self, resolution_target) } } impl + ?Sized> ResolveAs> for Spanned { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult> { T::resolve_shared(self, resolution_target) } } impl<'a, T: ResolvableShared + ?Sized> ResolveAs<&'a T> for Spanned<&'a AnyValue> { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult<&'a T> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult<&'a T> { T::resolve_ref(self, resolution_target) } } @@ -180,13 +180,13 @@ impl<'a, T: ResolvableShared + ?Sized> ResolveAs<&'a T> for Spanned<&' impl<'a, T: ResolvableShared + ?Sized> ResolveAs> for Spanned<&'a AnyValue> { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult> { T::resolve_spanned_ref(self, resolution_target) } } impl + ?Sized> ResolveAs> for Spanned { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult> { T::resolve_mutable(self, resolution_target) } } @@ -194,7 +194,7 @@ impl + ?Sized> ResolveAs> for Spanned< impl<'a, T: ResolvableMutable + ?Sized> ResolveAs<&'a mut T> for Spanned<&'a mut AnyValue> { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult<&'a mut T> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult<&'a mut T> { T::resolve_ref_mut(self, resolution_target) } } @@ -202,7 +202,7 @@ impl<'a, T: ResolvableMutable + ?Sized> ResolveAs<&'a mut T> impl<'a, T: ResolvableMutable + ?Sized> ResolveAs> for Spanned<&'a mut AnyValue> { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult> { T::resolve_spanned_ref_mut(self, resolution_target) } } @@ -212,12 +212,12 @@ pub(crate) trait ResolvableArgumentTarget { } pub(crate) trait ResolvableOwned: Sized { - fn resolve_from_value(value: T, context: ResolutionContext) -> ExecutionResult; + fn resolve_from_value(value: T, context: ResolutionContext) -> FunctionResult; fn resolve_spanned_from_value( value: T, context: ResolutionContext, - ) -> ExecutionResult>> { + ) -> FunctionResult>> { let span_range = context.span_range; Self::resolve_from_value(value, context).map(|x| x.spanned(*span_range)) } @@ -226,7 +226,7 @@ pub(crate) trait ResolvableOwned: Sized { fn resolve_value( Spanned(value, span): Spanned, resolution_target: &str, - ) -> ExecutionResult { + ) -> FunctionResult { let context = ResolutionContext { span_range: &span, resolution_target, @@ -236,13 +236,13 @@ pub(crate) trait ResolvableOwned: Sized { } pub(crate) trait ResolvableShared { - fn resolve_from_ref<'a>(value: &'a T, context: ResolutionContext) -> ExecutionResult<&'a Self>; + fn resolve_from_ref<'a>(value: &'a T, context: ResolutionContext) -> FunctionResult<&'a Self>; /// The `resolution_target` should be capitalized, e.g. "This argument" or "The value destructed with an object pattern" fn resolve_shared( Spanned(value, span): Spanned>, resolution_target: &str, - ) -> ExecutionResult> { + ) -> FunctionResult> { value .try_map(|v| { Self::resolve_from_ref( @@ -259,7 +259,7 @@ pub(crate) trait ResolvableShared { fn resolve_ref<'a>( Spanned(value, span): Spanned<&'a T>, resolution_target: &str, - ) -> ExecutionResult<&'a Self> { + ) -> FunctionResult<&'a Self> { Self::resolve_from_ref( value, ResolutionContext { @@ -272,7 +272,7 @@ pub(crate) trait ResolvableShared { fn resolve_spanned_ref<'a>( Spanned(value, span): Spanned<&'a T>, resolution_target: &str, - ) -> ExecutionResult> { + ) -> FunctionResult> { Spanned(value, span).try_map(|v| { Self::resolve_from_ref( v, @@ -289,12 +289,12 @@ pub(crate) trait ResolvableMutable { fn resolve_from_mut<'a>( value: &'a mut T, context: ResolutionContext, - ) -> ExecutionResult<&'a mut Self>; + ) -> FunctionResult<&'a mut Self>; fn resolve_assignee( Spanned(value, span): Spanned>, resolution_target: &str, - ) -> ExecutionResult> { + ) -> FunctionResult> { Ok(Assignee(Self::resolve_mutable( Spanned(value.0, span), resolution_target, @@ -304,7 +304,7 @@ pub(crate) trait ResolvableMutable { fn resolve_mutable( Spanned(value, span): Spanned>, resolution_target: &str, - ) -> ExecutionResult> { + ) -> FunctionResult> { value .try_map(|v| { Self::resolve_from_mut( @@ -321,7 +321,7 @@ pub(crate) trait ResolvableMutable { fn resolve_ref_mut<'a>( Spanned(value, span): Spanned<&'a mut T>, resolution_target: &str, - ) -> ExecutionResult<&'a mut Self> { + ) -> FunctionResult<&'a mut Self> { Self::resolve_from_mut( value, ResolutionContext { @@ -334,7 +334,7 @@ pub(crate) trait ResolvableMutable { fn resolve_spanned_ref_mut<'a>( Spanned(value, span): Spanned<&'a mut T>, resolution_target: &str, - ) -> ExecutionResult> { + ) -> FunctionResult> { Spanned(value, span).try_map(|value| { Self::resolve_from_mut( value, @@ -352,7 +352,7 @@ impl ResolvableArgumentTarget for AnyValue { } impl ResolvableOwned for AnyValue { - fn resolve_from_value(value: AnyValue, _context: ResolutionContext) -> ExecutionResult { + fn resolve_from_value(value: AnyValue, _context: ResolutionContext) -> FunctionResult { Ok(value) } } @@ -360,7 +360,7 @@ impl ResolvableShared for AnyValue { fn resolve_from_ref<'a>( value: &'a AnyValue, _context: ResolutionContext, - ) -> ExecutionResult<&'a Self> { + ) -> FunctionResult<&'a Self> { Ok(value) } } @@ -368,7 +368,7 @@ impl ResolvableMutable for AnyValue { fn resolve_from_mut<'a>( value: &'a mut AnyValue, _context: ResolutionContext, - ) -> ExecutionResult<&'a mut Self> { + ) -> FunctionResult<&'a mut Self> { Ok(value) } } @@ -383,7 +383,7 @@ macro_rules! impl_resolvable_argument_for { fn resolve_from_value( $value: AnyValue, $context: ResolutionContext, - ) -> ExecutionResult { + ) -> FunctionResult { $body } } @@ -392,7 +392,7 @@ macro_rules! impl_resolvable_argument_for { fn resolve_from_ref<'a>( $value: &'a AnyValue, $context: ResolutionContext, - ) -> ExecutionResult<&'a Self> { + ) -> FunctionResult<&'a Self> { $body } } @@ -401,7 +401,7 @@ macro_rules! impl_resolvable_argument_for { fn resolve_from_mut<'a>( $value: &'a mut AnyValue, $context: ResolutionContext, - ) -> ExecutionResult<&'a mut Self> { + ) -> FunctionResult<&'a mut Self> { $body } } @@ -420,7 +420,7 @@ macro_rules! impl_delegated_resolvable_argument_for { fn resolve_from_value( input_value: Value, context: ResolutionContext, - ) -> ExecutionResult { + ) -> FunctionResult { let $value: $delegate = ResolvableOwned::::resolve_from_value(input_value, context)?; Ok($expr) @@ -431,7 +431,7 @@ macro_rules! impl_delegated_resolvable_argument_for { fn resolve_from_ref<'a>( input_value: &'a Value, context: ResolutionContext, - ) -> ExecutionResult<&'a Self> { + ) -> FunctionResult<&'a Self> { let $value: &$delegate = ResolvableShared::::resolve_from_ref(input_value, context)?; Ok(&$expr) @@ -442,7 +442,7 @@ macro_rules! impl_delegated_resolvable_argument_for { fn resolve_from_mut<'a>( input_value: &'a mut Value, context: ResolutionContext, - ) -> ExecutionResult<&'a mut Self> { + ) -> FunctionResult<&'a mut Self> { let $value: &mut $delegate = ResolvableMutable::::resolve_from_mut(input_value, context)?; Ok(&mut $expr) diff --git a/src/expressions/type_resolution/interface_macros.rs b/src/expressions/type_resolution/interface_macros.rs index 54f7490d..245b0bb0 100644 --- a/src/expressions/type_resolution/interface_macros.rs +++ b/src/expressions/type_resolution/interface_macros.rs @@ -157,7 +157,7 @@ macro_rules! parse_arg_types { pub(crate) fn apply_fn0( f: fn(&mut FunctionCallContext) -> R, context: &mut FunctionCallContext, -) -> ExecutionResult +) -> FunctionResult where R: IsReturnable, { @@ -169,7 +169,7 @@ pub(crate) fn apply_fn1( f: fn(&mut FunctionCallContext, A) -> R, a: Spanned, context: &mut FunctionCallContext, -) -> ExecutionResult +) -> FunctionResult where A: IsArgument, R: IsReturnable, @@ -183,7 +183,7 @@ pub(crate) fn apply_fn1_optional1( a: Spanned, b: Option>, context: &mut FunctionCallContext, -) -> ExecutionResult +) -> FunctionResult where A: IsArgument, B: IsArgument, @@ -203,7 +203,7 @@ pub(crate) fn apply_fn2( a: Spanned, b: Spanned, context: &mut FunctionCallContext, -) -> ExecutionResult +) -> FunctionResult where A: IsArgument, B: IsArgument, @@ -218,7 +218,7 @@ pub(crate) fn apply_fn2_optional1( b: Spanned, c: Option>, context: &mut FunctionCallContext, -) -> ExecutionResult +) -> FunctionResult where A: IsArgument, B: IsArgument, @@ -241,7 +241,7 @@ pub(crate) fn apply_fn3( b: Spanned, c: Spanned, context: &mut FunctionCallContext, -) -> ExecutionResult +) -> FunctionResult where A: IsArgument, B: IsArgument, @@ -264,7 +264,7 @@ pub(crate) fn apply_fn3_optional1( c: Spanned, d: Option>, context: &mut FunctionCallContext, -) -> ExecutionResult +) -> FunctionResult where A: IsArgument, B: IsArgument, @@ -286,7 +286,7 @@ pub(crate) fn apply_unary_fn( f: fn(UnaryOperationCallContext, A) -> R, a: Spanned, context: UnaryOperationCallContext, -) -> ExecutionResult +) -> FunctionResult where A: IsArgument, R: IsReturnable, @@ -299,7 +299,7 @@ pub(crate) fn apply_binary_fn( lhs: Spanned, rhs: Spanned, context: BinaryOperationCallContext, -) -> ExecutionResult +) -> FunctionResult where A: IsArgument, B: IsArgument, @@ -313,10 +313,10 @@ where // ============================================================================ pub(crate) fn apply_property_shared<'a, S: ResolvableShared + ?Sized + 'a>( - f: for<'b> fn(PropertyAccessCallContext, &'b S) -> ExecutionResult<&'b AnyValue>, + f: for<'b> fn(PropertyAccessCallContext, &'b S) -> FunctionResult<&'b AnyValue>, ctx: PropertyAccessCallContext, source: &'a AnyValue, -) -> ExecutionResult<&'a AnyValue> { +) -> FunctionResult<&'a AnyValue> { let source = S::resolve_from_ref( source, ResolutionContext::new(&ctx.property.span_range(), "The property access source"), @@ -325,11 +325,11 @@ pub(crate) fn apply_property_shared<'a, S: ResolvableShared + ?Sized + } pub(crate) fn apply_property_mutable<'a, S: ResolvableMutable + ?Sized + 'a>( - f: for<'b> fn(PropertyAccessCallContext, &'b mut S, bool) -> ExecutionResult<&'b mut AnyValue>, + f: for<'b> fn(PropertyAccessCallContext, &'b mut S, bool) -> FunctionResult<&'b mut AnyValue>, ctx: PropertyAccessCallContext, source: &'a mut AnyValue, auto_create: bool, -) -> ExecutionResult<&'a mut AnyValue> { +) -> FunctionResult<&'a mut AnyValue> { let source = S::resolve_from_mut( source, ResolutionContext::new(&ctx.property.span_range(), "The property access source"), @@ -338,10 +338,10 @@ pub(crate) fn apply_property_mutable<'a, S: ResolvableMutable + ?Sized } pub(crate) fn apply_property_owned>( - f: fn(PropertyAccessCallContext, S) -> ExecutionResult, + f: fn(PropertyAccessCallContext, S) -> FunctionResult, ctx: PropertyAccessCallContext, source: AnyValue, -) -> ExecutionResult { +) -> FunctionResult { let source = S::resolve_from_value( source, ResolutionContext::new(&ctx.property.span_range(), "The property access source"), @@ -358,11 +358,11 @@ pub(crate) fn apply_index_shared<'a, S: ResolvableShared + ?Sized + 'a IndexAccessCallContext, &'b S, Spanned, - ) -> ExecutionResult<&'b AnyValue>, + ) -> FunctionResult<&'b AnyValue>, ctx: IndexAccessCallContext, source: &'a AnyValue, index: Spanned, -) -> ExecutionResult<&'a AnyValue> { +) -> FunctionResult<&'a AnyValue> { let source = S::resolve_from_ref( source, ResolutionContext::new(&ctx.access.span_range(), "The index access source"), @@ -376,12 +376,12 @@ pub(crate) fn apply_index_mutable<'a, S: ResolvableMutable + ?Sized + &'b mut S, Spanned, bool, - ) -> ExecutionResult<&'b mut AnyValue>, + ) -> FunctionResult<&'b mut AnyValue>, ctx: IndexAccessCallContext, source: &'a mut AnyValue, index: Spanned, auto_create: bool, -) -> ExecutionResult<&'a mut AnyValue> { +) -> FunctionResult<&'a mut AnyValue> { let source = S::resolve_from_mut( source, ResolutionContext::new(&ctx.access.span_range(), "The index access source"), @@ -390,11 +390,11 @@ pub(crate) fn apply_index_mutable<'a, S: ResolvableMutable + ?Sized + } pub(crate) fn apply_index_owned>( - f: fn(IndexAccessCallContext, S, Spanned) -> ExecutionResult, + f: fn(IndexAccessCallContext, S, Spanned) -> FunctionResult, ctx: IndexAccessCallContext, source: AnyValue, index: Spanned, -) -> ExecutionResult { +) -> FunctionResult { let source = S::resolve_from_value( source, ResolutionContext::new(&ctx.access.span_range(), "The index access source"), @@ -413,9 +413,9 @@ impl<'a> HasSpanRange for FunctionCallContext<'a> { } } -#[derive(Clone, Copy)] pub(crate) struct UnaryOperationCallContext<'a> { pub operation: &'a UnaryOperation, + pub interpreter: &'a mut Interpreter, } #[derive(Clone, Copy)] @@ -425,11 +425,11 @@ pub(crate) struct BinaryOperationCallContext<'a> { impl<'a> BinaryOperationCallContext<'a> { #[allow(unused)] - pub(crate) fn err(&self, message: impl std::fmt::Display) -> ExecutionResult { + pub(crate) fn err(&self, message: impl std::fmt::Display) -> FunctionResult { self.operation.value_err(message) } - pub(crate) fn error(&self, message: impl std::fmt::Display) -> ExecutionInterrupt { + pub(crate) fn error(&self, message: impl std::fmt::Display) -> FunctionError { self.operation.value_error(message) } } @@ -601,11 +601,11 @@ macro_rules! define_type_features { #[allow(unused)] use super::*; - pub(crate) fn shared<'a>(if_empty!([$($property_shared_context)?][_ctx]): PropertyAccessCallContext, $($property_shared_args)*) -> ExecutionResult<&'a AnyValue> $property_shared_body + pub(crate) fn shared<'a>(if_empty!([$($property_shared_context)?][_ctx]): PropertyAccessCallContext, $($property_shared_args)*) -> FunctionResult<&'a AnyValue> $property_shared_body - pub(crate) fn mutable<'a>(if_empty!([$($property_mutable_context)?][_ctx]): PropertyAccessCallContext, $($property_mutable_args)*) -> ExecutionResult<&'a mut AnyValue> $property_mutable_body + pub(crate) fn mutable<'a>(if_empty!([$($property_mutable_context)?][_ctx]): PropertyAccessCallContext, $($property_mutable_args)*) -> FunctionResult<&'a mut AnyValue> $property_mutable_body - pub(crate) fn owned(if_empty!([$($property_owned_context)?][_ctx]): PropertyAccessCallContext, $($property_owned_args)*) -> ExecutionResult $property_owned_body + pub(crate) fn owned(if_empty!([$($property_owned_context)?][_ctx]): PropertyAccessCallContext, $($property_owned_args)*) -> FunctionResult $property_owned_body } pub(crate) fn property_access_interface() -> PropertyAccessInterface { @@ -622,11 +622,11 @@ macro_rules! define_type_features { #[allow(unused)] use super::*; - pub(crate) fn shared<'a>(if_empty!([$($index_shared_context)?][_ctx]): IndexAccessCallContext, $($index_shared_args)*) -> ExecutionResult<&'a AnyValue> $index_shared_body + pub(crate) fn shared<'a>(if_empty!([$($index_shared_context)?][_ctx]): IndexAccessCallContext, $($index_shared_args)*) -> FunctionResult<&'a AnyValue> $index_shared_body - pub(crate) fn mutable<'a>(if_empty!([$($index_mutable_context)?][_ctx]): IndexAccessCallContext, $($index_mutable_args)*) -> ExecutionResult<&'a mut AnyValue> $index_mutable_body + pub(crate) fn mutable<'a>(if_empty!([$($index_mutable_context)?][_ctx]): IndexAccessCallContext, $($index_mutable_args)*) -> FunctionResult<&'a mut AnyValue> $index_mutable_body - pub(crate) fn owned(if_empty!([$($index_owned_context)?][_ctx]): IndexAccessCallContext, $($index_owned_args)*) -> ExecutionResult $index_owned_body + pub(crate) fn owned(if_empty!([$($index_owned_context)?][_ctx]): IndexAccessCallContext, $($index_owned_args)*) -> FunctionResult $index_owned_body } pub(crate) fn index_access_interface() -> IndexAccessInterface { diff --git a/src/expressions/type_resolution/outputs.rs b/src/expressions/type_resolution/outputs.rs index cd823619..74a66561 100644 --- a/src/expressions/type_resolution/outputs.rs +++ b/src/expressions/type_resolution/outputs.rs @@ -17,58 +17,29 @@ pub(crate) enum ReturnedValue { // note = "`ResolvableOutput` is not implemented for `Shared` or `Mutable` unless `X` is `Value`. If we wish to change this, we'd need to have some way to represent some kind of `ExpressionReference`, i.e. a `Typed>` rather than a `Shared>`" // )] pub(crate) trait IsReturnable { - fn to_returned_value(self) -> ExecutionResult; + fn to_returned_value(self) -> FunctionResult; } impl IsReturnable for ReturnedValue { - fn to_returned_value(self) -> ExecutionResult { + fn to_returned_value(self) -> FunctionResult { Ok(self) } } impl IsReturnable for AnyValueShared { - fn to_returned_value(self) -> ExecutionResult { + fn to_returned_value(self) -> FunctionResult { Ok(ReturnedValue::Shared(self)) } } impl IsReturnable for AnyValueMutable { - fn to_returned_value(self) -> ExecutionResult { + fn to_returned_value(self) -> FunctionResult { Ok(ReturnedValue::Mutable(self)) } } -impl IsReturnable for ExecutionResult { - fn to_returned_value(self) -> ExecutionResult { +impl IsReturnable for FunctionResult { + fn to_returned_value(self) -> FunctionResult { self?.to_returned_value() } } - -pub(crate) trait StreamAppender { - fn append(self, output: &mut OutputStream) -> ExecutionResult<()>; -} - -impl ExecutionResult<()>> StreamAppender for F { - fn append(self, output: &mut OutputStream) -> ExecutionResult<()> { - self(output) - } -} - -pub(crate) struct StreamOutput(T); -impl ExecutionResult<()>> StreamOutput { - pub fn new(appender: F) -> Self { - Self(appender) - } -} -impl From for StreamOutput { - fn from(value: T) -> Self { - Self(value) - } -} -impl IsReturnable for StreamOutput { - fn to_returned_value(self) -> ExecutionResult { - let mut output = OutputStream::new(); - self.0.append(&mut output)?; - output.to_returned_value() - } -} diff --git a/src/expressions/type_resolution/type_data.rs b/src/expressions/type_resolution/type_data.rs index 9ebd2726..c31cd275 100644 --- a/src/expressions/type_resolution/type_data.rs +++ b/src/expressions/type_resolution/type_data.rs @@ -86,12 +86,12 @@ pub(crate) trait TypeData { #[derive(Clone)] pub(crate) enum FunctionInterface { Arity0 { - method: fn(&mut FunctionCallContext) -> ExecutionResult, + method: fn(&mut FunctionCallContext) -> FunctionResult, argument_ownership: [ArgumentOwnership; 0], }, Arity1 { method: - fn(&mut FunctionCallContext, Spanned) -> ExecutionResult, + fn(&mut FunctionCallContext, Spanned) -> FunctionResult, argument_ownership: [ArgumentOwnership; 1], }, /// 1 argument, 1 optional argument @@ -100,7 +100,7 @@ pub(crate) enum FunctionInterface { &mut FunctionCallContext, Spanned, Option>, - ) -> ExecutionResult, + ) -> FunctionResult, argument_ownership: [ArgumentOwnership; 2], }, Arity2 { @@ -108,7 +108,7 @@ pub(crate) enum FunctionInterface { &mut FunctionCallContext, Spanned, Spanned, - ) -> ExecutionResult, + ) -> FunctionResult, argument_ownership: [ArgumentOwnership; 2], }, Arity2PlusOptional1 { @@ -117,7 +117,7 @@ pub(crate) enum FunctionInterface { Spanned, Spanned, Option>, - ) -> ExecutionResult, + ) -> FunctionResult, argument_ownership: [ArgumentOwnership; 3], }, Arity3 { @@ -126,7 +126,7 @@ pub(crate) enum FunctionInterface { Spanned, Spanned, Spanned, - ) -> ExecutionResult, + ) -> FunctionResult, argument_ownership: [ArgumentOwnership; 3], }, Arity3PlusOptional1 { @@ -136,14 +136,14 @@ pub(crate) enum FunctionInterface { Spanned, Spanned, Option>, - ) -> ExecutionResult, + ) -> FunctionResult, argument_ownership: [ArgumentOwnership; 4], }, ArityAny { method: fn( &mut FunctionCallContext, Vec>, - ) -> ExecutionResult, + ) -> FunctionResult, argument_ownership: Vec, }, } @@ -153,7 +153,7 @@ impl FunctionInterface { &self, arguments: Vec>, context: &mut FunctionCallContext, - ) -> ExecutionResult> { + ) -> FunctionResult> { let output_value = match self { FunctionInterface::Arity0 { method, .. } => { if !arguments.is_empty() { @@ -268,7 +268,7 @@ impl FunctionInterface { pub(crate) struct UnaryOperationInterface { pub method: - fn(UnaryOperationCallContext, Spanned) -> ExecutionResult, + fn(UnaryOperationCallContext, Spanned) -> FunctionResult, pub argument_ownership: ArgumentOwnership, } @@ -277,10 +277,14 @@ impl UnaryOperationInterface { &self, Spanned(input, input_span): Spanned, operation: &UnaryOperation, - ) -> ExecutionResult> { + interpreter: &mut Interpreter, + ) -> FunctionResult> { let output_span_range = operation.output_span_range(input_span); Ok((self.method)( - UnaryOperationCallContext { operation }, + UnaryOperationCallContext { + operation, + interpreter, + }, Spanned(input, input_span), )? .spanned(output_span_range)) @@ -296,7 +300,7 @@ pub(crate) struct BinaryOperationInterface { BinaryOperationCallContext, Spanned, Spanned, - ) -> ExecutionResult, + ) -> FunctionResult, pub lhs_ownership: ArgumentOwnership, pub rhs_ownership: ArgumentOwnership, } @@ -307,7 +311,7 @@ impl BinaryOperationInterface { Spanned(lhs, lhs_span): Spanned, Spanned(rhs, rhs_span): Spanned, operation: &BinaryOperation, - ) -> ExecutionResult> { + ) -> FunctionResult> { let output_span_range = SpanRange::new_between(lhs_span, rhs_span); Ok((self.method)( BinaryOperationCallContext { operation }, @@ -344,15 +348,15 @@ pub(crate) struct PropertyAccessCallContext<'a> { pub(crate) struct PropertyAccessInterface { /// Access a property by shared reference. pub shared_access: - for<'a> fn(PropertyAccessCallContext, &'a AnyValue) -> ExecutionResult<&'a AnyValue>, + for<'a> fn(PropertyAccessCallContext, &'a AnyValue) -> FunctionResult<&'a AnyValue>, /// Access a property by mutable reference, optionally auto-creating if missing. pub mutable_access: for<'a> fn( PropertyAccessCallContext, &'a mut AnyValue, bool, - ) -> ExecutionResult<&'a mut AnyValue>, + ) -> FunctionResult<&'a mut AnyValue>, /// Extract a property from an owned value. - pub owned_access: fn(PropertyAccessCallContext, AnyValue) -> ExecutionResult, + pub owned_access: fn(PropertyAccessCallContext, AnyValue) -> FunctionResult, } // ============================================================================ @@ -377,15 +381,15 @@ pub(crate) struct IndexAccessInterface { IndexAccessCallContext, &'a AnyValue, Spanned, - ) -> ExecutionResult<&'a AnyValue>, + ) -> FunctionResult<&'a AnyValue>, /// Access an element by mutable reference, optionally auto-creating if missing. pub mutable_access: for<'a> fn( IndexAccessCallContext, &'a mut AnyValue, Spanned, bool, - ) -> ExecutionResult<&'a mut AnyValue>, + ) -> FunctionResult<&'a mut AnyValue>, /// Extract an element from an owned value. pub owned_access: - fn(IndexAccessCallContext, AnyValue, Spanned) -> ExecutionResult, + fn(IndexAccessCallContext, AnyValue, Spanned) -> FunctionResult, } diff --git a/src/expressions/type_resolution/type_kinds.rs b/src/expressions/type_resolution/type_kinds.rs index e750baf8..df4d25ce 100644 --- a/src/expressions/type_resolution/type_kinds.rs +++ b/src/expressions/type_resolution/type_kinds.rs @@ -237,7 +237,7 @@ impl TypeProperty { pub(crate) fn resolve_spanned( &self, ownership: RequestedOwnership, - ) -> ExecutionResult> { + ) -> FunctionResult> { let resolver = self.source_type.kind.feature_resolver(); // TODO[performance] - lazily initialize properties as Shared let property_name = &self.property.to_string(); diff --git a/src/expressions/values/any_value.rs b/src/expressions/values/any_value.rs index 1af6e259..d3799c35 100644 --- a/src/expressions/values/any_value.rs +++ b/src/expressions/values/any_value.rs @@ -48,7 +48,7 @@ define_type_features! { this.clone_to_owned_infallible() } - fn as_mut(Spanned(this, span): Spanned) -> ExecutionResult { + fn as_mut(Spanned(this, span): Spanned) -> FunctionResult { Ok(match this { ArgumentValue::Owned(owned) => Mutable::new_from_owned(owned), ArgumentValue::CopyOnWrite(copy_on_write) => ArgumentOwnership::Mutable @@ -76,34 +76,40 @@ define_type_features! { core::mem::replace(a.0.deref_mut(), b) } - fn debug(Spanned(this, span_range): Spanned) -> ExecutionResult<()> { - let message = this.as_ref_value().concat_recursive(&ConcatBehaviour::debug(span_range))?; + [context] fn debug(Spanned(this, span_range): Spanned) -> FunctionResult<()> { + let message = this.as_ref_value().concat_recursive(&ConcatBehaviour::debug(span_range), context.interpreter)?; span_range.debug_err(message) } - fn to_debug_string(Spanned(this, span_range): Spanned) -> ExecutionResult { - this.as_ref_value().concat_recursive(&ConcatBehaviour::debug(span_range)) + [context] fn to_debug_string(Spanned(this, span_range): Spanned) -> FunctionResult { + this.as_ref_value().concat_recursive(&ConcatBehaviour::debug(span_range), context.interpreter) } - fn to_stream(Spanned(input, span_range): Spanned) -> ExecutionResult { + [context] fn to_stream(Spanned(input, span_range): Spanned) -> FunctionResult { + let interpreter_ptr = context.interpreter as *mut Interpreter; input.map_into( - |shared| shared.as_ref_value().output_to_new_stream(Grouping::Flattened, span_range), - |owned| owned.into_stream(Grouping::Flattened, span_range), + // SAFETY: map_into only calls one of these two closures, + // so only one mutable reference is active at a time. + |shared| shared.as_ref_value().output_to_new_stream(Grouping::Flattened, span_range, unsafe { &mut *interpreter_ptr }), + |owned| owned.into_stream(Grouping::Flattened, span_range, unsafe { &mut *interpreter_ptr }), ) } - fn to_group(Spanned(input, span_range): Spanned) -> ExecutionResult { + [context] fn to_group(Spanned(input, span_range): Spanned) -> FunctionResult { + let interpreter_ptr = context.interpreter as *mut Interpreter; input.map_into( - |shared| shared.as_ref_value().output_to_new_stream(Grouping::Grouped, span_range), - |owned| owned.into_stream(Grouping::Grouped, span_range), + // SAFETY: map_into only calls one of these two closures, + // so only one mutable reference is active at a time. + |shared| shared.as_ref_value().output_to_new_stream(Grouping::Grouped, span_range, unsafe { &mut *interpreter_ptr }), + |owned| owned.into_stream(Grouping::Grouped, span_range, unsafe { &mut *interpreter_ptr }), ) } - fn to_string(Spanned(input, span_range): Spanned) -> ExecutionResult { - input.as_ref_value().concat_recursive(&ConcatBehaviour::standard(span_range)) + [context] fn to_string(Spanned(input, span_range): Spanned) -> FunctionResult { + input.as_ref_value().concat_recursive(&ConcatBehaviour::standard(span_range), context.interpreter) } - [context] fn with_span(value: Spanned, spans: AnyRef) -> ExecutionResult { + [context] fn with_span(value: Spanned, spans: AnyRef) -> FunctionResult { let mut this = to_stream(context, value)?; let span_to_use = match spans.resolve_content_span_range() { Some(span_range) => span_range.span_from_join_else_start(), @@ -122,51 +128,51 @@ define_type_features! { // EQUALITY METHODS // =============================== // Compare values with strict type checking - errors on value kind mismatch. - [context] fn typed_eq(this: AnyValueAnyRef, other: AnyValueAnyRef) -> ExecutionResult { + [context] fn typed_eq(this: AnyValueAnyRef, other: AnyValueAnyRef) -> FunctionResult { this.as_ref_value().typed_eq(&other.as_ref_value(), context.span_range()) } // STRING-BASED CONVERSION METHODS // =============================== - [context] fn to_ident(this: Spanned) -> ExecutionResult { - let stream = this.into_stream()?; + [context] fn to_ident(this: Spanned) -> FunctionResult { + let stream = this.into_stream(context.interpreter)?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_ident(context, spanned) } - [context] fn to_ident_camel(this: Spanned) -> ExecutionResult { - let stream = this.into_stream()?; + [context] fn to_ident_camel(this: Spanned) -> FunctionResult { + let stream = this.into_stream(context.interpreter)?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_ident_camel(context, spanned) } - [context] fn to_ident_snake(this: Spanned) -> ExecutionResult { - let stream = this.into_stream()?; + [context] fn to_ident_snake(this: Spanned) -> FunctionResult { + let stream = this.into_stream(context.interpreter)?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_ident_snake(context, spanned) } - [context] fn to_ident_upper_snake(this: Spanned) -> ExecutionResult { - let stream = this.into_stream()?; + [context] fn to_ident_upper_snake(this: Spanned) -> FunctionResult { + let stream = this.into_stream(context.interpreter)?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_ident_upper_snake(context, spanned) } // Some literals become Value::UnsupportedLiteral but can still be round-tripped back to a stream - [context] fn to_literal(this: Spanned) -> ExecutionResult { - let stream = this.into_stream()?; + [context] fn to_literal(this: Spanned) -> FunctionResult { + let stream = this.into_stream(context.interpreter)?; let spanned = stream.into_spanned_ref(context.output_span_range); stream_interface::methods::to_literal(context, spanned) } } unary_operations { - fn cast_to_string(Spanned(input, span_range): Spanned) -> ExecutionResult { - input.as_ref_value().concat_recursive(&ConcatBehaviour::standard(span_range)) + [context] fn cast_to_string(Spanned(input, span_range): Spanned) -> FunctionResult { + input.as_ref_value().concat_recursive(&ConcatBehaviour::standard(span_range), context.interpreter) } - fn cast_to_stream(input: Spanned) -> ExecutionResult { - input.into_stream() + [context] fn cast_to_stream(Spanned(input, span_range): Spanned) -> FunctionResult { + input.into_stream(Grouping::Flattened, span_range, context.interpreter) } } binary_operations { @@ -251,7 +257,7 @@ impl AnyValue { pub(crate) fn try_transparent_clone( &self, error_span_range: SpanRange, - ) -> ExecutionResult { + ) -> FunctionResult { if !self.value_kind().supports_transparent_cloning() { return error_span_range.ownership_err(format!( "An owned value is required, but a reference was received, and {} does not support transparent cloning. You may wish to use .clone() explicitly.", @@ -317,18 +323,21 @@ impl AnyValue { self, grouping: Grouping, error_span_range: SpanRange, - ) -> ExecutionResult { + interpreter: &mut Interpreter, + ) -> FunctionResult { match (self, grouping) { (AnyValueContent::Stream(value), Grouping::Flattened) => Ok(value), (AnyValueContent::Stream(value), Grouping::Grouped) => { let mut output: OutputStream = OutputStream::new(); - let span = ToStreamContext::new(&mut output, error_span_range).new_token_span(); + let span = Span::call_site(); output.push_new_group(value, Delimiter::None, span); Ok(output) } - (other, grouping) => other - .as_ref_value() - .output_to_new_stream(grouping, error_span_range), + (other, grouping) => { + other + .as_ref_value() + .output_to_new_stream(grouping, error_span_range, interpreter) + } } } } @@ -338,20 +347,21 @@ impl<'a> AnyValueRef<'a> { self, grouping: Grouping, error_span_range: SpanRange, - ) -> ExecutionResult { - let mut output = OutputStream::new(); - self.output_to( - grouping, - &mut ToStreamContext::new(&mut output, error_span_range), - )?; - Ok(output) + interpreter: &mut Interpreter, + ) -> FunctionResult { + interpreter.capture_output(|output| { + self.output_to( + grouping, + &mut ToStreamContext::new(output, error_span_range), + ) + }) } pub(crate) fn output_to( self, grouping: Grouping, output: &mut ToStreamContext, - ) -> ExecutionResult<()> { + ) -> FunctionResult<()> { match grouping { Grouping::Grouped => { // Grouping can be important for different values, to ensure they're read atomically @@ -367,7 +377,7 @@ impl<'a> AnyValueRef<'a> { Ok(()) } - fn output_flattened_to(self, output: &mut ToStreamContext) -> ExecutionResult<()> { + fn output_flattened_to(self, output: &mut ToStreamContext) -> FunctionResult<()> { match self { AnyValueContent::None(_) => {} AnyValueContent::Integer(value) => { @@ -398,7 +408,7 @@ impl<'a> AnyValueRef<'a> { return output.type_err("Objects cannot be output to a stream"); } AnyValueContent::Array(array) => array.output_items_to(output, Grouping::Flattened)?, - AnyValueContent::Stream(value) => value.append_cloned_into(output.output_stream), + AnyValueContent::Stream(value) => value.append_cloned_into(output), AnyValueContent::Iterator(iterator) => iterator .clone() .output_items_to(output, Grouping::Flattened)?, @@ -417,9 +427,13 @@ impl<'a> AnyValueRef<'a> { Ok(()) } - pub(crate) fn concat_recursive(self, behaviour: &ConcatBehaviour) -> ExecutionResult { + pub(crate) fn concat_recursive( + self, + behaviour: &ConcatBehaviour, + interpreter: &mut Interpreter, + ) -> FunctionResult { let mut output = String::new(); - self.concat_recursive_into(&mut output, behaviour)?; + self.concat_recursive_into(&mut output, behaviour, interpreter)?; Ok(output) } @@ -427,7 +441,8 @@ impl<'a> AnyValueRef<'a> { self, output: &mut String, behaviour: &ConcatBehaviour, - ) -> ExecutionResult<()> { + interpreter: &mut Interpreter, + ) -> FunctionResult<()> { match self { AnyValueContent::None(_) => { if behaviour.show_none_values { @@ -438,16 +453,16 @@ impl<'a> AnyValueRef<'a> { stream.concat_as_literal_into(output, behaviour); } AnyValueContent::Array(array) => { - array.concat_recursive_into(output, behaviour)?; + array.concat_recursive_into(output, behaviour, interpreter)?; } AnyValueContent::Object(object) => { - object.concat_recursive_into(output, behaviour)?; + object.concat_recursive_into(output, behaviour, interpreter)?; } AnyValueContent::Iterator(iterator) => { - iterator.concat_recursive_into(output, behaviour)?; + iterator.concat_recursive_into(output, behaviour, interpreter)?; } AnyValueContent::Range(range) => { - range.concat_recursive_into(output, behaviour)?; + range.concat_recursive_into(output, behaviour, interpreter)?; } AnyValueContent::Parser(parser) => { if behaviour.use_debug_literal_syntax { @@ -475,7 +490,11 @@ impl<'a> AnyValueRef<'a> { | AnyValueContent::String(_) => { // This isn't the most efficient, but it's less code and debug doesn't need to be super efficient. let stream = self - .output_to_new_stream(Grouping::Flattened, behaviour.error_span_range) + .output_to_new_stream( + Grouping::Flattened, + behaviour.error_span_range, + interpreter, + ) .expect("Non-composite values should all be able to be outputted to a stream"); stream.concat_content_into(output, behaviour); } @@ -486,48 +505,50 @@ impl<'a> AnyValueRef<'a> { } pub(crate) struct ToStreamContext<'a> { - output_stream: &'a mut OutputStream, + inner: OutputInterpreter<'a>, error_span_range: SpanRange, } impl<'a> ToStreamContext<'a> { - pub(crate) fn new(output_stream: &'a mut OutputStream, error_span_range: SpanRange) -> Self { + pub(crate) fn new(output: &'a mut OutputInterpreter, error_span_range: SpanRange) -> Self { Self { - output_stream, + inner: output.reborrow(), error_span_range, } } - pub(crate) fn push_grouped( + pub(crate) fn push_grouped( &mut self, - f: impl FnOnce(&mut ToStreamContext) -> ExecutionResult<()>, + f: impl FnOnce(&mut ToStreamContext) -> Result<(), E>, delimiter: Delimiter, - ) -> ExecutionResult<()> { + ) -> Result<(), E> { let span = self.new_token_span(); - self.output_stream.push_grouped( - |inner| f(&mut ToStreamContext::new(inner, self.error_span_range)), - delimiter, - span, - ) + let error_span_range = self.error_span_range; + self.inner.in_output_group(delimiter, span, |inner| { + let mut ctx = ToStreamContext { + inner: inner.reborrow(), + error_span_range, + }; + f(&mut ctx) + }) } pub(crate) fn new_token_span(&self) -> Span { - // By default, we use call_site span for generated tokens Span::call_site() } } -impl Deref for ToStreamContext<'_> { - type Target = OutputStream; +impl<'a> Deref for ToStreamContext<'a> { + type Target = OutputInterpreter<'a>; fn deref(&self) -> &Self::Target { - self.output_stream + &self.inner } } impl DerefMut for ToStreamContext<'_> { fn deref_mut(&mut self) -> &mut Self::Target { - self.output_stream + &mut self.inner } } @@ -546,15 +567,15 @@ impl Spanned { } } - pub(crate) fn into_stream(self) -> ExecutionResult { + pub(crate) fn into_stream(self, interpreter: &mut Interpreter) -> FunctionResult { let Spanned(value, span_range) = self; - value.into_stream(Grouping::Flattened, span_range) + value.into_stream(Grouping::Flattened, span_range, interpreter) } pub(crate) fn resolve_any_iterator( self, resolution_target: &str, - ) -> ExecutionResult { + ) -> FunctionResult { self.dyn_resolve::(resolution_target)? .into_iterator() } diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index e6dc4a77..daf4b5ea 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -8,11 +8,11 @@ define_leaf_type! { articled_value_name: "an array", dyn_impls: { IterableType: impl IsIterable { - fn into_iterator(self: Box) -> ExecutionResult { + fn into_iterator(self: Box) -> FunctionResult { Ok(IteratorValue::new_for_array(*self)) } - fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + fn iterable_len(&self, _error_span_range: SpanRange) -> FunctionResult { Ok(self.items.len()) } } @@ -33,7 +33,7 @@ impl ArrayValue { &self, output: &mut ToStreamContext, grouping: Grouping, - ) -> ExecutionResult<()> { + ) -> FunctionResult<()> { for item in &self.items { item.as_ref_value().output_to(grouping, output)?; } @@ -43,7 +43,7 @@ impl ArrayValue { pub(super) fn into_indexed( mut self, Spanned(index, span_range): Spanned, - ) -> ExecutionResult { + ) -> FunctionResult { Ok(match index { AnyValueContent::Integer(integer) => { let index = @@ -62,7 +62,7 @@ impl ArrayValue { pub(super) fn index_mut( &mut self, Spanned(index, span_range): Spanned, - ) -> ExecutionResult<&mut AnyValue> { + ) -> FunctionResult<&mut AnyValue> { Ok(match index { AnyValueContent::Integer(integer) => { let index = @@ -80,7 +80,7 @@ impl ArrayValue { pub(super) fn index_ref( &self, Spanned(index, span_range): Spanned, - ) -> ExecutionResult<&AnyValue> { + ) -> FunctionResult<&AnyValue> { Ok(match index { AnyValueContent::Integer(integer) => { let index = @@ -99,7 +99,7 @@ impl ArrayValue { &self, Spanned(index, span_range): Spanned, is_exclusive: bool, - ) -> ExecutionResult { + ) -> FunctionResult { match index { AnyValueContent::Integer(int) => { self.resolve_valid_index_from_integer(Spanned(int, span_range), is_exclusive) @@ -112,7 +112,7 @@ impl ArrayValue { &self, Spanned(integer, span): Spanned, is_exclusive: bool, - ) -> ExecutionResult { + ) -> FunctionResult { let index: OptionalSuffix = Spanned(integer.clone_to_owned_infallible(), span).resolve_as("An array index")?; let index = index.0; @@ -141,15 +141,17 @@ impl ArrayValue { &self, output: &mut String, behaviour: &ConcatBehaviour, - ) -> ExecutionResult<()> { - IteratorValue::any_iterator_to_string( - self.items.iter(), + interpreter: &mut Interpreter, + ) -> FunctionResult<()> { + any_items_to_string( + &mut self.items.iter(), output, behaviour, "[]", "[", "]", false, // Output all the vec because it's already in memory + interpreter, ) } } @@ -195,21 +197,23 @@ define_type_features! { impl ArrayType, pub(crate) mod array_interface { methods { - fn push(mut this: Mutable, item: AnyValue) -> ExecutionResult<()> { + fn push(mut this: Mutable, item: AnyValue) -> FunctionResult<()> { this.items.push(item); Ok(()) } - [context] fn to_stream_grouped(this: ArrayValue) -> StreamOutput [ignore_type_assertion!] { + [context] fn to_stream_grouped(this: ArrayValue) -> FunctionResult { let error_span_range = context.span_range(); - StreamOutput::new(move |stream| this.output_items_to(&mut ToStreamContext::new(stream, error_span_range), Grouping::Grouped)) + context.interpreter.capture_output(|output| { + this.output_items_to(&mut ToStreamContext::new(output, error_span_range), Grouping::Grouped) + }) } } unary_operations { - [context] fn cast_singleton_to_value(Spanned(mut this, span): Spanned) -> ExecutionResult { + [context] fn cast_singleton_to_value(Spanned(mut this, span): Spanned) -> FunctionResult { let length = this.items.len(); if length == 1 { - Ok(context.operation.evaluate(this.items.pop().unwrap().spanned(span))?.0) + Ok(context.operation.evaluate(this.items.pop().unwrap().spanned(span), context.interpreter)?.0) } else { context.operation.value_err(format!( "Only a singleton array can be cast to this value but the array has {} elements", diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index 899af2bf..974c8a50 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -103,8 +103,8 @@ fn assign_op( mut left: Assignee, right: R, context: BinaryOperationCallContext, - op: fn(BinaryOperationCallContext, FloatValue, R) -> ExecutionResult, -) -> ExecutionResult<()> { + op: fn(BinaryOperationCallContext, FloatValue, R) -> FunctionResult, +) -> FunctionResult<()> { let left_value = core::mem::replace(&mut *left, FloatContent::F32(0.0)); let result = op(context, left_value, right)?; *left = result; @@ -212,7 +212,7 @@ define_type_features! { } } binary_operations { - fn add(left: FloatValue, right: Spanned) -> ExecutionResult { + fn add(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a + b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a + b), @@ -220,11 +220,11 @@ define_type_features! { } } - [context] fn add_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn add_assign(left: Assignee, right: Spanned) -> FunctionResult<()> { assign_op(left, right, context, add) } - fn sub(left: FloatValue, right: Spanned) -> ExecutionResult { + fn sub(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a - b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a - b), @@ -232,11 +232,11 @@ define_type_features! { } } - [context] fn sub_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn sub_assign(left: Assignee, right: Spanned) -> FunctionResult<()> { assign_op(left, right, context, sub) } - fn mul(left: FloatValue, right: Spanned) -> ExecutionResult { + fn mul(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a * b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a * b), @@ -244,11 +244,11 @@ define_type_features! { } } - [context] fn mul_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn mul_assign(left: Assignee, right: Spanned) -> FunctionResult<()> { assign_op(left, right, context, mul) } - fn div(left: FloatValue, right: Spanned) -> ExecutionResult { + fn div(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a / b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a / b), @@ -256,11 +256,11 @@ define_type_features! { } } - [context] fn div_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn div_assign(left: Assignee, right: Spanned) -> FunctionResult<()> { assign_op(left, right, context, div) } - fn rem(left: FloatValue, right: Spanned) -> ExecutionResult { + fn rem(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_operation(right, |a, b| a % b), FloatContent::F32(left) => left.paired_operation_no_overflow(right, |a, b| a % b), @@ -268,11 +268,11 @@ define_type_features! { } } - [context] fn rem_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + [context] fn rem_assign(left: Assignee, right: Spanned) -> FunctionResult<()> { assign_op(left, right, context, rem) } - fn lt(left: FloatValue, right: Spanned) -> ExecutionResult { + fn lt(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a < b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a < b), @@ -280,7 +280,7 @@ define_type_features! { } } - fn le(left: FloatValue, right: Spanned) -> ExecutionResult { + fn le(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a <= b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a <= b), @@ -288,7 +288,7 @@ define_type_features! { } } - fn gt(left: FloatValue, right: Spanned) -> ExecutionResult { + fn gt(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a > b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a > b), @@ -296,7 +296,7 @@ define_type_features! { } } - fn ge(left: FloatValue, right: Spanned) -> ExecutionResult { + fn ge(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a >= b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a >= b), @@ -304,7 +304,7 @@ define_type_features! { } } - fn eq(left: FloatValue, right: Spanned) -> ExecutionResult { + fn eq(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a == b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a == b), @@ -312,7 +312,7 @@ define_type_features! { } } - fn ne(left: FloatValue, right: Spanned) -> ExecutionResult { + fn ne(left: FloatValue, right: Spanned) -> FunctionResult { match left.resolve_untyped_to_match(right.as_ref_value()) { FloatContent::Untyped(left) => left.paired_comparison(right, |a, b| a != b), FloatContent::F32(left) => left.paired_comparison(right, |a, b| a != b), diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 7ec2dd58..df31f779 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -158,13 +158,13 @@ macro_rules! impl_resolvable_float_subtype { type ValueType = FloatType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - fn from_argument(argument: Spanned) -> ExecutionResult { + fn from_argument(argument: Spanned) -> FunctionResult { argument.expect_owned().resolve_as("This argument") } } impl ResolveAs> for Spanned { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult> { let span = self.span_range(); let float_value: FloatValue = self.resolve_as(resolution_target)?; Spanned(float_value, span).resolve_as(resolution_target) @@ -172,7 +172,7 @@ macro_rules! impl_resolvable_float_subtype { } impl ResolveAs> for Spanned { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult> { let Spanned(value, span) = self; match value { FloatContent::Untyped(v) => Ok(OptionalSuffix(v.into_fallback() as $type)), @@ -201,7 +201,7 @@ macro_rules! impl_resolvable_float_subtype { fn resolve_from_value( value: FloatValue, context: ResolutionContext, - ) -> ExecutionResult { + ) -> FunctionResult { match value { FloatContent::Untyped(x) => Ok(x.into_fallback() as $type), FloatContent::$variant(x) => Ok(x), @@ -214,7 +214,7 @@ macro_rules! impl_resolvable_float_subtype { fn resolve_from_value( value: AnyValue, context: ResolutionContext, - ) -> ExecutionResult { + ) -> FunctionResult { match value { AnyValue::Float(x) => <$type>::resolve_from_value(x, context), other => context.err($type_def::ARTICLED_VALUE_NAME, other), @@ -226,7 +226,7 @@ macro_rules! impl_resolvable_float_subtype { fn resolve_from_ref<'a>( value: &'a AnyValue, context: ResolutionContext, - ) -> ExecutionResult<&'a Self> { + ) -> FunctionResult<&'a Self> { match value { AnyValueContent::Float(FloatContent::$variant(x)) => Ok(x), other => context.err($type_def::ARTICLED_VALUE_NAME, other), @@ -238,7 +238,7 @@ macro_rules! impl_resolvable_float_subtype { fn resolve_from_mut<'a>( value: &'a mut AnyValue, context: ResolutionContext, - ) -> ExecutionResult<&'a mut Self> { + ) -> FunctionResult<&'a mut Self> { match value { AnyValueContent::Float(FloatContent::$variant(x)) => Ok(x), other => context.err($type_def::ARTICLED_VALUE_NAME, other), diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 0b4bde7f..a4cf3608 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -38,7 +38,7 @@ impl UntypedFloat { self, rhs: Spanned, perform_fn: fn(FallbackFloat, FallbackFloat) -> FallbackFloat, - ) -> ExecutionResult { + ) -> FunctionResult { let lhs = self.0; let rhs: UntypedFloat = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; @@ -50,7 +50,7 @@ impl UntypedFloat { self, rhs: Spanned, compare_fn: fn(FallbackFloat, FallbackFloat) -> bool, - ) -> ExecutionResult { + ) -> FunctionResult { let lhs = self.0; let rhs: UntypedFloat = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; @@ -189,7 +189,7 @@ pub(crate) struct UntypedFloatFallback(pub FallbackFloat); impl IsArgument for UntypedFloatFallback { type ValueType = UntypedFloatType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - fn from_argument(value: Spanned) -> ExecutionResult { + fn from_argument(value: Spanned) -> FunctionResult { Self::resolve_value(value.expect_owned(), "This argument") } } @@ -202,14 +202,14 @@ impl ResolvableOwned for UntypedFloatFallback { fn resolve_from_value( input_value: AnyValue, context: ResolutionContext, - ) -> ExecutionResult { + ) -> FunctionResult { let value = UntypedFloat::resolve_from_value(input_value, context)?; Ok(UntypedFloatFallback(value.0)) } } impl ResolvableOwned for UntypedFloat { - fn resolve_from_value(value: FloatValue, context: ResolutionContext) -> ExecutionResult { + fn resolve_from_value(value: FloatValue, context: ResolutionContext) -> FunctionResult { match value { FloatContent::Untyped(value) => Ok(value), _ => context.err("an untyped float", value), diff --git a/src/expressions/values/function.rs b/src/expressions/values/function.rs index df7f3563..ce37e42e 100644 --- a/src/expressions/values/function.rs +++ b/src/expressions/values/function.rs @@ -52,7 +52,7 @@ impl InvokableFunction { self, arguments: Vec>, context: &mut FunctionCallContext, - ) -> ExecutionResult> { + ) -> FunctionResult> { match self { InvokableFunction::Native(interface) => interface.invoke(arguments, context), InvokableFunction::Closure(closure) => closure.invoke(arguments, context), @@ -60,6 +60,37 @@ impl InvokableFunction { } } +impl FunctionValue { + /// Convenience method to invoke a `FunctionValue`, handling bound arguments. + /// Consumes `self` because `InvokableFunction::invoke` takes `self`. + pub(crate) fn invoke( + self, + mut extra_arguments: Vec>, + context: &mut FunctionCallContext, + ) -> FunctionResult> { + let mut arguments: Vec> = self + .disabled_bound_arguments + .into_iter() + .map(|arg| { + let span = arg.span_range(); + arg.try_map(|v| v.enable(span)) + }) + .collect::>()?; + arguments.append(&mut extra_arguments); + self.invokable.invoke(arguments, context) + } +} + +impl_resolvable_argument_for! { + FunctionType, + (value, context) -> FunctionValue { + match value { + AnyValue::Function(value) => Ok(value), + _ => context.err("a function", value), + } + } +} + impl ValuesEqual for FunctionValue { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { if self.invokable == other.invokable { diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index 90de463e..f4fa7ca3 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -58,7 +58,7 @@ impl IntegerValue { pub(crate) fn resolve_untyped_to_match_other( Spanned(value, span): Spanned, other: &AnyValue, - ) -> ExecutionResult { + ) -> FunctionResult { match (value, other) { (IntegerValue::Untyped(this), AnyValue::Integer(other)) => { this.spanned(span).into_kind(other.kind()) @@ -70,7 +70,7 @@ impl IntegerValue { pub(crate) fn resolve_untyped_to_match( Spanned(value, span): Spanned, target: &IntegerValue, - ) -> ExecutionResult { + ) -> FunctionResult { match value { IntegerValue::Untyped(this) => this.spanned(span).into_kind(target.kind()), value => Ok(value), @@ -85,8 +85,8 @@ impl IntegerValue { BinaryOperationCallContext, Spanned, R, - ) -> ExecutionResult, - ) -> ExecutionResult<()> { + ) -> FunctionResult, + ) -> FunctionResult<()> { let left_value = core::mem::replace(&mut *left, IntegerValue::U32(0)); let result = op(context, Spanned(left_value, left_span), right)?; *left = result; @@ -207,7 +207,7 @@ define_type_features! { impl IntegerType, pub(crate) mod integer_interface { binary_operations { - [context] fn add(left: Spanned, right: Spanned) -> ExecutionResult { + [context] fn add(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_add), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_add), @@ -225,11 +225,11 @@ define_type_features! { } } - [context] fn add_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { + [context] fn add_assign(lhs: Spanned>, rhs: Spanned) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, add) } - [context] fn sub(left: Spanned, right: Spanned) -> ExecutionResult { + [context] fn sub(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_sub), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_sub), @@ -247,11 +247,11 @@ define_type_features! { } } - [context] fn sub_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { + [context] fn sub_assign(lhs: Spanned>, rhs: Spanned) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, sub) } - [context] fn mul(left: Spanned, right: Spanned) -> ExecutionResult { + [context] fn mul(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_mul), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_mul), @@ -269,11 +269,11 @@ define_type_features! { } } - [context] fn mul_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { + [context] fn mul_assign(lhs: Spanned>, rhs: Spanned) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, mul) } - [context] fn div(left: Spanned, right: Spanned) -> ExecutionResult { + [context] fn div(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_div), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_div), @@ -291,11 +291,11 @@ define_type_features! { } } - [context] fn div_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { + [context] fn div_assign(lhs: Spanned>, rhs: Spanned) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, div) } - [context] fn rem(left: Spanned, right: Spanned) -> ExecutionResult { + [context] fn rem(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, FallbackInteger::checked_rem), IntegerValue::U8(left) => left.paired_operation(right, context, u8::checked_rem), @@ -313,11 +313,11 @@ define_type_features! { } } - [context] fn rem_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { + [context] fn rem_assign(lhs: Spanned>, rhs: Spanned) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, rem) } - [context] fn bitxor(left: Spanned, right: Spanned) -> ExecutionResult { + [context] fn bitxor(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, |a, b| Some(a ^ b)), IntegerValue::U8(left) => left.paired_operation(right, context, |a, b| Some(a ^ b)), @@ -335,11 +335,11 @@ define_type_features! { } } - [context] fn bitxor_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { + [context] fn bitxor_assign(lhs: Spanned>, rhs: Spanned) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, bitxor) } - [context] fn bitand(left: Spanned, right: Spanned) -> ExecutionResult { + [context] fn bitand(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, |a, b| Some(a & b)), IntegerValue::U8(left) => left.paired_operation(right, context, |a, b| Some(a & b)), @@ -357,11 +357,11 @@ define_type_features! { } } - [context] fn bitand_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { + [context] fn bitand_assign(lhs: Spanned>, rhs: Spanned) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, bitand) } - [context] fn bitor(left: Spanned, right: Spanned) -> ExecutionResult { + [context] fn bitor(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_operation(right, context, |a, b| Some(a | b)), IntegerValue::U8(left) => left.paired_operation(right, context, |a, b| Some(a | b)), @@ -379,11 +379,11 @@ define_type_features! { } } - [context] fn bitor_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { + [context] fn bitor_assign(lhs: Spanned>, rhs: Spanned) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, bitor) } - [context] fn shift_left(lhs: Spanned, CoercedToU32(right): CoercedToU32) -> ExecutionResult { + [context] fn shift_left(lhs: Spanned, CoercedToU32(right): CoercedToU32) -> FunctionResult { match lhs.0 { IntegerValue::Untyped(left) => left.shift_operation(right, context, FallbackInteger::checked_shl), IntegerValue::U8(left) => left.shift_operation(right, context, u8::checked_shl), @@ -401,11 +401,11 @@ define_type_features! { } } - [context] fn shift_left_assign(lhs: Spanned>, rhs: CoercedToU32) -> ExecutionResult<()> { + [context] fn shift_left_assign(lhs: Spanned>, rhs: CoercedToU32) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, shift_left) } - [context] fn shift_right(lhs: Spanned, CoercedToU32(right): CoercedToU32) -> ExecutionResult { + [context] fn shift_right(lhs: Spanned, CoercedToU32(right): CoercedToU32) -> FunctionResult { match lhs.0 { IntegerValue::Untyped(left) => left.shift_operation(right, context, FallbackInteger::checked_shr), IntegerValue::U8(left) => left.shift_operation(right, context, u8::checked_shr), @@ -423,11 +423,11 @@ define_type_features! { } } - [context] fn shift_right_assign(lhs: Spanned>, rhs: CoercedToU32) -> ExecutionResult<()> { + [context] fn shift_right_assign(lhs: Spanned>, rhs: CoercedToU32) -> FunctionResult<()> { IntegerValue::assign_op(lhs, rhs, context, shift_right) } - fn lt(left: Spanned, right: Spanned) -> ExecutionResult { + fn lt(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a < b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a < b), @@ -445,7 +445,7 @@ define_type_features! { } } - fn le(left: Spanned, right: Spanned) -> ExecutionResult { + fn le(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a <= b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a <= b), @@ -463,7 +463,7 @@ define_type_features! { } } - fn gt(left: Spanned, right: Spanned) -> ExecutionResult { + fn gt(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a > b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a > b), @@ -481,7 +481,7 @@ define_type_features! { } } - fn ge(left: Spanned, right: Spanned) -> ExecutionResult { + fn ge(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a >= b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a >= b), @@ -499,7 +499,7 @@ define_type_features! { } } - fn eq(left: Spanned, right: Spanned) -> ExecutionResult { + fn eq(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a == b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a == b), @@ -517,7 +517,7 @@ define_type_features! { } } - fn ne(left: Spanned, right: Spanned) -> ExecutionResult { + fn ne(left: Spanned, right: Spanned) -> FunctionResult { match IntegerValue::resolve_untyped_to_match(left, &right)? { IntegerValue::Untyped(left) => left.paired_comparison(right, |a, b| a != b), IntegerValue::U8(left) => left.paired_comparison(right, |a, b| a != b), @@ -592,7 +592,7 @@ pub(crate) struct CoercedToU32(pub(crate) u32); impl IsArgument for CoercedToU32 { type ValueType = IntegerType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - fn from_argument(value: Spanned) -> ExecutionResult { + fn from_argument(value: Spanned) -> FunctionResult { Self::resolve_value(value.expect_owned(), "This argument") } } @@ -605,7 +605,7 @@ impl ResolvableOwned for CoercedToU32 { fn resolve_from_value( input_value: AnyValue, context: ResolutionContext, - ) -> ExecutionResult { + ) -> FunctionResult { let integer = match input_value { AnyValue::Integer(value) => value, other => return context.err("an integer", other), diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 4be03512..ed848ce4 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -10,7 +10,7 @@ macro_rules! impl_int_operations { pub(crate) mod $mod_name { unary_operations { $( - fn neg(Spanned(value, span): Spanned<$integer_type>) -> ExecutionResult<$integer_type> { + fn neg(Spanned(value, span): Spanned<$integer_type>) -> FunctionResult<$integer_type> { ignore_all!($signed); // Include only for signed types match value.checked_neg() { Some(negated) => Ok(negated), @@ -191,13 +191,13 @@ macro_rules! impl_resolvable_integer_subtype { type ValueType = IntegerType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - fn from_argument(argument: Spanned) -> ExecutionResult { + fn from_argument(argument: Spanned) -> FunctionResult { argument.expect_owned().resolve_as("This argument") } } impl ResolveAs> for Spanned { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult> { let span = self.span_range(); let integer_value: IntegerValue = self.resolve_as(resolution_target)?; Spanned(integer_value, span).resolve_as(resolution_target) @@ -205,7 +205,7 @@ macro_rules! impl_resolvable_integer_subtype { } impl ResolveAs> for Spanned { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + fn resolve_as(self, resolution_target: &str) -> FunctionResult> { let Spanned(value, span) = self; match value { IntegerValue::Untyped(v) => Ok(OptionalSuffix(v.into_fallback() as $type)), @@ -234,7 +234,7 @@ macro_rules! impl_resolvable_integer_subtype { fn resolve_from_value( value: IntegerValue, context: ResolutionContext, - ) -> ExecutionResult { + ) -> FunctionResult { match value { IntegerValue::Untyped(x) => Ok(x.into_fallback() as $type), IntegerValue::$variant(x) => Ok(x), @@ -247,7 +247,7 @@ macro_rules! impl_resolvable_integer_subtype { fn resolve_from_value( value: AnyValue, context: ResolutionContext, - ) -> ExecutionResult { + ) -> FunctionResult { match value { AnyValue::Integer(x) => <$type>::resolve_from_value(x, context), other => context.err($type_def::ARTICLED_VALUE_NAME, other), @@ -259,7 +259,7 @@ macro_rules! impl_resolvable_integer_subtype { fn resolve_from_ref<'a>( value: &'a AnyValue, context: ResolutionContext, - ) -> ExecutionResult<&'a Self> { + ) -> FunctionResult<&'a Self> { match value { AnyValue::Integer(IntegerValue::$variant(x)) => Ok(x), other => context.err($type_def::ARTICLED_VALUE_NAME, other), @@ -271,7 +271,7 @@ macro_rules! impl_resolvable_integer_subtype { fn resolve_from_mut<'a>( value: &'a mut AnyValue, context: ResolutionContext, - ) -> ExecutionResult<&'a mut Self> { + ) -> FunctionResult<&'a mut Self> { match value { AnyValue::Integer(IntegerValue::$variant(x)) => Ok(x), other => context.err($type_def::ARTICLED_VALUE_NAME, other), diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index 44f9c6ae..da3f162b 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -28,7 +28,7 @@ impl UntypedInteger { context: BinaryOperationCallContext, lhs: impl std::fmt::Display, rhs: impl std::fmt::Display, - ) -> ExecutionInterrupt { + ) -> FunctionError { context.error(format!( "The untyped integer operation {} {} {} overflowed in {} space", lhs, @@ -63,7 +63,7 @@ impl UntypedInteger { rhs: Spanned, context: BinaryOperationCallContext, perform_fn: fn(FallbackInteger, FallbackInteger) -> Option, - ) -> ExecutionResult { + ) -> FunctionResult { let lhs = self.0; let rhs: UntypedInteger = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; @@ -76,7 +76,7 @@ impl UntypedInteger { self, rhs: Spanned, compare_fn: fn(FallbackInteger, FallbackInteger) -> bool, - ) -> ExecutionResult { + ) -> FunctionResult { let lhs = self.0; let rhs: UntypedInteger = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; @@ -88,7 +88,7 @@ impl UntypedInteger { rhs: u32, context: BinaryOperationCallContext, perform_fn: fn(FallbackInteger, u32) -> Option, - ) -> ExecutionResult { + ) -> FunctionResult { let lhs = self.0; let output = perform_fn(lhs, rhs) .ok_or_else(|| UntypedInteger::binary_overflow_error(context, lhs, rhs))?; @@ -109,9 +109,9 @@ impl UntypedInteger { } impl Spanned { - pub(crate) fn into_kind(self, kind: IntegerLeafKind) -> ExecutionResult { + pub(crate) fn into_kind(self, kind: IntegerLeafKind) -> FunctionResult { let Spanned(value, span_range) = self; - value.try_into_kind(kind).ok_or_else(|| { + value.try_into_kind(kind).ok_or_else(|| -> FunctionError { span_range.value_error(format!( "The integer value {} does not fit into {}", value.0, @@ -125,7 +125,7 @@ define_type_features! { impl UntypedIntegerType, pub(crate) mod untyped_integer_interface { unary_operations { - fn neg(Spanned(value, span): Spanned) -> ExecutionResult { + fn neg(Spanned(value, span): Spanned) -> FunctionResult { let input = value.into_fallback(); match input.checked_neg() { Some(negated) => Ok(UntypedInteger::from_fallback(negated)), @@ -249,7 +249,7 @@ impl IsValueContent for UntypedIntegerFallback { impl IsArgument for UntypedIntegerFallback { type ValueType = UntypedIntegerType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - fn from_argument(value: Spanned) -> ExecutionResult { + fn from_argument(value: Spanned) -> FunctionResult { Self::resolve_value(value.expect_owned(), "This argument") } } @@ -262,7 +262,7 @@ impl ResolvableOwned for UntypedIntegerFallback { fn resolve_from_value( input_value: AnyValue, context: ResolutionContext, - ) -> ExecutionResult { + ) -> FunctionResult { let value: UntypedInteger = ResolvableOwned::::resolve_from_value(input_value, context)?; Ok(UntypedIntegerFallback(value.into_fallback())) @@ -270,10 +270,7 @@ impl ResolvableOwned for UntypedIntegerFallback { } impl ResolvableOwned for UntypedInteger { - fn resolve_from_value( - value: IntegerValue, - context: ResolutionContext, - ) -> ExecutionResult { + fn resolve_from_value(value: IntegerValue, context: ResolutionContext) -> FunctionResult { match value { IntegerValue::Untyped(value) => Ok(value), _ => context.err("an untyped integer", value), diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index 2a8186c3..b46a4362 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -1,8 +1,8 @@ use super::*; pub(crate) trait IsIterable: 'static { - fn into_iterator(self: Box) -> ExecutionResult; - fn len(&self, error_span_range: SpanRange) -> ExecutionResult; + fn into_iterator(self: Box) -> FunctionResult; + fn iterable_len(&self, error_span_range: SpanRange) -> FunctionResult; } define_dyn_type!( @@ -20,43 +20,47 @@ define_type_features! { impl IterableType, pub(crate) mod iterable_interface { methods { - fn into_iter(this: IterableValue) -> ExecutionResult { + fn into_iter(this: IterableValue) -> FunctionResult { this.into_iterator() } - fn len(Spanned(this, span_range): Spanned) -> ExecutionResult { - this.len(span_range) + fn len(Spanned(this, span_range): Spanned) -> FunctionResult { + this.iterable_len(span_range) } - fn is_empty(Spanned(this, span_range): Spanned) -> ExecutionResult { - Ok(this.len(span_range)? == 0) + fn is_empty(Spanned(this, span_range): Spanned) -> FunctionResult { + Ok(this.iterable_len(span_range)? == 0) } - [context] fn zip(this: IterableValue) -> ExecutionResult { + [context] fn zip(this: IterableValue) -> FunctionResult { let iterator = this.into_iterator()?; - ZipIterators::new_from_iterator(iterator, context.span_range())?.run_zip(context.interpreter, true) + ZipIterators::new_from_iterator(iterator, context.span_range(), context.interpreter)?.run_zip(context.interpreter, true) } - [context] fn zip_truncated(this: IterableValue) -> ExecutionResult { + [context] fn zip_truncated(this: IterableValue) -> FunctionResult { let iterator = this.into_iterator()?; - ZipIterators::new_from_iterator(iterator, context.span_range())?.run_zip(context.interpreter, false) + ZipIterators::new_from_iterator(iterator, context.span_range(), context.interpreter)?.run_zip(context.interpreter, false) } - fn intersperse(this: IterableValue, separator: AnyValue, settings: Option) -> ExecutionResult { - run_intersperse(this, separator, settings.unwrap_or_default()) + [context] fn intersperse(this: IterableValue, separator: AnyValue, settings: Option) -> FunctionResult { + run_intersperse(this, separator, settings.unwrap_or_default(), context.interpreter) } - [context] fn to_vec(this: IterableValue) -> ExecutionResult> { + [context] fn to_vec(this: IterableValue) -> FunctionResult> { let error_span_range = context.span_range(); let mut counter = context.interpreter.start_iteration_counter(&error_span_range); - let iterator = this.into_iterator()?; - let max_hint = iterator.size_hint().1; + let mut iterator = this.into_iterator()?; + let max_hint = iterator.do_size_hint().1; let mut vec = if let Some(max) = max_hint { Vec::with_capacity(max) } else { Vec::new() }; - for item in iterator { + loop { + let item = match iterator.do_next(context.interpreter)? { + Some(item) => item, + None => break, + }; counter.increment_and_check()?; vec.push(item); } @@ -64,7 +68,7 @@ define_type_features! { } } unary_operations { - fn cast_into_iterator(this: IterableValue) -> ExecutionResult { + fn cast_into_iterator(this: IterableValue) -> FunctionResult { this.into_iterator() } } diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index 7e1f7b84..81283eba 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -8,43 +8,45 @@ define_leaf_type! { articled_value_name: "an iterator", dyn_impls: { IterableType: impl IsIterable { - fn into_iterator(self: Box) -> ExecutionResult { + fn into_iterator(self: Box) -> FunctionResult { Ok(*self) } - fn len(&self, error_span_range: SpanRange) -> ExecutionResult { - self.len(error_span_range) + fn iterable_len(&self, error_span_range: SpanRange) -> FunctionResult { + self.do_len(error_span_range) } } }, } +pub(crate) type ValueIterator = Box>; + #[derive(Clone)] pub(crate) struct IteratorValue { - iterator: IteratorValueInner, + inner: ValueIterator, } impl IteratorValue { - fn new(iterator: IteratorValueInner) -> Self { - Self { iterator } + pub(crate) fn new(iterator: ValueIterator) -> Self { + Self { inner: iterator } } #[allow(unused)] pub(crate) fn new_any(iterator: impl Iterator + 'static + Clone) -> Self { - Self::new_custom(Box::new(iterator)) + Self::new(Box::new(iterator)) } pub(crate) fn new_for_array(array: ArrayValue) -> Self { - Self::new_vec(array.items.into_iter()) + Self::new(Box::new(array.items.into_iter())) } pub(crate) fn new_for_stream(stream: OutputStream) -> Self { - Self::new(IteratorValueInner::Stream(Box::new(stream.into_iter()))) + Self::new(Box::new(StreamValueIterator(stream.into_iter()))) } - pub(crate) fn new_for_range(range: RangeValue) -> ExecutionResult { + pub(crate) fn new_for_range(range: RangeValue) -> FunctionResult { let iterator = range.inner.into_iterable()?.resolve_iterator()?; - Ok(Self::new_custom(iterator)) + Ok(Self::new(iterator)) } pub(crate) fn new_for_object(object: ObjectValue) -> Self { @@ -55,7 +57,7 @@ impl IteratorValue { .map(|(k, v)| vec![k.into_any_value(), v.value].into_any_value()) .collect::>() .into_iter(); - Self::new_vec(iterator) + Self::new(Box::new(iterator)) } pub(crate) fn new_for_string_over_chars(string: String) -> Self { @@ -69,46 +71,46 @@ impl IteratorValue { .map(|c| c.into_any_value()) .collect::>() .into_iter(); - Self::new_vec(iterator) - } - - fn new_vec(iterator: std::vec::IntoIter) -> Self { - Self::new(IteratorValueInner::Vec(Box::new(iterator))) + Self::new(Box::new(iterator)) } - pub(crate) fn new_custom(iterator: Box>) -> Self { - Self::new(IteratorValueInner::Other(iterator)) + /// Returns the inner BoxedIterator box (for creating Map/Filter wrappers). + pub(crate) fn into_inner(self) -> ValueIterator { + self.inner } - pub(crate) fn len(&self, error_span_range: SpanRange) -> ExecutionResult { - let (min, max) = self.size_hint(); - if max == Some(min) { - Ok(min) + pub(crate) fn singleton_value( + mut self, + interpreter: &mut Interpreter, + ) -> FunctionResult> { + let first = match self.do_next(interpreter)? { + Some(v) => v, + None => return Ok(None), + }; + if self.do_next(interpreter)?.is_none() { + Ok(Some(first)) } else { - error_span_range.value_err("Iterator has an inexact length") - } - } - - pub(crate) fn singleton_value(mut self) -> Option { - let first = self.next()?; - if self.next().is_none() { - Some(first) - } else { - None + Ok(None) } } pub(super) fn output_items_to( - self, + mut self, output: &mut ToStreamContext, grouping: Grouping, - ) -> ExecutionResult<()> { + ) -> FunctionResult<()> { const LIMIT: usize = 10_000; - for (i, item) in self.enumerate() { + let mut i = 0; + loop { + let item = match output.with_interpreter(|interpreter| self.do_next(interpreter))? { + Some(item) => item, + None => break, + }; if i > LIMIT { return output.debug_err(format!("Only a maximum of {} items can be output to a stream from an iterator, to protect you from infinite loops. This can't currently be reconfigured with the iteration limit.", LIMIT)); } item.as_ref_value().output_to(grouping, output)?; + i += 1; } Ok(()) } @@ -117,92 +119,60 @@ impl IteratorValue { &self, output: &mut String, behaviour: &ConcatBehaviour, - ) -> ExecutionResult<()> { - Self::any_iterator_to_string( - self.clone(), - output, - behaviour, - "[]", - "[ ", - "]", - true, - ) - } - - pub(crate) fn any_iterator_to_string>( - iterator: impl Iterator, - output: &mut String, - behaviour: &ConcatBehaviour, - literal_empty: &str, - literal_start: &str, - literal_end: &str, - possibly_unbounded: bool, - ) -> ExecutionResult<()> { - let mut is_empty = true; - let max = iterator.size_hint().1; - for (i, item) in iterator.enumerate() { - if i == 0 { - if behaviour.output_literal_structure { - output.push_str(literal_start); - } - is_empty = false; - } - if possibly_unbounded && i >= behaviour.iterator_limit { - if behaviour.error_after_iterator_limit { - return behaviour.error_span_range.debug_err(format!("To protect against infinite loops, only a maximum of {} items can be output to a string from an iterator. You can use .to_vec() to avoid this limit. This can't currently be reconfigured with the iteration limit.", behaviour.iterator_limit)); - } else { - if behaviour.output_literal_structure { - match max { - Some(max) => output.push_str(&format!( - ", ..<{} further items>", - max.saturating_sub(i) - )), - None => output.push_str(", .."), - } - } - break; - } - } - let item = item.borrow(); - if i != 0 && behaviour.output_literal_structure { - output.push(','); - } - if i != 0 && behaviour.add_space_between_token_trees { - output.push(' '); - } - item.as_ref_value() - .concat_recursive_into(output, behaviour)?; - } - if behaviour.output_literal_structure { - if is_empty { - output.push_str(literal_empty); - } else { - output.push_str(literal_end); - } + interpreter: &mut Interpreter, + ) -> FunctionResult<()> { + if behaviour.use_debug_literal_syntax { + any_items_to_string( + &mut self.clone(), + output, + behaviour, + "[]", + "[ ", + "]", + true, + interpreter, + ) + } else { + output.push_str("Iterator[?]"); + Ok(()) } - Ok(()) } } -impl IsValueContent for IteratorValueInner { - type Type = IteratorType; - type Form = BeOwned; +#[derive(Clone)] +struct StreamValueIterator(OutputStreamIntoIter); + +impl PreinterpretIterator for StreamValueIterator { + type Item = AnyValue; + fn do_next(&mut self, _: &mut Interpreter) -> FunctionResult> { + Ok(Iterator::next(&mut self.0).map(|segment| { + let stream: OutputStream = segment.into(); + stream.coerce_into_value() + })) + } + fn do_size_hint(&self) -> (usize, Option) { + Iterator::size_hint(&self.0) + } } -impl IntoValueContent<'static> for IteratorValueInner { - fn into_content(self) -> Content<'static, Self::Type, Self::Form> { - IteratorValue::new(self) +impl PreinterpretIterator for IteratorValue { + type Item = AnyValue; + fn do_next(&mut self, interpreter: &mut Interpreter) -> FunctionResult> { + self.inner.do_next(interpreter) + } + fn do_size_hint(&self) -> (usize, Option) { + self.inner.do_size_hint() } } -impl IsValueContent for Box> { +impl IsValueContent for ValueIterator { type Type = IteratorType; type Form = BeOwned; } -impl IntoValueContent<'static> for Box> { +impl IntoValueContent<'static> for ValueIterator { fn into_content(self) -> Content<'static, Self::Type, Self::Form> { - IteratorValue::new_custom(self) + IteratorValue::new(self) } } @@ -216,124 +186,74 @@ impl_resolvable_argument_for! { } } -fn definite_size_hint(size_hint: (usize, Option)) -> Option { - let (min, max) = size_hint; - if let Some(max) = max { - if min == max { - Some(min) - } else { - None - } - } else { - None - } -} - impl ValuesEqual for IteratorValue { - /// Compares two iterators by cloning and comparing element-by-element. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - let mut lhs_iter = self.clone(); - let mut rhs_iter = other.clone(); - let mut index = 0; - let lhs_size = definite_size_hint(lhs_iter.size_hint()); - let rhs_size = definite_size_hint(rhs_iter.size_hint()); - const MAX_ITERATIONS: usize = 1000; - while index < MAX_ITERATIONS { - match (lhs_iter.next(), rhs_iter.next()) { - (Some(l), Some(r)) => { - let result = ctx.with_iterator_index(index, |ctx| l.test_equality(&r, ctx)); - if ctx.should_short_circuit(&result) { - return result; - } - index += 1; - } - (None, None) => return ctx.values_equal(), - _ => return ctx.lengths_unequal(lhs_size, rhs_size), - } - } - ctx.iteration_limit_exceeded(MAX_ITERATIONS) - } -} - -#[derive(Clone)] -enum IteratorValueInner { - // We Box these so that Value is smaller on the stack - Vec(Box< as IntoIterator>::IntoIter>), - Stream(Box<::IntoIter>), - Other(Box>), -} - -impl Iterator for IteratorValue { - type Item = AnyValue; - - fn next(&mut self) -> Option { - match &mut self.iterator { - IteratorValueInner::Vec(iter) => iter.next(), - IteratorValueInner::Stream(iter) => { - let item = iter.next()?; - let stream: OutputStream = item.into(); - Some(stream.coerce_into_value()) - } - IteratorValueInner::Other(iter) => iter.next(), - } - } - - fn size_hint(&self) -> (usize, Option) { - match &self.iterator { - IteratorValueInner::Vec(iter) => iter.size_hint(), - IteratorValueInner::Stream(iter) => iter.size_hint(), - IteratorValueInner::Other(iter) => iter.size_hint(), + if std::ptr::eq(self, other) { + ctx.values_equal() + } else { + ctx.leaf_values_not_equal(&"Iterator[?]", &"Iterator[?]") } } } -impl Iterator for Mutable { - type Item = AnyValue; - - fn next(&mut self) -> Option { - let this: &mut IteratorValue = &mut *self; - this.next() - } - - fn size_hint(&self) -> (usize, Option) { - let this: &IteratorValue = self; - this.size_hint() - } -} - define_type_features! { impl IteratorType, pub(crate) mod iterator_interface { methods { - fn next(mut this: Mutable) -> AnyValue { - match this.next() { - Some(value) => value, - None => ().into_any_value(), + [context] fn next(mut this: Mutable) -> FunctionResult { + match this.do_next(context.interpreter)? { + Some(value) => Ok(value), + None => Ok(().into_any_value()), } } - fn skip(mut this: IteratorValue, n: OptionalSuffix) -> IteratorValue { - // We make this greedy instead of lazy because the Skip iterator is not clonable. - // We return an iterator for forwards compatibility in case we change it. - for _ in 0..n.0 { - if this.next().is_none() { - break; - } - } - this + fn skip(this: IteratorValue, n: OptionalSuffix) -> ValueIterator { + this.do_skip(n.0).boxed() + } + + fn take(this: IteratorValue, n: OptionalSuffix) -> ValueIterator { + this.do_take(n.0).boxed() + } + + [context] fn map(this: IteratorValue, function: FunctionValue) -> IteratorValue { + let inner = this.into_inner(); + let span = context.output_span_range; + let f = move |item: AnyValue, interpreter: &mut Interpreter| -> FunctionResult { + let mut ctx = FunctionCallContext { interpreter, output_span_range: span }; + let argument = Spanned(ArgumentValue::Owned(item), span); + let result = function.clone().invoke(vec![argument], &mut ctx)?; + let owned = RequestedOwnership::owned() + .map_from_returned(result)? + .0 + .expect_owned(); + Ok(owned) + }; + IteratorValue::new(Box::new(MapIterator::new(inner, f))) } - fn take(this: IteratorValue, n: OptionalSuffix) -> IteratorValue { - // We collect to a vec to satisfy the clonability requirement, - // but only return an iterator for forwards compatibility in case we change it. - let taken = this.take(n.0).collect::>(); - IteratorValue::new_for_array(ArrayValue::new(taken)) + [context] fn filter(this: IteratorValue, function: FunctionValue) -> IteratorValue { + let inner = this.into_inner(); + let span = context.output_span_range; + let f = move |item: &AnyValue, interpreter: &mut Interpreter| -> FunctionResult { + let mut ctx = FunctionCallContext { interpreter, output_span_range: span }; + let argument = Spanned(ArgumentValue::Owned(item.clone()), span); + let result = function.clone().invoke(vec![argument], &mut ctx)?; + let owned = RequestedOwnership::owned() + .map_from_returned(result)? + .0 + .expect_owned(); + let keep: bool = owned + .spanned(span) + .resolve_as("The result of a filter predicate")?; + Ok(keep) + }; + IteratorValue::new(Box::new(FilterIterator::new(inner, f))) } } unary_operations { - [context] fn cast_singleton_to_value(Spanned(this, span): Spanned) -> ExecutionResult { - match this.singleton_value() { - Some(value) => Ok(context.operation.evaluate(Spanned(value, span))?.0), + [context] fn cast_singleton_to_value(Spanned(this, span): Spanned) -> FunctionResult { + match this.singleton_value(context.interpreter)? { + Some(value) => Ok(context.operation.evaluate(Spanned(value, span), context.interpreter)?.0), None => span.value_err("Only an iterator with one item can be cast to this value"), } } diff --git a/src/expressions/values/none.rs b/src/expressions/values/none.rs index b336aebe..0f79e10a 100644 --- a/src/expressions/values/none.rs +++ b/src/expressions/values/none.rs @@ -14,7 +14,7 @@ impl ResolvableArgumentTarget for () { } impl ResolvableOwned for () { - fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> ExecutionResult { + fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> FunctionResult { match value { AnyValue::None(_) => Ok(()), other => context.err("None", other), @@ -22,6 +22,24 @@ impl ResolvableOwned for () { } } +/// Returns a reference to a `None` value with the `'static` lifetime. +/// MSRV: On Rust 1.68, `&AnyValue::None(())` can't be promoted to `'static` directly. +#[allow(clippy::missing_const_for_thread_local)] +pub(crate) fn static_none_ref() -> &'static AnyValue { + use std::cell::Cell; + thread_local! { + static NONE: Cell> = Cell::new(None); + } + NONE.with(|cell| match cell.get() { + Some(val) => val, + None => { + let val: &'static AnyValue = Box::leak(Box::new(AnyValue::None(()))); + cell.set(Some(val)); + val + } + }) +} + define_type_features! { impl NoneType, pub(crate) mod none_interface {} diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index d689523c..aa8dd59b 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -8,11 +8,11 @@ define_leaf_type! { articled_value_name: "an object", dyn_impls: { IterableType: impl IsIterable { - fn into_iterator(self: Box) -> ExecutionResult { + fn into_iterator(self: Box) -> FunctionResult { Ok(IteratorValue::new_for_object(*self)) } - fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + fn iterable_len(&self, _error_span_range: SpanRange) -> FunctionResult { Ok(self.entries.len()) } } @@ -42,12 +42,12 @@ pub(crate) struct ObjectEntry { } impl ObjectValue { - pub(super) fn into_indexed(mut self, index: Spanned) -> ExecutionResult { + pub(super) fn into_indexed(mut self, index: Spanned) -> FunctionResult { let key = index.downcast_resolve("An object key")?; Ok(self.remove_or_none(key)) } - pub(super) fn into_property(mut self, access: &PropertyAccess) -> ExecutionResult { + pub(super) fn into_property(mut self, access: &PropertyAccess) -> FunctionResult { let key = access.property.to_string(); Ok(self.remove_or_none(&key)) } @@ -76,16 +76,16 @@ impl ObjectValue { &mut self, index: Spanned, auto_create: bool, - ) -> ExecutionResult<&mut AnyValue> { + ) -> FunctionResult<&mut AnyValue> { let index: Spanned<&str> = index.downcast_resolve("An object key")?; self.mut_entry(index.map(|s| s.to_string()), auto_create) } - pub(super) fn index_ref(&self, index: Spanned) -> ExecutionResult<&AnyValue> { + pub(super) fn index_ref(&self, index: Spanned) -> FunctionResult<&AnyValue> { let key: Spanned<&str> = index.downcast_resolve("An object key")?; match self.entries.get(*key) { Some(entry) => Ok(&entry.value), - None => Ok(&AnyValue::None(())), + None => Ok(static_none_ref()), } } @@ -93,18 +93,18 @@ impl ObjectValue { &mut self, access: &PropertyAccess, auto_create: bool, - ) -> ExecutionResult<&mut AnyValue> { + ) -> FunctionResult<&mut AnyValue> { self.mut_entry( access.property.to_string().spanned(access.property.span()), auto_create, ) } - pub(super) fn property_ref(&self, access: &PropertyAccess) -> ExecutionResult<&AnyValue> { + pub(super) fn property_ref(&self, access: &PropertyAccess) -> FunctionResult<&AnyValue> { let key = access.property.to_string(); match self.entries.get(&key) { Some(entry) => Ok(&entry.value), - None => Ok(&AnyValue::None(())), + None => Ok(static_none_ref()), } } @@ -112,7 +112,7 @@ impl ObjectValue { &mut self, Spanned(key, key_span): Spanned, auto_create: bool, - ) -> ExecutionResult<&mut AnyValue> { + ) -> FunctionResult<&mut AnyValue> { use std::collections::btree_map::*; Ok(match self.entries.entry(key) { Entry::Occupied(entry) => &mut entry.into_mut().value, @@ -138,7 +138,8 @@ impl ObjectValue { &self, output: &mut String, behaviour: &ConcatBehaviour, - ) -> ExecutionResult<()> { + interpreter: &mut Interpreter, + ) -> FunctionResult<()> { if !behaviour.use_debug_literal_syntax { return behaviour .error_span_range @@ -176,7 +177,7 @@ impl ObjectValue { entry .value .as_ref_value() - .concat_recursive_into(output, behaviour)?; + .concat_recursive_into(output, behaviour, interpreter)?; is_first = false; } if behaviour.output_literal_structure { @@ -214,7 +215,7 @@ impl ValuesEqual for ObjectValue { } impl Spanned<&ObjectValue> { - pub(crate) fn validate(&self, validation: &impl ObjectValidate) -> ExecutionResult<()> { + pub(crate) fn validate(&self, validation: &impl ObjectValidate) -> FunctionResult<()> { let mut missing_fields = Vec::new(); for (field_name, _) in validation.required_fields() { match self.entries.get(field_name) { @@ -275,11 +276,11 @@ define_type_features! { impl ObjectType, pub(crate) mod object_interface { methods { - [context] fn zip(this: ObjectValue) -> ExecutionResult { + [context] fn zip(this: ObjectValue) -> FunctionResult { ZipIterators::new_from_object(this, context.span_range())?.run_zip(context.interpreter, true) } - [context] fn zip_truncated(this: ObjectValue) -> ExecutionResult { + [context] fn zip_truncated(this: ObjectValue) -> FunctionResult { ZipIterators::new_from_object(this, context.span_range())?.run_zip(context.interpreter, false) } } @@ -342,7 +343,6 @@ pub(crate) trait ObjectValidate { } fn describe_object(&self) -> String { - use std::fmt::Write; let mut buffer = String::new(); buffer.write_str("%{\n").unwrap(); for (key, definition) in self.all_fields() { diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index 1b40d0bb..5a6f4497 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -53,16 +53,16 @@ impl Spanned> { pub(crate) fn parser<'i>( &self, interpreter: &'i mut Interpreter, - ) -> ExecutionResult> { + ) -> FunctionResult> { let Spanned(handle, span) = self; interpreter.parser(**handle, *span) } - pub(crate) fn parse_with( + pub(crate) fn parse_with( &self, interpreter: &mut Interpreter, - f: impl FnOnce(&mut Interpreter) -> ExecutionResult, - ) -> ExecutionResult { + f: impl FnOnce(&mut Interpreter) -> Result, + ) -> Result { let Spanned(handle, _) = self; interpreter.parse_with(**handle, f) } @@ -71,7 +71,7 @@ impl Spanned> { fn parser<'a>( this: Spanned>, context: &'a mut FunctionCallContext, -) -> ExecutionResult> { +) -> FunctionResult> { this.parser(context.interpreter) } @@ -82,12 +82,12 @@ define_type_features! { // GENERAL // ======= - [context] fn is_end(this: Spanned>) -> ExecutionResult { + [context] fn is_end(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.is_empty()) } // Asserts that the parser has reached the end of input - [context] fn end(this: Spanned>) -> ExecutionResult<()> { + [context] fn end(this: Spanned>) -> FunctionResult<()> { let parser = parser(this, context)?; match parser.is_empty() { true => Ok(()), @@ -95,37 +95,37 @@ define_type_features! { } } - [context] fn token_tree(this: Spanned>) -> ExecutionResult { + [context] fn token_tree(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.parse()?) } - [context] fn ident(this: Spanned>) -> ExecutionResult { + [context] fn ident(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.parse()?) } - [context] fn any_ident(this: Spanned>) -> ExecutionResult { + [context] fn any_ident(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.parse_any_ident()?) } - [context] fn punct(this: Spanned>) -> ExecutionResult { + [context] fn punct(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.parse()?) } - [context] fn read(this: Spanned>, parse_template: AnyRef) -> ExecutionResult { + [context] fn read(this: Spanned>, parse_template: AnyRef) -> FunctionResult { let this = parser(this, context)?; let mut output = OutputStream::new(); parse_template.parse_exact_match(this, &mut output)?; Ok(output) } - [context] fn rest(this: Spanned>) -> ExecutionResult { + [context] fn rest(this: Spanned>) -> FunctionResult { let input = parser(this, context)?; let mut output = OutputStream::new(); ParseUntil::End.handle_parse_into(input, &mut output)?; Ok(output) } - [context] fn until(this: Spanned>, until: OutputStream) -> ExecutionResult { + [context] fn until(this: Spanned>, until: OutputStream) -> FunctionResult { let input = parser(this, context)?; let until: ParseUntil = until.parse_as()?; let mut output = OutputStream::new(); @@ -133,7 +133,7 @@ define_type_features! { Ok(output) } - [context] fn error(this: Spanned>, message: String) -> ExecutionResult<()> { + [context] fn error(this: Spanned>, message: String) -> FunctionResult<()> { let parser = parser(this, context)?; parser.parse_err(message).map_err(|e| e.into()) } @@ -143,9 +143,9 @@ define_type_features! { // Opens a group with the specified delimiter character ('(', '{', or '['). // Must be paired with `close`. - [context] fn open(this: Spanned>, Spanned(delimiter_char, char_span): Spanned) -> ExecutionResult<()> { + [context] fn open(this: Spanned>, Spanned(delimiter_char, char_span): Spanned) -> FunctionResult<()> { let delimiter = delimiter_from_open_char(delimiter_char) - .ok_or_else(|| char_span.value_error(format!( + .ok_or_else(|| char_span.value_error::(format!( "Invalid open delimiter '{}'. Expected '(', '{{', or '['", delimiter_char )))?; this.parse_with(context.interpreter, |interpreter| { @@ -156,15 +156,15 @@ define_type_features! { // Closes the current group. Must be paired with a prior `open`. // The close character must match: ')' for '(', '}' for '{', ']' for '[' - [context] fn close(this: Spanned>, Spanned(delimiter_char, char_span): Spanned) -> ExecutionResult<()> { + [context] fn close(this: Spanned>, Spanned(delimiter_char, char_span): Spanned) -> FunctionResult<()> { let expected_delimiter = delimiter_from_close_char(delimiter_char) - .ok_or_else(|| char_span.value_error(format!( + .ok_or_else(|| char_span.value_error::(format!( "Invalid close delimiter '{}'. Expected ')', '}}', or ']'", delimiter_char )))?; this.parse_with(context.interpreter, |interpreter| { // Check if there's a group to close first if !interpreter.has_active_input_group() { - return Err(char_span.value_error(format!( + return Err(char_span.value_error::(format!( "attempting to close '{}' isn't valid, because there is no open group", expected_delimiter.description_of_close() ))); @@ -182,72 +182,72 @@ define_type_features! { // LITERALS // ======== - [context] fn is_literal(this: Spanned>) -> ExecutionResult { + [context] fn is_literal(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.cursor().literal().is_some()) } - [context] fn literal(this: Spanned>) -> ExecutionResult { + [context] fn literal(this: Spanned>) -> FunctionResult { let literal = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_literal(literal))) } - [context] fn inferred_literal(this: Spanned>) -> ExecutionResult { + [context] fn inferred_literal(this: Spanned>) -> FunctionResult { let literal = parser(this, context)?.parse()?; Ok(AnyValue::for_literal(literal).into_any_value()) } - [context] fn is_char(this: Spanned>) -> ExecutionResult { + [context] fn is_char(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.peek(syn::LitChar)) } - [context] fn char_literal(this: Spanned>) -> ExecutionResult { + [context] fn char_literal(this: Spanned>) -> FunctionResult { let char: syn::LitChar = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_tokens(char))) } - [context] fn char(this: Spanned>) -> ExecutionResult { + [context] fn char(this: Spanned>) -> FunctionResult { let char: syn::LitChar = parser(this, context)?.parse()?; Ok(char.value()) } - [context] fn is_string(this: Spanned>) -> ExecutionResult { + [context] fn is_string(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.peek(syn::LitStr)) } - [context] fn string_literal(this: Spanned>) -> ExecutionResult { + [context] fn string_literal(this: Spanned>) -> FunctionResult { let string: syn::LitStr = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_tokens(string))) } - [context] fn string(this: Spanned>) -> ExecutionResult { + [context] fn string(this: Spanned>) -> FunctionResult { let string: syn::LitStr = parser(this, context)?.parse()?; Ok(string.value()) } - [context] fn is_integer(this: Spanned>) -> ExecutionResult { + [context] fn is_integer(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.peek(syn::LitInt)) } - [context] fn integer_literal(this: Spanned>) -> ExecutionResult { + [context] fn integer_literal(this: Spanned>) -> FunctionResult { let integer: syn::LitInt = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_tokens(integer))) } - [context] fn integer(this: Spanned>) -> ExecutionResult { + [context] fn integer(this: Spanned>) -> FunctionResult { let integer: syn::LitInt = parser(this, context)?.parse()?; Ok(IntegerValue::for_litint(&integer)?) } - [context] fn is_float(this: Spanned>) -> ExecutionResult { + [context] fn is_float(this: Spanned>) -> FunctionResult { Ok(parser(this, context)?.peek(syn::LitFloat)) } - [context] fn float_literal(this: Spanned>) -> ExecutionResult { + [context] fn float_literal(this: Spanned>) -> FunctionResult { let float: syn::LitFloat = parser(this, context)?.parse()?; Ok(OutputStream::new_with(|s| s.push_tokens(float))) } - [context] fn float(this: Spanned>) -> ExecutionResult { + [context] fn float(this: Spanned>) -> FunctionResult { let float: syn::LitFloat = parser(this, context)?.parse()?; Ok(FloatValue::for_litfloat(&float)?) } @@ -355,9 +355,7 @@ impl Evaluate for ParseTemplateLiteral { parser.parse_with(interpreter, |interpreter| self.content.consume(interpreter))?; - ownership - .map_from_owned(Spanned(().into_any_value(), self.span_range())) - .map(|spanned| spanned.0) + Ok(ownership.map_none(self.span_range())?.0) } } diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index 656238aa..9ef583d8 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -10,11 +10,11 @@ define_leaf_type! { articled_value_name: "a range", dyn_impls: { IterableType: impl IsIterable { - fn into_iterator(self: Box) -> ExecutionResult { + fn into_iterator(self: Box) -> FunctionResult { IteratorValue::new_for_range(*self) } - fn len(&self, error_span_range: SpanRange) -> ExecutionResult { + fn iterable_len(&self, error_span_range: SpanRange) -> FunctionResult { self.len(error_span_range) } } @@ -27,24 +27,27 @@ pub(crate) struct RangeValue { } impl RangeValue { - pub(crate) fn len(&self, error_span_range: SpanRange) -> ExecutionResult { - IteratorValue::new_for_range(self.clone())?.len(error_span_range) + pub(crate) fn len(&self, error_span_range: SpanRange) -> FunctionResult { + IteratorValue::new_for_range(self.clone())?.do_len(error_span_range) } pub(crate) fn concat_recursive_into( &self, output: &mut String, behaviour: &ConcatBehaviour, - ) -> ExecutionResult<()> { + interpreter: &mut Interpreter, + ) -> FunctionResult<()> { if !behaviour.use_debug_literal_syntax { - return IteratorValue::any_iterator_to_string( - self.clone().inner.into_iterable()?.resolve_iterator()?, + let mut iter = IteratorValue::new_for_range(self.clone())?; + return any_items_to_string( + &mut iter, output, behaviour, "[]", "[ ", "]", true, + interpreter, ); } match &*self.inner { @@ -53,27 +56,35 @@ impl RangeValue { end_exclusive, .. } => { - start_inclusive - .as_ref_value() - .concat_recursive_into(output, behaviour)?; + start_inclusive.as_ref_value().concat_recursive_into( + output, + behaviour, + interpreter, + )?; output.push_str(".."); - end_exclusive - .as_ref_value() - .concat_recursive_into(output, behaviour)?; + end_exclusive.as_ref_value().concat_recursive_into( + output, + behaviour, + interpreter, + )?; } RangeValueInner::RangeFrom { start_inclusive, .. } => { - start_inclusive - .as_ref_value() - .concat_recursive_into(output, behaviour)?; + start_inclusive.as_ref_value().concat_recursive_into( + output, + behaviour, + interpreter, + )?; output.push_str(".."); } RangeValueInner::RangeTo { end_exclusive, .. } => { output.push_str(".."); - end_exclusive - .as_ref_value() - .concat_recursive_into(output, behaviour)?; + end_exclusive.as_ref_value().concat_recursive_into( + output, + behaviour, + interpreter, + )?; } RangeValueInner::RangeFull { .. } => { output.push_str(".."); @@ -83,19 +94,25 @@ impl RangeValue { end_inclusive, .. } => { - start_inclusive - .as_ref_value() - .concat_recursive_into(output, behaviour)?; + start_inclusive.as_ref_value().concat_recursive_into( + output, + behaviour, + interpreter, + )?; output.push_str("..="); - end_inclusive - .as_ref_value() - .concat_recursive_into(output, behaviour)?; + end_inclusive.as_ref_value().concat_recursive_into( + output, + behaviour, + interpreter, + )?; } RangeValueInner::RangeToInclusive { end_inclusive, .. } => { output.push_str("..="); - end_inclusive - .as_ref_value() - .concat_recursive_into(output, behaviour)?; + end_inclusive.as_ref_value().concat_recursive_into( + output, + behaviour, + interpreter, + )?; } } Ok(()) @@ -106,7 +123,7 @@ impl Spanned<&RangeValue> { pub(crate) fn resolve_to_index_range( self, array: &ArrayValue, - ) -> ExecutionResult> { + ) -> FunctionResult> { let Spanned(value, span_range) = self; let mut start = 0; let mut end = array.items.len(); @@ -325,7 +342,7 @@ impl RangeValueInner { } } - pub(super) fn into_iterable(self) -> ExecutionResult> { + pub(super) fn into_iterable(self) -> FunctionResult> { Ok(match self { Self::Range { start_inclusive, @@ -399,9 +416,9 @@ define_type_features! { impl RangeType, pub(crate) mod range_interface { unary_operations { - [context] fn cast_via_iterator(Spanned(this, span): Spanned) -> ExecutionResult { + [context] fn cast_via_iterator(Spanned(this, span): Spanned) -> FunctionResult { let this_iterator = IteratorValue::new_for_range(this)?; - Ok(context.operation.evaluate(Spanned(this_iterator, span))?.0) + Ok(context.operation.evaluate(Spanned(this_iterator, span), context.interpreter)?.0) } } interface_items { @@ -437,7 +454,7 @@ fn resolve_range + ResolvableRange>( start: T, dots: syn::RangeLimits, end: Option>, -) -> ExecutionResult>> { +) -> FunctionResult { let definition = match (end, dots) { (Some(end), dots) => { let end = end.resolve_as("The end of this range bound")?; @@ -452,15 +469,11 @@ fn resolve_range + ResolvableRange>( } trait ResolvableRange: Sized { - fn resolve( - definition: IterableRangeOf, - ) -> ExecutionResult>>; + fn resolve(definition: IterableRangeOf) -> FunctionResult; } impl IterableRangeOf { - pub(super) fn resolve_iterator( - self, - ) -> ExecutionResult>> { + pub(super) fn resolve_iterator(self) -> FunctionResult { let (start, dots, end) = match self { Self::RangeFromTo { start, dots, end } => { (start, dots, Some(end.spanned(dots.span_range()))) @@ -498,9 +511,7 @@ impl IterableRangeOf { } impl ResolvableRange for UntypedInteger { - fn resolve( - definition: IterableRangeOf, - ) -> ExecutionResult>> { + fn resolve(definition: IterableRangeOf) -> FunctionResult { match definition { IterableRangeOf::RangeFromTo { start, dots, end } => { let start = start.into_fallback(); @@ -531,7 +542,7 @@ macro_rules! define_range_resolvers { $($the_type:ident),* $(,)? ) => {$( impl ResolvableRange for $the_type { - fn resolve(definition: IterableRangeOf) -> ExecutionResult>> { + fn resolve(definition: IterableRangeOf) -> FunctionResult { match definition { IterableRangeOf::RangeFromTo { start, dots, end } => { Ok(match dots { diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index 8076f66e..e4d3dc3e 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -8,11 +8,11 @@ define_leaf_type! { articled_value_name: "a stream", dyn_impls: { IterableType: impl IsIterable { - fn into_iterator(self: Box) -> ExecutionResult { + fn into_iterator(self: Box) -> FunctionResult { Ok(IteratorValue::new_for_stream(*self)) } - fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + fn iterable_len(&self, _error_span_range: SpanRange) -> FunctionResult { Ok(self.len()) } } @@ -132,7 +132,7 @@ define_type_features! { this.is_empty() } - fn flatten(this: OutputStream) -> ExecutionResult { + fn flatten(this: OutputStream) -> FunctionResult { Ok(this.to_token_stream_removing_any_transparent_groups()) } @@ -142,39 +142,39 @@ define_type_features! { OutputStream::raw(this.to_token_stream_removing_any_transparent_groups()) } - fn infer(this: OutputStream) -> ExecutionResult { + fn infer(this: OutputStream) -> FunctionResult { Ok(this.coerce_into_value()) } - fn split(this: OutputStream, separator: AnyRef, settings: Option) -> ExecutionResult { + fn split(this: OutputStream, separator: AnyRef, settings: Option) -> FunctionResult { handle_split(this, &separator, settings.unwrap_or_default()) } // STRING-BASED CONVERSION METHODS // =============================== - [context] fn to_ident(this: Spanned>) -> ExecutionResult { + [context] fn to_ident(this: Spanned>) -> FunctionResult { let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); string_interface::methods::to_ident(context, string.into_spanned_ref(this.span_range())) } - [context] fn to_ident_camel(this: Spanned>) -> ExecutionResult { + [context] fn to_ident_camel(this: Spanned>) -> FunctionResult { let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); string_interface::methods::to_ident_camel(context, string.into_spanned_ref(this.span_range())) } - [context] fn to_ident_snake(this: Spanned>) -> ExecutionResult { + [context] fn to_ident_snake(this: Spanned>) -> FunctionResult { let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); string_interface::methods::to_ident_snake(context, string.into_spanned_ref(this.span_range())) } - [context] fn to_ident_upper_snake(this: Spanned>) -> ExecutionResult { + [context] fn to_ident_upper_snake(this: Spanned>) -> FunctionResult { let string = this.concat_content(&ConcatBehaviour::standard(this.span_range())); string_interface::methods::to_ident_upper_snake(context, string.into_spanned_ref(this.span_range())) } // Some literals become Value::UnsupportedLiteral but can still be round-tripped back to a stream - [context] fn to_literal(this: Spanned>) -> ExecutionResult { + [context] fn to_literal(this: Spanned>) -> FunctionResult { let string = this.concat_content(&ConcatBehaviour::literal(this.span_range())); let literal = string_interface::methods::to_literal(context, string.into_spanned_ref(this.span_range()))?; Ok(AnyValue::for_literal(literal).into_any_value()) @@ -184,18 +184,18 @@ define_type_features! { // ============ // NOTE: with_span() exists on all values, this is just a specialized mutable version for streams - fn set_span(mut this: Mutable, span_source: AnyRef) -> ExecutionResult<()> { + fn set_span(mut this: Mutable, span_source: AnyRef) -> FunctionResult<()> { let span_range = span_source.resolve_content_span_range().unwrap_or(Span::call_site().span_range()); this.replace_first_level_spans(span_range.join_into_span_else_start()); Ok(()) } - fn error(this: AnyRef, message: AnyRef) -> ExecutionResult { + fn error(this: AnyRef, message: AnyRef) -> FunctionResult { let error_span_range = this.resolve_content_span_range().unwrap_or(Span::call_site().span_range()); error_span_range.assertion_err(message.as_str()) } - fn assert(this: AnyRef, condition: bool, message: Option>) -> ExecutionResult<()> { + fn assert(this: AnyRef, condition: bool, message: Option>) -> FunctionResult<()> { if condition { Ok(()) } else { @@ -208,7 +208,7 @@ define_type_features! { } } - fn assert_eq(this: AnyRef, lhs: Spanned, rhs: Spanned, message: Option>) -> ExecutionResult<()> { + [context] fn assert_eq(this: AnyRef, lhs: Spanned, rhs: Spanned, message: Option>) -> FunctionResult<()> { match AnyValueRef::debug_eq(&lhs.as_ref_value(), &rhs.as_ref_value()) { Ok(()) => Ok(()), Err(debug_error) => { @@ -218,8 +218,8 @@ define_type_features! { None => format!( "Assertion failed: {}\n lhs = {}\n rhs = {}", debug_error.format_message(), - lhs.as_ref_value().concat_recursive(&ConcatBehaviour::debug(lhs.span_range()))?, - rhs.as_ref_value().concat_recursive(&ConcatBehaviour::debug(rhs.span_range()))?, + lhs.as_ref_value().concat_recursive(&ConcatBehaviour::debug(lhs.span_range()), context.interpreter)?, + rhs.as_ref_value().concat_recursive(&ConcatBehaviour::debug(rhs.span_range()), context.interpreter)?, ), }; error_span_range.assertion_err(message) @@ -227,36 +227,39 @@ define_type_features! { } } - [context] fn reinterpret_as_run(Spanned(this, span_range): Spanned) -> ExecutionResult { + [context] fn reinterpret_as_run(Spanned(this, span_range): Spanned) -> FunctionResult { let source = this.into_token_stream(); let (reparsed, scope_definitions) = source.source_parse_and_analyze(ExpressionBlockContent::parse, ExpressionBlockContent::control_flow_pass)?; let mut inner_interpreter = Interpreter::new(scope_definitions); - let return_value = reparsed.evaluate_spanned(&mut inner_interpreter, span_range, RequestedOwnership::owned())?.expect_owned(); + let return_value = reparsed.evaluate_spanned(&mut inner_interpreter, span_range, RequestedOwnership::owned()) + .expect_no_interrupts()? + .expect_owned(); if !inner_interpreter.complete().is_empty() { return context.control_flow_err("reinterpret_as_run does not allow non-empty emit output") } Ok(return_value.0) } - fn reinterpret_as_stream(Spanned(this, span_range): Spanned) -> ExecutionResult { + fn reinterpret_as_stream(Spanned(this, span_range): Spanned) -> FunctionResult { let source = this.into_token_stream(); let (reparsed, scope_definitions) = source.source_parse_and_analyze( |input| SourceStream::parse_with_span(input, span_range.span_from_join_else_start()), SourceStream::control_flow_pass, )?; let mut inner_interpreter = Interpreter::new(scope_definitions); - reparsed.interpret(&mut inner_interpreter)?; + reparsed.output_to_stream(&mut OutputInterpreter::new_unchecked(&mut inner_interpreter)) + .expect_no_interrupts()?; Ok(inner_interpreter.complete()) } } unary_operations { - [context] fn cast_coerced_to_value(Spanned(this, span): Spanned) -> ExecutionResult { + [context] fn cast_coerced_to_value(Spanned(this, span): Spanned) -> FunctionResult { let coerced = this.coerce_into_value(); if let AnyValue::Stream(_) = &coerced { return span.value_err("The stream could not be coerced into a single value"); } // Re-run the cast operation on the coerced value - Ok(context.operation.evaluate(Spanned(coerced, span))?.0) + Ok(context.operation.evaluate(Spanned(coerced, span), context.interpreter)?.0) } } binary_operations { @@ -329,13 +332,13 @@ impl ParseSource for StreamLiteral { } } -impl Interpret for StreamLiteral { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { +impl OutputToStream for StreamLiteral { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { match self { - StreamLiteral::Regular(lit) => lit.interpret(interpreter), - StreamLiteral::Raw(lit) => lit.interpret(interpreter), - StreamLiteral::Grouped(lit) => lit.interpret(interpreter), - StreamLiteral::Concatenated(lit) => lit.interpret(interpreter), + StreamLiteral::Regular(lit) => lit.output_to_stream(output), + StreamLiteral::Raw(lit) => lit.output_to_stream(output), + StreamLiteral::Grouped(lit) => lit.output_to_stream(output), + StreamLiteral::Concatenated(lit) => lit.output_to_stream(output), } } } @@ -374,9 +377,9 @@ impl ParseSource for RegularStreamLiteral { } } -impl Interpret for RegularStreamLiteral { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { - self.content.interpret(interpreter) +impl OutputToStream for RegularStreamLiteral { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { + self.content.output_to_stream(output) } } @@ -412,11 +415,9 @@ impl ParseSource for RawStreamLiteral { } } -impl Interpret for RawStreamLiteral { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { - interpreter - .output(self)? - .extend_raw_tokens(self.content.clone()); +impl OutputToStream for RawStreamLiteral { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { + output.extend_raw_tokens(self.content.clone()); Ok(()) } } @@ -453,10 +454,10 @@ impl ParseSource for GroupedStreamLiteral { } } -impl Interpret for GroupedStreamLiteral { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { - interpreter.in_output_group(Delimiter::None, self.brackets.span(), |interpreter| { - self.content.interpret(interpreter) +impl OutputToStream for GroupedStreamLiteral { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { + output.in_output_group(Delimiter::None, self.brackets.span(), |output| { + self.content.output_to_stream(output) }) } } @@ -516,10 +517,9 @@ impl ParseSource for ConcatenatedStreamLiteral { } } -impl Interpret for ConcatenatedStreamLiteral { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { - let stream = - interpreter.capture_output(|interpreter| self.content.interpret(interpreter))?; +impl OutputToStream for ConcatenatedStreamLiteral { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { + let stream = output.capture_output(|output| self.content.output_to_stream(output))?; let string = stream.concat_content(&ConcatBehaviour::standard(self.span_range())); let ident_span = stream .resolve_content_span_range() @@ -548,10 +548,13 @@ impl Interpret for ConcatenatedStreamLiteral { string_to_literal(str, self, ident_span)?.into_any_value() } }; - value.as_ref_value().output_to( - Grouping::Flattened, - &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), - ) + value + .as_ref_value() + .output_to( + Grouping::Flattened, + &mut ToStreamContext::new(output, self.span_range()), + ) + .into_execution_result() } } diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index 5f32a666..4be261a4 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -8,11 +8,11 @@ define_leaf_type! { articled_value_name: "a string", dyn_impls: { IterableType: impl IsIterable { - fn into_iterator(self: Box) -> ExecutionResult { + fn into_iterator(self: Box) -> FunctionResult { Ok(IteratorValue::new_for_string_over_chars(*self)) } - fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + fn iterable_len(&self, _error_span_range: SpanRange) -> FunctionResult { // The iterator is over chars, so this must count chars. // But contrast, string.len() counts bytes Ok(self.chars().count()) @@ -46,9 +46,10 @@ pub(crate) fn string_to_ident( str: &str, error_source: &impl HasSpanRange, span: Span, -) -> ExecutionResult { +) -> FunctionResult { let ident = parse_str::(str).map_err(|err| { - error_source.value_error(format!("`{}` is not a valid ident: {:?}", str, err)) + error_source + .value_error::(format!("`{}` is not a valid ident: {:?}", str, err)) })?; Ok(ident.with_span(span)) } @@ -57,9 +58,10 @@ pub(crate) fn string_to_literal( str: &str, error_source: &impl HasSpanRange, span: Span, -) -> ExecutionResult { +) -> FunctionResult { let literal = Literal::from_str(str).map_err(|err| { - error_source.value_error(format!("`{}` is not a valid literal: {:?}", str, err)) + error_source + .value_error::(format!("`{}` is not a valid literal: {:?}", str, err)) })?; Ok(literal.with_span(span)) } @@ -71,26 +73,26 @@ define_type_features! { // ================== // CONVERSION METHODS // ================== - [context] fn to_ident(this: Spanned>) -> ExecutionResult { + [context] fn to_ident(this: Spanned>) -> FunctionResult { string_to_ident(&this, &this, context.span_from_join_else_start()) } - [context] fn to_ident_camel(this: Spanned>) -> ExecutionResult { + [context] fn to_ident_camel(this: Spanned>) -> FunctionResult { let str = string_conversion::to_upper_camel_case(&this); string_to_ident(&str, &this, context.span_from_join_else_start()) } - [context] fn to_ident_snake(this: Spanned>) -> ExecutionResult { + [context] fn to_ident_snake(this: Spanned>) -> FunctionResult { let str = string_conversion::to_lower_snake_case(&this); string_to_ident(&str, &this, context.span_from_join_else_start()) } - [context] fn to_ident_upper_snake(this: Spanned>) -> ExecutionResult { + [context] fn to_ident_upper_snake(this: Spanned>) -> FunctionResult { let str = string_conversion::to_upper_snake_case(&this); string_to_ident(&str, &this, context.span_from_join_else_start()) } - [context] fn to_literal(this: Spanned>) -> ExecutionResult { + [context] fn to_literal(this: Spanned>) -> FunctionResult { string_to_literal(&this, &this, context.span_from_join_else_start()) } @@ -228,7 +230,7 @@ impl ResolvableShared for str { fn resolve_from_ref<'a>( value: &'a AnyValue, context: ResolutionContext, - ) -> ExecutionResult<&'a Self> { + ) -> FunctionResult<&'a Self> { match value { AnyValueContent::String(s) => Ok(s.as_str()), _ => context.err("a string", value), diff --git a/src/extensions/errors_and_spans.rs b/src/extensions/errors_and_spans.rs index 9a1c04df..05ae24b8 100644 --- a/src/extensions/errors_and_spans.rs +++ b/src/extensions/errors_and_spans.rs @@ -23,60 +23,81 @@ pub(crate) trait SpanErrorExt { Err(self.syn_error(message).into()) } - fn syntax_err(&self, message: impl std::fmt::Display) -> ExecutionResult { + fn syntax_err>( + &self, + message: impl std::fmt::Display, + ) -> Result { Err(self.syntax_error(message)) } - fn syntax_error(&self, message: impl std::fmt::Display) -> ExecutionInterrupt { - ExecutionInterrupt::syntax_error(self.syn_error(message)) + fn syntax_error>(&self, message: impl std::fmt::Display) -> E { + ExecutionError::new(ErrorKind::Syntax, self.syn_error(message)).into() } - fn type_err(&self, message: impl std::fmt::Display) -> ExecutionResult { + fn type_err>( + &self, + message: impl std::fmt::Display, + ) -> Result { Err(self.type_error(message)) } - fn type_error(&self, message: impl std::fmt::Display) -> ExecutionInterrupt { - ExecutionInterrupt::type_error(self.syn_error(message)) + fn type_error>(&self, message: impl std::fmt::Display) -> E { + ExecutionError::new(ErrorKind::Type, self.syn_error(message)).into() } - fn control_flow_err(&self, message: impl std::fmt::Display) -> ExecutionResult { + fn control_flow_err>( + &self, + message: impl std::fmt::Display, + ) -> Result { Err(self.control_flow_error(message)) } - fn control_flow_error(&self, message: impl std::fmt::Display) -> ExecutionInterrupt { - ExecutionInterrupt::control_flow_error(self.syn_error(message)) + fn control_flow_error>(&self, message: impl std::fmt::Display) -> E { + ExecutionError::new(ErrorKind::ControlFlow, self.syn_error(message)).into() } - fn ownership_err(&self, message: impl std::fmt::Display) -> ExecutionResult { + fn ownership_err>( + &self, + message: impl std::fmt::Display, + ) -> Result { Err(self.ownership_error(message)) } - fn ownership_error(&self, message: impl std::fmt::Display) -> ExecutionInterrupt { - ExecutionInterrupt::ownership_error(self.syn_error(message)) + fn ownership_error>(&self, message: impl std::fmt::Display) -> E { + ExecutionError::new(ErrorKind::Ownership, self.syn_error(message)).into() } - fn assertion_err(&self, message: impl std::fmt::Display) -> ExecutionResult { + fn assertion_err>( + &self, + message: impl std::fmt::Display, + ) -> Result { Err(self.assertion_error(message)) } - fn debug_error(&self, message: impl std::fmt::Display) -> ExecutionInterrupt { - ExecutionInterrupt::debug_error(self.syn_error(message)) + fn assertion_error>(&self, message: impl std::fmt::Display) -> E { + ExecutionError::new(ErrorKind::Assertion, self.syn_error(message)).into() } - fn debug_err(&self, message: impl std::fmt::Display) -> ExecutionResult { + fn debug_err>( + &self, + message: impl std::fmt::Display, + ) -> Result { Err(self.debug_error(message)) } - fn assertion_error(&self, message: impl std::fmt::Display) -> ExecutionInterrupt { - ExecutionInterrupt::assertion_error(self.syn_error(message)) + fn debug_error>(&self, message: impl std::fmt::Display) -> E { + ExecutionError::new(ErrorKind::Debug, self.syn_error(message)).into() } - fn value_err(&self, message: impl std::fmt::Display) -> ExecutionResult { + fn value_err>( + &self, + message: impl std::fmt::Display, + ) -> Result { Err(self.value_error(message)) } - fn value_error(&self, message: impl std::fmt::Display) -> ExecutionInterrupt { - ExecutionInterrupt::value_error(self.syn_error(message)) + fn value_error>(&self, message: impl std::fmt::Display) -> E { + ExecutionError::new(ErrorKind::Value, self.syn_error(message)).into() } } diff --git a/src/internal_prelude.rs b/src/internal_prelude.rs index 34a546b3..7fb94530 100644 --- a/src/internal_prelude.rs +++ b/src/internal_prelude.rs @@ -1,8 +1,7 @@ pub(crate) use proc_macro2::{extra::*, *}; pub(crate) use quote::ToTokens; pub(crate) use std::{ - borrow::Borrow, - borrow::Cow, + borrow::{Borrow, Cow}, cell::{Ref, RefCell, RefMut}, collections::{BTreeMap, HashMap, HashSet}, fmt::Debug, diff --git a/src/interpretation/bindings.rs b/src/interpretation/bindings.rs index 7b673bcf..c83c0355 100644 --- a/src/interpretation/bindings.rs +++ b/src/interpretation/bindings.rs @@ -74,7 +74,7 @@ impl VariableState { is_final: bool, ownership: RequestedOwnership, blocked_from_mutation: Option, - ) -> ExecutionResult> { + ) -> FunctionResult> { let span_range = variable_span.span_range(); // If blocked from mutation, we technically could allow is_final to work and @@ -173,18 +173,20 @@ pub(crate) struct VariableBinding { impl VariableBinding { /// Gets the cloned expression value /// This only works if the value can be transparently cloned - pub(crate) fn into_transparently_cloned(self) -> ExecutionResult { + pub(crate) fn into_transparently_cloned(self) -> FunctionResult { let span_range = self.variable_span.span_range(); let shared = self.into_shared()?; let value = shared.as_ref().try_transparent_clone(span_range)?; Ok(value) } - fn into_mut(self) -> ExecutionResult { + fn into_mut(self) -> FunctionResult { match self.content { VariableContent::Referenceable(referenceable) => { - let inner = MutableSubRcRefCell::new(referenceable) - .map_err(|_| self.variable_span.ownership_error(MUTABLE_ERROR_MESSAGE))?; + let inner = MutableSubRcRefCell::new(referenceable).map_err(|_| { + self.variable_span + .ownership_error::(MUTABLE_ERROR_MESSAGE) + })?; Ok(Mutable(inner)) } VariableContent::Mutable(disabled) => disabled.enable(self.variable_span.span_range()), @@ -194,11 +196,13 @@ impl VariableBinding { } } - fn into_shared(self) -> ExecutionResult { + fn into_shared(self) -> FunctionResult { match self.content { VariableContent::Referenceable(referenceable) => { - let inner = SharedSubRcRefCell::new(referenceable) - .map_err(|_| self.variable_span.ownership_error(SHARED_ERROR_MESSAGE))?; + let inner = SharedSubRcRefCell::new(referenceable).map_err(|_| { + self.variable_span + .ownership_error::(SHARED_ERROR_MESSAGE) + })?; Ok(Shared(inner)) } VariableContent::Mutable(mutable) => mutable @@ -208,14 +212,15 @@ impl VariableBinding { } } - fn into_late_bound(self) -> ExecutionResult { + fn into_late_bound(self) -> FunctionResult { match self.content { VariableContent::Referenceable(referenceable) => { match MutableSubRcRefCell::new(referenceable) { Ok(mutable) => Ok(LateBoundValue::Mutable(Mutable(mutable))), Err(referenceable) => { let shared = SharedSubRcRefCell::new(referenceable).map_err(|_| { - self.variable_span.ownership_error(SHARED_ERROR_MESSAGE) + self.variable_span + .ownership_error::(SHARED_ERROR_MESSAGE) })?; Ok(LateBoundValue::Shared(LateBoundSharedValue::new( Shared(shared), @@ -280,7 +285,7 @@ pub(crate) enum LateBoundValue { } impl Spanned { - pub(crate) fn resolve(self, ownership: ArgumentOwnership) -> ExecutionResult { + pub(crate) fn resolve(self, ownership: ArgumentOwnership) -> FunctionResult { ownership.map_from_late_bound(self) } } @@ -293,13 +298,12 @@ impl LateBoundValue { /// key from an object) to still work, with the mutable error preserved as `reason_not_mutable`. pub(crate) fn map_any( self, - map_shared: impl FnOnce(AnyValueShared) -> ExecutionResult, + map_shared: impl FnOnce(AnyValueShared) -> FunctionResult, map_mutable: impl FnOnce( AnyValueMutable, - ) - -> Result, - map_owned: impl FnOnce(AnyValueOwned) -> ExecutionResult, - ) -> ExecutionResult { + ) -> Result, + map_owned: impl FnOnce(AnyValueOwned) -> FunctionResult, + ) -> FunctionResult { Ok(match self { LateBoundValue::Owned(owned) => LateBoundValue::Owned(LateBoundOwnedValue { owned: map_owned(owned.owned)?, @@ -446,7 +450,7 @@ impl Clone for DisabledMutable { impl DisabledMutable { /// Re-enables this disabled mutable reference by re-acquiring the borrow. - pub(crate) fn enable(self, span: SpanRange) -> ExecutionResult> { + pub(crate) fn enable(self, span: SpanRange) -> FunctionResult> { self.0 .enable() .map(Mutable) @@ -464,7 +468,7 @@ pub(crate) static SHARED_TO_MUTABLE_ERROR_MESSAGE: &str = "The variable cannot be modified as it is a shared reference"; impl Spanned { - pub(crate) fn transparent_clone(&self) -> ExecutionResult { + pub(crate) fn transparent_clone(&self) -> FunctionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; Ok(value) } @@ -569,7 +573,7 @@ impl Clone for DisabledShared { impl DisabledShared { /// Re-enables this disabled shared reference by re-acquiring the borrow. - pub(crate) fn enable(self, span: SpanRange) -> ExecutionResult> { + pub(crate) fn enable(self, span: SpanRange) -> FunctionResult> { self.0 .enable() .map(Shared) @@ -581,7 +585,7 @@ pub(crate) static SHARED_ERROR_MESSAGE: &str = "The variable cannot be read as it is already being modified"; impl Spanned { - pub(crate) fn transparent_clone(&self) -> ExecutionResult { + pub(crate) fn transparent_clone(&self) -> FunctionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; Ok(value) } @@ -689,9 +693,9 @@ impl CopyOnWrite { pub(crate) fn map( self, - map_shared: impl FnOnce(Shared) -> ExecutionResult>, - map_owned: impl FnOnce(Owned) -> ExecutionResult>, - ) -> ExecutionResult> { + map_shared: impl FnOnce(Shared) -> FunctionResult>, + map_owned: impl FnOnce(Owned) -> FunctionResult>, + ) -> FunctionResult> { let inner = match self.inner { CopyOnWriteInner::Owned(owned) => CopyOnWriteInner::Owned(map_owned(owned)?), CopyOnWriteInner::SharedWithInfallibleCloning(shared) => { @@ -765,7 +769,7 @@ where impl DisabledCopyOnWrite { /// Re-enables this disabled copy-on-write value by re-acquiring any borrow. - pub(crate) fn enable(self, span: SpanRange) -> ExecutionResult> { + pub(crate) fn enable(self, span: SpanRange) -> FunctionResult> { let inner = match self.inner { DisabledCopyOnWriteInner::Owned(owned) => CopyOnWriteInner::Owned(owned), DisabledCopyOnWriteInner::SharedWithInfallibleCloning(shared) => { @@ -838,7 +842,7 @@ impl CopyOnWrite { pub(crate) fn clone_to_owned_transparently( self, span: SpanRange, - ) -> ExecutionResult { + ) -> FunctionResult { match self.inner { CopyOnWriteInner::Owned(owned) => Ok(owned), CopyOnWriteInner::SharedWithInfallibleCloning(shared) => Ok(shared.infallible_clone()), diff --git a/src/interpretation/interpret_traits.rs b/src/interpretation/interpret_traits.rs index ccae002a..db4feaa1 100644 --- a/src/interpretation/interpret_traits.rs +++ b/src/interpretation/interpret_traits.rs @@ -1,8 +1,83 @@ use crate::internal_prelude::*; -// This trait isn't so important any more. It can probably be removed in future. -// It is typically used for things which output directly to the interpreter's output -// stream, rather than returning values. -pub(crate) trait Interpret: Sized { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()>; +pub(crate) trait OutputToStream: Sized { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()>; +} + +/// A wrapper around `Interpreter` that is intended to make outputting to the +/// top-most output stream more convenient, performant and less error-prone. +pub(crate) struct OutputInterpreter<'a> { + interpreter: &'a mut Interpreter, +} + +impl<'a> OutputInterpreter<'a> { + /// Should only be used with a new Interpreter + pub(crate) fn new_unchecked(interpreter: &'a mut Interpreter) -> Self { + Self { interpreter } + } + + pub(crate) fn new_checked( + interpreter: &'a mut Interpreter, + span: &impl HasSpanRange, + ) -> ExecutionResult { + // Validate that output is not frozen, then drop the temporary reference + let _ = interpreter.output(span)?; + Ok(Self { interpreter }) + } + + /// Creates a new `OutputInterpreter` by reborrowing from an existing one. + pub(crate) fn reborrow(&mut self) -> OutputInterpreter<'_> { + OutputInterpreter { + interpreter: self.interpreter, + } + } + + /// Executes `f` with mutable access to the underlying `Interpreter`. + /// + /// The executed code must not change the height of the output stack or + /// leave the output in a frozen state — i.e. it should evaluate expressions + /// or resolve variables, not manipulate output buffers directly. + pub(crate) fn with_interpreter( + &mut self, + f: impl FnOnce(&mut Interpreter) -> Result, + ) -> Result { + let height_before = self.interpreter.output_stack_height(); + let result = f(self.interpreter); + assert_eq!( + self.interpreter.output_stack_height(), + height_before, + "OutputInterpreter::with_interpreter: closure must not change the output stack height" + ); + result + } + + pub(crate) fn capture_output( + &mut self, + f: impl FnOnce(&mut OutputInterpreter) -> Result<(), E>, + ) -> Result { + self.interpreter.capture_output(f) + } + + pub(crate) fn in_output_group( + &mut self, + delimiter: Delimiter, + span: Span, + f: impl FnOnce(&mut OutputInterpreter) -> Result<(), E>, + ) -> Result<(), E> { + self.interpreter.in_output_group(delimiter, span, f) + } +} + +impl Deref for OutputInterpreter<'_> { + type Target = OutputStream; + + fn deref(&self) -> &Self::Target { + self.interpreter.current_output_unchecked() + } +} + +impl DerefMut for OutputInterpreter<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.interpreter.current_output_mut_unchecked() + } } diff --git a/src/interpretation/interpreter.rs b/src/interpretation/interpreter.rs index b1a07cd2..19868f66 100644 --- a/src/interpretation/interpreter.rs +++ b/src/interpretation/interpreter.rs @@ -251,7 +251,7 @@ impl Interpreter { &mut self, variable: &VariableReference, ownership: RequestedOwnership, - ) -> ExecutionResult> { + ) -> FunctionResult> { let reference = self.scope_definitions.references.get(variable.id); let (definition, span, is_final) = ( reference.definition, @@ -299,11 +299,11 @@ impl Interpreter { } // Input - pub(crate) fn start_parse( + pub(crate) fn start_parse>( &mut self, stream: OutputStream, - f: impl FnOnce(&mut Interpreter, ParserHandle) -> ExecutionResult, - ) -> ExecutionResult { + f: impl FnOnce(&mut Interpreter, ParserHandle) -> Result, + ) -> Result { stream.parse_with(|input| { let handle = unsafe { // SAFETY: This is paired with `finish_parse` below, @@ -324,11 +324,11 @@ impl Interpreter { }) } - pub(crate) fn parse_with( + pub(crate) fn parse_with( &mut self, handle: ParserHandle, - f: impl FnOnce(&mut Interpreter) -> ExecutionResult, - ) -> ExecutionResult { + f: impl FnOnce(&mut Interpreter) -> Result, + ) -> Result { unsafe { // SAFETY: This is paired with `pop_current_handle` below, // without any early returns in the middle @@ -347,11 +347,10 @@ impl Interpreter { &mut self, handle: ParserHandle, error_span_range: SpanRange, - ) -> ExecutionResult> { - let stack = self - .input_handler - .get(handle) - .ok_or_else(|| error_span_range.value_error("This parser is no longer available"))?; + ) -> FunctionResult> { + let stack = self.input_handler.get(handle).ok_or_else(|| { + error_span_range.value_error::("This parser is no longer available") + })?; Ok(stack.current()) } @@ -377,7 +376,7 @@ impl Interpreter { pub(crate) fn enter_input_group( &mut self, required_delimiter: Option, - ) -> ExecutionResult<(Delimiter, DelimSpan)> { + ) -> FunctionResult<(Delimiter, DelimSpan)> { self.input_handler .current_stack() .parse_and_enter_group(required_delimiter) @@ -389,7 +388,7 @@ impl Interpreter { pub(crate) fn exit_input_group( &mut self, expected_delimiter: Option, - ) -> ExecutionResult<()> { + ) -> FunctionResult<()> { self.input_handler .current_stack() .exit_group(expected_delimiter) @@ -406,20 +405,20 @@ impl Interpreter { } // Output - pub(crate) fn in_output_group( + pub(crate) fn in_output_group( &mut self, delimiter: Delimiter, span: Span, f: F, - ) -> ExecutionResult + ) -> Result where - F: FnOnce(&mut Interpreter) -> ExecutionResult, + F: FnOnce(&mut OutputInterpreter) -> Result, { unsafe { // SAFETY: This is paired with `finish_inner_buffer_as_group` self.output_handler.start_inner_buffer(); } - let result = f(self); + let result = f(&mut OutputInterpreter::new_unchecked(self)); unsafe { // SAFETY: This is paired with `start_inner_buffer`, // even if `f` returns an Err propogating a control flow interrupt. @@ -429,15 +428,15 @@ impl Interpreter { result } - pub(crate) fn capture_output(&mut self, f: F) -> ExecutionResult + pub(crate) fn capture_output(&mut self, f: F) -> Result where - F: FnOnce(&mut Interpreter) -> ExecutionResult<()>, + F: FnOnce(&mut OutputInterpreter) -> Result<(), E>, { unsafe { // SAFETY: This is paired with `finish_inner_buffer_as_separate_stream` self.output_handler.start_inner_buffer(); } - let result = f(self); + let result = f(&mut OutputInterpreter::new_unchecked(self)); let output = unsafe { // SAFETY: This is paired with `start_inner_buffer`, // even if `f` returns an Err propogating a control flow interrupt. @@ -447,6 +446,18 @@ impl Interpreter { Ok(output) } + pub(crate) fn output_stack_height(&self) -> usize { + self.output_handler.output_stack_height() + } + + pub(crate) fn current_output_unchecked(&self) -> &OutputStream { + self.output_handler.current_output_unchecked() + } + + pub(crate) fn current_output_mut_unchecked(&mut self) -> &mut OutputStream { + self.output_handler.current_output_mut_unchecked() + } + pub(crate) fn output( &mut self, span_source: &impl HasSpanRange, @@ -501,7 +512,7 @@ impl RuntimeScope { is_final: bool, ownership: RequestedOwnership, blocked_from_mutation: Option, - ) -> ExecutionResult> { + ) -> FunctionResult> { self.variables .get_mut(&definition_id) .expect("Variable data not found in scope") @@ -516,12 +527,12 @@ pub(crate) struct IterationCounter<'a, S: HasSpanRange> { } impl IterationCounter<'_, S> { - pub(crate) fn increment_and_check(&mut self) -> ExecutionResult<()> { + pub(crate) fn increment_and_check(&mut self) -> FunctionResult<()> { self.count += 1; self.check() } - pub(crate) fn check(&self) -> ExecutionResult<()> { + pub(crate) fn check(&self) -> FunctionResult<()> { if self.count > self.iteration_limit { return self.span_source.control_flow_err(format!("Iteration limit of {} exceeded.\nIf needed, the limit can be reconfigured with preinterpret::set_iteration_limit(XXX)", self.iteration_limit)); } diff --git a/src/interpretation/output_handler.rs b/src/interpretation/output_handler.rs index f10cdfa2..036f9842 100644 --- a/src/interpretation/output_handler.rs +++ b/src/interpretation/output_handler.rs @@ -94,6 +94,22 @@ impl OutputHandler { ); } + pub(super) fn output_stack_height(&self) -> usize { + self.output_stack.len() + } + + pub(super) fn current_output_unchecked(&self) -> &OutputStream { + self.output_stack + .last() + .expect("Output stack should never be empty") + } + + pub(super) fn current_output_mut_unchecked(&mut self) -> &mut OutputStream { + self.output_stack + .last_mut() + .expect("Output stack should never be empty") + } + fn validate_index(&self, index: usize) -> Result<(), OutputHandlerError> { if let Some(&(freeze_at_or_below_depth, reason)) = self.freeze_stack_indices_at_or_below.last() diff --git a/src/interpretation/output_parse_utilities.rs b/src/interpretation/output_parse_utilities.rs index cc03fdba..16da5a32 100644 --- a/src/interpretation/output_parse_utilities.rs +++ b/src/interpretation/output_parse_utilities.rs @@ -44,7 +44,7 @@ impl ParseUntil { &self, input: OutputParseStream, output: &mut OutputStream, - ) -> ExecutionResult<()> { + ) -> FunctionResult<()> { match self { ParseUntil::End => { let remaining = input.parse::()?; @@ -98,7 +98,7 @@ pub(crate) fn handle_parsing_exact_output_match( input: ParseStream, expected: &OutputStream, output: &mut OutputStream, -) -> ExecutionResult<()> { +) -> FunctionResult<()> { for item in expected.iter() { match item { OutputTokenTreeRef::TokenTree(token_tree) => { @@ -123,7 +123,7 @@ fn handle_parsing_exact_stream_match( input: ParseStream, expected: TokenStream, output: &mut OutputStream, -) -> ExecutionResult<()> { +) -> FunctionResult<()> { for item in expected.into_iter() { handle_parsing_exact_token_tree(input, &item, output)?; } @@ -134,7 +134,7 @@ fn handle_parsing_exact_token_tree( input: ParseStream, expected: &TokenTree, output: &mut OutputStream, -) -> ExecutionResult<()> { +) -> FunctionResult<()> { match expected { TokenTree::Group(group) => { handle_parsing_exact_group( @@ -162,9 +162,9 @@ fn handle_parsing_exact_token_tree( fn handle_parsing_exact_group( input: ParseStream, delimiter: Delimiter, - parse_inner: impl FnOnce(ParseStream, &mut OutputStream) -> ExecutionResult<()>, + parse_inner: impl FnOnce(ParseStream, &mut OutputStream) -> FunctionResult<()>, output: &mut OutputStream, -) -> ExecutionResult<()> { +) -> FunctionResult<()> { // Because `None` is ignored by Syn at parsing time, we can effectively be most permissive by ignoring them. // This removes a bit of a footgun for users. // If they really want to check for a None group, they can embed `@[GROUP @[EXACT ...]]` transformer. diff --git a/src/interpretation/output_stream.rs b/src/interpretation/output_stream.rs index aedc470c..26040093 100644 --- a/src/interpretation/output_stream.rs +++ b/src/interpretation/output_stream.rs @@ -61,10 +61,10 @@ impl OutputStream { pub(crate) fn push_grouped( &mut self, - appender: impl FnOnce(&mut Self) -> ExecutionResult<()>, + appender: impl FnOnce(&mut Self) -> FunctionResult<()>, delimiter: Delimiter, span: Span, - ) -> ExecutionResult<()> { + ) -> FunctionResult<()> { let mut inner = Self::new(); appender(&mut inner)?; self.push_new_group(inner, delimiter, span); @@ -131,10 +131,10 @@ impl OutputStream { /// WARNING: With rust-analyzer, this loses transparent groups which have been inserted. /// Use only where that doesn't matter: https://github.com/rust-lang/rust-analyzer/issues/18211#issuecomment-2604547032 - pub(crate) fn parse_with( + pub(crate) fn parse_with>( self, - parser: impl FnOnce(ParseStream) -> ExecutionResult, - ) -> ExecutionResult { + parser: impl FnOnce(ParseStream) -> Result, + ) -> Result { self.into_token_stream().interpreted_parse_with(parser) } @@ -274,7 +274,7 @@ impl OutputStream { } } - fn concat_recursive_token_stream>( + fn concat_recursive_token_stream>( behaviour: &ConcatBehaviour, output: &mut String, prefix_spacing: Spacing, @@ -340,7 +340,7 @@ impl OutputStream { &self, input: ParseStream, output: &mut OutputStream, - ) -> ExecutionResult<()> { + ) -> FunctionResult<()> { handle_parsing_exact_output_match(input, self, output) } diff --git a/src/interpretation/source_stream.rs b/src/interpretation/source_stream.rs index da680076..323d17aa 100644 --- a/src/interpretation/source_stream.rs +++ b/src/interpretation/source_stream.rs @@ -23,10 +23,10 @@ impl SourceStream { } } -impl Interpret for SourceStream { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { +impl OutputToStream for SourceStream { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { for item in self.items.iter() { - item.interpret(interpreter)?; + item.output_to_stream(output)?; } Ok(()) } @@ -83,27 +83,25 @@ impl ParseSource for SourceItem { } } -impl Interpret for SourceItem { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { +impl OutputToStream for SourceItem { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { match self { SourceItem::Variable(variable) => { - variable.interpret(interpreter)?; + variable.output_to_stream(output)?; } SourceItem::EmbeddedExpression(block) => { - block.interpret(interpreter)?; + block.output_to_stream(output)?; } SourceItem::EmbeddedStatements(statements) => { - statements.interpret(interpreter)?; + statements.output_to_stream(output)?; } SourceItem::SourceGroup(group) => { - group.interpret(interpreter)?; + group.output_to_stream(output)?; } - SourceItem::Punct(punct) => interpreter.output(punct)?.push_punct(punct.clone()), - SourceItem::Ident(ident) => interpreter.output(ident)?.push_ident(ident.clone()), - SourceItem::Literal(literal) => { - interpreter.output(literal)?.push_literal(literal.clone()) - } - SourceItem::StreamLiteral(stream_literal) => stream_literal.interpret(interpreter)?, + SourceItem::Punct(punct) => output.push_punct(punct.clone()), + SourceItem::Ident(ident) => output.push_ident(ident.clone()), + SourceItem::Literal(literal) => output.push_literal(literal.clone()), + SourceItem::StreamLiteral(stream_literal) => stream_literal.output_to_stream(output)?, } Ok(()) } @@ -147,12 +145,12 @@ impl ParseSource for SourceGroup { } } -impl Interpret for SourceGroup { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { - interpreter.in_output_group( +impl OutputToStream for SourceGroup { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { + output.in_output_group( self.source_delimiter, self.source_delim_span.join(), - |interpreter| self.content.interpret(interpreter), + |output| self.content.output_to_stream(output), ) } } diff --git a/src/interpretation/variable.rs b/src/interpretation/variable.rs index 221f6c4d..dd67fc24 100644 --- a/src/interpretation/variable.rs +++ b/src/interpretation/variable.rs @@ -19,10 +19,10 @@ impl ParseSource for EmbeddedVariable { } } -impl Interpret for EmbeddedVariable { - fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { +impl OutputToStream for EmbeddedVariable { + fn output_to_stream(&self, output: &mut OutputInterpreter) -> ExecutionResult<()> { self.reference - .substitute_into_output(interpreter, Grouping::Flattened) + .substitute_into_output(output, Grouping::Flattened) } } @@ -124,20 +124,23 @@ impl ParseSource for VariableReference { impl VariableReference { fn substitute_into_output( &self, - interpreter: &mut Interpreter, + output: &mut OutputInterpreter, grouping: Grouping, ) -> ExecutionResult<()> { - let value = self.resolve_shared(interpreter)?; - value.as_ref_value().output_to( - grouping, - &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), - ) + let value = output.with_interpreter(|i| self.resolve_shared(i))?; + value + .as_ref_value() + .output_to( + grouping, + &mut ToStreamContext::new(output, self.span_range()), + ) + .into_execution_result() } pub(crate) fn resolve_late_bound( &self, interpreter: &mut Interpreter, - ) -> ExecutionResult> { + ) -> FunctionResult> { interpreter.resolve(self, RequestedOwnership::LateBound) } @@ -145,7 +148,7 @@ impl VariableReference { &self, interpreter: &mut Interpreter, ownership: ArgumentOwnership, - ) -> ExecutionResult { + ) -> FunctionResult { interpreter .resolve(self, RequestedOwnership::Concrete(ownership))? .resolve(ownership) @@ -154,7 +157,7 @@ impl VariableReference { pub(crate) fn resolve_shared( &self, interpreter: &mut Interpreter, - ) -> ExecutionResult { + ) -> FunctionResult { Ok(self .resolve_concrete(interpreter, ArgumentOwnership::Shared)? .expect_shared()) diff --git a/src/lib.rs b/src/lib.rs index 488559d8..0278afe6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -431,7 +431,8 @@ fn preinterpret_stream_internal(input: TokenStream) -> SynResult { let mut interpreter = Interpreter::new(parse_state); stream - .interpret(&mut interpreter) + .output_to_stream(&mut OutputInterpreter::new_unchecked(&mut interpreter)) + .expect_no_interrupts() .convert_to_final_result()?; let output_stream = interpreter.complete(); @@ -460,9 +461,13 @@ fn preinterpret_run_internal(input: TokenStream) -> SynResult { let mut interpreter = Interpreter::new(parse_state); let entry_span = Span::call_site().span_range(); - let returned_stream = content + let returned_value = content .evaluate_spanned(&mut interpreter, entry_span, RequestedOwnership::owned()) - .and_then(|x| x.expect_owned().into_stream()) + .expect_no_interrupts() + .convert_to_final_result()?; + let returned_stream = returned_value + .expect_owned() + .into_stream(&mut interpreter) .convert_to_final_result()?; let mut output_stream = interpreter.complete(); @@ -583,9 +588,13 @@ mod benchmarking { let output = context.time("evaluation", move || -> SynResult { let mut interpreter = Interpreter::new(scopes); let entry_span = Span::call_site().span_range(); - let returned_stream = parsed + let returned_value = parsed .evaluate_spanned(&mut interpreter, entry_span, RequestedOwnership::owned()) - .and_then(|x| x.expect_owned().into_stream()) + .expect_no_interrupts() + .convert_to_final_result()?; + let returned_stream = returned_value + .expect_owned() + .into_stream(&mut interpreter) .convert_to_final_result()?; let mut output_stream = interpreter.complete(); diff --git a/src/misc/errors.rs b/src/misc/errors.rs index 187d3445..27c04977 100644 --- a/src/misc/errors.rs +++ b/src/misc/errors.rs @@ -11,7 +11,7 @@ pub(crate) trait ParseResultExt { impl ParseResultExt for ParseResult { fn convert_to_final_result(self) -> syn::Result { - self.map_err(|error| error.convert_to_final_error()) + self.map_err(|error| error.convert_to_syn_error()) } fn into_execution_result(self) -> ExecutionResult { @@ -85,7 +85,7 @@ impl ParseError { } /// This is not a `From` because it wants to be explicit - pub(crate) fn convert_to_final_error(self) -> syn::Error { + pub(crate) fn convert_to_syn_error(self) -> syn::Error { self.0.convert_to_syn_error() } } @@ -101,13 +101,70 @@ pub(crate) enum ExecutionOutcome { } pub(crate) trait ExecutionResultExt { + /// Asserts that the result contains no control flow interrupts (only errors). + /// This is used at boundaries where control flow should have already been caught. + fn expect_no_interrupts(self) -> FunctionResult; +} + +impl ExecutionResultExt for ExecutionResult { + fn expect_no_interrupts(self) -> FunctionResult { + self.map_err(FunctionError::new) + } +} + +/// A result type for functions that can produce errors but NOT control-flow interrupts. +pub(crate) type FunctionResult = core::result::Result; + +/// A newtype wrapping `ExecutionInterrupt` that asserts it is not a control flow interrupt. +/// This is used for functions that cannot produce control flow (break/continue/revert). +#[derive(Debug)] +pub(crate) struct FunctionError(ExecutionInterrupt); + +impl FunctionError { + pub(crate) fn new(interrupt: ExecutionInterrupt) -> Self { + debug_assert!( + !matches!( + interrupt.inner.as_ref(), + ExecutionInterruptInner::ControlFlowInterrupt(_) + ), + "FunctionError should not wrap a control flow interrupt. \ + Please report this bug at https://github.com/dhedey/preinterpret/issues" + ); + FunctionError(interrupt) + } + + /// Determines if the error can be caught when attempting to map a late-bound + /// mutable value, to retry as a shared value instead. + pub(crate) fn into_caught_mutable_map_attempt_error(self) -> Result { + match self.0.inner.as_ref() { + ExecutionInterruptInner::Error(ExecutionError(ErrorKind::Value, _)) => { + Ok(self.0.expect_error().convert_to_syn_error()) + } + _ => Err(self), + } + } +} + +pub(crate) trait FunctionResultExt { + fn into_execution_result(self) -> ExecutionResult; + /// This is not a `From` because it wants to be explicit fn convert_to_final_result(self) -> syn::Result; } -impl ExecutionResultExt for ExecutionResult { +impl FunctionResultExt for FunctionResult { + fn into_execution_result(self) -> ExecutionResult { + self.map_err(|error| error.into()) + } + fn convert_to_final_result(self) -> syn::Result { - self.map_err(|error| error.convert_to_final_error()) + self.map_err(|error| error.0.expect_error().convert_to_syn_error()) + } +} + +impl From for ExecutionInterrupt { + fn from(error: FunctionError) -> Self { + error.0 } } @@ -173,26 +230,10 @@ impl ExecutionInterrupt { }) } - pub(crate) fn syntax_error(error: syn::Error) -> Self { - Self::new_error(ErrorKind::Syntax, error) - } - - pub(crate) fn type_error(error: syn::Error) -> Self { - Self::new_error(ErrorKind::Type, error) - } - pub(crate) fn ownership_error(error: syn::Error) -> Self { Self::new_error(ErrorKind::Ownership, error) } - pub(crate) fn debug_error(error: syn::Error) -> Self { - Self::new_error(ErrorKind::Debug, error) - } - - pub(crate) fn assertion_error(error: syn::Error) -> Self { - Self::new_error(ErrorKind::Assertion, error) - } - pub(crate) fn parse_error(error: ParseError) -> Self { Self::new(ExecutionInterruptInner::Error(ExecutionError( ErrorKind::Parse, @@ -200,28 +241,9 @@ impl ExecutionInterrupt { ))) } - pub(crate) fn value_error(error: syn::Error) -> Self { - Self::new_error(ErrorKind::Value, error) - } - - pub(crate) fn control_flow_error(error: syn::Error) -> Self { - Self::new_error(ErrorKind::ControlFlow, error) - } - pub(crate) fn control_flow(control_flow: ControlFlowInterrupt) -> Self { Self::new(ExecutionInterruptInner::ControlFlowInterrupt(control_flow)) } - - /// Determines if the error can be caught when attempting to map a late-bound - /// mutable value, to retry as a shared value instead. - pub(crate) fn into_caught_mutable_map_attempt_error(self) -> Result { - match self.inner.as_ref() { - ExecutionInterruptInner::Error(ExecutionError(ErrorKind::Value, _)) => { - Ok(self.convert_to_final_error()) - } - _ => Err(self), - } - } } impl From for ExecutionInterrupt { @@ -230,6 +252,12 @@ impl From for ExecutionInterrupt { } } +impl From for FunctionError { + fn from(e: ParseError) -> Self { + FunctionError::new(ExecutionInterrupt::parse_error(e)) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) enum ErrorKind { /// Some error with preinterpret syntax @@ -278,11 +306,27 @@ enum ExecutionInterruptInner { pub(crate) struct ExecutionError(ErrorKind, DetailedError); impl ExecutionError { + pub(crate) fn new(kind: ErrorKind, error: syn::Error) -> Self { + ExecutionError(kind, DetailedError::Standard(error)) + } + pub(crate) fn convert_to_syn_error(self) -> syn::Error { self.1.convert_to_syn_error() } } +impl From for ExecutionInterrupt { + fn from(error: ExecutionError) -> Self { + ExecutionInterrupt::new(ExecutionInterruptInner::Error(error)) + } +} + +impl From for FunctionError { + fn from(error: ExecutionError) -> Self { + FunctionError(ExecutionInterrupt::from(error)) + } +} + pub(crate) enum ControlFlowInterrupt { Break(BreakInterrupt), Continue(ContinueInterrupt), @@ -350,9 +394,7 @@ impl BreakInterrupt { Some(value) => value, None => ().into_any_value(), }; - ownership - .map_from_owned(Spanned(value, span_range)) - .map(|spanned| spanned.0) + Ok(ownership.map_from_owned(Spanned(value, span_range))?.0) } } @@ -374,8 +416,4 @@ impl ExecutionInterrupt { ), } } - - pub(crate) fn convert_to_final_error(self) -> syn::Error { - self.expect_error().convert_to_syn_error() - } } diff --git a/src/misc/field_inputs.rs b/src/misc/field_inputs.rs index 9d19385a..cdd9bb37 100644 --- a/src/misc/field_inputs.rs +++ b/src/misc/field_inputs.rs @@ -66,7 +66,7 @@ macro_rules! define_typed_object { impl IsArgument for $model { type ValueType = ObjectType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - fn from_argument(value: Spanned) -> ExecutionResult { + fn from_argument(value: Spanned) -> FunctionResult { Self::resolve_value( value.expect_owned(), "This argument", @@ -79,7 +79,7 @@ macro_rules! define_typed_object { } impl ResolvableOwned for $model { - fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> ExecutionResult { + fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> FunctionResult { Self::from_object_value(ObjectValue::resolve_spanned_from_value(value, context)?) } } @@ -105,7 +105,7 @@ macro_rules! define_typed_object { } impl $model { - fn from_object_value(Spanned(mut object, span_range): Spanned) -> ExecutionResult { + fn from_object_value(Spanned(mut object, span_range): Spanned) -> FunctionResult { (&object).spanned(span_range).validate(&Self::validation())?; Ok($model { $( diff --git a/src/misc/iterators.rs b/src/misc/iterators.rs index 2925a367..71277a1c 100644 --- a/src/misc/iterators.rs +++ b/src/misc/iterators.rs @@ -1,21 +1,382 @@ use super::*; -impl ClonableIterator for I { - fn clone_box(&self) -> Box> { - Box::new(self.clone()) +// ============================================================================ +// PreinterpretIterator trait — associated Item, no Clone/'static required +// ============================================================================ + +/// An iterator protocol that takes `&mut Interpreter` on each `next()` call. +/// This allows iterators like `Map` and `Filter` to evaluate closures during iteration. +/// +/// Unlike `Iterator`, this has an associated `Item` type and passes the interpreter +/// through on each call. Types that don't need the interpreter (like `StdIteratorAdapter`) +/// simply ignore it. +pub(crate) trait PreinterpretIterator { + type Item; + fn do_next(&mut self, interpreter: &mut Interpreter) -> FunctionResult>; + fn do_size_hint(&self) -> (usize, Option); + + fn do_len(&self, error_span_range: SpanRange) -> FunctionResult { + let (min, max) = self.do_size_hint(); + if max == Some(min) { + Ok(min) + } else { + error_span_range.value_err("Iterator has an inexact length") + } + } + + fn do_map(self, f: F) -> MapIterator + where + Self: Sized, + F: FnMut(Self::Item, &mut Interpreter) -> FunctionResult, + { + MapIterator::new(self, f) + } + + fn do_take(self, count: usize) -> TakeIterator + where + Self: Sized, + { + TakeIterator::new(self, count) + } + + fn do_skip(self, count: usize) -> SkipIterator + where + Self: Sized, + { + SkipIterator::new(self, count) + } + + fn boxed(self) -> Box> + where + Self: Sized + 'static + Clone, + { + Box::new(self) + } + + fn do_into_iter<'i>(self, interpreter: &'i mut Interpreter) -> PreinterpretToIterator<'i, Self> + where + Self: Sized + 'i, + { + PreinterpretToIterator { + inner: self, + errored: false, + interpreter, + } + } + + fn do_collect>( + self, + interpreter: &mut Interpreter, + ) -> FunctionResult + where + Self: Sized, + { + self.do_into_iter(interpreter).collect() + } +} + +impl PreinterpretIterator for I { + type Item = I::Item; + fn do_next(&mut self, _: &mut Interpreter) -> FunctionResult> { + Ok(Iterator::next(self)) + } + fn do_size_hint(&self) -> (usize, Option) { + Iterator::size_hint(self) + } +} + +impl PreinterpretIterator for Box> { + type Item = Item; + + fn do_next(&mut self, interpreter: &mut Interpreter) -> FunctionResult> { + (**self).do_next(interpreter) + } + + fn do_size_hint(&self) -> (usize, Option) { + (**self).do_size_hint() + } +} + +impl PreinterpretIterator for Box> { + type Item = Item; + + fn do_next(&mut self, interpreter: &mut Interpreter) -> FunctionResult> { + (**self).do_next(interpreter) } + + fn do_size_hint(&self) -> (usize, Option) { + (**self).do_size_hint() + } +} + +pub(crate) struct PreinterpretToIterator<'a, I> { + inner: I, + errored: bool, + interpreter: &'a mut Interpreter, +} + +impl Iterator for PreinterpretToIterator<'_, I> { + type Item = FunctionResult; + + fn next(&mut self) -> Option { + if self.errored { + return None; + } + match self.inner.do_next(self.interpreter) { + Ok(Some(item)) => Some(Ok(item)), + Ok(None) => None, + Err(e) => { + self.errored = true; + Some(Err(e)) + } + } + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.do_size_hint() + } +} + +// ============================================================================ +// BoxedIterator — object-safe sub-trait with clone_box, stored in IteratorValue +// ============================================================================ + +/// An object-safe sub-trait with clone_box, stored in IteratorValue. +pub(crate) trait BoxedIterator: 'static + PreinterpretIterator { + fn clone_box(&self) -> Box>; } -pub(crate) trait ClonableIterator: Iterator { - fn clone_box(&self) -> Box>; +impl BoxedIterator for T { + fn clone_box(&self) -> Box> { + Box::new(self.clone()) + } } -impl Clone for Box> { +impl Clone for Box> { fn clone(&self) -> Self { (**self).clone_box() } } +// ============================================================================ +// Generic MapIterator +// ============================================================================ + +/// An iterator that applies a function to each item of the inner iterator. +#[derive(Clone)] +pub(crate) struct MapIterator { + inner: I, + f: F, +} + +impl MapIterator { + pub(crate) fn new(inner: I, f: F) -> Self { + Self { inner, f } + } +} + +impl PreinterpretIterator for MapIterator +where + I: PreinterpretIterator, + F: FnMut(I::Item, &mut Interpreter) -> FunctionResult, +{ + type Item = O; + fn do_next(&mut self, interpreter: &mut Interpreter) -> FunctionResult> { + match self.inner.do_next(interpreter)? { + Some(item) => Ok(Some((self.f)(item, interpreter)?)), + None => Ok(None), + } + } + fn do_size_hint(&self) -> (usize, Option) { + self.inner.do_size_hint() + } +} + +// ============================================================================ +// Generic FilterIterator +// ============================================================================ + +/// An iterator that filters items using a predicate function. +#[derive(Clone)] +pub(crate) struct FilterIterator { + inner: I, + f: F, +} + +impl FilterIterator { + pub(crate) fn new(inner: I, f: F) -> Self { + Self { inner, f } + } +} + +impl PreinterpretIterator for FilterIterator +where + I: PreinterpretIterator, + I::Item: Clone, + F: FnMut(&I::Item, &mut Interpreter) -> FunctionResult, +{ + type Item = I::Item; + fn do_next(&mut self, interpreter: &mut Interpreter) -> FunctionResult> { + loop { + let item = match self.inner.do_next(interpreter)? { + Some(item) => item, + None => return Ok(None), + }; + if (self.f)(&item, interpreter)? { + return Ok(Some(item)); + } + } + } + fn do_size_hint(&self) -> (usize, Option) { + (0, self.inner.do_size_hint().1) + } +} + +// ============================================================================ +// SkipIterator — lazy skip +// ============================================================================ + +/// An iterator that lazily skips the first `remaining` items. +#[derive(Clone)] +pub(crate) struct SkipIterator { + inner: I, + remaining: usize, +} + +impl SkipIterator { + pub(crate) fn new(inner: I, count: usize) -> Self { + Self { + inner, + remaining: count, + } + } +} + +impl PreinterpretIterator for SkipIterator { + type Item = I::Item; + fn do_next(&mut self, interpreter: &mut Interpreter) -> FunctionResult> { + while self.remaining > 0 { + self.remaining -= 1; + if self.inner.do_next(interpreter)?.is_none() { + return Ok(None); + } + } + self.inner.do_next(interpreter) + } + fn do_size_hint(&self) -> (usize, Option) { + let (lo, hi) = self.inner.do_size_hint(); + ( + lo.saturating_sub(self.remaining), + hi.map(|h| h.saturating_sub(self.remaining)), + ) + } +} + +// ============================================================================ +// TakeIterator — lazy take +// ============================================================================ + +/// An iterator that yields at most `remaining` items from the inner iterator. +#[derive(Clone)] +pub(crate) struct TakeIterator { + inner: I, + remaining: usize, +} + +impl TakeIterator { + pub(crate) fn new(inner: I, count: usize) -> Self { + Self { + inner, + remaining: count, + } + } +} + +impl PreinterpretIterator for TakeIterator { + type Item = I::Item; + fn do_next(&mut self, interpreter: &mut Interpreter) -> FunctionResult> { + if self.remaining == 0 { + return Ok(None); + } + self.remaining -= 1; + self.inner.do_next(interpreter) + } + fn do_size_hint(&self) -> (usize, Option) { + let (lo, hi) = self.inner.do_size_hint(); + ( + lo.min(self.remaining), + Some(match hi { + Some(h) => h.min(self.remaining), + None => self.remaining, + }), + ) + } +} + +// ============================================================================ +// Unified to_string for any PreinterpretIterator +// ============================================================================ + +#[allow(clippy::too_many_arguments)] +pub(crate) fn any_items_to_string>( + iterator: &mut impl PreinterpretIterator, + output: &mut String, + behaviour: &ConcatBehaviour, + literal_empty: &str, + literal_start: &str, + literal_end: &str, + possibly_unbounded: bool, + interpreter: &mut Interpreter, +) -> FunctionResult<()> { + let mut is_empty = true; + let max = iterator.do_size_hint().1; + let mut i = 0; + loop { + let item = match iterator.do_next(interpreter)? { + Some(item) => item, + None => break, + }; + if i == 0 { + if behaviour.output_literal_structure { + output.push_str(literal_start); + } + is_empty = false; + } + if possibly_unbounded && i >= behaviour.iterator_limit { + if behaviour.error_after_iterator_limit { + return behaviour.error_span_range.debug_err(format!("To protect against infinite loops, only a maximum of {} items can be output to a string from an iterator. You can use .to_vec() to avoid this limit. This can't currently be reconfigured with the iteration limit.", behaviour.iterator_limit)); + } else { + if behaviour.output_literal_structure { + match max { + Some(max) => output + .push_str(&format!(", ..<{} further items>", max.saturating_sub(i))), + None => output.push_str(", .."), + } + } + break; + } + } + let item = item.borrow(); + if i != 0 && behaviour.output_literal_structure { + output.push(','); + } + if i != 0 && behaviour.add_space_between_token_trees { + output.push(' '); + } + item.as_ref_value() + .concat_recursive_into(output, behaviour, interpreter)?; + i += 1; + } + if behaviour.output_literal_structure { + if is_empty { + output.push_str(literal_empty); + } else { + output.push_str(literal_end); + } + } + Ok(()) +} + #[derive(Clone)] pub(crate) enum EitherIterator { Left(L), @@ -46,12 +407,12 @@ impl ZipIterators { pub(crate) fn new_from_object( object: ObjectValue, span_range: SpanRange, - ) -> ExecutionResult { + ) -> FunctionResult { let entries = object .entries .into_iter() .take(101) - .map(|(k, v)| -> ExecutionResult<_> { + .map(|(k, v)| -> FunctionResult<_> { Ok(( k, v.key_span, @@ -70,11 +431,12 @@ impl ZipIterators { pub(crate) fn new_from_iterator( iterator: IteratorValue, span_range: SpanRange, - ) -> ExecutionResult { - let vec = iterator - .take(101) - .map(|x| x.spanned(span_range).resolve_any_iterator("Each zip input")) - .collect::, _>>()?; + interpreter: &mut Interpreter, + ) -> FunctionResult { + let vec: Vec<_> = iterator + .do_take(101) + .do_map(|x, _| x.spanned(span_range).resolve_any_iterator("Each zip input")) + .do_collect(interpreter)?; if vec.len() == 101 { return span_range.value_err("A maximum of 100 iterators are allowed"); } @@ -85,7 +447,7 @@ impl ZipIterators { self, interpreter: &mut Interpreter, error_on_length_mismatch: bool, - ) -> ExecutionResult { + ) -> FunctionResult { let mut iterators = self; let error_span_range = match &iterators { ZipIterators::Array(_, span_range) => *span_range, @@ -123,8 +485,8 @@ impl ZipIterators { /// Panics if called on an empty list of iterators fn size_hint_range(&self) -> (usize, Option) { let size_hints: Vec<_> = match self { - ZipIterators::Array(inner, _) => inner.iter().map(|x| x.size_hint()).collect(), - ZipIterators::Object(inner, _) => inner.iter().map(|x| x.2.size_hint()).collect(), + ZipIterators::Array(inner, _) => inner.iter().map(|x| x.do_size_hint()).collect(), + ZipIterators::Object(inner, _) => inner.iter().map(|x| x.2.do_size_hint()).collect(), }; let min_min = size_hints.iter().map(|s| s.0).min().unwrap(); let max_max = size_hints @@ -154,7 +516,7 @@ impl ZipIterators { interpreter: &mut Interpreter, error_span_range: SpanRange, output: &mut Vec, - ) -> ExecutionResult<()> { + ) -> FunctionResult<()> { let mut counter = interpreter.start_iteration_counter(&error_span_range); match self { @@ -163,7 +525,7 @@ impl ZipIterators { counter.increment_and_check()?; let mut inner = Vec::with_capacity(iterators.len()); for iter in iterators.iter_mut() { - inner.push(iter.next().unwrap()); + inner.push(iter.do_next(interpreter)?.unwrap()); } output.push(inner.into_any_value()); } @@ -177,7 +539,7 @@ impl ZipIterators { key.clone(), ObjectEntry { key_span: *key_span, - value: iter.next().unwrap(), + value: iter.do_next(interpreter)?.unwrap(), }, ); } @@ -201,12 +563,20 @@ pub(crate) fn run_intersperse( items: Box, separator: AnyValue, settings: IntersperseSettings, -) -> ExecutionResult { + interpreter: &mut Interpreter, +) -> FunctionResult { let mut output = Vec::new(); - let mut items = items.into_iterator()?.peekable(); + // Collect items eagerly since we need lookahead for separator logic. + let mut iterator = items.into_iterator()?; + let mut collected = Vec::new(); + while let Some(item) = iterator.do_next(interpreter)? { + collected.push(item); + } + + let mut collected_iter = collected.into_iter().peekable(); - let mut this_item = match items.next() { + let mut this_item = match collected_iter.next() { Some(next) => next, None => return Ok(ArrayValue { items: output }), }; @@ -219,10 +589,10 @@ pub(crate) fn run_intersperse( loop { output.push(this_item); - let next_item = items.next(); + let next_item = collected_iter.next(); match next_item { Some(next_item) => { - let remaining = if items.peek().is_some() { + let remaining = if collected_iter.peek().is_some() { RemainingItemCount::MoreThanOne } else { RemainingItemCount::ExactlyOne @@ -251,7 +621,7 @@ impl SeparatorAppender { &mut self, remaining: RemainingItemCount, output: &mut Vec, - ) -> ExecutionResult<()> { + ) -> FunctionResult<()> { match self.separator(remaining) { TrailingSeparator::Normal => output.push(self.separator.clone()), TrailingSeparator::Final => match self.final_separator.take() { @@ -308,7 +678,7 @@ pub(crate) fn handle_split( input: OutputStream, separator: &OutputStream, settings: SplitSettings, -) -> ExecutionResult { +) -> FunctionResult { input.parse_with(move |input| { let mut output = Vec::new(); let mut current_item = OutputStream::new(); diff --git a/tests/compilation_failures/core/assert_eq_long_iterator.stderr b/tests/compilation_failures/core/assert_eq_long_iterator.stderr index 16002692..fb66783c 100644 --- a/tests/compilation_failures/core/assert_eq_long_iterator.stderr +++ b/tests/compilation_failures/core/assert_eq_long_iterator.stderr @@ -1,4 +1,4 @@ -error: Assertion failed: lhs != rhs: "iteration limit 1000 exceeded" != "iteration limit 1000 exceeded" +error: Assertion failed: lhs != rhs: "Iterator[?]" != "Iterator[?]" lhs = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ..<1980 further items>] rhs = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ..<1980 further items>] --> tests/compilation_failures/core/assert_eq_long_iterator.rs:4:12 diff --git a/tests/compilation_failures/expressions/large_range_to_string.rs b/tests/compilation_failures/expressions/large_range_to_string.rs deleted file mode 100644 index 37e36f5c..00000000 --- a/tests/compilation_failures/expressions/large_range_to_string.rs +++ /dev/null @@ -1,7 +0,0 @@ -use preinterpret::*; - -fn main() { - let _ = stream!{ - #((0..10000) as iterator as string) - }; -} \ No newline at end of file diff --git a/tests/compilation_failures/expressions/large_range_to_string.stderr b/tests/compilation_failures/expressions/large_range_to_string.stderr deleted file mode 100644 index 1e971e34..00000000 --- a/tests/compilation_failures/expressions/large_range_to_string.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: To protect against infinite loops, only a maximum of 1000 items can be output to a string from an iterator. You can use .to_vec() to avoid this limit. This can't currently be reconfigured with the iteration limit. - --> tests/compilation_failures/expressions/large_range_to_string.rs:5:11 - | -5 | #((0..10000) as iterator as string) - | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/compilation_failures/iteration/long_iterable_to_string.rs b/tests/compilation_failures/iteration/long_iterable_to_string.rs deleted file mode 100644 index 1bb1d921..00000000 --- a/tests/compilation_failures/iteration/long_iterable_to_string.rs +++ /dev/null @@ -1,5 +0,0 @@ -use preinterpret::*; - -fn main() { - run!((0..10000).into_iter().to_string()) -} \ No newline at end of file diff --git a/tests/compilation_failures/iteration/long_iterable_to_string.stderr b/tests/compilation_failures/iteration/long_iterable_to_string.stderr deleted file mode 100644 index ccf86dff..00000000 --- a/tests/compilation_failures/iteration/long_iterable_to_string.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: To protect against infinite loops, only a maximum of 1000 items can be output to a string from an iterator. You can use .to_vec() to avoid this limit. This can't currently be reconfigured with the iteration limit. - --> tests/compilation_failures/iteration/long_iterable_to_string.rs:4:10 - | -4 | run!((0..10000).into_iter().to_string()) - | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/expressions.rs b/tests/expressions.rs index 72a9c09f..effbae4e 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -297,7 +297,7 @@ fn test_range() { 5 ); run! { - %[_].assert_eq('A'.. .into_iter().take(5).to_string(), "ABCDE"); + %[_].assert_eq('A'.. .into_iter().take(5).to_vec().to_string(), "ABCDE"); } } diff --git a/tests/iteration.rs b/tests/iteration.rs index 743c0f52..46c3c6cc 100644 --- a/tests/iteration.rs +++ b/tests/iteration.rs @@ -81,12 +81,21 @@ fn iterator_to_debug_string() { #[test] fn iterator_to_string() { - assert_eq!(run!([1, 2, 3].into_iter().to_string()), "123"); - assert_eq!(run!(%[%group[a b] c d].into_iter().to_string()), "abcd"); - assert_eq!(run!((3..=5).into_iter().to_string()), "345"); - assert_eq!(run!(%{ a: 1 }.into_iter().to_string()), r#"a1"#); - assert_eq!(run!("Hello World".into_iter().to_string()), "Hello World"); - assert_eq!(run!((0..10).into_iter().to_string()), "0123456789"); + // Iterators display as "Iterator[?]" to avoid consuming them + assert_eq!(run!([1, 2, 3].into_iter().to_string()), "Iterator[?]"); + // Use .to_vec().to_string() to get the actual concatenated values + assert_eq!(run!([1, 2, 3].into_iter().to_vec().to_string()), "123"); + assert_eq!( + run!(%[%group[a b] c d].into_iter().to_vec().to_string()), + "abcd" + ); + assert_eq!(run!((3..=5).into_iter().to_vec().to_string()), "345"); + assert_eq!(run!(%{ a: 1 }.into_iter().to_vec().to_string()), r#"a1"#); + assert_eq!( + run!("Hello World".into_iter().to_vec().to_string()), + "Hello World" + ); + assert_eq!(run!((0..10).into_iter().to_vec().to_string()), "0123456789"); } #[test] @@ -109,7 +118,7 @@ fn iterator_next() { fn iterator_skip_and_take() { run! { let iterator = ('A'..).into_iter(); - %[].assert_eq(iterator.skip(1).take(4).to_string(), "BCDE"); + %[].assert_eq(iterator.skip(1).take(4).to_vec().to_string(), "BCDE"); } }