diff options
author | Eugene Sandulenko | 2019-12-23 22:54:47 +0100 |
---|---|---|
committer | Eugene Sandulenko | 2019-12-24 00:19:27 +0100 |
commit | b022b8ad76d074e675ba2f05e116cf61bfa539e3 (patch) | |
tree | 0be07d7d1b5578da8ef028e66a77209b3e53024c | |
parent | 6ff4e96d584da5ceba8f5f4498dbab02b380fc3b (diff) | |
download | scummvm-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.cpp | 333 | ||||
-rw-r--r-- | engines/director/lingo/lingo.cpp | 306 | ||||
-rw-r--r-- | engines/director/module.mk | 1 |
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: |