[Scummvm-git-logs] scummvm master -> f600534707504b3a67f8e61298a507dea2fc292f

scemino noreply at scummvm.org
Fri May 17 12:18:01 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:
f600534707 DIRECTOR: Start to add code decompiler (WIP)


Commit: f600534707504b3a67f8e61298a507dea2fc292f
    https://github.com/scummvm/scummvm/commit/f600534707504b3a67f8e61298a507dea2fc292f
Author: scemino (scemino74 at gmail.com)
Date: 2024-05-17T14:17:41+02:00

Commit Message:
DIRECTOR: Start to add code decompiler (WIP)

Changed paths:
    engines/director/cast.cpp
    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


diff --git a/engines/director/cast.cpp b/engines/director/cast.cpp
index 7f7394e295c..39a8ed9fe09 100644
--- a/engines/director/cast.cpp
+++ b/engines/director/cast.cpp
@@ -1146,7 +1146,7 @@ public:
 		Common::SeekableReadStreamEndian *r;
 
 		r = _cast->_castArchive->getResource(MKTAG('L', 's', 'c', 'r'), id);
-		_scripts[id] = new LingoDec::Script(_cast->_version);
+		_scripts[id] = new LingoDec::Script(g_director->getVersion());
 		_scripts[id]->read(*r);
 		delete r;
 
@@ -1264,7 +1264,6 @@ void Cast::loadLingoContext(Common::SeekableReadStreamEndian &stream) {
 		error("Cast::loadLingoContext: unsupported Director version (%d)", _version);
 	}
 
-#if 0
 	// Rewind stream
 	stream.seek(0);
 	_chunkResolver = new ChunkResolver(this);
@@ -1274,10 +1273,8 @@ void Cast::loadLingoContext(Common::SeekableReadStreamEndian &stream) {
 	_lingodec->parseScripts();
 
 	for (auto it = _lingodec->scripts.begin(); it != _lingodec->scripts.end(); ++it) {
-		warning("%s", it->second->scriptText("\n", false).c_str());
+		warning("[%d/%d] %s", it->second->castID, it->first, it->second->scriptText("\n", false).c_str());
 	}
-
-#endif
 }
 
 void Cast::loadScriptV2(Common::SeekableReadStreamEndian &stream, uint16 id) {
diff --git a/engines/director/debugtools.cpp b/engines/director/debugtools.cpp
index 5d64c7806c8..e1472eaedfb 100644
--- a/engines/director/debugtools.cpp
+++ b/engines/director/debugtools.cpp
@@ -31,6 +31,12 @@
 #include "director/director.h"
 #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/context.h"
+#include "director/lingo/lingodec/handler.h"
+#include "director/lingo/lingodec/resolver.h"
+#include "director/lingo/lingodec/script.h"
 #include "director/cast.h"
 #include "director/castmember/bitmap.h"
 #include "director/castmember/castmember.h"
@@ -54,7 +60,7 @@ typedef struct ImGuiImage {
 } ImGuiImage;
 
 typedef struct ImGuiScriptCode {
-	uint pc;
+	uint32 pc;
 	Common::String code;
 } ImGuiScriptCode;
 
@@ -75,6 +81,167 @@ typedef struct ImGuiScript {
 	}
 } ImGuiScript;
 
