| 1 /* |
|
| 2 * botc source code |
|
| 3 * Copyright (C) 2012 Santeri `Dusk` Piippo |
|
| 4 * All rights reserved. |
|
| 5 * |
|
| 6 * Redistribution and use in source and binary forms, with or without |
|
| 7 * modification, are permitted provided that the following conditions are met: |
|
| 8 * |
|
| 9 * 1. Redistributions of source code must retain the above copyright notice, |
|
| 10 * this list of conditions and the following disclaimer. |
|
| 11 * 2. Redistributions in binary form must reproduce the above copyright notice, |
|
| 12 * this list of conditions and the following disclaimer in the documentation |
|
| 13 * and/or other materials provided with the distribution. |
|
| 14 * 3. Neither the name of the developer nor the names of its contributors may |
|
| 15 * be used to endorse or promote products derived from this software without |
|
| 16 * specific prior written permission. |
|
| 17 * 4. Redistributions in any form must be accompanied by information on how to |
|
| 18 * obtain complete source code for the software and any accompanying |
|
| 19 * software that uses the software. The source code must either be included |
|
| 20 * in the distribution or be available for no more than the cost of |
|
| 21 * distribution plus a nominal fee, and must be freely redistributable |
|
| 22 * under reasonable conditions. For an executable file, complete source |
|
| 23 * code means the source code for all modules it contains. It does not |
|
| 24 * include source code for modules or files that typically accompany the |
|
| 25 * major components of the operating system on which the executable file |
|
| 26 * runs. |
|
| 27 * |
|
| 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
| 29 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
| 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
| 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
| 32 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
| 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
| 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
| 35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
| 36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
| 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
| 38 * POSSIBILITY OF SUCH DAMAGE. |
|
| 39 */ |
|
| 40 |
|
| 41 #define __PARSER_CXX__ |
|
| 42 |
|
| 43 #include <stdio.h> |
|
| 44 #include <stdlib.h> |
|
| 45 #include "common.h" |
|
| 46 #include "str.h" |
|
| 47 #include "objwriter.h" |
|
| 48 #include "scriptreader.h" |
|
| 49 #include "events.h" |
|
| 50 #include "commands.h" |
|
| 51 #include "stringtable.h" |
|
| 52 #include "variables.h" |
|
| 53 #include "array.h" |
|
| 54 |
|
| 55 #define MUST_TOPLEVEL if (g_CurMode != MODE_TOPLEVEL) \ |
|
| 56 ParserError ("%s-statements may only be defined at top level!", token.chars()); |
|
| 57 |
|
| 58 #define MUST_NOT_TOPLEVEL if (g_CurMode == MODE_TOPLEVEL) \ |
|
| 59 ParserError ("%s-statements may not be defined at top level!", token.chars()); |
|
| 60 |
|
| 61 #define SCOPE(n) scopestack[g_ScopeCursor - n] |
|
| 62 |
|
| 63 int g_NumStates = 0; |
|
| 64 int g_NumEvents = 0; |
|
| 65 parsermode_e g_CurMode = MODE_TOPLEVEL; |
|
| 66 str g_CurState = ""; |
|
| 67 bool g_stateSpawnDefined = false; |
|
| 68 bool g_GotMainLoop = false; |
|
| 69 unsigned int g_ScopeCursor = 0; |
|
| 70 DataBuffer* g_IfExpression = NULL; |
|
| 71 bool g_CanElse = false; |
|
| 72 str* g_UndefinedLabels[MAX_MARKS]; |
|
| 73 bool g_Neurosphere = false; // neurosphere-compat |
|
| 74 array<constinfo_t> g_ConstInfo; |
|
| 75 |
|
| 76 // ============================================================================ |
|
| 77 // Main parser code. Begins read of the script file, checks the syntax of it |
|
| 78 // and writes the data to the object file via ObjWriter - which also takes care |
|
| 79 // of necessary buffering so stuff is written in the correct order. |
|
| 80 void ScriptReader::ParseBotScript (ObjWriter* w) { |
|
| 81 // Zero the entire block stack first |
|
| 82 for (int i = 0; i < MAX_SCOPE; i++) |
|
| 83 ZERO(scopestack[i]); |
|
| 84 |
|
| 85 for (int i = 0; i < MAX_MARKS; i++) |
|
| 86 g_UndefinedLabels[i] = NULL; |
|
| 87 |
|
| 88 while (Next()) { |
|
| 89 // Check if else is potentically valid |
|
| 90 if (token == "else" && !g_CanElse) |
|
| 91 ParserError ("else without preceding if"); |
|
| 92 if (token != "else") |
|
| 93 g_CanElse = false; |
|
| 94 |
|
| 95 // ============================================================ |
|
| 96 if (token == "state") { |
|
| 97 MUST_TOPLEVEL |
|
| 98 |
|
| 99 MustString (); |
|
| 100 |
|
| 101 // State name must be a word. |
|
| 102 if (token.first (" ") != token.len()) |
|
| 103 ParserError ("state name must be a single word, got `%s`", token.chars()); |
|
| 104 str statename = token; |
|
| 105 |
|
| 106 // stateSpawn is special - it *must* be defined. If we |
|
| 107 // encountered it, then mark down that we have it. |
|
| 108 if (-token == "statespawn") |
|
| 109 g_stateSpawnDefined = true; |
|
| 110 |
|
| 111 // Must end in a colon |
|
| 112 MustNext (":"); |
|
| 113 |
|
| 114 // Write the previous state's onenter and |
|
| 115 // mainloop buffers to file now |
|
| 116 if (g_CurState.len()) |
|
| 117 w->WriteBuffers(); |
|
| 118 |
|
| 119 w->Write (DH_STATENAME); |
|
| 120 w->WriteString (statename); |
|
| 121 w->Write (DH_STATEIDX); |
|
| 122 w->Write (g_NumStates); |
|
| 123 |
|
| 124 g_NumStates++; |
|
| 125 g_CurState = token; |
|
| 126 g_GotMainLoop = false; |
|
| 127 continue; |
|
| 128 } |
|
| 129 |
|
| 130 // ============================================================ |
|
| 131 if (token == "event") { |
|
| 132 MUST_TOPLEVEL |
|
| 133 |
|
| 134 // Event definition |
|
| 135 MustString (); |
|
| 136 |
|
| 137 EventDef* e = FindEventByName (token); |
|
| 138 if (!e) |
|
| 139 ParserError ("bad event, got `%s`\n", token.chars()); |
|
| 140 |
|
| 141 MustNext ("{"); |
|
| 142 |
|
| 143 g_CurMode = MODE_EVENT; |
|
| 144 |
|
| 145 w->Write (DH_EVENT); |
|
| 146 w->Write (e->number); |
|
| 147 g_NumEvents++; |
|
| 148 continue; |
|
| 149 } |
|
| 150 |
|
| 151 // ============================================================ |
|
| 152 if (token == "mainloop") { |
|
| 153 MUST_TOPLEVEL |
|
| 154 MustNext ("{"); |
|
| 155 |
|
| 156 // Mode must be set before dataheader is written here! |
|
| 157 g_CurMode = MODE_MAINLOOP; |
|
| 158 w->Write (DH_MAINLOOP); |
|
| 159 continue; |
|
| 160 } |
|
| 161 |
|
| 162 // ============================================================ |
|
| 163 if (token == "onenter" || token == "onexit") { |
|
| 164 MUST_TOPLEVEL |
|
| 165 bool onenter = token == "onenter"; |
|
| 166 MustNext ("{"); |
|
| 167 |
|
| 168 // Mode must be set before dataheader is written here, |
|
| 169 // because onenter goes to a separate buffer. |
|
| 170 g_CurMode = onenter ? MODE_ONENTER : MODE_ONEXIT; |
|
| 171 w->Write (onenter ? DH_ONENTER : DH_ONEXIT); |
|
| 172 continue; |
|
| 173 } |
|
| 174 |
|
| 175 // ============================================================ |
|
| 176 if (token == "int" || token == "str" || token == "bool") { |
|
| 177 // For now, only globals are supported |
|
| 178 if (g_CurMode != MODE_TOPLEVEL || g_CurState.len()) |
|
| 179 ParserError ("variables must only be global for now"); |
|
| 180 |
|
| 181 type_e type = (token == "int") ? TYPE_INT : |
|
| 182 (token == "str") ? TYPE_STRING : |
|
| 183 TYPE_BOOL; |
|
| 184 |
|
| 185 MustNext (); |
|
| 186 |
|
| 187 // Var name must not be a number |
|
| 188 if (token.isnumber()) |
|
| 189 ParserError ("variable name must not be a number"); |
|
| 190 |
|
| 191 str varname = token; |
|
| 192 ScriptVar* var = DeclareGlobalVariable (this, type, varname); |
|
| 193 |
|
| 194 if (!var) |
|
| 195 ParserError ("declaring %s variable %s failed", |
|
| 196 g_CurState.len() ? "state" : "global", varname.chars()); |
|
| 197 |
|
| 198 MustNext (";"); |
|
| 199 continue; |
|
| 200 } |
|
| 201 |
|
| 202 // ============================================================ |
|
| 203 // Goto |
|
| 204 if (token == "goto") { |
|
| 205 MUST_NOT_TOPLEVEL |
|
| 206 |
|
| 207 // Get the name of the label |
|
| 208 MustNext (); |
|
| 209 |
|
| 210 // Find the mark this goto statement points to |
|
| 211 unsigned int m = w->FindMark (token); |
|
| 212 |
|
| 213 // If not set, define it |
|
| 214 if (m == MAX_MARKS) { |
|
| 215 m = w->AddMark (token); |
|
| 216 g_UndefinedLabels[m] = new str (token); |
|
| 217 } |
|
| 218 |
|
| 219 // Add a reference to the mark. |
|
| 220 w->Write (DH_GOTO); |
|
| 221 w->AddReference (m); |
|
| 222 MustNext (";"); |
|
| 223 continue; |
|
| 224 } |
|
| 225 |
|
| 226 // ============================================================ |
|
| 227 // If |
|
| 228 if (token == "if") { |
|
| 229 MUST_NOT_TOPLEVEL |
|
| 230 PushScope (); |
|
| 231 |
|
| 232 // Condition |
|
| 233 MustNext ("("); |
|
| 234 |
|
| 235 // Read the expression and write it. |
|
| 236 MustNext (); |
|
| 237 DataBuffer* c = ParseExpression (TYPE_INT); |
|
| 238 w->WriteBuffer (c); |
|
| 239 |
|
| 240 MustNext (")"); |
|
| 241 MustNext ("{"); |
|
| 242 |
|
| 243 // Add a mark - to here temporarily - and add a reference to it. |
|
| 244 // Upon a closing brace, the mark will be adjusted. |
|
| 245 unsigned int marknum = w->AddMark (""); |
|
| 246 |
|
| 247 // Use DH_IFNOTGOTO - if the expression is not true, we goto the mark |
|
| 248 // we just defined - and this mark will be at the end of the scope block. |
|
| 249 w->Write (DH_IFNOTGOTO); |
|
| 250 w->AddReference (marknum); |
|
| 251 |
|
| 252 // Store it |
|
| 253 SCOPE(0).mark1 = marknum; |
|
| 254 SCOPE(0).type = SCOPETYPE_IF; |
|
| 255 continue; |
|
| 256 } |
|
| 257 |
|
| 258 if (token == "else") { |
|
| 259 MUST_NOT_TOPLEVEL |
|
| 260 MustNext ("{"); |
|
| 261 |
|
| 262 // Don't use PushScope as it resets the scope |
|
| 263 g_ScopeCursor++; |
|
| 264 if (g_ScopeCursor >= MAX_SCOPE) |
|
| 265 ParserError ("too deep scope"); |
|
| 266 |
|
| 267 if (SCOPE(0).type != SCOPETYPE_IF) |
|
| 268 ParserError ("else without preceding if"); |
|
| 269 |
|
| 270 // Write down to jump to the end of the else statement |
|
| 271 // Otherwise we have fall-throughs |
|
| 272 SCOPE(0).mark2 = w->AddMark (""); |
|
| 273 |
|
| 274 // Instruction to jump to the end after if block is complete |
|
| 275 w->Write (DH_GOTO); |
|
| 276 w->AddReference (SCOPE(0).mark2); |
|
| 277 |
|
| 278 // Move the ifnot mark here and set type to else |
|
| 279 w->MoveMark (SCOPE(0).mark1); |
|
| 280 SCOPE(0).type = SCOPETYPE_ELSE; |
|
| 281 continue; |
|
| 282 } |
|
| 283 |
|
| 284 // ============================================================ |
|
| 285 // While |
|
| 286 if (token == "while") { |
|
| 287 MUST_NOT_TOPLEVEL |
|
| 288 PushScope (); |
|
| 289 |
|
| 290 // While loops need two marks - one at the start of the loop and one at the |
|
| 291 // end. The condition is checked at the very start of the loop, if it fails, |
|
| 292 // we use goto to skip to the end of the loop. At the end, we loop back to |
|
| 293 // the beginning with a go-to statement. |
|
| 294 unsigned int mark1 = w->AddMark (""); // start |
|
| 295 unsigned int mark2 = w->AddMark (""); // end |
|
| 296 |
|
| 297 // Condition |
|
| 298 MustNext ("("); |
|
| 299 MustNext (); |
|
| 300 DataBuffer* expr = ParseExpression (TYPE_INT); |
|
| 301 MustNext (")"); |
|
| 302 MustNext ("{"); |
|
| 303 |
|
| 304 // Write condition |
|
| 305 w->WriteBuffer (expr); |
|
| 306 |
|
| 307 // Instruction to go to the end if it fails |
|
| 308 w->Write (DH_IFNOTGOTO); |
|
| 309 w->AddReference (mark2); |
|
| 310 |
|
| 311 // Store the needed stuff |
|
| 312 SCOPE(0).mark1 = mark1; |
|
| 313 SCOPE(0).mark2 = mark2; |
|
| 314 SCOPE(0).type = SCOPETYPE_WHILE; |
|
| 315 continue; |
|
| 316 } |
|
| 317 |
|
| 318 // ============================================================ |
|
| 319 // For loop |
|
| 320 if (token == "for") { |
|
| 321 MUST_NOT_TOPLEVEL |
|
| 322 PushScope (); |
|
| 323 |
|
| 324 // Initializer |
|
| 325 MustNext ("("); |
|
| 326 MustNext (); |
|
| 327 DataBuffer* init = ParseStatement (w); |
|
| 328 if (!init) |
|
| 329 ParserError ("bad statement for initializer of for"); |
|
| 330 |
|
| 331 MustNext (";"); |
|
| 332 |
|
| 333 // Condition |
|
| 334 MustNext (); |
|
| 335 DataBuffer* cond = ParseExpression (TYPE_INT); |
|
| 336 if (!cond) |
|
| 337 ParserError ("bad statement for condition of for"); |
|
| 338 |
|
| 339 MustNext (";"); |
|
| 340 |
|
| 341 // Incrementor |
|
| 342 MustNext (); |
|
| 343 DataBuffer* incr = ParseStatement (w); |
|
| 344 if (!incr) |
|
| 345 ParserError ("bad statement for incrementor of for"); |
|
| 346 |
|
| 347 MustNext (")"); |
|
| 348 MustNext ("{"); |
|
| 349 |
|
| 350 // First, write out the initializer |
|
| 351 w->WriteBuffer (init); |
|
| 352 |
|
| 353 // Init two marks |
|
| 354 int mark1 = w->AddMark (""); |
|
| 355 int mark2 = w->AddMark (""); |
|
| 356 |
|
| 357 // Add the condition |
|
| 358 w->WriteBuffer (cond); |
|
| 359 w->Write (DH_IFNOTGOTO); |
|
| 360 w->AddReference (mark2); |
|
| 361 |
|
| 362 // Store the marks and incrementor |
|
| 363 SCOPE(0).mark1 = mark1; |
|
| 364 SCOPE(0).mark2 = mark2; |
|
| 365 SCOPE(0).buffer1 = incr; |
|
| 366 SCOPE(0).type = SCOPETYPE_FOR; |
|
| 367 continue; |
|
| 368 } |
|
| 369 |
|
| 370 // ============================================================ |
|
| 371 // Do/while loop |
|
| 372 if (token == "do") { |
|
| 373 MUST_NOT_TOPLEVEL |
|
| 374 PushScope (); |
|
| 375 MustNext ("{"); |
|
| 376 SCOPE(0).mark1 = w->AddMark (""); |
|
| 377 SCOPE(0).type = SCOPETYPE_DO; |
|
| 378 continue; |
|
| 379 } |
|
| 380 |
|
| 381 // ============================================================ |
|
| 382 // Switch |
|
| 383 if (token == "switch") { |
|
| 384 /* This goes a bit tricky. switch is structured in the |
|
| 385 * bytecode followingly: |
|
| 386 * (expression) |
|
| 387 * case a: goto casemark1 |
|
| 388 * case b: goto casemark2 |
|
| 389 * case c: goto casemark3 |
|
| 390 * goto mark1 // jump to end if no matches |
|
| 391 * casemark1: ... |
|
| 392 * casemark2: ... |
|
| 393 * casemark3: ... |
|
| 394 * mark1: // end mark |
|
| 395 */ |
|
| 396 |
|
| 397 MUST_NOT_TOPLEVEL |
|
| 398 PushScope (); |
|
| 399 MustNext ("("); |
|
| 400 MustNext (); |
|
| 401 w->WriteBuffer (ParseExpression (TYPE_INT)); |
|
| 402 MustNext (")"); |
|
| 403 MustNext ("{"); |
|
| 404 SCOPE(0).type = SCOPETYPE_SWITCH; |
|
| 405 SCOPE(0).mark1 = w->AddMark (""); // end mark |
|
| 406 SCOPE(0).buffer1 = NULL; // default header |
|
| 407 continue; |
|
| 408 } |
|
| 409 |
|
| 410 // ============================================================ |
|
| 411 if (token == "case") { |
|
| 412 // case is only allowed inside switch |
|
| 413 if (SCOPE(0).type != SCOPETYPE_SWITCH) |
|
| 414 ParserError ("case label outside switch"); |
|
| 415 |
|
| 416 // Get the literal (Zandronum does not support expressions here) |
|
| 417 MustNumber (); |
|
| 418 int num = atoi (token.chars ()); |
|
| 419 MustNext (":"); |
|
| 420 |
|
| 421 for (int i = 0; i < MAX_CASE; i++) |
|
| 422 if (SCOPE(0).casenumbers[i] == num) |
|
| 423 ParserError ("multiple case %d labels in one switch", num); |
|
| 424 |
|
| 425 // Write down the expression and case-go-to. This builds |
|
| 426 // the case tree. The closing event will write the actual |
|
| 427 // blocks and move the marks appropriately. |
|
| 428 // AddSwitchCase will add the reference to the mark |
|
| 429 // for the case block that this heralds, and takes care |
|
| 430 // of buffering setup and stuff like that. |
|
| 431 // NULL the switch buffer for the case-go-to statement, |
|
| 432 // we want it all under the switch, not into the case-buffers. |
|
| 433 w->SwitchBuffer = NULL; |
|
| 434 w->Write (DH_CASEGOTO); |
|
| 435 w->Write (num); |
|
| 436 AddSwitchCase (w, NULL); |
|
| 437 SCOPE(0).casenumbers[SCOPE(0).casecursor] = num; |
|
| 438 continue; |
|
| 439 } |
|
| 440 |
|
| 441 if (token == "default") { |
|
| 442 if (SCOPE(0).type != SCOPETYPE_SWITCH) |
|
| 443 ParserError ("default label outside switch"); |
|
| 444 |
|
| 445 if (SCOPE(0).buffer1) |
|
| 446 ParserError ("multiple default labels in one switch"); |
|
| 447 |
|
| 448 MustNext (":"); |
|
| 449 |
|
| 450 // The default header is buffered into buffer1, since |
|
| 451 // it has to be the last of the case headers |
|
| 452 // |
|
| 453 // Since the expression is pushed into the switch |
|
| 454 // and is only popped when case succeeds, we have |
|
| 455 // to pop it with DH_DROP manually if we end up in |
|
| 456 // a default. |
|
| 457 DataBuffer* b = new DataBuffer; |
|
| 458 SCOPE(0).buffer1 = b; |
|
| 459 b->Write (DH_DROP); |
|
| 460 b->Write (DH_GOTO); |
|
| 461 AddSwitchCase (w, b); |
|
| 462 continue; |
|
| 463 } |
|
| 464 |
|
| 465 // ============================================================ |
|
| 466 // Break statement. |
|
| 467 if (token == "break") { |
|
| 468 if (!g_ScopeCursor) |
|
| 469 ParserError ("unexpected `break`"); |
|
| 470 |
|
| 471 w->Write (DH_GOTO); |
|
| 472 |
|
| 473 // switch and if use mark1 for the closing point, |
|
| 474 // for and while use mark2. |
|
| 475 switch (SCOPE(0).type) { |
|
| 476 case SCOPETYPE_IF: |
|
| 477 case SCOPETYPE_SWITCH: |
|
| 478 w->AddReference (SCOPE(0).mark1); |
|
| 479 break; |
|
| 480 case SCOPETYPE_FOR: |
|
| 481 case SCOPETYPE_WHILE: |
|
| 482 w->AddReference (SCOPE(0).mark2); |
|
| 483 break; |
|
| 484 default: |
|
| 485 ParserError ("unexpected `break`"); |
|
| 486 break; |
|
| 487 } |
|
| 488 |
|
| 489 MustNext (";"); |
|
| 490 continue; |
|
| 491 } |
|
| 492 |
|
| 493 // ============================================================ |
|
| 494 // Continue |
|
| 495 if (token == "continue") { |
|
| 496 MustNext (";"); |
|
| 497 |
|
| 498 int curs; |
|
| 499 bool found = false; |
|
| 500 |
|
| 501 // Drop through the scope until we find a loop block |
|
| 502 for (curs = g_ScopeCursor; curs > 0 && !found; curs--) { |
|
| 503 switch (scopestack[curs].type) { |
|
| 504 case SCOPETYPE_FOR: |
|
| 505 case SCOPETYPE_WHILE: |
|
| 506 case SCOPETYPE_DO: |
|
| 507 w->Write (DH_GOTO); |
|
| 508 w->AddReference (scopestack[curs].mark1); |
|
| 509 found = true; |
|
| 510 break; |
|
| 511 default: |
|
| 512 break; |
|
| 513 } |
|
| 514 } |
|
| 515 |
|
| 516 // No loop blocks |
|
| 517 if (!found) |
|
| 518 ParserError ("`continue`-statement not inside a loop"); |
|
| 519 |
|
| 520 continue; |
|
| 521 } |
|
| 522 |
|
| 523 // ============================================================ |
|
| 524 // Label |
|
| 525 if (PeekNext() == ":") { |
|
| 526 MUST_NOT_TOPLEVEL |
|
| 527 |
|
| 528 // want no conflicts.. |
|
| 529 if (IsKeyword (token)) |
|
| 530 ParserError ("label name `%s` conflicts with keyword\n", token.chars()); |
|
| 531 if (FindCommand (token)) |
|
| 532 ParserError ("label name `%s` conflicts with command name\n", token.chars()); |
|
| 533 if (FindGlobalVariable (token)) |
|
| 534 ParserError ("label name `%s` conflicts with variable\n", token.chars()); |
|
| 535 |
|
| 536 // See if a mark already exists for this label |
|
| 537 int mark = -1; |
|
| 538 for (int i = 0; i < MAX_MARKS; i++) { |
|
| 539 if (g_UndefinedLabels[i] && *g_UndefinedLabels[i] == token) { |
|
| 540 mark = i; |
|
| 541 w->MoveMark (i); |
|
| 542 |
|
| 543 // No longer undefinde |
|
| 544 delete g_UndefinedLabels[i]; |
|
| 545 g_UndefinedLabels[i] = NULL; |
|
| 546 } |
|
| 547 } |
|
| 548 |
|
| 549 // Not found in unmarked lists, define it now |
|
| 550 if (mark == -1) |
|
| 551 w->AddMark (token); |
|
| 552 |
|
| 553 MustNext (":"); |
|
| 554 continue; |
|
| 555 } |
|
| 556 |
|
| 557 // ============================================================ |
|
| 558 if (token == "const") { |
|
| 559 constinfo_t info; |
|
| 560 |
|
| 561 // Get the type |
|
| 562 MustNext (); |
|
| 563 info.type = GetTypeByName (token); |
|
| 564 |
|
| 565 if (info.type == TYPE_UNKNOWN || info.type == TYPE_VOID) |
|
| 566 ParserError ("unknown type `%s` for constant", (char*)token); |
|
| 567 |
|
| 568 MustNext (); |
|
| 569 info.name = token; |
|
| 570 |
|
| 571 MustNext ("="); |
|
| 572 |
|
| 573 switch (info.type) { |
|
| 574 case TYPE_BOOL: |
|
| 575 case TYPE_INT: |
|
| 576 MustNumber (false); |
|
| 577 info.val = token; |
|
| 578 break; |
|
| 579 case TYPE_STRING: |
|
| 580 MustString (); |
|
| 581 info.val = token; |
|
| 582 break; |
|
| 583 case TYPE_UNKNOWN: |
|
| 584 case TYPE_VOID: |
|
| 585 break; |
|
| 586 } |
|
| 587 |
|
| 588 g_ConstInfo << info; |
|
| 589 |
|
| 590 MustNext (";"); |
|
| 591 continue; |
|
| 592 } |
|
| 593 |
|
| 594 // ============================================================ |
|
| 595 if (token == "}") { |
|
| 596 // Closing brace |
|
| 597 |
|
| 598 // If we're in the block stack, we're descending down from it now |
|
| 599 if (g_ScopeCursor > 0) { |
|
| 600 switch (SCOPE(0).type) { |
|
| 601 case SCOPETYPE_IF: |
|
| 602 // Adjust the closing mark. |
|
| 603 w->MoveMark (SCOPE(0).mark1); |
|
| 604 |
|
| 605 // We're returning from if, thus else can be next |
|
| 606 g_CanElse = true; |
|
| 607 break; |
|
| 608 case SCOPETYPE_ELSE: |
|
| 609 // else instead uses mark1 for itself (so if expression |
|
| 610 // fails, jump to else), mark2 means end of else |
|
| 611 w->MoveMark (SCOPE(0).mark2); |
|
| 612 break; |
|
| 613 case SCOPETYPE_FOR: |
|
| 614 // Write the incrementor at the end of the loop block |
|
| 615 w->WriteBuffer (SCOPE(0).buffer1); |
|
| 616 // fall-thru |
|
| 617 case SCOPETYPE_WHILE: |
|
| 618 // Write down the instruction to go back to the start of the loop |
|
| 619 w->Write (DH_GOTO); |
|
| 620 w->AddReference (SCOPE(0).mark1); |
|
| 621 |
|
| 622 // Move the closing mark here since we're at the end of the while loop |
|
| 623 w->MoveMark (SCOPE(0).mark2); |
|
| 624 break; |
|
| 625 case SCOPETYPE_DO: { |
|
| 626 MustNext ("while"); |
|
| 627 MustNext ("("); |
|
| 628 MustNext (); |
|
| 629 DataBuffer* expr = ParseExpression (TYPE_INT); |
|
| 630 MustNext (")"); |
|
| 631 MustNext (";"); |
|
| 632 |
|
| 633 // If the condition runs true, go back to the start. |
|
| 634 w->WriteBuffer (expr); |
|
| 635 w->Write (DH_IFGOTO); |
|
| 636 w->AddReference (SCOPE(0).mark1); |
|
| 637 break; |
|
| 638 } |
|
| 639 case SCOPETYPE_SWITCH: { |
|
| 640 // Switch closes. Move down to the record buffer of |
|
| 641 // the lower block. |
|
| 642 if (SCOPE(1).casecursor != -1) |
|
| 643 w->SwitchBuffer = SCOPE(1).casebuffers[SCOPE(1).casecursor]; |
|
| 644 else |
|
| 645 w->SwitchBuffer = NULL; |
|
| 646 |
|
| 647 // If there was a default in the switch, write its header down now. |
|
| 648 // If not, write instruction to jump to the end of switch after |
|
| 649 // the headers (thus won't fall-through if no case matched) |
|
| 650 if (SCOPE(0).buffer1) |
|
| 651 w->WriteBuffer (SCOPE(0).buffer1); |
|
| 652 else { |
|
| 653 w->Write (DH_DROP); |
|
| 654 w->Write (DH_GOTO); |
|
| 655 w->AddReference (SCOPE(0).mark1); |
|
| 656 } |
|
| 657 |
|
| 658 // Go through all of the buffers we |
|
| 659 // recorded down and write them. |
|
| 660 for (unsigned int u = 0; u < MAX_CASE; u++) { |
|
| 661 if (!SCOPE(0).casebuffers[u]) |
|
| 662 continue; |
|
| 663 |
|
| 664 w->MoveMark (SCOPE(0).casemarks[u]); |
|
| 665 w->WriteBuffer (SCOPE(0).casebuffers[u]); |
|
| 666 } |
|
| 667 |
|
| 668 // Move the closing mark here |
|
| 669 w->MoveMark (SCOPE(0).mark1); |
|
| 670 break; |
|
| 671 } |
|
| 672 case SCOPETYPE_UNKNOWN: |
|
| 673 break; |
|
| 674 } |
|
| 675 |
|
| 676 // Descend down the stack |
|
| 677 g_ScopeCursor--; |
|
| 678 continue; |
|
| 679 } |
|
| 680 |
|
| 681 int dataheader = (g_CurMode == MODE_EVENT) ? DH_ENDEVENT : |
|
| 682 (g_CurMode == MODE_MAINLOOP) ? DH_ENDMAINLOOP : |
|
| 683 (g_CurMode == MODE_ONENTER) ? DH_ENDONENTER : |
|
| 684 (g_CurMode == MODE_ONEXIT) ? DH_ENDONEXIT : -1; |
|
| 685 |
|
| 686 if (dataheader == -1) |
|
| 687 ParserError ("unexpected `}`"); |
|
| 688 |
|
| 689 // Data header must be written before mode is changed because |
|
| 690 // onenter and mainloop go into special buffers, and we want |
|
| 691 // the closing data headers into said buffers too. |
|
| 692 w->Write (dataheader); |
|
| 693 g_CurMode = MODE_TOPLEVEL; |
|
| 694 |
|
| 695 if (PeekNext() == ";") |
|
| 696 MustNext (";"); |
|
| 697 continue; |
|
| 698 } |
|
| 699 |
|
| 700 // Check if it's a command |
|
| 701 CommandDef* comm = FindCommand (token); |
|
| 702 if (comm) { |
|
| 703 w->GetCurrentBuffer()->Merge (ParseCommand (comm)); |
|
| 704 MustNext (";"); |
|
| 705 continue; |
|
| 706 } |
|
| 707 |
|
| 708 // ============================================================ |
|
| 709 // If nothing else, parse it as a statement |
|
| 710 DataBuffer* b = ParseStatement (w); |
|
| 711 if (!b) |
|
| 712 ParserError ("unknown token `%s`", token.chars()); |
|
| 713 |
|
| 714 w->WriteBuffer (b); |
|
| 715 MustNext (";"); |
|
| 716 } |
|
| 717 |
|
| 718 // =============================================================================== |
|
| 719 // Script file ended. Do some last checks and write the last things to main buffer |
|
| 720 if (g_CurMode != MODE_TOPLEVEL) |
|
| 721 ParserError ("script did not end at top level; did you forget a `}`?"); |
|
| 722 |
|
| 723 // stateSpawn must be defined! |
|
| 724 if (!g_stateSpawnDefined) |
|
| 725 ParserError ("script must have a state named `stateSpawn`!"); |
|
| 726 |
|
| 727 for (int i = 0; i < MAX_MARKS; i++) |
|
| 728 if (g_UndefinedLabels[i]) |
|
| 729 ParserError ("label `%s` is referenced via `goto` but isn't defined\n", g_UndefinedLabels[i]->chars()); |
|
| 730 |
|
| 731 // Dump the last state's onenter and mainloop |
|
| 732 w->WriteBuffers (); |
|
| 733 |
|
| 734 // String table |
|
| 735 w->WriteStringTable (); |
|
| 736 } |
|
| 737 |
|
| 738 // ============================================================================ |
|
| 739 // Parses a command call |
|
| 740 DataBuffer* ScriptReader::ParseCommand (CommandDef* comm) { |
|
| 741 DataBuffer* r = new DataBuffer(64); |
|
| 742 if (g_CurMode == MODE_TOPLEVEL) |
|
| 743 ParserError ("command call at top level"); |
|
| 744 |
|
| 745 MustNext ("("); |
|
| 746 MustNext (); |
|
| 747 |
|
| 748 int curarg = 0; |
|
| 749 while (1) { |
|
| 750 if (token == ")") { |
|
| 751 if (curarg < comm->numargs) |
|
| 752 ParserError ("too few arguments passed to %s\n\tprototype: %s", |
|
| 753 comm->name.chars(), GetCommandPrototype (comm).chars()); |
|
| 754 break; |
|
| 755 curarg++; |
|
| 756 } |
|
| 757 |
|
| 758 if (curarg >= comm->maxargs) |
|
| 759 ParserError ("too many arguments passed to %s\n\tprototype: %s", |
|
| 760 comm->name.chars(), GetCommandPrototype (comm).chars()); |
|
| 761 |
|
| 762 r->Merge (ParseExpression (comm->argtypes[curarg])); |
|
| 763 MustNext (); |
|
| 764 |
|
| 765 if (curarg < comm->numargs - 1) { |
|
| 766 MustThis (","); |
|
| 767 MustNext (); |
|
| 768 } else if (curarg < comm->maxargs - 1) { |
|
| 769 // Can continue, but can terminate as well. |
|
| 770 if (token == ")") { |
|
| 771 curarg++; |
|
| 772 break; |
|
| 773 } else { |
|
| 774 MustThis (","); |
|
| 775 MustNext (); |
|
| 776 } |
|
| 777 } |
|
| 778 |
|
| 779 curarg++; |
|
| 780 } |
|
| 781 |
|
| 782 // If the script skipped any optional arguments, fill in defaults. |
|
| 783 while (curarg < comm->maxargs) { |
|
| 784 r->Write (DH_PUSHNUMBER); |
|
| 785 r->Write (comm->defvals[curarg]); |
|
| 786 curarg++; |
|
| 787 } |
|
| 788 |
|
| 789 r->Write (DH_COMMAND); |
|
| 790 r->Write (comm->number); |
|
| 791 r->Write (comm->maxargs); |
|
| 792 |
|
| 793 return r; |
|
| 794 } |
|
| 795 |
|
| 796 // ============================================================================ |
|
| 797 // Is the given operator an assignment operator? |
|
| 798 static bool IsAssignmentOperator (int oper) { |
|
| 799 switch (oper) { |
|
| 800 case OPER_ASSIGNADD: |
|
| 801 case OPER_ASSIGNSUB: |
|
| 802 case OPER_ASSIGNMUL: |
|
| 803 case OPER_ASSIGNDIV: |
|
| 804 case OPER_ASSIGNMOD: |
|
| 805 case OPER_ASSIGNLEFTSHIFT: |
|
| 806 case OPER_ASSIGNRIGHTSHIFT: |
|
| 807 case OPER_ASSIGN: |
|
| 808 return true; |
|
| 809 } |
|
| 810 return false; |
|
| 811 } |
|
| 812 |
|
| 813 // ============================================================================ |
|
| 814 // Finds an operator's corresponding dataheader |
|
| 815 static word DataHeaderByOperator (ScriptVar* var, int oper) { |
|
| 816 if (IsAssignmentOperator (oper)) { |
|
| 817 if (!var) |
|
| 818 error ("operator %d requires left operand to be a variable\n", oper); |
|
| 819 |
|
| 820 // TODO: At the moment, vars only are global |
|
| 821 // OPER_ASSIGNLEFTSHIFT and OPER_ASSIGNRIGHTSHIFT do not |
|
| 822 // have data headers, instead they are expanded out in |
|
| 823 // the operator parser |
|
| 824 switch (oper) { |
|
| 825 case OPER_ASSIGNADD: return DH_ADDGLOBALVAR; |
|
| 826 case OPER_ASSIGNSUB: return DH_SUBGLOBALVAR; |
|
| 827 case OPER_ASSIGNMUL: return DH_MULGLOBALVAR; |
|
| 828 case OPER_ASSIGNDIV: return DH_DIVGLOBALVAR; |
|
| 829 case OPER_ASSIGNMOD: return DH_MODGLOBALVAR; |
|
| 830 case OPER_ASSIGN: return DH_ASSIGNGLOBALVAR; |
|
| 831 default: error ("bad assignment operator!!\n"); |
|
| 832 } |
|
| 833 } |
|
| 834 |
|
| 835 switch (oper) { |
|
| 836 case OPER_ADD: return DH_ADD; |
|
| 837 case OPER_SUBTRACT: return DH_SUBTRACT; |
|
| 838 case OPER_MULTIPLY: return DH_MULTIPLY; |
|
| 839 case OPER_DIVIDE: return DH_DIVIDE; |
|
| 840 case OPER_MODULUS: return DH_MODULUS; |
|
| 841 case OPER_EQUALS: return DH_EQUALS; |
|
| 842 case OPER_NOTEQUALS: return DH_NOTEQUALS; |
|
| 843 case OPER_LESSTHAN: return DH_LESSTHAN; |
|
| 844 case OPER_GREATERTHAN: return DH_GREATERTHAN; |
|
| 845 case OPER_LESSTHANEQUALS: return DH_LESSTHANEQUALS; |
|
| 846 case OPER_GREATERTHANEQUALS: return DH_GREATERTHANEQUALS; |
|
| 847 case OPER_LEFTSHIFT: return DH_LSHIFT; |
|
| 848 case OPER_RIGHTSHIFT: return DH_RSHIFT; |
|
| 849 case OPER_OR: return DH_ORLOGICAL; |
|
| 850 case OPER_AND: return DH_ANDLOGICAL; |
|
| 851 case OPER_BITWISEOR: return DH_ORBITWISE; |
|
| 852 case OPER_BITWISEEOR: return DH_EORBITWISE; |
|
| 853 case OPER_BITWISEAND: return DH_ANDBITWISE; |
|
| 854 } |
|
| 855 |
|
| 856 error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper); |
|
| 857 return 0; |
|
| 858 } |
|
| 859 |
|
| 860 // ============================================================================ |
|
| 861 // Parses an expression, potentially recursively |
|
| 862 DataBuffer* ScriptReader::ParseExpression (type_e reqtype) { |
|
| 863 DataBuffer* retbuf = new DataBuffer (64); |
|
| 864 |
|
| 865 // Parse first operand |
|
| 866 retbuf->Merge (ParseExprValue (reqtype)); |
|
| 867 |
|
| 868 // Parse any and all operators we get |
|
| 869 int oper; |
|
| 870 while ((oper = ParseOperator (true)) != -1) { |
|
| 871 // We peeked the operator, move forward now |
|
| 872 Next (); |
|
| 873 |
|
| 874 // Can't be an assignement operator, those belong in assignments. |
|
| 875 if (IsAssignmentOperator (oper)) |
|
| 876 ParserError ("assignment operator inside expression"); |
|
| 877 |
|
| 878 // Parse the right operand. |
|
| 879 MustNext (); |
|
| 880 DataBuffer* rb = ParseExprValue (reqtype); |
|
| 881 |
|
| 882 if (oper == OPER_TERNARY) { |
|
| 883 // Ternary operator requires - naturally - a third operand. |
|
| 884 MustNext (":"); |
|
| 885 MustNext (); |
|
| 886 DataBuffer* tb = ParseExprValue (reqtype); |
|
| 887 |
|
| 888 // It also is handled differently: there isn't a dataheader for ternary |
|
| 889 // operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this. |
|
| 890 // Behold, big block of writing madness! :P |
|
| 891 int mark1 = retbuf->AddMark (""); // start of "else" case |
|
| 892 int mark2 = retbuf->AddMark (""); // end of expression |
|
| 893 retbuf->Write (DH_IFNOTGOTO); // if the first operand (condition) |
|
| 894 retbuf->AddMarkReference (mark1); // didn't eval true, jump into mark1 |
|
| 895 retbuf->Merge (rb); // otherwise, perform second operand (true case) |
|
| 896 retbuf->Write (DH_GOTO); // afterwards, jump to the end, which is |
|
| 897 retbuf->AddMarkReference (mark2); // marked by mark2. |
|
| 898 retbuf->MoveMark (mark1); // move mark1 at the end of the true case |
|
| 899 retbuf->Merge (tb); // perform third operand (false case) |
|
| 900 retbuf->MoveMark (mark2); // move the ending mark2 here |
|
| 901 } else { |
|
| 902 // Write to buffer |
|
| 903 retbuf->Merge (rb); |
|
| 904 retbuf->Write (DataHeaderByOperator (NULL, oper)); |
|
| 905 } |
|
| 906 } |
|
| 907 |
|
| 908 return retbuf; |
|
| 909 } |
|
| 910 |
|
| 911 // ============================================================================ |
|
| 912 // Parses an operator string. Returns the operator number code. |
|
| 913 #define ISNEXT(char) (!PeekNext (peek ? 1 : 0) == char) |
|
| 914 int ScriptReader::ParseOperator (bool peek) { |
|
| 915 str oper; |
|
| 916 if (peek) |
|
| 917 oper += PeekNext (); |
|
| 918 else |
|
| 919 oper += token; |
|
| 920 |
|
| 921 if (-oper == "strlen") |
|
| 922 return OPER_STRLEN; |
|
| 923 |
|
| 924 // Check one-char operators |
|
| 925 bool equalsnext = ISNEXT ("="); |
|
| 926 |
|
| 927 int o = (oper == "=" && !equalsnext) ? OPER_ASSIGN : |
|
| 928 (oper == ">" && !equalsnext && !ISNEXT (">")) ? OPER_GREATERTHAN : |
|
| 929 (oper == "<" && !equalsnext && !ISNEXT ("<")) ? OPER_LESSTHAN : |
|
| 930 (oper == "&" && !ISNEXT ("&")) ? OPER_BITWISEAND : |
|
| 931 (oper == "|" && !ISNEXT ("|")) ? OPER_BITWISEOR : |
|
| 932 (oper == "+" && !equalsnext) ? OPER_ADD : |
|
| 933 (oper == "-" && !equalsnext) ? OPER_SUBTRACT : |
|
| 934 (oper == "*" && !equalsnext) ? OPER_MULTIPLY : |
|
| 935 (oper == "/" && !equalsnext) ? OPER_DIVIDE : |
|
| 936 (oper == "%" && !equalsnext) ? OPER_MODULUS : |
|
| 937 (oper == "^") ? OPER_BITWISEEOR : |
|
| 938 (oper == "?") ? OPER_TERNARY : |
|
| 939 -1; |
|
| 940 |
|
| 941 if (o != -1) { |
|
| 942 return o; |
|
| 943 } |
|
| 944 |
|
| 945 // Two-char operators |
|
| 946 oper += PeekNext (peek ? 1 : 0); |
|
| 947 equalsnext = PeekNext (peek ? 2 : 1) == ("="); |
|
| 948 |
|
| 949 o = (oper == "+=") ? OPER_ASSIGNADD : |
|
| 950 (oper == "-=") ? OPER_ASSIGNSUB : |
|
| 951 (oper == "*=") ? OPER_ASSIGNMUL : |
|
| 952 (oper == "/=") ? OPER_ASSIGNDIV : |
|
| 953 (oper == "%=") ? OPER_ASSIGNMOD : |
|
| 954 (oper == "==") ? OPER_EQUALS : |
|
| 955 (oper == "!=") ? OPER_NOTEQUALS : |
|
| 956 (oper == ">=") ? OPER_GREATERTHANEQUALS : |
|
| 957 (oper == "<=") ? OPER_LESSTHANEQUALS : |
|
| 958 (oper == "&&") ? OPER_AND : |
|
| 959 (oper == "||") ? OPER_OR : |
|
| 960 (oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT : |
|
| 961 (oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT : |
|
| 962 -1; |
|
| 963 |
|
| 964 if (o != -1) { |
|
| 965 MustNext (); |
|
| 966 return o; |
|
| 967 } |
|
| 968 |
|
| 969 // Three-char opers |
|
| 970 oper += PeekNext (peek ? 2 : 1); |
|
| 971 o = oper == "<<=" ? OPER_ASSIGNLEFTSHIFT : |
|
| 972 oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT : |
|
| 973 -1; |
|
| 974 |
|
| 975 if (o != -1) { |
|
| 976 MustNext (); |
|
| 977 MustNext (); |
|
| 978 } |
|
| 979 |
|
| 980 return o; |
|
| 981 } |
|
| 982 |
|
| 983 // ============================================================================ |
|
| 984 str ScriptReader::ParseFloat () { |
|
| 985 MustNumber (true); |
|
| 986 str floatstring = token; |
|
| 987 |
|
| 988 // Go after the decimal point |
|
| 989 if (PeekNext () == ".") { |
|
| 990 Next ("."); |
|
| 991 MustNumber (false); |
|
| 992 floatstring += "."; |
|
| 993 floatstring += token; |
|
| 994 } |
|
| 995 |
|
| 996 return floatstring; |
|
| 997 } |
|
| 998 |
|
| 999 // ============================================================================ |
|
| 1000 // Parses a value in the expression and returns the data needed to push |
|
| 1001 // it, contained in a data buffer. A value can be either a variable, a command, |
|
| 1002 // a literal or an expression. |
|
| 1003 DataBuffer* ScriptReader::ParseExprValue (type_e reqtype) { |
|
| 1004 DataBuffer* b = new DataBuffer(16); |
|
| 1005 |
|
| 1006 ScriptVar* g; |
|
| 1007 |
|
| 1008 // Prefixing "!" means negation. |
|
| 1009 bool negate = (token == "!"); |
|
| 1010 if (negate) // Jump past the "!" |
|
| 1011 Next (); |
|
| 1012 |
|
| 1013 // Handle strlen |
|
| 1014 if (token == "strlen") { |
|
| 1015 MustNext ("("); |
|
| 1016 MustNext (); |
|
| 1017 |
|
| 1018 // By this token we should get a string constant. |
|
| 1019 constinfo_t* constant = FindConstant (token); |
|
| 1020 if (!constant || constant->type != TYPE_STRING) |
|
| 1021 ParserError ("strlen only works with const str"); |
|
| 1022 |
|
| 1023 if (reqtype != TYPE_INT) |
|
| 1024 ParserError ("strlen returns int but %s is expected\n", (char*)GetTypeName (reqtype)); |
|
| 1025 |
|
| 1026 b->Write (DH_PUSHNUMBER); |
|
| 1027 b->Write (constant->val.len ()); |
|
| 1028 |
|
| 1029 MustNext (")"); |
|
| 1030 } else if (token == "(") { |
|
| 1031 // Expression |
|
| 1032 MustNext (); |
|
| 1033 DataBuffer* c = ParseExpression (reqtype); |
|
| 1034 b->Merge (c); |
|
| 1035 MustNext (")"); |
|
| 1036 } else if (CommandDef* comm = FindCommand (token)) { |
|
| 1037 delete b; |
|
| 1038 |
|
| 1039 // Command |
|
| 1040 if (reqtype && comm->returnvalue != reqtype) |
|
| 1041 ParserError ("%s returns an incompatible data type", comm->name.chars()); |
|
| 1042 b = ParseCommand (comm); |
|
| 1043 } else if (constinfo_t* constant = FindConstant (token)) { |
|
| 1044 // Type check |
|
| 1045 if (reqtype != constant->type) |
|
| 1046 ParserError ("constant `%s` is %s, expression requires %s\n", |
|
| 1047 (char*)constant->name, (char*)GetTypeName (constant->type), |
|
| 1048 (char*)GetTypeName (reqtype)); |
|
| 1049 |
|
| 1050 switch (constant->type) { |
|
| 1051 case TYPE_BOOL: |
|
| 1052 case TYPE_INT: |
|
| 1053 b->Write (DH_PUSHNUMBER); |
|
| 1054 b->Write (atoi (constant->val)); |
|
| 1055 break; |
|
| 1056 case TYPE_STRING: |
|
| 1057 b->WriteString (constant->val); |
|
| 1058 break; |
|
| 1059 case TYPE_VOID: |
|
| 1060 case TYPE_UNKNOWN: |
|
| 1061 break; |
|
| 1062 } |
|
| 1063 } else if ((g = FindGlobalVariable (token))) { |
|
| 1064 // Global variable |
|
| 1065 b->Write (DH_PUSHGLOBALVAR); |
|
| 1066 b->Write (g->index); |
|
| 1067 } else { |
|
| 1068 // If nothing else, check for literal |
|
| 1069 switch (reqtype) { |
|
| 1070 case TYPE_VOID: |
|
| 1071 case TYPE_UNKNOWN: |
|
| 1072 ParserError ("unknown identifier `%s` (expected keyword, function or variable)", token.chars()); |
|
| 1073 break; |
|
| 1074 case TYPE_BOOL: |
|
| 1075 case TYPE_INT: { |
|
| 1076 MustNumber (true); |
|
| 1077 |
|
| 1078 // All values are written unsigned - thus we need to write the value's |
|
| 1079 // absolute value, followed by an unary minus for negatives. |
|
| 1080 b->Write (DH_PUSHNUMBER); |
|
| 1081 |
|
| 1082 long v = atol (token); |
|
| 1083 b->Write (static_cast<word> (abs (v))); |
|
| 1084 if (v < 0) |
|
| 1085 b->Write (DH_UNARYMINUS); |
|
| 1086 break; |
|
| 1087 } |
|
| 1088 case TYPE_STRING: |
|
| 1089 // PushToStringTable either returns the string index of the |
|
| 1090 // string if it finds it in the table, or writes it to the |
|
| 1091 // table and returns it index if it doesn't find it there. |
|
| 1092 MustString (true); |
|
| 1093 b->WriteString (token); |
|
| 1094 break; |
|
| 1095 } |
|
| 1096 } |
|
| 1097 |
|
| 1098 // Negate it now if desired |
|
| 1099 if (negate) |
|
| 1100 b->Write (DH_NEGATELOGICAL); |
|
| 1101 |
|
| 1102 return b; |
|
| 1103 } |
|
| 1104 |
|
| 1105 // ============================================================================ |
|
| 1106 // Parses an assignment. An assignment starts with a variable name, followed |
|
| 1107 // by an assignment operator, followed by an expression value. Expects current |
|
| 1108 // token to be the name of the variable, and expects the variable to be given. |
|
| 1109 DataBuffer* ScriptReader::ParseAssignment (ScriptVar* var) { |
|
| 1110 bool global = !var->statename.len (); |
|
| 1111 |
|
| 1112 // Get an operator |
|
| 1113 MustNext (); |
|
| 1114 int oper = ParseOperator (); |
|
| 1115 if (!IsAssignmentOperator (oper)) |
|
| 1116 ParserError ("expected assignment operator"); |
|
| 1117 |
|
| 1118 if (g_CurMode == MODE_TOPLEVEL) // TODO: lift this restriction |
|
| 1119 ParserError ("can't alter variables at top level"); |
|
| 1120 |
|
| 1121 // Parse the right operand |
|
| 1122 MustNext (); |
|
| 1123 DataBuffer* retbuf = new DataBuffer; |
|
| 1124 DataBuffer* expr = ParseExpression (var->type); |
|
| 1125 |
|
| 1126 // <<= and >>= do not have data headers. Solution: expand them. |
|
| 1127 // a <<= b -> a = a << b |
|
| 1128 // a >>= b -> a = a >> b |
|
| 1129 if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT) { |
|
| 1130 retbuf->Write (global ? DH_PUSHGLOBALVAR : DH_PUSHLOCALVAR); |
|
| 1131 retbuf->Write (var->index); |
|
| 1132 retbuf->Merge (expr); |
|
| 1133 retbuf->Write ((oper == OPER_ASSIGNLEFTSHIFT) ? DH_LSHIFT : DH_RSHIFT); |
|
| 1134 retbuf->Write (global ? DH_ASSIGNGLOBALVAR : DH_ASSIGNLOCALVAR); |
|
| 1135 retbuf->Write (var->index); |
|
| 1136 } else { |
|
| 1137 retbuf->Merge (expr); |
|
| 1138 long dh = DataHeaderByOperator (var, oper); |
|
| 1139 retbuf->Write (dh); |
|
| 1140 retbuf->Write (var->index); |
|
| 1141 } |
|
| 1142 |
|
| 1143 return retbuf; |
|
| 1144 } |
|
| 1145 |
|
| 1146 void ScriptReader::PushScope () { |
|
| 1147 g_ScopeCursor++; |
|
| 1148 if (g_ScopeCursor >= MAX_SCOPE) |
|
| 1149 ParserError ("too deep scope"); |
|
| 1150 |
|
| 1151 ScopeInfo* info = &SCOPE(0); |
|
| 1152 info->type = SCOPETYPE_UNKNOWN; |
|
| 1153 info->mark1 = 0; |
|
| 1154 info->mark2 = 0; |
|
| 1155 info->buffer1 = NULL; |
|
| 1156 info->casecursor = -1; |
|
| 1157 for (int i = 0; i < MAX_CASE; i++) { |
|
| 1158 info->casemarks[i] = MAX_MARKS; |
|
| 1159 info->casebuffers[i] = NULL; |
|
| 1160 info->casenumbers[i] = -1; |
|
| 1161 } |
|
| 1162 } |
|
| 1163 |
|
| 1164 DataBuffer* ScriptReader::ParseStatement (ObjWriter* w) { |
|
| 1165 if (FindConstant (token)) // There should not be constants here. |
|
| 1166 ParserError ("invalid use for constant\n"); |
|
| 1167 |
|
| 1168 // If it's a variable, expect assignment. |
|
| 1169 if (ScriptVar* var = FindGlobalVariable (token)) |
|
| 1170 return ParseAssignment (var); |
|
| 1171 |
|
| 1172 return NULL; |
|
| 1173 } |
|
| 1174 |
|
| 1175 void ScriptReader::AddSwitchCase (ObjWriter* w, DataBuffer* b) { |
|
| 1176 ScopeInfo* info = &SCOPE(0); |
|
| 1177 |
|
| 1178 info->casecursor++; |
|
| 1179 if (info->casecursor >= MAX_CASE) |
|
| 1180 ParserError ("too many cases in one switch"); |
|
| 1181 |
|
| 1182 // Init a mark for the case buffer |
|
| 1183 int m = w->AddMark (""); |
|
| 1184 info->casemarks[info->casecursor] = m; |
|
| 1185 |
|
| 1186 // Add a reference to the mark. "case" and "default" both |
|
| 1187 // add the necessary bytecode before the reference. |
|
| 1188 if (b) |
|
| 1189 b->AddMarkReference (m); |
|
| 1190 else |
|
| 1191 w->AddReference (m); |
|
| 1192 |
|
| 1193 // Init a buffer for the case block and tell the object |
|
| 1194 // writer to record all written data to it. |
|
| 1195 info->casebuffers[info->casecursor] = w->SwitchBuffer = new DataBuffer; |
|
| 1196 } |
|
| 1197 |
|
| 1198 constinfo_t* FindConstant (str token) { |
|
| 1199 for (uint i = 0; i < g_ConstInfo.size(); i++) |
|
| 1200 if (g_ConstInfo[i].name == token) |
|
| 1201 return &g_ConstInfo[i]; |
|
| 1202 return NULL; |
|
| 1203 } |
|