1

verilogのような言語(またはより具体的には、Verilogコードを生成するために使用されるいくつかの追加の構造を持つverilog)のflex / bisonを使用してパーサーを作成しようとしていますが、処理するbisonルールを構築する方法を理解するのに問題があります言語で使用可能な一般的なタイプの構造。

この構成の一般的な例は次のとおりです。

(
output wire          up_o,
input  wire [4:0]    up_i,
///////////////////////////////////////////////////////////
// A comment
///////////////////////////////////////////////////////////
`my_if (`$a eq `$b)
  input  wire        a_{n}_clk_i,
  output wire        a_{n}_rst_o,
`my_endif
output wire                                           out_o
);

原則として、my_ifはネストする( );ことができ、すべての中にはmy_if、なしでブロック内に含めることができますmy_if。これは、私の現在の考えに欠陥があり、この相互再帰を行う方法を単に理解していないと私に信じさせます。

私に問題を引き起こしているように見えるのは,、最後の行を除いて、ブロック内のすべての行を終了しているということです。私のレクサーはコメントを無視し、行を細かくトークン化します。また、my_if行を特別に解析します。

以下は私が今持っている関連するバイソンのルールです、

portdecla:
'(' ports ')' ';'
;

ports:
port
| ports ',' port
| ports ',' ifs
| ifs
;

ifs:
ifhead ports TENDIF
| if
;

ifheadおよびは、および入力/出力行の罰金をport解析します。my_if

これらのルールを作成する方法についてのアイデアは非常にありがたいです

4

1 に答える 1

3

あなたの質問を理解するのに少し苦労しました。問題を説明しておけば、コンマと関係があるかもしれないと漠然と主張するよりもはるかに簡単だったでしょう。もちろん、それはコンマと関係があります:あなたの文法は、後と後にコンマがあると主張しifsますport。(また、正しくコピーアンドペーストしなかったか、何か本当に奇妙なことがありifsます。)

実際には、は必要ありませんが、宣言の最後にあるとifsを区別する必要があります。ifif

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); }
         ;
于 2012-11-12T16:09:01.083 に答える