LoginSignup
0

More than 5 years have passed since last update.

Parslet で SystemVerilog のパーサを作るまでの道のり (2)

Posted at

前回は2進数リテラルのパーサを書いた。
今回はそれを拡張して整数型をパースするパーサを書いてみる。

8進型と16進型

この2種類については 2 進数と digit の種類及び base 識別子が変わるのみで
ほかは変わりが無い。そこで、2進数の定義部分を関数にして一括で定義してみる。

# 前回見落としていた、不定値とHi-Zのリテラル
rule(:x_digit){ match('[xX]') }
rule(:z_digit){ match('[zZ?]') }
rule(:underscore){ str('_') }

rule(:nz_dec_digit){ match('[0-9]') }
rule(:dec_digit) { str('0') | nz_dec_digit }
rule(:nz_uint) { nz_dec_digit >> (underscore | dec_digit).repeat }

# よく使うルールを先に定義
rule(:size?) { nz_uint.maybe.as(:size) }
rule(:signed_base) { match('[sS]') }
rule(:quote) { str('\'') }

# 2, 8, 16 進数のルールを定義する関数
def self.num_rule(define)
  define.each do |d|
    rule("#{d[:name]}_digit") { x_digit | z_digit | match(d[:digit]) }
    rule("#{d[:name]}_val") {
      eval("#{d[:name]}_digit") >> (underscore | eval("#{d[:name]}_digit")).repeat
    }
    rule("#{d[:name]}_base") {
      signed_base.maybe >> match(d[:base])
    }
    rule("#{d[:name]}_number") {
      size? >> quote >>
        self.instance_eval("#{d[:name]}_base").as(:base) >>
        self.instance_eval("#{d[:name]}_val").as(:value)
    }
  end
end

途中で定義したルールを呼び出す際に eval している。digit 部分を rule で定義せずに

digits = x_digit | z_digit | match(d[:digit])

と出来ないかと試してみたが、x_digitundefined local variableと怒らたために上記のように
定義した。

10進数のルール

Verilog で 10進数の定義は2種類あり、

10'd1023; //10bitの整数 1023
1023;     //32bitの整数 1023
4'dx__;   //4bitの不定値
4'dz_;    //4bitのHi-Z

なので、bit幅とbase指示子の無い場合も考慮する必要がある。また、数値部分も10進数では
数値と x, z の文字が混ざる事が無い。とりあえず以下の用に定義。
どうやら rule ブロックの中では代入演算が可能なようだ。

rule(:unsigned_number){ dec_digit >> (underscore | dec_digit).repeat }
rule(:decimal_base) { signed_base.maybe >> match('[dD]') }
rule(:decimal_number) {
  dec_number = unsigned_number.as(:value)
  dec_number = dec_number | size? >> quote >> decimal_base >> unsigned_number.as(:value)
  dec_number = dec_number | size? >> quote >> decimal_base >>
                            x_digit.as(:value) >> underscore.repeat
  dec_number = dec_number | size? >> quote >> decimal_base >> z_digit >> underscore.repeat
  dec_number
}

整数型のルールをまとめる

これで出来た2,8,10,16進を一つの :integral_numberとしてルールにする。

rule(:integral_number){
  hex_number | decimal_number | octal_number | binary_number
}

今回作った整数パーサ

今回作った Verilog の整数パーサのソースを gistに載せておく。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0