voodoo 0.7.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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