diff options
| author | Li Jin <dragon-fly@qq.com> | 2020-10-21 23:44:50 +0800 |
|---|---|---|
| committer | Li Jin <dragon-fly@qq.com> | 2020-10-21 23:44:50 +0800 |
| commit | b6725202f4a8cac5f829dac9a72a81f3ff73e787 (patch) | |
| tree | c173accb869b60cba14babc7685284864bc80426 /src | |
| parent | 0777356cbe599b3f88bdfa476e3ffa64bb3a3a8c (diff) | |
| download | yuescript-b6725202f4a8cac5f829dac9a72a81f3ff73e787.tar.gz yuescript-b6725202f4a8cac5f829dac9a72a81f3ff73e787.tar.bz2 yuescript-b6725202f4a8cac5f829dac9a72a81f3ff73e787.zip | |
extend macro feature to support compiling Moonscript to other Lua dialect like teal.
add examples for how to write MoonPlus codes that compile to teal.
fix C++ macro to build without MoonPlus macro feature or built-in Lua.
add support for passing arguments from command line to compiler that can be accessed or altered by "require('moonp').options".
Diffstat (limited to 'src')
| -rw-r--r-- | src/MoonP/moon_ast.h | 2 | ||||
| -rw-r--r-- | src/MoonP/moon_compiler.cpp | 198 | ||||
| -rw-r--r-- | src/MoonP/moon_compiler.h | 15 | ||||
| -rw-r--r-- | src/MoonP/moon_parser.cpp | 2 | ||||
| -rw-r--r-- | src/MoonP/moonplus.cpp | 26 | ||||
| -rw-r--r-- | src/MoonP/moonplus.h | 2 | ||||
| -rw-r--r-- | src/moonp.cpp | 37 |
7 files changed, 215 insertions, 67 deletions
diff --git a/src/MoonP/moon_ast.h b/src/MoonP/moon_ast.h index 7e480b5..02428ca 100644 --- a/src/MoonP/moon_ast.h +++ b/src/MoonP/moon_ast.h | |||
| @@ -636,7 +636,7 @@ AST_LEAF(macro_type) | |||
| 636 | AST_END(macro_type) | 636 | AST_END(macro_type) |
| 637 | 637 | ||
| 638 | AST_NODE(MacroName) | 638 | AST_NODE(MacroName) |
| 639 | ast_ptr<true, Name_t> name; | 639 | ast_ptr<false, Name_t> name; |
| 640 | AST_MEMBER(MacroName, &name) | 640 | AST_MEMBER(MacroName, &name) |
| 641 | AST_END(MacroName) | 641 | AST_END(MacroName) |
| 642 | 642 | ||
diff --git a/src/MoonP/moon_compiler.cpp b/src/MoonP/moon_compiler.cpp index debc43a..f24441f 100644 --- a/src/MoonP/moon_compiler.cpp +++ b/src/MoonP/moon_compiler.cpp | |||
| @@ -53,7 +53,7 @@ inline std::string s(std::string_view sv) { | |||
| 53 | return std::string(sv); | 53 | return std::string(sv); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | const std::string_view version = "0.4.18"sv; | 56 | const std::string_view version = "0.4.19"sv; |
| 57 | const std::string_view extension = "mp"sv; | 57 | const std::string_view extension = "mp"sv; |
| 58 | 58 | ||
| 59 | class MoonCompilerImpl { | 59 | class MoonCompilerImpl { |
| @@ -89,10 +89,17 @@ public: | |||
| 89 | } | 89 | } |
| 90 | #endif // MOONP_NO_MACRO | 90 | #endif // MOONP_NO_MACRO |
| 91 | 91 | ||
| 92 | std::tuple<std::string,std::string,GlobalVars> compile(std::string_view codes, const MoonConfig& config) { | 92 | CompileInfo compile(std::string_view codes, const MoonConfig& config) { |
| 93 | _config = config; | 93 | _config = config; |
| 94 | #ifndef MOONP_NO_MACRO | ||
| 95 | if (L) passOptions(); | ||
| 96 | #endif // MOONP_NO_MACRO | ||
| 94 | _info = _parser.parse<File_t>(codes); | 97 | _info = _parser.parse<File_t>(codes); |
| 95 | GlobalVars globals; | 98 | std::unique_ptr<GlobalVars> globals; |
| 99 | std::unique_ptr<Options> options; | ||
| 100 | if (!config.options.empty()) { | ||
| 101 | options = std::make_unique<Options>(config.options); | ||
| 102 | } | ||
| 96 | DEFER(clear()); | 103 | DEFER(clear()); |
| 97 | if (_info.node) { | 104 | if (_info.node) { |
| 98 | try { | 105 | try { |
| @@ -104,19 +111,39 @@ public: | |||
| 104 | nullptr, true); | 111 | nullptr, true); |
| 105 | popScope(); | 112 | popScope(); |
| 106 | if (config.lintGlobalVariable) { | 113 | if (config.lintGlobalVariable) { |
| 107 | globals = std::make_unique<std::list<GlobalVar>>(); | 114 | globals = std::make_unique<GlobalVars>(); |
| 108 | for (const auto& var : _globals) { | 115 | for (const auto& var : _globals) { |
| 109 | int line,col; | 116 | int line,col; |
| 110 | std::tie(line,col) = var.second; | 117 | std::tie(line,col) = var.second; |
| 111 | globals->push_back({var.first, line, col}); | 118 | globals->push_back({var.first, line, col}); |
| 112 | } | 119 | } |
| 113 | } | 120 | } |
| 114 | return {std::move(out.back()), Empty, std::move(globals)}; | 121 | #ifndef MOONP_NO_MACRO |
| 122 | if (L) { | ||
| 123 | int top = lua_gettop(L); | ||
| 124 | DEFER(lua_settop(L, top)); | ||
| 125 | if (!options) { | ||
| 126 | options = std::make_unique<Options>(); | ||
| 127 | } | ||
| 128 | pushMoonp("options"sv); | ||
| 129 | lua_pushnil(L); // options startKey | ||
| 130 | while (lua_next(L, -2) != 0) { // options key value | ||
| 131 | size_t len = 0; | ||
| 132 | auto pstr = lua_tolstring(L, -2, &len); | ||
| 133 | std::string key{pstr, len}; | ||
| 134 | pstr = lua_tolstring(L, -1, &len); | ||
| 135 | std::string value{pstr, len}; | ||
| 136 | (*options)[key] = value; | ||
| 137 | lua_pop(L, 1); // options key | ||
| 138 | } | ||
| 139 | } | ||
| 140 | #endif // MOONP_NO_MACRO | ||
| 141 | return {std::move(out.back()), Empty, std::move(globals), std::move(options)}; | ||
| 115 | } catch (const std::logic_error& error) { | 142 | } catch (const std::logic_error& error) { |
| 116 | return {Empty, error.what(), std::move(globals)}; | 143 | return {Empty, error.what(), std::move(globals), std::move(options)}; |
| 117 | } | 144 | } |
| 118 | } else { | 145 | } else { |
| 119 | return {Empty, std::move(_info.error), std::move(globals)}; | 146 | return {Empty, std::move(_info.error), std::move(globals), std::move(options)}; |
| 120 | } | 147 | } |
| 121 | } | 148 | } |
| 122 | 149 | ||
| @@ -2150,6 +2177,17 @@ private: | |||
| 2150 | } | 2177 | } |
| 2151 | 2178 | ||
| 2152 | #ifndef MOONP_NO_MACRO | 2179 | #ifndef MOONP_NO_MACRO |
| 2180 | void passOptions() { | ||
| 2181 | if (!_config.options.empty()) { | ||
| 2182 | pushMoonp("options"sv); // options | ||
| 2183 | for (const auto& option : _config.options) { | ||
| 2184 | lua_pushlstring(L, option.second.c_str(), option.second.size()); | ||
| 2185 | lua_setfield(L, -2, option.first.c_str()); | ||
| 2186 | } | ||
| 2187 | lua_pop(L, 1); | ||
| 2188 | } | ||
| 2189 | } | ||
| 2190 | |||
| 2153 | void pushCurrentModule() { | 2191 | void pushCurrentModule() { |
| 2154 | if (_useModule) { | 2192 | if (_useModule) { |
| 2155 | lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE | 2193 | lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE |
| @@ -2165,6 +2203,7 @@ private: | |||
| 2165 | if (_luaOpen) { | 2203 | if (_luaOpen) { |
| 2166 | _luaOpen(static_cast<void*>(L)); | 2204 | _luaOpen(static_cast<void*>(L)); |
| 2167 | } | 2205 | } |
| 2206 | passOptions(); | ||
| 2168 | _stateOwner = true; | 2207 | _stateOwner = true; |
| 2169 | } | 2208 | } |
| 2170 | lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE | 2209 | lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE |
| @@ -2314,7 +2353,7 @@ private: | |||
| 2314 | out.push_back(Empty); | 2353 | out.push_back(Empty); |
| 2315 | } | 2354 | } |
| 2316 | #else | 2355 | #else |
| 2317 | void transformMacro(Macro_t* macro, str_list& out, bool exporting) { | 2356 | void transformMacro(Macro_t* macro, str_list&, bool) { |
| 2318 | throw std::logic_error(_info.errorMessage("macro feature not supported"sv, macro)); | 2357 | throw std::logic_error(_info.errorMessage("macro feature not supported"sv, macro)); |
| 2319 | } | 2358 | } |
| 2320 | #endif // MOONP_NO_MACRO | 2359 | #endif // MOONP_NO_MACRO |
| @@ -3023,16 +3062,67 @@ private: | |||
| 3023 | } | 3062 | } |
| 3024 | 3063 | ||
| 3025 | #ifndef MOONP_NO_MACRO | 3064 | #ifndef MOONP_NO_MACRO |
| 3026 | std::pair<std::string,std::string> expandMacroStr(ChainValue_t* chainValue) { | 3065 | std::tuple<std::string,std::string,str_list> expandMacroStr(ChainValue_t* chainValue) { |
| 3027 | const auto& chainList = chainValue->items.objects(); | 3066 | const auto& chainList = chainValue->items.objects(); |
| 3028 | auto x = ast_to<Callable_t>(chainList.front())->item.to<MacroName_t>(); | 3067 | auto x = ast_to<Callable_t>(chainList.front())->item.to<MacroName_t>(); |
| 3029 | auto macroName = _parser.toString(x->name); | 3068 | auto macroName = x->name ? _parser.toString(x->name) : Empty; |
| 3030 | if (!_useModule) { | 3069 | if (!macroName.empty() && !_useModule) { |
| 3031 | throw std::logic_error(_info.errorMessage("can not resolve macro"sv, x)); | 3070 | throw std::logic_error(_info.errorMessage("can not resolve macro"sv, x)); |
| 3032 | } | 3071 | } |
| 3033 | pushCurrentModule(); // cur | 3072 | pushCurrentModule(); // cur |
| 3034 | int top = lua_gettop(L) - 1; | 3073 | int top = lua_gettop(L) - 1; |
| 3035 | DEFER(lua_settop(L, top)); | 3074 | DEFER(lua_settop(L, top)); |
| 3075 | if (macroName.empty()) { | ||
| 3076 | lua_pop(L, 1); // empty | ||
| 3077 | auto item = *(++chainList.begin()); | ||
| 3078 | const node_container* args = nullptr; | ||
| 3079 | if (auto invoke = ast_cast<Invoke_t>(item)) { | ||
| 3080 | args = &invoke->args.objects(); | ||
| 3081 | } else { | ||
| 3082 | args = &ast_to<InvokeArgs_t>(item)->args.objects(); | ||
| 3083 | } | ||
| 3084 | if (args->size() != 1) { | ||
| 3085 | throw std::logic_error(_info.errorMessage("in-place macro must be followed by a compile time function"sv, x)); | ||
| 3086 | } | ||
| 3087 | auto fcodes = _parser.toString(args->back()); | ||
| 3088 | Utils::trim(fcodes); | ||
| 3089 | pushMoonp("loadstring"sv); // loadstring | ||
| 3090 | lua_pushlstring(L, fcodes.c_str(), fcodes.size()); // loadstring codes | ||
| 3091 | lua_pushliteral(L, "=(macro in-place)"); // loadstring codes chunk | ||
| 3092 | pushOptions(args->back()->m_begin.m_line - 1); // loadstring codes chunk options | ||
| 3093 | if (lua_pcall(L, 3, 2, 0) != 0) { // loadstring(codes,chunk,options), f err | ||
| 3094 | std::string err = lua_tostring(L, -1); | ||
| 3095 | throw std::logic_error(_info.errorMessage(s("fail to load macro codes\n"sv) + err, x)); | ||
| 3096 | } // f err | ||
| 3097 | if (lua_isnil(L, -2) != 0) { // f == nil, f err | ||
| 3098 | std::string err = lua_tostring(L, -1); | ||
| 3099 | throw std::logic_error(_info.errorMessage(s("fail to load macro codes, at (macro in-place): "sv) + err, x)); | ||
| 3100 | } | ||
| 3101 | lua_pop(L, 1); // f | ||
| 3102 | pushMoonp("pcall"sv); // f pcall | ||
| 3103 | lua_insert(L, -2); // pcall f | ||
| 3104 | if (lua_pcall(L, 1, 2, 0) != 0) { // f(), success macroFunc | ||
| 3105 | std::string err = lua_tostring(L, -1); | ||
| 3106 | throw std::logic_error(_info.errorMessage(s("fail to generate macro function\n"sv) + err, x)); | ||
| 3107 | } // success res | ||
| 3108 | if (lua_toboolean(L, -2) == 0) { | ||
| 3109 | std::string err = lua_tostring(L, -1); | ||
| 3110 | throw std::logic_error(_info.errorMessage(s("fail to generate macro function\n"sv) + err, x)); | ||
| 3111 | } // true macroFunc | ||
| 3112 | lua_remove(L, -2); // macroFunc | ||
| 3113 | pushMoonp("pcall"sv); // macroFunc pcall | ||
| 3114 | lua_insert(L, -2); // pcall macroFunc | ||
| 3115 | bool success = lua_pcall(L, 1, 2, 0) == 0; | ||
| 3116 | if (!success) { // err | ||
| 3117 | std::string err = lua_tostring(L, -1); | ||
| 3118 | throw std::logic_error(_info.errorMessage(s("fail to expand macro: "sv) + err, x)); | ||
| 3119 | } // success err | ||
| 3120 | if (lua_toboolean(L, -2) == 0) { | ||
| 3121 | std::string err = lua_tostring(L, -1); | ||
| 3122 | throw std::logic_error(_info.errorMessage(s("fail to expand macro: "sv) + err, x)); | ||
| 3123 | } | ||
| 3124 | return {s("block"sv), Empty, {}}; | ||
| 3125 | } | ||
| 3036 | lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macroName | 3126 | lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macroName |
| 3037 | lua_rawget(L, -2); // cur[macroName], cur macro | 3127 | lua_rawget(L, -2); // cur[macroName], cur macro |
| 3038 | if (lua_istable(L, -1) == 0) { | 3128 | if (lua_istable(L, -1) == 0) { |
| @@ -3060,9 +3150,7 @@ private: | |||
| 3060 | BREAK_IF(!chainValue); | 3150 | BREAK_IF(!chainValue); |
| 3061 | BREAK_IF(!isMacroChain(chainValue)); | 3151 | BREAK_IF(!isMacroChain(chainValue)); |
| 3062 | BREAK_IF(chainValue->items.size() != 2); | 3152 | BREAK_IF(chainValue->items.size() != 2); |
| 3063 | std::string type, codes; | 3153 | str = std::get<1>(expandMacroStr(chainValue)); |
| 3064 | std::tie(type, codes) = expandMacroStr(chainValue); | ||
| 3065 | str = codes; | ||
| 3066 | BLOCK_END | 3154 | BLOCK_END |
| 3067 | if (str.empty()) { | 3155 | if (str.empty()) { |
| 3068 | // exp is reassembled due to backcall expressions | 3156 | // exp is reassembled due to backcall expressions |
| @@ -3092,30 +3180,55 @@ private: | |||
| 3092 | Utils::replace(str, "\r\n"sv, "\n"sv); | 3180 | Utils::replace(str, "\r\n"sv, "\n"sv); |
| 3093 | lua_pushlstring(L, str.c_str(), str.size()); | 3181 | lua_pushlstring(L, str.c_str(), str.size()); |
| 3094 | } // cur macro pcall func args... | 3182 | } // cur macro pcall func args... |
| 3095 | bool success = lua_pcall(L, static_cast<int>(args->size()) + 1, 2, 0) == 0; | 3183 | bool success = lua_pcall(L, static_cast<int>(args->size()) + 1, 3, 0) == 0; |
| 3096 | if (!success) { // cur macro err | 3184 | if (!success) { // cur macro err |
| 3097 | std::string err = lua_tostring(L, -1); | 3185 | std::string err = lua_tostring(L, -1); |
| 3098 | throw std::logic_error(_info.errorMessage(s("fail to expand macro: "sv) + err, x)); | 3186 | throw std::logic_error(_info.errorMessage(s("fail to expand macro: "sv) + err, x)); |
| 3099 | } // cur macro success res | 3187 | } // cur macro success res option |
| 3100 | if (lua_toboolean(L, -2) == 0) { | 3188 | if (lua_toboolean(L, -3) == 0) { |
| 3101 | std::string err = lua_tostring(L, -1); | 3189 | std::string err = lua_tostring(L, -2); |
| 3102 | throw std::logic_error(_info.errorMessage(s("fail to expand macro: "sv) + err, x)); | 3190 | throw std::logic_error(_info.errorMessage(s("fail to expand macro: "sv) + err, x)); |
| 3103 | } | 3191 | } |
| 3104 | lua_remove(L, -2); // cur macro res | 3192 | lua_remove(L, -3); // cur macro res option |
| 3105 | if (lua_isstring(L, -1) == 0) { | 3193 | if (lua_isstring(L, -2) == 0) { |
| 3106 | throw std::logic_error(_info.errorMessage(s("macro function must return string with expanded codes"sv), x)); | 3194 | throw std::logic_error(_info.errorMessage(s("macro function must return string with expanded codes"sv), x)); |
| 3107 | } // cur macro codes | 3195 | } // cur macro codes option |
| 3108 | lua_rawgeti(L, -2, 2); // cur macro codes type | 3196 | lua_rawgeti(L, -3, 2); // cur macro codes option type |
| 3109 | std::string type = lua_tostring(L, -1); | 3197 | std::string type = lua_tostring(L, -1); |
| 3198 | lua_pop(L, 1); // cur macro codes option | ||
| 3199 | str_list localVars; | ||
| 3200 | if (lua_isnil(L, -1) == 0) { | ||
| 3201 | if (lua_istable(L, -1) == 0) { | ||
| 3202 | throw std::logic_error(_info.errorMessage(s("macro function must return expanded codes followed by a config table"sv), x)); | ||
| 3203 | } | ||
| 3204 | if (type == "expr"sv || type == "block"sv) { | ||
| 3205 | throw std::logic_error(_info.errorMessage(s("expr or block macro is not accepting config table"sv), x)); | ||
| 3206 | } | ||
| 3207 | for (int i = 0; i < static_cast<int>(lua_objlen(L, -1)); i++) { | ||
| 3208 | lua_rawgeti(L, -1, i + 1); // cur macro codes option item | ||
| 3209 | size_t len = 0; | ||
| 3210 | if (lua_isstring(L, -1) == 0) { | ||
| 3211 | throw std::logic_error(_info.errorMessage(s("macro config table must contains strings"sv), x)); | ||
| 3212 | } | ||
| 3213 | auto name = lua_tolstring(L, -1, &len); | ||
| 3214 | if (_parser.match<Variable_t>({name, len})) { | ||
| 3215 | localVars.push_back(std::string(name, len)); | ||
| 3216 | } else { | ||
| 3217 | throw std::logic_error(_info.errorMessage(s("macro config table must contains names for local variables, got \""sv) + std::string(name, len) + '"', x)); | ||
| 3218 | } | ||
| 3219 | lua_pop(L, 1); | ||
| 3220 | } | ||
| 3221 | } // cur macro codes option | ||
| 3110 | std::string codes = lua_tostring(L, -2); | 3222 | std::string codes = lua_tostring(L, -2); |
| 3111 | return {type, codes}; | 3223 | return {type, codes, std::move(localVars)}; |
| 3112 | } | 3224 | } |
| 3113 | 3225 | ||
| 3114 | std::tuple<ast_ptr<false,ast_node>, std::unique_ptr<input>, std::string> expandMacro(ChainValue_t* chainValue, ExpUsage usage, bool allowBlockMacroReturn) { | 3226 | std::tuple<ast_ptr<false,ast_node>, std::unique_ptr<input>, std::string, str_list> expandMacro(ChainValue_t* chainValue, ExpUsage usage, bool allowBlockMacroReturn) { |
| 3115 | auto x = ast_to<Callable_t>(chainValue->items.front())->item.to<MacroName_t>(); | 3227 | auto x = ast_to<Callable_t>(chainValue->items.front())->item.to<MacroName_t>(); |
| 3116 | const auto& chainList = chainValue->items.objects(); | 3228 | const auto& chainList = chainValue->items.objects(); |
| 3117 | std::string type, codes; | 3229 | std::string type, codes; |
| 3118 | std::tie(type, codes) = expandMacroStr(chainValue); | 3230 | str_list localVars; |
| 3231 | std::tie(type, codes, localVars) = expandMacroStr(chainValue); | ||
| 3119 | std::string targetType(usage != ExpUsage::Common || chainList.size() > 2 ? "expr"sv : "block"sv); | 3232 | std::string targetType(usage != ExpUsage::Common || chainList.size() > 2 ? "expr"sv : "block"sv); |
| 3120 | if (type == "lua"sv) { | 3233 | if (type == "lua"sv) { |
| 3121 | if (!allowBlockMacroReturn && targetType != "block"sv) { | 3234 | if (!allowBlockMacroReturn && targetType != "block"sv) { |
| @@ -3128,19 +3241,19 @@ private: | |||
| 3128 | std::string err = lua_tostring(L, -1); | 3241 | std::string err = lua_tostring(L, -1); |
| 3129 | throw std::logic_error(_info.errorMessage(err, x)); | 3242 | throw std::logic_error(_info.errorMessage(err, x)); |
| 3130 | } | 3243 | } |
| 3131 | return {nullptr, nullptr, std::move(codes)}; | 3244 | return {nullptr, nullptr, std::move(codes), std::move(localVars)}; |
| 3132 | } else if (type == "text"sv) { | 3245 | } else if (type == "text"sv) { |
| 3133 | if (!allowBlockMacroReturn && targetType != "block"sv) { | 3246 | if (!allowBlockMacroReturn && targetType != "block"sv) { |
| 3134 | throw std::logic_error(_info.errorMessage("text macro can only be placed where block macro is allowed"sv, x)); | 3247 | throw std::logic_error(_info.errorMessage("text macro can only be placed where block macro is allowed"sv, x)); |
| 3135 | } | 3248 | } |
| 3136 | return {nullptr, nullptr, std::move(codes)}; | 3249 | return {nullptr, nullptr, std::move(codes), std::move(localVars)}; |
| 3137 | } else if (!allowBlockMacroReturn && type != targetType) { | 3250 | } else if (!allowBlockMacroReturn && type != targetType) { |
| 3138 | throw std::logic_error(_info.errorMessage(s("macro type mismatch, "sv) + targetType + s(" expected, got "sv) + type, x)); | 3251 | throw std::logic_error(_info.errorMessage(s("macro type mismatch, "sv) + targetType + s(" expected, got "sv) + type, x)); |
| 3139 | } | 3252 | } |
| 3140 | ParseInfo info; | 3253 | ParseInfo info; |
| 3141 | if (usage == ExpUsage::Common) { | 3254 | if (usage == ExpUsage::Common) { |
| 3142 | if (codes.empty()) { | 3255 | if (codes.empty()) { |
| 3143 | return {x->new_ptr<Block_t>().get(), std::move(info.codes), Empty}; | 3256 | return {x->new_ptr<Block_t>().get(), std::move(info.codes), Empty, std::move(localVars)}; |
| 3144 | } | 3257 | } |
| 3145 | if (type == "expr"sv) { | 3258 | if (type == "expr"sv) { |
| 3146 | info = _parser.parse<Exp_t>(codes); | 3259 | info = _parser.parse<Exp_t>(codes); |
| @@ -3202,7 +3315,7 @@ private: | |||
| 3202 | info.node.set(exp); | 3315 | info.node.set(exp); |
| 3203 | } | 3316 | } |
| 3204 | } | 3317 | } |
| 3205 | return {info.node, std::move(info.codes), Empty}; | 3318 | return {info.node, std::move(info.codes), Empty, std::move(localVars)}; |
| 3206 | } | 3319 | } |
| 3207 | #endif // MOONP_NO_MACRO | 3320 | #endif // MOONP_NO_MACRO |
| 3208 | 3321 | ||
| @@ -3212,10 +3325,11 @@ private: | |||
| 3212 | ast_ptr<false,ast_node> node; | 3325 | ast_ptr<false,ast_node> node; |
| 3213 | std::unique_ptr<input> codes; | 3326 | std::unique_ptr<input> codes; |
| 3214 | std::string luaCodes; | 3327 | std::string luaCodes; |
| 3215 | std::tie(node, codes, luaCodes) = expandMacro(chainValue, usage, allowBlockMacroReturn); | 3328 | str_list localVars; |
| 3329 | std::tie(node, codes, luaCodes, localVars) = expandMacro(chainValue, usage, allowBlockMacroReturn); | ||
| 3216 | Utils::replace(luaCodes, "\r\n"sv, "\n"sv); | 3330 | Utils::replace(luaCodes, "\r\n"sv, "\n"sv); |
| 3217 | Utils::trim(luaCodes); | 3331 | Utils::trim(luaCodes); |
| 3218 | if (!node && !codes) { | 3332 | if (!node) { |
| 3219 | if (!luaCodes.empty()) { | 3333 | if (!luaCodes.empty()) { |
| 3220 | if (_config.reserveLineNumber) { | 3334 | if (_config.reserveLineNumber) { |
| 3221 | luaCodes.insert(0, nll(chainValue).substr(1)); | 3335 | luaCodes.insert(0, nll(chainValue).substr(1)); |
| @@ -3223,6 +3337,11 @@ private: | |||
| 3223 | luaCodes.append(nlr(chainValue)); | 3337 | luaCodes.append(nlr(chainValue)); |
| 3224 | } | 3338 | } |
| 3225 | out.push_back(luaCodes); | 3339 | out.push_back(luaCodes); |
| 3340 | if (!localVars.empty()) { | ||
| 3341 | for (const auto& var : localVars) { | ||
| 3342 | addToScope(var); | ||
| 3343 | } | ||
| 3344 | } | ||
| 3226 | return; | 3345 | return; |
| 3227 | } | 3346 | } |
| 3228 | if (usage == ExpUsage::Common || (usage == ExpUsage::Return && node.is<Block_t>())) { | 3347 | if (usage == ExpUsage::Common || (usage == ExpUsage::Return && node.is<Block_t>())) { |
| @@ -3254,6 +3373,7 @@ private: | |||
| 3254 | } | 3373 | } |
| 3255 | return; | 3374 | return; |
| 3256 | #else | 3375 | #else |
| 3376 | (void)allowBlockMacroReturn; | ||
| 3257 | throw std::logic_error(_info.errorMessage("macro feature not supported"sv, chainValue)); | 3377 | throw std::logic_error(_info.errorMessage("macro feature not supported"sv, chainValue)); |
| 3258 | #endif // MOONP_NO_MACRO | 3378 | #endif // MOONP_NO_MACRO |
| 3259 | } | 3379 | } |
| @@ -4983,11 +5103,9 @@ private: | |||
| 4983 | config.lintGlobalVariable = false; | 5103 | config.lintGlobalVariable = false; |
| 4984 | config.reserveLineNumber = false; | 5104 | config.reserveLineNumber = false; |
| 4985 | config.implicitReturnRoot = _config.implicitReturnRoot; | 5105 | config.implicitReturnRoot = _config.implicitReturnRoot; |
| 4986 | std::string codes, err; | 5106 | auto result = compiler.compile(text, config); |
| 4987 | GlobalVars globals; | 5107 | if (result.codes.empty() && !result.error.empty()) { |
| 4988 | std::tie(codes, err, globals) = compiler.compile(text, config); | 5108 | throw std::logic_error(_info.errorMessage(s("fail to compile module '"sv) + moduleName + s("\': "sv) + result.error, x)); |
| 4989 | if (codes.empty() && !err.empty()) { | ||
| 4990 | throw std::logic_error(_info.errorMessage(s("fail to compile module '"sv) + moduleName + s("\': "sv) + err, x)); | ||
| 4991 | } | 5109 | } |
| 4992 | lua_pop(L, 1); // cur | 5110 | lua_pop(L, 1); // cur |
| 4993 | } | 5111 | } |
| @@ -5325,12 +5443,16 @@ MoonCompiler::MoonCompiler(void* sharedState, | |||
| 5325 | #ifndef MOONP_NO_MACRO | 5443 | #ifndef MOONP_NO_MACRO |
| 5326 | _compiler(std::make_unique<MoonCompilerImpl>(static_cast<lua_State*>(sharedState), luaOpen, sameModule)) {} | 5444 | _compiler(std::make_unique<MoonCompilerImpl>(static_cast<lua_State*>(sharedState), luaOpen, sameModule)) {} |
| 5327 | #else | 5445 | #else |
| 5328 | _compiler(std::make_unique<MoonCompilerImpl>()) {} | 5446 | _compiler(std::make_unique<MoonCompilerImpl>()) { |
| 5447 | (void)sharedState; | ||
| 5448 | (void)luaOpen; | ||
| 5449 | (void)sameModule; | ||
| 5450 | } | ||
| 5329 | #endif // MOONP_NO_MACRO | 5451 | #endif // MOONP_NO_MACRO |
| 5330 | 5452 | ||
| 5331 | MoonCompiler::~MoonCompiler() {} | 5453 | MoonCompiler::~MoonCompiler() {} |
| 5332 | 5454 | ||
| 5333 | std::tuple<std::string,std::string,GlobalVars> MoonCompiler::compile(std::string_view codes, const MoonConfig& config) { | 5455 | CompileInfo MoonCompiler::compile(std::string_view codes, const MoonConfig& config) { |
| 5334 | return _compiler->compile(codes, config); | 5456 | return _compiler->compile(codes, config); |
| 5335 | } | 5457 | } |
| 5336 | 5458 | ||
diff --git a/src/MoonP/moon_compiler.h b/src/MoonP/moon_compiler.h index cb57d57..a1a5ce5 100644 --- a/src/MoonP/moon_compiler.h +++ b/src/MoonP/moon_compiler.h | |||
| @@ -13,6 +13,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI | |||
| 13 | #include <tuple> | 13 | #include <tuple> |
| 14 | #include <list> | 14 | #include <list> |
| 15 | #include <memory> | 15 | #include <memory> |
| 16 | #include <unordered_map> | ||
| 16 | #include <functional> | 17 | #include <functional> |
| 17 | 18 | ||
| 18 | namespace MoonP { | 19 | namespace MoonP { |
| @@ -20,12 +21,15 @@ namespace MoonP { | |||
| 20 | extern const std::string_view version; | 21 | extern const std::string_view version; |
| 21 | extern const std::string_view extension; | 22 | extern const std::string_view extension; |
| 22 | 23 | ||
| 24 | using Options = std::unordered_map<std::string,std::string>; | ||
| 25 | |||
| 23 | struct MoonConfig { | 26 | struct MoonConfig { |
| 24 | bool lintGlobalVariable = false; | 27 | bool lintGlobalVariable = false; |
| 25 | bool implicitReturnRoot = true; | 28 | bool implicitReturnRoot = true; |
| 26 | bool reserveLineNumber = true; | 29 | bool reserveLineNumber = true; |
| 27 | bool useSpaceOverTab = false; | 30 | bool useSpaceOverTab = false; |
| 28 | int lineOffset = 0; | 31 | int lineOffset = 0; |
| 32 | Options options; | ||
| 29 | }; | 33 | }; |
| 30 | 34 | ||
| 31 | struct GlobalVar { | 35 | struct GlobalVar { |
| @@ -34,7 +38,14 @@ struct GlobalVar { | |||
| 34 | int col; | 38 | int col; |
| 35 | }; | 39 | }; |
| 36 | 40 | ||
| 37 | using GlobalVars = std::unique_ptr<std::list<GlobalVar>>; | 41 | using GlobalVars = std::list<GlobalVar>; |
| 42 | |||
| 43 | struct CompileInfo { | ||
| 44 | std::string codes; | ||
| 45 | std::string error; | ||
| 46 | std::unique_ptr<GlobalVars> globals; | ||
| 47 | std::unique_ptr<Options> options; | ||
| 48 | }; | ||
| 38 | 49 | ||
| 39 | class MoonCompilerImpl; | 50 | class MoonCompilerImpl; |
| 40 | 51 | ||
| @@ -44,7 +55,7 @@ public: | |||
| 44 | const std::function<void(void*)>& luaOpen = nullptr, | 55 | const std::function<void(void*)>& luaOpen = nullptr, |
| 45 | bool sameModule = false); | 56 | bool sameModule = false); |
| 46 | virtual ~MoonCompiler(); | 57 | virtual ~MoonCompiler(); |
| 47 | std::tuple<std::string,std::string,GlobalVars> compile(std::string_view codes, const MoonConfig& config = {}); | 58 | CompileInfo compile(std::string_view codes, const MoonConfig& config = {}); |
| 48 | private: | 59 | private: |
| 49 | std::unique_ptr<MoonCompilerImpl> _compiler; | 60 | std::unique_ptr<MoonCompilerImpl> _compiler; |
| 50 | }; | 61 | }; |
diff --git a/src/MoonP/moon_parser.cpp b/src/MoonP/moon_parser.cpp index e984997..63eabb3 100644 --- a/src/MoonP/moon_parser.cpp +++ b/src/MoonP/moon_parser.cpp | |||
| @@ -509,7 +509,7 @@ MoonParser::MoonParser() { | |||
| 509 | fn_arrow = expr("->") | expr("=>"); | 509 | fn_arrow = expr("->") | expr("=>"); |
| 510 | FunLit = -FnArgsDef >> Space >> fn_arrow >> -Body; | 510 | FunLit = -FnArgsDef >> Space >> fn_arrow >> -Body; |
| 511 | 511 | ||
| 512 | MacroName = expr('$') >> Name; | 512 | MacroName = expr('$') >> -Name; |
| 513 | macro_type = expr("expr") | expr("block") | expr("lua") | expr("text"); | 513 | macro_type = expr("expr") | expr("block") | expr("lua") | expr("text"); |
| 514 | macro_args_def = sym('(') >> White >> -FnArgDefList >> White >> sym(')'); | 514 | macro_args_def = sym('(') >> White >> -FnArgDefList >> White >> sym(')'); |
| 515 | MacroLit = -macro_args_def >> Space >> expr("->") >> Body; | 515 | MacroLit = -macro_args_def >> Space >> expr("->") >> Body; |
diff --git a/src/MoonP/moonplus.cpp b/src/MoonP/moonplus.cpp index c775d75..bea1d6e 100644 --- a/src/MoonP/moonplus.cpp +++ b/src/MoonP/moonplus.cpp | |||
| @@ -84,23 +84,21 @@ static int moontolua(lua_State* L) { | |||
| 84 | lua_pop(L, 1); | 84 | lua_pop(L, 1); |
| 85 | } | 85 | } |
| 86 | std::string s(input, size); | 86 | std::string s(input, size); |
| 87 | std::string codes, err; | 87 | auto result = MoonP::MoonCompiler(L, nullptr, sameModule).compile(s, config); |
| 88 | MoonP::GlobalVars globals; | 88 | if (result.codes.empty() && !result.error.empty()) { |
| 89 | std::tie(codes, err, globals) = MoonP::MoonCompiler(L, nullptr, sameModule).compile(s, config); | ||
| 90 | if (codes.empty() && !err.empty()) { | ||
| 91 | lua_pushnil(L); | 89 | lua_pushnil(L); |
| 92 | } else { | 90 | } else { |
| 93 | lua_pushlstring(L, codes.c_str(), codes.size()); | 91 | lua_pushlstring(L, result.codes.c_str(), result.codes.size()); |
| 94 | } | 92 | } |
| 95 | if (err.empty()) { | 93 | if (result.error.empty()) { |
| 96 | lua_pushnil(L); | 94 | lua_pushnil(L); |
| 97 | } else { | 95 | } else { |
| 98 | lua_pushlstring(L, err.c_str(), err.size()); | 96 | lua_pushlstring(L, result.error.c_str(), result.error.size()); |
| 99 | } | 97 | } |
| 100 | if (globals) { | 98 | if (result.globals) { |
| 101 | lua_createtable(L, static_cast<int>(globals->size()), 0); | 99 | lua_createtable(L, static_cast<int>(result.globals->size()), 0); |
| 102 | int i = 1; | 100 | int i = 1; |
| 103 | for (const auto& var : *globals) { | 101 | for (const auto& var : *result.globals) { |
| 104 | lua_createtable(L, 3, 0); | 102 | lua_createtable(L, 3, 0); |
| 105 | lua_pushlstring(L, var.name.c_str(), var.name.size()); | 103 | lua_pushlstring(L, var.name.c_str(), var.name.size()); |
| 106 | lua_rawseti(L, -2, 1); | 104 | lua_rawseti(L, -2, 1); |
| @@ -124,9 +122,11 @@ int luaopen_moonp(lua_State* L) { | |||
| 124 | lua_pushcfunction(L, moontolua); // package loaded moonp func | 122 | lua_pushcfunction(L, moontolua); // package loaded moonp func |
| 125 | lua_setfield(L, -2, "to_lua"); // moonp["to_lua"] = func, package loaded moonp | 123 | lua_setfield(L, -2, "to_lua"); // moonp["to_lua"] = func, package loaded moonp |
| 126 | lua_pushlstring(L, &MoonP::version.front(), MoonP::version.size()); // package loaded moonp version | 124 | lua_pushlstring(L, &MoonP::version.front(), MoonP::version.size()); // package loaded moonp version |
| 127 | lua_setfield(L, -2, "version"); // loaded["version"] = version, package loaded moonp | 125 | lua_setfield(L, -2, "version"); // moonp["version"] = version, package loaded moonp |
| 128 | lua_pushlstring(L, &MoonP::extension.front(), MoonP::extension.size()); // package loaded moonp ext | 126 | lua_createtable(L, 0, 0); // package loaded moonp options |
| 129 | lua_setfield(L, -2, "extension"); // loaded["extension"] = ext, package loaded moonp | 127 | lua_pushlstring(L, &MoonP::extension.front(), MoonP::extension.size()); // package loaded moonp options ext |
| 128 | lua_setfield(L, -2, "extension"); // options["extension"] = ext, package loaded moonp options | ||
| 129 | lua_setfield(L, -2, "options"); // moonp["options"] = options, package loaded moonp | ||
| 130 | lua_pushcfunction(L, init_stacktraceplus); // package loaded moonp func1 | 130 | lua_pushcfunction(L, init_stacktraceplus); // package loaded moonp func1 |
| 131 | lua_setfield(L, -2, "load_stacktraceplus"); // moonp["load_stacktraceplus"] = func1, package loaded moonp | 131 | lua_setfield(L, -2, "load_stacktraceplus"); // moonp["load_stacktraceplus"] = func1, package loaded moonp |
| 132 | lua_setfield(L, -2, "moonp"); // loaded["moonp"] = moonp, package loaded | 132 | lua_setfield(L, -2, "moonp"); // loaded["moonp"] = moonp, package loaded |
diff --git a/src/MoonP/moonplus.h b/src/MoonP/moonplus.h index b18ab64..ff82e9f 100644 --- a/src/MoonP/moonplus.h +++ b/src/MoonP/moonplus.h | |||
| @@ -73,7 +73,7 @@ get_options = function(...) | |||
| 73 | end | 73 | end |
| 74 | end | 74 | end |
| 75 | create_moonpath = function(package_path) | 75 | create_moonpath = function(package_path) |
| 76 | local extension = moonp.extension | 76 | local extension = moonp.options.extension |
| 77 | local moonpaths | 77 | local moonpaths |
| 78 | do | 78 | do |
| 79 | local _accum_0 = { } | 79 | local _accum_0 = { } |
diff --git a/src/moonp.cpp b/src/moonp.cpp index 25be015..1e4302e 100644 --- a/src/moonp.cpp +++ b/src/moonp.cpp | |||
| @@ -24,7 +24,7 @@ using namespace std::string_view_literals; | |||
| 24 | #include "ghc/fs_std.hpp" | 24 | #include "ghc/fs_std.hpp" |
| 25 | #include "linenoise.hpp" | 25 | #include "linenoise.hpp" |
| 26 | 26 | ||
| 27 | #ifndef MOONP_NO_MACRO | 27 | #if not (defined MOONP_NO_MACRO && defined MOONP_COMPILER_ONLY) |
| 28 | #define _DEFER(code,line) std::shared_ptr<void> _defer_##line(nullptr, [&](auto){code;}) | 28 | #define _DEFER(code,line) std::shared_ptr<void> _defer_##line(nullptr, [&](auto){code;}) |
| 29 | #define DEFER(code) _DEFER(code,__LINE__) | 29 | #define DEFER(code) _DEFER(code,__LINE__) |
| 30 | extern "C" { | 30 | extern "C" { |
| @@ -71,7 +71,9 @@ void pushOptions(lua_State* L, int lineOffset) { | |||
| 71 | lua_pushinteger(L, lineOffset); | 71 | lua_pushinteger(L, lineOffset); |
| 72 | lua_rawset(L, -3); | 72 | lua_rawset(L, -3); |
| 73 | } | 73 | } |
| 74 | #endif // not (defined MOONP_NO_MACRO && defined MOONP_COMPILER_ONLY) | ||
| 74 | 75 | ||
| 76 | #ifndef MOONP_NO_MACRO | ||
| 75 | #define MOONP_ARGS nullptr,openlibs | 77 | #define MOONP_ARGS nullptr,openlibs |
| 76 | #else | 78 | #else |
| 77 | #define MOONP_ARGS | 79 | #define MOONP_ARGS |
| @@ -278,12 +280,12 @@ int main(int narg, const char** args) { | |||
| 278 | conf.reserveLineNumber = false; | 280 | conf.reserveLineNumber = false; |
| 279 | conf.useSpaceOverTab = true; | 281 | conf.useSpaceOverTab = true; |
| 280 | auto result = MoonP::MoonCompiler{MOONP_ARGS}.compile(codes, conf); | 282 | auto result = MoonP::MoonCompiler{MOONP_ARGS}.compile(codes, conf); |
| 281 | if (std::get<1>(result).empty()) { | 283 | if (result.error.empty()) { |
| 282 | std::cout << std::get<0>(result); | 284 | std::cout << result.codes; |
| 283 | return 0; | 285 | return 0; |
| 284 | } else { | 286 | } else { |
| 285 | std::ostringstream buf; | 287 | std::ostringstream buf; |
| 286 | std::cout << std::get<1>(result) << '\n'; | 288 | std::cout << result.error << '\n'; |
| 287 | return 1; | 289 | return 1; |
| 288 | } | 290 | } |
| 289 | #ifndef MOONP_COMPILER_ONLY | 291 | #ifndef MOONP_COMPILER_ONLY |
| @@ -376,6 +378,12 @@ int main(int narg, const char** args) { | |||
| 376 | std::cout << help; | 378 | std::cout << help; |
| 377 | return 1; | 379 | return 1; |
| 378 | } | 380 | } |
| 381 | } else if (arg.substr(0, 1) == "-"sv && arg.find('=') != std::string::npos) { | ||
| 382 | auto argStr = arg.substr(1); | ||
| 383 | size_t idx = argStr.find('='); | ||
| 384 | auto key = argStr.substr(0, idx); | ||
| 385 | auto value = argStr.substr(idx + 1); | ||
| 386 | config.options[key] = value; | ||
| 379 | } else { | 387 | } else { |
| 380 | if (fs::is_directory(arg)) { | 388 | if (fs::is_directory(arg)) { |
| 381 | for (auto item : fs::recursive_directory_iterator(arg)) { | 389 | for (auto item : fs::recursive_directory_iterator(arg)) { |
| @@ -412,7 +420,7 @@ int main(int narg, const char** args) { | |||
| 412 | auto start = std::chrono::high_resolution_clock::now(); | 420 | auto start = std::chrono::high_resolution_clock::now(); |
| 413 | auto result = MoonP::MoonCompiler{MOONP_ARGS}.compile(s, config); | 421 | auto result = MoonP::MoonCompiler{MOONP_ARGS}.compile(s, config); |
| 414 | auto end = std::chrono::high_resolution_clock::now(); | 422 | auto end = std::chrono::high_resolution_clock::now(); |
| 415 | if (!std::get<0>(result).empty()) { | 423 | if (!result.codes.empty()) { |
| 416 | std::chrono::duration<double> diff = end - start; | 424 | std::chrono::duration<double> diff = end - start; |
| 417 | start = std::chrono::high_resolution_clock::now(); | 425 | start = std::chrono::high_resolution_clock::now(); |
| 418 | MoonP::MoonParser{}.parse<MoonP::File_t>(s); | 426 | MoonP::MoonParser{}.parse<MoonP::File_t>(s); |
| @@ -426,15 +434,22 @@ int main(int narg, const char** args) { | |||
| 426 | } else { | 434 | } else { |
| 427 | std::ostringstream buf; | 435 | std::ostringstream buf; |
| 428 | buf << "Fail to compile: "sv << file.first << ".\n"sv; | 436 | buf << "Fail to compile: "sv << file.first << ".\n"sv; |
| 429 | buf << std::get<1>(result) << '\n'; | 437 | buf << result.error << '\n'; |
| 430 | return std::tuple{1, file.first, buf.str()}; | 438 | return std::tuple{1, file.first, buf.str()}; |
| 431 | } | 439 | } |
| 432 | } | 440 | } |
| 433 | auto result = MoonP::MoonCompiler{MOONP_ARGS}.compile(s, config); | 441 | auto result = MoonP::MoonCompiler{MOONP_ARGS}.compile(s, config); |
| 434 | if (std::get<1>(result).empty()) { | 442 | if (result.error.empty()) { |
| 435 | if (!writeToFile) { | 443 | if (!writeToFile) { |
| 436 | return std::tuple{0, file.first, std::get<0>(result) + '\n'}; | 444 | return std::tuple{0, file.first, result.codes + '\n'}; |
| 437 | } else { | 445 | } else { |
| 446 | std::string targetExtension("lua"sv); | ||
| 447 | if (result.options) { | ||
| 448 | auto it = result.options->find("target_extension"); | ||
| 449 | if (it != result.options->end()) { | ||
| 450 | targetExtension = it->second; | ||
| 451 | } | ||
| 452 | } | ||
| 438 | fs::path targetFile; | 453 | fs::path targetFile; |
| 439 | if (!resultFile.empty()) { | 454 | if (!resultFile.empty()) { |
| 440 | targetFile = resultFile; | 455 | targetFile = resultFile; |
| @@ -444,14 +459,14 @@ int main(int narg, const char** args) { | |||
| 444 | } else { | 459 | } else { |
| 445 | targetFile = file.first; | 460 | targetFile = file.first; |
| 446 | } | 461 | } |
| 447 | targetFile.replace_extension(".lua"sv); | 462 | targetFile.replace_extension('.' + targetExtension); |
| 448 | } | 463 | } |
| 449 | if (!targetPath.empty()) { | 464 | if (!targetPath.empty()) { |
| 450 | fs::create_directories(targetFile.parent_path()); | 465 | fs::create_directories(targetFile.parent_path()); |
| 451 | } | 466 | } |
| 452 | std::ofstream output(targetFile, std::ios::trunc | std::ios::out); | 467 | std::ofstream output(targetFile, std::ios::trunc | std::ios::out); |
| 453 | if (output) { | 468 | if (output) { |
| 454 | const auto& codes = std::get<0>(result); | 469 | const auto& codes = result.codes; |
| 455 | if (config.reserveLineNumber) { | 470 | if (config.reserveLineNumber) { |
| 456 | auto head = std::string("-- [moonp]: "sv) + file.first + '\n'; | 471 | auto head = std::string("-- [moonp]: "sv) + file.first + '\n'; |
| 457 | output.write(head.c_str(), head.size()); | 472 | output.write(head.c_str(), head.size()); |
| @@ -465,7 +480,7 @@ int main(int narg, const char** args) { | |||
| 465 | } else { | 480 | } else { |
| 466 | std::ostringstream buf; | 481 | std::ostringstream buf; |
| 467 | buf << "Fail to compile: "sv << file.first << ".\n"; | 482 | buf << "Fail to compile: "sv << file.first << ".\n"; |
| 468 | buf << std::get<1>(result) << '\n'; | 483 | buf << result.error << '\n'; |
| 469 | return std::tuple{1, std::string(), buf.str()}; | 484 | return std::tuple{1, std::string(), buf.str()}; |
| 470 | } | 485 | } |
| 471 | } else { | 486 | } else { |
