読者です 読者をやめる 読者になる 読者になる

C言語のポインタ解析(Ruby)

Ruby C言語

いわゆる「K&R」本 p.148-152 のポインタ解析スクリプトを、Ruby に移植してみました。ポインタを使っているのを Ruby で書くのが留意点です。まったく Ruby らしくないので、Ruby らしく書き直すのが課題でしょう。

例えばコマンドライン引数に「char(*(*x[3])())[5]」(コマンドライン引数なので、空白は使えません)と入力した場合、出力は「x: array[3] of pointer to function returning pointer to array[5] of char」となります。

class Analysis_dcl
  NAME = "_1"; PARENS = "_2"; BRACKETS = "_3"
  
  def initialize(stg)
    @buf = []
    @line = stg; puts @line
    @token = @tokentype = @out = @datatype = ""
  end
  
  def getch
    if @buf.length > 0
      @buf.pop
    else
      @line.slice!(0).to_s
    end
  end
  
  def ungetch(a)
    @buf.push(a)
  end

  def dcl
    ns = 0
    while gettoken == "*"; ns += 1; end
    dirdcl
    while ns > 0
      ns -= 1
      @out << " pointer to"
    end
  end
  
  def dirdcl
    if @tokentype == "("
      dcl
      puts "error: missing )" unless @tokentype == ")"
    elsif @tokentype == NAME
      @name = @token
    else
      puts "error: expected name or (dcl)"
    end
    while ((type = gettoken) == PARENS or type == BRACKETS)
      if type == PARENS
        @out << " function returning"
      else
        @out << " array#{@token} of"
      end
    end
  end
  
  def gettoken
    @token = ""
    while (c = getch) == " "; end
    if c == "("
      if (c = getch) == ")"
        @token = "()"
        return @tokentype = PARENS
      else
        ungetch(c)
        return @tokentype = "("
      end
    elsif c == "["
      @token << c
      begin
        c = getch
        @token << c
      end while c != "]"
      return @tokentype = BRACKETS
    elsif c.match(/[A-Za-z]/)
      @token << c
      while (c = getch).match(/\w/)
        @token << c
      end
      ungetch(c)
      return @tokentype = NAME
    else
      return @tokentype = c
    end
  end
  
  def main
    while gettoken != ""
      @datatype = @token
      dcl
      puts "syntax error" unless @tokentype == ""
      puts "#{@name}: #{@out} #{@datatype}"
    end
  end
end


line = (ARGV[0] || 'int (*x)[13]').dup.to_s
a = Analysis_dcl.new(line)
a.main


クラスを継承する

上の操作の逆をやります。「x は char を返す関数へのポインタの配列[3]へのポインタを返す関数である」なら、コマンドライン引数に「x()*[3]*()char」と入力します(空白は入れないで下さい)。結果は「char (*(*x())[3])()」となります。

class Make_dcl < Analysis_dcl
  def main
    type = ""
    while gettoken != ""
      @out = @token
      while (type = gettoken) != ""
        if type == PARENS or type == BRACKETS
          @out << @token
        elsif type == '*'
          @out = "(*#{@out})"
        elsif type == NAME
          @out = "#{@token} #{@out}"
        else
          puts "invalid input at #{@token}"
        end
      end
      puts @out
    end
  end
end

line = (ARGV[0] || 'x()*[]*()char').dup.to_s
a = Make_dcl.new(line)
a.main