From 3c4cbff4d88892f153b70ccbbc69d1087ecbcc49 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 13 Dec 2023 17:32:53 +0800 Subject: add yue.format() function. fix yue ast to code function issues. --- spec/inputs/test/format_spec.yue | 126 ++++++++++++++++++ spec/outputs/test/format_spec.lua | 133 +++++++++++++++++++ src/yue_wasm.cpp | 2 +- src/yuescript/yue_ast.cpp | 260 ++++++++++++++++++++++++++++++-------- src/yuescript/yue_compiler.cpp | 2 +- src/yuescript/yue_parser.cpp | 42 +++--- src/yuescript/yue_parser.h | 3 + src/yuescript/yuescript.cpp | 43 ++++++- 8 files changed, 535 insertions(+), 76 deletions(-) create mode 100644 spec/inputs/test/format_spec.yue create mode 100644 spec/outputs/test/format_spec.lua diff --git a/spec/inputs/test/format_spec.yue b/spec/inputs/test/format_spec.yue new file mode 100644 index 0000000..5076445 --- /dev/null +++ b/spec/inputs/test/format_spec.yue @@ -0,0 +1,126 @@ +files = [ + "spec/inputs/macro_export.yue" + "spec/inputs/attrib.yue" + "spec/inputs/macro.yue" + "spec/inputs/using.yue" + "spec/inputs/whitespace.yue" + "spec/inputs/nil_coalescing.yue" + "spec/inputs/stub.yue" + "spec/inputs/pipe.yue" + "spec/inputs/teal_lang.yue" + "spec/inputs/string.yue" + "spec/inputs/local.yue" + "spec/inputs/tables.yue" + "spec/inputs/operators.yue" + "spec/inputs/lists.yue" + "spec/inputs/compile_doc.yue" + "spec/inputs/switch.yue" + "spec/inputs/multiline_chain.yue" + "spec/inputs/existential.yue" + "spec/inputs/export_default.yue" + "spec/inputs/assign.yue" + "spec/inputs/literals.yue" + "spec/inputs/luarocks_upload.yue" + "spec/inputs/ambiguous.yue" + "spec/inputs/bubbling.yue" + "spec/inputs/try_catch.yue" + "spec/inputs/funcs.yue" + "spec/inputs/do.yue" + "spec/inputs/with.yue" + "spec/inputs/export.yue" + "spec/inputs/macro_todo.yue" + "spec/inputs/backcall.yue" + "spec/inputs/cond.yue" + "spec/inputs/in_expression.yue" + "spec/inputs/comprehension.yue" + "spec/inputs/macro_teal.yue" + "spec/inputs/import.yue" + "spec/inputs/unless_else.yue" + "spec/inputs/destructure.yue" + "spec/inputs/return.yue" + "spec/inputs/loops.yue" + "spec/inputs/class.yue" + "spec/inputs/vararg.yue" + "spec/inputs/goto.yue" + "spec/inputs/metatable.yue" + "spec/inputs/syntax.yue" + "spec/inputs/global.yue" + "spec/inputs/plus.yue" + "spec/inputs/test/class_spec.yue" + "spec/inputs/test/table_spreading_spec.yue" + "spec/inputs/test/loops_spec.yue" + "spec/inputs/test/format_spec.yue" + "spec/inputs/unicode/macro_export.yue" + "spec/inputs/unicode/attrib.yue" + "spec/inputs/unicode/macro.yue" + "spec/inputs/unicode/using.yue" + "spec/inputs/unicode/whitespace.yue" + "spec/inputs/unicode/nil_coalescing.yue" + "spec/inputs/unicode/stub.yue" + "spec/inputs/unicode/pipe.yue" + "spec/inputs/unicode/string.yue" + "spec/inputs/unicode/local.yue" + "spec/inputs/unicode/tables.yue" + "spec/inputs/unicode/operators.yue" + "spec/inputs/unicode/lists.yue" + "spec/inputs/unicode/switch.yue" + "spec/inputs/unicode/multiline_chain.yue" + "spec/inputs/unicode/existential.yue" + "spec/inputs/unicode/export_default.yue" + "spec/inputs/unicode/assign.yue" + "spec/inputs/unicode/literals.yue" + "spec/inputs/unicode/ambiguous.yue" + "spec/inputs/unicode/bubbling.yue" + "spec/inputs/unicode/try_catch.yue" + "spec/inputs/unicode/funcs.yue" + "spec/inputs/unicode/do.yue" + "spec/inputs/unicode/with.yue" + "spec/inputs/unicode/export.yue" + "spec/inputs/unicode/macro_todo.yue" + "spec/inputs/unicode/backcall.yue" + "spec/inputs/unicode/cond.yue" + "spec/inputs/unicode/in_expression.yue" + "spec/inputs/unicode/comprehension.yue" + "spec/inputs/unicode/import.yue" + "spec/inputs/unicode/unless_else.yue" + "spec/inputs/unicode/destructure.yue" + "spec/inputs/unicode/return.yue" + "spec/inputs/unicode/loops.yue" + "spec/inputs/unicode/class.yue" + "spec/inputs/unicode/vararg.yue" + "spec/inputs/unicode/goto.yue" + "spec/inputs/unicode/metatable.yue" + "spec/inputs/unicode/syntax.yue" + "spec/inputs/unicode/global.yue" + "spec/inputs/unicode/plus.yue" +] + +import "yue" + +rewriteLineCol = (item)-> + item[2] = 0 + item[3] = 0 + for i = 4, #item + switch type item[i] when "table" + if item[i][1] == "comment" + table.remove item, i + rewriteLineCol item + return + rewriteLineCol item[i] + +<- describe "format" +for file in *files + <- it file + f = io.open file + code = f\read "a*" + f\close! + + original_ast = yue.to_ast code + assert.is_not_nil original_ast + rewriteLineCol original_ast + formated = yue.format code + ast = yue.to_ast formated + assert.is_not_nil ast + rewriteLineCol ast + assert.same original_ast, ast + diff --git a/spec/outputs/test/format_spec.lua b/spec/outputs/test/format_spec.lua new file mode 100644 index 0000000..b12fb04 --- /dev/null +++ b/spec/outputs/test/format_spec.lua @@ -0,0 +1,133 @@ +local files = { + "spec/inputs/macro_export.yue", + "spec/inputs/attrib.yue", + "spec/inputs/macro.yue", + "spec/inputs/using.yue", + "spec/inputs/whitespace.yue", + "spec/inputs/nil_coalescing.yue", + "spec/inputs/stub.yue", + "spec/inputs/pipe.yue", + "spec/inputs/teal_lang.yue", + "spec/inputs/string.yue", + "spec/inputs/local.yue", + "spec/inputs/tables.yue", + "spec/inputs/operators.yue", + "spec/inputs/lists.yue", + "spec/inputs/compile_doc.yue", + "spec/inputs/switch.yue", + "spec/inputs/multiline_chain.yue", + "spec/inputs/existential.yue", + "spec/inputs/export_default.yue", + "spec/inputs/assign.yue", + "spec/inputs/literals.yue", + "spec/inputs/luarocks_upload.yue", + "spec/inputs/ambiguous.yue", + "spec/inputs/bubbling.yue", + "spec/inputs/try_catch.yue", + "spec/inputs/funcs.yue", + "spec/inputs/do.yue", + "spec/inputs/with.yue", + "spec/inputs/export.yue", + "spec/inputs/macro_todo.yue", + "spec/inputs/backcall.yue", + "spec/inputs/cond.yue", + "spec/inputs/in_expression.yue", + "spec/inputs/comprehension.yue", + "spec/inputs/macro_teal.yue", + "spec/inputs/import.yue", + "spec/inputs/unless_else.yue", + "spec/inputs/destructure.yue", + "spec/inputs/return.yue", + "spec/inputs/loops.yue", + "spec/inputs/class.yue", + "spec/inputs/vararg.yue", + "spec/inputs/goto.yue", + "spec/inputs/metatable.yue", + "spec/inputs/syntax.yue", + "spec/inputs/global.yue", + "spec/inputs/plus.yue", + "spec/inputs/test/class_spec.yue", + "spec/inputs/test/table_spreading_spec.yue", + "spec/inputs/test/loops_spec.yue", + "spec/inputs/test/format_spec.yue", + "spec/inputs/unicode/macro_export.yue", + "spec/inputs/unicode/attrib.yue", + "spec/inputs/unicode/macro.yue", + "spec/inputs/unicode/using.yue", + "spec/inputs/unicode/whitespace.yue", + "spec/inputs/unicode/nil_coalescing.yue", + "spec/inputs/unicode/stub.yue", + "spec/inputs/unicode/pipe.yue", + "spec/inputs/unicode/string.yue", + "spec/inputs/unicode/local.yue", + "spec/inputs/unicode/tables.yue", + "spec/inputs/unicode/operators.yue", + "spec/inputs/unicode/lists.yue", + "spec/inputs/unicode/switch.yue", + "spec/inputs/unicode/multiline_chain.yue", + "spec/inputs/unicode/existential.yue", + "spec/inputs/unicode/export_default.yue", + "spec/inputs/unicode/assign.yue", + "spec/inputs/unicode/literals.yue", + "spec/inputs/unicode/ambiguous.yue", + "spec/inputs/unicode/bubbling.yue", + "spec/inputs/unicode/try_catch.yue", + "spec/inputs/unicode/funcs.yue", + "spec/inputs/unicode/do.yue", + "spec/inputs/unicode/with.yue", + "spec/inputs/unicode/export.yue", + "spec/inputs/unicode/macro_todo.yue", + "spec/inputs/unicode/backcall.yue", + "spec/inputs/unicode/cond.yue", + "spec/inputs/unicode/in_expression.yue", + "spec/inputs/unicode/comprehension.yue", + "spec/inputs/unicode/import.yue", + "spec/inputs/unicode/unless_else.yue", + "spec/inputs/unicode/destructure.yue", + "spec/inputs/unicode/return.yue", + "spec/inputs/unicode/loops.yue", + "spec/inputs/unicode/class.yue", + "spec/inputs/unicode/vararg.yue", + "spec/inputs/unicode/goto.yue", + "spec/inputs/unicode/metatable.yue", + "spec/inputs/unicode/syntax.yue", + "spec/inputs/unicode/global.yue", + "spec/inputs/unicode/plus.yue" +} +local yue = require("yue") +local rewriteLineCol +rewriteLineCol = function(item) + item[2] = 0 + item[3] = 0 + for i = 4, #item do + do + local _exp_0 = type(item[i]) + if "table" == _exp_0 then + if item[i][1] == "comment" then + table.remove(item, i) + rewriteLineCol(item) + return + end + rewriteLineCol(item[i]) + end + end + end +end +return describe("format", function() + for _index_0 = 1, #files do + local file = files[_index_0] + it(file, function() + local f = io.open(file) + local code = f:read("a*") + f:close() + local original_ast = yue.to_ast(code) + assert.is_not_nil(original_ast) + rewriteLineCol(original_ast) + local formated = yue.format(code) + local ast = yue.to_ast(formated) + assert.is_not_nil(ast) + rewriteLineCol(ast) + return assert.same(original_ast, ast) + end) + end +end) diff --git a/src/yue_wasm.cpp b/src/yue_wasm.cpp index 4a054e6..daa5757 100644 --- a/src/yue_wasm.cpp +++ b/src/yue_wasm.cpp @@ -138,7 +138,7 @@ std::string exec(const std::string& codes) { }; execStr(R"yuescript( -_G.__output = {} +_G.__output = [] _G.print = (...)-> _G.__output[] = table.concat [tostring select i, ... for i = 1, select "#", ...], " " _G.__output[] = "\n" diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp index 6126f71..6b2c96d 100644 --- a/src/yuescript/yue_ast.cpp +++ b/src/yuescript/yue_ast.cpp @@ -230,7 +230,7 @@ std::string LocalValues_t::to_string(void* ud) const { temp.emplace_back(nameList->to_string(ud)); if (valueList) { if (valueList.is()) { - temp.emplace_back("="s + valueList->to_string(ud)); + temp.emplace_back("=\n"s + valueList->to_string(ud)); } else { temp.emplace_back("="s); temp.emplace_back(valueList->to_string(ud)); @@ -312,7 +312,10 @@ std::string ShortTabAppending_t::to_string(void* ud) const { std::string Backcall_t::to_string(void* ud) const { str_list temp; if (argsDef) { - temp.emplace_back(argsDef->to_string(ud)); + auto def = argsDef->to_string(ud); + if (!def.empty()) { + temp.emplace_back(def); + } } temp.emplace_back(arrow->to_string(ud)); temp.emplace_back(value->to_string(ud)); @@ -343,7 +346,7 @@ std::string ExpList_t::to_string(void* ud) const { std::string Return_t::to_string(void* ud) const { str_list temp{"return"s}; if (valueList) { - temp.emplace_back(valueList.is() ? ""s : " "s); + temp.emplace_back(valueList.is() ? "\n"s : " "s); temp.emplace_back(valueList->to_string(ud)); } return join(temp); @@ -365,6 +368,9 @@ std::string With_t::to_string(void* ud) const { } info->pushScope(); auto code = body->to_string(ud); + if (code.empty()) { + code = info->ind() + "--"s; + } info->popScope(); return line + '\n' + code; } @@ -383,6 +389,9 @@ std::string SwitchCase_t::to_string(void* ud) const { } else { info->pushScope(); auto block = body->to_string(ud); + if (block.empty()) { + block = info->ind() + "--"s; + } info->popScope(); auto line = "when "s + condition->to_string(ud); if (line.find('\n') != std::string::npos) { @@ -405,6 +414,9 @@ std::string Switch_t::to_string(void* ud) const { temp.emplace_back(info->ind() + "else"s); info->pushScope(); temp.emplace_back(lastBranch->to_string(ud)); + if (temp.back().empty()) { + temp.back() = info->ind() + "--"s; + } info->popScope(); } } @@ -412,10 +424,18 @@ std::string Switch_t::to_string(void* ud) const { return join(temp, "\n"sv); } std::string Assignment_t::to_string(void* ud) const { - return expList->to_string(ud) + ' ' + assign->to_string(ud); + if (expList) { + return ", "s + expList->to_string(ud) + " :"s + assign->to_string(ud); + } else { + return " :"s + assign->to_string(ud); + } } std::string IfCond_t::to_string(void* ud) const { - return condition->to_string(ud); + if (assignment) { + return condition->to_string(ud) + assignment->to_string(ud); + } else { + return condition->to_string(ud); + } } std::string If_t::to_string(void* ud) const { auto info = reinterpret_cast(ud); @@ -447,11 +467,17 @@ std::string If_t::to_string(void* ud) const { if (condition) { info->pushScope(); temp.emplace_back(node->to_string(ud)); + if (temp.back().empty()) { + temp.back() = info->ind() + "--"s; + } info->popScope(); } else { temp.emplace_back(info->ind() + "else"s); info->pushScope(); temp.emplace_back(node->to_string(ud)); + if (temp.back().empty()) { + temp.back() = info->ind() + "--"s; + } info->popScope(); } condition = false; @@ -473,6 +499,9 @@ std::string While_t::to_string(void* ud) const { } info->pushScope(); temp.emplace_back(body->to_string(ud)); + if (temp.back().empty()) { + temp.back() = info->ind() + "--"s; + } info->popScope(); } return join(temp, "\n"sv); @@ -486,6 +515,9 @@ std::string Repeat_t::to_string(void* ud) const { temp.emplace_back("repeat"s); info->pushScope(); temp.emplace_back(body->to_string(ud)); + if (temp.back().empty()) { + temp.back() = info->ind() + "--"s; + } info->popScope(); } temp.emplace_back(info->ind() + "until "s + condition->to_string(ud)); @@ -508,6 +540,9 @@ std::string For_t::to_string(void* ud) const { } info->pushScope(); auto block = body->to_string(ud); + if (block.empty()) { + block = info->ind() + "--"s; + } info->popScope(); return line + '\n' + block; } @@ -523,6 +558,9 @@ std::string ForEach_t::to_string(void* ud) const { } info->pushScope(); auto block = body->to_string(ud); + if (block.empty()) { + block = info->ind() + "--"s; + } info->popScope(); return line + '\n' + block; } @@ -534,6 +572,9 @@ std::string Do_t::to_string(void* ud) const { } else { info->pushScope(); auto block = body->to_string(ud); + if (block.empty()) { + block = info->ind() + "--"s; + } info->popScope(); return "do\n"s + block; } @@ -543,6 +584,9 @@ std::string CatchBlock_t::to_string(void* ud) const { auto line = "catch "s + err->to_string(ud); info->pushScope(); auto block = body->to_string(ud); + if (block.empty()) { + block = info->ind() + "--"s; + } info->popScope(); return line + '\n' + block; } @@ -555,6 +599,9 @@ std::string Try_t::to_string(void* ud) const { temp.emplace_back("try"s); info->pushScope(); temp.emplace_back(func->to_string(ud)); + if (temp.back().empty()) { + temp.back() = info->ind() + "--"s; + } info->popScope(); } if (catchBlock) { @@ -571,7 +618,11 @@ std::string Comprehension_t::to_string(void* ud) const { temp.front().insert(0, temp.front()[0] == '[' ? " "s : ""s); } if (items.size() != 2 || !ast_is(items.back())) { - return '[' + join(temp, ", "sv) + ']'; + if (items.size() == 1) { + return '[' + join(temp, ", "sv) + ",]"s; + } else { + return '[' + join(temp, ", "sv) + ']'; + } } else { return '[' + join(temp, " "sv) + ']'; } @@ -613,7 +664,7 @@ std::string CompInner_t::to_string(void* ud) const { std::string Assign_t::to_string(void* ud) const { str_list temp; if (values.size() == 1 && ast_is(values.front())) { - return '=' + values.front()->to_string(ud); + return "=\n"s + values.front()->to_string(ud); } for (auto value : values.objects()) { temp.emplace_back(value->to_string(ud)); @@ -790,7 +841,13 @@ static bool isInBlockExp(ast_node* node) { TblComprehension_t, Comprehension_t>(simpleValue->value)) { return true; } + } else if (auto chainValue = value->item.as()) { + if (ast_is(chainValue->items.back())) { + return true; + } } + } else if (ast_is(node)) { + return true; } return false; } @@ -863,7 +920,7 @@ std::string TableBlock_t::to_string(void* ud) const { temp.emplace_back(info->ind() + "* "s + value->to_string(ud)); break; case id(): - temp.emplace_back(info->ind() + "*"s + value->to_string(ud)); + temp.emplace_back(info->ind() + "*\n"s + value->to_string(ud)); break; default: temp.emplace_back(info->ind() + value->to_string(ud)); @@ -871,21 +928,25 @@ std::string TableBlock_t::to_string(void* ud) const { } } info->popScope(); - return '\n' + join(temp, "\n"sv); + return join(temp, "\n"sv); } std::string TableBlockIndent_t::to_string(void* ud) const { auto info = reinterpret_cast(ud); str_list temp; info->pushScope(); for (auto value : values.objects()) { - if (value == values.front()) { - temp.emplace_back("* "s + value->to_string(ud)); + if (ast_is(value)) { + temp.emplace_back("\n"s + value->to_string(ud)); } else { - temp.emplace_back(info->ind() + value->to_string(ud)); + if (value == values.front()) { + temp.emplace_back(" "s + value->to_string(ud)); + } else { + temp.emplace_back(info->ind() + value->to_string(ud)); + } } } info->popScope(); - return join(temp, "\n"sv); + return "*"s + join(temp, "\n"sv); } std::string ClassMemberList_t::to_string(void* ud) const { auto info = reinterpret_cast(ud); @@ -893,7 +954,7 @@ std::string ClassMemberList_t::to_string(void* ud) const { for (auto value : values.objects()) { temp.emplace_back(info->ind() + value->to_string(ud)); } - return join(temp, "\n"sv); + return join(temp, ", "sv); } std::string ClassBlock_t::to_string(void* ud) const { auto info = reinterpret_cast(ud); @@ -929,7 +990,7 @@ std::string GlobalValues_t::to_string(void* ud) const { auto line = nameList->to_string(ud); if (valueList) { if (valueList.is()) { - line += " ="s + valueList->to_string(ud); + line += " =\n"s + valueList->to_string(ud); } else { line += " = "s + valueList->to_string(ud); } @@ -949,8 +1010,12 @@ std::string Export_t::to_string(void* ud) const { line += target->to_string(ud); break; case id(): { - auto valueStr = target->to_string(ud); - line += '[' + (valueStr[0] == '[' ? " "s : ""s) + valueStr + ']'; + if (def) { + line += target->to_string(ud); + } else { + auto valueStr = target->to_string(ud); + line += '[' + (valueStr[0] == '[' ? " "s : ""s) + valueStr + ']'; + } break; } default: @@ -973,7 +1038,7 @@ std::string NormalPair_t::to_string(void* ud) const { } else { line = key->to_string(ud) + ":"s; } - line += (value.is() ? ""s : " "s) + value->to_string(ud); + line += (value.is() ? "\n"s : " "s) + value->to_string(ud); return line; } std::string MetaVariablePair_t::to_string(void* ud) const { @@ -989,7 +1054,7 @@ std::string MetaNormalPair_t::to_string(void* ud) const { } else { line = '<' + key->to_string(ud) + ">:"s; } - line += (value.is() ? ""s : " "s) + value->to_string(ud); + line += (value.is() ? "\n"s : " "s) + value->to_string(ud); return line; } std::string VariablePairDef_t::to_string(void* ud) const { @@ -1033,23 +1098,38 @@ std::string FnArgDef_t::to_string(void* ud) const { line += op->to_string(ud); } if (defaultValue) { - if (isInBlockExp(defaultValue)) { - line += " = ("s + defaultValue->to_string(ud) + ')'; - } else { - line += " = "s + defaultValue->to_string(ud); - } + line += " = "s + defaultValue->to_string(ud); } return line; } std::string FnArgDefList_t::to_string(void* ud) const { + auto info = reinterpret_cast(ud); str_list temp; + bool hasInBlockExp = false; for (auto def : definitions.objects()) { - temp.emplace_back(def->to_string(ud)); + auto argDef = static_cast(def); + if (argDef->defaultValue && isInBlockExp(argDef->defaultValue)) { + hasInBlockExp = true; + break; + } } - if (varArg) { - temp.emplace_back(varArg->to_string(ud)); + if (hasInBlockExp) { + for (auto def : definitions.objects()) { + temp.emplace_back(info->ind() + def->to_string(ud)); + } + if (varArg) { + temp.emplace_back(info->ind() + varArg->to_string(ud)); + } + return join(temp, "\n"sv); + } else { + for (auto def : definitions.objects()) { + temp.emplace_back(def->to_string(ud)); + } + if (varArg) { + temp.emplace_back(varArg->to_string(ud)); + } + return join(temp, ", "sv); } - return join(temp, ", "sv); } std::string OuterVarShadow_t::to_string(void* ud) const { if (varList) { @@ -1059,14 +1139,38 @@ std::string OuterVarShadow_t::to_string(void* ud) const { } } std::string FnArgsDef_t::to_string(void* ud) const { - std::string line; + auto info = reinterpret_cast(ud); + bool hasInBlockExp = false; if (defList) { - line += defList->to_string(ud); + for (auto def : defList->definitions.objects()) { + auto argDef = static_cast(def); + if (argDef->defaultValue && isInBlockExp(argDef->defaultValue)) { + hasInBlockExp = true; + break; + } + } } - if (shadowOption) { - line += (line.empty() ? ""s : " "s) + shadowOption->to_string(ud); + if (hasInBlockExp) { + str_list temp; + info->pushScope(); + if (defList) { + temp.push_back(defList->to_string(ud)); + } + if (shadowOption) { + temp.push_back(info->ind() + shadowOption->to_string(ud)); + } + info->popScope(); + return "(\n" + join(temp, "\n"sv) + '\n' + info->ind() + ')'; + } else { + std::string line; + if (defList) { + line += defList->to_string(ud); + } + if (shadowOption) { + line += (line.empty() ? ""s : " "s) + shadowOption->to_string(ud); + } + return line.empty() ? (defList ? "()"s : ""s) : '(' + line + ')'; } - return line.empty() ? ""s : '(' + line + ')'; } std::string FunLit_t::to_string(void* ud) const { auto info = reinterpret_cast(ud); @@ -1080,7 +1184,11 @@ std::string FunLit_t::to_string(void* ud) const { line += ' ' + body->to_string(ud); } else { info->pushScope(); - line += '\n' + body->to_string(ud); + auto bodyStr = body->to_string(ud); + if (bodyStr.empty()) { + bodyStr = info->ind() + "--"s; + } + line += '\n' + bodyStr; info->popScope(); } } @@ -1096,14 +1204,16 @@ std::string MacroLit_t::to_string(void* ud) const { line = '(' + argsDef->to_string(ud) + ')'; } line += "->"s; - if (body) { - if (body->content.is()) { - line += ' ' + body->to_string(ud); - } else { - info->pushScope(); - line += '\n' + body->to_string(ud); - info->popScope(); + if (body->content.is()) { + line += ' ' + body->to_string(ud); + } else { + info->pushScope(); + auto bodyStr = body->to_string(ud); + if (bodyStr.empty()) { + bodyStr = info->ind() + "--"s; } + line += '\n' + bodyStr; + info->popScope(); } return line; } @@ -1113,14 +1223,16 @@ std::string Macro_t::to_string(void* ud) const { std::string MacroInPlace_t::to_string(void* ud) const { auto info = reinterpret_cast(ud); auto line = "$->"s; - if (body) { - if (body->content.is()) { - line += ' ' + body->to_string(ud); - } else { - info->pushScope(); - line += '\n' + body->to_string(ud); - info->popScope(); + if (body->content.is()) { + line += ' ' + body->to_string(ud); + } else { + info->pushScope(); + auto bodyStr = body->to_string(ud); + if (bodyStr.empty()) { + bodyStr = info->ind() + "--"s; } + line += '\n' + bodyStr; + info->popScope(); } return line; } @@ -1135,15 +1247,32 @@ std::string AssignableNameList_t::to_string(void* ud) const { return join(temp, ", "sv); } std::string InvokeArgs_t::to_string(void* ud) const { - if (!args.empty() && ast_is(args.back())) { + auto info = reinterpret_cast(ud); + bool hasInBlockExp = false; + for (auto arg : args.objects()) { + if (isInBlockExp(arg)) { + hasInBlockExp = true; + break; + } + } + if (hasInBlockExp) { str_list temp; + bool newLine = false; for (auto arg : args.objects()) { - if (arg != args.back()) { - temp.emplace_back(arg->to_string(ud)); + if (arg == args.front()) { + if (ast_is(arg)) { + newLine = true; + } + temp.push_back(arg->to_string(ud)); + info->pushScope(); + } else if (ast_is(arg)) { + temp.push_back(arg->to_string(ud)); + } else { + temp.push_back(info->ind() + arg->to_string(ud)); } } - auto list = join(temp, ", "sv); - return ' ' + list + (list.empty() ? ""s : ","s) + args.back()->to_string(ud); + info->popScope(); + return (newLine ? '\n' : ' ') + join(temp, ",\n"sv); } else { str_list temp; for (auto arg : args.objects()) { @@ -1203,10 +1332,29 @@ std::string StatementAppendix_t::to_string(void* ud) const { } std::string Statement_t::to_string(void* ud) const { std::string line; - if (appendix) { - return content->to_string(ud) + ' ' + appendix->to_string(ud); + if (!comments.empty()) { + auto info = reinterpret_cast(ud); + str_list temp; + for (ast_node* comment : comments.objects()) { + if (comment == comments.front()) { + temp.push_back(comment->to_string(ud)); + } else { + temp.push_back(info->ind() + comment->to_string(ud)); + } + } + if (appendix) { + temp.push_back(info->ind() + content->to_string(ud) + ' ' + appendix->to_string(ud)); + return join(temp, "\n"sv); + } else { + temp.push_back(info->ind() + content->to_string(ud)); + return join(temp, "\n"sv); + } } else { - return content->to_string(ud); + if (appendix) { + return content->to_string(ud) + ' ' + appendix->to_string(ud); + } else { + return content->to_string(ud); + } } } std::string StatementSep_t::to_string(void*) const { diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 3e60556..59f6d4b 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -75,7 +75,7 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.21.1"sv; +const std::string_view version = "0.21.2"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index 4b865f5..89791d6 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -477,7 +477,7 @@ YueParser::YueParser() { space ); - list_lit_lines = space_break >> list_lit_line >> *(-(space >> ',') >> space_break >> list_lit_line) >> -(space >> ','); + list_lit_lines = +space_break >> list_lit_line >> *(-(space >> ',') >> space_break >> list_lit_line) >> -(space >> ','); Comprehension = '[' >> not_('[') >> Seperator >> space >> ( @@ -489,7 +489,6 @@ YueParser::YueParser() { white >> ']' >> not_(space >> '=') ); - (space >> disable_for_rule(Exp) >> space >> CompInner >> space >> ']'); CompValue = ',' >> space >> Exp; TblComprehension = and_('{') >> ('{' >> space >> disable_for_rule(Exp >> space >> -(CompValue >> space)) >> CompInner >> space >> '}' | braces_expression_error); @@ -829,17 +828,26 @@ YueParser::YueParser() { '*' >> space >> (SpreadExp | Exp | TableBlock) ); + fn_arg_def_list = FnArgDef >> *(space >> ',' >> space >> FnArgDef); + + fn_arg_def_lit_line = ( + push_indent_match >> (space >> fn_arg_def_list >> pop_indent | pop_indent) + ) | ( + space + ); + + fn_arg_def_lit_lines = fn_arg_def_lit_line >> *(-(space >> ',') >> space_break >> fn_arg_def_lit_line); + FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '=' >> space >> Exp); - FnArgDefList = Seperator >> (( - FnArgDef >> - *(space >> (',' | line_break) >> white >> FnArgDef) >> - -(space >> (',' | line_break) >> white >> VarArg) - ) | VarArg); + FnArgDefList = Seperator >> ( + fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg) | + white >> VarArg + ); OuterVarShadow = key("using") >> space >> (NameList | key("nil")); - FnArgsDef = '(' >> white >> -FnArgDefList >> -(space >> OuterVarShadow) >> white >> ')'; + FnArgsDef = '(' >> *space_break >> -FnArgDefList >> -(white >> OuterVarShadow) >> white >> ')'; FnArrow = expr("->") | "=>"; FunLit = -FnArgsDef >> space >> FnArrow >> -(space >> Body); @@ -873,8 +881,8 @@ YueParser::YueParser() { invoke_args_with_table = ',' >> ( - TableBlock | - space_break >> advance_match >> arg_block >> -arg_table_block + arg_table_block | + space_break >> advance_match >> arg_block >> -(-(space >> ',') >> arg_table_block) ) | arg_table_block; leading_spaces_error = pl::user(+space_one >> '(' >> space >> Exp >> +(space >> ',' >> space >> Exp) >> space >> ')', [](const item_t& item) { @@ -954,11 +962,10 @@ YueParser::YueParser() { Body = in_block | Statement; - empty_line_break = ( - check_indent >> (multi_line_comment >> space | comment) | - advance >> ensure(multi_line_comment >> space | comment, pop_indent) | - plain_space - ) >> and_(stop); + empty_line_break = + check_indent >> (multi_line_comment >> space | comment) >> and_(stop) | + advance >> ensure(multi_line_comment >> space | comment, pop_indent) >> and_(stop) | + plain_space >> and_(line_break); indentation_error = pl::user(not_(pipe_operator | eof()), [](const item_t& item) { throw ParserError("unexpected indent"sv, item.begin); @@ -1084,6 +1091,11 @@ void trim(std::string& str) { } // namespace Utils std::string ParseInfo::errorMessage(std::string_view msg, int errLine, int errCol, int lineOffset) const { + if (!codes) { + std::ostringstream buf; + buf << errLine + lineOffset << ": "sv << msg; + return buf.str(); + } const int ASCII = 255; int length = errLine; auto begin = codes->begin(); diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index c3d5d7d..e183bf2 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -222,6 +222,9 @@ private: NONE_AST_RULE(fn_args_lit_line); NONE_AST_RULE(fn_args_lit_lines); NONE_AST_RULE(fn_args); + NONE_AST_RULE(fn_arg_def_list); + NONE_AST_RULE(fn_arg_def_lit_line); + NONE_AST_RULE(fn_arg_def_lit_lines); NONE_AST_RULE(destruct_def); NONE_AST_RULE(macro_args_def); NONE_AST_RULE(chain_call); diff --git a/src/yuescript/yuescript.cpp b/src/yuescript/yuescript.cpp index 856d8f3..dbaa287 100644 --- a/src/yuescript/yuescript.cpp +++ b/src/yuescript/yuescript.cpp @@ -172,6 +172,39 @@ static int yuetolua(lua_State* L) { return 3; } +static int yueformat(lua_State* L) { + size_t len = 0; + auto input = luaL_checklstring(L, 1, &len); + int tabSize = 0; + if (!lua_isnoneornil(L, 2)) { + tabSize = static_cast(luaL_checkinteger(L, 2)); + } + std::string_view codes(input, len); + auto info = yue::YueParser{}.parse(codes); + if (info.error) { + const auto& error = info.error.value(); + if (!info.codes) { + lua_pushnil(L); + lua_pushlstring(L, error.msg.c_str(), error.msg.size()); + return 2; + } + auto displayMessage = info.errorMessage(error.msg, error.line, error.col, 0); + lua_pushnil(L); + lua_pushlstring(L, displayMessage.c_str(), displayMessage.size()); + return 2; + } + yue::YueFormat formatter{}; + if (tabSize > 0) { + formatter.spaceOverTab = true; + formatter.tabSpaces = tabSize; + } else { + formatter.spaceOverTab = false; + } + auto result = formatter.toString(info.node.get()); + lua_pushlstring(L, result.c_str(), result.size()); + return 1; +} + static int yuecheck(lua_State* L) { size_t len = 0; auto input = luaL_checklstring(L, 1, &len); @@ -268,6 +301,7 @@ static int yuetoast(lua_State* L) { stack.pop(); }; do_call(info.node); + yue::YueFormat formatter{}; while (!stack.empty()) { auto& current = stack.top(); int continuation = current.continuation; @@ -319,7 +353,8 @@ static int yuetoast(lua_State* L) { lua_rawseti(L, -2, 2); lua_pushinteger(L, node->m_begin.m_col); lua_rawseti(L, -2, 3); - auto str = parser.toString(node); + formatter.indent = 0; + auto str = node->to_string(&formatter); yue::Utils::trim(str); lua_pushlstring(L, str.c_str(), str.length()); lua_rawseti(L, -2, 4); @@ -371,8 +406,9 @@ static int yuetoast(lua_State* L) { return 1; } else { lua_pushnil(L); - const auto& msg = info.error.value().msg; - lua_pushlstring(L, msg.c_str(), msg.length()); + const auto& err = info.error.value(); + auto displayMessage = info.errorMessage(err.msg, err.line, err.col); + lua_pushlstring(L, displayMessage.c_str(), displayMessage.length()); return 2; } } @@ -381,6 +417,7 @@ static const luaL_Reg yuelib[] = { {"to_lua", yuetolua}, {"to_ast", yuetoast}, {"check", yuecheck}, + {"format", yueformat}, {"version", nullptr}, {"options", nullptr}, {"load_stacktraceplus", nullptr}, -- cgit v1.2.3-55-g6feb