antelope 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|