|
|
1.1 root 1: #include <stdio.h>
2: #include <ctype.h>
3: #include <setjmp.h>
4: #include "bc.h"
5: #include "yy.h"
6:
7: /*
8: * The jump buffer lexenv is used to hold the environment in
9: * yylex that will be returned to if there is some lexical
10: * error.
11: */
12:
13: static jmp_buf lexenv;
14:
15:
16: /*
17: * Lexerror issues the appropriate error message on stderr and
18: * then longjumps back to lex which return ERROR.
19: */
20:
21: lexerror(str)
22: char *str;
23: {
24: gerror("%r", &str);
25: longjmp(lexenv, TRUE);
26: }
27:
28: /*
29: * This buffer is used for strings, words, and shell commands.
30: */
31: static char buff[MAXSTRING];
32:
33: /*
34: * Yylex is the lexical analyzer for bc.
35: */
36:
37: yylex()
38: {
39: register int ch;
40: register int nexteq;
41: register FILE *inf = infile;
42: static int newline = TRUE; /* TRUE iff at start of line */
43: char *getword();
44: char *getqs();
45:
46: Again:
47: if (setjmp(lexenv))
48: return ERROR;
49: ch = getc(inf);
50: if (newline && ch == '!') {
51: shell();
52: goto Again;
53: }
54: while (ch == '\t' || ch == ' ')
55: ch = getc(inf);
56: if (inline != 0 && newline == TRUE)
57: inline += 1;
58: if (ch == '\n' || ch == EOF) {
59: newline = TRUE;
60: return (ch);
61: } else
62: newline = FALSE;
63: if ( ! isascii(ch))
64: lexerror("Illegal character: %d", ch);
65: if (isalpha(ch))
66: return (token(getword(ch)));
67: if (isdigit(ch)) {
68: if (digit(ch) < ibase) {
69: yylval.lvalue = getnum(ch);
70: return (NUMBER);
71: }
72: lexerror("Illegal digit: %c", ch);
73: }
74: if (ch == '.') {
75: ch = getc(inf);
76: ungetc(ch, inf);
77: if (digit(ch) < ibase) {
78: yylval.lvalue = getnum('.');
79: return (NUMBER);
80: }
81: return (DOT);
82: }
83: nexteq = next('=');
84: switch (ch) {
85: case '+': return (nexteq ? ADDAB : (next('+') ? INCR : ch));
86: case '-': return (nexteq ? SUBAB : (next('-') ? DECR : ch));
87: case '*': return (nexteq ? MULAB : ch);
88: case '%': return (nexteq ? REMAB : ch);
89: case '^': return (nexteq ? EXPAB : ch);
90: case '/':
91: if (!nexteq && next('*')) {
92: do {
93: ch = getc(inf);
94: } while (ch != '*' || !next('/'));
95: goto Again;
96: }
97: return (nexteq ? DIVAB : ch);
98: case '=': return (nexteq ? EQP : ch);
99: case '<': return (nexteq ? LEP : LTP);
100: case '>': return (nexteq ? GEP : GTP);
101: case '!': return (nexteq ? NEP : ch);
102: case '"':
103: yylval.svalue = getqs();
104: return(STRING);
105: default:
106: if (nexteq)
107: ungetc('=', inf);
108: return(ch);
109: }
110: }
111:
112: /*
113: * Next returns TRUE iff the next character on input is "testc".
114: * If the next character is not "testc" then it is pushed back
115: * for later consumption.
116: */
117:
118: int
119: next(testc)
120: char testc;
121: {
122: register int ch;
123: register int result;
124:
125: ch = getc(infile);
126: if (! (result = ch == testc))
127: ungetc(ch, infile);
128: return(result);
129: }
130:
131: /*
132: * Getqs reads in a quoted string. It assumes that the initial
133: * double quote has already been read in and it returns a pointer
134: * to an allocated area where the string has been copyied.
135: */
136:
137: char *
138: getqs()
139: {
140: register char *ptr;
141: register int ch;
142:
143: ptr = buff;
144: while ((ch = getc(infile)) != '"') {
145: if (ch == EOF || ch == '\n')
146: lexerror("Unexpected end of quoted string");
147: if (ptr < &buff[MAXSTRING])
148: *ptr++ = ch;
149: }
150: if (ptr >= &buff[MAXSTRING])
151: lexerror("String too long");
152: *ptr = '\0';
153: ptr = (char *)mpalc(1 + ptr - buff);
154: strcpy(ptr, buff);
155: return (ptr);
156: }
157:
158: /*
159: * Token looks up the string pointed to by "word" in the
160: * list of keywords. If the word is found, then it returns
161: * the corresponding value. If not, then it makes sure that
162: * the string is in the string table, sets yylval to a pointer to
163: * the string table entry and returns IDENTIFIER.
164: */
165:
166: int
167: token(word)
168: char *word;
169: {
170: static struct keyword {
171: char *key;
172: int keyval;
173: } keywords[] = {
174: "auto", AUTO, "break", BREAK, "continue", CONTINUE,
175: "define", DEFINE, "do", DO, "else", ELSE,
176: "for", FOR, "ibase", IBASE, "if", IF,
177: "length", LENGTH_, "obase", OBASE, "quit", QUIT,
178: "return", RETURN_, "scale", SCALE_, "sqrt", SQRT_,
179: "while", WHILE
180: };
181: register struct keyword *probe;
182: register struct keyword *fwa = keywords;
183: register struct keyword *lwa = &keywords[nel(keywords) - 1];
184: int cmp;
185: dicent *lookword();
186:
187: while (fwa <= lwa) {
188: probe = fwa + (lwa - fwa) / 2;
189: cmp = strcmp(word, probe->key);
190: if (cmp > 0)
191: fwa = probe + 1;
192: else if (cmp < 0)
193: lwa = probe - 1;
194: else
195: return (probe->keyval);
196: }
197: yylval.dvalue = lookword(word);
198: return (IDENTIFIER);
199: }
200:
201: /*
202: * Lookword looks up the string str in the string table dictionary.
203: * If it is not already there, then it adds it and initializes the
204: * type fields to indicate it as undefined.
205: * It returns the pointer to the dictionary entry for the string.
206: */
207:
208: dicent *
209: lookword(str)
210: char *str;
211: {
212: register dicent **father,
213: *probe;
214: register int rel;
215:
216: father = &dictionary;
217: while ( (probe = *father) != NULL) {
218: rel = strcmp(probe->word, str);
219: if (rel == 0)
220: return (probe);
221: father = (rel < 0 ? &probe->left : &probe->right);
222: }
223: probe = (dicent *)mpalc((sizeof *probe) + strlen(str) + 1);
224: probe->left = probe->right = NULL;
225: probe->globalt = probe->localt = UNDEFINED;
226: strcpy(probe->word, str);
227: *father = probe;
228: return (probe);
229: }
230:
231: /*
232: * Getword reads in a word (alphanumeric sequence) which starts
233: * with "ch". It returns a pointer to the string read in.
234: * Note that the pointer is to a static area and hence
235: * the result must be copyied if it is to be used after another
236: * call to getword.
237: */
238:
239: char *
240: getword(ch)
241: register char ch;
242: {
243: register char *chp = &buff[0];
244:
245: do {
246: if (chp < &buff[MAXWORD])
247: *chp++ = ch;
248: ch = getc(infile);
249: } while (isascii(ch) && isalnum(ch));
250: ungetc(ch, infile);
251: if (chp >= &buff[MAXWORD])
252: lexerror("Identifier too long");
253: *chp = '\0';
254: return (buff);
255: }
256:
257: /*
258: * Shell reads in a line from the standard input and forks a shell
259: * to execute it.
260: */
261:
262: shell()
263: {
264: register char *ptr;
265: register int ch;
266:
267: ptr = buff;
268: while ((ch = getc(infile)) != '\n' && ch != EOF)
269: if (ptr < &buff[MAXSTRING])
270: *ptr++ = ch;
271: if (ptr >= &buff[MAXSTRING]) {
272: fprintf(stderr, "! line too long\n");
273: return;
274: }
275: *ptr = '\0';
276: fflush(infile);
277: if (system(buff) == NOSHELL)
278: fprintf(stderr, "bc: shell couldn't execute\n");
279: printf("!\n");
280: }
281:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.