aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2020-10-21 23:44:50 +0800
committerLi Jin <dragon-fly@qq.com>2020-10-21 23:44:50 +0800
commitb6725202f4a8cac5f829dac9a72a81f3ff73e787 (patch)
treec173accb869b60cba14babc7685284864bc80426
parent0777356cbe599b3f88bdfa476e3ffa64bb3a3a8c (diff)
downloadyuescript-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.md11
-rw-r--r--makefile12
-rw-r--r--spec/inputs/macro-export.mp (renamed from spec/inputs/macro_export.mp)1
-rw-r--r--spec/inputs/macro-teal.mp46
-rw-r--r--spec/inputs/macro.mp23
-rw-r--r--spec/inputs/teal-lang.mp32
-rw-r--r--src/MoonP/moon_ast.h2
-rw-r--r--src/MoonP/moon_compiler.cpp198
-rw-r--r--src/MoonP/moon_compiler.h15
-rw-r--r--src/MoonP/moon_parser.cpp2
-rw-r--r--src/MoonP/moonplus.cpp26
-rw-r--r--src/MoonP/moonplus.h2
-rw-r--r--src/moonp.cpp37
13 files changed, 328 insertions, 79 deletions
diff --git a/README.md b/README.md
index 0ccf9fe..a45a852 100644
--- a/README.md
+++ b/README.md
@@ -62,11 +62,20 @@ f!
62* **Binary Tool** 62* **Binary Tool**
63 63
64&emsp;&emsp;Clone this repo, then build and install executable with: 64&emsp;&emsp;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&emsp;&emsp;Build MoonPlus tool without macro feature:
70```sh
71> make install NO_MACRO=true
72```
73
74&emsp;&emsp;Build MoonPlus tool without built-in Lua binary:
75```sh
76> make install NO_LUA=true
77```
78
70&emsp;&emsp;Use MoonPlus tool with: 79&emsp;&emsp;Use MoonPlus tool with:
71 80
72```sh 81```sh
diff --git a/makefile b/makefile
index 70d6c97..38738e2 100644
--- a/makefile
+++ b/makefile
@@ -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'
42UNAME_S:=$(shell uname -s) 42UNAME_S:=$(shell uname -s)
43 43
44ifeq ($(NO_LUA),true)
45 COMPILE_FLAGS += -DMOONP_NO_MACRO
46 COMPILE_FLAGS += -DMOONP_COMPILER_ONLY
47else
48ifeq ($(NO_MACRO),true)
49 COMPILE_FLAGS += -DMOONP_NO_MACRO
50endif
51endif
52
44# Add platform related linker flag 53# Add platform related linker flag
45ifneq ($(UNAME_S),Darwin) 54ifneq ($(UNAME_S),Darwin)
46 LINK_FLAGS += -lstdc++fs 55 LINK_FLAGS += -lstdc++fs
@@ -235,7 +244,8 @@ clean:
235test: debug 244test: 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
6macro expr to_lua = (codes)->
7 "require('moonp').to_lua(#{codes}, reserve_line_number:false, same_module:true)"
8
9macro expr trim = (name)->
10 "if result = #{name}\\match '[\\'\"](.*)[\\'\"]' then result else #{name}"
11
12export 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
20export 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
29export macro text record = (name, decl)->
30 import "moonp" as {options:{:tl_enabled}}
31 if tl_enabled
32 "local record #{name}
33 #{decl}
34end", {name}
35 else
36 "local #{name} = {}", {name}
37
38export 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 @@
1macro 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! 4import "macro-export" as {
7
8import "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
125x = x + f(3) 121x = x + f(3)
126]] 122]]
127 123
124$lua[[
125function tb:func()
126 print(123)
127end
128]]
129
128print x 130print x
129 131
130macro lua def = (fname, ...)-> 132macro 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
151macro lua insertComment = (text)-> "-- #{text\match '[\'"](.*)[\'"]'}"
152
153$insertComment "a comment here"
149 154
150import 'underscore' as _ 155import '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
4import "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
11s = add(a.value, b)
12print(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
31p\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)
636AST_END(macro_type) 636AST_END(macro_type)
637 637
638AST_NODE(MacroName) 638AST_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)
641AST_END(MacroName) 641AST_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
56const std::string_view version = "0.4.18"sv; 56const std::string_view version = "0.4.19"sv;
57const std::string_view extension = "mp"sv; 57const std::string_view extension = "mp"sv;
58 58
59class MoonCompilerImpl { 59class 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
5331MoonCompiler::~MoonCompiler() {} 5453MoonCompiler::~MoonCompiler() {}
5332 5454
5333std::tuple<std::string,std::string,GlobalVars> MoonCompiler::compile(std::string_view codes, const MoonConfig& config) { 5455CompileInfo 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
18namespace MoonP { 19namespace MoonP {
@@ -20,12 +21,15 @@ namespace MoonP {
20extern const std::string_view version; 21extern const std::string_view version;
21extern const std::string_view extension; 22extern const std::string_view extension;
22 23
24using Options = std::unordered_map<std::string,std::string>;
25
23struct MoonConfig { 26struct 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
31struct GlobalVar { 35struct GlobalVar {
@@ -34,7 +38,14 @@ struct GlobalVar {
34 int col; 38 int col;
35}; 39};
36 40
37using GlobalVars = std::unique_ptr<std::list<GlobalVar>>; 41using GlobalVars = std::list<GlobalVar>;
42
43struct CompileInfo {
44 std::string codes;
45 std::string error;
46 std::unique_ptr<GlobalVars> globals;
47 std::unique_ptr<Options> options;
48};
38 49
39class MoonCompilerImpl; 50class 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 = {});
48private: 59private:
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
74end 74end
75create_moonpath = function(package_path) 75create_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__)
30extern "C" { 30extern "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 {