comparison examples/dsl/dsl.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 { // C Prologue
2 /*****
3
4 AnaGram Programming Examples
5
6 A Dos Script Language
7
8 Copyright 1993 Parsifal Software. All Rights Reserved.
9
10 This software is provided 'as-is', without any express or implied
11 warranty. In no event will the authors be held liable for any damages
12 arising from the use of this software.
13
14 Permission is granted to anyone to use this software for any purpose,
15 including commercial applications, and to alter it and redistribute it
16 freely, subject to the following restrictions:
17
18 1. The origin of this software must not be misrepresented; you must not
19 claim that you wrote the original software. If you use this software
20 in a product, an acknowledgment in the product documentation would be
21 appreciated but is not required.
22 2. Altered source versions must be plainly marked as such, and must not be
23 misrepresented as being the original software.
24 3. This notice may not be removed or altered from any source distribution.
25
26 *****/
27
28 #include "stack.h"
29 #include "charsink.h"
30 #include "strdict.h"
31 #include "array.h"
32 #include "symbol.h"
33 #include "query.h"
34
35 #ifdef __BCPLUSPLUS__
36 extern unsigned _stklen = 0x4000; // set stack size
37 #endif
38
39
40 // Define stacks for temporary storage
41
42 stack <action_pointer> as(25); // Stack actions
43 stack <int> is(100); // Stack string indices
44 stack <char *> ps(1000,20); // Stack parameter strings
45 stack <query_item> qs(23); // Stack query items
46
47
48 // Define data structures for symbol table
49
50 #define N_STRINGS 2000
51
52 string_accumulator sa(64000U,500);
53 string_dictionary sd(N_STRINGS);
54 array <symbol_table_entry> st(N_STRINGS);
55
56 } // End of C Prologue
57
58
59 // Character Set Definitions
60
61 digit = '0-9'
62 eof = 0 + ^Z
63 letter = 'a-z' + 'A-Z' + '_'
64 not double quote = ~eof - ('"' + '\\' + '\n')
65 not eol = ~(eof + '\n')
66 not paren = ~(eof + '(' + ')')
67 not single quote = ~eof - ('\'' + '\\' + '\n')
68 operator = '#' + '=' + '<' + '>' + '|'
69 punctuation = '(' + ')' + '{' + '}' + '[' + ']' + '"' + '\n'
70 text char = ~(eof + operator + white + punctuation + '@')
71 white = ' ' + '\t' + '\v' + '\f' + '\r'
72
73
74 // Configuration Section
75
76 [
77 // White space control
78 disregard ws
79 lexeme {literal, integer constant, string literal, paren string,
80 character constant, eol, literals, name}
81 distinguish lexemes
82
83 // parser configuration
84 pointer input
85 context type = action_pointer
86 distinguish keywords {letter + digit}
87 parser file name = "#.cpp"
88 far tables
89
90 // Debugging options
91 line numbers
92 test file mask = "*.dsl"
93 ]
94
95
96 // White Space Definitions
97
98 ws
99 -> white | comment
100
101 comment
102 -> comment head, "*/"
103
104 comment head
105 -> "/*"
106 -> comment head, ~eof
107
108 // Comment out one of the following two productions to determine whether
109 // comments nest or not
110
111 comment head
112 -> comment head, comment // comments nest
113
114 /*
115 comment
116 -> comment head, comment // comments do not nest
117 */
118
119 eol
120 -> '\n'
121 -> "//", not eol?..., '\n' // C++ style comments
122
123
124
125 // Script File Description
126
127 script file $
128 -> [execution block | declaration | eol]..., eof
129
130
131 // Dos Command and Parameter Recognition
132
133 word
134 -> paren string
135 -> string literal
136 -> integer variable:v =++sa << sd[v], lookup();
137 -> string variable:v =++sa << sd[v], lookup();
138 -> undeclared variable:v =++sa << sd[v], lookup();
139 -> word, '[', !sa << '[';,
140 parameter string, ']' =concat(sa) << ']', lookup();
141
142 string
143 -> word
144 -> string, '#', word =concat(sa);
145
146 parameter string
147 -> param word
148 -> parameter string, '#',
149 param word =concat(sa);
150
151 literal
152 -> text char:c =++sa << c;
153 -> literal, text char:c =sa << c;
154
155 param word
156 -> word
157 -> action text =action_string();
158
159
160 // Gather, but do not execute, the text of an action block
161
162 action text
163 -> action text head, '}'
164
165 action text head
166 -> '{' =as << CONTEXT;
167 -> action text head, action word
168 -> action text head, eol
169 -> action text head, action text ={action_pointer a; as >> a;}
170 -> action text head, operator+'@'
171
172
173 action word
174 -> paren string =--sa;
175 -> string literal =--sa;
176 -> literal =--sa;
177 -> action word, '[', action parameter string, ']'
178
179 action parameter string
180 -> action param word
181 -> action parameter string, '#',
182 action param word
183
184 action param word
185 -> action word
186 -> action text
187
188
189 /*****
190
191 Parenthesized string
192
193 May contain any characters inside balanced parentheses. If parentheses
194 are included, they must balance. Outer parentheses are stripped before
195 use.
196
197 *****/
198
199 paren string
200 -> paren string chars, ')'
201
202 paren string chars
203 -> '(' =++sa;
204 -> paren string chars, paren string char
205
206 paren string char
207 -> not paren:c =sa << c;
208 -> !sa << '(';, paren string chars, ')' =concat(sa) << ')';
209
210
211 /*****
212
213 String Literal
214
215 Follows the same rules as for string literals in C and C++
216
217 *****/
218
219 string literal
220 -> string chars, '"'
221
222 string chars
223 -> '"' =++sa;
224 -> string chars, string char
225
226 string char
227 -> not double quote:c =sa << c;
228 -> escape sequence:c =sa << c;
229
230 (int) escape sequence
231 -> "\\a" ='\a';
232 -> "\\b" ='\b';
233 -> "\\f" ='\f';
234 -> "\\n" ='\n';
235 -> "\\r" ='\r';
236 -> "\\t" ='\t';
237 -> "\\v" ='\v';
238 -> "\\\\" ='\\';
239 -> "\\?" = '\?';
240 -> "\\'" ='\'';
241 -> "\\\"" ='"';
242 -> octal escape
243 -> hex escape
244
245 (int) octal escape
246 -> one octal | two octal | three octal
247
248 (int) one octal
249 -> '\\', '0-7':d =d-'0';
250
251 (int) two octal
252 -> one octal:n, '0-7':d =8*n + d-'0';
253
254 (int) three octal
255 -> two octal:n, '0-7':d =8*n + d-'0';
256
257 (int) hex escape
258 -> "\\x", hex number:n =(int) n;
259
260 (long) hex number
261 -> hex digit
262 -> hex number:n, hex digit:d =16*n + d;
263
264 [
265 sticky {one octal, two octal, hex number}
266 ]
267
268
269 /*****
270
271 Command Line Interpretation
272
273 The identifier may be the name of a DOS command, internal or external,
274 a path name of an arbitrary executable, or an internal commmand of the
275 scripting language. It may appear literally, or may be the result of
276 string concatenation and substitution.
277
278 command is used in the program logic section, below.
279
280 *****/
281
282 command
283 -> identifier, parameters? =exec();
284 -> identifier, parameters?,
285 '<', parameter string =exec_redirect_in();
286 -> piped command:file, '|',
287 identifier, parameters? =exec_pipe_in(file);
288 -> piped command:file, '>',
289 parameter string =grab_output(file);
290 -> piped command:file, ">>",
291 parameter string =append_output(file);
292
293 (char *) piped command
294 -> identifier, parameters? =exec_pipe_out();
295 -> identifier, parameters?,
296 '<', parameter string =exec_redirect_in_pipe_out();
297 -> piped command:file, '|',
298 identifier, parameters? =exec_pipe_in_pipe_out(file);
299
300 identifier
301 -> string =sa << 0, ++ps << (sa.top());
302
303 parameters
304 -> parameter string =ps << (sa.top()), sa << 0;
305 -> parameters, parameter string =ps << (sa.top()), sa << 0;
306
307
308 /*****
309
310 Program logic.
311
312 This section of syntax controls the interpretation of a sequence
313 of commands enclosed within braces.
314
315 *****/
316
317 execution block
318 -> '{', eol?...,[command sequence, eol?... | if sequence, eol?...], '}'
319
320
321 /*****
322
323 A command sequence is any sequence of statements and
324 if statements that ends with a statement.
325
326 *****/
327
328 command sequence
329 -> statement
330 -> command sequence, eol..., statement
331 -> if sequence, eol..., statement
332 -> if sequence:pc, eol?...,
333 "else", action text =do_if(pc,1);
334
335 /*****
336
337 An if sequence is any direct sequence of statements and if statements
338 that ends with an if statement. The difference between an "if sequence" and
339 a "command sequence" is that an else clause may follow the "if sequence".
340
341 *****/
342
343 (int) if sequence
344 -> if statement
345 -> if sequence, eol..., if statement:cc =cc;
346 -> command sequence, eol..., if statement:cc =cc;
347 -> if sequence:pc, eol?..., "else", eol?...,
348 if condition:cc, action text =do_if(pc,cc!=0);
349
350 (int) if condition
351 -> "if",
352 '(', conditional exp:cc, ')' =(int) cc;
353
354 (int) if statement
355 -> if condition:cc, action text =do_if(0,cc != 0);
356
357 /*****
358
359 A statement is any command that isn't an if or else statement.
360 The iteration on the while statement is a ruse.
361
362 *****/
363
364 statement
365 -> command
366 -> assignment
367 -> for statement
368 -> declaration
369 -> screen description
370 -> while statement...
371
372
373 /*****
374
375 Assignment statements
376
377 There are four varieties of assignment statement depending on whether
378 or how the variable on the left hand side has been previously declared.
379
380 *****/
381
382 assignment
383 -> undeclared variable:v, '=',
384 parameter string =assign_value(v);
385 -> integer variable:v, '=',
386 conditional exp:x, ';'? =st[v].data.integer = (int) x;
387 -> string variable:v, '=',
388 string exp, ';'? =st[v].data.text = copy(sa--);
389 -> string variable:v, '@',
390 primary exp:n, '=',
391 conditional exp:x, ';'? =st[v].data.text[(unsigned)n] = (char) x;
392
393
394 /*****
395
396 Semantically determined production to determine treatment of variable
397 on left side of assignment statement.
398
399 *****/
400
401 (unsigned) integer variable,
402 string variable,
403 undeclared variable
404 -> literal =check_integer();
405
406
407 /*****
408
409 While Statement
410
411 The While statement loops by simply resetting the input pointer for
412 the parser back to the beginning of the while loop. This is the reason
413 for the iteration of the while statement in the production for "simple
414 command".
415
416 *****/
417
418 while statement
419 -> "while", '(', conditional exp:cc, ')',
420 action text =do_while(cc != 0);
421
422
423 /*****
424
425
426
427 For Statement
428
429 This for statement corresponds to the for statement in the DOS batch
430 programming language, not the for statement in C.
431
432 *****/
433
434 for statement
435 -> "for", name, // !++sa << '(';,
436 "in", parameter string,
437 "do"?, action text =do_for_loop();
438
439
440 /*****
441
442 Declaration statements
443
444 *****/
445
446 declaration
447 -> "action", literals:n,
448 action text =define_action(n);
449 -> "int",
450 name, '=', conditional exp:x, ';'? =define_integer((sa--).top(), x);
451 -> "string",
452 name, '=', string exp, ';'? =define_string();
453
454 (int) literals
455 -> literal =0;
456 -> literals:n, ws..., literal =n+1;
457
458
459 name
460 -> letter:c =++sa << c;
461 -> name, letter:c =sa << c;
462 -> name, digit:c =sa << c;
463
464
465 /*****
466
467 Integer and String Expression Logic
468
469 The syntax for expressions is essentially that of C, with the addition
470 of string comparison. The only missing operators are ++, --, and comma.
471
472 *****/
473
474
475 (long) conditional exp
476 -> logical or exp
477 -> logical or exp:c, '?', conditional exp:x, ':',
478 conditional exp:y = c != 0 ? x : y;
479
480 (long) logical or exp
481 -> logical and exp
482 -> logical or exp:x, "||",
483 logical and exp:y =x != 0 || y!=0;
484
485 (long) logical and exp
486 -> inclusive or exp
487 -> logical and exp:x, "&&",
488 inclusive or exp:y =x != 0 && y !=0;
489
490 (long) inclusive or exp
491 -> exclusive or exp
492 -> inclusive or exp:x, '|',
493 exclusive or exp:y =x | y;
494
495 (long) exclusive or exp
496 -> and exp
497 -> exclusive or exp:x, '^', and exp:y =x ^ y;
498
499 (long) and exp
500 -> equality exp
501 -> and exp:x, '&', equality exp:y =x & y;
502
503 (long) equality exp
504 -> relational exp
505 -> equality exp:x, "==", relational exp:y =x == y;
506 -> equality exp:x, "!=", relational exp:y =x != y;
507 -> string exp, "==", string exp =string_comp() == 0;
508 -> string exp, "!=", string exp =string_comp() != 0;
509
510
511 (long) relational exp
512 -> shift exp
513 -> relational exp:x, '<', shift exp:y =x < y;
514 -> relational exp:x, '>', shift exp:y =x > y;
515 -> relational exp:x, "<=", shift exp:y =x <= y;
516 -> relational exp:x, ">=", shift exp:y =x >= y;
517 -> string exp, '<', string exp =string_comp() < 0;
518 -> string exp, '>', string exp =string_comp() > 0;
519 -> string exp, "<=", string exp =string_comp() <= 0;
520 -> string exp, ">=", string exp =string_comp() >= 0;
521
522 (long) shift exp
523 -> additive exp
524 -> shift exp:x, "<<", additive exp:y =x << (int) y;
525 -> shift exp:x, ">>", additive exp:y =x >> (int) y;
526
527 (long) additive exp
528 -> multiplicative exp
529 -> additive exp:x, '+',
530 multiplicative exp:y =x + y;
531 -> additive exp:x, '-',
532 multiplicative exp:y =x - y;
533
534 (long) multiplicative exp
535 -> unary exp
536 -> multiplicative exp:x, '*', unary exp:y =x * y;
537 -> multiplicative exp:x, '/', nonzero:y =x / y;
538 -> multiplicative exp:x, '%', nonzero:y =x % y;
539
540 (long) nonzero
541 -> unary exp: x ={
542 assert(x);
543 return x;
544 }
545
546 (long) unary exp
547 -> primary exp
548 -> '+', unary exp:x =x;
549 -> '-', unary exp:x =-x;
550 -> '~', unary exp:x =~x;
551 -> '!', unary exp:x =!x;
552
553 (long) primary exp
554 -> integer constant:x =x;
555 -> character constant:x =x;
556 -> string term,'@', primary exp:n =((unsigned char *) (sa--).top())[(int) n];
557 -> '#', string element ={
558 long temp;
559 sscanf((sa--).top(), "%ld", &temp);
560 return temp;
561 }
562 -> numeric name
563 -> '(', conditional exp:x, ')' =x;
564 -> built_in name:x, built_in argument =(*st[(unsigned)x].data.func)();
565
566 built_in argument
567 -> '(', parameter string, ')'
568
569 (long) numeric name,
570 string name,
571 built_in name,
572 undefined name
573 -> name =name_type();
574
575
576 /*****
577
578 String Expressions
579
580 *****/
581
582 string exp
583 -> string term
584 -> string exp, '#', string term =concat(sa);
585
586 string term
587 -> string element
588 -> string term, '@', '(',
589 conditional exp:first, "..",
590 conditional exp:last, ')' =extract((unsigned)first, (unsigned) last);
591 -> string term, '[', !sa << '[';,
592 parameter string, ']' =concat(sa) << ']', lookup();
593
594 string element
595 -> string literal
596 -> string name:x =++sa << st[(unsigned)x].data.text;
597 -> undefined name:x =++sa << sd[(unsigned)x];
598 -> action text =action_string();
599 -> '=', primary exp:x =++sa,sa.printf("%ld",x);
600 -> '(', string exp, ')'
601
602 /*****
603
604 Integer constants
605
606 The syntax for integer constants is identical to that in C.
607
608 *****/
609
610 (long) integer constant
611 -> hex constant
612 -> octal constant
613 -> decimal constant
614
615 (long) hex constant
616 -> {"0x" | "0X"} =0;
617 -> hex constant:x, hex digit:d =16*x + d;
618
619 (long) hex digit
620 -> '0-9':d =d - '0';
621 -> 'a-f' + 'A-F':d =(d&7) + 9;
622
623 (long) octal constant
624 -> '0' =0;
625 -> octal constant:n, '0-7':d =8*n + d-'0';
626
627 (long) decimal constant
628 -> '1-9':d =d-'0';
629 -> decimal constant:n, '0-9':d =10*n + d-'0';
630
631
632 /*****
633
634 Character Constant
635
636 The rules for character constant are the same as in C.
637
638 *****/
639
640 (int) character constant
641 -> '\'', char constant element:c, '\'' =c;
642
643 (int) char constant element
644 -> not single quote
645 -> escape sequence
646
647
648 /*****
649
650 Screen Display
651
652 *****/
653
654 screen description
655 -> screen items:scd, '}' =display_queries(scd);
656
657 (screen_descriptor *) screen items
658 -> "screen", '{' =reset(qs), new screen_descriptor;
659 -> screen items, eol
660 -> screen items:scd,
661 "title", '=',
662 formula, eol =scd->title = formula(), scd;
663 -> screen items:scd,
664 color spec:c, eol =scd->color = (char) c, scd;
665 -> screen items:scd,
666 "entry", color spec:c, eol =scd->entry_color = (char) c, scd;
667 -> screen items:scd,
668 "highlight", color spec:c, eol =scd->highlight_color = (char) c, scd;
669 -> screen items:scd,
670 "size", '=', conditional exp:w,
671 ',', conditional exp:h =scd->width = (unsigned)w, scd->height = (unsigned) h, scd;
672 -> screen items:scd,
673 "location", '=', conditional exp:px,
674 ',', conditional exp:py =scd->pos.x = (unsigned) px,scd->pos.y = (unsigned) py, scd;
675 -> screen items:scd,
676 query line:q, '}', eol =qs << *q, delete q, scd;
677 -> screen items:scd,
678 button line:q, '}', eol =qs << *q, delete q, scd;
679
680 (int) color spec
681 -> "color", '=', conditional exp:fg,
682 ',', conditional exp:bg =COLOR((unsigned)fg,(unsigned)bg);
683
684
685 (query_item *) query line
686 -> "field", '{' =clear(new query_item);
687 -> query line, eol
688 -> query line:q,
689 "variable", '=',
690 literal, eol =q->id = sd << (sa--).top(), q;
691 -> query line:q,
692 "default", '=',
693 formula, eol =q->value = formula(), q;
694 -> query line:q,
695 "prompt", '=',
696 formula, eol =q->prompt = formula(), q;
697 -> query line:q,
698 "explanation", '=',
699 formula, eol =q->explanation = formula(),q;
700
701 (query_item *) button line
702 -> "button", '{' =clear(new query_item);
703 -> button line, eol
704 -> button line:q,
705 "prompt", '=', formula, eol =q->prompt = formula(), q;
706 -> button line:q,
707 "explanation", '=',
708 formula, eol =q->explanation = formula(),q;
709 -> button line:q,
710 action text, eol =q->action = copy_action(), q;
711
712 formula
713 -> formula element =reset(is) << (sd << (sa--).top());
714 -> formula, '#', formula element =is << (sd << (sa--).top());
715
716 formula element
717 -> paren string
718 -> string literal
719 -> literal
720 -> formula element, '[', !sa << '[';,
721 parameter string, ']' =concat(sa) << ']';
722
723 {
724
725 #include <stdlib.h>
726 #include <ctype.h>
727 #include <sys/stat.h>
728 #include <fcntl.h>
729 #include <time.h>
730 #include <assert.h>
731 #include <errno.h>
732
733 #if defined(__MSDOS__) || defined(__WINDOWS__)
734 #include <io.h>
735 #include <conio.h>
736 #include <process.h>
737 #else
738 #include <unistd.h>
739
740 /* This is only meant to compile, not run. (Unix) */
741 static int kbhit(void) { return 0; }
742 static int getch(void) { return '?'; }
743 static void ungetch(int) { }
744 static void strupr(char *) { }
745 static int spawnvp(int, const char *, char *const *) { return -1; }
746 #define O_TEXT 0
747 #define O_BINARY 0
748 #define P_WAIT 0
749 #define _MAX_PATH 128
750 #define _MAX_DRIVE 4
751 #define _MAX_DIR 128
752 #define _MAX_FNAME 16
753 #define _MAX_EXT 4
754 static void _splitpath(const char *, char *, char *, char *, char *) {}
755 static void _makepath(char *, const char *, const char *, const char *,
756 const char *) {}
757
758 #endif
759
760 #ifdef __MSDOS__
761 #include <dos.h>
762 #else
763
764 /* This is only meant to compile, not run. (Windows, Unix) */
765
766 #define far
767 struct find_t { int attrib; char name[16]; };
768 int _dos_findfirst(const char *, int, struct find_t *) { return -1; }
769 int _dos_findnext(struct find_t *) { return -1; }
770 #define _A_SUBDIR 1
771 struct diskfree_t {
772 unsigned avail_clusters, bytes_per_sector, sectors_per_cluster;
773 };
774 void _dos_getdiskfree(int, struct diskfree_t *f) {
775 f->avail_clusters = 0;
776 f->bytes_per_sector = 512;
777 f->sectors_per_cluster = 4;
778 }
779 void _dos_getftime(int, unsigned short *d, unsigned short *t) { *d=*t=0; }
780
781 #endif /* not MSDOS */
782
783 #include "edit.h"
784
785
786 #include "screen.h"
787 #include "util.h"
788 #include "redirect.h"
789
790 #define GET_CONTEXT CONTEXT.pointer = PCB.pointer;\
791 CONTEXT.line=PCB.line;\
792 CONTEXT.column = PCB.column;
793
794
795 int debug_switch = 0;
796 char *error_msg = NULL;
797 unsigned errorlevel_index;
798 int errorlevel;
799 int exitcode = 0;
800 int exitflag = 0;
801 int first_line = 1;
802 int first_column = 1;
803 unsigned stderr_index;
804
805 void display_queries(screen_descriptor *);
806
807 #define FIRST_LINE first_line
808 #define FIRST_COLUMN first_column
809
810
811 /*****
812
813 Internal Functions
814
815 *****/
816
817 long file_exists(void) {
818 FILE *f = fopen((sa--).top(),"r");
819 if (f != NULL) fclose(f);
820 return f != NULL;
821 }
822
823 long directory_exists(void) {
824 struct find_t ff;
825 int result;
826
827 sa << "\\*.*";
828 result = _dos_findfirst((sa--).top(),_A_SUBDIR,&ff);
829 return result == 0;
830 }
831
832 long string_length(void) {
833 return size(sa--);
834 }
835
836 long get_file_length(void) {
837 int handle = open((sa--).top(), O_RDONLY);
838 long length;
839 if (handle < 0) return 0;
840 length = filelength(handle);
841 close(handle);
842 return length;
843 }
844
845 long disk_space(void) {
846 struct diskfree_t free;
847 int drive = toupper(*(sa--).top()) - 64;
848 long avail;
849
850 _dos_getdiskfree(drive, &free);
851 avail = (long) free.avail_clusters
852 * (long) free.bytes_per_sector
853 * (long) free.sectors_per_cluster;
854 return avail;
855 }
856
857 long file_time(void) {
858 int handle = open((sa--).top(), O_RDONLY);
859 #ifdef __BCPLUSPLUS__
860 unsigned date, time;
861 #else
862 unsigned short date, time;
863 #endif
864 struct tm t;
865
866 if (handle < 0) return 0;
867 _dos_getftime(handle, &date, &time);
868 close(handle);
869 t.tm_year = ((date & 0xfe00) >> 9) + 80;
870 t.tm_mon = ((date & 0x1e00) >> 5) - 1;
871 t.tm_mday = date & 0x001f;
872 ;
873 t.tm_hour = (time & 0xf800) >> 11;
874 t.tm_min = (time & 0x07e0) >> 5;
875 t.tm_sec = (time & 0x001f) << 1;
876 return mktime(&t);
877 }
878
879
880 // Support for reduction procecures
881
882 // Compare top strings on string accumulator
883
884 /*
885 pops top two strings from string accumulator using strcmp
886 and returns
887 -1 if first string is less than top string
888 0 if strings match
889 +1 if top string is greater than first string
890 */
891
892 int string_comp(void) {
893 int n = size(sa);
894 array<char> right_string((sa--).top(), n+1);
895 return strcmp((sa--).top(),right_string);
896 }
897
898 /*
899 replace the top string on the stack, with a substring where the index
900 of the first character in the substring is given by "first" and the index
901 of the last character is given by "last"
902 */
903
904 void extract(unsigned first, unsigned last) {
905 int n = last - first + 1;
906 assert (last >= first);
907 array <char> x( (sa--).top() + first, n+1 );
908 x[n] = 0;
909 ++sa << x;
910 }
911
912 /*
913 Look up the top string on the accumulator stack in the string dictionary.
914 If it has a value in the symbol table, replace it with the symbol table
915 value. If the value is numeric, convert it to integer. Otherwise, leave the
916 string untouched on the stack.
917 */
918
919 void lookup(void) {
920 unsigned index = sd[sa.top()];
921 if (index == 0) return;
922 switch (st[index].type) {
923 case string_type:
924 case value_type: {
925 --sa; // discard name
926 ++sa << st[index].data.text; // stack value
927 break;
928 }
929 case integer_type: {
930 --sa; // discard name
931 (++sa).printf("%ld", st[index].data.integer); // convert to ascii
932 break;
933 }
934 default:
935 /* not supposed to happen? */
936 break;
937 }
938 }
939
940 /*
941 Find the data type of a symbol and change the reduction accordingly.
942 Return the dictionary index for strings, and the value itself for integers.
943 */
944
945 long name_type(void) {
946 unsigned index = sd << (sa--).top();
947 switch (st[index].type) {
948 case value_type:
949 case string_type: {
950 CHANGE_REDUCTION(string_name);
951 return index;
952 }
953 case built_in_function_type: {
954 CHANGE_REDUCTION(built_in_name);
955 return index;
956 }
957 case undefined_type: {
958 CHANGE_REDUCTION(undefined_name);
959 return index;
960 }
961 case integer_type: return st[index].data.integer;
962 default:
963 /* not supposed to happen? */
964 break;
965 }
966 return 0;
967 }
968
969 /*
970 Store a string formula. A string formula is a sequence of string identifiers
971 the values of which are to be concatenated. The parser has accumulated the
972 identifiers on the integer_stack, is. The formula is terminated by a zero
973 entry.
974 */
975
976 int *formula(void) {
977 int n = size(is << 0);
978 int *f = new int[n];
979 while (n--) is >> f[n];
980 return f;
981 }
982
983 /*
984 Make a copy of an action that has been identified in the text stream.
985 An action pointer was stacked at the beginning of the action text on the
986 action stack, as.
987 */
988
989 action_pointer copy_action(void) {
990 action_pointer ap;
991 as >> ap; // pop action descriptor
992 unsigned length = (unsigned) (PCB.pointer - ap.pointer);
993 unsigned char *action = memdup(ap.pointer,length + 1);
994 action[length] = 0;
995 ap.pointer = action;
996 return ap;
997 }
998
999
1000 // Internal Commands
1001
1002 int echo(int, char *args[]) {
1003 int i;
1004 const char *cs = "";
1005 for (i = 1; args[i]; i++) printf("%s%s", cs, args[i]), cs = " ";
1006 printf("\n");
1007 fflush(stdout);
1008 return 0;
1009 }
1010
1011 int pause(int, char *[]) {
1012 int c;
1013 while (kbhit()) getch(); // Empty buffer
1014 printf("Press any key to continue . . .\n");
1015 c = getch();
1016 if (c == 3) exit(1);
1017 return c;
1018 }
1019
1020 int exit_script(int n_args, char *args[]) {
1021 if (n_args > 1) sscanf(args[1], "%d", &exitcode);
1022 exit(exitcode);
1023 return exitcode;
1024 }
1025
1026 /*
1027 int return_script(int n_args, char *args[]) {
1028 if (n_args > 1) sscanf(args[1], "%d", &exitcode);
1029 PCB.exit_flag = AG_SUCCESS_CODE;
1030 return exitcode;
1031 }
1032 */
1033
1034 int subdirs(int, char *args[]) {
1035 struct find_t file_block;
1036 int flag;
1037 int length = strlen(args[1]);
1038 array <char> name(args[1],length + 5);
1039
1040 strcat(name, "\\*.*");
1041 for (flag = _dos_findfirst(name, _A_SUBDIR, &file_block);
1042 flag == 0; flag = _dos_findnext(&file_block)) {
1043 if ((file_block.attrib & _A_SUBDIR) == 0) continue;
1044 if (strcmp(file_block.name, ".") == 0) continue;
1045 if (strcmp(file_block.name, "..") == 0) continue;
1046 puts(file_block.name);
1047 }
1048 return 0;
1049 }
1050
1051 int files(int, char *args[]) {
1052 struct find_t file_block;
1053 int flag;
1054 int length = strlen(args[1]);
1055 array<char> name(args[1],length + 5);
1056
1057 strcat(name, "\\*.*");
1058 for (flag = _dos_findfirst(name, 0, &file_block);
1059 flag == 0; flag = _dos_findnext(&file_block)) {
1060 puts(file_block.name);
1061 }
1062 return 0;
1063 }
1064
1065
1066 /*****
1067
1068 Execute Command Line
1069
1070 *****/
1071
1072
1073 void perform_action(action_pointer ap) {
1074 dsl_pcb_type save_pcb = PCB;
1075
1076 PCB.pointer = ap.pointer;
1077 first_line = ap.line;
1078 first_column = ap.column;
1079 dsl();
1080 exitflag = PCB.exit_flag != AG_SUCCESS_CODE;
1081 PCB = save_pcb;
1082 if (exitflag) PCB.exit_flag = AG_SEMANTIC_ERROR_CODE;
1083 }
1084
1085 void exec(void) {
1086 int n = size(ps << (char *) NULL);
1087 int n_args = n - 1;
1088 unsigned index;
1089 unsigned uc_index;
1090 int i;
1091
1092 array <char *> args(n);
1093 while (n--) ps >> args[n];
1094
1095 index = sd[args[0]];
1096 while (index && st[index].type == string_type) {
1097 args[0] = st[index].data.text; // stack value
1098 index = sd[args[0]];
1099 }
1100 if (debug_switch) {
1101 for (i = 0; args[i]; i++) fprintf(stderr, "%s ", args[i]);
1102 fprintf(stderr,"\nPress any key to continue\n");
1103 while (!kbhit());
1104 getch();
1105 }
1106 strupr(args[0]);
1107 uc_index = sd[args[0]];
1108 if (n_args == 1 && strlen(args[0]) == 2 && args[0][1] == ':') {
1109 errorlevel = system(args[0]);
1110 }
1111 else if ( *args[0] && uc_index) switch (st[uc_index].type) {
1112 case internal_type: {
1113 errorlevel = (*st[uc_index].data.proc)(n_args, args);
1114 break;
1115 }
1116 case dos_type: {
1117 int i;
1118 for (i = 1; args[i]; i++) args[i][-1] = ' ';
1119 errorlevel = system(args[0]);
1120 if (errorlevel == -1) {
1121 fprintf(stderr,"Error invoking %s: %s\n", args[0], strerror(errno));
1122 exit(1);
1123 }
1124 break;
1125 }
1126 default:
1127 /* not supposed to happen? */
1128 break;
1129 }
1130 else if ( *args[0] && index) switch (st[index].type) {
1131 case action_type: {
1132 action_descriptor d = *st[index].data.action;
1133 stack <symbol_table_entry> old_entries(d.n_args);
1134 for (i = 0; i < d.n_args && args[i+1]; i++) {
1135 old_entries << st[d.args[i]];
1136 st[d.args[i]].type = value_type;
1137 st[d.args[i]].data.text = memdup(args[i+1], 1 + strlen(args[i+1]));
1138 }
1139 perform_action(d.ap);
1140 for (i = d.n_args; i--;) {
1141 release(st[d.args[i]]);
1142 old_entries >> st[d.args[i]];
1143 }
1144 break;
1145 }
1146 default: {
1147 char **tmpargs = args;
1148 errorlevel = spawnvp(P_WAIT,args[0], tmpargs);
1149 if (errorlevel == -1) {
1150 fprintf(stderr,"Error invoking %s: %s\n", args[0], strerror(errno));
1151 exit(1);
1152 }
1153 }
1154 }
1155 else {
1156 }
1157 st[errorlevel_index].data.integer = errorlevel;
1158 while (n_args--) --sa;
1159 --ps;
1160 if (kbhit()) {
1161 int c = getch();
1162 if (c == 3) exit(1);
1163 ungetch(c);
1164 }
1165 }
1166
1167 void discard_temp_file(char *file_name) {
1168 unlink(file_name); // Delete file
1169 delete [] file_name; // Free storage for name
1170 }
1171
1172
1173 /*****
1174
1175 Execute Command with piped input
1176
1177 *****/
1178
1179
1180 void exec_pipe_in(char *file_name) {
1181 {
1182 redirect sin(STDIN, file_name);
1183 exec();
1184 }
1185 discard_temp_file(file_name);
1186 }
1187
1188
1189 /*****
1190
1191 Execute Command with redirected I/O
1192
1193 *****/
1194
1195 void exec_redirect_in(void) {
1196 redirect sin(STDIN, (sa--).top());
1197 exec();
1198 }
1199
1200 char *exec_pipe_out(void) {
1201 fflush(stdout);
1202 redirect sout(STDOUT);
1203 exec();
1204 fflush(stdout);
1205 return save_file(sout);
1206 }
1207
1208 char *exec_pipe_in_pipe_out(char *file_name) {
1209 char *result;
1210 {
1211 redirect sin(STDIN, file_name);
1212 fflush(stdout);
1213 redirect sout(STDOUT);
1214 exec();
1215 fflush(stdout);
1216 result = save_file(sout);
1217 }
1218 discard_temp_file(file_name);
1219 return result;
1220 }
1221
1222 char *exec_redirect_in_pipe_out(void) {
1223 fflush(stdout);
1224 redirect sout(STDOUT);
1225 exec_redirect_in();
1226 fflush(stdout);
1227 return save_file(sout);
1228 }
1229
1230 unsigned check_integer(void) {
1231 unsigned index = sd << (sa--).top();
1232 if (st[index].type == integer_type) return index;
1233 CHANGE_REDUCTION(undeclared_variable);
1234 if (st[index].type == string_type) CHANGE_REDUCTION(string_variable);
1235 return index;
1236 }
1237
1238 void assign_value(unsigned index) {
1239 char *text = copy(sa--);
1240 release(st[index]);
1241 st[index].type = value_type;
1242 st[index].data.text = text;
1243 }
1244
1245 void grab_output(char *temp_name) {
1246 unlink(sa.top()); // delete old file
1247 rename(temp_name, (sa--).top()); // rename temp file
1248 delete [] temp_name; // discard name string
1249 }
1250
1251 void append_output(char *temp_name) {
1252 fflush(stdout);
1253 redirect sout(STDOUT, (sa--).top(), 1); // append to file named on sa
1254 redirect sin(STDIN, temp_name);
1255 char buf[2000];
1256 int n;
1257 while (1) {
1258 n = read(STDIN, buf, 2000);
1259 if (n == 0) break;
1260 write(STDOUT, buf, n);
1261 }
1262 fflush(stdout);
1263 unlink(temp_name);
1264 delete [] temp_name;
1265 }
1266
1267 void action_string(void) {
1268 action_pointer ap;
1269 as >> ap;
1270 unsigned length = (unsigned)(PCB.pointer - ap.pointer);
1271 array <unsigned char> action(ap.pointer,length + 1);
1272 action[length] = 0;
1273 fflush(stdout);
1274 redirect sout(STDOUT);
1275 char *result;
1276
1277 ap.pointer = action;
1278 perform_action(ap);
1279 fflush(stdout);
1280 result = content(sout);
1281 ++sa << result;
1282 delete [] result;
1283 }
1284
1285
1286 // Program Control functions
1287
1288 // If/else statement
1289
1290 int do_if(int pc, int cc) {
1291 action_pointer ap;
1292 as >> ap;
1293 if (!pc && cc && exitflag == 0) {
1294 unsigned length = (unsigned) (PCB.pointer - ap.pointer);
1295 array<unsigned char> q(ap.pointer, length+1);
1296 q[length] = 0;
1297 ap.pointer = q;
1298 perform_action(ap);
1299 }
1300 return pc || cc;
1301 }
1302
1303 // While statement
1304
1305 void do_while(int cc) {
1306 unsigned length;
1307 action_pointer ap;
1308 as >> ap;
1309 if (cc == 0) return;
1310 length = (unsigned) (PCB.pointer - ap.pointer);
1311 array<unsigned char> q(ap.pointer, length+1);
1312 q[length] = 0;
1313 ap.pointer = q;
1314 perform_action(ap);
1315 if (exitflag) return;
1316 PCB.pointer = CONTEXT.pointer;
1317 PCB.line = CONTEXT.line;
1318 PCB.column = CONTEXT.column;
1319 }
1320
1321
1322 // For Statement
1323 // Note that this is the for statement in the DOS batch languange for, not C
1324
1325 void do_for_loop(void) {
1326 int n,k;
1327 char *q;
1328 const char *seps = " \t\v\f\r\n";
1329 action_pointer ap;
1330 as >> ap;
1331 unsigned length = (unsigned)(PCB.pointer - ap.pointer);
1332 array <unsigned char> action(ap.pointer, length + 1);
1333 action[length] = 0;
1334
1335 ap.pointer = action;
1336 n = size(sa);
1337 array<char> text((sa--).top(), n + 1);
1338
1339
1340 unsigned index = sd << (sa--).top();
1341
1342 ++ps;
1343 for (q = strtok(text, seps); q != NULL; q = strtok(NULL,seps)) {
1344 if (*q == '(') {
1345 int k = strlen(q) - 1;
1346 assert(q[k] == ')');
1347 q[k] = 0;
1348 q++;
1349 }
1350 else if (*q == '"') {
1351 int k = strlen(q) - 1;
1352 assert(q[k] == '"');
1353 q[k] = 0;
1354 q++;
1355 }
1356 ps << q;
1357 }
1358 k = n = size(ps);
1359 array<char *> args(n);
1360 while (k--) ps >> args[k];
1361 --ps;
1362 symbol_table_entry save_table_entry = st[index];
1363 st[index].type = value_type;
1364
1365 for (k = 0; k < n && exitflag == 0; k++) {
1366 st[index].data.text = args[k];
1367 perform_action(ap);
1368 }
1369 st[index] = save_table_entry;
1370 }
1371
1372 void invoke_script(void) {
1373 int handle = open(sa.top(), O_TEXT | O_RDONLY);
1374 long size;
1375 unsigned n;
1376 action_pointer ap;
1377
1378 if (handle < 0) {
1379 fprintf(stderr,"Cannot open %s\n", (sa--).top());
1380 exit(1);
1381 }
1382 --sa;
1383 size = filelength(handle);
1384 assert(size < 65536L);
1385 array <unsigned char> data((unsigned) size+1);
1386 n = (unsigned) read(handle,data,(unsigned) size);
1387 data[n] = 0;
1388 close(handle);
1389 exitflag = 0;
1390 ap.pointer = data;
1391 ap.line = ap.column = 1;
1392 perform_action(ap);
1393 st[errorlevel_index].data.integer = exitcode;
1394 exitflag = exitcode = 0;
1395 return;
1396 }
1397
1398 internal_commands_descriptor internal_commands[] = {
1399 {"ECHO", echo},
1400 {"EXIT", exit_script},
1401 {"FILES", files},
1402 {"PAUSE", pause},
1403 // {"RETURN", return_script},
1404 {"SUBDIRS", subdirs},
1405 {NULL, NULL}
1406 };
1407
1408 struct built_ins_descriptor built_ins[] = {
1409 {"file_exists", file_exists},
1410 {"directory_exists", directory_exists},
1411 {"string_length", string_length},
1412 {"file_length", get_file_length},
1413 {"disk_space", disk_space},
1414 {"file_time", file_time},
1415 {NULL, NULL}
1416 };
1417
1418 void set_extension(char *path, const char *e) {
1419 char s[_MAX_PATH];
1420 char drive[_MAX_DRIVE];
1421 char dir[_MAX_DIR];
1422 char file[_MAX_FNAME];
1423 char ext[_MAX_EXT];
1424
1425 _splitpath(path,drive,dir,file,ext);
1426 _makepath(s, drive, dir, file, e);
1427 ++sa << s;
1428 }
1429
1430 /*
1431 Note that if this program is called without any arguments, it looks for a
1432 script with the same name as the executable. Thus, to make an install
1433 program that picks up the install script without any arguments, you simply
1434 rename DSL.EXE to INSTALL.EXE. Then when you run it without any arguments
1435 it will run the INSTALL.DSL script.
1436 */
1437
1438 int main(int argc, char *argv[]) {
1439 int arg_number = 0;
1440 int i = 1;
1441 int j = 0;
1442
1443 init_dos_internals();
1444 set_arg(j++, argv[0]);
1445 if (argc > i && (argv[i][0] == '/' || argv[i][0] == '-')) {
1446 if (toupper(argv[i][1]) != 'D') {
1447 printf("Unrecognized switch -- /%c\n", argv[i][1]);
1448 return 1;
1449 }
1450 debug_switch = 1;
1451 i++;
1452 }
1453 if (argc > i) arg_number = i++;
1454 set_extension(argv[arg_number], "DSL");
1455 set_arg(j++,copy(sa));
1456 while (i < argc) set_arg(j++, argv[i++]);
1457 define_integer("argc", j);
1458 invoke_script(); // Takes file name from sa
1459 return exitcode;
1460 }
1461 } // End Embedded C