From 975167856ed0b11c2ede03c6eb750ca4e4a6a7fc Mon Sep 17 00:00:00 2001 From: Li Jin Date: Tue, 29 Oct 2019 11:25:27 +0800 Subject: complete moon compiler in C++. --- MoonParser/moon_compiler.cpp | 3851 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3851 insertions(+) create mode 100644 MoonParser/moon_compiler.cpp (limited to 'MoonParser/moon_compiler.cpp') diff --git a/MoonParser/moon_compiler.cpp b/MoonParser/moon_compiler.cpp new file mode 100644 index 0000000..85b19ff --- /dev/null +++ b/MoonParser/moon_compiler.cpp @@ -0,0 +1,3851 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std::string_view_literals; +#include "parser.hpp" +#include "moon_ast.h" +#include "moon_compiler.h" + +namespace MoonP { + +#define BLOCK_START do { +#define BLOCK_END } while (false); +#define BREAK_IF(cond) if (cond) break + +typedef std::list str_list; + +inline std::string s(std::string_view sv) { + return std::string(sv); +} + +class MoonCompliler { +public: + std::pair complile(const std::string& codes, bool lintGlobalVar, bool implicitReturnRoot, bool lineNumber) { + _lintGlobalVar = lintGlobalVar; + _lineNumber = lineNumber; + _input = _converter.from_bytes(codes); + error_list el; + State st; + ast_ptr root; + try { + root = parse(_input, File, el, &st); + } catch (const std::logic_error& error) { + clear(); + return {Empty, error.what()}; + } + if (root) { + try { + str_list out; + pushScope(); + transformBlock(root->block, out, implicitReturnRoot); + popScope(); + return {std::move(out.back()), Empty}; + } catch (const std::logic_error& error) { + clear(); + return {Empty, error.what()}; + } + } else { + clearBuf(); + for (error_list::iterator it = el.begin(); it != el.end(); ++it) { + const error& err = *it; + _buf << debugInfo("Syntax error."sv, &err); + } + std::pair result{Empty, clearBuf()}; + clear(); + return result; + } + } + + const std::unordered_map>& getGlobals() const { + return _globals; + } + + void clear() { + _indentOffset = 0; + _scopes.clear(); + _codeCache.clear(); + std::stack emptyWith; + _withVars.swap(emptyWith); + std::stack emptyContinue; + _continueVars.swap(emptyContinue); + _buf.str(""); + _buf.clear(); + _joinBuf.str(""); + _joinBuf.clear(); + _globals.clear(); + _input.clear(); + } +private: + bool _lintGlobalVar = false; + bool _lineNumber = false; + int _indentOffset = 0; + Converter _converter; + input _input; + std::list _codeCache; + std::stack _withVars; + std::stack _continueVars; + std::unordered_map> _globals; + std::ostringstream _buf; + std::ostringstream _joinBuf; + std::string _newLine = "\n"; + enum class LocalMode { + None = 0, + Capital = 1, + Any = 2 + }; + enum class ExportMode { + None = 0, + Capital = 1, + Any = 2 + }; + struct Scope { + ExportMode mode = ExportMode::None; + std::unique_ptr> vars; + std::unique_ptr> allows; + std::unique_ptr> exports; + }; + std::list _scopes; + static const std::string Empty; + + enum class MemType { + Builtin, + Common, + Property + }; + + struct ClassMember { + std::string item; + MemType type; + ast_node* node; + }; + + struct DestructItem { + bool isVariable = false; + std::string name; + std::string structure; + }; + + struct Destructure { + std::string value; + std::list items; + }; + + enum class ExpUsage { + Return, + Assignment, + Common, + Closure + }; + + enum class IfUsage { + Return, + Closure, + Common + }; + + void pushScope() { + _scopes.emplace_back(); + _scopes.back().vars = std::make_unique>(); + } + + void popScope() { + _scopes.pop_back(); + } + + bool isDefined(const std::string& name) { + bool isDefined = false; + int mode = int(std::isupper(name[0]) ? ExportMode::Capital : ExportMode::Any); + const auto& current = _scopes.back(); + if (int(current.mode) >= mode) { + if (current.exports) { + if (current.exports->find(name) != current.exports->end()) { + isDefined = true; + current.vars->insert(name); + } + } else { + isDefined = true; + current.vars->insert(name); + } + } + decltype(_scopes.back().allows.get()) allows = nullptr; + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + if (it->allows) allows = it->allows.get(); + } + bool checkShadowScopeOnly = false; + if (allows) { + checkShadowScopeOnly = allows->find(name) == allows->end(); + } + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + auto vars = it->vars.get(); + if (vars->find(name) != vars->end()) { + isDefined = true; + break; + } + if (checkShadowScopeOnly && it->allows) break; + } + return isDefined; + } + + bool isSolidDefined(const std::string& name) { + bool isDefined = false; + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + auto vars = it->vars.get(); + if (vars->find(name) != vars->end()) { + isDefined = true; + break; + } + } + return isDefined; + } + + void markVarShadowed() { + auto& scope = _scopes.back(); + scope.allows = std::make_unique>(); + } + + void markVarExported(ExportMode mode, bool specified) { + auto& scope = _scopes.back(); + scope.mode = mode; + if (specified && !scope.exports) { + scope.exports = std::make_unique>(); + } + } + + void addExportedVar(const std::string& name) { + auto& scope = _scopes.back(); + scope.exports->insert(name); + } + + void addToAllowList(const std::string& name) { + auto& scope = _scopes.back(); + scope.allows->insert(name); + } + + void forceAddToScope(const std::string& name) { + auto& scope = _scopes.back(); + scope.vars->insert(name); + } + + Scope& currentScope() { + return _scopes.back(); + } + + bool addToScope(const std::string& name) { + bool defined = isDefined(name); + if (!defined) { + auto& scope = currentScope(); + scope.vars->insert(name); + } + return !defined; + } + + std::string getUnusedName(std::string_view name) { + int index = 0; + std::string newName; + do { + newName = s(name) + std::to_string(index); + index++; + } while (isSolidDefined(newName)); + return newName; + } + + const std::string nll(ast_node* node) { + if (_lineNumber) { + return s(" -- "sv) + std::to_string(node->m_begin.m_line) + _newLine; + } else { + return _newLine; + } + } + + const std::string nlr(ast_node* node) { + if (_lineNumber) { + return s(" -- "sv) + std::to_string(node->m_end.m_line) + _newLine; + } else { + return _newLine; + } + } + + void incIndentOffset() { + _indentOffset++; + } + + void decIndentOffset() { + _indentOffset--; + } + + std::string indent() { + return std::string((_scopes.size() - 1 + _indentOffset) * 2, ' '); + } + + std::string indent(int offset) { + return std::string((_scopes.size() - 1 + _indentOffset + offset) * 2, ' '); + } + + std::string clearBuf() { + std::string str = _buf.str(); + _buf.str(""); + _buf.clear(); + return str; + } + + std::string join(const str_list& items) { + if (items.empty()) return Empty; + else if (items.size() == 1) return items.front(); + for (const auto& item : items) { + _joinBuf << item; + } + auto result = _joinBuf.str(); + _joinBuf.str(""); + _joinBuf.clear(); + return result; + } + + std::string join(const str_list& items, std::string_view sep) { + if (items.empty()) return Empty; + else if (items.size() == 1) return items.front(); + std::string sepStr = s(sep); + auto begin = ++items.begin(); + _joinBuf << items.front(); + for (auto it = begin; it != items.end(); ++it) { + _joinBuf << sepStr << *it; + } + auto result = _joinBuf.str(); + _joinBuf.str(""); + _joinBuf.clear(); + return result; + } + + std::string toString(ast_node* node) { + return _converter.to_bytes(std::wstring(node->m_begin.m_it, node->m_end.m_it)); + } + + std::string toString(input::iterator begin, input::iterator end) { + return _converter.to_bytes(std::wstring(begin, end)); + } + + Value_t* singleValueFrom(ast_node* item) { + Exp_t* exp = nullptr; + switch (item->getId()) { + case "Exp"_id: + exp = static_cast(item); + break; + case "ExpList"_id: { + auto expList = static_cast(item); + if (expList->exprs.size() == 1) { + exp = static_cast(expList->exprs.front()); + } + break; + } + case "ExpListLow"_id: { + auto expList = static_cast(item); + if (expList->exprs.size() == 1) { + exp = static_cast(expList->exprs.front()); + } + break; + } + } + if (!exp) return nullptr; + if (exp->opValues.empty()) { + return exp->value.get(); + } + return nullptr; + } + + SimpleValue_t* simpleSingleValueFrom(ast_node* expList) { + auto value = singleValueFrom(expList); + if (value && value->item.is()) { + return static_cast(value->item.get()); + } + return nullptr; + } + + Value_t* firstValueFrom(ast_node* item) { + Exp_t* exp = nullptr; + if (auto expList = ast_cast(item)) { + if (!expList->exprs.empty()) { + exp = static_cast(expList->exprs.front()); + } + } else { + exp = ast_cast(item); + } + return exp->value.get(); + } + + Statement_t* lastStatementFrom(Body_t* body) { + if (auto stmt = body->content.as()) { + return stmt; + } else { + auto node = body->content.to()->statements.objects().back(); + return static_cast(node); + } + } + + Statement_t* lastStatementFrom(Block_t* block) { + auto node = block->statements.objects().back(); + return static_cast(node); + } + + template + ast_ptr toAst(std::string_view codes, rule& r, ast_node* parent) { + _codeCache.push_back(_converter.from_bytes(s(codes))); + error_list el; + State st; + auto ptr = parse(_codeCache.back(), r, el, &st); + ptr->traverse([&](ast_node* node) { + node->m_begin.m_line = parent->m_begin.m_line; + node->m_end.m_line = parent->m_begin.m_line; + return traversal::Continue; + }); + return ptr; + } + + bool matchAst(rule& r, std::string_view codes) { + error_list el; + State st; + input i = _converter.from_bytes(s(codes)); + auto rEnd = rule(r >> eof()); + ast_ptr result(_parse(i, rEnd, el, &st)); + return result; + } + + bool isChainValueCall(ChainValue_t* chainValue) { + return ast_is(chainValue->items.back()); + } + + std::string singleVariableFrom(ast_node* expList) { + if (!ast_is(expList)) return Empty; + BLOCK_START + auto value = singleValueFrom(expList); + BREAK_IF(!value); + auto chainValue = value->getByPath(); + BREAK_IF(!chainValue); + BREAK_IF(chainValue->items.size() != 1); + auto callable = ast_cast(chainValue->items.front()); + BREAK_IF(!callable || !callable->item.is()); + str_list tmp; + transformCallable(callable, tmp, false); + return tmp.back(); + BLOCK_END + return Empty; + } + + bool isColonChain(ChainValue_t* chainValue) { + return ast_is(chainValue->items.back()); + } + + bool hasKeywordColonChainItem(ChainValue_t* chainValue) { + const auto& chainList = chainValue->items.objects(); + for (auto it = chainList.begin(); it != chainList.end(); ++it) { + if (auto colonItem = ast_cast(*it)) { + if (colonItem->name.is()) { + return true; + } + } + } + return false; + } + + bool isAssignable(const node_container& chainItems) { + if (chainItems.size() == 1) { + auto firstItem = chainItems.back(); + if (auto callable = ast_cast(firstItem)) { + switch (callable->item->getId()) { + case "Variable"_id: + case "SelfName"_id: + return true; + } + } else if (firstItem->getId() == "DotChainItem"_id) { + return true; + } + } else { + auto lastItem = chainItems.back(); + switch (lastItem->getId()) { + case "DotChainItem"_id: + case "Exp"_id: + return true; + } + } + return false; + } + + bool isAssignable(Exp_t* exp) { + if (auto value = singleValueFrom(exp)) { + auto item = value->item.get(); + switch (item->getId()) { + case "simple_table"_id: + return true; + case "SimpleValue"_id: { + auto simpleValue = static_cast(item); + if (simpleValue->value.is()) { + return true; + } + return false; + } + case "ChainValue"_id: { + auto chainValue = static_cast(item); + return isAssignable(chainValue->items.objects()); + } + } + } + return false; + } + + bool isAssignable(Assignable_t* assignable) { + if (auto assignableChain = ast_cast(assignable->item)) { + return isAssignable(assignableChain->items.objects()); + } + return true; + } + + void checkAssignable(ExpList_t* expList) { + for (auto exp_ : expList->exprs.objects()) { + Exp_t* exp = static_cast(exp_); + if (!isAssignable(exp)) { + throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, exp)); + } + } + } + + std::string debugInfo(std::string_view msg, const input_range* loc) { + const int ASCII = 255; + int length = loc->m_begin.m_line; + auto begin = _input.begin(); + auto end = _input.end(); + int count = 0; + for (auto it = _input.begin(); it != _input.end(); ++it) { + if (*it == '\n') { + if (count + 1 == length) { + end = it; + break; + } else { + begin = it + 1; + } + count++; + } + } + auto line = _converter.to_bytes(std::wstring(begin, end)); + int oldCol = loc->m_begin.m_col; + int col = loc->m_begin.m_col - 1; + auto it = begin; + for (int i = 0; i < oldCol; ++i) { + if (*it > ASCII) { + ++col; + } + ++it; + } + replace(line, "\t"sv, " "sv); + std::ostringstream buf; + buf << loc->m_begin.m_line << ": "sv << msg << + '\n' << line << '\n' << std::string(col, ' ') << "^"sv; + return buf.str(); + } + + void transformStatement(Statement_t* statement, str_list& out) { + auto x = statement; + if (statement->appendix) { + if (auto assignment = assignmentFrom(statement)) { + auto preDefine = getPredefine(transformAssignDefs(assignment->expList)); + if (!preDefine.empty()) out.push_back(preDefine + nll(statement)); + } + auto appendix = statement->appendix.get(); + switch (appendix->item->getId()) { + case "if_else_line"_id: { + auto if_else_line = appendix->item.to(); + auto ifNode = x->new_ptr(); + + auto ifCond = x->new_ptr(); + ifCond->condition.set(if_else_line->condition); + ifNode->nodes.push_back(ifCond); + + auto stmt = x->new_ptr(); + stmt->content.set(statement->content); + auto body = x->new_ptr(); + body->content.set(stmt); + ifNode->nodes.push_back(body); + + if (!ast_is(if_else_line->elseExpr)) { + auto expList = x->new_ptr(); + expList->exprs.push_back(if_else_line->elseExpr); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto stmt = x->new_ptr(); + stmt->content.set(expListAssign); + auto body = x->new_ptr(); + body->content.set(stmt); + ifNode->nodes.push_back(body); + } + + statement->appendix.set(nullptr); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(ifNode); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + statement->content.set(expListAssign); + break; + } + case "unless_line"_id: { + auto unless_line = appendix->item.to(); + auto unless = x->new_ptr(); + + auto ifCond = x->new_ptr(); + ifCond->condition.set(unless_line->condition); + unless->nodes.push_back(ifCond); + + auto stmt = x->new_ptr(); + stmt->content.set(statement->content); + auto body = x->new_ptr(); + body->content.set(stmt); + unless->nodes.push_back(body); + + statement->appendix.set(nullptr); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(unless); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto exprList = x->new_ptr(); + exprList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(exprList); + statement->content.set(expListAssign); + break; + } + case "CompInner"_id: { + auto compInner = appendix->item.to(); + auto comp = x->new_ptr(); + comp->forLoop.set(compInner); + auto stmt = x->new_ptr(); + stmt->content.set(statement->content); + comp->value.set(stmt); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(comp); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + statement->content.set(expListAssign); + statement->appendix.set(nullptr); + break; + } + default: break; + } + } + auto content = statement->content.get(); + if (!content) { + out.push_back(Empty); + return; + } + switch (content->getId()) { + case "Import"_id: transformImport(static_cast(content), out); break; + case "While"_id: transformWhile(static_cast(content), out); break; + case "For"_id: transformFor(static_cast(content), out); break; + case "ForEach"_id: transformForEach(static_cast(content), out); break; + case "Return"_id: transformReturn(static_cast(content), out); break; + case "Local"_id: transformLocal(static_cast(content), out); break; + case "Export"_id: transformExport(static_cast(content), out); break; + case "BreakLoop"_id: transformBreakLoop(static_cast(content), out); break; + case "ExpListAssign"_id: { + auto expListAssign = static_cast(content); + if (expListAssign->action) { + transformAssignment(expListAssign, out); + } else { + auto expList = expListAssign->expList.get(); + if (expList->exprs.objects().empty()) { + out.push_back(Empty); + break; + } + if (auto singleValue = singleValueFrom(expList)) { + if (auto simpleValue = singleValue->item.as()) { + auto value = simpleValue->value.get(); + bool specialSingleValue = true; + switch (value->getId()) { + case "If"_id: transformIf(static_cast(value), out); break; + case "ClassDecl"_id: transformClassDecl(static_cast(value), out); break; + case "Unless"_id: transformUnless(static_cast(value), out); break; + case "Switch"_id: transformSwitch(static_cast(value), out); break; + case "With"_id: transformWith(static_cast(value), out); break; + case "ForEach"_id: transformForEach(static_cast(value), out); break; + case "For"_id: transformFor(static_cast(value), out); break; + case "While"_id: transformWhile(static_cast(value), out); break; + case "Do"_id: transformDo(static_cast(value), out); break; + case "Comprehension"_id: transformCompCommon(static_cast(value), out); break; + default: specialSingleValue = false; break; + } + if (specialSingleValue) { + break; + } + } + if (auto chainValue = singleValue->item.as()) { + if (isChainValueCall(chainValue)) { + transformChainValue(chainValue, out, ExpUsage::Common); + break; + } + } + } + auto assign = x->new_ptr(); + assign->values.dup(expList->exprs); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst("_", ExpList, x)); + assignment->action.set(assign); + transformAssignment(assignment, out); + } + break; + } + default: break; + } + } + + str_list getAssignVars(ExpListAssign_t* assignment) { + str_list vars; + if (!assignment->action.is()) return vars; + for (auto exp : assignment->expList->exprs.objects()) { + auto var = singleVariableFrom(exp); + vars.push_back(var.empty() ? Empty : var); + } + return vars; + } + + str_list getAssignVars(With_t* with) { + str_list vars; + for (auto exp : with->valueList->exprs.objects()) { + auto var = singleVariableFrom(exp); + vars.push_back(var.empty() ? Empty : var); + } + return vars; + } + + str_list getAssignDefs(ExpList_t* expList) { + str_list preDefs; + for (auto exp_ : expList->exprs.objects()) { + auto exp = static_cast(exp_); + if (auto value = singleValueFrom(exp)) { + if (auto chain = value->item.as()) { + BLOCK_START + BREAK_IF(chain->items.size() != 1); + auto callable = ast_cast(chain->items.front()); + BREAK_IF(!callable); + auto var = callable->item.as(); + BREAK_IF(!var); + auto name = toString(var); + if (!isDefined(name)) { + preDefs.push_back(name); + } + BLOCK_END + } + } else { + throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, exp)); + } + } + return preDefs; + } + + str_list transformAssignDefs(ExpList_t* expList) { + str_list preDefs; + for (auto exp_ : expList->exprs.objects()) { + auto exp = static_cast(exp_); + if (auto value = singleValueFrom(exp)) { + if (auto chain = value->item.as()) { + BLOCK_START + BREAK_IF(chain->items.size() != 1); + auto callable = ast_cast(chain->items.front()); + BREAK_IF(!callable); + auto var = callable->item.as(); + BREAK_IF(!var); + auto name = toString(var); + if (addToScope(name)) { + preDefs.push_back(name); + } + BLOCK_END + } + } else { + throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, exp)); + } + } + return preDefs; + } + + std::string getPredefine(const str_list& defs) { + if (defs.empty()) return Empty; + return indent() + s("local "sv) + join(defs, ", "sv); + } + + std::string getDestrucureDefine(ExpListAssign_t* assignment) { + auto info = extractDestructureInfo(assignment); + if (!info.first.empty()) { + for (const auto& destruct : info.first) { + str_list defs; + for (const auto& item : destruct.items) { + if (item.isVariable && addToScope(item.name)) { + defs.push_back(item.name); + } + } + if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv); + } + } + return clearBuf(); + } + + std::string getPredefine(ExpListAssign_t* assignment) { + auto preDefine = getDestrucureDefine(assignment); + if (preDefine.empty()) { + preDefine = getPredefine(transformAssignDefs(assignment->expList)); + } + return preDefine.empty() ? preDefine : preDefine + nll(assignment); + } + + ExpList_t* expListFrom(Statement_t* statement) { + if (auto expListAssign = statement->content.as()) { + if (!expListAssign->action) { + return expListAssign->expList.get(); + } + } + return nullptr; + } + + ExpListAssign_t* assignmentFrom(Statement_t* statement) { + if (auto expListAssign = statement->content.as()) { + if (expListAssign->action) { + return expListAssign; + } + } + return nullptr; + } + + void assignLastExplist(ExpList_t* expList, Body_t* body) { + auto last = lastStatementFrom(body); + if (!last) return; + bool lastAssignable = expListFrom(last) || ast_is(last->content); + if (lastAssignable) { + auto x = last; + auto newAssignment = x->new_ptr(); + newAssignment->expList.set(expList); + auto assign = x->new_ptr(); + if (auto valueList = last->content.as()) { + assign->values.dup(valueList->expList->exprs); + } else { + auto simpleValue = x->new_ptr(); + simpleValue->value.set(last->content); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + assign->values.push_back(exp); + } + newAssignment->action.set(assign); + last->content.set(newAssignment); + } + } + + void transformAssignment(ExpListAssign_t* assignment, str_list& out) { + checkAssignable(assignment->expList); + BLOCK_START + auto assign = ast_cast(assignment->action); + BREAK_IF(!assign || assign->values.objects().size() != 1); + auto value = assign->values.objects().front(); + if (ast_is(value)) { + if (auto val = simpleSingleValueFrom(value)) { + value = val->value.get(); + } + } + switch (value->getId()) { + case "If"_id: + case "Unless"_id: { + auto expList = assignment->expList.get(); + str_list temp; + auto defs = transformAssignDefs(expList); + if (!defs.empty()) temp.push_back(getPredefine(defs) + nll(expList)); + value->traverse([&](ast_node* node) { + switch (node->getId()) { + case "IfCond"_id: return traversal::Return; + case "Body"_id: { + auto body = static_cast(node); + assignLastExplist(expList, body); + return traversal::Return; + } + default: return traversal::Continue; + } + }); + switch (value->getId()) { + case "If"_id: transformIf(static_cast(value), temp); break; + case "Unless"_id: transformUnless(static_cast(value), temp); break; + } + out.push_back(join(temp)); + return; + } + case "Switch"_id: { + auto switchNode = static_cast(value); + auto expList = assignment->expList.get(); + for (auto branch_ : switchNode->branches.objects()) { + auto branch = static_cast(branch_); + assignLastExplist(expList, branch->body); + } + if (switchNode->lastBranch) { + assignLastExplist(expList, switchNode->lastBranch); + } + std::string preDefine = getPredefine(assignment); + transformSwitch(switchNode, out); + out.back() = preDefine + out.back(); + return; + } + case "With"_id: { + auto withNode = static_cast(value); + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformWith(withNode, temp, expList); + out.push_back(preDefine + temp.back()); + return; + } + case "Do"_id: { + auto doNode = static_cast(value); + auto expList = assignment->expList.get(); + assignLastExplist(expList, doNode->body); + std::string preDefine = getPredefine(assignment); + transformDo(doNode, out); + out.back() = preDefine + out.back(); + return; + } + case "Comprehension"_id: { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformCompInPlace(static_cast(value), expList, out); + out.back() = preDefine + out.back(); + return; + } + case "TblComprehension"_id: { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformTblCompInPlace(static_cast(value), expList, out); + out.back() = preDefine + out.back(); + return; + } + case "For"_id: { + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformForInPlace(static_cast(value), temp, expList); + out.push_back(preDefine + temp.back()); + return; + } + case "ForEach"_id: { + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformForEachInPlace(static_cast(value), temp, expList); + out.push_back(preDefine + temp.back()); + return; + } + case "ClassDecl"_id: { + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformClassDecl(static_cast(value), temp, ExpUsage::Assignment, expList); + out.push_back(preDefine + temp.back()); + return; + } + case "While"_id: { + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformWhileInPlace(static_cast(value), temp, expList); + out.push_back(preDefine + temp.back()); + return; + } + } + auto exp = ast_cast(value); + BREAK_IF(!exp); + if (auto chainValue = exp->value->item.as()) { + if (isColonChain(chainValue)) { + auto assignable = assignment->expList.get(); + std::string preDefine = getPredefine(transformAssignDefs(assignable)); + transformColonChain(chainValue, out, ExpUsage::Assignment, assignable); + auto nl = preDefine.empty() ? Empty : nll(chainValue); + if (!preDefine.empty()) out.back() = preDefine + nl + out.back(); + return; + } else if (hasKeywordColonChainItem(chainValue)) { + transformChainValue(chainValue, out, ExpUsage::Assignment, assignment->expList); + return; + } + } + BLOCK_END + auto info = extractDestructureInfo(assignment); + if (info.first.empty()) { + transformAssignmentCommon(assignment, out); + } else { + str_list temp; + for (const auto& destruct : info.first) { + if (destruct.items.size() == 1) { + auto& pair = destruct.items.front(); + _buf << indent(); + if (pair.isVariable && !isDefined(pair.name)) { + _buf << s("local "sv); + } + _buf << pair.name << " = "sv << info.first.front().value << pair.structure << nll(assignment); + addToScope(pair.name); + temp.push_back(clearBuf()); + } else if (matchAst(Name, destruct.value)) { + str_list defs, names, values; + for (const auto& item : destruct.items) { + if (item.isVariable && addToScope(item.name)) { + defs.push_back(item.name); + } + names.push_back(item.name); + values.push_back(item.structure); + } + for (auto& v : values) v.insert(0, destruct.value); + if (defs.empty()) { + _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); + } else { + _buf << indent() << "local "sv; + if (defs.size() != names.size()) { + _buf << join(defs,", "sv) << nll(assignment) << indent(); + } + _buf << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); + } + temp.push_back(clearBuf()); + } else { + str_list defs, names, values; + for (const auto& item : destruct.items) { + if (item.isVariable && addToScope(item.name)) { + defs.push_back(item.name); + } + names.push_back(item.name); + values.push_back(item.structure); + } + if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv) << nll(assignment); + _buf << indent() << "do"sv << nll(assignment); + pushScope(); + auto objVar = getUnusedName("_obj_"); + for (auto& v : values) v.insert(0, objVar); + _buf << indent() << "local "sv << objVar << " = "sv << destruct.value << nll(assignment); + _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); + popScope(); + _buf << indent() << "end"sv << nll(assignment); + temp.push_back(clearBuf()); + } + } + if (info.second) { + transformAssignmentCommon(info.second, temp); + } + out.push_back(join(temp)); + } + } + + void transformAssignItem(ast_node* value, str_list& out) { + switch (value->getId()) { + case "With"_id: transformWithClosure(static_cast(value), out); break; + case "If"_id: transformIf(static_cast(value), out, IfUsage::Closure); break; + case "Switch"_id: transformSwitchClosure(static_cast(value), out); break; + case "TableBlock"_id: transformTableBlock(static_cast(value), out); break; + case "Exp"_id: transformExp(static_cast(value), out); break; + default: break; + } + } + + std::list destructFromExp(ast_node* node) { + const node_container* tableItems = nullptr; + if (ast_cast(node)) { + auto item = singleValueFrom(node)->item.get(); + if (!item) throw std::logic_error(debugInfo("Invalid destructure value."sv, node)); + auto tbA = item->getByPath(); + if (tbA) { + tableItems = &tbA->values.objects(); + } else { + auto tbB = ast_cast(item); + if (tbB) tableItems = &tbB->pairs.objects(); + } + } else if (auto table = ast_cast(node)) { + tableItems = &table->values.objects(); + } + std::list pairs; + int index = 0; + for (auto pair : *tableItems) { + switch (pair->getId()) { + case "Exp"_id: { + ++index; + if (!isAssignable(static_cast(pair))) { + throw std::logic_error(debugInfo("Can't destructure value."sv, pair)); + } + auto value = singleValueFrom(pair); + auto item = value->item.get(); + if (ast_cast(item) || + item->getByPath()) { + auto subPairs = destructFromExp(pair); + for (auto& p : subPairs) { + pairs.push_back({p.isVariable, p.name, + s("["sv) + std::to_string(index) + s("]"sv) + p.structure}); + } + } else { + bool checkGlobal = _lintGlobalVar; + _lintGlobalVar = false; + auto exp = static_cast(pair); + auto varName = singleVariableFrom(exp); + bool isVariable = !varName.empty(); + if (!isVariable) { + str_list temp; + transformExp(exp, temp); + varName = std::move(temp.back()); + } + _lintGlobalVar = checkGlobal; + pairs.push_back({ + isVariable, + varName, + s("["sv) + std::to_string(index) + s("]"sv) + }); + } + break; + } + case "variable_pair"_id: { + auto vp = static_cast(pair); + auto name = toString(vp->name); + pairs.push_back({true, name, s("."sv) + name}); + break; + } + case "normal_pair"_id: { + auto np = static_cast(pair); + auto key = np->key->getByPath(); + if (!key) throw std::logic_error(debugInfo("Invalid key for destructure."sv, np)); + if (auto exp = np->value.as()) { + if (!isAssignable(exp)) throw std::logic_error(debugInfo("Can't destructure value."sv, exp)); + auto item = singleValueFrom(exp)->item.get(); + if (ast_cast(item) || + item->getByPath()) { + auto subPairs = destructFromExp(exp); + for (auto& p : subPairs) { + pairs.push_back({p.isVariable, p.name, + s("."sv) + toString(key) + p.structure}); + } + } else { + bool checkGlobal = _lintGlobalVar; + _lintGlobalVar = false; + auto varName = singleVariableFrom(exp); + bool isVariable = !varName.empty(); + if (!isVariable) { + str_list temp; + transformExp(exp, temp); + varName = std::move(temp.back()); + } + _lintGlobalVar = checkGlobal; + pairs.push_back({ + isVariable, + varName, + s("."sv) + toString(key) + }); + } + break; + } + if (np->value.as()) { + auto subPairs = destructFromExp(pair); + for (auto& p : subPairs) { + pairs.push_back({p.isVariable, p.name, + s("."sv) + toString(key) + p.structure}); + } + } + break; + } + } + } + return pairs; + } + + std::pair, ast_ptr> + extractDestructureInfo(ExpListAssign_t* assignment) { + auto x = assignment; + std::list destructs; + if (!assignment->action.is()) return { destructs, nullptr }; + auto exprs = assignment->expList->exprs.objects(); + auto values = assignment->action.to()->values.objects(); + size_t size = std::max(exprs.size(),values.size()); + ast_ptr var; + if (exprs.size() < size) { + var = toAst("_"sv, Exp, x); + while (exprs.size() < size) exprs.emplace_back(var); + } + ast_ptr nullNode; + if (values.size() < size) { + nullNode = toAst("nil"sv, Exp, x); + while (values.size() < size) values.emplace_back(nullNode); + } + using iter = node_container::iterator; + std::vector> destructPairs; + str_list temp; + for (auto i = exprs.begin(), j = values.begin(); i != exprs.end(); ++i, ++j) { + auto expr = *i; + ast_node* destructNode = expr->getByPath(); + if (destructNode || (destructNode = expr->getByPath())) { + destructPairs.push_back({i,j}); + pushScope(); + transformAssignItem(*j, temp); + popScope(); + auto& destruct = destructs.emplace_back(); + destruct.value = temp.back(); + temp.pop_back(); + auto pairs = destructFromExp(expr); + destruct.items = std::move(pairs); + } + } + for (const auto& p : destructPairs) { + exprs.erase(p.first); + values.erase(p.second); + } + ast_ptr newAssignment; + if (!destructPairs.empty() && !exprs.empty()) { + auto x = assignment; + auto expList = x->new_ptr(); + auto newAssign = x->new_ptr(); + newAssign->expList.set(expList); + for (auto expr : exprs) expList->exprs.push_back(expr); + auto assign = x->new_ptr(); + for (auto value : values) assign->values.push_back(value); + newAssign->action.set(assign); + newAssignment = newAssign; + } + return {std::move(destructs), newAssignment}; + } + + void transformAssignmentCommon(ExpListAssign_t* assignment, str_list& out) { + auto x = assignment; + str_list temp; + auto expList = assignment->expList.get(); + auto action = assignment->action.get(); + switch (action->getId()) { + case "Update"_id: { + auto update = static_cast(action); + auto leftExp = static_cast(expList->exprs.objects().front()); + auto leftValue = singleValueFrom(leftExp); + if (!leftValue) throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, leftExp)); + if (auto chain = leftValue->getByPath()) { + auto tmpChain = x->new_ptr(); + for (auto item : chain->items.objects()) { + bool itemAdded = false; + BLOCK_START + auto exp = ast_cast(item); + BREAK_IF(!exp); + auto var = singleVariableFrom(exp); + BREAK_IF(!var.empty()); + auto upVar = getUnusedName("_update_"); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(upVar, ExpList, x)); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + transformAssignment(assignment, temp); + tmpChain->items.push_back(toAst(upVar, Exp, x)); + itemAdded = true; + BLOCK_END + if (!itemAdded) tmpChain->items.push_back(item); + } + chain->items.clear(); + chain->items.dup(tmpChain->items); + } + transformValue(leftValue, temp); + auto left = std::move(temp.back()); + temp.pop_back(); + transformExp(update->value, temp); + auto right = std::move(temp.back()); + temp.pop_back(); + if (!singleValueFrom(update->value)) { + right = s("("sv) + right + s(")"sv); + } + _buf << join(temp) << indent() << left << " = "sv << left << + " "sv << toString(update->op) << " "sv << right << nll(assignment); + out.push_back(clearBuf()); + break; + } + case "Assign"_id: { + auto defs = getAssignDefs(expList); + bool oneLined = defs.size() == expList->exprs.objects().size() && + traversal::Stop != action->traverse([&](ast_node* n) { + if (n->getId() == "Callable"_id) { + if (auto name = n->getByPath()) { + for (const auto& def : defs) { + if (def == toString(name)) { + return traversal::Stop; + } + } + } + } + return traversal::Continue; + }); + if (oneLined) { + auto assign = static_cast(action); + for (auto value : assign->values.objects()) { + transformAssignItem(value, temp); + } + std::string preDefine = getPredefine(defs); + for (const auto& def : defs) { + addToScope(def); + } + if (preDefine.empty()) { + transformExpList(expList, temp); + std::string left = std::move(temp.back()); + temp.pop_back(); + out.push_back(indent() + left + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); + } else { + out.push_back(preDefine + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); + } + } + else { + std::string preDefine = getPredefine(defs); + for (const auto& def : defs) { + addToScope(def); + } + transformExpList(expList, temp); + std::string left = temp.back(); + temp.pop_back(); + auto assign = static_cast(action); + for (auto value : assign->values.objects()) { + transformAssignItem(value, temp); + } + out.push_back((preDefine.empty() ? Empty : preDefine + nll(assignment)) + indent() + left + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); + } + break; + } + default: break; + } + } + + void transformCond(const node_container& nodes, str_list& out, IfUsage usage = IfUsage::Common, bool unless = false) { + std::vector> ns(false); + for (auto it = nodes.rbegin(); it != nodes.rend(); ++it) { + ns.push_back(*it); + if (auto cond = ast_cast(*it)) { + if (*it != nodes.front() && cond->assign) { + auto x = *it; + auto newIf = x->new_ptr(); + for (auto j = ns.rbegin(); j != ns.rend(); ++j) { + newIf->nodes.push_back(*j); + } + ns.clear(); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(newIf); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto stmt = x->new_ptr(); + stmt->content.set(expListAssign); + auto body = x->new_ptr(); + body->content.set(stmt); + ns.push_back(body.get()); + } + } + } + if (nodes.size() != ns.size()) { + auto x = ns.back(); + auto newIf = x->new_ptr(); + for (auto j = ns.rbegin(); j != ns.rend(); ++j) { + newIf->nodes.push_back(*j); + } + transformCond(newIf->nodes.objects(), out, usage, unless); + return; + } + str_list temp; + if (usage == IfUsage::Closure) { + temp.push_back(s("(function()"sv) + nll(nodes.front())); + pushScope(); + } + std::list> ifCondPairs; + ifCondPairs.emplace_back(); + for (auto node : nodes) { + switch (node->getId()) { + case "IfCond"_id: + ifCondPairs.back().first = static_cast(node); + break; + case "Body"_id: + ifCondPairs.back().second = static_cast(node); + ifCondPairs.emplace_back(); + break; + default: break; + } + } + auto assign = ifCondPairs.front().first->assign.get(); + bool storingValue = false; + ast_ptr extraAssignment; + if (assign) { + auto exp = ifCondPairs.front().first->condition.get(); + auto x = exp; + auto var = singleVariableFrom(exp); + if (var.empty()) { + storingValue = true; + std::string desVar = getUnusedName("_des_"); + if (assign->values.objects().size() == 1) { + auto var = singleVariableFrom(assign->values.objects().front()); + if (!var.empty()) { + desVar = var; + storingValue = false; + } + } + if (storingValue) { + if (usage != IfUsage::Closure) { + temp.push_back(indent() + s("do"sv) + nll(assign)); + pushScope(); + } + auto expList = toAst(desVar, ExpList, x); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + { + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assignOne = x->new_ptr(); + auto valExp = toAst(desVar, Exp, x); + assignOne->values.push_back(valExp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assignOne); + extraAssignment.set(assignment); + ifCondPairs.front().first->condition.set(valExp); + } + } else { + if (!isDefined(var)) { + storingValue = true; + if (usage != IfUsage::Closure) { + temp.push_back(indent() + s("do"sv) + nll(assign)); + pushScope(); + } + } + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + } + for (const auto& pair : ifCondPairs) { + if (pair.first) { + str_list tmp; + auto condition = pair.first->condition.get(); + if (unless) { + if (auto value = singleValueFrom(condition)) { + transformValue(value, tmp); + } else { + transformExp(condition, tmp); + tmp.back() = s("("sv) + tmp.back() + s(")"sv); + } + tmp.back().insert(0, s("not "sv)); + unless = false; + } else { + transformExp(condition, tmp); + } + _buf << indent(); + if (pair != ifCondPairs.front()) { + _buf << "else"sv; + } + _buf << "if "sv << tmp.back() << " then"sv << nll(condition); + temp.push_back(clearBuf()); + } + if (pair.second) { + if (!pair.first) { + temp.push_back(indent() + s("else"sv) + nll(pair.second)); + } + pushScope(); + if (pair == ifCondPairs.front() && extraAssignment) { + transformAssignment(extraAssignment, temp); + } + transformBody(pair.second, temp, usage != IfUsage::Common); + popScope(); + } + if (!pair.first) { + temp.push_back(indent() + s("end"sv) + nll(nodes.front())); + break; + } + } + if (storingValue && usage != IfUsage::Closure) { + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(nodes.front())); + } + if (usage == IfUsage::Closure) { + popScope(); + temp.push_back(indent() + s("end)()"sv)); + } + out.push_back(join(temp)); + } + + void transformIf(If_t* ifNode, str_list& out, IfUsage usage = IfUsage::Common) { + transformCond(ifNode->nodes.objects(), out, usage); + } + + void transformUnless(Unless_t* unless, str_list& out, IfUsage usage = IfUsage::Common) { + transformCond(unless->nodes.objects(), out, usage, true); + } + + void transformExpList(ExpList_t* expList, str_list& out) { + str_list temp; + for (auto exp : expList->exprs.objects()) { + transformExp(static_cast(exp), temp); + } + out.push_back(join(temp, ", "sv)); + } + + void transformExpListLow(ExpListLow_t* expListLow, str_list& out) { + str_list temp; + for (auto exp : expListLow->exprs.objects()) { + transformExp(static_cast(exp), temp); + } + out.push_back(join(temp, ", "sv)); + } + + void transformExp(Exp_t* exp, str_list& out) { + str_list temp; + transformValue(exp->value, temp); + for (auto _opValue : exp->opValues.objects()) { + auto opValue = static_cast(_opValue); + transformBinaryOperator(opValue->op, temp); + transformValue(opValue->value, temp); + } + out.push_back(join(temp, " "sv)); + } + + void transformValue(Value_t* value, str_list& out) { + auto item = value->item.get(); + switch (item->getId()) { + case "SimpleValue"_id: transformSimpleValue(static_cast(item), out); break; + case "simple_table"_id: transform_simple_table(static_cast(item), out); break; + case "ChainValue"_id: { + auto chainValue = static_cast(item); + if (isColonChain(chainValue)) { + transformColonChainClosure(chainValue, out); + } else { + transformChainValue(chainValue, out); + } + break; + } + case "String"_id: transformString(static_cast(item), out); break; + default: break; + } + } + + void transformCallable(Callable_t* callable, str_list& out, bool invoke) { + auto item = callable->item.get(); + switch (item->getId()) { + case "Variable"_id: { + transformVariable(static_cast(item), out); + if (_lintGlobalVar && !isDefined(out.back())) { + if (_globals.find(out.back()) == _globals.end()) { + _globals[out.back()] = {item->m_begin.m_line, item->m_begin.m_col}; + } + } + break; + } + case "SelfName"_id: { transformSelfName(static_cast(item), out, invoke); + if (_lintGlobalVar) { + std::string self("self"sv); + if (!isDefined(self)) { + if (_globals.find(self) == _globals.end()) { + _globals[self] = {item->m_begin.m_line, item->m_begin.m_col}; + } + } + } + break; + } + case "VarArg"_id: out.push_back(s("..."sv)); break; + case "Parens"_id: transformParens(static_cast(item), out); break; + default: break; + } + } + + void transformParens(Parens_t* parans, str_list& out) { + str_list temp; + transformExp(parans->expr, temp); + out.push_back(s("("sv) + temp.front() + s(")"sv)); + } + + void transformSimpleValue(SimpleValue_t* simpleValue, str_list& out) { + auto value = simpleValue->value.get(); + switch (value->getId()) { + case "const_value"_id: transform_const_value(static_cast(value), out); break; + case "If"_id: transformIf(static_cast(value), out, IfUsage::Closure); break; + case "Unless"_id: transformUnless(static_cast(value), out, IfUsage::Closure); break; + case "Switch"_id: transformSwitchClosure(static_cast(value), out); break; + case "With"_id: transformWithClosure(static_cast(value), out); break; + case "ClassDecl"_id: transformClassDeclClosure(static_cast(value), out); break; + case "ForEach"_id: transformForEachClosure(static_cast(value), out); break; + case "For"_id: transformForClosure(static_cast(value), out); break; + case "While"_id: transformWhileClosure(static_cast(value), out); break; + case "Do"_id: transformDoClosure(static_cast(value), out); break; + case "unary_exp"_id: transform_unary_exp(static_cast(value), out); break; + case "TblComprehension"_id: transformTblCompClosure(static_cast(value), out); break; + case "TableLit"_id: transformTableLit(static_cast(value), out); break; + case "Comprehension"_id: transformCompClosure(static_cast(value), out); break; + case "FunLit"_id: transformFunLit(static_cast(value), out); break; + case "Num"_id: transformNum(static_cast(value), out); break; + default: break; + } + } + + void transformFunLit(FunLit_t* funLit, str_list& out) { + str_list temp; + bool isFatArrow = toString(funLit->arrow) == "=>"sv; + pushScope(); + if (isFatArrow) { + forceAddToScope(s("self"sv)); + } + if (auto argsDef = funLit->argsDef.get()) { + transformFnArgsDef(argsDef, temp); + if (funLit->body) { + transformBody(funLit->body, temp, true); + } else { + temp.push_back(Empty); + } + auto it = temp.begin(); + auto& args = *it; + auto& initArgs = *(++it); + auto& bodyCodes = *(++it); + _buf << "function("sv << + (isFatArrow ? s("self, "sv) : Empty) << + args << ')'; + if (!initArgs.empty() || !bodyCodes.empty()) { + _buf << nlr(argsDef) << initArgs << bodyCodes; + popScope(); + _buf << indent() << "end"sv; + } else { + popScope(); + _buf << " end"sv; + } + } else { + if (funLit->body) { + transformBody(funLit->body, temp, true); + } else { + temp.push_back(Empty); + } + auto& bodyCodes = temp.back(); + _buf << "function("sv << + (isFatArrow ? s("self"sv) : Empty) << + ')'; + if (!bodyCodes.empty()) { + _buf << nll(funLit) << bodyCodes; + popScope(); + _buf << indent() << "end"sv; + } else { + popScope(); + _buf << " end"sv; + } + } + out.push_back(clearBuf()); + } + + void transformCodes(const node_container& nodes, str_list& out, bool implicitReturn) { + LocalMode mode = LocalMode::None; + Local_t* any = nullptr, *capital = nullptr; + for (auto node : nodes) { + auto stmt = static_cast(node); + if (auto local = stmt->content.as()) { + if (auto flag = local->name.as()) { + LocalMode newMode = toString(flag) == "*"sv ? LocalMode::Any : LocalMode::Capital; + if (int(newMode) > int(mode)) { + mode = newMode; + } + if (mode == LocalMode::Any) { + if (!any) any = local; + if (!capital) capital = local; + } else { + if (!capital) capital = local; + } + } else { + auto names = local->name.to(); + for (auto name : names->names.objects()) { + local->forceDecls.push_back(toString(name)); + } + } + } else if (mode != LocalMode::None) { + ClassDecl_t* classDecl = nullptr; + if (auto assignment = assignmentFrom(stmt)) { + auto vars = getAssignVars(assignment); + for (const auto& var : vars) { + if (var.empty()) continue; + if (std::isupper(var[0]) && capital) { + capital->decls.push_back(var); + } else if (any) { + any->decls.push_back(var); + } + } + auto info = extractDestructureInfo(assignment); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable) { + if (std::isupper(item.name[0]) && capital) { capital->decls.push_back(item.name); + } else if (any) { + any->decls.push_back(item.name); + } + } + } + BLOCK_START + auto assign = assignment->action.as(); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto exp = ast_cast(assign->values.objects().front()); + BREAK_IF(!exp); + auto value = singleValueFrom(exp); + classDecl = value->getByPath(); + BLOCK_END + } else if (auto expList = expListFrom(stmt)) { + auto value = singleValueFrom(expList); + classDecl = value->getByPath(); + } + if (classDecl) { + if (auto variable = classDecl->name->item.as()) { + auto className = toString(variable); + if (!className.empty()) { + if (std::isupper(className[0]) && capital) { + capital->decls.push_back(className); + } else if (any) { + any->decls.push_back(className); + } + } + } + } + } + } + if (implicitReturn) { + auto last = static_cast(nodes.back()); + auto x = last; + BLOCK_START + auto expList = expListFrom(last); + BREAK_IF(!expList || + (last->appendix && + last->appendix->item.is())); + auto expListLow = x->new_ptr(); + expListLow->exprs.dup(expList->exprs); + auto returnNode = x->new_ptr(); + returnNode->valueList.set(expListLow); + last->content.set(returnNode); + BLOCK_END + } + str_list temp; + for (auto node : nodes) { + transformStatement(static_cast(node), temp); + } + out.push_back(join(temp)); + } + + void transformBody(Body_t* body, str_list& out, bool implicitReturn = false) { + if (auto stmt = body->content.as()) { + transformCodes(node_container{stmt}, out, implicitReturn); + } else { + transformCodes(body->content.to()->statements.objects(), out, implicitReturn); + } + } + + void transformBlock(Block_t* block, str_list& out, bool implicitReturn = true) { + transformCodes(block->statements.objects(), out, implicitReturn); + } + + void transformReturn(Return_t* returnNode, str_list& out) { + if (auto valueList = returnNode->valueList.get()) { + if (auto singleValue = singleValueFrom(valueList)) { + if (auto simpleValue = singleValue->item.as()) { + auto value = simpleValue->value.get(); + switch (value->getId()) { + case "Comprehension"_id: + transformCompReturn(static_cast(value), out); + return; + case "TblComprehension"_id: + transformTblCompReturn(static_cast(value), out); + return; + case "With"_id: + transformWith(static_cast(value), out, nullptr, true); + return; + case "ClassDecl"_id: + transformClassDecl(static_cast(value), out, ExpUsage::Return); + return; + case "Do"_id: + transformDo(static_cast(value), out, true); + return; + case "Switch"_id: + transformSwitch(static_cast(value), out, true); + return; + case "While"_id: + transformWhileInPlace(static_cast(value), out); + return; + case "For"_id: + transformForInPlace(static_cast(value), out); + return; + case "ForEach"_id: + transformForEachInPlace(static_cast(value), out); + return; + case "If"_id: + transformIf(static_cast(value), out, IfUsage::Return); + return; + case "Unless"_id: + transformUnless(static_cast(value), out, IfUsage::Return); + return; + } + } + if (auto chainValue = singleValue->item.as()) { + if (isColonChain(chainValue)) { + transformColonChain(chainValue, out, ExpUsage::Return); + } else { + transformChainValue(chainValue, out, ExpUsage::Return); + } + return; + } + transformValue(singleValue, out); + out.back() = indent() + s("return "sv) + out.back() + nlr(returnNode); + return; + } else { + str_list temp; + transformExpListLow(valueList, temp); + out.push_back(indent() + s("return "sv) + temp.back() + nlr(returnNode)); + } + } else { + out.push_back(indent() + s("return"sv) + nll(returnNode)); + } + } + + void transformFnArgsDef(FnArgsDef_t* argsDef, str_list& out) { + if (!argsDef->defList) { + out.push_back(Empty); + out.push_back(Empty); + } else { + transformFnArgDefList(argsDef->defList, out); + } + if (argsDef->shadowOption) { + transform_outer_var_shadow(argsDef->shadowOption); + } + } + + void transform_outer_var_shadow(outer_var_shadow_t* shadow) { + markVarShadowed(); + if (shadow->varList) { + for (auto name : shadow->varList->names.objects()) { + addToAllowList(toString(name)); + } + } + } + + void transformFnArgDefList(FnArgDefList_t* argDefList, str_list& out) { + auto x = argDefList; + struct ArgItem { + std::string name; + std::string assignSelf; + }; + std::list argItems; + str_list temp; + std::string varNames; + bool assignSelf = false; + for (auto _def : argDefList->definitions.objects()) { + auto def = static_cast(_def); + auto& arg = argItems.emplace_back(); + switch (def->name->getId()) { + case "Variable"_id: arg.name = toString(def->name); break; + case "SelfName"_id: { + assignSelf = true; + auto selfName = static_cast(def->name.get()); + switch (selfName->name->getId()) { + case "self_class_name"_id: { + auto clsName = static_cast(selfName->name.get()); + arg.name = toString(clsName->name); + arg.assignSelf = s("self.__class."sv) + arg.name; + break; + } + case "self_class"_id: + arg.name = "self.__class"sv; + break; + case "self_name"_id: { + + auto sfName = static_cast(selfName->name.get()); + arg.name = toString(sfName->name); + arg.assignSelf = s("self."sv) + arg.name; + break; + } + case "self"_id: + arg.name = "self"sv; + break; + default: break; + } + break; + } + } + forceAddToScope(arg.name); + if (def->defaultValue) { + pushScope(); + auto expList = toAst(arg.name, ExpList, x); + auto assign = x->new_ptr(); + assign->values.push_back(def->defaultValue.get()); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + _buf << indent() << "if "sv << arg.name << " == nil then"sv << nll(def); + _buf << temp.back(); + _buf << indent() << "end"sv << nll(def); + temp.back() = clearBuf(); + } + if (varNames.empty()) varNames = arg.name; + else varNames.append(s(", "sv) + arg.name); + } + if (argDefList->varArg) { + auto& arg = argItems.emplace_back(); + arg.name = "..."sv; + if (varNames.empty()) varNames = arg.name; + else varNames.append(s(", "sv) + arg.name); + } + std::string initCodes = join(temp); + if (assignSelf) { + auto sjoin = [](const decltype(argItems)& items, int index) { + std::string result; + for (auto it = items.begin(); it != items.end(); ++it) { + if (it->assignSelf.empty()) continue; + if (result.empty()) result = (&it->name)[index]; + else result.append(s(", "sv) + (&it->name)[index]); + } + return result; + }; + std::string sleft = sjoin(argItems, 1); + std::string sright = sjoin(argItems, 0); + initCodes.append(indent() + sleft + s(" = "sv) + sright + nll(argDefList)); + } + out.push_back(varNames); + out.push_back(initCodes); + } + + void transformSelfName(SelfName_t* selfName, str_list& out, bool invoke) { + auto name = selfName->name.get(); + switch (name->getId()) { + case "self_class_name"_id: { + auto clsName = static_cast(name); + out.push_back(s("self.__class"sv) + s(invoke ? ":"sv : "."sv) + toString(clsName->name)); + break; + } + case "self_class"_id: + out.push_back(s("self.__class"sv)); + break; + case "self_name"_id: { + auto sfName = static_cast(name); + out.push_back(s("self"sv) + s(invoke ? ":"sv : "."sv) + toString(sfName->name)); + break; + } + case "self"_id: + out.push_back(s("self"sv)); + break; + } + } + + void transformColonChainClosure(ChainValue_t* chainValue, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(chainValue)); + pushScope(); + transformColonChain(chainValue, temp, ExpUsage::Return); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformColonChain(ChainValue_t* chainValue, str_list& out, ExpUsage usage = ExpUsage::Common, ExpList_t* expList = nullptr) { + auto x = chainValue; + const auto& chainList = chainValue->items.objects(); + auto baseChain = x->new_ptr(); + switch (chainList.front()->getId()) { + case "DotChainItem"_id: + case "ColonChainItem"_id: + if (_withVars.empty()) { + throw std::logic_error(debugInfo("Short dot/colon syntax must be called within a with block."sv, chainList.front())); + } else { + baseChain->items.push_back(toAst(_withVars.top(), Callable, x)); + } + break; + } + auto end = --chainList.end(); + for (auto it = chainList.begin(); it != end; ++it) { + baseChain->items.push_back(*it); + } + auto colonChainItem = static_cast(chainList.back()); + auto funcName = toString(colonChainItem->name); + if (usage != ExpUsage::Return) pushScope(); + auto baseVar = getUnusedName("_base_"sv); + auto fnVar = getUnusedName("_fn_"sv); + str_list temp; + { + auto value = x->new_ptr(); + value->item.set(baseChain); + auto exp = x->new_ptr(); + exp->value.set(value); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(baseVar, ExpList, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + { + auto assign = x->new_ptr(); + assign->values.push_back(toAst(baseVar + "." + funcName, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(fnVar, ExpList, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + auto funLit = toAst(s("(...)-> "sv) + fnVar + s(" "sv) + baseVar + s(", ..."sv), Exp, x); + switch (usage) { + case ExpUsage::Return: + transformExp(funLit, temp); + _buf << temp.front(); + _buf << *(++temp.begin()); + _buf << indent() << "return " << temp.back() << nll(chainValue); + break; + case ExpUsage::Assignment: { + auto assign = x->new_ptr(); + assign->values.push_back(funLit); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + _buf << indent(-1) << "do"sv << nll(chainValue); + _buf << temp.front(); + _buf << *(++temp.begin()); + _buf << temp.back(); + popScope(); + _buf << indent() << "end"sv << nll(chainValue); + break; + } + case ExpUsage::Common: { + auto assign = x->new_ptr(); + assign->values.push_back(funLit); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst("_"sv, ExpList, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + _buf << indent(-1) << "do"sv << nll(chainValue); + _buf << temp.front(); + _buf << *(++temp.begin()); + _buf << temp.back(); + popScope(); + _buf << indent() << "end"sv << nll(chainValue); + break; + } + default: break; + } + out.push_back(clearBuf()); + } + + void transformChainList(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + auto x = chainList.front(); + str_list temp; + switch (chainList.front()->getId()) { + case "DotChainItem"_id: + case "ColonChainItem"_id: + if (_withVars.empty()) { + throw std::logic_error(debugInfo("Short dot/colon syntax must be called within a with block."sv, chainList.front())); + } else { + temp.push_back(_withVars.top()); + } + break; + } + for (auto it = chainList.begin(); it != chainList.end(); ++it) { + auto item = *it; + switch (item->getId()) { + case "Invoke"_id: + transformInvoke(static_cast(item), temp); + break; + case "DotChainItem"_id: + transformDotChainItem(static_cast(item), temp); + break; + case "ColonChainItem"_id: { + auto colonItem = static_cast(item); + auto next = it; ++next; + auto followItem = next != chainList.end() ? *next : nullptr; + if (!ast_is(followItem)) { + throw std::logic_error(debugInfo("Colon chain item must be followed by invoke arguments."sv, colonItem)); + } + if (colonItem->name.is()) { + auto callVar = getUnusedName(s("_call_"sv)); + auto block = x->new_ptr(); + { + auto chainValue = x->new_ptr(); + switch (chainList.front()->getId()) { + case "DotChainItem"_id: + case "ColonChainItem"_id: + chainValue->items.push_back(toAst(_withVars.top(), Callable, x)); + break; + } + for (auto i = chainList.begin(); i != it; ++i) { + chainValue->items.push_back(*i); + } + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(callVar, ExpList, x)); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + auto stmt = x->new_ptr(); + stmt->content.set(assignment); + block->statements.push_back(stmt); + } + { + auto name = toString(colonItem->name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(toAst(callVar, Callable, x)); + chainValue->items.push_back(toAst(s("\""sv) + name + s("\""sv), Exp, x)); + if (auto invoke = ast_cast(followItem)) { + invoke->args.push_front(toAst(callVar, Exp, x)); + } else { + auto invokeArgs = static_cast(followItem); + invokeArgs->args.push_front(toAst(callVar, Exp, x)); + } + chainValue->items.push_back(followItem); + for (auto i = ++next; i != chainList.end(); ++i) { + chainValue->items.push_back(*i); + } + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto stmt = x->new_ptr(); + stmt->content.set(expListAssign); + block->statements.push_back(stmt); + } + switch (usage) { + case ExpUsage::Common: + transformBlock(block, out, false); + return; + case ExpUsage::Return: + transformBlock(block, out, true); + return; + case ExpUsage::Assignment: { + auto body = x->new_ptr(); + body->content.set(block); + assignLastExplist(assignList, body); + transformBlock(block, out); + return; + } + default: + break; + } + auto body = x->new_ptr(); + body->content.set(block); + auto funLit = toAst("->"sv, FunLit, x); + funLit->body.set(body); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(funLit); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto paren = x->new_ptr(); + paren->expr.set(exp); + auto callable = x->new_ptr(); + callable->item.set(paren); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto invoke = x->new_ptr(); + chainValue->items.push_back(invoke); + transformChainValue(chainValue, out); + return; + } + transformColonChainItem(colonItem, temp); + break; + } + case "Slice"_id: + transformSlice(static_cast(item), temp); + break; + case "Callable"_id: { + auto next = it; ++next; + auto followItem = next != chainList.end() ? *next : nullptr; + transformCallable(static_cast(item), temp, + followItem && ast_is(followItem)); + break; + } + case "String"_id: + transformString(static_cast(item), temp); + temp.back() = s("("sv) + temp.back() + s(")"sv); + break; + case "Exp"_id: + transformExp(static_cast(item), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + case "InvokeArgs"_id: transformInvokeArgs(static_cast(item), temp); break; + default: break; + } + } + switch (usage) { + case ExpUsage::Common: + out.push_back(indent() + join(temp) + nll(chainList.front())); + break; + case ExpUsage::Return: + out.push_back(indent() + s("return "sv) + join(temp) + nll(chainList.front())); + break; + default: + out.push_back(join(temp)); + break; + } + } + + void transformChainValue(ChainValue_t* chainValue, str_list& out, ExpUsage usage = ExpUsage::Closure, ExpList_t* assignList = nullptr) { + transformChainList(chainValue->items.objects(), out, usage, assignList); + } + + void transformAssignableChain(AssignableChain_t* chain, str_list& out) { + transformChainList(chain->items.objects(), out, ExpUsage::Closure); + } + + void transformDotChainItem(DotChainItem_t* dotChainItem, str_list& out) { + auto name = toString(dotChainItem->name); + if (State::keywords.find(name) != State::keywords.end()) { + out.push_back(s("[\""sv) + name + s("\"]"sv)); + } else { + out.push_back(s("."sv) + name); + } + } + + void transformColonChainItem(ColonChainItem_t* colonChainItem, str_list& out) { + auto name = toString(colonChainItem->name); + out.push_back(s(colonChainItem->switchToDot ? "."sv : ":"sv) + name); + } + + void transformSlice(Slice_t* slice, str_list& out) { + throw std::logic_error(debugInfo("Slice syntax not supported here."sv, slice)); + } + + void transformInvoke(Invoke_t* invoke, str_list& out) { + str_list temp; + for (auto arg : invoke->args.objects()) { + switch (arg->getId()) { + case "Exp"_id: transformExp(static_cast(arg), temp); break; + case "SingleString"_id: transformSingleString(static_cast(arg), temp); break; + case "DoubleString"_id: transformDoubleString(static_cast(arg), temp); break; + case "LuaString"_id: transformLuaString(static_cast(arg), temp); break; + default: break; + } + } + out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); + } + + void transform_unary_exp(unary_exp_t* unary_exp, str_list& out) { + std::string op = toString(unary_exp->m_begin.m_it, unary_exp->item->m_begin.m_it); + str_list temp{op + (op == "not"sv ? s(" "sv) : Empty)}; + transformExp(unary_exp->item, temp); + out.push_back(join(temp)); + } + + void transformVariable(Variable_t* name, str_list& out) { + out.push_back(toString(name)); + } + + void transformNum(Num_t* num, str_list& out) { + out.push_back(toString(num)); + } + + void transformTableLit(TableLit_t* table, str_list& out) { + transformTable(table, table->values.objects(), out); + } + + void transformCompCommon(Comprehension_t* comp, str_list& out) { + str_list temp; + auto x = comp; + auto compInner = comp->forLoop.get(); + for (auto item : compInner->items.objects()) { + switch (item->getId()) { + case "CompForEach"_id: + transformCompForEach(static_cast(item), temp); + break; + case "CompFor"_id: + transformCompFor(static_cast(item), temp); + break; + case "Exp"_id: + transformExp(static_cast(item), temp); + temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); + pushScope(); + break; + } + } + if (auto stmt = comp->value.as()) { + transformStatement(stmt, temp); + } else if (auto exp = comp->value.as()) { + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto statement = x->new_ptr(); + statement->content.set(expListAssign); + transformStatement(statement, temp); + } + auto value = temp.back(); + temp.pop_back(); + _buf << join(temp) << value; + for (size_t i = 0; i < compInner->items.objects().size(); ++i) { + popScope(); + _buf << indent() << "end"sv << nll(comp); + } + out.push_back(clearBuf()); + } + + void transformComprehension(Comprehension_t* comp, str_list& out) { + str_list temp; + std::string accum = getUnusedName("_accum_"); + std::string len = getUnusedName("_len_"); + addToScope(accum); + addToScope(len); + auto compInner = comp->forLoop.get(); + for (auto item : compInner->items.objects()) { + switch (item->getId()) { + case "CompForEach"_id: + transformCompForEach(static_cast(item), temp); + break; + case "CompFor"_id: + transformCompFor(static_cast(item), temp); + break; + case "Exp"_id: + transformExp(static_cast(item), temp); + temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); + pushScope(); + break; + } + } + transformExp(comp->value.to(), temp); + auto value = temp.back(); + temp.pop_back(); + for (size_t i = 0; i < compInner->items.objects().size(); ++i) { + popScope(); + } + _buf << indent() << "local "sv << accum << " = { }"sv << nll(comp); + _buf << indent() << "local "sv << len << " = 1"sv << nll(comp); + _buf << join(temp); + _buf << indent(int(temp.size())) << accum << "["sv << len << "] = "sv << value << nll(comp); + _buf << indent(int(temp.size())) << len << " = "sv << len << " + 1"sv << nll(comp); + for (int ind = int(temp.size()) - 1; ind > -1 ; --ind) { + _buf << indent(ind) << "end"sv << nll(comp); + } + out.push_back(accum); + out.push_back(clearBuf()); + } + + void transformCompInPlace(Comprehension_t* comp, ExpList_t* expList, str_list& out) { + auto x = comp; + str_list temp; + auto ind = indent(); + pushScope(); + transformComprehension(comp, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(temp.front(), Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + out.push_back( + ind + s("do"sv) + nll(comp) + + *(++temp.begin()) + + temp.back()); + popScope(); + out.back() = out.back() + indent() + s("end"sv) + nlr(comp); + } + + void transformCompReturn(Comprehension_t* comp, str_list& out) { + str_list temp; + transformComprehension(comp, temp); + out.push_back(temp.back() + indent() + s("return "sv) + temp.front() + nlr(comp)); + } + + void transformCompClosure(Comprehension_t* comp, str_list& out) { + str_list temp; + std::string before = s("(function()"sv) + nll(comp); + pushScope(); + transformComprehension(comp, temp); + out.push_back( + before + + temp.back() + + indent() + s("return "sv) + temp.front() + nlr(comp)); + popScope(); + out.back() = out.back() + indent() + s("end)()"sv); + } + + void transformForEachHead(AssignableNameList_t* nameList, ast_node* loopTarget, str_list& out) { + auto x = nameList; + str_list temp; + str_list vars; + str_list varBefore, varAfter; + std::list>> destructPairs; + for (auto _item : nameList->items.objects()) { + auto item = static_cast(_item)->item.get(); + switch (item->getId()) { + case "Variable"_id: + transformVariable(static_cast(item), vars); + varAfter.push_back(vars.back()); + break; + case "TableLit"_id: { + auto desVar = getUnusedName("_des_"sv); + destructPairs.emplace_back(item, toAst(desVar, Exp, x)); + vars.push_back(desVar); + varAfter.push_back(desVar); + break; + } + default: break; + } + } + switch (loopTarget->getId()) { + case "star_exp"_id: { + auto star_exp = static_cast(loopTarget); + auto listVar = singleVariableFrom(star_exp->value); + auto indexVar = getUnusedName("_index_"); + varAfter.push_back(indexVar); + auto value = singleValueFrom(star_exp->value); + if (!value) throw std::logic_error(debugInfo("Invalid star syntax."sv, star_exp)); + bool endWithSlice = false; + BLOCK_START + auto chainValue = value->item.as(); + BREAK_IF(!chainValue); + auto chainList = chainValue->items.objects(); + auto slice = ast_cast(chainList.back()); + BREAK_IF(!slice); + endWithSlice = true; + if (listVar.empty() && chainList.size() == 2 && + ast_is(chainList.front())) { + transformCallable(static_cast(chainList.front()), temp, false); + listVar = temp.back(); + temp.pop_back(); + } + chainList.pop_back(); + auto chain = x->new_ptr(); + for (auto item : chainList) { + chain->items.push_back(item); + } + std::string startValue("1"sv); + if (auto exp = slice->startValue.as()) { + transformExp(exp, temp); + startValue = temp.back(); + temp.pop_back(); + } + std::string stopValue; + if (auto exp = slice->stopValue.as()) { + transformExp(exp, temp); + stopValue = temp.back(); + temp.pop_back(); + } + std::string stepValue; + if (auto exp = slice->stepValue.as()) { + transformExp(exp, temp); + stepValue = temp.back(); + temp.pop_back(); + } + if (listVar.empty()) { + listVar = getUnusedName("_list_"); + varBefore.push_back(listVar); + transformChainValue(chain, temp); + _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); + } + std::string maxVar; + if (!stopValue.empty()) { + maxVar = getUnusedName("_max_"); + varBefore.push_back(maxVar); + _buf << indent() << "local "sv << maxVar << " = "sv << stopValue << nll(nameList); + } + _buf << indent() << "for "sv << indexVar << " = "sv; + _buf << startValue << ", "sv; + if (stopValue.empty()) { + _buf << "#"sv << listVar; + } else { + _buf << maxVar << " < 0 and #"sv << listVar <<" + " << maxVar << " or "sv << maxVar; + } + if (!stepValue.empty()) { + _buf << ", "sv << stepValue; + } + _buf << " do"sv << nlr(loopTarget); + _buf << indent(1) << "local "sv << join(vars, ", "sv) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); + out.push_back(clearBuf()); + BLOCK_END + bool newListVal = false; + if (listVar.empty()) { + newListVal = true; + listVar = getUnusedName("_list_"); + varBefore.push_back(listVar); + } + if (!endWithSlice) { + transformExp(star_exp->value, temp); + if (newListVal) _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); + _buf << indent() << "for "sv << indexVar << " = 1, #"sv << listVar << " do"sv << nlr(loopTarget); + _buf << indent(1) << "local "sv << join(vars) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); + out.push_back(clearBuf()); + } + break; + } + case "Exp"_id: + transformExp(static_cast(loopTarget), temp); + _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); + out.push_back(clearBuf()); + break; + case "ExpList"_id: + transformExpList(static_cast(loopTarget), temp); + _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); + out.push_back(clearBuf()); + break; + default: break; + } + for (auto& var : varBefore) addToScope(var); + pushScope(); + for (auto& var : varAfter) addToScope(var); + if (!destructPairs.empty()) { + temp.clear(); + for (auto& pair : destructPairs) { + auto sValue = x->new_ptr(); + sValue->value.set(pair.first); + auto value = x->new_ptr(); + value->item.set(sValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assign = x->new_ptr(); + assign->values.push_back(pair.second); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + out.back().append(join(temp)); + } + } + + void transformCompForEach(CompForEach_t* comp, str_list& out) { + transformForEachHead(comp->nameList, comp->loopValue, out); + } + + void transformInvokeArgs(InvokeArgs_t* invokeArgs, str_list& out) { + str_list temp; + for (auto arg : invokeArgs->args.objects()) { + switch (arg->getId()) { + case "Exp"_id: transformExp(static_cast(arg), temp); break; + case "TableBlock"_id: transformTableBlock(static_cast(arg), temp); break; + default: break; + } + } + out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); + } + + void transformForHead(For_t* forNode, str_list& out) { + str_list temp; + std::string varName = toString(forNode->varName); + transformExp(forNode->startValue, temp); + transformExp(forNode->stopValue, temp); + if (forNode->stepValue) { + transformExp(forNode->stepValue->value, temp); + } else { + temp.emplace_back(); + } + auto it = temp.begin(); + const auto& start = *it; + const auto& stop = *(++it); + const auto& step = *(++it); + _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(forNode); + pushScope(); + addToScope(varName); + out.push_back(clearBuf()); + } + + void transformLoopBody(Body_t* body, str_list& out, const std::string& appendContent) { + str_list temp; + bool withContinue = traversal::Stop == body->traverse([&](ast_node* node) { + switch (node->getId()) { + case "For"_id: + case "ForEach"_id: + return traversal::Return; + case "BreakLoop"_id: { + return toString(node) == "continue"sv ? + traversal::Stop : traversal::Return; + } + default: + return traversal::Continue; + } + }); + if (withContinue) { + auto continueVar = getUnusedName("_continue_"sv); + addToScope(continueVar); + _buf << indent() << "local "sv << continueVar << " = false"sv << nll(body); + _buf << indent() << "repeat"sv << nll(body); + temp.push_back(clearBuf()); + _continueVars.push(continueVar); + pushScope(); + } + transformBody(body, temp); + if (withContinue) { + if (!appendContent.empty()) { + _buf << indent() << appendContent; + } + _buf << indent() << _continueVars.top() << " = true"sv << nll(body); + popScope(); + _buf << indent() << "until true"sv << nlr(body); + _buf << indent() << "if not "sv << _continueVars.top() << " then"sv << nlr(body); + _buf << indent(1) << "break"sv << nlr(body); + _buf << indent() << "end"sv << nlr(body); + temp.push_back(clearBuf()); + _continueVars.pop(); + } else if (!appendContent.empty()) { + temp.back().append(indent() + appendContent); + } + out.push_back(join(temp)); + } + + void transformFor(For_t* forNode, str_list& out) { + str_list temp; + transformForHead(forNode, temp); + transformLoopBody(forNode->body, temp, Empty); + popScope(); + out.push_back(join(temp) + indent() + s("end"sv) + nlr(forNode)); + } + + std::string transformForInner(For_t* forNode, str_list& out) { + auto x = forNode; + std::string accum = getUnusedName("_accum_"); + addToScope(accum); + std::string len = getUnusedName("_len_"); + addToScope(len); + _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); + _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); + out.push_back(clearBuf()); + transformForHead(forNode, out); + auto expList = toAst(accum + s("["sv) + len + s("]"sv), ExpList, x); + assignLastExplist(expList, forNode->body); + auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forNode->body); + transformLoopBody(forNode->body, out, lenLine); + popScope(); + out.push_back(indent() + s("end"sv) + nlr(forNode)); + return accum; + } + + void transformForClosure(For_t* forNode, str_list& out) { + str_list temp; + _buf << "(function()"sv << nll(forNode); + pushScope(); + auto accum = transformForInner(forNode, temp); + temp.push_back(indent() + s("return "sv) + accum + nlr(forNode)); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformForInPlace(For_t* forNode, str_list& out, ExpList_t* assignExpList = nullptr) { + auto x = forNode; + str_list temp; + if (assignExpList) { + _buf << indent() << "do"sv << nll(forNode); + pushScope(); + auto accum = transformForInner(forNode, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accum, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(assignExpList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(forNode)); + } else { + auto accum = transformForInner(forNode, temp); + auto returnNode = x->new_ptr(); + auto expListLow = toAst(accum, ExpListLow, x); + returnNode->valueList.set(expListLow); + transformReturn(returnNode, temp); + } + out.push_back(join(temp)); + } + + void transformBinaryOperator(BinaryOperator_t* node, str_list& out) { + auto op = toString(node); + out.push_back(op == "!="sv ? s("~="sv) : op); + } + + void transformForEach(ForEach_t* forEach, str_list& out) { + str_list temp; + transformForEachHead(forEach->nameList, forEach->loopValue, temp); + transformLoopBody(forEach->body, temp, Empty); + popScope(); + out.push_back(temp.front() + temp.back() + indent() + s("end"sv) + nlr(forEach)); + } + + std::string transformForEachInner(ForEach_t* forEach, str_list& out) { + auto x = forEach; + std::string accum = getUnusedName("_accum_"); + addToScope(accum); + std::string len = getUnusedName("_len_"); + addToScope(len); + _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); + _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); + out.push_back(clearBuf()); + transformForEachHead(forEach->nameList, forEach->loopValue, out); + auto expList = toAst(accum + s("["sv) + len + s("]"sv), ExpList, x); + assignLastExplist(expList, forEach->body); + auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forEach->body); + transformLoopBody(forEach->body, out, lenLine); + popScope(); + out.push_back(indent() + s("end"sv) + nlr(forEach)); + return accum; + } + + void transformForEachClosure(ForEach_t* forEach, str_list& out) { + str_list temp; + _buf << "(function()"sv << nll(forEach); + pushScope(); + auto accum = transformForEachInner(forEach, temp); + temp.push_back(indent() + s("return "sv) + accum + nlr(forEach)); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformForEachInPlace(ForEach_t* forEach, str_list& out, ExpList_t* assignExpList = nullptr) { + auto x = forEach; + str_list temp; + if (assignExpList) { + _buf << indent() << "do"sv << nll(forEach); + pushScope(); + auto accum = transformForEachInner(forEach, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accum, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(assignExpList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(forEach)); + } else { + auto accum = transformForEachInner(forEach, temp); + auto returnNode = x->new_ptr(); + auto expListLow = toAst(accum, ExpListLow, x); + returnNode->valueList.set(expListLow); + transformReturn(returnNode, temp); + } + out.push_back(join(temp)); + } + + void transform_variable_pair(variable_pair_t* pair, str_list& out) { + auto name = toString(pair->name); + out.push_back(name + s(" = "sv) + name); + } + + void transform_normal_pair(normal_pair_t* pair, str_list& out) { + auto key = pair->key.get(); + str_list temp; + switch (key->getId()) { + case "KeyName"_id: { + transformKeyName(static_cast(key), temp); + if (State::luaKeywords.find(temp.back()) != State::luaKeywords.end()) { + temp.back() = s("[\""sv) + temp.back() + s("\"]"); + } + break; + } + case "Exp"_id: + transformExp(static_cast(key), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + case "DoubleString"_id: + transformDoubleString(static_cast(key), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + case "SingleString"_id: transformSingleString(static_cast(key), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + default: break; + } + auto value = pair->value.get(); + switch (value->getId()) { + case "Exp"_id: transformExp(static_cast(value), temp); break; + case "TableBlock"_id: transformTableBlock(static_cast(value), temp); break; + default: break; + } + out.push_back(temp.front() + s(" = "sv) + temp.back()); + } + + void transformKeyName(KeyName_t* keyName, str_list& out) { + auto name = keyName->name.get(); + switch (name->getId()) { + case "SelfName"_id: transformSelfName(static_cast(name), out, false); break; + case "Name"_id: out.push_back(toString(name)); break; + default: break; + } + } + + void replace(std::string& str, std::string_view from, std::string_view to) { + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.size(), to); + start_pos += to.size(); + } + } + + void transformLuaString(LuaString_t* luaString, str_list& out) { + auto content = toString(luaString->content); + replace(content, "\r"sv, ""); + if (content[0] == '\n') content.erase(content.begin()); + out.push_back(toString(luaString->open) + content + toString(luaString->close)); + } + + void transformSingleString(SingleString_t* singleString, str_list& out) { + auto str = toString(singleString); + replace(str, "\r"sv, ""); + replace(str, "\n"sv, "\\n"sv); + out.push_back(str); + } + + void transformDoubleString(DoubleString_t* doubleString, str_list& out) { + str_list temp; + for (auto _seg : doubleString->segments.objects()) { + auto seg = static_cast(_seg); + auto content = seg->content.get(); + switch (content->getId()) { + case "double_string_inner"_id: { + auto str = toString(content); + replace(str, "\r"sv, ""); + replace(str, "\n"sv, "\\n"sv); + temp.push_back(s("\""sv) + str + s("\""sv)); + break; + } + case "Exp"_id: + transformExp(static_cast(content), temp); + temp.back() = s("tostring("sv) + temp.back() + s(")"sv); + break; + default: break; + } + } + out.push_back(temp.empty() ? s("\"\""sv) : join(temp, " .. "sv)); + } + + void transformString(String_t* string, str_list& out) { + auto str = string->str.get(); + switch (str->getId()) { + case "SingleString"_id: transformSingleString(static_cast(str), out); break; + case "DoubleString"_id: transformDoubleString(static_cast(str), out); break; + case "LuaString"_id: transformLuaString(static_cast(str), out); break; + default: break; + } + } + + std::pair defineClassVariable(Assignable_t* assignable) { + if (auto variable = assignable->item.as()) { + auto name = toString(variable); + if (addToScope(name)) { + return {name, true}; + } else { + return {name, false}; + } + } + return {Empty, false}; + } + + void transformClassDeclClosure(ClassDecl_t* classDecl, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(classDecl)); + pushScope(); + transformClassDecl(classDecl, temp, ExpUsage::Return); + popScope(); + temp.push_back(s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformClassDecl(ClassDecl_t* classDecl, str_list& out, ExpUsage usage = ExpUsage::Common, ExpList_t* expList = nullptr) { + str_list temp; + auto x = classDecl; + auto body = classDecl->body.get(); + auto assignable = classDecl->name.get(); + auto extend = classDecl->extend.get(); + std::string className; + std::string assignItem; + if (assignable) { + if (!isAssignable(assignable)) { + throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, assignable)); + } + bool newDefined = false; + std::tie(className, newDefined) = defineClassVariable(assignable); + if (newDefined) { + temp.push_back(indent() + s("local "sv) + className + nll(classDecl)); + } + if (className.empty()) { + if (auto chain = ast_cast(assignable->item)) { + if (auto dotChain = ast_cast(chain->items.back())) { + className = s("\""sv) + toString(dotChain->name) + s("\""sv); + } else if (auto index = ast_cast(chain->items.back())) { + if (auto name = index->getByPath()) { + transformString(name, temp); + className = temp.back(); + temp.pop_back(); + } + } + } + } else { + className = s("\""sv) + className + s("\""sv); + } + pushScope(); + transformAssignable(assignable, temp); + popScope(); + assignItem = temp.back(); + temp.pop_back(); + } else if (expList) { + auto name = singleVariableFrom(expList); + if (!name.empty()) { + className = s("\""sv) + name + s("\""sv); + } + } + temp.push_back(indent() + s("do"sv) + nll(classDecl)); + pushScope(); + auto classVar = getUnusedName("_class_"sv); + addToScope(classVar); + temp.push_back(indent() + s("local "sv) + classVar + nll(classDecl)); + if (body) { + str_list varDefs; + for (auto item : body->contents.objects()) { + if (auto statement = ast_cast(item)) { + ClassDecl_t* clsDecl = nullptr; + if (auto assignment = assignmentFrom(statement)) { + auto names = transformAssignDefs(assignment->expList.get()); + varDefs.insert(varDefs.end(), names.begin(), names.end()); + auto info = extractDestructureInfo(assignment); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable && addToScope(item.name)) + varDefs.push_back(item.name); + } + BLOCK_START + auto assign = assignment->action.as(); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto exp = ast_cast(assign->values.objects().front()); + BREAK_IF(!exp); + auto value = singleValueFrom(exp); + clsDecl = value->getByPath(); + BLOCK_END + } else if (auto expList = expListFrom(statement)) { + auto value = singleValueFrom(expList); + clsDecl = value->getByPath(); + } + if (clsDecl) { + std::string clsName; + bool newDefined = false; + std::tie(clsName,newDefined) = defineClassVariable(clsDecl->name); + if (newDefined) varDefs.push_back(clsName); + } + } + } + if (!varDefs.empty()) { + temp.push_back(indent() + s("local ") + join(varDefs, ", "sv) + nll(body)); + } + } + std::string parent, parentVar; + if (extend) { + parentVar = getUnusedName("_parent_"sv); + addToScope(parentVar); + transformExp(extend, temp); + parent = temp.back(); + temp.pop_back(); + temp.push_back(indent() + s("local "sv) + parentVar + s(" = "sv) + parent + nll(classDecl)); + } + auto baseVar = getUnusedName("_base_"sv); + addToScope(baseVar); + temp.push_back(indent() + s("local "sv) + baseVar + s(" = "sv)); + str_list builtins; + str_list commons; + str_list statements; + if (body) { + std::list members; + for (auto content : classDecl->body->contents.objects()) { + switch (content->getId()) { + case "class_member_list"_id: { + size_t inc = transform_class_member_list(static_cast(content), members, classVar); + auto it = members.end(); + for (size_t i = 0; i < inc; ++i, --it); + for (; it != members.end(); ++it) { + auto& member = *it; + if (member.type == MemType::Property) { + statements.push_back(indent() + member.item + nll(content)); + } else { + member.item = indent(1) + member.item; + } + } + break; + } + case "Statement"_id: + transformStatement(static_cast(content), statements); + break; + default: break; + } + } + for (auto& member : members) { + switch (member.type) { + case MemType::Common: + commons.push_back((commons.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); + break; + case MemType::Builtin: + builtins.push_back((builtins.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); + break; + default: break; + } + } + if (!commons.empty()) { + temp.back() += s("{"sv) + nll(body); + temp.push_back(join(commons) + nll(body)); + temp.push_back(indent() + s("}"sv) + nll(body)); + } else { + temp.back() += s("{ }"sv) + nll(body); + } + } else { + temp.back() += s("{ }"sv) + nll(classDecl); + } + temp.push_back(indent() + baseVar + s(".__index = "sv) + baseVar + nll(classDecl)); + str_list tmp; + if (usage == ExpUsage::Assignment) { + auto assign = x->new_ptr(); + assign->values.push_back(toAst(classVar, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, tmp); + } + if (extend) { + _buf << indent() << "setmetatable("sv << baseVar << ", "sv << parentVar << ".__base)"sv << nll(classDecl); + } + _buf << indent() << classVar << " = setmetatable({" << nll(classDecl); + if (!builtins.empty()) { + _buf << join(builtins) << ","sv << nll(classDecl); + } else { + if (extend) { + _buf << indent(1) << "__init = function(self, ...)"sv << nll(classDecl); + _buf << indent(2) << "return _class_0.__parent.__init(self, ...)"sv << nll(classDecl); + _buf << indent(1) << "end,"sv << nll(classDecl); + } else { + _buf << indent(1) << "__init = function() end,"sv << nll(classDecl); + } + } + _buf << indent(1) << "__base = "sv << baseVar; + if (!className.empty()) { + _buf << ","sv << nll(classDecl) << indent(1) << "__name = "sv << className << (extend ? s(","sv) : Empty) << nll(classDecl); + } else { + _buf << nll(classDecl); + } + if (extend) { + _buf << indent(1) << "__parent = "sv << parentVar << nll(classDecl); + } + _buf << indent() << "}, {"sv << nll(classDecl); + if (extend) { + _buf << indent(1) << "__index = function(cls, name)"sv << nll(classDecl); + _buf << indent(2) << "local val = rawget("sv << baseVar << ", name)"sv << nll(classDecl); + _buf << indent(2) << "if val == nil then"sv << nll(classDecl); + _buf << indent(3) << "local parent = rawget(cls, \"__parent\")"sv << nll(classDecl); + _buf << indent(3) << "if parent then"sv << nll(classDecl); + _buf << indent(4) << "return parent[name]"sv << nll(classDecl); + _buf << indent(3) << "end"sv << nll(classDecl); + _buf << indent(2) << "else"sv << nll(classDecl); + _buf << indent(3) << "return val"sv << nll(classDecl); + _buf << indent(2) << "end"sv << nll(classDecl); + _buf << indent(1) << "end,"sv << nll(classDecl); + } else { + _buf << indent(1) << "__index = "sv << baseVar << ","sv << nll(classDecl); + } + _buf << indent(1) << "__call = function(cls, ...)"sv << nll(classDecl); + pushScope(); + auto selfVar = getUnusedName("_self_"sv); + addToScope(selfVar); + _buf << indent(1) << "local " << selfVar << " = setmetatable({}, "sv << baseVar << ")"sv << nll(classDecl); + _buf << indent(1) << "cls.__init("sv << selfVar << ", ...)"sv << nll(classDecl); + _buf << indent(1) << "return "sv << selfVar << nll(classDecl); + popScope(); + _buf << indent(1) << "end"sv << nll(classDecl); + _buf << indent() << "})"sv << nll(classDecl); + _buf << indent() << baseVar << ".__class = "sv << classVar << nll(classDecl); + if (!statements.empty()) _buf << indent() << "local self = "sv << classVar << nll(classDecl); + _buf << join(statements); + if (extend) { + _buf << indent() << "if "sv << parentVar << ".__inherited then"sv << nll(classDecl); + _buf << indent(1) << parentVar << ".__inherited("sv << parentVar << ", "sv << classVar << ")"sv << nll(classDecl); + _buf << indent() << "end"sv << nll(classDecl); + } + if (!assignItem.empty()) { + _buf << indent() << assignItem << " = "sv << classVar << nll(classDecl); + } + switch (usage) { + case ExpUsage::Return: { + _buf << indent() << "return "sv << classVar << nlr(classDecl); + break; + } + case ExpUsage::Assignment: { + _buf << tmp.back(); + break; + } + default: break; + } + temp.push_back(clearBuf()); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(classDecl)); + out.push_back(join(temp)); + } + + size_t transform_class_member_list(class_member_list_t* class_member_list, std::list& out, const std::string& classVar) { + str_list temp; + size_t count = 0; + for (auto keyValue : class_member_list->values.objects()) { + MemType type = MemType::Common; + BLOCK_START + auto normal_pair = ast_cast(keyValue); + BREAK_IF(!normal_pair); + auto keyName = normal_pair->key.as(); + BREAK_IF(!keyName); + std::string newSuperCall; + auto selfName = keyName->name.as(); + if (selfName) { + type = MemType::Property; + auto name = ast_cast(selfName->name); + if (!name) throw std::logic_error(debugInfo("Invalid class poperty name."sv, selfName->name)); + newSuperCall = classVar + s(".__parent."sv) + toString(name->name); + } else { + auto x = keyName; + auto nameNode = keyName->name.as(); + if (!nameNode) break; + auto name = toString(nameNode); + if (name == "new"sv) { + type = MemType::Builtin; + keyName->name.set(toAst("__init"sv, Name, x)); + newSuperCall = classVar + s(".__parent.__init"sv); + } else { + newSuperCall = classVar + s(".__parent.__base."sv) + name; + } + } + normal_pair->value->traverse([&](ast_node* node) { + if (node->getId() == "ClassDecl"_id) return traversal::Return; + if (auto chainValue = ast_cast(node)) { + if (auto callable = ast_cast(chainValue->items.front())) { + auto var = callable->item.get(); + if (toString(var) == "super"sv) { + auto insertSelfToArguments = [&](ast_node* item) { + auto x = item; + switch (item->getId()) { + case "InvokeArgs"_id: { + auto invoke = static_cast(item); + invoke->args.push_front(toAst("self"sv, Exp, x)); + return true; + } + case "Invoke"_id: { + auto invoke = static_cast(item); + invoke->args.push_front(toAst("self"sv, Exp, x)); + return true; + } + default: + return false; + } + }; + const auto& chainList = chainValue->items.objects(); + if (chainList.size() >= 2) { + auto it = chainList.begin(); + auto secondItem = *(++it); + if (!insertSelfToArguments(secondItem)) { + if (auto colonChainItem = ast_cast(secondItem)) { + if (chainList.size() > 2 && insertSelfToArguments(*(++it))) { + colonChainItem->switchToDot = true; + } + } + newSuperCall = classVar + s(".__parent"sv); + } + } else { + newSuperCall = classVar + s(".__parent"sv); + } + auto newChain = toAst(newSuperCall, ChainValue, chainValue); + chainValue->items.pop_front(); + const auto& items = newChain->items.objects(); + for (auto it = items.rbegin(); it != items.rend(); ++it) { + chainValue->items.push_front(*it); + } + } + } + } + return traversal::Continue; + }); + BLOCK_END + pushScope(); + if (type == MemType::Property) { + decIndentOffset(); + } + switch (keyValue->getId()) { + case "variable_pair"_id: + transform_variable_pair(static_cast(keyValue), temp); + break; + case "normal_pair"_id: + transform_normal_pair(static_cast(keyValue), temp); + break; + } + if (type == MemType::Property) { + incIndentOffset(); + } + popScope(); + out.push_back({temp.back(), type, keyValue}); + temp.clear(); + ++count; + } + return count; + } + + void transformAssignable(Assignable_t* assignable, str_list& out) { + auto item = assignable->item.get(); + switch (item->getId()) { + case "AssignableChain"_id: transformAssignableChain(static_cast(item), out); break; + case "Variable"_id: transformVariable(static_cast(item), out); break; + case "SelfName"_id: transformSelfName(static_cast(item), out, false); break; + default: break; + } + } + + void transformWithClosure(With_t* with, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(with)); + pushScope(); + transformWith(with, temp, nullptr, true); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformWith(With_t* with, str_list& out, ExpList_t* assignList = nullptr, bool returnValue = false) { + auto x = with; + str_list temp; + std::string withVar; + bool scoped = false; + if (with->assigns) { + checkAssignable(with->valueList); + auto vars = getAssignVars(with); + if (vars.front().empty()) { + if (with->assigns->values.objects().size() == 1) { + auto var = singleVariableFrom(with->assigns->values.objects().front()); + if (!var.empty()) { + withVar = var; + } + } + if (withVar.empty()) { + withVar = getUnusedName("_with_"); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(withVar, ExpList, x)); + auto assign = x->new_ptr(); + assign->values.push_back(with->assigns->values.objects().front()); + assignment->action.set(assign); + if (!returnValue) { + scoped = true; + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + transformAssignment(assignment, temp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(with->valueList); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(withVar, Exp, x)); + bool skipFirst = true; + for (auto value : with->assigns->values.objects()) { + if (skipFirst) { + skipFirst = false; + continue; + } + assign->values.push_back(value); + } + assignment->action.set(assign); + transformAssignment(assignment, temp); + } else { + withVar = vars.front(); + auto assignment = x->new_ptr(); + assignment->expList.set(with->valueList); + assignment->action.set(with->assigns); + if (!returnValue) { + scoped = true; + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + transformAssignment(assignment, temp); + } + } else { + withVar = singleVariableFrom(with->valueList); + if (withVar.empty()) { + withVar = getUnusedName("_with_"); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(withVar, ExpList, x)); + auto assign = x->new_ptr(); + assign->values.dup(with->valueList->exprs); + assignment->action.set(assign); + if (!returnValue) { + scoped = true; + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + transformAssignment(assignment, temp); + } + } + if (!scoped && !returnValue) { + pushScope(); + scoped = traversal::Stop == with->body->traverse([&](ast_node* node) { + if (auto statement = ast_cast(node)) { + ClassDecl_t* clsDecl = nullptr; + if (auto assignment = assignmentFrom(statement)) { + auto names = getAssignDefs(assignment->expList.get()); + if (!names.empty()) { + return traversal::Stop; + } + auto info = extractDestructureInfo(assignment); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable && !isDefined(item.name)) + return traversal::Stop; + } + BLOCK_START + auto assign = assignment->action.as(); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto exp = ast_cast(assign->values.objects().front()); + BREAK_IF(!exp); + if (auto value = singleValueFrom(exp)) { + clsDecl = value->getByPath(); + } + BLOCK_END + } else if (auto expList = expListFrom(statement)) { + auto value = singleValueFrom(expList); + clsDecl = value->getByPath(); + } + if (clsDecl) { + auto variable = clsDecl->name.as(); + if (!isDefined(toString(variable))) return traversal::Stop; + } + return traversal::Return; + } + return traversal::Continue; + }); + popScope(); + if (scoped) { + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + } + _withVars.push(withVar); + transformBody(with->body, temp); + _withVars.pop(); + if (assignList) { + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(withVar, Exp, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + if (returnValue) { + auto stmt = lastStatementFrom(with->body); + if (!stmt->content.is()) { + temp.push_back(indent() + s("return "sv) + withVar + nll(with)); + } + } + if (scoped) { + popScope(); + temp.push_back(indent() + s("end"sv) + nll(with)); + } + out.push_back(join(temp)); + } + + void transform_const_value(const_value_t* const_value, str_list& out) { + out.push_back(toString(const_value)); + } + + void transformExport(Export_t* exportNode, str_list& out) { + auto x = exportNode; + auto item = exportNode->item.get(); + switch (item->getId()) { + case "ClassDecl"_id: { + auto classDecl = static_cast(item); + if (classDecl->name && classDecl->name->item->getId() == "Variable"_id) { + markVarExported(ExportMode::Any, true); + addExportedVar(toString(classDecl->name->item)); + } + transformClassDecl(classDecl, out); + break; + } + case "export_op"_id: + if (toString(item) == "*"sv) { + markVarExported(ExportMode::Any, false); + } else { + markVarExported(ExportMode::Capital, false); + } + break; + case "export_values"_id: { + markVarExported(ExportMode::Any, true); + auto values = exportNode->item.to(); + if (values->valueList) { + auto expList = x->new_ptr(); + for (auto name : values->nameList->names.objects()) { + addExportedVar(toString(name)); + auto callable = x->new_ptr(); + callable->item.set(name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + expList->exprs.push_back(exp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + auto assign = x->new_ptr(); + assign->values.dup(values->valueList->exprs); + assignment->action.set(assign); + transformAssignment(assignment, out); + } else { + for (auto name : values->nameList->names.objects()) { + addExportedVar(toString(name)); + } + } + break; + } + default: + break; + } + } + + void transformTable(ast_node* table, const node_container& pairs, str_list& out) { + if (pairs.empty()) { + out.push_back(s("{ }"sv)); + return; + } + str_list temp; + pushScope(); + for (auto pair : pairs) { + switch (pair->getId()) { + case "Exp"_id: transformExp(static_cast(pair), temp); break; + case "variable_pair"_id: transform_variable_pair(static_cast(pair), temp); break; + case "normal_pair"_id: transform_normal_pair(static_cast(pair), temp); break; + } + temp.back() = indent() + temp.back() + (pair == pairs.back() ? Empty : s(","sv)) + nll(pair); + } + out.push_back(s("{"sv) + nll(table) + join(temp)); + popScope(); + out.back() += (indent() + s("}"sv)); + } + + void transform_simple_table(simple_table_t* table, str_list& out) { + transformTable(table, table->pairs.objects(), out); + } + + void transformTblComprehension(TblComprehension_t* comp, str_list& out) { + str_list kv; + std::string tbl = getUnusedName("_tbl_"); + addToScope(tbl); + str_list temp; + auto compInner = comp->forLoop.get(); + for (auto item : compInner->items.objects()) { + switch (item->getId()) { + case "CompForEach"_id: + transformCompForEach(static_cast(item), temp); + break; + case "CompFor"_id: + transformCompFor(static_cast(item), temp); + break; + case "Exp"_id: + transformExp(static_cast(item), temp); + temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); + pushScope(); + break; + } + } + transformExp(comp->key, kv); + if (comp->value) { + transformExp(comp->value->value, kv); + } + for (size_t i = 0; i < compInner->items.objects().size(); ++i) { + popScope(); + } + _buf << indent() << "local "sv << tbl << " = { }"sv << nll(comp); + _buf << join(temp); + pushScope(); + if (!comp->value) { + auto keyVar = getUnusedName("_key_"); + auto valVar = getUnusedName("_val_"); + _buf << indent(int(temp.size()) - 1) << "local "sv << keyVar << ", "sv << valVar << " = "sv << kv.front() << nll(comp); + kv.front() = keyVar; + kv.push_back(valVar); + } + _buf << indent(int(temp.size()) - 1) << tbl << "["sv << kv.front() << "] = "sv << kv.back() << nll(comp); + for (int ind = int(temp.size()) - 2; ind > -1 ; --ind) { + _buf << indent(ind) << "end"sv << nll(comp); + } + popScope(); + _buf << indent() << "end"sv << nll(comp); + out.push_back(tbl); + out.push_back(clearBuf()); + } + + void transformTblCompInPlace(TblComprehension_t* comp, ExpList_t* expList, str_list& out) { + auto x = comp; + str_list temp; + auto ind = indent(); + pushScope(); + transformTblComprehension(comp, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(temp.front(), Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + out.push_back( + ind + s("do"sv) + nll(comp) + + *(++temp.begin()) + + temp.back()); + popScope(); + out.back() = out.back() + indent() + s("end"sv) + nlr(comp); + } + + void transformTblCompReturn(TblComprehension_t* comp, str_list& out) { + str_list temp; + transformTblComprehension(comp, temp); + out.push_back(temp.back() + indent() + s("return "sv) + temp.front() + nlr(comp)); + } + + void transformTblCompClosure(TblComprehension_t* comp, str_list& out) { + str_list temp; + std::string before = s("(function()"sv) + nll(comp); + pushScope(); + transformTblComprehension(comp, temp); + const auto& tbVar = temp.front(); + const auto& compBody = temp.back(); + out.push_back( + before + + compBody + + indent() + s("return "sv) + tbVar + nlr(comp)); + popScope(); + out.back() = out.back() + indent() + s("end)()"sv); + } + + void transformCompFor(CompFor_t* comp, str_list& out) { + str_list temp; + std::string varName = toString(comp->varName); + transformExp(comp->startValue, temp); + transformExp(comp->stopValue, temp); + if (comp->stepValue) { + transformExp(comp->stepValue->value, temp); + } else { + temp.emplace_back(); + } + auto it = temp.begin(); + const auto& start = *it; + const auto& stop = *(++it); + const auto& step = *(++it); + _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(comp); + out.push_back(clearBuf()); + pushScope(); + addToScope(varName); + } + + void transformTableBlock(TableBlock_t* table, str_list& out) { + transformTable(table, table->values.objects(), out); + } + + void transformDo(Do_t* doNode, str_list& out, bool implicitReturn = false) { + str_list temp; + temp.push_back(indent() + s("do"sv) + nll(doNode)); + pushScope(); + transformBody(doNode->body, temp, implicitReturn); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(doNode)); + out.push_back(join(temp)); + } + + void transformDoClosure(Do_t* doNode, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(doNode)); + pushScope(); + transformBody(doNode->body, temp, true); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformImport(Import_t* import, str_list& out) { + str_list temp; + auto x = import; + auto objVar = singleVariableFrom(import->exp); + ast_ptr objAssign; + if (objVar.empty()) { + objVar = getUnusedName("_obj_"sv); + auto expList = toAst(objVar, ExpList, x); + auto assign = x->new_ptr(); + assign->values.push_back(import->exp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + objAssign.set(assignment); + } + auto expList = x->new_ptr(); + auto assign = x->new_ptr(); + for (auto name : import->names.objects()) { + switch (name->getId()) { + case "Variable"_id: { + auto var = ast_to(name); + { + auto callable = toAst(objVar, Callable, x); + auto dotChainItem = x->new_ptr(); + dotChainItem->name.set(var->name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + chainValue->items.push_back(dotChainItem); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + assign->values.push_back(exp); + } + auto callable = x->new_ptr(); + callable->item.set(var); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + expList->exprs.push_back(exp); + break; + } + case "colon_import_name"_id: { + auto var = static_cast(name)->name.get(); + { + auto nameNode = var->name.get(); + auto callable = toAst(objVar, Callable, x); + auto colonChain = x->new_ptr(); + colonChain->name.set(nameNode); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + chainValue->items.push_back(colonChain); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + assign->values.push_back(exp); + } + auto callable = x->new_ptr(); + callable->item.set(var); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + expList->exprs.push_back(exp); + break; + } + } + } + if (objAssign) { + auto preDef = getPredefine(transformAssignDefs(expList)); + if (!preDef.empty()) { + temp.push_back(preDef + nll(import)); + } + temp.push_back(indent() + s("do"sv) + nll(import)); + pushScope(); + transformAssignment(objAssign, temp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + if (objAssign) { + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(import)); + } + out.push_back(join(temp)); + } + + void transformWhileInPlace(While_t* whileNode, str_list& out, ExpList_t* expList = nullptr) { + auto x = whileNode; + str_list temp; + if (expList) { + temp.push_back(indent() + s("do"sv) + nll(whileNode)); + } + pushScope(); + auto accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + auto lenVar = getUnusedName("_len_"sv); + addToScope(lenVar); + temp.push_back(indent() + s("local "sv) + accumVar + s(" = { }"sv) + nll(whileNode)); + temp.push_back(indent() + s("local "sv) + lenVar + s(" = 1"sv) + nll(whileNode)); + transformExp(whileNode->condition, temp); + temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); + pushScope(); + auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), ExpList, x); + assignLastExplist(assignLeft, whileNode->body); + auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); + transformLoopBody(whileNode->body, temp, lenLine); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(whileNode)); + if (expList) { + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accumVar, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } else { + temp.push_back(indent() + s("return "sv) + accumVar + nlr(whileNode)); + } + popScope(); + if (expList) { + temp.push_back(indent() + s("end"sv) + nlr(whileNode)); + } + out.push_back(join(temp)); + } + + void transformWhileClosure(While_t* whileNode, str_list& out) { + auto x = whileNode; + str_list temp; + temp.push_back(s("(function() "sv) + nll(whileNode)); + pushScope(); + auto accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + auto lenVar = getUnusedName("_len_"sv); + addToScope(lenVar); + temp.push_back(indent() + s("local "sv) + accumVar + s(" = { }"sv) + nll(whileNode)); + temp.push_back(indent() + s("local "sv) + lenVar + s(" = 1"sv) + nll(whileNode)); + transformExp(whileNode->condition, temp); + temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); + pushScope(); + auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), ExpList, x); + assignLastExplist(assignLeft, whileNode->body); + auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); + transformLoopBody(whileNode->body, temp, lenLine); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(whileNode)); + temp.push_back(indent() + s("return "sv) + accumVar + nlr(whileNode)); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformWhile(While_t* whileNode, str_list& out) { + str_list temp; + pushScope(); + transformExp(whileNode->condition, temp); + transformLoopBody(whileNode->body, temp, Empty); + popScope(); + _buf << indent() << "while "sv << temp.front() << " do"sv << nll(whileNode); + _buf << temp.back(); + _buf << indent() << "end"sv << nlr(whileNode); + out.push_back(clearBuf()); + } + + void transformSwitchClosure(Switch_t* switchNode, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(switchNode)); + pushScope(); + transformSwitch(switchNode, temp, true); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformSwitch(Switch_t* switchNode, str_list& out, bool implicitReturn = false) { + str_list temp; + auto objVar = singleVariableFrom(switchNode->target); + if (objVar.empty()) { + objVar = getUnusedName("_exp_"sv); + addToScope(objVar); + transformExp(switchNode->target, temp); + _buf << indent() << "local "sv << objVar << " = "sv << temp.back() << nll(switchNode); + temp.back() = clearBuf(); + } + const auto& branches = switchNode->branches.objects(); + for (auto branch_ : branches) { + auto branch = static_cast(branch_); + temp.push_back(indent() + s(branches.front() == branch ? "if"sv : "elseif"sv)); + str_list tmp; + const auto& exprs = branch->valueList->exprs.objects(); + for (auto exp_ : exprs) { + auto exp = static_cast(exp_); + transformExp(exp, tmp); + if (!singleValueFrom(exp)) { + tmp.back() = s("("sv) + tmp.back() + s(")"sv); + } + temp.back().append(s(" "sv) + tmp.back() + s(" == "sv) + objVar + + s(exp == exprs.back() ? ""sv : " or"sv)); + } + temp.back().append(s(" then"sv) + nll(branch)); + pushScope(); + transformBody(branch->body, temp, implicitReturn); + popScope(); + } + if (switchNode->lastBranch) { + temp.push_back(indent() + s("else"sv) + nll(switchNode->lastBranch)); + pushScope(); + transformBody(switchNode->lastBranch, temp, implicitReturn); + popScope(); + } + temp.push_back(indent() + s("end"sv) + nlr(switchNode)); + out.push_back(join(temp)); + } + + void transformLocal(Local_t* local, str_list& out) { + if (!local->forceDecls.empty() || !local->decls.empty()) { + str_list defs; + for (const auto& decl : local->forceDecls) { + forceAddToScope(decl); + defs.push_back(decl); + } + for (const auto& decl : local->decls) { + if (addToScope(decl)) { + defs.push_back(decl); + } + } + auto preDefine = getPredefine(defs); + if (!preDefine.empty()) { + out.push_back(preDefine + nll(local)); + } + } + } + + void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { + auto keyword = toString(breakLoop); + if (keyword == "break"sv) { + out.push_back(indent() + keyword + nll(breakLoop)); + return; + } + if (_continueVars.empty()) throw std::logic_error(debugInfo("Continue is not inside a loop."sv, breakLoop)); + _buf << indent() << _continueVars.top() << " = true"sv << nll(breakLoop); + _buf << indent() << "break"sv << nll(breakLoop); + out.push_back(clearBuf()); + } +}; + +const std::string MoonCompliler::Empty; + +std::pair moonCompile(const std::string& codes, bool implicitReturnRoot, bool lineNumber) { + return MoonCompliler{}.complile(codes, false, implicitReturnRoot, lineNumber); +} + +std::pair moonCompile(const std::string& codes, std::list& globals, bool implicitReturnRoot, bool lineNumber) { + auto compiler = MoonCompliler{}; + auto result = compiler.complile(codes, true, implicitReturnRoot, lineNumber); + for (const auto& var : compiler.getGlobals()) { + int line,col; + std::tie(line,col) = var.second; + globals.push_back({var.first, line, col}); + } + return result; +} + +} // namespace MoonP -- cgit v1.2.3-55-g6feb