src/parser.cc

changeset 88
5def6ff8b466
parent 87
8f65914e7046
child 89
029a330a9bef
equal deleted inserted replaced
87:8f65914e7046 88:5def6ff8b466
1 /*
2 Copyright 2012-2014 Santeri Piippo
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15 derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "parser.h"
30 #include "events.h"
31 #include "commands.h"
32 #include "stringtable.h"
33 #include "variables.h"
34 #include "containers.h"
35 #include "lexer.h"
36 #include "data_buffer.h"
37
38 #define SCOPE(n) (m_scope_stack[m_scope_cursor - n])
39
40 // ============================================================================
41 //
42 botscript_parser::botscript_parser() :
43 m_lx (new lexer) {}
44
45 // ============================================================================
46 //
47 botscript_parser::~botscript_parser()
48 {
49 delete m_lx;
50 }
51
52 // ============================================================================
53 //
54 void botscript_parser::check_toplevel()
55 {
56 if (m_current_mode != e_top_level_mode)
57 error ("%1-statements may only be defined at top level!", token_string());
58 }
59
60 // ============================================================================
61 //
62 void botscript_parser::check_not_toplevel()
63 {
64 if (m_current_mode == e_top_level_mode)
65 error ("%1-statements must not be defined at top level!", token_string());
66 }
67
68 // ============================================================================
69 // Main parser code. Begins read of the script file, checks the syntax of it
70 // and writes the data to the object file via Objwriter - which also takes care
71 // of necessary buffering so stuff is written in the correct order.
72 void botscript_parser::parse_botscript (string file_name)
73 {
74 // Lex and preprocess the file
75 m_lx->process_file (file_name);
76
77 m_current_mode = e_top_level_mode;
78 m_num_states = 0;
79 m_num_events = 0;
80 m_scope_cursor = 0;
81 m_state_spawn_defined = false;
82 m_got_main_loop = false;
83 m_if_expression = null;
84 m_can_else = false;
85
86 // Zero the entire block stack first
87 // TODO: this shouldn't be necessary
88 for (int i = 0; i < MAX_SCOPE; i++)
89 ZERO (m_scope_stack[i]);
90
91 while (m_lx->get_next())
92 {
93 // Check if else is potentically valid
94 if (token_is (tk_else) && !m_can_else)
95 error ("else without preceding if");
96
97 if (!token_is (tk_else))
98 m_can_else = false;
99
100 switch (m_lx->get_token()->type)
101 {
102 case tk_state:
103 parse_state_block();
104 break;
105
106 case tk_event:
107 parse_event_block();
108 break;
109
110 case tk_mainloop:
111 parse_mainloop();
112 break;
113
114 case tk_onenter:
115 case tk_onexit:
116 parse_on_enter_exit();
117 break;
118
119 case tk_int:
120 case tk_str:
121 case tk_void:
122 parse_variable_declaration();
123 break;
124
125 case tk_goto:
126 parse_goto();
127 break;
128
129 case tk_if:
130 parse_if();
131 break;
132
133 case tk_else:
134 parse_else();
135 break;
136
137 case tk_while:
138 parse_while_block();
139 break;
140
141 case tk_for:
142 parse_for_block();
143 break;
144
145 case tk_do:
146 parse_do_block();
147 break;
148
149 case tk_switch:
150 parse_switch_block();
151 break;
152
153 case tk_case:
154 parse_switch_case();
155 break;
156
157 case tk_default:
158 parse_switch_default();
159 break;
160
161 case tk_break:
162 parse_break();
163 break;
164
165 case tk_continue:
166 parse_continue();
167 break;
168
169 case tk_brace_end:
170 parse_block_end();
171 break;
172
173 case tk_const:
174 parse_const();
175 break;
176
177 case tk_eventdef:
178 parse_eventdef();
179 break;
180
181 case tk_funcdef:
182 parse_funcdef();
183 break;
184
185 default:
186 {
187 // Check for labels
188 lexer::token next;
189
190 if (token_is (tk_symbol) &&
191 m_lx->peek_next (&next) &&
192 next.type == tk_colon)
193 {
194 parse_label();
195 break;
196 }
197
198 // Check if it's a command
199 command_info* comm = find_command_by_name (token_string());
200
201 if (comm)
202 {
203 buffer()->merge_and_destroy (parse_command (comm));
204 m_lx->must_get_next (tk_semicolon);
205 continue;
206 }
207
208 // If nothing else, parse it as a statement
209 data_buffer* b = parse_statement();
210
211 if (!b)
212 error ("unknown token `%1`", token_string());
213
214 buffer()->merge_and_destroy (b);
215 m_lx->must_get_next (tk_semicolon);
216 }
217 break;
218 }
219 }
220
221 // ===============================================================================
222 // Script file ended. Do some last checks and write the last things to main buffer
223 if (m_current_mode != e_top_level_mode)
224 error ("script did not end at top level; a `}` is missing somewhere");
225
226 // stateSpawn must be defined!
227 if (!m_state_spawn_defined)
228 error ("script must have a state named `stateSpawn`!");
229
230 // Ensure no goto target is left undefined
231 if (m_undefined_labels.is_empty() == false)
232 {
233 string_list names;
234
235 for (undefined_label& undf : m_undefined_labels)
236 names << undf.name;
237
238 error ("labels `%1` are referenced via `goto` but are not defined\n", names);
239 }
240
241 // Dump the last state's onenter and mainloop
242 write_member_buffers();
243
244 // String table
245 write_string_table();
246 }
247
248 // ============================================================================
249 //
250 void botscript_parser::parse_state_block()
251 {
252 check_toplevel();
253 m_lx->must_get_next (tk_string);
254 string statename = token_string();
255
256 // State name must be a word.
257 if (statename.first (" ") != -1)
258 error ("state name must be a single word, got `%1`", statename);
259
260 // stateSpawn is special - it *must* be defined. If we
261 // encountered it, then mark down that we have it.
262 if (-statename == "statespawn")
263 m_state_spawn_defined = true;
264
265 // Must end in a colon
266 m_lx->must_get_next (tk_colon);
267
268 // write the previous state's onenter and
269 // mainloop buffers to file now
270 if (m_current_state.is_empty() == false)
271 write_member_buffers();
272
273 buffer()->write_dword (dh_state_name);
274 buffer()->write_string (statename);
275 buffer()->write_dword (dh_state_index);
276 buffer()->write_dword (m_num_states);
277
278 m_num_states++;
279 m_current_state = statename;
280 m_got_main_loop = false;
281 }
282
283 // ============================================================================
284 //
285 void botscript_parser::parse_event_block()
286 {
287 check_toplevel();
288 m_lx->must_get_next (tk_string);
289
290 event_info* e = find_event_by_name (token_string());
291
292 if (!e)
293 error ("bad event, got `%1`\n", token_string());
294
295 m_lx->must_get_next (tk_brace_start);
296 m_current_mode = e_event_mode;
297 buffer()->write_dword (dh_event);
298 buffer()->write_dword (e->number);
299 m_num_events++;
300 }
301
302 // ============================================================================
303 //
304 void botscript_parser::parse_mainloop()
305 {
306 check_toplevel();
307 m_lx->must_get_next (tk_brace_start);
308
309 m_current_mode = e_main_loop_mode;
310 m_main_loop_buffer->write_dword (dh_main_loop);
311 }
312
313 // ============================================================================
314 //
315 void botscript_parser::parse_on_enter_exit()
316 {
317 check_toplevel();
318 bool onenter = (token_is (tk_onenter));
319 m_lx->must_get_next (tk_brace_start);
320
321 m_current_mode = onenter ? e_onenter_mode : e_onexit_mode;
322 buffer()->write_dword (onenter ? dh_on_enter : dh_on_exit);
323 }
324
325 // ============================================================================
326 //
327 void botscript_parser::parse_variable_declaration()
328 {
329 // For now, only globals are supported
330 if (m_current_mode != e_top_level_mode || m_current_state.is_empty() == false)
331 error ("variables must only be global for now");
332
333 type_e type = (token_is (tk_int)) ? e_int_type :
334 (token_is (tk_str)) ? e_string_type :
335 e_bool_type;
336
337 m_lx->must_get_next();
338 string varname = token_string();
339
340 // Var name must not be a number
341 if (varname.is_numeric())
342 error ("variable name must not be a number");
343
344 script_variable* var = declare_global_variable (type, varname);
345 (void) var;
346 m_lx->must_get_next (tk_semicolon);
347 }
348
349 // ============================================================================
350 //
351 void botscript_parser::parse_goto()
352 {
353 check_not_toplevel();
354
355 // Get the name of the label
356 m_lx->must_get_next();
357
358 // Find the mark this goto statement points to
359 string target = token_string();
360 byte_mark* mark = buffer()->find_mark_by_name (target);
361
362 // If not set, define it
363 if (!mark)
364 {
365 undefined_label undf;
366 undf.name = target;
367 undf.target = buffer()->add_mark (target);
368 m_undefined_labels << undf;
369 }
370
371 // Add a reference to the mark.
372 buffer()->write_dword (dh_goto);
373 buffer()->add_reference (mark);
374 m_lx->must_get_next (tk_semicolon);
375 }
376
377 // ============================================================================
378 //
379 void botscript_parser::parse_if()
380 {
381 check_not_toplevel();
382 push_scope();
383
384 // Condition
385 m_lx->must_get_next (tk_paren_start);
386
387 // Read the expression and write it.
388 m_lx->must_get_next();
389 data_buffer* c = parse_expression (e_int_type);
390 buffer()->merge_and_destroy (c);
391
392 m_lx->must_get_next (tk_paren_end);
393 m_lx->must_get_next (tk_brace_start);
394
395 // Add a mark - to here temporarily - and add a reference to it.
396 // Upon a closing brace, the mark will be adjusted.
397 byte_mark* mark = buffer()->add_mark ("");
398
399 // Use dh_if_not_goto - if the expression is not true, we goto the mark
400 // we just defined - and this mark will be at the end of the scope block.
401 buffer()->write_dword (dh_if_not_goto);
402 buffer()->add_reference (mark);
403
404 // Store it
405 SCOPE (0).mark1 = mark;
406 SCOPE (0).type = e_if_scope;
407 }
408
409 // ============================================================================
410 //
411 void botscript_parser::parse_else()
412 {
413 check_not_toplevel();
414 m_lx->must_get_next (tk_brace_start);
415
416 // Don't use PushScope as it resets the scope
417 m_scope_cursor++;
418
419 if (m_scope_cursor >= MAX_SCOPE)
420 error ("too deep scope");
421
422 if (SCOPE (0).type != e_if_scope)
423 error ("else without preceding if");
424
425 // write down to jump to the end of the else statement
426 // Otherwise we have fall-throughs
427 SCOPE (0).mark2 = buffer()->add_mark ("");
428
429 // Instruction to jump to the end after if block is complete
430 buffer()->write_dword (dh_goto);
431 buffer()->add_reference (SCOPE (0).mark2);
432
433 // Move the ifnot mark here and set type to else
434 buffer()->adjust_mark (SCOPE (0).mark1);
435 SCOPE (0).type = e_else_scope;
436 }
437
438 // ============================================================================
439 //
440 void botscript_parser::parse_while_block()
441 {
442 check_not_toplevel();
443 push_scope();
444
445 // While loops need two marks - one at the start of the loop and one at the
446 // end. The condition is checked at the very start of the loop, if it fails,
447 // we use goto to skip to the end of the loop. At the end, we loop back to
448 // the beginning with a go-to statement.
449 byte_mark* mark1 = buffer()->add_mark (""); // start
450 byte_mark* mark2 = buffer()->add_mark (""); // end
451
452 // Condition
453 m_lx->must_get_next (tk_paren_start);
454 m_lx->must_get_next();
455 data_buffer* expr = parse_expression (e_int_type);
456 m_lx->must_get_next (tk_paren_end);
457 m_lx->must_get_next (tk_brace_start);
458
459 // write condition
460 buffer()->merge_and_destroy (expr);
461
462 // Instruction to go to the end if it fails
463 buffer()->write_dword (dh_if_not_goto);
464 buffer()->add_reference (mark2);
465
466 // Store the needed stuff
467 SCOPE (0).mark1 = mark1;
468 SCOPE (0).mark2 = mark2;
469 SCOPE (0).type = e_while_scope;
470 }
471
472 // ============================================================================
473 //
474 void botscript_parser::parse_for_block()
475 {
476 check_not_toplevel();
477 push_scope();
478
479 // Initializer
480 m_lx->must_get_next (tk_paren_start);
481 m_lx->must_get_next();
482 data_buffer* init = parse_statement();
483
484 if (!init)
485 error ("bad statement for initializer of for");
486
487 m_lx->must_get_next (tk_semicolon);
488
489 // Condition
490 m_lx->must_get_next();
491 data_buffer* cond = parse_expression (e_int_type);
492
493 if (!cond)
494 error ("bad statement for condition of for");
495
496 m_lx->must_get_next (tk_semicolon);
497
498 // Incrementor
499 m_lx->must_get_next();
500 data_buffer* incr = parse_statement();
501
502 if (!incr)
503 error ("bad statement for incrementor of for");
504
505 m_lx->must_get_next (tk_paren_end);
506 m_lx->must_get_next (tk_brace_start);
507
508 // First, write out the initializer
509 buffer()->merge_and_destroy (init);
510
511 // Init two marks
512 byte_mark* mark1 = buffer()->add_mark ("");
513 byte_mark* mark2 = buffer()->add_mark ("");
514
515 // Add the condition
516 buffer()->merge_and_destroy (cond);
517 buffer()->write_dword (dh_if_not_goto);
518 buffer()->add_reference (mark2);
519
520 // Store the marks and incrementor
521 SCOPE (0).mark1 = mark1;
522 SCOPE (0).mark2 = mark2;
523 SCOPE (0).buffer1 = incr;
524 SCOPE (0).type = e_for_scope;
525 }
526
527 // ============================================================================
528 //
529 void botscript_parser::parse_do_block()
530 {
531 check_not_toplevel();
532 push_scope();
533 m_lx->must_get_next (tk_brace_start);
534 SCOPE (0).mark1 = buffer()->add_mark ("");
535 SCOPE (0).type = e_do_scope;
536 }
537
538 // ============================================================================
539 //
540 void botscript_parser::parse_switch_block()
541 {
542 // This gets a bit tricky. switch is structured in the
543 // bytecode followingly:
544 //
545 // (expression)
546 // case a: goto casemark1
547 // case b: goto casemark2
548 // case c: goto casemark3
549 // goto mark1 // jump to end if no matches
550 // casemark1: ...
551 // casemark2: ...
552 // casemark3: ...
553 // mark1: // end mark
554
555 check_not_toplevel();
556 push_scope();
557 m_lx->must_get_next (tk_paren_start);
558 m_lx->must_get_next();
559 buffer()->merge_and_destroy (parse_expression (e_int_type));
560 m_lx->must_get_next (tk_paren_end);
561 m_lx->must_get_next (tk_brace_start);
562 SCOPE (0).type = e_switch_scope;
563 SCOPE (0).mark1 = buffer()->add_mark (""); // end mark
564 SCOPE (0).buffer1 = null; // default header
565 }
566
567 // ============================================================================
568 //
569 void botscript_parser::parse_switch_case()
570 {
571 // case is only allowed inside switch
572 if (SCOPE (0).type != e_switch_scope)
573 error ("case label outside switch");
574
575 // Get the literal (Zandronum does not support expressions here)
576 m_lx->must_get_next (tk_number);
577 int num = m_lx->get_token()->text.to_long();
578 m_lx->must_get_next (tk_colon);
579
580 for (int i = 0; i < MAX_CASE; i++)
581 if (SCOPE (0).casenumbers[i] == num)
582 error ("multiple case %d labels in one switch", num);
583
584 // Write down the expression and case-go-to. This builds
585 // the case tree. The closing event will write the actual
586 // blocks and move the marks appropriately.
587 //
588 // AddSwitchCase will add the reference to the mark
589 // for the case block that this heralds, and takes care
590 // of buffering setup and stuff like that.
591 //
592 // We null the switch buffer for the case-go-to statement as
593 // we want it all under the switch, not into the case-buffers.
594 m_switch_buffer = null;
595 buffer()->write_dword (dh_case_goto);
596 buffer()->write_dword (num);
597 add_switch_case (null);
598 SCOPE (0).casenumbers[SCOPE (0).casecursor] = num;
599 }
600
601 // ============================================================================
602 //
603 void botscript_parser::parse_switch_default()
604 {
605 if (SCOPE (0).type != e_switch_scope)
606 error ("default label outside switch");
607
608 if (SCOPE (0).buffer1)
609 error ("multiple default labels in one switch");
610
611 m_lx->must_get_next (tk_colon);
612
613 // The default header is buffered into buffer1, since
614 // it has to be the last of the case headers
615 //
616 // Since the expression is pushed into the switch
617 // and is only popped when case succeeds, we have
618 // to pop it with dh_drop manually if we end up in
619 // a default.
620 data_buffer* b = new data_buffer;
621 SCOPE (0).buffer1 = b;
622 b->write_dword (dh_drop);
623 b->write_dword (dh_goto);
624 add_switch_case (b);
625 }
626
627 // ============================================================================
628 //
629 void botscript_parser::parse_break()
630 {
631 if (!m_scope_cursor)
632 error ("unexpected `break`");
633
634 buffer()->write_dword (dh_goto);
635
636 // switch and if use mark1 for the closing point,
637 // for and while use mark2.
638 switch (SCOPE (0).type)
639 {
640 case e_if_scope:
641 case e_switch_scope:
642 {
643 buffer()->add_reference (SCOPE (0).mark1);
644 } break;
645
646 case e_for_scope:
647 case e_while_scope:
648 {
649 buffer()->add_reference (SCOPE (0).mark2);
650 } break;
651
652 default:
653 {
654 error ("unexpected `break`");
655 } break;
656 }
657
658 m_lx->must_get_next (tk_semicolon);
659 }
660
661 // ============================================================================
662 //
663 void botscript_parser::parse_continue()
664 {
665 m_lx->must_get_next (tk_semicolon);
666
667 int curs;
668 bool found = false;
669
670 // Fall through the scope until we find a loop block
671 for (curs = m_scope_cursor; curs > 0 && !found; curs--)
672 {
673 switch (m_scope_stack[curs].type)
674 {
675 case e_for_scope:
676 case e_while_scope:
677 case e_do_scope:
678 {
679 buffer()->write_dword (dh_goto);
680 buffer()->add_reference (m_scope_stack[curs].mark1);
681 found = true;
682 } break;
683
684 default:
685 break;
686 }
687 }
688
689 // No loop blocks
690 if (!found)
691 error ("`continue`-statement not inside a loop");
692 }
693
694 // ============================================================================
695 //
696 void botscript_parser::parse_block_end()
697 {
698 // Closing brace
699 // If we're in the block stack, we're descending down from it now
700 if (m_scope_cursor > 0)
701 {
702 switch (SCOPE (0).type)
703 {
704 case e_if_scope:
705 // Adjust the closing mark.
706 buffer()->adjust_mark (SCOPE (0).mark1);
707
708 // We're returning from if, thus else can be next
709 m_can_else = true;
710 break;
711
712 case e_else_scope:
713 // else instead uses mark1 for itself (so if expression
714 // fails, jump to else), mark2 means end of else
715 buffer()->adjust_mark (SCOPE (0).mark2);
716 break;
717
718 case e_for_scope:
719 // write the incrementor at the end of the loop block
720 buffer()->merge_and_destroy (SCOPE (0).buffer1);
721 case e_while_scope:
722 // write down the instruction to go back to the start of the loop
723 buffer()->write_dword (dh_goto);
724 buffer()->add_reference (SCOPE (0).mark1);
725
726 // Move the closing mark here since we're at the end of the while loop
727 buffer()->adjust_mark (SCOPE (0).mark2);
728 break;
729
730 case e_do_scope:
731 {
732 m_lx->must_get_next (tk_while);
733 m_lx->must_get_next (tk_paren_start);
734 m_lx->must_get_next();
735 data_buffer* expr = parse_expression (e_int_type);
736 m_lx->must_get_next (tk_paren_end);
737 m_lx->must_get_next (tk_semicolon);
738
739 // If the condition runs true, go back to the start.
740 buffer()->merge_and_destroy (expr);
741 buffer()->write_dword (dh_if_goto);
742 buffer()->add_reference (SCOPE (0).mark1);
743 break;
744 }
745
746 case e_switch_scope:
747 {
748 // Switch closes. Move down to the record buffer of
749 // the lower block.
750 if (SCOPE (1).casecursor != -1)
751 m_switch_buffer = SCOPE (1).casebuffers[SCOPE (1).casecursor];
752 else
753 m_switch_buffer = null;
754
755 // If there was a default in the switch, write its header down now.
756 // If not, write instruction to jump to the end of switch after
757 // the headers (thus won't fall-through if no case matched)
758 if (SCOPE (0).buffer1)
759 buffer()->merge_and_destroy (SCOPE (0).buffer1);
760 else
761 {
762 buffer()->write_dword (dh_drop);
763 buffer()->write_dword (dh_goto);
764 buffer()->add_reference (SCOPE (0).mark1);
765 }
766
767 // Go through all of the buffers we
768 // recorded down and write them.
769 for (int u = 0; u < MAX_CASE; u++)
770 {
771 if (!SCOPE (0).casebuffers[u])
772 continue;
773
774 buffer()->adjust_mark (SCOPE (0).casemarks[u]);
775 buffer()->merge_and_destroy (SCOPE (0).casebuffers[u]);
776 }
777
778 // Move the closing mark here
779 buffer()->adjust_mark (SCOPE (0).mark1);
780 break;
781 }
782
783 case e_unknown_scope:
784 break;
785 }
786
787 // Descend down the stack
788 m_scope_cursor--;
789 return;
790 }
791
792 int dataheader = (m_current_mode == e_event_mode) ? dh_end_event :
793 (m_current_mode == e_main_loop_mode) ? dh_end_main_loop :
794 (m_current_mode == e_onenter_mode) ? dh_end_on_enter :
795 (m_current_mode == e_onexit_mode) ? dh_end_on_exit : -1;
796
797 if (dataheader == -1)
798 error ("unexpected `}`");
799
800 // Data header must be written before mode is changed because
801 // onenter and mainloop go into special buffers, and we want
802 // the closing data headers into said buffers too.
803 buffer()->write_dword (dataheader);
804 m_current_mode = e_top_level_mode;
805 m_lx->get_next (tk_semicolon);
806 }
807
808 // ============================================================================
809 //
810 void botscript_parser::parse_const()
811 {
812 constant_info info;
813
814 // Get the type
815 m_lx->must_get_next();
816 string typestring = token_string();
817 info.type = get_type_by_name (typestring);
818
819 if (info.type == e_unknown_type || info.type == e_void_type)
820 error ("unknown type `%1` for constant", typestring);
821
822 m_lx->must_get_next();
823 info.name = token_string();
824
825 m_lx->must_get_next (tk_assign);
826
827 switch (info.type)
828 {
829 case e_bool_type:
830 case e_int_type:
831 {
832 m_lx->must_get_next (tk_number);
833 } break;
834
835 case e_string_type:
836 {
837 m_lx->must_get_next (tk_string);
838 } break;
839
840 case e_unknown_type:
841 case e_void_type:
842 break;
843 }
844
845 info.val = m_lx->get_token()->text;
846 m_constants << info;
847
848 m_lx->must_get_next (tk_semicolon);
849 }
850
851 // ============================================================================
852 //
853 void botscript_parser::parse_label()
854 {
855 check_not_toplevel();
856 string label_name = token_string();
857 byte_mark* mark = null;
858
859 // want no conflicts..
860 if (find_command_by_name (label_name))
861 error ("label name `%1` conflicts with command name\n", label_name);
862
863 if (find_global_variable (label_name))
864 error ("label name `%1` conflicts with variable\n", label_name);
865
866 // See if a mark already exists for this label
867 for (undefined_label& undf : m_undefined_labels)
868 {
869 if (undf.name != label_name)
870 continue;
871
872 mark = undf.target;
873 buffer()->adjust_mark (mark);
874
875 // No longer undefined
876 m_undefined_labels.remove (undf);
877 break;
878 }
879
880 // Not found in unmarked lists, define it now
881 if (mark == null)
882 buffer()->add_mark (label_name);
883
884 m_lx->must_get_next (tk_colon);
885 }
886
887 // =============================================================================
888 //
889 void botscript_parser::parse_eventdef()
890 {
891 event_info* e = new event_info;
892
893 m_lx->must_get_next (tk_number);
894 e->number = token_string().to_long();
895 m_lx->must_get_next (tk_colon);
896 m_lx->must_get_next (tk_symbol);
897 e->name = m_lx->get_token()->text;
898 m_lx->must_get_next (tk_paren_start);
899 m_lx->must_get_next (tk_paren_end);
900 m_lx->must_get_next (tk_semicolon);
901 add_event (e);
902 }
903
904 // =============================================================================
905 //
906 void botscript_parser::parse_funcdef()
907 {
908 command_info* comm = new command_info;
909
910 // Number
911 m_lx->must_get_next (tk_number);
912 comm->number = m_lx->get_token()->text.to_long();
913
914 m_lx->must_get_next (tk_colon);
915
916 // Name
917 m_lx->must_get_next (tk_symbol);
918 comm->name = m_lx->get_token()->text;
919
920 m_lx->must_get_next (tk_colon);
921
922 // Return value
923 m_lx->must_get_any_of ({tk_int, tk_void, tk_bool, tk_str});
924 comm->returnvalue = get_type_by_name (m_lx->get_token()->text); // TODO
925 assert (comm->returnvalue != -1);
926
927 m_lx->must_get_next (tk_colon);
928
929 // Num args
930 m_lx->must_get_next (tk_number);
931 comm->numargs = m_lx->get_token()->text.to_long();
932
933 m_lx->must_get_next (tk_colon);
934
935 // Max args
936 m_lx->must_get_next (tk_number);
937 comm->maxargs = m_lx->get_token()->text.to_long();
938
939 // Argument types
940 int curarg = 0;
941
942 while (curarg < comm->maxargs)
943 {
944 command_argument arg;
945 m_lx->must_get_next (tk_colon);
946 m_lx->must_get_any_of ({tk_int, tk_bool, tk_str});
947 type_e type = get_type_by_name (m_lx->get_token()->text);
948 assert (type != -1 && type != e_void_type);
949 arg.type = type;
950
951 m_lx->must_get_next (tk_paren_start);
952 m_lx->must_get_next (tk_symbol);
953 arg.name = m_lx->get_token()->text;
954
955 // If this is an optional parameter, we need the default value.
956 if (curarg >= comm->numargs)
957 {
958 m_lx->must_get_next (tk_assign);
959
960 switch (type)
961 {
962 case e_int_type:
963 case e_bool_type:
964 m_lx->must_get_next (tk_number);
965 break;
966
967 case e_string_type:
968 m_lx->must_get_next (tk_string);
969 break;
970
971 case e_unknown_type:
972 case e_void_type:
973 break;
974 }
975
976 arg.defvalue = m_lx->get_token()->text.to_long();
977 }
978
979 m_lx->must_get_next (tk_paren_end);
980 comm->args << arg;
981 curarg++;
982 }
983
984 m_lx->must_get_next (tk_semicolon);
985 add_command_definition (comm);
986 }
987
988 // ============================================================================
989 // Parses a command call
990 data_buffer* botscript_parser::parse_command (command_info* comm)
991 {
992 data_buffer* r = new data_buffer (64);
993
994 if (m_current_mode == e_top_level_mode)
995 error ("command call at top level");
996
997 m_lx->must_get_next (tk_paren_start);
998 m_lx->must_get_next();
999
1000 int curarg = 0;
1001
1002 while (1)
1003 {
1004 if (token_is (tk_paren_end))
1005 {
1006 if (curarg < comm->numargs)
1007 error ("too few arguments passed to %1\n\tprototype: %2",
1008 comm->name, get_command_signature (comm));
1009
1010 break;
1011 curarg++;
1012 }
1013
1014 if (curarg >= comm->maxargs)
1015 error ("too many arguments passed to %1\n\tprototype: %2",
1016 comm->name, get_command_signature (comm));
1017
1018 r->merge_and_destroy (parse_expression (comm->args[curarg].type));
1019 m_lx->must_get_next();
1020
1021 if (curarg < comm->numargs - 1)
1022 {
1023 m_lx->must_be (tk_comma);
1024 m_lx->must_get_next();
1025 }
1026 else if (curarg < comm->maxargs - 1)
1027 {
1028 // Can continue, but can terminate as well.
1029 if (token_is (tk_paren_end))
1030 {
1031 curarg++;
1032 break;
1033 }
1034 else
1035 {
1036 m_lx->must_be (tk_comma);
1037 m_lx->must_get_next();
1038 }
1039 }
1040
1041 curarg++;
1042 }
1043
1044 // If the script skipped any optional arguments, fill in defaults.
1045 while (curarg < comm->maxargs)
1046 {
1047 r->write_dword (dh_push_number);
1048 r->write_dword (comm->args[curarg].defvalue);
1049 curarg++;
1050 }
1051
1052 r->write_dword (dh_command);
1053 r->write_dword (comm->number);
1054 r->write_dword (comm->maxargs);
1055
1056 return r;
1057 }
1058
1059 // ============================================================================
1060 // Is the given operator an assignment operator?
1061 //
1062 static bool is_assignment_operator (int oper)
1063 {
1064 switch (oper)
1065 {
1066 case OPER_ASSIGNADD:
1067 case OPER_ASSIGNSUB:
1068 case OPER_ASSIGNMUL:
1069 case OPER_ASSIGNDIV:
1070 case OPER_ASSIGNMOD:
1071 case OPER_ASSIGNLEFTSHIFT:
1072 case OPER_ASSIGNRIGHTSHIFT:
1073 case OPER_ASSIGN:
1074 return true;
1075 }
1076
1077 return false;
1078 }
1079
1080 // ============================================================================
1081 // Finds an operator's corresponding dataheader
1082 //
1083 static word get_data_header_by_operator (script_variable* var, int oper)
1084 {
1085 if (is_assignment_operator (oper))
1086 {
1087 if (!var)
1088 error ("operator %d requires left operand to be a variable\n", oper);
1089
1090 // TODO: At the moment, vars only are global
1091 // OPER_ASSIGNLEFTSHIFT and OPER_ASSIGNRIGHTSHIFT do not
1092 // have data headers, instead they are expanded out in
1093 // the operator parser
1094 switch (oper)
1095 {
1096 case OPER_ASSIGNADD: return dh_add_global_var;
1097 case OPER_ASSIGNSUB: return dh_subtract_global_var;
1098 case OPER_ASSIGNMUL: return dh_multiply_global_var;
1099 case OPER_ASSIGNDIV: return dh_divide_global_var;
1100 case OPER_ASSIGNMOD: return dh_mod_global_var;
1101 case OPER_ASSIGN: return dh_assign_global_var;
1102
1103 default: error ("bad assignment operator!!\n");
1104 }
1105 }
1106
1107 switch (oper)
1108 {
1109 case OPER_ADD: return dh_add;
1110 case OPER_SUBTRACT: return dh_subtract;
1111 case OPER_MULTIPLY: return dh_multiply;
1112 case OPER_DIVIDE: return dh_divide;
1113 case OPER_MODULUS: return dh_modulus;
1114 case OPER_EQUALS: return dh_equals;
1115 case OPER_NOTEQUALS: return dh_not_equals;
1116 case OPER_LESSTHAN: return dh_less_than;
1117 case OPER_GREATERTHAN: return dh_greater_than;
1118 case OPER_LESSTHANEQUALS: return dh_at_most;
1119 case OPER_GREATERTHANEQUALS: return dh_at_least;
1120 case OPER_LEFTSHIFT: return dh_left_shift;
1121 case OPER_RIGHTSHIFT: return dh_right_shift;
1122 case OPER_OR: return dh_or_logical;
1123 case OPER_AND: return dh_and_logical;
1124 case OPER_BITWISEOR: return dh_or_bitwise;
1125 case OPER_BITWISEEOR: return dh_eor_bitwise;
1126 case OPER_BITWISEAND: return dh_and_bitwise;
1127 }
1128
1129 error ("DataHeaderByOperator: couldn't find dataheader for operator %d!\n", oper);
1130 return 0;
1131 }
1132
1133 // ============================================================================
1134 // Parses an expression, potentially recursively
1135 //
1136 data_buffer* botscript_parser::parse_expression (type_e reqtype)
1137 {
1138 data_buffer* retbuf = new data_buffer (64);
1139
1140 // Parse first operand
1141 retbuf->merge_and_destroy (parse_expr_value (reqtype));
1142
1143 // Parse any and all operators we get
1144 int oper;
1145
1146 while ( (oper = parse_operator (true)) != -1)
1147 {
1148 // We peeked the operator, move forward now
1149 m_lx->skip();
1150
1151 // Can't be an assignement operator, those belong in assignments.
1152 if (is_assignment_operator (oper))
1153 error ("assignment operator inside expression");
1154
1155 // Parse the right operand.
1156 m_lx->must_get_next();
1157 data_buffer* rb = parse_expr_value (reqtype);
1158
1159 if (oper == OPER_TERNARY)
1160 {
1161 // Ternary operator requires - naturally - a third operand.
1162 m_lx->must_get_next (tk_colon);
1163 m_lx->must_get_next();
1164 data_buffer* tb = parse_expr_value (reqtype);
1165
1166 // It also is handled differently: there isn't a dataheader for ternary
1167 // operator. Instead, we abuse PUSHNUMBER and IFNOTGOTO for this.
1168 // Behold, big block of writing madness! :P
1169 byte_mark* mark1 = retbuf->add_mark (""); // start of "else" case
1170 byte_mark* mark2 = retbuf->add_mark (""); // end of expression
1171 retbuf->write_dword (dh_if_not_goto); // if the first operand (condition)
1172 retbuf->add_reference (mark1); // didn't eval true, jump into mark1
1173 retbuf->merge_and_destroy (rb); // otherwise, perform second operand (true case)
1174 retbuf->write_dword (dh_goto); // afterwards, jump to the end, which is
1175 retbuf->add_reference (mark2); // marked by mark2.
1176 retbuf->adjust_mark (mark1); // move mark1 at the end of the true case
1177 retbuf->merge_and_destroy (tb); // perform third operand (false case)
1178 retbuf->adjust_mark (mark2); // move the ending mark2 here
1179 }
1180 else
1181 {
1182 // write to buffer
1183 retbuf->merge_and_destroy (rb);
1184 retbuf->write_dword (get_data_header_by_operator (null, oper));
1185 }
1186 }
1187
1188 return retbuf;
1189 }
1190
1191 // ============================================================================
1192 // Parses an operator string. Returns the operator number code.
1193 //
1194 #define ISNEXT(C) (m_lx->peek_next_string (peek ? 1 : 0) == C)
1195
1196 int botscript_parser::parse_operator (bool peek)
1197 {
1198 string oper;
1199
1200 if (peek)
1201 oper += m_lx->peek_next_string();
1202 else
1203 oper += token_string();
1204
1205 if (-oper == "strlen")
1206 return OPER_STRLEN;
1207
1208 // Check one-char operators
1209 bool equalsnext = ISNEXT ("=");
1210
1211 int o = (oper == "=" && !equalsnext) ? OPER_ASSIGN :
1212 (oper == ">" && !equalsnext && !ISNEXT (">")) ? OPER_GREATERTHAN :
1213 (oper == "<" && !equalsnext && !ISNEXT ("<")) ? OPER_LESSTHAN :
1214 (oper == "&" && !ISNEXT ("&")) ? OPER_BITWISEAND :
1215 (oper == "|" && !ISNEXT ("|")) ? OPER_BITWISEOR :
1216 (oper == "+" && !equalsnext) ? OPER_ADD :
1217 (oper == "-" && !equalsnext) ? OPER_SUBTRACT :
1218 (oper == "*" && !equalsnext) ? OPER_MULTIPLY :
1219 (oper == "/" && !equalsnext) ? OPER_DIVIDE :
1220 (oper == "%" && !equalsnext) ? OPER_MODULUS :
1221 (oper == "^") ? OPER_BITWISEEOR :
1222 (oper == "?") ? OPER_TERNARY :
1223 -1;
1224
1225 if (o != -1)
1226 {
1227 return o;
1228 }
1229
1230 // Two-char operators
1231 oper += m_lx->peek_next_string (peek ? 1 : 0);
1232 equalsnext = m_lx->peek_next_string (peek ? 2 : 1) == ("=");
1233
1234 o = (oper == "+=") ? OPER_ASSIGNADD :
1235 (oper == "-=") ? OPER_ASSIGNSUB :
1236 (oper == "*=") ? OPER_ASSIGNMUL :
1237 (oper == "/=") ? OPER_ASSIGNDIV :
1238 (oper == "%=") ? OPER_ASSIGNMOD :
1239 (oper == "==") ? OPER_EQUALS :
1240 (oper == "!=") ? OPER_NOTEQUALS :
1241 (oper == ">=") ? OPER_GREATERTHANEQUALS :
1242 (oper == "<=") ? OPER_LESSTHANEQUALS :
1243 (oper == "&&") ? OPER_AND :
1244 (oper == "||") ? OPER_OR :
1245 (oper == "<<" && !equalsnext) ? OPER_LEFTSHIFT :
1246 (oper == ">>" && !equalsnext) ? OPER_RIGHTSHIFT :
1247 -1;
1248
1249 if (o != -1)
1250 {
1251 m_lx->must_get_next();
1252 return o;
1253 }
1254
1255 // Three-char opers
1256 oper += m_lx->peek_next_string (peek ? 2 : 1);
1257 o = oper == "<<=" ? OPER_ASSIGNLEFTSHIFT :
1258 oper == ">>=" ? OPER_ASSIGNRIGHTSHIFT :
1259 -1;
1260
1261 if (o != -1)
1262 {
1263 m_lx->must_get_next();
1264 m_lx->must_get_next();
1265 }
1266
1267 return o;
1268 }
1269
1270 // ============================================================================
1271 //
1272 string botscript_parser::parse_float()
1273 {
1274 m_lx->must_be (tk_number);
1275 string floatstring = token_string();
1276 lexer::token tok;
1277
1278 // Go after the decimal point
1279 if (m_lx->peek_next (&tok) && tok.type == tk_dot)
1280 {
1281 m_lx->skip();
1282 m_lx->must_get_next (tk_number);
1283 floatstring += ".";
1284 floatstring += token_string();
1285 }
1286
1287 return floatstring;
1288 }
1289
1290 // ============================================================================
1291 // Parses a value in the expression and returns the data needed to push
1292 // it, contained in a data buffer. A value can be either a variable, a command,
1293 // a literal or an expression.
1294 //
1295 data_buffer* botscript_parser::parse_expr_value (type_e reqtype)
1296 {
1297 data_buffer* b = new data_buffer (16);
1298 script_variable* g;
1299
1300 // Prefixing "!" means negation.
1301 bool negate = token_is (tk_exclamation_mark);
1302
1303 if (negate) // Jump past the "!"
1304 m_lx->skip();
1305
1306 // Handle strlen
1307 /* if (token_string() == "strlen")
1308 {
1309 m_lx->must_get_next (tk_paren_start);
1310 m_lx->must_get_next();
1311
1312 // By this token we should get a string constant.
1313 constant_info* constant = find_constant (token_string());
1314
1315 if (!constant || constant->type != TYPE_STRING)
1316 error ("strlen only works with const str");
1317
1318 if (reqtype != TYPE_INT)
1319 error ("strlen returns int but %1 is expected\n", GetTypeName (reqtype));
1320
1321 b->write_dword (dh_push_number);
1322 b->write_dword (constant->val.len());
1323
1324 m_lx->must_get_next (tk_paren_end);
1325 }
1326 else */
1327 if (token_is (tk_paren_start))
1328 {
1329 // Expression
1330 m_lx->must_get_next();
1331 data_buffer* c = parse_expression (reqtype);
1332 b->merge_and_destroy (c);
1333 m_lx->must_get_next (tk_paren_end);
1334 }
1335 else if (command_info* comm = find_command_by_name (token_string()))
1336 {
1337 delete b;
1338
1339 // Command
1340 if (reqtype && comm->returnvalue != reqtype)
1341 error ("%1 returns an incompatible data type", comm->name);
1342
1343 b = parse_command (comm);
1344 }
1345 else if (constant_info* constant = find_constant (token_string()))
1346 {
1347 // Type check
1348 if (reqtype != constant->type)
1349 error ("constant `%1` is %2, expression requires %3\n",
1350 constant->name, get_type_name (constant->type),
1351 get_type_name (reqtype));
1352
1353 switch (constant->type)
1354 {
1355 case e_bool_type:
1356 case e_int_type:
1357 b->write_dword (dh_push_number);
1358 b->write_dword (atoi (constant->val));
1359 break;
1360
1361 case e_string_type:
1362 b->write_string_index (constant->val);
1363 break;
1364
1365 case e_void_type:
1366 case e_unknown_type:
1367 break;
1368 }
1369 }
1370 else if ((g = find_global_variable (token_string())))
1371 {
1372 // Global variable
1373 b->write_dword (dh_push_global_var);
1374 b->write_dword (g->index);
1375 }
1376 else
1377 {
1378 // If nothing else, check for literal
1379 switch (reqtype)
1380 {
1381 case e_void_type:
1382 case e_unknown_type:
1383 error ("unknown identifier `%1` (expected keyword, function or variable)", token_string());
1384 break;
1385
1386 case e_bool_type:
1387 case e_int_type:
1388 {
1389 m_lx->must_be (tk_number);
1390
1391 // All values are written unsigned - thus we need to write the value's
1392 // absolute value, followed by an unary minus for negatives.
1393 b->write_dword (dh_push_number);
1394
1395 long v = token_string().to_long();
1396 b->write_dword (static_cast<word> (abs (v)));
1397
1398 if (v < 0)
1399 b->write_dword (dh_unary_minus);
1400
1401 break;
1402 }
1403
1404 case e_string_type:
1405 // PushToStringTable either returns the string index of the
1406 // string if it finds it in the table, or writes it to the
1407 // table and returns it index if it doesn't find it there.
1408 m_lx->must_be (tk_string);
1409 b->write_string_index (token_string());
1410 break;
1411 }
1412 }
1413
1414 // Negate it now if desired
1415 if (negate)
1416 b->write_dword (dh_negate_logical);
1417
1418 return b;
1419 }
1420
1421 // ============================================================================
1422 // Parses an assignment. An assignment starts with a variable name, followed
1423 // by an assignment operator, followed by an expression value. Expects current
1424 // token to be the name of the variable, and expects the variable to be given.
1425 //
1426 data_buffer* botscript_parser::parse_assignment (script_variable* var)
1427 {
1428 // Get an operator
1429 m_lx->must_get_next();
1430 int oper = parse_operator();
1431
1432 if (!is_assignment_operator (oper))
1433 error ("expected assignment operator");
1434
1435 if (m_current_mode == e_top_level_mode)
1436 error ("can't alter variables at top level");
1437
1438 // Parse the right operand
1439 m_lx->must_get_next();
1440 data_buffer* retbuf = new data_buffer;
1441 data_buffer* expr = parse_expression (var->type);
1442
1443 // <<= and >>= do not have data headers. Solution: expand them.
1444 // a <<= b -> a = a << b
1445 // a >>= b -> a = a >> b
1446 if (oper == OPER_ASSIGNLEFTSHIFT || oper == OPER_ASSIGNRIGHTSHIFT)
1447 {
1448 retbuf->write_dword (var->is_global() ? dh_push_global_var : dh_push_local_var);
1449 retbuf->write_dword (var->index);
1450 retbuf->merge_and_destroy (expr);
1451 retbuf->write_dword ((oper == OPER_ASSIGNLEFTSHIFT) ? dh_left_shift : dh_right_shift);
1452 retbuf->write_dword (var->is_global() ? dh_assign_global_var : dh_assign_local_var);
1453 retbuf->write_dword (var->index);
1454 }
1455 else
1456 {
1457 retbuf->merge_and_destroy (expr);
1458 long dh = get_data_header_by_operator (var, oper);
1459 retbuf->write_dword (dh);
1460 retbuf->write_dword (var->index);
1461 }
1462
1463 return retbuf;
1464 }
1465
1466 // ============================================================================
1467 //
1468 void botscript_parser::push_scope()
1469 {
1470 m_scope_cursor++;
1471
1472 if (m_scope_cursor >= MAX_SCOPE)
1473 error ("too deep scope");
1474
1475 ScopeInfo* info = &SCOPE (0);
1476 info->type = e_unknown_scope;
1477 info->mark1 = null;
1478 info->mark2 = null;
1479 info->buffer1 = null;
1480 info->casecursor = -1;
1481
1482 for (int i = 0; i < MAX_CASE; i++)
1483 {
1484 info->casemarks[i] = null;
1485 info->casebuffers[i] = null;
1486 info->casenumbers[i] = -1;
1487 }
1488 }
1489
1490 // ============================================================================
1491 //
1492 data_buffer* botscript_parser::parse_statement()
1493 {
1494 if (find_constant (token_string())) // There should not be constants here.
1495 error ("invalid use for constant\n");
1496
1497 // If it's a variable, expect assignment.
1498 if (script_variable* var = find_global_variable (token_string()))
1499 return parse_assignment (var);
1500
1501 return null;
1502 }
1503
1504 // ============================================================================
1505 //
1506 void botscript_parser::add_switch_case (data_buffer* b)
1507 {
1508 ScopeInfo* info = &SCOPE (0);
1509
1510 info->casecursor++;
1511
1512 if (info->casecursor >= MAX_CASE)
1513 error ("too many cases in one switch");
1514
1515 // Init a mark for the case buffer
1516 byte_mark* casemark = buffer()->add_mark ("");
1517 info->casemarks[info->casecursor] = casemark;
1518
1519 // Add a reference to the mark. "case" and "default" both
1520 // add the necessary bytecode before the reference.
1521 if (b)
1522 b->add_reference (casemark);
1523 else
1524 buffer()->add_reference (casemark);
1525
1526 // Init a buffer for the case block and tell the object
1527 // writer to record all written data to it.
1528 info->casebuffers[info->casecursor] = m_switch_buffer = new data_buffer;
1529 }
1530
1531 // ============================================================================
1532 //
1533 constant_info* botscript_parser::find_constant (const string& tok)
1534 {
1535 for (int i = 0; i < m_constants.size(); i++)
1536 if (m_constants[i].name == tok)
1537 return &m_constants[i];
1538
1539 return null;
1540 }
1541
1542 // ============================================================================
1543 //
1544 bool botscript_parser::token_is (e_token a)
1545 {
1546 return (m_lx->get_token_type() == a);
1547 }
1548
1549 // ============================================================================
1550 //
1551 string botscript_parser::token_string()
1552 {
1553 return m_lx->get_token()->text;
1554 }
1555
1556 // ============================================================================
1557 //
1558 string botscript_parser::describe_position() const
1559 {
1560 lexer::token* tok = m_lx->get_token();
1561 return tok->file + ":" + string (tok->line) + ":" + string (tok->column);
1562 }
1563
1564 // ============================================================================
1565 //
1566 data_buffer* botscript_parser::buffer()
1567 {
1568 if (m_switch_buffer != null)
1569 return m_switch_buffer;
1570
1571 if (m_current_mode == e_main_loop_mode)
1572 return m_main_loop_buffer;
1573
1574 if (m_current_mode == e_onenter_mode)
1575 return m_on_enter_buffer;
1576
1577 return m_main_buffer;
1578 }
1579
1580 // ============================================================================
1581 //
1582 void botscript_parser::write_member_buffers()
1583 {
1584 // If there was no mainloop defined, write a dummy one now.
1585 if (!m_got_main_loop)
1586 {
1587 m_main_loop_buffer->write_dword (dh_main_loop);
1588 m_main_loop_buffer->write_dword (dh_end_main_loop);
1589 }
1590
1591 // Write the onenter and mainloop buffers, in that order in particular.
1592 for (data_buffer** bufp : list<data_buffer**> ({&m_on_enter_buffer, &m_main_loop_buffer}))
1593 {
1594 buffer()->merge_and_destroy (*bufp);
1595
1596 // Clear the buffer afterwards for potential next state
1597 *bufp = new data_buffer;
1598 }
1599
1600 // Next state definitely has no mainloop yet
1601 m_got_main_loop = false;
1602 }
1603
1604 // ============================================================================
1605 //
1606 // Write string table
1607 //
1608 void botscript_parser::write_string_table()
1609 {
1610 int stringcount = num_strings_in_table();
1611
1612 if (!stringcount)
1613 return;
1614
1615 // Write header
1616 m_main_buffer->write_dword (dh_string_list);
1617 m_main_buffer->write_dword (stringcount);
1618
1619 // Write all strings
1620 for (int i = 0; i < stringcount; i++)
1621 m_main_buffer->write_string (get_string_table()[i]);
1622 }
1623
1624 // ============================================================================
1625 //
1626 // Write the compiled bytecode to a file
1627 //
1628 void botscript_parser::write_to_file (string outfile)
1629 {
1630 FILE* fp = fopen (outfile, "w");
1631
1632 if (fp == null)
1633 error ("couldn't open %1 for writing: %2", outfile, strerror (errno));
1634
1635 // First, resolve references
1636 for (mark_reference* ref : m_main_buffer->get_refs())
1637 {
1638 // Substitute the placeholder with the mark position
1639 for (int v = 0; v < 4; v++)
1640 m_main_buffer->get_buffer()[ref->pos + v] = ((ref->target->pos) << (8 * v)) & 0xFF;
1641
1642 print ("reference at %1 resolved to mark at %2\n", ref->pos, ref->target->pos);
1643 }
1644
1645 // Then, dump the main buffer to the file
1646 fwrite (m_main_buffer->get_buffer(), 1, m_main_buffer->get_write_size(), fp);
1647 print ("-- %1 byte%s1 written to %2\n", m_main_buffer->get_write_size(), outfile);
1648 fclose (fp);
1649 }

mercurial