2 # Functionality for validating Voodoo code.
5 # Expressions that take two arguments
6 BINOPS = [:add, :and, :asr, :bsr, :div, :mod, :mul, :or,
7 :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]
13 # Symbols that are a valid start of a statement
14 STATEMENTS = [:block, :call,
15 :'get-byte', :'get-word', :goto,
16 :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne,
17 :label, :let, :return, :set,
18 :'set-byte', :'set-word', :'tail-call']
20 # Symbols that are valid at top-level
21 TOP_LEVELS = [:align, :byte, :export, :function, :import,
22 :section, :string, :word] + STATEMENTS
26 # Validates an expression.
27 # Returns true if the expression is valid.
28 # Raises ValidationError if the expression is not valid.
29 def validate_expression code
32 elsif code.respond_to? :[]
33 if BINOPS.member? code[0]
34 # binop must have 2 parameters, both of them atomic values
36 if atomic_value? code[1]
37 if atomic_value? code[2]
40 raise ValidationError.new("#{code[2].inspect} should be" +
41 "a Symbol or an Integer", code)
44 raise ValidationError.new("#{code[1].inspect} should be" +
45 "a Symbol or an Integer", code)
48 raise ValidationError.new("#{code[0]} should get exactly 2" +
52 # code[0] is not a binop
54 when :call, :'tail-call'
55 # call must have at least 1 parameter
56 # and all parameters must be atomic values
58 raise ValidationError.new("#{code[0]} requires at least" +
59 " one parameter", code)
61 code[1..-1].each do |x|
62 unless atomic_value? x
63 raise ValidationError.new("All parameters to #{code[0]}" +
64 " should be atomic values" +
65 " (symbols or integers)",
72 when :'get-byte', :'get-word'
73 # Must have exactly 2 parameters.
74 # First parameter must be a symbol.
75 # Second parameter must be an atomic value.
77 raise ValidationError.new("#{code[0]} should have exactly" +
78 " two parameters", code)
79 elsif !code[1].kind_of?(::Symbol)
80 raise ValidationError.new("First parameter to #{code[0]} must" +
82 elsif !atomic_value?(code[2])
83 raise ValidationError.new("Second parameter to #{code[0]} must" +
84 " be a symbol or an integer", code)
90 # must have a single, atomic parameter
92 raise ValidationError.new("not takes exactly one parameter",
94 elsif atomic_value? code[1]
97 raise ValidationError.new("Parameter to not must be a" +
98 " symbol or integer", code)
102 raise ValidationError.new("#{code[0].inspect}" +
103 " cannot start an expression",
108 # code is not an atomic value and does not respond to :[]
109 raise ValidationError.new("#{code.inspect} is not a valid expression",
114 # Validates a statement.
115 # Returns true if the statement is valid.
116 # Raises ValidationError if the statement is not valid.
117 def validate_statement code
121 code[1..-1].each {|stmt| validate_statement stmt}
124 when :call, :'tail-call'
125 validate_expression code
128 # must have exactly 1 parameter, which must be atomic
130 raise ValidationError.new("goto should have exactly" +
131 " one parameter", code)
132 elsif atomic_value? code[1]
135 raise ValidationError.new("parameter to goto must be a" +
136 " label or an integer", code)
139 when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
140 # Must have 2 or 3 parameters.
141 # First parameter must be an array (or similar) containing
142 # two elements, both atomic.
143 # Second parameter must consist of one or more statements.
144 # Third parameter, if present, must consist of zero or more
146 # let is not allowed as a statement in either parameter, though
147 # it can be embedded in a block in either.
148 if code.length < 3 || code.length > 4
149 raise ValidationError.new("#{code[0]} takes 2 or 3 parameters",
151 elsif code[1].length != 2
152 raise ValidationError.new("#{code[0]} requires two values to" +
153 " compare in its first parameter",
155 elsif !code[1].all? {|x| atomic_value? x}
156 raise ValidationError.new("Values to compare must be atomic" +
157 " (symbols or integers)", code)
159 code[2].each do |stmt|
160 validate_statement stmt
162 raise ValidationError.new("let is not allowed inside " +
168 code[3].each do |stmt|
169 validate_statement stmt
171 raise ValidationError.new("let is not allowed inside " +
181 # must have 1 parameter which must be a symbol
182 if code.length != 2 || !code[1].kind_of?(::Symbol)
183 raise ValidationError.new("label requires a single symbol" +
184 "as its parameter", code)
190 # must have exactly 2 parameters
192 raise ValidationError.new("#{code[0]} requires exactly" +
193 " two parameters", code)
194 elsif code[1].kind_of? ::Symbol
195 # Second parameter must be an expression
196 validate_expression code[2]
198 raise ValidationError.new("First parameter to #{code[0]} must be" +
207 validate_expression code[1]
209 raise ValidationError.new("return only takes a single parameter",
213 when :'set-byte', :'set-word'
214 # Must have exactly 3 parameters.
215 # First parameter must be a symbol.
216 # Second parameter must be an atomic value.
217 # Third parameter must be an expression.
219 raise ValidationError.new("#{code[0]} should have exactly" +
220 " three parameters", code)
221 elsif !code[1].kind_of?(::Symbol)
222 raise ValidationError.new("First parameter to #{code[0]} must" +
223 " be a symbol", code)
224 elsif !atomic_value?(code[2])
225 raise ValidationError.new("Second parameter to #{code[0]} must" +
226 " be a symbol or an integer", code)
228 validate_expression code[3]
232 raise ValidationError.new("Not a valid statement: #{code.inspect}",
236 rescue ValidationError
240 rescue Exception => e
241 if code.respond_to? :[]
242 # Pass on the execption
245 raise ValidationError.new("#{code.inspect} does not respond to" +
252 # Validates a top-level directive.
253 # Returns true if the directive is valid.
254 # Raises ValidationError if the directive is not valid.
255 def validate_toplevel code
259 # Must either have no parameter or a single integer parameter
260 if code.length == 1 || (code.length == 2 &&
261 code[1].kind_of?(::Integer))
264 raise ValidationError.new("align requires either a single" +
265 " integer parameter, or no parameters",
270 # Must have a single integer or symbol parameter
271 if code.length != 2 || !atomic_value?(code[1])
272 raise ValidationError.new("#{code[0]} requires a single" +
273 " parameter that is either an " +
274 " integer or a symbol", code)
279 when :export, :import
280 # Must have at least 1 parameter, and all parameters must
283 raise ValidationError.new("#{code[0]} requires at least " +
284 " one parameter", code)
285 elsif code[1..-1].all? {|x| x.kind_of? ::Symbol}
288 raise ValidationError.new("All parameters to #{code[0]}" +
289 " should be symbols", code)
294 # Check that formal parameters have been specified
296 raise ValidationError.new("Formal parameters should be" +
297 " specified for function",
301 # Check that all formal parameters are symbols
302 code[1].each do |formal|
303 unless formal.kind_of? ::Symbol
304 raise ValidationError.new("Formal parameter #{formal.inspect}" +
305 " should be a symbol",
311 code[2..-1].each {|stmt| validate_statement stmt}
315 # Check that we have a string or a symbol
318 raise ValidationError.new("Section name should be specified",
321 unless code[1].kind_of?(::String) || code[1].kind_of?(::Symbol)
322 raise ValidationError.new("Section name should be a String" +
328 raise ValidationError.new("section directive should have only" +
329 " a single parameter",
334 # Must have a single string parameter
335 if code.length != 2 || !code[1].kind_of?(::String)
336 raise ValidationError.new("string requires a single string" +
337 " as a parameter", code)
343 if STATEMENTS.member? code[0]
344 validate_statement code
346 raise ValidationError.new("Directive #{code[0].inspect}" +
347 " not valid at top-level;" +
348 " valid directives are: " +
349 TOP_LEVELS.join(', '),
354 rescue ValidationError
358 rescue Exception => e
359 if code.respond_to? :[]
360 # Pass on the exception
363 raise ValidationError.new("#{code.inspect} does not respond to" +
368 # If we got here, all is well
372 # Base class for errors signalled by the Validator.
373 class ValidationError < StandardError
374 def initialize message, code = nil
381 def atomic_value? code
382 code.kind_of?(::Symbol) || code.kind_of?(::Integer)