あなたの質問を理解するのに少し苦労しました。問題を説明しておけば、コンマと関係があるかもしれないと漠然と主張するよりもはるかに簡単だったでしょう。もちろん、それはコンマと関係があります:あなたの文法は、後と後にコンマがあると主張しifs
ますport
。(また、正しくコピーアンドペーストしなかったか、何か本当に奇妙なことがありifs
ます。)
実際には、は必要ありませんが、宣言の最後にあるとifs
を区別する必要があります。if
if
port_declaration: tail_ports;
ports: port ','
| port ',' ports
| if
| if ports
;
tail_ports
: port
| tail_if
| port ',' tail_ports
| if tail_ports
;
if : IF '(' condition ')' ports END_IF
;
tail_if
: IF '(' condition ')' tail_ports END_IF
;
(私はそれを右再帰的に書いたので、理解しやすく、IMHOであり、右再帰リストからASTを構築するのも簡単です。Levine、p。66を参照してください。最近のマシンでは、解析スタックに実際には問題はありません。大きくなります。デフォルトの最大スタック深度は10,000です。それでも不十分な場合は、増やすことができます。)
編集:私はついに上記の権利を得たと思います。
この種の問題は通常、2つのパスで解析することで解決されます。最初に前処理パスがあり、その出力は実際のパーサーに送られます。トークン化は1回だけ行われます。前処理パーサーは言語トークンを区別しません。それはそれ自身のトークンだけを気にします。(Cプリプロセッサはトークンを演算子と融合でき##
ますが、トークンを分割することはありません。これは一般的に合理的なアプローチであることがわかります。)データフローを正しく取得するのは少し難しいですが、これはbisonで実装できます。私は個人的lemon
に、レクサーがトークンをパーサーにプッシュするアプローチを好みます。これは、拡張がはるかに簡単だからです。
ただし、特定のケースでは、プリプロセッサが完全なフレーズ(port
)のみを受け入れるようにしたいので、あなたが言うよう,
に、最後port
が構造の奥深くに埋もれている場合でも、問題はsを正しく取得することif
です。if
したがって、とを区別する上記のアプローチtail_if
。
解析時に条件を評価できるかどうかに応じて、ASTを構築するためのいくつかのアプローチがあります。可能であれば、不要なポートを完全に除外することができます。これがおそらく最も簡単な解決策です。
条件を適用する前に解析全体を実行する必要がある場合でも、いくつかのオプションがあります。ノードがまたはのいずれport
かであるツリーを構築するという明白なオプションconditional_list
、または操作のベクトルを効果的に構築するコード生成スタイルです。build-port
およびjump-if-condition-false
。これらのオプションは両方とも、何らかの形式のタグ付き共用体を必要とします。
/編集(以下はまだ正しくありません。)
typedef struct PortList {
Port* port;
struct PortList* next;
} PortList;
typedef struct ConditionalPortList {
Condition* cond; /* Optional condition; if absent, unconditional */
PortList* ports;
} ConditionalPortList;
typedef struct PortDeclaration {
ConditionalPortList* clause;
struct PortDeclaration *next;
} PortDeclaration;
これは簡単に作成できます(このスニペットよりも簡単かもしれませんが、私は少し練習していません)(警告:未試行):
%union {
PortList* port_list;
ConditionalPortList* conditional;
PortDeclaration* port_declaration;
/* ... */
}
%type <port_list> ports
%type <conditional> if always
%type <port_declaration> port_tail port_declaration
/* ... */
%%
ports: port { $$ = NewPortList($1, NULL); }
| port ',' ports { $$ = NewPortList($1, $3); }
;
if: IF '(' condition ')' ports ENDIF
{ $$ = NewConditionalPortList($3, $5); }
;
always: ports { $$ = NewConditionalPortList(NULL, $1); }
;
port_tail: if
| if port_tail { $$ = NewPortDeclaration($1, $2); }
| if always port_tail
{ $$ = NewPortDeclaration($1, NewPortDeclaration($2, $3); }
;
port_declaration:
'(' always ')' ';' { $$ = $2; }
| '(' port_tail ')' ';' { $$ = $2; }
| '(' always port_tail ')' ';'
{ $$ = NewPortDeclaration($2, $3); }
;