当前位置: 首页 > 工具软件 > Berkeley Yacc > 使用案例 >

How To Build a Yacc?(13)

颛孙麻雀
2023-12-01

实际上,有了上面的准备后,计算DFA的算法很清楚:

class DFA
  SHIFT = 1
  REDUCE = 2
  ERROR = 3
  ACCEPT = 4
 
  def initialize()
    @state_set = Array.new
    
    @current_state = 0    
    @max_state = 0
    @action_table = Hash.new
    
    @first_set = Hash.new
    @follow_set = Hash.new
    @first_set.extend(HashDup)
    @follow_set.extend(HashDup)
  end
 
  def token_type(token, parser)
    parser.vocabs.identify(token)   
  end
 
  def action(state, token)
    key = "#{state},#{token}"
    return @action_table[key]
  end
 
  ########################################################
  # 生成DFA
  # 首先计算first, follow集合, 产生第一个状态,然后依次产生每一个后继
  ########################################################
  def generate(parser)
    calc_first_set(parser)
    calc_follow_set(parser)
    #@state_set.push(generate_first_state(parser))
    #dump_first_follow
    @state_set[@current_state] = generate_first_state(parser)
    #p "fisrt state: #{@state_set[@current_state].to_s}"
    while @current_state <= @max_state
      successors(@current_state, parser)
      @current_state = @current_state + 1
    end    
    @action_table.store("0,nil", [ACCEPT, 0])
    @action_table.store("0,$", [ACCEPT, 0])
  end
 
  ########################################################
  # 求DFA的第一个状态
  # 我们把nil = #S的item闭包作为第一个状态,其中S是开始符号
  ########################################################
  def generate_first_state(parser)  
    itemset = Array.new
    parser.rules.each do |rule|
      #p "DFA::#{rule}"
      if token_type(rule.lt, parser) == Vocab::NULL
        #p "DFA::match nil rule #{rule}"
        itemset.push(Item.new(rule, 0))
      end
    end
    first_state = closure(itemset, parser)
  end  
 
  ########################################################
  # 求一个状态的闭包
  # 对于状态集合中的任意一个item: S = av#BC, 如果B是nonterminal
  # 那么把所有rule中rule.lt = B的rule加入到这个闭包中
  ########################################################
  def closure(itemset, parser)    
    oldset = nil
    begin      
      itemset.each do |item|    
        oldset = itemset.dup    
        nt = item.next_token
        if !item.is_end? && token_type(nt, parser) == Vocab::NONTERMINAL
          additem = Array.new
          parser.rules.each do |rule|
            if rule.lt == nt
              expand = Item.new(rule, 0)
              additem.push(expand) if (!itemset.include?(expand))
            end            
          end            
          itemset.concat(additem)
        end
      end
    end while !oldset.eql?(itemset) # end begin...end while
    return itemset
  end
 
  ########################################################
  # 由item: S = a#vBC前进到 S = av#BC
  ########################################################
  def advance(itemset)
    newitemset = Array.new
    itemset.each do |item|     
      newitemset.push(item.step)
    end    
    return newitemset
  end
 
  ########################################################
  # 求每一个状态的所有后继
  # 对于状态s中任意一个item:
  # 1. 如果存在item: S = a#vBC, 那么当下一个 token是v时,意味着
  # 将v进行shift操作,并将状态转移到下一个状态closure(S = av#BC);
  # 2. 如果存在item: S = avBC#, 那么当下一个token在follow(S)中
  # 意味着需要救星reduce操作,将stack里的avBC序列替换为S, 并移动到
  # 下一个状态 goto(stack.last, S)
  ########################################################
  def successors(state, parser)
    itemset = @state_set[state]    
    parser.vocabs.each_token do |token|
      key = "#{state},#{token}"
      # 找到所有 s = a.vc中v=token的item
      next_items = itemset.find_all { |item| item.next_token == token }
      if !next_items.empty?
        next_items_c = closure(advance(next_items), parser)       
        # 检查next_items_s是否已经在状态表中        
        next_state_no = @state_set.index(next_items_c)
        if !next_state_no
          next_state_no = @max_state + 1
          @max_state = next_state_no
          @state_set[next_state_no] = next_items_c
        end        
        
        @action_table.store(key, [SHIFT, next_state_no])
      end
      
      # 找到所有 s= av. 的rule, 并将@follow_set(rule.rt.last)
      end_items = itemset.find_all { |item| item.is_end? == true }
      if !end_items.empty?
        end_items.each do |item|
          if @follow_set[item.rule.lt].include?(token)
            @action_table.store(key, [REDUCE, end_items])
          end
        end
      end
      
      # 如果没有任何可用的项目
      #@action_table.store(key, [ERROR, nil]) until @action_table[key]       
    end
  end  
    
  ########################################################
  # 计算token的first集合
  # 对于terminal, first(terminal) = [terminal]
  # 对于nonterminal S, 如果有S = aBC, first(S) = first(aBC)
  # if a -> nil , first(aBC) = first(BC), 依次类推
  # if a not-> nil, first(aBC) = first(a).
  ########################################################
  def calc_first_set(parser)
    parser.vocabs.each_terminal do |terminal|
      @first_set.store(terminal, terminal)
    end
    
    begin   
      old_first_set = @first_set.dup
      parser.vocabs.each_nonterminal do |nonterminal|
        parser.rules.each do |rule|
          if rule.lt == nonterminal
            if !rule.rt.empty? && @first_set[rule.rt[0]]
              @first_set.store(nonterminal, @first_set[rule.rt[0]])
            end
          end
        end
      end   
    end while @first_set.eql?(old_first_set)
    return @first_set
  end
 
  ########################################################
  # 计算token的follow集合
  # 对每个rule(产生式进行遍历)
  # S = aBC, 每个rule右边的产生序列(rule.rt=aBC)的每一个非结尾符号
  # 比如a,B; follow集合对于紧邻符号的first集合;follow(a) = fisrt(B).
  # 而每一个结尾符号,其follow集合等于左边非终结符的follow集合
  # follow(C) = follow(S)
  ########################################################
  def calc_follow_set(parser)
    begin
      old_follow_set = @follow_set.dup
      parser.rules.each do |rule|
        if token_type(rule.lt, parser) == Vocab::NULL
          @follow_set.store(rule.lt, Vocab.eofs)
        end
        for i in 0...rule.rt.length
          if i < rule.rt.length-1
            @follow_set.store(rule.rt[i], @first_set[rule.rt[i+1]])
          else
            @follow_set.store(rule.rt[i], @follow_set[rule.lt])
          end
        end #end for
      end #end parser.rules.each
    end while !@follow_set.eql?(old_follow_set)
    return @follow_set
  end
 
  #### debug util function################
  def dump_state_set
    index = 0
    @state_set.each do |state|
      p "state:#{index}, item:#{state.to_s}"
      index = index + 1
    end
  end
 
  def dump_action_table
    p "[action table]:"
    @action_table.each_pair do |key, value|
      cond = key.gsub(/,(.*)/, '(/1)')      
      p "#{cond} -->  [#{DFA.action_name(value[0])}], #{value[1]}"
    end
  end
 
  def dump_first_follow
    p "first: #{@first_set.inspect}"
    p "follow: #{@follow_set.inspect}"
  end
 
  def DFA.action_name(action)
    DFA.constants.each do |x|
      return x if eval(x) == action      
    end
    return "unknown action"
  end
 
  #attr_reader :state_set, :action_table, :goto_table
end



而Yacc这时的实现也仅仅是转调一下DFA的方法而已:
class Yacc
  def initialize(file_name)
    @parser = RuleParser.new(file_name)
    @dfa = DFA.new
  end
 
  def rule_parser
    @parser
  end 
 
  def dfa
    @dfa
  end
 
  def generate
    @dfa.generate(@parser)
  end 
end


回头运行一下我们的test_yacc,看看有什么结果?     
 类似资料: