Skip to content

Commit

Permalink
Improved wrappers.
Browse files Browse the repository at this point in the history
  • Loading branch information
Uralstech committed Dec 17, 2024
1 parent efc6e59 commit 68d032c
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -325,17 +325,20 @@ public EzrSharpCompatibilityWrapper(TMemberInfo wrappedMember, object? instance,
/// Converts a C# object to an ezr² object.
/// </summary>
/// <param name="value">The C# object to convert.</param>
/// <param name="valueType">The type of <paramref name="value"/>.</param>
/// <param name="result">Runtime result for carrying the result and any errors.</param>
/// <returns>The converted <see cref="IEzrObject"/>.</returns>
protected internal void CSharpToEzrObject(object? value, RuntimeResult result)
protected internal void CSharpToEzrObject(object? value, Type valueType, RuntimeResult result)
{
if (value is null)
{
result.Success(NewNothingConstant());
return;
}

Type valueType = value.GetType();
if (valueType == typeof(object))
valueType = value.GetType();

switch (Type.GetTypeCode(valueType))
{
case TypeCode.Int16:
Expand Down Expand Up @@ -411,7 +414,7 @@ protected internal void HandleCSharpArrayToEzrObject(Array value, Type valueType

for (int i = 0; i < value.Length; i++)
{
CSharpToEzrObject(value.GetValue(i), result);
CSharpToEzrObject(value.GetValue(i), arrayElementType, result);
if (result.ShouldReturn)
return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,14 @@ public EzrSharpCompatibilityConstructor(
/// <inheritdoc/>
public override void Execute(Reference[] arguments, Interpreter interpreter, RuntimeResult result)
{
Dictionary<string, IEzrObject> formattedArguments = ArgumentsArrayToDictionary(arguments, result);
if (result.ShouldReturn)
return;

object?[] mappedArguments = CheckAndPopulateArguments(formattedArguments, result);
object?[] mappedArguments = CheckAndPopulateArguments(arguments, result);
if (result.ShouldReturn)
return;

try
{
object? output = SharpMember.Invoke(mappedArguments);

if (output is null)
result.Success(NewNothingConstant());
else
{
IEzrObject wrapper = new EzrSharpCompatibilityObjectInstance(output, ConstructingType, _executionContext, StartPosition, EndPosition);
if (result.ShouldReturn)
return;

result.Success(ReferencePool.Get(wrapper));
}
CSharpToEzrObject(output, ConstructingType, result);
}
catch (Exception error)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using EzrSquared.Runtime.WrapperAttributes;
using EzrSquared.Util;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace EzrSquared.Runtime.Types.CSharpWrappers.CompatWrappers.ObjectMembers.Executables;
Expand Down Expand Up @@ -58,101 +57,82 @@ public EzrSharpCompatibilityExecutable(TMethodBase sharpMethodBase, object? inst
}

/// <summary>
/// Converts an array of arguments from ezr² code to an ordered dictionary.
/// Converts an array of arguments from ezr² code to an array of primitive C# objects in the order the executable expects them in.
/// </summary>
/// <param name="arguments">The arguments.</param>
/// <param name="result">Runtime result for carrying errors.</param>
/// <returns>The dictionary.</returns>
protected internal Dictionary<string, IEzrObject> ArgumentsArrayToDictionary(Reference[] arguments, RuntimeResult result)
/// <returns>The array of objects.</returns>
protected internal object?[] CheckAndPopulateArguments(Reference[] arguments, RuntimeResult result)
{
Dictionary<string, IEzrObject> formattedArguments = new(arguments.Length);
int parametersLength = Parameters.Length;

object?[] formattedArguments = parametersLength == 0 ? [] : new object?[parametersLength];
Array.Fill(formattedArguments, Type.Missing);

int index = 0;
int requiredKeywordArguments = 0;
for (int i = 0; i < arguments.Length; i++)
int nextUnnamedParamIndex = 0; // Track the index for unnamed params.
for (int argIndex = 0; argIndex < arguments.Length; argIndex++)
{
Reference reference = arguments[i];
if (!string.IsNullOrEmpty(reference.Name) && !reference.IsRegistered)
{
if (formattedArguments.ContainsKey(reference.Name))
{
result.Failure(new EzrIllegalOperationError($"Cannot override already defined argument \"{reference.Name}\"!", _executionContext, reference.Object.StartPosition, reference.Object.EndPosition));
break;
}
Reference argumentReference = arguments[argIndex];
string? argumentName = argumentReference.Name;
IEzrObject argumentObject = argumentReference.Object;

formattedArguments[reference.Name] = reference.Object;
if (Array.IndexOf(ParameterNames, reference.Name) >= 0)
requiredKeywordArguments++;
}
else
if (!string.IsNullOrEmpty(argumentName) && !argumentReference.IsRegistered)
{
IndexCheck:
if (ParameterNames.Length <= index)
{
result.Failure(new EzrUnexpectedArgumentError(
requiredKeywordArguments > 0
? $"Only expected {ParameterNames.Length - requiredKeywordArguments} unnamed argument(s) as {requiredKeywordArguments} required argument(s) has/have been declared as (a) keyword argument(s)!"
: $"Only expected {ParameterNames.Length} unnamed argument(s)!",
_executionContext, StartPosition, EndPosition));
break;
}

string argumentName = ParameterNames[index];
if (!formattedArguments.ContainsKey(argumentName))
// Handle named parameter.
int parameterIndex = Array.IndexOf(ParameterNames, argumentName);
if (parameterIndex == -1)
{
formattedArguments[argumentName] = reference.Object;
index++;
result.Failure(new EzrUnexpectedArgumentError($"Did not expect argument \"{argumentName}\"!", _executionContext, argumentObject.StartPosition, argumentObject.EndPosition));
return [];
}
else

if (!ReferenceEquals(formattedArguments[parameterIndex], Type.Missing))
{
index++;
goto IndexCheck;
result.Failure(new EzrIllegalOperationError($"Cannot override already defined argument \"{argumentName}\"!", _executionContext, argumentObject.StartPosition, argumentObject.EndPosition));
return [];
}
}
}

return formattedArguments;
}

/// <summary>
/// Converts an ordered dictionary of named arguments into an array of primitive C# objects in the order the executable expects them in.
/// </summary>
/// <param name="arguments">The arguments.</param>
/// <param name="result">Runtime result for carrying errors.</param>
/// <returns>The array of objects.</returns>
protected internal object?[] CheckAndPopulateArguments(Dictionary<string, IEzrObject> arguments, RuntimeResult result)
{
object?[] formattedArguments = new object?[Parameters.Length];
Array.Fill(formattedArguments, Type.Missing);

for (int i = 0; i < Parameters.Length; i++)
{
ParameterInfo parameter = Parameters[i];
if (arguments.TryGetValue(ParameterNames[i], out IEzrObject? argument))
{
object? primitiveArgument = EzrObjectToCSharp(argument, parameter.ParameterType, result);
formattedArguments[parameterIndex] = EzrObjectToCSharp(argumentObject, Parameters[parameterIndex].ParameterType, result);
if (result.ShouldReturn)
return [];

formattedArguments[i] = primitiveArgument;
arguments.Remove(ParameterNames[i]);
ReferencePool.TryRelease(argumentReference);
continue;
}
else if (parameter.HasDefaultValue)
formattedArguments[i] = parameter.DefaultValue;
else

// Handle unnamed parameter.

// Find the next unfilled parameter, skip already assigned ones.
for (; nextUnnamedParamIndex < parametersLength && !ReferenceEquals(formattedArguments[nextUnnamedParamIndex], Type.Missing); nextUnnamedParamIndex++)
continue;

if (nextUnnamedParamIndex >= parametersLength)
{
result.Failure(new EzrMissingRequiredArgumentError($"Expected required argument \"{ParameterNames[i]}\"!", _executionContext, StartPosition, EndPosition));
result.Failure(new EzrUnexpectedArgumentError("Did not expect any more unnamed arguments!", _executionContext, argumentObject.StartPosition, argumentObject.EndPosition));
return [];
}

formattedArguments[nextUnnamedParamIndex] = EzrObjectToCSharp(argumentObject, Parameters[nextUnnamedParamIndex].ParameterType, result);
if (result.ShouldReturn)
return [];

nextUnnamedParamIndex++;
ReferencePool.TryRelease(argumentReference);
}

if (arguments.Count > 0)
// Check for missing parameters, but skip parameters with default values.
for (int i = 0; i < parametersLength; i++)
{
using Dictionary<string, IEzrObject>.Enumerator argumentsEnumerator = arguments.GetEnumerator();
argumentsEnumerator.MoveNext();
if (!ReferenceEquals(formattedArguments[i], Type.Missing))
continue;

if (!Parameters[i].HasDefaultValue)
{
result.Failure(new EzrMissingRequiredArgumentError($"Expected required argument \"{ParameterNames[i]}\"!", _executionContext, StartPosition, EndPosition));
return [];
}

KeyValuePair<string, IEzrObject> first = argumentsEnumerator.Current;
result.Failure(new EzrUnexpectedArgumentError($"Did not expect argument \"{first.Key}\"!", _executionContext, first.Value.StartPosition, first.Value.EndPosition));
formattedArguments[i] = Parameters[i].DefaultValue;
}

return formattedArguments;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,29 @@ public class EzrSharpCompatibilityFunction(MethodInfo sharpFunction, object? ins
/// <inheritdoc/>
public override string Tag { get; protected internal set; } = "ezrSquared.CSharpFunction";

/// <summary>
/// Creates a new <see cref="EzrSharpCompatibilityFunction"/> from a delegate.
/// </summary>
/// <param name="sharpFunction">The method to wrap.</param>
/// <param name="parentContext">The context in which this object was created.</param>
/// <param name="startPosition">The starting position of the object.</param>
/// <param name="endPosition">The ending position of the object.</param>
/// <param name="skipValidation">Skip method signature validation?</param>
public EzrSharpCompatibilityFunction(Delegate sharpFunction, Context parentContext, Position startPosition, Position endPosition, bool skipValidation = false)
: this(sharpFunction.Method, sharpFunction.Target, parentContext, startPosition, endPosition, skipValidation)
{ }

/// <inheritdoc/>
public override void Execute(Reference[] arguments, Interpreter interpreter, RuntimeResult result)
{
Dictionary<string, IEzrObject> formattedArguments = ArgumentsArrayToDictionary(arguments, result);
if (result.ShouldReturn)
return;

object?[] mappedArguments = CheckAndPopulateArguments(formattedArguments, result);
object?[] mappedArguments = CheckAndPopulateArguments(arguments, result);
if (result.ShouldReturn)
return;

try
{
object? output = SharpMember.Invoke(Instance, mappedArguments);

if (output is null)
result.Success(NewNothingConstant());
else
CSharpToEzrObject(output, result);
CSharpToEzrObject(output, SharpMember.ReturnType, result);
}
catch (Exception error)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public override void Execute(Reference[] arguments, Interpreter interpreter, Run
}

object? value = SharpMember.GetValue(Instance);
CSharpToEzrObject(value, result);
CSharpToEzrObject(value, SharpMember.FieldType, result);
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,16 @@ public override void Execute(Reference[] arguments, Interpreter interpreter, Run
return;
}

object? value = SharpMember.GetValue(Instance);
CSharpToEzrObject(value, result);
try
{
object? value = SharpMember.GetValue(Instance);
CSharpToEzrObject(value, SharpMember.PropertyType, result);
}
catch (Exception error)
{
result.Failure(new EzrWrapperExecutionError(error.Message, Context, StartPosition, EndPosition));
return;
}
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/Runtime/Types/Collections/EzrDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public EzrDictionary(RuntimeEzrObjectDictionary value, Context parentContext, Po
Value = value;

Context.Set(null, "length", ReferencePool.Get(new EzrSharpCompatibilityProperty(GetMemberInfo<PropertyInfo, RuntimeEzrObjectDictionary>(nameof(Value.Count))!, Value, Context, StartPosition, EndPosition), AccessMod.Constant));
Context.Set(null, "remove_by_hash", ReferencePool.Get(new EzrSharpCompatibilityFunction(GetMemberInfo<MethodInfo, RuntimeEzrObjectDictionary>(nameof(Value.RemoveHash))!, Value, Context, StartPosition, EndPosition), AccessMod.Constant));
Context.Set(null, "remove_by_hash", ReferencePool.Get(new EzrSharpCompatibilityFunction(Value.RemoveHash, Context, StartPosition, EndPosition), AccessMod.Constant));
Context.Set(null, "has_key", ReferencePool.Get(new EzrSharpSourceFunctionWrapper(DictionaryExists, Context, StartPosition, EndPosition), AccessMod.Constant));
}

Expand Down
7 changes: 2 additions & 5 deletions src/Shell/EzrShell.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using EzrSquared;
using EzrSquared.Executor;
using EzrSquared.Runtime.Types.Collections;
using System;
using System.IO;
using System.Text;
Expand Down Expand Up @@ -141,14 +140,12 @@ private static bool ExecuteCode(string script)
return false;
}

string outputText = result.Result is EzrArray array && array.Value.Length == 1
? array.Value[0].ToString(CodeExecutor.Interpreter.RuntimeResult)
: result.Result!.ToString(CodeExecutor.Interpreter.RuntimeResult);

string outputText = result.Result!.ToString(CodeExecutor.Interpreter.RuntimeResult);
if (CodeExecutor.Interpreter.RuntimeResult.Error is not null)
EzrShellConsoleHelper.ShowError(CodeExecutor.Interpreter.RuntimeResult.Error.ToPureString(CodeExecutor.Interpreter.RuntimeResult));
else
EzrShellConsoleHelper.ShowOutput(outputText);

return true;
}
}

0 comments on commit 68d032c

Please sign in to comment.