From f0a944cf53974d991d6affc1e449edd2a8f90466 Mon Sep 17 00:00:00 2001 From: rollrat Date: Thu, 14 May 2020 16:11:39 +0900 Subject: [PATCH] Add LALR from LR(0) Method to GUI --- InhaCC/App.config | 9 + InhaCC/Graph.cs | 51 + InhaCC/InhaCC.csproj | 116 + InhaCC/MainForm.Designer.cs | 689 ++++++ InhaCC/MainForm.cs | 294 +++ InhaCC/MainForm.resx | 120 + InhaCC/Program.cs | 22 + InhaCC/Properties/AssemblyInfo.cs | 36 + InhaCC/Properties/Resources.Designer.cs | 63 + InhaCC/Properties/Resources.resx | 117 + InhaCC/Properties/Settings.Designer.cs | 26 + InhaCC/Properties/Settings.settings | 7 + InhaCC/cc/ParserGenerator.cs | 2827 +++++++++++++++++++++++ InhaCC/cc/ScannerGenerator.cs | 1231 ++++++++++ InhaCC/packages.config | 5 + 15 files changed, 5613 insertions(+) create mode 100644 InhaCC/App.config create mode 100644 InhaCC/Graph.cs create mode 100644 InhaCC/InhaCC.csproj create mode 100644 InhaCC/MainForm.Designer.cs create mode 100644 InhaCC/MainForm.cs create mode 100644 InhaCC/MainForm.resx create mode 100644 InhaCC/Program.cs create mode 100644 InhaCC/Properties/AssemblyInfo.cs create mode 100644 InhaCC/Properties/Resources.Designer.cs create mode 100644 InhaCC/Properties/Resources.resx create mode 100644 InhaCC/Properties/Settings.Designer.cs create mode 100644 InhaCC/Properties/Settings.settings create mode 100644 InhaCC/cc/ParserGenerator.cs create mode 100644 InhaCC/cc/ScannerGenerator.cs create mode 100644 InhaCC/packages.config diff --git a/InhaCC/App.config b/InhaCC/App.config new file mode 100644 index 0000000..b0ce3e1 --- /dev/null +++ b/InhaCC/App.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/InhaCC/Graph.cs b/InhaCC/Graph.cs new file mode 100644 index 0000000..eea2466 --- /dev/null +++ b/InhaCC/Graph.cs @@ -0,0 +1,51 @@ +/* + + Copyright (C) 2019. rollrat All Rights Reserved. + + Author: Jeong HyunJun + +*/ + +using GraphVizWrapper; +using GraphVizWrapper.Commands; +using GraphVizWrapper.Queries; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace InhaCC +{ + public class Graph + { + public static Bitmap ToImage(string str) + { + var getStartProcessQuery = new GetStartProcessQuery(); + var getProcessStartInfoQuery = new GetProcessStartInfoQuery(); + var registerLayoutPluginCommand = new RegisterLayoutPluginCommand(getProcessStartInfoQuery, getStartProcessQuery); + + var wrapper = new GraphGeneration(getStartProcessQuery, + getProcessStartInfoQuery, + registerLayoutPluginCommand); + + byte[] output = wrapper.GenerateGraph(str /*"digraph{a -> b; b -> c; c -> a;}"*/, Enums.GraphReturnType.Png); + + return ByteToImage(output); + } + + + private static Bitmap ByteToImage(byte[] blob) + { + MemoryStream mStream = new MemoryStream(); + byte[] pData = blob; + mStream.Write(pData, 0, Convert.ToInt32(pData.Length)); + Bitmap bm = new Bitmap(mStream, false); + mStream.Dispose(); + return bm; + } + + } +} diff --git a/InhaCC/InhaCC.csproj b/InhaCC/InhaCC.csproj new file mode 100644 index 0000000..339900b --- /dev/null +++ b/InhaCC/InhaCC.csproj @@ -0,0 +1,116 @@ + + + + + Debug + AnyCPU + {F608DB8C-F9F1-40C1-866F-BA348E566E64} + WinExe + InhaCC + InhaCC + v4.5 + 512 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + + ..\packages\GraphViz.NET.1.0.0\lib\net40\GraphVizWrapper.dll + + + + ..\packages\Microsoft.Bcl.Immutable.1.0.34\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + + + + + + + + + + + + + + + + Form + + + MainForm.cs + + + + + MainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + \ No newline at end of file diff --git a/InhaCC/MainForm.Designer.cs b/InhaCC/MainForm.Designer.cs new file mode 100644 index 0000000..d982bf7 --- /dev/null +++ b/InhaCC/MainForm.Designer.cs @@ -0,0 +1,689 @@ +namespace InhaCC +{ + partial class MainForm + { + /// + /// 필수 디자이너 변수입니다. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 사용 중인 모든 리소스를 정리합니다. + /// + /// 관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form 디자이너에서 생성한 코드 + + /// + /// 디자이너 지원에 필요한 메서드입니다. + /// 이 메서드의 내용을 코드 편집기로 수정하지 마세요. + /// + private void InitializeComponent() + { + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.bREA = new System.Windows.Forms.Button(); + this.tbRE = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.tabControl2 = new System.Windows.Forms.TabControl(); + this.tabPage7 = new System.Windows.Forms.TabPage(); + this.panel4 = new System.Windows.Forms.Panel(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.tabPage9 = new System.Windows.Forms.TabPage(); + this.panel3 = new System.Windows.Forms.Panel(); + this.pictureBox2 = new System.Windows.Forms.PictureBox(); + this.tabPage11 = new System.Windows.Forms.TabPage(); + this.panel2 = new System.Windows.Forms.Panel(); + this.pictureBox3 = new System.Windows.Forms.PictureBox(); + this.tabPage10 = new System.Windows.Forms.TabPage(); + this.panel1 = new System.Windows.Forms.Panel(); + this.pictureBox4 = new System.Windows.Forms.PictureBox(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.rtbLS = new System.Windows.Forms.RichTextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.bLT = new System.Windows.Forms.Button(); + this.rtbLT = new System.Windows.Forms.RichTextBox(); + this.bLG = new System.Windows.Forms.Button(); + this.rtbLLD = new System.Windows.Forms.RichTextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.bPGG = new System.Windows.Forms.Button(); + this.bPGT = new System.Windows.Forms.Button(); + this.rtbPGTEST = new System.Windows.Forms.RichTextBox(); + this.label10 = new System.Windows.Forms.Label(); + this.rtbPGS = new System.Windows.Forms.RichTextBox(); + this.label9 = new System.Windows.Forms.Label(); + this.rbLR1 = new System.Windows.Forms.RadioButton(); + this.rbLALR = new System.Windows.Forms.RadioButton(); + this.rbSLR = new System.Windows.Forms.RadioButton(); + this.label8 = new System.Windows.Forms.Label(); + this.rtbPGC = new System.Windows.Forms.RichTextBox(); + this.label7 = new System.Windows.Forms.Label(); + this.rtbPGPR = new System.Windows.Forms.RichTextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.rtbPGT = new System.Windows.Forms.RichTextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.rtbPGNT = new System.Windows.Forms.RichTextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.rbLALR0 = new System.Windows.Forms.RadioButton(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabControl2.SuspendLayout(); + this.tabPage7.SuspendLayout(); + this.panel4.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.tabPage9.SuspendLayout(); + this.panel3.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit(); + this.tabPage11.SuspendLayout(); + this.panel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).BeginInit(); + this.tabPage10.SuspendLayout(); + this.panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).BeginInit(); + this.tabPage2.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.SuspendLayout(); + // + // tabControl1 + // + this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Controls.Add(this.tabPage3); + this.tabControl1.Location = new System.Drawing.Point(12, 12); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(1391, 743); + this.tabControl1.TabIndex = 0; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.bREA); + this.tabPage1.Controls.Add(this.tbRE); + this.tabPage1.Controls.Add(this.label1); + this.tabPage1.Controls.Add(this.tabControl2); + this.tabPage1.Location = new System.Drawing.Point(4, 24); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(1383, 715); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Regular Expression"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // bREA + // + this.bREA.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.bREA.Location = new System.Drawing.Point(1261, 20); + this.bREA.Name = "bREA"; + this.bREA.Size = new System.Drawing.Size(85, 23); + this.bREA.TabIndex = 5; + this.bREA.Text = "Apply"; + this.bREA.UseVisualStyleBackColor = true; + this.bREA.Click += new System.EventHandler(this.bREA_Click); + // + // tbRE + // + this.tbRE.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tbRE.Font = new System.Drawing.Font("Consolas", 9F); + this.tbRE.Location = new System.Drawing.Point(76, 21); + this.tbRE.Name = "tbRE"; + this.tbRE.Size = new System.Drawing.Size(1179, 22); + this.tbRE.TabIndex = 4; + this.tbRE.Text = "[0-9]+(\\.[0-9]+)?[Ee][\\+\\-]?[0-9]+"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(18, 24); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(52, 15); + this.label1.TabIndex = 3; + this.label1.Text = "Pattern: "; + // + // tabControl2 + // + this.tabControl2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl2.Controls.Add(this.tabPage7); + this.tabControl2.Controls.Add(this.tabPage9); + this.tabControl2.Controls.Add(this.tabPage11); + this.tabControl2.Controls.Add(this.tabPage10); + this.tabControl2.Location = new System.Drawing.Point(6, 60); + this.tabControl2.Name = "tabControl2"; + this.tabControl2.SelectedIndex = 0; + this.tabControl2.Size = new System.Drawing.Size(1374, 649); + this.tabControl2.TabIndex = 0; + // + // tabPage7 + // + this.tabPage7.Controls.Add(this.panel4); + this.tabPage7.Location = new System.Drawing.Point(4, 24); + this.tabPage7.Name = "tabPage7"; + this.tabPage7.Padding = new System.Windows.Forms.Padding(3); + this.tabPage7.Size = new System.Drawing.Size(1366, 621); + this.tabPage7.TabIndex = 5; + this.tabPage7.Text = "NFA"; + this.tabPage7.UseVisualStyleBackColor = true; + // + // panel4 + // + this.panel4.AutoScroll = true; + this.panel4.Controls.Add(this.pictureBox1); + this.panel4.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel4.Location = new System.Drawing.Point(3, 3); + this.panel4.Name = "panel4"; + this.panel4.Size = new System.Drawing.Size(1360, 615); + this.panel4.TabIndex = 4; + // + // pictureBox1 + // + this.pictureBox1.Location = new System.Drawing.Point(3, 3); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(1067, 409); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.pictureBox1.TabIndex = 2; + this.pictureBox1.TabStop = false; + // + // tabPage9 + // + this.tabPage9.Controls.Add(this.panel3); + this.tabPage9.Location = new System.Drawing.Point(4, 24); + this.tabPage9.Name = "tabPage9"; + this.tabPage9.Size = new System.Drawing.Size(1366, 621); + this.tabPage9.TabIndex = 1; + this.tabPage9.Text = "e-NFA"; + this.tabPage9.UseVisualStyleBackColor = true; + // + // panel3 + // + this.panel3.AutoScroll = true; + this.panel3.Controls.Add(this.pictureBox2); + this.panel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel3.Location = new System.Drawing.Point(0, 0); + this.panel3.Name = "panel3"; + this.panel3.Size = new System.Drawing.Size(1366, 621); + this.panel3.TabIndex = 4; + // + // pictureBox2 + // + this.pictureBox2.Location = new System.Drawing.Point(3, 3); + this.pictureBox2.Name = "pictureBox2"; + this.pictureBox2.Size = new System.Drawing.Size(1067, 409); + this.pictureBox2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.pictureBox2.TabIndex = 2; + this.pictureBox2.TabStop = false; + // + // tabPage11 + // + this.tabPage11.Controls.Add(this.panel2); + this.tabPage11.Location = new System.Drawing.Point(4, 24); + this.tabPage11.Name = "tabPage11"; + this.tabPage11.Size = new System.Drawing.Size(1366, 621); + this.tabPage11.TabIndex = 3; + this.tabPage11.Text = "DFA"; + this.tabPage11.UseVisualStyleBackColor = true; + // + // panel2 + // + this.panel2.AutoScroll = true; + this.panel2.Controls.Add(this.pictureBox3); + this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel2.Location = new System.Drawing.Point(0, 0); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(1366, 621); + this.panel2.TabIndex = 4; + // + // pictureBox3 + // + this.pictureBox3.Location = new System.Drawing.Point(3, 3); + this.pictureBox3.Name = "pictureBox3"; + this.pictureBox3.Size = new System.Drawing.Size(1067, 409); + this.pictureBox3.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.pictureBox3.TabIndex = 2; + this.pictureBox3.TabStop = false; + // + // tabPage10 + // + this.tabPage10.Controls.Add(this.panel1); + this.tabPage10.Location = new System.Drawing.Point(4, 24); + this.tabPage10.Name = "tabPage10"; + this.tabPage10.Size = new System.Drawing.Size(1366, 621); + this.tabPage10.TabIndex = 4; + this.tabPage10.Text = "DFA Minimization"; + this.tabPage10.UseVisualStyleBackColor = true; + // + // panel1 + // + this.panel1.AutoScroll = true; + this.panel1.Controls.Add(this.pictureBox4); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(1366, 621); + this.panel1.TabIndex = 3; + // + // pictureBox4 + // + this.pictureBox4.Location = new System.Drawing.Point(3, 3); + this.pictureBox4.Name = "pictureBox4"; + this.pictureBox4.Size = new System.Drawing.Size(1067, 409); + this.pictureBox4.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.pictureBox4.TabIndex = 2; + this.pictureBox4.TabStop = false; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.rtbLS); + this.tabPage2.Controls.Add(this.label3); + this.tabPage2.Controls.Add(this.bLT); + this.tabPage2.Controls.Add(this.rtbLT); + this.tabPage2.Controls.Add(this.bLG); + this.tabPage2.Controls.Add(this.rtbLLD); + this.tabPage2.Controls.Add(this.label2); + this.tabPage2.Location = new System.Drawing.Point(4, 24); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Size = new System.Drawing.Size(1383, 715); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Lexer Generator"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // rtbLS + // + this.rtbLS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.rtbLS.Font = new System.Drawing.Font("Consolas", 9F); + this.rtbLS.Location = new System.Drawing.Point(404, 35); + this.rtbLS.Name = "rtbLS"; + this.rtbLS.Size = new System.Drawing.Size(943, 618); + this.rtbLS.TabIndex = 6; + this.rtbLS.Text = ""; + this.rtbLS.TextChanged += new System.EventHandler(this.rtbLS_TextChanged); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(401, 17); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(43, 15); + this.label3.TabIndex = 5; + this.label3.Text = "Status:"; + // + // bLT + // + this.bLT.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bLT.Location = new System.Drawing.Point(99, 659); + this.bLT.Name = "bLT"; + this.bLT.Size = new System.Drawing.Size(231, 34); + this.bLT.TabIndex = 4; + this.bLT.Text = "Test!"; + this.bLT.UseVisualStyleBackColor = true; + this.bLT.Click += new System.EventHandler(this.bLT_Click); + // + // rtbLT + // + this.rtbLT.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.rtbLT.Font = new System.Drawing.Font("Consolas", 9F); + this.rtbLT.Location = new System.Drawing.Point(31, 475); + this.rtbLT.Name = "rtbLT"; + this.rtbLT.Size = new System.Drawing.Size(367, 178); + this.rtbLT.TabIndex = 3; + this.rtbLT.Text = "2-(3+5);\n2 + (6 * 3);\n(3 + 2)*2 + 5;\n2.0E-2+0.5;\n5+10\n"; + // + // bLG + // + this.bLG.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bLG.Location = new System.Drawing.Point(99, 435); + this.bLG.Name = "bLG"; + this.bLG.Size = new System.Drawing.Size(231, 34); + this.bLG.TabIndex = 2; + this.bLG.Text = "Generate!"; + this.bLG.UseVisualStyleBackColor = true; + this.bLG.Click += new System.EventHandler(this.bLG_Click); + // + // rtbLLD + // + this.rtbLLD.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.rtbLLD.Font = new System.Drawing.Font("Consolas", 9F); + this.rtbLLD.Location = new System.Drawing.Point(31, 35); + this.rtbLLD.Name = "rtbLLD"; + this.rtbLLD.Size = new System.Drawing.Size(367, 394); + this.rtbLLD.TabIndex = 1; + this.rtbLLD.Text = "[\\r\\n ] => \"\"\n; => end\n\\+ => plus\n- => minus\n\\* => multiple\n\\/ => divide\n\\( => op" + + "_open\n\\) => op_close\n[_$a-zA-Z][_$a-zA-Z0-9]* => id\n[0-9]+(\\.[0-9]+)?[Ee][\\+\\-]?" + + "[0-9]+ => num\n[0-9]+(\\.[0-9]+)? => num"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(28, 17); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(99, 15); + this.label2.TabIndex = 0; + this.label2.Text = "Lexer Definition: "; + // + // tabPage3 + // + this.tabPage3.Controls.Add(this.rbLALR0); + this.tabPage3.Controls.Add(this.bPGG); + this.tabPage3.Controls.Add(this.bPGT); + this.tabPage3.Controls.Add(this.rtbPGTEST); + this.tabPage3.Controls.Add(this.label10); + this.tabPage3.Controls.Add(this.rtbPGS); + this.tabPage3.Controls.Add(this.label9); + this.tabPage3.Controls.Add(this.rbLR1); + this.tabPage3.Controls.Add(this.rbLALR); + this.tabPage3.Controls.Add(this.rbSLR); + this.tabPage3.Controls.Add(this.label8); + this.tabPage3.Controls.Add(this.rtbPGC); + this.tabPage3.Controls.Add(this.label7); + this.tabPage3.Controls.Add(this.rtbPGPR); + this.tabPage3.Controls.Add(this.label6); + this.tabPage3.Controls.Add(this.rtbPGT); + this.tabPage3.Controls.Add(this.label5); + this.tabPage3.Controls.Add(this.rtbPGNT); + this.tabPage3.Controls.Add(this.label4); + this.tabPage3.Location = new System.Drawing.Point(4, 24); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.Size = new System.Drawing.Size(1383, 715); + this.tabPage3.TabIndex = 2; + this.tabPage3.Text = "Parser Generator"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // bPGG + // + this.bPGG.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.bPGG.Location = new System.Drawing.Point(293, 660); + this.bPGG.Name = "bPGG"; + this.bPGG.Size = new System.Drawing.Size(99, 26); + this.bPGG.TabIndex = 19; + this.bPGG.Text = "Generate"; + this.bPGG.UseVisualStyleBackColor = true; + this.bPGG.Click += new System.EventHandler(this.bPGG_Click); + // + // bPGT + // + this.bPGT.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.bPGT.Location = new System.Drawing.Point(1250, 660); + this.bPGT.Name = "bPGT"; + this.bPGT.Size = new System.Drawing.Size(99, 26); + this.bPGT.TabIndex = 18; + this.bPGT.Text = "Test"; + this.bPGT.UseVisualStyleBackColor = true; + this.bPGT.Click += new System.EventHandler(this.bPGT_Click); + // + // rtbPGTEST + // + this.rtbPGTEST.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.rtbPGTEST.Font = new System.Drawing.Font("Consolas", 9F); + this.rtbPGTEST.Location = new System.Drawing.Point(293, 530); + this.rtbPGTEST.Name = "rtbPGTEST"; + this.rtbPGTEST.Size = new System.Drawing.Size(1056, 124); + this.rtbPGTEST.TabIndex = 17; + this.rtbPGTEST.Text = "2-(3+5);\n2 + (6 * 3);\n(3 + 2)*2 + 5;\n2.0E-2+0.5;\n5+10"; + // + // label10 + // + this.label10.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(290, 512); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(35, 15); + this.label10.TabIndex = 16; + this.label10.Text = "Test: "; + // + // rtbPGS + // + this.rtbPGS.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.rtbPGS.Font = new System.Drawing.Font("Consolas", 9F); + this.rtbPGS.Location = new System.Drawing.Point(293, 51); + this.rtbPGS.Name = "rtbPGS"; + this.rtbPGS.Size = new System.Drawing.Size(1056, 458); + this.rtbPGS.TabIndex = 15; + this.rtbPGS.Text = ""; + this.rtbPGS.TextChanged += new System.EventHandler(this.rtbPGS_TextChanged); + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(290, 33); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(43, 15); + this.label9.TabIndex = 14; + this.label9.Text = "Status:"; + // + // rbLR1 + // + this.rbLR1.AutoSize = true; + this.rbLR1.Location = new System.Drawing.Point(662, 16); + this.rbLR1.Name = "rbLR1"; + this.rbLR1.Size = new System.Drawing.Size(53, 19); + this.rbLR1.TabIndex = 13; + this.rbLR1.Text = "LR(1)"; + this.rbLR1.UseVisualStyleBackColor = true; + // + // rbLALR + // + this.rbLALR.AutoSize = true; + this.rbLALR.Checked = true; + this.rbLALR.Location = new System.Drawing.Point(542, 16); + this.rbLALR.Name = "rbLALR"; + this.rbLALR.Size = new System.Drawing.Size(114, 19); + this.rbLALR.TabIndex = 12; + this.rbLALR.Text = "LALR from LR(1)"; + this.rbLALR.UseVisualStyleBackColor = true; + // + // rbSLR + // + this.rbSLR.AutoSize = true; + this.rbSLR.Location = new System.Drawing.Point(371, 16); + this.rbSLR.Name = "rbSLR"; + this.rbSLR.Size = new System.Drawing.Size(45, 19); + this.rbSLR.TabIndex = 11; + this.rbSLR.Text = "SLR"; + this.rbSLR.UseVisualStyleBackColor = true; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(290, 18); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(75, 15); + this.label8.TabIndex = 10; + this.label8.Text = "Parser Type: "; + // + // rtbPGC + // + this.rtbPGC.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.rtbPGC.Font = new System.Drawing.Font("Consolas", 9F); + this.rtbPGC.Location = new System.Drawing.Point(26, 609); + this.rtbPGC.Name = "rtbPGC"; + this.rtbPGC.Size = new System.Drawing.Size(258, 77); + this.rtbPGC.TabIndex = 9; + this.rtbPGC.Text = "%left +, -\n%left *, /\n%right UMINUS"; + // + // label7 + // + this.label7.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(23, 591); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(57, 15); + this.label7.TabIndex = 8; + this.label7.Text = "Conflicts:"; + // + // rtbPGPR + // + this.rtbPGPR.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.rtbPGPR.Font = new System.Drawing.Font("Consolas", 9F); + this.rtbPGPR.Location = new System.Drawing.Point(26, 219); + this.rtbPGPR.Name = "rtbPGPR"; + this.rtbPGPR.Size = new System.Drawing.Size(258, 369); + this.rtbPGPR.TabIndex = 7; + this.rtbPGPR.Text = "S -> E ;\nE -> E + E\nE -> E - E\nE -> E * E\nE -> E / E\nE -> - E %prec UMINUS\nE -> " + + "( E )\nE -> num"; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(23, 201); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(101, 15); + this.label6.TabIndex = 6; + this.label6.Text = "Production Rules:"; + // + // rtbPGT + // + this.rtbPGT.Font = new System.Drawing.Font("Consolas", 9F); + this.rtbPGT.Location = new System.Drawing.Point(26, 111); + this.rtbPGT.Name = "rtbPGT"; + this.rtbPGT.Size = new System.Drawing.Size(258, 87); + this.rtbPGT.TabIndex = 5; + this.rtbPGT.Text = "equal, =\nmultiple, *\ndivide, /\nplus, +\nminus, -\nnum, num\nop_open, (\nop_close, )\ne" + + "nd, ;"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(23, 93); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(65, 15); + this.label5.TabIndex = 4; + this.label5.Text = "Terminals: "; + // + // rtbPGNT + // + this.rtbPGNT.Font = new System.Drawing.Font("Consolas", 9F); + this.rtbPGNT.Location = new System.Drawing.Point(26, 36); + this.rtbPGNT.Name = "rtbPGNT"; + this.rtbPGNT.Size = new System.Drawing.Size(258, 52); + this.rtbPGNT.TabIndex = 3; + this.rtbPGNT.Text = "S, E"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(23, 18); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(91, 15); + this.label4.TabIndex = 2; + this.label4.Text = "Non-terminals: "; + // + // rbLALR0 + // + this.rbLALR0.AutoSize = true; + this.rbLALR0.Location = new System.Drawing.Point(422, 16); + this.rbLALR0.Name = "rbLALR0"; + this.rbLALR0.Size = new System.Drawing.Size(114, 19); + this.rbLALR0.TabIndex = 20; + this.rbLALR0.Text = "LALR from LR(0)"; + this.rbLALR0.UseVisualStyleBackColor = true; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1415, 767); + this.Controls.Add(this.tabControl1); + this.Font = new System.Drawing.Font("맑은 고딕", 9F); + this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.Name = "MainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "InhaCC GUI - Compiler Compiler Collection"; + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.tabControl2.ResumeLayout(false); + this.tabPage7.ResumeLayout(false); + this.panel4.ResumeLayout(false); + this.panel4.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.tabPage9.ResumeLayout(false); + this.panel3.ResumeLayout(false); + this.panel3.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit(); + this.tabPage11.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).EndInit(); + this.tabPage10.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox4)).EndInit(); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); + this.tabPage3.ResumeLayout(false); + this.tabPage3.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.TabPage tabPage3; + private System.Windows.Forms.TabControl tabControl2; + private System.Windows.Forms.TabPage tabPage9; + private System.Windows.Forms.TabPage tabPage11; + private System.Windows.Forms.Button bREA; + private System.Windows.Forms.TextBox tbRE; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TabPage tabPage10; + private System.Windows.Forms.TabPage tabPage7; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.RichTextBox rtbLLD; + private System.Windows.Forms.Button bLG; + private System.Windows.Forms.RichTextBox rtbLT; + private System.Windows.Forms.Button bLT; + private System.Windows.Forms.RichTextBox rtbLS; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.RichTextBox rtbPGT; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.RichTextBox rtbPGNT; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.RichTextBox rtbPGPR; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.RichTextBox rtbPGC; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.RadioButton rbLR1; + private System.Windows.Forms.RadioButton rbLALR; + private System.Windows.Forms.RadioButton rbSLR; + private System.Windows.Forms.RichTextBox rtbPGTEST; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.RichTextBox rtbPGS; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Button bPGT; + private System.Windows.Forms.Button bPGG; + private System.Windows.Forms.PictureBox pictureBox4; + private System.Windows.Forms.Panel panel4; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Panel panel3; + private System.Windows.Forms.PictureBox pictureBox2; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.PictureBox pictureBox3; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.RadioButton rbLALR0; + } +} + diff --git a/InhaCC/MainForm.cs b/InhaCC/MainForm.cs new file mode 100644 index 0000000..35df9ba --- /dev/null +++ b/InhaCC/MainForm.cs @@ -0,0 +1,294 @@ +/* + + Copyright (C) 2019. rollrat All Rights Reserved. + + Author: Jeong HyunJun + +*/ + +using GraphVizWrapper; +using GraphVizWrapper.Commands; +using GraphVizWrapper.Queries; +using ParserGenerator; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace InhaCC +{ + public partial class MainForm : Form + { + public MainForm() + { + InitializeComponent(); + } + + private void bREA_Click(object sender, EventArgs e) + { + var sr = new SimpleRegex(); + sr.MakeNFA(tbRE.Text); + pictureBox1.Image = Graph.ToImage(SimpleRegex.PrintGraph(sr.Diagram)); + sr.OptimizeNFA(); + pictureBox2.Image = Graph.ToImage(SimpleRegex.PrintGraph(sr.Diagram)); + sr.NFAtoDFA(); + pictureBox3.Image = Graph.ToImage(SimpleRegex.PrintGraph(sr.Diagram)); + sr.MinimizeDFA(); + pictureBox4.Image = Graph.ToImage(SimpleRegex.PrintGraph(sr.Diagram)); + } + + Scanner scanner; + + private void bLG_Click(object sender, EventArgs e) + { + var sg = new ScannerGenerator(); + + try + { + foreach (var line in rtbLLD.Lines) + sg.PushRule(line.Split(new[] { "=>" }, StringSplitOptions.None)[1].Replace("\"", "").Trim(), line.Split(new[] { "=>" }, StringSplitOptions.None)[0].Trim()); + sg.Generate(); + scanner = sg.CreateScannerInstance(); + rtbLS.AppendText("New scanner instance generated!\r\n" + sg.PrintDiagram()); + } + catch (Exception ex) + { + rtbLS.Text = $"Scanner build error!\r\n{ex.Message}\r\n{ex.StackTrace}"; + } + } + + private void bLT_Click(object sender, EventArgs e) + { + if (scanner == null) + { + rtbLS.AppendText("Create scanner instance before testing!\r\n"); + return; + } + + rtbLS.AppendText(" ----------- Start Lexing -----------\r\n"); + scanner.AllocateTarget(rtbLT.Text); + + try + { + while (scanner.Valid()) + { + var ss = scanner.Next(); + if (scanner.Error()) + rtbLS.AppendText("Error!\r\n"); + rtbLS.AppendText($"{ss.Item1},".PadRight(10) + $" {ss.Item2} - line:{ss.Item3}, column:{ss.Item4}\r\n"); + } + } + catch (Exception ex) + { + rtbLS.AppendText("Error!\r\nCheck test case!\r\n"); + } + rtbLS.AppendText(" ------------ End Lexing ------------\r\n"); + } + + private void rtbLS_TextChanged(object sender, EventArgs e) + { + rtbLS.SelectionStart = rtbLS.Text.Length; + rtbLS.ScrollToCaret(); + } + + ShiftReduceParser srparser; + + private void bPGG_Click(object sender, EventArgs e) + { + var gen = new ParserGenerator.ParserGenerator(); + + try + { + var non_terminals = new Dictionary(); + var terminals = new Dictionary(); + + terminals.Add("''", ParserGenerator.ParserGenerator.EmptyString); + + foreach (var nt in rtbPGNT.Text.Split(',')) + non_terminals.Add(nt.Trim(), gen.CreateNewProduction(nt.Trim(), false)); + + foreach (var t in rtbPGT.Lines) + { + if (t.Trim() == "") continue; + + var name = t.Split(',')[0]; + var pp = t.Substring(name.Length + 1).Trim(); + + terminals.Add(pp, gen.CreateNewProduction(name.Trim())); + } + + var prec = new Dictionary>>(); + + foreach (var pp in rtbPGPR.Lines) + { + if (pp.Trim() == "") continue; + + var split = pp.Split(new[] { "->" }, StringSplitOptions.None); + var left = split[0].Trim(); + var right = split[1].Split(' '); + + var prlist = new List(); + bool stay_prec = false; + foreach (var ntt in right) + { + if (string.IsNullOrEmpty(ntt)) continue; + if (ntt == "%prec") { stay_prec = true; continue; } + if (stay_prec) + { + if (!prec.ContainsKey(ntt)) + prec.Add(ntt, new List>()); + prec[ntt].Add(new Tuple(non_terminals[left], non_terminals[left].sub_productions.Count)); + continue; + } + if (non_terminals.ContainsKey(ntt)) + prlist.Add(non_terminals[ntt]); + else if (terminals.ContainsKey(ntt)) + prlist.Add(terminals[ntt]); + else + { + rtbPGS.Text = $"Production rule build error!\r\n{ntt} is neither non-terminal nor terminal!\r\nDeclare the token-name!"; + return; + } + } + non_terminals[left].sub_productions.Add(prlist); + } + + for (int i = rtbPGC.Lines.Length - 1; i >= 0; i--) + { + var line = rtbPGC.Lines[i].Trim(); + if (line == "") continue; + var tt = line.Split(' ')[0]; + var rr = line.Substring(tt.Length).Trim().Split(','); + + var left = true; + var items1 = new List>(); + var items2 = new List(); + + if (tt == "%right") left = false; + + foreach (var ii in rr.Select(x => x.Trim())) + { + if (string.IsNullOrEmpty(ii)) continue; + if (terminals.ContainsKey(ii)) + items2.Add(terminals[ii]); + else if (prec.ContainsKey(ii)) + items1.AddRange(prec[ii]); + else + { + rtbPGS.Text = $"Conflict rule applying error!\r\n{ii} is neither terminal nor %prec!\r\nDeclare the token-name!"; + return; + } + } + + if (items1.Count > 0) + gen.PushConflictSolver(left, items1.ToArray()); + else + gen.PushConflictSolver(left, items2.ToArray()); + } + + rtbPGS.Clear(); + gen.GlobalPrinter.Clear(); + gen.PushStarts(non_terminals[rtbPGNT.Text.Split(',')[0].Trim()]); + if (rbSLR.Checked == true) + { + gen.Generate(); + } + else if (rbLALR0.Checked == true) + { + gen.GenerateLALR2(); + } + else if (rbLALR.Checked == true) + { + gen.GenerateLALR(); + } + else + { + gen.GenerateLR1(); + } + gen.PrintStates(); + gen.PrintTable(); + rtbPGS.AppendText(gen.GlobalPrinter.ToString()); + srparser = gen.CreateShiftReduceParserInstance(); + } catch (Exception ex) + { + rtbPGS.AppendText(gen.GlobalPrinter.ToString()); + rtbPGS.AppendText("Generate Error!\r\n" + ex.Message + "\r\n" + ex.StackTrace); + } + } + + private void bPGT_Click(object sender, EventArgs e) + { + if (scanner == null) + { + rtbPGS.AppendText("Create scanner instance before testing!\r\n"); + return; + } + + if (srparser == null) + { + rtbPGS.AppendText("Create parser instance before testing!\r\n"); + return; + } + + foreach (var line in rtbPGTEST.Lines) + { + rtbPGS.AppendText(" ------ TEST: " + line + "\r\n"); + + srparser.Clear(); + Action insert = (string x, string y) => + { + srparser.Insert(x, y); + if (srparser.Error()) + rtbPGS.AppendText("PARSING ERROR" + "\r\n"); + while (srparser.Reduce()) + { + rtbPGS.AppendText(srparser.Stack() + "\r\n"); + var l = srparser.LatestReduce(); + rtbPGS.AppendText(l.Production.PadLeft(8) + " => "); + rtbPGS.AppendText(string.Join(" ", l.Childs.Select(z => z.Production)) + "\r\n"); + rtbPGS.AppendText(l.Production.PadLeft(8) + " => "); + rtbPGS.AppendText(string.Join(" ", l.Childs.Select(z => z.Contents)) + "\r\n"); + srparser.Insert(x, y); + if (srparser.Error()) + rtbPGS.AppendText("PARSING ERROR" + "\r\n"); + } + rtbPGS.AppendText(srparser.Stack() + "\r\n"); + }; + + scanner.AllocateTarget(line); + + try + { + while (scanner.Valid()) + { + var ss = scanner.Next(); + insert(ss.Item1, ss.Item2); + } + insert("$", "$"); + + var x = srparser.Tree; + } + catch (Exception ex) + { + rtbPGS.AppendText("Error!\r\nCheck test case!\r\n"); + } + + rtbPGS.AppendText(" ------ END TEST ------\r\n"); + } + } + + private void rtbPGS_TextChanged(object sender, EventArgs e) + { + rtbPGS.SelectionStart = rtbPGS.Text.Length; + rtbPGS.ScrollToCaret(); + } + } +} diff --git a/InhaCC/MainForm.resx b/InhaCC/MainForm.resx new file mode 100644 index 0000000..29dcb1b --- /dev/null +++ b/InhaCC/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/InhaCC/Program.cs b/InhaCC/Program.cs new file mode 100644 index 0000000..fb1fa02 --- /dev/null +++ b/InhaCC/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace InhaCC +{ + static class Program + { + /// + /// 해당 응용 프로그램의 주 진입점입니다. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +} diff --git a/InhaCC/Properties/AssemblyInfo.cs b/InhaCC/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..baeda1c --- /dev/null +++ b/InhaCC/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해 +// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면 +// 이러한 특성 값을 변경하세요. +[assembly: AssemblyTitle("InhaCC")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("InhaCC")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에 +// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면 +// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요. +[assembly: ComVisible(false)] + +// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다. +[assembly: Guid("f608db8c-f9f1-40c1-866f-ba348e566e64")] + +// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다. +// +// 주 버전 +// 부 버전 +// 빌드 번호 +// 수정 버전 +// +// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로 +// 지정되도록 할 수 있습니다. +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/InhaCC/Properties/Resources.Designer.cs b/InhaCC/Properties/Resources.Designer.cs new file mode 100644 index 0000000..b83d372 --- /dev/null +++ b/InhaCC/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 이 코드는 도구를 사용하여 생성되었습니다. +// 런타임 버전:4.0.30319.42000 +// +// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 +// 이러한 변경 내용이 손실됩니다. +// +//------------------------------------------------------------------------------ + +namespace InhaCC.Properties { + using System; + + + /// + /// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다. + /// + // 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder + // 클래스에서 자동으로 생성되었습니다. + // 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여 ResGen을 + // 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("InhaCC.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대한 현재 스레드의 CurrentUICulture + /// 속성을 재정의합니다. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/InhaCC/Properties/Resources.resx b/InhaCC/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/InhaCC/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/InhaCC/Properties/Settings.Designer.cs b/InhaCC/Properties/Settings.Designer.cs new file mode 100644 index 0000000..fc6e4bb --- /dev/null +++ b/InhaCC/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// 이 코드는 도구를 사용하여 생성되었습니다. +// 런타임 버전:4.0.30319.42000 +// +// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 +// 이러한 변경 내용이 손실됩니다. +// +//------------------------------------------------------------------------------ + +namespace InhaCC.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.5.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/InhaCC/Properties/Settings.settings b/InhaCC/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/InhaCC/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/InhaCC/cc/ParserGenerator.cs b/InhaCC/cc/ParserGenerator.cs new file mode 100644 index 0000000..b7b4649 --- /dev/null +++ b/InhaCC/cc/ParserGenerator.cs @@ -0,0 +1,2827 @@ +/* + + Copyright (C) 2019. rollrat All Rights Reserved. + + Author: Jeong HyunJun + +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ParserGenerator +{ + #region Parser Production + + public class ParserAction + { + public Action SemanticAction; + public static ParserAction Create(Action action) + => new ParserAction { SemanticAction = action }; + } + + public class ParserProduction + { + public int index; + public string production_name; + public bool isterminal; + public List contents = new List(); + public List> sub_productions = new List>(); + public List temp_actions = new List(); + public List actions = new List(); + ParserGenerator parent; + + public ParserProduction(ParserGenerator parent) { this.parent = parent; } + + public static ParserProduction operator +(ParserProduction p1, ParserProduction p2) + { + p1.contents.Add(p2); + return p1; + } + + public static ParserProduction operator +(ParserProduction pp, ParserAction ac) + { + pp.temp_actions.Add(ac); + return pp; + } + + public static ParserProduction operator +(ParserProduction pp, string name) + { + pp.contents.Add(pp.parent.TryCreateNewProduction(name)); + return pp; + } + + public static ParserProduction operator +(string name, ParserProduction pp) + { + var p = pp.parent.TryCreateNewProduction(name); + p.contents.Add(pp); + return p; + } + + public static ParserProduction operator |(ParserProduction p1, ParserProduction p2) + { + p2.contents.Insert(0, p2); + p1.sub_productions.Add(new List(p2.contents)); + p1.actions.AddRange(p2.temp_actions); + p2.temp_actions.Clear(); + p2.contents.Clear(); + return p1; + } + + public static ParserProduction operator |(ParserProduction p1, string pt2) + { + var p2 = p1.parent.TryCreateNewProduction(pt2); + p2.contents.Insert(0, p2); + p1.sub_productions.Add(new List(p2.contents)); + p1.actions.AddRange(p2.temp_actions); + p2.temp_actions.Clear(); + p2.contents.Clear(); + return p1; + } + +#if false + public static ParserProduction operator +(ParserProduction p1, string p2) + { + p1.contents.Add(new ParserProduction { isterminal = true, token_specific = p2 }); + return p1; + } + + public static ParserProduction operator|(ParserProduction p1, string p2) + { + p1.sub_productions.Add(new List { p1, new ParserProduction { isterminal = true, token_specific = p2 } }); + return p1; + } +#endif + } + + #endregion + + /// + /// LR Parser Generator + /// + public class ParserGenerator + { + List production_rules; + Dictionary production_dict; + // (production_index, (priority, is_left_associativity?)) + Dictionary> shift_reduce_conflict_solve; + // (production_index, (sub_production_index, (priority, is_left_associativity?))) + Dictionary>> shift_reduce_conflict_solve_with_production_rule; + + public StringBuilder GlobalPrinter = new StringBuilder(); + + public readonly static ParserProduction EmptyString = new ParserProduction(null) { index = -2 }; + + public ParserGenerator() + { + production_rules = new List(); + production_rules.Add(new ParserProduction(this) { index = 0, production_name = "S'" }); + production_dict = new Dictionary(); + production_dict.Add("S'", production_rules[0]); + shift_reduce_conflict_solve = new Dictionary>(); + shift_reduce_conflict_solve_with_production_rule = new Dictionary>>(); + } + + #region Parser Generating Helper + + public ParserProduction CreateNewProduction(string name = "", bool is_terminal = true) + { + var pp = new ParserProduction(this) { index = production_rules.Count, production_name = name, isterminal = is_terminal }; + if (production_dict.ContainsKey(name)) + throw new Exception(name + " is already exsits production!"); + production_dict.Add(name, pp); + production_rules.Add(pp); + return pp; + } + + public ParserProduction TryCreateNewProduction(string name = "", bool is_terminal = true) + { + if (production_dict.ContainsKey(name)) + return production_dict[name]; + var pp = new ParserProduction(this) { index = production_rules.Count, production_name = name, isterminal = is_terminal }; + production_dict.Add(name, pp); + production_rules.Add(pp); + return pp; + } + + public void PushStarts(ParserProduction pp) + { + // Augment stats node + production_rules[0].sub_productions.Add(new List { pp }); + } + + /// + /// 터미널들의 Shift-Reduce Conflict solve 정보를 넣습니다. + /// + /// + /// + public void PushConflictSolver(bool left, params ParserProduction[] terminals) + { + var priority = shift_reduce_conflict_solve.Count + shift_reduce_conflict_solve_with_production_rule.Count; + foreach (var pp in terminals) + shift_reduce_conflict_solve.Add(pp.index, new Tuple(priority, left)); + } + + /// + /// 논터미널들의 Shift-Reduce Conflict solve 정보를 넣습니다. + /// + /// + /// + public void PushConflictSolver(bool left, params Tuple[] no) + { + var priority = shift_reduce_conflict_solve.Count + shift_reduce_conflict_solve_with_production_rule.Count; + foreach (var ppi in no) + { + if (!shift_reduce_conflict_solve_with_production_rule.ContainsKey(ppi.Item1.index)) + shift_reduce_conflict_solve_with_production_rule.Add(ppi.Item1.index, new Dictionary>()); + shift_reduce_conflict_solve_with_production_rule[ppi.Item1.index].Add(ppi.Item2, new Tuple(priority, left)); + } + } + + #endregion + + #region Simple ParserDescription Parser + + /// + /// 문자열로부터 ParserGenerator를 가져옵니다. + /// + /// 논터미널 심볼 + /// 터미널 심볼 + /// 프로덕션 룰 + /// Shift Reduce 규칙 + /// + public static ParserGenerator GetGenerator(string[] nt_syms, Tuple[] t_syms, string[] production_rules, string[] sr_rules) + { + var gen = new ParserGenerator(); + var non_terminals = new Dictionary(); + var terminals = new Dictionary(); + + terminals.Add("''", EmptyString); + + foreach (var nt in nt_syms) + non_terminals.Add(nt.Trim(), gen.CreateNewProduction(nt.Trim(), false)); + + foreach (var t in t_syms) + { + var name = t.Item1; + var pp = t.Item2; + + terminals.Add(pp, gen.CreateNewProduction(name.Trim())); + } + + var prec = new Dictionary>>(); + foreach (var pp in production_rules) + { + if (pp.Trim() == "") continue; + + var split = pp.Split(new[] { "->" }, StringSplitOptions.None); + var left = split[0].Trim(); + var right = split[1].Split(' '); + + var prlist = new List(); + bool stay_prec = false; + foreach (var ntt in right) + { + if (string.IsNullOrEmpty(ntt)) continue; + if (ntt == "%prec") { stay_prec = true; continue; } + if (stay_prec) + { + if (!prec.ContainsKey(ntt)) + prec.Add(ntt, new List>()); + prec[ntt].Add(new Tuple(non_terminals[left], non_terminals[left].sub_productions.Count)); + continue; + } + if (non_terminals.ContainsKey(ntt)) + prlist.Add(non_terminals[ntt]); + else if (terminals.ContainsKey(ntt)) + prlist.Add(terminals[ntt]); + else + throw new Exception($"Production rule build error!\r\n{ntt} is neither non-terminal nor terminal!\r\nDeclare the token-name!"); + } + non_terminals[left].sub_productions.Add(prlist); + } + + for (int i = sr_rules.Length - 1; i >= 0; i--) + { + var line = sr_rules[i].Trim(); + if (line == "") continue; + var tt = line.Split(' ')[0]; + var rr = line.Substring(tt.Length).Trim().Split(','); + + var left = true; + var items1 = new List>(); + var items2 = new List(); + + if (tt == "%right") left = false; + + foreach (var ii in rr.Select(x => x.Trim())) + { + if (string.IsNullOrEmpty(ii)) continue; + if (terminals.ContainsKey(ii)) + items2.Add(terminals[ii]); + else if (prec.ContainsKey(ii)) + items1.AddRange(prec[ii]); + else + throw new Exception($"Production rule build error!\r\n{ii} is neither non-terminal nor terminal!\r\nDeclare the token-name!"); + } + + if (items1.Count > 0) + gen.PushConflictSolver(left, items1.ToArray()); + else + gen.PushConflictSolver(left, items2.ToArray()); + } + + gen.PushStarts(non_terminals[nt_syms[0]]); + + return gen; + } + + #endregion + + #region String Hash Function + // 원래 해시가 아니라 set로 구현해야하는게 일반적임 + // 집합끼리의 비교연산, 일치여부 교집합을 구해 좀 더 최적화가능하지만 귀찮으니 string-hash를 쓰도록한다. + // + // # 2019-07-15 + // 확인결과 별도의 클래스를 만들어 set를 관리하는 것보다 string-hash가 더 빠르다 + // JSParserGenetor의 경우 set의 경우 13초, string-hash의 경우 11초로 string-hash가 더 빠른 속도를 내었다. + // set은 dictionary에서사용하는 GetHashCode 및 Equals 함수와, state의 kernel을 고려하여 만든 클래스였다. + + private string t2s(Tuple t) + { + return $"{t.Item1},{t.Item2},{t.Item3}"; + } + + private string t2s(Tuple> t) + { + var list = t.Item4.ToList(); + list.Sort(); + return $"{t.Item1},{t.Item2},{t.Item3},({string.Join(",", list)})"; + } + + private string l2s(List> h) + { + var list = h.ToList(); + list.Sort(); + return string.Join(",", list.Select(x => $"({x.Item1},{x.Item2},{x.Item3})")); + } + + private string l2s(List>> h) + { + var list = new List>>(); + foreach (var tt in h) + { + var ll = tt.Item4.ToList(); + ll.Sort(); + list.Add(new Tuple>(tt.Item1, tt.Item2, tt.Item3, ll)); + } + list.Sort(); + return string.Join(",", list.Select(x => $"({x.Item1},{x.Item2},{x.Item3},({(string.Join("/", x.Item4))}))")); + } + + private static string l2sl(List>> h, int kernel_cnt) + { + var list = new List>(); + var builder = new StringBuilder(); + for (int i = 0; i < kernel_cnt; i++) + list.Add(new Tuple(h[i].Item1, h[i].Item2, h[i].Item3)); + list.Sort(); + return string.Join(",", list.Select(x => $"({x.Item1},{x.Item2},{x.Item3})")); + } + + private string i2s(int a, int b, int c) + { + return $"{a},{b},{c}"; + } + #endregion + + #region Debug Printer + + private void print_hs(List> lhs, string prefix) + { + for (int i = 0; i < lhs.Count; i++) + if (lhs[i].Count > 0) + GlobalPrinter.Append( + $"{prefix}({production_rules[i].production_name})={{{string.Join(",", lhs[i].ToList().Select(x => x == -1 ? "$" : production_rules[x].production_name))}}}\r\n"); + } + + private void print_header(string head_text) + { + GlobalPrinter.Append("\r\n" + new string('=', 50) + "\r\n\r\n"); + int spaces = 50 - head_text.Length; + int padLeft = spaces / 2 + head_text.Length; + GlobalPrinter.Append(head_text.PadLeft(padLeft).PadRight(50)); + GlobalPrinter.Append("\r\n\r\n" + new string('=', 50) + "\r\n"); + } + + private void print_states(int state, List>> items) + { + var builder = new StringBuilder(); + builder.Append("-----" + "I" + state + "-----\r\n"); + + foreach (var item in items) + { + builder.Append($"{production_rules[item.Item1].production_name.ToString().PadLeft(10)} -> "); + + var builder2 = new StringBuilder(); + for (int i = 0; i < production_rules[item.Item1].sub_productions[item.Item2].Count; i++) + { + if (i == item.Item3) + builder2.Append("·"); + builder2.Append(production_rules[item.Item1].sub_productions[item.Item2][i].production_name + " "); + if (item.Item3 == production_rules[item.Item1].sub_productions[item.Item2].Count && i == item.Item3 - 1) + builder2.Append("·"); + } + builder.Append(builder2.ToString().PadRight(30)); + + builder.Append($"{string.Join("/", item.Item4.ToList().Select(x => x == -1 ? "$" : production_rules[x].production_name))}\r\n"); + } + + GlobalPrinter.Append(builder.ToString()); + } + + private void print_merged_states(int state, List>> items, List>> external_gotos) + { + var builder = new StringBuilder(); + builder.Append("-----" + "I" + state + "-----\r\n"); + + for (int j = 0; j < items.Count; j++) + { + var item = items[j]; + + builder.Append($"{production_rules[item.Item1].production_name.ToString().PadLeft(10)} -> "); + + var builder2 = new StringBuilder(); + for (int i = 0; i < production_rules[item.Item1].sub_productions[item.Item2].Count; i++) + { + if (i == item.Item3) + builder2.Append("·"); + builder2.Append(production_rules[item.Item1].sub_productions[item.Item2][i].production_name + " "); + if (item.Item3 == production_rules[item.Item1].sub_productions[item.Item2].Count && i == item.Item3 - 1) + builder2.Append("·"); + } + builder.Append(builder2.ToString().PadRight(30)); + + builder.Append($"[{string.Join("/", item.Item4.ToList().Select(x => x == -1 ? "$" : production_rules[x].production_name))}] "); + for (int i = 0; i < external_gotos.Count; i++) + builder.Append($"[{string.Join("/", external_gotos[i][j].ToList().Select(x => x == -1 ? "$" : production_rules[x].production_name))}] "); + builder.Append("\r\n"); + } + + GlobalPrinter.Append(builder.ToString()); + } + + #endregion + + int number_of_states = -1; + Dictionary>> shift_info; + Dictionary>> reduce_info; + + #region SLR Generator + /// + /// Generate SimpleLR Table + /// + public void Generate() + { + // --------------- Expand EmptyString --------------- + for (int i = 0; i < production_rules.Count; i++) + if (!production_rules[i].isterminal) + for (int j = 0; j < production_rules[i].sub_productions.Count; j++) + if (production_rules[i].sub_productions[j][0].index == EmptyString.index) + { + production_rules[i].sub_productions.RemoveAt(j--); + for (int ii = 0; ii < production_rules.Count; ii++) + if (!production_rules[ii].isterminal) + for (int jj = 0; jj < production_rules[ii].sub_productions.Count; jj++) + for (int kk = 0; kk < production_rules[ii].sub_productions[jj].Count; kk++) + if (production_rules[ii].sub_productions[jj][kk].index == production_rules[i].index) + { + var ll = new List(production_rules[ii].sub_productions[jj]); + ll.RemoveAt(kk); + production_rules[ii].sub_productions.Add(ll); + } + } + // -------------------------------------------------- + + // --------------- Collect FIRST,FOLLOW Set --------------- + var FIRST = new List>(); + foreach (var rule in production_rules) + FIRST.Add(first_terminals(rule.index)); + + var FOLLOW = follow_terminals(FIRST); + +#if true + print_header("FISRT, FOLLOW SETS"); + print_hs(FIRST, "FIRST"); + print_hs(FOLLOW, "FOLLOW"); +#endif + // -------------------------------------------------------- + + // (state_index, (production_rule_index, sub_productions_pos, dot_position) + var states = new Dictionary>>(); + // (state_specify, state_index) + var state_index = new Dictionary(); + // (state_index, (reduce_what, state_index)) + shift_info = new Dictionary>>(); + // (state_index, (shift_what, production_rule_index, sub_productions_pos)) + reduce_info = new Dictionary>>(); + var index_count = 0; + + // -------------------- Put first eater ------------------- + var first_l = first_nonterminals(0); + state_index.Add(l2s(first_l), 0); + states.Add(0, first_l); + // -------------------------------------------------------- + + // Create all LR states + // (states) + var q = new Queue(); + q.Enqueue(index_count++); + while (q.Count != 0) + { + var p = q.Dequeue(); + + // Collect goto + // (state_index, (production_rule_index, sub_productions_pos, dot_position)) + var gotos = new Dictionary>>(); + foreach (var transition in states[p]) + if (production_rules[transition.Item1].sub_productions[transition.Item2].Count > transition.Item3) + { + var pi = production_rules[transition.Item1].sub_productions[transition.Item2][transition.Item3].index; + if (!gotos.ContainsKey(pi)) + gotos.Add(pi, new List>()); + gotos[pi].Add(new Tuple(transition.Item1, transition.Item2, transition.Item3 + 1)); + } + + // Populate empty-string closure + foreach (var goto_unit in gotos) + { + var set = new HashSet(); + // Push exists transitions + foreach (var psd in goto_unit.Value) + set.Add(t2s(psd)); + // Find all transitions + var new_trans = new List>(); + foreach (var psd in goto_unit.Value) + { + if (production_rules[psd.Item1].sub_productions[psd.Item2].Count == psd.Item3) continue; + if (production_rules[psd.Item1].sub_productions[psd.Item2][psd.Item3].isterminal) continue; + var first_nt = first_nonterminals(production_rules[psd.Item1].sub_productions[psd.Item2][psd.Item3].index); + foreach (var nts in first_nt) + if (!set.Contains(t2s(nts))) + { + new_trans.Add(nts); + set.Add(t2s(nts)); + } + } + goto_unit.Value.AddRange(new_trans); + } + + // Build shift transitions ignore terminal, non-terminal + foreach (var pp in gotos) + { + var hash = l2s(pp.Value); + if (!state_index.ContainsKey(hash)) + { + states.Add(index_count, pp.Value); + state_index.Add(hash, index_count); + q.Enqueue(index_count++); + } + var index = state_index[hash]; + + if (!shift_info.ContainsKey(p)) + shift_info.Add(p, new List>()); + shift_info[p].Add(new Tuple(pp.Key, index)); + } + + // Check require reduce and build reduce transitions + foreach (var transition in states[p]) + if (production_rules[transition.Item1].sub_productions[transition.Item2].Count == transition.Item3) + { + if (!reduce_info.ContainsKey(p)) + reduce_info.Add(p, new List>()); + foreach (var term in FOLLOW[transition.Item1]) + reduce_info[p].Add(new Tuple(term, transition.Item1, transition.Item2)); + } + } + + number_of_states = states.Count; + } + #endregion + + #region LR(1) Generator + /// + /// Generate LR(1) Table + /// + /// There is a lookahead propagation error while trying to get the LR(1) item, and it is under review. + /// Don't use this function + /// + public void GenerateLR1() + { + // --------------- Delete EmptyString --------------- + for (int i = 0; i < production_rules.Count; i++) + if (!production_rules[i].isterminal) + for (int j = 0; j < production_rules[i].sub_productions.Count; j++) + if (production_rules[i].sub_productions[j][0].index == EmptyString.index) + production_rules[i].sub_productions[j].Clear(); + // -------------------------------------------------- + + // --------------- Collect FIRST,FOLLOW Set --------------- + var FIRST = new List>(); + foreach (var rule in production_rules) + FIRST.Add(first_terminals(rule.index)); + + var FOLLOW = follow_terminals(FIRST); + +#if true + print_header("FISRT, FOLLOW SETS"); + print_hs(FIRST, "FIRST"); + print_hs(FOLLOW, "FOLLOW"); +#endif + // -------------------------------------------------------- + + // (state_index, (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + var states = new Dictionary>>>(); + // (state_specify, state_index) + var state_index = new Dictionary(); + var goto_table = new List>>>(); + // (state_index, (reduce_what, state_index)) + shift_info = new Dictionary>>(); + // (state_index, (shift_what, production_rule_index, sub_productions_pos)) + reduce_info = new Dictionary>>(); + var index_count = 0; + + // -------------------- Put first eater ------------------- + var first_l = first_with_lookahead(0, 0, 0, new HashSet()); + state_index.Add(l2s(first_l), 0); + states.Add(0, first_l); + // -------------------------------------------------------- + + // Create all LR states + // (states) + var q = new Queue(); + q.Enqueue(index_count++); + while (q.Count != 0) + { + var p = q.Dequeue(); + + // Collect goto + // (state_index, (production_rule_index, sub_productions_pos, dot_position, lookahead)) + var gotos = new Dictionary>>>(); + foreach (var transition in states[p]) + if (production_rules[transition.Item1].sub_productions[transition.Item2].Count > transition.Item3) + { + var pi = production_rules[transition.Item1].sub_productions[transition.Item2][transition.Item3].index; + if (!gotos.ContainsKey(pi)) + gotos.Add(pi, new List>>()); + gotos[pi].Add(new Tuple>(transition.Item1, transition.Item2, transition.Item3 + 1, transition.Item4)); + } + + // Populate empty-string closure + foreach (var goto_unit in gotos) + { + var set = new HashSet(); + // Push exists transitions + foreach (var psd in goto_unit.Value) + set.Add(t2s(psd)); + // Find all transitions + var new_trans = new List>>(); + var trans_dic = new Dictionary(); + foreach (var psd in goto_unit.Value) + { + if (production_rules[psd.Item1].sub_productions[psd.Item2].Count == psd.Item3) continue; + if (production_rules[psd.Item1].sub_productions[psd.Item2][psd.Item3].isterminal) continue; + var first_nt = first_with_lookahead(psd.Item1, psd.Item2, psd.Item3, psd.Item4); + foreach (var nts in first_nt) + if (!set.Contains(t2s(nts))) + { + var ts = t2s(new Tuple(nts.Item1, nts.Item2, nts.Item3)); + if (trans_dic.ContainsKey(ts)) + { + nts.Item4.ToList().ForEach(x => new_trans[trans_dic[ts]].Item4.Add(x)); + } + else + { + trans_dic.Add(ts, new_trans.Count); + new_trans.Add(nts); + set.Add(t2s(nts)); + } + } + } + goto_unit.Value.AddRange(new_trans); + } + + //// Build shift transitions ignore terminal, non-terminal + //foreach (var pp in gotos) + //{ + // var hash = l2s(pp.Value); + // if (!state_index.ContainsKey(hash)) + // { + // states.Add(index_count, pp.Value); + // state_index.Add(hash, index_count); + // q.Enqueue(index_count++); + // } + // var index = state_index[hash]; + // + // if (!shift_info.ContainsKey(p)) + // shift_info.Add(p, new List>()); + // shift_info[p].Add(new Tuple(pp.Key, index)); + //} + // + //// Check require reduce and build reduce transitions + //foreach (var transition in states[p]) + // if (production_rules[transition.Item1].sub_productions[transition.Item2].Count == transition.Item3) + // { + // if (!reduce_info.ContainsKey(p)) + // reduce_info.Add(p, new List>()); + // foreach (var term in transition.Item4) + // reduce_info[p].Add(new Tuple(term, transition.Item1, transition.Item2)); + // } + + // Build goto transitions ignore terminal, non-terminal anywhere + var index_list = new List>(); + foreach (var pp in gotos) + { + var hash = l2s(pp.Value); + if (!state_index.ContainsKey(hash)) + { + states.Add(index_count, pp.Value); + state_index.Add(hash, index_count); + q.Enqueue(index_count++); + } + index_list.Add(new Tuple(pp.Key, state_index[hash])); + } + + goto_table.Add(new Tuple>>(p, index_list)); + } + + var occurred_conflict = false; + + // ------------- Find Shift-Reduce Conflict ------------ + foreach (var ms in states) + { + // (shift_what, state_index) + var small_shift_info = new List>(); + // (reduce_what, production_rule_index, sub_productions_pos) + var small_reduce_info = new List>(); + + // Fill Shift Info + foreach (var pp in goto_table[ms.Key].Item2) + small_shift_info.Add(new Tuple(pp.Item1, pp.Item2)); + + // Fill Reduce Info + foreach (var transition in ms.Value) + if (production_rules[transition.Item1].sub_productions[transition.Item2].Count == transition.Item3) + { + foreach (var term in transition.Item4) + small_reduce_info.Add(new Tuple(term, transition.Item1, transition.Item2)); + } + + // Conflict Check + // (shift_what, small_shift_info_index) + var shift_tokens = new Dictionary(); + for (int i = 0; i < small_shift_info.Count; i++) + shift_tokens.Add(small_shift_info[i].Item1, i); + var completes = new HashSet(); + + foreach (var tuple in small_reduce_info) + { + if (completes.Contains(tuple.Item1)) + { + // It's already added so do not have to work anymore. + continue; + } + + if (shift_tokens.ContainsKey(tuple.Item1)) + { +#if true + print_header("SHIFT-REDUCE CONFLICTS"); + GlobalPrinter.Append($"Shift-Reduce Conflict! {(tuple.Item1 == -1 ? "$" : production_rules[tuple.Item1].production_name)}\r\n"); + GlobalPrinter.Append($"States: {ms.Key} {small_shift_info[shift_tokens[tuple.Item1]].Item2}\r\n"); + print_states(ms.Key, states[ms.Key]); + print_states(small_shift_info[shift_tokens[tuple.Item1]].Item2, states[small_shift_info[shift_tokens[tuple.Item1]].Item2]); +#endif + Tuple p1 = null, p2 = null; + + try + { + var pp = get_first_on_right_terminal(production_rules[tuple.Item2], tuple.Item3); + if (shift_reduce_conflict_solve.ContainsKey(pp.index)) + p1 = shift_reduce_conflict_solve[pp.index]; + } + catch (Exception e) + { + GlobalPrinter.Append(e.Message + "\r\n"); + } + + if (shift_reduce_conflict_solve.ContainsKey(tuple.Item1)) + p2 = shift_reduce_conflict_solve[tuple.Item1]; + + if (shift_reduce_conflict_solve_with_production_rule.ContainsKey(tuple.Item2)) + if (shift_reduce_conflict_solve_with_production_rule[tuple.Item2].ContainsKey(tuple.Item3)) + p1 = shift_reduce_conflict_solve_with_production_rule[tuple.Item2][tuple.Item3]; + + //if (shift_reduce_conflict_solve_with_production_rule.ContainsKey(states[tuple.Item1][0].Item1)) + // if (shift_reduce_conflict_solve_with_production_rule[states[tuple.Item1][0].Item1].ContainsKey(states[tuple.Item1][0].Item2)) + // p2 = shift_reduce_conflict_solve_with_production_rule[states[tuple.Item1][0].Item1][states[tuple.Item1][0].Item2]; + + if (p1 == null || p2 == null) + { + occurred_conflict = true; + continue; + } + + if (p1.Item1 < p2.Item1 || (p1.Item1 == p2.Item1 && p1.Item2)) + { + // Reduce + if (!reduce_info.ContainsKey(ms.Key)) + reduce_info.Add(ms.Key, new List>()); + reduce_info[ms.Key].Add(new Tuple(tuple.Item1, tuple.Item2, tuple.Item3)); + } + else + { + // Shift + if (!shift_info.ContainsKey(ms.Key)) + shift_info.Add(ms.Key, new List>()); + shift_info[ms.Key].Add(new Tuple(tuple.Item1, small_shift_info[shift_tokens[tuple.Item1]].Item2)); + } + + completes.Add(tuple.Item1); + } + else + { + // Just add reduce item + if (!reduce_info.ContainsKey(ms.Key)) + reduce_info.Add(ms.Key, new List>()); + reduce_info[ms.Key].Add(new Tuple(tuple.Item1, tuple.Item2, tuple.Item3)); + + completes.Add(tuple.Item1); + } + } + + foreach (var pair in shift_tokens) + { + if (completes.Contains(pair.Key)) continue; + var shift = small_shift_info[pair.Value]; + if (!shift_info.ContainsKey(ms.Key)) + shift_info.Add(ms.Key, new List>()); + shift_info[ms.Key].Add(new Tuple(shift.Item1, shift.Item2)); + } + } + // ----------------------------------------------------- + + if (occurred_conflict) + throw new Exception("Specify the rules to resolve Shift-Reduce Conflict!"); + + number_of_states = states.Count; +#if true + print_header("STATES INFO"); + foreach (var s in states) + print_states(s.Key, s.Value); +#endif + } + #endregion + + #region LALR Generator + /// + /// Generate LALR Table + /// + public void GenerateLALR() + { + // --------------- Delete EmptyString --------------- + for (int i = 0; i < production_rules.Count; i++) + if (!production_rules[i].isterminal) + for (int j = 0; j < production_rules[i].sub_productions.Count; j++) + if (production_rules[i].sub_productions[j][0].index == EmptyString.index) + production_rules[i].sub_productions[j].Clear(); + // -------------------------------------------------- + + // --------------- Collect FIRST,FOLLOW Set --------------- + var FIRST = new List>(); + foreach (var rule in production_rules) + FIRST.Add(first_terminals(rule.index)); + + var FOLLOW = follow_terminals(FIRST); + +#if true + print_header("FISRT, FOLLOW SETS"); + print_hs(FIRST, "FIRST"); + print_hs(FOLLOW, "FOLLOW"); +#endif + // -------------------------------------------------------- + + // (state_index, (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + var states = new Dictionary>>>(); + // (state_specify, state_index) + var state_index = new Dictionary(); + var goto_table = new List>>>(); + // (state_index, (shift_what, state_index)) + shift_info = new Dictionary>>(); + // (state_index, (reduce_what, production_rule_index, sub_productions_pos)) + reduce_info = new Dictionary>>(); + var index_count = 0; + + // -------------------- Put first eater ------------------- + var first_l = first_with_lookahead(0, 0, 0, new HashSet()); + state_index.Add(l2s(first_l), 0); + states.Add(0, first_l); + // -------------------------------------------------------- + + // Create all LR states + // (states) + var q = new Queue(); + q.Enqueue(index_count++); + while (q.Count != 0) + { + var p = q.Dequeue(); + + // Collect goto + // (state_index, (production_rule_index, sub_productions_pos, dot_position, lookahead)) + var gotos = new Dictionary>>>(); + foreach (var transition in states[p]) + if (production_rules[transition.Item1].sub_productions[transition.Item2].Count > transition.Item3) + { + var pi = production_rules[transition.Item1].sub_productions[transition.Item2][transition.Item3].index; + if (!gotos.ContainsKey(pi)) + gotos.Add(pi, new List>>()); + gotos[pi].Add(new Tuple>(transition.Item1, transition.Item2, transition.Item3 + 1, transition.Item4)); + } + + // Populate empty-string closure + foreach (var goto_unit in gotos) + { + var set = new HashSet(); + // Push exists transitions + foreach (var psd in goto_unit.Value) + set.Add(t2s(psd)); + // Find all transitions + var new_trans = new List>>(); + var trans_dic = new Dictionary(); + foreach (var psd in goto_unit.Value) + { + if (production_rules[psd.Item1].sub_productions[psd.Item2].Count == psd.Item3) continue; + if (production_rules[psd.Item1].sub_productions[psd.Item2][psd.Item3].isterminal) continue; + var first_nt = first_with_lookahead(psd.Item1, psd.Item2, psd.Item3, psd.Item4); + foreach (var nts in first_nt) + if (!set.Contains(t2s(nts))) + { + var ts = t2s(new Tuple(nts.Item1, nts.Item2, nts.Item3)); + if (trans_dic.ContainsKey(ts)) + { + nts.Item4.ToList().ForEach(x => new_trans[trans_dic[ts]].Item4.Add(x)); + } + else + { + trans_dic.Add(ts, new_trans.Count); + new_trans.Add(nts); + set.Add(t2s(nts)); + } + } + } + goto_unit.Value.AddRange(new_trans); + } + + // Build goto transitions ignore terminal, non-terminal anywhere + var index_list = new List>(); + foreach (var pp in gotos) + { + try + { + var hash = l2s(pp.Value); + if (!state_index.ContainsKey(hash)) + { + states.Add(index_count, pp.Value); + state_index.Add(hash, index_count); + q.Enqueue(index_count++); + } + index_list.Add(new Tuple(pp.Key, state_index[hash])); + } + catch + { + // Now this error is not hit + // For debugging + print_header("GOTO CONFLICT!!"); + GlobalPrinter.Append($"Cannot solve lookahead overlapping!\r\n"); + GlobalPrinter.Append($"Please uses non-associative option or adds extra token to handle with shift-reduce conflict!\r\n"); + print_states(p, states[p]); + print_header("INCOMPLETE STATES"); + foreach (var s in states) + print_states(s.Key, s.Value); + return; + } + } + + goto_table.Add(new Tuple>>(p, index_list)); + } + +#if true + print_header("UNMERGED STATES"); + foreach (var s in states) + print_states(s.Key, s.Value); +#endif + + // -------------------- Merge States ------------------- + var merged_states = new Dictionary>(); + var merged_states_index = new Dictionary(); + var merged_index = new Dictionary(); + var merged_merged_index = new Dictionary(); + var merged_merged_inverse_index = new Dictionary(); + var count_of_completes_states = 0; + + for (int i = 0; i < states.Count; i++) + { + var str = l2s(states[i].Select(x => new Tuple(x.Item1, x.Item2, x.Item3)).ToList()); + + if (!merged_states_index.ContainsKey(str)) + { + merged_states_index.Add(str, i); + merged_states.Add(i, new List()); + merged_index.Add(i, i); + merged_merged_inverse_index.Add(str, count_of_completes_states); + merged_merged_index.Add(i, count_of_completes_states++); + } + else + { + merged_states[merged_states_index[str]].Add(i); + merged_index.Add(i, merged_states_index[str]); + merged_merged_index.Add(i, merged_merged_inverse_index[str]); + } + } + +#if true + print_header("MERGED STATES WITH SOME SETS"); + foreach (var s in merged_states) + print_merged_states(s.Key, states[s.Key], s.Value.Select(x => states[x].Select(y => y.Item4.ToList()).ToList()).ToList()); +#endif + + foreach (var pair in merged_states) + { + for (int i = 0; i < states[pair.Key].Count; i++) + { + foreach (var ii in pair.Value) + foreach (var lookahead in states[ii][i].Item4) + states[pair.Key][i].Item4.Add(lookahead); + } + } + +#if true + print_header("MERGED STATES"); + foreach (var s in merged_states) + print_states(s.Key, states[s.Key]); +#endif + // ----------------------------------------------------- + + var occurred_conflict = false; + + // ------------- Find Shift-Reduce Conflict ------------ + foreach (var ms in merged_states) + { + // (shift_what, state_index) + var small_shift_info = new List>(); + // (reduce_what, production_rule_index, sub_productions_pos) + var small_reduce_info = new List>(); + + // Fill Shift Info + foreach (var pp in goto_table[ms.Key].Item2) + small_shift_info.Add(new Tuple(pp.Item1, merged_index[pp.Item2])); + + // Fill Reduce Info + ms.Value.Add(ms.Key); + foreach (var index in ms.Value) + foreach (var transition in states[index]) + if (production_rules[transition.Item1].sub_productions[transition.Item2].Count == transition.Item3) + { + foreach (var term in transition.Item4) + small_reduce_info.Add(new Tuple(term, transition.Item1, transition.Item2)); + } + + // Conflict Check + // (shift_what, small_shift_info_index) + var shift_tokens = new Dictionary(); + for (int i = 0; i < small_shift_info.Count; i++) + shift_tokens.Add(small_shift_info[i].Item1, i); + var completes = new HashSet(); + + foreach (var tuple in small_reduce_info) + { + if (completes.Contains(tuple.Item1)) + { + // It's already added so do not have to work anymore. + continue; + } + + if (shift_tokens.ContainsKey(tuple.Item1)) + { +#if !DEBUG + print_header("SHIFT-REDUCE CONFLICTS"); + GlobalPrinter.Append($"Shift-Reduce Conflict! {(tuple.Item1 == -1 ? "$" : production_rules[tuple.Item1].production_name)}\r\n"); + GlobalPrinter.Append($"States: {ms.Key} {small_shift_info[shift_tokens[tuple.Item1]].Item2}\r\n"); + print_states(ms.Key, states[ms.Key]); + print_states(small_shift_info[shift_tokens[tuple.Item1]].Item2, states[small_shift_info[shift_tokens[tuple.Item1]].Item2]); +#endif + Tuple p1 = null, p2 = null; + +#if DEBUG + string mm = ""; +#endif + try + { + var pp = get_first_on_right_terminal(production_rules[tuple.Item2], tuple.Item3); + if (shift_reduce_conflict_solve.ContainsKey(pp.index)) + p1 = shift_reduce_conflict_solve[pp.index]; + } + catch (Exception e) + { +#if !DEBUG + GlobalPrinter.Append(e.Message + "\r\n"); +#else + mm = e.Message + "\r\n"; +#endif + } + + if (shift_reduce_conflict_solve.ContainsKey(tuple.Item1)) + p2 = shift_reduce_conflict_solve[tuple.Item1]; + + if (shift_reduce_conflict_solve_with_production_rule.ContainsKey(tuple.Item2)) + if (shift_reduce_conflict_solve_with_production_rule[tuple.Item2].ContainsKey(tuple.Item3)) + p1 = shift_reduce_conflict_solve_with_production_rule[tuple.Item2][tuple.Item3]; + + //if (shift_reduce_conflict_solve_with_production_rule.ContainsKey(states[tuple.Item1][0].Item1)) + // if (shift_reduce_conflict_solve_with_production_rule[states[tuple.Item1][0].Item1].ContainsKey(states[tuple.Item1][0].Item2)) + // p2 = shift_reduce_conflict_solve_with_production_rule[states[tuple.Item1][0].Item1][states[tuple.Item1][0].Item2]; + + if (p1 == null || p2 == null) + { +#if DEBUG + print_header("SHIFT-REDUCE CONFLICTS"); + GlobalPrinter.Append($"Shift-Reduce Conflict! {(tuple.Item1 == -1 ? "$" : production_rules[tuple.Item1].production_name)}\r\n"); + GlobalPrinter.Append($"States: {ms.Key} {small_shift_info[shift_tokens[tuple.Item1]].Item2}\r\n"); + print_states(ms.Key, states[ms.Key]); + print_states(small_shift_info[shift_tokens[tuple.Item1]].Item2, states[small_shift_info[shift_tokens[tuple.Item1]].Item2]); + if (mm != "") + GlobalPrinter.Append(mm); +#endif + occurred_conflict = true; + continue; + } + + if (p1.Item1 < p2.Item1 || (p1.Item1 == p2.Item1 && p1.Item2)) + { + // Reduce + if (!reduce_info.ContainsKey(merged_merged_index[ms.Key])) + reduce_info.Add(merged_merged_index[ms.Key], new List>()); + reduce_info[merged_merged_index[ms.Key]].Add(new Tuple(tuple.Item1, tuple.Item2, tuple.Item3)); + } + else + { + // Shift + if (!shift_info.ContainsKey(merged_merged_index[ms.Key])) + shift_info.Add(merged_merged_index[ms.Key], new List>()); + shift_info[merged_merged_index[ms.Key]].Add(new Tuple(tuple.Item1, merged_merged_index[small_shift_info[shift_tokens[tuple.Item1]].Item2])); + } + + completes.Add(tuple.Item1); + } + else + { + // Just add reduce item + if (!reduce_info.ContainsKey(merged_merged_index[ms.Key])) + reduce_info.Add(merged_merged_index[ms.Key], new List>()); + reduce_info[merged_merged_index[ms.Key]].Add(new Tuple(tuple.Item1, tuple.Item2, tuple.Item3)); + + completes.Add(tuple.Item1); + } + } + + foreach (var pair in shift_tokens) + { + if (completes.Contains(pair.Key)) continue; + var shift = small_shift_info[pair.Value]; + if (!shift_info.ContainsKey(merged_merged_index[ms.Key])) + shift_info.Add(merged_merged_index[ms.Key], new List>()); + shift_info[merged_merged_index[ms.Key]].Add(new Tuple(shift.Item1, merged_merged_index[shift.Item2])); + } + } + // ----------------------------------------------------- + + if (occurred_conflict) + throw new Exception("Specify the rules to resolve Shift-Reduce Conflict!"); + + number_of_states = merged_states.Count; + } + #endregion + + #region LALR Generator From LR(0) Items + + /// + /// http://3e8.org/pub/scheme/doc/parsing/Efficient%20Computation%20of%20LALR(1)%20Look-Ahead%20Sets.pdf + /// + /// DEREMER, Frank; PENNELLO, Thomas. Efficient computation of LALR (1) look-ahead sets. + /// ACM Transactions on Programming Languages and Systems (TOPLAS), 1982, 4.4: 615-649. + /// + /// Dragon Book 4.7.5 - Efficient Construction of LALR Parsing Table + /// + public void GenerateLALR2() + { + // --------------- Delete EmptyString --------------- + for (int i = 0; i < production_rules.Count; i++) + if (!production_rules[i].isterminal) + for (int j = 0; j < production_rules[i].sub_productions.Count; j++) + if (production_rules[i].sub_productions[j][0].index == EmptyString.index) + production_rules[i].sub_productions[j].Clear(); + // -------------------------------------------------- + + // --------------- Collect FIRST,FOLLOW Set --------------- + var FIRST = new List>(); + foreach (var rule in production_rules) + FIRST.Add(first_terminals(rule.index)); + + var FOLLOW = follow_terminals(FIRST); + +#if true + print_header("FISRT, FOLLOW SETS"); + print_hs(FIRST, "FIRST"); + print_hs(FOLLOW, "FOLLOW"); +#endif + // -------------------------------------------------------- + + // --------------- Determine exists epsillon --------------- + var include_epsillon = Enumerable.Repeat(false, production_rules.Count).ToList(); + + for (int i = 0; i < production_rules.Count; i++) + if (!production_rules[i].isterminal) + if (production_rules[i].sub_productions.Any(x => x.Count == 0)) + include_epsillon[i] = true; + + // Find productions contained epsillon. + while (true) + { + var change = false; + + for (int i = 0; i < production_rules.Count; i++) + for (int j = 0; j < production_rules[i].sub_productions.Count; j++) + if (production_rules[i].sub_productions[j].All(x => include_epsillon[x.index])) + { + if (include_epsillon[i] == false) + { + include_epsillon[i] = true; + change = true; + } + break; + } + + if (!change) break; + } + // --------------------------------------------------------- + + // (state_index, (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + var states = new Dictionary>>>(); + // (state_specify, state_index) + var state_index = new Dictionary(); + // (state_index, (state_item_index, (handle_position, parent_state_index, parent_state_item_index))) + var pred = new Dictionary>>>(); + // (from_state_index, (when_state_index, to_state_index)) + var goto_table = new List>>>(); + // (state_index, (shift_what, state_index)) + shift_info = new Dictionary>>(); + // (state_index, (reduce_what, production_rule_index, sub_productions_pos)) + reduce_info = new Dictionary>>(); + var index_count = 0; + + // -------------------- Put first eater ------------------- + var first_l = closure(0, 0, 0).Select(x => new Tuple>(x.Item1, x.Item2, x.Item3, new HashSet())).ToList(); + state_index.Add(l2sl(first_l, 1), 0); + states.Add(0, first_l); + // -------------------------------------------------------- + + // Create all LR states + // (states) + var q = new Queue(); + q.Enqueue(index_count++); + while (q.Count != 0) + { + var p = q.Dequeue(); + + // Collect goto + // (state_index, (production_rule_index, sub_productions_pos, dot_position, lookahead)) + var gotos = new Dictionary>>>(); + // (state_index, kernel_count) + var kernel_cnt = new Dictionary(); + // (state_index, (handle_position, parent_state_item_index)) + var ppred = new Dictionary>>(); + for (int i = 0; i < states[p].Count; i++) + { + var transition = states[p][i]; + if (production_rules[transition.Item1].sub_productions[transition.Item2].Count > transition.Item3) + { + var pi = production_rules[transition.Item1].sub_productions[transition.Item2][transition.Item3].index; + if (!gotos.ContainsKey(pi)) + { + gotos.Add(pi, new List>>()); + kernel_cnt.Add(pi, 0); + ppred.Add(pi, new List>()); + } + gotos[pi].Add(new Tuple>(transition.Item1, transition.Item2, transition.Item3 + 1, new HashSet())); + kernel_cnt[pi] = kernel_cnt[pi] + 1; + ppred[pi].Add(new Tuple(transition.Item3, i)); + } + } + + // Populate closures + foreach (var goto_unit in gotos) + { + var set = new HashSet(); + // Push exists transitions + foreach (var psd in goto_unit.Value) + set.Add(i2s(psd.Item1, psd.Item2, psd.Item3)); + // Find all transitions + var new_trans = new List>>(); + foreach (var psd in goto_unit.Value) + { + if (production_rules[psd.Item1].sub_productions[psd.Item2].Count == psd.Item3) continue; + if (production_rules[psd.Item1].sub_productions[psd.Item2][psd.Item3].isterminal) continue; + var first_nt = closure(psd.Item1, psd.Item2, psd.Item3); + foreach (var nts in first_nt) + if (!set.Contains(t2s(nts))) + { + new_trans.Add(new Tuple>(nts.Item1, nts.Item2, nts.Item3, new HashSet())); + set.Add(t2s(nts)); + } + } + goto_unit.Value.AddRange(new_trans); + } + + // Build goto transitions ignore terminal, non-terminal anywhere + var index_list = new List>(); + foreach (var pp in gotos) + { + var kernels = kernel_cnt[pp.Key]; + var hash = l2sl(pp.Value, kernels); + if (!state_index.ContainsKey(hash)) + { + states.Add(index_count, pp.Value); + state_index.Add(hash, index_count); + + if (!pred.ContainsKey(index_count)) + pred.Add(index_count, new Dictionary>>()); + for (int i = 0; i < kernels; i++) + { + if (!pred[index_count].ContainsKey(i)) + pred[index_count].Add(i, new List>()); + pred[index_count][i].Add(new Tuple(ppred[pp.Key][i].Item1, p, ppred[pp.Key][i].Item2)); + } + + q.Enqueue(index_count++); + } + else + { + var index = state_index[hash]; + if (!pred.ContainsKey(index)) + pred.Add(index, new Dictionary>>()); + for (int i = 0; i < kernels; i++) + { + if (!pred[index].ContainsKey(i)) + pred[index].Add(i, new List>()); + pred[index][i].Add(new Tuple(ppred[pp.Key][i].Item1, p, ppred[pp.Key][i].Item2)); + } + } + index_list.Add(new Tuple(pp.Key, state_index[hash])); + } + + goto_table.Add(new Tuple>>(p, index_list)); + } + +#if false + print_header("LR0 Items"); + foreach (var s in states) + print_states(s.Key, s.Value); +#endif + + // -------------------- Fill Lookahead ------------------- + + // Insert EOP (End of Parsing marker) + states[0][0].Item4.Add(-1); + + // Find all reduceable LR(0) states item and fill lookahead + while (true) + { + lookahead_change = false; + foreach (var state in states) + { + for (int i = 0; i < state.Value.Count; i++) + { + // Find the state that the handle is declared. + // If blew is declared, + // A -> abc. + // then trace location of handle definition recursive. + // A -> ab.c + // A -> a.bc + // A -> .abc + var lrs = state.Value[i]; + if (production_rules[lrs.Item1].sub_productions[lrs.Item2].Count == lrs.Item3) + { + fill_lookahead(FIRST, include_epsillon, states, pred, state.Key, i); + } + } + } + + if (!lookahead_change) + break; + } + + //var visit = Enumerable.Repeat(false, goto_table.Count); + //var indegree_count = new Dictionary(); + //for (int i = 0; i < goto_table.Count; i++) + // for (int j = 0; j < goto_table[i].Item2.Count; j++) + // { + // var from = goto_table[i].Item2[j].Item2; + // if (!indegree_count.ContainsKey(from)) + // indegree_count.Add(from, 0); + // indegree_count[from] = from + 1; + // } + + // ------------------------------------------------------- + +#if true + print_header("LALR STATES"); + foreach (var s in states) + print_states(s.Key, s.Value); +#endif + + var occurred_conflict = false; + + // ------------- Find Shift-Reduce Conflict ------------ + foreach (var state in states) + { + // (shift_what, state_index) + var small_shift_info = new List>(); + // (reduce_what, production_rule_index, sub_productions_pos) + var small_reduce_info = new List>(); + + // Fill Shift Info + foreach (var pp in goto_table[state.Key].Item2) + small_shift_info.Add(new Tuple(pp.Item1, pp.Item2)); + + // Fill Reduce Info + foreach (var transition in state.Value) + if (production_rules[transition.Item1].sub_productions[transition.Item2].Count == transition.Item3) + { + foreach (var term in transition.Item4) + small_reduce_info.Add(new Tuple(term, transition.Item1, transition.Item2)); + } + + // Conflict Check + // (shift_what, small_shift_info_index) + var shift_tokens = new Dictionary(); + for (int i = 0; i < small_shift_info.Count; i++) + shift_tokens.Add(small_shift_info[i].Item1, i); + var completes = new HashSet(); + + foreach (var tuple in small_reduce_info) + { + if (completes.Contains(tuple.Item1)) + { + // It's already added so do not have to work anymore. + continue; + } + + if (shift_tokens.ContainsKey(tuple.Item1)) + { +#if false + print_header("SHIFT-REDUCE CONFLICTS"); + GlobalPrinter.Append($"Shift-Reduce Conflict! {(tuple.Item1 == -1 ? "$" : production_rules[tuple.Item1].production_name)}\r\n"); + GlobalPrinter.Append($"States: {ms.Key} {small_shift_info[shift_tokens[tuple.Item1]].Item2}\r\n"); + print_states(ms.Key, states[ms.Key]); + print_states(small_shift_info[shift_tokens[tuple.Item1]].Item2, states[small_shift_info[shift_tokens[tuple.Item1]].Item2]); +#endif + Tuple p1 = null, p2 = null; + +#if DEBUG + string mm = ""; +#endif + try + { + var pp = get_first_on_right_terminal(production_rules[tuple.Item2], tuple.Item3); + if (shift_reduce_conflict_solve.ContainsKey(pp.index)) + p1 = shift_reduce_conflict_solve[pp.index]; + } + catch (Exception e) + { +#if !DEBUG + GlobalPrinter.Append(e.Message + "\r\n"); +#else + mm = e.Message + "\r\n"; +#endif + } + + if (shift_reduce_conflict_solve.ContainsKey(tuple.Item1)) + p2 = shift_reduce_conflict_solve[tuple.Item1]; + + if (shift_reduce_conflict_solve_with_production_rule.ContainsKey(tuple.Item2)) + if (shift_reduce_conflict_solve_with_production_rule[tuple.Item2].ContainsKey(tuple.Item3)) + p1 = shift_reduce_conflict_solve_with_production_rule[tuple.Item2][tuple.Item3]; + + //if (shift_reduce_conflict_solve_with_production_rule.ContainsKey(states[tuple.Item1][0].Item1)) + // if (shift_reduce_conflict_solve_with_production_rule[states[tuple.Item1][0].Item1].ContainsKey(states[tuple.Item1][0].Item2)) + // p2 = shift_reduce_conflict_solve_with_production_rule[states[tuple.Item1][0].Item1][states[tuple.Item1][0].Item2]; + + if (p1 == null || p2 == null) + { +#if DEBUG + print_header("SHIFT-REDUCE CONFLICTS"); + GlobalPrinter.Append($"Shift-Reduce Conflict! {(tuple.Item1 == -1 ? "$" : production_rules[tuple.Item1].production_name)}\r\n"); + GlobalPrinter.Append($"States: {state.Key} {small_shift_info[shift_tokens[tuple.Item1]].Item2}\r\n"); + print_states(state.Key, state.Value); + print_states(small_shift_info[shift_tokens[tuple.Item1]].Item2, states[small_shift_info[shift_tokens[tuple.Item1]].Item2]); + if (mm != "") + GlobalPrinter.Append(mm); +#endif + occurred_conflict = true; + continue; + } + + if (p1.Item1 < p2.Item1 || (p1.Item1 == p2.Item1 && p1.Item2)) + { + // Reduce + if (!reduce_info.ContainsKey(state.Key)) + reduce_info.Add(state.Key, new List>()); + reduce_info[state.Key].Add(new Tuple(tuple.Item1, tuple.Item2, tuple.Item3)); + } + else + { + // Shift + if (!shift_info.ContainsKey(state.Key)) + shift_info.Add(state.Key, new List>()); + shift_info[state.Key].Add(new Tuple(tuple.Item1, small_shift_info[shift_tokens[tuple.Item1]].Item2)); + } + + completes.Add(tuple.Item1); + } + else + { + // Just add reduce item + if (!reduce_info.ContainsKey(state.Key)) + reduce_info.Add(state.Key, new List>()); + reduce_info[state.Key].Add(new Tuple(tuple.Item1, tuple.Item2, tuple.Item3)); + + completes.Add(tuple.Item1); + } + } + + foreach (var pair in shift_tokens) + { + if (completes.Contains(pair.Key)) continue; + var shift = small_shift_info[pair.Value]; + if (!shift_info.ContainsKey(state.Key)) + shift_info.Add(state.Key, new List>()); + shift_info[state.Key].Add(new Tuple(shift.Item1, shift.Item2)); + } + } + // ----------------------------------------------------- + + if (occurred_conflict) + throw new Exception("Specify the rules to resolve Shift-Reduce Conflict!"); + + number_of_states = states.Count; + } + + #endregion + + #region Printer + + public void PrintProductionRules() + { + print_header("PRODUCTION RULES"); + int count = 1; + var builder = new StringBuilder(); + foreach (var pp in production_rules) + { + foreach (var p in pp.sub_productions) + { + builder.Append($"{(count++).ToString().PadLeft(4)}: "); + builder.Append($"{pp.production_name.ToString().PadLeft(10)} -> "); + + for (int i = 0; i < p.Count; i++) + { + builder.Append(p[i].production_name + " "); + } + + builder.Append("\r\n"); + } + } + GlobalPrinter.Append(builder.ToString()); + } + + /// + /// 파싱 테이블을 집합형태로 출력합니다. + /// + public void PrintStates() + { + print_header("FINAL STATES"); + for (int i = 0; i < number_of_states; i++) + { + var builder = new StringBuilder(); + var x = $"I{i} => "; + builder.Append(x); + if (shift_info.ContainsKey(i)) + { + builder.Append("SHIFT{" + string.Join(",", shift_info[i].Select(y => $"({production_rules[y.Item1].production_name},I{y.Item2})")) + "}"); + if (reduce_info.ContainsKey(i)) + builder.Append("\r\n" + "".PadLeft(x.Length) + "REDUCE{" + string.Join(",", reduce_info[i].Select(y => $"({(y.Item1 == -1 ? "$" : production_rules[y.Item1].production_name)},{(y.Item2 == 0 ? "accept" : production_rules[y.Item2].production_name)},{y.Item3})")) + "}"); + } + else if (reduce_info.ContainsKey(i)) + builder.Append("REDUCE{" + string.Join(",", reduce_info[i].Select(y => $"({(y.Item1 == -1 ? "$" : production_rules[y.Item1].production_name)},{(y.Item2 == 0 ? "accept" : production_rules[y.Item2].production_name)},{y.Item3})")) + "}"); + GlobalPrinter.Append(builder.ToString() + "\r\n"); + } + } + + /// + /// 파싱테이블을 테이블 형태로 출력합니다. + /// + public void PrintTable() + { + var production_mapping = new List>(); + var pm_count = 0; + + foreach (var pr in production_rules) + { + var pm = new List(); + foreach (var sub_pr in pr.sub_productions) + pm.Add(pm_count++); + production_mapping.Add(pm); + } + + var builder = new StringBuilder(); + + var tokens = new Dictionary(); + var max_len = 0; + foreach (var pp in production_rules) + if (pp.isterminal) + tokens.Add(tokens.Count, pp.index); + tokens.Add(tokens.Count, -1); + foreach (var pp in production_rules) + { + if (pp.index == 0) continue; + if (!pp.isterminal) + tokens.Add(tokens.Count, pp.index); + max_len = Math.Max(max_len, pp.production_name.Length); + } + + var split_line = "+" + new string('*', production_rules.Count + 1).Replace("*", new string('-', max_len + 2) + "+") + "\r\n"; + builder.Append(split_line); + + // print production rule + builder.Append('|' + "".PadLeft(max_len + 2) + '|'); + for (int i = 0; i < tokens.Count; i++) + { + builder.Append(" " + (tokens[i] == -1 ? "$" : production_rules[tokens[i]].production_name).PadLeft(max_len) + " "); + builder.Append('|'); + } + builder.Append("\r\n"); + builder.Append(split_line); + + // print states + for (int i = 0; i < number_of_states; i++) + { + builder.Append('|' + " " + $"{i}".PadLeft(max_len - 2) + " |"); + + // (what, (state_index, isshift)) + var sr_info = new Dictionary>(); + + if (shift_info.ContainsKey(i)) + { + foreach (var si in shift_info[i]) + if (!sr_info.ContainsKey(si.Item1)) + sr_info.Add(si.Item1, new Tuple(si.Item2, true)); + } + if (reduce_info.ContainsKey(i)) + { + foreach (var ri in reduce_info[i]) + if (!sr_info.ContainsKey(ri.Item1)) + sr_info.Add(ri.Item1, new Tuple(production_mapping[ri.Item2][ri.Item3], false)); + } + + for (int j = 0; j < tokens.Count; j++) + { + var k = tokens[j]; + if (sr_info.ContainsKey(k)) + { + var ss = ""; + if (sr_info[k].Item2) + { + if (production_rules[k].isterminal) + ss += "s" + sr_info[k].Item1; + else + ss = sr_info[k].Item1.ToString(); + } + else + { + if (sr_info[k].Item1 == 0) + ss += "acc"; + else + ss += "r" + sr_info[k].Item1; + } + builder.Append(" " + ss.PadLeft(max_len) + " |"); + } + else + { + builder.Append("".PadLeft(max_len + 2) + "|"); + } + } + + builder.Append("\r\n"); + } + builder.Append(split_line); + + print_header("PARSING TABLE"); + GlobalPrinter.Append(builder.ToString() + "\r\n"); + } + + #endregion + + #region FIRST + + /// + /// Calculate FIRST only Terminals + /// + /// + /// + private HashSet first_terminals(int index) + { + var result = new HashSet(); + var q = new Queue(); + var visit = new List(); + visit.AddRange(Enumerable.Repeat(false, production_rules.Count)); + q.Enqueue(index); + + while (q.Count != 0) + { + var p = q.Dequeue(); + if (p < 0 || visit[p]) continue; + visit[p] = true; + + if (p < 0 || production_rules[p].isterminal) + result.Add(p); + else + { + foreach (var pp in production_rules[p].sub_productions.Where(x => x.Count > 0)) + { + foreach (var ppp in pp) + { + q.Enqueue(ppp.index); + if (ppp.sub_productions.All(x => x.Count != 0)) + break; + } + } + } + } + + return result; + } + + /// + /// Calculate FIRST only Non-Terminals + /// + /// + /// + private List> first_nonterminals(int production_rule_index) + { + // (production_rule_index, sub_productions_pos, dot_position) + var first_l = new List>(); + // (production_rule_index, sub_productions_pos) + var first_q = new Queue>(); + // (production_rule_index, (sub_productions_pos)) + var first_visit = new Dictionary>(); + first_q.Enqueue(new Tuple(production_rule_index, 0)); + for (int j = 0; j < production_rules[production_rule_index].sub_productions.Count; j++) + first_q.Enqueue(new Tuple(production_rule_index, j)); + // Get all of starts node FIRST non-terminals + while (first_q.Count != 0) + { + var t = first_q.Dequeue(); + if (first_visit.ContainsKey(t.Item1) && first_visit[t.Item1].Contains(t.Item2)) continue; + if (!first_visit.ContainsKey(t.Item1)) first_visit.Add(t.Item1, new HashSet()); + first_visit[t.Item1].Add(t.Item2); + first_l.Add(new Tuple(t.Item1, t.Item2, 0)); + for (int i = 0; i < production_rules[t.Item1].sub_productions.Count; i++) + { + var sub_pr = production_rules[t.Item1].sub_productions[i]; + if (sub_pr.Count > 0 && sub_pr[0].isterminal == false) + for (int j = 0; j < production_rules[sub_pr[0].index].sub_productions.Count; j++) + first_q.Enqueue(new Tuple(sub_pr[0].index, j)); + } + } + return first_l; + } + + #endregion + + #region FOLLOW + + /// + /// Get FOLLOW set for all production-rules + /// + /// + /// + private List> follow_terminals(List> FIRST) + { + var FOLLOW = new List>(); + for (int i = 0; i < production_rules.Count; i++) + FOLLOW.Add(new HashSet()); + FOLLOW[0].Add(-1); // -1: Sentinel + + // 1. B -> a A b, Add FIRST(b) to FOLLOW(A) + for (int i = 0; i < production_rules.Count; i++) + if (!production_rules[i].isterminal) + foreach (var rule in production_rules[i].sub_productions) + for (int j = 0; j < rule.Count - 1; j++) + if (rule[j].isterminal == false || rule[j + 1].isterminal) + foreach (var r in FIRST[rule[j + 1].index]) + FOLLOW[rule[j].index].Add(r); + + // 2. B -> a A b and empty -> FIRST(b), Add FOLLOW(B) to FOLLOW(A) + for (int i = 0; i < production_rules.Count; i++) + if (!production_rules[i].isterminal) + foreach (var rule in production_rules[i].sub_productions) + if (rule.Count > 2 && rule[rule.Count - 2].isterminal == false && FIRST[rule.Last().index].Contains(EmptyString.index)) + foreach (var r in FOLLOW[i]) + FOLLOW[rule[rule.Count - 2].index].Add(r); + + // 3. B -> a A, Add FOLLOW(B) to FOLLOW(A) + for (int i = 0; i < production_rules.Count; i++) + if (!production_rules[i].isterminal) + foreach (var rule in production_rules[i].sub_productions) + if (rule.Count > 0 && rule.Last().isterminal == false) + foreach (var r in FOLLOW[i]) + if (rule.Last().index > 0) + FOLLOW[rule.Last().index].Add(r); + + return FOLLOW; + } + + #endregion + + #region Closure with Lookahead + + /// + /// Get lookahead states item with first item's closure + /// This function is used in first_with_lookahead function. + /// -1: Sentinel lookahead + /// + /// + /// + private List>> lookahead_with_first(int production_rule_index, int sub_production, int sub_production_index, HashSet pred) + { + // (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + var states = new List>>(); + // (production_rule_index, (sub_productions_pos)) + var first_visit = new Dictionary>(); + states.Add(new Tuple>(production_rule_index, sub_production, sub_production_index, pred)); + if (production_rule_index == 0 && sub_production == 0 && sub_production_index == 0) + pred.Add(-1); // Push sentinel + if (production_rules[production_rule_index].sub_productions[sub_production].Count > sub_production_index) + { + if (!production_rules[production_rule_index].sub_productions[sub_production][sub_production_index].isterminal) + { + var index_populate = production_rules[production_rule_index].sub_productions[sub_production][sub_production_index].index; + if (production_rules[production_rule_index].sub_productions[sub_production].Count <= sub_production_index + 1) + { + for (int i = 0; i < production_rules[index_populate].sub_productions.Count; i++) + states.Add(new Tuple>(index_populate, i, 0, new HashSet(pred))); + } + else + { + var first_lookahead = first_terminals(production_rules[production_rule_index].sub_productions[sub_production][sub_production_index + 1].index); + for (int i = 0; i < production_rules[index_populate].sub_productions.Count; i++) + states.Add(new Tuple>(index_populate, i, 0, new HashSet(first_lookahead))); + } + } + } + return states; + } + + /// + /// Get FIRST items with lookahead (Build specific states completely) + /// + /// TODO: Fix issues #4:first_terminals + /// + /// + /// + /// + /// + /// + private List>> first_with_lookahead(int production_rule_index, int sub_production, int sub_production_index, HashSet pred) + { + // (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + var states = new List>>(); + // (production_rule_index + sub_productions_pos + dot_position), (states_index) + var states_prefix = new Dictionary(); + + var q = new Queue>>>(); + q.Enqueue(lookahead_with_first(production_rule_index, sub_production, sub_production_index, pred)); + while (q.Count != 0) + { + var ll = q.Dequeue(); + foreach (var e in ll) + { + var ii = i2s(e.Item1, e.Item2, e.Item3); + if (!states_prefix.ContainsKey(ii)) + { + states_prefix.Add(ii, states.Count); + states.Add(e); + q.Enqueue(lookahead_with_first(e.Item1, e.Item2, e.Item3, e.Item4)); + } + else + { + foreach (var hse in e.Item4) + states[states_prefix[ii]].Item4.Add(hse); + } + } + } + + // (production_rule_index + sub_productions_pos + dot_position), (states_index) + var states_prefix2 = new Dictionary(); + var states_count = 0; + bool change = false; + + do + { + change = false; + q.Enqueue(lookahead_with_first(production_rule_index, sub_production, sub_production_index, pred)); + while (q.Count != 0) + { + var ll = q.Dequeue(); + foreach (var e in ll) + { + var ii = i2s(e.Item1, e.Item2, e.Item3); + if (!states_prefix2.ContainsKey(ii)) + { + states_prefix2.Add(ii, states_count); + foreach (var hse in e.Item4) + if (!states[states_prefix[ii]].Item4.Contains(hse)) + { + change = true; + states[states_prefix[ii]].Item4.Add(hse); + } + q.Enqueue(lookahead_with_first(e.Item1, e.Item2, e.Item3, states[states_count].Item4)); + states_count++; + } + else + { + foreach (var hse in e.Item4) + if (!states[states_prefix[ii]].Item4.Contains(hse)) + { + change = true; + states[states_prefix[ii]].Item4.Add(hse); + } + } + } + } + } while (change); + + return states; + } + + #endregion + + #region Closure + + /// + /// Get states item with first item's closure + /// This function is used in closure function. + /// -1: Sentinel lookahead + /// + /// + /// + private List> closure_first(int production_rule_index, int sub_production, int sub_production_index) + { + // (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + var states = new List>(); + states.Add(new Tuple(production_rule_index, sub_production, sub_production_index)); + if (production_rules[production_rule_index].sub_productions[sub_production].Count > sub_production_index) + { + if (!production_rules[production_rule_index].sub_productions[sub_production][sub_production_index].isterminal) + { + var index_populate = production_rules[production_rule_index].sub_productions[sub_production][sub_production_index].index; + for (int i = 0; i < production_rules[index_populate].sub_productions.Count; i++) + states.Add(new Tuple(index_populate, i, 0)); + } + } + return states; + } + + /// + /// Get CLOSURE items (Build specific states completely) + /// + /// + /// + /// + /// + /// + private List> closure(int production_rule_index, int sub_production, int sub_production_index) + { + // (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + var states = new List>(); + // (production_rule_index + sub_productions_pos + dot_position), (states_index) + var states_prefix = new Dictionary(); + + var q = new Queue>>(); + q.Enqueue(closure_first(production_rule_index, sub_production, sub_production_index)); + while (q.Count != 0) + { + var ll = q.Dequeue(); + foreach (var e in ll) + { + var ii = i2s(e.Item1, e.Item2, e.Item3); + if (!states_prefix.ContainsKey(ii)) + { + states_prefix.Add(ii, states.Count); + states.Add(e); + q.Enqueue(closure_first(e.Item1, e.Item2, e.Item3)); + } + } + } + + // (production_rule_index + sub_productions_pos + dot_position), (states_index) + var states_prefix2 = new Dictionary(); + var states_count = 0; + + q.Enqueue(closure_first(production_rule_index, sub_production, sub_production_index)); + while (q.Count != 0) + { + var ll = q.Dequeue(); + foreach (var e in ll) + { + var ii = i2s(e.Item1, e.Item2, e.Item3); + if (!states_prefix2.ContainsKey(ii)) + { + states_prefix2.Add(ii, states_count); + q.Enqueue(closure_first(e.Item1, e.Item2, e.Item3)); + states_count++; + } + } + } + + return states; + } + + #endregion + + #region SCC + + /// + /// Determine group using tarjan's strongly connected components algorithm. + /// + /// + /// + private Dictionary determine_group(Dictionary> degree) + { + return null; + } + + /// + /// Returns the graph traversal rule and SCC information + /// + /// + /// + private Tuple, Dictionary> how_to_visit( + // (from_state_index, (when_state_index, to_state_index)) + List>>> goto_table) + { + // to edge + var degree = new Dictionary>(); + for (int i = 0; i < goto_table.Count; i++) + { + degree.Add(i, new List()); + for (int j = 0; j < goto_table[i].Item2.Count; i++) + degree[i].Add(goto_table[i].Item2[j].Item2); + } + + return null; + } + + #endregion + + #region Lookahead + + /// + /// Get FIRST items of production rule item + /// + /// + /// + /// + /// + private HashSet first_terminals( + List> first, List include_epsillon, + int production_rule_index, int sub_production, int sub_production_index, HashSet lookahead) + { + // 1. If the handle points last of production rule item, + // A -> abc. [~] + // then return only lookaheads. + if (production_rules[production_rule_index].sub_productions[sub_production].Count <= sub_production_index) + return lookahead; + + // 2. Check is terminal + // A -> aB.c [~] (a,c=terminal, B=non-terminal) + // If 'c' is terminal, then just return 'c'. + if (production_rules[production_rule_index].sub_productions[sub_production][sub_production_index].isterminal) + return new HashSet { production_rules[production_rule_index].sub_productions[sub_production][sub_production_index].index }; + + // 3. Get FIRST of Non-terminal + // A -> a.Bc [~] (a,c=terminal, B=non-terminal) + // If 'B' is non-terminal, then get FIRST set of non-terminal 'B'. + var result = new HashSet(first[production_rules[production_rule_index].sub_productions[sub_production][sub_production_index].index]); + + // 4. Check empty-string + // A -> a.BC [~] (a=terminal, B,C=non-terminal) + // If epsillon contains in FIRST(BC) then add lookahead to result. + var fully_empty_string = true; + for (int i = sub_production_index; i < production_rules[production_rule_index].sub_productions[sub_production].Count; i++) + { + var index = production_rules[production_rule_index].sub_productions[sub_production][i]; + if (index.isterminal || !include_epsillon[index.index]) + { + fully_empty_string = false; + break; + } + + if (i != sub_production_index) + foreach (var lk in first[index.index]) + result.Add(lk); + } + + // 5. If all of after symbol contains epsillon + // A -> a.BCD [~] (a=terminal, B,C,D=non-terminal, B,C,D contains epsillon) + // Then insert FOLLOW(A) or lookahead to FIRST(BCD) + if (fully_empty_string) + foreach (var lk in lookahead) + result.Add(lk); + + return result; + } + + /// + /// Calculate lookahead from specific non-terminal symbol on LR(0) states + /// + /// + /// + /// + /// + /// + private HashSet first_from_nonterminal( + List> first, List include_epsillon, + // (state_index, (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + Dictionary>>> states, + int state_index, int /* non-terminal */ production_rule_index) + { + var result = new HashSet(); + + if (production_rule_index == 0) + result.Add(-1); + + foreach (var state in states[state_index]) + { + // If another item handle points input non-terminal symbol, + if (production_rules[state.Item1].sub_productions[state.Item2].Count != 0 && + production_rules[state.Item1].sub_productions[state.Item2].Count > state.Item3 && + production_rules[state.Item1].sub_productions[state.Item2][state.Item3].index == production_rule_index) + { + var ft = first_terminals(first, include_epsillon, state.Item1, state.Item2, state.Item3 + 1, state.Item4); + foreach (var lookahead in ft) + result.Add(lookahead); + } + } + + return result; + } + + bool lookahead_change = false; + + /// + /// Fill lookahead recursive. + /// + /// + /// + /// + /// + /// + private HashSet fill_lookahead( + List> first, List include_epsillon, + // (state_index, (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + Dictionary>>> states, + // (state_index, (state_item_index, (handle_position, parent_state_index, parent_state_item_index))) + Dictionary>>> pred, + int state, int state_index, int depth = int.MaxValue) + { + var lookaheads = new HashSet(); + + if (state == 0 || depth == 0 || !pred[state].ContainsKey(state_index)) + { + var my_lookahead = first_from_nonterminal(first, include_epsillon, states, state, states[state][state_index].Item1); + foreach (var lk in my_lookahead) + lookaheads.Add(lk); + + // 같은 production rule에 전파 + foreach (var item in states[state]) + if (item.Item1 == states[state][state_index].Item1) + foreach (var lk in lookaheads) + if (!item.Item4.Contains(lk)) + { + item.Item4.Add(lk); + lookahead_change = true; + } + } + else + { + foreach (var tracing in pred[state][state_index]) + { + var lookahead = fill_lookahead(first, include_epsillon, states, pred, tracing.Item2, tracing.Item3, tracing.Item1); + + foreach (var lk in lookahead) + lookaheads.Add(lk); + } + } + + foreach (var lk in lookaheads) + if (!states[state][state_index].Item4.Contains(lk)) + { + states[state][state_index].Item4.Add(lk); + lookahead_change = true; + } + + propagate_lookahead(states, state, state_index, first_terminals(first, include_epsillon, + states[state][state_index].Item1, states[state][state_index].Item2, states[state][state_index].Item3 + 1, states[state][state_index].Item4)); + + return lookaheads; + } + + /// + /// Propagate lookahead for same level item of states. + /// + /// + /// + /// + /// + /// + private void propagate_lookahead( + // (state_index, (production_rule_index, sub_productions_pos, dot_position, (lookahead)) + Dictionary>>> states, + int state, int state_index, HashSet lookahead) + { + var item = states[state][state_index]; + var nts = first_nonterminals(item.Item1, item.Item2, item.Item3); + + var index = 0; + foreach (var state_item in states[state]) + { + if (nts.Contains(state_item.Item1)) + { + var change = false; + var spont = new HashSet(state_item.Item4); + + foreach (var lk in lookahead) + { + if (!state_item.Item4.Contains(lk)) + { + change = true; + lookahead_change = true; + state_item.Item4.Add(lk); + } + } + + if (change) + propagate_lookahead(states, state, index, state_item.Item4); + } + index++; + } + } + + /// + /// Get FIRST nonterminals of specific LALR(1) item + /// + /// + /// + /// + /// + private HashSet first_nonterminals( + int production_rule_index, int sub_production_index, int dot_position) + { + var result = new HashSet(); + + for (int i = dot_position; i < production_rules[production_rule_index].sub_productions[sub_production_index].Count; i++) + { + var item = production_rules[production_rule_index].sub_productions[sub_production_index][i]; + + // We interested in only non-terminals. + if (item.isterminal) + break; + + result.Add(item.index); + + // Check this non-terminal contains epsillon. + // If not contains, then break + if (item.sub_productions.All(x => x.Count > 0)) + break; + } + + return result; + } + + #endregion + + #region Shift Reduce Conflict Solver Helper + + private ParserProduction get_first_on_right_terminal(ParserProduction pp, int sub_production) + { + for (int i = pp.sub_productions[sub_production].Count - 1; i >= 0; i--) + if (pp.sub_productions[sub_production][i].isterminal) + return pp.sub_productions[sub_production][i]; + throw new Exception($"Cannot solve shift-reduce conflict!\r\nProduction Name: {pp.production_name}\r\nProduction Index: {sub_production}"); + } + + #endregion + + #region Create Parser Instance + + /// + /// Create ShiftReduce Parser + /// + /// + public ShiftReduceParser CreateShiftReduceParserInstance() + { + var symbol_table = new Dictionary(); + var jump_table = new int[number_of_states][]; + var goto_table = new int[number_of_states][]; + var grammar = new List>(); + var grammar_group = new List(); + var production_mapping = new List>(); + var semantic_rules = new List(); + var pm_count = 0; + + foreach (var pr in production_rules) + { + var ll = new List>(); + var pm = new List(); + foreach (var sub_pr in pr.sub_productions) + { + ll.Add(sub_pr.Select(x => x.index).ToList()); + pm.Add(pm_count++); + grammar_group.Add(production_mapping.Count); + } + grammar.AddRange(ll); + production_mapping.Add(pm); + semantic_rules.AddRange(pr.actions); + } + + for (int i = 0; i < number_of_states; i++) + { + // Last elements is sentinel + jump_table[i] = new int[production_rules.Count + 1]; + goto_table[i] = new int[production_rules.Count + 1]; + } + + foreach (var pr in production_rules) + symbol_table.Add(pr.production_name ?? "^", pr.index); + symbol_table.Add("$", production_rules.Count); + + foreach (var shift in shift_info) + foreach (var elem in shift.Value) + { + jump_table[shift.Key][elem.Item1] = 1; + goto_table[shift.Key][elem.Item1] = elem.Item2; + } + + foreach (var reduce in reduce_info) + foreach (var elem in reduce.Value) + { + var index = elem.Item1; + if (index == -1) index = production_rules.Count; + if (jump_table[reduce.Key][index] != 0) + throw new Exception($"Error! Shift-Reduce Conflict is not solved! Please use LALR or LR(1) parser!\r\nJump-Table: {reduce.Key} {index}"); + if (elem.Item2 == 0) + jump_table[reduce.Key][index] = 3; + else + { + jump_table[reduce.Key][index] = 2; + goto_table[reduce.Key][index] = production_mapping[elem.Item2][elem.Item3]; + } + } + + return new ShiftReduceParser(symbol_table, jump_table, goto_table, grammar_group.ToArray(), grammar.Select(x => x.ToArray()).ToArray(), semantic_rules); + } + + /// + /// Create ShiftReduce Parser + /// + /// + public ExtendedShiftReduceParser CreateExtendedShiftReduceParserInstance() + { + var symbol_table = new Dictionary(); + var table = new int[number_of_states][]; + var grammar = new List>(); + var grammar_group = new List(); + var production_mapping = new List>(); + var semantic_rules = new List(); + var pm_count = 0; + + foreach (var pr in production_rules) + { + var ll = new List>(); + var pm = new List(); + foreach (var sub_pr in pr.sub_productions) + { + ll.Add(sub_pr.Select(x => x.index).ToList()); + pm.Add(pm_count++); + grammar_group.Add(production_mapping.Count); + } + grammar.AddRange(ll); + production_mapping.Add(pm); + semantic_rules.AddRange(pr.actions); + } + + for (int i = 0; i < number_of_states; i++) + { + // Last elements is sentinel + table[i] = new int[production_rules.Count + 1]; + } + + var acc_max = 0; + foreach (var pr in production_rules) + symbol_table.Add(pr.production_name ?? "^", pr.index); + symbol_table.Add("$", production_rules.Count); + + foreach (var shift in shift_info) + foreach (var elem in shift.Value) + { + table[shift.Key][elem.Item1] = elem.Item2; + if (elem.Item2 > acc_max) + acc_max = elem.Item2; + } + + foreach (var reduce in reduce_info) + foreach (var elem in reduce.Value) + { + var index = elem.Item1; + if (index == -1) index = production_rules.Count; + if (table[reduce.Key][index] != 0) + throw new Exception($"Error! Shift-Reduce Conflict is not solved! Please use LALR or LR(1) parser!\r\nJump-Table: {reduce.Key} {index}"); + if (elem.Item2 == 0) + table[reduce.Key][index] = acc_max + 1; + else + { + table[reduce.Key][index] = -production_mapping[elem.Item2][elem.Item3]; + } + } + + return new ExtendedShiftReduceParser(symbol_table, acc_max + 1, table, grammar_group.ToArray(), grammar.Select(x => x.Count).ToArray(), semantic_rules); + } + + #endregion + } + + public class ParsingTree + { + public class ParsingTreeNode + { + public string Production; + public string Contents; + public object UserContents; + public int ProductionRuleIndex; + public ParsingTreeNode Parent; + public List Childs; + + public static ParsingTreeNode NewNode() + => new ParsingTreeNode { Parent = null, Childs = new List() }; + public static ParsingTreeNode NewNode(string production) + => new ParsingTreeNode { Parent = null, Childs = new List(), Production = production }; + public static ParsingTreeNode NewNode(string production, string contents) + => new ParsingTreeNode { Parent = null, Childs = new List(), Production = production, Contents = contents }; + } + + public ParsingTreeNode root; + + public ParsingTree(ParsingTreeNode root) + { + this.root = root; + } + + static void inner_print(StringBuilder builder, ParsingTreeNode node, string indent, bool last) + { + builder.Append(indent); + if (last) + { + builder.Append("+-"); + indent += " "; + } + else + { + builder.Append("|-"); + indent += "| "; + } + + if (node.Childs.Count == 0) + { + builder.Append(node.Production + " " + node.Contents + "\r\n"); + } + else + { + builder.Append(node.Production + "\r\n"); + } + for (int i = 0; i < node.Childs.Count; i++) + inner_print(builder, node.Childs[i], indent, i == node.Childs.Count - 1); + } + + public void Print(StringBuilder builder) + { + inner_print(builder, root, "", true); + } + } + + /// + /// Shift-Reduce Parser for LR(1) + /// + public class ShiftReduceParser + { + Dictionary symbol_name_index = new Dictionary(); + List symbol_index_name = new List(); + Stack state_stack = new Stack(); + Stack treenode_stack = new Stack(); + List actions; + + // 3 1 2 0 + // Accept? Shift? Reduce? Error? + int[][] jump_table; + int[][] goto_table; + int[][] production; + int[] group_table; + + public ShiftReduceParser(Dictionary symbol_table, int[][] jump_table, int[][] goto_table, int[] group_table, int[][] production, List actions) + { + symbol_name_index = symbol_table; + this.jump_table = jump_table; + this.goto_table = goto_table; + this.production = production; + this.group_table = group_table; + this.actions = actions; + var l = symbol_table.ToList().Select(x => new Tuple(x.Value, x.Key)).ToList(); + l.Sort(); + l.ForEach(x => symbol_index_name.Add(x.Item2)); + } + + bool latest_error; + bool latest_reduce; + public bool Accept() => state_stack.Count == 0; + public bool Error() => latest_error; + public bool Reduce() => latest_reduce; + + public void Clear() + { + latest_error = latest_reduce = false; + state_stack.Clear(); + treenode_stack.Clear(); + } + + public ParsingTree Tree => new ParsingTree(treenode_stack.Peek()); + + public string Stack() => string.Join(" ", new Stack(state_stack)); + + public void Insert(string token_name, string contents) => Insert(symbol_name_index[token_name], contents); + public void Insert(int index, string contents) + { + if (state_stack.Count == 0) + { + state_stack.Push(0); + latest_error = false; + } + latest_reduce = false; + + switch (jump_table[state_stack.Peek()][index]) + { + case 0: + // Panic mode + state_stack.Clear(); + treenode_stack.Clear(); + latest_error = true; + break; + + case 1: + // Shift + state_stack.Push(goto_table[state_stack.Peek()][index]); + treenode_stack.Push(ParsingTree.ParsingTreeNode.NewNode(symbol_index_name[index], contents)); + break; + + case 2: + // Reduce + reduce(index); + latest_reduce = true; + break; + + case 3: + // Nothing + break; + } + } + + public ParsingTree.ParsingTreeNode LatestReduce() => treenode_stack.Peek(); + private void reduce(int index) + { + var reduce_production = goto_table[state_stack.Peek()][index]; + var reduce_treenodes = new List(); + + // Reduce Stack + for (int i = 0; i < production[reduce_production].Length; i++) + { + state_stack.Pop(); + reduce_treenodes.Insert(0, treenode_stack.Pop()); + } + + state_stack.Push(goto_table[state_stack.Peek()][group_table[reduce_production]]); + + var reduction_parent = ParsingTree.ParsingTreeNode.NewNode(symbol_index_name[group_table[reduce_production]]); + reduction_parent.ProductionRuleIndex = reduce_production - 1; + reduce_treenodes.ForEach(x => x.Parent = reduction_parent); + reduction_parent.Contents = string.Join("", reduce_treenodes.Select(x => x.Contents)); + reduction_parent.Childs = reduce_treenodes; + treenode_stack.Push(reduction_parent); + if (actions.Count != 0) + actions[reduction_parent.ProductionRuleIndex].SemanticAction(reduction_parent); + } + + public string ToCSCode(string class_name) + { + var builder = new StringBuilder(); + var indent = ""; + Action up_indent = () => { indent += " "; }; + Action down_indent = () => { if (indent.Length > 0) indent = indent.Substring(4); }; + Action append = (string s) => { builder.Append($"{indent}{s}\r\n"); }; + append("public class " + class_name); + append("{"); + up_indent(); + + /////////////////// + append("Dictionary symbol_table = new Dictionary()"); + append("{"); + up_indent(); + foreach (var st in symbol_name_index) + append("{" + ('"' + st.Key + '"').PadLeft(symbol_name_index.Select(x => x.Key.Length).Max() + 3) + "," + st.Value.ToString().PadLeft(4) + " },"); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("int[][] jump_table = new int[][] {"); + up_indent(); + foreach (var gt in jump_table) + append("new int[] {" + string.Join(",", gt.Select(x => x.ToString().PadLeft(4))) + " },"); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("int[][] goto_table = new int[][] {"); + up_indent(); + foreach (var gt in goto_table) + append("new int[] {" + string.Join(",", gt.Select(x => x.ToString().PadLeft(4))) + " },"); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("int[][] production = new int[][] {"); + up_indent(); + foreach (var gt in production) + append("new int[] {" + string.Join(",", gt.Select(x => x.ToString().PadLeft(4))) + " },"); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("int[] group_table = new int[] {"); + up_indent(); + append(string.Join(",", group_table.Select(x => x.ToString().PadLeft(4)))); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("public ShiftReduceParser Parser => new ShiftReduceParser("); + append(" symbol_table, jump_table, goto_table, group_table, production, "); + append(" Enumerable.Repeat(new ParserAction { SemanticAction = (ParsingTree.ParsingTreeNode node) => { } }, production.Length).ToList());"); + + down_indent(); + append("}"); + return builder.ToString(); + } + } + + /// + /// Shift-Reduce Parser for LR(1) + /// + public class ExtendedShiftReduceParser + { + Dictionary symbol_name_index = new Dictionary(); + List symbol_index_name = new List(); + Stack state_stack = new Stack(); + Stack treenode_stack = new Stack(); + List actions; + + // accept + - 0 + // Accept? Shift? Reduce? Error? + int[][] table; + int[] production; + int[] group_table; + int accept; + + public ExtendedShiftReduceParser(Dictionary symbol_table, int accept_code, int[][] table, int[] group_table, int[] production, List actions) + { + symbol_name_index = symbol_table; + this.table = table; + this.production = production; + this.group_table = group_table; + this.actions = actions; + this.accept = accept_code; + var l = symbol_table.ToList().Select(x => new Tuple(x.Value, x.Key)).ToList(); + l.Sort(); + l.ForEach(x => symbol_index_name.Add(x.Item2)); + } + + bool latest_error; + bool latest_reduce; + public bool Accept() => state_stack.Count == 0; + public bool Error() => latest_error; + public bool Reduce() => latest_reduce; + + public void Clear() + { + latest_error = latest_reduce = false; + state_stack.Clear(); + treenode_stack.Clear(); + } + + public ParsingTree Tree => new ParsingTree(treenode_stack.Peek()); + + public string Stack() => string.Join(" ", new Stack(state_stack)); + + public void Insert(string token_name, string contents) => Insert(symbol_name_index[token_name], contents); + public void Insert(int index, string contents) + { + if (state_stack.Count == 0) + { + state_stack.Push(0); + latest_error = false; + } + latest_reduce = false; + + int code = table[state_stack.Peek()][index]; + + if (code == accept) + { + // Nothing + } + else if (code > 0) + { + // Shift + state_stack.Push(table[state_stack.Peek()][index]); + treenode_stack.Push(ParsingTree.ParsingTreeNode.NewNode(symbol_index_name[index], contents)); + } + else if (code < 0) + { + // Reduce + reduce(index); + latest_reduce = true; + } + else + { + // Panic mode + state_stack.Clear(); + treenode_stack.Clear(); + latest_error = true; + } + } + + public ParsingTree.ParsingTreeNode LatestReduce() => treenode_stack.Peek(); + private void reduce(int index) + { + var reduce_production = -table[state_stack.Peek()][index]; + var reduce_treenodes = new List(); + + // Reduce Stack + for (int i = 0; i < production[reduce_production]; i++) + { + state_stack.Pop(); + reduce_treenodes.Insert(0, treenode_stack.Pop()); + } + + state_stack.Push(table[state_stack.Peek()][group_table[reduce_production]]); + + var reduction_parent = ParsingTree.ParsingTreeNode.NewNode(symbol_index_name[group_table[reduce_production]]); + reduction_parent.ProductionRuleIndex = reduce_production - 1; + reduce_treenodes.ForEach(x => x.Parent = reduction_parent); + reduction_parent.Contents = string.Join("", reduce_treenodes.Select(x => x.Contents)); + reduction_parent.Childs = reduce_treenodes; + treenode_stack.Push(reduction_parent); + } + + public string ToCSCode(string class_name) + { + var builder = new StringBuilder(); + var indent = ""; + Action up_indent = () => { indent += " "; }; + Action down_indent = () => { if (indent.Length > 0) indent = indent.Substring(4); }; + Action append = (string s) => { builder.Append($"{indent}{s}\r\n"); }; + append("public class " + class_name); + append("{"); + up_indent(); + + /////////////////// + append("Dictionary symbol_table = new Dictionary()"); + append("{"); + up_indent(); + foreach (var st in symbol_name_index) + append("{" + ('"' + st.Key + '"').PadLeft(symbol_name_index.Select(x => x.Key.Length).Max() + 3) + "," + st.Value.ToString().PadLeft(4) + " },"); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("int[][] goto_table = new int[][] {"); + up_indent(); + foreach (var gt in table) + append("new int[] {" + string.Join(",", gt.Select(x => x.ToString().PadLeft(4))) + " },"); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("int[] production = new int[] {"); + up_indent(); + append(string.Join(",", production.Select(x => x.ToString().PadLeft(4)))); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("int[] group_table = new int[] {"); + up_indent(); + append(string.Join(",", group_table.Select(x => x.ToString().PadLeft(4)))); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("int accept = " + accept + ";"); + append(""); + + /////////////////// + append("public ExtendedShiftReduceParser Parser => new ExtendedShiftReduceParser("); + append(" symbol_table, accept, goto_table, group_table, production, "); + append(" Enumerable.Repeat(new ParserAction { SemanticAction = (ParsingTree.ParsingTreeNode node) => { } }, production.Length).ToList());"); + + down_indent(); + append("}"); + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/InhaCC/cc/ScannerGenerator.cs b/InhaCC/cc/ScannerGenerator.cs new file mode 100644 index 0000000..3cfda6a --- /dev/null +++ b/InhaCC/cc/ScannerGenerator.cs @@ -0,0 +1,1231 @@ +/* + + Copyright (C) 2019. rollrat All Rights Reserved. + + Author: Jeong HyunJun + +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ParserGenerator +{ + /// + /// Optimized DFA Generator + /// + public class SimpleRegex + { + public SimpleRegex(diagram dia) { Diagram = dia; } + public SimpleRegex(string pattern) { Diagram = build(pattern); } + public SimpleRegex() { } + public List build_errors = new List(); + public diagram Diagram; + public const char e_closure = (char)0xFFFF; + + public const int byte_size = 256; + + public class transition_node + { + public int index; + + public bool is_acceptable; + + // will be used scanner-generator + public string accept_token_name; + public List accept_token_names; + + /// + /// 0: e-closure + /// 'a-z', 'A-Z', '0-9': shift terminals + /// [+-*/[]()_=^&$#@!~] + /// + public List> transition; + } + + public class diagram + { + /// + /// Starts node + /// + public transition_node start_node; + + /// + /// All nodes + /// + public List nodes; + + public int count_of_vertex; + } + + public void MakeNFA(string pattern) { Diagram = make_nfa("(" + pattern + ")"); } + public void OptimizeNFA() { while (opt_nfa(Diagram)) ; } + public void NFAtoDFA() { Diagram = nfa2dfa(Diagram); } + public void MinimizeDFA() { opt_dfa(Diagram); } + public string PrintDiagram() { return print_diagram(Diagram); } + + public static string PrintDiagram(diagram dia) { return print_diagram(dia); } + public static string PrintGraph(diagram dia) { return print_diagram_for_graphviz(dia); } + + /// + /// Try simple-regular-expression to optimized DFA + /// + /// + /// + private diagram build(string pattern) + { + var diagram = make_nfa("(" + pattern + ")"); + while (opt_nfa(diagram)) ; + var dfa = nfa2dfa(diagram); + opt_dfa(dfa); + return dfa; + } + + /// + /// return starts and ends and node_count + /// + /// + /// + /// + /// + private Tuple copy_nodes(ref List list, int starts, int ends) + { + var jump_count = ends - starts + 1; + var llist = new List(); + for (int i = 0; i < jump_count; i++) + llist.Add(new transition_node { transition = new List>() }); + for (int i = starts; i <= ends; i++) + { + llist[i - starts].index = list.Count + i - starts; + foreach (var ts in list[i].transition) + llist[i - starts].transition.Add(new Tuple(ts.Item1, llist[ts.Item2.index - starts])); + } + for (int i = 0; i < jump_count; i++) + list.Add(llist[i]); + return new Tuple(llist[0], llist.Last(), ends - starts + 1); + } + + /// + /// Try simple-regular-expression to NFA. + /// + /// + /// + private diagram make_nfa(string pattern) + { + var first_valid_stack = new Stack(); + var second_valid_stack = new Stack(); + var first_valid_stack_stack = new List>(); + var second_valid_stack_stack = new List>(); + var tail_nodes = new Stack>(); + var opstack = new Stack(); + var diagram = new diagram(); + + var index_count = 0; + var cur = new transition_node(); + var nodes = new List(); + + var depth = 0; + + cur.index = index_count++; + cur.transition = new List>(); + diagram.start_node = cur; + first_valid_stack.Push(cur); + nodes.Add(cur); + + for (int i = 0; i < pattern.Length; i++) + { + switch (pattern[i]) + { + case '(': + opstack.Push('('); + depth++; + + // Copy stack and push to stack stack + first_valid_stack_stack.Add(new Stack(new Stack(first_valid_stack))); + second_valid_stack_stack.Add(new Stack(new Stack(second_valid_stack))); + second_valid_stack.Push(first_valid_stack.Peek()); + first_valid_stack.Push(cur); + tail_nodes.Push(new List()); + break; + + case ')': + if (opstack.Count == 0 || opstack.Peek() != '(') + { + build_errors.Add($"[regex] {i} no opener!"); + return null; + } + tail_nodes.Peek().Add(cur); + var ends_point = new transition_node { index = index_count++, transition = new List>() }; + cur = ends_point; + nodes.Add(cur); + + // Connect tail nodes + foreach (var tail_node in tail_nodes.Peek()) + tail_node.transition.Add(new Tuple(e_closure, cur)); + tail_nodes.Pop(); + + // Pop from stack stack + first_valid_stack = first_valid_stack_stack.Last(); + first_valid_stack_stack.RemoveAt(first_valid_stack_stack.Count - 1); + second_valid_stack = second_valid_stack_stack.Last(); + second_valid_stack_stack.RemoveAt(second_valid_stack_stack.Count - 1); + second_valid_stack.Push(first_valid_stack.Peek()); + first_valid_stack.Push(cur); + + depth--; + break; + + case '|': + tail_nodes.Peek().Add(cur); + cur = first_valid_stack_stack[first_valid_stack_stack.Count - 1].Peek(); + break; + + case '?': + second_valid_stack.Peek().transition.Add(new Tuple(e_closure, cur)); + break; + + case '+': + var ttc = copy_nodes(ref nodes, second_valid_stack.Peek().index, cur.index); + cur.transition.Add(new Tuple(e_closure, ttc.Item1)); + ttc.Item2.transition.Add(new Tuple(e_closure, cur)); + index_count += ttc.Item3; + break; + + case '*': + second_valid_stack.Peek().transition.Add(new Tuple(e_closure, cur)); + cur.transition.Add(new Tuple(e_closure, second_valid_stack.Peek())); + break; + + case '[': + var ch_list = new List(); + i++; + bool inverse = false; + if (i < pattern.Length && pattern[i] == '^') + { + inverse = true; + i++; + } + for (; i < pattern.Length && pattern[i] != ']'; i++) + { + if (pattern[i] == '\\' && i + 1 < pattern.Length) + { + if (@"+-?*|()[].=<>/\".Contains(pattern[i + 1])) + ch_list.Add(pattern[++i]); + else + { + switch (pattern[++i]) + { + case 'n': + ch_list.Add('\n'); + break; + case 't': + ch_list.Add('\t'); + break; + case 'r': + ch_list.Add('\t'); + break; + case 'x': + char ch2; + ch2 = (char)(pattern[i + 1] >= 'A' ? (pattern[i + 1] - 'A' + 10) : pattern[i + 1] - '0'); + ch2 <<= 4; + ch2 |= (char)(pattern[i + 2] >= 'A' ? (pattern[i + 2] - 'A' + 10) : pattern[i + 2] - '0'); + i += 2; + ch_list.Add(ch2); + break; + + default: + build_errors.Add($"{pattern[i]} escape character not found!"); + ch_list.Add(pattern[i]); + break; + } + } + } + else if (i + 2 < pattern.Length && pattern[i + 1] == '-') + { + for (int j = pattern[i]; j <= pattern[i + 2]; j++) + ch_list.Add((char)j); + i += 2; + } + else + ch_list.Add(pattern[i]); + } + var ends_point2 = new transition_node { index = index_count++, transition = new List>() }; + if (inverse) + { + var set = new bool[byte_size]; + var nch_list = new List(); + foreach (var ch2 in ch_list) + set[ch2] = true; + for (int j = 0; j < byte_size; j++) + if (!set[j]) + nch_list.Add((char)j); + ch_list.Clear(); + ch_list = nch_list; + } + foreach (var ch2 in ch_list) + { + cur.transition.Add(new Tuple(ch2, ends_point2)); + } + cur = ends_point2; + nodes.Add(cur); + if (first_valid_stack.Count != 0) + { + second_valid_stack.Push(first_valid_stack.Peek()); + } + first_valid_stack.Push(cur); + break; + + case '.': + var ends_point3 = new transition_node { index = index_count++, transition = new List>() }; + for (int i2 = 0; i2 < byte_size; i2++) + { + cur.transition.Add(new Tuple((char)i2, ends_point3)); + } + cur = ends_point3; + nodes.Add(cur); + if (first_valid_stack.Count != 0) + { + second_valid_stack.Push(first_valid_stack.Peek()); + } + first_valid_stack.Push(cur); + break; + + case '\\': + default: + char ch = pattern[i]; + if (pattern[i] == '\\') + { + i++; + if (@"+-?*|()[].=<>/".Contains(pattern[i])) + ch = pattern[i]; + else + { + switch (pattern[i]) + { + case 'n': + ch = '\n'; + break; + case 't': + ch = '\t'; + break; + case 'r': + ch = '\r'; + break; + case 'x': + ch = (char)(pattern[i + 1] >= 'A' ? (pattern[i + 1] - 'A' + 10) : pattern[i + 1] - '0'); + ch <<= 4; + ch |= (char)(pattern[i + 2] >= 'A' ? (pattern[i + 2] - 'A' + 10) : pattern[i + 2] - '0'); + i += 2; + break; + + default: + build_errors.Add($"{pattern[i]} escape character not found!"); + ch = pattern[i]; + break; + } + + } + } + var etn = new transition_node { index = index_count++, transition = new List>() }; + cur.transition.Add(new Tuple(e_closure, etn)); + cur = etn; + nodes.Add(cur); + if (first_valid_stack.Count != 0) + { + second_valid_stack.Push(first_valid_stack.Peek()); + } + first_valid_stack.Push(cur); + var tn = new transition_node { index = index_count++, transition = new List>() }; + cur.transition.Add(new Tuple(ch, tn)); + cur = tn; + nodes.Add(cur); + if (first_valid_stack.Count != 0) + { + second_valid_stack.Push(first_valid_stack.Peek()); + } + first_valid_stack.Push(cur); + break; + } + } + diagram.count_of_vertex = index_count; + diagram.nodes = nodes; + nodes.Where(x => x.transition.Count == 0).ToList().ForEach(y => y.is_acceptable = true); + return diagram; + } + + /// + /// Diagram to string + /// + /// + /// + private static string print_diagram(diagram d) + { + var builder = new StringBuilder(); + var stack = new Stack(); + var check = new List(d.count_of_vertex); + check.AddRange(Enumerable.Repeat(false, d.count_of_vertex)); + + stack.Push(d.start_node); + + while (stack.Count != 0) + { + var tn = stack.Pop(); + if (check[tn.index]) continue; + check[tn.index] = true; + + builder.Append($"{tn.index.ToString().PadLeft(4)}: "); + foreach (var j in tn.transition) + builder.Append($"({(j.Item1 == 0 ? "null" : j.Item1.ToString())},{j.Item2.index}) "); + if (tn.transition.Count == 0 || tn.is_acceptable == true) + { + if (tn.accept_token_names == null) + builder.Append($"(ACCEPT,{tn.accept_token_name})"); + else + builder.Append($"(ACCEPT,{string.Join(",", tn.accept_token_names)})"); + } + builder.Append('\n'); + + tn.transition.ForEach(x => stack.Push(x.Item2)); + } + + return builder.ToString(); + } + + /// + /// GraphViz.Net(Jamie Dixon), Microsoft.Bcl.Immutable(Microsoft) 누겟 패키지 설치 필요 + /// + /// App.config 파일 수정해야함 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// public class Graph + /// { + /// public static Bitmap ToImage(string str) + /// { + /// var getStartProcessQuery = new GetStartProcessQuery(); + /// var getProcessStartInfoQuery = new GetProcessStartInfoQuery(); + /// var registerLayoutPluginCommand = new RegisterLayoutPluginCommand(getProcessStartInfoQuery, getStartProcessQuery); + /// + /// var wrapper = new GraphGeneration(getStartProcessQuery, + /// getProcessStartInfoQuery, + /// registerLayoutPluginCommand); + /// + /// byte[] output = wrapper.GenerateGraph(str /*"digraph{a -> b; b -> c; c -> a;}"*/, Enums.GraphReturnType.Png); + /// + /// return ByteToImage(output); + /// } + /// + /// + /// private static Bitmap ByteToImage(byte[] blob) + /// { + /// MemoryStream mStream = new MemoryStream(); + /// byte[] pData = blob; + /// mStream.Write(pData, 0, Convert.ToInt32(pData.Length)); + /// Bitmap bm = new Bitmap(mStream, false); + /// mStream.Dispose(); + /// return bm; + /// } + /// + /// } + /// + /// + /// + private static string print_diagram_for_graphviz(diagram d) + { + var builder = new StringBuilder(); + var used = new HashSet(); + + var stack_used = new Stack(); + var check_used = new List(d.count_of_vertex); + check_used.AddRange(Enumerable.Repeat(false, d.count_of_vertex)); + + stack_used.Push(d.start_node); + used.Add(d.start_node.index); + + while (stack_used.Count != 0) + { + var tn = stack_used.Pop(); + if (check_used[tn.index]) continue; + check_used[tn.index] = true; + + used.Add(tn.index); + + tn.transition.ForEach(x => stack_used.Push(x.Item2)); + } + + builder.Append("digraph finite_state_machine {\r\n"); + builder.Append(" rankdir=LR;\r\n"); + builder.Append(" size=\"20,30\"\r\n"); + + // print doublecircle + builder.Append(" node [shape = doublecircle]; "); + foreach (var dd in d.nodes) + if (dd.is_acceptable && used.Contains(dd.index)) + builder.Append(dd.index + "; "); + builder.Append("\r\n"); + + // print point + builder.Append(" node [shape = point]; ss\r\n"); + + // print circle + builder.Append(" node [shape = circle];\r\n"); + + var stack = new Stack(); + var check = new List(d.count_of_vertex); + check.AddRange(Enumerable.Repeat(false, d.count_of_vertex)); + + stack.Push(d.start_node); + builder.Append($" ss -> {d.start_node.index}"); + + while (stack.Count != 0) + { + var tn = stack.Pop(); + if (check[tn.index]) continue; + check[tn.index] = true; + + foreach (var j in tn.transition) + { + string v = ""; + if (j.Item1 == e_closure) + v = "ε"; + else if (j.Item1 == '"') + v = "\""; + else if (j.Item1 == '\n') + v = "\\n"; + else if (j.Item1 == '\r') + v = "\\r"; + else if (j.Item1 == '\t') + v = "\\t"; + else + v = new string(j.Item1, 1); + + builder.Append($@" {tn.index} -> {j.Item2.index} [ label = ""{v}"" ];" + "\r\n"); + } + + tn.transition.ForEach(x => stack.Push(x.Item2)); + } + + builder.Append("}"); + + return builder.ToString(); + } + + /// + /// Get inverse array of diagram nodes + /// + /// + /// + private Dictionary> get_inverse_transtition(diagram dia) + { + var inverse_transition = new Dictionary>(); + var check = new List(dia.count_of_vertex); + check.AddRange(Enumerable.Repeat(false, dia.count_of_vertex)); + + // Build inverse transition map. + var q = new Queue(); + q.Enqueue(dia.start_node); + while (q.Count != 0) + { + var tn = q.Dequeue(); + if (check[tn.index]) continue; + check[tn.index] = true; + + foreach (var j in tn.transition) + if (inverse_transition.ContainsKey(j.Item2.index)) + inverse_transition[j.Item2.index].Add(tn.index); + else + inverse_transition.Add(j.Item2.index, new HashSet() { tn.index }); + + tn.transition.ForEach(x => q.Enqueue(x.Item2)); + } + + return inverse_transition; + } + + /// + /// Delete unnecessary e-closure + /// + /// + /// + private bool opt_nfa(diagram dia) + { + var inverse_transition = get_inverse_transtition(dia); + bool opt = false; + + // Optimize NFA + var q = new Queue(); + var check = new List(dia.count_of_vertex); + check.AddRange(Enumerable.Repeat(false, dia.count_of_vertex)); + q.Enqueue(dia.start_node); + while (q.Count != 0) + { + var tn = q.Dequeue(); + if (check[tn.index]) continue; + check[tn.index] = true; + + // Delete unnecessary e-closure with pull left + if (tn.transition.Count == 1 && tn.transition[0].Item1 == e_closure) + { + var index_left = tn.index; + var index_right = tn.transition[0].Item2.index; + + if (inverse_transition.ContainsKey(index_left)) + foreach (var inv in inverse_transition[index_left]) + for (int i = 0; i < dia.nodes[inv].transition.Count; i++) + { + if (dia.nodes[inv].transition[i].Item2.index == tn.index) + { + dia.nodes[inv].transition[i] = new Tuple(dia.nodes[inv].transition[i].Item1, dia.nodes[index_right]); + opt = true; + } + } + } + + // Delete recursive e-closure + for (int i = 0; i < tn.transition.Count; i++) + if (tn.transition[i].Item1 == e_closure && tn.transition[i].Item2.index == tn.index) + tn.transition.RemoveAt(i--); + + // Merge rounding e-closure + for (int i = 0; i < tn.transition.Count; i++) + if (tn.transition[i].Item1 == e_closure) + for (int j = 0; j < tn.transition[i].Item2.transition.Count; j++) + if (tn.transition[i].Item2.transition[j].Item1 == e_closure && tn.transition[i].Item2.transition[j].Item2.index == tn.index) + { + var index_left = tn.index; + var index_right = tn.transition[i].Item2.index; + + if (tn.transition[i].Item2.is_acceptable) + { + tn.is_acceptable = true; + tn.accept_token_name = tn.transition[i].Item2.accept_token_name; + } + tn.transition[i].Item2.transition.RemoveAt(j--); + tn.transition.AddRange(dia.nodes[index_right].transition); + + foreach (var inv in inverse_transition[index_right]) + for (int k = 0; k < dia.nodes[inv].transition.Count; k++) + if (dia.nodes[inv].transition[k].Item2.index == index_right) + { + dia.nodes[inv].transition[k] = new Tuple(dia.nodes[inv].transition[k].Item1, tn); + } + + tn.transition.RemoveAt(i--); + opt = true; + break; + } + + // Delete unnecessary e-closure with pull right + if (inverse_transition.ContainsKey(tn.index) && inverse_transition[tn.index].Count == 1) + { + var index_left = inverse_transition[tn.index].First(); + var index_right = tn.index; + + for (int i = 0; i < dia.nodes[index_left].transition.Count; i++) + if (dia.nodes[index_left].transition[i].Item2.index == dia.nodes[index_right].index && dia.nodes[index_left].transition[i].Item1 == e_closure) + { + if (dia.nodes[index_left].transition[i].Item2.is_acceptable) + { + dia.nodes[index_left].is_acceptable = true; + dia.nodes[index_left].accept_token_name = dia.nodes[index_left].transition[i].Item2.accept_token_name; + } + dia.nodes[index_left].transition.RemoveAt(i); + dia.nodes[index_left].transition.AddRange(dia.nodes[index_right].transition); + opt = true; + } + } + + tn.transition.ForEach(x => q.Enqueue(x.Item2)); + } + + // Accept Backpropagation + var check2 = new List(dia.count_of_vertex); + check2.AddRange(Enumerable.Repeat(false, dia.count_of_vertex)); + var acc_nodes = new Queue(); + dia.nodes.Where(x => x.is_acceptable).ToList().ForEach(d => acc_nodes.Enqueue(d.index)); + // recalculate inverse transtion + inverse_transition = get_inverse_transtition(dia); + + while (acc_nodes.Count != 0) + { + var top = acc_nodes.Dequeue(); + if (check2[top]) continue; + check2[top] = true; + dia.nodes[top].is_acceptable = true; + if (inverse_transition.ContainsKey(top)) + foreach (var inv in inverse_transition[top]) + if (dia.nodes[inv].transition.Where(x => x.Item2.index == top).First().Item1 == e_closure) + acc_nodes.Enqueue(inv); + } + + return opt; + } + + private string set2str(HashSet hs) + { + var list = hs.ToList(); + list.Sort(); + return string.Join(",", list); + } + + /// + /// NFA to DFA + /// + /// + private diagram nfa2dfa(diagram dia) + { + // tn_name, diagram_index + var transition_node_index = new Dictionary(); + // tn_index, diagram_indexes + var transition_node_set = new Dictionary>(); + // tn_index, (tn_attribute, tn_index) + var transition_node = new Dictionary>(); + int node_count = 0; + + // Create DFA transitions table + var q = new Queue(); // tn_index + q.Enqueue(node_count); + transition_node_index.Add(dia.start_node.index.ToString(), node_count); + transition_node_set.Add(node_count, new HashSet { dia.start_node.index }); + transition_node.Add(node_count++, new Dictionary()); + while (q.Count != 0) + { + var hash = q.Dequeue(); + var hs = transition_node_set[hash]; + + // (tn_attribute, diagram_indexes) + var dic = new Dictionary>(); + var d_q = new Queue>(); // diagram indexes + + + hs.ToList().ForEach(dd => dia.nodes[dd].transition.ForEach( + x => d_q.Enqueue(new Tuple(x.Item1, x.Item2.index)))); + + // ----------- Expand all e-closure ----------- + var check = new List(dia.count_of_vertex); + check.AddRange(Enumerable.Repeat(false, dia.count_of_vertex)); + var e_q = new Queue(); + d_q.ToList().Where(qe => qe.Item1 == e_closure).ToList().ForEach(qee => { e_q.Enqueue(qee.Item2); }); + + foreach (var qe in d_q) + { + if (qe.Item1 == e_closure) + e_q.Enqueue(qe.Item2); + else + check[qe.Item2] = true; + } + + while (e_q.Count != 0) + { + var d = e_q.Dequeue(); + if (check[d]) continue; + check[d] = true; + foreach (var tns in dia.nodes[d].transition) + if (tns.Item1 == e_closure) + e_q.Enqueue(tns.Item2.index); + else + d_q.Enqueue(new Tuple(tns.Item1, tns.Item2.index)); + } + // -------------------------------------------- + + // ----------- Collect transitions ----------- + while (d_q.Count != 0) + { + var dd = d_q.Dequeue(); + if (dd.Item1 == e_closure) continue; + if (dic.ContainsKey(dd.Item1)) + dic[dd.Item1].Add(dd.Item2); + else + dic.Add(dd.Item1, new HashSet { dd.Item2 }); + foreach (var node in dia.nodes[dd.Item2].transition) + if (node.Item1 == e_closure) + dic[dd.Item1].Add(node.Item2.index); + } + + foreach (var p in dic) + { + var hash_string = set2str(p.Value); + if (!transition_node_index.ContainsKey(hash_string)) + { + transition_node_index.Add(hash_string, node_count); + transition_node_set.Add(node_count, p.Value); + transition_node.Add(node_count, new Dictionary()); + q.Enqueue(node_count++); + } + var hash_index = transition_node_index[hash_string]; + transition_node[hash].Add(p.Key, hash_index); + } + // -------------------------------------------- + } + + // Build DFA diagram + var diagram = new diagram(); + var transition_node_list = new List(); + var acc_nodes = new Dictionary(); + + for (int i = 0; i < transition_node.Count; i++) + transition_node_list.Add(new transition_node { index = i, transition = new List>() }); + + dia.nodes.Where(x => x.is_acceptable).ToList().ForEach(d => acc_nodes.Add(d.index, d.accept_token_name)); + + foreach (var p in transition_node) + { + foreach (var ts in p.Value) + transition_node_list[p.Key].transition.Add(new Tuple(ts.Key, transition_node_list[ts.Value])); + foreach (var hh in transition_node_set[p.Key]) + if (acc_nodes.ContainsKey(hh)) + { + transition_node_list[p.Key].is_acceptable = true; + transition_node_list[p.Key].accept_token_name = acc_nodes[hh]; + break; + } + } + + diagram.count_of_vertex = transition_node_list.Count; + diagram.nodes = transition_node_list; + diagram.start_node = transition_node_list[0]; + return diagram; + } + + private string dic2str(SortedDictionary dic) + { + return string.Join(",", dic.ToList().Select(x => $"({x.Key},{x.Value})")); + } + + /// + /// Minimization DFA using Hopcroft Algorithm + /// + /// + /// + private void opt_dfa(diagram dia) + { + var visit = new HashSet(); + var queue = new Queue>(); + + // Enqueue Nodes + var acc_nodes = new List(); + var nacc_nodes = new List(); + foreach (var node in dia.nodes) + if (node.is_acceptable && node.accept_token_names == null) + acc_nodes.Add(node.index); + else + nacc_nodes.Add(node.index); + + queue.Enqueue(acc_nodes); + queue.Enqueue(nacc_nodes); + + var color = new List(); + var color_count = 1; + color.AddRange(Enumerable.Repeat(0, dia.count_of_vertex)); + + acc_nodes.ForEach(x => color[x] = color_count); + color_count = 2; + +#if true // For distingushiable states + var dict_dist = new Dictionary>(); + foreach (var node in dia.nodes) + if (node.is_acceptable && node.accept_token_names != null) + if (dict_dist.ContainsKey(node.accept_token_names[0])) + dict_dist[node.accept_token_names[0]].Add(node.index); + else + dict_dist.Add(node.accept_token_names[0], new List { node.index }); + + foreach (var dist in dict_dist) + { + foreach (var dd in dist.Value) + color[dd] = color_count; + queue.Enqueue(dist.Value); + color_count++; + } +#endif + + while (queue.Count > 0) + { + var front = queue.Dequeue(); + front.Sort(); + var str = string.Join(",", front); + + if (visit.Contains(str)) continue; + visit.Add(str); + + // Collect transition color + var dic = new Dictionary>(); + foreach (var index in front) + { + var node = dia.nodes[index]; + foreach (var ts in node.transition) + { + if (!dic.ContainsKey(node.index)) + dic.Add(node.index, new SortedDictionary()); + dic[node.index].Add(ts.Item1, color[ts.Item2.index]); + } + } + + var list = dic.ToList(); + var group = new Dictionary>(); + for (int i = 0; i < list.Count; i++) + { + var ds = dic2str(list[i].Value); + if (!group.ContainsKey(ds)) + group.Add(ds, new List()); + group[ds].Add(list[i].Key); + } + + foreach (var gi in group) + { + queue.Enqueue(gi.Value); + gi.Value.ForEach(x => color[x] = color_count); + color_count++; + } + } + + var dicc = new Dictionary(); + var inverse_transition = get_inverse_transtition(dia); + for (int i = 0; i < color.Count; i++) + if (!dicc.ContainsKey(color[i])) + dicc.Add(color[i], i); + else if (inverse_transition.ContainsKey(i)) + { + foreach (var inv in inverse_transition[i]) + for (int j = 0; j < dia.nodes[inv].transition.Count; j++) + if (dia.nodes[inv].transition[j].Item2.index == i) + dia.nodes[inv].transition[j] = new Tuple(dia.nodes[inv].transition[j].Item1, dia.nodes[dicc[color[i]]]); + } + } + } + + /// + /// Lexical Analyzer Generator + /// + public class ScannerGenerator + { + bool freeze = false; + List> tokens = new List>(); + SimpleRegex.diagram diagram; + + public string PrintDiagram() + { + if (!freeze) throw new Exception("Retry after generate!"); + return SimpleRegex.PrintDiagram(diagram); + } + + public void PushRule(string token_name, string rule) + { + if (freeze) throw new Exception("You cannot push rule after generate! Please create new scanner-generator instance."); + var sd = new SimpleRegex(rule); + foreach (var node in sd.Diagram.nodes) + if (node.is_acceptable) + node.accept_token_name = token_name; + tokens.Add(new Tuple(token_name, sd.Diagram)); + } + + /// + /// Generate merged DFA using stack. + /// + public void Generate() + { + freeze = true; + + // * Warning * + // + // The merged_diagram index order is in the order of DFA's + // pattern mapping. Consider the PushRule function with this. + + var merged_diagram = get_merged_diagram(); + + // Generated transition nodes for DFA based patttern matching. + var diagram = new SimpleRegex.diagram(); + var nodes = new List(); + var states = new Dictionary(); + var index = new Dictionary(); + var states_count = 0; + + // (diagram_indexes) + var q = new Queue>(); + q.Enqueue(populate(merged_diagram, new List { 0 }, SimpleRegex.e_closure)); + + var t = new SimpleRegex.transition_node { index = states_count++, transition = new List>() }; + states.Add(string.Join(",", q.Peek()), t); + index.Add(t.index, string.Join(",", q.Peek())); + nodes.Add(t); + + while (q.Count != 0) + { + var list = q.Dequeue(); + var list2str = string.Join(",", list); + + var tn = states[list2str]; + + // Append accept tokens. + foreach (var ix in list) + if (merged_diagram.nodes[ix].is_acceptable) + { + tn.is_acceptable = true; + if (tn.accept_token_names == null) + tn.accept_token_names = new List(); + tn.accept_token_names.Add(merged_diagram.nodes[ix].accept_token_name); + } + + var available = available_matches(merged_diagram, list); + + foreach (var pair in available) + { + var populate = pair.Value.ToList(); + var l2s = string.Join(",", populate); + + if (!states.ContainsKey(l2s)) + { + var tnt = new SimpleRegex.transition_node { index = states_count++, transition = new List>() }; + states.Add(l2s, tnt); + index.Add(tnt.index, l2s); + nodes.Add(tnt); + q.Enqueue(populate); + } + + var state = states[l2s]; + tn.transition.Add(new Tuple(pair.Key, state)); + } + } + + diagram.nodes = nodes; + diagram.start_node = nodes[0]; + diagram.count_of_vertex = nodes.Count; + + this.diagram = diagram; + } + + /// + /// Get merged diagram with e-closure + /// + /// + private SimpleRegex.diagram get_merged_diagram() + { + var merged_diagram = new SimpleRegex.diagram { nodes = new List() }; + merged_diagram.nodes.Add(new SimpleRegex.transition_node { index = 0, transition = new List>() }); + merged_diagram.start_node = merged_diagram.nodes[0]; + + // Append diagrams + foreach (var token in tokens) + { + var count = merged_diagram.nodes.Count; + for (int i = 0; i < token.Item2.nodes.Count; i++) + token.Item2.nodes[i].index += count; + merged_diagram.nodes.AddRange(token.Item2.nodes); + merged_diagram.start_node.transition.Add(new Tuple(SimpleRegex.e_closure, merged_diagram.nodes[count])); + } + + merged_diagram.count_of_vertex = merged_diagram.nodes.Count; + return merged_diagram; + } + + /// + /// Populate with next token. + /// + /// + /// + /// + /// + private List populate(SimpleRegex.diagram dia, List diagram_indexes, char match) + { + var result = new List(); + foreach (var index in diagram_indexes) + foreach (var transition in dia.nodes[index].transition) + if (transition.Item1 == match) + result.Add(transition.Item2.index); + return result; + } + + /// + /// Get available next matches. + /// + /// + /// + /// + private Dictionary> available_matches(SimpleRegex.diagram dia, List diagram_indexes) + { + // (match, (diagram_index, transition_index)) + var result = new Dictionary>(); + foreach (var index in diagram_indexes) + for (int i = 0; i < dia.nodes[index].transition.Count; i++) + { + if (!result.ContainsKey(dia.nodes[index].transition[i].Item1)) + result.Add(dia.nodes[index].transition[i].Item1, new List()); + result[dia.nodes[index].transition[i].Item1].Add(dia.nodes[index].transition[i].Item2.index); + } + return result; + } + + public Scanner CreateScannerInstance(string delimiter = "\n\r ") + { + if (!freeze) throw new Exception("Retry after generate!"); + + var table = new int[diagram.count_of_vertex][]; + var accept_table = new string[diagram.count_of_vertex]; + for (int i = 0; i < table.Length; i++) + { + table[i] = new int[SimpleRegex.byte_size]; + for (int j = 0; j < SimpleRegex.byte_size; j++) + table[i][j] = -1; + } + + // Fill transitions + for (int i = 0; i < diagram.nodes.Count; i++) + foreach (var transition in diagram.nodes[i].transition) + table[i][transition.Item1] = transition.Item2.index; + + // Fill accept table + for (int i = 0; i < diagram.nodes.Count; i++) + if (diagram.nodes[i].accept_token_names != null) + accept_table[i] = diagram.nodes[i].accept_token_names[0]; + + return new Scanner(table, accept_table); + } + } + + /// + /// Simple Scanner + /// + public class Scanner + { + int[][] transition_table; + string[] accept_table; + string target; + int pos = 0; + bool err = false; + int latest_pos; + List err_pos; + int current_line; + int current_column; + + public Scanner(int[][] transition_table, string[] accept_table) + { + this.transition_table = transition_table; + this.accept_table = accept_table; + } + + public void AllocateTarget(string literal) + { + target = literal; + pos = 0; + current_line = 0; + current_column = 0; + err_pos = new List(); + err = false; + } + + public bool Valid() + { + return pos < target.Length; + } + + public bool Error() + { + return err; + } + + public int Position { get { return latest_pos; } } + public int Line { get { return current_line; } set { current_line = value; } } + public int Column { get { return current_column; } set { current_column = value; } } + + public Tuple Next() + { + var builder = new StringBuilder(); + var node_pos = 0; + latest_pos = pos; + + int cur_line = current_line; + int cur_column = current_column; + + for (; pos < target.Length; pos++) + { + int next_transition = transition_table[node_pos][target[pos]]; + + switch (next_transition) + { + case -1: + // No-name + if (accept_table[node_pos] == "") + { + // Drop string and initialization + builder.Clear(); + latest_pos = pos; + pos--; + node_pos = 0; + current_column--; + cur_line = current_line; + cur_column = current_column; + continue; + } + if (accept_table[node_pos] == null) + { + err = true; + err_pos.Add(pos); + continue; + } + return new Tuple(accept_table[node_pos], builder.ToString(), cur_line + 1, cur_column + 1); + + default: + if (target[pos] == '\n') { current_line++; current_column = 1; } else current_column++; + builder.Append(target[pos]); + break; + } + + node_pos = next_transition; + } + if (accept_table[node_pos] == null) + throw new Exception($"[SCANNER] Pattern not found! L:{cur_line}, C:{cur_column}, D:'{builder.ToString()}'"); + return new Tuple(accept_table[node_pos], builder.ToString(), cur_line + 1, cur_column + 1); + } + + public Tuple Lookahead() + { + var npos = pos; + var result = Next(); + pos = npos; + return result; + } + + public string ToCSCode(string class_name) + { + var builder = new StringBuilder(); + var indent = ""; + Action up_indent = () => { indent += " "; }; + Action down_indent = () => { if (indent.Length > 0) indent = indent.Substring(4); }; + Action append = (string s) => { builder.Append($"{indent}{s}\r\n"); }; + append("public class " + class_name); + append("{"); + up_indent(); + + /////////////////// + append("int[][] transition_table = new int[][] {"); + up_indent(); + foreach (var gt in transition_table) + append("new int[] {" + string.Join(",", gt.Select(x => x.ToString().PadLeft(4))) + " },"); + down_indent(); + append("};"); + append(""); + + /////////////////// + append("string[] accept_table = new string[] {"); + up_indent(); + append(string.Join(",", accept_table.Select(x => x != null ? $"\"{x.ToString().PadLeft(4)}\"" : "null"))); + down_indent(); + append("};"); + append(""); + + down_indent(); + append("}"); + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/InhaCC/packages.config b/InhaCC/packages.config new file mode 100644 index 0000000..16f5146 --- /dev/null +++ b/InhaCC/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file