diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca5bc723..7740c6a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,6 +72,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: stable + - run: ./bench_compilation.sh - run: ./bench.sh book: diff --git a/bench.sh b/bench.sh index 821f391a..59c81928 100755 --- a/bench.sh +++ b/bench.sh @@ -12,8 +12,23 @@ cd "$(dirname "$0")" # - Overhead invoking the macro # - The measured Parsing + Evaluation + Output time in the benchmark # - So the benchmark is useful, but should be considered alongside the compile time -# of preinterpret itself... +# of preinterpret itself - see `bench_compilation.sh` for that. -cargo bench --features benchmark --profile=dev; +echo Preparing benchmark builds... +cargo build --bench basic --features benchmark --profile=dev +cargo build --bench basic --features benchmark --profile=release -cargo bench --features benchmark; \ No newline at end of file +# Note - the benchmarks themselves actually run *during* build time +# So at this point (courtesy of the build cache) we already have the benchmark results. +# But we print them in a block to make it easier to copy-paste them + +echo +echo "Executing pre-run benchmark (dev profile)..." +cargo bench --bench basic --features benchmark --profile=dev + +echo +echo "Executing pre-run benchmark (release profile)..." +cargo bench --bench basic --features benchmark --profile=release + +echo +echo "If you want to get fresh results, run 'cargo clean' and re-run this script" \ No newline at end of file diff --git a/bench_compilation.sh b/bench_compilation.sh new file mode 100755 index 00000000..bdef9cd4 --- /dev/null +++ b/bench_compilation.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +set -e + +cd "$(dirname "$0")" + +# Usage: run_build_with_timings +run_build_with_timings() { + local profile="$1" + + echo + echo ">> Checking compilation timings ($profile profile)" + echo + cargo clean + + cargo build --lib --profile="$profile" --timings > /dev/null + + # Find the most recent timing file (cargo creates timestamped files) + local timing_file + timing_file=$(ls -t target/cargo-timings/cargo-timing-*.html 2>/dev/null | head -1) || true + + if [ -n "$timing_file" ] && [ -f "$timing_file" ]; then + echo + + # Extract total time from the HTML summary table + local total_time + total_time=$(grep -o 'Total time:[^<]*' "$timing_file" | grep -o '[0-9.]*s' | head -1) || true + echo "- Total lib build time | $total_time" + + # Extract preinterpret duration from UNIT_DATA JSON + # Use sed to extract just the duration value after finding the preinterpret entry + local preinterpret_duration + preinterpret_duration=$(sed -n '/"name": "preinterpret"/,/}/p' "$timing_file" | grep -o '"duration": [0-9.]*' | grep -o '[0-9.]*') || true + if [ -n "$preinterpret_duration" ]; then + echo "- preinterpret compile time | ${preinterpret_duration}s" + fi + + # Extract syn duration from UNIT_DATA JSON + local syn_duration + syn_duration=$(sed -n '/"name": "syn"/,/}/p' "$timing_file" | grep -o '"duration": [0-9.]*' | grep -o '[0-9.]*') || true + if [ -n "$syn_duration" ]; then + echo "- syn compile time | ${syn_duration}s" + fi + fi +} + +run_build_with_timings "dev" +run_build_with_timings "release" diff --git a/benches/basic.rs b/benches/basic.rs index eda5a1ef..2f83c358 100644 --- a/benches/basic.rs +++ b/benches/basic.rs @@ -26,7 +26,7 @@ fn main() { output }); benchmark!("Lots of casts", { - 0 as u32 as int as u8 as char as string as stream + 0 as u32 as untyped_int as u8 as char as string as stream }); benchmark!("Simple tuple impls", { for N in 0..=10 { diff --git a/plans/2026-01-types-and-forms.md b/plans/2026-01-types-and-forms.md new file mode 100644 index 00000000..f98009de --- /dev/null +++ b/plans/2026-01-types-and-forms.md @@ -0,0 +1,202 @@ +# Reflection on types and forms - January 2026 + +In December 2025 I embarked on a longer than expected journey, to "migrate forms to leaves": + +```rust +Shared(AnyValue::X(X::Y(y))) => AnyValue::X(X::Y(Shared(y))) +``` + +This was motivated by a few thoughts: +* It would be nice for e.g. a "Shared int" to be convertible to/from a "Shared any value" +* It's common knowledge that an `Option(&x)` is more useful than an `&Option(x)` +* The code around defining types and forms was ugly and repetitive and could do with a tidy up +* I wanted to start on methods/functions, for e.g. a `.map(|x| x * x)` but something in my + early investigation of function variable closures, I realized that this leaf migration would + be needed... both in an abstract sense, but also when I started investigating a "referenceable" + abstraction to be shared between variables and closures. + +### Original justification in the context of methods + +- [ ] Improved Shared/Mutable handling - See the `Better handling of value sub-references` section. The key requirement is we need to allow a `Shared` to map back to a `Shared`. Moving the "sharedness" to the leaves permits this. + * A function specifies the bindings of its variables + * If we have `my_len = |x: &array| x.len()` and invoke it as `my_len(a.b)` then + when I invoke it, I need to end up with the variable `x := &a.b` + * This is a problem - if we imagine changing what can be stored in a variable to + the following, then it's clear that we need some way to have a `SharedValue` which + has an outer-enum instead of an inner-enum. + * We also need to think about how things like `IterableValue` works. Perhaps it's like an interface, and so defined via `Box` / `Ref` etc? + +I don't fully understand what the problem I saw was now. I think that if `my_len` takes a `x: Shared` then to call `x.len()` I need to convert it back to a `Shared` in order to resolve `x.len()`? Maybe? I'm not too sure. + +## The problem + +The problem is that this works fine for covariant things like shared references... but not at all for things like assignee / mutable. In particular, consider: + +```rust + fn swap(mut a: AnyValueAssignee, mut b: AnyValueAssignee) -> () { + core::mem::swap(a.0.deref_mut(), b.0.deref_mut()); + } +``` + +This requires the thing that can be replaced to be the whole `AnyValue`, not just some leaf content. + +In a way, the "everything in the leaf" paradigm corresponds to walking around with `let x: &leaf_type`, restricting the place to only support that particular leaf type. + +## The reflection - the type of a place & type annotations + +So far places have only ever had one type - `any`. Whilst values have leaf types, the places themselves can be swapped to take `AnyValue`. This is pretty much how most dynamically typed languages work. + +If we wanted to support type annotations, they _could_ work like this, by constraining the type the place supports at a structural level... + +Or we could save the trouble and always store an `AnyValue` structurally but insert a runtime type assertion. This is a tiny bit less performant at runtime, but keeps the code simpler AND less code = less compile time, which is more important than faster runtime on smaller codebases that only use preinterpet a bit. + +### What would a hypothetical structurally type-restricted binding look like? + +Sidenote: I don't think we should necessarily support this, for reasons mentioned above. But it would allow us to support something like slices, e.g. `Mutable<[ArrayValue]>` + +Consider a structural `x: &mut integer`. Then, in the type system a value would look something like this: + +```rust +AnyValueContent::Integer(Mutable(IntegerValueContent::U8(u8))) +``` + +Essentially what we have is a form where the form wrapper isn't restricted to being at the leaf; rather it can be at any level of the type! + +But - how would this be modelled? i.e. how would we model a value which could be structurally type-restricted to be any type + +Well it would probably want to look something like: Each parent type having a `Content` enum and `Wrapper` enum, which looks the same, except it has an `AsSelf` variant option as well, i.e.: + +```rust +// &mut integer +AnyValueWrapper::Integer(IntegerValueWrapper::AsSelf(Mutable(IntegerValueContent::U8(u8)))) +// &mut u8 +AnyValueWrapper::Integer(IntegerValueWrapper::U8(Mutable(u8))) +// &mut any +AnyValueWrapper::AsSelf(Mutable(AnyValueContent::Integer(IntegerValueContent::U8(u8)))) +``` + +Essentially, outside of the `Form` we have `Wrapper` and inside the form we have `Mutable`. + +But then - how do we actually operate on this?? What operations for we supporty? + +Honestly, it's really hard. Would need to think about what operations can be performed etc. Even defining a leaf mapper was nigh-on impossible. + +Might come back to this at some point. + +## What's next? + +Let's put the "concepts" to-leaf on indefinite pause. It's definitely resulted in a lot of good cleaning up, BUT it's also created a lot of mess. + +Eventually we will need to clean it up: +* Better abstract the `IsArgument` impl +* Finish migrating from `bindings.rs` +* Finish removing `into_any_value()` etc +* Finish clearing up `arguments.rs` and `outputs.rs` + +We might well be left with only `BeOwned` and `BeRef`, `BeMut` - we'll see. + +See the below half-baked todo list for where this got to. + +--- + +But *first* I'd like to implement functions _without_ type annotations. Then if we add them, it would probably be easier for them to be runtime assertions rather than structural. + +## Half-finished to-do lists copied over (TODO: clean these up!) + +### Better handling of value sub-references + +- [x] Migrate from `Owned` having a span to a `Spanned` +- [ ] Implement and roll-out GATs + - [x] Initial shell implementation in `concepts` folder + - [x] Improved error handling in the macro (e.g. required arg after optional; no matching strongly-typed signature) + - [x] Separate Hierarchical and DynCompatible Forms + - [x] Improved macro support + - [x] Add source type name to type macro/s + - [x] Generate value kinds from the macros + - [x] Add (temporary) ability to link to TypeData and resolve methods from there + - [x] Then implement all the macros + - [x] And use that to generate AnyValueLeafKind from the new macros + - [x] Find a way to generate `from_source_name` - ideally efficiently + - [x] Add ability to implement IsIterable + - [x] `CastTarget` simply wraps `TypeKind` + - [x] Create a `Ref` and a `Mut` form + - [x] They won't implement `IsArgumentForm` + - [x] Create mappers and suitably generic `as_ref()` and `as_mut()` methods on `Actual` + - [x] Migrate method resolution to the trait macro properly + - [x] And property resolution `dyn TypeFeatureResolver` + - [x] Replace `impl TypeFeatureResolver for $type_def` + - [x] Remove `temp_type_data: $type_data:ident,` + - [x] Remove `HierarchicalTypeData` + - [ ] Stage 1 of the form migration: + - [x] Add temp blanket impl from `IntoValue` for `IntoValueContent` + - [x] Get rid of `BooleanValue` wrapper + - [x] Get rid of `StreamValue` wrapper + - [x] Get rid of `CharValue` wrapper + - [x] Replace `IntegerValue` with `type IntegerValue = IntegerContent<'static, BeOwned>` + - [x] Replace `FloatValue` with `type FloatValue = FloatContent<'static, BeOwned>` + - [x] Replace `Value` with `type Value = ValueContent<'static, BeOwned>` + - [x] Get rid of the `Actual` wrapper inside content + // --- + - [x] Trial getting rid of `Actual` completely? + - [x] Replace `type FloatValue = FloatValueContent` with `type FloatValue = QqqOwned` / `type FloatValueRef<'a> = QqqRef` / `type FloatValueMut = QqqMut` + - [x] Create new branch + - [x] Resolve issue with `2.3` not resolving into `2f32` any more + - [x] .. same for int... + - [x] Update Value: + - [x] `ValueType` => `AnyType` + - [x] `type AnyValue = QqqOwned` + - [x] `type AnyValueRef = QqqRef` + - [x] `type AnyValueMut = QqqMut` + - [x] ... and move methods + - [x] Remove `OwnedValue` + - [x] Get rid of `Owned` + - [ ] Stage 2 of the form migration: + - [x] Attempt to improve mappers: + - [x] Try to replace `ToRefMapper` etc with a `FormMapper::::map_content(content, |x| -> y)` - 6 methods... `map_content`, `map_content_ref`, `map_content_mut` and `try_x` *3; ... and a `ReduceMapper::::map(content, |x| -> y)`... sadly not possible! The lambda needs to be higher-ordered and work for all `L: IsValueLeaf`. + - [x] Finish mapper improvements + - [x] Migrate `self` + - [x] Consider if we even need `MapperOutput` or just some helper functions + - [x] Delay resolving the type kind into the error case when downcasting + - [x] Create macro to define inline mappers in various forms + - [x] Attempt to see if I can get rid of needing `leaf_to_content` and maybe `content_to_leaf` by exploiting a trick to bind associated types via a sub-trait (e.g. as I did with `IsValueContent::LeafType> + IsLeafValueContent`) + - [x] Pivot the argument resolution to work with either old or new values + - [ ] Remove as much from arguments.rs as possible + - [ ] Fix `todo!("Argument")` + - [ ] Pivot the return resolution to work with either old or new values + - [ ] Remove `ResolveAs` + - [ ] Look at better implementations of `FromArgument` + .. potentially via some new selector trait `IsResolvable` with a resolution strategy of `Hierarchichal` | `Dyn` | `Custom` + This will let us implement a more general resolution logic, and amalgamate argument parsing and downcast_resolve/dyn_resolve + - [ ] Reproduce `CopyOnWrite` + - [ ] Implement `TODO[concepts]: COPY ON WRITE MAPPING` + - [ ] `BeCopyOnWrite::owned()` / `shared_as_..` can go via `AnyLevelCopyOnWrite => into_copy_on_write` + - [ ] Reproduce `Shared` methods etc + - [ ] Reproduce `Mutable` methods etc + - [ ] Reproduce `Assignee` methods etc + - [ ] Change `CopyOnWrite` => `CopyOnWriteAnyValue` similarly with `Shared`, `Mutable` and `Assignee` + - [ ] Migrate `CopyOnWrite`, `Shared`, `Mutable`, `Assignee` + - [ ] `Shared` only works for leaves + - [ ] For specific parents, use e.g. `AnyValueShared` + - [ ] If you need something to apply across all leaves, implement it on some trait + `IsSelfSharedContent` depending/auto-implemented on `IsSelfValueContent
` + - [ ] Same for `Mutable` and `Assignee` + - [ ] Stage 3 + - [ ] Migrate `CopyOnWrite` and relevant interconversions + - [ ] Finish migrating to new Argument/Returned resolution and delete old code + - [ ] Remove `impl_resolvable_argument_for` and `TODO[concepts]: Remove when we get rid of impl_resolvable_argument_for` + - [ ] Complete ownership definitions and inter-conversions, including maybe-erroring inter-conversions (possibly with `Result` which we can map out of): + - [ ] Consider migrating LateBound and interconversions + - [ ] Argument, and `ArgumentOwnership` driven conversions into it + - [ ] Generate test over all value kinds which checks for: + - [ ] Maybe over TypeKinds with https://docs.rs/inventory/latest/inventory/ registered as a dev dependency + - [ ] Check for duplicate TypeKind registrations + - [ ] Source type has no spaces and is lower case, and is invertible + - [ ] Ancestor types agree with type kinds + - [ ] Clear up all `TODO[concepts]` + - [ ] Have variables store a `Referenceable` + - [ ] Separate methods and functions + - [ ] Add ability to add comments to types, methods/functions and operations, and generate docs from them + - [ ] Clean-up: + - [ ] Move indexing and property access (see e.g.`into_indexed`) etc to the type resolution system - this map allow us to remove + things like `AnyLevelCopyOnWrite` and the `TypeMapper` + - [ ] Most matching methods on `AnyValue` would be better as leaf methods \ No newline at end of file diff --git a/plans/TODO.md b/plans/TODO.md index 6144cf83..89162502 100644 --- a/plans/TODO.md +++ b/plans/TODO.md @@ -198,40 +198,29 @@ First, read the @./2025-11-vision.md - [x] `%literal[ .. ]` - [x] Update README.md for these +## Better handling of value sub-references + +Moved to [2026-01-types-and-forms.md](./2026-01-types-and-forms.md). + ## Methods and closures -- [ ] Improved Shared/Mutable handling - See the `Better handling of value sub-references` section - * A function specifies the bindings of its variables - * If we have `my_len = |x: &array| x.len()` and invoke it as `my_len(a.b)` then - when I invoke it, I need to end up with the variable `x := &a.b` - * This is a problem - if we imagine changing what can be stored in a variable to - the following, then it's clear that we need some way to have a `SharedValue` which - has an outer-enum instead of an inner-enum. - * We also need to think about how things like `IterableValue` works. Perhaps it's like an interface, - and so defined via `Box` / `Ref` etc? +- [ ] Consider pre-requisite work on [2026-01-types-and-forms.md](./2026-01-types-and-forms.md) for type annotations. Instead, let's move forward without support for specific types for now. To start, let's just support: `x` or `x: any`; `x: &any` and `x: &mut any`. +- [ ] Change bindings (currently just variables) to be able to store any of the following: (nb we still restrict variables to be owned for now). ```rust -// Before enum VariableContent { - Owned(Rc>), - Shared(SharedSubRcRefCell), - Mutable(MutSubRcRefCell), -} -// After -enum VariableContent { - Owned(ValueReferencable), - Shared(ValueRef<'static>), // 'static => only SharedSubRcRefCell, no actual refs - Mutable(ValueMut<'static>), // 'static => only MutSubRcRefCell, no refs + Owned(Referenceable), + Shared(Shared), + Mutable(Mutable), } ``` - [ ] Introduce basic function values * Value type function `let my_func = |x, y, z| { ... };` - * Parameters can be `x` (Owned), `&x` (Shared) or `&mut x` (Mutable), shorthand for - e.g. `x: &value` + * Parameters can be `x` / `x: any` (Owned), `x: &any` (Shared) or `x: &mut any` (Mutable). * To start with, they are not closures (i.e. they can't capture any outer variables) - - [ ] Break/continue label resolution in functions/closures - * Functions and closures must resolve break/continue labels statically - * Break and continue statements should not leak out of function boundaries - * This needs to be validated during the control flow pass +- [ ] Break/continue label resolution in functions/closures + * Functions and closures must resolve break/continue labels statically + * Break and continue statements should not leak out of function boundaries + * This needs to be validated during the control flow pass - [ ] New node extension in the expression parser: invocation `(...)` - [ ] Closures * A function may capture variable bindings from the parent scope, these are converted into a `VariableBinding::Closure()` @@ -447,6 +436,9 @@ preinterpret::run! { - [ ] References store on them cached information - either up-front, via an `Rc>` or via a "resolve on first execute" - Value's relative offset from the top of the stack - An is last use flag +- [ ] Change the storage model for `OutputStream` + - [ ] Either just use `TokenStream` directly(!) (...and ignore Rust analyzer's poor handling of none groups) + - [ ] Or use an `Rc` model like https://github.com/dtolnay/proc-macro2/pull/341/files and Rust itself - Address `TODO[performance]` ## Deferred @@ -493,7 +485,7 @@ Also: - [x] Merge `assignee_frames` into `value_frames` as per comment as the top of `assignee_frames` - [x] Rename `EvaluationItem` to `RequestedValue` and consider making `RequestedValue::AssignmentCompletion` wrap an `Owned<()>` so that it becomes truly a value. -- [x] Merge `HasValueType` with `ValueKind` +- [x] Merge `HasValueType` with `ValueLeafKind` - [ ] Add `preinterpret::macro` - can this be a declarative macro? Would be slightly more efficient, as it just needs to wrap a call to `preinterpret::stream` or `preinterpret::run`... - [ ] When we create `input = %raw[..]` we will need to set its `end_of_stream` span to the end of the macro_rules! macro somehow... I'm not sure how to get that span though. @@ -507,7 +499,7 @@ Also: - [ ] Better handling of `configure_preinterpret`: * Move `None.configure_preinterpret` to `preinterpret::set_iteration_limit(..)` - [ ] CastTarget revision: - * The `as int` operator is not supported for string values + * The `as untyped_int` operator is not supported for string values * The `as char` operator is not supported for untyped integer values * Add casts of any integer to char, via `char::from_u32(u32::try_from(x))` * Should we remove/replace any CastTargets? @@ -515,98 +507,6 @@ Also: - [ ] Check all `#[allow(unused)]` and remove any which aren't needed We can use `_xyz: Unused` in some places to reduce the size of types. -## Better handling of value sub-references - -### OPTION 1 - Enums with GATs - -> [!NOTE] -> See `sandbox/gat_value.rs` for playing around with this idea - -Returning/passing refs of sub-values requires taking the enum outside of the reference, -i.e. some `ValueRef<'a>`, perhaps similar to `IterableRef`? - -```rust -enum ValueRef<'a> { - Integer(IntegerRef<'a>), - Object(AnyRef<'a, ObjectValue>), - // ... -} -``` - -We could even consider abusing GATs further, to define the structures only once: -```rust -trait OwnershipSelector { - type Leaf; -} -struct IsOwned; -impl OwnershipSelector for IsOwned { - type Leaf = T; -} -// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. -struct IsReferencable; -impl OwnershipSelector for IsReferencable { - type Leaf = Rc>; -} -struct IsRef<'a>; -impl<'a> OwnershipSelector for IsRef<'a> { - type Leaf = AnyRef<'a, T>; -} -struct IsMut<'a>; -impl<'a> OwnershipSelector for IsMut<'a> { - type Leaf = AnyMutRef<'a, T>; -} - -enum ValueWhich { - Integer(IntegerStructure), - Object(H::Leaf::), -// ... -} - -type Value = ValueWhich; -type ValueReferencable = ValueWhich; -type ValueRef<'a> = ValueWhich>; -type ValueMut<'a> = ValueWhich>; -``` - -- [ ] Trial if `Shared` can actually store an `ValueRef<'a>` (which just stores `&'a`, not the `Ref` variable)... - * This could be done by adding GATs (raising MSRV to 1.65) so that TypeData can have a `Ref<'T>`, - with `Value::Ref<'T> = ValueRef<'T>`... although we only really need GATs for allowing arbitrary - references, not just static `SharedSubRcRefCell` from Shared - * And then `Shared<'t, T>` can wrap a `::Type::Ref<'t, T>` (in the file, this can be encapsulated as a `HasRefType` trait, which can be blanket implemeted for types implementing `..Target`). - * This would mean e.g. `Shared` could wrap a `&str`. - * 6 months later I'm not sure what this means: - * And then have a `AdvancedCellRef` store a `::Type::Ref<'T>` which can be owned and we can manually call increase strong count etc on the `RefCell`. - * To implement `AdvancedCellRef::map`, we'll need `TypeData::Ref<'T>` to implement Target in a self-fulfilling way. (i.e. `HasRefType { type Ref<'a>: HasRefParent }`, `HasRefParent { type Parent: HasRefType })`) - * If this works, we can replace our `Ref` with `T: HasRefType` - * Migrate `IterableRef` - -### OPTION 2 - Box + Dyn - -> [!NOTE] -> See `sandbox/dyn_value.rs` for playing around with this idea - -If we can make this work, it's perhaps slightly less performant (I wonder how much?) but would probably compile faster, and be less tied to structure; so support. - -See below for some rough ideas. - -For owned values: -* `Box` with `IsValue: Any` (maybe using https://docs.rs/downcast-rs/latest/downcast_rs/ to avoid `Any`) -* From that, `IsValue` allows resolving `&'static TypeData` -* Which can expose methods such as `as_integer(Box) -> Option>` - * Which can downcast `Box` to specific value, e.g. `Box` - * Then can upcast that to a specific trait such as `Box` or `Box` - -For reference values: -* `AnyRef` -* `TypeData` can expose methods such as `as_integer_ref(AnyRef) -> Option>` - .. using `downcast_ref` and then upcasting... - ... I wonder if this can be automatic. `if Self::Value : IsInteger` then we implement with a cast, if not? - -For mutable values: -* `AnyRefMut` -* `TypeData` can expose methods such as `as_integer_mut(AnyRefMut) -> Option>` - .. using `downcast_mut` and then upcasting. - ## Cloning * Consider making Iterator non-clonable (which will unlock many more easy lazy implementations, of e.g. `take` using the non-clonable `Take`, and similar for other mapped iterators), i.e. `ExpressionValue` has a manual `clone() -> ExecutionResult` - this will simplify some things. But then, `to_string()` would want to take a `CopyOnWrite` so that where we clone, the iterator can potentially take owned, attempt clone, else error. @@ -688,23 +588,23 @@ This means that this is low-clone: ## Value expansions [OPTIONAL] -Consider: -* Do we want some kind of slice object? (see `TODO[range-refactor]`) - * We can make `ExpressionValue` deref into `ExpressionRef`, e.g. `ExpressionRef::Array()` - * Then we can make `SharedValue(Ref)`, which can be constructed from a `Ref` with a map! - * And similarly `MutableValue(RefMut)` -* Using ArgumentValue in place of ExpressionValue e.g. inside arrays / objects, so that we can destructure `let (x, y) = (a, b)` without clone/take - * But then we end up with nested references which can be confusing! - * CONCLUSION: Maybe we don't want this - to destructure it needs to be owned anyway? +* Consider somehow adding some kind of slice object? (see `TODO[slice-supprt]`) + * To do this, we basically need to have a leaf-kind of `Mutable`, so we can map an + `arr[0..2]` to a `Mutable<[AnyValue]>` - but this then can't be converted back to + a `Mutable`. This is then a binding structurally of type `&mut slice`. + * Supporting structurally type-restricted bindings is ... complicated ... + And whilst the machinery for leaf-restricted bindings is in place, their exposure and + inter-op with any-bindings is very much not (as of Jan 26). + * See [2026-01-types-and-forms.md](./2026-01-types-and-forms.md) for more details. * Consider whether to expand to storing `ArgumentValue` or `CopyOnWriteValue` in variables instead of `OwnedValue`? - => The main issue is if it interferes with taking mutable references, but it's possibly OK, would need to see if it's a confusing problem in practice... (e.g. `let b = a[0]; a.push(1)` if `b` is a reference to `a[0]` then this is a problem when we push to `a`) - => If a mutable reference is created and there are pending references, the variable data RefCell could be replaced with a cloned value and then mutated... But this can be more expensive, because e.g. `let b = a[0]; a.push(1)` results in the whole array `a` being copied in the `CoW` case; but only the `a[0]` being cloned in the "clone on assign" case. - => Maybe we just stick to assignments being Owned/Cloned as currently + - The main issue is if it interferes with taking mutable references, but it's possibly OK, would need to see if it's a confusing problem in practice... (e.g. `let b = a[0]; a.push(1)` if `b` is a reference to `a[0]` then this is a problem when we push to `a`) + - If a mutable reference is created and there are pending references, the variable data RefCell could be replaced with a cloned value and then mutated... But this can be more expensive, because e.g. `let b = a[0]; a.push(1)` results in the whole array `a` being copied in the `CoW` case; but only the `a[0]` being cloned in the "clone on assign" case. + - Maybe we just stick to assignments being Owned/Cloned as currently * Support `#(x[..])` syntax for indexing streams, like with arrays - * `#(x[0])` returns the value at that position of the stream (using `INFER_TOKEN_TREE`) - * `#(x[0..3])` returns a TokenStream - * `#(x[0..=3])` returns a TokenStream + * `#(x[0])` returns the value at that position of the stream (using `INFER_TOKEN_TREE`) + * `#(x[0..3])` returns a TokenStream + * `#(x[0..=3])` returns a TokenStream -------------------------------------------------------------------------------- diff --git a/src/expressions/concepts/content.rs b/src/expressions/concepts/content.rs new file mode 100644 index 00000000..d33e97c7 --- /dev/null +++ b/src/expressions/concepts/content.rs @@ -0,0 +1,247 @@ +use super::*; + +/// Shorthand for representing the form F of a type T with a particular lifetime 'a. +pub(crate) type Content<'a, T, F> = ::Content<'a, F>; +pub(crate) type DynContent<'a, D, F> = + ::DynLeaf<'a, ::DynContent>; + +/// For types which have an associated value (type and form) +pub(crate) trait IsValueContent { + type Type: IsHierarchicalType; + type Form: IsHierarchicalForm; +} + +pub(crate) trait FromValueContent<'a>: IsValueContent { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self; +} + +pub(crate) trait FromSpannedValueContent<'a>: IsValueContent { + fn from_spanned_content(content: Spanned>) -> Self; +} + +impl<'a, C: FromValueContent<'a>> FromSpannedValueContent<'a> for C { + fn from_spanned_content(content: Spanned>) -> Self { + let Spanned(inner, _span_range) = content; + C::from_content(inner) + } +} + +impl IsValueContent for Spanned { + type Type = C::Type; + type Form = C::Form; +} + +impl<'a, C: FromValueContent<'a>> FromSpannedValueContent<'a> for Spanned { + fn from_spanned_content( + Spanned(content, span_range): Spanned>, + ) -> Self { + Spanned(C::from_content(content), span_range) + } +} + +pub(crate) trait IntoValueContent<'a>: IsValueContent +where + Self: Sized, +{ + fn into_content(self) -> Content<'a, Self::Type, Self::Form>; + + #[inline] + fn upcast(self) -> Content<'a, S, Self::Form> + where + Self::Type: UpcastTo, + { + ::upcast_to::(self.into_content()) + } + + #[inline] + #[allow(clippy::type_complexity)] // It's actually pretty readable + fn downcast(self) -> Result, Content<'a, Self::Type, Self::Form>> + where + U: DowncastFrom, + { + U::downcast_from::(self.into_content()) + } + + #[inline] + fn into_any(self) -> Content<'a, AnyType, Self::Form> + where + Self::Type: UpcastTo, + { + self.upcast() + } + + fn into_referenceable(self) -> Content<'a, Self::Type, BeReferenceable> + where + Self: IsValueContent, + { + map_via_leaf! { + input: (Content<'a, Self::Type, Self::Form>) = self.into_content(), + fn map_leaf(leaf) -> (Content<'a, T, BeReferenceable>) { + Rc::new(RefCell::new(leaf)) + } + } + } +} + +impl<'a, C> Spanned +where + C: IntoValueContent<'a>, + C::Type: IsHierarchicalType, + C::Form: IsHierarchicalForm, +{ + pub(crate) fn downcast_resolve>( + self, + resolution_target: &str, + ) -> ExecutionResult + where + ::Type: DowncastFrom, + { + let Spanned(value, span_range) = self; + let content = value.into_content(); + let resolved = <::Type>::resolve::( + content, + span_range, + resolution_target, + )?; + Ok(X::from_spanned_content(Spanned(resolved, span_range))) + } + + // TODO[concepts]: Change to use a FromSpannedDynContent trait, + // so that it can return a ExecutionResult and avoid needing to specify D. + pub(crate) fn dyn_resolve( + self, + resolution_target: &str, + ) -> ExecutionResult> + where + ::Type: DynResolveFrom, + C::Form: IsDynCompatibleForm, + { + let Spanned(value, span_range) = self; + let content = value.into_content(); + let resolved = + <::Type>::resolve::(content, span_range, resolution_target)?; + Ok(resolved) + } +} + +// TODO[concepts]: Remove eventually, along with IntoValue impl +impl> IntoAnyValue for X +where + X::Type: UpcastTo, +{ + fn into_any_value(self) -> AnyValue { + self.into_any() + } +} + +/// Implementations on the value contents *themselves*. +/// Typically this is reserved for things operating on references - otherwise we can +/// implement on IntoValueContent / FromValueContent. +pub(crate) trait IsSelfValueContent<'a>: IsValueContent +where + Self::Type: IsHierarchicalType = Self>, + Self::Form: IsHierarchicalForm, +{ + fn as_mut_value<'r>(&'r mut self) -> Content<'r, Self::Type, BeMut> + where + 'a: 'r, + Self::Form: LeafAsMutForm, + { + map_via_leaf! { + input: &'r mut (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (Content<'r, T, BeMut>) { + F::leaf_as_mut(leaf) + } + } + } + + fn as_ref_value<'r>(&'r self) -> Content<'r, Self::Type, BeRef> + where + 'a: 'r, + Self::Form: LeafAsRefForm, + { + map_via_leaf! { + input: &'r (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (Content<'r, T, BeRef>) { + F::leaf_as_ref(leaf) + } + } + } + + /// This method should only be used when you are certain that the value should be cloned. + /// In most situations, you may wish to use [IsSelfValueContent::clone_to_owned_transparently] + /// instead. + fn clone_to_owned_infallible<'r>(&'r self) -> Content<'static, Self::Type, BeOwned> + where + 'a: 'r, + Self::Form: LeafAsRefForm, + Self: Sized, + { + map_via_leaf! { + input: &'r (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (Content<'static, T, BeOwned>) { + F::leaf_clone_to_owned_infallible(leaf) + } + } + } + + /// A transparent clone is allowed for some types when doing method resolution. + /// * For these types, a &a can be transparently cloned into an owned a. + /// * For other types, an error is raised suggesting to use .clone() explicitly. + /// + /// See [TypeKind::supports_transparent_cloning] for more details. + fn clone_to_owned_transparently<'r>( + &'r self, + span_range: SpanRange, + ) -> ExecutionResult> + where + 'a: 'r, + Self::Form: LeafAsRefForm, + Self: Sized, + { + map_via_leaf! { + input: &'r (Content<'a, Self::Type, Self::Form>) = self, + state: SpanRange | let span_range = span_range, + fn map_leaf(leaf) -> (ExecutionResult>) { + F::leaf_clone_to_owned_transparently(leaf, span_range) + } + } + } +} + +impl<'a, X> IsSelfValueContent<'a> for X +where + X: IsValueContent, + X::Type: IsHierarchicalType = X>, + X::Form: IsHierarchicalForm, +{ +} + +impl< + X: FromValueContent<'static, Type = T, Form = F>, + F: MapFromArgument, + T: DowncastFrom, + > IsArgument for X +{ + type ValueType = T; + const OWNERSHIP: ArgumentOwnership = F::ARGUMENT_OWNERSHIP; + fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + 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)) + } +} + +impl< + X: IntoValueContent<'static, Type = T, Form = BeOwned>, + // TODO[concepts]: Migrate to BeOwned => F when it doesn't break MSRV + // due to clashes with `IntoValueContent` on Shared / Mutable + // F: MapIntoReturned, + T: UpcastTo, + > IsReturnable for X +{ + fn to_returned_value(self) -> ExecutionResult { + 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 new file mode 100644 index 00000000..f4b8e4fb --- /dev/null +++ b/src/expressions/concepts/form.rs @@ -0,0 +1,77 @@ +use super::*; + +/// A type representing a particular form of a value's ownership. +/// +/// Examples include: +/// - [BeOwned] representing [Owned] (floating) value +/// - [BeShared] representing [Shared] references +/// - [BeMutable] representing [Mutable] references +/// - [BeCopyOnWrite] representing [CopyOnWrite] values +/// +/// NB: The Sized + Clone bounds are just to make certain derive impls easier, +/// due to e.g. the poor auto-derive of Clone which requires Clone bounds on all +/// generics. +pub(crate) trait IsForm: Sized + Clone {} + +pub(crate) trait IsHierarchicalForm: IsForm { + /// The standard leaf for a hierachical type + type Leaf<'a, T: IsLeafType>: IsValueContent + + IntoValueContent<'a> + + FromValueContent<'a>; +} + +pub(crate) trait LeafAsRefForm: IsHierarchicalForm { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf; + + fn leaf_clone_to_owned_infallible<'r, 'a: 'r, T: IsLeafType>( + leaf: &'r Self::Leaf<'a, T>, + ) -> T::Leaf { + Self::leaf_as_ref(leaf).clone() + } + + fn leaf_clone_to_owned_transparently<'r, 'a: 'r, T: IsLeafType>( + leaf: &'r Self::Leaf<'a, T>, + error_span: SpanRange, + ) -> ExecutionResult { + let type_kind = T::type_kind(); + if type_kind.supports_transparent_cloning() { + Ok(Self::leaf_clone_to_owned_infallible(leaf)) + } else { + error_span.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.", + type_kind.articled_display_name() + )) + } + } +} + +pub(crate) trait LeafAsMutForm: IsHierarchicalForm { + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf; +} + +pub(crate) trait IsDynCompatibleForm: IsHierarchicalForm { + /// The container for a dyn Trait based type. + /// The DynLeaf can be similar to the standard leaf, but must be + /// able to support an unsized D. + type DynLeaf<'a, D: 'static + ?Sized>; + + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Result, Content<'a, T, Self>> + where + T::Leaf: CastDyn; +} + +pub(crate) trait MapFromArgument: IsHierarchicalForm { + const ARGUMENT_OWNERSHIP: ArgumentOwnership; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult>; +} + +pub(crate) trait MapIntoReturned: IsHierarchicalForm { + fn into_returned_value( + value: Content<'static, AnyType, Self>, + ) -> ExecutionResult; +} diff --git a/src/expressions/concepts/forms/any_mut.rs b/src/expressions/concepts/forms/any_mut.rs new file mode 100644 index 00000000..5bbaf984 --- /dev/null +++ b/src/expressions/concepts/forms/any_mut.rs @@ -0,0 +1,66 @@ +use super::*; + +impl<'a, L: IsValueLeaf> IsValueContent for AnyMut<'a, L> { + type Type = L::Type; + type Form = BeAnyMut; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for AnyMut<'a, L> { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for AnyMut<'a, L> { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +#[derive(Copy, Clone)] +pub(crate) struct BeAnyMut; +impl IsForm for BeAnyMut {} +impl IsHierarchicalForm for BeAnyMut { + type Leaf<'a, T: IsLeafType> = AnyMut<'a, T::Leaf>; +} + +impl IsDynCompatibleForm for BeAnyMut { + type DynLeaf<'a, D: 'static + ?Sized> = AnyMut<'a, D>; + + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Result, Content<'a, T, Self>> + where + T::Leaf: CastDyn, + { + leaf.replace(|content, emplacer| match ::map_mut(content) { + Ok(mapped) => Ok(emplacer.emplace(mapped)), + Err(this) => Err(emplacer.emplace(this)), + }) + } +} + +impl LeafAsRefForm for BeAnyMut { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + leaf + } +} + +impl LeafAsMutForm for BeAnyMut { + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf { + leaf + } +} + +impl MapFromArgument for BeAnyMut { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + Ok(value + .expect_mutable() + .0 + .replace(|inner, emplacer| inner.as_mut_value().into_mutable_any_mut(emplacer))) + } +} diff --git a/src/expressions/concepts/forms/any_ref.rs b/src/expressions/concepts/forms/any_ref.rs new file mode 100644 index 00000000..3e9220cd --- /dev/null +++ b/src/expressions/concepts/forms/any_ref.rs @@ -0,0 +1,61 @@ +use super::*; + +impl<'a, L: IsValueLeaf> IsValueContent for AnyRef<'a, L> { + type Type = L::Type; + type Form = BeAnyRef; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for AnyRef<'a, L> { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for AnyRef<'a, L> { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +#[derive(Copy, Clone)] +pub(crate) struct BeAnyRef; +impl IsForm for BeAnyRef {} + +impl IsHierarchicalForm for BeAnyRef { + type Leaf<'a, T: IsLeafType> = crate::internal_prelude::AnyRef<'a, T::Leaf>; +} + +impl IsDynCompatibleForm for BeAnyRef { + type DynLeaf<'a, D: 'static + ?Sized> = crate::internal_prelude::AnyRef<'a, D>; + + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Result, Content<'a, T, Self>> + where + T::Leaf: CastDyn, + { + leaf.replace(|content, emplacer| match ::map_ref(content) { + Ok(mapped) => Ok(emplacer.emplace(mapped)), + Err(this) => Err(emplacer.emplace(this)), + }) + } +} + +impl LeafAsRefForm for BeAnyRef { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + leaf + } +} + +impl MapFromArgument for BeAnyRef { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + Ok(value + .expect_shared() + .0 + .replace(|inner, emplacer| inner.as_ref_value().into_shared_any_ref(emplacer))) + } +} diff --git a/src/expressions/concepts/forms/argument.rs b/src/expressions/concepts/forms/argument.rs new file mode 100644 index 00000000..a2ac0e4d --- /dev/null +++ b/src/expressions/concepts/forms/argument.rs @@ -0,0 +1,44 @@ +use super::*; + +pub(crate) enum Argument { + Owned(L), + CopyOnWrite(QqqCopyOnWrite), + Mutable(QqqMutable), + Assignee(QqqAssignee), + Shared(QqqShared), +} + +impl IsValueContent for Argument { + type Type = L::Type; + type Form = BeArgument; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for Argument { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for Argument { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +#[derive(Copy, Clone)] +pub(crate) struct BeArgument; +impl IsForm for BeArgument {} + +impl IsHierarchicalForm for BeArgument { + type Leaf<'a, T: IsLeafType> = Argument; +} + +impl MapFromArgument for BeArgument { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + todo!("Argument") + } +} diff --git a/src/expressions/concepts/forms/assignee.rs b/src/expressions/concepts/forms/assignee.rs new file mode 100644 index 00000000..1a20eef6 --- /dev/null +++ b/src/expressions/concepts/forms/assignee.rs @@ -0,0 +1,68 @@ +use super::*; + +pub(crate) struct QqqAssignee(pub(crate) MutableSubRcRefCell); + +impl IsValueContent for QqqAssignee { + type Type = L::Type; + type Form = BeAssignee; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqAssignee { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqAssignee { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +#[derive(Copy, Clone)] +pub(crate) struct BeAssignee; +impl IsForm for BeAssignee {} + +impl IsHierarchicalForm for BeAssignee { + type Leaf<'a, T: IsLeafType> = QqqAssignee; +} + +impl IsDynCompatibleForm for BeAssignee { + type DynLeaf<'a, D: 'static + ?Sized> = QqqAssignee; + + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Result, Content<'a, T, Self>> + where + T::Leaf: CastDyn, + { + leaf.0 + .replace(|content, emplacer| match ::map_mut(content) { + Ok(mapped) => Ok(QqqAssignee(emplacer.emplace(mapped))), + Err(this) => Err(QqqAssignee(emplacer.emplace(this))), + }) + } +} + +impl LeafAsRefForm for BeAssignee { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + &leaf.0 + } +} + +impl LeafAsMutForm for BeAssignee { + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf { + &mut leaf.0 + } +} + +impl MapFromArgument for BeAssignee { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = + ArgumentOwnership::Assignee { auto_create: false }; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + 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 new file mode 100644 index 00000000..2e6ea9a0 --- /dev/null +++ b/src/expressions/concepts/forms/copy_on_write.rs @@ -0,0 +1,318 @@ +use super::*; + +pub(crate) enum QqqCopyOnWrite { + /// An owned value that can be used directly + Owned(Owned), + /// For use when the CopyOnWrite value effectively represents the owned value (post-clone). + /// In this case, returning a Cow is just an optimization and we can always clone infallibly. + SharedWithInfallibleCloning(QqqShared), + /// For use when the CopyOnWrite value represents a pre-cloned read-only value. + /// A transparent clone may fail in this case at use time. + SharedWithTransparentCloning(QqqShared), +} + +impl IsValueContent for QqqCopyOnWrite { + type Type = L::Type; + type Form = BeCopyOnWrite; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqCopyOnWrite { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqCopyOnWrite { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +#[derive(Copy, Clone)] +pub(crate) struct BeCopyOnWrite; +impl IsForm for BeCopyOnWrite {} + +impl IsHierarchicalForm for BeCopyOnWrite { + type Leaf<'a, T: IsLeafType> = QqqCopyOnWrite; +} + +impl BeCopyOnWrite { + pub(crate) fn new_owned<'a, C: IntoValueContent<'a, Form = BeOwned>>( + owned: C, + ) -> Content<'a, C::Type, BeCopyOnWrite> { + map_via_leaf! { + input: (Content<'a, C::Type, BeOwned>) = owned.into_content(), + fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { + QqqCopyOnWrite::Owned(leaf) + } + } + } + + pub(crate) fn new_shared_in_place_of_owned<'a, C: IntoValueContent<'a, Form = BeShared>>( + shared: C, + ) -> Content<'a, C::Type, BeCopyOnWrite> { + map_via_leaf! { + input: (Content<'a, C::Type, BeShared>) = shared.into_content(), + fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { + QqqCopyOnWrite::SharedWithInfallibleCloning(leaf) + } + } + } + + pub(crate) fn new_shared_in_place_of_shared<'a, C: IntoValueContent<'a, Form = BeShared>>( + shared: C, + ) -> Content<'a, C::Type, BeCopyOnWrite> { + map_via_leaf! { + input: (Content<'a, C::Type, BeShared>) = shared.into_content(), + fn map_leaf(leaf) -> (Content<'a, T, BeCopyOnWrite>) { + QqqCopyOnWrite::SharedWithTransparentCloning(leaf) + } + } + } +} + +impl MapFromArgument for BeCopyOnWrite { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + match value.expect_copy_on_write().inner { + CopyOnWriteInner::Owned(owned) => Ok(BeCopyOnWrite::new_owned(owned)), + CopyOnWriteInner::SharedWithInfallibleCloning(shared) => { + Ok(BeCopyOnWrite::new_shared_in_place_of_owned(shared)) + } + CopyOnWriteInner::SharedWithTransparentCloning(shared) => { + Ok(BeCopyOnWrite::new_shared_in_place_of_shared(shared)) + } + } + } +} + +impl LeafAsRefForm for BeCopyOnWrite { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + match leaf { + QqqCopyOnWrite::Owned(owned) => owned, + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => shared, + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => shared, + } + } + + fn leaf_clone_to_owned_infallible<'r, 'a: 'r, T: IsLeafType>( + leaf: &'r Self::Leaf<'a, T>, + ) -> T::Leaf { + match leaf { + QqqCopyOnWrite::Owned(owned) => owned.clone(), + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => { + BeShared::leaf_clone_to_owned_infallible::(shared) + } + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => { + BeShared::leaf_clone_to_owned_infallible::(shared) + } + } + } + + fn leaf_clone_to_owned_transparently<'r, 'a: 'r, T: IsLeafType>( + leaf: &'r Self::Leaf<'a, T>, + error_span: SpanRange, + ) -> ExecutionResult { + match leaf { + QqqCopyOnWrite::Owned(owned) => Ok(owned.clone()), + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => { + Ok(BeShared::leaf_clone_to_owned_infallible::(shared)) + } + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => { + BeShared::leaf_clone_to_owned_transparently::(shared, error_span) + } + } + } +} + +pub(crate) trait IsSelfCopyOnWriteContent<'a>: IsSelfValueContent<'a> +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ + fn into_any_level_copy_on_write(self) -> AnyLevelCopyOnWrite<'a, Self::Type> + where + Self: Sized, + { + map_via_leaf! { + input: (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (AnyLevelCopyOnWrite<'a, T>) { + match leaf { + QqqCopyOnWrite::Owned(owned) => AnyLevelCopyOnWrite::Owned(owned), + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared), + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared), + } + } + } + } + + fn into_owned_infallible(self) -> Content<'static, Self::Type, BeOwned> + where + Self: Sized, + { + map_via_leaf! { + input: (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (Content<'static, T, BeOwned>) { + match leaf { + QqqCopyOnWrite::Owned(owned) => owned, + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => BeShared::leaf_clone_to_owned_infallible::(&shared), + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => BeShared::leaf_clone_to_owned_infallible::(&shared), + } + } + } + } + + fn into_owned_transparently( + self, + error_span: SpanRange, + ) -> ExecutionResult> + where + Self: Sized, + { + map_via_leaf! { + input: (Content<'a, Self::Type, Self::Form>) = self, + state: SpanRange | let error_span = error_span, + fn map_leaf(leaf) -> (ExecutionResult>) { + Ok(match leaf { + QqqCopyOnWrite::Owned(owned) => owned, + QqqCopyOnWrite::SharedWithInfallibleCloning(shared) => BeShared::leaf_clone_to_owned_infallible::(&shared), + QqqCopyOnWrite::SharedWithTransparentCloning(shared) => BeShared::leaf_clone_to_owned_transparently::(&shared, error_span)?, + }) + } + } + } + + fn acts_as_shared_reference(&self) -> bool { + map_via_leaf! { + input: &'r (Content<'a, Self::Type, Self::Form>) = self, + fn map_leaf(leaf) -> (bool) { + match leaf { + QqqCopyOnWrite::Owned(_) => false, + QqqCopyOnWrite::SharedWithInfallibleCloning(_) => false, + QqqCopyOnWrite::SharedWithTransparentCloning(_) => true, + } + } + } + } + + // TODO - Find alternative implementation or replace + fn map(self, mapper: M) -> ExecutionResult> + where + 'a: 'static, + M: OwnedTypeMapper + RefTypeMapper, + M: TypeMapper, // u32 is temporary to see if we can make it work without adding generics to `map_via_leaf!` + Self: Sized, + Self: IsValueContent, // Temporary to see if we can make it work without adding generics to `map_via_leaf!` + { + Ok(match self.into_any_level_copy_on_write() { + AnyLevelCopyOnWrite::Owned(owned) => BeCopyOnWrite::new_owned(mapper.map_owned(owned)?), + AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { + // // Apply map, get Shared> + // let shared = map_via_leaf! { + // input: (Content<'a, Self::Type, BeShared>) = shared, + // fn map_leaf(leaf) -> (ExecutionResult>>) { + // leaf.try_map(|value_ref| { + // let from_ref = value_ref.into_any(); + // inner_map_ref(from_ref) // &Content + + // // If inner_map_ref returned a Content<'a, M::TTo, BeRef> + // // then we'd need to move the leaf map inside here, but it'd work the same + // }) + // } + // }?; + // // Migrate Shared into leaf + // let shared_content = shared.replace(|content, emplacer| { + // // TODO - use two lifetimes here + // // ---- + // map_via_leaf! { + // input: &'r (Content<'a, AnyType, BeOwned>) = content, + // state: | <'e> SharedEmplacer<'e, AnyValue, AnyValue> | let emplacer = emplacer, + // fn map_leaf(leaf) -> (Content<'static, T, BeShared>) { + // emplacer.emplace(leaf) + // } + // } + // }); + // BeCopyOnWrite::new_shared_in_place_of_owned::(shared_content) + todo!() + } + AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { + todo!() + } + }) + } +} + +fn inner_map_ref<'a>( + ref_value: Content<'a, AnyType, BeRef>, +) -> ExecutionResult<&'a Content<'static, AnyType, BeOwned>> { + unimplemented!() +} + +/// Using the leaf-based type [`Content<'a, T, BeCopyOnWrite>`] is usually preferred, +/// but in some instances (particularly around supporting old code), we may want the +/// partitioning to be at a higher level (e.g. an `Owned(OwnedValue)` or `Shared(SharedValue)`). +/// +/// That is what this type represents. +pub(crate) enum AnyLevelCopyOnWrite<'a, T: IsHierarchicalType> { + Owned(Content<'a, T, BeOwned>), + SharedWithInfallibleCloning(Content<'a, T, BeShared>), + SharedWithTransparentCloning(Content<'a, T, BeShared>), +} + +impl<'a, T: IsHierarchicalType> AnyLevelCopyOnWrite<'a, T> { + pub fn into_copy_on_write(self) -> Content<'a, T, BeCopyOnWrite> { + match self { + AnyLevelCopyOnWrite::Owned(owned) => BeCopyOnWrite::new_owned(owned), + AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { + BeCopyOnWrite::new_shared_in_place_of_owned(shared) + } + AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { + BeCopyOnWrite::new_shared_in_place_of_shared(shared) + } + } + } +} + +// TODO[concepts]: COPY ON WRITE MAPPING +// How map can work: +// - Create AnyLevelCopyOnWrite<'a, T>: +// * AnyLevelCopyOnWrite::<'a, T>::Owned(Content<'a, T, BeOwned>) +// * AnyLevelCopyOnWrite::<'a, T>::SharedAsX(Content<'a, T, BeShared>) +// - Use a leaf mapper to convert Content<'a, TFrom, BeCopyOnWrite> to AnyLevelCopyOnWrite::<'a, TFrom> +// - Then map the inner Content via TFrom::map_to::() +// - Then convert AnyLevelCopyOnWrite<'a, TTo> back to Content<'a, TTo, BeCopyOnWrite> via leaf mapping `Owned` / `Shared` *to* copy on write (see e.g. as_referencable) +// - Create an AnyValuePropertyAccessor and an AnyValueIndexer +pub(crate) trait TypeMapper { + type TFrom: IsHierarchicalType; + type TTo: IsHierarchicalType; +} +pub(crate) trait OwnedTypeMapper: TypeMapper { + fn map_owned<'a>( + self, + owned_value: Content<'a, Self::TFrom, BeOwned>, + ) -> ExecutionResult>; +} + +pub(crate) trait RefTypeMapper: TypeMapper { + fn map_ref<'a>( + self, + ref_value: Content<'a, Self::TFrom, BeRef>, + ) -> ExecutionResult<&'a Content<'a, Self::TTo, BeOwned>>; +} + +pub(crate) trait MutTypeMapper: TypeMapper { + fn map_mut<'a>( + self, + mut_value: Content<'a, Self::TFrom, BeMut>, + ) -> ExecutionResult<&'a mut Content<'a, Self::TTo, BeOwned>>; +} + +impl<'a, C: IsSelfValueContent<'a>> IsSelfCopyOnWriteContent<'a> for C +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ +} diff --git a/src/expressions/concepts/forms/late_bound.rs b/src/expressions/concepts/forms/late_bound.rs new file mode 100644 index 00000000..0395e1ca --- /dev/null +++ b/src/expressions/concepts/forms/late_bound.rs @@ -0,0 +1,58 @@ +use super::*; + +pub(crate) enum QqqLateBound { + /// An owned value that can be converted to any ownership type + Owned(QqqLateBoundOwned), + /// A copy-on-write value that can be converted to an owned value + CopyOnWrite(QqqCopyOnWrite), + /// A mutable reference + Mutable(QqqMutable), + /// A shared reference where mutable access failed for a specific reason + Shared(QqqLateBoundShared), +} + +impl IsValueContent for QqqLateBound { + type Type = L::Type; + type Form = BeLateBound; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqLateBound { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqLateBound { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +/// Universal value type that can resolve to any concrete ownership type. +/// +/// Sometimes, a value can be accessed, but we don't yet know *how* we need to access it. +/// In this case, we attempt to load it with the most powerful access we can have, and convert it later. +/// +/// ## Example of requirement +/// For example, if we have `x[a].method()`, we first need to resolve the type of `x[a]` to know +/// whether the method needs `x[a]` to be a shared reference, mutable reference or an owned value. +/// +/// So instead, we take the most powerful access we can have for `x[a]`, and convert it later. +#[derive(Copy, Clone)] +pub(crate) struct BeLateBound; +impl IsForm for BeLateBound {} + +impl IsHierarchicalForm for BeLateBound { + type Leaf<'a, T: IsLeafType> = QqqLateBound; +} + +pub(crate) struct QqqLateBoundOwned { + pub(crate) owned: O, + pub(crate) is_from_last_use: bool, +} + +/// A shared value where mutable access failed for a specific reason +pub(crate) struct QqqLateBoundShared { + pub(crate) shared: QqqShared, + pub(crate) reason_not_mutable: syn::Error, +} diff --git a/src/expressions/concepts/forms/mod.rs b/src/expressions/concepts/forms/mod.rs new file mode 100644 index 00000000..3f42e697 --- /dev/null +++ b/src/expressions/concepts/forms/mod.rs @@ -0,0 +1,28 @@ +#![allow(unused)] // Whilst we're building it out +use super::*; + +mod any_mut; +mod any_ref; +mod argument; +mod assignee; +mod copy_on_write; +mod late_bound; +mod mutable; +mod owned; +mod referenceable; +mod shared; +mod simple_mut; +mod simple_ref; + +pub(crate) use any_mut::*; +pub(crate) use any_ref::*; +pub(crate) use argument::*; +pub(crate) use assignee::*; +pub(crate) use copy_on_write::*; +pub(crate) use late_bound::*; +pub(crate) use mutable::*; +pub(crate) use owned::*; +pub(crate) use referenceable::*; +pub(crate) use shared::*; +pub(crate) use simple_mut::*; +pub(crate) use simple_ref::*; diff --git a/src/expressions/concepts/forms/mutable.rs b/src/expressions/concepts/forms/mutable.rs new file mode 100644 index 00000000..b17e1c43 --- /dev/null +++ b/src/expressions/concepts/forms/mutable.rs @@ -0,0 +1,74 @@ +use super::*; + +pub(crate) type QqqMutable = MutableSubRcRefCell; + +impl IsValueContent for QqqMutable { + type Type = L::Type; + type Form = BeMutable; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqMutable { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqMutable { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +#[derive(Copy, Clone)] +pub(crate) struct BeMutable; +impl IsForm for BeMutable {} + +impl IsHierarchicalForm for BeMutable { + type Leaf<'a, T: IsLeafType> = QqqMutable; +} + +impl IsDynCompatibleForm for BeMutable { + type DynLeaf<'a, D: 'static + ?Sized> = QqqMutable; + + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Result, Content<'a, T, Self>> + where + T::Leaf: CastDyn, + { + leaf.replace(|content, emplacer| match ::map_mut(content) { + Ok(mapped) => Ok(emplacer.emplace(mapped)), + Err(this) => Err(emplacer.emplace(this)), + }) + } +} + +impl LeafAsRefForm for BeMutable { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + leaf + } +} + +impl LeafAsMutForm for BeMutable { + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf { + leaf + } +} + +impl MapFromArgument for BeMutable { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + Ok(value.expect_mutable().into_content()) + } +} + +// impl MapIntoReturned for BeMutable { +// fn into_returned_value( +// content: Content<'static, AnyType, Self>, +// ) -> ExecutionResult { +// todo!("Return mutable") +// } +// } diff --git a/src/expressions/concepts/forms/owned.rs b/src/expressions/concepts/forms/owned.rs new file mode 100644 index 00000000..5095ac42 --- /dev/null +++ b/src/expressions/concepts/forms/owned.rs @@ -0,0 +1,93 @@ +use super::*; + +/// Just [`T`]! This exists simply to be a name for symmetry with e.g. Shared or Mutable. +pub(crate) type Owned = T; + +// NOTE: Attempting to blanket implement IsValueContent etc for all T: IsValueLeaf causes +// a clash (inlike the other forms) - so instead we work around this with (a) manual impls +// in impl_value_content_traits for each L, and (b) bounds on L: IsValueLeaf. + +/// Represents floating owned values. +/// +/// If you need span information, wrap with `Spanned`. For example, with `x.y[4]`, this would capture both: +/// * The output owned value +/// * The lexical span of the tokens `x.y[4]` +#[derive(Copy, Clone)] +pub(crate) struct BeOwned; +impl IsForm for BeOwned {} + +impl IsHierarchicalForm for BeOwned { + type Leaf<'a, T: IsLeafType> = T::Leaf; +} + +impl IsDynCompatibleForm for BeOwned { + type DynLeaf<'a, D: 'static + ?Sized> = Box; + + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Result, Content<'a, T, Self>> + where + T::Leaf: CastDyn, + { + ::map_boxed(Box::new(leaf)).map_err(|boxed| *boxed) + } +} + +impl LeafAsRefForm for BeOwned { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + leaf + } +} + +impl LeafAsMutForm for BeOwned { + fn leaf_as_mut<'r, 'a: 'r, T: IsLeafType>(leaf: &'r mut Self::Leaf<'a, T>) -> &'r mut T::Leaf { + leaf + } +} + +impl MapFromArgument for BeOwned { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + Ok(value.expect_owned()) + } +} + +impl MapIntoReturned for BeOwned { + fn into_returned_value( + content: Content<'static, AnyType, Self>, + ) -> ExecutionResult { + Ok(ReturnedValue::Owned(content)) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn can_resolve_owned() { + let owned_value: Owned<_> = 42u64; + let resolved = owned_value + .spanned(Span::call_site().span_range()) + .downcast_resolve::("My value") + .unwrap(); + assert_eq!(resolved, 42u64); + } + + #[test] + fn can_as_ref_owned() { + let owned_value = 42u64; + let as_ref: Content = owned_value.as_ref_value(); + assert_eq!(*as_ref, 42u64); + } + + #[test] + fn can_as_mut_owned() { + let mut owned_value = 42u64; + *owned_value.as_mut_value() = 41u64; + assert_eq!(owned_value, 41u64); + } +} diff --git a/src/expressions/concepts/forms/referenceable.rs b/src/expressions/concepts/forms/referenceable.rs new file mode 100644 index 00000000..47b7b622 --- /dev/null +++ b/src/expressions/concepts/forms/referenceable.rs @@ -0,0 +1,33 @@ +use super::*; + +pub(crate) type Referenceable = Rc>; + +impl IsValueContent for Referenceable { + type Type = L::Type; + type Form = BeReferenceable; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for Referenceable { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for Referenceable { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +/// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. +/// This is useful for the content of variables. +/// +/// Note that Referenceable form does not support dyn casting, because Rc> cannot be +/// directly cast to Rc>. +#[derive(Copy, Clone)] +pub(crate) struct BeReferenceable; +impl IsForm for BeReferenceable {} + +impl IsHierarchicalForm for BeReferenceable { + type Leaf<'a, T: IsLeafType> = Referenceable; +} diff --git a/src/expressions/concepts/forms/shared.rs b/src/expressions/concepts/forms/shared.rs new file mode 100644 index 00000000..106cf736 --- /dev/null +++ b/src/expressions/concepts/forms/shared.rs @@ -0,0 +1,68 @@ +use super::*; + +pub(crate) type QqqShared = SharedSubRcRefCell; + +impl IsValueContent for QqqShared { + type Type = L::Type; + type Form = BeShared; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for QqqShared { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for QqqShared { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +#[derive(Copy, Clone)] +pub(crate) struct BeShared; +impl IsForm for BeShared {} + +impl IsHierarchicalForm for BeShared { + type Leaf<'a, T: IsLeafType> = QqqShared; +} + +impl IsDynCompatibleForm for BeShared { + type DynLeaf<'a, D: 'static + ?Sized> = QqqShared; + + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Result, Content<'a, T, Self>> + where + T::Leaf: CastDyn, + { + leaf.replace(|content, emplacer| match ::map_ref(content) { + Ok(mapped) => Ok(emplacer.emplace(mapped)), + Err(this) => Err(emplacer.emplace(this)), + }) + } +} + +impl LeafAsRefForm for BeShared { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + leaf + } +} + +impl MapFromArgument for BeShared { + const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; + + fn from_argument_value( + value: ArgumentValue, + ) -> ExecutionResult> { + Ok(value.expect_shared().into_content()) + } +} + +// impl MapIntoReturned for BeShared { +// fn into_returned_value( +// content: Content<'static, AnyType, Self>, +// ) -> ExecutionResult { +// todo!("Return shared") +// } +// } diff --git a/src/expressions/concepts/forms/simple_mut.rs b/src/expressions/concepts/forms/simple_mut.rs new file mode 100644 index 00000000..fcd4bbe4 --- /dev/null +++ b/src/expressions/concepts/forms/simple_mut.rs @@ -0,0 +1,154 @@ +use super::*; + +impl IsValueContent for &mut L { + type Type = L::Type; + type Form = BeMut; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for &'a mut L { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for &'a mut L { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +/// It can't be an argument because arguments must be owned in some way; +/// so that the drop glue can work properly (because they may come from +/// e.g. a reference counted Shared handle) +#[derive(Copy, Clone)] +pub(crate) struct BeMut; +impl IsForm for BeMut {} + +impl IsHierarchicalForm for BeMut { + type Leaf<'a, T: IsLeafType> = &'a mut T::Leaf; +} + +impl IsDynCompatibleForm for BeMut { + type DynLeaf<'a, D: 'static + ?Sized> = &'a mut D; + + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Result, Content<'a, T, Self>> + where + T::Leaf: CastDyn, + { + ::map_mut(leaf) + } +} + +impl LeafAsRefForm for BeMut { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + leaf + } +} + +pub(crate) trait IsSelfMutContent<'a>: IsSelfValueContent<'a> +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ + fn into_mutable<'b, T: 'static>( + self, + emplacer: &'b mut MutableEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeMutable> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut MutableEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeMutable>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { self.emplacer.emplace_unchecked(leaf) } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } + + fn into_assignee<'b, T: 'static>( + self, + emplacer: &'b mut MutableEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeAssignee> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut MutableEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeAssignee>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { QqqAssignee(self.emplacer.emplace_unchecked(leaf)) } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } + + fn into_mutable_any_mut<'b, T: 'static>( + self, + emplacer: &'b mut MutableEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeAnyMut> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut MutableEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeAnyMut>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { Mutable(self.emplacer.emplace_unchecked(leaf)).into() } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } +} + +impl<'a, C: IsSelfValueContent<'a>> IsSelfMutContent<'a> for C +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ +} diff --git a/src/expressions/concepts/forms/simple_ref.rs b/src/expressions/concepts/forms/simple_ref.rs new file mode 100644 index 00000000..d466b492 --- /dev/null +++ b/src/expressions/concepts/forms/simple_ref.rs @@ -0,0 +1,123 @@ +use super::*; + +impl IsValueContent for &L { + type Type = L::Type; + type Form = BeRef; +} + +impl<'a, L: IsValueLeaf> IntoValueContent<'a> for &'a L { + fn into_content(self) -> Content<'a, Self::Type, Self::Form> { + self + } +} + +impl<'a, L: IsValueLeaf> FromValueContent<'a> for &'a L { + fn from_content(content: Content<'a, Self::Type, Self::Form>) -> Self { + content + } +} + +/// It can't be an argument because arguments must be owned in some way; +/// so that the drop glue can work properly (because they may come from +/// e.g. a reference counted Shared handle) +#[derive(Copy, Clone)] +pub(crate) struct BeRef; +impl IsForm for BeRef {} + +impl IsHierarchicalForm for BeRef { + type Leaf<'a, T: IsLeafType> = &'a T::Leaf; +} + +impl IsDynCompatibleForm for BeRef { + type DynLeaf<'a, D: 'static + ?Sized> = &'a D; + + fn leaf_to_dyn<'a, T: IsLeafType, D: ?Sized + 'static>( + leaf: Self::Leaf<'a, T>, + ) -> Result, Content<'a, T, Self>> + where + T::Leaf: CastDyn, + { + ::map_ref(leaf) + } +} + +impl LeafAsRefForm for BeRef { + fn leaf_as_ref<'r, 'a: 'r, T: IsLeafType>(leaf: &'r Self::Leaf<'a, T>) -> &'r T::Leaf { + leaf + } +} + +pub(crate) trait IsSelfRefContent<'a>: IsSelfValueContent<'a> +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ + fn into_shared<'b, T: 'static>( + self, + emplacer: &'b mut SharedEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeShared> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut SharedEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeShared>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { self.emplacer.emplace_unchecked(leaf) } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } + + fn into_shared_any_ref<'b, T: 'static>( + self, + emplacer: &'b mut SharedEmplacer<'a, T>, + ) -> Content<'static, Self::Type, BeAnyRef> + where + Self: Sized, + { + struct __InlineMapper<'b, 'e2, X: 'static> { + emplacer: &'b mut SharedEmplacer<'e2, X>, + } + impl<'b, 'e2, X> LeafMapper for __InlineMapper<'b, 'e2, X> { + type Output<'a, T: IsHierarchicalType> = Content<'static, T, BeAnyRef>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + T::into_parent(output) + } + + fn map_leaf<'l, T: IsLeafType>( + self, + leaf: ::Leaf<'l, T>, + ) -> Self::Output<'l, T> { + // SAFETY: 'l = 'a = 'e so this is valid + unsafe { Shared(self.emplacer.emplace_unchecked(leaf)).into() } + } + }; + let __mapper = __InlineMapper { emplacer }; + ::map_with::(__mapper, self) + } +} + +impl<'a, C: IsSelfValueContent<'a>> IsSelfRefContent<'a> for C +where + Self: IsValueContent, + Self::Type: IsHierarchicalType = Self>, +{ +} diff --git a/src/expressions/concepts/mapping.rs b/src/expressions/concepts/mapping.rs new file mode 100644 index 00000000..6b58a307 --- /dev/null +++ b/src/expressions/concepts/mapping.rs @@ -0,0 +1,188 @@ +use super::*; + +pub(crate) trait LeafMapper { + type Output<'a, T: IsHierarchicalType>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType>; + + fn map_leaf<'a, T: IsLeafType>(self, leaf: F::Leaf<'a, T>) -> Self::Output<'a, T>; +} + +pub(crate) trait RefLeafMapper { + type Output<'r, 'a: 'r, T: IsHierarchicalType>; + + fn to_parent_output<'r, 'a: 'r, T: IsChildType>( + output: Self::Output<'r, 'a, T>, + ) -> Self::Output<'r, 'a, T::ParentType>; + + fn map_leaf<'r, 'a: 'r, T: IsLeafType>( + self, + leaf: &'r F::Leaf<'a, T>, + ) -> Self::Output<'r, 'a, T>; +} + +pub(crate) trait MutLeafMapper { + type Output<'r, 'a: 'r, T: IsHierarchicalType>; + + fn to_parent_output<'r, 'a: 'r, T: IsChildType>( + output: Self::Output<'r, 'a, T>, + ) -> Self::Output<'r, 'a, T::ParentType>; + + fn map_leaf<'r, 'a: 'r, T: IsLeafType>( + self, + leaf: &'r mut F::Leaf<'a, T>, + ) -> Self::Output<'r, 'a, T>; +} + +/// Macro for creating and immediately applying a leaf mapper inline. +/// +/// This macro creates an anonymous mapper struct, implements the appropriate mapper trait +/// (LeafMapper, RefLeafMapper, or MutLeafMapper) based on the leaf pattern, and applies +/// it to the input content. +/// +/// Specific output type forms are supported for parent propogation. +/// If you get a cryptic error in the to_parent method, you may need to add +/// an explicit implementation into [`__map_via_leaf_to_parent`] +/// +/// # Syntax +/// +/// ```ignore +/// map_via_leaf! { +/// input: [ | &'r | &'r mut] (Content<'a, InputType, InputForm>) = input_value, +/// state: StateType | let state_binding = state_init, // Optional +/// fn map_leaf | : | = ], T>(leaf) -> (OutputType) +/// [where <...>] // Optional +/// { +/// // mapper body +/// } +/// } +/// ``` +macro_rules! map_via_leaf { + ( + input: $(&$r:lifetime $($mut:ident)?)? (Content<$a:lifetime, $input_type:ty, $input_form:ty>) = $input_value:expr, + $(state: $(| <$($state_l:lifetime,)* $($state_t:ident = $state_t_ty:ty,)*>)? $state_type:ty | let $state_pat:pat = $state_init:expr,)? + fn map_leaf <$f:ident $(: $fb:ident $(+ $fbe:ident )*)? $(= $fixed_form:ty)?, $t:ident> ($leaf:ident) -> ($($output_type:tt)+) + $(where $($where_clause:tt)*)? + $body:block + ) => {{ + struct __InlineMapper $($(<$($state_l,)* $($state_t,)*>)?)? { + state: $crate::if_exists!{ {$($state_type)?} {$($state_type)?} {()} }, + } + + $crate::__map_via_leaf_trait_impl!{ + $(@fixed_form $fixed_form |)? impl<$f $(: $fb $(+ $fbe)*)?> @mutability $(&$r $($mut)?)? for __InlineMapper $($(<$($state_l,)* $($state_t,)*>)?)? $(where $($where_clause)*)? + { + type Output<$($r,)? $a $(: $r)?, $t: $crate::expressions::concepts::IsHierarchicalType> = $($output_type)+; + + fn to_parent_output<$($r,)? $a $(: $r)?, $t: $crate::expressions::concepts::IsChildType>( + output: Self::Output<$($r,)? $a, $t>, + ) -> Self::Output<$($r,)? $a, $t::ParentType> { + $crate::__map_via_leaf_to_parent!(output -> $($output_type)+) + } + + #[allow(unused_mut)] + fn map_leaf<$($r,)? $a $(: $r)?, $t: $crate::expressions::concepts::IsLeafType>( + self, + $leaf: $crate::__map_via_leaf_leaf_type!($(@fixed_form $fixed_form |)? $(&$r $($mut)?)? | $f::Leaf<$a, $t>), + ) -> Self::Output<$($r,)? 'a, T> { + $(let $state_pat = self.state;)? + $body + } + } + }; + + let __mapper = __InlineMapper { + state: $crate::if_exists!{ {$($state_type)?} {$($state_init)?} {()} }, + }; + $crate::__map_via_leaf_output!( + <$input_type, $input_form> @mutability $(&$r $($mut)?)? ( + __mapper, + $input_value + ) + ) + }}; +} + +#[doc(hidden)] +macro_rules! __map_via_leaf_trait_impl { + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime mut for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? <$f $(: $fb $(+ $fbe)*)?> MutLeafMapper<$f> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability &$r:lifetime for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? <$f $(: $fb $(+ $fbe)*)?> RefLeafMapper<$f> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (impl<$f:ident $(: $fb:ident $(+ $fbe:ident )*)?> @mutability for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? <$f $(: $fb $(+ $fbe)*)?> LeafMapper<$f> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime mut for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? MutLeafMapper<$fixed_form> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability &$r:lifetime for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? RefLeafMapper<$fixed_form> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; + (@fixed_form $fixed_form:ty | impl<$f:ident> @mutability for $mapper:ident $(<$($state_l:lifetime,)* $($state_t:ident,)*>)? $(where $($where_clause:tt)*)? { $($body:tt)* } ) => { impl$(<$($state_l,)* $($state_t,)*>)? LeafMapper<$fixed_form> for $mapper $(<$($state_l,)* $($state_t,)*>)? $(where $($where_clause)*)? { $($body)* } }; +} + +#[doc(hidden)] +macro_rules! __map_via_leaf_output { + (<$input_type:ty, $input_form:ty> @mutability &$r:lifetime mut ($mapper:expr, $input:expr) ) => { + <$input_type>::map_mut_with::<$input_form, _>($mapper, $input) + }; + (<$input_type:ty, $input_form:ty> @mutability &$r:lifetime ($mapper:expr, $input:expr) ) => { + <$input_type>::map_ref_with::<$input_form, _>($mapper, $input) + }; + (<$input_type:ty, $input_form:ty> @mutability ($mapper:expr, $input:expr) ) => { + <$input_type>::map_with::<$input_form, _>($mapper, $input) + }; +} + +#[doc(hidden)] +macro_rules! __map_via_leaf_leaf_type { + (@fixed_form $fixed_form:ty | $(&$r:lifetime $($mut:ident)?)? | $f:ident::Leaf<$a:lifetime, $t:ident>) => { + $(&$r $($mut)?)? <$fixed_form as IsHierarchicalForm>::Leaf<$a, $t> + }; + ($(&$r:lifetime $($mut:ident)?)? | $f:ident::Leaf<$a:lifetime, $t:ident>) => { + $(&$r $($mut)?)? <$f>::Leaf<$a, $t> + }; +} + +#[doc(hidden)] +macro_rules! __map_via_leaf_to_parent { + ($output:ident -> Content<$lt:lifetime, $t:ident, $form:ty>) => { + $t::into_parent($output) + }; + ($output:ident -> ExecutionResult>) => { + Ok($t::into_parent($output?)) + }; + ($output:ident -> Result, $err:ty>) => { + Ok($t::into_parent($output?)) + }; + ($output:ident -> AnyLevelCopyOnWrite<$lt:lifetime, $t:ident>) => { + match $output { + AnyLevelCopyOnWrite::Owned(owned) => AnyLevelCopyOnWrite::Owned($t::into_parent(owned)), + AnyLevelCopyOnWrite::SharedWithInfallibleCloning(shared) => { + AnyLevelCopyOnWrite::SharedWithInfallibleCloning($t::into_parent(shared)) + } + AnyLevelCopyOnWrite::SharedWithTransparentCloning(shared) => { + AnyLevelCopyOnWrite::SharedWithTransparentCloning($t::into_parent(shared)) + } + } + }; + ($output:ident -> $ty:ty) => { + $output + }; +} + +pub(crate) use { + __map_via_leaf_leaf_type, __map_via_leaf_output, __map_via_leaf_to_parent, + __map_via_leaf_trait_impl, map_via_leaf, +}; + +#[cfg(test)] +mod test_macro { + use super::*; + + #[test] + fn test_map_via_leaf_owned() { + let mut x = 4; + let owned = map_via_leaf! { + input: &'r mut (Content<'a, U8Type, BeOwned>) = &mut x, + fn map_leaf(leaf) -> (Content<'static, T, BeOwned>) { + F::leaf_clone_to_owned_infallible(leaf) + } + }; + assert_eq!(owned, 4); + } +} diff --git a/src/expressions/concepts/mod.rs b/src/expressions/concepts/mod.rs new file mode 100644 index 00000000..4e1d0177 --- /dev/null +++ b/src/expressions/concepts/mod.rs @@ -0,0 +1,14 @@ +#![allow(dead_code)] // Whilst we're building it out +use super::*; + +mod content; +mod form; +mod forms; +mod mapping; +mod type_traits; + +pub(crate) use content::*; +pub(crate) use form::*; +pub(crate) use forms::*; +pub(crate) use mapping::*; +pub(crate) use type_traits::*; diff --git a/src/expressions/concepts/type_traits.rs b/src/expressions/concepts/type_traits.rs new file mode 100644 index 00000000..e1bd9f26 --- /dev/null +++ b/src/expressions/concepts/type_traits.rs @@ -0,0 +1,871 @@ +use super::*; + +pub(crate) trait TypeVariant {} + +pub(crate) struct HierarchicalTypeVariant; +impl TypeVariant for HierarchicalTypeVariant {} + +pub(crate) struct DynTypeVariant; +impl TypeVariant for DynTypeVariant {} + +pub(crate) trait IsType: Sized + TypeData { + type Variant: TypeVariant; + + const SOURCE_TYPE_NAME: &'static str; + const ARTICLED_DISPLAY_NAME: &'static str; + + fn type_kind() -> TypeKind; + fn type_kind_from_source_name(name: &str) -> Option; +} + +pub(crate) trait IsHierarchicalType: IsType { + type Content<'a, F: IsHierarchicalForm>: IsValueContent + + IntoValueContent<'a> + + FromValueContent<'a>; + type LeafKind: IsLeafKind; + + fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( + mapper: M, + content: Self::Content<'a, F>, + ) -> M::Output<'a, Self>; + + fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( + mapper: M, + content: &'r Self::Content<'a, F>, + ) -> M::Output<'r, 'a, Self>; + + fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( + mapper: M, + content: &'r mut Self::Content<'a, F>, + ) -> M::Output<'r, 'a, Self>; + + fn content_to_leaf_kind( + content: &Self::Content<'_, F>, + ) -> Self::LeafKind; +} + +pub(crate) trait IsLeafType: + IsHierarchicalType + // NOTE: We can't create a universal bound over F: IsHierarchicalForm in rust + // If you need a generic interconversion over all such F, we'll need to reintroduce + // e.g. a fn content_to_leaf(content) method, with body { self } in each impl. + // For now though, listing out all the forms here is sufficient. + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + for<'a> IsHierarchicalType = ::Leaf<'a, Self>> + + UpcastTo +{ + type Leaf: IsValueLeaf; + + fn leaf_kind() -> Self::LeafKind; +} + +pub(crate) trait IsDynType: IsType { + type DynContent: ?Sized + 'static; +} + +pub(crate) trait UpcastTo: IsHierarchicalType { + fn upcast_to<'a, F: IsHierarchicalForm>(content: Content<'a, Self, F>) -> Content<'a, T, F>; +} + +pub(crate) trait DowncastFrom: IsHierarchicalType { + fn downcast_from<'a, F: IsHierarchicalForm>( + content: Content<'a, T, F>, + ) -> Result, Content<'a, T, F>>; + + fn resolve<'a, F: IsHierarchicalForm>( + content: Content<'a, T, F>, + span_range: SpanRange, + resolution_target: &str, + ) -> ExecutionResult> { + let content = match Self::downcast_from(content) { + Ok(c) => c, + Err(existing) => { + let leaf_kind = T::content_to_leaf_kind::(&existing); + return span_range.value_err(format!( + "{} is expected to be {}, but it is {}", + resolution_target, + Self::ARTICLED_DISPLAY_NAME, + leaf_kind.articled_display_name(), + )); + } + }; + Ok(content) + } +} + +pub(crate) trait DynResolveFrom: IsDynType { + fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>( + content: Content<'a, T, F>, + ) -> Result, Content<'a, T, F>>; + + fn resolve<'a, F: IsHierarchicalForm + IsDynCompatibleForm>( + content: Content<'a, T, F>, + span_range: SpanRange, + resolution_target: &str, + ) -> ExecutionResult> { + let content = match Self::downcast_from(content) { + Ok(c) => c, + Err(existing) => { + let leaf_kind = T::content_to_leaf_kind::(&existing); + return span_range.value_err(format!( + "{} is expected to be {}, but it is {}", + resolution_target, + Self::ARTICLED_DISPLAY_NAME, + leaf_kind.articled_display_name(), + )); + } + }; + Ok(content) + } +} + +pub(crate) trait IsChildType: IsHierarchicalType { + type ParentType: IsHierarchicalType; + + fn into_parent<'a, F: IsHierarchicalForm>( + content: Self::Content<'a, F>, + ) -> Content<'a, Self::ParentType, F>; + + fn from_parent<'a, F: IsHierarchicalForm>( + content: ::Content<'a, F>, + ) -> Result, Content<'a, Self::ParentType, F>>; +} + +macro_rules! impl_type_feature_resolver { + (impl TypeFeatureResolver for $type_def:ty: [$($type_defs:ty)+]) => { + impl TypeFeatureResolver for $type_def { + fn resolve_method(&self, method_name: &str) -> Option { + $( + if let Some(method) = <$type_defs as TypeData>::resolve_own_method(method_name) { + return Some(method); + }; + )+ + None + } + + fn resolve_unary_operation( + &self, + operation: &UnaryOperation, + ) -> Option { + $( + if let Some(operation) = <$type_defs as TypeData>::resolve_own_unary_operation(operation) { + return Some(operation); + }; + )+ + None + } + + fn resolve_binary_operation( + &self, + operation: &BinaryOperation, + ) -> Option { + $( + if let Some(operation) = <$type_defs as TypeData>::resolve_own_binary_operation(operation) { + return Some(operation); + }; + )+ + None + } + + fn resolve_type_property(&self, property_name: &str) -> Option { + // Purposefully doesn't resolve parents, but TBC if this is right + <$type_def as TypeData>::resolve_type_property(property_name) + } + + fn resolve_property_access(&self) -> Option { + $( + if let Some(interface) = <$type_defs as TypeData>::resolve_own_property_access() { + return Some(interface); + }; + )+ + None + } + + fn resolve_index_access(&self) -> Option { + $( + if let Some(interface) = <$type_defs as TypeData>::resolve_own_index_access() { + return Some(interface); + }; + )+ + None + } + } + }; +} + +pub(crate) use impl_type_feature_resolver; + +macro_rules! impl_ancestor_chain_conversions { + ($child:ty $(=> $parent:ident ($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?) => { + impl DowncastFrom<$child> for $child + { + fn downcast_from<'a, F: IsHierarchicalForm>( + content: Content<'a, Self, F>, + ) -> Result, Content<'a, Self, F>> { + Ok(content) + } + } + + impl UpcastTo<$child> for $child + { + fn upcast_to<'a, F: IsHierarchicalForm>( + content: Content<'a, Self, F>, + ) -> Content<'a, Self, F> { + content + } + } + + $( + impl IsChildType for $child { + type ParentType = $parent; + + fn into_parent<'a, F: IsHierarchicalForm>( + content: Self::Content<'a, F>, + ) -> ::Content<'a, F> { + $parent_content::$parent_variant(content) + } + + fn from_parent<'a, F: IsHierarchicalForm>( + content: ::Content<'a, F>, + ) -> Result, Content<'a, Self::ParentType, F>> { + match content { + $parent_content::$parent_variant(i) => Ok(i), + other => Err(other), + } + } + } + + impl DowncastFrom<$parent> for $child + { + fn downcast_from<'a, F: IsHierarchicalForm>( + content: Content<'a, $parent, F>, + ) -> Result, Content<'a, $parent, F>> { + <$child as IsChildType>::from_parent(content) + } + } + + impl UpcastTo<$parent> for $child + { + fn upcast_to<'a, F: IsHierarchicalForm>( + content: Content<'a, $child, F>, + ) -> Content<'a, $parent, F> { + <$child as IsChildType>::into_parent(content) + } + } + + $( + impl DowncastFrom<$ancestor> for $child { + fn downcast_from<'a, F: IsHierarchicalForm>( + content: Content<'a, $ancestor, F>, + ) -> Result, Content<'a, $ancestor, F>> { + let inner = <$parent as DowncastFrom<$ancestor>>::downcast_from::(content)?; + match <$child as DowncastFrom<$parent>>::downcast_from::(inner) { + Ok(c) => Ok(c), + Err(existing) => Err(<$parent as UpcastTo<$ancestor>>::upcast_to::(existing)), + } + } + } + + impl UpcastTo<$ancestor> for $child { + fn upcast_to<'a, F: IsHierarchicalForm>( + content: Content<'a, $child, F>, + ) -> Content<'a, $ancestor, F> { + <$parent as UpcastTo<$ancestor>>::upcast_to::(<$child as UpcastTo<$parent>>::upcast_to::(content)) + } + } + )* + )? + }; +} + +pub(crate) use impl_ancestor_chain_conversions; + +pub(crate) trait IsValueLeaf: + 'static + // This whole creation of IsLeafValueContent and the LeafType bound is a workaround to + // add an implied bound to IsValueLeaf that Self::Type: IsLeafType + // - This "use an associated type equality constraint" workaround came from a rust thread + // related to implied bounds, which of course I can't find now. + // - The main caveat is that order matters (i.e. I had to set Type == LeafType) and bound the + // correct one of Type or LeafType in other places to avoid circularity and ensure the one way + // resolution logic works correctly. + + IsValueContent::LeafType, Form = BeOwned> + + for<'a> IntoValueContent<'a> + + for<'a> FromValueContent<'a> + + IsLeafValueContent + + CastDyn + + Clone +{ +} + +pub(crate) trait IsLeafValueContent: IsValueContent { + type LeafType: IsLeafType; +} + +impl IsLeafValueContent for L +where + L::Type: IsLeafType, +{ + type LeafType = L::Type; +} + +pub(crate) trait IsDynLeaf: 'static { + type Type: IsDynType; +} + +pub(crate) trait CastDyn { + fn map_boxed(self: Box) -> Result, Box> { + Err(self) + } + fn map_ref(&self) -> Result<&T, &Self> { + Err(self) + } + fn map_mut(&mut self) -> Result<&mut T, &mut Self> { + Err(self) + } +} + +pub(crate) trait HasLeafKind { + type LeafKind: IsLeafKind; + + fn kind(&self) -> Self::LeafKind; + + fn value_kind(&self) -> AnyValueLeafKind { + self.kind().into() + } + + fn articled_kind(&self) -> &'static str { + self.kind().articled_display_name() + } +} + +// TODO[concepts]: Remove when we get rid of impl_resolvable_argument_for +impl HasLeafKind for &T { + type LeafKind = T::LeafKind; + + fn kind(&self) -> Self::LeafKind { + (**self).kind() + } +} + +// TODO[concepts]: Remove when we get rid of impl_resolvable_argument_for +impl HasLeafKind for &mut T { + type LeafKind = T::LeafKind; + + fn kind(&self) -> Self::LeafKind { + (**self).kind() + } +} + +macro_rules! define_parent_type { + ( + $type_def_vis:vis $type_def:ident $(=> $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*)?, + content: $content_vis:vis $content:ident, + leaf_kind: $leaf_kind_vis:vis $leaf_kind:ident, + type_kind: ParentTypeKind::$parent_kind:ident($type_kind_vis:vis $type_kind:ident), + variants: { + $($variant:ident => $variant_type:ty,)* + }, + type_name: $source_type_name:literal, + articled_display_name: $articled_display_name:literal, + ) => { + #[derive(Copy, Clone)] + $type_def_vis struct $type_def; + + impl IsType for $type_def { + type Variant = HierarchicalTypeVariant; + + const SOURCE_TYPE_NAME: &'static str = $source_type_name; + const ARTICLED_DISPLAY_NAME: &'static str = $articled_display_name; + + fn type_kind() -> TypeKind { + TypeKind::Parent(ParentTypeKind::$parent_kind($type_kind)) + } + + #[inline(always)] + fn type_kind_from_source_name(name: &str) -> Option { + if name == Self::SOURCE_TYPE_NAME { + return Some(Self::type_kind()); + } + $( + if let Some(type_kind) = <$variant_type as IsType>::type_kind_from_source_name(name) { + return Some(type_kind); + } + )* + None + } + } + + impl_type_feature_resolver! { + impl TypeFeatureResolver for $type_def: [ + $type_def $( $parent $( $ancestor )* )? + ] + } + + impl IsHierarchicalType for $type_def { + type Content<'a, F: IsHierarchicalForm> = $content<'a, F>; + type LeafKind = $leaf_kind; + + fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( + mapper: M, + content: Self::Content<'a, F>, + ) -> M::Output<'a, Self> { + match content { + $( $content::$variant(x) => M::to_parent_output::<'a, $variant_type>( + <$variant_type>::map_with::<'a, F, M>(mapper, x) + ), )* + } + } + + fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( + mapper: M, + content: &'r Self::Content<'a, F>, + ) -> M::Output<'r, 'a, Self> { + match content { + $( $content::$variant(x) => M::to_parent_output::<'r, 'a, $variant_type>( + <$variant_type>::map_ref_with::<'r, 'a, F, M>(mapper, x) + ), )* + } + } + + fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( + mapper: M, + content: &'r mut Self::Content<'a, F>, + ) -> M::Output<'r, 'a, Self> { + match content { + $( $content::$variant(x) => M::to_parent_output::<'r, 'a, $variant_type>( + <$variant_type>::map_mut_with::<'r, 'a, F, M>(mapper, x) + ), )* + } + } + + fn content_to_leaf_kind( + content: &Self::Content<'_, F>, + ) -> Self::LeafKind { + content.kind() + } + } + + $content_vis enum $content<'a, F: IsHierarchicalForm> { + $( $variant(Content<'a, $variant_type, F>), )* + } + + impl<'a, F: IsHierarchicalForm> Clone for $content<'a, F> + where + $( Content<'a, $variant_type, F>: Clone ),* + { + fn clone(&self) -> Self { + match self { + $( $content::$variant(x) => $content::$variant(x.clone()), )* + } + } + } + + + impl<'a, F: IsHierarchicalForm> Copy for $content<'a, F> + where + $( Content<'a, $variant_type, F>: Copy ),* + {} + + impl_value_content_traits!(parent: $type_def, $content); + + impl<'a, F: IsHierarchicalForm> HasLeafKind for $content<'a, F> { + type LeafKind = $leaf_kind; + + fn kind(&self) -> Self::LeafKind { + match self { + $($content::$variant(x) => $leaf_kind::$variant( + <$variant_type as IsHierarchicalType>::content_to_leaf_kind::(x) + ),)* + } + } + } + + #[derive(Clone, Copy, PartialEq, Eq)] + $type_kind_vis struct $type_kind; + + impl $type_kind { + pub(crate) fn articled_display_name(&self) -> &'static str { + $articled_display_name + } + + pub(crate) fn source_type_name(&self) -> &'static str { + $source_type_name + } + + pub(crate) fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { + &$type_def + } + } + + #[derive(Clone, Copy, PartialEq, Eq)] + $leaf_kind_vis enum $leaf_kind { + $( $variant(<$variant_type as IsHierarchicalType>::LeafKind), )* + } + + $( + impl From<$leaf_kind> for AnyValueLeafKind { + fn from(kind: $leaf_kind) -> Self { + let as_parent_kind = <$parent as IsHierarchicalType>::LeafKind::$parent_variant(kind); + AnyValueLeafKind::from(as_parent_kind) + } + } + )? + + impl IsLeafKind for $leaf_kind { + fn source_type_name(&self) -> &'static str { + match self { + $( Self::$variant(x) => x.source_type_name(), )* + } + } + + fn articled_display_name(&self) -> &'static str { + match self { + $( Self::$variant(x) => x.articled_display_name(), )* + } + } + + fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { + match self { + $( Self::$variant(x) => x.feature_resolver(), )* + } + } + } + + impl_ancestor_chain_conversions!( + $type_def $(=> $parent($parent_content :: $parent_variant) $(=> $ancestor)*)? + ); + }; +} + +pub(crate) use define_parent_type; + +macro_rules! impl_fallback_is_iterable { + ({IsIterable $($haystack:tt)*} for $content_type:ty) => { + // Already implemented, ignoring + }; + ({$discard:tt $($haystack:tt)*} for $content_type:ty) => { + // Recurse to check the rest + impl_is_iterable_if_missing!({$($haystack)*} for $content_type) + }; + ({} for $content_type:ty) => { + // Implement fallback + impl CastDyn for $content_type {} + }; +} + +pub(crate) use impl_fallback_is_iterable; + +macro_rules! define_leaf_type { + ( + $type_def_vis:vis $type_def:ident => $parent:ident($parent_content:ident :: $parent_variant:ident) $(=> $ancestor:ty)*, + content: $content_type:ty, + kind: $kind_vis:vis $kind:ident, + type_name: $source_type_name:literal, + articled_display_name: $articled_display_name:literal, + dyn_impls: { + $($dyn_type:ty: impl $dyn_trait:ident { $($dyn_trait_impl:tt)* })* + }, + ) => { + #[derive(Copy, Clone)] + $type_def_vis struct $type_def; + + impl IsType for $type_def { + type Variant = HierarchicalTypeVariant; + + const SOURCE_TYPE_NAME: &'static str = $source_type_name; + const ARTICLED_DISPLAY_NAME: &'static str = $articled_display_name; + + fn type_kind() -> TypeKind { + TypeKind::Leaf(AnyValueLeafKind::from($kind)) + } + + // This is called for every leaf in a row as part of parsing + // Use inline(always) to avoid function call overhead, even in Debug builds + // which are used when macros are run during development + #[inline(always)] + fn type_kind_from_source_name(name: &str) -> Option { + if name == Self::SOURCE_TYPE_NAME { + Some(Self::type_kind()) + } else { + None + } + } + } + + impl IsHierarchicalType for $type_def { + type Content<'a, F: IsHierarchicalForm> = F::Leaf<'a, Self>; + type LeafKind = $kind; + + fn map_with<'a, F: IsHierarchicalForm, M: LeafMapper>( + mapper: M, + content: Self::Content<'a, F>, + ) -> M::Output<'a, Self> { + mapper.map_leaf::(content) + } + + fn map_ref_with<'r, 'a: 'r, F: IsHierarchicalForm, M: RefLeafMapper>( + mapper: M, + content: &'r Self::Content<'a, F>, + ) -> M::Output<'r, 'a, Self> { + mapper.map_leaf::(content) + } + + fn map_mut_with<'r, 'a: 'r, F: IsHierarchicalForm, M: MutLeafMapper>( + mapper: M, + content: &'r mut Self::Content<'a, F>, + ) -> M::Output<'r, 'a, Self> { + mapper.map_leaf::(content) + } + + fn content_to_leaf_kind( + _content: &Self::Content<'_, F>, + ) -> Self::LeafKind { + $kind + } + } + + impl IsLeafType for $type_def { + type Leaf = $content_type; + + fn leaf_kind() -> $kind { + $kind + } + } + + impl_type_feature_resolver! { + impl TypeFeatureResolver for $type_def: [ + $type_def $($dyn_type)* $parent $( $ancestor )* + ] + } + + #[derive(Clone, Copy, PartialEq, Eq)] + $kind_vis struct $kind; + + impl From<$kind> for AnyValueLeafKind { + fn from(kind: $kind) -> Self { + let as_parent_kind = <$parent as IsHierarchicalType>::LeafKind::$parent_variant(kind); + AnyValueLeafKind::from(as_parent_kind) + } + } + + impl IsLeafKind for $kind { + fn source_type_name(&self) -> &'static str { + $source_type_name + } + + fn articled_display_name(&self) -> &'static str { + $articled_display_name + } + + fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { + &$type_def + } + } + + impl HasLeafKind for $content_type { + type LeafKind = $kind; + + fn kind(&self) -> Self::LeafKind { + $kind + } + } + + impl_value_content_traits!(leaf: $type_def, $content_type); + + impl IsValueLeaf for $content_type {} + + $( + impl $dyn_trait for $content_type { + $($dyn_trait_impl)* + } + impl CastDyn for $content_type { + fn map_boxed(self: Box) -> Result, Box> { + Ok(self) + } + fn map_ref(&self) -> Result<&dyn $dyn_trait, &Self> { + Ok(self) + } + fn map_mut(&mut self) -> Result<&mut dyn $dyn_trait, &mut Self> { + Ok(self) + } + } + )* + impl_fallback_is_iterable!({$($dyn_trait)*} for $content_type); + + impl_ancestor_chain_conversions!( + $type_def => $parent($parent_content :: $parent_variant) $(=> $ancestor)* + ); + }; +} + +pub(crate) use define_leaf_type; + +pub(crate) struct DynMapper(std::marker::PhantomData); + +impl DynMapper { + pub(crate) const fn new() -> Self { + Self(std::marker::PhantomData) + } +} + +/// Implements `IsValueContent`, `IntoValueContent`, and `FromValueContent` for various +/// form wrappers of a content type. This macro deduplicates code across `define_leaf_type`, +/// `define_parent_type`, and `define_dyn_type`. +macro_rules! impl_value_content_traits { + // For leaf types - implements for all common form wrappers + (leaf: $type_def:ty, $content_type:ty) => { + // NB - we can't blanket implement this for all L: IsValueLeaf (unlike all the other forms) + // because it potentially conflicts with the &'a L and &'a mut L blanket implementations. + + // BeOwned: content is X + impl IsValueContent for $content_type { + type Type = $type_def; + type Form = BeOwned; + } + impl<'a> IntoValueContent<'a> for $content_type { + fn into_content(self) -> Self { + self + } + } + impl<'a> FromValueContent<'a> for $content_type { + fn from_content(content: Self) -> Self { + content + } + } + }; + + // For parent types - $content<'a, F> where F: IsHierarchicalForm + (parent: $type_def:ty, $content:ident) => { + impl<'a, F: IsHierarchicalForm> IsValueContent for $content<'a, F> { + type Type = $type_def; + type Form = F; + } + impl<'a, F: IsHierarchicalForm> IntoValueContent<'a> for $content<'a, F> { + fn into_content(self) -> Self { + self + } + } + impl<'a, F: IsHierarchicalForm> FromValueContent<'a> for $content<'a, F> { + fn from_content(content: Self) -> Self { + content + } + } + }; +} + +pub(crate) use impl_value_content_traits; + +macro_rules! define_dyn_type { + ( + $type_def_vis:vis $type_def:ident, + content: $dyn_type:ty, + dyn_kind: DynTypeKind::$dyn_kind:ident, + type_name: $source_type_name:literal, + articled_display_name: $articled_display_name:literal, + ) => { + #[derive(Copy, Clone)] + $type_def_vis struct $type_def; + + impl IsType for $type_def { + type Variant = DynTypeVariant; + + const SOURCE_TYPE_NAME: &'static str = $source_type_name; + const ARTICLED_DISPLAY_NAME: &'static str = $articled_display_name; + + fn type_kind() -> TypeKind { + TypeKind::Dyn(DynTypeKind::$dyn_kind) + } + + // This is called for every leaf in a row as part of parsing + // Use inline(always) to avoid function call overhead, even in Debug builds + // which are used when macros are run during development + #[inline(always)] + fn type_kind_from_source_name(name: &str) -> Option { + if name == Self::SOURCE_TYPE_NAME { + Some(Self::type_kind()) + } else { + None + } + } + } + + impl IsDynType for $type_def { + type DynContent = $dyn_type; + } + + impl_type_feature_resolver! { + impl TypeFeatureResolver for $type_def: [$type_def] + } + + impl IsDynLeaf for $dyn_type { + type Type = $type_def; + } + + impl IsArgument for Box<$dyn_type> { + type ValueType = $type_def; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument(Spanned(value, span_range): Spanned) -> ExecutionResult { + let form_mapped = BeOwned::from_argument_value(value)?; + <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") + } + } + + 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 { + let form_mapped = BeAnyRef::from_argument_value(value)?; + <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") + } + } + + 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 { + let form_mapped = BeAnyMut::from_argument_value(value)?; + <$type_def as DynResolveFrom>::resolve(form_mapped, span_range, "This argument") + } + } + + impl DynResolveFrom for $type_def + { + fn downcast_from<'a, F: IsHierarchicalForm + IsDynCompatibleForm>(content: Content<'a, T, F>) -> Result, Content<'a, T, F>> { + T::map_with::<'a, F, _>(DynMapper::<$dyn_type>::new(), content) + } + } + + impl LeafMapper for DynMapper<$dyn_type> { + type Output<'a, T: IsHierarchicalType> = Result, Content<'a, T, F>>; + + fn to_parent_output<'a, T: IsChildType>( + output: Self::Output<'a, T>, + ) -> Self::Output<'a, T::ParentType> { + match output { + Ok(dyn_content) => Ok(dyn_content), + Err(content) => Err(T::into_parent(content)), + } + } + + fn map_leaf<'a, T: IsLeafType>( + self, + leaf: F::Leaf<'a, T>, + ) -> Self::Output<'a, T> { + F::leaf_to_dyn(leaf) + } + } + }; +} + +pub(crate) use define_dyn_type; diff --git a/src/expressions/control_flow.rs b/src/expressions/control_flow.rs index e70eef6d..1080af25 100644 --- a/src/expressions/control_flow.rs +++ b/src/expressions/control_flow.rs @@ -126,7 +126,7 @@ impl Evaluate for IfExpression { } requested_ownership - .map_from_owned(Spanned(Value::None.into_owned(), self.span_range())) + .map_from_owned(Spanned(().into_any_value(), self.span_range())) .map(|spanned| spanned.0) } } @@ -203,7 +203,8 @@ impl Evaluate for WhileExpression { ExecutionOutcome::ControlFlow(control_flow_interrupt) => { match control_flow_interrupt { ControlFlowInterrupt::Break(break_interrupt) => { - return break_interrupt.into_value(self.span_range(), ownership); + return break_interrupt + .into_requested_value(self.span_range(), ownership); } ControlFlowInterrupt::Continue { .. } => { continue; @@ -285,7 +286,8 @@ impl Evaluate for LoopExpression { ExecutionOutcome::ControlFlow(control_flow_interrupt) => { match control_flow_interrupt { ControlFlowInterrupt::Break(break_interrupt) => { - return break_interrupt.into_value(self.span_range(), ownership); + return break_interrupt + .into_requested_value(self.span_range(), ownership); } ControlFlowInterrupt::Continue { .. } => { continue; @@ -371,7 +373,7 @@ impl Evaluate for ForExpression { let iterable: IterableValue = self .iterable .evaluate_owned(interpreter)? - .resolve_as("A for loop iterable")?; + .dyn_resolve::("A for loop iterable")?; let span = self.body.span(); let scope = interpreter.current_scope_id(); @@ -391,7 +393,8 @@ impl Evaluate for ForExpression { ExecutionOutcome::ControlFlow(control_flow_interrupt) => { match control_flow_interrupt { ControlFlowInterrupt::Break(break_interrupt) => { - return break_interrupt.into_value(self.span_range(), ownership); + return break_interrupt + .into_requested_value(self.span_range(), ownership); } ControlFlowInterrupt::Continue { .. } => { continue; diff --git a/src/expressions/equality.rs b/src/expressions/equality.rs new file mode 100644 index 00000000..4f3b78f2 --- /dev/null +++ b/src/expressions/equality.rs @@ -0,0 +1,578 @@ +use super::*; + +// ============================================================================ +// Equality Context - Controls behavior of value equality comparisons +// ============================================================================ + +/// A segment in the path to the current comparison location. +#[derive(Clone, Debug)] +pub(crate) enum PathSegment { + ArrayIndex(usize), + ObjectKey(String), + IteratorIndex(usize), + RangeStart, + RangeEnd, +} + +impl PathSegment { + fn fmt_path(path: &[PathSegment]) -> String { + let mut result = String::new(); + for segment in path { + match segment { + PathSegment::ArrayIndex(i) => result.push_str(&format!("[{}]", i)), + PathSegment::ObjectKey(k) => { + if syn::parse_str::(k).is_ok() { + result.push_str(&format!(".{}", k)) + } else { + 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"), + } + } + result + } +} + +/// Context trait for controlling equality comparison behavior. +/// +/// Different implementations allow for: +/// - Simple equality: returns `false` on type mismatch (like JS `===`) +/// - Typed equality: errors on type mismatch, tracks path for error messages +/// - Assert equality: errors on any difference, useful for assert_eq +pub(crate) trait EqualityContext { + /// The result type returned by equality comparisons. + type Result; + + /// Values are equal. + fn values_equal(&mut self) -> Self::Result; + + /// Values of the same type are not equal. + fn leaf_values_not_equal(&mut self, lhs: &T, rhs: &T) -> Self::Result; + + /// Values have different kinds. + fn kind_mismatch(&mut self, lhs: &L, rhs: &R) -> Self::Result; + + /// Range values have different structures. + fn range_structure_mismatch( + &mut self, + lhs: RangeStructure, + rhs: RangeStructure, + ) -> Self::Result; + + /// Arrays or iterators have different lengths. + fn lengths_unequal(&mut self, lhs_len: Option, rhs_len: Option) -> Self::Result; + + /// 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; + + /// Wrap a comparison within a range end context. + fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R; + + /// Returns true if the result indicates we should stop comparing and return early. + /// This is true when the result indicates "not equal" or an error occurred. + fn should_short_circuit(&self, result: &Self::Result) -> bool; +} + +/// Simple equality context - returns `false` on type mismatch, no path tracking. +/// This is the most efficient option when you just need a bool result. +pub(crate) struct SimpleEquality; + +impl EqualityContext for SimpleEquality { + type Result = bool; + + #[inline] + fn values_equal(&mut self) -> bool { + true + } + + #[inline] + fn leaf_values_not_equal(&mut self, _lhs: &T, _rhs: &T) -> bool { + false + } + + #[inline] + fn kind_mismatch(&mut self, _lhs: &L, _rhs: &R) -> bool { + false + } + + #[inline] + fn range_structure_mismatch(&mut self, _lhs: RangeStructure, _rhs: RangeStructure) -> bool { + false + } + + #[inline] + fn lengths_unequal(&mut self, _lhs_len: Option, _rhs_len: Option) -> bool { + false + } + + #[inline] + fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> bool { + false + } + + #[inline] + fn with_array_index(&mut self, _index: usize, f: impl FnOnce(&mut Self) -> R) -> R { + f(self) + } + + #[inline] + fn with_object_key(&mut self, _key: &str, f: impl FnOnce(&mut Self) -> R) -> R { + 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) + } + + #[inline] + fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + f(self) + } + + #[inline] + fn should_short_circuit(&self, result: &bool) -> bool { + !*result + } +} + +/// Typed equality context - errors on type mismatch, tracks path for error messages. +pub(crate) struct TypedEquality { + path: Vec, + error_span: SpanRange, +} + +impl TypedEquality { + pub(crate) fn new(error_span: SpanRange) -> Self { + Self { + path: Vec::new(), + error_span, + } + } +} + +impl EqualityContext for TypedEquality { + type Result = ExecutionResult; + + #[inline] + fn values_equal(&mut self) -> ExecutionResult { + Ok(true) + } + + #[inline] + fn leaf_values_not_equal(&mut self, _lhs: &T, _rhs: &T) -> ExecutionResult { + Ok(false) + } + + fn kind_mismatch( + &mut self, + lhs: &L, + rhs: &R, + ) -> ExecutionResult { + let path_str = PathSegment::fmt_path(&self.path); + Err(self.error_span.type_error(format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs.articled_kind(), + path_str, + rhs.articled_kind() + ))) + } + + fn range_structure_mismatch( + &mut self, + lhs: RangeStructure, + rhs: RangeStructure, + ) -> Self::Result { + let path_str = PathSegment::fmt_path(&self.path); + Err(self.error_span.type_error(format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs.articled_display_name(), + path_str, + rhs.articled_display_name() + ))) + } + + #[inline] + fn lengths_unequal( + &mut self, + _lhs_len: Option, + _rhs_len: Option, + ) -> ExecutionResult { + Ok(false) + } + + #[inline] + fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> ExecutionResult { + Ok(false) + } + + #[inline] + fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::ArrayIndex(index)); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::ObjectKey(key.to_string())); + let result = f(self); + self.path.pop(); + 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); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::RangeEnd); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn should_short_circuit(&self, result: &ExecutionResult) -> bool { + // Short-circuit on Ok(false) or Err(_) + !matches!(result, Ok(true)) + } +} + +// ============================================================================ +// Debug Equality - Captures detailed information about equality failures +// ============================================================================ + +/// Which side of the comparison is missing a key. +#[derive(Debug, Clone, Copy)] +pub(crate) enum MissingSide { + #[allow(dead_code)] + Lhs, + Rhs, +} + +/// The reason why two values were not equal. +#[derive(Clone)] +pub(crate) enum DebugInequalityReason { + /// Values of the same type have different values. + ValueMismatch { + lhs_display: String, + rhs_display: String, + }, + /// Values have incompatible value kinds. + ValueLeafKindMismatch { + lhs_kind: AnyValueLeafKind, + rhs_kind: AnyValueLeafKind, + }, + /// Ranges have incompatible structures. + RangeStructureMismatch { + lhs_structure: RangeStructure, + rhs_structure: RangeStructure, + }, + /// Collections have different lengths. + LengthMismatch { + lhs_len: Option, + rhs_len: Option, + }, + /// Object is missing a key on one side. + MissingKey { + key: String, + missing_on: MissingSide, + }, +} + +pub(crate) struct DebugEqualityError { + inner: Box, +} + +struct DebugEqualityErrorInner { + path: Vec, + reason: DebugInequalityReason, +} + +impl DebugEqualityError { + pub fn format_message(&self) -> String { + let inner = &self.inner; + let path_str = PathSegment::fmt_path(&inner.path); + + match &inner.reason { + DebugInequalityReason::ValueMismatch { + lhs_display, + rhs_display, + } => { + format!( + "lhs{} != rhs{}: {} != {}", + path_str, path_str, lhs_display, rhs_display + ) + } + DebugInequalityReason::ValueLeafKindMismatch { lhs_kind, rhs_kind } => { + format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs_kind.articled_display_name(), + path_str, + rhs_kind.articled_display_name() + ) + } + DebugInequalityReason::RangeStructureMismatch { + lhs_structure: lhs_kind, + rhs_structure: rhs_kind, + } => { + format!( + "lhs{} is {}, but rhs{} is {}", + path_str, + lhs_kind.articled_display_name(), + path_str, + rhs_kind.articled_display_name() + ) + } + DebugInequalityReason::LengthMismatch { lhs_len, rhs_len } => { + format!( + "lhs{} has length {}, but rhs{} has length {}", + path_str, + lhs_len.unwrap_or(0), + path_str, + rhs_len.unwrap_or(0) + ) + } + DebugInequalityReason::MissingKey { key, missing_on } => match missing_on { + MissingSide::Lhs => { + format!( + "lhs{} is missing key {:?}, compared to rhs{}", + path_str, key, path_str + ) + } + MissingSide::Rhs => { + format!( + "lhs{} has extra key {:?}, compared to rhs{}", + path_str, key, path_str + ) + } + }, + } + } +} + +impl From for DebugEqualityError { + fn from(inner: DebugEqualityErrorInner) -> Self { + Self { + inner: Box::new(inner), + } + } +} + +/// Debug equality context - captures detailed information about why values differ. +/// This is useful for assertion failures where you want to show exactly where +/// the mismatch occurred. +pub(crate) struct DebugEquality { + path: Vec, +} + +impl DebugEquality { + pub(crate) fn new() -> Self { + Self { path: Vec::new() } + } +} + +impl EqualityContext for DebugEquality { + type Result = Result<(), DebugEqualityError>; + + #[inline] + fn values_equal(&mut self) -> Result<(), DebugEqualityError> { + Ok(()) + } + + #[inline] + fn leaf_values_not_equal( + &mut self, + lhs: &T, + rhs: &T, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::ValueMismatch { + lhs_display: format!("{:?}", lhs), + rhs_display: format!("{:?}", rhs), + }, + })? + } + + fn kind_mismatch( + &mut self, + lhs: &L, + rhs: &R, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::ValueLeafKindMismatch { + lhs_kind: lhs.value_kind(), + rhs_kind: rhs.value_kind(), + }, + })? + } + + fn range_structure_mismatch( + &mut self, + lhs: RangeStructure, + rhs: RangeStructure, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::RangeStructureMismatch { + lhs_structure: lhs, + rhs_structure: rhs, + }, + })? + } + + #[inline] + fn lengths_unequal( + &mut self, + lhs_len: Option, + rhs_len: Option, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::LengthMismatch { lhs_len, rhs_len }, + })? + } + + #[inline] + fn missing_key( + &mut self, + key: &str, + missing_on: MissingSide, + ) -> Result<(), DebugEqualityError> { + Err(DebugEqualityErrorInner { + path: self.path.clone(), + reason: DebugInequalityReason::MissingKey { + key: key.to_string(), + missing_on, + }, + })? + } + + #[inline] + fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::ArrayIndex(index)); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::ObjectKey(key.to_string())); + let result = f(self); + self.path.pop(); + 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); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + self.path.push(PathSegment::RangeEnd); + let result = f(self); + self.path.pop(); + result + } + + #[inline] + fn should_short_circuit(&self, result: &Result<(), DebugEqualityError>) -> bool { + result.is_err() + } +} + +// ============================================================================ +// ValuesEqual trait - Value equality with configurable context +// ============================================================================ + +/// A trait for comparing values for equality with preinterpret semantics. +/// +/// This is NOT the same as Rust's `PartialEq`/`Eq` traits because: +/// - **Type coercion**: Untyped integers/floats can equal typed ones (e.g., `5 == 5u32`) +/// - **Structural comparison**: Arrays, objects, and iterators are compared element-wise +/// - **Token comparison**: Streams and unsupported literals compare via token string representation +/// - **Float semantics**: Floats use Rust's `==`, so `NaN != NaN` +/// +/// The comparison behavior is controlled by the `EqualityContext`: +/// - `SimpleEquality`: Returns `false` on type mismatch (like JS `===`) +/// - `TypedEquality`: Errors on type mismatch with path information +/// - `DebugEquality`: Returns detailed error info for assertion messages +pub(crate) trait ValuesEqual: Sized + HasLeafKind { + /// Compare two values for equality using the given context. + fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result; + + /// Lenient equality - returns `false` for incompatible types instead of erroring. + /// Behaves like JavaScript's `===` operator. + fn lenient_eq(&self, other: &Self) -> bool { + self.test_equality(other, &mut SimpleEquality) + } + + /// Strict equality check that errors on incompatible types. + fn typed_eq(&self, other: &Self, error_span: SpanRange) -> ExecutionResult { + self.test_equality(other, &mut TypedEquality::new(error_span)) + } + + /// Debug equality check - returns detailed information about why values differ. + /// Useful for generating informative assertion failure messages. + fn debug_eq(&self, other: &Self) -> Result<(), DebugEqualityError> { + self.test_equality(other, &mut DebugEquality::new()) + } +} diff --git a/src/expressions/evaluation/assignment_frames.rs b/src/expressions/evaluation/assignment_frames.rs index c9d8de9b..4d858b5c 100644 --- a/src/expressions/evaluation/assignment_frames.rs +++ b/src/expressions/evaluation/assignment_frames.rs @@ -28,14 +28,14 @@ impl AnyAssignmentFrame { struct PrivateUnit; pub(super) struct AssigneeAssigner { - value: Value, + value: AnyValue, } impl AssigneeAssigner { pub(super) fn start( context: AssignmentContext, assignee: ExpressionNodeId, - value: Value, + value: AnyValue, ) -> NextAction { let frame = Self { value }; context.request_assignee(frame, assignee, true) @@ -67,7 +67,7 @@ impl GroupedAssigner { pub(super) fn start( context: AssignmentContext, inner: ExpressionNodeId, - value: Value, + value: AnyValue, ) -> NextAction { let frame = Self(PrivateUnit); context.request_assignment(frame, inner, value) @@ -93,7 +93,7 @@ impl EvaluationFrame for GroupedAssigner { pub(super) struct ArrayBasedAssigner { span_range: SpanRange, - assignee_stack: Vec<(ExpressionNodeId, Value)>, + assignee_stack: Vec<(ExpressionNodeId, AnyValue)>, } impl ArrayBasedAssigner { @@ -102,7 +102,7 @@ impl ArrayBasedAssigner { nodes: &Arena, brackets: &Brackets, assignee_item_node_ids: &[ExpressionNodeId], - value: Value, + value: AnyValue, ) -> ExecutionResult { let frame = Self::new(nodes, brackets.join(), assignee_item_node_ids, value)?; Ok(frame.handle_next_subassignment(context)) @@ -113,11 +113,11 @@ impl ArrayBasedAssigner { nodes: &Arena, assignee_span: Span, assignee_item_node_ids: &[ExpressionNodeId], - value: Value, + value: AnyValue, ) -> ExecutionResult { let span_range = assignee_span.span_range(); - let array: ArrayValue = Spanned(value.into_owned(), span_range) - .resolve_as("The value destructured as an array")?; + let array: ArrayValue = + Spanned(value, span_range).resolve_as("The value destructured as an array")?; let mut has_seen_dot_dot = false; let mut prefix_assignees = Vec::new(); let mut suffix_assignees = Vec::new(); @@ -231,7 +231,7 @@ impl ObjectBasedAssigner { context: AssignmentContext, braces: &Braces, assignee_pairs: &[(ObjectKey, ExpressionNodeId)], - value: Value, + value: AnyValue, ) -> ExecutionResult { let frame = Box::new(Self::new(braces.join(), assignee_pairs, value)?); frame.handle_next_subassignment(context) @@ -240,11 +240,11 @@ impl ObjectBasedAssigner { fn new( assignee_span: Span, assignee_pairs: &[(ObjectKey, ExpressionNodeId)], - value: Value, + value: AnyValue, ) -> ExecutionResult { let span_range = assignee_span.span_range(); - let object: ObjectValue = Spanned(value.into_owned(), span_range) - .resolve_as("The value destructured as an object")?; + let object: ObjectValue = + Spanned(value, span_range).resolve_as("The value destructured as an object")?; Ok(Self { span_range, @@ -259,12 +259,12 @@ impl ObjectBasedAssigner { mut self: Box, context: AssignmentContext, access: IndexAccess, - index: &Value, + index: AnyValueRef, assignee_node: ExpressionNodeId, ) -> ExecutionResult { let key: &str = index .spanned(access.span_range()) - .resolve_as("An object key")?; + .downcast_resolve("An object key")?; let value = self.resolve_value(key.to_string(), access.span())?; Ok(context.request_assignment(self, assignee_node, value)) } @@ -292,7 +292,7 @@ impl ObjectBasedAssigner { }) } - fn resolve_value(&mut self, key: String, key_span: Span) -> ExecutionResult { + fn resolve_value(&mut self, key: String, key_span: Span) -> ExecutionResult { if self.already_used_keys.contains(&key) { return key_span.syntax_err(format!("The key `{}` has already used", key)); } @@ -300,7 +300,7 @@ impl ObjectBasedAssigner { .entries .remove(&key) .map(|entry| entry.value) - .unwrap_or_else(|| Value::None); + .unwrap_or_else(|| ().into_any_value()); self.already_used_keys.insert(key); Ok(value) } @@ -324,7 +324,7 @@ impl EvaluationFrame for Box { access, } => { let index_place = value.expect_shared(); - self.handle_index_value(context, access, index_place.as_ref(), assignee_node) + self.handle_index_value(context, access, index_place.as_ref_value(), assignee_node) } ObjectAssignmentState::WaitingForSubassignment => { let AssignmentCompletion = value.expect_assignment_completion(); diff --git a/src/expressions/evaluation/evaluator.rs b/src/expressions/evaluation/evaluator.rs index f3a21729..0348ec2d 100644 --- a/src/expressions/evaluation/evaluator.rs +++ b/src/expressions/evaluation/evaluator.rs @@ -125,7 +125,7 @@ enum NextActionInner { // This covers atomic assignments and composite assignments // (similar to patterns but for existing values/reassignments) // let a = ["x", "y"]; let b; [a[1], .. b] = [1, 2, 3, 4] - ReadNodeAsAssignmentTarget(ExpressionNodeId, Value), + ReadNodeAsAssignmentTarget(ExpressionNodeId, AnyValue), HandleReturnedValue(Spanned), } @@ -146,9 +146,9 @@ impl From for NextAction { pub(crate) enum RequestedValue { // RequestedOwnership::Concrete(_) // ------------------------------- - Owned(OwnedValue), - Shared(SharedValue), - Mutable(MutableValue), + Owned(AnyValueOwned), + Shared(AnyValueShared), + Mutable(AnyValueMutable), CopyOnWrite(CopyOnWriteValue), Assignee(AssigneeValue), @@ -162,21 +162,21 @@ pub(crate) enum RequestedValue { } impl RequestedValue { - pub(crate) fn expect_owned(self) -> OwnedValue { + pub(crate) fn expect_owned(self) -> AnyValueOwned { match self { RequestedValue::Owned(value) => value, _ => panic!("expect_owned() called on non-owned RequestedValue"), } } - pub(crate) fn expect_shared(self) -> SharedValue { + pub(crate) fn expect_shared(self) -> AnyValueShared { match self { RequestedValue::Shared(shared) => shared, _ => panic!("expect_shared() called on non-shared RequestedValue"), } } - pub(super) fn expect_assignee(self) -> AssigneeValue { + pub(super) fn expect_assignee(self) -> AnyValueAssignee { match self { RequestedValue::Assignee(assignee) => assignee, _ => panic!("expect_assignee() called on non-assignee RequestedValue"), @@ -212,11 +212,26 @@ impl RequestedValue { } } + /// Returns the leaf kind of the underlying value, for type resolution purposes. + pub(crate) fn value_kind(&self) -> AnyValueLeafKind { + match self { + RequestedValue::Owned(value) => value.value_kind(), + RequestedValue::Shared(shared) => shared.value_kind(), + RequestedValue::Mutable(mutable) => mutable.value_kind(), + RequestedValue::CopyOnWrite(cow) => cow.value_kind(), + RequestedValue::Assignee(assignee) => assignee.value_kind(), + RequestedValue::LateBound(late_bound) => late_bound.value_kind(), + RequestedValue::AssignmentCompletion(_) => { + panic!("value_kind() called on AssignmentCompletion") + } + } + } + pub(crate) fn expect_any_value_and_map( self, - map_shared: impl FnOnce(SharedValue) -> ExecutionResult, - map_mutable: impl FnOnce(MutableValue) -> ExecutionResult, - map_owned: impl FnOnce(OwnedValue) -> ExecutionResult, + map_shared: impl FnOnce(AnyValueShared) -> ExecutionResult, + map_mutable: impl FnOnce(AnyValueMutable) -> ExecutionResult, + map_owned: impl FnOnce(AnyValueOwned) -> ExecutionResult, ) -> ExecutionResult { Ok(match self { RequestedValue::LateBound(late_bound) => { @@ -241,17 +256,17 @@ impl RequestedValue { #[allow(unused)] impl Spanned { #[inline] - pub(crate) fn expect_owned(self) -> Spanned { + pub(crate) fn expect_owned(self) -> Spanned { self.map(|v| v.expect_owned()) } #[inline] - pub(crate) fn expect_shared(self) -> Spanned { + pub(crate) fn expect_shared(self) -> Spanned { self.map(|v| v.expect_shared()) } #[inline] - pub(crate) fn expect_assignee(self) -> Spanned { + pub(crate) fn expect_assignee(self) -> Spanned { self.map(|v| v.expect_assignee()) } @@ -372,7 +387,7 @@ impl<'a, T: RequestedValueType> Context<'a, T> { self, handler: H, node: ExpressionNodeId, - value: Value, + value: AnyValue, ) -> NextAction { self.stack .handlers diff --git a/src/expressions/evaluation/node_conversion.rs b/src/expressions/evaluation/node_conversion.rs index 1600014c..8ceb680d 100644 --- a/src/expressions/evaluation/node_conversion.rs +++ b/src/expressions/evaluation/node_conversion.rs @@ -134,7 +134,7 @@ impl ExpressionNode { // NB: This might intrisically be a part of a larger value, and might have been // created many lines previously, so doesn't have an obvious span associated with it // Instead, we put errors on the assignee syntax side - value: Value, + value: AnyValue, ) -> ExecutionResult { Ok(match self { ExpressionNode::Leaf(Leaf::Discarded(underscore)) => { diff --git a/src/expressions/evaluation/value_frames.rs b/src/expressions/evaluation/value_frames.rs index 09cb43df..c0debd2a 100644 --- a/src/expressions/evaluation/value_frames.rs +++ b/src/expressions/evaluation/value_frames.rs @@ -6,15 +6,15 @@ use super::*; /// It is typically paired with an [`ArgumentOwnership`] (maybe wrapped in a [`RequestedOwnership`]) /// which indicates what ownership type to resolve to. pub(crate) enum ArgumentValue { - Owned(OwnedValue), + Owned(AnyValueOwned), CopyOnWrite(CopyOnWriteValue), - Mutable(MutableValue), - Assignee(AssigneeValue), - Shared(SharedValue), + Mutable(AnyValueMutable), + Assignee(AnyValueAssignee), + Shared(AnyValueShared), } impl ArgumentValue { - pub(crate) fn expect_owned(self) -> OwnedValue { + pub(crate) fn expect_owned(self) -> AnyValueOwned { match self { ArgumentValue::Owned(value) => value, _ => panic!("expect_owned() called on a non-owned ArgumentValue"), @@ -28,21 +28,21 @@ impl ArgumentValue { } } - pub(crate) fn expect_mutable(self) -> MutableValue { + pub(crate) fn expect_mutable(self) -> AnyValueMutable { match self { ArgumentValue::Mutable(value) => value, _ => panic!("expect_mutable() called on a non-mutable ArgumentValue"), } } - pub(crate) fn expect_assignee(self) -> AssigneeValue { + pub(crate) fn expect_assignee(self) -> AnyValueAssignee { match self { ArgumentValue::Assignee(value) => value, _ => panic!("expect_assignee() called on a non-assignee ArgumentValue"), } } - pub(crate) fn expect_shared(self) -> SharedValue { + pub(crate) fn expect_shared(self) -> AnyValueShared { match self { ArgumentValue::Shared(value) => value, _ => panic!("expect_shared() called on a non-shared ArgumentValue"), @@ -52,22 +52,22 @@ impl ArgumentValue { impl Spanned { #[inline] - pub(crate) fn expect_owned(self) -> Spanned { + pub(crate) fn expect_owned(self) -> Spanned { self.map(|value| value.expect_owned()) } #[inline] - pub(crate) fn expect_mutable(self) -> Spanned { + pub(crate) fn expect_mutable(self) -> Spanned { self.map(|value| value.expect_mutable()) } #[inline] - pub(crate) fn expect_assignee(self) -> Spanned { + pub(crate) fn expect_assignee(self) -> Spanned { self.map(|value| value.expect_assignee()) } #[inline] - pub(crate) fn expect_shared(self) -> Spanned { + pub(crate) fn expect_shared(self) -> Spanned { self.map(|value| value.expect_shared()) } } @@ -106,7 +106,7 @@ impl Spanned<&mut ArgumentValue> { } impl Deref for ArgumentValue { - type Target = Value; + type Target = AnyValue; fn deref(&self) -> &Self::Target { self.as_ref() @@ -119,8 +119,8 @@ impl AsMut for ArgumentValue { } } -impl AsRef for ArgumentValue { - fn as_ref(&self) -> &Value { +impl AsRef for ArgumentValue { + fn as_ref(&self) -> &AnyValue { match self { ArgumentValue::Owned(owned) => owned, ArgumentValue::Mutable(mutable) => mutable, @@ -169,7 +169,7 @@ impl RequestedOwnership { } pub(crate) fn map_none(self, span: SpanRange) -> ExecutionResult> { - self.map_from_owned(Spanned(().into_owned_value(), span)) + self.map_from_owned(Spanned(().into_any_value(), span)) } pub(crate) fn map_from_late_bound( @@ -240,7 +240,7 @@ impl RequestedOwnership { pub(crate) fn map_from_owned( &self, - Spanned(value, span): Spanned, + Spanned(value, span): Spanned, ) -> ExecutionResult> { Ok(Spanned( match self { @@ -277,7 +277,7 @@ impl RequestedOwnership { pub(crate) fn map_from_mutable( &self, - Spanned(mutable, span): Spanned, + Spanned(mutable, span): Spanned, ) -> ExecutionResult> { Ok(Spanned( match self { @@ -409,7 +409,7 @@ impl ArgumentOwnership { ) -> ExecutionResult { match self { ArgumentOwnership::Owned => Ok(ArgumentValue::Owned( - copy_on_write.into_owned_transparently(span)?, + copy_on_write.clone_to_owned_transparently(span)?, )), ArgumentOwnership::Shared => Ok(ArgumentValue::Shared(copy_on_write.into_shared())), ArgumentOwnership::Mutable => { @@ -417,7 +417,7 @@ impl ArgumentOwnership { span.ownership_err("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()` to get a mutable reference to a cloned value.") } else { Ok(ArgumentValue::Mutable(Mutable::new_from_owned( - copy_on_write.into_owned_transparently(span)?, + copy_on_write.clone_to_owned_transparently(span)?, ))) } } @@ -466,7 +466,7 @@ impl ArgumentOwnership { pub(crate) fn map_from_mutable( &self, - spanned_mutable: Spanned, + spanned_mutable: Spanned, ) -> ExecutionResult { self.map_from_mutable_inner(spanned_mutable, false) } @@ -480,7 +480,7 @@ impl ArgumentOwnership { fn map_from_mutable_inner( &self, - Spanned(mutable, span): Spanned, + Spanned(mutable, span): Spanned, is_late_bound: bool, ) -> ExecutionResult { match self { @@ -506,14 +506,14 @@ impl ArgumentOwnership { pub(crate) fn map_from_owned( &self, - owned: Spanned, + owned: Spanned, ) -> ExecutionResult { self.map_from_owned_with_is_last_use(owned, false) } fn map_from_owned_with_is_last_use( &self, - Spanned(owned, span): Spanned, + Spanned(owned, span): Spanned, is_from_last_use: bool, ) -> ExecutionResult { match self { @@ -609,7 +609,7 @@ impl EvaluationFrame for GroupBuilder { pub(super) struct ArrayBuilder { span: Span, unevaluated_items: Vec, - evaluated_items: Vec, + evaluated_items: Vec, } impl ArrayBuilder { @@ -655,7 +655,7 @@ impl EvaluationFrame for ArrayBuilder { Spanned(value, _span): Spanned, ) -> ExecutionResult { let value = value.expect_owned(); - self.evaluated_items.push(value.into_inner()); + self.evaluated_items.push(value); self.next(context) } } @@ -750,7 +750,7 @@ impl EvaluationFrame for Box { context.request_owned(self, value_node) } Some(PendingEntryPath::OnValueBranch { key, key_span }) => { - let value = value.expect_owned().into_inner(); + let value = value.expect_owned(); let entry = ObjectEntry { key_span, value }; self.evaluated_entries.insert(key, entry); self.next(context)? @@ -794,7 +794,10 @@ impl EvaluationFrame for UnaryOperationBuilder { let operand_kind = operand.kind(); // Try method resolution first - if let Some(interface) = operand_kind.resolve_unary_operation(&self.operation) { + if let Some(interface) = operand_kind + .feature_resolver() + .resolve_unary_operation(&self.operation) + { let operand_span = operand.span_range(); let resolved_value = operand.resolve(interface.argument_ownership())?; let result = @@ -802,9 +805,9 @@ impl EvaluationFrame for UnaryOperationBuilder { return context.return_returned_value(result); } self.operation.type_err(format!( - "The {} operator is not supported for {} values", - self.operation.symbolic_description(), - operand.value_type(), + "The {} operator is not supported for {}", + self.operation, + operand.articled_kind(), )) } } @@ -866,7 +869,10 @@ impl EvaluationFrame for BinaryOperationBuilder { .return_returned_value(Spanned(ReturnedValue::Owned(result), left_span))? } else { // Try method resolution based on left operand's kind and resolve left operand immediately - let interface = left.kind().resolve_binary_operation(&self.operation); + let interface = left + .kind() + .feature_resolver() + .resolve_binary_operation(&self.operation); match interface { Some(interface) => { @@ -888,7 +894,7 @@ impl EvaluationFrame for BinaryOperationBuilder { return self.operation.type_err(format!( "The {} operator is not supported for {} operand", self.operation.symbolic_description(), - left.articled_value_type(), + left.articled_kind(), )); } } @@ -952,12 +958,29 @@ impl EvaluationFrame for ValuePropertyAccessBuilder { context: ValueContext, Spanned(value, source_span): Spanned, ) -> ExecutionResult { + // Get the source kind to check if property access is supported + let source_kind = value.value_kind(); + + // Query type resolution for property access capability + let Some(interface) = source_kind.feature_resolver().resolve_property_access() else { + return self.access.type_err(format!( + "Cannot access properties on {}", + source_kind.articled_display_name() + )); + }; + + let ctx = PropertyAccessCallContext { + property: &self.access, + }; let auto_create = context.requested_ownership().requests_auto_create(); + + // Execute the access via the interface let mapped = value.expect_any_value_and_map( - |shared| shared.try_map(|value| value.property_ref(&self.access)), - |mutable| mutable.try_map(|value| value.property_mut(&self.access, auto_create)), - |owned| owned.try_map(|value| value.into_property(&self.access)), + |shared| shared.try_map(|value| (interface.shared_access)(ctx, value)), + |mutable| mutable.try_map(|value| (interface.mutable_access)(ctx, value, auto_create)), + |owned| (interface.owned_access)(ctx, owned), )?; + // The result span covers source through property let result_span = SpanRange::new_between(source_span, self.access.span_range()); context.return_not_necessarily_matching_requested(Spanned(mapped, result_span)) @@ -970,8 +993,13 @@ pub(super) struct ValueIndexAccessBuilder { } enum IndexPath { - OnSourceBranch { index: ExpressionNodeId }, - OnIndexBranch { source: Spanned }, + OnSourceBranch { + index: ExpressionNodeId, + }, + OnIndexBranch { + source: Spanned, + interface: IndexAccessInterface, + }, } impl ValueIndexAccessBuilder { @@ -1009,27 +1037,48 @@ impl EvaluationFrame for ValueIndexAccessBuilder { ) -> ExecutionResult { Ok(match self.state { IndexPath::OnSourceBranch { index } => { + // Get the source kind to check if index access is supported + let source_kind = value.value_kind(); + + // Query type resolution for index access capability + let Some(interface) = source_kind.feature_resolver().resolve_index_access() else { + return self.access.type_err(format!( + "Cannot index into {}", + source_kind.articled_display_name() + )); + }; + + // Store the interface for the next phase + let index_ownership = interface.index_ownership; self.state = IndexPath::OnIndexBranch { source: Spanned(value, span), + interface, }; - // This is a value, so we are _accessing it_ and can't create values - // (that's only possible in a place!) - therefore we don't need an owned key, - // and can use &index for reading values from our array - context.request_shared(self, index) + + // Request the index with ownership specified by the interface + context.request_argument_value(self, index, index_ownership) } IndexPath::OnIndexBranch { source: Spanned(source, source_span), + interface, } => { let index = value.expect_shared(); - let index = index.as_ref().spanned(span); + let index = index.as_ref_value().spanned(span); + let ctx = IndexAccessCallContext { + access: &self.access, + }; let auto_create = context.requested_ownership().requests_auto_create(); + + // Execute the access via the interface let result = source.expect_any_value_and_map( - |shared| shared.try_map(|value| value.index_ref(self.access, index)), + |shared| shared.try_map(|value| (interface.shared_access)(ctx, value, index)), |mutable| { - mutable.try_map(|value| value.index_mut(self.access, index, auto_create)) + mutable.try_map(|value| { + (interface.mutable_access)(ctx, value, index, auto_create) + }) }, - |owned| owned.try_map(|value| value.into_indexed(self.access, index)), + |owned| (interface.owned_access)(ctx, owned, index), )?; let result_span = SpanRange::new_between(source_span, self.access.span_range()); context.return_not_necessarily_matching_requested(Spanned(result, result_span))? @@ -1045,7 +1094,7 @@ pub(super) struct RangeBuilder { enum RangePath { OnLeftBranch { right: Option }, - OnRightBranch { left: Option }, + OnRightBranch { left: Option }, } impl RangeBuilder { @@ -1097,8 +1146,7 @@ impl EvaluationFrame for RangeBuilder { context: ValueContext, Spanned(value, _span): Spanned, ) -> ExecutionResult { - // TODO[range-refactor]: Change to not always clone the value - let value = value.expect_owned().into_inner(); + let value = value.expect_owned(); Ok(match (self.state, self.range_limits) { (RangePath::OnLeftBranch { right: Some(right) }, _) => { self.state = RangePath::OnRightBranch { left: Some(value) }; @@ -1188,7 +1236,7 @@ impl EvaluationFrame for AssignmentBuilder { ) -> ExecutionResult { Ok(match self.state { AssignmentPath::OnValueBranch { assignee } => { - let value = value.expect_owned().into_inner(); + let value = value.expect_owned(); self.state = AssignmentPath::OnAwaitingAssignment; context.request_assignment(self, assignee, value) } @@ -1256,6 +1304,7 @@ impl EvaluationFrame for MethodCallBuilder { let caller = value.expect_late_bound(); let method = caller .kind() + .feature_resolver() .resolve_method(self.method.method.to_string().as_str()); let method = match method { Some(m) => m, @@ -1263,7 +1312,7 @@ impl EvaluationFrame for MethodCallBuilder { return self.method.method.type_err(format!( "The method {} does not exist on {}", self.method.method, - caller.articled_value_type(), + caller.articled_kind(), )) } }; diff --git a/src/expressions/expression.rs b/src/expressions/expression.rs index 23da418c..3912aa90 100644 --- a/src/expressions/expression.rs +++ b/src/expressions/expression.rs @@ -41,7 +41,7 @@ impl Expression { pub(crate) fn evaluate_owned( &self, interpreter: &mut Interpreter, - ) -> ExecutionResult> { + ) -> ExecutionResult> { Ok(self .evaluate(interpreter, RequestedOwnership::owned())? .expect_owned()) diff --git a/src/expressions/expression_block.rs b/src/expressions/expression_block.rs index 9b4b8a9f..cacdfcca 100644 --- a/src/expressions/expression_block.rs +++ b/src/expressions/expression_block.rs @@ -32,7 +32,7 @@ impl HasSpanRange for EmbeddedExpression { impl Interpret for EmbeddedExpression { fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { let value = self.content.evaluate_shared(interpreter)?; - value.output_to( + value.as_ref_value().output_to( Grouping::Flattened, &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), ) @@ -75,7 +75,7 @@ impl Interpret for EmbeddedStatements { .evaluate_spanned(interpreter, self.span_range(), RequestedOwnership::shared())? .0 .expect_shared(); - value.output_to( + value.as_ref_value().output_to( Grouping::Flattened, &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), ) @@ -160,7 +160,7 @@ impl Evaluate for ExpressionBlock { match interpreter.catch_control_flow(output_result, *catch_location, scope)? { ExecutionOutcome::Value(Spanned(value, _)) => value, ExecutionOutcome::ControlFlow(ControlFlowInterrupt::Break(break_interrupt)) => { - break_interrupt.into_value(self.span_range(), ownership)? + break_interrupt.into_requested_value(self.span_range(), ownership)? } ExecutionOutcome::ControlFlow(_) => { unreachable!("Only break control flow should be catchable by labeled blocks") @@ -225,7 +225,7 @@ impl ScopedBlock { pub(crate) fn evaluate_owned( &self, interpreter: &mut Interpreter, - ) -> ExecutionResult> { + ) -> ExecutionResult> { let span_range = self.span().span_range(); self.evaluate(interpreter, RequestedOwnership::owned()) .map(|value| Spanned(value.expect_owned(), span_range)) @@ -271,7 +271,7 @@ impl UnscopedBlock { pub(crate) fn evaluate_owned( &self, interpreter: &mut Interpreter, - ) -> ExecutionResult> { + ) -> ExecutionResult> { let span_range = self.span().span_range(); self.evaluate(interpreter, RequestedOwnership::owned()) .map(|value| Spanned(value.expect_owned(), span_range)) @@ -332,6 +332,6 @@ impl ExpressionBlockContent { statement.evaluate_as_statement(interpreter)?; } } - ownership.map_from_owned(Spanned(Owned(Value::None), output_span)) + ownership.map_from_owned(Spanned(().into_any_value(), output_span)) } } diff --git a/src/expressions/expression_parsing.rs b/src/expressions/expression_parsing.rs index edc0f062..2fc27fdf 100644 --- a/src/expressions/expression_parsing.rs +++ b/src/expressions/expression_parsing.rs @@ -121,7 +121,7 @@ impl<'a> ExpressionParser<'a> { "true" | "false" => { let bool = input.parse::()?; UnaryAtom::Leaf(Leaf::Value(Spanned( - SharedValue::new_from_owned(BooleanValue::for_litbool(&bool).into_owned_value()), + SharedValue::new_from_owned(bool.value.into_any_value()), bool.span.span_range(), ))) } @@ -134,7 +134,7 @@ impl<'a> ExpressionParser<'a> { "None" => { let none_ident = input.parse_any_ident()?; // consume the "None" token UnaryAtom::Leaf(Leaf::Value(Spanned( - SharedValue::new_from_owned(Value::None.into_owned_value()), + SharedValue::new_from_owned(().into_any_value()), none_ident.span().span_range(), ))) } @@ -153,7 +153,7 @@ impl<'a> ExpressionParser<'a> { let lit: syn::Lit = input.parse()?; let span_range = lit.span().span_range(); UnaryAtom::Leaf(Leaf::Value(Spanned( - SharedValue::new_from_owned(Value::for_syn_lit(lit)), + SharedValue::new_from_owned(AnyValue::for_syn_lit(lit)), span_range, ))) }, diff --git a/src/expressions/mod.rs b/src/expressions/mod.rs index ff71f979..e24690e4 100644 --- a/src/expressions/mod.rs +++ b/src/expressions/mod.rs @@ -1,4 +1,10 @@ +// Marked as use for expression sub-modules to use with a `use super::*` statement +use crate::internal_prelude::*; +use expression_parsing::*; + +mod concepts; mod control_flow; +mod equality; mod evaluation; mod expression; mod expression_block; @@ -10,7 +16,10 @@ mod statements; mod type_resolution; mod values; +#[allow(unused_imports)] // Whilst we're building it out +pub(crate) use concepts::*; pub(crate) use control_flow::*; +pub(crate) use equality::*; pub(crate) use evaluation::*; pub(crate) use expression::*; pub(crate) use expression_block::*; @@ -20,7 +29,3 @@ pub(crate) use patterns::*; pub(crate) use statements::*; pub(crate) use type_resolution::*; pub(crate) use values::*; - -// Marked as use for expression sub-modules to use with a `use super::*` statement -use crate::internal_prelude::*; -use expression_parsing::*; diff --git a/src/expressions/operations.rs b/src/expressions/operations.rs index 7927ce0c..271198be 100644 --- a/src/expressions/operations.rs +++ b/src/expressions/operations.rs @@ -1,3 +1,5 @@ +use std::fmt::{Display, Formatter}; + use super::*; pub(crate) trait Operation: HasSpanRange { @@ -59,10 +61,8 @@ impl UnaryOperation { as_token: Token![as], target_ident: Ident, ) -> ParseResult { - let target = Type::from_ident(&target_ident)?; - let target = CastTarget::from_source_type(target).ok_or_else(|| { - target_ident.parse_error("This type is not supported in cast expressions") - })?; + let target = TypeIdent::from_ident(&target_ident)?; + let target = CastTarget::from_source_type(target)?; Ok(Self::Cast { as_token, @@ -82,18 +82,22 @@ impl UnaryOperation { operand_span_range } - pub(super) fn evaluate( + pub(super) fn evaluate( &self, - Spanned(input, input_span): Spanned>, + Spanned(input, input_span): Spanned, ) -> ExecutionResult> { - let input = input.into_owned_value(); - let method = input.kind().resolve_unary_operation(self).ok_or_else(|| { - self.type_error(format!( - "The {} operator is not supported for {} operand", - self.symbolic_description(), - input.articled_value_type(), - )) - })?; + let input = input.into_any_value(); + let method = input + .kind() + .feature_resolver() + .resolve_unary_operation(self) + .ok_or_else(|| { + self.type_error(format!( + "The {} operator is not supported for {} operand", + self, + input.articled_kind(), + )) + })?; let input = method .argument_ownership .map_from_owned(Spanned(input, input_span))?; @@ -102,62 +106,43 @@ impl UnaryOperation { } #[derive(Copy, Clone)] -pub(crate) enum CastTarget { - Integer(IntegerKind), - Float(FloatKind), - Boolean, - String, - Char, - Stream, -} +pub(crate) struct CastTarget(pub(crate) AnyValueLeafKind); impl CastTarget { - fn from_source_type(s: Type) -> Option { - Some(match s.kind { - TypeKind::Integer => CastTarget::Integer(IntegerKind::Untyped), - TypeKind::SpecificInteger(kind) => CastTarget::Integer(kind), - TypeKind::Float => CastTarget::Float(FloatKind::Untyped), - TypeKind::SpecificFloat(kind) => CastTarget::Float(kind), - TypeKind::Boolean => CastTarget::Boolean, - TypeKind::String => CastTarget::String, - TypeKind::Char => CastTarget::Char, - TypeKind::Stream => CastTarget::Stream, - _ => return None, - }) + fn from_source_type(s: TypeIdent) -> ParseResult { + match s.kind { + TypeKind::Parent(ParentTypeKind::Integer(_)) => s.parse_err(format!( + "This type is not supported in cast expressions. Perhaps you want 'as {}'?", + UntypedIntegerKind.source_type_name() + )), + TypeKind::Parent(ParentTypeKind::Float(_)) => s.parse_err(format!( + "This type is not supported in cast expressions. Perhaps you want 'as {}'?", + UntypedFloatKind.source_type_name() + )), + TypeKind::Leaf(leaf_kind) => Ok(CastTarget(leaf_kind)), + _ => s.parse_err("This type is not supported in cast expressions"), + } } - fn symbolic_description(&self) -> &'static str { - match self { - CastTarget::Integer(IntegerKind::Untyped) => "as int", - CastTarget::Integer(IntegerKind::U8) => "as u8", - CastTarget::Integer(IntegerKind::U16) => "as u16", - CastTarget::Integer(IntegerKind::U32) => "as u32", - CastTarget::Integer(IntegerKind::U64) => "as u64", - CastTarget::Integer(IntegerKind::U128) => "as u128", - CastTarget::Integer(IntegerKind::Usize) => "as usize", - CastTarget::Integer(IntegerKind::I8) => "as i8", - CastTarget::Integer(IntegerKind::I16) => "as i16", - CastTarget::Integer(IntegerKind::I32) => "as i32", - CastTarget::Integer(IntegerKind::I64) => "as i64", - CastTarget::Integer(IntegerKind::I128) => "as i128", - CastTarget::Integer(IntegerKind::Isize) => "as isize", - CastTarget::Float(FloatKind::Untyped) => "as float", - CastTarget::Float(FloatKind::F32) => "as f32", - CastTarget::Float(FloatKind::F64) => "as f64", - CastTarget::Boolean => "as bool", - CastTarget::String => "as string", - CastTarget::Char => "as char", - CastTarget::Stream => "as stream", - } + /// Denotes that this cast target for a singleton iterable + /// (e.g. array or stream) + pub(crate) fn is_singleton_target(&self) -> bool { + matches!( + self.0, + AnyValueLeafKind::Bool(_) + | AnyValueLeafKind::Char(_) + | AnyValueLeafKind::Integer(_) + | AnyValueLeafKind::Float(_) + ) } } -impl Operation for UnaryOperation { - fn symbolic_description(&self) -> &'static str { +impl Display for UnaryOperation { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - UnaryOperation::Neg { .. } => "-", - UnaryOperation::Not { .. } => "!", - UnaryOperation::Cast { target, .. } => target.symbolic_description(), + UnaryOperation::Neg { .. } => write!(f, "-"), + UnaryOperation::Not { .. } => write!(f, "!"), + UnaryOperation::Cast { target, .. } => write!(f, "as {}", target.0.source_type_name()), } } } @@ -292,13 +277,13 @@ impl SynParse for BinaryOperation { impl BinaryOperation { pub(super) fn lazy_evaluate( &self, - left: Spanned<&Value>, - ) -> ExecutionResult> { + left: Spanned<&AnyValue>, + ) -> ExecutionResult> { match self { BinaryOperation::LogicalAnd { .. } => { let bool: Spanned<&bool> = left.resolve_as("The left operand to &&")?; if !**bool { - Ok(Some((*bool).into_owned_value())) + Ok(Some((*bool).into_any_value())) } else { Ok(None) } @@ -306,7 +291,7 @@ impl BinaryOperation { BinaryOperation::LogicalOr { .. } => { let bool: Spanned<&bool> = left.resolve_as("The left operand to ||")?; if **bool { - Ok(Some((*bool).into_owned_value())) + Ok(Some((*bool).into_any_value())) } else { Ok(None) } @@ -316,14 +301,18 @@ impl BinaryOperation { } #[allow(unused)] - pub(crate) fn evaluate( + pub(crate) fn evaluate( &self, - Spanned(left, left_span): Spanned>, - Spanned(right, right_span): Spanned>, + Spanned(left, left_span): Spanned, + Spanned(right, right_span): Spanned, ) -> ExecutionResult> { - let left = left.into_owned_value(); - let right = right.into_owned_value(); - match left.kind().resolve_binary_operation(self) { + let left = left.into_any_value(); + let right = right.into_any_value(); + match left + .kind() + .feature_resolver() + .resolve_binary_operation(self) + { Some(interface) => { let left = interface .lhs_ownership @@ -336,7 +325,7 @@ impl BinaryOperation { None => self.type_err(format!( "The {} operator is not supported for {} operand", self.symbolic_description(), - left.articled_value_type(), + left.articled_kind(), )), } } @@ -441,35 +430,35 @@ pub(super) trait HandleBinaryOperation: Sized + std::fmt::Display + Copy { fn paired_operation>( self, - rhs: impl ResolveAs, + rhs: impl ResolveAs>, context: BinaryOperationCallContext, perform_fn: fn(Self, Self) -> Option, ) -> ExecutionResult { let lhs = self; let rhs = rhs.resolve_as("This operand")?; - perform_fn(lhs, rhs) + perform_fn(lhs, rhs.0) .map(|r| r.into()) - .ok_or_else(|| Self::binary_overflow_error(context, lhs, rhs)) + .ok_or_else(|| Self::binary_overflow_error(context, lhs, rhs.0)) } fn paired_operation_no_overflow>( self, - rhs: impl ResolveAs, + rhs: impl ResolveAs>, perform_fn: fn(Self, Self) -> Self, ) -> ExecutionResult { let lhs = self; let rhs = rhs.resolve_as("This operand")?; - Ok(perform_fn(lhs, rhs).into()) + Ok(perform_fn(lhs, rhs.0).into()) } fn paired_comparison( self, - rhs: impl ResolveAs, + rhs: impl ResolveAs>, compare_fn: fn(Self, Self) -> bool, ) -> ExecutionResult { let lhs = self; let rhs = rhs.resolve_as("This operand")?; - Ok(compare_fn(lhs, rhs)) + Ok(compare_fn(lhs, rhs.0)) } fn shift_operation>( diff --git a/src/expressions/patterns.rs b/src/expressions/patterns.rs index 3937bfde..1f07033c 100644 --- a/src/expressions/patterns.rs +++ b/src/expressions/patterns.rs @@ -4,7 +4,7 @@ pub(crate) trait HandleDestructure { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()>; } @@ -68,7 +68,7 @@ impl HandleDestructure for Pattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { match self { Pattern::Variable(variable) => variable.handle_destructure(interpreter, value), @@ -111,9 +111,9 @@ impl HandleDestructure for ArrayPattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { - let array: ArrayValue = Spanned(value.into_owned(), self.brackets.span_range()) + let array: ArrayValue = Spanned(value, self.brackets.span_range()) .resolve_as("The value destructured with an array pattern")?; let mut has_seen_dot_dot = false; let mut prefix_assignees = Vec::new(); @@ -226,9 +226,9 @@ impl HandleDestructure for ObjectPattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { - let object: ObjectValue = Spanned(value.into_owned(), self.braces.span_range()) + let object: ObjectValue = Spanned(value, self.braces.span_range()) .resolve_as("The value destructured with an object pattern")?; let mut value_map = object.entries; let mut already_used_keys = HashSet::with_capacity(self.entries.len()); @@ -253,7 +253,7 @@ impl HandleDestructure for ObjectPattern { let value = value_map .remove(&key) .map(|entry| entry.value) - .unwrap_or_else(|| Value::None); + .unwrap_or_else(|| ().into_any_value()); already_used_keys.insert(key); pattern.handle_destructure(interpreter, value)?; } @@ -352,13 +352,11 @@ impl HandleDestructure for StreamPattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { - let stream: StreamValue = Spanned(value.into_owned(), self.brackets.span_range()) - .resolve_as("The value destructured with a stream pattern")?; - interpreter.start_parse(stream.value, |interpreter, _| { - self.content.consume(interpreter) - }) + let stream = Spanned(value, self.brackets.span_range()) + .downcast_resolve("The value destructured with a stream pattern")?; + interpreter.start_parse(stream, |interpreter, _| self.content.consume(interpreter)) } } @@ -396,11 +394,11 @@ impl HandleDestructure for ParseTemplatePattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { - let stream: StreamValue = Spanned(value.into_owned(), self.brackets.span_range()) - .resolve_as("The value destructured with a parse template pattern")?; - interpreter.start_parse(stream.value, |interpreter, handle| { + let stream = Spanned(value, self.brackets.span_range()) + .downcast_resolve("The value destructured with a parse template pattern")?; + interpreter.start_parse(stream, |interpreter, handle| { self.parser_definition.define(interpreter, handle); self.content.consume(interpreter) }) diff --git a/src/expressions/statements.rs b/src/expressions/statements.rs index dcdafc5f..e0d4fca3 100644 --- a/src/expressions/statements.rs +++ b/src/expressions/statements.rs @@ -144,8 +144,8 @@ impl LetStatement { assignment, } = self; let value = match assignment { - Some(assignment) => assignment.expression.evaluate_owned(interpreter)?.0 .0, - None => Value::None, + Some(assignment) => assignment.expression.evaluate_owned(interpreter)?.0, + None => ().into_any_value(), }; pattern.handle_destructure(interpreter, value)?; Ok(()) @@ -369,7 +369,7 @@ impl EmitStatement { interpreter: &mut Interpreter, ) -> ExecutionResult<()> { let Spanned(value, span) = self.expression.evaluate_owned(interpreter)?; - value.output_to( + value.as_ref_value().output_to( Grouping::Flattened, &mut ToStreamContext::new(interpreter.output(&self.emit)?, span), ) diff --git a/src/expressions/type_resolution/arguments.rs b/src/expressions/type_resolution/arguments.rs index 7ac2babd..4e272095 100644 --- a/src/expressions/type_resolution/arguments.rs +++ b/src/expressions/type_resolution/arguments.rs @@ -2,6 +2,9 @@ // TODO[unused-clearup] use super::*; +/// Used for resolution of integers and floats from either typed or untyped literals. +pub(crate) struct OptionalSuffix(pub(crate) T); + pub(crate) struct ResolutionContext<'a> { span_range: &'a SpanRange, resolution_target: &'a str, @@ -26,7 +29,7 @@ impl<'a> ResolutionContext<'a> { } /// Create an error for the resolution context. - pub(crate) fn err( + pub(crate) fn err( &self, articled_expected_value_kind: &str, value: V, @@ -35,19 +38,19 @@ impl<'a> ResolutionContext<'a> { "{} is expected to be {}, but it is {}", self.resolution_target, articled_expected_value_kind, - value.articled_value_type() + value.articled_kind() )) } } pub(crate) trait IsArgument: Sized { - type ValueType: HierarchicalTypeData; + type ValueType: TypeData; const OWNERSHIP: ArgumentOwnership; fn from_argument(value: Spanned) -> ExecutionResult; } impl IsArgument for ArgumentValue { - type ValueType = ValueTypeData; + type ValueType = AnyType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::AsIs; fn from_argument(Spanned(value, _): Spanned) -> ExecutionResult { @@ -55,7 +58,7 @@ impl IsArgument for ArgumentValue { } } -impl + ResolvableArgumentTarget + ?Sized> IsArgument for Shared { +impl + ResolvableArgumentTarget + ?Sized> IsArgument for Shared { type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; @@ -64,19 +67,21 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgument } } -impl IsArgument for AnyRef<'static, T> -where - Shared: IsArgument, -{ - type ValueType = as IsArgument>::ValueType; - const OWNERSHIP: ArgumentOwnership = as IsArgument>::OWNERSHIP; +// impl IsArgument for AnyRef<'static, T> +// where +// Shared: IsArgument, +// { +// type ValueType = as IsArgument>::ValueType; +// const OWNERSHIP: ArgumentOwnership = as IsArgument>::OWNERSHIP; - fn from_argument(argument: Spanned) -> ExecutionResult { - Ok(Shared::::from_argument(argument)?.into()) - } -} +// fn from_argument(argument: Spanned) -> ExecutionResult { +// Ok(Shared::::from_argument(argument)?.into()) +// } +// } -impl + ResolvableArgumentTarget + ?Sized> IsArgument for Assignee { +impl + ResolvableArgumentTarget + ?Sized> IsArgument + for Assignee +{ type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Assignee { auto_create: false }; @@ -85,7 +90,7 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgument } } -impl + ResolvableArgumentTarget + ?Sized> IsArgument for Mutable { +impl + ResolvableArgumentTarget + ?Sized> IsArgument for Mutable { type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; @@ -94,39 +99,31 @@ impl + ResolvableArgumentTarget + ?Sized> IsArgument } } -impl IsArgument for AnyRefMut<'static, T> -where - Mutable: IsArgument, -{ - type ValueType = as IsArgument>::ValueType; - const OWNERSHIP: ArgumentOwnership = as IsArgument>::OWNERSHIP; +// impl IsArgument for AnyMut<'static, T> +// where +// Mutable: IsArgument, +// { +// type ValueType = as IsArgument>::ValueType; +// const OWNERSHIP: ArgumentOwnership = as IsArgument>::OWNERSHIP; - fn from_argument(argument: Spanned) -> ExecutionResult { - Ok(Mutable::::from_argument(argument)?.into()) - } -} +// fn from_argument(argument: Spanned) -> ExecutionResult { +// Ok(Mutable::::from_argument(argument)?.into()) +// } +// } -impl + ResolvableArgumentTarget> IsArgument for Owned { - type ValueType = T::ValueType; - const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - - fn from_argument(argument: Spanned) -> ExecutionResult { - T::resolve_owned(argument.expect_owned(), "This argument") - } -} +// impl + ResolvableArgumentTarget> IsArgument for T { +// type ValueType = T::ValueType; +// const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; -impl + ResolvableArgumentTarget> IsArgument for T { - type ValueType = T::ValueType; - const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; +// fn from_argument(argument: Spanned) -> ExecutionResult { +// T::resolve_value(argument.expect_owned(), "This argument") +// } +// } - fn from_argument(argument: Spanned) -> ExecutionResult { - T::resolve_value(argument.expect_owned(), "This argument") - } -} - -impl + ResolvableArgumentTarget + ToOwned> IsArgument for CopyOnWrite +impl + ResolvableArgumentTarget + ToOwned> IsArgument + for CopyOnWrite where - T::Owned: ResolvableOwned, + T::Owned: ResolvableOwned, { type ValueType = T::ValueType; const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::CopyOnWrite; @@ -135,7 +132,7 @@ where value.expect_copy_on_write().map( |v| T::resolve_shared(v.spanned(span), "This argument"), |v| { - >::resolve_owned( + >::resolve_value( v.spanned(span), "This argument", ) @@ -159,53 +156,51 @@ pub(crate) trait ResolveAs { fn resolve_as(self, resolution_target: &str) -> ExecutionResult; } -impl, V> ResolveAs for Spanned> { - fn resolve_as(self, resolution_target: &str) -> ExecutionResult { - T::resolve_value(self, resolution_target) - } -} - // 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> { - T::resolve_owned(self, resolution_target) +impl> ResolveAs for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult { + T::resolve_value(self, resolution_target) } } -impl + ?Sized> ResolveAs> for Spanned> { +impl + ?Sized> ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_shared(self, resolution_target) } } -impl<'a, T: ResolvableShared + ?Sized> ResolveAs<&'a T> for Spanned<&'a Value> { +impl<'a, T: ResolvableShared + ?Sized> ResolveAs<&'a T> for Spanned<&'a AnyValue> { fn resolve_as(self, resolution_target: &str) -> ExecutionResult<&'a T> { T::resolve_ref(self, resolution_target) } } -impl<'a, T: ResolvableShared + ?Sized> ResolveAs> for Spanned<&'a Value> { +impl<'a, T: ResolvableShared + ?Sized> ResolveAs> + for Spanned<&'a AnyValue> +{ fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_spanned_ref(self, resolution_target) } } -impl + ?Sized> ResolveAs> for Spanned> { +impl + ?Sized> ResolveAs> for Spanned { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_mutable(self, resolution_target) } } -impl<'a, T: ResolvableMutable + ?Sized> ResolveAs<&'a mut T> for Spanned<&'a mut Value> { +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> { T::resolve_ref_mut(self, resolution_target) } } -impl<'a, T: ResolvableMutable + ?Sized> ResolveAs> - for Spanned<&'a mut Value> +impl<'a, T: ResolvableMutable + ?Sized> ResolveAs> + for Spanned<&'a mut AnyValue> { fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { T::resolve_spanned_ref_mut(self, resolution_target) @@ -213,49 +208,30 @@ impl<'a, T: ResolvableMutable + ?Sized> ResolveAs> } pub(crate) trait ResolvableArgumentTarget { - type ValueType: HierarchicalTypeData; + type ValueType: TypeData; } pub(crate) trait ResolvableOwned: Sized { fn resolve_from_value(value: T, context: ResolutionContext) -> ExecutionResult; - fn resolve_spanned_owned_from_value( + fn resolve_spanned_from_value( value: T, context: ResolutionContext, ) -> ExecutionResult>> { let span_range = context.span_range; - Self::resolve_from_value(value, context).map(|v| Owned(v).spanned(*span_range)) - } - - fn resolve_owned_from_value( - value: T, - context: ResolutionContext, - ) -> ExecutionResult> { - Self::resolve_from_value(value, context).map(Owned::new) + Self::resolve_from_value(value, context).map(|x| x.spanned(*span_range)) } /// The `resolution_target` should be capitalized, e.g. "This argument" or "The value destructed with an object pattern" fn resolve_value( - Spanned(value, span): Spanned>, + Spanned(value, span): Spanned, resolution_target: &str, ) -> ExecutionResult { let context = ResolutionContext { span_range: &span, resolution_target, }; - Self::resolve_from_value(value.into_inner(), context) - } - - /// The `resolution_target` should be capitalized, e.g. "This argument" or "The value destructed with an object pattern" - fn resolve_owned( - Spanned(value, span): Spanned>, - resolution_target: &str, - ) -> ExecutionResult> { - let context = ResolutionContext { - span_range: &span, - resolution_target, - }; - Self::resolve_from_value(value.into_inner(), context).map(Owned::new) + Self::resolve_from_value(value, context) } } @@ -367,26 +343,26 @@ pub(crate) trait ResolvableMutable { } } -impl ResolvableArgumentTarget for Value { - type ValueType = ValueTypeData; +impl ResolvableArgumentTarget for AnyValue { + type ValueType = AnyType; } -impl ResolvableOwned for Value { - fn resolve_from_value(value: Value, _context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for AnyValue { + fn resolve_from_value(value: AnyValue, _context: ResolutionContext) -> ExecutionResult { Ok(value) } } -impl ResolvableShared for Value { +impl ResolvableShared for AnyValue { fn resolve_from_ref<'a>( - value: &'a Value, + value: &'a AnyValue, _context: ResolutionContext, ) -> ExecutionResult<&'a Self> { Ok(value) } } -impl ResolvableMutable for Value { +impl ResolvableMutable for AnyValue { fn resolve_from_mut<'a>( - value: &'a mut Value, + value: &'a mut AnyValue, _context: ResolutionContext, ) -> ExecutionResult<&'a mut Self> { Ok(value) @@ -399,27 +375,27 @@ macro_rules! impl_resolvable_argument_for { type ValueType = $value_type; } - impl ResolvableOwned for $type { + impl ResolvableOwned for $type { fn resolve_from_value( - $value: Value, + $value: AnyValue, $context: ResolutionContext, ) -> ExecutionResult { $body } } - impl ResolvableShared for $type { + impl ResolvableShared for $type { fn resolve_from_ref<'a>( - $value: &'a Value, + $value: &'a AnyValue, $context: ResolutionContext, ) -> ExecutionResult<&'a Self> { $body } } - impl ResolvableMutable for $type { + impl ResolvableMutable for $type { fn resolve_from_mut<'a>( - $value: &'a mut Value, + $value: &'a mut AnyValue, $context: ResolutionContext, ) -> ExecutionResult<&'a mut Self> { $body diff --git a/src/expressions/type_resolution/interface_macros.rs b/src/expressions/type_resolution/interface_macros.rs index 69e0b3ca..0786469f 100644 --- a/src/expressions/type_resolution/interface_macros.rs +++ b/src/expressions/type_resolution/interface_macros.rs @@ -13,6 +13,7 @@ macro_rules! if_empty { }; } +#[cfg(test)] macro_rules! handle_first_arg_type { ([NEXT] : $type:ty) => { $type @@ -110,6 +111,9 @@ macro_rules! generate_method_interface { ], } }; + (REQUIRED $req:tt OPTIONAL $opt:tt ARGS[$($arg:tt)*]) => { + compile_error!(stringify!("This method arity is currently unsupported - add support in `MethodInterface` and `generate_method_interface`: ", $($arg)*)); + }; } macro_rules! parse_arg_types { @@ -123,12 +127,18 @@ macro_rules! parse_arg_types { parse_arg_types!([REQUIRED $req OPTIONAL[$($opt)* $type,] => $callback! $callback_args] $($rest)*) }; // Next tokens are `: X)` - we have a required argument (variant 1) + ([REQUIRED [$($req:tt)*] OPTIONAL [] => $callback:ident! $callback_args:tt] : $type:ty) => { + parse_arg_types!([REQUIRED[$($req)* $type,] OPTIONAL [] => $callback! $callback_args]) + }; ([REQUIRED [$($req:tt)*] OPTIONAL $opt:tt => $callback:ident! $callback_args:tt] : $type:ty) => { - parse_arg_types!([REQUIRED[$($req)* $type,] OPTIONAL $opt => $callback! $callback_args]) + compile_error!(stringify!("Required arguments must come before optional arguments:" $type)); }; // Next tokens are `: X, ...` - we have a required argument (variant 2) + ([REQUIRED [$($req:tt)*] OPTIONAL [] => $callback:ident! $callback_args:tt] : $type:ty, $($rest:tt)*) => { + parse_arg_types!([REQUIRED[$($req)* $type,] OPTIONAL [] => $callback! $callback_args] $($rest)*) + }; ([REQUIRED [$($req:tt)*] OPTIONAL $opt:tt => $callback:ident! $callback_args:tt] : $type:ty, $($rest:tt)*) => { - parse_arg_types!([REQUIRED[$($req)* $type,] OPTIONAL $opt => $callback! $callback_args] $($rest)*) + compile_error!(stringify!("Required arguments must come before optional arguments:" $type)); }; // Next tokens are something else - ignore it and look at next ([REQUIRED $req:tt OPTIONAL $opt:tt => $callback:ident! $callback_args:tt] $consumed:tt $($rest:tt)*) => { @@ -298,6 +308,100 @@ where f(context, A::from_argument(lhs)?, B::from_argument(rhs)?).to_returned_value() } +// ============================================================================ +// Property Access Wrapper Functions +// ============================================================================ + +pub(crate) fn apply_property_shared<'a, S: ResolvableShared + ?Sized + 'a>( + f: for<'b> fn(PropertyAccessCallContext, &'b S) -> ExecutionResult<&'b AnyValue>, + ctx: PropertyAccessCallContext, + source: &'a AnyValue, +) -> ExecutionResult<&'a AnyValue> { + let source = S::resolve_from_ref( + source, + ResolutionContext::new(&ctx.property.span_range(), "The property access source"), + )?; + f(ctx, source) +} + +pub(crate) fn apply_property_mutable<'a, S: ResolvableMutable + ?Sized + 'a>( + f: for<'b> fn(PropertyAccessCallContext, &'b mut S, bool) -> ExecutionResult<&'b mut AnyValue>, + ctx: PropertyAccessCallContext, + source: &'a mut AnyValue, + auto_create: bool, +) -> ExecutionResult<&'a mut AnyValue> { + let source = S::resolve_from_mut( + source, + ResolutionContext::new(&ctx.property.span_range(), "The property access source"), + )?; + f(ctx, source, auto_create) +} + +pub(crate) fn apply_property_owned>( + f: fn(PropertyAccessCallContext, S) -> ExecutionResult, + ctx: PropertyAccessCallContext, + source: AnyValue, +) -> ExecutionResult { + let source = S::resolve_from_value( + source, + ResolutionContext::new(&ctx.property.span_range(), "The property access source"), + )?; + f(ctx, source) +} + +// ============================================================================ +// Index Access Wrapper Functions +// ============================================================================ + +pub(crate) fn apply_index_shared<'a, S: ResolvableShared + ?Sized + 'a>( + f: for<'b> fn( + IndexAccessCallContext, + &'b S, + Spanned, + ) -> ExecutionResult<&'b AnyValue>, + ctx: IndexAccessCallContext, + source: &'a AnyValue, + index: Spanned, +) -> ExecutionResult<&'a AnyValue> { + let source = S::resolve_from_ref( + source, + ResolutionContext::new(&ctx.access.span_range(), "The index access source"), + )?; + f(ctx, source, index) +} + +pub(crate) fn apply_index_mutable<'a, S: ResolvableMutable + ?Sized + 'a>( + f: for<'b> fn( + IndexAccessCallContext, + &'b mut S, + Spanned, + bool, + ) -> ExecutionResult<&'b mut AnyValue>, + ctx: IndexAccessCallContext, + source: &'a mut AnyValue, + index: Spanned, + auto_create: bool, +) -> ExecutionResult<&'a mut AnyValue> { + let source = S::resolve_from_mut( + source, + ResolutionContext::new(&ctx.access.span_range(), "The index access source"), + )?; + f(ctx, source, index, auto_create) +} + +pub(crate) fn apply_index_owned>( + f: fn(IndexAccessCallContext, S, Spanned) -> ExecutionResult, + ctx: IndexAccessCallContext, + source: AnyValue, + index: Spanned, +) -> ExecutionResult { + let source = S::resolve_from_value( + source, + ResolutionContext::new(&ctx.access.span_range(), "The index access source"), + )?; + f(ctx, source, index) +} + pub(crate) struct MethodCallContext<'a> { pub interpreter: &'a mut Interpreter, pub output_span_range: SpanRange, @@ -330,10 +434,9 @@ impl<'a> BinaryOperationCallContext<'a> { } } -macro_rules! define_interface { +macro_rules! define_type_features { ( - struct $type_data:ident, - parent: $parent_type_data:ident, + impl $type_def:ident, $mod_vis:vis mod $mod_name:ident { $mod_methods_vis:vis mod methods { $( @@ -350,54 +453,55 @@ macro_rules! define_interface { $([$binary_context:ident])? fn $binary_name:ident($($binary_args:tt)*) $(-> $binary_output_ty:ty)? $([ignore_type_assertion $binary_ignore_type_assertion:tt])? $binary_body:block )* } + $(property_access($property_source_ty:ty) { + $([$property_shared_context:ident])? fn shared($($property_shared_args:tt)*) $property_shared_body:block + $([$property_mutable_context:ident])? fn mutable($($property_mutable_args:tt)*) $property_mutable_body:block + $([$property_owned_context:ident])? fn owned($($property_owned_args:tt)*) $property_owned_body:block + })? + $(index_access($index_source_ty:ty) { + $([$index_shared_context:ident])? fn shared($($index_shared_args:tt)*) $index_shared_body:block + $([$index_mutable_context:ident])? fn mutable($($index_mutable_args:tt)*) $index_mutable_body:block + $([$index_owned_context:ident])? fn owned($($index_owned_args:tt)*) $index_owned_body:block + })? interface_items { $($items:item)* } } ) => { - #[derive(Clone, Copy)] - pub(crate) struct $type_data; - $mod_vis mod $mod_name { use super::*; - $mod_vis const fn parent() -> Option<$parent_type_data> { - // Type ids aren't const, and strings aren't const-comparable, but I can use this work-around: - // https://internals.rust-lang.org/t/why-i-cannot-compare-two-static-str-s-in-a-const-context/17726/2 - const OWN_TYPE_NAME: &'static [u8] = stringify!($type_data).as_bytes(); - const PARENT_TYPE_NAME: &'static [u8] = stringify!($parent_type_data).as_bytes(); - match PARENT_TYPE_NAME { - OWN_TYPE_NAME => None, - _ => Some($parent_type_data), - } - } - #[allow(unused)] + #[cfg(test)] fn asserts() { + fn assert_first_argument>() {} + fn assert_output_type() {} $( - $type_data::assert_first_argument::(); + assert_first_argument::(); if_exists! { {$($method_ignore_type_assertion)?} {} - {$($type_data::assert_output_type::<$method_output_ty>();)?} + {$(assert_output_type::<$method_output_ty>();)?} } )* $( - $type_data::assert_first_argument::(); + assert_first_argument::(); if_exists! { {$($unary_ignore_type_assertion)?} {} - {$($type_data::assert_output_type::<$unary_output_ty>();)?} + {$(assert_output_type::<$unary_output_ty>();)?} } )* $( - $type_data::assert_first_argument::(); + assert_first_argument::(); if_exists! { {$($binary_ignore_type_assertion)?} {} - {$($type_data::assert_output_type::<$binary_output_ty>();)?} + {$(assert_output_type::<$binary_output_ty>();)?} } )* + // Note: property_access and index_access source types are verified + // at compile time through the apply_* wrapper functions } $mod_methods_vis mod methods { @@ -460,10 +564,50 @@ macro_rules! define_interface { )* } - impl HierarchicalTypeData for $type_data { - type Parent = $parent_type_data; - const PARENT: Option = $mod_name::parent(); + $( + pub(crate) mod property_access { + #[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 mutable<'a>(if_empty!([$($property_mutable_context)?][_ctx]): PropertyAccessCallContext, $($property_mutable_args)*) -> ExecutionResult<&'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 property_access_interface() -> PropertyAccessInterface { + PropertyAccessInterface { + shared_access: |ctx, source| apply_property_shared::<$property_source_ty>(property_access::shared, ctx, source), + mutable_access: |ctx, source, auto_create| apply_property_mutable::<$property_source_ty>(property_access::mutable, ctx, source, auto_create), + owned_access: |ctx, source| apply_property_owned::<$property_source_ty>(property_access::owned, ctx, source), + } + } + )? + + $( + pub(crate) mod index_access { + #[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 mutable<'a>(if_empty!([$($index_mutable_context)?][_ctx]): IndexAccessCallContext, $($index_mutable_args)*) -> ExecutionResult<&'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 index_access_interface() -> IndexAccessInterface { + IndexAccessInterface { + index_ownership: ArgumentOwnership::Shared, + shared_access: |ctx, source, index| apply_index_shared::<$index_source_ty>(index_access::shared, ctx, source, index), + mutable_access: |ctx, source, index, auto_create| apply_index_mutable::<$index_source_ty>(index_access::mutable, ctx, source, index, auto_create), + owned_access: |ctx, source, index| apply_index_owned::<$index_source_ty>(index_access::owned, ctx, source, index), + } + } + )? + + impl TypeData for $type_def { #[allow(unreachable_code)] fn resolve_own_method(method_name: &str) -> Option { Some(match method_name { @@ -474,15 +618,36 @@ macro_rules! define_interface { }) } + define_type_features!(@property_access_impl $($property_source_ty)?); + define_type_features!(@index_access_impl $($index_source_ty)?); + // Pass through resolve_own_unary_operation and resolve_own_binary_operation // until there's a better way to define them $($items)* } } - } + }; + + // Helper rules for generating resolve_own_property_access when property_access is defined + (@property_access_impl $source_ty:ty) => { + fn resolve_own_property_access() -> Option { + Some(property_access_interface()) + } + }; + (@property_access_impl) => {}; + + // Helper rules for generating resolve_own_index_access when index_access is defined + (@index_access_impl $source_ty:ty) => { + fn resolve_own_index_access() -> Option { + Some(index_access_interface()) + } + }; + (@index_access_impl) => {}; } +#[cfg(test)] +pub(crate) use handle_first_arg_type; pub(crate) use { - define_interface, generate_binary_interface, generate_method_interface, - generate_unary_interface, handle_first_arg_type, if_empty, ignore_all, parse_arg_types, + define_type_features, generate_binary_interface, generate_method_interface, + generate_unary_interface, if_empty, ignore_all, parse_arg_types, }; diff --git a/src/expressions/type_resolution/mod.rs b/src/expressions/type_resolution/mod.rs index f8e22e6c..3e86854f 100644 --- a/src/expressions/type_resolution/mod.rs +++ b/src/expressions/type_resolution/mod.rs @@ -4,10 +4,10 @@ mod arguments; mod interface_macros; mod outputs; mod type_data; -mod value_kinds; +mod type_kinds; pub(crate) use arguments::*; pub(crate) use interface_macros::*; pub(crate) use outputs::*; pub(crate) use type_data::*; -pub(crate) use value_kinds::*; +pub(crate) use type_kinds::*; diff --git a/src/expressions/type_resolution/outputs.rs b/src/expressions/type_resolution/outputs.rs index aa97a515..cd823619 100644 --- a/src/expressions/type_resolution/outputs.rs +++ b/src/expressions/type_resolution/outputs.rs @@ -5,10 +5,10 @@ use super::*; /// /// See also [`RequestedValue`] for values that have been fully evaluated. pub(crate) enum ReturnedValue { - Owned(OwnedValue), + Owned(AnyValueOwned), CopyOnWrite(CopyOnWriteValue), - Mutable(MutableValue), - Shared(SharedValue), + Mutable(AnyValueMutable), + Shared(AnyValueShared), } // TODO: Find some way to selectively enable only on MSRV (e.g. following the build.rs feature flag pattern) @@ -26,30 +26,18 @@ impl IsReturnable for ReturnedValue { } } -impl IsReturnable for Shared { +impl IsReturnable for AnyValueShared { fn to_returned_value(self) -> ExecutionResult { Ok(ReturnedValue::Shared(self)) } } -impl IsReturnable for Mutable { +impl IsReturnable for AnyValueMutable { fn to_returned_value(self) -> ExecutionResult { Ok(ReturnedValue::Mutable(self)) } } -impl IsReturnable for T { - fn to_returned_value(self) -> ExecutionResult { - Ok(ReturnedValue::Owned(Owned(self.into_value()))) - } -} - -impl IsReturnable for Owned { - fn to_returned_value(self) -> ExecutionResult { - Ok(ReturnedValue::Owned(self.map(|f| f.into_value()))) - } -} - impl IsReturnable for ExecutionResult { fn to_returned_value(self) -> ExecutionResult { self?.to_returned_value() diff --git a/src/expressions/type_resolution/type_data.rs b/src/expressions/type_resolution/type_data.rs index 3cd8d070..c7877739 100644 --- a/src/expressions/type_resolution/type_data.rs +++ b/src/expressions/type_resolution/type_data.rs @@ -1,7 +1,7 @@ #![allow(clippy::type_complexity)] use super::*; -pub(in crate::expressions) trait MethodResolver { +pub(crate) trait TypeFeatureResolver { /// Resolves a unary operation as a method interface for this type. fn resolve_method(&self, method_name: &str) -> Option; @@ -18,69 +18,56 @@ pub(in crate::expressions) trait MethodResolver { ) -> Option; /// Resolves a property of this type. - fn resolve_type_property(&self, _property_name: &str) -> Option; -} - -impl MethodResolver for T { - fn resolve_method(&self, method_name: &str) -> Option { - match Self::resolve_own_method(method_name) { - Some(method) => Some(method), - None => Self::PARENT.and_then(|p| p.resolve_method(method_name)), - } - } - - fn resolve_unary_operation( - &self, - operation: &UnaryOperation, - ) -> Option { - match Self::resolve_own_unary_operation(operation) { - Some(method) => Some(method), - None => Self::PARENT.and_then(|p| p.resolve_unary_operation(operation)), - } - } + fn resolve_type_property(&self, _property_name: &str) -> Option; - fn resolve_binary_operation( - &self, - operation: &BinaryOperation, - ) -> Option { - match Self::resolve_own_binary_operation(operation) { - Some(method) => Some(method), - None => Self::PARENT.and_then(|p| p.resolve_binary_operation(operation)), - } + /// Resolves property access capability for this type (e.g., `obj.field`). + /// Returns Some if this type supports property access, None otherwise. + fn resolve_property_access(&self) -> Option { + None } - fn resolve_type_property(&self, property_name: &str) -> Option { - ::resolve_type_property(property_name) + /// Resolves index access capability for this type (e.g., `arr[0]`). + /// Returns Some if this type supports indexing, None otherwise. + fn resolve_index_access(&self) -> Option { + None } } -pub(crate) trait HierarchicalTypeData { - type Parent: HierarchicalTypeData; - const PARENT: Option; - - fn assert_first_argument>() {} - - fn assert_output_type() {} - +pub(crate) trait TypeData { + /// Returns None if the method is not supported on this type itself. + /// The method may still be supported on a type further up the resolution chain. fn resolve_own_method(_method_name: &str) -> Option { None } - /// Resolves a unary operation as a method interface for this type. - /// Returns None if the operation should fallback to the legacy system. + /// Returns None if the operation is not supported on this type itself. + /// The operation may still be supported on a type further up the resolution chain. fn resolve_own_unary_operation(_operation: &UnaryOperation) -> Option { None } - /// Resolves a binary operation as a method interface for this type. - /// Returns None if the operation is not supported by this type. + /// Returns None if the operation is not supported on this type itself. + /// The operation may still be supported on a type further up the resolution chain. fn resolve_own_binary_operation( _operation: &BinaryOperation, ) -> Option { None } - fn resolve_type_property(_property_name: &str) -> Option { + /// Returns a property on the type. + /// Properties are *not* currently resolved up the resolution chain... but maybe they should be? + /// ... similarly, maybe functions should be too, when they are added? + fn resolve_type_property(_property_name: &str) -> Option { + None + } + + /// Returns the property access interface for this type, if supported. + fn resolve_own_property_access() -> Option { + None + } + + /// Returns the index access interface for this type, if supported. + fn resolve_own_index_access() -> Option { None } } @@ -327,3 +314,67 @@ impl BinaryOperationInterface { self.rhs_ownership } } + +// ============================================================================ +// Property Access Interface +// ============================================================================ + +/// Context provided to property access methods. +#[derive(Clone, Copy)] +pub(crate) struct PropertyAccessCallContext<'a> { + pub property: &'a PropertyAccess, +} + +/// Interface for property access on a type (e.g., `obj.field`). +/// +/// Unlike unary/binary operations which return owned values, property access +/// returns references into the source value. This requires three separate +/// access methods for shared, mutable, and owned access patterns. +pub(crate) struct PropertyAccessInterface { + /// Access a property by shared reference. + pub shared_access: + for<'a> fn(PropertyAccessCallContext, &'a AnyValue) -> ExecutionResult<&'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>, + /// Extract a property from an owned value. + pub owned_access: fn(PropertyAccessCallContext, AnyValue) -> ExecutionResult, +} + +// ============================================================================ +// Index Access Interface +// ============================================================================ + +/// Context provided to index access methods. +#[derive(Clone, Copy)] +pub(crate) struct IndexAccessCallContext<'a> { + pub access: &'a IndexAccess, +} + +/// Interface for index access on a type (e.g., `arr[0]` or `obj["key"]`). +/// +/// Similar to property access, but the index is an evaluated expression +/// rather than a static identifier. +pub(crate) struct IndexAccessInterface { + /// The ownership requirement for the index value. + pub index_ownership: ArgumentOwnership, + /// Access an element by shared reference. + pub shared_access: for<'a> fn( + IndexAccessCallContext, + &'a AnyValue, + Spanned, + ) -> ExecutionResult<&'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>, + /// Extract an element from an owned value. + pub owned_access: + fn(IndexAccessCallContext, AnyValue, Spanned) -> ExecutionResult, +} diff --git a/src/expressions/type_resolution/type_kinds.rs b/src/expressions/type_resolution/type_kinds.rs new file mode 100644 index 00000000..7c27e743 --- /dev/null +++ b/src/expressions/type_resolution/type_kinds.rs @@ -0,0 +1,260 @@ +use super::*; + +/// A trait for specific value kinds that can provide a display name. +/// This is implemented by `ValueLeafKind`, `IntegerKind`, `FloatKind`, etc. +pub(crate) trait IsLeafKind: Copy + Into { + fn source_type_name(&self) -> &'static str; + fn articled_display_name(&self) -> &'static str; + fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver; +} + +impl AnyValueLeafKind { + /// This should be true for types which users expect to have value + /// semantics, but false for types which are expensive to clone or + /// are expected to have reference semantics. + /// + /// This indicates if an &x can be converted to an x via cloning + /// when doing method resolution. + pub(crate) fn supports_transparent_cloning(&self) -> bool { + match self { + AnyValueLeafKind::None(_) => true, + AnyValueLeafKind::Integer(_) => true, + AnyValueLeafKind::Float(_) => true, + AnyValueLeafKind::Bool(_) => true, + // Strings are value-like, so it makes sense to transparently clone them + AnyValueLeafKind::String(_) => true, + AnyValueLeafKind::Char(_) => true, + AnyValueLeafKind::UnsupportedLiteral(_) => false, + AnyValueLeafKind::Array(_) => false, + AnyValueLeafKind::Object(_) => false, + AnyValueLeafKind::Stream(_) => false, + AnyValueLeafKind::Range(_) => true, + AnyValueLeafKind::Iterator(_) => false, + // A parser is a handle, so can be cloned transparently. + // It may fail to be able to be used to parse if the underlying stream is out of scope of course. + AnyValueLeafKind::Parser(_) => true, + } + } +} + +// A AnyValueLeafKind represents a kind of leaf value. +// But a TypeKind represents a type in the hierarchy, which points at a type data. +pub(crate) enum TypeKind { + Leaf(AnyValueLeafKind), + Parent(ParentTypeKind), + Dyn(DynTypeKind), +} + +impl TypeKind { + /// This should be true for types which users expect to have value + /// semantics, but false for types which are expensive to clone or + /// are expected to have reference semantics. + /// + /// This indicates if an &x can be converted to an x via cloning + /// when doing method resolution. + pub(crate) fn supports_transparent_cloning(&self) -> bool { + match self { + TypeKind::Leaf(leaf_kind) => leaf_kind.supports_transparent_cloning(), + TypeKind::Parent(_) => false, + TypeKind::Dyn(_) => false, + } + } + + pub(crate) fn articled_display_name(&self) -> &'static str { + match self { + TypeKind::Leaf(leaf_kind) => leaf_kind.articled_display_name(), + TypeKind::Parent(parent_kind) => parent_kind.articled_display_name(), + TypeKind::Dyn(dyn_kind) => dyn_kind.articled_display_name(), + } + } + + pub(crate) fn from_source_name(name: &str) -> Option { + // Parse Leaf and Parent TypeKinds + if let Some(tk) = ::type_kind_from_source_name(name) { + return Some(tk); + } + // Parse Dyn TypeKinds + if let Some(dyn_type_kind) = DynTypeKind::from_source_name(name) { + return Some(TypeKind::Dyn(dyn_type_kind)); + } + None + } + + pub(crate) fn source_name(&self) -> &'static str { + match self { + TypeKind::Leaf(leaf_kind) => leaf_kind.source_type_name(), + TypeKind::Parent(parent_kind) => parent_kind.source_name(), + TypeKind::Dyn(dyn_kind) => dyn_kind.source_name(), + } + } +} + +pub(crate) enum ParentTypeKind { + Value(AnyValueTypeKind), + Integer(IntegerTypeKind), + Float(FloatTypeKind), +} + +impl ParentTypeKind { + pub(crate) fn articled_display_name(&self) -> &'static str { + match self { + ParentTypeKind::Value(x) => x.articled_display_name(), + ParentTypeKind::Integer(x) => x.articled_display_name(), + ParentTypeKind::Float(x) => x.articled_display_name(), + } + } + + pub(crate) fn source_name(&self) -> &'static str { + match self { + ParentTypeKind::Value(x) => x.source_type_name(), + ParentTypeKind::Integer(x) => x.source_type_name(), + ParentTypeKind::Float(x) => x.source_type_name(), + } + } + + pub(crate) fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { + match self { + ParentTypeKind::Value(x) => x.feature_resolver(), + ParentTypeKind::Integer(x) => x.feature_resolver(), + ParentTypeKind::Float(x) => x.feature_resolver(), + } + } +} + +pub(crate) enum DynTypeKind { + Iterable, +} + +impl DynTypeKind { + pub(crate) fn articled_display_name(&self) -> &'static str { + match self { + DynTypeKind::Iterable => IterableType::ARTICLED_DISPLAY_NAME, + } + } + + pub(crate) fn from_source_name(name: &str) -> Option { + match name { + IterableType::SOURCE_TYPE_NAME => Some(DynTypeKind::Iterable), + _ => None, + } + } + + pub(crate) fn source_name(&self) -> &'static str { + match self { + DynTypeKind::Iterable => IterableType::SOURCE_TYPE_NAME, + } + } + + pub(crate) fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { + match self { + DynTypeKind::Iterable => &IterableType, + } + } +} + +pub(crate) struct TypeIdent { + span: Span, + pub(crate) kind: TypeKind, +} + +impl TypeIdent { + pub(crate) fn from_ident(ident: &Ident) -> ParseResult { + let span = ident.span(); + let name = ident.to_string(); + let kind = match TypeKind::from_source_name(name.as_str()) { + Some(kind) => kind, + None => { + let lower_case_name = name.to_lowercase(); + let error_message = match TypeKind::from_source_name(&lower_case_name) { + Some(_) => format!("Expected '{}'", lower_case_name), + None => match lower_case_name.as_str() { + "integer" => format!("Expected '{}'", IntegerType::SOURCE_TYPE_NAME), + "str" => format!("Expected '{}'", StringType::SOURCE_TYPE_NAME), + "character" => format!("Expected '{}'", CharType::SOURCE_TYPE_NAME), + "list" | "vec" | "tuple" => { + format!("Expected '{}'", ArrayType::SOURCE_TYPE_NAME) + } + "obj" => format!("Expected '{}'", ObjectType::SOURCE_TYPE_NAME), + _ => format!("Unknown type '{}'", name), + }, + }; + return span.parse_err(error_message); + } + }; + Ok(Self { span, kind }) + } +} + +impl HasSpan for TypeIdent { + fn span(&self) -> Span { + self.span + } +} + +impl ParseSource for TypeIdent { + fn parse(input: SourceParser) -> ParseResult { + Self::from_ident(&input.parse()?) + } + + fn control_flow_pass(&mut self, _context: FlowCapturer) -> ParseResult<()> { + Ok(()) + } +} + +impl TypeKind { + pub(in crate::expressions) fn feature_resolver(&self) -> &'static dyn TypeFeatureResolver { + match self { + TypeKind::Leaf(leaf_kind) => leaf_kind.feature_resolver(), + TypeKind::Parent(parent_kind) => parent_kind.feature_resolver(), + TypeKind::Dyn(dyn_kind) => dyn_kind.feature_resolver(), + } + } +} + +pub(crate) struct TypeProperty { + pub(crate) source_type: TypeIdent, + _colons: Unused, + pub(crate) property: Ident, +} + +impl ParseSource for TypeProperty { + fn parse(input: SourceParser) -> ParseResult { + Ok(Self { + source_type: input.parse()?, + _colons: input.parse()?, + property: input.parse()?, + }) + } + + fn control_flow_pass(&mut self, _context: FlowCapturer) -> ParseResult<()> { + Ok(()) + } +} + +impl TypeProperty { + pub(crate) fn resolve_spanned( + &self, + ownership: RequestedOwnership, + ) -> ExecutionResult> { + let resolver = self.source_type.kind.feature_resolver(); + // TODO[performance] - lazily initialize properties as Shared + let resolved_property = resolver.resolve_type_property(&self.property.to_string()); + match resolved_property { + Some(value) => ownership.map_from_shared(Spanned( + SharedValue::new_from_owned(value.into_any_value()), + self.span_range(), + )), + None => self.type_err(format!( + "Type '{}' has no property named '{}'", + self.source_type.kind.source_name(), + self.property, + )), + } + } +} + +impl HasSpanRange for TypeProperty { + fn span_range(&self) -> SpanRange { + SpanRange::new_between(self.source_type.span, self.property.span()) + } +} diff --git a/src/expressions/type_resolution/value_kinds.rs b/src/expressions/type_resolution/value_kinds.rs deleted file mode 100644 index 77f2ecc9..00000000 --- a/src/expressions/type_resolution/value_kinds.rs +++ /dev/null @@ -1,507 +0,0 @@ -use super::*; - -/// A trait for specific value kinds that can provide a display name. -/// This is implemented by `ValueKind`, `IntegerKind`, `FloatKind`, etc. -pub(crate) trait IsSpecificValueKind: Copy + Into { - fn display_name(&self) -> &'static str; - - fn articled_display_name(&self) -> &'static str; -} - -/// A trait for types that have a value kind. -pub(crate) trait HasValueKind { - type SpecificKind: IsSpecificValueKind; - - fn kind(&self) -> Self::SpecificKind; - - fn value_kind(&self) -> ValueKind { - self.kind().into() - } - - fn value_type(&self) -> &'static str { - self.kind().display_name() - } - - fn articled_value_type(&self) -> &'static str { - self.kind().articled_display_name() - } -} - -impl HasValueKind for &T { - type SpecificKind = T::SpecificKind; - - fn kind(&self) -> Self::SpecificKind { - (**self).kind() - } -} - -impl HasValueKind for &mut T { - type SpecificKind = T::SpecificKind; - - fn kind(&self) -> Self::SpecificKind { - (**self).kind() - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum ValueKind { - None, - Integer(IntegerKind), - Float(FloatKind), - Boolean, - String, - Char, - UnsupportedLiteral, - Array, - Object, - Stream, - Range(RangeKind), - Iterator, - Parser, -} - -impl IsSpecificValueKind for ValueKind { - fn display_name(&self) -> &'static str { - match self { - ValueKind::None => "None", - ValueKind::Integer(kind) => kind.display_name(), - ValueKind::Float(kind) => kind.display_name(), - ValueKind::Boolean => "bool", - ValueKind::String => "string", - ValueKind::Char => "char", - ValueKind::UnsupportedLiteral => "unsupported literal", - ValueKind::Array => "array", - ValueKind::Object => "object", - ValueKind::Stream => "stream", - ValueKind::Range(kind) => kind.display_name(), - ValueKind::Iterator => "iterator", - ValueKind::Parser => "parser", - } - } - - fn articled_display_name(&self) -> &'static str { - match self { - // Instead of saying "expected a none value", we can say "expected None" - ValueKind::None => "None", - ValueKind::Integer(kind) => kind.articled_display_name(), - ValueKind::Float(kind) => kind.articled_display_name(), - ValueKind::Boolean => "a bool", - ValueKind::String => "a string", - ValueKind::Char => "a char", - ValueKind::UnsupportedLiteral => "an unsupported literal", - ValueKind::Array => "an array", - ValueKind::Object => "an object", - ValueKind::Stream => "a stream", - ValueKind::Range(kind) => kind.articled_display_name(), - ValueKind::Iterator => "an iterator", - ValueKind::Parser => "a parser", - } - } -} - -static NONE: NoneTypeData = NoneTypeData; -static BOOLEAN: BooleanTypeData = BooleanTypeData; -static STRING: StringTypeData = StringTypeData; -static CHAR: CharTypeData = CharTypeData; -static UNSUPPORTED_LITERAL: UnsupportedLiteralTypeData = UnsupportedLiteralTypeData; -static ARRAY: ArrayTypeData = ArrayTypeData; -static OBJECT: ObjectTypeData = ObjectTypeData; -static STREAM: StreamTypeData = StreamTypeData; -static RANGE: RangeTypeData = RangeTypeData; -static ITERABLE: IterableTypeData = IterableTypeData; -static ITERATOR: IteratorTypeData = IteratorTypeData; -static PARSER: ParserTypeData = ParserTypeData; - -impl ValueKind { - fn method_resolver(&self) -> &'static dyn MethodResolver { - match self { - ValueKind::None => &NONE, - ValueKind::Integer(kind) => kind.method_resolver(), - ValueKind::Float(kind) => kind.method_resolver(), - ValueKind::Boolean => &BOOLEAN, - ValueKind::String => &STRING, - ValueKind::Char => &CHAR, - ValueKind::UnsupportedLiteral => &UNSUPPORTED_LITERAL, - ValueKind::Array => &ARRAY, - ValueKind::Object => &OBJECT, - ValueKind::Stream => &STREAM, - ValueKind::Range(_) => &RANGE, - ValueKind::Iterator => &ITERATOR, - ValueKind::Parser => &PARSER, - } - } - - /// This should be true for types which users expect to have value - /// semantics, but false for types which are expensive to clone or - /// are expected to have reference semantics. - /// - /// This indicates if an &x can be converted to an x via cloning - /// when doing method resolution. - pub(crate) fn supports_transparent_cloning(&self) -> bool { - match self { - ValueKind::None => true, - ValueKind::Integer(_) => true, - ValueKind::Float(_) => true, - ValueKind::Boolean => true, - // Strings are value-like, so it makes sense to transparently clone them - ValueKind::String => true, - ValueKind::Char => true, - ValueKind::UnsupportedLiteral => false, - ValueKind::Array => false, - ValueKind::Object => false, - ValueKind::Stream => false, - ValueKind::Range(_) => true, - ValueKind::Iterator => false, - // A parser is a handle, so can be cloned transparently. - // It may fail to be able to be used to parse if the underlying stream is out of scope of course. - ValueKind::Parser => true, - } - } -} - -impl MethodResolver for ValueKind { - fn resolve_method(&self, method_name: &str) -> Option { - self.method_resolver().resolve_method(method_name) - } - - fn resolve_unary_operation( - &self, - operation: &UnaryOperation, - ) -> Option { - self.method_resolver().resolve_unary_operation(operation) - } - - fn resolve_binary_operation( - &self, - operation: &BinaryOperation, - ) -> Option { - self.method_resolver().resolve_binary_operation(operation) - } - - fn resolve_type_property(&self, property_name: &str) -> Option { - self.method_resolver().resolve_type_property(property_name) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum IntegerKind { - Untyped, - I8, - I16, - I32, - I64, - I128, - Isize, - U8, - U16, - U32, - U64, - U128, - Usize, -} - -impl IsSpecificValueKind for IntegerKind { - fn display_name(&self) -> &'static str { - match self { - IntegerKind::Untyped => "untyped integer", - IntegerKind::I8 => "i8", - IntegerKind::I16 => "i16", - IntegerKind::I32 => "i32", - IntegerKind::I64 => "i64", - IntegerKind::I128 => "i128", - IntegerKind::Isize => "isize", - IntegerKind::U8 => "u8", - IntegerKind::U16 => "u16", - IntegerKind::U32 => "u32", - IntegerKind::U64 => "u64", - IntegerKind::U128 => "u128", - IntegerKind::Usize => "usize", - } - } - - fn articled_display_name(&self) -> &'static str { - match self { - IntegerKind::Untyped => "an untyped integer", - IntegerKind::I8 => "an i8", - IntegerKind::I16 => "an i16", - IntegerKind::I32 => "an i32", - IntegerKind::I64 => "an i64", - IntegerKind::I128 => "an i128", - IntegerKind::Isize => "an isize", - IntegerKind::U8 => "a u8", - IntegerKind::U16 => "a u16", - IntegerKind::U32 => "a u32", - IntegerKind::U64 => "a u64", - IntegerKind::U128 => "a u128", - IntegerKind::Usize => "a usize", - } - } -} - -impl From for ValueKind { - fn from(kind: IntegerKind) -> Self { - ValueKind::Integer(kind) - } -} - -static INTEGER: IntegerTypeData = IntegerTypeData; -static UNTYPED_INTEGER: UntypedIntegerTypeData = UntypedIntegerTypeData; -static I8: I8TypeData = I8TypeData; -static I16: I16TypeData = I16TypeData; -static I32: I32TypeData = I32TypeData; -static I64: I64TypeData = I64TypeData; -static I128: I128TypeData = I128TypeData; -static ISIZE: IsizeTypeData = IsizeTypeData; -static U8: U8TypeData = U8TypeData; -static U16: U16TypeData = U16TypeData; -static U32: U32TypeData = U32TypeData; -static U64: U64TypeData = U64TypeData; -static U128: U128TypeData = U128TypeData; -static USIZE: UsizeTypeData = UsizeTypeData; - -impl IntegerKind { - pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { - match self { - IntegerKind::Untyped => &UNTYPED_INTEGER, - IntegerKind::I8 => &I8, - IntegerKind::I16 => &I16, - IntegerKind::I32 => &I32, - IntegerKind::I64 => &I64, - IntegerKind::I128 => &I128, - IntegerKind::Isize => &ISIZE, - IntegerKind::U8 => &U8, - IntegerKind::U16 => &U16, - IntegerKind::U32 => &U32, - IntegerKind::U64 => &U64, - IntegerKind::U128 => &U128, - IntegerKind::Usize => &USIZE, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum FloatKind { - Untyped, - F32, - F64, -} - -impl IsSpecificValueKind for FloatKind { - fn display_name(&self) -> &'static str { - match self { - FloatKind::Untyped => "untyped float", - FloatKind::F32 => "f32", - FloatKind::F64 => "f64", - } - } - - fn articled_display_name(&self) -> &'static str { - match self { - FloatKind::Untyped => "an untyped float", - FloatKind::F32 => "an f32", - FloatKind::F64 => "an f64", - } - } -} - -impl From for ValueKind { - fn from(kind: FloatKind) -> Self { - ValueKind::Float(kind) - } -} - -static FLOAT: FloatTypeData = FloatTypeData; -static UNTYPED_FLOAT: UntypedFloatTypeData = UntypedFloatTypeData; -static F32: F32TypeData = F32TypeData; -static F64: F64TypeData = F64TypeData; - -impl FloatKind { - pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { - match self { - FloatKind::Untyped => &UNTYPED_FLOAT, - FloatKind::F32 => &F32, - FloatKind::F64 => &F64, - } - } -} - -pub(crate) struct Type { - span: Span, - pub(crate) kind: TypeKind, -} - -// A ValueKind represents a kind of leaf value. -// But a TypeKind represents a type in the hierarchy, which points at a type data. -pub(crate) enum TypeKind { - Integer, - SpecificInteger(IntegerKind), - Float, - SpecificFloat(FloatKind), - Boolean, - String, - Char, - Array, - Object, - Stream, - Range, - Iterable, - Iterator, - Parser, -} - -impl Type { - pub(crate) fn from_source_name(name: &str) -> Option { - Some(match name { - "int" => TypeKind::Integer, - "i8" => TypeKind::SpecificInteger(IntegerKind::I8), - "i16" => TypeKind::SpecificInteger(IntegerKind::I16), - "i32" => TypeKind::SpecificInteger(IntegerKind::I32), - "i64" => TypeKind::SpecificInteger(IntegerKind::I64), - "i128" => TypeKind::SpecificInteger(IntegerKind::I128), - "isize" => TypeKind::SpecificInteger(IntegerKind::Isize), - "u8" => TypeKind::SpecificInteger(IntegerKind::U8), - "u16" => TypeKind::SpecificInteger(IntegerKind::U16), - "u32" => TypeKind::SpecificInteger(IntegerKind::U32), - "u64" => TypeKind::SpecificInteger(IntegerKind::U64), - "u128" => TypeKind::SpecificInteger(IntegerKind::U128), - "usize" => TypeKind::SpecificInteger(IntegerKind::Usize), - "float" => TypeKind::Float, - "f32" => TypeKind::SpecificFloat(FloatKind::F32), - "f64" => TypeKind::SpecificFloat(FloatKind::F64), - "bool" => TypeKind::Boolean, - "string" => TypeKind::String, - "char" => TypeKind::Char, - "array" => TypeKind::Array, - "object" => TypeKind::Object, - "stream" => TypeKind::Stream, - "range" => TypeKind::Range, - "iterable" => TypeKind::Iterable, - "iterator" => TypeKind::Iterator, - "parser" => TypeKind::Parser, - _ => return None, - }) - } - - pub(crate) fn source_name(&self) -> &'static str { - // This should be inverse of parse below - match &self.kind { - TypeKind::Integer => "int", - TypeKind::SpecificInteger(kind) => kind.display_name(), - TypeKind::Float => "float", - TypeKind::SpecificFloat(kind) => kind.display_name(), - TypeKind::Boolean => "bool", - TypeKind::String => "string", - TypeKind::Char => "char", - TypeKind::Array => "array", - TypeKind::Object => "object", - TypeKind::Stream => "stream", - TypeKind::Range => "range", - TypeKind::Iterable => "iterable", - TypeKind::Iterator => "iterator", - TypeKind::Parser => "parser", - } - } - - pub(crate) fn from_ident(ident: &Ident) -> ParseResult { - let span = ident.span(); - let name = ident.to_string(); - let kind = match Self::from_source_name(name.as_str()) { - Some(kind) => kind, - None => { - let lower_case_name = name.to_lowercase(); - let error_message = match Self::from_source_name(&lower_case_name) { - Some(_) => format!("Expected '{}'", lower_case_name), - None => match lower_case_name.as_str() { - "integer" => "Expected 'int'".to_string(), - "str" => "Expected 'string'".to_string(), - "character" => "Expected 'char'".to_string(), - "list" => "Expected 'array'".to_string(), - "obj" => "Expected 'object'".to_string(), - _ => format!("Unknown type '{}'", name), - }, - }; - return span.parse_err(error_message); - } - }; - Ok(Self { span, kind }) - } -} - -impl ParseSource for Type { - fn parse(input: SourceParser) -> ParseResult { - Self::from_ident(&input.parse()?) - } - - fn control_flow_pass(&mut self, _context: FlowCapturer) -> ParseResult<()> { - Ok(()) - } -} - -impl TypeKind { - pub(in crate::expressions) fn method_resolver(&self) -> &'static dyn MethodResolver { - match self { - TypeKind::Integer => &INTEGER, - TypeKind::SpecificInteger(integer_kind) => integer_kind.method_resolver(), - TypeKind::Float => &FLOAT, - TypeKind::SpecificFloat(float_kind) => float_kind.method_resolver(), - TypeKind::Boolean => &BOOLEAN, - TypeKind::String => &STRING, - TypeKind::Char => &CHAR, - TypeKind::Array => &ARRAY, - TypeKind::Object => &OBJECT, - TypeKind::Stream => &STREAM, - TypeKind::Range => &RANGE, - TypeKind::Iterable => &ITERABLE, - TypeKind::Iterator => &ITERATOR, - TypeKind::Parser => &PARSER, - } - } -} - -pub(crate) struct TypeProperty { - pub(crate) source_type: Type, - _colons: Unused, - pub(crate) property: Ident, -} - -impl ParseSource for TypeProperty { - fn parse(input: SourceParser) -> ParseResult { - Ok(Self { - source_type: input.parse()?, - _colons: input.parse()?, - property: input.parse()?, - }) - } - - fn control_flow_pass(&mut self, _context: FlowCapturer) -> ParseResult<()> { - Ok(()) - } -} - -impl TypeProperty { - pub(crate) fn resolve_spanned( - &self, - ownership: RequestedOwnership, - ) -> ExecutionResult> { - let resolver = self.source_type.kind.method_resolver(); - // TODO[performance] - lazily initialize properties as Shared - let resolved_property = resolver.resolve_type_property(&self.property.to_string()); - match resolved_property { - Some(value) => ownership.map_from_shared(Spanned( - SharedValue::new_from_owned(value.into_owned_value()), - self.span_range(), - )), - None => self.type_err(format!( - "Type '{}' has no property named '{}'", - self.source_type.source_name(), - self.property, - )), - } - } -} - -impl HasSpanRange for TypeProperty { - fn span_range(&self) -> SpanRange { - SpanRange::new_between(self.source_type.span, self.property.span()) - } -} diff --git a/src/expressions/values/any_value.rs b/src/expressions/values/any_value.rs new file mode 100644 index 00000000..3c1a6f96 --- /dev/null +++ b/src/expressions/values/any_value.rs @@ -0,0 +1,544 @@ +use super::*; + +pub(crate) type AnyValue = AnyValueContent<'static, BeOwned>; +/// For symmetry +pub(crate) type AnyValueOwned = AnyValue; +pub(crate) type AnyValueRef<'a> = AnyValueContent<'a, BeRef>; +pub(crate) type AnyValueAnyRef<'a> = AnyValueContent<'a, BeAnyRef>; +pub(crate) type AnyValueShared = Shared; +pub(crate) type AnyValueMutable = Mutable; +pub(crate) type AnyValueAssignee = Assignee; +// pub(crate) type AnyValueShared = AnyValueContent<'static, BeShared>; +// pub(crate) type AnyValueMutable = AnyValueContent<'static, BeMutable>; +// pub(crate) type AnyValueAssignee = AnyValueContent<'static, BeAssignee>; + +define_parent_type! { + pub(crate) AnyType, + content: pub(crate) AnyValueContent, + leaf_kind: pub(crate) AnyValueLeafKind, + type_kind: ParentTypeKind::Value(pub(crate) AnyValueTypeKind), + variants: { + None => NoneType, + Integer => IntegerType, + Float => FloatType, + Bool => BoolType, + String => StringType, + Char => CharType, + // Unsupported literal is a type here so that we can parse such a token + // as a value rather than a stream, and give it better error messages + UnsupportedLiteral => UnsupportedLiteralType, + Array => ArrayType, + Object => ObjectType, + Stream => StreamType, + Range => RangeType, + Iterator => IteratorType, + Parser => ParserType, + }, + type_name: "value", + articled_display_name: "any value", +} + +define_type_features! { + impl AnyType, + pub(crate) mod value_interface { + pub(crate) mod methods { + fn clone(this: CopyOnWriteValue) -> AnyValue { + this.clone_to_owned_infallible() + } + + fn as_mut(Spanned(this, span): Spanned) -> ExecutionResult { + Ok(match this { + ArgumentValue::Owned(owned) => Mutable::new_from_owned(owned), + ArgumentValue::CopyOnWrite(copy_on_write) => ArgumentOwnership::Mutable + .map_from_copy_on_write(Spanned(copy_on_write, span))? + .expect_mutable(), + ArgumentValue::Mutable(mutable) => mutable, + ArgumentValue::Assignee(assignee) => assignee.0, + ArgumentValue::Shared(shared) => ArgumentOwnership::Mutable + .map_from_shared(Spanned(shared, span))? + .expect_mutable(), + }) + } + + // NOTE: + // All value types can be coerced into SharedValue as an input, so this method does actually do something + fn as_ref(this: SharedValue) -> SharedValue { + this + } + + fn swap(mut a: AssigneeValue, mut b: AssigneeValue) -> () { + core::mem::swap(a.0.deref_mut(), b.0.deref_mut()); + } + + fn replace(mut a: AssigneeValue, b: AnyValue) -> AnyValue { + 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))?; + 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)) + } + + fn to_stream(Spanned(input, span_range): Spanned) -> ExecutionResult { + input.map_into( + |shared| shared.as_ref_value().output_to_new_stream(Grouping::Flattened, span_range), + |owned| owned.into_stream(Grouping::Flattened, span_range), + ) + } + + fn to_group(Spanned(input, span_range): Spanned) -> ExecutionResult { + input.map_into( + |shared| shared.as_ref_value().output_to_new_stream(Grouping::Grouped, span_range), + |owned| owned.into_stream(Grouping::Grouped, span_range), + ) + } + + fn to_string(Spanned(input, span_range): Spanned) -> ExecutionResult { + input.as_ref_value().concat_recursive(&ConcatBehaviour::standard(span_range)) + } + + [context] fn with_span(value: Spanned, spans: AnyRef) -> ExecutionResult { + 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(), + None => Span::call_site(), + }; + this.replace_first_level_spans(span_to_use); + Ok(this) + } + + // TYPE CHECKING + // =============================== + fn is_none(this: SharedValue) -> bool { + this.is_none() + } + + // EQUALITY METHODS + // =============================== + // Compare values with strict type checking - errors on value kind mismatch. + [context] fn typed_eq(this: AnyValueAnyRef, other: AnyValueAnyRef) -> ExecutionResult { + 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()?; + 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()?; + 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()?; + 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()?; + 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()?; + let spanned = stream.into_spanned_ref(context.output_span_range); + stream_interface::methods::to_literal(context, spanned) + } + } + pub(crate) mod unary_operations { + fn cast_to_string(Spanned(input, span_range): Spanned) -> ExecutionResult { + input.as_ref_value().concat_recursive(&ConcatBehaviour::standard(span_range)) + } + + fn cast_to_stream(input: Spanned) -> ExecutionResult { + input.into_stream() + } + } + pub(crate) mod binary_operations { + fn eq(lhs: AnyValueAnyRef, rhs: AnyValueAnyRef) -> bool { + AnyValue::values_equal(lhs.as_ref_value(), rhs.as_ref_value()) + } + + fn ne(lhs: AnyValueAnyRef, rhs: AnyValueAnyRef) -> bool { + !AnyValue::values_equal(lhs.as_ref_value(), rhs.as_ref_value()) + } + } + interface_items { + fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { + Some(match operation { + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), + AnyValueLeafKind::Stream(_) => unary_definitions::cast_to_stream(), + _ => return None, + }, + _ => return None, + }) + } + + fn resolve_own_binary_operation( + operation: &BinaryOperation, + ) -> Option { + Some(match operation { + BinaryOperation::Equal { .. } => binary_definitions::eq(), + BinaryOperation::NotEqual { .. } => binary_definitions::ne(), + _ => return None, + }) + } + } + } +} + +pub(crate) trait IntoAnyValue: Sized { + fn into_any_value(self) -> AnyValue; +} + +impl<'a, F: IsHierarchicalForm> AnyValueContent<'a, F> { + pub(crate) fn is_none(&self) -> bool { + matches!(&self, AnyValueContent::None(_)) + } +} + +impl AnyValue { + pub(crate) fn for_literal(literal: Literal) -> AnyValue { + // The unwrap should be safe because all Literal should be parsable + // as syn::Lit; falling back to syn::Lit::Verbatim if necessary. + Self::for_syn_lit( + literal + .to_token_stream() + .interpreted_parse_with(|input| input.parse()) + .unwrap(), + ) + } + + pub(crate) fn for_syn_lit(lit: syn::Lit) -> AnyValue { + // https://docs.rs/syn/latest/syn/enum.Lit.html + let matched = match &lit { + Lit::Int(lit) => match IntegerValue::for_litint(lit) { + Ok(int) => Some(int.into_any_value()), + Err(_) => None, + }, + Lit::Float(lit) => match FloatValue::for_litfloat(lit) { + Ok(float) => Some(float.into_any_value()), + Err(_) => None, + }, + Lit::Bool(lit) => Some(lit.value.into_any_value()), + Lit::Str(lit) => Some(lit.value().into_any_value()), + Lit::Char(lit) => Some(lit.value().into_any_value()), + _ => None, + }; + match matched { + Some(value) => value, + None => UnsupportedLiteral(lit).into_any_value(), + } + } + + // TODO[concepts]: Remove from here + pub(crate) fn try_transparent_clone( + &self, + error_span_range: SpanRange, + ) -> ExecutionResult { + 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.", + self.articled_kind() + )); + } + Ok(self.clone()) + } + + pub(crate) fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { + self.as_ref_value() + .test_equality(&other.as_ref_value(), ctx) + } + + /// Recursively compares two values for equality using `ValuesEqual` semantics. + pub(crate) fn values_equal<'a>(lhs: AnyValueRef<'a>, rhs: AnyValueRef<'a>) -> bool { + lhs.lenient_eq(&rhs) + } +} + +impl<'a> ValuesEqual for AnyValueRef<'a> { + fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { + // Each variant has two lines: same-type comparison, then type-mismatch fallback. + // This ensures adding a new variant only requires adding two lines at the bottom. + match (self, other) { + (AnyValueContent::None(_), AnyValueContent::None(_)) => ctx.values_equal(), + (AnyValueContent::None(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Bool(l), AnyValueContent::Bool(r)) => l.test_equality(r, ctx), + (AnyValueContent::Bool(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Char(l), AnyValueContent::Char(r)) => l.test_equality(r, ctx), + (AnyValueContent::Char(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::String(l), AnyValueContent::String(r)) => l.test_equality(r, ctx), + (AnyValueContent::String(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Integer(l), AnyValueContent::Integer(r)) => l.test_equality(r, ctx), + (AnyValueContent::Integer(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Float(l), AnyValueContent::Float(r)) => l.test_equality(r, ctx), + (AnyValueContent::Float(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Array(l), AnyValueContent::Array(r)) => l.test_equality(r, ctx), + (AnyValueContent::Array(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Object(l), AnyValueContent::Object(r)) => l.test_equality(r, ctx), + (AnyValueContent::Object(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Stream(l), AnyValueContent::Stream(r)) => l.test_equality(r, ctx), + (AnyValueContent::Stream(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Range(l), AnyValueContent::Range(r)) => l.test_equality(r, ctx), + (AnyValueContent::Range(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::UnsupportedLiteral(l), AnyValueContent::UnsupportedLiteral(r)) => { + l.test_equality(r, ctx) + } + (AnyValueContent::UnsupportedLiteral(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Parser(l), AnyValueContent::Parser(r)) => l.test_equality(r, ctx), + (AnyValueContent::Parser(_), _) => ctx.kind_mismatch(self, other), + (AnyValueContent::Iterator(l), AnyValueContent::Iterator(r)) => l.test_equality(r, ctx), + (AnyValueContent::Iterator(_), _) => ctx.kind_mismatch(self, other), + } + } +} + +impl AnyValue { + pub(crate) fn into_stream( + self, + grouping: Grouping, + error_span_range: SpanRange, + ) -> ExecutionResult { + 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(); + output.push_new_group(value, Delimiter::None, span); + Ok(output) + } + (other, grouping) => other + .as_ref_value() + .output_to_new_stream(grouping, error_span_range), + } + } +} + +impl<'a> AnyValueRef<'a> { + pub(crate) fn output_to_new_stream( + 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) + } + + pub(crate) fn output_to( + self, + grouping: Grouping, + output: &mut ToStreamContext, + ) -> ExecutionResult<()> { + match grouping { + Grouping::Grouped => { + // Grouping can be important for different values, to ensure they're read atomically + // when the output stream is viewed as an array/iterable, e.g. in a for loop. + // * Grouping means -1 is interpreted atomically, rather than as a punct then a number + // * Grouping means that a stream is interpreted atomically + output.push_grouped(|inner| self.output_flattened_to(inner), Delimiter::None)?; + } + Grouping::Flattened => { + self.output_flattened_to(output)?; + } + } + Ok(()) + } + + fn output_flattened_to(self, output: &mut ToStreamContext) -> ExecutionResult<()> { + match self { + AnyValueContent::None(_) => {} + AnyValueContent::Integer(value) => { + let literal = value + .clone_to_owned_infallible() + .to_literal(output.new_token_span()); + output.push_literal(literal); + } + AnyValueContent::Float(value) => { + value.clone_to_owned_infallible().output_to(output); + } + AnyValueContent::Bool(value) => { + let ident = Ident::new_bool(*value, output.new_token_span()); + output.push_ident(ident); + } + AnyValueContent::String(value) => { + let literal = Literal::string(value).with_span(output.new_token_span()); + output.push_literal(literal); + } + AnyValueContent::Char(value) => { + let literal = Literal::character(*value).with_span(output.new_token_span()); + output.push_literal(literal); + } + AnyValueContent::UnsupportedLiteral(literal) => { + output.extend_raw_tokens(literal.0.to_token_stream()) + } + AnyValueContent::Object(_) => { + 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::Iterator(iterator) => iterator + .clone() + .output_items_to(output, Grouping::Flattened)?, + AnyValueContent::Range(value) => { + let iterator = IteratorValue::new_for_range(value.clone())?; + iterator.output_items_to(output, Grouping::Flattened)? + } + AnyValueContent::Parser(_) => { + return output.type_err("Parsers cannot be output to a stream"); + } + }; + Ok(()) + } + + pub(crate) fn concat_recursive(self, behaviour: &ConcatBehaviour) -> ExecutionResult { + let mut output = String::new(); + self.concat_recursive_into(&mut output, behaviour)?; + Ok(output) + } + + pub(crate) fn concat_recursive_into( + self, + output: &mut String, + behaviour: &ConcatBehaviour, + ) -> ExecutionResult<()> { + match self { + AnyValueContent::None(_) => { + if behaviour.show_none_values { + output.push_str("None"); + } + } + AnyValueContent::Stream(stream) => { + stream.concat_as_literal_into(output, behaviour); + } + AnyValueContent::Array(array) => { + array.concat_recursive_into(output, behaviour)?; + } + AnyValueContent::Object(object) => { + object.concat_recursive_into(output, behaviour)?; + } + AnyValueContent::Iterator(iterator) => { + iterator.concat_recursive_into(output, behaviour)?; + } + AnyValueContent::Range(range) => { + range.concat_recursive_into(output, behaviour)?; + } + AnyValueContent::Parser(_) => { + return behaviour + .error_span_range + .type_err("Parsers cannot be output to a string"); + } + AnyValueContent::Integer(_) + | AnyValueContent::Float(_) + | AnyValueContent::Char(_) + | AnyValueContent::Bool(_) + | AnyValueContent::UnsupportedLiteral(_) + | 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) + .expect("Non-composite values should all be able to be outputted to a stream"); + stream.concat_content_into(output, behaviour); + } + } + Ok(()) + } +} + +pub(crate) struct ToStreamContext<'a> { + output_stream: &'a mut OutputStream, + error_span_range: SpanRange, +} + +impl<'a> ToStreamContext<'a> { + pub(crate) fn new(output_stream: &'a mut OutputStream, error_span_range: SpanRange) -> Self { + Self { + output_stream, + error_span_range, + } + } + + pub(crate) fn push_grouped( + &mut self, + f: impl FnOnce(&mut ToStreamContext) -> ExecutionResult<()>, + delimiter: Delimiter, + ) -> ExecutionResult<()> { + let span = self.new_token_span(); + self.output_stream.push_grouped( + |inner| f(&mut ToStreamContext::new(inner, self.error_span_range)), + delimiter, + span, + ) + } + + 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; + + fn deref(&self) -> &Self::Target { + self.output_stream + } +} + +impl DerefMut for ToStreamContext<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.output_stream + } +} + +impl HasSpanRange for ToStreamContext<'_> { + fn span_range(&self) -> SpanRange { + self.error_span_range + } +} + +impl Spanned { + pub(crate) fn into_statement_result(self) -> ExecutionResult<()> { + let Spanned(value, span_range) = self; + match value { + AnyValueContent::None(_) => Ok(()), + _ => span_range.control_flow_err("A non-returning statement must not return a value. If you wish to explicitly discard the expression's result, use `let _ = ...;`. Alternatively, If you wish to output the value into the parent token stream, use `emit ...;`"), + } + } + + pub(crate) fn into_stream(self) -> ExecutionResult { + let Spanned(value, span_range) = self; + value.into_stream(Grouping::Flattened, span_range) + } + + pub(crate) fn resolve_any_iterator( + self, + resolution_target: &str, + ) -> ExecutionResult { + self.dyn_resolve::(resolution_target)? + .into_iterator() + } +} + +#[derive(Copy, Clone)] +pub(crate) enum Grouping { + Grouped, + Flattened, +} diff --git a/src/expressions/values/array.rs b/src/expressions/values/array.rs index 2c2d949a..eaa02b03 100644 --- a/src/expressions/values/array.rs +++ b/src/expressions/values/array.rs @@ -1,12 +1,31 @@ use super::*; +define_leaf_type! { + pub(crate) ArrayType => AnyType(AnyValueContent::Array), + content: ArrayValue, + kind: pub(crate) ArrayKind, + type_name: "array", + articled_display_name: "an array", + dyn_impls: { + IterableType: impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(IteratorValue::new_for_array(*self)) + } + + fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + Ok(self.items.len()) + } + } + }, +} + #[derive(Clone)] pub(crate) struct ArrayValue { - pub(crate) items: Vec, + pub(crate) items: Vec, } impl ArrayValue { - pub(crate) fn new(items: Vec) -> Self { + pub(crate) fn new(items: Vec) -> Self { Self { items } } @@ -16,25 +35,25 @@ impl ArrayValue { grouping: Grouping, ) -> ExecutionResult<()> { for item in &self.items { - item.output_to(grouping, output)?; + item.as_ref_value().output_to(grouping, output)?; } Ok(()) } pub(super) fn into_indexed( mut self, - Spanned(index, span_range): Spanned<&Value>, - ) -> ExecutionResult { + Spanned(index, span_range): Spanned, + ) -> ExecutionResult { Ok(match index { - Value::Integer(integer) => { + AnyValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; - std::mem::replace(&mut self.items[index], Value::None) + std::mem::replace(&mut self.items[index], ().into_any_value()) } - Value::Range(range) => { + AnyValueContent::Range(range) => { let range = Spanned(range, span_range).resolve_to_index_range(&self)?; let new_items: Vec<_> = self.items.drain(range).collect(); - new_items.into_value() + new_items.into_any_value() } _ => return span_range.type_err("The index must be an integer or a range"), }) @@ -42,16 +61,16 @@ impl ArrayValue { pub(super) fn index_mut( &mut self, - Spanned(index, span_range): Spanned<&Value>, - ) -> ExecutionResult<&mut Value> { + Spanned(index, span_range): Spanned, + ) -> ExecutionResult<&mut AnyValue> { Ok(match index { - Value::Integer(integer) => { + AnyValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; &mut self.items[index] } - Value::Range(..) => { - // Temporary until we add slice types - we error here + AnyValueContent::Range(..) => { + // TODO[slice-support] Temporary until we add slice types - we error here return span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]"); } _ => return span_range.type_err("The index must be an integer or a range"), @@ -60,16 +79,16 @@ impl ArrayValue { pub(super) fn index_ref( &self, - Spanned(index, span_range): Spanned<&Value>, - ) -> ExecutionResult<&Value> { + Spanned(index, span_range): Spanned, + ) -> ExecutionResult<&AnyValue> { Ok(match index { - Value::Integer(integer) => { + AnyValueContent::Integer(integer) => { let index = self.resolve_valid_index_from_integer(Spanned(integer, span_range), false)?; &self.items[index] } - Value::Range(..) => { - // Temporary until we add slice types - we error here + AnyValueContent::Range(..) => { + // TODO[slice-support] Temporary until we add slice types - we error here return span_range.ownership_err("Currently, a range-indexed array must be owned. Use `.take()` or `.clone()` before indexing [..]"); } _ => return span_range.type_err("The index must be an integer or a range"), @@ -78,11 +97,11 @@ impl ArrayValue { pub(super) fn resolve_valid_index( &self, - Spanned(index, span_range): Spanned<&Value>, + Spanned(index, span_range): Spanned, is_exclusive: bool, ) -> ExecutionResult { match index { - Value::Integer(int) => { + AnyValueContent::Integer(int) => { self.resolve_valid_index_from_integer(Spanned(int, span_range), is_exclusive) } _ => span_range.type_err("The index must be an integer"), @@ -91,11 +110,12 @@ impl ArrayValue { fn resolve_valid_index_from_integer( &self, - Spanned(integer, span): Spanned<&IntegerValue>, + Spanned(integer, span): Spanned, is_exclusive: bool, ) -> ExecutionResult { - let index: usize = - Spanned((*integer).into_owned_value(), span).resolve_as("An array index")?; + let index: OptionalSuffix = + Spanned(integer.clone_to_owned_infallible(), span).resolve_as("An array index")?; + let index = index.0; if is_exclusive { if index <= self.items.len() { Ok(index) @@ -134,14 +154,6 @@ impl ArrayValue { } } -impl HasValueKind for ArrayValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Array - } -} - impl ValuesEqual for ArrayValue { /// Recursively compares two arrays element-by-element. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { @@ -158,35 +170,33 @@ impl ValuesEqual for ArrayValue { } } -impl IntoValue for Vec { - fn into_value(self) -> Value { - Value::Array(ArrayValue { items: self }) - } +impl IsValueContent for Vec { + type Type = ArrayType; + type Form = BeOwned; } -impl IntoValue for ArrayValue { - fn into_value(self) -> Value { - Value::Array(self) +impl IntoValueContent<'static> for Vec { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + ArrayValue { items: self } } } impl_resolvable_argument_for! { - ArrayTypeData, + ArrayType, (value, context) -> ArrayValue { match value { - Value::Array(value) => Ok(value), + AnyValueContent::Array(value) => Ok(value), _ => context.err("an array", value), } } } -define_interface! { - struct ArrayTypeData, - parent: IterableTypeData, +define_type_features! { + impl ArrayType, pub(crate) mod array_interface { pub(crate) mod methods { - fn push(mut this: Mutable, item: OwnedValue) -> ExecutionResult<()> { - this.items.push(item.into()); + fn push(mut this: Mutable, item: AnyValue) -> ExecutionResult<()> { + this.items.push(item); Ok(()) } @@ -196,11 +206,10 @@ define_interface! { } } pub(crate) mod unary_operations { - [context] fn cast_to_numeric(Spanned(this, span): Spanned>) -> ExecutionResult { - let mut this = this.into_inner(); + [context] fn cast_singleton_to_value(Spanned(mut this, span): Spanned) -> ExecutionResult { let length = this.items.len(); if length == 1 { - Ok(context.operation.evaluate(this.items.pop().unwrap().into_owned().spanned(span))?.0) + Ok(context.operation.evaluate(this.items.pop().unwrap().spanned(span))?.0) } else { context.operation.value_err(format!( "Only a singleton array can be cast to this value but the array has {} elements", @@ -219,17 +228,26 @@ define_interface! { lhs.items.extend(rhs.items); } } + index_access(ArrayValue) { + fn shared(source: &'a ArrayValue, index: Spanned) { + source.index_ref(index) + } + fn mutable(source: &'a mut ArrayValue, index: Spanned, _auto_create: bool) { + source.index_mut(index) + } + fn owned(source: ArrayValue, index: Spanned) { + source.into_indexed(index) + } + } interface_items { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } | UnaryOperation::Not { .. } => return None, - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Boolean - | CastTarget::Char - | CastTarget::Integer(_) - | CastTarget::Float(_) => unary_definitions::cast_to_numeric(), - _ => return None, - }, + UnaryOperation::Cast { target, .. } => if target.is_singleton_target() { + unary_definitions::cast_singleton_to_value() + } else { + return None; + } }) } diff --git a/src/expressions/values/boolean.rs b/src/expressions/values/boolean.rs index 14adef3d..adce8579 100644 --- a/src/expressions/values/boolean.rs +++ b/src/expressions/values/boolean.rs @@ -2,44 +2,18 @@ use super::*; -#[derive(Clone)] -pub(crate) struct BooleanValue { - pub(crate) value: bool, +define_leaf_type! { + pub(crate) BoolType => AnyType(AnyValueContent::Bool), + content: bool, + kind: pub(crate) BoolKind, + type_name: "bool", + articled_display_name: "a bool", + dyn_impls: {}, } -impl IntoValue for BooleanValue { - fn into_value(self) -> Value { - Value::Boolean(self) - } -} - -impl Debug for BooleanValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.value) - } -} - -impl BooleanValue { - pub(crate) fn for_litbool(lit: &syn::LitBool) -> Owned { - Self { value: lit.value }.into_owned() - } - - pub(super) fn to_ident(&self, span: Span) -> Ident { - Ident::new_bool(self.value, span) - } -} - -impl HasValueKind for BooleanValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Boolean - } -} - -impl ValuesEqual for BooleanValue { +impl ValuesEqual for bool { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.value == other.value { + if self == other { ctx.values_equal() } else { ctx.leaf_values_not_equal(self, other) @@ -47,15 +21,8 @@ impl ValuesEqual for BooleanValue { } } -impl IntoValue for bool { - fn into_value(self) -> Value { - Value::Boolean(BooleanValue { value: self }) - } -} - -define_interface! { - struct BooleanTypeData, - parent: ValueTypeData, +define_type_features! { + impl BoolType, pub(crate) mod boolean_interface { pub(crate) mod methods { } @@ -191,22 +158,22 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Not { .. } => unary_definitions::not(), - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Boolean => unary_definitions::cast_to_boolean(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Bool(_) => unary_definitions::cast_to_boolean(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, @@ -217,15 +184,11 @@ define_interface! { } impl_resolvable_argument_for! { - BooleanTypeData, - (value, context) -> BooleanValue { + BoolType, + (value, context) -> bool { match value { - Value::Boolean(value) => Ok(value), - other => context.err("a boolean", other), + AnyValueContent::Bool(value) => Ok(value), + other => context.err("a bool", other), } } } - -impl_delegated_resolvable_argument_for! { - (value: BooleanValue) -> bool { value.value } -} diff --git a/src/expressions/values/character.rs b/src/expressions/values/character.rs index a89ee26f..af15e2e8 100644 --- a/src/expressions/values/character.rs +++ b/src/expressions/values/character.rs @@ -1,43 +1,17 @@ use super::*; -#[derive(Clone)] -pub(crate) struct CharValue { - pub(super) value: char, +define_leaf_type! { + pub(crate) CharType => AnyType(AnyValueContent::Char), + content: char, + kind: pub(crate) CharKind, + type_name: "char", + articled_display_name: "a char", + dyn_impls: {}, } -impl IntoValue for CharValue { - fn into_value(self) -> Value { - Value::Char(self) - } -} - -impl Debug for CharValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.value) - } -} - -impl CharValue { - pub(super) fn for_litchar(lit: &syn::LitChar) -> Owned { - Self { value: lit.value() }.into_owned() - } - - pub(super) fn to_literal(&self, span: Span) -> Literal { - Literal::character(self.value).with_span(span) - } -} - -impl HasValueKind for CharValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Char - } -} - -impl ValuesEqual for CharValue { +impl ValuesEqual for char { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.value == other.value { + if self == other { ctx.values_equal() } else { ctx.leaf_values_not_equal(self, other) @@ -45,15 +19,8 @@ impl ValuesEqual for CharValue { } } -impl IntoValue for char { - fn into_value(self) -> Value { - Value::Char(CharValue { value: self }) - } -} - -define_interface! { - struct CharTypeData, - parent: ValueTypeData, +define_type_features! { + impl CharType, pub(crate) mod char_interface { pub(crate) mod methods { } @@ -160,22 +127,22 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Char => unary_definitions::cast_to_char(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Char(_) => unary_definitions::cast_to_char(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, @@ -186,15 +153,11 @@ define_interface! { } impl_resolvable_argument_for! { - CharTypeData, - (value, context) -> CharValue { + CharType, + (value, context) -> char { match value { - Value::Char(value) => Ok(value), + AnyValueContent::Char(char) => Ok(char), _ => context.err("a char", value), } } } - -impl_delegated_resolvable_argument_for!( - (value: CharValue) -> char { value.value } -); diff --git a/src/expressions/values/float.rs b/src/expressions/values/float.rs index 967f6f25..3f80c925 100644 --- a/src/expressions/values/float.rs +++ b/src/expressions/values/float.rs @@ -1,40 +1,66 @@ use super::*; -#[derive(Copy, Clone)] -pub(crate) enum FloatValue { - Untyped(UntypedFloat), - F32(f32), - F64(f64), +define_parent_type! { + pub(crate) FloatType => AnyType(AnyValueContent::Float), + content: pub(crate) FloatContent, + leaf_kind: pub(crate) FloatLeafKind, + type_kind: ParentTypeKind::Float(pub(crate) FloatTypeKind), + variants: { + Untyped => UntypedFloatType, + F32 => F32Type, + F64 => F64Type, + }, + type_name: "float", + articled_display_name: "a float", } -impl IntoValue for FloatValue { - fn into_value(self) -> Value { - Value::Float(self) - } -} +pub(crate) type FloatValue = FloatContent<'static, BeOwned>; +pub(crate) type FloatValueRef<'a> = FloatContent<'a, BeRef>; impl FloatValue { - pub(super) fn for_litfloat(lit: &syn::LitFloat) -> ParseResult> { + pub(super) fn for_litfloat(lit: &syn::LitFloat) -> ParseResult { Ok(match lit.suffix() { - "" => Self::Untyped(UntypedFloat::new_from_lit_float(lit)?), - "f32" => Self::F32(lit.base10_parse()?), - "f64" => Self::F64(lit.base10_parse()?), + "" => FloatContent::Untyped(UntypedFloat::new_from_lit_float(lit)?), + "f32" => FloatContent::F32(lit.base10_parse()?), + "f64" => FloatContent::F64(lit.base10_parse()?), suffix => { return lit.span().parse_err(format!( "The literal suffix {suffix} is not supported in preinterpret expressions" )); } + }) + } + + pub(crate) fn resolve_untyped_to_match(self, target: FloatValueRef) -> FloatValue { + match self { + FloatContent::Untyped(this) => this.into_kind(target.kind()), + other => other, } - .into_owned()) } + #[allow(dead_code)] + pub(super) fn to_literal(self, span: Span) -> Literal { + self.to_unspanned_literal().with_span(span) + } + + fn to_unspanned_literal(self) -> Literal { + match self { + FloatContent::Untyped(float) => float.to_unspanned_literal(), + FloatContent::F32(float) => Literal::f32_suffixed(float), + FloatContent::F64(float) => Literal::f64_suffixed(float), + } + } +} + +// TODO[concepts]: Move to FloatRef<'a> when we can +impl FloatValue { /// Outputs this float value to a token stream. /// For finite values, outputs a literal. For non-finite values (infinity, NaN), /// outputs the equivalent constant path like `f32::INFINITY`. pub(super) fn output_to(&self, output: &mut ToStreamContext) { let span = output.new_token_span(); match self { - FloatValue::Untyped(float) => { + FloatContent::Untyped(float) => { let f = float.into_fallback(); if f.is_finite() { output.push_literal(Literal::f64_unsuffixed(f).with_span(span)); @@ -47,7 +73,7 @@ impl FloatValue { output.extend_raw_tokens(quote::quote_spanned!(span=> f64::NEG_INFINITY)); } } - FloatValue::F32(f) => { + FloatContent::F32(f) => { if f.is_finite() { output.push_literal(Literal::f32_suffixed(*f).with_span(span)); } else if f.is_nan() { @@ -58,7 +84,7 @@ impl FloatValue { output.extend_raw_tokens(quote::quote_spanned!(span=> f32::NEG_INFINITY)); } } - FloatValue::F64(f) => { + FloatContent::F64(f) => { if f.is_finite() { output.push_literal(Literal::f64_suffixed(*f).with_span(span)); } else if f.is_nan() { @@ -71,98 +97,66 @@ impl FloatValue { } } } - - #[allow(dead_code)] - pub(super) fn to_literal(self, span: Span) -> Literal { - self.to_unspanned_literal().with_span(span) - } - - pub(crate) fn resolve_untyped_to_match(this: Owned, target: &FloatValue) -> Self { - match this.into_inner() { - FloatValue::Untyped(this) => this.into_owned().into_kind(target.kind()), - other => other, - } - } - - pub(crate) fn assign_op( - mut left: Assignee, - right: R, - context: BinaryOperationCallContext, - op: fn(BinaryOperationCallContext, Owned, R) -> ExecutionResult, - ) -> ExecutionResult<()> { - let left_value = core::mem::replace(&mut *left, FloatValue::F32(0.0)); - let result = op(context, left_value.into_owned(), right)?; - *left = result; - Ok(()) - } - - fn to_unspanned_literal(self) -> Literal { - match self { - FloatValue::Untyped(float) => float.to_unspanned_literal(), - FloatValue::F32(float) => Literal::f32_suffixed(float), - FloatValue::F64(float) => Literal::f64_suffixed(float), - } - } } -impl HasValueKind for FloatValue { - type SpecificKind = FloatKind; - - fn kind(&self) -> FloatKind { - match self { - Self::Untyped(_) => FloatKind::Untyped, - Self::F32(_) => FloatKind::F32, - Self::F64(_) => FloatKind::F64, - } - } +fn assign_op( + mut left: Assignee, + right: R, + context: BinaryOperationCallContext, + op: fn(BinaryOperationCallContext, FloatValue, R) -> ExecutionResult, +) -> ExecutionResult<()> { + let left_value = core::mem::replace(&mut *left, FloatContent::F32(0.0)); + let result = op(context, left_value, right)?; + *left = result; + Ok(()) } -impl Debug for FloatValue { +impl Debug for FloatValueRef<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Untyped(v) => write!(f, "{}", v.into_fallback()), - Self::F32(v) => write!(f, "{:?}", v), - Self::F64(v) => write!(f, "{:?}", v), + FloatContent::Untyped(v) => write!(f, "{}", v.into_fallback()), + FloatContent::F32(v) => write!(f, "{:?}", v), + FloatContent::F64(v) => write!(f, "{:?}", v), } } } -impl FloatValue { - /// Aligns types for comparison - converts untyped to match the other's type. - /// Unlike integers, float conversion never fails (may lose precision). - fn align_types(mut lhs: Self, mut rhs: Self) -> (Self, Self) { - match (&lhs, &rhs) { - (FloatValue::Untyped(l), typed) if !matches!(typed, FloatValue::Untyped(_)) => { - lhs = l.into_kind(typed.kind()); - } - (typed, FloatValue::Untyped(r)) if !matches!(typed, FloatValue::Untyped(_)) => { - rhs = r.into_kind(lhs.kind()); - } - _ => {} // Both same type or both untyped - no conversion needed +/// Aligns types for comparison - converts untyped to match the other's type. +/// Unlike integers, float conversion never fails (may lose precision). +fn align_types(mut lhs: FloatValue, mut rhs: FloatValue) -> (FloatValue, FloatValue) { + match (&lhs, &rhs) { + (FloatContent::Untyped(l), typed) if !matches!(typed, FloatContent::Untyped(_)) => { + lhs = l.into_kind(typed.kind()); + } + (typed, FloatContent::Untyped(r)) if !matches!(typed, FloatContent::Untyped(_)) => { + rhs = r.into_kind(lhs.kind()); } - (lhs, rhs) + _ => {} // Both same type or both untyped - no conversion needed } + (lhs, rhs) } -impl ValuesEqual for FloatValue { +impl<'a> ValuesEqual for FloatValueRef<'a> { /// Handles type coercion between typed and untyped floats. /// Uses Rust's float `==`, so `NaN != NaN`. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { // Align types (untyped -> typed conversion) - let (lhs, rhs) = Self::align_types(*self, *other); + let lhs = self.clone_to_owned_infallible(); + let rhs = other.clone_to_owned_infallible(); + let (lhs, rhs) = align_types(lhs, rhs); // After alignment, compare directly. // Each variant has two lines: same-type comparison, then type-mismatch fallback. // This ensures adding a new variant causes a compiler error. let equal = match (lhs, rhs) { - (FloatValue::Untyped(l), FloatValue::Untyped(r)) => { + (FloatContent::Untyped(l), FloatContent::Untyped(r)) => { l.into_fallback() == r.into_fallback() } - (FloatValue::Untyped(_), _) => return ctx.leaf_values_not_equal(self, other), - (FloatValue::F32(l), FloatValue::F32(r)) => l == r, - (FloatValue::F32(_), _) => return ctx.leaf_values_not_equal(self, other), - (FloatValue::F64(l), FloatValue::F64(r)) => l == r, - (FloatValue::F64(_), _) => return ctx.leaf_values_not_equal(self, other), + (FloatContent::Untyped(_), _) => return ctx.leaf_values_not_equal(self, other), + (FloatContent::F32(l), FloatContent::F32(r)) => l == r, + (FloatContent::F32(_), _) => return ctx.leaf_values_not_equal(self, other), + (FloatContent::F64(l), FloatContent::F64(r)) => l == r, + (FloatContent::F64(_), _) => return ctx.leaf_values_not_equal(self, other), }; if equal { @@ -173,159 +167,158 @@ impl ValuesEqual for FloatValue { } } -define_interface! { - struct FloatTypeData, - parent: ValueTypeData, +define_type_features! { + impl FloatType, pub(crate) mod float_interface { pub(crate) mod methods { fn is_nan(this: FloatValue) -> bool { match this { - FloatValue::Untyped(x) => x.into_fallback().is_nan(), - FloatValue::F32(x) => x.is_nan(), - FloatValue::F64(x) => x.is_nan(), + FloatContent::Untyped(x) => x.into_fallback().is_nan(), + FloatContent::F32(x) => x.is_nan(), + FloatContent::F64(x) => x.is_nan(), } } fn is_infinite(this: FloatValue) -> bool { match this { - FloatValue::Untyped(x) => x.into_fallback().is_infinite(), - FloatValue::F32(x) => x.is_infinite(), - FloatValue::F64(x) => x.is_infinite(), + FloatContent::Untyped(x) => x.into_fallback().is_infinite(), + FloatContent::F32(x) => x.is_infinite(), + FloatContent::F64(x) => x.is_infinite(), } } fn is_finite(this: FloatValue) -> bool { match this { - FloatValue::Untyped(x) => x.into_fallback().is_finite(), - FloatValue::F32(x) => x.is_finite(), - FloatValue::F64(x) => x.is_finite(), + FloatContent::Untyped(x) => x.into_fallback().is_finite(), + FloatContent::F32(x) => x.is_finite(), + FloatContent::F64(x) => x.is_finite(), } } fn is_sign_positive(this: FloatValue) -> bool { match this { - FloatValue::Untyped(x) => x.into_fallback().is_sign_positive(), - FloatValue::F32(x) => x.is_sign_positive(), - FloatValue::F64(x) => x.is_sign_positive(), + FloatContent::Untyped(x) => x.into_fallback().is_sign_positive(), + FloatContent::F32(x) => x.is_sign_positive(), + FloatContent::F64(x) => x.is_sign_positive(), } } fn is_sign_negative(this: FloatValue) -> bool { match this { - FloatValue::Untyped(x) => x.into_fallback().is_sign_negative(), - FloatValue::F32(x) => x.is_sign_negative(), - FloatValue::F64(x) => x.is_sign_negative(), + FloatContent::Untyped(x) => x.into_fallback().is_sign_negative(), + FloatContent::F32(x) => x.is_sign_negative(), + FloatContent::F64(x) => x.is_sign_negative(), } } } pub(crate) mod unary_operations { } pub(crate) mod binary_operations { - fn add(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a + b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a + b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a + b), + fn add(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a + b), } } - [context] fn add_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, add) + [context] fn add_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, add) } - fn sub(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a - b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a - b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a - b), + fn sub(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a - b), } } - [context] fn sub_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, sub) + [context] fn sub_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, sub) } - fn mul(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a * b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a * b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a * b), + fn mul(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a * b), } } - [context] fn mul_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, mul) + [context] fn mul_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, mul) } - fn div(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a / b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a / b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a / b), + fn div(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a / b), } } - [context] fn div_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, div) + [context] fn div_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, div) } - fn rem(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_operation(right, |a, b| a % b), - FloatValue::F32(left) => left.paired_operation_no_overflow(right, |a, b| a % b), - FloatValue::F64(left) => left.paired_operation_no_overflow(right, |a, b| a % b), + fn rem(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_operation_no_overflow(right, |a, b| a % b), } } - [context] fn rem_assign(left: Assignee, right: Spanned>) -> ExecutionResult<()> { - FloatValue::assign_op(left, right, context, rem) + [context] fn rem_assign(left: Assignee, right: Spanned) -> ExecutionResult<()> { + assign_op(left, right, context, rem) } - fn lt(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a < b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a < b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a < b), + fn lt(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a < b), } } - fn le(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a <= b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a <= b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a <= b), + fn le(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a <= b), } } - fn gt(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a > b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a > b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a > b), + fn gt(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a > b), } } - fn ge(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a >= b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a >= b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a >= b), + fn ge(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a >= b), } } - fn eq(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a == b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a == b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a == b), + fn eq(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a == b), } } - fn ne(left: Owned, right: Spanned>) -> ExecutionResult { - match FloatValue::resolve_untyped_to_match(left, &right) { - FloatValue::Untyped(left) => left.paired_comparison(right, |a, b| a != b), - FloatValue::F32(left) => left.paired_comparison(right, |a, b| a != b), - FloatValue::F64(left) => left.paired_comparison(right, |a, b| a != b), + fn ne(left: FloatValue, right: Spanned) -> ExecutionResult { + 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), + FloatContent::F64(left) => left.paired_comparison(right, |a, b| a != b), } } } @@ -361,10 +354,10 @@ define_interface! { } impl_resolvable_argument_for! { - FloatTypeData, + FloatType, (value, context) -> FloatValue { match value { - Value::Float(value) => Ok(value), + AnyValue::Float(value) => Ok(value), other => context.err("a float", other), } } diff --git a/src/expressions/values/float_subtypes.rs b/src/expressions/values/float_subtypes.rs index 597db858..c3b21c33 100644 --- a/src/expressions/values/float_subtypes.rs +++ b/src/expressions/values/float_subtypes.rs @@ -4,9 +4,8 @@ macro_rules! impl_float_operations { ( $($type_data:ident mod $mod_name:ident: $float_enum_variant:ident($float_type:ident)),* $(,)? ) => {$( - define_interface! { - struct $type_data, - parent: FloatTypeData, + define_type_features! { + impl $type_data, pub(crate) mod $mod_name { pub(crate) mod methods { } @@ -89,24 +88,24 @@ macro_rules! impl_float_operations { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatKind::Untyped) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatKind::F32) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatKind::F64) => unary_definitions::cast_to_f64(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + AnyValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + AnyValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, } _ => return None, @@ -122,15 +121,15 @@ macro_rules! impl_float_operations { fn resolve_type_property( property_name: &str, - ) -> Option { + ) -> Option { match property_name { - "MAX" => Some($float_type::MAX.into_value()), - "MIN" => Some($float_type::MIN.into_value()), - "MIN_POSITIVE" => Some($float_type::MIN_POSITIVE.into_value()), - "INFINITY" => Some($float_type::INFINITY.into_value()), - "NEG_INFINITY" => Some($float_type::NEG_INFINITY.into_value()), - "NAN" => Some($float_type::NAN.into_value()), - "EPSILON" => Some($float_type::EPSILON.into_value()), + "MAX" => Some($float_type::MAX.into_any_value()), + "MIN" => Some($float_type::MIN.into_any_value()), + "MIN_POSITIVE" => Some($float_type::MIN_POSITIVE.into_any_value()), + "INFINITY" => Some($float_type::INFINITY.into_any_value()), + "NEG_INFINITY" => Some($float_type::NEG_INFINITY.into_any_value()), + "NAN" => Some($float_type::NAN.into_any_value()), + "EPSILON" => Some($float_type::EPSILON.into_any_value()), _ => None, } } @@ -138,21 +137,6 @@ macro_rules! impl_float_operations { } } - impl HasValueKind for $float_type { - type SpecificKind = FloatKind; - - fn kind(&self) -> FloatKind { - FloatKind::$float_enum_variant - } - } - - impl IntoValue for $float_type { - fn into_value(self) -> Value { - Value::Float(FloatValue::$float_enum_variant(self)) - } - } - - impl HandleBinaryOperation for $float_type { fn type_name() -> &'static str { stringify!($integer_type) @@ -161,17 +145,59 @@ macro_rules! impl_float_operations { )*}; } -impl_float_operations!(F32TypeData mod f32_interface: F32(f32), F64TypeData mod f64_interface: F64(f64)); +impl_float_operations!(F32Type mod f32_interface: F32(f32), F64Type mod f64_interface: F64(f64)); macro_rules! impl_resolvable_float_subtype { - ($value_type:ty, $type:ty, $variant:ident, $expected_msg:expr) => { + ($type_def:ident, $kind:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { + define_leaf_type! { + pub(crate) $type_def => FloatType(FloatContent::$variant) => AnyType, + content: $type, + kind: pub(crate) $kind, + type_name: $type_name, + articled_display_name: $articled_display_name, + dyn_impls: {}, + } + + impl IsArgument for OptionalSuffix<$type> { + type ValueType = FloatType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + + fn from_argument(argument: Spanned) -> ExecutionResult { + argument.expect_owned().resolve_as("This argument") + } + } + + impl ResolveAs> for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + let span = self.span_range(); + let float_value: FloatValue = self.resolve_as(resolution_target)?; + Spanned(float_value, span).resolve_as(resolution_target) + } + } + + impl ResolveAs> for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + let Spanned(value, span) = self; + match value { + FloatContent::Untyped(v) => Ok(OptionalSuffix(v.into_fallback() as $type)), + FloatContent::$variant(v) => Ok(OptionalSuffix(v)), + v => span.type_err(format!( + "{} is expected to be {}, but it is {}", + resolution_target, + $kind.articled_display_name(), + v.articled_kind(), + )), + } + } + } + impl ResolvableArgumentTarget for $type { - type ValueType = $value_type; + type ValueType = $type_def; } impl From<$type> for FloatValue { fn from(value: $type) -> Self { - FloatValue::$variant(value) + FloatContent::$variant(value) } } @@ -181,50 +207,50 @@ macro_rules! impl_resolvable_float_subtype { context: ResolutionContext, ) -> ExecutionResult { match value { - FloatValue::Untyped(x) => Ok(x.into_fallback() as $type), - FloatValue::$variant(x) => Ok(x), - other => context.err($expected_msg, other), + FloatContent::Untyped(x) => Ok(x.into_fallback() as $type), + FloatContent::$variant(x) => Ok(x), + other => context.err($articled_display_name, other), } } } - impl ResolvableOwned for $type { + impl ResolvableOwned for $type { fn resolve_from_value( - value: Value, + value: AnyValue, context: ResolutionContext, ) -> ExecutionResult { match value { - Value::Float(x) => <$type>::resolve_from_value(x, context), - other => context.err($expected_msg, other), + AnyValue::Float(x) => <$type>::resolve_from_value(x, context), + other => context.err($articled_display_name, other), } } } - impl ResolvableShared for $type { + impl ResolvableShared for $type { fn resolve_from_ref<'a>( - value: &'a Value, + value: &'a AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a Self> { match value { - Value::Float(FloatValue::$variant(x)) => Ok(x), - other => context.err($expected_msg, other), + AnyValueContent::Float(FloatContent::$variant(x)) => Ok(x), + other => context.err($articled_display_name, other), } } } - impl ResolvableMutable for $type { + impl ResolvableMutable for $type { fn resolve_from_mut<'a>( - value: &'a mut Value, + value: &'a mut AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a mut Self> { match value { - Value::Float(FloatValue::$variant(x)) => Ok(x), - other => context.err($expected_msg, other), + AnyValueContent::Float(FloatContent::$variant(x)) => Ok(x), + other => context.err($articled_display_name, other), } } } }; } -impl_resolvable_float_subtype!(F32TypeData, f32, F32, "an f32"); -impl_resolvable_float_subtype!(F64TypeData, f64, F64, "an f64"); +impl_resolvable_float_subtype!(F32Type, F32Kind, f32, F32, "f32", "an f32"); +impl_resolvable_float_subtype!(F64Type, F64Kind, f64, F64, "f64", "an f64"); diff --git a/src/expressions/values/float_untyped.rs b/src/expressions/values/float_untyped.rs index 43ab7d00..360feb4f 100644 --- a/src/expressions/values/float_untyped.rs +++ b/src/expressions/values/float_untyped.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) UntypedFloatType => FloatType(FloatContent::Untyped) => AnyType, + content: UntypedFloat, + kind: pub(crate) UntypedFloatKind, + type_name: "untyped_float", + articled_display_name: "an untyped float", + dyn_impls: {}, +} + #[derive(Copy, Clone)] pub(crate) struct UntypedFloat(FallbackFloat); pub(crate) type FallbackFloat = f64; @@ -17,42 +26,42 @@ impl UntypedFloat { /// Converts an untyped float to a specific float kind. /// Unlike integers, float conversion never fails (may lose precision). - pub(crate) fn into_kind(self, kind: FloatKind) -> FloatValue { + pub(crate) fn into_kind(self, kind: FloatLeafKind) -> FloatValue { match kind { - FloatKind::Untyped => FloatValue::Untyped(self), - FloatKind::F32 => FloatValue::F32(self.0 as f32), - FloatKind::F64 => FloatValue::F64(self.0), + FloatLeafKind::Untyped(_) => FloatContent::Untyped(self), + FloatLeafKind::F32(_) => FloatContent::F32(self.0 as f32), + FloatLeafKind::F64(_) => FloatContent::F64(self.0), } } pub(crate) fn paired_operation( self, - rhs: Spanned>, + rhs: Spanned, perform_fn: fn(FallbackFloat, FallbackFloat) -> FallbackFloat, ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedFloat = rhs.resolve_as("This operand")?; + let rhs: UntypedFloat = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; let output = perform_fn(lhs, rhs); - Ok(FloatValue::Untyped(UntypedFloat::from_fallback(output))) + Ok(FloatContent::Untyped(UntypedFloat::from_fallback(output))) } pub(crate) fn paired_comparison( self, - rhs: Spanned>, + rhs: Spanned, compare_fn: fn(FallbackFloat, FallbackFloat) -> bool, ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedFloat = rhs.resolve_as("This operand")?; + let rhs: UntypedFloat = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; Ok(compare_fn(lhs, rhs)) } - pub(super) fn into_fallback(self) -> FallbackFloat { + pub(crate) fn into_fallback(self) -> FallbackFloat { self.0 } - pub(super) fn from_fallback(value: FallbackFloat) -> Self { + pub(crate) fn from_fallback(value: FallbackFloat) -> Self { Self(value) } @@ -61,23 +70,8 @@ impl UntypedFloat { } } -impl HasValueKind for UntypedFloat { - type SpecificKind = FloatKind; - - fn kind(&self) -> FloatKind { - FloatKind::Untyped - } -} - -impl IntoValue for UntypedFloat { - fn into_value(self) -> Value { - Value::Float(FloatValue::Untyped(self)) - } -} - -define_interface! { - struct UntypedFloatTypeData, - parent: FloatTypeData, +define_type_features! { + impl UntypedFloatType, pub(crate) mod untyped_float_interface { pub(crate) mod methods { } @@ -160,24 +154,24 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatKind::Untyped) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatKind::F32) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatKind::F64) => unary_definitions::cast_to_f64(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + AnyValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + AnyValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, @@ -196,12 +190,23 @@ define_interface! { pub(crate) struct UntypedFloatFallback(pub FallbackFloat); +impl IsArgument for UntypedFloatFallback { + type ValueType = UntypedFloatType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument(value: Spanned) -> ExecutionResult { + Self::resolve_value(value.expect_owned(), "This argument") + } +} + impl ResolvableArgumentTarget for UntypedFloatFallback { - type ValueType = UntypedFloatTypeData; + type ValueType = UntypedFloatType; } -impl ResolvableOwned for UntypedFloatFallback { - fn resolve_from_value(input_value: Value, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for UntypedFloatFallback { + fn resolve_from_value( + input_value: AnyValue, + context: ResolutionContext, + ) -> ExecutionResult { let value = UntypedFloat::resolve_from_value(input_value, context)?; Ok(UntypedFloatFallback(value.0)) } @@ -210,17 +215,17 @@ impl ResolvableOwned for UntypedFloatFallback { impl ResolvableOwned for UntypedFloat { fn resolve_from_value(value: FloatValue, context: ResolutionContext) -> ExecutionResult { match value { - FloatValue::Untyped(value) => Ok(value), + FloatContent::Untyped(value) => Ok(value), _ => context.err("an untyped float", value), } } } impl_resolvable_argument_for! { - UntypedFloatTypeData, + UntypedFloatType, (value, context) -> UntypedFloat { match value { - Value::Float(FloatValue::Untyped(x)) => Ok(x), + AnyValueContent::Float(FloatContent::Untyped(x)) => Ok(x), other => context.err("an untyped float", other), } } diff --git a/src/expressions/values/integer.rs b/src/expressions/values/integer.rs index 80274fe3..532f267b 100644 --- a/src/expressions/values/integer.rs +++ b/src/expressions/values/integer.rs @@ -1,51 +1,54 @@ use super::*; -#[derive(Copy, Clone)] -pub(crate) enum IntegerValue { - Untyped(UntypedInteger), - U8(u8), - U16(u16), - U32(u32), - U64(u64), - U128(u128), - Usize(usize), - I8(i8), - I16(i16), - I32(i32), - I64(i64), - I128(i128), - Isize(isize), +define_parent_type! { + pub(crate) IntegerType => AnyType(AnyValueContent::Integer), + content: pub(crate) IntegerContent, + leaf_kind: pub(crate) IntegerLeafKind, + type_kind: ParentTypeKind::Integer(pub(crate) IntegerTypeKind), + variants: { + Untyped => UntypedIntegerType, + U8 => U8Type, + U16 => U16Type, + U32 => U32Type, + U64 => U64Type, + U128 => U128Type, + Usize => UsizeType, + I8 => I8Type, + I16 => I16Type, + I32 => I32Type, + I64 => I64Type, + I128 => I128Type, + Isize => IsizeType, + }, + type_name: "int", + articled_display_name: "an integer", } -impl IntoValue for IntegerValue { - fn into_value(self) -> Value { - Value::Integer(self) - } -} +pub(crate) type IntegerValue = IntegerContent<'static, BeOwned>; +pub(crate) type IntegerValueRef<'a> = IntegerContent<'a, BeRef>; impl IntegerValue { - pub(super) fn for_litint(lit: &syn::LitInt) -> ParseResult> { + pub(super) fn for_litint(lit: &syn::LitInt) -> ParseResult { Ok(match lit.suffix() { - "" => Self::Untyped(UntypedInteger::new_from_lit_int(lit)?), - "u8" => Self::U8(lit.base10_parse()?), - "u16" => Self::U16(lit.base10_parse()?), - "u32" => Self::U32(lit.base10_parse()?), - "u64" => Self::U64(lit.base10_parse()?), - "u128" => Self::U128(lit.base10_parse()?), - "usize" => Self::Usize(lit.base10_parse()?), - "i8" => Self::I8(lit.base10_parse()?), - "i16" => Self::I16(lit.base10_parse()?), - "i32" => Self::I32(lit.base10_parse()?), - "i64" => Self::I64(lit.base10_parse()?), - "i128" => Self::I128(lit.base10_parse()?), - "isize" => Self::Isize(lit.base10_parse()?), + "" => IntegerContent::Untyped(UntypedInteger::new_from_lit_int(lit)?), + "u8" => IntegerContent::U8(lit.base10_parse()?), + "u16" => IntegerContent::U16(lit.base10_parse()?), + "u32" => IntegerContent::U32(lit.base10_parse()?), + "u64" => IntegerContent::U64(lit.base10_parse()?), + "u128" => IntegerContent::U128(lit.base10_parse()?), + "usize" => IntegerContent::Usize(lit.base10_parse()?), + "i8" => IntegerContent::I8(lit.base10_parse()?), + "i16" => IntegerContent::I16(lit.base10_parse()?), + "i32" => IntegerContent::I32(lit.base10_parse()?), + "i64" => IntegerContent::I64(lit.base10_parse()?), + "i128" => IntegerContent::I128(lit.base10_parse()?), + "isize" => IntegerContent::Isize(lit.base10_parse()?), suffix => { return lit.span().parse_err(format!( "The literal suffix {suffix} is not supported in preinterpret expressions" )); } - } - .into_owned()) + }) } pub(super) fn to_literal(self, span: Span) -> Literal { @@ -53,11 +56,11 @@ impl IntegerValue { } pub(crate) fn resolve_untyped_to_match_other( - Spanned(Owned(value), span): Spanned>, - other: &Value, + Spanned(value, span): Spanned, + other: &AnyValue, ) -> ExecutionResult { match (value, other) { - (IntegerValue::Untyped(this), Value::Integer(other)) => { + (IntegerValue::Untyped(this), AnyValue::Integer(other)) => { this.spanned(span).into_kind(other.kind()) } (value, _) => Ok(value), @@ -65,7 +68,7 @@ impl IntegerValue { } pub(crate) fn resolve_untyped_to_match( - Spanned(Owned(value), span): Spanned>, + Spanned(value, span): Spanned, target: &IntegerValue, ) -> ExecutionResult { match value { @@ -80,12 +83,12 @@ impl IntegerValue { context: BinaryOperationCallContext, op: fn( BinaryOperationCallContext, - Spanned>, + Spanned, R, ) -> ExecutionResult, ) -> ExecutionResult<()> { let left_value = core::mem::replace(&mut *left, IntegerValue::U32(0)); - let result = op(context, Spanned(Owned(left_value), left_span), right)?; + let result = op(context, Spanned(left_value, left_span), right)?; *left = result; Ok(()) } @@ -109,29 +112,7 @@ impl IntegerValue { } } -impl HasValueKind for IntegerValue { - type SpecificKind = IntegerKind; - - fn kind(&self) -> IntegerKind { - match self { - Self::Untyped(_) => IntegerKind::Untyped, - Self::U8(_) => IntegerKind::U8, - Self::U16(_) => IntegerKind::U16, - Self::U32(_) => IntegerKind::U32, - Self::U64(_) => IntegerKind::U64, - Self::U128(_) => IntegerKind::U128, - Self::Usize(_) => IntegerKind::Usize, - Self::I8(_) => IntegerKind::I8, - Self::I16(_) => IntegerKind::I16, - Self::I32(_) => IntegerKind::I32, - Self::I64(_) => IntegerKind::I64, - Self::I128(_) => IntegerKind::I128, - Self::Isize(_) => IntegerKind::Isize, - } - } -} - -impl Debug for IntegerValue { +impl Debug for IntegerValueRef<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Untyped(v) => write!(f, "{}", v.into_fallback()), @@ -151,29 +132,32 @@ impl Debug for IntegerValue { } } -impl IntegerValue { - /// Aligns types for comparison - converts untyped to match the other's type. - /// Returns `None` if the untyped value doesn't fit in the target type. - fn align_types(mut lhs: Self, mut rhs: Self) -> Option<(Self, Self)> { - match (&lhs, &rhs) { - (IntegerValue::Untyped(l), typed) if !matches!(typed, IntegerValue::Untyped(_)) => { - lhs = l.try_into_kind(typed.kind())?; - } - (typed, IntegerValue::Untyped(r)) if !matches!(typed, IntegerValue::Untyped(_)) => { - rhs = r.try_into_kind(lhs.kind())?; - } - _ => {} // Both same type or both untyped - no conversion needed +/// Aligns types for comparison - converts untyped to match the other's type. +/// Returns `None` if the untyped value doesn't fit in the target type. +fn align_types( + mut lhs: IntegerValue, + mut rhs: IntegerValue, +) -> Option<(IntegerValue, IntegerValue)> { + match (&lhs, &rhs) { + (IntegerValue::Untyped(l), typed) if !matches!(typed, IntegerValue::Untyped(_)) => { + lhs = l.try_into_kind(typed.kind())?; + } + (typed, IntegerValue::Untyped(r)) if !matches!(typed, IntegerValue::Untyped(_)) => { + rhs = r.try_into_kind(lhs.kind())?; } - Some((lhs, rhs)) + _ => {} // Both same type or both untyped - no conversion needed } + Some((lhs, rhs)) } -impl ValuesEqual for IntegerValue { +impl<'a> ValuesEqual for IntegerValueRef<'a> { /// Handles type coercion between typed and untyped integers. /// E.g., `5 == 5u32` returns true. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { // Align types (untyped -> typed conversion) - let Some((lhs, rhs)) = Self::align_types(*self, *other) else { + let lhs = self.clone_to_owned_infallible(); + let rhs = other.clone_to_owned_infallible(); + let Some((lhs, rhs)) = align_types(lhs, rhs) else { return ctx.leaf_values_not_equal(self, other); }; @@ -219,16 +203,15 @@ impl ValuesEqual for IntegerValue { } } -define_interface! { - struct IntegerTypeData, - parent: ValueTypeData, +define_type_features! { + impl IntegerType, pub(crate) mod integer_interface { pub(crate) mod methods { } pub(crate) mod unary_operations { } pub(crate) mod binary_operations { - [context] fn add(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn add(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -246,11 +229,11 @@ define_interface! { } } - [context] fn add_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn add_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, add) } - [context] fn sub(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn sub(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -268,11 +251,11 @@ define_interface! { } } - [context] fn sub_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn sub_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, sub) } - [context] fn mul(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn mul(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -290,11 +273,11 @@ define_interface! { } } - [context] fn mul_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn mul_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, mul) } - [context] fn div(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn div(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -312,11 +295,11 @@ define_interface! { } } - [context] fn div_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn div_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, div) } - [context] fn rem(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn rem(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -334,11 +317,11 @@ define_interface! { } } - [context] fn rem_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn rem_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, rem) } - [context] fn bitxor(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn bitxor(left: Spanned, right: Spanned) -> ExecutionResult { 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)), @@ -356,11 +339,11 @@ define_interface! { } } - [context] fn bitxor_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn bitxor_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, bitxor) } - [context] fn bitand(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn bitand(left: Spanned, right: Spanned) -> ExecutionResult { 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)), @@ -378,11 +361,11 @@ define_interface! { } } - [context] fn bitand_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn bitand_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, bitand) } - [context] fn bitor(left: Spanned>, right: Spanned>) -> ExecutionResult { + [context] fn bitor(left: Spanned, right: Spanned) -> ExecutionResult { 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)), @@ -400,12 +383,12 @@ define_interface! { } } - [context] fn bitor_assign(lhs: Spanned>, rhs: Spanned>) -> ExecutionResult<()> { + [context] fn bitor_assign(lhs: Spanned>, rhs: Spanned) -> ExecutionResult<()> { IntegerValue::assign_op(lhs, rhs, context, bitor) } - [context] fn shift_left(lhs: Spanned>, CoercedToU32(right): CoercedToU32) -> ExecutionResult { - match lhs.0.into_inner() { + [context] fn shift_left(lhs: Spanned, CoercedToU32(right): CoercedToU32) -> ExecutionResult { + 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), IntegerValue::U16(left) => left.shift_operation(right, context, u16::checked_shl), @@ -426,8 +409,8 @@ define_interface! { IntegerValue::assign_op(lhs, rhs, context, shift_left) } - [context] fn shift_right(lhs: Spanned>, CoercedToU32(right): CoercedToU32) -> ExecutionResult { - match lhs.0.into_inner() { + [context] fn shift_right(lhs: Spanned, CoercedToU32(right): CoercedToU32) -> ExecutionResult { + 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), IntegerValue::U16(left) => left.shift_operation(right, context, u16::checked_shr), @@ -448,7 +431,7 @@ define_interface! { IntegerValue::assign_op(lhs, rhs, context, shift_right) } - fn lt(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn lt(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -466,7 +449,7 @@ define_interface! { } } - fn le(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn le(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -484,7 +467,7 @@ define_interface! { } } - fn gt(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn gt(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -502,7 +485,7 @@ define_interface! { } } - fn ge(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn ge(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -520,7 +503,7 @@ define_interface! { } } - fn eq(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn eq(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -538,7 +521,7 @@ define_interface! { } } - fn ne(left: Spanned>, right: Spanned>) -> ExecutionResult { + fn ne(left: Spanned, right: Spanned) -> ExecutionResult { 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), @@ -599,10 +582,10 @@ define_interface! { } impl_resolvable_argument_for! { - IntegerTypeData, + IntegerType, (value, context) -> IntegerValue { match value { - Value::Integer(value) => Ok(value), + AnyValue::Integer(value) => Ok(value), other => context.err("an integer", other), } } @@ -610,14 +593,25 @@ impl_resolvable_argument_for! { 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 { + Self::resolve_value(value.expect_owned(), "This argument") + } +} + impl ResolvableArgumentTarget for CoercedToU32 { - type ValueType = IntegerTypeData; + type ValueType = IntegerType; } -impl ResolvableOwned for CoercedToU32 { - fn resolve_from_value(input_value: Value, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for CoercedToU32 { + fn resolve_from_value( + input_value: AnyValue, + context: ResolutionContext, + ) -> ExecutionResult { let integer = match input_value { - Value::Integer(value) => value, + AnyValue::Integer(value) => value, other => return context.err("an integer", other), }; let coerced = match integer { @@ -637,7 +631,7 @@ impl ResolvableOwned for CoercedToU32 { }; match coerced { Some(value) => Ok(CoercedToU32(value)), - None => context.err("a u32-compatible integer", Value::Integer(integer)), + None => context.err("a u32-compatible integer", AnyValue::Integer(integer)), } } } diff --git a/src/expressions/values/integer_subtypes.rs b/src/expressions/values/integer_subtypes.rs index 71b93d35..ef053838 100644 --- a/src/expressions/values/integer_subtypes.rs +++ b/src/expressions/values/integer_subtypes.rs @@ -5,17 +5,15 @@ macro_rules! impl_int_operations { ( $($integer_type_data:ident mod $mod_name:ident: [$(CharCast[$char_cast:ident],)?$(Signed[$signed:ident],)?] $integer_enum_variant:ident($integer_type:ident)),* $(,)? ) => {$( - define_interface! { - struct $integer_type_data, - parent: IntegerTypeData, + define_type_features! { + impl $integer_type_data, pub(crate) mod $mod_name { pub(crate) mod methods { } pub(crate) mod unary_operations { $( - fn neg(Spanned(value, span): Spanned>) -> ExecutionResult<$integer_type> { + fn neg(Spanned(value, span): Spanned<$integer_type>) -> ExecutionResult<$integer_type> { ignore_all!($signed); // Include only for signed types - let value = value.into_inner(); match value.checked_neg() { Some(negated) => Ok(negated), None => span.value_err("Negating this value would overflow"), @@ -109,30 +107,30 @@ macro_rules! impl_int_operations { unary_definitions::neg() } )? - UnaryOperation::Cast { target, .. } => match target { + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { $( - CastTarget::Char => { + AnyValueLeafKind::Char(_) => { ignore_all!($char_cast); // Only include for types with CharCast unary_definitions::cast_to_char() } )? - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatKind::Untyped) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatKind::F32) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatKind::F64) => unary_definitions::cast_to_f64(), - CastTarget::String => unary_definitions::cast_to_string(), + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + AnyValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + AnyValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, } _ => return None, @@ -148,10 +146,10 @@ macro_rules! impl_int_operations { fn resolve_type_property( property_name: &str, - ) -> Option { + ) -> Option { match property_name { - "MAX" => Some($integer_type::MAX.into_value()), - "MIN" => Some($integer_type::MIN.into_value()), + "MAX" => Some($integer_type::MAX.into_any_value()), + "MIN" => Some($integer_type::MIN.into_any_value()), _ => None, } } @@ -159,20 +157,6 @@ macro_rules! impl_int_operations { } } - impl HasValueKind for $integer_type { - type SpecificKind = IntegerKind; - - fn kind(&self) -> IntegerKind { - IntegerKind::$integer_enum_variant - } - } - - impl IntoValue for $integer_type { - fn into_value(self) -> Value { - Value::Integer(IntegerValue::$integer_enum_variant(self)) - } - } - impl HandleBinaryOperation for $integer_type { fn type_name() -> &'static str { stringify!($integer_type) @@ -182,24 +166,66 @@ macro_rules! impl_int_operations { } impl_int_operations!( - U8TypeData mod u8_interface: [CharCast[yes],] U8(u8), - U16TypeData mod u16_interface: [] U16(u16), - U32TypeData mod u32_interface: [] U32(u32), - U64TypeData mod u64_interface: [] U64(u64), - U128TypeData mod u128_interface: [] U128(u128), - UsizeTypeData mod usize_interface: [] Usize(usize), - I8TypeData mod i8_interface: [Signed[yes],] I8(i8), - I16TypeData mod i16_interface: [Signed[yes],] I16(i16), - I32TypeData mod i32_interface: [Signed[yes],] I32(i32), - I64TypeData mod i64_interface: [Signed[yes],] I64(i64), - I128TypeData mod i128_interface: [Signed[yes],] I128(i128), - IsizeTypeData mod isize_interface: [Signed[yes],] Isize(isize), + U8Type mod u8_interface: [CharCast[yes],] U8(u8), + U16Type mod u16_interface: [] U16(u16), + U32Type mod u32_interface: [] U32(u32), + U64Type mod u64_interface: [] U64(u64), + U128Type mod u128_interface: [] U128(u128), + UsizeType mod usize_interface: [] Usize(usize), + I8Type mod i8_interface: [Signed[yes],] I8(i8), + I16Type mod i16_interface: [Signed[yes],] I16(i16), + I32Type mod i32_interface: [Signed[yes],] I32(i32), + I64Type mod i64_interface: [Signed[yes],] I64(i64), + I128Type mod i128_interface: [Signed[yes],] I128(i128), + IsizeType mod isize_interface: [Signed[yes],] Isize(isize), ); macro_rules! impl_resolvable_integer_subtype { - ($value_type:ty, $type:ty, $variant:ident, $expected_msg:expr) => { + ($type_def:ident, $kind:ident, $type:ty, $variant:ident, $type_name:literal, $articled_display_name:expr) => { + define_leaf_type! { + pub(crate) $type_def => IntegerType(IntegerContent::$variant) => AnyType, + content: $type, + kind: pub(crate) $kind, + type_name: $type_name, + articled_display_name: $articled_display_name, + dyn_impls: {}, + } + + impl IsArgument for OptionalSuffix<$type> { + type ValueType = IntegerType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + + fn from_argument(argument: Spanned) -> ExecutionResult { + argument.expect_owned().resolve_as("This argument") + } + } + + impl ResolveAs> for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + let span = self.span_range(); + let integer_value: IntegerValue = self.resolve_as(resolution_target)?; + Spanned(integer_value, span).resolve_as(resolution_target) + } + } + + impl ResolveAs> for Spanned { + fn resolve_as(self, resolution_target: &str) -> ExecutionResult> { + let Spanned(value, span) = self; + match value { + IntegerValue::Untyped(v) => Ok(OptionalSuffix(v.into_fallback() as $type)), + IntegerValue::$variant(v) => Ok(OptionalSuffix(v)), + v => span.type_err(format!( + "{} is expected to be {}, but it is {}", + resolution_target, + $kind.articled_display_name(), + v.articled_kind(), + )), + } + } + } + impl ResolvableArgumentTarget for $type { - type ValueType = $value_type; + type ValueType = $type_def; } impl From<$type> for IntegerValue { @@ -216,58 +242,58 @@ macro_rules! impl_resolvable_integer_subtype { match value { IntegerValue::Untyped(x) => Ok(x.into_fallback() as $type), IntegerValue::$variant(x) => Ok(x), - other => context.err($expected_msg, other), + other => context.err($articled_display_name, other), } } } - impl ResolvableOwned for $type { + impl ResolvableOwned for $type { fn resolve_from_value( - value: Value, + value: AnyValue, context: ResolutionContext, ) -> ExecutionResult { match value { - Value::Integer(x) => <$type>::resolve_from_value(x, context), - other => context.err($expected_msg, other), + AnyValue::Integer(x) => <$type>::resolve_from_value(x, context), + other => context.err($articled_display_name, other), } } } - impl ResolvableShared for $type { + impl ResolvableShared for $type { fn resolve_from_ref<'a>( - value: &'a Value, + value: &'a AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a Self> { match value { - Value::Integer(IntegerValue::$variant(x)) => Ok(x), - other => context.err($expected_msg, other), + AnyValue::Integer(IntegerValue::$variant(x)) => Ok(x), + other => context.err($articled_display_name, other), } } } - impl ResolvableMutable for $type { + impl ResolvableMutable for $type { fn resolve_from_mut<'a>( - value: &'a mut Value, + value: &'a mut AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a mut Self> { match value { - Value::Integer(IntegerValue::$variant(x)) => Ok(x), - other => context.err($expected_msg, other), + AnyValue::Integer(IntegerValue::$variant(x)) => Ok(x), + other => context.err($articled_display_name, other), } } } }; } -impl_resolvable_integer_subtype!(I8TypeData, i8, I8, "an i8"); -impl_resolvable_integer_subtype!(I16TypeData, i16, I16, "an i16"); -impl_resolvable_integer_subtype!(I32TypeData, i32, I32, "an i32"); -impl_resolvable_integer_subtype!(I64TypeData, i64, I64, "an i64"); -impl_resolvable_integer_subtype!(I128TypeData, i128, I128, "an i128"); -impl_resolvable_integer_subtype!(IsizeTypeData, isize, Isize, "an isize"); -impl_resolvable_integer_subtype!(U8TypeData, u8, U8, "a u8"); -impl_resolvable_integer_subtype!(U16TypeData, u16, U16, "a u16"); -impl_resolvable_integer_subtype!(U32TypeData, u32, U32, "a u32"); -impl_resolvable_integer_subtype!(U64TypeData, u64, U64, "a u64"); -impl_resolvable_integer_subtype!(U128TypeData, u128, U128, "a u128"); -impl_resolvable_integer_subtype!(UsizeTypeData, usize, Usize, "a usize"); +impl_resolvable_integer_subtype!(I8Type, I8Kind, i8, I8, "i8", "an i8"); +impl_resolvable_integer_subtype!(I16Type, I16Kind, i16, I16, "i16", "an i16"); +impl_resolvable_integer_subtype!(I32Type, I32Kind, i32, I32, "i32", "an i32"); +impl_resolvable_integer_subtype!(I64Type, I64Kind, i64, I64, "i64", "an i64"); +impl_resolvable_integer_subtype!(I128Type, I128Kind, i128, I128, "i128", "an i128"); +impl_resolvable_integer_subtype!(IsizeType, IsizeKind, isize, Isize, "isize", "an isize"); +impl_resolvable_integer_subtype!(U8Type, U8Kind, u8, U8, "u8", "a u8"); +impl_resolvable_integer_subtype!(U16Type, U16Kind, u16, U16, "u16", "a u16"); +impl_resolvable_integer_subtype!(U32Type, U32Kind, u32, U32, "u32", "a u32"); +impl_resolvable_integer_subtype!(U64Type, U64Kind, u64, U64, "u64", "a u64"); +impl_resolvable_integer_subtype!(U128Type, U128Kind, u128, U128, "u128", "a u128"); +impl_resolvable_integer_subtype!(UsizeType, UsizeKind, usize, Usize, "usize", "a usize"); diff --git a/src/expressions/values/integer_untyped.rs b/src/expressions/values/integer_untyped.rs index fe4fcfd6..0a38b75a 100644 --- a/src/expressions/values/integer_untyped.rs +++ b/src/expressions/values/integer_untyped.rs @@ -1,5 +1,14 @@ use super::*; +define_leaf_type! { + pub(crate) UntypedIntegerType => IntegerType(IntegerContent::Untyped) => AnyType, + content: UntypedInteger, + kind: pub(crate) UntypedIntegerKind, + type_name: "untyped_int", + articled_display_name: "an untyped integer", + dyn_impls: {}, +} + #[derive(Copy, Clone)] pub(crate) struct UntypedInteger(FallbackInteger); pub(crate) type FallbackInteger = i128; @@ -30,33 +39,33 @@ impl UntypedInteger { } /// Tries to convert to a specific integer kind, returning None if the value doesn't fit. - pub(crate) fn try_into_kind(self, kind: IntegerKind) -> Option { + pub(crate) fn try_into_kind(self, kind: IntegerLeafKind) -> Option { let value = self.0; Some(match kind { - IntegerKind::Untyped => IntegerValue::Untyped(UntypedInteger(value)), - IntegerKind::I8 => IntegerValue::I8(value.try_into().ok()?), - IntegerKind::I16 => IntegerValue::I16(value.try_into().ok()?), - IntegerKind::I32 => IntegerValue::I32(value.try_into().ok()?), - IntegerKind::I64 => IntegerValue::I64(value.try_into().ok()?), - IntegerKind::I128 => IntegerValue::I128(value), - IntegerKind::Isize => IntegerValue::Isize(value.try_into().ok()?), - IntegerKind::U8 => IntegerValue::U8(value.try_into().ok()?), - IntegerKind::U16 => IntegerValue::U16(value.try_into().ok()?), - IntegerKind::U32 => IntegerValue::U32(value.try_into().ok()?), - IntegerKind::U64 => IntegerValue::U64(value.try_into().ok()?), - IntegerKind::U128 => IntegerValue::U128(value.try_into().ok()?), - IntegerKind::Usize => IntegerValue::Usize(value.try_into().ok()?), + IntegerLeafKind::Untyped(_) => IntegerValue::Untyped(UntypedInteger(value)), + IntegerLeafKind::I8(_) => IntegerValue::I8(value.try_into().ok()?), + IntegerLeafKind::I16(_) => IntegerValue::I16(value.try_into().ok()?), + IntegerLeafKind::I32(_) => IntegerValue::I32(value.try_into().ok()?), + IntegerLeafKind::I64(_) => IntegerValue::I64(value.try_into().ok()?), + IntegerLeafKind::I128(_) => IntegerValue::I128(value), + IntegerLeafKind::Isize(_) => IntegerValue::Isize(value.try_into().ok()?), + IntegerLeafKind::U8(_) => IntegerValue::U8(value.try_into().ok()?), + IntegerLeafKind::U16(_) => IntegerValue::U16(value.try_into().ok()?), + IntegerLeafKind::U32(_) => IntegerValue::U32(value.try_into().ok()?), + IntegerLeafKind::U64(_) => IntegerValue::U64(value.try_into().ok()?), + IntegerLeafKind::U128(_) => IntegerValue::U128(value.try_into().ok()?), + IntegerLeafKind::Usize(_) => IntegerValue::Usize(value.try_into().ok()?), }) } pub(crate) fn paired_operation( self, - rhs: Spanned>, + rhs: Spanned, context: BinaryOperationCallContext, perform_fn: fn(FallbackInteger, FallbackInteger) -> Option, ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedInteger = rhs.resolve_as("This operand")?; + let rhs: UntypedInteger = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; let output = perform_fn(lhs, rhs) .ok_or_else(|| UntypedInteger::binary_overflow_error(context, lhs, rhs))?; @@ -65,11 +74,11 @@ impl UntypedInteger { pub(crate) fn paired_comparison( self, - rhs: Spanned>, + rhs: Spanned, compare_fn: fn(FallbackInteger, FallbackInteger) -> bool, ) -> ExecutionResult { let lhs = self.0; - let rhs: UntypedInteger = rhs.resolve_as("This operand")?; + let rhs: UntypedInteger = rhs.downcast_resolve("This operand")?; let rhs = rhs.0; Ok(compare_fn(lhs, rhs)) } @@ -100,7 +109,7 @@ impl UntypedInteger { } impl Spanned { - pub(crate) fn into_kind(self, kind: IntegerKind) -> ExecutionResult { + pub(crate) fn into_kind(self, kind: IntegerLeafKind) -> ExecutionResult { let Spanned(value, span_range) = self; value.try_into_kind(kind).ok_or_else(|| { span_range.value_error(format!( @@ -112,29 +121,13 @@ impl Spanned { } } -impl HasValueKind for UntypedInteger { - type SpecificKind = IntegerKind; - - fn kind(&self) -> IntegerKind { - IntegerKind::Untyped - } -} - -impl IntoValue for UntypedInteger { - fn into_value(self) -> Value { - Value::Integer(IntegerValue::Untyped(self)) - } -} - -define_interface! { - struct UntypedIntegerTypeData, - parent: IntegerTypeData, +define_type_features! { + impl UntypedIntegerType, pub(crate) mod untyped_integer_interface { pub(crate) mod methods { } pub(crate) mod unary_operations { - fn neg(Spanned(value, span): Spanned>) -> ExecutionResult { - let value = value.into_inner(); + fn neg(Spanned(value, span): Spanned) -> ExecutionResult { let input = value.into_fallback(); match input.checked_neg() { Some(negated) => Ok(UntypedInteger::from_fallback(negated)), @@ -216,24 +209,24 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } => unary_definitions::neg(), - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Integer(IntegerKind::Untyped) => unary_definitions::cast_to_untyped_integer(), - CastTarget::Integer(IntegerKind::I8) => unary_definitions::cast_to_i8(), - CastTarget::Integer(IntegerKind::I16) => unary_definitions::cast_to_i16(), - CastTarget::Integer(IntegerKind::I32) => unary_definitions::cast_to_i32(), - CastTarget::Integer(IntegerKind::I64) => unary_definitions::cast_to_i64(), - CastTarget::Integer(IntegerKind::I128) => unary_definitions::cast_to_i128(), - CastTarget::Integer(IntegerKind::Isize) => unary_definitions::cast_to_isize(), - CastTarget::Integer(IntegerKind::U8) => unary_definitions::cast_to_u8(), - CastTarget::Integer(IntegerKind::U16) => unary_definitions::cast_to_u16(), - CastTarget::Integer(IntegerKind::U32) => unary_definitions::cast_to_u32(), - CastTarget::Integer(IntegerKind::U64) => unary_definitions::cast_to_u64(), - CastTarget::Integer(IntegerKind::U128) => unary_definitions::cast_to_u128(), - CastTarget::Integer(IntegerKind::Usize) => unary_definitions::cast_to_usize(), - CastTarget::Float(FloatKind::Untyped) => unary_definitions::cast_to_untyped_float(), - CastTarget::Float(FloatKind::F32) => unary_definitions::cast_to_f32(), - CastTarget::Float(FloatKind::F64) => unary_definitions::cast_to_f64(), - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + AnyValueLeafKind::Integer(IntegerLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_integer(), + AnyValueLeafKind::Integer(IntegerLeafKind::I8(_)) => unary_definitions::cast_to_i8(), + AnyValueLeafKind::Integer(IntegerLeafKind::I16(_)) => unary_definitions::cast_to_i16(), + AnyValueLeafKind::Integer(IntegerLeafKind::I32(_)) => unary_definitions::cast_to_i32(), + AnyValueLeafKind::Integer(IntegerLeafKind::I64(_)) => unary_definitions::cast_to_i64(), + AnyValueLeafKind::Integer(IntegerLeafKind::I128(_)) => unary_definitions::cast_to_i128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Isize(_)) => unary_definitions::cast_to_isize(), + AnyValueLeafKind::Integer(IntegerLeafKind::U8(_)) => unary_definitions::cast_to_u8(), + AnyValueLeafKind::Integer(IntegerLeafKind::U16(_)) => unary_definitions::cast_to_u16(), + AnyValueLeafKind::Integer(IntegerLeafKind::U32(_)) => unary_definitions::cast_to_u32(), + AnyValueLeafKind::Integer(IntegerLeafKind::U64(_)) => unary_definitions::cast_to_u64(), + AnyValueLeafKind::Integer(IntegerLeafKind::U128(_)) => unary_definitions::cast_to_u128(), + AnyValueLeafKind::Integer(IntegerLeafKind::Usize(_)) => unary_definitions::cast_to_usize(), + AnyValueLeafKind::Float(FloatLeafKind::Untyped(_)) => unary_definitions::cast_to_untyped_float(), + AnyValueLeafKind::Float(FloatLeafKind::F32(_)) => unary_definitions::cast_to_f32(), + AnyValueLeafKind::Float(FloatLeafKind::F64(_)) => unary_definitions::cast_to_f64(), + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, _ => return None, @@ -252,14 +245,30 @@ define_interface! { pub(crate) struct UntypedIntegerFallback(pub(crate) FallbackInteger); +impl IsValueContent for UntypedIntegerFallback { + type Type = IntegerType; + type Form = BeOwned; +} + +impl IsArgument for UntypedIntegerFallback { + type ValueType = UntypedIntegerType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument(value: Spanned) -> ExecutionResult { + Self::resolve_value(value.expect_owned(), "This argument") + } +} + impl ResolvableArgumentTarget for UntypedIntegerFallback { - type ValueType = UntypedIntegerTypeData; + type ValueType = UntypedIntegerType; } -impl ResolvableOwned for UntypedIntegerFallback { - fn resolve_from_value(input_value: Value, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for UntypedIntegerFallback { + fn resolve_from_value( + input_value: AnyValue, + context: ResolutionContext, + ) -> ExecutionResult { let value: UntypedInteger = - ResolvableOwned::::resolve_from_value(input_value, context)?; + ResolvableOwned::::resolve_from_value(input_value, context)?; Ok(UntypedIntegerFallback(value.into_fallback())) } } @@ -277,10 +286,10 @@ impl ResolvableOwned for UntypedInteger { } impl_resolvable_argument_for! { - UntypedIntegerTypeData, + UntypedIntegerType, (value, context) -> UntypedInteger { match value { - Value::Integer(IntegerValue::Untyped(x)) => Ok(x), + AnyValue::Integer(IntegerValue::Untyped(x)) => Ok(x), _ => context.err("an untyped integer", value), } } diff --git a/src/expressions/values/iterable.rs b/src/expressions/values/iterable.rs index b50f8ff7..5a87f161 100644 --- a/src/expressions/values/iterable.rs +++ b/src/expressions/values/iterable.rs @@ -1,56 +1,35 @@ use super::*; -// If you add a new variant, also update: -// * ResolvableOwned for IterableValue -// * IsArgument for IterableRef -// * The parent of the value's TypeData to be IterableTypeData -pub(crate) enum IterableValue { - Iterator(IteratorValue), - Array(ArrayValue), - Stream(StreamValue), - Object(ObjectValue), - Range(RangeValue), - String(StringValue), +pub(crate) trait IsIterable: 'static { + fn into_iterator(self: Box) -> ExecutionResult; + fn len(&self, error_span_range: SpanRange) -> ExecutionResult; } -impl ResolvableArgumentTarget for IterableValue { - type ValueType = IterableTypeData; -} +define_dyn_type!( + pub(crate) IterableType, + content: dyn IsIterable, + dyn_kind: DynTypeKind::Iterable, + type_name: "iterable", + articled_display_name: "an iterable (e.g. array, list, etc.)", +); -impl ResolvableOwned for IterableValue { - fn resolve_from_value(value: Value, context: ResolutionContext) -> ExecutionResult { - Ok(match value { - Value::Array(x) => Self::Array(x), - Value::Object(x) => Self::Object(x), - Value::Stream(x) => Self::Stream(x), - Value::Range(x) => Self::Range(x), - Value::Iterator(x) => Self::Iterator(x), - Value::String(x) => Self::String(x), - _ => { - return context.err( - "an iterable (iterator, array, object, stream, range or string)", - value, - ); - } - }) - } -} +pub(crate) type IterableValue = Box; +pub(crate) type IterableAnyRef<'a> = AnyRef<'a, dyn IsIterable>; -define_interface! { - struct IterableTypeData, - parent: ValueTypeData, +define_type_features! { + impl IterableType, pub(crate) mod iterable_interface { pub(crate) mod methods { fn into_iter(this: IterableValue) -> ExecutionResult { this.into_iterator() } - fn len(this: Spanned) -> ExecutionResult { - this.len() + fn len(Spanned(this, span_range): Spanned) -> ExecutionResult { + this.len(span_range) } - fn is_empty(this: Spanned) -> ExecutionResult { - Ok(this.len()? == 0) + fn is_empty(Spanned(this, span_range): Spanned) -> ExecutionResult { + Ok(this.len(span_range)? == 0) } [context] fn zip(this: IterableValue) -> ExecutionResult { @@ -63,11 +42,11 @@ define_interface! { ZipIterators::new_from_iterator(iterator, context.span_range())?.run_zip(context.interpreter, false) } - fn intersperse(this: IterableValue, separator: Value, settings: Option) -> ExecutionResult { + fn intersperse(this: IterableValue, separator: AnyValue, settings: Option) -> ExecutionResult { run_intersperse(this, separator, settings.unwrap_or_default()) } - [context] fn to_vec(this: IterableValue) -> ExecutionResult> { + [context] fn to_vec(this: IterableValue) -> ExecutionResult> { let error_span_range = context.span_range(); let mut counter = context.interpreter.start_iteration_counter(&error_span_range); let iterator = this.into_iterator()?; @@ -85,67 +64,20 @@ define_interface! { } } pub(crate) mod unary_operations { + fn cast_into_iterator(this: IterableValue) -> ExecutionResult { + this.into_iterator() + } } pub(crate) mod binary_operations {} interface_items { - } - } -} - -impl IterableValue { - pub(crate) fn into_iterator(self) -> ExecutionResult { - Ok(match self { - IterableValue::Array(value) => IteratorValue::new_for_array(value), - IterableValue::Stream(value) => IteratorValue::new_for_stream(value), - IterableValue::Iterator(value) => value, - IterableValue::Range(value) => IteratorValue::new_for_range(value)?, - IterableValue::Object(value) => IteratorValue::new_for_object(value), - IterableValue::String(value) => IteratorValue::new_for_string(value), - }) - } -} - -pub(crate) enum IterableRef<'a> { - Iterator(AnyRef<'a, IteratorValue>), - Array(AnyRef<'a, ArrayValue>), - Stream(AnyRef<'a, OutputStream>), - Range(AnyRef<'a, RangeValue>), - Object(AnyRef<'a, ObjectValue>), - String(AnyRef<'a, str>), -} - -impl IsArgument for IterableRef<'static> { - type ValueType = IterableTypeData; - const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; - - fn from_argument(argument: Spanned) -> ExecutionResult { - Ok(match argument.kind() { - ValueKind::Iterator => IterableRef::Iterator(IsArgument::from_argument(argument)?), - ValueKind::Array => IterableRef::Array(IsArgument::from_argument(argument)?), - ValueKind::Stream => IterableRef::Stream(IsArgument::from_argument(argument)?), - ValueKind::Range(_) => IterableRef::Range(IsArgument::from_argument(argument)?), - ValueKind::Object => IterableRef::Object(IsArgument::from_argument(argument)?), - ValueKind::String => IterableRef::String(IsArgument::from_argument(argument)?), - _ => { - return argument.type_err( - "Expected iterable (iterator, array, object, stream, range or string)", - ); + fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { + Some(match operation { + UnaryOperation::Cast { target: CastTarget(AnyValueLeafKind::Iterator(_)), .. } =>{ + unary_definitions::cast_into_iterator() + } + _ => return None, + }) } - }) - } -} - -impl Spanned> { - pub(crate) fn len(&self) -> ExecutionResult { - let Spanned(value, span) = self; - match value { - IterableRef::Iterator(iterator) => iterator.len(*span), - IterableRef::Array(value) => Ok(value.items.len()), - IterableRef::Stream(value) => Ok(value.len()), - IterableRef::Range(value) => value.len(*span), - IterableRef::Object(value) => Ok(value.entries.len()), - // NB - this is different to string.len() which counts bytes - IterableRef::String(value) => Ok(value.chars().count()), } } } diff --git a/src/expressions/values/iterator.rs b/src/expressions/values/iterator.rs index 917d32cb..b9031076 100644 --- a/src/expressions/values/iterator.rs +++ b/src/expressions/values/iterator.rs @@ -1,5 +1,24 @@ use super::*; +define_leaf_type! { + pub(crate) IteratorType => AnyType(AnyValueContent::Iterator), + content: IteratorValue, + kind: pub(crate) IteratorKind, + type_name: "iterator", + articled_display_name: "an iterator", + dyn_impls: { + IterableType: impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(*self) + } + + fn len(&self, error_span_range: SpanRange) -> ExecutionResult { + self.len(error_span_range) + } + } + }, +} + #[derive(Clone)] pub(crate) struct IteratorValue { iterator: IteratorValueInner, @@ -11,7 +30,7 @@ impl IteratorValue { } #[allow(unused)] - pub(crate) fn new_any(iterator: impl Iterator + 'static + Clone) -> Self { + pub(crate) fn new_any(iterator: impl Iterator + 'static + Clone) -> Self { Self::new_custom(Box::new(iterator)) } @@ -19,10 +38,8 @@ impl IteratorValue { Self::new_vec(array.items.into_iter()) } - pub(crate) fn new_for_stream(stream: StreamValue) -> Self { - Self::new(IteratorValueInner::Stream(Box::new( - stream.value.into_iter(), - ))) + pub(crate) fn new_for_stream(stream: OutputStream) -> Self { + Self::new(IteratorValueInner::Stream(Box::new(stream.into_iter()))) } pub(crate) fn new_for_range(range: RangeValue) -> ExecutionResult { @@ -35,30 +52,31 @@ impl IteratorValue { let iterator = object .entries .into_iter() - .map(|(k, v)| vec![k.into_value(), v.value].into_value()) + .map(|(k, v)| vec![k.into_any_value(), v.value].into_any_value()) .collect::>() .into_iter(); Self::new_vec(iterator) } - pub(crate) fn new_for_string(string: StringValue) -> Self { + pub(crate) fn new_for_string_over_chars(string: String) -> Self { // We have to collect to vec and back to make the iterator owned // That's because value.chars() creates a `Chars<'_>` iterator which // borrows from the string, which we don't allow in a Boxed iterator + // TODO: Replace with the owned iterator here to avoid this clone: + // https://internals.rust-lang.org/t/is-there-a-good-reason-why-string-has-no-into-chars/19496/5 let iterator = string - .value .chars() - .map(|c| c.into_value()) + .map(|c| c.into_any_value()) .collect::>() .into_iter(); Self::new_vec(iterator) } - fn new_vec(iterator: std::vec::IntoIter) -> Self { + fn new_vec(iterator: std::vec::IntoIter) -> Self { Self::new(IteratorValueInner::Vec(Box::new(iterator))) } - pub(crate) fn new_custom(iterator: Box>) -> Self { + pub(crate) fn new_custom(iterator: Box>) -> Self { Self::new(IteratorValueInner::Other(iterator)) } @@ -71,7 +89,7 @@ impl IteratorValue { } } - pub(crate) fn singleton_value(mut self) -> Option { + pub(crate) fn singleton_value(mut self) -> Option { let first = self.next()?; if self.next().is_none() { Some(first) @@ -90,7 +108,7 @@ impl IteratorValue { 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.output_to(grouping, output)?; + item.as_ref_value().output_to(grouping, output)?; } Ok(()) } @@ -111,7 +129,7 @@ impl IteratorValue { ) } - pub(crate) fn any_iterator_to_string>( + pub(crate) fn any_iterator_to_string>( iterator: impl Iterator, output: &mut String, behaviour: &ConcatBehaviour, @@ -152,7 +170,8 @@ impl IteratorValue { if i != 0 && behaviour.add_space_between_token_trees { output.push(' '); } - item.concat_recursive_into(output, behaviour)?; + item.as_ref_value() + .concat_recursive_into(output, behaviour)?; } if behaviour.output_literal_structure { if is_empty { @@ -165,44 +184,38 @@ impl IteratorValue { } } -impl IntoValue for IteratorValueInner { - fn into_value(self) -> Value { - Value::Iterator(IteratorValue::new(self)) - } +impl IsValueContent for IteratorValueInner { + type Type = IteratorType; + type Form = BeOwned; } -impl IntoValue for Box> { - fn into_value(self) -> Value { - Value::Iterator(IteratorValue::new_custom(self)) +impl IntoValueContent<'static> for IteratorValueInner { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + IteratorValue::new(self) } } -impl IntoValue for IteratorValue { - fn into_value(self) -> Value { - Value::Iterator(IteratorValue { - iterator: self.iterator, - }) +impl IsValueContent for Box> { + type Type = IteratorType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Box> { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + IteratorValue::new_custom(self) } } impl_resolvable_argument_for! { - IteratorTypeData, + IteratorType, (value, context) -> IteratorValue { match value { - Value::Iterator(value) => Ok(value), + AnyValue::Iterator(value) => Ok(value), _ => context.err("an iterator", value), } } } -impl HasValueKind for IteratorValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Iterator - } -} - fn definite_size_hint(size_hint: (usize, Option)) -> Option { let (min, max) = size_hint; if let Some(max) = max { @@ -245,13 +258,13 @@ impl ValuesEqual for IteratorValue { #[derive(Clone)] enum IteratorValueInner { // We Box these so that Value is smaller on the stack - Vec(Box< as IntoIterator>::IntoIter>), + Vec(Box< as IntoIterator>::IntoIter>), Stream(Box<::IntoIter>), - Other(Box>), + Other(Box>), } impl Iterator for IteratorValue { - type Item = Value; + type Item = AnyValue; fn next(&mut self) -> Option { match &mut self.iterator { @@ -275,7 +288,7 @@ impl Iterator for IteratorValue { } impl Iterator for Mutable { - type Item = Value; + type Item = AnyValue; fn next(&mut self) -> Option { let this: &mut IteratorValue = &mut *self; @@ -288,22 +301,21 @@ impl Iterator for Mutable { } } -define_interface! { - struct IteratorTypeData, - parent: IterableTypeData, +define_type_features! { + impl IteratorType, pub(crate) mod iterator_interface { pub(crate) mod methods { - fn next(mut this: Mutable) -> Value { + fn next(mut this: Mutable) -> AnyValue { match this.next() { Some(value) => value, - None => Value::None, + None => ().into_any_value(), } } - fn skip(mut this: IteratorValue, n: usize) -> IteratorValue { + 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 { + for _ in 0..n.0 { if this.next().is_none() { break; } @@ -311,17 +323,17 @@ define_interface! { this } - fn take(this: IteratorValue, n: usize) -> IteratorValue { + 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).collect::>(); + let taken = this.take(n.0).collect::>(); IteratorValue::new_for_array(ArrayValue::new(taken)) } } pub(crate) mod unary_operations { - [context] fn cast_singleton_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { - match this.into_inner().singleton_value() { - Some(value) => Ok(context.operation.evaluate(Spanned(Owned::new(value), span))?.0), + [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), None => span.value_err("Only an iterator with one item can be cast to this value"), } } @@ -331,13 +343,11 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } | UnaryOperation::Not { .. } => return None, - UnaryOperation::Cast { target, .. } => match target { - CastTarget::Boolean - | CastTarget::Char - | CastTarget::Integer(_) - | CastTarget::Float(_) => unary_definitions::cast_singleton_to_value(), - _ => return None, - }, + UnaryOperation::Cast { target, .. } => if target.is_singleton_target() { + unary_definitions::cast_singleton_to_value() + } else { + return None; + } }) } } diff --git a/src/expressions/values/mod.rs b/src/expressions/values/mod.rs index 3bdf28b8..14fb0485 100644 --- a/src/expressions/values/mod.rs +++ b/src/expressions/values/mod.rs @@ -1,3 +1,4 @@ +mod any_value; mod array; mod boolean; mod character; @@ -16,8 +17,8 @@ mod range; mod stream; mod string; mod unsupported_literal; -mod value; +pub(crate) use any_value::*; pub(crate) use array::*; pub(crate) use boolean::*; pub(crate) use character::*; @@ -36,7 +37,6 @@ pub(crate) use range::*; pub(crate) use stream::*; pub(crate) use string::*; pub(crate) use unsupported_literal::*; -pub(crate) use value::*; // Marked as use for sub-modules to use with a `use super::*` statement use super::*; diff --git a/src/expressions/values/none.rs b/src/expressions/values/none.rs index 569b6a65..1984a6b1 100644 --- a/src/expressions/values/none.rs +++ b/src/expressions/values/none.rs @@ -1,19 +1,23 @@ use super::*; -impl IntoValue for () { - fn into_value(self) -> Value { - Value::None - } +define_leaf_type! { + pub(crate) NoneType => AnyType(AnyValueContent::None), + content: (), + kind: pub(crate) NoneKind, + type_name: "none", + // Instead of saying "expected a none value", we can say "expected None" + articled_display_name: "None", + dyn_impls: {}, } impl ResolvableArgumentTarget for () { - type ValueType = NoneTypeData; + type ValueType = NoneType; } -impl ResolvableOwned for () { - fn resolve_from_value(value: Value, context: ResolutionContext) -> ExecutionResult { +impl ResolvableOwned for () { + fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> ExecutionResult { match value { - Value::None => Ok(()), + AnyValue::None(_) => Ok(()), other => context.err("None", other), } } @@ -25,9 +29,8 @@ define_optional_object! { } } -define_interface! { - struct NoneTypeData, - parent: ValueTypeData, +define_type_features! { + impl NoneType, pub(crate) mod none_interface { pub(crate) mod methods { [context] fn configure_preinterpret(_none: (), inputs: SettingsInputs) { diff --git a/src/expressions/values/object.rs b/src/expressions/values/object.rs index a464d8d0..b410cce6 100644 --- a/src/expressions/values/object.rs +++ b/src/expressions/values/object.rs @@ -1,21 +1,34 @@ use super::*; +define_leaf_type! { + pub(crate) ObjectType => AnyType(AnyValueContent::Object), + content: ObjectValue, + kind: pub(crate) ObjectKind, + type_name: "object", + articled_display_name: "an object", + dyn_impls: { + IterableType: impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(IteratorValue::new_for_object(*self)) + } + + fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + Ok(self.entries.len()) + } + } + }, +} + #[derive(Clone)] pub(crate) struct ObjectValue { pub(crate) entries: BTreeMap, } -impl IntoValue for ObjectValue { - fn into_value(self) -> Value { - Value::Object(self) - } -} - impl_resolvable_argument_for! { - ObjectTypeData, + ObjectType, (value, context) -> ObjectValue { match value { - Value::Object(value) => Ok(value), + AnyValue::Object(value) => Ok(value), _ => context.err("an object", value), } } @@ -25,28 +38,28 @@ impl_resolvable_argument_for! { pub(crate) struct ObjectEntry { #[allow(unused)] pub(crate) key_span: Span, - pub(crate) value: Value, + pub(crate) value: AnyValue, } impl ObjectValue { - pub(super) fn into_indexed(mut self, index: Spanned<&Value>) -> ExecutionResult { - let key = index.resolve_as("An object key")?; + pub(super) fn into_indexed(mut self, index: Spanned) -> ExecutionResult { + 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) -> ExecutionResult { let key = access.property.to_string(); Ok(self.remove_or_none(&key)) } - pub(crate) fn remove_or_none(&mut self, key: &str) -> Value { + pub(crate) fn remove_or_none(&mut self, key: &str) -> AnyValue { match self.entries.remove(key) { Some(entry) => entry.value, - None => Value::None, + None => ().into_any_value(), } } - pub(crate) fn remove_no_none(&mut self, key: &str) -> Option { + pub(crate) fn remove_no_none(&mut self, key: &str) -> Option { match self.entries.remove(key) { Some(entry) => { if entry.value.is_none() { @@ -61,15 +74,15 @@ impl ObjectValue { pub(super) fn index_mut( &mut self, - index: Spanned<&Value>, + index: Spanned, auto_create: bool, - ) -> ExecutionResult<&mut Value> { - let index: Spanned<&str> = index.resolve_as("An object key")?; + ) -> ExecutionResult<&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<&Value>) -> ExecutionResult<&Value> { - let key: Spanned<&str> = index.resolve_as("An object key")?; + pub(super) fn index_ref(&self, index: Spanned) -> ExecutionResult<&AnyValue> { + let key: Spanned<&str> = index.downcast_resolve("An object key")?; let entry = self.entries.get(*key).ok_or_else(|| { key.value_error(format!("The object does not have a field named `{}`", *key)) })?; @@ -80,14 +93,14 @@ impl ObjectValue { &mut self, access: &PropertyAccess, auto_create: bool, - ) -> ExecutionResult<&mut Value> { + ) -> ExecutionResult<&mut AnyValue> { self.mut_entry( access.property.to_string().spanned(access.property.span()), auto_create, ) } - pub(super) fn property_ref(&self, access: &PropertyAccess) -> ExecutionResult<&Value> { + pub(super) fn property_ref(&self, access: &PropertyAccess) -> ExecutionResult<&AnyValue> { let key = access.property.to_string(); let entry = self.entries.get(&key).ok_or_else(|| { access.value_error(format!("The object does not have a field named `{}`", key)) @@ -99,7 +112,7 @@ impl ObjectValue { &mut self, Spanned(key, key_span): Spanned, auto_create: bool, - ) -> ExecutionResult<&mut Value> { + ) -> ExecutionResult<&mut AnyValue> { use std::collections::btree_map::*; Ok(match self.entries.entry(key) { Entry::Occupied(entry) => &mut entry.into_mut().value, @@ -108,7 +121,7 @@ impl ObjectValue { &mut entry .insert(ObjectEntry { key_span: key_span.join_into_span_else_start(), - value: Value::None, + value: ().into_any_value(), }) .value } else { @@ -158,7 +171,10 @@ impl ObjectValue { if behaviour.add_space_between_token_trees { output.push(' '); } - entry.value.concat_recursive_into(output, behaviour)?; + entry + .value + .as_ref_value() + .concat_recursive_into(output, behaviour)?; is_first = false; } if behaviour.output_literal_structure { @@ -202,7 +218,8 @@ impl Spanned<&ObjectValue> { match self.entries.get(field_name) { None | Some(ObjectEntry { - value: Value::None, .. + value: AnyValue::None(_), + .. }) => { missing_fields.push(field_name); } @@ -241,23 +258,19 @@ impl Spanned<&ObjectValue> { } } -impl HasValueKind for ObjectValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Object - } +impl IsValueContent for BTreeMap { + type Type = ObjectType; + type Form = BeOwned; } -impl IntoValue for BTreeMap { - fn into_value(self) -> Value { - Value::Object(ObjectValue { entries: self }) +impl IntoValueContent<'static> for BTreeMap { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + ObjectValue { entries: self } } } -define_interface! { - struct ObjectTypeData, - parent: IterableTypeData, +define_type_features! { + impl ObjectType, pub(crate) mod object_interface { pub(crate) mod methods { [context] fn zip(this: ObjectValue) -> ExecutionResult { @@ -271,6 +284,28 @@ define_interface! { pub(crate) mod unary_operations { } pub(crate) mod binary_operations {} + property_access(ObjectValue) { + [ctx] fn shared(source: &'a ObjectValue) { + source.property_ref(ctx.property) + } + [ctx] fn mutable(source: &'a mut ObjectValue, auto_create: bool) { + source.property_mut(ctx.property, auto_create) + } + [ctx] fn owned(source: ObjectValue) { + source.into_property(ctx.property) + } + } + index_access(ObjectValue) { + fn shared(source: &'a ObjectValue, index: Spanned) { + source.index_ref(index) + } + fn mutable(source: &'a mut ObjectValue, index: Spanned, auto_create: bool) { + source.index_mut(index, auto_create) + } + fn owned(source: ObjectValue, index: Spanned) { + source.into_indexed(index) + } + } interface_items { } } diff --git a/src/expressions/values/parser.rs b/src/expressions/values/parser.rs index aa3416d9..492c6ad5 100644 --- a/src/expressions/values/parser.rs +++ b/src/expressions/values/parser.rs @@ -1,27 +1,19 @@ use super::*; -#[derive(Clone)] -pub(crate) struct ParserValue { - handle: ParserHandle, +define_leaf_type! { + pub(crate) ParserType => AnyType(AnyValueContent::Parser), + content: ParserHandle, + kind: pub(crate) ParserKind, + type_name: "parser", + articled_display_name: "a parser", + dyn_impls: {}, } -impl HasValueKind for ParserValue { - type SpecificKind = ValueKind; +struct ParserHandleValueWrapper(ParserHandle); - fn kind(&self) -> ValueKind { - ValueKind::Parser - } -} - -impl Debug for ParserValue { +impl Debug for ParserHandleValueWrapper { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Parser[{:?}]", self.handle) - } -} - -impl ParserValue { - pub(crate) fn new(handle: ParserHandle) -> Self { - Self { handle } + write!(f, "Parser[{:?}]", self.0) } } @@ -43,35 +35,27 @@ fn delimiter_from_close_char(c: char) -> Option { } } -impl ValuesEqual for ParserValue { +impl ValuesEqual for ParserHandle { /// Parsers are equal if they reference the same handle. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.handle == other.handle { + if self == other { ctx.values_equal() } else { - ctx.leaf_values_not_equal(self, other) + ctx.leaf_values_not_equal( + &ParserHandleValueWrapper(*self), + &ParserHandleValueWrapper(*other), + ) } } } -impl IntoValue for ParserValue { - fn into_value(self) -> Value { - Value::Parser(self) - } -} - -impl IntoValue for ParserHandle { - fn into_value(self) -> Value { - ParserValue::new(self).into_value() - } -} - -impl Spanned> { +impl Spanned> { pub(crate) fn parser<'i>( &self, interpreter: &'i mut Interpreter, ) -> ExecutionResult> { - interpreter.parser(self.handle, self.1) + let Spanned(handle, span) = self; + interpreter.parser(**handle, *span) } pub(crate) fn parse_with( @@ -79,31 +63,31 @@ impl Spanned> { interpreter: &mut Interpreter, f: impl FnOnce(&mut Interpreter) -> ExecutionResult, ) -> ExecutionResult { - interpreter.parse_with(self.handle, f) + let Spanned(handle, _) = self; + interpreter.parse_with(**handle, f) } } fn parser<'a>( - this: Spanned>, + this: Spanned>, context: &'a mut MethodCallContext, ) -> ExecutionResult> { this.parser(context.interpreter) } -define_interface! { - struct ParserTypeData, - parent: ValueTypeData, +define_type_features! { + impl ParserType, pub(crate) mod parser_interface { pub(crate) mod methods { // GENERAL // ======= - [context] fn is_end(this: Spanned>) -> ExecutionResult { + [context] fn is_end(this: Spanned>) -> ExecutionResult { 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>) -> ExecutionResult<()> { let parser = parser(this, context)?; match parser.is_empty() { true => Ok(()), @@ -111,37 +95,37 @@ define_interface! { } } - [context] fn token_tree(this: Spanned>) -> ExecutionResult { + [context] fn token_tree(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.parse()?) } - [context] fn ident(this: Spanned>) -> ExecutionResult { + [context] fn ident(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.parse()?) } - [context] fn any_ident(this: Spanned>) -> ExecutionResult { + [context] fn any_ident(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.parse_any_ident()?) } - [context] fn punct(this: Spanned>) -> ExecutionResult { + [context] fn punct(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.parse()?) } - [context] fn read(this: Spanned>, parse_template: AnyRef) -> ExecutionResult { + [context] fn read(this: Spanned>, parse_template: AnyRef) -> ExecutionResult { 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>) -> ExecutionResult { 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) -> ExecutionResult { let input = parser(this, context)?; let until: ParseUntil = until.parse_as()?; let mut output = OutputStream::new(); @@ -149,7 +133,7 @@ define_interface! { Ok(output) } - [context] fn error(this: Spanned>, message: String) -> ExecutionResult<()> { + [context] fn error(this: Spanned>, message: String) -> ExecutionResult<()> { let parser = parser(this, context)?; parser.parse_err(message).map_err(|e| e.into()) } @@ -159,8 +143,7 @@ define_interface! { // 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<()> { - let delimiter_char = delimiter_char.into_inner(); + [context] fn open(this: Spanned>, Spanned(delimiter_char, char_span): Spanned) -> ExecutionResult<()> { let delimiter = delimiter_from_open_char(delimiter_char) .ok_or_else(|| char_span.value_error(format!( "Invalid open delimiter '{}'. Expected '(', '{{', or '['", delimiter_char @@ -173,8 +156,7 @@ define_interface! { // 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<()> { - let delimiter_char = delimiter_char.into_inner(); + [context] fn close(this: Spanned>, Spanned(delimiter_char, char_span): Spanned) -> ExecutionResult<()> { let expected_delimiter = delimiter_from_close_char(delimiter_char) .ok_or_else(|| char_span.value_error(format!( "Invalid close delimiter '{}'. Expected ')', '}}', or ']'", delimiter_char @@ -200,74 +182,74 @@ define_interface! { // LITERALS // ======== - [context] fn is_literal(this: Spanned>) -> ExecutionResult { + [context] fn is_literal(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.cursor().literal().is_some()) } - [context] fn literal(this: Spanned>) -> ExecutionResult { + [context] fn literal(this: Spanned>) -> ExecutionResult { 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>) -> ExecutionResult { let literal = parser(this, context)?.parse()?; - Ok(Value::for_literal(literal).into_value()) + Ok(AnyValue::for_literal(literal).into_any_value()) } - [context] fn is_char(this: Spanned>) -> ExecutionResult { + [context] fn is_char(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.peek(syn::LitChar)) } - [context] fn char_literal(this: Spanned>) -> ExecutionResult { + [context] fn char_literal(this: Spanned>) -> ExecutionResult { 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>) -> ExecutionResult { let char: syn::LitChar = parser(this, context)?.parse()?; Ok(char.value()) } - [context] fn is_string(this: Spanned>) -> ExecutionResult { + [context] fn is_string(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.peek(syn::LitStr)) } - [context] fn string_literal(this: Spanned>) -> ExecutionResult { + [context] fn string_literal(this: Spanned>) -> ExecutionResult { 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>) -> ExecutionResult { let string: syn::LitStr = parser(this, context)?.parse()?; Ok(string.value()) } - [context] fn is_integer(this: Spanned>) -> ExecutionResult { + [context] fn is_integer(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.peek(syn::LitInt)) } - [context] fn integer_literal(this: Spanned>) -> ExecutionResult { + [context] fn integer_literal(this: Spanned>) -> ExecutionResult { 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>) -> ExecutionResult { let integer: syn::LitInt = parser(this, context)?.parse()?; - Ok(IntegerValue::for_litint(&integer)?.into_inner()) + Ok(IntegerValue::for_litint(&integer)?) } - [context] fn is_float(this: Spanned>) -> ExecutionResult { + [context] fn is_float(this: Spanned>) -> ExecutionResult { Ok(parser(this, context)?.peek(syn::LitFloat)) } - [context] fn float_literal(this: Spanned>) -> ExecutionResult { + [context] fn float_literal(this: Spanned>) -> ExecutionResult { 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>) -> ExecutionResult { let float: syn::LitFloat = parser(this, context)?.parse()?; - Ok(FloatValue::for_litfloat(&float)?.into_inner()) + Ok(FloatValue::for_litfloat(&float)?) } } pub(crate) mod unary_operations { @@ -279,36 +261,56 @@ define_interface! { } impl_resolvable_argument_for! { - ParserTypeData, - (value, context) -> ParserValue { + ParserType, + (value, context) -> ParserHandle { match value { - Value::Parser(value) => Ok(value), + AnyValue::Parser(value) => Ok(value), other => context.err("a parser", other), } } } -impl IntoValue for TokenTree { - fn into_value(self) -> Value { - OutputStream::new_with(|s| s.push_raw_token_tree(self)).into_value() +impl IsValueContent for TokenTree { + type Type = StreamType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for TokenTree { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::new_with(|s| s.push_raw_token_tree(self)) } } -impl IntoValue for Ident { - fn into_value(self) -> Value { - OutputStream::new_with(|s| s.push_ident(self)).into_value() +impl IsValueContent for Ident { + type Type = StreamType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Ident { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::new_with(|s| s.push_ident(self)) } } -impl IntoValue for Punct { - fn into_value(self) -> Value { - OutputStream::new_with(|s| s.push_punct(self)).into_value() +impl IsValueContent for Punct { + type Type = StreamType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Punct { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::new_with(|s| s.push_punct(self)) } } -impl IntoValue for Literal { - fn into_value(self) -> Value { - OutputStream::new_with(|s| s.push_literal(self)).into_value() +impl IsValueContent for Literal { + type Type = StreamType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Literal { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::new_with(|s| s.push_literal(self)) } } @@ -348,7 +350,7 @@ impl Evaluate for ParseTemplateLiteral { interpreter: &mut Interpreter, ownership: RequestedOwnership, ) -> ExecutionResult { - let parser: Shared = Spanned( + let parser: Shared = Spanned( self.parser_reference.resolve_shared(interpreter)?, self.parser_reference.span_range(), ) @@ -359,7 +361,7 @@ impl Evaluate for ParseTemplateLiteral { parser.parse_with(interpreter, |interpreter| self.content.consume(interpreter))?; ownership - .map_from_owned(Spanned(().into_owned_value(), self.span_range())) + .map_from_owned(Spanned(().into_any_value(), self.span_range())) .map(|spanned| spanned.0) } } diff --git a/src/expressions/values/range.rs b/src/expressions/values/range.rs index 2dcc5370..d20da791 100644 --- a/src/expressions/values/range.rs +++ b/src/expressions/values/range.rs @@ -2,6 +2,25 @@ use syn::RangeLimits; use super::*; +define_leaf_type! { + pub(crate) RangeType => AnyType(AnyValueContent::Range), + content: RangeValue, + kind: pub(crate) RangeKind, + type_name: "range", + articled_display_name: "a range", + dyn_impls: { + IterableType: impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + IteratorValue::new_for_range(*self) + } + + fn len(&self, error_span_range: SpanRange) -> ExecutionResult { + self.len(error_span_range) + } + } + }, +} + #[derive(Clone)] pub(crate) struct RangeValue { pub(crate) inner: Box, @@ -34,19 +53,27 @@ impl RangeValue { end_exclusive, .. } => { - start_inclusive.concat_recursive_into(output, behaviour)?; + start_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; output.push_str(".."); - end_exclusive.concat_recursive_into(output, behaviour)?; + end_exclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; } RangeValueInner::RangeFrom { start_inclusive, .. } => { - start_inclusive.concat_recursive_into(output, behaviour)?; + start_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; output.push_str(".."); } RangeValueInner::RangeTo { end_exclusive, .. } => { output.push_str(".."); - end_exclusive.concat_recursive_into(output, behaviour)?; + end_exclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; } RangeValueInner::RangeFull { .. } => { output.push_str(".."); @@ -56,13 +83,19 @@ impl RangeValue { end_inclusive, .. } => { - start_inclusive.concat_recursive_into(output, behaviour)?; + start_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; output.push_str("..="); - end_inclusive.concat_recursive_into(output, behaviour)?; + end_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; } RangeValueInner::RangeToInclusive { end_inclusive, .. } => { output.push_str("..="); - end_inclusive.concat_recursive_into(output, behaviour)?; + end_inclusive + .as_ref_value() + .concat_recursive_into(output, behaviour)?; } } Ok(()) @@ -83,18 +116,26 @@ impl Spanned<&RangeValue> { end_exclusive, .. } => { - start = array.resolve_valid_index(Spanned(start_inclusive, span_range), false)?; - end = array.resolve_valid_index(Spanned(end_exclusive, span_range), true)?; + start = array.resolve_valid_index( + Spanned(start_inclusive.as_ref_value(), span_range), + false, + )?; + end = array + .resolve_valid_index(Spanned(end_exclusive.as_ref_value(), span_range), true)?; start..end } RangeValueInner::RangeFrom { start_inclusive, .. } => { - start = array.resolve_valid_index(Spanned(start_inclusive, span_range), false)?; + start = array.resolve_valid_index( + Spanned(start_inclusive.as_ref_value(), span_range), + false, + )?; start..array.items.len() } RangeValueInner::RangeTo { end_exclusive, .. } => { - end = array.resolve_valid_index(Spanned(end_exclusive, span_range), true)?; + end = array + .resolve_valid_index(Spanned(end_exclusive.as_ref_value(), span_range), true)?; start..end } RangeValueInner::RangeFull { .. } => start..end, @@ -103,14 +144,23 @@ impl Spanned<&RangeValue> { end_inclusive, .. } => { - start = array.resolve_valid_index(Spanned(start_inclusive, span_range), false)?; + start = array.resolve_valid_index( + Spanned(start_inclusive.as_ref_value(), span_range), + false, + )?; // +1 is safe because it must be < array length. - end = array.resolve_valid_index(Spanned(end_inclusive, span_range), false)? + 1; + end = array.resolve_valid_index( + Spanned(end_inclusive.as_ref_value(), span_range), + false, + )? + 1; start..end } RangeValueInner::RangeToInclusive { end_inclusive, .. } => { // +1 is safe because it must be < array length. - end = array.resolve_valid_index(Spanned(end_inclusive, span_range), false)? + 1; + end = array.resolve_valid_index( + Spanned(end_inclusive.as_ref_value(), span_range), + false, + )? + 1; start..end } }) @@ -118,59 +168,34 @@ impl Spanned<&RangeValue> { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum RangeKind { +pub(crate) enum RangeStructure { /// `start .. end` - Range, + FromTo, /// `start ..` - RangeFrom, + From, /// `.. end` - RangeTo, + To, /// `..` - RangeFull, + Full, /// `start ..= end` - RangeInclusive, + FromToInclusive, /// `..= end` - RangeToInclusive, + ToInclusive, } -impl IsSpecificValueKind for RangeKind { - fn display_name(&self) -> &'static str { +impl RangeStructure { + pub(crate) fn articled_display_name(&self) -> &'static str { match self { - RangeKind::Range => "range start..end", - RangeKind::RangeFrom => "range start..", - RangeKind::RangeTo => "range ..end", - RangeKind::RangeFull => "range ..", - RangeKind::RangeInclusive => "range start..=end", - RangeKind::RangeToInclusive => "range ..=end", - } - } - - fn articled_display_name(&self) -> &'static str { - match self { - RangeKind::Range => "a range start..end", - RangeKind::RangeFrom => "a range start..", - RangeKind::RangeTo => "a range ..end", - RangeKind::RangeFull => "a range ..", - RangeKind::RangeInclusive => "a range start..=end", - RangeKind::RangeToInclusive => "a range ..=end", + RangeStructure::FromTo => "a range start..end", + RangeStructure::From => "a range start..", + RangeStructure::To => "a range ..end", + RangeStructure::Full => "a range ..", + RangeStructure::FromToInclusive => "a range start..=end", + RangeStructure::ToInclusive => "a range ..=end", } } } -impl From for ValueKind { - fn from(kind: RangeKind) -> Self { - ValueKind::Range(kind) - } -} - -impl HasValueKind for RangeValue { - type SpecificKind = RangeKind; - - fn kind(&self) -> RangeKind { - self.inner.kind() - } -} - impl ValuesEqual for RangeValue { /// Ranges are equal if they have the same kind and the same bounds. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { @@ -244,7 +269,10 @@ impl ValuesEqual for RangeValue { .. }, ) => ctx.with_range_end(|ctx| l_end.test_equality(r_end, ctx)), - _ => ctx.kind_mismatch(self, other), + _ => ctx.range_structure_mismatch( + self.inner.structure_kind(), + other.inner.structure_kind(), + ), } } } @@ -256,48 +284,48 @@ impl ValuesEqual for RangeValue { pub(crate) enum RangeValueInner { /// `start .. end` Range { - start_inclusive: Value, + start_inclusive: AnyValue, token: Token![..], - end_exclusive: Value, + end_exclusive: AnyValue, }, /// `start ..` RangeFrom { - start_inclusive: Value, + start_inclusive: AnyValue, token: Token![..], }, /// `.. end` RangeTo { token: Token![..], - end_exclusive: Value, + end_exclusive: AnyValue, }, /// `..` (used inside arrays) RangeFull { token: Token![..] }, /// `start ..= end` RangeInclusive { - start_inclusive: Value, + start_inclusive: AnyValue, token: Token![..=], - end_inclusive: Value, + end_inclusive: AnyValue, }, /// `..= end` RangeToInclusive { token: Token![..=], - end_inclusive: Value, + end_inclusive: AnyValue, }, } impl RangeValueInner { - fn kind(&self) -> RangeKind { + fn structure_kind(&self) -> RangeStructure { match self { - Self::Range { .. } => RangeKind::Range, - Self::RangeFrom { .. } => RangeKind::RangeFrom, - Self::RangeTo { .. } => RangeKind::RangeTo, - Self::RangeFull { .. } => RangeKind::RangeFull, - Self::RangeInclusive { .. } => RangeKind::RangeInclusive, - Self::RangeToInclusive { .. } => RangeKind::RangeToInclusive, + Self::Range { .. } => RangeStructure::FromTo, + Self::RangeFrom { .. } => RangeStructure::From, + Self::RangeTo { .. } => RangeStructure::To, + Self::RangeFull { .. } => RangeStructure::Full, + Self::RangeInclusive { .. } => RangeStructure::FromToInclusive, + Self::RangeToInclusive { .. } => RangeStructure::ToInclusive, } } - pub(super) fn into_iterable(self) -> ExecutionResult> { + pub(super) fn into_iterable(self) -> ExecutionResult> { Ok(match self { Self::Range { start_inclusive, @@ -344,33 +372,37 @@ impl RangeValueInner { } } -impl IntoValue for RangeValueInner { - fn into_value(self) -> Value { - Value::Range(RangeValue { +impl IsValueContent for RangeValueInner { + type Type = RangeType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for RangeValueInner { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + RangeValue { inner: Box::new(self), - }) + } } } impl_resolvable_argument_for! { - RangeTypeData, + RangeType, (value, context) -> RangeValue { match value { - Value::Range(value) => Ok(value), + AnyValue::Range(value) => Ok(value), _ => context.err("a range", value), } } } -define_interface! { - struct RangeTypeData, - parent: IterableTypeData, +define_type_features! { + impl RangeType, pub(crate) mod range_interface { pub(crate) mod methods { } pub(crate) mod unary_operations { - [context] fn cast_via_iterator(Spanned(this, span): Spanned>) -> ExecutionResult { - let this_iterator = this.try_map(IteratorValue::new_for_range)?; + [context] fn cast_via_iterator(Spanned(this, span): Spanned) -> ExecutionResult { + let this_iterator = IteratorValue::new_for_range(this)?; Ok(context.operation.evaluate(Spanned(this_iterator, span))?.0) } } @@ -379,7 +411,7 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Cast { .. } - if IteratorTypeData::resolve_own_unary_operation(operation).is_some() => + if IteratorType::resolve_own_unary_operation(operation).is_some() => { unary_definitions::cast_via_iterator() } @@ -404,11 +436,11 @@ pub(super) enum IterableRangeOf { }, } -fn resolve_range + ResolvableRange>( +fn resolve_range + ResolvableRange>( start: T, dots: syn::RangeLimits, - end: Option>, -) -> ExecutionResult>> { + end: Option>, +) -> ExecutionResult>> { let definition = match (end, dots) { (Some(end), dots) => { let end = end.resolve_as("The end of this range bound")?; @@ -425,26 +457,24 @@ fn resolve_range + ResolvableRange>( trait ResolvableRange: Sized { fn resolve( definition: IterableRangeOf, - ) -> ExecutionResult>>; + ) -> ExecutionResult>>; } -impl IterableRangeOf { +impl IterableRangeOf { pub(super) fn resolve_iterator( self, - ) -> ExecutionResult>> { + ) -> ExecutionResult>> { let (start, dots, end) = match self { - Self::RangeFromTo { start, dots, end } => ( - start, - dots, - Some(end.into_owned().spanned(dots.span_range())), - ), + Self::RangeFromTo { start, dots, end } => { + (start, dots, Some(end.spanned(dots.span_range()))) + } Self::RangeFrom { start, dots } => (start, RangeLimits::HalfOpen(dots), None), }; match start { - Value::Integer(mut start) => { + AnyValue::Integer(mut start) => { if let Some(end) = &end { start = IntegerValue::resolve_untyped_to_match_other( - start.into_owned().spanned(dots.span_range()), + start.spanned(dots.span_range()), end, )?; } @@ -464,7 +494,7 @@ impl IterableRangeOf { IntegerValue::Isize(start) => resolve_range(start, dots, end), } } - Value::Char(start) => resolve_range(start.value, dots, end), + AnyValue::Char(start) => resolve_range(start, dots, end), _ => dots.value_err("The range must be between two integers or two characters"), } } @@ -473,24 +503,26 @@ impl IterableRangeOf { impl ResolvableRange for UntypedInteger { fn resolve( definition: IterableRangeOf, - ) -> ExecutionResult>> { + ) -> ExecutionResult>> { match definition { IterableRangeOf::RangeFromTo { start, dots, end } => { let start = start.into_fallback(); let end = end.into_fallback(); Ok(match dots { syn::RangeLimits::HalfOpen { .. } => Box::new( - (start..end).map(move |x| UntypedInteger::from_fallback(x).into_value()), + (start..end) + .map(move |x| UntypedInteger::from_fallback(x).into_any_value()), ), syn::RangeLimits::Closed { .. } => Box::new( - (start..=end).map(move |x| UntypedInteger::from_fallback(x).into_value()), + (start..=end) + .map(move |x| UntypedInteger::from_fallback(x).into_any_value()), ), }) } IterableRangeOf::RangeFrom { start, .. } => { let start = start.into_fallback(); Ok(Box::new((start..).map(move |x| { - UntypedInteger::from_fallback(x).into_value() + UntypedInteger::from_fallback(x).into_any_value() }))) } } @@ -502,20 +534,20 @@ macro_rules! define_range_resolvers { $($the_type:ident),* $(,)? ) => {$( impl ResolvableRange for $the_type { - fn resolve(definition: IterableRangeOf) -> ExecutionResult>> { + fn resolve(definition: IterableRangeOf) -> ExecutionResult>> { match definition { IterableRangeOf::RangeFromTo { start, dots, end } => { Ok(match dots { syn::RangeLimits::HalfOpen { .. } => { - Box::new((start..end).map(move |x| x.into_value())) + Box::new((start..end).map(move |x| x.into_any_value())) } syn::RangeLimits::Closed { .. } => { - Box::new((start..=end).map(move |x| x.into_value())) + Box::new((start..=end).map(move |x| x.into_any_value())) } }) }, IterableRangeOf::RangeFrom { start, .. } => { - Ok(Box::new((start..).map(move |x| x.into_value()))) + Ok(Box::new((start..).map(move |x| x.into_any_value()))) }, } } diff --git a/src/expressions/values/stream.rs b/src/expressions/values/stream.rs index 1fe5f711..fc856de3 100644 --- a/src/expressions/values/stream.rs +++ b/src/expressions/values/stream.rs @@ -1,22 +1,36 @@ use super::*; -#[derive(Clone)] -pub(crate) struct StreamValue { - pub(crate) value: OutputStream, +define_leaf_type! { + pub(crate) StreamType => AnyType(AnyValueContent::Stream), + content: OutputStream, + kind: pub(crate) StreamKind, + type_name: "stream", + articled_display_name: "a stream", + dyn_impls: { + IterableType: impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(IteratorValue::new_for_stream(*self)) + } + + fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + Ok(self.len()) + } + } + }, } -impl StreamValue { - pub(crate) fn concat_recursive_into(&self, output: &mut String, behaviour: &ConcatBehaviour) { +impl OutputStream { + pub(crate) fn concat_as_literal_into(&self, output: &mut String, behaviour: &ConcatBehaviour) { if behaviour.use_stream_literal_syntax { - if self.value.is_empty() { + if self.is_empty() { output.push_str("%[]"); } else { output.push_str("%["); - self.value.concat_recursive_into(output, behaviour); + self.concat_content_into(output, behaviour); output.push(']'); } } else { - self.value.concat_recursive_into(output, behaviour); + self.concat_content_into(output, behaviour); } } @@ -47,7 +61,7 @@ impl StreamValue { // transparent groups (as of Jan 2025), so gets it right without this flattening: // https://github.com/rust-lang/rust-analyzer/issues/18211 - let error_span_stream = self.value.to_token_stream_removing_any_transparent_groups(); + let error_span_stream = self.to_token_stream_removing_any_transparent_groups(); if error_span_stream.is_empty() { None } else { @@ -56,18 +70,10 @@ impl StreamValue { } } -impl HasValueKind for StreamValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::Stream - } -} - -impl Debug for StreamValue { +impl Debug for OutputStream { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut debug_string = String::new(); - self.concat_recursive_into( + self.concat_as_literal_into( &mut debug_string, &ConcatBehaviour::debug(Span::call_site().span_range()), ); @@ -75,18 +81,14 @@ impl Debug for StreamValue { } } -impl ValuesEqual for StreamValue { +impl ValuesEqual for OutputStream { /// Compares two streams by their debug string representation, ignoring spans. /// Transparent groups (none-delimited groups) are preserved in comparison. /// Use `remove_transparent_groups()` before comparison if you want to ignore them. fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { // Use debug concat_recursive which preserves transparent group structure - let lhs = self - .value - .concat_recursive(&ConcatBehaviour::debug(Span::call_site().span_range())); - let rhs = other - .value - .concat_recursive(&ConcatBehaviour::debug(Span::call_site().span_range())); + let lhs = self.concat_content(&ConcatBehaviour::debug(Span::call_site().span_range())); + let rhs = other.concat_content(&ConcatBehaviour::debug(Span::call_site().span_range())); if lhs == rhs { ctx.values_equal() } else { @@ -95,41 +97,29 @@ impl ValuesEqual for StreamValue { } } -impl IntoValue for StreamValue { - fn into_value(self) -> Value { - Value::Stream(self) - } -} - -impl IntoValue for OutputStream { - fn into_value(self) -> Value { - StreamValue { value: self }.into_value() - } +impl IsValueContent for TokenStream { + type Type = StreamType; + type Form = BeOwned; } -impl IntoValue for TokenStream { - fn into_value(self) -> Value { - OutputStream::raw(self).into_value() +impl IntoValueContent<'static> for TokenStream { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + OutputStream::raw(self) } } impl_resolvable_argument_for! { - StreamTypeData, - (value, context) -> StreamValue { + StreamType, + (value, context) -> OutputStream { match value { - Value::Stream(value) => Ok(value), + AnyValueContent::Stream(value) => Ok(value), _ => context.err("a stream", value), } } } -impl_delegated_resolvable_argument_for!( - (value: StreamValue) -> OutputStream { value.value } -); - -define_interface! { - struct StreamTypeData, - parent: IterableTypeData, +define_type_features! { + impl StreamType, pub(crate) mod stream_interface { pub(crate) mod methods { // This is also on iterable, but is specialized here for performance @@ -152,7 +142,7 @@ define_interface! { OutputStream::raw(this.to_token_stream_removing_any_transparent_groups()) } - fn infer(this: OutputStream) -> ExecutionResult { + fn infer(this: OutputStream) -> ExecutionResult { Ok(this.coerce_into_value()) } @@ -164,48 +154,48 @@ define_interface! { // =============================== [context] fn to_ident(this: Spanned>) -> ExecutionResult { - let string = this.concat_recursive(&ConcatBehaviour::standard(this.span_range())); - string_interface::methods::to_ident(context, string.as_str().into_spanned_ref(this.span_range())) + 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 { - let string = this.concat_recursive(&ConcatBehaviour::standard(this.span_range())); - string_interface::methods::to_ident_camel(context, string.as_str().into_spanned_ref(this.span_range())) + 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 { - let string = this.concat_recursive(&ConcatBehaviour::standard(this.span_range())); - string_interface::methods::to_ident_snake(context, string.as_str().into_spanned_ref(this.span_range())) + 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 { - let string = this.concat_recursive(&ConcatBehaviour::standard(this.span_range())); - string_interface::methods::to_ident_upper_snake(context, string.as_str().into_spanned_ref(this.span_range())) + 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 { - let string = this.concat_recursive(&ConcatBehaviour::literal(this.span_range())); - let literal = string_interface::methods::to_literal(context, string.as_str().into_spanned_ref(this.span_range()))?; - Ok(Value::for_literal(literal).into_value()) + [context] fn to_literal(this: Spanned>) -> ExecutionResult { + 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()) } // CORE METHODS // ============ // NOTE: with_span() exists on all values, this is just a specialized mutable version for streams - fn set_span(mut this: Mutable, span_source: Shared) -> ExecutionResult<()> { + fn set_span(mut this: Mutable, span_source: AnyRef) -> ExecutionResult<()> { let span_range = span_source.resolve_content_span_range().unwrap_or(Span::call_site().span_range()); - this.value.replace_first_level_spans(span_range.join_into_span_else_start()); + this.replace_first_level_spans(span_range.join_into_span_else_start()); Ok(()) } - fn error(this: Shared, message: Shared) -> ExecutionResult { + fn error(this: AnyRef, message: AnyRef) -> ExecutionResult { 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: Shared, condition: bool, message: Option>) -> ExecutionResult<()> { + fn assert(this: AnyRef, condition: bool, message: Option>) -> ExecutionResult<()> { if condition { Ok(()) } else { @@ -218,10 +208,8 @@ define_interface! { } } - fn assert_eq(this: Shared, lhs: Spanned>, rhs: Spanned>, message: Option>) -> ExecutionResult<()> { - let lhs_value: &Value = &lhs; - let rhs_value: &Value = &rhs; - match Value::debug_eq(lhs_value, rhs_value) { + fn assert_eq(this: AnyRef, lhs: Spanned, rhs: Spanned, message: Option>) -> ExecutionResult<()> { + match AnyValueRef::debug_eq(&lhs.as_ref_value(), &rhs.as_ref_value()) { Ok(()) => Ok(()), Err(debug_error) => { let error_span_range = this.resolve_content_span_range().unwrap_or(Span::call_site().span_range()); @@ -230,8 +218,8 @@ define_interface! { None => format!( "Assertion failed: {}\n lhs = {}\n rhs = {}", debug_error.format_message(), - lhs.concat_recursive(&ConcatBehaviour::debug(lhs.span_range()))?, - rhs.concat_recursive(&ConcatBehaviour::debug(rhs.span_range()))?, + lhs.as_ref_value().concat_recursive(&ConcatBehaviour::debug(lhs.span_range()))?, + rhs.as_ref_value().concat_recursive(&ConcatBehaviour::debug(rhs.span_range()))?, ), }; error_span_range.assertion_err(message) @@ -239,8 +227,8 @@ define_interface! { } } - [context] fn reinterpret_as_run(Spanned(this, span_range): Spanned>) -> ExecutionResult { - let source = this.into_inner().value.into_token_stream(); + [context] fn reinterpret_as_run(Spanned(this, span_range): Spanned) -> ExecutionResult { + 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(); @@ -250,8 +238,8 @@ define_interface! { Ok(return_value.0) } - fn reinterpret_as_stream(Spanned(this, span_range): Spanned>) -> ExecutionResult { - let source = this.into_inner().value.into_token_stream(); + fn reinterpret_as_stream(Spanned(this, span_range): Spanned) -> ExecutionResult { + 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, @@ -262,14 +250,13 @@ define_interface! { } } pub(crate) mod unary_operations { - [context] fn cast_to_value(Spanned(this, span): Spanned>) -> ExecutionResult { - let this = this.into_inner(); - let coerced = this.value.coerce_into_value(); - if let Value::Stream(_) = &coerced { + [context] fn cast_coerced_to_value(Spanned(this, span): Spanned) -> ExecutionResult { + 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.into_owned(), span))?.0) + Ok(context.operation.evaluate(Spanned(coerced, span))?.0) } } pub(crate) mod binary_operations { @@ -285,14 +272,7 @@ define_interface! { interface_items { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { - UnaryOperation::Cast { - target: - CastTarget::Boolean - | CastTarget::Char - | CastTarget::Integer(_) - | CastTarget::Float(_), - .. - } => unary_definitions::cast_to_value(), + UnaryOperation::Cast { target, .. } if target.is_singleton_target() => unary_definitions::cast_coerced_to_value(), _ => return None, }) } @@ -540,35 +520,35 @@ impl Interpret for ConcatenatedStreamLiteral { fn interpret(&self, interpreter: &mut Interpreter) -> ExecutionResult<()> { let stream = interpreter.capture_output(|interpreter| self.content.interpret(interpreter))?; - let string = stream.concat_recursive(&ConcatBehaviour::standard(self.span_range())); - let ident_span = StreamValue { value: stream } + let string = stream.concat_content(&ConcatBehaviour::standard(self.span_range())); + let ident_span = stream .resolve_content_span_range() .unwrap_or_else(|| self.span_range()) .join_into_span_else_start(); let value = match self.kind { - ConcatenatedStreamLiteralKind::String => string.into_value(), + ConcatenatedStreamLiteralKind::String => string.into_any_value(), ConcatenatedStreamLiteralKind::Ident => { let str = &string; - string_to_ident(str, self, ident_span)?.into_value() + string_to_ident(str, self, ident_span)?.into_any_value() } ConcatenatedStreamLiteralKind::IdentCamel => { let str = &string_conversion::to_upper_camel_case(&string); - string_to_ident(str, self, ident_span)?.into_value() + string_to_ident(str, self, ident_span)?.into_any_value() } ConcatenatedStreamLiteralKind::IdentSnake => { let str = &string_conversion::to_lower_snake_case(&string); - string_to_ident(str, self, ident_span)?.into_value() + string_to_ident(str, self, ident_span)?.into_any_value() } ConcatenatedStreamLiteralKind::IdentUpperSnake => { let str = &string_conversion::to_upper_snake_case(&string); - string_to_ident(str, self, ident_span)?.into_value() + string_to_ident(str, self, ident_span)?.into_any_value() } ConcatenatedStreamLiteralKind::Literal => { let str = &string; - string_to_literal(str, self, ident_span)?.into_value() + string_to_literal(str, self, ident_span)?.into_any_value() } }; - value.output_to( + value.as_ref_value().output_to( Grouping::Flattened, &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), ) diff --git a/src/expressions/values/string.rs b/src/expressions/values/string.rs index fee4eeb3..99124508 100644 --- a/src/expressions/values/string.rs +++ b/src/expressions/values/string.rs @@ -1,43 +1,40 @@ use super::*; -#[derive(Clone)] -pub(crate) struct StringValue { - pub(crate) value: String, -} - -impl Debug for StringValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.value) - } +define_leaf_type! { + pub(crate) StringType => AnyType(AnyValueContent::String), + content: String, + kind: pub(crate) StringKind, + type_name: "string", + articled_display_name: "a string", + dyn_impls: { + IterableType: impl IsIterable { + fn into_iterator(self: Box) -> ExecutionResult { + Ok(IteratorValue::new_for_string_over_chars(*self)) + } + + fn len(&self, _error_span_range: SpanRange) -> ExecutionResult { + // The iterator is over chars, so this must count chars. + // But contrast, string.len() counts bytes + Ok(self.chars().count()) + } + } + }, } -impl IntoValue for StringValue { - fn into_value(self) -> Value { - Value::String(self) - } +impl IsValueContent for &str { + type Type = StringType; + type Form = BeRef; } -impl StringValue { - pub(super) fn for_litstr(lit: &syn::LitStr) -> Owned { - Self { value: lit.value() }.into_owned() - } - - pub(super) fn to_literal(&self, span: Span) -> Literal { - Literal::string(&self.value).with_span(span) +impl<'a> FromValueContent<'a> for &'a str { + fn from_content(value: &'a String) -> Self { + value.as_str() } } -impl HasValueKind for StringValue { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::String - } -} - -impl ValuesEqual for StringValue { +impl ValuesEqual for String { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.value == other.value { + if self == other { ctx.values_equal() } else { ctx.leaf_values_not_equal(self, other) @@ -45,20 +42,6 @@ impl ValuesEqual for StringValue { } } -impl IntoValue for String { - fn into_value(self) -> Value { - Value::String(StringValue { value: self }) - } -} - -impl IntoValue for &str { - fn into_value(self) -> Value { - Value::String(StringValue { - value: self.to_string(), - }) - } -} - pub(crate) fn string_to_ident( str: &str, error_source: &impl HasSpanRange, @@ -81,81 +64,80 @@ pub(crate) fn string_to_literal( Ok(literal.with_span(span)) } -define_interface! { - struct StringTypeData, - parent: IterableTypeData, +define_type_features! { + impl StringType, pub(crate) mod string_interface { pub(crate) mod methods { // ================== // CONVERSION METHODS // ================== - [context] fn to_ident(this: Spanned>) -> ExecutionResult { + [context] fn to_ident(this: Spanned>) -> ExecutionResult { 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>) -> ExecutionResult { 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>) -> ExecutionResult { 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>) -> ExecutionResult { 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>) -> ExecutionResult { string_to_literal(&this, &this, context.span_from_join_else_start()) } // ====================== // STRING RESHAPE METHODS // ====================== - fn to_uppercase(this: AnyRef) -> String { + fn to_uppercase(this: AnyRef) -> String { string_conversion::to_uppercase(&this) } - fn to_lowercase(this: AnyRef) -> String { + fn to_lowercase(this: AnyRef) -> String { string_conversion::to_lowercase(&this) } - fn to_lower_snake_case(this: AnyRef) -> String { + fn to_lower_snake_case(this: AnyRef) -> String { string_conversion::to_lower_snake_case(&this) } - fn to_upper_snake_case(this: AnyRef) -> String { + fn to_upper_snake_case(this: AnyRef) -> String { string_conversion::to_upper_snake_case(&this) } - fn to_kebab_case(this: AnyRef) -> String { + fn to_kebab_case(this: AnyRef) -> String { string_conversion::to_lower_kebab_case(&this) } - fn to_lower_camel_case(this: AnyRef) -> String { + fn to_lower_camel_case(this: AnyRef) -> String { string_conversion::to_lower_camel_case(&this) } - fn to_upper_camel_case(this: AnyRef) -> String { + fn to_upper_camel_case(this: AnyRef) -> String { string_conversion::to_upper_camel_case(&this) } - fn capitalize(this: AnyRef) -> String { + fn capitalize(this: AnyRef) -> String { string_conversion::capitalize(&this) } - fn decapitalize(this: AnyRef) -> String { + fn decapitalize(this: AnyRef) -> String { string_conversion::decapitalize(&this) } - fn to_title_case(this: AnyRef) -> String { + fn to_title_case(this: AnyRef) -> String { string_conversion::title_case(&this) } - fn insert_spaces(this: AnyRef) -> String { + fn insert_spaces(this: AnyRef) -> String { string_conversion::insert_spaces_between_words(&this) } } @@ -165,36 +147,36 @@ define_interface! { } } pub(crate) mod binary_operations { - fn add(mut lhs: String, rhs: Shared) -> String { + fn add(mut lhs: String, rhs: AnyRef) -> String { lhs.push_str(rhs.deref()); lhs } - fn add_assign(mut lhs: Assignee, rhs: Shared) { + fn add_assign(mut lhs: Assignee, rhs: AnyRef) { lhs.push_str(rhs.deref()); } - fn eq(lhs: Shared, rhs: Shared) -> bool { + fn eq(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() == rhs.deref() } - fn ne(lhs: Shared, rhs: Shared) -> bool { + fn ne(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() != rhs.deref() } - fn lt(lhs: Shared, rhs: Shared) -> bool { + fn lt(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() < rhs.deref() } - fn le(lhs: Shared, rhs: Shared) -> bool { + fn le(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() <= rhs.deref() } - fn ge(lhs: Shared, rhs: Shared) -> bool { + fn ge(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() >= rhs.deref() } - fn gt(lhs: Shared, rhs: Shared) -> bool { + fn gt(lhs: AnyRef, rhs: AnyRef) -> bool { lhs.deref() > rhs.deref() } } @@ -202,8 +184,8 @@ define_interface! { fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { Some(match operation { UnaryOperation::Neg { .. } | UnaryOperation::Not { .. } => return None, - UnaryOperation::Cast { target, .. } => match target { - CastTarget::String => unary_definitions::cast_to_string(), + UnaryOperation::Cast { target: CastTarget(kind), .. } => match kind { + AnyValueLeafKind::String(_) => unary_definitions::cast_to_string(), _ => return None, }, }) @@ -229,30 +211,26 @@ define_interface! { } impl_resolvable_argument_for! { - StringTypeData, - (value, context) -> StringValue { + StringType, + (value, context) -> String { match value { - Value::String(value) => Ok(value), + AnyValueContent::String(value) => Ok(value), _ => context.err("a string", value), } } } -impl_delegated_resolvable_argument_for!( - (value: StringValue) -> String { value.value } -); - impl ResolvableArgumentTarget for str { - type ValueType = StringTypeData; + type ValueType = StringType; } -impl ResolvableShared for str { +impl ResolvableShared for str { fn resolve_from_ref<'a>( - value: &'a Value, + value: &'a AnyValue, context: ResolutionContext, ) -> ExecutionResult<&'a Self> { match value { - Value::String(s) => Ok(s.value.as_str()), + AnyValueContent::String(s) => Ok(s.as_str()), _ => context.err("a string", value), } } diff --git a/src/expressions/values/unsupported_literal.rs b/src/expressions/values/unsupported_literal.rs index d4fc7d80..071a4b8e 100644 --- a/src/expressions/values/unsupported_literal.rs +++ b/src/expressions/values/unsupported_literal.rs @@ -1,13 +1,20 @@ use super::*; -#[derive(Clone)] -pub(crate) struct UnsupportedLiteral { - pub(crate) lit: syn::Lit, +define_leaf_type! { + pub(crate) UnsupportedLiteralType => AnyType(AnyValueContent::UnsupportedLiteral), + content: UnsupportedLiteral, + kind: pub(crate) UnsupportedLiteralKind, + type_name: "unsupported_literal", + articled_display_name: "an unsupported literal", + dyn_impls: {}, } +#[derive(Clone)] +pub(crate) struct UnsupportedLiteral(pub(crate) syn::Lit); + impl ValuesEqual for UnsupportedLiteral { fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - if self.lit.to_token_stream().to_string() == other.lit.to_token_stream().to_string() { + if self.0.to_token_stream().to_string() == other.0.to_token_stream().to_string() { ctx.values_equal() } else { ctx.leaf_values_not_equal(self, other) @@ -17,21 +24,12 @@ impl ValuesEqual for UnsupportedLiteral { impl Debug for UnsupportedLiteral { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.lit.to_token_stream()) - } -} - -impl HasValueKind for UnsupportedLiteral { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - ValueKind::UnsupportedLiteral + write!(f, "{}", self.0.to_token_stream()) } } -define_interface! { - struct UnsupportedLiteralTypeData, - parent: ValueTypeData, +define_type_features! { + impl UnsupportedLiteralType, pub(crate) mod unsupported_literal_interface { pub(crate) mod methods {} pub(crate) mod unary_operations {} diff --git a/src/expressions/values/value.rs b/src/expressions/values/value.rs deleted file mode 100644 index c80a99e3..00000000 --- a/src/expressions/values/value.rs +++ /dev/null @@ -1,1128 +0,0 @@ -use super::*; - -// ============================================================================ -// Equality Context - Controls behavior of value equality comparisons -// ============================================================================ - -/// A segment in the path to the current comparison location. -#[derive(Clone, Debug)] -pub(crate) enum PathSegment { - ArrayIndex(usize), - ObjectKey(String), - IteratorIndex(usize), - RangeStart, - RangeEnd, -} - -impl PathSegment { - fn fmt_path(path: &[PathSegment]) -> String { - let mut result = String::new(); - for segment in path { - match segment { - PathSegment::ArrayIndex(i) => result.push_str(&format!("[{}]", i)), - PathSegment::ObjectKey(k) => { - if syn::parse_str::(k).is_ok() { - result.push_str(&format!(".{}", k)) - } else { - 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"), - } - } - result - } -} - -/// Context trait for controlling equality comparison behavior. -/// -/// Different implementations allow for: -/// - Simple equality: returns `false` on type mismatch (like JS `===`) -/// - Typed equality: errors on type mismatch, tracks path for error messages -/// - Assert equality: errors on any difference, useful for assert_eq -pub(crate) trait EqualityContext { - /// The result type returned by equality comparisons. - type Result; - - /// Values are equal. - fn values_equal(&mut self) -> Self::Result; - - /// Values of the same type are not equal. - fn leaf_values_not_equal(&mut self, lhs: &T, rhs: &T) -> Self::Result; - - /// Values have different types. - fn kind_mismatch(&mut self, lhs: &L, rhs: &R) - -> Self::Result; - - /// Arrays or iterators have different lengths. - fn lengths_unequal(&mut self, lhs_len: Option, rhs_len: Option) -> Self::Result; - - /// 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; - - /// Wrap a comparison within a range end context. - fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R; - - /// Returns true if the result indicates we should stop comparing and return early. - /// This is true when the result indicates "not equal" or an error occurred. - fn should_short_circuit(&self, result: &Self::Result) -> bool; -} - -/// Simple equality context - returns `false` on type mismatch, no path tracking. -/// This is the most efficient option when you just need a bool result. -pub(crate) struct SimpleEquality; - -impl EqualityContext for SimpleEquality { - type Result = bool; - - #[inline] - fn values_equal(&mut self) -> bool { - true - } - - #[inline] - fn leaf_values_not_equal(&mut self, _lhs: &T, _rhs: &T) -> bool { - false - } - - #[inline] - fn kind_mismatch(&mut self, _lhs: &L, _rhs: &R) -> bool { - false - } - - #[inline] - fn lengths_unequal(&mut self, _lhs_len: Option, _rhs_len: Option) -> bool { - false - } - - #[inline] - fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> bool { - false - } - - #[inline] - fn with_array_index(&mut self, _index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - f(self) - } - - #[inline] - fn with_object_key(&mut self, _key: &str, f: impl FnOnce(&mut Self) -> R) -> R { - 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) - } - - #[inline] - fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - f(self) - } - - #[inline] - fn should_short_circuit(&self, result: &bool) -> bool { - !*result - } -} - -/// Typed equality context - errors on type mismatch, tracks path for error messages. -pub(crate) struct TypedEquality { - path: Vec, - error_span: SpanRange, -} - -impl TypedEquality { - pub(crate) fn new(error_span: SpanRange) -> Self { - Self { - path: Vec::new(), - error_span, - } - } -} - -impl EqualityContext for TypedEquality { - type Result = ExecutionResult; - - #[inline] - fn values_equal(&mut self) -> ExecutionResult { - Ok(true) - } - - #[inline] - fn leaf_values_not_equal(&mut self, _lhs: &T, _rhs: &T) -> ExecutionResult { - Ok(false) - } - - fn kind_mismatch( - &mut self, - lhs: &L, - rhs: &R, - ) -> ExecutionResult { - let path_str = PathSegment::fmt_path(&self.path); - Err(self.error_span.type_error(format!( - "lhs{} is {}, but rhs{} is {}", - path_str, - lhs.articled_value_type(), - path_str, - rhs.articled_value_type() - ))) - } - - #[inline] - fn lengths_unequal( - &mut self, - _lhs_len: Option, - _rhs_len: Option, - ) -> ExecutionResult { - Ok(false) - } - - #[inline] - fn missing_key(&mut self, _key: &str, _missing_on: MissingSide) -> ExecutionResult { - Ok(false) - } - - #[inline] - fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::ArrayIndex(index)); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::ObjectKey(key.to_string())); - let result = f(self); - self.path.pop(); - 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); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::RangeEnd); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn should_short_circuit(&self, result: &ExecutionResult) -> bool { - // Short-circuit on Ok(false) or Err(_) - !matches!(result, Ok(true)) - } -} - -// ============================================================================ -// Debug Equality - Captures detailed information about equality failures -// ============================================================================ - -/// Which side of the comparison is missing a key. -#[derive(Debug, Clone, Copy)] -pub(crate) enum MissingSide { - #[allow(dead_code)] - Lhs, - Rhs, -} - -/// The reason why two values were not equal. -#[derive(Debug, Clone)] -pub(crate) enum DebugInequalityReason { - /// Values of the same type have different values. - ValueMismatch { - lhs_display: String, - rhs_display: String, - }, - /// Values have incompatible value kinds. - ValueKindMismatch { - lhs_kind: ValueKind, - rhs_kind: ValueKind, - }, - /// Collections have different lengths. - LengthMismatch { - lhs_len: Option, - rhs_len: Option, - }, - /// Object is missing a key on one side. - MissingKey { - key: String, - missing_on: MissingSide, - }, -} - -pub(crate) struct DebugEqualityError { - inner: Box, -} - -struct DebugEqualityErrorInner { - path: Vec, - reason: DebugInequalityReason, -} - -impl DebugEqualityError { - pub fn format_message(&self) -> String { - let inner = &self.inner; - let path_str = PathSegment::fmt_path(&inner.path); - - match &inner.reason { - DebugInequalityReason::ValueMismatch { - lhs_display, - rhs_display, - } => { - format!( - "lhs{} != rhs{}: {} != {}", - path_str, path_str, lhs_display, rhs_display - ) - } - DebugInequalityReason::ValueKindMismatch { lhs_kind, rhs_kind } => { - format!( - "lhs{} is {}, but rhs{} is {}", - path_str, - lhs_kind.articled_display_name(), - path_str, - rhs_kind.articled_display_name() - ) - } - DebugInequalityReason::LengthMismatch { lhs_len, rhs_len } => { - format!( - "lhs{} has length {}, but rhs{} has length {}", - path_str, - lhs_len.unwrap_or(0), - path_str, - rhs_len.unwrap_or(0) - ) - } - DebugInequalityReason::MissingKey { key, missing_on } => match missing_on { - MissingSide::Lhs => { - format!( - "lhs{} is missing key {:?}, compared to rhs{}", - path_str, key, path_str - ) - } - MissingSide::Rhs => { - format!( - "lhs{} has extra key {:?}, compared to rhs{}", - path_str, key, path_str - ) - } - }, - } - } -} - -impl From for DebugEqualityError { - fn from(inner: DebugEqualityErrorInner) -> Self { - Self { - inner: Box::new(inner), - } - } -} - -/// Debug equality context - captures detailed information about why values differ. -/// This is useful for assertion failures where you want to show exactly where -/// the mismatch occurred. -pub(crate) struct DebugEquality { - path: Vec, -} - -impl DebugEquality { - pub(crate) fn new() -> Self { - Self { path: Vec::new() } - } -} - -impl EqualityContext for DebugEquality { - type Result = Result<(), DebugEqualityError>; - - #[inline] - fn values_equal(&mut self) -> Result<(), DebugEqualityError> { - Ok(()) - } - - #[inline] - fn leaf_values_not_equal( - &mut self, - lhs: &T, - rhs: &T, - ) -> Result<(), DebugEqualityError> { - Err(DebugEqualityErrorInner { - path: self.path.clone(), - reason: DebugInequalityReason::ValueMismatch { - lhs_display: format!("{:?}", lhs), - rhs_display: format!("{:?}", rhs), - }, - })? - } - - fn kind_mismatch( - &mut self, - lhs: &L, - rhs: &R, - ) -> Result<(), DebugEqualityError> { - Err(DebugEqualityErrorInner { - path: self.path.clone(), - reason: DebugInequalityReason::ValueKindMismatch { - lhs_kind: lhs.value_kind(), - rhs_kind: rhs.value_kind(), - }, - })? - } - - #[inline] - fn lengths_unequal( - &mut self, - lhs_len: Option, - rhs_len: Option, - ) -> Result<(), DebugEqualityError> { - Err(DebugEqualityErrorInner { - path: self.path.clone(), - reason: DebugInequalityReason::LengthMismatch { lhs_len, rhs_len }, - })? - } - - #[inline] - fn missing_key( - &mut self, - key: &str, - missing_on: MissingSide, - ) -> Result<(), DebugEqualityError> { - Err(DebugEqualityErrorInner { - path: self.path.clone(), - reason: DebugInequalityReason::MissingKey { - key: key.to_string(), - missing_on, - }, - })? - } - - #[inline] - fn with_array_index(&mut self, index: usize, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::ArrayIndex(index)); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_object_key(&mut self, key: &str, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::ObjectKey(key.to_string())); - let result = f(self); - self.path.pop(); - 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); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn with_range_end(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - self.path.push(PathSegment::RangeEnd); - let result = f(self); - self.path.pop(); - result - } - - #[inline] - fn should_short_circuit(&self, result: &Result<(), DebugEqualityError>) -> bool { - result.is_err() - } -} - -// ============================================================================ -// ValuesEqual trait - Value equality with configurable context -// ============================================================================ - -/// A trait for comparing values for equality with preinterpret semantics. -/// -/// This is NOT the same as Rust's `PartialEq`/`Eq` traits because: -/// - **Type coercion**: Untyped integers/floats can equal typed ones (e.g., `5 == 5u32`) -/// - **Structural comparison**: Arrays, objects, and iterators are compared element-wise -/// - **Token comparison**: Streams and unsupported literals compare via token string representation -/// - **Float semantics**: Floats use Rust's `==`, so `NaN != NaN` -/// -/// The comparison behavior is controlled by the `EqualityContext`: -/// - `SimpleEquality`: Returns `false` on type mismatch (like JS `===`) -/// - `TypedEquality`: Errors on type mismatch with path information -/// - `DebugEquality`: Returns detailed error info for assertion messages -pub(crate) trait ValuesEqual: Sized + HasValueKind { - /// Compare two values for equality using the given context. - fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result; - - /// Lenient equality - returns `false` for incompatible types instead of erroring. - /// Behaves like JavaScript's `===` operator. - fn lenient_eq(&self, other: &Self) -> bool { - self.test_equality(other, &mut SimpleEquality) - } - - /// Strict equality check that errors on incompatible types. - fn typed_eq(&self, other: &Self, error_span: SpanRange) -> ExecutionResult { - self.test_equality(other, &mut TypedEquality::new(error_span)) - } - - /// Debug equality check - returns detailed information about why values differ. - /// Useful for generating informative assertion failure messages. - fn debug_eq(&self, other: &Self) -> Result<(), DebugEqualityError> { - self.test_equality(other, &mut DebugEquality::new()) - } -} - -#[derive(Clone)] -pub(crate) enum Value { - None, - Integer(IntegerValue), - Float(FloatValue), - Boolean(BooleanValue), - String(StringValue), - Char(CharValue), - // Unsupported literal is a type here so that we can parse such a token - // as a value rather than a stream, and give it better error messages - UnsupportedLiteral(UnsupportedLiteral), - Array(ArrayValue), - Object(ObjectValue), - Stream(StreamValue), - Range(RangeValue), - Iterator(IteratorValue), - Parser(ParserValue), -} - -define_interface! { - struct ValueTypeData, - parent: ValueTypeData, - pub(crate) mod value_interface { - pub(crate) mod methods { - fn clone(this: CopyOnWriteValue) -> OwnedValue { - this.into_owned_infallible() - } - - fn as_mut(Spanned(this, span): Spanned) -> ExecutionResult { - Ok(match this { - ArgumentValue::Owned(owned) => Mutable::new_from_owned(owned), - ArgumentValue::CopyOnWrite(copy_on_write) => ArgumentOwnership::Mutable - .map_from_copy_on_write(Spanned(copy_on_write, span))? - .expect_mutable(), - ArgumentValue::Mutable(mutable) => mutable, - ArgumentValue::Assignee(assignee) => assignee.0, - ArgumentValue::Shared(shared) => ArgumentOwnership::Mutable - .map_from_shared(Spanned(shared, span))? - .expect_mutable(), - }) - } - - // NOTE: - // All value types can be coerced into SharedValue as an input, so this method does actually do something - fn as_ref(this: SharedValue) -> SharedValue { - this - } - - fn swap(mut a: AssigneeValue, mut b: AssigneeValue) -> () { - core::mem::swap(a.0.deref_mut(), b.0.deref_mut()); - } - - fn replace(mut a: AssigneeValue, b: Value) -> Value { - core::mem::replace(a.0.deref_mut(), b) - } - - fn debug(Spanned(this, span_range): Spanned) -> ExecutionResult<()> { - let message = this.concat_recursive(&ConcatBehaviour::debug(span_range))?; - span_range.debug_err(message) - } - - fn to_debug_string(Spanned(this, span_range): Spanned) -> ExecutionResult { - this.concat_recursive(&ConcatBehaviour::debug(span_range)) - } - - fn to_stream(Spanned(input, span_range): Spanned) -> ExecutionResult { - input.map_into( - |shared| shared.output_to_new_stream(Grouping::Flattened, span_range), - |owned| owned.0.into_stream(Grouping::Flattened, span_range), - ) - } - - fn to_group(Spanned(input, span_range): Spanned) -> ExecutionResult { - input.map_into( - |shared| shared.output_to_new_stream(Grouping::Grouped, span_range), - |owned| owned.0.into_stream(Grouping::Grouped, span_range), - ) - } - - fn to_string(Spanned(input, span_range): Spanned) -> ExecutionResult { - input.concat_recursive(&ConcatBehaviour::standard(span_range)) - } - - [context] fn with_span(value: Spanned, spans: AnyRef) -> ExecutionResult { - 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(), - None => Span::call_site(), - }; - this.replace_first_level_spans(span_to_use); - Ok(this) - } - - // TYPE CHECKING - // =============================== - fn is_none(this: SharedValue) -> bool { - this.is_none() - } - - // EQUALITY METHODS - // =============================== - // Compare values with strict type checking - errors on value kind mismatch. - [context] fn typed_eq(this: AnyRef, other: AnyRef) -> ExecutionResult { - let this_value: &Value = &this; - let other_value: &Value = &other; - this_value.typed_eq(other_value, context.span_range()) - } - - // STRING-BASED CONVERSION METHODS - // =============================== - - [context] fn to_ident(this: Spanned) -> ExecutionResult { - let stream = this.into_stream()?; - 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()?; - 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()?; - 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()?; - 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()?; - let spanned = stream.into_spanned_ref(context.output_span_range); - stream_interface::methods::to_literal(context, spanned) - } - } - pub(crate) mod unary_operations { - fn cast_to_string(Spanned(input, span_range): Spanned) -> ExecutionResult { - input.concat_recursive(&ConcatBehaviour::standard(span_range)) - } - - fn cast_to_stream(input: Spanned) -> ExecutionResult { - input.into_stream() - } - } - pub(crate) mod binary_operations { - fn eq(lhs: AnyRef, rhs: AnyRef) -> bool { - Value::values_equal(&lhs, &rhs) - } - - fn ne(lhs: AnyRef, rhs: AnyRef) -> bool { - !Value::values_equal(&lhs, &rhs) - } - } - interface_items { - fn resolve_own_unary_operation(operation: &UnaryOperation) -> Option { - Some(match operation { - UnaryOperation::Cast { target, .. } => match target { - CastTarget::String => unary_definitions::cast_to_string(), - CastTarget::Stream => unary_definitions::cast_to_stream(), - _ => return None, - }, - _ => return None, - }) - } - - fn resolve_own_binary_operation( - operation: &BinaryOperation, - ) -> Option { - Some(match operation { - BinaryOperation::Equal { .. } => binary_definitions::eq(), - BinaryOperation::NotEqual { .. } => binary_definitions::ne(), - _ => return None, - }) - } - } - } -} - -pub(crate) trait IntoValue: Sized { - fn into_value(self) -> Value; - fn into_owned(self) -> Owned { - Owned::new(self) - } - fn into_owned_value(self) -> OwnedValue { - Owned(self.into_value()) - } -} - -impl Value { - pub(crate) fn for_literal(literal: Literal) -> OwnedValue { - // The unwrap should be safe because all Literal should be parsable - // as syn::Lit; falling back to syn::Lit::Verbatim if necessary. - Self::for_syn_lit( - literal - .to_token_stream() - .interpreted_parse_with(|input| input.parse()) - .unwrap(), - ) - } - - pub(crate) fn for_syn_lit(lit: syn::Lit) -> OwnedValue { - // https://docs.rs/syn/latest/syn/enum.Lit.html - let matched = match &lit { - Lit::Int(lit) => match IntegerValue::for_litint(lit) { - Ok(int) => Some(int.into_owned_value()), - Err(_) => None, - }, - Lit::Float(lit) => match FloatValue::for_litfloat(lit) { - Ok(float) => Some(float.into_owned_value()), - Err(_) => None, - }, - Lit::Bool(lit) => Some(BooleanValue::for_litbool(lit).into_owned_value()), - Lit::Str(lit) => Some(StringValue::for_litstr(lit).into_owned_value()), - Lit::Char(lit) => Some(CharValue::for_litchar(lit).into_owned_value()), - _ => None, - }; - match matched { - Some(value) => value, - None => Self::UnsupportedLiteral(UnsupportedLiteral { lit }).into_owned(), - } - } - - pub(crate) fn try_transparent_clone( - &self, - error_span_range: SpanRange, - ) -> ExecutionResult { - 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.", - self.articled_value_type() - )); - } - Ok(self.clone()) - } - - pub(crate) fn is_none(&self) -> bool { - matches!(self, Value::None) - } - - /// Recursively compares two values for equality using `ValuesEqual` semantics. - pub(crate) fn values_equal(lhs: &Value, rhs: &Value) -> bool { - lhs.lenient_eq(rhs) - } -} - -impl ValuesEqual for Value { - fn test_equality(&self, other: &Self, ctx: &mut C) -> C::Result { - // Each variant has two lines: same-type comparison, then type-mismatch fallback. - // This ensures adding a new variant only requires adding two lines at the bottom. - match (self, other) { - (Value::None, Value::None) => ctx.values_equal(), - (Value::None, _) => ctx.kind_mismatch(self, other), - (Value::Boolean(l), Value::Boolean(r)) => l.test_equality(r, ctx), - (Value::Boolean(_), _) => ctx.kind_mismatch(self, other), - (Value::Char(l), Value::Char(r)) => l.test_equality(r, ctx), - (Value::Char(_), _) => ctx.kind_mismatch(self, other), - (Value::String(l), Value::String(r)) => l.test_equality(r, ctx), - (Value::String(_), _) => ctx.kind_mismatch(self, other), - (Value::Integer(l), Value::Integer(r)) => l.test_equality(r, ctx), - (Value::Integer(_), _) => ctx.kind_mismatch(self, other), - (Value::Float(l), Value::Float(r)) => l.test_equality(r, ctx), - (Value::Float(_), _) => ctx.kind_mismatch(self, other), - (Value::Array(l), Value::Array(r)) => l.test_equality(r, ctx), - (Value::Array(_), _) => ctx.kind_mismatch(self, other), - (Value::Object(l), Value::Object(r)) => l.test_equality(r, ctx), - (Value::Object(_), _) => ctx.kind_mismatch(self, other), - (Value::Stream(l), Value::Stream(r)) => l.test_equality(r, ctx), - (Value::Stream(_), _) => ctx.kind_mismatch(self, other), - (Value::Range(l), Value::Range(r)) => l.test_equality(r, ctx), - (Value::Range(_), _) => ctx.kind_mismatch(self, other), - (Value::UnsupportedLiteral(l), Value::UnsupportedLiteral(r)) => l.test_equality(r, ctx), - (Value::UnsupportedLiteral(_), _) => ctx.kind_mismatch(self, other), - (Value::Parser(l), Value::Parser(r)) => l.test_equality(r, ctx), - (Value::Parser(_), _) => ctx.kind_mismatch(self, other), - (Value::Iterator(l), Value::Iterator(r)) => l.test_equality(r, ctx), - (Value::Iterator(_), _) => ctx.kind_mismatch(self, other), - } - } -} - -impl Value { - pub(crate) fn into_indexed( - self, - access: IndexAccess, - index: Spanned<&Self>, - ) -> ExecutionResult { - match self { - Value::Array(array) => array.into_indexed(index), - Value::Object(object) => object.into_indexed(index), - other => access.type_err(format!("Cannot index into a {}", other.value_type())), - } - } - - pub(crate) fn index_mut( - &mut self, - access: IndexAccess, - index: Spanned<&Self>, - auto_create: bool, - ) -> ExecutionResult<&mut Self> { - match self { - Value::Array(array) => array.index_mut(index), - Value::Object(object) => object.index_mut(index, auto_create), - other => access.type_err(format!("Cannot index into a {}", other.value_type())), - } - } - - pub(crate) fn index_ref( - &self, - access: IndexAccess, - index: Spanned<&Self>, - ) -> ExecutionResult<&Self> { - match self { - Value::Array(array) => array.index_ref(index), - Value::Object(object) => object.index_ref(index), - other => access.type_err(format!("Cannot index into a {}", other.value_type())), - } - } - - pub(crate) fn into_property(self, access: &PropertyAccess) -> ExecutionResult { - match self { - Value::Object(object) => object.into_property(access), - other => access.type_err(format!( - "Cannot access properties on a {}", - other.value_type() - )), - } - } - - pub(crate) fn property_mut( - &mut self, - access: &PropertyAccess, - auto_create: bool, - ) -> ExecutionResult<&mut Self> { - match self { - Value::Object(object) => object.property_mut(access, auto_create), - other => access.type_err(format!( - "Cannot access properties on a {}", - other.value_type() - )), - } - } - - pub(crate) fn property_ref(&self, access: &PropertyAccess) -> ExecutionResult<&Self> { - match self { - Value::Object(object) => object.property_ref(access), - other => access.type_err(format!( - "Cannot access properties on a {}", - other.value_type() - )), - } - } - - pub(crate) fn into_stream( - self, - grouping: Grouping, - error_span_range: SpanRange, - ) -> ExecutionResult { - match (self, grouping) { - (Self::Stream(value), Grouping::Flattened) => Ok(value.value), - (Self::Stream(value), Grouping::Grouped) => { - let mut output = OutputStream::new(); - let span = ToStreamContext::new(&mut output, error_span_range).new_token_span(); - output.push_new_group(value.value, Delimiter::None, span); - Ok(output) - } - (other, grouping) => other.output_to_new_stream(grouping, error_span_range), - } - } - - pub(crate) fn output_to_new_stream( - &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) - } - - pub(crate) fn output_to( - &self, - grouping: Grouping, - output: &mut ToStreamContext, - ) -> ExecutionResult<()> { - match grouping { - Grouping::Grouped => { - // Grouping can be important for different values, to ensure they're read atomically - // when the output stream is viewed as an array/iterable, e.g. in a for loop. - // * Grouping means -1 is interpreted atomically, rather than as a punct then a number - // * Grouping means that a stream is interpreted atomically - output.push_grouped(|inner| self.output_flattened_to(inner), Delimiter::None)?; - } - Grouping::Flattened => { - self.output_flattened_to(output)?; - } - } - Ok(()) - } - - fn output_flattened_to(&self, output: &mut ToStreamContext) -> ExecutionResult<()> { - match self { - Self::None => {} - Self::Integer(value) => { - let literal = value.to_literal(output.new_token_span()); - output.push_literal(literal); - } - Self::Float(value) => { - value.output_to(output); - } - Self::Boolean(value) => { - let ident = value.to_ident(output.new_token_span()); - output.push_ident(ident); - } - Self::String(value) => { - let literal = value.to_literal(output.new_token_span()); - output.push_literal(literal); - } - Self::Char(value) => { - let literal = value.to_literal(output.new_token_span()); - output.push_literal(literal); - } - Self::UnsupportedLiteral(literal) => { - output.extend_raw_tokens(literal.lit.to_token_stream()) - } - Self::Object(_) => { - return output.type_err("Objects cannot be output to a stream"); - } - Self::Array(array) => array.output_items_to(output, Grouping::Flattened)?, - Self::Stream(value) => value.value.append_cloned_into(output.output_stream), - Self::Iterator(iterator) => iterator - .clone() - .output_items_to(output, Grouping::Flattened)?, - Self::Range(range) => { - let iterator = IteratorValue::new_for_range(range.clone())?; - iterator.output_items_to(output, Grouping::Flattened)? - } - Self::Parser(_) => { - return output.type_err("Parsers cannot be output to a stream"); - } - }; - Ok(()) - } - - pub(crate) fn concat_recursive(&self, behaviour: &ConcatBehaviour) -> ExecutionResult { - let mut output = String::new(); - self.concat_recursive_into(&mut output, behaviour)?; - Ok(output) - } - - pub(crate) fn concat_recursive_into( - &self, - output: &mut String, - behaviour: &ConcatBehaviour, - ) -> ExecutionResult<()> { - match self { - Value::None => { - if behaviour.show_none_values { - output.push_str("None"); - } - } - Value::Stream(stream) => { - stream.concat_recursive_into(output, behaviour); - } - Value::Array(array) => { - array.concat_recursive_into(output, behaviour)?; - } - Value::Object(object) => { - object.concat_recursive_into(output, behaviour)?; - } - Value::Iterator(iterator) => { - iterator.concat_recursive_into(output, behaviour)?; - } - Value::Range(range) => { - range.concat_recursive_into(output, behaviour)?; - } - Value::Parser(_) => { - return behaviour - .error_span_range - .type_err("Parsers cannot be output to a string"); - } - Value::Integer(_) - | Value::Float(_) - | Value::Char(_) - | Value::Boolean(_) - | Value::UnsupportedLiteral(_) - | Value::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) - .expect("Non-composite values should all be able to be outputted to a stream"); - stream.concat_recursive_into(output, behaviour); - } - } - Ok(()) - } -} - -pub(crate) struct ToStreamContext<'a> { - output_stream: &'a mut OutputStream, - error_span_range: SpanRange, -} - -impl<'a> ToStreamContext<'a> { - pub(crate) fn new(output_stream: &'a mut OutputStream, error_span_range: SpanRange) -> Self { - Self { - output_stream, - error_span_range, - } - } - - pub(crate) fn push_grouped( - &mut self, - f: impl FnOnce(&mut ToStreamContext) -> ExecutionResult<()>, - delimiter: Delimiter, - ) -> ExecutionResult<()> { - let span = self.new_token_span(); - self.output_stream.push_grouped( - |inner| f(&mut ToStreamContext::new(inner, self.error_span_range)), - delimiter, - span, - ) - } - - 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; - - fn deref(&self) -> &Self::Target { - self.output_stream - } -} - -impl DerefMut for ToStreamContext<'_> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.output_stream - } -} - -impl HasSpanRange for ToStreamContext<'_> { - fn span_range(&self) -> SpanRange { - self.error_span_range - } -} - -impl Spanned { - pub(crate) fn into_stream(self) -> ExecutionResult { - let Spanned(value, span_range) = self; - value.0.into_stream(Grouping::Flattened, span_range) - } - - pub(crate) fn resolve_any_iterator( - self, - resolution_target: &str, - ) -> ExecutionResult> { - IterableValue::resolve_owned(self, resolution_target)?.try_map(|v| v.into_iterator()) - } -} - -impl IntoValue for Value { - fn into_value(self) -> Value { - self - } -} - -#[derive(Copy, Clone)] -pub(crate) enum Grouping { - Grouped, - Flattened, -} - -impl HasValueKind for Value { - type SpecificKind = ValueKind; - - fn kind(&self) -> ValueKind { - match self { - Value::None => ValueKind::None, - Value::Integer(integer) => ValueKind::Integer(integer.kind()), - Value::Float(float) => ValueKind::Float(float.kind()), - Value::Boolean(_) => ValueKind::Boolean, - Value::String(_) => ValueKind::String, - Value::Char(_) => ValueKind::Char, - Value::Array(_) => ValueKind::Array, - Value::Object(_) => ValueKind::Object, - Value::Stream(_) => ValueKind::Stream, - Value::Range(range) => ValueKind::Range(range.kind()), - Value::Iterator(_) => ValueKind::Iterator, - Value::Parser(_) => ValueKind::Parser, - Value::UnsupportedLiteral(_) => ValueKind::UnsupportedLiteral, - } - } -} diff --git a/src/interpretation/bindings.rs b/src/interpretation/bindings.rs index c50f0236..a5378fa9 100644 --- a/src/interpretation/bindings.rs +++ b/src/interpretation/bindings.rs @@ -6,12 +6,12 @@ use std::rc::Rc; pub(super) enum VariableState { Uninitialized, - Value(Rc>), + Value(Rc>), Finished, } impl VariableState { - pub(crate) fn define(&mut self, value: Value) { + pub(crate) fn define(&mut self, value: AnyValue) { match self { content @ VariableState::Uninitialized => { *content = VariableState::Value(Rc::new(RefCell::new(value))); @@ -50,7 +50,7 @@ impl VariableState { } return Ok(Spanned( LateBoundValue::Owned(LateBoundOwnedValue { - owned: Owned(ref_cell.into_inner()), + owned: ref_cell.into_inner(), is_from_last_use: true, }), span_range, @@ -119,7 +119,7 @@ impl VariableState { #[derive(Clone)] pub(crate) struct VariableBinding { - data: Rc>, + data: Rc>, variable_span: Span, } @@ -127,18 +127,18 @@ 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) -> ExecutionResult { let span_range = self.variable_span.span_range(); let shared = self.into_shared()?; let value = shared.as_ref().try_transparent_clone(span_range)?; - Ok(Owned(value)) + Ok(value) } - fn into_mut(self) -> ExecutionResult { + fn into_mut(self) -> ExecutionResult { MutableValue::new_from_variable(self).map_err(ExecutionInterrupt::ownership_error) } - fn into_shared(self) -> ExecutionResult { + fn into_shared(self) -> ExecutionResult { SharedValue::new_from_variable(self).map_err(ExecutionInterrupt::ownership_error) } @@ -159,7 +159,7 @@ impl VariableBinding { } pub(crate) struct LateBoundOwnedValue { - pub(crate) owned: OwnedValue, + pub(crate) owned: AnyValueOwned, pub(crate) is_from_last_use: bool, } @@ -194,7 +194,7 @@ pub(crate) enum LateBoundValue { /// A copy-on-write value that can be converted to an owned value CopyOnWrite(CopyOnWriteValue), /// A mutable reference - Mutable(MutableValue), + Mutable(AnyValueMutable), /// A shared reference where mutable access failed for a specific reason Shared(LateBoundSharedValue), } @@ -208,9 +208,9 @@ impl Spanned { impl LateBoundValue { pub(crate) fn map_any( self, - map_shared: impl FnOnce(SharedValue) -> ExecutionResult, - map_mutable: impl FnOnce(MutableValue) -> ExecutionResult, - map_owned: impl FnOnce(OwnedValue) -> ExecutionResult, + map_shared: impl FnOnce(AnyValueShared) -> ExecutionResult, + map_mutable: impl FnOnce(AnyValueMutable) -> ExecutionResult, + map_owned: impl FnOnce(AnyValueOwned) -> ExecutionResult, ) -> ExecutionResult { Ok(match self { LateBoundValue::Owned(owned) => LateBoundValue::Owned(LateBoundOwnedValue { @@ -231,7 +231,7 @@ impl LateBoundValue { }) } - pub(crate) fn as_value(&self) -> &Value { + pub(crate) fn as_value(&self) -> &AnyValue { match self { LateBoundValue::Owned(owned) => &owned.owned, LateBoundValue::CopyOnWrite(cow) => cow.as_ref(), @@ -242,88 +242,15 @@ impl LateBoundValue { } impl Deref for LateBoundValue { - type Target = Value; + type Target = AnyValue; fn deref(&self) -> &Self::Target { self.as_value() } } -pub(crate) type OwnedValue = Owned; - -/// A semantic wrapper for floating owned values. -/// -/// Can be destructured as: `Owned(value): Owned` -/// -/// If you need span information, wrap with `Spanned>`. For example, for `x.y[4]`, this would capture both: -/// * The output owned value -/// * The lexical span of the tokens `x.y[4]` -pub(crate) struct Owned(pub(crate) T); - -#[allow(unused)] -impl Owned { - pub(crate) fn new(value: T) -> Self { - Owned(value) - } - - pub(crate) fn into_inner(self) -> T { - self.0 - } - - pub(crate) fn map(self, value_map: impl FnOnce(T) -> V) -> Owned { - Owned(value_map(self.0)) - } - - pub(crate) fn try_map( - self, - value_map: impl FnOnce(T) -> Result, - ) -> Result, E> { - Ok(Owned(value_map(self.0)?)) - } -} - -impl Spanned { - pub(crate) fn into_statement_result(self) -> ExecutionResult<()> { - let Spanned(value, span_range) = self; - match value.0 { - Value::None => Ok(()), - _ => span_range.control_flow_err("A non-returning statement must not return a value. If you wish to explicitly discard the expression's result, use `let _ = ...;`. Alternatively, If you wish to output the value into the parent token stream, use `emit ...;`"), - } - } -} - -impl Owned { - pub(crate) fn into_value(self) -> Value { - self.0.into_value() - } - - pub(crate) fn into_owned_value(self) -> OwnedValue { - Owned(self.into_value()) - } -} - -impl From for Value { - fn from(value: OwnedValue) -> Self { - value.0 - } -} - -impl Deref for Owned { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Owned { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -pub(crate) type MutableValue = Mutable; -pub(crate) type AssigneeValue = Assignee; +pub(crate) type MutableValue = AnyValueMutable; +pub(crate) type AssigneeValue = AnyValueAssignee; /// A binding of a unique (mutable) reference to a value. /// See [`ArgumentOwnership::Assignee`] for more details. @@ -332,8 +259,21 @@ pub(crate) type AssigneeValue = Assignee; pub(crate) struct Assignee(pub Mutable); impl AssigneeValue { - pub(crate) fn set(&mut self, content: impl IntoValue) { - *self.0 .0 = content.into_value(); + pub(crate) fn set(&mut self, content: impl IntoAnyValue) { + *self.0 .0 = content.into_any_value(); + } +} + +impl IsValueContent for Assignee { + type Type = AnyType; + type Form = BeAssignee; +} + +impl IntoValueContent<'static> for Assignee { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + self.0 + .0 + .replace(|inner, emplacer| inner.as_mut_value().into_assignee(emplacer)) } } @@ -356,7 +296,7 @@ impl DerefMut for Assignee { /// Can be destructured as: `Mutable(cell): Mutable` /// /// If you need span information, wrap with `Spanned>`. -pub(crate) struct Mutable(pub(crate) MutSubRcRefCell); +pub(crate) struct Mutable(pub(crate) MutableSubRcRefCell); impl Mutable { pub(crate) fn into_shared(self) -> Shared { @@ -382,7 +322,7 @@ impl Mutable { pub(crate) static MUTABLE_ERROR_MESSAGE: &str = "The variable cannot be modified as it is already being modified"; -impl Spanned<&mut Mutable> { +impl Spanned<&mut AnyValueMutable> { /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -402,26 +342,38 @@ impl Spanned<&mut Mutable> { } } -impl Spanned> { - pub(crate) fn transparent_clone(&self) -> ExecutionResult { +impl Spanned { + pub(crate) fn transparent_clone(&self) -> ExecutionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; - Ok(Owned(value)) + Ok(value) } } -impl Mutable { - pub(crate) fn new_from_owned(value: OwnedValue) -> Self { +impl AnyValueMutable { + pub(crate) fn new_from_owned(value: AnyValue) -> Self { // Unwrap is safe because it's a new refcell - Mutable(MutSubRcRefCell::new(Rc::new(RefCell::new(value.0))).unwrap()) + Mutable(MutableSubRcRefCell::new(Rc::new(RefCell::new(value))).unwrap()) } fn new_from_variable(reference: VariableBinding) -> syn::Result { - Ok(Mutable(MutSubRcRefCell::new(reference.data).map_err( + Ok(Mutable(MutableSubRcRefCell::new(reference.data).map_err( |_| reference.variable_span.syn_error(MUTABLE_ERROR_MESSAGE), )?)) } } +impl IsValueContent for Mutable { + type Type = AnyType; + type Form = BeMutable; +} + +impl IntoValueContent<'static> for Mutable { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + self.0 + .replace(|inner, emplacer| inner.as_mut_value().into_mutable(emplacer)) + } +} + impl AsMut for Mutable { fn as_mut(&mut self) -> &mut T { &mut self.0 @@ -448,14 +400,14 @@ impl DerefMut for Mutable { } } -pub(crate) type SharedValue = Shared; +pub(crate) type SharedValue = AnyValueShared; /// A simple wrapper for a shared (immutable) reference to a value. /// /// Can be destructured as: `Shared(cell): Shared` /// /// If you need span information, wrap with `Spanned>`. -pub(crate) struct Shared(pub(crate) SharedSubRcRefCell); +pub(crate) struct Shared(pub(crate) SharedSubRcRefCell); #[allow(unused)] impl Shared { @@ -478,7 +430,7 @@ impl Shared { pub(crate) static SHARED_ERROR_MESSAGE: &str = "The variable cannot be read as it is already being modified"; -impl Spanned<&mut Shared> { +impl Spanned<&mut AnyValueShared> { /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -498,21 +450,21 @@ impl Spanned<&mut Shared> { } } -impl Spanned> { - pub(crate) fn transparent_clone(&self) -> ExecutionResult { +impl Spanned { + pub(crate) fn transparent_clone(&self) -> ExecutionResult { let value = self.0.as_ref().try_transparent_clone(self.1)?; - Ok(Owned(value)) + Ok(value) } } -impl Shared { - pub(crate) fn new_from_owned(value: OwnedValue) -> Self { +impl AnyValueShared { + pub(crate) fn new_from_owned(value: AnyValue) -> Self { // Unwrap is safe because it's a new refcell - Shared(SharedSubRcRefCell::new(Rc::new(RefCell::new(value.0))).unwrap()) + Shared(SharedSubRcRefCell::new(Rc::new(RefCell::new(value))).unwrap()) } - pub(crate) fn infallible_clone(&self) -> OwnedValue { - Owned(self.0.clone()) + pub(crate) fn infallible_clone(&self) -> AnyValue { + self.0.clone() } fn new_from_variable(reference: VariableBinding) -> syn::Result { @@ -522,6 +474,18 @@ impl Shared { } } +impl IsValueContent for Shared { + type Type = AnyType; + type Form = BeShared; +} + +impl IntoValueContent<'static> for Shared { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { + self.0 + .replace(|inner, emplacer| inner.as_ref_value().into_shared(emplacer)) + } +} + impl AsRef for Shared { fn as_ref(&self) -> &T { &self.0 @@ -538,10 +502,10 @@ impl Deref for Shared { /// Copy-on-write value that can be either owned or shared pub(crate) struct CopyOnWrite { - inner: CopyOnWriteInner, + pub(crate) inner: CopyOnWriteInner, } -enum CopyOnWriteInner { +pub(crate) enum CopyOnWriteInner { /// An owned value that can be used directly Owned(Owned), /// For use when the CopyOnWrite value effectively represents the owned value (post-clone). @@ -577,10 +541,10 @@ impl CopyOnWrite { map: impl FnOnce(T::Owned) -> Result, ) -> Result { match self.inner { - CopyOnWriteInner::Owned(Owned(value)) => match map(value) { + CopyOnWriteInner::Owned(value) => match map(value) { Ok(mapped) => Ok(mapped), Err(other) => Err(Self { - inner: CopyOnWriteInner::Owned(Owned::new(other)), + inner: CopyOnWriteInner::Owned(other), }), }, other => Err(Self { inner: other }), @@ -625,7 +589,7 @@ impl CopyOnWrite { } } -impl Spanned<&mut CopyOnWrite> { +impl Spanned<&mut CopyOnWrite> { /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -669,16 +633,16 @@ impl Deref for CopyOnWrite { fn deref(&self) -> &T { match self.inner { - CopyOnWriteInner::Owned(ref owned) => (**owned).borrow(), + CopyOnWriteInner::Owned(ref owned) => (*owned).borrow(), CopyOnWriteInner::SharedWithInfallibleCloning(ref shared) => shared.as_ref(), CopyOnWriteInner::SharedWithTransparentCloning(ref shared) => shared.as_ref(), } } } -impl CopyOnWrite { +impl CopyOnWrite { /// Converts to owned, cloning if necessary - pub(crate) fn into_owned_infallible(self) -> OwnedValue { + pub(crate) fn clone_to_owned_infallible(self) -> AnyValueOwned { match self.inner { CopyOnWriteInner::Owned(owned) => owned, CopyOnWriteInner::SharedWithInfallibleCloning(shared) => shared.infallible_clone(), @@ -687,13 +651,16 @@ impl CopyOnWrite { } /// Converts to owned, using transparent clone for shared values where cloning was not requested - pub(crate) fn into_owned_transparently(self, span: SpanRange) -> ExecutionResult { + pub(crate) fn clone_to_owned_transparently( + self, + span: SpanRange, + ) -> ExecutionResult { match self.inner { CopyOnWriteInner::Owned(owned) => Ok(owned), CopyOnWriteInner::SharedWithInfallibleCloning(shared) => Ok(shared.infallible_clone()), CopyOnWriteInner::SharedWithTransparentCloning(shared) => { let value = shared.as_ref().try_transparent_clone(span)?; - Ok(Owned(value)) + Ok(value) } } } @@ -708,4 +675,4 @@ impl CopyOnWrite { } } -pub(crate) type CopyOnWriteValue = CopyOnWrite; +pub(crate) type CopyOnWriteValue = CopyOnWrite; diff --git a/src/interpretation/interpreter.rs b/src/interpretation/interpreter.rs index 452a9d9a..8416046a 100644 --- a/src/interpretation/interpreter.rs +++ b/src/interpretation/interpreter.rs @@ -163,7 +163,7 @@ impl Interpreter { } } - pub(crate) fn define_variable(&mut self, definition_id: VariableDefinitionId, value: Value) { + pub(crate) fn define_variable(&mut self, definition_id: VariableDefinitionId, value: AnyValue) { let definition = self.scope_definitions.definitions.get(definition_id); let scope_data = self.scope_mut(definition.scope); scope_data.define_variable(definition_id, value) @@ -400,7 +400,7 @@ struct RuntimeScope { } impl RuntimeScope { - fn define_variable(&mut self, definition_id: VariableDefinitionId, value: Value) { + fn define_variable(&mut self, definition_id: VariableDefinitionId, value: AnyValue) { self.variables .get_mut(&definition_id) .expect("Variable data not found in scope") diff --git a/src/interpretation/output_stream.rs b/src/interpretation/output_stream.rs index 6cbb7a07..aedc470c 100644 --- a/src/interpretation/output_stream.rs +++ b/src/interpretation/output_stream.rs @@ -120,12 +120,12 @@ impl OutputStream { self.token_length == 0 } - pub(crate) fn coerce_into_value(self) -> Value { + pub(crate) fn coerce_into_value(self) -> AnyValue { let parse_result = self.clone().parse_as::(); match parse_result { - Ok(syn_lit) => Value::for_syn_lit(syn_lit).into_inner(), + Ok(syn_lit) => AnyValue::for_syn_lit(syn_lit), // Keep as stream otherwise - Err(_) => self.into_value(), + Err(_) => self.into_any_value(), } } @@ -234,13 +234,13 @@ impl OutputStream { } } - pub(crate) fn concat_recursive(&self, behaviour: &ConcatBehaviour) -> String { + pub(crate) fn concat_content(&self, behaviour: &ConcatBehaviour) -> String { let mut output = String::new(); - self.concat_recursive_into(&mut output, behaviour); + self.concat_content_into(&mut output, behaviour); output } - pub(crate) fn concat_recursive_into(&self, output: &mut String, behaviour: &ConcatBehaviour) { + pub(crate) fn concat_content_into(&self, output: &mut String, behaviour: &ConcatBehaviour) { fn concat_recursive_interpreted_stream( behaviour: &ConcatBehaviour, output: &mut String, diff --git a/src/interpretation/refs.rs b/src/interpretation/refs.rs index bc14d296..216638ef 100644 --- a/src/interpretation/refs.rs +++ b/src/interpretation/refs.rs @@ -1,12 +1,26 @@ +use std::mem::transmute; + use super::*; /// A flexible type which can either be a reference to a value of type `T`, -/// or an encapsulated reference from a [`Shared`]. +/// or an emplaced reference from a [`Shared`]. pub(crate) struct AnyRef<'a, T: ?Sized + 'static> { inner: AnyRefInner<'a, T>, } impl<'a, T: ?Sized + 'static> AnyRef<'a, T> { + #[allow(unused)] + pub(crate) fn map(self, f: impl for<'r> FnOnce(&'r T) -> &'r S) -> AnyRef<'a, S> { + match self.inner { + AnyRefInner::Direct(value) => AnyRef { + inner: AnyRefInner::Direct(f(value)), + }, + AnyRefInner::Encapsulated(shared) => AnyRef { + inner: AnyRefInner::Encapsulated(shared.map(|x| f(x))), + }, + } + } + #[allow(unused)] pub(crate) fn map_optional( self, @@ -17,10 +31,56 @@ impl<'a, T: ?Sized + 'static> AnyRef<'a, T> { inner: AnyRefInner::Direct(f(value)?), }, AnyRefInner::Encapsulated(shared) => AnyRef { - inner: AnyRefInner::Encapsulated(shared.try_map(|x| f(x).ok_or(())).ok()?), + inner: AnyRefInner::Encapsulated(shared.map_optional(f)?), }, }) } + + pub(crate) fn replace( + self, + f: impl for<'e> FnOnce(&'e T, &mut AnyRefEmplacer<'a, 'e, T>) -> O, + ) -> O { + let copied_ref = self.deref() as *const T; + let mut emplacer = AnyRefEmplacer { + inner: Some(self), + encapsulation_lifetime: std::marker::PhantomData, + }; + f( + // SAFETY: The underlying reference is valid for the lifetime of self + // So we can copy it fine + unsafe { &*copied_ref }, + &mut emplacer, + ) + } +} + +pub(crate) struct AnyRefEmplacer<'a, 'e: 'a, T: 'static + ?Sized> { + inner: Option>, + encapsulation_lifetime: std::marker::PhantomData<&'e ()>, +} + +impl<'a, 'e: 'a, T: 'static + ?Sized> AnyRefEmplacer<'a, 'e, T> { + pub(crate) fn emplace(&mut self, value: &'e V) -> AnyRef<'a, V> { + unsafe { + // SAFETY: The lifetime 'e is equal to the &'e content argument in replace + // So this guarantees that the returned reference is valid as long as the AnyRef exists + self.emplace_unchecked(value) + } + } + + // SAFETY: + // * The caller must ensure that the value's lifetime is derived from the original content + pub(crate) unsafe fn emplace_unchecked( + &mut self, + value: &V, + ) -> AnyRef<'a, V> { + self.inner + .take() + .expect("You can only emplace to create a new AnyRef value once") + .map(|_| + // SAFETY: As defined in the rustdoc above + unsafe { transmute::<&V, &'static V>(value) }) + } } impl<'a, T: ?Sized> From<&'a T> for AnyRef<'a, T> { @@ -67,7 +127,7 @@ impl ToSpannedRef<'static> for Shared { enum AnyRefInner<'a, T: 'static + ?Sized> { Direct(&'a T), - Encapsulated(SharedSubRcRefCell), + Encapsulated(SharedSubRcRefCell), } impl<'a, T: 'static + ?Sized> Deref for AnyRef<'a, T> { @@ -82,62 +142,141 @@ impl<'a, T: 'static + ?Sized> Deref for AnyRef<'a, T> { } /// A flexible type which can either be a mutable reference to a value of type `T`, -/// or an encapsulated reference from a [`Mutable`]. -pub(crate) struct AnyRefMut<'a, T: 'static + ?Sized> { - inner: AnyRefMutInner<'a, T>, +/// or an emplaced reference from a [`Mutable`]. +pub(crate) struct AnyMut<'a, T: 'static + ?Sized> { + inner: AnyMutInner<'a, T>, } -impl<'a, T: ?Sized> From<&'a mut T> for AnyRefMut<'a, T> { +impl<'a, T: ?Sized> From<&'a mut T> for AnyMut<'a, T> { fn from(value: &'a mut T) -> Self { Self { - inner: AnyRefMutInner::Direct(value), + inner: AnyMutInner::Direct(value), } } } +impl<'a, T: ?Sized + 'static> AnyMut<'a, T> { + #[allow(unused)] + pub(crate) fn map( + self, + f: impl for<'r> FnOnce(&'r mut T) -> &'r mut S, + ) -> AnyMut<'a, S> { + match self.inner { + AnyMutInner::Direct(value) => AnyMut { + inner: AnyMutInner::Direct(f(value)), + }, + AnyMutInner::Encapsulated(mutable) => AnyMut { + inner: AnyMutInner::Encapsulated(mutable.map(|x| f(x))), + }, + } + } + + #[allow(unused)] + pub(crate) fn map_optional( + self, + f: impl for<'r> FnOnce(&'r mut T) -> Option<&'r mut S>, + ) -> Option> { + Some(match self.inner { + AnyMutInner::Direct(value) => AnyMut { + inner: AnyMutInner::Direct(f(value)?), + }, + AnyMutInner::Encapsulated(mutable) => AnyMut { + inner: AnyMutInner::Encapsulated(mutable.map_optional(f)?), + }, + }) + } + + pub(crate) fn replace( + mut self, + f: impl for<'e> FnOnce(&'e mut T, &mut AnyMutEmplacer<'a, 'e, T>) -> O, + ) -> O { + let copied_mut = self.deref_mut() as *mut T; + let mut emplacer = AnyMutEmplacer { + inner: Some(self), + encapsulation_lifetime: std::marker::PhantomData, + }; + f( + // SAFETY: We are cloning a mutable reference here, but it is safe because: + // - What it's pointing at still lives, inside emplacer.inner + // - No other "mutable reference" is created except at encapsulation time + unsafe { &mut *copied_mut }, + &mut emplacer, + ) + } +} + +pub(crate) struct AnyMutEmplacer<'a, 'e: 'a, T: 'static + ?Sized> { + inner: Option>, + encapsulation_lifetime: std::marker::PhantomData<&'e ()>, +} + +impl<'a, 'e: 'a, T: 'static + ?Sized> AnyMutEmplacer<'a, 'e, T> { + pub(crate) fn emplace(&mut self, value: &'e mut V) -> AnyMut<'a, V> { + unsafe { + // SAFETY: The lifetime 'e is equal to the &'e content argument in replace + // So this guarantees that the returned reference is valid as long as the AnyMut exists + self.emplace_unchecked(value) + } + } + + // SAFETY: + // * The caller must ensure that the value's lifetime is derived from the original content + pub(crate) unsafe fn emplace_unchecked( + &mut self, + value: &mut V, + ) -> AnyMut<'a, V> { + self.inner + .take() + .expect("You can only emplace to create a new AnyMut value once") + .map(|_| + // SAFETY: As defined in the rustdoc above + unsafe { transmute::<&mut V, &'static mut V>(value) }) + } +} + #[allow(unused)] -pub(crate) trait IntoRefMut<'a> { +pub(crate) trait IntoAnyMut<'a> { type Target: ?Sized; - fn into_ref_mut(self) -> AnyRefMut<'a, Self::Target>; + fn into_any_mut(self) -> AnyMut<'a, Self::Target>; } -impl<'a, T: ?Sized + 'static> IntoRefMut<'a> for &'a mut T { +impl<'a, T: ?Sized + 'static> IntoAnyMut<'a> for &'a mut T { type Target = T; - fn into_ref_mut(self) -> AnyRefMut<'a, Self::Target> { + fn into_any_mut(self) -> AnyMut<'a, Self::Target> { self.into() } } -impl<'a, T: ?Sized> From> for AnyRefMut<'a, T> { +impl<'a, T: ?Sized> From> for AnyMut<'a, T> { fn from(value: Mutable) -> Self { Self { - inner: AnyRefMutInner::Encapsulated(value.0), + inner: AnyMutInner::Encapsulated(value.0), } } } -enum AnyRefMutInner<'a, T: 'static + ?Sized> { +enum AnyMutInner<'a, T: 'static + ?Sized> { Direct(&'a mut T), - Encapsulated(MutSubRcRefCell), + Encapsulated(MutableSubRcRefCell), } -impl<'a, T: 'static + ?Sized> Deref for AnyRefMut<'a, T> { +impl<'a, T: 'static + ?Sized> Deref for AnyMut<'a, T> { type Target = T; fn deref(&self) -> &T { match &self.inner { - AnyRefMutInner::Direct(value) => value, - AnyRefMutInner::Encapsulated(shared) => shared, + AnyMutInner::Direct(value) => value, + AnyMutInner::Encapsulated(shared) => shared, } } } -impl<'a, T: 'static + ?Sized> DerefMut for AnyRefMut<'a, T> { +impl<'a, T: 'static + ?Sized> DerefMut for AnyMut<'a, T> { fn deref_mut(&mut self) -> &mut T { match &mut self.inner { - AnyRefMutInner::Direct(value) => value, - AnyRefMutInner::Encapsulated(shared) => &mut *shared, + AnyMutInner::Direct(value) => value, + AnyMutInner::Encapsulated(shared) => &mut *shared, } } } diff --git a/src/interpretation/variable.rs b/src/interpretation/variable.rs index 55e7f5fa..72ee8431 100644 --- a/src/interpretation/variable.rs +++ b/src/interpretation/variable.rs @@ -65,8 +65,8 @@ impl ParseSource for VariableDefinition { } impl VariableDefinition { - pub(crate) fn define(&self, interpreter: &mut Interpreter, value_source: impl IntoValue) { - interpreter.define_variable(self.id, value_source.into_value()); + pub(crate) fn define(&self, interpreter: &mut Interpreter, value_source: impl IntoAnyValue) { + interpreter.define_variable(self.id, value_source.into_any_value()); } } @@ -125,7 +125,7 @@ impl VariableReference { grouping: Grouping, ) -> ExecutionResult<()> { let value = self.resolve_shared(interpreter)?; - value.output_to( + value.as_ref_value().output_to( grouping, &mut ToStreamContext::new(interpreter.output(self)?, self.span_range()), ) @@ -185,7 +185,7 @@ impl HandleDestructure for VariablePattern { fn handle_destructure( &self, interpreter: &mut Interpreter, - value: Value, + value: AnyValue, ) -> ExecutionResult<()> { self.definition.define(interpreter, value); Ok(()) diff --git a/src/misc/errors.rs b/src/misc/errors.rs index e44afd76..6dd464c9 100644 --- a/src/misc/errors.rs +++ b/src/misc/errors.rs @@ -281,7 +281,7 @@ impl std::fmt::Debug for ControlFlowInterrupt { impl ControlFlowInterrupt { pub(crate) fn new_break( target_catch_location: CatchLocationId, - value: Option, + value: Option, ) -> Self { ControlFlowInterrupt::Break(BreakInterrupt { target_catch_location, @@ -316,18 +316,18 @@ impl ControlFlowInterrupt { pub(crate) struct BreakInterrupt { target_catch_location: CatchLocationId, - value: Option, + value: Option, } impl BreakInterrupt { - pub(crate) fn into_value( + pub(crate) fn into_requested_value( self, span_range: SpanRange, ownership: RequestedOwnership, ) -> ExecutionResult { let value = match self.value { Some(value) => value, - None => ().into_owned_value(), + None => ().into_any_value(), }; ownership .map_from_owned(Spanned(value, span_range)) diff --git a/src/misc/field_inputs.rs b/src/misc/field_inputs.rs index aaf34ed3..9d19385a 100644 --- a/src/misc/field_inputs.rs +++ b/src/misc/field_inputs.rs @@ -63,13 +63,24 @@ macro_rules! define_typed_object { )* } + impl IsArgument for $model { + type ValueType = ObjectType; + const OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; + fn from_argument(value: Spanned) -> ExecutionResult { + Self::resolve_value( + value.expect_owned(), + "This argument", + ) + } + } + impl ResolvableArgumentTarget for $model { - type ValueType = ObjectTypeData; + type ValueType = ObjectType; } - impl ResolvableOwned for $model { - fn resolve_from_value(value: Value, context: ResolutionContext) -> ExecutionResult { - Self::from_object_value(ObjectValue::resolve_spanned_owned_from_value(value, context)?) + impl ResolvableOwned for $model { + fn resolve_from_value(value: AnyValue, context: ResolutionContext) -> ExecutionResult { + Self::from_object_value(ObjectValue::resolve_spanned_from_value(value, context)?) } } @@ -94,8 +105,7 @@ macro_rules! define_typed_object { } impl $model { - fn from_object_value(Spanned(object, span_range): Spanned>) -> ExecutionResult { - let mut object = object.into_inner(); + fn from_object_value(Spanned(mut object, span_range): Spanned) -> ExecutionResult { (&object).spanned(span_range).validate(&Self::validation())?; Ok($model { $( @@ -109,14 +119,14 @@ macro_rules! define_typed_object { { // Need to return the $optional_field_type match optional { - Some(value) => ResolveAs::<$optional_field_type>::resolve_as(Spanned(value.into_owned(), span_range), stringify!($optional_field))?, + Some(value) => ResolveAs::<$optional_field_type>::resolve_as(Spanned(value, span_range), stringify!($optional_field))?, None => $($optional_field_default)?, } } { // Need to return Option<$optional_field_type> match optional { - Some(value) => Some(ResolveAs::<$optional_field_type>::resolve_as(Spanned(value.into_owned(), span_range), stringify!($optional_field))?), + Some(value) => Some(ResolveAs::<$optional_field_type>::resolve_as(Spanned(value, span_range), stringify!($optional_field))?), None => None, } } diff --git a/src/misc/iterators.rs b/src/misc/iterators.rs index 1722d163..2925a367 100644 --- a/src/misc/iterators.rs +++ b/src/misc/iterators.rs @@ -56,10 +56,8 @@ impl ZipIterators { k, v.key_span, v.value - .into_owned() .spanned(span_range) - .resolve_any_iterator("Each zip input")? - .into_inner(), + .resolve_any_iterator("Each zip input")?, )) }) .collect::, _>>()?; @@ -75,12 +73,7 @@ impl ZipIterators { ) -> ExecutionResult { let vec = iterator .take(101) - .map(|x| { - x.into_owned() - .spanned(span_range) - .resolve_any_iterator("Each zip input") - .map(|x| x.into_inner()) - }) + .map(|x| x.spanned(span_range).resolve_any_iterator("Each zip input")) .collect::, _>>()?; if vec.len() == 101 { return span_range.value_err("A maximum of 100 iterators are allowed"); @@ -160,7 +153,7 @@ impl ZipIterators { count: usize, interpreter: &mut Interpreter, error_span_range: SpanRange, - output: &mut Vec, + output: &mut Vec, ) -> ExecutionResult<()> { let mut counter = interpreter.start_iteration_counter(&error_span_range); @@ -172,7 +165,7 @@ impl ZipIterators { for iter in iterators.iter_mut() { inner.push(iter.next().unwrap()); } - output.push(inner.into_value()); + output.push(inner.into_any_value()); } } ZipIterators::Object(iterators, _) => { @@ -188,7 +181,7 @@ impl ZipIterators { }, ); } - output.push(inner.into_value()); + output.push(inner.into_any_value()); } } } @@ -200,13 +193,13 @@ impl ZipIterators { define_optional_object! { pub(crate) struct IntersperseSettings { add_trailing: bool = false => ("false", "Whether to add the separator after the last item (default: false)"), - final_separator: Value => ("%[or]", "Define a different final separator (default: same as normal separator)"), + final_separator: AnyValue => ("%[or]", "Define a different final separator (default: same as normal separator)"), } } pub(crate) fn run_intersperse( - items: IterableValue, - separator: Value, + items: Box, + separator: AnyValue, settings: IntersperseSettings, ) -> ExecutionResult { let mut output = Vec::new(); @@ -248,8 +241,8 @@ pub(crate) fn run_intersperse( } struct SeparatorAppender { - separator: Value, - final_separator: Option, + separator: AnyValue, + final_separator: Option, add_trailing: bool, } @@ -257,7 +250,7 @@ impl SeparatorAppender { fn add_separator( &mut self, remaining: RemainingItemCount, - output: &mut Vec, + output: &mut Vec, ) -> ExecutionResult<()> { match self.separator(remaining) { TrailingSeparator::Normal => output.push(self.separator.clone()), @@ -325,7 +318,7 @@ pub(crate) fn handle_split( while !input.is_empty() { current_item.push_raw_token_tree(input.parse()?); let complete_item = core::mem::replace(&mut current_item, OutputStream::new()); - output.push(complete_item.into_value()); + output.push(complete_item.into_any_value()); } return Ok(ArrayValue::new(output)); } @@ -345,12 +338,12 @@ pub(crate) fn handle_split( input.advance_to(&separator_fork); if !current_item.is_empty() || !drop_empty_next { let complete_item = core::mem::replace(&mut current_item, OutputStream::new()); - output.push(complete_item.into_value()); + output.push(complete_item.into_any_value()); } drop_empty_next = settings.drop_empty_middle; } if !current_item.is_empty() || !settings.drop_empty_end { - output.push(current_item.into_value()); + output.push(current_item.into_any_value()); } Ok(ArrayValue::new(output)) }) diff --git a/src/misc/mod.rs b/src/misc/mod.rs index 4e685bd9..36aa4457 100644 --- a/src/misc/mod.rs +++ b/src/misc/mod.rs @@ -38,8 +38,13 @@ pub(crate) fn print_if_slow( // Equivalent to `!` but stable in our MSRV pub(crate) enum Never {} -impl IntoValue for Never { - fn into_value(self) -> Value { +impl IsValueContent for Never { + type Type = NoneType; + type Form = BeOwned; +} + +impl IntoValueContent<'static> for Never { + fn into_content(self) -> Content<'static, Self::Type, Self::Form> { match self {} } } diff --git a/src/misc/mut_rc_ref_cell.rs b/src/misc/mut_rc_ref_cell.rs index ff354705..777e55d0 100644 --- a/src/misc/mut_rc_ref_cell.rs +++ b/src/misc/mut_rc_ref_cell.rs @@ -2,8 +2,8 @@ use crate::internal_prelude::*; use std::cell::{BorrowError, BorrowMutError}; /// A mutable reference to a sub-value `U` inside a [`Rc>`]. -/// Only one [`MutSubRcRefCell`] can exist at a time for a given [`Rc>`]. -pub(crate) struct MutSubRcRefCell { +/// Only one [`MutableSubRcRefCell`] can exist at a time for a given [`Rc>`]. +pub(crate) struct MutableSubRcRefCell { /// This is actually a reference to the contents of the RefCell /// but we store it using `unsafe` as `'static`, and use unsafe blocks /// to ensure it's dropped first. @@ -13,7 +13,7 @@ pub(crate) struct MutSubRcRefCell { pointed_at: Rc>, } -impl MutSubRcRefCell { +impl MutableSubRcRefCell { pub(crate) fn new(pointed_at: Rc>) -> Result { let ref_mut = pointed_at.try_borrow_mut()?; Ok(Self { @@ -31,14 +31,14 @@ impl MutSubRcRefCell { } } -impl MutSubRcRefCell { +impl MutableSubRcRefCell { pub(crate) fn into_shared(self) -> SharedSubRcRefCell { let ptr = self.ref_mut.deref() as *const U; drop(self.ref_mut); // SAFETY: // - The pointer was previously a reference, so it is safe to deference it here // (the pointer is pointing into the Rc> which hasn't moved) - // - All our invariants for SharedSubRcRefCell / MutSubRcRefCell are maintained + // - All our invariants for SharedSubRcRefCell / MutableSubRcRefCell are maintained unsafe { // The unwrap cannot panic because we just held a mutable borrow, we're not in Sync land, so no-one else can have a borrow. SharedSubRcRefCell::new(self.pointed_at) @@ -72,17 +72,30 @@ impl MutSubRcRefCell { } } - pub(crate) fn map(self, f: impl FnOnce(&mut U) -> &mut V) -> MutSubRcRefCell { - MutSubRcRefCell { + pub(crate) fn map( + self, + f: impl FnOnce(&mut U) -> &mut V, + ) -> MutableSubRcRefCell { + MutableSubRcRefCell { ref_mut: RefMut::map(self.ref_mut, f), pointed_at: self.pointed_at, } } + pub(crate) fn map_optional( + self, + f: impl FnOnce(&mut U) -> Option<&mut V>, + ) -> Option> { + Some(MutableSubRcRefCell { + ref_mut: RefMut::filter_map(self.ref_mut, f).ok()?, + pointed_at: self.pointed_at, + }) + } + pub(crate) fn try_map( self, f: impl FnOnce(&mut U) -> Result<&mut V, E>, - ) -> Result, E> { + ) -> Result, E> { let mut error = None; let outcome = RefMut::filter_map(self.ref_mut, |inner| match f(inner) { Ok(value) => Some(value), @@ -92,22 +105,75 @@ impl MutSubRcRefCell { } }); match outcome { - Ok(ref_mut) => Ok(MutSubRcRefCell { + Ok(ref_mut) => Ok(MutableSubRcRefCell { ref_mut, pointed_at: self.pointed_at, }), Err(_) => Err(error.unwrap()), } } + + pub(crate) fn replace( + mut self, + f: impl for<'a> FnOnce(&'a mut U, &mut MutableSubEmplacer<'a, T, U>) -> O, + ) -> O { + let ref_mut = self.ref_mut.deref_mut() as *mut U; + let mut emplacer = MutableSubEmplacer { + inner: Some(self), + encapsulation_lifetime: std::marker::PhantomData, + }; + f( + // SAFETY: We are cloning a mutable reference here, but it is safe because: + // - What it's pointing at still lives, as RefMut still lives inside emplacer.inner + // - No other "mutable reference" is created from the RefMut except at encapsulation time + unsafe { &mut *ref_mut }, + &mut emplacer, + ) + } } -impl DerefMut for MutSubRcRefCell { +#[allow(unused)] +pub(crate) type MutableEmplacer<'e, U> = MutableSubEmplacer<'e, AnyValue, U>; + +pub(crate) struct MutableSubEmplacer<'e, T: 'static + ?Sized, U: 'static + ?Sized> { + inner: Option>, + encapsulation_lifetime: std::marker::PhantomData<&'e ()>, +} + +impl<'e, T: 'static + ?Sized, U: 'static + ?Sized> MutableSubEmplacer<'e, T, U> { + pub(crate) fn emplace( + &mut self, + value: &'e mut V, + ) -> MutableSubRcRefCell { + unsafe { + // SAFETY: The lifetime 'e is equal to the &'e content argument in replace + // So this guarantees that the returned reference is valid as long as the MutableSubRcRefCell exists + self.emplace_unchecked(value) + } + } + + // SAFETY: + // * The caller must ensure that the value's lifetime is derived from the original content + pub(crate) unsafe fn emplace_unchecked( + &mut self, + value: &mut V, + ) -> MutableSubRcRefCell { + self.inner + .take() + .expect("You can only emplace to create a new Mutable value once") + .map(|_| + // SAFETY: As defined in the rustdoc above + unsafe { less_buggy_transmute::<&mut V, &'static mut V>(value) }) + } +} + +impl DerefMut for MutableSubRcRefCell { fn deref_mut(&mut self) -> &mut U { &mut self.ref_mut } } -impl Deref for MutSubRcRefCell { +impl Deref for MutableSubRcRefCell { type Target = U; fn deref(&self) -> &U { &self.ref_mut @@ -116,7 +182,7 @@ impl Deref for MutSubRcRefCell { /// A shared (immutable) reference to a sub-value `U` inside a [`Rc>`]. /// Many [`SharedSubRcRefCell`] can exist at the same time for a given [`Rc>`], -/// but if any exist, then no [`MutSubRcRefCell`] can exist. +/// but if any exist, then no [`MutableSubRcRefCell`] can exist. pub(crate) struct SharedSubRcRefCell { /// This is actually a reference to the contents of the RefCell /// but we store it using `unsafe` as `'static`, and use unsafe blocks @@ -149,13 +215,26 @@ impl SharedSubRcRefCell { } } - pub(crate) fn map(self, f: impl FnOnce(&U) -> &V) -> SharedSubRcRefCell { + pub(crate) fn map( + self, + f: impl for<'a> FnOnce(&'a U) -> &'a V, + ) -> SharedSubRcRefCell { SharedSubRcRefCell { shared_ref: Ref::map(self.shared_ref, f), pointed_at: self.pointed_at, } } + pub(crate) fn map_optional( + self, + f: impl FnOnce(&U) -> Option<&V>, + ) -> Option> { + Some(SharedSubRcRefCell { + shared_ref: Ref::filter_map(self.shared_ref, f).ok()?, + pointed_at: self.pointed_at, + }) + } + pub(crate) fn try_map( self, f: impl FnOnce(&U) -> Result<&V, E>, @@ -177,6 +256,18 @@ impl SharedSubRcRefCell { } } + pub(crate) fn replace( + self, + f: impl for<'e> FnOnce(&'e U, &mut SharedSubEmplacer<'e, T, U>) -> O, + ) -> O { + let copied_ref = Ref::clone(&self.shared_ref); + let mut emplacer = SharedSubEmplacer { + inner: Some(self), + encapsulation_lifetime: std::marker::PhantomData, + }; + f(&*copied_ref, &mut emplacer) + } + /// SAFETY: /// * Must be paired with a call to `enable()` before any further use of the value. /// * Must not use the value while disabled. @@ -203,6 +294,40 @@ impl SharedSubRcRefCell { } } +pub(crate) type SharedEmplacer<'e, U> = SharedSubEmplacer<'e, AnyValue, U>; + +pub(crate) struct SharedSubEmplacer<'e, T: ?Sized, U: 'static + ?Sized> { + inner: Option>, + encapsulation_lifetime: std::marker::PhantomData<&'e ()>, +} + +impl<'e, T: 'static + ?Sized, U: 'static + ?Sized> SharedSubEmplacer<'e, T, U> { + pub(crate) fn emplace( + &mut self, + value: &'e V, + ) -> SharedSubRcRefCell { + unsafe { + // SAFETY: The lifetime 'e is equal to the &'e content argument in replace + // So this guarantees that the returned reference is valid as long as the SharedSubRcRefCell exists + self.emplace_unchecked(value) + } + } + + // SAFETY: + // * The caller must ensure that the value's lifetime is derived from the original content + pub(crate) unsafe fn emplace_unchecked( + &mut self, + value: &V, + ) -> SharedSubRcRefCell { + self.inner + .take() + .expect("You can only emplace to create a new shared value once") + .map(|_| + // SAFETY: As defined in the rustdoc above + unsafe { less_buggy_transmute::<&V, &'static V>(value) }) + } +} + impl Deref for SharedSubRcRefCell { type Target = U; diff --git a/src/sandbox/dyn_value.rs b/src/sandbox/dyn_value.rs deleted file mode 100644 index 990c3603..00000000 --- a/src/sandbox/dyn_value.rs +++ /dev/null @@ -1,120 +0,0 @@ -#![allow(unused)] - -use std::any::Any; -trait IsValue: Any { - type TypeData: 'static + TypeData; -} -trait DynValue: Any { - fn type_data(&self) -> &dyn DynTypeData; -} -impl DynValue for T { - fn type_data(&self) -> &'static dyn DynTypeData { - ::TypeData::static_ref() - } -} - -trait IsIterable { - fn do_something(self) -> String; -} - -trait DynIterable { - fn do_something(self: Box) -> String; -} - -impl DynIterable for T { - fn do_something(self: Box) -> String { - ::do_something(*self) - } -} - -enum IntegerValue { - U32(u32), - // Other integer types could be added here -} - -impl IsValue for u32 { - type TypeData = U32TypeData; -} -struct AnyRef<'a, T: ?Sized + 'static> { - value: &'a T, -} - -trait TypeData { - type Value: 'static + IsValue; - - fn static_ref() -> &'static dyn DynTypeData; - - fn as_value(value: Box) -> Option { - let value_any: Box = value; - value_any.downcast::().ok().map(|v| *v) - } - - fn as_value_ref(value: AnyRef) -> Option> { - let value_any: &dyn Any = value.value; - value_any - .downcast_ref::() - .map(|v| AnyRef { value: v }) - } - - fn as_integer(value: Self::Value) -> Option { - let _ = value; - None - } - - fn as_iterable(value: Self::Value) -> Option> { - let _ = value; - None - } -} - -trait DynTypeData { - fn as_integer(&self, value: Box) -> Option; - fn as_iterable(&self, value: Box) -> Option>; -} - -impl DynTypeData for T { - fn as_integer(&self, value: Box) -> Option { - T::as_integer(Self::as_value(value)?) - } - - fn as_iterable(&self, value: Box) -> Option> { - T::as_iterable(Self::as_value(value)?) - } -} - -struct U32TypeData; - -impl TypeData for U32TypeData { - type Value = u32; - - fn static_ref() -> &'static dyn DynTypeData { - &Self - } - - fn as_integer(value: Self::Value) -> Option { - Some(IntegerValue::U32(value)) - } -} - -struct ArrayValue; -impl IsValue for ArrayValue { - type TypeData = ArrayTypeData; -} -impl IsIterable for ArrayValue { - fn do_something(self) -> String { - "ArrayValue iterable".to_string() - } -} -struct ArrayTypeData; - -impl TypeData for ArrayTypeData { - type Value = ArrayValue; - - fn static_ref() -> &'static dyn DynTypeData { - &Self - } - - fn as_iterable(value: Self::Value) -> Option> { - Some(Box::new(value)) - } -} diff --git a/src/sandbox/gat_value.rs b/src/sandbox/gat_value.rs deleted file mode 100644 index c82d85fd..00000000 --- a/src/sandbox/gat_value.rs +++ /dev/null @@ -1,662 +0,0 @@ -#![allow(unused)] - -// SUMMARY: -// - Strip SpanRange out of Owned/Shared etc. -// - Instead, have Spanned wrapper type, which can be destructured with Spanned(value, span) -// - Implement something akin to the below: -// - `type Owned = Actual<'static, T, BeOwned>` -// - `type CopyOnWrite = Actual<'static, T, BeCopyOnWrite>` -// - `type Shared = Actual<'static, T, BeShared>` -// -// - For mapping outputs to methods/functions, we can do: -// `>::into_actual().map_type::().into_returned_value()` -// - For mapping arguments to methods/functions, we can use the `IsArgument` implementation below - -use crate::internal_prelude::*; - -trait IsOwnership: Sized { - type Leaf<'a, T: IsLeaf>; - type DynLeaf<'a, T: 'static + ?Sized>; - const ARGUMENT_OWNERSHIP: ArgumentOwnership; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult>; -} - -trait IsLeaf: 'static { - fn into_iterable(self: Box) -> Option> { - None - } - fn as_iterable(&self) -> Option<&dyn IsIterable> { - None - } -} - -struct BeOwned; -impl IsOwnership for BeOwned { - type Leaf<'a, T: IsLeaf> = T; - type DynLeaf<'a, T: 'static + ?Sized> = Box; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult> { - // value.expect_owned() - todo!() - } -} - -// Roughly equivalent to an owned, but wrapped so that it can be turned into a Shared/Mutable easily. -struct BeReferencable; -impl IsOwnership for BeReferencable { - type Leaf<'a, T: IsLeaf> = Rc>; - type DynLeaf<'a, T: 'static + ?Sized> = Rc>; - - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Owned; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult> { - // Rc::new(RefCell::new(value.expect_owned())) - todo!() - } -} - -struct BeAnyRef; -impl IsOwnership for BeAnyRef { - type Leaf<'a, T: IsLeaf> = AnyRef<'a, T>; - type DynLeaf<'a, T: 'static + ?Sized> = AnyRef<'a, T>; - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Shared; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult> { - todo!() - } -} - -struct BeAnyRefMut; -impl IsOwnership for BeAnyRefMut { - type Leaf<'a, T: IsLeaf> = AnyRefMut<'a, T>; - type DynLeaf<'a, T: 'static + ?Sized> = AnyRefMut<'a, T>; - - const ARGUMENT_OWNERSHIP: ArgumentOwnership = ArgumentOwnership::Mutable; - - fn from_argument_value( - value: ArgumentValue, - ) -> ExecutionResult> { - todo!() - } -} - -struct OwnedToReferencableMapper; - -trait OwnershipMapper { - type HFrom: IsOwnership; - type HTo: IsOwnership; - - fn map_leaf<'a, T: IsLeaf>( - leaf: ::Leaf<'a, T>, - ) -> ::Leaf<'a, T>; -} - -impl OwnershipMapper for OwnedToReferencableMapper { - type HFrom = BeOwned; - type HTo = BeReferencable; - - fn map_leaf<'a, T: IsLeaf>( - leaf: ::Leaf<'a, T>, - ) -> ::Leaf<'a, T> { - Rc::new(RefCell::new(leaf)) - } -} - -trait IsType: Sized { - type Content<'a, H: IsOwnership>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo>; - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a>; - - fn type_name() -> &'static str { - todo!() - } -} - -trait MapToType: IsType { - fn map_to_type<'a>(content: Self::Content<'a, H>) -> T::Content<'a, H>; -} - -trait MaybeMapFromType: IsType { - fn maybe_map_from_type<'a>(content: T::Content<'a, H>) -> Option>; - - fn resolve<'a>( - actual: Actual<'a, T, H>, - span_range: SpanRange, - context: &str, - ) -> ExecutionResult> { - let content = match Self::maybe_map_from_type(actual.0) { - Some(c) => c, - None => { - return span_range.value_err(format!( - "{} cannot be mapped to {}", - context, - T::type_name(), - )) - } - }; - Ok(Actual(content)) - } -} - -trait IsChildType: IsType { - type ParentType: IsType; - fn into_parent<'a, H: IsOwnership>( - content: Self::Content<'a, H>, - ) -> ::Content<'a, H>; - fn from_parent<'a, H: IsOwnership>( - content: ::Content<'a, H>, - ) -> Option>; -} - -macro_rules! impl_ancestor_chain_conversions { - ($child:ty => $parent:ty => [$($ancestor:ty),* $(,)?]) => { - impl MaybeMapFromType<$child, H> for $child - { - fn maybe_map_from_type<'a>( - content: <$child as IsType>::Content<'a, H>, - ) -> Option<<$child as IsType>::Content<'a, H>> { - Some(content) - } - } - - impl MapToType<$child, H> for $child - { - fn map_to_type<'a>( - content: <$child as IsType>::Content<'a, H>, - ) -> <$child as IsType>::Content<'a, H> { - content - } - } - - impl MaybeMapFromType<$parent, H> for $child - { - fn maybe_map_from_type<'a>( - content: <$parent as IsType>::Content<'a, H>, - ) -> Option<<$child as IsType>::Content<'a, H>> { - <$child as IsChildType>::from_parent(content) - } - } - - impl MapToType<$parent, H> for $child - { - fn map_to_type<'a>( - content: <$child as IsType>::Content<'a, H>, - ) -> <$parent as IsType>::Content<'a, H> { - <$child as IsChildType>::into_parent(content) - } - } - - $( - impl MaybeMapFromType<$ancestor, H> for $child { - fn maybe_map_from_type<'a>( - content: <$ancestor as IsType>::Content<'a, H>, - ) -> Option<<$child as IsType>::Content<'a, H>> { - <$child as MaybeMapFromType<$parent, H>>::maybe_map_from_type(<$parent as MaybeMapFromType<$ancestor, H>>::maybe_map_from_type(content)?) - } - } - - impl MapToType<$ancestor, H> for $child { - fn map_to_type<'a>( - content: <$child as IsType>::Content<'a, H>, - ) -> <$ancestor as IsType>::Content<'a, H> { - <$parent as MapToType<$ancestor, H>>::map_to_type(<$child as MapToType<$parent, H>>::map_to_type(content)) - } - } - )* - }; -} - -struct ValueType; - -impl MapToType for ValueType { - fn map_to_type<'a>(content: Self::Content<'a, H>) -> Self::Content<'a, H> { - content - } -} - -impl MaybeMapFromType for ValueType { - fn maybe_map_from_type<'a>(content: Self::Content<'a, H>) -> Option> { - Some(content) - } -} - -impl IsType for ValueType { - type Content<'a, H: IsOwnership> = ValueContent<'a, H>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - match content { - ValueContent::Integer(x) => ValueContent::Integer(x.map_ownership::()), - ValueContent::Object(x) => ValueContent::Object(x.map_ownership::()), - } - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - match content { - ValueContent::Integer(x) => x.map_content::(), - ValueContent::Object(x) => x.map_content::(), - } - } -} - -enum ValueContent<'a, H: IsOwnership> { - Integer(Actual<'a, IntegerType, H>), - Object(Actual<'a, ObjectType, H>), - // ... -} - -struct Actual<'a, K: IsType, H: IsOwnership>(K::Content<'a, H>); - -impl<'a, K: IsType, H: IsOwnership> Actual<'a, K, H> { - #[inline] - fn of(content: impl IntoValueContent<'a, TypeData = K, Ownership = H>) -> Self { - Actual(content.into_content()) - } - - #[inline] - fn map_ownership>(self) -> Actual<'a, K, M::HTo> { - Actual(K::map_ownership::(self.0)) - } - - #[inline] - fn map_content>(self) -> M::TOut<'a> { - K::map_content::<'a, H, M>(self.0) - } - - fn map_type(self) -> Actual<'a, U, H> - where - K: MapToType, - { - Actual(K::map_to_type(self.0)) - } - - fn map_type_maybe>(self) -> Option> { - Some(Actual(U::maybe_map_from_type(self.0)?)) - } -} - -impl<'a, K: IsType, H: IsOwnership> Spanned> { - fn resolve_as>( - self, - description: &str, - ) -> ExecutionResult> { - let Spanned(value, span_range) = self; - U::resolve(value, span_range, description) - } -} - -impl<'a, K: IsType> Actual<'a, K, BeOwned> { - fn into_referencable(self) -> Actual<'a, K, BeReferencable> { - self.map_ownership::() - } -} - -impl<'a, K: IsType + MapToType, H: IsOwnership> Actual<'a, K, H> { - fn into_value(self) -> Actual<'a, ValueType, H> { - self.map_type() - } -} - -type Value = Actual<'static, ValueType, BeOwned>; -type ValueReferencable = Actual<'static, ValueType, BeReferencable>; -type ValueRef<'a> = Actual<'a, ValueType, BeAnyRef>; -type ValueMut<'a> = Actual<'a, ValueType, BeAnyRefMut>; - -struct ObjectValue; - -impl IsLeaf for ObjectValue {} - -struct ObjectType; - -impl IsType for ObjectType { - type Content<'a, H: IsOwnership> = H::Leaf<'a, ObjectValue>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - M::map_leaf(content) - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - M::map_leaf(content) - } -} - -impl_ancestor_chain_conversions!( - ObjectType => ValueType => [] -); - -impl IsChildType for ObjectType { - type ParentType = ValueType; - - fn into_parent<'a, H: IsOwnership>( - content: Self::Content<'a, H>, - ) -> ::Content<'a, H> { - ValueContent::Object(Actual(content)) - } - - fn from_parent<'a, H: IsOwnership>( - content: ::Content<'a, H>, - ) -> Option> { - match content { - ValueContent::Object(o) => Some(o.0), - _ => None, - } - } -} - -enum IntegerContent<'a, H: IsOwnership> { - U32(Actual<'a, U32Type, H>), - // ... -} - -struct IntegerType; - -impl IsType for IntegerType { - type Content<'a, H: IsOwnership> = IntegerContent<'a, H>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - match content { - IntegerContent::U32(i) => IntegerContent::U32(i.map_ownership::()), - } - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - match content { - IntegerContent::U32(x) => x.map_content::(), - } - } -} - -impl_ancestor_chain_conversions!( - IntegerType => ValueType => [] -); - -impl IsChildType for IntegerType { - type ParentType = ValueType; - - fn into_parent<'a, H: IsOwnership>( - content: Self::Content<'a, H>, - ) -> ::Content<'a, H> { - ValueContent::Integer(Actual(content)) - } - - fn from_parent<'a, H: IsOwnership>( - content: ::Content<'a, H>, - ) -> Option> { - match content { - ValueContent::Integer(i) => Some(i.0), - _ => None, - } - } -} - -impl IsLeaf for u32 { - fn into_iterable(self: Box) -> Option> { - Some(self) - } - fn as_iterable(&self) -> Option<&dyn IsIterable> { - Some(self) - } -} - -impl IsIterable for u32 { - fn into_iterator(self: Box) -> ExecutionResult { - Ok(IteratorValue::new_any(std::iter::once( - (*self).into_value(), - ))) - } - - fn len(&self, error_span_range: SpanRange) -> ExecutionResult { - Ok(0) - } -} - -struct U32Type; - -impl IsType for U32Type { - type Content<'a, H: IsOwnership> = H::Leaf<'a, u32>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - M::map_leaf(content) - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - M::map_leaf(content) - } -} - -impl IsChildType for U32Type { - type ParentType = IntegerType; - - fn into_parent<'a, H: IsOwnership>( - content: Self::Content<'a, H>, - ) -> ::Content<'a, H> { - IntegerContent::U32(Actual(content)) - } - - fn from_parent<'a, H: IsOwnership>( - content: ::Content<'a, H>, - ) -> Option> { - match content { - IntegerContent::U32(i) => Some(i.0), - _ => None, - } - } -} - -impl_ancestor_chain_conversions!( - U32Type => IntegerType => [ValueType] -); - -#[test] -fn test() { - let my_u32 = Actual::of(42u32); - let as_value = my_u32.map_type::(); - let back_to_u32 = as_value.map_type_maybe::().unwrap(); - assert_eq!(back_to_u32.0, 42u32); -} - -// Clashes with other blanket trait it will replace! -// -// impl< -// X: FromValueContent<'static, TypeData = T, Ownership = H>, -// T: MaybeMapFromType + HierarchicalTypeData, -// H: IsOwnership, -// > IsArgument for X { -// type ValueType = T; -// const OWNERSHIP: ArgumentOwnership = H::ARGUMENT_OWNERSHIP; - -// fn from_argument(value: ArgumentValue) -> ExecutionResult { -// let span_range = value.span_range(); -// let ownership_mapped = H::from_argument_value(value)?; -// let type_mapped = T::resolve(ownership_mapped, span_range, "This argument")?; -// Ok(X::from_actual(type_mapped)) -// } -// } - -trait IsValueContent<'a> { - type TypeData: IsType; - type Ownership: IsOwnership; -} - -trait IntoValueContent<'a>: IsValueContent<'a> { - fn into_content(self) -> ::Content<'a, Self::Ownership>; - - #[inline] - fn into_actual(self) -> Actual<'a, Self::TypeData, Self::Ownership> - where - Self: Sized, - { - Actual::of(self) - } -} - -trait FromValueContent<'a>: IsValueContent<'a> { - fn from_content(content: ::Content<'a, Self::Ownership>) -> Self; - - #[inline] - fn from_actual(actual: Actual<'a, Self::TypeData, Self::Ownership>) -> Self - where - Self: Sized, - { - Self::from_content(actual.0) - } -} - -impl<'a, T: IsType, H: IsOwnership> IsValueContent<'a> for Actual<'a, T, H> { - type TypeData = T; - type Ownership = H; -} - -impl<'a, T: IsType, H: IsOwnership> IntoValueContent<'a> for Actual<'a, T, H> { - fn into_content(self) -> ::Content<'a, Self::Ownership> { - self.0 - } -} - -impl<'a, T: IsType, H: IsOwnership> FromValueContent<'a> for Actual<'a, T, H> { - fn from_content(content: ::Content<'a, Self::Ownership>) -> Self { - Actual(content) - } -} - -impl<'a> IsValueContent<'a> for u32 { - type TypeData = U32Type; - type Ownership = BeOwned; -} - -impl<'a> IntoValueContent<'a> for u32 { - fn into_content(self) -> u32 { - self - } -} - -impl<'a> FromValueContent<'a> for u32 { - fn from_content(content: u32) -> Self { - content - } -} - -impl<'a, H: IsOwnership> IsValueContent<'a> for ValueContent<'a, H> { - type TypeData = ValueType; - type Ownership = H; -} - -impl<'a, H: IsOwnership> IntoValueContent<'a> for ValueContent<'a, H> { - fn into_content(self) -> ::Content<'a, Self::Ownership> { - self - } -} - -struct IterableType; - -trait IsIterable: 'static { - fn into_iterator(self: Box) -> ExecutionResult; - fn len(&self, error_span_range: SpanRange) -> ExecutionResult; -} - -impl IsType for IterableType { - type Content<'a, H: IsOwnership> = H::DynLeaf<'a, dyn IsIterable>; - - fn map_ownership<'a, M: OwnershipMapper>( - content: Self::Content<'a, M::HFrom>, - ) -> Self::Content<'a, M::HTo> { - // Might need to create separate IsMappableType trait and not impl it for dyn types - // And/or have a separate dyn mapping facility if we need it - todo!(); - } - - fn map_content<'a, H: IsOwnership, M: ContentMapper>( - content: Self::Content<'a, H>, - ) -> M::TOut<'a> { - // Might need to create separate IsMappableType trait and not impl it for dyn types - // And/or have a separate dyn mapping facility if we need it - todo!() - } -} - -impl MaybeMapFromType for IterableType { - fn maybe_map_from_type<'a>( - content: ::Content<'a, BeOwned>, - ) -> Option> { - T::map_content::<'a, BeOwned, IterableMapper>(content) - } -} - -impl MaybeMapFromType for IterableType { - fn maybe_map_from_type<'a>( - content: ::Content<'a, BeAnyRef>, - ) -> Option> { - T::map_content::<'a, BeAnyRef, IterableMapper>(content) - } -} - -trait ContentMapper { - type TOut<'a>; - - fn map_leaf<'a, L: IsLeaf>(leaf: H::Leaf<'a, L>) -> Self::TOut<'a>; -} - -struct IterableMapper; - -impl ContentMapper for IterableMapper { - type TOut<'a> = Option>; - - fn map_leaf<'a, L: IsLeaf>(leaf: L) -> Self::TOut<'a> { - L::into_iterable(Box::new(leaf)) - } -} - -impl ContentMapper for IterableMapper { - type TOut<'a> = Option>; - - fn map_leaf<'a, L: IsLeaf>(leaf: ::Leaf<'a, L>) -> Self::TOut<'a> { - leaf.map_optional(|x| L::as_iterable(x)) - } -} - -#[test] -fn test_iterable_mapping() { - let my_value = Actual::of(42u32); - // let my_value = my_value.map_type::(); - let as_iterable = my_value.map_type_maybe::().unwrap(); - let iterator = as_iterable.0.into_iterator().unwrap(); - let collected: Vec = iterator - .map(|v| { - Spanned(Owned::new(v), Span::call_site().span_range()) - .resolve_as("u32") - .unwrap() - }) - .collect(); - assert_eq!(collected, vec![42u32]); -} diff --git a/src/sandbox/mod.rs b/src/sandbox/mod.rs deleted file mode 100644 index 50e1436c..00000000 --- a/src/sandbox/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod dyn_value; -mod gat_value; diff --git a/tests/compilation_failures/expressions/cast_int_to_bool.stderr b/tests/compilation_failures/expressions/cast_int_to_bool.stderr index 52aaf6bd..b8cfc15b 100644 --- a/tests/compilation_failures/expressions/cast_int_to_bool.stderr +++ b/tests/compilation_failures/expressions/cast_int_to_bool.stderr @@ -1,4 +1,4 @@ -error: The as bool operator is not supported for untyped integer values +error: The as bool operator is not supported for an untyped integer --> tests/compilation_failures/expressions/cast_int_to_bool.rs:5:13 | 5 | #(1 as bool) diff --git a/tests/compilation_failures/expressions/cast_to_float.rs b/tests/compilation_failures/expressions/cast_to_float.rs new file mode 100644 index 00000000..3342659c --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_float.rs @@ -0,0 +1,7 @@ +use preinterpret::*; + +fn main() { + let _ = run!{ + 0u8 as float + }; +} \ No newline at end of file diff --git a/tests/compilation_failures/expressions/cast_to_float.stderr b/tests/compilation_failures/expressions/cast_to_float.stderr new file mode 100644 index 00000000..7503700e --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_float.stderr @@ -0,0 +1,5 @@ +error: This type is not supported in cast expressions. Perhaps you want 'as untyped_float'? + --> tests/compilation_failures/expressions/cast_to_float.rs:5:16 + | +5 | 0u8 as float + | ^^^^^ diff --git a/tests/compilation_failures/expressions/cast_to_int.rs b/tests/compilation_failures/expressions/cast_to_int.rs new file mode 100644 index 00000000..c5f6ad90 --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_int.rs @@ -0,0 +1,7 @@ +use preinterpret::*; + +fn main() { + let _ = run!{ + 0u8 as int + }; +} \ No newline at end of file diff --git a/tests/compilation_failures/expressions/cast_to_int.stderr b/tests/compilation_failures/expressions/cast_to_int.stderr new file mode 100644 index 00000000..55810f00 --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_int.stderr @@ -0,0 +1,5 @@ +error: This type is not supported in cast expressions. Perhaps you want 'as untyped_int'? + --> tests/compilation_failures/expressions/cast_to_int.rs:5:16 + | +5 | 0u8 as int + | ^^^ diff --git a/tests/compilation_failures/expressions/cast_to_unknown_type.rs b/tests/compilation_failures/expressions/cast_to_unknown_type.rs new file mode 100644 index 00000000..523675cb --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_unknown_type.rs @@ -0,0 +1,7 @@ +use preinterpret::*; + +fn main() { + let _ = run!{ + 0u8 as nonexistent_type + }; +} \ No newline at end of file diff --git a/tests/compilation_failures/expressions/cast_to_unknown_type.stderr b/tests/compilation_failures/expressions/cast_to_unknown_type.stderr new file mode 100644 index 00000000..ca49fb70 --- /dev/null +++ b/tests/compilation_failures/expressions/cast_to_unknown_type.stderr @@ -0,0 +1,5 @@ +error: Unknown type 'nonexistent_type' + --> tests/compilation_failures/expressions/cast_to_unknown_type.rs:5:16 + | +5 | 0u8 as nonexistent_type + | ^^^^^^^^^^^^^^^^ diff --git a/tests/compilation_failures/expressions/fix_me_negative_max_int_fails.stderr b/tests/compilation_failures/expressions/fix_me_negative_max_int_fails.stderr index 20765e62..71c954bf 100644 --- a/tests/compilation_failures/expressions/fix_me_negative_max_int_fails.stderr +++ b/tests/compilation_failures/expressions/fix_me_negative_max_int_fails.stderr @@ -1,4 +1,4 @@ -error: The - operator is not supported for unsupported literal values +error: The - operator is not supported for an unsupported literal --> tests/compilation_failures/expressions/fix_me_negative_max_int_fails.rs:8:11 | 8 | #(-128i8) diff --git a/tests/compilation_failures/expressions/large_range_to_string.stderr b/tests/compilation_failures/expressions/large_range_to_string.stderr index 54e7f415..1e971e34 100644 --- a/tests/compilation_failures/expressions/large_range_to_string.stderr +++ b/tests/compilation_failures/expressions/large_range_to_string.stderr @@ -1,5 +1,5 @@ -error: This type is not supported in cast expressions - --> tests/compilation_failures/expressions/large_range_to_string.rs:5:25 +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/expressions/untyped_integer_overflow.rs b/tests/compilation_failures/expressions/untyped_integer_overflow.rs index 50b58009..48ceb487 100644 --- a/tests/compilation_failures/expressions/untyped_integer_overflow.rs +++ b/tests/compilation_failures/expressions/untyped_integer_overflow.rs @@ -1,5 +1,5 @@ use preinterpret::*; fn main() { - let _ = run!(i128::MAX as int + 1); + let _ = run!(i128::MAX as untyped_int + 1); } \ No newline at end of file diff --git a/tests/compilation_failures/expressions/untyped_integer_overflow.stderr b/tests/compilation_failures/expressions/untyped_integer_overflow.stderr index 6418f54d..2a1e29b4 100644 --- a/tests/compilation_failures/expressions/untyped_integer_overflow.stderr +++ b/tests/compilation_failures/expressions/untyped_integer_overflow.stderr @@ -1,5 +1,5 @@ error: The untyped integer operation 170141183460469231731687303715884105727 + 1 overflowed in i128 space - --> tests/compilation_failures/expressions/untyped_integer_overflow.rs:4:35 + --> tests/compilation_failures/expressions/untyped_integer_overflow.rs:4:43 | -4 | let _ = run!(i128::MAX as int + 1); - | ^ +4 | let _ = run!(i128::MAX as untyped_int + 1); + | ^ diff --git a/tests/compilation_failures/operations/compare_bool_and_int.stderr b/tests/compilation_failures/operations/compare_bool_and_int.stderr index 9f7cc033..cd8c5ce4 100644 --- a/tests/compilation_failures/operations/compare_bool_and_int.stderr +++ b/tests/compilation_failures/operations/compare_bool_and_int.stderr @@ -1,4 +1,4 @@ -error: This argument is expected to be a boolean, but it is an untyped integer +error: This argument is expected to be a bool, but it is an untyped integer --> tests/compilation_failures/operations/compare_bool_and_int.rs:4:26 | 4 | let _ = run!(true == 1); diff --git a/tests/compilation_failures/operations/logical_and_on_int.stderr b/tests/compilation_failures/operations/logical_and_on_int.stderr index dcdfd0fa..0e3f47bb 100644 --- a/tests/compilation_failures/operations/logical_and_on_int.stderr +++ b/tests/compilation_failures/operations/logical_and_on_int.stderr @@ -1,4 +1,4 @@ -error: The left operand to && is expected to be a boolean, but it is an untyped integer +error: The left operand to && is expected to be a bool, but it is an untyped integer --> tests/compilation_failures/operations/logical_and_on_int.rs:4:18 | 4 | let _ = run!(1 && 2); diff --git a/tests/compilation_failures/operations/logical_not_on_int.stderr b/tests/compilation_failures/operations/logical_not_on_int.stderr index 49d0d36b..f9bf8831 100644 --- a/tests/compilation_failures/operations/logical_not_on_int.stderr +++ b/tests/compilation_failures/operations/logical_not_on_int.stderr @@ -1,4 +1,4 @@ -error: The ! operator is not supported for untyped integer values +error: The ! operator is not supported for an untyped integer --> tests/compilation_failures/operations/logical_not_on_int.rs:4:18 | 4 | let _ = run!(!5); diff --git a/tests/compilation_failures/operations/logical_or_on_int.stderr b/tests/compilation_failures/operations/logical_or_on_int.stderr index 26c068d7..ce7a0f7e 100644 --- a/tests/compilation_failures/operations/logical_or_on_int.stderr +++ b/tests/compilation_failures/operations/logical_or_on_int.stderr @@ -1,4 +1,4 @@ -error: The left operand to || is expected to be a boolean, but it is an untyped integer +error: The left operand to || is expected to be a bool, but it is an untyped integer --> tests/compilation_failures/operations/logical_or_on_int.rs:4:18 | 4 | let _ = run!(1 || 2); diff --git a/tests/compilation_failures/operations/negate_unsigned.stderr b/tests/compilation_failures/operations/negate_unsigned.stderr index d257b1b1..5316148d 100644 --- a/tests/compilation_failures/operations/negate_unsigned.stderr +++ b/tests/compilation_failures/operations/negate_unsigned.stderr @@ -1,4 +1,4 @@ -error: The - operator is not supported for u32 values +error: The - operator is not supported for a u32 --> tests/compilation_failures/operations/negate_unsigned.rs:4:18 | 4 | let _ = run!(-5u32); diff --git a/tests/expressions.rs b/tests/expressions.rs index 2e8b64ce..750dfff9 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -32,8 +32,8 @@ fn test_basic_evaluate_works() { assert!(run!(true || false && false)); // The && has priority assert!(run!(true | false & false)); // The & has priority assert_eq!(run!(true as u32 + 2), 3); - assert_eq!(run!(3.57 as int + 1), 4u32); - assert_eq!(run!(3.57 as int + 1), 4u64); + assert_eq!(run!(3.57 as untyped_int + 1), 4u32); + assert_eq!(run!(3.57 as untyped_int + 1), 4u64); assert_eq!(run!(0b1000 & 0b1101), 0b1000); assert_eq!(run!(0b1000 | 0b1101), 0b1101); assert_eq!(run!(0b1000 ^ 0b1101), 0b101); @@ -53,7 +53,7 @@ fn test_basic_evaluate_works() { ), "%[5 + 2 = 7]" ); - assert_eq!(run!(1 + (1..2) as int), 2); + assert_eq!(run!(1 + (1..2) as untyped_int), 2); assert!(!run!("hello" == "world")); assert!(run!("hello" == "hello")); assert!(run!('A' as u8 == 65)); diff --git a/tests/operations.rs b/tests/operations.rs index a3980310..a1c5a95a 100644 --- a/tests/operations.rs +++ b/tests/operations.rs @@ -981,6 +981,8 @@ mod float_compound_assignment { #[test] fn add_assign() { assert_eq!(run!(let x = 1.5; x += 2.5; x), 4.0); + assert_eq!(run!(let x = 1.5; x += 2.5f32; x), 4.0); + assert_eq!(run!(let x = 1.5; x += 2.5f64; x), 4.0); assert_eq!(run!(let x = 1.5f32; x += 2.5; x), 4.0f32); assert_eq!(run!(let x = 1.5f64; x += 2.5; x), 4.0f64); } @@ -1541,21 +1543,21 @@ mod cast_operations { assert_eq!(run!(256u32 as u8), 0u8); // overflow wraps assert_eq!(run!(-1i32 as u32), u32::MAX); assert_eq!(run!(5 as i32), 5i32); - assert_eq!(run!(5 as int), 5); + assert_eq!(run!(5 as untyped_int), 5); } #[test] fn cast_integer_to_float() { assert_eq!(run!(5u32 as f32), 5.0f32); assert_eq!(run!(5i32 as f64), 5.0f64); - assert_eq!(run!(5 as float), 5.0); + assert_eq!(run!(5 as untyped_float), 5.0); } #[test] fn cast_float_to_integer() { assert_eq!(run!(5.7f32 as i32), 5i32); assert_eq!(run!(5.7f64 as u32), 5u32); - assert_eq!(run!(5.7 as int), 5); + assert_eq!(run!(5.7 as untyped_int), 5); } #[test] @@ -1577,7 +1579,7 @@ mod cast_operations { fn cast_char_to_integer() { assert_eq!(run!('A' as u8), 65u8); assert_eq!(run!('A' as u32), 65u32); - assert_eq!(run!('A' as int), 65); + assert_eq!(run!('A' as untyped_int), 65); } #[test]