lib/antelope/generation/constructor/first.rb in antelope-0.0.1 vs lib/antelope/generation/constructor/first.rb in antelope-0.1.0
- old
+ new
@@ -1,20 +1,40 @@
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 = parser.productions[token.name]
+ productions = grammar.productions[token.name]
productions.map { |prod|
first(prod[:items]) }.inject(Set.new, :+)
end
when Array
first_array(token)
@@ -27,20 +47,34 @@
end
end
private
- def first_array(token)
- token.dup.delete_if { |tok| @firstifying.include?(tok) }.
- each_with_index.take_while do |tok, i|
+ # 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?(token[i - 1])
+ nullable?(tokens[i - 1])
end
- end.map(&:first).map { |tok| first(tok) }.inject(Set.new, :+)
+ 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