[Scummvm-git-logs] scummvm master -> 71ea2d7f57766bd8a498dfc3bf76d90f434a993a

scemino noreply at scummvm.org
Fri May 17 20:41:22 UTC 2024


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
71ea2d7f57 DIRECTOR: Remove use of CodeWriter


Commit: 71ea2d7f57766bd8a498dfc3bf76d90f434a993a
    https://github.com/scummvm/scummvm/commit/71ea2d7f57766bd8a498dfc3bf76d90f434a993a
Author: scemino (scemino74 at gmail.com)
Date: 2024-05-17T22:37:33+02:00

Commit Message:
DIRECTOR: Remove use of CodeWriter

Changed paths:
  A engines/director/lingo/lingodec/codewritervisitor.cpp
  A engines/director/lingo/lingodec/codewritervisitor.h
  R engines/director/lingo/lingodec/codewriter.cpp
  R engines/director/lingo/lingodec/codewriter.h
    engines/director/debugtools.cpp
    engines/director/lingo/lingodec/ast.cpp
    engines/director/lingo/lingodec/ast.h
    engines/director/lingo/lingodec/handler.cpp
    engines/director/lingo/lingodec/handler.h
    engines/director/lingo/lingodec/script.cpp
    engines/director/lingo/lingodec/script.h
    engines/director/module.mk


diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index d81bde6a9eb..8cf8fc042da 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -32,7 +32,7 @@
 #include "director/lingo/lingo.h"
 #include "director/lingo/lingo-object.h"
 #include "director/lingo/lingodec/ast.h"
-#include "director/lingo/lingodec/codewriter.h"
+#include "director/lingo/lingodec/codewritervisitor.h"
 #include "director/lingo/lingodec/context.h"
 #include "director/lingo/lingodec/handler.h"
 #include "director/lingo/lingodec/resolver.h"
@@ -102,67 +102,67 @@ public:
 		LingoDec::Script *script = node.handler->script;
 		bool isMethod = script->isFactory();
 		{
-			LingoDec::CodeWriter code("\n");
+			Common::String code;
 			if (isMethod) {
-				code.write("method ");
+				code += "method ";
 			} else {
-				code.write("on ");
+				code += "on ";
 			}
-			code.write(node.handler->name);
+			code += node.handler->name;
 
 			if (node.handler->argumentNames.size() > 0) {
-				code.write(" ");
+				code += " ";
 				for (size_t i = 0; i < node.handler->argumentNames.size(); i++) {
 					if (i > 0)
-						code.write(", ");
-					code.write(node.handler->argumentNames[i]);
+						code += ", ";
+					code += node.handler->argumentNames[i];
 				}
 			}
-			write(node._startOffset, code.str());
+			write(node._startOffset, code);
 		}
 
 		{
-			LingoDec::CodeWriter code("\n");
+			Common::String code;
 			if (isMethod && node.handler->script->propertyNames.size() > 0 && node.handler == &node.handler->script->handlers[0]) {
-				code.write("instance ");
+				code += "instance ";
 				for (size_t i = 0; i < node.handler->script->propertyNames.size(); i++) {
 					if (i > 0)
-						code.write(", ");
-					code.write(node.handler->script->propertyNames[i]);
+						code += ", ";
+					code += node.handler->script->propertyNames[i];
 				}
-				write(node._startOffset, code.str());
+				write(node._startOffset, code);
 			}
 		}
 
 		{
 			if (node.handler->globalNames.size() > 0) {
-				LingoDec::CodeWriter code("\n");
-				code.write("global ");
+				Common::String code;
+				code += "global ";
 				for (size_t i = 0; i < node.handler->globalNames.size(); i++) {
 					if (i > 0)
-						code.write(", ");
-					code.write(node.handler->globalNames[i]);
+						code += ", ";
+					code += node.handler->globalNames[i];
 				}
-				write(node._startOffset, code.str());
+				write(node._startOffset, code);
 			}
 		}
 
 		node.block->accept(*this);
 
 		{
-			LingoDec::CodeWriter code("\n");
+			Common::String code;
 			if (!isMethod) {
-				code.writeLine("end");
+				code += "end";
 			}
-			_script.code.push_back({node.block->_endOffset, code.str()});
+			_script.code.push_back({node.block->_endOffset, code});
 		}
 	}
 
 	virtual void visit(const LingoDec::RepeatWhileStmtNode &node) override {
-		LingoDec::CodeWriter code("\n");
+		LingoDec::CodeWriterVisitor code(_dot, false);
 		code.write("repeat while ");
-		node.condition->writeScriptText(code, _dot, false);
-		write(node._startOffset, code.str());
+		node.condition->accept(code);
+		write(node._startOffset, code._str);
 
 		node.block->accept(*this);
 
@@ -170,40 +170,40 @@ public:
 	}
 
 	virtual void visit(const LingoDec::RepeatWithInStmtNode &node) override {
-		LingoDec::CodeWriter code("\n");
+		LingoDec::CodeWriterVisitor code(_dot, false);
 		code.write("repeat with ");
 		code.write(node.varName);
 		code.write(" in ");
-		node.list->writeScriptText(code, _dot, false);
-		write(node._startOffset, code.str());
+		node.list->accept(code);
+		write(node._startOffset, code._str);
 		node.block->accept(*this);
 		write(node._endOffset, "end repeat");
 	}
 
 	virtual void visit(const LingoDec::RepeatWithToStmtNode &node) override {
-		LingoDec::CodeWriter code("\n");
+		LingoDec::CodeWriterVisitor code(_dot, false);
 		code.write("repeat with ");
 		code.write(node.varName);
 		code.write(" = ");
-		node.start->writeScriptText(code, _dot, false);
+		node.start->accept(code);
 		if (node.up) {
 			code.write(" to ");
 		} else {
 			code.write(" down to ");
 		}
-		node.end->writeScriptText(code, _dot, false);
-		write(node._startOffset, code.str());
+		node.end->accept(code);
+		write(node._startOffset, code._str);
 		node.block->accept(*this);
 		write(node._endOffset, "end repeat");
 	}
 
 	virtual void visit(const LingoDec::IfStmtNode &node) override {
 		{
-			LingoDec::CodeWriter code("\n");
+			LingoDec::CodeWriterVisitor code(_dot, false);
 			code.write("if ");
-			node.condition->writeScriptText(code, _dot, false);
+			node.condition->accept(code);
 			code.write(" then");
-			write(node._startOffset, code.str());
+			write(node._startOffset, code._str);
 		}
 		node.block1->accept(*this);
 		if (node.hasElse) {
@@ -214,18 +214,18 @@ public:
 	}
 
 	virtual void visit(const LingoDec::TellStmtNode &node) override {
-		LingoDec::CodeWriter code("\n");
+		LingoDec::CodeWriterVisitor code(_dot, false);
 		code.write("tell ");
-		node.window->writeScriptText(code, _dot, false);
-		write(node._startOffset, code.str());
+		node.window->accept(*this);
+		write(node._startOffset, code._str);
 		node.block->accept(*this);
 		write(node._endOffset, "end tell");
 	}
 
 	virtual void defaultVisit(const LingoDec::Node &node) override {
-		LingoDec::CodeWriter code("\n");
-		node.writeScriptText(code, _dot, false);
-		write(node._startOffset, code.str());
+		LingoDec::CodeWriterVisitor code(_dot, false);
+		node.accept(code);
+		write(node._startOffset, code._str);
 	}
 
 	void write(uint32 offset, const Common::String &code) {
diff --git a/engines/director/lingo/lingodec/ast.cpp b/engines/director/lingo/lingodec/ast.cpp
index ea357808c2d..a0a6121855a 100644
--- a/engines/director/lingo/lingodec/ast.cpp
+++ b/engines/director/lingo/lingodec/ast.cpp
@@ -6,7 +6,6 @@
 
 #include "common/util.h"
 #include "./ast.h"
-#include "./codewriter.h"
 #include "./handler.h"
 #include "./names.h"
 #include "./script.h"
@@ -27,96 +26,8 @@ int Datum::toInt() {
 	return 0;
 }
 
-void Datum::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	switch (type) {
-	case kDatumVoid:
-		code.write("VOID");
-		return;
-	case kDatumSymbol:
-		code.write("#" + s);
-		return;
-	case kDatumVarRef:
-		code.write(s);
-		return;
-	case kDatumString:
-		if (s.size() == 0) {
-			code.write("EMPTY");
-			return;
-		}
-		if (s.size() == 1) {
-			switch (s[0]) {
-			case '\x03':
-				code.write("ENTER");
-				return;
-			case '\x08':
-				code.write("BACKSPACE");
-				return;
-			case '\t':
-				code.write("TAB");
-				return;
-			case '\r':
-				code.write("RETURN");
-				return;
-			case '"':
-				code.write("QUOTE");
-				return;
-			default:
-				break;
-			}
-		}
-		if (sum) {
-			code.write("\"" + Common::toPrintable(s) + "\"");
-			return;
-		}
-		code.write("\"" + s + "\"");
-		return;
-	case kDatumInt:
-		code.write(Common::String::format("%d", i));
-		return;
-	case kDatumFloat:
-		code.write(Common::String::format("%g", f));
-		return;
-	case kDatumList:
-	case kDatumArgList:
-	case kDatumArgListNoRet:
-		{
-			if (type == kDatumList)
-				code.write("[");
-			for (size_t ii = 0; ii < l.size(); ii++) {
-				if (ii > 0)
-					code.write(", ");
-				l[ii]->writeScriptText(code, dot, sum);
-			}
-			if (type == kDatumList)
-				code.write("]");
-		}
-		return;
-	case kDatumPropList:
-		{
-			code.write("[");
-			if (l.size() == 0) {
-				code.write(":");
-			} else {
-				for (size_t ii = 0; ii < l.size(); ii += 2) {
-					if (ii > 0)
-						code.write(", ");
-					l[ii]->writeScriptText(code, dot, sum);
-					code.write(": ");
-					l[ii + 1]->writeScriptText(code, dot, sum);
-				}
-			}
-			code.write("]");
-		}
-		return;
-	}
-}
-
 /* AST */
 
-void AST::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	root->writeScriptText(code, dot, sum);
-}
-
 void AST::addStatement(Common::SharedPtr<Node> statement) {
 	currentBlock->addChild(Common::move(statement));
 }
