/* Copyright (c) 2020 Jin Li, http://www.luvfight.me Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #include #include #include #include #include #include #include #include "MoonP/moon_parser.h" #include "MoonP/moon_compiler.h" namespace MoonP { using namespace std::string_view_literals; using namespace parserlib; #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); } const char* moonScriptVersion() { return "0.5.0-r0.2.0"; } class MoonCompilerImpl { public: std::tuple compile(std::string_view codes, const MoonConfig& config) { _config = config; _info = _parser.parse(codes); GlobalVars globals; if (_info.node) { try { str_list out; pushScope(); enableReturn.push(_info.moduleName.empty()); transformBlock(_info.node.to()->block, out, config.implicitReturnRoot ? ExpUsage::Return : ExpUsage::Common, nullptr, true); popScope(); if (config.lintGlobalVariable) { globals = std::make_unique>(); for (const auto& var : _globals) { int line,col; std::tie(line,col) = var.second; globals->push_back({var.first, line, col}); } } clear(); return {std::move(out.back()), Empty, std::move(globals)}; } catch (const std::logic_error& error) { clear(); return {Empty, error.what(), std::move(globals)}; } } else { clear(); return {Empty, _info.error, std::move(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(); } private: MoonConfig _config; MoonParser _parser; ParseInfo _info; int _indentOffset = 0; std::stack enableReturn; std::list> _codeCache; std::stack _withVars; std::stack _continueVars; std::unordered_map> _globals; std::ostringstream _buf; std::ostringstream _joinBuf; const std::string _newLine = "\n"; enum class LocalMode { None = 0, Capital = 1, Any = 2 }; enum class GlobalMode { None = 0, Capital = 1, Any = 2 }; struct Scope { GlobalMode mode = GlobalMode::None; std::unique_ptr> vars; std::unique_ptr> allows; std::unique_ptr> globals; }; 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 }; void pushScope() { _scopes.emplace_back(); _scopes.back().vars = std::make_unique>(); } void popScope() { _scopes.pop_back(); } bool isDefined(const std::string& name) const { bool isDefined = false; int mode = int(std::isupper(name[0]) ? GlobalMode::Capital : GlobalMode::Any); const auto& current = _scopes.back(); if (int(current.mode) >= mode) { if (current.globals) { if (current.globals->find(name) != current.globals->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) const { 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 markVarGlobal(GlobalMode mode, bool specified) { auto& scope = _scopes.back(); scope.mode = mode; if (specified && !scope.globals) { scope.globals = std::make_unique>(); } } void addGlobalVar(const std::string& name) { auto& scope = _scopes.back(); scope.globals->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) const { 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) const { if (_config.reserveLineNumber) { return s(" -- "sv) + std::to_string(node->m_begin.m_line) + _newLine; } else { return _newLine; } } const std::string nlr(ast_node* node) const { if (_config.reserveLineNumber) { return s(" -- "sv) + std::to_string(node->m_end.m_line) + _newLine; } else { return _newLine; } } void incIndentOffset() { _indentOffset++; } void decIndentOffset() { _indentOffset--; } std::string indent() const { return std::string(_scopes.size() - 1 + _indentOffset, '\t'); } std::string indent(int offset) const { return std::string(_scopes.size() - 1 + _indentOffset + offset, '\t'); } 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; } Value_t* singleValueFrom(ast_node* item) const { Exp_t* exp = nullptr; switch (item->getId()) { case id(): exp = static_cast(item); break; case id(): { auto expList = static_cast(item); if (expList->exprs.size() == 1) { exp = static_cast(expList->exprs.front()); } break; } case 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) const { auto value = singleValueFrom(expList); if (value && value->item.is()) { return static_cast(value->item.get()); } return nullptr; } Value_t* firstValueFrom(ast_node* item) const { 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) const { if (auto stmt = body->content.as()) { return stmt; } else { const auto& stmts = body->content.to()->statements.objects(); return stmts.empty() ? nullptr : static_cast(stmts.back()); } } Statement_t* lastStatementFrom(Block_t* block) const { const auto& stmts = block->statements.objects(); return stmts.empty() ? nullptr : static_cast(stmts.back()); } template ast_ptr toAst(std::string_view codes, ast_node* parent) { auto res = _parser.parse(s(codes)); res.node->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; }); _codeCache.push_back(std::move(res.codes)); return ast_ptr(res.node.template to()); } bool isChainValueCall(ChainValue_t* chainValue) const { return ast_is(chainValue->items.back()); } enum class ChainType { Common, EndWithColon, EndWithEOP, HasEOP, HasKeyword }; ChainType specialChainValue(ChainValue_t* chainValue) const { if (ast_is(chainValue->items.back())) { return ChainType::EndWithColon; } if (ast_is(chainValue->items.back())) { return ChainType::EndWithEOP; } ChainType type = ChainType::Common; for (auto item : chainValue->items.objects()) { if (auto colonChain = ast_cast(item)) { if (ast_is(colonChain->name)) { type = ChainType::HasKeyword; } } else if (ast_is(item)) { return ChainType::HasEOP; } } return type; } std::string singleVariableFrom(ChainValue_t* chainValue) { BLOCK_START BREAK_IF(!chainValue); BREAK_IF(chainValue->items.size() != 1); auto callable = ast_cast(chainValue->items.front()); BREAK_IF(!callable); ast_node* var = callable->item.as(); if (!var) { if (auto self = callable->item.as()) { var = self->name.as(); } } BREAK_IF(!var); str_list tmp; transformCallable(callable, tmp); return tmp.back(); BLOCK_END return Empty; } 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() || callable->getByPath())); str_list tmp; transformCallable(callable, tmp); return tmp.back(); BLOCK_END return Empty; } Variable_t* variableFrom(Exp_t* exp) { BLOCK_START auto value = singleValueFrom(exp); 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); return callable->item.as(); BLOCK_END return nullptr; } bool isAssignable(const node_container& chainItems) const { if (chainItems.size() == 1) { auto firstItem = chainItems.back(); if (auto callable = ast_cast(firstItem)) { switch (callable->item->getId()) { case id(): case id(): return true; } } else if (firstItem->getId() == id()) { return true; } } else { auto lastItem = chainItems.back(); switch (lastItem->getId()) { case id(): case id(): return true; } } return false; } bool isAssignable(Exp_t* exp) const { if (auto value = singleValueFrom(exp)) { auto item = value->item.get(); switch (item->getId()) { case id(): return true; case id(): { auto simpleValue = static_cast(item); if (simpleValue->value.is()) { return true; } return false; } case id(): { auto chainValue = static_cast(item); return isAssignable(chainValue->items.objects()); } } } return false; } bool isAssignable(Assignable_t* assignable) const { if (auto assignableChain = ast_cast(assignable->item)) { return isAssignable(assignableChain->items.objects()); } return true; } void checkAssignable(ExpList_t* expList) const { for (auto exp_ : expList->exprs.objects()) { Exp_t* exp = static_cast(exp_); if (!isAssignable(exp)) { throw std::logic_error(_info.errorMessage("Left hand expression is not assignable."sv, exp)); } } } bool isPureBackcall(Exp_t* exp) const { if (exp->opValues.empty()) { return false; } bool backcall = true; for (auto _opValue : exp->opValues.objects()) { auto opValue = static_cast(_opValue); if (!opValue->op.is()) { backcall = false; break; } } return backcall; } void transformStatement(Statement_t* statement, str_list& out) { auto x = statement; if (statement->appendix) { if (auto assignment = assignmentFrom(statement)) { auto preDefine = getPredefine(assignment); if (!preDefine.empty()) out.push_back(preDefine + nll(statement)); } auto appendix = statement->appendix.get(); switch (appendix->item->getId()) { case 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); ifCond->assign.set(if_else_line->assign); 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 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 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: assert(false); break; } } auto content = statement->content.get(); if (!content) { out.push_back(Empty); return; } switch (content->getId()) { case id(): transformImport(static_cast(content), out); break; case id(): transformWhile(static_cast(content), out); break; case id(): transformFor(static_cast(content), out); break; case id(): transformForEach(static_cast(content), out); break; case id(): transformReturn(static_cast(content), out); break; case id(): transformLocal(static_cast(content), out); break; case id(): transformGlobal(static_cast(content), out); break; case id(): transformExport(static_cast(content), out); break; case id(): transformBreakLoop(static_cast(content), out); break; case 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 id(): transformIf(static_cast(value), out, ExpUsage::Common); break; case id(): transformClassDecl(static_cast(value), out, ExpUsage::Common); break; case id(): transformUnless(static_cast(value), out, ExpUsage::Common); break; case id(): transformSwitch(static_cast(value), out, ExpUsage::Common); break; case id(): transformWith(static_cast(value), out); break; case id(): transformForEach(static_cast(value), out); break; case id(): transformFor(static_cast(value), out); break; case id(): transformWhile(static_cast(value), out); break; case id(): transformDo(static_cast(value), out, ExpUsage::Common); break; case 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; } } } else if (expList->exprs.size() == 1){ auto exp = static_cast(expList->exprs.back()); if (isPureBackcall(exp)) { transformExp(exp, out, ExpUsage::Common); break; } } throw std::logic_error(_info.errorMessage("Expression list is not supported here."sv, expList)); } break; } default: assert(false); 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); std::string name; if (auto var = callable->item.as()) { name = _parser.toString(var); } else if (auto self = callable->item.as()) { if (self->name.is()) name = "self"sv; } BREAK_IF(name.empty()); if (!isDefined(name)) { preDefs.push_back(name); } BLOCK_END } } else { throw std::logic_error(_info.errorMessage("Left hand expression is not assignable."sv, exp)); } } return preDefs; } str_list transformAssignDefs(ExpList_t* expList, bool markDefined = true) { str_list defs; 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); std::string name; if (auto var = callable->item.as()) { name = _parser.toString(var); } else if (auto self = callable->item.as()) { if (self->name.is()) name = "self"sv; } BREAK_IF(name.empty()); if (!markDefined || addToScope(name)) { defs.push_back(name); } BLOCK_END } } else { throw std::logic_error(_info.errorMessage("Left hand expression is not assignable."sv, exp)); } } return defs; } 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, true); 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; } 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 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 id(): case id(): { auto expList = assignment->expList.get(); str_list temp; auto defs = transformAssignDefs(expList); if (!defs.empty()) temp.push_back(getPredefine(defs) + nll(expList)); switch (value->getId()) { case id(): transformIf(static_cast(value), temp, ExpUsage::Assignment, expList); break; case id(): transformUnless(static_cast(value), temp, ExpUsage::Assignment, expList); break; } out.push_back(join(temp)); return; } case id(): { auto switchNode = static_cast(value); auto assignList = assignment->expList.get(); std::string preDefine = getPredefine(assignment); transformSwitch(switchNode, out, ExpUsage::Assignment, assignList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } case id(): { auto withNode = static_cast(value); auto expList = assignment->expList.get(); std::string preDefine = getPredefine(assignment); transformWith(withNode, out, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } case id(): { auto expList = assignment->expList.get(); auto doNode = static_cast(value); std::string preDefine = getPredefine(assignment); transformDo(doNode, out, ExpUsage::Assignment, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } case id(): { auto expList = assignment->expList.get(); std::string preDefine = getPredefine(assignment); transformComprehension(static_cast(value), out, ExpUsage::Assignment, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } case id(): { auto expList = assignment->expList.get(); std::string preDefine = getPredefine(assignment); transformTblComprehension(static_cast(value), out, ExpUsage::Assignment, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } case id(): { auto expList = assignment->expList.get(); std::string preDefine = getPredefine(assignment); transformForInPlace(static_cast(value), out, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } case id(): { auto expList = assignment->expList.get(); std::string preDefine = getPredefine(assignment); transformForEachInPlace(static_cast(value), out, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } case id(): { auto expList = assignment->expList.get(); std::string preDefine = getPredefine(assignment); transformClassDecl(static_cast(value), out, ExpUsage::Assignment, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } case id(): { auto expList = assignment->expList.get(); std::string preDefine = getPredefine(assignment); transformWhileInPlace(static_cast(value), out, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } } auto exp = ast_cast(value); BREAK_IF(!exp); if (auto chainValue = exp->value->item.as()) { auto type = specialChainValue(chainValue); auto expList = assignment->expList.get(); switch (type) { case ChainType::HasEOP: case ChainType::EndWithColon: { std::string preDefine = getPredefine(assignment); transformChainValue(chainValue, out, ExpUsage::Assignment, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } case ChainType::HasKeyword: transformChainValue(chainValue, out, ExpUsage::Assignment, expList); return; case ChainType::Common: case ChainType::EndWithEOP: break; } } if (isPureBackcall(exp)) { auto expList = assignment->expList.get(); transformExp(exp, out, ExpUsage::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 (_parser.match(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 id(): transformWithClosure(static_cast(value), out); break; case id(): transformIf(static_cast(value), out, ExpUsage::Closure); break; case id(): transformSwitch(static_cast(value), out, ExpUsage::Closure); break; case id(): transformTableBlock(static_cast(value), out); break; case id(): transformExp(static_cast(value), out, ExpUsage::Closure); break; default: assert(false); break; } } std::list destructFromExp(ast_node* node) { const node_container* tableItems = nullptr; if (ast_is(node)) { auto item = singleValueFrom(node)->item.get(); if (!item) throw std::logic_error(_info.errorMessage("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 id(): { ++index; if (!isAssignable(static_cast(pair))) { throw std::logic_error(_info.errorMessage("Can't destructure value."sv, pair)); } auto value = singleValueFrom(pair); auto item = value->item.get(); if (ast_is(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 lintGlobal = _config.lintGlobalVariable; _config.lintGlobalVariable = false; auto exp = static_cast(pair); auto varName = singleVariableFrom(exp); bool isVariable = !varName.empty(); if (!isVariable) { str_list temp; transformExp(exp, temp, ExpUsage::Closure); varName = std::move(temp.back()); } _config.lintGlobalVariable = lintGlobal; pairs.push_back({ isVariable, varName, s("["sv) + std::to_string(index) + s("]"sv) }); } break; } case id(): { auto vp = static_cast(pair); auto name = _parser.toString(vp->name); if (Keywords.find(name) != Keywords.end()) { pairs.push_back({true, name, s("[\""sv) + name + s("\"]"sv)}); } else { pairs.push_back({true, name, s("."sv) + name}); } break; } case id(): { auto np = static_cast(pair); auto key = np->key->getByPath(); if (!key) throw std::logic_error(_info.errorMessage("Invalid key for destructure."sv, np)); if (auto exp = np->value.as()) { if (!isAssignable(exp)) throw std::logic_error(_info.errorMessage("Can't destructure value."sv, exp)); auto item = singleValueFrom(exp)->item.get(); if (ast_is(item) || item->getByPath()) { auto subPairs = destructFromExp(exp); auto name = _parser.toString(key); for (auto& p : subPairs) { if (Keywords.find(name) != Keywords.end()) { pairs.push_back({p.isVariable, p.name, s("[\""sv) + name + s("\"]"sv) + p.structure}); } else { pairs.push_back({p.isVariable, p.name, s("."sv) + name + p.structure}); } } } else { bool lintGlobal = _config.lintGlobalVariable; _config.lintGlobalVariable = false; auto varName = singleVariableFrom(exp); bool isVariable = !varName.empty(); if (!isVariable) { str_list temp; transformExp(exp, temp, ExpUsage::Closure); varName = std::move(temp.back()); } _config.lintGlobalVariable = lintGlobal; auto name = _parser.toString(key); if (Keywords.find(name) != Keywords.end()) { pairs.push_back({ isVariable, varName, s("[\""sv) + name + s("\"]"sv) }); } else { pairs.push_back({ isVariable, varName, s("."sv) + name }); } } break; } if (np->value.is()) { auto subPairs = destructFromExp(pair); for (auto& p : subPairs) { pairs.push_back({p.isVariable, p.name, s("."sv) + _parser.toString(key) + p.structure}); } } break; } default: assert(false); break; } } return pairs; } std::pair, ast_ptr> extractDestructureInfo(ExpListAssign_t* assignment, bool varDefOnly = false) { 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, x); while (exprs.size() < size) exprs.emplace_back(var); } ast_ptr nullNode; if (values.size() < size) { nullNode = toAst("nil"sv, 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}); auto& destruct = destructs.emplace_back(); if (!varDefOnly) { pushScope(); transformAssignItem(*j, temp); destruct.value = temp.back(); temp.pop_back(); popScope(); } 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 id(): { if (expList->exprs.size() > 1) throw std::logic_error(_info.errorMessage("Can not apply update to multiple values."sv, expList)); auto update = static_cast(action); auto leftExp = static_cast(expList->exprs.objects().front()); auto leftValue = singleValueFrom(leftExp); if (!leftValue) throw std::logic_error(_info.errorMessage("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_"sv); auto assignment = x->new_ptr(); assignment->expList.set(toAst(upVar, x)); auto assign = x->new_ptr(); assign->values.push_back(exp); assignment->action.set(assign); transformAssignment(assignment, temp); tmpChain->items.push_back(toAst(upVar, 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, ExpUsage::Closure); 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 << _parser.toString(update->op) << " "sv << right << nll(assignment); out.push_back(clearBuf()); break; } case id(): { bool oneLined = true; auto assign = static_cast(action); for (auto val : assign->values.objects()) { if (auto value = singleValueFrom(val)) { if (auto spValue = value->item.as()) { if (spValue->value.is()) { oneLined = false; break; } } } } auto defs = getAssignDefs(expList); if (oneLined && defs.size() == expList->exprs.objects().size()) { 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(); 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: assert(false); break; } } void transformCond(const node_container& nodes, str_list& out, ExpUsage usage, bool unless, ExpList_t* assignList) { 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, assignList); return; } str_list temp; if (usage == ExpUsage::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 id(): ifCondPairs.back().first = static_cast(node); break; case id(): ifCondPairs.back().second = static_cast(node); ifCondPairs.emplace_back(); break; default: assert(false); 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; auto desVar = getUnusedName("_des_"sv); if (assign->values.objects().size() == 1) { auto var = singleVariableFrom(assign->values.objects().front()); if (!var.empty()) { desVar = var; storingValue = false; } } if (storingValue) { if (usage != ExpUsage::Closure) { temp.push_back(indent() + s("do"sv) + nll(assign)); pushScope(); } auto expList = toAst(desVar, 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, 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 != ExpUsage::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, ExpUsage::Closure); tmp.back() = s("("sv) + tmp.back() + s(")"sv); } tmp.back().insert(0, s("not "sv)); unless = false; } else { transformExp(condition, tmp, ExpUsage::Closure); } _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, assignList); popScope(); } if (!pair.first) { temp.push_back(indent() + s("end"sv) + nll(nodes.front())); break; } } if (storingValue && usage != ExpUsage::Closure) { popScope(); temp.push_back(indent() + s("end"sv) + nlr(nodes.front())); } if (usage == ExpUsage::Closure) { popScope(); temp.push_back(indent() + s("end)()"sv)); } out.push_back(join(temp)); } void transformIf(If_t* ifNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { transformCond(ifNode->nodes.objects(), out, usage, false, assignList); } void transformUnless(Unless_t* unless, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { transformCond(unless->nodes.objects(), out, usage, true, assignList); } void transformExpList(ExpList_t* expList, str_list& out) { str_list temp; for (auto exp : expList->exprs.objects()) { transformExp(static_cast(exp), temp, ExpUsage::Closure); } 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, ExpUsage::Closure); } out.push_back(join(temp, ", "sv)); } void transformExp(Exp_t* exp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { auto x = exp; const auto& opValues = exp->opValues.objects(); for (auto it = opValues.begin(); it != opValues.end(); ++it) { auto opValue = static_cast(*it); if (opValue->op.is()) { if (auto chainValue = opValue->value->item.as()) { auto newExp = x->new_ptr(); { auto arg = x->new_ptr(); arg->value.set(exp->value); for (auto i = opValues.begin(); i != it; ++i) { arg->opValues.push_back(*i); } auto next = it; ++next; for (auto i = next; i != opValues.end(); ++i) { newExp->opValues.push_back(*i); } if (isChainValueCall(chainValue)) { auto last = chainValue->items.back(); _ast_list* args = nullptr; if (auto invoke = ast_cast(last)) { args = &invoke->args; } else { args = &(ast_to(last)->args); } bool findPlaceHolder = false; for (auto a : args->objects()) { bool lintGlobal = _config.lintGlobalVariable; _config.lintGlobalVariable = false; auto name = singleVariableFrom(a); _config.lintGlobalVariable = lintGlobal; if (name == "_"sv) { if (!findPlaceHolder) { args->swap(a, arg); findPlaceHolder = true; } else { throw std::logic_error(_info.errorMessage("backcall placeholder can be used only in one place."sv, a)); } } } if (!findPlaceHolder) { args->push_front(arg); } } else { auto invoke = x->new_ptr(); invoke->args.push_front(arg); chainValue->items.push_back(invoke); } auto value = x->new_ptr(); value->item.set(chainValue); newExp->value.set(value); } if (newExp->opValues.size() == 0) { if (usage == ExpUsage::Assignment) { auto assign = x->new_ptr(); assign->values.push_back(newExp); auto assignment = x->new_ptr(); assignment->expList.set(assignList); assignment->action.set(assign); transformAssignment(assignment, out); } else { transformChainValue(chainValue, out, usage); } } else { transformExp(newExp, out, usage, assignList); } return; } else { throw std::logic_error(_info.errorMessage("Backcall operator must be followed by chain value."sv, opValue->value)); } } } str_list temp; transformValue(exp->value, temp); for (auto _opValue : exp->opValues.objects()) { auto opValue = static_cast(_opValue); if (auto op = opValue->op.as()) { transformBinaryOperator(op, temp); } else { temp.push_back(s("|>"sv)); } 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 id(): transformSimpleValue(static_cast(item), out); break; case id(): transform_simple_table(static_cast(item), out); break; case id(): { auto chainValue = static_cast(item); transformChainValue(chainValue, out, ExpUsage::Closure); break; } case id(): transformString(static_cast(item), out); break; default: assert(false); break; } } void transformCallable(Callable_t* callable, str_list& out, const ast_sel& invoke = {}) { auto item = callable->item.get(); switch (item->getId()) { case id(): { transformVariable(static_cast(item), out); if (_config.lintGlobalVariable && !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 id(): { transformSelfName(static_cast(item), out, invoke); if (_config.lintGlobalVariable) { 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 id(): out.push_back(s("..."sv)); break; case id(): transformParens(static_cast(item), out); break; default: assert(false); break; } } void transformParens(Parens_t* parans, str_list& out) { str_list temp; transformExp(parans->expr, temp, ExpUsage::Closure); 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 id(): transform_const_value(static_cast(value), out); break; case id(): transformIf(static_cast(value), out, ExpUsage::Closure); break; case id(): transformUnless(static_cast(value), out, ExpUsage::Closure); break; case id(): transformSwitch(static_cast(value), out, ExpUsage::Closure); break; case id(): transformWithClosure(static_cast(value), out); break; case id(): transformClassDeclClosure(static_cast(value), out); break; case id(): transformForEachClosure(static_cast(value), out); break; case id(): transformForClosure(static_cast(value), out); break; case id(): transformWhileClosure(static_cast(value), out); break; case id(): transformDo(static_cast(value), out, ExpUsage::Closure); break; case id(): transform_unary_exp(static_cast(value), out); break; case id(): transformTblComprehension(static_cast(value), out, ExpUsage::Closure); break; case id(): transformTableLit(static_cast(value), out); break; case id(): transformComprehension(static_cast(value), out, ExpUsage::Closure); break; case id(): transformFunLit(static_cast(value), out); break; case id(): transformNum(static_cast(value), out); break; default: assert(false); break; } } void transformFunLit(FunLit_t* funLit, str_list& out) { enableReturn.push(true); str_list temp; bool isFatArrow = _parser.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, ExpUsage::Return); } 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, ExpUsage::Return); } 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()); enableReturn.pop(); } void transformBody(Body_t* body, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { auto x = body; if (auto stmt = body->content.as()) { auto block = x->new_ptr(); block->statements.push_back(stmt); transformBlock(block, out, usage, assignList); } else { transformBlock(body->content.to(), out, usage, assignList); } } void transformBlock(Block_t* block, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr, bool isRoot = false) { const auto& nodes = block->statements.objects(); LocalMode mode = LocalMode::None; Local_t* any = nullptr, *capital = nullptr; for (auto it = nodes.begin(); it != nodes.end(); ++it) { auto node = *it; auto stmt = static_cast(node); if (auto backcall = stmt->content.as()) { auto x = *nodes.begin(); auto newBlock = x->new_ptr(); if (it != nodes.begin()) { for (auto i = nodes.begin(); i != it; ++i) { newBlock->statements.push_back(*i); } } x = backcall; auto arg = x->new_ptr(); { auto block = x->new_ptr(); auto next = it; ++next; if (next != nodes.end()) { for (auto i = next; i != nodes.end(); ++i) { block->statements.push_back(*i); } } auto body = x->new_ptr(); body->content.set(block); auto funLit = x->new_ptr(); funLit->argsDef.set(backcall->argsDef); auto arrow = _parser.toString(backcall->arrow); funLit->arrow.set(toAst(arrow == "<-"sv ? "->"sv : "=>"sv, x)); funLit->body.set(body); auto simpleValue = x->new_ptr(); simpleValue->value.set(funLit); auto value = x->new_ptr(); value->item.set(simpleValue); arg->value.set(value); } if (isChainValueCall(backcall->value)) { auto last = backcall->value->items.back(); _ast_list* args = nullptr; if (auto invoke = ast_cast(last)) { args = &invoke->args; } else { args = &(ast_to(last)->args); } bool findPlaceHolder = false; for (auto a : args->objects()) { bool lintGlobal = _config.lintGlobalVariable; _config.lintGlobalVariable = false; auto name = singleVariableFrom(a); _config.lintGlobalVariable = lintGlobal; if (name == "_"sv) { if (!findPlaceHolder) { args->swap(a, arg); findPlaceHolder = true; } else { throw std::logic_error(_info.errorMessage("Backcall placeholder can be used only in one place."sv, a)); } } } if (!findPlaceHolder) { args->push_back(arg); } } else { auto invoke = x->new_ptr(); invoke->args.push_back(arg); backcall->value->items.push_back(invoke); } auto newStmt = x->new_ptr(); { auto chainValue = backcall->value.get(); 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); newStmt->content.set(expListAssign); newStmt->appendix.set(stmt->appendix); newBlock->statements.push_back(newStmt); } transformBlock(newBlock, out, usage, assignList, isRoot); return; } if (auto local = stmt->content.as()) { if (auto flag = local->name.as()) { LocalMode newMode = _parser.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(_parser.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, true); 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); } } } if (info.second) { auto defs = transformAssignDefs(info.second->expList, false); for (const auto& def : defs) { if (std::isupper(def[0]) && capital) { capital->decls.push_back(def); } else if (any) { any->decls.push_back(def); } } } 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 = _parser.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 (isRoot && !_info.moduleName.empty()) { block->statements.push_front(toAst(_info.moduleName + s(_info.exportDefault ? "=nil"sv : "={}"sv), block)); } switch (usage) { case ExpUsage::Closure: case ExpUsage::Return: { BLOCK_START BREAK_IF(isRoot && !_info.moduleName.empty()); BREAK_IF(nodes.empty()); auto last = static_cast(nodes.back()); auto x = last; 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 break; } case ExpUsage::Assignment: { auto last = lastStatementFrom(block); if (!last) return; bool lastAssignable = expListFrom(last) || ast_is(last->content); if (lastAssignable) { auto x = last; auto newAssignment = x->new_ptr(); newAssignment->expList.set(assignList); 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); } break; } default: break; } if (!nodes.empty()) { str_list temp; for (auto node : nodes) { transformStatement(static_cast(node), temp); } out.push_back(join(temp)); } else { out.push_back(Empty); } if (isRoot && !_info.moduleName.empty()) { out.back().append(indent() + s("return "sv) + _info.moduleName + nlr(block)); } } void transformReturn(Return_t* returnNode, str_list& out) { if (!enableReturn.top()) { ast_node* target = returnNode->valueList.get(); if (!target) target = returnNode; throw std::logic_error(_info.errorMessage("Illegal return statement here."sv, target)); } if (auto valueList = returnNode->valueList.get()) { if (valueList->exprs.size() == 1) { auto exp = static_cast(valueList->exprs.back()); if (isPureBackcall(exp)) { transformExp(exp, out, ExpUsage::Return); return; } } if (auto singleValue = singleValueFrom(valueList)) { if (auto simpleValue = singleValue->item.as()) { auto value = simpleValue->value.get(); switch (value->getId()) { case id(): transformComprehension(static_cast(value), out, ExpUsage::Return); return; case id(): transformTblComprehension(static_cast(value), out, ExpUsage::Return); return; case id(): transformWith(static_cast(value), out, nullptr, true); return; case id(): transformClassDecl(static_cast(value), out, ExpUsage::Return); return; case id(): transformDo(static_cast(value), out, ExpUsage::Return); return; case id(): transformSwitch(static_cast(value), out, ExpUsage::Return); return; case id(): transformWhileInPlace(static_cast(value), out); return; case id(): transformForInPlace(static_cast(value), out); return; case id(): transformForEachInPlace(static_cast(value), out); return; case id(): transformIf(static_cast(value), out, ExpUsage::Return); return; case id(): transformUnless(static_cast(value), out, ExpUsage::Return); return; } } if (auto chainValue = singleValue->item.as()) { if (specialChainValue(chainValue) != ChainType::Common) { 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(_parser.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 id(): arg.name = _parser.toString(def->name); break; case id(): { assignSelf = true; auto selfName = static_cast(def->name.get()); switch (selfName->name->getId()) { case id(): { auto clsName = static_cast(selfName->name.get()); arg.name = _parser.toString(clsName->name); arg.assignSelf = s("self.__class."sv) + arg.name; break; } case id(): arg.name = "self.__class"sv; break; case id(): { auto sfName = static_cast(selfName->name.get()); arg.name = _parser.toString(sfName->name); arg.assignSelf = s("self."sv) + arg.name; break; } case id(): arg.name = "self"sv; break; default: assert(false); break; } break; } default: assert(false); break; } forceAddToScope(arg.name); if (def->defaultValue) { pushScope(); auto expList = toAst(arg.name, 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, const ast_sel& invoke = {}) { auto x = selfName; auto name = selfName->name.get(); switch (name->getId()) { case id(): { auto clsName = static_cast(name); auto nameStr = _parser.toString(clsName->name); if (LuaKeywords.find(nameStr) != LuaKeywords.end()) { out.push_back(s("self.__class[\""sv) + nameStr + s("\"]"sv)); if (invoke) { if (auto invokePtr = invoke.as()) { invokePtr->args.push_front(toAst("self.__class"sv, x)); } else { auto invokeArgsPtr = invoke.as(); invokeArgsPtr->args.push_front(toAst("self.__class"sv, x)); } } } else { out.push_back(s("self.__class"sv) + s(invoke ? ":"sv : "."sv) + nameStr); } break; } case id(): out.push_back(s("self.__class"sv)); break; case id(): { auto sfName = static_cast(name); auto nameStr = _parser.toString(sfName->name); if (LuaKeywords.find(nameStr) != LuaKeywords.end()) { out.push_back(s("self[\""sv) + nameStr + s("\"]"sv)); if (invoke) { if (auto invokePtr = invoke.as()) { invokePtr->args.push_front(toAst("self"sv, x)); } else { auto invokeArgsPtr = invoke.as(); invokeArgsPtr->args.push_front(toAst("self"sv, x)); } } } else { out.push_back(s("self"sv) + s(invoke ? ":"sv : "."sv) + nameStr); } break; } case id(): out.push_back(s("self"sv)); break; default: assert(false); break; } } bool transformChainEndWithEOP(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { auto x = chainList.front(); if (ast_is(chainList.back())) { auto parens = x->new_ptr(); { auto chainValue = x->new_ptr(); for (auto item : chainList) { chainValue->items.push_back(item); } chainValue->items.pop_back(); auto value = x->new_ptr(); value->item.set(chainValue); auto opValue = x->new_ptr(); opValue->op.set(toAst("!="sv, x)); opValue->value.set(toAst("nil"sv, x)); auto exp = x->new_ptr(); exp->value.set(value); exp->opValues.push_back(opValue); parens->expr.set(exp); } switch (usage) { case ExpUsage::Assignment: { auto callable = x->new_ptr(); callable->item.set(parens); 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); auto assignment = x->new_ptr(); assignment->expList.set(assignList); auto assign = x->new_ptr(); assign->values.push_back(exp); assignment->action.set(assign); transformAssignment(assignment, out); break; } case ExpUsage::Return: transformParens(parens, out); out.back().insert(0, indent() + s("return "sv)); out.back().append(nlr(x)); break; default: transformParens(parens, out); break; } return true; } return false; } bool transformChainWithEOP(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { auto opIt = std::find_if(chainList.begin(), chainList.end(), [](ast_node* node) { return ast_is(node); }); if (opIt != chainList.end()) { auto x = chainList.front(); str_list temp; if (usage == ExpUsage::Closure) { temp.push_back(s("(function()"sv) + nll(x)); pushScope(); } auto partOne = x->new_ptr(); for (auto it = chainList.begin();it != opIt;++it) { partOne->items.push_back(*it); } BLOCK_START auto back = ast_cast(partOne->items.back()); BREAK_IF(!back); auto selfName = ast_cast(back->item); BREAK_IF(!selfName); if (auto sname = ast_cast(selfName->name)) { auto colonItem = x->new_ptr(); colonItem->name.set(sname->name); partOne->items.pop_back(); partOne->items.push_back(toAst("@"sv, x)); partOne->items.push_back(colonItem); break; } if (auto cname = ast_cast(selfName->name)) { auto colonItem = x->new_ptr(); colonItem->name.set(cname->name); partOne->items.pop_back(); partOne->items.push_back(toAst("@@"sv, x)); partOne->items.push_back(colonItem); break; } BLOCK_END auto objVar = singleVariableFrom(partOne); if (objVar.empty()) { objVar = getUnusedName("_obj_"sv); if (auto colonItem = ast_cast(partOne->items.back())) { auto chainValue = x->new_ptr(); chainValue->items.dup(partOne->items); chainValue->items.pop_back(); if (chainValue->items.empty()) { if (_withVars.empty()) { throw std::logic_error(_info.errorMessage("Short dot/colon syntax must be called within a with block."sv, x)); } chainValue->items.push_back(toAst(_withVars.top(), x)); } auto newObj = singleVariableFrom(chainValue); if (!newObj.empty()) { objVar = newObj; } else { auto value = x->new_ptr(); value->item.set(chainValue); auto exp = x->new_ptr(); exp->value.set(value); auto assign = x->new_ptr(); assign->values.push_back(exp); auto expListAssign = x->new_ptr(); expListAssign->expList.set(toAst(objVar, x)); expListAssign->action.set(assign); transformAssignment(expListAssign, temp); } auto dotItem = x->new_ptr(); auto name = colonItem->name.get(); if (auto keyword = ast_cast(name)) { name = keyword->name.get(); } dotItem->name.set(name); partOne->items.clear(); partOne->items.push_back(toAst(objVar, x)); partOne->items.push_back(dotItem); auto it = opIt; ++it; if (it != chainList.end() && ast_is(*it)) { if (auto invoke = ast_cast(*it)) { invoke->args.push_front(toAst(objVar, x)); } else { auto invokeArgs = static_cast(*it); invokeArgs->args.push_front(toAst(objVar, x)); } } objVar = getUnusedName("_obj_"sv); } auto value = x->new_ptr(); value->item.set(partOne); auto exp = x->new_ptr(); exp->value.set(value); auto assign = x->new_ptr(); assign->values.push_back(exp); auto expListAssign = x->new_ptr(); expListAssign->expList.set(toAst(objVar, x)); expListAssign->action.set(assign); transformAssignment(expListAssign, temp); } _buf << indent() << "if "sv << objVar << " ~= nil then"sv << nll(x); temp.push_back(clearBuf()); pushScope(); auto partTwo = x->new_ptr(); partTwo->items.push_back(toAst(objVar, x)); for (auto it = ++opIt;it != chainList.end();++it) { partTwo->items.push_back(*it); } switch (usage) { case ExpUsage::Common: transformChainValue(partTwo, temp, ExpUsage::Common); break; case ExpUsage::Assignment: { auto value = x->new_ptr(); value->item.set(partTwo); 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(assignList); assignment->action.set(assign); transformAssignment(assignment, temp); break; } case ExpUsage::Return: case ExpUsage::Closure: { auto value = x->new_ptr(); value->item.set(partTwo); auto exp = x->new_ptr(); exp->value.set(value); auto ret = x->new_ptr(); auto expListLow = x->new_ptr(); expListLow->exprs.push_back(exp); ret->valueList.set(expListLow); transformReturn(ret, temp); break; } } popScope(); temp.push_back(indent() + s("end"sv) + nlr(x)); switch (usage) { case ExpUsage::Return: temp.push_back(indent() + s("return nil"sv) + nlr(x)); break; case ExpUsage::Closure: temp.push_back(indent() + s("return nil"sv) + nlr(x)); popScope(); temp.push_back(indent() + s("end)()"sv)); break; default: break; } out.push_back(join(temp)); return true; } return false; } bool transformChainEndWithColonItem(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { if (ast_is(chainList.back())) { auto x = chainList.front(); str_list temp; switch (usage) { case ExpUsage::Assignment: temp.push_back(indent() + s("do"sv) + nll(x)); pushScope(); break; case ExpUsage::Closure: temp.push_back(s("(function()"sv) + nll(x)); pushScope(); break; default: break; } auto baseChain = x->new_ptr(); switch (chainList.front()->getId()) { case id(): case id(): if (_withVars.empty()) { throw std::logic_error(_info.errorMessage("Short dot/colon syntax must be called within a with block."sv, chainList.front())); } else { baseChain->items.push_back(toAst(_withVars.top(), 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 = _parser.toString(colonChainItem->name); auto baseVar = getUnusedName("_base_"sv); auto fnVar = getUnusedName("_fn_"sv); { 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, x)); assignment->action.set(assign); transformAssignment(assignment, temp); } { auto assign = x->new_ptr(); assign->values.push_back(toAst(baseVar + "." + funcName, x)); auto assignment = x->new_ptr(); assignment->expList.set(toAst(fnVar, x)); assignment->action.set(assign); transformAssignment(assignment, temp); } auto funLit = toAst(fnVar + s(" and (...)-> "sv) + fnVar + s(" "sv) + baseVar + s(", ..."sv), x); switch (usage) { case ExpUsage::Closure: case ExpUsage::Return: { auto returnNode = x->new_ptr(); auto expListLow = x->new_ptr(); expListLow->exprs.push_back(funLit); returnNode->valueList.set(expListLow); transformReturn(returnNode, temp); break; } case ExpUsage::Assignment: { auto assign = x->new_ptr(); assign->values.push_back(funLit); auto assignment = x->new_ptr(); assignment->expList.set(assignList); assignment->action.set(assign); transformAssignment(assignment, temp); break; } default: break; } switch (usage) { case ExpUsage::Assignment: popScope(); temp.push_back(indent() + s("end"sv) + nlr(x)); break; case ExpUsage::Closure: popScope(); temp.push_back(indent() + s("end)()"sv)); break; default: break; } out.push_back(join(temp)); return true; } return false; } void transformChainList(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { auto x = chainList.front(); str_list temp; switch (x->getId()) { case id(): case id(): if (_withVars.empty()) { throw std::logic_error(_info.errorMessage("Short dot/colon syntax must be called within a with block."sv, x)); } else { temp.push_back(_withVars.top()); } break; } for (auto it = chainList.begin(); it != chainList.end(); ++it) { auto item = *it; switch (item->getId()) { case id(): transformInvoke(static_cast(item), temp); break; case id(): transformDotChainItem(static_cast(item), temp); break; case id(): { auto colonItem = static_cast(item); auto current = it; auto next = current; ++next; auto followItem = next != chainList.end() ? *next : nullptr; if (current != chainList.begin()) { --current; if (!ast_is(*current)) { ++current; } } if (ast_is(followItem)) { ++next; followItem = next != chainList.end() ? *next : nullptr; --next; } if (!ast_is(followItem)) { throw std::logic_error(_info.errorMessage("Colon chain item must be followed by invoke arguments."sv, colonItem)); } if (colonItem->name.is()) { std::string callVar; auto block = x->new_ptr(); { auto chainValue = x->new_ptr(); switch (chainList.front()->getId()) { case id(): case id(): chainValue->items.push_back(toAst(_withVars.top(), x)); break; } for (auto i = chainList.begin(); i != current; ++i) { chainValue->items.push_back(*i); } auto value = x->new_ptr(); value->item.set(chainValue); auto exp = x->new_ptr(); exp->value.set(value); callVar = singleVariableFrom(exp); if (callVar.empty()) { callVar = getUnusedName(s("_call_"sv)); auto assignment = x->new_ptr(); assignment->expList.set(toAst(callVar, 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 = _parser.toString(colonItem->name); auto chainValue = x->new_ptr(); chainValue->items.push_back(toAst(callVar, x)); if (ast_is(*current)) { chainValue->items.push_back(x->new_ptr()); } chainValue->items.push_back(toAst(s("\""sv) + name + s("\""sv), x)); if (auto invoke = ast_cast(followItem)) { invoke->args.push_front(toAst(callVar, x)); } else { auto invokeArgs = static_cast(followItem); invokeArgs->args.push_front(toAst(callVar, x)); } 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: case ExpUsage::Return: transformBlock(block, out, usage); return; case ExpUsage::Assignment: { transformBlock(block, out, ExpUsage::Assignment, assignList); return; } default: break; } auto body = x->new_ptr(); body->content.set(block); auto funLit = toAst("->"sv, 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, ExpUsage::Closure); return; } transformColonChainItem(colonItem, temp); break; } case id(): transformSlice(static_cast(item), temp); break; case id(): { auto next = it; ++next; auto followItem = next != chainList.end() ? *next : nullptr; ast_sel invoke; if (ast_is(followItem)) { invoke.set(followItem); } transformCallable(static_cast(item), temp, invoke); break; } case id(): transformString(static_cast(item), temp); temp.back() = s("("sv) + temp.back() + s(")"sv); break; case id(): transformExp(static_cast(item), temp, ExpUsage::Closure); temp.back() = s("["sv) + temp.back() + s("]"sv); break; case id(): transformInvokeArgs(static_cast(item), temp); break; default: assert(false); 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, ExpList_t* assignList = nullptr) { const auto& chainList = chainValue->items.objects(); if (transformChainEndWithColonItem(chainList, out, usage, assignList)) { return; } if (transformChainEndWithEOP(chainList, out, usage, assignList)) { return; } if (transformChainWithEOP(chainList, out, usage, assignList)) { return; } transformChainList(chainList, 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 = _parser.toString(dotChainItem->name); if (Keywords.find(name) != 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 = _parser.toString(colonChainItem->name); out.push_back(s(colonChainItem->switchToDot ? "."sv : ":"sv) + name); } void transformSlice(Slice_t* slice, str_list&) { throw std::logic_error(_info.errorMessage("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 id(): transformExp(static_cast(arg), temp, ExpUsage::Closure); break; case id(): transformSingleString(static_cast(arg), temp); break; case id(): transformDoubleString(static_cast(arg), temp); break; case id(): transformLuaString(static_cast(arg), temp); break; default: assert(false); 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 = _parser.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, ExpUsage::Closure); out.push_back(join(temp)); } void transformVariable(Variable_t* name, str_list& out) { out.push_back(_parser.toString(name)); } void transformNum(Num_t* num, str_list& out) { out.push_back(_parser.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 id(): transformCompForEach(static_cast(item), temp); break; case id(): transformCompFor(static_cast(item), temp); break; case id(): transformExp(static_cast(item), temp, ExpUsage::Closure); temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); pushScope(); break; default: assert(false); 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, ExpUsage usage, ExpList_t* assignList = nullptr) { auto x = comp; switch (usage) { case ExpUsage::Closure: case ExpUsage::Assignment: pushScope(); break; default: break; } str_list temp; std::string accumVar = getUnusedName("_accum_"sv); std::string lenVar = getUnusedName("_len_"sv); addToScope(accumVar); addToScope(lenVar); auto compInner = comp->forLoop.get(); for (auto item : compInner->items.objects()) { switch (item->getId()) { case id(): transformCompForEach(static_cast(item), temp); break; case id(): transformCompFor(static_cast(item), temp); break; case id(): transformExp(static_cast(item), temp, ExpUsage::Closure); temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); pushScope(); break; default: assert(false); break; } } { auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), x); auto assign = x->new_ptr(); assign->values.push_back(comp->value); auto assignment = x->new_ptr(); assignment->expList.set(assignLeft); assignment->action.set(assign); transformAssignment(assignment, temp); } auto assignStr = temp.back(); temp.pop_back(); for (size_t i = 0; i < compInner->items.objects().size(); ++i) { popScope(); } _buf << indent() << "local "sv << accumVar << " = { }"sv << nll(comp); _buf << indent() << "local "sv << lenVar << " = 1"sv << nll(comp); _buf << join(temp); _buf << assignStr; _buf << indent(int(temp.size())) << lenVar << " = "sv << lenVar << " + 1"sv << nll(comp); for (int ind = int(temp.size()) - 1; ind > -1; --ind) { _buf << indent(ind) << "end"sv << nll(comp); } switch (usage) { case ExpUsage::Common: break; case ExpUsage::Closure: { out.push_back(clearBuf()); out.back().append(indent() + s("return "sv) + accumVar + nlr(comp)); popScope(); out.back().insert(0, s("(function()"sv) + nll(comp)); out.back().append(indent() + s("end)()"sv)); break; } case ExpUsage::Assignment: { out.push_back(clearBuf()); auto assign = x->new_ptr(); assign->values.push_back(toAst(accumVar, x)); auto assignment = x->new_ptr(); assignment->expList.set(assignList); assignment->action.set(assign); transformAssignment(assignment, temp); popScope(); out.back() = indent() + s("do"sv) + nll(comp) + out.back() + temp.back() + indent() + s("end"sv) + nlr(comp); break; } case ExpUsage::Return: out.push_back(clearBuf()); out.back().append(indent() + s("return "sv) + accumVar + nlr(comp)); break; default: break; } } 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 id(): transformVariable(static_cast(item), vars); varAfter.push_back(vars.back()); break; case id(): { auto desVar = getUnusedName("_des_"sv); destructPairs.emplace_back(item, toAst(desVar, x)); vars.push_back(desVar); varAfter.push_back(desVar); break; } default: assert(false); break; } } switch (loopTarget->getId()) { case id(): { auto star_exp = static_cast(loopTarget); auto listVar = singleVariableFrom(star_exp->value); auto indexVar = getUnusedName("_index_"sv); varAfter.push_back(indexVar); auto value = singleValueFrom(star_exp->value); if (!value) throw std::logic_error(_info.errorMessage("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); 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, ExpUsage::Closure); startValue = temp.back(); temp.pop_back(); } std::string stopValue; if (auto exp = slice->stopValue.as()) { transformExp(exp, temp, ExpUsage::Closure); stopValue = temp.back(); temp.pop_back(); } std::string stepValue; if (auto exp = slice->stepValue.as()) { transformExp(exp, temp, ExpUsage::Closure); stepValue = temp.back(); temp.pop_back(); } if (listVar.empty()) { listVar = getUnusedName("_list_"sv); varBefore.push_back(listVar); transformChainValue(chain, temp, ExpUsage::Closure); _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); } std::string maxVar; if (!stopValue.empty()) { maxVar = getUnusedName("_max_"sv); 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 << " + "sv << 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_"sv); varBefore.push_back(listVar); } if (!endWithSlice) { transformExp(star_exp->value, temp, ExpUsage::Closure); 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 id(): transformExp(static_cast(loopTarget), temp, ExpUsage::Closure); _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); out.push_back(clearBuf()); break; case 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: assert(false); 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 id(): transformExp(static_cast(arg), temp, ExpUsage::Closure); break; case id(): transformTableBlock(static_cast(arg), temp); break; default: assert(false); 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 = _parser.toString(forNode->varName); transformExp(forNode->startValue, temp, ExpUsage::Closure); transformExp(forNode->stopValue, temp, ExpUsage::Closure); if (forNode->stepValue) { transformExp(forNode->stepValue->value, temp, ExpUsage::Closure); } 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, ExpUsage usage, ExpList_t* assignList = nullptr) { str_list temp; bool withContinue = traversal::Stop == body->traverse([&](ast_node* node) { switch (node->getId()) { case id(): case id(): return traversal::Return; case id(): { return _parser.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, usage, assignList); 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, ExpUsage::Common); 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_"sv); addToScope(accum); std::string len = getUnusedName("_len_"sv); 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), x); auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forNode->body); transformLoopBody(forNode->body, out, lenLine, ExpUsage::Assignment, expList); 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, 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, x); returnNode->valueList.set(expListLow); transformReturn(returnNode, temp); } out.push_back(join(temp)); } void transformBinaryOperator(BinaryOperator_t* node, str_list& out) { auto op = _parser.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, ExpUsage::Common); 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_"sv); addToScope(accum); std::string len = getUnusedName("_len_"sv); 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), x); auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forEach->body); transformLoopBody(forEach->body, out, lenLine, ExpUsage::Assignment, expList); 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, 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, 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 = _parser.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 id(): { transformKeyName(static_cast(key), temp); if (LuaKeywords.find(temp.back()) != LuaKeywords.end()) { temp.back() = s("[\""sv) + temp.back() + s("\"]"); } break; } case id(): transformExp(static_cast(key), temp, ExpUsage::Closure); temp.back() = s("["sv) + temp.back() + s("]"sv); break; case id(): transformDoubleString(static_cast(key), temp); temp.back() = s("["sv) + temp.back() + s("]"sv); break; case id(): transformSingleString(static_cast(key), temp); temp.back() = s("["sv) + temp.back() + s("]"sv); break; default: assert(false); break; } auto value = pair->value.get(); switch (value->getId()) { case id(): transformExp(static_cast(value), temp, ExpUsage::Closure); break; case id(): transformTableBlock(static_cast(value), temp); break; default: assert(false); 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 id(): transformSelfName(static_cast(name), out); break; case id(): out.push_back(_parser.toString(name)); break; default: assert(false); break; } } void transformLuaString(LuaString_t* luaString, str_list& out) { auto content = _parser.toString(luaString->content); Utils::replace(content, "\r"sv, ""); if (content[0] == '\n') content.erase(content.begin()); out.push_back(_parser.toString(luaString->open) + content + _parser.toString(luaString->close)); } void transformSingleString(SingleString_t* singleString, str_list& out) { auto str = _parser.toString(singleString); Utils::replace(str, "\r"sv, ""); Utils::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 id(): { auto str = _parser.toString(content); Utils::replace(str, "\r"sv, ""); Utils::replace(str, "\n"sv, "\\n"sv); temp.push_back(s("\""sv) + str + s("\""sv)); break; } case id(): transformExp(static_cast(content), temp, ExpUsage::Closure); temp.back() = s("tostring("sv) + temp.back() + s(")"sv); break; default: assert(false); 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 id(): transformSingleString(static_cast(str), out); break; case id(): transformDoubleString(static_cast(str), out); break; case id(): transformLuaString(static_cast(str), out); break; default: assert(false); break; } } std::pair defineClassVariable(Assignable_t* assignable) { if (auto variable = assignable->item.as()) { auto name = _parser.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, 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(_info.errorMessage("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) + _parser.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, true); 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 "sv) + join(varDefs, ", "sv) + nll(body)); } } std::string parent, parentVar; if (extend) { parentVar = getUnusedName("_parent_"sv); addToScope(parentVar); transformExp(extend, temp, ExpUsage::Closure); 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 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 id(): transformStatement(static_cast(content), statements); break; default: assert(false); 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, 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({"sv << 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 "sv << 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(_info.errorMessage("Invalid class poperty name."sv, selfName->name)); newSuperCall = classVar + s(".__parent."sv) + _parser.toString(name->name); } else { auto x = keyName; auto nameNode = keyName->name.as(); if (!nameNode) break; auto name = _parser.toString(nameNode); if (name == "new"sv) { type = MemType::Builtin; keyName->name.set(toAst("__init"sv, 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() == id()) return traversal::Return; if (auto chainValue = ast_cast(node)) { if (auto callable = ast_cast(chainValue->items.front())) { auto var = callable->item.get(); if (_parser.toString(var) == "super"sv) { auto insertSelfToArguments = [&](ast_node* item) { auto x = item; switch (item->getId()) { case id(): { auto invoke = static_cast(item); invoke->args.push_front(toAst("self"sv, x)); return true; } case id(): { auto invoke = static_cast(item); invoke->args.push_front(toAst("self"sv, 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->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 id(): transform_variable_pair(static_cast(keyValue), temp); break; case id(): transform_normal_pair(static_cast(keyValue), temp); break; default: assert(false); 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 id(): transformAssignableChain(static_cast(item), out); break; case id(): transformVariable(static_cast(item), out); break; case id(): transformSelfName(static_cast(item), out); break; default: assert(false); 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_"sv); auto assignment = x->new_ptr(); assignment->expList.set(toAst(withVar, 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, 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_"sv); auto assignment = x->new_ptr(); assignment->expList.set(toAst(withVar, 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, true); 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(_parser.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, ExpUsage::Common); _withVars.pop(); if (assignList) { auto assignment = x->new_ptr(); assignment->expList.set(assignList); auto assign = x->new_ptr(); assign->values.push_back(toAst(withVar, x)); assignment->action.set(assign); transformAssignment(assignment, temp); } if (returnValue) { auto last = lastStatementFrom(with->body); if (last && !last->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(_parser.toString(const_value)); } void transformGlobal(Global_t* global, str_list& out) { auto x = global; auto item = global->item.get(); switch (item->getId()) { case id(): { auto classDecl = static_cast(item); if (classDecl->name && classDecl->name->item->getId() == id()) { markVarGlobal(GlobalMode::Any, true); addGlobalVar(_parser.toString(classDecl->name->item)); } transformClassDecl(classDecl, out, ExpUsage::Common); break; } case id(): if (_parser.toString(item) == "*"sv) { markVarGlobal(GlobalMode::Any, false); } else { markVarGlobal(GlobalMode::Capital, false); } break; case id(): { markVarGlobal(GlobalMode::Any, true); auto values = global->item.to(); if (values->valueList) { auto expList = x->new_ptr(); for (auto name : values->nameList->names.objects()) { addGlobalVar(_parser.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()) { addGlobalVar(_parser.toString(name)); } } break; } default: assert(false); break; } } void transformExport(Export_t* exportNode, str_list& out) { auto x = exportNode; if (_scopes.size() > 1) { throw std::logic_error(_info.errorMessage("Can not do module export outside root block."sv, x)); } if (exportNode->assign) { auto expList = exportNode->target.to(); if (expList->exprs.size() != exportNode->assign->values.size()) { throw std::logic_error(_info.errorMessage("Left and right expressions must be matched in export statement."sv, x)); } for (auto _exp : expList->exprs.objects()) { auto exp = static_cast(_exp); if (!variableFrom(exp) && !exp->getByPath() && !exp->getByPath()) { throw std::logic_error(_info.errorMessage("Left hand expressions must be variables in export statement."sv, x)); } } auto assignment = x->new_ptr(); assignment->expList.set(expList); assignment->action.set(exportNode->assign); transformAssignment(assignment, out); str_list names = transformAssignDefs(expList, false); auto info = extractDestructureInfo(assignment, true); if (!info.first.empty()) { for (const auto& destruct : info.first) for (const auto& item : destruct.items) if (item.isVariable) names.push_back(item.name); } if (_info.exportDefault) { out.back().append(indent() + _info.moduleName + s(" = "sv) + names.back() + nlr(exportNode)); } else { str_list lefts, rights; for (const auto& name : names) { lefts.push_back(_info.moduleName + s("[\""sv) + name + s("\"]"sv)); rights.push_back(name); } out.back().append(indent() + join(lefts,", "sv) + s(" = "sv) + join(rights, ", "sv) + nlr(exportNode)); } } else { if (_info.exportDefault) { auto exp = exportNode->target.to(); auto assignment = x->new_ptr(); assignment->expList.set(toAst(_info.moduleName, x)); auto assign = x->new_ptr(); assign->values.push_back(exp); assignment->action.set(assign); transformAssignment(assignment, out); } else { str_list temp; auto expList = exportNode->target.to(); auto assignment = x->new_ptr(); auto assignList = toAst(_info.moduleName + s("[#"sv) + _info.moduleName + s("+1]"sv), x); assignment->expList.set(assignList); for (auto exp : expList->exprs.objects()) { if (auto classDecl = exp->getByPath()) { if (classDecl->name && classDecl->name->item->getId() == id()) { transformClassDecl(classDecl, temp, ExpUsage::Common); auto name = _parser.toString(classDecl->name->item); assignment->expList.set(toAst(_info.moduleName + s("[\""sv) + name + s("\"]"sv), x)); auto assign = x->new_ptr(); assign->values.push_back(toAst(name, x)); assignment->action.set(assign); transformAssignment(assignment, temp); assignment->expList.set(assignList); continue; } } auto assign = x->new_ptr(); assign->values.push_back(exp); assignment->action.set(assign); transformAssignment(assignment, temp); } out.push_back(join(temp)); } } } 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 id(): transformExp(static_cast(pair), temp, ExpUsage::Closure); break; case id(): transform_variable_pair(static_cast(pair), temp); break; case id(): transform_normal_pair(static_cast(pair), temp); break; default: assert(false); 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, ExpUsage usage, ExpList_t* assignList = nullptr) { switch (usage) { case ExpUsage::Closure: case ExpUsage::Assignment: pushScope(); break; default: break; } auto x = comp; str_list kv; std::string tbl = getUnusedName("_tbl_"sv); addToScope(tbl); str_list temp; auto compInner = comp->forLoop.get(); for (auto item : compInner->items.objects()) { switch (item->getId()) { case id(): transformCompForEach(static_cast(item), temp); break; case id(): transformCompFor(static_cast(item), temp); break; case id(): transformExp(static_cast(item), temp, ExpUsage::Closure); temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); pushScope(); break; default: assert(false); break; } } transformExp(comp->key, kv, ExpUsage::Closure); if (comp->value) { transformExp(comp->value->value, kv, ExpUsage::Closure); } 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_"sv); auto valVar = getUnusedName("_val_"sv); _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); switch (usage) { case ExpUsage::Closure: out.push_back(clearBuf() + indent() + s("return "sv) + tbl + nlr(comp)); popScope(); out.back().insert(0, s("(function()"sv) + nll(comp)); out.back().append(indent() + s("end)()"sv)); break; case ExpUsage::Assignment: { out.push_back(clearBuf()); auto assign = x->new_ptr(); assign->values.push_back(toAst(tbl, x)); auto assignment = x->new_ptr(); assignment->expList.set(assignList); assignment->action.set(assign); transformAssignment(assignment, temp); out.back().append(temp.back()); popScope(); out.back().insert(0, indent() + s("do"sv) + nll(comp)); out.back().append(indent() + s("end"sv) + nlr(comp)); break; } case ExpUsage::Return: out.push_back(clearBuf() + indent() + s("return "sv) + tbl + nlr(comp)); break; default: break; } } void transformCompFor(CompFor_t* comp, str_list& out) { str_list temp; std::string varName = _parser.toString(comp->varName); transformExp(comp->startValue, temp, ExpUsage::Closure); transformExp(comp->stopValue, temp, ExpUsage::Closure); if (comp->stepValue) { transformExp(comp->stepValue->value, temp, ExpUsage::Closure); } 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, ExpUsage usage, ExpList_t* assignList = nullptr) { str_list temp; if (usage == ExpUsage::Closure) { temp.push_back(s("(function()"sv) + nll(doNode)); } else { temp.push_back(indent() + s("do"sv) + nll(doNode)); } pushScope(); transformBody(doNode->body, temp, usage, assignList); popScope(); if (usage == ExpUsage::Closure) { temp.push_back(indent() + s("end)()"sv)); } else { temp.push_back(indent() + s("end"sv) + nlr(doNode)); } out.push_back(join(temp)); } void transformImportFrom(ImportFrom_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, 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 id(): { auto var = ast_to(name); { auto callable = toAst(objVar, 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 id(): { auto var = static_cast(name)->name.get(); { auto nameNode = var->name.get(); auto callable = toAst(objVar, 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; } default: assert(false); 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 transformImportAs(ImportAs_t* import, str_list& out) { auto x = import; if (!import->target) { auto name = _parser.toString(import->literal->inners.back()); Utils::replace(name, "-"sv, "_"sv); Utils::replace(name, " "sv, "_"sv); import->target.set(toAst(name, x)); } auto target = import->target.get(); auto value = x->new_ptr(); if (auto var = ast_cast(target)) { auto callable = x->new_ptr(); callable->item.set(var); auto chainValue = x->new_ptr(); chainValue->items.push_back(callable); value->item.set(chainValue); } else { auto tableLit = ast_to(target); auto simpleValue = x->new_ptr(); simpleValue->value.set(tableLit); value->item.set(simpleValue); } auto exp = x->new_ptr(); exp->value.set(value); auto assignList = x->new_ptr(); assignList->exprs.push_back(exp); auto assign = x->new_ptr(); assign->values.push_back(toAst(s("require "sv) + _parser.toString(import->literal), x)); auto assignment = x->new_ptr(); assignment->expList.set(assignList); assignment->action.set(assign); transformAssignment(assignment, out); } void transformImport(Import_t* import, str_list& out) { auto content = import->content.get(); switch (content->getId()) { case id(): transformImportAs(static_cast(content), out); break; case id(): transformImportFrom(static_cast(content), out); break; default: assert(false); break; } } 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, ExpUsage::Closure); temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); pushScope(); auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), x); auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); popScope(); temp.push_back(indent() + s("end"sv) + nlr(whileNode)); if (expList) { auto assign = x->new_ptr(); assign->values.push_back(toAst(accumVar, 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, ExpUsage::Closure); temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); pushScope(); auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), x); auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); 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, ExpUsage::Closure); transformLoopBody(whileNode->body, temp, Empty, ExpUsage::Common); 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 transformSwitch(Switch_t* switchNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { str_list temp; if (usage == ExpUsage::Closure) { temp.push_back(s("(function()"sv) + nll(switchNode)); pushScope(); } auto objVar = singleVariableFrom(switchNode->target); if (objVar.empty()) { objVar = getUnusedName("_exp_"sv); addToScope(objVar); transformExp(switchNode->target, temp, ExpUsage::Closure); _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, ExpUsage::Closure); 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, usage, assignList); popScope(); } if (switchNode->lastBranch) { temp.push_back(indent() + s("else"sv) + nll(switchNode->lastBranch)); pushScope(); transformBody(switchNode->lastBranch, temp, usage, assignList); popScope(); } temp.push_back(indent() + s("end"sv) + nlr(switchNode)); if (usage == ExpUsage::Closure) { popScope(); temp.push_back(indent() + s("end)()"sv)); } 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 = _parser.toString(breakLoop); if (keyword == "break"sv) { out.push_back(indent() + keyword + nll(breakLoop)); return; } if (_continueVars.empty()) throw std::logic_error(_info.errorMessage("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 MoonCompilerImpl::Empty; MoonCompiler::MoonCompiler(): _compiler(std::make_unique()) { } MoonCompiler::~MoonCompiler() { } std::tuple MoonCompiler::compile(std::string_view codes, const MoonConfig& config) { return _compiler->compile(codes, config); } } // namespace MoonP