kmyacc version4.1.4を使ってjavascriptで動くパーサを書いてて、それについての話です
http://sorrex.googlecode.com/svn-history/r48/trunk/js/parse.html
に
。水平。
:背景
青
。左
ここも青で表示されてしまう
を食わせると
<div id="root"> <hr style="background-color:#0095d9;" /> <div style="background-color:#0095d9;" align="left"> <span class="line" id="5">ここも青で表示されてしまう</span> </div> </div>
が出力される
hrがbackgound-colorを持つのは正しいが、divが持つのはおかしい
http://sorrex.googlecode.com/svn-history/r48/trunk/js/sorrex.parser.jsy
がソース
novalue :prefix IDENTIFIER NOVALUE property_expression { var t = new Node($2[1], $1); t.startline = $2[2]; t.endline = $2[2]; if($4){ t.addProperty($4); } $$ = t; } ;
の後に
oneline :prefix IDENTIFIER property_expression tag { var t = new Node($2[1], $1); t.startline = $2[2]; t.endline = $4[2]; if($3){ t.addProperty($3); } t.setChild([$4]); $$ = t; } ;
があるときに発生することがある、というのが見つけたときに気付いたこと
とりあえず
if($4){ t.addProperty($4); $4 = null; }
としてみたらこの現象は発生しなくなった
http://sorrex.googlecode.com/svn-history/r50/trunk/js/parse.html
<div id="root"> <hr style="background-color:#0095d9;" /> <div align="left"> <span class="line" id="5">ここも青で表示されてしまう</span> </div> </div>
hrだけがbackground-colorを持っているので正しい
これでOK、としたかったが、空であるかもしれない要素について全てこんなことを考慮しないといけないんでは大変すぎる
ソースを見渡してもこのバグの原因になりそうな箇所はなかった
パーサ生成の定義部分が問題なのかなーと前述のyaccな定義のところを見てるうちに
novalue | prefix | IDENTIFIER | NOVALUE | property_expression | |
oneline | prefix | IDENTIFIER | property_expression | tag |
となるのが原因じゃないかと思いついた
http://sorrex.googlecode.com/svn-history/r31/trunk/js/kmyacc.js.parser
パーサのテンプレートの
/* Following line will be replaced by reduce actions */ switch(yyn){ $reduce case %n: { %b } break; $endreduce }
の%bの部分に前述のyaccな定義の部分が一部の文字列を置換されて展開される
例えばnovalueだと
var t = new Node(yyastk[yysp-(4-2)][1], yyastk[yysp-(4-1)]); t.startline = yyastk[yysp-(4-2)][2]; t.endline = yyastk[yysp-(4-2)][2]; if(yyastk[yysp-(4-4)]){ t.addProperty(yyastk[yysp-(4-4)]); yyastk[yysp-(4-4)] = null; } yyval = t;
のようになる
問題はその後の
/* Goto - shift nonterminal */ yysp -= yyl; yyn = yylhs[yyn]; if((yyp = yygbase[yyn] + yysstk[yysp]) >= 0 && yyp < YYGLAST && yygcheck[yyp] == yyn){ yystate = yygoto[yyp]; }else{ yystate = yygdefault[yyn]; } yysp++; yysstk[yysp] = yystate; yyastk[yysp] = yyval;
の部分
yysp -= yyl;
末尾を指してたyyspが現在の位置 - 1に戻された後、
yysp++;
現在の位置を指して
yysstk[yysp] = yystate; yyastk[yysp] = yyval;
yysstk、yyastkそれぞれに現在の値を入れている(yyvalはnovalueとかonelineとかでnewしたTag)
ここで、配列の現在以降の内容が消えてないので、結果
novalue | prefix | IDENTIFIER | NOVALUE | property_expression | |
oneline | prefix | IDENTIFIER | property_expression | tag | |
↑が重なる |
となって、過去のプロパティが渡されてしまう
これが今回の問題の原因
パーサのテンプレートを
yysp++; yysstk.length = yysp;//配列の現在以後を初期化 yyastk.length = yysp;//配列の現在以後を初期化 yysstk[yysp] = yystate; yyastk[yysp] = yyval;
のように修正するのが正しいのか
もしくは
property_expression : //ここに処理を入れる? |properties { $$ = $1; } ;
のが正しいのか
ちょっと判断つかなかったので作者様に問い合わせ中
解決しました。
後者の方が正しい