aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/archetype/archetype.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/archetype/archetype.cpp')
-rw-r--r--engines/glk/archetype/archetype.cpp876
1 files changed, 873 insertions, 3 deletions
diff --git a/engines/glk/archetype/archetype.cpp b/engines/glk/archetype/archetype.cpp
index d2409bfce3..4b19c57854 100644
--- a/engines/glk/archetype/archetype.cpp
+++ b/engines/glk/archetype/archetype.cpp
@@ -20,8 +20,16 @@
*
*/
-#include "glk/archetype/archetype.h"
#include "common/config-manager.h"
+#include "glk/archetype/archetype.h"
+#include "glk/archetype/crypt.h"
+#include "glk/archetype/expression.h"
+#include "glk/archetype/heap_sort.h"
+#include "glk/archetype/misc.h"
+#include "glk/archetype/saveload.h"
+#include "glk/archetype/sys_object.h"
+#include "glk/archetype/timestamp.h"
+#include "glk/archetype/wrap.h"
namespace Glk {
namespace Archetype {
@@ -29,15 +37,44 @@ namespace Archetype {
Archetype *g_vm;
Archetype::Archetype(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
- _saveSlot(-1) {
+ _saveSlot(-1) {
g_vm = this;
}
void Archetype::runGame() {
-
+ initialize();
+ interpret();
+ deinitialize();
}
bool Archetype::initialize() {
+ crypt_init();
+ expression_init();
+ heap_sort_init();
+ misc_init();
+ saveload_init();
+ sys_object_init();
+ timestamp_init();
+
+ // keywords
+ new_xarray(Literals);
+ new_xarray(Vocabulary);
+
+ new_xarray(Type_ID_List);
+ new_xarray(Object_ID_List);
+ new_xarray(Attribute_ID_List);
+
+ // parser
+ Abbreviate = 0x7fffffff;
+ new_list(Proximate);
+ new_list(object_names);
+ new_list(verb_names);
+
+ // semantic
+ new_xarray(Type_List);
+ new_xarray(Object_List);
+ NullStr = NewConstStr("null");
+
return true;
}
@@ -54,5 +91,838 @@ Common::Error Archetype::writeGameData(Common::WriteStream *ws) {
return Common::kWritingFailed;
}
+void Archetype::interpret() {
+ Translating = false;
+ bool success = loadGame();
+ _gameFile.close();
+
+ if (!success)
+ error("Could not load game");
+
+ ContextType context;
+ ResultType result;
+ undefine(result);
+
+ if (!send_message(OP_SEND, find_message("START"), MainObject, result, context))
+ error("Cannot execute; no ''START'' message for main object.");
+
+ cleanup(result);
+}
+
+bool Archetype::loadGame() {
+ return false;
+}
+
+void Archetype::write(const String &fmt, ...) {
+ // TODO
+}
+
+void Archetype::writeln(const String &fmt, ...) {
+ // TODO
+}
+
+void Archetype::readln(String &s) {
+ // TODO
+}
+
+char Archetype::ReadKey() {
+ // TODO
+ return '\0';
+}
+
+void Archetype::lookup(int the_obj, int the_attr, ResultType &result, ContextType &context,
+ DesiredType desired) {
+ NodePtr np;
+ bool done, first_pass;
+ ListType attrs;
+ int parent;
+ void *p, *original;
+ ExprTree e;
+ ContextType c;
+
+ cleanup(result);
+
+ if (desired == NAME) {
+ result._kind = IDENT;
+ result._ident.ident_kind = ATTRIBUTE_ID;
+ result._ident.ident_int = the_attr;
+ return;
+ }
+
+ if (the_obj == 0)
+ // system object - all attributes UNDEFINED
+ return;
+
+ if (!index_xarray(Object_List, the_obj, original)) {
+ g_vm->writeln("Internal error: cannot reference object %d", the_obj);
+ return;
+ }
+
+ // Return UNDEFINED for attempting to reference any attribute of a destroyed object
+ if (original == nullptr)
+ return;
+
+ // It is important to change the context before a lookup so that any non-scalar expressions
+ // that are referenced will be evaluated in the light of that object's context
+ c = context;
+ c.self = the_obj; // references to self must be right
+ c.each = 0;
+
+ first_pass = true;
+ p = original;
+ done = false;
+
+ // Inheritance loop
+ do {
+ ObjectType &obj = *((ObjectPtr)p);
+ attrs = obj.attributes;
+ parent = obj.inherited_from;
+
+ np = find_item(attrs, the_attr);
+ if (np != nullptr || parent == 0) {
+ done = true;
+ } else {
+ // Track back
+ if (!index_xarray(Type_List, parent, p)) {
+ writeln("Internal error: lookup cannot find parent type %d", parent);
+ return;
+ }
+
+ first_pass = false;
+ }
+ } while (!done);
+
+ if (np == nullptr)
+ // not found anywhere
+ return;
+
+ switch (desired) {
+ case RVALUE:
+ eval_expr((ExprPtr)np->data, result, c, RVALUE);
+ break;
+
+ // Getting an inherited LVALUE is tricky. We must remember that since we have come
+ // this far, we definitely will return an ATTR_PTR result
+ case LVALUE:
+ if (first_pass) {
+ result._kind = ATTR_PTR;
+ result._attr.acl_attr = np;
+ } else {
+ // inherited - must create new node }
+ result._kind = ATTR_PTR;
+ result._attr.acl_attr = (NodePtr)malloc(sizeof(NodeType));
+
+ e = (ExprTree)malloc(sizeof(ExprNode));
+ undefine(*e);
+ eval_expr((ExprPtr)np->data, *e, c, RVALUE);
+
+ result._attr.acl_attr->data = e;
+ result._attr.acl_attr->key = the_attr;
+ insert_item(((ObjectPtr)original)->attributes, result._attr.acl_attr);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool Archetype::send_message(int transport, int message_sent, int recipient,
+ ResultType &result, ContextType &context) {
+ bool done, find_other;
+ ObjectPtr op, original;
+ ResultType r;
+ NodePtr np;
+ StatementPtr st;
+ void *p;
+ ContextType c;
+
+ if (message_sent == 0) {
+ cleanup(result);
+ return false;
+ }
+
+ if ((Debug & DEBUG_MSGS) > 0) {
+ r._kind = IDENT;
+ r._ident.ident_kind = OBJECT_ID;
+ r._ident.ident_int = context.self;
+ wrapout(" : ", false);
+ display_result(r);
+
+ if (transport == OP_SEND)
+ wrapout(" sending ", false);
+ else
+ wrapout(" passing ", false);
+
+ if (index_xarray(Vocabulary, message_sent, p)) {
+ String str = String::format("'%s'", ((StringPtr)p)->c_str());
+ wrapout(str, false);
+ }
+
+ if (transport == OP_SEND_TO_TYPE)
+ r._ident.ident_kind = TYPE_ID;
+
+ wrapout(" to ", false);
+ r._ident.ident_int = recipient;
+ display_result(r);
+ wrapout("", true);
+ }
+
+ // Trying to send a message to a destroyed object results in UNDEFINED
+
+ if ((((transport == OP_SEND_TO_TYPE) && index_xarray(Type_List, recipient, p))
+ || index_xarray(Object_List, recipient, p))
+ && (p != nullptr)) {
+ c = context;
+ c.each = 0;
+ c.message = message_sent;
+ if (transport == OP_SEND) {
+ c.sender = context.self;
+ c.self = recipient;
+ }
+
+ op = (ObjectPtr)p;
+ original = op;
+ done = false;
+ find_other = false;
+ while (!done) {
+ if (find_other) {
+ st = op->other;
+ } else {
+ np = find_item(op->methods, message_sent);
+ if (np != nullptr)
+ st = (StatementPtr)np->data;
+ else
+ st = nullptr;
+ }
+
+ if (st != nullptr) {
+ // found it
+ exec_stmt(st, result, c);
+ return true;
+ } else {
+ // no message for recipient
+ if (op->inherited_from == 0) {
+ if (find_other) {
+ done = true;
+ } else {
+ op = original;
+ find_other = true;
+ }
+ }
+ else if (index_xarray(Type_List, op->inherited_from, p)) {
+ op = (ObjectPtr)p;
+ } else {
+ wraperr("Internal error: invalid inheritance");
+ return false;
+ }
+ }
+ }
+ }
+
+ // If we get here, it means that there was not even a "default" handler for
+ // the message in the given object or its lineage. Return ABSENT
+ result._kind = RESERVED;
+ result._reserved.keyword = RW_ABSENT;
+
+ return false;
+}
+
+void Archetype::eval_expr(ExprTree the_expr, ResultType &result, ContextType &context, DesiredType desired) {
+ ResultType r1, r2;
+ int i;
+ ExprTree e;
+ bool b;
+
+ // It is very important to make sure that the "kind" fields of our temporary result variables
+ // are properly set to RESERVED/UNDEFINED before doing anything with them, so that if someone
+ // tries to clean them up later on, they won"t try to dispose of a string that isn't there
+ undefine(r1);
+ undefine(r2);
+
+ cleanup(result);
+
+ if (the_expr == nullptr)
+ return;
+
+ // Check: if this is a lone attribute, look it up in this object"s table
+ if (the_expr->_kind == IDENT && the_expr->_ident.ident_kind == ATTRIBUTE_ID)
+ lookup(context.self, the_expr->_ident.ident_int, result, context, desired);
+
+ else if (the_expr->_kind == RESERVED) {
+ // it is a special reserved word that requires an action
+ switch (the_expr->_reserved.keyword) {
+ case RW_READ:
+ case RW_KEY:
+ result._kind = STR_PTR;
+ if (the_expr->_reserved.keyword == RW_READ)
+ result._str.acl_str = ReadLine(true); // read full line
+ else
+ result._str.acl_str = ReadLine(false); // read single key
+
+ Rows = 0;
+ cursor_reset(); // user will have had to hit <RETURN>
+ break;
+
+ case RW_MESSAGE:
+ result._kind = MESSAGE;
+ result._msgTextQuote.index = context.message;
+ break;
+
+ case RW_EACH:
+ case RW_SELF:
+ case RW_SENDER:
+ result._kind = IDENT;
+ result._ident.ident_kind = OBJECT_ID;
+
+ switch (the_expr->_reserved.keyword) {
+ case RW_EACH:
+ result._ident.ident_int = context.each;
+ break;
+ case RW_SELF:
+ result._ident.ident_int = context.self;
+ break;
+ case RW_SENDER:
+ result._ident.ident_int = context.sender;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else if (the_expr->_kind == OPER) {
+ // It's an operator, need to evaulate it
+ switch (the_expr->_oper.op_name) {
+ case OP_SEND:
+ case OP_PASS:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+
+ if (r2._kind == IDENT && (r2._ident.ident_kind == OBJECT_ID || r2._ident.ident_kind == TYPE_ID)) {
+ // Object 0 is the system object and always receives string messages
+
+ if (r2._ident.ident_kind == OBJECT_ID && r2._ident.ident_int == 0) {
+ if (convert_to(STR_PTR, r1))
+ send_to_system(the_expr->_oper.op_name, *r1._str.acl_str, result, context);
+
+ } else if (convert_to(MESSAGE, r1)) {
+ if (r2._ident.ident_kind == TYPE_ID)
+ b = send_message(OP_SEND_TO_TYPE, r1._msgTextQuote.index, r2._ident.ident_int,
+ result, context);
+ else
+ b = send_message(the_expr->_oper.op_name, r1._msgTextQuote.index,
+ r2._ident.ident_int, result, context);
+ }
+ }
+ break;
+
+ case OP_DOT:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+
+ if (r1._kind == IDENT && r1._ident.ident_kind == OBJECT_ID) {
+ eval_expr(the_expr->_oper.right, r2, context, NAME);
+ if (r2._kind == IDENT && r2._ident.ident_kind == ATTRIBUTE_ID)
+ lookup(r1._ident.ident_int, r2._ident.ident_int, result, context, desired);
+ }
+ break;
+
+ case OP_ASSIGN:
+ if (desired == NAME)
+ return;
+
+ eval_expr(the_expr->_oper.right, result, context, RVALUE);
+ eval_expr(the_expr->_oper.left, r1, context, LVALUE);
+
+ if (!assignment(r1, result))
+ cleanup(result);
+ else if (desired == LVALUE) {
+ cleanup(result);
+ result._kind = ATTR_PTR;
+ result._attr.acl_attr = r1._attr.acl_attr;
+ }
+ break;
+
+ case OP_C_MULTIPLY:
+ case OP_C_DIVIDE:
+ case OP_C_PLUS:
+ case OP_C_MINUS:
+ case OP_C_CONCAT:
+ if (desired == NAME)
+ return;
+
+ // Do the two operations using a dummy expression node
+ e = (ExprTree)malloc(sizeof(ExprNode));
+ *e = *the_expr;
+
+ switch (the_expr->_oper.op_name) {
+ case OP_C_MULTIPLY:
+ e->_oper.op_name = OP_MULTIPLY;
+ break;
+ case OP_C_DIVIDE:
+ e->_oper.op_name = OP_DIVIDE;
+ break;
+ case OP_C_PLUS:
+ e->_oper.op_name = OP_PLUS;
+ break;
+ case OP_C_MINUS:
+ e->_oper.op_name = OP_MINUS;
+ break;
+ case OP_C_CONCAT:
+ e->_oper.op_name = OP_CONCAT;
+ break;
+ default:
+ break;
+ }
+
+ eval_expr(e, r1, context, RVALUE);
+ e->_oper.op_name = OP_ASSIGN;
+ e->_oper.right = &r1;
+
+ eval_expr(e, result, context, desired);
+ free(e);
+ break;
+
+ case OP_CHS:
+ case OP_NUMERIC:
+ eval_expr(the_expr->_oper.right, result, context, RVALUE);
+ if (!convert_to(NUMERIC, result))
+ cleanup(result);
+ else if (the_expr->_oper.op_name == OP_CHS)
+ result._numeric.acl_int = -result._numeric.acl_int;
+ break;
+
+ case OP_STRING:
+ eval_expr(the_expr->_oper.right, result, context, RVALUE);
+ if (!convert_to(STR_PTR, result))
+ cleanup(result);
+ break;
+
+ case OP_LENGTH:
+ eval_expr(the_expr->_oper.right, r1, context, RVALUE);
+ if (convert_to(STR_PTR, r1)) {
+ result._kind = NUMERIC;
+ result._numeric.acl_int = r1._str.acl_str->size();
+ }
+ break;
+
+ // For the random operator, we must be careful: ? "01234" should select a random digit
+ // out of that set, not attempt to convert it to 1234 and take a random number in the
+ // range 1 - 1234. However, we can neither immediately convert it to string, because
+ // ? 6 should produce a value in the range 1 - 6, not the character "6". }
+ case OP_RANDOM:
+ eval_expr(the_expr->_oper.right, result, context, RVALUE);
+ if (result._kind == NUMERIC)
+ // convert x < range to 1 <= x <= range
+ result._numeric.acl_int = g_vm->getRandomNumber(result._numeric.acl_int - 1) + 1;
+ else if (convert_to(STR_PTR, result)) {
+ // Replace the string with a single random character for it
+ String &s = *result._str.acl_str;
+ s = s[g_vm->getRandomNumber(s.size() - 1)];
+ }
+ break;
+
+ case OP_NOT:
+ result._kind = RESERVED;
+ if (eval_condition(the_expr->_oper.right, context))
+ result._reserved.keyword = RW_FALSE;
+ else
+ result._reserved.keyword = RW_TRUE;
+ break;
+
+ case OP_PLUS:
+ case OP_MINUS:
+ case OP_MULTIPLY:
+ case OP_DIVIDE:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ if (convert_to(NUMERIC, r1) && convert_to(NUMERIC, r2)) {
+ result._kind = NUMERIC;
+ switch (the_expr->_oper.op_name) {
+ case OP_PLUS:
+ result._numeric.acl_int = r1._numeric.acl_int + r2._numeric.acl_int;
+ break;
+ case OP_MINUS:
+ result._numeric.acl_int = r1._numeric.acl_int - r2._numeric.acl_int;
+ break;
+ case OP_MULTIPLY:
+ result._numeric.acl_int = r1._numeric.acl_int * r2._numeric.acl_int;
+ break;
+ case OP_DIVIDE:
+ result._numeric.acl_int = r1._numeric.acl_int / r2._numeric.acl_int;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case OP_AND:
+ result._kind = RESERVED;
+ if (eval_condition(the_expr->_oper.left, context) && eval_condition(the_expr->_oper.right, context))
+ result._reserved.keyword = RW_TRUE;
+ else
+ result._reserved.keyword = RW_FALSE;
+ break;
+
+ case OP_OR:
+ if (eval_condition(the_expr->_oper.left, context) || eval_condition(the_expr->_oper.right, context))
+ result._reserved.keyword = RW_TRUE;
+ else
+ result._reserved.keyword = RW_FALSE;
+ break;
+
+ case OP_POWER:
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ if (convert_to(NUMERIC, r2) && convert_to(NUMERIC, r1)) {
+ result._kind = NUMERIC;
+ result._numeric.acl_int = 1;
+ for (i = 1; i <= r2._numeric.acl_int; ++i)
+ result._numeric.acl_int *= r1._numeric.acl_int;
+ }
+ break;
+
+ case OP_CONCAT:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ if (convert_to(STR_PTR, r1) && convert_to(STR_PTR, r2)) {
+ result._kind = STR_PTR;
+ result._str.acl_str = MakeNewDynStr(*r1._str.acl_str + *r2._str.acl_str);
+ }
+ break;
+
+ case OP_LEFTFROM:
+ case OP_RIGHTFROM:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ if (convert_to(STR_PTR, r1) && convert_to(NUMERIC, r2)) {
+ result._kind = STR_PTR;
+ if (the_expr->_oper.op_name == OP_LEFTFROM)
+ result._str.acl_str = MakeNewDynStr(r1._str.acl_str->left(r2._numeric.acl_int));
+ else
+ result._str.acl_str = MakeNewDynStr(r1._str.acl_str->right(r2._numeric.acl_int));
+ }
+ break;
+
+ case OP_WITHIN:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+ if (convert_to(STR_PTR, r1) && convert_to(STR_PTR, r2)) {
+ result._kind = NUMERIC;
+ result._numeric.acl_int = r2._str.acl_str->indexOf(*r1._str.acl_str);
+ if (result._numeric.acl_int == -1)
+ cleanup(result);
+ }
+ break;
+
+ case OP_EQ:
+ case OP_NE:
+ case OP_LT:
+ case OP_GT:
+ case OP_LE:
+ case OP_GE:
+ eval_expr(the_expr->_oper.left, r1, context, RVALUE);
+ eval_expr(the_expr->_oper.right, r2, context, RVALUE);
+
+ result._kind = RESERVED;
+ if (result_compare(the_expr->_oper.op_name, r1, r2))
+ result._reserved.keyword = RW_TRUE;
+ else
+ result._reserved.keyword = RW_FALSE;
+ break;
+
+ default:
+ g_vm->writeln("Internal error: \"%s\" not yet supported", Operators[the_expr->_oper.op_name]);
+ break;
+ }
+
+ cleanup(r1);
+ cleanup(r2);
+
+ if ((Debug & DEBUG_EXPR) > 0) {
+ wrapout(" -- ", false);
+ display_expr(the_expr);
+ wrapout(" ==> ", false);
+ display_result(result);
+ wrapout("", true);
+ }
+ } else {
+ switch (desired) {
+ case RVALUE:
+ copy_result(result, *the_expr);
+ break;
+ case LVALUE:
+ result = *the_expr;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+bool Archetype::eval_condition(ExprTree the_expr, ContextType &context) {
+ ResultType result;
+ bool failure;
+
+ undefine(result);
+ eval_expr(the_expr, result, context, RVALUE);
+
+ failure = (result._kind == RESERVED) && (result._reserved.keyword = RW_UNDEFINED
+ || result._reserved.keyword == RW_FALSE || result._reserved.keyword == RW_ABSENT);
+
+ cleanup(result);
+ return !failure;
+}
+
+void Archetype::exec_stmt(StatementPtr the_stmt, ResultType &result, ContextType &context) {
+ NodePtr np;
+ void *p, *q;
+ ResultType r1, r2;
+ CasePairPtr this_case;
+ bool b;
+ ContextType c;
+ int i;
+ ObjectPtr the_object;
+ bool verbose;
+
+ undefine(r1);
+ undefine(r2);
+ cleanup(result);
+
+ verbose = (Debug & DEBUG_STMT) > 0;
+
+ if (verbose)
+ wrapout(" == ", false);
+
+ switch (the_stmt->_kind) {
+ case COMPOUND:
+ np = nullptr;
+ b = false;
+ while (!b && iterate_list(the_stmt->_compound.statements, np)) {
+ cleanup(result);
+ exec_stmt((StatementPtr)np->data, result, context);
+
+ b = (result._kind == RESERVED) && (result._reserved.keyword == RW_BREAK);
+ }
+ break;
+
+ case ST_EXPR:
+ if (verbose)
+ display_expr(the_stmt->_expr.expression);
+
+ switch (the_stmt->_expr.expression->_kind) {
+ case QUOTE_LIT:
+ if (index_xarray(Literals, the_stmt->_expr.expression->_msgTextQuote.index, p)) {
+ result._kind = TEXT_LIT;
+ result._msgTextQuote.index = the_stmt->_expr.expression->_msgTextQuote.index;
+ wrapout(*((StringPtr)p), true);
+ }
+ break;
+
+ case MESSAGE:
+ b = send_message(OP_PASS, the_stmt->_expr.expression->_msgTextQuote.index,
+ context.self, result, context);
+ default:
+ eval_expr(the_stmt->_expr.expression, result, context, RVALUE);
+ break;
+ }
+ break;
+
+ case ST_WRITE:
+ case ST_WRITES:
+ case ST_STOP:
+ if (verbose) {
+ switch (the_stmt->_kind) {
+ case ST_WRITE:
+ wrapout("write ", false);
+ break;
+ case ST_WRITES:
+ wrapout("writes ", false);
+ break;
+ case ST_STOP:
+ wrapout("stop ", false);
+ break;
+ default:
+ break;
+ }
+
+ wrapout(" ", false);
+ np = nullptr;
+ while (iterate_list(the_stmt->_write.print_list, np)) {
+ display_expr((ExprTree)np->data);
+ if (np->next != the_stmt->_write.print_list)
+ wrapout(", ", false);
+ }
+
+ wrapout("", true);
+ }
+
+ np = nullptr;
+ while (iterate_list(the_stmt->_write.print_list, np)) {
+ cleanup(result);
+ eval_expr((ExprTree)np->data, result, context, RVALUE);
+ write_result(result);
+ }
+
+ if (the_stmt->_kind == ST_WRITE) {
+ wrapout("", true);
+ }
+ else if (the_stmt->_kind == ST_STOP) {
+ g_vm->writeln();
+ g_vm->writeln();
+ error("%s", VERSION);
+ }
+ break;
+
+ case ST_IF:
+ if (verbose) {
+ wrapout("if: Testing ", false);
+ display_expr(the_stmt->_if.condition);
+ }
+ if (eval_condition(the_stmt->_if.condition, context)) {
+ if (verbose)
+ wrapout(" Evaluated true; executing then branch", true);
+ exec_stmt(the_stmt->_if.then_branch, result, context);
+
+ }
+ else if (the_stmt->_if.else_branch != nullptr) {
+ if (verbose)
+ wrapout(" Evaluated false; executing else branch", true);
+ exec_stmt(the_stmt->_if.else_branch, result, context);
+ }
+ break;
+
+ case ST_CASE:
+ if (verbose) {
+ wrapout("case ", false);
+ display_expr(the_stmt->_case.test_expr);
+ wrapout(" of", false);
+ wrapout("", true);
+ }
+
+ eval_expr(the_stmt->_case.test_expr, r1, context, RVALUE);
+ np = nullptr;
+
+ while (iterate_list(the_stmt->_case.cases, np)) {
+ this_case = (CasePairPtr)np->data;
+
+ //with this_case^ do begin
+ eval_expr(this_case->value, r2, context, RVALUE);
+ if ((r2._kind == RESERVED && r2._reserved.keyword == RW_DEFAULT)
+ || result_compare(OP_EQ, r1, r2)) {
+ exec_stmt(this_case->action, result, context);
+ cleanup(r1);
+ cleanup(r2);
+ return;
+ }
+
+ cleanup(r2);
+ }
+
+ cleanup(result);
+ cleanup(r1);
+ break;
+
+ case ST_BREAK:
+ result._kind = RESERVED;
+ result._reserved.keyword = RW_BREAK;
+ break;
+
+ case ST_FOR:
+ b = false;
+ c = context;
+ c.each = 1;
+
+ while (!b && c.each < (int)Object_List.size()) {
+ if (eval_condition(the_stmt->_loop.selection, c)) {
+ exec_stmt(the_stmt->_loop.action, result, c);
+ b = (result._kind == RESERVED) && (result._reserved.keyword == RW_BREAK);
+ cleanup(result);
+ }
+
+ ++c.each;
+ }
+ break;
+
+ case ST_WHILE:
+ b = false;
+ while (!b && eval_condition(the_stmt->_loop.selection, context)) {
+ exec_stmt(the_stmt->_loop.action, result, context);
+ b = (result._kind == RESERVED) && (result._reserved.keyword == RW_BREAK);
+ cleanup(result);
+ }
+ break;
+
+ case ST_CREATE:
+ eval_expr(the_stmt->_create.new_name, r1, context, LVALUE);
+
+ // Attempt a dummy assignment just to see if it works
+ result._kind = IDENT;
+ result._ident.ident_kind = OBJECT_ID;
+ result._ident.ident_int = 0;
+
+ if (!assignment(r1, result)) {
+ cleanup(result);
+ }
+ else {
+ // do it for real
+ the_object = (ObjectPtr)malloc(sizeof(ObjectType));
+ the_object->inherited_from = the_stmt->_create.archetype;
+ new_list(the_object->attributes);
+ new_list(the_object->methods);
+ the_object->other = nullptr;
+ p = the_object;
+
+ // NOTE: Search the list for an empty slot; if none found, append
+ i = Dynamic;
+ b = true;
+ while (access_xarray(Object_List, i, q, PEEK_ACCESS) && q != nullptr)
+ ++i;
+
+ if (i >= (int)Object_List.size())
+ append_to_xarray(Object_List, p);
+ else
+ b = access_xarray(Object_List, i, p, POKE_ACCESS);
+
+ // Now we know its number; go back and update the result"s object reference.
+ // "Return" this same value
+ ((ExprPtr)r1._attr.acl_attr->data)->_ident.ident_int = i;
+ copy_result(result, *(ExprPtr)r1._attr.acl_attr->data);
+
+ cleanup(r1);
+ }
+ break;
+
+ // Just dispose of the indicated object in the Object_List. Shrink the list only if
+ // the very last object was destroyed
+ case ST_DESTROY:
+ eval_expr(the_stmt->_destroy.victim, result, context, RVALUE);
+ if (result._kind == IDENT && result._ident.ident_kind == OBJECT_ID
+ && index_xarray(Object_List, result._ident.ident_int, p)) {
+ the_object = (ObjectPtr)p;
+ dispose_object(the_object);
+ p = nullptr;
+ b = access_xarray(Object_List, result._ident.ident_int, p, POKE_ACCESS);
+ if (result._ident.ident_int == ((int)Object_List.size() - 1))
+ shrink_xarray(Object_List);
+ } else {
+ wraperr("Can only destroy previously created objects");
+ }
+
+ cleanup(result);
+ break;
+
+ default:
+ wraperr("Internal error: statement not supported yet");
+ break;
+ }
+
+ if (verbose)
+ wrapout("", true); // finish off dangling lines
+}
+
} // End of namespace Archetype
} // End of namespace Glk