Skip to content

Commit

Permalink
Added slightly jank implementation of EPAs for source wrappers.
Browse files Browse the repository at this point in the history
  • Loading branch information
Uralstech committed Oct 31, 2024
1 parent c4243a4 commit dca483a
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 40 deletions.
63 changes: 48 additions & 15 deletions src/Runtime/Types/CSharpWrappers/Builtins/EzrBuiltinFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using EzrSquared.Runtime.Types.Core.Text;
using EzrSquared.Runtime.WrapperAttributes;
using System;
using System.Collections.Generic;
using System.Text;

namespace EzrSquared.Runtime.Types.CSharpWrappers.Builtins;

Expand All @@ -19,50 +21,81 @@ public static class EzrBuiltinFunctions
/// ezr² parameters:
/// <list type="table">
/// <item>
/// <term>message</term>
/// <description>(<see cref="IEzrObject"/>) The message to display on the console.</description>
/// <term>Extra Positional Arguments</term>
/// <description>(<see cref="List{T}"/> of <see cref="Reference"/>s) The message(s) to display on the console.</description>
/// </item>
/// <item>
/// <term>line_end</term>
/// <description>(Optional, <see cref="EzrString"/>, <see cref="EzrCharacterList"/>, <see cref="EzrCharacter"/>) Line end character(s) to use instead of \n.</description>
/// <description>(Optional, <see cref="IEzrString"/>) Line end character(s) to use instead of \n.</description>
/// </item>
/// <item>
/// <term>separator</term>
/// <description>(Optional, <see cref="IEzrString"/>) separator to separate each message to be printed.</description>
/// </item>
/// </list>
///
/// ezr² return type:
/// <see cref="EzrNothing"/>
/// <br/>
/// ezr² errors:
/// <see cref="EzrUnexpectedTypeError"/> if "line_end" is not one of the specified types.
/// <see cref="MissingFieldException"/> if no messages provided.<br/>
/// <see cref="EzrUnexpectedTypeError"/> if "line_end" is not one of the specified types.<br/>
/// <see cref="EzrUnexpectedTypeError"/> if "separator" is not one of the specified types.
/// </remarks>
/// <param name="arguments">The constructor arguments.</param>
[SharpMethodWrapper("show", RequiredParameters = ["message"], OptionalParameters = ["line_end"])]
[SharpMethodWrapper("show", HasExtraPositionalArguments = true, OptionalParameters = ["line_end", "separator"])]
public static void Show(SharpMethodParameters arguments)
{
RuntimeResult result = arguments.Result;
Reference messageReference = arguments.ArgumentReferences["message"];
List<Reference> messageReferences = arguments.ExtraPositionalArgumentReferences!;

string message = messageReference.Object.ToPureString(result);
if (result.ShouldReturn)
if (messageReferences.Count == 0)
{
result.Failure(new EzrMissingRequiredArgumentError("At least one message must be provided!", arguments.ExecutionContext, arguments.StartPosition, arguments.EndPosition));
return;
}

string separator = string.Empty;
if (arguments.ArgumentReferences.TryGetValue("separator", out Reference? separatorReference))
{
IEzrObject separatorObject = separatorReference.Object;
if (separatorObject is IEzrString separatorString)
separator = separatorString.StringValue;
else
{
result.Failure(new EzrUnexpectedTypeError($"Expected separator of type string, character or character list, but got object of type \"{separatorObject.TypeName}\"", arguments.ExecutionContext, separatorObject.StartPosition, separatorObject.EndPosition));
return;
}
}

StringBuilder messageBuilder = new();
int messagesCount = messageReferences.Count;

for (int i = 0; i < messagesCount; i++)
{
string messagePart = messageReferences[i].Object.ToPureString(result);
if (result.ShouldReturn)
return;

messageBuilder.Append(messagePart);
if (i < messagesCount - 1)
messageBuilder.Append(separator);
}

string lineEnd = Environment.NewLine;
if (arguments.ArgumentReferences.TryGetValue("line_end", out Reference? lineEndReference))
{
IEzrObject lineEndObject = lineEndReference.Object;
if (lineEndObject is EzrString lineEndString)
lineEnd = lineEndString.Value;
else if (lineEndObject is EzrCharacter lineEndCharacter)
lineEnd = lineEndCharacter.Value.ToString();
else if (lineEndObject is EzrCharacterList lineEndCharacterList)
lineEnd = lineEndCharacterList.StringValue;
if (lineEndObject is IEzrString lineEndString)
lineEnd = lineEndString.StringValue;
else
{
result.Failure(new EzrUnexpectedTypeError($"Expected line ending of type string, character or character list, but got object of type \"{lineEndObject.TypeName}\"", arguments.ExecutionContext, lineEndObject.StartPosition, lineEndObject.EndPosition));
return;
}
}

Console.Write($"{message}{lineEnd}");
Console.Write(messageBuilder.Append(lineEnd).ToString());
result.Success(ReferencePool.Get(EzrConstants.Nothing, AccessMod.PrivateConstant));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using EzrSquared.Runtime.Types.Core.Errors;
global using WrapperArgumentPopulationResult = (System.Collections.Generic.Dictionary<string, EzrSquared.Runtime.Reference> Arguments, System.Collections.Generic.List<EzrSquared.Runtime.Reference>? ExtraPositionalArguments);

using EzrSquared.Runtime.Types.Core.Errors;
using EzrSquared.Util;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -28,7 +30,12 @@ public abstract class EzrSharpSourceExecutableWrapper(Context parentContext, Pos
/// <summary>
/// Does the executable accept extra keyword arguments?
/// </summary>
public bool HasKeywordArguments;
public bool HasExtraKeywordArguments;

/// <summary>
/// Does the executable accept extra positional arguments?
/// </summary>
public bool HasExtraPositionalArguments;

/// <summary>
/// Converts a string from PascalCase to lowecase plain text, seperated by spaces.
Expand Down Expand Up @@ -58,13 +65,21 @@ internal protected static string PascalCaseToLowerCasePlainText(string text)
/// <param name="arguments">The array of arguments.</param>
/// <param name="result">Runtime result for carrying any errors.</param>
/// <returns>The dictionary of all the arguments.</returns>
protected internal Dictionary<string, Reference> CheckAndPopulateArguments(Reference[] arguments, RuntimeResult result)
protected internal WrapperArgumentPopulationResult CheckAndPopulateArguments(Reference[] arguments, RuntimeResult result)
{
int totalRequiredArguments = 0;
foreach ((string Name, bool IsRequired) in Parameters)
{
if (IsRequired)
totalRequiredArguments++;
}

int calculatedParameterIndex = 0;
int requiredKeywordArguments = 0;
int flaggedRequiredArguments = -1;

Dictionary<string, Reference> argumentReferences = new(arguments.Length);
List<Reference>? extraPositionalArgumentReferences = HasExtraPositionalArguments ? new() : null;

for (int i = 0; i < arguments.Length; i++)
{
Expand All @@ -78,35 +93,30 @@ protected internal Dictionary<string, Reference> CheckAndPopulateArguments(Refer
if (argumentReferences.ContainsKey(name))
{
result.Failure(new EzrIllegalOperationError($"Cannot override already defined argument \"{name}\"!", _executionContext, reference.Object.StartPosition, reference.Object.EndPosition));
return argumentReferences;
return (argumentReferences, extraPositionalArgumentReferences);
}

bool found = Array.Find(Parameters, (v) =>
{
parameterIndex++;
return v.Name == name;
}) != default;

if (found)
parameterIndex = Array.FindIndex(Parameters, (param) => param.Name == name);
if (parameterIndex > -1)
{
argumentReferences.Add(name, reference);
requiredKeywordArguments++;
}
else if (HasKeywordArguments)
else if (HasExtraKeywordArguments)
{
argumentReferences.Add(name, reference);
parameterIndex = -1;
}
else
{
result.Failure(new EzrUnexpectedArgumentError($"Did not expect argument \"{name}\"!", _executionContext, reference.Object.StartPosition, reference.Object.EndPosition));
return argumentReferences;
return (argumentReferences, extraPositionalArgumentReferences);
}
}
else
{
IndexCheck:
if (Parameters.Length <= calculatedParameterIndex)
if (Parameters.Length <= calculatedParameterIndex && !HasExtraPositionalArguments)
{
result.Failure(new EzrUnexpectedArgumentError(
requiredKeywordArguments > 0
Expand All @@ -115,6 +125,11 @@ protected internal Dictionary<string, Reference> CheckAndPopulateArguments(Refer
_executionContext, StartPosition, EndPosition));
break;
}
else if (totalRequiredArguments <= calculatedParameterIndex && HasExtraPositionalArguments)
{
extraPositionalArgumentReferences!.Add(reference);
continue;
}

string argumentName = Parameters[calculatedParameterIndex].Name;
if (!argumentReferences.ContainsKey(argumentName))
Expand Down Expand Up @@ -145,11 +160,11 @@ protected internal Dictionary<string, Reference> CheckAndPopulateArguments(Refer
if (Parameters[i].IsRequired && (flaggedRequiredArguments < 0 || (flaggedRequiredArguments & parameterFlag) != parameterFlag))
{
result.Failure(new EzrMissingRequiredArgumentError($"Expected required argument \"{Parameters[i].Name}\"!", _executionContext, StartPosition, EndPosition));
return argumentReferences;
return (argumentReferences, extraPositionalArgumentReferences);
}
}

return argumentReferences;
return (argumentReferences, extraPositionalArgumentReferences);
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public EzrSharpSourceFieldWrapper(FieldInfo fieldInfo, object? instance, Context
/// <inheritdoc/>
public override void Execute(Reference[] arguments, Interpreter interpreter, RuntimeResult result)
{
Dictionary<string, Reference> argumentReferences = CheckAndPopulateArguments(arguments, result);
Dictionary<string, Reference> argumentReferences = CheckAndPopulateArguments(arguments, result).Arguments;
if (result.ShouldReturn)
return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,21 @@ private void AddParameters(SharpMethodWrapperAttribute attribute)
for (int j = 0; j < attribute.OptionalParameters.Length; j++)
Parameters[j + requiredParameters] = new(attribute.OptionalParameters[j], false);

HasKeywordArguments = attribute.HasKeywordArguments;
HasExtraKeywordArguments = attribute.HasExtraKeywordArguments;
HasExtraPositionalArguments = attribute.HasExtraPositionalArguments;
}

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

SharpFunction.Invoke(new SharpMethodParameters
(
argumentReferences,
argumentReferences.Arguments,
argumentReferences.ExtraPositionalArguments,
_executionContext,
CreationContext,
Context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public EzrSharpSourcePropertyWrapper(PropertyInfo propertyInfo, object? instance
/// <inheritdoc/>
public override void Execute(Reference[] arguments, Interpreter interpreter, RuntimeResult result)
{
Dictionary<string, Reference> argumentReferences = CheckAndPopulateArguments(arguments, result);
Dictionary<string, Reference> argumentReferences = CheckAndPopulateArguments(arguments, result).Arguments;
if (result.ShouldReturn)
return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ public EzrSharpSourceTypeWrapper(
Parameters[j + requiredParameters] = new(constructor.Value.Attribute.OptionalParameters[j], false);

// Set variables.
HasKeywordArguments = constructor.Value.Attribute.HasKeywordArguments;
HasExtraKeywordArguments = constructor.Value.Attribute.HasExtraKeywordArguments;
HasExtraPositionalArguments = constructor.Value.Attribute.HasExtraPositionalArguments;
Constructor = constructor.Value.Info;

// Get static methods to wrap.
Expand Down Expand Up @@ -238,15 +239,16 @@ public EzrSharpSourceTypeWrapper(
/// <inheritdoc/>
public override void Execute(Reference[] arguments, Interpreter interpreter, RuntimeResult result)
{
Dictionary<string, Reference> argumentReferences = CheckAndPopulateArguments(arguments, result);
WrapperArgumentPopulationResult argumentReferences = CheckAndPopulateArguments(arguments, result);
if (result.ShouldReturn)
return;

IEzrObject newObject = (IEzrObject)Constructor.Invoke(
[
new SharpMethodParameters
(
argumentReferences,
argumentReferences.Arguments,
argumentReferences.ExtraPositionalArguments,
_executionContext,
CreationContext,
Context,
Expand Down
7 changes: 7 additions & 0 deletions src/Runtime/WrapperAttributes/SharpMethodParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace EzrSquared.Runtime.WrapperAttributes;
/// Class for the paramters of a wrapped C# method.
/// </summary>
/// <param name="argumentReferences">See <see cref="ArgumentReferences"/>.</param>
/// <param name="extraPositionalArgumentReferences">See <see cref="ExtraPositionalArgumentReferences"/>.</param>
/// <param name="executionContext">See <see cref="ExecutionContext"/>.</param>
/// <param name="creationContext">See <see cref="CreationContext"/>.</param>
/// <param name="methodContext">See <see cref="MethodContext"/>.</param>
Expand All @@ -15,6 +16,7 @@ namespace EzrSquared.Runtime.WrapperAttributes;
/// <param name="result">See <see cref="Result"/>.</param>
public class SharpMethodParameters(
Dictionary<string, Reference> argumentReferences,
List<Reference>? extraPositionalArgumentReferences,
Context executionContext,
Context creationContext,
Context methodContext,
Expand All @@ -28,6 +30,11 @@ public class SharpMethodParameters(
/// </summary>
public Dictionary<string, Reference> ArgumentReferences = argumentReferences;

/// <summary>
/// The references to optional extra positional ezr² arguments.
/// </summary>
public List<Reference>? ExtraPositionalArgumentReferences = extraPositionalArgumentReferences;

/// <summary>
/// The context under which the method is being executed.
/// </summary>
Expand Down
7 changes: 6 additions & 1 deletion src/Runtime/WrapperAttributes/SharpMethodWrapperAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ public class SharpMethodWrapperAttribute : Attribute
/// <summary>
/// Does this method support additional keyword arguments (kwargs)?
/// </summary>
public bool HasKeywordArguments;
public bool HasExtraKeywordArguments;

/// <summary>
/// Does this method support additional positional arguments (args)?
/// </summary>
public bool HasExtraPositionalArguments;

/// <summary>
/// Creates a new instance of <see cref="SharpMethodWrapperAttribute"/> with a name.
Expand Down

0 comments on commit dca483a

Please sign in to comment.