From 975167856ed0b11c2ede03c6eb750ca4e4a6a7fc Mon Sep 17 00:00:00 2001 From: Li Jin Date: Tue, 29 Oct 2019 11:25:27 +0800 Subject: complete moon compiler in C++. --- MoonParser.xcodeproj/project.pbxproj | 16 +- MoonParser/ast.cpp | 85 +- MoonParser/ast.hpp | 194 +- MoonParser/moon_ast.cpp | 3688 +------------------------------- MoonParser/moon_ast.h | 418 ++-- MoonParser/moon_compiler.cpp | 3851 ++++++++++++++++++++++++++++++++++ MoonParser/moon_compiler.h | 18 + MoonParser/moon_parser.cpp | 31 +- MoonParser/moon_parser.h | 6 +- MoonParser/moonc.cpp | 21 + MoonParser/parser.hpp | 13 +- MoonParser/parserlib.hpp | 8 - 12 files changed, 4276 insertions(+), 4073 deletions(-) create mode 100644 MoonParser/moon_compiler.cpp create mode 100644 MoonParser/moon_compiler.h create mode 100644 MoonParser/moonc.cpp delete mode 100644 MoonParser/parserlib.hpp diff --git a/MoonParser.xcodeproj/project.pbxproj b/MoonParser.xcodeproj/project.pbxproj index db93034..c9744d1 100644 --- a/MoonParser.xcodeproj/project.pbxproj +++ b/MoonParser.xcodeproj/project.pbxproj @@ -7,7 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 3C1C21CD2367E69100147A19 /* moon_compiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C1C21CC2367E69100147A19 /* moon_compiler.cpp */; }; 3C3B1C601F1EF0AB0052A206 /* moon_ast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C3B1C5E1F1EF0AB0052A206 /* moon_ast.cpp */; }; + 3C54E26C234C80CC00202423 /* moonc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C54E26B234C80CC00202423 /* moonc.cpp */; }; 3C6FFA331F16F97400541BCA /* ast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C6FFA2D1F16F97400541BCA /* ast.cpp */; }; 3C6FFA341F16F97400541BCA /* moon_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C6FFA2F1F16F97400541BCA /* moon_parser.cpp */; }; 3C6FFA351F16F97400541BCA /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3C6FFA301F16F97400541BCA /* parser.cpp */; }; @@ -27,15 +29,17 @@ /* Begin PBXFileReference section */ 3C0F0F6C1EF3781E000EADDB /* MoonParser */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MoonParser; sourceTree = BUILT_PRODUCTS_DIR; }; + 3C1C21CB2367E69100147A19 /* moon_compiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = moon_compiler.h; sourceTree = ""; }; + 3C1C21CC2367E69100147A19 /* moon_compiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moon_compiler.cpp; sourceTree = ""; }; 3C3B1C5E1F1EF0AB0052A206 /* moon_ast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moon_ast.cpp; sourceTree = ""; }; 3C3B1C5F1F1EF0AB0052A206 /* moon_ast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = moon_ast.h; sourceTree = ""; }; 3C3B1C611F1EFC810052A206 /* moon_parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = moon_parser.h; sourceTree = ""; }; + 3C54E26B234C80CC00202423 /* moonc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = moonc.cpp; sourceTree = ""; }; 3C6FFA2D1F16F97400541BCA /* ast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ast.cpp; sourceTree = ""; }; 3C6FFA2E1F16F97400541BCA /* ast.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ast.hpp; sourceTree = ""; }; 3C6FFA2F1F16F97400541BCA /* moon_parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moon_parser.cpp; sourceTree = ""; }; 3C6FFA301F16F97400541BCA /* parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parser.cpp; sourceTree = ""; }; 3C6FFA311F16F97400541BCA /* parser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = parser.hpp; sourceTree = ""; }; - 3C6FFA321F16F97400541BCA /* parserlib.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = parserlib.hpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,13 +58,15 @@ children = ( 3C6FFA2D1F16F97400541BCA /* ast.cpp */, 3C6FFA2E1F16F97400541BCA /* ast.hpp */, + 3C6FFA301F16F97400541BCA /* parser.cpp */, + 3C6FFA311F16F97400541BCA /* parser.hpp */, 3C6FFA2F1F16F97400541BCA /* moon_parser.cpp */, 3C3B1C611F1EFC810052A206 /* moon_parser.h */, 3C3B1C5E1F1EF0AB0052A206 /* moon_ast.cpp */, 3C3B1C5F1F1EF0AB0052A206 /* moon_ast.h */, - 3C6FFA301F16F97400541BCA /* parser.cpp */, - 3C6FFA311F16F97400541BCA /* parser.hpp */, - 3C6FFA321F16F97400541BCA /* parserlib.hpp */, + 3C1C21CC2367E69100147A19 /* moon_compiler.cpp */, + 3C1C21CB2367E69100147A19 /* moon_compiler.h */, + 3C54E26B234C80CC00202423 /* moonc.cpp */, ); path = MoonParser; sourceTree = ""; @@ -141,8 +147,10 @@ files = ( 3C6FFA341F16F97400541BCA /* moon_parser.cpp in Sources */, 3C3B1C601F1EF0AB0052A206 /* moon_ast.cpp in Sources */, + 3C54E26C234C80CC00202423 /* moonc.cpp in Sources */, 3C6FFA351F16F97400541BCA /* parser.cpp in Sources */, 3C6FFA331F16F97400541BCA /* ast.cpp in Sources */, + 3C1C21CD2367E69100147A19 /* moon_compiler.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MoonParser/ast.cpp b/MoonParser/ast.cpp index 7f46bd0..6c86854 100644 --- a/MoonParser/ast.cpp +++ b/MoonParser/ast.cpp @@ -4,10 +4,6 @@ namespace parserlib { - -//current AST container. -static ast_container* _current = 0; - int ast_type_id = 0; traversal ast_node::traverse(const std::function& func) { @@ -42,12 +38,6 @@ bool ast_node::visitChild(const std::function&) { return false; } -/** sets the container under construction to be this. - */ -ast_container::ast_container() { - _current = this; -} - /** Asks all members to construct themselves from the stack. The members are asked to construct themselves in reverse order. @@ -106,71 +96,6 @@ bool ast_container::visitChild(const std::function& func) { return false; } -ast_node* ast_container::getChild(int index) const { - int i = 0; - const auto& members = this->members(); - for (auto member : members) { - if (_ast_ptr* ptr = ast_cast<_ast_ptr>(member)) { - if (i == index) return ptr->get(); - } else if (_ast_list* list = ast_cast<_ast_list>(member)) { - for (auto obj : list->objects()) { - if (i == index) return obj; - } - } - i++; - } - return nullptr; -} - -ast_node* ast_container::getFirstChild() const { - const auto& members = this->members(); - if (!members.empty()) { - auto member = members.front(); - if (_ast_ptr* ptr = ast_cast<_ast_ptr>(member)) { - return ptr->get(); - } else if (_ast_list* list = ast_cast<_ast_list>(member)) { - if (!list->objects().empty()) { - return list->objects().front(); - } - } - } - return nullptr; -} - -ast_node* ast_container::getLastChild() const { - const auto& members = this->members(); - if (!members.empty()) { - auto member = members.back(); - if (_ast_ptr* ptr = ast_cast<_ast_ptr>(member)) { - return ptr->get(); - } else if (_ast_list* list = ast_cast<_ast_list>(member)) { - if (!list->objects().empty()) { - return list->objects().front(); - } - } - } - return nullptr; -} - -size_t ast_container::getChildCount() const { - size_t count = 0; - const auto& members = this->members(); - for (auto member : members) { - if (_ast_ptr* ptr = ast_cast<_ast_ptr>(member)) { - count += 1; - } else if (_ast_list* list = ast_cast<_ast_list>(member)) { - count += list->objects().size(); - } - } - return count; -} - -//register the AST member to the current container. -void ast_member::add_to_owner() { - assert(_current); - _current->m_members.push_back(this); -} - /** parses the given input. @param i input. @@ -182,9 +107,15 @@ void ast_member::add_to_owner() { */ ast_node* _parse(input &i, rule &g, error_list &el, void* ud) { ast_stack st; - if (!parse(i, g, el, &st, ud)) return 0; + if (!parse(i, g, el, &st, ud)) { + for (auto node : st) { + delete node; + } + st.clear(); + return nullptr; + } assert(st.size() == 1); - return st[0]; + return st.front(); } diff --git a/MoonParser/ast.hpp b/MoonParser/ast.hpp index 8d28177..b388e77 100644 --- a/MoonParser/ast.hpp +++ b/MoonParser/ast.hpp @@ -1,5 +1,4 @@ -#ifndef AST_HPP -#define AST_HPP +#pragma once #include @@ -13,14 +12,15 @@ namespace parserlib { class ast_node; -template class ast_ptr; -template class ast_list; +template class ast_ptr; +template class ast_list; template class ast; /** type of AST node stack. */ typedef std::vector ast_stack; +typedef std::list node_container; extern int ast_type_id; @@ -78,26 +78,22 @@ public: virtual bool visitChild(const std::function& func); - virtual ast_node* getChild(int) const { return nullptr; } - - virtual size_t getChildCount() const { return 0; } - - virtual ast_node* getFirstChild() const { return nullptr; } - - virtual ast_node* getLastChild() const { return nullptr; } - virtual size_t getId() const { return "ast_node"_id; } virtual const char* getName() const { return "ast_node"; } virtual int get_type() { return ast_type(); } + + template + inline ast_ptr new_ptr() { + auto item = new T; + item->m_begin.m_line = m_begin.m_line; + item->m_end.m_line = m_begin.m_line; + return ast_ptr(item); + } private: int _ref; ast_node* getByTypeIds(int* begin, int* end); - template friend class ast_ptr; - template friend class ast_choice; - template friend class ast_list; - template friend class ast; }; template @@ -132,9 +128,11 @@ typedef std::vector ast_member_vector; */ class ast_container : public ast_node { public: - /** sets the container under construction to be this. - */ - ast_container(); + void add_members(std::initializer_list members) { + for (auto member : members) { + m_members.push_back(member); + } + } /** returns the vector of AST members. @return the vector of AST members. @@ -154,14 +152,6 @@ public: virtual bool visitChild(const std::function& func) override; - virtual ast_node* getChild(int index) const override; - - virtual size_t getChildCount() const override; - - virtual ast_node* getFirstChild() const override; - - virtual ast_node* getLastChild() const override; - virtual size_t getId() const override { return "ast_container"_id; } virtual const char* getName() const override { return "ast_container"; } @@ -176,12 +166,6 @@ private: */ class ast_member { public: - /** automatically registers itself to the container under construction. - */ - ast_member(bool is_member) { - if (is_member) add_to_owner(); - } - virtual ~ast_member() {} /** interface for filling the the member from a node stack. @@ -192,9 +176,6 @@ public: virtual bool accept(ast_node* node) = 0; virtual int get_type() { return ast_type(); } -private: - //register the AST member to the current container. - void add_to_owner(); }; template @@ -204,7 +185,7 @@ T* ast_cast(ast_member* member) { class _ast_ptr : public ast_member { public: - _ast_ptr(ast_node* node, bool is_member) : ast_member(is_member), m_ptr(node) { + _ast_ptr(ast_node* node) : m_ptr(node) { if (node) node->retain(); } @@ -226,13 +207,13 @@ public: template T* to() const { - assert(m_ptr->get_type() == ast_type()); + assert(m_ptr && m_ptr->get_type() == ast_type()); return static_cast(m_ptr); } template bool is() const { - return m_ptr->get_type() == ast_type(); + return m_ptr && m_ptr->get_type() == ast_type(); } void set(ast_node* node) { @@ -259,16 +240,16 @@ protected: /** pointer to an AST object. It assumes ownership of the object. It pops an object of the given type from the stack. + @tparam Required if true, the object is required. @tparam T type of object to control. - @tparam OPT if true, the object becomes optional. */ -template class ast_ptr : public _ast_ptr { +template class ast_ptr : public _ast_ptr { public: - ast_ptr(T* node = nullptr) : _ast_ptr(node, MEM) {} + ast_ptr(T* node = nullptr) : _ast_ptr(node) {} - ast_ptr(const ast_ptr& other) : _ast_ptr(other.get(), MEM) {} + ast_ptr(const ast_ptr& other) : _ast_ptr(other.get()) {} - ast_ptr& operator=(const ast_ptr& other) { + ast_ptr& operator=(const ast_ptr& other) { set(other.get()); return *this; } @@ -298,28 +279,22 @@ public: /** Pops a node from the stack. @param st stack. @exception std::logic_error thrown if the node is not of the appropriate type; - thrown only if OPT == false or if the stack is empty. + thrown only if Required == true or if the stack is empty. */ virtual void construct(ast_stack& st) override { - //check the stack node + // check the stack node if (st.empty()) { - if (OPT) return; - else throw std::logic_error("invalid AST stack"); + if (!Required) return; + throw std::logic_error("Invalid AST stack"); } - ast_node* node = st.back(); - if (!ast_ptr::accept(node)) { - //if the object is optional, simply return - if (OPT) { - return; - } else { //else if the object is mandatory, throw an exception - throw std::logic_error("invalid AST node"); - } + // if the object is not required, simply return + if (!Required) return; + // else if the object is mandatory, throw an exception + throw std::logic_error("Invalid AST node"); } - st.pop_back(); - m_ptr = node; node->retain(); } @@ -329,18 +304,13 @@ private: } }; -template -inline ast_ptr new_ptr() { - return ast_ptr(new T); -} - -template class ast_sel : public _ast_ptr { +template class ast_sel : public _ast_ptr { public: - ast_sel() : _ast_ptr(nullptr, true) {} + ast_sel() : _ast_ptr(nullptr) {} - ast_sel(const ast_sel& other) : _ast_ptr(other.get(), true) {} + ast_sel(const ast_sel& other) : _ast_ptr(other.get()) {} - ast_sel& operator=(const ast_sel& other) { + ast_sel& operator=(const ast_sel& other) { set(other.get()); return *this; } @@ -356,15 +326,15 @@ public: virtual void construct(ast_stack& st) override { if (st.empty()) { - throw std::logic_error("invalid AST stack"); + if (!Required) return; + throw std::logic_error("Invalid AST stack"); } - ast_node* node = st.back(); - - if (!ast_sel::accept(node)) throw std::logic_error("invalid AST node"); - + if (!ast_sel::accept(node)) { + if (!Required) return; + throw std::logic_error("Invalid AST node"); + } st.pop_back(); - m_ptr = node; node->retain(); } @@ -380,10 +350,6 @@ private: class _ast_list : public ast_member { public: - typedef std::list container; - - _ast_list() : ast_member(true) {} - ~_ast_list() { clear(); } @@ -416,6 +382,12 @@ public: node->retain(); } + void pop_front() { + auto node = m_objects.front(); + m_objects.pop_front(); + node->release(); + } + void set_front(ast_node* node) { assert(node && accept(node)); m_objects.front()->release(); @@ -430,7 +402,7 @@ public: node->retain(); } - const container& objects() const { + const node_container& objects() const { return m_objects; } @@ -450,24 +422,24 @@ public: virtual int get_type() override { return ast_type<_ast_list>(); } protected: - container m_objects; + node_container m_objects; }; /** A list of objects. It pops objects of the given type from the ast stack, until no more objects can be popped. It assumes ownership of objects. + @tparam Required if true, the object is required. @tparam T type of object to control. */ -template class ast_list : public _ast_list { +template class ast_list : public _ast_list { public: - ast_list() {} + ast_list() { } - ast_list(const ast_list& other) { - clear(); + ast_list(const ast_list& other) { dup(other); } - ast_list& operator=(const ast_list& other) { + ast_list& operator=(const ast_list& other) { clear(); dup(other); return *this; @@ -479,17 +451,22 @@ public: virtual void construct(ast_stack &st) override { while (!st.empty()) { ast_node* node = st.back(); - - //if the object was not not of the appropriate type, - //end the list parsing - if (!ast_list::accept(node)) return; - + // if the object was not not of the appropriate type, + // end the list parsing + if (!ast_list::accept(node)) { + if (Required && m_objects.empty()) { + throw std::logic_error("Invalid AST node"); + } + return; + } st.pop_back(); - - //insert the object in the list, in reverse order + // insert the object in the list, in reverse order m_objects.push_front(node); node->retain(); } + if (Required && m_objects.empty()) { + throw std::logic_error("Invalid AST stack"); + } } private: virtual bool accept(ast_node* node) override { @@ -497,38 +474,36 @@ private: } }; -template class ast_sel_list : public _ast_list { +template class ast_sel_list : public _ast_list { public: - ast_sel_list() {} + ast_sel_list() { } - ast_sel_list(const ast_sel_list& other) { - clear(); + ast_sel_list(const ast_sel_list& other) { dup(other); } - ast_sel_list& operator=(const ast_sel_list& other) { + ast_sel_list& operator=(const ast_sel_list& other) { clear(); dup(other); return *this; } - /** Pops objects of type T from the stack until no more objects can be popped. - @param st stack. - */ virtual void construct(ast_stack &st) override { while (!st.empty()) { ast_node* node = st.back(); - - //if the object was not not of the appropriate type, - //end the list parsing - if (!ast_sel_list::accept(node)) return; - + if (!ast_sel_list::accept(node)) { + if (Required && m_objects.empty()) { + throw std::logic_error("Invalid AST node"); + } + return; + } st.pop_back(); - - //insert the object in the list, in reverse order m_objects.push_front(node); node->retain(); } + if (Required && m_objects.empty()) { + throw std::logic_error("Invalid AST stack"); + } } private: virtual bool accept(ast_node* node) override { @@ -583,10 +558,10 @@ ast_node* _parse(input &i, rule &g, error_list &el, void* ud); @param ud user data, passed to the parse procedures. @return ast nodes. */ -template ast_ptr parse(input &i, rule &g, error_list &el, void* ud = nullptr) { +template ast_ptr parse(input &i, rule &g, error_list &el, void* ud = nullptr) { ast_node* node = _parse(i, g, el, ud); T* ast = ast_cast(node); - ast_ptr ptr; + ast_ptr ptr; if (ast) { ast_stack st{node}; ptr.construct(st); @@ -598,6 +573,3 @@ template ast_ptr parse(input &i, rule &g, error_list } //namespace parserlib - - -#endif //AST_HPP diff --git a/MoonParser/moon_ast.cpp b/MoonParser/moon_ast.cpp index 50c13db..6b175fc 100644 --- a/MoonParser/moon_ast.cpp +++ b/MoonParser/moon_ast.cpp @@ -1,24 +1,6 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std::string_view_literals; #include "moon_ast.h" -typedef std::list str_list; - -const input& AstLeaf::getValue() { - if (_value.empty()) { - _value.assign(m_begin.m_it, m_end.m_it); - return trim(_value); - } - return _value; -} +namespace MoonP { #define AST_IMPL(type) \ ast __##type##_t(type); @@ -26,6 +8,7 @@ const input& AstLeaf::getValue() { AST_IMPL(Num) AST_IMPL(Name) AST_IMPL(Variable) +AST_IMPL(LuaKeyword) AST_IMPL(self) AST_IMPL(self_name) AST_IMPL(self_class) @@ -109,7 +92,7 @@ AST_IMPL(AssignableNameList) AST_IMPL(InvokeArgs) AST_IMPL(const_value) AST_IMPL(unary_exp) -AST_IMPL(Assignment) +AST_IMPL(ExpListAssign) AST_IMPL(if_else_line) AST_IMPL(unless_line) AST_IMPL(statement_appendix) @@ -119,3667 +102,4 @@ AST_IMPL(Body) AST_IMPL(Block) AST_IMPL(File) -#include - -inline std::string s(std::string_view sv) { - return std::string(sv); -} - -class MoonCompliler -{ -public: - void complile(const std::string& codes) { - input input = _converter.from_bytes(codes); - error_list el; - State st; - auto root = parse(input, File, el, &st); - if (root) { - std::cout << "compiled!\n\n"; - str_list out; - pushScope(); - transformBlock(root->block, out); - popScope(); - std::string result = std::move(out.back()); - std::cout << result << '\n'; - } else { - std::cout << "compile failed!\n"; - for (error_list::iterator it = el.begin(); it != el.end(); ++it) { - const error& err = *it; - std::cout << "line " << err.m_begin.m_line << ", col " << err.m_begin.m_col << ": syntax error\n"; - } - } - _codeCache.clear(); - std::stack emptyWith; - _withVars.swap(emptyWith); - std::stack emptyContinue; - _continueVars.swap(emptyContinue); - } -private: - int _indentOffset = 0; - Converter _converter; - std::list _codeCache; - std::stack _withVars; - std::stack _continueVars; - std::ostringstream _buf; - std::string _newLine = "\n"; - std::vector _lineTable; - enum class LocalMode { - None = 0, - Capital = 1, - Any = 2 - }; - enum class ExportMode { - None = 0, - Capital = 1, - Any = 2 - }; - struct Scope { - ExportMode mode = ExportMode::None; - std::unique_ptr> vars; - std::unique_ptr> allows; - std::unique_ptr> exports; - }; - std::list _scopes; - static const std::string Empty; - - enum class MemType { - Builtin, - Common, - Property - }; - - struct ClassMember { - std::string item; - MemType type; - ast_node* node; - }; - - struct DestructItem { - bool isVariable = false; - std::string name; - std::string structure; - }; - - struct Destructure { - std::string value; - std::list items; - }; - - enum class ExpUsage { - Return, - Assignment, - Common - }; - - enum class IfUsage { - Return, - Closure, - Common - }; - - void pushScope() { - _scopes.emplace_back(); - _scopes.back().vars = std::make_unique>(); - } - - void popScope() { - _scopes.pop_back(); - } - - bool isDefined(const std::string& name, bool checkShadowScopeOnly = false) { - bool isDefined = false; - int mode = int(std::isupper(name[0]) ? ExportMode::Capital : ExportMode::Any); - const auto& current = _scopes.back(); - if (int(current.mode) >= mode) { - if (current.exports) { - if (current.exports->find(name) != current.exports->end()) { - isDefined = true; - current.vars->insert(name); - } - } else { - isDefined = true; - current.vars->insert(name); - } - } - for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { - auto vars = it->vars.get(); - if (vars->find(name) != vars->end()) { - isDefined = true; - break; - } - if (checkShadowScopeOnly && it->allows) break; - } - return isDefined; - } - - bool isSolidDefined(const std::string& name) { - bool isDefined = false; - for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { - auto vars = it->vars.get(); - if (vars->find(name) != vars->end()) { - isDefined = true; - break; - } - } - return isDefined; - } - - void markVarShadowed() { - auto& scope = _scopes.back(); - scope.allows = std::make_unique>(); - } - - void markVarExported(ExportMode mode, bool specified) { - auto& scope = _scopes.back(); - scope.mode = mode; - if (specified && !scope.exports) { - scope.exports = std::make_unique>(); - } - } - - void addExportedVar(const std::string& name) { - auto& scope = _scopes.back(); - scope.exports->insert(name); - } - - void addToAllowList(const std::string& name) { - auto& scope = _scopes.back(); - scope.allows->insert(name); - } - - void forceAddToScope(const std::string& name) { - auto& scope = _scopes.back(); - scope.vars->insert(name); - } - - Scope& currentScope() { - return _scopes.back(); - } - - bool addToScope(const std::string& name) { - bool defined = false; - auto& scope = _scopes.back(); - decltype(scope.allows.get()) allows = nullptr; - for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { - if (it->allows) allows = it->allows.get(); - } - if (allows) { - bool shadowed = allows->find(name) == allows->end(); - defined = isDefined(name, shadowed); - } else { - defined = isDefined(name); - } - if (!defined) scope.vars->insert(name); - return !defined; - } - - std::string getUnusedName(std::string_view name) { - int index = 0; - std::string newName; - do { - newName = s(name) + std::to_string(index); - index++; - } while (isSolidDefined(newName)); - return newName; - } - - const std::string nll(ast_node* node) { - // return s(" -- "sv) + std::to_string(node->m_begin.m_line) + _newLine; - _lineTable.push_back(node->m_begin.m_line); - return _newLine; - } - - const std::string nlr(ast_node* node) { - // return s(" -- "sv) + std::to_string(node->m_end.m_line) + _newLine; - _lineTable.push_back(node->m_end.m_line); - return _newLine; - } - - void incIndentOffset() { - _indentOffset++; - } - - void decIndentOffset() { - _indentOffset--; - } - - std::string indent() { - return std::string((_scopes.size() - 1 + _indentOffset) * 2, ' '); - } - - std::string indent(int offset) { - return std::string((_scopes.size() - 1 + _indentOffset + offset) * 2, ' '); - } - - std::string clearBuf() { - std::string str = _buf.str(); - _buf.str(""); - _buf.clear(); - return str; - } - - std::string join(const str_list& items) { - if (items.empty()) return Empty; - else if (items.size() == 1) return items.front(); - auto begin = ++items.begin(); - return std::accumulate(begin, items.end(), items.front(), - [&](const std::string& a, const std::string& b) { return a + b; }); - } - - 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(); - return std::accumulate(begin, items.end(), items.front(), - [&](const std::string& a, const std::string& b) { return a + sepStr + b; }); - } - - std::string toString(ast_node* node) { - return _converter.to_bytes(std::wstring(node->m_begin.m_it, node->m_end.m_it)); - } - - std::string toString(input::iterator begin, input::iterator end) { - return _converter.to_bytes(std::wstring(begin, end)); - } - - Value_t* singleValueFrom(ast_node* expList) { - ast_node* singleValue = nullptr; - expList->traverse([&](ast_node* n) { - if (n->getId() == "Value"_id) { - if (!singleValue) { - singleValue = n; - return traversal::Return; - } else { - singleValue = nullptr; - return traversal::Stop; - } - } - return traversal::Continue; - }); - return static_cast(singleValue); - } - - SimpleValue_t* simpleSingleValueFrom(ast_node* expList) { - auto value = singleValueFrom(expList); - if (value && value->item.is()) { - return static_cast(value->item.get()); - } - return nullptr; - } - - Value_t* firstValueFrom(ast_node* expList) { - Value_t* firstValue = nullptr; - expList->traverse([&](ast_node* n) { - if (n->getId() == "Value"_id) { - firstValue = static_cast(n); - return traversal::Stop; - } - return traversal::Continue; - }); - return firstValue; - } - - Statement_t* lastStatementFrom(Body_t* body) { - if (auto stmt = body->content.as()) { - return stmt; - } else { - auto node = body->content.to()->statements.objects().back(); - return static_cast(node); - } - } - - Statement_t* lastStatementFrom(Block_t* block) { - auto node = block->statements.objects().back(); - return static_cast(node); - } - - template - ast_ptr toAst(std::string_view codes, rule& r) { - _codeCache.push_back(_converter.from_bytes(s(codes))); - error_list el; - State st; - return parse(_codeCache.back(), r, el, &st); - } - - bool matchAst(rule& r, std::string_view codes) { - error_list el; - State st; - input i = _converter.from_bytes(s(codes)); - auto rEnd = rule(r >> eof()); - return _parse(i, rEnd, el, &st); - } - - bool isChainValueCall(ChainValue_t* chainValue) { - return ast_is(chainValue->items.back()); - } - - std::string variableFrom(ast_node* expList) { - if (!ast_is(expList)) return Empty; - if (auto value = singleValueFrom(expList)) { - if (auto chainValue = value->getByPath()) { - if (chainValue->items.size() == 1) { - if (auto callable = ast_cast(chainValue->items.front())) { - return toString(callable->item); - } - } - } - } - return Empty; - } - - bool isColonChain(ChainValue_t* chainValue) { - return ast_is(chainValue->items.back()); - } - - void transformStatement(Statement_t* statement, str_list& out) { - if (statement->appendix) { - if (auto assignment = statement->content.as()) { - auto preDefine = getPredefine(transformAssignDefs(assignment->assignable)); - if (!preDefine.empty()) out.push_back(preDefine + nll(statement)); - } - auto appendix = statement->appendix.get(); - switch (appendix->item->getId()) { - case "if_else_line"_id: { - auto if_else_line = appendix->item.to(); - auto ifNode = new_ptr(); - - auto ifCond = new_ptr(); - ifCond->condition.set(if_else_line->condition); - ifNode->nodes.push_back(ifCond); - - auto stmt = new_ptr(); - stmt->content.set(statement->content); - auto body = new_ptr(); - body->content.set(stmt); - ifNode->nodes.push_back(body); - - if (!ast_is(if_else_line->elseExpr)) { - auto exprList = new_ptr(); - exprList->exprs.push_back(if_else_line->elseExpr); - auto stmt = new_ptr(); - stmt->content.set(exprList); - auto body = new_ptr(); - body->content.set(stmt); - ifNode->nodes.push_back(body); - } - - statement->appendix.set(nullptr); - auto simpleValue = new_ptr(); - simpleValue->value.set(ifNode); - auto value = new_ptr(); - value->item.set(simpleValue); - auto exp = new_ptr(); - exp->value.set(value); - auto exprList = new_ptr(); - exprList->exprs.push_back(exp); - statement->content.set(exprList); - break; - } - case "unless_line"_id: { - auto unless_line = appendix->item.to(); - auto unless = new_ptr(); - - auto ifCond = new_ptr(); - ifCond->condition.set(unless_line->condition); - unless->nodes.push_back(ifCond); - - auto stmt = new_ptr(); - stmt->content.set(statement->content); - auto body = new_ptr(); - body->content.set(stmt); - unless->nodes.push_back(body); - - statement->appendix.set(nullptr); - auto simpleValue = new_ptr(); - simpleValue->value.set(unless); - auto value = new_ptr(); - value->item.set(simpleValue); - auto exp = new_ptr(); - exp->value.set(value); - auto exprList = new_ptr(); - exprList->exprs.push_back(exp); - statement->content.set(exprList); - break; - } - case "CompInner"_id: { - auto compInner = appendix->item.to(); - auto comp = new_ptr(); - comp->forLoop.set(compInner); - auto stmt = new_ptr(); - stmt->content.set(statement->content); - comp->value.set(stmt); - auto simpleValue = new_ptr(); - simpleValue->value.set(comp); - auto value = new_ptr(); - value->item.set(simpleValue); - auto exp = new_ptr(); - exp->value.set(value); - auto expList = new_ptr(); - expList->exprs.push_back(exp); - statement->content.set(expList); - statement->appendix.set(nullptr); - break; - } - default: break; - } - } - auto content = statement->content.get(); - if (!content) { - out.push_back(Empty); - return; - } - switch (content->getId()) { - case "Import"_id: transformImport(static_cast(content), out); break; - case "While"_id: transformWhile(static_cast(content), out); break; - case "For"_id: transformFor(static_cast(content), out); break; - case "ForEach"_id: transformForEach(static_cast(content), out); break; - case "Return"_id: transformReturn(static_cast(content), out); break; - case "Local"_id: transformLocal(static_cast(content), out); break; - case "Export"_id: transformExport(static_cast(content), out); break; - case "BreakLoop"_id: transformBreakLoop(static_cast(content), out); break; - case "Assignment"_id: transformAssignment(static_cast(content), out); break; - case "ExpList"_id: { - auto expList = static_cast(content); - if (expList->exprs.objects().empty()) { - out.push_back(Empty); - break; - } - if (auto singleValue = singleValueFrom(expList)) { - if (auto simpleValue = singleValue->item.as()) { - auto value = simpleValue->value.get(); - bool specialSingleValue = true; - switch (value->getId()) { - case "If"_id: transformIf(static_cast(value), out); break; - case "ClassDecl"_id: transformClassDecl(static_cast(value), out); break; - case "Unless"_id: transformUnless(static_cast(value), out); break; - case "Switch"_id: transformSwitch(static_cast(value), out); break; - case "With"_id: transformWith(static_cast(value), out); break; - case "ForEach"_id: transformForEach(static_cast(value), out); break; - case "For"_id: transformFor(static_cast(value), out); break; - case "While"_id: transformWhile(static_cast(value), out); break; - case "Do"_id: transformDo(static_cast(value), out); break; - case "Comprehension"_id: transformCompCommon(static_cast(value), out); break; - default: specialSingleValue = false; break; - } - if (specialSingleValue) { - break; - } - } - if (auto chainValue = singleValue->item.as()) { - if (isChainValueCall(chainValue)) { - transformValue(singleValue, out); - out.back() = indent() + out.back() + nlr(singleValue); - break; - } - } - } - auto assign = new_ptr(); - assign->values.dup(expList->exprs); - auto assignment = new_ptr(); - assignment->assignable.set(toAst("_", ExpList)); - assignment->target.set(assign); - transformAssignment(assignment, out); - break; - } - default: break; - } - } - - str_list getAssignVars(Assignment_t* assignment) { - str_list vars; - if (!assignment->target.is()) return vars; - for (auto exp : assignment->assignable->exprs.objects()) { - auto var = variableFrom(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 = variableFrom(exp); - vars.push_back(var.empty() ? Empty : var); - } - return vars; - } - - str_list transformAssignDefs(ExpList_t* expList) { - str_list preDefs; - for (auto exp_ : expList->exprs.objects()) { - auto exp = static_cast(exp_); - if (auto value = singleValueFrom(exp)) { - if (auto chain = value->item.as()) { - do { - if (chain->items.size() != 1) break; - auto callable = ast_cast(chain->items.front()); - if (!callable) break; - auto name = toString(callable->item); - if (addToScope(name)) { - preDefs.push_back(name); - } - } while (false); - } - } else { - throw std::logic_error("left hand expression is not assignable"); - } - } - return preDefs; - } - - std::string getPredefine(const str_list& defs) { - if (defs.empty()) return Empty; - return indent() + s("local "sv) + join(defs, ", "sv); - } - - std::string getDestrucureDefine(Assignment_t* assignment) { - auto info = extractDestructureInfo(assignment); - if (!info.first.empty()) { - for (const auto& destruct : info.first) { - str_list defs; - for (const auto& item : destruct.items) { - if (item.isVariable && addToScope(item.name)) { - defs.push_back(item.name); - } - } - if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv); - } - } - return clearBuf(); - } - - std::string getPredefine(Assignment_t* assignment) { - auto preDefine = getDestrucureDefine(assignment); - if (preDefine.empty()) { - preDefine = getPredefine(transformAssignDefs(assignment->assignable)); - } - return preDefine.empty() ? preDefine : preDefine + nll(assignment); - } - - void assignLastExplist(ExpList_t* expList, Body_t* body) { - auto last = lastStatementFrom(body); - bool lastAssignable = last && ast_is(last->content); - if (lastAssignable) { - auto newAssignment = new_ptr(); - newAssignment->assignable.set(expList); - auto assign = new_ptr(); - if (auto valueList = last->content.as()) { - assign->values.dup(valueList->exprs); - } else { - auto simpleValue = new_ptr(); - simpleValue->value.set(last->content); - auto value = new_ptr(); - value->item.set(simpleValue); - auto exp = new_ptr(); - exp->value.set(value); - assign->values.push_back(exp); - } - newAssignment->target.set(assign); - last->content.set(newAssignment); - } - } - - void transformAssignment(Assignment_t* assignment, str_list& out) { - auto assign = ast_cast(assignment->target); - do { - if (!assign || assign->values.objects().size() != 1) break; - auto value = assign->values.objects().front(); - ast_node* item = nullptr; - if (ast_is(value)) { - item = value; - } else if (auto val = simpleSingleValueFrom(value)) { - if (ast_is(val->value)) { - item = val->value; - } - } - if (item) { - auto expList = assignment->assignable.get(); - str_list temp; - auto defs = transformAssignDefs(expList); - if (!defs.empty()) temp.push_back(getPredefine(defs) + nll(expList)); - item->traverse([&](ast_node* node) { - switch (node->getId()) { - case "IfCond"_id: return traversal::Return; - case "Body"_id: { - auto body = static_cast(node); - assignLastExplist(expList, body); - return traversal::Return; - } - default: return traversal::Continue; - } - }); - switch (item->getId()) { - case "If"_id: transformIf(static_cast(item), temp); break; - case "Unless"_id: transformUnless(static_cast(item), temp); break; - } - out.push_back(join(temp)); - return; - } - if (auto switchNode = ast_cast(value)) { - auto expList = assignment->assignable.get(); - for (auto branch_ : switchNode->branches.objects()) { - auto branch = static_cast(branch_); - assignLastExplist(expList, branch->body); - } - if (switchNode->lastBranch) { - assignLastExplist(expList, switchNode->lastBranch); - } - std::string preDefine = getPredefine(assignment); - transformSwitch(switchNode, out); - out.back() = preDefine + out.back(); - return; - } - if (auto withNode = ast_cast(value)) { - str_list temp; - auto expList = assignment->assignable.get(); - std::string preDefine = getPredefine(assignment); - transformWith(withNode, temp, expList); - out.push_back(preDefine + temp.back()); - return; - } - auto exp = ast_cast(value); - if (!exp) break; - if (auto simpleVal = exp->value->item.as()) { - auto valueItem = simpleVal->value.get(); - switch (valueItem->getId()) { - case "Do"_id: { - auto doNode = static_cast(valueItem); - auto expList = assignment->assignable.get(); - assignLastExplist(expList, doNode->body); - std::string preDefine = getPredefine(assignment); - transformDo(doNode, out); - out.back() = preDefine + out.back(); - return; - } - case "Comprehension"_id: { - auto expList = assignment->assignable.get(); - std::string preDefine = getPredefine(assignment); - transformCompInPlace(static_cast(valueItem), expList, out); - out.back() = preDefine + out.back(); - return; - } - case "TblComprehension"_id: { - auto expList = assignment->assignable.get(); - std::string preDefine = getPredefine(assignment); - transformTblCompInPlace(static_cast(valueItem), expList, out); - out.back() = preDefine + out.back(); - return; - } - case "For"_id: { - str_list temp; - auto expList = assignment->assignable.get(); - std::string preDefine = getPredefine(assignment); - transformForInPlace(static_cast(valueItem), temp, expList); - out.push_back(preDefine + temp.back()); - return; - } - case "ForEach"_id: { - str_list temp; - auto expList = assignment->assignable.get(); - std::string preDefine = getPredefine(assignment); - transformForEachInPlace(static_cast(valueItem), temp, expList); - out.push_back(preDefine + temp.back()); - return; - } - case "ClassDecl"_id: { - str_list temp; - auto expList = assignment->assignable.get(); - std::string preDefine = getPredefine(assignment); - transformClassDecl(static_cast(valueItem), temp, ExpUsage::Assignment, expList); - out.push_back(preDefine + temp.back()); - return; - } - case "While"_id: { - str_list temp; - auto expList = assignment->assignable.get(); - std::string preDefine = getPredefine(assignment); - transformWhileClosure(static_cast(valueItem), temp, expList); - out.push_back(preDefine + temp.back()); - return; - } - } - } - if (auto chainValue = exp->value->item.as()) { - if (isColonChain(chainValue)) { - auto assignable = assignment->assignable.get(); - std::string preDefine = getPredefine(transformAssignDefs(assignable)); - transformColonChain(chainValue, out, ExpUsage::Assignment, assignable); - auto nl = preDefine.empty() ? Empty : nll(chainValue); - if (!preDefine.empty()) out.back() = preDefine + nl + out.back(); - return; - } - } - } while (false); - 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 && addToScope(pair.name)) { - _buf << s("local "sv); - } - _buf << pair.name << " = "sv << info.first.front().value << pair.structure << nll(assignment); - temp.push_back(clearBuf()); - } else if (matchAst(Name, destruct.value)) { - str_list defs, names, values; - for (const auto& item : destruct.items) { - if (item.isVariable && addToScope(item.name)) { - defs.push_back(item.name); - } - names.push_back(item.name); - values.push_back(item.structure); - } - for (auto& v : values) v.insert(0, destruct.value); - if (defs.empty()) { - _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); - } else { - _buf << indent() << "local "sv; - if (defs.size() != names.size()) { - _buf << join(defs,", "sv) << nll(assignment) << indent(); - } - _buf << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); - } - temp.push_back(clearBuf()); - } else { - str_list defs, names, values; - for (const auto& item : destruct.items) { - if (item.isVariable && addToScope(item.name)) { - defs.push_back(item.name); - } - names.push_back(item.name); - values.push_back(item.structure); - } - if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv) << nll(assignment); - _buf << indent() << "do"sv << nll(assignment); - pushScope(); - auto objVar = getUnusedName("_obj_"); - for (auto& v : values) v.insert(0, objVar); - _buf << indent() << "local "sv << objVar << " = "sv << destruct.value << nll(assignment); - _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); - popScope(); - _buf << indent() << "end"sv << nll(assignment); - temp.push_back(clearBuf()); - } - } - if (info.second) { - transformAssignmentCommon(info.second, temp); - } - out.push_back(join(temp)); - } - } - - void transformAssignItem(ast_node* value, str_list& out) { - switch (value->getId()) { - case "With"_id: transformWithClosure(static_cast(value), out); break; - case "If"_id: transformIf(static_cast(value), out, IfUsage::Closure); break; - case "Switch"_id: transformSwitchClosure(static_cast(value), out); break; - case "TableBlock"_id: transformTableBlock(static_cast(value), out); break; - case "Exp"_id: transformExp(static_cast(value), out); break; - default: break; - } - } - - std::list destructFromExp(ast_node* node) { - const std::list* tableItems = nullptr; - if (ast_cast(node)) { - auto item = singleValueFrom(node)->item.get(); - if (!item) throw std::logic_error("Invalid destructure value"); - auto tbA = item->getByPath(); - if (tbA) { - tableItems = &tbA->values.objects(); - } else { - auto tbB = ast_cast(item); - if (tbB) tableItems = &tbB->pairs.objects(); - } - } else if (auto table = ast_cast(node)) { - tableItems = &table->values.objects(); - } - std::list pairs; - int index = 0; - for (auto pair : *tableItems) { - switch (pair->getId()) { - case "Exp"_id: { - ++index; - auto item = singleValueFrom(pair)->item.get(); - if (!item) throw std::logic_error("Invalid destructure value"); - if (item->getId() == "Parens"_id) throw std::logic_error("Can't destructure value of type: parens"); - if (ast_cast(item) || - item->getByPath()) { - auto subPairs = destructFromExp(pair); - for (auto& p : subPairs) { - pairs.push_back({p.isVariable, p.name, - s("["sv) + std::to_string(index) + s("]"sv) + p.structure}); - } - } else { - str_list temp; - transformExp(static_cast(pair), temp); - pairs.push_back({ - item->getByPath() != nullptr, - temp.back(), - s("["sv) + std::to_string(index) + s("]"sv) - }); - } - break; - } - case "variable_pair"_id: { - auto vp = static_cast(pair); - auto name = toString(vp->name); - pairs.push_back({true, name, s("."sv) + name}); - break; - } - case "normal_pair"_id: { - auto np = static_cast(pair); - auto key = np->key->getByPath(); - if (!key) throw std::logic_error("Invalid key for destructure"); - if (auto exp = np->value.as()) { - auto item = singleValueFrom(exp)->item.get(); - if (!item) throw std::logic_error("Invalid destructure value"); - if (ast_cast(item) || - item->getByPath()) { - auto subPairs = destructFromExp(exp); - for (auto& p : subPairs) { - pairs.push_back({p.isVariable, p.name, - s("."sv) + toString(key) + p.structure}); - } - } else { - str_list temp; - transformExp(exp, temp); - pairs.push_back({ - item->getByPath() != nullptr, - temp.back(), s("."sv) + toString(key) - }); - } - break; - } - if (np->value.as()) { - auto subPairs = destructFromExp(pair); - for (auto& p : subPairs) { - pairs.push_back({p.isVariable, p.name, - s("."sv) + toString(key) + p.structure}); - } - } - break; - } - } - } - return pairs; - } - - std::pair, ast_ptr> - extractDestructureInfo(Assignment_t* assignment) { - std::list destructs; - if (!assignment->target.is()) return { destructs, nullptr }; - auto exprs = assignment->assignable->exprs.objects(); - auto values = assignment->target.to()->values.objects(); - size_t size = std::max(exprs.size(),values.size()); - ast_ptr var; - if (exprs.size() < size) { - var = toAst("_"sv, Exp); - while (exprs.size() < size) exprs.emplace_back(var); - } - ast_ptr nullNode; - if (values.size() < size) { - nullNode = toAst("nil"sv, Exp); - while (values.size() < size) values.emplace_back(nullNode); - } - using iter = std::list::iterator; - std::vector> destructPairs; - str_list temp; - for (auto i = exprs.begin(), j = values.begin(); i != exprs.end(); ++i, ++j) { - auto expr = *i; - ast_node* destructNode = expr->getByPath(); - if (destructNode || (destructNode = expr->getByPath())) { - destructPairs.push_back({i,j}); - pushScope(); - transformAssignItem(*j, temp); - popScope(); - auto& destruct = destructs.emplace_back(); - destruct.value = temp.back(); - temp.pop_back(); - auto pairs = destructFromExp(expr); - destruct.items = std::move(pairs); - } - } - for (const auto& p : destructPairs) { - exprs.erase(p.first); - values.erase(p.second); - } - ast_ptr newAssignment; - if (!destructPairs.empty() && !exprs.empty()) { - auto expList = new_ptr(); - auto newAssign = new_ptr(); - newAssign->assignable.set(expList); - for (auto expr : exprs) expList->exprs.push_back(expr); - auto assign = new_ptr(); - for (auto value : values) assign->values.push_back(value); - newAssign->target.set(assign); - newAssignment = newAssign; - } - return {std::move(destructs), newAssignment}; - } - - void transformAssignmentCommon(Assignment_t* assignment, str_list& out) { - str_list temp; - auto expList = assignment->assignable.get(); - auto action = assignment->target.get(); - switch (action->getId()) { - case "Update"_id: { - auto update = static_cast(action); - auto leftExp = static_cast(expList->exprs.objects().front()); - auto leftValue = singleValueFrom(leftExp); - if (!leftValue) throw std::logic_error("left hand expression is not assignable"); - if (auto chain = leftValue->getByPath()) { - auto tmpChain = new_ptr(); - for (auto item : chain->items.objects()) { - bool itemAdded = false; - do { - auto exp = ast_cast(item); - if (!exp) break; - auto var = variableFrom(exp); - if (!var.empty()) break; - auto upVar = getUnusedName("_update_"); - auto assignment = new_ptr(); - assignment->assignable.set(toAst(upVar, ExpList)); - auto assign = new_ptr(); - assign->values.push_back(exp); - assignment->target.set(assign); - transformAssignment(assignment, temp); - tmpChain->items.push_back(toAst(upVar, Exp)); - itemAdded = true; - } while (false); - if (!itemAdded) tmpChain->items.push_back(item); - } - chain->items.clear(); - chain->items.dup(tmpChain->items); - } - transformValue(leftValue, temp); - auto left = std::move(temp.back()); - temp.pop_back(); - transformExp(update->value, temp); - auto right = std::move(temp.back()); - temp.pop_back(); - if (!singleValueFrom(update->value)) { - right = s("("sv) + right + s(")"sv); - } - _buf << join(temp) << indent() << left << " = "sv << left << - " "sv << toString(update->op) << " "sv << right << nll(assignment); - out.push_back(clearBuf()); - break; - } - case "Assign"_id: { - auto defs = transformAssignDefs(expList); - std::string preDefine = getPredefine(defs); - bool oneLined = defs.size() == expList->exprs.objects().size() && - traversal::Stop != action->traverse([&](ast_node* n) { - if (n->getId() == "Callable"_id) { - if (auto name = n->getByPath()) { - for (const auto& def : defs) { - if (def == toString(name)) { - return traversal::Stop; - } - } - } - } - return traversal::Continue; - }); - transformExpList(expList, temp); - std::string left = temp.back(); - temp.pop_back(); - auto assign = static_cast(action); - for (auto value : assign->values.objects()) { - transformAssignItem(value, temp); - } - if (oneLined) { - out.push_back((preDefine.empty() ? indent() + left : preDefine) + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); - } - else { - out.push_back((preDefine.empty() ? Empty : preDefine + nll(assignment)) + indent() + left + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); - } - break; - } - default: break; - } - } - - void transformCond(const std::list& nodes, str_list& out, IfUsage usage = IfUsage::Common, bool unless = false) { - std::vector> ns; - 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 newIf = new_ptr(); - for (auto j = ns.rbegin(); j != ns.rend(); ++j) { - newIf->nodes.push_back(*j); - } - ns.clear(); - auto simpleValue = new_ptr(); - simpleValue->value.set(newIf); - auto value = new_ptr(); - value->item.set(simpleValue); - auto exp = new_ptr(); - exp->value.set(value); - auto expList = new_ptr(); - expList->exprs.push_back(exp); - auto stmt = new_ptr(); - stmt->content.set(expList); - auto body = new_ptr(); - body->content.set(stmt); - ns.push_back(body.get()); - } - } - } - if (nodes.size() != ns.size()) { - auto newIf = new_ptr(); - for (auto j = ns.rbegin(); j != ns.rend(); ++j) { - newIf->nodes.push_back(*j); - } - transformCond(newIf->nodes.objects(), out, usage, unless); - return; - } - str_list temp; - if (usage == IfUsage::Closure) { - temp.push_back(s("(function()"sv) + nll(nodes.front())); - pushScope(); - } - std::list> ifCondPairs; - ifCondPairs.emplace_back(); - for (auto node : nodes) { - switch (node->getId()) { - case "IfCond"_id: - ifCondPairs.back().first = static_cast(node); - break; - case "Body"_id: - ifCondPairs.back().second = static_cast(node); - ifCondPairs.emplace_back(); - break; - default: break; - } - } - auto assign = ifCondPairs.front().first->assign.get(); - if (assign) { - if (usage != IfUsage::Closure) { - temp.push_back(indent() + s("do"sv) + nll(assign)); - pushScope(); - } - auto exp = ifCondPairs.front().first->condition.get(); - if (auto table = exp->getByPath()) { - std::string desVar = getUnusedName("_des_"); - bool storingValue = true; - if (assign->values.objects().size() == 1) { - auto var = variableFrom(assign->values.objects().front()); - if (!var.empty()) { - desVar = var; - storingValue = false; - } - } - if (storingValue) { - auto expList = toAst(desVar, ExpList); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assign); - transformAssignment(assignment, temp); - } - { - auto expList = new_ptr(); - expList->exprs.push_back(exp); - auto assignOne = new_ptr(); - auto valExp = toAst(desVar, Exp); - assignOne->values.push_back(valExp); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assignOne); - transformAssignment(assignment, temp); - auto pair = destructFromExp(exp); - auto cond = toAst(pair.front().name, Exp); - ifCondPairs.front().first->condition.set(cond); - } - } else { - auto expList = new_ptr(); - expList->exprs.push_back(exp); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assign); - transformAssignment(assignment, temp); - } - } - for (const auto& pair : ifCondPairs) { - if (pair.first) { - str_list tmp; - auto condition = pair.first->condition.get(); - if (unless) { - if (auto value = singleValueFrom(condition)) { - transformValue(value, tmp); - } else { - transformExp(condition, tmp); - tmp.back() = s("("sv) + tmp.back() + s(")"sv); - } - tmp.back().insert(0, s("not "sv)); - } else { - transformExp(condition, tmp); - } - _buf << indent() << (pair == ifCondPairs.front() ? ""sv : "else"sv) << - "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(); - transformBody(pair.second, temp, usage != IfUsage::Common); - popScope(); - } - if (!pair.first) { - temp.push_back(indent() + s("end"sv) + nll(nodes.front())); - break; - } - } - if (assign && usage != IfUsage::Closure) { - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(nodes.front())); - } - if (usage == IfUsage::Closure) { - popScope(); - temp.push_back(indent() + s("end)()"sv)); - } - out.push_back(join(temp)); - } - - void transformIf(If_t* ifNode, str_list& out, IfUsage usage = IfUsage::Common) { - transformCond(ifNode->nodes.objects(), out, usage); - } - - void transformUnless(Unless_t* unless, str_list& out, IfUsage usage = IfUsage::Common) { - transformCond(unless->nodes.objects(), out, usage, true); - } - - void transformExpList(ExpList_t* expList, str_list& out) { - str_list temp; - for (auto exp : expList->exprs.objects()) { - transformExp(static_cast(exp), temp); - } - out.push_back(join(temp, ", "sv)); - } - - void transformExpListLow(ExpListLow_t* expListLow, str_list& out) { - str_list temp; - for (auto exp : expListLow->exprs.objects()) { - transformExp(static_cast(exp), temp); - } - out.push_back(join(temp, ", "sv)); - } - - void transformExp(Exp_t* exp, str_list& out) { - str_list temp; - transformValue(exp->value, temp); - for (auto _opValue : exp->opValues.objects()) { - auto opValue = static_cast(_opValue); - transformBinaryOperator(opValue->op, temp); - transformValue(opValue->value, temp); - } - out.push_back(join(temp, " "sv)); - } - - void transformValue(Value_t* value, str_list& out) { - auto item = value->item.get(); - switch (item->getId()) { - case "SimpleValue"_id: transformSimpleValue(static_cast(item), out); break; - case "simple_table"_id: transform_simple_table(static_cast(item), out); break; - case "ChainValue"_id: { - auto chainValue = static_cast(item); - if (isColonChain(chainValue)) { - transformColonChainClosure(chainValue, out); - } else { - transformChainValue(chainValue, out); - } - break; - } - case "String"_id: transformString(static_cast(item), out); break; - default: break; - } - } - - void transformCallable(Callable_t* callable, str_list& out, bool invoke) { - auto item = callable->item.get(); - switch (item->getId()) { - case "Variable"_id: transformVariable(static_cast(item), out); break; - case "SelfName"_id: transformSelfName(static_cast(item), out, invoke); break; - case "VarArg"_id: out.push_back(s("..."sv)); break; - case "Parens"_id: transformParens(static_cast(item), out); break; - default: break; - } - } - - void transformParens(Parens_t* parans, str_list& out) { - str_list temp; - transformExp(parans->expr, temp); - out.push_back(s("("sv) + temp.front() + s(")"sv)); - } - - void transformSimpleValue(SimpleValue_t* simpleValue, str_list& out) { - auto value = simpleValue->value.get(); - switch (value->getId()) { - case "const_value"_id: transform_const_value(static_cast(value), out); break; - case "If"_id: transformIf(static_cast(value), out, IfUsage::Closure); break; - case "Unless"_id: transformUnless(static_cast(value), out, IfUsage::Closure); break; - case "Switch"_id: transformSwitchClosure(static_cast(value), out); break; - case "With"_id: transformWithClosure(static_cast(value), out); break; - case "ClassDecl"_id: transformClassDeclClosure(static_cast(value), out); break; - case "ForEach"_id: transformForEachClosure(static_cast(value), out); break; - case "For"_id: transformForClosure(static_cast(value), out); break; - case "While"_id: transformWhileClosure(static_cast(value), out); break; - case "Do"_id: transformDoClosure(static_cast(value), out); break; - case "unary_exp"_id: transform_unary_exp(static_cast(value), out); break; - case "TblComprehension"_id: transformTblCompClosure(static_cast(value), out); break; - case "TableLit"_id: transformTableLit(static_cast(value), out); break; - case "Comprehension"_id: transformCompClosure(static_cast(value), out); break; - case "FunLit"_id: transformFunLit(static_cast(value), out); break; - case "Num"_id: transformNum(static_cast(value), out); break; - default: break; - } - } - - void transformFunLit(FunLit_t* funLit, str_list& out) { - str_list temp; - bool isFatArrow = toString(funLit->arrow) == "=>"sv; - pushScope(); - if (auto argsDef = funLit->argsDef.get()) { - transformFnArgsDef(argsDef, temp); - if (funLit->body) { - transformBody(funLit->body, temp, true); - } else { - temp.push_back(Empty); - } - auto it = temp.begin(); - auto& args = *it; - auto& initArgs = *(++it); - auto& bodyCodes = *(++it); - _buf << "function("sv << - (isFatArrow ? s("self, "sv) : Empty) << - args << ')'; - if (!initArgs.empty() || !bodyCodes.empty()) { - _buf << nlr(argsDef) << initArgs << bodyCodes; - popScope(); - _buf << indent() << "end"sv; - } else { - popScope(); - _buf << " end"sv; - } - } else { - if (funLit->body) { - transformBody(funLit->body, temp, true); - } else { - temp.push_back(Empty); - } - auto& bodyCodes = temp.back(); - _buf << "function("sv << - (isFatArrow ? s("self"sv) : Empty) << - ')'; - if (!bodyCodes.empty()) { - _buf << nll(funLit) << bodyCodes; - popScope(); - _buf << indent() << "end"sv; - } else { - popScope(); - _buf << " end"sv; - } - } - out.push_back(clearBuf()); - } - - void transformCodes(const std::list& nodes, str_list& out, bool implicitReturn) { - LocalMode mode = LocalMode::None; - Local_t* any = nullptr, *capital = nullptr; - for (auto node : nodes) { - auto stmt = static_cast(node); - if (auto local = stmt->content.as()) { - if (auto flag = local->name.as()) { - LocalMode newMode = toString(flag) == "*"sv ? LocalMode::Any : LocalMode::Capital; - if (int(newMode) > int(mode)) { - mode = newMode; - } - if (mode == LocalMode::Any) { - if (!any) any = local; - if (!capital) capital = local; - } else { - if (!capital) capital = local; - } - } else { - auto names = local->name.to(); - for (auto name : names->names.objects()) { - local->forceDecls.push_back(toString(name)); - } - } - } else if (mode != LocalMode::None) { - ClassDecl_t* classDecl = nullptr; - if (auto assignment = stmt->content.as()) { - auto vars = getAssignVars(assignment); - for (const auto& var : vars) { - if (var.empty()) continue; - if (std::isupper(var[0]) && capital) { - capital->decls.push_back(var); - } else if (any) { - any->decls.push_back(var); - } - } - auto info = extractDestructureInfo(assignment); - if (!info.first.empty()) { - for (const auto& destruct : info.first) - for (const auto& item : destruct.items) - if (item.isVariable) { - if (std::isupper(item.name[0]) && capital) { capital->decls.push_back(item.name); - } else if (any) { - any->decls.push_back(item.name); - } - } - } - do { - auto assign = assignment->target.as(); - if (!assign) break; - if (assign->values.objects().size() != 1) break; - auto exp = ast_cast(assign->values.objects().front()); - if (!exp) break; - auto value = singleValueFrom(exp); - classDecl = value->getByPath(); - } while (false); - } else if (auto expList = stmt->content.as()) { - auto value = singleValueFrom(expList); - classDecl = value->getByPath(); - } - if (classDecl) { - if (auto variable = classDecl->name->item.as()) { - auto className = toString(variable); - if (!className.empty()) { - if (std::isupper(className[0]) && capital) { - capital->decls.push_back(className); - } else if (any) { - any->decls.push_back(className); - } - } - } - } - } - } - if (implicitReturn) { - auto last = static_cast(nodes.back()); - if (ast_is(last->content) && (!last->appendix || - !last->appendix->item.is())) { - auto expList = static_cast(last->content.get()); - auto expListLow = new_ptr(); - expListLow->exprs = expList->exprs; - auto returnNode = new_ptr(); - returnNode->valueList.set(expListLow); - last->content.set(returnNode); - } - } - str_list temp; - for (auto node : nodes) { - transformStatement(static_cast(node), temp); - } - out.push_back(join(temp)); - } - - void transformBody(Body_t* body, str_list& out, bool implicitReturn = false) { - if (auto stmt = body->content.as()) { - transformCodes(std::list{stmt}, out, implicitReturn); - } else { - transformCodes(body->content.to()->statements.objects(), out, implicitReturn); - } - } - - void transformBlock(Block_t* block, str_list& out, bool implicitReturn = true) { - transformCodes(block->statements.objects(), out, implicitReturn); - } - - void transformReturn(Return_t* returnNode, str_list& out) { - if (auto valueList = returnNode->valueList.get()) { - if (auto singleValue = singleValueFrom(valueList)) { - if (auto simpleValue = singleValue->item.as()) { - if (auto comp = simpleValue->value.as()) { - transformCompReturn(comp, out); - return; - } - if (auto comp = simpleValue->value.as()) { - transformTblCompReturn(comp, out); - return; - } - if (auto with = simpleValue->value.as()) { - transformWith(with, out, nullptr, true); - return; - } - if (auto classDecl = simpleValue->value.as()) { - transformClassDecl(classDecl, out, ExpUsage::Return); - return; - } - if (auto doNode = simpleValue->value.as()) { - transformDo(doNode, out, true); - return; - } - if (auto switchNode = simpleValue->value.as()) { - transformSwitch(switchNode, out, true); - return; - } - if (auto ifNode = simpleValue->value.as()) { - transformIf(ifNode, out, IfUsage::Return); - return; - } - if (auto unlessNode = simpleValue->value.as()) { - transformUnless(unlessNode, out, IfUsage::Return); - return; - } - } - if (auto chainValue = singleValue->item.as()) { - if (isColonChain(chainValue)) { - transformColonChain(chainValue, out, ExpUsage::Return); - return; - } - } - transformValue(singleValue, out); - out.back() = indent() + s("return "sv) + out.back() + nlr(returnNode); - return; - } else { - str_list temp; - transformExpListLow(valueList, temp); - out.push_back(indent() + s("return "sv) + temp.back() + nlr(returnNode)); - } - } else { - out.push_back(indent() + s("return"sv) + nll(returnNode)); - } - } - - void transformFnArgsDef(FnArgsDef_t* argsDef, str_list& out) { - if (!argsDef->defList) { - out.push_back(Empty); - out.push_back(Empty); - } else { - transformFnArgDefList(argsDef->defList, out); - } - if (argsDef->shadowOption) { - transform_outer_var_shadow(argsDef->shadowOption); - } - } - - void transform_outer_var_shadow(outer_var_shadow_t* shadow) { - markVarShadowed(); - if (shadow->varList) { - for (auto name : shadow->varList->names.objects()) { - addToAllowList(toString(name)); - } - } - } - - void transformFnArgDefList(FnArgDefList_t* argDefList, str_list& out) { - struct ArgItem { - std::string name; - std::string assignSelf; - }; - std::list argItems; - str_list temp; - std::string varNames; - bool assignSelf = false; - for (auto _def : argDefList->definitions.objects()) { - auto def = static_cast(_def); - auto& arg = argItems.emplace_back(); - switch (def->name->getId()) { - case "Variable"_id: arg.name = toString(def->name); break; - case "SelfName"_id: { - assignSelf = true; - auto selfName = static_cast(def->name.get()); - switch (selfName->name->getId()) { - case "self_class_name"_id: - arg.name = toString(selfName->name->getFirstChild()); - arg.assignSelf = s("self.__class."sv) + arg.name; - break; - case "self_class"_id: - arg.name = "self.__class"sv; - break; - case "self_name"_id: - arg.name = toString(selfName->name->getFirstChild()); - arg.assignSelf = s("self."sv) + arg.name; - break; - case "self"_id: - arg.name = "self"sv; - break; - default: break; - } - break; - } - } - forceAddToScope(arg.name); - if (def->defaultValue) { - pushScope(); - auto expList = toAst(arg.name, ExpList); - auto assign = new_ptr(); - assign->values.push_back(def->defaultValue.get()); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assign); - transformAssignment(assignment, temp); - popScope(); - _buf << indent() << "if "sv << arg.name << " == nil then"sv << nll(def); - _buf << temp.back(); - _buf << indent() << "end"sv << nll(def); - temp.back() = clearBuf(); - } - if (varNames.empty()) varNames = arg.name; - else varNames.append(s(", "sv) + arg.name); - } - if (argDefList->varArg) { - auto& arg = argItems.emplace_back(); - arg.name = "..."sv; - if (varNames.empty()) varNames = arg.name; - else varNames.append(s(", "sv) + arg.name); - } - std::string initCodes = join(temp); - if (assignSelf) { - auto sjoin = [](const decltype(argItems)& items, int index) { - std::string result; - for (auto it = items.begin(); it != items.end(); ++it) { - if (it->assignSelf.empty()) continue; - if (result.empty()) result = (&it->name)[index]; - else result.append(s(", "sv) + (&it->name)[index]); - } - return result; - }; - std::string sleft = sjoin(argItems, 1); - std::string sright = sjoin(argItems, 0); - initCodes.append(indent() + sleft + s(" = "sv) + sright + nll(argDefList)); - } - out.push_back(varNames); - out.push_back(initCodes); - } - - void transformSelfName(SelfName_t* selfName, str_list& out, bool invoke) { - auto name = selfName->name.get(); - switch (name->getId()) { - case "self_class_name"_id: - out.push_back(s("self.__class"sv) + s(invoke ? ":"sv : "."sv) + toString(name->getFirstChild())); - break; - case "self_class"_id: - out.push_back(s("self.__class"sv)); - break; - case "self_name"_id: - out.push_back(s("self"sv) + s(invoke ? ":"sv : "."sv) + toString(name->getFirstChild())); - break; - case "self"_id: - out.push_back(s("self"sv)); - break; - } - } - - void transformColonChainClosure(ChainValue_t* chainValue, str_list& out) { - str_list temp; - temp.push_back(s("(function()"sv) + nll(chainValue)); - pushScope(); - transformColonChain(chainValue, temp, ExpUsage::Return); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformColonChain(ChainValue_t* chainValue, str_list& out, ExpUsage usage = ExpUsage::Common, ExpList_t* expList = nullptr) { - str_list temp; - const auto& chainList = chainValue->items.objects(); - auto end = --chainList.end(); - for (auto it = chainList.begin(); it != end; ++it) { - auto item = *it; - switch (item->getId()) { - case "Invoke"_id: transformInvoke(static_cast(item), temp); break; - case "DotChainItem"_id: transformDotChainItem(static_cast(item), temp); break; - case "ColonChainItem"_id: transformColonChainItem(static_cast(item), temp); break; - case "Slice"_id: transformSlice(static_cast(item), temp); break; - case "Callable"_id: { - auto next = it; ++next; - auto followItem = next != chainList.end() ? *next : nullptr; - transformCallable(static_cast(item), temp, - followItem && ast_is(followItem)); - break; - } - case "String"_id: - transformString(static_cast(item), temp); - temp.back() = s("("sv) + temp.back() + s(")"sv); - break; - case "Exp"_id: - transformExp(static_cast(item), temp); - temp.back() = s("["sv) + temp.back() + s("]"sv); - break; - default: break; - } - } - auto caller = join(temp); - auto colonChainItem = static_cast(chainList.back()); - auto funcName = toString(colonChainItem->name); - std::string assignList; - if (expList) { - str_list tmp; - transformExpList(expList, tmp); - assignList = tmp.back(); - } - if (usage != ExpUsage::Return) pushScope(); - auto baseVar = getUnusedName("_base_"sv); - addToScope(baseVar); - auto fnVar = getUnusedName("_fn_"sv); - addToScope(fnVar); - if (usage != ExpUsage::Return) { - _buf << indent(-1) << "do"sv << nll(chainValue); - } - _buf << indent() << "local "sv << baseVar << " = "sv << caller << nll(chainValue); - _buf << indent() << "local "sv << fnVar << " = "sv << baseVar; - if (State::luaKeywords.find(funcName) != State::luaKeywords.end()) { - _buf << "[\""sv << funcName << "\"]" << nll(chainValue); - } else { - _buf << "."sv << funcName << nll(chainValue); - } - switch (usage) { - case ExpUsage::Return: - _buf << indent() << "return function(...)" << nll(chainValue); - break; - case ExpUsage::Assignment: - _buf << indent() << assignList << " = function(...)"sv << nll(chainValue); - break; - case ExpUsage::Common: - _buf << indent() << "_ = function(...)" << nll(chainValue); - break; - default: break; - } - _buf << indent(1) << "return "sv << fnVar << "("sv << baseVar << ", ...)"sv << nll(chainValue); - _buf << indent() << "end"sv << nll(chainValue); - if (usage != ExpUsage::Return) { - popScope(); - _buf << indent() << "end"sv << nll(chainValue); - } - out.push_back(clearBuf()); - } - - void transformChainList(const std::list& chainList, str_list& out) { - str_list temp; - switch (chainList.front()->getId()) { - case "DotChainItem"_id: - case "ColonChainItem"_id: - if (_withVars.empty()) { - throw std::logic_error("Short-dot syntax must be called within a with block."); - } else { - temp.push_back(_withVars.top()); - } - break; - } - for (auto it = chainList.begin(); it != chainList.end(); ++it) { - auto item = *it; - switch (item->getId()) { - case "Invoke"_id: - transformInvoke(static_cast(item), temp); - break; - case "DotChainItem"_id: - transformDotChainItem(static_cast(item), temp); - break; - case "ColonChainItem"_id: - transformColonChainItem(static_cast(item), temp); - break; - case "Slice"_id: - transformSlice(static_cast(item), temp); - break; - case "Callable"_id: { - auto next = it; ++next; - auto followItem = next != chainList.end() ? *next : nullptr; - transformCallable(static_cast(item), temp, - followItem && ast_is(followItem)); - break; - } - case "String"_id: - transformString(static_cast(item), temp); - temp.back() = s("("sv) + temp.back() + s(")"sv); - break; - case "Exp"_id: - transformExp(static_cast(item), temp); - temp.back() = s("["sv) + temp.back() + s("]"sv); - break; - case "InvokeArgs"_id: transformInvokeArgs(static_cast(item), temp); break; - default: break; - } - } - out.push_back(join(temp)); - } - - void transformChainValue(ChainValue_t* chainValue, str_list& out) { - transformChainList(chainValue->items.objects(), out); - } - - void transformAssignableChain(AssignableChain_t* chain, str_list& out) { - transformChainList(chain->items.objects(), out); - } - - void transformDotChainItem(DotChainItem_t* dotChainItem, str_list& out) { - auto name = toString(dotChainItem->name); - if (State::keywords.find(name) != State::keywords.end()) { - out.push_back(s("[\""sv) + name + s("\"]"sv)); - } else { - out.push_back(s("."sv) + name); - } - } - - void transformColonChainItem(ColonChainItem_t* colonChainItem, str_list& out) { - auto name = toString(colonChainItem->name); - out.push_back(s(colonChainItem->switchToDot ? "."sv : ":"sv) + name); - } - - void transformSlice(Slice_t* slice, str_list& out) { - throw std::logic_error("Slice syntax not supported here"); - } - - void transformInvoke(Invoke_t* invoke, str_list& out) { - str_list temp; - for (auto arg : invoke->args.objects()) { - switch (arg->getId()) { - case "Exp"_id: transformExp(static_cast(arg), temp); break; - case "SingleString"_id: transformSingleString(static_cast(arg), temp); break; - case "DoubleString"_id: transformDoubleString(static_cast(arg), temp); break; - case "LuaString"_id: transformLuaString(static_cast(arg), temp); break; - default: break; - } - } - out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); - } - - void transform_unary_exp(unary_exp_t* unary_exp, str_list& out) { - std::string op = toString(unary_exp->m_begin.m_it, unary_exp->item->m_begin.m_it); - str_list temp{op + (op == "not"sv ? s(" "sv) : Empty)}; - transformExp(unary_exp->item, temp); - out.push_back(join(temp)); - } - - void transformVariable(Variable_t* name, str_list& out) { - out.push_back(toString(name)); - } - - void transformNum(Num_t* num, str_list& out) { - out.push_back(toString(num)); - } - - void transformTableLit(TableLit_t* table, str_list& out) { - transformTable(table, table->values.objects(), out); - } - - void transformCompCommon(Comprehension_t* comp, str_list& out) { - str_list temp; - auto compInner = comp->forLoop.get(); - for (auto item : compInner->items.objects()) { - switch (item->getId()) { - case "CompForEach"_id: - transformCompForEach(static_cast(item), temp); - break; - case "CompFor"_id: - transformCompFor(static_cast(item), temp); - break; - case "Exp"_id: - transformExp(static_cast(item), temp); - temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); - pushScope(); - break; - } - } - if (auto stmt = comp->value.as()) { - transformStatement(stmt, temp); - } else if (auto exp = comp->value.as()) { - auto expList = new_ptr(); - expList->exprs.push_back(exp); - auto statement = new_ptr(); - statement->content.set(expList); - transformStatement(statement, temp); - } - auto value = temp.back(); - temp.pop_back(); - _buf << join(temp) << value; - for (size_t i = 0; i < compInner->items.objects().size(); ++i) { - popScope(); - _buf << indent() << "end"sv << nll(comp); - } - out.push_back(clearBuf()); - } - - void transformComprehension(Comprehension_t* comp, str_list& out) { - str_list temp; - std::string accum = getUnusedName("_accum_"); - std::string len = getUnusedName("_len_"); - addToScope(accum); - addToScope(len); - auto compInner = comp->forLoop.get(); - for (auto item : compInner->items.objects()) { - switch (item->getId()) { - case "CompForEach"_id: - transformCompForEach(static_cast(item), temp); - break; - case "CompFor"_id: - transformCompFor(static_cast(item), temp); - break; - case "Exp"_id: - transformExp(static_cast(item), temp); - temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); - pushScope(); - break; - } - } - transformExp(comp->value.to(), temp); - auto value = temp.back(); - temp.pop_back(); - for (size_t i = 0; i < compInner->items.objects().size(); ++i) { - popScope(); - } - _buf << indent() << "local "sv << accum << " = { }"sv << nll(comp); - _buf << indent() << "local "sv << len << " = 1"sv << nll(comp); - _buf << join(temp); - _buf << indent(int(temp.size())) << accum << "["sv << len << "] = "sv << value << nll(comp); - _buf << indent(int(temp.size())) << len << " = "sv << len << " + 1"sv << nll(comp); - for (int ind = int(temp.size()) - 1; ind > -1 ; --ind) { - _buf << indent(ind) << "end"sv << nll(comp); - } - out.push_back(accum); - out.push_back(clearBuf()); - } - - void transformCompInPlace(Comprehension_t* comp, ExpList_t* expList, str_list& out) { - str_list temp; - pushScope(); - transformComprehension(comp, temp); - auto assign = new_ptr(); - assign->values.push_back(toAst(temp.front(), Exp)); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assign); - assignment->m_begin.m_line = comp->m_end.m_line; - assignment->m_end.m_line = comp->m_end.m_line; - transformAssignment(assignment, temp); - out.push_back( - s("do"sv) + nll(comp) + - *(++temp.begin()) + - temp.back()); - popScope(); - out.back() = out.back() + indent() + s("end"sv) + nlr(comp); - } - - void transformCompReturn(Comprehension_t* comp, str_list& out) { - str_list temp; - transformComprehension(comp, temp); - out.push_back(temp.back() + indent() + s("return "sv) + temp.front() + nlr(comp)); - } - - void transformCompClosure(Comprehension_t* comp, str_list& out) { - str_list temp; - std::string before = s("(function()"sv) + nll(comp); - pushScope(); - transformComprehension(comp, temp); - out.push_back( - before + - temp.back() + - indent() + s("return "sv) + temp.front() + nlr(comp)); - popScope(); - out.back() = out.back() + indent() + s("end)()"sv); - } - - void transformForEachHead(AssignableNameList_t* nameList, ast_node* loopTarget, str_list& out) { - str_list temp; - str_list vars; - std::list>> destructPairs; - for (auto _item : nameList->items.objects()) { - auto item = static_cast(_item)->item.get(); - switch (item->getId()) { - case "Variable"_id: - transformVariable(static_cast(item), vars); - break; - case "TableLit"_id: { - auto desVar = getUnusedName("_des_"sv); - destructPairs.emplace_back(item, toAst(desVar, Exp)); - vars.push_back(desVar); - break; - } - default: break; - } - } - str_list varBefore, varAfter; - switch (loopTarget->getId()) { - case "star_exp"_id: { - auto star_exp = static_cast(loopTarget); - bool newListVal = false; - auto listVar = variableFrom(star_exp->value); - auto indexVar = getUnusedName("_index_"); - varAfter.push_back(indexVar); - auto value = singleValueFrom(star_exp->value); - if (!value) throw std::logic_error("Invalid star syntax"); - bool endWithSlice = false; - do { - auto chainValue = value->item.as(); - if (!chainValue) break; - auto chainList = chainValue->items.objects(); - auto slice = ast_cast(chainList.back()); - if (!slice) break; - endWithSlice = true; - if (listVar.empty() && chainList.size() == 2 && - ast_is(chainList.front())) { - listVar = toString(ast_to(chainList.front())->item); - } - chainList.pop_back(); - auto chain = new_ptr(); - for (auto item : chainList) { - chain->items.push_back(item); - } - std::string startValue("1"sv); - if (auto exp = slice->startValue.as()) { - transformExp(exp, temp); - startValue = temp.back(); - temp.pop_back(); - } - std::string stopValue; - if (auto exp = slice->stopValue.as()) { - transformExp(exp, temp); - stopValue = temp.back(); - temp.pop_back(); - } - std::string stepValue; - if (auto exp = slice->stepValue.as()) { - transformExp(exp, temp); - stepValue = temp.back(); - temp.pop_back(); - } - if (newListVal) { - transformChainValue(chain, temp); - _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); - } - std::string maxVar; - if (!stopValue.empty()) { - maxVar = getUnusedName("_max_"); - varBefore.push_back(maxVar); - _buf << indent() << "local "sv << maxVar << " = "sv << stopValue << nll(nameList); - } - _buf << indent() << "for "sv << indexVar << " = "sv; - _buf << startValue << ", "sv; - if (stopValue.empty()) { - _buf << "#"sv << listVar; - } else { - _buf << maxVar << " < 0 and #"sv << listVar <<" + " << maxVar << " or "sv << maxVar; - } - if (!stepValue.empty()) { - _buf << ", "sv << stepValue; - } - _buf << " do"sv << nlr(loopTarget); - _buf << indent(1) << "local "sv << join(vars, ", "sv) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); - out.push_back(clearBuf()); - } while (false); - if (listVar.empty()) { - newListVal = true; - listVar = getUnusedName("_list_"); - varBefore.push_back(listVar); - } - if (!endWithSlice) { - transformExp(star_exp->value, temp); - if (newListVal) _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); - _buf << indent() << "for "sv << indexVar << " = 1, #"sv << listVar << " do"sv << nlr(loopTarget); - _buf << indent(1) << "local "sv << join(vars) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); - out.push_back(clearBuf()); - } - break; - } - case "Exp"_id: - transformExp(static_cast(loopTarget), temp); - _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); - out.push_back(clearBuf()); - break; - case "ExpList"_id: - transformExpList(static_cast(loopTarget), temp); - _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); - out.push_back(clearBuf()); - break; - default: break; - } - if (!destructPairs.empty()) { - temp.clear(); - pushScope(); - for (auto& pair : destructPairs) { - auto sValue = new_ptr(); - sValue->value.set(pair.first); - auto value = new_ptr(); - value->item.set(sValue); - auto exp = new_ptr(); - exp->value.set(value); - auto expList = new_ptr(); - expList->exprs.push_back(exp); - auto assign = new_ptr(); - assign->values.push_back(pair.second); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assign); - transformAssignment(assignment, temp); - } - out.back().append(join(temp)); - popScope(); - } - for (auto& var : varBefore) addToScope(var); - pushScope(); - for (auto& var : varAfter) addToScope(var); - } - - void transformCompForEach(CompForEach_t* comp, str_list& out) { - transformForEachHead(comp->nameList, comp->loopValue, out); - } - - void transformInvokeArgs(InvokeArgs_t* invokeArgs, str_list& out) { - str_list temp; - for (auto arg : invokeArgs->args.objects()) { - switch (arg->getId()) { - case "Exp"_id: transformExp(static_cast(arg), temp); break; - case "TableBlock"_id: transformTableBlock(static_cast(arg), temp); break; - default: break; - } - } - out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); - } - - void transformForHead(For_t* forNode, str_list& out) { - str_list temp; - std::string varName = toString(forNode->varName); - transformExp(forNode->startValue, temp); - transformExp(forNode->stopValue, temp); - if (forNode->stepValue) { - transformExp(forNode->stepValue->value, temp); - } else { - temp.emplace_back(); - } - auto it = temp.begin(); - const auto& start = *it; - const auto& stop = *(++it); - const auto& step = *(++it); - _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(forNode); - out.push_back(clearBuf()); - } - - void transformLoopBody(Body_t* body, str_list& out) { - str_list temp; - bool withContinue = traversal::Stop == body->traverse([&](ast_node* node) { - switch (node->getId()) { - case "For"_id: - case "ForEach"_id: - return traversal::Return; - case "BreakLoop"_id: { - return toString(node) == "continue"sv ? - traversal::Stop : traversal::Return; - } - default: - return traversal::Continue; - } - }); - if (withContinue) { - auto continueVar = getUnusedName("_continue_"sv); - addToScope(continueVar); - _buf << indent() << "local "sv << continueVar << " = false"sv << nll(body); - _buf << indent() << "repeat"sv << nll(body); - temp.push_back(clearBuf()); - _continueVars.push(continueVar); - pushScope(); - } - transformBody(body, temp); - if (withContinue) { - _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(); - } - out.push_back(join(temp)); - } - - void transformFor(For_t* forNode, str_list& out) { - str_list temp; - transformForHead(forNode, temp); - pushScope(); - transformLoopBody(forNode->body, temp); - popScope(); - out.push_back(join(temp) + indent() + s("end"sv) + nlr(forNode)); - } - - void transformForClosure(For_t* forNode, str_list& out) { - str_list temp; - std::string accum = getUnusedName("_accum_"); - std::string len = getUnusedName("_len_"); - addToScope(accum); - addToScope(len); - _buf << "(function()"sv << nll(forNode); - pushScope(); - _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); - _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); - temp.push_back(clearBuf()); - transformForHead(forNode, temp); - auto last = lastStatementFrom(forNode->body); - bool hasTableItem = ast_is(last->content); - if (hasTableItem) { - _buf << accum << "["sv << len << "]"sv; - std::string assignLeft = clearBuf(); - auto expList = toAst(assignLeft, ExpList); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - auto assign = new_ptr(); - assign->values.dup(ast_cast(last->content)->exprs); - assignment->target.set(assign); - last->content.set(assignment); - } - pushScope(); - transformLoopBody(forNode->body, temp); - temp.push_back(indent() + len + s(" = "sv) + len + s(" + 1"sv) + nlr(forNode->body)); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(forNode) + 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) { - str_list temp; - std::string accum = getUnusedName("_accum_"); - std::string len = getUnusedName("_len_"); - _buf << indent() << "do"sv << nll(forNode); - pushScope(); - addToScope(accum); - addToScope(len); - _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); - _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); - temp.push_back(clearBuf()); - transformForHead(forNode, temp); - auto last = lastStatementFrom(forNode->body); - bool hasTableItem = ast_is(last->content); - if (hasTableItem) { - _buf << accum << "["sv << len << "]"sv; - std::string assignLeft = clearBuf(); - auto expList = toAst(assignLeft, ExpList); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - auto assign = new_ptr(); - assign->values.dup(ast_cast(last->content)->exprs); - assignment->target.set(assign); - last->content.set(assignment); - } - pushScope(); - transformLoopBody(forNode->body, temp); - temp.push_back(indent() + len + s(" = "sv) + len + s(" + 1"sv) + nlr(forNode->body)); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(forNode)); - auto assign = new_ptr(); - assign->values.push_back(toAst(accum, Exp)); - auto assignment = new_ptr(); - assignment->assignable.set(assignExpList); - assignment->target.set(assign); - assignment->m_begin.m_line = forNode->m_end.m_line; - assignment->m_end.m_line = forNode->m_end.m_line; - transformAssignment(assignment, temp); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(forNode)); - out.push_back(join(temp)); - } - - void transformBinaryOperator(BinaryOperator_t* node, str_list& out) { - auto op = toString(node); - out.push_back(op == "!="sv ? s("~="sv) : op); - } - - void transformForEach(ForEach_t* forEach, str_list& out) { - str_list temp; - transformForEachHead(forEach->nameList, forEach->loopValue, temp); - transformLoopBody(forEach->body, temp); - popScope(); - out.push_back(temp.front() + temp.back() + indent() + s("end"sv) + nlr(forEach)); - } - - void transformForEachClosure(ForEach_t* forEach, str_list& out) { - str_list temp; - std::string accum = getUnusedName("_accum_"); - std::string len = getUnusedName("_len_"); - _buf << "(function()"sv << nll(forEach); - pushScope(); - addToScope(accum); - addToScope(len); - _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); - _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); - temp.push_back(clearBuf()); - transformForEachHead(forEach->nameList, forEach->loopValue, temp); - auto last = lastStatementFrom(forEach->body); - bool hasTableItem = ast_is(last->content); - if (hasTableItem) { - _buf << accum << "["sv << len << "]"sv; - std::string assignLeft = clearBuf(); - auto expList = toAst(assignLeft, ExpList); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - auto assign = new_ptr(); - assign->values.dup(ast_cast(last->content)->exprs); - assignment->target.set(assign); - last->content.set(assignment); - } - transformLoopBody(forEach->body, temp); - temp.push_back(indent() + len + s(" = "sv) + len + s(" + 1"sv) + nlr(forEach->body)); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(forEach) + 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) { - str_list temp; - std::string accum = getUnusedName("_accum_"); - std::string len = getUnusedName("_len_"); - _buf << indent() << "do"sv << nll(forEach); - pushScope(); - addToScope(accum); - addToScope(len); - _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); - _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); - temp.push_back(clearBuf()); - transformForEachHead(forEach->nameList, forEach->loopValue, temp); - auto last = lastStatementFrom(forEach->body); - bool hasTableItem = ast_is(last->content); - if (hasTableItem) { - _buf << accum << "["sv << len << "]"sv; - std::string assignLeft = clearBuf(); - auto expList = toAst(assignLeft, ExpList); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - auto assign = new_ptr(); - assign->values.dup(ast_cast(last->content)->exprs); - assignment->target.set(assign); - last->content.set(assignment); - } - transformLoopBody(forEach->body, temp); - temp.push_back(indent() + len + s(" = "sv) + len + s(" + 1"sv) + nlr(forEach->body)); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(forEach)); - auto assign = new_ptr(); - assign->values.push_back(toAst(accum, Exp)); - auto assignment = new_ptr(); - assignment->assignable.set(assignExpList); - assignment->target.set(assign); - assignment->m_begin.m_line = forEach->m_end.m_line; - assignment->m_end.m_line = forEach->m_end.m_line; - transformAssignment(assignment, temp); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(forEach)); - out.push_back(join(temp)); - } - - void transform_variable_pair(variable_pair_t* pair, str_list& out) { - auto name = toString(pair->name); - out.push_back(name + s(" = "sv) + name); - } - - void transform_normal_pair(normal_pair_t* pair, str_list& out) { - auto key = pair->key.get(); - str_list temp; - switch (key->getId()) { - case "KeyName"_id: { - transformKeyName(static_cast(key), temp); - if (State::luaKeywords.find(temp.back()) != State::luaKeywords.end()) { - temp.back() = s("[\""sv) + temp.back() + s("\"]"); - } - break; - } - case "Exp"_id: - transformExp(static_cast(key), temp); - temp.back() = s("["sv) + temp.back() + s("]"sv); - break; - case "DoubleString"_id: - transformDoubleString(static_cast(key), temp); - temp.back() = s("["sv) + temp.back() + s("]"sv); - break; - case "SingleString"_id: transformSingleString(static_cast(key), temp); - temp.back() = s("["sv) + temp.back() + s("]"sv); - break; - default: break; - } - auto value = pair->value.get(); - switch (value->getId()) { - case "Exp"_id: transformExp(static_cast(value), temp); break; - case "TableBlock"_id: transformTableBlock(static_cast(value), temp); break; - default: break; - } - out.push_back(temp.front() + s(" = "sv) + temp.back()); - } - - void transformKeyName(KeyName_t* keyName, str_list& out) { - auto name = keyName->name.get(); - switch (name->getId()) { - case "SelfName"_id: transformSelfName(static_cast(name), out, false); break; - case "Name"_id: out.push_back(toString(name)); break; - default: break; - } - } - - void replace(std::string& str, std::string_view from, std::string_view to) { - size_t start_pos = 0; - while((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.size(), to); - start_pos += to.size(); - } - } - - void transformLuaString(LuaString_t* luaString, str_list& out) { - auto content = toString(luaString->content); - replace(content, "\r"sv, ""); - if (content[0] == '\n') content.erase(content.begin()); - out.push_back(toString(luaString->open) + content + toString(luaString->close)); - } - - void transformSingleString(SingleString_t* singleString, str_list& out) { - auto str = toString(singleString); - replace(str, "\r"sv, ""); - replace(str, "\n"sv, "\\n"sv); - out.push_back(str); - } - - void transformDoubleString(DoubleString_t* doubleString, str_list& out) { - str_list temp; - for (auto _seg : doubleString->segments.objects()) { - auto seg = static_cast(_seg); - auto content = seg->content.get(); - switch (content->getId()) { - case "double_string_inner"_id: { - auto str = toString(content); - replace(str, "\r"sv, ""); - replace(str, "\n"sv, "\\n"sv); - temp.push_back(s("\""sv) + str + s("\""sv)); - break; - } - case "Exp"_id: - transformExp(static_cast(content), temp); - temp.back() = s("tostring("sv) + temp.back() + s(")"sv); - break; - default: break; - } - } - out.push_back(temp.empty() ? s("\"\""sv) : join(temp, " .. "sv)); - } - - void transformString(String_t* string, str_list& out) { - auto str = string->str.get(); - switch (str->getId()) { - case "SingleString"_id: transformSingleString(static_cast(str), out); break; - case "DoubleString"_id: transformDoubleString(static_cast(str), out); break; - case "LuaString"_id: transformLuaString(static_cast(str), out); break; - default: break; - } - } - - std::pair defineClassVariable(Assignable_t* assignable) { - if (assignable->item->getId() == "Variable"_id) { - auto variable = static_cast(assignable->item.get()); - auto name = toString(variable); - if (addToScope(name)) { - return {name, true}; - } else { - return {name, false}; - } - } - return {Empty, false}; - } - - void transformClassDeclClosure(ClassDecl_t* classDecl, str_list& out) { - str_list temp; - temp.push_back(s("(function()"sv) + nll(classDecl)); - pushScope(); - transformClassDecl(classDecl, temp, ExpUsage::Return); - popScope(); - temp.push_back(s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformClassDecl(ClassDecl_t* classDecl, str_list& out, ExpUsage usage = ExpUsage::Common, ExpList_t* expList = nullptr) { - str_list temp; - auto body = classDecl->body.get(); - auto assignable = classDecl->name.get(); - auto extend = classDecl->extend.get(); - std::string className; - std::string assignItem; - if (assignable) { - bool newDefined = false; - std::tie(className, newDefined) = defineClassVariable(assignable); - if (newDefined) { - temp.push_back(indent() + s("local "sv) + className + nll(classDecl)); - } - if (className.empty()) { - if (auto chain = ast_cast(assignable->item)) { - if (auto dotChain = ast_cast(chain->items.back())) { - className = s("\""sv) + toString(dotChain->name) + s("\""sv); - } else if (auto index = ast_cast(chain->items.back())) { - if (auto name = index->getByPath()) { - transformString(name, temp); - className = temp.back(); - temp.pop_back(); - } - } - } - } else { - className = s("\""sv) + className + s("\""sv); - } - pushScope(); - transformAssignable(assignable, temp); - popScope(); - assignItem = temp.back(); - temp.pop_back(); - } else if (expList) { - auto name = variableFrom(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; - body->traverse([&](ast_node* node) { - if (node->getId() == "Statement"_id) { - ClassDecl_t* clsDecl = nullptr; - if (auto assignment = node->getByPath()) { - auto names = transformAssignDefs(assignment->assignable.get()); - varDefs.insert(varDefs.end(), names.begin(), names.end()); - auto info = extractDestructureInfo(assignment); - if (!info.first.empty()) { - for (const auto& destruct : info.first) - for (const auto& item : destruct.items) - if (item.isVariable && addToScope(item.name)) - varDefs.push_back(item.name); - } - do { - auto assign = assignment->target.as(); - if (!assign) break; - if (assign->values.objects().size() != 1) break; - auto exp = ast_cast(assign->values.objects().front()); - if (!exp) break; - auto value = singleValueFrom(exp); - clsDecl = value->getByPath(); - } while (false); - } else if (auto expList = node->getByPath()) { - 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); - } - return traversal::Return; - } - return traversal::Continue; - }); - if (!varDefs.empty()) { - temp.push_back(indent() + s("local ") + join(varDefs, ", "sv) + nll(body)); - } - } - std::string parent, parentVar; - if (extend) { - parentVar = getUnusedName("_parent_"sv); - addToScope(parentVar); - transformExp(extend, temp); - parent = temp.back(); - temp.pop_back(); - temp.push_back(indent() + s("local "sv) + parentVar + s(" = "sv) + parent + nll(classDecl)); - } - auto baseVar = getUnusedName("_base_"sv); - addToScope(baseVar); - temp.push_back(indent() + s("local "sv) + baseVar + s(" = "sv)); - str_list builtins; - str_list commons; - str_list statements; - if (body) { - std::list members; - for (auto content : classDecl->body->contents.objects()) { - switch (content->getId()) { - case "class_member_list"_id: { - size_t inc = transform_class_member_list(static_cast(content), members, classVar); - auto it = members.end(); - for (size_t i = 0; i < inc; ++i, --it); - for (; it != members.end(); ++it) { - auto& member = *it; - if (member.type == MemType::Property) { - statements.push_back(indent() + member.item + nll(content)); - } else { - member.item = indent(1) + member.item; - } - } - break; - } - case "Statement"_id: - transformStatement(static_cast(content), statements); - break; - default: break; - } - } - for (auto& member : members) { - switch (member.type) { - case MemType::Common: - commons.push_back((commons.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); - break; - case MemType::Builtin: - builtins.push_back((builtins.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); - break; - default: break; - } - } - if (!commons.empty()) { - temp.back() += s("{"sv) + nll(body); - temp.push_back(join(commons) + nll(body)); - temp.push_back(indent() + s("}"sv) + nll(body)); - } else { - temp.back() += s("{ }"sv) + nll(body); - } - } else { - temp.back() += s("{ }"sv) + nll(classDecl); - } - temp.push_back(indent() + baseVar + s(".__index = "sv) + baseVar + nll(classDecl)); - str_list tmp; - if (usage == ExpUsage::Assignment) { - auto assign = new_ptr(); - assign->values.push_back(toAst(classVar, Exp)); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assign); - transformAssignment(assignment, tmp); - } - if (extend) { - _buf << indent() << "setmetatable("sv << baseVar << ", "sv << parentVar << ".__base)"sv << nll(classDecl); - } - _buf << indent() << classVar << " = setmetatable({" << nll(classDecl); - if (!builtins.empty()) { - _buf << join(builtins) << ","sv << nll(classDecl); - } else { - if (extend) { - _buf << indent(1) << "__init = function(self, ...)"sv << nll(classDecl); - _buf << indent(2) << "return _class_0.__parent.__init(self, ...)"sv << nll(classDecl); - _buf << indent(1) << "end,"sv << nll(classDecl); - } else { - _buf << indent(1) << "__init = function() end,"sv << nll(classDecl); - } - } - _buf << indent(1) << "__base = "sv << baseVar; - if (!className.empty()) { - _buf << ","sv << nll(classDecl) << indent(1) << "__name = "sv << className << (extend ? s(","sv) : Empty) << nll(classDecl); - } else { - _buf << nll(classDecl); - } - if (extend) { - _buf << indent(1) << "__parent = "sv << parentVar << nll(classDecl); - } - _buf << indent() << "}, {"sv << nll(classDecl); - if (extend) { - _buf << indent(1) << "__index = function(cls, name)"sv << nll(classDecl); - _buf << indent(2) << "local val = rawget("sv << baseVar << ", name)"sv << nll(classDecl); - _buf << indent(2) << "if val == nil then"sv << nll(classDecl); - _buf << indent(3) << "local parent = rawget(cls, \"__parent\")"sv << nll(classDecl); - _buf << indent(3) << "if parent then"sv << nll(classDecl); - _buf << indent(4) << "return parent[name]"sv << nll(classDecl); - _buf << indent(3) << "end"sv << nll(classDecl); - _buf << indent(2) << "else"sv << nll(classDecl); - _buf << indent(3) << "return val"sv << nll(classDecl); - _buf << indent(2) << "end"sv << nll(classDecl); - _buf << indent(1) << "end,"sv << nll(classDecl); - } else { - _buf << indent(1) << "__index = "sv << baseVar << ","sv << nll(classDecl); - } - _buf << indent(1) << "__call = function(cls, ...)"sv << nll(classDecl); - pushScope(); - auto selfVar = getUnusedName("_self_"sv); - addToScope(selfVar); - _buf << indent(1) << "local " << selfVar << " = setmetatable({}, "sv << baseVar << ")"sv << nll(classDecl); - _buf << indent(1) << "cls.__init("sv << selfVar << ", ...)"sv << nll(classDecl); - _buf << indent(1) << "return "sv << selfVar << nll(classDecl); - popScope(); - _buf << indent(1) << "end"sv << nll(classDecl); - _buf << indent() << "})"sv << nll(classDecl); - _buf << indent() << baseVar << ".__class = "sv << classVar << nll(classDecl); - if (!statements.empty()) _buf << indent() << "local self = "sv << classVar << nll(classDecl); - _buf << join(statements); - if (extend) { - _buf << indent() << "if "sv << parentVar << ".__inherited then"sv << nll(classDecl); - _buf << indent(1) << parentVar << ".__inherited("sv << parentVar << ", "sv << classVar << ")"sv << nll(classDecl); - _buf << indent() << "end"sv << nll(classDecl); - } - if (!assignItem.empty()) { - _buf << indent() << assignItem << " = "sv << classVar << nll(classDecl); - } - switch (usage) { - case ExpUsage::Return: { - _buf << indent() << "return "sv << classVar << nlr(classDecl); - break; - } - case ExpUsage::Assignment: { - _buf << tmp.back(); - break; - } - default: break; - } - temp.push_back(clearBuf()); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(classDecl)); - out.push_back(join(temp)); - } - - size_t transform_class_member_list(class_member_list_t* class_member_list, std::list& out, const std::string& classVar) { - str_list temp; - size_t count = 0; - for (auto keyValue : class_member_list->values.objects()) { - MemType type = MemType::Common; - do { - auto normal_pair = ast_cast(keyValue); - if (!normal_pair) break; - auto keyName = normal_pair->key.as(); - if (!keyName) break; - input newSuperCall; - auto selfName = keyName->name.as(); - if (selfName) { - type = MemType::Property; - auto name = ast_cast(selfName->name); - if (!name) throw std::logic_error("Invalid class poperty name"); - newSuperCall = _converter.from_bytes(classVar) + L".__parent." + _converter.from_bytes(toString(name->name)); - } else { - auto nameNode = keyName->name.as(); - if (!nameNode) break; - auto name = toString(nameNode); - if (name == "new"sv) { - type = MemType::Builtin; - keyName->name.set(toAst("__init"sv, Name)); - newSuperCall = _converter.from_bytes(classVar) + L".__parent.__init"; - } else { - newSuperCall = _converter.from_bytes(classVar) + L".__parent.__base." + _converter.from_bytes(name); - } - } - normal_pair->value->traverse([&](ast_node* node) { - if (node->getId() == "ClassDecl"_id) return traversal::Return; - if (auto chainValue = ast_cast(node)) { - if (auto callable = ast_cast(chainValue->items.front())) { - auto var = callable->item.get(); - if (toString(var) == "super"sv) { - auto insertSelfToArguments = [&](ast_node* item) { - switch (item->getId()) { - case "InvokeArgs"_id: { - auto invoke = static_cast(item); - invoke->args.push_front(toAst("self"sv, Exp)); - return true; - } - case "Invoke"_id: { - auto invoke = static_cast(item); - invoke->args.push_front(toAst("self"sv, Exp)); - 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)) { - _codeCache.push_back(newSuperCall); - } else { - if (auto colonChainItem = ast_cast(secondItem)) { - if (chainList.size() > 2 && insertSelfToArguments(*(++it))) { - colonChainItem->switchToDot = true; - } - } - _codeCache.push_back(_converter.from_bytes(classVar) + L".__parent"); - } - var->m_begin.m_it = _codeCache.back().begin(); - var->m_end.m_it = _codeCache.back().end(); - } else { - _codeCache.push_back(_converter.from_bytes(classVar) + L".__parent"); - var->m_begin.m_it = _codeCache.back().begin(); - var->m_end.m_it = _codeCache.back().end(); - } - } - } - } - return traversal::Continue; - }); - } while (false); - pushScope(); - if (type == MemType::Property) { - decIndentOffset(); - } - switch (keyValue->getId()) { - case "variable_pair"_id: - transform_variable_pair(static_cast(keyValue), temp); - break; - case "normal_pair"_id: - transform_normal_pair(static_cast(keyValue), temp); - break; - } - if (type == MemType::Property) { - incIndentOffset(); - } - popScope(); - out.push_back({temp.back(), type, keyValue}); - temp.clear(); - ++count; - } - return count; - } - - void transformAssignable(Assignable_t* assignable, str_list& out) { - auto item = assignable->item.get(); - switch (item->getId()) { - case "AssignableChain"_id: transformAssignableChain(static_cast(item), out); break; - case "Variable"_id: transformVariable(static_cast(item), out); break; - case "SelfName"_id: transformSelfName(static_cast(item), out, false); break; - default: break; - } - } - - void transformWithClosure(With_t* with, str_list& out) { - str_list temp; - temp.push_back(s("(function()"sv) + nll(with)); - pushScope(); - transformWith(with, temp, nullptr, true); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformWith(With_t* with, str_list& out, ExpList_t* assignList = nullptr, bool returnValue = false) { - str_list temp; - std::string withVar; - bool scoped = false; - if (with->assigns) { - auto vars = getAssignVars(with); - if (vars.front().empty()) { - if (with->assigns->values.objects().size() == 1) { - auto var = variableFrom(with->assigns->values.objects().front()); - if (!var.empty()) { - withVar = var; - } - } - if (withVar.empty()) { - withVar = getUnusedName("_with_"); - auto assignment = new_ptr(); - assignment->assignable.set(toAst(withVar, ExpList)); - auto assign = new_ptr(); - assign->values.push_back(with->assigns->values.objects().front()); - assignment->target.set(assign); - scoped = true; - temp.push_back(indent() + s("do"sv) + nll(with)); - pushScope(); - transformAssignment(assignment, temp); - } - auto assignment = new_ptr(); - assignment->assignable.set(with->valueList); - auto assign = new_ptr(); - assign->values.push_back(toAst(withVar, Exp)); - bool skipFirst = true; - for (auto value : with->assigns->values.objects()) { - if (skipFirst) { - skipFirst = false; - continue; - } - assign->values.push_back(value); - } - assignment->target.set(assign); - transformAssignment(assignment, temp); - } else { - withVar = vars.front(); - auto assignment = new_ptr(); - assignment->assignable.set(with->valueList); - assignment->target.set(with->assigns); - scoped = true; - temp.push_back(indent() + s("do"sv) + nll(with)); - pushScope(); - transformAssignment(assignment, temp); - } - } else { - withVar = variableFrom(with->valueList); - if (withVar.empty()) { - withVar = getUnusedName("_with_"); - auto assignment = new_ptr(); - assignment->assignable.set(toAst(withVar, ExpList)); - auto assign = new_ptr(); - assign->values.dup(with->valueList->exprs); - assignment->target.set(assign); - scoped = true; - temp.push_back(indent() + s("do"sv) + nll(with)); - pushScope(); - transformAssignment(assignment, temp); - } - } - auto exp = with->valueList->exprs.objects().front(); - if (exp->getByPath()) { - auto pair = destructFromExp(exp); - withVar = pair.front().name; - } - _withVars.push(withVar); - transformBody(with->body, temp); - _withVars.pop(); - if (assignList) { - auto assignment = new_ptr(); - assignment->assignable.set(assignList); - auto assign = new_ptr(); - assign->values.push_back(toAst(withVar, Exp)); - assignment->target.set(assign); - transformAssignment(assignment, temp); - } - if (returnValue) { - auto stmt = lastStatementFrom(with->body); - if (!stmt->content.is()) { - temp.push_back(indent() + s("return "sv) + withVar + nll(with)); - } - } - if (scoped) { - popScope(); - temp.push_back(indent() + s("end"sv) + nll(with)); - } - out.push_back(join(temp)); - } - - void transform_const_value(const_value_t* const_value, str_list& out) { - out.push_back(toString(const_value)); - } - - void transformExport(Export_t* exportNode, str_list& out) { - auto item = exportNode->item.get(); - switch (item->getId()) { - case "ClassDecl"_id: { - auto classDecl = static_cast(item); - if (classDecl->name && classDecl->name->item->getId() == "Variable"_id) { - markVarExported(ExportMode::Any, true); - addExportedVar(toString(classDecl->name->item)); - } - transformClassDecl(classDecl, out); - break; - } - case "export_op"_id: - if (toString(item) == "*"sv) { - markVarExported(ExportMode::Any, false); - } else { - markVarExported(ExportMode::Capital, false); - } - break; - case "export_values"_id: { - markVarExported(ExportMode::Any, true); - auto values = exportNode->item.to(); - if (values->valueList) { - auto expList = new_ptr(); - for (auto name : values->nameList->names.objects()) { - addExportedVar(toString(name)); - auto callable = new_ptr(); - callable->item.set(name); - auto chainValue = new_ptr(); - chainValue->items.push_back(callable); - auto value = new_ptr(); - value->item.set(chainValue); - auto exp = new_ptr(); - exp->value.set(value); - expList->exprs.push_back(exp); - } - auto assignment = new_ptr(); - assignment->assignable.set(expList); - auto assign = new_ptr(); - assign->values.dup(values->valueList->exprs); - assignment->target.set(assign); - transformAssignment(assignment, out); - } else { - for (auto name : values->nameList->names.objects()) { - addExportedVar(toString(name)); - } - } - break; - } - default: - break; - } - } - - void transformTable(ast_node* table, const std::list& pairs, str_list& out) { - if (pairs.empty()) { - out.push_back(s("{ }"sv)); - return; - } - str_list temp; - pushScope(); - for (auto pair : pairs) { - switch (pair->getId()) { - case "Exp"_id: transformExp(static_cast(pair), temp); break; - case "variable_pair"_id: transform_variable_pair(static_cast(pair), temp); break; - case "normal_pair"_id: transform_normal_pair(static_cast(pair), temp); break; - } - temp.back() = indent() + temp.back() + (pair == pairs.back() ? Empty : s(","sv)) + nll(pair); - } - out.push_back(s("{"sv) + nll(table) + join(temp)); - popScope(); - out.back() += (indent() + s("}"sv)); - } - - void transform_simple_table(simple_table_t* table, str_list& out) { - transformTable(table, table->pairs.objects(), out); - } - - void transformTblComprehension(TblComprehension_t* comp, str_list& out) { - str_list kv; - std::string tbl = getUnusedName("_tbl_"); - addToScope(tbl); - str_list temp; - auto compInner = comp->forLoop.get(); - for (auto item : compInner->items.objects()) { - switch (item->getId()) { - case "CompForEach"_id: - transformCompForEach(static_cast(item), temp); - break; - case "CompFor"_id: - transformCompFor(static_cast(item), temp); - break; - case "Exp"_id: - transformExp(static_cast(item), temp); - temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); - pushScope(); - break; - } - } - transformExp(comp->key, kv); - if (comp->value) { - transformExp(comp->value->value, kv); - } - for (size_t i = 0; i < compInner->items.objects().size(); ++i) { - popScope(); - } - _buf << indent() << "local "sv << tbl << " = { }"sv << nll(comp); - _buf << join(temp); - pushScope(); - if (!comp->value) { - auto keyVar = getUnusedName("_key_"); - auto valVar = getUnusedName("_val_"); - _buf << indent(int(temp.size())-1) << "local "sv << keyVar << ", "sv << valVar << " = "sv << kv.front() << nll(comp); - kv.front() = keyVar; - kv.push_back(valVar); - } - _buf << indent(int(temp.size())-1) << tbl << "["sv << kv.front() << "] = "sv << kv.back() << nll(comp); - for (int ind = int(temp.size()) - 2; ind > -1 ; --ind) { - _buf << indent(ind) << "end"sv << nll(comp); - } - popScope(); - _buf << indent() << "end"sv << nll(comp); - out.push_back(tbl); - out.push_back(clearBuf()); - } - - void transformTblCompInPlace(TblComprehension_t* comp, ExpList_t* expList, str_list& out) { - str_list temp; - pushScope(); - transformTblComprehension(comp, temp); - auto assign = new_ptr(); - assign->values.push_back(toAst(temp.front(), Exp)); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assign); - assignment->m_begin.m_line = comp->m_end.m_line; - assignment->m_end.m_line = comp->m_end.m_line; - transformAssignment(assignment, temp); - out.push_back( - s("do"sv) + nll(comp) + - *(++temp.begin()) + - temp.back()); - popScope(); - out.back() = out.back() + indent() + s("end"sv) + nlr(comp); - } - - void transformTblCompReturn(TblComprehension_t* comp, str_list& out) { - str_list temp; - transformTblComprehension(comp, temp); - out.push_back(temp.back() + indent() + s("return "sv) + temp.front() + nlr(comp)); - } - - void transformTblCompClosure(TblComprehension_t* comp, str_list& out) { - str_list temp; - std::string before = s("(function()"sv) + nll(comp); - pushScope(); - transformTblComprehension(comp, temp); - const auto& tbVar = temp.front(); - const auto& compBody = temp.back(); - out.push_back( - before + - compBody + - indent() + s("return "sv) + tbVar + nlr(comp)); - popScope(); - out.back() = out.back() + indent() + s("end)()"sv); - } - - void transformCompFor(CompFor_t* comp, str_list& out) { - str_list temp; - std::string varName = toString(comp->varName); - transformExp(comp->startValue, temp); - transformExp(comp->stopValue, temp); - if (comp->stepValue) { - transformExp(comp->stepValue->value, temp); - } else { - temp.emplace_back(); - } - auto it = temp.begin(); - const auto& start = *it; - const auto& stop = *(++it); - const auto& step = *(++it); - _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(comp); - out.push_back(clearBuf()); - pushScope(); - addToScope(varName); - } - - void transformTableBlock(TableBlock_t* table, str_list& out) { - transformTable(table, table->values.objects(), out); - } - - void transformDo(Do_t* doNode, str_list& out, bool implicitReturn = false) { - str_list temp; - temp.push_back(indent() + s("do"sv) + nll(doNode)); - pushScope(); - transformBody(doNode->body, temp, implicitReturn); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(doNode)); - out.push_back(join(temp)); - } - - void transformDoClosure(Do_t* doNode, str_list& out) { - str_list temp; - temp.push_back(s("(function()"sv) + nll(doNode)); - pushScope(); - transformBody(doNode->body, temp, true); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformImport(Import_t* import, str_list& out) { - str_list temp; - auto objVar = variableFrom(import->exp); - ast_ptr objAssign; - if (objVar.empty()) { - objVar = getUnusedName("_obj_"sv); - auto expList = toAst(objVar, ExpList); - auto assign = new_ptr(); - assign->values.push_back(import->exp); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assign); - objAssign.set(assignment); - } - auto expList = new_ptr(); - auto assign = new_ptr(); - for (auto name : import->names.objects()) { - switch (name->getId()) { - case "Variable"_id: { - auto var = ast_to(name); - { - auto callable = toAst(objVar, Callable); - auto dotChainItem = new_ptr(); - dotChainItem->name.set(var->name); - auto chainValue = new_ptr(); - chainValue->items.push_back(callable); - chainValue->items.push_back(dotChainItem); - auto value = new_ptr(); - value->item.set(chainValue); - auto exp = new_ptr(); - exp->value.set(value); - assign->values.push_back(exp); - } - auto callable = new_ptr(); - callable->item.set(var); - auto chainValue = new_ptr(); - chainValue->items.push_back(callable); - auto value = new_ptr(); - value->item.set(chainValue); - auto exp = new_ptr(); - exp->value.set(value); - expList->exprs.push_back(exp); - break; - } - case "colon_import_name"_id: { - auto var = static_cast(name)->name.get(); - { - auto nameNode = var->name.get(); - auto callable = toAst(objVar, Callable); - auto colonChain = new_ptr(); - colonChain->name.set(nameNode); - auto chainValue = new_ptr(); - chainValue->items.push_back(callable); - chainValue->items.push_back(colonChain); - auto value = new_ptr(); - value->item.set(chainValue); - auto exp = new_ptr(); - exp->value.set(value); - assign->values.push_back(exp); - } - auto callable = new_ptr(); - callable->item.set(var); - auto chainValue = new_ptr(); - chainValue->items.push_back(callable); - auto value = new_ptr(); - value->item.set(chainValue); - auto exp = new_ptr(); - exp->value.set(value); - expList->exprs.push_back(exp); - break; - } - } - } - if (objAssign) { - auto preDef = getPredefine(transformAssignDefs(expList)); - if (!preDef.empty()) { - temp.push_back(preDef + nll(import)); - } - temp.push_back(indent() + s("do"sv) + nll(import)); - pushScope(); - transformAssignment(objAssign, temp); - } - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.set(assign); - transformAssignment(assignment, temp); - if (objAssign) { - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(import)); - } - out.push_back(join(temp)); - } - - void transformWhileClosure(While_t* whileNode, str_list& out, ExpList_t* expList = nullptr) { - str_list temp; - if (expList) { - temp.push_back(indent() + s("do"sv) + nll(whileNode)); - } else { - temp.push_back(s("(function() "sv) + nll(whileNode)); - } - pushScope(); - auto accumVar = getUnusedName("_accum_"sv); - addToScope(accumVar); - auto lenVar = getUnusedName("_len_"sv); - addToScope(lenVar); - temp.push_back(indent() + s("local "sv) + accumVar + s(" = { }"sv) + nll(whileNode)); - temp.push_back(indent() + s("local "sv) + lenVar + s(" = 1"sv) + nll(whileNode)); - transformExp(whileNode->condition, temp); - temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); - pushScope(); - auto last = lastStatementFrom(whileNode->body); - auto valueList = last ? last->content.as() : nullptr; - if (last && valueList) { - auto newAssignment = new_ptr(); - newAssignment->assignable.set(toAst(accumVar + s("["sv) + lenVar + s("]"sv), ExpList)); - auto assign = new_ptr(); - assign->values.dup(valueList->exprs); - newAssignment->target.set(assign); - last->content.set(newAssignment); - } - transformLoopBody(whileNode->body, temp); - temp.push_back(indent() + lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode)); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(whileNode)); - if (expList) { - auto assign = new_ptr(); - assign->values.push_back(toAst(accumVar, Exp)); - auto assignment = new_ptr(); - assignment->assignable.set(expList); - assignment->target.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)); - } else { - temp.push_back(indent() + s("end)()"sv)); - } - out.push_back(join(temp)); - } - - void transformWhile(While_t* whileNode, str_list& out) { - str_list temp; - pushScope(); - transformExp(whileNode->condition, temp); - transformLoopBody(whileNode->body, temp); - popScope(); - _buf << indent() << "while "sv << temp.front() << " do"sv << nll(whileNode); - _buf << temp.back(); - _buf << indent() << "end"sv << nlr(whileNode); - out.push_back(clearBuf()); - } - - void transformSwitchClosure(Switch_t* switchNode, str_list& out) { - str_list temp; - temp.push_back(s("(function()"sv) + nll(switchNode)); - pushScope(); - transformSwitch(switchNode, temp, true); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformSwitch(Switch_t* switchNode, str_list& out, bool implicitReturn = false) { - str_list temp; - auto objVar = variableFrom(switchNode->target); - if (objVar.empty()) { - objVar = getUnusedName("_exp_"sv); - addToScope(objVar); - transformExp(switchNode->target, temp); - _buf << indent() << "local "sv << objVar << " = "sv << temp.back() << nll(switchNode); - temp.back() = clearBuf(); - } - const auto& branches = switchNode->branches.objects(); - for (auto branch_ : branches) { - auto branch = static_cast(branch_); - temp.push_back(indent() + s(branches.front() == branch ? "if"sv : "elseif"sv)); - str_list tmp; - const auto& exprs = branch->valueList->exprs.objects(); - for (auto exp_ : exprs) { - auto exp = static_cast(exp_); - transformExp(exp, tmp); - if (!singleValueFrom(exp)) { - tmp.back() = s("("sv) + tmp.back() + s(")"sv); - } - temp.back().append(s(" "sv) + tmp.back() + s(" == "sv) + objVar + - s(exp == exprs.back() ? ""sv : " or"sv)); - } - temp.back().append(s(" then"sv) + nll(branch)); - pushScope(); - transformBody(branch->body, temp, implicitReturn); - popScope(); - } - if (switchNode->lastBranch) { - temp.push_back(indent() + s("else"sv) + nll(switchNode->lastBranch)); - pushScope(); - transformBody(switchNode->lastBranch, temp, implicitReturn); - popScope(); - } - temp.push_back(indent() + s("end"sv) + nlr(switchNode)); - out.push_back(join(temp)); - } - - void transformLocal(Local_t* local, str_list& out) { - if (!local->forceDecls.empty() || !local->decls.empty()) { - str_list defs; - for (const auto& decl : local->forceDecls) { - forceAddToScope(decl); - defs.push_back(decl); - } - for (const auto& decl : local->decls) { - if (addToScope(decl)) { - defs.push_back(decl); - } - } - auto preDefine = getPredefine(defs); - if (!preDefine.empty()) { - out.push_back(preDefine + nll(local)); - } - } - } - - void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { - auto keyword = toString(breakLoop); - if (keyword == "break"sv) { - out.push_back(indent() + keyword + nll(breakLoop)); - return; - } - if (_continueVars.empty()) throw std::logic_error("continue must be inside of a loop"); - _buf << indent() << _continueVars.top() << " = true"sv << nll(breakLoop); - _buf << indent() << "break"sv << nll(breakLoop); - out.push_back(clearBuf()); - } -}; - -const std::string MoonCompliler::Empty; - -int main() -{ - std::string s = R"TestCodesHere( -Dorothy builtin.ImGui -import Set,Path from require "Utils" -LintGlobal = require "LintGlobal" -moonscript = require "moonscript" - -LoadFontTTF "Font/DroidSansFallback.ttf", 20, "Chinese" - -moduleCache = {} -oldRequire = _G.require -newRequire = (path)-> - loaded = package.loaded[path] - if not loaded - table.insert moduleCache,path - return oldRequire path - loaded -_G.require = newRequire -builtin.require = newRequire - -allowedUseOfGlobals = Set { -} - -LintMoonGlobals = (moonCodes,entry)-> - globals,err = LintGlobal moonCodes - if not globals - error "Compile failed in #{entry}\n#{err}" - requireModules = {} - withImGui = false - withPlatformer = false - importCodes = table.concat ( - for importLine in moonCodes\gmatch "Dorothy%s*%(?([^%)!\r\n]*)%s*[%)!]?" - continue if importLine == "" - importLine - ), "," - importItems = if importCodes - for item in importCodes\gmatch "%s*([^,\n\r]+)%s*" - getImport = loadstring "return #{item}" - importItem = if getImport then getImport! else nil - continue if not importItem or "table" ~= type importItem - {importItem, item} - else {} - importSet = {} - for name,_ in pairs globals - if not allowedUseOfGlobals[name] - if builtin[name] - table.insert requireModules, "local #{name} = require(\"#{name}\")" - else - findModule = false - for i,importItem in ipairs importItems - if importItem[1][name] ~= nil - moduleName = "_module_#{i-1}" - if not importSet[importItem[1]] - importSet[importItem[1]] = true - table.insert requireModules, "local #{moduleName} = #{importItem[2]}" - table.insert requireModules, "local #{name} = #{moduleName}.#{name}" - findModule = true - break - if not findModule - error "Used invalid global value \"#{name}\" in \"#{entry}\"." - table.concat requireModules, "\n" - -totalFiles = 0 -totalMoonTime = 0 -totalXmlTime = 0 -totalMinifyTime = 0 -compile = (dir,clean,minify)-> - {:ParseLua} = require "luaminify.ParseLua" - FormatMini = require "luaminify.FormatMini" - files = Path.getAllFiles dir, {"moon","xml"} - for file in *files - path = Path.getPath file - name = Path.getName file - isXml = "xml" == Path.getExtension file - compileFunc = isXml and xmltolua or moonscript.to_lua - requires = nil - if not clean - sourceCodes = Content\loadAsync "#{dir}/#{file}" - requires = LintMoonGlobals sourceCodes, file unless isXml - startTime = App.eclapsedTime - codes,err = compileFunc sourceCodes - if isXml - totalXmlTime += App.eclapsedTime - startTime - else - totalMoonTime += App.eclapsedTime - startTime - startTime = App.eclapsedTime - if not codes - print "Compile errors in #{file}." - print err - return false - else - codes = requires..codes\gsub "Dorothy%([^%)]*%)","" unless isXml - if minify - st, ast = ParseLua codes - if not st - print ast - return false - codes = FormatMini ast - totalMinifyTime += App.eclapsedTime - startTime - filePath = Content.writablePath..path - Content\mkdir filePath - filename = "#{filePath}#{name}.lua" - Content\saveAsync filename,codes - print "#{isXml and "Xml" or "Moon"} compiled: #{path}#{name}.#{isXml and "xml" or "moon"}" - totalFiles += 1 - else - filePath = Content.writablePath..path - Content\mkdir filePath - filename = "#{filePath}#{name}.lua" - if Content\exist filename - print "#{isXml and "Xml" or "Moon"} cleaned: #{path}#{name}.lua" - Content\remove filename - if clean or minify - files = Path.getAllFiles dir, "lua" - for file in *files - path = Path.getPath file - name = Path.getName file - if not clean - sourceCodes = Content\loadAsync "#{dir}/#{file}" - startTime = App.eclapsedTime - st, ast = ParseLua sourceCodes - if not st - print ast - return false - codes = FormatMini ast - totalMinifyTime += App.eclapsedTime - startTime - filePath = Content.writablePath..path - Content\mkdir filePath - filename = "#{filePath}#{name}.lua" - Content\saveAsync filename,codes - print "Lua minified: #{path}#{name}.lua" - totalFiles += 1 - else - filePath = Content.writablePath..path - Content\mkdir filePath - filename = "#{filePath}#{name}.lua" - if Content\exist filename - print "Lua cleaned: #{path}#{name}.lua" - Content\remove filename - return true - -building = false - -doCompile = (minify)-> - return if building - building = true - totalFiles = 0 - totalMoonTime = 0 - totalXmlTime = 0 - totalMinifyTime = 0 - thread -> - print "Output path: #{Content.writablePath}" - xpcall (-> compile Content.assetPath\sub(1,-2),false,minify),(msg)-> - msg = debug.traceback msg - print msg - building = false - print string.format "Compile #{minify and 'and minify ' or ''}done. %d files in total.\nCompile time, Moon %.3fs, Xml %.3fs#{minify and ', Minify %.3fs' or ''}.\n",totalFiles,totalMoonTime,totalXmlTime,totalMinifyTime - building = false - -doClean = -> - return if building - building = true - thread -> - print "Clean path: #{Content.writablePath}" - compile Content.assetPath\sub(1,-2),true - print "Clean done.\n" - building = false - -isInEntry = true - -allClear = -> - for module in *moduleCache - package.loaded[module] = nil - moduleCache = {} - with Director.ui - \removeAllChildren! - .userData = nil - with Director.entry - \removeAllChildren! - .userData = nil - with Director.postNode - \removeAllChildren! - .userData = nil - Director\popCamera! - Cache\unload! - Entity\clear! - Platformer.Data\clear! - Platformer.UnitAction\clear! - currentEntryName = nil - isInEntry = true - Audio\stopStream 0.2 - -games = [Path.getName item for item in *Path.getFolders Content.assetPath.."Script/Game", {"xml","lua","moon"}] -table.sort games -examples = [Path.getName item for item in *Path.getAllFiles Content.assetPath.."Script/Example", {"xml","lua","moon"}] -table.sort examples -tests = [Path.getName item for item in *Path.getAllFiles Content.assetPath.."Script/Test", {"xml","lua","moon"}] -table.sort tests -currentEntryName = nil -allNames = for game in *games do "Game/#{game}/init" -for example in *examples do table.insert allNames,"Example/#{example}" -for test in *tests do table.insert allNames,"Test/#{test}" - -enterDemoEntry = (name)-> - isInEntry = false - xpcall (-> - result = require name - if "function" == type result - result = result! - Director.entry\addChild if tolua.cast result, "Node" - result - else - Node! - else - Director.entry\addChild Node! - currentEntryName = name - ),(msg)-> - msg = debug.traceback msg - print msg - allClear! - -showEntry = false - -thread -> - {:width,:height} = App.visualSize - scale = App.deviceRatio*0.7*math.min(width,height)/760 - with Sprite GetDorothySSRHappyWhite scale - \addTo Director.entry - sleep 1.0 - \removeFromParent! - showEntry = true - Director.clearColor = Color 0xff1a1a1a - -showStats = false -showLog = false -showFooter = true -scaleContent = false -footerFocus = false -screenScale = 2 -- App.deviceRatio -threadLoop -> - return unless showEntry - left = Keyboard\isKeyDown "Left" - right = Keyboard\isKeyDown "Right" - App\shutdown! if Keyboard\isKeyDown "Escape" - {:width,:height} = App.visualSize - SetNextWindowSize Vec2(190,50) - SetNextWindowPos Vec2(width-190,height-50) - if width >= 600 - if not footerFocus - footerFocus = true - SetNextWindowFocus! - PushStyleColor "WindowBg", Color(0x0), -> - Begin "Show", "NoTitleBar|NoResize|NoMove|NoCollapse|NoSavedSettings", -> - Columns 2,false - if showFooter - changed, scaleContent = Checkbox string.format("%.1fx",screenScale), scaleContent - View.scale = scaleContent and screenScale or 1 if changed - else - Dummy Vec2 10,30 - SameLine! - NextColumn! - _, showFooter = Checkbox "Footer", showFooter - elseif footerFocus - footerFocus = false - return unless showFooter - SetNextWindowSize Vec2(width,60) - SetNextWindowPos Vec2(0,height-60) - Begin "Footer", "NoTitleBar|NoResize|NoMove|NoCollapse|NoBringToFrontOnFocus|NoSavedSettings", -> - Separator! - _, showStats = Checkbox "Stats", showStats - SameLine! - _, showLog = Checkbox "Log", showLog - SameLine! - if isInEntry - OpenPopup "build" if Button "Build", Vec2(70,30) - BeginPopup "build", -> - doCompile false if Selectable "Compile" - Separator! - doCompile true if Selectable "Minify" - Separator! - doClean! if Selectable "Clean" - else - SameLine! - allClear! if Button "Home", Vec2(70,30) - currentIndex = 1 - for i,name in ipairs allNames - if currentEntryName == name - currentIndex = i - SameLine! - if currentIndex > 1 - if Button("Prev", Vec2(70,30)) or left - allClear! - isInEntry = false - thread -> enterDemoEntry allNames[currentIndex-1] - else Dummy Vec2 70,30 - SameLine! - if currentIndex < #allNames - if Button("Next", Vec2(70,30)) or right - allClear! - isInEntry = false - thread -> enterDemoEntry allNames[currentIndex+1] - else Dummy Vec2 70,30 - if showStats - SetNextWindowPos Vec2(0,height-65-296), "FirstUseEver" - ShowStats! - if showLog - SetNextWindowPos Vec2(width-400,height-65-300), "FirstUseEver" - ShowLog! - -threadLoop -> - return unless showEntry - return unless isInEntry - {:width,:height} = App.visualSize - SetNextWindowPos Vec2.zero - SetNextWindowSize Vec2(width,53) - PushStyleColor "TitleBgActive", Color(0xcc000000), -> - Begin "Dorothy Dev", "NoResize|NoMove|NoCollapse|NoBringToFrontOnFocus|NoSavedSettings", -> - Separator! - SetNextWindowPos Vec2(0,53) - SetNextWindowSize Vec2(width,height-107) - PushStyleColor "WindowBg",Color(0x0), -> - Begin "Content", "NoTitleBar|NoResize|NoMove|NoCollapse|NoBringToFrontOnFocus|NoSavedSettings", -> - TextColored Color(0xff00ffff), "Game Demos" - Columns math.max(math.floor(width/200),1), false - for game in *games - if Button game, Vec2(-1,40) - enterDemoEntry "Game/#{game}/init" - NextColumn! - Columns 1, false - TextColored Color(0xff00ffff), "Examples" - Columns math.max(math.floor(width/200),1), false - for example in *examples - if Button example, Vec2(-1,40) - enterDemoEntry "Example/#{example}" - NextColumn! - Columns 1, false - TextColored Color(0xff00ffff), "Tests" - Columns math.max(math.floor(width/200),1), false - for test in *tests - if Button test, Vec2(-1,40) - enterDemoEntry "Test/#{test}" - NextColumn! -)TestCodesHere"; - MoonCompliler{}.complile(s); - return 0; -} +} // namespace MoonP diff --git a/MoonParser/moon_ast.h b/MoonParser/moon_ast.h index 795c0c8..a77fb40 100644 --- a/MoonParser/moon_ast.h +++ b/MoonParser/moon_ast.h @@ -2,31 +2,11 @@ #include "moon_parser.h" -template -std::basic_string& trim(std::basic_string& s) -{ - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](T ch) - { - return !std::isspace(ch); - })); - s.erase(std::find_if(s.rbegin(), s.rend(), [](T ch) - { - return !std::isspace(ch); - }).base(), s.end()); - return s; -} - -class AstLeaf : public ast_node -{ -public: - const input& getValue(); -private: - input _value; -}; +namespace MoonP { #define AST_LEAF(type, id) \ extern rule type; \ -class type##_t : public AstLeaf \ +class type##_t : public ast_node \ { \ public: \ virtual int get_type() override { return ast_type(); } \ @@ -42,6 +22,11 @@ public: \ virtual size_t getId() const override { return id; } \ virtual const char* getName() const override { return #type; } +#define AST_MEMBER(type, ...) \ + type##_t() { \ + add_members({__VA_ARGS__}); \ + } + #define AST_END(type) \ }; @@ -52,29 +37,39 @@ AST_LEAF(Name, "Name"_id) AST_END(Name) AST_NODE(Variable, "Variable"_id) - ast_ptr name; + ast_ptr name; + AST_MEMBER(Variable, &name) AST_END(Variable) +AST_NODE(LuaKeyword, "LuaKeyword"_id) + ast_ptr name; + AST_MEMBER(LuaKeyword, &name) +AST_END(LuaKeyword) + AST_LEAF(self, "self"_id) AST_END(self) AST_NODE(self_name, "self_name"_id) - ast_ptr name; + ast_ptr name; + AST_MEMBER(self_name, &name) AST_END(self_name) AST_LEAF(self_class, "self_class"_id) AST_END(self_class) AST_NODE(self_class_name, "self_class_name"_id) - ast_ptr name; + ast_ptr name; + AST_MEMBER(self_class_name, &name) AST_END(self_class_name) AST_NODE(SelfName, "SelfName"_id) - ast_ptr name; // self_class_name_t | self_class_t | self_name_t | self_t + ast_sel name; + AST_MEMBER(SelfName, &name) AST_END(SelfName) AST_NODE(KeyName, "KeyName"_id) - ast_ptr name; // SelfName_t | Name_t + ast_sel name; + AST_MEMBER(KeyName, &name) AST_END(KeyName) AST_LEAF(VarArg, "VarArg"_id) @@ -87,207 +82,254 @@ AST_LEAF(Seperator, "Seperator"_id) AST_END(Seperator) AST_NODE(NameList, "NameList"_id) - ast_ptr sep; - ast_list names; + ast_ptr sep; + ast_list names; + AST_MEMBER(NameList, &sep, &names) AST_END(NameList) AST_NODE(Local, "Local"_id) - ast_ptr name; // local_flag_t | NameList_t + ast_sel name; std::list forceDecls; std::list decls; + AST_MEMBER(Local, &name) AST_END(Local) AST_NODE(colon_import_name, "colon_import_name"_id) - ast_ptr name; + ast_ptr name; + AST_MEMBER(colon_import_name, &name) AST_END(colon_import_name) class Exp_t; AST_NODE(Import, "Import"_id) - ast_ptr sep; - ast_sel_list names; - ast_ptr exp; + ast_ptr sep; + ast_sel_list names; + ast_ptr exp; + AST_MEMBER(Import, &sep, &names, &exp) AST_END(Import) AST_NODE(ExpListLow, "ExpListLow"_id) - ast_ptr sep; - ast_list exprs; + ast_ptr sep; + ast_list exprs; + AST_MEMBER(ExpListLow, &sep, &exprs) AST_END(ExpListLow) AST_NODE(ExpList, "ExpList"_id) - ast_ptr sep; - ast_list exprs; + ast_ptr sep; + ast_list exprs; + AST_MEMBER(ExpList, &sep, &exprs) AST_END(ExpList) AST_NODE(Return, "Return"_id) - ast_ptr valueList; + ast_ptr valueList; + AST_MEMBER(Return, &valueList) AST_END(Return) class Assign_t; class Body_t; AST_NODE(With, "With"_id) - ast_ptr valueList; - ast_ptr assigns; - ast_ptr body; + ast_ptr valueList; + ast_ptr assigns; + ast_ptr body; + AST_MEMBER(With, &valueList, &assigns, &body) AST_END(With) AST_NODE(SwitchCase, "SwitchCase"_id) - ast_ptr valueList; - ast_ptr body; + ast_ptr valueList; + ast_ptr body; + AST_MEMBER(SwitchCase, &valueList, &body) AST_END(SwitchCase) AST_NODE(Switch, "Switch"_id) - ast_ptr target; - ast_ptr sep; - ast_list branches; - ast_ptr lastBranch; + ast_ptr target; + ast_ptr sep; + ast_list branches; + ast_ptr lastBranch; + AST_MEMBER(Switch, &target, &sep, &branches, &lastBranch) AST_END(Switch) AST_NODE(IfCond, "IfCond"_id) - ast_ptr condition; - ast_ptr assign; + ast_ptr condition; + ast_ptr assign; + AST_MEMBER(IfCond, &condition, &assign) AST_END(IfCond) AST_NODE(If, "If"_id) - ast_ptr sep; - ast_sel_list nodes; + ast_ptr sep; + ast_sel_list nodes; + AST_MEMBER(If, &sep, &nodes) AST_END(If) AST_NODE(Unless, "Unless"_id) - ast_ptr sep; - ast_sel_list nodes; + ast_ptr sep; + ast_sel_list nodes; + AST_MEMBER(Unless, &sep, &nodes) AST_END(Unless) AST_NODE(While, "While"_id) - ast_ptr condition; - ast_ptr body; + ast_ptr condition; + ast_ptr body; + AST_MEMBER(While, &condition, &body) AST_END(While) AST_NODE(for_step_value, "for_step_value"_id) - ast_ptr value; + ast_ptr value; + AST_MEMBER(for_step_value, &value) AST_END(for_step_value) AST_NODE(For, "For"_id) - ast_ptr varName; - ast_ptr startValue; - ast_ptr stopValue; - ast_ptr stepValue; - ast_ptr body; + ast_ptr varName; + ast_ptr startValue; + ast_ptr stopValue; + ast_ptr stepValue; + ast_ptr body; + AST_MEMBER(For, &varName, &startValue, &stopValue, &stepValue, &body) AST_END(For) class AssignableNameList_t; +class star_exp_t; AST_NODE(ForEach, "ForEach"_id) - ast_ptr nameList; - ast_ptr loopValue; // Exp_t | ExpList_t - ast_ptr body; + ast_ptr nameList; + ast_sel loopValue; + ast_ptr body; + AST_MEMBER(ForEach, &nameList, &loopValue, &body) AST_END(ForEach) AST_NODE(Do, "Do"_id) - ast_ptr body; + ast_ptr body; + AST_MEMBER(Do, &body) AST_END(Do) class CompInner_t; class Statement_t; AST_NODE(Comprehension, "Comprehension"_id) - ast_sel value; - ast_ptr forLoop; + ast_sel value; + ast_ptr forLoop; + AST_MEMBER(Comprehension, &value, &forLoop) AST_END(Comprehension) AST_NODE(comp_value, "comp_value"_id) - ast_ptr value; + ast_ptr value; + AST_MEMBER(comp_value, &value) AST_END(comp_value) AST_NODE(TblComprehension, "TblComprehension"_id) - ast_ptr key; - ast_ptr value; - ast_ptr forLoop; + ast_ptr key; + ast_ptr value; + ast_ptr forLoop; + AST_MEMBER(TblComprehension, &key, &value, &forLoop) AST_END(TblComprehension) AST_NODE(star_exp, "star_exp"_id) - ast_ptr value; + ast_ptr value; + AST_MEMBER(star_exp, &value) AST_END(star_exp) AST_NODE(CompForEach, "CompForEach"_id) - ast_ptr nameList; - ast_ptr loopValue; // star_exp_t | Exp_t + ast_ptr nameList; + ast_sel loopValue; + AST_MEMBER(CompForEach, &nameList, &loopValue) AST_END(CompForEach) AST_NODE(CompFor, "CompFor"_id) - ast_ptr varName; - ast_ptr startValue; - ast_ptr stopValue; - ast_ptr stepValue; + ast_ptr varName; + ast_ptr startValue; + ast_ptr stopValue; + ast_ptr stepValue; + AST_MEMBER(CompFor, &varName, &startValue, &stopValue, &stepValue) AST_END(CompFor) AST_NODE(CompInner, "CompInner"_id) - ast_ptr sep; - ast_sel_list items; + ast_ptr sep; + ast_sel_list items; + AST_MEMBER(CompInner, &sep, &items) AST_END(CompInner) class TableBlock_t; AST_NODE(Assign, "Assign"_id) - ast_ptr sep; - ast_sel_list values; + ast_ptr sep; + ast_sel_list values; + AST_MEMBER(Assign, &sep, &values) AST_END(Assign) AST_LEAF(update_op, "update_op"_id) AST_END(update_op) AST_NODE(Update, "Update"_id) - ast_ptr op; - ast_ptr value; + ast_ptr op; + ast_ptr value; + AST_MEMBER(Update, &op, &value) AST_END(Update) AST_LEAF(BinaryOperator, "BinaryOperator"_id) AST_END(BinaryOperator) +class AssignableChain_t; + AST_NODE(Assignable, "Assignable"_id) - ast_ptr item; // AssignableChain_t | Variable_t | SelfName_t + ast_sel item; + AST_MEMBER(Assignable, &item) AST_END(Assignable) class Value_t; AST_NODE(exp_op_value, "exp_op_value"_id) - ast_ptr op; - ast_ptr value; + ast_ptr op; + ast_ptr value; + AST_MEMBER(exp_op_value, &op, &value) AST_END(exp_op_value) AST_NODE(Exp, "Exp"_id) - ast_ptr value; - ast_list opValues; + ast_ptr value; + ast_list opValues; + AST_MEMBER(Exp, &value, &opValues) AST_END(Exp) +class Parens_t; + AST_NODE(Callable, "Callable"_id) - ast_ptr item; // Variable_t | SelfName_t | VarArg_t | Parens_t + ast_sel item; + AST_MEMBER(Callable, &item) AST_END(Callable) AST_NODE(variable_pair, "variable_pair"_id) - ast_ptr name; + ast_ptr name; + AST_MEMBER(variable_pair, &name) AST_END(variable_pair) +class DoubleString_t; +class SingleString_t; + AST_NODE(normal_pair, "normal_pair"_id) - ast_ptr key; // KeyName_t | [Exp_t] | DoubleString_t | SingleString_t - ast_ptr value; // Exp_t | TableBlock_t + ast_sel key; + ast_sel value; + AST_MEMBER(normal_pair, &key, &value) AST_END(normal_pair) AST_NODE(simple_table, "simple_table"_id) - ast_ptr sep; - ast_sel_list pairs; + ast_ptr sep; + ast_sel_list pairs; + AST_MEMBER(simple_table, &sep, &pairs) AST_END(simple_table) class String_t; +class const_value_t; +class ClassDecl_t; +class unary_exp_t; +class TableLit_t; +class FunLit_t; AST_NODE(SimpleValue, "SimpleValue"_id) - ast_ptr value; /* - const_value_t | - If_t | Unless_t | Switch_t | With_t | ClassDecl_t | ForEach_t | For_t | While_t | Do_t | - unary_exp_t | - TblComprehension_t | TableLit_t | Comprehension_t | FunLit_t | Num_t; - */ + ast_sel value; + AST_MEMBER(SimpleValue, &value) AST_END(SimpleValue) AST_LEAF(LuaStringOpen, "LuaStringOpen"_id) @@ -300,9 +342,10 @@ AST_LEAF(LuaStringClose, "LuaStringClose"_id) AST_END(LuaStringClose) AST_NODE(LuaString, "LuaString"_id) - ast_ptr open; - ast_ptr content; - ast_ptr close; + ast_ptr open; + ast_ptr content; + ast_ptr close; + AST_MEMBER(LuaString, &open, &content, &close) AST_END(LuaString) AST_LEAF(SingleString, "SingleString"_id) @@ -312,188 +355,227 @@ AST_LEAF(double_string_inner, "double_string_inner"_id) AST_END(double_string_inner) AST_NODE(double_string_content, "double_string_content"_id) - ast_ptr content; // double_string_inner_t | Exp_t + ast_sel content; + AST_MEMBER(double_string_content, &content) AST_END(double_string_content) AST_NODE(DoubleString, "DoubleString"_id) - ast_ptr sep; - ast_list segments; + ast_ptr sep; + ast_list segments; + AST_MEMBER(DoubleString, &sep, &segments) AST_END(DoubleString) AST_NODE(String, "String"_id) - ast_ptr str; // DoubleString_t | SingleString_t | LuaString_t + ast_sel str; + AST_MEMBER(String, &str) AST_END(String) AST_NODE(DotChainItem, "DotChainItem"_id) - ast_ptr name; + ast_ptr name; + AST_MEMBER(DotChainItem, &name) AST_END(DotChainItem) AST_NODE(ColonChainItem, "ColonChainItem"_id) - ast_ptr name; + ast_sel name; bool switchToDot = false; + AST_MEMBER(ColonChainItem, &name) AST_END(ColonChainItem) +class default_value_t; + AST_NODE(Slice, "Slice"_id) - ast_ptr startValue; // Exp_t | default_value_t - ast_ptr stopValue; // Exp_t | default_value_t - ast_ptr stepValue; // Exp_t | default_value_t + ast_sel startValue; + ast_sel stopValue; + ast_sel stepValue; + AST_MEMBER(Slice, &startValue, &stopValue, &stepValue) AST_END(Slice) AST_NODE(Parens, "Parens"_id) - ast_ptr expr; + ast_ptr expr; + AST_MEMBER(Parens, &expr) AST_END(Parens) AST_NODE(Invoke, "Invoke"_id) - ast_ptr sep; - ast_sel_list args; + ast_ptr sep; + ast_sel_list args; + AST_MEMBER(Invoke, &sep, &args) AST_END(Invoke) class InvokeArgs_t; AST_NODE(ChainValue, "ChainValue"_id) - ast_ptr sep; - ast_sel_list items; + ast_ptr sep; + ast_sel_list items; + AST_MEMBER(ChainValue, &sep, &items) AST_END(ChainValue) AST_NODE(AssignableChain, "AssignableChain"_id) - ast_ptr sep; - ast_sel_list items; + ast_ptr sep; + ast_sel_list items; + AST_MEMBER(AssignableChain, &sep, &items) AST_END(AssignableChain) AST_NODE(Value, "Value"_id) - ast_ptr item; // SimpleValue_t | simple_table_t | ChainValue_t | String_t + ast_sel item; + AST_MEMBER(Value, &item) AST_END(Value) AST_LEAF(default_value, "default_value"_id) AST_END(default_value) AST_NODE(TableLit, "TableLit"_id) - ast_ptr sep; - ast_sel_list values; + ast_ptr sep; + ast_sel_list values; + AST_MEMBER(TableLit, &sep, &values) AST_END(TableLit) AST_NODE(TableBlock, "TableBlock"_id) - ast_ptr sep; - ast_sel_list values; + ast_ptr sep; + ast_sel_list values; + AST_MEMBER(TableBlock, &sep, &values) AST_END(TableBlock) AST_NODE(class_member_list, "class_member_list"_id) - ast_ptr sep; - ast_sel_list values; + ast_ptr sep; + ast_sel_list values; + AST_MEMBER(class_member_list, &sep, &values) AST_END(class_member_list) AST_NODE(ClassBlock, "ClassBlock"_id) - ast_ptr sep; - ast_sel_list contents; + ast_ptr sep; + ast_sel_list contents; + AST_MEMBER(ClassBlock, &sep, &contents) AST_END(ClassBlock) AST_NODE(ClassDecl, "ClassDecl"_id) - ast_ptr name; - ast_ptr extend; - ast_ptr body; + ast_ptr name; + ast_ptr extend; + ast_ptr body; + AST_MEMBER(ClassDecl, &name, &extend, &body) AST_END(ClassDecl) AST_NODE(export_values, "export_values"_id) - ast_ptr nameList; - ast_ptr valueList; + ast_ptr nameList; + ast_ptr valueList; + AST_MEMBER(export_values, &nameList, &valueList) AST_END(export_values) AST_LEAF(export_op, "export_op"_id) AST_END(export_op) AST_NODE(Export, "Export"_id) - ast_ptr item; // ClassDecl_t | export_op_t | export_values_t + ast_sel item; + AST_MEMBER(Export, &item) AST_END(Export) AST_NODE(FnArgDef, "FnArgDef"_id) - ast_ptr name; // Variable_t | SelfName_t - ast_ptr defaultValue; + ast_sel name; + ast_ptr defaultValue; + AST_MEMBER(FnArgDef, &name, &defaultValue) AST_END(FnArgDef) AST_NODE(FnArgDefList, "FnArgDefList"_id) - ast_ptr sep; - ast_list definitions; - ast_ptr varArg; + ast_ptr sep; + ast_list definitions; + ast_ptr varArg; + AST_MEMBER(FnArgDefList, &sep, &definitions, &varArg) AST_END(FnArgDefList) AST_NODE(outer_var_shadow, "outer_var_shadow"_id) - ast_ptr varList; + ast_ptr varList; + AST_MEMBER(outer_var_shadow, &varList) AST_END(outer_var_shadow) AST_NODE(FnArgsDef, "FnArgsDef"_id) - ast_ptr defList; - ast_ptr shadowOption; + ast_ptr defList; + ast_ptr shadowOption; + AST_MEMBER(FnArgsDef, &defList, &shadowOption) AST_END(FnArgsDef) AST_LEAF(fn_arrow, "fn_arrow"_id) AST_END(fn_arrow) AST_NODE(FunLit, "FunLit"_id) - ast_ptr argsDef; - ast_ptr arrow; - ast_ptr body; + ast_ptr argsDef; + ast_ptr arrow; + ast_ptr body; + AST_MEMBER(FunLit, &argsDef, &arrow, &body) AST_END(FunLit) AST_NODE(NameOrDestructure, "NameOrDestructure"_id) - ast_ptr item; // Variable_t | TableLit_t + ast_sel item; + AST_MEMBER(NameOrDestructure, &item) AST_END(NameOrDestructure) AST_NODE(AssignableNameList, "AssignableNameList"_id) - ast_ptr sep; - ast_list items; + ast_ptr sep; + ast_list items; + AST_MEMBER(AssignableNameList, &sep, &items) AST_END(AssignableNameList) AST_NODE(InvokeArgs, "InvokeArgs"_id) - ast_ptr sep; - ast_sel_list args; + ast_ptr sep; + ast_sel_list args; + AST_MEMBER(InvokeArgs, &sep, &args) AST_END(InvokeArgs) AST_LEAF(const_value, "const_value"_id) AST_END(const_value) AST_NODE(unary_exp, "unary_exp"_id) - ast_ptr item; + ast_ptr item; + AST_MEMBER(unary_exp, &item) AST_END(unary_exp) -AST_NODE(Assignment, "Assignment"_id) - ast_ptr assignable; - ast_ptr target; // Update_t | Assign_t -AST_END(Assignment) +AST_NODE(ExpListAssign, "ExpListAssign"_id) + ast_ptr expList; + ast_sel action; + AST_MEMBER(ExpListAssign, &expList, &action) +AST_END(ExpListAssign) AST_NODE(if_else_line, "if_else_line"_id) - ast_ptr condition; - ast_ptr elseExpr; // Exp_t | default_value_t + ast_ptr condition; + ast_sel elseExpr; + AST_MEMBER(if_else_line, &condition, &elseExpr) AST_END(if_else_line) AST_NODE(unless_line, "unless_line"_id) - ast_ptr condition; + ast_ptr condition; + AST_MEMBER(unless_line, &condition) AST_END(unless_line) AST_NODE(statement_appendix, "statement_appendix"_id) - ast_ptr item; // if_else_line_t | unless_line_t | CompInner_t + ast_sel item; + AST_MEMBER(statement_appendix, &item) AST_END(statement_appendix) AST_LEAF(BreakLoop, "BreakLoop"_id) AST_END(BreakLoop) AST_NODE(Statement, "Statement"_id) - ast_sel content; - ast_ptr appendix; + ExpListAssign_t> content; + ast_ptr appendix; + AST_MEMBER(Statement, &content, &appendix) AST_END(Statement) class Block_t; AST_NODE(Body, "Body"_id) - ast_ptr content; // Block | Statement + ast_sel content; + AST_MEMBER(Body, &content) AST_END(Body) AST_NODE(Block, "Block"_id) - ast_ptr sep; - ast_list statements; + ast_ptr sep; + ast_list statements; + AST_MEMBER(Block, &sep, &statements) AST_END(Block) AST_NODE(File, "File"_id) - ast_ptr block; + ast_ptr block; + AST_MEMBER(File, &block) AST_END(File) + +} // namespace MoonP diff --git a/MoonParser/moon_compiler.cpp b/MoonParser/moon_compiler.cpp new file mode 100644 index 0000000..85b19ff --- /dev/null +++ b/MoonParser/moon_compiler.cpp @@ -0,0 +1,3851 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std::string_view_literals; +#include "parser.hpp" +#include "moon_ast.h" +#include "moon_compiler.h" + +namespace MoonP { + +#define BLOCK_START do { +#define BLOCK_END } while (false); +#define BREAK_IF(cond) if (cond) break + +typedef std::list str_list; + +inline std::string s(std::string_view sv) { + return std::string(sv); +} + +class MoonCompliler { +public: + std::pair complile(const std::string& codes, bool lintGlobalVar, bool implicitReturnRoot, bool lineNumber) { + _lintGlobalVar = lintGlobalVar; + _lineNumber = lineNumber; + _input = _converter.from_bytes(codes); + error_list el; + State st; + ast_ptr root; + try { + root = parse(_input, File, el, &st); + } catch (const std::logic_error& error) { + clear(); + return {Empty, error.what()}; + } + if (root) { + try { + str_list out; + pushScope(); + transformBlock(root->block, out, implicitReturnRoot); + popScope(); + return {std::move(out.back()), Empty}; + } catch (const std::logic_error& error) { + clear(); + return {Empty, error.what()}; + } + } else { + clearBuf(); + for (error_list::iterator it = el.begin(); it != el.end(); ++it) { + const error& err = *it; + _buf << debugInfo("Syntax error."sv, &err); + } + std::pair result{Empty, clearBuf()}; + clear(); + return result; + } + } + + const std::unordered_map>& getGlobals() const { + return _globals; + } + + void clear() { + _indentOffset = 0; + _scopes.clear(); + _codeCache.clear(); + std::stack emptyWith; + _withVars.swap(emptyWith); + std::stack emptyContinue; + _continueVars.swap(emptyContinue); + _buf.str(""); + _buf.clear(); + _joinBuf.str(""); + _joinBuf.clear(); + _globals.clear(); + _input.clear(); + } +private: + bool _lintGlobalVar = false; + bool _lineNumber = false; + int _indentOffset = 0; + Converter _converter; + input _input; + std::list _codeCache; + std::stack _withVars; + std::stack _continueVars; + std::unordered_map> _globals; + std::ostringstream _buf; + std::ostringstream _joinBuf; + std::string _newLine = "\n"; + enum class LocalMode { + None = 0, + Capital = 1, + Any = 2 + }; + enum class ExportMode { + None = 0, + Capital = 1, + Any = 2 + }; + struct Scope { + ExportMode mode = ExportMode::None; + std::unique_ptr> vars; + std::unique_ptr> allows; + std::unique_ptr> exports; + }; + std::list _scopes; + static const std::string Empty; + + enum class MemType { + Builtin, + Common, + Property + }; + + struct ClassMember { + std::string item; + MemType type; + ast_node* node; + }; + + struct DestructItem { + bool isVariable = false; + std::string name; + std::string structure; + }; + + struct Destructure { + std::string value; + std::list items; + }; + + enum class ExpUsage { + Return, + Assignment, + Common, + Closure + }; + + enum class IfUsage { + Return, + Closure, + Common + }; + + void pushScope() { + _scopes.emplace_back(); + _scopes.back().vars = std::make_unique>(); + } + + void popScope() { + _scopes.pop_back(); + } + + bool isDefined(const std::string& name) { + bool isDefined = false; + int mode = int(std::isupper(name[0]) ? ExportMode::Capital : ExportMode::Any); + const auto& current = _scopes.back(); + if (int(current.mode) >= mode) { + if (current.exports) { + if (current.exports->find(name) != current.exports->end()) { + isDefined = true; + current.vars->insert(name); + } + } else { + isDefined = true; + current.vars->insert(name); + } + } + decltype(_scopes.back().allows.get()) allows = nullptr; + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + if (it->allows) allows = it->allows.get(); + } + bool checkShadowScopeOnly = false; + if (allows) { + checkShadowScopeOnly = allows->find(name) == allows->end(); + } + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + auto vars = it->vars.get(); + if (vars->find(name) != vars->end()) { + isDefined = true; + break; + } + if (checkShadowScopeOnly && it->allows) break; + } + return isDefined; + } + + bool isSolidDefined(const std::string& name) { + bool isDefined = false; + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + auto vars = it->vars.get(); + if (vars->find(name) != vars->end()) { + isDefined = true; + break; + } + } + return isDefined; + } + + void markVarShadowed() { + auto& scope = _scopes.back(); + scope.allows = std::make_unique>(); + } + + void markVarExported(ExportMode mode, bool specified) { + auto& scope = _scopes.back(); + scope.mode = mode; + if (specified && !scope.exports) { + scope.exports = std::make_unique>(); + } + } + + void addExportedVar(const std::string& name) { + auto& scope = _scopes.back(); + scope.exports->insert(name); + } + + void addToAllowList(const std::string& name) { + auto& scope = _scopes.back(); + scope.allows->insert(name); + } + + void forceAddToScope(const std::string& name) { + auto& scope = _scopes.back(); + scope.vars->insert(name); + } + + Scope& currentScope() { + return _scopes.back(); + } + + bool addToScope(const std::string& name) { + bool defined = isDefined(name); + if (!defined) { + auto& scope = currentScope(); + scope.vars->insert(name); + } + return !defined; + } + + std::string getUnusedName(std::string_view name) { + int index = 0; + std::string newName; + do { + newName = s(name) + std::to_string(index); + index++; + } while (isSolidDefined(newName)); + return newName; + } + + const std::string nll(ast_node* node) { + if (_lineNumber) { + return s(" -- "sv) + std::to_string(node->m_begin.m_line) + _newLine; + } else { + return _newLine; + } + } + + const std::string nlr(ast_node* node) { + if (_lineNumber) { + return s(" -- "sv) + std::to_string(node->m_end.m_line) + _newLine; + } else { + return _newLine; + } + } + + void incIndentOffset() { + _indentOffset++; + } + + void decIndentOffset() { + _indentOffset--; + } + + std::string indent() { + return std::string((_scopes.size() - 1 + _indentOffset) * 2, ' '); + } + + std::string indent(int offset) { + return std::string((_scopes.size() - 1 + _indentOffset + offset) * 2, ' '); + } + + std::string clearBuf() { + std::string str = _buf.str(); + _buf.str(""); + _buf.clear(); + return str; + } + + std::string join(const str_list& items) { + if (items.empty()) return Empty; + else if (items.size() == 1) return items.front(); + for (const auto& item : items) { + _joinBuf << item; + } + auto result = _joinBuf.str(); + _joinBuf.str(""); + _joinBuf.clear(); + return result; + } + + std::string join(const str_list& items, std::string_view sep) { + if (items.empty()) return Empty; + else if (items.size() == 1) return items.front(); + std::string sepStr = s(sep); + auto begin = ++items.begin(); + _joinBuf << items.front(); + for (auto it = begin; it != items.end(); ++it) { + _joinBuf << sepStr << *it; + } + auto result = _joinBuf.str(); + _joinBuf.str(""); + _joinBuf.clear(); + return result; + } + + std::string toString(ast_node* node) { + return _converter.to_bytes(std::wstring(node->m_begin.m_it, node->m_end.m_it)); + } + + std::string toString(input::iterator begin, input::iterator end) { + return _converter.to_bytes(std::wstring(begin, end)); + } + + Value_t* singleValueFrom(ast_node* item) { + Exp_t* exp = nullptr; + switch (item->getId()) { + case "Exp"_id: + exp = static_cast(item); + break; + case "ExpList"_id: { + auto expList = static_cast(item); + if (expList->exprs.size() == 1) { + exp = static_cast(expList->exprs.front()); + } + break; + } + case "ExpListLow"_id: { + auto expList = static_cast(item); + if (expList->exprs.size() == 1) { + exp = static_cast(expList->exprs.front()); + } + break; + } + } + if (!exp) return nullptr; + if (exp->opValues.empty()) { + return exp->value.get(); + } + return nullptr; + } + + SimpleValue_t* simpleSingleValueFrom(ast_node* expList) { + auto value = singleValueFrom(expList); + if (value && value->item.is()) { + return static_cast(value->item.get()); + } + return nullptr; + } + + Value_t* firstValueFrom(ast_node* item) { + Exp_t* exp = nullptr; + if (auto expList = ast_cast(item)) { + if (!expList->exprs.empty()) { + exp = static_cast(expList->exprs.front()); + } + } else { + exp = ast_cast(item); + } + return exp->value.get(); + } + + Statement_t* lastStatementFrom(Body_t* body) { + if (auto stmt = body->content.as()) { + return stmt; + } else { + auto node = body->content.to()->statements.objects().back(); + return static_cast(node); + } + } + + Statement_t* lastStatementFrom(Block_t* block) { + auto node = block->statements.objects().back(); + return static_cast(node); + } + + template + ast_ptr toAst(std::string_view codes, rule& r, ast_node* parent) { + _codeCache.push_back(_converter.from_bytes(s(codes))); + error_list el; + State st; + auto ptr = parse(_codeCache.back(), r, el, &st); + ptr->traverse([&](ast_node* node) { + node->m_begin.m_line = parent->m_begin.m_line; + node->m_end.m_line = parent->m_begin.m_line; + return traversal::Continue; + }); + return ptr; + } + + bool matchAst(rule& r, std::string_view codes) { + error_list el; + State st; + input i = _converter.from_bytes(s(codes)); + auto rEnd = rule(r >> eof()); + ast_ptr result(_parse(i, rEnd, el, &st)); + return result; + } + + bool isChainValueCall(ChainValue_t* chainValue) { + return ast_is(chainValue->items.back()); + } + + std::string singleVariableFrom(ast_node* expList) { + if (!ast_is(expList)) return Empty; + BLOCK_START + auto value = singleValueFrom(expList); + BREAK_IF(!value); + auto chainValue = value->getByPath(); + BREAK_IF(!chainValue); + BREAK_IF(chainValue->items.size() != 1); + auto callable = ast_cast(chainValue->items.front()); + BREAK_IF(!callable || !callable->item.is()); + str_list tmp; + transformCallable(callable, tmp, false); + return tmp.back(); + BLOCK_END + return Empty; + } + + bool isColonChain(ChainValue_t* chainValue) { + return ast_is(chainValue->items.back()); + } + + bool hasKeywordColonChainItem(ChainValue_t* chainValue) { + const auto& chainList = chainValue->items.objects(); + for (auto it = chainList.begin(); it != chainList.end(); ++it) { + if (auto colonItem = ast_cast(*it)) { + if (colonItem->name.is()) { + return true; + } + } + } + return false; + } + + bool isAssignable(const node_container& chainItems) { + if (chainItems.size() == 1) { + auto firstItem = chainItems.back(); + if (auto callable = ast_cast(firstItem)) { + switch (callable->item->getId()) { + case "Variable"_id: + case "SelfName"_id: + return true; + } + } else if (firstItem->getId() == "DotChainItem"_id) { + return true; + } + } else { + auto lastItem = chainItems.back(); + switch (lastItem->getId()) { + case "DotChainItem"_id: + case "Exp"_id: + return true; + } + } + return false; + } + + bool isAssignable(Exp_t* exp) { + if (auto value = singleValueFrom(exp)) { + auto item = value->item.get(); + switch (item->getId()) { + case "simple_table"_id: + return true; + case "SimpleValue"_id: { + auto simpleValue = static_cast(item); + if (simpleValue->value.is()) { + return true; + } + return false; + } + case "ChainValue"_id: { + auto chainValue = static_cast(item); + return isAssignable(chainValue->items.objects()); + } + } + } + return false; + } + + bool isAssignable(Assignable_t* assignable) { + if (auto assignableChain = ast_cast(assignable->item)) { + return isAssignable(assignableChain->items.objects()); + } + return true; + } + + void checkAssignable(ExpList_t* expList) { + for (auto exp_ : expList->exprs.objects()) { + Exp_t* exp = static_cast(exp_); + if (!isAssignable(exp)) { + throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, exp)); + } + } + } + + std::string debugInfo(std::string_view msg, const input_range* loc) { + const int ASCII = 255; + int length = loc->m_begin.m_line; + auto begin = _input.begin(); + auto end = _input.end(); + int count = 0; + for (auto it = _input.begin(); it != _input.end(); ++it) { + if (*it == '\n') { + if (count + 1 == length) { + end = it; + break; + } else { + begin = it + 1; + } + count++; + } + } + auto line = _converter.to_bytes(std::wstring(begin, end)); + int oldCol = loc->m_begin.m_col; + int col = loc->m_begin.m_col - 1; + auto it = begin; + for (int i = 0; i < oldCol; ++i) { + if (*it > ASCII) { + ++col; + } + ++it; + } + replace(line, "\t"sv, " "sv); + std::ostringstream buf; + buf << loc->m_begin.m_line << ": "sv << msg << + '\n' << line << '\n' << std::string(col, ' ') << "^"sv; + return buf.str(); + } + + void transformStatement(Statement_t* statement, str_list& out) { + auto x = statement; + if (statement->appendix) { + if (auto assignment = assignmentFrom(statement)) { + auto preDefine = getPredefine(transformAssignDefs(assignment->expList)); + if (!preDefine.empty()) out.push_back(preDefine + nll(statement)); + } + auto appendix = statement->appendix.get(); + switch (appendix->item->getId()) { + case "if_else_line"_id: { + auto if_else_line = appendix->item.to(); + auto ifNode = x->new_ptr(); + + auto ifCond = x->new_ptr(); + ifCond->condition.set(if_else_line->condition); + ifNode->nodes.push_back(ifCond); + + auto stmt = x->new_ptr(); + stmt->content.set(statement->content); + auto body = x->new_ptr(); + body->content.set(stmt); + ifNode->nodes.push_back(body); + + if (!ast_is(if_else_line->elseExpr)) { + auto expList = x->new_ptr(); + expList->exprs.push_back(if_else_line->elseExpr); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto stmt = x->new_ptr(); + stmt->content.set(expListAssign); + auto body = x->new_ptr(); + body->content.set(stmt); + ifNode->nodes.push_back(body); + } + + statement->appendix.set(nullptr); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(ifNode); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + statement->content.set(expListAssign); + break; + } + case "unless_line"_id: { + auto unless_line = appendix->item.to(); + auto unless = x->new_ptr(); + + auto ifCond = x->new_ptr(); + ifCond->condition.set(unless_line->condition); + unless->nodes.push_back(ifCond); + + auto stmt = x->new_ptr(); + stmt->content.set(statement->content); + auto body = x->new_ptr(); + body->content.set(stmt); + unless->nodes.push_back(body); + + statement->appendix.set(nullptr); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(unless); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto exprList = x->new_ptr(); + exprList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(exprList); + statement->content.set(expListAssign); + break; + } + case "CompInner"_id: { + auto compInner = appendix->item.to(); + auto comp = x->new_ptr(); + comp->forLoop.set(compInner); + auto stmt = x->new_ptr(); + stmt->content.set(statement->content); + comp->value.set(stmt); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(comp); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + statement->content.set(expListAssign); + statement->appendix.set(nullptr); + break; + } + default: break; + } + } + auto content = statement->content.get(); + if (!content) { + out.push_back(Empty); + return; + } + switch (content->getId()) { + case "Import"_id: transformImport(static_cast(content), out); break; + case "While"_id: transformWhile(static_cast(content), out); break; + case "For"_id: transformFor(static_cast(content), out); break; + case "ForEach"_id: transformForEach(static_cast(content), out); break; + case "Return"_id: transformReturn(static_cast(content), out); break; + case "Local"_id: transformLocal(static_cast(content), out); break; + case "Export"_id: transformExport(static_cast(content), out); break; + case "BreakLoop"_id: transformBreakLoop(static_cast(content), out); break; + case "ExpListAssign"_id: { + auto expListAssign = static_cast(content); + if (expListAssign->action) { + transformAssignment(expListAssign, out); + } else { + auto expList = expListAssign->expList.get(); + if (expList->exprs.objects().empty()) { + out.push_back(Empty); + break; + } + if (auto singleValue = singleValueFrom(expList)) { + if (auto simpleValue = singleValue->item.as()) { + auto value = simpleValue->value.get(); + bool specialSingleValue = true; + switch (value->getId()) { + case "If"_id: transformIf(static_cast(value), out); break; + case "ClassDecl"_id: transformClassDecl(static_cast(value), out); break; + case "Unless"_id: transformUnless(static_cast(value), out); break; + case "Switch"_id: transformSwitch(static_cast(value), out); break; + case "With"_id: transformWith(static_cast(value), out); break; + case "ForEach"_id: transformForEach(static_cast(value), out); break; + case "For"_id: transformFor(static_cast(value), out); break; + case "While"_id: transformWhile(static_cast(value), out); break; + case "Do"_id: transformDo(static_cast(value), out); break; + case "Comprehension"_id: transformCompCommon(static_cast(value), out); break; + default: specialSingleValue = false; break; + } + if (specialSingleValue) { + break; + } + } + if (auto chainValue = singleValue->item.as()) { + if (isChainValueCall(chainValue)) { + transformChainValue(chainValue, out, ExpUsage::Common); + break; + } + } + } + auto assign = x->new_ptr(); + assign->values.dup(expList->exprs); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst("_", ExpList, x)); + assignment->action.set(assign); + transformAssignment(assignment, out); + } + break; + } + default: break; + } + } + + str_list getAssignVars(ExpListAssign_t* assignment) { + str_list vars; + if (!assignment->action.is()) return vars; + for (auto exp : assignment->expList->exprs.objects()) { + auto var = singleVariableFrom(exp); + vars.push_back(var.empty() ? Empty : var); + } + return vars; + } + + str_list getAssignVars(With_t* with) { + str_list vars; + for (auto exp : with->valueList->exprs.objects()) { + auto var = singleVariableFrom(exp); + vars.push_back(var.empty() ? Empty : var); + } + return vars; + } + + str_list getAssignDefs(ExpList_t* expList) { + str_list preDefs; + for (auto exp_ : expList->exprs.objects()) { + auto exp = static_cast(exp_); + if (auto value = singleValueFrom(exp)) { + if (auto chain = value->item.as()) { + BLOCK_START + BREAK_IF(chain->items.size() != 1); + auto callable = ast_cast(chain->items.front()); + BREAK_IF(!callable); + auto var = callable->item.as(); + BREAK_IF(!var); + auto name = toString(var); + if (!isDefined(name)) { + preDefs.push_back(name); + } + BLOCK_END + } + } else { + throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, exp)); + } + } + return preDefs; + } + + str_list transformAssignDefs(ExpList_t* expList) { + str_list preDefs; + for (auto exp_ : expList->exprs.objects()) { + auto exp = static_cast(exp_); + if (auto value = singleValueFrom(exp)) { + if (auto chain = value->item.as()) { + BLOCK_START + BREAK_IF(chain->items.size() != 1); + auto callable = ast_cast(chain->items.front()); + BREAK_IF(!callable); + auto var = callable->item.as(); + BREAK_IF(!var); + auto name = toString(var); + if (addToScope(name)) { + preDefs.push_back(name); + } + BLOCK_END + } + } else { + throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, exp)); + } + } + return preDefs; + } + + std::string getPredefine(const str_list& defs) { + if (defs.empty()) return Empty; + return indent() + s("local "sv) + join(defs, ", "sv); + } + + std::string getDestrucureDefine(ExpListAssign_t* assignment) { + auto info = extractDestructureInfo(assignment); + if (!info.first.empty()) { + for (const auto& destruct : info.first) { + str_list defs; + for (const auto& item : destruct.items) { + if (item.isVariable && addToScope(item.name)) { + defs.push_back(item.name); + } + } + if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv); + } + } + return clearBuf(); + } + + std::string getPredefine(ExpListAssign_t* assignment) { + auto preDefine = getDestrucureDefine(assignment); + if (preDefine.empty()) { + preDefine = getPredefine(transformAssignDefs(assignment->expList)); + } + return preDefine.empty() ? preDefine : preDefine + nll(assignment); + } + + ExpList_t* expListFrom(Statement_t* statement) { + if (auto expListAssign = statement->content.as()) { + if (!expListAssign->action) { + return expListAssign->expList.get(); + } + } + return nullptr; + } + + ExpListAssign_t* assignmentFrom(Statement_t* statement) { + if (auto expListAssign = statement->content.as()) { + if (expListAssign->action) { + return expListAssign; + } + } + return nullptr; + } + + void assignLastExplist(ExpList_t* expList, Body_t* body) { + auto last = lastStatementFrom(body); + if (!last) return; + bool lastAssignable = expListFrom(last) || ast_is(last->content); + if (lastAssignable) { + auto x = last; + auto newAssignment = x->new_ptr(); + newAssignment->expList.set(expList); + auto assign = x->new_ptr(); + if (auto valueList = last->content.as()) { + assign->values.dup(valueList->expList->exprs); + } else { + auto simpleValue = x->new_ptr(); + simpleValue->value.set(last->content); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + assign->values.push_back(exp); + } + newAssignment->action.set(assign); + last->content.set(newAssignment); + } + } + + void transformAssignment(ExpListAssign_t* assignment, str_list& out) { + checkAssignable(assignment->expList); + BLOCK_START + auto assign = ast_cast(assignment->action); + BREAK_IF(!assign || assign->values.objects().size() != 1); + auto value = assign->values.objects().front(); + if (ast_is(value)) { + if (auto val = simpleSingleValueFrom(value)) { + value = val->value.get(); + } + } + switch (value->getId()) { + case "If"_id: + case "Unless"_id: { + auto expList = assignment->expList.get(); + str_list temp; + auto defs = transformAssignDefs(expList); + if (!defs.empty()) temp.push_back(getPredefine(defs) + nll(expList)); + value->traverse([&](ast_node* node) { + switch (node->getId()) { + case "IfCond"_id: return traversal::Return; + case "Body"_id: { + auto body = static_cast(node); + assignLastExplist(expList, body); + return traversal::Return; + } + default: return traversal::Continue; + } + }); + switch (value->getId()) { + case "If"_id: transformIf(static_cast(value), temp); break; + case "Unless"_id: transformUnless(static_cast(value), temp); break; + } + out.push_back(join(temp)); + return; + } + case "Switch"_id: { + auto switchNode = static_cast(value); + auto expList = assignment->expList.get(); + for (auto branch_ : switchNode->branches.objects()) { + auto branch = static_cast(branch_); + assignLastExplist(expList, branch->body); + } + if (switchNode->lastBranch) { + assignLastExplist(expList, switchNode->lastBranch); + } + std::string preDefine = getPredefine(assignment); + transformSwitch(switchNode, out); + out.back() = preDefine + out.back(); + return; + } + case "With"_id: { + auto withNode = static_cast(value); + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformWith(withNode, temp, expList); + out.push_back(preDefine + temp.back()); + return; + } + case "Do"_id: { + auto doNode = static_cast(value); + auto expList = assignment->expList.get(); + assignLastExplist(expList, doNode->body); + std::string preDefine = getPredefine(assignment); + transformDo(doNode, out); + out.back() = preDefine + out.back(); + return; + } + case "Comprehension"_id: { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformCompInPlace(static_cast(value), expList, out); + out.back() = preDefine + out.back(); + return; + } + case "TblComprehension"_id: { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformTblCompInPlace(static_cast(value), expList, out); + out.back() = preDefine + out.back(); + return; + } + case "For"_id: { + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformForInPlace(static_cast(value), temp, expList); + out.push_back(preDefine + temp.back()); + return; + } + case "ForEach"_id: { + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformForEachInPlace(static_cast(value), temp, expList); + out.push_back(preDefine + temp.back()); + return; + } + case "ClassDecl"_id: { + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformClassDecl(static_cast(value), temp, ExpUsage::Assignment, expList); + out.push_back(preDefine + temp.back()); + return; + } + case "While"_id: { + str_list temp; + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformWhileInPlace(static_cast(value), temp, expList); + out.push_back(preDefine + temp.back()); + return; + } + } + auto exp = ast_cast(value); + BREAK_IF(!exp); + if (auto chainValue = exp->value->item.as()) { + if (isColonChain(chainValue)) { + auto assignable = assignment->expList.get(); + std::string preDefine = getPredefine(transformAssignDefs(assignable)); + transformColonChain(chainValue, out, ExpUsage::Assignment, assignable); + auto nl = preDefine.empty() ? Empty : nll(chainValue); + if (!preDefine.empty()) out.back() = preDefine + nl + out.back(); + return; + } else if (hasKeywordColonChainItem(chainValue)) { + transformChainValue(chainValue, out, ExpUsage::Assignment, assignment->expList); + return; + } + } + BLOCK_END + auto info = extractDestructureInfo(assignment); + if (info.first.empty()) { + transformAssignmentCommon(assignment, out); + } else { + str_list temp; + for (const auto& destruct : info.first) { + if (destruct.items.size() == 1) { + auto& pair = destruct.items.front(); + _buf << indent(); + if (pair.isVariable && !isDefined(pair.name)) { + _buf << s("local "sv); + } + _buf << pair.name << " = "sv << info.first.front().value << pair.structure << nll(assignment); + addToScope(pair.name); + temp.push_back(clearBuf()); + } else if (matchAst(Name, destruct.value)) { + str_list defs, names, values; + for (const auto& item : destruct.items) { + if (item.isVariable && addToScope(item.name)) { + defs.push_back(item.name); + } + names.push_back(item.name); + values.push_back(item.structure); + } + for (auto& v : values) v.insert(0, destruct.value); + if (defs.empty()) { + _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); + } else { + _buf << indent() << "local "sv; + if (defs.size() != names.size()) { + _buf << join(defs,", "sv) << nll(assignment) << indent(); + } + _buf << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); + } + temp.push_back(clearBuf()); + } else { + str_list defs, names, values; + for (const auto& item : destruct.items) { + if (item.isVariable && addToScope(item.name)) { + defs.push_back(item.name); + } + names.push_back(item.name); + values.push_back(item.structure); + } + if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv) << nll(assignment); + _buf << indent() << "do"sv << nll(assignment); + pushScope(); + auto objVar = getUnusedName("_obj_"); + for (auto& v : values) v.insert(0, objVar); + _buf << indent() << "local "sv << objVar << " = "sv << destruct.value << nll(assignment); + _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); + popScope(); + _buf << indent() << "end"sv << nll(assignment); + temp.push_back(clearBuf()); + } + } + if (info.second) { + transformAssignmentCommon(info.second, temp); + } + out.push_back(join(temp)); + } + } + + void transformAssignItem(ast_node* value, str_list& out) { + switch (value->getId()) { + case "With"_id: transformWithClosure(static_cast(value), out); break; + case "If"_id: transformIf(static_cast(value), out, IfUsage::Closure); break; + case "Switch"_id: transformSwitchClosure(static_cast(value), out); break; + case "TableBlock"_id: transformTableBlock(static_cast(value), out); break; + case "Exp"_id: transformExp(static_cast(value), out); break; + default: break; + } + } + + std::list destructFromExp(ast_node* node) { + const node_container* tableItems = nullptr; + if (ast_cast(node)) { + auto item = singleValueFrom(node)->item.get(); + if (!item) throw std::logic_error(debugInfo("Invalid destructure value."sv, node)); + auto tbA = item->getByPath(); + if (tbA) { + tableItems = &tbA->values.objects(); + } else { + auto tbB = ast_cast(item); + if (tbB) tableItems = &tbB->pairs.objects(); + } + } else if (auto table = ast_cast(node)) { + tableItems = &table->values.objects(); + } + std::list pairs; + int index = 0; + for (auto pair : *tableItems) { + switch (pair->getId()) { + case "Exp"_id: { + ++index; + if (!isAssignable(static_cast(pair))) { + throw std::logic_error(debugInfo("Can't destructure value."sv, pair)); + } + auto value = singleValueFrom(pair); + auto item = value->item.get(); + if (ast_cast(item) || + item->getByPath()) { + auto subPairs = destructFromExp(pair); + for (auto& p : subPairs) { + pairs.push_back({p.isVariable, p.name, + s("["sv) + std::to_string(index) + s("]"sv) + p.structure}); + } + } else { + bool checkGlobal = _lintGlobalVar; + _lintGlobalVar = false; + auto exp = static_cast(pair); + auto varName = singleVariableFrom(exp); + bool isVariable = !varName.empty(); + if (!isVariable) { + str_list temp; + transformExp(exp, temp); + varName = std::move(temp.back()); + } + _lintGlobalVar = checkGlobal; + pairs.push_back({ + isVariable, + varName, + s("["sv) + std::to_string(index) + s("]"sv) + }); + } + break; + } + case "variable_pair"_id: { + auto vp = static_cast(pair); + auto name = toString(vp->name); + pairs.push_back({true, name, s("."sv) + name}); + break; + } + case "normal_pair"_id: { + auto np = static_cast(pair); + auto key = np->key->getByPath(); + if (!key) throw std::logic_error(debugInfo("Invalid key for destructure."sv, np)); + if (auto exp = np->value.as()) { + if (!isAssignable(exp)) throw std::logic_error(debugInfo("Can't destructure value."sv, exp)); + auto item = singleValueFrom(exp)->item.get(); + if (ast_cast(item) || + item->getByPath()) { + auto subPairs = destructFromExp(exp); + for (auto& p : subPairs) { + pairs.push_back({p.isVariable, p.name, + s("."sv) + toString(key) + p.structure}); + } + } else { + bool checkGlobal = _lintGlobalVar; + _lintGlobalVar = false; + auto varName = singleVariableFrom(exp); + bool isVariable = !varName.empty(); + if (!isVariable) { + str_list temp; + transformExp(exp, temp); + varName = std::move(temp.back()); + } + _lintGlobalVar = checkGlobal; + pairs.push_back({ + isVariable, + varName, + s("."sv) + toString(key) + }); + } + break; + } + if (np->value.as()) { + auto subPairs = destructFromExp(pair); + for (auto& p : subPairs) { + pairs.push_back({p.isVariable, p.name, + s("."sv) + toString(key) + p.structure}); + } + } + break; + } + } + } + return pairs; + } + + std::pair, ast_ptr> + extractDestructureInfo(ExpListAssign_t* assignment) { + auto x = assignment; + std::list destructs; + if (!assignment->action.is()) return { destructs, nullptr }; + auto exprs = assignment->expList->exprs.objects(); + auto values = assignment->action.to()->values.objects(); + size_t size = std::max(exprs.size(),values.size()); + ast_ptr var; + if (exprs.size() < size) { + var = toAst("_"sv, Exp, x); + while (exprs.size() < size) exprs.emplace_back(var); + } + ast_ptr nullNode; + if (values.size() < size) { + nullNode = toAst("nil"sv, Exp, x); + while (values.size() < size) values.emplace_back(nullNode); + } + using iter = node_container::iterator; + std::vector> destructPairs; + str_list temp; + for (auto i = exprs.begin(), j = values.begin(); i != exprs.end(); ++i, ++j) { + auto expr = *i; + ast_node* destructNode = expr->getByPath(); + if (destructNode || (destructNode = expr->getByPath())) { + destructPairs.push_back({i,j}); + pushScope(); + transformAssignItem(*j, temp); + popScope(); + auto& destruct = destructs.emplace_back(); + destruct.value = temp.back(); + temp.pop_back(); + auto pairs = destructFromExp(expr); + destruct.items = std::move(pairs); + } + } + for (const auto& p : destructPairs) { + exprs.erase(p.first); + values.erase(p.second); + } + ast_ptr newAssignment; + if (!destructPairs.empty() && !exprs.empty()) { + auto x = assignment; + auto expList = x->new_ptr(); + auto newAssign = x->new_ptr(); + newAssign->expList.set(expList); + for (auto expr : exprs) expList->exprs.push_back(expr); + auto assign = x->new_ptr(); + for (auto value : values) assign->values.push_back(value); + newAssign->action.set(assign); + newAssignment = newAssign; + } + return {std::move(destructs), newAssignment}; + } + + void transformAssignmentCommon(ExpListAssign_t* assignment, str_list& out) { + auto x = assignment; + str_list temp; + auto expList = assignment->expList.get(); + auto action = assignment->action.get(); + switch (action->getId()) { + case "Update"_id: { + auto update = static_cast(action); + auto leftExp = static_cast(expList->exprs.objects().front()); + auto leftValue = singleValueFrom(leftExp); + if (!leftValue) throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, leftExp)); + if (auto chain = leftValue->getByPath()) { + auto tmpChain = x->new_ptr(); + for (auto item : chain->items.objects()) { + bool itemAdded = false; + BLOCK_START + auto exp = ast_cast(item); + BREAK_IF(!exp); + auto var = singleVariableFrom(exp); + BREAK_IF(!var.empty()); + auto upVar = getUnusedName("_update_"); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(upVar, ExpList, x)); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + transformAssignment(assignment, temp); + tmpChain->items.push_back(toAst(upVar, Exp, x)); + itemAdded = true; + BLOCK_END + if (!itemAdded) tmpChain->items.push_back(item); + } + chain->items.clear(); + chain->items.dup(tmpChain->items); + } + transformValue(leftValue, temp); + auto left = std::move(temp.back()); + temp.pop_back(); + transformExp(update->value, temp); + auto right = std::move(temp.back()); + temp.pop_back(); + if (!singleValueFrom(update->value)) { + right = s("("sv) + right + s(")"sv); + } + _buf << join(temp) << indent() << left << " = "sv << left << + " "sv << toString(update->op) << " "sv << right << nll(assignment); + out.push_back(clearBuf()); + break; + } + case "Assign"_id: { + auto defs = getAssignDefs(expList); + bool oneLined = defs.size() == expList->exprs.objects().size() && + traversal::Stop != action->traverse([&](ast_node* n) { + if (n->getId() == "Callable"_id) { + if (auto name = n->getByPath()) { + for (const auto& def : defs) { + if (def == toString(name)) { + return traversal::Stop; + } + } + } + } + return traversal::Continue; + }); + if (oneLined) { + auto assign = static_cast(action); + for (auto value : assign->values.objects()) { + transformAssignItem(value, temp); + } + std::string preDefine = getPredefine(defs); + for (const auto& def : defs) { + addToScope(def); + } + if (preDefine.empty()) { + transformExpList(expList, temp); + std::string left = std::move(temp.back()); + temp.pop_back(); + out.push_back(indent() + left + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); + } else { + out.push_back(preDefine + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); + } + } + else { + std::string preDefine = getPredefine(defs); + for (const auto& def : defs) { + addToScope(def); + } + transformExpList(expList, temp); + std::string left = temp.back(); + temp.pop_back(); + auto assign = static_cast(action); + for (auto value : assign->values.objects()) { + transformAssignItem(value, temp); + } + out.push_back((preDefine.empty() ? Empty : preDefine + nll(assignment)) + indent() + left + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); + } + break; + } + default: break; + } + } + + void transformCond(const node_container& nodes, str_list& out, IfUsage usage = IfUsage::Common, bool unless = false) { + std::vector> ns(false); + for (auto it = nodes.rbegin(); it != nodes.rend(); ++it) { + ns.push_back(*it); + if (auto cond = ast_cast(*it)) { + if (*it != nodes.front() && cond->assign) { + auto x = *it; + auto newIf = x->new_ptr(); + for (auto j = ns.rbegin(); j != ns.rend(); ++j) { + newIf->nodes.push_back(*j); + } + ns.clear(); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(newIf); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto stmt = x->new_ptr(); + stmt->content.set(expListAssign); + auto body = x->new_ptr(); + body->content.set(stmt); + ns.push_back(body.get()); + } + } + } + if (nodes.size() != ns.size()) { + auto x = ns.back(); + auto newIf = x->new_ptr(); + for (auto j = ns.rbegin(); j != ns.rend(); ++j) { + newIf->nodes.push_back(*j); + } + transformCond(newIf->nodes.objects(), out, usage, unless); + return; + } + str_list temp; + if (usage == IfUsage::Closure) { + temp.push_back(s("(function()"sv) + nll(nodes.front())); + pushScope(); + } + std::list> ifCondPairs; + ifCondPairs.emplace_back(); + for (auto node : nodes) { + switch (node->getId()) { + case "IfCond"_id: + ifCondPairs.back().first = static_cast(node); + break; + case "Body"_id: + ifCondPairs.back().second = static_cast(node); + ifCondPairs.emplace_back(); + break; + default: break; + } + } + auto assign = ifCondPairs.front().first->assign.get(); + bool storingValue = false; + ast_ptr extraAssignment; + if (assign) { + auto exp = ifCondPairs.front().first->condition.get(); + auto x = exp; + auto var = singleVariableFrom(exp); + if (var.empty()) { + storingValue = true; + std::string desVar = getUnusedName("_des_"); + if (assign->values.objects().size() == 1) { + auto var = singleVariableFrom(assign->values.objects().front()); + if (!var.empty()) { + desVar = var; + storingValue = false; + } + } + if (storingValue) { + if (usage != IfUsage::Closure) { + temp.push_back(indent() + s("do"sv) + nll(assign)); + pushScope(); + } + auto expList = toAst(desVar, ExpList, x); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + { + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assignOne = x->new_ptr(); + auto valExp = toAst(desVar, Exp, x); + assignOne->values.push_back(valExp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assignOne); + extraAssignment.set(assignment); + ifCondPairs.front().first->condition.set(valExp); + } + } else { + if (!isDefined(var)) { + storingValue = true; + if (usage != IfUsage::Closure) { + temp.push_back(indent() + s("do"sv) + nll(assign)); + pushScope(); + } + } + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + } + for (const auto& pair : ifCondPairs) { + if (pair.first) { + str_list tmp; + auto condition = pair.first->condition.get(); + if (unless) { + if (auto value = singleValueFrom(condition)) { + transformValue(value, tmp); + } else { + transformExp(condition, tmp); + tmp.back() = s("("sv) + tmp.back() + s(")"sv); + } + tmp.back().insert(0, s("not "sv)); + unless = false; + } else { + transformExp(condition, tmp); + } + _buf << indent(); + if (pair != ifCondPairs.front()) { + _buf << "else"sv; + } + _buf << "if "sv << tmp.back() << " then"sv << nll(condition); + temp.push_back(clearBuf()); + } + if (pair.second) { + if (!pair.first) { + temp.push_back(indent() + s("else"sv) + nll(pair.second)); + } + pushScope(); + if (pair == ifCondPairs.front() && extraAssignment) { + transformAssignment(extraAssignment, temp); + } + transformBody(pair.second, temp, usage != IfUsage::Common); + popScope(); + } + if (!pair.first) { + temp.push_back(indent() + s("end"sv) + nll(nodes.front())); + break; + } + } + if (storingValue && usage != IfUsage::Closure) { + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(nodes.front())); + } + if (usage == IfUsage::Closure) { + popScope(); + temp.push_back(indent() + s("end)()"sv)); + } + out.push_back(join(temp)); + } + + void transformIf(If_t* ifNode, str_list& out, IfUsage usage = IfUsage::Common) { + transformCond(ifNode->nodes.objects(), out, usage); + } + + void transformUnless(Unless_t* unless, str_list& out, IfUsage usage = IfUsage::Common) { + transformCond(unless->nodes.objects(), out, usage, true); + } + + void transformExpList(ExpList_t* expList, str_list& out) { + str_list temp; + for (auto exp : expList->exprs.objects()) { + transformExp(static_cast(exp), temp); + } + out.push_back(join(temp, ", "sv)); + } + + void transformExpListLow(ExpListLow_t* expListLow, str_list& out) { + str_list temp; + for (auto exp : expListLow->exprs.objects()) { + transformExp(static_cast(exp), temp); + } + out.push_back(join(temp, ", "sv)); + } + + void transformExp(Exp_t* exp, str_list& out) { + str_list temp; + transformValue(exp->value, temp); + for (auto _opValue : exp->opValues.objects()) { + auto opValue = static_cast(_opValue); + transformBinaryOperator(opValue->op, temp); + transformValue(opValue->value, temp); + } + out.push_back(join(temp, " "sv)); + } + + void transformValue(Value_t* value, str_list& out) { + auto item = value->item.get(); + switch (item->getId()) { + case "SimpleValue"_id: transformSimpleValue(static_cast(item), out); break; + case "simple_table"_id: transform_simple_table(static_cast(item), out); break; + case "ChainValue"_id: { + auto chainValue = static_cast(item); + if (isColonChain(chainValue)) { + transformColonChainClosure(chainValue, out); + } else { + transformChainValue(chainValue, out); + } + break; + } + case "String"_id: transformString(static_cast(item), out); break; + default: break; + } + } + + void transformCallable(Callable_t* callable, str_list& out, bool invoke) { + auto item = callable->item.get(); + switch (item->getId()) { + case "Variable"_id: { + transformVariable(static_cast(item), out); + if (_lintGlobalVar && !isDefined(out.back())) { + if (_globals.find(out.back()) == _globals.end()) { + _globals[out.back()] = {item->m_begin.m_line, item->m_begin.m_col}; + } + } + break; + } + case "SelfName"_id: { transformSelfName(static_cast(item), out, invoke); + if (_lintGlobalVar) { + std::string self("self"sv); + if (!isDefined(self)) { + if (_globals.find(self) == _globals.end()) { + _globals[self] = {item->m_begin.m_line, item->m_begin.m_col}; + } + } + } + break; + } + case "VarArg"_id: out.push_back(s("..."sv)); break; + case "Parens"_id: transformParens(static_cast(item), out); break; + default: break; + } + } + + void transformParens(Parens_t* parans, str_list& out) { + str_list temp; + transformExp(parans->expr, temp); + out.push_back(s("("sv) + temp.front() + s(")"sv)); + } + + void transformSimpleValue(SimpleValue_t* simpleValue, str_list& out) { + auto value = simpleValue->value.get(); + switch (value->getId()) { + case "const_value"_id: transform_const_value(static_cast(value), out); break; + case "If"_id: transformIf(static_cast(value), out, IfUsage::Closure); break; + case "Unless"_id: transformUnless(static_cast(value), out, IfUsage::Closure); break; + case "Switch"_id: transformSwitchClosure(static_cast(value), out); break; + case "With"_id: transformWithClosure(static_cast(value), out); break; + case "ClassDecl"_id: transformClassDeclClosure(static_cast(value), out); break; + case "ForEach"_id: transformForEachClosure(static_cast(value), out); break; + case "For"_id: transformForClosure(static_cast(value), out); break; + case "While"_id: transformWhileClosure(static_cast(value), out); break; + case "Do"_id: transformDoClosure(static_cast(value), out); break; + case "unary_exp"_id: transform_unary_exp(static_cast(value), out); break; + case "TblComprehension"_id: transformTblCompClosure(static_cast(value), out); break; + case "TableLit"_id: transformTableLit(static_cast(value), out); break; + case "Comprehension"_id: transformCompClosure(static_cast(value), out); break; + case "FunLit"_id: transformFunLit(static_cast(value), out); break; + case "Num"_id: transformNum(static_cast(value), out); break; + default: break; + } + } + + void transformFunLit(FunLit_t* funLit, str_list& out) { + str_list temp; + bool isFatArrow = toString(funLit->arrow) == "=>"sv; + pushScope(); + if (isFatArrow) { + forceAddToScope(s("self"sv)); + } + if (auto argsDef = funLit->argsDef.get()) { + transformFnArgsDef(argsDef, temp); + if (funLit->body) { + transformBody(funLit->body, temp, true); + } else { + temp.push_back(Empty); + } + auto it = temp.begin(); + auto& args = *it; + auto& initArgs = *(++it); + auto& bodyCodes = *(++it); + _buf << "function("sv << + (isFatArrow ? s("self, "sv) : Empty) << + args << ')'; + if (!initArgs.empty() || !bodyCodes.empty()) { + _buf << nlr(argsDef) << initArgs << bodyCodes; + popScope(); + _buf << indent() << "end"sv; + } else { + popScope(); + _buf << " end"sv; + } + } else { + if (funLit->body) { + transformBody(funLit->body, temp, true); + } else { + temp.push_back(Empty); + } + auto& bodyCodes = temp.back(); + _buf << "function("sv << + (isFatArrow ? s("self"sv) : Empty) << + ')'; + if (!bodyCodes.empty()) { + _buf << nll(funLit) << bodyCodes; + popScope(); + _buf << indent() << "end"sv; + } else { + popScope(); + _buf << " end"sv; + } + } + out.push_back(clearBuf()); + } + + void transformCodes(const node_container& nodes, str_list& out, bool implicitReturn) { + LocalMode mode = LocalMode::None; + Local_t* any = nullptr, *capital = nullptr; + for (auto node : nodes) { + auto stmt = static_cast(node); + if (auto local = stmt->content.as()) { + if (auto flag = local->name.as()) { + LocalMode newMode = toString(flag) == "*"sv ? LocalMode::Any : LocalMode::Capital; + if (int(newMode) > int(mode)) { + mode = newMode; + } + if (mode == LocalMode::Any) { + if (!any) any = local; + if (!capital) capital = local; + } else { + if (!capital) capital = local; + } + } else { + auto names = local->name.to(); + for (auto name : names->names.objects()) { + local->forceDecls.push_back(toString(name)); + } + } + } else if (mode != LocalMode::None) { + ClassDecl_t* classDecl = nullptr; + if (auto assignment = assignmentFrom(stmt)) { + auto vars = getAssignVars(assignment); + for (const auto& var : vars) { + if (var.empty()) continue; + if (std::isupper(var[0]) && capital) { + capital->decls.push_back(var); + } else if (any) { + any->decls.push_back(var); + } + } + auto info = extractDestructureInfo(assignment); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable) { + if (std::isupper(item.name[0]) && capital) { capital->decls.push_back(item.name); + } else if (any) { + any->decls.push_back(item.name); + } + } + } + BLOCK_START + auto assign = assignment->action.as(); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto exp = ast_cast(assign->values.objects().front()); + BREAK_IF(!exp); + auto value = singleValueFrom(exp); + classDecl = value->getByPath(); + BLOCK_END + } else if (auto expList = expListFrom(stmt)) { + auto value = singleValueFrom(expList); + classDecl = value->getByPath(); + } + if (classDecl) { + if (auto variable = classDecl->name->item.as()) { + auto className = toString(variable); + if (!className.empty()) { + if (std::isupper(className[0]) && capital) { + capital->decls.push_back(className); + } else if (any) { + any->decls.push_back(className); + } + } + } + } + } + } + if (implicitReturn) { + auto last = static_cast(nodes.back()); + auto x = last; + BLOCK_START + auto expList = expListFrom(last); + BREAK_IF(!expList || + (last->appendix && + last->appendix->item.is())); + auto expListLow = x->new_ptr(); + expListLow->exprs.dup(expList->exprs); + auto returnNode = x->new_ptr(); + returnNode->valueList.set(expListLow); + last->content.set(returnNode); + BLOCK_END + } + str_list temp; + for (auto node : nodes) { + transformStatement(static_cast(node), temp); + } + out.push_back(join(temp)); + } + + void transformBody(Body_t* body, str_list& out, bool implicitReturn = false) { + if (auto stmt = body->content.as()) { + transformCodes(node_container{stmt}, out, implicitReturn); + } else { + transformCodes(body->content.to()->statements.objects(), out, implicitReturn); + } + } + + void transformBlock(Block_t* block, str_list& out, bool implicitReturn = true) { + transformCodes(block->statements.objects(), out, implicitReturn); + } + + void transformReturn(Return_t* returnNode, str_list& out) { + if (auto valueList = returnNode->valueList.get()) { + if (auto singleValue = singleValueFrom(valueList)) { + if (auto simpleValue = singleValue->item.as()) { + auto value = simpleValue->value.get(); + switch (value->getId()) { + case "Comprehension"_id: + transformCompReturn(static_cast(value), out); + return; + case "TblComprehension"_id: + transformTblCompReturn(static_cast(value), out); + return; + case "With"_id: + transformWith(static_cast(value), out, nullptr, true); + return; + case "ClassDecl"_id: + transformClassDecl(static_cast(value), out, ExpUsage::Return); + return; + case "Do"_id: + transformDo(static_cast(value), out, true); + return; + case "Switch"_id: + transformSwitch(static_cast(value), out, true); + return; + case "While"_id: + transformWhileInPlace(static_cast(value), out); + return; + case "For"_id: + transformForInPlace(static_cast(value), out); + return; + case "ForEach"_id: + transformForEachInPlace(static_cast(value), out); + return; + case "If"_id: + transformIf(static_cast(value), out, IfUsage::Return); + return; + case "Unless"_id: + transformUnless(static_cast(value), out, IfUsage::Return); + return; + } + } + if (auto chainValue = singleValue->item.as()) { + if (isColonChain(chainValue)) { + transformColonChain(chainValue, out, ExpUsage::Return); + } else { + transformChainValue(chainValue, out, ExpUsage::Return); + } + return; + } + transformValue(singleValue, out); + out.back() = indent() + s("return "sv) + out.back() + nlr(returnNode); + return; + } else { + str_list temp; + transformExpListLow(valueList, temp); + out.push_back(indent() + s("return "sv) + temp.back() + nlr(returnNode)); + } + } else { + out.push_back(indent() + s("return"sv) + nll(returnNode)); + } + } + + void transformFnArgsDef(FnArgsDef_t* argsDef, str_list& out) { + if (!argsDef->defList) { + out.push_back(Empty); + out.push_back(Empty); + } else { + transformFnArgDefList(argsDef->defList, out); + } + if (argsDef->shadowOption) { + transform_outer_var_shadow(argsDef->shadowOption); + } + } + + void transform_outer_var_shadow(outer_var_shadow_t* shadow) { + markVarShadowed(); + if (shadow->varList) { + for (auto name : shadow->varList->names.objects()) { + addToAllowList(toString(name)); + } + } + } + + void transformFnArgDefList(FnArgDefList_t* argDefList, str_list& out) { + auto x = argDefList; + struct ArgItem { + std::string name; + std::string assignSelf; + }; + std::list argItems; + str_list temp; + std::string varNames; + bool assignSelf = false; + for (auto _def : argDefList->definitions.objects()) { + auto def = static_cast(_def); + auto& arg = argItems.emplace_back(); + switch (def->name->getId()) { + case "Variable"_id: arg.name = toString(def->name); break; + case "SelfName"_id: { + assignSelf = true; + auto selfName = static_cast(def->name.get()); + switch (selfName->name->getId()) { + case "self_class_name"_id: { + auto clsName = static_cast(selfName->name.get()); + arg.name = toString(clsName->name); + arg.assignSelf = s("self.__class."sv) + arg.name; + break; + } + case "self_class"_id: + arg.name = "self.__class"sv; + break; + case "self_name"_id: { + + auto sfName = static_cast(selfName->name.get()); + arg.name = toString(sfName->name); + arg.assignSelf = s("self."sv) + arg.name; + break; + } + case "self"_id: + arg.name = "self"sv; + break; + default: break; + } + break; + } + } + forceAddToScope(arg.name); + if (def->defaultValue) { + pushScope(); + auto expList = toAst(arg.name, ExpList, x); + auto assign = x->new_ptr(); + assign->values.push_back(def->defaultValue.get()); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + _buf << indent() << "if "sv << arg.name << " == nil then"sv << nll(def); + _buf << temp.back(); + _buf << indent() << "end"sv << nll(def); + temp.back() = clearBuf(); + } + if (varNames.empty()) varNames = arg.name; + else varNames.append(s(", "sv) + arg.name); + } + if (argDefList->varArg) { + auto& arg = argItems.emplace_back(); + arg.name = "..."sv; + if (varNames.empty()) varNames = arg.name; + else varNames.append(s(", "sv) + arg.name); + } + std::string initCodes = join(temp); + if (assignSelf) { + auto sjoin = [](const decltype(argItems)& items, int index) { + std::string result; + for (auto it = items.begin(); it != items.end(); ++it) { + if (it->assignSelf.empty()) continue; + if (result.empty()) result = (&it->name)[index]; + else result.append(s(", "sv) + (&it->name)[index]); + } + return result; + }; + std::string sleft = sjoin(argItems, 1); + std::string sright = sjoin(argItems, 0); + initCodes.append(indent() + sleft + s(" = "sv) + sright + nll(argDefList)); + } + out.push_back(varNames); + out.push_back(initCodes); + } + + void transformSelfName(SelfName_t* selfName, str_list& out, bool invoke) { + auto name = selfName->name.get(); + switch (name->getId()) { + case "self_class_name"_id: { + auto clsName = static_cast(name); + out.push_back(s("self.__class"sv) + s(invoke ? ":"sv : "."sv) + toString(clsName->name)); + break; + } + case "self_class"_id: + out.push_back(s("self.__class"sv)); + break; + case "self_name"_id: { + auto sfName = static_cast(name); + out.push_back(s("self"sv) + s(invoke ? ":"sv : "."sv) + toString(sfName->name)); + break; + } + case "self"_id: + out.push_back(s("self"sv)); + break; + } + } + + void transformColonChainClosure(ChainValue_t* chainValue, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(chainValue)); + pushScope(); + transformColonChain(chainValue, temp, ExpUsage::Return); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformColonChain(ChainValue_t* chainValue, str_list& out, ExpUsage usage = ExpUsage::Common, ExpList_t* expList = nullptr) { + auto x = chainValue; + const auto& chainList = chainValue->items.objects(); + auto baseChain = x->new_ptr(); + switch (chainList.front()->getId()) { + case "DotChainItem"_id: + case "ColonChainItem"_id: + if (_withVars.empty()) { + throw std::logic_error(debugInfo("Short dot/colon syntax must be called within a with block."sv, chainList.front())); + } else { + baseChain->items.push_back(toAst(_withVars.top(), Callable, x)); + } + break; + } + auto end = --chainList.end(); + for (auto it = chainList.begin(); it != end; ++it) { + baseChain->items.push_back(*it); + } + auto colonChainItem = static_cast(chainList.back()); + auto funcName = toString(colonChainItem->name); + if (usage != ExpUsage::Return) pushScope(); + auto baseVar = getUnusedName("_base_"sv); + auto fnVar = getUnusedName("_fn_"sv); + str_list temp; + { + auto value = x->new_ptr(); + value->item.set(baseChain); + auto exp = x->new_ptr(); + exp->value.set(value); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(baseVar, ExpList, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + { + auto assign = x->new_ptr(); + assign->values.push_back(toAst(baseVar + "." + funcName, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(fnVar, ExpList, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + auto funLit = toAst(s("(...)-> "sv) + fnVar + s(" "sv) + baseVar + s(", ..."sv), Exp, x); + switch (usage) { + case ExpUsage::Return: + transformExp(funLit, temp); + _buf << temp.front(); + _buf << *(++temp.begin()); + _buf << indent() << "return " << temp.back() << nll(chainValue); + break; + case ExpUsage::Assignment: { + auto assign = x->new_ptr(); + assign->values.push_back(funLit); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + _buf << indent(-1) << "do"sv << nll(chainValue); + _buf << temp.front(); + _buf << *(++temp.begin()); + _buf << temp.back(); + popScope(); + _buf << indent() << "end"sv << nll(chainValue); + break; + } + case ExpUsage::Common: { + auto assign = x->new_ptr(); + assign->values.push_back(funLit); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst("_"sv, ExpList, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + _buf << indent(-1) << "do"sv << nll(chainValue); + _buf << temp.front(); + _buf << *(++temp.begin()); + _buf << temp.back(); + popScope(); + _buf << indent() << "end"sv << nll(chainValue); + break; + } + default: break; + } + out.push_back(clearBuf()); + } + + void transformChainList(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + auto x = chainList.front(); + str_list temp; + switch (chainList.front()->getId()) { + case "DotChainItem"_id: + case "ColonChainItem"_id: + if (_withVars.empty()) { + throw std::logic_error(debugInfo("Short dot/colon syntax must be called within a with block."sv, chainList.front())); + } else { + temp.push_back(_withVars.top()); + } + break; + } + for (auto it = chainList.begin(); it != chainList.end(); ++it) { + auto item = *it; + switch (item->getId()) { + case "Invoke"_id: + transformInvoke(static_cast(item), temp); + break; + case "DotChainItem"_id: + transformDotChainItem(static_cast(item), temp); + break; + case "ColonChainItem"_id: { + auto colonItem = static_cast(item); + auto next = it; ++next; + auto followItem = next != chainList.end() ? *next : nullptr; + if (!ast_is(followItem)) { + throw std::logic_error(debugInfo("Colon chain item must be followed by invoke arguments."sv, colonItem)); + } + if (colonItem->name.is()) { + auto callVar = getUnusedName(s("_call_"sv)); + auto block = x->new_ptr(); + { + auto chainValue = x->new_ptr(); + switch (chainList.front()->getId()) { + case "DotChainItem"_id: + case "ColonChainItem"_id: + chainValue->items.push_back(toAst(_withVars.top(), Callable, x)); + break; + } + for (auto i = chainList.begin(); i != it; ++i) { + chainValue->items.push_back(*i); + } + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(callVar, ExpList, x)); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + auto stmt = x->new_ptr(); + stmt->content.set(assignment); + block->statements.push_back(stmt); + } + { + auto name = toString(colonItem->name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(toAst(callVar, Callable, x)); + chainValue->items.push_back(toAst(s("\""sv) + name + s("\""sv), Exp, x)); + if (auto invoke = ast_cast(followItem)) { + invoke->args.push_front(toAst(callVar, Exp, x)); + } else { + auto invokeArgs = static_cast(followItem); + invokeArgs->args.push_front(toAst(callVar, Exp, x)); + } + chainValue->items.push_back(followItem); + for (auto i = ++next; i != chainList.end(); ++i) { + chainValue->items.push_back(*i); + } + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto stmt = x->new_ptr(); + stmt->content.set(expListAssign); + block->statements.push_back(stmt); + } + switch (usage) { + case ExpUsage::Common: + transformBlock(block, out, false); + return; + case ExpUsage::Return: + transformBlock(block, out, true); + return; + case ExpUsage::Assignment: { + auto body = x->new_ptr(); + body->content.set(block); + assignLastExplist(assignList, body); + transformBlock(block, out); + return; + } + default: + break; + } + auto body = x->new_ptr(); + body->content.set(block); + auto funLit = toAst("->"sv, FunLit, x); + funLit->body.set(body); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(funLit); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto paren = x->new_ptr(); + paren->expr.set(exp); + auto callable = x->new_ptr(); + callable->item.set(paren); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto invoke = x->new_ptr(); + chainValue->items.push_back(invoke); + transformChainValue(chainValue, out); + return; + } + transformColonChainItem(colonItem, temp); + break; + } + case "Slice"_id: + transformSlice(static_cast(item), temp); + break; + case "Callable"_id: { + auto next = it; ++next; + auto followItem = next != chainList.end() ? *next : nullptr; + transformCallable(static_cast(item), temp, + followItem && ast_is(followItem)); + break; + } + case "String"_id: + transformString(static_cast(item), temp); + temp.back() = s("("sv) + temp.back() + s(")"sv); + break; + case "Exp"_id: + transformExp(static_cast(item), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + case "InvokeArgs"_id: transformInvokeArgs(static_cast(item), temp); break; + default: break; + } + } + switch (usage) { + case ExpUsage::Common: + out.push_back(indent() + join(temp) + nll(chainList.front())); + break; + case ExpUsage::Return: + out.push_back(indent() + s("return "sv) + join(temp) + nll(chainList.front())); + break; + default: + out.push_back(join(temp)); + break; + } + } + + void transformChainValue(ChainValue_t* chainValue, str_list& out, ExpUsage usage = ExpUsage::Closure, ExpList_t* assignList = nullptr) { + transformChainList(chainValue->items.objects(), out, usage, assignList); + } + + void transformAssignableChain(AssignableChain_t* chain, str_list& out) { + transformChainList(chain->items.objects(), out, ExpUsage::Closure); + } + + void transformDotChainItem(DotChainItem_t* dotChainItem, str_list& out) { + auto name = toString(dotChainItem->name); + if (State::keywords.find(name) != State::keywords.end()) { + out.push_back(s("[\""sv) + name + s("\"]"sv)); + } else { + out.push_back(s("."sv) + name); + } + } + + void transformColonChainItem(ColonChainItem_t* colonChainItem, str_list& out) { + auto name = toString(colonChainItem->name); + out.push_back(s(colonChainItem->switchToDot ? "."sv : ":"sv) + name); + } + + void transformSlice(Slice_t* slice, str_list& out) { + throw std::logic_error(debugInfo("Slice syntax not supported here."sv, slice)); + } + + void transformInvoke(Invoke_t* invoke, str_list& out) { + str_list temp; + for (auto arg : invoke->args.objects()) { + switch (arg->getId()) { + case "Exp"_id: transformExp(static_cast(arg), temp); break; + case "SingleString"_id: transformSingleString(static_cast(arg), temp); break; + case "DoubleString"_id: transformDoubleString(static_cast(arg), temp); break; + case "LuaString"_id: transformLuaString(static_cast(arg), temp); break; + default: break; + } + } + out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); + } + + void transform_unary_exp(unary_exp_t* unary_exp, str_list& out) { + std::string op = toString(unary_exp->m_begin.m_it, unary_exp->item->m_begin.m_it); + str_list temp{op + (op == "not"sv ? s(" "sv) : Empty)}; + transformExp(unary_exp->item, temp); + out.push_back(join(temp)); + } + + void transformVariable(Variable_t* name, str_list& out) { + out.push_back(toString(name)); + } + + void transformNum(Num_t* num, str_list& out) { + out.push_back(toString(num)); + } + + void transformTableLit(TableLit_t* table, str_list& out) { + transformTable(table, table->values.objects(), out); + } + + void transformCompCommon(Comprehension_t* comp, str_list& out) { + str_list temp; + auto x = comp; + auto compInner = comp->forLoop.get(); + for (auto item : compInner->items.objects()) { + switch (item->getId()) { + case "CompForEach"_id: + transformCompForEach(static_cast(item), temp); + break; + case "CompFor"_id: + transformCompFor(static_cast(item), temp); + break; + case "Exp"_id: + transformExp(static_cast(item), temp); + temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); + pushScope(); + break; + } + } + if (auto stmt = comp->value.as()) { + transformStatement(stmt, temp); + } else if (auto exp = comp->value.as()) { + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto statement = x->new_ptr(); + statement->content.set(expListAssign); + transformStatement(statement, temp); + } + auto value = temp.back(); + temp.pop_back(); + _buf << join(temp) << value; + for (size_t i = 0; i < compInner->items.objects().size(); ++i) { + popScope(); + _buf << indent() << "end"sv << nll(comp); + } + out.push_back(clearBuf()); + } + + void transformComprehension(Comprehension_t* comp, str_list& out) { + str_list temp; + std::string accum = getUnusedName("_accum_"); + std::string len = getUnusedName("_len_"); + addToScope(accum); + addToScope(len); + auto compInner = comp->forLoop.get(); + for (auto item : compInner->items.objects()) { + switch (item->getId()) { + case "CompForEach"_id: + transformCompForEach(static_cast(item), temp); + break; + case "CompFor"_id: + transformCompFor(static_cast(item), temp); + break; + case "Exp"_id: + transformExp(static_cast(item), temp); + temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); + pushScope(); + break; + } + } + transformExp(comp->value.to(), temp); + auto value = temp.back(); + temp.pop_back(); + for (size_t i = 0; i < compInner->items.objects().size(); ++i) { + popScope(); + } + _buf << indent() << "local "sv << accum << " = { }"sv << nll(comp); + _buf << indent() << "local "sv << len << " = 1"sv << nll(comp); + _buf << join(temp); + _buf << indent(int(temp.size())) << accum << "["sv << len << "] = "sv << value << nll(comp); + _buf << indent(int(temp.size())) << len << " = "sv << len << " + 1"sv << nll(comp); + for (int ind = int(temp.size()) - 1; ind > -1 ; --ind) { + _buf << indent(ind) << "end"sv << nll(comp); + } + out.push_back(accum); + out.push_back(clearBuf()); + } + + void transformCompInPlace(Comprehension_t* comp, ExpList_t* expList, str_list& out) { + auto x = comp; + str_list temp; + auto ind = indent(); + pushScope(); + transformComprehension(comp, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(temp.front(), Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + out.push_back( + ind + s("do"sv) + nll(comp) + + *(++temp.begin()) + + temp.back()); + popScope(); + out.back() = out.back() + indent() + s("end"sv) + nlr(comp); + } + + void transformCompReturn(Comprehension_t* comp, str_list& out) { + str_list temp; + transformComprehension(comp, temp); + out.push_back(temp.back() + indent() + s("return "sv) + temp.front() + nlr(comp)); + } + + void transformCompClosure(Comprehension_t* comp, str_list& out) { + str_list temp; + std::string before = s("(function()"sv) + nll(comp); + pushScope(); + transformComprehension(comp, temp); + out.push_back( + before + + temp.back() + + indent() + s("return "sv) + temp.front() + nlr(comp)); + popScope(); + out.back() = out.back() + indent() + s("end)()"sv); + } + + void transformForEachHead(AssignableNameList_t* nameList, ast_node* loopTarget, str_list& out) { + auto x = nameList; + str_list temp; + str_list vars; + str_list varBefore, varAfter; + std::list>> destructPairs; + for (auto _item : nameList->items.objects()) { + auto item = static_cast(_item)->item.get(); + switch (item->getId()) { + case "Variable"_id: + transformVariable(static_cast(item), vars); + varAfter.push_back(vars.back()); + break; + case "TableLit"_id: { + auto desVar = getUnusedName("_des_"sv); + destructPairs.emplace_back(item, toAst(desVar, Exp, x)); + vars.push_back(desVar); + varAfter.push_back(desVar); + break; + } + default: break; + } + } + switch (loopTarget->getId()) { + case "star_exp"_id: { + auto star_exp = static_cast(loopTarget); + auto listVar = singleVariableFrom(star_exp->value); + auto indexVar = getUnusedName("_index_"); + varAfter.push_back(indexVar); + auto value = singleValueFrom(star_exp->value); + if (!value) throw std::logic_error(debugInfo("Invalid star syntax."sv, star_exp)); + bool endWithSlice = false; + BLOCK_START + auto chainValue = value->item.as(); + BREAK_IF(!chainValue); + auto chainList = chainValue->items.objects(); + auto slice = ast_cast(chainList.back()); + BREAK_IF(!slice); + endWithSlice = true; + if (listVar.empty() && chainList.size() == 2 && + ast_is(chainList.front())) { + transformCallable(static_cast(chainList.front()), temp, false); + listVar = temp.back(); + temp.pop_back(); + } + chainList.pop_back(); + auto chain = x->new_ptr(); + for (auto item : chainList) { + chain->items.push_back(item); + } + std::string startValue("1"sv); + if (auto exp = slice->startValue.as()) { + transformExp(exp, temp); + startValue = temp.back(); + temp.pop_back(); + } + std::string stopValue; + if (auto exp = slice->stopValue.as()) { + transformExp(exp, temp); + stopValue = temp.back(); + temp.pop_back(); + } + std::string stepValue; + if (auto exp = slice->stepValue.as()) { + transformExp(exp, temp); + stepValue = temp.back(); + temp.pop_back(); + } + if (listVar.empty()) { + listVar = getUnusedName("_list_"); + varBefore.push_back(listVar); + transformChainValue(chain, temp); + _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); + } + std::string maxVar; + if (!stopValue.empty()) { + maxVar = getUnusedName("_max_"); + varBefore.push_back(maxVar); + _buf << indent() << "local "sv << maxVar << " = "sv << stopValue << nll(nameList); + } + _buf << indent() << "for "sv << indexVar << " = "sv; + _buf << startValue << ", "sv; + if (stopValue.empty()) { + _buf << "#"sv << listVar; + } else { + _buf << maxVar << " < 0 and #"sv << listVar <<" + " << maxVar << " or "sv << maxVar; + } + if (!stepValue.empty()) { + _buf << ", "sv << stepValue; + } + _buf << " do"sv << nlr(loopTarget); + _buf << indent(1) << "local "sv << join(vars, ", "sv) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); + out.push_back(clearBuf()); + BLOCK_END + bool newListVal = false; + if (listVar.empty()) { + newListVal = true; + listVar = getUnusedName("_list_"); + varBefore.push_back(listVar); + } + if (!endWithSlice) { + transformExp(star_exp->value, temp); + if (newListVal) _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); + _buf << indent() << "for "sv << indexVar << " = 1, #"sv << listVar << " do"sv << nlr(loopTarget); + _buf << indent(1) << "local "sv << join(vars) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); + out.push_back(clearBuf()); + } + break; + } + case "Exp"_id: + transformExp(static_cast(loopTarget), temp); + _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); + out.push_back(clearBuf()); + break; + case "ExpList"_id: + transformExpList(static_cast(loopTarget), temp); + _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); + out.push_back(clearBuf()); + break; + default: break; + } + for (auto& var : varBefore) addToScope(var); + pushScope(); + for (auto& var : varAfter) addToScope(var); + if (!destructPairs.empty()) { + temp.clear(); + for (auto& pair : destructPairs) { + auto sValue = x->new_ptr(); + sValue->value.set(pair.first); + auto value = x->new_ptr(); + value->item.set(sValue); + auto exp = x->new_ptr(); + exp->value.set(value); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assign = x->new_ptr(); + assign->values.push_back(pair.second); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + out.back().append(join(temp)); + } + } + + void transformCompForEach(CompForEach_t* comp, str_list& out) { + transformForEachHead(comp->nameList, comp->loopValue, out); + } + + void transformInvokeArgs(InvokeArgs_t* invokeArgs, str_list& out) { + str_list temp; + for (auto arg : invokeArgs->args.objects()) { + switch (arg->getId()) { + case "Exp"_id: transformExp(static_cast(arg), temp); break; + case "TableBlock"_id: transformTableBlock(static_cast(arg), temp); break; + default: break; + } + } + out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); + } + + void transformForHead(For_t* forNode, str_list& out) { + str_list temp; + std::string varName = toString(forNode->varName); + transformExp(forNode->startValue, temp); + transformExp(forNode->stopValue, temp); + if (forNode->stepValue) { + transformExp(forNode->stepValue->value, temp); + } else { + temp.emplace_back(); + } + auto it = temp.begin(); + const auto& start = *it; + const auto& stop = *(++it); + const auto& step = *(++it); + _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(forNode); + pushScope(); + addToScope(varName); + out.push_back(clearBuf()); + } + + void transformLoopBody(Body_t* body, str_list& out, const std::string& appendContent) { + str_list temp; + bool withContinue = traversal::Stop == body->traverse([&](ast_node* node) { + switch (node->getId()) { + case "For"_id: + case "ForEach"_id: + return traversal::Return; + case "BreakLoop"_id: { + return toString(node) == "continue"sv ? + traversal::Stop : traversal::Return; + } + default: + return traversal::Continue; + } + }); + if (withContinue) { + auto continueVar = getUnusedName("_continue_"sv); + addToScope(continueVar); + _buf << indent() << "local "sv << continueVar << " = false"sv << nll(body); + _buf << indent() << "repeat"sv << nll(body); + temp.push_back(clearBuf()); + _continueVars.push(continueVar); + pushScope(); + } + transformBody(body, temp); + if (withContinue) { + if (!appendContent.empty()) { + _buf << indent() << appendContent; + } + _buf << indent() << _continueVars.top() << " = true"sv << nll(body); + popScope(); + _buf << indent() << "until true"sv << nlr(body); + _buf << indent() << "if not "sv << _continueVars.top() << " then"sv << nlr(body); + _buf << indent(1) << "break"sv << nlr(body); + _buf << indent() << "end"sv << nlr(body); + temp.push_back(clearBuf()); + _continueVars.pop(); + } else if (!appendContent.empty()) { + temp.back().append(indent() + appendContent); + } + out.push_back(join(temp)); + } + + void transformFor(For_t* forNode, str_list& out) { + str_list temp; + transformForHead(forNode, temp); + transformLoopBody(forNode->body, temp, Empty); + popScope(); + out.push_back(join(temp) + indent() + s("end"sv) + nlr(forNode)); + } + + std::string transformForInner(For_t* forNode, str_list& out) { + auto x = forNode; + std::string accum = getUnusedName("_accum_"); + addToScope(accum); + std::string len = getUnusedName("_len_"); + addToScope(len); + _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); + _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); + out.push_back(clearBuf()); + transformForHead(forNode, out); + auto expList = toAst(accum + s("["sv) + len + s("]"sv), ExpList, x); + assignLastExplist(expList, forNode->body); + auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forNode->body); + transformLoopBody(forNode->body, out, lenLine); + popScope(); + out.push_back(indent() + s("end"sv) + nlr(forNode)); + return accum; + } + + void transformForClosure(For_t* forNode, str_list& out) { + str_list temp; + _buf << "(function()"sv << nll(forNode); + pushScope(); + auto accum = transformForInner(forNode, temp); + temp.push_back(indent() + s("return "sv) + accum + nlr(forNode)); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformForInPlace(For_t* forNode, str_list& out, ExpList_t* assignExpList = nullptr) { + auto x = forNode; + str_list temp; + if (assignExpList) { + _buf << indent() << "do"sv << nll(forNode); + pushScope(); + auto accum = transformForInner(forNode, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accum, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(assignExpList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(forNode)); + } else { + auto accum = transformForInner(forNode, temp); + auto returnNode = x->new_ptr(); + auto expListLow = toAst(accum, ExpListLow, x); + returnNode->valueList.set(expListLow); + transformReturn(returnNode, temp); + } + out.push_back(join(temp)); + } + + void transformBinaryOperator(BinaryOperator_t* node, str_list& out) { + auto op = toString(node); + out.push_back(op == "!="sv ? s("~="sv) : op); + } + + void transformForEach(ForEach_t* forEach, str_list& out) { + str_list temp; + transformForEachHead(forEach->nameList, forEach->loopValue, temp); + transformLoopBody(forEach->body, temp, Empty); + popScope(); + out.push_back(temp.front() + temp.back() + indent() + s("end"sv) + nlr(forEach)); + } + + std::string transformForEachInner(ForEach_t* forEach, str_list& out) { + auto x = forEach; + std::string accum = getUnusedName("_accum_"); + addToScope(accum); + std::string len = getUnusedName("_len_"); + addToScope(len); + _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); + _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); + out.push_back(clearBuf()); + transformForEachHead(forEach->nameList, forEach->loopValue, out); + auto expList = toAst(accum + s("["sv) + len + s("]"sv), ExpList, x); + assignLastExplist(expList, forEach->body); + auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forEach->body); + transformLoopBody(forEach->body, out, lenLine); + popScope(); + out.push_back(indent() + s("end"sv) + nlr(forEach)); + return accum; + } + + void transformForEachClosure(ForEach_t* forEach, str_list& out) { + str_list temp; + _buf << "(function()"sv << nll(forEach); + pushScope(); + auto accum = transformForEachInner(forEach, temp); + temp.push_back(indent() + s("return "sv) + accum + nlr(forEach)); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformForEachInPlace(ForEach_t* forEach, str_list& out, ExpList_t* assignExpList = nullptr) { + auto x = forEach; + str_list temp; + if (assignExpList) { + _buf << indent() << "do"sv << nll(forEach); + pushScope(); + auto accum = transformForEachInner(forEach, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accum, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(assignExpList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(forEach)); + } else { + auto accum = transformForEachInner(forEach, temp); + auto returnNode = x->new_ptr(); + auto expListLow = toAst(accum, ExpListLow, x); + returnNode->valueList.set(expListLow); + transformReturn(returnNode, temp); + } + out.push_back(join(temp)); + } + + void transform_variable_pair(variable_pair_t* pair, str_list& out) { + auto name = toString(pair->name); + out.push_back(name + s(" = "sv) + name); + } + + void transform_normal_pair(normal_pair_t* pair, str_list& out) { + auto key = pair->key.get(); + str_list temp; + switch (key->getId()) { + case "KeyName"_id: { + transformKeyName(static_cast(key), temp); + if (State::luaKeywords.find(temp.back()) != State::luaKeywords.end()) { + temp.back() = s("[\""sv) + temp.back() + s("\"]"); + } + break; + } + case "Exp"_id: + transformExp(static_cast(key), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + case "DoubleString"_id: + transformDoubleString(static_cast(key), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + case "SingleString"_id: transformSingleString(static_cast(key), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + default: break; + } + auto value = pair->value.get(); + switch (value->getId()) { + case "Exp"_id: transformExp(static_cast(value), temp); break; + case "TableBlock"_id: transformTableBlock(static_cast(value), temp); break; + default: break; + } + out.push_back(temp.front() + s(" = "sv) + temp.back()); + } + + void transformKeyName(KeyName_t* keyName, str_list& out) { + auto name = keyName->name.get(); + switch (name->getId()) { + case "SelfName"_id: transformSelfName(static_cast(name), out, false); break; + case "Name"_id: out.push_back(toString(name)); break; + default: break; + } + } + + void replace(std::string& str, std::string_view from, std::string_view to) { + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.size(), to); + start_pos += to.size(); + } + } + + void transformLuaString(LuaString_t* luaString, str_list& out) { + auto content = toString(luaString->content); + replace(content, "\r"sv, ""); + if (content[0] == '\n') content.erase(content.begin()); + out.push_back(toString(luaString->open) + content + toString(luaString->close)); + } + + void transformSingleString(SingleString_t* singleString, str_list& out) { + auto str = toString(singleString); + replace(str, "\r"sv, ""); + replace(str, "\n"sv, "\\n"sv); + out.push_back(str); + } + + void transformDoubleString(DoubleString_t* doubleString, str_list& out) { + str_list temp; + for (auto _seg : doubleString->segments.objects()) { + auto seg = static_cast(_seg); + auto content = seg->content.get(); + switch (content->getId()) { + case "double_string_inner"_id: { + auto str = toString(content); + replace(str, "\r"sv, ""); + replace(str, "\n"sv, "\\n"sv); + temp.push_back(s("\""sv) + str + s("\""sv)); + break; + } + case "Exp"_id: + transformExp(static_cast(content), temp); + temp.back() = s("tostring("sv) + temp.back() + s(")"sv); + break; + default: break; + } + } + out.push_back(temp.empty() ? s("\"\""sv) : join(temp, " .. "sv)); + } + + void transformString(String_t* string, str_list& out) { + auto str = string->str.get(); + switch (str->getId()) { + case "SingleString"_id: transformSingleString(static_cast(str), out); break; + case "DoubleString"_id: transformDoubleString(static_cast(str), out); break; + case "LuaString"_id: transformLuaString(static_cast(str), out); break; + default: break; + } + } + + std::pair defineClassVariable(Assignable_t* assignable) { + if (auto variable = assignable->item.as()) { + auto name = toString(variable); + if (addToScope(name)) { + return {name, true}; + } else { + return {name, false}; + } + } + return {Empty, false}; + } + + void transformClassDeclClosure(ClassDecl_t* classDecl, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(classDecl)); + pushScope(); + transformClassDecl(classDecl, temp, ExpUsage::Return); + popScope(); + temp.push_back(s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformClassDecl(ClassDecl_t* classDecl, str_list& out, ExpUsage usage = ExpUsage::Common, ExpList_t* expList = nullptr) { + str_list temp; + auto x = classDecl; + auto body = classDecl->body.get(); + auto assignable = classDecl->name.get(); + auto extend = classDecl->extend.get(); + std::string className; + std::string assignItem; + if (assignable) { + if (!isAssignable(assignable)) { + throw std::logic_error(debugInfo("Left hand expression is not assignable."sv, assignable)); + } + bool newDefined = false; + std::tie(className, newDefined) = defineClassVariable(assignable); + if (newDefined) { + temp.push_back(indent() + s("local "sv) + className + nll(classDecl)); + } + if (className.empty()) { + if (auto chain = ast_cast(assignable->item)) { + if (auto dotChain = ast_cast(chain->items.back())) { + className = s("\""sv) + toString(dotChain->name) + s("\""sv); + } else if (auto index = ast_cast(chain->items.back())) { + if (auto name = index->getByPath()) { + transformString(name, temp); + className = temp.back(); + temp.pop_back(); + } + } + } + } else { + className = s("\""sv) + className + s("\""sv); + } + pushScope(); + transformAssignable(assignable, temp); + popScope(); + assignItem = temp.back(); + temp.pop_back(); + } else if (expList) { + auto name = singleVariableFrom(expList); + if (!name.empty()) { + className = s("\""sv) + name + s("\""sv); + } + } + temp.push_back(indent() + s("do"sv) + nll(classDecl)); + pushScope(); + auto classVar = getUnusedName("_class_"sv); + addToScope(classVar); + temp.push_back(indent() + s("local "sv) + classVar + nll(classDecl)); + if (body) { + str_list varDefs; + for (auto item : body->contents.objects()) { + if (auto statement = ast_cast(item)) { + ClassDecl_t* clsDecl = nullptr; + if (auto assignment = assignmentFrom(statement)) { + auto names = transformAssignDefs(assignment->expList.get()); + varDefs.insert(varDefs.end(), names.begin(), names.end()); + auto info = extractDestructureInfo(assignment); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable && addToScope(item.name)) + varDefs.push_back(item.name); + } + BLOCK_START + auto assign = assignment->action.as(); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto exp = ast_cast(assign->values.objects().front()); + BREAK_IF(!exp); + auto value = singleValueFrom(exp); + clsDecl = value->getByPath(); + BLOCK_END + } else if (auto expList = expListFrom(statement)) { + auto value = singleValueFrom(expList); + clsDecl = value->getByPath(); + } + if (clsDecl) { + std::string clsName; + bool newDefined = false; + std::tie(clsName,newDefined) = defineClassVariable(clsDecl->name); + if (newDefined) varDefs.push_back(clsName); + } + } + } + if (!varDefs.empty()) { + temp.push_back(indent() + s("local ") + join(varDefs, ", "sv) + nll(body)); + } + } + std::string parent, parentVar; + if (extend) { + parentVar = getUnusedName("_parent_"sv); + addToScope(parentVar); + transformExp(extend, temp); + parent = temp.back(); + temp.pop_back(); + temp.push_back(indent() + s("local "sv) + parentVar + s(" = "sv) + parent + nll(classDecl)); + } + auto baseVar = getUnusedName("_base_"sv); + addToScope(baseVar); + temp.push_back(indent() + s("local "sv) + baseVar + s(" = "sv)); + str_list builtins; + str_list commons; + str_list statements; + if (body) { + std::list members; + for (auto content : classDecl->body->contents.objects()) { + switch (content->getId()) { + case "class_member_list"_id: { + size_t inc = transform_class_member_list(static_cast(content), members, classVar); + auto it = members.end(); + for (size_t i = 0; i < inc; ++i, --it); + for (; it != members.end(); ++it) { + auto& member = *it; + if (member.type == MemType::Property) { + statements.push_back(indent() + member.item + nll(content)); + } else { + member.item = indent(1) + member.item; + } + } + break; + } + case "Statement"_id: + transformStatement(static_cast(content), statements); + break; + default: break; + } + } + for (auto& member : members) { + switch (member.type) { + case MemType::Common: + commons.push_back((commons.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); + break; + case MemType::Builtin: + builtins.push_back((builtins.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); + break; + default: break; + } + } + if (!commons.empty()) { + temp.back() += s("{"sv) + nll(body); + temp.push_back(join(commons) + nll(body)); + temp.push_back(indent() + s("}"sv) + nll(body)); + } else { + temp.back() += s("{ }"sv) + nll(body); + } + } else { + temp.back() += s("{ }"sv) + nll(classDecl); + } + temp.push_back(indent() + baseVar + s(".__index = "sv) + baseVar + nll(classDecl)); + str_list tmp; + if (usage == ExpUsage::Assignment) { + auto assign = x->new_ptr(); + assign->values.push_back(toAst(classVar, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, tmp); + } + if (extend) { + _buf << indent() << "setmetatable("sv << baseVar << ", "sv << parentVar << ".__base)"sv << nll(classDecl); + } + _buf << indent() << classVar << " = setmetatable({" << nll(classDecl); + if (!builtins.empty()) { + _buf << join(builtins) << ","sv << nll(classDecl); + } else { + if (extend) { + _buf << indent(1) << "__init = function(self, ...)"sv << nll(classDecl); + _buf << indent(2) << "return _class_0.__parent.__init(self, ...)"sv << nll(classDecl); + _buf << indent(1) << "end,"sv << nll(classDecl); + } else { + _buf << indent(1) << "__init = function() end,"sv << nll(classDecl); + } + } + _buf << indent(1) << "__base = "sv << baseVar; + if (!className.empty()) { + _buf << ","sv << nll(classDecl) << indent(1) << "__name = "sv << className << (extend ? s(","sv) : Empty) << nll(classDecl); + } else { + _buf << nll(classDecl); + } + if (extend) { + _buf << indent(1) << "__parent = "sv << parentVar << nll(classDecl); + } + _buf << indent() << "}, {"sv << nll(classDecl); + if (extend) { + _buf << indent(1) << "__index = function(cls, name)"sv << nll(classDecl); + _buf << indent(2) << "local val = rawget("sv << baseVar << ", name)"sv << nll(classDecl); + _buf << indent(2) << "if val == nil then"sv << nll(classDecl); + _buf << indent(3) << "local parent = rawget(cls, \"__parent\")"sv << nll(classDecl); + _buf << indent(3) << "if parent then"sv << nll(classDecl); + _buf << indent(4) << "return parent[name]"sv << nll(classDecl); + _buf << indent(3) << "end"sv << nll(classDecl); + _buf << indent(2) << "else"sv << nll(classDecl); + _buf << indent(3) << "return val"sv << nll(classDecl); + _buf << indent(2) << "end"sv << nll(classDecl); + _buf << indent(1) << "end,"sv << nll(classDecl); + } else { + _buf << indent(1) << "__index = "sv << baseVar << ","sv << nll(classDecl); + } + _buf << indent(1) << "__call = function(cls, ...)"sv << nll(classDecl); + pushScope(); + auto selfVar = getUnusedName("_self_"sv); + addToScope(selfVar); + _buf << indent(1) << "local " << selfVar << " = setmetatable({}, "sv << baseVar << ")"sv << nll(classDecl); + _buf << indent(1) << "cls.__init("sv << selfVar << ", ...)"sv << nll(classDecl); + _buf << indent(1) << "return "sv << selfVar << nll(classDecl); + popScope(); + _buf << indent(1) << "end"sv << nll(classDecl); + _buf << indent() << "})"sv << nll(classDecl); + _buf << indent() << baseVar << ".__class = "sv << classVar << nll(classDecl); + if (!statements.empty()) _buf << indent() << "local self = "sv << classVar << nll(classDecl); + _buf << join(statements); + if (extend) { + _buf << indent() << "if "sv << parentVar << ".__inherited then"sv << nll(classDecl); + _buf << indent(1) << parentVar << ".__inherited("sv << parentVar << ", "sv << classVar << ")"sv << nll(classDecl); + _buf << indent() << "end"sv << nll(classDecl); + } + if (!assignItem.empty()) { + _buf << indent() << assignItem << " = "sv << classVar << nll(classDecl); + } + switch (usage) { + case ExpUsage::Return: { + _buf << indent() << "return "sv << classVar << nlr(classDecl); + break; + } + case ExpUsage::Assignment: { + _buf << tmp.back(); + break; + } + default: break; + } + temp.push_back(clearBuf()); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(classDecl)); + out.push_back(join(temp)); + } + + size_t transform_class_member_list(class_member_list_t* class_member_list, std::list& out, const std::string& classVar) { + str_list temp; + size_t count = 0; + for (auto keyValue : class_member_list->values.objects()) { + MemType type = MemType::Common; + BLOCK_START + auto normal_pair = ast_cast(keyValue); + BREAK_IF(!normal_pair); + auto keyName = normal_pair->key.as(); + BREAK_IF(!keyName); + std::string newSuperCall; + auto selfName = keyName->name.as(); + if (selfName) { + type = MemType::Property; + auto name = ast_cast(selfName->name); + if (!name) throw std::logic_error(debugInfo("Invalid class poperty name."sv, selfName->name)); + newSuperCall = classVar + s(".__parent."sv) + toString(name->name); + } else { + auto x = keyName; + auto nameNode = keyName->name.as(); + if (!nameNode) break; + auto name = toString(nameNode); + if (name == "new"sv) { + type = MemType::Builtin; + keyName->name.set(toAst("__init"sv, Name, x)); + newSuperCall = classVar + s(".__parent.__init"sv); + } else { + newSuperCall = classVar + s(".__parent.__base."sv) + name; + } + } + normal_pair->value->traverse([&](ast_node* node) { + if (node->getId() == "ClassDecl"_id) return traversal::Return; + if (auto chainValue = ast_cast(node)) { + if (auto callable = ast_cast(chainValue->items.front())) { + auto var = callable->item.get(); + if (toString(var) == "super"sv) { + auto insertSelfToArguments = [&](ast_node* item) { + auto x = item; + switch (item->getId()) { + case "InvokeArgs"_id: { + auto invoke = static_cast(item); + invoke->args.push_front(toAst("self"sv, Exp, x)); + return true; + } + case "Invoke"_id: { + auto invoke = static_cast(item); + invoke->args.push_front(toAst("self"sv, Exp, x)); + return true; + } + default: + return false; + } + }; + const auto& chainList = chainValue->items.objects(); + if (chainList.size() >= 2) { + auto it = chainList.begin(); + auto secondItem = *(++it); + if (!insertSelfToArguments(secondItem)) { + if (auto colonChainItem = ast_cast(secondItem)) { + if (chainList.size() > 2 && insertSelfToArguments(*(++it))) { + colonChainItem->switchToDot = true; + } + } + newSuperCall = classVar + s(".__parent"sv); + } + } else { + newSuperCall = classVar + s(".__parent"sv); + } + auto newChain = toAst(newSuperCall, ChainValue, chainValue); + chainValue->items.pop_front(); + const auto& items = newChain->items.objects(); + for (auto it = items.rbegin(); it != items.rend(); ++it) { + chainValue->items.push_front(*it); + } + } + } + } + return traversal::Continue; + }); + BLOCK_END + pushScope(); + if (type == MemType::Property) { + decIndentOffset(); + } + switch (keyValue->getId()) { + case "variable_pair"_id: + transform_variable_pair(static_cast(keyValue), temp); + break; + case "normal_pair"_id: + transform_normal_pair(static_cast(keyValue), temp); + break; + } + if (type == MemType::Property) { + incIndentOffset(); + } + popScope(); + out.push_back({temp.back(), type, keyValue}); + temp.clear(); + ++count; + } + return count; + } + + void transformAssignable(Assignable_t* assignable, str_list& out) { + auto item = assignable->item.get(); + switch (item->getId()) { + case "AssignableChain"_id: transformAssignableChain(static_cast(item), out); break; + case "Variable"_id: transformVariable(static_cast(item), out); break; + case "SelfName"_id: transformSelfName(static_cast(item), out, false); break; + default: break; + } + } + + void transformWithClosure(With_t* with, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(with)); + pushScope(); + transformWith(with, temp, nullptr, true); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformWith(With_t* with, str_list& out, ExpList_t* assignList = nullptr, bool returnValue = false) { + auto x = with; + str_list temp; + std::string withVar; + bool scoped = false; + if (with->assigns) { + checkAssignable(with->valueList); + auto vars = getAssignVars(with); + if (vars.front().empty()) { + if (with->assigns->values.objects().size() == 1) { + auto var = singleVariableFrom(with->assigns->values.objects().front()); + if (!var.empty()) { + withVar = var; + } + } + if (withVar.empty()) { + withVar = getUnusedName("_with_"); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(withVar, ExpList, x)); + auto assign = x->new_ptr(); + assign->values.push_back(with->assigns->values.objects().front()); + assignment->action.set(assign); + if (!returnValue) { + scoped = true; + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + transformAssignment(assignment, temp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(with->valueList); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(withVar, Exp, x)); + bool skipFirst = true; + for (auto value : with->assigns->values.objects()) { + if (skipFirst) { + skipFirst = false; + continue; + } + assign->values.push_back(value); + } + assignment->action.set(assign); + transformAssignment(assignment, temp); + } else { + withVar = vars.front(); + auto assignment = x->new_ptr(); + assignment->expList.set(with->valueList); + assignment->action.set(with->assigns); + if (!returnValue) { + scoped = true; + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + transformAssignment(assignment, temp); + } + } else { + withVar = singleVariableFrom(with->valueList); + if (withVar.empty()) { + withVar = getUnusedName("_with_"); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(withVar, ExpList, x)); + auto assign = x->new_ptr(); + assign->values.dup(with->valueList->exprs); + assignment->action.set(assign); + if (!returnValue) { + scoped = true; + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + transformAssignment(assignment, temp); + } + } + if (!scoped && !returnValue) { + pushScope(); + scoped = traversal::Stop == with->body->traverse([&](ast_node* node) { + if (auto statement = ast_cast(node)) { + ClassDecl_t* clsDecl = nullptr; + if (auto assignment = assignmentFrom(statement)) { + auto names = getAssignDefs(assignment->expList.get()); + if (!names.empty()) { + return traversal::Stop; + } + auto info = extractDestructureInfo(assignment); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable && !isDefined(item.name)) + return traversal::Stop; + } + BLOCK_START + auto assign = assignment->action.as(); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto exp = ast_cast(assign->values.objects().front()); + BREAK_IF(!exp); + if (auto value = singleValueFrom(exp)) { + clsDecl = value->getByPath(); + } + BLOCK_END + } else if (auto expList = expListFrom(statement)) { + auto value = singleValueFrom(expList); + clsDecl = value->getByPath(); + } + if (clsDecl) { + auto variable = clsDecl->name.as(); + if (!isDefined(toString(variable))) return traversal::Stop; + } + return traversal::Return; + } + return traversal::Continue; + }); + popScope(); + if (scoped) { + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + } + _withVars.push(withVar); + transformBody(with->body, temp); + _withVars.pop(); + if (assignList) { + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(withVar, Exp, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + if (returnValue) { + auto stmt = lastStatementFrom(with->body); + if (!stmt->content.is()) { + temp.push_back(indent() + s("return "sv) + withVar + nll(with)); + } + } + if (scoped) { + popScope(); + temp.push_back(indent() + s("end"sv) + nll(with)); + } + out.push_back(join(temp)); + } + + void transform_const_value(const_value_t* const_value, str_list& out) { + out.push_back(toString(const_value)); + } + + void transformExport(Export_t* exportNode, str_list& out) { + auto x = exportNode; + auto item = exportNode->item.get(); + switch (item->getId()) { + case "ClassDecl"_id: { + auto classDecl = static_cast(item); + if (classDecl->name && classDecl->name->item->getId() == "Variable"_id) { + markVarExported(ExportMode::Any, true); + addExportedVar(toString(classDecl->name->item)); + } + transformClassDecl(classDecl, out); + break; + } + case "export_op"_id: + if (toString(item) == "*"sv) { + markVarExported(ExportMode::Any, false); + } else { + markVarExported(ExportMode::Capital, false); + } + break; + case "export_values"_id: { + markVarExported(ExportMode::Any, true); + auto values = exportNode->item.to(); + if (values->valueList) { + auto expList = x->new_ptr(); + for (auto name : values->nameList->names.objects()) { + addExportedVar(toString(name)); + auto callable = x->new_ptr(); + callable->item.set(name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + expList->exprs.push_back(exp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + auto assign = x->new_ptr(); + assign->values.dup(values->valueList->exprs); + assignment->action.set(assign); + transformAssignment(assignment, out); + } else { + for (auto name : values->nameList->names.objects()) { + addExportedVar(toString(name)); + } + } + break; + } + default: + break; + } + } + + void transformTable(ast_node* table, const node_container& pairs, str_list& out) { + if (pairs.empty()) { + out.push_back(s("{ }"sv)); + return; + } + str_list temp; + pushScope(); + for (auto pair : pairs) { + switch (pair->getId()) { + case "Exp"_id: transformExp(static_cast(pair), temp); break; + case "variable_pair"_id: transform_variable_pair(static_cast(pair), temp); break; + case "normal_pair"_id: transform_normal_pair(static_cast(pair), temp); break; + } + temp.back() = indent() + temp.back() + (pair == pairs.back() ? Empty : s(","sv)) + nll(pair); + } + out.push_back(s("{"sv) + nll(table) + join(temp)); + popScope(); + out.back() += (indent() + s("}"sv)); + } + + void transform_simple_table(simple_table_t* table, str_list& out) { + transformTable(table, table->pairs.objects(), out); + } + + void transformTblComprehension(TblComprehension_t* comp, str_list& out) { + str_list kv; + std::string tbl = getUnusedName("_tbl_"); + addToScope(tbl); + str_list temp; + auto compInner = comp->forLoop.get(); + for (auto item : compInner->items.objects()) { + switch (item->getId()) { + case "CompForEach"_id: + transformCompForEach(static_cast(item), temp); + break; + case "CompFor"_id: + transformCompFor(static_cast(item), temp); + break; + case "Exp"_id: + transformExp(static_cast(item), temp); + temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); + pushScope(); + break; + } + } + transformExp(comp->key, kv); + if (comp->value) { + transformExp(comp->value->value, kv); + } + for (size_t i = 0; i < compInner->items.objects().size(); ++i) { + popScope(); + } + _buf << indent() << "local "sv << tbl << " = { }"sv << nll(comp); + _buf << join(temp); + pushScope(); + if (!comp->value) { + auto keyVar = getUnusedName("_key_"); + auto valVar = getUnusedName("_val_"); + _buf << indent(int(temp.size()) - 1) << "local "sv << keyVar << ", "sv << valVar << " = "sv << kv.front() << nll(comp); + kv.front() = keyVar; + kv.push_back(valVar); + } + _buf << indent(int(temp.size()) - 1) << tbl << "["sv << kv.front() << "] = "sv << kv.back() << nll(comp); + for (int ind = int(temp.size()) - 2; ind > -1 ; --ind) { + _buf << indent(ind) << "end"sv << nll(comp); + } + popScope(); + _buf << indent() << "end"sv << nll(comp); + out.push_back(tbl); + out.push_back(clearBuf()); + } + + void transformTblCompInPlace(TblComprehension_t* comp, ExpList_t* expList, str_list& out) { + auto x = comp; + str_list temp; + auto ind = indent(); + pushScope(); + transformTblComprehension(comp, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(temp.front(), Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + out.push_back( + ind + s("do"sv) + nll(comp) + + *(++temp.begin()) + + temp.back()); + popScope(); + out.back() = out.back() + indent() + s("end"sv) + nlr(comp); + } + + void transformTblCompReturn(TblComprehension_t* comp, str_list& out) { + str_list temp; + transformTblComprehension(comp, temp); + out.push_back(temp.back() + indent() + s("return "sv) + temp.front() + nlr(comp)); + } + + void transformTblCompClosure(TblComprehension_t* comp, str_list& out) { + str_list temp; + std::string before = s("(function()"sv) + nll(comp); + pushScope(); + transformTblComprehension(comp, temp); + const auto& tbVar = temp.front(); + const auto& compBody = temp.back(); + out.push_back( + before + + compBody + + indent() + s("return "sv) + tbVar + nlr(comp)); + popScope(); + out.back() = out.back() + indent() + s("end)()"sv); + } + + void transformCompFor(CompFor_t* comp, str_list& out) { + str_list temp; + std::string varName = toString(comp->varName); + transformExp(comp->startValue, temp); + transformExp(comp->stopValue, temp); + if (comp->stepValue) { + transformExp(comp->stepValue->value, temp); + } else { + temp.emplace_back(); + } + auto it = temp.begin(); + const auto& start = *it; + const auto& stop = *(++it); + const auto& step = *(++it); + _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(comp); + out.push_back(clearBuf()); + pushScope(); + addToScope(varName); + } + + void transformTableBlock(TableBlock_t* table, str_list& out) { + transformTable(table, table->values.objects(), out); + } + + void transformDo(Do_t* doNode, str_list& out, bool implicitReturn = false) { + str_list temp; + temp.push_back(indent() + s("do"sv) + nll(doNode)); + pushScope(); + transformBody(doNode->body, temp, implicitReturn); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(doNode)); + out.push_back(join(temp)); + } + + void transformDoClosure(Do_t* doNode, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(doNode)); + pushScope(); + transformBody(doNode->body, temp, true); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformImport(Import_t* import, str_list& out) { + str_list temp; + auto x = import; + auto objVar = singleVariableFrom(import->exp); + ast_ptr objAssign; + if (objVar.empty()) { + objVar = getUnusedName("_obj_"sv); + auto expList = toAst(objVar, ExpList, x); + auto assign = x->new_ptr(); + assign->values.push_back(import->exp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + objAssign.set(assignment); + } + auto expList = x->new_ptr(); + auto assign = x->new_ptr(); + for (auto name : import->names.objects()) { + switch (name->getId()) { + case "Variable"_id: { + auto var = ast_to(name); + { + auto callable = toAst(objVar, Callable, x); + auto dotChainItem = x->new_ptr(); + dotChainItem->name.set(var->name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + chainValue->items.push_back(dotChainItem); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + assign->values.push_back(exp); + } + auto callable = x->new_ptr(); + callable->item.set(var); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + expList->exprs.push_back(exp); + break; + } + case "colon_import_name"_id: { + auto var = static_cast(name)->name.get(); + { + auto nameNode = var->name.get(); + auto callable = toAst(objVar, Callable, x); + auto colonChain = x->new_ptr(); + colonChain->name.set(nameNode); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + chainValue->items.push_back(colonChain); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + assign->values.push_back(exp); + } + auto callable = x->new_ptr(); + callable->item.set(var); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = x->new_ptr(); + exp->value.set(value); + expList->exprs.push_back(exp); + break; + } + } + } + if (objAssign) { + auto preDef = getPredefine(transformAssignDefs(expList)); + if (!preDef.empty()) { + temp.push_back(preDef + nll(import)); + } + temp.push_back(indent() + s("do"sv) + nll(import)); + pushScope(); + transformAssignment(objAssign, temp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + if (objAssign) { + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(import)); + } + out.push_back(join(temp)); + } + + void transformWhileInPlace(While_t* whileNode, str_list& out, ExpList_t* expList = nullptr) { + auto x = whileNode; + str_list temp; + if (expList) { + temp.push_back(indent() + s("do"sv) + nll(whileNode)); + } + pushScope(); + auto accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + auto lenVar = getUnusedName("_len_"sv); + addToScope(lenVar); + temp.push_back(indent() + s("local "sv) + accumVar + s(" = { }"sv) + nll(whileNode)); + temp.push_back(indent() + s("local "sv) + lenVar + s(" = 1"sv) + nll(whileNode)); + transformExp(whileNode->condition, temp); + temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); + pushScope(); + auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), ExpList, x); + assignLastExplist(assignLeft, whileNode->body); + auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); + transformLoopBody(whileNode->body, temp, lenLine); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(whileNode)); + if (expList) { + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accumVar, Exp, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } else { + temp.push_back(indent() + s("return "sv) + accumVar + nlr(whileNode)); + } + popScope(); + if (expList) { + temp.push_back(indent() + s("end"sv) + nlr(whileNode)); + } + out.push_back(join(temp)); + } + + void transformWhileClosure(While_t* whileNode, str_list& out) { + auto x = whileNode; + str_list temp; + temp.push_back(s("(function() "sv) + nll(whileNode)); + pushScope(); + auto accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + auto lenVar = getUnusedName("_len_"sv); + addToScope(lenVar); + temp.push_back(indent() + s("local "sv) + accumVar + s(" = { }"sv) + nll(whileNode)); + temp.push_back(indent() + s("local "sv) + lenVar + s(" = 1"sv) + nll(whileNode)); + transformExp(whileNode->condition, temp); + temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); + pushScope(); + auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), ExpList, x); + assignLastExplist(assignLeft, whileNode->body); + auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); + transformLoopBody(whileNode->body, temp, lenLine); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(whileNode)); + temp.push_back(indent() + s("return "sv) + accumVar + nlr(whileNode)); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformWhile(While_t* whileNode, str_list& out) { + str_list temp; + pushScope(); + transformExp(whileNode->condition, temp); + transformLoopBody(whileNode->body, temp, Empty); + popScope(); + _buf << indent() << "while "sv << temp.front() << " do"sv << nll(whileNode); + _buf << temp.back(); + _buf << indent() << "end"sv << nlr(whileNode); + out.push_back(clearBuf()); + } + + void transformSwitchClosure(Switch_t* switchNode, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(switchNode)); + pushScope(); + transformSwitch(switchNode, temp, true); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformSwitch(Switch_t* switchNode, str_list& out, bool implicitReturn = false) { + str_list temp; + auto objVar = singleVariableFrom(switchNode->target); + if (objVar.empty()) { + objVar = getUnusedName("_exp_"sv); + addToScope(objVar); + transformExp(switchNode->target, temp); + _buf << indent() << "local "sv << objVar << " = "sv << temp.back() << nll(switchNode); + temp.back() = clearBuf(); + } + const auto& branches = switchNode->branches.objects(); + for (auto branch_ : branches) { + auto branch = static_cast(branch_); + temp.push_back(indent() + s(branches.front() == branch ? "if"sv : "elseif"sv)); + str_list tmp; + const auto& exprs = branch->valueList->exprs.objects(); + for (auto exp_ : exprs) { + auto exp = static_cast(exp_); + transformExp(exp, tmp); + if (!singleValueFrom(exp)) { + tmp.back() = s("("sv) + tmp.back() + s(")"sv); + } + temp.back().append(s(" "sv) + tmp.back() + s(" == "sv) + objVar + + s(exp == exprs.back() ? ""sv : " or"sv)); + } + temp.back().append(s(" then"sv) + nll(branch)); + pushScope(); + transformBody(branch->body, temp, implicitReturn); + popScope(); + } + if (switchNode->lastBranch) { + temp.push_back(indent() + s("else"sv) + nll(switchNode->lastBranch)); + pushScope(); + transformBody(switchNode->lastBranch, temp, implicitReturn); + popScope(); + } + temp.push_back(indent() + s("end"sv) + nlr(switchNode)); + out.push_back(join(temp)); + } + + void transformLocal(Local_t* local, str_list& out) { + if (!local->forceDecls.empty() || !local->decls.empty()) { + str_list defs; + for (const auto& decl : local->forceDecls) { + forceAddToScope(decl); + defs.push_back(decl); + } + for (const auto& decl : local->decls) { + if (addToScope(decl)) { + defs.push_back(decl); + } + } + auto preDefine = getPredefine(defs); + if (!preDefine.empty()) { + out.push_back(preDefine + nll(local)); + } + } + } + + void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { + auto keyword = toString(breakLoop); + if (keyword == "break"sv) { + out.push_back(indent() + keyword + nll(breakLoop)); + return; + } + if (_continueVars.empty()) throw std::logic_error(debugInfo("Continue is not inside a loop."sv, breakLoop)); + _buf << indent() << _continueVars.top() << " = true"sv << nll(breakLoop); + _buf << indent() << "break"sv << nll(breakLoop); + out.push_back(clearBuf()); + } +}; + +const std::string MoonCompliler::Empty; + +std::pair moonCompile(const std::string& codes, bool implicitReturnRoot, bool lineNumber) { + return MoonCompliler{}.complile(codes, false, implicitReturnRoot, lineNumber); +} + +std::pair moonCompile(const std::string& codes, std::list& globals, bool implicitReturnRoot, bool lineNumber) { + auto compiler = MoonCompliler{}; + auto result = compiler.complile(codes, true, implicitReturnRoot, lineNumber); + for (const auto& var : compiler.getGlobals()) { + int line,col; + std::tie(line,col) = var.second; + globals.push_back({var.first, line, col}); + } + return result; +} + +} // namespace MoonP diff --git a/MoonParser/moon_compiler.h b/MoonParser/moon_compiler.h new file mode 100644 index 0000000..e54c774 --- /dev/null +++ b/MoonParser/moon_compiler.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +namespace MoonP { + +std::pair moonCompile(const std::string& codes, bool implicitReturnRoot = true, bool lineNumber = true); + +struct GlobalVar { + std::string name; + int line; + int col; +}; +std::pair moonCompile(const std::string& codes, std::list& globals, bool implicitReturnRoot = true, bool lineNumber = true); + +} // namespace MoonP diff --git a/MoonParser/moon_parser.cpp b/MoonParser/moon_parser.cpp index d709e29..2b93c22 100644 --- a/MoonParser/moon_parser.cpp +++ b/MoonParser/moon_parser.cpp @@ -1,5 +1,7 @@ #include "moon_parser.h" +namespace MoonP { + std::unordered_set State::luaKeywords = { "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if", @@ -14,8 +16,8 @@ std::unordered_set State::keywords = { "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", // Lua keywords - "class", "continue", "export", "extends", - "import", "switch", "when", "unless", "using", + "class", "continue", "export", "extends", "from", + "import", "switch", "unless", "using", "when", "with" // Moon keywords }; @@ -35,10 +37,7 @@ rule Name = (range('a', 'z') | range('A', 'Z') | '_') >> *AlphaNum; rule Num = ( "0x" >> - +(range('0', '9') | range('a', 'f') | range('A', 'F')) >> - -(-set("uU") >> set("lL") >> set("lL")) - ) | ( - +range('0', '9') >> -set("uU") >> set("lL") >> set("lL") + +(range('0', '9') | range('a', 'f') | range('A', 'F')) ) | ( ( (+range('0', '9') >> -('.' >> +range('0', '9'))) | @@ -56,9 +55,17 @@ rule Seperator = true_(); rule Variable = user(Name, [](const item_t& item) { State* st = reinterpret_cast(item.user_data); for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); - auto it = st->keywords.find(st->buffer); + auto it = State::keywords.find(st->buffer); + st->buffer.clear(); + return it == State::keywords.end(); +}); + +rule LuaKeyword = user(Name, [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); + auto it = State::luaKeywords.find(st->buffer); st->buffer.clear(); - return it == st->keywords.end(); + return it != State::luaKeywords.end(); }); rule self = expr('@'); @@ -344,7 +351,7 @@ extern rule Invoke, Slice; rule Index = symx('[') >> Exp >> sym(']'); rule ChainItem = Invoke | DotChainItem | Slice | Index; rule DotChainItem = symx('.') >> Name; -rule ColonChainItem = symx('\\') >> Name; +rule ColonChainItem = symx('\\') >> (LuaKeyword | Name); rule invoke_chain = Invoke >> -ChainItems; rule ColonChain = ColonChainItem >> -invoke_chain; @@ -480,7 +487,7 @@ rule SimpleValue = TblComprehension | TableLit | Comprehension | FunLit | (Space >> Num); -rule Assignment = ExpList >> (Update | Assign); +rule ExpListAssign = ExpList >> -(Update | Assign); rule if_else_line = key("if") >> Exp >> (key("else") >> Exp | default_value); rule unless_line = key("unless") >> Exp; @@ -490,7 +497,7 @@ rule Statement = ( Import | While | For | ForEach | Return | Local | Export | Space >> BreakLoop | - Assignment | ExpList + ExpListAssign ) >> Space >> -statement_appendix; @@ -502,3 +509,5 @@ rule Block = Seperator >> Line >> *(+Break >> Line); rule Shebang = expr("#!") >> *(not_(Stop) >> Any); rule File = White >> -Shebang >> Block >> eof(); + +} // namespace MoonP diff --git a/MoonParser/moon_parser.h b/MoonParser/moon_parser.h index 0c45538..07380cc 100644 --- a/MoonParser/moon_parser.h +++ b/MoonParser/moon_parser.h @@ -6,9 +6,11 @@ #include #include #include -#include "parserlib.hpp" +#include "ast.hpp" using namespace parserlib; +namespace MoonP { + struct State { State() { indents.push(0); @@ -21,3 +23,5 @@ struct State { static std::unordered_set luaKeywords; static std::unordered_set keywords; }; + +} // namespace MoonP diff --git a/MoonParser/moonc.cpp b/MoonParser/moonc.cpp new file mode 100644 index 0000000..1a383fe --- /dev/null +++ b/MoonParser/moonc.cpp @@ -0,0 +1,21 @@ +#include +#include "moon_compiler.h" + +int main() +{ + std::string s = R"TestCodesHere( +print nil + 1 +)TestCodesHere"; + std::list globals; + auto result = MoonP::moonCompile(s, globals, true, false); + if (!result.first.empty()) { + std::cout << result.first << '\n'; + } else { + std::cout << result.second << '\n'; + } + std::cout << "globals:\n"; + for (const auto& var : globals) { + std::cout << var.name << '\n'; + } + return 0; +} diff --git a/MoonParser/parser.hpp b/MoonParser/parser.hpp index a84b9a3..cbf0168 100644 --- a/MoonParser/parser.hpp +++ b/MoonParser/parser.hpp @@ -1,5 +1,4 @@ -#ifndef PARSER_HPP -#define PARSER_HPP +#pragma once //gcc chokes without rule::rule(const rule &), @@ -16,6 +15,8 @@ #include #include +namespace parserlib { + // const str hash helper functions inline constexpr size_t hash(char const* input) { @@ -35,9 +36,6 @@ typedef std::basic_string input; typedef input::iterator input_it; typedef std::wstring_convert> Converter; -namespace parserlib { - - class _private; class _expr; class _context; @@ -66,7 +64,7 @@ public: int m_col; ///null constructor. - pos() {} + pos():m_line(-1),m_col(0) {} /** constructor from input. @param i input. @@ -425,6 +423,3 @@ template T &operator << (T &stream, const input_range &ir) { } //namespace parserlib - - -#endif //PARSER_HPP diff --git a/MoonParser/parserlib.hpp b/MoonParser/parserlib.hpp deleted file mode 100644 index afbd1f0..0000000 --- a/MoonParser/parserlib.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef PARSERLIB_HPP -#define PARSERLIB_HPP - - -#include "ast.hpp" - - -#endif //PARSERLIB_HPP -- cgit v1.2.3-55-g6feb