Skip to content

Commit

Permalink
Improved converters for wrappers, and updated constraints.
Browse files Browse the repository at this point in the history
  • Loading branch information
Uralstech committed Dec 21, 2024
1 parent 67528e2 commit 8373cc5
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/docsrc/Embedding-ezrSquared.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The NuGet package is targeted for .NET 9 and .NET Standard 2.1 and is compatible
You may need to use third-party packages like [*NuGetForUnity*](https://github.com/GlitchEnzo/NuGetForUnity)
to import it into Unity.

## CodeExecutor.cs
## CodeExecutor

The high-level class to interact with the ezr² runtime is the [`CodeExecutor`](~/api/EzrSquared.Executor.CodeExecutor.yml) class.

Expand Down
74 changes: 74 additions & 0 deletions docs/docsrc/Get-started-with-CSAELs.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,80 @@ of the `EzrTypeWrapper`. A single constructor, for each type, can be declared a
will be used when the `Execute` method is called on the type wrapper. The constructor must be declared with
a [PrimaryConstructorAttribute](~/api/EzrSquared.Runtime.Types.Wrappers.Members.Methods.PrimaryConstructorAttribute.yml).

## Type Support

### ezr² Object -> C# Object

The method responsible for converting `IEzrObject`s to C# objects is [`EzrWrapper.EzrObjectToCSharp`](~/api/EzrSquared.Runtime.Types.Wrappers.EzrWrapper-1.yml).

The following table shows which C# types can be converted from which ezr² objects.

| C# Type | Converted From |
|-------------------|-----------------------|
| byte | EzrFloat, EzrInteger |
| sbyte | EzrFloat, EzrInteger |
| short | EzrFloat, EzrInteger |
| ushort | EzrFloat, EzrInteger |
| int | EzrFloat, EzrInteger |
| uint | EzrFloat, EzrInteger |
| long | EzrFloat, EzrInteger |
| ulong | EzrFloat, EzrInteger |
| BigInteger | EzrFloat, EzrInteger |
| float | EzrFloat, EzrInteger |
| double | EzrFloat, EzrInteger |
| decimal | EzrFloat, EzrInteger |
| bool | EzrBoolean |
| char | EzrCharacter |
| string | IEzrString |
| Null Reference | EzrNothing |

If the C# type is assignable from the ezr² object's type, for example, if the C# type
is `IEzrObject` and the ezr² object is `EzrInteger`, the method passes the ezr² object
without any conversion.

If the C# type is `Task`, then it returns `Task<T>.FromResult([the ezr² object])` where `T` is the type of
the ezr² object. If the type is a variant of task, like `Task<EzrInteger>`, then it uses the enclosed type
to run the method again with it as the target.

Arrays are also supported, the method checks if the ezr² object is an `IEzrIndexedCollection` and tries to
convert each element to the array's element type.

For all other types, it checks if the ezr² object is an `EzrObjectWrapper` and checks if the target type
is assignable from the wrapped type.

The method also supports the `Nullable<T>` variants of all the above, where it checks if the ezr² object
is an `EzrNothing` or tries to convert the object to the underlying type of the `Nullable`.

### C# Object -> ezr² Object

The method responsible for converting C# objects to `IEzrObject`s is [`EzrWrapper.CSharpToEzrObject`](~/api/EzrSquared.Runtime.Types.Wrappers.EzrWrapper-1.yml).

The following table shows which C# types are natively supported:

| C# Type | Converted To |
|-------------------|-----------------------|
| byte | EzrInteger |
| sbyte | EzrInteger |
| short | EzrInteger |
| ushort | EzrInteger |
| int | EzrInteger |
| uint | EzrInteger |
| long | EzrInteger |
| ulong | EzrInteger |
| BigInteger | EzrInteger |
| float | EzrFloat |
| double | EzrFloat |
| decimal | EzrFloat |
| bool | EzrBoolean |
| char | EzrCharacter |
| string | EzrString |
| Null Reference | EzrNothing |
| Array | EzrArray |

Array elements are converted based on the array element type. If the C# object is itself
an `IEzrObject`, it is returned as-is. All other types not listed here are wrapped into
`EzrObjectWrapper`s.

## Example References

All runtime error types in ezr² are wrapped! Although the objects themselves are `IEzrObject`s, the type is
Expand Down
5 changes: 4 additions & 1 deletion src/Runtime/Types/Wrappers/EzrWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ public EzrWrapper(TMemberInfo wrappedMember, object? instance, Context parentCon
result.Failure(new EzrUnexpectedTypeError($"Expected string, character or character list, but got object of type \"{value.TypeName}\"!", Context, value.StartPosition, value.EndPosition));
break;

case TypeCode.Object when Nullable.GetUnderlyingType(targetType) is Type underlyingType:
return value is EzrNothing ? null : EzrObjectToCSharp(value, underlyingType, result);

case TypeCode.Object when targetType.IsAssignableFrom(value.GetType()):
return value;

Expand Down Expand Up @@ -280,7 +283,7 @@ public EzrWrapper(TMemberInfo wrappedMember, object? instance, Context parentCon
break;

default:
if (value is EzrObjectWrapper wrapper && wrapper.SharpMember == targetType)
if (value is EzrObjectWrapper wrapper && targetType.IsAssignableFrom(wrapper.SharpMember))
return wrapper.Instance;

result.Failure(new EzrUnexpectedTypeError($"Expected wrapped object of C# type \"{targetType.Name}\", but got object of type \"{value.TypeName}\"!", Context, value.StartPosition, value.EndPosition));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ public EzrMethodBaseWrapper(TMethodBase sharpMethodBase, object? instance, Conte

if (runtimeParamAttribute is null)
{
if (autoWrapperAttribute?.Optional == true
&& parameterInfo.ParameterType.IsValueType
&& Nullable.GetUnderlyingType(parameterInfo.ParameterType) is null)
throw new ArgumentException($"Method \"{SharpMember.Name}\" contains a parameter declared optional through its {nameof(ParameterAttribute)} which is not nullable ({parameterInfo.Name})!", nameof(sharpMethodBase));

exposedParameters.Add((parameterInfo, autoWrapperAttribute?.Optional ?? false));
exposedParameterNames.Add(string.IsNullOrEmpty(autoWrapperAttribute?.Name) ? PascalToSnakeCase(parameterInfo.Name) ?? $"param_{i}" : autoWrapperAttribute.Name);
continue;
Expand Down

0 comments on commit 8373cc5

Please sign in to comment.