diff --git a/src/Runtime/Types/CSharpWrappers/Builtins/EzrBuiltinFunctions.cs b/src/Runtime/Types/CSharpWrappers/Builtins/EzrBuiltinFunctions.cs index 8af0c6e..a7bef0a 100644 --- a/src/Runtime/Types/CSharpWrappers/Builtins/EzrBuiltinFunctions.cs +++ b/src/Runtime/Types/CSharpWrappers/Builtins/EzrBuiltinFunctions.cs @@ -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; @@ -19,12 +21,16 @@ public static class EzrBuiltinFunctions /// ezr² parameters: /// /// - /// message - /// () The message to display on the console. + /// Extra Positional Arguments + /// ( of s) The message(s) to display on the console. /// /// /// line_end - /// (Optional, , , ) Line end character(s) to use instead of \n. + /// (Optional, ) Line end character(s) to use instead of \n. + /// + /// + /// separator + /// (Optional, ) separator to separate each message to be printed. /// /// /// @@ -32,29 +38,56 @@ public static class EzrBuiltinFunctions /// ///
/// ezr² errors: - /// if "line_end" is not one of the specified types. + /// if no messages provided.
+ /// if "line_end" is not one of the specified types.
+ /// if "separator" is not one of the specified types. /// /// The constructor arguments. - [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 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)); @@ -62,7 +95,7 @@ public static void Show(SharpMethodParameters arguments) } } - Console.Write($"{message}{lineEnd}"); + Console.Write(messageBuilder.Append(lineEnd).ToString()); result.Success(ReferencePool.Get(EzrConstants.Nothing, AccessMod.PrivateConstant)); } diff --git a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceExecutableWrapper.cs b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceExecutableWrapper.cs index d38d43d..dffa865 100644 --- a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceExecutableWrapper.cs +++ b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceExecutableWrapper.cs @@ -1,4 +1,6 @@ -using EzrSquared.Runtime.Types.Core.Errors; +global using WrapperArgumentPopulationResult = (System.Collections.Generic.Dictionary Arguments, System.Collections.Generic.List? ExtraPositionalArguments); + +using EzrSquared.Runtime.Types.Core.Errors; using EzrSquared.Util; using System; using System.Collections.Generic; @@ -28,7 +30,12 @@ public abstract class EzrSharpSourceExecutableWrapper(Context parentContext, Pos /// /// Does the executable accept extra keyword arguments? /// - public bool HasKeywordArguments; + public bool HasExtraKeywordArguments; + + /// + /// Does the executable accept extra positional arguments? + /// + public bool HasExtraPositionalArguments; /// /// Converts a string from PascalCase to lowecase plain text, seperated by spaces. @@ -58,13 +65,21 @@ internal protected static string PascalCaseToLowerCasePlainText(string text) /// The array of arguments. /// Runtime result for carrying any errors. /// The dictionary of all the arguments. - protected internal Dictionary 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 argumentReferences = new(arguments.Length); + List? extraPositionalArgumentReferences = HasExtraPositionalArguments ? new() : null; for (int i = 0; i < arguments.Length; i++) { @@ -78,21 +93,16 @@ protected internal Dictionary 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; @@ -100,13 +110,13 @@ protected internal Dictionary CheckAndPopulateArguments(Refer 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 @@ -115,6 +125,11 @@ protected internal Dictionary CheckAndPopulateArguments(Refer _executionContext, StartPosition, EndPosition)); break; } + else if (totalRequiredArguments <= calculatedParameterIndex && HasExtraPositionalArguments) + { + extraPositionalArgumentReferences!.Add(reference); + continue; + } string argumentName = Parameters[calculatedParameterIndex].Name; if (!argumentReferences.ContainsKey(argumentName)) @@ -145,11 +160,11 @@ protected internal Dictionary 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); } /// diff --git a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceFieldWrapper.cs b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceFieldWrapper.cs index 6cf23f2..3b0deb7 100644 --- a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceFieldWrapper.cs +++ b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceFieldWrapper.cs @@ -79,7 +79,7 @@ public EzrSharpSourceFieldWrapper(FieldInfo fieldInfo, object? instance, Context /// public override void Execute(Reference[] arguments, Interpreter interpreter, RuntimeResult result) { - Dictionary argumentReferences = CheckAndPopulateArguments(arguments, result); + Dictionary argumentReferences = CheckAndPopulateArguments(arguments, result).Arguments; if (result.ShouldReturn) return; diff --git a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceFunctionWrapper.cs b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceFunctionWrapper.cs index 01a7548..9da1406 100644 --- a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceFunctionWrapper.cs +++ b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceFunctionWrapper.cs @@ -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; } /// public override void Execute(Reference[] arguments, Interpreter interpreter, RuntimeResult result) { - Dictionary 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, diff --git a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourcePropertyWrapper.cs b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourcePropertyWrapper.cs index 837429a..ae90a33 100644 --- a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourcePropertyWrapper.cs +++ b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourcePropertyWrapper.cs @@ -74,7 +74,7 @@ public EzrSharpSourcePropertyWrapper(PropertyInfo propertyInfo, object? instance /// public override void Execute(Reference[] arguments, Interpreter interpreter, RuntimeResult result) { - Dictionary argumentReferences = CheckAndPopulateArguments(arguments, result); + Dictionary argumentReferences = CheckAndPopulateArguments(arguments, result).Arguments; if (result.ShouldReturn) return; diff --git a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceTypeWrapper.cs b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceTypeWrapper.cs index 1f0fd7d..af90862 100644 --- a/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceTypeWrapper.cs +++ b/src/Runtime/Types/CSharpWrappers/SourceWrappers/EzrSharpSourceTypeWrapper.cs @@ -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. @@ -238,7 +239,7 @@ public EzrSharpSourceTypeWrapper( /// public override void Execute(Reference[] arguments, Interpreter interpreter, RuntimeResult result) { - Dictionary argumentReferences = CheckAndPopulateArguments(arguments, result); + WrapperArgumentPopulationResult argumentReferences = CheckAndPopulateArguments(arguments, result); if (result.ShouldReturn) return; @@ -246,7 +247,8 @@ public override void Execute(Reference[] arguments, Interpreter interpreter, Run [ new SharpMethodParameters ( - argumentReferences, + argumentReferences.Arguments, + argumentReferences.ExtraPositionalArguments, _executionContext, CreationContext, Context, diff --git a/src/Runtime/WrapperAttributes/SharpMethodParameters.cs b/src/Runtime/WrapperAttributes/SharpMethodParameters.cs index 0d2e28d..07e48be 100644 --- a/src/Runtime/WrapperAttributes/SharpMethodParameters.cs +++ b/src/Runtime/WrapperAttributes/SharpMethodParameters.cs @@ -6,6 +6,7 @@ namespace EzrSquared.Runtime.WrapperAttributes; /// Class for the paramters of a wrapped C# method. /// /// See . +/// See . /// See . /// See . /// See . @@ -15,6 +16,7 @@ namespace EzrSquared.Runtime.WrapperAttributes; /// See . public class SharpMethodParameters( Dictionary argumentReferences, + List? extraPositionalArgumentReferences, Context executionContext, Context creationContext, Context methodContext, @@ -28,6 +30,11 @@ public class SharpMethodParameters( /// public Dictionary ArgumentReferences = argumentReferences; + /// + /// The references to optional extra positional ezr² arguments. + /// + public List? ExtraPositionalArgumentReferences = extraPositionalArgumentReferences; + /// /// The context under which the method is being executed. /// diff --git a/src/Runtime/WrapperAttributes/SharpMethodWrapperAttribute.cs b/src/Runtime/WrapperAttributes/SharpMethodWrapperAttribute.cs index 0d33e1a..79898ff 100644 --- a/src/Runtime/WrapperAttributes/SharpMethodWrapperAttribute.cs +++ b/src/Runtime/WrapperAttributes/SharpMethodWrapperAttribute.cs @@ -29,7 +29,12 @@ public class SharpMethodWrapperAttribute : Attribute /// /// Does this method support additional keyword arguments (kwargs)? /// - public bool HasKeywordArguments; + public bool HasExtraKeywordArguments; + + /// + /// Does this method support additional positional arguments (args)? + /// + public bool HasExtraPositionalArguments; /// /// Creates a new instance of with a name.