aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2025-05-28 18:20:16 +0800
committerLi Jin <dragon-fly@qq.com>2025-05-28 18:20:16 +0800
commit5604bbbb80bfcedb4a9085b90864e221f8104b33 (patch)
tree8828cddb1b0fe0ca734d6209507bdbda3133f48d /src
parent87267ca9e93606b70bdc0397349b176b8d142514 (diff)
downloadyuescript-5604bbbb80bfcedb4a9085b90864e221f8104b33.tar.gz
yuescript-5604bbbb80bfcedb4a9085b90864e221f8104b33.tar.bz2
yuescript-5604bbbb80bfcedb4a9085b90864e221f8104b33.zip
Added `try!` syntax.
Diffstat (limited to 'src')
-rw-r--r--src/yuescript/yue_ast.cpp10
-rw-r--r--src/yuescript/yue_ast.h6
-rw-r--r--src/yuescript/yue_compiler.cpp155
-rw-r--r--src/yuescript/yue_compiler.h1
-rw-r--r--src/yuescript/yue_parser.cpp25
-rw-r--r--src/yuescript/yue_parser.h13
-rw-r--r--src/yuescript/yuescript.cpp15
7 files changed, 163 insertions, 62 deletions
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 {
609 info->popScope(); 609 info->popScope();
610 return line + '\n' + blockStr; 610 return line + '\n' + blockStr;
611} 611}
612std::string Omit_t::to_string(void*) const {
613 return "!"s;
614}
612std::string Try_t::to_string(void* ud) const { 615std::string Try_t::to_string(void* ud) const {
613 auto info = reinterpret_cast<YueFormat*>(ud); 616 auto info = reinterpret_cast<YueFormat*>(ud);
614 str_list temp; 617 str_list temp;
618 temp.emplace_back("try"s);
619 if (omit) {
620 temp.back() += '!';
621 }
615 if (func.is<Exp_t>()) { 622 if (func.is<Exp_t>()) {
616 temp.emplace_back("try "s + func->to_string(ud)); 623 temp.back() += (" "s + func->to_string(ud));
617 } else { 624 } else {
618 temp.emplace_back("try"s);
619 info->pushScope(); 625 info->pushScope();
620 temp.emplace_back(func->to_string(ud)); 626 temp.emplace_back(func->to_string(ud));
621 if (temp.back().empty()) { 627 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)
383 AST_MEMBER(CatchBlock, &err, &block) 383 AST_MEMBER(CatchBlock, &err, &block)
384AST_END(CatchBlock) 384AST_END(CatchBlock)
385 385
386AST_LEAF(Omit)
387AST_END(Omit)
388
386AST_NODE(Try) 389AST_NODE(Try)
390 ast_ptr<false, Omit_t> omit;
387 ast_sel<true, Block_t, Exp_t> func; 391 ast_sel<true, Block_t, Exp_t> func;
388 ast_ptr<false, CatchBlock_t> catchBlock; 392 ast_ptr<false, CatchBlock_t> catchBlock;
389 AST_MEMBER(Try, &func, &catchBlock) 393 AST_MEMBER(Try, &omit, &func, &catchBlock)
390AST_END(Try) 394AST_END(Try)
391 395
392AST_NODE(Comprehension) 396AST_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<std::string> Metamethods = {
78 "close"s // Lua 5.4 78 "close"s // Lua 5.4
79}; 79};
80 80
81const std::string_view version = "0.28.5"sv; 81const std::string_view version = "0.28.6"sv;
82const std::string_view extension = "yue"sv; 82const std::string_view extension = "yue"sv;
83 83
84class CompileError : public std::logic_error { 84class CompileError : public std::logic_error {
@@ -165,12 +165,12 @@ public:
165 double compileTime = 0.0; 165 double compileTime = 0.0;
166 if (config.profiling) { 166 if (config.profiling) {
167 auto start = std::chrono::high_resolution_clock::now(); 167 auto start = std::chrono::high_resolution_clock::now();
168 _info = _parser.parse<File_t>(codes); 168 _info = _parser.parse<File_t>(codes, config.lax);
169 auto stop = std::chrono::high_resolution_clock::now(); 169 auto stop = std::chrono::high_resolution_clock::now();
170 std::chrono::duration<double> diff = stop - start; 170 std::chrono::duration<double> diff = stop - start;
171 parseTime = diff.count(); 171 parseTime = diff.count();
172 } else { 172 } else {
173 _info = _parser.parse<File_t>(codes); 173 _info = _parser.parse<File_t>(codes, config.lax);
174 } 174 }
175 std::unique_ptr<GlobalVars> globals; 175 std::unique_ptr<GlobalVars> globals;
176 std::unique_ptr<Options> options; 176 std::unique_ptr<Options> options;
@@ -1258,7 +1258,7 @@ private:
1258 1258
1259 template <class T> 1259 template <class T>
1260 ast_ptr<false, T> toAst(std::string_view codes, ast_node* parent) { 1260 ast_ptr<false, T> toAst(std::string_view codes, ast_node* parent) {
1261 auto res = _parser.parse<T>(std::string(codes)); 1261 auto res = _parser.parse<T>(std::string(codes), false);
1262 if (res.error) { 1262 if (res.error) {
1263 throw CompileError(res.error.value().msg, parent); 1263 throw CompileError(res.error.value().msg, parent);
1264 } 1264 }
@@ -2330,6 +2330,17 @@ private:
2330 out.back().insert(0, preDefine); 2330 out.back().insert(0, preDefine);
2331 return false; 2331 return false;
2332 } 2332 }
2333 case id<Try_t>(): {
2334 auto tryNode = static_cast<Try_t*>(value);
2335 if (tryNode->omit) {
2336 auto assignList = assignment->expList.get();
2337 std::string preDefine = getPreDefineLine(assignment);
2338 transformTry(tryNode, out, ExpUsage::Assignment, assignList);
2339 out.back().insert(0, preDefine);
2340 return false;
2341 }
2342 break;
2343 }
2333 case id<Switch_t>(): { 2344 case id<Switch_t>(): {
2334 auto switchNode = static_cast<Switch_t*>(value); 2345 auto switchNode = static_cast<Switch_t*>(value);
2335 auto assignList = assignment->expList.get(); 2346 auto assignList = assignment->expList.get();
@@ -4272,12 +4283,22 @@ private:
4272 4283
4273 std::optional<std::pair<std::string, str_list>> upValueFuncFromExp(Exp_t* exp, str_list* ensureArgListInTheEnd, bool blockRewrite) { 4284 std::optional<std::pair<std::string, str_list>> upValueFuncFromExp(Exp_t* exp, str_list* ensureArgListInTheEnd, bool blockRewrite) {
4274 if (checkUpValueFuncAvailable(exp)) { 4285 if (checkUpValueFuncAvailable(exp)) {
4286 auto block = exp->new_ptr<Block_t>();
4287 if (auto sVal = simpleSingleValueFrom(exp)) {
4288 if (auto doNode = sVal->value.as<Do_t>()) {
4289 if (auto blk = doNode->body->content.as<Block_t>()) {
4290 block->statements.dup(blk->statements);
4291 } else {
4292 block->statements.push_back(doNode->body->content.to<Statement_t>());
4293 }
4294 return getUpValueFuncFromBlock(block, ensureArgListInTheEnd, false, blockRewrite);
4295 }
4296 }
4275 auto returnNode = exp->new_ptr<Return_t>(); 4297 auto returnNode = exp->new_ptr<Return_t>();
4276 returnNode->explicitReturn = false; 4298 returnNode->explicitReturn = false;
4277 auto returnList = exp->new_ptr<ExpListLow_t>(); 4299 auto returnList = exp->new_ptr<ExpListLow_t>();
4278 returnList->exprs.push_back(exp); 4300 returnList->exprs.push_back(exp);
4279 returnNode->valueList.set(returnList); 4301 returnNode->valueList.set(returnList);
4280 auto block = exp->new_ptr<Block_t>();
4281 auto stmt = exp->new_ptr<Statement_t>(); 4302 auto stmt = exp->new_ptr<Statement_t>();
4282 stmt->content.set(returnNode); 4303 stmt->content.set(returnNode);
4283 block->statements.push_back(stmt); 4304 block->statements.push_back(stmt);
@@ -4799,11 +4820,7 @@ private:
4799 auto newBody = x->new_ptr<Body_t>(); 4820 auto newBody = x->new_ptr<Body_t>();
4800 newBody->content.set(followingBlock); 4821 newBody->content.set(followingBlock);
4801 { 4822 {
4802 auto doNode = x->new_ptr<Do_t>(); 4823 if (auto result = upValueFuncFromBlock(followingBlock.get(), &argNames, false, true)) {
4803 doNode->body.set(newBody);
4804 auto simpleValue = x->new_ptr<SimpleValue_t>();
4805 simpleValue->value.set(doNode);
4806 if (auto result = upValueFuncFromExp(newExp(simpleValue, x), &argNames, true)) {
4807 auto [funcName, args] = std::move(*result); 4824 auto [funcName, args] = std::move(*result);
4808 str_list finalArgs; 4825 str_list finalArgs;
4809 for (const auto& arg : args) { 4826 for (const auto& arg : args) {
@@ -4811,9 +4828,13 @@ private:
4811 finalArgs.push_back(arg); 4828 finalArgs.push_back(arg);
4812 } 4829 }
4813 } 4830 }
4814 newBlock->statements.push_back(toAst<Statement_t>(funcName + ' ' + join(finalArgs, ","sv), x)); 4831 newBlock->statements.push_back(toAst<Statement_t>(funcName + ' ' + (finalArgs.empty() ? "nil"s : join(finalArgs, ","sv)), x));
4815 auto sVal = singleValueFrom(static_cast<Statement_t*>(newBlock->statements.back())->content.to<ExpListAssign_t>()->expList); 4832 auto sVal = singleValueFrom(static_cast<Statement_t*>(newBlock->statements.back())->content.to<ExpListAssign_t>()->expList);
4816 ast_to<InvokeArgs_t>(sVal->item.to<ChainValue_t>()->items.back())->args.dup(newInvoke->args); 4833 auto invokArgs = ast_to<InvokeArgs_t>(sVal->item.to<ChainValue_t>()->items.back());
4834 if (finalArgs.empty()) {
4835 invokArgs->args.clear();
4836 }
4837 invokArgs->args.dup(newInvoke->args);
4817 transformBlock(newBlock, out, usage, assignList, isRoot); 4838 transformBlock(newBlock, out, usage, assignList, isRoot);
4818 return; 4839 return;
4819 } 4840 }
@@ -5093,36 +5114,45 @@ private:
5093 if (!nodes.empty()) { 5114 if (!nodes.empty()) {
5094 str_list temp; 5115 str_list temp;
5095 for (auto node : nodes) { 5116 for (auto node : nodes) {
5096 currentScope().lastStatement = (node == nodes.back()) && currentScope().mode == GlobalMode::None; 5117 auto transformNode = [&]() {
5097 transformStatement(static_cast<Statement_t*>(node), temp); 5118 currentScope().lastStatement = (node == nodes.back()) && currentScope().mode == GlobalMode::None;
5098 if (isRoot && !_rootDefs.empty()) { 5119 transformStatement(static_cast<Statement_t*>(node), temp);
5099 auto last = std::move(temp.back()); 5120 if (isRoot && !_rootDefs.empty()) {
5100 temp.pop_back(); 5121 auto last = std::move(temp.back());
5101 temp.insert(temp.end(), _rootDefs.begin(), _rootDefs.end()); 5122 temp.pop_back();
5102 _rootDefs.clear(); 5123 temp.insert(temp.end(), _rootDefs.begin(), _rootDefs.end());
5103 temp.push_back(std::move(last)); 5124 _rootDefs.clear();
5104 } 5125 temp.push_back(std::move(last));
5105 if (!temp.empty() && _parser.startWith<StatementSep_t>(temp.back())) { 5126 }
5106 auto rit = ++temp.rbegin(); 5127 if (!temp.empty() && _parser.startWith<StatementSep_t>(temp.back())) {
5107 if (rit != temp.rend() && !rit->empty()) { 5128 auto rit = ++temp.rbegin();
5108 auto index = std::string::npos; 5129 if (rit != temp.rend() && !rit->empty()) {
5109 if (_config.reserveLineNumber) { 5130 auto index = std::string::npos;
5110 index = rit->rfind(" -- "sv); 5131 if (_config.reserveLineNumber) {
5111 } else { 5132 index = rit->rfind(" -- "sv);
5112 index = rit->find_last_not_of('\n'); 5133 } else {
5113 if (index != std::string::npos) index++; 5134 index = rit->find_last_not_of('\n');
5114 } 5135 if (index != std::string::npos) index++;
5115 if (index != std::string::npos) {
5116 auto ending = rit->substr(0, index);
5117 auto ind = ending.find_last_of(" \t\n"sv);
5118 if (ind != std::string::npos) {
5119 ending = ending.substr(ind + 1);
5120 } 5136 }
5121 if (LuaKeywords.find(ending) == LuaKeywords.end()) { 5137 if (index != std::string::npos) {
5122 rit->insert(index, ";"sv); 5138 auto ending = rit->substr(0, index);
5139 auto ind = ending.find_last_of(" \t\n"sv);
5140 if (ind != std::string::npos) {
5141 ending = ending.substr(ind + 1);
5142 }
5143 if (LuaKeywords.find(ending) == LuaKeywords.end()) {
5144 rit->insert(index, ";"sv);
5145 }
5123 } 5146 }
5124 } 5147 }
5125 } 5148 }
5149 };
5150 if (_config.lax) {
5151 try {
5152 transformNode();
5153 } catch (const CompileError&) { }
5154 } else {
5155 transformNode();
5126 } 5156 }
5127 } 5157 }
5128 out.push_back(join(temp)); 5158 out.push_back(join(temp));
@@ -6193,7 +6223,7 @@ private:
6193 case id<ColonChainItem_t>(): 6223 case id<ColonChainItem_t>():
6194 case id<Exp_t>(): 6224 case id<Exp_t>():
6195 if (_withVars.empty()) { 6225 if (_withVars.empty()) {
6196 throw CompileError("short dot/colon and indexing syntax must be called within a with block"sv, x); 6226 throw CompileError("short dot/colon/indexing syntax must be called within a with block"sv, x);
6197 } else { 6227 } else {
6198 temp.push_back(_withVars.top()); 6228 temp.push_back(_withVars.top());
6199 } 6229 }
@@ -6714,14 +6744,14 @@ private:
6714 } else { 6744 } else {
6715 if (!codes.empty()) { 6745 if (!codes.empty()) {
6716 if (isBlock) { 6746 if (isBlock) {
6717 info = _parser.parse<BlockEnd_t>(codes); 6747 info = _parser.parse<BlockEnd_t>(codes, false);
6718 if (info.error) { 6748 if (info.error) {
6719 throw CompileError("failed to expand macro as block: "s + info.error.value().msg, x); 6749 throw CompileError("failed to expand macro as block: "s + info.error.value().msg, x);
6720 } 6750 }
6721 } else { 6751 } else {
6722 info = _parser.parse<Exp_t>(codes); 6752 info = _parser.parse<Exp_t>(codes, false);
6723 if (!info.node && allowBlockMacroReturn) { 6753 if (!info.node && allowBlockMacroReturn) {
6724 info = _parser.parse<BlockEnd_t>(codes); 6754 info = _parser.parse<BlockEnd_t>(codes, false);
6725 if (info.error) { 6755 if (info.error) {
6726 throw CompileError("failed to expand macro as expr or block: "s + info.error.value().msg, x); 6756 throw CompileError("failed to expand macro as expr or block: "s + info.error.value().msg, x);
6727 } 6757 }
@@ -10023,8 +10053,47 @@ private:
10023 out.push_back(join(temp)); 10053 out.push_back(join(temp));
10024 } 10054 }
10025 10055
10026 void transformTry(Try_t* tryNode, str_list& out, ExpUsage usage) { 10056 void transformTry(Try_t* tryNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) {
10027 auto x = tryNode; 10057 auto x = tryNode;
10058 if (tryNode->omit && usage == ExpUsage::Assignment) {
10059 str_list rets;
10060 pushScope();
10061 auto okVar = getUnusedName("_ok_"sv);
10062 for (size_t i = 0; i < assignList->exprs.size(); i++) {
10063 auto retVar = getUnusedName("_ret_"sv);
10064 rets.emplace_back(retVar);
10065 addToScope(retVar);
10066 }
10067 popScope();
10068 auto varList = join(rets, ","sv);
10069 auto ifNode = toAst<If_t>("if "s + okVar + ',' + varList + ":=try nil then "s + varList, x);
10070 auto exp = ast_to<IfCond_t>(ifNode->nodes.front())->assignment->assign->values.front();
10071 auto sVal = simpleSingleValueFrom(exp);
10072 auto newTry = sVal->value.to<Try_t>();
10073 newTry->func.set(tryNode->func);
10074 newTry->catchBlock.set(tryNode->catchBlock);
10075 auto assignment = x->new_ptr<ExpListAssign_t>();
10076 assignment->expList.set(assignList);
10077 auto assign = x->new_ptr<Assign_t>();
10078 assign->values.push_back(ifNode);
10079 assignment->action.set(assign);
10080 transformAssignment(assignment, out);
10081 return;
10082 }
10083 if (tryNode->omit && usage != ExpUsage::Common) {
10084 auto okVar = getUnusedName("_ok_"sv);
10085 auto code = "do\n\t"s + okVar + ", ... = try nil\n\t... if "s + okVar;
10086 auto doNode = toAst<Do_t>(code, x);
10087 auto block = doNode->body->content.to<Block_t>();
10088 auto asmt = static_cast<Statement_t*>(block->statements.front())->content.to<ExpListAssign_t>();
10089 auto assign = asmt->action.to<Assign_t>();
10090 auto sVal = simpleSingleValueFrom(assign->values.back());
10091 auto newTry = sVal->value.to<Try_t>();
10092 newTry->func.set(tryNode->func);
10093 newTry->catchBlock.set(tryNode->catchBlock);
10094 transformDo(doNode, out, usage);
10095 return;
10096 }
10028 ast_ptr<true, Exp_t> errHandler; 10097 ast_ptr<true, Exp_t> errHandler;
10029 if (tryNode->catchBlock) { 10098 if (tryNode->catchBlock) {
10030 auto catchBlock = tryNode->catchBlock.get(); 10099 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 {
31 bool reserveLineNumber = true; 31 bool reserveLineNumber = true;
32 bool useSpaceOverTab = false; 32 bool useSpaceOverTab = false;
33 bool reserveComment = false; 33 bool reserveComment = false;
34 bool lax = false;
34 // internal options 35 // internal options
35 bool exporting = false; 36 bool exporting = false;
36 bool profiling = false; 37 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() {
332 Exp; 332 Exp;
333 import_tab_list = import_tab_item >> *(space >> ',' >> space >> import_tab_item); 333 import_tab_list = import_tab_item >> *(space >> ',' >> space >> import_tab_item);
334 import_tab_line = ( 334 import_tab_line = (
335 push_indent_match >> (space >> import_tab_list >> pop_indent | pop_indent) 335 push_indent_match >> ensure(space >> import_tab_list, pop_indent)
336 ) | space; 336 ) | space;
337 import_tab_lines = space_break >> import_tab_line >> *(-(space >> ',') >> space_break >> import_tab_line) >> -(space >> ','); 337 import_tab_lines = space_break >> import_tab_line >> *(-(space >> ',') >> space_break >> import_tab_line) >> -(space >> ',');
338 import_tab_key_value = key_value | ':' >> MacroName | MacroNamePair | ImportAllMacro; 338 import_tab_key_value = key_value | ':' >> MacroName | MacroNamePair | ImportAllMacro;
@@ -501,8 +501,9 @@ YueParser::YueParser() {
501 return true; 501 return true;
502 }); 502 });
503 503
504 Omit = expr('!');
504 CatchBlock = line_break >> *space_break >> check_indent_match >> space >> key("catch") >> space >> Variable >> space >> in_block; 505 CatchBlock = line_break >> *space_break >> check_indent_match >> space >> key("catch") >> space >> Variable >> space >> in_block;
505 Try = key("try") >> space >> (in_block | Exp) >> -CatchBlock; 506 Try = key("try") >> -Omit >> space >> (in_block | Exp) >> -CatchBlock;
506 507
507 list_value = 508 list_value =
508 and_( 509 and_(
@@ -667,7 +668,7 @@ YueParser::YueParser() {
667 fn_args_value_list = Exp >> *(space >> ',' >> space >> Exp); 668 fn_args_value_list = Exp >> *(space >> ',' >> space >> Exp);
668 669
669 fn_args_lit_line = ( 670 fn_args_lit_line = (
670 push_indent_match >> (space >> fn_args_value_list >> pop_indent | pop_indent) 671 push_indent_match >> ensure(space >> fn_args_value_list, pop_indent)
671 ) | ( 672 ) | (
672 space 673 space
673 ); 674 );
@@ -875,7 +876,7 @@ YueParser::YueParser() {
875 fn_arg_def_list = FnArgDef >> *(space >> ',' >> space >> FnArgDef); 876 fn_arg_def_list = FnArgDef >> *(space >> ',' >> space >> FnArgDef);
876 877
877 fn_arg_def_lit_line = ( 878 fn_arg_def_lit_line = (
878 push_indent_match >> (space >> fn_arg_def_list >> pop_indent | pop_indent) 879 push_indent_match >> ensure(space >> fn_arg_def_list, pop_indent)
879 ) | ( 880 ) | (
880 space 881 space
881 ); 882 );
@@ -1030,11 +1031,16 @@ YueParser::YueParser() {
1030 empty_line_break | 1031 empty_line_break |
1031 advance_match >> ensure(space >> (indentation_error | Statement), pop_indent) 1032 advance_match >> ensure(space >> (indentation_error | Statement), pop_indent)
1032 ); 1033 );
1033 Block = Seperator >> line >> *(+line_break >> line); 1034 Block = Seperator >> (pl::user(true_(), [](const item_t& item) {
1035 State* st = reinterpret_cast<State*>(item.user_data);
1036 return st->lax;
1037 }) >> lax_line >> *(+line_break >> lax_line) | line >> *(+line_break >> line));
1034 1038
1035 shebang = "#!" >> *(not_(stop) >> any_char); 1039 shebang = "#!" >> *(not_(stop) >> any_char);
1036 BlockEnd = Block >> white >> stop; 1040 BlockEnd = Block >> white >> stop;
1037 File = -shebang >> -Block >> white >> stop; 1041 File = -shebang >> -Block >> white >> stop;
1042
1043 lax_line = advance_match >> ensure(*(not_(stop) >> any()), pop_indent) | line >> and_(stop) | check_indent_match >> *(not_(stop) >> any());
1038} 1044}
1039// clang-format on 1045// clang-format on
1040 1046
@@ -1064,7 +1070,7 @@ bool YueParser::startWith(std::string_view codes, rule& r) {
1064 return true; 1070 return true;
1065} 1071}
1066 1072
1067ParseInfo YueParser::parse(std::string_view codes, rule& r) { 1073ParseInfo YueParser::parse(std::string_view codes, rule& r, bool lax) {
1068 ParseInfo res; 1074 ParseInfo res;
1069 if (codes.substr(0, 3) == "\xEF\xBB\xBF"sv) { 1075 if (codes.substr(0, 3) == "\xEF\xBB\xBF"sv) {
1070 codes = codes.substr(3); 1076 codes = codes.substr(3);
@@ -1082,6 +1088,7 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) {
1082 error_list errors; 1088 error_list errors;
1083 try { 1089 try {
1084 State state; 1090 State state;
1091 state.lax = lax;
1085 res.node.set(::yue::parse(*(res.codes), r, errors, &state)); 1092 res.node.set(::yue::parse(*(res.codes), r, errors, &state));
1086 if (state.exportCount > 0) { 1093 if (state.exportCount > 0) {
1087 int index = 0; 1094 int index = 0;
@@ -1119,10 +1126,10 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) {
1119 return res; 1126 return res;
1120} 1127}
1121 1128
1122ParseInfo YueParser::parse(std::string_view astName, std::string_view codes) { 1129ParseInfo YueParser::parse(std::string_view astName, std::string_view codes, bool lax) {
1123 auto it = _rules.find(astName); 1130 auto it = _rules.find(astName);
1124 if (it != _rules.end()) { 1131 if (it != _rules.end()) {
1125 return parse(codes, *it->second); 1132 return parse(codes, *it->second, lax);
1126 } 1133 }
1127 return {}; 1134 return {};
1128} 1135}
@@ -1131,7 +1138,7 @@ bool YueParser::match(std::string_view astName, std::string_view codes) {
1131 auto it = _rules.find(astName); 1138 auto it = _rules.find(astName);
1132 if (it != _rules.end()) { 1139 if (it != _rules.end()) {
1133 auto rEnd = rule(*it->second >> eof()); 1140 auto rEnd = rule(*it->second >> eof());
1134 return parse(codes, rEnd).node; 1141 return parse(codes, rEnd, false).node;
1135 } 1142 }
1136 return false; 1143 return false;
1137} 1144}
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<std::string> Keywords;
74class YueParser { 74class YueParser {
75public: 75public:
76 template <class AST> 76 template <class AST>
77 ParseInfo parse(std::string_view codes) { 77 ParseInfo parse(std::string_view codes, bool lax) {
78 return parse(codes, getRule<AST>()); 78 return parse(codes, getRule<AST>(), lax);
79 } 79 }
80 80
81 ParseInfo parse(std::string_view astName, std::string_view codes); 81 ParseInfo parse(std::string_view astName, std::string_view codes, bool lax);
82 82
83 template <class AST> 83 template <class AST>
84 bool match(std::string_view codes) { 84 bool match(std::string_view codes) {
85 auto rEnd = rule(getRule<AST>() >> eof()); 85 auto rEnd = rule(getRule<AST>() >> eof());
86 return parse(codes, rEnd).node; 86 return parse(codes, rEnd, false).node;
87 } 87 }
88 88
89 bool match(std::string_view astName, std::string_view codes); 89 bool match(std::string_view astName, std::string_view codes);
@@ -102,13 +102,14 @@ public:
102 102
103protected: 103protected:
104 YueParser(); 104 YueParser();
105 ParseInfo parse(std::string_view codes, rule& r); 105 ParseInfo parse(std::string_view codes, rule& r, bool lax);
106 bool startWith(std::string_view codes, rule& r); 106 bool startWith(std::string_view codes, rule& r);
107 107
108 struct State { 108 struct State {
109 State() { 109 State() {
110 indents.push(0); 110 indents.push(0);
111 } 111 }
112 bool lax = false;
112 bool exportDefault = false; 113 bool exportDefault = false;
113 bool exportMacro = false; 114 bool exportMacro = false;
114 bool exportMetatable = false; 115 bool exportMetatable = false;
@@ -287,6 +288,7 @@ private:
287 NONE_AST_RULE(yue_line_comment); 288 NONE_AST_RULE(yue_line_comment);
288 NONE_AST_RULE(line); 289 NONE_AST_RULE(line);
289 NONE_AST_RULE(shebang); 290 NONE_AST_RULE(shebang);
291 NONE_AST_RULE(lax_line);
290 292
291 AST_RULE(Num); 293 AST_RULE(Num);
292 AST_RULE(Name); 294 AST_RULE(Name);
@@ -345,6 +347,7 @@ private:
345 AST_RULE(ForEach); 347 AST_RULE(ForEach);
346 AST_RULE(Do); 348 AST_RULE(Do);
347 AST_RULE(CatchBlock); 349 AST_RULE(CatchBlock);
350 AST_RULE(Omit);
348 AST_RULE(Try); 351 AST_RULE(Try);
349 AST_RULE(Comprehension); 352 AST_RULE(Comprehension);
350 AST_RULE(CompValue); 353 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) {
93 config.useSpaceOverTab = lua_toboolean(L, -1) != 0; 93 config.useSpaceOverTab = lua_toboolean(L, -1) != 0;
94 } 94 }
95 lua_pop(L, 1); 95 lua_pop(L, 1);
96 lua_pushliteral(L, "lax");
97 lua_gettable(L, -2);
98 if (lua_isboolean(L, -1) != 0) {
99 config.lax = lua_toboolean(L, -1) != 0;
100 }
101 lua_pop(L, 1);
96 lua_pushliteral(L, "options"); 102 lua_pushliteral(L, "options");
97 lua_gettable(L, -2); 103 lua_gettable(L, -2);
98 if (lua_istable(L, -1) != 0) { 104 if (lua_istable(L, -1) != 0) {
@@ -180,7 +186,7 @@ static int yueformat(lua_State* L) {
180 tabSize = static_cast<int>(luaL_checkinteger(L, 2)); 186 tabSize = static_cast<int>(luaL_checkinteger(L, 2));
181 } 187 }
182 std::string_view codes(input, len); 188 std::string_view codes(input, len);
183 auto info = yue::YueParser::shared().parse<yue::File_t>(codes); 189 auto info = yue::YueParser::shared().parse<yue::File_t>(codes, false);
184 if (info.error) { 190 if (info.error) {
185 const auto& error = info.error.value(); 191 const auto& error = info.error.value();
186 if (!info.codes) { 192 if (!info.codes) {
@@ -282,8 +288,13 @@ static int yuetoast(lua_State* L) {
282 ruleName = {name, nameSize}; 288 ruleName = {name, nameSize};
283 } 289 }
284 } 290 }
291 bool lax = false;
292 if (!lua_isnoneornil(L, 4)) {
293 luaL_checktype(L, 4, LUA_TBOOLEAN);
294 lax = lua_toboolean(L, 4) != 0;
295 }
285 auto& yueParser = yue::YueParser::shared(); 296 auto& yueParser = yue::YueParser::shared();
286 auto info = ruleName.empty() ? yueParser.parse<yue::File_t>({input, size}) : yueParser.parse(ruleName, {input, size}); 297 auto info = ruleName.empty() ? yueParser.parse<yue::File_t>({input, size}, lax) : yueParser.parse(ruleName, {input, size}, lax);
287 if (!info.error) { 298 if (!info.error) {
288 lua_createtable(L, 0, 0); 299 lua_createtable(L, 0, 0);
289 int tableIndex = lua_gettop(L); 300 int tableIndex = lua_gettop(L);