From 10afeaf30bfe03774c12908235520eebf0c192ee Mon Sep 17 00:00:00 2001 From: Li Jin Date: Mon, 25 Apr 2022 13:19:26 +0800 Subject: add spread syntax support for table block. fix a gcc compiler issue. update doc. --- doc/docs/doc/README.md | 68 ++++++-- spec/inputs/return.yue | 13 ++ spec/inputs/tables.yue | 23 +++ spec/outputs/return.lua | 24 +++ spec/outputs/tables.lua | 61 +++++++ src/yuescript/yue_ast.h | 6 +- src/yuescript/yue_compiler.cpp | 351 +++++++++++++++++++++++++---------------- src/yuescript/yue_parser.cpp | 9 +- src/yuescript/yue_parser.h | 1 + 9 files changed, 407 insertions(+), 149 deletions(-) diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md index adff569..65266cb 100755 --- a/doc/docs/doc/README.md +++ b/doc/docs/doc/README.md @@ -118,14 +118,14 @@ export yuescript = "ζœˆδΉ‹θ„šζœ¬"  Use Yuescript module in Lua: -* **Case 1** +* **Case 1** Require "your_yuescript_entry.yue" in Lua. ```Lua require("yue")("your_yuescript_entry") ```  And this code still works when you compile "your_yuescript_entry.yue" to "your_yuescript_entry.lua" in the same path. In the rest Yuescript files just use the normal **require** or **import**. The code line numbers in error messages will also be handled correctly. -* **Case 2** +* **Case 2** Require Yuescript module and rewite message by hand. ```lua local yue = require("yue") @@ -136,7 +136,7 @@ end, function(err) end) ``` -* **Case 3** +* **Case 3** Use the Yuescript compiler function in Lua. ```lua local yue = require("yue") @@ -174,12 +174,12 @@ Usage: yue [options|files|directories] ... Execute without options to enter REPL, type symbol '$' in a single line to start/stop multi-line mode ``` -  Use cases: -  Recursively compile every Yuescript file with extension **.yue** under current path: **yue .** -  Compile and save results to a target path: **yue -t /target/path/ .** -  Compile and reserve debug info: **yue -l .** -  Compile and generate minified codes: **yue -m .** -  Execute raw codes: **yue -e 'print 123'** +  Use cases: +  Recursively compile every Yuescript file with extension **.yue** under current path: **yue .** +  Compile and save results to a target path: **yue -t /target/path/ .** +  Compile and reserve debug info: **yue -l .** +  Compile and generate minified codes: **yue -m .** +  Execute raw codes: **yue -e 'print 123'**   Execute a Yuescript file: **yue -e main.yue** ## Macro @@ -606,6 +606,45 @@ tb = +### Spread Table + +You can concatenate array tables or hash tables using spread operator `...` before expressions in table literals. + +```moonscript +parts = + * "shoulders" + * "knees" +lyrics = + * "head" + * ...parts + * "and" + * "toes" + +copy = {...other} + +a = {1, 2, 3, x: 1} +b = {4, 5, y: 1} +merge = {...a, ...b} +``` + +
+parts =
+	* "shoulders"
+	* "knees"
+lyrics =
+	* "head"
+	* ...parts
+	* "and"
+	* "toes"
+
+copy = {...other}
+
+a = {1, 2, 3, x: 1}
+a = {4, 5, y: 1}
+merge = {...a, ...b}
+
+
+ ## Module ### Import @@ -981,6 +1020,17 @@ You can write default values while doing destructuring like: +You can use `_` as placeholder when doing a list destructuring: + +```moonscript +{_, two, _, four} = items +``` + +
+{_, two, _, four} = items
+
+
+ ### Destructuring In Other Places Destructuring can also show up in places where an assignment implicitly takes place. An example of this is a for loop: diff --git a/spec/inputs/return.yue b/spec/inputs/return.yue index f170ffd..96fa0cd 100644 --- a/spec/inputs/return.yue +++ b/spec/inputs/return.yue @@ -49,6 +49,19 @@ do else b +do + return + :value + itemA: 123 + itemB: "abc" + +do + return + * 1 + * 2 + * ...three + * 4 + _ = -> a\b do a\b diff --git a/spec/inputs/tables.yue b/spec/inputs/tables.yue index 3a245a7..e1b9e1d 100644 --- a/spec/inputs/tables.yue +++ b/spec/inputs/tables.yue @@ -281,5 +281,28 @@ specializedB = { else: false } +parts = + * "shoulders" + * "knees" + +lyrics = + * "head" + * ...parts + * "and" + * "toes" + +tbBlock = + sub: + :value + * ...items + * ... + +func + * ...items + :value + * ... + k: v + * ... + nil diff --git a/spec/outputs/return.lua b/spec/outputs/return.lua index 0735b23..775ae11 100644 --- a/spec/outputs/return.lua +++ b/spec/outputs/return.lua @@ -86,6 +86,30 @@ do end end end +do + return { + value = value, + itemA = 123, + itemB = "abc" + } +end +do + local _tab_0 = { + 1, + 2 + } + local _idx_0 = 1 + for _key_0, _value_0 in pairs(three) do + if _idx_0 == _key_0 then + _tab_0[#_tab_0 + 1] = _value_0 + _idx_0 = _idx_0 + 1 + else + _tab_0[_key_0] = _value_0 + end + end + _tab_0[#_tab_0 + 1] = 4 + return _tab_0 +end _ = function() local _base_0 = a local _fn_0 = _base_0.b diff --git a/spec/outputs/tables.lua b/spec/outputs/tables.lua index 165706d..1d28a43 100644 --- a/spec/outputs/tables.lua +++ b/spec/outputs/tables.lua @@ -527,4 +527,65 @@ do _tab_0["else"] = false specializedB = _tab_0 end +local parts = { + "shoulders", + "knees" +} +local lyrics +do + local _tab_0 = { + "head" + } + local _idx_0 = 1 + for _key_0, _value_0 in pairs(parts) do + if _idx_0 == _key_0 then + _tab_0[#_tab_0 + 1] = _value_0 + _idx_0 = _idx_0 + 1 + else + _tab_0[_key_0] = _value_0 + end + end + _tab_0[#_tab_0 + 1] = "and" + _tab_0[#_tab_0 + 1] = "toes" + lyrics = _tab_0 +end +local tbBlock = { + sub = (function(...) + local _tab_0 = { + value = value + } + local _idx_0 = 1 + for _key_0, _value_0 in pairs(items) do + if _idx_0 == _key_0 then + _tab_0[#_tab_0 + 1] = _value_0 + _idx_0 = _idx_0 + 1 + else + _tab_0[_key_0] = _value_0 + end + end + for _index_0 = 1, select('#', ...) do + _tab_0[#_tab_0 + 1] = select(_index_0, ...) + end + return _tab_0 + end)(...) +} +func((function(...) + local _tab_0 = { } + local _idx_0 = 1 + for _key_0, _value_0 in pairs(items) do + if _idx_0 == _key_0 then + _tab_0[#_tab_0 + 1] = _value_0 + _idx_0 = _idx_0 + 1 + else + _tab_0[_key_0] = _value_0 + end + end + _tab_0.value = value + _tab_0[#_tab_0 + 1] = ... + _tab_0.k = v + for _index_0 = 1, select('#', ...) do + _tab_0[#_tab_0 + 1] = select(_index_0, ...) + end + return _tab_0 +end)(...)) return nil diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index ed6f7d2..06896b8 100755 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -221,9 +221,11 @@ AST_NODE(ExpList) AST_MEMBER(ExpList, &sep, &exprs) AST_END(ExpList) +class TableBlock_t; + AST_NODE(Return) bool allowBlockMacroReturn = false; - ast_ptr valueList; + ast_sel valueList; AST_MEMBER(Return, &valueList) AST_END(Return) @@ -628,7 +630,7 @@ AST_END(TableBlockIndent) AST_NODE(TableBlock) ast_ptr sep; - ast_sel_list values; AST_MEMBER(TableBlock, &sep, &values) AST_END(TableBlock) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 6a0fba6..476494e 100755 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -60,7 +60,7 @@ using namespace parserlib; typedef std::list str_list; -const std::string_view version = "0.10.14"sv; +const std::string_view version = "0.10.15"sv; const std::string_view extension = "yue"sv; class YueCompilerImpl { @@ -1355,10 +1355,20 @@ private: } case id(): { auto tableLit = static_cast(value); - if (hasSpreadExp(tableLit)) { + if (hasSpreadExp(tableLit->values.objects())) { auto expList = assignment->expList.get(); std::string preDefine = getPredefine(assignment); - transformSpreadTableLit(tableLit, out, ExpUsage::Assignment, expList); + transformSpreadTable(tableLit->values.objects(), out, ExpUsage::Assignment, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + } + case id(): { + auto tableBlock = static_cast(value); + if (hasSpreadExp(tableBlock->values.objects())) { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformSpreadTable(tableBlock->values.objects(), out, ExpUsage::Assignment, expList); out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); return; } @@ -3239,7 +3249,7 @@ private: if (!target) target = returnNode; throw std::logic_error(_info.errorMessage("illegal return statement here"sv, target)); } - if (auto valueList = returnNode->valueList.get()) { + if (auto valueList = returnNode->valueList.as()) { if (valueList->exprs.size() == 1) { auto exp = static_cast(valueList->exprs.back()); if (isPureBackcall(exp)) { @@ -3286,8 +3296,8 @@ private: return; case id(): { auto tableLit = static_cast(value); - if (hasSpreadExp(tableLit)) { - transformSpreadTableLit(tableLit, out, ExpUsage::Return); + if (hasSpreadExp(tableLit->values.objects())) { + transformSpreadTable(tableLit->values.objects(), out, ExpUsage::Return); return; } } @@ -3306,6 +3316,14 @@ private: transformExpListLow(valueList, temp); out.push_back(indent() + "return "s + temp.back() + nlr(returnNode)); } + } else if (auto tableBlock = returnNode->valueList.as()) { + const auto& values = tableBlock->values.objects(); + if (hasSpreadExp(values)) { + transformSpreadTable(values, out, ExpUsage::Return); + } else { + transformTable(values, out); + out.back() = indent() + "return "s + out.back() + nlr(returnNode); + } } else { out.push_back(indent() + "return"s + nll(returnNode)); } @@ -4495,23 +4513,15 @@ private: out.push_back(_parser.toString(num)); } - bool hasSpreadExp(TableLit_t* table) { - for (auto item : table->values.objects()) { + bool hasSpreadExp(const node_container& items) { + for (auto item : items) { if (ast_is(item)) return true; } return false; } - void transformTableLit(TableLit_t* table, str_list& out) { - if (hasSpreadExp(table)) { - transformSpreadTableLit(table, out, ExpUsage::Closure); - } else { - transformTable(table, table->values.objects(), out); - } - } - - void transformSpreadTableLit(TableLit_t* table, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - auto x = table; + void transformSpreadTable(const node_container& values, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + auto x = values.front(); switch (usage) { case ExpUsage::Closure: _enableReturn.push(true); @@ -4527,19 +4537,19 @@ private: str_list temp; std::string tableVar = getUnusedName("_tab_"sv); forceAddToScope(tableVar); - auto it = table->values.objects().begin(); + auto it = values.begin(); if (ast_is(*it)) { temp.push_back(indent() + "local "s + tableVar + " = { }"s + nll(x)); } else { auto initialTab = x->new_ptr(); - while (it != table->values.objects().end() && !ast_is(*it)) { + while (it != values.end() && !ast_is(*it)) { initialTab->values.push_back(*it); ++it; } - transformTable(table, initialTab->values.objects(), temp); + transformTable(initialTab->values.objects(), temp); temp.back() = indent() + "local "s + tableVar + " = "s + temp.back() + nll(*it); } - for (; it != table->values.objects().end(); ++it) { + for (; it != values.end(); ++it) { auto item = *it; switch (item->getId()) { case id(): { @@ -4623,7 +4633,7 @@ private: case id(): { bool lastVarArg = false; BLOCK_START - BREAK_IF(item != table->values.back()); + BREAK_IF(item != values.back()); auto value = singleValueFrom(item); BREAK_IF(!value); auto chainValue = value->item.as(); @@ -4644,6 +4654,66 @@ private: } break; } + case id(): { + auto tbIndent = static_cast(item); + auto tableBlock = item->new_ptr(); + tableBlock->values.dup(tbIndent->values); + auto assignment = toAst(tableVar + "[]=nil"s, item); + auto assign = assignment->action.to(); + assign->values.clear(); + assign->values.push_back(tableBlock); + transformAssignment(assignment, temp); + break; + } + case id(): { + auto assignment = toAst(tableVar + "[]=nil"s, item); + auto assign = assignment->action.to(); + assign->values.clear(); + assign->values.push_back(item); + transformAssignment(assignment, temp); + break; + } + case id(): { + auto metaVarPair = static_cast(item); + auto nameStr = _parser.toString(metaVarPair->name); + auto assignment = toAst(tableVar + '.' + nameStr + "#="s + nameStr, item); + transformAssignment(assignment, temp); + break; + } + case id(): { + auto metaNormalPair = static_cast(item); + auto assignment = toAst(tableVar + "=nil"s, item); + auto chainValue = singleValueFrom(ast_to(assignment->expList->exprs.front()))->item.to(); + auto key = metaNormalPair->key.get(); + switch (key->getId()) { + case id(): { + auto dotItem = x->new_ptr(); + dotItem->name.set(key); + chainValue->items.push_back(dotItem); + break; + } + case id(): { + auto dotItem = toAst(".#"sv, key); + chainValue->items.push_back(dotItem); + chainValue->items.push_back(key); + break; + } + default: YUEE("AST node mismatch", key); break; + } + auto assign = assignment->action.to(); + assign->values.clear(); + assign->values.push_back(metaNormalPair->value); + transformAssignment(assignment, temp); + break; + } + case id(): { + throw std::logic_error(_info.errorMessage("invalid default value"sv, static_cast(item)->defVal)); + break; + } + case id(): { + throw std::logic_error(_info.errorMessage("invalid default value"sv, static_cast(item)->defVal)); + break; + } default: YUEE("AST node mismatch", item); break; } } @@ -4682,6 +4752,123 @@ private: } } + void transformTable(const node_container& values, str_list& out) { + if (values.empty()) { + out.push_back("{ }"s); + return; + } + auto x = values.front(); + str_list temp; + incIndentOffset(); + auto metatable = x->new_ptr(); + ast_sel metatableItem; + for (auto item : values) { + bool isMetamethod = false; + switch (item->getId()) { + case id(): transformExp(static_cast(item), temp, ExpUsage::Closure); break; + case id(): transform_variable_pair(static_cast(item), temp); break; + case id(): transform_normal_pair(static_cast(item), temp, false); break; + case id(): transformTableBlockIndent(static_cast(item), temp); break; + case id(): transformTableBlock(static_cast(item), temp); break; + case id(): { + isMetamethod = true; + auto mp = static_cast(item); + if (metatableItem) { + throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->name)); + } + auto name = _parser.toString(mp->name); + _buf << "__"sv << name << ':' << name; + auto newPair = toAst(clearBuf(), item); + metatable->pairs.push_back(newPair); + break; + } + case id(): { + isMetamethod = true; + auto mp = static_cast(item); + auto newPair = item->new_ptr(); + if (mp->key) { + if (metatableItem) { + throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->key)); + } + switch (mp->key->getId()) { + case id(): { + auto key = _parser.toString(mp->key); + _buf << "__"sv << key; + auto newKey = toAst(clearBuf(), mp->key); + newPair->key.set(newKey); + break; + } + case id(): + newPair->key.set(mp->key); + break; + default: YUEE("AST node mismatch", mp->key); break; + } + newPair->value.set(mp->value); + metatable->pairs.push_back(newPair); + } else { + if (!metatable->pairs.empty()) { + throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->value)); + } + metatableItem.set(mp->value); + } + break; + } + case id(): { + throw std::logic_error(_info.errorMessage("invalid default value"sv, static_cast(item)->defVal)); + break; + } + case id(): { + throw std::logic_error(_info.errorMessage("invalid default value"sv, static_cast(item)->defVal)); + break; + } + default: YUEE("AST node mismatch", item); break; + } + if (!isMetamethod) { + temp.back() = indent() + (item == values.back() ? temp.back() : temp.back() + ',') + nll(item); + } + } + if (metatable->pairs.empty() && !metatableItem) { + out.push_back('{' + nll(x) + join(temp)); + decIndentOffset(); + out.back() += (indent() + '}'); + } else { + auto tabStr = globalVar("setmetatable"sv, x); + tabStr += '('; + if (temp.empty()) { + decIndentOffset(); + tabStr += "{ }"sv; + } else { + tabStr += ('{' + nll(x) + join(temp)); + decIndentOffset(); + tabStr += (indent() + '}'); + } + tabStr += ", "sv; + str_list tmp; + if (!metatable->pairs.empty()) { + transform_simple_table(metatable, tmp); + } else switch (metatableItem->getId()) { + case id(): + transformExp(static_cast(metatableItem.get()), tmp, ExpUsage::Closure); + break; + case id(): + transformTableBlock(static_cast(metatableItem.get()), tmp); + break; + } + tabStr += tmp.back(); + tabStr += ')'; + out.push_back(tabStr); + } + } + + void transformTableLit(TableLit_t* table, str_list& out) { + const auto& values = table->values.objects(); + if (hasSpreadExp(values)) { + transformSpreadTable(values, out, ExpUsage::Closure); + } else { + transformTable(values, out); + } + } + void transformCompCommon(Comprehension_t* comp, str_list& out) { str_list temp; auto x = comp; @@ -6079,115 +6266,8 @@ private: } } - void transformTable(ast_node* table, const node_container& pairs, str_list& out) { - if (pairs.empty()) { - out.push_back("{ }"s); - return; - } - str_list temp; - incIndentOffset(); - auto metatable = table->new_ptr(); - ast_sel metatableItem; - for (auto pair : pairs) { - bool isMetamethod = false; - switch (pair->getId()) { - case id(): transformExp(static_cast(pair), temp, ExpUsage::Closure); break; - case id(): transform_variable_pair(static_cast(pair), temp); break; - case id(): transform_normal_pair(static_cast(pair), temp, false); break; - case id(): transformTableBlockIndent(static_cast(pair), temp); break; - case id(): transformTableBlock(static_cast(pair), temp); break; - case id(): { - isMetamethod = true; - auto mp = static_cast(pair); - if (metatableItem) { - throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->name)); - } - auto name = _parser.toString(mp->name); - _buf << "__"sv << name << ':' << name; - auto newPair = toAst(clearBuf(), pair); - metatable->pairs.push_back(newPair); - break; - } - case id(): { - isMetamethod = true; - auto mp = static_cast(pair); - auto newPair = pair->new_ptr(); - if (mp->key) { - if (metatableItem) { - throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->key)); - } - switch (mp->key->getId()) { - case id(): { - auto key = _parser.toString(mp->key); - _buf << "__"sv << key; - auto newKey = toAst(clearBuf(), mp->key); - newPair->key.set(newKey); - break; - } - case id(): - newPair->key.set(mp->key); - break; - default: YUEE("AST node mismatch", mp->key); break; - } - newPair->value.set(mp->value); - metatable->pairs.push_back(newPair); - } else { - if (!metatable->pairs.empty()) { - throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->value)); - } - metatableItem.set(mp->value); - } - break; - } - case id(): { - throw std::logic_error(_info.errorMessage("invalid use of default value"sv, static_cast(pair)->defVal)); - break; - } - case id(): { - throw std::logic_error(_info.errorMessage("invalid use of default value"sv, static_cast(pair)->defVal)); - break; - } - default: YUEE("AST node mismatch", pair); break; - } - if (!isMetamethod) { - temp.back() = indent() + (pair == pairs.back() ? temp.back() : temp.back() + ',') + nll(pair); - } - } - if (metatable->pairs.empty() && !metatableItem) { - out.push_back('{' + nll(table) + join(temp)); - decIndentOffset(); - out.back() += (indent() + '}'); - } else { - auto tabStr = globalVar("setmetatable"sv, table); - tabStr += '('; - if (temp.empty()) { - decIndentOffset(); - tabStr += "{ }"sv; - } else { - tabStr += ('{' + nll(table) + join(temp)); - decIndentOffset(); - tabStr += (indent() + '}'); - } - tabStr += ", "sv; - str_list tmp; - if (!metatable->pairs.empty()) { - transform_simple_table(metatable, tmp); - } else switch (metatableItem->getId()) { - case id(): - transformExp(static_cast(metatableItem.get()), tmp, ExpUsage::Closure); - break; - case id(): - transformTableBlock(static_cast(metatableItem.get()), tmp); - break; - } - tabStr += tmp.back(); - tabStr += ')'; - out.push_back(tabStr); - } - } - void transform_simple_table(simple_table_t* table, str_list& out) { - transformTable(table, table->pairs.objects(), out); + transformTable(table->pairs.objects(), out); } void transformTblComprehension(TblComprehension_t* comp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { @@ -6284,11 +6364,16 @@ private: } void transformTableBlockIndent(TableBlockIndent_t* table, str_list& out) { - transformTable(table, table->values.objects(), out); + transformTable(table->values.objects(), out); } void transformTableBlock(TableBlock_t* table, str_list& out) { - transformTable(table, table->values.objects(), out); + const auto& values = table->values.objects(); + if (hasSpreadExp(values)) { + transformSpreadTable(values, out, ExpUsage::Closure); + } else { + transformTable(values, out); + } } void transformDo(Do_t* doNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index 0c08d4f..1f1b57c 100755 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -28,9 +28,8 @@ std::unordered_set Keywords = { "or", "repeat", "return", "then", "true", "until", "while", // Lua keywords "as", "class", "continue", "export", "extends", - "from", "global", "import", "is", "macro", - "switch", "try", "unless", "using", "when", - "with" // Yue keywords + "from", "global", "import", "macro", "switch", + "try", "unless", "using", "when", "with" // Yue keywords }; YueParser::YueParser() { @@ -219,7 +218,7 @@ YueParser::YueParser() { BreakLoop = (expr("break") | expr("continue")) >> not_(AlphaNum); - Return = key("return") >> -ExpListLow; + Return = key("return") >> -(TableBlock | ExpListLow); WithExp = ExpList >> -Assign; @@ -556,7 +555,7 @@ YueParser::YueParser() { KeyValue = variable_pair | normal_pair | meta_variable_pair | meta_normal_pair; KeyValueList = KeyValue >> *(sym(',') >> KeyValue); - KeyValueLine = CheckIndent >> (KeyValueList >> -sym(',') | TableBlockIndent | Space >> expr('*') >> (Exp | TableBlock)); + KeyValueLine = CheckIndent >> (KeyValueList >> -sym(',') | TableBlockIndent | Space >> expr('*') >> (SpreadExp | Exp | TableBlock)); FnArgDef = (Variable | SelfName >> -existential_op) >> -(sym('=') >> Space >> Exp); diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index eeadaf6..159bbca 100755 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -15,6 +15,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include "yuescript/ast.hpp" #include "yuescript/yue_ast.h" -- cgit v1.2.3-55-g6feb