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 | |
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".
-rw-r--r-- | README.md | 11 | ||||
-rw-r--r-- | makefile | 12 | ||||
-rw-r--r-- | spec/inputs/macro-export.mp (renamed from spec/inputs/macro_export.mp) | 1 | ||||
-rw-r--r-- | spec/inputs/macro-teal.mp | 46 | ||||
-rw-r--r-- | spec/inputs/macro.mp | 23 | ||||
-rw-r--r-- | spec/inputs/teal-lang.mp | 32 | ||||
-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 |
13 files changed, 328 insertions, 79 deletions
@@ -62,11 +62,20 @@ f! | |||
62 | * **Binary Tool** | 62 | * **Binary Tool** |
63 | 63 | ||
64 |   Clone this repo, then build and install executable with: | 64 |   Clone this repo, then build and install executable with: |
65 | |||
66 | ```sh | 65 | ```sh |
67 | > make install | 66 | > make install |
68 | ``` | 67 | ``` |
69 | 68 | ||
69 |   Build MoonPlus tool without macro feature: | ||
70 | ```sh | ||
71 | > make install NO_MACRO=true | ||
72 | ``` | ||
73 | |||
74 |   Build MoonPlus tool without built-in Lua binary: | ||
75 | ```sh | ||
76 | > make install NO_LUA=true | ||
77 | ``` | ||
78 | |||
70 |   Use MoonPlus tool with: | 79 |   Use MoonPlus tool with: |
71 | 80 | ||
72 | ```sh | 81 | ```sh |
@@ -41,6 +41,15 @@ TEST_OUTPUT = ./spec/outputs | |||
41 | # Obtains the OS type, either 'Darwin' (OS X) or 'Linux' | 41 | # Obtains the OS type, either 'Darwin' (OS X) or 'Linux' |
42 | UNAME_S:=$(shell uname -s) | 42 | UNAME_S:=$(shell uname -s) |
43 | 43 | ||
44 | ifeq ($(NO_LUA),true) | ||
45 | COMPILE_FLAGS += -DMOONP_NO_MACRO | ||
46 | COMPILE_FLAGS += -DMOONP_COMPILER_ONLY | ||
47 | else | ||
48 | ifeq ($(NO_MACRO),true) | ||
49 | COMPILE_FLAGS += -DMOONP_NO_MACRO | ||
50 | endif | ||
51 | endif | ||
52 | |||
44 | # Add platform related linker flag | 53 | # Add platform related linker flag |
45 | ifneq ($(UNAME_S),Darwin) | 54 | ifneq ($(UNAME_S),Darwin) |
46 | LINK_FLAGS += -lstdc++fs | 55 | LINK_FLAGS += -lstdc++fs |
@@ -235,7 +244,8 @@ clean: | |||
235 | test: debug | 244 | test: debug |
236 | @echo "Compiling Moonscript codes..." | 245 | @echo "Compiling Moonscript codes..." |
237 | @$(START_TIME) | 246 | @$(START_TIME) |
238 | @./$(BIN_NAME) $(TEST_INPUT) -t $(TEST_OUTPUT) | 247 | @./$(BIN_NAME) $(TEST_INPUT) -t $(TEST_OUTPUT) -tl_enabled=true |
248 | @./$(BIN_NAME) $(TEST_INPUT)/teal-lang.mp -o $(TEST_OUTPUT)/teal-lang.lua | ||
239 | @echo -en "Compile time: " | 249 | @echo -en "Compile time: " |
240 | @$(END_TIME) | 250 | @$(END_TIME) |
241 | 251 | ||
diff --git a/spec/inputs/macro_export.mp b/spec/inputs/macro-export.mp index 7208b2a..b6079ca 100644 --- a/spec/inputs/macro_export.mp +++ b/spec/inputs/macro-export.mp | |||
@@ -26,4 +26,3 @@ export macro expr assert = (cond)-> | |||
26 | "#{cond}" | 26 | "#{cond}" |
27 | 27 | ||
28 | $config! | 28 | $config! |
29 | |||
diff --git a/spec/inputs/macro-teal.mp b/spec/inputs/macro-teal.mp new file mode 100644 index 0000000..20444e1 --- /dev/null +++ b/spec/inputs/macro-teal.mp | |||
@@ -0,0 +1,46 @@ | |||
1 | $ -> | ||
2 | import "moonp" as {:options} | ||
3 | if options.tl_enabled | ||
4 | options.target_extension = "tl" | ||
5 | |||
6 | macro expr to_lua = (codes)-> | ||
7 | "require('moonp').to_lua(#{codes}, reserve_line_number:false, same_module:true)" | ||
8 | |||
9 | macro expr trim = (name)-> | ||
10 | "if result = #{name}\\match '[\\'\"](.*)[\\'\"]' then result else #{name}" | ||
11 | |||
12 | export macro text var = (name, type, value = nil)-> | ||
13 | import "moonp" as {options:{:tl_enabled}} | ||
14 | value = $to_lua(value)\gsub "^return ", "" | ||
15 | if tl_enabled | ||
16 | "local #{name}:#{$trim type} = #{value}", {name} | ||
17 | else | ||
18 | "local #{name} = #{value}", {name} | ||
19 | |||
20 | export macro text def = (name, type, value)-> | ||
21 | import "moonp" as {options:{:tl_enabled}} | ||
22 | if tl_enabled | ||
23 | value = $to_lua(value)\match "function%(.*%)(.*)end" | ||
24 | "local function #{name}#{$trim type}\n#{value}\nend", {name} | ||
25 | else | ||
26 | value = $to_lua(value)\gsub "^return ", "" | ||
27 | "local #{name} = #{value}", {name} | ||
28 | |||
29 | export macro text record = (name, decl)-> | ||
30 | import "moonp" as {options:{:tl_enabled}} | ||
31 | if tl_enabled | ||
32 | "local record #{name} | ||
33 | #{decl} | ||
34 | end", {name} | ||
35 | else | ||
36 | "local #{name} = {}", {name} | ||
37 | |||
38 | export macro text field = (tab, sym, func, type, value)-> | ||
39 | import "moonp" as {options:{:tl_enabled}} | ||
40 | if tl_enabled | ||
41 | value = $to_lua(value)\match "^return function%(.-%)\n(.*)end" | ||
42 | "function #{tab}#{$trim sym}#{func}#{$trim type}\n#{value}\nend" | ||
43 | else | ||
44 | value = $to_lua(value)\gsub "^return ", "" | ||
45 | "#{tab}.#{func} = #{value}" | ||
46 | |||
diff --git a/spec/inputs/macro.mp b/spec/inputs/macro.mp index 103df95..fafa522 100644 --- a/spec/inputs/macro.mp +++ b/spec/inputs/macro.mp | |||
@@ -1,11 +1,7 @@ | |||
1 | macro block init = -> | 1 | $ -> |
2 | with require "moonp" | 2 | package.moonpath = "?.mp;./spec/inputs/?.mp" |
3 | package.moonpath = "?.mp;./spec/inputs/?.mp" | ||
4 | "" | ||
5 | 3 | ||
6 | $init! | 4 | import "macro-export" as { |
7 | |||
8 | import "macro_export" as { | ||
9 | $, -- import all macros | 5 | $, -- import all macros |
10 | $config:$myconfig, -- rename macro $config to $myconfig | 6 | $config:$myconfig, -- rename macro $config to $myconfig |
11 | } | 7 | } |
@@ -125,6 +121,12 @@ end | |||
125 | x = x + f(3) | 121 | x = x + f(3) |
126 | ]] | 122 | ]] |
127 | 123 | ||
124 | $lua[[ | ||
125 | function tb:func() | ||
126 | print(123) | ||
127 | end | ||
128 | ]] | ||
129 | |||
128 | print x | 130 | print x |
129 | 131 | ||
130 | macro lua def = (fname, ...)-> | 132 | macro lua def = (fname, ...)-> |
@@ -144,8 +146,11 @@ $def sel, a, b, c, [[ | |||
144 | end | 146 | end |
145 | ]] | 147 | ]] |
146 | 148 | ||
147 | $def dummy,[[ | 149 | $def dummy,[[]] |
148 | ]] | 150 | |
151 | macro lua insertComment = (text)-> "-- #{text\match '[\'"](.*)[\'"]'}" | ||
152 | |||
153 | $insertComment "a comment here" | ||
149 | 154 | ||
150 | import 'underscore' as _ | 155 | import 'underscore' as _ |
151 | 156 | ||
diff --git a/spec/inputs/teal-lang.mp b/spec/inputs/teal-lang.mp new file mode 100644 index 0000000..3c9c79b --- /dev/null +++ b/spec/inputs/teal-lang.mp | |||
@@ -0,0 +1,32 @@ | |||
1 | $ -> | ||
2 | package.moonpath = "?.mp;./spec/inputs/?.mp" | ||
3 | |||
4 | import "macro-teal" as {$} | ||
5 | |||
6 | $var a, "{string:number}", {value:123} | ||
7 | $var b, "number", a.value | ||
8 | |||
9 | $def add, "(a:number,b:number):number", (a, b)-> a + b | ||
10 | |||
11 | s = add(a.value, b) | ||
12 | print(s) | ||
13 | |||
14 | $record Point, [[ | ||
15 | x: number | ||
16 | y: number | ||
17 | ]] | ||
18 | |||
19 | $field Point, '.', new, "(x: number, y: number):Point", (x, y)-> | ||
20 | $var point, "Point", setmetatable {}, __index: Point | ||
21 | point.x = x or 0 | ||
22 | point.y = y or 0 | ||
23 | point | ||
24 | |||
25 | $field Point, ":", move, "(dx: number, dy: number)", (dx, dy)=> | ||
26 | @x += dx | ||
27 | @y += dy | ||
28 | |||
29 | $var p, "Point", Point.new 100, 100 | ||
30 | |||
31 | p\move 50, 50 | ||
32 | |||
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 { |