人気ブログランキング | 話題のタグを見る

PEG コンパイラ・コンパイラ pegc.rb

連休の時間をもてあましたので、peg.rb を改造して、PEG コンパイラ・コンパイラ pegc.rb を作ってみた。使い方は、おおむね次のようになる。サンプルプログラムは次の記事に投稿する。

1. ソースリストの作り方

require 'pegc.rb' # コンパイラコンパイラのインクルード

parser = Peg.new # コンパイラオブジェクトの作成

parser.mkhead('Parse') # オブジェクトプログラムのヘッダー部分の作成、引数はパーサのクラス名

(文法の定義)

parser.mkend # パーサクラスの定義の終わり

以上がソースリストの記述になる。このリストを仮に foo.rb とすると、コンソールから

> ruby foo.rb

と入力すると、コンパイルの結果が画面に表示される。リダイレクトで、

> ruby foo.rb > bar.rb

とすると、オブジェクトプログラム(パーサのクラスを定義したプログラム)が出来上がる。

簡単なプログラムだが、手作業で再帰下降構文分析のルーチンを書くことを考えたら随分楽ができる。骨組みができているので後は、動作のプログラムだけになる。(それも結構大変だが。)

2.PEG コンパイラ・コンパイラ pegc.rb

class Peg
  def initialize
    @line = ''
    @match = ''
    @stack = []
  end
  attr_accessor :line, :match, :stack

  def push(value)
    @stack.push(value)
  end

  def pop
    @stack.pop
  end

  def mkhead(class_name)
    puts <<END
class #{class_name}
  def initialize
    @line = ''
    @match = ''
    @stack = []
  end
  attr_accessor :line, :match, :stack

  def push(value)
    @stack.push(value)
  end

  def pop
    @stack.pop
  end

END
  end

  def mkend
    puts 'end'
    puts
  end

  def mkterm(func_name, reg, action = '')
    prog = <<END
  def #{func_name}
    if (@line =~ #{reg})
      @match = $&
      @line.sub!(#{reg}, '')
      #{action}
      return true
    else
      return false
    end
  end

END
    puts prog
  end

  def mkandt(func_name, reg)
    prog = <<END
  def #{func_name}
    if (@line =~ #{reg})
      return true
    else
      return false
    end
  end

END
    puts prog
  end

  def mknott(func_name, reg)
    prog = <<END
  def #{func_name}
    if (@line =~ #{reg})
      return false
    else
      return true
    end
  end

END
    puts prog
  end

  def mkand(func_name, expr)
    prog = <<END
  def #{func_name}
    line = @line.dup
    stack = @stack.dup
    if ( #{expr} )
      @line = line
      @stack = @stack.dup
      return true
    else
      @line = line
      @stack = @stack.dup
      return false
    end
  end

END
    puts prog
  end

  def mknot(func_name, expr)
    prog = <<END
  def #{func_name}
    line = @line.dup
    stack = @line.dup
    if ( #{expr} )
      @line = line
      @stack = stack
      return false
    else
      @line = line
      @stack = stack
      return true
    end
  end

END
    puts prog
  end

  def mkstar(func_name, expr)
    prog = <<END
  def #{func_name}
    while ( #{expr} )
    end
    return true
  end

END
    puts prog
  end

  def mkseq(func_name, expr, action = '')
    prog = <<END
  def #{func_name}
    line = @line.dup
    stack = @stack.dup
    if (#{expr})
      @match = line[0...(line.size - @line.size)]
      #{action}
      return true
    else
      @line = line
      @stack = stack
      return false
    end
  end

END
    puts prog
  end

  def mkslash(func_name, *exprs)
    prog = <<END
  def #{func_name}
    line = @line.dup
    stack = @stack.dup
END
    for expr in exprs
      prog += <<END
    @line = line.dup
    @stack = stack.dup
    if ( #{expr} )
      return true
    end
END
  end
    prog += <<END
    @line = line
    @stack = stack
    return false
  end

END
    puts prog
  end

  def mkconsole
    puts <<END
while true
  print 'console> '
  parser.line = gets
  exit if (parser.line =~ /exit/)
  parser.program
end

END
  end

  def mkcheck
    puts <<END
while true
  print 'check> '
  puts gets
end

END
  end
end
by tnomura9 | 2008-05-04 17:02 | Ruby | Comments(0)
<< pegc.rb 用サンプルソー... 整数の四則演算計算機 arit... >>