LoginSignup
0
0

More than 5 years have passed since last update.

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

Posted at

パーサ初心者、かつ PEG 初心者だけども必要に狩られてパーサを書いてみる。
少しずつ更新していきます。

SystemVerilog の規格書

とりあえず正確な規格書がなければと思い、IEEE 1800-2012 から規格書を入手。1300ページ以上あるが、文法に関しては100ページ無いくらい。

Parslet のインストール

gem でインストール出来るので、インストールはらくちん

gem install parslet

2進数リテラルの Parse

とりあえず練習がてらに Verilog の 2進数リテラルをパース出来るものを考える。
Verilog の 数値リテラルは以下の用に [bitwidth]'[s|S][base]数値 となっていて、
s|Sは signed, base は 2 進数なら b|Bとなる。また数値は桁数を
数えやすいように 0000_1111 の用にアンダースコアを混ぜて良い。

wire a = 1'b1; // 1bit
wire [1:0] b = 2'b10; // 2bit で 2
wire [7:0] c = 8'b1111_0000;

パーサ実装

とりあえず Parslet の Getting Started を参考にしつつパーサを書いてみる。
というものの、ほとんど規格書の定義どおり。

require 'parslet'

class VBinaryParser < Parslet::Parser
  rule(:nz_dec_digit) { match('[1-9]') }

  rule(:dec_digit) {
    str('0') | nz_dec_digit
  }

  rule(:nz_us) {
    nz_dec_digit >> (str('_') | dec_digit).repeat
  }

  rule(:binary_digit) { match('[01]') }

  rule(:binary_base) {
    str('\'') >> (match('[sS]').maybe >> match('[bB]').as(:base))
  }

  rule(:binary_val) {
    binary_digit >> (str('_') | binary_digit).repeat
  }

  rule(:binary_num) {
    nz_us.maybe.as(:size) >> binary_base >> binary_val.as(:value)
  }

  root(:binary_num)
end

# test
require 'pp'
parser = VBinaryParser.new

pattern = [
  "'b1", 
  "1'b1",
  "2'b10",
  "8'b1111_0000",
  "2'b2"
]

pattern.each do |str|
  pp parser.parse(str)
end

実行結果

$ ruby v_bin.rb                                                  (git)-[master]
{:size=>nil, :base=>"b"@1, :value=>"1"@2}
{:size=>"1"@0, :base=>"b"@2, :value=>"1"@3}
{:size=>"2"@0, :base=>"b"@2, :value=>"10"@3}
{:size=>"8"@0, :base=>"b"@2, :value=>"1111_0000"@3}
/usr/lib/ruby/gems/2.3.0/gems/parslet-1.7.1/lib/parslet/cause.rb:70:in `raise': Failed to match sequence (size:(NZ_US?) BINARY_BASE value:BINARY_VAL) at line 1 char 4. (Parslet::ParseFailed)
        from /usr/lib/ruby/gems/2.3.0/gems/parslet-1.7.1/lib/parslet/atoms/base.rb:49:in `parse'
        from v_bin.rb:44:in `block in <main>'
        from v_bin.rb:43:in `each'
        from v_bin.rb:43:in `<main>'

最初の3つのケースは問題無くパースできているようだし、最後のパターンは正しくエラーと
認識されている。大丈夫な様子。

8進数, 10進数, 16進数についても同様に出来るだろう。ただ、コードが冗長になるので
うまく記述出来る方法を考えねば…

0
0
0

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
0