1 /*-
2  * Copyright (c) 2007,2008 Igor Serikov
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation. The copyright holders make no representations about the
9  * suitability of this software for any purpose.  It is provided "as is"
10  * without express or implied warranty.
11  *
12  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
13  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
14  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
15  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
16  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
17  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18  * PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <limits.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <sys/param.h>
28 
29 #include "scanner.h"
30 #include "util.h"
31 
32 char Token [MAXPATHLEN + 1];
33 int Number;
34 
35 typedef struct SOURCE SOURCE;
36 
37 struct SOURCE {
38     SOURCE* Prev;
39     FILE* File;
40     char * FileName;
41     int Line;
42 };
43 
44 static SOURCE * Src = NULL;
45 static int TokenType, Expected = 0, Reinit = 0;
46 
47 /*  Mind prefetched token. */
OpenScanner(char * Fn)48 void OpenScanner (char * Fn) {
49     SOURCE * tsrc;
50 
51     if (!Reinit) {
52 	tsrc = (SOURCE*) Alloc (sizeof (SOURCE));
53 	tsrc->Prev = Src;
54 	Src = tsrc;
55     } else {
56 	Reinit = 0;
57 	Expected = 0;
58 	free (Src->FileName);
59     }
60     Src->FileName = DupStr (Fn);
61     Src->Line = 1;
62     Src->File = fopen (Src->FileName, "r");
63     if (!Src->File)
64 	Err ("cannot open %s", Src->FileName);
65 }
66 
GetToken(void)67 static inline void GetToken (void) {
68     int i, c;
69     char ** wa;
70     SOURCE * tsrc;
71 
72     do {
73     again:
74 	c = fgetc (Src->File);
75 	if (ferror (Src->File))
76 	    have_error:	Err ("error reading %s", Src->FileName);
77 	if (c == -1) {
78 	have_eof:
79 	    fclose (Src->File);
80 	    if (Src->Prev) {
81 		free (Src->FileName);
82 		tsrc = Src;
83 		Src = Src->Prev;
84 		free (tsrc);
85 		goto again;
86 	    }
87 	    Reinit = 1;
88 	    TokenType = w_eof;
89 	    return;
90 	}
91 	if (c == '\n') {
92 	    Src->Line ++;
93 	    goto again;
94 	}
95 	if (c == '#') {
96 	    do {
97 		c = fgetc (Src->File);
98 		if (ferror (Src->File))
99 		    goto have_error;
100 		if (c == -1)
101 		    goto have_eof;
102 	    } while (c != '\n');
103 	    Src->Line ++;
104 	    goto again;
105 	}
106     } while (isspace (c));
107 
108     if (c >= '0' && c <= '9') {
109 	Number = c - '0';
110 	for (;;) {
111 	    c = fgetc (Src->File);
112 	    if (ferror (Src->File))
113 		goto have_error;
114 	    if (c < '0' || c > '9') {
115 		if (c != -1 && (c == '_' || isalpha (c)))
116 		    Syntax ("invalid integer");
117 		ungetc (c, Src->File);
118 		TokenType = w_number;
119 		return;
120 	    }
121 	    if (Number > INT_MAX / 10)
122 		iover: Syntax ("integer overflow");
123 	    Number *= 10;
124 	    c -= '0';
125 	    Number += c;
126 	    if (Number < 0)
127 		goto iover;
128 	}
129     }
130 
131     if (c == '"') {
132 	i = 0;
133         for (;;) {
134 	    c = fgetc (Src->File);
135 	    if (ferror (Src->File))
136 		goto have_error;
137 	    if (c == -1 || c == '\n')
138 		have_unterm: Syntax ("unterminated quoted string");
139 	    if (c == '"') {
140 		Token [i] = 0;
141 		TokenType = w_string;
142 		return;
143 	    }
144 	    if (i > (int) sizeof Token - 1)
145 		Syntax ("too long quoted string");
146 	    if (c == '\\') {
147 		c = fgetc (Src->File);
148 		if (ferror (Src->File))
149 		    goto have_error;
150 		switch (c) {
151 		  case -1:
152 		  case '\n':
153 		    goto have_unterm;
154 		  case '\\':
155 		    c = '\\';
156 		    break;
157 		  case '"':
158 		    c = '"';
159 		    break;
160 		  case 't':
161 		    c = '\t';
162 		    break;
163 		  case 'r':
164 		    c = '\r';
165 		    break;
166 		  case 'n':
167 		    c = '\n';
168 		    break;
169 		  case 'b':
170 		    c = '\b';
171 		    break;
172 		  default:
173 		    Syntax ("invalid escape sequence");
174 		}
175 	    }
176 	    Token [i] = (char) c;
177 	    i ++;
178 	}
179     }
180 
181     if (c != '_' && !isalpha (c)) {
182 	for (wa = SymbolTable, TokenType = w_app; *wa; wa ++, TokenType ++)
183 	    if ((*wa) [0] == c)
184 		return;
185 	Syntax ("invalid character '%c'", c);
186     }
187 
188     i = 0;
189 
190     do {
191 	if (i > (int) sizeof Token - 1)
192 	    Syntax ("too long token");
193 	Token [i] = (char) c;
194 	i ++;
195 	c = fgetc (Src->File);
196 	if (ferror (Src->File))
197 	    goto have_error;
198     } while (c == '_' || (c >= '0' && c <= '9') || isalpha (c));
199 
200     ungetc (c, Src->File);
201 
202     Token [i] = 0;
203 
204     for (wa = SymbolTable, TokenType = w_app; *wa; wa ++, TokenType ++)
205 	if (strcmp (*wa, Token) == 0)
206 	    return;
207 
208     TokenType = w_name;
209 }
210 
Expect(int Type)211 int Expect (int Type) {
212     if (Expected == 0)
213 	GetToken ();
214     if (Type != TokenType) {
215 	Expected |= (int) 1 << Type;
216 	return 0;
217     }
218     Expected = 0;
219     return 1;
220 }
221 
MustBe(int Type)222 void MustBe (int Type) {
223     if (!Expect (Type))
224 	Unexpected ();
225 }
226 
Syntax(char * Str,...)227 void __attribute__((__noreturn__)) Syntax (char * Str, ...) {
228     va_list args;
229 
230     fprintf (stderr, "%s: %s(%u): ", GetProgName (), Src->FileName, Src->Line);
231 
232     va_start (args, Str);
233     vfprintf (stderr, Str, args);
234 
235     fputc ('\n', stderr);
236 
237     exit (1);
238 }
239 
Unexpected(void)240 void __attribute__((__noreturn__)) Unexpected (void) {
241     static char * strs [] = { "end of file", "numeral", "quoted string", "name"};
242 
243     int nf, n;
244     char c;
245 
246     fprintf (stderr, "%s: %s(%u): expected: ", GetProgName (), Src->FileName, Src->Line);
247 
248     nf = n = 0;
249     for (n = 0; Expected; n ++, Expected >>= 1)
250 	if (Expected & 1) {
251 	    if (nf)
252 		fputc (' ', stderr);
253 	    nf = 1;
254 	    if (n < w_app)
255 		fprintf (stderr, "%s", strs [n]);
256 	    else {
257 		c = SymbolTable [n - w_app] [0];
258 		if (c != '_' && !isalpha (c))
259 		    fprintf (stderr, "'%c'", c);
260 		else
261 		    fprintf (stderr, "\"%s\"", SymbolTable [n - w_app]);
262 	    }
263 	}
264 
265     fputc ('\n', stderr);
266 
267     exit (1);
268 }
269