add native extensions

This commit is contained in:
Qrakhen 2025-11-09 10:00:32 +01:00
parent 0e11be061b
commit 3013a338ed
5 changed files with 98 additions and 6 deletions

View File

@ -368,6 +368,25 @@ public class Runner : IDisposable
}
}
private bool InvokeNative(Value target, NativeExtension extension, int argumentCount)
{
_logger.Method(extension);
if (argumentCount != extension.Parameters.Length) {
Error($"{extension} expects {extension.Parameters.Length} arguments, received {argumentCount}");
return false;
}
var values = new Value[argumentCount];
for (int i = 0; i < argumentCount; i++) {
values[i] = Peek(-i - 1);
}
Stack.Decimate(Stack.Position - argumentCount - 1);
Value result = extension.Callback(target, values);
Push(result);
return true;
}
private bool Invoke(Context closure, int argumentCount)
{
_logger.Method();
@ -390,8 +409,12 @@ public class Runner : IDisposable
{
Value value = Peek(-argumentCount - 1);
if (!value.Is(T.Instance)) {
Error($"Can not call {methodName} on value of {value}");
return false;
NativeExtension? extension = NativeExtension.Get(value.Type, methodName);
if (extension == null) {
Error($"Could not invoke {methodName} on {value}, no method or extension by that name found for this type.");
return false;
}
return InvokeNative(value, extension, argumentCount);
}
Instance instance = value.Ptr.As<Instance>()!;

View File

@ -0,0 +1,52 @@
using Qrakhen.Qamp.Core.Collections;
using Qrakhen.Qamp.Core.Values.Objects;
namespace Qrakhen.Qamp.Core.Values;
public delegate Value ExtensionDelegate(Value target, Value[] parameters);
public class NativeExtension(
ValueType targetType,
string name,
ExtensionDelegate callback,
string[] parameters)
{
private static readonly Register<ValueType, Register<string, NativeExtension>> _register = new();
public readonly string Name = name;
public readonly ValueType TargetType = targetType;
public readonly ExtensionDelegate Callback = callback;
public readonly string[] Parameters = parameters.ToArray();
public static NativeExtension? Get(ValueType targetType, string name)
{
if (!_register.Has(targetType))
return null;
if (!_register[targetType].Has(name))
return null;
return _register[targetType][name];
}
public static void Register(ValueType targetType,
string name,
ExtensionDelegate callback,
params string[] parameters)
{
if (!_register.Has(targetType))
_register.Add(targetType, new());
if (_register[targetType].Has(name))
throw new QampException($"Extension {name} already exists for type {targetType}.");
_register[targetType].Add(name, new NativeExtension(targetType, name, callback, parameters));
}
static NativeExtension()
{
Register(ValueType.String, "Length", (v, p) => new Value((long)v.Ptr.As<Objects.String>()!.Value!.Length), []);
Register(ValueType.String, "IndexOf", (v, p) => new Value((long)v.Ptr.As<Objects.String>()!.Value!.IndexOf(p[0].Ptr.As<Objects.String>()!.Value!)), [ "needle" ]);
}
public override string ToString()
{
return $"{TargetType}.{Name}({string.Join(", ", Parameters)}) (NativeExtension)";
}
}

View File

@ -0,0 +1,11 @@
namespace Qrakhen.Qamp.Core.Values.Objects;
public class Extension(ValueType targetType, Function function) : Context(function)
{
public ValueType TargetType = targetType;
public override string ToString()
{
return $"{TargetType}.{Function.Name}() (Extension)";
}
}

View File

@ -0,0 +1,9 @@
namespace Qrakhen.Qamp.Core.Values.Objects;
public class Native(string name) : Class(name)
{
public override string ToString()
{
return $"{Name} (Native)";
}
}

View File

@ -4,8 +4,5 @@ namespace Qrakhen.Qamp.Core.Values;
internal static class ValueExtensions
{
public static Value ExecuteOperation(this Value value, Value other, OpCode operation)
{
return new Value();
}
}