src/scriptreader.cxx

changeset 73
1ee9b312dc18
parent 72
03e4d9db3fd9
equal deleted inserted replaced
72:03e4d9db3fd9 73:1ee9b312dc18
1 #include "main.h"
2 #include "scriptreader.h"
3
4 #define STORE_POSITION \
5 bool stored_atnewline = atnewline; \
6 unsigned int stored_curline = curline[fc]; \
7 unsigned int stored_curchar = curchar[fc];
8
9 #define RESTORE_POSITION \
10 atnewline = stored_atnewline; \
11 curline[fc] = stored_curline; \
12 curchar[fc] = stored_curchar;
13
14 // ============================================================================
15 ScriptReader::ScriptReader (string path) {
16 token = "";
17 prevtoken = "";
18 prevpos = 0;
19 fc = -1;
20
21 for (unsigned int u = 0; u < MAX_FILESTACK; u++)
22 fp[u] = null;
23
24 OpenFile (path);
25 commentmode = 0;
26 }
27
28 // ============================================================================
29 ScriptReader::~ScriptReader () {
30 // If comment mode is 2 by the time the file ended, the
31 // comment was left unterminated. 1 is no problem, since
32 // it's terminated by newlines anyway.
33 if (commentmode == 2)
34 ParserError ("unterminated `/*`-style comment");
35
36 for (unsigned int u = 0; u < MAX_FILESTACK; u++) {
37 if (fp[u]) {
38 ParserWarning ("file idx %u remained open after parsing", u);
39 CloseFile (u);
40 }
41 }
42 }
43
44 // ============================================================================
45 // Opens a file and pushes its pointer to stack
46 void ScriptReader::OpenFile (string path) {
47 if (fc+1 >= MAX_FILESTACK)
48 ParserError ("supposed to open file `%s` but file stack is full! do you have recursive `#include` directives?",
49 path.chars());
50
51 // Save the position first.
52 if (fc != -1) {
53 savedpos[fc] = ftell (fp[fc]);
54 }
55
56 fc++;
57
58 fp[fc] = fopen (path, "r");
59 if (!fp[fc]) {
60 ParserError ("couldn't open %s for reading!\n", path.chars ());
61 exit (1);
62 }
63
64 fseek (fp[fc], 0, SEEK_SET);
65 filepath[fc] = path.chars();
66 curline[fc] = 1;
67 curchar[fc] = 1;
68 pos[fc] = 0;
69 atnewline = 0;
70 }
71
72 // ============================================================================
73 // Closes the current file
74 void ScriptReader::CloseFile (unsigned int u) {
75 if (u >= MAX_FILESTACK)
76 u = fc;
77
78 if (!fp[u])
79 return;
80
81 fclose (fp[u]);
82 fp[u] = null;
83 fc--;
84
85 if (fc != -1)
86 fseek (fp[fc], savedpos[fc], SEEK_SET);
87 }
88
89 // ============================================================================
90 char ScriptReader::ReadChar () {
91 if (feof (fp[fc]))
92 return 0;
93
94 char c;
95 if (!fread (&c, 1, 1, fp[fc]))
96 return 0;
97
98 // We're at a newline, thus next char read will begin the next line
99 if (atnewline) {
100 atnewline = false;
101 curline[fc]++;
102 curchar[fc] = 0; // gets incremented to 1
103 }
104
105 if (c == '\n') {
106 atnewline = true;
107
108 // Check for pre-processor directives
109 PreprocessDirectives ();
110 }
111
112 curchar[fc]++;
113 return c;
114 }
115
116 // ============================================================================
117 // Peeks the next character
118 char ScriptReader::PeekChar (int offset) {
119 // Store current position
120 long curpos = ftell (fp[fc]);
121 STORE_POSITION
122
123 // Forward by offset
124 fseek (fp[fc], offset, SEEK_CUR);
125
126 // Read the character
127 char* c = (char*)malloc (sizeof (char));
128
129 if (!fread (c, sizeof (char), 1, fp[fc])) {
130 fseek (fp[fc], curpos, SEEK_SET);
131 return 0;
132 }
133
134 // Rewind back
135 fseek (fp[fc], curpos, SEEK_SET);
136 RESTORE_POSITION
137
138 return c[0];
139 }
140
141 // ============================================================================
142 // Read a token from the file buffer. Returns true if token was found, false if not.
143 bool ScriptReader::Next (bool peek) {
144 prevpos = ftell (fp[fc]);
145 string tmp = "";
146
147 while (1) {
148 // Check end-of-file
149 if (feof (fp[fc])) {
150 // If we're just peeking, we shouldn't
151 // actually close anything..
152 if (peek)
153 break;
154
155 CloseFile ();
156 if (fc == -1)
157 break;
158 }
159
160 // Check if the next token possibly starts a comment.
161 if (PeekChar () == '/' && !tmp.len()) {
162 char c2 = PeekChar (1);
163 // C++-style comment
164 if (c2 == '/')
165 commentmode = 1;
166 else if (c2 == '*')
167 commentmode = 2;
168
169 // We don't need to actually read in the
170 // comment characters, since they will get
171 // ignored due to comment mode anyway.
172 }
173
174 c = ReadChar ();
175
176 // If this is a comment we're reading, check if this character
177 // gets the comment terminated, otherwise ignore it.
178 if (commentmode > 0) {
179 if (commentmode == 1 && c == '\n') {
180 // C++-style comments are terminated by a newline
181 commentmode = 0;
182 continue;
183 } else if (commentmode == 2 && c == '*') {
184 // C-style comments are terminated by a `*/`
185 if (PeekChar() == '/') {
186 commentmode = 0;
187 ReadChar ();
188 }
189 }
190
191 // Otherwise, ignore it.
192 continue;
193 }
194
195 // Non-alphanumber characters (sans underscore) break the word too.
196 // If there was prior data, the delimeter pushes the cursor back so
197 // that the next character will be the same delimeter. If there isn't,
198 // the delimeter itself is included (and thus becomes a token itself.)
199 if ((c >= 33 && c <= 47) ||
200 (c >= 58 && c <= 64) ||
201 (c >= 91 && c <= 96 && c != '_') ||
202 (c >= 123 && c <= 126)) {
203 if (tmp.len())
204 fseek (fp[fc], ftell (fp[fc]) - 1, SEEK_SET);
205 else
206 tmp += c;
207 break;
208 }
209
210 if (isspace (c)) {
211 // Don't break if we haven't gathered anything yet.
212 if (tmp.len())
213 break;
214 } else {
215 tmp += c;
216 }
217 }
218
219 // If we got nothing here, read failed. This should
220 // only happen in the case of EOF.
221 if (!tmp.len()) {
222 token = "";
223 return false;
224 }
225
226 pos[fc]++;
227 prevtoken = token;
228 token = tmp;
229 return true;
230 }
231
232 // ============================================================================
233 // Returns the next token without advancing the cursor.
234 string ScriptReader::PeekNext (int offset) {
235 // Store current information
236 string storedtoken = token;
237 int cpos = ftell (fp[fc]);
238 STORE_POSITION
239
240 // Advance on the token.
241 while (offset >= 0) {
242 if (!Next (true))
243 return "";
244 offset--;
245 }
246
247 string tmp = token;
248
249 // Restore position
250 fseek (fp[fc], cpos, SEEK_SET);
251 pos[fc]--;
252 token = storedtoken;
253 RESTORE_POSITION
254 return tmp;
255 }
256
257 // ============================================================================
258 void ScriptReader::Seek (unsigned int n, int origin) {
259 switch (origin) {
260 case SEEK_SET:
261 fseek (fp[fc], 0, SEEK_SET);
262 pos[fc] = 0;
263 break;
264 case SEEK_CUR:
265 break;
266 case SEEK_END:
267 printf ("ScriptReader::Seek: SEEK_END not yet supported.\n");
268 break;
269 }
270
271 for (unsigned int i = 0; i < n+1; i++)
272 Next();
273 }
274
275 // ============================================================================
276 void ScriptReader::MustNext (const char* c) {
277 if (!Next()) {
278 if (strlen (c))
279 ParserError ("expected `%s`, reached end of file instead\n", c);
280 else
281 ParserError ("expected a token, reached end of file instead\n");
282 }
283
284 if (strlen (c))
285 MustThis (c);
286 }
287
288 // ============================================================================
289 void ScriptReader::MustThis (const char* c) {
290 if (token != c)
291 ParserError ("expected `%s`, got `%s` instead", c, token.chars());
292 }
293
294 // ============================================================================
295 void ScriptReader::ParserError (const char* message, ...) {
296 char buf[512];
297 va_list va;
298 va_start (va, message);
299 sprintf (buf, message, va);
300 va_end (va);
301 ParserMessage ("\nError: ", buf);
302 exit (1);
303 }
304
305 // ============================================================================
306 void ScriptReader::ParserWarning (const char* message, ...) {
307 char buf[512];
308 va_list va;
309 va_start (va, message);
310 sprintf (buf, message, va);
311 va_end (va);
312 ParserMessage ("Warning: ", buf);
313 }
314
315 // ============================================================================
316 void ScriptReader::ParserMessage (const char* header, string message) {
317 if (fc >= 0 && fc < MAX_FILESTACK)
318 fprintf (stderr, "%s%s:%u:%u: %s\n",
319 header, filepath[fc].c_str(), curline[fc], curchar[fc], message.c_str());
320 else
321 fprintf (stderr, "%s%s\n", header, message.c_str());
322 }
323
324 // ============================================================================
325 // if gotquote == 1, the current token already holds the quotation mark.
326 void ScriptReader::MustString (bool gotquote) {
327 if (gotquote)
328 MustThis ("\"");
329 else
330 MustNext ("\"");
331
332 string string;
333 // Keep reading characters until we find a terminating quote.
334 while (1) {
335 // can't end here!
336 if (feof (fp[fc]))
337 ParserError ("unterminated string");
338
339 char c = ReadChar ();
340 if (c == '"')
341 break;
342
343 string += c;
344 }
345
346 token = string;
347 }
348
349 // ============================================================================
350 void ScriptReader::MustNumber (bool fromthis) {
351 if (!fromthis)
352 MustNext ();
353
354 string num = token;
355 if (num == "-") {
356 MustNext ();
357 num += token;
358 }
359
360 // "true" and "false" are valid numbers
361 if (-token == "true")
362 token = "1";
363 else if (-token == "false")
364 token = "0";
365 else {
366 bool ok;
367 int num = token.to_long (&ok);
368
369 if (!ok)
370 ParserError ("expected a number, got `%s`", token.chars());
371
372 token = num;
373 }
374 }

mercurial