antelope 0.0.1
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 +7 -0
- data/.gitignore +23 -0
- data/.rspec +3 -0
- data/.yardopts +4 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/antelope.gemspec +30 -0
- data/bin/antelope +24 -0
- data/examples/deterministic.ace +27 -0
- data/examples/deterministic.output +229 -0
- data/examples/example.ace +45 -0
- data/examples/example.output +610 -0
- data/examples/simple.ace +26 -0
- data/examples/simple.output +194 -0
- data/lib/antelope/ace/compiler.rb +290 -0
- data/lib/antelope/ace/errors.rb +27 -0
- data/lib/antelope/ace/grammar/generation.rb +47 -0
- data/lib/antelope/ace/grammar/loading.rb +51 -0
- data/lib/antelope/ace/grammar/presidence.rb +59 -0
- data/lib/antelope/ace/grammar/production.rb +47 -0
- data/lib/antelope/ace/grammar/productions.rb +119 -0
- data/lib/antelope/ace/grammar/terminals.rb +41 -0
- data/lib/antelope/ace/grammar.rb +59 -0
- data/lib/antelope/ace/presidence.rb +51 -0
- data/lib/antelope/ace/scanner/first.rb +61 -0
- data/lib/antelope/ace/scanner/second.rb +160 -0
- data/lib/antelope/ace/scanner/third.rb +25 -0
- data/lib/antelope/ace/scanner.rb +110 -0
- data/lib/antelope/ace/token/epsilon.rb +22 -0
- data/lib/antelope/ace/token/error.rb +24 -0
- data/lib/antelope/ace/token/nonterminal.rb +15 -0
- data/lib/antelope/ace/token/terminal.rb +15 -0
- data/lib/antelope/ace/token.rb +171 -0
- data/lib/antelope/ace.rb +50 -0
- data/lib/antelope/automaton.rb +36 -0
- data/lib/antelope/generation/conflictor/conflict.rb +7 -0
- data/lib/antelope/generation/conflictor.rb +45 -0
- data/lib/antelope/generation/constructor/first.rb +52 -0
- data/lib/antelope/generation/constructor/follow.rb +46 -0
- data/lib/antelope/generation/constructor/lookahead.rb +42 -0
- data/lib/antelope/generation/constructor/nullable.rb +40 -0
- data/lib/antelope/generation/constructor.rb +81 -0
- data/lib/antelope/generation/recognizer/rule.rb +93 -0
- data/lib/antelope/generation/recognizer/state.rb +56 -0
- data/lib/antelope/generation/recognizer.rb +152 -0
- data/lib/antelope/generation/tableizer.rb +80 -0
- data/lib/antelope/generation.rb +12 -0
- data/lib/antelope/generator/output.rb +30 -0
- data/lib/antelope/generator/ruby.rb +57 -0
- data/lib/antelope/generator/templates/output.erb +49 -0
- data/lib/antelope/generator/templates/ruby.erb +62 -0
- data/lib/antelope/generator.rb +84 -0
- data/lib/antelope/version.rb +4 -0
- data/lib/antelope.rb +9 -0
- data/spec/antelope/ace/compiler_spec.rb +50 -0
- data/spec/antelope/ace/scanner_spec.rb +27 -0
- data/spec/antelope/automaton_spec.rb +29 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/benchmark_helper.rb +5 -0
- metadata +223 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module Antelope
|
2
|
+
module Ace
|
3
|
+
class Token
|
4
|
+
|
5
|
+
# Defines an error token. This may be used internally by the
|
6
|
+
# parser when it enters panic mode; any tokens following this
|
7
|
+
# are the synchronisation tokens. This is considered a terminal
|
8
|
+
# for the purposes of rule definitions.
|
9
|
+
class Error < Terminal
|
10
|
+
|
11
|
+
# Initialize the error token. Technically takes no arguments.
|
12
|
+
# Sets the name to be `:$error`.
|
13
|
+
def initialize(*)
|
14
|
+
super :$error
|
15
|
+
end
|
16
|
+
|
17
|
+
# (see Token#error?)
|
18
|
+
def error?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require "antelope/ace/token/nonterminal"
|
2
|
+
require "antelope/ace/token/terminal"
|
3
|
+
require "antelope/ace/token/epsilon"
|
4
|
+
require "antelope/ace/token/error"
|
5
|
+
|
6
|
+
|
7
|
+
module Antelope
|
8
|
+
module Ace
|
9
|
+
|
10
|
+
# Defines a token type for productions/rules.
|
11
|
+
#
|
12
|
+
# @abstract This class should be inherited to define a real token.
|
13
|
+
# A base class does not match any token; however, any token can
|
14
|
+
# match the base class.
|
15
|
+
class Token
|
16
|
+
|
17
|
+
# The name of the token.
|
18
|
+
#
|
19
|
+
# @return [Symbol]
|
20
|
+
attr_reader :name
|
21
|
+
|
22
|
+
# The from state that this token is transitioned from. This is
|
23
|
+
# the _source_. This is used in the constructor in order to
|
24
|
+
# handle lookahead sets.
|
25
|
+
#
|
26
|
+
# @return [Recognizer::State]
|
27
|
+
attr_accessor :from
|
28
|
+
|
29
|
+
# The to state that this token is transitioned to. This is the
|
30
|
+
# _destination_. This is used in the constructor in order to
|
31
|
+
# handle lookahead sets.
|
32
|
+
#
|
33
|
+
# @return [Recognizer::State]
|
34
|
+
attr_accessor :to
|
35
|
+
|
36
|
+
# Initialize.
|
37
|
+
#
|
38
|
+
# @param name [Symbol] the name of the token.
|
39
|
+
# @param value [String?] the value of the token. This is only
|
40
|
+
# used in output representation to the developer.
|
41
|
+
def initialize(name, value = nil)
|
42
|
+
@name = name
|
43
|
+
@value = value
|
44
|
+
@from = nil
|
45
|
+
@to = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
include Comparable
|
49
|
+
|
50
|
+
# Whether or not the token is a terminal.
|
51
|
+
#
|
52
|
+
# @abstract
|
53
|
+
# @return [Boolean]
|
54
|
+
def terminal?
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Whether or not the token is a nonterminal.
|
59
|
+
#
|
60
|
+
# @abstract
|
61
|
+
# @return [Boolean]
|
62
|
+
def nonterminal?
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
# Whether or not the token is an epsilon token.
|
67
|
+
#
|
68
|
+
# @abstract
|
69
|
+
# @return [Boolean]
|
70
|
+
def epsilon?
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
# Whether or not the token is an error token.
|
75
|
+
#
|
76
|
+
# @abstract
|
77
|
+
# @return [Boolean]
|
78
|
+
def error?
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
# Gives a string representation of the token. The output is
|
83
|
+
# formatted like so: `<data>["(" [<from_id>][:<to_id>] ")"]`,
|
84
|
+
# where `<data>` is either the value (if it's non-nil) or the
|
85
|
+
# name, `<from_id>` is the from state id, and `<to_id>` is the
|
86
|
+
# to state id. The last part of the format is optional; if
|
87
|
+
# neither the from state or to state is non-nil, it's non-
|
88
|
+
# existant.
|
89
|
+
#
|
90
|
+
# @return [String] the string representation.
|
91
|
+
# @see #from
|
92
|
+
# @see #to
|
93
|
+
# @see #name
|
94
|
+
def to_s
|
95
|
+
buf = if @value
|
96
|
+
@value.inspect
|
97
|
+
else
|
98
|
+
@name.to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
if from or to
|
102
|
+
buf << "("
|
103
|
+
buf << "#{from.id}" if from
|
104
|
+
buf << ":#{to.id}" if to
|
105
|
+
buf << ")"
|
106
|
+
end
|
107
|
+
|
108
|
+
buf
|
109
|
+
end
|
110
|
+
|
111
|
+
# Compares this class to any other object. If the other object
|
112
|
+
# is a token, it converts both this class and the other object
|
113
|
+
# to an array and compares the array. Otherwise, it delegates
|
114
|
+
# the comparison.
|
115
|
+
#
|
116
|
+
# @param other [Object] the other object to compare.
|
117
|
+
# @return [Numeric]
|
118
|
+
def <=>(other)
|
119
|
+
if other.is_a? Token
|
120
|
+
to_a <=> other.to_a
|
121
|
+
else
|
122
|
+
super
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Compares this class and another object, fuzzily. If the other
|
127
|
+
# object is a token, it removes the transitions (to and from)
|
128
|
+
# on both objects and compares them like that. Otherwise, it
|
129
|
+
# delegates the comparison.
|
130
|
+
#
|
131
|
+
# @param other [Object] the other object to compare.
|
132
|
+
# @return [Boolean] if they are equal.
|
133
|
+
def ===(other)
|
134
|
+
if other.is_a? Token
|
135
|
+
without_transitions == other.without_transitions
|
136
|
+
else
|
137
|
+
super
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Creates a new token without to or from states.
|
142
|
+
#
|
143
|
+
# @return [Token]
|
144
|
+
def without_transitions
|
145
|
+
self.class.new(name, @value)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Generates a hashs for this class.
|
149
|
+
#
|
150
|
+
# @note This is not intended for use. It is only defined to be
|
151
|
+
# compatible with Hashs (and by extension, Sets).
|
152
|
+
# @private
|
153
|
+
# @return [Object]
|
154
|
+
def hash
|
155
|
+
to_a.hash
|
156
|
+
end
|
157
|
+
|
158
|
+
alias_method :eql?, :==
|
159
|
+
|
160
|
+
# Creates an array representation of this class.
|
161
|
+
#
|
162
|
+
# @note This is not intended for use. It is only defined to
|
163
|
+
# make equality checking easier, and to create a hash.
|
164
|
+
# @private
|
165
|
+
# @return [Array<(Recognizer::State, Recognizer::State, Class, Symbol, String?)>]
|
166
|
+
def to_a
|
167
|
+
[to, from, self.class, name, @value]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
data/lib/antelope/ace.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "antelope/ace/errors"
|
2
|
+
require "antelope/ace/scanner"
|
3
|
+
require "antelope/ace/compiler"
|
4
|
+
require "antelope/ace/token"
|
5
|
+
require "antelope/ace/presidence"
|
6
|
+
require "antelope/ace/grammar"
|
7
|
+
|
8
|
+
module Antelope
|
9
|
+
|
10
|
+
# Defines the Ace file. The Ace file format works similarly to
|
11
|
+
# bison's y file format. The Ace file is seperated into three
|
12
|
+
# parts:
|
13
|
+
#
|
14
|
+
# <first>
|
15
|
+
# %%
|
16
|
+
# <second>
|
17
|
+
# %%
|
18
|
+
# <third>
|
19
|
+
#
|
20
|
+
# All parts may be empty; thus, the minimal file that Ace will
|
21
|
+
# accept would be
|
22
|
+
#
|
23
|
+
# %%
|
24
|
+
# %%
|
25
|
+
#
|
26
|
+
# The first part consists of _directives_ and _blocks_; directives
|
27
|
+
# look something like `"%" <directive>[ <argument>]*\n`, with
|
28
|
+
# `<directive>` being any alphanumerical character, including
|
29
|
+
# underscores and dashes, and `<argument>` being any word character
|
30
|
+
# or a quote-delimited string. Blocks consist of
|
31
|
+
# `"%{" <content> "\n" "\s"* "%}"`, with `<content>` being any
|
32
|
+
# characters. The content is copied directly into the body of the
|
33
|
+
# output.
|
34
|
+
#
|
35
|
+
# The second part consists of rules. Rules look something like
|
36
|
+
# this:
|
37
|
+
#
|
38
|
+
# <nonterminal>: (<nonterminal> | <terminal>)* ["{" <content> "}"] ["|" (<nonterminal> | <terminal>)* ["{" <content> "}"]]* [;]
|
39
|
+
#
|
40
|
+
# Where `<nonterminal>` is any lowercase alphabetical cahracter,
|
41
|
+
# `<terminal>` is any uppercase alphabetical character, and
|
42
|
+
# `<content>` is code to be used in the output file upon matching
|
43
|
+
# the specific rule.
|
44
|
+
#
|
45
|
+
# The thid part consists of a body, which is copied directly into
|
46
|
+
# the output.
|
47
|
+
module Ace
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Antelope
|
2
|
+
class Automaton
|
3
|
+
attr_accessor :states
|
4
|
+
attr_accessor :alphabet
|
5
|
+
attr_accessor :start
|
6
|
+
attr_accessor :accept
|
7
|
+
attr_accessor :transitions
|
8
|
+
attr_accessor :stack
|
9
|
+
|
10
|
+
def initialize(states = [], alphabet = [],
|
11
|
+
start = nil, accept = [], transitions = {})
|
12
|
+
@states = states
|
13
|
+
@alphabet = alphabet
|
14
|
+
@start = start
|
15
|
+
@accept = accept
|
16
|
+
@transitions = transitions
|
17
|
+
@stack = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def run(input, &block)
|
21
|
+
block = block || method(:default_transition)
|
22
|
+
|
23
|
+
@stack = [@start]
|
24
|
+
|
25
|
+
input.each do |part|
|
26
|
+
@stack.push(block.call(@stack.last, part))
|
27
|
+
end
|
28
|
+
|
29
|
+
@accept.include? @stack.last
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_transition(state, part)
|
33
|
+
@transitions[state][part]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "antelope/generation/conflictor/conflict"
|
2
|
+
|
3
|
+
module Antelope
|
4
|
+
module Generation
|
5
|
+
class Conflictor
|
6
|
+
|
7
|
+
attr_accessor :parser
|
8
|
+
attr_accessor :conflicts
|
9
|
+
|
10
|
+
def initialize(parser)
|
11
|
+
@parser = parser
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
recognize_conflicts
|
16
|
+
end
|
17
|
+
|
18
|
+
def recognize_conflicts
|
19
|
+
|
20
|
+
@conflicts = []
|
21
|
+
|
22
|
+
parser.states.each do |state|
|
23
|
+
state.rules.each do |rule|
|
24
|
+
if rule.lookahead.
|
25
|
+
any? { |tok| state.transitions.key?(tok.name) }
|
26
|
+
@conflicts << Conflict.new(state, :shift_reduce, [rule],
|
27
|
+
rule.lookahead - state.transitions.keys)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
final_rules = state.rules.select(&:final?)
|
32
|
+
|
33
|
+
final_rules.each_cons(2) do |r1, r2|
|
34
|
+
if r1.lookahead.intersect? r2.lookahead
|
35
|
+
@conflicts << Conflict.new(state,
|
36
|
+
:reduce_reduce,
|
37
|
+
[r1, r2],
|
38
|
+
r1.lookahead.intersection(r2.lookahead))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Antelope
|
2
|
+
module Generation
|
3
|
+
class Constructor
|
4
|
+
module First
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@firstifying = []
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def first(token)
|
12
|
+
case token
|
13
|
+
when Ace::Token::Nonterminal
|
14
|
+
firstifying(token) do
|
15
|
+
productions = parser.productions[token.name]
|
16
|
+
productions.map { |prod|
|
17
|
+
first(prod[:items]) }.inject(Set.new, :+)
|
18
|
+
end
|
19
|
+
when Array
|
20
|
+
first_array(token)
|
21
|
+
when Ace::Token::Epsilon
|
22
|
+
Set.new
|
23
|
+
when Ace::Token::Terminal
|
24
|
+
Set.new([token])
|
25
|
+
else
|
26
|
+
incorrect_argument! token, Ace::Token, Array
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def first_array(token)
|
33
|
+
token.dup.delete_if { |tok| @firstifying.include?(tok) }.
|
34
|
+
each_with_index.take_while do |tok, i|
|
35
|
+
if i.zero?
|
36
|
+
true
|
37
|
+
else
|
38
|
+
nullable?(token[i - 1])
|
39
|
+
end
|
40
|
+
end.map(&:first).map { |tok| first(tok) }.inject(Set.new, :+)
|
41
|
+
end
|
42
|
+
|
43
|
+
def firstifying(tok)
|
44
|
+
@firstifying << tok
|
45
|
+
out = yield
|
46
|
+
@firstifying.delete tok
|
47
|
+
out
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Antelope
|
2
|
+
module Generation
|
3
|
+
class Constructor
|
4
|
+
module Follow
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@follows = {}
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
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
|
18
|
+
end
|
19
|
+
|
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
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@follows[token] = set
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Antelope
|
2
|
+
module Generation
|
3
|
+
class Constructor
|
4
|
+
module Lookahead
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@lookaheads = {}
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def lookahead(left, right = nil)
|
12
|
+
@lookaheads.fetch([left, right]) do
|
13
|
+
if right
|
14
|
+
set = Set.new
|
15
|
+
|
16
|
+
set += if nullable?(right)
|
17
|
+
first(right) + follow(left)
|
18
|
+
else
|
19
|
+
first(right)
|
20
|
+
end
|
21
|
+
else
|
22
|
+
set = lookahead_nonterminal(left)
|
23
|
+
end
|
24
|
+
|
25
|
+
@lookaheads[[left, right]] = set
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def lookahead_nonterminal(left)
|
32
|
+
set = Set.new
|
33
|
+
parser.productions[left].each do |production|
|
34
|
+
set += lookahead(left, production[:items])
|
35
|
+
end
|
36
|
+
|
37
|
+
set
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Antelope
|
2
|
+
module Generation
|
3
|
+
class Constructor
|
4
|
+
module Nullable
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@nullifying = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def nullable?(token)
|
11
|
+
case token
|
12
|
+
when Ace::Token::Nonterminal
|
13
|
+
nullifying(token) do
|
14
|
+
productions = parser.productions[token.name]
|
15
|
+
!!productions.any? { |prod| nullable?(prod[:items]) }
|
16
|
+
end
|
17
|
+
when Array
|
18
|
+
token.dup.delete_if { |tok|
|
19
|
+
@nullifying.include?(tok) }.all? { |tok| nullable?(tok) }
|
20
|
+
when Ace::Token::Epsilon
|
21
|
+
true
|
22
|
+
when Ace::Token::Terminal
|
23
|
+
false
|
24
|
+
else
|
25
|
+
incorrect_argument! token, Ace::Token, Array
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def nullifying(tok)
|
32
|
+
@nullifying << tok
|
33
|
+
out = yield
|
34
|
+
@nullifying.delete tok
|
35
|
+
out
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "set"
|
2
|
+
require "antelope/generation/constructor/nullable"
|
3
|
+
require "antelope/generation/constructor/first"
|
4
|
+
require "antelope/generation/constructor/follow"
|
5
|
+
require "antelope/generation/constructor/lookahead"
|
6
|
+
|
7
|
+
module Antelope
|
8
|
+
module Generation
|
9
|
+
class Constructor
|
10
|
+
|
11
|
+
include Nullable
|
12
|
+
include First
|
13
|
+
include Follow
|
14
|
+
include Lookahead
|
15
|
+
|
16
|
+
attr_reader :parser
|
17
|
+
attr_reader :productions
|
18
|
+
|
19
|
+
def initialize(parser)
|
20
|
+
@parser = parser
|
21
|
+
@productions = []
|
22
|
+
super()
|
23
|
+
end
|
24
|
+
|
25
|
+
def call
|
26
|
+
parser.states.each do |state|
|
27
|
+
augment_state(state)
|
28
|
+
end.each do |state|
|
29
|
+
augment_rules(state)
|
30
|
+
end
|
31
|
+
|
32
|
+
@productions
|
33
|
+
end
|
34
|
+
|
35
|
+
def augment_state(state)
|
36
|
+
state.rules.select { |x| x.position.zero? }.each do |rule|
|
37
|
+
current_state = state
|
38
|
+
|
39
|
+
rule.left.from = state
|
40
|
+
rule.left.to = state.transitions[rule.left.name]
|
41
|
+
|
42
|
+
states = [state]
|
43
|
+
|
44
|
+
rule.right.each_with_index do |part, pos|
|
45
|
+
transition = current_state.transitions[part.name]
|
46
|
+
if part.nonterminal?
|
47
|
+
part.from = current_state
|
48
|
+
part.to = transition
|
49
|
+
end
|
50
|
+
|
51
|
+
states.push(transition)
|
52
|
+
current_state = transition
|
53
|
+
end
|
54
|
+
|
55
|
+
productions << rule unless productions.include?(rule)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def augment_rules(state)
|
60
|
+
state.rules.select { |x| x.position.zero? }.each do |rule|
|
61
|
+
current_state = state
|
62
|
+
|
63
|
+
rule.right.each do |part|
|
64
|
+
transition = current_state.transitions[part.name]
|
65
|
+
current_state = transition
|
66
|
+
end
|
67
|
+
|
68
|
+
final = current_state.rule_for(rule)
|
69
|
+
|
70
|
+
final.lookahead = follow(rule.left)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def incorrect_argument!(arg, *types)
|
77
|
+
raise ArgumentError, "Expected one of #{types.join(", ")}, got #{arg.class}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|