aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2019-12-23 22:54:47 +0100
committerEugene Sandulenko2019-12-24 00:19:27 +0100
commitb022b8ad76d074e675ba2f05e116cf61bfa539e3 (patch)
tree0be07d7d1b5578da8ef028e66a77209b3e53024c
parent6ff4e96d584da5ceba8f5f4498dbab02b380fc3b (diff)
downloadscummvm-rg350-b022b8ad76d074e675ba2f05e116cf61bfa539e3.tar.gz
scummvm-rg350-b022b8ad76d074e675ba2f05e116cf61bfa539e3.tar.bz2
scummvm-rg350-b022b8ad76d074e675ba2f05e116cf61bfa539e3.zip
DIRECTOR: Split out Lingo preprocessor code into a separate file
-rw-r--r--engines/director/lingo/lingo-preprocessor.cpp333
-rw-r--r--engines/director/lingo/lingo.cpp306
-rw-r--r--engines/director/module.mk1
3 files changed, 334 insertions, 306 deletions
diff --git a/engines/director/lingo/lingo-preprocessor.cpp b/engines/director/lingo/lingo-preprocessor.cpp
new file mode 100644
index 0000000000..c7cc491b3d
--- /dev/null
+++ b/engines/director/lingo/lingo-preprocessor.cpp
@@ -0,0 +1,333 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "director/lingo/lingo.h"
+
+namespace Director {
+
+bool isspecial(char c) {
+ return strchr("-+*/%%^:,()><&[]", c) != NULL;
+}
+
+static Common::String nexttok(const char *s, const char **newP = nullptr) {
+ Common::String res;
+
+ // Scan first non-whitespace
+ while (*s && (*s == ' ' || *s == '\t')) // If we see a whitespace
+ s++;
+
+ if (Common::isAlnum(*s)) {
+ // Now copy everything till whitespace
+ while (*s && (Common::isAlnum(*s) || *s == '.'))
+ res += *s++;
+ } else {
+ while (*s && isspecial(*s))
+ res += *s++;
+ }
+
+ if (newP)
+ *newP = s;
+
+ return res;
+}
+
+static Common::String prevtok(const char *s, const char *lineStart, const char **newP = nullptr) {
+ Common::String res;
+
+ // Scan first non-whitespace
+ while (s >= lineStart && (*s == ' ' || *s == '\t')) // If we see a whitespace
+ s--;
+
+ // Now copy everything till whitespace
+ while (s >= lineStart && *s != ' ' && *s != '\t')
+ res = *s-- + res;
+
+ if (newP)
+ *newP = s;
+
+ return res;
+}
+
+Common::String Lingo::codePreprocessor(const char *s, bool simple) {
+ Common::String res;
+
+ // Strip comments
+ while (*s) {
+ if (*s == '-' && *(s + 1) == '-') { // At the end of the line we will have \0
+ while (*s && *s != '\n')
+ s++;
+ }
+
+ if (*s == '\r')
+ res += '\n';
+ else if (*s)
+ res += *s;
+
+ s++;
+ }
+
+ Common::String tmp(res);
+ res.clear();
+
+ // Strip trailing whitespaces
+ s = tmp.c_str();
+ while (*s) {
+ if (*s == ' ' || *s == '\t') { // If we see a whitespace
+ const char *ps = s; // Remember where we saw it
+
+ while (*ps == ' ' || *ps == '\t') // Scan until end of whitespaces
+ ps++;
+
+ if (*ps) { // Not end of the string
+ if (*ps == '\n') { // If it is newline, then we continue from it
+ s = ps;
+ } else { // It is not a newline
+ while (s != ps) { // Add all whitespaces
+ res += *s;
+ s++;
+ }
+ }
+ }
+ }
+
+ if (*s)
+ res += *s;
+
+ s++;
+ }
+
+ if (simple)
+ return res;
+
+ tmp = res;
+ s = tmp.c_str();
+ res.clear();
+
+ // Preprocess if statements
+ // Here we add ' end if' at end of each statement, which lets us
+ // make the grammar very straightforward
+ Common::String line, tok, res1;
+ const char *lineStart, *prevEnd;
+ int iflevel = 0;
+
+ while (*s) {
+ line.clear();
+ res1.clear();
+
+ // Get next line
+ while (*s && *s != '\n') { // If we see a whitespace
+ if (*s == '\xc2') {
+ res1 += *s++;
+ if (*s == '\n') {
+ line += ' ';
+ res1 += *s++;
+ }
+ } else {
+ res1 += *s;
+ line += tolower(*s++);
+ }
+ }
+ debugC(2, kDebugLingoParse, "line: %d '%s'", iflevel, line.c_str());
+
+ res1 = preprocessReturn(res1);
+
+ res += res1;
+
+ if (line.size() < 4) { // If line is too small, then skip it
+ if (*s) // copy newline symbol
+ res += *s++;
+
+ debugC(2, kDebugLingoParse, "too small");
+
+ continue;
+ }
+
+ tok = nexttok(line.c_str(), &lineStart);
+ if (tok.equals("if")) {
+ tok = prevtok(&line.c_str()[line.size() - 1], lineStart, &prevEnd);
+ debugC(2, kDebugLingoParse, "start-if <%s>", tok.c_str());
+
+ if (tok.equals("if")) {
+ debugC(2, kDebugLingoParse, "end-if");
+ tok = prevtok(prevEnd, lineStart);
+
+ if (tok.equals("end")) {
+ // do nothing, we open and close same line
+ debugC(2, kDebugLingoParse, "end-end");
+ } else {
+ iflevel++;
+ }
+ } else if (tok.equals("then")) {
+ debugC(2, kDebugLingoParse, "last-then");
+ iflevel++;
+ } else if (tok.equals("else")) {
+ debugC(2, kDebugLingoParse, "last-else");
+ iflevel++;
+ } else { // other token
+ // Now check if we have tNLELSE
+ if (!*s) {
+ iflevel++; // end, we have to add 'end if'
+ break;
+ }
+ const char *s1 = s + 1;
+
+ while (*s1 && *s1 == '\n')
+ s1++;
+ tok = nexttok(s1);
+
+ if (tok.equalsIgnoreCase("else")) { // ignore case because it is look-ahead
+ debugC(2, kDebugLingoParse, "tNLELSE");
+ iflevel++;
+ } else {
+ debugC(2, kDebugLingoParse, "++++ end if (no nlelse after single liner)");
+ res += " end if";
+ }
+ }
+ } else if (tok.equals("else")) {
+ debugC(2, kDebugLingoParse, "start-else");
+ bool elseif = false;
+
+ tok = nexttok(lineStart);
+ if (tok.equals("if")) {
+ debugC(2, kDebugLingoParse, "second-if");
+ elseif = true;
+ } else if (tok.empty()) {
+ debugC(2, kDebugLingoParse, "lonely-else");
+ continue;
+ }
+
+ tok = prevtok(&line.c_str()[line.size() - 1], lineStart, &prevEnd);
+ debugC(2, kDebugLingoParse, "last: '%s'", tok.c_str());
+
+ if (tok.equals("if")) {
+ debugC(2, kDebugLingoParse, "end-if");
+ tok = prevtok(prevEnd, lineStart);
+
+ if (tok.equals("end")) {
+ debugC(2, kDebugLingoParse, "end-end");
+ iflevel--;
+ }
+ } else if (tok.equals("then")) {
+ debugC(2, kDebugLingoParse, "last-then");
+
+ if (elseif == false) {
+ warning("Badly nested then");
+ }
+ } else if (tok.equals("else")) {
+ debugC(2, kDebugLingoParse, "last-else");
+ if (elseif == false) {
+ warning("Badly nested else");
+ }
+ } else { // check if we have tNLELSE
+ if (!*s) {
+ break;
+ }
+ const char *s1 = s + 1;
+
+ while (*s1 && *s1 == '\n')
+ s1++;
+ tok = nexttok(s1);
+
+ if (tok.equalsIgnoreCase("else") && elseif) {
+ // Nothing to do here, same level
+ debugC(2, kDebugLingoParse, "tNLELSE");
+ } else {
+ debugC(2, kDebugLingoParse, "++++ end if (no tNLELSE)");
+ res += " end if";
+ iflevel--;
+ }
+ }
+ } else if (tok.equals("end")) {
+ debugC(2, kDebugLingoParse, "start-end");
+
+ tok = nexttok(lineStart);
+ if (tok.equals("if")) {
+ debugC(2, kDebugLingoParse, "second-if");
+ iflevel--;
+ }
+ }
+ }
+
+ for (int i = 0; i < iflevel; i++) {
+ debugC(2, kDebugLingoParse, "++++ end if (unclosed)");
+ res += "\nend if";
+ }
+
+
+ debugC(2, kDebugLingoParse, "#############\n%s\n#############", res.c_str());
+
+ return res;
+}
+
+#ifndef strcasestr
+const char *strcasestr(const char *s, const char *find) {
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != 0) {
+ c = (char)tolower((unsigned char)c);
+ len = strlen(find);
+ do {
+ do {
+ if ((sc = *s++) == 0)
+ return (NULL);
+ } while ((char)tolower((unsigned char)sc) != c);
+ } while (scumm_strnicmp(s, find, len) != 0);
+ s--;
+ }
+ return s;
+}
+#endif
+
+Common::String Lingo::preprocessReturn(Common::String in) {
+ Common::String res, prev, next;
+ const char *ptr = in.c_str();
+ const char *beg = ptr;
+
+ while ((ptr = strcasestr(beg, "return")) != NULL) {
+ res += Common::String(beg, ptr);
+
+ if (ptr == beg)
+ prev = "";
+ else
+ prev = prevtok(ptr - 1, beg);
+
+ next = nexttok(ptr + 6); // end of 'return'
+
+ if (prev.equals("&") || prev.equals("&&") || prev.equals("=") ||
+ next.equals("&") || next.equals("&&")) {
+ res += "scummvm_"; // Turn it into scummvm_return
+ }
+
+ res += *ptr++; // We advance one character, so 'eturn' is left
+ beg = ptr;
+ }
+
+ res += Common::String(beg);
+
+ if (in.size() != res.size())
+ debugC(2, kDebugLingoParse, "RETURN: in: %s\nout: %s", in.c_str(), res.c_str());
+
+ return res;
+}
+
+} // End of namespace Director
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 02c7060e5b..e233328160 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -217,312 +217,6 @@ void Lingo::addCode(const char *code, ScriptType type, uint16 id) {
}
}
-bool isspecial(char c) {
- return strchr("-+*/%%^:,()><&[]", c) != NULL;
-}
-
-static Common::String nexttok(const char *s, const char **newP = nullptr) {
- Common::String res;
-
- // Scan first non-whitespace
- while (*s && (*s == ' ' || *s == '\t')) // If we see a whitespace
- s++;
-
- if (Common::isAlnum(*s)) {
- // Now copy everything till whitespace
- while (*s && (Common::isAlnum(*s) || *s == '.'))
- res += *s++;
- } else {
- while (*s && isspecial(*s))
- res += *s++;
- }
-
- if (newP)
- *newP = s;
-
- return res;
-}
-
-static Common::String prevtok(const char *s, const char *lineStart, const char **newP = nullptr) {
- Common::String res;
-
- // Scan first non-whitespace
- while (s >= lineStart && (*s == ' ' || *s == '\t')) // If we see a whitespace
- s--;
-
- // Now copy everything till whitespace
- while (s >= lineStart && *s != ' ' && *s != '\t')
- res = *s-- + res;
-
- if (newP)
- *newP = s;
-
- return res;
-}
-
-Common::String Lingo::codePreprocessor(const char *s, bool simple) {
- Common::String res;
-
- // Strip comments
- while (*s) {
- if (*s == '-' && *(s + 1) == '-') { // At the end of the line we will have \0
- while (*s && *s != '\n')
- s++;
- }
-
- if (*s == '\r')
- res += '\n';
- else if (*s)
- res += *s;
-
- s++;
- }
-
- Common::String tmp(res);
- res.clear();
-
- // Strip trailing whitespaces
- s = tmp.c_str();
- while (*s) {
- if (*s == ' ' || *s == '\t') { // If we see a whitespace
- const char *ps = s; // Remember where we saw it
-
- while (*ps == ' ' || *ps == '\t') // Scan until end of whitespaces
- ps++;
-
- if (*ps) { // Not end of the string
- if (*ps == '\n') { // If it is newline, then we continue from it
- s = ps;
- } else { // It is not a newline
- while (s != ps) { // Add all whitespaces
- res += *s;
- s++;
- }
- }
- }
- }
-
- if (*s)
- res += *s;
-
- s++;
- }
-
- if (simple)
- return res;
-
- tmp = res;
- s = tmp.c_str();
- res.clear();
-
- // Preprocess if statements
- // Here we add ' end if' at end of each statement, which lets us
- // make the grammar very straightforward
- Common::String line, tok, res1;
- const char *lineStart, *prevEnd;
- int iflevel = 0;
-
- while (*s) {
- line.clear();
- res1.clear();
-
- // Get next line
- while (*s && *s != '\n') { // If we see a whitespace
- if (*s == '\xc2') {
- res1 += *s++;
- if (*s == '\n') {
- line += ' ';
- res1 += *s++;
- }
- } else {
- res1 += *s;
- line += tolower(*s++);
- }
- }
- debugC(2, kDebugLingoParse, "line: %d '%s'", iflevel, line.c_str());
-
- res1 = preprocessReturn(res1);
-
- res += res1;
-
- if (line.size() < 4) { // If line is too small, then skip it
- if (*s) // copy newline symbol
- res += *s++;
-
- debugC(2, kDebugLingoParse, "too small");
-
- continue;
- }
-
- tok = nexttok(line.c_str(), &lineStart);
- if (tok.equals("if")) {
- tok = prevtok(&line.c_str()[line.size() - 1], lineStart, &prevEnd);
- debugC(2, kDebugLingoParse, "start-if <%s>", tok.c_str());
-
- if (tok.equals("if")) {
- debugC(2, kDebugLingoParse, "end-if");
- tok = prevtok(prevEnd, lineStart);
-
- if (tok.equals("end")) {
- // do nothing, we open and close same line
- debugC(2, kDebugLingoParse, "end-end");
- } else {
- iflevel++;
- }
- } else if (tok.equals("then")) {
- debugC(2, kDebugLingoParse, "last-then");
- iflevel++;
- } else if (tok.equals("else")) {
- debugC(2, kDebugLingoParse, "last-else");
- iflevel++;
- } else { // other token
- // Now check if we have tNLELSE
- if (!*s) {
- iflevel++; // end, we have to add 'end if'
- break;
- }
- const char *s1 = s + 1;
-
- while (*s1 && *s1 == '\n')
- s1++;
- tok = nexttok(s1);
-
- if (tok.equalsIgnoreCase("else")) { // ignore case because it is look-ahead
- debugC(2, kDebugLingoParse, "tNLELSE");
- iflevel++;
- } else {
- debugC(2, kDebugLingoParse, "++++ end if (no nlelse after single liner)");
- res += " end if";
- }
- }
- } else if (tok.equals("else")) {
- debugC(2, kDebugLingoParse, "start-else");
- bool elseif = false;
-
- tok = nexttok(lineStart);
- if (tok.equals("if")) {
- debugC(2, kDebugLingoParse, "second-if");
- elseif = true;
- } else if (tok.empty()) {
- debugC(2, kDebugLingoParse, "lonely-else");
- continue;
- }
-
- tok = prevtok(&line.c_str()[line.size() - 1], lineStart, &prevEnd);
- debugC(2, kDebugLingoParse, "last: '%s'", tok.c_str());
-
- if (tok.equals("if")) {
- debugC(2, kDebugLingoParse, "end-if");
- tok = prevtok(prevEnd, lineStart);
-
- if (tok.equals("end")) {
- debugC(2, kDebugLingoParse, "end-end");
- iflevel--;
- }
- } else if (tok.equals("then")) {
- debugC(2, kDebugLingoParse, "last-then");
-
- if (elseif == false) {
- warning("Badly nested then");
- }
- } else if (tok.equals("else")) {
- debugC(2, kDebugLingoParse, "last-else");
- if (elseif == false) {
- warning("Badly nested else");
- }
- } else { // check if we have tNLELSE
- if (!*s) {
- break;
- }
- const char *s1 = s + 1;
-
- while (*s1 && *s1 == '\n')
- s1++;
- tok = nexttok(s1);
-
- if (tok.equalsIgnoreCase("else") && elseif) {
- // Nothing to do here, same level
- debugC(2, kDebugLingoParse, "tNLELSE");
- } else {
- debugC(2, kDebugLingoParse, "++++ end if (no tNLELSE)");
- res += " end if";
- iflevel--;
- }
- }
- } else if (tok.equals("end")) {
- debugC(2, kDebugLingoParse, "start-end");
-
- tok = nexttok(lineStart);
- if (tok.equals("if")) {
- debugC(2, kDebugLingoParse, "second-if");
- iflevel--;
- }
- }
- }
-
- for (int i = 0; i < iflevel; i++) {
- debugC(2, kDebugLingoParse, "++++ end if (unclosed)");
- res += "\nend if";
- }
-
-
- debugC(2, kDebugLingoParse, "#############\n%s\n#############", res.c_str());
-
- return res;
-}
-
-#ifndef strcasestr
-const char *strcasestr(const char *s, const char *find) {
- char c, sc;
- size_t len;
-
- if ((c = *find++) != 0) {
- c = (char)tolower((unsigned char)c);
- len = strlen(find);
- do {
- do {
- if ((sc = *s++) == 0)
- return (NULL);
- } while ((char)tolower((unsigned char)sc) != c);
- } while (scumm_strnicmp(s, find, len) != 0);
- s--;
- }
- return s;
-}
-#endif
-
-Common::String Lingo::preprocessReturn(Common::String in) {
- Common::String res, prev, next;
- const char *ptr = in.c_str();
- const char *beg = ptr;
-
- while ((ptr = strcasestr(beg, "return")) != NULL) {
- res += Common::String(beg, ptr);
-
- if (ptr == beg)
- prev = "";
- else
- prev = prevtok(ptr - 1, beg);
-
- next = nexttok(ptr + 6); // end of 'return'
-
- if (prev.equals("&") || prev.equals("&&") || prev.equals("=") ||
- next.equals("&") || next.equals("&&")) {
- res += "scummvm_"; // Turn it into scummvm_return
- }
-
- res += *ptr++; // We advance one character, so 'eturn' is left
- beg = ptr;
- }
-
- res += Common::String(beg);
-
- if (in.size() != res.size())
- debugC(2, kDebugLingoParse, "RETURN: in: %s\nout: %s", in.c_str(), res.c_str());
-
- return res;
-}
-
void Lingo::executeScript(ScriptType type, uint16 id, uint16 function) {
if (!_scriptContexts[type].contains(id)) {
debugC(3, kDebugLingoExec, "Request to execute non-existant script type %d id %d", type, id);
diff --git a/engines/director/module.mk b/engines/director/module.mk
index e445e576f4..06ceae0c1d 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -26,6 +26,7 @@ MODULE_OBJS = \
lingo/lingo-events.o \
lingo/lingo-funcs.o \
lingo/lingo-lex.o \
+ lingo/lingo-preprocessor.o \
lingo/lingo-the.o
director-grammar: