1 /*****************************************************************************
4 ** POL - Problem Oriented Language
6 ** This is part of the CTSim program
7 ** Copyright (C) 1983-2000 Kevin Rosenberg
9 ** $Id: pol.cpp,v 1.5 2000/12/25 21:54:26 kevin Exp $
11 ** This program is free software; you can redistribute it and/or modify
12 ** it under the terms of the GNU General Public License (version 2) as
13 ** published by the Free Software Foundation.
15 ** This program is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ** GNU General Public License for more details.
20 ** You should have received a copy of the GNU General Public License
21 ** along with this program; if not, write to the Free Software
22 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 ******************************************************************************/
25 /*----------------------------------------------------------------------*/
31 #include "ctsupport.h"
35 static const int HASHSIZE=100;
38 // Tables words stored with install() & found with lookup()
39 static SYMBOL *skiptable[HASHSIZE]; // words to ignore and skip
40 static SYMBOL *cmdtable[HASHSIZE]; // pol parameter commands
41 static SYMBOL *usertable[HASHSIZE]; // user defined symbols
43 static struct token_st token; // current token
46 static struct metachar {
47 char eoc; /* end of command character */
48 char str; /* string delimiter */
49 char com; /* comment character */
50 char cmd; /* pol parameter command character */
51 char prg; /* program load character */
52 char con; /* continuation across newline character */
53 char out; /* character that delimits output to terminal */
54 char ter; /* character indicates insertion of input from terminal */
55 char inb; /* input from graphics device */
60 static struct pol_st {
61 char skipchars[MAXSKIPCHAR]; // characters to skip
62 int nl_eoc; // TRUE if newline character ends a command
63 int trace; // TRUE if trace is on
71 // Internal codes for pol commands
90 static struct key cmdlist[] = {
101 { "nl_eoc",PC_NL_EOC,},
102 { "nl_neoc", PC_NL_NEOC,},
104 { "troff", PC_TROFF,},
109 const int NUMCMD = (sizeof(cmdlist) / sizeof (struct key));
112 static int skiptok(char term[]);
113 static int pol_tok(struct token_st *token);
114 static void dumptok(struct token_st *token);
117 static int getpol_tok(struct token_st *token);
118 static int getcmd(void);
119 static int gettok (TOKEN *tok);
120 static void getblank(char *s, int toksiz);
121 static int getalpha(char *s, int toksiz);
122 static void getquote(char *qs, int toksiz);
123 static void getescape(char *s, int delim, int toksiz);
124 static int getnumber (char str[], int strsize, double *fnum, int *inum);
125 static void eatline(void);
126 static int type(int c);
127 static void inittable(SYMBOL *table[]);
128 static void freetable(SYMBOL *table[]);
129 static int hash(char *s);
130 static SYMBOL *lookup(SYMBOL *table[], char *s);
131 static SYMBOL *install(SYMBOL *table[], char *s, int def);
132 static int pol_getch(FILE *fp);
142 meta.con = AMPERSAND;
148 pol.skipchars[0] = EOS;
150 inittable (cmdtable); /* initialize symbol tables */
151 inittable (usertable);
152 inittable (skiptable);
154 for (unsigned int i = 0; i < NUMCMD; i++)
155 install (cmdtable, cmdlist[i].keyword, cmdlist[i].code);
157 token.ready = FALSE; /* no token read yet */
162 * char *w - word for pol to ignore and skip over in input
164 * pol_tok() compares all tokens to words given to this routine. If it finds it,
165 * it will immediately read another token.
169 pol_skpword (char *w)
171 if (install (skiptable, w, 0) == NULL)
172 sys_error (ERR_SEVERE, "Too many skip words defined");
177 * skip all characters that appear in string s
180 pol_skpchar (char *s)
182 strncpy (pol.skipchars, s, MAXSKIPCHAR);
185 /* pol_install (str, code)
187 * char *str - token string to install
188 * int code - code to return for token
190 * pol_tok() looks for these user defined tokens. If it finds one,
191 * it stores the tokens code in the token structure and returns TT_USERTOK
194 pol_install (char *str, int code)
196 if (install (usertable, str, code) == NULL)
198 sys_error (ERR_SEVERE, "Out of memory installing user tokens");
205 /* get_word - matches tokens on a letter by letter basis
207 * char *search - string to search for
208 * int nlet - maximum number of chars to search for match
212 pol_word (char *search, int nlet)
215 if (pol.trace == TRUE)
216 printf ("matching current token %s against word %s\n", token.tokstr, search);
218 if (strncasecmp (search, token.tokstr, nlet) == 0) {
225 /* pol_usertok (str,code)
226 * see if current token is a user defined token set with pol_install()
228 * char *str - token string as read from input
229 * int *code - returned code for user defined symbol
230 * return value - TRUE if current token has been user defined
231 * FALSE if current token is not user defined
234 pol_usertok (char *str, int *code)
238 if (pol.trace == TRUE)
239 printf ("checking if current token '%s' is user defined\n", token.tokstr);
241 if (token.type == TT_USERTOK) {
243 strcpy (str, token.tokstr);
252 /* isstring (s) - returns TRUE if current token is a string
254 * char *s - pointer to place to store token string
258 pol_string (char *str)
262 if (token.type == TT_STRING) {
263 strcpy (str, token.tokstr);
270 /* pol_integer - test for an integer
272 * int *n: returned integer value
273 * int typecode = TT_INT if accept only integer values
274 * = TT_REAL if accept both real and integer values
275 * int boundcode= TRUE if force to lie between boundries
276 * = FALSE can take any value it likes
277 * int bb1: lower bound
278 * int bb2: upper bound
281 pol_integer (int *n, int typecode, int boundcode, int bb1, int bb2)
285 if (pol.trace == TRUE)
286 printf ("checking if current token %s is an integer\n", token.tokstr);
288 if (token.type == TT_INT || token.type == TT_REAL) {
289 if (boundcode == TRUE) {
290 if (token.inum < bb1)
292 else if (token.inum > bb2)
306 pol_float (double *n, double typecode, double boundcode, double bb1, double bb2)
310 if (pol.trace == TRUE)
311 printf ("checking if current token %s is an floating point number\n", token.tokstr);
313 if (token.type == TT_INT || token.type == TT_REAL) {
314 if (boundcode == TRUE) {
315 if (token.fnum < bb1)
317 else if (token.fnum > bb2)
330 /*----------------------------------------------------------------------*/
331 /* pol_skip() - skip over any token except for end of command sequence */
333 /* returns TRUE if succesful skip */
334 /* returns FALSE if already at end of command or EOF */
335 /*----------------------------------------------------------------------*/
339 char term[5]; /* string of characters not to skip */
342 if (pol.nl_eoc == TRUE) {
348 return (skiptok (term));
351 void pol_reader(void)
353 while (pol_skip() == TRUE)
356 dumptok (&token); /* skip end of command token */
359 /* skiptok (term) - skip a token unless the first character of a token is
360 * in the string of terminators, term.
361 * char *term - string of termination characters, don't skip these characters
362 * skiptok() also does NOT skip TT_EOF
363 * returns (TRUE) if succesful skip of a token
364 * returns (FALSE) if didn't skip, read termination character or TT_EOF
368 skiptok (char term[])
372 if (token.type == TT_EOF
373 || (token.type == TT_SPECLCHAR && strchr(term, token.tokstr[0]) != NULL))
382 pol_tok (struct token_st *token)
384 if (token->ready == FALSE)
387 if (token->type == TT_EOF && pol_lookchar() != EOF)
389 return (token->type);
393 dumptok (struct token_st *token)
395 if (token->ready == FALSE)
397 token->ready = FALSE;
401 getpol_tok (struct token_st *token)
405 token->ready = FALSE;
409 if (token->type == TT_BLANK)
411 if (token->type == TT_SPECLCHAR) {
412 if (strchr(pol.skipchars, token->tokstr[0]) != NULL)
414 if (token->tokstr[0] == NEWLINE)
416 if (token->tokstr[0] == meta.cmd) {
420 if (token->tokstr[0] == meta.com) { /* skip comment */
424 if (token->tokstr[0] == meta.out) {
425 getescape(token->tokstr, meta.out, MAXTOK);
426 fputs (token->tokstr, stderr);
429 if (token->tokstr[0] == meta.con) { /* continuation across NEWLINE */
430 while (pol_lookchar() == BLANK || pol_lookchar() == TAB)
432 if (pol_lookchar() == NEWLINE)
435 if (token->tokstr[0] == meta.ter) { /* get input from terminal */
436 pol_usefile (P_USE_FILE, "");
439 return (token->type);
443 /* look for filler words */
445 if (lookup (skiptable, token->tokstr) != NULL) /* ignore words in skip table */
448 /* look for user defined symbols */
450 if ((sym = lookup (usertable, token->tokstr)) != NULL) {
451 token->type = TT_USERTOK;
452 token->code = sym->code;
456 if (pol.trace == TRUE)
457 printf ("Read token '%s', type = %d\n", token->tokstr, token->type);
459 return (token->type);
463 static int getcmd(void)
470 tt = getalpha (str, MAXTOK);
471 if (tt == TT_ERROR) {
472 sys_error (ERR_WARNING, "Error in POL parameter command");
476 if ((cmd = lookup (cmdtable,str)) == NULL) {
477 sys_error (ERR_WARNING, "POL: Unrecognized command %s", cmd);
494 pol_usefile (P_USE_FILE, tok.tokstr);
506 printf("eoc = %c str = %c com = %c cmd = %c prg = %c\n",
507 meta.eoc, meta.str, meta.com, meta.cmd, meta.prg);
508 printf("con = %c out = %c ter = %c inb = %c\n",
509 meta.con, meta.out, meta.ter, meta.inb);
512 if (found == FALSE) {
514 if (tt != TT_SPECLCHAR) {
515 sys_error (ERR_SEVERE, "POL: Illegal command character");
520 meta.eoc = tok.tokstr[0];
523 meta.str = tok.tokstr[0];
526 meta.com = tok.tokstr[0];
529 meta.cmd = tok.tokstr[0];
532 meta.prg = tok.tokstr[0];
535 meta.con = tok.tokstr[0];
538 meta.out = tok.tokstr[0];
541 meta.ter = tok.tokstr[0];
544 meta.inb = tok.tokstr[0];
547 printf("command not implemented\n");
549 } /* switch (tok->type) */
550 } /* if (found == FALSE) */
551 pol_reader(); /* clean up command */
552 } /* if legal command */
564 int toksiz = MAXTOK; /* maximum length of token string */
566 while ((c = pol_inchar()) == BLANK || c == TAB)
576 if (c == BLANK || c == TAB) { /* skip white space */
577 getblank(tok->tokstr, toksiz);
579 } else if (toktype == LETTER) {
580 toktype = getalpha (tok->tokstr, toksiz);
581 } else if (c == meta.str) { /* quoted string */
582 getquote (tok->tokstr, toksiz);
584 } else if (type(c) == DIGIT || c == PLUS || c == HYPHEN || c == PERIOD) {
585 toktype = getnumber (tok->tokstr, toksiz, &fnum, &inum);
586 } else if (c == EOF) {
587 tok->tokstr[0] = EOS;
592 tok->tokstr[1] = EOS;
593 toktype = TT_SPECLCHAR;
598 if (tok->type == TT_REAL || tok->type == TT_INT) {
611 getblank (char *s, int toksiz)
615 while ((c = pol_inchar()) == BLANK || c == TAB)
625 getalpha (char *s, int toksiz)
627 int i, chartype, alphatype;
629 if (type(pol_lookchar()) != LETTER) {
634 alphatype = TT_ALPHA;
635 for (i = 0; i < toksiz; i++) { /* get alphanumeric token */
637 chartype = type (s[i]);
638 if (chartype != LETTER && chartype != DIGIT)
640 if (chartype == DIGIT)
641 alphatype = TT_ALPNUM;
646 sys_error (ERR_SEVERE, "POL token too long.");
648 s[i] = EOS; /* terminate token */
653 /* getquote - get quoted string from file */
654 /* have already gotten delimiter in qs[0] */
656 getquote (char *qs, int toksiz)
660 delim = pol_inchar(); /* char = delimiter */
661 getescape(qs, delim, toksiz);
666 getescape ( /* reads up to delim */
674 for (i = 0; (c = pol_inchar()) != delim; i++) {
676 sys_error (ERR_WARNING, "Missing closing delimiter.");
680 sys_error (ERR_SEVERE, "string too long.");
685 sys_error (ERR_SEVERE, "end of file inside quotation");
687 } else if (c == BSLASH) { /* escape character */
689 c = pol_inchar(); /* get escaped character */
697 gettext (char *str, int lim)
701 while ((c = pol_inchar()) == BLANK || c == TAB)
705 for (i = 0; i < lim && (c = pol_inchar()) != EOF && c != NEWLINE; i++)
711 /*----------------------------------------------*/
712 /* Get a number for gettok() */
713 /*----------------------------------------------*/
717 char str[], /* string to return token in */
718 int strsize, /* maximum length of token string */
719 double *fnum, /* floating point value of number read */
720 int *inum /* integer value of number read */
724 double sign, whole, frac, powerof10, exp, expsign;
728 isSigned = FALSE; /* TRUE if number prefixed by '+' or '-' */
738 } else if (c == PLUS) {
742 } else if (c == PERIOD) {
743 if (type(pol_lookchar()) != DIGIT) {
746 return (TT_SPECLCHAR);
748 pol_ungetch (PERIOD);
749 } else if (type(c) != DIGIT) {
755 if (isSigned == TRUE) {
758 pol_inchar(); /* get period */
759 c = pol_lookchar(); /* look at character past period */
760 pol_ungetch (PERIOD); /* put back period */
761 if (type(c) != DIGIT) {
763 return (TT_SPECLCHAR);
765 } else if (type (c) != DIGIT) {
767 return (TT_SPECLCHAR);
772 while (type(c = pol_inchar()) == DIGIT) {
775 whole = 10.0 * whole + (c - '0');
777 pol_ungetch (c); /* put back non-numeric character */
779 if (c != PERIOD && tolower(c) != 'e') {
781 *fnum = whole * sign;
784 else if (*fnum > MAX_INT)
791 if (pol_lookchar() == PERIOD) {
800 while (type(c = pol_inchar()) == DIGIT) {
803 frac += (double) (c - '0') / powerof10;
811 if (tolower(c) != 'e')
816 if ((c = pol_inchar()) == PLUS) {
820 } else if (c == HYPHEN) {
824 } else if (type(c) != DIGIT) {
825 --sp; /* erase 'e' */
833 while (type(c = pol_inchar()) == DIGIT) {
836 exp = 10 * exp + (c - '0');
843 *fnum = sign * (whole + frac) * pow (10.0, expsign * exp);
846 else if (*fnum > MAX_INT)
864 type ( /* return type of ASCII character */
868 if (isalpha(c) || c == UNDERLIN)
876 /*----------------------------------------------------------------------*/
878 /* hash table routines. Kernighan & Ritchie */
880 /*----------------------------------------------------------------------*/
887 inittable (SYMBOL *table[])
891 for (i = 0; i < HASHSIZE; i++)
896 * free all memory allocated to table, then clear table
900 freetable (SYMBOL *table[])
905 for (i = 0; i < HASHSIZE; i++) {
917 hash ( /* form hash value of string s */
923 for (hashval = 0; *s != EOS; )
925 return (hashval % HASHSIZE);
928 /* Look for s in hash table */
930 lookup ( SYMBOL *table[], char *s )
933 SYMBOL *found = NULL;
935 for (np = table[hash(s)]; np != NULL; np = np->next)
936 if (strcasecmp(s, np->name) == 0) {
937 found = np; /* found it */
945 install (SYMBOL *table[], char *name, int def)
947 static char installerr[] = "install: out of memory";
951 if ((np = lookup (table, name)) == NULL) { /* not found */
952 np = (SYMBOL *) malloc (sizeof(*np));
954 sys_error (ERR_SEVERE, installerr);
957 if ((np->name = strdup(name)) == NULL) {
958 sys_error (ERR_SEVERE, installerr);
961 str_lower (np->name);
963 hashval = hash(np->name);
964 np->next = table[hashval];
966 } else /* already there */
971 /*----------------------------------------------------------------------*/
973 /*----------------------------------------------------------------------*/
977 static int currentf = -1; /* pointer to current fp */
978 static FILE *filep[MAXFILE]; /* == NULL for string input */
979 static char *fname[MAXFILE]; /* pointer to filename */
981 static char inputline[MAXLINE]; /* current input line */
982 static int lineptr; /* current position in inputline */
985 //----------------------------------------------------------------------
987 //----------------------------------------------------------------------
990 static int bp = 0; /* pointer to next free position */
991 static int buf[BUFSIZE]; /* pushed back input characters */
993 /* pol_usefile - set source of POL input
995 * int source - source of input
996 * P_USE_STR - have POL use strings as input
997 * P_USE_FILE - use file. filename is in str
1002 pol_usefile (int source, char *fn)
1007 if (currentf >= MAXFILE) {
1009 sys_error (ERR_SEVERE, "files nested too deeply");
1013 bp = 0; /* clear any pushed back input */
1015 if (source == P_USE_STR) {
1016 filep[currentf] = NULL;
1017 } else if (source == P_USE_FILE) {
1018 if (fn == NULL || strlen(fn) == 0) {
1020 } else if ((fp = fopen(fn, "r")) == NULL) {
1022 sys_error (ERR_SEVERE, "can't open file");
1025 filep[currentf] = fp;
1026 fname[currentf] = strdup (fn);
1030 void pol_closefile(void)
1032 if (currentf >= 0) {
1033 if (filep[currentf] != NULL)
1034 fclose (filep[currentf]);
1039 /*-----------------------------*/
1040 /* Lowest Level Input Routines */
1041 /*-----------------------------*/
1044 int pol_lookchar(void)
1053 int pol_inchar(void)
1060 while (currentf >= 0 && (c = pol_getch(filep[currentf])) == EOF && filep[currentf] != NULL) {
1067 /*--------------------------------------------------------------*/
1068 /* getch - get a (possibly pushed back) character */
1069 /* if fp == NULL, then get character from inputline */
1070 /*--------------------------------------------------------------*/
1073 pol_getch (FILE *fp)
1081 if ((c = inputline[lineptr]) == EOS)
1093 /* push character back on input */
1098 sys_error (ERR_SEVERE, "too many characters pushed back [pol_ungetch]");
1105 get_inputline (FILE *fp)
1109 if (fgets (inputline, MAXLINE, fp) == NULL)
1116 set_inputline (const char* const line)
1120 strncpy (inputline, line, MAXLINE);