antelope 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/antelope.gemspec +0 -2
  4. data/bin/antelope +3 -20
  5. data/examples/deterministic.output +97 -103
  6. data/examples/example.ace +0 -1
  7. data/examples/example.output +259 -333
  8. data/examples/simple.output +85 -87
  9. data/lib/antelope/ace/compiler.rb +14 -16
  10. data/lib/antelope/ace/errors.rb +9 -3
  11. data/lib/antelope/ace/grammar/generation.rb +38 -7
  12. data/lib/antelope/ace/grammar/precedences.rb +59 -0
  13. data/lib/antelope/ace/grammar/production.rb +24 -25
  14. data/lib/antelope/ace/grammar/productions.rb +8 -8
  15. data/lib/antelope/ace/grammar.rb +3 -3
  16. data/lib/antelope/ace/{presidence.rb → precedence.rb} +11 -11
  17. data/lib/antelope/ace/scanner/second.rb +2 -2
  18. data/lib/antelope/ace/token.rb +1 -1
  19. data/lib/antelope/ace.rb +2 -2
  20. data/lib/antelope/cli.rb +33 -0
  21. data/lib/antelope/errors.rb +6 -0
  22. data/lib/antelope/generation/constructor/first.rb +40 -6
  23. data/lib/antelope/generation/constructor/follow.rb +83 -25
  24. data/lib/antelope/generation/constructor/nullable.rb +24 -2
  25. data/lib/antelope/generation/constructor.rb +39 -13
  26. data/lib/antelope/generation/errors.rb +15 -0
  27. data/lib/antelope/generation/recognizer/rule.rb +111 -11
  28. data/lib/antelope/generation/recognizer/state.rb +53 -5
  29. data/lib/antelope/generation/recognizer.rb +31 -1
  30. data/lib/antelope/generation/tableizer.rb +42 -10
  31. data/lib/antelope/generation.rb +1 -1
  32. data/lib/antelope/generator/templates/output.erb +19 -18
  33. data/lib/antelope/version.rb +1 -1
  34. data/lib/antelope.rb +3 -2
  35. metadata +7 -36
  36. data/lib/antelope/ace/grammar/presidence.rb +0 -59
  37. data/lib/antelope/automaton.rb +0 -36
  38. data/lib/antelope/generation/conflictor/conflict.rb +0 -7
  39. data/lib/antelope/generation/conflictor.rb +0 -45
  40. data/lib/antelope/generation/constructor/lookahead.rb +0 -42
@@ -8,7 +8,7 @@ module Antelope
8
8
  # identifier followed by a colon; the body consists of "parts",
9
9
  # an "or", a "prec", and/or a "block". The part may consist
10
10
  # of any alphabetical characters. An or is just a vertical bar
11
- # (`|`). A prec is a presidence declaraction, which is `%prec `
11
+ # (`|`). A prec is a precedence declaraction, which is `%prec `
12
12
  # followed by any alphabetical characters. A block is a `{`,
13
13
  # followed by code, followed by a terminating `}`. Rules _may_
14
14
  # be terminated by a semicolon, but this is optional.
@@ -100,7 +100,7 @@ module Antelope
100
100
  end
101
101
  end
102
102
 
103
- # Attempts to scan a presidence definition. A presidence
103
+ # Attempts to scan a precedence definition. A precedence
104
104
  # definition is "%prec " followed by a terminal or nonterminal.
105
105
  #
106
106
  # @return [Boolean] if it matched.
@@ -145,7 +145,7 @@ module Antelope
145
145
  self.class.new(name, @value)
146
146
  end
147
147
 
148
- # Generates a hashs for this class.
148
+ # Generates a hash for this class.
149
149
  #
150
150
  # @note This is not intended for use. It is only defined to be
151
151
  # compatible with Hashs (and by extension, Sets).
data/lib/antelope/ace.rb CHANGED
@@ -2,7 +2,7 @@ require "antelope/ace/errors"
2
2
  require "antelope/ace/scanner"
3
3
  require "antelope/ace/compiler"
4
4
  require "antelope/ace/token"
5
- require "antelope/ace/presidence"
5
+ require "antelope/ace/precedence"
6
6
  require "antelope/ace/grammar"
7
7
 
8
8
  module Antelope
@@ -42,7 +42,7 @@ module Antelope
42
42
  # `<content>` is code to be used in the output file upon matching
43
43
  # the specific rule.
44
44
  #
45
- # The thid part consists of a body, which is copied directly into
45
+ # The third part consists of a body, which is copied directly into
46
46
  # the output.
47
47
  module Ace
48
48
 
@@ -0,0 +1,33 @@
1
+ require "thor"
2
+
3
+ module Antelope
4
+ class CLI < Thor
5
+
6
+ class_option :verbose, default: false, type: :boolean
7
+
8
+ option :type, default: nil, type: :string,
9
+ desc: "The type of generator to use"
10
+ desc "compile FILE [FILE]*", "compile the given files"
11
+ def compile(*files)
12
+ files.each do |file|
13
+ compile_file(file)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def compile_file(file)
20
+ puts "Compiling #{file}... "
21
+
22
+ grammar = Ace::Grammar.from_file(file)
23
+ grammar.generate(options)
24
+
25
+ rescue => e
26
+ $stderr.puts "Error while compiling: #{e.class}: #{e.message}"
27
+
28
+ if options[:verbose]
29
+ $stderr.puts e.backtrace[0..10].map { |_| "\t#{_}" }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,6 @@
1
+ module Antelope
2
+
3
+ # Every error in antelope inherits this error class.
4
+ class Error < StandardError
5
+ end
6
+ end
@@ -1,18 +1,38 @@
1
1
  module Antelope
2
2
  module Generation
3
3
  class Constructor
4
+
5
+ # Contains the methods to construct first sets for tokens.
4
6
  module First
5
7
 
8
+ # Initialize.
6
9
  def initialize
7
10
  @firstifying = []
8
11
  super
9
12
  end
10
13
 
14
+ # Constructs the first set for a given token. This is how
15
+ # the method should behave:
16
+ #
17
+ # FIRST(ε) == [] # if ϵ is the epsilon token
18
+ # FIRST(x) == [x] # if x is a terminal
19
+ # FIRST(αβ) == if nullable?(α)
20
+ # FIRST(α) U FIRST(β)
21
+ # else
22
+ # FIRST(α)
23
+ # end
24
+ # FIRST(A) == FIRST(a_1) U FIRST(a_2) U ... U FIRST(a_n)
25
+ # # if A is a nonterminal and a_1, a_2, ..., a_3 are all
26
+ # # of the right-hand sides of its productions.
27
+ #
28
+ # @param token [Ace::Token, Array<Ace::Token>]
29
+ # @return [Set<Ace::Token::Terminal>]
30
+ # @see #first_array
11
31
  def first(token)
12
32
  case token
13
33
  when Ace::Token::Nonterminal
14
34
  firstifying(token) do
15
- productions = parser.productions[token.name]
35
+ productions = grammar.productions[token.name]
16
36
  productions.map { |prod|
17
37
  first(prod[:items]) }.inject(Set.new, :+)
18
38
  end
@@ -29,17 +49,31 @@ module Antelope
29
49
 
30
50
  private
31
51
 
32
- def first_array(token)
33
- token.dup.delete_if { |tok| @firstifying.include?(tok) }.
34
- each_with_index.take_while do |tok, i|
52
+ # Determines the FIRST set of an array of tokens. First, it
53
+ # removes any terminals we are finding the FIRST set for;
54
+ # then, it determines which tokens we have to find the FIRST
55
+ # sets for (since some tokens may be nullable). We then add
56
+ # those sets to our set.
57
+ #
58
+ # @param tokens [Array<Ace::Token>]
59
+ # @return [Set<Ace::Token>]
60
+ def first_array(tokens)
61
+ tokens.dup.delete_if { |_| @firstifying.include?(_) }.
62
+ each_with_index.take_while do |token, i|
35
63
  if i.zero?
36
64
  true
37
65
  else
38
- nullable?(token[i - 1])
66
+ nullable?(tokens[i - 1])
39
67
  end
40
- end.map(&:first).map { |tok| first(tok) }.inject(Set.new, :+)
68
+ end.map(&:first).map { |_| first(_) }.inject(Set.new, :+)
41
69
  end
42
70
 
71
+ # Helps keep track of the nonterminals we're finding FIRST
72
+ # sets for. This helps prevent recursion.
73
+ #
74
+ # @param tok [Ace::Token::Nonterminal]
75
+ # @yield once.
76
+ # @return [Set<Ace::Token>]
43
77
  def firstifying(tok)
