class Vernacular::AST::ASTParser

Handles monkeying around with the `parser` gem to get it to handle the various modifications that users can configure `Vernacular` to perform.

Public Class Methods

parse(string) click to toggle source
# File lib/vernacular/ast/ast_parser.rb, line 23
def parse(string)
  parser.reset
  buffer = Parser::Base.send(:setup_source_buffer, '(string)', 1,
                             string, @parser.default_encoding)
  parser.parse(buffer)
end
parser() click to toggle source
# File lib/vernacular/ast/ast_parser.rb, line 30
def parser
  @parser ||= new.parser
end

Public Instance Methods

parser() click to toggle source
# File lib/vernacular/ast/ast_parser.rb, line 8
def parser
  source = parser_source

  ast_modifiers.each do |modifier|
    modifier.parser_extensions.each do |parser_extension|
      source = extend_parser(source, parser_extension)
    end
  end

  write_parser(source)
  load 'vernacular/ast/parser.rb'
  Parser::Vernacular.new(builder)
end

Private Instance Methods

ast_modifiers() click to toggle source
# File lib/vernacular/ast/ast_parser.rb, line 37
def ast_modifiers
  Vernacular.modifiers.grep(ASTModifier)
end
builder() click to toggle source
# File lib/vernacular/ast/ast_parser.rb, line 41
def builder
  modifiers = ast_modifiers

  Class.new(Parser::Builders::Default) do
    modifiers.each do |modifier|
      modifier.builder_extensions.each do |builder_extension|
        define_method(builder_extension.method, &builder_extension.block)
      end
    end
  end.new
end
compile_parser(filepath) click to toggle source
# File lib/vernacular/ast/ast_parser.rb, line 53
def compile_parser(filepath)
  output = PARSER_PATH
  exec_path = Gem.activate_bin_path('racc', 'racc', [])
  `#{exec_path} --superclass=Parser::Base -o #{output} #{filepath}`

  parser_name = "Ruby#{parser_version}"
  File.write(output, File.read(output).gsub(parser_name, 'Vernacular'))
end
extend_parser(source, parser_extension) click to toggle source

rubocop:disable Metrics/MethodLength

# File lib/vernacular/ast/ast_parser.rb, line 63
def extend_parser(source, parser_extension)
  needle = "#{parser_extension.symbol}:"
  pattern = /\A\s+#{needle}/

  source.split("\n").each_with_object([]) do |line, edited|
    if line.match?(pattern)
      lhs, rhs = line.split(needle)
      edited << "#{lhs}#{needle} #{parser_extension.pattern}\n" \
                "{\n#{parser_extension.code}\n}\n#{lhs}|#{rhs}"
    else
      edited << line
    end
  end.join("\n")
end
parser_source() click to toggle source

rubocop:enable Metrics/MethodLength

# File lib/vernacular/ast/ast_parser.rb, line 79
def parser_source
  filepath, = Parser::Base.method(:parse).source_location
  grammar_filepath = "../../parser/ruby#{parser_version}.y"
  File.read(File.expand_path(grammar_filepath, filepath))
end
parser_version() click to toggle source
# File lib/vernacular/ast/ast_parser.rb, line 85
def parser_version
  @parser_version ||= RUBY_VERSION.gsub(/\A(\d)\.(\d).+/, '\1\2')
end
write_parser(source) click to toggle source
# File lib/vernacular/ast/ast_parser.rb, line 89
def write_parser(source)
  file = Tempfile.new(['parser-', '.y'])
  file.write(source)
  compile_parser(file.path)
ensure
  file.close
  file.unlink
end