1 #!/usr/bin/env python2
2 """
3 cmd_parse_test.py: Tests for cmd_parse.py
4 """
5
6 import unittest
7
8 from _devbuild.gen.id_kind_asdl import Id, Id_str
9 from _devbuild.gen.syntax_asdl import command_e, for_iter_e, pat_e
10 from core import error
11 from core import state
12 from core import test_lib
13 from core import ui
14
15 from osh import word_
16
17
18 def _assertParseMethod(test, code_str, method, expect_success=True):
19 arena = test_lib.MakeArena('<cmd_parse_test>')
20 errfmt = ui.ErrorFormatter()
21 c_parser = test_lib.InitCommandParser(code_str, arena=arena)
22 m = getattr(c_parser, method)
23 try:
24 node = m()
25
26 except error.Parse as e:
27 errfmt.PrettyPrintError(e)
28 if expect_success:
29 test.fail('%r failed' % code_str)
30 node = None
31 else:
32 node.PrettyPrint()
33 if not expect_success:
34 test.fail('Expected %r to fail ' % code_str)
35
36 return node
37
38
39 def _assert_ParseCommandListError(test, code_str):
40 arena = test_lib.MakeArena('<cmd_parse_test>')
41 errfmt = ui.ErrorFormatter()
42 c_parser = test_lib.InitCommandParser(code_str, arena=arena)
43
44 try:
45 node = c_parser._ParseCommandLine()
46 except error.Parse as e:
47 errfmt.PrettyPrintError(e)
48 else:
49 print('UNEXPECTED:')
50 node.PrettyPrint()
51 test.fail("Expected %r to fail" % code_str)
52
53
54 #
55 # Successes
56 #
57 # (These differences might not matter, but preserve the diversity for now)
58
59
60 def assertParseSimpleCommand(test, code_str):
61 return _assertParseMethod(test, code_str, 'ParseSimpleCommand')
62
63
64 def assertParsePipeline(test, code_str):
65 return _assertParseMethod(test, code_str, 'ParsePipeline')
66
67
68 def assertParseAndOr(test, code_str):
69 return _assertParseMethod(test, code_str, 'ParseAndOr')
70
71
72 def assert_ParseCommandLine(test, code_str):
73 return _assertParseMethod(test, code_str, '_ParseCommandLine')
74
75
76 def assert_ParseCommandList(test, code_str):
77 node = _assertParseMethod(test, code_str, '_ParseCommandList')
78 if len(node.children) == 1:
79 return node.children[0]
80 else:
81 return node
82
83
84 def assertParseRedirect(test, code_str):
85 return _assertParseMethod(test, code_str, 'ParseRedirect')
86
87
88 #
89 # Failures
90 #
91
92 #def assertFailSimpleCommand(test, code_str):
93 # return _assertParseMethod(test, code_str, 'ParseSimpleCommand',
94 # expect_success=False)
95 #
96 #def assertFailCommandLine(test, code_str):
97 # return _assertParseMethod(test, code_str, '_ParseCommandLine',
98 # expect_success=False)
99
100
101 def assertFailCommandList(test, code_str):
102 return _assertParseMethod(test,
103 code_str,
104 '_ParseCommandList',
105 expect_success=False)
106
107
108 #def assertFailRedirect(test, code_str):
109 # return _assertParseMethod(test, code_str, 'ParseRedirect',
110 # expect_success=False)
111
112
113 class SimpleCommandTest(unittest.TestCase):
114 def testParseSimpleCommand1(self):
115 node = assertParseSimpleCommand(self, 'ls foo')
116 self.assertEqual(2, len(node.words), node.words)
117
118 node = assertParseSimpleCommand(self, 'FOO=bar ls foo')
119 self.assertEqual(2, len(node.words))
120 self.assertEqual(1, len(node.more_env))
121
122 node = assertParseSimpleCommand(self,
123 'FOO=bar >output.txt SPAM=eggs ls foo')
124 self.assertEqual(2, len(node.words))
125 self.assertEqual(2, len(node.more_env))
126 self.assertEqual(1, len(node.redirects))
127
128 node = assertParseSimpleCommand(
129 self, 'FOO=bar >output.txt SPAM=eggs ls foo >output2.txt')
130 self.assertEqual(2, len(node.words))
131 self.assertEqual(2, len(node.more_env))
132 self.assertEqual(2, len(node.redirects))
133
134 def testMultipleGlobalShAssignments(self):
135 node = assert_ParseCommandList(self, 'ONE=1 TWO=2')
136 self.assertEqual(command_e.ShAssignment, node.tag())
137 self.assertEqual(2, len(node.pairs))
138
139 def testOnlyRedirect(self):
140 # This just touches the file
141 node = assert_ParseCommandList(self, '>out.txt')
142 self.assertEqual(command_e.Simple, node.tag())
143 self.assertEqual(0, len(node.words))
144 self.assertEqual(1, len(node.redirects))
145
146 def testParseRedirectInTheMiddle(self):
147 node = assert_ParseCommandList(self, 'echo >out.txt 1 2 3')
148 self.assertEqual(command_e.Simple, node.tag())
149 self.assertEqual(4, len(node.words))
150 self.assertEqual(1, len(node.redirects))
151
152 def testParseRedirectBeforeShAssignment(self):
153 # Write ENV to a file
154 node = assert_ParseCommandList(self, '>out.txt PYTHONPATH=. env')
155 self.assertEqual(command_e.Simple, node.tag())
156 self.assertEqual(1, len(node.words))
157 self.assertEqual(1, len(node.redirects))
158 self.assertEqual(1, len(node.more_env))
159
160 def testParseAdjacentDoubleQuotedWords(self):
161 node = assertParseSimpleCommand(self,
162 'echo "one"two "three""four" five')
163 self.assertEqual(4, len(node.words))
164
165
166 class OldStaticParsing(object):
167 def testRedirectsInShAssignment(self):
168 err = _assert_ParseCommandListError(self, 'x=1 >/dev/null')
169 err = _assert_ParseCommandListError(self, 'echo hi; x=1 >/dev/null')
170 err = _assert_ParseCommandListError(self, 'declare x=1 >/dev/null')
171
172 def testParseShAssignment(self):
173 node = assert_ParseCommandList(self, 'local foo=bar spam eggs one=1')
174 self.assertEqual(4, len(node.pairs))
175
176 node = assert_ParseCommandList(self, 'foo=bar')
177 self.assertEqual(1, len(node.pairs))
178
179 # This is not valid since env isn't respected
180 assertFailCommandList(self, 'FOO=bar local foo=$(env)')
181
182 def testExport(self):
183 # This is the old static parsing. Probably need to revisit.
184 return
185 node = assert_ParseCommandList(self, 'export ONE=1 TWO=2 THREE')
186 self.assertEqual(command_e.ShAssignment, node.tag())
187 self.assertEqual(3, len(node.pairs))
188
189 def testReadonly(self):
190 return
191 node = assert_ParseCommandList(self, 'readonly ONE=1 TWO=2 THREE')
192 self.assertEqual(command_e.ShAssignment, node.tag())
193 self.assertEqual(3, len(node.pairs))
194
195
196 def assertHereDocToken(test, expected_token_val, node):
197 """A sanity check for some ad hoc tests."""
198 test.assertEqual(1, len(node.redirects))
199 h = node.redirects[0].arg
200 test.assertEqual(expected_token_val, h.stdin_parts[0].tval)
201
202
203 class HereDocTest(unittest.TestCase):
204 """NOTE: These ares come from tests/09-here-doc.sh, but add assertions."""
205
206 def testUnquotedHereDoc(self):
207 # Unquoted here docs use the double quoted context.
208 node = assert_ParseCommandLine(self, """\
209 cat <<EOF
210 $v
211 "two
212 EOF
213 """)
214 self.assertEqual(1, len(node.redirects))
215 h = node.redirects[0].arg
216 # 4 literal parts: VarSub, newline, right ", "two\n"
217 self.assertEqual(4, len(h.stdin_parts))
218
219 def testQuotedHereDocs(self):
220 # Quoted here doc
221 node = assert_ParseCommandLine(self, """\
222 cat <<"EOF"
223 $v
224 "two
225 EOF
226 """)
227 self.assertEqual(1, len(node.redirects))
228 h = node.redirects[0].arg
229 self.assertEqual(2, len(h.stdin_parts)) # 2 literal parts
230
231 node = assert_ParseCommandLine(
232 self, """\
233 cat <<'EOF'
234 single-quoted: $var
235 EOF
236 """)
237 self.assertEqual(1, len(node.redirects))
238 h = node.redirects[0].arg
239 self.assertEqual(1, len(h.stdin_parts)) # 1 line, one literal part
240
241 # \ escape
242 node = assert_ParseCommandLine(
243 self, r"""\
244 cat <<EO\F
245 single-quoted: $var
246 EOF
247 """)
248 self.assertEqual(1, len(node.redirects))
249 h = node.redirects[0].arg
250 self.assertEqual(1, len(h.stdin_parts)) # 1 line, one literal part
251
252 def testLeadingTabs(self):
253 node = assert_ParseCommandLine(
254 self, """\
255 \tcat <<-EOF
256 \tone tab then foo: $foo
257 \tEOF
258 echo hi
259 """)
260 self.assertEqual(node.tag(), command_e.Simple)
261 assertHereDocToken(self, 'one tab then foo: ', node)
262
263 def testHereDocInPipeline(self):
264 # Pipe and command on SAME LINE
265 node = assert_ParseCommandLine(
266 self, """\
267 cat <<EOF | tac
268 PIPE 1
269 PIPE 2
270 EOF
271 """)
272 self.assertEqual(2, len(node.children))
273 assertHereDocToken(self, 'PIPE 1\n', node.children[0])
274
275 # Pipe command AFTER here doc
276 node = assert_ParseCommandLine(
277 self, """\
278 cat <<EOF |
279 PIPE 1
280 PIPE 2
281 EOF
282 tac
283 """)
284 self.assertEqual(2, len(node.children))
285 assertHereDocToken(self, 'PIPE 1\n', node.children[0])
286
287 def testTwoHereDocsInPipeline(self):
288 # Pipeline with two here docs
289 node = assert_ParseCommandList(
290 self, """\
291 cat <<EOF1 | tac <<EOF2
292 PIPE A1
293 PIPE A2
294 EOF1
295 PIPE B1
296 PIPE B2
297 EOF2
298 """)
299 self.assertEqual(2, len(node.children))
300 assertHereDocToken(self, 'PIPE A1\n', node.children[0])
301 assertHereDocToken(self, 'PIPE B1\n', node.children[1])
302
303 def testHereDocInAndOrChain(self):
304 # || command AFTER here doc
305 node = assert_ParseCommandLine(
306 self, """\
307 cat <<EOF ||
308 PIPE 1
309 PIPE 2
310 EOF
311 echo hi
312 """)
313 self.assertEqual(2, len(node.children))
314 assertHereDocToken(self, 'PIPE 1\n', node.children[0])
315
316 # && and command on SAME LINE
317 node = assert_ParseCommandLine(
318 self, """\
319 cat <<EOF && echo hi
320 PIPE 1
321 PIPE 2
322 EOF
323 """)
324 self.assertEqual(2, len(node.children))
325 assertHereDocToken(self, 'PIPE 1\n', node.children[0])
326
327 node = assert_ParseCommandLine(
328 self, """\
329 tac <<EOF1 && tac <<EOF2
330 PIPE A1
331 PIPE A2
332 EOF1
333 PIPE B1
334 PIPE B2
335 EOF2
336 echo
337 """)
338 self.assertEqual(2, len(node.children))
339 assertHereDocToken(self, 'PIPE A1\n', node.children[0])
340 assertHereDocToken(self, 'PIPE B1\n', node.children[1])
341
342 def testHereDocInSequence(self):
343 # PROBLEM: _ParseCommandList vs _ParseCommandLine
344 # _ParseCommandLine only used interactively. _ParseCommandList is used by
345 # ParseFile.
346
347 # command AFTER here doc
348 node = assert_ParseCommandList(
349 self, """\
350 cat <<EOF ;
351 PIPE 1
352 PIPE 2
353 EOF
354 echo hi
355 """)
356 self.assertEqual(node.tag(), command_e.CommandList)
357 self.assertEqual(2, len(node.children), repr(node))
358 assertHereDocToken(self, 'PIPE 1\n', node.children[0].child)
359
360 def testHereDocInSequence2(self):
361 # ; and command on SAME LINE
362 node = assert_ParseCommandList(
363 self, """\
364 cat <<EOF ; echo hi
365 PIPE 1
366 PIPE 2
367 EOF
368 """)
369 self.assertEqual(node.tag(), command_e.CommandList)
370 self.assertEqual(2, len(node.children))
371 assertHereDocToken(self, 'PIPE 1\n', node.children[0].child)
372
373 def testCommandSubInHereDoc(self):
374 node = assert_ParseCommandLine(
375 self, """\
376 cat <<EOF
377 1 $(echo 2
378 echo 3) 4
379 EOF
380 """)
381 self.assertEqual(1, len(node.words))
382 self.assertEqual(1, len(node.redirects))
383
384
385 class ArrayTest(unittest.TestCase):
386 def testArrayLiteral(self):
387 # Empty array
388 node = assert_ParseCommandList(self, 'empty=()')
389 self.assertEqual(['empty'], [p.lhs.name for p in node.pairs])
390 self.assertEqual([], node.pairs[0].rhs.parts[0].words) # No words
391 self.assertEqual(command_e.ShAssignment, node.tag())
392
393 # Array with 3 elements
394 node = assert_ParseCommandList(self, 'array=(a b c)')
395 self.assertEqual(['array'], [p.lhs.name for p in node.pairs])
396 self.assertEqual(3, len(node.pairs[0].rhs.parts[0].words))
397 self.assertEqual(command_e.ShAssignment, node.tag())
398
399 # Array literal can't come after word
400 # Now caught at runtime
401 #assertFailCommandList(self,
402 # 'ls array=(a b c)')
403
404 # Word can't come after array literal
405 assertFailCommandList(self, 'array=(a b c) ls')
406
407 # Two array literals
408 node = assert_ParseCommandList(self, 'array=(a b c); array2=(d e f)')
409 self.assertEqual(2, len(node.children))
410 a2 = node.children[1]
411 self.assertEqual(['array2'], [p.lhs.name for p in a2.pairs])
412
413
414 class RedirectTest(unittest.TestCase):
415 def testParseRedirects1(self):
416 node = assertParseSimpleCommand(self, '>out.txt cat 1>&2')
417 self.assertEqual(1, len(node.words))
418 self.assertEqual(2, len(node.redirects))
419
420 node = assertParseSimpleCommand(self, ' cat <&3')
421 self.assertEqual(1, len(node.redirects))
422
423 def testParseFilenameRedirect(self):
424 node = assertParseRedirect(self, '>out.txt cat')
425
426 def testDescriptorRedirect(self):
427 node = assertParseRedirect(self, '1>& 2 cat')
428
429 def testHereDoc(self):
430 node = assertParseRedirect(self, """\
431 <<EOF cat
432 hi
433 EOF
434 """)
435
436 def testHereDocStrip(self):
437 node = assertParseRedirect(self, """\
438 <<-EOF cat
439 hi
440 EOF
441 """)
442
443 def testParseRedirectList(self):
444 node = assertParseRedirect(self, """\
445 <<EOF >out.txt cat
446 hi
447 EOF
448 """)
449
450 def testParseCommandWithLeadingRedirects(self):
451 node = assertParseSimpleCommand(self, """\
452 <<EOF >out.txt cat
453 hi
454 EOF
455 """)
456 self.assertEqual(1, len(node.words))
457 self.assertEqual(2, len(node.redirects))
458
459 def testClobberRedirect(self):
460 node = assertParseSimpleCommand(self, 'echo hi >| clobbered.txt')
461
462
463 class CommandParserTest(unittest.TestCase):
464 def testParsePipeline(self):
465 node = assertParsePipeline(self, 'ls foo')
466 self.assertEqual(2, len(node.words))
467
468 node = assertParsePipeline(self, 'ls foo|wc -l')
469 self.assertEqual(2, len(node.children))
470 self.assertEqual(command_e.Pipeline, node.tag())
471
472 node = assertParsePipeline(self, '! echo foo | grep foo')
473 self.assertEqual(2, len(node.children))
474 self.assertEqual(command_e.Pipeline, node.tag())
475 self.assertTrue(node.negated)
476
477 node = assertParsePipeline(self, 'ls foo|wc -l|less')
478 self.assertEqual(3, len(node.children))
479 self.assertEqual(command_e.Pipeline, node.tag())
480
481 _assertParseMethod(self,
482 'ls foo|',
483 'ParsePipeline',
484 expect_success=False)
485
486 def testParsePipelineBash(self):
487 node = assert_ParseCommandList(self, 'ls | cat |& cat')
488 self.assertEqual(command_e.Pipeline, node.tag())
489 self.assertEqual(2, len(node.ops))
490 self.assertEqual(Id.Op_Pipe, node.ops[0].id)
491 self.assertEqual(Id.Op_PipeAmp, node.ops[1].id)
492
493 node = assert_ParseCommandList(self, 'ls |& cat | cat')
494 self.assertEqual(command_e.Pipeline, node.tag())
495 self.assertEqual(2, len(node.ops))
496 self.assertEqual(Id.Op_PipeAmp, node.ops[0].id)
497 self.assertEqual(Id.Op_Pipe, node.ops[1].id)
498
499 node = assert_ParseCommandList(self, 'ls |& cat |& cat')
500 self.assertEqual(command_e.Pipeline, node.tag())
501 self.assertEqual(2, len(node.ops))
502 self.assertEqual(Id.Op_PipeAmp, node.ops[0].id)
503 self.assertEqual(Id.Op_PipeAmp, node.ops[1].id)
504
505 def testParseAndOr(self):
506 node = assertParseAndOr(self, 'ls foo')
507 self.assertEqual(2, len(node.words))
508
509 node = assertParseAndOr(self, 'ls foo|wc -l')
510 self.assertEqual(2, len(node.children))
511 self.assertEqual(command_e.Pipeline, node.tag())
512
513 node = assertParseAndOr(self, 'ls foo || die')
514 self.assertEqual(2, len(node.children))
515 self.assertEqual(command_e.AndOr, node.tag())
516
517 node = assertParseAndOr(self, 'ls foo|wc -l || die')
518 self.assertEqual(2, len(node.children))
519 self.assertEqual(command_e.AndOr, node.tag())
520
521 def testParseCommand(self):
522 c_parser = test_lib.InitCommandParser('ls foo')
523 node = c_parser.ParseCommand()
524 self.assertEqual(2, len(node.words))
525 print(node)
526
527 c_parser = test_lib.InitCommandParser('fun() { echo hi; }')
528 node = c_parser.ParseCommand()
529 print(node)
530 self.assertEqual(command_e.ShFunction, node.tag())
531
532 def test_ParseCommandLine(self):
533 node = assert_ParseCommandLine(self, 'ls foo 2>/dev/null')
534 self.assertEqual(2, len(node.words))
535
536 node = assert_ParseCommandLine(self, 'ls foo|wc -l')
537 self.assertEqual(command_e.Pipeline, node.tag())
538
539 node = assert_ParseCommandLine(self, 'ls foo|wc -l || die')
540 self.assertEqual(command_e.AndOr, node.tag())
541
542 node = assert_ParseCommandLine(self, 'ls foo|wc -l || die; ls /')
543 self.assertEqual(command_e.CommandList, node.tag())
544 self.assertEqual(2, len(node.children)) # two top level things
545
546 def test_ParseCommandList(self):
547 node = assert_ParseCommandList(self, 'ls foo')
548 self.assertEqual(2, len(node.words))
549
550 node = assert_ParseCommandList(self, 'ls foo|wc -l || die; ls /')
551 self.assertEqual(command_e.CommandList, node.tag())
552 self.assertEqual(2, len(node.children))
553
554 node = assert_ParseCommandList(
555 self, """\
556 ls foo | wc -l || echo fail ;
557 echo bar | wc -c || echo f2
558 """)
559 self.assertEqual(command_e.CommandList, node.tag())
560 self.assertEqual(2, len(node.children))
561
562 # TODO: Check that we get (LIST (AND_OR (PIPELINE (COMMAND ...)))) here.
563 # We want all levels.
564
565 def testParseCase(self):
566 # Empty case
567 node = assert_ParseCommandLine(self, """\
568 case foo in
569 esac
570 """)
571 self.assertEqual(command_e.Case, node.tag())
572 self.assertEqual(0, len(node.arms))
573
574 # TODO: Test all these. Probably need to add newlines too.
575 # case foo esac # INVALID
576 # case foo in esac
577 # case foo in foo) esac
578 # case foo in foo) ;; esac
579 # case foo in foo) echo hi ;; esac
580 # case foo in foo) echo hi; ;; esac
581
582 node = assert_ParseCommandLine(
583 self, """\
584 case word in
585 foo|foo2|foo3) echo hi ;;
586 esac
587 """)
588 self.assertEqual(command_e.Case, node.tag())
589 self.assertEqual(1, len(node.arms))
590
591 node = assert_ParseCommandLine(
592 self, """\
593 case word in foo) echo one-line ;; esac
594 """)
595 self.assertEqual(command_e.Case, node.tag())
596 self.assertEqual(1, len(node.arms))
597
598 node = assert_ParseCommandLine(
599 self, """\
600 case word in
601 foo) echo foo ;;
602 bar) echo bar ;;
603 esac
604 """)
605 self.assertEqual(command_e.Case, node.tag())
606 self.assertEqual(2, len(node.arms))
607
608 node = assert_ParseCommandLine(
609 self, """\
610 case word in
611 foo) echo foo ;; # NO TRAILING ;; but trailing ;
612 bar) echo bar ;
613 esac
614 """)
615 self.assertEqual(command_e.Case, node.tag())
616 self.assertEqual(2, len(node.arms))
617
618 node = assert_ParseCommandLine(
619 self, """\
620 case word in
621 foo) echo foo ;; # NO TRAILING ;;
622 bar) echo bar
623 esac
624 """)
625 self.assertEqual(command_e.Case, node.tag())
626 self.assertEqual(2, len(node.arms))
627
628 def testParseYshCase(self):
629 # Empty case
630 node = assert_ParseCommandLine(self, """\
631 case (x) {
632 }
633 """)
634 self.assertEqual(command_e.Case, node.tag())
635 self.assertEqual(0, len(node.arms))
636
637 node = assert_ParseCommandLine(
638 self, """\
639 case (x) {
640 (else) { echo hi; }
641 }
642 """)
643 self.assertEqual(command_e.Case, node.tag())
644 self.assertEqual(1, len(node.arms))
645 self.assertEqual(pat_e.Else, node.arms[0].pattern.tag())
646
647 node = assert_ParseCommandLine(
648 self, """\
649 case (x) {
650 (2) | (3) { echo hi; }
651 }
652 """)
653 self.assertEqual(command_e.Case, node.tag())
654 self.assertEqual(1, len(node.arms))
655 pattern = node.arms[0].pattern
656 self.assertEqual(pat_e.YshExprs, pattern.tag())
657
658 self.assertEqual(2, len(pattern.exprs))
659
660 node = assert_ParseCommandLine(
661 self, """\
662 case (x) {
663 bare | x | 'string' { echo hi; }
664 }
665 """)
666 self.assertEqual(command_e.Case, node.tag())
667 self.assertEqual(1, len(node.arms))
668 pattern = node.arms[0].pattern
669 self.assertEqual(pat_e.Words, pattern.tag())
670 self.assertEqual(3, len(pattern.words))
671
672 node = assert_ParseCommandLine(
673 self, """\
674 case (x) {
675 / d+ / { echo space; }
676 /d+/ { echo space2; }
677 }
678 """)
679 self.assertEqual(command_e.Case, node.tag())
680 self.assertEqual(2, len(node.arms))
681
682 pattern0 = node.arms[0].pattern
683 self.assertEqual(pat_e.Eggex, pattern0.tag())
684
685 pattern1 = node.arms[1].pattern
686 self.assertEqual(pat_e.Eggex, pattern1.tag())
687
688 node = assert_ParseCommandLine(
689 self, """\
690 case (x) {
691 word { = x }
692 }
693 """)
694 self.assertEqual(command_e.Case, node.tag())
695 self.assertEqual(1, len(node.arms))
696
697 arm = node.arms[0]
698 self.assertEqual(Id.Lit_Chars, arm.left.id)
699
700 node = assert_ParseCommandLine(
701 self, """\
702 case (x) {
703 /'eggex'/ { = x }
704 }
705 """)
706 self.assertEqual(command_e.Case, node.tag())
707 self.assertEqual(1, len(node.arms))
708
709 arm = node.arms[0]
710 self.assertEqual(Id.Arith_Slash, arm.left.id)
711
712 node = assert_ParseCommandLine(
713 self, """\
714 case (x) {
715 ('expr') { = x }
716 }
717 """)
718 self.assertEqual(command_e.Case, node.tag())
719 self.assertEqual(1, len(node.arms))
720
721 arm = node.arms[0]
722 self.assertEqual(Id.Op_LParen, arm.left.id)
723
724 node = assert_ParseCommandLine(
725 self, """\
726 case (x) {
727 (else) { = x }
728 }
729 """)
730 self.assertEqual(command_e.Case, node.tag())
731 self.assertEqual(1, len(node.arms))
732
733 arm = node.arms[0]
734 self.assertEqual(Id.Op_LParen, arm.left.id)
735
736
737 def testParseWhile(self):
738 node = assert_ParseCommandList(
739 self, """\
740 while true; do
741 echo hi
742 break
743 done
744 """)
745
746 node = assert_ParseCommandList(
747 self, """\
748 while true # comment
749 do # comment
750 echo hi # comment
751 break # comment
752 done # comment
753 """)
754
755 def testParseUntil(self):
756 node = assert_ParseCommandList(
757 self, """\
758 until false; do
759 echo hi
760 break
761 done
762 """)
763
764 def testParseFor(self):
765 node = assert_ParseCommandList(
766 self, """\
767 for i in 1 2 3; do
768 echo $i
769 done
770 """)
771 self.assertEqual(3, len(node.iterable.words))
772
773 # Don't iterate over anything!
774 node = assert_ParseCommandList(self, """\
775 for i in ; do
776 echo $i
777 done
778 """)
779 self.assertEqual(0, len(node.iterable.words))
780
781 # Iterate over the default
782 node = assert_ParseCommandList(self, """\
783 for i; do echo $i; done
784 """)
785 self.assertEqual(for_iter_e.Args, node.iterable.tag())
786
787 # Iterate over the default, over multiple lines
788 node = assert_ParseCommandList(self, """\
789 for i
790 do
791 echo $i
792 done
793 """)
794 self.assertEqual(for_iter_e.Args, node.iterable.tag())
795
796 def testParseForExpression(self):
797 node = assert_ParseCommandList(
798 self, """\
799 for ((i=0; i<5; ++i)); do
800 echo $i
801 done
802 """)
803 self.assertEqual(Id.Arith_Equal, node.init.op_id)
804 self.assertEqual(Id.Arith_Less, node.cond.op_id)
805 self.assertEqual(Id.Arith_DPlus, node.update.op_id)
806 self.assertEqual(command_e.DoGroup, node.body.tag())
807
808 # Now without the ; OR a newline
809 node = assert_ParseCommandList(
810 self, """\
811 for ((i=0; i<5; ++i)) do
812 echo $i
813 done
814 """)
815 self.assertEqual(Id.Arith_Equal, node.init.op_id)
816 self.assertEqual(Id.Arith_Less, node.cond.op_id)
817 self.assertEqual(Id.Arith_DPlus, node.update.op_id)
818 self.assertEqual(command_e.DoGroup, node.body.tag())
819
820 node = assert_ParseCommandList(self, """\
821 for ((;;)); do
822 echo $i
823 done
824 """)
825 self.assertEqual(command_e.DoGroup, node.body.tag())
826
827 def testParseCommandSub(self):
828 # Two adjacent command subs
829 node = assertParseSimpleCommand(self, 'echo $(echo 12)$(echo 34)')
830 self.assertEqual(2, len(node.words))
831
832 # Two adjacent command subs, quoted
833 node = assertParseSimpleCommand(self, 'echo "$(echo 12)$(echo 34)"')
834 self.assertEqual(2, len(node.words))
835
836 def testParseTildeSub(self):
837 node = assert_ParseCommandList(
838 self, "ls ~ ~root ~/src ~/src/foo ~root/src ~weird!name/blah!blah ")
839
840 def testParseDBracket(self):
841 node = assert_ParseCommandList(self, '[[ $# -gt 1 ]]')
842
843 # Bash allows embedded newlines in some places, but not all
844 node = assert_ParseCommandList(self, """\
845 [[ $# -gt 1 &&
846
847 foo ]]""")
848
849 # Newline needs to be Id.Op_Newline!
850 node = assert_ParseCommandList(
851 self, """\
852 if [[ $# -gt 1 ]]
853 then
854 echo hi
855 fi
856 """)
857
858 # Doh, technically this works!
859 # [[ =~ =~ =~ ]]; echo $?
860 # 0
861
862 def testParseDParen(self):
863 node = assert_ParseCommandList(self, '(( 1 + 2 ))')
864
865 def testParseDBracketRegex(self):
866 node = assert_ParseCommandList(self, '[[ foo =~ foo ]]')
867 self.assertEqual(Id.BoolBinary_EqualTilde, node.expr.op_id)
868
869 node = assert_ParseCommandList(self, '[[ foo =~ (foo|bar) ]]')
870 self.assertEqual(Id.BoolBinary_EqualTilde, node.expr.op_id)
871 right = node.expr.right
872 self.assertEqual(5, len(right.parts))
873 self.assertEqual('(', right.parts[0].tval)
874
875 # TODO: Implement BASH_REGEX_CHARS
876 return
877 node = assert_ParseCommandList(self, '[[ "< >" =~ (< >) ]]')
878 self.assertEqual(Id.BoolBinary_EqualTilde, node.expr.op_id)
879
880 node = assert_ParseCommandList(self, '[[ "ba ba" =~ ([a b]+) ]]')
881 self.assertEqual(Id.BoolBinary_EqualTilde, node.expr.op_id)
882
883 def testParseIf(self):
884 node = assert_ParseCommandList(self, 'if true; then echo yes; fi')
885 # Subshell in condition
886 node = assert_ParseCommandList(self, 'if (true); then echo yes; fi')
887
888 def testParseFunction(self):
889 node = assert_ParseCommandList(self, 'foo() { echo hi; }')
890
891 node = assert_ParseCommandList(self, 'foo() ( echo hi )')
892 node = assert_ParseCommandList(self,
893 'foo() for i in x; do echo $i; done')
894
895 # KSH FUNCTION
896 node = assert_ParseCommandList(self, 'function foo { echo hi; }')
897 node = assert_ParseCommandList(self, 'function foo () { echo hi; }')
898
899 node = assert_ParseCommandList(self, 'function foo() ( echo hi )')
900 node = assert_ParseCommandList(
901 self, 'function foo() for i in x; do echo $i; done')
902
903 # No () is OK here!
904 node = assert_ParseCommandList(
905 self, 'function foo for i in x; do echo $i; done')
906
907 # Redirects
908 node = assert_ParseCommandList(self,
909 'foo() { echo hi; } 1>&2 2>/dev/null')
910 self.assertEqual(command_e.BraceGroup, node.body.tag())
911 self.assertEqual(2, len(node.body.redirects))
912
913 def testParseKeyword(self):
914 # NOTE: It chooses the longest match, which is Lit_Chars>
915 node = assert_ParseCommandList(self, 'ifFOO')
916
917
918 class NestedParensTest(unittest.TestCase):
919 """Test the hard $() and () nesting.
920
921 Meanings of ):
922
923 ( echo x ) # subshell (cmd_parse)
924 echo $(echo x) # command substitution (word_parse)
925 (( )) # end arith command (cmd_parse)
926 $(( )) # end arith sub (word_parse))
927 a=(1 2 3) # array literal and assoc array literal
928 a[1*(2+3)]=x # grouping in arith context
929 fun() { echo x ; } # function def
930
931 case x in x) echo x ;; esac # case, with balanced or unbalanced
932 case x in (x) echo x ;; esac
933 """
934
935 def testParseSubshell(self):
936 node = assert_ParseCommandLine(self, '(cd /; echo PWD 1); echo PWD 2')
937 self.assertEqual(2, len(node.children))
938 self.assertEqual(command_e.CommandList, node.tag())
939
940 def testParseBraceGroup(self):
941 node = assert_ParseCommandLine(self, '{ cd /; echo PWD; }')
942 self.assertEqual(2, len(node.children))
943 self.assertEqual(command_e.BraceGroup, node.tag())
944
945 node = assert_ParseCommandLine(self, '{ cd /; echo PWD; }; echo PWD')
946 self.assertEqual(2, len(node.children))
947 self.assertEqual(command_e.CommandList, node.tag())
948
949 def testUnquotedComSub(self):
950 # CommandSub with two Literal instances surrounding it
951 node = assertParseSimpleCommand(self, 'echo ab$(echo hi)cd ef')
952 self.assertEqual(3, len(node.words))
953
954 def testNestedComSub(self):
955 node = assertParseSimpleCommand(self, 'echo $(one$(echo two)one) three')
956 self.assertEqual(3, len(node.words))
957
958 def testArithSubWithin(self):
959 # Within com sub
960 node = assertParseSimpleCommand(self, 'echo $(echo $((1+2)))')
961 self.assertEqual(command_e.Simple, node.tag())
962 self.assertEqual(2, len(node.words))
963
964 # Within subshell
965 node = assert_ParseCommandList(self, '(echo $((1+2)))')
966 self.assertEqual(command_e.Subshell, node.tag())
967 self.assertEqual(command_e.Simple, node.child.tag())
968
969 def testArithGroupingWithin(self):
970 # Within com sub
971 node = assertParseSimpleCommand(self, 'echo $(echo $((1*(2+3))) )')
972 self.assertEqual(command_e.Simple, node.tag())
973 self.assertEqual(2, len(node.words))
974
975 # Within subshell
976 node = assert_ParseCommandList(self, '(echo $((1*(2+3))) )')
977 self.assertEqual(command_e.Subshell, node.tag())
978 self.assertEqual(command_e.Simple, node.child.tag())
979
980 def testLhsArithGroupingWithin(self):
981 # Within Arith sub
982 node = assertParseSimpleCommand(self, 'echo $((a[1*(2+3)]=x))')
983 self.assertEqual(2, len(node.words))
984
985 # Within Command Sub -- NOT IMPLEMENTED
986 return
987 node = assertParseSimpleCommand(self, 'echo $(a[1*(2+3)]=x)')
988 self.assertEqual(2, len(node.words))
989
990 def testShFunctionWithin(self):
991 node = assert_ParseCommandList(self, 'echo $(fun() { echo hi; }; fun)')
992 self.assertEqual(command_e.Simple, node.tag())
993 self.assertEqual(2, len(node.words))
994
995 node = assert_ParseCommandList(self, '(fun() { echo hi; }; fun)')
996 self.assertEqual(command_e.Subshell, node.tag())
997 self.assertEqual(command_e.CommandList, node.child.tag())
998
999 def testArrayLiteralWithin(self):
1000 node = assert_ParseCommandList(self, 'echo $(array=(a b c))')
1001 self.assertEqual(command_e.Simple, node.tag())
1002 self.assertEqual(2, len(node.words))
1003
1004 node = assert_ParseCommandList(self, '(array=(a b c))')
1005 self.assertEqual(command_e.Subshell, node.tag())
1006 self.assertEqual(command_e.ShAssignment, node.child.tag())
1007
1008 def testSubshellWithinComSub(self):
1009 node = assert_ParseCommandList(
1010 self,
1011 'echo one; echo $( (cd /; echo subshell_PWD); echo comsub_PWD); echo two'
1012 )
1013 self.assertEqual(command_e.CommandList, node.tag())
1014 self.assertEqual(3, len(node.children)) # 3 echo statements
1015
1016 # TODO: Need a way to test the literal value of a word
1017 #words = [w.UnquotedLiteralValue() for w in node.children[2].words]
1018 #print(words)
1019
1020 def testCaseWithinComSub(self):
1021 node = assert_ParseCommandList(
1022 self, 'echo $( case foo in one) echo comsub;; esac)')
1023 self.assertEqual(2, len(node.words))
1024
1025 node = assert_ParseCommandList(
1026 self, """\
1027 echo $(
1028 case foo in one) echo comsub1;; esac
1029 case bar in two) echo comsub2;; esac
1030 )
1031 """)
1032 self.assertEqual(2, len(node.words))
1033
1034 def testComsubWithinCaseWithinComSub(self):
1035 # Comsub within case within comsub
1036 node = assert_ParseCommandList(
1037 self,
1038 'echo one; echo $( case one in $(echo one)) echo $(comsub);; esac ); echo two'
1039 )
1040 self.assertEqual(command_e.CommandList, node.tag())
1041 # Top level should have 3 echo statements
1042 self.assertEqual(3, len(node.children))
1043
1044 def testComSubWithinDoubleQuotes(self):
1045 # CommandSub with two Literal instances surrounding it
1046 node = assertParseSimpleCommand(self,
1047 'echo "double $(echo hi) quoted" two')
1048 self.assertEqual(3, len(node.words))
1049
1050 def testEmptyCaseWithinSubshell(self):
1051 node = assert_ParseCommandList(self, """\
1052 ( case foo in
1053 esac
1054 )
1055 """)
1056 self.assertEqual(command_e.Subshell, node.tag())
1057
1058 def testBalancedCaseWithin(self):
1059 # With leading ( in case. This one doesn't cause problems! We don't need
1060 # the MaybeUnreadOne() lexer hack.
1061 node = assert_ParseCommandList(
1062 self, """\
1063 $( case foo in
1064 (one) echo hi ;;
1065 esac
1066 )
1067 """)
1068 self.assertEqual(command_e.Simple, node.tag())
1069
1070 node = assert_ParseCommandList(
1071 self, """\
1072 ( case foo in
1073 (one) echo hi ;;
1074 esac
1075 )
1076 """)
1077 self.assertEqual(command_e.Subshell, node.tag())
1078
1079 def testUnbalancedCaseWithin(self):
1080 # With leading ( in case. This one doesn't cause problems! We don't need
1081 # the MaybeUnreadOne() lexer hack.
1082 node = assert_ParseCommandList(
1083 self, """\
1084 $( case foo in
1085 one) echo hi ;;
1086 esac
1087 )
1088 """)
1089 self.assertEqual(command_e.Simple, node.tag())
1090
1091 node = assert_ParseCommandList(
1092 self, """\
1093 ( case foo in
1094 one) echo hi ;;
1095 esac
1096 )
1097 """)
1098 self.assertEqual(command_e.Subshell, node.tag())
1099
1100 def testForExpressionWithin(self):
1101 # With leading ( in case. This one doesn't cause problems! We don't need
1102 # the MaybeUnreadOne() lexer hack.
1103 node = assert_ParseCommandList(
1104 self, """\
1105 $( for ((i=0; i<3; ++i)); do
1106 echo hi
1107 done
1108 )
1109 """)
1110 self.assertEqual(command_e.Simple, node.tag())
1111
1112 node = assert_ParseCommandList(
1113 self, """\
1114 ( for ((i=0; i<3; ++i)); do
1115 echo hi
1116 done
1117 )
1118 """)
1119 self.assertEqual(command_e.Subshell, node.tag())
1120
1121
1122 class RealBugsTest(unittest.TestCase):
1123 def testGitBug(self):
1124 # Original bug from git codebase. Case in subshell.
1125 node = assert_ParseCommandList(
1126 self, """\
1127 ( cd "$PACKDIR" &&
1128 for e in $existing
1129 do
1130 case " $fullbases " in
1131 *" $e "*) ;;
1132 *) rm -f "$e.pack" "$e.idx" "$e.keep" ;;
1133 esac
1134 done
1135 )
1136 """)
1137 self.assertEqual(command_e.Subshell, node.tag())
1138
1139 def testParseCase3(self):
1140 # Bug from git codebase. NOT a comment token.
1141 node = assert_ParseCommandLine(
1142 self, """\
1143 case "$fd,$command" in
1144 3,#*|3,)
1145 # copy comments
1146 ;;
1147 esac
1148 """)
1149 self.assertEqual(command_e.Case, node.tag())
1150
1151 def testGitComment(self):
1152 # ;# is a comment! Gah.
1153 # Conclusion: Comments are NOT LEXICAL. They are part of word parsing.
1154
1155 node = assert_ParseCommandList(
1156 self, """\
1157 . "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
1158 """)
1159 self.assertEqual(command_e.Sentence, node.tag())
1160 self.assertEqual(2, len(node.child.words))
1161
1162 # This is NOT a comment
1163 node = assert_ParseCommandList(self, """\
1164 echo foo#bar
1165 """)
1166 self.assertEqual(command_e.Simple, node.tag())
1167 self.assertEqual(2, len(node.words))
1168 _, s, _ = word_.StaticEval(node.words[1])
1169 self.assertEqual('foo#bar', s)
1170
1171 # This is a comment
1172 node = assert_ParseCommandList(self, """\
1173 echo foo #comment
1174 """)
1175 self.assertEqual(command_e.Simple, node.tag())
1176 self.assertEqual(2, len(node.words))
1177 _, s, _ = word_.StaticEval(node.words[1])
1178 self.assertEqual('foo', s)
1179
1180 # Empty comment
1181 node = assert_ParseCommandList(self, """\
1182 echo foo #
1183 """)
1184 self.assertEqual(command_e.Simple, node.tag())
1185 self.assertEqual(2, len(node.words))
1186 _, s, _ = word_.StaticEval(node.words[1])
1187 self.assertEqual('foo', s)
1188
1189 def testChromeIfSubshell(self):
1190 node = assert_ParseCommandList(self, """\
1191 if true; then (
1192 echo hi
1193 )
1194 fi
1195 """)
1196 self.assertEqual(command_e.If, node.tag())
1197
1198 node = assert_ParseCommandList(
1199 self, """\
1200 while true; do {
1201 echo hi
1202 break
1203 } done
1204 """)
1205 self.assertEqual(command_e.WhileUntil, node.tag())
1206 self.assertEqual(Id.KW_While, node.keyword.id)
1207
1208 node = assert_ParseCommandList(self, """\
1209 if true; then (
1210 echo hi
1211 ) fi
1212 """)
1213 self.assertEqual(command_e.If, node.tag())
1214
1215 # Related: two fi's in a row, found in Chrome configure. Compound commands
1216 # are special; don't need newlines.
1217 node = assert_ParseCommandList(
1218 self, """\
1219 if true; then
1220 if true; then
1221 echo hi
1222 fi fi
1223 echo hi
1224 """)
1225 self.assertEqual(command_e.CommandList, node.tag())
1226
1227 def testBackticks(self):
1228 # Another empty command sub
1229 node = assert_ParseCommandList(self, """\
1230 echo $()
1231 """)
1232
1233 # Simplest case
1234 node = assert_ParseCommandList(self, """\
1235 echo ``
1236 """)
1237
1238 # Found in the wild.
1239 # Just a comment trick found in sandstorm
1240 node = assert_ParseCommandList(
1241 self, """\
1242 cmd \
1243 flag `# comment` \
1244 flag2
1245 """)
1246
1247 # Empty should be allowed
1248 node = assert_ParseCommandList(self, """\
1249 FOO="bar"`
1250 `"baz"
1251 """)
1252
1253 def testQuineDb(self):
1254 # Need to handle the DOLLAR_SQ lex state
1255 node = assert_ParseCommandList(
1256 self, r"""\
1257 case foo in
1258 $'\'')
1259 ret+="\\"\'
1260 ;;
1261 esac
1262 """)
1263 self.assertEqual(command_e.Case, node.tag())
1264
1265 node = assert_ParseCommandList(self, r"""\
1266 $'abc\ndef'
1267 """)
1268 self.assertEqual(command_e.Simple, node.tag())
1269 self.assertEqual(1, len(node.words))
1270 w = node.words[0]
1271 self.assertEqual(1, len(w.parts))
1272 p = w.parts[0]
1273 self.assertEqual(3, len(p.tokens))
1274 self.assertEqual(Id.Char_Literals, p.tokens[0].id)
1275 self.assertEqual(Id.Char_OneChar, p.tokens[1].id)
1276 self.assertEqual(Id.Char_Literals, p.tokens[2].id)
1277
1278 def testArithConstants(self):
1279 # Found in Gherkin
1280 node = assert_ParseCommandList(
1281 self, r"""\
1282 [[ -n "${marks[${tag_marker}002${cons_ptr}]}" ]];
1283 """)
1284 # Dynamic constant
1285 node = assert_ParseCommandList(self, r"""\
1286 echo $(( 0x$foo ))
1287 """)
1288
1289 def testBacktickCommentHack(self):
1290 # Found in sandstorm.
1291 # The problem here is that the comment goes to the end of the line, which
1292 # eats up the closing backtick! We could change the mode of the lexer
1293 # inside a command sub, or possibly just ignore this use case.
1294 return
1295
1296 node = assert_ParseCommandList(
1297 self, r"""\
1298 openssl \
1299 -newkey rsa:4096 `# Create a new RSA key of length 4096 bits.` \
1300 `# Sandcats just needs the CN= (common name) in the request.` \
1301 -subj "/CN=*.${SS_HOSTNAME}/"
1302 """)
1303
1304 def testArrayLiteralFromSetup(self):
1305 # Found in setup.shl/bin/setup -- this is the "Parsing Bash is
1306 # Undecidable" problem.
1307 err = _assert_ParseCommandListError(
1308 self, """\
1309 errcmd=( "${SETUP_STATE[$err.cmd]}" )
1310 """)
1311
1312 # Double quotes fix it.
1313 node = assert_ParseCommandList(
1314 self, r"""\
1315 errcmd=( "${SETUP_STATE["$err.cmd"]}" )
1316 """)
1317
1318
1319 class ErrorLocationsTest(unittest.TestCase):
1320 def testCommand(self):
1321 """Enumerating errors in cmd_parse.py."""
1322
1323 err = _assert_ParseCommandListError(self, 'ls <')
1324
1325 err = _assert_ParseCommandListError(self, 'ls < <')
1326
1327 # Word parse error in command parser
1328 err = _assert_ParseCommandListError(self, r'echo foo$(ls <)bar')
1329
1330 err = _assert_ParseCommandListError(self, r'BAD_ENV=(1 2 3) ls')
1331
1332 # This needs more context
1333 err = _assert_ParseCommandListError(
1334 self, 'for ((i=1; i<)); do echo $i; done')
1335
1336 err = _assert_ParseCommandListError(
1337 self, 'for ((i=1; i<5; ++i)) OOPS echo $i; ERR')
1338
1339 # After semi
1340 err = _assert_ParseCommandListError(
1341 self, 'for ((i=1; i<5; ++i)); OOPS echo $i; ERR')
1342
1343 err = _assert_ParseCommandListError(
1344 self, 'for $bad in 1 2; do echo hi; done')
1345
1346 err = _assert_ParseCommandListError(self, 'for foo BAD')
1347
1348 err = _assert_ParseCommandListError(self, 'if foo; then echo hi; z')
1349
1350 err = _assert_ParseCommandListError(self,
1351 'foo$(invalid) () { echo hi; }')
1352
1353 def testErrorInHereDoc(self):
1354 return
1355 # Here doc body. Hm this should be failing. Does it just fail to get
1356 # filled?
1357 err = _assert_ParseCommandListError(self, """cat <<EOF
1358 $(echo <)
1359 EOF
1360 """)
1361 return
1362
1363 def testBool(self):
1364 """Enumerating errors in bool_parse.py."""
1365 err = _assert_ParseCommandListError(self, '[[ foo bar ]]')
1366 err = _assert_ParseCommandListError(self, '[[ foo -eq ]]')
1367
1368 # error in word
1369 err = _assert_ParseCommandListError(self, '[[ foo$(echo <) -eq foo ]]')
1370
1371 return
1372 # NOTE: This was disabled because of escaping.
1373 # Invalid regex
1374 err = _assert_ParseCommandListError(self, '[[ foo =~ \( ]]')
1375
1376 def testArith(self):
1377 """Enumerating errors in arith_parse.py."""
1378 err = _assert_ParseCommandListError(self, '(( 1 + ))')
1379
1380 def testArraySyntax(self):
1381 err = _assert_ParseCommandListError(self, 'A= (1 2)')
1382
1383 def testEofInDoubleQuoted(self):
1384 err = _assert_ParseCommandListError(self, 'foo="" echo "bar ')
1385
1386 def testQuotesInFunctionName(self):
1387 err = _assert_ParseCommandListError(
1388 self, """\
1389 foo"bar" () {
1390 echo hi
1391 }
1392 """)
1393
1394 def testForLoopName(self):
1395 err = _assert_ParseCommandListError(
1396 self, """\
1397 for [ i = 1; i < 10; i++ ]
1398 """)
1399 err = _assert_ParseCommandListError(self, """\
1400 for = in a
1401 """)
1402
1403 def testHereDocCommandSub(self):
1404 # Originally from spec/09-here-doc.sh.
1405 err = _assert_ParseCommandListError(
1406 self, """\
1407 for x in 1 2 $(cat <<EOF
1408 THREE
1409 EOF); do
1410 echo for word $x
1411 done
1412 """)
1413
1414 def testForLoopEof(self):
1415 err = _assert_ParseCommandListError(self, "for x in 1 2 $(")
1416
1417
1418 class ParserInteractionsTest(unittest.TestCase):
1419
1420 def _dumpLexerState(self, lexer):
1421 print("----")
1422 print(lexer.line_lexer.src_line.content)
1423 print(" " * lexer.line_lexer.line_pos + "^ We are here")
1424 print("----")
1425
1426 def testBraceGroup(self):
1427 code_str = '{ echo hello; } '
1428
1429 c_parser = test_lib.InitCommandParser(code_str)
1430 lexer = c_parser.lexer
1431
1432 c_parser.ParseBraceGroup()
1433
1434 if 0:
1435 self._dumpLexerState(lexer)
1436
1437 # We should be at the end of the line:
1438 # '{ echo hello; } '
1439 # ^ Which is here
1440 self.assertEqual(len(lexer.line_lexer.src_line.content),
1441 lexer.line_lexer.line_pos)
1442
1443 next_id = c_parser.w_parser.LookPastSpace()
1444 self.assertEqual(next_id, Id.Unknown_Tok, Id_str(next_id))
1445
1446 def testYSHBraceGroup(self):
1447 code_str = '{ echo hello } '
1448
1449 c_parser = test_lib.InitCommandParser(code_str)
1450 c_parser.parse_opts = state.MakeOilOpts() # place parser in YSH mode
1451 lexer = c_parser.lexer
1452
1453 c_parser.ParseBraceGroup()
1454
1455 if 0:
1456 self._dumpLexerState(lexer)
1457
1458 self.assertEqual(len(lexer.line_lexer.src_line.content),
1459 lexer.line_lexer.line_pos)
1460
1461 next_id = c_parser.w_parser.LookPastSpace()
1462 self.assertEqual(next_id, Id.Unknown_Tok)
1463
1464 def testCmd2Expr2Cmd(self):
1465 code_str = '{ = hello } '
1466
1467 c_parser = test_lib.InitCommandParser(code_str)
1468 c_parser.parse_opts = state.MakeOilOpts() # place parser in YSH mode
1469 lexer = c_parser.lexer
1470
1471 c_parser.ParseBraceGroup()
1472
1473 if 0:
1474 self._dumpLexerState(lexer)
1475
1476 self.assertEqual(len(lexer.line_lexer.src_line.content),
1477 lexer.line_lexer.line_pos)
1478
1479 next_id = c_parser.w_parser.LookPastSpace()
1480 self.assertEqual(next_id, Id.Unknown_Tok)
1481
1482
1483
1484 if __name__ == '__main__':
1485 unittest.main()