//Checks whether a variable exists. public bool VariableExists(string varname) { if (varname.Contains('.')) { //Validate it by fake value grabbing. List <string> parts = varname.Split('.').ToList(); AlgoValue partParent = GetVariable(parts[0]); if (partParent == null || (partParent.Type != AlgoValueType.Object && parts.Count > 1)) { return(false); } //If the variable is known to exist and no points, this is just a sanity check to make sure nothing slips through. if (parts.Count == 1) { return(true); } //Iterate through children recursively to check they all exist. AlgoObject currentObj = (AlgoObject)partParent.Value; parts.RemoveAt(0); string childString = string.Join(".", parts.ToArray()); //Recursive return. return(currentObj.ObjectScopes.VariableExists(childString)); } return(GetVariable(varname) != null); }
//Returns the value of a referenced object within an object string. private AlgoValue GetValueFromObjectString(string objString) { //Splitting the string by "." to get individual variable parts. string[] objParts = objString.Split('.'); if (objParts.Length == 1) { if (!VariableExists(objString)) { return(null); } return(GetVariable(objString)); } //Loop and get value. if (!VariableExists(objParts[0])) { return(null); } //Get the first parent object. AlgoValue currentObjValue = GetVariable(objParts[0]); if (currentObjValue.Type != AlgoValueType.Object) { return(null); } //Getting the deepest scope. AlgoObject currentObj = (AlgoObject)currentObjValue.Value; for (int i = 1; i < objParts.Length - 1; i++) { //Attempt to get the child. if (!currentObj.ObjectScopes.VariableExists(objParts[i])) { return(null); } currentObjValue = currentObj.ObjectScopes.GetVariable(objParts[i]); //Check if it's an object. if (currentObjValue.Type != AlgoValueType.Object) { return(null); } //Set current object. currentObj = (AlgoObject)currentObjValue.Value; } //Getting the variable referenced at the deepest scope. if (!currentObj.ObjectScopes.VariableExists(objParts[objParts.Length - 1])) { return(null); } //Returning it. return(currentObj.ObjectScopes.GetVariable(objParts[objParts.Length - 1])); }
//Add a variable to the scope. public void AddVariable(string name, AlgoValue value, ParserRuleContext context = null) { //Is it an object member? if (!name.Contains(".")) { //Check if a variable with this name already exists. if (VariableExistsLowest(name)) { Error.Fatal(context, "A variable with this name already exists, cannot create one."); return; } //Nope, add it to the lowest scope. Scopes[Scopes.Count - 1].Add(name, value); } else { //Object member, a more complicated procedure. //Get the parent object that the member is being assigned to. string objTxt = name; List <string> objParts = objTxt.Split('.').ToList(); string final = objParts.Last(); objParts.RemoveAt(objParts.Count - 1); objTxt = string.Join(".", objParts.ToArray()); if (!VariableExists(objTxt)) { Error.Fatal(context, "Cannot create an object member for nonexistant object '" + objTxt + "'."); } //Check if the variable is an object. AlgoValue objVal = GetVariable(objTxt); if (objVal.Type != AlgoValueType.Object) { Error.Fatal(context, "Cannot create an object member of non-object value '" + objTxt + "'."); } AlgoObject obj = (AlgoObject)objVal.Value; //Check if the variable has the new member already defined. if (obj.ObjectScopes.VariableExists(final)) { Error.Fatal(context, "An object member with that name already exists."); } //Create the variable. obj.ObjectScopes.AddVariable(final, value); //Set the object parent. SetVariable(objTxt, new AlgoValue() { Type = AlgoValueType.Object, Value = obj }); } }
//When an enum is first defined. public override object VisitStat_enumDef([NotNull] algoParser.Stat_enumDefContext context) { //Check if the variable already exists at local scope. if (Scopes.VariableExistsLowest(context.IDENTIFIER().GetText())) { Error.Fatal(context, "A variable with the name '" + context.IDENTIFIER().GetText() + "'already exists, cannot create a duplicate."); return(null); } //Create an object for the enum. AlgoObject enumObj = new AlgoObject(); //For each enum member, create a child in the object with an integer value. BigInteger enumIndex = 0; if (context.abstract_params() != null) { foreach (var id in context.abstract_params().IDENTIFIER()) { //Does a member with this name already exist? if (enumObj.ObjectScopes.VariableExists(id.GetText())) { Error.Fatal(context, "An enum member with the name '" + id.GetText() + "' already exists."); return(null); } //Add member. enumObj.ObjectScopes.AddVariable(id.GetText(), new AlgoValue() { Type = AlgoValueType.Integer, Value = enumIndex }); enumIndex++; } } //Create an enum variable with this name. Scopes.AddVariable(context.IDENTIFIER().GetText(), new AlgoValue() { Type = AlgoValueType.Object, Value = enumObj }); return(null); }
//Remove a variable from the scope. public void RemoveVariable(string name) { //Check if a variable with this name exists. if (!VariableExists(name)) { Error.FatalNoContext("No variable with this name exists, so can't destroy it."); return; } //If the name contains a '.', got to go to deepest scope recursively. if (name.Contains('.')) { //Get the parent. List <string> parts = name.Split('.').ToList(); AlgoValue partParent = GetVariable(parts[0]); //Checking if it's an object. if (partParent.Type != AlgoValueType.Object) { Error.FatalNoContext("The value given is not an object, so cannot remove children."); return; } //Remove variable from this scope recursively. parts.RemoveAt(0); string newPartString = string.Join(".", parts.ToList()); AlgoObject partObj = (AlgoObject)partParent.Value; partObj.ObjectScopes.RemoveVariable(newPartString); } else { //Literal variable in this scope, check through all scopes (from deepest) and delete. for (int i = Scopes.Count - 1; i >= 0; i--) { if (Scopes[i].ContainsKey(name)) { Scopes[i].Remove(name); return; } } } //Could not find. Error.FatalNoContext("Could not find variable to delete, despite the fact it exists."); }
//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 }); } }
//Set a variable within the scopes. //Start from deepest depth. public void SetVariable(string varname, AlgoValue value, ParserRuleContext context = null) { //Is it a normal variable or an object? if (!varname.Contains('.')) { //Finding and setting. for (int i = Scopes.Count - 1; i >= 0; i--) { if (Scopes[i].Keys.Contains(varname)) { Scopes[i][varname] = value; return; } } //Uncaught missing variable. Error.Fatal(context, "No variable found with name '" + varname + "' to set."); } else { string[] objParts = varname.Split('.'); //Loop and get value. if (!VariableExists(objParts[0])) { Error.Fatal(context, "No parent variable '" + objParts[0] + "' exists."); } AlgoValue currentObjValue = GetVariable(objParts[0]); if (currentObjValue.Type != AlgoValueType.Object) { Error.Fatal(context, "Parent variable is not an object, so cannot access children."); } //Getting the deepest scope. AlgoObject currentObj = (AlgoObject)currentObjValue.Value; for (int i = 1; i < objParts.Length - 1; i++) { //Attempt to get the child. if (!currentObj.ObjectScopes.VariableExists(objParts[i])) { Error.Fatal(context, "A child property '" + objParts[i] + "' does not exist."); } currentObjValue = currentObj.ObjectScopes.GetVariable(objParts[i]); //Check if it's an object. if (currentObjValue.Type != AlgoValueType.Object) { Error.Fatal(context, "A child property '" + objParts[i] + "' is not an object, so can't access further children."); } //Set current object. currentObj = (AlgoObject)currentObjValue.Value; } //Getting the variable referenced at the deepest scope. if (!currentObj.ObjectScopes.VariableExists(objParts[objParts.Length - 1])) { Error.Fatal(context, "No variable exists at the deepest scope with name '" + objParts[objParts.Length - 1] + "'."); } //Set value of variable. currentObj.ObjectScopes.SetVariable(objParts[objParts.Length - 1], value); } }
/// <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); } }
//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())); } }