@@ -169,27 +80,12 @@ bool Node::hasSpaces(bool) {
 
 /* ErrorNode */
 
-void ErrorNode::writeScriptText(CodeWriter &code, bool, bool) const {
-	code.write("ERROR");
-}
-
 bool ErrorNode::hasSpaces(bool) {
 	return false;
 }
 
-/* CommentNode */
-
-void CommentNode::writeScriptText(CodeWriter &code, bool, bool) const {
-	code.write("-- ");
-	code.write(text);
-}
-
 /* LiteralNode */
 
-void LiteralNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	value->writeScriptText(code, dot, sum);
-}
-
 Common::SharedPtr<Datum> LiteralNode::getValue() {
 	return value;
 }
@@ -200,139 +96,13 @@ bool LiteralNode::hasSpaces(bool) {
 
 /* BlockNode */
 
-void BlockNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	for (const auto &child : children) {
-		child->writeScriptText(code, dot, sum);
-		code.writeLine();
-	}
-}
-
 void BlockNode::addChild(Common::SharedPtr<Node> child) {
 	child->parent = this;
 	children.push_back(Common::move(child));
 }
 
-/* HandlerNode */
-
-void HandlerNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	if (handler->isGenericEvent) {
-		block->writeScriptText(code, dot, sum);
-	} else {
-		Script *script = handler->script;
-		bool isMethod = script->isFactory();
-		if (isMethod) {
-			code.write("method ");
-		} else {
-			code.write("on ");
-		}
-		code.write(handler->name);
-		if (handler->argumentNames.size() > 0) {
-			code.write(" ");
-			for (size_t i = 0; i < handler->argumentNames.size(); i++) {
-				if (i > 0)
-					code.write(", ");
-				code.write(handler->argumentNames[i]);
-			}
-		}
-		code.writeLine();
-		code.indent();
-		if (isMethod && script->propertyNames.size() > 0 && handler == &script->handlers[0]) {
-			code.write("instance ");
-			for (size_t i = 0; i < script->propertyNames.size(); i++) {
-				if (i > 0)
-					code.write(", ");
-				code.write(script->propertyNames[i]);
-			}
-			code.writeLine();
-		}
-		if (handler->globalNames.size() > 0) {
-			code.write("global ");
-			for (size_t i = 0; i < handler->globalNames.size(); i++) {
-				if (i > 0)
-					code.write(", ");
-				code.write(handler->globalNames[i]);
-			}
-			code.writeLine();
-		}
-		block->writeScriptText(code, dot, sum);
-		code.unindent();
-		if (!isMethod) {
-			code.writeLine("end");
-		}
-	}
-}
-
-/* ExitStmtNode */
-
-void ExitStmtNode::writeScriptText(CodeWriter &code, bool, bool) const {
-	code.write("exit");
-}
-
-/* InverseOpNode */
-
-void InverseOpNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("-");
-
-	bool parenOperand = operand->hasSpaces(dot);
-	if (parenOperand) {
-		code.write("(");
-	}
-	operand->writeScriptText(code, dot, sum);
-	if (parenOperand) {
-		code.write(")");
-	}
-}
-
-/* NotOpNode */
-
-void NotOpNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("not ");
-
-	bool parenOperand = operand->hasSpaces(dot);
-	if (parenOperand) {
-		code.write("(");
-	}
-	operand->writeScriptText(code, dot, sum);
-	if (parenOperand) {
-		code.write(")");
-	}
-}
-
 /* BinaryOpNode */
 
-void BinaryOpNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	unsigned int precedence = getPrecedence();
-	bool parenLeft = false;
-	bool parenRight = false;
-	if (precedence) {
-		if (left->type == kBinaryOpNode) {
-			auto leftBinaryOpNode = static_cast<BinaryOpNode *>(left.get());
-			parenLeft = (leftBinaryOpNode->getPrecedence() != precedence);
-		}
-		parenRight = (right->type == kBinaryOpNode);
-	}
-
-	if (parenLeft) {
-		code.write("(");
-	}
-	left->writeScriptText(code, dot, sum);
-	if (parenLeft) {
-		code.write(")");
-	}
-
-	code.write(" ");
-	code.write(StandardNames::binaryOpNames[opcode]);
-	code.write(" ");
-
-	if (parenRight) {
-		code.write("(");
-	}
-	right->writeScriptText(code, dot, sum);
-	if (parenRight) {
-		code.write(")");
-	}
-}
-
 unsigned int BinaryOpNode::getPrecedence() const {
 	switch (opcode) {
 	case kOpMul:
@@ -359,390 +129,26 @@ unsigned int BinaryOpNode::getPrecedence() const {
 	return 0;
 }
 
-/* ChunkExprNode */
-
-void ChunkExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write(StandardNames::chunkTypeNames[type]);
-	code.write(" ");
-	first->writeScriptText(code, dot, sum);
-	if (!(last->type == kLiteralNode && last->getValue()->type == kDatumInt && last->getValue()->i == 0)) {
-		code.write(" to ");
-		last->writeScriptText(code, dot, sum);
-	}
-	code.write(" of ");
-	string->writeScriptText(code, false, sum); // we want the string to always be verbose
-}
-
-/* ChunkHiliteStmtNode */
-
-void ChunkHiliteStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("hilite ");
-	chunk->writeScriptText(code, dot, sum);
-}
-
-/* ChunkDeleteStmtNode */
-
-void ChunkDeleteStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("delete ");
-	chunk->writeScriptText(code, dot, sum);
-}
-
-/* SpriteIntersectsExprNode */
-
-void SpriteIntersectsExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("sprite ");
-
-	bool parenFirstSprite = (firstSprite->type == kBinaryOpNode);
-	if (parenFirstSprite) {
-		code.write("(");
-	}
-	firstSprite->writeScriptText(code, dot, sum);
-	if (parenFirstSprite) {
-		code.write(")");
-	}
-
-	code.write(" intersects ");
-
-	bool parenSecondSprite = (secondSprite->type == kBinaryOpNode);
-	if (parenSecondSprite) {
-		code.write("(");
-	}
-	secondSprite->writeScriptText(code, dot, sum);
-	if (parenSecondSprite) {
-		code.write(")");
-	}
-}
-
-/* SpriteWithinExprNode */
-
-void SpriteWithinExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("sprite ");
-
-	bool parenFirstSprite = (firstSprite->type == kBinaryOpNode);
-	if (parenFirstSprite) {
-		code.write("(");
-	}
-	firstSprite->writeScriptText(code, dot, sum);
-	if (parenFirstSprite) {
-		code.write(")");
-	}
-
-	code.write(" within ");
-
-	bool parenSecondSprite = (secondSprite->type == kBinaryOpNode);
-	if (parenSecondSprite) {
-		code.write("(");
-	}
-	secondSprite->writeScriptText(code, dot, sum);
-	if (parenSecondSprite) {
-		code.write(")");
-	}
-}
-
 /* MemberExprNode */
 
-void MemberExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	bool hasCastID = castID && !(castID->type == kLiteralNode && castID->getValue()->type == kDatumInt && castID->getValue()->i == 0);
-	code.write(type);
-	if (dot) {
-		code.write("(");
-		memberID->writeScriptText(code, dot, sum);
-		if (hasCastID) {
-			code.write(", ");
-			castID->writeScriptText(code, dot, sum);
-		}
-		code.write(")");
-	} else {
-		code.write(" ");
-
-		bool parenMemberID = (memberID->type == kBinaryOpNode);
-		if (parenMemberID) {
-			code.write("(");
-		}
-		memberID->writeScriptText(code, dot, sum);
-		if (parenMemberID) {
-			code.write(")");
-		}
-
-		if (hasCastID) {
-			code.write(" of castLib ");
-
-			bool parenCastID = (castID->type == kBinaryOpNode);
-			if (parenCastID) {
-				code.write("(");
-			}
-			castID->writeScriptText(code, dot, sum);
-			if (parenCastID) {
-				code.write(")");
-			}
-		}
-	}
-}
-
 bool MemberExprNode::hasSpaces(bool dot) {
 	return !dot;
 }
 
 /* VarNode */
 
-void VarNode::writeScriptText(CodeWriter &code, bool, bool) const {
-	code.write(varName);
-}
-
 bool VarNode::hasSpaces(bool) {
 	return false;
 }
 
-/* AssignmentStmtNode */
-
-void AssignmentStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	if (!dot || forceVerbose) {
-		code.write("set ");
-		variable->writeScriptText(code, false, sum); // we want the variable to always be verbose
-		code.write(" to ");
-		value->writeScriptText(code, dot, sum);
-	} else {
-		variable->writeScriptText(code, dot, sum);
-		code.write(" = ");
-		value->writeScriptText(code, dot, sum);
-	}
-}
-
-/* IfStmtNode */
-
-void IfStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("if ");
-	condition->writeScriptText(code, dot, sum);
-	code.write(" then");
-	if (sum) {
-		if (hasElse) {
-			code.write(" / else");
-		}
-	} else {
-		code.writeLine();
-		code.indent();
-		block1->writeScriptText(code, dot, sum);
-		code.unindent();
-		if (hasElse) {
-			code.writeLine("else");
-			code.indent();
-			block2->writeScriptText(code, dot, sum);
-			code.unindent();
-		}
-		code.write("end if");
-	}
-}
-
-/* RepeatWhileStmtNode */
-
-void RepeatWhileStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("repeat while ");
-	condition->writeScriptText(code, dot, sum);
-	if (!sum) {
-		code.writeLine();
-		code.indent();
-		block->writeScriptText(code, dot, sum);
-		code.unindent();
-		code.write("end repeat");
-	}
-}
-
-/* RepeatWithInStmtNode */
-
-void RepeatWithInStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("repeat with ");
-	code.write(varName);
-	code.write(" in ");
-	list->writeScriptText(code, dot, sum);
-	if (!sum) {
-		code.writeLine();
-		code.indent();
-		block->writeScriptText(code, dot, sum);
-		code.unindent();
-		code.write("end repeat");
-	}
-}
-
-/* RepeatWithToStmtNode */
-
-void RepeatWithToStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("repeat with ");
-	code.write(varName);
-	code.write(" = ");
-	start->writeScriptText(code, dot, sum);
-	if (up) {
-		code.write(" to ");
-	} else {
-		code.write(" down to ");
-	}
-	end->writeScriptText(code, dot, sum);
-	if (!sum) {
-		code.writeLine();
-		code.indent();
-		block->writeScriptText(code, dot, sum);
-		code.unindent();
-		code.write("end repeat");
-	}
-}
-
-/* CaseLabelNode */
-
-void CaseLabelNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	if (sum) {
-		code.write("(case) ");
-		if (parent->type == kCaseLabelNode) {
-			auto parentLabel = static_cast<CaseLabelNode *>(parent);
-			if (parentLabel->nextOr.get() == this) {
-				code.write("..., ");
-			}
-		}
-
-		bool parenValue = value->hasSpaces(dot);
-		if (parenValue) {
-			code.write("(");
-		}
-		value->writeScriptText(code, dot, sum);
-		if (parenValue) {
-			code.write(")");
-		}
-
-		if (nextOr) {
-			code.write(", ...");
-		} else {
-			code.write(":");
-		}
-	} else {
-		bool parenValue = value->hasSpaces(dot);
-		if (parenValue) {
-			code.write("(");
-		}
-		value->writeScriptText(code, dot, sum);
-		if (parenValue) {
-			code.write(")");
-		}
-
-		if (nextOr) {
-			code.write(", ");
-			nextOr->writeScriptText(code, dot, sum);
-		} else {
-			code.writeLine(":");
-			code.indent();
-			block->writeScriptText(code, dot, sum);
-			code.unindent();
-		}
-		if (nextLabel) {
-			nextLabel->writeScriptText(code, dot, sum);
-		}
-	}
-}
-
-/* OtherwiseNode */
-
-void OtherwiseNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	if (sum) {
-		code.write("(case) otherwise:");
-	} else {
-		code.writeLine("otherwise:");
-		code.indent();
-		block->writeScriptText(code, dot, sum);
-		code.unindent();
-	}
-}
-
-/* EndCaseNode */
-
-void EndCaseNode::writeScriptText(CodeWriter &code, bool, bool) const {
-	code.write("end case");
-}
-
 /* CaseStmtNode */
 
