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.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/antelope.gemspec +0 -2
- data/bin/antelope +3 -20
- data/examples/deterministic.output +97 -103
- data/examples/example.ace +0 -1
- data/examples/example.output +259 -333
- data/examples/simple.output +85 -87
- data/lib/antelope/ace/compiler.rb +14 -16
- data/lib/antelope/ace/errors.rb +9 -3
- data/lib/antelope/ace/grammar/generation.rb +38 -7
- data/lib/antelope/ace/grammar/precedences.rb +59 -0
- data/lib/antelope/ace/grammar/production.rb +24 -25
- data/lib/antelope/ace/grammar/productions.rb +8 -8
- data/lib/antelope/ace/grammar.rb +3 -3
- data/lib/antelope/ace/{presidence.rb → precedence.rb} +11 -11
- data/lib/antelope/ace/scanner/second.rb +2 -2
- data/lib/antelope/ace/token.rb +1 -1
- data/lib/antelope/ace.rb +2 -2
- data/lib/antelope/cli.rb +33 -0
- data/lib/antelope/errors.rb +6 -0
- data/lib/antelope/generation/constructor/first.rb +40 -6
- data/lib/antelope/generation/constructor/follow.rb +83 -25
- data/lib/antelope/generation/constructor/nullable.rb +24 -2
- data/lib/antelope/generation/constructor.rb +39 -13
- data/lib/antelope/generation/errors.rb +15 -0
- data/lib/antelope/generation/recognizer/rule.rb +111 -11
- data/lib/antelope/generation/recognizer/state.rb +53 -5
- data/lib/antelope/generation/recognizer.rb +31 -1
- data/lib/antelope/generation/tableizer.rb +42 -10
- data/lib/antelope/generation.rb +1 -1
- data/lib/antelope/generator/templates/output.erb +19 -18
- data/lib/antelope/version.rb +1 -1
- data/lib/antelope.rb +3 -2
- metadata +7 -36
- data/lib/antelope/ace/grammar/presidence.rb +0 -59
- data/lib/antelope/automaton.rb +0 -36
- data/lib/antelope/generation/conflictor/conflict.rb +0 -7
- data/lib/antelope/generation/conflictor.rb +0 -45
- 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
|
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
|
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.
|
data/lib/antelope/ace/token.rb
CHANGED
@@ -145,7 +145,7 @@ module Antelope
|
|
145
145
|
self.class.new(name, @value)
|
146
146
|
end
|
147
147
|
|
148
|
-
# Generates a
|
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/
|
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
|
45
|
+
# The third part consists of a body, which is copied directly into
|
46
46
|
# the output.
|
47
47
|
module Ace
|
48
48
|
|
data/lib/antelope/cli.rb
ADDED
@@ -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
|
@@ -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 =
|
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
|
-
|
33
|
-
|
34
|
-
|
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?(
|
66
|
+
nullable?(tokens[i - 1])
|
39
67
|
end
|
40
|
-
end.map(&:first).map { |
|
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
|
-
|
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)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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 =
|
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
|
-
|
17
|
+
# The grammar.
|
18
|
+
#
|
19
|
+
# @return [Ace::Grammar]
|
20
|
+
attr_reader :grammar
|
21
|
+
|
17
22
|
attr_reader :productions
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
@
|
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}
|
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}/#{
|
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 >
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|