antelope 0.1.8 → 0.1.9
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 +2 -0
- data/CONTRIBUTING.md +4 -4
- data/GENERATORS.md +61 -19
- data/README.md +84 -9
- data/TODO.md +58 -0
- data/examples/deterministic.ace +21 -9
- data/examples/example.ace +16 -10
- data/examples/example.output +213 -146
- data/examples/simple.ace +1 -1
- data/lib/antelope/ace/compiler.rb +52 -15
- data/lib/antelope/ace/errors.rb +7 -0
- data/lib/antelope/ace/grammar/generation.rb +3 -3
- data/lib/antelope/ace/grammar/precedences.rb +5 -7
- data/lib/antelope/ace/grammar/productions.rb +36 -11
- data/lib/antelope/ace/grammar/{terminals.rb → symbols.rb} +25 -2
- data/lib/antelope/ace/grammar.rb +12 -3
- data/lib/antelope/ace/precedence.rb +4 -0
- data/lib/antelope/ace/scanner/argument.rb +57 -0
- data/lib/antelope/ace/scanner/first.rb +32 -6
- data/lib/antelope/ace/scanner/second.rb +23 -8
- data/lib/antelope/ace/scanner.rb +32 -26
- data/lib/antelope/ace/token.rb +21 -2
- data/lib/antelope/cli.rb +22 -2
- data/lib/antelope/generation/constructor/first.rb +1 -1
- data/lib/antelope/generation/constructor.rb +2 -0
- data/lib/antelope/generation/null.rb +13 -0
- data/lib/antelope/generation/recognizer/rule.rb +4 -3
- data/lib/antelope/generation/recognizer/state.rb +18 -3
- data/lib/antelope/generation/recognizer.rb +19 -24
- data/lib/antelope/generation/tableizer.rb +30 -2
- data/lib/antelope/generation.rb +1 -0
- data/lib/antelope/generator/base.rb +150 -13
- data/lib/antelope/generator/c.rb +11 -0
- data/lib/antelope/generator/c_header.rb +105 -0
- data/lib/antelope/generator/c_source.rb +39 -0
- data/lib/antelope/generator/null.rb +5 -0
- data/lib/antelope/generator/output.rb +3 -3
- data/lib/antelope/generator/ruby.rb +23 -5
- data/lib/antelope/generator/templates/c_header.ant +36 -0
- data/lib/antelope/generator/templates/c_source.ant +202 -0
- data/lib/antelope/generator/templates/output.ant +68 -0
- data/lib/antelope/generator/templates/ruby.ant +146 -0
- data/lib/antelope/generator.rb +15 -3
- data/lib/antelope/template/compiler.rb +78 -0
- data/lib/antelope/template/errors.rb +9 -0
- data/lib/antelope/template/scanner.rb +111 -0
- data/lib/antelope/template.rb +60 -0
- data/lib/antelope/version.rb +1 -1
- data/lib/antelope.rb +1 -0
- data/spec/antelope/template_spec.rb +39 -0
- data/subl/Ace (Ruby).JSON-tmLanguage +94 -0
- data/subl/Ace (Ruby).tmLanguage +153 -0
- metadata +21 -8
- data/examples/deterministic.output +0 -131
- data/examples/simple.output +0 -121
- data/lib/antelope/generator/templates/output.erb +0 -56
- data/lib/antelope/generator/templates/ruby.erb +0 -63
@@ -14,8 +14,8 @@ module Antelope
|
|
14
14
|
# - `:directive` (2 arguments)
|
15
15
|
# - `:copy` (1 argument)
|
16
16
|
# - `:second` (no arguments)
|
17
|
-
# - `:label` (
|
18
|
-
# - `:part` (
|
17
|
+
# - `:label` (2 arguments)
|
18
|
+
# - `:part` (2 arguments)
|
19
19
|
# - `:or` (no arguments)
|
20
20
|
# - `:prec` (1 argument)
|
21
21
|
# - `:block` (1 argument)
|
@@ -92,14 +92,21 @@ module Antelope
|
|
92
92
|
@rules = []
|
93
93
|
@current = nil
|
94
94
|
@current_label = nil
|
95
|
-
@options = {
|
95
|
+
@options = {
|
96
|
+
:terminals => [],
|
97
|
+
:nonterminals => [],
|
98
|
+
:prec => [],
|
99
|
+
:type => nil,
|
100
|
+
:extra => Hashie::Extensions::IndifferentAccess.
|
101
|
+
inject!({})
|
102
|
+
}
|
96
103
|
end
|
97
104
|
|
98
105
|
# Pretty inspect.
|
99
106
|
#
|
100
107
|
# @return [String]
|
101
108
|
def inspect
|
102
|
-
"#<#{self.class} state=#{@state.inspect} options=#{
|
109
|
+
"#<#{self.class} state=#{@state.inspect} options=#{options.inspect}>"
|
103
110
|
end
|
104
111
|
|
105
112
|
# Runs the compiler on the input tokens. For each token,
|
@@ -152,19 +159,35 @@ module Antelope
|
|
152
159
|
name = name.intern
|
153
160
|
case name
|
154
161
|
when :terminal, :token
|
155
|
-
|
162
|
+
handle_token(args)
|
156
163
|
when :require
|
157
164
|
compare_versions(args[0])
|
158
165
|
when :left, :right, :nonassoc
|
159
|
-
|
166
|
+
options[:prec] << [name, *args.map(&:intern)]
|
167
|
+
when :language, :generator, :"grammar.type"
|
168
|
+
options[:type] = args[0].downcase
|
160
169
|
when :type
|
161
|
-
|
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])
|
162
177
|
else
|
163
|
-
|
164
|
-
$stderr.puts "Unknown Directive: #{name}"
|
178
|
+
compile_extra(name, args)
|
165
179
|
end
|
166
180
|
end
|
167
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
|
+
|
168
191
|
# Compiles a copy token. A copy token basically copies its
|
169
192
|
# argument directly into the body. Used in both the first
|
170
193
|
# and third parts.
|
@@ -192,16 +215,18 @@ module Antelope
|
|
192
215
|
# @param label [String] the left-hand side of the rule; it
|
193
216
|
# should be a nonterminal.
|
194
217
|
# @return [void]
|
195
|
-
def compile_label(label)
|
218
|
+
def compile_label(label, val)
|
196
219
|
require_state! :second
|
197
220
|
if @current
|
198
221
|
@rules << @current
|
199
222
|
end
|
200
223
|
|
201
|
-
|
224
|
+
label = label.intern
|
225
|
+
@current_label = [label, val]
|
202
226
|
|
203
227
|
@current = {
|
204
|
-
label:
|
228
|
+
label: label,
|
229
|
+
label_id: val,
|
205
230
|
set: [],
|
206
231
|
block: "",
|
207
232
|
prec: ""
|
@@ -213,9 +238,9 @@ module Antelope
|
|
213
238
|
# It adds the first argument to the set of the current rule.
|
214
239
|
#
|
215
240
|
# @param text [String] the symbol to append to the current rule.
|
216
|
-
def compile_part(text)
|
241
|
+
def compile_part(text, val)
|
217
242
|
require_state! :second
|
218
|
-
@current[:set] << text.intern
|
243
|
+
@current[:set] << [text.intern, val]
|
219
244
|
end
|
220
245
|
|
221
246
|
# Compiles an or. This should only occur in a rule definition,
|
@@ -225,7 +250,7 @@ module Antelope
|
|
225
250
|
# @return [void]
|
226
251
|
# @see #compile_label
|
227
252
|
def compile_or
|
228
|
-
compile_label(
|
253
|
+
compile_label(*@current_label)
|
229
254
|
end
|
230
255
|
|
231
256
|
# Compiles the precedence operator. This should only occur in a
|
@@ -265,6 +290,18 @@ module Antelope
|
|
265
290
|
|
266
291
|
private
|
267
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
|
+
|
268
305
|
# Checks the current state against the given states.
|
269
306
|
#
|
270
307
|
# @raise [InvalidStateError] if none of the given states match
|
data/lib/antelope/ace/errors.rb
CHANGED
@@ -37,5 +37,12 @@ module Antelope
|
|
37
37
|
# generator to use for the generation, it raises this.
|
38
38
|
class NoTypeError < Error
|
39
39
|
end
|
40
|
+
|
41
|
+
# Primarily used in the {Compiler}, it is raised if it encounters
|
42
|
+
# a directive it cannot handle. This is more to warn the
|
43
|
+
# developer that a directive they wrote may not be accepted by any
|
44
|
+
# generator.
|
45
|
+
class NoDirectiveError < Error
|
46
|
+
end
|
40
47
|
end
|
41
48
|
end
|
@@ -33,14 +33,14 @@ module Antelope
|
|
33
33
|
mods = modifiers.map(&:last).
|
34
34
|
map { |x| x.new(self) }
|
35
35
|
mods.each do |mod|
|
36
|
-
puts "Running mod #{mod.class}..."
|
36
|
+
puts "Running mod #{mod.class}..." if options[:verbose]
|
37
37
|
mod.call
|
38
38
|
end
|
39
39
|
hash = Hash[modifiers.map(&:first).zip(mods)]
|
40
40
|
# This is when we'd generate
|
41
41
|
|
42
42
|
find_generators(generators, options).each do |gen|
|
43
|
-
puts "Running generator #{gen}..."
|
43
|
+
puts "Running generator #{gen}..." if options[:verbose]
|
44
44
|
gen.new(self, hash).generate
|
45
45
|
end
|
46
46
|
end
|
@@ -71,7 +71,7 @@ module Antelope
|
|
71
71
|
|
72
72
|
generators
|
73
73
|
|
74
|
-
rescue KeyError
|
74
|
+
rescue KeyError
|
75
75
|
raise NoTypeError, "Undefined type #{type}"
|
76
76
|
end
|
77
77
|
end
|
@@ -27,14 +27,12 @@ module Antelope
|
|
27
27
|
def precedence_for(token)
|
28
28
|
token = token.name if token.is_a?(Token)
|
29
29
|
|
30
|
-
set = Set.new([token, :_])
|
31
|
-
|
32
30
|
prec = precedence.
|
33
|
-
select { |pr|
|
34
|
-
first
|
31
|
+
select { |pr| pr.tokens.include?(token) }.first
|
35
32
|
|
36
|
-
|
37
|
-
|
33
|
+
unless prec
|
34
|
+
prec = precedence.
|
35
|
+
select { |pr| pr.tokens.include?(:_) }.first
|
38
36
|
end
|
39
37
|
|
40
38
|
prec
|
@@ -56,7 +54,7 @@ module Antelope
|
|
56
54
|
end
|
57
55
|
|
58
56
|
precedence <<
|
59
|
-
Ace::Precedence.new(:nonassoc, [
|
57
|
+
Ace::Precedence.new(:nonassoc, [:$end].to_set, 0) <<
|
60
58
|
Ace::Precedence.new(:nonassoc, [:_].to_set, 1)
|
61
59
|
precedence.sort_by { |_| _.level }.reverse
|
62
60
|
end
|
@@ -35,8 +35,10 @@ module Antelope
|
|
35
35
|
# @return [Token]
|
36
36
|
def find_token(value)
|
37
37
|
value = value.intern
|
38
|
+
|
38
39
|
if productions.key?(value)
|
39
|
-
|
40
|
+
typed_nonterminals.find { |term| term.name == value } ||
|
41
|
+
Token::Nonterminal.new(value)
|
40
42
|
elsif terminal = terminals.
|
41
43
|
find { |term| term.name == value }
|
42
44
|
terminal
|
@@ -45,7 +47,8 @@ module Antelope
|
|
45
47
|
elsif [:nothing, :ε].include?(value)
|
46
48
|
Token::Epsilon.new
|
47
49
|
else
|
48
|
-
raise UndefinedTokenError, "Could not find a token
|
50
|
+
raise UndefinedTokenError, "Could not find a token " \
|
51
|
+
"named #{value.inspect}"
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
@@ -88,22 +91,44 @@ module Antelope
|
|
88
91
|
# @param id [Numeric] the id for the production.
|
89
92
|
# @return [Production]
|
90
93
|
def generate_production_for(rule, id)
|
91
|
-
left = rule[:label]
|
92
|
-
items = rule[:set].map { |_| find_token(_) }
|
94
|
+
left = Token::Nonterminal.new(rule[:label])
|
95
|
+
items = rule[:set].map { |_| find_token(_[0]) }
|
93
96
|
prec = if rule[:prec].empty?
|
94
97
|
items.select(&:terminal?).last
|
95
98
|
else
|
96
|
-
|
99
|
+
rule[:prec].intern
|
97
100
|
end
|
98
101
|
|
99
|
-
|
100
|
-
|
102
|
+
prec = precedence_for(prec)
|
103
|
+
left.type = type_for(rule[:label])
|
104
|
+
left.id = rule[:label_id]
|
105
|
+
|
106
|
+
rule[:set].each_with_index do |tok, i|
|
107
|
+
items[i] = items[i].dup
|
108
|
+
items[i].id = tok[1]
|
101
109
|
end
|
102
110
|
|
103
|
-
prec
|
111
|
+
Production.new(left, items, rule[:block], prec, id + 1)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns the defined type for the given token name.
|
115
|
+
# Uses the `%type` directive to infer the corresponding types.
|
116
|
+
#
|
117
|
+
# @param token [Symbol] the token to check for
|
118
|
+
# types.
|
119
|
+
def type_for(token)
|
120
|
+
token = find_token(token) unless token.is_a?(Token)
|
104
121
|
|
105
|
-
|
106
|
-
|
122
|
+
case token
|
123
|
+
when Token::Nonterminal
|
124
|
+
token.type
|
125
|
+
when Token::Terminal
|
126
|
+
token.type
|
127
|
+
when Token::Epsilon
|
128
|
+
""
|
129
|
+
when Token::Error
|
130
|
+
""
|
131
|
+
end
|
107
132
|
end
|
108
133
|
|
109
134
|
# Creates the default production for the grammar. The left
|
@@ -116,7 +141,7 @@ module Antelope
|
|
116
141
|
def default_production
|
117
142
|
Production.new(Token::Nonterminal.new(:$start), [
|
118
143
|
Token::Nonterminal.new(@compiler.rules.first[:label]),
|
119
|
-
Token::Terminal.new(
|
144
|
+
Token::Terminal.new(:$end)
|
120
145
|
], "", precedence.last, 0)
|
121
146
|
end
|
122
147
|
end
|
@@ -4,8 +4,8 @@ module Antelope
|
|
4
4
|
module Ace
|
5
5
|
class Grammar
|
6
6
|
|
7
|
-
# Manages a list of the
|
8
|
-
module
|
7
|
+
# Manages a list of the symbols in the grammar.
|
8
|
+
module Symbols
|
9
9
|
|
10
10
|
# A list of all terminals in the grammar. Checks the compiler
|
11
11
|
# options for terminals, and then returns an array of
|
@@ -28,6 +28,21 @@ module Antelope
|
|
28
28
|
@_nonterminals ||= productions.keys
|
29
29
|
end
|
30
30
|
|
31
|
+
# A list of all nonterminals, with types.
|
32
|
+
#
|
33
|
+
# @return [Array<Token::Nonterminal>>]
|
34
|
+
def typed_nonterminals
|
35
|
+
@_typed_nonterminals ||= begin
|
36
|
+
typed = []
|
37
|
+
compiler.options[:nonterminals].each do |data|
|
38
|
+
data[1].each do |nonterm|
|
39
|
+
typed << Token::Nonterminal.new(nonterm, data[0])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
typed
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
31
46
|
# A list of all symbols in the grammar; includes both
|
32
47
|
# terminals and nonterminals.
|
33
48
|
#
|
@@ -37,6 +52,14 @@ module Antelope
|
|
37
52
|
def symbols
|
38
53
|
@_symbols ||= terminals + nonterminals
|
39
54
|
end
|
55
|
+
|
56
|
+
# Checks to see if the grammar uses the `error` terminal
|
57
|
+
# anywhere.
|
58
|
+
#
|
59
|
+
# @return [Boolean]
|
60
|
+
def contains_error_token?
|
61
|
+
all_productions.any? { |_| _.items.any?(&:error?) }
|
62
|
+
end
|
40
63
|
end
|
41
64
|
end
|
42
65
|
end
|
data/lib/antelope/ace/grammar.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "hashie"
|
4
|
+
require "antelope/ace/grammar/symbols"
|
4
5
|
require "antelope/ace/grammar/productions"
|
5
6
|
require "antelope/ace/grammar/precedences"
|
6
7
|
require "antelope/ace/grammar/loading"
|
@@ -10,11 +11,11 @@ module Antelope
|
|
10
11
|
module Ace
|
11
12
|
|
12
13
|
# Defines a grammar from an Ace file. This handles setting up
|
13
|
-
# productions, loading from files,
|
14
|
+
# productions, loading from files, symbols, precedence, and
|
14
15
|
# generation.
|
15
16
|
class Grammar
|
16
17
|
|
17
|
-
include
|
18
|
+
include Symbols
|
18
19
|
include Productions
|
19
20
|
include Precedences
|
20
21
|
include Loading
|
@@ -55,6 +56,14 @@ module Antelope
|
|
55
56
|
@output = Pathname.new(output)
|
56
57
|
@compiler = compiler
|
57
58
|
end
|
59
|
+
|
60
|
+
# Extra options from the compiler. This can be used by
|
61
|
+
# generators for output information.
|
62
|
+
#
|
63
|
+
# @return [Hash]
|
64
|
+
def options
|
65
|
+
compiler.options[:extra]
|
66
|
+
end
|
58
67
|
end
|
59
68
|
end
|
60
69
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Antelope
|
2
|
+
module Ace
|
3
|
+
class Scanner
|
4
|
+
|
5
|
+
# Represents an argument to a directive. It encapsulates a
|
6
|
+
# string object, which is the value of the argument.
|
7
|
+
class Argument < String
|
8
|
+
|
9
|
+
# Initialize the argument.
|
10
|
+
#
|
11
|
+
# @param type [Symbol] the type of argument it is; it can be
|
12
|
+
# a `:block`, `:text`, or `:caret`. The type is defined by
|
13
|
+
# the encapsulating characters. If the encapsulating
|
14
|
+
# characters are `{` and `}`, it's a `:block`; if they are
|
15
|
+
# `<` and `>`, it's a `:caret`; otherwise, it's a `:text`.
|
16
|
+
# @param value [String] the value of the argument.
|
17
|
+
def initialize(type, value)
|
18
|
+
@type = type
|
19
|
+
super(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
# If this argument is type `:block`.
|
23
|
+
#
|
24
|
+
# @return [Boolean]
|
25
|
+
# @see type?
|
26
|
+
def block?
|
27
|
+
type? :block
|
28
|
+
end
|
29
|
+
|
30
|
+
# If this argument is type `:text`.
|
31
|
+
#
|
32
|
+
# @return [Boolean]
|
33
|
+
# @see type?
|
34
|
+
def text?
|
35
|
+
type? :text
|
36
|
+
end
|
37
|
+
|
38
|
+
# If this argument is type `:caret`.
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
# @see type?
|
42
|
+
def caret?
|
43
|
+
type? :caret
|
44
|
+
end
|
45
|
+
|
46
|
+
# Checks to see if any of the given arguments match the type
|
47
|
+
# of this argument.
|
48
|
+
#
|
49
|
+
# @param inc [Array<Symbol>]
|
50
|
+
# @return [Boolean]
|
51
|
+
def type?(*inc)
|
52
|
+
inc.include?(@type)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -45,17 +45,43 @@ module Antelope
|
|
45
45
|
#
|
46
46
|
# @return [Boolean] if it matched.
|
47
47
|
def scan_first_directive
|
48
|
-
if @scanner.scan(/%(
|
48
|
+
if @scanner.scan(/%(#{IDENTIFIER}) ?/)
|
49
49
|
directive = @scanner[1]
|
50
|
-
arguments =
|
51
|
-
|
50
|
+
arguments = scan_first_directive_arguments
|
51
|
+
|
52
|
+
tokens << [:directive, directive, arguments]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Scan the arguments for a directive. It keeps attempting to
|
57
|
+
# scan arguments until the first newline that was not in a
|
58
|
+
# block. Arguments can be blocks, carets, or text; blocks are
|
59
|
+
# encapsulated with `{` and `}`, carets are encapsulated with
|
60
|
+
# `<` and `>`, and text is encapsulated with quotes or
|
61
|
+
# nothing.
|
62
|
+
#
|
63
|
+
# @return [Array<Argument>]
|
64
|
+
def scan_first_directive_arguments
|
65
|
+
arguments = []
|
66
|
+
until @scanner.check(/\n/)
|
67
|
+
if @scanner.scan(/\{/)
|
68
|
+
argument =
|
69
|
+
Argument.new(:block, _scan_block[1..-2])
|
70
|
+
elsif @scanner.scan(/</)
|
71
|
+
@scanner.scan(/((?:\\>|[^>])*)\>/)
|
72
|
+
argument =
|
73
|
+
Argument.new(:caret, @scanner[1])
|
74
|
+
else
|
52
75
|
@scanner.scan(/#{VALUE}/x) or error!
|
53
|
-
|
54
|
-
|
76
|
+
argument = Argument.new(:text,
|
77
|
+
@scanner[2] || @scanner[3])
|
55
78
|
end
|
56
79
|
|
57
|
-
|
80
|
+
arguments.push(argument)
|
81
|
+
@scanner.scan(/ */)
|
58
82
|
end
|
83
|
+
|
84
|
+
arguments
|
59
85
|
end
|
60
86
|
end
|
61
87
|
end
|
@@ -45,7 +45,7 @@ module Antelope
|
|
45
45
|
# @see #scan_second_rule_body
|
46
46
|
# @see #error!
|
47
47
|
def scan_second_rule
|
48
|
-
if @scanner.check(/([
|
48
|
+
if @scanner.check(/(#{IDENTIFIER})(\[#{IDENTIFIER}\])?:/)
|
49
49
|
scan_second_rule_label or error!
|
50
50
|
scan_second_rule_body
|
51
51
|
true
|
@@ -57,8 +57,8 @@ module Antelope
|
|
57
57
|
#
|
58
58
|
# @return [Boolean] if it matched.
|
59
59
|
def scan_second_rule_label
|
60
|
-
if @scanner.scan(/([
|
61
|
-
tokens << [:label, @scanner[1]]
|
60
|
+
if @scanner.scan(/(#{IDENTIFIER})(?:\[(#{IDENTIFIER})\])?: ?/)
|
61
|
+
tokens << [:label, @scanner[1], @scanner[2]]
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
@@ -88,8 +88,8 @@ module Antelope
|
|
88
88
|
#
|
89
89
|
# @return [Boolean] if it matched.
|
90
90
|
def scan_second_rule_part
|
91
|
-
if @scanner.scan(/([
|
92
|
-
tokens << [:part, @scanner[1]]
|
91
|
+
if @scanner.scan(/(#{IDENTIFIER})(?:\[(#{IDENTIFIER})\])?(?!\:|[A-Za-z._])/)
|
92
|
+
tokens << [:part, @scanner[1], @scanner[2]]
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
@@ -107,7 +107,7 @@ module Antelope
|
|
107
107
|
#
|
108
108
|
# @return [Boolean] if it matched.
|
109
109
|
def scan_second_rule_prec
|
110
|
-
if @scanner.scan(/%prec (
|
110
|
+
if @scanner.scan(/%prec (#{IDENTIFIER})/)
|
111
111
|
tokens << [:prec, @scanner[1]]
|
112
112
|
end
|
113
113
|
end
|
@@ -139,17 +139,32 @@ module Antelope
|
|
139
139
|
def _scan_block
|
140
140
|
brack = 1
|
141
141
|
body = "{"
|
142
|
+
scan_for = %r{
|
143
|
+
(
|
144
|
+
(?: " ( \\\\ | \\" | [^"] )* "? )
|
145
|
+
| (?: ' ( \\\\ | \\' | [^'] )* '? )
|
146
|
+
| (?: // .*? \n )
|
147
|
+
| (?: \# .*? \n )
|
148
|
+
| (?: /\* [\s\S]+? \*/ )
|
149
|
+
| (?: \} )
|
150
|
+
| (?: \{ )
|
151
|
+
)
|
152
|
+
}x
|
142
153
|
|
143
154
|
until brack.zero?
|
144
|
-
if part = @scanner.scan_until(
|
155
|
+
if part = @scanner.scan_until(scan_for)
|
145
156
|
body << part
|
146
157
|
|
158
|
+
|
147
159
|
if @scanner[1] == "}"
|
148
160
|
brack -= 1
|
149
|
-
|
161
|
+
elsif @scanner[1] == "{"
|
150
162
|
brack += 1
|
151
163
|
end
|
152
164
|
else
|
165
|
+
if @scanner.scan(/(.+)/m)
|
166
|
+
@line += @scanner[1].count("\n")
|
167
|
+
end
|
153
168
|
error!
|
154
169
|
end
|
155
170
|
end
|
data/lib/antelope/ace/scanner.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require "strscan"
|
4
|
+
require "antelope/ace/scanner/argument"
|
4
5
|
require "antelope/ace/scanner/first"
|
5
6
|
require "antelope/ace/scanner/second"
|
6
7
|
require "antelope/ace/scanner/third"
|
@@ -8,16 +9,19 @@ require "antelope/ace/scanner/third"
|
|
8
9
|
module Antelope
|
9
10
|
module Ace
|
10
11
|
|
11
|
-
# Scans a given input. The input should be a properly formatted
|
12
|
-
# see the Ace module for more information. This scanner
|
13
|
-
# StringScanner class internally; see the ruby
|
14
|
-
# that. This scanner seperates scanning
|
15
|
-
# First, Second, and Third, for each
|
12
|
+
# Scans a given input. The input should be a properly formatted
|
13
|
+
# ACE file; see the Ace module for more information. This scanner
|
14
|
+
# uses the StringScanner class internally; see the ruby
|
15
|
+
# documentation for more on that. This scanner seperates scanning
|
16
|
+
# into three seperate stages: First, Second, and Third, for each
|
17
|
+
# section of the file, respectively.
|
16
18
|
#
|
17
19
|
# @see Ace
|
18
20
|
# @see http://ruby-doc.org/stdlib-2.1.2/libdoc/strscan/rdoc/StringScanner.html
|
19
21
|
class Scanner
|
20
22
|
|
23
|
+
IDENTIFIER = "[a-zA-Z_.][a-zA-Z0-9_.-]*"
|
24
|
+
|
21
25
|
include First
|
22
26
|
include Second
|
23
27
|
include Third
|
@@ -32,30 +36,30 @@ module Antelope
|
|
32
36
|
# @return [Array<Array<(Symbol, Object, ...)>>]
|
33
37
|
attr_reader :tokens
|
34
38
|
|
35
|
-
# The boundry between each section. Placed here to be easily
|
36
|
-
# **MUST** be a regular expression.
|
39
|
+
# The boundry between each section. Placed here to be easily.
|
40
|
+
# modifiable. **MUST** be a regular expression.
|
37
41
|
#
|
38
42
|
# @return [RegExp]
|
39
43
|
CONTENT_BOUNDRY = /%%/
|
40
44
|
|
41
|
-
# The value regular expression. It should match values; for
|
42
|
-
# things quoted in strings or word letters without
|
43
|
-
# to #to_s, since it is embedded within
|
44
|
-
# regular expression should
|
45
|
-
# groups 2 or 3.
|
45
|
+
# The value regular expression. It should match values; for
|
46
|
+
# example, things quoted in strings or word letters without
|
47
|
+
# quotes. Must respond to #to_s, since it is embedded within
|
48
|
+
# other regular expressions. The regular expression should
|
49
|
+
# place the contents of the value in the groups 2 or 3.
|
46
50
|
#
|
47
51
|
# @return [#to_s]
|
48
52
|
VALUE = %q{(?:
|
49
53
|
(?:("|')((?:\\\\|\\"|\\'|.)+?)\\1)
|
50
|
-
| ([
|
54
|
+
| ([A-Za-z0-9_.<>*-]+)
|
51
55
|
)}
|
52
56
|
|
53
57
|
# Scans a file. It returns the tokens resulting from scanning.
|
54
58
|
#
|
55
|
-
# @param source [String] the source to scan. This should be
|
56
|
-
# with StringScanner.
|
57
|
-
# @param name [String] the name of the source file. This is
|
58
|
-
#
|
59
|
+
# @param source [String] the source to scan. This should be
|
60
|
+
# compatible with StringScanner.
|
61
|
+
# @param name [String] the name of the source file. This is
|
62
|
+
# primarilyused in backtrace information.
|
59
63
|
# @return [Array<Array<(Symbol, Object, ...)>>]
|
60
64
|
# @see #tokens
|
61
65
|
def self.scan(source, name = "(ace file)")
|
@@ -92,8 +96,15 @@ module Antelope
|
|
92
96
|
start = [@scanner.pos - 8, 0].max
|
93
97
|
stop = [@scanner.pos + 8, @scanner.string.length].min
|
94
98
|
snip = @scanner.string[start..stop].strip.inspect
|
95
|
-
char = @scanner.string[@scanner.pos]
|
96
|
-
|
99
|
+
char = @scanner.string[@scanner.pos]
|
100
|
+
char = if char
|
101
|
+
char.inspect
|
102
|
+
else
|
103
|
+
"EOF"
|
104
|
+
end
|
105
|
+
|
106
|
+
new_line = "#{@source}:#{@line}: unexpected #{char} " \
|
107
|
+
"(near #{snip})"
|
97
108
|
|
98
109
|
raise e, e.message, [new_line, *e.backtrace]
|
99
110
|
end
|
@@ -111,17 +122,12 @@ module Antelope
|
|
111
122
|
|
112
123
|
private
|
113
124
|
|
114
|
-
# Raises an error
|
115
|
-
# some context.
|
125
|
+
# Raises an error.
|
116
126
|
#
|
117
127
|
# @raise [SyntaxError] always.
|
118
128
|
# @return [void]
|
119
129
|
def error!
|
120
|
-
|
121
|
-
stop = [@scanner.pos + 8, @scanner.string.length].min
|
122
|
-
snip = @scanner.string[start..stop].strip
|
123
|
-
char = @scanner.string[@scanner.pos]
|
124
|
-
raise SyntaxError, "invalid syntax"# near `#{snip.inspect}' (#{char.inspect})"
|
130
|
+
raise SyntaxError, "invalid syntax"
|
125
131
|
end
|
126
132
|
end
|
127
133
|
end
|