aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk
diff options
context:
space:
mode:
authorAlyssa Milburn2011-07-02 00:16:55 +0200
committerAlyssa Milburn2011-07-02 00:16:55 +0200
commit1b2b9e7604122075a15cb54fe665a32d739b27b0 (patch)
tree5bc51c68bf85752c1111dec87dd2e5da5073acb2 /engines/mohawk
parentd2035b34e0a6b829737e65e8cc382924b7d356fc (diff)
downloadscummvm-rg350-1b2b9e7604122075a15cb54fe665a32d739b27b0.tar.gz
scummvm-rg350-1b2b9e7604122075a15cb54fe665a32d739b27b0.tar.bz2
scummvm-rg350-1b2b9e7604122075a15cb54fe665a32d739b27b0.zip
MOHAWK: Add LBCode::parseCode.
This allows script strings to be parsed into LB bytecode.
Diffstat (limited to 'engines/mohawk')
-rw-r--r--engines/mohawk/livingbooks_code.cpp274
-rw-r--r--engines/mohawk/livingbooks_code.h4
2 files changed, 278 insertions, 0 deletions
diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp
index 0aaedfe274..89a77fbcfb 100644
--- a/engines/mohawk/livingbooks_code.cpp
+++ b/engines/mohawk/livingbooks_code.cpp
@@ -1057,4 +1057,278 @@ void LBCode::runNotifyCommand() {
}
}
+/*
+ * Helper function for parseCode/parseCodeSymbol:
+ * Returns an unused string id.
+ */
+uint LBCode::nextFreeString() {
+ for (uint i = 0; i <= 0xffff; i++) {
+ if (!_strings.contains(i))
+ return i;
+ }
+
+ error("nextFreeString couldn't find a space");
+}
+
+/*
+ * Helper function for parseCode:
+ * Given a name, appends the appropriate data to the provided code array and
+ * returns true if it's a function, or false otherwise.
+ */
+bool LBCode::parseCodeSymbol(const Common::String &name, uint &pos, Common::Array<byte> &code) {
+ // first, check whether the name matches a known function
+ for (uint i = 0; i < 2; i++) {
+ byte cmdToken;
+ CodeCommandInfo *cmdInfo;
+ uint cmdCount;
+
+ switch (i) {
+ case 0:
+ cmdInfo = generalCommandInfo;
+ cmdToken = kTokenGeneralCommand;
+ cmdCount = NUM_GENERAL_COMMANDS;
+ break;
+ case 1:
+ cmdInfo = itemCommandInfo;
+ cmdToken = kTokenItemCommand;
+ cmdCount = NUM_ITEM_COMMANDS;
+ break;
+ }
+
+ for (uint n = 0; n < cmdCount; n++) {
+ const char *cmdName = cmdInfo[n].name;
+ if (!cmdName)
+ continue;
+ if (!name.equalsIgnoreCase(cmdName))
+ continue;
+
+ // found a matching function
+ code.push_back(cmdToken);
+ code.push_back(n + 1);
+ return true;
+ }
+ }
+
+ // not a function, so must be an identifier
+ code.push_back(kTokenIdentifier);
+
+ uint stringId = nextFreeString();
+ _strings[stringId] = name;
+
+ char tmp[2];
+ WRITE_BE_UINT16(tmp, (int16)stringId);
+ code.push_back(tmp[0]);
+ code.push_back(tmp[1]);
+
+ return false;
+}
+
+/*
+ * Parse a string for later execution, and return the offset where it was
+ * stored.
+ */
+uint LBCode::parseCode(const Common::String &source) {
+ struct LBCodeOperator {
+ byte token;
+ byte op;
+ byte lookahead1;
+ byte lookahead1Op;
+ byte lookahead2;
+ byte lookahead2Op;
+ };
+
+ #define NUM_LB_OPERATORS 11
+ static const LBCodeOperator operators[NUM_LB_OPERATORS] = {
+ { '+', kTokenPlus, '+', kTokenPlusPlus, '=', kTokenPlusEquals },
+ { '-', kTokenMinus, '-', kTokenMinusMinus, '=', kTokenMinusEquals },
+ { '/', kTokenDivide, '=', kTokenDivideEquals, 0, 0 },
+ { '*', kTokenMultiply, '=', kTokenMultiplyEquals, 0, 0 },
+ { '=', kTokenAssign, '=', kTokenEquals, 0, 0 },
+ { '>', kTokenGreaterThan, '=', kTokenGreaterThanEq, 0, 0 },
+ { '<', kTokenLessThan, '=', kTokenLessThanEq, 0, 0 },
+ { '!', kTokenNot, '=', kTokenNotEq, 0, 0 },
+ { '&', kTokenConcat, '&', kTokenAnd, '=', kTokenAndEquals },
+ { '|', 0, '|', kTokenOr, 0, 0 },
+ { ';', kTokenEndOfStatement, 0, 0, 0, 0 }
+ };
+
+ uint pos = 0;
+ Common::Array<byte> code;
+ Common::Array<uint> counterPositions;
+ bool wasFunction = false;
+
+ while (pos < source.size()) {
+ byte token = source[pos];
+ byte lookahead = 0;
+ if (pos + 1 < source.size())
+ lookahead = source[pos + 1];
+ pos++;
+
+ if (token != ' ' && token != '(' && wasFunction)
+ error("while parsing script '%s', encountered incomplete function call", source.c_str());
+
+ // First, we check for simple operators.
+ for (uint i = 0; i < NUM_LB_OPERATORS; i++) {
+ if (token != operators[i].token)
+ continue;
+ if (lookahead) {
+ if (lookahead == operators[i].lookahead1) {
+ code.push_back(operators[i].lookahead1Op);
+ token = 0;
+ } else if (lookahead == operators[i].lookahead2) {
+ code.push_back(operators[i].lookahead2Op);
+ token = 0;
+ }
+ if (!token) {
+ pos++;
+ break;
+ }
+ }
+ if (operators[i].op) {
+ code.push_back(operators[i].op);
+ token = 0;
+ }
+ break;
+ }
+ if (!token)
+ continue;
+
+ // Then, we check for more complex tokens.
+ switch (token) {
+ // whitespace
+ case ' ':
+ // ignore
+ break;
+ // literal string
+ case '"':
+ case '\'':
+ {
+ Common::String tempString;
+ while (pos < source.size()) {
+ if (source[pos] == token)
+ break;
+ tempString += source[pos++];
+ }
+ if (pos++ == source.size())
+ error("while parsing script '%s', string had no end", source.c_str());
+
+ code.push_back(kTokenString);
+
+ uint stringId = nextFreeString();
+ _strings[stringId] = tempString;
+
+ char tmp[2];
+ WRITE_BE_UINT16(tmp, (int16)stringId);
+ code.push_back(tmp[0]);
+ code.push_back(tmp[1]);
+ }
+ break;
+ // open bracket
+ case '(':
+ if (wasFunction) {
+ // function call parameters
+ wasFunction = false;
+ // we will need to back-patch the parameter count,
+ // if parameters are encountered
+ counterPositions.push_back(code.size());
+ code.push_back(1);
+ // if the next token is a ) then there are no
+ // parameters, otherwise start with 1 and increment
+ // if/when we encounter commas
+ for (uint i = pos; i < source.size(); i++) {
+ if (source[i] == ' ')
+ continue;
+ if (source[i] != ')')
+ break;
+ code[code.size() - 1] = 0;
+ break;
+ }
+ } else {
+ // brackets around expression
+ counterPositions.push_back(0);
+ }
+ code.push_back(kTokenOpenBracket);
+ break;
+ // close bracket
+ case ')':
+ if (counterPositions.empty())
+ error("while parsing script '%s', encountered unmatched )", source.c_str());
+ counterPositions.pop_back();
+ code.push_back(kTokenCloseBracket);
+ break;
+ // comma (seperating function params)
+ case ',':
+ {
+ if (counterPositions.empty())
+ error("while parsing script '%s', encountered unexpected ,", source.c_str());
+ code.push_back(kTokenComma);
+ uint counterPos = counterPositions.back();
+ if (!counterPos)
+ error("while parsing script '%s', encountered , outside parameter list", source.c_str());
+ code[counterPos]++;
+ }
+ break;
+ // old-style explicit function call
+ case '@':
+ {
+ Common::String tempString;
+ while (pos < source.size()) {
+ if (!isalpha(source[pos]) && !isdigit(source[pos]))
+ break;
+ tempString += source[pos++];
+ }
+ wasFunction = parseCodeSymbol(tempString, pos, code);
+ if (!wasFunction)
+ error("while parsing script '%s', encountered explicit function call to unknown function '%s'",
+ source.c_str(), tempString.c_str());
+ }
+ break;
+ default:
+ if (isdigit(token)) {
+ const char *in = source.c_str() + pos - 1;
+ // FIXME: handle floats?
+ char *endptr;
+ long int intValue = strtol(in, &endptr, 0);
+ assert(endptr > in);
+ pos += (endptr - in) - 1;
+
+ // FIXME: handle storing longs if needed
+ code.push_back(kTokenLiteral);
+ code.push_back(kLBCodeLiteralInteger);
+ char tmp[2];
+ WRITE_BE_UINT16(tmp, (int16)intValue);
+ code.push_back(tmp[0]);
+ code.push_back(tmp[1]);
+ } else if (isalpha(token)) {
+ Common::String tempString;
+ tempString += token;
+ while (pos < source.size()) {
+ if (!isalpha(source[pos]) && !isdigit(source[pos]))
+ break;
+ tempString += source[pos++];
+ }
+ wasFunction = parseCodeSymbol(tempString, pos, code);
+ } else {
+ error("while parsing script '%s', couldn't parse '%c'", source.c_str(), token);
+ }
+ }
+ }
+
+ if (wasFunction)
+ error("while parsing script '%s', encountered incomplete function call", source.c_str());
+ if (counterPositions.size())
+ error("while parsing script '%s', unmatched (", source.c_str());
+
+ code.push_back(kTokenEndOfFile);
+
+ uint codeOffset = _size;
+ byte *newData = new byte[_size + code.size()];
+ memcpy(newData, _data, _size);
+ memcpy(newData, &code[0], code.size());
+ delete[] _data;
+ _data = newData;
+ _size += code.size();
+ return codeOffset;
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/livingbooks_code.h b/engines/mohawk/livingbooks_code.h
index 3e33549b4d..85bb706515 100644
--- a/engines/mohawk/livingbooks_code.h
+++ b/engines/mohawk/livingbooks_code.h
@@ -181,6 +181,7 @@ public:
~LBCode();
LBValue runCode(LBItem *src, uint32 offset);
+ uint parseCode(const Common::String &source);
protected:
MohawkEngine_LivingBooks *_vm;
@@ -214,6 +215,9 @@ protected:
void runItemCommand();
void runNotifyCommand();
+ uint nextFreeString();
+ bool parseCodeSymbol(const Common::String &name, uint &pos, Common::Array<byte> &code);
+
public:
void cmdUnimplemented(const Common::Array<LBValue> &params);
void cmdGetRect(const Common::Array<LBValue> &params);