Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Clean up the Perl6 implementation.

Which is now the reference implementation.
  • Loading branch information...
commit edb41cf0b82c163f311d12286b080515e4b382d5 1 parent fd053b5
Ingy döt Net authored
8 Makefile
... ... @@ -1,10 +1,14 @@
1 1 ALL_TESTS = $(shell ls t/*.t)
2 2
3   -all:
  3 +default:
  4 + @echo 'Try these `make` targets:'
  5 + @echo
  6 + @echo ' make test # Run all tests'
  7 + @echo ' make t/test.t # Run a specific test'
4 8
5 9 test: $(ALL_TESTS)
6 10
7 11 $(ALL_TESTS): force
8   - perl6 $@
  12 + PERL6LIB=t:lib perl6 $@
9 13
10 14 force:
10 lib/TestML.pm
... ... @@ -1,12 +1,4 @@
1 1 use v6;
2 2 class TestML;
3 3
4   -# $*VERSION = '0.01';
5   -
6   -# =for pod
7   -#
8   -# =head1 NAME
9   -#
10   -# TestML - A Generic Testing Language
11   -#
12   -# =end pod
  4 +our $*VERSION = '0.01';
2  lib/TestML/Document.pm
@@ -2,9 +2,9 @@ use v6;
2 2
3 3 class TestML::Document::Meta { ... }
4 4 class TestML::Document::Tests { ... }
5   -class TestML::Document::Data { ... }
6 5 class TestML::Expression { ... }
7 6 class TestML::Assertion { ... }
  7 +class TestML::Document::Data { ... }
8 8
9 9 #-----------------------------------------------------------------------------
10 10 class TestML::Document;
294 lib/TestML/Parser.pm
... ... @@ -1,6 +1,7 @@
1 1 use v6;
2   -class Parser;
  2 +class TestML::Parser;
3 3
  4 +use TestML::Parser::Grammar;
4 5 use TestML::Document;
5 6
6 7 my $doc;
@@ -8,293 +9,41 @@ my $data;
8 9 my $statement;
9 10 my @insertion_stack;
10 11
11   -grammar TestMLGrammar { ... }
12   -grammar TestMLDataSection { ... }
13   -grammar TestMLActions { ... }
  12 +grammar TestML::Parser::Actions { ... }
14 13
15 14 method parse($testml) {
16 15 $doc = TestML::Document.new();
17 16 @insertion_stack = ();
18   - my $rc1 = TestMLGrammar.parse($testml, :actions(TestMLActions));
  17 + my $rc1 = TestML::Parser::Grammar.parse($testml, :actions(TestML::Parser::Actions));
19 18 if (not $rc1) {
20 19 die "Parse TestML failed";
21 20 }
22   - my $rc2 = TestMLDataSection.parse($data, :actions(TestMLActions));
  21 + my $rc2 = TestML::Parser::Grammar::DataSection.parse(
  22 + $data, :actions(TestML::Parser::Actions)
  23 + );
23 24 if (not $rc2) {
24 25 die "Parse TestML Data failed";
25 26 }
26 27 return $doc;
27 28 }
28 29
29   -#------------------------------------------------------------------------------#
30   -grammar TestMLBase;
31   -regex ALWAYS { <?> } # Always match
32   -regex ANY { . } # Any unicode character
33   -regex SPACE { <[\ \t]> } # A space or tab character
34   -regex BREAK { \n } # A newline character
35   -regex EOL { \r? \n } # A Unix or DOS line ending
36   -regex NON_BREAK { \N } # Any character except newline
37   -regex NON_SPACE_BREAK
38   - { <![\ \n]> } # Any character except space or newline
39   -regex LOWER { <[a..z]> } # Lower case ASCII alphabetic character
40   -regex UPPER { <[A..Z]> } # Upper case ASCII alphabetic character
41   -regex ALPHANUM { <[A..Za..z0..9]> } # ASCII alphanumeric character
42   -regex WORD { <[A..Za..z0..9_]> } # A "word" character
43   -regex DIGIT { <[0..9]> } # A numeric digit
44   -regex STAR { '*' } # An asterisk
45   -regex DOT { '.' } # A period character
46   -regex HASH { '#' } # An octothorpe (or hash) character
47   -regex BACK { '\\' } # A backslash character
48   -regex SINGLE { "'" } # A single quote character
49   -regex DOUBLE { '"' } # A double quote character
50   -regex ESCAPE { <[0nt]> } # One of the escapable character IDs
51   -
52   -token comment { <HASH> <line> }
53   -token line { <NON_BREAK>* <EOL> }
54   -token blank_line { <SPACE>* <EOL> }
55   -token unquoted_string {
56   - [
57   - <!before <SPACE>+ <HASH>>
58   - <!before <SPACE>* <EOL>>
59   - <ANY>
60   - ]+
61   -}
62   -token single_quoted_string {
63   - \' ~ \' [ <sq_string> | \\ <sq_escape> ]*
64   -}
65   -token sq_string {
66   - [
67   - <!before \n>
68   - <!before \\>
69   - <!before \'>
70   - <ANY>
71   - ]+
72   -}
73   -token sq_escape {
74   - <['\\]>
75   -}
76   -token double_quoted_string {
77   - \" ~ \" [ <dq_string> | \\ <dq_escape> ]*
78   -}
79   -token dq_string {
80   - [
81   - <!before \t>
82   - <!before \n>
83   - <!before \\>
84   - <!before \">
85   - <ANY>
86   - ]+
87   -}
88   -token dq_escape {
89   - <["\\nrt]>
90   -}
91   -
92   -
93   -#------------------------------------------------------------------------------#
94   -grammar TestMLGrammar is TestMLBase;
95   -rule TOP {^ <document> $}
96   -
97   -rule document {
98   - <meta_section> <test_section> <data_section>?
99   -}
100   -
101   -#------------------------------------------------------------------------------#
102   -rule meta_section {
103   - [ <comment> | <blank_line> ]*
104   - <meta_testml_statement>
105   - [ <meta_statement> | <comment> | <blank_line> ]*
106   -}
107   -
108   -regex meta_testml_statement {
109   - '%TestML:' <SPACE>+ <testml_version>
110   - [ <SPACE>+ <comment> | <EOL> ]
111   -}
112   -
113   -token testml_version { <.DIGIT> <.DOT> <.DIGIT>+ }
114   -
115   -regex meta_statement {
116   - '%' <meta_keyword> ':' <SPACE>+ <meta_value>
117   - [ <SPACE>+ <comment> | <EOL> ]
118   -}
119   -
120   -token meta_keyword {
121   - <core_meta_keyword> | <user_meta_keyword>
122   -}
123   -
124   -token core_meta_keyword {
125   - Title | Data | Plan | BlockMarker | PointMarker
126   -}
127   -
128   -token user_meta_keyword {
129   - <LOWER> <WORD>*
130   -}
131   -
132   -token meta_value {
133   - <quoted_string> | <unquoted_string>
134   -}
135   -
136   -token quoted_string {
137   - <single_quoted_string> | <double_quoted_string>
138   -}
139   -
140   -
141   -#------------------------------------------------------------------------------#
142   -token test_section {
143   - [ <wspace> | <test_statement> ]*
144   -}
145   -
146   -token wspace {
147   - <SPACE> | <EOL> | <comment>
148   -}
149   -
150   -token test_statement {
151   - <test_statement_start>
152   - <test_expression> <assertion_expression>? ';'
153   -}
154   -
155   -token test_statement_start {
156   - <ALWAYS>
157   -}
158   -
159   -token test_expression {
160   - <sub_expression>
161   - [
162   - <!assertion_call>
163   - <call_indicator>
164   - <sub_expression>
165   - ]*
166   -}
167   -
168   -token sub_expression {
169   - <transform_call> | <data_point> | <quoted_string> | <constant>
170   -}
171   -
172   -token transform_call {
173   - <transform_name> '(' <wspace>* <argument_list> <wspace>* ')'
174   -}
175   -
176   -token transform_name {
177   - <user_transform> | <core_transform>
178   -}
179   -
180   -token user_transform {
181   - <LOWER> <WORD>*
182   -}
183   -
184   -token core_transform {
185   - <UPPER> <WORD>*
186   -}
187   -
188   -token call_indicator {
189   - <DOT> <wspace>* | <wspace>* <DOT>
190   -}
191   -
192   -token data_point {
193   - <.STAR> ( <.LOWER> <.WORD>* )
194   -}
195   -
196   -token constant {
197   - <UPPER> <WORD>*
198   -}
199   -
200   -token argument_list {
201   - [ <argument> [ <wspace>* ',' <wspace>* <argument> ]* ]?
202   -}
203   -
204   -token argument {
205   - <sub_expression>
206   -}
207   -
208   -token assertion_expression {
209   - <assertion_operation> | <assertion_call>
210   -}
211   -
212   -token assertion_operation {
213   - <wspace>+ <assertion_operator> <wspace>+ <test_expression>
214   -}
215   -
216   -token assertion_operator {
217   - '=='
218   -}
219   -
220   -token assertion_call {
221   - <call_indicator> <assertion_name>
222   - '(' <wspace>* <test_expression> <wspace>* ')'
223   -}
224   -
225   -token assertion_name {
226   - <UPPER>+
227   -}
228   -
229   -token data_section {
230   - <ANY>*
231   -}
232   -
233   -
234   -#------------------------------------------------------------------------------#
235   -grammar TestMLDataSection is TestMLBase;
236   -token TOP { ^ <data_section> $ }
237   -
238   -token data_section {
239   - <data_block>*
240   -}
241   -
242   -token data_block {
243   - <block_header> [ <blank_line> | <comment> ]* <block_point>*
244   -}
245   -
246   -token block_header {
247   - <block_marker> [ <SPACE>+ <block_label> ]? <SPACE>* <EOL>
248   -}
249   -
250   -token block_marker {
251   - '==='
252   -}
253   -
254   -token block_label {
255   -# [ <ANY> - [ <SPACE> | <BREAK> ] ]
256   -# [ <NON_BREAK>* [ <ANY> - [ <SPACE> | <BREAK> ] ] ]?
257   - <unquoted_string>
258   -}
259   -
260   -token block_point {
261   - <lines_point> | <phrase_point>
262   -}
263   -
264   -token lines_point {
265   - <point_marker> <SPACE>+ <point_name> <SPACE>* <EOL>
266   - ([
267   - <!before <block_marker>>
268   - <!before <point_marker>>
269   - <line>
270   - ]*)
271   -}
272   -
273   -token phrase_point {
274   - <point_marker> <SPACE>+ <point_name> ':' <SPACE>+
275   - (<unquoted_string>) <SPACE>* <EOL>
276   - [<blank_line> | <comment>]*
277   -}
278   -
279   -token point_marker {
280   - '---'
281   -}
282   -
283   -token point_name {
284   - <core_point_name> | <user_point_name>
285   -}
286   -
287   -token core_point_name {
288   - <UPPER> <WORD>*
289   -}
290   -
291   -token user_point_name {
292   - <LOWER> <WORD>*
  30 +method parse_data ($parser) {
  31 + my $builder = $parser.receiver;
  32 + my $document = $builder.document;
  33 + for $document.meta.data<Data> -> $file {
  34 + if $file eq '_' {
  35 + $parser.stream($builder.inline_data);
  36 + }
  37 + else {
  38 + $parser.open("self.base/$file");
  39 + }
  40 + $parser.parse;
  41 + $document.data.blocks.push(|$parser.receiver.blocks);
  42 + }
293 43 }
294 44
295   -
296 45 #------------------------------------------------------------------------------#
297   -class TestMLActions;
  46 +class TestML::Parser::Actions;
298 47
299 48 ### Base Section ###
300 49
@@ -397,3 +146,4 @@ method data_block($/) {
397 146 }
398 147 $doc.data.blocks.push($block);
399 148 }
  149 +
267 lib/TestML/Parser/Grammar.pm
... ... @@ -0,0 +1,267 @@
  1 +grammar TestML::Parser::Grammar::Base;
  2 +
  3 +regex ALWAYS { <?> } # Always match
  4 +regex ANY { . } # Any unicode character
  5 +regex SPACE { <[\ \t]> } # A space or tab character
  6 +regex BREAK { \n } # A newline character
  7 +regex EOL { \r? \n } # A Unix or DOS line ending
  8 +regex NON_BREAK { \N } # Any character except newline
  9 +regex NON_SPACE_BREAK
  10 + { <![\ \n]> } # Any character except space or newline
  11 +regex LOWER { <[a..z]> } # Lower case ASCII alphabetic character
  12 +regex UPPER { <[A..Z]> } # Upper case ASCII alphabetic character
  13 +regex ALPHANUM { <[A..Za..z0..9]> } # ASCII alphanumeric character
  14 +regex WORD { <[A..Za..z0..9_]> } # A "word" character
  15 +regex DIGIT { <[0..9]> } # A numeric digit
  16 +regex STAR { '*' } # An asterisk
  17 +regex DOT { '.' } # A period character
  18 +regex HASH { '#' } # An octothorpe (or hash) character
  19 +regex BACK { '\\' } # A backslash character
  20 +regex SINGLE { "'" } # A single quote character
  21 +regex DOUBLE { '"' } # A double quote character
  22 +regex ESCAPE { <[0nt]> } # One of the escapable character IDs
  23 +
  24 +token comment { <HASH> <line> }
  25 +token line { <NON_BREAK>* <EOL> }
  26 +token blank_line { <SPACE>* <EOL> }
  27 +token unquoted_string {
  28 + [
  29 + <!before <SPACE>+ <HASH>>
  30 + <!before <SPACE>* <EOL>>
  31 + <ANY>
  32 + ]+
  33 +}
  34 +token single_quoted_string {
  35 + \' ~ \' [ <sq_string> | \\ <sq_escape> ]*
  36 +}
  37 +token sq_string {
  38 + [
  39 + <!before \n>
  40 + <!before \\>
  41 + <!before \'>
  42 + <ANY>
  43 + ]+
  44 +}
  45 +token sq_escape {
  46 + <['\\]>
  47 +}
  48 +token double_quoted_string {
  49 + \" ~ \" [ <dq_string> | \\ <dq_escape> ]*
  50 +}
  51 +token dq_string {
  52 + [
  53 + <!before \t>
  54 + <!before \n>
  55 + <!before \\>
  56 + <!before \">
  57 + <ANY>
  58 + ]+
  59 +}
  60 +token dq_escape {
  61 + <["\\nrt]>
  62 +}
  63 +
  64 +
  65 +#------------------------------------------------------------------------------#
  66 +grammar TestML::Parser::Grammar is TestML::Parser::Grammar::Base;
  67 +
  68 +rule TOP {^ <document> $}
  69 +
  70 +rule document {
  71 + <meta_section> <test_section> <data_section>?
  72 +}
  73 +
  74 +#------------------------------------------------------------------------------#
  75 +rule meta_section {
  76 + [ <comment> | <blank_line> ]*
  77 + <meta_testml_statement>
  78 + [ <meta_statement> | <comment> | <blank_line> ]*
  79 +}
  80 +
  81 +regex meta_testml_statement {
  82 + '%TestML:' <SPACE>+ <testml_version>
  83 + [ <SPACE>+ <comment> | <EOL> ]
  84 +}
  85 +
  86 +token testml_version { <.DIGIT> <.DOT> <.DIGIT>+ }
  87 +
  88 +regex meta_statement {
  89 + '%' <meta_keyword> ':' <SPACE>+ <meta_value>
  90 + [ <SPACE>+ <comment> | <EOL> ]
  91 +}
  92 +
  93 +token meta_keyword {
  94 + <core_meta_keyword> | <user_meta_keyword>
  95 +}
  96 +
  97 +token core_meta_keyword {
  98 + Title | Data | Plan | BlockMarker | PointMarker
  99 +}
  100 +
  101 +token user_meta_keyword {
  102 + <LOWER> <WORD>*
  103 +}
  104 +
  105 +token meta_value {
  106 + <quoted_string> | <unquoted_string>
  107 +}
  108 +
  109 +token quoted_string {
  110 + <single_quoted_string> | <double_quoted_string>
  111 +}
  112 +
  113 +
  114 +#------------------------------------------------------------------------------#
  115 +token test_section {
  116 + [ <wspace> | <test_statement> ]*
  117 +}
  118 +
  119 +token wspace {
  120 + <SPACE> | <EOL> | <comment>
  121 +}
  122 +
  123 +token test_statement {
  124 + <test_statement_start>
  125 + <test_expression> <assertion_expression>? ';'
  126 +}
  127 +
  128 +token test_statement_start {
  129 + <ALWAYS>
  130 +}
  131 +
  132 +token test_expression {
  133 + <sub_expression>
  134 + [
  135 + <!assertion_call>
  136 + <call_indicator>
  137 + <sub_expression>
  138 + ]*
  139 +}
  140 +
  141 +token sub_expression {
  142 + <transform_call> | <data_point> | <quoted_string> | <constant>
  143 +}
  144 +
  145 +token transform_call {
  146 + <transform_name> '(' <wspace>* <argument_list> <wspace>* ')'
  147 +}
  148 +
  149 +token transform_name {
  150 + <user_transform> | <core_transform>
  151 +}
  152 +
  153 +token user_transform {
  154 + <LOWER> <WORD>*
  155 +}
  156 +
  157 +token core_transform {
  158 + <UPPER> <WORD>*
  159 +}
  160 +
  161 +token call_indicator {
  162 + <DOT> <wspace>* | <wspace>* <DOT>
  163 +}
  164 +
  165 +token data_point {
  166 + <.STAR> ( <.LOWER> <.WORD>* )
  167 +}
  168 +
  169 +token constant {
  170 + <UPPER> <WORD>*
  171 +}
  172 +
  173 +token argument_list {
  174 + [ <argument> [ <wspace>* ',' <wspace>* <argument> ]* ]?
  175 +}
  176 +
  177 +token argument {
  178 + <sub_expression>
  179 +}
  180 +
  181 +token assertion_expression {
  182 + <assertion_operation> | <assertion_call>
  183 +}
  184 +
  185 +token assertion_operation {
  186 + <wspace>+ <assertion_operator> <wspace>+ <test_expression>
  187 +}
  188 +
  189 +token assertion_operator {
  190 + '=='
  191 +}
  192 +
  193 +token assertion_call {
  194 + <call_indicator> <assertion_name>
  195 + '(' <wspace>* <test_expression> <wspace>* ')'
  196 +}
  197 +
  198 +token assertion_name {
  199 + <UPPER>+
  200 +}
  201 +
  202 +token data_section {
  203 + <ANY>*
  204 +}
  205 +
  206 +
  207 +#------------------------------------------------------------------------------#
  208 +grammar TestML::Parser::Grammar::DataSection is TestML::Parser::Grammar::Base;
  209 +
  210 +token TOP { ^ <data_section> $ }
  211 +
  212 +token data_section {
  213 + <data_block>*
  214 +}
  215 +
  216 +token data_block {
  217 + <block_header> [ <blank_line> | <comment> ]* <block_point>*
  218 +}
  219 +
  220 +token block_header {
  221 + <block_marker> [ <SPACE>+ <block_label> ]? <SPACE>* <EOL>
  222 +}
  223 +
  224 +token block_marker {
  225 + '==='
  226 +}
  227 +
  228 +token block_label {
  229 +# [ <ANY> - [ <SPACE> | <BREAK> ] ]
  230 +# [ <NON_BREAK>* [ <ANY> - [ <SPACE> | <BREAK> ] ] ]?
  231 + <unquoted_string>
  232 +}
  233 +
  234 +token block_point {
  235 + <lines_point> | <phrase_point>
  236 +}
  237 +
  238 +token lines_point {
  239 + <point_marker> <SPACE>+ <point_name> <SPACE>* <EOL>
  240 + ([
  241 + <!before <block_marker>>
  242 + <!before <point_marker>>
  243 + <line>
  244 + ]*)
  245 +}
  246 +
  247 +token phrase_point {
  248 + <point_marker> <SPACE>+ <point_name> ':' <SPACE>+
  249 + (<unquoted_string>) <SPACE>* <EOL>
  250 + [<blank_line> | <comment>]*
  251 +}
  252 +
  253 +token point_marker {
  254 + '---'
  255 +}
  256 +
  257 +token point_name {
  258 + <core_point_name> | <user_point_name>
  259 +}
  260 +
  261 +token core_point_name {
  262 + <UPPER> <WORD>*
  263 +}
  264 +
  265 +token user_point_name {
  266 + <LOWER> <WORD>*
  267 +}
39 lib/TestML/Runner.pm
... ... @@ -1,8 +1,8 @@
  1 +use v6;
1 2 class TestML::Runner;
  3 +
2 4 class TestML::Context { ... }
3   -use v6;
4 5
5   -use TestML::Document;
6 6 use TestML::Parser;
7 7
8 8 has $.bridge;
@@ -100,28 +100,22 @@ method evaluate_expression ($expression, $block) {
100 100 return $context;
101 101 }
102 102
  103 +method get_transform_function ($name) {
  104 + my @modules = self.transform_modules;
  105 + for @modules -> $module_name {
  106 + my $function = eval "&$module_name" ~ "::$name";
  107 + return $function if $function;
  108 + }
  109 + die "Can't locate function '$name'";
  110 +}
  111 +
103 112 method parse () {
104 113 my $testml = slurp join '/', self.base, self.document;
105   - my $document = Parser.parse($testml)
  114 + my $document = TestML::Parser.parse($testml)
106 115 or die "TestML document failed to parse";
107 116 return $document;
108 117 }
109 118
110   -method parse_data ($parser) {
111   - my $builder = $parser.receiver;
112   - my $document = $builder.document;
113   - for $document.meta.data<Data> -> $file {
114   - if $file eq '_' {
115   - $parser.stream($builder.inline_data);
116   - }
117   - else {
118   - $parser.open("self.base/$file");
119   - }
120   - $parser.parse;
121   - $document.data.blocks.push(|$parser.receiver.blocks);
122   - }
123   -}
124   -
125 119 method init_transform_modules() {
126 120 my @modules = (
127 121 self.bridge,
@@ -136,15 +130,6 @@ method init_transform_modules() {
136 130 return @modules;
137 131 }
138 132
139   -method get_transform_function ($name) {
140   - my @modules = self.transform_modules;
141   - for @modules -> $module_name {
142   - my $function = eval "&$module_name" ~ "::$name";
143   - return $function if $function;
144   - }
145   - die "Can't locate function '$name'";
146   -}
147   -
148 133
149 134 class TestML::Context;
150 135
5 lib/TestML/Standard.pm
... ... @@ -1,6 +1,5 @@
1 1 use v6;
2   -
3   -module TestML::Standard {
  2 +module TestML::Standard;
4 3
5 4 # sub Select () {
6 5 # return (shift).value;
@@ -101,5 +100,3 @@ our sub Item ($this) {
101 100 # return $string.chomp;
102 101 # }
103 102 #
104   -
105   -}
1  t/basic.t
... ... @@ -1,4 +1,3 @@
1   -BEGIN { @*INC.unshift: 't', 'lib'; }
2 1 use TestML::Runner::TAP;
3 2
4 3 TestML::Runner::TAP.new(
3  t/parse-testml-document.t
@@ -2,7 +2,6 @@ use v6;
2 2 use Test;
3 3 plan 22;
4 4
5   -BEGIN { @*INC.unshift: 'lib' }
6 5 use TestML::Parser;
7 6
8 7 my $testml = '
@@ -23,7 +22,7 @@ my $testml = '
23 22 ';
24 23
25 24 try {
26   - my $match = Parser.parse($testml);
  25 + my $match = TestML::Parser.parse($testml);
27 26 ok $match, 'TestML string matches against TestML grammar';
28 27 is $match.meta.data<TestML>, '1.0', 'Version parses';
29 28 is $match.meta.data<Plan>, '2', 'Plan parses';
1  t/t1.t
... ... @@ -1,4 +1,3 @@
1   -BEGIN { @*INC.unshift: 't', 'lib'; }
2 1 use TestML::Runner::TAP;
3 2
4 3 TestML::Runner::TAP.new(

0 comments on commit edb41cf

Please sign in to comment.
Something went wrong with that request. Please try again.