antelope 0.2.0 → 0.2.2
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/.gitignore +25 -23
- data/.rspec +3 -3
- data/.travis.yml +10 -9
- data/.yardopts +7 -7
- data/CONTRIBUTING.md +38 -38
- data/GENERATORS.md +124 -124
- data/Gemfile +7 -7
- data/LICENSE.txt +22 -22
- data/README.md +104 -104
- data/Rakefile +2 -2
- data/TODO.md +58 -58
- data/antelope.gemspec +28 -28
- data/bin/antelope +7 -7
- data/examples/deterministic.ace +35 -35
- data/examples/example.ace +51 -50
- data/examples/example.err +192 -0
- data/examples/{example.output → example.inf} +384 -385
- data/examples/liquidscript.ace +233 -162
- data/examples/simple.ace +22 -22
- data/lib/antelope/ace/compiler.rb +334 -334
- data/lib/antelope/ace/errors.rb +48 -48
- data/lib/antelope/ace/grammar/generation.rb +80 -80
- data/lib/antelope/ace/grammar/loading.rb +53 -53
- data/lib/antelope/ace/grammar/precedences.rb +68 -65
- data/lib/antelope/ace/grammar/productions.rb +156 -150
- data/lib/antelope/ace/grammar/symbols.rb +66 -66
- data/lib/antelope/ace/grammar.rb +69 -69
- data/lib/antelope/ace/precedence.rb +61 -61
- data/lib/antelope/ace/production.rb +57 -57
- data/lib/antelope/ace/scanner/argument.rb +57 -57
- data/lib/antelope/ace/scanner/first.rb +89 -89
- data/lib/antelope/ace/scanner/second.rb +177 -177
- data/lib/antelope/ace/scanner/third.rb +27 -27
- data/lib/antelope/ace/scanner.rb +134 -134
- data/lib/antelope/ace/token/epsilon.rb +24 -24
- data/lib/antelope/ace/token/error.rb +26 -26
- data/lib/antelope/ace/token/nonterminal.rb +17 -17
- data/lib/antelope/ace/token/terminal.rb +17 -17
- data/lib/antelope/ace/token.rb +238 -238
- data/lib/antelope/ace.rb +53 -53
- data/lib/antelope/cli.rb +55 -55
- data/lib/antelope/errors.rb +8 -8
- data/lib/antelope/generation/constructor/first.rb +88 -88
- data/lib/antelope/generation/constructor/follow.rb +103 -103
- data/lib/antelope/generation/constructor/nullable.rb +64 -64
- data/lib/antelope/generation/constructor.rb +126 -126
- data/lib/antelope/generation/errors.rb +17 -17
- data/lib/antelope/generation/null.rb +13 -13
- data/lib/antelope/generation/recognizer/rule.rb +216 -216
- data/lib/antelope/generation/recognizer/state.rb +130 -130
- data/lib/antelope/generation/recognizer.rb +180 -180
- data/lib/antelope/generation/tableizer.rb +175 -154
- data/lib/antelope/generation.rb +15 -15
- data/lib/antelope/generator/base.rb +264 -264
- data/lib/antelope/generator/c.rb +11 -11
- data/lib/antelope/generator/c_header.rb +105 -105
- data/lib/antelope/generator/c_source.rb +39 -39
- data/lib/antelope/generator/error.rb +34 -0
- data/lib/antelope/generator/group.rb +57 -57
- data/lib/antelope/generator/html.rb +51 -0
- data/lib/antelope/generator/info.rb +47 -0
- data/lib/antelope/generator/null.rb +18 -18
- data/lib/antelope/generator/output.rb +17 -49
- data/lib/antelope/generator/ruby.rb +79 -79
- data/lib/antelope/generator/templates/c_header.ant +36 -36
- data/lib/antelope/generator/templates/c_source.ant +202 -202
- data/lib/antelope/generator/templates/error.ant +33 -0
- data/lib/antelope/generator/templates/html/antelope.css +1 -0
- data/lib/antelope/generator/templates/html/antelope.html +1 -0
- data/lib/antelope/generator/templates/html/antelope.js +1 -0
- data/lib/antelope/generator/templates/html/css.ant +53 -0
- data/lib/antelope/generator/templates/html/html.ant +82 -0
- data/lib/antelope/generator/templates/html/js.ant +9 -0
- data/lib/antelope/generator/templates/info.ant +53 -0
- data/lib/antelope/generator/templates/ruby.ant +178 -146
- data/lib/antelope/generator.rb +66 -63
- data/lib/antelope/template/compiler.rb +78 -78
- data/lib/antelope/template/errors.rb +9 -9
- data/lib/antelope/template/scanner.rb +109 -109
- data/lib/antelope/template.rb +65 -60
- data/lib/antelope/version.rb +6 -6
- data/lib/antelope.rb +13 -13
- data/optimizations.txt +42 -0
- data/spec/antelope/ace/compiler_spec.rb +60 -60
- data/spec/antelope/ace/scanner_spec.rb +27 -27
- data/spec/antelope/constructor_spec.rb +133 -136
- data/spec/antelope/template_spec.rb +50 -49
- data/spec/fixtures/simple.ace +22 -22
- data/spec/spec_helper.rb +39 -39
- data/spec/support/benchmark_helper.rb +5 -5
- data/spec/support/grammar_helper.rb +15 -15
- data/subl/Ace (Ruby).JSON-tmLanguage +94 -94
- data/subl/Ace (Ruby).tmLanguage +153 -153
- metadata +17 -6
- data/lib/antelope/generator/templates/output.ant +0 -68
@@ -1,334 +1,334 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require "rubygems/requirement"
|
4
|
-
|
5
|
-
module Antelope
|
6
|
-
module Ace
|
7
|
-
|
8
|
-
# Compiles a set of tokens generated by {Scanner}. These tokens
|
9
|
-
# may not nessicarily have been generated by {Scanner}, however
|
10
|
-
# the tokens must follow the same rules even still.
|
11
|
-
#
|
12
|
-
# A list of all tokens that this compiler accepts:
|
13
|
-
#
|
14
|
-
# - `:directive` (2 arguments)
|
15
|
-
# - `:copy` (1 argument)
|
16
|
-
# - `:second` (no arguments)
|
17
|
-
# - `:label` (2 arguments)
|
18
|
-
# - `:part` (2 arguments)
|
19
|
-
# - `:or` (no arguments)
|
20
|
-
# - `:prec` (1 argument)
|
21
|
-
# - `:block` (1 argument)
|
22
|
-
# - `:third` (no arguments)
|
23
|
-
# - `:body` (1 argument)
|
24
|
-
#
|
25
|
-
# The tokens are handled by methods that follow the rule
|
26
|
-
# `compile_<token name>`.
|
27
|
-
class Compiler
|
28
|
-
|
29
|
-
# The body of the output compiler. This should be formatted in
|
30
|
-
# the language that the parser is to be written in. Some output
|
31
|
-
# generators may have special syntax that allows the parser to
|
32
|
-
# be put in the body; see the output generators for more.
|
33
|
-
#
|
34
|
-
# @return [String]
|
35
|
-
attr_accessor :body
|
36
|
-
|
37
|
-
# A list of all the rules that are defined in the file. The
|
38
|
-
# rules are defined as such:
|
39
|
-
#
|
40
|
-
# - **`label`** (`Symbol`) — The left-hand side of the rule;
|
41
|
-
# this is the nonterminal that the right side reduces to.
|
42
|
-
# - **`set`** (`Array<Symbol>`) — The right-hand side of the
|
43
|
-
# rule. This is a combination of terminals and nonterminals.
|
44
|
-
# - **`block`** (`String`) — The code to be run on a reduction.
|
45
|
-
# this should be formatted in the language that the output
|
46
|
-
# parser is written in. Optional; default value is `""`.
|
47
|
-
# - **`prec`** (`String`) — The precedence level for the
|
48
|
-
# rule. This should be a nonterminal or terminal. Optional;
|
49
|
-
# default value is `""`.
|
50
|
-
#
|
51
|
-
# @return [Array<Hash>]
|
52
|
-
attr_accessor :rules
|
53
|
-
|
54
|
-
# Options defined by directives in the first part of the file.
|
55
|
-
#
|
56
|
-
# - **`:terminals`** (`Array<Symbol, String?)>)` — A list
|
57
|
-
# of all of the terminals in the language. If this is not
|
58
|
-
# properly defined, the grammar will throw an error saying
|
59
|
-
# that a symbol used in the grammar is not defined.
|
60
|
-
# - **`:prec`** (`Array<(Symbol, Array<Symbol>)>`) — A list
|
61
|
-
# of the precedence rules of the grammar. The first element
|
62
|
-
# of each element is the _type_ of precedence (and should be
|
63
|
-
# any of `:left`, `:right`, or `:nonassoc`), and the second
|
64
|
-
# element should be the symbols that are on that level.
|
65
|
-
# - **`:type`** (`String`) — The type of generator to
|
66
|
-
# generate; this should be a language.
|
67
|
-
# - **`:extra`** (`Hash<Symbol, Array<Object>>`) — Extra
|
68
|
-
# options that are not defined here.
|
69
|
-
# @return [Hash]
|
70
|
-
attr_accessor :options
|
71
|
-
|
72
|
-
# Creates a compiler, and then runs the compiler.
|
73
|
-
#
|
74
|
-
# @param (see #initialize)
|
75
|
-
# @see #compile
|
76
|
-
# @return [Compiler] the compiler.
|
77
|
-
def self.compile(tokens)
|
78
|
-
new(tokens).compile
|
79
|
-
end
|
80
|
-
|
81
|
-
# Initialize the compiler. The compiler keeps track of a state;
|
82
|
-
# this state is basically which part of the file we're in. The
|
83
|
-
# state can be `:first`, `:second`, or `:third`; some tokens
|
84
|
-
# may not exist in certain states.
|
85
|
-
#
|
86
|
-
# @param tokens [Array<Array<(Symbol, Object, ...)>>] the tokens
|
87
|
-
# from the {Scanner}.
|
88
|
-
def initialize(tokens)
|
89
|
-
@tokens = tokens
|
90
|
-
@body = ""
|
91
|
-
@state = :first
|
92
|
-
@rules = []
|
93
|
-
@current = nil
|
94
|
-
@current_label = nil
|
95
|
-
@options = {
|
96
|
-
:terminals => [],
|
97
|
-
:nonterminals => [],
|
98
|
-
:prec => [],
|
99
|
-
:type => nil,
|
100
|
-
:extra => Hashie::Extensions::IndifferentAccess.
|
101
|
-
inject!({})
|
102
|
-
}
|
103
|
-
end
|
104
|
-
|
105
|
-
# Pretty inspect.
|
106
|
-
#
|
107
|
-
# @return [String]
|
108
|
-
def inspect
|
109
|
-
"#<#{self.class} state=#{@state.inspect} options=#{options.inspect}>"
|
110
|
-
end
|
111
|
-
|
112
|
-
# Runs the compiler on the input tokens. For each token,
|
113
|
-
# it calls `compile_<type>` with `<type>` being the first
|
114
|
-
# element of the token, with the remaining part of the array
|
115
|
-
# passed as arguments.
|
116
|
-
#
|
117
|
-
# @return [self]
|
118
|
-
def compile
|
119
|
-
@tokens.each do |token|
|
120
|
-
send(:"compile_#{token[0]}", *token[1..-1])
|
121
|
-
end
|
122
|
-
|
123
|
-
self
|
124
|
-
end
|
125
|
-
|
126
|
-
# Compiles a directive. This may only be triggered in the first
|
127
|
-
# section of the file. The directive accepts two arguments. The
|
128
|
-
# directive name can be any of the following:
|
129
|
-
#
|
130
|
-
# - `:terminal` — adds a terminal. Requires 1-2
|
131
|
-
# arguments; the first argument is the terminal name, and the
|
132
|
-
# second argument is a string that can represent the terminal.
|
133
|
-
# - `:require` — requires a certain version of Antelope.
|
134
|
-
# Requires 1 argument. If the first argument is a version
|
135
|
-
# greater than the current version of Antelope, it raises
|
136
|
-
# an error.
|
137
|
-
# - `:left` — creates a new precedence level, with the
|
138
|
-
# argument values being the symbols. The precedence level
|
139
|
-
# is left associative.
|
140
|
-
# - `:right` — creates a new precedence level, with the
|
141
|
-
# argument valeus being the symbols. The precedence level
|
142
|
-
# is right associative.
|
143
|
-
# - `:nonassoc` — creates a nre precedence level, with the
|
144
|
-
# argument values being the symbols. The precedence level
|
145
|
-
# is nonassociative.
|
146
|
-
# - `:type` — the type of parser to generate. This should
|
147
|
-
# correspond to the output language of the parser.
|
148
|
-
#
|
149
|
-
# @param name [String, Symbol] the name of the directive.
|
150
|
-
# Accepts any of `:terminal`, `:require`, `:left`, `:right`,
|
151
|
-
# `:nonassoc`, and `:type`. Any other values produce an
|
152
|
-
# error on stderr and are put in the `:extra` hash on
|
153
|
-
# {#options}.
|
154
|
-
# @param args [Array<String>] the arguments to the directive.
|
155
|
-
# @return [void]
|
156
|
-
# @see #options
|
157
|
-
def compile_directive(name, args)
|
158
|
-
require_state! :first
|
159
|
-
name = name.intern
|
160
|
-
case name
|
161
|
-
when :terminal, :token
|
162
|
-
handle_token(args)
|
163
|
-
when :require
|
164
|
-
compare_versions(args[0])
|
165
|
-
when :left, :right, :nonassoc
|
166
|
-
options[:prec] << [name, *args.map(&:intern)]
|
167
|
-
when :language, :generator, :"grammar.type"
|
168
|
-
options[:type] = args[0].downcase
|
169
|
-
when :type
|
170
|
-
raise SyntaxError, "%type directive requires first " \
|
171
|
-
"argument to be caret" unless args[0].caret?
|
172
|
-
|
173
|
-
options[:nonterminals] <<
|
174
|
-
[args[0], args[1..-1].map(&:intern)]
|
175
|
-
when :define
|
176
|
-
compile_extra(args[0], args[1..-1])
|
177
|
-
else
|
178
|
-
compile_extra(name, args)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def compile_extra(name, args)
|
183
|
-
matching = Generator.directives[name.to_s]
|
184
|
-
|
185
|
-
raise NoDirectiveError, "no directive named #{name}" \
|
186
|
-
unless matching
|
187
|
-
|
188
|
-
options[:extra][name] = args
|
189
|
-
end
|
190
|
-
|
191
|
-
# Compiles a copy token. A copy token basically copies its
|
192
|
-
# argument directly into the body. Used in both the first
|
193
|
-
# and third parts.
|
194
|
-
#
|
195
|
-
# @param body [String] the string to copy into the body.
|
196
|
-
# @return [void]
|
197
|
-
def compile_copy(body)
|
198
|
-
require_state! :first, :third
|
199
|
-
@body << body
|
200
|
-
end
|
201
|
-
|
202
|
-
# Sets the state to the second part.
|
203
|
-
#
|
204
|
-
# @return [void]
|
205
|
-
def compile_second
|
206
|
-
@state = :second
|
207
|
-
end
|
208
|
-
|
209
|
-
# Compiles a label. This starts a rule definition. The token
|
210
|
-
# should only exist in the second part. A rule definition
|
211
|
-
# occurs by setting the `@current_label` to the first argument,
|
212
|
-
# and `@current` to a blank rule save the label set. If a
|
213
|
-
# rule definition was already in progress, it is completed.
|
214
|
-
#
|
215
|
-
# @param label [String] the left-hand side of the rule; it
|
216
|
-
# should be a nonterminal.
|
217
|
-
# @return [void]
|
218
|
-
def compile_label(label, val)
|
219
|
-
require_state! :second
|
220
|
-
if @current
|
221
|
-
@rules << @current
|
222
|
-
end
|
223
|
-
|
224
|
-
label = label.intern
|
225
|
-
@current_label = [label, val]
|
226
|
-
|
227
|
-
@current = {
|
228
|
-
label: label,
|
229
|
-
label_id: val,
|
230
|
-
set: [],
|
231
|
-
block: "",
|
232
|
-
prec: ""
|
233
|
-
}
|
234
|
-
end
|
235
|
-
|
236
|
-
# Compiles a part. This should only occur during a rule
|
237
|
-
# definition. The token should only exist in the second part.
|
238
|
-
# It adds the first argument to the set of the current rule.
|
239
|
-
#
|
240
|
-
# @param text [String] the symbol to append to the current rule.
|
241
|
-
def compile_part(text, val)
|
242
|
-
require_state! :second
|
243
|
-
@current[:set] << [text.intern, val]
|
244
|
-
end
|
245
|
-
|
246
|
-
# Compiles an or. This should only occur in a rule definition,
|
247
|
-
# and in the second part. It starts a new rule definition by
|
248
|
-
# calling {#compile_label} with the current label.
|
249
|
-
#
|
250
|
-
# @return [void]
|
251
|
-
# @see #compile_label
|
252
|
-
def compile_or
|
253
|
-
compile_label(*@current_label)
|
254
|
-
end
|
255
|
-
|
256
|
-
# Compiles the precedence operator. This should only occur in a
|
257
|
-
# rule definition, and in the second part. It sets the
|
258
|
-
# precedence definition on the current rule.
|
259
|
-
#
|
260
|
-
# @param prec [String] the precedence of the rule.
|
261
|
-
# @return [void]
|
262
|
-
def compile_prec(prec)
|
263
|
-
require_state! :second
|
264
|
-
@current[:prec] = prec
|
265
|
-
end
|
266
|
-
|
267
|
-
# Compiles a block. This should only occur in a rule
|
268
|
-
# definition, and in the second part. It sets the block on the
|
269
|
-
# current rule.
|
270
|
-
#
|
271
|
-
# @param block [String] the block.
|
272
|
-
# @return [void]
|
273
|
-
def compile_block(block)
|
274
|
-
require_state! :second
|
275
|
-
@current[:block] = block
|
276
|
-
end
|
277
|
-
|
278
|
-
# Sets the state to the third part. If a rule definition was
|
279
|
-
# in progress, it finishes the rule.
|
280
|
-
#
|
281
|
-
# @return [void]
|
282
|
-
def compile_third
|
283
|
-
if @current
|
284
|
-
@rules << @current
|
285
|
-
@current_label = @current = nil
|
286
|
-
end
|
287
|
-
|
288
|
-
@state = :third
|
289
|
-
end
|
290
|
-
|
291
|
-
private
|
292
|
-
|
293
|
-
def handle_token(args)
|
294
|
-
type = ""
|
295
|
-
if args[0].caret?
|
296
|
-
type = args.shift
|
297
|
-
end
|
298
|
-
|
299
|
-
name = args.shift
|
300
|
-
value = args.shift
|
301
|
-
|
302
|
-
options[:terminals] << [name.intern, type, nil, value]
|
303
|
-
end
|
304
|
-
|
305
|
-
# Checks the current state against the given states.
|
306
|
-
#
|
307
|
-
# @raise [InvalidStateError] if none of the given states match
|
308
|
-
# the current state.
|
309
|
-
# @return [void]
|
310
|
-
def require_state!(*state)
|
311
|
-
raise InvalidStateError,
|
312
|
-
"In state #{@state}, " \
|
313
|
-
"required state #{state.join(", ")}" \
|
314
|
-
unless state.include?(@state)
|
315
|
-
end
|
316
|
-
|
317
|
-
# Compares the required version and the Antelope version.
|
318
|
-
#
|
319
|
-
# @raise [IncompatibleVersionError] if the Antelope version
|
320
|
-
# doesn't meet the requirement.
|
321
|
-
# @return [void]
|
322
|
-
def compare_versions(required)
|
323
|
-
antelope_version = Gem::Version.new(Antelope::VERSION)
|
324
|
-
required_version = Gem::Requirement.new(required)
|
325
|
-
|
326
|
-
unless required_version =~ antelope_version
|
327
|
-
raise IncompatibleVersionError,
|
328
|
-
"Grammar requires #{required}, " \
|
329
|
-
"have #{Antelope::VERSION}"
|
330
|
-
end
|
331
|
-
end
|
332
|
-
end
|
333
|
-
end
|
334
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "rubygems/requirement"
|
4
|
+
|
5
|
+
module Antelope
|
6
|
+
module Ace
|
7
|
+
|
8
|
+
# Compiles a set of tokens generated by {Scanner}. These tokens
|
9
|
+
# may not nessicarily have been generated by {Scanner}, however
|
10
|
+
# the tokens must follow the same rules even still.
|
11
|
+
#
|
12
|
+
# A list of all tokens that this compiler accepts:
|
13
|
+
#
|
14
|
+
# - `:directive` (2 arguments)
|
15
|
+
# - `:copy` (1 argument)
|
16
|
+
# - `:second` (no arguments)
|
17
|
+
# - `:label` (2 arguments)
|
18
|
+
# - `:part` (2 arguments)
|
19
|
+
# - `:or` (no arguments)
|
20
|
+
# - `:prec` (1 argument)
|
21
|
+
# - `:block` (1 argument)
|
22
|
+
# - `:third` (no arguments)
|
23
|
+
# - `:body` (1 argument)
|
24
|
+
#
|
25
|
+
# The tokens are handled by methods that follow the rule
|
26
|
+
# `compile_<token name>`.
|
27
|
+
class Compiler
|
28
|
+
|
29
|
+
# The body of the output compiler. This should be formatted in
|
30
|
+
# the language that the parser is to be written in. Some output
|
31
|
+
# generators may have special syntax that allows the parser to
|
32
|
+
# be put in the body; see the output generators for more.
|
33
|
+
#
|
34
|
+
# @return [String]
|
35
|
+
attr_accessor :body
|
36
|
+
|
37
|
+
# A list of all the rules that are defined in the file. The
|
38
|
+
# rules are defined as such:
|
39
|
+
#
|
40
|
+
# - **`label`** (`Symbol`) — The left-hand side of the rule;
|
41
|
+
# this is the nonterminal that the right side reduces to.
|
42
|
+
# - **`set`** (`Array<Symbol>`) — The right-hand side of the
|
43
|
+
# rule. This is a combination of terminals and nonterminals.
|
44
|
+
# - **`block`** (`String`) — The code to be run on a reduction.
|
45
|
+
# this should be formatted in the language that the output
|
46
|
+
# parser is written in. Optional; default value is `""`.
|
47
|
+
# - **`prec`** (`String`) — The precedence level for the
|
48
|
+
# rule. This should be a nonterminal or terminal. Optional;
|
49
|
+
# default value is `""`.
|
50
|
+
#
|
51
|
+
# @return [Array<Hash>]
|
52
|
+
attr_accessor :rules
|
53
|
+
|
54
|
+
# Options defined by directives in the first part of the file.
|
55
|
+
#
|
56
|
+
# - **`:terminals`** (`Array<Symbol, String?)>)` — A list
|
57
|
+
# of all of the terminals in the language. If this is not
|
58
|
+
# properly defined, the grammar will throw an error saying
|
59
|
+
# that a symbol used in the grammar is not defined.
|
60
|
+
# - **`:prec`** (`Array<(Symbol, Array<Symbol>)>`) — A list
|
61
|
+
# of the precedence rules of the grammar. The first element
|
62
|
+
# of each element is the _type_ of precedence (and should be
|
63
|
+
# any of `:left`, `:right`, or `:nonassoc`), and the second
|
64
|
+
# element should be the symbols that are on that level.
|
65
|
+
# - **`:type`** (`String`) — The type of generator to
|
66
|
+
# generate; this should be a language.
|
67
|
+
# - **`:extra`** (`Hash<Symbol, Array<Object>>`) — Extra
|
68
|
+
# options that are not defined here.
|
69
|
+
# @return [Hash]
|
70
|
+
attr_accessor :options
|
71
|
+
|
72
|
+
# Creates a compiler, and then runs the compiler.
|
73
|
+
#
|
74
|
+
# @param (see #initialize)
|
75
|
+
# @see #compile
|
76
|
+
# @return [Compiler] the compiler.
|
77
|
+
def self.compile(tokens)
|
78
|
+
new(tokens).compile
|
79
|
+
end
|
80
|
+
|
81
|
+
# Initialize the compiler. The compiler keeps track of a state;
|
82
|
+
# this state is basically which part of the file we're in. The
|
83
|
+
# state can be `:first`, `:second`, or `:third`; some tokens
|
84
|
+
# may not exist in certain states.
|
85
|
+
#
|
86
|
+
# @param tokens [Array<Array<(Symbol, Object, ...)>>] the tokens
|
87
|
+
# from the {Scanner}.
|
88
|
+
def initialize(tokens)
|
89
|
+
@tokens = tokens
|
90
|
+
@body = ""
|
91
|
+
@state = :first
|
92
|
+
@rules = []
|
93
|
+
@current = nil
|
94
|
+
@current_label = nil
|
95
|
+
@options = {
|
96
|
+
:terminals => [],
|
97
|
+
:nonterminals => [],
|
98
|
+
:prec => [],
|
99
|
+
:type => nil,
|
100
|
+
:extra => Hashie::Extensions::IndifferentAccess.
|
101
|
+
inject!({})
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
# Pretty inspect.
|
106
|
+
#
|
107
|
+
# @return [String]
|
108
|
+
def inspect
|
109
|
+
"#<#{self.class} state=#{@state.inspect} options=#{options.inspect}>"
|
110
|
+
end
|
111
|
+
|
112
|
+
# Runs the compiler on the input tokens. For each token,
|
113
|
+
# it calls `compile_<type>` with `<type>` being the first
|
114
|
+
# element of the token, with the remaining part of the array
|
115
|
+
# passed as arguments.
|
116
|
+
#
|
117
|
+
# @return [self]
|
118
|
+
def compile
|
119
|
+
@tokens.each do |token|
|
120
|
+
send(:"compile_#{token[0]}", *token[1..-1])
|
121
|
+
end
|
122
|
+
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
# Compiles a directive. This may only be triggered in the first
|
127
|
+
# section of the file. The directive accepts two arguments. The
|
128
|
+
# directive name can be any of the following:
|
129
|
+
#
|
130
|
+
# - `:terminal` — adds a terminal. Requires 1-2
|
131
|
+
# arguments; the first argument is the terminal name, and the
|
132
|
+
# second argument is a string that can represent the terminal.
|
133
|
+
# - `:require` — requires a certain version of Antelope.
|
134
|
+
# Requires 1 argument. If the first argument is a version
|
135
|
+
# greater than the current version of Antelope, it raises
|
136
|
+
# an error.
|
137
|
+
# - `:left` — creates a new precedence level, with the
|
138
|
+
# argument values being the symbols. The precedence level
|
139
|
+
# is left associative.
|
140
|
+
# - `:right` — creates a new precedence level, with the
|
141
|
+
# argument valeus being the symbols. The precedence level
|
142
|
+
# is right associative.
|
143
|
+
# - `:nonassoc` — creates a nre precedence level, with the
|
144
|
+
# argument values being the symbols. The precedence level
|
145
|
+
# is nonassociative.
|
146
|
+
# - `:type` — the type of parser to generate. This should
|
147
|
+
# correspond to the output language of the parser.
|
148
|
+
#
|
149
|
+
# @param name [String, Symbol] the name of the directive.
|
150
|
+
# Accepts any of `:terminal`, `:require`, `:left`, `:right`,
|
151
|
+
# `:nonassoc`, and `:type`. Any other values produce an
|
152
|
+
# error on stderr and are put in the `:extra` hash on
|
153
|
+
# {#options}.
|
154
|
+
# @param args [Array<String>] the arguments to the directive.
|
155
|
+
# @return [void]
|
156
|
+
# @see #options
|
157
|
+
def compile_directive(name, args)
|
158
|
+
require_state! :first
|
159
|
+
name = name.intern
|
160
|
+
case name
|
161
|
+
when :terminal, :token
|
162
|
+
handle_token(args)
|
163
|
+
when :require
|
164
|
+
compare_versions(args[0])
|
165
|
+
when :left, :right, :nonassoc
|
166
|
+
options[:prec] << [name, *args.map(&:intern)]
|
167
|
+
when :language, :generator, :"grammar.type"
|
168
|
+
options[:type] = args[0].downcase
|
169
|
+
when :type
|
170
|
+
raise SyntaxError, "%type directive requires first " \
|
171
|
+
"argument to be caret" unless args[0].caret?
|
172
|
+
|
173
|
+
options[:nonterminals] <<
|
174
|
+
[args[0], args[1..-1].map(&:intern)]
|
175
|
+
when :define
|
176
|
+
compile_extra(args[0], args[1..-1])
|
177
|
+
else
|
178
|
+
compile_extra(name, args)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def compile_extra(name, args)
|
183
|
+
matching = Generator.directives[name.to_s]
|
184
|
+
|
185
|
+
raise NoDirectiveError, "no directive named #{name}" \
|
186
|
+
unless matching
|
187
|
+
|
188
|
+
options[:extra][name] = args
|
189
|
+
end
|
190
|
+
|
191
|
+
# Compiles a copy token. A copy token basically copies its
|
192
|
+
# argument directly into the body. Used in both the first
|
193
|
+
# and third parts.
|
194
|
+
#
|
195
|
+
# @param body [String] the string to copy into the body.
|
196
|
+
# @return [void]
|
197
|
+
def compile_copy(body)
|
198
|
+
require_state! :first, :third
|
199
|
+
@body << body
|
200
|
+
end
|
201
|
+
|
202
|
+
# Sets the state to the second part.
|
203
|
+
#
|
204
|
+
# @return [void]
|
205
|
+
def compile_second
|
206
|
+
@state = :second
|
207
|
+
end
|
208
|
+
|
209
|
+
# Compiles a label. This starts a rule definition. The token
|
210
|
+
# should only exist in the second part. A rule definition
|
211
|
+
# occurs by setting the `@current_label` to the first argument,
|
212
|
+
# and `@current` to a blank rule save the label set. If a
|
213
|
+
# rule definition was already in progress, it is completed.
|
214
|
+
#
|
215
|
+
# @param label [String] the left-hand side of the rule; it
|
216
|
+
# should be a nonterminal.
|
217
|
+
# @return [void]
|
218
|
+
def compile_label(label, val)
|
219
|
+
require_state! :second
|
220
|
+
if @current
|
221
|
+
@rules << @current
|
222
|
+
end
|
223
|
+
|
224
|
+
label = label.intern
|
225
|
+
@current_label = [label, val]
|
226
|
+
|
227
|
+
@current = {
|
228
|
+
label: label,
|
229
|
+
label_id: val,
|
230
|
+
set: [],
|
231
|
+
block: "",
|
232
|
+
prec: ""
|
233
|
+
}
|
234
|
+
end
|
235
|
+
|
236
|
+
# Compiles a part. This should only occur during a rule
|
237
|
+
# definition. The token should only exist in the second part.
|
238
|
+
# It adds the first argument to the set of the current rule.
|
239
|
+
#
|
240
|
+
# @param text [String] the symbol to append to the current rule.
|
241
|
+
def compile_part(text, val)
|
242
|
+
require_state! :second
|
243
|
+
@current[:set] << [text.intern, val]
|
244
|
+
end
|
245
|
+
|
246
|
+
# Compiles an or. This should only occur in a rule definition,
|
247
|
+
# and in the second part. It starts a new rule definition by
|
248
|
+
# calling {#compile_label} with the current label.
|
249
|
+
#
|
250
|
+
# @return [void]
|
251
|
+
# @see #compile_label
|
252
|
+
def compile_or
|
253
|
+
compile_label(*@current_label)
|
254
|
+
end
|
255
|
+
|
256
|
+
# Compiles the precedence operator. This should only occur in a
|
257
|
+
# rule definition, and in the second part. It sets the
|
258
|
+
# precedence definition on the current rule.
|
259
|
+
#
|
260
|
+
# @param prec [String] the precedence of the rule.
|
261
|
+
# @return [void]
|
262
|
+
def compile_prec(prec)
|
263
|
+
require_state! :second
|
264
|
+
@current[:prec] = prec
|
265
|
+
end
|
266
|
+
|
267
|
+
# Compiles a block. This should only occur in a rule
|
268
|
+
# definition, and in the second part. It sets the block on the
|
269
|
+
# current rule.
|
270
|
+
#
|
271
|
+
# @param block [String] the block.
|
272
|
+
# @return [void]
|
273
|
+
def compile_block(block)
|
274
|
+
require_state! :second
|
275
|
+
@current[:block] = block
|
276
|
+
end
|
277
|
+
|
278
|
+
# Sets the state to the third part. If a rule definition was
|
279
|
+
# in progress, it finishes the rule.
|
280
|
+
#
|
281
|
+
# @return [void]
|
282
|
+
def compile_third
|
283
|
+
if @current
|
284
|
+
@rules << @current
|
285
|
+
@current_label = @current = nil
|
286
|
+
end
|
287
|
+
|
288
|
+
@state = :third
|
289
|
+
end
|
290
|
+
|
291
|
+
private
|
292
|
+
|
293
|
+
def handle_token(args)
|
294
|
+
type = ""
|
295
|
+
if args[0].caret?
|
296
|
+
type = args.shift
|
297
|
+
end
|
298
|
+
|
299
|
+
name = args.shift
|
300
|
+
value = args.shift
|
301
|
+
|
302
|
+
options[:terminals] << [name.intern, type, nil, value]
|
303
|
+
end
|
304
|
+
|
305
|
+
# Checks the current state against the given states.
|
306
|
+
#
|
307
|
+
# @raise [InvalidStateError] if none of the given states match
|
308
|
+
# the current state.
|
309
|
+
# @return [void]
|
310
|
+
def require_state!(*state)
|
311
|
+
raise InvalidStateError,
|
312
|
+
"In state #{@state}, " \
|
313
|
+
"required state #{state.join(", ")}" \
|
314
|
+
unless state.include?(@state)
|
315
|
+
end
|
316
|
+
|
317
|
+
# Compares the required version and the Antelope version.
|
318
|
+
#
|
319
|
+
# @raise [IncompatibleVersionError] if the Antelope version
|
320
|
+
# doesn't meet the requirement.
|
321
|
+
# @return [void]
|
322
|
+
def compare_versions(required)
|
323
|
+
antelope_version = Gem::Version.new(Antelope::VERSION)
|
324
|
+
required_version = Gem::Requirement.new(required)
|
325
|
+
|
326
|
+
unless required_version =~ antelope_version
|
327
|
+
raise IncompatibleVersionError,
|
328
|
+
"Grammar requires #{required}, " \
|
329
|
+
"have #{Antelope::VERSION}"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|