diff options
Diffstat (limited to 'lib/racc/grammar.rb')
-rw-r--r-- | lib/racc/grammar.rb | 1118 |
1 files changed, 0 insertions, 1118 deletions
diff --git a/lib/racc/grammar.rb b/lib/racc/grammar.rb deleted file mode 100644 index 3444dfcce3..0000000000 --- a/lib/racc/grammar.rb +++ /dev/null @@ -1,1118 +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/compat' -require 'racc/iset' -require 'racc/sourcetext' -require 'racc/logfilegenerator' -require 'racc/exception' -require 'forwardable' - -module Racc - - class Grammar - - def initialize(debug_flags = DebugFlags.new) - @symboltable = SymbolTable.new - @debug_symbol = debug_flags.token - @rules = [] # :: [Rule] - @start = nil - @n_expected_srconflicts = nil - @prec_table = [] - @prec_table_closed = false - @closed = false - @states = nil - end - - attr_reader :start - attr_reader :symboltable - attr_accessor :n_expected_srconflicts - - def [](x) - @rules[x] - end - - def each_rule(&block) - @rules.each(&block) - end - - alias each each_rule - - def each_index(&block) - @rules.each_index(&block) - end - - def each_with_index(&block) - @rules.each_with_index(&block) - end - - def size - @rules.size - end - - def to_s - "<Racc::Grammar>" - end - - extend Forwardable - - def_delegator "@symboltable", :each, :each_symbol - def_delegator "@symboltable", :each_terminal - def_delegator "@symboltable", :each_nonterminal - - def intern(value, dummy = false) - @symboltable.intern(value, dummy) - end - - def symbols - @symboltable.symbols - end - - def nonterminal_base - @symboltable.nt_base - end - - def useless_nonterminal_exist? - n_useless_nonterminals() != 0 - end - - def n_useless_nonterminals - @n_useless_nonterminals ||= each_useless_nonterminal.count - end - - def each_useless_nonterminal - return to_enum __method__ unless block_given? - - @symboltable.each_nonterminal do |sym| - yield sym if sym.useless? - end - end - - def useless_rule_exist? - n_useless_rules() != 0 - end - - def n_useless_rules - @n_useless_rules ||= each_useless_rule.count - end - - def each_useless_rule - return to_enum __method__ unless block_given? - - each do |r| - yield r if r.useless? - end - end - - def nfa - (@states ||= States.new(self)).nfa - end - - def dfa - (@states ||= States.new(self)).dfa - end - - alias states dfa - - def state_transition_table - states().state_transition_table - end - - def parser_class - states = states() # cache - if $DEBUG - srcfilename = caller(1).first.slice(/\A(.*?):/, 1) - begin - write_log srcfilename + ".output" - rescue SystemCallError - end - report = lambda {|s| $stderr.puts "racc: #{srcfilename}: #{s}" } - if states.should_report_srconflict? - report["#{states.n_srconflicts} shift/reduce conflicts"] - end - if states.rrconflict_exist? - report["#{states.n_rrconflicts} reduce/reduce conflicts"] - end - g = states.grammar - if g.useless_nonterminal_exist? - report["#{g.n_useless_nonterminals} useless nonterminals"] - end - if g.useless_rule_exist? - report["#{g.n_useless_rules} useless rules"] - end - end - states.state_transition_table.parser_class - end - - def write_log(path) - File.open(path, 'w') {|f| - LogFileGenerator.new(states()).output f - } - end - - # - # Grammar Definition Interface - # - - def add(rule) - raise ArgumentError, "rule added after the Grammar closed" if @closed - @rules.push rule - end - - def added?(sym) - @rules.detect {|r| r.target == sym } - end - - def start_symbol=(s) - raise CompileError, "start symbol set twice'" if @start - @start = s - end - - def declare_precedence(assoc, syms) - raise CompileError, "precedence table defined twice" if @prec_table_closed - @prec_table.push [assoc, syms] - end - - def end_precedence_declaration(reverse) - @prec_table_closed = true - return if @prec_table.empty? - table = reverse ? @prec_table.reverse : @prec_table - table.each_with_index do |(assoc, syms), idx| - syms.each do |sym| - sym.assoc = assoc - sym.precedence = idx - end - end - end - - # - # Dynamic Generation Interface - # - - def Grammar.define(&block) - env = DefinitionEnv.new - env.instance_eval(&block) - env.grammar - end - - class DefinitionEnv - def initialize - @grammar = Grammar.new - @seqs = Hash.new(0) - @delayed = [] - end - - def grammar - flush_delayed - @grammar.each do |rule| - if rule.specified_prec - rule.specified_prec = @grammar.intern(rule.specified_prec) - end - end - @grammar.init - @grammar - end - - def precedence_table(&block) - env = PrecedenceDefinitionEnv.new(@grammar) - env.instance_eval(&block) - @grammar.end_precedence_declaration env.reverse - end - - def method_missing(mid, *args, &block) - unless mid.to_s[-1,1] == '=' - super # raises NoMethodError - end - target = @grammar.intern(mid.to_s.chop.intern) - unless args.size == 1 - raise ArgumentError, "too many arguments for #{mid} (#{args.size} for 1)" - end - _add target, args.first - end - - def _add(target, x) - case x - when Sym - @delayed.each do |rule| - rule.replace x, target if rule.target == x - end - @grammar.symboltable.delete x - else - x.each_rule do |r| - r.target = target - @grammar.add r - end - end - flush_delayed - end - - def _delayed_add(rule) - @delayed.push rule - end - - def _added?(sym) - @grammar.added?(sym) or @delayed.detect {|r| r.target == sym } - end - - def flush_delayed - return if @delayed.empty? - @delayed.each do |rule| - @grammar.add rule - end - @delayed.clear - end - - def seq(*list, &block) - Rule.new(nil, list.map {|x| _intern(x) }, UserAction.proc(block)) - end - - def null(&block) - seq(&block) - end - - def action(&block) - id = "@#{@seqs["action"] += 1}".intern - _delayed_add Rule.new(@grammar.intern(id), [], UserAction.proc(block)) - id - end - - alias _ action - - def option(sym, default = nil, &block) - _defmetasyntax("option", _intern(sym), block) {|target| - seq() { default } | seq(sym) - } - end - - def many(sym, &block) - _defmetasyntax("many", _intern(sym), block) {|target| - seq() { [] }\ - | seq(target, sym) {|list, x| list.push x; list } - } - end - - def many1(sym, &block) - _defmetasyntax("many1", _intern(sym), block) {|target| - seq(sym) {|x| [x] }\ - | seq(target, sym) {|list, x| list.push x; list } - } - end - - def separated_by(sep, sym, &block) - option(separated_by1(sep, sym), [], &block) - end - - def separated_by1(sep, sym, &block) - _defmetasyntax("separated_by1", _intern(sym), block) {|target| - seq(sym) {|x| [x] }\ - | seq(target, sep, sym) {|list, _, x| list.push x; list } - } - end - - def _intern(x) - case x - when Symbol, String - @grammar.intern(x) - when Racc::Sym - x - else - raise TypeError, "wrong type #{x.class} (expected Symbol/String/Racc::Sym)" - end - end - - private - - def _defmetasyntax(type, id, action, &block) - if action - idbase = "#{type}@#{id}-#{@seqs[type] += 1}" - target = _wrap(idbase, "#{idbase}-core", action) - _regist("#{idbase}-core", &block) - else - target = _regist("#{type}@#{id}", &block) - end - @grammar.intern(target) - end - - def _regist(target_name) - target = target_name.intern - unless _added?(@grammar.intern(target)) - yield(target).each_rule do |rule| - rule.target = @grammar.intern(target) - _delayed_add rule - end - end - target - end - - def _wrap(target_name, sym, block) - target = target_name.intern - _delayed_add Rule.new(@grammar.intern(target), - [@grammar.intern(sym.intern)], - UserAction.proc(block)) - target - end - end - - class PrecedenceDefinitionEnv - def initialize(g) - @grammar = g - @prechigh_seen = false - @preclow_seen = false - @reverse = false - end - - attr_reader :reverse - - def higher - if @prechigh_seen - raise CompileError, "prechigh used twice" - end - @prechigh_seen = true - end - - def lower - if @preclow_seen - raise CompileError, "preclow used twice" - end - if @prechigh_seen - @reverse = true - end - @preclow_seen = true - end - - def left(*syms) - @grammar.declare_precedence :Left, syms.map {|s| @grammar.intern(s) } - end - - def right(*syms) - @grammar.declare_precedence :Right, syms.map {|s| @grammar.intern(s) } - end - - def nonassoc(*syms) - @grammar.declare_precedence :Nonassoc, syms.map {|s| @grammar.intern(s)} - end - end - - # - # Computation - # - - def init - return if @closed - @closed = true - @start ||= @rules.map {|r| r.target }.detect {|sym| not sym.dummy? } - raise CompileError, 'no rule in input' if @rules.empty? - add_start_rule - @rules.freeze - fix_ident - compute_hash - compute_heads - determine_terminals - compute_nullable_0 - @symboltable.fix - compute_locate - @symboltable.each_nonterminal {|t| compute_expand t } - compute_nullable - compute_useless - end - - private - - def add_start_rule - r = Rule.new(@symboltable.dummy, - [@start, @symboltable.anchor, @symboltable.anchor], - UserAction.empty) - r.ident = 0 - r.hash = 0 - r.precedence = nil - @rules.unshift r - end - - # Rule#ident - # LocationPointer#ident - def fix_ident - @rules.each_with_index do |rule, idx| - rule.ident = idx - end - end - - # Rule#hash - def compute_hash - hash = 4 # size of dummy rule - @rules.each do |rule| - rule.hash = hash - hash += (rule.size + 1) - end - end - - # Sym#heads - def compute_heads - @rules.each do |rule| - rule.target.heads.push rule.ptrs[0] - end - end - - # Sym#terminal? - def determine_terminals - @symboltable.each do |s| - s.term = s.heads.empty? - end - end - - # Sym#self_null? - def compute_nullable_0 - @symboltable.each do |s| - if s.terminal? - s.snull = false - else - s.snull = s.heads.any? {|loc| loc.reduce? } - end - end - end - - # Sym#locate - def compute_locate - @rules.each do |rule| - t = nil - rule.ptrs.each do |ptr| - unless ptr.reduce? - tok = ptr.dereference - tok.locate.push ptr - t = tok if tok.terminal? - end - end - rule.precedence = t - end - end - - # Sym#expand - def compute_expand(t) - puts "expand> #{t.to_s}" if @debug_symbol - t.expand = _compute_expand(t, ISet.new, []) - puts "expand< #{t.to_s}: #{t.expand.to_s}" if @debug_symbol - end - - def _compute_expand(t, set, lock) - if tmp = t.expand - set.update tmp - return set - end - tok = nil - set.update_a t.heads - t.heads.each do |ptr| - tok = ptr.dereference - if tok and tok.nonterminal? - unless lock[tok.ident] - lock[tok.ident] = true - _compute_expand tok, set, lock - end - end - end - set - end - - # Sym#nullable?, Rule#nullable? - def compute_nullable - @rules.each {|r| r.null = false } - @symboltable.each {|t| t.null = false } - r = @rules.dup - s = @symboltable.nonterminals - begin - rs = r.size - ss = s.size - check_rules_nullable r - check_symbols_nullable s - end until rs == r.size and ss == s.size - end - - def check_rules_nullable(rules) - rules.delete_if do |rule| - rule.null = true - rule.symbols.each do |t| - unless t.nullable? - rule.null = false - break - end - end - rule.nullable? - end - end - - def check_symbols_nullable(symbols) - symbols.delete_if do |sym| - sym.heads.each do |ptr| - if ptr.rule.nullable? - sym.null = true - break - end - end - sym.nullable? - end - end - - # Sym#useless?, Rule#useless? - # FIXME: what means "useless"? - def compute_useless - @symboltable.each_terminal {|sym| sym.useless = false } - @symboltable.each_nonterminal {|sym| sym.useless = true } - @rules.each {|rule| rule.useless = true } - r = @rules.dup - s = @symboltable.nonterminals - begin - rs = r.size - ss = s.size - check_rules_useless r - check_symbols_useless s - end until r.size == rs and s.size == ss - end - - def check_rules_useless(rules) - rules.delete_if do |rule| - rule.useless = false - rule.symbols.each do |sym| - if sym.useless? - rule.useless = true - break - end - end - not rule.useless? - end - end - - def check_symbols_useless(s) - s.delete_if do |t| - t.heads.each do |ptr| - unless ptr.rule.useless? - t.useless = false - break - end - end - not t.useless? - end - end - - end # class Grammar - - - class Rule - - def initialize(target, syms, act) - @target = target - @symbols = syms - @action = act - @alternatives = [] - - @ident = nil - @hash = nil - @ptrs = nil - @precedence = nil - @specified_prec = nil - @null = nil - @useless = nil - end - - attr_accessor :target - attr_reader :symbols - attr_reader :action - - def |(x) - @alternatives.push x.rule - self - end - - def rule - self - end - - def each_rule(&block) - yield self - @alternatives.each(&block) - end - - attr_accessor :ident - - attr_reader :hash - attr_reader :ptrs - - def hash=(n) - @hash = n - ptrs = [] - @symbols.each_with_index do |sym, idx| - ptrs.push LocationPointer.new(self, idx, sym) - end - ptrs.push LocationPointer.new(self, @symbols.size, nil) - @ptrs = ptrs - end - - def precedence - @specified_prec || @precedence - end - - def precedence=(sym) - @precedence ||= sym - end - - def prec(sym, &block) - @specified_prec = sym - if block - unless @action.empty? - raise CompileError, 'both of rule action block and prec block given' - end - @action = UserAction.proc(block) - end - self - end - - attr_accessor :specified_prec - - def nullable?() @null end - def null=(n) @null = n end - - def useless?() @useless end - def useless=(u) @useless = u end - - def inspect - "#<Racc::Rule id=#{@ident} (#{@target})>" - end - - def ==(other) - other.kind_of?(Rule) and @ident == other.ident - end - - def [](idx) - @symbols[idx] - end - - def size - @symbols.size - end - - def empty? - @symbols.empty? - end - - def to_s - "#<rule#{@ident}>" - end - - def accept? - if tok = @symbols[-1] - tok.anchor? - else - false - end - end - - def each(&block) - @symbols.each(&block) - end - - def replace(src, dest) - @target = dest - @symbols = @symbols.map {|s| s == src ? dest : s } - end - - end # class Rule - - - class UserAction - - def UserAction.source_text(src) - new(src, nil) - end - - def UserAction.proc(pr = nil, &block) - if pr and block - raise ArgumentError, "both of argument and block given" - end - new(nil, pr || block) - end - - def UserAction.empty - new(nil, nil) - end - - private_class_method :new - - def initialize(src, proc) - @source = src - @proc = proc - end - - attr_reader :source - attr_reader :proc - - def source? - not @proc - end - - def proc? - not @source - end - - def empty? - not @proc and not @source - end - - def name - "{action type=#{@source || @proc || 'nil'}}" - end - - alias inspect name - - end - - - class OrMark - def initialize(lineno) - @lineno = lineno - end - - def name - '|' - end - - alias inspect name - - attr_reader :lineno - end - - - class Prec - def initialize(symbol, lineno) - @symbol = symbol - @lineno = lineno - end - - def name - "=#{@symbol}" - end - - alias inspect name - - attr_reader :symbol - attr_reader :lineno - end - - - # - # A set of rule and position in it's RHS. - # Note that the number of pointers is more than rule's RHS array, - # because pointer points right edge of the final symbol when reducing. - # - class LocationPointer - - def initialize(rule, i, sym) - @rule = rule - @index = i - @symbol = sym - @ident = @rule.hash + i - @reduce = sym.nil? - end - - attr_reader :rule - attr_reader :index - attr_reader :symbol - - alias dereference symbol - - attr_reader :ident - alias hash ident - attr_reader :reduce - alias reduce? reduce - - def to_s - sprintf('(%d,%d %s)', - @rule.ident, @index, (reduce?() ? '#' : @symbol.to_s)) - end - - alias inspect to_s - - def eql?(ot) - @hash == ot.hash - end - - alias == eql? - - def head? - @index == 0 - end - - def next - @rule.ptrs[@index + 1] or ptr_bug! - end - - alias increment next - - def before(len) - @rule.ptrs[@index - len] or ptr_bug! - end - - private - - def ptr_bug! - raise "racc: fatal: pointer not exist: self: #{to_s}" - end - - end # class LocationPointer - - - class SymbolTable - - include Enumerable - - def initialize - @symbols = [] # :: [Racc::Sym] - @cache = {} # :: {(String|Symbol) => Racc::Sym} - @dummy = intern(:$start, true) - @anchor = intern(false, true) # Symbol ID = 0 - @error = intern(:error, false) # Symbol ID = 1 - end - - attr_reader :dummy - attr_reader :anchor - attr_reader :error - - def [](id) - @symbols[id] - end - - def intern(val, dummy = false) - @cache[val] ||= - begin - sym = Sym.new(val, dummy) - @symbols.push sym - sym - end - end - - attr_reader :symbols - alias to_a symbols - - def delete(sym) - @symbols.delete sym - @cache.delete sym.value - end - - attr_reader :nt_base - - def nt_max - @symbols.size - end - - def each(&block) - @symbols.each(&block) - end - - def terminals(&block) - @symbols[0, @nt_base] - end - - def each_terminal(&block) - @terms.each(&block) - end - - def nonterminals - @symbols[@nt_base, @symbols.size - @nt_base] - end - - def each_nonterminal(&block) - @nterms.each(&block) - end - - def fix - terms, nterms = @symbols.partition {|s| s.terminal? } - @symbols = terms + nterms - @terms = terms - @nterms = nterms - @nt_base = terms.size - fix_ident - check_terminals - end - - private - - def fix_ident - @symbols.each_with_index do |t, i| - t.ident = i - end - end - - def check_terminals - return unless @symbols.any? {|s| s.should_terminal? } - @anchor.should_terminal - @error.should_terminal - each_terminal do |t| - t.should_terminal if t.string_symbol? - end - each do |s| - s.should_terminal if s.assoc - end - terminals().reject {|t| t.should_terminal? }.each do |t| - raise CompileError, "terminal #{t} not declared as terminal" - end - nonterminals().select {|n| n.should_terminal? }.each do |n| - raise CompileError, "symbol #{n} declared as terminal but is not terminal" - end - end - - end # class SymbolTable - - - # Stands terminal and nonterminal symbols. - class Sym - - def initialize(value, dummyp) - @ident = nil - @value = value - @dummyp = dummyp - - @term = nil - @nterm = nil - @should_terminal = false - @precedence = nil - case value - when Symbol - @to_s = value.to_s - @serialized = value.inspect - @string = false - when String - @to_s = value.inspect - @serialized = value.dump - @string = true - when false - @to_s = '$end' - @serialized = 'false' - @string = false - when ErrorSymbolValue - @to_s = 'error' - @serialized = 'Object.new' - @string = false - else - raise ArgumentError, "unknown symbol value: #{value.class}" - end - - @heads = [] - @locate = [] - @snull = nil - @null = nil - @expand = nil - @useless = nil - end - - class << self - def once_writer(nm) - nm = nm.id2name - module_eval(<<-EOS) - def #{nm}=(v) - raise 'racc: fatal: @#{nm} != nil' unless @#{nm}.nil? - @#{nm} = v - end - EOS - end - end - - once_writer :ident - attr_reader :ident - - alias hash ident - - attr_reader :value - - def dummy? - @dummyp - end - - def terminal? - @term - end - - def nonterminal? - @nterm - end - - def term=(t) - raise 'racc: fatal: term= called twice' unless @term.nil? - @term = t - @nterm = !t - end - - def should_terminal - @should_terminal = true - end - - def should_terminal? - @should_terminal - end - - def string_symbol? - @string - end - - def serialize - @serialized - end - - attr_writer :serialized - - attr_accessor :precedence - attr_accessor :assoc - - def to_s - @to_s.dup - end - - alias inspect to_s - - def |(x) - rule() | x.rule - end - - def rule - Rule.new(nil, [self], UserAction.empty) - end - - # - # cache - # - - attr_reader :heads - attr_reader :locate - - def self_null? - @snull - end - - once_writer :snull - - def nullable? - @null - end - - def null=(n) - @null = n - end - - attr_reader :expand - once_writer :expand - - def useless? - @useless - end - - def useless=(f) - @useless = f - end - - end # class Sym - -end # module Racc |