FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

FIRRTLの調査(1. ANTLR4のパーサを使ってC++版パーサの作成に挑戦)

以前、少しだけFIRRTLのパーサをC++に移植するプロジェクトを進めていたのだが、そのあと辞めてしまっていたのだった。 ただ、FIRRTLをC++で再実装するプロジェクトは自分の向学のためにも役に立ちそうなので進めていきたい (本当はRustでやりたかったのだけれどパーサコンビネータを評価している段階ですでに時間を食ってしまっていたのでやめた)。

FIRRTLの文法自体はANTLR4を使って記述されており、非常に分かりやすく書いてある。ANTLR4は様々な言語にパーサを出力することが可能なので、ScalaはもちろんC++への出力も対応している。

github.com

この文法ファイルからC++の形式にパーサを出力すること自体は可能なのだが、少しテストコードを動かしてみると上手くParseしてくれない。

$ antlr4 -Dlanguage=Cpp FIRRTL.g4
$ g++ -o firrtl_cpp ../src/main.cpp ../antlr4/FIRRTLLexer.cpp ../antlr4/FIRRTLParser.cpp -I../antlr4 -I../runtime/runtime/src ../runtime/dist/libantlr4-runtime.a
  • simple_circuit.fir
circuit SimpleCircuit :
  module SimpleCircuit :
    output io : {flip in : UInt<32>, out : UInt<32>}

    io.out <= io.in
$ ./firrtl_cpp ../tests/simple_circuit.fir
line 1:23 mismatched input '\n  ' expecting {FileInfo, INDENT}
(circuit circuit (id SimpleCircuit) : \n   module SimpleCircuit : \n     output io : { flip in : UInt < 32 > out : UInt < 32 > } \n \n     io . out <= io . in \n)

何故改行がこんなに嫌われるのか理解できていなかったのだが、最終的にFIRRTL.g4に手を加えた。

  • INDENT, DEDENT を削除
 module
-  : 'module' id ':' info? INDENT port* moduleBlock DEDENT
-  | 'extmodule' id ':' info? INDENT port* defname? parameter* DEDENT
+  : 'module' id ':' info? port* moduleBlock
+  | 'extmodule' id ':' info? port* defname? parameter*
   ;
  • NEWLINEを削除、WHITESPACEのスキップを確保
+// NEWLINE
+//     :'\r'? '\n' ' '*
+//     ;
+
+WS : [\t\n, ]+ -> skip ; // skip spaces, tabs, newlines

少しびっくりしたのは、FIRRTLは,コンマを区切り用の文字として使用していることだ。スキップする文字を\t\n,に設定する。

これでパーサの再生成を行ってテストを行った。

$ ./firrtl_cpp ../tests/simple_circuit.fir
(circuit circuit (id SimpleCircuit) : (module module (id SimpleCircuit) : (port (dir output) (id io) : (type { (field flip (fieldId in) : (type UInt < (intLit 32) >)) (field (fieldId out) : (type UInt < (intLit 32) >)) })) (moduleBlock (simple_stmt (stmt (exp (exp (id io)) . (fieldId out)) <= (exp (exp (id io)) . (fieldId in)))))) <EOF>)

問題なく生成できたようだ。次は内部の実装を進めていく。