/// <summary> /// Initializes a new instance of the EvaluateTimeBase class. /// </summary> /// <param name="expr">The expression to evaluate.</param> /// <param name="count">The number of times to evaluate.</param> /// <param name="env">The evaluation environment</param> /// <param name="caller">The caller. Return to this when done.</param> protected EvaluateTimeBase(SchemeObject expr, int count, Environment env, Evaluator caller) : base(InitialStep, expr, env, caller) { this.counter = count; this.startMem = GC.GetTotalMemory(true); this.stopwatch = Stopwatch.StartNew(); }
/// <summary> /// Create a new list of the given length, where each element is given. /// </summary> /// <param name="n">The length of the list.</param> /// <param name="fill">The element to place in each list element.</param> /// <returns>A list filled with the given element.</returns> public static SchemeObject Fill(int n, SchemeObject fill) { SchemeObject res = EmptyList.Instance; for (int i = 0; i < n; i++) { res = Cons(fill, res); } return res; }
/// <summary> /// Execute the continuation. /// Transfers execution to the evaluator saved when the continuation was created. /// The environment in effect at that time is also restored. /// Again, the chain of steps back to the beginning need to be clones so that this application /// does not alter the evaluation, making it impossible to return back to the continuation. /// </summary> /// <param name="args">The value to return.</param> /// <param name="env"></param> /// <param name="returnTo">The evaluator to return to. This can be different from caller if this is the last step in evaluation</param> /// <param name="caller">The calling evaluator. Not used, since control is transferred away.</param> /// <returns>The next evaluator to execute.</returns> internal override Evaluator Apply(SchemeObject args, Environment env, Evaluator returnTo, Evaluator caller) { #if Check this.CheckArgCount(ListLength(args), args, "Continuation", caller); #endif Evaluator nextStep = this.savedEvaluator.CloneChain(); nextStep.ReturnedExpr = First(args); nextStep.ReturnedEnv = this.savedEvaluator.Env; return nextStep; }
/// <summary> /// Call the parallel evaluator. /// </summary> /// <param name="expr">The expressions to evaluate.</param> /// <param name="env">The environment to evaluate in.</param> /// <param name="caller">The caller. Return to this when done.</param> /// <returns>The parallel evaluator.</returns> internal static Evaluator Call(SchemeObject expr, Environment env, Evaluator caller) { if (expr is EmptyList) { caller = caller.Caller; caller.ReturnedExpr = EmptyList.Instance; return caller; } return new EvaluateParallel(expr, env, caller); }
/// <summary> /// Open a file for input. /// </summary> /// <param name="filename">The filename of the file to open.</param> /// <param name="interp">The interpreter.</param> /// <returns>The input port, used for reading.</returns> internal static InputPort OpenInputFile(SchemeObject filename, Interpreter interp) { try { return InputPort.New(new StreamReader(filename.ToString(false)), interp); } catch (FileNotFoundException) { return (InputPort)ErrorHandlers.IoError("No such file: " + filename.ToString(true)); } catch (IOException ex) { return (InputPort)ErrorHandlers.IoError("IOException: " + ex.Message); } }
/// <summary> /// Make a copy of the given expression. /// If the expression is not a list, then just return it. /// This is used in connection with destructive operations where the original values /// might need to be preserved. /// </summary> /// <param name="expr">The list to copy.</param> /// <returns>A copy of the list.</returns> public static SchemeObject Copy(SchemeObject expr) { // TODO is this needed? if (!(expr is Pair)) { return expr; } Pair result = MakeList(); Pair accum = result; // Iterate down the list, building a list of the results. while (expr is Pair) { accum = accum.SetRest(Cons(First(expr), EmptyList.Instance)); expr = Rest(expr); } return Rest(result); }
/// <summary> /// Take a list of CLR method arguments and turn them into an array, suitable for /// calling the method. /// Add the extra async arguments for the Begin call. /// Check to make sure the number and types of the arguments in the list match /// what is expected. /// </summary> /// <param name="args">A list of the method arguments.</param> /// <param name="state">State, passed on to completion function.</param> /// <param name="caller">The calling evaluator.</param> /// <returns>An array of arguments for the method call.</returns> private object[] ToArgListBegin(SchemeObject args, object state, Evaluator caller) { object[] additionalArgs = { (AsyncCallback)this.CompletionMethod, state }; return this.ToArgList(args, additionalArgs, "AsynchronousClrProcedure", caller); }
/// <summary> /// Begin an asynchronous evaluation /// </summary> /// <param name="expr">The expression to evaluate.</param> /// <param name="cb">Call this when evaluation is complete.</param> /// <param name="state">Pass this through for the callback function.</param> /// <returns>Async result, used to monitor progress.</returns> private IAsyncResult UnsafeBeginEval(SchemeObject expr, AsyncCallback cb, object state) { this.asyncResult = new AsyncResult<SchemeObject>(cb, state); SchemeObject res = this.Eval(expr, this.GlobalEnvironment); if ((res is ClrObject) && ((ClrObject)res).Value is SuspendedEvaluator) { return this.asyncResult; } if (!this.asyncResult.IsCompleted) { this.asyncResult.SetAsCompleted(res, true); } return this.asyncResult; }
/// <summary> /// Evaluate an expression (expressed as a list) in the global environment. /// </summary> /// <param name="expr">The expression to evaluate.</param> /// <returns>The result of the evaluation.</returns> private SchemeObject UnsafeEval(SchemeObject expr) { return this.Eval(expr, this.GlobalEnvironment); }
/// <summary> /// Evaluate an expression in an environment. /// Do it by executing a set of steps. /// </summary> /// <param name="expr">The expression to evaluate.</param> /// <param name="env">The environment in which to evaluate it.</param> /// <returns>The result of the evaluation.</returns> internal SchemeObject Eval(SchemeObject expr, Environment env) { return this.EvalSteps(EvaluateExpression.Call(expr, env, this.halted)); }
/// <summary> /// Set the asynchronous completion value, if we were /// doing an async call. /// </summary> /// <param name="returnedExpr">The value to return as the asynchronous result.</param> internal void SetComplete(SchemeObject returnedExpr) { if (this.asyncResult != null) { this.asyncResult.SetAsCompleted(returnedExpr, false); } }
/// <summary> /// Take a list of ArgType or type name elements and create a corresponding /// List of ArgType. /// </summary> /// <param name="args">A list of ArgType or type name elements. There may be more than this.</param> /// <param name="extra">Number of extra types to allocate.</param> /// <returns>An array of Types corresponding to the list.</returns> protected static Type[] ClassList(SchemeObject args, int extra = 0) { var array = new Type[ListLength(args) + extra]; int i = 0; while (args is Pair) { array[i++] = Class(First(args)); args = Rest(args); } return array; }
/// <summary> /// Create a printable representation of the object. /// </summary> /// <param name="obj">The object to print.</param> /// <returns>The string representing the object.</returns> public string Print(SchemeObject obj) { try { return obj.ToString(true); } catch (Exception ex) { ErrorHandlers.PrintException(ex); return string.Empty; } }
/// <summary> /// Write a character on a given port. /// The output is not quoted. /// If the expr is not actually a character, it is written nevertheless. /// </summary> /// <param name="expr">The expression to write.</param> /// <returns>The undefined object.</returns> private SchemeObject WriteChar(SchemeObject expr) { this.Write(expr.ToString(false)); return Undefined.Instance; }
/// <summary> /// Initializes a new instance of the EvaluateCallWithOutputFile class. /// </summary> /// <param name="args">A pair, containing a filename and a proc to evaluate.</param> /// <param name="env">The evaluation environment</param> /// <param name="caller">The caller. Return to this when done.</param> /// <param name="port">The output port.</param> private EvaluateCallWithOutputFile(SchemeObject args, Environment env, Evaluator caller, OutputPort port) : base(InitialStep, args, env, caller) { this.port = port; }
/// <summary> /// Take a list of CLR method arguments and turn them into a list, suitable for /// calling the method. /// Check to make sure the number and types of the arguments in the list match /// what is expected. /// </summary> /// <param name="args">A list of the method arguments.</param> /// <param name="additionalArgs">A list of the additional args, not supplied by the caller. /// These are part of the asynchronous calling pattern.</param> /// <param name="evaluatorName">The evaluator name, for the error message.</param> /// <param name="caller">The calling evaluator.</param> /// <returns>An array of arguments for the method call.</returns> protected object[] ToArgList(SchemeObject args, object[] additionalArgs, string evaluatorName, Evaluator caller) { // This has been checked once already, in Procedure.CheckArgCount, but that included // an instance argument. Check again to protect the rest of this method. int additionalN = additionalArgs != null ? additionalArgs.Length : 0; int numArgs = ListLength(args) + additionalN; int expectedArgs = this.ArgClasses.Length; if (numArgs != expectedArgs) { this.ArgCountError(numArgs, expectedArgs, args, evaluatorName, caller); } var array = new object[numArgs]; int a = 0; while (args is Pair) { array[a] = ClrObject.ToClrObject(First(args), this.ArgClasses[a]); a++; args = Rest(args); } for (int i = 0; i < additionalN; i++) { Debug.Assert(additionalArgs != null, "ClrProcedure: additionalArgs != null"); array[a++] = additionalArgs[i]; } return array; }
/// <summary> /// Determine the port object to use with the OutputPort primitives. /// The port is optional: if supplied, it is the port to use. /// Otherwise, the current output port is used instead. /// </summary> /// <param name="port">The port to use, if supplied. If is is not empty list, then it is an output port.</param> /// <param name="curr">The current output port.</param> /// <returns>The port to use.</returns> private static OutputPort Port(SchemeObject port, OutputPort curr) { return port is EmptyList ? curr : (OutputPort)port; }
/// <summary> /// Take a list of ArgType or type name elements and create a corresponding /// array of ArgType. /// Add the extra async arguments for a Begin method. /// </summary> /// <param name="args">A list of ArgType or type name elements.</param> /// <returns>An array of Types corresponding to the list.</returns> private static Type[] ClassListBegin(SchemeObject args) { Type[] array = ClassList(args, 2); array[array.Length - 2] = typeof(AsyncCallback); array[array.Length - 1] = typeof(object); return array; }
/// <summary> /// Clear the token buffer. /// </summary> private void Clear() { this.present = false; this.buffer = null; }
/// <summary> /// Push the character into the buffer. /// </summary> /// <param name="token">The token to push.</param> internal void Push(SchemeObject token) { this.present = true; this.buffer = token; }
/// <summary> /// Push a token into the buffer. /// </summary> /// <param name="token">The token to push.</param> internal void PushToken(SchemeObject token) { this.tokBuffer.Push(token); }
/// <summary> /// Create an array of objects. /// These can throw a variety of exceptions. Let them propogate up and be caught at /// a higher level. /// </summary> /// <param name="className">The class name of the array elements.</param> /// <param name="length">The array length.</param> /// <returns>An array of the given length.</returns> private static object NewArray(string className, SchemeObject length) { return Array.CreateInstance(Class(className), Number.AsInt(length)); }
/// <summary> /// Convert an scheme object to the given type of clr object. /// </summary> /// <param name="elem">The object to convert.</param> /// <param name="clrClass">The desired clr class to convert it to.</param> /// <returns>The converted object.</returns> public static object ToClrObject(SchemeObject elem, Type clrClass) { if (elem is ClrObject) { var value = ((ClrObject)elem).Value; if (value.GetType() == clrClass) { return value; } } if (clrClass != null && toClrMap.ContainsKey(clrClass)) { return toClrMap[clrClass](elem); } return elem; }
/// <summary> /// Apply the method to the given arguments. /// If the method is static, all arguments are passed to the method. /// Otherwise, the first argument is the class instance, and the rest are passed /// to the method. /// </summary> /// <param name="args">Arguments to pass to the method.</param> /// <param name="env">The environment of the application.</param> /// <param name="returnTo">The evaluator to return to. This can be different from caller if this is the last step in evaluation</param> /// <param name="caller">The calling evaluator.</param> /// <returns>The next evaluator to execute.</returns> internal override Evaluator Apply(SchemeObject args, Environment env, Evaluator returnTo, Evaluator caller) { #if Check this.CheckArgCount(ListLength(args), args, "AsynchronousClrProcedure", caller); #endif SchemeObject target = null; if (!this.MethodInfo.IsStatic) { target = First(args); args = Rest(args); } var actualTarget = ClrObject.ToClrObject(target, this.InstanceClass); var argArray = this.ToArgListBegin(args, new Tuple<object, Evaluator>(actualTarget, returnTo), caller); var res = this.MethodInfo.Invoke(actualTarget, argArray) as IAsyncResult; // res is not converted because it is IAsyncResult -- convert in completion method return new SuspendedEvaluator(ClrObject.New(res), returnTo).NextStep(); }
/// <summary> /// Print the obj on the console. /// </summary> /// <param name="x">The obj to print.</param> /// <returns>Undefined value.</returns> private SchemeObject P(SchemeObject x) { this.WriteLine(x.ToString(false)); return Undefined.Instance; }
/// <summary> /// Begin an asynchronous evaluation. /// Catch any exceptions that might happen. /// </summary> /// <param name="expr">The expression to evaluate.</param> /// <param name="cb">Call this when evaluation is complete.</param> /// <param name="state">Pass this through for the callback function.</param> /// <returns>Async result, used to monitor progress.</returns> public IAsyncResult BeginEval(SchemeObject expr, AsyncCallback cb, object state) { this.asyncResult = null; try { return this.UnsafeBeginEval(expr, cb, state); } catch (Exception ex) { ErrorHandlers.PrintException(ex); return null; } }
/// <summary> /// Convert to text writer /// </summary> /// <param name="obj">The object.</param> /// <returns>The text writer.</returns> internal static TextWriter AsTextWriter(SchemeObject obj) { if (obj is OutputPort) { return ((OutputPort)obj).Writer; } ErrorHandlers.TypeError(typeof(OutputPort), obj); return null; }
/// <summary> /// Evaluate an expression (expressed as a list) in the global environment. /// Catch any exceptions that may happen. /// </summary> /// <param name="expr">The expression to evaluate.</param> /// <returns>The result of the evaluation.</returns> public SchemeObject Eval(SchemeObject expr) { this.asyncResult = null; try { return this.UnsafeEval(expr); } catch (Exception ex) { ErrorHandlers.PrintException(ex); return Undefined.Instance; } }
/// <summary> /// Create an evaluator with output file. /// </summary> /// <param name="args">A pair, containing a filename and a proc to evaluate.</param> /// <param name="caller">The caller. Return to this when done.</param> /// <returns>The created evaluator.</returns> internal static Evaluator Call(SchemeObject args, Evaluator caller) { OutputPort port = OpenOutputFile(First(args), caller.Interp); return new EvaluateCallWithOutputFile(args, caller.Env, caller, port); }
/// <summary> /// Load a file. /// Open the file and read it. /// Evaluate whatever it contains. /// This may be one or more expressions. /// If any of them are asynchronous, then the evaluation is NOT blocked, but continues on. /// </summary> /// <param name="fileName">The filename.</param> /// <param name="outp">If not null, input and results are written here.</param> public void LoadFile(SchemeObject fileName, OutputPort outp) { string name = string.Empty; try { name = fileName.ToString(); using (var fs = new FileStream(name, FileMode.Open, FileAccess.Read)) { this.Load(InputPort.New(new StreamReader(fs), this), outp); } } catch (IOException) { ErrorHandlers.IoError("Can't load " + name); } }