123456789_123456789_123456789_123456789_123456789_

Class: Racc::ParserFileGenerator

Relationships & Source Files
Namespace Children
Classes:
Inherits: Object
Defined in: lib/racc/parserfilegenerator.rb

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(states, params) ⇒ ParserFileGenerator

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 74

def initialize(states, params)
  @states = states
  @grammar = states.grammar
  @params = params
end

Instance Attribute Details

#toplevel?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 224

def toplevel?
  @cref.empty?
end

Instance Method Details

#actions (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 367

def actions
  @grammar.each do |rule|
    unless rule.action.source?
      raise "racc: fatal: cannot generate parser file when any action is a Proc"
    end
  end

  if @params.result_var?
    decl = ', result'
    retval = "\n    result"
    default_body = ''
  else
    decl = ''
    retval = ''
    default_body = 'val[0]'
  end
  @grammar.each do |rule|
    line
    if rule.action.empty? and @params.omit_action_call?
      line "# reduce #{rule.ident} omitted"
    else
      src0 = rule.action.source || SourceText.new(default_body, __FILE__, 0)
      if @params.convert_line?
        src = remove_blank_lines(src0)
        delim = make_delimiter(src.text)
        @f.printf unindent_auto(<<-End),
          module_eval(<<'%s', '%s', %d)
            def _reduce_%d(val, _values%s)
              %s%s
            end
          %s
        End
                  delim, src.filename, src.lineno - 1,
                    rule.ident, decl,
                    src.text, retval,
                  delim
      else
        src = remove_blank_lines(src0)
        @f.printf unindent_auto(<<-End),
          def _reduce_%d(val, _values%s)
          %s%s
          end
        End
                  rule.ident, decl,
                  src.text, retval
      end
    end
  end
  line
  @f.printf unindent_auto(<<-'End'), decl
    def _reduce_none(val, _values%s)
      val[0]
    end
  End
  line
end

#cref_pop (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 216

def cref_pop
  @cref.pop
end

#cref_push(name) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 212

def cref_push(name)
  @cref.push name
end

#detab(str, ts = 8) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 461