44
78
  @firstifying << tok
45
79
  out = yield
@@ -1,44 +1,102 @@
1
1
  module Antelope
2
2
  module Generation
3
3
  class Constructor
4
+
5
+ # Contains the methods to find the FOLLOW sets of nonterminals.
4
6
  module Follow
5
7
 
8
+ # Initialize.
6
9
  def initialize
7
10
  @follows = {}
8
11
  super
9
12
  end
10
13
 
14
+ # Returns the FOLLOW set of the given token. If the given
15
+ # token isn't a nonterminal, it raises an error. It then
16
+ # generates the FOLLOW set for the given token, and then
17
+ # caches it.
18
+ #
19
+ # @return [Set<Ace::Token>]
20
+ # @see Constructor#incorrect_argument!
21
+ # @see #generate_follow_set
11
22
  def follow(token)
12
-
13
- if token.nonterminal?
14
- token = token.name
15
- elsif token.is_a? Symbol
16
- else
17
- incorrect_argument! token, Ace::Token::Nonterminal, Symbol
23
+ unless token.is_a? Ace::Token::Nonterminal
24
+ incorrect_argument! token, Ace::Token::Nonterminal
18
25
  end
19
26
 
20
- @follows.fetch(token) do
21
- @follows[token] = Set.new
22
- set = Set.new
23
-
24
- parser.productions.each do |key, value|
25
- value.each do |production|
26
- items = production[:items]
27
- positions = items.each_with_index.
28
- find_all { |t, _| t.name == token }.
29
- map(&:last).map(&:succ)
30
- positions.map { |pos| first(items[pos..-1]) }.
31
- inject(set, :merge)
32
- positions.each do |pos|
33
- if pos == items.size || nullable?(items[pos..-1])
34
- set.merge follow(Ace::Token::Nonterminal.new(key))
35
- end
36
- end
27
+ @follows.fetch(token) { generate_follow_set(token) }
28
+ end
29
+
30
+ private
31
+
32
+ # Generates the FOLLOW set for the given token. It finds the
33
+ # positions at which the token appears in the grammar, and
34
+ # sees what could possibly follow it. For example, given the
35
+ # following production:
36
+ #
37
+ # A -> aBz
38
+ #
39
+ # With `a` and `z` being any combination of terminals and
40
+ # nonterminals, and we're trying to find the FOLLOW set of
41
+ # `B` we add the FIRST set of `z` to the FOLLOW set of `B`:
42
+ #
43
+ # FOLLOW(B) = FOLLOW(B) ∪ FIRST(z)
44
+ #
45
+ # In the case that `B` is at the end of a production, like so:
46
+ #
47
+ # A -> aB
48
+ #
49
+ # or
50
+ #
51
+ # A -> aBw
52
+ #
53
+ # (with `w` being nullable) We also add the FOLLOW set of `A`
54
+ # to `B`:
55
+ #
56
+ # FOLLOW(B) = FOLLOW(B) ∪ FOLLOW(A)
57
+ #
58
+ # In case this operation is potentially recursive, we make
59
+ # sure to set the FOLLOW set of `B` to an empty set (since we
60
+ # cache the result of a FOLLOW set, the empty set will be
61
+ # returned).
62
+ #
63
+ # @param token [Ace::Token::Nonterminal]
64
+ # @return [Set<Ace::Token>]
65
+ # @see First#first
66
+ # @see Nullable#nullable?
67
+ def generate_follow_set(token)
68
+ # Set it to the empty set so we don't end up recursing.
69
+ @follows[token] = Set.new
70
+
71
+ # This is going to be the output set.
72
+ set = Set.new
73
+
74
+ productions.each do |rule|
75
+ items = rule.right
76
+
77
+ # Find all of the positions within the rule that our token
78
+ # occurs, and then increment that position by one.
79
+ positions = items.each_with_index.
80
+ find_all { |t, _| t == token }.
81
+ map(&:last).map(&:succ)
82
+
83
+ # Find the FIRST set of every item after our token, and
84
+ # put that in our set.
85
+ positions.map { |pos| first(items[pos..-1]) }.
86
+ inject(set, :merge)
87
+
88
+ positions.each do |pos|
89
+ # If we're at the end of the rule...
90
+ if pos == items.size || nullable?(items[pos..-1])
91
+ # Then add the FOLLOW set of the left-hand side to our
92
+ # set.
93
+ set.merge follow(rule.left)
37
94
  end
