testml 0.0.1 → 0.0.2

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.
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