diff options
author | Li Jin <dragon-fly@qq.com> | 2022-07-14 02:48:49 +0800 |
---|---|---|
committer | Li Jin <dragon-fly@qq.com> | 2022-07-14 02:48:49 +0800 |
commit | 3159a45de9e691ad758dcbc933446f61b7ae1940 (patch) | |
tree | b695f8356986accc84c82ece09eb427a2b1db230 | |
parent | a1d341085eed96d567329a30f2cf57c95fe6f071 (diff) | |
download | yuescript-3159a45de9e691ad758dcbc933446f61b7ae1940.tar.gz yuescript-3159a45de9e691ad758dcbc933446f61b7ae1940.tar.bz2 yuescript-3159a45de9e691ad758dcbc933446f61b7ae1940.zip |
fix table matching issue and update doc.
-rwxr-xr-x | doc/docs/doc/README.md | 119 | ||||
-rw-r--r-- | spec/inputs/switch.yue | 6 | ||||
-rw-r--r-- | spec/outputs/switch.lua | 28 | ||||
-rwxr-xr-x | src/yuescript/yue_compiler.cpp | 72 |
4 files changed, 176 insertions, 49 deletions
diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md index 5d856bb..ef00c23 100755 --- a/doc/docs/doc/README.md +++ b/doc/docs/doc/README.md | |||
@@ -311,7 +311,7 @@ end | |||
311 | 311 | ||
312 | ### Export Macro | 312 | ### Export Macro |
313 | 313 | ||
314 | Macro functions can be exported from a module and get imported in another module. It is recommanded to export macro functions in a single file to speed up compilation. | 314 | Macro functions can be exported from a module and get imported in another module. You have to put export macro functions in a single file to be used, and only macro definition, macro importing and macro expansion in place can be put into the macro exporting module. |
315 | ```moonscript | 315 | ```moonscript |
316 | -- file: utils.yue | 316 | -- file: utils.yue |
317 | export macro map = (items, action)-> "[#{action} for _ in *#{items}]" | 317 | export macro map = (items, action)-> "[#{action} for _ in *#{items}]" |
@@ -345,6 +345,20 @@ import "utils" as { | |||
345 | </pre> | 345 | </pre> |
346 | </YueDisplay> | 346 | </YueDisplay> |
347 | 347 | ||
348 | ### Builtin Macro | ||
349 | |||
350 | There are some builtin macros but you can override them by declaring macros with the same names. | ||
351 | ```moonscript | ||
352 | print $FILE -- get string of current module name | ||
353 | print $LINE -- get number 2 | ||
354 | ``` | ||
355 | <YueDisplay> | ||
356 | <pre> | ||
357 | print $FILE -- get string of current module name | ||
358 | print $LINE -- get number 2 | ||
359 | </pre> | ||
360 | </YueDisplay> | ||
361 | |||
348 | ## Operator | 362 | ## Operator |
349 | 363 | ||
350 | All of Lua's binary and unary operators are available. Additionally **!=** is as an alias for **~=**, and either **\\** or **::** can be used to write a chaining function call like `tb\func!` or `tb::func!`. And Yuescipt offers some other special operators to write more expressive codes. | 364 | All of Lua's binary and unary operators are available. Additionally **!=** is as an alias for **~=**, and either **\\** or **::** can be used to write a chaining function call like `tb\func!` or `tb::func!`. And Yuescipt offers some other special operators to write more expressive codes. |
@@ -1237,10 +1251,6 @@ close _ = close#: -> print "Out of scope." | |||
1237 | </pre> | 1251 | </pre> |
1238 | </YueDisplay> | 1252 | </YueDisplay> |
1239 | 1253 | ||
1240 | ::: warning NOTICE | ||
1241 | The rest of the document is describing mostly the same syntax taken from Moonscript. So you may as well refer to the [Moonscript Reference](http://moonscript.org/reference) to get the same explanation. | ||
1242 | ::: | ||
1243 | |||
1244 | ## Literals | 1254 | ## Literals |
1245 | 1255 | ||
1246 | All of the primitive literals in Lua can be used. This applies to numbers, strings, booleans, and **nil**. | 1256 | All of the primitive literals in Lua can be used. This applies to numbers, strings, booleans, and **nil**. |
@@ -2295,6 +2305,60 @@ msg = switch math.random(1, 5) | |||
2295 | 2305 | ||
2296 | It is worth noting the order of the case comparison expression. The case’s expression is on the left hand side. This can be useful if the case’s expression wants to overwrite how the comparison is done by defining an eq metamethod. | 2306 | It is worth noting the order of the case comparison expression. The case’s expression is on the left hand side. This can be useful if the case’s expression wants to overwrite how the comparison is done by defining an eq metamethod. |
2297 | 2307 | ||
2308 | ### Table Matching | ||
2309 | |||
2310 | You can do table matching in a switch when clause, if the table can be destructured by a specific structure and get non-nil values. | ||
2311 | |||
2312 | ```moonscript | ||
2313 | items = | ||
2314 | * x: 100 | ||
2315 | y: 200 | ||
2316 | * width: 300 | ||
2317 | height: 400 | ||
2318 | |||
2319 | for item in *items | ||
2320 | switch item | ||
2321 | when :x, :y | ||
2322 | print "Vec2 #{x}, #{y}" | ||
2323 | when :width, :height | ||
2324 | print "size #{width}, #{height}" | ||
2325 | ``` | ||
2326 | <YueDisplay> | ||
2327 | <pre> | ||
2328 | items = | ||
2329 | * x: 100 | ||
2330 | y: 200 | ||
2331 | * width: 300 | ||
2332 | height: 400 | ||
2333 | |||
2334 | for item in *items | ||
2335 | switch item | ||
2336 | when :x, :y | ||
2337 | print "Vec2 #{x}, #{y}" | ||
2338 | when :width, :height | ||
2339 | print "size #{width}, #{height}" | ||
2340 | </pre> | ||
2341 | </YueDisplay> | ||
2342 | |||
2343 | You can use default values to optionally destructure the table for some fields. | ||
2344 | |||
2345 | ```moonscript | ||
2346 | item = x: 100 | ||
2347 | |||
2348 | switch item | ||
2349 | when {:x, :y = 200} | ||
2350 | print "Vec2 #{x}, #{y}" -- table matching will pass | ||
2351 | ``` | ||
2352 | <YueDisplay> | ||
2353 | <pre> | ||
2354 | item = x: 100 | ||
2355 | |||
2356 | switch item | ||
2357 | when {:x, :y = 200} | ||
2358 | print "Vec2 #{x}, #{y}" -- table matching will pass | ||
2359 | </pre> | ||
2360 | </YueDisplay> | ||
2361 | |||
2298 | ## Object Oriented Programming | 2362 | ## Object Oriented Programming |
2299 | 2363 | ||
2300 | In these examples, the generated Lua code may appear overwhelming. It is best to focus on the meaning of the Yuescript code at first, then look into the Lua code if you wish to know the implementation details. | 2364 | In these examples, the generated Lua code may appear overwhelming. It is best to focus on the meaning of the Yuescript code at first, then look into the Lua code if you wish to know the implementation details. |
@@ -2718,6 +2782,51 @@ x = class | |||
2718 | </pre> | 2782 | </pre> |
2719 | </YueDisplay> | 2783 | </YueDisplay> |
2720 | 2784 | ||
2785 | ### Class Mixing | ||
2786 | |||
2787 | You can do mixing with keyword `using` to copy functions from either a plain table or a predefined class object into your new class. When doing mixing with a plain table, you can override the class indexing function (metamethod `__index`) to your customized implementation. When doing mixing with an existing class object, the class object's metamethods won't be copied. | ||
2788 | |||
2789 | ```moonscript | ||
2790 | MyIndex = __index: var: 1 | ||
2791 | |||
2792 | class X using MyIndex | ||
2793 | func: => | ||
2794 | print 123 | ||
2795 | |||
2796 | x = X! | ||
2797 | print x.var | ||
2798 | |||
2799 | class Y using X | ||
2800 | |||
2801 | y = Y! | ||
2802 | y\func! | ||
2803 | |||
2804 | assert y.__class.__parent ~= X -- X is not parent of Y | ||
2805 | ``` | ||
2806 | <YueDisplay> | ||
2807 | <pre> | ||
2808 | MyIndex = __index: var: 1 | ||
2809 | |||
2810 | class X using MyIndex | ||
2811 | func: => | ||
2812 | print 123 | ||
2813 | |||
2814 | x = X! | ||
2815 | print x.var | ||
2816 | |||
2817 | class Y using X | ||
2818 | |||
2819 | y = Y! | ||
2820 | y\func! | ||
2821 | |||
2822 | assert y.__class.__parent ~= X -- X is not parent of Y | ||
2823 | </pre> | ||
2824 | </YueDisplay> | ||
2825 | |||
2826 | ::: warning NOTICE | ||
2827 | The rest of the document is describing mostly the same syntax taken from Moonscript. So you may as well refer to the [Moonscript Reference](http://moonscript.org/reference) to get the same explanation. | ||
2828 | ::: | ||
2829 | |||
2721 | ## With Statement | 2830 | ## With Statement |
2722 | 2831 | ||
2723 | A common pattern involving the creation of an object is calling a series of functions and setting a series of properties immediately after creating it. | 2832 | A common pattern involving the creation of an object is calling a series of functions and setting a series of properties immediately after creating it. |
diff --git a/spec/inputs/switch.yue b/spec/inputs/switch.yue index 36f9be6..04bb02e 100644 --- a/spec/inputs/switch.yue +++ b/spec/inputs/switch.yue | |||
@@ -139,10 +139,10 @@ do | |||
139 | "#{a + b}" | 139 | "#{a + b}" |
140 | when 1, 2, 3, 4, 5 | 140 | when 1, 2, 3, 4, 5 |
141 | "number 1 - 5" | 141 | "number 1 - 5" |
142 | when {:alwaysMatch = "fallback"} | 142 | when {:matchAnyTable = "fallback"} |
143 | alwaysMatch | 143 | matchAnyTable |
144 | else | 144 | else |
145 | "should not reach here" | 145 | "should not reach here unless it is not a table" |
146 | 146 | ||
147 | nil | 147 | nil |
148 | 148 | ||
diff --git a/spec/outputs/switch.lua b/spec/outputs/switch.lua index 03a0d37..7b413f8 100644 --- a/spec/outputs/switch.lua +++ b/spec/outputs/switch.lua | |||
@@ -182,8 +182,8 @@ do | |||
182 | local x = item.x | 182 | local x = item.x |
183 | local y = item.y | 183 | local y = item.y |
184 | if x ~= nil and y ~= nil then | 184 | if x ~= nil and y ~= nil then |
185 | print("Vec2 " .. tostring(x) .. ", " .. tostring(y)) | ||
186 | _match_0 = true | 185 | _match_0 = true |
186 | print("Vec2 " .. tostring(x) .. ", " .. tostring(y)) | ||
187 | end | 187 | end |
188 | end | 188 | end |
189 | if not _match_0 then | 189 | if not _match_0 then |
@@ -192,8 +192,8 @@ do | |||
192 | local width = item.width | 192 | local width = item.width |
193 | local height = item.height | 193 | local height = item.height |
194 | if width ~= nil and height ~= nil then | 194 | if width ~= nil and height ~= nil then |
195 | print("Size " .. tostring(width) .. ", " .. tostring(height)) | ||
196 | _match_1 = true | 195 | _match_1 = true |
196 | print("Size " .. tostring(width) .. ", " .. tostring(height)) | ||
197 | end | 197 | end |
198 | end | 198 | end |
199 | if not _match_1 then | 199 | if not _match_1 then |
@@ -204,12 +204,12 @@ do | |||
204 | if _tab_0 then | 204 | if _tab_0 then |
205 | local cls = item.__class | 205 | local cls = item.__class |
206 | if cls ~= nil then | 206 | if cls ~= nil then |
207 | _match_2 = true | ||
207 | if ClassA == cls then | 208 | if ClassA == cls then |
208 | print("Object A") | 209 | print("Object A") |
209 | elseif ClassB == cls then | 210 | elseif ClassB == cls then |
210 | print("Object B") | 211 | print("Object B") |
211 | end | 212 | end |
212 | _match_2 = true | ||
213 | end | 213 | end |
214 | end | 214 | end |
215 | if not _match_2 then | 215 | if not _match_2 then |
@@ -241,9 +241,7 @@ do | |||
241 | if b == nil then | 241 | if b == nil then |
242 | b = 2 | 242 | b = 2 |
243 | end | 243 | end |
244 | if a ~= nil and b ~= nil then | 244 | print(a, b) |
245 | print(a, b) | ||
246 | end | ||
247 | end | 245 | end |
248 | end | 246 | end |
249 | end | 247 | end |
@@ -258,8 +256,8 @@ do | |||
258 | local x = tb.x | 256 | local x = tb.x |
259 | local y = tb.y | 257 | local y = tb.y |
260 | if x ~= nil and y ~= nil then | 258 | if x ~= nil and y ~= nil then |
261 | print("x: " .. tostring(x) .. " with y: " .. tostring(y)) | ||
262 | _match_0 = true | 259 | _match_0 = true |
260 | print("x: " .. tostring(x) .. " with y: " .. tostring(y)) | ||
263 | end | 261 | end |
264 | end | 262 | end |
265 | if not _match_0 then | 263 | if not _match_0 then |
@@ -284,8 +282,8 @@ do | |||
284 | if _tab_0 then | 282 | if _tab_0 then |
285 | local x = _exp_0.x | 283 | local x = _exp_0.x |
286 | if x ~= nil then | 284 | if x ~= nil then |
287 | matched = x | ||
288 | _match_0 = true | 285 | _match_0 = true |
286 | matched = x | ||
289 | end | 287 | end |
290 | end | 288 | end |
291 | if not _match_0 then | 289 | if not _match_0 then |
@@ -310,8 +308,8 @@ do | |||
310 | local a = _exp_0.a | 308 | local a = _exp_0.a |
311 | local b = _exp_0.b | 309 | local b = _exp_0.b |
312 | if a ~= nil and b ~= nil then | 310 | if a ~= nil and b ~= nil then |
313 | return tostring(a + b) | ||
314 | _match_0 = true | 311 | _match_0 = true |
312 | return tostring(a + b) | ||
315 | end | 313 | end |
316 | end | 314 | end |
317 | if not _match_0 then | 315 | if not _match_0 then |
@@ -319,15 +317,13 @@ do | |||
319 | return "number 1 - 5" | 317 | return "number 1 - 5" |
320 | else | 318 | else |
321 | if _tab_0 then | 319 | if _tab_0 then |
322 | local alwaysMatch = _exp_0.alwaysMatch | 320 | local matchAnyTable = _exp_0.matchAnyTable |
323 | if alwaysMatch == nil then | 321 | if matchAnyTable == nil then |
324 | alwaysMatch = "fallback" | 322 | matchAnyTable = "fallback" |
325 | end | ||
326 | if alwaysMatch ~= nil then | ||
327 | return alwaysMatch | ||
328 | end | 323 | end |
324 | return matchAnyTable | ||
329 | else | 325 | else |
330 | return "should not reach here" | 326 | return "should not reach here unless it is not a table" |
331 | end | 327 | end |
332 | end | 328 | end |
333 | end | 329 | end |
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index c68746c..2565878 100755 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp | |||
@@ -56,7 +56,7 @@ using namespace parserlib; | |||
56 | 56 | ||
57 | typedef std::list<std::string> str_list; | 57 | typedef std::list<std::string> str_list; |
58 | 58 | ||
59 | const std::string_view version = "0.13.3"sv; | 59 | const std::string_view version = "0.13.4"sv; |
60 | const std::string_view extension = "yue"sv; | 60 | const std::string_view extension = "yue"sv; |
61 | 61 | ||
62 | class YueCompilerImpl { | 62 | class YueCompilerImpl { |
@@ -1582,12 +1582,22 @@ private: | |||
1582 | added--; | 1582 | added--; |
1583 | } | 1583 | } |
1584 | if (pair.defVal) { | 1584 | if (pair.defVal) { |
1585 | auto stmt = toAst<Statement_t>(pair.targetVar + "=nil if "s + pair.targetVar + "==nil", pair.defVal); | 1585 | bool isNil = false; |
1586 | auto defAssign = stmt->content.as<ExpListAssign_t>(); | 1586 | if (auto v1 = singleValueFrom(pair.defVal)) { |
1587 | auto assign = defAssign->action.as<Assign_t>(); | 1587 | if (auto v2 = v1->item.as<SimpleValue_t>()) { |
1588 | assign->values.clear(); | 1588 | if (auto v3 = v2->value.as<const_value_t>()) { |
1589 | assign->values.push_back(pair.defVal); | 1589 | isNil = _parser.toString(v3) == "nil"sv; |
1590 | transformStatement(stmt, temp); | 1590 | } |
1591 | } | ||
1592 | } | ||
1593 | if (!isNil) { | ||
1594 | auto stmt = toAst<Statement_t>(pair.targetVar + "=nil if "s + pair.targetVar + "==nil", pair.defVal); | ||
1595 | auto defAssign = stmt->content.as<ExpListAssign_t>(); | ||
1596 | auto assign = defAssign->action.as<Assign_t>(); | ||
1597 | assign->values.clear(); | ||
1598 | assign->values.push_back(pair.defVal); | ||
1599 | transformStatement(stmt, temp); | ||
1600 | } | ||
1591 | } | 1601 | } |
1592 | continue; | 1602 | continue; |
1593 | } | 1603 | } |
@@ -1709,12 +1719,22 @@ private: | |||
1709 | } | 1719 | } |
1710 | for (const auto& item : destruct.items) { | 1720 | for (const auto& item : destruct.items) { |
1711 | if (item.defVal) { | 1721 | if (item.defVal) { |
1712 | auto stmt = toAst<Statement_t>(item.targetVar + "=nil if "s + item.targetVar + "==nil", item.defVal); | 1722 | bool isNil = false; |
1713 | auto defAssign = stmt->content.as<ExpListAssign_t>(); | 1723 | if (auto v1 = singleValueFrom(item.defVal)) { |
1714 | auto assign = defAssign->action.as<Assign_t>(); | 1724 | if (auto v2 = v1->item.as<SimpleValue_t>()) { |
1715 | assign->values.clear(); | 1725 | if (auto v3 = v2->value.as<const_value_t>()) { |
1716 | assign->values.push_back(item.defVal); | 1726 | isNil = _parser.toString(v3) == "nil"sv; |
1717 | transformStatement(stmt, temp); | 1727 | } |
1728 | } | ||
1729 | } | ||
1730 | if (!isNil) { | ||
1731 | auto stmt = toAst<Statement_t>(item.targetVar + "=nil if "s + item.targetVar + "==nil", item.defVal); | ||
1732 | auto defAssign = stmt->content.as<ExpListAssign_t>(); | ||
1733 | auto assign = defAssign->action.as<Assign_t>(); | ||
1734 | assign->values.clear(); | ||
1735 | assign->values.push_back(item.defVal); | ||
1736 | transformStatement(stmt, temp); | ||
1737 | } | ||
1718 | } | 1738 | } |
1719 | } | 1739 | } |
1720 | for (const auto& item : leftPairs) { | 1740 | for (const auto& item : leftPairs) { |
@@ -7160,32 +7180,34 @@ private: | |||
7160 | auto assignment = assignmentFrom(static_cast<Exp_t*>(branch->valueList->exprs.front()), toAst<Exp_t>(objVar, branch), branch); | 7180 | auto assignment = assignmentFrom(static_cast<Exp_t*>(branch->valueList->exprs.front()), toAst<Exp_t>(objVar, branch), branch); |
7161 | auto info = extractDestructureInfo(assignment, true, false); | 7181 | auto info = extractDestructureInfo(assignment, true, false); |
7162 | transformAssignment(assignment, temp, true); | 7182 | transformAssignment(assignment, temp, true); |
7163 | temp.push_back(indent() + "if"s); | 7183 | str_list conds; |
7164 | bool firstItem = true; | ||
7165 | for (const auto& destruct : info.first) { | 7184 | for (const auto& destruct : info.first) { |
7166 | for (const auto& item : destruct.items) { | 7185 | for (const auto& item : destruct.items) { |
7167 | str_list tmp; | 7186 | if (!item.defVal) { |
7168 | transformExp(item.target, tmp, ExpUsage::Closure); | 7187 | transformExp(item.target, conds, ExpUsage::Closure); |
7169 | temp.back().append((firstItem ? " " : " and "s) + tmp.back() + " ~= nil"s); | 7188 | conds.back().append(" ~= nil"s); |
7170 | if (firstItem) firstItem = false; | 7189 | } |
7171 | } | 7190 | } |
7172 | } | 7191 | } |
7173 | temp.back().append(" then"s + nll(branch)); | 7192 | if (!conds.empty()) { |
7174 | pushScope(); | 7193 | temp.push_back(indent() + "if "s + join(conds, " and "sv) + " then"s + nll(branch)); |
7175 | transform_plain_body(branch->body, temp, usage, assignList); | 7194 | pushScope(); |
7195 | } | ||
7176 | if (!lastBranch) { | 7196 | if (!lastBranch) { |
7177 | temp.push_back(indent() + matchVar + " = true"s + nll(branch)); | 7197 | temp.push_back(indent() + matchVar + " = true"s + nll(branch)); |
7178 | } | 7198 | } |
7179 | popScope(); | 7199 | transform_plain_body(branch->body, temp, usage, assignList); |
7180 | if (!lastBranch) { | 7200 | if (!conds.empty()) { |
7201 | popScope(); | ||
7181 | temp.push_back(indent() + "end"s + nll(branch)); | 7202 | temp.push_back(indent() + "end"s + nll(branch)); |
7203 | } | ||
7204 | if (!lastBranch) { | ||
7182 | popScope(); | 7205 | popScope(); |
7183 | temp.push_back(indent() + "end"s + nll(branch)); | 7206 | temp.push_back(indent() + "end"s + nll(branch)); |
7184 | temp.push_back(indent() + "if not "s + matchVar + " then"s + nll(branch)); | 7207 | temp.push_back(indent() + "if not "s + matchVar + " then"s + nll(branch)); |
7185 | pushScope(); | 7208 | pushScope(); |
7186 | addScope++; | 7209 | addScope++; |
7187 | } else { | 7210 | } else { |
7188 | temp.push_back(indent() + "end"s + nll(branch)); | ||
7189 | popScope(); | 7211 | popScope(); |
7190 | } | 7212 | } |
7191 | firstBranch = true; | 7213 | firstBranch = true; |