-void CaseStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("case ");
-	value->writeScriptText(code, dot, sum);
-	code.write(" of");
-	if (sum) {
-		if (!firstLabel) {
-			if (otherwise) {
-				code.write(" / otherwise:");
-			} else {
-				code.write(" / end case");
-			}
-		}
-	} else {
-		code.writeLine();
-		code.indent();
-		if (firstLabel) {
-			firstLabel->writeScriptText(code, dot, sum);
-		}
-		if (otherwise) {
-			otherwise->writeScriptText(code, dot, sum);
-		}
-		code.unindent();
-		code.write("end case");
-	}
-}
-
 void CaseStmtNode::addOtherwise(uint32 offset) {
 	otherwise = Common::SharedPtr<OtherwiseNode>(new OtherwiseNode(offset));
 	otherwise->parent = this;
 	otherwise->block->endPos = endPos;
 }
 
-/* TellStmtNode */
-
-void TellStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("tell ");
-	window->writeScriptText(code, dot, sum);
-	if (!sum) {
-		code.writeLine();
-		code.indent();
-		block->writeScriptText(code, dot, sum);
-		code.unindent();
-		code.write("end tell");
-	}
-}
-
-/* SoundCmdStmtNode */
-
-void SoundCmdStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("sound ");
-	code.write(cmd);
-	if (argList->getValue()->l.size() > 0) {
-		code.write(" ");
-		argList->writeScriptText(code, dot, sum);
-	}
-}
-
-/* PlayCmdStmtNode */
-
-void PlayCmdStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	auto &rawArgs = argList->getValue()->l;
-
-	code.write("play");
-
-	if (rawArgs.size() == 0) {
-		code.write(" done");
-		return;
-	}
-
-	auto &frame = rawArgs[0];
-	if (rawArgs.size() == 1) {
-		code.write(" frame ");
-		frame->writeScriptText(code, dot, sum);
-		return;
-	}
-
-	auto &movie = rawArgs[1];
-	if (!(frame->type == kLiteralNode && frame->getValue()->type == kDatumInt && frame->getValue()->i == 1)) {
-		code.write(" frame ");
-		frame->writeScriptText(code, dot, sum);
-		code.write(" of");
-	}
-	code.write(" movie ");
-	movie->writeScriptText(code, dot, sum);
-}
-
 /* CallNode */
 
 bool CallNode::noParens() const {
@@ -775,70 +181,6 @@ bool CallNode::isMemberExpr() const {
 	return false;
 }
 
-void CallNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	if (isExpression && argList->getValue()->l.size() == 0) {
-		if (name == "pi") {
-			code.write("PI");
-			return;
-		}
-		if (name == "space") {
-			code.write("SPACE");
-			return;
-		}
-		if (name == "void") {
-			code.write("VOID");
-			return;
-		}
-	}
-
-	if (!dot && isMemberExpr()) {
-		/**
-		 * In some cases, member expressions such as `member 1 of castLib 1` compile
-		 * to the function call `member(1, 1)`. However, this doesn't parse correctly
-		 * in pre-dot-syntax versions of Director, and `put(member(1, 1))` does not
-		 * compile. Therefore, we rewrite these expressions to the verbose syntax when
-		 * in verbose mode.
-		 */
-		code.write(name);
-		code.write(" ");
-
-		auto memberID = argList->getValue()->l[0];
-		bool parenMemberID = (memberID->type == kBinaryOpNode);
-		if (parenMemberID) {
-			code.write("(");
-		}
-		memberID->writeScriptText(code, dot, sum);
-		if (parenMemberID) {
-			code.write(")");
-		}
-
-		if (argList->getValue()->l.size() == 2) {
-			code.write(" of castLib ");
-
-			auto castID = argList->getValue()->l[1];
-			bool parenCastID = (castID->type == kBinaryOpNode);
-			if (parenCastID) {
-				code.write("(");
-			}
-			castID->writeScriptText(code, dot, sum);
-			if (parenCastID) {
-				code.write(")");
-			}
-		}
-		return;
-	}
-
-	code.write(name);
-	if (noParens()) {
-		code.write(" ");
-		argList->writeScriptText(code, dot, sum);
-	} else {
-		code.write("(");
-		argList->writeScriptText(code, dot, sum);
-		code.write(")");
-	}
-}
-
 bool CallNode::hasSpaces(bool dot) {
 	if (!dot && isMemberExpr())
 		return true;
@@ -851,321 +193,34 @@ bool CallNode::hasSpaces(bool dot) {
 
 /* ObjCallNode */
 
-void ObjCallNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	auto &rawArgs = argList->getValue()->l;
-
-	auto &obj = rawArgs[0];
-	bool parenObj = obj->hasSpaces(dot);
-	if (parenObj) {
-		code.write("(");
-	}
-	obj->writeScriptText(code, dot, sum);
-	if (parenObj) {
-		code.write(")");
-	}
-
-	code.write(".");
-	code.write(name);
-	code.write("(");
-	for (size_t i = 1; i < rawArgs.size(); i++) {
-		if (i > 1)
-			code.write(", ");
-		rawArgs[i]->writeScriptText(code, dot, sum);
-	}
-	code.write(")");
-}
-
 bool ObjCallNode::hasSpaces(bool) {
 	return false;
 }
 
 /* ObjCallV4Node */
 
-void ObjCallV4Node::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	obj->writeScriptText(code, dot, sum);
-	code.write("(");
-	argList->writeScriptText(code, dot, sum);
-	code.write(")");
-}
-
 bool ObjCallV4Node::hasSpaces(bool) {
 	return false;
 }
 
-/* TheExprNode */
-
-void TheExprNode::writeScriptText(CodeWriter &code, bool, bool) const {
-	code.write("the ");
-	code.write(prop);
-}
-
-/* LastStringChunkExprNode */
-
-void LastStringChunkExprNode::writeScriptText(CodeWriter &code, bool, bool sum) const {
-	code.write("the last ");
-	code.write(StandardNames::chunkTypeNames[type]);
-	code.write(" in ");
-
-	bool parenObj = (obj->type == kBinaryOpNode);
-	if (parenObj) {
-		code.write("(");
-	}
-	obj->writeScriptText(code, false, sum); // we want the object to always be verbose
-	if (parenObj) {
-		code.write(")");
-	}
-}
-
-/* StringChunkCountExprNode */
-
-void StringChunkCountExprNode::writeScriptText(CodeWriter &code, bool, bool sum) const {
-	code.write("the number of ");
-	code.write(StandardNames::chunkTypeNames[type]); // we want the object to always be verbose
-	code.write("s in ");
-
-	bool parenObj = (obj->type == kBinaryOpNode);
-	if (parenObj) {
-		code.write("(");
-	}
-	obj->writeScriptText(code, false, sum);
-	if (parenObj) {
-		code.write(")");
-	}
-}
-
-/* MenuPropExprNode */
-
-void MenuPropExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("the ");
-	code.write(StandardNames::menuPropertyNames[prop]);
-	code.write(" of menu ");
-
-	bool parenMenuID = (menuID->type == kBinaryOpNode);
-	if (parenMenuID) {
-		code.write("(");
-	}
-	menuID->writeScriptText(code, dot, sum);
-	if (parenMenuID) {
-		code.write(")");
-	}
-}
-
-/* MenuItemPropExprNode */
-
-void MenuItemPropExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("the ");
-	code.write(StandardNames::menuItemPropertyNames[prop]);
-	code.write(" of menuItem ");
-
-	bool parenItemID = (itemID->type == kBinaryOpNode);
-	if (parenItemID) {
-		code.write("(");
-	}
-	itemID->writeScriptText(code, dot, sum);
-	if (parenItemID) {
-		code.write(")");
-	}
-
-	code.write(" of menu ");
-
-	bool parenMenuID = (menuID->type == kBinaryOpNode);
-	if (parenMenuID) {
-		code.write("(");
-	}
-	menuID->writeScriptText(code, dot, sum);
-	if (parenMenuID) {
-		code.write(")");
-	}
-}
-
-/* SoundPropExprNode */
-
-void SoundPropExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("the ");
-	code.write(StandardNames::soundPropertyNames[prop]);
-	code.write(" of sound ");
-
-	bool parenSoundID = (soundID->type == kBinaryOpNode);
-	if (parenSoundID) {
-		code.write("(");
-	}
-	soundID->writeScriptText(code, dot, sum);
-	if (parenSoundID) {
-		code.write(")");
-	}
-}
-
-/* SpritePropExprNode */
-
-void SpritePropExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("the ");
-	code.write(StandardNames::spritePropertyNames[prop]);
-	code.write(" of sprite ");
-
-	bool parenSpriteID = (spriteID->type == kBinaryOpNode);
-	if (parenSpriteID) {
-		code.write("(");
-	}
-	spriteID->writeScriptText(code, dot, sum);
-	if (parenSpriteID) {
-		code.write(")");
-	}
-}
-
-/* ThePropExprNode */
-
-void ThePropExprNode::writeScriptText(CodeWriter &code, bool, bool sum) const {
-	code.write("the ");
-	code.write(prop);
-	code.write(" of ");
-
-	bool parenObj = (obj->type == kBinaryOpNode);
-	if (parenObj) {
-		code.write("(");
-	}
-	obj->writeScriptText(code, false, sum); // we want the object to always be verbose
-	if (parenObj) {
-		code.write(")");
-	}
-}
-
 /* ObjPropExprNode */
 
