testml 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.gemspec +3 -1
  2. data/CHANGELOG.yaml +4 -1
  3. data/LICENSE +1 -1
  4. data/README.rdoc +30 -5
  5. data/Rakefile +10 -1
  6. data/ToDo +56 -0
  7. data/lib/rake/testml.rb +14 -0
  8. data/lib/testml.rb +46 -2
  9. data/lib/testml/bridge.rb +5 -0
  10. data/lib/testml/compiler.rb +106 -0
  11. data/lib/testml/compiler/lite.rb +194 -0
  12. data/lib/testml/compiler/pegex.rb +50 -0
  13. data/lib/testml/compiler/pegex/ast.rb +145 -0
  14. data/lib/testml/compiler/pegex/grammar.rb +173 -0
  15. data/lib/testml/library.rb +7 -0
  16. data/lib/testml/library/debug.rb +18 -0
  17. data/lib/testml/library/standard.rb +86 -0
  18. data/lib/testml/runtime.rb +501 -0
  19. data/lib/testml/runtime/unit.rb +94 -0
  20. data/lib/testml/setup.rb +65 -0
  21. data/lib/testml/util.rb +22 -0
  22. data/test/ast/arguments.tml +87 -0
  23. data/test/ast/basic.tml +83 -0
  24. data/test/ast/dataless.tml +36 -0
  25. data/test/ast/exceptions.tml +59 -0
  26. data/test/ast/external.tml +42 -0
  27. data/test/ast/function.tml +276 -0
  28. data/test/ast/label.tml +58 -0
  29. data/test/ast/markers.tml +36 -0
  30. data/test/ast/semicolons.tml +30 -0
  31. data/test/ast/truth.tml +85 -0
  32. data/test/ast/types.tml +163 -0
  33. data/test/compile-lite.rb +38 -0
  34. data/test/compile-testml-document.rb +59 -0
  35. data/test/compile.rb +57 -0
  36. data/test/inline-bridge.rb +30 -0
  37. data/test/inline.rb +28 -0
  38. data/test/strings.rb +24 -0
  39. data/test/testml.rb +38 -0
  40. data/test/testml.yaml +10 -0
  41. data/test/testml/arguments.tml +18 -0
  42. data/test/testml/assertions.tml +15 -0
  43. data/test/testml/basic.tml +37 -0
  44. data/test/testml/dataless.tml +9 -0
  45. data/test/testml/exceptions.tml +16 -0
  46. data/test/testml/external.tml +8 -0
  47. data/test/testml/external1.tml +10 -0
  48. data/test/testml/external2.tml +3 -0
  49. data/test/testml/function.tml +82 -0
  50. data/test/testml/label.tml +24 -0
  51. data/test/testml/markers.tml +19 -0
  52. data/test/testml/semicolons.tml +10 -0
  53. data/test/testml/standard.tml +50 -0
  54. data/test/testml/truth.tml +22 -0
  55. data/test/testml/types.tml +24 -0
  56. data/test/testml_bridge.rb +28 -0
  57. metadata +69 -4
  58. data/test/fail.rb +0 -12
