OILS / osh / cmd_parse_test.py View on Github | oils.pub

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