//When an expression is parsed in Algo. public override object VisitExpr([NotNull] algoParser.ExprContext context) { //Is it an addition? if (context.ADD_OP() != null) { //Yes. Evaluate the left hand expression. AlgoValue left = (AlgoValue)VisitExpr(context.expr()); //Evaluate the right hand term. AlgoValue right = (AlgoValue)VisitTerm(context.term()); //Perform an add operation, based on type. return(AlgoOperators.Add(context, left, right)); } else if (context.TAKE_OP() != null) { //Take operation. //Evaluate the left hand expression. AlgoValue left = (AlgoValue)VisitExpr(context.expr()); //Evaluate the right hand term. AlgoValue right = (AlgoValue)VisitTerm(context.term()); //Perform a take operation, based on type. return(AlgoOperators.Sub(context, left, right)); } else { //No, just a term, evaluate. return((AlgoValue)VisitTerm(context.term())); } }
//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); }
//Converts an Algo Rational to an Algo Float. public static BigFloat RationalToFloat(AlgoValue rational) { BigInteger numerator = ((BigRational)rational.Value).FractionalPart.Numerator; BigInteger denominator = ((BigRational)rational.Value).FractionalPart.Denominator; return(BigFloat.Divide(new BigFloat(numerator), new BigFloat(denominator))); }
//Get boolean value for the given AlgoValue. public static bool GetBooleanValue(AlgoValue value, ParserRuleContext context) { switch (value.Type) { case AlgoValueType.Boolean: return((bool)value.Value); case AlgoValueType.Integer: if ((BigInteger)value.Value == 1) { return(true); } else if ((BigInteger)value.Value == 0) { return(false); } else { Error.Fatal(context, "Cannot implicitly cast an integer that is not one or zero to boolean."); return(false); } default: Error.Fatal(context, "Cannot implicitly cast type " + value.Type.ToString() + " to a boolean."); return(false); } }
//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])); }
//Less than. public static bool LessThan(ParserRuleContext context, AlgoValue left, AlgoValue right, bool equalTo) { //Get the values for both left and right in a specific format. //(Statements must be in order of casting heirarchy, top down). if (left.Type == AlgoValueType.Float || right.Type == AlgoValueType.Float) { //Convert both to float. AlgoValue leftConverted = AlgoOperators.ConvertType(context, left, AlgoValueType.Float); AlgoValue rightConverted = AlgoOperators.ConvertType(context, right, AlgoValueType.Float); //Check if the left is less than the right. if (equalTo) { return((BigFloat)left.Value <= (BigFloat)right.Value); } else { return((BigFloat)left.Value < (BigFloat)right.Value); } } else if (left.Type == AlgoValueType.Rational || right.Type == AlgoValueType.Rational) { //Convert both to rational. AlgoValue leftConverted = AlgoOperators.ConvertType(context, left, AlgoValueType.Rational); AlgoValue rightConverted = AlgoOperators.ConvertType(context, right, AlgoValueType.Rational); //Check if the left is less than the right. if (equalTo) { return((BigRational)left.Value <= (BigRational)right.Value); } else { return((BigRational)left.Value < (BigRational)right.Value); } } else if (left.Type == AlgoValueType.Integer || right.Type == AlgoValueType.Integer) { //Convert both to integer. AlgoValue leftConverted = AlgoOperators.ConvertType(context, left, AlgoValueType.Integer); AlgoValue rightConverted = AlgoOperators.ConvertType(context, right, AlgoValueType.Integer); //Check if the left is less than the right. if (equalTo) { return((BigInteger)left.Value <= (BigInteger)right.Value); } else { return((BigInteger)left.Value < (BigInteger)right.Value); } } else { Error.Fatal(context, "Cannot compare values with type " + left.Type.ToString() + " and " + right.Type.ToString() + "."); return(false); } }
//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); }
//Evaluate a check into a raw boolean (no value wrapper). public override object VisitCheck([NotNull] algoParser.CheckContext context) { //Switch for the type of check. if (context.BIN_AND() != null) { //Binary AND. //Evaluate the left and right values. AlgoValue left = (AlgoValue)VisitCheck(context.check()); AlgoValue right = (AlgoValue)VisitCheckfrag(context.checkfrag()); //Return the comparison result of these two (as AlgoValue). return(new AlgoValue() { Type = AlgoValueType.Boolean, Value = AlgoComparators.AND(context, left, right), }); } else if (context.BIN_OR() != null) { //Binary OR. //Evaluate the left and right values. AlgoValue left = (AlgoValue)VisitCheck(context.check()); AlgoValue right = (AlgoValue)VisitCheckfrag(context.checkfrag()); //Return an OR on these values. return(new AlgoValue() { Type = AlgoValueType.Boolean, Value = AlgoComparators.OR(context, left, right), }); } else if (context.LBRACKET() != null) { //Bracketed check. return((AlgoValue)VisitCheck(context.check())); } else if (context.INVERT_SYM() != null) { //Boolean invert. AlgoValue evaled = (AlgoValue)VisitCheck(context.check()); //Get the boolean value for the evaluated value. bool evaledBool = AlgoComparators.GetBooleanValue(evaled, context); //Return the inverted bool. return(new AlgoValue() { Type = AlgoValueType.Boolean, Value = !evaledBool, }); } else { //Just a checkfrag. Evaluate and return. return((AlgoValue)VisitCheckfrag(context.checkfrag())); } }
//Binary OR. public static bool OR(ParserRuleContext context, AlgoValue left, AlgoValue right) { //Get value for left and right type. bool leftBV = GetBooleanValue(left, context); bool rightBV = GetBooleanValue(right, context); //Check if one of these are true. return(leftBV || rightBV); }
//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 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); }
//Convert an Algo list to JSON. public static string ListToJsonStr(ParserRuleContext context, AlgoValue listVal) { //Enumerate over object members, and add them to the string. string json = "["; foreach (var prop in (List <AlgoValue>)listVal.Value) { AppendValueString(context, prop, ref json); json += ", "; } json = json.Trim(',', ' ') + "]"; return(json); }
//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 an external or internal plugin library function is loaded. public override object VisitStat_loadFuncExt([NotNull] algoParser.Stat_loadFuncExtContext context) { //Get the value of the function. AlgoValue func = Plugins.GetEmulatedFuncValue(context); //Check if a variable with the supplied name already exists in scope. if (Scopes.VariableExists(context.IDENTIFIER()[0].GetText())) { Error.Fatal(context, "A variable with the name '" + context.IDENTIFIER()[0].GetText() + "' already exists, can't redefine it."); return(null); } //Add to scope. Scopes.AddVariable(context.IDENTIFIER()[0].GetText(), func); return(null); }
//When an error is manually thrown by a user. public override object VisitStat_throw([NotNull] algoParser.Stat_throwContext context) { //Evaluate the expression. AlgoValue throwStr = (AlgoValue)VisitExpr(context.expr()); //Is it a string? if (throwStr.Type != AlgoValueType.String) { Error.Fatal(context, "Error message to throw must be a string."); return(null); } //Throw the error. Error.Fatal(context, (string)throwStr.Value); 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); }
//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 term is parsed in Algo. public override object VisitTerm([NotNull] algoParser.TermContext context) { //Is it a multiplication? if (context.MUL_OP() != null) { //Multiplication. //Evaluate the left side (term). AlgoValue left = (AlgoValue)VisitTerm(context.term()); //Evaluate the right side. AlgoValue right = (AlgoValue)VisitFactor(context.factor()); //Perform multiplication. return(AlgoOperators.Mul(context, left, right)); } else if (context.DIV_OP() != null) { //Division. //Evaluate the left side (term). AlgoValue left = (AlgoValue)VisitTerm(context.term()); //Evaluate the right side. AlgoValue right = (AlgoValue)VisitFactor(context.factor()); //Perform division. return(AlgoOperators.Div(context, left, right)); } else if (context.MOD_OP() != null) { //Modulus. AlgoValue left = (AlgoValue)VisitTerm(context.term()); AlgoValue right = (AlgoValue)VisitFactor(context.factor()); //Perform modulus. return(AlgoOperators.Mod(context, left, right)); } else { //No, just a factor, evaluate. return((AlgoValue)VisitFactor(context.factor())); } //Error. throw new Exception("Invalid expression type."); }
//A "print" statement. public override object VisitStat_print([NotNull] algoParser.Stat_printContext context) { //Evaluate the expression. AlgoValue toPrint = (AlgoValue)VisitExpr(context.expr()); //Depending on the type, set string accordingly via. casts. string printString = ""; printString = AlgoConversion.GetStringRepresentation(toPrint); //Check if a rounding expression is present. if (context.rounding_expr() != null) { //Evaluate the rounding expression. AlgoValue roundingNum = (AlgoValue)VisitExpr(context.rounding_expr().expr()); //Check if it's an integer. if (roundingNum.Type != AlgoValueType.Integer) { Error.Warning(context, "Given rounding expression is not an integer, so can't round. Rounding was ignored."); } else { //Check if the rounding value is too large. if ((BigInteger)roundingNum.Value > int.MaxValue) { Error.Warning(context, "Given rounding number is too large to process, so rounding was ignored."); } else { //Round the actual value. int roundingInt = int.Parse(((BigInteger)roundingNum.Value).ToString()); toPrint = AlgoOperators.Round(context, toPrint, roundingInt); //Set print string. printString = AlgoConversion.GetStringRepresentation(toPrint); } } } //Print and return. Console.WriteLine(printString); return(null); }
//Override for checking whether AlgoValues are equal. public bool _Equals(AlgoValue obj) { //Check if value types are the same. if (Type != obj.Type) { //No. return(false); } //Are the actual values themselves the same? var objVal = obj.Value; switch (Type) { case AlgoValueType.String: return((string)Value == (string)objVal); case AlgoValueType.Boolean: return((bool)Value == (bool)objVal); case AlgoValueType.Float: return(((BigFloat)Value).Equals((BigFloat)objVal)); case AlgoValueType.Integer: return(((BigInteger)Value).Equals((BigInteger)objVal)); case AlgoValueType.List: return(AlgoComparators.ListsEqual(this, obj)); case AlgoValueType.Null: return(true); case AlgoValueType.Object: return(AlgoComparators.ObjectsEqual(this, obj)); case AlgoValueType.Rational: return(((BigRational)Value).Equals((BigRational)objVal)); default: Error.FatalNoContext("Cannot compare whether two " + Type.ToString() + " values are equal."); return(false); } }
public static void AppendValueString(ParserRuleContext context, AlgoValue prop, ref string json) { switch (prop.Type) { case AlgoValueType.Boolean: json += ((bool)prop.Value).ToString(); break; case AlgoValueType.Float: json += ((BigFloat)prop.Value).ToString(); break; case AlgoValueType.Integer: json += ((BigInteger)prop.Value).ToString(); break; case AlgoValueType.List: json += ListToJsonStr(context, prop); break; case AlgoValueType.Null: json += "null"; break; case AlgoValueType.Object: json += ObjToJsonStr(context, prop); break; case AlgoValueType.Rational: AlgoValue rational_as_float = new AlgoValue() { Type = AlgoValueType.Float, Value = RationalToFloat(prop) }; json += rational_as_float.ToString(); break; case AlgoValueType.String: json += "\"" + (string)prop.Value + "\""; break; } }
//When a value is returned from a function. public override object VisitStat_return([NotNull] algoParser.Stat_returnContext context) { //Evaluate the expression to return. AlgoValue toReturn; if (context.expr() != null) { toReturn = (AlgoValue)VisitExpr(context.expr()); } else { toReturn = new AlgoValue() { Type = AlgoValueType.Null, Value = null }; } //Return it. return(toReturn); }
//When a function is defined. public override object VisitStat_functionDef([NotNull] algoParser.Stat_functionDefContext context) { //Does a variable with this name already exist? if (Scopes.VariableExistsLowest(context.IDENTIFIER().GetText())) { Error.Fatal(context, "A variable with this name already exists, cannot create function with this name."); return(null); } //Getting parameters. List <string> params_ = new List <string>(); if (context.abstract_params() != null) { foreach (var param in context.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()); } } //No, it doesn't exist. Define it. AlgoFunction func = new AlgoFunction(context.statement().ToList(), params_, context.IDENTIFIER().GetText()); AlgoValue funcValue = new AlgoValue() { Type = AlgoValueType.Function, Value = func }; //Add to scope. Scopes.AddVariable(context.IDENTIFIER().GetText(), funcValue); return(null); }
//Converts an Algo Object to JSON string. public static string ObjToJsonStr(ParserRuleContext context, AlgoValue objVal) { //Enumerate over object members, and add them to the string. string json = "{ "; foreach (var prop in ((AlgoObject)objVal.Value).ObjectScopes.GetDeepestScope()) { json += "\"" + prop.Key + "\": "; AppendValueString(context, prop.Value, ref json); json += ", "; } json = json.Trim(',', ' ') + " }"; //Fix empty objects having a double space. if (json == "{ }") { json = "{}"; } //Return. return(json); }
//Checks whether two lists are equal to each other. public static bool ListsEqual(AlgoValue leftval, AlgoValue rightval) { //Casting. var left = (List <AlgoValue>)leftval.Value; var right = (List <AlgoValue>)rightval.Value; //Simple length check. if (left.Count != right.Count) { return(false); } //Loop over, check. for (int i = 0; i < left.Count; i++) { if (!left[i]._Equals(right[i])) { return(false); } } return(true); }
//When a single factor is parsed in Algo. public override object VisitFactor([NotNull] algoParser.FactorContext context) { //Is there a power? if (context.POW_OP() != null) { //Yes. //Evaluate left factor. AlgoValue left = (AlgoValue)VisitFactor(context.factor()); //Evaluate right sub. AlgoValue right = (AlgoValue)VisitSub(context.sub()); //Perform a power. return(AlgoOperators.Pow(context, left, right)); } else { //No, just evaluate the sub. return((AlgoValue)VisitSub(context.sub())); } //Error. throw new Exception("Invalid expression type."); }
//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 }); } }
//Returns the string representation of a given Algo value. public static string GetStringRepresentation(AlgoValue toPrint) { string printString = ""; switch (toPrint.Type) { case AlgoValueType.Null: printString = "null"; break; case AlgoValueType.Boolean: printString = ((bool)toPrint.Value).ToString().ToLower(); break; case AlgoValueType.Float: printString = ((BigFloat)toPrint.Value).ToString(); break; case AlgoValueType.Integer: printString = ((BigInteger)toPrint.Value).ToString(); break; case AlgoValueType.Rational: printString = ((BigRational)toPrint.Value).ToString(); break; case AlgoValueType.Bytes: printString = ((byte[])toPrint.Value).ToHexString(); break; case AlgoValueType.String: printString = (string)toPrint.Value; break; case AlgoValueType.List: //Evaluate each of the list values one by one. printString += '['; foreach (var listVal in (List <AlgoValue>)toPrint.Value) { printString += GetStringRepresentation(listVal) + ", "; } //Only trim commas if there are actually items. if (((List <AlgoValue>)toPrint.Value).Count > 0) { printString = printString.Substring(0, printString.Length - 2); } printString += ']'; break; case AlgoValueType.Object: printString += "{ "; foreach (var prop in (Dictionary <string, AlgoValue>)((AlgoObject)(toPrint.Value)).ObjectScopes.GetScopes().Last()) { printString += prop.Key + ": " + GetStringRepresentation(prop.Value) + ", "; } printString = printString.Trim(',', ' '); printString += " }"; return(printString); default: //Just return the type name. return("types." + toPrint.Type.ToString()); } return(printString); }
//When a file is imported. public override object VisitStat_import([NotNull] algoParser.Stat_importContext context) { //The following are checked for the parent library, in order: //1. Executing directory of the script + whatever referenced folder path. //2. Packages directory for Algo. //3. Standard libraries. //Make sure they're not trying to import multiple as a single identifier. if (context.literal_params().expr().Length > 1 && context.AS_SYM() != null) { Error.Warning(context, "Failed to import scripts, cannot import multiple scripts as a single identifier."); return(null); } //Import each. foreach (var item in context.literal_params().expr()) { //Getting directory tree text. string importLoc = ""; //Evaluating the statement to get dir text. AlgoValue locVal = (AlgoValue)VisitExpr(item); if (locVal.Type != AlgoValueType.String) { Error.Fatal(context, "Given file path to import was not a string."); return(null); } importLoc = (string)locVal.Value; List <string> fileParts = importLoc.Split('/').ToList(); //Append the extension to the end (imports don't require an extension). if (!fileParts[fileParts.Count - 1].EndsWith(".ag")) { fileParts[fileParts.Count - 1] = fileParts.Last() + ".ag"; } //Is the import being placed into a different scope? string importScope = ""; if (context.AS_SYM() != null) { //Yes, get the name of the scope. importScope = context.IDENTIFIER().GetText(); } //Test 1: Executing directory of the script. string[] dirParts = new string[] { Environment.CurrentDirectory }.Concat(fileParts).ToArray(); string dirToCheck = CPFilePath.GetPlatformFilePath(dirParts); //Is it there? if (File.Exists(dirToCheck)) { //Yes! Run the load function. RunAlgoScript(dirToCheck, importScope); continue; } //Nope. //Test 2: Packages directory for Algo. dirParts = DefaultDirectories.PackagesDirectory.Concat(fileParts).ToArray(); dirToCheck = CPFilePath.GetPlatformFilePath(dirParts); //Is it there? if (File.Exists(dirToCheck)) { //Yep, load it. RunAlgoScript(dirToCheck, importScope); continue; } //Nope. //Test 3: Standard libraries. dirParts = DefaultDirectories.StandardLibDirectory.Concat(fileParts).ToArray(); dirToCheck = CPFilePath.GetPlatformFilePath(dirParts); //Is it there? if (File.Exists(dirToCheck)) { //Yep, load it. RunAlgoScript(dirToCheck, importScope); continue; } //No, nowhere else to check from, so throw a linking warning. Error.Warning(context, "Failed to link the Algo script '" + importLoc + "'. It has not been loaded."); } return(null); }