-void ObjPropExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	if (dot) {
-		bool parenObj = obj->hasSpaces(dot);
-		if (parenObj) {
-			code.write("(");
-		}
-		obj->writeScriptText(code, dot, sum);
-		if (parenObj) {
-			code.write(")");
-		}
-
-		code.write(".");
-		code.write(prop);
-	} else {
-		code.write("the ");
-		code.write(prop);
-		code.write(" of ");
-
-		bool parenObj = (obj->type == kBinaryOpNode);
-		if (parenObj) {
-			code.write("(");
-		}
-		obj->writeScriptText(code, dot, sum);
-		if (parenObj) {
-			code.write(")");
-		}
-	}
-}
-
 bool ObjPropExprNode::hasSpaces(bool dot) {
 	return !dot;
 }
 
 /* ObjBracketExprNode */
 
-void ObjBracketExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	bool parenObj = obj->hasSpaces(dot);
-	if (parenObj) {
-		code.write("(");
-	}
-	obj->writeScriptText(code, dot, sum);
-	if (parenObj) {
-		code.write(")");
-	}
-
-	code.write("[");
-	prop->writeScriptText(code, dot, sum);
-	code.write("]");
-}
-
 bool ObjBracketExprNode::hasSpaces(bool) {
 	return false;
 }
 
 /* ObjPropIndexExprNode */
 
-void ObjPropIndexExprNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	bool parenObj = obj->hasSpaces(dot);
-	if (parenObj) {
-		code.write("(");
-	}
-	obj->writeScriptText(code, dot, sum);
-	if (parenObj) {
-		code.write(")");
-	}
-
-	code.write(".");
-	code.write(prop);
-	code.write("[");
-	index->writeScriptText(code, dot, sum);
-	if (index2) {
-		code.write("..");
-		index2->writeScriptText(code, dot, sum);
-	}
-	code.write("]");
-}
-
 bool ObjPropIndexExprNode::hasSpaces(bool) {
 	return false;
 }
 
-/* ExitRepeatStmtNode */
-
-void ExitRepeatStmtNode::writeScriptText(CodeWriter &code, bool, bool) const {
-	code.write("exit repeat");
-}
-
-/* NextRepeatStmtNode */
-
-void NextRepeatStmtNode::writeScriptText(CodeWriter &code, bool, bool) const {
-	code.write("next repeat");
-}
-
-/* PutStmtNode */
-
-void PutStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("put ");
-	value->writeScriptText(code, dot, sum);
-	code.write(" ");
-	code.write(StandardNames::putTypeNames[type]);
-	code.write(" ");
-	variable->writeScriptText(code, false, sum); // we want the variable to always be verbose
-}
-
-/* WhenStmtNode */
-
-void WhenStmtNode::writeScriptText(CodeWriter &code, bool, bool) const {
-	code.write("when ");
-	code.write(StandardNames::whenEventNames[event]);
-	code.write(" then");
-
-	code.doIndentation = false;
-	for (size_t i = 0; i < script.size(); i++) {
-		char ch = script[i];
-		if (ch == '\r') {
-			if (i != script.size() - 1) {
-				code.writeLine();
-			}
-		} else {
-			code.write(ch);
-		}
-	}
-	code.doIndentation = true;
-}
-
-/* NewObjNode */
-
-void NewObjNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
-	code.write("new ");
-	code.write(objType);
-	code.write("(");
-	objArgs->writeScriptText(code, dot, sum);
-	code.write(")");
-}
-
 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); }
diff --git a/engines/director/lingo/lingodec/ast.h b/engines/director/lingo/lingodec/ast.h
index 3ec33b7e22f..1a0e334ce8a 100644
--- a/engines/director/lingo/lingodec/ast.h
+++ b/engines/director/lingo/lingodec/ast.h
@@ -11,7 +11,6 @@
 #include "common/ptr.h"
 #include "common/str.h"
 #include "common/util.h"
-#include "./codewriter.h"
 #include "./enums.h"
 
 namespace LingoDec {
@@ -52,7 +51,6 @@ struct Datum {
 	}
 
 	int toInt();
-	void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
 };
 
 class NodeVisitor;
