C言語のポインタ解析(Ruby)
いわゆる「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