言語解析

言語解析の流れ

言語解析は、大まかに以下の流れになります。

  1. 字句解析
  2. 構文解析
  3. 意味解析
  4. コード生成
  5. コード解析実行

 

字句解析

字句解析は、プログラムやスクリプトを「意味のある最小単位の文字列」に分解することです。
この最小単位をトークンといいます。
 
たとえば、以下のNScripterスクリプトを読み込むとします。

; 定義ラベル
*define

game
; 実行開始ラベル
*start
こんにちは@
wave "se.wav"
さようなら\

end

 
これを字句解析するとこんな感じです。

*
define
game
*
start
こんにちは
@
wave
se.wav
さようなら
\
end

スクリプトの実行に影響のないコメントは、この段階で削除しています。
 
単純に、

  • スペース
  • タブ
  • 改行

で分解していけばOKです。
ただ、改行が特別な意味を持つ場合は、トークンとして残しておく必要があります。
NScripterは、行頭に*があると後ろの文字列をラベルとして扱うので、
特殊な文字列に置き換えたり、行番号・列番号を持たせておくのもありかもしれません。
 
あと、たいていの言語は””で囲まれていると、
文字列として扱うので、この段階で切り出してしまいます。
 
 

構文解析

構文解析では、字句解析でバラバラに分解したトークンを、
扱いやすいようにまとめることです。
 
まとめ方としては、

にまとめます。
 
例えば、NScripterの、

if %0=%1 gosub *eq

というスクリプトは、

gosub *eq …(1)

が「文」になります。(正確にはgosub文)
 
また、

%0=%1 …(2)

のように演算を行うまとまりを「式」と呼びます。
 
そして、このスクリプト全体が「if文」となります。
 
つまり、
if文に(1)文と(2)式がぶら下がっている
ような構造になります。
 
 
プログラム的には、

#include <string>
#include <vector>

/**
 * 構文木ノードクラス
 */
class CTree {
private:
	string m_value;              // 値
	vector<CTree*> m_childNodes; // 子ノード
public:
	// コンストラクタ
	CTree(string value)
	{
		SetValue(value);
	}
	// 値の設定
	void SetValue(string value)
	{
		m_value = value;
	}
	// 子ノードの追加
	void AppendChild(CTree *tree)
	{
		m_childNodes.push_back(tree);
	}
};

というクラスを定義しておきます。
 
 
構文木クラスを使って先ほどのスクリプト構文解析すると、
こんな感じになります。

	// if文
	CTree *treeIf = new CTree("IfStatement");
	// 式
	CTree *treeExp = new CTree("Expression");
	CTree *treeParam0 = new CTree("%0");
	treeExp.AppendChild(treeParam0);
	CTree *treeEq = new CTree("=");
	treeExp.AppendChild(treeEq);
	CTree *treeParam1 = new CTree("%1");
	treeExp.AppendChild(treeParam1);
	treeIf.AppendChild(treeExp);
	// 文
	CTree *treeGosub = new CTree("GosubStatement");
	CTree *treeLavel = new Ctree("eq");
	treeGosub.AppendChild(treeLavel);
	treeIf.AppendChild(treeGosub);

(つづく)