voodoo 0.7.0 → 1.0.0

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.
@@ -0,0 +1,391 @@
1
+ module Voodoo
2
+ # Functionality for validating Voodoo code.
3
+ module Validator
4
+
5
+ # Expressions that take two arguments
6
+ BINOPS = [:add, :and, :asr, :bsr, :div, :'get-byte', :'get-word',
7
+ :mod, :mul, :or, :rol, :ror, :shl, :shr, :sub, :xor]
8
+ # Expressions that take zero or more parameters
9
+ VARARG_EXPRS = [:call, :'tail-call']
10
+ # Symbols that may occur as the first word of an expression
11
+ EXPRS = BINOPS + VARARG_EXPRS + [:not]
12
+
13
+ # Symbols that are a valid start of a statement
14
+ STATEMENTS = [:block, :call, :goto,
15
+ :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne,
16
+ :label, :let, :return, :set,
17
+ :'set-byte', :'set-word', :'tail-call']
18
+
19
+ # Symbols that are valid at top-level
20
+ TOP_LEVELS = [:align, :byte, :export, :function, :import,
21
+ :section, :string, :word] + STATEMENTS
22
+
23
+ NTH = ['First', 'Second', 'Third']
24
+
25
+ module_function
26
+
27
+ # Validates an expression.
28
+ # Returns true if the expression is valid.
29
+ # Raises ValidationError if the expression is not valid.
30
+ def validate_expression code
31
+ if int_or_symbol_or_at? code
32
+ true
33
+ elsif code.respond_to? :[]
34
+ if BINOPS.member? code[0]
35
+ # binop should have 2 parameters, both of them atomic values
36
+ assert_n_params code, 2
37
+ assert_params_are_values code
38
+ else
39
+ # code[0] is not a binop
40
+ case code[0]
41
+ when :call, :'tail-call'
42
+ # call should have at least 1 parameter
43
+ # and all parameters should be atomic values
44
+ assert_at_least_n_params code, 1
45
+ assert_params_are_values code
46
+ when :'get-byte', :'get-word'
47
+ # Should have exactly 2 parameters, both of which should be values.
48
+ assert_n_params code, 2
49
+ assert_params_are_values code
50
+ when :not
51
+ # should have a single, atomic parameter
52
+ assert_n_params code, 1
53
+ assert_params_are_values code
54
+ else
55
+ raise ValidationError.new("#{code[0].inspect}" +
56
+ " cannot start an expression",
57
+ code)
58
+ end
59
+ end
60
+ else
61
+ # code is not an atomic value and does not respond to :[]
62
+ raise ValidationError.new("#{code.inspect} is not a valid expression",
63
+ code)
64
+ end
65
+ end
66
+
67
+ # Validates a statement.
68
+ # Returns true if the statement is valid.
69
+ # Raises ValidationError if the statement is not valid.
70
+ def validate_statement code
71
+ begin
72
+ case code[0]
73
+ when :block
74
+ code[1..-1].each {|stmt| validate_statement stmt}
75
+ true
76
+
77
+ when :call, :'tail-call'
78
+ validate_expression code
79
+
80
+ when :goto
81
+ # should have exactly 1 parameter, which should be atomic
82
+ assert_n_params code, 1
83
+ assert_params_are_values code
84
+
85
+ when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
86
+ # Should have 2 or 3 parameters.
87
+ # First parameter should be an array (or similar) containing
88
+ # two elements, both atomic.
89
+ # Second parameter should consist of one or more statements.
90
+ # Third parameter, if present, should consist of zero or more
91
+ # statements.
92
+ # let is not allowed as a statement in either parameter, though
93
+ # it can be embedded in a block in either.
94
+ if code.length < 3 || code.length > 4
95
+ raise ValidationError.new("#{code[0]} takes 2 or 3 parameters",
96
+ code)
97
+ elsif code[1].length != 2
98
+ raise ValidationError.new("#{code[0]} requires two values to" +
99
+ " compare in its first parameter",
100
+ code)
101
+ elsif !code[1].all? {|x| int_or_symbol_or_at? x}
102
+ raise ValidationError.new("Values to compare should be values" +
103
+ " (symbols, integers, or at-exrpssions)",
104
+ code)
105
+ else
106
+ code[2].each do |stmt|
107
+ validate_statement stmt
108
+ if stmt[0] == :let
109
+ raise ValidationError.new("let is not allowed inside " +
110
+ code[0].to_s, code)
111
+ end
112
+ end
113
+
114
+ if code.length > 3
115
+ code[3].each do |stmt|
116
+ validate_statement stmt
117
+ if stmt[0] == :let
118
+ raise ValidationError.new("let is not allowed inside " +
119
+ code[0].to_s, code)
120
+ end
121
+ end
122
+ end
123
+
124
+ true
125
+ end
126
+
127
+ when :label
128
+ # should have 1 parameter which should be a symbol
129
+ if code.length != 2 || !code[1].kind_of?(::Symbol)
130
+ raise ValidationError.new("label requires a single symbol" +
131
+ "as its parameter", code)
132
+ else
133
+ true
134
+ end
135
+
136
+ when :let, :set
137
+ # should have at least 2 parameters
138
+ if code.length < 3
139
+ raise ValidationError.new("#{code[0]} requires a symbol" +
140
+ " and an expression", code)
141
+ elsif code[1].kind_of? ::Symbol
142
+ # After the symbol, there should be an expression
143
+ expr = code[2..-1]
144
+ if expr.length == 1
145
+ validate_expression expr[0]
146
+ else
147
+ validate_expression expr
148
+ end
149
+ else
150
+ raise ValidationError.new("First parameter to #{code[0]} should be" +
151
+ " a symbol", code)
152
+ end
153
+
154
+ when :return
155
+ # Should either have no parameters, or a single expression as
156
+ # a parameter.
157
+ case code.length
158
+ when 1
159
+ true
160
+ when 2
161
+ validate_expression code[1]
162
+ else
163
+ validate_expression code[1..-1]
164
+ end
165
+
166
+ when :'set-byte', :'set-word'
167
+ # Should have exactly 3 parameters, all of which should be
168
+ # atomic values.
169
+ assert_n_params code, 3
170
+ assert_params_are_values code
171
+
172
+ else
173
+ raise ValidationError.new("Not a valid statement: #{code.inspect}",
174
+ code)
175
+ end
176
+
177
+ rescue ValidationError
178
+ # Pass it on
179
+ raise
180
+
181
+ rescue Exception => e
182
+ if code.respond_to? :[]
183
+ # Pass on the exception
184
+ raise
185
+ else
186
+ raise ValidationError.new("#{code.inspect} does not respond to" +
187
+ ":[]", code)
188
+ end
189
+
190
+ end
191
+ end
192
+
193
+ # Validates a top-level directive.
194
+ # Returns true if the directive is valid.
195
+ # Raises ValidationError if the directive is not valid.
196
+ def validate_top_level code
197
+ begin
198
+ case code[0]
199
+ when :align
200
+ # Should either have no parameter or a single integer parameter
201
+ if code.length == 1 || (code.length == 2 &&
202
+ code[1].kind_of?(::Integer))
203
+ true
204
+ else
205
+ raise ValidationError.new("align requires either a single" +
206
+ " integer parameter, or no parameters",
207
+ code)
208
+ end
209
+
210
+ when :byte, :word
211
+ # Should have a single integer or symbol parameter
212
+ if code.length != 2 || !int_or_symbol?(code[1])
213
+ raise ValidationError.new("#{code[0]} requires a single" +
214
+ " parameter that is either an " +
215
+ " integer or a symbol", code)
216
+ else
217
+ true
218
+ end
219
+
220
+ when :export, :import
221
+ # Should have at least 1 parameter, and all parameters should
222
+ # be symbols.
223
+ if code.length < 2
224
+ raise ValidationError.new("#{code[0]} requires at least " +
225
+ " one parameter", code)
226
+ elsif code[1..-1].all? {|x| x.kind_of? ::Symbol}
227
+ true
228
+ else
229
+ raise ValidationError.new("All parameters to #{code[0]}" +
230
+ " should be symbols", code)
231
+ end
232
+
233
+ when :function
234
+
235
+ # Check that formal parameters have been specified
236
+ if code.length < 2
237
+ raise ValidationError.new("Formal parameters should be" +
238
+ " specified for function",
239
+ code)
240
+ end
241
+
242
+ # Check that all formal parameters are symbols
243
+ code[1].each do |formal|
244
+ unless formal.kind_of? ::Symbol
245
+ raise ValidationError.new("Formal parameter #{formal.inspect}" +
246
+ " should be a symbol",
247
+ formal)
248
+ end
249
+ end
250
+
251
+ # Verify statements
252
+ code[2..-1].each do |stmt|
253
+ if stmt.respond_to?(:[]) && stmt[0] == :function
254
+ raise ValidationError.new("Function definitions are only " +
255
+ "valid at top-level", stmt)
256
+ else
257
+ validate_statement stmt
258
+ end
259
+ end
260
+
261
+ when :section
262
+
263
+ # Check that we have a string or a symbol
264
+ case code.length
265
+ when 1
266
+ raise ValidationError.new("Section name should be specified",
267
+ code)
268
+ when 2
269
+ unless code[1].kind_of?(::String) || code[1].kind_of?(::Symbol)
270
+ raise ValidationError.new("Section name should be a string" +
271
+ " or a symbol",
272
+ code)
273
+ end
274
+
275
+ else
276
+ raise ValidationError.new("section directive should have only" +
277
+ " a single parameter",
278
+ code);
279
+ end
280
+
281
+ when :string
282
+ # Should have a single string parameter
283
+ if code.length != 2 || !code[1].kind_of?(::String)
284
+ raise ValidationError.new("string requires a single string" +
285
+ " as a parameter", code)
286
+ else
287
+ true
288
+ end
289
+
290
+ else
291
+ if STATEMENTS.member? code[0]
292
+ validate_statement code
293
+ else
294
+ raise ValidationError.new("Directive #{code[0]}" +
295
+ " not valid at top-level",
296
+ code)
297
+ end
298
+ end
299
+
300
+ rescue ValidationError
301
+ # Pass it on
302
+ raise
303
+
304
+ rescue Exception => e
305
+ if code.respond_to? :[]
306
+ # Pass on the exception
307
+ raise
308
+ else
309
+ raise ValidationError.new("#{code.inspect} does not respond to" +
310
+ ":[]", code)
311
+ end
312
+ end
313
+
314
+ # If we got here, all is well
315
+ true
316
+ end
317
+
318
+ # Base class for errors signaled by the Validator.
319
+ class ValidationError < StandardError
320
+ def initialize message, code = nil
321
+ super message
322
+ @code = code
323
+ end
324
+ attr_reader :code
325
+ end
326
+
327
+ # Tests that an expression has at least n parameters. Raises a
328
+ # ValidationError if this is not the case.
329
+ def assert_at_least_n_params expr, n
330
+ if expr.length <= n
331
+ if n == 1
332
+ raise ValidationError.new \
333
+ "#{expr[0]} should have at least one parameter"
334
+ else
335
+ raise ValidationError.new \
336
+ "#{expr[0]} should have at least #{n} parameters"
337
+ end
338
+ end
339
+ true
340
+ end
341
+
342
+ # Tests that an expression has exactly n parameters. Raises a
343
+ # ValidationError if this is not the case.
344
+ def assert_n_params expr, n
345
+ if expr.length != n + 1
346
+ if n == 1
347
+ raise ValidationError.new \
348
+ "#{expr[0]} should have exactly one parameter"
349
+ else
350
+ raise ValidationError.new \
351
+ "#{expr[0]} should have exactly #{n} parameters"
352
+ end
353
+ end
354
+ true
355
+ end
356
+
357
+ # Tests that parameters to an expression are
358
+ # values (integers, symbols, or at-expressions),
359
+ # and raises ValidationError if this is not the
360
+ # case.
361
+ # If ns is nil (default) all parameters should me
362
+ # values.
363
+ # Alternatively, ns may be a range or array containing
364
+ # the indices of the parameters that should be
365
+ # values.
366
+ def assert_params_are_values expr, ns = nil
367
+ if ns == nil
368
+ ns = (1...expr.length)
369
+ end
370
+ ns.each do |i|
371
+ unless int_or_symbol_or_at?(expr[i])
372
+ raise ValidationError.new \
373
+ "#{NTH[i - 1]} parameter to #{expr[0]}" +
374
+ " should be a value (symbol, integer, or at-expression)"
375
+ end
376
+ end
377
+ true
378
+ end
379
+
380
+ def int_or_symbol? x
381
+ x.kind_of?(::Symbol) || x.kind_of?(::Integer)
382
+ end
383
+
384
+ def int_or_symbol_or_at? x
385
+ int_or_symbol?(x) ||
386
+ (x.respond_to?(:length) && x.length == 2 && x[0] == :'@' &&
387
+ int_or_symbol?(x[1]))
388
+ end
389
+
390
+ end
391
+ 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: 3
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
+ - 1
7
8
  - 0
