//Remove an item from a given list. public override object VisitStat_list_remove([NotNull] algoParser.Stat_list_removeContext context) { //Get the value that needs to be removed. AlgoValue toRemove = (AlgoValue)VisitExpr(context.expr()); //Get the variable. var listVar = Particles.ParseParticleBlock(this, context, context.IDENTIFIER(), context.particle()); if (listVar == null) { Error.Fatal(context, "No variable was returned to remove an item from, so can't remove an item from it."); return(null); } //Variable exists, so get the list value from it. if (listVar.Type != AlgoValueType.List) { Error.Fatal(context, "Variable given is not list, so can't remove an item from it."); return(null); } //Get the value of the list. List <AlgoValue> toSet = (List <AlgoValue>)listVar.Value; if (context.FROM_SYM() != null) { //Remove a given value from the list. //Check if the list contains the value. if (!toSet.Any(x => x.Value.Equals(toRemove.Value))) { Error.Fatal(context, "The list selected does not contain an item with value given to remove."); return(null); } //Remove the value. int index = toSet.FindIndex(x => x._Equals(toRemove)); toSet.RemoveAt(index); } else { //Is the index valid? if (toRemove.Type != AlgoValueType.Integer) { Error.Fatal(context, "The index given to remove at is not an integer."); return(null); } if ((BigInteger)toRemove.Value < 0 || (BigInteger)toRemove.Value > toSet.Count) { Error.Fatal(context, "Index to remove out of range for the given list."); } //Yes, remove there. toSet.RemoveAt(int.Parse(((BigInteger)toRemove.Value).ToString())); } return(null); }
//When a variable is first defined. public override object VisitStat_define([NotNull] algoParser.Stat_defineContext context) { //Get the name of the identifier/objtext. string name = Particles.StitchVariableName(context, context.IDENTIFIER().GetText(), context.particle()); //Evaluate the expression on the right side of the define. AlgoValue value = (AlgoValue)VisitExpr(context.expr()); //Create the variable at the current scope. Scopes.AddVariable(name, value); return(null); }
//When a variable is modified using a postfix operator. public override object VisitStat_setvar_postfix([NotNull] algoParser.Stat_setvar_postfixContext context) { //Get variable name. AlgoValue oldValue = Particles.ParseParticleBlock(this, context, context.IDENTIFIER(), context.particle()); if (oldValue == null) { Error.Fatal(context, "No value returned to modify with operator."); return(null); } //Switch on the operator type. int toAdd = 0; if (context.postfix_op().ADD_PFOP() != null) { //Increment. toAdd = 1; } else { //Decrement. toAdd = -1; } //Apply the operator. AlgoValue newValue = null; switch (oldValue.Type) { case AlgoValueType.Integer: case AlgoValueType.Float: case AlgoValueType.Rational: newValue = AlgoOperators.Add(context, oldValue, new AlgoValue() { Type = AlgoValueType.Integer, Value = new BigInteger(toAdd) }); break; default: Error.Fatal(context, "Cannot increment a variable of type " + oldValue.Type.ToString() + "."); return(null); } //Set the variable value. oldValue.Value = newValue.Value; oldValue.Type = newValue.Type; return(null); }
//When a variable value is changed by a self modifying operator. public override object VisitStat_setvar_op([NotNull] algoParser.Stat_setvar_opContext context) { //Get variable reference. AlgoValue oldValue = Particles.ParseParticleBlock(this, context, context.IDENTIFIER(), context.particle()); if (oldValue == null) { Error.Fatal(context, "No value returned to modify with operator."); return(null); } //Does, evaluate the expression to set the value. AlgoValue value = (AlgoValue)VisitExpr(context.expr()); //Switching on selfmod value. AlgoValue newValue; if (context.selfmod_op().ADDFROM_OP() != null) { //Attempt to add. newValue = AlgoOperators.Add(context, oldValue, value); } else if (context.selfmod_op().DIVFROM_OP() != null) { //Attempt to divide. newValue = AlgoOperators.Div(context, oldValue, value); } else if (context.selfmod_op().MULFROM_OP() != null) { //Attempt to multiply. newValue = AlgoOperators.Mul(context, oldValue, value); } else if (context.selfmod_op().TAKEFROM_OP() != null) { //Attempt to subtract. newValue = AlgoOperators.Sub(context, oldValue, value); } else { //Invalid operator type, oopsie! Implemented in parser but not here. Error.Fatal(context, "Invalid operator given for self modifying variable, implemented in parser but not in interpreter."); return(null); } //Set the value of the variable. oldValue.Value = newValue.Value; oldValue.Type = newValue.Type; return(null); }
//When a variable's value is changed. public override object VisitStat_setvar([NotNull] algoParser.Stat_setvarContext context) { //Get the variable/object reference. AlgoValue varToSet = Particles.ParseParticleBlock(this, context, context.IDENTIFIER(), context.particle()); if (varToSet == null) { Error.Fatal(context, "No value returned to set."); return(null); } //Does, evaluate the expression to set the value. AlgoValue value = (AlgoValue)VisitExpr(context.expr()); //Check if there's a rounding expression. if (context.rounding_expr() != null) { //Evaluate rounding number expression. AlgoValue roundingNum = (AlgoValue)VisitExpr(context.rounding_expr().expr()); if (roundingNum.Type != AlgoValueType.Integer) { Error.Warning(context, "Rounding expression did not return an integer to round by, so rounding was ignored."); } else if ((BigInteger)roundingNum.Value > int.MaxValue) { Error.Warning(context, "Rounding number too large, so rounding was ignored."); } else { //Getting rounding integer. int roundingInt = int.Parse(((BigInteger)roundingNum.Value).ToString()); //Rounding the value, setting. Scopes.SetVariable(context.IDENTIFIER().GetText(), AlgoOperators.Round(context, value, roundingInt)); return(null); } } //Set variable. varToSet.Value = value.Value; varToSet.Type = value.Type; return(null); }
//When a single value is visited in Algo. public override object VisitValue([NotNull] algoParser.ValueContext context) { //Check what type the value is. if (context.INTEGER() != null) { //INTEGER return(new AlgoValue() { Type = AlgoValueType.Integer, Value = BigInteger.Parse(context.INTEGER().GetText()), }); } else if (context.FLOAT() != null) { //FLOAT return(new AlgoValue() { Type = AlgoValueType.Float, Value = BigFloat.Parse(context.FLOAT().GetText()), }); } else if (context.STRING() != null) { //STRING return(new AlgoValue() { Type = AlgoValueType.String, Value = context.STRING().GetText().Substring(1, context.STRING().GetText().Length - 2) //text escape codes .Replace("\\n", "\n") .Replace("\\t", "\t") .Replace("\\r", "\r") .Replace("\\q", "\"") }); } else if (context.RATIONAL() != null) { //RATIONAL //Get the two integer halves from the rational. string[] halves = context.RATIONAL().GetText().Split('/'); string numerator = halves[0]; string denominator = halves[1]; return(new AlgoValue() { Type = AlgoValueType.Rational, Value = new BigRational(BigInteger.Zero, new Fraction(BigInteger.Parse(numerator), BigInteger.Parse(denominator))), }); } else if (context.BOOLEAN() != null) { //BOOLEAN return(new AlgoValue() { Type = AlgoValueType.Boolean, Value = (context.BOOLEAN().GetText() == "true"), }); } else if (context.HEX() != null) { //BYTES return(new AlgoValue() { Type = AlgoValueType.Bytes, Value = context.HEX().GetText().ToByteArray() }); } else if (context.NULL() != null) { //NULL VALUE return(new AlgoValue() { Type = AlgoValueType.Null, Value = null, }); } else if (context.IDENTIFIER() != null) { //VARIABLE/OBJECT return(Particles.ParseParticleBlock(this, context, context.IDENTIFIER(), context.particle())); } else if (context.stat_functionCall() != null) { //FUNCTION CALL AlgoValue value = (AlgoValue)VisitStat_functionCall(context.stat_functionCall()); if (value != null) { return(value); } else { //Return a null value. return(new AlgoValue() { Type = AlgoValueType.Null, Value = null, }); } } else if (context.array() != null) { //LIST. //Evaluate all list items as expressions. List <AlgoValue> list = new List <AlgoValue>(); foreach (var item in context.array().value()) { list.Add((AlgoValue)VisitValue(item)); } //Return as a single value. return(new AlgoValue() { Type = AlgoValueType.List, Value = list }); } else if (context.@object() != null) { //OBJECT DEFINITION //Create a new object. AlgoObject toReturn = new AlgoObject(); //Anything to enumerate? if (context.@object().obj_child_definitions() == null) { //Nope. return(new AlgoValue() { Type = AlgoValueType.Object, Value = toReturn }); } //Enumerate through all the define statements and evaluate their values. foreach (var value in context.@object().obj_child_definitions().obj_vardefine()) { //Check if the variable already exists. if (toReturn.ObjectScopes.VariableExists(value.IDENTIFIER().GetText())) { Error.Fatal(context, "The variable with name '" + value.IDENTIFIER().GetText() + "' is defined twice or more in an object."); return(null); } //Evaluate the value on the right. AlgoValue evaluated = (AlgoValue)VisitExpr(value.expr()); //Add the variable to scope. toReturn.ObjectScopes.AddVariable(value.IDENTIFIER().GetText(), evaluated); } //Enumerate through all functions and define them. foreach (var value in context.@object().obj_child_definitions().obj_funcdefine()) { //Check if the variable already exists. if (toReturn.ObjectScopes.VariableExists(value.IDENTIFIER().GetText())) { Error.Fatal(context, "The variable with name '" + value.IDENTIFIER().GetText() + "' is defined twice or more in an object."); return(null); } //Create a list of parameters. List <string> params_ = new List <string>(); if (value.abstract_params() != null) { foreach (var param in value.abstract_params().IDENTIFIER()) { //Check if param already exists. if (params_.Contains(param.GetText())) { Error.Fatal(context, "The parameter with name '" + param.GetText() + "' is already defined in the function."); return(null); } params_.Add(param.GetText()); } } //Create a function, push. AlgoFunction func = new AlgoFunction(value.statement().ToList(), params_, value.IDENTIFIER().GetText()); AlgoValue funcValue = new AlgoValue() { Type = AlgoValueType.Function, Value = func, }; toReturn.ObjectScopes.AddVariable(value.IDENTIFIER().GetText(), funcValue); } //Enumerate all external functions defined. foreach (var ext in context.@object().obj_child_definitions().obj_externdefine()) { //Get the value of the function. var loadFuncStat = ext.stat_loadFuncExt(); AlgoValue func = Plugins.GetEmulatedFuncValue(loadFuncStat); //Check if a variable with this name already exists. if (toReturn.ObjectScopes.VariableExists(loadFuncStat.IDENTIFIER()[0].GetText())) { Error.Fatal(context, "A variable with the name '" + loadFuncStat.IDENTIFIER()[0].GetText() + "' already exists in this object. Cannot duplicate."); return(null); } //Add that function to the return object. toReturn.ObjectScopes.AddVariable(loadFuncStat.IDENTIFIER()[0].GetText(), func); } //Return the object. return(new AlgoValue() { Type = AlgoValueType.Object, Value = toReturn }); } else { //No proper detected value type. Error.Fatal(context, "Unknown or invalid type given for value."); return(new AlgoValue() { Type = AlgoValueType.Null, Value = null }); } }
//Adding an item to a list. public override object VisitStat_list_add([NotNull] algoParser.Stat_list_addContext context) { //Evaluate the value to be added to the list. AlgoValue toAdd = (AlgoValue)VisitExpr(context.expr()[0]); //Get the list variable from the identifier and particle. AlgoValue listVar = Particles.ParseParticleBlock(this, context, context.IDENTIFIER(), context.particle()); if (listVar == null) { Error.Fatal(context, "No list value returned to add to from particle block."); return(null); } //Check the type is actually a list. if (listVar.Type != AlgoValueType.List) { Error.Fatal(context, "Variable given is not list, so can't remove an item from it."); return(null); } //Add a value to the list, either at a specific index or to the end. List <AlgoValue> toReturn = (List <AlgoValue>)listVar.Value; if (context.AT_SYM() != null) { //Evaluate the index. AlgoValue index = (AlgoValue)VisitExpr(context.expr()[1]); //Is it an integer? if (index.Type == AlgoValueType.Integer) { Error.Fatal(context, "The index supplied to insert at was not an integer."); return(null); } //Is it 0 or above? if ((BigInteger)index.Value < 0) { Error.Fatal(context, "The index supplied is below zero, out of range."); return(null); } //Is the index greater than the length of the list? if ((BigInteger)index.Value > ((List <AlgoValue>)listVar.Value).Count || (BigInteger)index.Value > int.MaxValue) { Error.Fatal(context, "The index supplied was out of range (too large)."); return(null); } //Insert. toReturn.Insert(int.Parse(((BigInteger)index.Value).ToString()), toAdd); } else { toReturn.Add(toAdd); } //Set the variable via. reference (TEST ME!). listVar.Value = toReturn; return(null); }
/// <summary> /// Evaluates a given particle, and returns the result. /// </summary> public override object VisitParticle([NotNull] algoParser.ParticleContext context) { //Get the last result of the particle before this. AlgoValue current = Particles.GetParticleResult(); if (current == null) { Error.Internal("Particle chain started without setting an initial value."); return(null); } //Prepare an "old" scope for swapping. AlgoScopeCollection oldScope = null; //What type of particle is it? //CHILD VARIABLE if (context.IDENTIFIER() != null) { //Sub-variable. Attempt to get from the object. if (current.Type != AlgoValueType.Object) { Error.Fatal(context, "Cannot access children when given variable is not an object, and has no children to access."); return(null); } //Does the sub-variable exist? AlgoObject curObj = (AlgoObject)current.Value; AlgoValue newObj = curObj.ObjectScopes.GetVariable(context.IDENTIFIER().GetText()); if (newObj == null) { Error.Fatal(context, "No child of the given object exists with name '" + context.IDENTIFIER().GetText() + "'."); return(null); } //Yes, set result and return. Particles.SetParticleInput(newObj); return(null); } //ARRAY ACCESS else if (context.array_access_particle() != null) { var aap = context.array_access_particle(); //Indexing. //For each index, iterate over. AlgoValue currentLV = current; foreach (var index in aap.literal_params().expr()) { //Is the current list value a list? if (currentLV.Type != AlgoValueType.List) { Error.Fatal(context, "Cannot index into variable that is not a list or indexable object."); return(null); } //Need to switch scopes for evaluating indexes? if (Particles.funcArgumentScopes != null) { oldScope = Scopes; Scopes = Particles.funcArgumentScopes; } //Evaluate the expression. AlgoValue indexVal = (AlgoValue)VisitExpr(index); //Back to normal. if (Particles.funcArgumentScopes != null) { Scopes = oldScope; } //Valid? if (indexVal.Type != AlgoValueType.Integer) { Error.Fatal(context, "Index value provided is not an integer. Indexes must be whole integer values."); return(null); } //Is the index outside of the bounds of the array? if (((List <AlgoValue>)currentLV.Value).Count <= (BigInteger)indexVal.Value) { Error.Fatal(context, "Index provided is outside the bounds of the array."); return(null); } //Index into it, set the current value. currentLV = ((List <AlgoValue>)currentLV.Value)[(int)((BigInteger)indexVal.Value)]; } //Set result. Particles.SetParticleInput(currentLV); return(null); } //CHILD FUNCTION else if (context.functionCall_particle() != null) { //Function call. if (current.Type != AlgoValueType.Object) { Error.Fatal(context, "Cannot call a child function on a value with no children (given value was not an object)."); return(null); } //Get the child, see if it's a function. AlgoObject thisObj = current.Value as AlgoObject; string funcName = context.functionCall_particle().IDENTIFIER().GetText(); AlgoValue childFunc = thisObj.ObjectScopes.GetVariable(funcName); if (childFunc == null || (childFunc.Type != AlgoValueType.Function && childFunc.Type != AlgoValueType.EmulatedFunction)) { Error.Fatal(context, "No child function exists with name '" + funcName + "'."); return(null); } //Set the particle result as the function value, call the function call particle. Particles.SetParticleInput(childFunc); var result = VisitFunctionCall_particle(context.functionCall_particle()); if (result == null) { Particles.ResetParticleInput(); //Returned no result. } else { //A result came back, set input for the next particle. Particles.SetParticleInput((AlgoValue)result); } return(null); } else { //Unrecognized! Error.Internal("Unrecognized particle type to parse, implementation either unfinished or incorrect."); return(null); } }
/// <summary> /// Evaluates a single function call particle, and returns the result as a value (NOT using particle manager). /// </summary> public override object VisitFunctionCall_particle([NotNull] algoParser.FunctionCall_particleContext context) { //Set up an old scope. AlgoScopeCollection oldScope = null; //Get the previous particle output. var current = Particles.GetParticleResult(); //Right length of parameters? var paramCtx = context.literal_params(); int givenNumParams = 0; if (paramCtx != null) { givenNumParams = paramCtx.expr().Length; } else if (current.Type == AlgoValueType.EmulatedFunction) { var func = ((AlgoPluginFunction)current.Value); int paramNum = func.ParameterCount; if (givenNumParams != paramNum) { Error.Fatal(context, "Invalid number of parameters for function '" + func.Name + "' (Expected " + func.ParameterCount + ", got " + givenNumParams + ")."); return(null); } } else if (current.Type == AlgoValueType.Function) { var func = ((AlgoFunction)current.Value); int paramNum = func.Parameters.Count; if (givenNumParams != paramNum) { Error.Fatal(context, "Invalid number of parameters for function '" + func.Name + "' (Expected " + func.Parameters.Count + ", got " + givenNumParams + ")."); return(null); } } //Evaluate all the parameters, in the paramScope if necessary. if (Particles.funcArgumentScopes != null) { oldScope = Scopes; Scopes = Particles.funcArgumentScopes; } List <AlgoValue> params_ = new List <AlgoValue>(); if (paramCtx != null) { foreach (var paramToEval in paramCtx.expr()) { params_.Add((AlgoValue)VisitExpr(paramToEval)); } } //Switch back scopes. if (Particles.funcArgumentScopes != null) { Scopes = oldScope; } //Depending on the type of function, execute. if (current.Type == AlgoValueType.Function) { //STANDARD FUNCTION var func = (AlgoFunction)current.Value; //Create a new scope, add the parameters to it. Scopes.AddScope(); for (int i = 0; i < params_.Count; i++) { Scopes.AddVariable(func.Parameters[i], params_[i], context); } //Running the function's body. foreach (var statement in func.Body) { AlgoValue returned = (AlgoValue)VisitStatement(statement); if (returned != null) { //Normal function, remove scope and return. Scopes.RemoveScope(); return(returned); } } //Remove the scope. Scopes.RemoveScope(); //Return nothing, no result. return(null); } else if (current.Type == AlgoValueType.EmulatedFunction) { //EMULATED FUNCTION var func = (AlgoPluginFunction)current.Value; //Attempt to evaluate, and return result. AlgoValue returned = null; try { returned = func.Function(context, params_.ToArray()); } catch (Exception e) { Error.Fatal(context, "External function returned an error, " + e.Message); return(null); } return(returned); } else { //Unsupported uncaught function type. Error.Internal("Unsupported uncaught function type detected (" + current.Type.ToString() + "), cannot execute."); return(null); } }
//When a function is called. public override object VisitStat_functionCall([NotNull] algoParser.Stat_functionCallContext context) { //If there are particles, get the last value. AlgoValue currentVal = null; bool isLibraryTopLevelCall = false; if (context.IDENTIFIER() != null) { //Is the identifier a library and has no particles? if (!Scopes.VariableExists(context.IDENTIFIER().GetText()) && Scopes.LibraryExists(context.IDENTIFIER().GetText()) && (context.particle() == null || context.particle().Length == 0)) { //Library top level call, so don't evaluate like normal particles. isLibraryTopLevelCall = true; //Just get the very last function like a normal variable, but from the library. string libName = context.IDENTIFIER().GetText(); string name = context.functionCall_particle().IDENTIFIER().GetText(); var lib = Scopes.GetLibrary(libName); if (!lib.VariableExists(name)) { Error.Fatal(context, "No variable named '" + name + "' exists in library '" + libName + "'."); return(null); } currentVal = lib.GetVariable(name); } else { //Not a library top level call, so execute like a normal particle block. currentVal = Particles.ParseParticleBlock(this, context, context.IDENTIFIER(), context.particle()); } } else { //No particles, just grab the value as an identifier. string name = context.functionCall_particle().IDENTIFIER().GetText(); if (!Scopes.VariableExists(name)) { Error.Fatal(context, "No function exists with name '" + name + "'."); return(null); } currentVal = Scopes.GetVariable(name); } //Attempt to execute the final function on the result. if (context.IDENTIFIER() == null || isLibraryTopLevelCall) { //Visit and execute THIS value, not a child value (this was a straight normal call). Particles.SetParticleInput(currentVal); return(VisitFunctionCall_particle(context.functionCall_particle())); } else { //There were particles, visit and execute CHILD value. if (currentVal.Type != AlgoValueType.Object) { Error.Fatal(context, "Cannot call a child function on a value with no children (given value was not an object)."); return(null); } //Get the child, see if it's a function. AlgoObject thisObj = currentVal.Value as AlgoObject; string funcName = context.IDENTIFIER().GetText(); AlgoValue childFunc = thisObj.ObjectScopes.GetVariable(funcName); if (childFunc == null || (childFunc.Type != AlgoValueType.Function && childFunc.Type != AlgoValueType.EmulatedFunction)) { Error.Fatal(context, "No child function exists with name '" + funcName + "'."); return(null); } //Set the particle result as the function value, call the function call particle. Particles.SetParticleInput(childFunc); return(VisitFunctionCall_particle(context.functionCall_particle())); } }