1<?php
2/**
3* @version $Id: lexer.todo.php,v 1.2 2005/03/25 21:00:22 harryf Exp $
4* @package Doku
5* @subpackage Tests
6*/
7
8use dokuwiki\Parsing\Lexer\Lexer;
9use dokuwiki\Parsing\Lexer\ParallelRegex;
10use dokuwiki\Parsing\Lexer\StateStack;
11
12/**
13* @package Doku
14* @subpackage Tests
15*/
16class TestOfLexerParallelRegex extends DokuWikiTest {
17
18    function testNoPatterns() {
19        $regex = new ParallelRegex(false);
20        $this->assertFalse($regex->apply("Hello", $match));
21        $this->assertEquals($match, "");
22    }
23    function testNoSubject() {
24        $regex = new ParallelRegex(false);
25        $regex->addPattern(".*");
26        $this->assertTrue($regex->apply("", $match));
27        $this->assertEquals($match, "");
28    }
29    function testMatchAll() {
30        $regex = new ParallelRegex(false);
31        $regex->addPattern(".*");
32        $this->assertTrue($regex->apply("Hello", $match));
33        $this->assertEquals($match, "Hello");
34    }
35    function testCaseSensitive() {
36        $regex = new ParallelRegex(true);
37        $regex->addPattern("abc");
38        $this->assertTrue($regex->apply("abcdef", $match));
39        $this->assertEquals($match, "abc");
40        $this->assertTrue($regex->apply("AAABCabcdef", $match));
41        $this->assertEquals($match, "abc");
42    }
43    function testCaseInsensitive() {
44        $regex = new ParallelRegex(false);
45        $regex->addPattern("abc");
46        $this->assertTrue($regex->apply("abcdef", $match));
47        $this->assertEquals($match, "abc");
48        $this->assertTrue($regex->apply("AAABCabcdef", $match));
49        $this->assertEquals($match, "ABC");
50    }
51    function testMatchMultiple() {
52        $regex = new ParallelRegex(true);
53        $regex->addPattern("abc");
54        $regex->addPattern("ABC");
55        $this->assertTrue($regex->apply("abcdef", $match));
56        $this->assertEquals($match, "abc");
57        $this->assertTrue($regex->apply("AAABCabcdef", $match));
58        $this->assertEquals($match, "ABC");
59        $this->assertFalse($regex->apply("Hello", $match));
60    }
61    function testPatternLabels() {
62        $regex = new ParallelRegex(false);
63        $regex->addPattern("abc", "letter");
64        $regex->addPattern("123", "number");
65        $this->assertEquals($regex->apply("abcdef", $match), "letter");
66        $this->assertEquals($match, "abc");
67        $this->assertEquals($regex->apply("0123456789", $match), "number");
68        $this->assertEquals($match, "123");
69    }
70    function testMatchMultipleWithLookaheadNot() {
71        $regex = new ParallelRegex(true);
72        $regex->addPattern("abc");
73        $regex->addPattern("ABC");
74        $regex->addPattern("a(?!\n).{1}");
75        $this->assertTrue($regex->apply("abcdef", $match));
76        $this->assertEquals($match, "abc");
77        $this->assertTrue($regex->apply("AAABCabcdef", $match));
78        $this->assertEquals($match, "ABC");
79        $this->assertTrue($regex->apply("a\nab", $match));
80        $this->assertEquals($match, "ab");
81        $this->assertFalse($regex->apply("Hello", $match));
82    }
83    function testMatchSetOptionCaseless() {
84        $regex = new ParallelRegex(true);
85        $regex->addPattern("a(?i)b(?i)c");
86        $this->assertTrue($regex->apply("aBc", $match));
87        $this->assertEquals($match, "aBc");
88    }
89    function testMatchSetOptionUngreedy() {
90        $regex = new ParallelRegex(true);
91        $regex->addPattern("(?U)\w+");
92        $this->assertTrue($regex->apply("aaaaaa", $match));
93        $this->assertEquals($match, "a");
94    }
95    function testMatchLookaheadEqual() {
96        $regex = new ParallelRegex(true);
97        $regex->addPattern("\w(?=c)");
98        $this->assertTrue($regex->apply("xbyczd", $match));
99        $this->assertEquals($match, "y");
100    }
101    function testMatchLookaheadNot() {
102        $regex = new ParallelRegex(true);
103        $regex->addPattern("\w(?!b|c)");
104        $this->assertTrue($regex->apply("xbyczd", $match));
105        $this->assertEquals($match, "b");
106    }
107    function testMatchLookbehindEqual() {
108        $regex = new ParallelRegex(true);
109        $regex->addPattern("(?<=c)\w");
110        $this->assertTrue($regex->apply("xbyczd", $match));
111        $this->assertEquals($match, "z");
112    }
113    function testMatchLookbehindNot() {
114        $regex = new ParallelRegex(true);
115        $regex->addPattern("(?<!\A|x|b)\w");
116        $this->assertTrue($regex->apply("xbyczd", $match));
117        $this->assertEquals($match, "c");
118    }
119}
120
121
122class TestOfLexerStateStack extends DokuWikiTest {
123    function testStartState() {
124        $stack = new StateStack("one");
125        $this->assertEquals($stack->getCurrent(), "one");
126    }
127    function testExhaustion() {
128        $stack = new StateStack("one");
129        $this->assertFalse($stack->leave());
130    }
131    function testStateMoves() {
132        $stack = new StateStack("one");
133        $stack->enter("two");
134        $this->assertEquals($stack->getCurrent(), "two");
135        $stack->enter("three");
136        $this->assertEquals($stack->getCurrent(), "three");
137        $this->assertTrue($stack->leave());
138        $this->assertEquals($stack->getCurrent(), "two");
139        $stack->enter("third");
140        $this->assertEquals($stack->getCurrent(), "third");
141        $this->assertTrue($stack->leave());
142        $this->assertTrue($stack->leave());
143        $this->assertEquals($stack->getCurrent(), "one");
144    }
145}
146
147class TestParser {
148    function __construct() {
149    }
150    function accept() {
151    }
152    function a() {
153    }
154    function b() {
155    }
156}
157
158class TestOfLexer extends DokuWikiTest {
159    function testNoPatterns() {
160        $handler = $this->createMock('TestParser');
161        $handler->expects($this->never())->method('accept');
162        $lexer = new Lexer($handler);
163        $this->assertFalse($lexer->parse("abcdef"));
164    }
165    function testEmptyPage() {
166        $handler = $this->createMock('TestParser');
167        $handler->expects($this->never())->method('accept');
168        $lexer = new Lexer($handler);
169        $lexer->addPattern("a+");
170        $this->assertTrue($lexer->parse(""));
171    }
172    function testSinglePattern() {
173        $acceptArguments = [
174            ["aaa", DOKU_LEXER_MATCHED, 0],
175            ["x", DOKU_LEXER_UNMATCHED, 3],
176            ["a", DOKU_LEXER_MATCHED, 4],
177            ["yyy", DOKU_LEXER_UNMATCHED, 5],
178            ["a", DOKU_LEXER_MATCHED, 8],
179            ["x", DOKU_LEXER_UNMATCHED, 9],
180            ["aaa", DOKU_LEXER_MATCHED, 10],
181            ["z", DOKU_LEXER_UNMATCHED, 13],
182        ];
183        $acceptArgumentCount = count($acceptArguments);
184
185        $handler = $this->createMock('TestParser');
186        $handler
187            ->expects($this->exactly($acceptArgumentCount))
188            ->method('accept')
189            ->withConsecutive(...$acceptArguments)
190            ->willReturnOnConsecutiveCalls(...array_fill(0, $acceptArgumentCount, true));
191
192        $lexer = new Lexer($handler);
193        $lexer->addPattern("a+");
194        $this->assertTrue($lexer->parse("aaaxayyyaxaaaz"));
195    }
196    function testMultiplePattern() {
197        $acceptArguments = [
198            ["a", $this->anything(), 0],
199            ["b", $this->anything(), 1],
200            ["a", $this->anything(), 2],
201            ["bb", $this->anything(), 3],
202            ["x", $this->anything(), 5],
203            ["b", $this->anything(), 6],
204            ["a", $this->anything(), 7],
205            ["xxxxxx", $this->anything(), 8],
206            ["a", $this->anything(), 14],
207            ["x", $this->anything(), 15],
208        ];
209        $acceptArgumentCount = count($acceptArguments);
210
211        $handler = $this->createPartialMock('TestParser', ['accept']);
212        $handler
213            ->expects($this->exactly($acceptArgumentCount))
214            ->method('accept')
215            ->withConsecutive(...$acceptArguments)
216            ->willReturnOnConsecutiveCalls(...array_fill(0, $acceptArgumentCount, true));
217
218        $lexer = new Lexer($handler);
219        $lexer->addPattern("a+");
220        $lexer->addPattern("b+");
221        $this->assertTrue($lexer->parse("ababbxbaxxxxxxax"));
222    }
223}
224
225class TestOfLexerModes extends DokuWikiTest {
226    function testIsolatedPattern() {
227        $aArguments = [
228            ["a", DOKU_LEXER_MATCHED, 0],
229            ["b", DOKU_LEXER_UNMATCHED, 1],
230            ["aa", DOKU_LEXER_MATCHED, 2],
231            ["bxb", DOKU_LEXER_UNMATCHED, 4],
232            ["aaa", DOKU_LEXER_MATCHED, 7],
233            ["x", DOKU_LEXER_UNMATCHED, 10],
234            ["aaaa", DOKU_LEXER_MATCHED, 11],
235            ["x", DOKU_LEXER_UNMATCHED, 15],
236        ];
237        $aArgumentCount = count($aArguments);
238
239        $handler = $this->createMock('TestParser');
240        $handler
241            ->expects($this->exactly($aArgumentCount))
242            ->method('a')
243            ->withConsecutive(...$aArguments)
244            ->willReturnOnConsecutiveCalls(...array_fill(0, $aArgumentCount, true));
245
246        $lexer = new Lexer($handler, "a");
247        $lexer->addPattern("a+", "a");
248        $lexer->addPattern("b+", "b");
249        $this->assertTrue($lexer->parse("abaabxbaaaxaaaax"));
250    }
251    function testModeChange() {
252        $methodArguments = [
253            'a' => [
254                ["a", DOKU_LEXER_MATCHED, 0],
255                ["b", DOKU_LEXER_UNMATCHED, 1],
256                ["aa", DOKU_LEXER_MATCHED, 2],
257                ["b", DOKU_LEXER_UNMATCHED, 4],
258                ["aaa", DOKU_LEXER_MATCHED, 5],
259            ],
260            'b' => [
261                [":", DOKU_LEXER_ENTER, 8],
262                ["a", DOKU_LEXER_UNMATCHED, 9],
263                ["b", DOKU_LEXER_MATCHED, 10],
264                ["a", DOKU_LEXER_UNMATCHED, 11],
265                ["bb", DOKU_LEXER_MATCHED, 12],
266                ["a", DOKU_LEXER_UNMATCHED, 14],
267                ["bbb", DOKU_LEXER_MATCHED, 15],
268                ["a", DOKU_LEXER_UNMATCHED, 18],
269            ],
270        ];
271
272        $handler = $this->createMock('TestParser');
273        foreach ($methodArguments as $method => $arguments) {
274            $count = count($arguments);
275            $handler
276                ->expects($this->exactly($count))
277                ->method($method)
278                ->withConsecutive(...$arguments)
279                ->willReturnOnConsecutiveCalls(...array_fill(0, $count, true));
280        }
281
282        $lexer = new Lexer($handler, "a");
283        $lexer->addPattern("a+", "a");
284        $lexer->addEntryPattern(":", "a", "b");
285        $lexer->addPattern("b+", "b");
286        $this->assertTrue($lexer->parse("abaabaaa:ababbabbba"));
287    }
288    function testNesting() {
289        $methodArguments = [
290            'a' => [
291                ["aa", DOKU_LEXER_MATCHED, 0],
292                ["b", DOKU_LEXER_UNMATCHED, 2],
293                ["aa", DOKU_LEXER_MATCHED, 3],
294                ["b", DOKU_LEXER_UNMATCHED, 5],
295                // some b calls in between here
296                ["aa", DOKU_LEXER_MATCHED, 13],
297                ["b", DOKU_LEXER_UNMATCHED, 15],
298            ],
299            'b' => [
300                ["(", DOKU_LEXER_ENTER, 6],
301                ["bb", DOKU_LEXER_MATCHED, 7],
302                ["a", DOKU_LEXER_UNMATCHED, 9],
303                ["bb", DOKU_LEXER_MATCHED, 10],
304                [")", DOKU_LEXER_EXIT, 12],
305            ],
306        ];
307
308        $handler = $this->createMock('TestParser');
309        foreach ($methodArguments as $method => $arguments) {
310            $count = count($arguments);
311            $handler
312                ->expects($this->exactly($count))
313                ->method($method)
314                ->withConsecutive(...$arguments)
315                ->willReturnOnConsecutiveCalls(...array_fill(0, $count, true));
316        }
317
318        $lexer = new Lexer($handler, "a");
319        $lexer->addPattern("a+", "a");
320        $lexer->addEntryPattern("(", "a", "b");
321        $lexer->addPattern("b+", "b");
322        $lexer->addExitPattern(")", "b");
323        $this->assertTrue($lexer->parse("aabaab(bbabb)aab"));
324    }
325    function testSingular() {
326        $methodArguments = [
327            'a' => [
328                ["aa", DOKU_LEXER_MATCHED, 0],
329                ["aa", DOKU_LEXER_MATCHED, 3],
330                ["xx", DOKU_LEXER_UNMATCHED, 5],
331                ["xx", DOKU_LEXER_UNMATCHED, 10],
332            ],
333            'b' => [
334                ["b", DOKU_LEXER_SPECIAL, 2],
335                ["bbb", DOKU_LEXER_SPECIAL, 7],
336            ],
337        ];
338
339        $handler = $this->createMock('TestParser');
340        foreach ($methodArguments as $method => $arguments) {
341            $count = count($arguments);
342            $handler
343                ->expects($this->exactly($count))
344                ->method($method)
345                ->withConsecutive(...$arguments)
346                ->willReturnOnConsecutiveCalls(...array_fill(0, $count, true));
347        }
348
349        $lexer = new Lexer($handler, "a");
350        $lexer->addPattern("a+", "a");
351        $lexer->addSpecialPattern("b+", "a", "b");
352        $this->assertTrue($lexer->parse("aabaaxxbbbxx"));
353    }
354    function testUnwindTooFar() {
355        $aArguments = [
356            ["aa", DOKU_LEXER_MATCHED,0],
357            [")", DOKU_LEXER_EXIT,2],
358        ];
359        $aArgumentCount = count($aArguments);
360
361        $handler = $this->createMock('TestParser');
362        $handler
363            ->expects($this->exactly($aArgumentCount))
364            ->method('a')
365            ->withConsecutive(...$aArguments)
366            ->willReturnOnConsecutiveCalls(...array_fill(0, $aArgumentCount, true));
367
368        $lexer = new Lexer($handler, "a");
369        $lexer->addPattern("a+", "a");
370        $lexer->addExitPattern(")", "a");
371        $this->assertFalse($lexer->parse("aa)aa"));
372    }
373}
374
375class TestOfLexerHandlers extends DokuWikiTest {
376    function testModeMapping() {
377        $aArguments = [
378            ["aa", DOKU_LEXER_MATCHED, 0],
379            ["(", DOKU_LEXER_ENTER, 2],
380            ["bb", DOKU_LEXER_MATCHED, 3],
381            ["a", DOKU_LEXER_UNMATCHED, 5],
382            ["bb", DOKU_LEXER_MATCHED, 6],
383            [")", DOKU_LEXER_EXIT, 8],
384            ["b", DOKU_LEXER_UNMATCHED, 9],
385        ];
386        $aArgumentCount = count($aArguments);
387
388        $handler = $this->createMock('TestParser');
389        $handler
390            ->expects($this->exactly($aArgumentCount))
391            ->method('a')
392            ->withConsecutive(...$aArguments)
393            ->willReturnOnConsecutiveCalls(...array_fill(0, $aArgumentCount, true));
394
395        $lexer = new Lexer($handler, "mode_a");
396        $lexer->addPattern("a+", "mode_a");
397        $lexer->addEntryPattern("(", "mode_a", "mode_b");
398        $lexer->addPattern("b+", "mode_b");
399        $lexer->addExitPattern(")", "mode_b");
400        $lexer->mapHandler("mode_a", "a");
401        $lexer->mapHandler("mode_b", "a");
402        $this->assertTrue($lexer->parse("aa(bbabb)b"));
403    }
404}
405
406class TestParserByteIndex {
407
408    function __construct() {}
409
410    function ignore() {}
411
412    function caught() {}
413}
414
415class TestOfLexerByteIndices extends DokuWikiTest {
416
417    function testIndex() {
418        $doc = "aaa<file>bcd</file>eee";
419
420        $caughtArguments = [
421            ["<file>", DOKU_LEXER_ENTER, strpos($doc, '<file>')],
422            ["b", DOKU_LEXER_SPECIAL, strpos($doc, 'b')],
423            ["c", DOKU_LEXER_MATCHED, strpos($doc, 'c')],
424            ["d", DOKU_LEXER_UNMATCHED, strpos($doc, 'd')],
425            ["</file>", DOKU_LEXER_EXIT, strpos($doc, '</file>')],
426        ];
427        $caughtArgumentCount = count($caughtArguments);
428
429        $handler = $this->createMock('TestParserByteIndex');
430        $handler->expects($this->any())->method('ignore')->will($this->returnValue(true));
431        $handler
432            ->expects($this->exactly($caughtArgumentCount))
433            ->method('caught')
434            ->withConsecutive(...$caughtArguments)
435            ->willReturnOnConsecutiveCalls(...array_fill(0, $caughtArgumentCount, true));
436
437        $lexer = new Lexer($handler, "ignore");
438        $lexer->addEntryPattern("<file>", "ignore", "caught");
439        $lexer->addExitPattern("</file>", "caught");
440        $lexer->addSpecialPattern('b','caught','special');
441        $lexer->mapHandler('special','caught');
442        $lexer->addPattern('c','caught');
443
444        $this->assertTrue($lexer->parse($doc));
445    }
446
447    function testIndexLookaheadEqual() {
448        $doc = "aaa<file>bcd</file>eee";
449
450        $caughtArguments = [
451            ["<file>", DOKU_LEXER_ENTER, strpos($doc, '<file>')],
452            ["b", DOKU_LEXER_SPECIAL, strpos($doc, 'b')],
453            ["c", DOKU_LEXER_MATCHED, strpos($doc, 'c')],
454            ["d", DOKU_LEXER_UNMATCHED, strpos($doc, 'd')],
455            ["</file>", DOKU_LEXER_EXIT, strpos($doc, '</file>')],
456        ];
457        $caughtArgumentCount = count($caughtArguments);
458
459        $handler = $this->createMock('TestParserByteIndex');
460        $handler->expects($this->any())->method('ignore')->will($this->returnValue(true));
461        $handler
462            ->expects($this->exactly($caughtArgumentCount))
463            ->method('caught')
464            ->withConsecutive(...$caughtArguments)
465            ->willReturnOnConsecutiveCalls(...array_fill(0, $caughtArgumentCount, true));
466
467        $lexer = new Lexer($handler, "ignore");
468        $lexer->addEntryPattern('<file>(?=.*</file>)', "ignore", "caught");
469        $lexer->addExitPattern("</file>", "caught");
470        $lexer->addSpecialPattern('b','caught','special');
471        $lexer->mapHandler('special','caught');
472        $lexer->addPattern('c','caught');
473
474        $this->assertTrue($lexer->parse($doc));
475    }
476
477    function testIndexLookaheadNotEqual() {
478        $doc = "aaa<file>bcd</file>eee";
479
480        $caughtArguments = [
481            ["<file>", DOKU_LEXER_ENTER, strpos($doc, '<file>')],
482            ["b", DOKU_LEXER_SPECIAL, strpos($doc, 'b')],
483            ["c", DOKU_LEXER_MATCHED, strpos($doc, 'c')],
484            ["d", DOKU_LEXER_UNMATCHED, strpos($doc, 'd')],
485            ["</file>", DOKU_LEXER_EXIT, strpos($doc, '</file>')],
486        ];
487        $caughtArgumentCount = count($caughtArguments);
488
489        $handler = $this->createMock('TestParserByteIndex');
490        $handler->expects($this->any())->method('ignore')->will($this->returnValue(true));
491        $handler
492            ->expects($this->exactly($caughtArgumentCount))
493            ->method('caught')
494            ->withConsecutive(...$caughtArguments)
495            ->willReturnOnConsecutiveCalls(...array_fill(0, $caughtArgumentCount, true));
496
497        $lexer = new Lexer($handler, "ignore");
498        $lexer->addEntryPattern('<file>(?!foo)', "ignore", "caught");
499        $lexer->addExitPattern("</file>", "caught");
500        $lexer->addSpecialPattern('b','caught','special');
501        $lexer->mapHandler('special','caught');
502        $lexer->addPattern('c','caught');
503
504        $this->assertTrue($lexer->parse($doc));
505    }
506
507    function testIndexLookbehindEqual() {
508        $doc = "aaa<file>bcd</file>eee";
509
510        $caughtArguments = [
511            ["<file>", DOKU_LEXER_ENTER, strpos($doc, '<file>')],
512            ["b", DOKU_LEXER_SPECIAL, strpos($doc, 'b')],
513            ["c", DOKU_LEXER_MATCHED, strpos($doc, 'c')],
514            ["d", DOKU_LEXER_UNMATCHED, strpos($doc, 'd')],
515            ["</file>", DOKU_LEXER_EXIT, strpos($doc, '</file>')],
516        ];
517        $caughtArgumentCount = count($caughtArguments);
518
519        $handler = $this->createMock('TestParserByteIndex');
520        $handler->expects($this->any())->method('ignore')->will($this->returnValue(true));
521        $handler
522            ->expects($this->exactly($caughtArgumentCount))
523            ->method('caught')
524            ->withConsecutive(...$caughtArguments)
525            ->willReturnOnConsecutiveCalls(...array_fill(0, $caughtArgumentCount, true));
526
527        $lexer = new Lexer($handler, "ignore");
528        $lexer->addEntryPattern('<file>', "ignore", "caught");
529        $lexer->addExitPattern("(?<=d)</file>", "caught");
530        $lexer->addSpecialPattern('b','caught','special');
531        $lexer->mapHandler('special','caught');
532        $lexer->addPattern('c','caught');
533
534        $this->assertTrue($lexer->parse($doc));
535    }
536
537    function testIndexLookbehindNotEqual() {
538        $doc = "aaa<file>bcd</file>eee";
539
540        $caughtArguments = [
541            ["<file>", DOKU_LEXER_ENTER, strpos($doc, '<file>')],
542            ["b", DOKU_LEXER_SPECIAL, strpos($doc, 'b')],
543            ["c", DOKU_LEXER_MATCHED, strpos($doc, 'c')],
544            ["d", DOKU_LEXER_UNMATCHED, strpos($doc, 'd')],
545            ["</file>", DOKU_LEXER_EXIT, strpos($doc, '</file>')],
546        ];
547        $caughtArgumentCount = count($caughtArguments);
548
549        $handler = $this->createMock('TestParserByteIndex');
550        $handler->expects($this->any())->method('ignore')->will($this->returnValue(true));
551        $handler
552            ->expects($this->exactly($caughtArgumentCount))
553            ->method('caught')
554            ->withConsecutive(...$caughtArguments)
555            ->willReturnOnConsecutiveCalls(...array_fill(0, $caughtArgumentCount, true));
556
557        $lexer = new Lexer($handler, 'ignore');
558        $lexer->addEntryPattern('<file>', 'ignore', 'caught');
559        $lexer->addExitPattern('(?<!c)</file>', 'caught');
560        $lexer->addSpecialPattern('b','caught','special');
561        $lexer->mapHandler('special','caught');
562        $lexer->addPattern('c','caught');
563
564        $this->assertTrue($lexer->parse($doc));
565    }
566
567    /**
568     * This test is primarily to ensure the correct match is chosen
569     * when there are non-captured elements in the pattern.
570     */
571    function testIndexSelectCorrectMatch() {
572        $doc = "ALL FOOLS ARE FOO";
573        $pattern = '\bFOO\b';
574
575        $handler = $this->createMock('TestParserByteIndex');
576        $handler->expects($this->any())->method('ignore')->will($this->returnValue(true));
577
578        $matches = [];
579        preg_match('/'.$pattern.'/',$doc,$matches,PREG_OFFSET_CAPTURE);
580
581        $handler->expects($this->once())->method('caught')
582            ->with("FOO", DOKU_LEXER_SPECIAL, $matches[0][1])->will($this->returnValue(true));
583
584        $lexer = new Lexer($handler, "ignore");
585        $lexer->addSpecialPattern($pattern,'ignore','caught');
586
587        $this->assertTrue($lexer->parse($doc));
588    }
589
590}
591