/// <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> /// Initializes a new instance of the SynchronousClrProcedure class. /// </summary> /// <param name="targetClassName">The class of the object to invoke.</param> /// <param name="methodName">The method to invoke.</param> /// <param name="instanceClass">The type of the instance argument. Null if static or constructor.</param> /// <param name="argClasses">The types of each argument.</param> /// <param name="caller">The calling evaluator.</param> private SynchronousClrProcedure(string targetClassName, string methodName, Type instanceClass, Type[] argClasses, Evaluator caller) : base(targetClassName, methodName, GetMethodInfo(targetClassName, methodName, argClasses, caller), instanceClass, argClasses, argClasses.Length) { }
/// <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> /// Initializes a new instance of the AsynchronousClrProcedure class. /// </summary> /// <param name="targetClassName">The class name of the CLR function.</param> /// <param name="methodName">The method name of the CLR function.</param> /// <param name="instanceClass">The type of the instance argument. Null if static or constructor.</param> /// <param name="argClasses">The types of all method arguments.</param> /// <param name="caller">The calling evaluator.</param> internal AsynchronousClrProcedure(string targetClassName, string methodName, Type instanceClass, Type[] argClasses, Evaluator caller) : base(targetClassName, methodName, GetMethodInfo(targetClassName, "Begin" + methodName, argClasses, caller), instanceClass, argClasses, argClasses.Length - 1) { var endClasses = new[] { typeof(IAsyncResult) }; this.endMethodInfo = GetMethodInfo(targetClassName, "End" + methodName, endClasses, caller); }
/// <summary> /// Closes the output port and returns the evaluation result. /// </summary> /// <param name="s">This evaluator.</param> /// <returns>The evaluation result.</returns> private static Evaluator CloseStep(Evaluator s) { var step = (EvaluateCallWithOutputFile)s; if (step.port != null) { step.port.Close(); } Evaluator caller = step.Caller; caller.ReturnedExpr = s.ReturnedExpr; return caller; }
/// <summary> /// Back here after expression evaluation. /// Increment and test the counter. /// If not done, loop back. /// Otherwise, calculate elapsed time and mem use. /// </summary> /// <param name="s">This evaluator.</param> /// <returns>Continue, or else give the timer results.</returns> protected static Evaluator CompleteStep(Evaluator s) { var step = (EvaluateTimeBase)s; step.i++; if (step.i < step.counter) { s.Pc = EvaluateStep; return s; } step.stopwatch.Stop(); long time = step.stopwatch.ElapsedMilliseconds; long mem = GC.GetTotalMemory(false) - step.startMem; Evaluator caller = step.Caller; caller.ReturnedExpr = MakeList( s.ReturnedExpr, MakeList((Number)time, (Symbol)"msec"), MakeList((Number)mem, (Symbol)"bytes")); return caller; }
/// <summary> /// Initializes a new instance of the Interpreter class. /// Create an interpreter and install the primitives into the global environment. /// Then read a list of files. /// </summary> /// <param name="loadStandardMacros">Load standard macros and other primitives.</param> /// <param name="primEnvironment">Environment containing the primitives (can be null).</param> /// <param name="files">The files to read.</param> /// <param name="reader">The input reader.</param> /// <param name="writer">The output writer.</param> private Interpreter( bool loadStandardMacros, PrimitiveEnvironment primEnvironment, IEnumerable<string> files, TextReader reader, TextWriter writer) { this.Trace = false; this.Count = false; this.Transcript = new TranscriptLogger(this); this.CurrentInputPort = InputPort.New(reader ?? Console.In, this); this.CurrentOutputPort = OutputPort.New(writer ?? Console.Out, this); this.PrimEnvironment = primEnvironment ?? new PrimitiveEnvironment(); this.CurrentCounters = new Counter(); this.GlobalEnvironment = new Environment(this, this.PrimEnvironment); this.halted = new HaltedEvaluator(this.GlobalEnvironment); var echo = (writer != null) ? this.CurrentOutputPort : null; try { if (loadStandardMacros) { this.Load(SchemePrimitives.Code); } if (files != null) { foreach (string file in files) { this.LoadFile((Symbol)file, echo); } } } catch (Exception ex) { ErrorHandlers.PrintException(ex); } }
/// <summary> /// Initializes a new instance of the EvaluateExpression class. /// At this point, fn is something that evaluates to a proc. /// It might be a symbol that, when looked up, resolves to a primitive or to a /// proc of some kind, like a lambda. /// It is not a symbol like "and" where there are special argument-evaluation rules, so /// the first task is to evaluate all the arguments. /// </summary> /// <param name="fn">The function to evaluate (the expression in the first position).</param> /// <param name="args">The function args to evaluate (the rest of the expressions in the list).</param> /// <param name="env">The evaluation environment</param> /// <param name="caller">The caller. Return to this when done.</param> private EvaluateExpression(SchemeObject fn, SchemeObject args, Environment env, Evaluator caller) : base(InitialStep, args, env, caller) { this.fn = fn; if (fn is Symbol) { // If the fun is a symbol, we can avoid the first step. this.Pc = ApplyProcStep; Call(fn, env, this); } else if (fn is Procedure) { // Or if the fun is already a procedure, skip the first step. this.Pc = ApplyProcStep; this.ReturnedExpr = fn; } }
/// <summary> /// Start by setting up timers and counter. /// </summary> /// <param name="s">This evaluator.</param> /// <returns>Continue to next step.</returns> private static Evaluator InitialStep(Evaluator s) { var step = (EvaluateTimeBase)s; step.i = 0; s.Pc = EvaluateStep; return s; }
/// <summary> /// Comes back here after evaluation completes synchronously or is suspended. /// If evaluation is suspended, then EvaluateExpressionWithCatch will catch and return undefined. /// Loop back and evaluate another expression. /// Continue looping until all evaluations return an actual result. /// Accumulate the results and return when everything finishes. /// </summary> /// <param name="s">This evaluator.</param> /// <returns>Immediately steps back.</returns> private static Evaluator LoopStep(Evaluator s) { var step = (EvaluateParallel)s; lock (step.lockObj) { step.FetchReturnValue(); switch (step.ReturnFlag) { case ReturnType.CaughtSuspended: // caught an asynchronous suspension -- go on to the next expr step.forked++; break; case ReturnType.AsynchronousReturn: // return after suspension // Record return result and either end the thread or // return from the whole thing. step.accum = Cons(step.ReturnedExpr, step.accum); step.joined++; if (step.joined < step.forked || !(s.Expr is EmptyList)) { return new FinalEvaluator(Undefined.Instance); } Evaluator caller = step.Caller; caller.ReturnedExpr = step.accum; return caller; case ReturnType.SynchronousReturn: // synchronous return step.accum = Cons(step.ReturnedExpr, step.accum); break; } s.Expr = Rest(s.Expr); if (s.Expr is EmptyList) { // finished with expressions -- either suspend (if there is more pending) or // return from the whole thing if (step.joined < step.forked) { s.Pc = LoopStep; return new SuspendedEvaluator(s.ReturnedExpr, s).NextStep(); } Evaluator caller = step.Caller; caller.ReturnedExpr = step.accum; return caller; } s.Pc = LoopStep; return EvaluateExpressionWithCatch.Call(First(s.Expr), s.Env, s); } }
/// <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> /// Open the output file and apply the proc. /// </summary> /// <param name="s">This evaluator.</param> /// <returns>The next step in the application, or if the result is ready, /// continues to the next step.</returns> private static Evaluator InitialStep(Evaluator s) { var step = (EvaluateCallWithOutputFile)s; var proc = Second(s.Expr); s.Pc = CloseStep; return ((Procedure)proc).Apply(MakeList(step.port), null, s, s); }
/// <summary> /// Initializes a new instance of the Continuation class. /// The evaluator and its chain of evaluators back to the beginning have to be cloned because they /// hold information about the progress of the evaluation. When the evaluation proceeds /// these evaluators might be altered, damaging the ability to continue, which is what makes the /// clone necessary. /// </summary> /// <param name="eval">The continuation to return to when applied.</param> /// <returns>A new continuation.</returns> public static Continuation New(Evaluator eval) { return new Continuation(eval); }
/// <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> /// 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> /// Look up the method info. /// </summary> /// <param name="className">The class name of the class to get method info about</param> /// <param name="theMethodName">The method name</param> /// <param name="argClassList">The argument types.</param> /// <param name="caller">The calling evaluator.</param> /// <returns>The method info for the method.</returns> protected static MethodInfo GetMethodInfo( string className, string theMethodName, Type[] argClassList, Evaluator caller) { // TODO use caller to get line number caller.FindLineNumberInCallStack(); try { Type cls = className.ToClass(); if (cls == null) { ErrorHandlers.ClrError(string.Format("Can't find class: {0} at line {1}", className, caller.FindLineNumberInCallStack())); return null; } MethodInfo info = cls.GetMethod(theMethodName, argClassList); if (info == null) { ErrorHandlers.ClrError(string.Format("Can't find method: {0}:{1} at line {2}", className, theMethodName, caller.FindLineNumberInCallStack())); return null; } return info; } catch (TypeLoadException) { ErrorHandlers.ClrError(string.Format("Bad class, can't load: {0} at line {1}", className, caller.FindLineNumberInCallStack())); return null; } }
/// <summary> /// Execute the constructor. /// Match all arguments supplied up to the constructor's types. /// </summary> /// <param name="args">Arguments to pass to the constructor.</param> /// <param name="env">The environment of the evaluation.</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 excute.</returns> internal override Evaluator Apply(SchemeObject args, Environment env, Evaluator returnTo, Evaluator caller) { #if Check this.CheckArgCount(ListLength(args), args, "ClrConstructor", caller); #endif Assembly assembly = this.classType.Assembly; object[] argArray = this.ToArgList(args, null, "ClrConstructor", caller); object res = assembly.CreateInstance(this.classType.FullName, false, BindingFlags.Default, null, argArray, null, null); returnTo.ReturnedExpr = ClrObject.New(res); return returnTo; }
/// <summary> /// Initial step: evaluate the first expression. /// Instead of calling normal EvaluateExpression, call a variant that catches suspended /// execution and halts the evaluation. /// </summary> /// <param name="s">This evaluator.</param> /// <returns>The next evaluator.</returns> private static Evaluator InitialStep(Evaluator s) { s.Pc = LoopStep; return EvaluateExpressionWithCatch.Call(First(s.Expr), s.Env, s); }
/// <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 excute.</returns> internal override Evaluator Apply(SchemeObject args, Environment env, Evaluator returnTo, Evaluator caller) { SchemeObject target = null; if (!this.MethodInfo.IsStatic) { target = First(args); args = Rest(args); } #if Check this.CheckArgCount(ListLength(args), args, "SynchronousClrProcedure", caller); #endif var actualTarget = ClrObject.ToClrObject(target, this.InstanceClass); var argList = this.ToArgList(args, null, "SynchronousClrProcedure", caller); object res = this.MethodInfo.Invoke(actualTarget, argList); res = res ?? Undefined.Instance; returnTo.ReturnedExpr = ClrObject.FromClrObject(res); return returnTo; }
/// <summary> /// Exit the whole process. /// </summary> /// <param name="args">If given, the process exit code.</param> /// <param name="caller">The calling evaluator (not used).</param> /// <returns>Does not return.</returns> private static SchemeObject Exit(SchemeObject args, Evaluator caller) { System.Environment.Exit(List.First(args) is EmptyList ? 0 : Number.AsInt(List.First(args))); return Undefined.Instance; }
/// <summary> /// Subclass must provide an implementation. /// This evaluates the expression, in a way that is appropriate /// to the function (eval or apply). /// </summary> /// <param name="s">This evaluator.</param> /// <returns>The next step.</returns> protected static Evaluator EvaluateStep(Evaluator s) { return ((EvaluateTimeBase)s).EvaluateStep(); }
/// <summary> /// Initializes a new instance of the Continuation class. /// The evaluator and its chain of evaluators back to the beginning have to be cloned because they /// hold information about the progress of the evaluation. When the evaluation proceeds /// these evaluators might be altered, damaging the ability to continue, which is what makes the /// clone necessary. /// </summary> /// <param name="eval">The continuation to return to when applied.</param> private Continuation(Evaluator eval) : base(null, new ArgsInfo(1, 1, false)) { this.savedEvaluator = eval.CloneChain(); }
/// <summary> /// Increment the variable. /// The intention is that this should be atomic. /// Used by increment! primitive. /// </summary> /// <param name="expr">The symbol whose value is incremented.</param> /// <param name="env">The environment.</param> /// <param name="caller">Return to this caller.</param> /// <returns>The next step to execute.</returns> private static Evaluator Increment(SchemeObject expr, Environment env, Evaluator caller) { SchemeObject lhs = First(expr); if (!(lhs is Symbol)) { ErrorHandlers.SemanticError(string.Format(@"Increment: first argument must be a symbol. Got: ""{0}""", lhs), null); } caller.ReturnedExpr = env.Increment(lhs); return caller; }
/// <summary> /// Start evaluation by testing the various forms. /// The special forms have their own evaluators. /// Otherwise, evaluate the first argument (the proc) in preparation for a call. /// </summary> /// <param name="s">This evaluator.</param> /// <returns>The next thing to do.</returns> private static Evaluator InitialStep(Evaluator s) { var step = (EvaluateExpression)s; // If we get here, it wasn't one of the special forms. // So we need to evaluate the first item (the function) in preparation for // doing a procedure call. //// <r4rs section="4.1.3">(<operator> <operand1> ...)</r4rs> s.Pc = ApplyProcStep; return Call(step.fn, s.Env, s); }
/// <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> /// 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> /// Perform steps until evaluation is complete or suspended. /// Calling "a subroutine" does not recursively call EvalSteps. /// A "call" is handled by creating a new evaluator and passing a step within it as the nextStep. /// A "return" is handled by return a step in the "caller" as the next step. /// </summary> /// <param name="evaluator">The evaluator to execute first.</param> /// <returns>The evaluation result, or suspended evaluator.</returns> internal SchemeObject EvalSteps(Evaluator evaluator) { while (true) { // Get the PC from the evaluator and call the action it points to, passing it the evaluator instance. evaluator = evaluator.Pc(evaluator); if (evaluator.Finished) { return evaluator.ReturnedExpr; } } }
/// <summary> /// Evaluate a quote expression. /// </summary> /// <param name="args">The args to quote.</param> /// <param name="env">The environment (unused).</param> /// <param name="caller">The caller.</param> /// <returns>The quoted expression.</returns> private static Evaluator EvalQuote(SchemeObject args, Environment env, Evaluator caller) { caller.ReturnedExpr = First(args); return caller; }
/// <summary> /// Initializes a new instance of the EvaluateParallel class. /// </summary> /// <param name="expr">The expressions to evaluate.</param> /// <param name="env">The evaluation environment</param> /// <param name="caller">The caller. Return to this when done.</param> private EvaluateParallel(SchemeObject expr, Environment env, Evaluator caller) : base(InitialStep, expr, env, caller) { this.forked = this.joined = 0; this.accum = EmptyList.Instance; }