Initial commit
This commit is contained in:
506
engines/twp/yack.cpp
Normal file
506
engines/twp/yack.cpp
Normal file
@@ -0,0 +1,506 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twp/yack.h"
|
||||
#include "twp/detection.h"
|
||||
|
||||
namespace Twp {
|
||||
|
||||
Common::String YackToken::toString() const {
|
||||
switch (id) {
|
||||
case YackTokenId::Assign:
|
||||
return "Assign";
|
||||
case YackTokenId::NewLine:
|
||||
return "NewLine";
|
||||
case YackTokenId::Colon:
|
||||
return "Colon";
|
||||
case YackTokenId::Code:
|
||||
return "Code";
|
||||
case YackTokenId::Comment:
|
||||
return "Comment";
|
||||
case YackTokenId::End:
|
||||
return "End";
|
||||
case YackTokenId::Goto:
|
||||
return "Goto";
|
||||
case YackTokenId::Identifier:
|
||||
return "Identifier";
|
||||
case YackTokenId::None:
|
||||
return "None";
|
||||
case YackTokenId::Int:
|
||||
return "Integer";
|
||||
case YackTokenId::Float:
|
||||
return "Float";
|
||||
case YackTokenId::Condition:
|
||||
return "Condition";
|
||||
case YackTokenId::String:
|
||||
return "String";
|
||||
case YackTokenId::Whitespace:
|
||||
return "Whitespace";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
YackTokenReader::Iterator::Iterator(YackTokenReader &reader, int64 pos)
|
||||
: _reader(&reader),
|
||||
_pos(pos) {
|
||||
operator++();
|
||||
}
|
||||
|
||||
YackTokenReader::Iterator::Iterator(const Iterator &it)
|
||||
: _reader(it._reader), _pos(it._pos), _token(it._token) {
|
||||
}
|
||||
|
||||
YackTokenReader::Iterator &YackTokenReader::Iterator::operator++() {
|
||||
_reader->_stream->seek(_pos);
|
||||
_reader->readYackToken(_token);
|
||||
_pos = _reader->_stream->pos();
|
||||
return *this;
|
||||
}
|
||||
|
||||
YackTokenReader::Iterator YackTokenReader::Iterator::operator++(int) {
|
||||
Iterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
YackToken &YackTokenReader::Iterator::operator*() {
|
||||
return _token;
|
||||
}
|
||||
|
||||
const YackToken &YackTokenReader::Iterator::operator*() const {
|
||||
return _token;
|
||||
}
|
||||
|
||||
YackToken *YackTokenReader::Iterator::operator->() {
|
||||
return &_token;
|
||||
}
|
||||
|
||||
void YackTokenReader::open(Common::SeekableReadStream *stream) {
|
||||
_stream = stream;
|
||||
_line = 1;
|
||||
}
|
||||
|
||||
byte YackTokenReader::peek() {
|
||||
byte b = _stream->readByte();
|
||||
_stream->seek(-1, SEEK_CUR);
|
||||
return b;
|
||||
}
|
||||
|
||||
void YackTokenReader::ignore(int64 n, int delim) {
|
||||
int64 i = 0;
|
||||
while ((i < n) && (_stream->readByte() != delim)) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
YackTokenId YackTokenReader::readCode() {
|
||||
byte c;
|
||||
byte previousChar = 0;
|
||||
while ((c = peek()) != '\n' && c != '\0') {
|
||||
ignore();
|
||||
if (previousChar == ' ' && c == '[' && peek() != ' ') {
|
||||
_stream->seek(-1, SEEK_CUR);
|
||||
return YackTokenId::Code;
|
||||
}
|
||||
previousChar = c;
|
||||
}
|
||||
return YackTokenId::Code;
|
||||
}
|
||||
|
||||
YackTokenId YackTokenReader::readDollar() {
|
||||
char c;
|
||||
while ((c = peek()) != '[' && c != ' ' && c != '\n' && c != '\0') {
|
||||
ignore();
|
||||
}
|
||||
return YackTokenId::Dollar;
|
||||
}
|
||||
|
||||
YackTokenId YackTokenReader::readCondition() {
|
||||
while (peek() != ']') {
|
||||
ignore();
|
||||
}
|
||||
ignore();
|
||||
return YackTokenId::Condition;
|
||||
}
|
||||
|
||||
YackTokenId YackTokenReader::readNumber() {
|
||||
bool isFloat = false;
|
||||
while (Common::isDigit(peek())) {
|
||||
ignore();
|
||||
}
|
||||
if (peek() == '.') {
|
||||
ignore();
|
||||
isFloat = true;
|
||||
}
|
||||
while (Common::isDigit(peek())) {
|
||||
ignore();
|
||||
}
|
||||
return isFloat ? YackTokenId::Float : YackTokenId::Int;
|
||||
}
|
||||
|
||||
YackTokenId YackTokenReader::readComment() {
|
||||
ignore(INT_MAX, '\n');
|
||||
_stream->seek(_stream->pos() - 1);
|
||||
return YackTokenId::Comment;
|
||||
}
|
||||
|
||||
YackTokenId YackTokenReader::readString() {
|
||||
ignore(INT_MAX, '\"');
|
||||
return YackTokenId::String;
|
||||
}
|
||||
|
||||
YackTokenId YackTokenReader::readIdentifier(char c) {
|
||||
Common::String id;
|
||||
id += c;
|
||||
while (Common::isAlnum(peek()) || peek() == '_') {
|
||||
c = _stream->readByte();
|
||||
id += c;
|
||||
}
|
||||
if (id == "waitwhile") {
|
||||
readCode();
|
||||
return YackTokenId::WaitWhile;
|
||||
}
|
||||
return YackTokenId::Identifier;
|
||||
}
|
||||
|
||||
YackTokenId YackTokenReader::readYackTokenId() {
|
||||
char c;
|
||||
_stream->read(&c, 1);
|
||||
if (_stream->eos()) {
|
||||
return YackTokenId::End;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '\0':
|
||||
return YackTokenId::End;
|
||||
case '\n':
|
||||
_line++;
|
||||
return YackTokenId::NewLine;
|
||||
case '\t':
|
||||
case ' ':
|
||||
while (Common::isSpace(peek()) && peek() != '\n')
|
||||
ignore();
|
||||
return YackTokenId::Whitespace;
|
||||
case '!':
|
||||
return readCode();
|
||||
case ':':
|
||||
return YackTokenId::Colon;
|
||||
case '$':
|
||||
return readDollar();
|
||||
case '[':
|
||||
return readCondition();
|
||||
case '=':
|
||||
return YackTokenId::Assign;
|
||||
case '\"':
|
||||
return readString();
|
||||
case '#':
|
||||
case ';':
|
||||
return readComment();
|
||||
default:
|
||||
if (c == '-' && peek() == '>') {
|
||||
ignore();
|
||||
return YackTokenId::Goto;
|
||||
}
|
||||
if (c == '-' || Common::isDigit(c)) {
|
||||
return readNumber();
|
||||
} else if (Common::isAlpha(c)) {
|
||||
return readIdentifier(c);
|
||||
}
|
||||
debugC(kDebugDialog, "unknown character: %c", c);
|
||||
return YackTokenId::None;
|
||||
}
|
||||
}
|
||||
|
||||
bool YackTokenReader::readYackToken(YackToken &YackToken) {
|
||||
int64 start = _stream->pos();
|
||||
int line = _line;
|
||||
auto id = readYackTokenId();
|
||||
while (id == YackTokenId::Whitespace || id == YackTokenId::Comment || id == YackTokenId::NewLine || id == YackTokenId::None) {
|
||||
start = _stream->pos();
|
||||
line = _line;
|
||||
id = readYackTokenId();
|
||||
}
|
||||
int64 end = _stream->pos();
|
||||
YackToken.id = id;
|
||||
YackToken.start = start;
|
||||
YackToken.end = end;
|
||||
YackToken.line = line;
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::String YackTokenReader::readText(int64 pos, int64 size) {
|
||||
Common::String out;
|
||||
_stream->seek(pos);
|
||||
char c;
|
||||
for (int i = 0; i < size; i++) {
|
||||
c = _stream->readByte();
|
||||
out += c;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Common::String YackTokenReader::readText(const YackToken &YackToken) {
|
||||
return readText(YackToken.start, YackToken.end - YackToken.start);
|
||||
}
|
||||
|
||||
YackTokenReader::iterator YackTokenReader::begin() {
|
||||
return Iterator(*this, 0);
|
||||
}
|
||||
|
||||
YackTokenReader::iterator YackTokenReader::end() {
|
||||
int64 pos = _stream->size();
|
||||
return Iterator(*this, pos);
|
||||
}
|
||||
|
||||
bool YackParser::match(const std::initializer_list<YackTokenId> &ids) {
|
||||
auto it = _it;
|
||||
for (auto id : ids) {
|
||||
if (it->id != id)
|
||||
return false;
|
||||
it++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void YCodeCond::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YOnce::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YShowOnce::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YOnceEver::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YTempOnce::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YGoto::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YChoice::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YSay::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YPause::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YStatement::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YWaitWhile::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YAllowObjects::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YLimit::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YDialog::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YLabel::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YParrot::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YShutup::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YCodeExp::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YWaitFor::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YOverride::accept(YackVisitor &v) { v.visit(*this); }
|
||||
void YCompilationUnit::accept(YackVisitor &v) { v.visit(*this); }
|
||||
|
||||
YLabel::YLabel(int line) { _line = line; }
|
||||
|
||||
YLabel::~YLabel() {
|
||||
}
|
||||
|
||||
YCompilationUnit::~YCompilationUnit() {
|
||||
}
|
||||
|
||||
Common::SharedPtr<YLabel> YackParser::parseLabel() {
|
||||
Common::SharedPtr<YLabel> pLabel;
|
||||
// :
|
||||
_it++;
|
||||
// label
|
||||
pLabel.reset(new YLabel(_it->line));
|
||||
pLabel->_name = _reader.readText(*_it++);
|
||||
// debugC(kDebugDialog, "label %s", pLabel->_name.c_str());
|
||||
do {
|
||||
if (match({YackTokenId::Colon}) || match({YackTokenId::End}))
|
||||
break;
|
||||
Common::SharedPtr<YStatement> pStatement = parseStatement();
|
||||
pLabel->_stmts.push_back(pStatement);
|
||||
} while (true);
|
||||
|
||||
return pLabel;
|
||||
}
|
||||
|
||||
Common::SharedPtr<YStatement> YackParser::parseStatement() {
|
||||
Common::SharedPtr<YStatement> pStatement(new YStatement());
|
||||
|
||||
// expression
|
||||
pStatement->_exp.reset(parseExpression());
|
||||
// conditions
|
||||
while (match({YackTokenId::Condition})) {
|
||||
pStatement->_conds.push_back(parseCondition());
|
||||
}
|
||||
return pStatement;
|
||||
}
|
||||
|
||||
Common::SharedPtr<YCond> YackParser::parseCondition() {
|
||||
auto text = _reader.readText(*_it);
|
||||
auto conditionText = text.substr(1, text.size() - 2);
|
||||
auto line = _it->line;
|
||||
_it++;
|
||||
if (conditionText == "once") {
|
||||
return Common::SharedPtr<YOnce>(new YOnce(line));
|
||||
} else if (conditionText == "showonce") {
|
||||
return Common::SharedPtr<YShowOnce>(new YShowOnce(line));
|
||||
} else if (conditionText == "onceever") {
|
||||
return Common::SharedPtr<YOnceEver>(new YOnceEver(line));
|
||||
} else if (conditionText == "temponce") {
|
||||
return Common::SharedPtr<YTempOnce>(new YTempOnce(line));
|
||||
}
|
||||
Common::SharedPtr<YCodeCond> pCondition(new YCodeCond(line));
|
||||
pCondition->_code = Common::move(conditionText);
|
||||
return pCondition;
|
||||
}
|
||||
|
||||
Common::SharedPtr<YExp> YackParser::parseExpression() {
|
||||
if (match({YackTokenId::Identifier, YackTokenId::Colon, YackTokenId::String}))
|
||||
return parseSayExpression();
|
||||
if (match({YackTokenId::WaitWhile}))
|
||||
return parseWaitWhileExpression();
|
||||
if (match({YackTokenId::Identifier}))
|
||||
return parseInstructionExpression();
|
||||
if (match({YackTokenId::Goto}))
|
||||
return parseGotoExpression();
|
||||
if (match({YackTokenId::Int}))
|
||||
return parseChoiceExpression();
|
||||
if (match({YackTokenId::Code}))
|
||||
return parseCodeExpression();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::SharedPtr<YSay> YackParser::parseSayExpression() {
|
||||
auto actor = _reader.readText(*_it++);
|
||||
_it++;
|
||||
auto text = _reader.readText(*_it);
|
||||
_it++;
|
||||
Common::SharedPtr<YSay> pExp(new YSay());
|
||||
pExp->_actor = Common::move(actor);
|
||||
pExp->_text = text.substr(1, text.size() - 2);
|
||||
return pExp;
|
||||
}
|
||||
|
||||
Common::SharedPtr<YExp> YackParser::parseWaitWhileExpression() {
|
||||
auto waitwhile = _reader.readText(*_it++);
|
||||
auto code = waitwhile.substr(10);
|
||||
Common::SharedPtr<YWaitWhile> pExp(new YWaitWhile());
|
||||
pExp->_cond = Common::move(code);
|
||||
return pExp;
|
||||
}
|
||||
|
||||
Common::SharedPtr<YExp> YackParser::parseInstructionExpression() {
|
||||
auto identifier = _reader.readText(*_it++);
|
||||
if (identifier == "shutup") {
|
||||
return Common::SharedPtr<YShutup>(new YShutup());
|
||||
} else if (identifier == "pause") {
|
||||
// pause number
|
||||
auto time = atof(_reader.readText(*_it++).c_str());
|
||||
Common::SharedPtr<YPause> pExp(new YPause());
|
||||
pExp->_time = time;
|
||||
return pExp;
|
||||
} else if (identifier == "waitfor") {
|
||||
// waitfor [actor]
|
||||
Common::SharedPtr<YWaitFor> pExp(new YWaitFor());
|
||||
if (_it->id == YackTokenId::Identifier) {
|
||||
auto actor = _reader.readText(*_it++);
|
||||
pExp->_actor = Common::move(actor);
|
||||
}
|
||||
return pExp;
|
||||
} else if (identifier == "parrot") {
|
||||
// parrot [active]
|
||||
Common::SharedPtr<YParrot> pExp(new YParrot());
|
||||
if (_it->id == YackTokenId::Identifier) {
|
||||
auto active = _reader.readText(*_it++);
|
||||
pExp->_active = active == "yes";
|
||||
}
|
||||
return pExp;
|
||||
} else if (identifier == "dialog") {
|
||||
// dialog [actor]
|
||||
Common::SharedPtr<YDialog> pExp(new YDialog());
|
||||
if (_it->id == YackTokenId::Identifier) {
|
||||
auto actor = _reader.readText(*_it++);
|
||||
pExp->_actor = Common::move(actor);
|
||||
}
|
||||
return pExp;
|
||||
} else if (identifier == "override") {
|
||||
// override [node]
|
||||
Common::SharedPtr<YOverride> pExp(new YOverride());
|
||||
if (_it->id == YackTokenId::Identifier) {
|
||||
auto node = _reader.readText(*_it++);
|
||||
pExp->_node = Common::move(node);
|
||||
}
|
||||
return pExp;
|
||||
} else if (identifier == "allowobjects") {
|
||||
// allowobjects [allow]
|
||||
Common::SharedPtr<YAllowObjects> pExp(new YAllowObjects());
|
||||
if (_it->id == YackTokenId::Identifier) {
|
||||
auto node = _reader.readText(*_it++);
|
||||
pExp->_active = node == "YES";
|
||||
}
|
||||
return pExp;
|
||||
} else if (identifier == "limit") {
|
||||
// limit [number]
|
||||
Common::SharedPtr<YLimit> pExp(new YLimit());
|
||||
if (_it->id == YackTokenId::Int) {
|
||||
auto node = _reader.readText(*_it++);
|
||||
pExp->_max = strtol(node.c_str(), nullptr, 10);
|
||||
}
|
||||
return pExp;
|
||||
}
|
||||
error("Unknown instruction: %s", identifier.c_str());
|
||||
}
|
||||
|
||||
Common::SharedPtr<YGoto> YackParser::parseGotoExpression() {
|
||||
_it++;
|
||||
int line = _it->line;
|
||||
auto name = _reader.readText(*_it++);
|
||||
Common::SharedPtr<YGoto> pExp(new YGoto(line));
|
||||
pExp->_name = Common::move(name);
|
||||
return pExp;
|
||||
}
|
||||
|
||||
Common::SharedPtr<YCodeExp> YackParser::parseCodeExpression() {
|
||||
auto code = _reader.readText(*_it++);
|
||||
Common::SharedPtr<YCodeExp> pExp(new YCodeExp());
|
||||
pExp->_code = code.substr(1);
|
||||
return pExp;
|
||||
}
|
||||
|
||||
Common::SharedPtr<YChoice> YackParser::parseChoiceExpression() {
|
||||
auto number = atol(_reader.readText(*_it).c_str());
|
||||
_it++;
|
||||
Common::String text;
|
||||
if (_it->id == YackTokenId::Dollar) {
|
||||
text = _reader.readText(*_it);
|
||||
} else {
|
||||
text = _reader.readText(*_it);
|
||||
text = text.substr(1, text.size() - 2);
|
||||
}
|
||||
|
||||
_it++;
|
||||
Common::SharedPtr<YChoice> pExp(new YChoice());
|
||||
pExp->_number = number;
|
||||
pExp->_text = Common::move(text);
|
||||
pExp->_goto = parseGotoExpression();
|
||||
return pExp;
|
||||
}
|
||||
|
||||
YCompilationUnit *YackParser::parse(Common::SeekableReadStream *stream) {
|
||||
_reader.open(stream);
|
||||
_it = _reader.begin();
|
||||
auto pCu = unique_ptr<YCompilationUnit>();
|
||||
pCu.reset(new YCompilationUnit());
|
||||
while (!match({YackTokenId::End})) {
|
||||
pCu->_labels.push_back(parseLabel());
|
||||
}
|
||||
return pCu.release();
|
||||
}
|
||||
|
||||
} // namespace Twp
|
||||
Reference in New Issue
Block a user