| 43 #include "string.h" |
43 #include "string.h" |
| 44 #include "str.h" |
44 #include "str.h" |
| 45 #include "common.h" |
45 #include "common.h" |
| 46 #include "scriptreader.h" |
46 #include "scriptreader.h" |
| 47 |
47 |
| 48 static bool IsWhitespace (char c) { |
|
| 49 // These characters are invisible, thus considered whitespace |
|
| 50 if (c <= 32 || c == 127 || c == 255) |
|
| 51 return true; |
|
| 52 |
|
| 53 return false; |
|
| 54 } |
|
| 55 |
|
| 56 ScriptReader::ScriptReader (str path) { |
48 ScriptReader::ScriptReader (str path) { |
| 57 if (!(fp = fopen (path, "r"))) { |
|
| 58 error ("couldn't open %s for reading!\n", path.chars ()); |
|
| 59 exit (1); |
|
| 60 } |
|
| 61 |
|
| 62 filepath = path; |
|
| 63 curline = 1; |
|
| 64 curchar = 1; |
|
| 65 pos = 0; |
|
| 66 token = ""; |
49 token = ""; |
| 67 atnewline = false; |
50 fc = -1; |
| |
51 |
| |
52 for (unsigned int u = 0; u < MAX_FILESTACK; u++) |
| |
53 fp[u] = NULL; |
| |
54 |
| |
55 OpenFile (path); |
| 68 commentmode = 0; |
56 commentmode = 0; |
| 69 } |
57 } |
| 70 |
58 |
| 71 ScriptReader::~ScriptReader () { |
59 ScriptReader::~ScriptReader () { |
| 72 FinalChecks (); |
60 FinalChecks (); |
| 73 fclose (fp); |
61 |
| |
62 for (unsigned int u = 0; u < MAX_FILESTACK; u++) { |
| |
63 if (fp[u]) { |
| |
64 ParserWarning ("file idx %u remained open after parsing", u); |
| |
65 CloseFile (u); |
| |
66 } |
| |
67 } |
| |
68 } |
| |
69 |
| |
70 // Opens a file and pushes its pointer to stack |
| |
71 void ScriptReader::OpenFile (str path) { |
| |
72 if (fc+1 >= MAX_FILESTACK) |
| |
73 ParserError ("supposed to open file `%s` but file stack is full! \ |
| |
74 do you have recursive `#include` directives?", |
| |
75 path.chars()); |
| |
76 |
| |
77 // Save the position first. |
| |
78 if (fc != -1) { |
| |
79 savedpos[fc] = ftell (fp[fc]); |
| |
80 } |
| |
81 |
| |
82 fc++; |
| |
83 |
| |
84 fp[fc] = fopen (path, "r"); |
| |
85 if (!fp[fc]) { |
| |
86 ParserError ("couldn't open %s for reading!\n", path.chars ()); |
| |
87 exit (1); |
| |
88 } |
| |
89 |
| |
90 fseek (fp[fc], 0, SEEK_SET); |
| |
91 filepath[fc] = path.chars(); |
| |
92 curline[fc] = 1; |
| |
93 curchar[fc] = 1; |
| |
94 pos[fc] = 0; |
| |
95 atnewline = 0; |
| |
96 } |
| |
97 |
| |
98 void ScriptReader::CloseFile (unsigned int u) { |
| |
99 if (u >= MAX_FILESTACK) |
| |
100 u = fc; |
| |
101 |
| |
102 if (!fp[u]) |
| |
103 return; |
| |
104 |
| |
105 fclose (fp[u]); |
| |
106 fp[u] = NULL; |
| |
107 fc--; |
| |
108 |
| |
109 if (fc != -1) |
| |
110 fseek (fp[fc], savedpos[fc], SEEK_SET); |
| 74 } |
111 } |
| 75 |
112 |
| 76 char ScriptReader::ReadChar () { |
113 char ScriptReader::ReadChar () { |
| |
114 if (feof (fp[fc])) |
| |
115 return 0; |
| |
116 |
| 77 char* c = (char*)malloc (sizeof (char)); |
117 char* c = (char*)malloc (sizeof (char)); |
| 78 if (!fread (c, sizeof (char), 1, fp)) |
118 if (!fread (c, sizeof (char), 1, fp[fc])) |
| 79 return 0; |
119 return 0; |
| 80 |
120 |
| 81 // We're at a newline, thus next char read will begin the next line |
121 // We're at a newline, thus next char read will begin the next line |
| 82 if (atnewline) { |
122 if (atnewline) { |
| 83 atnewline = false; |
123 atnewline = false; |
| 84 curline++; |
124 curline[fc]++; |
| 85 curchar = 0; // gets incremented to 1 |
125 curchar[fc] = 0; // gets incremented to 1 |
| 86 } |
126 } |
| 87 |
127 |
| 88 if (c[0] == '\n') |
128 if (c[0] == '\n') { |
| 89 atnewline = true; |
129 atnewline = true; |
| 90 |
130 |
| 91 curchar++; |
131 // Check for pre-processor directives |
| |
132 PreprocessDirectives (); |
| |
133 } |
| |
134 |
| |
135 curchar[fc]++; |
| 92 return c[0]; |
136 return c[0]; |
| 93 } |
137 } |
| 94 |
138 |
| 95 char ScriptReader::PeekChar (int offset) { |
139 char ScriptReader::PeekChar (int offset) { |
| 96 // Store current position |
140 // Store current position |
| 97 long curpos = ftell (fp); |
141 long curpos = ftell (fp[fc]); |
| 98 |
142 |
| 99 // Forward by offset |
143 // Forward by offset |
| 100 fseek (fp, offset, SEEK_CUR); |
144 fseek (fp[fc], offset, SEEK_CUR); |
| 101 |
145 |
| 102 // Read the character |
146 // Read the character |
| 103 char* c = (char*)malloc (sizeof (char)); |
147 char* c = (char*)malloc (sizeof (char)); |
| 104 if (!fread (c, sizeof (char), 1, fp)) |
148 |
| |
149 if (!fread (c, sizeof (char), 1, fp[fc])) { |
| |
150 fseek (fp[fc], curpos, SEEK_SET); |
| 105 return 0; |
151 return 0; |
| |
152 } |
| 106 |
153 |
| 107 // Rewind back |
154 // Rewind back |
| 108 fseek (fp, curpos, SEEK_SET); |
155 fseek (fp[fc], curpos, SEEK_SET); |
| 109 |
156 |
| 110 return c[0]; |
157 return c[0]; |
| 111 } |
158 } |
| 112 |
159 |
| 113 // true if was found, false if not. |
160 // true if was found, false if not. |
| 114 bool ScriptReader::Next () { |
161 bool ScriptReader::Next (bool peek) { |
| 115 str tmp = ""; |
162 str tmp = ""; |
| 116 |
163 // printf ("begin token\n"); |
| 117 while (!feof (fp)) { |
164 |
| |
165 while (1) { |
| |
166 // Check end-of-file |
| |
167 if (feof (fp[fc])) { |
| |
168 // If we're just peeking, we shouldn't |
| |
169 // actually close anything.. |
| |
170 if (peek) |
| |
171 break; |
| |
172 |
| |
173 CloseFile (); |
| |
174 if (fc == -1) |
| |
175 break; |
| |
176 } |
| |
177 |
| 118 // Check if the next token possibly starts a comment. |
178 // Check if the next token possibly starts a comment. |
| 119 if (PeekChar () == '/' && !tmp.len()) { |
179 if (PeekChar () == '/' && !tmp.len()) { |
| 120 char c2 = PeekChar (1); |
180 char c2 = PeekChar (1); |
| 121 // C++-style comment |
181 // C++-style comment |
| 122 if (c2 == '/') |
182 if (c2 == '/') |
| 181 if (!tmp.len()) { |
240 if (!tmp.len()) { |
| 182 token = ""; |
241 token = ""; |
| 183 return false; |
242 return false; |
| 184 } |
243 } |
| 185 |
244 |
| 186 pos++; |
245 pos[fc]++; |
| 187 token = tmp; |
246 token = tmp; |
| 188 return true; |
247 return true; |
| 189 } |
248 } |
| 190 |
249 |
| 191 // Returns the next token without advancing the cursor. |
250 // Returns the next token without advancing the cursor. |
| 192 str ScriptReader::PeekNext () { |
251 str ScriptReader::PeekNext () { |
| 193 // Store current position |
252 // Store current position |
| 194 int cpos = ftell (fp); |
253 int cpos = ftell (fp[fc]); |
| 195 |
254 |
| 196 // Advance on the token. |
255 // Advance on the token. |
| 197 if (!Next ()) |
256 if (!Next (true)) |
| 198 return ""; |
257 return ""; |
| 199 |
258 |
| 200 str tmp = token; |
259 str tmp = token; |
| 201 |
260 |
| 202 // Restore position |
261 // Restore position |
| 203 fseek (fp, cpos, SEEK_SET); |
262 fseek (fp[fc], cpos, SEEK_SET); |
| 204 pos--; |
263 pos[fc]--; |
| 205 |
264 |
| 206 return tmp; |
265 return tmp; |
| 207 } |
266 } |
| 208 |
267 |
| 209 void ScriptReader::Seek (unsigned int n, int origin) { |
268 void ScriptReader::Seek (unsigned int n, int origin) { |
| 210 switch (origin) { |
269 switch (origin) { |
| 211 case SEEK_SET: |
270 case SEEK_SET: |
| 212 fseek (fp, 0, SEEK_SET); |
271 fseek (fp[fc], 0, SEEK_SET); |
| 213 pos = 0; |
272 pos[fc] = 0; |
| 214 break; |
273 break; |
| 215 case SEEK_CUR: |
274 case SEEK_CUR: |
| 216 break; |
275 break; |
| 217 case SEEK_END: |
276 case SEEK_END: |
| 218 printf ("ScriptReader::Seek: SEEK_END not yet supported.\n"); |
277 printf ("ScriptReader::Seek: SEEK_END not yet supported.\n"); |
| 236 } |
295 } |
| 237 } |
296 } |
| 238 |
297 |
| 239 void ScriptReader::ParserError (const char* message, ...) { |
298 void ScriptReader::ParserError (const char* message, ...) { |
| 240 PERFORM_FORMAT (message, outmessage); |
299 PERFORM_FORMAT (message, outmessage); |
| 241 ParserMessage ("\nParse error\n", outmessage); |
300 ParserMessage ("\nError: ", outmessage); |
| 242 exit (1); |
301 exit (1); |
| 243 } |
302 } |
| 244 |
303 |
| 245 void ScriptReader::ParserWarning (const char* message, ...) { |
304 void ScriptReader::ParserWarning (const char* message, ...) { |
| 246 PERFORM_FORMAT (message, outmessage); |
305 PERFORM_FORMAT (message, outmessage); |
| 247 ParserMessage ("Warning: ", outmessage); |
306 ParserMessage ("Warning: ", outmessage); |
| 248 } |
307 } |
| 249 |
308 |
| 250 void ScriptReader::ParserMessage (const char* header, char* message) { |
309 void ScriptReader::ParserMessage (const char* header, char* message) { |
| 251 fprintf (stderr, "%sIn file %s, at line %u, col %u: %s\n", |
310 if (fc >= 0 && fc < MAX_FILESTACK) |
| 252 header, filepath.chars(), curline, curchar, message); |
311 fprintf (stderr, "%sIn file %s, at line %u, col %u: %s\n", |
| |
312 header, filepath[fc], curline[fc], curchar[fc], message); |
| |
313 else |
| |
314 fprintf (stderr, "%s%s\n", header, message); |
| 253 } |
315 } |
| 254 |
316 |
| 255 void ScriptReader::MustString () { |
317 void ScriptReader::MustString () { |
| 256 MustNext ("\""); |
318 MustNext ("\""); |
| 257 |
319 |
| 258 str string; |
320 str string; |
| 259 // Keep reading characters until we find a terminating quote. |
321 // Keep reading characters until we find a terminating quote. |
| 260 while (1) { |
322 while (1) { |
| 261 // can't end here! |
323 // can't end here! |
| 262 if (feof (fp)) |
324 if (feof (fp[fc])) |
| 263 ParserError ("unterminated string"); |
325 ParserError ("unterminated string"); |
| 264 |
326 |
| 265 char c = ReadChar (); |
327 char c = ReadChar (); |
| 266 if (c == '"') |
328 if (c == '"') |
| 267 break; |
329 break; |