@@ -0,0 +1,501 @@
1
+ class TestML;end
2
+
3
+ class TestML::Runtime
4
+ attr_accessor :testml
5
+ attr_accessor :bridge
6
+ attr_accessor :library
7
+ attr_accessor :compiler
8
+ attr_accessor :skip
9
+
10
+ attr_accessor :function
11
+ attr_accessor :error
12
+ attr_accessor :global
13
+ attr_accessor :base
14
+
15
+ def initialize(attributes={})
16
+ attributes.each { |k,v| self.send "#{k}=", v }
17
+ $TestMLRuntimeSingleton = self
18
+ @base ||= 'test'
19
+ end
20
+
21
+ def run
22
+ compile_testml
23
+ initialize_runtime
24
+ run_function(@function, [])
25
+ end
26
+
27
+ def run_function(function, args)
28
+ signature = apply_signature(function, args)
29
+
30
+ parent = @function
31
+ @function = function
32
+
33
+ function.statements.each do |statement|
34
+ if statement.kind_of? TestML::Assignment
35
+ run_assignment(statement)
36
+ else
37
+ run_statement(statement)
38
+ end
39
+ end
40
+
41
+ @function = parent
42
+ return
43
+ end
44
+
45
+ def apply_signature(function, args)
46
+ signature = function.signature || []
47
+
48
+ fail "Function received #{args.size} args but expected #{signature.size}" \
49
+ if ! signature.empty? and args.size != signature.size
50
+
51
+ @function.setvar('Self', function)
52
+ signature.each_with_index do |sig_elem, i|
53
+ arg = args[i]
54
+ arg = run_expression(arg) \
55
+ if arg.kind_of? TestML::Expression
56
+ function.setvar(sig_elem, arg)
57
+ end
58
+ end
59
+
60
+ def run_statement(statement)
61
+ blocks = select_blocks(statement.points || [])
62
+ blocks.each do |block|
63
+ @function.setvar('Block', block) if block != 1
64
+
65
+ result = run_expression(statement.expr)
66
+ if assertion = statement.assert
67
+ run_assertion(result, assertion)
68
+ end
69
+ end
70
+ end
71
+
72
+ def run_assignment(assignment)
73
+ @function.setvar(
74
+ assignment.name,
75
+ run_expression(assignment.expr)
76
+ )
77
+ end
78
+
79
+ def run_assertion left, assert
80
+ method_ = method(('assert_' + assert.name).to_sym)
81
+
82
+ @function.getvar('TestNumber').value += 1
83
+
84
+ if assert.expr
85
+ method_.call(left, run_expression(assert.expr))
86
+ else
87
+ method_.call(left)
88
+ end
89
+ end
90
+
91
+ def run_expression(expr)
92
+ context = nil
93
+ @error = nil
94
+ if expr.kind_of? TestML::Expression
95
+ calls = expr.calls.clone
96
+ fail if calls.size <= 1
97
+ context = run_call(calls.shift)
98
+ calls.each do |call|
99
+ if @error
100
+ next unless
101
+ call.kind_of?(TestML::Call) and
102
+ call.name == 'Catch'
103
+ end
104
+ context = run_call(call, context)
105
+ end
106
+ else
107
+ context = run_call(expr)
108
+ end
109
+ if @error
110
+ fail @error
111
+ end
112
+ return context
113
+ end
114
+
115
+ def run_call call, context=nil
116
+ if call.kind_of? TestML::Object
117
+ return call
118
+ end
119
+ if call.kind_of? TestML::Function
120
+ return call
121
+ end
122
+ if call.kind_of? TestML::Point
123
+ return get_point(call.name)
124
+ end
125
+ if call.kind_of? TestML::Call
126
+ name = call.name
127
+ callable =
128
+ @function.getvar(name) ||
129
+ get_point(name) ||
130
+ lookup_callable(name) ||
131
+ fail("Can't locate '#{name}' callable")
132
+ if callable.kind_of? TestML::Object
133
+ return callable
134
+ end
135
+ return callable unless call.args or !context.nil?
136
+ call.args ||= []
137
+ args = call.args.map{|arg| run_expression(arg)}
138
+ args.unshift context if !context.nil?
139
+ if callable.kind_of? TestML::Callable
140
+ begin
141
+ value = callable.value.call(*args)
142
+ rescue
143
+ @error = $!.message
144
+ # @error = "#{$!.class}: #{$!.message}\n at #{$!.backtrace[0]}"
145
+ return TestML::Error.new(@error)
146
+ end
147
+ fail "'#{name}' did not return a TestML::Object object" \
148
+ unless value.kind_of? TestML::Object
149
+ return value
150
+ end
151
+ if callable.kind_of? TestML::Function
152
+ return run_function(callable, args)
153
+ end
154
+ fail
155
+ end
156
+ fail
157
+ end
158
+
159
+ def lookup_callable(name)
160
+ @function.getvar('Library').value.each do |library|
161
+ if library.respond_to?(name)
162
+ function = lambda do |*args|
163
+ library.method(name).call(*args)
164
+ end
165
+ callable = TestML::Callable.new(function)
166
+ @function.setvar(name, callable)
167
+ return callable
168
+ end
169
+ end
170
+ return nil
171
+ end
172
+
173
+ def get_point(name)
174
+ value = @function.getvar('Block').points[name] or return
175
+ if value.sub!(/\n+\z/, "\n") and value == "\n"
176
+ value = ''
177
+ end
178
+ return TestML::Str.new(value)
179
+ end
180
+
181
+ def select_blocks(wanted)
182
+ return [1] if wanted.empty?
183
+ selected = []
184
+ @function.data.each do |block|
185
+ points = block.points
186
+ next if points.key?('SKIP')
187
+ next unless wanted.all?{|point| points.key?(point)}
188
+ if points.key?('ONLY')
189
+ selected = [block]
190
+ break
191
+ end
192
+ selected << block
193
+ break if points.key?('LAST')
194
+ end
195
+ return selected
196
+ end
197
+
198
+ def compile_testml
199
+ fail "'testml' document required but not found" \
200
+ unless @testml
201
+ if @testml !~ /\n/
202
+ @testml =~ /(.*)\/(.*)/ or fail
203
+ testml = $2
204
+ @base = @base + '/' + $1
205
+ @testml = read_testml_file testml
206
+ end
207
+ @function = @compiler.new.compile(@testml)
208
+ end
209
+
210
+ def initialize_runtime
211
+ @global = @function.outer
212
+
213
+ @global.setvar('Block', TestML::Block.new)
214
+ @global.setvar('Label', TestML::Str.new('$BlockLabel'))
215
+ @global.setvar('True', TestML::Constant::True)
216
+ @global.setvar('False', TestML::Constant::False)
217
+ @global.setvar('None', TestML::Constant::None)
218
+ @global.setvar('TestNumber', TestML::Num.new(0))
219
+ @global.setvar('Library', TestML::List.new)
220
+
221
+ library = @function.getvar('Library')
222
+ [@bridge, @library].each do |lib|
223
+ if lib.kind_of? Array
224
+ lib.each {|l| library.push(l.new)}
225
+ else
226
+ library.push(lib.new)
227
+ end
228
+ end
229
+ end
230
+
231
+ def get_label
232
+ label = @function.getvar('Label') or return
233
+ label = label.value
234
+ label.gsub(/\$(\w+)/) {|m| replace_label($1)}
235
+ end
236
+
237
+ def replace_label(var)
238
+ block = @function.getvar('Block')
239
+ if var == 'BlockLabel'
240
+ block.label
241
+ elsif v = block.points[var]
242
+ v.sub!(/\n.*/m, '')
243
+ v.strip
244
+ elsif v = function.getvar(var)
245
+ v.value
246
+ end
247
+ end
248
+
249
+ def read_testml_file file
250
+ path = @base + '/' + file
251
+ File.read(path)
252
+ end
253
+ end
254
+
255
+ #-----------------------------------------------------------------------------
256
+ class TestML::Function
257
+ attr_accessor :signature
258
+ attr_accessor :statements
259
+ attr_accessor :namespace
260
+ attr_accessor :data
261
+
262
+ @@outer = {}
263
+ def outer; @@outer[self.object_id] end
264
+ def outer=(value); @@outer[self.object_id] = value end
265
+ def type; 'Func' end
266
+ def namespace; @namespace ||= {} end
267
+
268
+ def initialize
269
+ @statements = []
270
+ end
271
+
272
+ def getvar name
273
+ s = self
274
+ while s
275
+ if s.namespace.key? name
276
+ return s.namespace[name]
277
+ end
278
+ s = s.outer
279
+ end
280
+ nil
281
+ end
282
+
283
+ def setvar name, value
284
+ namespace[name] = value
285
+ end
286
+
287
+ def forgetvar name
288
+ namespace.delete name
289
+ end
290
+ end
291
+
292
+ #-----------------------------------------------------------------------------
293
+ class TestML::Assignment
294
+ attr_accessor :name
295
+ attr_accessor :expr
296
+
297
+ def initialize(name, expr)
298
+ @name = name
299
+ @expr = expr
300
+ end
301
+ end
302
+
303
+ #-----------------------------------------------------------------------------
304
+ class TestML::Statement
305
+ attr_accessor :expr
306
+ attr_accessor :assert
307
+ attr_accessor :points
308
+
309
+ def initialize(expr, assert=nil, points=nil)
310
+ @expr = expr
311
+ @assert = assert if assert
312
+ @points = points if points
313
+ end
314
+ end
315
+
316
+ #-----------------------------------------------------------------------------
317
+ class TestML::Expression
318
+ attr_accessor :calls
319
+
320
+ def initialize(calls=[])
321
+ @calls = calls
322
+ end
323
+ end
324
+
325
+ #-----------------------------------------------------------------------------
326
+ class TestML::Assertion
327
+ attr_accessor :name
328
+ attr_accessor :expr
329
+
330
+ def initialize(name, expr=nil)
331
+ @name = name
332
+ @expr = expr if expr
333
+ end
334
+ end
335
+
336
+ #-----------------------------------------------------------------------------
337
+ class TestML::Call
338
+ attr_accessor :name
339
+ attr_accessor :args
340
+
341
+ def initialize(name, args=[])
342
+ @name = name
343
+ @args = args if !args.empty?
344
+ end
345
+ end
346
+
347
+ #-----------------------------------------------------------------------------
348
+ class TestML::Callable
349
+ attr_accessor :value
350
+
351
+ def initialize(value)
352
+ @value = value
353
+ end
354
+ end
355
+
356
+ #-----------------------------------------------------------------------------
357
+ class TestML::Block
358
+ attr_accessor :label
359
+ attr_accessor :points
360
+
361
+ def initialize(label='', points={})
362
+ @label = label
363
+ @points = points
364
+ end
365
+ end
366
+
367
+ #-----------------------------------------------------------------------------
368
+ class TestML::Point
369
+ attr_accessor :name
370
+
371
+ def initialize(name)
372
+ @name = name
373
+ end
374
+ end
375
+
376
+ #-----------------------------------------------------------------------------
377
+ class TestML::Object
378
+ attr_accessor :value
379
+
380
+ def initialize(value)
381
+ @value = value
382
+ end
383
+
384
+ def type
385
+ type = self.class.to_s
386
+ type.sub! /^TestML::/, '' or fail "Can't find type of '#{type}'"
387
+ return type
388
+ end
389
+
390
+ def str
391
+ fail "Cast from #{type} to Str is not supported"
392
+ end
393
+ def num
394
+ fail "Cast from #{type} to Num is not supported"
395
+ end
396
+ def bool
397
+ fail "Cast from #{type} to Bool is not supported"
398
+ end
399
+ def list
400
+ fail "Cast from #{type} to List is not supported"
401
+ end
402
+ def none
403
+ TestML::Constant::None
404
+ end
405
+ end
406
+
407
+ #-----------------------------------------------------------------------------
408
+ class TestML::Str < TestML::Object
409
+ def initialize(str)
410
+ @value = str.to_s
411
+ end
412
+ def str
413
+ self
414
+ end
415
+ def num
416
+ TestML::Num.new(@value =~ /^-?\d+(?:\.\d+)$/ ? $1.to_i : 0)
417
+ end
418
+ def bool
419
+ !@value.empty? ? TestML::Constant::True : TestML::Constant::False
420
+ end
421
+ def list
422
+ TestML::List.new(@value.split //)
423
+ end
424
+ end
425
+
426
+ #-----------------------------------------------------------------------------
427
+ class TestML::Num < TestML::Object
428
+ def str
429
+ TestML::Str.new(@value.to_s)
430
+ end
431
+ def num
432
+ self
433
+ end
434
+ def bool
435
+ @value != 0 ? TestML::Constant::True : TestML::Constant::False
436
+ end
437
+ def list
438
+ list = []
439
+ [1..(@value-1)].each { |i| list[i - 1] = nil }
440
+ end
441
+ end
442
+
443
+ #-----------------------------------------------------------------------------
444
+ class TestML::Bool < TestML::Object
445
+ def str
446
+ TestML::Str.new(@value ? "1" : "")
447
+ end
448
+ def num
449
+ TestML::Num.new(@value ? 1 : 0)
450
+ end
451
+ def bool
452
+ self
453
+ end
454
+ end
455
+
456
+ #-----------------------------------------------------------------------------
457
+ class TestML::List < TestML::Object
458
+ def initialize(value=[])
459
+ super(value)
460
+ end
461
+ def push elem
462
+ @value.push elem
463
+ end
464
+ def list
465
+ return self
466
+ end
467
+ end
468
+
469
+ #-----------------------------------------------------------------------------
470
+ class TestML::None < TestML::Object
471
+ def initialize
472
+ super(nil)
473
+ end
474
+ def str
475
+ TestML::Str.new('')
476
+ end
477
+ def num
478
+ TestML::Num.new(0)
479
+ end
480
+ def bool
481
+ TestML::Constant::False
482
+ end
483
+ def list
484
+ TestML::List.new []
485
+ end
486
+ end
487
+
488
+ #-----------------------------------------------------------------------------
489
+ class TestML::Native < TestML::Object
490
+ end
491
+
492
+ #-----------------------------------------------------------------------------
493
+ class TestML::Error < TestML::Object
494
+ end
495
+
496
+ #-----------------------------------------------------------------------------
497
+ module TestML::Constant
498
+ True = TestML::Bool.new(true)
499
+ False = TestML::Bool.new(false)
500
+ None = TestML::None.new
501
+ end