/// <summary> /// Entry point for the rcl interpreter /// </summary> public void InstanceMain(string[] argv, string appDomainVersionString) { string flags = Environment.GetEnvironmentVariable("RCL_FLAGS"); RCLArgv cmd; if (flags != null) { string[] flagsv = flags.Split(' '); string[] newArgv = new string[flagsv.Length + argv.Length]; for (int i = 0; i < flagsv.Length; ++i) { newArgv[i] = flagsv[i]; } for (int i = 0; i < argv.Length; ++i) { newArgv[flagsv.Length + i] = argv[i]; } cmd = RCLArgv.Init(newArgv); } else { cmd = RCLArgv.Init(argv); } // Someday do color output like this // string message = "\x1b[0;33mYELLOW\x1b[0;31m RED\x1b[0;34m BLUE\x1b[0;37m"; // Initialize runner environment AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler( UnhandledException); string prompt = "RCL>"; LineEditor editor = new LineEditor("RCL"); RCRunner runner = new RCRunner(workers: 1); InstallSignalHandler(runner); cmd.PrintStartup(appDomainVersionString); string line = ""; if (cmd.Program != "" || IsolateCode != null) { int status = 0; RCValue codeResult = null; try { RCValue code = null; if (IsolateCode != null) { code = runner.Read(IsolateCode); } else if (cmd.Program != "") { string file = File.ReadAllText(cmd.Program, Encoding.UTF8); code = runner.Read(file); } codeResult = runner.Rep(code, restoreStateOnError: true); if (cmd.Action != "") { RCValue result = runner.RepAction(cmd.Action); if (cmd.OutputEnum != RCOutput.Clean && !cmd.NoResult) { Console.Out.WriteLine(result.Format(RCFormat.Pretty, RCSystem.Log.GetColmap())); } } if (cmd.Batch && !cmd.Exit) { Thread.Sleep(Timeout.Infinite); } if (cmd.Batch) { status = runner.ExitStatus(); runner.Dispose(); Environment.Exit(status); } // otherwise go on and keep listening for further commands. } catch (ThreadAbortException) { status = runner.ExitStatus(); runner.Dispose(); Environment.Exit(status); } catch (ArgumentException ex) { // This is for when the action name is not in _state. RCSystem.Log.Record(0, 0, "runner", 0, "fatal", ex); Environment.Exit(1); } catch (FileNotFoundException ex) { // This for when the program file cannot be read. RCSystem.Log.Record(0, 0, "runner", 0, "fatal", ex); Environment.Exit(1); } catch (RCSyntaxException ex) { // Program file has bad syntax RCSystem.Log.Record(0, 0, "runner", 0, "fatal", ex); Environment.Exit(2); } catch (Exception ex) { // For all other exceptions keep the process open unless instructed to --exit. // This is so you can hack around in the environment. // Does this result in duplicate exception reports on the console? // I don't want it to, but without this there are errors that do not show up at // all. RCSystem.Log.Record(0, 0, "fiber", 0, "fatal", ex); status = runner.ExitStatus(); if (IsolateCode != null) { AppDomain.CurrentDomain.SetData("IsolateException", ex); } } finally { if (codeResult != null) { IsolateResult = codeResult.ToString(); AppDomain.CurrentDomain.SetData("IsolateResult", IsolateResult); } if (cmd.Exit) { runner.Dispose(); Environment.Exit(status); } } if (IsolateCode != null) { // When running isolated, do not call Environment.Exit because it would close // the entire // process. runner.Dispose(); return; } } else if (cmd.Exit && !cmd.Batch) { int status = runner.ExitStatus(); runner.Dispose(); // This means there is no program and no input from stdin. // The process simply starts and then stops. // There is no way external way to cause an error to be generated, // so there is no test for the possible non-zero status result. Environment.Exit(status); } // Process batch (standard input) and interactive commands. while (true) { int status = 0; try { if (cmd.Batch) { StringBuilder text = new StringBuilder(); // Read all commands from standard input. while (true) { line = Console.ReadLine(); if (line == null) { break; } text.AppendLine(line); } bool fragment; RCValue code = RCSystem.Parse(text.ToString(), out fragment); RCValue codeResult = runner.Rep(code, restoreStateOnError: true); if (cmd.Action != "") { RCValue actionResult = runner.RepAction(cmd.Action); if (cmd.OutputEnum != RCOutput.Clean && !cmd.NoResult) { Console.Out.WriteLine(actionResult.Format(RCFormat.Pretty, RCSystem.Log.GetColmap())); } } else if (codeResult != null && !cmd.NoResult) { Console.Out.WriteLine(codeResult.Format(RCFormat.Pretty, RCSystem.Log.GetColmap())); } if (cmd.Exit) { status = runner.ExitStatus(); runner.Dispose(); Environment.Exit(status); } } else { if (cmd.NoKeys) { // No read requires nokeys to have an effect, obvs. if (cmd.NoRead) { Thread.Sleep(Timeout.Infinite); } else { line = Console.ReadLine(); } } else { line = editor.Edit(prompt, ""); } _firstSigint = false; if (line != null) { string trimmed = line.TrimStart(' ').TrimEnd(' '); line = Alias(trimmed, runner, cmd); RCValue result = runner.Rep(line, restoreStateOnError: false); if (result != null) { Console.Out.WriteLine(result.Format(RCFormat.Pretty, RCSystem.Log.GetColmap())); } } else { break; } } } catch (ThreadAbortException) { status = runner.ExitStatus(); runner.Dispose(); // This prevents the last RCL prompt from appearing on the same line as the next // bash // prompt. // I want to do something so that log output *never* appears on the same line as // the // prompt. Console.Out.Flush(); Environment.Exit(status); } catch (Exception ex) { // Prevent having duplicate output in the log for these. // Also allow the runner to report this exception and count it towards // determination of // exit status. if (!runner.RunnerUnhandled) { runner.Report(ex, "unhandled"); } } } runner.Dispose(); Environment.Exit(0); }