antelope 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +25 -23
  3. data/.rspec +3 -3
  4. data/.travis.yml +10 -9
  5. data/.yardopts +7 -7
  6. data/CONTRIBUTING.md +38 -38
  7. data/GENERATORS.md +124 -124
  8. data/Gemfile +7 -7
  9. data/LICENSE.txt +22 -22
  10. data/README.md +104 -104
  11. data/Rakefile +2 -2
  12. data/TODO.md +58 -58
  13. data/antelope.gemspec +28 -28
  14. data/bin/antelope +7 -7
  15. data/examples/deterministic.ace +35 -35
  16. data/examples/example.ace +51 -50
  17. data/examples/example.err +192 -0
  18. data/examples/{example.output → example.inf} +384 -385
  19. data/examples/liquidscript.ace +233 -162
  20. data/examples/simple.ace +22 -22
  21. data/lib/antelope/ace/compiler.rb +334 -334
  22. data/lib/antelope/ace/errors.rb +48 -48
  23. data/lib/antelope/ace/grammar/generation.rb +80 -80
  24. data/lib/antelope/ace/grammar/loading.rb +53 -53
  25. data/lib/antelope/ace/grammar/precedences.rb +68 -65
  26. data/lib/antelope/ace/grammar/productions.rb +156 -150
  27. data/lib/antelope/ace/grammar/symbols.rb +66 -66
  28. data/lib/antelope/ace/grammar.rb +69 -69
  29. data/lib/antelope/ace/precedence.rb +61 -61
  30. data/lib/antelope/ace/production.rb +57 -57
  31. data/lib/antelope/ace/scanner/argument.rb +57 -57
  32. data/lib/antelope/ace/scanner/first.rb +89 -89
  33. data/lib/antelope/ace/scanner/second.rb +177 -177
  34. data/lib/antelope/ace/scanner/third.rb +27 -27
  35. data/lib/antelope/ace/scanner.rb +134 -134
  36. data/lib/antelope/ace/token/epsilon.rb +24 -24
  37. data/lib/antelope/ace/token/error.rb +26 -26
  38. data/lib/antelope/ace/token/nonterminal.rb +17 -17
  39. data/lib/antelope/ace/token/terminal.rb +17 -17
  40. data/lib/antelope/ace/token.rb +238 -238
  41. data/lib/antelope/ace.rb +53 -53
  42. data/lib/antelope/cli.rb +55 -55
  43. data/lib/antelope/errors.rb +8 -8
  44. data/lib/antelope/generation/constructor/first.rb +88 -88
  45. data/lib/antelope/generation/constructor/follow.rb +103 -103
  46. data/lib/antelope/generation/constructor/nullable.rb +64 -64
  47. data/lib/antelope/generation/constructor.rb +126 -126
  48. data/lib/antelope/generation/errors.rb +17 -17
  49. data/lib/antelope/generation/null.rb +13 -13
  50. data/lib/antelope/generation/recognizer/rule.rb +216 -216
  51. data/lib/antelope/generation/recognizer/state.rb +130 -130
  52. data/lib/antelope/generation/recognizer.rb +180 -180
  53. data/lib/antelope/generation/tableizer.rb +175 -154
  54. data/lib/antelope/generation.rb +15 -15
  55. data/lib/antelope/generator/base.rb +264 -264
  56. data/lib/antelope/generator/c.rb +11 -11
  57. data/lib/antelope/generator/c_header.rb +105 -105
  58. data/lib/antelope/generator/c_source.rb +39 -39
  59. data/lib/antelope/generator/error.rb +34 -0
  60. data/lib/antelope/generator/group.rb +57 -57
  61. data/lib/antelope/generator/html.rb +51 -0
  62. data/lib/antelope/generator/info.rb +47 -0
  63. data/lib/antelope/generator/null.rb +18 -18
  64. data/lib/antelope/generator/output.rb +17 -49
  65. data/lib/antelope/generator/ruby.rb +79 -79
  66. data/lib/antelope/generator/templates/c_header.ant +36 -36
  67. data/lib/antelope/generator/templates/c_source.ant +202 -202
  68. data/lib/antelope/generator/templates/error.ant +33 -0
  69. data/lib/antelope/generator/templates/html/antelope.css +1 -0
  70. data/lib/antelope/generator/templates/html/antelope.html +1 -0
  71. data/lib/antelope/generator/templates/html/antelope.js +1 -0
  72. data/lib/antelope/generator/templates/html/css.ant +53 -0
  73. data/lib/antelope/generator/templates/html/html.ant +82 -0
  74. data/lib/antelope/generator/templates/html/js.ant +9 -0
  75. data/lib/antelope/generator/templates/info.ant +53 -0
  76. data/lib/antelope/generator/templates/ruby.ant +178 -146
  77. data/lib/antelope/generator.rb +66 -63
  78. data/lib/antelope/template/compiler.rb +78 -78
  79. data/lib/antelope/template/errors.rb +9 -9
  80. data/lib/antelope/template/scanner.rb +109 -109
  81. data/lib/antelope/template.rb +65 -60
  82. data/lib/antelope/version.rb +6 -6
  83. data/lib/antelope.rb +13 -13
  84. data/optimizations.txt +42 -0
  85. data/spec/antelope/ace/compiler_spec.rb +60 -60
  86. data/spec/antelope/ace/scanner_spec.rb +27 -27
  87. data/spec/antelope/constructor_spec.rb +133 -136
  88. data/spec/antelope/template_spec.rb +50 -49
  89. data/spec/fixtures/simple.ace +22 -22
  90. data/spec/spec_helper.rb +39 -39
  91. data/spec/support/benchmark_helper.rb +5 -5
  92. data/spec/support/grammar_helper.rb +15 -15
  93. data/subl/Ace (Ruby).JSON-tmLanguage +94 -94
  94. data/subl/Ace (Ruby).tmLanguage +153 -153
  95. metadata +17 -6
  96. data/lib/antelope/generator/templates/output.ant +0 -68
