diff --git a/LexicalAnalyzer.csproj b/LexicalAnalyzer.csproj index 8cbe81d..27346cc 100644 --- a/LexicalAnalyzer.csproj +++ b/LexicalAnalyzer.csproj @@ -46,7 +46,7 @@ - + Form @@ -56,6 +56,8 @@ + + MainForm.cs diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs index 582cb87..2f7f14e 100644 --- a/MainForm.Designer.cs +++ b/MainForm.Designer.cs @@ -28,9 +28,9 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); this.tabControl1 = new System.Windows.Forms.TabControl(); - this.tabPage_sourceFile = new System.Windows.Forms.TabPage(); + this.tabPage_SourceFile = new System.Windows.Forms.TabPage(); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.textBox_FileViewer = new System.Windows.Forms.TextBox(); this.button_openFile = new System.Windows.Forms.Button(); @@ -39,36 +39,53 @@ private void InitializeComponent() this.groupBox2 = new System.Windows.Forms.GroupBox(); this.dataGridView_table = new System.Windows.Forms.DataGridView(); this.Number = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Type = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Value = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Type = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.tabPage_SyntaxTree = new System.Windows.Forms.TabPage(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.button_RegenerateTreeView = new System.Windows.Forms.Button(); + this.button_ShowDeepestTreeView = new System.Windows.Forms.Button(); + this.button_ToggleTreeViewVisib = new System.Windows.Forms.Button(); + this.SyntaxTreeView = new System.Windows.Forms.TreeView(); + this.tabPage_CodeGeneration = new System.Windows.Forms.TabPage(); + this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.button_SimpleButton = new System.Windows.Forms.Button(); + this.button_AnotherButton = new System.Windows.Forms.Button(); + this.treeView1 = new System.Windows.Forms.TreeView(); this.tabControl1.SuspendLayout(); - this.tabPage_sourceFile.SuspendLayout(); + this.tabPage_SourceFile.SuspendLayout(); this.groupBox1.SuspendLayout(); this.tabPage_LexicalTable.SuspendLayout(); this.groupBox2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.dataGridView_table)).BeginInit(); + this.tabPage_SyntaxTree.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.tabPage_CodeGeneration.SuspendLayout(); + this.groupBox4.SuspendLayout(); this.SuspendLayout(); // // tabControl1 // - this.tabControl1.Controls.Add(this.tabPage_sourceFile); + this.tabControl1.Controls.Add(this.tabPage_SourceFile); this.tabControl1.Controls.Add(this.tabPage_LexicalTable); + this.tabControl1.Controls.Add(this.tabPage_SyntaxTree); + this.tabControl1.Controls.Add(this.tabPage_CodeGeneration); this.tabControl1.Location = new System.Drawing.Point(0, 0); this.tabControl1.Name = "tabControl1"; this.tabControl1.SelectedIndex = 0; this.tabControl1.Size = new System.Drawing.Size(785, 460); this.tabControl1.TabIndex = 0; // - // tabPage_sourceFile + // tabPage_SourceFile // - this.tabPage_sourceFile.BackColor = System.Drawing.Color.Transparent; - this.tabPage_sourceFile.Controls.Add(this.groupBox1); - this.tabPage_sourceFile.Location = new System.Drawing.Point(4, 22); - this.tabPage_sourceFile.Name = "tabPage_sourceFile"; - this.tabPage_sourceFile.Padding = new System.Windows.Forms.Padding(3); - this.tabPage_sourceFile.Size = new System.Drawing.Size(777, 434); - this.tabPage_sourceFile.TabIndex = 0; - this.tabPage_sourceFile.Text = "Source file"; + this.tabPage_SourceFile.BackColor = System.Drawing.Color.Transparent; + this.tabPage_SourceFile.Controls.Add(this.groupBox1); + this.tabPage_SourceFile.Location = new System.Drawing.Point(4, 22); + this.tabPage_SourceFile.Name = "tabPage_SourceFile"; + this.tabPage_SourceFile.Padding = new System.Windows.Forms.Padding(3); + this.tabPage_SourceFile.Size = new System.Drawing.Size(777, 434); + this.tabPage_SourceFile.TabIndex = 0; + this.tabPage_SourceFile.Text = "Source file"; // // groupBox1 // @@ -139,19 +156,20 @@ private void InitializeComponent() this.dataGridView_table.AllowUserToDeleteRows = false; this.dataGridView_table.AllowUserToResizeColumns = false; this.dataGridView_table.AllowUserToResizeRows = false; + this.dataGridView_table.BackgroundColor = System.Drawing.SystemColors.Control; this.dataGridView_table.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing; this.dataGridView_table.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.Number, - this.Type, - this.Value}); - dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window; - dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(254))); - dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText; - dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.ControlDark; - dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; - dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.False; - this.dataGridView_table.DefaultCellStyle = dataGridViewCellStyle1; + this.Value, + this.Type}); + dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(254))); + dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.ActiveCaption; + dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this.dataGridView_table.DefaultCellStyle = dataGridViewCellStyle2; this.dataGridView_table.Location = new System.Drawing.Point(6, 19); this.dataGridView_table.MultiSelect = false; this.dataGridView_table.Name = "dataGridView_table"; @@ -171,6 +189,14 @@ private void InitializeComponent() this.Number.Resizable = System.Windows.Forms.DataGridViewTriState.False; this.Number.Width = 200; // + // Value + // + this.Value.HeaderText = "Value"; + this.Value.Name = "Value"; + this.Value.ReadOnly = true; + this.Value.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.Value.Width = 300; + // // Type // this.Type.HeaderText = "Type"; @@ -179,13 +205,118 @@ private void InitializeComponent() this.Type.Resizable = System.Windows.Forms.DataGridViewTriState.False; this.Type.Width = 245; // - // Value + // tabPage_SyntaxTree // - this.Value.HeaderText = "Value"; - this.Value.Name = "Value"; - this.Value.ReadOnly = true; - this.Value.Resizable = System.Windows.Forms.DataGridViewTriState.False; - this.Value.Width = 300; + this.tabPage_SyntaxTree.Controls.Add(this.groupBox3); + this.tabPage_SyntaxTree.Location = new System.Drawing.Point(4, 22); + this.tabPage_SyntaxTree.Name = "tabPage_SyntaxTree"; + this.tabPage_SyntaxTree.Size = new System.Drawing.Size(777, 434); + this.tabPage_SyntaxTree.TabIndex = 2; + this.tabPage_SyntaxTree.Text = "Syntax tree"; + this.tabPage_SyntaxTree.UseVisualStyleBackColor = true; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.button_RegenerateTreeView); + this.groupBox3.Controls.Add(this.button_ShowDeepestTreeView); + this.groupBox3.Controls.Add(this.button_ToggleTreeViewVisib); + this.groupBox3.Controls.Add(this.SyntaxTreeView); + this.groupBox3.Location = new System.Drawing.Point(8, 6); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(760, 421); + this.groupBox3.TabIndex = 1; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Syntax tree"; + // + // button_RegenerateTreeView + // + this.button_RegenerateTreeView.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(254))); + this.button_RegenerateTreeView.Location = new System.Drawing.Point(343, 18); + this.button_RegenerateTreeView.Name = "button_RegenerateTreeView"; + this.button_RegenerateTreeView.Size = new System.Drawing.Size(129, 22); + this.button_RegenerateTreeView.TabIndex = 4; + this.button_RegenerateTreeView.Text = "Regenerate tree"; + this.button_RegenerateTreeView.UseVisualStyleBackColor = true; + this.button_RegenerateTreeView.Click += new System.EventHandler(this.button_RegenerateTreeView_Click); + // + // button_ShowDeepestTreeView + // + this.button_ShowDeepestTreeView.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(254))); + this.button_ShowDeepestTreeView.Location = new System.Drawing.Point(484, 18); + this.button_ShowDeepestTreeView.Name = "button_ShowDeepestTreeView"; + this.button_ShowDeepestTreeView.Size = new System.Drawing.Size(129, 22); + this.button_ShowDeepestTreeView.TabIndex = 3; + this.button_ShowDeepestTreeView.Text = "Show deepest path"; + this.button_ShowDeepestTreeView.UseVisualStyleBackColor = true; + this.button_ShowDeepestTreeView.Click += new System.EventHandler(this.button_ShowDeepestTreeView_Click); + // + // button_ToggleTreeViewVisib + // + this.button_ToggleTreeViewVisib.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(254))); + this.button_ToggleTreeViewVisib.Location = new System.Drawing.Point(625, 18); + this.button_ToggleTreeViewVisib.Name = "button_ToggleTreeViewVisib"; + this.button_ToggleTreeViewVisib.Size = new System.Drawing.Size(129, 22); + this.button_ToggleTreeViewVisib.TabIndex = 2; + this.button_ToggleTreeViewVisib.Text = "Toggle Visibility"; + this.button_ToggleTreeViewVisib.UseVisualStyleBackColor = true; + this.button_ToggleTreeViewVisib.Click += new System.EventHandler(this.button_ToggleTreeViewVisib_Click); + // + // SyntaxTreeView + // + this.SyntaxTreeView.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.SyntaxTreeView.Location = new System.Drawing.Point(6, 46); + this.SyntaxTreeView.Name = "SyntaxTreeView"; + this.SyntaxTreeView.Size = new System.Drawing.Size(748, 369); + this.SyntaxTreeView.TabIndex = 0; + // + // tabPage_CodeGeneration + // + this.tabPage_CodeGeneration.Controls.Add(this.groupBox4); + this.tabPage_CodeGeneration.Location = new System.Drawing.Point(4, 22); + this.tabPage_CodeGeneration.Name = "tabPage_CodeGeneration"; + this.tabPage_CodeGeneration.Size = new System.Drawing.Size(777, 434); + this.tabPage_CodeGeneration.TabIndex = 3; + this.tabPage_CodeGeneration.Text = "Code generation"; + this.tabPage_CodeGeneration.UseVisualStyleBackColor = true; + // + // groupBox4 + // + this.groupBox4.Controls.Add(this.button_SimpleButton); + this.groupBox4.Controls.Add(this.button_AnotherButton); + this.groupBox4.Controls.Add(this.treeView1); + this.groupBox4.Location = new System.Drawing.Point(8, 6); + this.groupBox4.Name = "groupBox4"; + this.groupBox4.Size = new System.Drawing.Size(760, 421); + this.groupBox4.TabIndex = 2; + this.groupBox4.TabStop = false; + this.groupBox4.Text = "Code generation"; + // + // button_SimpleButton + // + this.button_SimpleButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(254))); + this.button_SimpleButton.Location = new System.Drawing.Point(484, 18); + this.button_SimpleButton.Name = "button_SimpleButton"; + this.button_SimpleButton.Size = new System.Drawing.Size(129, 22); + this.button_SimpleButton.TabIndex = 5; + this.button_SimpleButton.Text = "Simple Button"; + this.button_SimpleButton.UseVisualStyleBackColor = true; + // + // button_AnotherButton + // + this.button_AnotherButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(254))); + this.button_AnotherButton.Location = new System.Drawing.Point(625, 18); + this.button_AnotherButton.Name = "button_AnotherButton"; + this.button_AnotherButton.Size = new System.Drawing.Size(129, 22); + this.button_AnotherButton.TabIndex = 4; + this.button_AnotherButton.Text = "Another Button"; + this.button_AnotherButton.UseVisualStyleBackColor = true; + // + // treeView1 + // + this.treeView1.Location = new System.Drawing.Point(6, 46); + this.treeView1.Name = "treeView1"; + this.treeView1.Size = new System.Drawing.Size(748, 369); + this.treeView1.TabIndex = 0; // // MainForm // @@ -198,12 +329,16 @@ private void InitializeComponent() this.Name = "MainForm"; this.Text = "Lexical analyzer"; this.tabControl1.ResumeLayout(false); - this.tabPage_sourceFile.ResumeLayout(false); + this.tabPage_SourceFile.ResumeLayout(false); this.groupBox1.ResumeLayout(false); this.groupBox1.PerformLayout(); this.tabPage_LexicalTable.ResumeLayout(false); this.groupBox2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.dataGridView_table)).EndInit(); + this.tabPage_SyntaxTree.ResumeLayout(false); + this.groupBox3.ResumeLayout(false); + this.tabPage_CodeGeneration.ResumeLayout(false); + this.groupBox4.ResumeLayout(false); this.ResumeLayout(false); } @@ -211,7 +346,7 @@ private void InitializeComponent() #endregion private System.Windows.Forms.TabControl tabControl1; - private System.Windows.Forms.TabPage tabPage_sourceFile; + private System.Windows.Forms.TabPage tabPage_SourceFile; private System.Windows.Forms.TabPage tabPage_LexicalTable; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.Button button_openFile; @@ -219,8 +354,19 @@ private void InitializeComponent() private System.Windows.Forms.TextBox textBox_FileViewer; private System.Windows.Forms.DataGridView dataGridView_table; private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.TabPage tabPage_SyntaxTree; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.TreeView SyntaxTreeView; private System.Windows.Forms.DataGridViewTextBoxColumn Number; - private System.Windows.Forms.DataGridViewTextBoxColumn Type; private System.Windows.Forms.DataGridViewTextBoxColumn Value; + private System.Windows.Forms.DataGridViewTextBoxColumn Type; + private System.Windows.Forms.TabPage tabPage_CodeGeneration; + private System.Windows.Forms.Button button_ToggleTreeViewVisib; + private System.Windows.Forms.GroupBox groupBox4; + private System.Windows.Forms.TreeView treeView1; + private System.Windows.Forms.Button button_ShowDeepestTreeView; + private System.Windows.Forms.Button button_SimpleButton; + private System.Windows.Forms.Button button_AnotherButton; + private System.Windows.Forms.Button button_RegenerateTreeView; } } \ No newline at end of file diff --git a/MainForm.cs b/MainForm.cs index ce7c75e..e7572c0 100644 --- a/MainForm.cs +++ b/MainForm.cs @@ -1,5 +1,7 @@ using LexicalAnalyzer.LexicalAnalyzer.Source; +using LexicalAnalyzer.Source; using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Windows.Forms; @@ -8,9 +10,29 @@ namespace LexicalAnalyzer { public partial class MainForm : Form { + private const bool DEV_MODE = true; + private bool syntaxTreeIsExpanded = false; + + private ArrayList list = new ArrayList(); + public MainForm() { InitializeComponent(); + + if (DEV_MODE) + { + textBox_FilePath.Text = "X:\\Dev\\Projects\\GUMRF\\LexicalAnalyzer\\Tests\\Normal code Simple.txt"; + if (File.Exists(textBox_FilePath.Text)) + { + using (StreamReader reader = new StreamReader(textBox_FilePath.Text)) + { + string fileContent = reader.ReadToEnd(); + textBox_FileViewer.Text = fileContent; + } + + fillTabsContent(); + } + } } private void button_openFile_Click(object sender, EventArgs e) @@ -34,7 +56,7 @@ private void button_openFile_Click(object sender, EventArgs e) textBox_FilePath.Text = filePath; textBox_FileViewer.Text = fileContent; } - addListLexToDataTableView(); + fillTabsContent(); } } } @@ -45,8 +67,49 @@ private void button_openFile_Click(object sender, EventArgs e) } catch (Exception exception) { - MessageBox.Show(exception.Message, "Unexpected exception", MessageBoxButtons.OK); - throw; + MessageBox.Show(exception.Message, this.GetType().Name, MessageBoxButtons.OK); + } + } + + private void button_ToggleTreeViewVisib_Click(object sender, EventArgs e) + { + try + { + if (syntaxTreeIsExpanded) + { + SyntaxTreeView?.CollapseAll(); + } + else + { + SyntaxTreeView?.ExpandAll(); + } + syntaxTreeIsExpanded = !syntaxTreeIsExpanded; + } + catch (Exception exception) + { + MessageBox.Show(exception.Message, this.GetType().Name, MessageBoxButtons.OK); + } + } + + private void button_ShowDeepestTreeView_Click(object sender, EventArgs e) + { + try + { + GetDeeperLevel(SyntaxTreeView?.Nodes[0]); + list.Sort(); + } + catch (Exception exception) + { + MessageBox.Show(exception.Message, this.GetType().Name, MessageBoxButtons.OK); + } + } + + private void GetDeeperLevel(TreeNode node) + { + for (int i = 0; i < node.Nodes.Count; i++) + { + GetDeeperLevel(node.Nodes[i]); + list.Add(node.Nodes[i].Level); } } @@ -54,30 +117,59 @@ private void textBox_FilePath_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == (Int32)(Keys.Enter)) { - if (File.Exists(textBox_FilePath.Text)) + try { - using (StreamReader reader = new StreamReader(textBox_FilePath.Text)) + if (File.Exists(textBox_FilePath.Text)) { - string fileContent = reader.ReadToEnd(); - textBox_FileViewer.Text = fileContent; + using (StreamReader reader = new StreamReader(textBox_FilePath.Text)) + { + string fileContent = reader.ReadToEnd(); + textBox_FileViewer.Text = fileContent; + } + fillTabsContent(); } - addListLexToDataTableView(); + } + catch (Exception exception) + { + MessageBox.Show(exception.Message, this.GetType().Name, MessageBoxButtons.OK); + throw; } } } - private void addListLexToDataTableView() + private void fillTabsContent() { - dataGridView_table.Rows.Clear(); - - // Transferring data to the method and then analyzing it - Analyzer lexicAnalyzer = new Analyzer(); - List lexicList = lexicAnalyzer.getLexemesList(textBox_FileViewer.Text); + Lexer lexicalAnalyzer = new Lexer(); + Parser parser = new Parser(); + // Fill in the table of lexemes + dataGridView_table?.Rows.Clear(); + List lexicList = lexicalAnalyzer.getLexemesList(textBox_FileViewer.Text); + for (int i = 0; i < lexicList.Count; i++) { - dataGridView_table.Rows.Add(i, lexicList[i].lexemWord, lexicList[i].lexemType); + dataGridView_table.Rows.Add(i + 1, lexicList[i].word, lexicList[i].type); + } + + // Build a syntax tree + parser.GenerateAbstractSyntaxTree(SyntaxTreeView, lexicList); + } + + private void button_RegenerateTreeView_Click(object sender, EventArgs e) + { + if (File.Exists(textBox_FilePath.Text)) + { + using (StreamReader reader = new StreamReader(textBox_FilePath.Text)) + { + string fileContent = reader.ReadToEnd(); + textBox_FileViewer.Text = fileContent; + } + + fillTabsContent(); + SyntaxTreeView?.ExpandAll(); + syntaxTreeIsExpanded = true; } + } } } diff --git a/MainForm.resx b/MainForm.resx index c6eb12d..4643a61 100644 --- a/MainForm.resx +++ b/MainForm.resx @@ -120,10 +120,10 @@ True - + True - + True \ No newline at end of file diff --git a/Source/Lex.cs b/Source/Lex.cs index 8622528..24377cf 100644 --- a/Source/Lex.cs +++ b/Source/Lex.cs @@ -1,21 +1,40 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LexicalAnalyzer.LexicalAnalyzer.Source +namespace LexicalAnalyzer.LexicalAnalyzer.Source { + // TODO: Replace text type with enum? + // This may be more convenient than the current implementation. + // Address/search by type (.Semicolon, ,Variable) , but display static name in DataGridTable. + + + public class Lex { - public readonly string lexemType; - public readonly string lexemWord; - - public Lex(string lexemType, string lexemWord) + public readonly Type type; + public readonly string word; + + public enum Type { - this.lexemType = lexemType; - this.lexemWord = lexemWord; + Assign, // := + Colon, // : + Comment_Open, // { + Comment_Close, // } + End, // . + Logical_AND, // and + Logical_NOT, // not + Logical_OR, // or + Logical_XOR, // xor + Parenthesis, // () + Semicolon, // ; + Condition, // true/false + Constant, // const + Variable, // variable + Null, // null-value } + + public Lex(Type _lexemType, string _lexemWord) + { + this.type = _lexemType; + this.word = _lexemWord; + } + } } diff --git a/Source/Analyzer.cs b/Source/Lexer.cs similarity index 73% rename from Source/Analyzer.cs rename to Source/Lexer.cs index b917957..9a4852c 100644 --- a/Source/Analyzer.cs +++ b/Source/Lexer.cs @@ -15,13 +15,21 @@ internal class ServiceWordsDictionary /// This dictionary contains a list of all service words. /// For example: "Condition" : "true", "false" /// - public readonly Dictionary> serviceWords = new Dictionary>() + public readonly Dictionary> serviceWords = new Dictionary>() { - {"Condition", new List { "if", "else", } }, - {"Statement", new List{ "true", "false", } }, - {"Logical", new List {"xor", "or", "and", "not", } }, - {"Service", new List {"program", "var", "begin", "write", "writeln", "for", "to", "do", "random", "randomize", "end"} }, - {"Variable type", new List {"integer", "boolean", } } + {Lex.Type.Condition, new List{ "true", "false", } }, + {Lex.Type.Logical_XOR, new List {"xor"} }, + {Lex.Type.Logical_AND, new List {"and"} }, + {Lex.Type.Logical_NOT, new List {"not"} }, + {Lex.Type.Logical_OR, new List {"or"} }, + }; + + public readonly Dictionary> serviceDelimiters = new Dictionary>() + { + {Lex.Type.Semicolon, new List{";"} }, + {Lex.Type.Colon, new List{":"} }, + {Lex.Type.End, new List{"."} }, + {Lex.Type.Parenthesis, new List{")","("} } }; /// @@ -30,12 +38,12 @@ internal class ServiceWordsDictionary /// The word to be found /// Returns the name of the category in which the passed word was found. /// If no word is found, it returns null. - public string FindServiceWordCategory(string word) + public Lex.Type FindServiceCategory(string word, Dictionary> serviceDict) { // Read every key and get his values - foreach (KeyValuePair> keyValuePair in serviceWords) + foreach (KeyValuePair> keyValuePair in serviceDict) { - string key = keyValuePair.Key; + Lex.Type key = keyValuePair.Key; List values = keyValuePair.Value; foreach (var serviceWord in values) @@ -43,25 +51,22 @@ public string FindServiceWordCategory(string word) if (serviceWord == word) return key; } } - return null; + return Lex.Type.Null; } - public bool isDelimiter(string symbol) - { - List delimiters = new List() - { - ".", ";", ",", "(", ")", "+", "-", "*", "/", "=", ">", "<", - }; - return delimiters.Contains(symbol); - } } - internal class Analyzer + internal class Lexer { + /// + /// Looking for "end." at the end of the program? + /// + private bool waitEndOfProgram = false; + private enum States { SCANNING, IS_WORD, IS_CONST, IS_DELIMITER, IS_ASSIGN, IS_COMMENT, ERROR, FINISHED, }; private States _state; - - private ServiceWordsDictionary _serviceWordsDict = new ServiceWordsDictionary(); + + private ServiceWordsDictionary _serviceDict = new ServiceWordsDictionary(); public readonly List lexemesList = new List(); // File reader: @@ -96,10 +101,17 @@ private void GetNextChar() // If no new characters are found in the string: if (countOfNewSymbols == 0) { - // Most likely, the search for "end." is a task of the syntax analyzer, - // so we can replace it with States.FINISHED. - _state = States.ERROR; - error_message = "The file is read to the end, but \"end.\" never came up."; + if (!waitEndOfProgram) + { + _state = States.FINISHED; + } + else + { + // Most likely, the search for "end." is a task of the syntax analyzer, + // so we can replace it with States.FINISHED. + _state = States.ERROR; + error_message = "The file is read to the end, but \"end.\" never came up."; + } } } @@ -117,9 +129,9 @@ private void AddToBuffer(char symbol) bufferOfChars += symbol; } - private void AddToLexemesList(List _lexemesList, string wordType, string wordBuffer) + private void AddToLexemesList(List _lexemesList, Lex.Type lexType, string wordBuffer) { - _lexemesList.Add(new Lex(wordType, wordBuffer)); + _lexemesList.Add(new Lex(lexType, wordBuffer)); } public List getLexemesList(string text) @@ -144,7 +156,7 @@ private void AnalyzeLexemes(string text) { GetNextChar(); } - else if (char.IsLetter(currentChar[0])) + else if (char.IsLetter(currentChar[0]) || currentChar[0] == '_') { ClearBuffer(); AddToBuffer(currentChar[0]); @@ -174,7 +186,7 @@ private void AnalyzeLexemes(string text) } else if (currentChar[0] == '.') { - AddToLexemesList(lexemesList, "End of program", currentChar[0].ToString()); + AddToLexemesList(lexemesList, Lex.Type.End, currentChar[0].ToString()); _state = States.FINISHED; } else @@ -185,24 +197,24 @@ private void AnalyzeLexemes(string text) // This state tries to find a service word equal to the word in the buffer case States.IS_WORD: - if (char.IsLetterOrDigit(currentChar[0])) + if (char.IsLetterOrDigit(currentChar[0]) || currentChar[0] == '_') { AddToBuffer(currentChar[0]); GetNextChar(); } else { - string serviceWordType = _serviceWordsDict.FindServiceWordCategory(bufferOfChars); + Lex.Type serviceWordType = _serviceDict.FindServiceCategory(bufferOfChars, _serviceDict.serviceWords); // Found service word from the dictionary - if (serviceWordType != null) + if (serviceWordType != Lex.Type.Null) { AddToLexemesList(lexemesList, serviceWordType, bufferOfChars); } - // If the word is not found in the dictionary, write it as "Variable" else { - AddToLexemesList(lexemesList, "Variable", bufferOfChars); + //If the word is not found in the dictionary, write it as "Variable" + AddToLexemesList(lexemesList, Lex.Type.Variable, bufferOfChars); } ClearBuffer(); _state = States.SCANNING; @@ -219,14 +231,14 @@ private void AnalyzeLexemes(string text) } else if (char.IsLetter(currentChar[0])) { - _state = States.ERROR; error_message = "An invalid character was found: a letter.\n" + "No letters are allowed in constants!"; + _state = States.ERROR; break; } else { - AddToLexemesList(lexemesList, "Constant", bufferOfChars); + AddToLexemesList(lexemesList, Lex.Type.Constant, bufferOfChars); ClearBuffer(); _state = States.SCANNING; } @@ -235,15 +247,20 @@ private void AnalyzeLexemes(string text) case States.IS_DELIMITER: ClearBuffer(); AddToBuffer(currentChar[0]); - if (_serviceWordsDict.isDelimiter(bufferOfChars)) + + Lex.Type serviceDelimiters = _serviceDict.FindServiceCategory(bufferOfChars, _serviceDict.serviceDelimiters); + + // Found service word from the dictionary + if (serviceDelimiters != Lex.Type.Null) { - AddToLexemesList(lexemesList, "Delimiter", currentChar[0].ToString()); + AddToLexemesList(lexemesList, serviceDelimiters, bufferOfChars); ClearBuffer(); _state = States.SCANNING; GetNextChar(); } else { + error_message = $"The \"IS_DELIMITER\" state is active, but the current symbol \"{bufferOfChars}\" is not delimiter!"; _state = States.ERROR; } break; @@ -252,13 +269,13 @@ private void AnalyzeLexemes(string text) if (currentChar[0] == '=') { AddToBuffer(currentChar[0]); - AddToLexemesList(lexemesList, "Assign", bufferOfChars); + AddToLexemesList(lexemesList, Lex.Type.Assign, bufferOfChars); ClearBuffer(); GetNextChar(); } else { - AddToLexemesList(lexemesList, "Type delimiter", bufferOfChars); + AddToLexemesList(lexemesList, Lex.Type.Colon, bufferOfChars); } _state = States.SCANNING; break; @@ -266,7 +283,7 @@ private void AnalyzeLexemes(string text) case States.IS_COMMENT: bool bClosingBlockFound = false; - AddToLexemesList(lexemesList, "Start of comment block", bufferOfChars); + AddToLexemesList(lexemesList, Lex.Type.Comment_Open, bufferOfChars); ClearBuffer(); while (stringReader.Peek() >= 0) @@ -284,13 +301,13 @@ private void AnalyzeLexemes(string text) _state = States.ERROR; } - AddToLexemesList(lexemesList, "End of comment block", currentChar[0].ToString()); + AddToLexemesList(lexemesList, Lex.Type.Comment_Close, currentChar[0].ToString()); _state = States.SCANNING; GetNextChar(); break; case States.ERROR: - MessageBox.Show(error_message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(error_message, "State error!", MessageBoxButtons.OK, MessageBoxIcon.Error); _state = States.FINISHED; break; @@ -301,7 +318,7 @@ private void AnalyzeLexemes(string text) } catch (Exception _err) { - MessageBox.Show(_err.Message, "Exception", MessageBoxButtons.OK); + MessageBox.Show(_err.Message, "{FSM} Exception", MessageBoxButtons.OK); throw; } diff --git a/Source/Parser.cs b/Source/Parser.cs new file mode 100644 index 0000000..3fc3e45 --- /dev/null +++ b/Source/Parser.cs @@ -0,0 +1,120 @@ +using LexicalAnalyzer.LexicalAnalyzer.Source; +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace LexicalAnalyzer.Source +{ + class Parser + { + private readonly ParserRules rules = new ParserRules(); + + public void GenerateAbstractSyntaxTree(TreeView treeView, List lexes) + { + try + { + treeView.Nodes.Clear(); + treeView.Nodes.Add("E"); + + TreeNode rootTreeNode = treeView.Nodes[0]; + TreeNode blockTreeNode = null; + int blockExprIndex = 0; + int blockBeginIndex = 0; + + // Iterate through the list of lexemes, looking at each + for (int blockEndIndex = blockBeginIndex; blockEndIndex < lexes.Count; blockEndIndex++) + { + // If a syntactically correct string ending with a semicolon is found + if (lexes[blockEndIndex].type == Lex.Type.Semicolon) + { + rootTreeNode.Nodes.Add("E"); + + // The root block of the expression is temporarily stored here + blockTreeNode = rootTreeNode.Nodes[blockExprIndex]; + + GenerateTreeNodes(blockTreeNode, lexes, blockBeginIndex, blockEndIndex); + blockTreeNode.Nodes.Add(";"); + blockBeginIndex = blockEndIndex + 1; + + // Move to the next root block of the expression + blockExprIndex++; + } + } + } + catch (Exception e) + { + MessageBox.Show(e.Message, this.GetType().Name, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private void GenerateTreeNodes(TreeNode root, List lexes, int _begin, int _end) + { + for (int i = _begin; i <= _end; i++) + { + // Terminal types (true, not, variable, symbols, etc) + if (lexes[i].type == Lex.Type.Variable) AddChildNode(root, lexes[i].word); + else if (lexes[i].type == Lex.Type.Condition) AddChildNode(root, lexes[i].word); + else if (isLogicalType(lexes[i].type)) root.Nodes.Add(lexes[i].word); + + // Can be a nested expressions + else if (lexes[i].word == "(") + { + TreeNode parenthesisExp = root.Nodes.Add("E"); + parenthesisExp.Nodes.Add("("); + + TreeNode temporary = parenthesisExp.Nodes.Add("E"); + int parenthesisEnd = i; + + for (; parenthesisEnd < _end; parenthesisEnd++) + { + if (lexes[parenthesisEnd].word == ")") + { + GenerateTreeNodes(temporary, lexes, i + 1, parenthesisEnd); + } + } + i = parenthesisEnd; + parenthesisExp.Nodes.Add(")"); + } + + // It can be either a nested expression or a terminal type + else if (lexes[i].type == Lex.Type.Assign) + { + root.Nodes.Add(":="); + + // If there are more than 2 tokens left in the string (a difficult expression is found) + if (_end - i > 2) + { + TreeNode expression = root.Nodes.Add("E"); + GenerateTreeNodes(expression, lexes, i + 1, _end); + return; + } + } + } + } + + + /// + /// Adds a child node with the specified name + /// + /// The parent node to which to add a new node + /// Name of added node + private void AddChildNode(TreeNode parent, string name) + { + TreeNode temporary = parent.Nodes.Add("E"); + temporary.Nodes.Add(name); + } + + private bool isLogicalType(Lex.Type type) + { + if (type == Lex.Type.Logical_AND || type == Lex.Type.Logical_XOR || + type == Lex.Type.Logical_OR || type == Lex.Type.Logical_NOT) + { + return true; + } + else + { + return false; + } + } + } +} diff --git a/Source/ParserRules.cs b/Source/ParserRules.cs new file mode 100644 index 0000000..41d2b8a --- /dev/null +++ b/Source/ParserRules.cs @@ -0,0 +1,96 @@ +using LexicalAnalyzer.LexicalAnalyzer.Source; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace LexicalAnalyzer.Source +{ + class ParserRulesPriorities + { + public Dictionary RulesPriorities = new Dictionary() + { + {0, Lex.Type.Semicolon }, + {1, Lex.Type.Parenthesis }, + {2, Lex.Type.Logical_NOT }, + {3, Lex.Type.Logical_AND }, + {4, Lex.Type.Logical_XOR }, + {5, Lex.Type.Logical_OR }, + {6, Lex.Type.Condition }, + {7, Lex.Type.Assign }, + {8, Lex.Type.Variable }, + {9, Lex.Type.Null }, + }; + } + + class ParserRules + { + // TODO: Rule_IsVariable (List lexesList, int currentLexIndex) { ... } + public bool Rule_EndsOnSemicolon(string text) + { + string pattern = @";$"; + return Regex.IsMatch(text, pattern); + } + + public bool Rule_IsVariable(string text) + { + string pattern = "Variable"; + return pattern == text; + } + + public bool Rule_IsAssign(string text) + { + string pattern = @":="; + return Regex.IsMatch(text, pattern); + } + + public bool Rule_IsSemicolon(string text) + { + return text == ";"; + } + + public bool Rule_IsStatement(string text) + { + return text == "Statement"; + } + + /// + /// AND, OR, XOR + /// + /// + /// + public bool Rule_IsBinaryOperand(string text) + { + return text == "and" || text == "or" || text == "xor"; + } + + /// + /// NOT + /// + /// + /// + public bool Rule_IsUnaryOperand(string text) + { + return text == "not"; + } + + public bool Rule_IsOpeningParenthesis(string text) + { + return text == "("; + } + + public bool Rule_IsClosingParenthesis(string text) + { + return text == ")"; + } + + /// + /// Binary-AND/OR/XOR, unary-NOT, ( + /// + /// + /// + public bool Rule_IsExpression(string text) + { + return Rule_IsBinaryOperand(text) || Rule_IsUnaryOperand(text) || Rule_IsOpeningParenthesis(text); + } + + } +} diff --git a/Tests/AST.drawio.png b/Tests/AST.drawio.png new file mode 100644 index 0000000..efeb4e1 Binary files /dev/null and b/Tests/AST.drawio.png differ diff --git a/Tests/AST.png b/Tests/AST.png new file mode 100644 index 0000000..fd5df78 Binary files /dev/null and b/Tests/AST.png differ diff --git a/Tests/Normal code - Binary.txt b/Tests/Normal code - Binary.txt new file mode 100644 index 0000000..12be583 --- /dev/null +++ b/Tests/Normal code - Binary.txt @@ -0,0 +1,12 @@ +program bool; + var x, y: boolean; +begin + x := true; + y := x and true; + y := false1; + y := true | false; + if x = true then + writeln(x); + +end. + \ No newline at end of file diff --git a/Tests/Normal code Simple 2.txt b/Tests/Normal code Simple 2.txt new file mode 100644 index 0000000..7b54fc0 --- /dev/null +++ b/Tests/Normal code Simple 2.txt @@ -0,0 +1,7 @@ + +true_val := true; +false_val := false; + +val_1 := not (true_val and false_val); + +val_2 := true or not (true_val xor false); \ No newline at end of file diff --git a/Tests/Normal code Simple.txt b/Tests/Normal code Simple.txt new file mode 100644 index 0000000..ed1d4d5 --- /dev/null +++ b/Tests/Normal code Simple.txt @@ -0,0 +1,4 @@ +value_1 := true; +value_2 := false; + +value_3 := false or (not true); \ No newline at end of file diff --git a/Tests/Normal code.txt b/Tests/Normal code.txt index af94b2d..0de0e65 100644 --- a/Tests/Normal code.txt +++ b/Tests/Normal code.txt @@ -1,12 +1,21 @@ program bool; var x, y: boolean; begin + {a1 := true; + b1 := false; + c1 := false;} + x := true; y := false; - y := false1; + z := false; + z1 := true; + y := x or true and false xor y not z not y; + + { Пример плохого кода } + {y := xor z andd trues not x or y or z1;} + if x = true then writeln(x); -end. - \ No newline at end of file +end. \ No newline at end of file diff --git a/Tests/One line code.txt b/Tests/One line code.txt deleted file mode 100644 index c1856a4..0000000 --- a/Tests/One line code.txt +++ /dev/null @@ -1 +0,0 @@ -program bool;var x,y:boolean;begin x:=trrue;y:=false;y:=false1;if x=true then writeln (x);end. \ No newline at end of file diff --git a/Tests/PriorityTable.drawio.png b/Tests/PriorityTable.drawio.png new file mode 100644 index 0000000..a6796e6 Binary files /dev/null and b/Tests/PriorityTable.drawio.png differ