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]
context[R]
current_arg_stack[R]
diagnostics[R]
lexer[R]
max_numparam_stack[R]
pattern_hash_keys[R]
pattern_variables[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 84
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 126
def initialize(builder=Parser::Builders::Default.new)
  @diagnostics = Diagnostic::Engine.new

  @static_env  = StaticEnvironment.new

  # Stack that holds current parsing context
  @context = Context.new

  # Maximum numbered parameters stack
  @max_numparam_stack = MaxNumparamStack.new

  # Current argument names stack
  @current_arg_stack = CurrentArgStack.new

  # Stack of set of variables used in the current pattern
  @pattern_variables = VariablesStack.new

  # Stack of set of keys used in the current hash in pattern matchinig
  @pattern_hash_keys = VariablesStack.new

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

  @builder = builder
  @builder.parser = self

  # Last emitted token
  @last_token = nil

  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 30
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 64
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 77
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 49
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 97
def self.setup_source_buffer(file, line, string, encoding)
  string = string.dup.force_encoding(encoding)

  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

parse(source_buffer) click to toggle source

Parses a source buffer and returns the AST, or ‘nil` in case of a non fatal error.

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

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

  do_parse || nil # Force `false` to `nil`, see https://github.com/ruby/racc/pull/136
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 204
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 167
def reset
  @source_buffer = nil

  @lexer.reset
  @static_env.reset
  @context.reset
  @current_arg_stack.reset
  @pattern_variables.reset
  @pattern_hash_keys.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. In case of a fatal error, a {SyntaxError} is raised, unless ‘recover` is true. In case of an error (non-fatal or recovered), `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 233
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 257
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 266
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 251
def next_token
  token = @lexer.advance
  @last_token = token
  token
end
on_error(error_token_id, error_value, value_stack) click to toggle source
# File lib/parser/base.rb, line 282
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