8
- - 7
9
9
  - 0
10
- version: 0.7.0
10
+ version: 1.0.0
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: 2011-01-15 00:00:00 +01:00
18
+ date: 2012-01-14 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -41,18 +41,20 @@ files:
41
41
  - lib/voodoo/code_generator.rb
42
42
  - lib/voodoo/compiler.rb
43
43
  - lib/voodoo/config.rb
44
- - lib/voodoo/generators/amd64_elf_generator.rb
44
+ - lib/voodoo/validator.rb
45
45
  - lib/voodoo/generators/common_code_generator.rb
46
- - lib/voodoo/generators/nasm_generator.rb
46
+ - lib/voodoo/generators/mips_gas_generator.rb
47
+ - lib/voodoo/generators/i386_elf_generator.rb
48
+ - lib/voodoo/generators/command_postprocessor.rb
47
49
  - lib/voodoo/generators/mips_elf_generator.rb
48
50
  - lib/voodoo/generators/gas_elf_generator.rb
51
+ - lib/voodoo/generators/arm_elf_generator.rb
52
+ - lib/voodoo/generators/arm_gas_generator.rb
49
53
  - lib/voodoo/generators/amd64_nasm_generator.rb
50
- - lib/voodoo/generators/mips_gas_generator.rb
51
- - lib/voodoo/generators/generator_api1.rb
52
- - lib/voodoo/generators/i386_nasm_generator.rb
53
- - lib/voodoo/generators/command_postprocessor.rb
54
54
  - lib/voodoo/generators/nasm_elf_generator.rb
