Skip to content

Commit

Permalink
Merge pull request #33 from active-logic/Fast-Bindings
Browse files Browse the repository at this point in the history
Cache function bindings
  • Loading branch information
eelstork committed Sep 26, 2022
2 parents da631b9 + 14897e1 commit e521ffa
Show file tree
Hide file tree
Showing 15 changed files with 188 additions and 68 deletions.
23 changes: 20 additions & 3 deletions Elk/Runtime/Basic/Graph/FuncDef.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using UnityEngine;
using System.Text;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -9,7 +10,9 @@ public class FuncDef{
public readonly string[] parameters;
public object body;

public FuncDef(string name, IEnumerable<string> @params, object body){
public FuncDef(
string name, IEnumerable<string> @params, object body
){
this.name = name;
this.parameters = @params?.ToArray() ?? null;
this.body = body;
Expand All @@ -18,8 +21,22 @@ public class FuncDef{
public int paramCount => parameters?.Length ?? 0;

public bool MatchesSignatureOf(FuncDef that)
=> that.name == this.name
&& that.paramCount == this.paramCount;
=> Matches(that.name, that.paramCount);

public bool Matches(string name, int paramCount)
=> name == this.name && paramCount == this.paramCount;

bool FuncMatches(FuncDef fdef, string name, int argLength){
if(fdef == null){
Debug.LogError("fdef is null");
return false;
}
if(fdef.name == null){
Debug.LogError("fdef name is null");
return false;
}
return fdef.name == name && fdef.paramCount == argLength;
}

override public string ToString(){
if(parameters == null) return name + " → {" + body + "}";
Expand Down
6 changes: 6 additions & 0 deletions Elk/Runtime/Basic/Graph/Invocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ public class Invocation : Expression{

public readonly string name;
public readonly object[] arguments;
public readonly object[] values;
public object binding;

public Invocation(string name, IEnumerable<object> arguments=null){
this.name = name;
this.arguments = arguments?.ToArray() ?? null;
this.values = this.arguments != null
? new object[this.arguments.Length] : new object[]{};
}

public int argumentCount => arguments?.Length ?? 0;

override public string ToString(){
if(arguments == null) return $"{name}()";
var @out = new StringBuilder();
Expand Down
23 changes: 23 additions & 0 deletions Elk/Runtime/Basic/Runner-Details/InternalFunctionBinding.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Elk.Basic.Graph;

namespace Elk.Basic.Runtime{
public class InternalFunctionBinding : InvocationBinding{

protected object target; // NOTE unused; for instance func
protected FuncDef function;

public InternalFunctionBinding(object target, FuncDef function){
this.target = target;
this.function = function;
}

override protected object Invoke(
object[] values, Runner<Context> ρ, Context cx
){
cx.Push(function.parameters, values);
var @out = ρ.Eval(function.body, cx);
cx.Pop();
return @out;
}

}}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions Elk/Runtime/Basic/Runner-Details/InvocationBinding.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Reflection;
using Elk.Basic.Graph;
using Elk.Util;

namespace Elk.Basic.Runtime{
public abstract class InvocationBinding{

public object Eval(Invocation ι, Runner ρ, Context cx)
=> Invoke(ι.values, ρ, cx);

protected abstract object Invoke(
object[] values, Runner<Context> ρ, Context cx
);

}}
11 changes: 11 additions & 0 deletions Elk/Runtime/Basic/Runner-Details/InvocationBinding.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 9 additions & 57 deletions Elk/Runtime/Basic/Runner-Details/InvocationEval.cs
Original file line number Diff line number Diff line change
@@ -1,75 +1,27 @@
using Array = System.Array;
using Ex = System.Exception;
using UnityEngine;
using Elk.Util;
using Elk.Bindings.CSharp;
using Elk.Basic.Graph;
using Elk.Basic.Runtime;

namespace Elk.Basic{
public class InvocationEval{

public object Eval(Invocation ι, Runner ρ, Context cx){
var values = EvalArgs(ι.arguments, ρ, cx);
cx.graph.Push(ι.name + values.NeatFormat());
var @out = DoEval(ι, values, ρ, cx);
ρ.EvalArgs(ι.arguments, @out: ι.values, cx);
cx.graph.Push(ι.name + ι.values.NeatFormat());
var @out = DoEval(ι, ρ, cx);
cx.graph.Pop(@out);
return @out;
}

public object DoEval(Invocation inv, object[] values,
Runner ρ, Context cx){
object @out;
if(Invoke(inv.name, inv.arguments, values, ρ, cx, out @out))
return @out;
return cx.externals.Invoke(inv.name, values);
}

public bool Invoke(string name, object[] args,
object[] values, Runner ρ,
Context cx, out object @out){
foreach(var module in cx.modules){
var fdef = Array.Find(
module,
x => FuncMatches(x, name, args?.Length ?? 0)
);
if(fdef != null){
@out = InvokeInternalFunction(fdef, values, ρ, cx);
return true;
}
public object DoEval(Invocation ι, Runner ρ, Context cx){
if(ι.binding == null){
ι.binding = cx.modules .Bind(ι, ρ, cx)
?? cx.externals.Bind(ι.name, ι.values);
}
@out = null;
return false;
}

bool FuncMatches(FuncDef fdef, string name, int argLength){
if(fdef == null){
Debug.LogError("fdef is null");
return false;
}
if(fdef.name == null){
Debug.LogError("fdef name is null");
return false;
}
return fdef.name == name && fdef.paramCount == argLength;
}

object InvokeInternalFunction(FuncDef func,
object[] values,
Runner ρ,
Context cx){
cx.Push(func.parameters, values);
var @out = ρ.Eval(func.body, cx);
cx.Pop();
return @out;
}

object[] EvalArgs(object[] args, Runner ρ, Context cx){
var len = args?.Length ?? 0;
var @out = new object[len];
for(int i = 0; i < len; i++){
@out[i] = ρ.Eval(args[i], cx);
}
return @out;
return ((InvocationBinding)ι.binding).Eval(ι, ρ, cx);
}

}}
23 changes: 23 additions & 0 deletions Elk/Runtime/Basic/Runner-Details/Modules.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Generic;
using Array = System.Array;
using Elk.Basic.Graph;

namespace Elk.Basic.Runtime{
public static class Modules{

public static InvocationBinding Bind(
this IEnumerable<FuncDef[]> self, Invocation ι,
Runner ρ, Context cx
){
foreach(var module in self){
var fdef = Array.Find(
module, x => x.Matches(ι.name, ι.argumentCount)
);
if(fdef != null){
return new InternalFunctionBinding(null, fdef);
}
}
return null;
}

}}
11 changes: 11 additions & 0 deletions Elk/Runtime/Basic/Runner-Details/Modules.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion Elk/Runtime/Basic/Runner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Elk.Basic.Runtime;

namespace Elk.Basic{
public partial class Runner : Elk.Runner<Context>{
public class Runner : Elk.Runner<Context>{

public UnaEval una;
public BinEval bin;
Expand Down Expand Up @@ -32,6 +32,13 @@ public partial class Runner : Elk.Runner<Context>{
}
}

public void EvalArgs(object[] args, object[] @out, Context cx){
var len = args?.Length ?? 0;
for(int i = 0; i < len; i++){
@out[i] = this.Eval(args[i], cx);
}
}

static bool IsLiteral(object arg) => arg is bool || arg is int;

}}
22 changes: 22 additions & 0 deletions Elk/Runtime/Extern/ExternalFunctionBinding.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Reflection;
using Elk.Util;
using Elk.Basic;
using Elk.Basic.Graph;
using Elk.Basic.Runtime;

namespace Elk.Bindings.CSharp{
public class ExternalFunctionBinding : InvocationBinding{

readonly object target;
readonly MethodInfo method;

public ExternalFunctionBinding(object target, MethodInfo method){
this.target = target;
this.method = method;
}

override protected object Invoke(
object[] values, Runner<Context> ρ, Context cx
) => method.Invoke(target, values);

}}
11 changes: 11 additions & 0 deletions Elk/Runtime/Extern/ExternalFunctionBinding.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
using System.Collections;
using System.Reflection;
using Ex = System.Exception;
using Elk.Basic.Runtime;

namespace Elk.Bindings.CSharp{
public static class IEnumerableExt{
public static class Externals{

public static object Invoke(
public static InvocationBinding Bind(
this IEnumerable cx, string func, object[] args
){
foreach(var e in cx)
if(e.Invoke(func, args, out object @out))
return @out;
throw new Ex($"Not found ({func})");
foreach(var obj in cx){
var method = obj.Bind(func, args);
if(method != null){
return new ExternalFunctionBinding(obj, method);
}
}
return null;
}

public static object Eval(
Expand Down
11 changes: 11 additions & 0 deletions Elk/Runtime/Extern/Externals.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Elk/Runtime/Extern/ObjectExt.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
using System.Reflection;

namespace Elk.Bindings.CSharp{
public static class ObjectExt{

public static MethodInfo Bind(
this object self, string func, object[] args
) => self.GetType().GetMethod(func, args);

public static bool Invoke(
this object self, string func, object[] args, out object @out
){
Expand Down

0 comments on commit e521ffa

Please sign in to comment.