How To Build a Yacc?(12)

生成DFA的第1步,计算first集合和follow集合。

first_set和follow_set都是一个hast set结构,这个hash的key是一个 vocab,而value是一个集合,用一个array表示,这与普通的hash不同,因此写了一个HashDup的module,其中重写了hash的store方法,用来满足上述要求:

###### hashdup.rb ###########
module HashDup
  def store(key, value)
    return if !value
    if self.has_key?(key)     
      self[key].push(value)
    else
      self[key] = [value]     
    end
    self[key].flatten!
    self[key].uniq!
  end
 
  def eql?(other)
    self.each_pair do |key, value|
      if !other[key].eql?(value)
        return false
      end
    end
    return true   
  end
end

其中eql?方法十分有用,在计算first和follow集合时,每遍循环都要检查集合是否有变化以决定集合是否计算终止。

class DFA
  def initialize()
    @first_set = Hash.new
    @follow_set = Hash.new
    @first_set.extend(HashDup)
    @follow_set.extend(HashDup)
  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

end


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页