/// <summary> /// The method that represents the 'exit' function in the language. /// Exits from the current function, returning any value passed to the function. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="args">The arguments being passed to the function as a TArgumentList.</param> /// <returns>The argument passed to the function. If no arguments were passed, it returns TNil.</returns> public static TType Exit(Interpreter interpreter, TArgumentList args) { // Get the argument passed (if any) to return TType returnValue = TNil.Instance; if (args.Count > 0) returnValue = args[0]; // Pops from the function stack or kill the interpreter if the stack level is already at it's lowest // When this method returns, the calling method should check the stack level and exit accordingly if (interpreter.Stack.Level <= 1) interpreter.Kill(); else interpreter.Stack.Pop(); return returnValue; }
/// <summary> /// Appends a TType to this TArgumentList. If a TArgumentList is passed, then the values contained in that /// TArgumentList are appended to this TArgumentList. If the values to be appended are TVariables, then the /// value of the TVariable will be appended instead of the TVariable itself (although if TVariable A /// containing TVariable B is passed, then TVariable B will be appended, as opposed to TVariable B's value). /// </summary> /// <param name="argument">The TType to append.</param> public void Add(TType argument) { TArgumentList argList = argument as TArgumentList; if (argList == null) { TVariable variable = argument as TVariable; if (variable == null) { arguments.Add(argument); } else { arguments.Add(variable.Value); } } else { for (int i = 0; i < argList.Count; ++i) { arguments.Add(argList[i]); } } }
/// <summary> /// Calls the TFunction with the specified arguments. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="value"> /// The argument to pass to the function. When passing multiple arguments, use a TArgumentList. /// </param> /// <returns></returns> public TType Call(Interpreter interpreter, TType value) { // If value is already a TArgumentList, then simply copy the reference, otherwise create a new // TArgumentList and add the value to it. This TArgument list is to be passed to the function. TArgumentList argList = value as TArgumentList; if (argList == null) { argList = new TArgumentList(); argList.Add(value); } // If the function takes a fixed number of arguments... if (ArgNames != null) { // Occupy the argument list with the default arguments. If there is no default argument in a place // where an argument should have been given, return a TException. if (argList.Count < ArgNames.Length) { for (int i = argList.Count; i < DefaultArgs.Length; ++i) { if (DefaultArgs[i] == null) { break; } else { argList.Add(DefaultArgs[i]); } } } if (argList.Count != ArgNames.Length) { return(new TException(interpreter, "Incorrect number of arguments for function '" + Name + "'", argList.Count.ToString() + " out of " + ArgNames.Length.ToString() + " given")); } } interpreter.Stack.Push(); // Keep a track of the new stack level so that if a function calls 'exit()', which pops from the stack, // this will be able to be detected and the function call can be terminated properly int stackLevel = interpreter.Stack.Level; // Put the arguments on the current stack 'frame' if (ArgNames == null) { for (int i = 0; i < argList.Count; ++i) { interpreter.Stack.AddVariable(new TVariable("arg" + i.ToString(), argList[i])); } } else { for (int i = 0; i < argList.Count; ++i) { interpreter.Stack.AddVariable(new TVariable(ArgNames[i], argList[i])); } } TType returnValue = null; bool dontPop = false; // Set to true if the stack is popped during the call, i.e. if 'exit()' is called // Call the function if (HardCodedFunction != null) { returnValue = HardCodedFunction.Invoke(interpreter, argList); } else if (Block != null) { bool breakUsed; returnValue = Block.Execute(interpreter, out dontPop, out breakUsed); } else if (CustomFunction != "") { returnValue = interpreter.Interpret(CustomFunction, true); if (interpreter.Stack.Level < stackLevel) { dontPop = true; } } // If returnValue is a TVariable, then return the value of the TVariable (e.g. we want to return 5, as // opposed to the variable X which contains 5) TVariable variable = returnValue as TVariable; if (variable != null) { returnValue = variable.Value; } if (!dontPop) { interpreter.Stack.Pop(); } return(returnValue ?? TNil.Instance); }
/// <summary> /// Parses the commas in the group, converting a comma separated list into a TArgumentList, and replacing the /// list in the group with it. /// </summary> /// <param name="group">The group to parse.</param> /// <returns>A TException on failure, otherwise null.</returns> TException ParseCommasOfGroup(Group group) { int index; while ((index = group.IndexOf(",")) >= 0) { if ((index - 1 < 0) || (index + 1 >= group.Count)) return new TException(this, "Invalid expression term ','"); TType a = TType.Parse(this, group[index - 1]); if (a is TException) return a as TException; TType b = TType.Parse(this, group[index + 1]); if (b is TException) return b as TException; TArgumentList argList = a as TArgumentList; if (argList == null) { argList = new TArgumentList(); argList.Add(a); argList.Add(b); } else argList.Add(b); group[index - 1] = argList; group.RemoveRange(index, 2); } return null; }
/// <summary> /// The method that represents the 'load' function in the language. /// Loads a script file with the specified file name (given by a TString) and runs it. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="args">The arguments being passed to the function as a TArgumentList.</param> /// <returns>TNil</returns> public static TType Load(Interpreter interpreter, TArgumentList args) { TString fileName = args[0] as TString; if (fileName == null) return new TException(interpreter, "Name of file to load must be given as a string"); interpreter.LoadFile(fileName.Value); return TNil.Instance; }
/// <summary> /// The method that represents the 'read_string' function in the language. /// Reads in a string returns it as a TString. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="args">The arguments being passed to the function as a TArgumentList.</param> /// <returns>An TString representing the string that was entered.</returns> public static TType ReadString(Interpreter interpreter, TArgumentList args) { return new TString(System.Console.ReadLine()); }
/// <summary> /// The method that represents the 'read' function in the language. /// Reads in a string and converts it into the most suitable TType. /// If the string cannot be converted, it just returns the string as a TString. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="args">The arguments being passed to the function as a TArgumentList.</param> /// <returns>An TType of the type that the entered string represents best.</returns> public static TType Read(Interpreter interpreter, TArgumentList args) { // Read in a string and convert it to a suitable TType with TType.Parse. string str = System.Console.ReadLine(); TType value = TType.Parse(interpreter, str); if (value is TException) value = new TString(str); // If the string can't be parsed, return it as a TString return value; }
/// <summary> /// The method that represents the 'random' function in the language. /// Generates a random TInteger between 0 and the given value - 1. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="args">The arguments being passed to the function as a TArgumentList.</param> /// <returns>An TInteger with a value between 0 and the given value - 1</returns> public static TType Random(Interpreter interpreter, TArgumentList args) { // Check if the argument passed is a number, and return a new TInteger from the random value generated TNumber maximum = args[0] as TNumber; if (maximum == null) return new TException(interpreter, "Arguments of type '" + args[0].TypeName + "' cannot be used by random function"); return new TInteger(randomNumberGenerator.Next((int)maximum.TIntegerValue)); }
/// <summary> /// The method that represents the 'print' function in the language. /// Takes any number of values and writes their string values. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="args">The arguments being passed to the function as a TArgumentList.</param> /// <returns>TNil</returns> public static TType Print(Interpreter interpreter, TArgumentList args) { // Loop through arguments, append their string values to a StringBuilder, and output the StringBuilder StringBuilder str = new StringBuilder(); for (int i = 0; i < args.Count; ++i) { TString arg = args[i] as TString; if (arg == null) str.Append(args[i].ToCSString()); else str.Append(arg.Value); } System.Console.WriteLine(str); return TNil.Instance; }
/// <summary> /// Calls the TFunction with the specified arguments. /// </summary> /// <param name="interpreter">The interpreter that the method is being called from.</param> /// <param name="value"> /// The argument to pass to the function. When passing multiple arguments, use a TArgumentList. /// </param> /// <returns></returns> public TType Call(Interpreter interpreter, TType value) { // If value is already a TArgumentList, then simply copy the reference, otherwise create a new // TArgumentList and add the value to it. This TArgument list is to be passed to the function. TArgumentList argList = value as TArgumentList; if (argList == null) { argList = new TArgumentList(); argList.Add(value); } // If the function takes a fixed number of arguments... if (ArgNames != null) { // Occupy the argument list with the default arguments. If there is no default argument in a place // where an argument should have been given, return a TException. if (argList.Count < ArgNames.Length) { for (int i = argList.Count; i < DefaultArgs.Length; ++i) { if (DefaultArgs[i] == null) break; else argList.Add(DefaultArgs[i]); } } if (argList.Count != ArgNames.Length) { return new TException(interpreter, "Incorrect number of arguments for function '" + Name + "'", argList.Count.ToString() + " out of " + ArgNames.Length.ToString() + " given"); } } interpreter.Stack.Push(); // Keep a track of the new stack level so that if a function calls 'exit()', which pops from the stack, // this will be able to be detected and the function call can be terminated properly int stackLevel = interpreter.Stack.Level; // Put the arguments on the current stack 'frame' if (ArgNames == null) { for (int i = 0; i < argList.Count; ++i) interpreter.Stack.AddVariable(new TVariable("arg" + i.ToString(), argList[i])); } else { for (int i = 0; i < argList.Count; ++i) interpreter.Stack.AddVariable(new TVariable(ArgNames[i], argList[i])); } TType returnValue = null; bool dontPop = false; // Set to true if the stack is popped during the call, i.e. if 'exit()' is called // Call the function if (HardCodedFunction != null) returnValue = HardCodedFunction.Invoke(interpreter, argList); else if (Block != null) { bool breakUsed; returnValue = Block.Execute(interpreter, out dontPop, out breakUsed); } else if (CustomFunction != "") { returnValue = interpreter.Interpret(CustomFunction, true); if (interpreter.Stack.Level < stackLevel) dontPop = true; } // If returnValue is a TVariable, then return the value of the TVariable (e.g. we want to return 5, as // opposed to the variable X which contains 5) TVariable variable = returnValue as TVariable; if (variable != null) returnValue = variable.Value; if (!dontPop) interpreter.Stack.Pop(); return returnValue ?? TNil.Instance; }