注釈付きでマークされた文字列の解析 #3

リテラル文字トークン':'を非終端記号char_except_parenthesisに還元するときに、
わざわざアクション$$ = ':';を実行しないといけないのは何となく無駄な感じがするかもしれない。
トークンの種類を定めるトークン値とトークンの持つ意味を定める意味値は異なるものなので、
もちろん実際には無駄どころか必要なものである。
しかし、リテラル文字トークンをトークン値として使っていると、
直感的にはその意味値もトークンの文字コードに一致しているように感じてしまうのだ。
これがCHAR_EXCEPT_SPECIALだと文字集合の名前であって、
実際にyylexから戻ってくる具体的な文字のもつ意味値は別にあると感じることができるのだが。
そこで、

...snip
char_except_parenthesis :
      CHAR_EXCEPT_SPECIAL
    | ':'
    ;
...snip
int yylex(void)
{
    int c = getchar();
    yylval = c;
    if (c == '(' || c == ')' || c == ':') return c;
    if (c == EOF) return 0;
    return CHAR_EXCEPT_SPECIAL;
}
...snip

のように変更する。
関数yylexからトークン値を返すときに、
これまでCHAR_EXCEPT_SPECIALの場合だけ、その意味値として文字コードをyylvalに代入していたのだが、
yylval = c;を前へ移動して、全ての文字についてその文字コードを意味値とするようにした。
これで、'('')'':'の意味値はそれぞれの文字コードに一致する。
これにより、':'を還元する時の意味値を決めるアクションは書かなくて済む。

$ echo -n "(x::):" | ./parser
marked-string [x]: annotation [:]
string [:]

ただ、括弧の場合には意味値は必要ないのでその点ではこの書き方は別の無駄を作っているといえるかもしれない。