Ripper 클래스

Ripper 클래스

Ripper는 Ruby 스크립트 파서(parser)예요. 이벤트 기반(event-based) 방식으로 파서로부터 정보를 얻을 수 있어요. 예를 들어 Ruby 프로그램의 추상 구문 트리(abstract syntax tree)나 간단한 어휘 분석(lexical analysis) 정보 같은 걸 얻을 수 있죠.

출처: Ruby 3.3 API

본문

사용법 (Usage)

Ripper는 프로그램을 기호 표현식 트리(문자 표현식을 담은 트리, S-expression)로 파싱하는 쉬운 인터페이스를 제공해요. 파서의 출력을 이해하는 건 어려울 수 있으니, 가독성을 위해 PP로 출력을 정리하는 걸 권장해요.

require 'ripper'
require 'pp'

pp Ripper.sexp('def hello(world) "Hello, #{world}!"; end')
  #=> [:program,
       [[:def,
         [:@ident, "hello", [1, 4]],
         [:paren,
          [:params, [[:@ident, "world", [1, 10]]], nil, nil, nil, nil, nil, nil]],
         [:bodystmt,
          [[:string_literal,
            [:string_content,
             [:@tstring_content, "Hello, ", [1, 18]],
             [:string_embexpr, [[:var_ref, [:@ident, "world", [1, 27]]]]],
             [:@tstring_content, "!", [1, 33]]]]],
          nil,
          nil,
          nil]]]]

위 예제에서 표현식이 :program으로 시작하는 걸 볼 수 있어요. 여기서부터 메서드 정의는 :def로 시작하고, 그다음 메서드의 식별자 :@ident가 나와요. 메서드 식별자 다음에는 괄호 :paren가 오고, :params 아래에 메서드 매개변수가 놓여요.

그다음은 메서드 본문인데 :bodystmt(stmt는 statement를 뜻해요)로 시작하며, 메서드의 전체 정의를 담고 있어요. 여기서는 단순히 String을 반환하므로 다음으로 :string_literal 표현식이 와요. :string_literal 안에는 @tstring_content가 두 개 있는데, Hello, !의 리터럴 부분이에요. 두 @tstring_content 사이에는 :string_embexpr이 있고, embexpr은 삽입된 표현식(embedded expression)을 의미해요. 우리 표현식은 world라는 식별자(@ident)를 가진 지역 변수, 즉 var_ref로 구성돼 있어요.

상수 (Constants)

  • EVENTS: 모든 ripper 이벤트의 이름을 담고 있는 배열이에요.
  • PARSER_EVENTS: 파서 이벤트의 이름을 담고 있는 배열이에요.
  • SCANNER_EVENTS: 스캐너 이벤트의 이름을 담고 있는 배열이에요.

Public Class Methods

lex(src, filename = '-', lineno = 1, **kw)

Ruby 프로그램을 토큰화하고 [[lineno, column], type, token, state]처럼 형식화된 배열의 배열을 반환해요. filename 인자는 대부분 무시돼요. 기본적으로 이 메서드는 src의 문법 오류를 처리하지 않고, src의 오류에 대해 SyntaxError를 발생시키려면 raise_errors 키워드를 사용해요.

require 'ripper'
require 'pp'

pp Ripper.lex("def m(a) nil end")
#=> [[[1,  0], :on_kw,     "def", FNAME    ],
     [[1,  3], :on_sp,     " ",   FNAME    ],
     [[1,  4], :on_ident,  "m",   ENDFN    ],
     [[1,  5], :on_lparen, "(",   BEG|LABEL],
     [[1,  6], :on_ident,  "a",   ARG      ],
     [[1,  7], :on_rparen, ")",   ENDFN    ],
     [[1,  8], :on_sp,     " ",   BEG      ],
     [[1,  9], :on_kw,     "nil", END      ],
     [[1, 12], :on_sp,     " ",   END      ],
     [[1, 13], :on_kw,     "end", END      ]]

parse(src, filename = '(ripper)', lineno = 1)

src에서 읽은 주어진 Ruby 프로그램을 파싱해요. srcString이거나 IO, 또는 gets 메서드를 가진 객체여야 해요.

sexp(src, filename = '-', lineno = 1, raise_errors: false)

EXPERIMENTALsrc를 파싱해 S-exp 트리를 만들어요. Ripper.sexp_raw보다 더 읽기 쉬운 트리를 반환해요. 주로 개발자용 메서드예요. filename 인자는 대부분 무시돼요. 기본적으로 이 메서드는 src의 문법 오류를 처리하지 않고, 그러한 경우 nil을 반환해요. 오류에 대해 SyntaxError를 발생시키려면 raise_errors 키워드를 사용해요.

require 'ripper'
require 'pp'

pp Ripper.sexp("def m(a) nil end")
  #=> [:program,
       [[:def,
        [:@ident, "m", [1, 4]],
        [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil, nil, nil, nil]],
        [:bodystmt, [[:var_ref, [:@kw, "nil", [1, 9]]]], nil, nil, nil]]]]

sexp_raw(src, filename = '-', lineno = 1, raise_errors: false)

EXPERIMENTALsrc를 파싱해 S-exp 트리를 만들어요. 주로 개발자용 메서드예요. filename 인자는 대부분 무시돼요. 기본적으로 문법 오류가 있으면 nil을 반환하고, raise_errors 키워드로 SyntaxError를 발생시킬 수 있어요.

require 'ripper'
require 'pp'

pp Ripper.sexp_raw("def m(a) nil end")
  #=> [:program,
       [:stmts_add,
        [:stmts_new],
        [:def,
         [:@ident, "m", [1, 4]],
         [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil]],
         [:bodystmt,
          [:stmts_add, [:stmts_new], [:var_ref, [:@kw, "nil", [1, 9]]]],
          nil,
          nil,
          nil]]]]

slice(src, pattern, n = 0)

EXPERIMENTALsrc를 파싱하고 pattern에 매칭된 문자열을 반환해요. patternRegexp로 표현해야 해요.

require 'ripper'

p Ripper.slice('def m(a) nil end', 'ident')                   #=> "m"
p Ripper.slice('def m(a) nil end', '[ident lparen rparen]+')  #=> "m(a)"
p Ripper.slice("<<EOS\nstring\nEOS",
               'heredoc_beg nl $(tstring_content*) heredoc_end', 1)
    #=> "string\n"

tokenize(src, filename = '-', lineno = 1, **kw)

Ruby 프로그램을 토큰화하고 문자열 배열을 반환해요. 반환값이 그냥 토큰화된 입력이므로 filenamelineno 인자는 대부분 무시돼요. 기본적으로 문법 오류를 처리하지 않고, raise_errors 키워드로 SyntaxError를 발생시킬 수 있어요.

p Ripper.tokenize("def m(a) nil end")
   # => ["def", " ", "m", "(", "a", ")", " ", "nil", " ", "end"]

Private Instance Methods

compile_error(msg)

파서가 문법 오류를 찾았을 때 호출되는 메서드예요.

warn(fmt, *args)

파서가 약한 경고(weak warning)를 만들 때 호출되는 메서드예요. fmtargs는 printf 스타일이에요.

warning(fmt, *args)

파서가 강한 경고(strong warning)를 만들 때 호출되는 메서드예요. fmtargs는 printf 스타일이에요.