comparison tests/agcl/oldagsrc/ts.syn @ 0:13d2b8934445

Import AnaGram (near-)release tree into Mercurial.
author David A. Holland
date Sat, 22 Dec 2007 17:52:45 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:13d2b8934445
1 {
2 /*
3 AnaGram, a System for Syntax Directed Programming
4 C Macro preprocessor and parser
5
6 Copyright (c) 1993, Parsifal Software.
7 All Rights Reserved.
8
9 TS.SYN: Token Scanner Module
10 */
11
12 #include "mpp.h"
13
14
15 // context structure for diagnostics
16
17 struct location { unsigned line, column;};
18
19 }
20
21
22 // Configuration section
23
24 [
25 context type = location // request context tracking
26 ~allow macros // function defs for red procs
27 auto resynch
28 line numbers // #line statements in output
29 error trace // build trace on syntax error
30 ~test range // not necessary
31 ~declare pcb
32 ~error frame
33 subgrammar {
34 simple token,
35 expanded token,
36 initial arg element,
37 ws,
38 eol,
39 macro definition header,
40 }
41 parser file name = "#.cpp"
42 ]
43
44
45 // Character Set Definitions
46
47 any text char = ~eof - newline - '\\'
48 ascii = 1..126
49 blank = ' ' + '\t' + '\r' + '\f' + '\v'
50 digit = '0-9'
51 eof = -1 + 0
52 hex digit = '0-9' + 'A-F' + 'a-f'
53 newline = '\n'
54 letter = 'a-z' + 'A-Z' + '_'
55 not punctuation = '#' + blank + letter + digit + '\'' + '"' + newline + '\\'
56 punctuation = ascii - not punctuation
57 simple char = ~eof - ('\'' + '\\' + '\n')
58 string char = ~eof - ('"' + '\\' + '\n')
59
60
61 // Grammar, or Start token
62
63 input file $ // Grammar Token
64 -> [section | eol]/..., eof // Alternating sequence
65
66 eol
67 -> newline, [newline | space]...
68
69
70 // Conditional Compilation Control
71
72 section
73 -> expanded token... =*scanner_sink << op('\n');
74 -> control line
75 -> conditional block
76
77 conditional block
78 -> true if section, eol, endif line
79 -> true if section, eol, skip else section, eol, endif line
80 -> false if section, eol, endif line
81 -> false if section, eol, else section, eol, endif line
82
83 true if section
84 -> true condition
85 -> true if section, eol, section
86 -> false if section, eol, true else condition
87
88 false if section
89 -> false condition
90 -> false if section, eol, skip section
91 -> false if section, eol, false else condition
92
93 else section
94 -> '#', ws?, "else", ws?
95 -> else section, eol, section
96
97 endif line
98 -> '#', ws?, "endif", ws?
99
100 skip section
101 -> skip line
102 -> skip if section, eol, endif line
103
104 skip if section
105 -> '#', ws?, {"if" | "ifdef" | "ifndef"}, any text?...
106 -> skip if section, eol, skip section
107 -> skip if section, eol, skip else line
108
109 skip else section
110 -> skip else line
111 -> skip else section, eol, skip else line
112 -> skip else section, eol, skip section
113
114 skip else line
115 -> '#', ws?, "elif", any text?...
116 -> '#', ws?, "else", ws?
117
118 skip line
119 -> '#', ws?, [{"define" | "undefine" | "include" | "line" |
120 "error" | "pragma"}, any text?...]
121 -> not control mark, any text?...
122
123 any text
124 -> any text char
125 -> '\\', ~eof
126
127 not control mark
128 -> any text char - '#'
129 -> '\\', ~eof
130
131
132 // Conditional Control Lines
133
134 true condition, false condition
135 -> '#', ws?, "ifdef", ws, name string, ws? =check_defined(1);
136 -> '#', ws?, "ifndef", ws, name string, ws? =check_defined(0);
137 -> '#', ws?, if header, expanded token... =eval_if();
138
139 true else condition, false else condition
140 -> '#', ws?, else if header, expanded token... =eval_elif();
141
142 if header
143 -> "if", ws =init_condition();
144
145 else if header
146 -> "elif", ws =init_condition();
147
148
149 // Other Control Lines
150
151 control line
152 -> include header, expanded token... =include_file();
153 -> '#', ws?, "undef", ws, name string, ws? =undefine();
154 -> '#', ws?, [{"line" | "error" | "pragma"}, any text?...]
155 -> macro definition header:id, simple token?... =save_macro_body(id);
156
157 include header
158 -> '#', ws?, "include" =save_sink << scanner_sink, scanner_sink = &++ta;
159
160
161 // Macro Definitions
162
163 (int) macro definition header
164 -> '#', ws?, "define", ws, name string =init_macro_def(0,0);
165 -> '#', ws?, "define", ws, name string,
166 '(', ws?, parameter list:n, ')' =init_macro_def(n,1);
167
168 (int) parameter list
169 -> =0;
170 -> names, ws?
171
172 (int) names
173 -> name string =1;
174 -> names:n, ws?, ',', ws?, name string =n+1;
175
176
177 // Unexpanded text (for macro definitions
178
179 simple token
180 -> space:c =*scanner_sink << space_op(c);
181 -> word
182 -> separator
183 -> '#' =*scanner_sink << op('#');
184 -> qualified real
185 -> integer constant
186
187 word
188 -> name string =*scanner_sink << name_token();
189
190
191 // Expanded text
192
193 expanded token
194 -> expanded word
195 -> separator
196 -> space
197 -> qualified real
198 -> integer constant
199
200 expanded word
201 -> variable:t =*scanner_sink << t;
202 -> simple macro:t =expand(t,0);
203 -> macro:t, ws? =*scanner_sink << t;
204 -> macro:t, ws?, '(', ws?, macro arg list:n, ')' =expand(t,n);
205 -> defined, ws?, '(', ws?, name string, ws?, ')' =*scanner_sink << defined();
206 -> defined, ws, name string =*scanner_sink << defined();
207
208
209 // Name classification
210
211 (token) variable, simple macro, macro, defined
212 -> name string =id_macro();
213
214
215 // Macro Arguments
216
217 (int) macro arg list
218 -> =0;
219 -> !save_sink << scanner_sink, scanner_sink = &ta;, macro args:n =
220 save_sink >> scanner_sink, n;
221
222 (int) macro args
223 -> !++ta;, arg elements =1;
224 -> macro args:n, ',', ws?, !++ta;, arg elements =n+1;
225
226 arg elements
227 -> initial arg element
228 -> arg elements, arg element
229
230 arg element
231 -> space:c =*scanner_sink << space_op(c);
232 -> initial arg element
233
234 initial arg element
235 -> name string =*scanner_sink << name_token();
236 -> qualified real
237 -> integer constant
238 -> string literal =*scanner_sink << tkn(STRINGliteral);
239 -> character constant =*scanner_sink << tkn(CHARACTERconstant);
240 -> operator
241 -> punctuation - '(' - ',' - ')':p =*scanner_sink << op(p);
242 -> nested elements, ')':t =*scanner_sink << op(t);
243
244 nested elements
245 -> '(':t =*scanner_sink << op(t);
246 -> nested elements, arg element
247 -> nested elements, ',':t =*scanner_sink << op(t);
248
249
250 // Basic syntactic elements
251
252 separator
253 -> string literal =*scanner_sink << tkn(STRINGliteral);
254 -> character constant =*scanner_sink << tkn(CHARACTERconstant);
255 -> operator
256 -> punctuation:p =*scanner_sink << op(p);
257 -> '\\', '\n'
258
259 (int) space
260 -> blank
261 -> comment =' ';
262
263 ws = space...
264
265 comment
266 -> comment head, "*/"
267
268 comment head
269 -> "/*"
270 -> comment head, ~eof
271
272 comment, comment head
273 -> comment head, comment ={if (nest_comments) CHANGE_REDUCTION(comment_head);}
274
275 operator
276 -> '&', '&' =*scanner_sink << op(ANDAND);
277 -> '&', '=' =*scanner_sink << op(ANDassign);
278 -> '-', '>' =*scanner_sink << op(ARROW);
279 -> '#', '#' =*scanner_sink << op(CONCAT);
280 -> '-', '-' =*scanner_sink << op(DECR);
281 -> '/', '=' =*scanner_sink << op(DIVassign);
282 -> '.', '.', '.' =*scanner_sink << op(ELLIPSIS);
283 -> '=', '=' =*scanner_sink << op(EQ);
284 -> '^', '=' =*scanner_sink << op(ERassign);
285 -> '>', '=' =*scanner_sink << op(GE);
286 -> '+', '+' =*scanner_sink << op(ICR);
287 -> '<', '=' =*scanner_sink << op(LE);
288 -> '<', '<' =*scanner_sink << op(LS);
289 -> '<', '<', '=' =*scanner_sink << op(LSassign);
290 -> '%', '=' =*scanner_sink << op(MODassign);
291 -> '-', '=' =*scanner_sink << op(MINUSassign);
292 -> '*', '=' =*scanner_sink << op(MULTassign);
293 -> '!', '=' =*scanner_sink << op(NE);
294 -> '|', '=' =*scanner_sink << op(ORassign);
295 -> '|', '|' =*scanner_sink << op(OROR);
296 -> '+', '=' =*scanner_sink << op(PLUSassign);
297 -> '>', '>' =*scanner_sink << op(RS);
298 -> '>', '>', '=' =*scanner_sink << op(RSassign);
299
300
301 // Numeric constants
302
303 qualified real
304 -> real constant, floating qualifier =*scanner_sink << tkn(FLOATconstant);
305
306 real constant
307 -> real
308
309 floating qualifier
310 ->
311 -> 'f' + 'F' =sa << 'F';
312 -> 'l' + 'L' =sa << 'L';
313
314 real
315 -> simple real
316 -> simple real, exponent
317 -> confusion, exponent
318 -> decimal integer, exponent
319
320 simple real
321 -> confusion, '.' =sa << '.';
322 -> octal integer, '.'
323 -> decimal integer, '.' =sa << '.';
324 -> '.', '0-9':d =++sa << '.' << d;
325 -> simple real, '0-9':d =sa << d;
326
327 confusion
328 -> octal integer, '8-9':d =sa << d;
329 -> confusion, '0-9':d =sa << d;
330
331 exponent
332 -> 'e' + 'E', '-', '0-9':d =sa << '-' << d;
333 -> 'e' + 'E', '+'?, '0-9':d =sa << '+' << d;
334 -> exponent, '0-9':d =sa << d;
335
336 integer qualifier
337 -> 'u' + 'U' =sa << 'U';
338 -> 'l' + 'L' =sa << 'L';
339
340 integer constant
341 -> octal constant =*scanner_sink << tkn(OCTconstant);
342 -> decimal constant =*scanner_sink << tkn(DECconstant);
343 -> hex constant =*scanner_sink << tkn(HEXconstant);
344
345 octal constant
346 -> octal integer
347 -> octal constant, integer qualifier
348
349 octal integer
350 -> '0' =++sa << '0';
351 -> octal integer, '0-7':d =sa << d;
352
353 hex constant
354 -> hex integer
355 -> hex constant, integer qualifier
356
357 hex integer
358 -> '0', 'x' + 'X', hex digit:d =++sa << "0X" << d;
359 -> hex integer, hex digit:d =sa << d;
360
361 decimal constant
362 -> decimal integer
363 -> decimal constant, integer qualifier
364
365 decimal integer
366 -> '1-9':d =++sa << d;
367 -> decimal integer, '0-9':d =sa << d;
368
369
370 // String Literals and Character Constants
371
372 string literal
373 -> string chars, '"' =sa << '"';
374
375 string chars
376 -> '"' =++sa << '"';
377 -> string chars, string char:c =sa << c;
378 -> string chars, '\\', ~eof - '\n':c =sa << '\\' << c;
379 -> string chars, '\\', '\n'
380
381
382 // Character constants
383
384 character constant
385 -> simple chars, '\'' =sa << '\'';
386
387 simple chars
388 -> '\'' =++sa << '\'';
389 -> simple chars, simple char:c = sa << c;
390 -> simple chars, '\\', ~eof - '\n': c = sa << '\\' << c;
391 -> simple chars, '\\', '\n'
392
393
394 // Identifiers
395
396 name string
397 -> letter:c =++sa << c;
398 -> name string, letter+digit:c =sa << c;
399
400
401 { // Embedded C
402 #include "array.h" // \AnaGram\classlib\include\array.h
403 #include "stack.h" // \AnaGram\classlib\include\stack.h
404 #include <io.h> // If not found, not necessary
405 #include <sys/types.h> // If not found, not necessary
406 #include <sys/stat.h>
407 #include <fcntl.h>
408
409
410 // Macro Definitions
411
412 #define SYNTAX_ERROR syntax_error_scanning(PCB.error_message)
413 #define GET_CONTEXT (CONTEXT.line = PCB.line, CONTEXT.column = PCB.column)
414 #define GET_INPUT (PCB.input_code = getc(input.file))
415 #define PCB input.pcb
416
417
418 // Structure Definition
419
420 struct file_descriptor {
421 char *name; // name of file
422 FILE *file; // source of input characters
423 ts_pcb_type pcb; // parser control block for file
424 };
425
426
427 // Static Data Declarations
428
429 static char *error_modifier = "";
430 static file_descriptor input;
431 static stack<token_sink *> save_sink(5);
432
433
434 // Syntax Error Reporting
435 /*
436 syntax_error() provides an error diagnostic procedure for those
437 parsers which are called by the token scanner. error_modifier is set
438 by expand() so that an error encountered during a macro expansion
439 will be so described. Otherwise, the diagnostic will not make
440 sense.
441
442 Since all other parsers are called from reduction procedures, the
443 line and column number of the token they are dealing with is given
444 by the context of the token scanner production that is being
445 reduced.
446 */
447
448 void syntax_error(char *msg) {
449 printf("%s: Line %d, Column %d: %s%s\n",
450 input.name, CONTEXT.line, CONTEXT.column, msg, error_modifier);
451 }
452
453 /*
454 syntax_error_scanning() provides an error diagnostic procedure for
455 the token scanner itself. The locus of the error is given by the
456 current line and column number of the token scan, as given in the
457 parser control block.
458 */
459
460 static void syntax_error_scanning(char *msg) {
461 printf("%s: Line %d, Column %d: %s\n",
462 input.name, PCB.line, PCB.column, msg);
463 }
464
465
466 // Support for Reduction Procedures
467 /*
468 name_token() looks up the name string in the string accumulator,
469 identifies it in the token dictionary, checks to see if it is a
470 reserved word, and creates a token.
471 */
472
473 static token name_token(void) {
474 token t;
475 t.id = NAME;
476 t.handle = td << sa;
477 --sa;
478 if (t.handle <= n_reserved_words) t.id = reserved_words[t.handle].id;
479 return t;
480 }
481
482 /*
483 op() creates a token for a punctuation character.
484 */
485
486 static token op(unsigned x) {
487 token t;
488 t.id = (token_id) x;
489 t.handle = token_handles[x];
490 return t;
491 }
492
493 /*
494 space_op() creates a token for a space character. Note that a space
495 could be a tab, vertical tab, or form feed character as well as a
496 blank.
497 */
498
499 static token space_op(unsigned x) {
500 token t;
501 t.id = (token_id) ' ';
502 t.handle = token_handles[x];
503 return t;
504 }
505
506 /*
507 tkn() creates a token with a specified id for the string on the top
508 of the string accumulator
509 */
510
511 static token tkn(token_id id) {
512 token t;
513 t.id = id;
514 t.handle = td << sa;
515 --sa;
516 return t;
517 }
518
519
520 // Macro Processing Procedures
521
522 /*
523 check_defined() looks up the name on the string accumulator to see if
524 it is the name of a macro. It then selects a reduction token according
525 to the outcome of the test and an input flag.
526 */
527
528 static void check_defined(int flag) {
529 unsigned id = macro_id[td[sa]];
530 --sa;
531 flag ^= id != 0;
532 if (flag) CHANGE_REDUCTION(false_condition);
533 else CHANGE_REDUCTION(true_condition);
534 }
535
536 /*
537 defined() returns a decimal constant token equal to one or zero
538 depending on whether the token named on the string accumulator is or
539 is not defined as a macro
540 */
541
542 static token defined(void) {
543 unsigned id = macro_id[td[sa]];
544 token t;
545 t.id = DECconstant;
546 t.handle = id ? one_value : zero_value;
547 --sa;
548 return t;
549 }
550
551 /*
552 expand() expands and outputs a macro. t.handle is the token dictionary
553 index of the macro name. n is the number of arguments found.
554
555 Since it is possible that scanner sink is pointing to ta, it is
556 necessary to pop the expanded macro from ta before passing it on to
557 scanner_sink. Otherwise, we would have effectively ta << ta, a
558 situation which causes an infinite loop.
559 */
560
561 static void expand(token t, unsigned n) {
562 error_modifier = " in macro expansion"; // fix error diagnostic
563 expand_macro(t,n); // Defined in MAS.SYN
564 if (size(ta)) {
565 array<token> x(ta,size(ta) + 1);
566 --ta;
567 *scanner_sink << x;
568 } else --ta;
569 error_modifier = "";
570 }
571
572 /*
573 Look up the name string on the string accumulator. Determine whether
574 it is a reserved word, or a simple identifier. Then determine
575 whether it is the name of a macro.
576 */
577
578 static token id_macro(void) {
579 token t;
580 unsigned id;
581
582 t.id = NAME;
583 t.handle = td << sa;
584 --sa;
585 if (t.handle <= n_reserved_words) t.id = reserved_words[t.handle].id;
586
587 if (if_clause && t.handle == defined_value) {
588 CHANGE_REDUCTION(defined);
589 return t;
590 }
591 id = macro_id[t.handle];
592 if (id == 0) return t;
593
594 if (macro[id].parens) CHANGE_REDUCTION(macro);
595 else CHANGE_REDUCTION(simple_macro);
596 return t;
597 }
598
599 /*
600 Start a macro definition. This procedure defines all but the body of
601 the macro.
602
603 nargs is the count of parameters that were found. flag is set if
604 the macro was defined with parentheses.
605
606 The parameter names are on the string accumulator, with the last
607 name on the top of the stack, so they must be popped off, identified
608 and stored in reverse order.
609
610 The name of the macro is beneath the parameter names on the string
611 accumulator.
612
613 Before returning, this procedure saves the current value of
614 scanner_sink, increments the level on the token stack and sets
615 scanner_sink so that subsequent tokens produced by the token scanner
616 will accumulate on the token stack. These tokens comprise the body
617 of the macro. When the end of the macro body is encountered, the
618 procedure save_macro_body will remove them from the token stack and
619 restore the value of scanner_sink.
620 */
621
622 static int init_macro_def(int nargs, int flag) {
623 int k;
624 int id = ++n_macros;
625 unsigned name;
626 unsigned *arg_list = nargs ? new unsigned[nargs] : NULL;
627
628 assert(id < N_MACROS);
629 for (k = nargs; k--;) {
630 arg_list[k] = td << sa;
631 --sa;
632 }
633
634 macro[id].arg_names = arg_list;
635 macro[id].n_args = nargs;
636
637 macro[id].name = name = td << sa;
638 --sa;
639
640 macro_id[name] = id;
641
642 macro[id].busy_flag = 0;
643 macro[id].parens = flag ;
644
645 save_sink << scanner_sink;
646 scanner_sink = &++ta;
647 return id;
648 }
649
650 /*
651 save_macro_body() finishes the definition of a macro by making a
652 permanent copy of the token string on the token accumulator. It then
653 restores the scanner_sink to the value it had when the macro
654 definition was encountered.
655 */
656
657 static void save_macro_body(int id) {
658 macro[id].body = size(ta) ? copy(ta) : NULL;
659 --ta;
660 save_sink >> scanner_sink;
661 }
662
663 /*
664 undefine() deletes the macro definition for the macro whose name is
665 on the top of the string accumulator. If there is no macro with the
666 given name, undefine simply returns.
667
668 Otherwise, it frees the storage associated with the macro. It then
669 fills the resulting hole in the table with the last macro in the
670 table. The macro_id table is updated appropriately.
671 */
672
673 static void undefine(void) {
674 unsigned name = td << sa;
675 int id = macro_id[name];
676 --sa;
677 if (id == 0) return;
678 macro_id[name] = 0;
679 if (macro[id].arg_names) delete [] macro[id].arg_names;
680 if (macro[id].body) delete [] macro[id].body;
681 macro[id] = macro[n_macros--];
682 macro_id[macro[id].name] = id;
683 }
684
685
686 // Include file procedures
687
688 /*
689 file_name() interprets the file name provided by an #include
690 statement. If the file name is enclosed in <> brackets it scans the
691 directory list in paths to try to find the file. If it finds it, it
692 prefixes the path to the file name.
693
694 If the file name is enclosed in "" quotation marks, file_name()
695 simply strips the quotation marks.
696
697 If file_name() succeeds, it returns 1 and provides path-name in the
698 string accumulator, otherwise it returns 0 and nothing in the string
699 accumulator.
700
701 Note that file name uses a temporary string accumulator, lsa.
702 */
703
704 static int file_name(char *file) {
705 int c;
706 int tc;
707 string_accumulator lsa(100); // for temporary storage of name
708
709 while (*file == ' ') file++;
710 tc = *file++;
711 if (tc == '<') tc = '>';
712 else if (tc != '"') return 0;
713 while ((c = *file++) != 0 && c != tc) lsa << c;
714 if (c != tc) return 0;
715 if (tc == '>') {
716 int k, n;
717 n = size(paths);
718 for (k = 0; k < n; k++) {
719 FILE *f;
720 ++sa << paths[k];
721 if (sa[0] != '\\' || sa[0] != '/') sa << '/';
722 sa << lsa;
723 f = fopen(sa,"rt");
724 if (f != NULL) {
725 fclose(f);
726 return 1;
727 }
728 --sa;
729 }
730 return 0;
731 }
732 ++sa << lsa;
733 return 1;
734 }
735
736 /*
737 include_file() is called in response to a #include statement.
738
739 First, it saves the file_descriptor for the current input. Then it
740 restores the scanner_sink which was saved prior to accumulating
741 macro expanded tokens on the token_accumulator.
742
743 When include_file() is called, the argument of the #include
744 statement exists in the form of tokens on the token accumulator.
745 These tokens are passed to a token_translator which turns the tokens
746 into a string on the string accumulator.
747
748 file_name() is then called to distinguish between "" and <> files.
749 In the latter case, file_name() prefixes a directory path to the name.
750 The name is then in the string accumulator.
751
752 scan_input() is then called to scan the include file.
753
754 Finally, before returning, the previous file_descriptor is restored.
755 */
756
757 static void include_file(void) {
758 file_descriptor save_input = input; // save input state
759 int flag;
760
761 save_sink >> scanner_sink; // restore scanner_sink
762
763 token_translator tt(&++sa);
764 tt << ta; // recover string from tokens
765 --ta; // discard token string
766
767 array<char> file(sa, size(sa)+1); // local copy of string
768 --sa;
769
770 flag = file_name(file);
771
772 if (!flag) {
773 fprintf(stderr, "Bad include file name: %s\n", (char *) file);
774 return;
775 }
776 array<char> path(sa, size(sa) + 1);
777 --sa;
778 scan_input(path); // recursive call to ts()
779 input = save_input; // restore input state
780 return;
781 }
782
783
784 // Conditional compilation procedures
785
786 /*
787 init_condition() prepares for evaluation the condition expression in
788 #if and #elif statements.
789
790 It protects scanner_sink by pushing it onto the save_sink stack.
791 Then it resets the expression evaluatior, condition, and sets
792 scanner_sink to point to it.
793
794 Finally it sets the if_clause flag so that defined() will be handled
795 properly.
796 */
797
798 static void init_condition(void) {
799 save_sink << scanner_sink;
800 scanner_sink = &reset(condition);
801 if_clause = 1;
802 }
803
804 /*
805 eval_condition() is called to deal with #if and #elif statements. The
806 init_condition() procedure has redirected scanner output to the
807 expression evaluator, so eval_condition() restores the previous
808 scanner destination.
809
810 It then sends an eof token to the expression evaluator, resets
811 if_clause and reads the value of the condition. Remember that
812 (long) condition returns the value of the expression.
813 */
814
815 static int eval_condition(void) {
816 save_sink >> scanner_sink;
817 condition << op(0); // eof to exp evaluator
818 if_clause = 0;
819 return condition != 0L;
820 }
821
822 /*
823 In eval_if() and eval_elif() note the use of CHANGE_REDUCTION to
824 select the appropriate reduction token depending on the outcome of
825 the condition.
826 */
827
828 static void eval_elif(void) {
829 if (eval_condition()) CHANGE_REDUCTION(true_else_condition);
830 else CHANGE_REDUCTION(false_else_condition);
831 }
832
833 static void eval_if(void) {
834 if (eval_condition()) CHANGE_REDUCTION(true_condition);
835 else CHANGE_REDUCTION(false_condition);
836 }
837
838
839 // Do token scan
840
841 /*
842 scan_input()
843 1) opens the specified file, if possible
844 2) calls the parser
845 3) closes the input file
846 */
847
848 void scan_input(char *path) {
849 input.file = fopen(path, "rt");
850 input.name = path;
851 if (input.file == NULL) {
852 fprintf(stderr,"Cannot open %s\n", (char *) path);
853 return;
854 }
855 ts();
856 fclose(input.file);
857 }
858
859 } // End of Embedded C