class Racc::GrammarFileParser

reopen

def _add_group_rule(enum)

def _add_group_rule(enum)
  target = @grammar.intern("-temp-group", true)
  rules, _ = _add_rule_block(target, enum)
  target_name = rules.map{|syms, sprec| syms.join("-")}.join("|")
  @group_rule_registry ||= {}
  unless target = @group_rule_registry[target_name]
    target = @grammar.intern("-group@#{target_name}", true)
    @group_rule_registry[target_name] = target
    src = SourceText.new("result = val", @filename, @scanner.lineno + 1)
    act = UserAction.source_text(src)
    rules.each do |syms, sprec|
      rule = Rule.new(target, syms, act)
      rule.specified_prec = sprec
      @grammar.add rule
    end
  end
  target
end

def _add_many1_rule(prev)

def _add_many1_rule(prev)
  @many1_rule_registry ||= {}
  target = @many1_rule_registry[prev.to_s]
  return target if target
  target = _gen_target_name("many1", prev)
  @many1_rule_registry[prev.to_s] = target
  src = SourceText.new("result = val[1] ? val[1].unshift(val[0]) : val", @filename, @scanner.lineno + 1)
  act = UserAction.source_text(src)
  @grammar.add Rule.new(target, [prev], act)
  @grammar.add Rule.new(target, [prev, target], act)
  target
end

def _add_many_rule(prev)

def _add_many_rule(prev)
  @many_rule_registry ||= {}
  target = @many_rule_registry[prev.to_s]
  return target if target
  target = _gen_target_name("many", prev)
  @many_rule_registry[prev.to_s] = target
  src = SourceText.new("result = val[1] ? val[1].unshift(val[0]) : val", @filename, @scanner.lineno + 1)
  act = UserAction.source_text(src)
  @grammar.add Rule.new(target, [], act)
  @grammar.add Rule.new(target, [prev, target], act)
  target
end

def _add_option_rule(prev)

def _add_option_rule(prev)
  @option_rule_registry ||= {}
  target = @option_rule_registry[prev.to_s]
  return target if target
  target = _gen_target_name("option", prev)
  @option_rule_registry[prev.to_s] = target
  act = UserAction.empty
  @grammar.add Rule.new(target, [], act)
  @grammar.add Rule.new(target, [prev], act)
  target
end

def _add_rule_block(target, enum)

def _add_rule_block(target, enum)
  rules = [] # [ [seqs, sprec], .. ]
  curr = []
  sprec = nil
  while (sym, idx = enum.next rescue nil)
    case sym
    when OrMark
      rules << [curr, sprec]
      curr = []
      sprec = nil
    when OptionMark
      curr << _add_option_rule(curr.pop)
    when ManyMark
      curr << _add_many_rule(curr.pop)
    when Many1Mark
      curr << _add_many1_rule(curr.pop)
    when GroupStartMark
      curr << _add_group_rule(enum)
    when GroupEndMark
      rules << [curr, sprec]
      return rules, sym, idx
    when Prec
      raise CompileError, "'=<prec>' used twice in one rule" if sprec
      sprec = sym.symbol
    else
      curr.push sym
    end
  end
  rules << [curr, sprec]
  rules.each do |syms, sprec|
    add_rule target, syms, sprec
  end
  nil
end

def _gen_target_name(type, sym)

def _gen_target_name(type, sym)
  @grammar.intern("-#{type}@#{sym.value}", true)
end

def add_rule(target, list, sprec)

def add_rule(target, list, sprec)
  if list.last.kind_of?(UserAction)
    act = list.pop
  else
    act = UserAction.empty
  end
  list.map! {|s| s.kind_of?(UserAction) ? embedded_action(s) : s }
  rule = Rule.new(target, list, act)
  rule.specified_prec = sprec
  @grammar.add rule
end

def add_rule_block(list)

def add_rule_block(list)
  target = list.shift
  case target
  when OrMark, OptionMark, ManyMark, Many1Mark, GroupStartMark, GroupEndMark, UserAction, Prec
    raise CompileError, "#{target.lineno}: unexpected symbol #{target.name}"
  end
  enum = list.each.with_index
  _, sym, idx = _add_rule_block(target, enum)
  if idx
    # sym is Racc::GroupEndMark
    raise "#{sym.lineno}: unexpected symbol ')' at pos=#{idx}"
  end
end

def add_user_code(label, src)

def add_user_code(label, src)
  @result.params.public_send(USER_CODE_LABELS[label]).push src
end

def canonical_label(src)

def canonical_label(src)
  label = src.to_s.strip.downcase.slice(/\w+/)
  unless USER_CODE_LABELS.key?(label)
    raise CompileError, "unknown user code type: #{label.inspect}"
  end
  label
end

def embedded_action(act)

def embedded_action(act)
  sym = @grammar.intern("@#{@embedded_action_seq += 1}".intern, true)
  @grammar.add Rule.new(sym, [], act)
  sym
end

def initialize(debug_flags = DebugFlags.new)

def initialize(debug_flags = DebugFlags.new)
  @yydebug = debug_flags.parse
end

def location

def location
  "#{@filename}:#{@lineno - 1 + @scanner.lineno}"
end

def next_token

def next_token
  @scanner.scan
end

def on_error(tok, val, _values)

def on_error(tok, val, _values)
  if val.respond_to?(:id2name)
    v = val.id2name
  elsif val.kind_of?(String)
    v = val
  else
    v = val.inspect
  end
  raise CompileError, "#{location()}: unexpected token '#{v}'"
end

def parse(src, filename = '-', lineno = 1)

def parse(src, filename = '-', lineno = 1)
  @filename = filename
  @lineno = lineno
  @scanner = GrammarFileScanner.new(src, @filename)
  @scanner.debug = @yydebug
  @grammar = Grammar.new
  @result = Result.new(@grammar)
  @embedded_action_seq = 0
  yyparse @scanner, :yylex
  parse_user_code
  @result.grammar.init
  @result
end

def parse_user_code

def parse_user_code
  line = @scanner.lineno
  _, *blocks = *@scanner.epilogue.split(/^----/)
  blocks.each do |block|
    header, *body = block.lines.to_a
    label0, paths = *header.sub(/\A-+/, '').split('=', 2)
    label = canonical_label(label0)
    (paths ? paths.strip.split(' ') : []).each do |path|
      add_user_code label, SourceText.new(File.read(path), path, 1)
    end
    add_user_code label, SourceText.new(body.join(''), @filename, line + 1)
    line += (1 + body.size)
  end
end