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,50 @@
1
+ require 'pegex/parser'
2
+ require 'testml/compiler'
3
+
4
+ class TestML::Compiler::Pegex < TestML::Compiler
5
+ attr_accessor :parser
6
+
7
+ def compile_code
8
+ @parser = ::Pegex::Parser.new do |p|
9
+ p.grammar = TestML::Compiler::Pegex::Grammar.new
10
+ p.receiver = TestML::Compiler::Pegex::AST.new
11
+ end
12
+ fixup_grammar
13
+ parser.parse(@code, 'code_section') \
14
+ or fail "Parse TestML code section failed"
15
+ end
16
+
17
+ def compile_data
18
+ if !@data.empty?
19
+ parser.parse(@data, 'data_section') \
20
+ or fail "Parse TestML data section failed"
21
+ end
22
+ @function = parser.receiver.function
23
+ end
24
+
25
+ def fixup_grammar
26
+ tree = @parser.grammar.tree
27
+ point_lines = tree['point_lines']['.rgx']
28
+
29
+ block_marker = @directives['BlockMarker']
30
+ if block_marker
31
+ block_marker.gsub! /([\$\%\^\*\+\?\|])/, '\\\\\1'
32
+ tree['block_marker']['.rgx'] = %r!\A#{block_marker}!
33
+ tree['point_lines']['.rgx'] = Regexp.new(
34
+ point_lines.to_s.sub!(/===/, block_marker)
35
+ )
36
+ end
37
+
38
+ point_marker = @directives['PointMarker']
39
+ if point_marker
40
+ point_marker.gsub! /([\$\%\^\*\+\?\|])/, '\\\\\1'
41
+ tree['point_marker']['.rgx'] = %r!\A#{point_marker}!
42
+ tree['point_lines']['.rgx'] = Regexp.new(
43
+ point_lines.to_s.sub!(/\\-\\-\\-/, point_marker)
44
+ )
45
+ end
46
+ end
47
+ end
48
+
49
+ require 'testml/compiler/pegex/grammar'
50
+ require 'testml/compiler/pegex/ast'
@@ -0,0 +1,145 @@
1
+ require 'pegex/tree'
2
+ require 'testml/compiler/pegex'
3
+
4
+ class TestML::Compiler::Pegex::AST < Pegex::Tree
5
+ require 'testml/runtime'
6
+
7
+ attr_accessor :points
8
+ attr_accessor :function
9
+
10
+ def initialize
11
+ @points = []
12
+ @function = TestML::Function.new
13
+ end
14
+
15
+ def got_code_section(code)
16
+ @function.statements = code
17
+ end
18
+
19
+ def got_assignment_statement(match)
20
+ return TestML::Assignment.new(match[0], match[1])
21
+ end
22
+
23
+ def got_code_statement(list)
24
+ expression, assertion = nil, nil
25
+ points = @points
26
+ @points = []
27
+ list.each do |e|
28
+ if e.kind_of? TestML::Assertion
29
+ assertion = e
30
+ else
31
+ expression = e
32
+ end
33
+ end
34
+ return TestML::Statement.new(
35
+ expression,
36
+ assertion,
37
+ !points.empty? ? points : nil,
38
+ )
39
+ end
40
+
41
+ def got_code_expression(list)
42
+ calls = []
43
+ calls.push(list.shift) if !list.empty?
44
+ list = !list.empty? ? list.shift : []
45
+ list.each do |e|
46
+ call = e[1] # XXX this is e[0] in perl
47
+ calls.push(call)
48
+ end
49
+ return calls[0] if calls.size == 1
50
+ return TestML::Expression.new(calls)
51
+ end
52
+
53
+ def got_string_object(string)
54
+ return TestML::Str.new(string)
55
+ end
56
+
57
+ def got_number_object(number)
58
+ return TestML::Num.new(number.to_i)
59
+ end
60
+
61
+ def got_point_object(point)
62
+ point.sub!(/^\*/, '') or fail
63
+ @points.push(point)
64
+ return TestML::Point.new(point)
65
+ end
66
+
67
+ def got_assertion_call(call)
68
+ name, expr = nil, nil
69
+ %w( eq has ok ).each do |a|
70
+ if expr = call["assertion_#{a}"]
71
+ name = a.upcase
72
+ expr =
73
+ expr.fetch("assertion_operator_#{a}", [])[0] ||
74
+ expr.fetch("assertion_function_#{a}", [])[0]
75
+ break
76
+ end
77
+ end
78
+ return TestML::Assertion.new(name, expr)
79
+ end
80
+
81
+ def got_assertion_function_ok(ok)
82
+ return { 'assertion_function_ok' => [] }
83
+ end
84
+
85
+ def got_function_start(dummy)
86
+ function = TestML::Function.new
87
+ function.outer = @function
88
+ @function = function
89
+ return true
90
+ end
91
+
92
+ def got_function_object(object)
93
+ function = @function
94
+ @function = function.outer
95
+
96
+ if object[0].kind_of? Array and object[0][0].kind_of? Array
97
+ function.signature = object[0][0]
98
+ end
99
+ function.statements = object[-1]
100
+
101
+ return function
102
+ end
103
+
104
+ def got_call_name(name)
105
+ return TestML::Call.new(name)
106
+ end
107
+
108
+ def got_call_object(object)
109
+ call = object[0]
110
+ args = object[1] && object[1][-1]
111
+ if args
112
+ args = args.map do |arg|
113
+ (arg.kind_of?(TestML::Expression) and arg.calls.size == 1 and
114
+ (
115
+ arg.calls[0].kind_of?(TestML::Point) ||
116
+ arg.calls[0].kind_of?(TestML::Object)
117
+ )) ? arg.calls[0] : arg
118
+ end
119
+ call.args = args
120
+ end
121
+ return call
122
+ end
123
+
124
+ def got_call_argument_list(list)
125
+ return list
126
+ end
127
+
128
+ def got_call_indicator(dummy)
129
+ return
130
+ end
131
+
132
+ def got_data_section(data)
133
+ @function.data = data
134
+ end
135
+
136
+ def got_data_block(block)
137
+ label = block[0][0][0]
138
+ points = block[1].inject({}){|r, h| r.merge!(h)}
139
+ return TestML::Block.new(label, points)
140
+ end
141
+
142
+ def got_block_point(point)
143
+ return { point[0] => point[1] }
144
+ end
145
+ end
@@ -0,0 +1,173 @@
1
+ require 'pegex/grammar'
2
+ require 'testml/compiler/pegex'
3
+
4
+ class TestML::Compiler::Pegex::Grammar < Pegex::Grammar
5
+ File = '../testml-pgx/testml.pgx'
6
+
7
+ def make_tree
8
+ {"+toprule"=>"testml_document",
9
+ "assertion_call"=>
10
+ {".any"=>
11
+ [{"-wrap"=>1, ".ref"=>"assertion_eq"},
12
+ {"-wrap"=>1, ".ref"=>"assertion_ok"},
13
+ {"-wrap"=>1, ".ref"=>"assertion_has"}]},
14
+ "assertion_call_test"=>
15
+ {".rgx"=>
16
+ /\A(?:\.(?:[\ \t]|\r?\n|\#.*\r?\n)*|(?:[\ \t]|\r?\n|\#.*\r?\n)*\.)(?:EQ|OK|HAS)/},
17
+ "assertion_eq"=>
18
+ {".any"=>
19
+ [{"-wrap"=>1, ".ref"=>"assertion_operator_eq"},
20
+ {"-wrap"=>1, ".ref"=>"assertion_function_eq"}]},
21
+ "assertion_function_eq"=>
22
+ {".all"=>
23
+ [{".rgx"=>
24
+ /\A(?:\.(?:[\ \t]|\r?\n|\#.*\r?\n)*|(?:[\ \t]|\r?\n|\#.*\r?\n)*\.)EQ\(/},
25
+ {".ref"=>"code_expression"},
26
+ {".rgx"=>/\A\)/}]},
27
+ "assertion_function_has"=>
28
+ {".all"=>
29
+ [{".rgx"=>
30
+ /\A(?:\.(?:[\ \t]|\r?\n|\#.*\r?\n)*|(?:[\ \t]|\r?\n|\#.*\r?\n)*\.)HAS\(/},
31
+ {".ref"=>"code_expression"},
32
+ {".rgx"=>/\A\)/}]},
33
+ "assertion_function_ok"=>
34
+ {".rgx"=>
35
+ /\A(?:\.(?:[\ \t]|\r?\n|\#.*\r?\n)*|(?:[\ \t]|\r?\n|\#.*\r?\n)*\.)(OK)(?:\((?:[\ \t]|\r?\n|\#.*\r?\n)*\))?/},
36
+ "assertion_has"=>
37
+ {".any"=>
38
+ [{"-wrap"=>1, ".ref"=>"assertion_operator_has"},
39
+ {"-wrap"=>1, ".ref"=>"assertion_function_has"}]},
40
+ "assertion_ok"=>{".ref"=>"assertion_function_ok"},
41
+ "assertion_operator_eq"=>
42
+ {".all"=>
43
+ [{".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)+==(?:[\ \t]|\r?\n|\#.*\r?\n)+/},
44
+ {".ref"=>"code_expression"}]},
45
+ "assertion_operator_has"=>
46
+ {".all"=>
47
+ [{".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)+\~\~(?:[\ \t]|\r?\n|\#.*\r?\n)+/},
48
+ {".ref"=>"code_expression"}]},
49
+ "assignment_statement"=>
50
+ {".all"=>
51
+ [{".ref"=>"variable_name"},
52
+ {".rgx"=>/\A\s+=\s+/},
53
+ {".ref"=>"code_expression"},
54
+ {".ref"=>"ending"}]},
55
+ "blank_line"=>{".rgx"=>/\A[\ \t]*\r?\n/},
56
+ "block_header"=>
57
+ {".all"=>
58
+ [{".ref"=>"block_marker"},
59
+ {"+max"=>1, ".all"=>[{".rgx"=>/\A[\ \t]+/}, {".ref"=>"block_label"}]},
60
+ {".rgx"=>/\A[\ \t]*\r?\n/}]},
61
+ "block_label"=>{".ref"=>"unquoted_string"},
62
+ "block_marker"=>{".rgx"=>/\A===/},
63
+ "block_point"=>{".any"=>[{".ref"=>"lines_point"}, {".ref"=>"phrase_point"}]},
64
+ "call_argument"=>{".ref"=>"code_expression"},
65
+ "call_argument_list"=>
66
+ {".all"=>
67
+ [{".rgx"=>/\A\((?:[\ \t]|\r?\n|\#.*\r?\n)*/},
68
+ {"+min"=>0,
69
+ ".ref"=>"call_argument",
70
+ ".sep"=>
71
+ {".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)*,(?:[\ \t]|\r?\n|\#.*\r?\n)*/}},
72
+ {".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)*\)/}]},
73
+ "call_call"=>
74
+ {".all"=>
75
+ [{"+asr"=>-1, ".ref"=>"assertion_call_test"},
76
+ {".ref"=>"call_indicator"},
77
+ {".ref"=>"code_object"}]},
78
+ "call_indicator"=>
79
+ {".rgx"=>
80
+ /\A(?:\.(?:[\ \t]|\r?\n|\#.*\r?\n)*|(?:[\ \t]|\r?\n|\#.*\r?\n)*\.)/},
81
+ "call_name"=>{".any"=>[{".ref"=>"user_call"}, {".ref"=>"core_call"}]},
82
+ "call_object"=>
83
+ {".all"=>[{".ref"=>"call_name"}, {"+max"=>1, ".ref"=>"call_argument_list"}]},
84
+ "code_expression"=>
85
+ {".all"=>[{".ref"=>"code_object"}, {"+min"=>0, ".ref"=>"call_call"}]},
86
+ "code_object"=>
87
+ {".any"=>
88
+ [{".ref"=>"function_object"},
89
+ {".ref"=>"point_object"},
90
+ {".ref"=>"string_object"},
91
+ {".ref"=>"number_object"},
92
+ {".ref"=>"call_object"}]},
93
+ "code_section"=>
94
+ {"+min"=>0,
95
+ ".any"=>
96
+ [{".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)+/},
97
+ {".ref"=>"assignment_statement"},
98
+ {".ref"=>"code_statement"}]},
99
+ "code_statement"=>
100
+ {".all"=>
101
+ [{".ref"=>"code_expression"},
102
+ {"+max"=>1, ".ref"=>"assertion_call"},
103
+ {".ref"=>"ending"}]},
104
+ "comment"=>{".rgx"=>/\A\#.*\r?\n/},
105
+ "core_call"=>{".rgx"=>/\A([A-Z]\w*)/},
106
+ "data_block"=>
107
+ {".all"=>
108
+ [{".ref"=>"block_header"},
109
+ {"+min"=>0,
110
+ "-skip"=>1,
111
+ ".any"=>[{".ref"=>"blank_line"}, {".ref"=>"comment"}]},
112
+ {"+min"=>0, ".ref"=>"block_point"}]},
113
+ "data_section"=>{"+min"=>0, ".ref"=>"data_block"},
114
+ "double_quoted_string"=>{".rgx"=>/\A(?:"((?:[^\n\\"]|\\"|\\\\|\\[0nt])*?)")/},
115
+ "ending"=>{".rgx"=>/\A(?:;|\r?\n)/},
116
+ "function_object"=>
117
+ {".all"=>
118
+ [{"+max"=>1, ".ref"=>"function_signature"},
119
+ {".ref"=>"function_start"},
120
+ {"+min"=>0,
121
+ ".any"=>
122
+ [{".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)+/},
123
+ {".ref"=>"assignment_statement"},
124
+ {".ref"=>"code_statement"}]},
125
+ {".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)*\}/}]},
126
+ "function_signature"=>
127
+ {".all"=>
128
+ [{".rgx"=>/\A\((?:[\ \t]|\r?\n|\#.*\r?\n)*/},
129
+ {"+max"=>1, ".ref"=>"function_variables"},
130
+ {".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)*\)/}]},
131
+ "function_start"=>
132
+ {".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)*(\{)(?:[\ \t]|\r?\n|\#.*\r?\n)*/},
133
+ "function_variable"=>{".rgx"=>/\A([a-zA-Z]\w*)/},
134
+ "function_variables"=>
135
+ {"+min"=>1,
136
+ ".ref"=>"function_variable",
137
+ ".sep"=>
138
+ {".rgx"=>/\A(?:[\ \t]|\r?\n|\#.*\r?\n)*,(?:[\ \t]|\r?\n|\#.*\r?\n)*/}},
139
+ "lines_point"=>
140
+ {".all"=>
141
+ [{".ref"=>"point_marker"},
142
+ {".rgx"=>/\A[\ \t]+/},
143
+ {".ref"=>"point_name"},
144
+ {".rgx"=>/\A[\ \t]*\r?\n/},
145
+ {".ref"=>"point_lines"}]},
146
+ "number"=>{".rgx"=>/\A([0-9]+)/},
147
+ "number_object"=>{".ref"=>"number"},
148
+ "phrase_point"=>
149
+ {".all"=>
150
+ [{".ref"=>"point_marker"},
151
+ {".rgx"=>/\A[\ \t]+/},
152
+ {".ref"=>"point_name"},
153
+ {".rgx"=>/\A:[\ \t]/},
154
+ {".ref"=>"point_phrase"},
155
+ {".rgx"=>/\A\r?\n/},
156
+ {".rgx"=>/\A(?:\#.*\r?\n|[\ \t]*\r?\n)*/}]},
157
+ "point_lines"=>{".rgx"=>/\A((?:(?!===|\-\-\-).*\r?\n)*)/},
158
+ "point_marker"=>{".rgx"=>/\A\-\-\-/},
159
+ "point_name"=>{".rgx"=>/\A([a-z]\w*|[A-Z]\w*)/},
160
+ "point_object"=>{".rgx"=>/\A(\*[a-z]\w*)/},
161
+ "point_phrase"=>{".ref"=>"unquoted_string"},
162
+ "quoted_string"=>
163
+ {".any"=>
164
+ [{".ref"=>"single_quoted_string"}, {".ref"=>"double_quoted_string"}]},
165
+ "single_quoted_string"=>{".rgx"=>/\A(?:'((?:[^\n\\']|\\'|\\\\)*?)')/},
166
+ "string_object"=>{".ref"=>"quoted_string"},
167
+ "testml_document"=>
168
+ {".all"=>[{".ref"=>"code_section"}, {"+max"=>1, ".ref"=>"data_section"}]},
169
+ "unquoted_string"=>{".rgx"=>/\A([^\ \t\n\#](?:[^\n\#]*[^\ \t\n\#])?)/},
170
+ "user_call"=>{".rgx"=>/\A([a-z]\w*)/},
171
+ "variable_name"=>{".rgx"=>/\A([a-zA-Z]\w*)/}}
172
+ end
173
+ end
@@ -0,0 +1,7 @@
1
+ class TestML::Library
2
+ def runtime
3
+ $TestMLRuntimeSingleton
4
+ end
5
+ end
6
+
7
+
@@ -0,0 +1,18 @@
1
+ require 'testml/library'
2
+
3
+ # TODO Either require or duplicate wxyz.rb here
4
+ class TestML::Library::Debug < TestML::Library
5
+ def XXX(*args)
6
+ require 'yaml'
7
+ args.each {|node| puts YAML.dump(node)}
8
+ puts 'XXX from: ' + caller.first
9
+ exit
10
+ end
11
+
12
+ def YYY(*args)
13
+ require 'yaml'
14
+ args.each {|node| puts YAML.dump(node)}
15
+ puts 'YYY from: ' + caller.first
16
+ return *args
17
+ end
18
+ end
@@ -0,0 +1,86 @@
1
+ require 'testml/library'
2
+ require 'testml/util'
3
+
4
+ class TestML::Library::Standard < TestML::Library
5
+ include TestML::Util
6
+
7
+ def Get(key)
8
+ key = key.str.value
9
+ return runtime.function.getvar(key)
10
+ end
11
+
12
+ # def Set(key, value)
13
+ # return runtime.function.setvar(key, value)
14
+ # end
15
+
16
+ def GetLabel
17
+ return str(runtime.get_label)
18
+ end
19
+
20
+ def Type(var)
21
+ return str(var.type)
22
+ end
23
+
24
+ def Catch(*args)
25
+ error = runtime.error \
26
+ or fail "Catch called but no TestML error found"
27
+ runtime.error = nil
28
+ return str(error)
29
+ end
30
+
31
+ def Throw(msg)
32
+ fail(msg.value)
33
+ end
34
+
35
+ def Str(object)
36
+ return str(object.str.value)
37
+ end
38
+
39
+ # def Num(object)
40
+ # return num(object.num.value)
41
+ # end
42
+
43
+ # def Bool(object)
44
+ # return bool(object.bool.value)
45
+ # end
46
+
47
+ def List(*args)
48
+ return list(args)
49
+ end
50
+
51
+ def Join(list, separator=nil)
52
+ separator = separator ? separator.value : ''
53
+ return str(list.list.value.map {|e| e.value}.join(separator))
54
+ end
55
+
56
+ def Not(bool_)
57
+ return bool(bool_.bool.value ? false : true)
58
+ end
59
+
60
+ def Text(lines)
61
+ value = lines.list.value
62
+ return str(((value.map {|l| l.value}) + ['']).join($/))
63
+ end
64
+
65
+ def Count(list)
66
+ return num(list.list.value.size)
67
+ end
68
+
69
+ def Lines(text)
70
+ return list(text.value.split(/\n/).map {|l| str(l)})
71
+ end
72
+
73
+ def Reverse(list)
74
+ return list(list.list.value.reverse)
75
+ end
76
+
77
+ def Sort(list)
78
+ return list(list.list.value.sort {|a, b| a.value <=> b.value})
79
+ end
80
+
81
+ def Strip(string, part)
82
+ string = string.str.value
83
+ part = part.str.value
84
+ return str string.sub(part, '')
85
+ end
86
+ end