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.
@@ -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
@@ -2,7 +2,7 @@ module Voodoo
2
2
  # Methods to get and set configuration parameters
3
3
  module Config
4
4
  IMPLEMENTATION_NAME = 'Voodoo Compiler'
5
- IMPLEMENTATION_VERSION = '1.0.0'
5
+ IMPLEMENTATION_VERSION = '1.0.1'
6
6
 
7
7
  # Class that holds configuration parameters
8
8
  class Configuration
data/lib/voodoo/parser.rb CHANGED
@@ -33,16 +33,83 @@ module Voodoo
33
33
  @text = ''
34
34
  end
35
35
 
36
- class ParseError < StandardError
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
- @message = message
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 :message, :input_name, :start_line, :start_column, :text
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
- # Skip whitespace, comments, and empty lines
83
- skip_to_next_top_level
149
+ wrap_exceptions do
150
+ @text = ''
151
+ # Skip whitespace, comments, and empty lines
152
+ skip_to_next_top_level
84
153
 
85
- validate_top_level do
86
- parse_top_level_nonvalidating
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
- body = []
93
- error = nil
94
- case kind
95
- when :function
96
- kind_text = 'function definition'
97
- else
98
- kind_text = kind.to_s
99
- end
100
- done = false
101
- until done
102
- begin
103
- with_position do
104
- statement = parse_top_level_nonvalidating
105
- if statement == nil
106
- done = true
107
- parse_error "End of input while inside #{kind_text}", nil
108
- elsif statement[0] == :end
109
- # Done parsing body
110
- done = true
111
- elsif kind == :conditional && statement[0] == :else
112
- # Done parsing body, but there is another one coming up
113
- body << statement
114
- done = true
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
- rescue Validator::ValidationError => e
124
- parse_error e.message
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
- end
128
- rescue => e
129
- # Got some kind of error. Still try to parse the rest of the body.
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
- if error != nil
138
- raise error
139
- end
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
- body
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
- result = nil
150
- consume
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
- when "n"
158
- # \n is newline
159
- consume
160
- result = "\n"
161
- when "r"
162
- # \r is carriage return
163
- consume
164
- result = "\r"
165
- when "x"
166
- # \xXX is byte with hex value XX
167
- code = @input.read 2
168
- @column = @column + 2
169
- consume
170
- @text << code
171
- result = [code].pack('H2')
172
- when "\n"
173
- # \<newline> is line continuation character
174
- consume
175
- # Skip indentation of next line
176
- while lookahead =~ /\s/
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
- text = lookahead
193
- consume
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
- consume
206
- result = ''
207
- while true
208
- case lookahead
209
- when "\""
210
- consume
211
- break
212
- when "\\"
213
- result << parse_escape
214
- else
215
- result << lookahead
216
- consume
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
- name = ''
227
- while true
228
- case lookahead
229
- when "\\"
230
- name << parse_escape
231
- when /\w|-/
232
- name << lookahead
233
- consume
234
- when ':'
235
- # Colon parsed as last character of the symbol name
236
- name << lookahead
237
- consume
238
- break
239
- else
240
- break
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
- if old == 10
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 +nil+ when the end of the input has been reached.
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
- @column = @column.succ
282
- end
283
- case @lookahead
284
- when :eof
285
- :eof
286
- else
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 != 1 && lookahead != :eof
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
- yield
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: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 0
10
- version: 1.0.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-14 00:00:00 -08:00
18
+ date: 2012-01-29 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies: []
21
21