From 5604bbbb80bfcedb4a9085b90864e221f8104b33 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 28 May 2025 18:20:16 +0800 Subject: Added `try!` syntax. --- src/yuescript/yue_ast.cpp | 10 ++- src/yuescript/yue_ast.h | 6 +- src/yuescript/yue_compiler.cpp | 155 +++++++++++++++++++++++++++++------------ src/yuescript/yue_compiler.h | 1 + src/yuescript/yue_parser.cpp | 25 ++++--- src/yuescript/yue_parser.h | 13 ++-- src/yuescript/yuescript.cpp | 15 +++- 7 files changed, 163 insertions(+), 62 deletions(-) (limited to 'src') diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp index 9d68161..be10859 100644 --- a/src/yuescript/yue_ast.cpp +++ b/src/yuescript/yue_ast.cpp @@ -609,13 +609,19 @@ std::string CatchBlock_t::to_string(void* ud) const { info->popScope(); return line + '\n' + blockStr; } +std::string Omit_t::to_string(void*) const { + return "!"s; +} std::string Try_t::to_string(void* ud) const { auto info = reinterpret_cast(ud); str_list temp; + temp.emplace_back("try"s); + if (omit) { + temp.back() += '!'; + } if (func.is()) { - temp.emplace_back("try "s + func->to_string(ud)); + temp.back() += (" "s + func->to_string(ud)); } else { - temp.emplace_back("try"s); info->pushScope(); temp.emplace_back(func->to_string(ud)); if (temp.back().empty()) { diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h index d3e6368..393f374 100644 --- a/src/yuescript/yue_ast.h +++ b/src/yuescript/yue_ast.h @@ -383,10 +383,14 @@ AST_NODE(CatchBlock) AST_MEMBER(CatchBlock, &err, &block) AST_END(CatchBlock) +AST_LEAF(Omit) +AST_END(Omit) + AST_NODE(Try) + ast_ptr omit; ast_sel func; ast_ptr catchBlock; - AST_MEMBER(Try, &func, &catchBlock) + AST_MEMBER(Try, &omit, &func, &catchBlock) AST_END(Try) AST_NODE(Comprehension) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 2312025..35745f2 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -78,7 +78,7 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.28.5"sv; +const std::string_view version = "0.28.6"sv; const std::string_view extension = "yue"sv; class CompileError : public std::logic_error { @@ -165,12 +165,12 @@ public: double compileTime = 0.0; if (config.profiling) { auto start = std::chrono::high_resolution_clock::now(); - _info = _parser.parse(codes); + _info = _parser.parse(codes, config.lax); auto stop = std::chrono::high_resolution_clock::now(); std::chrono::duration diff = stop - start; parseTime = diff.count(); } else { - _info = _parser.parse(codes); + _info = _parser.parse(codes, config.lax); } std::unique_ptr globals; std::unique_ptr options; @@ -1258,7 +1258,7 @@ private: template ast_ptr toAst(std::string_view codes, ast_node* parent) { - auto res = _parser.parse(std::string(codes)); + auto res = _parser.parse(std::string(codes), false); if (res.error) { throw CompileError(res.error.value().msg, parent); } @@ -2330,6 +2330,17 @@ private: out.back().insert(0, preDefine); return false; } + case id(): { + auto tryNode = static_cast(value); + if (tryNode->omit) { + auto assignList = assignment->expList.get(); + std::string preDefine = getPreDefineLine(assignment); + transformTry(tryNode, out, ExpUsage::Assignment, assignList); + out.back().insert(0, preDefine); + return false; + } + break; + } case id(): { auto switchNode = static_cast(value); auto assignList = assignment->expList.get(); @@ -4272,12 +4283,22 @@ private: std::optional> upValueFuncFromExp(Exp_t* exp, str_list* ensureArgListInTheEnd, bool blockRewrite) { if (checkUpValueFuncAvailable(exp)) { + auto block = exp->new_ptr(); + if (auto sVal = simpleSingleValueFrom(exp)) { + if (auto doNode = sVal->value.as()) { + if (auto blk = doNode->body->content.as()) { + block->statements.dup(blk->statements); + } else { + block->statements.push_back(doNode->body->content.to()); + } + return getUpValueFuncFromBlock(block, ensureArgListInTheEnd, false, blockRewrite); + } + } auto returnNode = exp->new_ptr(); returnNode->explicitReturn = false; auto returnList = exp->new_ptr(); returnList->exprs.push_back(exp); returnNode->valueList.set(returnList); - auto block = exp->new_ptr(); auto stmt = exp->new_ptr(); stmt->content.set(returnNode); block->statements.push_back(stmt); @@ -4799,11 +4820,7 @@ private: auto newBody = x->new_ptr(); newBody->content.set(followingBlock); { - auto doNode = x->new_ptr(); - doNode->body.set(newBody); - auto simpleValue = x->new_ptr(); - simpleValue->value.set(doNode); - if (auto result = upValueFuncFromExp(newExp(simpleValue, x), &argNames, true)) { + if (auto result = upValueFuncFromBlock(followingBlock.get(), &argNames, false, true)) { auto [funcName, args] = std::move(*result); str_list finalArgs; for (const auto& arg : args) { @@ -4811,9 +4828,13 @@ private: finalArgs.push_back(arg); } } - newBlock->statements.push_back(toAst(funcName + ' ' + join(finalArgs, ","sv), x)); + newBlock->statements.push_back(toAst(funcName + ' ' + (finalArgs.empty() ? "nil"s : join(finalArgs, ","sv)), x)); auto sVal = singleValueFrom(static_cast(newBlock->statements.back())->content.to()->expList); - ast_to(sVal->item.to()->items.back())->args.dup(newInvoke->args); + auto invokArgs = ast_to(sVal->item.to()->items.back()); + if (finalArgs.empty()) { + invokArgs->args.clear(); + } + invokArgs->args.dup(newInvoke->args); transformBlock(newBlock, out, usage, assignList, isRoot); return; } @@ -5093,36 +5114,45 @@ private: if (!nodes.empty()) { str_list temp; for (auto node : nodes) { - currentScope().lastStatement = (node == nodes.back()) && currentScope().mode == GlobalMode::None; - transformStatement(static_cast(node), temp); - if (isRoot && !_rootDefs.empty()) { - auto last = std::move(temp.back()); - temp.pop_back(); - temp.insert(temp.end(), _rootDefs.begin(), _rootDefs.end()); - _rootDefs.clear(); - temp.push_back(std::move(last)); - } - if (!temp.empty() && _parser.startWith(temp.back())) { - auto rit = ++temp.rbegin(); - if (rit != temp.rend() && !rit->empty()) { - auto index = std::string::npos; - if (_config.reserveLineNumber) { - index = rit->rfind(" -- "sv); - } else { - index = rit->find_last_not_of('\n'); - if (index != std::string::npos) index++; - } - if (index != std::string::npos) { - auto ending = rit->substr(0, index); - auto ind = ending.find_last_of(" \t\n"sv); - if (ind != std::string::npos) { - ending = ending.substr(ind + 1); + auto transformNode = [&]() { + currentScope().lastStatement = (node == nodes.back()) && currentScope().mode == GlobalMode::None; + transformStatement(static_cast(node), temp); + if (isRoot && !_rootDefs.empty()) { + auto last = std::move(temp.back()); + temp.pop_back(); + temp.insert(temp.end(), _rootDefs.begin(), _rootDefs.end()); + _rootDefs.clear(); + temp.push_back(std::move(last)); + } + if (!temp.empty() && _parser.startWith(temp.back())) { + auto rit = ++temp.rbegin(); + if (rit != temp.rend() && !rit->empty()) { + auto index = std::string::npos; + if (_config.reserveLineNumber) { + index = rit->rfind(" -- "sv); + } else { + index = rit->find_last_not_of('\n'); + if (index != std::string::npos) index++; } - if (LuaKeywords.find(ending) == LuaKeywords.end()) { - rit->insert(index, ";"sv); + if (index != std::string::npos) { + auto ending = rit->substr(0, index); + auto ind = ending.find_last_of(" \t\n"sv); + if (ind != std::string::npos) { + ending = ending.substr(ind + 1); + } + if (LuaKeywords.find(ending) == LuaKeywords.end()) { + rit->insert(index, ";"sv); + } } } } + }; + if (_config.lax) { + try { + transformNode(); + } catch (const CompileError&) { } + } else { + transformNode(); } } out.push_back(join(temp)); @@ -6193,7 +6223,7 @@ private: case id(): case id(): if (_withVars.empty()) { - throw CompileError("short dot/colon and indexing syntax must be called within a with block"sv, x); + throw CompileError("short dot/colon/indexing syntax must be called within a with block"sv, x); } else { temp.push_back(_withVars.top()); } @@ -6714,14 +6744,14 @@ private: } else { if (!codes.empty()) { if (isBlock) { - info = _parser.parse(codes); + info = _parser.parse(codes, false); if (info.error) { throw CompileError("failed to expand macro as block: "s + info.error.value().msg, x); } } else { - info = _parser.parse(codes); + info = _parser.parse(codes, false); if (!info.node && allowBlockMacroReturn) { - info = _parser.parse(codes); + info = _parser.parse(codes, false); if (info.error) { throw CompileError("failed to expand macro as expr or block: "s + info.error.value().msg, x); } @@ -10023,8 +10053,47 @@ private: out.push_back(join(temp)); } - void transformTry(Try_t* tryNode, str_list& out, ExpUsage usage) { + void transformTry(Try_t* tryNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { auto x = tryNode; + if (tryNode->omit && usage == ExpUsage::Assignment) { + str_list rets; + pushScope(); + auto okVar = getUnusedName("_ok_"sv); + for (size_t i = 0; i < assignList->exprs.size(); i++) { + auto retVar = getUnusedName("_ret_"sv); + rets.emplace_back(retVar); + addToScope(retVar); + } + popScope(); + auto varList = join(rets, ","sv); + auto ifNode = toAst("if "s + okVar + ',' + varList + ":=try nil then "s + varList, x); + auto exp = ast_to(ifNode->nodes.front())->assignment->assign->values.front(); + auto sVal = simpleSingleValueFrom(exp); + auto newTry = sVal->value.to(); + newTry->func.set(tryNode->func); + newTry->catchBlock.set(tryNode->catchBlock); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = x->new_ptr(); + assign->values.push_back(ifNode); + assignment->action.set(assign); + transformAssignment(assignment, out); + return; + } + if (tryNode->omit && usage != ExpUsage::Common) { + auto okVar = getUnusedName("_ok_"sv); + auto code = "do\n\t"s + okVar + ", ... = try nil\n\t... if "s + okVar; + auto doNode = toAst(code, x); + auto block = doNode->body->content.to(); + auto asmt = static_cast(block->statements.front())->content.to(); + auto assign = asmt->action.to(); + auto sVal = simpleSingleValueFrom(assign->values.back()); + auto newTry = sVal->value.to(); + newTry->func.set(tryNode->func); + newTry->catchBlock.set(tryNode->catchBlock); + transformDo(doNode, out, usage); + return; + } ast_ptr errHandler; if (tryNode->catchBlock) { auto catchBlock = tryNode->catchBlock.get(); diff --git a/src/yuescript/yue_compiler.h b/src/yuescript/yue_compiler.h index d352636..aff5978 100644 --- a/src/yuescript/yue_compiler.h +++ b/src/yuescript/yue_compiler.h @@ -31,6 +31,7 @@ struct YueConfig { bool reserveLineNumber = true; bool useSpaceOverTab = false; bool reserveComment = false; + bool lax = false; // internal options bool exporting = false; bool profiling = false; diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index e5bdc26..2b0aea8 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -332,7 +332,7 @@ YueParser::YueParser() { Exp; import_tab_list = import_tab_item >> *(space >> ',' >> space >> import_tab_item); import_tab_line = ( - push_indent_match >> (space >> import_tab_list >> pop_indent | pop_indent) + push_indent_match >> ensure(space >> import_tab_list, pop_indent) ) | space; import_tab_lines = space_break >> import_tab_line >> *(-(space >> ',') >> space_break >> import_tab_line) >> -(space >> ','); import_tab_key_value = key_value | ':' >> MacroName | MacroNamePair | ImportAllMacro; @@ -501,8 +501,9 @@ YueParser::YueParser() { return true; }); + Omit = expr('!'); CatchBlock = line_break >> *space_break >> check_indent_match >> space >> key("catch") >> space >> Variable >> space >> in_block; - Try = key("try") >> space >> (in_block | Exp) >> -CatchBlock; + Try = key("try") >> -Omit >> space >> (in_block | Exp) >> -CatchBlock; list_value = and_( @@ -667,7 +668,7 @@ YueParser::YueParser() { fn_args_value_list = Exp >> *(space >> ',' >> space >> Exp); fn_args_lit_line = ( - push_indent_match >> (space >> fn_args_value_list >> pop_indent | pop_indent) + push_indent_match >> ensure(space >> fn_args_value_list, pop_indent) ) | ( space ); @@ -875,7 +876,7 @@ YueParser::YueParser() { 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) + push_indent_match >> ensure(space >> fn_arg_def_list, pop_indent) ) | ( space ); @@ -1030,11 +1031,16 @@ YueParser::YueParser() { empty_line_break | advance_match >> ensure(space >> (indentation_error | Statement), pop_indent) ); - Block = Seperator >> line >> *(+line_break >> line); + Block = Seperator >> (pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + return st->lax; + }) >> lax_line >> *(+line_break >> lax_line) | line >> *(+line_break >> line)); shebang = "#!" >> *(not_(stop) >> any_char); BlockEnd = Block >> white >> stop; File = -shebang >> -Block >> white >> stop; + + lax_line = advance_match >> ensure(*(not_(stop) >> any()), pop_indent) | line >> and_(stop) | check_indent_match >> *(not_(stop) >> any()); } // clang-format on @@ -1064,7 +1070,7 @@ bool YueParser::startWith(std::string_view codes, rule& r) { return true; } -ParseInfo YueParser::parse(std::string_view codes, rule& r) { +ParseInfo YueParser::parse(std::string_view codes, rule& r, bool lax) { ParseInfo res; if (codes.substr(0, 3) == "\xEF\xBB\xBF"sv) { codes = codes.substr(3); @@ -1082,6 +1088,7 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) { error_list errors; try { State state; + state.lax = lax; res.node.set(::yue::parse(*(res.codes), r, errors, &state)); if (state.exportCount > 0) { int index = 0; @@ -1119,10 +1126,10 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) { return res; } -ParseInfo YueParser::parse(std::string_view astName, std::string_view codes) { +ParseInfo YueParser::parse(std::string_view astName, std::string_view codes, bool lax) { auto it = _rules.find(astName); if (it != _rules.end()) { - return parse(codes, *it->second); + return parse(codes, *it->second, lax); } return {}; } @@ -1131,7 +1138,7 @@ bool YueParser::match(std::string_view astName, std::string_view codes) { auto it = _rules.find(astName); if (it != _rules.end()) { auto rEnd = rule(*it->second >> eof()); - return parse(codes, rEnd).node; + return parse(codes, rEnd, false).node; } return false; } diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index 773bdbe..99f3d45 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -74,16 +74,16 @@ extern std::unordered_set Keywords; class YueParser { public: template - ParseInfo parse(std::string_view codes) { - return parse(codes, getRule()); + ParseInfo parse(std::string_view codes, bool lax) { + return parse(codes, getRule(), lax); } - ParseInfo parse(std::string_view astName, std::string_view codes); + ParseInfo parse(std::string_view astName, std::string_view codes, bool lax); template bool match(std::string_view codes) { auto rEnd = rule(getRule() >> eof()); - return parse(codes, rEnd).node; + return parse(codes, rEnd, false).node; } bool match(std::string_view astName, std::string_view codes); @@ -102,13 +102,14 @@ public: protected: YueParser(); - ParseInfo parse(std::string_view codes, rule& r); + ParseInfo parse(std::string_view codes, rule& r, bool lax); bool startWith(std::string_view codes, rule& r); struct State { State() { indents.push(0); } + bool lax = false; bool exportDefault = false; bool exportMacro = false; bool exportMetatable = false; @@ -287,6 +288,7 @@ private: NONE_AST_RULE(yue_line_comment); NONE_AST_RULE(line); NONE_AST_RULE(shebang); + NONE_AST_RULE(lax_line); AST_RULE(Num); AST_RULE(Name); @@ -345,6 +347,7 @@ private: AST_RULE(ForEach); AST_RULE(Do); AST_RULE(CatchBlock); + AST_RULE(Omit); AST_RULE(Try); AST_RULE(Comprehension); AST_RULE(CompValue); diff --git a/src/yuescript/yuescript.cpp b/src/yuescript/yuescript.cpp index 7e8e8b7..aa19b70 100644 --- a/src/yuescript/yuescript.cpp +++ b/src/yuescript/yuescript.cpp @@ -93,6 +93,12 @@ static void get_config(lua_State* L, yue::YueConfig& config) { config.useSpaceOverTab = lua_toboolean(L, -1) != 0; } lua_pop(L, 1); + lua_pushliteral(L, "lax"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.lax = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); lua_pushliteral(L, "options"); lua_gettable(L, -2); if (lua_istable(L, -1) != 0) { @@ -180,7 +186,7 @@ static int yueformat(lua_State* L) { tabSize = static_cast(luaL_checkinteger(L, 2)); } std::string_view codes(input, len); - auto info = yue::YueParser::shared().parse(codes); + auto info = yue::YueParser::shared().parse(codes, false); if (info.error) { const auto& error = info.error.value(); if (!info.codes) { @@ -282,8 +288,13 @@ static int yuetoast(lua_State* L) { ruleName = {name, nameSize}; } } + bool lax = false; + if (!lua_isnoneornil(L, 4)) { + luaL_checktype(L, 4, LUA_TBOOLEAN); + lax = lua_toboolean(L, 4) != 0; + } auto& yueParser = yue::YueParser::shared(); - auto info = ruleName.empty() ? yueParser.parse({input, size}) : yueParser.parse(ruleName, {input, size}); + auto info = ruleName.empty() ? yueParser.parse({input, size}, lax) : yueParser.parse(ruleName, {input, size}, lax); if (!info.error) { lua_createtable(L, 0, 0); int tableIndex = lua_gettop(L); -- cgit v1.2.3-55-g6feb