class Parser::Base

Base class for version-specific parsers.

@api public

@!attribute [r] diagnostics

@return [Parser::Diagnostic::Engine]

@!attribute [r] static_env

@return [Parser::StaticEnvironment]

Attributes

builder[R]
diagnostics[R]
source_buffer[R]
static_env[R]

Public Class Methods

default_parser() click to toggle source

@return [Parser::Base] parser with the default options set.

# File lib/parser/base.rb, line 82
def self.default_parser
  parser = new

  parser.diagnostics.all_errors_are_fatal = true
  parser.diagnostics.ignore_warnings      = true

  parser.diagnostics.consumer = lambda do |diagnostic|
    $stderr.puts(diagnostic.render)
  end

  parser
end
new(builder=Parser::Builders::Default.new) click to toggle source

@param [Parser::Builders::Default] builder The AST builder to use.

# File lib/parser/base.rb, line 120
def initialize(builder=Parser::Builders::Default.new)
  @diagnostics = Diagnostic::Engine.new

  @static_env  = StaticEnvironment.new

  @lexer = Lexer.new(version)
  @lexer.diagnostics = @diagnostics
  @lexer.static_env  = @static_env

  @builder = builder
  @builder.parser = self

  if self.class::Racc_debug_parser && ENV['RACC_DEBUG']
    @yydebug = true
  end

  reset
end
parse(string, file='(string)', line=1) click to toggle source

Parses a string of Ruby code and returns the AST. If the source cannot be parsed, {SyntaxError} is raised and a diagnostic is printed to `stderr`.

@example

Parser::Base.parse('puts "hello"')

@param [String] string The block of code to parse. @param [String] file The name of the file the code originated from. @param [Numeric] line The initial line number. @return [Parser::AST::Node]

# File lib/parser/base.rb, line 28
def self.parse(string, file='(string)', line=1)
  parser = default_parser
  source_buffer = setup_source_buffer(file, line, string, parser.default_encoding)
  parser.parse(source_buffer)
end
parse_file(filename) click to toggle source

Parses Ruby source code by reading it from a file. If the source cannot be parsed, {SyntaxError} is raised and a diagnostic is printed to `stderr`.

@param [String] filename Path to the file to parse. @return [Parser::AST::Node] @see parse

# File lib/parser/base.rb, line 62
def self.parse_file(filename)
  parse(File.read(filename), filename)
end
parse_file_with_comments(filename) click to toggle source

Parses Ruby source code by reading it from a file and returns the AST and comments. If the source cannot be parsed, {SyntaxError} is raised and a diagnostic is printed to `stderr`.

@param [String] filename Path to the file to parse. @return [Array] @see parse

# File lib/parser/base.rb, line 75
def self.parse_file_with_comments(filename)
  parse_with_comments(File.read(filename), filename)
end
parse_with_comments(string, file='(string)', line=1) click to toggle source

Parses a string of Ruby code and returns the AST and comments. If the source cannot be parsed, {SyntaxError} is raised and a diagnostic is printed to `stderr`.

@example

Parser::Base.parse_with_comments('puts "hello"')

@param [String] string The block of code to parse. @param [String] file The name of the file the code originated from. @param [Numeric] line The initial line number. @return [Array]

# File lib/parser/base.rb, line 47
def self.parse_with_comments(string, file='(string)', line=1)
  parser = default_parser
  source_buffer = setup_source_buffer(file, line, string, parser.default_encoding)
  parser.parse_with_comments(source_buffer)
end

Private Class Methods

setup_source_buffer(file, line, string, encoding) click to toggle source
# File lib/parser/base.rb, line 95
def self.setup_source_buffer(file, line, string, encoding)
  if string.respond_to? :force_encoding
    string = string.dup.force_encoding(encoding)
  end

  source_buffer = Source::Buffer.new(file, line)

  if name == 'Parser::Ruby18'
    source_buffer.raw_source = string
  else
    source_buffer.source     = string
  end

  source_buffer
end

Public Instance Methods

in_def?() click to toggle source

@api private @return [Boolean]

# File lib/parser/base.rb, line 224
def in_def?
  @def_level > 0
end
parse(source_buffer) click to toggle source

Parses a source buffer and returns the AST.

@param [Parser::Source::Buffer] source_buffer The source buffer to parse. @return [Parser::AST::Node]

# File lib/parser/base.rb, line 158
def parse(source_buffer)
  @lexer.source_buffer = source_buffer
  @source_buffer       = source_buffer

  do_parse
ensure
  # Don't keep references to the source file.
  @source_buffer       = nil
  @lexer.source_buffer = nil
end
parse_with_comments(source_buffer) click to toggle source

Parses a source buffer and returns the AST and the source code comments.

@see parse @see Parser::Source::Comment#associate @return [Array]

# File lib/parser/base.rb, line 176
def parse_with_comments(source_buffer)
  @lexer.comments = []

  [ parse(source_buffer), @lexer.comments ]
ensure
  @lexer.comments = nil
end
reset() click to toggle source

Resets the state of the parser.

# File lib/parser/base.rb, line 142
def reset
  @source_buffer = nil
  @def_level     = 0 # count of nested def's.

  @lexer.reset
  @static_env.reset

  self
end
tokenize(source_buffer, recover=false) click to toggle source

Parses a source buffer and returns the AST, the source code comments, and the tokens emitted by the lexer. If `recover` is true and a fatal {SyntaxError} is encountered, `nil` is returned instead of the AST, and comments as well as tokens are only returned up to the location of the error.

Currently, token stream format returned by tokenize is not documented, but is considered part of a public API and only changed according to Semantic Versioning.

However, note that the exact token composition of various constructs might vary. For example, a string `“foo”` is represented equally well by `:tSTRING_BEG “ :tSTRING_CONTENT foo :tSTRING_END ”` and `:tSTRING “foo”`; such details must not be relied upon.

@param [Parser::Source::Buffer] source_buffer @param [Boolean] recover If true, recover from syntax errors. False by default. @return [Array]

# File lib/parser/base.rb, line 204
def tokenize(source_buffer, recover=false)
  @lexer.tokens = []
  @lexer.comments = []

  begin
    ast = parse(source_buffer)
  rescue Parser::SyntaxError
    raise if !recover
  end

  [ ast, @lexer.comments, @lexer.tokens ]
ensure
  @lexer.tokens = nil
  @lexer.comments = nil
end

Private Instance Methods

check_kwarg_name(name_t) click to toggle source
# File lib/parser/base.rb, line 234
def check_kwarg_name(name_t)
  case name_t[0]
  when /^[a-z_]/
    # OK
  when /^[A-Z]/
    diagnostic :error, :argument_const, nil, name_t
  end
end
diagnostic(level, reason, arguments, location_t, highlights_ts=[]) click to toggle source
# File lib/parser/base.rb, line 243
def diagnostic(level, reason, arguments, location_t, highlights_ts=[])
  _, location = location_t

  highlights = highlights_ts.map do |token|
    _, range = token
    range
  end

  @diagnostics.process(
      Diagnostic.new(level, reason, arguments, location, highlights))

  if level == :error
    yyerror
  end
end
next_token() click to toggle source
# File lib/parser/base.rb, line 230
def next_token
  @lexer.advance
end
on_error(error_token_id, error_value, value_stack) click to toggle source
# File lib/parser/base.rb, line 259
def on_error(error_token_id, error_value, value_stack)
  token_name = token_to_str(error_token_id)
  _, location = error_value

  @diagnostics.process(Diagnostic.new(
      :error, :unexpected_token, { :token => token_name }, location))
end