voodoo 1.0.0 → 1.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.
- data/lib/voodoo/compiler.rb +7 -3
- data/lib/voodoo/config.rb +1 -1
- data/lib/voodoo/parser.rb +229 -132
- metadata +4 -4
data/lib/voodoo/compiler.rb
CHANGED
@@ -54,13 +54,17 @@ module Voodoo
|
|
54
54
|
@generator.add section, statement
|
55
55
|
end
|
56
56
|
|
57
|
+
rescue Parser::MultipleErrors => e
|
58
|
+
errors.concat e.errors
|
59
|
+
|
57
60
|
rescue Parser::ParseError => e
|
58
61
|
errors << e
|
59
|
-
if errors.length >= 100
|
60
|
-
raise Error.new(errors)
|
61
|
-
end
|
62
62
|
end
|
63
63
|
|
64
|
+
if errors.length >= 100
|
65
|
+
# Too many errors, give up.
|
66
|
+
raise Error.new(errors)
|
67
|
+
end
|
64
68
|
end
|
65
69
|
|
66
70
|
if errors.empty?
|
data/lib/voodoo/config.rb
CHANGED
data/lib/voodoo/parser.rb
CHANGED
@@ -33,16 +33,83 @@ module Voodoo
|
|
33
33
|
@text = ''
|
34
34
|
end
|
35
35
|
|
36
|
-
class
|
36
|
+
# Base class for errors reported from the parser.
|
37
|
+
# This provides methods to get the name of the input being processed,
|
38
|
+
# as well as the start_line, start_column, and text of the code
|
39
|
+
# that triggered the error.
|
40
|
+
class Error < StandardError
|
37
41
|
def initialize message, input_name, start_line, start_column, text
|
38
|
-
|
42
|
+
super message
|
39
43
|
@input_name = input_name
|
40
44
|
@start_line = start_line
|
41
45
|
@start_column = start_column
|
42
46
|
@text = text
|
43
47
|
end
|
44
48
|
|
45
|
-
attr_reader :
|
49
|
+
attr_reader :input_name, :start_line, :start_column, :text
|
50
|
+
end
|
51
|
+
|
52
|
+
# Class for parse errors.
|
53
|
+
# A ParseError indicates an error in the code being parsed.
|
54
|
+
# For other errors that the parser may raise, see ParserInternalError.
|
55
|
+
class ParseError < Parser::Error
|
56
|
+
def initialize message, input_name, start_line, start_column, text
|
57
|
+
super message, input_name, start_line, start_column, text
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Class for parser internal errors.
|
62
|
+
# A ParserInternalError indicates an error in the parser that is not
|
63
|
+
# flagged as an error in the code being parsed. Possible causes
|
64
|
+
# include I/O errors while reading code, as well as bugs in the
|
65
|
+
# parser.
|
66
|
+
#
|
67
|
+
# The +cause+ attribute indicates the initial cause for the error.
|
68
|
+
# The other attributes of ParserInternalError are inherited from
|
69
|
+
# Parser::Error and indicate the input that was being
|
70
|
+
# processed when the error occurred.
|
71
|
+
class ParserInternalError < Parser::Error
|
72
|
+
def initialize cause, input_name, start_line, start_column, text
|
73
|
+
super cause.message, input_name, start_line, start_column, text
|
74
|
+
@cause = cause
|
75
|
+
end
|
76
|
+
|
77
|
+
attr_reader :cause
|
78
|
+
end
|
79
|
+
|
80
|
+
# Class wrapping multiple Parser::Errors.
|
81
|
+
class MultipleErrors < Parser::Error
|
82
|
+
def initialize errors
|
83
|
+
@errors = errors
|
84
|
+
super(nil, errors[0].input_name, errors[0].start_line,
|
85
|
+
errors[0].start_column, nil)
|
86
|
+
end
|
87
|
+
|
88
|
+
attr_reader :errors
|
89
|
+
|
90
|
+
def message
|
91
|
+
if @message == nil
|
92
|
+
msg = "Multiple errors:\n\n"
|
93
|
+
@errors.each do |error|
|
94
|
+
msg << error.input_name << ":" if error.input_name
|
95
|
+
msg << "#{error.start_line}: " << error.message
|
96
|
+
if error.text != nil
|
97
|
+
msg << "\n\n #{error.text.gsub("\n", "\n ")}"
|
98
|
+
end
|
99
|
+
msg << "\n"
|
100
|
+
end
|
101
|
+
@message = msg
|
102
|
+
end
|
103
|
+
@message
|
104
|
+
end
|
105
|
+
|
106
|
+
def text
|
107
|
+
if @text == nil
|
108
|
+
texts = @errors.map {|error| error.text}
|
109
|
+
@text = texts.join "\n"
|
110
|
+
end
|
111
|
+
@text
|
112
|
+
end
|
46
113
|
end
|
47
114
|
|
48
115
|
# Parses a top-level element.
|
@@ -79,66 +146,73 @@ module Voodoo
|
|
79
146
|
# # [:function, [:x, :y], [:let, :z, :add, :x, :y], [:return, :z]]
|
80
147
|
#
|
81
148
|
def parse_top_level
|
82
|
-
|
83
|
-
|
149
|
+
wrap_exceptions do
|
150
|
+
@text = ''
|
151
|
+
# Skip whitespace, comments, and empty lines
|
152
|
+
skip_to_next_top_level
|
84
153
|
|
85
|
-
|
86
|
-
|
154
|
+
validate_top_level do
|
155
|
+
parse_top_level_nonvalidating
|
156
|
+
end
|
87
157
|
end
|
88
158
|
end
|
89
159
|
|
90
160
|
# Parses a body for a function or a conditional
|
91
161
|
def parse_body kind
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
else
|
116
|
-
# Should be a normal statement. Validate it, then add it to body
|
117
|
-
if statement[0] == :function
|
118
|
-
parse_error "Function definitions are only allowed at top-level"
|
119
|
-
end
|
120
|
-
begin
|
121
|
-
Validator.validate_statement statement
|
162
|
+
wrap_exceptions do
|
163
|
+
body = []
|
164
|
+
errors = []
|
165
|
+
case kind
|
166
|
+
when :function
|
167
|
+
kind_text = 'function definition'
|
168
|
+
else
|
169
|
+
kind_text = kind.to_s
|
170
|
+
end
|
171
|
+
done = false
|
172
|
+
until done
|
173
|
+
begin
|
174
|
+
with_position do
|
175
|
+
statement = parse_top_level_nonvalidating
|
176
|
+
if statement == nil
|
177
|
+
done = true
|
178
|
+
parse_error "End of input while inside #{kind_text}", nil
|
179
|
+
|
180
|
+
elsif statement[0] == :end
|
181
|
+
# Done parsing body
|
182
|
+
done = true
|
183
|
+
elsif kind == :conditional && statement[0] == :else
|
184
|
+
# Done parsing body, but there is another one coming up
|
122
185
|
body << statement
|
123
|
-
|
124
|
-
|
186
|
+
done = true
|
187
|
+
else
|
188
|
+
# Should be a normal statement. Validate it, then add it to body
|
189
|
+
if statement[0] == :function
|
190
|
+
parse_error "Function definitions are only allowed at top-level"
|
191
|
+
end
|
192
|
+
begin
|
193
|
+
Validator.validate_statement statement
|
194
|
+
body << statement
|
195
|
+
rescue Validator::ValidationError => e
|
196
|
+
parse_error e.message
|
197
|
+
end
|
125
198
|
end
|
126
199
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
# Save the error if it was the first one.
|
131
|
-
if error == nil
|
132
|
-
error = e
|
200
|
+
rescue => e
|
201
|
+
# Got some kind of error. Still try to parse the rest of the body.
|
202
|
+
errors << e
|
133
203
|
end
|
134
204
|
end
|
135
|
-
end
|
136
205
|
|
137
|
-
|
138
|
-
raise
|
139
|
-
|
206
|
+
# Raise error if we had just one.
|
207
|
+
# If we had more than one, raise a MultipleErrors instance.
|
208
|
+
if errors.length == 1
|
209
|
+
raise errors[0]
|
210
|
+
elsif errors.length > 1
|
211
|
+
raise MultipleErrors.new errors
|
212
|
+
end
|
140
213
|
|
141
|
-
|
214
|
+
body
|
215
|
+
end
|
142
216
|
end
|
143
217
|
|
144
218
|
# Parses an escape sequence.
|
@@ -146,101 +220,109 @@ module Voodoo
|
|
146
220
|
# character (backslash). It decodes the escape sequence and returns
|
147
221
|
# the result as a string.
|
148
222
|
def parse_escape
|
149
|
-
|
150
|
-
|
151
|
-
case lookahead
|
152
|
-
when :eof
|
153
|
-
parse_error "Unexpected end of input in escape sequence", nil
|
154
|
-
when "\\", "\"", " "
|
155
|
-
result = lookahead
|
223
|
+
wrap_exceptions do
|
224
|
+
result = nil
|
156
225
|
consume
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
226
|
+
case lookahead
|
227
|
+
when :eof
|
228
|
+
parse_error "Unexpected end of input in escape sequence", nil
|
229
|
+
when "\\", "\"", " "
|
230
|
+
result = lookahead
|
231
|
+
consume
|
232
|
+
when "n"
|
233
|
+
# \n is newline
|
234
|
+
consume
|
235
|
+
result = "\n"
|
236
|
+
when "r"
|
237
|
+
# \r is carriage return
|
238
|
+
consume
|
239
|
+
result = "\r"
|
240
|
+
when "x"
|
241
|
+
# \xXX is byte with hex value XX
|
242
|
+
code = @input.read 2
|
243
|
+
@column = @column + 2
|
244
|
+
consume
|
245
|
+
@text << code
|
246
|
+
result = [code].pack('H2')
|
247
|
+
when "\n"
|
248
|
+
# \<newline> is line continuation character
|
249
|
+
consume
|
250
|
+
# Skip indentation of next line
|
251
|
+
while lookahead =~ /\s/
|
252
|
+
consume
|
253
|
+
end
|
254
|
+
result = ''
|
255
|
+
else
|
256
|
+
# Default to just passing on next character
|
257
|
+
result = lookahead
|
177
258
|
consume
|
178
259
|
end
|
179
|
-
result
|
180
|
-
else
|
181
|
-
# Default to just passing on next character
|
182
|
-
result = lookahead
|
183
|
-
consume
|
260
|
+
result
|
184
261
|
end
|
185
|
-
result
|
186
262
|
end
|
187
263
|
|
188
264
|
# Parses a number.
|
189
265
|
# This method should be called while the lookahead is the first
|
190
266
|
# character of the number.
|
191
267
|
def parse_number
|
192
|
-
|
193
|
-
|
194
|
-
while lookahead =~ /\d/
|
195
|
-
text << lookahead
|
268
|
+
wrap_exceptions do
|
269
|
+
text = lookahead
|
196
270
|
consume
|
271
|
+
while lookahead =~ /\d/
|
272
|
+
text << lookahead
|
273
|
+
consume
|
274
|
+
end
|
275
|
+
text.to_i
|
197
276
|
end
|
198
|
-
text.to_i
|
199
277
|
end
|
200
278
|
|
201
279
|
# Parses a string.
|
202
280
|
# This method should be called while the lookahead is the opening
|
203
281
|
# double quote.
|
204
282
|
def parse_string
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
283
|
+
wrap_exceptions do
|
284
|
+
consume
|
285
|
+
result = ''
|
286
|
+
while true
|
287
|
+
case lookahead
|
288
|
+
when "\""
|
289
|
+
consume
|
290
|
+
break
|
291
|
+
when "\\"
|
292
|
+
result << parse_escape
|
293
|
+
else
|
294
|
+
result << lookahead
|
295
|
+
consume
|
296
|
+
end
|
217
297
|
end
|
298
|
+
result
|
218
299
|
end
|
219
|
-
result
|
220
300
|
end
|
221
301
|
|
222
302
|
# Parses a symbol.
|
223
303
|
# This method should be called while the lookahead is the first
|
224
304
|
# character of the symbol name.
|
225
305
|
def parse_symbol
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
306
|
+
wrap_exceptions do
|
307
|
+
name = ''
|
308
|
+
while true
|
309
|
+
case lookahead
|
310
|
+
when "\\"
|
311
|
+
name << parse_escape
|
312
|
+
when /\w|-/
|
313
|
+
name << lookahead
|
314
|
+
consume
|
315
|
+
when ':'
|
316
|
+
# Colon parsed as last character of the symbol name
|
317
|
+
name << lookahead
|
318
|
+
consume
|
319
|
+
break
|
320
|
+
else
|
321
|
+
break
|
322
|
+
end
|
241
323
|
end
|
324
|
+
name.to_sym
|
242
325
|
end
|
243
|
-
name.to_sym
|
244
326
|
end
|
245
327
|
|
246
328
|
#
|
@@ -252,13 +334,11 @@ module Voodoo
|
|
252
334
|
# The character is appended to @text.
|
253
335
|
def consume
|
254
336
|
old = @lookahead
|
255
|
-
|
337
|
+
@lookahead = nil
|
338
|
+
if old == "\n"
|
256
339
|
@line = @line.succ
|
257
340
|
@column = 0
|
258
341
|
end
|
259
|
-
@lookahead = @input.getc
|
260
|
-
@lookahead = :eof if @lookahead == nil
|
261
|
-
@column = @column.succ unless @lookahead == :eof
|
262
342
|
@text << old
|
263
343
|
old
|
264
344
|
end
|
@@ -274,18 +354,18 @@ module Voodoo
|
|
274
354
|
end
|
275
355
|
|
276
356
|
# Returns the current lookahead character,
|
277
|
-
# or +
|
357
|
+
# or +:eof+ when the end of the input has been reached.
|
278
358
|
def lookahead
|
279
359
|
if @lookahead == nil
|
280
360
|
@lookahead = @input.getc
|
281
|
-
@
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
@lookahead.chr
|
361
|
+
if @lookahead == nil
|
362
|
+
@lookahead = :eof
|
363
|
+
else
|
364
|
+
@lookahead = @lookahead.chr
|
365
|
+
@column = @column.succ
|
366
|
+
end
|
288
367
|
end
|
368
|
+
@lookahead
|
289
369
|
end
|
290
370
|
|
291
371
|
# Parses a conditional statement
|
@@ -316,7 +396,7 @@ module Voodoo
|
|
316
396
|
error.set_backtrace caller
|
317
397
|
|
318
398
|
# If we are not at a new line, skip until the next line
|
319
|
-
while @column
|
399
|
+
while @column > 1 && lookahead != :eof
|
320
400
|
consume
|
321
401
|
end
|
322
402
|
|
@@ -348,7 +428,6 @@ module Voodoo
|
|
348
428
|
when "#"
|
349
429
|
# Skip comment
|
350
430
|
while lookahead != :eof && lookahead != "\n"
|
351
|
-
word << lookahead
|
352
431
|
consume
|
353
432
|
end
|
354
433
|
else
|
@@ -364,6 +443,7 @@ module Voodoo
|
|
364
443
|
# Add word to statement
|
365
444
|
words << word
|
366
445
|
end
|
446
|
+
words
|
367
447
|
end
|
368
448
|
|
369
449
|
# We have a line of input. Conditionals and function declarations
|
@@ -505,7 +585,9 @@ module Voodoo
|
|
505
585
|
@start_line = @line
|
506
586
|
@start_column = @column
|
507
587
|
@text = ''
|
508
|
-
|
588
|
+
wrap_exceptions do
|
589
|
+
yield
|
590
|
+
end
|
509
591
|
ensure
|
510
592
|
# Restore old values
|
511
593
|
@start_line = old_line
|
@@ -514,5 +596,20 @@ module Voodoo
|
|
514
596
|
end
|
515
597
|
end
|
516
598
|
|
599
|
+
# Ensures that any exceptions that escape from block are instances of
|
600
|
+
# Parser::Error.
|
601
|
+
def wrap_exceptions &block
|
602
|
+
begin
|
603
|
+
yield
|
604
|
+
rescue Parser::Error
|
605
|
+
# Already an instance of Parser::Error; pass it through.
|
606
|
+
raise
|
607
|
+
rescue => e
|
608
|
+
# Some other error; wrap in ParserInternalError.
|
609
|
+
raise ParserInternalError.new(e, @input_name, @line,
|
610
|
+
@column, @text)
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
517
614
|
end
|
518
615
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: voodoo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 1
|
10
|
+
version: 1.0.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Robbert Haarman
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-01-
|
18
|
+
date: 2012-01-29 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|