55
- - lib/voodoo/generators/i386_elf_generator.rb
55
+ - lib/voodoo/generators/amd64_elf_generator.rb
56
+ - lib/voodoo/generators/nasm_generator.rb
57
+ - lib/voodoo/generators/i386_nasm_generator.rb
56
58
  has_rdoc: true
57
59
  homepage: http://inglorion.net/software/voodoo/
58
60
  licenses: []
@@ -1,95 +0,0 @@
1
- module Voodoo
2
- # This module implements the old code generation API, consisting of
3
- # the following methods:
4
- #
5
- # #add_code
6
- # #add_code_label
7
- # #add_data
8
- # #add_data_label
9
- # #add_function
10
- # #add_function_label
11
- # #align_code
12
- # #align_data
13
- # #align_function
14
- #
15
- module GeneratorApi1
16
- # Add code.
17
- #
18
- # Parameters:
19
- # [actions] an Array of actions to be added
20
- #
21
- # Example:
22
- # add_code [:call, :exit, 0]
23
- def add_code *actions
24
- add :code, *actions
25
- end
26
-
27
- # Add a label at the current address in the code section.
28
- #
29
- # Example:
30
- # add_code_label :mylabel
31
- def add_code_label name
32
- in_section(:code) { label name }
33
- end
34
-
35
- # Add data.
36
- #
37
- # Parameters:
38
- # [defs] an Array of data definitions
39
- #
40
- # Example:
41
- # add_data [:word, 42]
42
- def add_data *defs
43
- add :data, *defs
44
- end
45
-
46
- # Add a label at the current address in the data section.
47
- #
48
- # Example:
49
- # add_data_label :msg
50
- def add_data_label name
51
- add :data, [:label, name]
52
- end
53
-
54
- # Add a label at the address where the next function will be generated.
55
- #
56
- # Example:
57
- # add_function_label :add_one
58
- def add_function_label name
59
- add :functions, [:label, name]
60
- end
61
-
62
- # Align code on the next _alignment_-byte boundary.
63
- # If _alignment_ is not specified, the default code alignment
64
- # is used.
65
- def align_code alignment = nil
66
- if alignment
67
- add :code, [:align, alignment]
68
- else
69
- add :code, [:align]
70
- end
71
- end
72
-
73
- # Align data on the next _alignment_-byte boundary.
74
- # If _alignment_ is not specified, the default data alignment
75
- # is used.
76
- def align_data alignment = nil
77
- if alignment
78
- add :data, [:align, alignment]
79
- else
80
- add :data, [:align]
81
- end
82
- end
83
-
84
- # Align function on the next _alignment_-byte boundary.
85
- # If _alignment_ is not specified, the default function alignment
86
- # is used.
87
- def align_function alignment = nil
88
- if alignment
89
- add :functions, [:align, alignment]
90
- else
91
- add :functions, [:align]
92
- end
93
- end
94
- end
95
- end