Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
CommandLine.cs
Go to the documentation of this file.
1 #region License
2 /* **********************************************************************************
3  * Copyright (c) Roman Ivantsov
4  * This source code is subject to terms and conditions of the MIT License
5  * for Irony. A copy of the license can be found in the License.txt file
6  * at the root of this distribution.
7  * By using this source code in any fashion, you are agreeing to be bound by the terms of the
8  * MIT License.
9  * You must not remove this notice from this software.
10  * **********************************************************************************/
11 #endregion
12 
13 using System;
14 using System.Collections.Generic;
15 using System.Linq;
16 using System.Text;
17 using System.Threading;
18 using Irony.Parsing;
19 
20 namespace Irony.Interpreter {
21 
22  public class CommandLine {
23  #region Fields and properties
24  public readonly Grammar Grammar;
25  //Initialized from grammar
26  public string Title;
27  public string Greeting;
28  public string Prompt; //default prompt
29  public string PromptMoreInput; //prompt to show when more input is expected
30 
31  public readonly ScriptInterpreter Interpreter;
32  private bool _ctrlCPressed;
33  #endregion
34 
35  public CommandLine(Grammar grammar) {
36  Grammar = grammar;
37  Title = grammar.ConsoleTitle;
38  Greeting = grammar.ConsoleGreeting;
39  Prompt = grammar.ConsolePrompt;
40  PromptMoreInput = grammar.ConsolePromptMoreInput;
41 
42  Interpreter = new ScriptInterpreter(grammar);
43  Interpreter.RethrowExceptions = false;
44  Interpreter.ParseMode = ParseMode.CommandLine;
45  Interpreter.PrintParseErrors = false;
46  }
47 
48  public void Run() {
49  try {
50  RunImpl();
51  } catch (Exception ex) {
52  Console.ForegroundColor = ConsoleColor.Red;
53  Console.WriteLine(Resources.ErrConsoleFatalError);
54  Console.WriteLine(ex.ToString());
55  Console.ForegroundColor = ConsoleColor.White;
56  Console.WriteLine(Resources.MsgPressAnyKeyToExit);
57  Console.Read();
58  }
59 
60  }
61 
62 
63  private void RunImpl() {
64 
65  Console.Title = Title;
66  Console.CancelKeyPress += OnCancelKeyPress;
67  Console.WriteLine(Greeting);
68 
69  string input;
70  while (true) {
71  Console.ForegroundColor = ConsoleColor.White;
72  string prompt = (Interpreter.Status == InterpreterStatus.WaitingMoreInput ? PromptMoreInput : Prompt);
73  Console.Write(prompt);
74  var result = ReadInput(out input);
75  //Check the result type - it may be the response to "Abort?" question, not a script to execute.
76  switch (result) {
77  case ReadResult.AbortYes: return; //exit
78  case ReadResult.AbortNo: continue; //while loop
79  case ReadResult.Script: break; //do nothing, continue to evaluate script
80  }
81  Interpreter.ClearOutputBuffer();
82  Interpreter.EvaluateAsync(input);
83  while (Interpreter.IsBusy())
84  Thread.Sleep(50);
85  switch (Interpreter.Status) {
86  case InterpreterStatus.Ready: //success
87  Console.WriteLine(Interpreter.GetOutput());
88  break;
89  case InterpreterStatus.SyntaxError:
90  Console.WriteLine(Interpreter.GetOutput()); //write all output we have
91  Console.ForegroundColor = ConsoleColor.Red;
92  foreach (var err in Interpreter.ParsedScript.ParserMessages) {
93  Console.WriteLine(string.Empty.PadRight(prompt.Length + err.Location.Column) + "^"); //show err location
94  Console.WriteLine(err.Message); //print message
95  }
96  break;
97  case InterpreterStatus.RuntimeError:
98  ReportException();
99  break;
100  default: break;
101  }//switch
102  }
103 
104  }//Run method
105 
106  private void ReportException() {
107  Console.ForegroundColor = ConsoleColor.Red;
108  var ex = Interpreter.LastException;
109  var runtimeEx = ex as RuntimeException;
110  if (runtimeEx != null)
111  Console.WriteLine(runtimeEx.Message + " " + Resources.LabelLocation + " " + runtimeEx.Location.ToUiString());
112  else
113  Console.WriteLine(ex.Message);
114  //Console.WriteLine(ex.ToString()); //Uncomment to see the full stack when debugging your language
115  }
116 
117  #region Reading input methods
118  private enum ReadResult {
119  Script,
120  AbortYes,
121  AbortNo,
122  }
123 
124  private ReadResult ReadInput(out string input) {
125  //When user presses Ctrl-C system sends null as input immediately, then fires Ctrl-C event
126  do {
127  input = Console.ReadLine();
128  } while (input == null);
129  if (!_ctrlCPressed) return ReadResult.Script;
130  _ctrlCPressed = false;
131  if (Resources.ConsoleYesChars.Contains(input))
132  return ReadResult.AbortYes;
133  if (Resources.ConsoleNoChars.Contains(input))
134  return ReadResult.AbortNo;
135  //anything else return NO
136  return ReadResult.AbortNo;
137  }
138  #endregion
139 
140  #region Ctrl-C handling
141  //It might seem that we can do all here: ask the question "Abort?", get the answer and abort the app (by setting e.Cancel flag)
142  // It is possible when the interpreter is busy, and we do it all here. But when system waits for
143  // user input, it is not so straightforward. Here's what would happen if we did this.
144  // When the app is waiting for user input, and user presses the Ctrl-C, the currently waiting "ReadLine()" call in the main loop
145  // returns null; this will cause main loop in RunImpl run once again and one more prompt (>>>) will be printed;
146  // ReadLine will be called again from main loop. Only then this CancelKeyPress event is fired.
147  // Now, if we try to print question and read the answer in the event handler, the answer will go to the still waiting ReadLine
148  // call in the main loop, and event handler's ReadLine call for the answer will be blocked until NEXT user input.
149  // So we cannot do this.
150  // The solution is the following. First, the main loop uses ReadInput wrapper method to read console input - this method takes into
151  // account the internal flag _ctrlCPressed which is set by the Cancel event handler. The event handler, when it is invoked
152  // simply prints the question, sets the _ctrlCPressed flag and returns. When user answers the question (Y/N),
153  // the answer will be returned to ReadInput method which in turn will check the _ctrlCPressed flag.
154  // If this flag is set, it will return the appropriate result value indicating to the main loop
155  // that user input is in fact not a script but an answer to abort-yes/no question.
156  // The main loop will then either exit the app or continue running, without trying to evaluate the input value as script.
157  public virtual void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e) {
158  e.Cancel = true; //do not abort Console here.
159  _ctrlCPressed = true;
160  if (Interpreter.IsBusy()) {
161  //This is interpreter-busy situation, we do all here.
162  Console.Write(Resources.MsgAbortScriptYN);
163  string input;
164  var result = ReadInput(out input);
165  switch (result) {
166  case ReadResult.AbortYes:
167  Interpreter.Abort();
168  return;
169  default:
170  return;
171  }
172  } else {
173  //Ask the question and return;
174  //ReadInput is currently waiting for ReadLine return; it will get the answer (Y/N), and because
175  // _ctrlCPressed flag is set, ReadInput will return the answer as AbortYes/AbortNo to the main loop in RunImpl method
176  // The _crtlCPressed flag is already set.
177  Console.WriteLine();
178  Console.Write(Resources.MsgExitConsoleYN);
179  }
180  }//method
181  #endregion
182 
183  }//class
184 }
readonly ScriptInterpreter Interpreter
Definition: CommandLine.cs:31
static string MsgPressAnyKeyToExit
Looks up a localized string similar to Press any key to end the program..
static string MsgExitConsoleYN
Looks up a localized string similar to Exit console (y/n)?.
CommandLine(Grammar grammar)
Definition: CommandLine.cs:35
static string ErrConsoleFatalError
Looks up a localized string similar to Fatal error:.
readonly Grammar Grammar
Definition: CommandLine.cs:24
static string MsgAbortScriptYN
Looks up a localized string similar to Abort script(y/n)?.
virtual void OnCancelKeyPress(object sender, ConsoleCancelEventArgs e)
Definition: CommandLine.cs:157