def detab(str, ts = 8)
  add = 0
  len = nil
  str.gsub(/\t/) {
    len = ts - ($`.size + add) % ts
    add += len - 1
    ' ' * len
  }
end

#embed_library(src) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 139

def embed_library(src)
  line %[###### #{src.filename} begin]
  line %[unless $".find {|p| p.end_with?('/#{src.filename}')}]
  line %[$".push "\#{__dir__}/#{src.filename}"]
  put src, @params.convert_line?
  line %[end]
  line %[###### #{src.filename} end]
end

#generate_parser

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 80

def generate_parser
  string_io = StringIO.new

  init_line_conversion_system
  @f = string_io
  parser_file

  string_io.rewind
  string_io.read
end

#generate_parser_file(destpath)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 91

def generate_parser_file(destpath)
  init_line_conversion_system
  File.open(destpath, 'w') {|f|
    @f = f
    parser_file
  }
  File.chmod 0755, destpath if @params.make_executable?
end

#header (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 170

def header
  @params.header.each do |src|
    line
    put src, @params.convert_line_all?
  end
end

#i_i_sym_list(name, table) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 337

def i_i_sym_list(name, table)
  sep = ''
  line "#{name} = ["
  table.each_slice(3) do |len, target, mid|
    @f.print sep; sep = ",\n"
    @f.printf '  %d, %d, %s', len, target, mid.inspect
  end
  line " ]"
end

#indent (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 220

def indent
  @f.print '  ' * @cref.size
end

#indent_re(n) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 457

def indent_re(n)
  RE_CACHE[n] ||= /\A {#{n}}/
end

#init_line_conversion_system (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 207

def init_line_conversion_system
  @cref = []
  @used_separator = {}
end

#inner (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 177

def inner
  @params.inner.each do |src|
    line
    put src, @params.convert_line?
  end
end

#integer_list(name, table) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 327

def integer_list(name, table)
  sep = ''
  line "#{name} = ["
  table.each_slice(10) do |ns|
    @f.print sep; sep = ",\n"
    @f.print ns.map {|n| sprintf('%6s', n ? n.to_s : 'nil') }.join(',')
  end
  line ' ]'
end

#line(str = '') (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 203

def line(str = '')
  @f.puts str
end

#make_delimiter(body) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 433

def make_delimiter(body)
  delim = '.,.,'
  while body.index(delim)
    delim *= 2
  end
  delim
end

#make_separator(src) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 236

def make_separator(src)
  sep = unique_separator(src.filename)
  sep *= 2 while src.text.index(sep)
  sep
end

#minimum_indent(lines) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 447

def minimum_indent(lines)
  lines.map {|line| n_indent(line) }.min
end

#n_indent(line) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 451

def n_indent(line)
  line.slice(/\A\s+/).size
end

#notice (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 126

def notice
  line %q[# frozen_string_literal: true] if @params.frozen_strings?
  line %q[#]
  line %q[# DO NOT MODIFY!!!!]
  line %Q[# This file is automatically generated by Racc #{Racc::Version}]
  line %Q[# from Racc grammar file "#{@params.filename}".]
  line %q[#]
end

#parser_class(classname, superclass) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 152

def parser_class(classname, superclass)
  mods = classname.split('::')
  classid = mods.pop
  mods.each do |mod|
    indent; line "module #{mod}"
    cref_push mod
  end
  indent; line "class #{classid} < #{superclass}"
  cref_push classid
  yield
  cref_pop
  indent; line "end   \# class #{classid}"
  mods.reverse_each do |mod|
    cref_pop
    indent; line "end   \# module #{mod}"
  end
end

#parser_file (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 102

def parser_file
  shebang @params.interpreter if @params.make_executable?
  notice
  line
  if @params.embed_runtime?
    embed_library runtime_source()
  else
    require 'racc/parser.rb'
  end
  header
  parser_class(@params.classname, @params.superclass) {
    inner
    state_transition_table
  }
  footer
end

#put(src, convert_line = false) (private)

Low Level Routines

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 193

def put(src, convert_line = false)
  if convert_line
    replace_location(src) {
      @f.puts src.text
    }
  else
    @f.puts src.text
  end
end

#put_state_transition_table(f)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 257

def put_state_transition_table(f)
  @f = f
  state_transition_table
end

#remove_blank_lines(src) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 424

def remove_blank_lines(src)
  body = src.text.dup
  line = src.lineno
  while body.slice!(/\A[ \t\f]*(?:\n|\r\n|\r)/)
    line += 1
  end
  SourceText.new(body, src.filename, line)
end

#replace_location(src) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 228

def replace_location(src)
  sep = make_separator(src)
  @f.print 'self.class.' if toplevel?
  @f.puts "module_eval(<<'#{sep}', '#{src.filename}', #{src.lineno})"
  yield
  @f.puts sep
end

#require(feature) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 148

def require(feature)
  line "require '#{feature}'"
end

#runtime_source (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 135

def runtime_source
  SourceText.new(::Racc::PARSER_TEXT, 'racc/parser.rb', 1)
end

#shebang(path) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 122

def shebang(path)
  line '#!' + (path == 'ruby' ? RUBY_PATH : path)
end

#state_transition_table (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 264

def state_transition_table
  table = @states.state_transition_table
  table.use_result_var = @params.result_var?
  table.debug_parser = @params.debug_parser?

  line "##### State transition tables begin ###"
  line
  integer_list 'racc_action_table', table.action_table
  line
  integer_list 'racc_action_check', table.action_check
  line
  integer_list 'racc_action_pointer', table.action_pointer
  line
  integer_list 'racc_action_default', table.action_default
  line
  integer_list 'racc_goto_table', table.goto_table
  line
  integer_list 'racc_goto_check', table.goto_check
  line
  integer_list 'racc_goto_pointer', table.goto_pointer
  line
  integer_list 'racc_goto_default', table.goto_default
  line
  i_i_sym_list 'racc_reduce_table', table.reduce_table
  line
  line "racc_reduce_n = #{table.reduce_n}"
  line
  line "racc_shift_n = #{table.shift_n}"
  line
  sym_int_hash 'racc_token_table', table.token_table
  line
  line "racc_nt_base = #{table.nt_base}"
  line
  line "racc_use_result_var = #{table.use_result_var}"
  line
  @f.print(unindent_auto(<<-End))
    Racc_arg = [
      racc_action_table,
      racc_action_check,
      racc_action_default,
      racc_action_pointer,
      racc_goto_table,
      racc_goto_check,
      racc_goto_default,
      racc_goto_pointer,
      racc_nt_base,
      racc_reduce_table,
      racc_token_table,
      racc_shift_n,
      racc_reduce_n,
      racc_use_result_var ]
  End
  line "Ractor.make_shareable(Racc_arg) if defined?(Ractor)"
  line
  string_list 'Racc_token_to_s_table', table.token_to_s_table
  line "Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)"
  line
  line "Racc_debug_parser = #{table.debug_parser}"
  line
  line '##### State transition tables end #####'
  actions
end

#string_list(name, list) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 357

def string_list(name, list)
  sep = "  "
  line "#{name} = ["
  list.each do |s|
    @f.print sep; sep = ",\n  "
    @f.print s.dump
  end
  line ' ]'
end

#sym_int_hash(name, h) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 347

def sym_int_hash(name, h)
  sep = "\n"
  @f.print "#{name} = {"
  h.to_a.sort_by {|sym, i| i }.each do |sym, i|
    @f.print sep; sep = ",\n"
    @f.printf "  %s => %d", sym.serialize, i
  end
  line " }"
end

#unindent_auto(str) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 441

def unindent_auto(str)
  lines = str.lines.to_a
  n = minimum_indent(lines)
  lines.map {|line| detab(line).sub(indent_re(n), '').rstrip + "\n" }.join('')
end

#unique_separator(id) (private)

[ GitHub ]

  
# File 'lib/racc/parserfilegenerator.rb', line 242

def unique_separator(id)
  sep = String.new "...end #{id}/module_eval..."
  while @used_separator.key?(sep)
    sep.concat sprintf('%02x', rand(255))
  end
  @used_separator[sep] = true
  sep
end