bash - ast_test.go
1 package bash
2
3 import (
4 "reflect"
5 "testing"
6
7 "vimagination.zapto.org/parser"
8 )
9
10 type sourceFn struct {
11 Source string
12 Fn func(*test, Tokens)
13 }
14
15 type test struct {
16 Parser *bashParser
17 Output Type
18 Err error
19 }
20
21 func makeTokeniser(tk parser.Tokeniser) *parser.Tokeniser {
22 return &tk
23 }
24
25 func doTests(t *testing.T, tests []sourceFn, fn func(*test) (Type, error)) {
26 t.Helper()
27
28 var err error
29
30 for n, tt := range tests {
31 var ts test
32
33 if ts.Parser, err = newBashParser(makeTokeniser(parser.NewStringTokeniser(tt.Source))); err != nil {
34 t.Errorf("test %d: unexpected error: %s", n+1, err)
35
36 continue
37 }
38
39 tt.Fn(&ts, Tokens(ts.Parser.Tokens[:cap(ts.Parser.Tokens)]))
40
41 if output, err := fn(&ts); !reflect.DeepEqual(err, ts.Err) {
42 t.Errorf("test %d: expecting error: %v, got %v", n+1, ts.Err, err)
43 } else if ts.Output != nil && !reflect.DeepEqual(output, ts.Output) {
44 t.Errorf("test %d: expecting \n%+v\n...got...\n%+v", n+1, ts.Output, output)
45 }
46 }
47 }
48
49 func TestFile(t *testing.T) {
50 doTests(t, []sourceFn{
51 {"a", func(t *test, tk Tokens) { // 1
52 t.Output = File{
53 Lines: []Line{
54 {
55 Statements: []Statement{
56 {
57 Pipeline: Pipeline{
58 CommandOrCompound: CommandOrCompound{
59 Command: &Command{
60 Words: []Word{
61 {
62 Parts: []WordPart{
63 {
64 Part: &tk[0],
65 Tokens: tk[:1],
66 },
67 },
68 Tokens: tk[:1],
69 },
70 },
71 Tokens: tk[:1],
72 },
73 Tokens: tk[:1],
74 },
75 Tokens: tk[:1],
76 },
77 Tokens: tk[:1],
78 },
79 },
80 Tokens: tk[:1],
81 },
82 },
83 Tokens: tk[:1],
84 }
85 }},
86 {"a\nb", func(t *test, tk Tokens) { // 2
87 t.Output = File{
88 Lines: []Line{
89 {
90 Statements: []Statement{
91 {
92 Pipeline: Pipeline{
93 CommandOrCompound: CommandOrCompound{
94 Command: &Command{
95 Words: []Word{
96 {
97 Parts: []WordPart{
98 {
99 Part: &tk[0],
100 Tokens: tk[:1],
101 },
102 },
103 Tokens: tk[:1],
104 },
105 },
106 Tokens: tk[:1],
107 },
108 Tokens: tk[:1],
109 },
110 Tokens: tk[:1],
111 },
112 Tokens: tk[:1],
113 },
114 },
115 Tokens: tk[:1],
116 },
117 {
118 Statements: []Statement{
119 {
120 Pipeline: Pipeline{
121 CommandOrCompound: CommandOrCompound{
122 Command: &Command{
123 Words: []Word{
124 {
125 Parts: []WordPart{
126 {
127 Part: &tk[2],
128 Tokens: tk[2:3],
129 },
130 },
131 Tokens: tk[2:3],
132 },
133 },
134 Tokens: tk[2:3],
135 },
136 Tokens: tk[2:3],
137 },
138 Tokens: tk[2:3],
139 },
140 Tokens: tk[2:3],
141 },
142 },
143 Tokens: tk[2:3],
144 },
145 },
146 Tokens: tk[:3],
147 }
148 }},
149 {"a\n\nb\n", func(t *test, tk Tokens) { // 3
150 t.Output = File{
151 Lines: []Line{
152 {
153 Statements: []Statement{
154 {
155 Pipeline: Pipeline{
156 CommandOrCompound: CommandOrCompound{
157 Command: &Command{
158 Words: []Word{
159 {
160 Parts: []WordPart{
161 {
162 Part: &tk[0],
163 Tokens: tk[:1],
164 },
165 },
166 Tokens: tk[:1],
167 },
168 },
169 Tokens: tk[:1],
170 },
171 Tokens: tk[:1],
172 },
173 Tokens: tk[:1],
174 },
175 Tokens: tk[:1],
176 },
177 },
178 Tokens: tk[:1],
179 },
180 {
181 Statements: []Statement{
182 {
183 Pipeline: Pipeline{
184 CommandOrCompound: CommandOrCompound{
185 Command: &Command{
186 Words: []Word{
187 {
188 Parts: []WordPart{
189 {
190 Part: &tk[2],
191 Tokens: tk[2:3],
192 },
193 },
194 Tokens: tk[2:3],
195 },
196 },
197 Tokens: tk[2:3],
198 },
199 Tokens: tk[2:3],
200 },
201 Tokens: tk[2:3],
202 },
203 Tokens: tk[2:3],
204 },
205 },
206 Tokens: tk[2:3],
207 },
208 },
209 Tokens: tk[:3],
210 }
211 }},
212 {"||", func(t *test, tk Tokens) { // 4
213 t.Err = Error{
214 Err: Error{
215 Err: Error{
216 Err: Error{
217 Err: Error{
218 Err: Error{
219 Err: ErrMissingWord,
220 Parsing: "Command",
221 Token: tk[0],
222 },
223 Parsing: "CommandOrCompound",
224 Token: tk[0],
225 },
226 Parsing: "Pipeline",
227 Token: tk[0],
228 },
229 Parsing: "Statement",
230 Token: tk[0],
231 },
232 Parsing: "Line",
233 Token: tk[0],
234 },
235 Parsing: "File",
236 Token: tk[0],
237 }
238 }},
239 {"<<a\nb\na", func(t *test, tk Tokens) { // 5
240 t.Output = File{
241 Lines: []Line{
242 {
243 Statements: []Statement{
244 {
245 Pipeline: Pipeline{
246 CommandOrCompound: CommandOrCompound{
247 Command: &Command{
248 Redirections: []Redirection{
249 {
250 Redirector: &tk[0],
251 Output: Word{
252 Parts: []WordPart{
253 {
254 Part: &tk[1],
255 Tokens: tk[1:2],
256 },
257 },
258 Tokens: tk[1:2],
259 },
260 Heredoc: &Heredoc{
261 HeredocPartsOrWords: []HeredocPartOrWord{
262 {
263 HeredocPart: &tk[3],
264 Tokens: tk[3:4],
265 },
266 },
267 Tokens: tk[3:5],
268 },
269 Tokens: tk[:2],
270 },
271 },
272 Tokens: tk[:2],
273 },
274 Tokens: tk[:2],
275 },
276 Tokens: tk[:2],
277 },
278 Tokens: tk[:2],
279 },
280 },
281 Tokens: tk[:5],
282 },
283 },
284 Tokens: tk[:5],
285 }
286 }},
287 {"<<a;<<b\nc\na\nd\nb", func(t *test, tk Tokens) { // 6
288 t.Output = File{
289 Lines: []Line{
290 {
291 Statements: []Statement{
292 {
293 Pipeline: Pipeline{
294 CommandOrCompound: CommandOrCompound{
295 Command: &Command{
296 Redirections: []Redirection{
297 {
298 Redirector: &tk[0],
299 Output: Word{
300 Parts: []WordPart{
301 {
302 Part: &tk[1],
303 Tokens: tk[1:2],
304 },
305 },
306 Tokens: tk[1:2],
307 },
308 Heredoc: &Heredoc{
309 HeredocPartsOrWords: []HeredocPartOrWord{
310 {
311 HeredocPart: &tk[6],
312 Tokens: tk[6:7],
313 },
314 },
315 Tokens: tk[6:8],
316 },
317 Tokens: tk[:2],
318 },
319 },
320 Tokens: tk[:2],
321 },
322 Tokens: tk[:2],
323 },
324 Tokens: tk[:2],
325 },
326 Tokens: tk[:3],
327 },
328 {
329 Pipeline: Pipeline{
330 CommandOrCompound: CommandOrCompound{
331 Command: &Command{
332 Redirections: []Redirection{
333 {
334 Redirector: &tk[3],
335 Output: Word{
336 Parts: []WordPart{
337 {
338 Part: &tk[4],
339 Tokens: tk[4:5],
340 },
341 },
342 Tokens: tk[4:5],
343 },
344 Heredoc: &Heredoc{
345 HeredocPartsOrWords: []HeredocPartOrWord{
346 {
347 HeredocPart: &tk[9],
348 Tokens: tk[9:10],
349 },
350 },
351 Tokens: tk[9:11],
352 },
353 Tokens: tk[3:5],
354 },
355 },
356 Tokens: tk[3:5],
357 },
358 Tokens: tk[3:5],
359 },
360 Tokens: tk[3:5],
361 },
362 Tokens: tk[3:5],
363 },
364 },
365 Tokens: tk[:11],
366 },
367 },
368 Tokens: tk[:11],
369 }
370 }},
371 {"<<a\n$(||)\na", func(t *test, tk Tokens) { // 7
372 t.Err = Error{
373 Err: Error{
374 Err: Error{
375 Err: Error{
376 Err: Error{
377 Err: Error{
378 Err: Error{
379 Err: Error{
380 Err: Error{
381 Err: Error{
382 Err: Error{
383 Err: Error{
384 Err: Error{
385 Err: Error{
386 Err: Error{
387 Err: Error{
388 Err: Error{
389 Err: Error{
390 Err: ErrMissingWord,
391 Parsing: "Command",
392 Token: tk[4],
393 },
394 Parsing: "CommandOrCompound",
395 Token: tk[4],
396 },
397 Parsing: "Pipeline",
398 Token: tk[4],
399 },
400 Parsing: "Statement",
401 Token: tk[4],
402 },
403 Parsing: "Line",
404 Token: tk[4],
405 },
406 Parsing: "File",
407 Token: tk[4],
408 },
409 Parsing: "CommandSubstitution",
410 Token: tk[4],
411 },
412 Parsing: "WordPart",
413 Token: tk[3],
414 },
415 Parsing: "Word",
416 Token: tk[3],
417 },
418 Parsing: "HeredocPartOrWord",
419 Token: tk[3],
420 },
421 Parsing: "Heredoc",
422 Token: tk[3],
423 },
424 Parsing: "Redirection",
425 Token: tk[3],
426 },
427 Parsing: "Command",
428 Token: tk[3],
429 },
430 Parsing: "CommandOrCompound",
431 Token: tk[3],
432 },
433 Parsing: "Pipeline",
434 Token: tk[3],
435 },
436 Parsing: "Statement",
437 Token: tk[3],
438 },
439 Parsing: "Line",
440 Token: tk[3],
441 },
442 Parsing: "File",
443 Token: tk[0],
444 }
445 }},
446 }, func(t *test) (Type, error) {
447 var f File
448
449 err := f.parse(t.Parser)
450
451 return f, err
452 })
453 }
454
455 func TestLine(t *testing.T) {
456 doTests(t, []sourceFn{
457 {"a", func(t *test, tk Tokens) { // 1
458 t.Output = Line{
459 Statements: []Statement{
460 {
461 Pipeline: Pipeline{
462 CommandOrCompound: CommandOrCompound{
463 Command: &Command{
464 Words: []Word{
465 {
466 Parts: []WordPart{
467 {
468 Part: &tk[0],
469 Tokens: tk[:1],
470 },
471 },
472 Tokens: tk[:1],
473 },
474 },
475 Tokens: tk[:1],
476 },
477 Tokens: tk[:1],
478 },
479 Tokens: tk[:1],
480 },
481 Tokens: tk[:1],
482 },
483 },
484 Tokens: tk[:1],
485 }
486 }},
487 {"a;b", func(t *test, tk Tokens) { // 2
488 t.Output = Line{
489 Statements: []Statement{
490 {
491 Pipeline: Pipeline{
492 CommandOrCompound: CommandOrCompound{
493 Command: &Command{
494 Words: []Word{
495 {
496 Parts: []WordPart{
497 {
498 Part: &tk[0],
499 Tokens: tk[:1],
500 },
501 },
502 Tokens: tk[:1],
503 },
504 },
505 Tokens: tk[:1],
506 },
507 Tokens: tk[:1],
508 },
509 Tokens: tk[:1],
510 },
511 Tokens: tk[:2],
512 },
513 {
514 Pipeline: Pipeline{
515 CommandOrCompound: CommandOrCompound{
516 Command: &Command{
517 Words: []Word{
518 {
519 Parts: []WordPart{
520 {
521 Part: &tk[2],
522 Tokens: tk[2:3],
523 },
524 },
525 Tokens: tk[2:3],
526 },
527 },
528 Tokens: tk[2:3],
529 },
530 Tokens: tk[2:3],
531 },
532 Tokens: tk[2:3],
533 },
534 Tokens: tk[2:3],
535 },
536 },
537 Tokens: tk[:3],
538 }
539 }},
540 {"a & b;", func(t *test, tk Tokens) { // 3
541 t.Output = Line{
542 Statements: []Statement{
543 {
544 Pipeline: Pipeline{
545 CommandOrCompound: CommandOrCompound{
546 Command: &Command{
547 Words: []Word{
548 {
549 Parts: []WordPart{
550 {
551 Part: &tk[0],
552 Tokens: tk[:1],
553 },
554 },
555 Tokens: tk[:1],
556 },
557 },
558 Tokens: tk[:1],
559 },
560 Tokens: tk[:1],
561 },
562 Tokens: tk[:1],
563 },
564 JobControl: JobControlBackground,
565 Tokens: tk[:3],
566 },
567 {
568 Pipeline: Pipeline{
569 CommandOrCompound: CommandOrCompound{
570 Command: &Command{
571 Words: []Word{
572 {
573 Parts: []WordPart{
574 {
575 Part: &tk[4],
576 Tokens: tk[4:5],
577 },
578 },
579 Tokens: tk[4:5],
580 },
581 },
582 Tokens: tk[4:5],
583 },
584 Tokens: tk[4:5],
585 },
586 Tokens: tk[4:5],
587 },
588 Tokens: tk[4:6],
589 },
590 },
591 Tokens: tk[:6],
592 }
593 }},
594 {"||", func(t *test, tk Tokens) { // 4
595 t.Err = Error{
596 Err: Error{
597 Err: Error{
598 Err: Error{
599 Err: Error{
600 Err: ErrMissingWord,
601 Parsing: "Command",
602 Token: tk[0],
603 },
604 Parsing: "CommandOrCompound",
605 Token: tk[0],
606 },
607 Parsing: "Pipeline",
608 Token: tk[0],
609 },
610 Parsing: "Statement",
611 Token: tk[0],
612 },
613 Parsing: "Line",
614 Token: tk[0],
615 }
616 }},
617 }, func(t *test) (Type, error) {
618 var l Line
619
620 err := l.parse(t.Parser)
621
622 return l, err
623 })
624 }
625
626 func TestStatement(t *testing.T) {
627 doTests(t, []sourceFn{
628 {"a", func(t *test, tk Tokens) { // 1
629 t.Output = Statement{
630 Pipeline: Pipeline{
631 CommandOrCompound: CommandOrCompound{
632 Command: &Command{
633 Words: []Word{
634 {
635 Parts: []WordPart{
636 {
637 Part: &tk[0],
638 Tokens: tk[:1],
639 },
640 },
641 Tokens: tk[:1],
642 },
643 },
644 Tokens: tk[:1],
645 },
646 Tokens: tk[:1],
647 },
648 Tokens: tk[:1],
649 },
650 Tokens: tk[:1],
651 }
652 }},
653 {"a||b", func(t *test, tk Tokens) { // 2
654 t.Output = Statement{
655 Pipeline: Pipeline{
656 CommandOrCompound: CommandOrCompound{
657 Command: &Command{
658 Words: []Word{
659 {
660 Parts: []WordPart{
661 {
662 Part: &tk[0],
663 Tokens: tk[:1],
664 },
665 },
666 Tokens: tk[:1],
667 },
668 },
669 Tokens: tk[:1],
670 },
671 Tokens: tk[:1],
672 },
673 Tokens: tk[:1],
674 },
675 LogicalOperator: LogicalOperatorOr,
676 Statement: &Statement{
677 Pipeline: Pipeline{
678 CommandOrCompound: CommandOrCompound{
679 Command: &Command{
680 Words: []Word{
681 {
682 Parts: []WordPart{
683 {
684 Part: &tk[2],
685 Tokens: tk[2:3],
686 },
687 },
688 Tokens: tk[2:3],
689 },
690 },
691 Tokens: tk[2:3],
692 },
693 Tokens: tk[2:3],
694 },
695 Tokens: tk[2:3],
696 },
697 Tokens: tk[2:3],
698 },
699 Tokens: tk[:3],
700 }
701 }},
702 {"a && b", func(t *test, tk Tokens) { // 3
703 t.Output = Statement{
704 Pipeline: Pipeline{
705 CommandOrCompound: CommandOrCompound{
706 Command: &Command{
707 Words: []Word{
708 {
709 Parts: []WordPart{
710 {
711 Part: &tk[0],
712 Tokens: tk[:1],
713 },
714 },
715 Tokens: tk[:1],
716 },
717 },
718 Tokens: tk[:1],
719 },
720 Tokens: tk[:1],
721 },
722 Tokens: tk[:1],
723 },
724 LogicalOperator: LogicalOperatorAnd,
725 Statement: &Statement{
726 Pipeline: Pipeline{
727 CommandOrCompound: CommandOrCompound{
728 Command: &Command{
729 Words: []Word{
730 {
731 Parts: []WordPart{
732 {
733 Part: &tk[4],
734 Tokens: tk[4:5],
735 },
736 },
737 Tokens: tk[4:5],
738 },
739 },
740 Tokens: tk[4:5],
741 },
742 Tokens: tk[4:5],
743 },
744 Tokens: tk[4:5],
745 },
746 Tokens: tk[4:5],
747 },
748 Tokens: tk[:5],
749 }
750 }},
751 {"a||b;", func(t *test, tk Tokens) { // 4
752 t.Output = Statement{
753 Pipeline: Pipeline{
754 CommandOrCompound: CommandOrCompound{
755 Command: &Command{
756 Words: []Word{
757 {
758 Parts: []WordPart{
759 {
760 Part: &tk[0],
761 Tokens: tk[:1],
762 },
763 },
764 Tokens: tk[:1],
765 },
766 },
767 Tokens: tk[:1],
768 },
769 Tokens: tk[:1],
770 },
771 Tokens: tk[:1],
772 },
773 LogicalOperator: LogicalOperatorOr,
774 Statement: &Statement{
775 Pipeline: Pipeline{
776 CommandOrCompound: CommandOrCompound{
777 Command: &Command{
778 Words: []Word{
779 {
780 Parts: []WordPart{
781 {
782 Part: &tk[2],
783 Tokens: tk[2:3],
784 },
785 },
786 Tokens: tk[2:3],
787 },
788 },
789 Tokens: tk[2:3],
790 },
791 Tokens: tk[2:3],
792 },
793 Tokens: tk[2:3],
794 },
795 Tokens: tk[2:3],
796 },
797 Tokens: tk[:4],
798 }
799 }},
800 {"a||b &", func(t *test, tk Tokens) { // 5
801 t.Output = Statement{
802 Pipeline: Pipeline{
803 CommandOrCompound: CommandOrCompound{
804 Command: &Command{
805 Words: []Word{
806 {
807 Parts: []WordPart{
808 {
809 Part: &tk[0],
810 Tokens: tk[:1],
811 },
812 },
813 Tokens: tk[:1],
814 },
815 },
816 Tokens: tk[:1],
817 },
818 Tokens: tk[:1],
819 },
820 Tokens: tk[:1],
821 },
822 LogicalOperator: LogicalOperatorOr,
823 Statement: &Statement{
824 Pipeline: Pipeline{
825 CommandOrCompound: CommandOrCompound{
826 Command: &Command{
827 Words: []Word{
828 {
829 Parts: []WordPart{
830 {
831 Part: &tk[2],
832 Tokens: tk[2:3],
833 },
834 },
835 Tokens: tk[2:3],
836 },
837 },
838 Tokens: tk[2:3],
839 },
840 Tokens: tk[2:3],
841 },
842 Tokens: tk[2:3],
843 },
844 Tokens: tk[2:3],
845 },
846 JobControl: JobControlBackground,
847 Tokens: tk[:5],
848 }
849 }},
850 {"||", func(t *test, tk Tokens) { // 6
851 t.Err = Error{
852 Err: Error{
853 Err: Error{
854 Err: Error{
855 Err: ErrMissingWord,
856 Parsing: "Command",
857 Token: tk[0],
858 },
859 Parsing: "CommandOrCompound",
860 Token: tk[0],
861 },
862 Parsing: "Pipeline",
863 Token: tk[0],
864 },
865 Parsing: "Statement",
866 Token: tk[0],
867 }
868 }},
869 {"a || ||", func(t *test, tk Tokens) { // 7
870 t.Err = Error{
871 Err: Error{
872 Err: Error{
873 Err: Error{
874 Err: Error{
875 Err: ErrMissingWord,
876 Parsing: "Command",
877 Token: tk[4],
878 },
879 Parsing: "CommandOrCompound",
880 Token: tk[4],
881 },
882 Parsing: "Pipeline",
883 Token: tk[4],
884 },
885 Parsing: "Statement",
886 Token: tk[4],
887 },
888 Parsing: "Statement",
889 Token: tk[4],
890 }
891 }},
892 }, func(t *test) (Type, error) {
893 var s Statement
894
895 err := s.parse(t.Parser, true)
896
897 return s, err
898 })
899 }
900
901 func TestPipeline(t *testing.T) {
902 doTests(t, []sourceFn{
903 {"a", func(t *test, tk Tokens) { // 1
904 t.Output = Pipeline{
905 CommandOrCompound: CommandOrCompound{
906 Command: &Command{
907 Words: []Word{
908 {
909 Parts: []WordPart{
910 {
911 Part: &tk[0],
912 Tokens: tk[:1],
913 },
914 },
915 Tokens: tk[:1],
916 },
917 },
918 Tokens: tk[:1],
919 },
920 Tokens: tk[:1],
921 },
922 Tokens: tk[:1],
923 }
924 }},
925 {"time a", func(t *test, tk Tokens) { // 2
926 t.Output = Pipeline{
927 PipelineTime: PipelineTimeBash,
928 CommandOrCompound: CommandOrCompound{
929 Command: &Command{
930 Words: []Word{
931 {
932 Parts: []WordPart{
933 {
934 Part: &tk[2],
935 Tokens: tk[2:3],
936 },
937 },
938 Tokens: tk[2:3],
939 },
940 },
941 Tokens: tk[2:3],
942 },
943 Tokens: tk[2:3],
944 },
945 Tokens: tk[:3],
946 }
947 }},
948 {"time -p a", func(t *test, tk Tokens) { // 3
949 t.Output = Pipeline{
950 PipelineTime: PipelineTimePosix,
951 CommandOrCompound: CommandOrCompound{
952 Command: &Command{
953 Words: []Word{
954 {
955 Parts: []WordPart{
956 {
957 Part: &tk[4],
958 Tokens: tk[4:5],
959 },
960 },
961 Tokens: tk[4:5],
962 },
963 },
964 Tokens: tk[4:5],
965 },
966 Tokens: tk[4:5],
967 },
968 Tokens: tk[:5],
969 }
970 }},
971 {"! a", func(t *test, tk Tokens) { // 4
972 t.Output = Pipeline{
973 Not: true,
974 CommandOrCompound: CommandOrCompound{
975 Command: &Command{
976 Words: []Word{
977 {
978 Parts: []WordPart{
979 {
980 Part: &tk[2],
981 Tokens: tk[2:3],
982 },
983 },
984 Tokens: tk[2:3],
985 },
986 },
987 Tokens: tk[2:3],
988 },
989 Tokens: tk[2:3],
990 },
991 Tokens: tk[:3],
992 }
993 }},
994 {"a|b", func(t *test, tk Tokens) { // 5
995 t.Output = Pipeline{
996 CommandOrCompound: CommandOrCompound{
997 Command: &Command{
998 Words: []Word{
999 {
1000 Parts: []WordPart{
1001 {
1002 Part: &tk[0],
1003 Tokens: tk[:1],
1004 },
1005 },
1006 Tokens: tk[:1],
1007 },
1008 },
1009 Tokens: tk[:1],
1010 },
1011 Tokens: tk[:1],
1012 },
1013 Pipeline: &Pipeline{
1014 CommandOrCompound: CommandOrCompound{
1015 Command: &Command{
1016 Words: []Word{
1017 {
1018 Parts: []WordPart{
1019 {
1020 Part: &tk[2],
1021 Tokens: tk[2:3],
1022 },
1023 },
1024 Tokens: tk[2:3],
1025 },
1026 },
1027 Tokens: tk[2:3],
1028 },
1029 Tokens: tk[2:3],
1030 },
1031 Tokens: tk[2:3],
1032 },
1033 Tokens: tk[:3],
1034 }
1035 }},
1036 {"a | b", func(t *test, tk Tokens) { // 6
1037 t.Output = Pipeline{
1038 CommandOrCompound: CommandOrCompound{
1039 Command: &Command{
1040 Words: []Word{
1041 {
1042 Parts: []WordPart{
1043 {
1044 Part: &tk[0],
1045 Tokens: tk[:1],
1046 },
1047 },
1048 Tokens: tk[:1],
1049 },
1050 },
1051 Tokens: tk[:1],
1052 },
1053 Tokens: tk[:1],
1054 },
1055 Pipeline: &Pipeline{
1056 CommandOrCompound: CommandOrCompound{
1057 Command: &Command{
1058 Words: []Word{
1059 {
1060 Parts: []WordPart{
1061 {
1062 Part: &tk[4],
1063 Tokens: tk[4:5],
1064 },
1065 },
1066 Tokens: tk[4:5],
1067 },
1068 },
1069 Tokens: tk[4:5],
1070 },
1071 Tokens: tk[4:5],
1072 },
1073 Tokens: tk[4:5],
1074 },
1075 Tokens: tk[:5],
1076 }
1077 }},
1078 {"||", func(t *test, tk Tokens) { // 7
1079 t.Err = Error{
1080 Err: Error{
1081 Err: Error{
1082 Err: ErrMissingWord,
1083 Parsing: "Command",
1084 Token: tk[0],
1085 },
1086 Parsing: "CommandOrCompound",
1087 Token: tk[0],
1088 },
1089 Parsing: "Pipeline",
1090 Token: tk[0],
1091 }
1092 }},
1093 {"a | ||", func(t *test, tk Tokens) { // 8
1094 t.Err = Error{
1095 Err: Error{
1096 Err: Error{
1097 Err: Error{
1098 Err: ErrMissingWord,
1099 Parsing: "Command",
1100 Token: tk[4],
1101 },
1102 Parsing: "CommandOrCompound",
1103 Token: tk[4],
1104 },
1105 Parsing: "Pipeline",
1106 Token: tk[4],
1107 },
1108 Parsing: "Pipeline",
1109 Token: tk[4],
1110 }
1111 }},
1112 }, func(t *test) (Type, error) {
1113 var p Pipeline
1114
1115 err := p.parse(t.Parser, true)
1116
1117 return p, err
1118 })
1119 }
1120
1121 func TestCommand(t *testing.T) {
1122 doTests(t, []sourceFn{
1123 {"a", func(t *test, tk Tokens) { // 1
1124 t.Output = Command{
1125 Words: []Word{
1126 {
1127 Parts: []WordPart{
1128 {
1129 Part: &tk[0],
1130 Tokens: tk[:1],
1131 },
1132 },
1133 Tokens: tk[:1],
1134 },
1135 },
1136 Tokens: tk[:1],
1137 }
1138 }},
1139 {"a=", func(t *test, tk Tokens) { // 2
1140 t.Output = Command{
1141 Vars: []Assignment{
1142 {
1143 Identifier: ParameterAssign{
1144 Identifier: &tk[0],
1145 Tokens: tk[:1],
1146 },
1147 Value: Value{
1148 Word: &Word{
1149 Tokens: tk[2:2],
1150 },
1151 Tokens: tk[2:2],
1152 },
1153 Tokens: tk[:2],
1154 },
1155 },
1156 Tokens: tk[:2],
1157 }
1158 }},
1159 {"a=b c", func(t *test, tk Tokens) { // 3
1160 t.Output = Command{
1161 Vars: []Assignment{
1162 {
1163 Identifier: ParameterAssign{
1164 Identifier: &tk[0],
1165 Tokens: tk[:1],
1166 },
1167 Value: Value{
1168 Word: &Word{
1169 Parts: []WordPart{
1170 {
1171 Part: &tk[2],
1172 Tokens: tk[2:3],
1173 },
1174 },
1175 Tokens: tk[2:3],
1176 },
1177 Tokens: tk[2:3],
1178 },
1179 Tokens: tk[:3],
1180 },
1181 },
1182 Words: []Word{
1183 {
1184 Parts: []WordPart{
1185 {
1186 Part: &tk[4],
1187 Tokens: tk[4:5],
1188 },
1189 },
1190 Tokens: tk[4:5],
1191 },
1192 },
1193 Tokens: tk[:5],
1194 }
1195 }},
1196 {"a=b >c d", func(t *test, tk Tokens) { // 4
1197 t.Output = Command{
1198 Vars: []Assignment{
1199 {
1200 Identifier: ParameterAssign{
1201 Identifier: &tk[0],
1202 Tokens: tk[:1],
1203 },
1204 Value: Value{
1205 Word: &Word{
1206 Parts: []WordPart{
1207 {
1208 Part: &tk[2],
1209 Tokens: tk[2:3],
1210 },
1211 },
1212 Tokens: tk[2:3],
1213 },
1214 Tokens: tk[2:3],
1215 },
1216 Tokens: tk[:3],
1217 },
1218 },
1219 Redirections: []Redirection{
1220 {
1221 Redirector: &tk[4],
1222 Output: Word{
1223 Parts: []WordPart{
1224 {
1225 Part: &tk[5],
1226 Tokens: tk[5:6],
1227 },
1228 },
1229 Tokens: tk[5:6],
1230 },
1231 Tokens: tk[4:6],
1232 },
1233 },
1234 Words: []Word{
1235 {
1236 Parts: []WordPart{
1237 {
1238 Part: &tk[7],
1239 Tokens: tk[7:8],
1240 },
1241 },
1242 Tokens: tk[7:8],
1243 },
1244 },
1245 Tokens: tk[:8],
1246 }
1247 }},
1248 {"a=b c=d >e f g=h 2>i", func(t *test, tk Tokens) { // 5
1249 t.Output = Command{
1250 Vars: []Assignment{
1251 {
1252 Identifier: ParameterAssign{
1253 Identifier: &tk[0],
1254 Tokens: tk[:1],
1255 },
1256 Value: Value{
1257 Word: &Word{
1258 Parts: []WordPart{
1259 {
1260 Part: &tk[2],
1261 Tokens: tk[2:3],
1262 },
1263 },
1264 Tokens: tk[2:3],
1265 },
1266 Tokens: tk[2:3],
1267 },
1268 Tokens: tk[:3],
1269 },
1270 {
1271 Identifier: ParameterAssign{
1272 Identifier: &tk[4],
1273 Tokens: tk[4:5],
1274 },
1275 Value: Value{
1276 Word: &Word{
1277 Parts: []WordPart{
1278 {
1279 Part: &tk[6],
1280 Tokens: tk[6:7],
1281 },
1282 },
1283 Tokens: tk[6:7],
1284 },
1285 Tokens: tk[6:7],
1286 },
1287 Tokens: tk[4:7],
1288 },
1289 },
1290 Redirections: []Redirection{
1291 {
1292 Redirector: &tk[8],
1293 Output: Word{
1294 Parts: []WordPart{
1295 {
1296 Part: &tk[9],
1297 Tokens: tk[9:10],
1298 },
1299 },
1300 Tokens: tk[9:10],
1301 },
1302 Tokens: tk[8:10],
1303 },
1304 {
1305 Input: &tk[17],
1306 Redirector: &tk[18],
1307 Output: Word{
1308 Parts: []WordPart{
1309 {
1310 Part: &tk[19],
1311 Tokens: tk[19:20],
1312 },
1313 },
1314 Tokens: tk[19:20],
1315 },
1316 Tokens: tk[17:20],
1317 },
1318 },
1319 Words: []Word{
1320 {
1321 Parts: []WordPart{
1322 {
1323 Part: &tk[11],
1324 Tokens: tk[11:12],
1325 },
1326 },
1327 Tokens: tk[11:12],
1328 },
1329 {
1330 Parts: []WordPart{
1331 {
1332 Part: &tk[13],
1333 Tokens: tk[13:14],
1334 },
1335 {
1336 Part: &tk[14],
1337 Tokens: tk[14:15],
1338 },
1339 {
1340 Part: &tk[15],
1341 Tokens: tk[15:16],
1342 },
1343 },
1344 Tokens: tk[13:16],
1345 },
1346 },
1347 Tokens: tk[:20],
1348 }
1349 }},
1350 {"a[$(||)]=", func(t *test, tk Tokens) { // 6
1351 t.Err = Error{
1352 Err: Error{
1353 Err: Error{
1354 Err: Error{
1355 Err: Error{
1356 Err: Error{
1357 Err: Error{
1358 Err: Error{
1359 Err: Error{
1360 Err: Error{
1361 Err: Error{
1362 Err: Error{
1363 Err: ErrMissingWord,
1364 Parsing: "Command",
1365 Token: tk[3],
1366 },
1367 Parsing: "CommandOrCompound",
1368 Token: tk[3],
1369 },
1370 Parsing: "Pipeline",
1371 Token: tk[3],
1372 },
1373 Parsing: "Statement",
1374 Token: tk[3],
1375 },
1376 Parsing: "Line",
1377 Token: tk[3],
1378 },
1379 Parsing: "File",
1380 Token: tk[3],
1381 },
1382 Parsing: "CommandSubstitution",
1383 Token: tk[3],
1384 },
1385 Parsing: "WordPart",
1386 Token: tk[2],
1387 },
1388 Parsing: "Word",
1389 Token: tk[2],
1390 },
1391 Parsing: "ParameterAssign",
1392 Token: tk[2],
1393 },
1394 Parsing: "Assignment",
1395 Token: tk[0],
1396 },
1397 Parsing: "Command",
1398 Token: tk[0],
1399 }
1400 }},
1401 {">$(||)", func(t *test, tk Tokens) { // 7
1402 t.Err = Error{
1403 Err: Error{
1404 Err: Error{
1405 Err: Error{
1406 Err: Error{
1407 Err: Error{
1408 Err: Error{
1409 Err: Error{
1410 Err: Error{
1411 Err: Error{
1412 Err: Error{
1413 Err: ErrMissingWord,
1414 Parsing: "Command",
1415 Token: tk[2],
1416 },
1417 Parsing: "CommandOrCompound",
1418 Token: tk[2],
1419 },
1420 Parsing: "Pipeline",
1421 Token: tk[2],
1422 },
1423 Parsing: "Statement",
1424 Token: tk[2],
1425 },
1426 Parsing: "Line",
1427 Token: tk[2],
1428 },
1429 Parsing: "File",
1430 Token: tk[2],
1431 },
1432 Parsing: "CommandSubstitution",
1433 Token: tk[2],
1434 },
1435 Parsing: "WordPart",
1436 Token: tk[1],
1437 },
1438 Parsing: "Word",
1439 Token: tk[1],
1440 },
1441 Parsing: "Redirection",
1442 Token: tk[1],
1443 },
1444 Parsing: "Command",
1445 Token: tk[0],
1446 }
1447 }},
1448 {"$(||)", func(t *test, tk Tokens) { // 8
1449 t.Err = Error{
1450 Err: Error{
1451 Err: Error{
1452 Err: Error{
1453 Err: Error{
1454 Err: Error{
1455 Err: Error{
1456 Err: Error{
1457 Err: Error{
1458 Err: Error{
1459 Err: ErrMissingWord,
1460 Parsing: "Command",
1461 Token: tk[1],
1462 },
1463 Parsing: "CommandOrCompound",
1464 Token: tk[1],
1465 },
1466 Parsing: "Pipeline",
1467 Token: tk[1],
1468 },
1469 Parsing: "Statement",
1470 Token: tk[1],
1471 },
1472 Parsing: "Line",
1473 Token: tk[1],
1474 },
1475 Parsing: "File",
1476 Token: tk[1],
1477 },
1478 Parsing: "CommandSubstitution",
1479 Token: tk[1],
1480 },
1481 Parsing: "WordPart",
1482 Token: tk[0],
1483 },
1484 Parsing: "Word",
1485 Token: tk[0],
1486 },
1487 Parsing: "Command",
1488 Token: tk[0],
1489 }
1490 }},
1491 {"a >$(||)", func(t *test, tk Tokens) { // 9
1492 t.Err = Error{
1493 Err: Error{
1494 Err: Error{
1495 Err: Error{
1496 Err: Error{
1497 Err: Error{
1498 Err: Error{
1499 Err: Error{
1500 Err: Error{
1501 Err: Error{
1502 Err: Error{
1503 Err: ErrMissingWord,
1504 Parsing: "Command",
1505 Token: tk[4],
1506 },
1507 Parsing: "CommandOrCompound",
1508 Token: tk[4],
1509 },
1510 Parsing: "Pipeline",
1511 Token: tk[4],
1512 },
1513 Parsing: "Statement",
1514 Token: tk[4],
1515 },
1516 Parsing: "Line",
1517 Token: tk[4],
1518 },
1519 Parsing: "File",
1520 Token: tk[4],
1521 },
1522 Parsing: "CommandSubstitution",
1523 Token: tk[4],
1524 },
1525 Parsing: "WordPart",
1526 Token: tk[3],
1527 },
1528 Parsing: "Word",
1529 Token: tk[3],
1530 },
1531 Parsing: "Redirection",
1532 Token: tk[3],
1533 },
1534 Parsing: "Command",
1535 Token: tk[2],
1536 }
1537 }},
1538 }, func(t *test) (Type, error) {
1539 var c Command
1540
1541 err := c.parse(t.Parser, false)
1542
1543 return c, err
1544 })
1545 }
1546
1547 func TestAssignment(t *testing.T) {
1548 doTests(t, []sourceFn{
1549 {"a=", func(t *test, tk Tokens) { // 1
1550 t.Output = Assignment{
1551 Identifier: ParameterAssign{
1552 Identifier: &tk[0],
1553 Tokens: tk[:1],
1554 },
1555 Value: Value{
1556 Word: &Word{
1557 Tokens: tk[2:2],
1558 },
1559 Tokens: tk[2:2],
1560 },
1561 Tokens: tk[:2],
1562 }
1563 }},
1564 {"a+=b", func(t *test, tk Tokens) { // 2
1565 t.Output = Assignment{
1566 Identifier: ParameterAssign{
1567 Identifier: &tk[0],
1568 Tokens: tk[:1],
1569 },
1570 Assignment: AssignmentAppend,
1571 Value: Value{
1572 Word: &Word{
1573 Parts: []WordPart{
1574 {
1575 Part: &tk[2],
1576 Tokens: tk[2:3],
1577 },
1578 },
1579 Tokens: tk[2:3],
1580 },
1581 Tokens: tk[2:3],
1582 },
1583 Tokens: tk[:3],
1584 }
1585 }},
1586 {"a[$(||)]=", func(t *test, tk Tokens) { // 3
1587 t.Err = Error{
1588 Err: Error{
1589 Err: Error{
1590 Err: Error{
1591 Err: Error{
1592 Err: Error{
1593 Err: Error{
1594 Err: Error{
1595 Err: Error{
1596 Err: Error{
1597 Err: Error{
1598 Err: ErrMissingWord,
1599 Parsing: "Command",
1600 Token: tk[3],
1601 },
1602 Parsing: "CommandOrCompound",
1603 Token: tk[3],
1604 },
1605 Parsing: "Pipeline",
1606 Token: tk[3],
1607 },
1608 Parsing: "Statement",
1609 Token: tk[3],
1610 },
1611 Parsing: "Line",
1612 Token: tk[3],
1613 },
1614 Parsing: "File",
1615 Token: tk[3],
1616 },
1617 Parsing: "CommandSubstitution",
1618 Token: tk[3],
1619 },
1620 Parsing: "WordPart",
1621 Token: tk[2],
1622 },
1623 Parsing: "Word",
1624 Token: tk[2],
1625 },
1626 Parsing: "ParameterAssign",
1627 Token: tk[2],
1628 },
1629 Parsing: "Assignment",
1630 Token: tk[0],
1631 }
1632 }},
1633 {"a=$(||)", func(t *test, tk Tokens) { // 4
1634 t.Err = Error{
1635 Err: Error{
1636 Err: Error{
1637 Err: Error{
1638 Err: Error{
1639 Err: Error{
1640 Err: Error{
1641 Err: Error{
1642 Err: Error{
1643 Err: Error{
1644 Err: Error{
1645 Err: ErrMissingWord,
1646 Parsing: "Command",
1647 Token: tk[3],
1648 },
1649 Parsing: "CommandOrCompound",
1650 Token: tk[3],
1651 },
1652 Parsing: "Pipeline",
1653 Token: tk[3],
1654 },
1655 Parsing: "Statement",
1656 Token: tk[3],
1657 },
1658 Parsing: "Line",
1659 Token: tk[3],
1660 },
1661 Parsing: "File",
1662 Token: tk[3],
1663 },
1664 Parsing: "CommandSubstitution",
1665 Token: tk[3],
1666 },
1667 Parsing: "WordPart",
1668 Token: tk[2],
1669 },
1670 Parsing: "Word",
1671 Token: tk[2],
1672 },
1673 Parsing: "Value",
1674 Token: tk[2],
1675 },
1676 Parsing: "Assignment",
1677 Token: tk[2],
1678 }
1679 }},
1680 }, func(t *test) (Type, error) {
1681 var a Assignment
1682
1683 err := a.parse(t.Parser)
1684
1685 return a, err
1686 })
1687 }
1688
1689 func TestParameterAssign(t *testing.T) {
1690 doTests(t, []sourceFn{
1691 {"a=", func(t *test, tk Tokens) { // 1
1692 t.Output = ParameterAssign{
1693 Identifier: &tk[0],
1694 Tokens: tk[:1],
1695 }
1696 }},
1697 {"a[0]=", func(t *test, tk Tokens) { // 2
1698 t.Output = ParameterAssign{
1699 Identifier: &tk[0],
1700 Subscript: &Word{
1701 Parts: []WordPart{
1702 {
1703 Part: &tk[2],
1704 Tokens: tk[2:3],
1705 },
1706 },
1707 Tokens: tk[2:3],
1708 },
1709 Tokens: tk[:4],
1710 }
1711 }},
1712 {"a[$a]=", func(t *test, tk Tokens) { // 3
1713 t.Output = ParameterAssign{
1714 Identifier: &tk[0],
1715 Subscript: &Word{
1716 Parts: []WordPart{
1717 {
1718 Part: &tk[2],
1719 Tokens: tk[2:3],
1720 },
1721 },
1722 Tokens: tk[2:3],
1723 },
1724 Tokens: tk[:4],
1725 }
1726 }},
1727 {"a[$(||)]=", func(t *test, tk Tokens) { // 4
1728 t.Err = Error{
1729 Err: Error{
1730 Err: Error{
1731 Err: Error{
1732 Err: Error{
1733 Err: Error{
1734 Err: Error{
1735 Err: Error{
1736 Err: Error{
1737 Err: Error{
1738 Err: ErrMissingWord,
1739 Parsing: "Command",
1740 Token: tk[3],
1741 },
1742 Parsing: "CommandOrCompound",
1743 Token: tk[3],
1744 },
1745 Parsing: "Pipeline",
1746 Token: tk[3],
1747 },
1748 Parsing: "Statement",
1749 Token: tk[3],
1750 },
1751 Parsing: "Line",
1752 Token: tk[3],
1753 },
1754 Parsing: "File",
1755 Token: tk[3],
1756 },
1757 Parsing: "CommandSubstitution",
1758 Token: tk[3],
1759 },
1760 Parsing: "WordPart",
1761 Token: tk[2],
1762 },
1763 Parsing: "Word",
1764 Token: tk[2],
1765 },
1766 Parsing: "ParameterAssign",
1767 Token: tk[2],
1768 }
1769 }},
1770 {"a[0 1]=", func(t *test, tk Tokens) { // 5
1771 t.Err = Error{
1772 Err: ErrMissingClosingBracket,
1773 Parsing: "ParameterAssign",
1774 Token: tk[4],
1775 }
1776 }},
1777 }, func(t *test) (Type, error) {
1778 var pa ParameterAssign
1779
1780 err := pa.parse(t.Parser)
1781
1782 return pa, err
1783 })
1784 }
1785
1786 func TestValue(t *testing.T) {
1787 doTests(t, []sourceFn{
1788 {"a=b", func(t *test, tk Tokens) { // 1
1789 t.Output = Value{
1790 Word: &Word{
1791 Parts: []WordPart{
1792 {
1793 Part: &tk[2],
1794 Tokens: tk[2:3],
1795 },
1796 },
1797 Tokens: tk[2:3],
1798 },
1799 Tokens: tk[2:3],
1800 }
1801 }},
1802 {"a=()", func(t *test, tk Tokens) { // 2
1803 t.Output = Value{
1804 Array: []Word{},
1805 Tokens: tk[2:4],
1806 }
1807 }},
1808 {"a=(b)", func(t *test, tk Tokens) { // 3
1809 t.Output = Value{
1810 Array: []Word{
1811 {
1812 Parts: []WordPart{
1813 {
1814 Part: &tk[3],
1815 Tokens: tk[3:4],
1816 },
1817 },
1818 Tokens: tk[3:4],
1819 },
1820 },
1821 Tokens: tk[2:5],
1822 }
1823 }},
1824 {"a=( b )", func(t *test, tk Tokens) { // 4
1825 t.Output = Value{
1826 Array: []Word{
1827 {
1828 Parts: []WordPart{
1829 {
1830 Part: &tk[4],
1831 Tokens: tk[4:5],
1832 },
1833 },
1834 Tokens: tk[4:5],
1835 },
1836 },
1837 Tokens: tk[2:7],
1838 }
1839 }},
1840 {"a=( b c )", func(t *test, tk Tokens) { // 5
1841 t.Output = Value{
1842 Array: []Word{
1843 {
1844 Parts: []WordPart{
1845 {
1846 Part: &tk[4],
1847 Tokens: tk[4:5],
1848 },
1849 },
1850 Tokens: tk[4:5],
1851 },
1852 {
1853 Parts: []WordPart{
1854 {
1855 Part: &tk[6],
1856 Tokens: tk[6:7],
1857 },
1858 },
1859 Tokens: tk[6:7],
1860 },
1861 },
1862 Tokens: tk[2:9],
1863 }
1864 }},
1865 {"a=$(||)", func(t *test, tk Tokens) { // 6
1866 t.Err = Error{
1867 Err: Error{
1868 Err: Error{
1869 Err: Error{
1870 Err: Error{
1871 Err: Error{
1872 Err: Error{
1873 Err: Error{
1874 Err: Error{
1875 Err: Error{
1876 Err: ErrMissingWord,
1877 Parsing: "Command",
1878 Token: tk[3],
1879 },
1880 Parsing: "CommandOrCompound",
1881 Token: tk[3],
1882 },
1883 Parsing: "Pipeline",
1884 Token: tk[3],
1885 },
1886 Parsing: "Statement",
1887 Token: tk[3],
1888 },
1889 Parsing: "Line",
1890 Token: tk[3],
1891 },
1892 Parsing: "File",
1893 Token: tk[3],
1894 },
1895 Parsing: "CommandSubstitution",
1896 Token: tk[3],
1897 },
1898 Parsing: "WordPart",
1899 Token: tk[2],
1900 },
1901 Parsing: "Word",
1902 Token: tk[2],
1903 },
1904 Parsing: "Value",
1905 Token: tk[2],
1906 }
1907 }},
1908 {"a=($(||))", func(t *test, tk Tokens) { // 7
1909 t.Err = Error{
1910 Err: Error{
1911 Err: Error{
1912 Err: Error{
1913 Err: Error{
1914 Err: Error{
1915 Err: Error{
1916 Err: Error{
1917 Err: Error{
1918 Err: Error{
1919 Err: ErrMissingWord,
1920 Parsing: "Command",
1921 Token: tk[4],
1922 },
1923 Parsing: "CommandOrCompound",
1924 Token: tk[4],
1925 },
1926 Parsing: "Pipeline",
1927 Token: tk[4],
1928 },
1929 Parsing: "Statement",
1930 Token: tk[4],
1931 },
1932 Parsing: "Line",
1933 Token: tk[4],
1934 },
1935 Parsing: "File",
1936 Token: tk[4],
1937 },
1938 Parsing: "CommandSubstitution",
1939 Token: tk[4],
1940 },
1941 Parsing: "WordPart",
1942 Token: tk[3],
1943 },
1944 Parsing: "Word",
1945 Token: tk[3],
1946 },
1947 Parsing: "Value",
1948 Token: tk[3],
1949 }
1950 }},
1951 }, func(t *test) (Type, error) {
1952 var v Value
1953
1954 t.Parser.Tokens = t.Parser.Tokens[2:2]
1955
1956 err := v.parse(t.Parser)
1957
1958 return v, err
1959 })
1960 }
1961
1962 func TestWord(t *testing.T) {
1963 doTests(t, []sourceFn{
1964 {"a", func(t *test, tk Tokens) { // 1
1965 t.Output = Word{
1966 Parts: []WordPart{
1967 {
1968 Part: &tk[0],
1969 Tokens: tk[:1],
1970 },
1971 },
1972 Tokens: tk[:1],
1973 }
1974 }},
1975 {"a b", func(t *test, tk Tokens) { // 2
1976 t.Output = Word{
1977 Parts: []WordPart{
1978 {
1979 Part: &tk[0],
1980 Tokens: tk[:1],
1981 },
1982 },
1983 Tokens: tk[:1],
1984 }
1985 }},
1986 {"a\nb", func(t *test, tk Tokens) { // 3
1987 t.Output = Word{
1988 Parts: []WordPart{
1989 {
1990 Part: &tk[0],
1991 Tokens: tk[:1],
1992 },
1993 },
1994 Tokens: tk[:1],
1995 }
1996 }},
1997 {"a|b", func(t *test, tk Tokens) { // 4
1998 t.Output = Word{
1999 Parts: []WordPart{
2000 {
2001 Part: &tk[0],
2002 Tokens: tk[:1],
2003 },
2004 },
2005 Tokens: tk[:1],
2006 }
2007 }},
2008 {"a$b", func(t *test, tk Tokens) { // 5
2009 t.Output = Word{
2010 Parts: []WordPart{
2011 {
2012 Part: &tk[0],
2013 Tokens: tk[:1],
2014 },
2015 {
2016 Part: &tk[1],
2017 Tokens: tk[1:2],
2018 },
2019 },
2020 Tokens: tk[:2],
2021 }
2022 }},
2023 {"a$()", func(t *test, tk Tokens) { // 6
2024 t.Output = Word{
2025 Parts: []WordPart{
2026 {
2027 Part: &tk[0],
2028 Tokens: tk[:1],
2029 },
2030 {
2031 CommandSubstitution: &CommandSubstitution{
2032 Command: File{
2033 Tokens: tk[2:2],
2034 },
2035 Tokens: tk[1:3],
2036 },
2037 Tokens: tk[1:3],
2038 },
2039 },
2040 Tokens: tk[:3],
2041 }
2042 }},
2043 {"${a}b", func(t *test, tk Tokens) { // 7
2044 t.Output = Word{
2045 Parts: []WordPart{
2046 {
2047 ParameterExpansion: &ParameterExpansion{
2048 Parameter: Parameter{
2049 Parameter: &tk[1],
2050 Tokens: tk[1:2],
2051 },
2052 Tokens: tk[:3],
2053 },
2054 Tokens: tk[:3],
2055 },
2056 {
2057 Part: &tk[3],
2058 Tokens: tk[3:4],
2059 },
2060 },
2061 Tokens: tk[:4],
2062 }
2063 }},
2064 {"a$(())", func(t *test, tk Tokens) { // 8
2065 t.Output = Word{
2066 Parts: []WordPart{
2067 {
2068 Part: &tk[0],
2069 Tokens: tk[:1],
2070 },
2071 {
2072 ArithmeticExpansion: &ArithmeticExpansion{
2073 Tokens: tk[1:3],
2074 },
2075 Tokens: tk[1:3],
2076 },
2077 },
2078 Tokens: tk[:3],
2079 }
2080 }},
2081 {"$(||)", func(t *test, tk Tokens) { // 9
2082 t.Err = Error{
2083 Err: Error{
2084 Err: Error{
2085 Err: Error{
2086 Err: Error{
2087 Err: Error{
2088 Err: Error{
2089 Err: Error{
2090 Err: Error{
2091 Err: ErrMissingWord,
2092 Parsing: "Command",
2093 Token: tk[1],
2094 },
2095 Parsing: "CommandOrCompound",
2096 Token: tk[1],
2097 },
2098 Parsing: "Pipeline",
2099 Token: tk[1],
2100 },
2101 Parsing: "Statement",
2102 Token: tk[1],
2103 },
2104 Parsing: "Line",
2105 Token: tk[1],
2106 },
2107 Parsing: "File",
2108 Token: tk[1],
2109 },
2110 Parsing: "CommandSubstitution",
2111 Token: tk[1],
2112 },
2113 Parsing: "WordPart",
2114 Token: tk[0],
2115 },
2116 Parsing: "Word",
2117 Token: tk[0],
2118 }
2119 }},
2120 }, func(t *test) (Type, error) {
2121 var w Word
2122
2123 err := w.parse(t.Parser)
2124
2125 return w, err
2126 })
2127 }
2128
2129 func TestWordPart(t *testing.T) {
2130 doTests(t, []sourceFn{
2131 {"a", func(t *test, tk Tokens) { // 1
2132 t.Output = WordPart{
2133 Part: &tk[0],
2134 Tokens: tk[:1],
2135 }
2136 }},
2137 {"${a}", func(t *test, tk Tokens) { // 2
2138 t.Output = WordPart{
2139 ParameterExpansion: &ParameterExpansion{
2140 Parameter: Parameter{
2141 Parameter: &tk[1],
2142 Tokens: tk[1:2],
2143 },
2144 Tokens: tk[:3],
2145 },
2146 Tokens: tk[:3],
2147 }
2148 }},
2149 {"$()", func(t *test, tk Tokens) { // 3
2150 t.Output = WordPart{
2151 CommandSubstitution: &CommandSubstitution{
2152 Command: File{
2153 Tokens: tk[1:1],
2154 },
2155 Tokens: tk[:2],
2156 },
2157 Tokens: tk[:2],
2158 }
2159 }},
2160 {"``", func(t *test, tk Tokens) { // 4
2161 t.Output = WordPart{
2162 CommandSubstitution: &CommandSubstitution{
2163 SubstitutionType: SubstitutionBacktick,
2164 Command: File{
2165 Tokens: tk[1:1],
2166 },
2167 Tokens: tk[:2],
2168 },
2169 Tokens: tk[:2],
2170 }
2171 }},
2172 {"$(())", func(t *test, tk Tokens) { // 5
2173 t.Output = WordPart{
2174 ArithmeticExpansion: &ArithmeticExpansion{
2175 Tokens: tk[:2],
2176 },
2177 Tokens: tk[:2],
2178 }
2179 }},
2180 {"${a[$(||)]}", func(t *test, tk Tokens) { // 6
2181 t.Err = Error{
2182 Err: Error{
2183 Err: Error{
2184 Err: Error{
2185 Err: Error{
2186 Err: Error{
2187 Err: Error{
2188 Err: Error{
2189 Err: Error{
2190 Err: Error{
2191 Err: Error{
2192 Err: Error{
2193 Err: ErrMissingWord,
2194 Parsing: "Command",
2195 Token: tk[4],
2196 },
2197 Parsing: "CommandOrCompound",
2198 Token: tk[4],
2199 },
2200 Parsing: "Pipeline",
2201 Token: tk[4],
2202 },
2203 Parsing: "Statement",
2204 Token: tk[4],
2205 },
2206 Parsing: "Line",
2207 Token: tk[4],
2208 },
2209 Parsing: "File",
2210 Token: tk[4],
2211 },
2212 Parsing: "CommandSubstitution",
2213 Token: tk[4],
2214 },
2215 Parsing: "WordPart",
2216 Token: tk[3],
2217 },
2218 Parsing: "Word",
2219 Token: tk[3],
2220 },
2221 Parsing: "Parameter",
2222 Token: tk[3],
2223 },
2224 Parsing: "ParameterExpansion",
2225 Token: tk[1],
2226 },
2227 Parsing: "WordPart",
2228 Token: tk[0],
2229 }
2230 }},
2231 {"$(($(||)))", func(t *test, tk Tokens) { // 7
2232 t.Err = Error{
2233 Err: Error{
2234 Err: Error{
2235 Err: Error{
2236 Err: Error{
2237 Err: Error{
2238 Err: Error{
2239 Err: Error{
2240 Err: Error{
2241 Err: Error{
2242 Err: Error{
2243 Err: Error{
2244 Err: ErrMissingWord,
2245 Parsing: "Command",
2246 Token: tk[2],
2247 },
2248 Parsing: "CommandOrCompound",
2249 Token: tk[2],
2250 },
2251 Parsing: "Pipeline",
2252 Token: tk[2],
2253 },
2254 Parsing: "Statement",
2255 Token: tk[2],
2256 },
2257 Parsing: "Line",
2258 Token: tk[2],
2259 },
2260 Parsing: "File",
2261 Token: tk[2],
2262 },
2263 Parsing: "CommandSubstitution",
2264 Token: tk[2],
2265 },
2266 Parsing: "WordPart",
2267 Token: tk[1],
2268 },
2269 Parsing: "Word",
2270 Token: tk[1],
2271 },
2272 Parsing: "WordOrOperator",
2273 Token: tk[1],
2274 },
2275 Parsing: "ArithmeticExpansion",
2276 Token: tk[1],
2277 },
2278 Parsing: "WordPart",
2279 Token: tk[0],
2280 }
2281 }},
2282 {"$(||)", func(t *test, tk Tokens) { // 8
2283 t.Err = Error{
2284 Err: Error{
2285 Err: Error{
2286 Err: Error{
2287 Err: Error{
2288 Err: Error{
2289 Err: Error{
2290 Err: Error{
2291 Err: ErrMissingWord,
2292 Parsing: "Command",
2293 Token: tk[1],
2294 },
2295 Parsing: "CommandOrCompound",
2296 Token: tk[1],
2297 },
2298 Parsing: "Pipeline",
2299 Token: tk[1],
2300 },
2301 Parsing: "Statement",
2302 Token: tk[1],
2303 },
2304 Parsing: "Line",
2305 Token: tk[1],
2306 },
2307 Parsing: "File",
2308 Token: tk[1],
2309 },
2310 Parsing: "CommandSubstitution",
2311 Token: tk[1],
2312 },
2313 Parsing: "WordPart",
2314 Token: tk[0],
2315 }
2316 }},
2317 }, func(t *test) (Type, error) {
2318 var wp WordPart
2319
2320 err := wp.parse(t.Parser)
2321
2322 return wp, err
2323 })
2324 }
2325
2326 func TestParameterExpansion(t *testing.T) {
2327 doTests(t, []sourceFn{
2328 {"${a}", func(t *test, tk Tokens) { // 1
2329 t.Output = ParameterExpansion{
2330 Parameter: Parameter{
2331 Parameter: &tk[1],
2332 Tokens: tk[1:2],
2333 },
2334 Tokens: tk[:3],
2335 }
2336 }},
2337 {"${a[0]}", func(t *test, tk Tokens) { // 2
2338 t.Output = ParameterExpansion{
2339 Parameter: Parameter{
2340 Parameter: &tk[1],
2341 Array: &Word{
2342 Parts: []WordPart{
2343 {
2344 Part: &tk[3],
2345 Tokens: tk[3:4],
2346 },
2347 },
2348 Tokens: tk[3:4],
2349 },
2350 Tokens: tk[1:5],
2351 },
2352 Tokens: tk[:6],
2353 }
2354 }},
2355 {"${@}", func(t *test, tk Tokens) { // 3
2356 t.Output = ParameterExpansion{
2357 Parameter: Parameter{
2358 Parameter: &tk[1],
2359 Tokens: tk[1:2],
2360 },
2361 Tokens: tk[:3],
2362 }
2363 }},
2364 {"${*}", func(t *test, tk Tokens) { // 4
2365 t.Output = ParameterExpansion{
2366 Parameter: Parameter{
2367 Parameter: &tk[1],
2368 Tokens: tk[1:2],
2369 },
2370 Tokens: tk[:3],
2371 }
2372 }},
2373 {"${!}", func(t *test, tk Tokens) { // 5
2374 t.Output = ParameterExpansion{
2375 Parameter: Parameter{
2376 Parameter: &tk[1],
2377 Tokens: tk[1:2],
2378 },
2379 Tokens: tk[:3],
2380 }
2381 }},
2382 {"${0}", func(t *test, tk Tokens) { // 6
2383 t.Output = ParameterExpansion{
2384 Parameter: Parameter{
2385 Parameter: &tk[1],
2386 Tokens: tk[1:2],
2387 },
2388 Tokens: tk[:3],
2389 }
2390 }},
2391 {"${99}", func(t *test, tk Tokens) { // 7
2392 t.Output = ParameterExpansion{
2393 Parameter: Parameter{
2394 Parameter: &tk[1],
2395 Tokens: tk[1:2],
2396 },
2397 Tokens: tk[:3],
2398 }
2399 }},
2400 {"${#a}", func(t *test, tk Tokens) { // 8
2401 t.Output = ParameterExpansion{
2402 Type: ParameterLength,
2403 Parameter: Parameter{
2404 Parameter: &tk[2],
2405 Tokens: tk[2:3],
2406 },
2407 Tokens: tk[:4],
2408 }
2409 }},
2410 {"${!a}", func(t *test, tk Tokens) { // 9
2411 t.Output = ParameterExpansion{
2412 Indirect: true,
2413 Parameter: Parameter{
2414 Parameter: &tk[2],
2415 Tokens: tk[2:3],
2416 },
2417 Tokens: tk[:4],
2418 }
2419 }},
2420 {"${a:=b}", func(t *test, tk Tokens) { // 10
2421 t.Output = ParameterExpansion{
2422 Type: ParameterSubstitution,
2423 Parameter: Parameter{
2424 Parameter: &tk[1],
2425 Tokens: tk[1:2],
2426 },
2427 Word: &Word{
2428 Parts: []WordPart{
2429 {
2430 Part: &tk[3],
2431 Tokens: tk[3:4],
2432 },
2433 },
2434 Tokens: tk[3:4],
2435 },
2436 Tokens: tk[:5],
2437 }
2438 }},
2439 {"${a:?b}", func(t *test, tk Tokens) { // 11
2440 t.Output = ParameterExpansion{
2441 Type: ParameterAssignment,
2442 Parameter: Parameter{
2443 Parameter: &tk[1],
2444 Tokens: tk[1:2],
2445 },
2446 Word: &Word{
2447 Parts: []WordPart{
2448 {
2449 Part: &tk[3],
2450 Tokens: tk[3:4],
2451 },
2452 },
2453 Tokens: tk[3:4],
2454 },
2455 Tokens: tk[:5],
2456 }
2457 }},
2458 {"${a:+b}", func(t *test, tk Tokens) { // 12
2459 t.Output = ParameterExpansion{
2460 Type: ParameterMessage,
2461 Parameter: Parameter{
2462 Parameter: &tk[1],
2463 Tokens: tk[1:2],
2464 },
2465 Word: &Word{
2466 Parts: []WordPart{
2467 {
2468 Part: &tk[3],
2469 Tokens: tk[3:4],
2470 },
2471 },
2472 Tokens: tk[3:4],
2473 },
2474 Tokens: tk[:5],
2475 }
2476 }},
2477 {"${a:-b}", func(t *test, tk Tokens) { // 13
2478 t.Output = ParameterExpansion{
2479 Type: ParameterSetAssign,
2480 Parameter: Parameter{
2481 Parameter: &tk[1],
2482 Tokens: tk[1:2],
2483 },
2484 Word: &Word{
2485 Parts: []WordPart{
2486 {
2487 Part: &tk[3],
2488 Tokens: tk[3:4],
2489 },
2490 },
2491 Tokens: tk[3:4],
2492 },
2493 Tokens: tk[:5],
2494 }
2495 }},
2496 {"${a:1}", func(t *test, tk Tokens) { // 14
2497 t.Output = ParameterExpansion{
2498 Type: ParameterSubstring,
2499 Parameter: Parameter{
2500 Parameter: &tk[1],
2501 Tokens: tk[1:2],
2502 },
2503 SubstringStart: &tk[3],
2504 Tokens: tk[:5],
2505 }
2506 }},
2507 {"${a: 1}", func(t *test, tk Tokens) { // 15
2508 t.Output = ParameterExpansion{
2509 Type: ParameterSubstring,
2510 Parameter: Parameter{
2511 Parameter: &tk[1],
2512 Tokens: tk[1:2],
2513 },
2514 SubstringStart: &tk[4],
2515 Tokens: tk[:6],
2516 }
2517 }},
2518 {"${a: -1}", func(t *test, tk Tokens) { // 16
2519 t.Output = ParameterExpansion{
2520 Type: ParameterSubstring,
2521 Parameter: Parameter{
2522 Parameter: &tk[1],
2523 Tokens: tk[1:2],
2524 },
2525 SubstringStart: &tk[4],
2526 Tokens: tk[:6],
2527 }
2528 }},
2529 {"${a:1:2}", func(t *test, tk Tokens) { // 17
2530 t.Output = ParameterExpansion{
2531 Type: ParameterSubstring,
2532 Parameter: Parameter{
2533 Parameter: &tk[1],
2534 Tokens: tk[1:2],
2535 },
2536 SubstringStart: &tk[3],
2537 SubstringEnd: &tk[5],
2538 Tokens: tk[:7],
2539 }
2540 }},
2541 {"${a:1:-2}", func(t *test, tk Tokens) { // 18
2542 t.Output = ParameterExpansion{
2543 Type: ParameterSubstring,
2544 Parameter: Parameter{
2545 Parameter: &tk[1],
2546 Tokens: tk[1:2],
2547 },
2548 SubstringStart: &tk[3],
2549 SubstringEnd: &tk[5],
2550 Tokens: tk[:7],
2551 }
2552 }},
2553 {"${a:1: -2}", func(t *test, tk Tokens) { // 19
2554 t.Output = ParameterExpansion{
2555 Type: ParameterSubstring,
2556 Parameter: Parameter{
2557 Parameter: &tk[1],
2558 Tokens: tk[1:2],
2559 },
2560 SubstringStart: &tk[3],
2561 SubstringEnd: &tk[6],
2562 Tokens: tk[:8],
2563 }
2564 }},
2565 {"${a#b}", func(t *test, tk Tokens) { // 20
2566 t.Output = ParameterExpansion{
2567 Type: ParameterRemoveStartShortest,
2568 Parameter: Parameter{
2569 Parameter: &tk[1],
2570 Tokens: tk[1:2],
2571 },
2572 Word: &Word{
2573 Parts: []WordPart{
2574 {
2575 Part: &tk[3],
2576 Tokens: tk[3:4],
2577 },
2578 },
2579 Tokens: tk[3:4],
2580 },
2581 Tokens: tk[:5],
2582 }
2583 }},
2584 {"${a##b}", func(t *test, tk Tokens) { // 21
2585 t.Output = ParameterExpansion{
2586 Type: ParameterRemoveStartLongest,
2587 Parameter: Parameter{
2588 Parameter: &tk[1],
2589 Tokens: tk[1:2],
2590 },
2591 Word: &Word{
2592 Parts: []WordPart{
2593 {
2594 Part: &tk[3],
2595 Tokens: tk[3:4],
2596 },
2597 },
2598 Tokens: tk[3:4],
2599 },
2600 Tokens: tk[:5],
2601 }
2602 }},
2603 {"${a%b}", func(t *test, tk Tokens) { // 22
2604 t.Output = ParameterExpansion{
2605 Type: ParameterRemoveEndShortest,
2606 Parameter: Parameter{
2607 Parameter: &tk[1],
2608 Tokens: tk[1:2],
2609 },
2610 Word: &Word{
2611 Parts: []WordPart{
2612 {
2613 Part: &tk[3],
2614 Tokens: tk[3:4],
2615 },
2616 },
2617 Tokens: tk[3:4],
2618 },
2619 Tokens: tk[:5],
2620 }
2621 }},
2622 {"${a%%b}", func(t *test, tk Tokens) { // 23
2623 t.Output = ParameterExpansion{
2624 Type: ParameterRemoveEndLongest,
2625 Parameter: Parameter{
2626 Parameter: &tk[1],
2627 Tokens: tk[1:2],
2628 },
2629 Word: &Word{
2630 Parts: []WordPart{
2631 {
2632 Part: &tk[3],
2633 Tokens: tk[3:4],
2634 },
2635 },
2636 Tokens: tk[3:4],
2637 },
2638 Tokens: tk[:5],
2639 }
2640 }},
2641 {"${a/b}", func(t *test, tk Tokens) { // 24
2642 t.Output = ParameterExpansion{
2643 Type: ParameterReplace,
2644 Parameter: Parameter{
2645 Parameter: &tk[1],
2646 Tokens: tk[1:2],
2647 },
2648 Pattern: &tk[3],
2649 Tokens: tk[:5],
2650 }
2651 }},
2652 {"${a/b/c}", func(t *test, tk Tokens) { // 25
2653 t.Output = ParameterExpansion{
2654 Type: ParameterReplace,
2655 Parameter: Parameter{
2656 Parameter: &tk[1],
2657 Tokens: tk[1:2],
2658 },
2659 Pattern: &tk[3],
2660 String: &String{
2661 WordsOrTokens: []WordOrToken{
2662 {
2663 Word: &Word{
2664 Parts: []WordPart{
2665 {
2666 Part: &tk[5],
2667 Tokens: tk[5:6],
2668 },
2669 },
2670 Tokens: tk[5:6],
2671 },
2672 Tokens: tk[5:6],
2673 },
2674 },
2675 Tokens: tk[5:6],
2676 },
2677 Tokens: tk[:7],
2678 }
2679 }},
2680 {"${a//b}", func(t *test, tk Tokens) { // 26
2681 t.Output = ParameterExpansion{
2682 Type: ParameterReplaceAll,
2683 Parameter: Parameter{
2684 Parameter: &tk[1],
2685 Tokens: tk[1:2],
2686 },
2687 Pattern: &tk[3],
2688 Tokens: tk[:5],
2689 }
2690 }},
2691 {"${a//b/c}", func(t *test, tk Tokens) { // 27
2692 t.Output = ParameterExpansion{
2693 Type: ParameterReplaceAll,
2694 Parameter: Parameter{
2695 Parameter: &tk[1],
2696 Tokens: tk[1:2],
2697 },
2698 Pattern: &tk[3],
2699 String: &String{
2700 WordsOrTokens: []WordOrToken{
2701 {
2702 Word: &Word{
2703 Parts: []WordPart{
2704 {
2705 Part: &tk[5],
2706 Tokens: tk[5:6],
2707 },
2708 },
2709 Tokens: tk[5:6],
2710 },
2711 Tokens: tk[5:6],
2712 },
2713 },
2714 Tokens: tk[5:6],
2715 },
2716 Tokens: tk[:7],
2717 }
2718 }},
2719 {"${a/#b}", func(t *test, tk Tokens) { // 28
2720 t.Output = ParameterExpansion{
2721 Type: ParameterReplaceStart,
2722 Parameter: Parameter{
2723 Parameter: &tk[1],
2724 Tokens: tk[1:2],
2725 },
2726 Pattern: &tk[3],
2727 Tokens: tk[:5],
2728 }
2729 }},
2730 {"${a/#b/c}", func(t *test, tk Tokens) { // 29
2731 t.Output = ParameterExpansion{
2732 Type: ParameterReplaceStart,
2733 Parameter: Parameter{
2734 Parameter: &tk[1],
2735 Tokens: tk[1:2],
2736 },
2737 Pattern: &tk[3],
2738 String: &String{
2739 WordsOrTokens: []WordOrToken{
2740 {
2741 Word: &Word{
2742 Parts: []WordPart{
2743 {
2744 Part: &tk[5],
2745 Tokens: tk[5:6],
2746 },
2747 },
2748 Tokens: tk[5:6],
2749 },
2750 Tokens: tk[5:6],
2751 },
2752 },
2753 Tokens: tk[5:6],
2754 },
2755 Tokens: tk[:7],
2756 }
2757 }},
2758 {"${a/%b}", func(t *test, tk Tokens) { // 30
2759 t.Output = ParameterExpansion{
2760 Type: ParameterReplaceEnd,
2761 Parameter: Parameter{
2762 Parameter: &tk[1],
2763 Tokens: tk[1:2],
2764 },
2765 Pattern: &tk[3],
2766 Tokens: tk[:5],
2767 }
2768 }},
2769 {"${a/%b/c}", func(t *test, tk Tokens) { // 31
2770 t.Output = ParameterExpansion{
2771 Type: ParameterReplaceEnd,
2772 Parameter: Parameter{
2773 Parameter: &tk[1],
2774 Tokens: tk[1:2],
2775 },
2776 Pattern: &tk[3],
2777 String: &String{
2778 WordsOrTokens: []WordOrToken{
2779 {
2780 Word: &Word{
2781 Parts: []WordPart{
2782 {
2783 Part: &tk[5],
2784 Tokens: tk[5:6],
2785 },
2786 },
2787 Tokens: tk[5:6],
2788 },
2789 Tokens: tk[5:6],
2790 },
2791 },
2792 Tokens: tk[5:6],
2793 },
2794 Tokens: tk[:7],
2795 }
2796 }},
2797 {"${a^b}", func(t *test, tk Tokens) { // 32
2798 t.Output = ParameterExpansion{
2799 Type: ParameterUppercaseFirstMatch,
2800 Parameter: Parameter{
2801 Parameter: &tk[1],
2802 Tokens: tk[1:2],
2803 },
2804 Pattern: &tk[3],
2805 Tokens: tk[:5],
2806 }
2807 }},
2808 {"${a^^b}", func(t *test, tk Tokens) { // 33
2809 t.Output = ParameterExpansion{
2810 Type: ParameterUppercaseAllMatches,
2811 Parameter: Parameter{
2812 Parameter: &tk[1],
2813 Tokens: tk[1:2],
2814 },
2815 Pattern: &tk[3],
2816 Tokens: tk[:5],
2817 }
2818 }},
2819 {"${a,b}", func(t *test, tk Tokens) { // 34
2820 t.Output = ParameterExpansion{
2821 Type: ParameterLowercaseFirstMatch,
2822 Parameter: Parameter{
2823 Parameter: &tk[1],
2824 Tokens: tk[1:2],
2825 },
2826 Pattern: &tk[3],
2827 Tokens: tk[:5],
2828 }
2829 }},
2830 {"${a,,b}", func(t *test, tk Tokens) { // 35
2831 t.Output = ParameterExpansion{
2832 Type: ParameterLowercaseAllMatches,
2833 Parameter: Parameter{
2834 Parameter: &tk[1],
2835 Tokens: tk[1:2],
2836 },
2837 Pattern: &tk[3],
2838 Tokens: tk[:5],
2839 }
2840 }},
2841 {"${!a@}", func(t *test, tk Tokens) { // 36
2842 t.Output = ParameterExpansion{
2843 Type: ParameterPrefixSeperate,
2844 Parameter: Parameter{
2845 Parameter: &tk[2],
2846 Tokens: tk[2:3],
2847 },
2848 Tokens: tk[:5],
2849 }
2850 }},
2851 {"${!a*}", func(t *test, tk Tokens) { // 37
2852 t.Output = ParameterExpansion{
2853 Type: ParameterPrefix,
2854 Parameter: Parameter{
2855 Parameter: &tk[2],
2856 Tokens: tk[2:3],
2857 },
2858 Tokens: tk[:5],
2859 }
2860 }},
2861 {"${a@U}", func(t *test, tk Tokens) { // 38
2862 t.Output = ParameterExpansion{
2863 Type: ParameterUppercase,
2864 Parameter: Parameter{
2865 Parameter: &tk[1],
2866 Tokens: tk[1:2],
2867 },
2868 Tokens: tk[:5],
2869 }
2870 }},
2871 {"${a@u}", func(t *test, tk Tokens) { // 39
2872 t.Output = ParameterExpansion{
2873 Type: ParameterUppercaseFirst,
2874 Parameter: Parameter{
2875 Parameter: &tk[1],
2876 Tokens: tk[1:2],
2877 },
2878 Tokens: tk[:5],
2879 }
2880 }},
2881 {"${a@L}", func(t *test, tk Tokens) { // 40
2882 t.Output = ParameterExpansion{
2883 Type: ParameterLowercase,
2884 Parameter: Parameter{
2885 Parameter: &tk[1],
2886 Tokens: tk[1:2],
2887 },
2888 Tokens: tk[:5],
2889 }
2890 }},
2891 {"${a@Q}", func(t *test, tk Tokens) { // 41
2892 t.Output = ParameterExpansion{
2893 Type: ParameterQuoted,
2894 Parameter: Parameter{
2895 Parameter: &tk[1],
2896 Tokens: tk[1:2],
2897 },
2898 Tokens: tk[:5],
2899 }
2900 }},
2901 {"${a@E}", func(t *test, tk Tokens) { // 42
2902 t.Output = ParameterExpansion{
2903 Type: ParameterEscaped,
2904 Parameter: Parameter{
2905 Parameter: &tk[1],
2906 Tokens: tk[1:2],
2907 },
2908 Tokens: tk[:5],
2909 }
2910 }},
2911 {"${a@P}", func(t *test, tk Tokens) { // 43
2912 t.Output = ParameterExpansion{
2913 Type: ParameterPrompt,
2914 Parameter: Parameter{
2915 Parameter: &tk[1],
2916 Tokens: tk[1:2],
2917 },
2918 Tokens: tk[:5],
2919 }
2920 }},
2921 {"${a@A}", func(t *test, tk Tokens) { // 44
2922 t.Output = ParameterExpansion{
2923 Type: ParameterDeclare,
2924 Parameter: Parameter{
2925 Parameter: &tk[1],
2926 Tokens: tk[1:2],
2927 },
2928 Tokens: tk[:5],
2929 }
2930 }},
2931 {"${a@K}", func(t *test, tk Tokens) { // 45
2932 t.Output = ParameterExpansion{
2933 Type: ParameterQuotedArrays,
2934 Parameter: Parameter{
2935 Parameter: &tk[1],
2936 Tokens: tk[1:2],
2937 },
2938 Tokens: tk[:5],
2939 }
2940 }},
2941 {"${a@a}", func(t *test, tk Tokens) { // 46
2942 t.Output = ParameterExpansion{
2943 Type: ParameterAttributes,
2944 Parameter: Parameter{
2945 Parameter: &tk[1],
2946 Tokens: tk[1:2],
2947 },
2948 Tokens: tk[:5],
2949 }
2950 }},
2951 {"${a@k}", func(t *test, tk Tokens) { // 47
2952 t.Output = ParameterExpansion{
2953 Type: ParameterQuotedArraysSeperate,
2954 Parameter: Parameter{
2955 Parameter: &tk[1],
2956 Tokens: tk[1:2],
2957 },
2958 Tokens: tk[:5],
2959 }
2960 }},
2961 {"${a[$(||)]}", func(t *test, tk Tokens) { // 48
2962 t.Err = Error{
2963 Err: Error{
2964 Err: Error{
2965 Err: Error{
2966 Err: Error{
2967 Err: Error{
2968 Err: Error{
2969 Err: Error{
2970 Err: Error{
2971 Err: Error{
2972 Err: Error{
2973 Err: ErrMissingWord,
2974 Parsing: "Command",
2975 Token: tk[4],
2976 },
2977 Parsing: "CommandOrCompound",
2978 Token: tk[4],
2979 },
2980 Parsing: "Pipeline",
2981 Token: tk[4],
2982 },
2983 Parsing: "Statement",
2984 Token: tk[4],
2985 },
2986 Parsing: "Line",
2987 Token: tk[4],
2988 },
2989 Parsing: "File",
2990 Token: tk[4],
2991 },
2992 Parsing: "CommandSubstitution",
2993 Token: tk[4],
2994 },
2995 Parsing: "WordPart",
2996 Token: tk[3],
2997 },
2998 Parsing: "Word",
2999 Token: tk[3],
3000 },
3001 Parsing: "Parameter",
3002 Token: tk[3],
3003 },
3004 Parsing: "ParameterExpansion",
3005 Token: tk[1],
3006 }
3007 }},
3008 {"${a:=$(||)}", func(t *test, tk Tokens) { // 49
3009 t.Err = Error{
3010 Err: Error{
3011 Err: Error{
3012 Err: Error{
3013 Err: Error{
3014 Err: Error{
3015 Err: Error{
3016 Err: Error{
3017 Err: Error{
3018 Err: Error{
3019 Err: ErrMissingWord,
3020 Parsing: "Command",
3021 Token: tk[4],
3022 },
3023 Parsing: "CommandOrCompound",
3024 Token: tk[4],
3025 },
3026 Parsing: "Pipeline",
3027 Token: tk[4],
3028 },
3029 Parsing: "Statement",
3030 Token: tk[4],
3031 },
3032 Parsing: "Line",
3033 Token: tk[4],
3034 },
3035 Parsing: "File",
3036 Token: tk[4],
3037 },
3038 Parsing: "CommandSubstitution",
3039 Token: tk[4],
3040 },
3041 Parsing: "WordPart",
3042 Token: tk[3],
3043 },
3044 Parsing: "Word",
3045 Token: tk[3],
3046 },
3047 Parsing: "ParameterExpansion",
3048 Token: tk[3],
3049 }
3050 }},
3051 {"${a:1:2b}", func(t *test, tk Tokens) { // 50
3052 t.Err = Error{
3053 Err: ErrMissingClosingBrace,
3054 Parsing: "ParameterExpansion",
3055 Token: tk[6],
3056 }
3057 }},
3058 }, func(t *test) (Type, error) {
3059 var pe ParameterExpansion
3060
3061 err := pe.parse(t.Parser)
3062
3063 return pe, err
3064 })
3065 }
3066
3067 func TestParameter(t *testing.T) {
3068 doTests(t, []sourceFn{
3069 {"${a}", func(t *test, tk Tokens) { // 1
3070 t.Output = Parameter{
3071 Parameter: &tk[1],
3072 Tokens: tk[1:2],
3073 }
3074 }},
3075 {"${0}", func(t *test, tk Tokens) { // 2
3076 t.Output = Parameter{
3077 Parameter: &tk[1],
3078 Tokens: tk[1:2],
3079 }
3080 }},
3081 {"${9}", func(t *test, tk Tokens) { // 3
3082 t.Output = Parameter{
3083 Parameter: &tk[1],
3084 Tokens: tk[1:2],
3085 }
3086 }},
3087 {"${@}", func(t *test, tk Tokens) { // 4
3088 t.Output = Parameter{
3089 Parameter: &tk[1],
3090 Tokens: tk[1:2],
3091 }
3092 }},
3093 {"${*}", func(t *test, tk Tokens) { // 5
3094 t.Output = Parameter{
3095 Parameter: &tk[1],
3096 Tokens: tk[1:2],
3097 }
3098 }},
3099 {"${a[0]}", func(t *test, tk Tokens) { // 6
3100 t.Output = Parameter{
3101 Parameter: &tk[1],
3102 Array: &Word{
3103 Parts: []WordPart{
3104 {
3105 Part: &tk[3],
3106 Tokens: tk[3:4],
3107 },
3108 },
3109 Tokens: tk[3:4],
3110 },
3111 Tokens: tk[1:5],
3112 }
3113 }},
3114 {"${a[@]}", func(t *test, tk Tokens) { // 7
3115 t.Output = Parameter{
3116 Parameter: &tk[1],
3117 Array: &Word{
3118 Parts: []WordPart{
3119 {
3120 Part: &tk[3],
3121 Tokens: tk[3:4],
3122 },
3123 },
3124 Tokens: tk[3:4],
3125 },
3126 Tokens: tk[1:5],
3127 }
3128 }},
3129 {"${a[*]}", func(t *test, tk Tokens) { // 8
3130 t.Output = Parameter{
3131 Parameter: &tk[1],
3132 Array: &Word{
3133 Parts: []WordPart{
3134 {
3135 Part: &tk[3],
3136 Tokens: tk[3:4],
3137 },
3138 },
3139 Tokens: tk[3:4],
3140 },
3141 Tokens: tk[1:5],
3142 }
3143 }},
3144 {"${a[$(||)]}", func(t *test, tk Tokens) { // 9
3145 t.Err = Error{
3146 Err: Error{
3147 Err: Error{
3148 Err: Error{
3149 Err: Error{
3150 Err: Error{
3151 Err: Error{
3152 Err: Error{
3153 Err: Error{
3154 Err: Error{
3155 Err: ErrMissingWord,
3156 Parsing: "Command",
3157 Token: tk[4],
3158 },
3159 Parsing: "CommandOrCompound",
3160 Token: tk[4],
3161 },
3162 Parsing: "Pipeline",
3163 Token: tk[4],
3164 },
3165 Parsing: "Statement",
3166 Token: tk[4],
3167 },
3168 Parsing: "Line",
3169 Token: tk[4],
3170 },
3171 Parsing: "File",
3172 Token: tk[4],
3173 },
3174 Parsing: "CommandSubstitution",
3175 Token: tk[4],
3176 },
3177 Parsing: "WordPart",
3178 Token: tk[3],
3179 },
3180 Parsing: "Word",
3181 Token: tk[3],
3182 },
3183 Parsing: "Parameter",
3184 Token: tk[3],
3185 }
3186 }},
3187 }, func(t *test) (Type, error) {
3188 var p Parameter
3189
3190 t.Parser.Tokens = t.Parser.Tokens[1:1]
3191
3192 err := p.parse(t.Parser)
3193
3194 return p, err
3195 })
3196 }
3197
3198 func TestString(t *testing.T) {
3199 doTests(t, []sourceFn{
3200 {"a", func(t *test, tk Tokens) { // 1
3201 t.Output = String{
3202 WordsOrTokens: []WordOrToken{
3203 {
3204 Word: &Word{
3205 Parts: []WordPart{
3206 {
3207 Part: &tk[0],
3208 Tokens: tk[:1],
3209 },
3210 },
3211 Tokens: tk[:1],
3212 },
3213 Tokens: tk[:1],
3214 },
3215 },
3216 Tokens: tk[:1],
3217 }
3218 }},
3219 {"a\t or b", func(t *test, tk Tokens) { // 2
3220 t.Output = String{
3221 WordsOrTokens: []WordOrToken{
3222 {
3223 Word: &Word{
3224 Parts: []WordPart{
3225 {
3226 Part: &tk[0],
3227 Tokens: tk[:1],
3228 },
3229 },
3230 Tokens: tk[:1],
3231 },
3232 Tokens: tk[:1],
3233 },
3234 {
3235 Token: &tk[1],
3236 Tokens: tk[1:2],
3237 },
3238 {
3239 Word: &Word{
3240 Parts: []WordPart{
3241 {
3242 Part: &tk[2],
3243 Tokens: tk[2:3],
3244 },
3245 },
3246 Tokens: tk[2:3],
3247 },
3248 Tokens: tk[2:3],
3249 },
3250 {
3251 Token: &tk[3],
3252 Tokens: tk[3:4],
3253 },
3254 {
3255 Word: &Word{
3256 Parts: []WordPart{
3257 {
3258 Part: &tk[4],
3259 Tokens: tk[4:5],
3260 },
3261 },
3262 Tokens: tk[4:5],
3263 },
3264 Tokens: tk[4:5],
3265 },
3266 },
3267 Tokens: tk[:5],
3268 }
3269 }},
3270 {"$a", func(t *test, tk Tokens) { // 3
3271 t.Output = String{
3272 WordsOrTokens: []WordOrToken{
3273 {
3274 Word: &Word{
3275 Parts: []WordPart{
3276 {
3277 Part: &tk[0],
3278 Tokens: tk[:1],
3279 },
3280 },
3281 Tokens: tk[:1],
3282 },
3283 Tokens: tk[:1],
3284 },
3285 },
3286 Tokens: tk[:1],
3287 }
3288 }},
3289 {"$(||)", func(t *test, tk Tokens) { // 4
3290 t.Err = Error{
3291 Err: Error{
3292 Err: Error{
3293 Err: Error{
3294 Err: Error{
3295 Err: Error{
3296 Err: Error{
3297 Err: Error{
3298 Err: Error{
3299 Err: Error{
3300 Err: Error{
3301 Err: ErrMissingWord,
3302 Parsing: "Command",
3303 Token: tk[1],
3304 },
3305 Parsing: "CommandOrCompound",
3306 Token: tk[1],
3307 },
3308 Parsing: "Pipeline",
3309 Token: tk[1],
3310 },
3311 Parsing: "Statement",
3312 Token: tk[1],
3313 },
3314 Parsing: "Line",
3315 Token: tk[1],
3316 },
3317 Parsing: "File",
3318 Token: tk[1],
3319 },
3320 Parsing: "CommandSubstitution",
3321 Token: tk[1],
3322 },
3323 Parsing: "WordPart",
3324 Token: tk[0],
3325 },
3326 Parsing: "Word",
3327 Token: tk[0],
3328 },
3329 Parsing: "WordOrToken",
3330 Token: tk[0],
3331 },
3332 Parsing: "String",
3333 Token: tk[0],
3334 }
3335 }},
3336 }, func(t *test) (Type, error) {
3337 var s String
3338
3339 err := s.parse(t.Parser)
3340
3341 return s, err
3342 })
3343 }
3344
3345 func TestWordOrToken(t *testing.T) {
3346 doTests(t, []sourceFn{
3347 {"a", func(t *test, tk Tokens) { // 1
3348 t.Output = WordOrToken{
3349 Word: &Word{
3350 Parts: []WordPart{
3351 {
3352 Part: &tk[0],
3353 Tokens: tk[:1],
3354 },
3355 },
3356 Tokens: tk[:1],
3357 },
3358 Tokens: tk[:1],
3359 }
3360 }},
3361 {" ", func(t *test, tk Tokens) { // 2
3362 t.Output = WordOrToken{
3363 Token: &tk[0],
3364 Tokens: tk[:1],
3365 }
3366 }},
3367 {"$(||)", func(t *test, tk Tokens) { // 3
3368 t.Err = Error{
3369 Err: Error{
3370 Err: Error{
3371 Err: Error{
3372 Err: Error{
3373 Err: Error{
3374 Err: Error{
3375 Err: Error{
3376 Err: Error{
3377 Err: Error{
3378 Err: ErrMissingWord,
3379 Parsing: "Command",
3380 Token: tk[1],
3381 },
3382 Parsing: "CommandOrCompound",
3383 Token: tk[1],
3384 },
3385 Parsing: "Pipeline",
3386 Token: tk[1],
3387 },
3388 Parsing: "Statement",
3389 Token: tk[1],
3390 },
3391 Parsing: "Line",
3392 Token: tk[1],
3393 },
3394 Parsing: "File",
3395 Token: tk[1],
3396 },
3397 Parsing: "CommandSubstitution",
3398 Token: tk[1],
3399 },
3400 Parsing: "WordPart",
3401 Token: tk[0],
3402 },
3403 Parsing: "Word",
3404 Token: tk[0],
3405 },
3406 Parsing: "WordOrToken",
3407 Token: tk[0],
3408 }
3409 }},
3410 }, func(t *test) (Type, error) {
3411 var w WordOrToken
3412
3413 err := w.parse(t.Parser)
3414
3415 return w, err
3416 })
3417 }
3418
3419 func TestCommandSubstitution(t *testing.T) {
3420 doTests(t, []sourceFn{
3421 {"$()", func(t *test, tk Tokens) { // 1
3422 t.Output = CommandSubstitution{
3423 SubstitutionType: SubstitutionNew,
3424 Command: File{
3425 Tokens: tk[1:1],
3426 },
3427 Tokens: tk[:2],
3428 }
3429 }},
3430 {"``", func(t *test, tk Tokens) { // 2
3431 t.Output = CommandSubstitution{
3432 SubstitutionType: SubstitutionBacktick,
3433 Command: File{
3434 Tokens: tk[1:1],
3435 },
3436 Tokens: tk[:2],
3437 }
3438 }},
3439 {"$(``)", func(t *test, tk Tokens) { // 3
3440 t.Output = CommandSubstitution{
3441 SubstitutionType: SubstitutionNew,
3442 Command: File{
3443 Lines: []Line{
3444 {
3445 Statements: []Statement{
3446 {
3447 Pipeline: Pipeline{
3448 CommandOrCompound: CommandOrCompound{
3449 Command: &Command{
3450 Words: []Word{
3451 {
3452 Parts: []WordPart{
3453 {
3454 CommandSubstitution: &CommandSubstitution{
3455 SubstitutionType: SubstitutionBacktick,
3456 Command: File{
3457 Tokens: tk[2:2],
3458 },
3459 Tokens: tk[1:3],
3460 },
3461 Tokens: tk[1:3],
3462 },
3463 },
3464 Tokens: tk[1:3],
3465 },
3466 },
3467 Tokens: tk[1:3],
3468 },
3469 Tokens: tk[1:3],
3470 },
3471 Tokens: tk[1:3],
3472 },
3473 Tokens: tk[1:3],
3474 },
3475 },
3476 Tokens: tk[1:3],
3477 },
3478 },
3479 Tokens: tk[1:3],
3480 },
3481 Tokens: tk[:4],
3482 }
3483 }},
3484 {"`\\`\\``", func(t *test, tk Tokens) { // 4
3485 t.Output = CommandSubstitution{
3486 SubstitutionType: SubstitutionBacktick,
3487 Command: File{
3488 Lines: []Line{
3489 {
3490 Statements: []Statement{
3491 {
3492 Pipeline: Pipeline{
3493 CommandOrCompound: CommandOrCompound{
3494 Command: &Command{
3495 Words: []Word{
3496 {
3497 Parts: []WordPart{
3498 {
3499 CommandSubstitution: &CommandSubstitution{
3500 SubstitutionType: SubstitutionBacktick,
3501 Command: File{
3502 Tokens: tk[2:2],
3503 },
3504 Tokens: tk[1:3],
3505 },
3506 Tokens: tk[1:3],
3507 },
3508 },
3509 Tokens: tk[1:3],
3510 },
3511 },
3512 Tokens: tk[1:3],
3513 },
3514 Tokens: tk[1:3],
3515 },
3516 Tokens: tk[1:3],
3517 },
3518 Tokens: tk[1:3],
3519 },
3520 },
3521 Tokens: tk[1:3],
3522 },
3523 },
3524 Tokens: tk[1:3],
3525 },
3526 Tokens: tk[:4],
3527 }
3528 }},
3529 {"$(||)", func(t *test, tk Tokens) { // 5
3530 t.Err = Error{
3531 Err: Error{
3532 Err: Error{
3533 Err: Error{
3534 Err: Error{
3535 Err: Error{
3536 Err: Error{
3537 Err: ErrMissingWord,
3538 Parsing: "Command",
3539 Token: tk[1],
3540 },
3541 Parsing: "CommandOrCompound",
3542 Token: tk[1],
3543 },
3544 Parsing: "Pipeline",
3545 Token: tk[1],
3546 },
3547 Parsing: "Statement",
3548 Token: tk[1],
3549 },
3550 Parsing: "Line",
3551 Token: tk[1],
3552 },
3553 Parsing: "File",
3554 Token: tk[1],
3555 },
3556 Parsing: "CommandSubstitution",
3557 Token: tk[1],
3558 }
3559 }},
3560 }, func(t *test) (Type, error) {
3561 var c CommandSubstitution
3562
3563 err := c.parse(t.Parser)
3564
3565 return c, err
3566 })
3567 }
3568
3569 func TestRedirection(t *testing.T) {
3570 doTests(t, []sourceFn{
3571 {">a", func(t *test, tk Tokens) { // 1
3572 t.Output = Redirection{
3573 Redirector: &tk[0],
3574 Output: Word{
3575 Parts: []WordPart{
3576 {
3577 Part: &tk[1],
3578 Tokens: tk[1:2],
3579 },
3580 },
3581 Tokens: tk[1:2],
3582 },
3583 Tokens: tk[:2],
3584 }
3585 }},
3586 {"> a", func(t *test, tk Tokens) { // 2
3587 t.Output = Redirection{
3588 Redirector: &tk[0],
3589 Output: Word{
3590 Parts: []WordPart{
3591 {
3592 Part: &tk[2],
3593 Tokens: tk[2:3],
3594 },
3595 },
3596 Tokens: tk[2:3],
3597 },
3598 Tokens: tk[:3],
3599 }
3600 }},
3601 {"2>&1", func(t *test, tk Tokens) { // 3
3602 t.Output = Redirection{
3603 Input: &tk[0],
3604 Redirector: &tk[1],
3605 Output: Word{
3606 Parts: []WordPart{
3607 {
3608 Part: &tk[2],
3609 Tokens: tk[2:3],
3610 },
3611 },
3612 Tokens: tk[2:3],
3613 },
3614 Tokens: tk[:3],
3615 }
3616 }},
3617 {"<a", func(t *test, tk Tokens) { // 4
3618 t.Output = Redirection{
3619 Redirector: &tk[0],
3620 Output: Word{
3621 Parts: []WordPart{
3622 {
3623 Part: &tk[1],
3624 Tokens: tk[1:2],
3625 },
3626 },
3627 Tokens: tk[1:2],
3628 },
3629 Tokens: tk[:2],
3630 }
3631 }},
3632 {"2< a", func(t *test, tk Tokens) { // 5
3633 t.Output = Redirection{
3634 Input: &tk[0],
3635 Redirector: &tk[1],
3636 Output: Word{
3637 Parts: []WordPart{
3638 {
3639 Part: &tk[3],
3640 Tokens: tk[3:4],
3641 },
3642 },
3643 Tokens: tk[3:4],
3644 },
3645 Tokens: tk[:4],
3646 }
3647 }},
3648 {">|a", func(t *test, tk Tokens) { // 6
3649 t.Output = Redirection{
3650 Redirector: &tk[0],
3651 Output: Word{
3652 Parts: []WordPart{
3653 {
3654 Part: &tk[1],
3655 Tokens: tk[1:2],
3656 },
3657 },
3658 Tokens: tk[1:2],
3659 },
3660 Tokens: tk[:2],
3661 }
3662 }},
3663 {"3>|a", func(t *test, tk Tokens) { // 7
3664 t.Output = Redirection{
3665 Input: &tk[0],
3666 Redirector: &tk[1],
3667 Output: Word{
3668 Parts: []WordPart{
3669 {
3670 Part: &tk[2],
3671 Tokens: tk[2:3],
3672 },
3673 },
3674 Tokens: tk[2:3],
3675 },
3676 Tokens: tk[:3],
3677 }
3678 }},
3679 {">>a", func(t *test, tk Tokens) { // 8
3680 t.Output = Redirection{
3681 Redirector: &tk[0],
3682 Output: Word{
3683 Parts: []WordPart{
3684 {
3685 Part: &tk[1],
3686 Tokens: tk[1:2],
3687 },
3688 },
3689 Tokens: tk[1:2],
3690 },
3691 Tokens: tk[:2],
3692 }
3693 }},
3694 {"1>>a", func(t *test, tk Tokens) { // 9
3695 t.Output = Redirection{
3696 Input: &tk[0],
3697 Redirector: &tk[1],
3698 Output: Word{
3699 Parts: []WordPart{
3700 {
3701 Part: &tk[2],
3702 Tokens: tk[2:3],
3703 },
3704 },
3705 Tokens: tk[2:3],
3706 },
3707 Tokens: tk[:3],
3708 }
3709 }},
3710 {"&>a", func(t *test, tk Tokens) { // 10
3711 t.Output = Redirection{
3712 Redirector: &tk[0],
3713 Output: Word{
3714 Parts: []WordPart{
3715 {
3716 Part: &tk[1],
3717 Tokens: tk[1:2],
3718 },
3719 },
3720 Tokens: tk[1:2],
3721 },
3722 Tokens: tk[:2],
3723 }
3724 }},
3725 {">&a", func(t *test, tk Tokens) { // 11
3726 t.Output = Redirection{
3727 Redirector: &tk[0],
3728 Output: Word{
3729 Parts: []WordPart{
3730 {
3731 Part: &tk[1],
3732 Tokens: tk[1:2],
3733 },
3734 },
3735 Tokens: tk[1:2],
3736 },
3737 Tokens: tk[:2],
3738 }
3739 }},
3740 {"&>>a", func(t *test, tk Tokens) { // 12
3741 t.Output = Redirection{
3742 Redirector: &tk[0],
3743 Output: Word{
3744 Parts: []WordPart{
3745 {
3746 Part: &tk[1],
3747 Tokens: tk[1:2],
3748 },
3749 },
3750 Tokens: tk[1:2],
3751 },
3752 Tokens: tk[:2],
3753 }
3754 }},
3755 {"<<abc\nabc", func(t *test, tk Tokens) { // 13
3756 t.Output = Redirection{
3757 Redirector: &tk[0],
3758 Output: Word{
3759 Parts: []WordPart{
3760 {
3761 Part: &tk[1],
3762 Tokens: tk[1:2],
3763 },
3764 },
3765 Tokens: tk[1:2],
3766 },
3767 Tokens: tk[:2],
3768 }
3769 }},
3770 {"2<<-abc\nabc", func(t *test, tk Tokens) { // 14
3771 t.Output = Redirection{
3772 Input: &tk[0],
3773 Redirector: &tk[1],
3774 Output: Word{
3775 Parts: []WordPart{
3776 {
3777 Part: &tk[2],
3778 Tokens: tk[2:3],
3779 },
3780 },
3781 Tokens: tk[2:3],
3782 },
3783 Tokens: tk[:3],
3784 }
3785 }},
3786 {"2<&3", func(t *test, tk Tokens) { // 15
3787 t.Output = Redirection{
3788 Input: &tk[0],
3789 Redirector: &tk[1],
3790 Output: Word{
3791 Parts: []WordPart{
3792 {
3793 Part: &tk[2],
3794 Tokens: tk[2:3],
3795 },
3796 },
3797 Tokens: tk[2:3],
3798 },
3799 Tokens: tk[:3],
3800 }
3801 }},
3802 {"<<<abc", func(t *test, tk Tokens) { // 16
3803 t.Output = Redirection{
3804 Redirector: &tk[0],
3805 Output: Word{
3806 Parts: []WordPart{
3807 {
3808 Part: &tk[1],
3809 Tokens: tk[1:2],
3810 },
3811 },
3812 Tokens: tk[1:2],
3813 },
3814 Tokens: tk[:2],
3815 }
3816 }},
3817 {"2<&3-", func(t *test, tk Tokens) { // 17
3818 t.Output = Redirection{
3819 Input: &tk[0],
3820 Redirector: &tk[1],
3821 Output: Word{
3822 Parts: []WordPart{
3823 {
3824 Part: &tk[2],
3825 Tokens: tk[2:3],
3826 },
3827 },
3828 Tokens: tk[2:3],
3829 },
3830 Tokens: tk[:3],
3831 }
3832 }},
3833 {">&2-", func(t *test, tk Tokens) { // 18
3834 t.Output = Redirection{
3835 Redirector: &tk[0],
3836 Output: Word{
3837 Parts: []WordPart{
3838 {
3839 Part: &tk[1],
3840 Tokens: tk[1:2],
3841 },
3842 },
3843 Tokens: tk[1:2],
3844 },
3845 Tokens: tk[:2],
3846 }
3847 }},
3848 {"<>abc", func(t *test, tk Tokens) { // 19
3849 t.Output = Redirection{
3850 Redirector: &tk[0],
3851 Output: Word{
3852 Parts: []WordPart{
3853 {
3854 Part: &tk[1],
3855 Tokens: tk[1:2],
3856 },
3857 },
3858 Tokens: tk[1:2],
3859 },
3860 Tokens: tk[:2],
3861 }
3862 }},
3863 {">$(||)", func(t *test, tk Tokens) { // 20
3864 t.Err = Error{
3865 Err: Error{
3866 Err: Error{
3867 Err: Error{
3868 Err: Error{
3869 Err: Error{
3870 Err: Error{
3871 Err: Error{
3872 Err: Error{
3873 Err: Error{
3874 Err: ErrMissingWord,
3875 Parsing: "Command",
3876 Token: tk[2],
3877 },
3878 Parsing: "CommandOrCompound",
3879 Token: tk[2],
3880 },
3881 Parsing: "Pipeline",
3882 Token: tk[2],
3883 },
3884 Parsing: "Statement",
3885 Token: tk[2],
3886 },
3887 Parsing: "Line",
3888 Token: tk[2],
3889 },
3890 Parsing: "File",
3891 Token: tk[2],
3892 },
3893 Parsing: "CommandSubstitution",
3894 Token: tk[2],
3895 },
3896 Parsing: "WordPart",
3897 Token: tk[1],
3898 },
3899 Parsing: "Word",
3900 Token: tk[1],
3901 },
3902 Parsing: "Redirection",
3903 Token: tk[1],
3904 }
3905 }},
3906 }, func(t *test) (Type, error) {
3907 var r Redirection
3908
3909 err := r.parse(t.Parser)
3910
3911 return r, err
3912 })
3913 }
3914
3915 func TestHeredoc(t *testing.T) {
3916 doTests(t, []sourceFn{
3917 {"<<a\nb\na", func(t *test, tk Tokens) { // 1
3918 t.Output = Heredoc{
3919 HeredocPartsOrWords: []HeredocPartOrWord{
3920 {
3921 HeredocPart: &tk[3],
3922 Tokens: tk[3:4],
3923 },
3924 },
3925 Tokens: tk[3:5],
3926 }
3927 }},
3928 {"<<a\n\tb\na", func(t *test, tk Tokens) { // 2
3929 t.Output = Heredoc{
3930 HeredocPartsOrWords: []HeredocPartOrWord{
3931 {
3932 HeredocPart: &tk[3],
3933 Tokens: tk[3:4],
3934 },
3935 },
3936 Tokens: tk[3:5],
3937 }
3938 }},
3939 {"<<-a\n\tb\na", func(t *test, tk Tokens) { // 3
3940 t.Output = Heredoc{
3941 HeredocPartsOrWords: []HeredocPartOrWord{
3942 {
3943 HeredocPart: &tk[4],
3944 Tokens: tk[4:5],
3945 },
3946 },
3947 Tokens: tk[3:6],
3948 }
3949 }},
3950 {"<<a\n$a\na", func(t *test, tk Tokens) { // 4
3951 t.Output = Heredoc{
3952 HeredocPartsOrWords: []HeredocPartOrWord{
3953 {
3954 Word: &Word{
3955 Parts: []WordPart{
3956 {
3957 Part: &tk[3],
3958 Tokens: tk[3:4],
3959 },
3960 },
3961 Tokens: tk[3:4],
3962 },
3963 Tokens: tk[3:4],
3964 },
3965 {
3966 HeredocPart: &tk[4],
3967 Tokens: tk[4:5],
3968 },
3969 },
3970 Tokens: tk[3:6],
3971 }
3972 }},
3973 {"<<a\na$b\na", func(t *test, tk Tokens) { // 5
3974 t.Output = Heredoc{
3975 HeredocPartsOrWords: []HeredocPartOrWord{
3976 {
3977 HeredocPart: &tk[3],
3978 Tokens: tk[3:4],
3979 },
3980 {
3981 Word: &Word{
3982 Parts: []WordPart{
3983 {
3984 Part: &tk[4],
3985 Tokens: tk[4:5],
3986 },
3987 },
3988 Tokens: tk[4:5],
3989 },
3990 Tokens: tk[4:5],
3991 },
3992 {
3993 HeredocPart: &tk[5],
3994 Tokens: tk[5:6],
3995 },
3996 },
3997 Tokens: tk[3:7],
3998 }
3999 }},
4000 {"<<a\n$(||)\na", func(t *test, tk Tokens) { // 6
4001 t.Err = Error{
4002 Err: Error{
4003 Err: Error{
4004 Err: Error{
4005 Err: Error{
4006 Err: Error{
4007 Err: Error{
4008 Err: Error{
4009 Err: Error{
4010 Err: Error{
4011 Err: Error{
4012 Err: ErrMissingWord,
4013 Parsing: "Command",
4014 Token: tk[4],
4015 },
4016 Parsing: "CommandOrCompound",
4017 Token: tk[4],
4018 },
4019 Parsing: "Pipeline",
4020 Token: tk[4],
4021 },
4022 Parsing: "Statement",
4023 Token: tk[4],
4024 },
4025 Parsing: "Line",
4026 Token: tk[4],
4027 },
4028 Parsing: "File",
4029 Token: tk[4],
4030 },
4031 Parsing: "CommandSubstitution",
4032 Token: tk[4],
4033 },
4034 Parsing: "WordPart",
4035 Token: tk[3],
4036 },
4037 Parsing: "Word",
4038 Token: tk[3],
4039 },
4040 Parsing: "HeredocPartOrWord",
4041 Token: tk[3],
4042 },
4043 Parsing: "Heredoc",
4044 Token: tk[3],
4045 }
4046 }},
4047 }, func(t *test) (Type, error) {
4048 var h Heredoc
4049
4050 t.Parser.Tokens = t.Parser.Tokens[3:3]
4051
4052 err := h.parse(t.Parser)
4053
4054 return h, err
4055 })
4056 }
4057
4058 func TestHeredocPartOrWord(t *testing.T) {
4059 doTests(t, []sourceFn{
4060 {"<<a\nb\na", func(t *test, tk Tokens) { // 1
4061 t.Output = HeredocPartOrWord{
4062 HeredocPart: &tk[3],
4063 Tokens: tk[3:4],
4064 }
4065 }},
4066 {"<<a\n$b\na", func(t *test, tk Tokens) { // 2
4067 t.Output = HeredocPartOrWord{
4068 Word: &Word{
4069 Parts: []WordPart{
4070 {
4071 Part: &tk[3],
4072 Tokens: tk[3:4],
4073 },
4074 },
4075 Tokens: tk[3:4],
4076 },
4077 Tokens: tk[3:4],
4078 }
4079 }},
4080 {"<<a\n$(||)\na", func(t *test, tk Tokens) { // 3
4081 t.Err = Error{
4082 Err: Error{
4083 Err: Error{
4084 Err: Error{
4085 Err: Error{
4086 Err: Error{
4087 Err: Error{
4088 Err: Error{
4089 Err: Error{
4090 Err: Error{
4091 Err: ErrMissingWord,
4092 Parsing: "Command",
4093 Token: tk[4],
4094 },
4095 Parsing: "CommandOrCompound",
4096 Token: tk[4],
4097 },
4098 Parsing: "Pipeline",
4099 Token: tk[4],
4100 },
4101 Parsing: "Statement",
4102 Token: tk[4],
4103 },
4104 Parsing: "Line",
4105 Token: tk[4],
4106 },
4107 Parsing: "File",
4108 Token: tk[4],
4109 },
4110 Parsing: "CommandSubstitution",
4111 Token: tk[4],
4112 },
4113 Parsing: "WordPart",
4114 Token: tk[3],
4115 },
4116 Parsing: "Word",
4117 Token: tk[3],
4118 },
4119 Parsing: "HeredocPartOrWord",
4120 Token: tk[3],
4121 }
4122 }},
4123 }, func(t *test) (Type, error) {
4124 var h HeredocPartOrWord
4125
4126 t.Parser.Tokens = t.Parser.Tokens[3:3]
4127
4128 err := h.parse(t.Parser)
4129
4130 return h, err
4131 })
4132 }
4133
4134 func TestArithmeticExpansion(t *testing.T) {
4135 doTests(t, []sourceFn{
4136 {"$((a))", func(t *test, tk Tokens) { // 1
4137 t.Output = ArithmeticExpansion{
4138 WordsAndOperators: []WordOrOperator{
4139 {
4140 Word: &Word{
4141 Parts: []WordPart{
4142 {
4143 Part: &tk[1],
4144 Tokens: tk[1:2],
4145 },
4146 },
4147 Tokens: tk[1:2],
4148 },
4149 Tokens: tk[1:2],
4150 },
4151 },
4152 Tokens: tk[:3],
4153 }
4154 }},
4155 {"$(( a ))", func(t *test, tk Tokens) { // 2
4156 t.Output = ArithmeticExpansion{
4157 WordsAndOperators: []WordOrOperator{
4158 {
4159 Word: &Word{
4160 Parts: []WordPart{
4161 {
4162 Part: &tk[2],
4163 Tokens: tk[2:3],
4164 },
4165 },
4166 Tokens: tk[2:3],
4167 },
4168 Tokens: tk[2:3],
4169 },
4170 },
4171 Tokens: tk[:5],
4172 }
4173 }},
4174 {"$(( a$b ))", func(t *test, tk Tokens) { // 3
4175 t.Output = ArithmeticExpansion{
4176 WordsAndOperators: []WordOrOperator{
4177 {
4178 Word: &Word{
4179 Parts: []WordPart{
4180 {
4181 Part: &tk[2],
4182 Tokens: tk[2:3],
4183 },
4184 {
4185 Part: &tk[3],
4186 Tokens: tk[3:4],
4187 },
4188 },
4189 Tokens: tk[2:4],
4190 },
4191 Tokens: tk[2:4],
4192 },
4193 },
4194 Tokens: tk[:6],
4195 }
4196 }},
4197 {"$((a+b))", func(t *test, tk Tokens) { // 4
4198 t.Output = ArithmeticExpansion{
4199 WordsAndOperators: []WordOrOperator{
4200 {
4201 Word: &Word{
4202 Parts: []WordPart{
4203 {
4204 Part: &tk[1],
4205 Tokens: tk[1:2],
4206 },
4207 },
4208 Tokens: tk[1:2],
4209 },
4210 Tokens: tk[1:2],
4211 },
4212 {
4213 Operator: &tk[2],
4214 Tokens: tk[2:3],
4215 },
4216 {
4217 Word: &Word{
4218 Parts: []WordPart{
4219 {
4220 Part: &tk[3],
4221 Tokens: tk[3:4],
4222 },
4223 },
4224 Tokens: tk[3:4],
4225 },
4226 Tokens: tk[3:4],
4227 },
4228 },
4229 Tokens: tk[:5],
4230 }
4231 }},
4232 {"$(($(||)))", func(t *test, tk Tokens) { // 5
4233 t.Err = Error{
4234 Err: Error{
4235 Err: Error{
4236 Err: Error{
4237 Err: Error{
4238 Err: Error{
4239 Err: Error{
4240 Err: Error{
4241 Err: Error{
4242 Err: Error{
4243 Err: Error{
4244 Err: ErrMissingWord,
4245 Parsing: "Command",
4246 Token: tk[2],
4247 },
4248 Parsing: "CommandOrCompound",
4249 Token: tk[2],
4250 },
4251 Parsing: "Pipeline",
4252 Token: tk[2],
4253 },
4254 Parsing: "Statement",
4255 Token: tk[2],
4256 },
4257 Parsing: "Line",
4258 Token: tk[2],
4259 },
4260 Parsing: "File",
4261 Token: tk[2],
4262 },
4263 Parsing: "CommandSubstitution",
4264 Token: tk[2],
4265 },
4266 Parsing: "WordPart",
4267 Token: tk[1],
4268 },
4269 Parsing: "Word",
4270 Token: tk[1],
4271 },
4272 Parsing: "WordOrOperator",
4273 Token: tk[1],
4274 },
4275 Parsing: "ArithmeticExpansion",
4276 Token: tk[1],
4277 }
4278 }},
4279 }, func(t *test) (Type, error) {
4280 var ae ArithmeticExpansion
4281
4282 err := ae.parse(t.Parser)
4283
4284 return ae, err
4285 })
4286 }
4287
4288 func TestWordOrOperator(t *testing.T) {
4289 doTests(t, []sourceFn{
4290 {"a", func(t *test, tk Tokens) { // 1
4291 t.Output = WordOrOperator{
4292 Word: &Word{
4293 Parts: []WordPart{
4294 {
4295 Part: &tk[0],
4296 Tokens: tk[:1],
4297 },
4298 },
4299 Tokens: tk[:1],
4300 },
4301 Tokens: tk[:1],
4302 }
4303 }},
4304 {"$a", func(t *test, tk Tokens) { // 2
4305 t.Output = WordOrOperator{
4306 Word: &Word{
4307 Parts: []WordPart{
4308 {
4309 Part: &tk[0],
4310 Tokens: tk[:1],
4311 },
4312 },
4313 Tokens: tk[:1],
4314 },
4315 Tokens: tk[:1],
4316 }
4317 }},
4318 {">", func(t *test, tk Tokens) { // 3
4319 t.Output = WordOrOperator{
4320 Operator: &tk[0],
4321 Tokens: tk[:1],
4322 }
4323 }},
4324 {"&", func(t *test, tk Tokens) { // 4
4325 t.Output = WordOrOperator{
4326 Operator: &tk[0],
4327 Tokens: tk[:1],
4328 }
4329 }},
4330 {"$(||)", func(t *test, tk Tokens) { // 5
4331 t.Err = Error{
4332 Err: Error{
4333 Err: Error{
4334 Err: Error{
4335 Err: Error{
4336 Err: Error{
4337 Err: Error{
4338 Err: Error{
4339 Err: Error{
4340 Err: Error{
4341 Err: ErrMissingWord,
4342 Parsing: "Command",
4343 Token: tk[1],
4344 },
4345 Parsing: "CommandOrCompound",
4346 Token: tk[1],
4347 },
4348 Parsing: "Pipeline",
4349 Token: tk[1],
4350 },
4351 Parsing: "Statement",
4352 Token: tk[1],
4353 },
4354 Parsing: "Line",
4355 Token: tk[1],
4356 },
4357 Parsing: "File",
4358 Token: tk[1],
4359 },
4360 Parsing: "CommandSubstitution",
4361 Token: tk[1],
4362 },
4363 Parsing: "WordPart",
4364 Token: tk[0],
4365 },
4366 Parsing: "Word",
4367 Token: tk[0],
4368 },
4369 Parsing: "WordOrOperator",
4370 Token: tk[0],
4371 }
4372 }},
4373 }, func(t *test) (Type, error) {
4374 var wo WordOrOperator
4375
4376 err := wo.parse(t.Parser)
4377
4378 return wo, err
4379 })
4380 }
4381