antelope 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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