lib/antelope/generation/constructor/first.rb in antelope-0.2.0 vs lib/antelope/generation/constructor/first.rb in antelope-0.2.2

- old
+ new

@@ -1,88 +1,88 @@ -# encoding: utf-8 - -module Antelope - module Generation - class Constructor - - # Contains the methods to construct first sets for tokens. - module First - - # Initialize. - def initialize - @firstifying = [] - super - end - - # Constructs the first set for a given token. This is how - # the method should behave: - # - # FIRST(ε) == [] # if ε is the epsilon token - # FIRST(x) == [x] # if x is a terminal - # FIRST(αβ) == if nullable?(α) - # FIRST(α) U FIRST(β) - # else - # FIRST(α) - # end - # FIRST(A) == FIRST(a_1) U FIRST(a_2) U ... U FIRST(a_n) - # # if A is a nonterminal and a_1, a_2, ..., a_3 are all - # # of the right-hand sides of its productions. - # - # @param token [Ace::Token, Array<Ace::Token>] - # @return [Set<Ace::Token::Terminal>] - # @see #first_array - def first(token) - case token - when Ace::Token::Nonterminal - firstifying(token) do - productions = grammar.productions[token.name] - productions.map { |prod| - first(prod[:items]) }.inject(Set.new, :+) - end - when Array - first_array(token) - when Ace::Token::Epsilon - Set.new - when Ace::Token::Terminal - Set.new([token]) - else - incorrect_argument! token, Ace::Token, Array - end - end - - private - - # Determines the FIRST set of an array of tokens. First, it - # removes any terminals we are finding the FIRST set for; - # then, it determines which tokens we have to find the FIRST - # sets for (since some tokens may be nullable). We then add - # those sets to our set. - # - # @param tokens [Array<Ace::Token>] - # @return [Set<Ace::Token>] - def first_array(tokens) - tokens.dup.delete_if { |_| @firstifying.include?(_) }. - each_with_index.take_while do |token, i| - if i.zero? - true - else - nullable?(tokens[i - 1]) - end - end.map(&:first).map { |_| first(_) }.inject(Set.new, :+) - end - - # Helps keep track of the nonterminals we're finding FIRST - # sets for. This helps prevent recursion. - # - # @param tok [Ace::Token::Nonterminal] - # @yield once. - # @return [Set<Ace::Token>] - def firstifying(tok) - @firstifying << tok - out = yield - @firstifying.delete tok - out - end - end - end - end -end +# encoding: utf-8 + +module Antelope + module Generation + class Constructor + + # Contains the methods to construct first sets for tokens. + module First + + # Initialize. + def initialize + @firstifying = [] + super + end + + # Constructs the first set for a given token. This is how + # the method should behave: + # + # FIRST(ε) == [] # if ε is the epsilon token + # FIRST(x) == [x] # if x is a terminal + # FIRST(αβ) == if nullable?(α) + # FIRST(α) U FIRST(β) + # else + # FIRST(α) + # end + # FIRST(A) == FIRST(a_1) U FIRST(a_2) U ... U FIRST(a_n) + # # if A is a nonterminal and a_1, a_2, ..., a_3 are all + # # of the right-hand sides of its productions. + # + # @param token [Ace::Token, Array<Ace::Token>] + # @return [Set<Ace::Token::Terminal>] + # @see #first_array + def first(token) + case token + when Ace::Token::Nonterminal + firstifying(token) do + productions = grammar.productions[token.name] + productions.map { |prod| + first(prod[:items]) }.inject(Set.new, :+) + end + when Array + first_array(token) + when Ace::Token::Epsilon + Set.new + when Ace::Token::Terminal + Set.new([token]) + else + incorrect_argument! token, Ace::Token, Array + end + end + + private + + # Determines the FIRST set of an array of tokens. First, it + # removes any terminals we are finding the FIRST set for; + # then, it determines which tokens we have to find the FIRST + # sets for (since some tokens may be nullable). We then add + # those sets to our set. + # + # @param tokens [Array<Ace::Token>] + # @return [Set<Ace::Token>] + def first_array(tokens) + tokens.dup.delete_if { |_| @firstifying.include?(_) }. + each_with_index.take_while do |token, i| + if i.zero? + true + else + nullable?(tokens[i - 1]) + end + end.map(&:first).map { |_| first(_) }.inject(Set.new, :+) + end + + # Helps keep track of the nonterminals we're finding FIRST + # sets for. This helps prevent recursion. + # + # @param tok [Ace::Token::Nonterminal] + # @yield once. + # @return [Set<Ace::Token>] + def firstifying(tok) + @firstifying << tok + out = yield + @firstifying.delete tok + out + end + end + end + end +end