lexer slightly relaxed: '{', '}' and ';' now delimiters
[k8jam.git] / src / scan.c
blobca9d3ddd2df203f7b32f24c7caa2a0be36d5a43a
1 /*
2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
5 */
6 /*
7 * scan.c - the jam yacc scanner
9 * 12/26/93 (seiwald) - bump buf in yylex to 10240 - yuk.
10 * 09/16/94 (seiwald) - check for overflows, unmatched {}'s, etc.
11 * Also handle tokens abutting EOF by remembering
12 * to return EOF now matter how many times yylex()
13 * reinvokes yyline().
14 * 02/11/95 (seiwald) - honor only punctuation keywords if SCAN_PUNCT.
15 * 07/27/95 (seiwald) - Include jamgram.h after scan.h, so that YYSTYPE is
16 * defined before Linux's yacc tries to redefine it.
17 * 01/10/01 (seiwald) - \ can now escape any whitespace char
18 * 11/04/02 (seiwald) - const-ing for string literals
20 #include "jam.h"
21 #include "lists.h"
22 #include "parse.h"
23 #include "scan.h"
24 #include "jamgram.h"
25 #include "jambase.h"
26 #include "newstr.h"
29 const struct keyword {
30 const char *word;
31 int type;
32 } keywords[] = {
33 #include "jamgramtab.h"
34 {0,0}
38 struct include {
39 struct include *next; /* next serial include file */
40 const char *string; /* pointer into current line */
41 char **strings; /* for yyfparse() -- text to parse */
42 FILE *file; /* for yyfparse() -- file being read */
43 const char *fname; /* for yyfparse() -- file name */
44 int line; /* line counter for error messages */
45 char buf[512]; /* for yyfparse() -- line buffer */
48 static struct include *incp = 0; /* current file; head of chain */
50 static int scanmode = SCAN_NORMAL;
51 static int anyerrors = 0;
52 static char *symdump (YYSTYPE *s);
54 /* no single token can be larger */
55 #define BIGGEST_TOKEN (10240)
59 * Set parser mode: normal, string, or keyword
61 void yymode (int n) {
62 scanmode = n;
66 void yyerror (const char *s) {
67 if (incp) printf("%s: line %d: ", incp->fname, incp->line);
68 printf("%s at %s\n", s, symdump(&yylval));
69 ++anyerrors;
73 int yyanyerrors (void) {
74 return anyerrors != 0;
78 void yyfparse (const char *s) {
79 struct include *i = (struct include *)malloc(sizeof(*i));
80 /* push this onto the incp chain */
81 i->string = "";
82 i->strings = 0;
83 i->file = 0;
84 i->fname = copystr(s);
85 i->line = 0;
86 i->next = incp;
87 incp = i;
88 /* if the filename is "+", it means use the internal jambase */
89 if (!strcmp(s, "+")) i->strings = jambase;
94 * yyline() - read new line and return first character
96 * fabricates a continuous stream of characters across include files, returning EOF at the bitter end
98 int yyline (void) {
99 struct include *i = incp;
101 if (!incp) return EOF;
102 /* once we start reading from the input stream, we reset the
103 * include insertion point so that the next include file becomes
104 * the head of the list */
105 /* if there is more data in this line, return it */
106 if (*i->string) return *i->string++;
107 /* if we're reading from an internal string list, go to the next string */
108 if (i->strings) {
109 if (!*i->strings) goto next;
110 ++i->line;
111 i->string = *(i->strings++);
112 return *i->string++;
114 /* if necessary, open the file */
115 if (!i->file) {
116 FILE *f = stdin;
117 if (strcmp(i->fname, "-") && !(f = fopen(i->fname, "r"))) perror(i->fname);
118 i->file = f;
120 /* if there's another line in this file, start it */
121 if (i->file && fgets(i->buf, sizeof(i->buf), i->file)) {
122 ++i->line;
123 i->string = i->buf;
124 return *i->string++;
126 next:
127 /* this include is done */
128 /* free it up and return EOF so yyparse() returns to parse_file() */
129 incp = i->next;
130 /* close file, free name */
131 if (i->file && i->file != stdin) fclose(i->file);
132 freestr(i->fname);
133 free((char *)i);
134 return EOF;
139 * yylex() - set yylval to current token; return its type
141 * Macros to move things along:
143 * yychar() - return and advance character; invalid after EOF
144 * yyprev() - back up one character; invalid before yychar()
146 * yychar() returns a continuous stream of characters, until it hits
147 * the EOF of the current include file.
149 #define yychar() (*incp->string ? *incp->string++ : yyline())
150 #define yyprev() (incp->string--)
153 /* eat white space */
154 static int skipSpaces (int c) {
155 for (;;) {
156 /* skip past white space */
157 while (c != EOF && isspace(c)) c = yychar();
158 /* not a comment? swallow up comment line */
159 if (c != '#') break;
160 while ((c = yychar()) != EOF && c != '\n') ;
162 return c;
166 int yylex (void) {
167 int c;
168 char buf[BIGGEST_TOKEN];
169 char *b = buf;
171 if (!incp) goto eof;
172 /* get first character (whitespace or of token) */
173 c = yychar();
174 if (scanmode == SCAN_STRING) {
175 /* if scanning for a string (action's {}'s), look for the closing brace */
176 /* we handle matching braces, if they match! */
177 int nest = 1;
179 while (c != EOF && b < buf+sizeof(buf)) {
180 if (c == '{') ++nest;
181 if (c == '}' && !--nest) break;
182 *b++ = c;
183 c = yychar();
185 /* we ate the ending brace -- regurgitate it */
186 if (c != EOF) yyprev();
187 /* check obvious errors */
188 if (b == buf+sizeof(buf)) { yyerror("action block too big"); goto eof; }
189 if (nest) { yyerror("unmatched {} in action block"); goto eof; }
190 *b = 0;
191 yylval.type = STRING;
192 yylval.string = newstr(buf);
193 } else {
194 char *b = buf;
195 const struct keyword *k;
196 int inquote = 0, notkeyword = 0;
198 c = skipSpaces(c);
199 /* c now points to the first character of a token */
200 if (c == EOF) goto eof;
201 //printf(":'%c'\n", c);
202 #if 0
203 if (!isalpha(c) && c != '$' && c != '_' && c != '"' && c != '\'') {
204 const struct keyword *kgood = NULL;
205 /* special chars are delimiters */
206 while (c != EOF) {
207 *b++ = c;
208 *b = 0;
209 for (k = keywords; k->word != NULL; ++k) if (!isalpha(k->word[0]) && strcmp(buf, k->word) == 0) break;
210 if (k->word != NULL) {
211 /* good keyword */
212 kgood = k;
213 c = yychar();
214 continue;
216 /* bad keyword */
217 break;
219 if (c != EOF) --b; /* remove last char from token buffer */
220 if (kgood != NULL) {
221 /* ok, we got it */
222 printf("![%s]\n", buf);
223 if (c != EOF) yyprev();
224 goto lexdone;
227 /* bad luck, try it another way */
228 #else
229 if (c == '{' || c == '}' || c == ';') {
230 *b++ = c;
231 goto lexdone;
233 #endif
234 /* while scanning the word, disqualify it for (expensive)
235 * keyword lookup when we can: $anything, "anything", \anything */
236 notkeyword = (c == '$');
237 /* look for white space to delimit word */
238 /* "'s get stripped but preserve white space */
239 /* \ protects next character */
240 while (c != EOF && b < buf+sizeof(buf) && (inquote || !isspace(c))) {
241 if (c == '"') {
242 /* begin or end " */
243 inquote = !inquote;
244 notkeyword = 1;
245 } else if (c == '{' || c == '}' || c == ';') {
246 /* k8: allow specials to work as delimiters */
247 if (!inquote) break;
248 *b++ = c;
249 } else if (c != '\\') {
250 /* normal char */
251 *b++ = c;
252 } else if ((c = yychar()) != EOF) {
253 /* \c */
254 *b++ = c;
255 notkeyword = 1;
256 } else {
257 /* \EOF */
258 break;
260 c = yychar();
262 /* we looked ahead a character - back up */
263 if (c != EOF) yyprev();
264 lexdone:
265 /* check obvious errors */
266 if (b == buf+sizeof(buf)) { yyerror("string too big"); goto eof; }
267 if (inquote) { yyerror("unmatched \" in string"); goto eof; }
268 /* scan token table */
269 /* don't scan if it's obviously not a keyword or if its */
270 /* an alphabetic when were looking for punctuation */
271 *b = 0;
272 yylval.type = ARG;
273 if (!notkeyword && !(isalpha(*buf) && scanmode == SCAN_PUNCT)) {
274 for (k = keywords; k->word; ++k) {
275 if (*buf == *k->word && strcmp(k->word, buf) == 0) {
276 yylval.type = k->type;
277 yylval.string = k->word; /* used by symdump */
278 break;
282 if (yylval.type == ARG) yylval.string = newstr(buf);
284 if (DEBUG_SCAN) printf("scan %s\n", symdump(&yylval));
285 return yylval.type;
286 eof:
287 yylval.type = EOF;
288 return yylval.type;
292 static char *symdump (YYSTYPE *s) {
293 static char buf[BIGGEST_TOKEN+20];
295 switch (s->type) {
296 case EOF: sprintf(buf, "EOF"); break;
297 case 0: sprintf(buf, "unknown symbol %s", s->string); break;
298 case ARG: sprintf(buf, "argument %s", s->string); break;
299 case STRING: sprintf(buf, "string \"%s\"", s->string); break;
300 default: sprintf(buf, "keyword %s", s->string); break;
302 return buf;