/// <summary> /// REPL-execute a given line in an interpreter, /// indicating whether it was incomplete, and /// restoring the interpreter afterwards. /// </summary> /// <param name="interp"> /// Interpreter to use /// </param> /// <param name="obj"> /// Module object where method declarations will be added. /// </param> /// <param name="memo"> /// Restoration point for the interpreter context. /// </param> /// <param name="line"> /// Line of code to execute. /// </param> /// <param name="unfinished"> /// Set to true if this line was incomplete and could not /// be executed for that reason. /// </param> /// <param name="result"> /// Result of the executed expression. /// </param> public static int RunLine(Interpreter interp, UserObject obj, Interpreter.ScopeMemo memo, string line, out bool unfinished, out GraceObject result) { result = null; ParseNode module; ObjectConstructorNode mod = null; var isExtensionTrait = false; try { var p = new Parser("source code", line); module = p.Parse(); var opm = (ObjectParseNode)module; if (opm.Body.Count == 1) { // Tricky hack to let extensions be defined interactively. var trait = opm.Body[0] as TraitDeclarationParseNode; isExtensionTrait = (trait != null && trait.Signature.Name.EndsWith("Extension")); } var trans = new ExecutionTreeTranslator(); mod = (ObjectConstructorNode)trans.Translate(opm); } catch (StaticErrorException ex) { if (ex.Code == "P1001") { // "Unexpected end of file" is expected in the // repl for unfinished statements. unfinished = true; return(1); } else { // All other errors are errors. unfinished = false; return(1); } } unfinished = false; if (mod != null) { try { // The "module" object can only really have // a single element, but we don't know whether // it's a method, statement, or expression yet. foreach (var meth in mod.Methods.Values) { obj.AddMethod(meth.Name, new Method(meth, memo)); } foreach (var node in mod.Body) { var inherits = node as InheritsNode; if (inherits != null) { var ms = inherits.Inherit(interp, obj); obj.AddMethods(ms); obj.RunInitialisers(interp); } var v = node as VarDeclarationNode; var d = node as DefDeclarationNode; Cell cell; var meths = new Dictionary <string, Method>(); if (v != null) { obj.CreateVar(v.Name, meths, v.Readable, v.Writable, out cell); obj.AddMethods(meths); if (v.Value != null) { cell.Value = v.Value.Evaluate(interp); } result = GraceObject.Done; continue; } if (d != null) { obj.CreateDef(d.Name, meths, d.Public, out cell); obj.AddMethods(meths); cell.Value = d.Value.Evaluate(interp); result = GraceObject.Done; continue; } var ret = node.Evaluate(interp); if (ret != null && ret != GraceObject.Done && ret != GraceObject.Uninitialised) { interp.Print(interp, ret); } result = ret; } if (isExtensionTrait) { interp.LoadExtensionsFromObject(obj); } } catch (GraceExceptionPacketException e) { ErrorReporting.WriteLine("Uncaught exception:"); ErrorReporting.WriteException(e.ExceptionPacket); if (e.ExceptionPacket.StackTrace != null) { foreach (var l in e.ExceptionPacket.StackTrace) { ErrorReporting.WriteLine(" from " + l); } } return(1); } finally { // No matter what happened, restore the interpreter // to as pristine a state as we can manage before // the next time. interp.RestoreExactly(memo); interp.PopCallStackTo(0); mod = null; } } return(0); }