/// <summary> /// This function handles unsupported features by <see cref="Regen.Flee"/> evaluator by creating temporary /// variables or even changing the type of expression. /// </summary> /// <remarks> /// Because flee does not support arrays, we temporarly store any parsed array /// into a variable and then just pass the variable name, letting Regen just fetch it /// and pass it around. ///</remarks> public Expression HandleUnsupported(Expression expr, List <TemporaryVariable> temps, Type caller = null) { //todo after we support dictionaries, add support here switch (expr) { case NullIdentity _: case CharLiteral _: case NumberLiteral _: case StringLiteral _: case BooleanLiteral _: case StringIdentity _: case ReferenceIdentity _: case EmptyExpression _: return(expr); case ArgumentsExpression argumentsExpression: { for (var i = 0; i < argumentsExpression.Arguments.Length; i++) { argumentsExpression.Arguments[i] = HandleUnsupported(argumentsExpression.Arguments[i], temps, typeof(ArgumentsExpression)); } return(argumentsExpression); } case ArrayExpression arrayExpression: { for (var i = 0; i < arrayExpression.Values.Length; i++) { arrayExpression.Values[i] = HandleUnsupported(arrayExpression.Values[i], temps, typeof(ArrayExpression)); } var parsedArray = Compute(arrayExpression, temps, typeof(ArrayExpression)); var temp = new TemporaryVariable(Context, parsedArray); //todo this might lead to memory leaks! //temps.Add(temp); //if (caller == typeof(RegenCompiler)) { //if this is the first expression that is being parsed // temp.MarkPermanent(); //} return(IdentityExpression.WrapVariable(temp.Name)); } case IndexerCallExpression indexerCallExpression: { indexerCallExpression.Left = HandleUnsupported(indexerCallExpression.Left, temps, typeof(IndexerCallExpression)); indexerCallExpression.Arguments = (ArgumentsExpression)HandleUnsupported(indexerCallExpression.Arguments, temps, typeof(IndexerCallExpression)); return(indexerCallExpression); } case CallExpression callExpression: { callExpression.FunctionName = HandleUnsupported(callExpression.FunctionName, temps, typeof(CallExpression)); callExpression.Arguments = (ArgumentsExpression)HandleUnsupported(callExpression.Arguments, temps, typeof(CallExpression)); return(callExpression); } case IdentityExpression identityExpression: { //here we turn any string literal into a reference to a variable. //if theres no such variable, we assume it is for a functionname of property. if (identityExpression.Identity is StringIdentity sr) { if (Context.Variables.ContainsKey(sr.Name)) { return(new IdentityExpression(ReferenceIdentity.Wrap(sr))); } } identityExpression.Identity = HandleUnsupported(identityExpression.Identity, temps, caller ?? typeof(IdentityExpression)); return(identityExpression); } case HashtagReferenceExpression hashtagReference: { var key = $"__{hashtagReference.Number}__"; return(new IdentityExpression(new ReferenceIdentity(key, new RegexResult() { Value = key, Index = hashtagReference.Matches().First().Index, Length = 1 + hashtagReference.Number.Length }))); } case GroupExpression groupExpression: groupExpression.InnerExpression = HandleUnsupported(groupExpression.InnerExpression, temps, caller ?? typeof(GroupExpression)); return(groupExpression); case PropertyIdentity propertyIdentity: //todo maybe here we parse Left, store and push? but first invalidate that it is not just a name. propertyIdentity.Left = HandleUnsupported(propertyIdentity.Left, temps, caller ?? typeof(PropertyIdentity)); propertyIdentity.Right = HandleUnsupported(propertyIdentity.Right, temps, caller ?? typeof(PropertyIdentity)); return(propertyIdentity); case KeyValueExpression keyValueExpression: keyValueExpression.Key = HandleUnsupported(keyValueExpression.Key, temps, typeof(KeyValueExpression)); keyValueExpression.Value = HandleUnsupported(keyValueExpression.Value, temps, typeof(KeyValueExpression)); return(keyValueExpression); case NewExpression newExpression: newExpression.Constructor = HandleUnsupported(newExpression.Constructor, temps, typeof(NewExpression)); return(newExpression); case LeftOperatorExpression leftOperatorExpression: leftOperatorExpression.Right = HandleUnsupported(leftOperatorExpression.Right, temps, typeof(LeftOperatorExpression)); return(leftOperatorExpression); case OperatorExpression operatorExpression: operatorExpression.Left = HandleUnsupported(operatorExpression.Left, temps, typeof(OperatorExpression)); operatorExpression.Right = HandleUnsupported(operatorExpression.Right, temps, typeof(OperatorExpression)); return(operatorExpression); case RightOperatorExpression rightOperatorExpression: rightOperatorExpression.Left = HandleUnsupported(rightOperatorExpression.Left, temps, typeof(RightOperatorExpression)); return(rightOperatorExpression); case ThrowExpression throwExpression: throwExpression.Right = HandleUnsupported(throwExpression.Right, temps, typeof(ThrowExpression)); return(throwExpression); case ForeachExpression foreachExpression: case ImportExpression importExpression: case InteractableExpression interactableExpression: case VariableDeclarationExpression variableExpression: throw new NotSupportedException(); //todo support? this should be found in an expression. it is a higher level expression case Identity identity: //this is an abstract class. throw new NotSupportedException(); default: throw new NotImplementedException(); } }
/// <summary> /// Perform the evaluation or translate <see cref="Expression"/> into a variable for later use in computation. /// </summary> /// <returns></returns> private Data Compute(Expression expression, List <TemporaryVariable> temps, Type _caller = null) { return(_eval(expression, _caller)); Data _eval(Expression express, Type caller = null) { switch (express) { case null: throw new NullReferenceException(); case ArgumentsExpression argumentsExpression: { var arr = new Array(); foreach (var expr in argumentsExpression.Arguments) { arr.Add(_eval(expr)); } return(arr); } case ArrayExpression arrayExpression: { var arr = new Array(); foreach (var expr in arrayExpression.Values) { arr.Add(_eval(expr)); } return(arr); } case NumberLiteral numberLiteral: { return(new NumberScalar(_evaluate(numberLiteral.Value))); } case BooleanLiteral booleanLiteral: { return(new BoolScalar(booleanLiteral.Value)); } case CharLiteral charLiteral: { return(new StringScalar(charLiteral.Value.ToString())); } case NullIdentity nullIdentity: { return(Data.Null); } case StringLiteral stringLiteral: { return(new StringScalar(stringLiteral.Value)); } case KeyValueExpression keyValueExpression: { //todo create a KeyValue regen data type. return(new NetObject(new KeyValuePair <object, object>(_eval(keyValueExpression.Key), _eval(keyValueExpression.Value)))); } case EmptyExpression emptyExpression: { return(Data.Null); } case ForeachExpression foreachExpression: { break; } case GroupExpression groupExpression: { return(_eval(groupExpression.InnerExpression)); } case ReferenceIdentity referenceIdentity: { if (!Context.Variables.ContainsKey(referenceIdentity.Name)) { return(new ReferenceData(referenceIdentity.Name)); } if (!Context.Variables.TryGetValue(referenceIdentity.Name, out var value, true)) { throw new Exception("This should never occur."); } //if it is not a reference, make one. if (!(value is ReferenceData)) { return(new ReferenceData(referenceIdentity.Name)); } return((ReferenceData)value); } case PropertyIdentity propertyIdentity: { TemporaryVariable tmp = null; var left = _eval(propertyIdentity.Left); if (left is ReferenceData rf) { var right = new PropertyIdentity(IdentityExpression.WrapVariable(rf.EmitExpressive()), propertyIdentity.Right).AsString(); return(Data.Create(_evaluate(right))); } //not reference using (tmp = new TemporaryVariable(Context, left is NetObject no ? no.Value : left)) { return(Data.Create(_evaluate($"{tmp.Name}.{propertyIdentity.Right.AsString()}"))); } using (var var = new TemporaryVariable(Context, left is NetObject no ? no.Value : left)) { var right = new PropertyIdentity(IdentityExpression.WrapVariable(var.Name), propertyIdentity.Right).AsString(); return(Data.Create(_evaluate(right))); } } case StringIdentity stringIdentity: { return(new ReferenceData(stringIdentity.Name)); } case IdentityExpression identityExpression: { return(_eval(identityExpression.Identity, caller)); //todo test } case Identity identity: { throw new NotSupportedException(); } case CallExpression callExpression: { var left = _eval(callExpression.FunctionName, typeof(CallExpression)); var args = (Array)_eval(callExpression.Arguments); if (left is NetObject || left is Array || left is Dictionary) { goto _storing; } //try regular parsing: try { var parsed = $"{left.Emit()}({args.Select(arg => arg.EmitExpressive()).StringJoin(", ")})"; return(Data.Create(_evaluate(parsed))); } catch (ExpressionCompileException e) when(e.InnerException?.Message.Contains("FunctionCallElement: Could find not function") ?? false) { throw; } catch (ExpressionCompileException) { } _storing: //try storing left as variable using (var var = new TemporaryVariable(Context, left is NetObject no ? no.Value : left)) { var parsed = $"{var.Name}({args.Select(arg => arg.EmitExpressive()).StringJoin(", ")})"; return(Data.Create(_evaluate(parsed))); } } case IndexerCallExpression indexerCallExpression: { var left = Data.Create(_eval(indexerCallExpression.Left, typeof(IndexerCallExpression))); var args = (Array)_eval(indexerCallExpression.Arguments); if (left is NetObject || left is Array || left is Dictionary) { goto _storing; } //try regular parsing: try { var parsed = $"{left.Emit()}[{args.Select(arg => arg.EmitExpressive()).StringJoin(", ")}]"; return(Data.Create(_evaluate(parsed))); } catch (ExpressionCompileException) { } _storing: //try storing left as variable using (var var = new TemporaryVariable(Context, left is NetObject no ? no.Value : left)) { var parsed = $"{var.Name}[{args.Select(arg => arg.EmitExpressive()).StringJoin(", ")}]"; return(Data.Create(_evaluate(parsed))); } } case NewExpression newExpression: { //todo new break; } case LeftOperatorExpression leftOperatorExpression: { foreach (var e in leftOperatorExpression.Iterate()) { if (e is ArrayExpression) { throw new NotSupportedException("Unable to compile a nested array, please define it in a variable first."); } } //todo BuiltinTests.len fails here because we do not expand left or right. we should. return(Data.Create(_evaluate(leftOperatorExpression.AsString()))); } case OperatorExpression operatorExpression: { foreach (var e in operatorExpression.Iterate()) { if (e is ArrayExpression) { throw new NotSupportedException("Unable to compile a nested array, please define it in a variable first."); } } //todo BuiltinTests.len fails here because we do not expand left or right. we should. return(Data.Create(_evaluate(operatorExpression.AsString()))); } case RightOperatorExpression rightOperatorExpression: { foreach (var e in rightOperatorExpression.Iterate()) { if (e is ArrayExpression) { throw new NotSupportedException("Unable to compile a nested array, please define it in a variable first."); } } //todo BuiltinTests.len fails here because we do not expand left or right. we should. return(Data.Create(_evaluate(rightOperatorExpression.AsString()))); } case ThrowExpression throwExpression: { break; } case VariableDeclarationExpression variableExpression: { var name = Data.Create(_eval(variableExpression.Name)); if (name.GetType() != typeof(StringScalar)) { throw new NotSupportedException("Variable names can contain only _azAZ0-9"); } var value = Data.Create(_eval(variableExpression.Right)); Context.Variables[name.ToString()] = value; return(value); } } return(Data.Null); } }