+class ImGuiNodeVisitor : public LingoDec::NodeVisitor {
+public:
+	explicit ImGuiNodeVisitor(ImGuiScript &script) : _script(script) {}
+
+	virtual void visit(const LingoDec::BlockNode &node) override {
+		_indent++;
+		for (const auto &child : node.children) {
+			child->accept(*this);
+		}
+		_indent--;
+	}
+
+	virtual void visit(const LingoDec::HandlerNode &node) override {
+		if (node.handler->isGenericEvent) {
+			node.block->accept(*this);
+			return;
+		}
+
+		LingoDec::Script *script = node.handler->script;
+		bool isMethod = script->isFactory();
+		{
+			LingoDec::CodeWriter code("\n");
+			if (isMethod) {
+				code.write("method ");
+			} else {
+				code.write("on ");
+			}
+			code.write(node.handler->name);
+
+			if (node.handler->argumentNames.size() > 0) {
+				code.write(" ");
+				for (size_t i = 0; i < node.handler->argumentNames.size(); i++) {
+					if (i > 0)
+						code.write(", ");
+					code.write(node.handler->argumentNames[i]);
+				}
+			}
+			write(node._startOffset, code.str());
+		}
+
+		{
+			LingoDec::CodeWriter code("\n");
+			if (isMethod && node.handler->script->propertyNames.size() > 0 && node.handler == &node.handler->script->handlers[0]) {
+				code.write("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]);
+				}
+				write(node._startOffset, code.str());
+			}
+		}
+
+		{
+			if (node.handler->globalNames.size() > 0) {
+				LingoDec::CodeWriter code("\n");
+				code.write("global ");
+				for (size_t i = 0; i < node.handler->globalNames.size(); i++) {
+					if (i > 0)
+						code.write(", ");
+					code.write(node.handler->globalNames[i]);
+				}
+				write(node._startOffset, code.str());
+			}
+		}
+
+		node.block->accept(*this);
+
+		{
+			LingoDec::CodeWriter code("\n");
+			if (!isMethod) {
+				code.writeLine("end");
+			}
+			_script.code.push_back({node.block->_endOffset, code.str()});
+		}
+	}
+
+	virtual void visit(const LingoDec::RepeatWhileStmtNode &node) override {
+		LingoDec::CodeWriter code("\n");
+		code.write("repeat while ");
+		node.condition->writeScriptText(code, _dot, false);
+		write(node._startOffset, code.str());
+
+		node.block->accept(*this);
+
+		write(node._endOffset, "end repeat");
+	}
+
+	virtual void visit(const LingoDec::RepeatWithInStmtNode &node) override {
+		LingoDec::CodeWriter code("\n");
+		code.write("repeat with ");
+		code.write(node.varName);
+		code.write(" in ");
+		node.list->writeScriptText(code, _dot, false);
+		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");
+		code.write("repeat with ");
+		code.write(node.varName);
+		code.write(" = ");
+		node.start->writeScriptText(code, _dot, false);
+		if (node.up) {
+			code.write(" to ");
+		} else {
+			code.write(" down to ");
+		}
+		node.end->writeScriptText(code, _dot, false);
+		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");
+			code.write("if ");
+			node.condition->writeScriptText(code, _dot, false);
+			code.write(" then");
+			write(node._startOffset, code.str());
+		}
+		node.block1->accept(*this);
+		if (node.hasElse) {
+			write(node.block2->_startOffset, "else");
+			node.block2->accept(*this);
+		}
+		write(node._endOffset, "end if");
+	}
+
+	virtual void visit(const LingoDec::TellStmtNode &node) override {
+		LingoDec::CodeWriter code("\n");
+		code.write("tell ");
+		node.window->writeScriptText(code, _dot, false);
+		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());
+	}
+
+	void write(uint32 offset, const Common::String &code) {
+		Common::String s;
+		for (int i = 0; i < _indent; i++) {
+			s += "  ";
+		}
+		_script.code.push_back({offset, s + code});
+	}
+
+private:
+	ImGuiScript &_script;
+	bool _dot = false;
+	int _indent = 0;
+};
+
 typedef struct ImGuiState {
 	struct {
 		Common::HashMap<Graphics::Surface *, ImGuiImage> _textures;
@@ -1025,7 +1192,7 @@ static void displayScripts() {
 	ImGui::End();
 }
 
-static void getScriptCode(ImGuiScript& script, Symbol &sym) {
+static void getScriptCode(ImGuiScript &script, Symbol &sym) {
 	uint pc = 0;
 	while (pc < sym.u.defn->size()) {
 		script.code.push_back({pc, g_lingo->decodeInstruction(sym.u.defn, pc, &pc)});
@@ -1090,11 +1257,10 @@ static void showFuncList() {
 					ImGui::Text("-");
 					ImGui::TableNextColumn();
 					ImGui::Text("%s", scriptType.c_str());
-
 				}
 			}
 
-			for (auto cast : *movie->getCasts()) {
+			for (auto &cast : *movie->getCasts()) {
 				for (int i = 0; i <= kMaxScriptType; i++) {
 					if (cast._value->_lingoArchive->scriptContexts[i].empty())
 						continue;
@@ -1120,7 +1286,12 @@ static void showFuncList() {
 								script.type = (ScriptType)i;
 								script.handlerId = functionHandler._key;
 								script.handlerName = getHandlerName(functionHandler._value);
-								getScriptCode(script, functionHandler._value);
+								uint32 scriptId = movie->getCastMemberInfo(memberID)->scriptId;
+								const LingoDec::Script *s = cast._value->_lingodec->scripts[scriptId];
+								ImGuiNodeVisitor visitor(script);
+								for (auto &h : s->handlers) {
+									h.ast.root->accept(visitor);
+								}
 								setScriptToDisplay(script);
 							}
 							ImGui::TableNextColumn();
@@ -1129,7 +1300,6 @@ static void showFuncList() {
 							ImGui::Text("%d", cast._key);
 							ImGui::TableNextColumn();
 							ImGui::Text("%s", scriptType.c_str());
-
 						}
 					}
 				}
diff --git a/engines/director/lingo/lingodec/ast.cpp b/engines/director/lingo/lingodec/ast.cpp
index 49aa410ac6f..044255ab28e 100644
--- a/engines/director/lingo/lingodec/ast.cpp
+++ b/engines/director/lingo/lingodec/ast.cpp
@@ -683,8 +683,8 @@ void CaseStmtNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
 	}
 }
 
-void CaseStmtNode::addOtherwise() {
-	otherwise = Common::SharedPtr<OtherwiseNode>(new OtherwiseNode());
+void CaseStmtNode::addOtherwise(uint32 offset) {
+	otherwise = Common::SharedPtr<OtherwiseNode>(new OtherwiseNode(offset));
 	otherwise->parent = this;
 	otherwise->block->endPos = endPos;
 }
@@ -1166,4 +1166,52 @@ void NewObjNode::writeScriptText(CodeWriter &code, bool dot, bool sum) const {
 	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); }
+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
diff --git a/engines/director/lingo/lingodec/ast.h b/engines/director/lingo/lingodec/ast.h
index caeb9bf6e1f..3ec33b7e22f 100644
--- a/engines/director/lingo/lingodec/ast.h
+++ b/engines/director/lingo/lingodec/ast.h
@@ -55,6 +55,8 @@ struct Datum {
 	void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
 };
 
+class NodeVisitor;
+
 /* Node */
 
 struct Node {
@@ -64,10 +66,13 @@ struct Node {
 	bool isLabel;
 	bool isLoop;
 	Node *parent;
+	uint32 _startOffset;
+	uint32 _endOffset;
 
-	Node(NodeType t) : type(t), isExpression(false), isStatement(false), isLabel(false), isLoop(false), parent(nullptr) {}
+	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();
 	LoopNode *ancestorLoop();
@@ -77,7 +82,7 @@ struct Node {
 /* ExprNode */
 
 struct ExprNode : Node {
-	ExprNode(NodeType t) : Node(t) {
+	ExprNode(NodeType t, uint32 offset) : Node(t, offset) {
 		isExpression = true;
 	}
 };
@@ -85,7 +90,7 @@ struct ExprNode : Node {
 /* StmtNode */
 
 struct StmtNode : Node {
-	StmtNode(NodeType t) : Node(t) {
+	StmtNode(NodeType t, uint32 offset) : Node(t, offset) {
 		isStatement = true;
 	}
 };
@@ -93,7 +98,7 @@ struct StmtNode : Node {
 /* LabelNode */
 
 struct LabelNode : Node {
-	LabelNode(NodeType t) : Node(t) {
+	LabelNode(NodeType t, uint32 offset) : Node(t, offset) {
 		isLabel = true;
 	}
 };
@@ -103,7 +108,7 @@ struct LabelNode : Node {
 struct LoopNode : StmtNode {
 	uint32 startIndex;
 
-	LoopNode(NodeType t, uint32 startIndex_) : StmtNode(t), startIndex(startIndex_) {
+	LoopNode(NodeType t, uint32 startIndex_, uint32 offset) : StmtNode(t, offset), startIndex(startIndex_) {
 		isLoop = true;
 	}
 };
@@ -111,9 +116,10 @@ struct LoopNode : StmtNode {
 /* ErrorNode */
 
 struct ErrorNode : ExprNode {
-	ErrorNode() : ExprNode(kErrorNode) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual bool hasSpaces(bool dot);
+	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;
 };
 
 /* CommentNode */
@@ -121,8 +127,9 @@ struct ErrorNode : ExprNode {
 struct CommentNode : Node {
 	Common::String text;
 
-	CommentNode(Common::String t) : Node(kCommentNode), text(t) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	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;
 };
 
 /* LiteralNode */
@@ -130,12 +137,13 @@ struct CommentNode : Node {
 struct LiteralNode : ExprNode {
 	Common::SharedPtr<Datum> value;
 
-	LiteralNode(Common::SharedPtr<Datum> d) : ExprNode(kLiteralNode) {
+	LiteralNode(uint32 offset, Common::SharedPtr<Datum> d) : ExprNode(kLiteralNode, offset) {
 		value = Common::move(d);
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual Common::SharedPtr<Datum> getValue();
-	virtual bool hasSpaces(bool dot);
+	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;
 };
 
 /* BlockNode */
@@ -147,9 +155,10 @@ struct BlockNode : Node {
 	uint32 endPos;
 	CaseLabelNode *currentCaseLabel;
 
-	BlockNode() : Node(kBlockNode), endPos(-1), currentCaseLabel(nullptr) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	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;
 };
 
 /* HandlerNode */
@@ -158,19 +167,21 @@ struct HandlerNode : Node {
 	Handler *handler;
 	Common::SharedPtr<BlockNode> block;
 
-	HandlerNode(Handler *h)
-		: Node(kHandlerNode), handler(h) {
-		block = Common::SharedPtr<BlockNode>(new BlockNode());
+	HandlerNode(uint32 offset, Handler *h)
+		: Node(kHandlerNode, offset), handler(h) {
+		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ExitStmtNode */
 
 struct ExitStmtNode : StmtNode {
-	ExitStmtNode() : StmtNode(kExitStmtNode) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	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;
 };
 
 /* InverseOpNode */
@@ -178,11 +189,12 @@ struct ExitStmtNode : StmtNode {
 struct InverseOpNode : ExprNode {
 	Common::SharedPtr<Node> operand;
 
-	InverseOpNode(Common::SharedPtr<Node> o) : ExprNode(kInverseOpNode) {
+	InverseOpNode(uint32 offset, Common::SharedPtr<Node> o) : ExprNode(kInverseOpNode, offset) {
 		operand = Common::move(o);
 		operand->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* NotOpNode */
@@ -190,11 +202,12 @@ struct InverseOpNode : ExprNode {
 struct NotOpNode : ExprNode {
 	Common::SharedPtr<Node> operand;
 
-	NotOpNode(Common::SharedPtr<Node> o) : ExprNode(kNotOpNode) {
+	NotOpNode(uint32 offset, Common::SharedPtr<Node> o) : ExprNode(kNotOpNode, offset) {
 		operand = Common::move(o);
 		operand->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* BinaryOpNode */
@@ -204,15 +217,16 @@ struct BinaryOpNode : ExprNode {
 	Common::SharedPtr<Node> left;
 	Common::SharedPtr<Node> right;
 
-	BinaryOpNode(OpCode op, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
-		: ExprNode(kBinaryOpNode), opcode(op) {
+	BinaryOpNode(uint32 offset, OpCode op, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
+		: ExprNode(kBinaryOpNode, offset), opcode(op) {
 		left = Common::move(a);
 		left->parent = this;
 		right = Common::move(b);
 		right->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
 	virtual unsigned int getPrecedence() const;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ChunkExprNode */
@@ -223,8 +237,8 @@ struct ChunkExprNode : ExprNode {
 	Common::SharedPtr<Node> last;
 	Common::SharedPtr<Node> string;
 
-	ChunkExprNode(ChunkExprType t, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b, Common::SharedPtr<Node> s)
-		: ExprNode(kChunkExprNode), type(t) {
+	ChunkExprNode(uint32 offset, ChunkExprType t, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b, Common::SharedPtr<Node> s)
+		: ExprNode(kChunkExprNode, offset), type(t) {
 		first = Common::move(a);
 		first->parent = this;
 		last = Common::move(b);
@@ -232,7 +246,8 @@ struct ChunkExprNode : ExprNode {
 		string = Common::move(s);
 		string->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ChunkHiliteStmtNode */
@@ -240,11 +255,12 @@ struct ChunkExprNode : ExprNode {
 struct ChunkHiliteStmtNode : StmtNode {
 	Common::SharedPtr<Node> chunk;
 
-	ChunkHiliteStmtNode(Common::SharedPtr<Node> c) : StmtNode(kChunkHiliteStmtNode) {
+	ChunkHiliteStmtNode(uint32 offset, Common::SharedPtr<Node> c) : StmtNode(kChunkHiliteStmtNode, offset) {
 		chunk = Common::move(c);
 		chunk->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ChunkDeleteStmtNode */
@@ -252,11 +268,12 @@ struct ChunkHiliteStmtNode : StmtNode {
 struct ChunkDeleteStmtNode : StmtNode {
 	Common::SharedPtr<Node> chunk;
 
-	ChunkDeleteStmtNode(Common::SharedPtr<Node> c) : StmtNode(kChunkDeleteStmtNode) {
+	ChunkDeleteStmtNode(uint32 offset, Common::SharedPtr<Node> c) : StmtNode(kChunkDeleteStmtNode, offset) {
 		chunk = Common::move(c);
 		chunk->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* SpriteIntersectsExprNode */
@@ -265,14 +282,15 @@ struct SpriteIntersectsExprNode : ExprNode {
 	Common::SharedPtr<Node> firstSprite;
 	Common::SharedPtr<Node> secondSprite;
 
-	SpriteIntersectsExprNode(Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
-		: ExprNode(kSpriteIntersectsExprNode) {
+	SpriteIntersectsExprNode(uint32 offset, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
+		: ExprNode(kSpriteIntersectsExprNode, offset) {
 		firstSprite = Common::move(a);
 		firstSprite->parent = this;
 		secondSprite = Common::move(b);
 		secondSprite->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* SpriteWithinExprNode */
@@ -281,14 +299,15 @@ struct SpriteWithinExprNode : ExprNode {
 	Common::SharedPtr<Node> firstSprite;
 	Common::SharedPtr<Node> secondSprite;
 
-	SpriteWithinExprNode(Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
-		: ExprNode(kSpriteWithinExprNode) {
+	SpriteWithinExprNode(uint32 offset, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
+		: ExprNode(kSpriteWithinExprNode, offset) {
 		firstSprite = Common::move(a);
 		firstSprite->parent = this;
 		secondSprite = Common::move(b);
 		secondSprite->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* MemberExprNode */
@@ -298,8 +317,8 @@ struct MemberExprNode : ExprNode {
 	Common::SharedPtr<Node> memberID;
 	Common::SharedPtr<Node> castID;
 
-	MemberExprNode(Common::String type_, Common::SharedPtr<Node> memberID_, Common::SharedPtr<Node> castID_)
-		: ExprNode(kMemberExprNode), type(type_) {
+	MemberExprNode(uint32 offset, Common::String type_, Common::SharedPtr<Node> memberID_, Common::SharedPtr<Node> castID_)
+		: ExprNode(kMemberExprNode, offset), type(type_) {
 		this->memberID = Common::move(memberID_);
 		this->memberID->parent = this;
 		if (castID_) {
@@ -307,8 +326,9 @@ struct MemberExprNode : ExprNode {
 			this->castID->parent = this;
 		}
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual bool hasSpaces(bool dot);
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual bool hasSpaces(bool dot) override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* VarNode */
@@ -316,9 +336,10 @@ struct MemberExprNode : ExprNode {
 struct VarNode : ExprNode {
 	Common::String varName;
 
-	VarNode(Common::String v) : ExprNode(kVarNode), varName(v) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual bool hasSpaces(bool dot);
+	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;
 };
 
 /* AssignmentStmtNode */
@@ -328,15 +349,16 @@ struct AssignmentStmtNode : StmtNode {
 	Common::SharedPtr<Node> value;
 	bool forceVerbose;
 
-	AssignmentStmtNode(Common::SharedPtr<Node> var, Common::SharedPtr<Node> val, bool forceVerbose_ = false)
-		: StmtNode(kAssignmentStmtNode), forceVerbose(forceVerbose_) {
+	AssignmentStmtNode(uint32 offset, Common::SharedPtr<Node> var, Common::SharedPtr<Node> val, bool forceVerbose_ = false)
+		: StmtNode(kAssignmentStmtNode, offset), forceVerbose(forceVerbose_) {
 		variable = Common::move(var);
 		variable->parent = this;
 		value = Common::move(val);
 		value->parent = this;
 	}
 
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* IfStmtNode */
@@ -347,15 +369,16 @@ struct IfStmtNode : StmtNode {
 	Common::SharedPtr<BlockNode> block1;
 	Common::SharedPtr<BlockNode> block2;
 
-	IfStmtNode(Common::SharedPtr<Node> c) : StmtNode(kIfStmtNode), hasElse(false) {
+	IfStmtNode(uint32 offset, Common::SharedPtr<Node> c) : StmtNode(kIfStmtNode, offset), hasElse(false) {
 		condition = Common::move(c);
 		condition->parent = this;
-		block1 = Common::SharedPtr<BlockNode>(new BlockNode());
+		block1 = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block1->parent = this;
-		block2 = Common::SharedPtr<BlockNode>(new BlockNode());
+		block2 = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block2->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* RepeatWhileStmtNode */
@@ -364,14 +387,15 @@ struct RepeatWhileStmtNode : LoopNode {
 	Common::SharedPtr<Node> condition;
 	Common::SharedPtr<BlockNode> block;
 
-	RepeatWhileStmtNode(uint32 startIndex_, Common::SharedPtr<Node> c)
-		: LoopNode(kRepeatWhileStmtNode, startIndex_) {
+	RepeatWhileStmtNode(uint32 startIndex_, Common::SharedPtr<Node> c, uint32 offset)
+		: LoopNode(kRepeatWhileStmtNode, startIndex_, offset) {
 		condition = Common::move(c);
 		condition->parent = this;
-		block = Common::SharedPtr<BlockNode>(new BlockNode());
+		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* RepeatWithInStmtNode */
@@ -381,15 +405,16 @@ struct RepeatWithInStmtNode : LoopNode {
 	Common::SharedPtr<Node> list;
 	Common::SharedPtr<BlockNode> block;
 
-	RepeatWithInStmtNode(uint32 startIndex_, Common::String v, Common::SharedPtr<Node> l)
-		: LoopNode(kRepeatWithInStmtNode, startIndex_) {
+	RepeatWithInStmtNode(uint32 startIndex_, Common::String v, Common::SharedPtr<Node> l, uint32 offset)
+		: LoopNode(kRepeatWithInStmtNode, startIndex_, offset) {
 		varName = v;
 		list = Common::move(l);
 		list->parent = this;
-		block = Common::SharedPtr<BlockNode>(new BlockNode());
+		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* RepeatWithToStmtNode */
@@ -401,17 +426,18 @@ struct RepeatWithToStmtNode : LoopNode {
 	Common::SharedPtr<Node> end;
 	Common::SharedPtr<BlockNode> block;
 
-	RepeatWithToStmtNode(uint32 startIndex_, Common::String v, Common::SharedPtr<Node> s, bool up_, Common::SharedPtr<Node> e)
-		: LoopNode(kRepeatWithToStmtNode, startIndex_), up(up_) {
+	RepeatWithToStmtNode(uint32 startIndex_, Common::String v, Common::SharedPtr<Node> s, bool up, Common::SharedPtr<Node> e, uint32 offset)
+		: LoopNode(kRepeatWithToStmtNode, startIndex_, offset), up(up) {
 		varName = v;
 		start = Common::move(s);
 		start->parent = this;
 		end = Common::move(e);
 		end->parent = this;
-		block = Common::SharedPtr<BlockNode>(new BlockNode());
+		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* CaseLabelNode */
@@ -425,11 +451,12 @@ struct CaseLabelNode : LabelNode {
 	Common::SharedPtr<CaseLabelNode> nextLabel;
 	Common::SharedPtr<BlockNode> block;
 
-	CaseLabelNode(Common::SharedPtr<Node> v, CaseExpect e) : LabelNode(kCaseLabelNode), expect(e) {
+	CaseLabelNode(uint32 offset, Common::SharedPtr<Node> v, CaseExpect e) : LabelNode(kCaseLabelNode, offset), expect(e) {
 		value = Common::move(v);
 		value->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* OtherwiseNode */
@@ -437,18 +464,20 @@ struct CaseLabelNode : LabelNode {
 struct OtherwiseNode : LabelNode {
 	Common::SharedPtr<BlockNode> block;
 
-	OtherwiseNode() : LabelNode(kOtherwiseNode) {
-		block = Common::SharedPtr<BlockNode>(new BlockNode());
+	explicit OtherwiseNode(uint32 offset) : LabelNode(kOtherwiseNode, offset) {
+		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* EndCaseNode */
 
 struct EndCaseNode : LabelNode {
-	EndCaseNode() : LabelNode(kEndCaseNode) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	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;
 };
 
 /* CaseStmtNode */
@@ -462,12 +491,13 @@ struct CaseStmtNode : StmtNode {
 	int32 endPos = -1;
 	int32 potentialOtherwisePos = -1;
 
-	CaseStmtNode(Common::SharedPtr<Node> v) : StmtNode(kCaseStmtNode) {
+	CaseStmtNode(uint32 offset, Common::SharedPtr<Node> v) : StmtNode(kCaseStmtNode, offset) {
 		value = Common::move(v);
 		value->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	void addOtherwise();
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	void addOtherwise(uint32 offset);
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* TellStmtNode */
@@ -476,13 +506,14 @@ struct TellStmtNode : StmtNode {
 	Common::SharedPtr<Node> window;
 	Common::SharedPtr<BlockNode> block;
 
-	TellStmtNode(Common::SharedPtr<Node> w) : StmtNode(kTellStmtNode) {
+	TellStmtNode(uint32 offset, Common::SharedPtr<Node> w) : StmtNode(kTellStmtNode, offset) {
 		window = Common::move(w);
 		window->parent = this;
-		block = Common::SharedPtr<BlockNode>(new BlockNode());
+		block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
 		block->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* SoundCmdStmtNode */
@@ -491,12 +522,13 @@ struct SoundCmdStmtNode : StmtNode {
 	Common::String cmd;
 	Common::SharedPtr<Node> argList;
 
-	SoundCmdStmtNode(Common::String c, Common::SharedPtr<Node> a) : StmtNode(kSoundCmdStmtNode) {
+	SoundCmdStmtNode(uint32 offset, Common::String c, Common::SharedPtr<Node> a) : StmtNode(kSoundCmdStmtNode, offset) {
 		cmd = c;
 		argList = Common::move(a);
 		argList->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* PlayCmdStmtNode */
@@ -504,11 +536,12 @@ struct SoundCmdStmtNode : StmtNode {
 struct PlayCmdStmtNode : StmtNode {
 	Common::SharedPtr<Node> argList;
 
-	PlayCmdStmtNode(Common::SharedPtr<Node> a) : StmtNode(kPlayCmdStmtNode) {
+	PlayCmdStmtNode(uint32 offset, Common::SharedPtr<Node> a) : StmtNode(kPlayCmdStmtNode, offset) {
 		argList = Common::move(a);
 		argList->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* CallNode */
@@ -517,7 +550,7 @@ struct CallNode : Node {
 	Common::String name;
 	Common::SharedPtr<Node> argList;
 
-	CallNode(Common::String n, Common::SharedPtr<Node> a) : Node(kCallNode) {
+	CallNode(uint32 offset, Common::String n, Common::SharedPtr<Node> a) : Node(kCallNode, offset) {
 		name = n;
 		argList = Common::move(a);
 		argList->parent = this;
@@ -528,8 +561,9 @@ struct CallNode : Node {
 	}
 	bool noParens() const;
 	bool isMemberExpr() const;
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual bool hasSpaces(bool dot);
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual bool hasSpaces(bool dot) override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ObjCallNode */
@@ -538,7 +572,7 @@ struct ObjCallNode : Node {
 	Common::String name;
 	Common::SharedPtr<Node> argList;
 
-	ObjCallNode(Common::String n, Common::SharedPtr<Node> a) : Node(kObjCallNode) {
+	ObjCallNode(uint32 offset, Common::String n, Common::SharedPtr<Node> a) : Node(kObjCallNode, offset) {
 		name = n;
 		argList = Common::move(a);
 		argList->parent = this;
@@ -547,8 +581,9 @@ struct ObjCallNode : Node {
 		else
 			isExpression = true;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual bool hasSpaces(bool dot);
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual bool hasSpaces(bool dot) override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ObjCallV4Node */
@@ -557,7 +592,7 @@ struct ObjCallV4Node : Node {
 	Common::SharedPtr<Node> obj;
 	Common::SharedPtr<Node> argList;
 
-	ObjCallV4Node(Common::SharedPtr<Node> o, Common::SharedPtr<Node> a) : Node(kObjCallV4Node) {
+	ObjCallV4Node(uint32 offset, Common::SharedPtr<Node> o, Common::SharedPtr<Node> a) : Node(kObjCallV4Node, offset) {
 		obj = o;
 		argList = Common::move(a);
 		argList->parent = this;
@@ -566,8 +601,9 @@ struct ObjCallV4Node : Node {
 		else
 			isExpression = true;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual bool hasSpaces(bool dot);
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual bool hasSpaces(bool dot) override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* TheExprNode */
@@ -575,8 +611,9 @@ struct ObjCallV4Node : Node {
 struct TheExprNode : ExprNode {
 	Common::String prop;
 
-	TheExprNode(Common::String p) : ExprNode(kTheExprNode), prop(p) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	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;
 };
 
 /* LastStringChunkExprNode */
@@ -585,12 +622,13 @@ struct LastStringChunkExprNode : ExprNode {
 	ChunkExprType type;
 	Common::SharedPtr<Node> obj;
 
-	LastStringChunkExprNode(ChunkExprType t, Common::SharedPtr<Node> o)
-		: ExprNode(kLastStringChunkExprNode), type(t) {
+	LastStringChunkExprNode(uint32 offset, ChunkExprType t, Common::SharedPtr<Node> o)
+		: ExprNode(kLastStringChunkExprNode, offset), type(t) {
 		obj = Common::move(o);
 		obj->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* StringChunkCountExprNode */
@@ -599,12 +637,13 @@ struct StringChunkCountExprNode : ExprNode {
 	ChunkExprType type;
 	Common::SharedPtr<Node> obj;
 
-	StringChunkCountExprNode(ChunkExprType t, Common::SharedPtr<Node> o)
-		: ExprNode(kStringChunkCountExprNode), type(t) {
+	StringChunkCountExprNode(uint32 offset, ChunkExprType t, Common::SharedPtr<Node> o)
+		: ExprNode(kStringChunkCountExprNode, offset), type(t) {
 		obj = Common::move(o);
 		obj->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* MenuPropExprNode */
@@ -613,12 +652,13 @@ struct MenuPropExprNode : ExprNode {
 	Common::SharedPtr<Node> menuID;
 	unsigned int prop;
 
-	MenuPropExprNode(Common::SharedPtr<Node> m, unsigned int p)
-		: ExprNode(kMenuPropExprNode), prop(p) {
+	MenuPropExprNode(uint32 offset, Common::SharedPtr<Node> m, unsigned int p)
+		: ExprNode(kMenuPropExprNode, offset), prop(p) {
 		menuID = Common::move(m);
 		menuID->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* MenuItemPropExprNode */
@@ -628,14 +668,15 @@ struct MenuItemPropExprNode : ExprNode {
 	Common::SharedPtr<Node> itemID;
 	unsigned int prop;
 
-	MenuItemPropExprNode(Common::SharedPtr<Node> m, Common::SharedPtr<Node> i, unsigned int p)
-		: ExprNode(kMenuItemPropExprNode), prop(p) {
+	MenuItemPropExprNode(uint32 offset, Common::SharedPtr<Node> m, Common::SharedPtr<Node> i, unsigned int p)
+		: ExprNode(kMenuItemPropExprNode, offset), prop(p) {
 		menuID = Common::move(m);
 		menuID->parent = this;
 		itemID = Common::move(i);
 		itemID->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* SoundPropExprNode */
@@ -644,12 +685,13 @@ struct SoundPropExprNode : ExprNode {
 	Common::SharedPtr<Node> soundID;
 	unsigned int prop;
 
-	SoundPropExprNode(Common::SharedPtr<Node> s, unsigned int p)
-		: ExprNode(kSoundPropExprNode), prop(p) {
+	SoundPropExprNode(uint32 offset, Common::SharedPtr<Node> s, unsigned int p)
+		: ExprNode(kSoundPropExprNode, offset), prop(p) {
 		soundID = Common::move(s);
 		soundID->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* SpritePropExprNode */
@@ -658,12 +700,13 @@ struct SpritePropExprNode : ExprNode {
 	Common::SharedPtr<Node> spriteID;
 	unsigned int prop;
 
-	SpritePropExprNode(Common::SharedPtr<Node> s, unsigned int p)
-		: ExprNode(kSpritePropExprNode), prop(p) {
+	SpritePropExprNode(uint32 offset, Common::SharedPtr<Node> s, unsigned int p)
+		: ExprNode(kSpritePropExprNode, offset), prop(p) {
 		spriteID = Common::move(s);
 		spriteID->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ThePropExprNode */
@@ -672,12 +715,13 @@ struct ThePropExprNode : ExprNode {
 	Common::SharedPtr<Node> obj;
 	Common::String prop;
 
-	ThePropExprNode(Common::SharedPtr<Node> o, Common::String p)
-		: ExprNode(kThePropExprNode), prop(p) {
+	ThePropExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::String p)
+		: ExprNode(kThePropExprNode, offset), prop(p) {
 		obj = Common::move(o);
 		obj->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ObjPropExprNode */
@@ -686,13 +730,14 @@ struct ObjPropExprNode : ExprNode {
 	Common::SharedPtr<Node> obj;
 	Common::String prop;
 
-	ObjPropExprNode(Common::SharedPtr<Node> o, Common::String p)
-		: ExprNode(kObjPropExprNode), prop(p) {
+	ObjPropExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::String p)
+		: ExprNode(kObjPropExprNode, offset), prop(p) {
 		obj = Common::move(o);
 		obj->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual bool hasSpaces(bool dot);
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual bool hasSpaces(bool dot) override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ObjBracketExprNode */
@@ -701,15 +746,16 @@ struct ObjBracketExprNode : ExprNode {
 	Common::SharedPtr<Node> obj;
 	Common::SharedPtr<Node> prop;
 
-	ObjBracketExprNode(Common::SharedPtr<Node> o, Common::SharedPtr<Node> p)
-		: ExprNode(kObjBracketExprNode) {
+	ObjBracketExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::SharedPtr<Node> p)
+		: ExprNode(kObjBracketExprNode, offset) {
 		obj = Common::move(o);
 		obj->parent = this;
 		prop = Common::move(p);
 		prop->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual bool hasSpaces(bool dot);
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual bool hasSpaces(bool dot) override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ObjPropIndexExprNode */
@@ -720,8 +766,8 @@ struct ObjPropIndexExprNode : ExprNode {
 	Common::SharedPtr<Node> index;
 	Common::SharedPtr<Node> index2;
 
-	ObjPropIndexExprNode(Common::SharedPtr<Node> o, Common::String p, Common::SharedPtr<Node> i, Common::SharedPtr<Node> i2)
-		: ExprNode(kObjPropIndexExprNode), prop(p) {
+	ObjPropIndexExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::String p, Common::SharedPtr<Node> i, Common::SharedPtr<Node> i2)
+		: ExprNode(kObjPropIndexExprNode, offset), prop(p) {
 		obj = Common::move(o);
 		obj->parent = this;
 		index = Common::move(i);
@@ -731,22 +777,25 @@ struct ObjPropIndexExprNode : ExprNode {
 			index2->parent = this;
 		}
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
-	virtual bool hasSpaces(bool dot);
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual bool hasSpaces(bool dot) override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* ExitRepeatStmtNode */
 
 struct ExitRepeatStmtNode : StmtNode {
-	ExitRepeatStmtNode() : StmtNode(kExitRepeatStmtNode) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	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;
 };
 
 /* NextRepeatStmtNode */
 
 struct NextRepeatStmtNode : StmtNode {
-	NextRepeatStmtNode() : StmtNode(kNextRepeatStmtNode) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	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;
 };
 
 /* PutStmtNode */
@@ -756,14 +805,15 @@ struct PutStmtNode : StmtNode {
 	Common::SharedPtr<Node> variable;
 	Common::SharedPtr<Node> value;
 
-	PutStmtNode(PutType t, Common::SharedPtr<Node> var, Common::SharedPtr<Node> val)
-		: StmtNode(kPutStmtNode), type(t) {
+	PutStmtNode(uint32 offset, PutType t, Common::SharedPtr<Node> var, Common::SharedPtr<Node> val)
+		: StmtNode(kPutStmtNode, offset), type(t) {
 		variable = Common::move(var);
 		variable->parent = this;
 		value = Common::move(val);
 		value->parent = this;
 	}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const override;
+	virtual void accept(NodeVisitor &visitor) const override;
 };
 
 /* WhenStmtNode */
@@ -772,9 +822,10 @@ struct WhenStmtNode : StmtNode {
 	int event;
 	Common::String script;
 
-	WhenStmtNode(int e, Common::String s)
-		: StmtNode(kWhenStmtNode), event(e), script(s) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	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;
 };
 
 /* NewObjNode */
@@ -783,8 +834,63 @@ struct NewObjNode : ExprNode {
 	Common::String objType;
 	Common::SharedPtr<Node> objArgs;
 
-	NewObjNode(Common::String o, Common::SharedPtr<Node> args) : ExprNode(kNewObjNode), objType(o), objArgs(args) {}
-	virtual void writeScriptText(CodeWriter &code, bool dot, bool sum) const;
+	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;
+};
+
+class NodeVisitor {
+public:
+	virtual ~NodeVisitor() {}
+	virtual void visit(const HandlerNode &node) { defaultVisit(node); }
+	virtual void visit(const ErrorNode &node) { defaultVisit(node); }
+	virtual void visit(const CommentNode &node) { defaultVisit(node); }
+	virtual void visit(const NewObjNode &node) { defaultVisit(node); }
+	virtual void visit(const LiteralNode &node) { defaultVisit(node); }
+	virtual void visit(const IfStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const EndCaseNode &node) { defaultVisit(node); }
+	virtual void visit(const ObjCallNode &node) { defaultVisit(node); }
+	virtual void visit(const PutStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const TheExprNode &node) { defaultVisit(node); }
+	virtual void visit(const BinaryOpNode &node) { defaultVisit(node); }
+	virtual void visit(const CaseStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const ExitStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const TellStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const WhenStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const CaseLabelNode &node) { defaultVisit(node); }
+	virtual void visit(const ChunkExprNode &node) { defaultVisit(node); }
+	virtual void visit(const InverseOpNode &node) { defaultVisit(node); }
+	virtual void visit(const ObjCallV4Node &node) { defaultVisit(node); }
+	virtual void visit(const OtherwiseNode &node) { defaultVisit(node); }
+	virtual void visit(const MemberExprNode &node) { defaultVisit(node); }
+	virtual void visit(const ObjPropExprNode &node) { defaultVisit(node); }
+	virtual void visit(const PlayCmdStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const ThePropExprNode &node) { defaultVisit(node); }
+	virtual void visit(const MenuPropExprNode &node) { defaultVisit(node); }
+	virtual void visit(const SoundCmdStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const SoundPropExprNode &node) { defaultVisit(node); }
+	virtual void visit(const AssignmentStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const ExitRepeatStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const NextRepeatStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const ObjBracketExprNode &node) { defaultVisit(node); }
+	virtual void visit(const SpritePropExprNode &node) { defaultVisit(node); }
+	virtual void visit(const ChunkDeleteStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const ChunkHiliteStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const RepeatWhileStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const MenuItemPropExprNode &node) { defaultVisit(node); }
+	virtual void visit(const ObjPropIndexExprNode &node) { defaultVisit(node); }
+	virtual void visit(const RepeatWithInStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const RepeatWithToStmtNode &node) { defaultVisit(node); }
+	virtual void visit(const SpriteWithinExprNode &node) { defaultVisit(node); }
+	virtual void visit(const LastStringChunkExprNode &node) { defaultVisit(node); }
+	virtual void visit(const SpriteIntersectsExprNode &node) { defaultVisit(node); }
+	virtual void visit(const StringChunkCountExprNode &node) { defaultVisit(node); }
+	virtual void visit(const VarNode &node) { defaultVisit(node); }
+	virtual void visit(const CallNode &node) { defaultVisit(node); }
+	virtual void visit(const BlockNode &node) { defaultVisit(node); }
+	virtual void visit(const NotOpNode &node) { defaultVisit(node); }
+
+	virtual void defaultVisit(const Node &) {}
 };
 
 /* AST */
@@ -793,8 +899,8 @@ struct AST {
 	Common::SharedPtr<HandlerNode> root;
 	BlockNode *currentBlock;
 
-	AST(Handler *handler){
-		root = Common::SharedPtr<HandlerNode>(new HandlerNode(handler));
+	AST(uint32 offset, Handler *handler){
+		root = Common::SharedPtr<HandlerNode>(new HandlerNode(offset, handler));
 		currentBlock = root->block.get();
 	}
 
diff --git a/engines/director/lingo/lingodec/handler.cpp b/engines/director/lingo/lingodec/handler.cpp
index fba36cb4aa5..c8eb821c88d 100644
--- a/engines/director/lingo/lingodec/handler.cpp
+++ b/engines/director/lingo/lingodec/handler.cpp
@@ -128,7 +128,7 @@ Common::String Handler::getLocalName(int id) const {
 
 Common::SharedPtr<Node> Handler::pop() {
 	if (stack.empty())
-		return Common::SharedPtr<Node>(new ErrorNode());
+		return Common::SharedPtr<Node>(new ErrorNode(0));
 
 	auto res = stack.back();
 	stack.pop_back();
@@ -158,21 +158,21 @@ Common::SharedPtr<Node> Handler::readVar(int varType) {
 		{
 			Common::String name_ = getArgumentName(id->getValue()->i / variableMultiplier());
 			auto ref = Common::SharedPtr<Datum>(new Datum(kDatumVarRef, name_));
-			return Common::SharedPtr<Node>(new LiteralNode(Common::move(ref)));
+			return Common::SharedPtr<Node>(new LiteralNode(id->_startOffset, Common::move(ref)));
 		}
 	case 0x5: // local
 		{
 			Common::String name_ = getLocalName(id->getValue()->i / variableMultiplier());
 			auto ref = Common::SharedPtr<Datum>(new Datum(kDatumVarRef, name_));
-			return Common::SharedPtr<Node>(new LiteralNode(Common::move(ref)));
+			return Common::SharedPtr<Node>(new LiteralNode(id->_startOffset, Common::move(ref)));
 		}
 	case 0x6: // field
-		return Common::SharedPtr<Node>(new MemberExprNode("field", Common::move(id), Common::move(castID)));
+		return Common::SharedPtr<Node>(new MemberExprNode(id->_startOffset, "field", Common::move(id), Common::move(castID)));
 	default:
 		warning("%s", Common::String::format("findVar: unhandled var type %d", varType).c_str());
 		break;
 	}
-	return Common::SharedPtr<Node>(new ErrorNode());
+	return Common::SharedPtr<Node>(new ErrorNode(id->_startOffset));
 }
 
 Common::String Handler::getVarNameFromSet(const Bytecode &bytecode) {
@@ -198,64 +198,64 @@ Common::String Handler::getVarNameFromSet(const Bytecode &bytecode) {
 	return varName;
 }
 
-Common::SharedPtr<Node> Handler::readV4Property(int propertyType, int propertyID) {
+Common::SharedPtr<Node> Handler::readV4Property(uint32 offset, int propertyType, int propertyID) {
 	switch (propertyType) {
 	case 0x00:
 		{
 			if (propertyID <= 0x0b) { // movie property
 				auto propName = StandardNames::getName(StandardNames::moviePropertyNames, propertyID);
-				return Common::SharedPtr<Node>(new TheExprNode(propName));
+				return Common::SharedPtr<Node>(new TheExprNode(offset, propName));
 			} else { // last chunk
 				auto string = pop();
 				auto chunkType = static_cast<ChunkExprType>(propertyID - 0x0b);
-				return Common::SharedPtr<Node>(new LastStringChunkExprNode(chunkType, Common::move(string)));
+				return Common::SharedPtr<Node>(new LastStringChunkExprNode(offset, chunkType, Common::move(string)));
 			}
 		}
 		break;
 	case 0x01: // number of chunks
 		{
 			auto string = pop();
-			return Common::SharedPtr<Node>(new StringChunkCountExprNode(static_cast<ChunkExprType>(propertyID), Common::move(string)));
+			return Common::SharedPtr<Node>(new StringChunkCountExprNode(offset, static_cast<ChunkExprType>(propertyID), Common::move(string)));
 		}
 		break;
 	case 0x02: // menu property
 		{
 			auto menuID = pop();
-			return Common::SharedPtr<Node>(new MenuPropExprNode(Common::move(menuID), propertyID));
+			return Common::SharedPtr<Node>(new MenuPropExprNode(offset, Common::move(menuID), propertyID));
 		}
 		break;
 	case 0x03: // menu item property
 		{
 			auto menuID = pop();
 			auto itemID = pop();
-			return Common::SharedPtr<Node>(new MenuItemPropExprNode(Common::move(menuID), Common::move(itemID), propertyID));
+			return Common::SharedPtr<Node>(new MenuItemPropExprNode(offset, Common::move(menuID), Common::move(itemID), propertyID));
 		}
 		break;
 	case 0x04: // sound property
 		{
 			auto soundID = pop();
-			return Common::SharedPtr<Node>(new SoundPropExprNode(Common::move(soundID), propertyID));
+			return Common::SharedPtr<Node>(new SoundPropExprNode(offset, Common::move(soundID), propertyID));
 		}
 		break;
 	case 0x05: // resource property - unused?
-		return Common::SharedPtr<Node>(new CommentNode("ERROR: Resource property"));
+		return Common::SharedPtr<Node>(new CommentNode(offset, "ERROR: Resource property"));
 	case 0x06: // sprite property
 		{
 			auto spriteID = pop();
-			return Common::SharedPtr<Node>(new SpritePropExprNode(Common::move(spriteID), propertyID));
+			return Common::SharedPtr<Node>(new SpritePropExprNode(offset, Common::move(spriteID), propertyID));
 		}
 		break;
 	case 0x07: // animation property
-		return Common::SharedPtr<Node>(new TheExprNode(StandardNames::getName(StandardNames::animationPropertyNames, propertyID)));
+		return Common::SharedPtr<Node>(new TheExprNode(offset, StandardNames::getName(StandardNames::animationPropertyNames, propertyID)));
 	case 0x08: // animation 2 property
 		if (propertyID == 0x02 && script->version >= 500) { // the number of castMembers supports castLib selection from Director 5.0
 			auto castLib = pop();
 			if (!(castLib->type == kLiteralNode && castLib->getValue()->type == kDatumInt && castLib->getValue()->toInt() == 0)) {
-				auto castLibNode = Common::SharedPtr<Node>(new MemberExprNode("castLib", castLib, nullptr));
-				return Common::SharedPtr<Node>(new ThePropExprNode(castLibNode, StandardNames::getName(StandardNames::animation2PropertyNames, propertyID)));
+				auto castLibNode = Common::SharedPtr<Node>(new MemberExprNode(offset, "castLib", castLib, nullptr));
+				return Common::SharedPtr<Node>(new ThePropExprNode(offset, castLibNode, StandardNames::getName(StandardNames::animation2PropertyNames, propertyID)));
 			}
 		}
-		return Common::SharedPtr<Node>(new TheExprNode(StandardNames::getName(StandardNames::animation2PropertyNames, propertyID)));
+		return Common::SharedPtr<Node>(new TheExprNode(offset, StandardNames::getName(StandardNames::animation2PropertyNames, propertyID)));
 	case 0x09: // generic cast member
 	case 0x0a: // chunk of cast member
 	case 0x0b: // field
@@ -284,23 +284,23 @@ Common::SharedPtr<Node> Handler::readV4Property(int propertyType, int propertyID
 			} else {
 				prefix = (script->version >= 500) ? "member" : "cast";
 			}
-			auto member = Common::SharedPtr<Node>(new MemberExprNode(prefix, Common::move(memberID), Common::move(castID)));
+			auto member = Common::SharedPtr<Node>(new MemberExprNode(offset, prefix, Common::move(memberID), Common::move(castID)));
 			Common::SharedPtr<Node> entity;
 			if (propertyType == 0x0a || propertyType == 0x0c || propertyType == 0x15) {
-				entity = readChunkRef(Common::move(member));
+				entity = readChunkRef(offset, Common::move(member));
 			} else {
 				entity = member;
 			}
-			return Common::SharedPtr<Node>(new ThePropExprNode(Common::move(entity), propName));
+			return Common::SharedPtr<Node>(new ThePropExprNode(offset, Common::move(entity), propName));
 		}
 		break;
 	default:
 		break;
 	}
-	return Common::SharedPtr<Node>(new CommentNode(Common::String::format("ERROR: Unknown property type %d", propertyType)));
+	return Common::SharedPtr<Node>(new CommentNode(offset, Common::String::format("ERROR: Unknown property type %d", propertyType)));
 }
 
-Common::SharedPtr<Node> Handler::readChunkRef(Common::SharedPtr<Node> string) {
+Common::SharedPtr<Node> Handler::readChunkRef(uint32 offset, Common::SharedPtr<Node> string) {
 	auto lastLine = pop();
 	auto firstLine = pop();
 	auto lastItem = pop();
@@ -311,13 +311,13 @@ Common::SharedPtr<Node> Handler::readChunkRef(Common::SharedPtr<Node> string) {
 	auto firstChar = pop();
 
 	if (!(firstLine->type == kLiteralNode && firstLine->getValue()->type == kDatumInt && firstLine->getValue()->toInt() == 0))
-		string = Common::SharedPtr<Node>(new ChunkExprNode(kChunkLine, Common::move(firstLine), Common::move(lastLine), Common::move(string)));
+		string = Common::SharedPtr<Node>(new ChunkExprNode(offset, kChunkLine, Common::move(firstLine), Common::move(lastLine), Common::move(string)));
 	if (!(firstItem->type == kLiteralNode && firstItem->getValue()->type == kDatumInt && firstItem->getValue()->toInt() == 0))
-		string = Common::SharedPtr<Node>(new ChunkExprNode(kChunkItem, Common::move(firstItem), Common::move(lastItem), Common::move(string)));
+		string = Common::SharedPtr<Node>(new ChunkExprNode(offset, kChunkItem, Common::move(firstItem), Common::move(lastItem), Common::move(string)));
 	if (!(firstWord->type == kLiteralNode && firstWord->getValue()->type == kDatumInt && firstWord->getValue()->toInt() == 0))
-		string = Common::SharedPtr<Node>(new ChunkExprNode(kChunkWord, Common::move(firstWord), Common::move(lastWord), Common::move(string)));
+		string = Common::SharedPtr<Node>(new ChunkExprNode(offset, kChunkWord, Common::move(firstWord), Common::move(lastWord), Common::move(string)));
 	if (!(firstChar->type == kLiteralNode && firstChar->getValue()->type == kDatumInt && firstChar->getValue()->toInt() == 0))
-		string = Common::SharedPtr<Node>(new ChunkExprNode(kChunkChar, Common::move(firstChar), Common::move(lastChar), Common::move(string)));
+		string = Common::SharedPtr<Node>(new ChunkExprNode(offset, kChunkChar, Common::move(firstChar), Common::move(lastChar), Common::move(string)));
 
 	return string;
 }
@@ -514,7 +514,7 @@ void Handler::parse() {
 					if (caseLabel) {
 						if (caseLabel->expect == kCaseExpectOtherwise) {
 							ast.currentBlock->currentCaseLabel = nullptr;
-							caseStmt->addOtherwise();
+							caseStmt->addOtherwise(i);
 							size_t otherwiseIndex = bytecodePosMap[caseStmt->potentialOtherwisePos];
 							bytecodeArray[otherwiseIndex].translation = Common::SharedPtr<Node>(caseStmt->otherwise);
 							ast.enterBlock(caseStmt->otherwise->block.get());
@@ -543,12 +543,14 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 	case kOpRet:
 	case kOpRetFactory:
 		if (index == bytecodeArray.size() - 1) {
+			ast.root->_endOffset = bytecode.pos;
+			ast.currentBlock->_endOffset = bytecode.pos;
 			return 1; // end of handler
 		}
-		translation = Common::SharedPtr<Node>(new ExitStmtNode());
+		translation = Common::SharedPtr<Node>(new ExitStmtNode(index));
 		break;
 	case kOpPushZero:
-		translation = Common::SharedPtr<Node>(new LiteralNode(Common::SharedPtr<Datum>(new Datum(0))));
+		translation = Common::SharedPtr<Node>(new LiteralNode(index, Common::SharedPtr<Datum>(new Datum(0))));
 		break;
 	case kOpMul:
 	case kOpAdd:
@@ -570,25 +572,25 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 		{
 			auto b = pop();
 			auto a = pop();
-			translation = Common::SharedPtr<Node>(new BinaryOpNode(bytecode.opcode, Common::move(a), Common::move(b)));
+			translation = Common::SharedPtr<Node>(new BinaryOpNode(index, bytecode.opcode, Common::move(a), Common::move(b)));
 		}
 		break;
 	case kOpInv:
 		{
 			auto x = pop();
-			translation = Common::SharedPtr<Node>(new InverseOpNode(Common::move(x)));
+			translation = Common::SharedPtr<Node>(new InverseOpNode(index, Common::move(x)));
 		}
 		break;
 	case kOpNot:
 		{
 			auto x = pop();
-			translation = Common::SharedPtr<Node>(new NotOpNode(Common::move(x)));
+			translation = Common::SharedPtr<Node>(new NotOpNode(index, Common::move(x)));
 		}
 		break;
 	case kOpGetChunk:
 		{
 			auto string = pop();
-			translation = readChunkRef(Common::move(string));
+			translation = readChunkRef(index, Common::move(string));
 		}
 		break;
 	case kOpHiliteChunk:
@@ -597,12 +599,12 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			if (script->version >= 500)
 				castID = pop();
 			auto fieldID = pop();
-			auto field = Common::SharedPtr<Node>(new MemberExprNode("field", Common::move(fieldID), Common::move(castID)));
-			auto chunk = readChunkRef(Common::move(field));
+			auto field = Common::SharedPtr<Node>(new MemberExprNode(index, "field", Common::move(fieldID), Common::move(castID)));
+			auto chunk = readChunkRef(index, Common::move(field));
 			if (chunk->type == kCommentNode) { // error comment
 				translation = chunk;
 			} else {
-				translation = Common::SharedPtr<Node>(new ChunkHiliteStmtNode(Common::move(chunk)));
+				translation = Common::SharedPtr<Node>(new ChunkHiliteStmtNode(index, Common::move(chunk)));
 			}
 		}
 		break;
@@ -610,14 +612,14 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 		{
 			auto secondSprite = pop();
 			auto firstSprite = pop();
-			translation = Common::SharedPtr<Node>(new SpriteIntersectsExprNode(Common::move(firstSprite), Common::move(secondSprite)));
+			translation = Common::SharedPtr<Node>(new SpriteIntersectsExprNode(index, Common::move(firstSprite), Common::move(secondSprite)));
 		}
 		break;
 	case kOpIntoSpr:
 		{
 			auto secondSprite = pop();
 			auto firstSprite = pop();
-			translation = Common::SharedPtr<Node>(new SpriteWithinExprNode(Common::move(firstSprite), Common::move(secondSprite)));
+			translation = Common::SharedPtr<Node>(new SpriteWithinExprNode(index, Common::move(firstSprite), Common::move(secondSprite)));
 		}
 		break;
 	case kOpGetField:
@@ -626,13 +628,13 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			if (script->version >= 500)
 				castID = pop();
 			auto fieldID = pop();
-			translation = Common::SharedPtr<Node>(new MemberExprNode("field", Common::move(fieldID), Common::move(castID)));
+			translation = Common::SharedPtr<Node>(new MemberExprNode(index, "field", Common::move(fieldID), Common::move(castID)));
 		}
 		break;
 	case kOpStartTell:
 		{
 			auto window = pop();
-			auto tellStmt = Common::SharedPtr<TellStmtNode>(new TellStmtNode(Common::move(window)));
+			auto tellStmt = Common::SharedPtr<TellStmtNode>(new TellStmtNode(index, Common::move(window)));
 			translation = tellStmt;
 			nextBlock = tellStmt->block.get();
 		}
@@ -669,13 +671,13 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 	case kOpPushInt32:
 		{
 			auto i = Common::SharedPtr<Datum>(new Datum((int)bytecode.obj));
-			translation = Common::SharedPtr<Node>(new LiteralNode(Common::move(i)));
+			translation = Common::SharedPtr<Node>(new LiteralNode(index, Common::move(i)));
 		}
 		break;
 	case kOpPushFloat32:
 		{
 			auto f = Common::SharedPtr<Datum>(new Datum(*(float *)(&bytecode.obj)));
-			translation = Common::SharedPtr<Node>(new LiteralNode(Common::move(f)));
+			translation = Common::SharedPtr<Node>(new LiteralNode(index, Common::move(f)));
 		}
 		break;
 	case kOpPushArgListNoRet:
@@ -688,7 +690,7 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				args[argCount] = pop();
 			}
 			auto argList = Common::SharedPtr<Datum>(new Datum(kDatumArgListNoRet, args));
-			translation = Common::SharedPtr<Node>(new LiteralNode(Common::move(argList)));
+			translation = Common::SharedPtr<Node>(new LiteralNode(bytecode.pos, Common::move(argList)));
 		}
 		break;
 	case kOpPushArgList:
@@ -701,75 +703,75 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				args[argCount] = pop();
 			}
 			auto argList = Common::SharedPtr<Datum>(new Datum(kDatumArgList, args));
-			translation = Common::SharedPtr<Node>(new LiteralNode(Common::move(argList)));
+			translation = Common::SharedPtr<Node>(new LiteralNode(bytecode.pos, Common::move(argList)));
 		}
 		break;
 	case kOpPushCons:
 		{
 			int literalID = bytecode.obj / variableMultiplier();
 			if (-1 < literalID && (unsigned)literalID < script->literals.size()) {
-				translation = Common::SharedPtr<Node>(new LiteralNode(script->literals[literalID].value));
+				translation = Common::SharedPtr<Node>(new LiteralNode(bytecode.pos, script->literals[literalID].value));
 			} else {
-				translation = Common::SharedPtr<Node>(new ErrorNode());
+				translation = Common::SharedPtr<Node>(new ErrorNode(bytecode.pos));
 			}
 			break;
 		}
 	case kOpPushSymb:
 		{
 			auto sym = Common::SharedPtr<Datum>(new Datum(kDatumSymbol, getName(bytecode.obj)));
-			translation = Common::SharedPtr<Node>(new LiteralNode(Common::move(sym)));
+			translation = Common::SharedPtr<Node>(new LiteralNode(bytecode.pos, Common::move(sym)));
 		}
 		break;
 	case kOpPushVarRef:
 		{
 			auto ref = Common::SharedPtr<Datum>(new Datum(kDatumVarRef, getName(bytecode.obj)));
-			translation = Common::SharedPtr<Node>(new LiteralNode(Common::move(ref)));
+			translation = Common::SharedPtr<Node>(new LiteralNode(bytecode.pos, Common::move(ref)));
 		}
 		break;
 	case kOpGetGlobal:
 	case kOpGetGlobal2:
 		{
 			auto name_ = getName(bytecode.obj);
-			translation = Common::SharedPtr<Node>(new VarNode(name_));
+			translation = Common::SharedPtr<Node>(new VarNode(bytecode.pos, name_));
 		}
 		break;
 	case kOpGetProp:
-		translation = Common::SharedPtr<Node>(new VarNode(getName(bytecode.obj)));
+		translation = Common::SharedPtr<Node>(new VarNode(bytecode.pos, getName(bytecode.obj)));
 		break;
 	case kOpGetParam:
-		translation = Common::SharedPtr<Node>(new VarNode(getArgumentName(bytecode.obj / variableMultiplier())));
+		translation = Common::SharedPtr<Node>(new VarNode(bytecode.pos, getArgumentName(bytecode.obj / variableMultiplier())));
 		break;
 	case kOpGetLocal:
-		translation = Common::SharedPtr<Node>(new VarNode(getLocalName(bytecode.obj / variableMultiplier())));
+		translation = Common::SharedPtr<Node>(new VarNode(bytecode.pos, getLocalName(bytecode.obj / variableMultiplier())));
 		break;
 	case kOpSetGlobal:
 	case kOpSetGlobal2:
 		{
 			auto varName = getName(bytecode.obj);
-			auto var = Common::SharedPtr<Node>(new VarNode(varName));
+			auto var = Common::SharedPtr<Node>(new VarNode(bytecode.pos, varName));
 			auto value = pop();
-			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(Common::move(var), Common::move(value)));
+			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(bytecode.pos, Common::move(var), Common::move(value)));
 		}
 		break;
 	case kOpSetProp:
 		{
-			auto var = Common::SharedPtr<Node>(new VarNode(getName(bytecode.obj)));
+			auto var = Common::SharedPtr<Node>(new VarNode(bytecode.pos, getName(bytecode.obj)));
 			auto value = pop();
-			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(Common::move(var), Common::move(value)));
+			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(bytecode.pos, Common::move(var), Common::move(value)));
 		}
 		break;
 	case kOpSetParam:
 		{
-			auto var = Common::SharedPtr<Node>(new VarNode(getArgumentName(bytecode.obj / variableMultiplier())));
+			auto var = Common::SharedPtr<Node>(new VarNode(bytecode.pos, getArgumentName(bytecode.obj / variableMultiplier())));
 			auto value = pop();
-			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(Common::move(var), Common::move(value)));
+			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(bytecode.pos, Common::move(var), Common::move(value)));
 		}
 		break;
 	case kOpSetLocal:
 		{
-			auto var = Common::SharedPtr<Node>(new VarNode(getLocalName(bytecode.obj / variableMultiplier())));
+			auto var = Common::SharedPtr<Node>(new VarNode(bytecode.pos, getLocalName(bytecode.obj / variableMultiplier())));
 			auto value = pop();
-			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(Common::move(var), Common::move(value)));
+			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(bytecode.pos, Common::move(var), Common::move(value)));
 		}
 		break;
 	case kOpJmp:
@@ -780,10 +782,10 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			auto ancestorLoop = ast.currentBlock->ancestorLoop();
 			if (ancestorLoop) {
 				if (bytecodeArray[targetIndex - 1].opcode == kOpEndRepeat && bytecodeArray[targetIndex - 1].ownerLoop == ancestorLoop->startIndex) {
-					translation = Common::SharedPtr<Node>(new ExitRepeatStmtNode());
+					translation = Common::SharedPtr<Node>(new ExitRepeatStmtNode(bytecode.pos));
 					break;
 				} else if (bytecodeArray[targetIndex].tag == kTagNextRepeatTarget && bytecodeArray[targetIndex].ownerLoop == ancestorLoop->startIndex) {
-					translation = Common::SharedPtr<Node>(new NextRepeatStmtNode());
+					translation = Common::SharedPtr<Node>(new NextRepeatStmtNode(bytecode.pos));
 					break;
 				}
 			}
@@ -794,13 +796,17 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 					auto ifStmt = static_cast<IfStmtNode *>(ancestorStatement);
 					if (ast.currentBlock == ifStmt->block1.get()) {
 						ifStmt->hasElse = true;
+						ifStmt->block2->_startOffset = ifStmt->block1->_endOffset;
 						ifStmt->block2->endPos = targetPos;
+						ifStmt->block2->_endOffset = targetPos;
+						ifStmt->_endOffset = targetPos;
 						return 1; // if statement amended, nothing to push
 					}
 				} else if (ancestorStatement->type == kCaseStmtNode) {
 					auto caseStmt = static_cast<CaseStmtNode *>(ancestorStatement);
 					caseStmt->potentialOtherwisePos = bytecode.pos;
 					caseStmt->endPos = targetPos;
+					caseStmt->_endOffset = targetPos;
 					targetBytecode.tag = kTagEndCase;
 					return 1;
 				}
@@ -808,20 +814,21 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			if (targetBytecode.opcode == kOpPop && targetBytecode.obj == 1) {
 				// This is a case statement starting with 'otherwise'
 				auto value = pop();
-				auto caseStmt = Common::SharedPtr<CaseStmtNode>(new CaseStmtNode(Common::move(value)));
+				auto caseStmt = Common::SharedPtr<CaseStmtNode>(new CaseStmtNode(bytecode.pos, Common::move(value)));
 				caseStmt->endPos = targetPos;
+				caseStmt->_endOffset = targetPos;
 				targetBytecode.tag = kTagEndCase;
-				caseStmt->addOtherwise();
+				caseStmt->addOtherwise(bytecode.pos);
 				translation = caseStmt;
 				nextBlock = caseStmt->otherwise->block.get();
 				break;
 			}
-			translation = Common::SharedPtr<Node>(new CommentNode("ERROR: Could not identify jmp"));
+			translation = Common::SharedPtr<Node>(new CommentNode(bytecode.pos, "ERROR: Could not identify jmp"));
 		}
 		break;
 	case kOpEndRepeat:
 		// This should normally be tagged kTagSkip or kTagNextRepeatTarget and skipped.
-		translation = Common::SharedPtr<Node>(new CommentNode("ERROR: Stray endrepeat"));
+		translation = Common::SharedPtr<Node>(new CommentNode(bytecode.pos, "ERROR: Stray endrepeat"));
 		break;
 	case kOpJmpIfZ:
 		{
@@ -831,8 +838,10 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			case kTagRepeatWhile:
 				{
 					auto condition = pop();
-					auto loop = Common::SharedPtr<RepeatWhileStmtNode>(new RepeatWhileStmtNode(index, Common::move(condition)));
+					auto loop = Common::SharedPtr<RepeatWhileStmtNode>(new RepeatWhileStmtNode(index, Common::move(condition), bytecode.pos));
 					loop->block->endPos = endPos;
+					loop->block->_endOffset = endPos;
+					loop->_endOffset = endPos;
 					translation = loop;
 					nextBlock = loop->block.get();
 				}
@@ -841,8 +850,10 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				{
 					auto list = pop();
 					Common::String varName = getVarNameFromSet(bytecodeArray[index + 5]);
-					auto loop = Common::SharedPtr<RepeatWithInStmtNode>(new RepeatWithInStmtNode(index, varName, Common::move(list)));
+					auto loop = Common::SharedPtr<RepeatWithInStmtNode>(new RepeatWithInStmtNode(index, varName, Common::move(list), bytecode.pos));
 					loop->block->endPos = endPos;
+					loop->block->_endOffset = endPos;
+					loop->_endOffset = endPos;
 					translation = loop;
 					nextBlock = loop->block.get();
 				}
@@ -856,8 +867,10 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 					auto endRepeat = bytecodeArray[endIndex - 1];
 					uint32 conditionStartIndex = bytecodePosMap[endRepeat.pos - endRepeat.obj];
 					Common::String varName = getVarNameFromSet(bytecodeArray[conditionStartIndex - 1]);
-					auto loop = Common::SharedPtr<RepeatWithToStmtNode>(new RepeatWithToStmtNode(index, varName, Common::move(start), up, Common::move(end)));
+					auto loop = Common::SharedPtr<RepeatWithToStmtNode>(new RepeatWithToStmtNode(index, varName, Common::move(start), up, Common::move(end), bytecode.pos));
 					loop->block->endPos = endPos;
+					loop->block->_endOffset = endPos;
+					loop->_endOffset = endPos;
 					translation = loop;
 					nextBlock = loop->block.get();
 				}
@@ -865,8 +878,10 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			default:
 				{
 					auto condition = pop();
-					auto ifStmt = Common::SharedPtr<IfStmtNode>(new IfStmtNode(Common::move(condition)));
+					auto ifStmt = Common::SharedPtr<IfStmtNode>(new IfStmtNode(bytecode.pos, Common::move(condition)));
 					ifStmt->block1->endPos = endPos;
+					ifStmt->block1->_endOffset = endPos;
+					ifStmt->_endOffset = endPos;
 					translation = ifStmt;
 					nextBlock = ifStmt->block1.get();
 				}
@@ -877,7 +892,7 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 	case kOpLocalCall:
 		{
 			auto argList = pop();
-			translation = Common::SharedPtr<Node>(new CallNode(script->handlers[bytecode.obj].name, Common::move(argList)));
+			translation = Common::SharedPtr<Node>(new CallNode(bytecode.pos, script->handlers[bytecode.obj].name, Common::move(argList)));
 		}
 		break;
 	case kOpExtCall:
@@ -891,11 +906,11 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			if (isStatement && name_ == "sound" && nargs > 0 && rawArgList[0]->type == kLiteralNode && rawArgList[0]->getValue()->type == kDatumSymbol) {
 				Common::String cmd = rawArgList[0]->getValue()->s;
 				rawArgList.erase(rawArgList.begin());
-				translation = Common::SharedPtr<Node>(new SoundCmdStmtNode(cmd, Common::move(argList)));
+				translation = Common::SharedPtr<Node>(new SoundCmdStmtNode(bytecode.pos, cmd, Common::move(argList)));
 			} else if (isStatement && name_ == "play" && nargs <= 2) {
-				translation = Common::SharedPtr<Node>(new PlayCmdStmtNode(Common::move(argList)));
+				translation = Common::SharedPtr<Node>(new PlayCmdStmtNode(bytecode.pos, Common::move(argList)));
 			} else {
-				translation = Common::SharedPtr<Node>(new CallNode(name_, Common::move(argList)));
+				translation = Common::SharedPtr<Node>(new CallNode(bytecode.pos, name_, Common::move(argList)));
 			}
 		}
 		break;
@@ -907,9 +922,9 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			if (rawArgList.size() > 0) {
 				// first arg is a symbol
 				// replace it with a variable
-				rawArgList[0] = Common::SharedPtr<Node>(new VarNode(rawArgList[0]->getValue()->s));
+				rawArgList[0] = Common::SharedPtr<Node>(new VarNode(bytecode.pos, rawArgList[0]->getValue()->s));
 			}
-			translation = Common::SharedPtr<Node>(new ObjCallV4Node(Common::move(object), Common::move(argList)));
+			translation = Common::SharedPtr<Node>(new ObjCallV4Node(bytecode.pos, Common::move(object), Common::move(argList)));
 		}
 		break;
 	case kOpPut:
@@ -918,7 +933,7 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			uint32 varType = bytecode.obj & 0xF;
 			auto var = readVar(varType);
 			auto val = pop();
-			translation = Common::SharedPtr<Node>(new PutStmtNode(putType, Common::move(var), Common::move(val)));
+			translation = Common::SharedPtr<Node>(new PutStmtNode(bytecode.pos, putType, Common::move(var), Common::move(val)));
 		}
 		break;
 	case kOpPutChunk:
@@ -926,30 +941,30 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			PutType putType = static_cast<PutType>((bytecode.obj >> 4) & 0xF);
 			uint32 varType = bytecode.obj & 0xF;
 			auto var = readVar(varType);
-			auto chunk = readChunkRef(Common::move(var));
+			auto chunk = readChunkRef(bytecode.pos, Common::move(var));
 			auto val = pop();
 			if (chunk->type == kCommentNode) { // error comment
 				translation = chunk;
 			} else {
-				translation = Common::SharedPtr<Node>(new PutStmtNode(putType, Common::move(chunk), Common::move(val)));
+				translation = Common::SharedPtr<Node>(new PutStmtNode(bytecode.pos, putType, Common::move(chunk), Common::move(val)));
 			}
 		}
 		break;
 	case kOpDeleteChunk:
 		{
 			auto var = readVar(bytecode.obj);
-			auto chunk = readChunkRef(Common::move(var));
+			auto chunk = readChunkRef(bytecode.pos, Common::move(var));
 			if (chunk->type == kCommentNode) { // error comment
 				translation = chunk;
 			} else {
-				translation = Common::SharedPtr<Node>(new ChunkDeleteStmtNode(Common::move(chunk)));
+				translation = Common::SharedPtr<Node>(new ChunkDeleteStmtNode(bytecode.pos, Common::move(chunk)));
 			}
 		}
 		break;
 	case kOpGet:
 		{
 			int propertyID = pop()->getValue()->toInt();
-			translation = readV4Property(bytecode.obj, propertyID);
+			translation = readV4Property(bytecode.pos, bytecode.obj, propertyID);
 		}
 		break;
 	case kOpSet:
@@ -962,42 +977,42 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				// If the script contains a line break, it's definitely a when statement.
 				Common::String script_ = value->getValue()->s;
 				if (script_.size() > 0 && (script_[0] == ' ' || script_.find('\r') != Common::String::npos)) {
-					translation = Common::SharedPtr<Node>(new WhenStmtNode(propertyID, script_));
+					translation = Common::SharedPtr<Node>(new WhenStmtNode(bytecode.pos, propertyID, script_));
 				}
 			}
 			if (!translation) {
-				auto prop = readV4Property(bytecode.obj, propertyID);
+				auto prop = readV4Property(bytecode.pos, bytecode.obj, propertyID);
 				if (prop->type == kCommentNode) { // error comment
 					translation = prop;
 				} else {
-					translation = Common::SharedPtr<Node>(new AssignmentStmtNode(Common::move(prop), Common::move(value), true));
+					translation = Common::SharedPtr<Node>(new AssignmentStmtNode(bytecode.pos, Common::move(prop), Common::move(value), true));
 				}
 			}
 		}
 		break;
 	case kOpGetMovieProp:
-		translation = Common::SharedPtr<Node>(new TheExprNode(getName(bytecode.obj)));
+		translation = Common::SharedPtr<Node>(new TheExprNode(bytecode.pos, getName(bytecode.obj)));
 		break;
 	case kOpSetMovieProp:
 		{
 			auto value = pop();
-			auto prop = Common::SharedPtr<TheExprNode>(new TheExprNode(getName(bytecode.obj)));
-			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(Common::move(prop), Common::move(value)));
+			auto prop = Common::SharedPtr<TheExprNode>(new TheExprNode(bytecode.pos, getName(bytecode.obj)));
+			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(bytecode.pos, Common::move(prop), Common::move(value)));
 		}
 		break;
 	case kOpGetObjProp:
 	case kOpGetChainedProp:
 		{
 			auto object = pop();
-			translation = Common::SharedPtr<Node>(new ObjPropExprNode(Common::move(object), getName(bytecode.obj)));
+			translation = Common::SharedPtr<Node>(new ObjPropExprNode(bytecode.pos, Common::move(object), getName(bytecode.obj)));
 		}
 		break;
 	case kOpSetObjProp:
 		{
 			auto value = pop();
 			auto object = pop();
-			auto prop = Common::SharedPtr<ObjPropExprNode>(new ObjPropExprNode(Common::move(object), getName(bytecode.obj)));
-			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(Common::move(prop), Common::move(value)));
+			auto prop = Common::SharedPtr<ObjPropExprNode>(new ObjPropExprNode(bytecode.pos, Common::move(object), getName(bytecode.obj)));
+			translation = Common::SharedPtr<Node>(new AssignmentStmtNode(bytecode.pos, Common::move(prop), Common::move(value)));
 		}
 		break;
 	case kOpPeek:
@@ -1022,7 +1037,7 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				&& !(stack.size() == originalStackSize + 1 && (currBytecode->opcode == kOpEq || currBytecode->opcode == kOpNtEq))
 			);
 			if (currIndex >= bytecodeArray.size()) {
-				bytecode.translation = Common::SharedPtr<Node>(new CommentNode("ERROR: Expected eq or nteq!"));
+				bytecode.translation = Common::SharedPtr<Node>(new CommentNode(bytecode.pos, "ERROR: Expected eq or nteq!"));
 				ast.addStatement(bytecode.translation);
 				return currIndex - index + 1;
 			}
@@ -1035,7 +1050,7 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			currIndex += 1;
 			currBytecode = &bytecodeArray[currIndex];
 			if (currIndex >= bytecodeArray.size() || currBytecode->opcode != kOpJmpIfZ) {
-				bytecode.translation = Common::SharedPtr<Node>(new CommentNode("ERROR: Expected jmpifz!"));
+				bytecode.translation = Common::SharedPtr<Node>(new CommentNode(bytecode.pos, "ERROR: Expected jmpifz!"));
 				ast.addStatement(bytecode.translation);
 				return currIndex - index + 1;
 			}
@@ -1058,13 +1073,13 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				expect = kCaseExpectOtherwise; // Expect an 'otherwise' block.
 			}
 
-			auto currLabel = Common::SharedPtr<CaseLabelNode>(new CaseLabelNode(Common::move(caseValue), expect));
+			auto currLabel = Common::SharedPtr<CaseLabelNode>(new CaseLabelNode(bytecode.pos, Common::move(caseValue), expect));
 			jmpifz.translation = currLabel;
 			ast.currentBlock->currentCaseLabel = currLabel.get();
 
 			if (!prevLabel) {
 				auto peekedValue = pop();
-				auto caseStmt = Common::SharedPtr<CaseStmtNode>(new CaseStmtNode(Common::move(peekedValue)));
+				auto caseStmt = Common::SharedPtr<CaseStmtNode>(new CaseStmtNode(bytecode.pos, Common::move(peekedValue)));
 				caseStmt->firstLabel = currLabel;
 				currLabel->parent = caseStmt.get();
 				bytecode.translation = caseStmt;
@@ -1080,9 +1095,11 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			// The block doesn't start until the after last equivalent case,
 			// so don't create a block yet if we're expecting an equivalent case.
 			if (currLabel->expect != kCaseExpectOr) {
-				currLabel->block = Common::SharedPtr<BlockNode>(new BlockNode());
+				currLabel->block = Common::SharedPtr<BlockNode>(new BlockNode(bytecode.pos));
 				currLabel->block->parent = currLabel.get();
 				currLabel->block->endPos = jmpPos;
+				currLabel->block->_endOffset = jmpPos;
+				currLabel->_endOffset = jmpPos;
 				ast.enterBlock(currLabel->block.get());
 			}
 
@@ -1102,7 +1119,7 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				// We have an unused value on the stack, so this must be the end
 				// of a case statement with no labels.
 				auto value = pop();
-				translation = Common::SharedPtr<Node>(new CaseStmtNode(Common::move(value)));
+				translation = Common::SharedPtr<Node>(new CaseStmtNode(bytecode.pos, Common::move(value)));
 				break;
 			}
 			// Otherwise, this pop instruction occurs before a 'return' within
@@ -1113,7 +1130,7 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 	case kOpTheBuiltin:
 		{
 			pop(); // empty arglist
-			translation = Common::SharedPtr<Node>(new TheExprNode(getName(bytecode.obj)));
+			translation = Common::SharedPtr<Node>(new TheExprNode(bytecode.pos, getName(bytecode.obj)));
 		}
 		break;
 	case kOpObjCall:
@@ -1126,14 +1143,14 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				// obj.getAt(i) => obj[i]
 				auto obj = rawArgList[0];
 				auto prop = rawArgList[1];
-				translation = Common::SharedPtr<Node>(new ObjBracketExprNode(Common::move(obj), Common::move(prop)));
+				translation = Common::SharedPtr<Node>(new ObjBracketExprNode(bytecode.pos, Common::move(obj), Common::move(prop)));
 			} else if (method == "setAt" && nargs == 3) {
 				// obj.setAt(i) => obj[i] = val
 				auto obj = rawArgList[0];
 				auto prop = rawArgList[1];
 				auto val = rawArgList[2];
-				Common::SharedPtr<Node> propExpr = Common::SharedPtr<Node>(new ObjBracketExprNode(Common::move(obj), Common::move(prop)));
-				translation = Common::SharedPtr<Node>(new AssignmentStmtNode(Common::move(propExpr), Common::move(val)));
+				Common::SharedPtr<Node> propExpr = Common::SharedPtr<Node>(new ObjBracketExprNode(bytecode.pos, Common::move(obj), Common::move(prop)));
+				translation = Common::SharedPtr<Node>(new AssignmentStmtNode(bytecode.pos, Common::move(propExpr), Common::move(val)));
 			} else if ((method == "getProp" || method == "getPropRef") && (nargs == 3 || nargs == 4) && rawArgList[1]->getValue()->type == kDatumSymbol) {
 				// obj.getProp(#prop, i) => obj.prop[i]
 				// obj.getProp(#prop, i, i2) => obj.prop[i..i2]
@@ -1141,7 +1158,7 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				Common::String propName  = rawArgList[1]->getValue()->s;
 				auto i = rawArgList[2];
 				auto i2 = (nargs == 4) ? rawArgList[3] : nullptr;
-				translation = Common::SharedPtr<Node>(new ObjPropIndexExprNode(Common::move(obj), propName, Common::move(i), Common::move(i2)));
+				translation = Common::SharedPtr<Node>(new ObjPropIndexExprNode(bytecode.pos, Common::move(obj), propName, Common::move(i), Common::move(i2)));
 			} else if (method == "setProp" && (nargs == 4 || nargs == 5) && rawArgList[1]->getValue()->type == kDatumSymbol) {
 				// obj.setProp(#prop, i, val) => obj.prop[i] = val
 				// obj.setProp(#prop, i, i2, val) => obj.prop[i..i2] = val
@@ -1149,15 +1166,15 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				Common::String propName  = rawArgList[1]->getValue()->s;
 				auto i = rawArgList[2];
 				auto i2 = (nargs == 5) ? rawArgList[3] : nullptr;
-				auto propExpr = Common::SharedPtr<ObjPropIndexExprNode>(new ObjPropIndexExprNode(Common::move(obj), propName, Common::move(i), Common::move(i2)));
+				auto propExpr = Common::SharedPtr<ObjPropIndexExprNode>(new ObjPropIndexExprNode(bytecode.pos, Common::move(obj), propName, Common::move(i), Common::move(i2)));
 				auto val = rawArgList[nargs - 1];
-				translation = Common::SharedPtr<Node>(new AssignmentStmtNode(Common::move(propExpr), Common::move(val)));
+				translation = Common::SharedPtr<Node>(new AssignmentStmtNode(bytecode.pos, Common::move(propExpr), Common::move(val)));
 			} else if (method == "count" && nargs == 2 && rawArgList[1]->getValue()->type == kDatumSymbol) {
 				// obj.count(#prop) => obj.prop.count
 				auto obj = rawArgList[0];
 				Common::String propName  = rawArgList[1]->getValue()->s;
-				auto propExpr = Common::SharedPtr<ObjPropExprNode>(new ObjPropExprNode(Common::move(obj), propName));
-				translation = Common::SharedPtr<Node>(new ObjPropExprNode(Common::move(propExpr), "count"));
+				auto propExpr = Common::SharedPtr<ObjPropExprNode>(new ObjPropExprNode(bytecode.pos, Common::move(obj), propName));
+				translation = Common::SharedPtr<Node>(new ObjPropExprNode(bytecode.pos, Common::move(propExpr), "count"));
 			} else if ((method == "setContents" || method == "setContentsAfter" || method == "setContentsBefore") && nargs == 2) {
 				// var.setContents(val) => put val into var
 				// var.setContentsAfter(val) => put val after var
@@ -1172,17 +1189,17 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 				}
 				auto var = rawArgList[0];
 				auto val = rawArgList[1];
-				translation = Common::SharedPtr<Node>(new PutStmtNode(putType, Common::move(var), Common::move(val)));
+				translation = Common::SharedPtr<Node>(new PutStmtNode(bytecode.pos, putType, Common::move(var), Common::move(val)));
 			} else if (method == "hilite" && nargs == 1) {
 				// chunk.hilite() => hilite chunk
 				auto chunk = rawArgList[0];
-				translation = Common::SharedPtr<Node>(new ChunkHiliteStmtNode(chunk));
+				translation = Common::SharedPtr<Node>(new ChunkHiliteStmtNode(bytecode.pos, chunk));
 			} else if (method == "delete" && nargs == 1) {
 				// chunk.delete() => delete chunk
 				auto chunk = rawArgList[0];
-				translation = Common::SharedPtr<Node>(new ChunkDeleteStmtNode(chunk));
+				translation = Common::SharedPtr<Node>(new ChunkDeleteStmtNode(bytecode.pos, chunk));
 			} else {
-				translation = Common::SharedPtr<Node>(new ObjCallNode(method, Common::move(argList)));
+				translation = Common::SharedPtr<Node>(new ObjCallNode(bytecode.pos, method, Common::move(argList)));
 			}
 		}
 		break;
@@ -1192,14 +1209,14 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 	case kOpGetTopLevelProp:
 		{
 			auto name_ = getName(bytecode.obj);
-			translation = Common::SharedPtr<VarNode>(new VarNode(name_));
+			translation = Common::SharedPtr<VarNode>(new VarNode(bytecode.pos, name_));
 		}
 		break;
 	case kOpNewObj:
 		{
 			auto objType = getName(bytecode.obj);
 			auto objArgs = pop();
-			translation = Common::SharedPtr<NewObjNode>(new NewObjNode(objType, Common::move(objArgs)));
+			translation = Common::SharedPtr<NewObjNode>(new NewObjNode(bytecode.pos, objType, Common::move(objArgs)));
 		}
 		break;
 	default:
@@ -1207,13 +1224,13 @@ uint32 Handler::translateBytecode(Bytecode &bytecode, uint32 index) {
 			auto commentText = StandardNames::getOpcodeName(bytecode.opID);
 			if (bytecode.opcode >= 0x40)
 				commentText += Common::String::format(" %d", bytecode.obj);
-			translation = Common::SharedPtr<CommentNode>(new CommentNode(commentText));
+			translation = Common::SharedPtr<CommentNode>(new CommentNode(bytecode.pos, commentText));
 			stack.clear(); // Clear stack so later bytecode won't be too screwed up
 		}
 	}
 
 	if (!translation)
-		translation = Common::SharedPtr<ErrorNode>(new ErrorNode());
+		translation = Common::SharedPtr<ErrorNode>(new ErrorNode(bytecode.pos));
 
 	bytecode.translation = translation;
 	if (translation->isExpression) {
diff --git a/engines/director/lingo/lingodec/handler.h b/engines/director/lingo/lingodec/handler.h
index 8fcafcfdad7..ae6ff0034e0 100644
--- a/engines/director/lingo/lingodec/handler.h
+++ b/engines/director/lingo/lingodec/handler.h
@@ -60,7 +60,7 @@ struct Handler {
 
 	bool isGenericEvent = false;
 
-	Handler(): ast(this) {}
+	Handler(): ast(0, this) {}
 
 	void setScript(Script *s) {
 		script = s;
@@ -78,8 +78,8 @@ struct Handler {
 	int variableMultiplier();
 	Common::SharedPtr<Node> readVar(int varType);
 	Common::String getVarNameFromSet(const Bytecode &bytecode);
-	Common::SharedPtr<Node> readV4Property(int propertyType, int propertyID);
-	Common::SharedPtr<Node> readChunkRef(Common::SharedPtr<Node> string);
+	Common::SharedPtr<Node> readV4Property(uint32 offset, int propertyType, int propertyID);
+	Common::SharedPtr<Node> readChunkRef(uint32 offset, Common::SharedPtr<Node> string);
 	void tagLoops();
 	bool isRepeatWithIn(uint32 startIndex, uint32 endIndex);
 	BytecodeTag identifyLoop(uint32 startIndex, uint32 endIndex);




More information about the Scummvm-git-logs mailing list