Initial commit
This commit is contained in:
274
engines/director/lingo/lingodec/ast.cpp
Normal file
274
engines/director/lingo/lingodec/ast.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "common/util.h"
|
||||
#include "./ast.h"
|
||||
#include "./handler.h"
|
||||
#include "./names.h"
|
||||
#include "./script.h"
|
||||
|
||||
namespace LingoDec {
|
||||
|
||||
/* Datum */
|
||||
|
||||
int Datum::toInt() {
|
||||
switch (type) {
|
||||
case kDatumInt:
|
||||
return i;
|
||||
case kDatumFloat:
|
||||
return f;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* AST */
|
||||
|
||||
void AST::addStatement(Common::SharedPtr<Node> statement) {
|
||||
currentBlock->addChild(Common::move(statement));
|
||||
}
|
||||
|
||||
void AST::enterBlock(BlockNode *block) {
|
||||
currentBlock = block;
|
||||
}
|
||||
|
||||
void AST::exitBlock() {
|
||||
auto ancestorStatement = currentBlock->ancestorStatement();
|
||||
if (!ancestorStatement) {
|
||||
currentBlock = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
ancestorStatement->_endOffset = currentBlock->_endOffset;
|
||||
|
||||
auto block = ancestorStatement->parent;
|
||||
if (!block || block->type != kBlockNode) {
|
||||
currentBlock = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
currentBlock = static_cast<BlockNode *>(block);
|
||||
}
|
||||
|
||||
/* Node */
|
||||
|
||||
Common::SharedPtr<Datum> Node::getValue() {
|
||||
return Common::SharedPtr<Datum>(new Datum());
|
||||
}
|
||||
|
||||
Node *Node::ancestorStatement() {
|
||||
Node *ancestor = parent;
|
||||
while (ancestor && !ancestor->isStatement) {
|
||||
ancestor = ancestor->parent;
|
||||
}
|
||||
return ancestor;
|
||||
}
|
||||
|
||||
LoopNode *Node::ancestorLoop() {
|
||||
Node *ancestor = parent;
|
||||
while (ancestor && !ancestor->isLoop) {
|
||||
ancestor = ancestor->parent;
|
||||
}
|
||||
return static_cast<LoopNode *>(ancestor);
|
||||
}
|
||||
|
||||
bool Node::hasSpaces(bool) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ErrorNode */
|
||||
|
||||
bool ErrorNode::hasSpaces(bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* LiteralNode */
|
||||
|
||||
Common::SharedPtr<Datum> LiteralNode::getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
bool LiteralNode::hasSpaces(bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* BlockNode */
|
||||
|
||||
void BlockNode::addChild(Common::SharedPtr<Node> child) {
|
||||
child->parent = this;
|
||||
children.push_back(Common::move(child));
|
||||
}
|
||||
|
||||
/* BinaryOpNode */
|
||||
|
||||
unsigned int BinaryOpNode::getPrecedence() const {
|
||||
switch (opcode) {
|
||||
case kOpMul:
|
||||
case kOpDiv:
|
||||
case kOpMod:
|
||||
return 1;
|
||||
case kOpAdd:
|
||||
case kOpSub:
|
||||
return 2;
|
||||
case kOpLt:
|
||||
case kOpLtEq:
|
||||
case kOpNtEq:
|
||||
case kOpEq:
|
||||
case kOpGt:
|
||||
case kOpGtEq:
|
||||
return 3;
|
||||
case kOpAnd:
|
||||
return 4;
|
||||
case kOpOr:
|
||||
return 5;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* MemberExprNode */
|
||||
|
||||
bool MemberExprNode::hasSpaces(bool dot) {
|
||||
return !dot;
|
||||
}
|
||||
|
||||
/* VarNode */
|
||||
|
||||
bool VarNode::hasSpaces(bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* CaseStmtNode */
|
||||
|
||||
void CaseStmtNode::addOtherwise(uint32 offset) {
|
||||
otherwise = Common::SharedPtr<OtherwiseNode>(new OtherwiseNode(offset));
|
||||
otherwise->parent = this;
|
||||
otherwise->block->endPos = endPos;
|
||||
}
|
||||
|
||||
/* CallNode */
|
||||
|
||||
bool CallNode::noParens() const {
|
||||
if (isStatement) {
|
||||
// TODO: Make a complete list of commonly paren-less commands
|
||||
if (name == "put")
|
||||
return true;
|
||||
if (name == "return")
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CallNode::isMemberExpr() const {
|
||||
if (isExpression) {
|
||||
size_t nargs = argList->getValue()->l.size();
|
||||
if (name == "cast" && (nargs == 1 || nargs == 2))
|
||||
return true;
|
||||
if (name == "member" && (nargs == 1 || nargs == 2))
|
||||
return true;
|
||||
if (name == "script" && (nargs == 1 || nargs == 2))
|
||||
return true;
|
||||
if (name == "castLib" && nargs == 1)
|
||||
return true;
|
||||
if (name == "window" && nargs == 1)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CallNode::hasSpaces(bool dot) {
|
||||
if (!dot && isMemberExpr())
|
||||
return true;
|
||||
|
||||
if (noParens())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ObjCallNode */
|
||||
|
||||
bool ObjCallNode::hasSpaces(bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ObjCallV4Node */
|
||||
|
||||
bool ObjCallV4Node::hasSpaces(bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ObjPropExprNode */
|
||||
|
||||
bool ObjPropExprNode::hasSpaces(bool dot) {
|
||||
return !dot;
|
||||
}
|
||||
|
||||
/* ObjBracketExprNode */
|
||||
|
||||
bool ObjBracketExprNode::hasSpaces(bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ObjPropIndexExprNode */
|
||||
|
||||
bool ObjPropIndexExprNode::hasSpaces(bool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ErrorNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void CommentNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void LiteralNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void IfStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void NewObjNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void EndCaseNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void HandlerNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ObjCallNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void PutStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void TheExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void BinaryOpNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void CaseStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ExitStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void TellStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void WhenStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void CaseLabelNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ChunkExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void InverseOpNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ObjCallV4Node::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void OtherwiseNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void MemberExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ObjPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void PlayCmdStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ThePropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void MenuPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void SoundCmdStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void SoundPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void AssignmentStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ExitRepeatStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void NextRepeatStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ObjBracketExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void SpritePropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ChunkDeleteStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ChunkHiliteStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void RepeatWhileStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void MenuItemPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void ObjPropIndexExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void RepeatWithInStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void RepeatWithToStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void SpriteWithinExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void LastStringChunkExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void SpriteIntersectsExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void StringChunkCountExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void VarNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void CallNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void BlockNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
void NotOpNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
|
||||
|
||||
} // namespace LingoDec
|
||||
Reference in New Issue
Block a user