data/lib/antelope/ace.rb CHANGED
@@ -1,53 +1,53 @@
1
- # encoding: utf-8
2
-
3
- require "antelope/ace/errors"
4
- require "antelope/ace/scanner"
5
- require "antelope/ace/compiler"
6
- require "antelope/ace/token"
7
- require "antelope/ace/precedence"
8
- require "antelope/ace/production"
9
- require "antelope/ace/grammar"
10
-
11
- module Antelope
12
-
13
- # Defines the Ace file. The Ace file format works similarly to
14
- # bison's y file format. The Ace file is seperated into three
15
- # parts:
16
- #
17
- # <first>
18
- # %%
19
- # <second>
20
- # %%
21
- # <third>
22
- #
23
- # All parts may be empty; thus, the minimal file that Ace will
24
- # accept would be
25
- #
26
- # %%
27
- # %%
28
- #
29
- # The first part consists of _directives_ and _blocks_; directives
30
- # look something like `"%" <directive>[ <argument>]*\n`, with
31
- # `<directive>` being any alphanumerical character, including
32
- # underscores and dashes, and `<argument>` being any word character
33
- # or a quote-delimited string. Blocks consist of
34
- # `"%{" <content> "\n" "\s"* "%}"`, with `<content>` being any
35
- # characters. The content is copied directly into the body of the
36
- # output.
37
- #
38
- # The second part consists of rules. Rules look something like
39
- # this:
40
- #
41
- # <nonterminal>: (<nonterminal> | <terminal>)* ["{" <content> "}"] ["|" (<nonterminal> | <terminal>)* ["{" <content> "}"]]* [;]
42
- #
43
- # Where `<nonterminal>` is any lowercase alphabetical cahracter,
44
- # `<terminal>` is any uppercase alphabetical character, and
45
- # `<content>` is code to be used in the output file upon matching
46
- # the specific rule.
47
- #
48
- # The third part consists of a body, which is copied directly into
49
- # the output.
50
- module Ace
51
-
52
- end
53
- end
1
+ # encoding: utf-8
2
+
3
+ require "antelope/ace/errors"
4
+ require "antelope/ace/scanner"
5
+ require "antelope/ace/compiler"
6
+ require "antelope/ace/token"
7
+ require "antelope/ace/precedence"
8
+ require "antelope/ace/production"
9
+ require "antelope/ace/grammar"
10
+
11
+ module Antelope
12
+
13
+ # Defines the Ace file. The Ace file format works similarly to
14
+ # bison's y file format. The Ace file is seperated into three
15
+ # parts:
16
+ #
17
+ # <first>
18
+ # %%
19
+ # <second>
20
+ # %%
21
+ # <third>
22
+ #
23
+ # All parts may be empty; thus, the minimal file that Ace will
24
+ # accept would be
25
+ #
26
+ # %%
27
+ # %%
28
+ #
29
+ # The first part consists of _directives_ and _blocks_; directives
30
+ # look something like `"%" <directive>[ <argument>]*\n`, with
31
+ # `<directive>` being any alphanumerical character, including
32
+ # underscores and dashes, and `<argument>` being any word character
33
+ # or a quote-delimited string. Blocks consist of
34
+ # `"%{" <content> "\n" "\s"* "%}"`, with `<content>` being any
35
+ # characters. The content is copied directly into the body of the
36
+ # output.
37
+ #
38
+ # The second part consists of rules. Rules look something like
39
+ # this:
40
+ #
41
+ # <nonterminal>: (<nonterminal> | <terminal>)* ["{" <content> "}"] ["|" (<nonterminal> | <terminal>)* ["{" <content> "}"]]* [;]
42
+ #
43
+ # Where `<nonterminal>` is any lowercase alphabetical cahracter,
44
+ # `<terminal>` is any uppercase alphabetical character, and
45
+ # `<content>` is code to be used in the output file upon matching
46
+ # the specific rule.
47
+ #
48
+ # The third part consists of a body, which is copied directly into
49
+ # the output.
50
+ module Ace
51
+
52
+ end
53
+ end
data/lib/antelope/cli.rb CHANGED
@@ -1,55 +1,55 @@
1
- # encoding: utf-8
2
-
3
- require "thor"
4
-
5
- module Antelope
6
-
7
- # Handles the command line interface.
8
- class CLI < Thor
9
-
10
- class_option :verbose, default: false, type: :boolean
11
-
12
- option :type, default: nil, type: :string,
13
- desc: "The type of generator to use"
14
- desc "compile FILE [FILE]*", "compile the given files"
15
-
16
- # Compile.
17
- def compile(*files)
18
- files.each do |file|
19
- compile_file(file)
20
- end
21
- end
22
-
23
- desc "check FILE [FILE]*", "check the syntax of the given files"
24
-
25
- # Check.
26
- def check(*files)
27
- files.each do |file|
28
- compile_file(file, [Generator::Null])
29
- end
30
- end
31
-
32
- private
33
-
34
- # Compiles the given file, and then generates. If an error
35
- # occurs, it prints it out to stderr, along with a backtrace if
36
- # the verbose flag was set.
37
- #
38
- # @param file [String] the file to compile.
39
- # @param gen [Array, Symbol] the generator to use.
40
- # @return [void]
41
- def compile_file(file, gen = :guess)
42
- puts "Compiling #{file}... "
43
-
44
- grammar = Ace::Grammar.from_file(file)
45
- grammar.generate(options, gen)
46
-
47
- rescue => e
48
- $stderr.puts "Error while compiling: #{e.class}: #{e.message}"
49
-
50
- if options[:verbose]
51
- $stderr.puts e.backtrace[0..10].map { |_| "\t#{_}" }
52
- end
53
- end
54
- end
55
- end
1
+ # encoding: utf-8
2
+
3
+ require "thor"
4
+
5
+ module Antelope
6
+
7
+ # Handles the command line interface.
8
+ class CLI < Thor
9
+
10
+ class_option :verbose, default: false, type: :boolean
11
+
12
+ option :type, default: nil, type: :string,
13
+ desc: "The type of generator to use"
14
+ desc "compile FILE [FILE]*", "compile the given files"
15
+
16
+ # Compile.
17
+ def compile(*files)
18
+ files.each do |file|
19
+ compile_file(file)
20
+ end
21
+ end
22
+
23
+ desc "check FILE [FILE]*", "check the syntax of the given files"
24
+
25
+ # Check.
26
+ def check(*files)
27
+ files.each do |file|
28
+ compile_file(file, [Generator::Null])
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # Compiles the given file, and then generates. If an error
35
+ # occurs, it prints it out to stderr, along with a backtrace if
36
+ # the verbose flag was set.
37
+ #
38
+ # @param file [String] the file to compile.
39
+ # @param gen [Array, Symbol] the generator to use.
40
+ # @return [void]
41
+ def compile_file(file, gen = :guess)
42
+ puts "Compiling #{file}... "
43
+
44
+ grammar = Ace::Grammar.from_file(file)
45
+ grammar.generate(options, gen)
46
+
47
+ rescue => e
48
+ $stderr.puts "Error while compiling: #{e.class}: #{e.message}"
49
+
50
+ if options[:verbose]
51
+ $stderr.puts e.backtrace[0..10].map { |_| "\t#{_}" }
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,8 +1,8 @@
1
- # encoding: utf-8
2
-
3
- module Antelope
4
-
5
- # Every error in antelope inherits this error class.
6
- class Error < StandardError
7
- end
8
- end
1
+ # encoding: utf-8
2
+
3
+ module Antelope
4
+
5
+ # Every error in antelope inherits this error class.
6
+ class Error < StandardError
7
+ end
8
+ end
@@ -1,88 +1,88 @@
1
- # encoding: utf-8
2
-
3
- module Antelope
4
- module Generation
5
- class Constructor
6
-
7
- # Contains the methods to construct first sets for tokens.
8
- module First
9
-
10
- # Initialize.
11
- def initialize
12
- @firstifying = []
13
- super
14
- end
15
-
16
- # Constructs the first set for a given token. This is how
17
- # the method should behave:
18
- #
19
- # FIRST(ε) == [] # if ε is the epsilon token
20
- # FIRST(x) == [x] # if x is a terminal
21
- # FIRST(αβ) == if nullable?(α)
22
- # FIRST(α) U FIRST(β)
23
- # else
24
- # FIRST(α)
25
- # end
26
- # FIRST(A) == FIRST(a_1) U FIRST(a_2) U ... U FIRST(a_n)
27
- # # if A is a nonterminal and a_1, a_2, ..., a_3 are all
28
- # # of the right-hand sides of its productions.
29
- #
30
- # @param token [Ace::Token, Array<Ace::Token>]
31
- # @return [Set<Ace::Token::Terminal>]
32
- # @see #first_array
33
- def first(token)
34
- case token
35
- when Ace::Token::Nonterminal
36
- firstifying(token) do
37
- productions = grammar.productions[token.name]
38
- productions.map { |prod|
39
- first(prod[:items]) }.inject(Set.new, :+)
40
- end
41
- when Array
42
- first_array(token)
43
- when Ace::Token::Epsilon
44
- Set.new
45
- when Ace::Token::Terminal
46
- Set.new([token])
47
- else
48
- incorrect_argument! token, Ace::Token, Array
49
- end
50
- end
51
-
52
- private
53
-
54
- # Determines the FIRST set of an array of tokens. First, it
55
- # removes any terminals we are finding the FIRST set for;
56
- # then, it determines which tokens we have to find the FIRST
57
- # sets for (since some tokens may be nullable). We then add
58
- # those sets to our set.
59
- #
60
- # @param tokens [Array<Ace::Token>]
61
- # @return [Set<Ace::Token>]
62
- def first_array(tokens)
63
- tokens.dup.delete_if { |_| @firstifying.include?(_) }.
64
- each_with_index.take_while do |token, i|
65
- if i.zero?
66
- true
67
- else
68
- nullable?(tokens[i - 1])
69
- end
70
- end.map(&:first).map { |_| first(_) }.inject(Set.new, :+)
71
- end
72
-
73
- # Helps keep track of the nonterminals we're finding FIRST
74
- # sets for. This helps prevent recursion.
75
- #
76
- # @param tok [Ace::Token::Nonterminal]
77
- # @yield once.
78
- # @return [Set<Ace::Token>]
79
- def firstifying(tok)
80
- @firstifying << tok
81
- out = yield
82
- @firstifying.delete tok
83
- out
84
- end
85
- end
86
- end
87
- end
88
- end
1
+ # encoding: utf-8
2
+
3
+ module Antelope
4
+ module Generation
5
+ class Constructor
6
+
7
+ # Contains the methods to construct first sets for tokens.
8
+ module First
9
+
10
+ # Initialize.
11
+ def initialize
12
+ @firstifying = []
13
+ super
14
+ end
15
+
16
+ # Constructs the first set for a given token. This is how
17
+ # the method should behave:
18
+ #
19
+ # FIRST(ε) == [] # if ε is the epsilon token
20
+ # FIRST(x) == [x] # if x is a terminal
21
+ # FIRST(αβ) == if nullable?(α)
22
+ # FIRST(α) U FIRST(β)
23
+ # else
24
+ # FIRST(α)
25
+ # end
26
+ # FIRST(A) == FIRST(a_1) U FIRST(a_2) U ... U FIRST(a_n)
27
+ # # if A is a nonterminal and a_1, a_2, ..., a_3 are all
28
+ # # of the right-hand sides of its productions.
29
+ #
30
+ # @param token [Ace::Token, Array<Ace::Token>]
31
+ # @return [Set<Ace::Token::Terminal>]
32
+ # @see #first_array
33
+ def first(token)
34
+ case token
35
+ when Ace::Token::Nonterminal
36
+ firstifying(token) do
37
+ productions = grammar.productions[token.name]
38
+ productions.map { |prod|
39
+ first(prod[:items]) }.inject(Set.new, :+)
40
+ end
41
+ when Array
42
+ first_array(token)
43
+ when Ace::Token::Epsilon
44
+ Set.new
45
+ when Ace::Token::Terminal
46
+ Set.new([token])
47
+ else
48
+ incorrect_argument! token, Ace::Token, Array
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ # Determines the FIRST set of an array of tokens. First, it
55
+ # removes any terminals we are finding the FIRST set for;
56
+ # then, it determines which tokens we have to find the FIRST
57
+ # sets for (since some tokens may be nullable). We then add
58
+ # those sets to our set.
59
+ #
60
+ # @param tokens [Array<Ace::Token>]
61
+ # @return [Set<Ace::Token>]
62
+ def first_array(tokens)
63
+ tokens.dup.delete_if { |_| @firstifying.include?(_) }.
64
+ each_with_index.take_while do |token, i|
65
+ if i.zero?
66
+ true
67
+ else
68
+ nullable?(tokens[i - 1])
69
+ end
70
+ end.map(&:first).map { |_| first(_) }.inject(Set.new, :+)
71
+ end
72
+
73
+ # Helps keep track of the nonterminals we're finding FIRST
74
+ # sets for. This helps prevent recursion.
75
+ #
76
+ # @param tok [Ace::Token::Nonterminal]
77
+ # @yield once.
78
+ # @return [Set<Ace::Token>]
79
+ def firstifying(tok)
80
+ @firstifying << tok
81
+ out = yield
82
+ @firstifying.delete tok
83
+ out
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,103 +1,103 @@
1
- # encoding: utf-8
2
-
3
- module Antelope
4
- module Generation
5
- class Constructor
6
-
7
- # Contains the methods to find the FOLLOW sets of nonterminals.
8
- module Follow
9
-
10
- # Initialize.
11
- def initialize
12
- @follows = {}
13
- super
14
- end
15
-
16
- # Returns the FOLLOW set of the given token. If the given
17
- # token isn't a nonterminal, it raises an error. It then
18
- # generates the FOLLOW set for the given token, and then
19
- # caches it.
20
- #
21
- # @return [Set<Ace::Token>]
22
- # @see Constructor#incorrect_argument!
23
- # @see #generate_follow_set
24
- def follow(token)
25
- unless token.is_a? Ace::Token::Nonterminal
26
- incorrect_argument! token, Ace::Token::Nonterminal
27
- end
28
-
29
- @follows.fetch(token) { generate_follow_set(token) }
30
- end
31
-
32
- private
33
-
34
- # Generates the FOLLOW set for the given token. It finds the
35
- # positions at which the token appears in the grammar, and
36
- # sees what could possibly follow it. For example, given the
37
- # following production:
38
- #
39
- # A -> aBz
40
- #
41
- # With `a` and `z` being any combination of terminals and
42
- # nonterminals, and we're trying to find the FOLLOW set of
43
- # `B` we add the FIRST set of `z` to the FOLLOW set of `B`:
44
- #
45
- # FOLLOW(B) = FOLLOW(B) ∪ FIRST(z)
46
- #
47
- # In the case that `B` is at the end of a production, like so:
48
- #
49
- # A -> aB
50
- #
51
- # or
52
- #
53
- # A -> aBw
54
- #
55
- # (with `w` being nullable) We also add the FOLLOW set of `A`
56
- # to `B`:
57
- #
58
- # FOLLOW(B) = FOLLOW(B) ∪ FOLLOW(A)
59
- #
60
- # In case this operation is potentially recursive, we make
61
- # sure to set the FOLLOW set of `B` to an empty set (since we
62
- # cache the result of a FOLLOW set, the empty set will be
63
- # returned).
64
- #
65
- # @param token [Ace::Token::Nonterminal]
66
- # @return [Set<Ace::Token>]
67
- # @see First#first
68
- # @see Nullable#nullable?
69
- def generate_follow_set(token)
70
- # Set it to the empty set so we don't end up recursing.
71
- set = @follows[token] = Set.new
72
-
73
- productions.each do |rule|
74
- items = rule.items
75
-
76
- # Find all of the positions within the rule that our token
77
- # occurs, and then increment that position by one.
78
- positions = items.each_with_index.
79
- find_all { |t, _| t == token }.
80
- map(&:last).map(&:succ)
81
-
82
- # Find the FIRST set of every item after our token, and
83
- # put that in our set.
84
- positions.map { |pos| first(items[pos..-1]) }.
85
- inject(set, :merge)
86
-
87
- positions.each do |pos|
88
- # If we're at the end of the rule...
89
- if pos == items.size || nullable?(items[pos..-1])
90
- # Then add the FOLLOW set of the left-hand side to our
91
- # set.
92
- set.merge follow(rule.label)
93
- end
94
- end
95
- end
96
-
97
- # Replace the cached empty set with our filled set.
98
- @follows[token] = set
99
- end
100
- end
101
- end
102
- end
103
- end
1
+ # encoding: utf-8
2
+
3
+ module Antelope
4
+ module Generation
5
+ class Constructor
6
+
7
+ # Contains the methods to find the FOLLOW sets of nonterminals.
8
+ module Follow
9
+
10
+ # Initialize.
11
+ def initialize
12
+ @follows = {}
13
+ super
14
+ end
15
+
16
+ # Returns the FOLLOW set of the given token. If the given
17
+ # token isn't a nonterminal, it raises an error. It then
18
+ # generates the FOLLOW set for the given token, and then
19
+ # caches it.
20
+ #
21
+ # @return [Set<Ace::Token>]
22
+ # @see Constructor#incorrect_argument!
23
+ # @see #generate_follow_set
24
+ def follow(token)
25
+ unless token.is_a? Ace::Token::Nonterminal
26
+ incorrect_argument! token, Ace::Token::Nonterminal
27
+ end
28
+
29
+ @follows.fetch(token) { generate_follow_set(token) }
30
+ end
31
+
32
+ private
33
+
34
+ # Generates the FOLLOW set for the given token. It finds the
35
+ # positions at which the token appears in the grammar, and
36
+ # sees what could possibly follow it. For example, given the
37
+ # following production:
38
+ #
39
+ # A -> aBz
40
+ #
41
+ # With `a` and `z` being any combination of terminals and
42
+ # nonterminals, and we're trying to find the FOLLOW set of
43
+ # `B` we add the FIRST set of `z` to the FOLLOW set of `B`:
44
+ #
45
+ # FOLLOW(B) = FOLLOW(B) ∪ FIRST(z)
46
+ #
47
+ # In the case that `B` is at the end of a production, like so:
48
+ #
49
+ # A -> aB
50
+ #
51
+ # or
52
+ #
53
+ # A -> aBw
54
+ #
55
+ # (with `w` being nullable) We also add the FOLLOW set of `A`
56
+ # to `B`:
57
+ #
58
+ # FOLLOW(B) = FOLLOW(B) ∪ FOLLOW(A)
59
+ #
60
+ # In case this operation is potentially recursive, we make
61
+ # sure to set the FOLLOW set of `B` to an empty set (since we
62
+ # cache the result of a FOLLOW set, the empty set will be
63
+ # returned).
64
+ #
65
+ # @param token [Ace::Token::Nonterminal]
66
+ # @return [Set<Ace::Token>]
67
+ # @see First#first
68
+ # @see Nullable#nullable?
69
+ def generate_follow_set(token)
70
+ # Set it to the empty set so we don't end up recursing.
71
+ set = @follows[token] = Set.new
72
+
73
+ productions.each do |rule|
74
+ items = rule.items
75
+
76
+ # Find all of the positions within the rule that our token
77
+ # occurs, and then increment that position by one.
78
+ positions = items.each_with_index.
79
+ find_all { |t, _| t == token }.
80
+ map(&:last).map(&:succ)
81
+
82
+ # Find the FIRST set of every item after our token, and
83
+ # put that in our set.
84
+ positions.map { |pos| first(items[pos..-1]) }.
85
+ inject(set, :merge)
86
+
87
+ positions.each do |pos|
88
+ # If we're at the end of the rule...
89
+ if pos == items.size || nullable?(items[pos..-1])
90
+ # Then add the FOLLOW set of the left-hand side to our
91
+ # set.
92
+ set.merge follow(rule.label)
93
+ end
94
+ end
95
+ end
96
+
97
+ # Replace the cached empty set with our filled set.
98
+ @follows[token] = set
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end