* move imageManager code into new imageManager.h/imageManager.cpp
[openc2e.git] / caosScript.cpp
blobb6650906c1d7dbb13062fca5a80d2f5c267bc04d
1 /*
2 * caosScript.cpp
3 * openc2e
5 * Created by Alyssa Milburn on Wed May 26 2004.
6 * Copyright (c) 2004 Alyssa Milburn. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
20 #include "bytecode.h"
21 #include "cmddata.h"
22 #include "exceptions.h"
23 #include "caosScript.h"
24 #include "caosVM.h"
25 #include "openc2e.h"
26 #include "Engine.h"
27 #include "World.h"
28 #include "token.h"
29 #include "dialect.h"
30 #include "lex.yy.h"
31 #undef yyFlexLexer // flex/C++ is horrrrible I should use the C interface instead probably
32 #include "lex.c2.h"
33 #include "lexutil.h"
34 #include <iostream>
35 #include <sstream>
36 #include <algorithm>
37 #include <cstring>
38 #include <boost/format.hpp>
39 #include <boost/scoped_ptr.hpp>
41 using std::string;
43 class unexpectedEOIexception { };
45 script::~script() {
48 // resolve relocations into fixed addresses
49 void script::link() {
50 ops.push_back(caosOp(CAOS_STOP, 0, -1));
51 assert(!linked);
52 // std::cout << "Pre-link:" << std::endl << dump();
53 // check relocations
54 for (unsigned int i = 1; i < relocations.size(); i++) {
55 // handle relocations-to-relocations
56 int p = relocations[i];
57 while (p < 0)
58 p = relocations[-p];
59 relocations[i] = p;
61 for (unsigned int i = 0; i < ops.size(); i++) {
62 if (op_is_relocatable(ops[i].opcode) && ops[i].argument < 0)
63 ops[i].argument = relocations[-ops[i].argument];
65 linked = true;
66 // std::cout << "Post-link:" << std::endl << dump();
67 relocations.clear();
70 script::script(const Dialect *v, const std::string &fn)
71 : fmly(-1), gnus(-1), spcs(-1), scrp(-1),
72 dialect(v), filename(fn)
74 // advance past reserved index 0
75 ops.push_back(caosOp(CAOS_NOP, 0, -1));
76 relocations.push_back(0);
77 linked = false;
80 script::script(const Dialect *v, const std::string &fn,
81 int fmly_, int gnus_, int spcs_, int scrp_)
82 : fmly(fmly_), gnus(gnus_), spcs(spcs_), scrp(scrp_),
83 dialect(v), filename(fn)
85 ops.push_back(caosOp(CAOS_NOP, 0, -1));
86 relocations.push_back(0);
87 linked = false;
90 std::string script::dump() {
91 std::ostringstream oss;
92 oss << "Relocations:" << std::endl;
93 for (unsigned int i = 1; i < relocations.size(); i++) {
94 oss << boost::format("%08d -> %08d") % i % relocations[i]
95 << std::endl;
97 oss << "Code:" << std::endl;
98 for (unsigned int i = 0; i < ops.size(); i++) {
99 oss << boost::format("%08d: ") % i;
100 oss << dumpOp(dialect, ops[i]);
101 oss << std::endl;
103 return oss.str();
106 caosScript::caosScript(const std::string &dialect, const std::string &fn) {
107 enumdepth = 0;
108 d = dialects[dialect].get();
109 if (!d)
110 throw parseException(std::string("Unknown dialect ") + dialect);
111 current = installer = shared_ptr<script> (new script(d, fn));
112 filename = fn;
115 caosScript::~caosScript() {
116 // Nothing to do, yay shared_ptr!
119 void caosScript::installScripts() {
120 std::vector<shared_ptr<script> >::iterator i = scripts.begin();
121 while (i != scripts.end()) {
122 shared_ptr<script> s = *i;
123 world.scriptorium.addScript(s->fmly, s->gnus, s->spcs, s->scrp, s);
124 i++;
128 void caosScript::installInstallScript(unsigned char family, unsigned char genus, unsigned short species, unsigned short eventid) {
129 assert((d->name == "c1") || (d->name == "c2"));
131 installer->fmly = family;
132 installer->gnus = genus;
133 installer->spcs = species;
134 installer->scrp = eventid;
136 world.scriptorium.addScript(installer->fmly, installer->gnus, installer->spcs, installer->scrp, installer);
140 saveVisit::saveVisit(caosScript *s)
141 : scr(s)
144 void saveVisit::operator()(const CAOSCmd &cmd) const {
145 scr->errindex = scr->traceindex = cmd.traceidx - 1;
146 if (cmd.op->rettype != CI_VARIABLE) {
147 throw parseException(std::string("RValue ") + cmd.op->fullname + " used where LValue expected");
149 scr->emitOp(CAOS_RESTORE_AUX, cmd.arguments.size());
150 scr->emitOp(CAOS_SAVE_CMD, scr->d->cmd_index(cmd.op));
153 evalVisit::evalVisit(caosScript *s, bool save_here_)
154 : scr(s), save_here(save_here_)
159 void evalVisit::operator()(const CAOSCmd &cmd) const {
160 for (size_t i = 0; i < cmd.arguments.size(); i++) {
161 bool save_there = (cmd.op->argtypes[i] == CI_VARIABLE);
162 cmd.arguments[i]->eval(scr, save_there);
164 scr->traceindex = cmd.traceidx - 1;
165 // If we're to be invoked to save our result later,
166 // stash our args for that time.
167 if (save_here) {
168 // Note: These indices refer to stack positions, with 0 being the top.
169 // We thus transfer the arguments in reverse order, as the order will again be
170 // reversed when the stack is restored.
171 for (size_t i = 0; i < cmd.arguments.size(); i++)
172 scr->emitOp(CAOS_PUSH_AUX, i);
174 scr->emitOp(CAOS_CMD, scr->d->cmd_index(cmd.op));
175 // If we emit variable-result arguments as well, we need to move our
176 // result down below them.
177 // This is theoretical at the moment - no expression-type commands also
178 // write back to their args.
180 if (cmd.op->rettype != CI_COMMAND) {
181 int rotcount = 0;
182 for (size_t i = 0; cmd.op->argtypes[i] != CI_END; i++) {
183 if (cmd.op->argtypes[i] == CI_VARIABLE)
184 rotcount++;
186 if (rotcount)
187 scr->emitOp(CAOS_STACK_ROT, rotcount);
189 for (int i = cmd.arguments.size() - 1; i >= 0; i--) {
190 if (cmd.op->argtypes[i] == CI_VARIABLE)
191 cmd.arguments[i]->save(scr);
195 void evalVisit::operator()(const caosVar &v) const {
196 if (v.hasInt()) {
197 int val = v.getInt();
198 if (val >= -(1 << 24) && val < (1 << 24)) {
199 scr->emitOp(CAOS_CONSTINT, val);
200 return;
203 scr->current->consts.push_back(v);
204 scr->emitOp(CAOS_CONST, scr->current->consts.size() - 1);
206 void evalVisit::operator()(const bytestring_t &bs) const {
207 scr->current->bytestrs.push_back(bs);
208 scr->emitOp(CAOS_BYTESTR, scr->current->bytestrs.size() - 1);
211 int costVisit::operator()(const CAOSCmd &cmd) const {
212 int accum = cmd.op->evalcost;
213 for (size_t i = 0; i < cmd.arguments.size(); i++)
214 accum += cmd.arguments[i]->cost();
215 return accum;
218 // parser states
219 enum {
220 ST_INSTALLER,
221 ST_BODY,
222 ST_REMOVAL,
223 ST_DOIF,
224 ST_ENUM,
225 ST_LOOP,
226 ST_REPS,
227 ST_INVALID
230 struct doifinfo {
231 int failreloc;
232 int donereloc;
235 struct repsinfo {
236 int jnzreloc;
237 int loopidx;
241 token *caosScript::tokenPeek() {
242 if ((size_t)curindex >= tokens->size())
243 return NULL;
244 return &(*tokens)[curindex];
247 token *caosScript::getToken(toktype expected) {
248 token *t = tokenPeek();
249 token dummy;
250 token &r = (t ? *t : dummy);
252 errindex = curindex;
254 if (expected != ANYTOKEN && r.type() != expected)
255 r.unexpected();
257 curindex++;
259 return t;
262 void caosScript::putBackToken(token *) {
263 curindex--;
264 errindex = curindex - 1; // curindex refers to the /next/ token to be parsed
265 // so make sure we refer to the token before it
268 void caosScript::parse(std::istream &in) {
269 assert(!tokens);
270 // run the token parser
272 extern int lex_lineno;
273 extern bool using_c2;
274 using_c2 = (d->name == "c1" || d->name == "c2");
275 lexreset();
276 boost::scoped_ptr<FlexLexer> l(
277 using_c2 ? (FlexLexer *)new c2FlexLexer()
278 : (FlexLexer *)new c2eFlexLexer()
280 l->yyrestart(&in);
282 tokens = shared_ptr<std::vector<token> >(new std::vector<token>());
283 while (l->yylex()) {
284 tokens->push_back(lasttok);
285 tokens->back().lineno = lex_lineno;
286 tokens->back().index = tokens->size() - 1;
288 tokens->push_back(token()); // tokens default to being EOI tokens
289 tokens->back().lineno = lex_lineno;
290 tokens->back().index = tokens->size() - 1;
292 curindex = errindex = traceindex = 0;
294 try {
295 parseloop(ST_INSTALLER, NULL);
297 std::ostringstream oss;
298 shared_ptr<std::vector<toktrace> > tokinfo(new std::vector<toktrace>());
299 for (size_t p = 0; p < tokens->size(); p++) {
300 std::string tok = (*tokens)[p].format();
301 int len = tok.size();
302 if (len > 65535) {
303 errindex = p;
304 throw parseException("Overlong token");
306 oss << tok << " ";
307 tokinfo->push_back(toktrace(len, (*tokens)[p].lineno));
309 shared_str code(oss.str());
310 installer->code = code;
311 installer->tokinfo = tokinfo;
312 installer->link();
313 if (removal) {
314 removal->link();
315 removal->tokinfo = tokinfo;
316 removal->code = code;
318 std::vector<shared_ptr<script> >::iterator i = scripts.begin();
319 while (i != scripts.end()) {
320 (*i)->tokinfo = tokinfo;
321 (*i)->code = code;
322 (*i++)->link();
324 } catch (parseException &e) {
325 e.filename = filename;
326 if (!tokens)
327 throw;
328 if (errindex < 0 || (size_t)errindex >= tokens->size())
329 throw;
330 e.lineno = (*tokens)[errindex].lineno;
331 e.context = boost::shared_ptr<std::vector<token> >(new std::vector<token>());
332 /* We'd like to capture N tokens on each side of the target, but
333 * if we can't get all those from one side, get it from the other.
335 int contextlen = 5;
336 int leftct = contextlen;
337 int rightct = contextlen;
339 if (errindex < leftct) {
340 rightct += leftct - errindex;
341 leftct = errindex;
343 if ((size_t)(errindex + rightct + 1) >= tokens->size()) {
344 int overflow = errindex + rightct + 1 - tokens->size();
345 rightct -= overflow;
346 while (overflow > 0 && errindex > leftct) {
347 overflow--;
348 leftct++;
351 assert(leftct >= 0 && rightct >= 0 && errindex >= leftct && (size_t)(errindex + rightct) < tokens->size());
353 if (errindex - leftct != 0) {
354 e.context->push_back(token());
355 e.context->back().payload = std::string("...");
358 for (int i = errindex - leftct; i < errindex + rightct; i++) {
359 e.context->push_back((*tokens)[i]);
361 if (errindex + rightct + 1 < (int)tokens->size()) {
362 e.context->push_back(token());
363 e.context->back().payload = std::string("...");
365 e.ctxoffset = leftct;
366 throw;
370 const cmdinfo *caosScript::readCommand(token *t, const std::string &prefix) {
371 std::string fullname = prefix + t->word();
372 errindex = t->index;
373 const cmdinfo *ci = d->find_command(fullname.c_str());
374 // See if there'{s a subcommand namespace
375 token *t2 = NULL;
376 try {
377 t2 = getToken(TOK_WORD);
378 if (!t2 || t2->type() != TOK_WORD)
379 throw parseException("dummy");
380 return readCommand(t2, fullname + " ");
381 } catch (parseException &e) {
382 if (ci->argtypes && ci->argtypes[0] == CI_SUBCOMMAND)
383 throw;
384 if (t2)
385 putBackToken(t2);
386 return ci;
390 void caosScript::emitOp(opcode_t op, int argument) {
391 if (op == CAOS_YIELD && engine.version < 3 && enumdepth > 0)
392 return;
393 current->ops.push_back(caosOp(op, argument, traceindex));
396 void caosScript::emitExpr(boost::shared_ptr<CAOSExpression> ce) {
397 ce->eval(this, false);
398 int cost = ce->cost();
399 if (cost)
400 emitOp(CAOS_YIELD, cost);
403 boost::shared_ptr<CAOSExpression> caosScript::readExpr(const enum ci_type xtype) {
404 token *t = getToken();
405 traceindex = errindex = curindex;
406 if (xtype == CI_BAREWORD) {
407 if (t->type() == TOK_WORD) {
408 return boost::shared_ptr<CAOSExpression>(new CAOSExpression(errindex, caosVar(t->word())));
409 } else if (t->type() == TOK_CONST) {
410 if (t->constval().getType() != CAOSSTR)
411 t->unexpected();
412 return boost::shared_ptr<CAOSExpression>(new CAOSExpression(errindex, t->constval()));
413 } else {
414 t->unexpected();
416 assert(!"UNREACHABLE");
417 return boost::shared_ptr<CAOSExpression>();
419 switch (t->type()) {
420 case TOK_CONST:
421 return boost::shared_ptr<CAOSExpression>(new CAOSExpression(errindex, t->constval()));
422 case TOK_BYTESTR:
423 return boost::shared_ptr<CAOSExpression>(new CAOSExpression(errindex, t->bytestr()));
424 case TOK_WORD: break; // fall through to remainder of function
425 default: t->unexpected();
428 std::string oldpayload = t->word();
429 if (t->word() == "face" && xtype != CI_COMMAND) {
430 // horrible hack, yay
431 if (xtype == CI_NUMERIC)
432 t->payload = std::string("face int");
433 else
434 t->payload = std::string("face string");
437 boost::shared_ptr<CAOSExpression> ce(new CAOSExpression(errindex, CAOSCmd()));
438 CAOSCmd *cmd = boost::get<CAOSCmd>(&ce->value);
440 if (t->word().size() == 4 && isdigit(t->word()[2]) && isdigit(t->word()[3])) {
441 if ( !strncmp(t->word().c_str(), "va", 2)
442 || !strncmp(t->word().c_str(), "ov", 2)
443 || !strncmp(t->word().c_str(), "mv", 2)) {
444 int idx = atoi(t->word().c_str() + 2);
445 t->payload = t->word().substr(0, 2) + "xx";
446 const cmdinfo *op = readCommand(t, std::string("expr "));
447 t->payload = oldpayload;
449 boost::shared_ptr<CAOSExpression> arg(new CAOSExpression(errindex, caosVar(idx)));
450 cmd->op = op;
451 cmd->arguments.push_back(arg);
452 return ce;
456 if (t->word().size() == 4 && isdigit(t->word()[3]) && engine.version < 3) {
457 // OBVx VARx hacks
458 if ( !strncmp(t->word().c_str(), "obv", 3)
459 || !strncmp(t->word().c_str(), "var", 3)) {
460 int idx = atoi(t->word().c_str() + 3);
461 t->payload = t->word().substr(0, 3) + "x";
462 const cmdinfo *op = readCommand(t, std::string("expr "));
463 t->payload = oldpayload;
465 boost::shared_ptr<CAOSExpression> arg(new CAOSExpression(errindex, caosVar(idx)));
466 cmd->op = op;
467 cmd->arguments.push_back(arg);
468 return ce;
472 const cmdinfo *ci = readCommand(t, std::string(xtype == CI_COMMAND ? "cmd " : "expr "));
473 t->payload = oldpayload;
474 cmd->op = ci;
475 for (int i = 0; ci->argtypes[i] != CI_END; i++) {
476 cmd->arguments.push_back(readExpr(ci->argtypes[i]));
478 return ce;
481 int caosScript::readCond() {
482 token *t = getToken(TOK_WORD);
483 typedef struct { const char *n; int cnd; } cond_entry;
484 const static cond_entry conds[] = {
485 { "eq", CEQ },
486 { "gt", CGT },
487 { "ge", CGE },
488 { "lt", CLT },
489 { "le", CLE },
490 { "ne", CNE },
491 { "bt", CBT },
492 { "bf", CBF },
493 { NULL, 0 }
496 const cond_entry *c = conds;
497 while (c->n != NULL) {
498 if (t->word() == c->n)
499 return c->cnd;
500 c++;
502 throw parseException(std::string("Unexpected non-condition word: ") + t->word());
505 void caosScript::parseCondition() {
506 emitOp(CAOS_CONSTINT, 1);
508 bool nextIsAnd = true;
509 while (1) {
510 boost::shared_ptr<CAOSExpression> a1, a2;
511 a1 = readExpr(CI_ANYVALUE);
512 int cond = readCond();
513 a2 = readExpr(CI_ANYVALUE);
514 emitExpr(a1);
515 emitExpr(a2);
516 emitOp(CAOS_COND, cond | (nextIsAnd ? CAND : COR));
518 token *peek = tokenPeek();
519 if (!peek) break;
520 if (peek->type() != TOK_WORD) break;
521 if (peek->word() == "and") {
522 getToken();
523 nextIsAnd = true;
524 } else if (peek->word() == "or") {
525 getToken();
526 nextIsAnd = false;
527 } else break;
531 void caosScript::parseloop(int state, void *info) {
532 token *t;
533 while ((t = getToken(ANYTOKEN))) {
534 traceindex = errindex;
535 if (t->type() == EOI) {
536 switch (state) {
537 case ST_INSTALLER:
538 case ST_BODY:
539 case ST_REMOVAL:
540 return;
541 default:
542 throw parseException("Unexpected end of input");
545 if (t->type() != TOK_WORD) {
546 throw parseException("Unexpected non-word token");
548 if (t->word() == "scrp") {
549 assert(!enumdepth);
550 if (state != ST_INSTALLER && state != ST_BODY && state != ST_REMOVAL)
551 throw parseException("Unexpected SCRP");
552 state = ST_BODY;
553 int bits[4];
554 for (int i = 0; i < 4; i++) {
555 caosVar val = getToken(TOK_CONST)->constval();
556 if (!val.getType() == CAOSINT)
557 throw parseException("Expected integer constant");
558 bits[i] = val.getInt();
560 int fmly = bits[0];
561 int gnus = bits[1];
562 int spcs = bits[2];
563 int scrp = bits[3];
564 scripts.push_back(shared_ptr<script>(new script(d, filename, fmly, gnus, spcs, scrp)));
565 current = scripts.back();
566 } else if (t->word() == "rscr") {
567 if (state == ST_INSTALLER || state == ST_BODY || state == ST_REMOVAL)
568 state = ST_REMOVAL;
569 else
570 throw parseException("Unexpected RSCR");
571 if (!removal)
572 removal = shared_ptr<script>(new script(d, filename));
573 current = removal;
574 } else if (t->word() == "iscr") {
575 if (state == ST_INSTALLER || state == ST_BODY || state == ST_REMOVAL)
576 state = ST_INSTALLER;
577 else
578 throw parseException("Unexpected RSCR");
579 assert(!enumdepth);
580 current = installer;
581 } else if (t->word() == "endm") {
582 emitOp(CAOS_STOP, 0);
584 if (state == ST_INSTALLER || state == ST_BODY || state == ST_REMOVAL) {
585 assert(!enumdepth);
586 state = ST_INSTALLER;
587 current = installer;
588 } else {
589 // I hate you. Die in a fire.
590 putBackToken(t);
591 return;
593 // No we will not emit c_ENDM() thankyouverymuch
595 } else if (t->word() == "enum"
596 || t->word() == "esee"
597 || t->word() == "etch"
598 || t->word() == "epas"
599 || t->word() == "econ") {
600 int nextreloc = current->newRelocation();
601 putBackToken(t);
603 emitExpr(readExpr(CI_COMMAND));
604 emitOp(CAOS_JMP, nextreloc);
605 int startp = current->getNextIndex();
606 enumdepth++;
607 parseloop(ST_ENUM, NULL);
608 enumdepth--;
609 current->fixRelocation(nextreloc);
611 emitCmd("cmd next");
612 emitOp(CAOS_ENUMPOP, startp);
613 } else if (t->word() == "next") {
614 if (state != ST_ENUM) {
615 throw parseException("Unexpected NEXT");
617 return;
619 } else if (t->word() == "subr") {
620 // Yes, this will work in a doif or whatever. This is UB, it may
621 // be made to not compile later.
622 t = getToken(TOK_WORD);
623 std::string label = t->word();
624 emitOp(CAOS_STOP, 0);
625 current->affixLabel(label);
626 } else if (t->word() == "gsub") {
627 t = getToken(TOK_WORD);
628 std::string label = t->word();
629 emitOp(CAOS_GSUB, current->getLabel(label));
631 } else if (t->word() == "loop") {
632 int loop = current->getNextIndex();
633 emitCmd("cmd loop");
634 parseloop(ST_LOOP, (void *)&loop);
635 } else if (t->word() == "untl") {
636 if (state != ST_LOOP)
637 throw parseException("Unexpected UNTL");
638 // TODO: zerocost logic inversion - do in c_UNTL()?
639 int loop = *(int *)info;
640 int out = current->newRelocation();
641 parseCondition();
642 emitCmd("cmd untl");
643 emitOp(CAOS_CJMP, out);
644 emitOp(CAOS_JMP, loop);
645 current->fixRelocation(out);
646 return;
647 } else if (t->word() == "ever") {
648 if (state != ST_LOOP)
649 throw parseException("Unexpected EVER");
650 int loop = *(int *)info;
651 emitOp(CAOS_JMP, loop);
652 return;
654 } else if (t->word() == "reps") {
655 struct repsinfo ri;
656 ri.jnzreloc = current->newRelocation();
657 putBackToken(t);
658 emitExpr(readExpr(CI_COMMAND));
659 emitOp(CAOS_JMP, ri.jnzreloc);
660 ri.loopidx = current->getNextIndex();
661 parseloop(ST_REPS, (void *)&ri);
662 } else if (t->word() == "repe") {
663 if (state != ST_REPS)
664 throw parseException("Unexpected repe");
665 struct repsinfo *ri = (repsinfo *)info;
666 current->fixRelocation(ri->jnzreloc);
667 emitOp(CAOS_DECJNZ, ri->loopidx);
668 emitCmd("cmd repe");
669 return;
671 } else if (t->word() == "doif") {
672 struct doifinfo di;
673 di.donereloc = current->newRelocation();
674 di.failreloc = current->newRelocation();
675 int okreloc = current->newRelocation();
677 parseCondition();
678 emitCmd("cmd doif");
679 emitOp(CAOS_CJMP, okreloc);
680 emitOp(CAOS_JMP, di.failreloc);
681 current->fixRelocation(okreloc);
682 parseloop(ST_DOIF, (void *)&di);
683 if (di.failreloc)
684 current->fixRelocation(di.failreloc);
685 current->fixRelocation(di.donereloc);
686 emitCmd("cmd endi");
687 } else if (t->word() == "elif") {
688 if (state != ST_DOIF) {
689 // XXX this is horrible
690 t->payload = std::string("doif");
691 continue;
693 struct doifinfo *di = (struct doifinfo *)info;
694 int okreloc = current->newRelocation();
696 emitOp(CAOS_JMP, di->donereloc);
697 current->fixRelocation(di->failreloc);
698 di->failreloc = current->newRelocation();
699 parseCondition();
700 emitCmd("cmd elif");
701 emitOp(CAOS_CJMP, okreloc);
702 emitOp(CAOS_JMP, di->failreloc);
703 current->fixRelocation(okreloc);
704 parseloop(ST_DOIF, info);
705 return;
706 } else if (t->word() == "else") {
707 if (state != ST_DOIF)
708 throw parseException("Unexpected ELSE");
709 struct doifinfo *di = (struct doifinfo *)info;
710 if (!di->failreloc)
711 throw parseException("Duplicate ELSE");
712 emitOp(CAOS_JMP, di->donereloc);
713 current->fixRelocation(di->failreloc);
714 di->failreloc = 0;
715 emitCmd("cmd else");
716 } else if (t->word() == "endi") {
717 if (state != ST_DOIF) {
718 if (engine.version >= 3)
719 throw parseException("Unexpected ENDI");
720 // you are a horrible person if you get here
721 // damn you CL coders.
722 continue;
724 return;
725 } else {
726 if (t->word() == "dbg:") {
727 token *t2 = tokenPeek();
728 if (t2 && t2->type() == TOK_WORD && t2->word() == "asrt") {
729 getToken(TOK_WORD);
730 emitOp(CAOS_CONSTINT, 1);
731 parseCondition();
732 int endreloc = current->newRelocation();
733 emitOp(CAOS_CJMP, endreloc);
734 emitCmd("cmd dbg: asrt");
735 current->fixRelocation(endreloc);
736 continue;
739 putBackToken(t);
740 emitExpr(readExpr(CI_COMMAND));
745 void caosScript::emitCmd(const char *name) {
746 const cmdinfo *ci = d->find_command(name);
747 emitOp(CAOS_CMD, d->cmd_index(ci));
748 if (ci->evalcost)
749 emitOp(CAOS_YIELD, ci->evalcost);
752 void CAOSExpression::eval(caosScript *scr, bool save_here) const {
753 boost::apply_visitor(evalVisit(scr, save_here), value);
756 void CAOSExpression::save(caosScript *scr) const {
757 boost::apply_visitor(saveVisit(scr), value);
760 int CAOSExpression::cost() const {
761 return boost::apply_visitor(costVisit(), value);
763 /* vim: set noet: */