コンパイラの設計の基本。 C#の字句解析

字句解析のタスクは、入力シーケンス(私の場合はPascalコード)を単語とトークンに分割することです。



まず、データを保存するための5つのタイプ付きシート、つまり、識別子、定数、キーワード、区切り文字、および畳み込みを作成しました。 区切り文字の配列も必要です。

static char[] limiters = {',', '.', '(', ')', '[', ']', ':', ';', '+', '-', '*', '/', '<', '>', '@'};
      
      





およびキーワードの配列。 この記事はC#でのPascal言語の字句解析の実装の最初の例として書かれたため、11個のキーワードに限定しました。

したがって、キーワードの配列:

 static string[] reservedWords = { "program", "var", "real", "integer", "begin", "for", "downto", "do", "begin", "end", "writeln" };
      
      





入力では、path変数にプログラムがある.txtファイルへのパスがあります。 AllTextProgramのファイルにすべてを読み込み、開始します。

 StreamReader sr = new StreamReader(path, Encoding.Default); AllTextProgram = sr.ReadToEnd(); sr.Close();
      
      





forループでは、AllTextProgramから各文字を順番に取得し、Analysisメソッドに送信します。 それとは別に、2つの例外のみを考慮します。 最初はアポストロフィです。これは、私の場合、キーワード「writeln」を使用したときに発生します。 アポストロフィが発生した場合、ルールから離れて、2番目のアポストロフィが見つかるまでAllTextPorgram変数を調べます。 2番目は、プログラム内のコメントです。 後者については、中括弧の開始と終了の数を単純に数え、差がゼロになるまで待ちます。

後続のアポストロフィが見つからない場合、または中括弧の数が0でない場合、プログラムはサイクルで実行されるように見えます。はい、これは事実です。 構文解析の段階でこれらの条件を体系化することにしました。

 for (i = 0; i < AllTextProgram.Length; i++) { char c = AllTextProgram[i]; if (AllTextProgram[i] == '\'') { temp += '\''; i++; while (AllTextProgram[i] != '\'') { temp += AllTextProgram[i]; i++; } temp += '\''; type = 2; Result(temp); temp = null; } if (AllTextProgram[i] == '{') { int chet = 1; while (chet != 0) { i++; if (AllTextProgram[i] == '{') chet++; if (AllTextProgram[i] == '}') chet--; } } Analysis(AllTextProgram[i]); }
      
      





型変数は、トークンが属するテーブルの番号を担当します。

識別子-表1

定数-表2

キーワード-表3

セパレーター-表4



次に、分析メソッドで何が起こるかについて説明します。 これは、コードコメントとして行うのが最適です。 すぐに、セパレーターに達するまで、作業の中間結果を一時変数に格納することに注意してください。 区切り記号に到達したら、現在のトークンが識別子、定数、またはキーワードに属するかどうかを判断するために、temp変数をResultメソッドに送信する必要があります。

 static void Analysis(char nextChar) { int acsiiCode = (int)nextChar; //    //              if (((acsiiCode >= 65) && (acsiiCode <= 90)) || ((acsiiCode >= 97) && (acsiiCode <= 122)) || (acsiiCode == 95) { if (temp == null) type = 1; temp += nextChar; return; } //           if (((acsiiCode >= 48) && (acsiiCode <= 57)) || (acsiiCode == 46)) { //  ,       ,    temp //  .   temp ,   //    ,     -  if (acsiiCode == 46) { int out_r; if (!int.TryParse(temp, out out_r)) goto not_the_number; } if (temp == null) type = 2; temp += nextChar; return; } not_the_number: if ((nextChar == ' ' || nextChar == '\n') && temp != null) { Result(temp); temp = null; return; } //        .      «:=»    . foreach (char c in limiters) { if (nextChar == c) { if (temp != null) Result(temp); type = 3; if (nextChar == ':' && AllTextProgram[i+1] == '=') { temp = nextChar.ToString() + AllTextProgram[i + 1]; Result(temp); temp = null; return; } if (nextChar == '<' && (AllTextProgram[i + 1] == '>' || AllTextProgram[i + 1] == '=')) { temp = nextChar.ToString() + AllTextProgram[i + 1]; Result(temp); temp = null; return; } if (nextChar == '>' && AllTextProgram[i + 1] == '=') { temp = nextChar.ToString() + AllTextProgram[i + 1]; Result(temp); temp = null; return; } temp = nextChar.ToString(); Result(temp); temp = null; return; } }
      
      





最後に考慮するのはResultメソッドです。ここでは、見つかったものを判別します。 最初に、キーワードに属する一時変数をチェックします。 見つかったトークンと識別子を混同しないように、最初に確認します。 結果がキーワードではない場合、スイッチを介して、事前に分析メソッドで定義されているテーブルタイプを確認します。 見つかった識別子/定数/セパレーターがまだテーブルに表示されていないことを確認することを忘れないでください。

 static void Result(string temp) { for (int j = 0; j < reservedWords.Length; j++) { if (temp == reservedWords[j]) { for (int i = 0; i < tableR.Count; i++) { if (temp == tableR[i]) { LConv.Add("3" + i); return; } } tableR.Add(temp); LConv.Add("3" + (tableR.Count - 1)); return; } } switch (type) { case 1: for (int j = 0; j < tableI.Count; j++) { if (temp == tableI[j]) { LConv.Add("1" + j); return; } } tableI.Add(temp); LConv.Add("1" + (tableI.Count - 1)); break; case 2: for (int j = 0; j < tableC.Count; j++) { if (temp == tableC[j]) { LConv.Add("2" + j); return; } } tableC.Add(temp); LConv.Add("2" + (tableC.Count - 1)); break; case 3: for (int j = 0; j < tableL.Count; j++) { if (temp == tableL[j]) { LConv.Add("4" + j); return; } } tableL.Add(temp); LConv.Add("4" + (tableL.Count - 1)); break; } }
      
      





そして、それがすべてどのように機能するかを示す簡単な例です。

プログラムの入力時:



program main; { pro{g}am }

var sum: real;

f, per_1, x_1,i:integer;

begin

{sum:=(-x_1+2.5)*4 - (x_1-6)*((((x_1+2))));}

x_1:=18;

f:= 456;

for i:=10 downto per_1-f+i*(x_1+1) do

begin

per_1 := per_1 + x_1*sum*f;

x_1:=-x_1-1;

f:=(f+1)*(x_1-24700);

sum:=(x_1+2.5)*4 - (x_1-6)*(x_1+2);

end;

writeln( 'summa = ' , sum);

end.








そして仕事の結果:










All Articles