/// <summary> /// Adds a TFunction to the function dictionary. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="function">The TFunction to be added to the function dictionary.</param> /// <returns>Null if the operation was successful, otherwise a TException.</returns> public static TException AddFunction(Interpreter interpreter, TFunction function) { if (function == null) { return(new TException(interpreter, "Null TFunction given")); } // Reject the function if it's already in the standard library TFunction existingFunction = StdLibrary.GetFunction(function.Name); if (existingFunction != null) { return(new TException(interpreter, "Standard library function with name '" + function.Name + "' already exists")); } // Add it to the function dictionary, overwriting another function with the same name if neccessary existingFunction = GetFunction(function.Name, false); if (existingFunction == null) { functions.Add(function.Name, function); } else { existingFunction.HardCodedFunction = null; existingFunction.CustomFunction = ""; existingFunction.Block = null; existingFunction.CopyFrom(function); } return(null); }
/// <summary> /// Copies the data from another TFunction into this TFunction. /// </summary> /// <param name="function">The existing TFunction whose data is to be copied into the new TFunction.</param> public void CopyFrom(TFunction function) { Name = function.Name; HardCodedFunction = function.HardCodedFunction; CustomFunction = function.CustomFunction; Block = function.Block; CopyArguments(function.ArgNames, function.DefaultArgs); }
/// <summary> /// A copy constructor or TFunction. /// </summary> /// <param name="function">The existing TFunction whose data is to be copied into the new TFunction.</param> public TFunction(TFunction function) { CopyFrom(function); }
/// <summary> /// Attempts to convert an object into a TType. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="obj">The object that is to be converted into a TType derivative.</param> /// <returns>An object of base class TType. If the operation was unsuccessful, null is returned.</returns> public static TType Parse(Interpreter interpreter, object obj) { if (obj is string) { string str = ((string)obj).Trim(); if (str == "") { return(TNil.Instance); } // Attempt to convert to a keyword first if ((str == "yes") || (str == "no")) { return(new TBoolean(str)); } if (str == "nil") { return(TNil.Instance); } if (str == "break") { return(TBreak.Instance); } { long result; if (long.TryParse(str, out result)) { return(new TInteger(result)); } } { double result; if (double.TryParse(str, out result)) { return(new TReal(result)); } } { // If first and last characters of string are '"', then return a TString if ((str.First() == STRING_CHARACTER) && (str.Last() == STRING_CHARACTER)) { return(new TString(str.Substring(1, str.Length - 2))); } } { // Attempt to find a function or variable with the name TType result = TFunction.GetFunction(str); if (result == null) { result = interpreter.Stack.FindVariable(str); if (result == null) { return(new TException(interpreter, "Variable or function with identifier '" + str + "' not found")); } } return(result); } } else if (obj is TType) { return((TType)obj); } else if (obj is Interpreter.Group) { return(interpreter.ParseGroup(obj as Interpreter.Group)); } return(null); }
/// <summary> /// Parses an assignment operation. /// </summary> /// <param name="group">The group to parse.</param> /// <returns>The assigned variable or function on success, otherwise a TException.</returns> TType ParseAssignmentGroup(Group group) { /* BNF for assignment: * <assignment> ::= 'let' ( <variable-assignment> | <function-declaration> ) * <var-assignment> ::= <identifier> '=' <expression> * <func-declaration> ::= <identifier> '(' { <parameters> } ')' '=' <statements> * <parameters> ::= <identifier> { ',' <identifier> }* * <statements> ::= <block> | <statement> * <block> ::= <new-line> (<statement> <new-line>)* 'end' * You can probably guess what <identifier>, <statement> and <new-line> are * The 'let' is assumed to have already been checked for */ int equalsIndex = group.IndexOf("="); if (equalsIndex < 0) return new TException(this, "Variable or function could not be assigned a value"); // Could be assigning a dereferenced variable TException exception = ParseReferencesOfGroup(group, equalsIndex); if (exception != null) return exception; if (group.Count == 1) return new TException(this, "Could not assign variable", "no variable name given"); string variableName = group[1] as string; TVariable existingVariable = null; if (variableName == null) { exception = new TException(this, "Could not assign variable", "invalid variable name given"); // Check if an existing variable or function is being assigned Group groupToParse = group[1] as Group; TType value = group[1] as TType; if (groupToParse != null) value = ParseGroup(groupToParse); if (value == null) return exception; TVariable variable = value as TVariable; if (variable != null) existingVariable = variable; else { TFunction function = value as TFunction; if (function != null) variableName = function.Name; else return exception; } } if (group.Count == 2) return new TException(this, "Variable could not be assigned a value"); string assignmentOperator = group[2] as string; if (assignmentOperator == null) // Now we assume that we're dealing with a function declaration { Group paramGroup = group[2] as Group; if (paramGroup == null) // The user probably just wanted to assign a variable but made a typo return new TException(this, "Variable could not be assigned a value", "value to assign to variable must be given"); // Get the identifiers of all the parameters within the brackets, keeping strict watch on comma usage TParameterList paramList = new TParameterList(); bool commaExpected = false; for (int i = 0; i < paramGroup.Count; ++i) { if (commaExpected && (i == paramGroup.Count - 1)) return new TException(this, "Parameters could not be parsed", "last parameter missing"); string paramName = paramGroup[i] as string; if (commaExpected && (paramName != ",")) paramName = null; if (paramName == null) return new TException(this, "Parameters could not be parsed", "invalid parameter name given"); if (!commaExpected) paramList.Add(paramName); commaExpected = !commaExpected; } exception = new TException(this, "Function could not be given a body", "function body must be given"); if (group.Count == 3) return exception; assignmentOperator = group[3] as string; if (assignmentOperator == null) return exception; else if (assignmentOperator != "=") return exception; TFunction function; if (group.Count == 4) // statement is just 'let <i><params> =', so get a block { TBlock block = new TBlock(this, out exception, false); if (exception != null) return exception; function = new TFunction(variableName ?? existingVariable.Identifier, block, paramList.ParameterNames, null); } else // Create a single line function { Group funcBody = new Group(null); funcBody.AddRange(group.GetRange(4, group.Count - 4)); function = new TFunction(variableName ?? existingVariable.Identifier, funcBody.ToString(), paramList.ParameterNames, null); } exception = TFunction.AddFunction(this, function); if (exception != null) return exception; return function; } { // Assume that we're dealing with a variable assigment exception = new TException(this, "Variable could not be assigned a value", "value to assign to variable must be given"); if (assignmentOperator != "=") return exception; if (group.Count == 3) return exception; // Parse the expression on the right hand side of the assigment operator, and if the result is a // TVariable, then get it's value (we don't want to assign the TVariable itself to the new TVariable) Group valueGroup = new Group(null); valueGroup.AddRange(group.GetRange(3, group.Count - 3)); TType value = ParseGroup(valueGroup); if (value is TException) return value; TVariable variable = value as TVariable; if (variable != null) value = variable.Value; // Make sure we don't get any circular references; they cause stack overflows when outputting them variable = existingVariable ?? Stack.FindVariable(variableName); if (value == variable) return new TException(this, "Illegal assignment attempted", "variables cannot reference themselves"); TVariable circularRefCheckVar = value as TVariable; while (circularRefCheckVar != null) { TVariable variableValue = circularRefCheckVar.Value as TVariable; if (variableValue != null) { if (variableValue == variable) return new TException(this, "Illegal assignment attempted", "circular reference detected"); else circularRefCheckVar = variableValue; } else circularRefCheckVar = null; } if (variable == null) // If the variable doesn't exist already, add it to the stack variable dictionary { variable = new TVariable(variableName, value); Stack.AddVariable(variable); } else variable.Value = value; return variable; } }
/// <summary> /// Adds a TFunction to the function dictionary. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="function">The TFunction to be added to the function dictionary.</param> /// <returns>Null if the operation was successful, otherwise a TException.</returns> public static TException AddFunction(Interpreter interpreter, TFunction function) { if (function == null) return new TException(interpreter, "Null TFunction given"); // Reject the function if it's already in the standard library TFunction existingFunction = StdLibrary.GetFunction(function.Name); if (existingFunction != null) return new TException(interpreter, "Standard library function with name '" + function.Name + "' already exists"); // Add it to the function dictionary, overwriting another function with the same name if neccessary existingFunction = GetFunction(function.Name, false); if (existingFunction == null) functions.Add(function.Name, function); else { existingFunction.HardCodedFunction = null; existingFunction.CustomFunction = ""; existingFunction.Block = null; existingFunction.CopyFrom(function); } return null; }