正規表現を解析して有限状態オートマトンを構築する再帰アルゴリズムを書いています。オートマトンは式を繰り返し処理し、文字をスタックにプッシュし、演算子を「演算子スタック」にプッシュします。「(」(グループ化操作を示す)に遭遇すると、「サブオートマトン」をスタックにプッシュし、パターンの残りをサブオートマトンに渡して解析します。そのオートマトンが「)」に遭遇すると、残りの部分を渡します。解析を終了する親オートマトンまでの文字列。コードは次のとおりです。
var NFA = function(par) {
this.stack = [];
this.op_stack = [];
this.parent = par;
};
NFA.prototype.parse = function(pattern) {
var done = false;
for(var i in pattern) {
if (done === true) {
break;
}
switch(pattern.charAt(i)) {
case "(":
var sub_nfa = new NFA(this);
this.stack.push(sub_nfa);
sub_nfa.parse(pattern.substring(i+1, pattern.length));
done = true;
break;
case ")":
if (this.parent !== null) {
var len = pattern.length;
/*TROUBLE SPOT*/
this.parent.parse(pattern.substring(i, pattern.length));
done = true;
break;
}
case "*":
this.op_stack.push(operator.KLEENE);
break;
case "|":
this.op_stack.push(operator.UNION);
break;
default:
if(this.stack.length > 0) {
//only push concat after we see at least one symbol
this.op_stack.push(operator.CONCAT);
}
this.stack.push(pattern.charAt(i));
}
}
};
「TROUBLESPOT」とマークされた領域に注意してください。正規表現"(a | b)a"が与えられた場合、this.parent.parseの呼び出しは、サブオートマトンが ")"に遭遇したときに1回だけ呼び出されます。この時点で、pattern.substring(i、pattern.length)= ")a"です。これは「機能」しますが、文字列を親オートマトンに渡す前に「)」入力を消費する必要があるため、正しくありません。ただし、this.parent.parse(pattern.substring(i + 1、pattern.length))の呼び出しを変更すると、parseは空の文字列を渡されます!コードをステップ実行しようとしましたが、この動作を説明できません。私は行方不明ですか?
Juanの提案で、このアルゴリズムで「(a | b)a」を解析しようとしたときの問題を示すために、簡単なjsfiddleを作成しました。「)」の場合、空のdivにiインデックスのサブストリングとi+1インデックスのサブストリングが入力されます。iの部分文字列には2文字ありますが、i+1の部分文字列は空の文字列であることを示しています。リンクは次のとおりです:http://jsfiddle.net/XC6QM/1/
編集:charAt(i)を使用してもアルゴリズムの動作が変わらないという事実を反映するために、この質問を編集しました。