Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ScriptInterpreter.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.Interpreter.Ast;
19 using Irony.Parsing;
20 
21 namespace Irony.Interpreter {
22 
23  public enum InterpreterStatus {
24  Ready,
25  Evaluating,
26  WaitingMoreInput, //command line only
29  Aborted
30  }
31 
32  public class ScriptInterpreter {
33  #region Fields and properties
34  public readonly LanguageData Language;
35  public readonly LanguageRuntime Runtime;
37  public readonly Parser Parser;
38 
39  public Thread WorkerThread { get; private set; }
40  public Exception LastException {get; private set;}
41 
42  public InterpreterStatus Status { get; private set; }
43 
44  public bool RethrowExceptions = true;
45  public bool PrintParseErrors = true;
47  get { return Parser.Context.Mode; }
48  set { Parser.Context.Mode = value; }
49  }
50  public ValuesTable Globals {
51  get { return EvaluationContext.TopFrame.Values; }
52  }
53  //internal, real status of interpreter. The public Status field gets updated only on exit from public methods
54  // We want to make sure external code sees interpeter as BUSY until we actually completed operation internally
55  private InterpreterStatus _internalStatus;
56  #endregion
57 
58  #region constructors
59  public ScriptInterpreter(Grammar grammar) : this(new LanguageData(grammar)) { }
60 
61  public ScriptInterpreter(LanguageData language) {
62  Language = language;
63  Runtime = Language.Grammar.CreateRuntime(Language);
64  Parser = new Parser(Language);
65  EvaluationContext = new EvaluationContext(Runtime);
66  Status = _internalStatus = InterpreterStatus.Ready;
67  }
68  #endregion
69 
70  #region Evaluate overloads
71  public void Evaluate(string script) {
72  Script = script;
73  Evaluate();
74  }
75  public void Evaluate(ParseTree parsedScript) {
76  ParsedScript = parsedScript;
77  Evaluate();
78  }
79  public void Evaluate() {
80  try {
81  _internalStatus = Status = InterpreterStatus.Evaluating;
82  ParseAndEvaluate();
83  } finally {
84  Status = _internalStatus;
85  }
86  }
87 
88  public void EvaluateAsync(string script) {
89  Script = script;
90  EvaluateAsync();
91  }
92  public void EvaluateAsync(ParseTree parsedScript) {
93  ParsedScript = parsedScript;
94  EvaluateAsync();
95  }
96  public void EvaluateAsync() {
97  CheckNotBusy();
98  Status = _internalStatus = InterpreterStatus.Evaluating;
99  WorkerThread = new Thread(AsyncThreadStart);
100  WorkerThread.Start(null);
101  }
102  #endregion
103 
104  #region Other public members: Script, ParsedScript, IsBusy(), GetOutput()
105  public string Script {
106  get { return _script; }
107  set {
108  CheckNotBusy();
109  _script = value;
110  _parsedScript = null;
111  }
112  } string _script;
113 
114  public ParseTree ParsedScript {
115  get { return _parsedScript; }
116  set {
117  _parsedScript = value;
118  _script = (_parsedScript == null ? null : _parsedScript.SourceText);
119  }
120  } ParseTree _parsedScript;
121 
122  public bool IsBusy() {
123  return Status == InterpreterStatus.Evaluating;
124  }
125 
126  public string GetOutput() {
127  return EvaluationContext.OutputBuffer.ToString();
128  }
129  public void ClearOutputBuffer() {
130  EvaluationContext.OutputBuffer.Length = 0;
131  }
132 
134  if (ParsedScript == null)
135  return new ParserMessageList();
136  else
137  return ParsedScript.ParserMessages;
138  }
139 
140  public void Abort() {
141  try {
142  if (WorkerThread == null) return;
143  WorkerThread.Abort();
144  WorkerThread.Join(50);
145  } catch { }
146  WorkerThread = null;
147  }
148  #endregion
149 
150  #region private implementations -------------------------------------------------------------------------------
151  private void AsyncThreadStart(object data) {
152  try {
153  ParseAndEvaluate();
154  } finally {
155  Status = _internalStatus;
156  }
157  }
158  private void CheckNotBusy() {
159  if (IsBusy())
160  throw new Exception(Resources.ErrInterpreterIsBusy);
161  }
162 
163  private void ParseAndEvaluate() {
164  EvaluationContext.EvaluationTime = 0;
165  try {
166  LastException = null;
167  if(ParsedScript == null) {
168  //don't evaluate empty strings, just return
169  if (Script == null || Script.Trim() == string.Empty && Status == InterpreterStatus.Ready) return;
170  ParsedScript = this.Parser.Parse(Script, "source");
171  CheckParseStatus();
172  if(_internalStatus != InterpreterStatus.Evaluating) return;
173  }
174  if(ParsedScript == null)
175  return;
176  EvaluateParsedScript();
177  _internalStatus = InterpreterStatus.Ready;
178  } catch (Exception ex) {
179  LastException = ex;
180  _internalStatus = InterpreterStatus.RuntimeError;
181  if (LastException != null && RethrowExceptions)
182  throw;
183  }
184  }
185 
186  private void EvaluateParsedScript() {
187  var iRoot = GetAstInterface();
188  if (iRoot == null) return;
189  EvaluationContext.ClearLastResult();
190  var start = Environment.TickCount;
191  iRoot.Evaluate(EvaluationContext, AstMode.Read);
192  EvaluationContext.EvaluationTime = Environment.TickCount - start;
193  if (EvaluationContext.HasLastResult)
194  EvaluationContext.Write(EvaluationContext.LastResult + Environment.NewLine);
195  }
196 
197  private IInterpretedAstNode GetAstInterface() {
198  Check(ParsedScript != null, Resources.ErrParseTreeNull);
199  Check(ParsedScript.Root != null, Resources.ErrParseTreeRootNull);
200  var astNode = ParsedScript.Root.AstNode;
201  Check(astNode != null, Resources.ErrRootAstNodeNull);
202  var iInterpNode = astNode as IInterpretedAstNode;
203  Check(iInterpNode != null, Resources.ErrRootAstNoInterface);
204  return iInterpNode;
205  }
206 
207  private bool CheckParseStatus() {
208  if (ParsedScript == null) return false;
209  if (ParsedScript.HasErrors()) {
210  _internalStatus = InterpreterStatus.SyntaxError;
211  if (PrintParseErrors) {
212  foreach(var err in ParsedScript.ParserMessages) {
213  var msg = string.Format(Resources.ErrOutErrorPrintFormat, err.Location.ToUiString(), err.Message);
214  this.EvaluationContext.OutputBuffer.AppendLine(msg);
215  }//foreach
216  }//if
217  return false;
218  }
219  switch (ParsedScript.Status) {
220  case ParseTreeStatus.Error:
221  _internalStatus = InterpreterStatus.SyntaxError;
222  return false;
223  case ParseTreeStatus.Partial:
224  _internalStatus = InterpreterStatus.WaitingMoreInput;
225  return false;
226  default:
227  _internalStatus = InterpreterStatus.Evaluating;
228  return true;
229  }
230  }
231 
232  private static void Check(bool condition, string message) {
233  if (!condition)
234  throw new Exception(message);
235  }
236 
237  #endregion
238 
239 
240  }//class
241 
242 }//namespace
readonly EvaluationContext EvaluationContext
readonly LanguageRuntime Runtime
void EvaluateAsync(ParseTree parsedScript)
Describes a language.
Definition: LanguageData.cs:23
void Evaluate(ParseTree parsedScript)
static string ErrOutErrorPrintFormat
Looks up a localized string similar to {0}: {1}.
ScriptInterpreter(LanguageData language)