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