[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> &params);
 	void cmdGetRect(const Common::Array<LBValue> &params);


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