38
95
  end
39
-
40
- @follows[token] = set
41
96
  end
97
+
98
+ # Replace the cached empty set with our filled set.
99
+ @follows[token] = set
42
100
  end
43
101
  end
44
102
  end
@@ -1,17 +1,33 @@
1
1
  module Antelope
2
2
  module Generation
3
3
  class Constructor
4
+
5
+ # Contains the methods to determine if an object is nullable.
4
6
  module Nullable
5
7
 
8
+ # Initialize.
6
9
  def initialize
7
- @nullifying = []
10
+ @nullifying = Set.new
8
11
  end
9
12
 
13
+ # Determine if a given token is nullable. This is how the
14
+ # method should behave:
15
+ #
16
+ # nullable?(ϵ) == true # if ϵ is the epsilon token
17
+ # nullable?(x) == false # if x is a terminal
18
+ # nullable?(αβ) == nullable?(α) && nullable?(β)
19
+ # nullable?(A) == nullable?(a_1) || nullable?(a_2) || ... nullable?(a_n)
20
+ # # if A is a nonterminal and a_1, a_2, ..., a_n are all
21
+ # # of the right-hand sides of its productions
22
+ #
23
+ # @param token [Ace::Token, Array<Ace::Token>] the token to
24
+ # check.
25
+ # @return [Boolean] if the token can reduce to ϵ.
10
26
  def nullable?(token)
11
27
  case token
12
28
  when Ace::Token::Nonterminal
13
29
  nullifying(token) do
14
- productions = parser.productions[token.name]
30
+ productions = grammar.productions[token.name]
15
31
  !!productions.any? { |prod| nullable?(prod[:items]) }
16
32
  end
17
33
  when Array
@@ -28,6 +44,12 @@ module Antelope
28
44
 
29
45
  private
30
46
 
47
+ # Helps keep track of the nonterminals we're checking for
48
+ # nullability. This helps prevent recursion.
49
+ #
50
+ # @param tok [Ace::Token::Nonterminal]
51
+ # @yield once.
52
+ # @return [Boolean]
31
53
  def nullifying(tok)
32
54
  @nullifying << tok
33
55
  out = yield
@@ -2,36 +2,56 @@ require "set"
2
2
  require "antelope/generation/constructor/nullable"
3
3
  require "antelope/generation/constructor/first"
4
4
  require "antelope/generation/constructor/follow"
5
- require "antelope/generation/constructor/lookahead"
6
5
 
7
6
  module Antelope
8
7
  module Generation
8
+
9
+ # Constructs the lookahead sets for all of the rules in the
10
+ # grammar.
9
11
  class Constructor
10
12
 
11
13
  include Nullable
12
14
  include First
13
15
  include Follow
14
- include Lookahead
15
16
 
16
- attr_reader :parser
17
+ # The grammar.
18
+ #
19
+ # @return [Ace::Grammar]
20
+ attr_reader :grammar
21
+
17
22
  attr_reader :productions
18
23
 
19
- def initialize(parser)
20
- @parser = parser
21
- @productions = []
24
+ # Initialize.
25
+ #
26
+ # @param grammar [Ace::Grammar] the grammar.
27
+ def initialize(grammar)
28
+ @productions = Set.new
29
+ @grammar = grammar
22
30
  super()
23
31
  end
24
32
 
33
+ # Performs the construction. First, it goes through every state
34
+ # and augments the state. It then goes through every rule and
35
+ # augments it.
36
+ #
37
+ # @return [void]
38
+ # @see #augment_state
39
+ # @see #augment_rule
25
40
  def call
26
- parser.states.each do |state|
41
+ grammar.states.each do |state|
27
42
  augment_state(state)
28
43
  end.each do |state|
29
44
  augment_rules(state)
30
45
  end
31
-
32
- @productions
33
46
  end
34
47
 
48
+ # Augments the given state. On every rule within that state
49
+ # that has a position of zero, it follows the rule throughout
50
+ # the DFA until the end; it marks every nonterminal it
51
+ # encounters with the transitions it took on that nonterminal.
52
+ #
53
+ # @param state [Recognizer::State] the state to augment.
54
+ # @return [void]
35
55
  def augment_state(state)
