// When a function takes in a lambda expression // This creates it Lambda CreateLambda(Expression lambda, int lambdaParams) { //Set up the list of parameters that this lambda will use List <string> parameterNames = new List <string>(lambdaParams); for (int i = 0; i < lambdaParams; i++) { parameterNames.Add(ParamVars.Peek()); //Rotate the parameter variable list ParamVars.Enqueue(ParamVars.Dequeue()); } //This is the function that will be returned //Everytime the lambda is used, this is what is being executed VObject func(VObject[] args) { var paramArgPairs = parameterNames.Zip(args).ToList(); var oldValues = new VObject[lambdaParams]; int i = 0; //Set the values of the parameter variables to whatever arguments were passed paramArgPairs.ForEach((paramArgPair) => { var(paramName, argVal) = paramArgPair; oldValues[i++] = programState.Variables[paramName]; programState.Variables[paramName] = argVal; }); //Set the autofill variable names to the names of the parameter variables //And store the old values into temp variables string oldAutofill1Name = programState.Autofill1Name; string oldAutofill2Name = programState.Autofill2Name; programState.Autofill1Name = parameterNames[0]; if (lambdaParams > 1) { programState.Autofill2Name = parameterNames[1]; } //Now evaluate the lambda with the parameter variables and autofills properly set var result = Evaluate(lambda); //Reset the autofill variable names to what they were before programState.Autofill1Name = oldAutofill1Name; programState.Autofill2Name = oldAutofill2Name; //Reset the parameter variables to their old values i = 0; paramArgPairs.ForEach((paramArgPair) => { var(paramName, _) = paramArgPair; programState.Variables[paramName] = oldValues[i]; }); return(result); } return(func); }
//How the value of an AutoExpression is decided //If the AutoExpression is the parameter to a higher order function // - Return the input //If we are inside a higher-order function // - The 'parameter variables' are 斯,成,它,感,干,法 // - They are placed upon a stack // - Based on how many is needed, that number is popped from the stack // - E.g some higher-order functions require 3 parameters, some only need 2 or 1 // - If there are at least two parameter variables, the first one will be designated as the first autofill // - and the second will be designated as the second autofill //Else if we are not inside a higher-order function // - The first autofill will be the first input passed to the program // - The second autofill will be the second input passed to the program //Now we have our first and second autofill // - If there are zero autofill, just use 0 // - If there is only one autofill, use that one autofill // - If there are at least two autofills defined: // - If the expression is an argument to a function, use the first autofill // - Use the second autofill // - Else just use the first VObject Evaluate(Expression ast) { switch (ast) { //Self-explanatory case NumericLiteralExpression number: return(new VObject(number.Value)); case StringLiteralExpression str: return(new VObject(str.Value)); case VariableReferenceExpression variable: return(programState.Variables[variable.Name]); case ConditionalExpression conditional: if (Evaluate(conditional.Condition).IsTruthy()) { return(Evaluate(conditional.TrueExpression)); } else { return(Evaluate(conditional.FalseExpression)); } //Use the first input as autofill by default case AutoExpression _: return(programState.Autofill_1); case FunctionInvocationExpression funcExpr: VObject caller; if (funcExpr.Caller is AutoExpression) { caller = programState.Autofill_1; } else { caller = Evaluate(funcExpr.Caller); } if (Function.IsHigherOrder(funcExpr.Function, caller.ObjectType)) { HigherOrderFunction func = (HigherOrderFunction)Function.Get(funcExpr.Function, caller.ObjectType); Lambda lambda; bool createdLambda = false; // If an autoexpression is submitted as a lambda, then either use the default lambda or if not available, a lambda that returns the first input if (funcExpr.Argument is AutoExpression) { lambda = func.DefaultLambda ?? (x => x[0]); } else { lambda = CreateLambda(funcExpr.Argument, func.LambdaParameters); createdLambda = true; } //We pass the lambda that we created into the function var res = func.Invoke(caller, lambda); //Re-rotate the parameter variables back //We rotated them in the CreateLambda method //Note this only triggers if CreateLambda was called if (createdLambda) { for (int i = 0; i < PARAMETER_VARIABLES.Length - func.LambdaParameters; i++) { ParamVars.Enqueue(ParamVars.Dequeue()); } } return(res); } if (Function.IsUnary(funcExpr.Function)) { UnaryFunction func = (UnaryFunction)Function.Get(funcExpr.Function, caller.ObjectType); if (func is UnaryFunctionWithProgramState funcWithProgState) { return(funcWithProgState.Invoke(caller, programState)); } else { return(func.Invoke(caller)); } } else { VObject arg = funcExpr.Argument is AutoExpression ? programState.Autofill_2 : Evaluate(funcExpr.Argument); BinaryFunction func = (BinaryFunction)Function.Get(funcExpr.Function, caller.ObjectType, arg.ObjectType); return(func.Invoke(caller, arg)); } throw new Exception(); case InterpolatedStringExpression intpStr: return(string.Concat(intpStr.Expressions.Select(x => Evaluate(x).ToString()))); default: ErrorHandler.InternalError("This shouldn't happen."); throw new Exception(); } }