[Scummvm-cvs-logs] scummvm master -> aceb1470cb8448bc76453c6db47b76750219a318
fuzzie
fuzzie at fuzzie.org
Sat Jul 2 00:21:24 CEST 2011
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
d2035b34e0 MOHAWK: Allow creation of LBCode instances without an associated BCOD.
1b2b9e7604 MOHAWK: Add LBCode::parseCode.
aceb1470cb MOHAWK: Use LBCode instead of running scripts in LBItem.
Commit: d2035b34e0a6b829737e65e8cc382924b7d356fc
https://github.com/scummvm/scummvm/commit/d2035b34e0a6b829737e65e8cc382924b7d356fc
Author: Alyssa Milburn (fuzzie at fuzzie.org)
Date: 2011-07-01T15:11:44-07:00
Commit Message:
MOHAWK: Allow creation of LBCode instances without an associated BCOD.
Changed paths:
engines/mohawk/livingbooks_code.cpp
diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp
index d091f95..0aaedfe 100644
--- a/engines/mohawk/livingbooks_code.cpp
+++ b/engines/mohawk/livingbooks_code.cpp
@@ -127,6 +127,12 @@ Common::Rect LBValue::toRect() const {
}
LBCode::LBCode(MohawkEngine_LivingBooks *vm, uint16 baseId) : _vm(vm) {
+ if (!baseId) {
+ _data = new byte[0];
+ _size = 0;
+ return;
+ }
+
Common::SeekableSubReadStreamEndian *bcodStream = _vm->wrapStreamEndian(ID_BCOD, baseId);
uint32 totalSize = bcodStream->readUint32();
Commit: 1b2b9e7604122075a15cb54fe665a32d739b27b0
https://github.com/scummvm/scummvm/commit/1b2b9e7604122075a15cb54fe665a32d739b27b0
Author: Alyssa Milburn (fuzzie at fuzzie.org)
Date: 2011-07-01T15:16:55-07:00
Commit Message:
MOHAWK: Add LBCode::parseCode.
This allows script strings to be parsed into LB bytecode.
Changed paths:
engines/mohawk/livingbooks_code.cpp
engines/mohawk/livingbooks_code.h
diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp
index 0aaedfe..89a77fb 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 3e33549..85bb706 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> ¶ms);
void cmdGetRect(const Common::Array<LBValue> ¶ms);
Commit: aceb1470cb8448bc76453c6db47b76750219a318
https://github.com/scummvm/scummvm/commit/aceb1470cb8448bc76453c6db47b76750219a318
Author: Alyssa Milburn (fuzzie at fuzzie.org)
Date: 2011-07-01T15:18:26-07:00
Commit Message:
MOHAWK: Use LBCode instead of running scripts in LBItem.
Changed paths:
engines/mohawk/livingbooks.cpp
engines/mohawk/livingbooks.h
diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp
index 06500bc..d05c571 100644
--- a/engines/mohawk/livingbooks.cpp
+++ b/engines/mohawk/livingbooks.cpp
@@ -2884,257 +2884,24 @@ void LBItem::setNextTime(uint16 min, uint16 max, uint32 start) {
debug(9, "nextTime is now %d frames away", _nextTime - (uint)(_vm->_system->getMillis() / 16));
}
-enum LBTokenType {
- kLBNoToken,
- kLBNameToken,
- kLBStringToken,
- kLBOperatorToken,
- kLBIntegerToken,
- kLBEndToken
-};
-
-static Common::String readToken(const Common::String &source, uint &pos, LBTokenType &type) {
- Common::String token;
- type = kLBNoToken;
-
- bool done = false;
- while (pos < source.size() && !done) {
- if (type == kLBStringToken) {
- if (source[pos] == '"') {
- pos++;
- return token;
- }
-
- token += source[pos];
- pos++;
- continue;
- }
-
- switch (source[pos]) {
- case ' ':
- pos++;
- done = true;
- break;
-
- case ')':
- if (type == kLBNoToken) {
- type = kLBEndToken;
- return Common::String();
- }
- done = true;
- break;
-
- case ';':
- if (type == kLBNoToken) {
- pos++;
- type = kLBEndToken;
- return Common::String();
- }
- done = true;
- break;
-
- case '@':
- // FIXME
- error("found @ in string '%s', not supported yet", source.c_str());
-
- case '+':
- case '-':
- case '!':
- case '=':
- case '>':
- case '<':
- if (type == kLBNoToken)
- type = kLBOperatorToken;
- if (type == kLBOperatorToken)
- token += source[pos];
- else
- done = true;
- break;
-
- case '"':
- if (type == kLBNoToken)
- type = kLBStringToken;
- else
- done = true;
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (type == kLBNoToken)
- type = kLBIntegerToken;
- if (type == kLBNameToken || type == kLBIntegerToken)
- token += source[pos];
- else
- done = true;
- break;
-
- default:
- if (type == kLBNoToken)
- type = kLBNameToken;
- if (type == kLBNameToken)
- token += source[pos];
- else
- done = true;
- break;
- }
-
- if (!done)
- pos++;
- }
-
- if (type == kLBStringToken)
- error("readToken: ran out of input while parsing string from '%s'", source.c_str());
-
- if (!token.size()) {
- assert(type == kLBNoToken);
- type = kLBEndToken;
- }
-
- return token;
-}
-
-LBValue LBItem::parseValue(const Common::String &source, uint &pos) {
- LBTokenType type, postOpType;
- Common::String preOp, postOp;
-
- Common::String str = readToken(source, pos, type);
- if (type == kLBOperatorToken) {
- preOp = str;
- str = readToken(source, pos, type);
- }
-
- LBValue value;
- if (type == kLBStringToken) {
- value.type = kLBValueString;
- value.string = str;
- } else if (type == kLBIntegerToken) {
- value.type = kLBValueInteger;
- value.integer = atoi(str.c_str());
- } else if (type == kLBNameToken) {
- value = _vm->_variables[str];
- } else {
- error("expected string/integer as value in '%s', got '%s'", source.c_str(), str.c_str());
- }
-
- uint readAheadPos = pos;
- postOp = readToken(source, readAheadPos, postOpType);
- if (postOpType != kLBEndToken) {
- if (postOpType != kLBOperatorToken)
- error("expected operator after '%s' in '%s', got '%s'", str.c_str(), source.c_str(), postOp.c_str());
- // might be a comparison operator, caller will handle other cases if valid
- if (postOp == "-" || postOp == "+") {
- pos = readAheadPos;
- LBValue nextValue = parseValue(source, pos);
- if (value.type != kLBValueInteger || nextValue.type != kLBValueInteger)
- error("expected integer for arthmetic operator in '%s'", source.c_str());
- if (postOp == "+")
- value.integer += nextValue.integer;
- else if (postOp == "-")
- value.integer -= nextValue.integer;
- }
- }
-
- if (preOp.size()) {
- if (preOp == "!") {
- if (value.type == kLBValueInteger)
- value.integer = !value.integer;
- else
- error("expected integer after ! operator in '%s'", source.c_str());
- } else {
- error("expected valid operator before '%s' in '%s', got '%s'", str.c_str(), source.c_str(), preOp.c_str());
- }
- }
-
- return value;
-}
-
void LBItem::runCommand(const Common::String &command) {
- uint pos = 0;
- LBTokenType type;
+ LBCode tempCode(_vm, 0);
debug(2, "running command '%s'", command.c_str());
- while (pos < command.size()) {
- Common::String varname = readToken(command, pos, type);
- if (type != kLBNameToken)
- error("expected name as lvalue of command '%s', got '%s'", command.c_str(), varname.c_str());
- Common::String op = readToken(command, pos, type);
- if (type != kLBOperatorToken || (op != "=" && op != "++" && op != "--"))
- error("expected assignment/postincrement/postdecrement operator for command '%s', got '%s'", command.c_str(), op.c_str());
-
- if (op == "=") {
- LBValue value = parseValue(command, pos);
- _vm->_variables[varname] = value;
- } else {
- if (_vm->_variables[varname].type != kLBValueInteger)
- error("expected integer after postincrement/postdecrement operator in '%s'", command.c_str());
- if (op == "++")
- _vm->_variables[varname].integer++;
- else if (op == "--")
- _vm->_variables[varname].integer--;
- }
-
- if (pos < command.size() && command[pos] == ';')
- pos++;
- }
+ uint offset = tempCode.parseCode(command);
+ tempCode.runCode(this, offset);
}
bool LBItem::checkCondition(const Common::String &condition) {
- uint pos = 0;
- LBTokenType type;
+ LBCode tempCode(_vm, 0);
debug(3, "checking condition '%s'", condition.c_str());
- if (condition.size() <= pos || condition[pos] != '(')
- error("bad condition '%s' (started wrong)", condition.c_str());
- pos++;
-
- LBValue value1 = parseValue(condition, pos);
-
- Common::String op = readToken(condition, pos, type);
- if (type == kLBEndToken) {
- if (condition.size() != pos + 1 || condition[pos] != ')')
- error("bad condition '%s' (ended wrong)", condition.c_str());
-
- if (value1.type == kLBValueInteger)
- return value1.integer;
- else
- error("expected comparison operator for condition '%s'", condition.c_str());
- }
- if (type != kLBOperatorToken || (op != "!=" && op != "==" && op != ">" && op != "<" && op != ">=" && op != "<="))
- error("expected comparison operator for condition '%s', got '%s'", condition.c_str(), op.c_str());
-
- LBValue value2 = parseValue(condition, pos);
-
- if (condition.size() != pos + 1 || condition[pos] != ')')
- error("bad condition '%s' (ended wrong)", condition.c_str());
-
- if (op == "!=")
- return (value1 != value2);
- else if (op == "==")
- return (value1 == value2);
-
- if (value1.type != kLBValueInteger || value2.type != kLBValueInteger)
- error("evaluation operator %s in condition '%s' expected two integer operands!", op.c_str(), condition.c_str());
-
- if (op == ">")
- return (value1.integer > value2.integer);
- else if (op == ">=")
- return (value1.integer >= value2.integer);
- else if (op == "<")
- return (value1.integer < value2.integer);
- else if (op == "<=")
- return (value1.integer <= value2.integer);
+ uint offset = tempCode.parseCode(condition);
+ LBValue result = tempCode.runCode(this, offset);
- return false; // unreachable
+ return result.toInt();
}
LBSoundItem::LBSoundItem(MohawkEngine_LivingBooks *vm, LBPage *page, Common::Rect rect) : LBItem(vm, page, rect) {
diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h
index ed198a6..6cf3ee3 100644
--- a/engines/mohawk/livingbooks.h
+++ b/engines/mohawk/livingbooks.h
@@ -434,7 +434,6 @@ protected:
void runScript(uint event, uint16 data = 0, uint16 from = 0);
int runScriptEntry(LBScriptEntry *entry);
- LBValue parseValue(const Common::String &command, uint &pos);
void runCommand(const Common::String &command);
bool checkCondition(const Common::String &condition);
More information about the Scummvm-git-logs
mailing list