36
56
  state.rules.select { |x| x.position.zero? }.each do |rule|
37
57
  current_state = state
@@ -39,8 +59,6 @@ module Antelope
39
59
  rule.left.from = state
40
60
  rule.left.to = state.transitions[rule.left.name]
41
61
 
42
- states = [state]
43
-
44
62
  rule.right.each_with_index do |part, pos|
45
63
  transition = current_state.transitions[part.name]
46
64
  if part.nonterminal?
@@ -48,14 +66,22 @@ module Antelope
48
66
  part.to = transition
49
67
  end
50
68
 
51
- states.push(transition)
52
69
  current_state = transition
53
70
  end
54
71
 
55
- productions << rule unless productions.include?(rule)
72
+ productions << rule
56
73
  end
57
74
  end
58
75
 
76
+ # Augments every final rule. For every rule in the current
77
+ # state that has a position of zero, it follows the rule through
78
+ # the DFA until the ending state; it then modifies the ending
79
+ # state's lookahead set to be the FOLLOW set of the nonterminal
80
+ # it reduces to.
81
+ #
82
+ # @param state [Recognizer::State]
83
+ # @return [void]
84
+ # @see Follow#follow
59
85
  def augment_rules(state)
60
86
  state.rules.select { |x| x.position.zero? }.each do |rule|
61
87
  current_state = state
@@ -0,0 +1,15 @@
1
+ module Antelope
2
+ module Generation
3
+
4
+ # Defines an error that can occur within the Generation module.
5
+ # All errors that are raised within the Generation module are
6
+ # subclasses of this.
7
+ class Error < Antelope::Error
8
+ end
9
+
10
+ # Used mainly in the {Tableizer}, this is raised when a conflict
11
+ # could not be resolved.
12
+ class UnresolvableConflictError < Error
13
+ end
14
+ end
15
+ end
@@ -1,26 +1,74 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require "securerandom"
4
+
3
5
  module Antelope
4
6
  module Generation
5
7
  class Recognizer
8
+
9
+ # Defines a rule. A rule has a corresponding production, and a
10
+ # position in that production. It also contains extra
11
+ # information for other reasons.
6
12
  class Rule
7
13
 
14
+ # The left-hand side of the rule.
15
+ #
16
+ # @return [Ace::Token::Nonterminal]
8
17
  attr_reader :left
18
+
19
+ # The right-hand side of the rule.
20
+ #
21
+ # @return [Ace::Token]
9
22
  attr_reader :right
23
+
24
+ # The current position inside of the rule.
25
+ #
26
+ # @return [Numeric]
10
27
  attr_reader :position
28
+
29
+ # The block to be executed on production match.
30
+ #
31
+ # @deprecated Use {Ace::Grammar::Production#block} instead.
32
+ # @return [String]
11
33
  attr_reader :block
34
+
35
+ # The lookahead set for this specific rule. Contains nothing
36
+ # unless {#final?} returns true.
37
+ #
38
+ # @return [Set<Symbol>]
12
39
  attr_accessor :lookahead
40
+
41
+ # The id for this rule. Initialy, this is set to a string of
42
+ # hexadecimal characters; after construction of all states,
43
+ # however, it is a number.
44
+ #
45
+ # @return [String, Numeric]
13
46
  attr_accessor :id
14
- attr_accessor :presidence
47
+
48
+ # The precedence for this rule.
49
+ #
50
+ # @return [Ace::Precedence]
51
+ attr_accessor :precedence
52
+
53
+ # The associated production.
54
+ #
55
+ # @return [Ace::Grammar::Production]
15
56
  attr_reader :production
16
57
 
17
58
  include Comparable
18
59
 
60
+ # Initialize the rule.
61
+ #
62
+ # @param production [Ace::Grammar::Production] the production
63
+ # that this rule is based off of.
64
+ # @param position [Numeric] the position that this rule is in
65
+ # the production.
66
+ # @param inherited [nil] do not use.
19
67
  def initialize(production, position, inherited = false)
20
- @left = production.label
68
+ @left = production.label.dup
21
69
  @position = position
22
70
  @lookahead = Set.new
23
- @presidence = production.prec
71
+ @precedence = production.prec
24
72
  @production = production
25
73
  @block = production.block
26
74
  @id = SecureRandom.hex
@@ -32,30 +80,69 @@ module Antelope
32
80
  end
