- using System;
- using System.Collections.Generic;
- namespace Ink.Runtime
- {
- public class NativeFunctionCall : Runtime.Object
- {
- public const string Add = "+";
- public const string Subtract = "-";
- public const string Divide = "/";
- public const string Multiply = "*";
- public const string Mod = "%";
- public const string Negate = "_";
- public const string Equal = "==";
- public const string Greater = ">";
- public const string Less = "<";
- public const string GreaterThanOrEquals = ">=";
- public const string LessThanOrEquals = "<=";
- public const string NotEquals = "!=";
- public const string Not = "!";
- public const string And = "&&";
- public const string Or = "||";
- public const string Min = "MIN";
- public const string Max = "MAX";
- public const string Pow = "POW";
- public const string Floor = "FLOOR";
- public const string Ceiling = "CEILING";
- public const string Int = "INT";
- public const string Float = "FLOAT";
- public const string Has = "?";
- public const string Hasnt = "!?";
- public const string Intersect = "^";
- public const string ListMin = "LIST_MIN";
- public const string ListMax = "LIST_MAX";
- public const string All = "LIST_ALL";
- public const string Count = "LIST_COUNT";
- public const string ValueOfList = "LIST_VALUE";
- public const string Invert = "LIST_INVERT";
- public static NativeFunctionCall CallWithName(string functionName)
- {
- return new NativeFunctionCall (functionName);
- }
- public static bool CallExistsWithName(string functionName)
- {
- GenerateNativeFunctionsIfNecessary ();
- return _nativeFunctions.ContainsKey (functionName);
- }
-
- public string name {
- get {
- return _name;
- }
- protected set {
- _name = value;
- if( !_isPrototype )
- _prototype = _nativeFunctions [_name];
- }
- }
- string _name;
- public int numberOfParameters {
- get {
- if (_prototype) {
- return _prototype.numberOfParameters;
- } else {
- return _numberOfParameters;
- }
- }
- protected set {
- _numberOfParameters = value;
- }
- }
- int _numberOfParameters;
- public Runtime.Object Call(List<Runtime.Object> parameters)
- {
- if (_prototype) {
- return _prototype.Call(parameters);
- }
- if (numberOfParameters != parameters.Count) {
- throw new System.Exception ("Unexpected number of parameters");
- }
- bool hasList = false;
- foreach (var p in parameters) {
- if (p is Void)
- throw new StoryException ("Attempting to perform operation on a void value. Did you forget to 'return' a value from a function you called here?");
- if (p is ListValue)
- hasList = true;
- }
-
- if( parameters.Count == 2 && hasList )
- return CallBinaryListOperation (parameters);
- var coercedParams = CoerceValuesToSingleType (parameters);
- ValueType coercedType = coercedParams[0].valueType;
- if (coercedType == ValueType.Int) {
- return Call<int> (coercedParams);
- } else if (coercedType == ValueType.Float) {
- return Call<float> (coercedParams);
- } else if (coercedType == ValueType.String) {
- return Call<string> (coercedParams);
- } else if (coercedType == ValueType.DivertTarget) {
- return Call<Path> (coercedParams);
- } else if (coercedType == ValueType.List) {
- return Call<InkList> (coercedParams);
- }
- return null;
- }
- Value Call<T>(List<Value> parametersOfSingleType)
- {
- Value param1 = (Value) parametersOfSingleType [0];
- ValueType valType = param1.valueType;
- var val1 = (Value<T>)param1;
- int paramCount = parametersOfSingleType.Count;
- if (paramCount == 2 || paramCount == 1) {
- object opForTypeObj = null;
- if (!_operationFuncs.TryGetValue (valType, out opForTypeObj)) {
- throw new StoryException ("Cannot perform operation '"+this.name+"' on "+valType);
- }
-
- if (paramCount == 2) {
- Value param2 = (Value) parametersOfSingleType [1];
- var val2 = (Value<T>)param2;
- var opForType = (BinaryOp<T>)opForTypeObj;
-
- object resultVal = opForType (val1.value, val2.value);
- return Value.Create (resultVal);
- }
-
- else {
- var opForType = (UnaryOp<T>)opForTypeObj;
- var resultVal = opForType (val1.value);
- return Value.Create (resultVal);
- }
- }
-
- else {
- throw new System.Exception ("Unexpected number of parameters to NativeFunctionCall: " + parametersOfSingleType.Count);
- }
- }
- Value CallBinaryListOperation (List<Runtime.Object> parameters)
- {
-
- if ((name == "+" || name == "-") && parameters [0] is ListValue && parameters [1] is IntValue)
- return CallListIncrementOperation (parameters);
- var v1 = parameters [0] as Value;
- var v2 = parameters [1] as Value;
-
- if ((name == "&&" || name == "||") && (v1.valueType != ValueType.List || v2.valueType != ValueType.List)) {
- var op = _operationFuncs [ValueType.Int] as BinaryOp<int>;
- var result = (bool)op (v1.isTruthy ? 1 : 0, v2.isTruthy ? 1 : 0);
- return new BoolValue (result);
- }
-
- if (v1.valueType == ValueType.List && v2.valueType == ValueType.List)
- return Call<InkList> (new List<Value> { v1, v2 });
- throw new StoryException ("Can not call use '" + name + "' operation on " + v1.valueType + " and " + v2.valueType);
- }
- Value CallListIncrementOperation (List<Runtime.Object> listIntParams)
- {
- var listVal = (ListValue)listIntParams [0];
- var intVal = (IntValue)listIntParams [1];
- var resultRawList = new InkList ();
- foreach (var listItemWithValue in listVal.value) {
- var listItem = listItemWithValue.Key;
- var listItemValue = listItemWithValue.Value;
-
- var intOp = (BinaryOp<int>)_operationFuncs [ValueType.Int];
-
- int targetInt = (int) intOp (listItemValue, intVal.value);
-
- ListDefinition itemOrigin = null;
- foreach (var origin in listVal.value.origins) {
- if (origin.name == listItem.originName) {
- itemOrigin = origin;
- break;
- }
- }
- if (itemOrigin != null) {
- InkListItem incrementedItem;
- if (itemOrigin.TryGetItemWithValue (targetInt, out incrementedItem))
- resultRawList.Add (incrementedItem, targetInt);
- }
- }
- return new ListValue (resultRawList);
- }
- List<Value> CoerceValuesToSingleType(List<Runtime.Object> parametersIn)
- {
- ValueType valType = ValueType.Int;
- ListValue specialCaseList = null;
-
-
-
-
- foreach (var obj in parametersIn) {
- var val = (Value)obj;
- if (val.valueType > valType) {
- valType = val.valueType;
- }
- if (val.valueType == ValueType.List) {
- specialCaseList = val as ListValue;
- }
- }
-
- var parametersOut = new List<Value> ();
-
-
-
- if (valType == ValueType.List) {
-
- foreach (Value val in parametersIn) {
- if (val.valueType == ValueType.List) {
- parametersOut.Add (val);
- } else if (val.valueType == ValueType.Int) {
- int intVal = (int)val.valueObject;
- var list = specialCaseList.value.originOfMaxItem;
- InkListItem item;
- if (list.TryGetItemWithValue (intVal, out item)) {
- var castedValue = new ListValue (item, intVal);
- parametersOut.Add (castedValue);
- } else
- throw new StoryException ("Could not find List item with the value " + intVal + " in " + list.name);
- } else
- throw new StoryException ("Cannot mix Lists and " + val.valueType + " values in this operation");
- }
-
- }
-
- else {
- foreach (Value val in parametersIn) {
- var castedValue = val.Cast (valType);
- parametersOut.Add (castedValue);
- }
- }
- return parametersOut;
- }
- public NativeFunctionCall(string name)
- {
- GenerateNativeFunctionsIfNecessary ();
- this.name = name;
- }
-
- public NativeFunctionCall() {
- GenerateNativeFunctionsIfNecessary ();
- }
-
- NativeFunctionCall (string name, int numberOfParameters)
- {
- _isPrototype = true;
- this.name = name;
- this.numberOfParameters = numberOfParameters;
- }
-
-
-
- static object Identity<T>(T t) {
- return t;
- }
- static void GenerateNativeFunctionsIfNecessary()
- {
- if (_nativeFunctions == null) {
- _nativeFunctions = new Dictionary<string, NativeFunctionCall> ();
-
-
-
-
-
-
-
-
- AddIntBinaryOp(Add, (x, y) => x + y);
- AddIntBinaryOp(Subtract, (x, y) => x - y);
- AddIntBinaryOp(Multiply, (x, y) => x * y);
- AddIntBinaryOp(Divide, (x, y) => x / y);
- AddIntBinaryOp(Mod, (x, y) => x % y);
- AddIntUnaryOp (Negate, x => -x);
- AddIntBinaryOp(Equal, (x, y) => x == y);
- AddIntBinaryOp(Greater, (x, y) => x > y);
- AddIntBinaryOp(Less, (x, y) => x < y);
- AddIntBinaryOp(GreaterThanOrEquals, (x, y) => x >= y);
- AddIntBinaryOp(LessThanOrEquals, (x, y) => x <= y);
- AddIntBinaryOp(NotEquals, (x, y) => x != y);
- AddIntUnaryOp (Not, x => x == 0);
- AddIntBinaryOp(And, (x, y) => x != 0 && y != 0);
- AddIntBinaryOp(Or, (x, y) => x != 0 || y != 0);
- AddIntBinaryOp(Max, (x, y) => Math.Max(x, y));
- AddIntBinaryOp(Min, (x, y) => Math.Min(x, y));
-
- AddIntBinaryOp (Pow, (x, y) => (float) Math.Pow(x, y));
- AddIntUnaryOp(Floor, Identity);
- AddIntUnaryOp(Ceiling, Identity);
- AddIntUnaryOp(Int, Identity);
- AddIntUnaryOp (Float, x => (float)x);
-
- AddFloatBinaryOp(Add, (x, y) => x + y);
- AddFloatBinaryOp(Subtract, (x, y) => x - y);
- AddFloatBinaryOp(Multiply, (x, y) => x * y);
- AddFloatBinaryOp(Divide, (x, y) => x / y);
- AddFloatBinaryOp(Mod, (x, y) => x % y);
- AddFloatUnaryOp (Negate, x => -x);
- AddFloatBinaryOp(Equal, (x, y) => x == y);
- AddFloatBinaryOp(Greater, (x, y) => x > y);
- AddFloatBinaryOp(Less, (x, y) => x < y);
- AddFloatBinaryOp(GreaterThanOrEquals, (x, y) => x >= y);
- AddFloatBinaryOp(LessThanOrEquals, (x, y) => x <= y);
- AddFloatBinaryOp(NotEquals, (x, y) => x != y);
- AddFloatUnaryOp (Not, x => (x == 0.0f));
- AddFloatBinaryOp(And, (x, y) => x != 0.0f && y != 0.0f);
- AddFloatBinaryOp(Or, (x, y) => x != 0.0f || y != 0.0f);
- AddFloatBinaryOp(Max, (x, y) => Math.Max(x, y));
- AddFloatBinaryOp(Min, (x, y) => Math.Min(x, y));
- AddFloatBinaryOp (Pow, (x, y) => (float)Math.Pow(x, y));
- AddFloatUnaryOp(Floor, x => (float)Math.Floor(x));
- AddFloatUnaryOp(Ceiling, x => (float)Math.Ceiling(x));
- AddFloatUnaryOp(Int, x => (int)x);
- AddFloatUnaryOp(Float, Identity);
-
- AddStringBinaryOp(Add, (x, y) => x + y);
- AddStringBinaryOp(Equal, (x, y) => x.Equals(y));
- AddStringBinaryOp (NotEquals, (x, y) => !x.Equals (y));
- AddStringBinaryOp (Has, (x, y) => x.Contains(y));
- AddStringBinaryOp (Hasnt, (x, y) => !x.Contains(y));
-
- AddListBinaryOp (Add, (x, y) => x.Union (y));
- AddListBinaryOp (Subtract, (x, y) => x.Without(y));
- AddListBinaryOp (Has, (x, y) => x.Contains (y));
- AddListBinaryOp (Hasnt, (x, y) => !x.Contains (y));
- AddListBinaryOp (Intersect, (x, y) => x.Intersect (y));
- AddListBinaryOp (Equal, (x, y) => x.Equals(y));
- AddListBinaryOp (Greater, (x, y) => x.GreaterThan(y));
- AddListBinaryOp (Less, (x, y) => x.LessThan(y));
- AddListBinaryOp (GreaterThanOrEquals, (x, y) => x.GreaterThanOrEquals(y));
- AddListBinaryOp (LessThanOrEquals, (x, y) => x.LessThanOrEquals(y));
- AddListBinaryOp (NotEquals, (x, y) => !x.Equals(y));
- AddListBinaryOp (And, (x, y) => x.Count > 0 && y.Count > 0);
- AddListBinaryOp (Or, (x, y) => x.Count > 0 || y.Count > 0);
- AddListUnaryOp (Not, x => x.Count == 0 ? (int)1 : (int)0);
-
-
- AddListUnaryOp (Invert, x => x.inverse);
- AddListUnaryOp (All, x => x.all);
- AddListUnaryOp (ListMin, (x) => x.MinAsList());
- AddListUnaryOp (ListMax, (x) => x.MaxAsList());
- AddListUnaryOp (Count, (x) => x.Count);
- AddListUnaryOp (ValueOfList, (x) => x.maxItem.Value);
-
- BinaryOp<Path> divertTargetsEqual = (Path d1, Path d2) => {
- return d1.Equals (d2);
- };
- BinaryOp<Path> divertTargetsNotEqual = (Path d1, Path d2) => {
- return !d1.Equals (d2);
- };
- AddOpToNativeFunc (Equal, 2, ValueType.DivertTarget, divertTargetsEqual);
- AddOpToNativeFunc (NotEquals, 2, ValueType.DivertTarget, divertTargetsNotEqual);
- }
- }
- void AddOpFuncForType(ValueType valType, object op)
- {
- if (_operationFuncs == null) {
- _operationFuncs = new Dictionary<ValueType, object> ();
- }
- _operationFuncs [valType] = op;
- }
- static void AddOpToNativeFunc(string name, int args, ValueType valType, object op)
- {
- NativeFunctionCall nativeFunc = null;
- if (!_nativeFunctions.TryGetValue (name, out nativeFunc)) {
- nativeFunc = new NativeFunctionCall (name, args);
- _nativeFunctions [name] = nativeFunc;
- }
- nativeFunc.AddOpFuncForType (valType, op);
- }
- static void AddIntBinaryOp(string name, BinaryOp<int> op)
- {
- AddOpToNativeFunc (name, 2, ValueType.Int, op);
- }
- static void AddIntUnaryOp(string name, UnaryOp<int> op)
- {
- AddOpToNativeFunc (name, 1, ValueType.Int, op);
- }
- static void AddFloatBinaryOp(string name, BinaryOp<float> op)
- {
- AddOpToNativeFunc (name, 2, ValueType.Float, op);
- }
- static void AddStringBinaryOp(string name, BinaryOp<string> op)
- {
- AddOpToNativeFunc (name, 2, ValueType.String, op);
- }
- static void AddListBinaryOp (string name, BinaryOp<InkList> op)
- {
- AddOpToNativeFunc (name, 2, ValueType.List, op);
- }
- static void AddListUnaryOp (string name, UnaryOp<InkList> op)
- {
- AddOpToNativeFunc (name, 1, ValueType.List, op);
- }
- static void AddFloatUnaryOp(string name, UnaryOp<float> op)
- {
- AddOpToNativeFunc (name, 1, ValueType.Float, op);
- }
- public override string ToString ()
- {
- return "Native '" + name + "'";
- }
- delegate object BinaryOp<T>(T left, T right);
- delegate object UnaryOp<T>(T val);
- NativeFunctionCall _prototype;
- bool _isPrototype;
-
- Dictionary<ValueType, object> _operationFuncs;
- static Dictionary<string, NativeFunctionCall> _nativeFunctions;
- }
- }