@@ -71,7 +69,6 @@ struct Node {
 
 	Node(NodeType t, uint32 offset) : type(t), isExpression(false), isStatement(false), isLabel(false), isLoop(false), parent(nullptr), _startOffset(offset), _endOffset(offset) {}
 	virtual ~Node() {}
-	virtual void writeScriptText(CodeWriter&, bool, bool) const {}
 	virtual void accept(NodeVisitor& visitor) const = 0;
 	virtual Common::SharedPtr<Datum> getValue();
 	Node *ancestorStatement();
@@ -117,7 +114,6 @@ struct LoopNode : StmtNode {
 
 struct ErrorNode : ExprNode {
 	explicit ErrorNode(uint32 offset) : ExprNode(kErrorNode, offset) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -128,7 +124,6 @@ struct CommentNode : Node {
 	Common::String text;
 
 	CommentNode(uint32 offset, Common::String t) : Node(kCommentNode, offset), text(t) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -140,7 +135,6 @@ struct LiteralNode : ExprNode {
 	LiteralNode(uint32 offset, Common::SharedPtr<Datum> d) : ExprNode(kLiteralNode, offset) {
 		value = Common::move(d);
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual Common::SharedPtr<Datum> getValue() override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
@@ -156,7 +150,6 @@ struct BlockNode : Node {
 	CaseLabelNode *currentCaseLabel;
 
 	explicit BlockNode(uint32 offset) : Node(kBlockNode, offset), endPos(-1), currentCaseLabel(nullptr) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	void addChild(Common::SharedPtr<Node> child);
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -172,7 +165,6 @@ struct HandlerNode : Node {
 		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -180,7 +172,6 @@ struct HandlerNode : Node {
 
 struct ExitStmtNode : StmtNode {
 	explicit ExitStmtNode(uint32 offset) : StmtNode(kExitStmtNode, offset) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -193,7 +184,6 @@ struct InverseOpNode : ExprNode {
 		operand = Common::move(o);
 		operand->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -206,7 +196,6 @@ struct NotOpNode : ExprNode {
 		operand = Common::move(o);
 		operand->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -224,7 +213,6 @@ struct BinaryOpNode : ExprNode {
 		right = Common::move(b);
 		right->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual unsigned int getPrecedence() const;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -246,7 +234,6 @@ struct ChunkExprNode : ExprNode {
 		string = Common::move(s);
 		string->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -259,7 +246,6 @@ struct ChunkHiliteStmtNode : StmtNode {
 		chunk = Common::move(c);
 		chunk->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -272,7 +258,6 @@ struct ChunkDeleteStmtNode : StmtNode {
 		chunk = Common::move(c);
 		chunk->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -289,7 +274,6 @@ struct SpriteIntersectsExprNode : ExprNode {
 		secondSprite = Common::move(b);
 		secondSprite->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -306,7 +290,6 @@ struct SpriteWithinExprNode : ExprNode {
 		secondSprite = Common::move(b);
 		secondSprite->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -326,7 +309,6 @@ struct MemberExprNode : ExprNode {
 			this->castID->parent = this;
 		}
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -337,7 +319,6 @@ struct VarNode : ExprNode {
 	Common::String varName;
 
 	VarNode(uint32 offset, Common::String v) : ExprNode(kVarNode, offset), varName(v) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -357,7 +338,6 @@ struct AssignmentStmtNode : StmtNode {
 		value->parent = this;
 	}
 
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -377,7 +357,6 @@ struct IfStmtNode : StmtNode {
 		block2 = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block2->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -394,7 +373,6 @@ struct RepeatWhileStmtNode : LoopNode {
 		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -413,7 +391,6 @@ struct RepeatWithInStmtNode : LoopNode {
 		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -436,7 +413,6 @@ struct RepeatWithToStmtNode : LoopNode {
 		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -455,7 +431,6 @@ struct CaseLabelNode : LabelNode {
 		value = Common::move(v);
 		value->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -468,7 +443,6 @@ struct OtherwiseNode : LabelNode {
 		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -476,7 +450,6 @@ struct OtherwiseNode : LabelNode {
 
 struct EndCaseNode : LabelNode {
 	explicit EndCaseNode(uint32 offset) : LabelNode(kEndCaseNode, offset) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -495,7 +468,6 @@ struct CaseStmtNode : StmtNode {
 		value = Common::move(v);
 		value->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	void addOtherwise(uint32 offset);
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -512,7 +484,6 @@ struct TellStmtNode : StmtNode {
 		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -527,7 +498,6 @@ struct SoundCmdStmtNode : StmtNode {
 		argList = Common::move(a);
 		argList->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -540,7 +510,6 @@ struct PlayCmdStmtNode : StmtNode {
 		argList = Common::move(a);
 		argList->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -561,7 +530,6 @@ struct CallNode : Node {
 	}
 	bool noParens() const;
 	bool isMemberExpr() const;
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -581,7 +549,6 @@ struct ObjCallNode : Node {
 		else
 			isExpression = true;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -601,7 +568,6 @@ struct ObjCallV4Node : Node {
 		else
 			isExpression = true;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -612,7 +578,6 @@ struct TheExprNode : ExprNode {
 	Common::String prop;
 
 	TheExprNode(uint32 offset, Common::String p) : ExprNode(kTheExprNode, offset), prop(p) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -627,7 +592,6 @@ struct LastStringChunkExprNode : ExprNode {
 		obj = Common::move(o);
 		obj->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -642,7 +606,6 @@ struct StringChunkCountExprNode : ExprNode {
 		obj = Common::move(o);
 		obj->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -657,7 +620,6 @@ struct MenuPropExprNode : ExprNode {
 		menuID = Common::move(m);
 		menuID->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -675,7 +637,6 @@ struct MenuItemPropExprNode : ExprNode {
 		itemID = Common::move(i);
 		itemID->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -690,7 +651,6 @@ struct SoundPropExprNode : ExprNode {
 		soundID = Common::move(s);
 		soundID->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -705,7 +665,6 @@ struct SpritePropExprNode : ExprNode {
 		spriteID = Common::move(s);
 		spriteID->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -720,7 +679,6 @@ struct ThePropExprNode : ExprNode {
 		obj = Common::move(o);
 		obj->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -735,7 +693,6 @@ struct ObjPropExprNode : ExprNode {
 		obj = Common::move(o);
 		obj->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -753,7 +710,6 @@ struct ObjBracketExprNode : ExprNode {
 		prop = Common::move(p);
 		prop->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -777,7 +733,6 @@ struct ObjPropIndexExprNode : ExprNode {
 			index2->parent = this;
 		}
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual bool hasSpaces(bool dot) override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
@@ -786,7 +741,6 @@ struct ObjPropIndexExprNode : ExprNode {
 
 struct ExitRepeatStmtNode : StmtNode {
 	explicit ExitRepeatStmtNode(uint32 offset) : StmtNode(kExitRepeatStmtNode, offset) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -794,7 +748,6 @@ struct ExitRepeatStmtNode : StmtNode {
 
 struct NextRepeatStmtNode : StmtNode {
 	explicit NextRepeatStmtNode(uint32 offset) : StmtNode(kNextRepeatStmtNode, offset) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -812,7 +765,6 @@ struct PutStmtNode : StmtNode {
 		value = Common::move(val);
 		value->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -824,7 +776,6 @@ struct WhenStmtNode : StmtNode {
 
 	WhenStmtNode(uint32 offset, int e, Common::String s)
 		: StmtNode(kWhenStmtNode, offset), event(e), script(s) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -835,7 +786,6 @@ struct NewObjNode : ExprNode {
 	Common::SharedPtr<Node> objArgs;
 
 	NewObjNode(uint32 offset, Common::String o, Common::SharedPtr<Node> args) : ExprNode(kNewObjNode, offset), objType(o), objArgs(args) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual void accept(NodeVisitor &visitor) const override;
 };
 
@@ -904,7 +854,6 @@ struct AST {
 		currentBlock = root->block.get();
 	}
 
-	void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
 	void addStatement(Common::SharedPtr<Node> statement);
 	void enterBlock(BlockNode *block);
 	void exitBlock();
diff --git a/engines/director/lingo/lingodec/codewriter.cpp b/engines/director/lingo/lingodec/codewriter.cpp
deleted file mode 100644
index fa20eae8883..00000000000
--- a/engines/director/lingo/lingodec/codewriter.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 "./codewriter.h"
-
-namespace LingoDec {
-
-void CodeWriter::write(Common::String str) {
-	if (str.empty())
-		return;
-
-	writeIndentation();
-	_res += str;
-	_lineWidth += str.size();
-	_size += str.size();
-}
-
-void CodeWriter::write(char ch) {
-	writeIndentation();
-	_res += ch;
-	_lineWidth += 1;
-	_size += 1;
-}
-
-void CodeWriter::writeLine(Common::String str) {
-	if (str.empty()) {
-		_res += _lineEnding;
-	} else {
-		writeIndentation();
-		_res += str;
-		_res += _lineEnding;
-	}
-	_indentationWritten = false;
-	_lineWidth = 0;
-	_size += str.size() + _lineEnding.size();
-}
-
-void CodeWriter::writeLine() {
-	_res += _lineEnding;
-	_indentationWritten = false;
-	_lineWidth = 0;
-	_size += _lineEnding.size();
-}
-
-void CodeWriter::indent() {
-	_indentationLevel += 1;
-}
-
-void CodeWriter::unindent() {
-	if (_indentationLevel > 0) {
-		_indentationLevel -= 1;
-	}
-}
-
-Common::String CodeWriter::str() const {
-	return _res;
-}
-
-void CodeWriter::writeIndentation() {
-	if (_indentationWritten || !doIndentation)
-		return;
-
-	for (int i = 0; i < _indentationLevel; i++) {
-		_res += _indentation;
-	}
-
-	_indentationWritten = true;
-	_lineWidth = _indentationLevel * _indentation.size();
-	_size += _lineWidth;
-}
-
-} // namespace Common
diff --git a/engines/director/lingo/lingodec/codewriter.h b/engines/director/lingo/lingodec/codewriter.h
deleted file mode 100644
index 81e0f8d7209..00000000000
--- a/engines/director/lingo/lingodec/codewriter.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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/.
- */
-
-#ifndef LINGODEC_CODEWRITER_H
-#define LINGODEC_CODEWRITER_H
-
-#include "common/str.h"
-
-namespace LingoDec {
-
-class CodeWriter {
-protected:
-	Common::String _res;
-
-	Common::String _lineEnding;
-	Common::String _indentation;
-
-	int _indentationLevel = 0;
-	bool _indentationWritten = false;
-	size_t _lineWidth = 0;
-	size_t _size = 0;
-
-public:
-	bool doIndentation = true;
-
-public:
-	CodeWriter(Common::String lineEnding, Common::String indentation = "  ")
-		: _lineEnding(lineEnding), _indentation(indentation) {}
-
-	void write(Common::String str);
-	void write(char ch);
-	void writeLine(Common::String str);
-	void writeLine();
-
-	void indent();
-	void unindent();
-
-	Common::String str() const;
-	size_t lineWidth() const { return _lineWidth; }
-	size_t size() const { return _size; }
-
-protected:
-	void writeIndentation();
-};
-
-} // namespace Common
-
-#endif // LINGODEC_CODEWRITER_H
diff --git a/engines/director/lingo/lingodec/codewritervisitor.cpp b/engines/director/lingo/lingodec/codewritervisitor.cpp
new file mode 100644
index 00000000000..78a8a3b5ea6
--- /dev/null
+++ b/engines/director/lingo/lingodec/codewritervisitor.cpp
@@ -0,0 +1,876 @@
+/*
+ * 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/ptr.h"
+#include "./ast.h"
+#include "./handler.h"
+#include "./names.h"
+#include "./script.h"
+#include "./codewritervisitor.h"
+
+namespace LingoDec {
+
+void CodeWriterVisitor::visit(const HandlerNode &node) {
+	if (node.handler->isGenericEvent) {
+		node.block->accept(*this);
+	} else {
+		Script *script = node.handler->script;
+		bool isMethod = script->isFactory();
+		if (isMethod) {
+			write("method ");
+		} else {
+			write("on ");
+		}
+		write(node.handler->name);
+		if (node.handler->argumentNames.size() > 0) {
+			write(" ");
+			for (size_t i = 0; i < node.handler->argumentNames.size(); i++) {
+				if (i > 0)
+					write(", ");
+				write(node.handler->argumentNames[i]);
+			}
+		}
+		writeLine();
+		indent();
+		if (isMethod && script->propertyNames.size() > 0 && node.handler == &script->handlers[0]) {
+			write("instance ");
+			for (size_t i = 0; i < script->propertyNames.size(); i++) {
+				if (i > 0)
+					write(", ");
+				write(script->propertyNames[i]);
+			}
+			writeLine();
+		}
+		if (node.handler->globalNames.size() > 0) {
+			write("global ");
+			for (size_t i = 0; i < node.handler->globalNames.size(); i++) {
+				if (i > 0)
+					write(", ");
+				write(node.handler->globalNames[i]);
+			}
+			writeLine();
+		}
+		unindent();
+		node.block->accept(*this);
+		if (!isMethod) {
+			writeLine("end");
+		}
+	}
+}
+void CodeWriterVisitor::visit(const ErrorNode &) {
+	write("ERROR");
+}
+void CodeWriterVisitor::visit(const CommentNode &node) {
+	write("-- ");
+	write(node.text);
+}
+void CodeWriterVisitor::visit(const NewObjNode &node) {
+	write("new ");
+	write(node.objType);
+	write("(");
+	node.objArgs->accept(*this);
+	write(")");
+}
+void CodeWriterVisitor::visit(const LiteralNode &node) {
+	write(*node.value);
+}
+void CodeWriterVisitor::visit(const IfStmtNode &node) {
+	write("if ");
+	node.condition->accept(*this);
+	write(" then");
+	if (_sum) {
+		if (node.hasElse) {
+			write(" / else");
+		}
+	} else {
+		writeLine();
+		node.block1->accept(*this);
+		if (node.hasElse) {
+			writeLine("else");
+			node.block2->accept(*this);
+		}
+		write("end if");
+	}
+}
+void CodeWriterVisitor::visit(const EndCaseNode &) {
+	write("end case");
+}
+
+void CodeWriterVisitor::visit(const ObjCallNode &node) {
+	auto &rawArgs = node.argList->getValue()->l;
+
+	auto &obj = rawArgs[0];
+	bool parenObj = obj->hasSpaces(_dot);
+	if (parenObj) {
+		write("(");
+	}
+	obj->accept(*this);
+	if (parenObj) {
+		write(")");
+	}
+
+	write(".");
+	write(node.name);
+	write("(");
+	for (size_t i = 1; i < rawArgs.size(); i++) {
+		if (i > 1)
+			write(", ");
+		rawArgs[i]->accept(*this);
+	}
+	write(")");
+}
+void CodeWriterVisitor::visit(const PutStmtNode &node) {
+	write("put ");
+	node.value->accept(*this);
+	write(" ");
+	write(StandardNames::putTypeNames[node.type]);
+	write(" ");
+	node.variable->accept(*this); // TODO: we want the variable to always be verbose
+}
+void CodeWriterVisitor::visit(const TheExprNode &node) {
+	write("the ");
+	write(node.prop);
+}
+void CodeWriterVisitor::visit(const BinaryOpNode &node) {
+	unsigned int precedence = node.getPrecedence();
+	bool parenLeft = false;
+	bool parenRight = false;
+	if (precedence) {
+		if (node.left->type == kBinaryOpNode) {
+			auto leftBinaryOpNode = static_cast<BinaryOpNode *>(node.left.get());
+			parenLeft = (leftBinaryOpNode->getPrecedence() != precedence);
+		}
+		parenRight = (node.right->type == kBinaryOpNode);
+	}
+
+	if (parenLeft) {
+		write("(");
+	}
+	node.left->accept(*this);
+	if (parenLeft) {
+		write(")");
+	}
+
+	write(" ");
+	write(StandardNames::binaryOpNames[node.opcode]);
+	write(" ");
+
+	if (parenRight) {
+		write("(");
+	}
+	node.right->accept(*this);
+	if (parenRight) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const CaseStmtNode &node) {
+	write("case ");
+	node.value->accept(*this);
+	write(" of");
+	if (_sum) {
+		if (!node.firstLabel) {
+			if (node.otherwise) {
+				write(" / otherwise:");
+			} else {
+				write(" / end case");
+			}
+		}
+	} else {
+		writeLine();
+		indent();
+		if (node.firstLabel) {
+			node.firstLabel->accept(*this);
+		}
+		if (node.otherwise) {
+			node.otherwise->accept(*this);
+		}
+		unindent();
+		write("end case");
+	}
+}
+void CodeWriterVisitor::visit(const ExitStmtNode &) {
+	write("exit");
+}
+void CodeWriterVisitor::visit(const TellStmtNode &node) {
+	write("tell ");
+	node.window->accept(*this);
+	if (!_sum) {
+		writeLine();
+		node.block->accept(*this);
+		write("end tell");
+	}
+}
+void CodeWriterVisitor::visit(const WhenStmtNode &node) {
+	write("when ");
+	write(StandardNames::whenEventNames[node.event]);
+	write(" then");
+
+	for (size_t i = 0; i < node.script.size(); i++) {
+		char ch = node.script[i];
+		if (ch == '\r') {
+			if (i != node.script.size() - 1) {
+				writeLine();
+			}
+		} else {
+			write(ch);
+		}
+	}
+}
+
+void CodeWriterVisitor::visit(const CaseLabelNode &node) {
+	if (_sum) {
+		write("(case) ");
+		if (node.parent->type == kCaseLabelNode) {
+			auto parentLabel = static_cast<CaseLabelNode *>(node.parent);
+			if (parentLabel->nextOr.get() == &node) {
+				write("..., ");
+			}
+		}
+
+		bool parenValue = node.value->hasSpaces(_dot);
+		if (parenValue) {
+			write("(");
+		}
+		node.value->accept(*this);
+		if (parenValue) {
+			write(")");
+		}
+
+		if (node.nextOr) {
+			write(", ...");
+		} else {
+			write(":");
+		}
+	} else {
+		bool parenValue = node.value->hasSpaces(_dot);
+		if (parenValue) {
+			write("(");
+		}
+		node.value->accept(*this);
+		if (parenValue) {
+			write(")");
+		}
+
+		if (node.nextOr) {
+			write(", ");
+			node.nextOr->accept(*this);
+		} else {
+			writeLine(":");
+			node.block->accept(*this);
+		}
+		if (node.nextLabel) {
+			node.nextLabel->accept(*this);
+		}
+	}
+}
+void CodeWriterVisitor::visit(const ChunkExprNode &node) {
+	write(StandardNames::chunkTypeNames[node.type]);
+	write(" ");
+	node.first->accept(*this);
+	if (!(node.last->type == kLiteralNode && node.last->getValue()->type == kDatumInt && node.last->getValue()->i == 0)) {
+		write(" to ");
+		node.last->accept(*this);
+	}
+	write(" of ");
+	node.string->accept(*this); // TODO: we want the string to always be verbose
+}
+void CodeWriterVisitor::visit(const InverseOpNode &node) {
+	write("-");
+
+	bool parenOperand = node.operand->hasSpaces(_dot);
+	if (parenOperand) {
+		write("(");
+	}
+	node.operand->accept(*this);
+	if (parenOperand) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const ObjCallV4Node &node) {
+	node.obj->accept(*this);
+	write("(");
+	node.argList->accept(*this);
+	write(")");
+}
+void CodeWriterVisitor::visit(const OtherwiseNode &node) {
+	if (_sum) {
+		write("(case) otherwise:");
+	} else {
+		writeLine("otherwise:");
+		node.block->accept(*this);
+	}
+}
+void CodeWriterVisitor::visit(const MemberExprNode &node) {
+	bool hasCastID = node.castID && !(node.castID->type == kLiteralNode && node.castID->getValue()->type == kDatumInt && node.castID->getValue()->i == 0);
+	write(node.type);
+	if (_dot) {
+		write("(");
+		node.memberID->accept(*this);
+		if (hasCastID) {
+			write(", ");
+			node.castID->accept(*this);
+		}
+		write(")");
+	} else {
+		write(" ");
+
+		bool parenMemberID = (node.memberID->type == kBinaryOpNode);
+		if (parenMemberID) {
+			write("(");
+		}
+		node.memberID->accept(*this);
+		if (parenMemberID) {
+			write(")");
+		}
+
+		if (hasCastID) {
+			write(" of castLib ");
+
+			bool parenCastID = (node.castID->type == kBinaryOpNode);
+			if (parenCastID) {
+				write("(");
+			}
+			node.castID->accept(*this);
+			if (parenCastID) {
+				write(")");
+			}
+		}
+	}
+}
+void CodeWriterVisitor::visit(const ObjPropExprNode &node) {
+	if (_dot) {
+		bool parenObj = node.obj->hasSpaces(_dot);
+		if (parenObj) {
+			write("(");
+		}
+		node.obj->accept(*this);
+		if (parenObj) {
+			write(")");
+		}
+
+		write(".");
+		write(node.prop);
+	} else {
+		write("the ");
+		write(node.prop);
+		write(" of ");
+
+		bool parenObj = (node.obj->type == kBinaryOpNode);
+		if (parenObj) {
+			write("(");
+		}
+		node.obj->accept(*this);
+		if (parenObj) {
+			write(")");
+		}
+	}
+}
+void CodeWriterVisitor::visit(const PlayCmdStmtNode &node) {
+	auto &rawArgs = node.argList->getValue()->l;
+
+	write("play");
+
+	if (rawArgs.size() == 0) {
+		write(" done");
+		return;
+	}
+
+	auto &frame = rawArgs[0];
+	if (rawArgs.size() == 1) {
+		write(" frame ");
+		frame->accept(*this);
+		return;
+	}
+
+	auto &movie = rawArgs[1];
+	if (!(frame->type == kLiteralNode && frame->getValue()->type == kDatumInt && frame->getValue()->i == 1)) {
+		write(" frame ");
+		frame->accept(*this);
+		write(" of");
+	}
+	write(" movie ");
+	movie->accept(*this);
+}
+void CodeWriterVisitor::visit(const ThePropExprNode &node) {
+	write("the ");
+	write(node.prop);
+	write(" of ");
+
+	bool parenObj = (node.obj->type == kBinaryOpNode);
+	if (parenObj) {
+		write("(");
+	}
+	node.obj->accept(*this); // TODO: we want the object to always be verbose
+	if (parenObj) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const MenuPropExprNode &node) {
+	write("the ");
+	write(StandardNames::menuPropertyNames[node.prop]);
+	write(" of menu ");
+
+	bool parenMenuID = (node.menuID->type == kBinaryOpNode);
+	if (parenMenuID) {
+		write("(");
+	}
+	node.menuID->accept(*this);
+	if (parenMenuID) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const SoundCmdStmtNode &node) {
+	write("sound ");
+	write(node.cmd);
+	if (node.argList->getValue()->l.size() > 0) {
+		write(" ");
+		node.argList->accept(*this);
+	}
+}
+void CodeWriterVisitor::visit(const SoundPropExprNode &node) {
+	write("the ");
+	write(StandardNames::soundPropertyNames[node.prop]);
+	write(" of sound ");
+
+	bool parenSoundID = (node.soundID->type == kBinaryOpNode);
+	if (parenSoundID) {
+		write("(");
+	}
+	node.soundID->accept(*this);
+	if (parenSoundID) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const AssignmentStmtNode &node) {
+	if (!_dot) { // TODO: forceVerboseÃ’
+		write("set ");
+		node.variable->accept(*this); // TODO: we want the variable to always be verbose
+		write(" to ");
+		node.value->accept(*this);
+	} else {
+		node.variable->accept(*this);
+		write(" = ");
+		node.value->accept(*this);
+	}
+}
+void CodeWriterVisitor::visit(const ExitRepeatStmtNode &) {
+	write("exit repeat");
+}
+void CodeWriterVisitor::visit(const NextRepeatStmtNode &) {
+	write("next repeat");
+}
+void CodeWriterVisitor::visit(const ObjBracketExprNode &node) {
+	bool parenObj = node.obj->hasSpaces(_dot);
+	if (parenObj) {
+		write("(");
+	}
+	node.obj->accept(*this);
+	if (parenObj) {
+		write(")");
+	}
+
+	write("[");
+	node.prop->accept(*this);
+	write("]");
+}
+void CodeWriterVisitor::visit(const SpritePropExprNode &node) {
+	write("the ");
+	write(StandardNames::spritePropertyNames[node.prop]);
+	write(" of sprite ");
+
+	bool parenSpriteID = (node.spriteID->type == kBinaryOpNode);
+	if (parenSpriteID) {
+		write("(");
+	}
+	node.spriteID->accept(*this);
+	if (parenSpriteID) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const ChunkDeleteStmtNode &node) {
+	write("delete ");
+	node.chunk->accept(*this);
+}
+void CodeWriterVisitor::visit(const ChunkHiliteStmtNode &node) {
+	write("hilite ");
+	node.chunk->accept(*this);
+}
+void CodeWriterVisitor::visit(const RepeatWhileStmtNode &node) {
+	write("repeat while ");
+	node.condition->accept(*this);
+	if (!_sum) {
+		writeLine();
+		node.block->accept(*this);
+		write("end repeat");
+	}
+}
+void CodeWriterVisitor::visit(const MenuItemPropExprNode &node) {
+	write("the ");
+	write(StandardNames::menuItemPropertyNames[node.prop]);
+	write(" of menuItem ");
+
+	bool parenItemID = (node.itemID->type == kBinaryOpNode);
+	if (parenItemID) {
+		write("(");
+	}
+	node.itemID->accept(*this);
+	if (parenItemID) {
+		write(")");
+	}
+
+	write(" of menu ");
+
+	bool parenMenuID = (node.menuID->type ==kBinaryOpNode);
+	if (parenMenuID) {
+		write("(");
+	}
+	node.menuID->accept(*this);
+	if (parenMenuID) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const ObjPropIndexExprNode &node) {
+	bool parenObj = node.obj->hasSpaces(_dot);
+	if (parenObj) {
+		write("(");
+	}
+	node.obj->accept(*this);
+	if (parenObj) {
+		write(")");
+	}
+
+	write(".");
+	write(node.prop);
+	write("[");
+	node.index->accept(*this);
+	if (node.index2) {
+		write("..");
+		node.index2->accept(*this);
+	}
+	write("]");
+}
+void CodeWriterVisitor::visit(const RepeatWithInStmtNode &node) {
+	write("repeat with ");
+	write(node.varName);
+	write(" in ");
+	node.list->accept(*this);
+	if (!_sum) {
+		writeLine();
+		node.block->accept(*this);
+		write("end repeat");
+	}
+}
+void CodeWriterVisitor::visit(const RepeatWithToStmtNode &node) {
+	write("repeat with ");
+	write(node.varName);
+	write(" = ");
+	node.start->accept(*this);
+	if (node.up) {
+		write(" to ");
+	} else {
+		write(" down to ");
+	}
+	node.end->accept(*this);
+	if (!_sum) {
+		writeLine();
+		node.block->accept(*this);
+		write("end repeat");
+	}
+}
+void CodeWriterVisitor::visit(const SpriteWithinExprNode &node) {
+	write("sprite ");
+
+	bool parenFirstSprite = (node.firstSprite->type == kBinaryOpNode);
+	if (parenFirstSprite) {
+		write("(");
+	}
+	node.firstSprite->accept(*this);
+	if (parenFirstSprite) {
+		write(")");
+	}
+
+	write(" within ");
+
+	bool parenSecondSprite = (node.secondSprite->type == kBinaryOpNode);
+	if (parenSecondSprite) {
+		write("(");
+	}
+	node.secondSprite->accept(*this);
+	if (parenSecondSprite) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const LastStringChunkExprNode &node) {
+	write("the last ");
+	write(StandardNames::chunkTypeNames[node.type]);
+	write(" in ");
+
+	bool parenObj = (node.obj->type == kBinaryOpNode);
+	if (parenObj) {
+		write("(");
+	}
+	node.obj->accept(*this); // TODO: we want the object to always be verbose
+	if (parenObj) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const SpriteIntersectsExprNode &node) {
+	write("sprite ");
+
+	bool parenFirstSprite = (node.firstSprite->type == kBinaryOpNode);
+	if (parenFirstSprite) {
+		write("(");
+	}
+	node.firstSprite->accept(*this);
+	if (parenFirstSprite) {
+		write(")");
+	}
+
+	write(" intersects ");
+
+	bool parenSecondSprite = (node.secondSprite->type == kBinaryOpNode);
+	if (parenSecondSprite) {
+		write("(");
+	}
+	node.secondSprite->accept(*this);
+	if (parenSecondSprite) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const StringChunkCountExprNode &node) {
+	write("the number of ");
+	write(StandardNames::chunkTypeNames[node.type]); // we want the object to always be verbose
+	write("s in ");
+
+	bool parenObj = (node.obj->type == kBinaryOpNode);
+	if (parenObj) {
+		write("(");
+	}
+	node.obj->accept(*this); // TODO dot false?
+	if (parenObj) {
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const VarNode &node) {
+	write(node.varName);
+}
+void CodeWriterVisitor::visit(const CallNode &node) {
+	if (node.isExpression && node.argList->getValue()->l.size() == 0) {
+		if (node.name == "pi") {
+			write("PI");
+			return;
+		}
+		if (node.name == "space") {
+			write("SPACE");
+			return;
+		}
+		if (node.name == "void") {
+			write("VOID");
+			return;
+		}
+	}
+
+	if (!_dot && node.isMemberExpr()) {
+		/**
+		 * In some cases, member expressions such as `member 1 of castLib 1` compile
+		 * to the function call `member(1, 1)`. However, this doesn't parse correctly
+		 * in pre-dot-syntax versions of Director, and `put(member(1, 1))` does not
+		 * compile. Therefore, we rewrite these expressions to the verbose syntax when
+		 * in verbose mode.
+		 */
+		write(node.name);
+		write(" ");
+
+		auto memberID = node.argList->getValue()->l[0];
+		bool parenMemberID = (memberID->type == kBinaryOpNode);
+		if (parenMemberID) {
+			write("(");
+		}
+		memberID->accept(*this);
+		if (parenMemberID) {
+			write(")");
+		}
+
+		if (node.argList->getValue()->l.size() == 2) {
+			write(" of castLib ");
+
+			auto castID = node.argList->getValue()->l[1];
+			bool parenCastID = (castID->type == kBinaryOpNode);
+			if (parenCastID) {
+				write("(");
+			}
+			castID->accept(*this);
+			if (parenCastID) {
+				write(")");
+			}
+		}
+		return;
+	}
+
+	write(node.name);
+	if (node.noParens()) {
+		write(" ");
+		node.argList->accept(*this);
+	} else {
+		write("(");
+		node.argList->accept(*this);
+		write(")");
+	}
+}
+void CodeWriterVisitor::visit(const BlockNode &node) {
+	indent();
+	for (const auto &child : node.children) {
+		child->accept(*this);
+		writeLine();
+	}
+	unindent();
+}
+void CodeWriterVisitor::visit(const NotOpNode &node) {
+	write("not ");
+
+	bool parenOperand = node.operand->hasSpaces(_dot);
+	if (parenOperand) {
+		write("(");
+	}
+	node.operand->accept(*this);
+	if (parenOperand) {
+		write(")");
+	}
+}
+
+void CodeWriterVisitor::indent() {
+	_indent++;
+}
+
+void CodeWriterVisitor::unindent() {
+	if (_indent > 0)
+		_indent--;
+}
+
+void CodeWriterVisitor::writeIndentation() {
+	if (_indentWritten)
+		return;
+
+	for (int i = 0; i < _indent; i++) {
+		_str += _indentation;
+	}
+
+	_indentWritten = true;
+	_lineWidth = _indent * _indentation.size();
+}
+
+void CodeWriterVisitor::write(char c) {
+	writeIndentation();
+	_str += c;
+	_lineWidth++;
+}
+
+void CodeWriterVisitor::write(const Common::String &s) {
+	writeIndentation();
+	_str += s;
+	_lineWidth += s.size();
+}
+
+void CodeWriterVisitor::writeLine() {
+	_str += _lineEnding;
+	_lineWidth += _lineEnding.size();
+	_indentWritten = false;
+	_lineWidth = 0;
+}
+
+void CodeWriterVisitor::writeLine(const Common::String &s) {
+	writeIndentation();
+	_str += s;
+	_lineWidth += s.size();
+	_str += _lineEnding;
+	_lineWidth += _lineEnding.size();
+	_indentWritten = false;
+	_lineWidth = 0;
+}
+
+void CodeWriterVisitor::write(Datum &datum) {
+	switch (datum.type) {
+	case kDatumVoid:
+		write("VOID");
+		return;
+	case kDatumSymbol:
+		write("#" + datum.s);
+		return;
+	case kDatumVarRef:
+		write(datum.s);
+		return;
+	case kDatumString:
+		if (datum.s.size() == 0) {
+			write("EMPTY");
+			return;
+		}
+		if (datum.s.size() == 1) {
+			switch (datum.s[0]) {
+			case '\x03':
+				write("ENTER");
+				return;
+			case '\x08':
+				write("BACKSPACE");
+				return;
+			case '\t':
+				write("TAB");
+				return;
+			case '\r':
+				write("RETURN");
+				return;
+			case '"':
+				write("QUOTE");
+				return;
+			default:
+				break;
+			}
+		}
+		if (_sum) {
+			write("\"" + Common::toPrintable(datum.s) + "\"");
+			return;
+		}
+		write("\"" + datum.s + "\"");
+		return;
+	case kDatumInt:
+		write(Common::String::format("%d", datum.i));
+		return;
+	case kDatumFloat:
+		write(Common::String::format("%g", datum.f));
+		return;
+	case kDatumList:
+	case kDatumArgList:
+	case kDatumArgListNoRet: {
+		if (datum.type == kDatumList)
+			write("[");
+		for (size_t ii = 0; ii < datum.l.size(); ii++) {
+			if (ii > 0)
+				write(", ");
+			datum.l[ii]->accept(*this);
+		}
+		if (datum.type == kDatumList)
+			write("]");
+	}
+		return;
+	case kDatumPropList: {
+		write("[");
+		if (datum.l.size() == 0) {
+			write(":");
+		} else {
+			for (size_t ii = 0; ii < datum.l.size(); ii += 2) {
+				if (ii > 0)
+					write(", ");
+				datum.l[ii]->accept(*this);
+				write(": ");
+				datum.l[ii + 1]->accept(*this);
+			}
+		}
+		write("]");
+	}
+		return;
+	}
+}
+
+} // namespace LingoDec
diff --git a/engines/director/lingo/lingodec/codewritervisitor.h b/engines/director/lingo/lingodec/codewritervisitor.h
new file mode 100644
index 00000000000..aaa4e2e43bb
--- /dev/null
+++ b/engines/director/lingo/lingodec/codewritervisitor.h
@@ -0,0 +1,92 @@
+/*
+ * 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/.
+ */
+
+#ifndef LINGODEC_CODEWRITERVISITOR_H
+#define LINGODEC_CODEWRITERVISITOR_H
+
+#include "./ast.h"
+
+namespace LingoDec {
+
+class CodeWriterVisitor: public LingoDec::NodeVisitor {
+public:
+	CodeWriterVisitor(bool dotSyntax, bool sum, const Common::String &lineEnding = "\n", const Common::String &indentation = "  ")
+		: _dot(dotSyntax), _sum(sum), _lineEnding(lineEnding), _indentation(indentation) {}
+	virtual ~CodeWriterVisitor() {}
+	virtual void visit(const LingoDec::HandlerNode& node) override;
+	virtual void visit(const LingoDec::ErrorNode& node) override;
+	virtual void visit(const LingoDec::CommentNode& node) override;
+	virtual void visit(const LingoDec::NewObjNode& node) override;
+	virtual void visit(const LingoDec::LiteralNode& node) override;
+	virtual void visit(const LingoDec::IfStmtNode& node) override;
+	virtual void visit(const LingoDec::EndCaseNode& node) override;
+	virtual void visit(const LingoDec::ObjCallNode& node) override;
+	virtual void visit(const LingoDec::PutStmtNode& node) override;
+	virtual void visit(const LingoDec::TheExprNode& node) override;
+	virtual void visit(const LingoDec::BinaryOpNode& node) override;
+	virtual void visit(const LingoDec::CaseStmtNode& node) override;
+	virtual void visit(const LingoDec::ExitStmtNode& node) override;
+	virtual void visit(const LingoDec::TellStmtNode& node) override;
+	virtual void visit(const LingoDec::WhenStmtNode& node) override;
+	virtual void visit(const LingoDec::CaseLabelNode& node) override;
+	virtual void visit(const LingoDec::ChunkExprNode& node) override;
+	virtual void visit(const LingoDec::InverseOpNode& node) override;
+	virtual void visit(const LingoDec::ObjCallV4Node& node) override;
+	virtual void visit(const LingoDec::OtherwiseNode& node) override;
+	virtual void visit(const LingoDec::MemberExprNode& node) override;
+	virtual void visit(const LingoDec::ObjPropExprNode& node) override;
+	virtual void visit(const LingoDec::PlayCmdStmtNode& node) override;
+	virtual void visit(const LingoDec::ThePropExprNode& node) override;
+	virtual void visit(const LingoDec::MenuPropExprNode& node) override;
+	virtual void visit(const LingoDec::SoundCmdStmtNode& node) override;
+	virtual void visit(const LingoDec::SoundPropExprNode& node) override;
+	virtual void visit(const LingoDec::AssignmentStmtNode& node) override;
+	virtual void visit(const LingoDec::ExitRepeatStmtNode& node) override;
+	virtual void visit(const LingoDec::NextRepeatStmtNode& node) override;
+	virtual void visit(const LingoDec::ObjBracketExprNode& node) override;
+	virtual void visit(const LingoDec::SpritePropExprNode& node) override;
+	virtual void visit(const LingoDec::ChunkDeleteStmtNode& node) override;
+	virtual void visit(const LingoDec::ChunkHiliteStmtNode& node) override;
+	virtual void visit(const LingoDec::RepeatWhileStmtNode& node) override;
+	virtual void visit(const LingoDec::MenuItemPropExprNode& node) override;
+	virtual void visit(const LingoDec::ObjPropIndexExprNode& node) override;
+	virtual void visit(const LingoDec::RepeatWithInStmtNode& node) override;
+	virtual void visit(const LingoDec::RepeatWithToStmtNode& node) override;
+	virtual void visit(const LingoDec::SpriteWithinExprNode& node) override;
+	virtual void visit(const LingoDec::LastStringChunkExprNode& node) override;
+	virtual void visit(const LingoDec::SpriteIntersectsExprNode& node) override;
+	virtual void visit(const LingoDec::StringChunkCountExprNode& node) override;
+	virtual void visit(const LingoDec::VarNode& node) override;
+	virtual void visit(const LingoDec::CallNode& node) override;
+	virtual void visit(const LingoDec::BlockNode& node) override;
+	virtual void visit(const LingoDec::NotOpNode& node) override;
+
+	size_t lineWidth() const { return _lineWidth; }
+	void indent();
+	void unindent();
+	void writeIndentation();
+	void write(char c);
+	void write(const Common::String& s);
+	void writeLine();
+	void writeLine(const Common::String& s);
+	void write(LingoDec::Datum& datum);
+
+public:
+	Common::String _str;
+
+private:
+	bool _dot = false;
+	bool _sum = false;
+	Common::String _lineEnding;
+	Common::String _indentation = "  ";
+	bool _indentWritten = false;
+	int _indent = 0;
+	size_t _lineWidth = 0;
+};
+
+} // namespace LingoDec
+
+#endif // LINGODEC_CODEWRITERVISITOR_H
diff --git a/engines/director/lingo/lingodec/handler.cpp b/engines/director/lingo/lingodec/handler.cpp
index a8da30ae7ac..909d93823a9 100644
--- a/engines/director/lingo/lingodec/handler.cpp
+++ b/engines/director/lingo/lingodec/handler.cpp
@@ -7,7 +7,7 @@
 #include "common/stream.h"
 #include "common/util.h"
 #include "./ast.h"
-#include "./codewriter.h"
+#include "./codewritervisitor.h"
 #include "./handler.h"
 #include "./names.h"
 #include "./script.h"
@@ -1249,7 +1249,7 @@ Common::String posToString(int32 pos) {
 	return Common::String::format("[%3d]", pos);
 }
 
-void Handler::writeBytecodeText(CodeWriter &code, bool dotSyntax) const {
+void Handler::writeBytecodeText(CodeWriterVisitor &code) const {
 	bool isMethod = script->isFactory();
 
 	if (!isGenericEvent) {
@@ -1304,7 +1304,7 @@ void Handler::writeBytecodeText(CodeWriter &code, bool dotSyntax) const {
 			if (bytecode.translation->isExpression) {
 				code.write("<");
 			}
-			bytecode.translation->writeScriptText(code, dotSyntax, true);
+			bytecode.translation->accept(code);
 			if (bytecode.translation->isExpression) {
 				code.write(">");
 			}
diff --git a/engines/director/lingo/lingodec/handler.h b/engines/director/lingo/lingodec/handler.h
index ae6ff0034e0..d6cbb3536bf 100644
--- a/engines/director/lingo/lingodec/handler.h
+++ b/engines/director/lingo/lingodec/handler.h
@@ -20,7 +20,7 @@ namespace LingoDec {
 
 struct AST;
 struct Bytecode;
-class CodeWriter;
+class CodeWriterVisitor;
 struct Node;
 struct Script;
 
@@ -85,7 +85,7 @@ struct Handler {
 	BytecodeTag identifyLoop(uint32 startIndex, uint32 endIndex);
 	void parse();
 	uint32 translateBytecode(Bytecode &bytecode, uint32 index);
-	void writeBytecodeText(CodeWriter &code, bool dotSyntax) const;
+	void writeBytecodeText(CodeWriterVisitor &code) const;
 };
 
 /* Bytecode */
diff --git a/engines/director/lingo/lingodec/script.cpp b/engines/director/lingo/lingodec/script.cpp
index 59e783090e0..77949bb2ae1 100644
--- a/engines/director/lingo/lingodec/script.cpp
+++ b/engines/director/lingo/lingodec/script.cpp
@@ -6,7 +6,7 @@
 
 #include "common/stream.h"
 #include "./ast.h"
-#include "./codewriter.h"
+#include "./codewritervisitor.h"
 #include "./context.h"
 #include "./handler.h"
 #include "./script.h"
@@ -127,7 +127,7 @@ void Script::parse() {
 	}
 }
 
-void Script::writeVarDeclarations(CodeWriter &code) const {
+void Script::writeVarDeclarations(CodeWriterVisitor &code) const {
 	if (!isFactory()) {
 		if (propertyNames.size() > 0) {
 			code.write("property ");
@@ -150,64 +150,64 @@ void Script::writeVarDeclarations(CodeWriter &code) const {
 	}
 }
 
-void Script::writeScriptText(CodeWriter &code, bool dotSyntax) const {
-	size_t origSize = code.size();
+void Script::writeScriptText(CodeWriterVisitor &code) const {
+	size_t origSize = code._str.size();
 	writeVarDeclarations(code);
 	if (isFactory()) {
-		if (code.size() != origSize) {
+		if (code._str.size() != origSize) {
 			code.writeLine();
 		}
 		code.write("factory ");
 		code.writeLine(factoryName);
 	}
 	for (size_t i = 0; i < handlers.size(); i++) {
-		if ((!isFactory() || i > 0) && code.size() != origSize) {
+		if ((!isFactory() || i > 0) && code._str.size() != origSize) {
 			code.writeLine();
 		}
-		handlers[i].ast.writeScriptText(code, dotSyntax, false);
+		handlers[i].ast.root->accept(code);
 	}
 	for (auto factory : factories) {
-		if (code.size() != origSize) {
+		if (code._str.size() != origSize) {
 			code.writeLine();
 		}
-		factory->writeScriptText(code, dotSyntax);
+		factory->writeScriptText(code);
 	}
 }
 
 Common::String Script::scriptText(const char *lineEnding, bool dotSyntax) const {
-	CodeWriter code(lineEnding);
-	writeScriptText(code, dotSyntax);
-	return code.str();
+	CodeWriterVisitor code(dotSyntax, false, lineEnding);
+	writeScriptText(code);
+	return code._str;
 }
 
-void Script::writeBytecodeText(CodeWriter &code, bool dotSyntax) const {
-	size_t origSize = code.size();
+void Script::writeBytecodeText(CodeWriterVisitor &code) const {
+	size_t origSize = code._str.size();
 	writeVarDeclarations(code);
 	if (isFactory()) {
-		if (code.size() != origSize) {
+		if (code._str.size() != origSize) {
 			code.writeLine();
 		}
 		code.write("factory ");
 		code.writeLine(factoryName);
 	}
 	for (size_t i = 0; i < handlers.size(); i++) {
-		if ((!isFactory() || i > 0) && code.size() != origSize) {
+		if ((!isFactory() || i > 0) && code._str.size() != origSize) {
 			code.writeLine();
 		}
-		handlers[i].writeBytecodeText(code, dotSyntax);
+		handlers[i].writeBytecodeText(code);
 	}
 	for (auto factory : factories) {
-		if (code.size() != origSize) {
+		if (code._str.size() != origSize) {
 			code.writeLine();
 		}
-		factory->writeBytecodeText(code, dotSyntax);
+		factory->writeBytecodeText(code);
 	}
 }
 
 Common::String Script::bytecodeText(const char *lineEnding, bool dotSyntax) const {
-	CodeWriter code(lineEnding);
-	writeBytecodeText(code, dotSyntax);
-	return code.str();
+	CodeWriterVisitor code(dotSyntax, true, lineEnding);
+	writeBytecodeText(code);
+	return code._str;
 }
 
 bool Script::isFactory() const {
diff --git a/engines/director/lingo/lingodec/script.h b/engines/director/lingo/lingodec/script.h
index 33945ee4c81..53cb6bcdbd7 100644
--- a/engines/director/lingo/lingodec/script.h
+++ b/engines/director/lingo/lingodec/script.h
@@ -17,7 +17,7 @@ class ReadStream;
 
 namespace LingoDec {
 
-class CodeWriter;
+class CodeWriterVisitor;
 struct Datum;
 struct Handler;
 struct ScriptContext;
@@ -82,10 +82,10 @@ struct Script {
 	Common::String getName(int id) const;
 	void setContext(ScriptContext *ctx);
 	void parse();
-	void writeVarDeclarations(CodeWriter &code) const;
-	void writeScriptText(CodeWriter &code, bool dotSyntax) const;
+	void writeVarDeclarations(CodeWriterVisitor &code) const;
+	void writeScriptText(CodeWriterVisitor &code) const;
 	Common::String scriptText(const char *lineEnding, bool dotSyntax) const;
-	void writeBytecodeText(CodeWriter &code, bool dotSyntax) const;
+	void writeBytecodeText(CodeWriterVisitor &code) const;
 	Common::String bytecodeText(const char *lineEnding, bool dotSyntax) const;
 
 	bool isFactory() const;
diff --git a/engines/director/module.mk b/engines/director/module.mk
index 36d494a4411..6f8d817928d 100644
--- a/engines/director/module.mk
+++ b/engines/director/module.mk
@@ -55,7 +55,7 @@ MODULE_OBJS = \
 	lingo/lingo-utils.o \
 	lingo/lingodec/ast.o \
 	lingo/lingodec/context.o \
-	lingo/lingodec/codewriter.o \
+	lingo/lingodec/codewritervisitor.o \
 	lingo/lingodec/handler.o \
 	lingo/lingodec/names.o \
 	lingo/lingodec/script.o \




More information about the Scummvm-git-logs mailing list