33
81
  end
34
82
 
83
+ # Give a nice representation of the rule as a string.
84
+ #
85
+ # @return [String]
35
86
  def inspect
36
- "#<#{self.class} id=#{id} left=#{left} right=[#{right.join(" ")}] position=#{position}>"
87
+ "#<#{self.class} id=#{id} left=#{left} " \
88
+ "right=[#{right.join(" ")}] position=#{position}>"
37
89
  end
38
90
 
91
+ # Give a nicer representation of the rule as a string. Shows
92
+ # the id of the rule, the precedence, and the actual
93
+ # production; if the given argument is true, it will show a
94
+ # dot to show the position of the rule.
95
+ #
96
+ # @param dot [Boolean] show the current position of the rule.
97
+ # @return [String]
39
98
  def to_s(dot = true)
40
- "#{id}/#{presidence.type.to_s[0]}#{presidence.level}: #{left} → #{right[0, position].join(" ")}#{" • " if dot}#{right[position..-1].join(" ")}"
99
+ "#{id}/#{precedence.type.to_s[0]}#{precedence.level}: " \
100
+ "#{left} → #{right[0, position].join(" ")}" \
101
+ "#{" • " if dot}#{right[position..-1].join(" ")}"
41
102
  end
42
103
 
104
+ # Returns the active token. If there is no active token, it
105
+ # returns a blank {Ace::Token}.
106
+ #
107
+ # @return [Ace::Token]
43
108
  def active
44
109
  right[position] or Ace::Token.new(nil)
45
110
  end
46
111
 
112
+ # Creates the rule after this one by incrementing the position
113
+ # by one. {#succ?} should be called to make sure that this
114
+ # rule exists.
115
+ #
116
+ # @return [Rule]
47
117
  def succ
48
118
  Rule.new(production, position + 1)
49
119
  end
50
120
 
121
+ # Checks to see if a rule can exist after this one; i.e. the
122
+ # position is not equal to the size of the right side of the
123
+ # rule.
124
+ #
125
+ # @return [Boolean]
51
126
  def succ?
52
- right.size > (position)
127
+ right.size > position
53
128
  end
54
129
 
130
+ # Checks to see if this is the final rule, as in no rule can
131
+ # exist after this one; i.e. the position is equal to the
132
+ # size of the right side.
133
+ #
134
+ # @return [Boolean]
55
135
  def final?
56
136
  !succ?
57
137
  end
58
138
 
139
+ # Compares this rule to another object. If the other object
140
+ # is not a rule, it delegates the comparison. Otherwise, it
141
+ # converts both this and the other rule into arrays and
142
+ # compares the result.
143
+ #
144
+ # @param other [Object] the object to compare.
145
+ # @return [Numeric]
59
146
  def <=>(other)
60
147
  if other.is_a? Rule
61
148
  to_a <=> other.to_a
@@ -64,11 +151,12 @@ module Antelope
64
151
  end
65
152
  end
66
153
 
67
- def without_transitions
68
- @_without_transitions ||=
69
- Rule.new(production, position)
70
- end
71
-
154
+ # Fuzzily compares this object to another object. If the
155
+ # other object is not a rule, it delegates the comparison.
156
+ # Otherwise, it fuzzily compares the left and right sides.
157
+ #
158
+ # @param other [Object] the object to compare.
159
+ # @return [Numeric]
72
160
  def ===(other)
73
161
  if other.is_a? Rule
74
162
  left === other.left and right.each_with_index.
@@ -78,12 +166,24 @@ module Antelope
78
166
  end
79
167
  end
80
168
 
169
+ # Generates a hash for this class.
170
+ #
171
+ # @note This is not intended for use. It is only defined to be
172
+ # compatible with Hashs (and by extension, Sets).
173
+ # @private
174
+ # @return [Object]
81
175
  def hash
82
176
  to_a.hash
83
177
  end
84
178
 
85
179
  alias_method :eql?, :==
86
180
 
181
+ # Creates an array representation of this class.
182
+ #
183
+ # @note This is not intended for use. It is only defined to
184
+ # make equality checking easier, and to create a hash.
185
+ # @private
186
+ # @return [Array<(Ace::Token::Nonterminal, Array<Ace::Token>, Numeric)>]
87
187
  def to_a
88
188
  [left, right, position]
89
189
  end