class ProcToAst::Parser

Attributes

parser[R]

Public Class Methods

new() click to toggle source
# File lib/proc_to_ast.rb, line 13
def initialize
  @parser = ::Parser::CurrentRuby.default_parser
  @parser.diagnostics.consumer = ->(diagnostic) {} # suppress error message
end

Public Instance Methods

parse(filename, linenum) click to toggle source

Read file and try parsing if success parse, find proc AST

@param filename [String] reading file path @param linenum [Integer] start line number @return [Parser::AST::Node] Proc AST

# File lib/proc_to_ast.rb, line 24
def parse(filename, linenum)
  @filename, @linenum = filename, linenum
  buf = []
  File.open(filename, "rb").each_with_index do |line, index|
    next if index < linenum - 1
    buf << line
    begin
      return do_parse(buf.join)
    rescue ::Parser::SyntaxError
      node = trim_and_retry(buf)
      return node if node
    end
  end
  fail(::Parser::SyntaxError, 'Unknown error')
end

Private Instance Methods

do_parse(source) click to toggle source
# File lib/proc_to_ast.rb, line 42
def do_parse(source)
  parser.reset

  source_buffer = ::Parser::Source::Buffer.new(@filename, @linenum)
  source_buffer.source = source
  node = parser.parse(source_buffer)
  block_nodes = traverse_node(node)

  if block_nodes.length == 1
    block_nodes.first
  else
    raise ProcToAst::MultiMatchError
  end
end
traverse_node(node) click to toggle source
# File lib/proc_to_ast.rb, line 68
def traverse_node(node)
  if node.type != :block
    node.children.flat_map { |child|
      if child.is_a?(AST::Node)
        traverse_node(child)
      end
    }.compact
  else
    [node]
  end
end
trim_and_retry(buf) click to toggle source

Remove tail comma and wrap dummy method, and retry parsing For proc inner Array or Hash

# File lib/proc_to_ast.rb, line 59
def trim_and_retry(buf)
  *lines, last = buf

  # For inner Array or Hash or Arguments list.
  lines << last.gsub(/,\s*$/, "")
  do_parse("a(#{lines.join})") # wrap dummy method
rescue ::Parser::SyntaxError
end