diff options
Diffstat (limited to 'lib/racc/grammarfileparser.rb')
-rw-r--r-- | lib/racc/grammarfileparser.rb | 561 |
1 files changed, 0 insertions, 561 deletions
diff --git a/lib/racc/grammarfileparser.rb b/lib/racc/grammarfileparser.rb deleted file mode 100644 index 419495113b..0000000000 --- a/lib/racc/grammarfileparser.rb +++ /dev/null @@ -1,561 +0,0 @@ -#-- -# -# -# -# Copyright (c) 1999-2006 Minero Aoki -# -# This program is free software. -# You can distribute/modify this program under the same terms of ruby. -# see the file "COPYING". -# -#++ - -require 'racc' -require 'racc/compat' -require 'racc/grammar' -require 'racc/parserfilegenerator' -require 'racc/sourcetext' -require 'stringio' - -module Racc - - grammar = Grammar.define { - g = self - - g.class = seq(:CLASS, :cname, many(:param), :RULE, :rules, option(:END)) - - g.cname = seq(:rubyconst) {|name| - @result.params.classname = name - }\ - | seq(:rubyconst, "<", :rubyconst) {|c, _, s| - @result.params.classname = c - @result.params.superclass = s - } - - g.rubyconst = separated_by1(:colon2, :SYMBOL) {|syms| - syms.map {|s| s.to_s }.join('::') - } - - g.colon2 = seq(':', ':') - - g.param = seq(:CONV, many1(:convdef), :END) {|*| - #@grammar.end_convert_block # FIXME - }\ - | seq(:PRECHIGH, many1(:precdef), :PRECLOW) {|*| - @grammar.end_precedence_declaration true - }\ - | seq(:PRECLOW, many1(:precdef), :PRECHIGH) {|*| - @grammar.end_precedence_declaration false - }\ - | seq(:START, :symbol) {|_, sym| - @grammar.start_symbol = sym - }\ - | seq(:TOKEN, :symbols) {|_, syms| - syms.each do |s| - s.should_terminal - end - }\ - | seq(:OPTION, :options) {|_, syms| - syms.each do |opt| - case opt - when 'result_var' - @result.params.result_var = true - when 'no_result_var' - @result.params.result_var = false - when 'omit_action_call' - @result.params.omit_action_call = true - when 'no_omit_action_call' - @result.params.omit_action_call = false - else - raise CompileError, "unknown option: #{opt}" - end - end - }\ - | seq(:EXPECT, :DIGIT) {|_, num| - if @grammar.n_expected_srconflicts - raise CompileError, "`expect' seen twice" - end - @grammar.n_expected_srconflicts = num - } - - g.convdef = seq(:symbol, :STRING) {|sym, code| - sym.serialized = code - } - - g.precdef = seq(:LEFT, :symbols) {|_, syms| - @grammar.declare_precedence :Left, syms - }\ - | seq(:RIGHT, :symbols) {|_, syms| - @grammar.declare_precedence :Right, syms - }\ - | seq(:NONASSOC, :symbols) {|_, syms| - @grammar.declare_precedence :Nonassoc, syms - } - - g.symbols = seq(:symbol) {|sym| - [sym] - }\ - | seq(:symbols, :symbol) {|list, sym| - list.push sym - list - }\ - | seq(:symbols, "|") - - g.symbol = seq(:SYMBOL) {|sym| @grammar.intern(sym) }\ - | seq(:STRING) {|str| @grammar.intern(str) } - - g.options = many(:SYMBOL) {|syms| syms.map {|s| s.to_s } } - - g.rules = option(:rules_core) {|list| - add_rule_block list unless list.empty? - nil - } - - g.rules_core = seq(:symbol) {|sym| - [sym] - }\ - | seq(:rules_core, :rule_item) {|list, i| - list.push i - list - }\ - | seq(:rules_core, ';') {|list, *| - add_rule_block list unless list.empty? - list.clear - list - }\ - | seq(:rules_core, ':') {|list, *| - next_target = list.pop - add_rule_block list unless list.empty? - [next_target] - } - - g.rule_item = seq(:symbol)\ - | seq("|") {|*| - OrMark.new(@scanner.lineno) - }\ - | seq("=", :symbol) {|_, sym| - Prec.new(sym, @scanner.lineno) - }\ - | seq(:ACTION) {|src| - UserAction.source_text(src) - } - } - - GrammarFileParser = grammar.parser_class - - if grammar.states.srconflict_exist? - raise 'Racc boot script fatal: S/R conflict in build' - end - if grammar.states.rrconflict_exist? - raise 'Racc boot script fatal: R/R conflict in build' - end - - class GrammarFileParser # reopen - - class Result - def initialize(grammar) - @grammar = grammar - @params = ParserFileGenerator::Params.new - end - - attr_reader :grammar - attr_reader :params - end - - def GrammarFileParser.parse_file(filename) - parse(File.read(filename), filename, 1) - end - - def GrammarFileParser.parse(src, filename = '-', lineno = 1) - new().parse(src, filename, lineno) - end - - def initialize(debug_flags = DebugFlags.new) - @yydebug = debug_flags.parse - end - - 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 - - private - - def next_token - @scanner.scan - end - - 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 location - "#{@filename}:#{@lineno - 1 + @scanner.lineno}" - end - - def add_rule_block(list) - sprec = nil - target = list.shift - case target - when OrMark, UserAction, Prec - raise CompileError, "#{target.lineno}: unexpected symbol #{target.name}" - end - curr = [] - list.each do |i| - case i - when OrMark - add_rule target, curr, sprec - curr = [] - sprec = nil - when Prec - raise CompileError, "'=<prec>' used twice in one rule" if sprec - sprec = i.symbol - else - curr.push i - end - end - add_rule target, curr, sprec - end - - 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 embedded_action(act) - sym = @grammar.intern("@#{@embedded_action_seq += 1}".intern, true) - @grammar.add Rule.new(sym, [], act) - sym - end - - # - # User Code Block - # - - def parse_user_code - line = @scanner.lineno - _, *blocks = *@scanner.epilogue.split(/^----/) - blocks.each do |block| - header, *body = block.lines.to_a - label0, pathes = *header.sub(/\A-+/, '').split('=', 2) - label = canonical_label(label0) - (pathes ? pathes.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 - - USER_CODE_LABELS = { - 'header' => :header, - 'prepare' => :header, # obsolete - 'inner' => :inner, - 'footer' => :footer, - 'driver' => :footer # obsolete - } - - 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 add_user_code(label, src) - @result.params.public_send(USER_CODE_LABELS[label]).push src - end - - end - - - class GrammarFileScanner - - def initialize(str, filename = '-') - @lines = str.b.split(/\n|\r\n|\r/) - @filename = filename - @lineno = -1 - @line_head = true - @in_rule_blk = false - @in_conv_blk = false - @in_block = nil - @epilogue = '' - @debug = false - next_line - end - - attr_reader :epilogue - - def lineno - @lineno + 1 - end - - attr_accessor :debug - - def yylex(&block) - unless @debug - yylex0(&block) - else - yylex0 do |sym, tok| - $stderr.printf "%7d %-10s %s\n", lineno(), sym.inspect, tok.inspect - yield [sym, tok] - end - end - end - - private - - def yylex0 - begin - until @line.empty? - @line.sub!(/\A\s+/, '') - if /\A\#/ =~ @line - break - elsif /\A\/\*/ =~ @line - skip_comment - elsif s = reads(/\A[a-zA-Z_]\w*/) - yield [atom_symbol(s), s.intern] - elsif s = reads(/\A\d+/) - yield [:DIGIT, s.to_i] - elsif ch = reads(/\A./) - case ch - when '"', "'" - yield [:STRING, eval(scan_quoted(ch))] - when '{' - lineno = lineno() - yield [:ACTION, SourceText.new(scan_action(), @filename, lineno)] - else - if ch == '|' - @line_head = false - end - yield [ch, ch] - end - else - end - end - end while next_line() - yield nil - end - - def next_line - @lineno += 1 - @line = @lines[@lineno] - if not @line or /\A----/ =~ @line - @epilogue = @lines.join("\n") - @lines.clear - @line = nil - if @in_block - @lineno -= 1 - scan_error! sprintf('unterminated %s', @in_block) - end - false - else - @line.sub!(/(?:\n|\r\n|\r)\z/, '') - @line_head = true - true - end - end - - ReservedWord = { - 'right' => :RIGHT, - 'left' => :LEFT, - 'nonassoc' => :NONASSOC, - 'preclow' => :PRECLOW, - 'prechigh' => :PRECHIGH, - 'token' => :TOKEN, - 'convert' => :CONV, - 'options' => :OPTION, - 'start' => :START, - 'expect' => :EXPECT, - 'class' => :CLASS, - 'rule' => :RULE, - 'end' => :END - } - - def atom_symbol(token) - if token == 'end' - symbol = :END - @in_conv_blk = false - @in_rule_blk = false - else - if @line_head and not @in_conv_blk and not @in_rule_blk - symbol = ReservedWord[token] || :SYMBOL - else - symbol = :SYMBOL - end - case symbol - when :RULE then @in_rule_blk = true - when :CONV then @in_conv_blk = true - end - end - @line_head = false - symbol - end - - def skip_comment - @in_block = 'comment' - until m = /\*\//.match(@line) - next_line - end - @line = m.post_match - @in_block = nil - end - - $raccs_print_type = false - - def scan_action - buf = String.new - nest = 1 - pre = nil - @in_block = 'action' - begin - pre = nil - if s = reads(/\A\s+/) - # does not set 'pre' - buf << s - end - until @line.empty? - if s = reads(/\A[^'"`{}%#\/\$]+/) - buf << (pre = s) - next - end - case ch = read(1) - when '{' - nest += 1 - buf << (pre = ch) - when '}' - nest -= 1 - if nest == 0 - @in_block = nil - buf.sub!(/[ \t\f]+\z/, '') - return buf - end - buf << (pre = ch) - when '#' # comment - buf << ch << @line - break - when "'", '"', '`' - buf << (pre = scan_quoted(ch)) - when '%' - if literal_head? pre, @line - # % string, regexp, array - buf << ch - case ch = read(1) - when /[qQx]/n - buf << ch << (pre = scan_quoted(read(1), '%string')) - when /wW/n - buf << ch << (pre = scan_quoted(read(1), '%array')) - when /s/n - buf << ch << (pre = scan_quoted(read(1), '%symbol')) - when /r/n - buf << ch << (pre = scan_quoted(read(1), '%regexp')) - when /[a-zA-Z0-9= ]/n # does not include "_" - scan_error! "unknown type of % literal '%#{ch}'" - else - buf << (pre = scan_quoted(ch, '%string')) - end - else - # operator - buf << '||op->' if $raccs_print_type - buf << (pre = ch) - end - when '/' - if literal_head? pre, @line - # regexp - buf << (pre = scan_quoted(ch, 'regexp')) - else - # operator - buf << '||op->' if $raccs_print_type - buf << (pre = ch) - end - when '$' # gvar - buf << ch << (pre = read(1)) - else - raise 'racc: fatal: must not happen' - end - end - buf << "\n" - end while next_line() - raise 'racc: fatal: scan finished before parser finished' - end - - def literal_head?(pre, post) - (!pre || /[a-zA-Z_0-9]/n !~ pre[-1,1]) && - !post.empty? && /\A[\s\=]/n !~ post - end - - def read(len) - s = @line[0, len] - @line = @line[len .. -1] - s - end - - def reads(re) - m = re.match(@line) or return nil - @line = m.post_match - m[0] - end - - def scan_quoted(left, tag = 'string') - buf = left.dup - buf = "||#{tag}->" + buf if $raccs_print_type - re = get_quoted_re(left) - sv, @in_block = @in_block, tag - begin - if s = reads(re) - buf << s - break - else - buf << @line - end - end while next_line() - @in_block = sv - buf << "<-#{tag}||" if $raccs_print_type - buf - end - - LEFT_TO_RIGHT = { - '(' => ')', - '{' => '}', - '[' => ']', - '<' => '>' - } - - CACHE = {} - - def get_quoted_re(left) - term = Regexp.quote(LEFT_TO_RIGHT[left] || left) - CACHE[left] ||= /\A[^#{term}\\]*(?:\\.[^\\#{term}]*)*#{term}/ - end - - def scan_error!(msg) - raise CompileError, "#{lineno()}: #{msg}" - end - - end - -end # module Racc |