context: #region License
  /* **********************************************************************************
   * Copyright (c) Roman Ivantsov
   * This source code is subject to terms and conditions of the MIT License
   * for Irony. A copy of the license can be found in the License.txt file
   * at the root of this distribution.
   * By using this source code in any fashion, you are agreeing to be bound by the terms of the
   * MIT License.
   * You must not remove this notice from this software.
   * **********************************************************************************/
  //with contributions by Andrew Bradnan and Alexey Yakovlev
  #endregion
  using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Drawing;
  using System.Text;
  using System.Windows.Forms;
  using System.Diagnostics;
  using System.IO;
  using System.Configuration;
  using System.Text.RegularExpressions;
  using System.Xml;
  using Irony.Ast;
  using Irony.Parsing;
  using Irony.GrammarExplorer.Properties;
  using Irony.GrammarExplorer.Highlighter;
  using IronyExplorer.GrammarExplorer.Properties;
  
  namespace Irony.GrammarExplorer {
    using ScriptException = Irony.Interpreter.ScriptException; //that's the only place we use stuff from Irony.Interpreter
  
    public partial class fmGrammarExplorer : Form {
      public fmGrammarExplorer() {
        InitializeComponent();
        _grammarLoader.AssemblyUpdated += GrammarAssemblyUpdated;
      }
  
      //fields
      Grammar _grammar;
      LanguageData _language;
      Parser _parser;
      ParseTree _parseTree;
      ScriptException _runtimeError;
      GrammarLoader _grammarLoader = new GrammarLoader();
      bool _loaded;
      bool _treeClickDisabled; //to temporarily disable tree click when we locate the node programmatically
  
      #region Form load/unload events
      private void fmExploreGrammar_Load(object sender, EventArgs e) {
        ClearLanguageInfo();
        try {
          txtSource.Text = Settings.Default.SourceSample;
          txtSearch.Text = Settings.Default.SearchPattern;
          GrammarItemList grammars = GrammarItemList.FromXml(Settings.Default.Grammars);
          grammars.ShowIn(cboGrammars);
          chkParserTrace.Checked = Settings.Default.EnableTrace;
          chkDisableHili.Checked = Settings.Default.DisableHili;
          chkAutoRefresh.Checked = Settings.Default.AutoRefresh;
          cboGrammars.SelectedIndex = Settings.Default.LanguageIndex; //this will build parser and start colorizer
        } catch { }
        _loaded = true;
      }
  
      private void fmExploreGrammar_FormClosing(object sender, FormClosingEventArgs e) {
        Settings.Default.SourceSample = txtSource.Text;
        Settings.Default.LanguageIndex = cboGrammars.SelectedIndex;
        Settings.Default.SearchPattern = txtSearch.Text;
        Settings.Default.EnableTrace = chkParserTrace.Checked;
        Settings.Default.DisableHili = chkDisableHili.Checked;
        Settings.Default.AutoRefresh = chkAutoRefresh.Checked;
        var grammars = GrammarItemList.FromCombo(cboGrammars);
        Settings.Default.Grammars = grammars.ToXml();
        Settings.Default.Save();
      }//method
      #endregion
  
      #region Show... methods
      //Show... methods ######################################################################################################################
      private void ClearLanguageInfo() {
        lblLanguage.Text = string.Empty;
        lblLanguageVersion.Text = string.Empty;
        lblLanguageDescr.Text = string.Empty;
        txtGrammarComments.Text = string.Empty;
      }
  
      private void ClearParserOutput() {
        lblSrcLineCount.Text = string.Empty;
        lblSrcTokenCount.Text = "";
        lblParseTime.Text = "";
        lblParseErrorCount.Text = "";
  
        lstTokens.Items.Clear();
        gridCompileErrors.Rows.Clear();
        gridParserTrace.Rows.Clear();
        lstTokens.Items.Clear();
        tvParseTree.Nodes.Clear();
        tvAst.Nodes.Clear();
        Application.DoEvents();
      }
  
      private void ShowLanguageInfo() {
        if (_grammar == null) return;
        var langAttr = LanguageAttribute.GetValue(_grammar.GetType());
        if (langAttr == null) return;
        lblLanguage.Text = langAttr.LanguageName;
        lblLanguageVersion.Text = langAttr.Version;
        lblLanguageDescr.Text = langAttr.Description;
        txtGrammarComments.Text = _grammar.GrammarComments;
      }
  
      private void ShowCompilerErrors() {
        gridCompileErrors.Rows.Clear();
        if (_parseTree == null || _parseTree.ParserMessages.Count == 0) return;
        foreach (var err in _parseTree.ParserMessages)
          gridCompileErrors.Rows.Add(err.Location, err, err.ParserState);
        var needPageSwitch = tabBottom.SelectedTab != pageParserOutput &&
          !(tabBottom.SelectedTab == pageParserTrace && chkParserTrace.Checked);
        if (needPageSwitch)
          tabBottom.SelectedTab = pageParserOutput;
      }
  
      private void ShowParseTrace() {
        gridParserTrace.Rows.Clear();
        foreach (var entry in _parser.Context.ParserTrace) {
          int index = gridParserTrace.Rows.Add(entry.State, entry.StackTop, entry.Input, entry.Message);
          if (entry.IsError)
            gridParserTrace.Rows[gridParserTrace.Rows.Count - 1].DefaultCellStyle.ForeColor = Color.Red;
        }
        //Show tokens
        foreach (Token tkn in _parseTree.Tokens) {
          if (chkExcludeComments.Checked && tkn.Category == TokenCategory.Comment) continue;
          lstTokens.Items.Add(tkn);
        }
      }//method
  
      private void ShowCompileStats() {
        if (_parseTree == null) return;
        lblSrcLineCount.Text = string.Empty;
        if (_parseTree.Tokens.Count > 0)
          lblSrcLineCount.Text = (_parseTree.Tokens[_parseTree.Tokens.Count - 1].Location.Line + 1).ToString();
        lblSrcTokenCount.Text = _parseTree.Tokens.Count.ToString();
        lblParseTime.Text = _parseTree.ParseTimeMilliseconds.ToString();
        lblParseErrorCount.Text = _parseTree.ParserMessages.Count.ToString();
        Application.DoEvents();
        //Note: this time is "pure" parse time; actual delay after cliking "Compile" includes time to fill ParseTree, AstTree controls
      }
  
      private void ShowParseTree() {
        tvParseTree.Nodes.Clear();
        if (_parseTree == null) return;
        AddParseNodeRec(null, _parseTree.Root);
      }
      private void AddParseNodeRec(TreeNode parent, ParseTreeNode node) {
        if (node == null) return;
        string txt = node.ToString();
        TreeNode tvNode = (parent == null? tvParseTree.Nodes.Add(txt) : parent.Nodes.Add(txt) );
        tvNode.Tag = node;
        foreach(var child in node.ChildNodes)
          AddParseNodeRec(tvNode, child);
      }
  
      private void ShowAstTree() {
        tvAst.Nodes.Clear();
        if (_parseTree == null || _parseTree.Root == null || _parseTree.Root.AstNode == null) return;
        AddAstNodeRec(null, _parseTree.Root.AstNode);
      }
  
      private void AddAstNodeRec(TreeNode parent, object astNode) {
        if (astNode == null) return;
        string txt = astNode.ToString();
        TreeNode newNode = (parent == null ?
          tvAst.Nodes.Add(txt) : parent.Nodes.Add(txt));
        newNode.Tag = astNode;
        var iBrowsable = astNode as IBrowsableAstNode;
        if (iBrowsable == null) return;
        var childList = iBrowsable.GetChildNodes();
        foreach (var child in childList)
          AddAstNodeRec(newNode, child);
      }
  
      private void ShowParserConstructionResults() {
        lblParserStateCount.Text = _language.ParserData.States.Count.ToString();
        lblParserConstrTime.Text = _language.ConstructionTime.ToString();
        txtParserStates.Text = string.Empty;
        gridGrammarErrors.Rows.Clear();
        txtTerms.Text = string.Empty;
        txtNonTerms.Text = string.Empty;
        txtParserStates.Text = string.Empty;
        tabBottom.SelectedTab = pageLanguage;
        if (_parser == null) return;
        txtTerms.Text = ParserDataPrinter.PrintTerminals(_language);
        txtNonTerms.Text = ParserDataPrinter.PrintNonTerminals(_language);
        txtParserStates.Text = ParserDataPrinter.PrintStateList(_language);
        ShowGrammarErrors();
      }//method
  
      private void ShowGrammarErrors() {
        gridGrammarErrors.Rows.Clear();
        var errors = _parser.Language.Errors;
        if (errors.Count == 0) return;
        foreach (var err in errors)
          gridGrammarErrors.Rows.Add(err.Level.ToString(), err.Message, err.State);
        if (tabBottom.SelectedTab != pageGrammarErrors)
          tabBottom.SelectedTab = pageGrammarErrors;
      }
  
      private void ShowSourcePosition(int position, int length) {
        if (position < 0) return;
        txtSource.SelectionStart = position;
        txtSource.SelectionLength = length;
        //txtSource.Select(location.Position, length);
        txtSource.DoCaretVisible();
        if (tabGrammar.SelectedTab != pageTest)
          tabGrammar.SelectedTab = pageTest;
        txtSource.Focus();
        //lblLoc.Text = location.ToString();
      }
      private void ShowSourcePositionAndTraceToken(int position, int length) {
        ShowSourcePosition(position, length);
        //find token in trace
        for (int i = 0; i < lstTokens.Items.Count; i++) {
          var tkn = lstTokens.Items[i] as Token;
          if (tkn.Location.Position == position) {
            lstTokens.SelectedIndex = i;
            return;
          }//if
        }//for i
      }
      private void LocateParserState(ParserState state) {
        if (state == null) return;
        if (tabGrammar.SelectedTab != pageParserStates)
          tabGrammar.SelectedTab = pageParserStates;
        //first scroll to the bottom, so that scrolling to needed position brings it to top
        txtParserStates.SelectionStart = txtParserStates.Text.Length - 1;
        txtParserStates.ScrollToCaret();
        DoSearch(txtParserStates, "State " + state.Name, 0);
      }
  
      private void ShowRuntimeError(ScriptException error){
        _runtimeError = error;
        lnkShowErrLocation.Enabled = _runtimeError != null;
        lnkShowErrStack.Enabled = lnkShowErrLocation.Enabled;
        if (_runtimeError != null) {
          //the exception was caught and processed by Interpreter
          WriteOutput("Error: " + error.Message + " At " + _runtimeError.Location.ToUiString() + ".");
          ShowSourcePosition(_runtimeError.Location.Position, 1);
        } else {
          //the exception was not caught by interpreter/AST node. Show full exception info
          WriteOutput("Error: " + error.Message);
          fmShowException.ShowException(error);
  
        }
        tabBottom.SelectedTab = pageOutput;
      }
  
      private void SelectTreeNode(TreeView tree, TreeNode node) {
        _treeClickDisabled = true;
        tree.SelectedNode = node;
        if (node != null)
          node.EnsureVisible();
        _treeClickDisabled = false;
      }
  
  
  
      private void ClearRuntimeInfo() {
        lnkShowErrLocation.Enabled = false;
        lnkShowErrStack.Enabled = false;
        _runtimeError = null;
        txtOutput.Text = string.Empty;
      }
  
      #endregion
  
      #region Grammar combo menu commands
      private void menuGrammars_Opening(object sender, CancelEventArgs e) {
        miRemove.Enabled = cboGrammars.Items.Count > 0;
      }
  
      private void miAdd_Click(object sender, EventArgs e) {
        if (dlgSelectAssembly.ShowDialog() != DialogResult.OK) return;
        string location = dlgSelectAssembly.FileName;
        if (string.IsNullOrEmpty(location)) return;
        var oldGrammars = new GrammarItemList();
        foreach(var item in cboGrammars.Items)
          oldGrammars.Add((GrammarItem) item);
        var grammars = fmSelectGrammars.SelectGrammars(location, oldGrammars);
        if (grammars == null) return;
        foreach (GrammarItem item in grammars)
          cboGrammars.Items.Add(item);
        btnRefresh.Enabled = false;
        // auto-select the first grammar if no grammar currently selected
        if (cboGrammars.SelectedIndex < 0 && grammars.Count > 0)
          cboGrammars.SelectedIndex = 0;
      }
  
      private void miRemove_Click(object sender, EventArgs e) {
        if (MessageBox.Show("Are you sure you want to remove grammmar " + cboGrammars.SelectedItem + "?",
          "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) {
          cboGrammars.Items.RemoveAt(cboGrammars.SelectedIndex);
          _parser = null;
          if (cboGrammars.Items.Count > 0)
            cboGrammars.SelectedIndex = 0;
          else
            btnRefresh.Enabled = false;
        }
      }
  
      private void miRemoveAll_Click(object sender, EventArgs e) {
        if (MessageBox.Show("Are you sure you want to remove all grammmars in the list?",
          "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) {
          cboGrammars.Items.Clear();
          btnRefresh.Enabled = false;
          _parser = null;
        }
      }
      #endregion
  
      #region Parsing and running
      private void CreateGrammar() {
        _grammar = _grammarLoader.CreateGrammar();
      }
  
      private void CreateParser() {
        StopHighlighter();
        btnRun.Enabled = false;
        txtOutput.Text = string.Empty;
        _parseTree = null;
  
        btnRun.Enabled = _grammar is ICanRunSample;
  
gt:       _language = new LanguageData(_grammar);