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 } |