1 /*
2 * aegis - project change supervisor
3 * Copyright (C) 1997-1999, 2001-2008, 2011, 2012 Peter Miller
4 * Copyright (C) 2007 Walter Franzini
5 * Copyright (C) 2020 Aryeh M. Friedman
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 %define api.prefix {cmdline_}
22 %{
23
24 #include <common/ac/assert.h>
25 #include <common/ac/stdio.h>
26 #include <common/ac/stdlib.h>
27
28 #include <common/progname.h>
29 #include <common/quit.h>
30 #include <common/str_list.h>
31 #include <common/trace.h>
32 #include <libaegis/aer/value/boolean.h>
33 #include <libaegis/aer/value/error.h>
34 #include <libaegis/aer/value/integer.h>
35 #include <libaegis/aer/value/real.h>
36 #include <libaegis/aer/value/string.h>
37 #include <libaegis/arglex2.h>
38 #include <libaegis/change.h>
39 #include <libaegis/change/file.h>
40 #include <libaegis/gonzo.h>
41 #include <libaegis/help.h>
42 #include <libaegis/os.h>
43 #include <libaegis/project.h>
44 #include <libaegis/project/file.h>
45 #include <libaegis/sub.h>
46 #include <libaegis/user.h>
47 #include <libaegis/version.h>
48
49 #include <aefind/cmdline.h>
50 #include <aefind/descend.h>
51 #include <aefind/function.h>
52 #include <aefind/function/execute.h>
53 #include <aefind/lex.h>
54 #include <aefind/shorthand/delete.h>
55 #include <aefind/shorthand/name.h>
56 #include <aefind/shorthand/path.h>
57 #include <aefind/shorthand/print.h>
58 #include <aefind/shorthand/stat.h>
59 #include <aefind/tree.h>
60 #include <aefind/tree/arithmetic.h>
61 #include <aefind/tree/bitwise.h>
62 #include <aefind/tree/constant.h>
63 #include <aefind/tree/list.h>
64 #include <aefind/tree/logical.h>
65 #include <aefind/tree/match.h>
66 #include <aefind/tree/now.h>
67 #include <aefind/tree/relative.h>
68 #include <aefind/tree/this.h>
69
70 #ifdef DEBUG
71 #define YYDEBUG 1
72 #define YYERROR_VERBOSE 1
73 #endif
74
75 %}
76
77 %token AMIN
78 %token ANDAND
79 %token ATIME
80 %token BASELINE
81 %token BASE_REL
82 %token BIT_AND
83 %token BIT_OR
84 %token BIT_XOR
85 %token BRANCH
86 %token CHANGE
87 %token CMIN
88 %token COLON
89 %token COMMA
90 %token CTIME
91 %token CUR_REL
92 %token DEBUG_keyword
93 %token DELETE
94 %token DEVDIR
95 %token DIV
96 %token EQ
97 %token EXECUTE
98 %token FALSE_keyword
99 %token GE
100 %token GRANDPARENT
101 %token GT
102 %token HELP
103 %token JOIN
104 %token JUNK
105 %token LE
106 %token LIBRARY
107 %token LPAREN
108 %token LT
109 %token MINUS
110 %token MMIN
111 %token MOD
112 %token MTIME
113 %token MUL
114 %token NAME
115 %token NE
116 %token NEWER
117 %token NOT
118 %token NOW
119 %token NUMBER
120 %token OROR
121 %token PATH
122 %token PERM
123 %token PLUS
124 %token PRINT
125 %token PROJECT
126 %token QUESTION
127 %token REAL
128 %token RESOLVE
129 %token RESOLVE_NOT
130 %token RPAREN
131 %token SEMICOLON
132 %token SHIFT_LEFT
133 %token SHIFT_RIGHT
134 %token SSIZE
135 %token STRING
136 %token STRINGIZE
137 %token THIS
138 %token TILDE
139 %token TRACE
140 %token TRUE_keyword
141 %token TRUNK
142 %token TYPE
143 %token VERSION
144
145 %union
146 {
147 struct string_ty *lv_string;
148 struct string_list_ty *lv_string_list;
149 tree::pointer *lv_tree;
150 class tree_list *lv_tree_list;
151 long lv_number;
152 double lv_real;
153 diadic_t comparator;
154 }
155
156 %type <comparator> comparator
157 %type <lv_number> NUMBER THIS
158 %type <lv_real> REAL
159 %type <lv_string> STRING
160 %type <lv_string> number_or_string
161 %type <lv_string_list> strings strings_or_dot
162 %type <lv_tree> tree1 tree2 tree3 tree4 tree5 tree6 tree7
163 %type <lv_tree> tree8 tree9 tree10 tree11 tree12 tree13 tree14
164 %type <lv_tree_list> list
165 %type <lv_tree_list> list_opt
166 %type <lv_tree> exec_list
167
168 %left COMMA
169 %right QUESTION COLON
170 %left OROR
171 %left ANDAND
172 %left BIT_OR
173 %left BIT_XOR
174 %left BIT_AND
175 %left LT LE GT GE
176 %left SHIFT_LEFT SHIFT_RIGHT
177 %left PLUS MINUS JOIN
178 %left MUL DIV MOD TILDE
179 %right NOT unary
180 %right LPAREN RPAREN
181
182 %{
183
184 static int number_of_ops;
185 static string_list_ty *path;
186 static tree::pointer filter;
187 static string_ty *project_name;
188 static long change_number;
189 static int grandparent;
190 static string_ty *branch;
191 static int trunk;
192 static int baseline;
193 static int resolve;
194 static int debug;
195
196
197 static void
report_error(const rpt_value::pointer & vp)198 report_error(const rpt_value::pointer &vp)
199 {
200 trace(("%s\n", __PRETTY_FUNCTION__));
201 const rpt_value_error *rve =
202 dynamic_cast<const rpt_value_error *>(vp.get());
203 if (!rve)
204 return;
205 sub_context_ty sc;
206 sc.var_set_string("MeSsaGe", rve->query());
207 sc.fatal_intl(i18n("$message"));
208 /* NOTREACHED */
209 }
210
211
212 static void
walker(void *,descend_message_ty msg,string_ty * path_unres,string_ty * path_maybe,string_ty * path_res,struct stat * st)213 walker(void *, descend_message_ty msg, string_ty *path_unres,
214 string_ty *path_maybe, string_ty *path_res, struct stat *st)
215 {
216 trace(("%s\n", __PRETTY_FUNCTION__));
217 switch (msg)
218 {
219 case descend_message_file:
220 case descend_message_dir_before:
221 {
222 rpt_value::pointer vp =
223 filter->evaluate(path_unres, path_maybe, path_res, st);
224 if (vp->is_an_error())
225 report_error(vp);
226 }
227 break;
228
229 case descend_message_dir_after:
230 break;
231 }
232 }
233
234
235 static string_list_ty *stack;
236 static project *pp;
237 static change::pointer cp;
238
239
240 string_ty *
stack_relative(string_ty * fn)241 stack_relative(string_ty *fn)
242 {
243 trace(("%s\n", __PRETTY_FUNCTION__));
244 assert(stack);
245 os_become_orig();
246 string_ty *s1 = os_pathname(fn, 1);
247 os_become_undo();
248
249 string_ty *s2 = 0;
250 for (size_t k = 0; k < stack->nstrings; ++k)
251 {
252 s2 = os_below_dir(stack->string[k], s1);
253 if (s2)
254 break;
255 }
256 str_free(s1);
257
258 if (!s2)
259 {
260 sub_context_ty sc;
261 sc.var_set_string("File_Name", s1);
262 if (cp)
263 change_fatal(cp, &sc, i18n("$filename unrelated"));
264 project_fatal(pp, &sc, i18n("$filename unrelated"));
265 /* NOTREACHED */
266 }
267
268 if (s2->str_length == 0)
269 {
270 str_free(s2);
271 s2 = str_from_c(".");
272 }
273
274 return s2;
275 }
276
277
278 string_ty *
stack_nth(int n)279 stack_nth(int n)
280 {
281 trace(("%s\n", __PRETTY_FUNCTION__));
282 assert(n >= 0);
283 assert(stack);
284 assert(stack->nstrings);
285 if (!stack)
286 return 0;
287 if (n < 0 || n >= (int)stack->nstrings)
288 return 0;
289 return stack->string[n];
290 }
291
292
293 int
stack_eliminate(string_ty * filename)294 stack_eliminate(string_ty *filename)
295 {
296 trace(("%s\n", __PRETTY_FUNCTION__));
297 fstate_src_ty *src = pp->file_find(filename, view_path_simple);
298 if (!src)
299 return 0;
300 switch (src->action)
301 {
302 case file_action_create:
303 case file_action_modify:
304 break;
305
306 case file_action_remove:
307 return 1;
308
309 case file_action_insulate:
310 case file_action_transparent:
311 break;
312 }
313 return 0;
314 }
315
316
317 void
cmdline_grammar(int argc,char ** argv)318 cmdline_grammar(int argc, char **argv)
319 {
320 trace(("%s\n", __PRETTY_FUNCTION__));
321 extern int yyparse(void);
322 size_t j;
323 cstate_ty *cstate_data;
324 int based;
325
326 /*
327 * parse the command line
328 */
329 cmdline_lex_open(argc, argv);
330 number_of_ops = 0;
331 resolve = -1;
332 yyparse();
333 cmdline_lex_close();
334
335 /*
336 * reject illegal combinations of options
337 */
338 if (grandparent)
339 {
340 if (branch)
341 {
342 mutually_exclusive_options
343 (
344 arglex_token_branch,
345 arglex_token_grandparent,
346 usage
347 );
348 }
349 if (trunk)
350 {
351 mutually_exclusive_options
352 (
353 arglex_token_trunk,
354 arglex_token_grandparent,
355 usage
356 );
357 }
358 branch = str_from_c("..");
359 }
360 if (trunk)
361 {
362 if (branch)
363 {
364 mutually_exclusive_options
365 (
366 arglex_token_branch,
367 arglex_token_trunk,
368 usage
369 );
370 }
371 branch = str_from_c("");
372 }
373
374 /*
375 * locate project data
376 */
377 if (!project_name)
378 {
379 nstring n = user_ty::create()->default_project();
380 project_name = str_copy(n.get_ref());
381 }
382 pp = project_alloc(project_name);
383 str_free(project_name);
384 pp->bind_existing();
385
386 stack = new string_list_ty();
387 user_ty::pointer up = user_ty::create();
388 if (baseline)
389 {
390 if (change_number)
391 {
392 mutually_exclusive_options
393 (
394 arglex_token_branch,
395 arglex_token_change,
396 usage
397 );
398 }
399
400 /*
401 * Get the search path from the project.
402 */
403 pp->search_path_get(stack, true);
404
405 cp = 0;
406 cstate_data = 0;
407 }
408 else
409 {
410 /*
411 * locate change data
412 */
413 if (!change_number)
414 change_number = up->default_change(pp);
415 cp = change_alloc(pp, change_number);
416 change_bind_existing(cp);
417 cstate_data = cp->cstate_get();
418
419 if (cstate_data->state == cstate_state_completed)
420 {
421 /*
422 * Get the search path from the project.
423 */
424 pp->search_path_get(stack, true);
425
426 cp = 0;
427 cstate_data = 0;
428 }
429 else
430 {
431 /*
432 * It is an error if the change is not in the
433 * being_developed state (if it does not have a
434 * directory).
435 */
436 if (cstate_data->state < cstate_state_being_developed)
437 change_fatal(cp, 0, i18n("bad aefind state"));
438
439 /*
440 * Get the search path from the change.
441 */
442 cp->search_path_get(stack, true);
443 }
444 }
445
446 /*
447 * resolve the path of each path
448 * 1. the absolute path of the file name is obtained
449 * 2. if the file is inside the development directory, ok
450 * 3. if the file is inside the baseline, ok
451 * 4. if neither, error
452 */
453 assert(up);
454 based =
455 (
456 stack->nstrings >= 1
457 &&
458 (
459 up->relative_filename_preference
460 (
461 uconf_relative_filename_preference_current
462 )
463 ==
464 uconf_relative_filename_preference_base
465 )
466 );
467 for (j = 0; j < path->nstrings; ++j)
468 {
469 string_ty *s0;
470 string_ty *s1;
471 string_ty *s2;
472
473 s0 = path->string[j];
474 if (s0->str_text[0] == '/' || !based)
475 s1 = str_copy(s0);
476 else
477 s1 = os_path_join(stack->string[0], s0);
478 str_free(s0);
479 s2 = stack_relative(s1);
480 assert(s2);
481 str_free(s1);
482 path->string[j] = s2;
483 }
484
485 /*
486 * Optimize the tree.
487 *
488 * We can't do this as we go, because we don't know the search
489 * path until we finish parsing the command line.
490 */
491 if (debug > 1)
492 {
493 filter->print();
494 printf("\n");
495 fflush(stdout);
496 }
497 filter = filter->optimize();
498
499 /*
500 * walk each of the directories in turn
501 */
502 if (debug)
503 {
504 filter->print();
505 printf("\n");
506 fflush(stdout);
507 }
508 for (j = 0; j < path->nstrings; ++j)
509 descend(path->string[j], resolve, walker, 0);
510
511 /*
512 * None of the stuff is deleted.
513 * Assume program exits shortly.
514 */
515 }
516
517
518 static tree::pointer
make_sure_has_side_effects(const tree::pointer & x)519 make_sure_has_side_effects(const tree::pointer &x)
520 {
521 trace(("%s\n", __PRETTY_FUNCTION__));
522 if (x->useful())
523 return x;
524
525 #if 0
526 /*
527 * Most of the time, this is exactly what users want, a file list.
528 * So we don't whine at them.
529 */
530 error_intl
531 (
532 0,
533 i18n("warning: expression has no side effects, assuming you meant -PRint")
534 );
535 #endif
536
537 return tree_and::create(x, shorthand_print());
538 }
539
540 %}
541
542 %%
543
544 find
545 : HELP
546 {
547 help(0, usage);
548 quit(0);
549 }
550 | generic_options op generic_options
551 ;
552
553 op
554 : strings_or_dot tree14
555 {
556 path = $1;
557 filter = make_sure_has_side_effects(*$2);
558 delete $2;
559 }
560 ;
561
562 strings_or_dot
563 : strings
564 | /* empty */
565 {
566 string_ty *dot;
567
568 /*
569 * Default the path list to "." (the current directory).
570 */
571 $$ = new string_list_ty();
572 dot = str_from_c(".");
573 $$->push_back(dot);
574 str_free(dot);
575 }
576 ;
577
578 strings
579 : STRING
580 {
581 $$ = new string_list_ty();
582 $$->push_back($1);
583 str_free($1);
584 }
585 | strings STRING
586 {
587 $$ = $1;
588 $$->push_back($2);
589 str_free($2);
590 }
591 ;
592
593 /*
594 * The fundamental building blocks of expressions.
595 */
596 tree1
597 : THIS
598 {
599 tree::pointer tp = tree_this::create($1);
600 $$ = new tree::pointer(tp);
601 }
602 | NOW
603 {
604 tree::pointer tp = tree_now_new();
605 $$ = new tree::pointer(tp);
606 }
607 | STRING
608 %prec LPAREN
609 {
610 rpt_value::pointer vp = rpt_value_string::create(nstring($1));
611 str_free($1);
612 tree::pointer tp = tree_constant::create(vp);
613 $$ = new tree::pointer(tp);
614 }
615 | TRUE_keyword
616 {
617 rpt_value::pointer vp = rpt_value_boolean::create(true);
618 tree::pointer tp = tree_constant::create(vp);
619 $$ = new tree::pointer(tp);
620 }
621 | FALSE_keyword
622 {
623 rpt_value::pointer vp = rpt_value_boolean::create(false);
624 tree::pointer tp = tree_constant::create(vp);
625 $$ = new tree::pointer(tp);
626 }
627 | NUMBER
628 {
629 rpt_value::pointer vp = rpt_value_integer::create($1);
630 tree::pointer tp = tree_constant::create(vp);
631 $$ = new tree::pointer(tp);
632 }
633 | REAL
634 {
635 rpt_value::pointer vp = rpt_value_real::create($1);
636 tree::pointer tp = tree_constant::create(vp);
637 $$ = new tree::pointer(tp);
638 }
639 | LPAREN tree14 RPAREN
640 {
641 $$ = $2;
642 }
643 ;
644
645 /*
646 * The simple tests and actions.
647 */
648 tree1
649 : PRINT
650 {
651 tree::pointer tp = shorthand_print();
652 $$ = new tree::pointer(tp);
653 }
654 | DELETE
655 {
656 tree::pointer tp = shorthand_delete();
657 $$ = new tree::pointer(tp);
658 }
659 | EXECUTE exec_list SEMICOLON
660 {
661 tree::pointer tp = tree_execute::create(*$2);
662 delete $2;
663 $$ = new tree::pointer(tp);
664 }
665 | NAME STRING
666 {
667 tree::pointer tp = shorthand_name(nstring($2));
668 str_free($2);
669 $$ = new tree::pointer(tp);
670 }
671 | PATH STRING
672 {
673 tree::pointer tp = shorthand_path(nstring($2));
674 str_free($2);
675 $$ = new tree::pointer(tp);
676 }
677 | TYPE STRING
678 {
679 tree::pointer tp = shorthand_type(nstring($2));
680 str_free($2);
681 $$ = new tree::pointer(tp);
682 }
683 ;
684
685 /*
686 * These next few deal with comparing various inode aspects. They are
687 * all shorthand for various function invocations and a comparison.
688 */
689 tree1
690 : NEWER STRING
691 {
692 tree::pointer tp = shorthand_newer(nstring($2));
693 str_free($2);
694 $$ = new tree::pointer(tp);
695 }
696 | AMIN comparator NUMBER
697 {
698 tree::pointer tp = shorthand_atime($2, $3, 60);
699 $$ = new tree::pointer(tp);
700 }
701 | ATIME comparator NUMBER
702 {
703 tree::pointer tp = shorthand_atime($2, $3, 24*60*60);
704 $$ = new tree::pointer(tp);
705 }
706 | CMIN comparator NUMBER
707 {
708 tree::pointer tp = shorthand_ctime($2, $3, 60);
709 $$ = new tree::pointer(tp);
710 }
711 | CTIME comparator NUMBER
712 {
713 tree::pointer tp = shorthand_ctime($2, $3, 24*60*60);
714 $$ = new tree::pointer(tp);
715 }
716 | MMIN comparator NUMBER
717 {
718 tree::pointer tp = shorthand_mtime($2, $3, 60);
719 $$ = new tree::pointer(tp);
720 }
721 | MTIME comparator NUMBER
722 {
723 tree::pointer tp = shorthand_mtime($2, $3, 24*60*60);
724 $$ = new tree::pointer(tp);
725 }
726 | SSIZE comparator NUMBER
727 {
728 tree::pointer tp = shorthand_size($2, $3);
729 $$ = new tree::pointer(tp);
730 }
731 ;
732
733 comparator
734 : /* empty */
735 { $$ = &tree_eq::create; }
736 | EQ
737 { $$ = &tree_eq::create; }
738 | NE
739 { $$ = &tree_ne::create; }
740 | LT
741 { $$ = &tree_lt::create; }
742 | LE
743 { $$ = &tree_le::create; }
744 | GT
745 { $$ = &tree_gt::create; }
746 | GE
747 { $$ = &tree_ge::create; }
748 ;
749
750 tree1
751 : STRING LPAREN list_opt RPAREN
752 {
753 tree::pointer tp = function_indirection($1, *$3);
754 str_free($1);
755 delete $3;
756 $$ = new tree::pointer(tp);
757 }
758 ;
759
760 list_opt
761 : /* empty */
762 {
763 $$ = new tree_list();
764 }
765 | list
766 {
767 $$ = $1;
768 }
769 ;
770
771 list
772 : tree13
773 {
774 $$ = new tree_list();
775 $$->append(*$1);
776 delete $1;
777 }
778 | list COMMA tree13
779 {
780 $$ = $1;
781 $$->append(*$3);
782 delete $3;
783 }
784 ;
785
786 exec_list
787 : tree10
788 {
789 $$ = $1;
790 }
791 | exec_list tree10
792 %prec ANDAND
793 {
794 static tree::pointer t1;
795 if (!t1)
796 {
797 rpt_value::pointer vp = rpt_value_string::create(" ");
798 t1 = tree_constant::create(vp);
799 }
800
801 tree::pointer t2 = tree_join::create(*$1, t1);
802 delete $1;
803 tree::pointer tp = tree_join::create(t2, *$2);
804 delete $2;
805 $$ = new tree::pointer(tp);
806 }
807 ;
808
809 tree2
810 : tree1
811 {
812 $$ = $1;
813 }
814 | NOT tree2
815 {
816 tree::pointer tp = tree_not::create(*$2);
817 delete $2;
818 $$ = new tree::pointer(tp);
819 }
820 | PLUS tree2
821 %prec unary
822 {
823 tree::pointer tp = tree_pos::create(*$2);
824 delete $2;
825 $$ = new tree::pointer(tp);
826 }
827 | MINUS tree2
828 %prec unary
829 {
830 tree::pointer tp = tree_neg::create(*$2);
831 delete $2;
832 $$ = new tree::pointer(tp);
833 }
834 | TILDE tree2
835 %prec unary
836 {
837 tree::pointer tp = tree_bitwise_not::create(*$2);
838 delete $2;
839 $$ = new tree::pointer(tp);
840 }
841 ;
842
843 tree3
844 : tree2
845 {
846 $$ = $1;
847 }
848 | tree3 MUL tree2
849 {
850 tree::pointer tp = tree_mul::create(*$1, *$3);
851 delete $1;
852 delete $3;
853 $$ = new tree::pointer(tp);
854 }
855 | tree3 DIV tree2
856 {
857 tree::pointer tp = tree_divide::create(*$1, *$3);
858 delete $1;
859 delete $3;
860 $$ = new tree::pointer(tp);
861 }
862 | tree3 MOD tree2
863 {
864 tree::pointer tp = tree_mod::create(*$1, *$3);
865 delete $1;
866 delete $3;
867 $$ = new tree::pointer(tp);
868 }
869 | tree3 TILDE tree2
870 {
871 tree::pointer tp = tree_match::create(*$1, *$3);
872 delete $1;
873 delete $3;
874 $$ = new tree::pointer(tp);
875 }
876 ;
877
878 tree4
879 : tree3
880 {
881 $$ = $1;
882 }
883 | tree4 PLUS tree3
884 {
885 tree::pointer tp = tree_plus::create(*$1, *$3);
886 delete $1;
887 delete $3;
888 $$ = new tree::pointer(tp);
889 }
890 | tree4 MINUS tree3
891 {
892 tree::pointer tp = tree_subtract::create(*$1, *$3);
893 delete $1;
894 delete $3;
895 $$ = new tree::pointer(tp);
896 }
897 | tree4 JOIN tree3
898 {
899 tree::pointer tp = tree_join::create(*$1, *$3);
900 delete $1;
901 delete $3;
902 $$ = new tree::pointer(tp);
903 }
904 ;
905
906 tree5
907 : tree4
908 {
909 $$ = $1;
910 }
911 | tree5 SHIFT_LEFT tree4
912 {
913 tree::pointer tp = tree_shift_left::create(*$1, *$3);
914 delete $1;
915 delete $3;
916 $$ = new tree::pointer(tp);
917 }
918 | tree5 SHIFT_RIGHT tree4
919 {
920 tree::pointer tp = tree_shift_right::create(*$1, *$3);
921 delete $1;
922 delete $3;
923 $$ = new tree::pointer(tp);
924 }
925 ;
926
927 tree6
928 : tree5
929 {
930 $$ = $1;
931 }
932 | tree6 LT tree5
933 {
934 tree::pointer tp = tree_lt::create(*$1, *$3);
935 delete $1;
936 delete $3;
937 $$ = new tree::pointer(tp);
938 }
939 | tree6 LE tree5
940 {
941 tree::pointer tp = tree_le::create(*$1, *$3);
942 delete $1;
943 delete $3;
944 $$ = new tree::pointer(tp);
945 }
946 | tree6 GT tree5
947 {
948 tree::pointer tp = tree_gt::create(*$1, *$3);
949 delete $1;
950 delete $3;
951 $$ = new tree::pointer(tp);
952 }
953 | tree6 GE tree5
954 {
955 tree::pointer tp = tree_ge::create(*$1, *$3);
956 delete $1;
957 delete $3;
958 $$ = new tree::pointer(tp);
959 }
960 ;
961
962 tree7
963 : tree6
964 {
965 $$ = $1;
966 }
967 | tree7 EQ tree6
968 {
969 tree::pointer tp = tree_eq::create(*$1, *$3);
970 delete $1;
971 delete $3;
972 $$ = new tree::pointer(tp);
973 }
974 | tree7 NE tree6
975 {
976 tree::pointer tp = tree_ne::create(*$1, *$3);
977 delete $1;
978 delete $3;
979 $$ = new tree::pointer(tp);
980 }
981 ;
982
983 tree8
984 : tree7
985 {
986 $$ = $1;
987 }
988 | tree8 BIT_AND tree7
989 {
990 tree::pointer tp = tree_bitwise_and::create(*$1, *$3);
991 delete $1;
992 delete $3;
993 $$ = new tree::pointer(tp);
994 }
995 ;
996
997 tree9
998 : tree8
999 {
1000 $$ = $1;
1001 }
1002 | tree9 BIT_XOR tree8
1003 {
1004 tree::pointer tp = tree_bitwise_xor::create(*$1, *$3);
1005 delete $1;
1006 delete $3;
1007 $$ = new tree::pointer(tp);
1008 }
1009 ;
1010
1011 tree10
1012 : tree9
1013 {
1014 $$ = $1;
1015 }
1016 | tree10 BIT_OR tree9
1017 {
1018 tree::pointer tp = tree_bitwise_or::create(*$1, *$3);
1019 delete $1;
1020 delete $3;
1021 $$ = new tree::pointer(tp);
1022 }
1023 ;
1024
1025 tree11
1026 : tree10
1027 {
1028 $$ = $1;
1029 }
1030 | tree11 ANDAND tree10
1031 {
1032 tree::pointer tp = tree_and::create(*$1, *$3);
1033 delete $1;
1034 delete $3;
1035 $$ = new tree::pointer(tp);
1036 }
1037 | tree11 tree10
1038 %prec ANDAND
1039 {
1040 tree::pointer tp = tree_and::create(*$1, *$2);
1041 delete $1;
1042 delete $2;
1043 $$ = new tree::pointer(tp);
1044 }
1045 ;
1046
1047 tree12
1048 : tree11
1049 {
1050 $$ = $1;
1051 }
1052 | tree12 OROR tree11
1053 {
1054 tree::pointer tp = tree_or::create(*$1, *$3);
1055 delete $1;
1056 delete $3;
1057 $$ = new tree::pointer(tp);
1058 }
1059 ;
1060
1061 tree13
1062 : tree12
1063 {
1064 $$ = $1;
1065 }
1066 | tree13 QUESTION tree13 COLON tree12
1067 %prec QUESTION
1068 {
1069 tree::pointer tp = tree_triadic::create(*$1, *$3, *$5);
1070 delete $1;
1071 delete $3;
1072 delete $5;
1073 $$ = new tree::pointer(tp);
1074 }
1075 ;
1076
1077 tree14
1078 : tree13
1079 {
1080 $$ = $1;
1081 }
1082 | tree14 COMMA tree13
1083 {
1084 tree::pointer tp2 = make_sure_has_side_effects(*$1);
1085 delete $1;
1086 tree::pointer tp = tree_comma::create(tp2, *$3);
1087 delete $3;
1088 $$ = new tree::pointer(tp);
1089 }
1090 ;
1091
1092 generic_options
1093 : /* empty */
1094 | generic_options generic_option
1095 ;
1096
1097 generic_option
1098 : LIBRARY STRING
1099 {
1100 gonzo_library_append($2->str_text);
1101 str_free($2);
1102 }
1103 | RESOLVE
1104 {
1105 if (resolve > 0)
1106 {
1107 duplicate_option_by_name(arglex_token_resolve, usage);
1108 }
1109 if (resolve >= 0)
1110 {
1111 mutually_exclusive_options
1112 (
1113 arglex_token_resolve,
1114 arglex_token_resolve_not,
1115 usage
1116 );
1117 }
1118 resolve = 1;
1119 }
1120 | RESOLVE_NOT
1121 {
1122 if (resolve == 0)
1123 {
1124 duplicate_option_by_name(arglex_token_resolve_not, usage);
1125 }
1126 if (resolve >= 0)
1127 {
1128 mutually_exclusive_options
1129 (
1130 arglex_token_resolve,
1131 arglex_token_resolve_not,
1132 usage
1133 );
1134 }
1135 resolve = 0;
1136 }
1137 | TRACE trace_strings
1138 {
1139 #ifndef DEBUG
1140 error_intl(0, i18n("-TRace needs DEBUG"));
1141 #endif
1142 }
1143 | VERSION
1144 {
1145 version();
1146 quit(0);
1147 }
1148 ;
1149
1150 trace_strings
1151 : trace_string
1152 | trace_strings trace_string
1153 ;
1154
1155 trace_string
1156 : STRING
1157 {
1158 #ifdef DEBUG
1159 trace_enable(arglex_value.alv_string);
1160 yydebug = trace_pretest_;
1161 #endif
1162 }
1163 ;
1164
1165 generic_option
1166 : BASELINE
1167 {
1168 if (baseline)
1169 {
1170 duplicate_option_by_name(arglex_token_baseline, usage);
1171 }
1172 baseline = 1;
1173 }
1174 | CHANGE NUMBER
1175 {
1176 if (change_number)
1177 {
1178 duplicate_option_by_name(arglex_token_change, usage);
1179 }
1180 change_number = $2;
1181 if (change_number == 0)
1182 change_number = MAGIC_ZERO;
1183 else if (change_number < 1)
1184 {
1185 sub_context_ty *scp;
1186
1187 scp = sub_context_new();
1188 sub_var_set_long(scp, "Number", change_number);
1189 fatal_intl(scp, i18n("change $number out of range"));
1190 /* NOTREACHED */
1191 sub_context_delete(scp);
1192 }
1193 }
1194 | PROJECT STRING
1195 {
1196 if (project_name)
1197 duplicate_option_by_name(arglex_token_project, usage);
1198 project_name = str_from_c(arglex_value.alv_string);
1199 }
1200 | BRANCH number_or_string
1201 {
1202 if (branch)
1203 duplicate_option(usage);
1204 branch = $2;
1205 }
1206 | TRUNK
1207 {
1208 if (trunk)
1209 duplicate_option(usage);
1210 ++trunk;
1211 }
1212 | GRANDPARENT
1213 {
1214 if (grandparent)
1215 duplicate_option(usage);
1216 ++grandparent;
1217 }
1218 | DEBUG_keyword
1219 {
1220 ++debug;
1221 }
1222 | BASE_REL
1223 {
1224 user_ty::relative_filename_preference_argument(usage);
1225 }
1226 | CUR_REL
1227 {
1228 user_ty::relative_filename_preference_argument(usage);
1229 }
1230 ;
1231
1232 number_or_string
1233 : NUMBER
1234 {
1235 $$ = str_format("%ld", $1);
1236 }
1237 | STRING
1238 {
1239 $$ = $1;
1240 }
1241 ;
1242
1243
1244 // vim: set ts=8 sw=4 et :
1245