From f52f54f953feb6bb36a279c42882d7a744695271 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Thu, 23 Mar 2023 09:50:56 +0800 Subject: add yue.check function. --- src/yuescript/stacktraceplus.h | 2 +- src/yuescript/yue_compiler.cpp | 405 ++++++++++++++++++++++++----------------- src/yuescript/yue_compiler.h | 22 ++- src/yuescript/yue_parser.cpp | 63 ++++--- src/yuescript/yue_parser.h | 46 ++--- src/yuescript/yuescript.cpp | 151 ++++++++++----- src/yuescript/yuescript.h | 7 +- 7 files changed, 423 insertions(+), 273 deletions(-) (limited to 'src') diff --git a/src/yuescript/stacktraceplus.h b/src/yuescript/stacktraceplus.h index 83460ef..4b6f370 100644 --- a/src/yuescript/stacktraceplus.h +++ b/src/yuescript/stacktraceplus.h @@ -345,7 +345,7 @@ local function getYueLineNumber(fname, line) if source then local current, target = 1, tonumber(line) local findLine = line - for lineCode in source:gmatch("([^\n]*)\n") do + for lineCode in source:gmatch("[^\n\r]*") do local num = lineCode:match("--%s*(%d+)%s*$") if num then findLine = num diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 3a9e584..07c0e70 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -52,13 +52,12 @@ namespace yue { #define _DEFER(code, line) std::shared_ptr _defer_##line(nullptr, [&](auto) { code; }) #define DEFER(code) _DEFER(code, __LINE__) -#define YUEE(msg, node) throw std::logic_error( \ - _info.errorMessage( \ - "[File] "s + __FILE__ \ - + ",\n[Func] "s + __FUNCTION__ \ - + ",\n[Line] "s + std::to_string(__LINE__) \ - + ",\n[Error] "s + msg, \ - node)) +#define YUEE(msg, node) throw CompileError( \ + "[File] "s + __FILE__ \ + + ",\n[Func] "s + __FUNCTION__ \ + + ",\n[Line] "s + std::to_string(__LINE__) \ + + ",\n[Error] "s + msg, \ + node) typedef std::list str_list; @@ -73,9 +72,51 @@ static std::unordered_set Metamethods = { "close"s // Lua 5.4 }; -const std::string_view version = "0.15.28"sv; +const std::string_view version = "0.15.29"sv; const std::string_view extension = "yue"sv; +class CompileError : public std::logic_error { +public: + explicit CompileError(std::string_view msg, const input_range* range) + : std::logic_error(std::string(msg)) + , line(range->m_begin.m_line) + , col(range->m_begin.m_col) { } + + int line; + int col; +}; + +CompileInfo::CompileInfo( + std::string&& codes, + std::optional&& error, + std::unique_ptr&& globals, + std::unique_ptr&& options, + double parseTime, + double compileTime) + : codes(std::move(codes)) + , error(std::move(error)) + , globals(std::move(globals)) + , options(std::move(options)) + , parseTime(parseTime) + , compileTime(compileTime) { } + +CompileInfo::CompileInfo(CompileInfo&& other) + : codes(std::move(other.codes)) + , error(std::move(other.error)) + , globals(std::move(other.globals)) + , options(std::move(other.options)) + , parseTime(other.parseTime) + , compileTime(other.compileTime) { } + +void CompileInfo::operator=(CompileInfo&& other) { + codes = std::move(other.codes); + error = std::move(other.error); + globals = std::move(other.globals); + options = std::move(other.options); + parseTime = other.parseTime; + compileTime = other.compileTime; +} + class YueCompilerImpl { public: #ifndef YUE_NO_MACRO @@ -162,7 +203,7 @@ public: if (exportNode->target.is()) break; } default: - throw std::logic_error(_info.errorMessage("macro exporting module only accepts macro definition, macro importing and macro expansion in place", stmt)); + throw CompileError("macro exporting module only accepts macro definition, macro importing and macro expansion in place"sv, stmt); break; } } @@ -200,7 +241,7 @@ public: noLabel = false; BLOCK_END if (noLabel) { - throw std::logic_error(_info.errorMessage("no visible label '"s + gotoNode.label + "' for "s, gotoNode.ptr->label)); + throw CompileError("no visible label '"s + gotoNode.label + "' for "s, gotoNode.ptr->label); } } } @@ -241,12 +282,42 @@ public: } } #endif // YUE_NO_MACRO - return {std::move(out.back()), Empty, std::move(globals), std::move(options), parseTime, compileTime}; - } catch (const std::logic_error& error) { - return {Empty, error.what(), std::move(globals), std::move(options), parseTime, compileTime}; + return {std::move(out.back()), std::nullopt, std::move(globals), std::move(options), parseTime, compileTime}; + } catch (const CompileError& error) { + auto displayMessage = _info.errorMessage(error.what(), error.line, error.col); + return { + std::string(), + CompileInfo::Error{ + error.what(), + error.line, error.col, + displayMessage}, + std::move(globals), + std::move(options), + parseTime, compileTime}; } } else { - return {Empty, std::move(_info.error), std::move(globals), std::move(options), parseTime, compileTime}; + const auto& error = _info.error.value(); + if (!_info.codes) { + return { + std::string(), + CompileInfo::Error{ + error.msg, + error.line, error.col, + ""}, + std::move(globals), + std::move(options), + parseTime, compileTime}; + } + auto displayMessage = _info.errorMessage(error.msg, error.line, error.col); + return { + std::string(), + CompileInfo::Error{ + error.msg, + error.line, error.col, + displayMessage}, + std::move(globals), + std::move(options), + parseTime, compileTime}; } } @@ -483,7 +554,7 @@ private: void checkConst(const std::string& name, ast_node* x) const { if (isConst(name)) { - throw std::logic_error(_info.errorMessage("attempt to assign to const variable '"s + name + '\'', x)); + throw CompileError("attempt to assign to const variable '"s + name + '\'', x); } } @@ -503,7 +574,7 @@ private: } void addGlobalVar(const std::string& name, ast_node* x) { - if (isLocal(name)) throw std::logic_error(_info.errorMessage("can not declare a local variable to be global"sv, x)); + if (isLocal(name)) throw CompileError("can not declare a local variable to be global"sv, x); auto& scope = _scopes.back(); if (!scope.globals) { scope.globals = std::make_unique>(); @@ -1051,7 +1122,7 @@ private: for (auto exp_ : expList->exprs.objects()) { Exp_t* exp = static_cast(exp_); if (!isAssignable(exp)) { - throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, exp)); + throw CompileError("left hand expression is not assignable"sv, exp); } } } @@ -1120,7 +1191,7 @@ private: void checkMetamethod(const std::string& name, ast_node* x) { if (Metamethods.find(name) == Metamethods.end()) { - throw std::logic_error(_info.errorMessage("invalid metamethod name"sv, x)); + throw CompileError("invalid metamethod name"sv, x); } int target = getLuaTarget(x); switch (target) { @@ -1128,33 +1199,30 @@ private: case 502: goto metamethod52; case 503: { if (name == "ipairs"sv) { - throw std::logic_error(_info.errorMessage("metamethod is deprecated since Lua 5.3"sv, x)); + throw CompileError("metamethod is deprecated since Lua 5.3"sv, x); } goto metamethod53; } case 504: { if (name == "ipairs"sv) { - throw std::logic_error(_info.errorMessage("metamethod is not supported since Lua 5.4"sv, x)); + throw CompileError("metamethod is not supported since Lua 5.4"sv, x); } goto metamethod54; } } - metamethod51: + metamethod51: if (name == "pairs"sv || name == "ipairs"sv) { - throw std::logic_error(_info.errorMessage("metamethod is not supported until Lua 5.2"sv, x)); + throw CompileError("metamethod is not supported until Lua 5.2"sv, x); } - metamethod52: - if (name == "name"sv || name == "idiv"sv || - name == "band"sv || name == "bor"sv || - name == "bxor"sv || name == "bnot"sv || - name == "shl"sv || name == "shr"sv) { - throw std::logic_error(_info.errorMessage("metamethod is not supported until Lua 5.3"sv, x)); + metamethod52: + if (name == "name"sv || name == "idiv"sv || name == "band"sv || name == "bor"sv || name == "bxor"sv || name == "bnot"sv || name == "shl"sv || name == "shr"sv) { + throw CompileError("metamethod is not supported until Lua 5.3"sv, x); } - metamethod53: + metamethod53: if (name == "close"sv) { - throw std::logic_error(_info.errorMessage("metamethod is not supported until Lua 5.4"sv, x)); + throw CompileError("metamethod is not supported until Lua 5.4"sv, x); } - metamethod54: + metamethod54: return; } @@ -1236,11 +1304,11 @@ private: return; } case id(): { - throw std::logic_error(_info.errorMessage("while-loop line decorator is not supported here"sv, appendix->item.get())); + throw CompileError("while-loop line decorator is not supported here"sv, appendix->item.get()); break; } case id(): { - throw std::logic_error(_info.errorMessage("for-loop line decorator is not supported here"sv, appendix->item.get())); + throw CompileError("for-loop line decorator is not supported here"sv, appendix->item.get()); break; } default: YUEE("AST node mismatch", appendix->item.get()); break; @@ -1249,10 +1317,10 @@ private: auto appendix = statement->appendix->item.get(); switch (statement->content->getId()) { case id(): - throw std::logic_error(_info.errorMessage("loop line decorator can not be used in a return statement"sv, appendix)); + throw CompileError("loop line decorator can not be used in a return statement"sv, appendix); break; case id(): - throw std::logic_error(_info.errorMessage("loop line decorator can not be used in a break-loop statement"sv, appendix)); + throw CompileError("loop line decorator can not be used in a break-loop statement"sv, appendix); break; } } @@ -1343,7 +1411,7 @@ private: case id(): transformGoto(static_cast(content), out); break; case id(): transformShortTabAppending(static_cast(content), out); break; case id(): transformLocalAttrib(static_cast(content), out); break; - case id(): throw std::logic_error(_info.errorMessage("pipe chain must be following a value"sv, x)); break; + case id(): throw CompileError("pipe chain must be following a value"sv, x); break; case id(): { auto expListAssign = static_cast(content); if (expListAssign->action) { @@ -1388,7 +1456,7 @@ private: break; } } - throw std::logic_error(_info.errorMessage("unexpected expression"sv, expList)); + throw CompileError("unexpected expression"sv, expList); } break; } @@ -1478,7 +1546,7 @@ private: BLOCK_END } } else { - throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, exp)); + throw CompileError("left hand expression is not assignable"sv, exp); } } return defs; @@ -1558,7 +1626,7 @@ private: for (auto& destruct : info.destructures) { for (auto& item : destruct.items) { if (item.targetVar.empty()) { - throw std::logic_error(_info.errorMessage("can only declare variable as const"sv, item.target)); + throw CompileError("can only declare variable as const"sv, item.target); } markVarConst(item.targetVar); } @@ -1580,7 +1648,7 @@ private: } else { _buf << "only one right value expected, got "sv << values.size(); } - throw std::logic_error(_info.errorMessage(clearBuf(), values.front())); + throw CompileError(clearBuf(), values.front()); } bool checkValuesLater = false; if (exprs.size() > values.size()) { @@ -1595,7 +1663,7 @@ private: auto value = singleValueFrom(values.back()); if (!value) { _buf << exprs.size() << " right values expected, got "sv << values.size(); - throw std::logic_error(_info.errorMessage(clearBuf(), values.front())); + throw CompileError(clearBuf(), values.front()); } if (auto val = value->item.as()) { switch (val->value->getId()) { @@ -1611,7 +1679,7 @@ private: auto chainValue = value->item.as(); if (!chainValue || !ast_is(chainValue->items.back())) { _buf << exprs.size() << " right values expected, got "sv << values.size(); - throw std::logic_error(_info.errorMessage(clearBuf(), values.front())); + throw CompileError(clearBuf(), values.front()); } auto newAssign = assign->new_ptr(); newAssign->values.dup(assign->values); @@ -1695,7 +1763,7 @@ private: tmpChain->items.pop_back(); if (tmpChain->items.empty()) { if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); + throw CompileError("short dot/colon syntax must be called within a with block"sv, x); } else { args.push_back(_withVars.top()); } @@ -1703,7 +1771,7 @@ private: transformExp(newExp(tmpChain, tmpChain), args, ExpUsage::Closure); } if (vit == values.end()) { - throw std::logic_error(_info.errorMessage("right value missing"sv, values.front())); + throw CompileError("right value missing"sv, values.front()); } transformAssignItem(*vit, args); _buf << indent() << globalVar("setmetatable"sv, x) << '(' << join(args, ", "sv) << ')' << nll(x); @@ -1714,7 +1782,7 @@ private: tmpChain->items.pop_back(); if (tmpChain->items.empty()) { if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short table appending must be called within a with block"sv, x)); + throw CompileError("short table appending must be called within a with block"sv, x); } else { tmpChain->items.push_back(toAst(_withVars.top(), chainValue)); } @@ -1738,7 +1806,7 @@ private: newAssignment->expList.set(toAst(varName + "[#"s + varName + "+1]"s, x)); auto assign = x->new_ptr(); if (vit == values.end()) { - throw std::logic_error(_info.errorMessage("right value missing"sv, values.front())); + throw CompileError("right value missing"sv, values.front()); } assign->values.push_back(*vit); newAssignment->action.set(assign); @@ -2176,7 +2244,7 @@ private: switch (node->getId()) { case id(): { auto item = singleValueFrom(node)->item.get(); - if (!item) throw std::logic_error(_info.errorMessage("invalid destructure value"sv, node)); + if (!item) throw CompileError("invalid destructure value"sv, node); auto tbA = item->getByPath(); if (tbA) { tableItems = &tbA->values.objects(); @@ -2208,7 +2276,7 @@ private: } default: YUEE("AST node mismatch", node); break; } - if (!tableItems) throw std::logic_error(_info.errorMessage("invalid destructure value"sv, node)); + if (!tableItems) throw CompileError("invalid destructure value"sv, node); std::list pairs; int index = 0; auto subMetaDestruct = node->new_ptr(); @@ -2223,7 +2291,7 @@ private: } ++index; if (!isAssignable(static_cast(pair))) { - throw std::logic_error(_info.errorMessage("can't destructure value"sv, pair)); + throw CompileError("can't destructure value"sv, pair); } auto value = singleValueFrom(pair); auto item = value->item.get(); @@ -2231,7 +2299,7 @@ private: auto subPairs = destructFromExp(pair, optional); if (!subPairs.empty()) { if (defVal) { - throw std::logic_error(_info.errorMessage("default value is not supported here"sv, defVal)); + throw CompileError("default value is not supported here"sv, defVal); } } auto indexItem = toAst(std::to_string(index), value); @@ -2298,17 +2366,17 @@ private: } else if (auto key = np->key.as()) { keyIndex = newExp(key, np->key).get(); } else { - throw std::logic_error(_info.errorMessage("unsupported key for destructuring"sv, np)); + throw CompileError("unsupported key for destructuring"sv, np); } } if (auto exp = np->value.as()) { - if (!isAssignable(exp)) throw std::logic_error(_info.errorMessage("can't do destructure value"sv, exp)); + if (!isAssignable(exp)) throw CompileError("can't do destructure value"sv, exp); auto item = singleValueFrom(exp)->item.get(); if (ast_is(item) || item->getByPath()) { auto subPairs = destructFromExp(exp, optional); if (!subPairs.empty()) { if (defVal) { - throw std::logic_error(_info.errorMessage("default value is not supported here"sv, defVal)); + throw CompileError("default value is not supported here"sv, defVal); } } for (auto& p : subPairs) { @@ -2333,7 +2401,7 @@ private: auto subPairs = destructFromExp(np->value, optional); if (!subPairs.empty()) { if (defVal) { - throw std::logic_error(_info.errorMessage("default value is not supported here"sv, defVal)); + throw CompileError("default value is not supported here"sv, defVal); } } for (auto& p : subPairs) { @@ -2461,13 +2529,13 @@ private: if (auto ssVal = simpleSingleValueFrom(*j)) { switch (ssVal->value->getId()) { case id(): - throw std::logic_error(_info.errorMessage("can not destructure a constant"sv, ssVal->value)); + throw CompileError("can not destructure a constant"sv, ssVal->value); break; case id(): - throw std::logic_error(_info.errorMessage("can not destructure a number"sv, ssVal->value)); + throw CompileError("can not destructure a number"sv, ssVal->value); break; case id(): - throw std::logic_error(_info.errorMessage("can not destructure a function"sv, ssVal->value)); + throw CompileError("can not destructure a function"sv, ssVal->value); break; } } @@ -2613,7 +2681,7 @@ private: simpleValue->value.set(tab); auto pairs = destructFromExp(newExp(simpleValue, expr), optional); if (pairs.empty()) { - throw std::logic_error(_info.errorMessage("expect items to be destructured"sv, tab)); + throw CompileError("expect items to be destructured"sv, tab); } destruct.items = std::move(pairs); if (!varDefOnly) { @@ -2715,17 +2783,17 @@ private: auto action = assignment->action.get(); switch (action->getId()) { case id(): { - if (expList->exprs.size() > 1) throw std::logic_error(_info.errorMessage("can not apply update to multiple values"sv, expList)); + if (expList->exprs.size() > 1) throw CompileError("can not apply update to multiple values"sv, expList); auto update = static_cast(action); auto leftExp = static_cast(expList->exprs.objects().front()); auto leftValue = singleValueFrom(leftExp); - if (!leftValue) throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, leftExp)); + if (!leftValue) throw CompileError("left hand expression is not assignable"sv, leftExp); auto chain = leftValue->item.as(); - if (!chain) throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, leftValue)); + if (!chain) throw CompileError("left hand expression is not assignable"sv, leftValue); BLOCK_START { auto dot = ast_cast(chain->items.back()); if (dot && dot->name.is()) { - throw std::logic_error(_info.errorMessage("can not apply update to a metatable"sv, leftExp)); + throw CompileError("can not apply update to a metatable"sv, leftExp); } BREAK_IF(chain->items.size() < 2); if (chain->items.size() == 2) { @@ -3060,9 +3128,9 @@ private: auto unary = static_cast(*it); auto value = static_cast(singleUnaryExpFrom(unary) ? unary->expos.back() : nullptr); if (values.back() == *it && !unary->ops.empty() && usage == ExpUsage::Common) { - throw std::logic_error(_info.errorMessage("unexpected expression"sv, x)); + throw CompileError("unexpected expression"sv, x); } - if (!value) throw std::logic_error(_info.errorMessage("pipe operator must be followed by chain value"sv, *it)); + if (!value) throw CompileError("pipe operator must be followed by chain value"sv, *it); if (auto chainValue = value->item.as()) { if (isChainValueCall(chainValue)) { auto last = chainValue->items.back(); @@ -3080,7 +3148,7 @@ private: args->swap(a, arg); findPlaceHolder = true; } else { - throw std::logic_error(_info.errorMessage("pipe placeholder can be used only in one place"sv, a)); + throw CompileError("pipe placeholder can be used only in one place"sv, a); } } } @@ -3094,7 +3162,7 @@ private: } arg.set(newExp(unary, x)); } else { - throw std::logic_error(_info.errorMessage("pipe operator must be followed by chain value"sv, value)); + throw CompileError("pipe operator must be followed by chain value"sv, value); } } switch (usage) { @@ -3428,7 +3496,7 @@ private: --last; auto lst = static_cast(*last); if (lst->appendix) { - throw std::logic_error(_info.errorMessage("statement decorator must be placed at the end of pipe chain"sv, lst->appendix.get())); + throw CompileError("statement decorator must be placed at the end of pipe chain"sv, lst->appendix.get()); } lst->appendix.set(stmt->appendix); stmt->appendix.set(nullptr); @@ -3441,14 +3509,14 @@ private: } cond = true; BLOCK_END - if (!cond) throw std::logic_error(_info.errorMessage("pipe chain must be following a value"sv, x)); + if (!cond) throw CompileError("pipe chain must be following a value"sv, x); stmt->content.set(nullptr); auto next = it; ++next; BLOCK_START BREAK_IF(next == nodes.end()); BREAK_IF(!static_cast(*next)->content.as()); - throw std::logic_error(_info.errorMessage("indent mismatch in pipe chain"sv, *next)); + throw CompileError("indent mismatch in pipe chain"sv, *next); BLOCK_END } else if (auto backcall = stmt->content.as()) { auto x = *nodes.begin(); @@ -3496,7 +3564,7 @@ private: args->swap(a, arg); findPlaceHolder = true; } else { - throw std::logic_error(_info.errorMessage("backcall placeholder can be used only in one place"sv, a)); + throw CompileError("backcall placeholder can be used only in one place"sv, a); } } } @@ -3663,15 +3731,15 @@ private: } case ExpUsage::Assignment: { auto last = lastStatementFrom(block); - if (!last) throw std::logic_error(_info.errorMessage("block is not assignable"sv, block)); + if (!last) throw CompileError("block is not assignable"sv, block); if (last->appendix) { auto appendix = last->appendix->item.get(); switch (appendix->getId()) { case id(): - throw std::logic_error(_info.errorMessage("while-loop line decorator is not supported here"sv, appendix)); + throw CompileError("while-loop line decorator is not supported here"sv, appendix); break; case id(): - throw std::logic_error(_info.errorMessage("for-loop line decorator is not supported here"sv, appendix)); + throw CompileError("for-loop line decorator is not supported here"sv, appendix); break; } } @@ -3697,7 +3765,7 @@ private: static_cast(*bLast)->needSep.set(nullptr); } } else if (!last->content.is()) { - throw std::logic_error(_info.errorMessage("expecting assignable statement or break loop"sv, last)); + throw CompileError("expecting assignable statement or break loop"sv, last); } break; } @@ -3750,7 +3818,7 @@ private: } else if (target.value() == "5.4"sv) { return 504; } else { - throw std::logic_error(_info.errorMessage("get invalid Lua target \""s + target.value() + "\", should be 5.1, 5.2, 5.3 or 5.4"s, x)); + throw CompileError("get invalid Lua target \""s + target.value() + "\", should be 5.1, 5.2, 5.3 or 5.4"s, x); } } #ifndef YUE_NO_MACRO @@ -3865,7 +3933,7 @@ private: void transformMacro(Macro_t* macro, str_list& out, bool exporting) { if (_scopes.size() > 1) { - throw std::logic_error(_info.errorMessage("can not define macro outside the root block"sv, macro)); + throw CompileError("can not define macro outside the root block"sv, macro); } auto macroName = _parser.toString(macro->name); auto argsDef = macro->macroLit->argsDef.get(); @@ -3874,7 +3942,7 @@ private: for (auto def_ : argsDef->definitions.objects()) { auto def = static_cast(def_); if (def->name.is()) { - throw std::logic_error(_info.errorMessage("self name is not supported for macro function argument"sv, def->name)); + throw CompileError("self name is not supported for macro function argument"sv, def->name); } else { std::string defVal; if (def->defaultValue) { @@ -3904,22 +3972,22 @@ private: pushOptions(macro->m_begin.m_line - 1); // cur loadstring codes chunk options if (lua_pcall(L, 3, 2, 0) != 0) { // loadstring(codes,chunk,options), cur f err std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to load macro codes\n"s + err, macro->macroLit)); + throw CompileError("failed to load macro codes\n"s + err, macro->macroLit); } // cur f err if (lua_isnil(L, -2) != 0) { // f == nil, cur f err std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to load macro codes, at (macro "s + macroName + "): "s + err, macro->macroLit)); + throw CompileError("failed to load macro codes, at (macro "s + macroName + "): "s + err, macro->macroLit); } lua_pop(L, 1); // cur f pushYue("pcall"sv); // cur f pcall lua_insert(L, -2); // cur pcall f if (lua_pcall(L, 1, 2, 0) != 0) { // f(), cur success macro std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to generate macro function\n"s + err, macro->macroLit)); + throw CompileError("failed to generate macro function\n"s + err, macro->macroLit); } // cur success res if (lua_toboolean(L, -2) == 0) { std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to generate macro function\n"s + err, macro->macroLit)); + throw CompileError("failed to generate macro function\n"s + err, macro->macroLit); } // cur true macro lua_remove(L, -2); // cur macro if (exporting && _config.exporting && !_config.module.empty()) { @@ -3936,7 +4004,7 @@ private: } #else void transformMacro(Macro_t* macro, str_list&, bool) { - throw std::logic_error(_info.errorMessage("macro feature not supported"sv, macro)); + throw CompileError("macro feature not supported"sv, macro)); } #endif // YUE_NO_MACRO @@ -3944,7 +4012,7 @@ private: if (!_enableReturn.top()) { ast_node* target = returnNode->valueList.get(); if (!target) target = returnNode; - throw std::logic_error(_info.errorMessage("can not mix use of return and export statements in module scope"sv, target)); + throw CompileError("can not mix use of return and export statements in module scope"sv, target); } if (auto valueList = returnNode->valueList.as()) { if (valueList->exprs.size() == 1) { @@ -4067,7 +4135,7 @@ private: assignSelf = true; if (def->op) { if (def->defaultValue) { - throw std::logic_error(_info.errorMessage("argument with default value should not check for existence"sv, def->op)); + throw CompileError("argument with default value should not check for existence"sv, def->op); } arg.checkExistence = true; } @@ -4087,10 +4155,10 @@ private: } case id(): arg.name = "self"sv; - if (def->op) throw std::logic_error(_info.errorMessage("can only check existence for assigning self field"sv, selfName->name)); + if (def->op) throw CompileError("can only check existence for assigning self field"sv, selfName->name); break; default: - throw std::logic_error(_info.errorMessage("invald self expression here"sv, selfName->name)); + throw CompileError("invald self expression here"sv, selfName->name); break; } break; @@ -4296,7 +4364,7 @@ private: chainValue->items.pop_back(); if (chainValue->items.empty()) { if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); + throw CompileError("short dot/colon syntax must be called within a with block"sv, x); } chainValue->items.push_back(toAst(_withVars.top(), x)); } @@ -4434,7 +4502,7 @@ private: case id(): case id(): if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, chainList.front())); + throw CompileError("short dot/colon syntax must be called within a with block"sv, chainList.front()); } else { baseChain->items.push_back(toAst(_withVars.top(), x)); } @@ -4527,7 +4595,7 @@ private: auto chain = x->new_ptr(); if (opIt == chainList.begin() && ast_is(x)) { if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); + throw CompileError("short dot/colon syntax must be called within a with block"sv, x); } else { chain->items.push_back(toAst(_withVars.top(), x)); } @@ -4671,7 +4739,7 @@ private: case id(): case id(): if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short dot/colon and indexing syntax must be called within a with block"sv, x)); + throw CompileError("short dot/colon and indexing syntax must be called within a with block"sv, x); } else { temp.push_back(_withVars.top()); } @@ -4704,7 +4772,7 @@ private: --next; } if (!ast_is(followItem)) { - throw std::logic_error(_info.errorMessage("colon chain item must be followed by invoke arguments"sv, colonItem)); + throw CompileError("colon chain item must be followed by invoke arguments"sv, colonItem); } if (colonItem->name.is()) { std::string callVar; @@ -4843,7 +4911,7 @@ private: void transformMacroInPlace(MacroInPlace_t* macroInPlace) { #ifdef YUE_NO_MACRO - throw std::logic_error(_info.errorMessage("macro feature not supported"sv, macroInPlace)); + throw CompileError("macro feature not supported"sv, macroInPlace)); #else // YUE_NO_MACRO auto x = macroInPlace; pushCurrentModule(); // cur @@ -4858,22 +4926,22 @@ private: pushOptions(macroInPlace->m_begin.m_line - 1); // loadstring codes chunk options if (lua_pcall(L, 3, 2, 0) != 0) { // loadstring(codes,chunk,options), f err std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to load macro codes\n"s + err, x)); + throw CompileError("failed to load macro codes\n"s + err, x); } // f err if (lua_isnil(L, -2) != 0) { // f == nil, f err std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to load macro codes, at (macro in-place): "s + err, x)); + throw CompileError("failed to load macro codes, at (macro in-place): "s + err, x); } lua_pop(L, 1); // f pushYue("pcall"sv); // f pcall lua_insert(L, -2); // pcall f if (lua_pcall(L, 1, 2, 0) != 0) { // f(), success macroFunc std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to generate macro function\n"s + err, x)); + throw CompileError("failed to generate macro function\n"s + err, x); } // success res if (lua_toboolean(L, -2) == 0) { std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to generate macro function\n"s + err, x)); + throw CompileError("failed to generate macro function\n"s + err, x); } // true macroFunc lua_remove(L, -2); // macroFunc pushYue("pcall"sv); // macroFunc pcall @@ -4881,11 +4949,11 @@ private: bool success = lua_pcall(L, 1, 2, 0) == 0; if (!success) { // err std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to expand macro: "s + err, x)); + throw CompileError("failed to expand macro: "s + err, x); } // success err if (lua_toboolean(L, -2) == 0) { std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to expand macro: "s + err, x)); + throw CompileError("failed to expand macro: "s + err, x); } #endif // YUE_NO_MACRO } @@ -4908,7 +4976,7 @@ private: if (!_useModule) { auto code = expandBuiltinMacro(macroName, x); if (!code.empty()) return {Empty, code, {}}; - throw std::logic_error(_info.errorMessage("can not resolve macro"sv, x)); + throw CompileError("can not resolve macro"sv, x); } pushCurrentModule(); // cur int top = lua_gettop(L) - 1; @@ -4918,7 +4986,7 @@ private: if (lua_isfunction(L, -1) == 0) { auto code = expandBuiltinMacro(macroName, x); if (!code.empty()) return {Empty, code, {}}; - throw std::logic_error(_info.errorMessage("can not resolve macro"sv, x)); + throw CompileError("can not resolve macro"sv, x); } // cur macroFunc pushYue("pcall"sv); // cur macroFunc pcall lua_insert(L, -2); // cur pcall macroFunc @@ -4981,15 +5049,15 @@ private: bool success = lua_pcall(L, (args ? static_cast(args->size()) : 0) + 1, 2, 0) == 0; if (!success) { // cur err std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to expand macro: "s + err, x)); + throw CompileError("failed to expand macro: "s + err, x); } // cur success res if (lua_toboolean(L, -2) == 0) { std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to expand macro: "s + err, x)); + throw CompileError("failed to expand macro: "s + err, x); } lua_remove(L, -2); // cur res if (lua_isstring(L, -1) == 0 && lua_istable(L, -1) == 0) { - throw std::logic_error(_info.errorMessage("macro function must return string or table"sv, x)); + throw CompileError("macro function must return string or table"sv, x); } // cur res std::string codes; std::string type; @@ -4999,7 +5067,7 @@ private: if (lua_isstring(L, -1) != 0) { codes = lua_tostring(L, -1); } else { - throw std::logic_error(_info.errorMessage("macro table must contain field \"code\" of string"sv, x)); + throw CompileError("macro table must contain field \"code\" of string"sv, x); } lua_pop(L, 1); // cur res lua_getfield(L, -1, "type"); // cur res type @@ -5007,7 +5075,7 @@ private: type = lua_tostring(L, -1); } if (type != "lua"sv && type != "text"sv) { - throw std::logic_error(_info.errorMessage("macro table must contain field \"type\" of value \"lua\" or \"text\""sv, x)); + throw CompileError("macro table must contain field \"type\" of value \"lua\" or \"text\""sv, x); } lua_pop(L, 1); // cur res lua_getfield(L, -1, "locals"); // cur res locals @@ -5016,13 +5084,13 @@ private: lua_rawgeti(L, -1, i + 1); // cur res locals item size_t len = 0; if (lua_isstring(L, -1) == 0) { - throw std::logic_error(_info.errorMessage("macro table field \"locals\" must be a table of strings"sv, x)); + throw CompileError("macro table field \"locals\" must be a table of strings"sv, x); } auto name = lua_tolstring(L, -1, &len); if (_parser.match({name, len})) { localVars.push_back(std::string(name, len)); } else { - throw std::logic_error(_info.errorMessage("macro table field \"locals\" must contain names for local variables, got \""s + std::string(name, len) + '"', x)); + throw CompileError("macro table field \"locals\" must contain names for local variables, got \""s + std::string(name, len) + '"', x); } lua_pop(L, 1); } @@ -5046,14 +5114,14 @@ private: ParseInfo info; if (type == "lua"sv) { if (!isBlock) { - throw std::logic_error(_info.errorMessage("lua macro can only be placed where block macro is allowed"sv, x)); + throw CompileError("lua macro can only be placed where block macro is allowed"sv, x); } auto macroChunk = "=(macro "s + _parser.toString(x->name) + ')'; int top = lua_gettop(L); DEFER(lua_settop(L, top)); if (luaL_loadbuffer(L, codes.c_str(), codes.size(), macroChunk.c_str()) != 0) { std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(err, x)); + throw CompileError(err, x); } if (!codes.empty()) { if (_config.reserveLineNumber) { @@ -5064,7 +5132,7 @@ private: return {nullptr, nullptr, std::move(codes), std::move(localVars)}; } else if (type == "text"sv) { if (!isBlock) { - throw std::logic_error(_info.errorMessage("text macro can only be placed where block macro is allowed"sv, x)); + throw CompileError("text macro can only be placed where block macro is allowed"sv, x); } if (!codes.empty()) { codes.append(_newLine); @@ -5074,22 +5142,19 @@ private: if (!codes.empty()) { if (isBlock) { info = _parser.parse(codes); - if (!info.node) { - info.error = info.error.substr(info.error.find(':') + 2); - throw std::logic_error(_info.errorMessage("failed to expand macro as block: "s + info.error, x)); + if (info.error) { + throw CompileError("failed to expand macro as block: "s + info.error.value().msg, x); } } else { info = _parser.parse(codes); if (!info.node && allowBlockMacroReturn) { info = _parser.parse(codes); - if (!info.node) { - info.error = info.error.substr(info.error.find(':') + 2); - throw std::logic_error(_info.errorMessage("failed to expand macro as expr or block: "s + info.error, x)); + if (info.error) { + throw CompileError("failed to expand macro as expr or block: "s + info.error.value().msg, x); } isBlock = true; - } else if (!info.node) { - info.error = info.error.substr(info.error.find(':') + 2); - throw std::logic_error(_info.errorMessage("failed to expand macro as expr: "s + info.error, x)); + } else if (info.error) { + throw CompileError("failed to expand macro as expr: "s + info.error.value().msg, x); } } int line = x->m_begin.m_line; @@ -5140,18 +5205,18 @@ private: auto stmt = static_cast(stmt_); if (auto global = stmt->content.as()) { if (global->item.is()) { - throw std::logic_error(_info.errorMessage("can not insert global statement with wildcard operator from macro"sv, x)); + throw CompileError("can not insert global statement with wildcard operator from macro"sv, x); } } else if (auto local = stmt->content.as()) { if (local->item.is()) { - throw std::logic_error(_info.errorMessage("can not insert local statement with wildcard operator from macro"sv, x)); + throw CompileError("can not insert local statement with wildcard operator from macro"sv, x); } } } } return {info.node, std::move(info.codes), Empty, std::move(localVars)}; } else { - if (!isBlock) throw std::logic_error(_info.errorMessage("failed to expand empty macro as expr"sv, x)); + if (!isBlock) throw CompileError("failed to expand empty macro as expr"sv, x); return {x->new_ptr().get(), std::move(info.codes), Empty, std::move(localVars)}; } } @@ -5205,7 +5270,7 @@ private: return; #else (void)allowBlockMacroReturn; - throw std::logic_error(_info.errorMessage("macro feature not supported"sv, chainValue)); + throw CompileError("macro feature not supported"sv, chainValue)); #endif // YUE_NO_MACRO } const auto& chainList = chainValue->items.objects(); @@ -5243,11 +5308,11 @@ private: } void transformSlice(Slice_t* slice, str_list&) { - throw std::logic_error(_info.errorMessage("slice syntax not supported here"sv, slice)); + throw CompileError("slice syntax not supported here"sv, slice); } void transform_table_appending_op(TableAppendingOp_t* op, str_list&) { - throw std::logic_error(_info.errorMessage("table appending syntax not supported here"sv, op)); + throw CompileError("table appending syntax not supported here"sv, op); } void transformInvoke(Invoke_t* invoke, str_list& out) { @@ -5270,7 +5335,7 @@ private: for (auto _op : unary_value->ops.objects()) { std::string op = _parser.toString(_op); if (op == "~"sv && getLuaTarget(_op) < 503) { - throw std::logic_error(_info.errorMessage("bitwise operator is not available when not targeting Lua version 5.3 or higher"sv, _op)); + throw CompileError("bitwise operator is not available when not targeting Lua version 5.3 or higher"sv, _op); } temp.push_back(op == "not"sv ? op + ' ' : op); } @@ -5287,7 +5352,7 @@ private: for (auto _op : unary_exp->ops.objects()) { std::string op = _parser.toString(_op); if (op == "~"sv && getLuaTarget(_op) < 503) { - throw std::logic_error(_info.errorMessage("bitwise operator is not available when not targeting Lua version 5.3 or higher"sv, _op)); + throw CompileError("bitwise operator is not available when not targeting Lua version 5.3 or higher"sv, _op); } unary_op.append(op == "not"sv ? op + ' ' : op); } @@ -5311,7 +5376,7 @@ private: void transformVarArg(VarArg_t* varArg, str_list& out) { if (_varArgs.empty() || !_varArgs.top().hasVar) { - throw std::logic_error(_info.errorMessage("cannot use '...' outside a vararg function near '...'"sv, varArg)); + throw CompileError("cannot use '...' outside a vararg function near '...'"sv, varArg); } _varArgs.top().usedVar = true; out.push_back("..."s); @@ -5385,7 +5450,7 @@ private: case id(): { if (auto pair = ast_cast(item)) { if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value here"sv, pair->defVal)); + throw CompileError("invalid default value here"sv, pair->defVal); } item = pair->pair.get(); } @@ -5399,7 +5464,7 @@ private: case id(): { if (auto pair = ast_cast(item)) { if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value here"sv, pair->defVal)); + throw CompileError("invalid default value here"sv, pair->defVal); } item = pair->pair.get(); } @@ -5451,7 +5516,7 @@ private: auto current = item; if (auto pair = ast_cast(item)) { if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value here"sv, pair->defVal)); + throw CompileError("invalid default value here"sv, pair->defVal); } item = pair->item.get(); } @@ -5500,7 +5565,7 @@ private: case id(): { if (auto pair = ast_cast(item)) { if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value here"sv, pair->defVal)); + throw CompileError("invalid default value here"sv, pair->defVal); } item = pair->pair.get(); } @@ -5514,7 +5579,7 @@ private: case id(): { if (auto pair = ast_cast(item)) { if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value here"sv, pair->defVal)); + throw CompileError("invalid default value here"sv, pair->defVal); } item = pair->pair.get(); } @@ -5601,7 +5666,7 @@ private: case id(): { auto pair = static_cast(item); if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value"sv, pair->defVal)); + throw CompileError("invalid default value"sv, pair->defVal); } item = pair->pair.get(); break; @@ -5609,7 +5674,7 @@ private: case id(): { auto pair = static_cast(item); if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value"sv, pair->defVal)); + throw CompileError("invalid default value"sv, pair->defVal); } item = pair->pair.get(); break; @@ -5617,7 +5682,7 @@ private: case id(): { auto pair = static_cast(item); if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value"sv, pair->defVal)); + throw CompileError("invalid default value"sv, pair->defVal); } item = pair->pair.get(); break; @@ -5625,7 +5690,7 @@ private: case id(): { auto pair = static_cast(item); if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value"sv, pair->defVal)); + throw CompileError("invalid default value"sv, pair->defVal); } item = pair->pair.get(); break; @@ -5633,7 +5698,7 @@ private: case id(): { auto pair = static_cast(item); if (pair->defVal) { - throw std::logic_error(_info.errorMessage("invalid default value"sv, pair->defVal)); + throw CompileError("invalid default value"sv, pair->defVal); } item = pair->item.get(); break; @@ -5650,7 +5715,7 @@ private: isMetamethod = true; auto mp = static_cast(item); if (metatableItem) { - throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->name)); + throw CompileError("too many metatable declarations"sv, mp->name); } auto name = _parser.toString(mp->name); checkMetamethod(name, mp->name); @@ -5665,7 +5730,7 @@ private: auto newPair = item->new_ptr(); if (mp->key) { if (metatableItem) { - throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->key)); + throw CompileError("too many metatable declarations"sv, mp->key); } switch (mp->key->getId()) { case id(): { @@ -5690,7 +5755,7 @@ private: metatable->pairs.push_back(newPair); } else { if (!metatable->pairs.empty()) { - throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->value)); + throw CompileError("too many metatable declarations"sv, mp->value); } metatableItem.set(mp->value); } @@ -5910,7 +5975,7 @@ private: auto indexVar = getUnusedName("_index_"sv); varAfter.push_back(indexVar); auto value = singleValueFrom(star_exp->value); - if (!value) throw std::logic_error(_info.errorMessage("invalid star syntax"sv, star_exp)); + if (!value) throw CompileError("invalid star syntax"sv, star_exp); bool endWithSlice = false; BLOCK_START auto chainValue = value->item.as(); @@ -6422,11 +6487,11 @@ private: void checkOperatorAvailable(const std::string& op, ast_node* node) { if (op == "&"sv || op == "~"sv || op == "|"sv || op == ">>"sv || op == "<<"sv) { if (getLuaTarget(node) < 503) { - throw std::logic_error(_info.errorMessage("bitwise operator is not available when not targeting Lua version 5.3 or higher"sv, node)); + throw CompileError("bitwise operator is not available when not targeting Lua version 5.3 or higher"sv, node); } } else if (op == "//"sv) { if (getLuaTarget(node) < 503) { - throw std::logic_error(_info.errorMessage("floor division is not available when not targeting Lua version 5.3 or higher"sv, node)); + throw CompileError("floor division is not available when not targeting Lua version 5.3 or higher"sv, node); } } } @@ -6668,7 +6733,7 @@ private: std::string assignItem; if (assignable) { if (!isAssignable(assignable)) { - throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, assignable)); + throw CompileError("left hand expression is not assignable"sv, assignable); } bool newDefined = false; std::tie(className, newDefined) = defineClassVariable(assignable); @@ -6962,7 +7027,7 @@ private: if (selfItem) { type = MemType::Property; auto name = ast_cast(selfItem->name); - if (!name) throw std::logic_error(_info.errorMessage("invalid class poperty name"sv, selfItem->name)); + if (!name) throw CompileError("invalid class poperty name"sv, selfItem->name); newSuperCall = classVar + ".__parent."s + _parser.toString(name->name); } else { auto x = keyName; @@ -7287,17 +7352,17 @@ private: void transformExport(Export_t* exportNode, str_list& out) { auto x = exportNode; if (_scopes.size() > 1) { - throw std::logic_error(_info.errorMessage("can not do module export outside the root block"sv, exportNode)); + throw CompileError("can not do module export outside the root block"sv, exportNode); } if (exportNode->assign) { auto expList = exportNode->target.to(); if (expList->exprs.size() != exportNode->assign->values.size()) { - throw std::logic_error(_info.errorMessage("left and right expressions must be matched in export statement"sv, x)); + throw CompileError("left and right expressions must be matched in export statement"sv, x); } for (auto _exp : expList->exprs.objects()) { auto exp = static_cast(_exp); if (!variableFrom(exp) && !exp->getByPath() && !exp->getByPath()) { - throw std::logic_error(_info.errorMessage("left hand expressions must be variables in export statement"sv, x)); + throw CompileError("left hand expressions must be variables in export statement"sv, x); } } auto assignment = x->new_ptr(); @@ -7696,7 +7761,7 @@ private: case id(): case id(): case id(): { - throw std::logic_error(_info.errorMessage("macro feature not supported"sv, item)); + throw CompileError("macro feature not supported"sv, item)); break; } #else // YUE_NO_MACRO @@ -7712,7 +7777,7 @@ private: break; } case id(): - if (importAllMacro) throw std::logic_error(_info.errorMessage("import all macro symbol duplicated"sv, item)); + if (importAllMacro) throw CompileError("import all macro symbol duplicated"sv, item); importAllMacro = true; break; #endif // YUE_NO_MACRO @@ -7740,7 +7805,7 @@ private: lua_pushlstring(L, moduleName.c_str(), moduleName.size()); // cur find_modulepath moduleName if (lua_pcall(L, 1, 2, 0) != 0) { std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to resolve module path\n"s + err, x)); + throw CompileError("failed to resolve module path\n"s + err, x); } if (lua_isnil(L, -2) != 0) { str_list files; @@ -7752,7 +7817,7 @@ private: lua_pop(L, 1); } } - throw std::logic_error(_info.errorMessage("module '"s + moduleName + "\' not found:\n\t"s + join(files, "\n\t"sv), x)); + throw CompileError("module '"s + moduleName + "\' not found:\n\t"s + join(files, "\n\t"sv), x); } lua_pop(L, 1); std::string moduleFullName = lua_tostring(L, -1); @@ -7762,10 +7827,10 @@ private: lua_pushlstring(L, moduleFullName.c_str(), moduleFullName.size()); // cur load_text moduleFullName if (lua_pcall(L, 1, 1, 0) != 0) { std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage("failed to read module file\n"s + err, x)); + throw CompileError("failed to read module file\n"s + err, x); } // cur text if (lua_isnil(L, -1) != 0) { - throw std::logic_error(_info.errorMessage("failed to get module text"sv, x)); + throw CompileError("failed to get module text"sv, x); } // cur text std::string text = lua_tostring(L, -1); auto compiler = YueCompilerImpl(L, _luaOpen, false); @@ -7777,8 +7842,8 @@ private: config.module = moduleFullName; config.exporting = true; auto result = compiler.compile(text, config); - if (result.codes.empty() && !result.error.empty()) { - throw std::logic_error(_info.errorMessage("failed to compile module '"s + moduleName + "\': "s + result.error, x)); + if (result.error) { + throw CompileError("failed to compile module '"s + moduleName + "\': "s + result.error.value().msg, x); } lua_pop(L, 1); // cur } @@ -7798,7 +7863,7 @@ private: } #else // YUE_NO_MACRO if (importAllMacro) { - throw std::logic_error(_info.errorMessage("macro feature not supported"sv, import->target)); + throw CompileError("macro feature not supported"sv, import->target)); } #endif // YUE_NO_MACRO if (newTab->items.empty()) { @@ -8034,10 +8099,12 @@ private: pushScope(); extraScope = true; } + auto typeVar = getUnusedName("_type_"); + forceAddToScope(typeVar); tabCheckVar = getUnusedName("_tab_"); forceAddToScope(tabCheckVar); - temp.push_back(indent() + "local "s + tabCheckVar + " = "s + globalVar("type", branch) + '(' + objVar + ')' + nll(branch)); - temp.push_back(indent() + tabCheckVar + " = \"table\" == "s + tabCheckVar + " or \"userdata\" == "s + tabCheckVar + nll(branch)); + temp.push_back(indent() + "local "s + typeVar + " = "s + globalVar("type", branch) + '(' + objVar + ')' + nll(branch)); + temp.push_back(indent() + "local "s + tabCheckVar + " = \"table\" == "s + typeVar + " or \"userdata\" == "s + typeVar + nll(branch)); } std::string matchVar; bool lastBranch = branches.back() == branch_ && !switchNode->lastBranch; @@ -8199,7 +8266,7 @@ private: } else { _buf << "only one right value expected, got "sv << x->assign->values.size(); } - throw std::logic_error(_info.errorMessage(clearBuf(), x->assign->values.front())); + throw CompileError(clearBuf(), x->assign->values.front()); } auto listA = x->new_ptr(); auto assignA = x->new_ptr(); @@ -8257,7 +8324,7 @@ private: } } else { if (localAttrib->attrib.is()) { - throw std::logic_error(_info.errorMessage("close attribute is not available when not targeting Lua version 5.4 or higher"sv, x)); + throw CompileError("close attribute is not available when not targeting Lua version 5.4 or higher"sv, x); } for (auto& var : vars) { markVarConst(var); @@ -8281,13 +8348,13 @@ private: void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { auto keyword = _parser.toString(breakLoop); if (_enableBreakLoop.empty() || !_enableBreakLoop.top()) { - throw std::logic_error(_info.errorMessage(keyword + " is not inside a loop"s, breakLoop)); + throw CompileError(keyword + " is not inside a loop"s, breakLoop); } if (keyword == "break"sv) { out.push_back(indent() + keyword + nll(breakLoop)); return; } - if (_continueVars.empty()) throw std::logic_error(_info.errorMessage("continue is not inside a loop"sv, breakLoop)); + if (_continueVars.empty()) throw CompileError("continue is not inside a loop"sv, breakLoop); str_list temp; auto& item = _continueVars.top(); if (item.condAssign) { @@ -8308,7 +8375,7 @@ private: void transformLabel(Label_t* label, str_list& out) { if (getLuaTarget(label) < 502) { - throw std::logic_error(_info.errorMessage("label statement is not available when not targeting Lua version 5.2 or higher"sv, label)); + throw CompileError("label statement is not available when not targeting Lua version 5.2 or higher"sv, label); } auto labelStr = _parser.toString(label->label); int currentScope = _gotoScopes.top(); @@ -8321,7 +8388,7 @@ private: } auto& scope = _labels[currentScope].value(); if (auto it = scope.find(labelStr); it != scope.end()) { - throw std::logic_error(_info.errorMessage("label '"s + labelStr + "' already defined at line "s + std::to_string(it->second.line), label)); + throw CompileError("label '"s + labelStr + "' already defined at line "s + std::to_string(it->second.line), label); } scope[labelStr] = {label->m_begin.m_line, static_cast(_scopes.size())}; out.push_back(indent() + "::"s + labelStr + "::"s + nll(label)); @@ -8329,7 +8396,7 @@ private: void transformGoto(Goto_t* gotoNode, str_list& out) { if (getLuaTarget(gotoNode) < 502) { - throw std::logic_error(_info.errorMessage("goto statement is not available when not targeting Lua version 5.2 or higher"sv, gotoNode)); + throw CompileError("goto statement is not available when not targeting Lua version 5.2 or higher"sv, gotoNode); } auto labelStr = _parser.toString(gotoNode->label); gotos.push_back({gotoNode, labelStr, _gotoScopes.top(), static_cast(_scopes.size())}); @@ -8338,7 +8405,7 @@ private: void transformShortTabAppending(ShortTabAppending_t* tab, str_list& out) { if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short table appending syntax must be called within a with block"sv, tab)); + throw CompileError("short table appending syntax must be called within a with block"sv, tab); } auto assignment = toAst(_withVars.top() + "[]=nil"s, tab); assignment->action.set(tab->assign); @@ -8349,7 +8416,7 @@ private: auto x = chainAssign; auto value = chainAssign->assign->values.front(); if (chainAssign->assign->values.size() != 1) { - throw std::logic_error(_info.errorMessage("only one right value expected"sv, value)); + throw CompileError("only one right value expected"sv, value); } str_list temp; bool constVal = false; diff --git a/src/yuescript/yue_compiler.h b/src/yuescript/yue_compiler.h index a5d33cc..860ddfb 100644 --- a/src/yuescript/yue_compiler.h +++ b/src/yuescript/yue_compiler.h @@ -11,6 +11,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include #include #include @@ -30,7 +31,7 @@ struct YueConfig { bool reserveLineNumber = true; bool useSpaceOverTab = false; bool reserveComment = false; - + // internal options bool exporting = false; bool profiling = false; int lineOffset = 0; @@ -48,11 +49,28 @@ using GlobalVars = std::vector; struct CompileInfo { std::string codes; - std::string error; + struct Error { + std::string msg; + int line; + int col; + std::string displayMessage; + }; + std::optional error; std::unique_ptr globals; std::unique_ptr options; double parseTime; double compileTime; + + CompileInfo() { } + CompileInfo( + std::string&& codes, + std::optional&& error, + std::unique_ptr&& globals, + std::unique_ptr&& options, + double parseTime, + double compileTime); + CompileInfo(CompileInfo&& other); + void operator=(CompileInfo&& other); }; class YueCompilerImpl; diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index d8fdc28..f215ee4 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp @@ -30,6 +30,17 @@ std::unordered_set Keywords = { "try"s, "unless"s, "using"s, "when"s, "with"s // Yue keywords }; +class ParserError : public std::logic_error { +public: + explicit ParserError(std::string_view msg, const pos* begin) + : std::logic_error(std::string(msg)) + , line(begin->m_line) + , col(begin->m_col) { } + + int line; + int col; +}; + // clang-format off YueParser::YueParser() { plain_space = *set(" \t"); @@ -78,7 +89,7 @@ YueParser::YueParser() { Seperator = true_(); empty_block_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("must be followed by a statement or an indented block", *item.begin, *item.end); + throw ParserError("must be followed by a statement or an indented block"sv, item.begin); return false; }); @@ -489,7 +500,7 @@ YueParser::YueParser() { st->expLevel++; const int max_exp_level = 100; if (st->expLevel > max_exp_level) { - throw ParserError("nesting expressions exceeds 100 levels", *item.begin, *item.end); + throw ParserError("nesting expressions exceeds 100 levels"sv, item.begin); } return true; }); @@ -658,10 +669,10 @@ YueParser::YueParser() { }) >> (pl::user(space >> ExportDefault >> space >> Exp, [](const item_t& item) { State* st = reinterpret_cast(item.user_data); if (st->exportDefault) { - throw ParserError("export default has already been declared", *item.begin, *item.end); + throw ParserError("export default has already been declared"sv, item.begin); } if (st->exportCount > 1) { - throw ParserError("there are items already being exported", *item.begin, *item.end); + throw ParserError("there are items already being exported"sv, item.begin); } st->exportDefault = true; return true; @@ -669,7 +680,7 @@ YueParser::YueParser() { | (not_(space >> ExportDefault) >> pl::user(true_(), [](const item_t& item) { State* st = reinterpret_cast(item.user_data); if (st->exportDefault && st->exportCount > 1) { - throw ParserError("can not export any more items when 'export default' is declared", *item.begin, *item.end); + throw ParserError("can not export any more items when 'export default' is declared"sv, item.begin); } return true; }) >> space >> ExpList >> -(space >> Assign)) @@ -762,7 +773,7 @@ YueParser::YueParser() { ) | arg_table_block; leading_spaces_error = pl::user(+space_one >> '(' >> space >> Exp >> +(space >> ',' >> space >> Exp) >> space >> ')', [](const item_t& item) { - throw ParserError("write invoke arguments in parentheses without leading spaces or just leading spaces without parentheses", *item.begin, *item.end); + throw ParserError("write invoke arguments in parentheses without leading spaces or just leading spaces without parentheses"sv, item.begin); return false; }); @@ -776,12 +787,12 @@ YueParser::YueParser() { ConstValue = (expr("nil") | "true" | "false") >> not_alpha_num; braces_expression_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("syntax error in brace expression", *item.begin, *item.end); + throw ParserError("syntax error in brace expression"sv, item.begin); return false; }); brackets_expression_error = pl::user(true_(), [](const item_t& item) { - throw ParserError("syntax error in bracket expression", *item.begin, *item.end); + throw ParserError("syntax error in bracket expression"sv, item.begin); return false; }); @@ -846,7 +857,7 @@ YueParser::YueParser() { ) >> and_(line_break); indentation_error = pl::user(not_(pipe_operator | eof()), [](const item_t& item) { - throw ParserError("unexpected indent", *item.begin, *item.end); + throw ParserError("unexpected indent"sv, item.begin); return false; }); @@ -875,7 +886,7 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) { res.codes = std::make_unique(); } } catch (const std::range_error&) { - res.error = "invalid text encoding"sv; + res.error = {"invalid text encoding"s, 1, 1}; return res; } error_list errors; @@ -888,26 +899,22 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) { res.exportMacro = state.exportMacro; } } catch (const ParserError& err) { - res.error = res.errorMessage(err.what(), &err.loc); + res.error = {err.what(), err.line, err.col}; return res; } catch (const std::logic_error& err) { - res.error = err.what(); + res.error = {err.what(), 1, 1}; return res; } if (!errors.empty()) { - std::ostringstream buf; - for (error_list::iterator it = errors.begin(); it != errors.end(); ++it) { - const error& err = *it; - switch (err.m_type) { - case ERROR_TYPE::ERROR_SYNTAX_ERROR: - buf << res.errorMessage("syntax error"sv, &err); - break; - case ERROR_TYPE::ERROR_INVALID_EOF: - buf << res.errorMessage("invalid EOF"sv, &err); - break; - } + const error& err = errors.front(); + switch (err.m_type) { + case ERROR_TYPE::ERROR_SYNTAX_ERROR: + res.error = {"syntax error"s, err.m_begin.m_line, err.m_begin.m_col}; + break; + case ERROR_TYPE::ERROR_INVALID_EOF: + res.error = {"invalid EOF"s, err.m_begin.m_line, err.m_begin.m_col}; + break; } - res.error = buf.str(); } return res; } @@ -944,9 +951,9 @@ void trim(std::string& str) { } } // namespace Utils -std::string ParseInfo::errorMessage(std::string_view msg, const input_range* loc) const { +std::string ParseInfo::errorMessage(std::string_view msg, int errLine, int errCol) const { const int ASCII = 255; - int length = loc->m_begin.m_line; + int length = errLine; auto begin = codes->begin(); auto end = codes->end(); int count = 0; @@ -961,7 +968,7 @@ std::string ParseInfo::errorMessage(std::string_view msg, const input_range* loc count++; } } - int oldCol = loc->m_begin.m_col; + int oldCol = errCol; int col = std::max(0, oldCol - 1); auto it = begin; for (int i = 0; i < oldCol && it != end; ++i) { @@ -977,7 +984,7 @@ std::string ParseInfo::errorMessage(std::string_view msg, const input_range* loc } Utils::replace(line, "\t"sv, " "sv); std::ostringstream buf; - buf << loc->m_begin.m_line << ": "sv << msg << '\n' + buf << errLine << ": "sv << msg << '\n' << line << '\n' << std::string(col, ' ') << "^"sv; return buf.str(); diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index cce6741..31134e1 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h @@ -11,6 +11,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include #include #include @@ -26,25 +27,18 @@ using namespace std::string_literals; using namespace parserlib; struct ParseInfo { + struct Error { + std::string msg; + int line; + int col; + }; ast_ptr node; - std::string error; + std::optional error; std::unique_ptr codes; bool exportDefault = false; bool exportMacro = false; std::string moduleName; - std::string errorMessage(std::string_view msg, const input_range* loc) const; -}; - -class ParserError : public std::logic_error { -public: - explicit ParserError(const std::string& msg, const pos& begin, const pos& end) - : std::logic_error(msg) - , loc(begin, end) { } - - explicit ParserError(const char* msg, const pos& begin, const pos& end) - : std::logic_error(msg) - , loc(begin, end) { } - input_range loc; + std::string errorMessage(std::string_view msg, int errLine, int errCol) const; }; template @@ -53,21 +47,21 @@ struct identity { }; #ifdef NDEBUG - #define NONE_AST_RULE(type) \ - rule type; +#define NONE_AST_RULE(type) \ + rule type; - #define AST_RULE(type) \ - rule type; \ - ast type##_impl = type; \ - inline rule& getRule(identity) { return type; } +#define AST_RULE(type) \ + rule type; \ + ast type##_impl = type; \ + inline rule& getRule(identity) { return type; } #else // NDEBUG - #define NONE_AST_RULE(type) \ - rule type{#type, rule::initTag{}}; +#define NONE_AST_RULE(type) \ + rule type{#type, rule::initTag{}}; - #define AST_RULE(type) \ - rule type{#type, rule::initTag{}}; \ - ast type##_impl = type; \ - inline rule& getRule(identity) { return type; } +#define AST_RULE(type) \ + rule type{#type, rule::initTag{}}; \ + ast type##_impl = type; \ + inline rule& getRule(identity) { return type; } #endif // NDEBUG extern std::unordered_set LuaKeywords; diff --git a/src/yuescript/yuescript.cpp b/src/yuescript/yuescript.cpp index b5043e0..ef427ad 100644 --- a/src/yuescript/yuescript.cpp +++ b/src/yuescript/yuescript.cpp @@ -62,43 +62,55 @@ static int init_stacktraceplus(lua_State* L) { return 1; } +static yue::YueConfig get_config(lua_State* L) { + yue::YueConfig config; + lua_pushliteral(L, "lint_global"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.lintGlobalVariable = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "implicit_return_root"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.implicitReturnRoot = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "reserve_line_number"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.reserveLineNumber = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "reserve_comment"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.reserveComment = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "space_over_tab"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.useSpaceOverTab = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "target"); + lua_gettable(L, -2); + if (lua_isstring(L, -1) != 0) { + config.options["target"] = lua_tostring(L, -1); + } + lua_pop(L, 1); + return config; +} + static int yuetolua(lua_State* L) { - size_t size = 0; - const char* input = luaL_checklstring(L, 1, &size); + size_t len = 0; + std::string codes(luaL_checklstring(L, 1, &len), len); yue::YueConfig config; bool sameModule = false; if (lua_gettop(L) == 2) { luaL_checktype(L, 2, LUA_TTABLE); - lua_pushliteral(L, "lint_global"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - config.lintGlobalVariable = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); - lua_pushliteral(L, "implicit_return_root"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - config.implicitReturnRoot = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); - lua_pushliteral(L, "reserve_line_number"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - config.reserveLineNumber = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); - lua_pushliteral(L, "reserve_comment"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - config.reserveComment = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); - lua_pushliteral(L, "space_over_tab"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - config.useSpaceOverTab = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); + config = get_config(L); lua_pushliteral(L, "same_module"); lua_gettable(L, -2); if (lua_isboolean(L, -1) != 0) { @@ -117,24 +129,18 @@ static int yuetolua(lua_State* L) { config.module = lua_tostring(L, -1); } lua_pop(L, 1); - lua_pushliteral(L, "target"); - lua_gettable(L, -2); - if (lua_isstring(L, -1) != 0) { - config.options["target"] = lua_tostring(L, -1); - } - lua_pop(L, 1); } - std::string s(input, size); - auto result = yue::YueCompiler(L, nullptr, sameModule).compile(s, config); - if (result.codes.empty() && !result.error.empty()) { + auto result = yue::YueCompiler(L, nullptr, sameModule).compile(codes, config); + if (result.error) { lua_pushnil(L); } else { lua_pushlstring(L, result.codes.c_str(), result.codes.size()); } - if (result.error.empty()) { - lua_pushnil(L); + if (result.error) { + const auto& msg = result.error.value().displayMessage; + lua_pushlstring(L, msg.c_str(), msg.size()); } else { - lua_pushlstring(L, result.error.c_str(), result.error.size()); + lua_pushnil(L); } if (result.globals) { lua_createtable(L, static_cast(result.globals->size()), 0); @@ -156,6 +162,57 @@ static int yuetolua(lua_State* L) { return 3; } +static int yuecheck(lua_State* L) { + size_t len = 0; + std::string codes{luaL_checklstring(L, 1, &len), len}; + yue::YueConfig config; + config.lintGlobalVariable = true; + if (lua_gettop(L) == 2) { + luaL_checktype(L, 2, LUA_TTABLE); + config = get_config(L); + } + auto result = yue::YueCompiler(L).compile(codes, config); + lua_createtable(L, 0, 0); + int i = 0; + if (result.error) { + const auto& error = result.error.value(); + lua_createtable(L, 4, 0); + lua_pushliteral(L, "error"); + lua_rawseti(L, -2, 1); + lua_pushlstring(L, error.msg.c_str(), error.msg.length()); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, error.line); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, error.col); + lua_rawseti(L, -2, 4); + lua_rawseti(L, -2, ++i); + } + if (result.globals) { + for (const auto& global : *result.globals) { + lua_createtable(L, 4, 0); + lua_pushliteral(L, "global"); + lua_rawseti(L, -2, 1); + lua_pushlstring(L, global.name.c_str(), global.name.length()); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, global.line); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, global.col); + lua_rawseti(L, -2, 4); + lua_rawseti(L, -2, ++i); + } + } + if (result.error) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; + } else { + lua_pushboolean(L, 1); + lua_insert(L, -2); + lua_pushlstring(L, result.codes.c_str(), result.codes.length()); + return 3; + } +} + struct yue_stack { int continuation; yue::ast_node* node; @@ -174,7 +231,7 @@ static int yuetoast(lua_State* L) { } yue::YueParser parser; auto info = parser.parse({input, size}); - if (info.node) { + if (!info.error) { lua_createtable(L, 0, 0); int tableIndex = lua_gettop(L); lua_createtable(L, 0, 0); @@ -300,7 +357,8 @@ static int yuetoast(lua_State* L) { return 1; } else { lua_pushnil(L); - lua_pushlstring(L, info.error.c_str(), info.error.length()); + const auto& msg = info.error.value().msg; + lua_pushlstring(L, msg.c_str(), msg.length()); return 2; } } @@ -308,6 +366,7 @@ static int yuetoast(lua_State* L) { static const luaL_Reg yuelib[] = { {"to_lua", yuetolua}, {"to_ast", yuetoast}, + {"check", yuecheck}, {"version", nullptr}, {"options", nullptr}, {"load_stacktraceplus", nullptr}, diff --git a/src/yuescript/yuescript.h b/src/yuescript/yuescript.h index 2d2d660..51622f8 100644 --- a/src/yuescript/yuescript.h +++ b/src/yuescript/yuescript.h @@ -56,7 +56,12 @@ end local function find_modulepath(name) local suffix = "." .. yue.options.extension local dirsep = yue.options.dirsep - local name_path = name:match("[\\/]") and name or name:gsub("%.", dirsep) + local name_path + if name:match("[\\/]") then + name_path = name:gsub("%" .. suffix .. "$", "") + else + name_path = name:gsub("%.", dirsep) + end local file_exist, file_path local tried = {} local paths = {package.path, yue.options.path} -- cgit v1.2.3-55-g6feb