parser.cxx

changeset 34
0a9a5902beaa
parent 33
fd35f6cb5f28
child 35
3d3f6ed40171
equal deleted inserted replaced
33:fd35f6cb5f28 34:0a9a5902beaa
59 int g_CurMode = MODE_TOPLEVEL; 59 int g_CurMode = MODE_TOPLEVEL;
60 str g_CurState = ""; 60 str g_CurState = "";
61 bool g_stateSpawnDefined = false; 61 bool g_stateSpawnDefined = false;
62 bool g_GotMainLoop = false; 62 bool g_GotMainLoop = false;
63 63
64 // ============================================================================
65 // Main parser
64 void ScriptReader::BeginParse (ObjWriter* w) { 66 void ScriptReader::BeginParse (ObjWriter* w) {
65 while (Next()) { 67 while (Next()) {
66 if (!token.icompare ("state")) { 68 if (!token.icompare ("state")) {
67 MUST_TOPLEVEL 69 MUST_TOPLEVEL
68 70
110 MustNext ("{"); 112 MustNext ("{");
111 113
112 g_CurMode = MODE_EVENT; 114 g_CurMode = MODE_EVENT;
113 115
114 w->Write (DH_EVENT); 116 w->Write (DH_EVENT);
115 w->Write<long> (e->number); 117 w->Write<byte> (e->number);
116 g_NumEvents++; 118 g_NumEvents++;
117 continue; 119 continue;
118 } 120 }
119 121
120 if (!token.icompare ("mainloop")) { 122 if (!token.icompare ("mainloop")) {
179 181
180 if (!PeekNext().compare (";")) 182 if (!PeekNext().compare (";"))
181 MustNext (";"); 183 MustNext (";");
182 continue; 184 continue;
183 } 185 }
184 // Check global variables 186
185 ScriptVar* g = FindGlobalVariable (token); 187 // If it's not a keyword, parse it as an expression.
186 if (g) { 188 DataBuffer* b = ParseExpression (TYPE_VOID);
187 // Not in top level, unfortunately.. 189 w->WriteBuffer (b);
188 if (g_CurMode == MODE_TOPLEVEL) 190 delete b;
189 ParserError ("can't alter variables at top level"); 191 printf ("expression done!\n");
190 192 MustThis (";");
191 // Build operator string. Only '=' is one
192 // character, others are two.
193 MustNext ();
194 str oper = token;
195 if (token.compare ("=") != 0) {
196 MustNext ();
197 oper += token;
198 }
199
200 // Unary operators
201 if (!oper.compare ("++")) {
202 w->Write<long> (DH_INCGLOBALVAR);
203 w->Write<long> (g->index);
204 } else if (!oper.compare ("--")) {
205 w->Write<long> (DH_DECGLOBALVAR);
206 w->Write<long> (g->index);
207 } else {
208 // Binary operators
209 // And only with numbers for now too.
210 // TODO: make a proper expression parser!
211 MustNumber();
212
213 int val = atoi (token.chars());
214 w->Write<long> (DH_PUSHNUMBER);
215 w->Write<long> (val);
216
217 int h = !oper.compare("=") ? DH_ASSIGNGLOBALVAR :
218 !oper.compare("+=") ? DH_ADDGLOBALVAR :
219 !oper.compare("-=") ? DH_SUBGLOBALVAR :
220 !oper.compare("*=") ? DH_MULGLOBALVAR :
221 !oper.compare("/=") ? DH_DIVGLOBALVAR :
222 !oper.compare("%=") ? DH_MODGLOBALVAR : -1;
223
224 if (h == -1)
225 ParserError ("bad operator `%s`!", oper.chars());
226
227 w->Write<long> (h);
228 w->Write<long> (g->index);
229 }
230
231 MustNext (";");
232 continue;
233 }
234
235 // Check if it's a command.
236 CommandDef* comm = GetCommandByName (token);
237 if (comm) {
238 ParseCommand (comm, w);
239 continue;
240 }
241
242 ParserError ("unknown keyword `%s`", token.chars());
243 } 193 }
244 194
245 if (g_CurMode != MODE_TOPLEVEL) 195 if (g_CurMode != MODE_TOPLEVEL)
246 ParserError ("script did not end at top level; did you forget a `}`?"); 196 ParserError ("script did not end at top level; did you forget a `}`?");
247 197
248 // stateSpawn must be defined! 198 // stateSpawn must be defined!
249 if (!g_stateSpawnDefined) 199 if (!g_stateSpawnDefined)
250 ParserError ("script must have a state named `stateSpawn`!"); 200 ParserError ("script must have a state named `stateSpawn`!");
251 201
252 // Dump the last state's onenter and mainloop 202 // Dump the last state's onenter and mainloop
253 w->WriteBuffers(); 203 w->WriteBuffers ();
254 204
255 // If we added strings here, we need to write a list of them. 205 // String table
256 unsigned int stringcount = CountStringTable (); 206 w->WriteStringTable ();
257 if (stringcount) { 207 }
258 w->Write<long> (DH_STRINGLIST); 208
259 w->Write<long> (stringcount); 209 // ============================================================================
260 for (unsigned int a = 0; a < stringcount; a++) 210 // Parses a given command
261 w->WriteString (g_StringTable[a]); 211 DataBuffer* ScriptReader::ParseCommand (CommandDef* comm) {
262 } 212 DataBuffer* r = new DataBuffer(64);
263
264 printf ("%u string%s written\n", stringcount, PLURAL (stringcount));
265 }
266
267 void ScriptReader::ParseCommand (CommandDef* comm, ObjWriter* w) {
268 // If this was defined at top-level, we stop right at square one! 213 // If this was defined at top-level, we stop right at square one!
269 if (g_CurMode == MODE_TOPLEVEL) 214 if (g_CurMode == MODE_TOPLEVEL)
270 ParserError ("no commands allowed at top level!"); 215 ParserError ("no commands allowed at top level!");
271 216
217 printf ("token: %s\n", token.chars());
272 MustNext ("("); 218 MustNext ("(");
219
273 int curarg = 0; 220 int curarg = 0;
274 while (1) { 221 while (1) {
275 if (curarg >= comm->maxargs) { 222 if (!token.compare (")")) {
276 if (!PeekNext().compare (",")) 223 printf ("closing command with token `%s`\n", token.chars());
277 ParserError ("got `,` while expecting command-terminating `)`, are you passing too many parameters? (max %d)", 224 if (curarg < comm->numargs - 1)
278 comm->maxargs); 225 ParserError ("too few arguments passed to %s\n", comm->name.chars());
279 MustNext (")"); 226 break;
280 curarg++; 227 curarg++;
281 break; 228 }
282 } 229 printf ("prev token: %s\n", token.chars());
283 230 MustNext ();
284 if (!PeekNext().len()) 231 printf ("next token: %s\n", token.chars());
285 ParserError ("unexpected end-of-file, unterminated command"); 232
286 233 // jump back to start
287 // If we get a ")" now, the user probably gave too few parameters 234 if (!token.compare (")"))
288 if (!PeekNext().compare (")")) 235 continue;
289 ParserError ("unexpected `)`, did you pass too few parameters? (need %d)", comm->numargs); 236
290 237 if (curarg >= comm->maxargs)
291 // Argument may be using a variable 238 ParserError ("too many arguments passed to %s\n", comm->name.chars());
292 ScriptVar* g = FindGlobalVariable (PeekNext ()); 239
293 if (g && comm->argtypes[curarg] != RETURNVAL_STRING) { 240 r->Merge (ParseExpression (comm->argtypes[curarg]));
294 // Advance cursor past the var name
295 Next();
296
297 w->Write<long> (DH_PUSHGLOBALVAR);
298 w->Write<long> (g->index);
299 } else {
300 // Check for raw value
301 switch (comm->argtypes[curarg]) {
302 case RETURNVAL_INT:
303 MustNumber();
304 w->Write<long> (DH_PUSHNUMBER);
305 w->Write<long> (atoi (token.chars ()));
306 break;
307 case RETURNVAL_BOOLEAN:
308 MustBool();
309 w->Write<long> (DH_PUSHNUMBER);
310 w->Write<long> (BoolValue ());
311 break;
312 case RETURNVAL_STRING:
313 MustString();
314 w->Write<long> (DH_PUSHSTRINGINDEX);
315 w->Write<long> (PushToStringTable (token.chars()));
316 break;
317 }
318 }
319 241
320 if (curarg < comm->numargs - 1) { 242 if (curarg < comm->numargs - 1) {
321 MustNext (","); 243 MustThis (",");
322 } else if (curarg < comm->maxargs - 1) { 244 } else if (curarg < comm->maxargs - 1) {
323 // Can continue, but can terminate as well. 245 // Can continue, but can terminate as well.
324 if (!PeekNext ().compare (")")) { 246 if (!token.compare (")")) {
325 MustNext (")");
326 curarg++; 247 curarg++;
327 break; 248 break;
328 } else 249 } else
329 MustNext (","); 250 MustThis (",");
330 } 251 }
331 252
332 curarg++; 253 curarg++;
333 } 254 }
334 MustNext (";");
335 255
336 // If the script skipped any optional arguments, fill in defaults. 256 // If the script skipped any optional arguments, fill in defaults.
337 while (curarg < comm->maxargs) { 257 while (curarg < comm->maxargs) {
338 w->Write<long> (DH_PUSHNUMBER); 258 r->Write<byte> (DH_PUSHNUMBER);
339 w->Write<long> (comm->defvals[curarg]); 259 r->Write<byte> (comm->defvals[curarg]);
340 curarg++; 260 curarg++;
341 } 261 }
342 262
343 w->Write<long> (DH_COMMAND); 263 r->Write<byte> (DH_COMMAND);
344 w->Write<long> (comm->number); 264 r->Write<byte> (comm->number);
345 w->Write<long> (comm->maxargs); 265 r->Write<byte> (comm->maxargs);
346 } 266
267 printf ("command complete\n");
268 return r;
269 }
270
271 static bool IsAssignmentOperator (int oper) {
272 switch (oper) {
273 case OPER_ASSIGNADD:
274 case OPER_ASSIGNSUB:
275 case OPER_ASSIGNMUL:
276 case OPER_ASSIGNDIV:
277 case OPER_ASSIGNMOD:
278 case OPER_ASSIGN:
279 return true;
280 }
281 return false;
282 }
283
284 // ============================================================================
285 static long DataHeaderByOperator (ScriptVar* var, int oper) {
286 if (IsAssignmentOperator (oper)) {
287 if (!var)
288 error ("operator %d requires left operand to be a variable\n", oper);
289
290 // TODO: At the moment, vars only are global
291 switch (oper) {
292 case OPER_ASSIGNADD: return DH_ADDGLOBALVAR;
293 case OPER_ASSIGNSUB: return DH_SUBGLOBALVAR;
294 case OPER_ASSIGNMUL: return DH_MULGLOBALVAR;
295 case OPER_ASSIGNDIV: return DH_DIVGLOBALVAR;
296 case OPER_ASSIGNMOD: return DH_MODGLOBALVAR;
297 case OPER_ASSIGN: return DH_ASSIGNGLOBALVAR;
298 default: error ("bad assignment operator!!\n");
299 }
300 }
301
302 switch (oper) {
303 case OPER_ADD: return DH_ADD;
304 case OPER_SUBTRACT: return DH_SUBTRACT;
305 case OPER_MULTIPLY: return DH_MULTIPLY;
306 case OPER_DIVIDE: return DH_DIVIDE;
307 case OPER_MODULUS: return DH_MODULUS;
308 }
309
310 error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper);
311 return 0;
312 }
313
314 // ============================================================================
315 // Parses an expression
316 DataBuffer* ScriptReader::ParseExpression (int reqtype) {
317 printf ("begin parsing expression. this token is `%s`, next token is `%s`\n",
318 token.chars(), PeekNext().chars());
319 DataBuffer* retbuf = new DataBuffer (64);
320
321 DataBuffer* lb = NULL;
322
323 ScriptVar* var = FindGlobalVariable (token);
324 if (var) {
325 MustNext ();
326 if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction
327 ParserError ("can't alter variables at top level");
328 reqtype = TYPE_INT;
329 } else
330 lb = ParseExprValue (reqtype);
331 printf ("done\n");
332
333 // Get an operator
334 printf ("parse operator at token %s\n", token.chars());
335 int oper = ParseOperator ();
336 printf ("got %d\n", oper);
337 // No operator found - stop here.
338 if (oper == -1) {
339 retbuf->Merge (lb);
340 printf ("expression complete without operator, stopping at `%s`\n", token.chars());
341 return retbuf;
342 }
343
344 // Parse the right operand,
345 printf ("parse right operand\n");
346 MustNext ();
347 DataBuffer* rb = ParseExprValue (reqtype);
348 printf ("done\n");
349
350 retbuf->Merge (lb);
351 retbuf->Merge (rb);
352
353 long dh = DataHeaderByOperator (var, oper);
354 retbuf->Write<byte> (dh);
355
356 if (IsAssignmentOperator (oper))
357 retbuf->Write<byte> (var->index);
358
359 printf ("expression complete\n");
360 return retbuf;
361 }
362
363 // ============================================================================
364 // Parsess an operator from tokens and returns an identifier.
365 int ScriptReader::ParseOperator () {
366 str oper;
367 oper += PeekNext ();
368
369 // Check one-char operators
370 int o = !oper.compare ("=") ? OPER_ASSIGN :
371 !oper.compare ("+") ? OPER_ADD :
372 !oper.compare ("-") ? OPER_SUBTRACT :
373 !oper.compare ("*") ? OPER_MULTIPLY :
374 !oper.compare ("/") ? OPER_DIVIDE :
375 !oper.compare ("%") ? OPER_MODULUS :
376 -1;
377
378 if (o != -1) {
379 MustNext ();
380 return o;
381 }
382
383 // Two-char operators
384 MustNext ();
385 oper += PeekNext (1);
386
387 o = !oper.compare ("+=") ? OPER_ASSIGNADD :
388 !oper.compare ("-=") ? OPER_ASSIGNSUB :
389 !oper.compare ("*=") ? OPER_ASSIGNMUL :
390 !oper.compare ("/=") ? OPER_ASSIGNDIV :
391 !oper.compare ("%=") ? OPER_ASSIGNMOD :
392 -1;
393
394 if (o != -1) {
395 MustNext ();
396 MustNext ();
397 }
398
399 return o;
400 }
401
402 // ============================================================================
403 // Parses a value in the expression and returns the data needed to push
404 // it, contained in a data buffer. A value can be either a variable, a command,
405 // a literal or an expression.
406 DataBuffer* ScriptReader::ParseExprValue (int reqtype) {
407 printf ("parse expr value `%s` with requirement type %d\n", token.chars(), reqtype);
408 DataBuffer* b = new DataBuffer(16);
409
410 ScriptVar* g;
411
412 if (!token.compare ("(")) {
413 printf ("value is an expression\n");
414 // Expression
415 MustNext ();
416 DataBuffer* c = ParseExpression (reqtype);
417 b->Merge (c);
418 MustNext (")");
419 } else if (CommandDef* comm = FindCommand (token)) {
420 printf ("value is a command\n");
421 delete b;
422
423 // Command
424 if (reqtype && comm->returnvalue != reqtype)
425 ParserError ("%s returns an incompatible data type", comm->name.chars());
426 return ParseCommand (comm);
427 } else if ((g = FindGlobalVariable (token)) && reqtype != TYPE_STRING) {
428 printf ("value is a global var\n");
429 // Global variable
430 b->Write<byte> (DH_PUSHGLOBALVAR);
431 b->Write<byte> (g->index);
432 } else {
433 printf ("value is a literal\n");
434 // If nothing else, check for literal
435 switch (reqtype) {
436 case TYPE_VOID:
437 ParserError ("bad syntax");
438 break;
439 case TYPE_INT: {
440 if (!token.isnumber ())
441 ParserError ("expected an integer, got `%s`", token.chars());
442
443 // All values are written unsigned - thus we need to write the value's
444 // absolute value, followed by an unary minus if it was negative.
445 b->Write<byte> (DH_PUSHNUMBER);
446 long v = atoi (token.chars ());
447 b->Write<byte> (static_cast<byte> (abs (v)));
448 if (v < 0)
449 b->Write<byte> (DH_UNARYMINUS);
450 break;
451 }
452 case TYPE_STRING:
453 // PushToStringTable either returns the string index of the
454 // string if it finds it in the table, or writes it to the
455 // table and returns it index if it doesn't find it there.
456 printf ("value is a string literal\n");
457 MustString (true);
458 b->Write<byte> (DH_PUSHSTRINGINDEX);
459 b->Write<byte> (PushToStringTable (token.chars()));
460 break;
461 }
462 }
463
464 return b;
465 }

mercurial