Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
fmGrammarExplorer.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 //with contributions by Andrew Bradnan
12 #endregion
13 using System;
14 using System.Collections.Generic;
15 using System.ComponentModel;
16 using System.Drawing;
17 using System.Text;
18 using System.Windows.Forms;
19 using System.Diagnostics;
20 using System.IO;
21 using System.Configuration;
22 using System.Text.RegularExpressions;
23 using System.Xml;
24 
25 using Irony.GrammarExplorer.Properties;
26 using Irony.Parsing;
27 
28 namespace Irony.GrammarExplorer {
29  public partial class fmGrammarExplorer : Form {
30  public fmGrammarExplorer() {
31  InitializeComponent();
32  _grammarLoader.AssemblyUpdated += GrammarAssemblyUpdated;
33  }
34 
35  //fields
36  Parsing.Grammar _grammar;
37  LanguageData _language;
38  Parsing.Parser _parser;
39  ParseTree _parseTree;
40  GrammarLoader _grammarLoader = new GrammarLoader();
41  bool _loaded;
42 
43  #region Form load/unload events
44  private void fmExploreGrammar_Load(object sender, EventArgs e) {
45  ClearLanguageInfo();
46  try {
47  txtSource.Text = Settings.Default.SourceSample;
48  txtSearch.Text = Settings.Default.SearchPattern;
49  GrammarItemList grammars = GrammarItemList.FromXml(Settings.Default.Grammars);
50  grammars.ShowIn(cboGrammars);
51  chkParserTrace.Checked = Settings.Default.EnableTrace;
52  chkDisableHili.Checked = Settings.Default.DisableHili;
53  chkAutoRefresh.Checked = Settings.Default.AutoRefresh;
54  cboGrammars.SelectedIndex = Settings.Default.LanguageIndex; //this will build parser and start colorizer
55  } catch { }
56  _loaded = true;
57  }
58 
59  private void fmExploreGrammar_FormClosing(object sender, FormClosingEventArgs e) {
60  Settings.Default.SourceSample = txtSource.Text;
61  Settings.Default.LanguageIndex = cboGrammars.SelectedIndex;
62  Settings.Default.SearchPattern = txtSearch.Text;
63  Settings.Default.EnableTrace = chkParserTrace.Checked;
64  Settings.Default.DisableHili = chkDisableHili.Checked;
65  Settings.Default.AutoRefresh = chkAutoRefresh.Checked;
66  var grammars = GrammarItemList.FromCombo(cboGrammars);
67  Settings.Default.Grammars = grammars.ToXml();
68  Settings.Default.Save();
69  }//method
70  #endregion
71 
72  #region Show... methods
73  //Show... methods ######################################################################################################################
74  private void ClearLanguageInfo() {
75  lblLanguage.Text = string.Empty;
76  lblLanguageVersion.Text = string.Empty;
77  lblLanguageDescr.Text = string.Empty;
78  txtGrammarComments.Text = string.Empty;
79  }
80 
81  private void ClearParserOutput() {
82  lblSrcLineCount.Text = string.Empty;
83  lblSrcTokenCount.Text = "";
84  lblParseTime.Text = "";
85  lblParseErrorCount.Text = "";
86 
87  lstTokens.Items.Clear();
88  gridCompileErrors.Rows.Clear();
89  gridParserTrace.Rows.Clear();
90  lstTokens.Items.Clear();
91  tvParseTree.Nodes.Clear();
92  tvAst.Nodes.Clear();
93  Application.DoEvents();
94  }
95 
96  private void ShowLanguageInfo() {
97  if (_grammar == null) return;
98  var langAttr = LanguageAttribute.GetValue(_grammar.GetType());
99  if (langAttr == null) return;
100  lblLanguage.Text = langAttr.LanguageName;
101  lblLanguageVersion.Text = langAttr.Version;
102  lblLanguageDescr.Text = langAttr.Description;
103  txtGrammarComments.Text = _grammar.GrammarComments;
104  }
105 
106  private void ShowCompilerErrors() {
107  gridCompileErrors.Rows.Clear();
108  if (_parseTree == null || _parseTree.ParserMessages.Count == 0) return;
109  foreach (var err in _parseTree.ParserMessages)
110  gridCompileErrors.Rows.Add(err.Location, err, err.ParserState);
111  var needPageSwitch = tabBottom.SelectedTab != pageParserOutput &&
112  !(tabBottom.SelectedTab == pageParserTrace && chkParserTrace.Checked);
113  if (needPageSwitch)
114  tabBottom.SelectedTab = pageParserOutput;
115  }
116 
117  private void ShowParseTrace() {
118  gridParserTrace.Rows.Clear();
119  foreach (var entry in _parser.Context.ParserTrace) {
120  int index = gridParserTrace.Rows.Add(entry.State, entry.StackTop, entry.Input, entry.Message);
121  if (entry.IsError)
122  gridParserTrace.Rows[gridParserTrace.Rows.Count - 1].DefaultCellStyle.ForeColor = Color.Red;
123  }
124  //Show tokens
125  foreach (Token tkn in _parseTree.Tokens) {
126  if (chkExcludeComments.Checked && tkn.Category == TokenCategory.Comment) continue;
127  lstTokens.Items.Add(tkn);
128  }
129  }//method
130 
131  private void ShowCompileStats() {
132  if (_parseTree == null) return;
133  lblSrcLineCount.Text = string.Empty;
134  if (_parseTree.Tokens.Count > 0)
135  lblSrcLineCount.Text = (_parseTree.Tokens[_parseTree.Tokens.Count - 1].Location.Line + 1).ToString();
136  lblSrcTokenCount.Text = _parseTree.Tokens.Count.ToString();
137  lblParseTime.Text = _parseTree.ParseTime.ToString();
138  lblParseErrorCount.Text = _parseTree.ParserMessages.Count.ToString();
139  Application.DoEvents();
140  //Note: this time is "pure" parse time; actual delay after cliking "Compile" includes time to fill ParseTree, AstTree controls
141  }
142 
143  private void ShowParseTree() {
144  tvParseTree.Nodes.Clear();
145  if (_parseTree == null) return;
146  AddParseNodeRec(null, _parseTree.Root);
147  }
148  private void AddParseNodeRec(TreeNode parent, ParseTreeNode node) {
149  if (node == null) return;
150  string txt = node.ToString();
151  TreeNode tvNode = (parent == null? tvParseTree.Nodes.Add(txt) : parent.Nodes.Add(txt) );
152  tvNode.Tag = node;
153  foreach(var child in node.ChildNodes)
154  AddParseNodeRec(tvNode, child);
155  }
156 
157  private void ShowAstTree() {
158  tvAst.Nodes.Clear();
159  if (_parseTree == null || _parseTree.Root == null || _parseTree.Root.AstNode == null) return;
160  AddAstNodeRec(null, _parseTree.Root.AstNode);
161  }
162 
163  private void AddAstNodeRec(TreeNode parent, object astNode) {
164  if (astNode == null) return;
165  string txt = astNode.ToString();
166  TreeNode newNode = (parent == null ?
167  tvAst.Nodes.Add(txt) : parent.Nodes.Add(txt));
168  newNode.Tag = astNode;
169  var iBrowsable = astNode as IBrowsableAstNode;
170  if (iBrowsable == null) return;
171  var childList = iBrowsable.GetChildNodes();
172  foreach (var child in childList)
173  AddAstNodeRec(newNode, child);
174  }
175 
176  private void ShowParserConstructionResults() {
177  lblParserStateCount.Text = _language.ParserData.States.Count.ToString();
178  lblParserConstrTime.Text = _language.ConstructionTime.ToString();
179  txtParserStates.Text = string.Empty;
180  gridGrammarErrors.Rows.Clear();
181  txtTerms.Text = string.Empty;
182  txtNonTerms.Text = string.Empty;
183  txtParserStates.Text = string.Empty;
184  tabBottom.SelectedTab = pageLanguage;
185  if (_parser == null) return;
186  txtTerms.Text = ParserDataPrinter.PrintTerminals(_parser.Language);
187  txtNonTerms.Text = ParserDataPrinter.PrintNonTerminals(_parser.Language);
188  txtParserStates.Text = ParserDataPrinter.PrintStateList(_parser.Language);
189  ShowGrammarErrors();
190  }//method
191 
192  private void ShowGrammarErrors() {
193  gridGrammarErrors.Rows.Clear();
194  var errors = _parser.Language.Errors;
195  if (errors.Count == 0) return;
196  foreach (var err in errors)
197  gridGrammarErrors.Rows.Add(err.Level.ToString(), err.Message, err.State);
198  if (tabBottom.SelectedTab != pageGrammarErrors)
199  tabBottom.SelectedTab = pageGrammarErrors;
200  }
201 
202  private void ShowSourceLocation(SourceLocation location, int length) {
203  if (location.Position < 0) return;
204  txtSource.SelectionStart = location.Position;
205  txtSource.SelectionLength = length;
206  //txtSource.Select(location.Position, length);
207  txtSource.ScrollToCaret();
208  if (tabGrammar.SelectedTab != pageTest)
209  tabGrammar.SelectedTab = pageTest;
210  txtSource.Focus();
211  //lblLoc.Text = location.ToString();
212  }
213  private void ShowSourceLocationAndTraceToken(SourceLocation location, int length) {
214  ShowSourceLocation(location, length);
215  //find token in trace
216  for (int i = 0; i < lstTokens.Items.Count; i++) {
217  var tkn = lstTokens.Items[i] as Token;
218  if (tkn.Location.Position == location.Position) {
219  lstTokens.SelectedIndex = i;
220  return;
221  }//if
222  }//for i
223  }
224  private void LocateParserState(ParserState state) {
225  if (state == null) return;
226  if (tabGrammar.SelectedTab != pageParserStates)
227  tabGrammar.SelectedTab = pageParserStates;
228  //first scroll to the bottom, so that scrolling to needed position brings it to top
229  txtParserStates.SelectionStart = txtParserStates.Text.Length - 1;
230  txtParserStates.ScrollToCaret();
231  DoSearch(txtParserStates, "State " + state.Name, 0);
232  }
233 
234  private void ClearRuntimeInfo() {
235  lnkShowErrLocation.Enabled = false;
236  lnkShowErrStack.Enabled = false;
237  txtOutput.Text = string.Empty;
238  }
239 
240  #endregion
241 
242  #region Grammar combo menu commands
243  private void menuGrammars_Opening(object sender, CancelEventArgs e) {
244  miRemove.Enabled = cboGrammars.Items.Count > 0;
245  }
246 
247  private void miAdd_Click(object sender, EventArgs e) {
248  if (dlgSelectAssembly.ShowDialog() != DialogResult.OK) return;
249  string location = dlgSelectAssembly.FileName;
250  if (string.IsNullOrEmpty(location)) return;
251  var oldGrammars = new GrammarItemList();
252  foreach(var item in cboGrammars.Items)
253  oldGrammars.Add((GrammarItem) item);
254  var grammars = fmSelectGrammars.SelectGrammars(location, oldGrammars);
255  if (grammars == null) return;
256  foreach (GrammarItem item in grammars)
257  cboGrammars.Items.Add(item);
258  // auto-select the first grammar if no grammar currently selected
259  if (cboGrammars.SelectedIndex < 0 && grammars.Count > 0)
260  cboGrammars.SelectedIndex = 0;
261  }
262 
263  private void miRemove_Click(object sender, EventArgs e) {
264  if (MessageBox.Show("Are you sure you want to remove grammmar " + cboGrammars.SelectedItem + "?",
265  "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) {
266  cboGrammars.Items.RemoveAt(cboGrammars.SelectedIndex);
267  _parser = null;
268  if (cboGrammars.Items.Count > 0)
269  cboGrammars.SelectedIndex = 0;
270  }
271  }
272 
273  private void miRemoveAll_Click(object sender, EventArgs e) {
274  if (MessageBox.Show("Are you sure you want to remove all grammmars in the list?",
275  "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) {
276  cboGrammars.Items.Clear();
277  _parser = null;
278  }
279  }
280  #endregion
281 
282  #region Parsing and running
283  private void CreateGrammar() {
284  _grammar = _grammarLoader.CreateGrammar();
285  }
286 
287  private void CreateParser() {
288  StopHighlighter();
289  btnRun.Enabled = false;
290  txtOutput.Text = string.Empty;
291  _parseTree = null;
292 
293  btnRun.Enabled = _grammar.FlagIsSet(LanguageFlags.CanRunSample);
294  _language = _grammar.CreateLanguageData();
295  _parser = new Parsing.Parser (_language);
296  ShowParserConstructionResults();
297  StartHighlighter();
298  }
299 
300  private void ParseSample() {
301  ClearParserOutput();
302  if (_parser == null || !_parser.Language.CanParse()) return;
303  _parseTree = null;
304  GC.Collect(); //to avoid disruption of perf times with occasional collections
305  _parser.Context.SetOption(ParseOptions.TraceParser, chkParserTrace.Checked);
306  try {
307  _parser.Parse(txtSource.Text, "<source>");
308  } catch (Exception ex) {
309  gridCompileErrors.Rows.Add(null, ex.Message, null);
310  tabBottom.SelectedTab = pageParserOutput;
311  throw;
312  } finally {
313  _parseTree = _parser.Context.CurrentParseTree;
314  ShowCompilerErrors();
315  if (chkParserTrace.Checked) {
316  ShowParseTrace();
317  }
318  ShowCompileStats();
319  ShowParseTree();
320  ShowAstTree();
321  }
322  }
323 
324  private void WriteOutput(string text) {
325  if (string.IsNullOrEmpty(text)) return;
326  txtOutput.Text += text + Environment.NewLine;
327  txtOutput.Select(txtOutput.Text.Length - 1, 0);
328  }
329 
330  #endregion
331 
332  #region miscellaneous: LoadSourceFile, Search, Source highlighting
333  private void LoadSourceFile(string path) {
334  _parseTree = null;
335  StreamReader reader = null;
336  try {
337  reader = new StreamReader(path);
338  txtSource.Text = null; //to clear any old formatting
339  txtSource.Text = reader.ReadToEnd();
340  txtSource.Select(0, 0);
341  } catch (Exception e) {
342  MessageBox.Show(e.Message);
343  } finally {
344  if (reader != null)
345  reader.Close();
346  }
347  }
348 
349  //Source highlighting
350  RichTextBoxHighlighter _highlighter;
351  private void StartHighlighter() {
352  if (_highlighter != null)
353  StopHighlighter();
354  if (chkDisableHili.Checked) return;
355  if (!_parser.Language.CanParse()) return;
356  _highlighter = new RichTextBoxHighlighter(txtSource, _language);
357  _highlighter.Adapter.Activate();
358  }
359  private void StopHighlighter() {
360  if (_highlighter == null) return;
361  _highlighter.Dispose();
362  _highlighter = null;
363  ClearHighlighting();
364  }
365  private void ClearHighlighting() {
366  var txt = txtSource.Text;
367  txtSource.Clear();
368  txtSource.Text = txt; //remove all old highlighting
369  }
370  private void EnableHighlighter(bool enable) {
371  if (_highlighter != null)
372  StopHighlighter();
373  if (enable)
374  StartHighlighter();
375  }
376 
377  //The following methods are contributed by Andrew Bradnan; pasted here with minor changes
378  private void DoSearch() {
379  lblSearchError.Visible = false;
380  TextBoxBase textBox = GetSearchContentBox();
381  if (textBox == null) return;
382  int idxStart = textBox.SelectionStart + textBox.SelectionLength;
383  if (!DoSearch(textBox, txtSearch.Text, idxStart)) {
384  lblSearchError.Text = "Not found.";
385  lblSearchError.Visible = true;
386  }
387  }//method
388 
389  private bool DoSearch(TextBoxBase textBox, string fragment, int start) {
390  textBox.SelectionLength = 0;
391  // Compile the regular expression.
392  Regex r = new Regex(fragment, RegexOptions.IgnoreCase);
393  // Match the regular expression pattern against a text string.
394  Match m = r.Match(textBox.Text.Substring(start));
395  if (m.Success) {
396  int i = 0;
397  Group g = m.Groups[i];
398  CaptureCollection cc = g.Captures;
399  Capture c = cc[0];
400  textBox.SelectionStart = c.Index + start;
401  textBox.SelectionLength = c.Length;
402  textBox.Focus();
403  textBox.ScrollToCaret();
404  return true;
405  }
406  return false;
407  }//method
408 
409  public TextBoxBase GetSearchContentBox() {
410  switch (tabGrammar.SelectedIndex) {
411  case 0:
412  return txtTerms;
413  case 1:
414  return txtNonTerms;
415  case 2:
416  return txtParserStates;
417  case 4:
418  return txtSource;
419  default:
420  return null;
421  }//switch
422  }
423 
424  #endregion
425 
426  #region Controls event handlers
427  //Controls event handlers ###################################################################################################
428  private void btnParse_Click(object sender, EventArgs e) {
429  ParseSample();
430  }
431 
432  private void btnRun_Click(object sender, EventArgs e)
433  {
434  MessageBox.Show(this, "No longer implemented");
435  }
436 
437  private void tvParseTree_AfterSelect(object sender, TreeViewEventArgs e) {
438  var vtreeNode = tvParseTree.SelectedNode;
439  if (vtreeNode == null) return;
440  var parseNode = vtreeNode.Tag as ParseTreeNode;
441  if (parseNode == null) return;
442  ShowSourceLocation(parseNode.Span.Location, 1);
443  }
444 
445  private void tvAst_AfterSelect(object sender, TreeViewEventArgs e) {
446  var treeNode = tvAst.SelectedNode;
447  if (treeNode == null) return;
448  var iBrowsable = treeNode.Tag as IBrowsableAstNode;
449  if (iBrowsable == null) return;
450  ShowSourceLocation(iBrowsable.Location, 1);
451  }
452 
453  bool _changingGrammar;
454  private void LoadSelectedGrammar() {
455  try {
456  ClearLanguageInfo();
457  ClearParserOutput();
458  ClearRuntimeInfo();
459 
460  _changingGrammar = true;
461  CreateGrammar();
462  ShowLanguageInfo();
463  CreateParser();
464  } finally {
465  _changingGrammar = false; //in case of exception
466  }
467  }
468 
469  private void cboGrammars_SelectedIndexChanged(object sender, EventArgs e) {
470  _grammarLoader.SelectedGrammar = cboGrammars.SelectedItem as GrammarItem;
471  LoadSelectedGrammar();
472  }
473 
474  private void GrammarAssemblyUpdated(object sender, EventArgs args) {
475  if (InvokeRequired) {
476  Invoke(new EventHandler(GrammarAssemblyUpdated), sender, args);
477  return;
478  }
479  if (chkAutoRefresh.Checked) {
480  LoadSelectedGrammar();
481  txtGrammarComments.Text += String.Format("{0}Grammar assembly reloaded: {1:HH:mm:ss}", Environment.NewLine, DateTime.Now);
482  }
483  }
484 
485  private void btnFileOpen_Click(object sender, EventArgs e) {
486  if (dlgOpenFile.ShowDialog() != DialogResult.OK) return;
487  LoadSourceFile(dlgOpenFile.FileName);
488  }
489 
490  private void txtSource_TextChanged(object sender, EventArgs e) {
491  _parseTree = null; //force it to recompile on run
492  }
493 
494  private void btnManageGrammars_Click(object sender, EventArgs e) {
495  menuGrammars.Show(btnManageGrammars, 0, btnManageGrammars.Height);
496  }
497 
498  private void btnToXml_Click(object sender, EventArgs e) {
499  txtOutput.Text = string.Empty;
500  if (_parseTree == null)
501  ParseSample();
502  if (_parseTree == null) return;
503  txtOutput.Text += _parseTree.ToXml();
504  txtOutput.Select(0, 0);
505  tabBottom.SelectedTab = pageOutput;
506  }
507 
508  private void cboParseMethod_SelectedIndexChanged(object sender, EventArgs e) {
509  //changing grammar causes setting of parse method combo, so to prevent double-call to ConstructParser
510  // we don't do it here if _changingGrammar is set
511  if (!_changingGrammar)
512  CreateParser();
513  }
514 
515  private void gridParserTrace_CellDoubleClick(object sender, DataGridViewCellEventArgs e) {
516  if (_parser.Context == null || e.RowIndex < 0 || e.RowIndex >= _parser.Context.ParserTrace.Count) return;
517  var entry = _parser.Context.ParserTrace[e.RowIndex];
518  switch (e.ColumnIndex) {
519  case 0: //state
520  case 3: //action
521  LocateParserState(entry.State);
522  break;
523  case 1: //stack top
524  if (entry.StackTop != null)
525  ShowSourceLocationAndTraceToken(entry.StackTop.Span.Location, entry.StackTop.Span.Length);
526  break;
527  case 2: //input
528  if (entry.Input != null)
529  ShowSourceLocationAndTraceToken(entry.Input.Span.Location, entry.Input.Span.Length);
530  break;
531  }//switch
532  }
533 
534  private void lstTokens_Click(object sender, EventArgs e) {
535  if (lstTokens.SelectedIndex < 0)
536  return;
537  Token token = (Token)lstTokens.SelectedItem;
538  ShowSourceLocation(token.Location, token.Length);
539  }
540 
541  private void gridCompileErrors_CellDoubleClick(object sender, DataGridViewCellEventArgs e) {
542  if (e.RowIndex < 0 || e.RowIndex >= gridCompileErrors.Rows.Count) return;
543  var err = gridCompileErrors.Rows[e.RowIndex].Cells[1].Value as ParserMessage;
544  switch (e.ColumnIndex) {
545  case 0: //state
546  case 1: //stack top
547  ShowSourceLocation(err.Location, 1);
548  break;
549  case 2: //input
550  if (err.ParserState != null)
551  LocateParserState(err.ParserState);
552  break;
553  }//switch
554  }
555 
556  private void gridGrammarErrors_CellDoubleClick(object sender, DataGridViewCellEventArgs e) {
557  if (e.RowIndex < 0 || e.RowIndex >= gridGrammarErrors.Rows.Count) return;
558  var state = gridGrammarErrors.Rows[e.RowIndex].Cells[2].Value as ParserState;
559  if (state != null)
560  LocateParserState(state);
561  }
562 
563  private void btnSearch_Click(object sender, EventArgs e) {
564  DoSearch();
565  }//method
566 
567  private void txtSearch_KeyPress(object sender, KeyPressEventArgs e) {
568  if (e.KeyChar == '\r') // <Enter> key
569  DoSearch();
570  }
571 
572  private void lnkShowErrLocation_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
573  {
574  MessageBox.Show(this, "No longer implemented");
575  }
576 
577  private void lnkShowErrStack_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
578  MessageBox.Show(this, "No longer implemented");
579  }
580 
581  #endregion
582 
583  private void chkDisableHili_CheckedChanged(object sender, EventArgs e) {
584  if (!_loaded) return;
585  EnableHighlighter(!chkDisableHili.Checked);
586  }
587 
588  }//class
589 }
TokenCategory
Token category.
Definition: Token.cs:29
TokenCategory Category
Gets the category.
Definition: Token.cs:186
Maintains grammar assemblies, reloads updated files automatically.
readonly string Name
Definition: ParserData.cs:39
readonly SourceLocation Location
Location in the source code.
Definition: Token.cs:116
ParseTreeNodeList ChildNodes
Definition: ParseTree.cs:41
Describes a language.
Definition: LanguageData.cs:23
Tokens are produced by scanner and fed to parser, optionally passing through Token filters in between...
Definition: Token.cs:74
int Length
Gets the length.
Definition: Token.cs:131