aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdoc/docs/doc/README.md580
-rwxr-xr-xdoc/docs/zh/doc/README.md608
-rw-r--r--spec/inputs/backcall.yue4
-rw-r--r--spec/inputs/destructure.yue40
-rw-r--r--spec/inputs/global.yue6
-rw-r--r--spec/inputs/import.yue10
-rw-r--r--spec/inputs/lists.yue47
-rw-r--r--spec/inputs/literals.yue9
-rw-r--r--spec/inputs/loops.yue52
-rw-r--r--spec/inputs/macro.yue54
-rw-r--r--spec/inputs/macro_export.yue31
-rw-r--r--spec/inputs/macro_teal.yue13
-rw-r--r--spec/inputs/macro_todo.yue7
-rw-r--r--spec/inputs/props.yue61
-rw-r--r--spec/inputs/string.yue73
-rw-r--r--spec/inputs/switch.yue125
-rw-r--r--spec/inputs/tables.yue18
-rw-r--r--spec/inputs/try_catch.yue122
-rw-r--r--spec/inputs/unicode/destructure.yue2
-rw-r--r--spec/inputs/unicode/macro_export.yue4
-rw-r--r--spec/inputs/unicode/with.yue24
-rw-r--r--spec/inputs/with.yue42
-rw-r--r--spec/outputs/5.1/attrib.lua18
-rw-r--r--spec/outputs/5.1/loops.lua132
-rw-r--r--spec/outputs/5.1/try_catch.lua271
-rw-r--r--spec/outputs/assign.lua12
-rw-r--r--spec/outputs/attrib.lua18
-rw-r--r--spec/outputs/codes_from_doc.lua844
-rw-r--r--spec/outputs/codes_from_doc_zh.lua844
-rw-r--r--spec/outputs/comprehension.lua10
-rw-r--r--spec/outputs/destructure.lua90
-rw-r--r--spec/outputs/do.lua6
-rw-r--r--spec/outputs/global.lua25
-rw-r--r--spec/outputs/import.lua10
-rw-r--r--spec/outputs/lists.lua215
-rw-r--r--spec/outputs/literals.lua8
-rw-r--r--spec/outputs/loops.lua132
-rw-r--r--spec/outputs/macro.lua15
-rw-r--r--spec/outputs/props.lua240
-rw-r--r--spec/outputs/string.lua41
-rw-r--r--spec/outputs/switch.lua362
-rw-r--r--spec/outputs/syntax.lua8
-rw-r--r--spec/outputs/tables.lua22
-rw-r--r--spec/outputs/try_catch.lua272
-rw-r--r--spec/outputs/unicode/assign.lua12
-rw-r--r--spec/outputs/unicode/attrib.lua18
-rw-r--r--spec/outputs/unicode/comprehension.lua10
-rw-r--r--spec/outputs/unicode/do.lua6
-rw-r--r--spec/outputs/unicode/lists.lua23
-rw-r--r--spec/outputs/unicode/loops.lua4
-rw-r--r--spec/outputs/unicode/macro.lua95
-rw-r--r--spec/outputs/unicode/multiline_chain.lua6
-rw-r--r--spec/outputs/unicode/syntax.lua14
-rw-r--r--spec/outputs/unicode/try_catch.lua10
-rw-r--r--spec/outputs/unicode/vararg.lua28
-rw-r--r--spec/outputs/upvalue_func.lua48
-rw-r--r--spec/outputs/vararg.lua28
-rw-r--r--spec/outputs/with.lua62
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Atomic.hpp18
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Debug.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Debug.hpp6
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/DirWatcherGeneric.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/DirWatcherGeneric.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/DirectorySnapshot.cpp4
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/DirectorySnapshot.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/DirectorySnapshotDiff.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/DirectorySnapshotDiff.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileInfo.cpp14
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileInfo.hpp10
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileSystem.cpp59
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileSystem.hpp4
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcher.cpp239
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherCWrapper.cpp26
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherFSEvents.cpp114
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherFSEvents.hpp49
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherGeneric.cpp8
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherGeneric.hpp19
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherImpl.cpp57
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherImpl.hpp121
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherInotify.cpp140
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherInotify.hpp22
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherKqueue.cpp8
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherKqueue.hpp17
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherWin32.cpp524
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/FileWatcherWin32.hpp141
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/LICENSE0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Lock.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Log.cpp19
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Mutex.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Mutex.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/String.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/String.hpp3
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/System.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/System.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Thread.cpp3
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Thread.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Utf.hpp1442
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Utf.inl1152
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Watcher.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/Watcher.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherFSEvents.cpp80
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherFSEvents.hpp47
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherGeneric.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherGeneric.hpp2
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherInotify.cpp4
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherInotify.hpp4
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherKqueue.cpp15
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherKqueue.hpp2
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherWin32.cpp214
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/WatcherWin32.hpp25
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/base.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/efsw.h175
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/efsw.hpp456
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/inotify-nosys.h0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/platformimpl.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/posix/FileSystemImpl.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/posix/FileSystemImpl.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/posix/MutexImpl.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/posix/MutexImpl.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/posix/SystemImpl.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/posix/SystemImpl.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/posix/ThreadImpl.cpp17
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/posix/ThreadImpl.hpp7
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/win/FileSystemImpl.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/win/FileSystemImpl.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/win/MutexImpl.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/win/MutexImpl.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/win/SystemImpl.cpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/win/SystemImpl.hpp0
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/win/ThreadImpl.cpp2
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/platform/win/ThreadImpl.hpp2
-rw-r--r--[-rwxr-xr-x]src/3rdParty/efsw/sophist.h294
-rw-r--r--src/yuescript/yue_ast.cpp201
-rw-r--r--src/yuescript/yue_ast.h101
-rw-r--r--src/yuescript/yue_compiler.cpp1248
-rw-r--r--src/yuescript/yue_compiler.h1
-rw-r--r--src/yuescript/yue_parser.cpp190
-rw-r--r--src/yuescript/yue_parser.h37
-rw-r--r--src/yuescript/yuescript.cpp16
-rw-r--r--win-build/Yuescript/Yuescript.vcxproj4
140 files changed, 9871 insertions, 3383 deletions
diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md
index 89bd643..f0d67a5 100755
--- a/doc/docs/doc/README.md
+++ b/doc/docs/doc/README.md
@@ -16,19 +16,29 @@ Yue (月) is the name of moon in Chinese and it's pronounced as [jyɛ].
16### An Overview of YueScript 16### An Overview of YueScript
17```moonscript 17```moonscript
18-- import syntax 18-- import syntax
19import "yue" as :p, :to_lua 19import p, to_lua from "yue"
20 20
21-- object literals 21-- object literals
22inventory = 22inventory =
23 equipment: 23 equipment:
24 * "sword" 24 - "sword"
25 * "shield" 25 - "shield"
26 items: 26 items:
27 * name: "potion" 27 - name: "potion"
28 count: 10 28 count: 10
29 * name: "bread" 29 - name: "bread"
30 count: 3 30 count: 3
31 31
32-- list comprehension
33map = (arr, action) ->
34 [action item for item in *arr]
35
36filter = (arr, cond) ->
37 [item for item in *arr when cond item]
38
39reduce = (arr, init, action): init ->
40 init = action init, item for item in *arr
41
32-- pipe operator 42-- pipe operator
33[1, 2, 3] 43[1, 2, 3]
34 |> map (x) -> x * 2 44 |> map (x) -> x * 2
@@ -51,19 +61,29 @@ export 🌛 = "月之脚本"
51<YueDisplay> 61<YueDisplay>
52<pre> 62<pre>
53-- import syntax 63-- import syntax
54import "yue" as :p, :to_lua 64import p, to_lua from "yue"
55 65
56-- object literals 66-- object literals
57inventory = 67inventory =
58 equipment: 68 equipment:
59 * "sword" 69 - "sword"
60 * "shield" 70 - "shield"
61 items: 71 items:
62 * name: "potion" 72 - name: "potion"
63 count: 10 73 count: 10
64 * name: "bread" 74 - name: "bread"
65 count: 3 75 count: 3
66 76
77-- list comprehension
78map = (arr, action) ->
79 [action item for item in *arr]
80
81filter = (arr, cond) ->
82 [item for item in *arr when cond item]
83
84reduce = (arr, init, action): init ->
85 init = action init, item for item in *arr
86
67-- pipe operator 87-- pipe operator
68[1, 2, 3] 88[1, 2, 3]
69 |> map (x) -> x * 2 89 |> map (x) -> x * 2
@@ -132,14 +152,14 @@ export 🌛 = "月之脚本"
132 152
133&emsp;Use YueScript module in Lua: 153&emsp;Use YueScript module in Lua:
134 154
135* **Case 1** 155* **Case 1**
136Require "your_yuescript_entry.yue" in Lua. 156Require "your_yuescript_entry.yue" in Lua.
137```Lua 157```Lua
138require("yue")("your_yuescript_entry") 158require("yue")("your_yuescript_entry")
139``` 159```
140&emsp;And this code still works when you compile "your_yuescript_entry.yue" to "your_yuescript_entry.lua" in the same path. In the rest YueScript files just use the normal **require** or **import**. The code line numbers in error messages will also be handled correctly. 160&emsp;And this code still works when you compile "your_yuescript_entry.yue" to "your_yuescript_entry.lua" in the same path. In the rest YueScript files just use the normal **require** or **import**. The code line numbers in error messages will also be handled correctly.
141 161
142* **Case 2** 162* **Case 2**
143Require YueScript module and rewite message by hand. 163Require YueScript module and rewite message by hand.
144```lua 164```lua
145local yue = require("yue") 165local yue = require("yue")
@@ -151,7 +171,7 @@ end, function(err)
151end) 171end)
152``` 172```
153 173
154* **Case 3** 174* **Case 3**
155Use the YueScript compiler function in Lua. 175Use the YueScript compiler function in Lua.
156```lua 176```lua
157local yue = require("yue") 177local yue = require("yue")
@@ -203,12 +223,12 @@ Usage: yue [options|files|directories] ...
203 Execute without options to enter REPL, type symbol '$' 223 Execute without options to enter REPL, type symbol '$'
204 in a single line to start/stop multi-line mode 224 in a single line to start/stop multi-line mode
205``` 225```
206&emsp;&emsp;Use cases: 226&emsp;&emsp;Use cases:
207&emsp;&emsp;Recursively compile every YueScript file with extension **.yue** under current path: **yue .** 227&emsp;&emsp;Recursively compile every YueScript file with extension **.yue** under current path: **yue .**
208&emsp;&emsp;Compile and save results to a target path: **yue -t /target/path/ .** 228&emsp;&emsp;Compile and save results to a target path: **yue -t /target/path/ .**
209&emsp;&emsp;Compile and reserve debug info: **yue -l .** 229&emsp;&emsp;Compile and reserve debug info: **yue -l .**
210&emsp;&emsp;Compile and generate minified codes: **yue -m .** 230&emsp;&emsp;Compile and generate minified codes: **yue -m .**
211&emsp;&emsp;Execute raw codes: **yue -e 'print 123'** 231&emsp;&emsp;Execute raw codes: **yue -e 'print 123'**
212&emsp;&emsp;Execute a YueScript file: **yue -e main.yue** 232&emsp;&emsp;Execute a YueScript file: **yue -e main.yue**
213 233
214## Macro 234## Macro
@@ -424,6 +444,54 @@ print "Valid enum type:", $BodyType Static
424</pre> 444</pre>
425</YueDisplay> 445</YueDisplay>
426 446
447### Argument Validation
448
449You can declare the expected AST node types in the argument list, and check whether the incoming macro arguments meet the expectations at compile time.
450
451```moonscript
452macro printNumAndStr = (num `Num, str `String) -> |
453 print(
454 #{num}
455 #{str}
456 )
457
458$printNumAndStr 123, "hello"
459```
460<YueDisplay>
461<pre>
462macro printNumAndStr = (num `Num, str `String) -> |
463 print(
464 #{num}
465 #{str}
466 )
467
468$printNumAndStr 123, "hello"
469</pre>
470</YueDisplay>
471
472If you need more flexible argument checking, you can use the built-in `$is_ast` macro function to manually check at the appropriate place.
473
474```moonscript
475macro printNumAndStr = (num, str) ->
476 error "expected Num as first argument" unless $is_ast Num, num
477 error "expected String as second argument" unless $is_ast String, str
478 "print(#{num}, #{str})"
479
480$printNumAndStr 123, "hello"
481```
482<YueDisplay>
483<pre>
484macro printNumAndStr = (num, str) ->
485 error "expected Num as first argument" unless $is_ast Num, num
486 error "expected String as second argument" unless $is_ast String, str
487 "print(#{num}, #{str})"
488
489$printNumAndStr 123, "hello"
490</pre>
491</YueDisplay>
492
493For more details about available AST nodes, please refer to the uppercased definitions in [yue_parser.cpp](https://github.com/IppClub/YueScript/blob/main/src/yuescript/yue_parser.cpp).
494
427## Operator 495## Operator
428 496
429All 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. 497All 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.
@@ -566,11 +634,26 @@ merge = {...a, ...b}
566</pre> 634</pre>
567</YueDisplay> 635</YueDisplay>
568 636
637### Table Reversed Indexing
638
639You can use the **#** operator to get the last elements of a table.
640
641```moonscript
642last = data.items[#]
643second_last = data.items[#-1]
644```
645<YueDisplay>
646<pre>
647last = data.items[#]
648second_last = data.items[#-1]
649</pre>
650</YueDisplay>
651
569### Metatable 652### Metatable
570 653
571The **<>** operator can be used as a shortcut for metatable manipulation. 654The **<>** operator can be used as a shortcut for metatable manipulation.
572 655
573* **Metatable Creation** 656* **Metatable Creation**
574Create normal table with empty bracekets **<>** or metamethod key which is surrounded by **<>**. 657Create normal table with empty bracekets **<>** or metamethod key which is surrounded by **<>**.
575 658
576```moonscript 659```moonscript
@@ -606,7 +689,7 @@ close _ = &lt;close&gt;: -> print "out of scope"
606</pre> 689</pre>
607</YueDisplay> 690</YueDisplay>
608 691
609* **Metatable Accessing** 692* **Metatable Accessing**
610Accessing metatable with **<>** or metamethod name surrounded by **<>** or writing some expression in **<>**. 693Accessing metatable with **<>** or metamethod name surrounded by **<>** or writing some expression in **<>**.
611 694
612```moonscript 695```moonscript
@@ -630,7 +713,7 @@ print tb.item
630</pre> 713</pre>
631</YueDisplay> 714</YueDisplay>
632 715
633* **Metatable Destructure** 716* **Metatable Destructure**
634Destruct metatable with metamethod key surrounded by **<>**. 717Destruct metatable with metamethod key surrounded by **<>**.
635 718
636```moonscript 719```moonscript
@@ -732,34 +815,45 @@ a ??= false
732 815
733### Implicit Object 816### Implicit Object
734 817
735You can write a list of implicit structures that starts with the symbol **\*** inside a table block. If you are creating implicit object, the fields of the object must be with the same indent. 818You can write a list of implicit structures that starts with the symbol **\*** or **-** inside a table block. If you are creating implicit object, the fields of the object must be with the same indent.
819
736```moonscript 820```moonscript
821-- assignment with implicit object
737list = 822list =
738 * 1 823 * 1
739 * 2 824 * 2
740 * 3 825 * 3
741 826
827-- function call with implicit object
742func 828func
743 * 1 829 * 1
744 * 2 830 * 2
745 * 3 831 * 3
746 832
833-- return with implicit object
834f = ->
835 return
836 * 1
837 * 2
838 * 3
839
840-- table with implicit object
747tb = 841tb =
748 name: "abc" 842 name: "abc"
749 843
750 values: 844 values:
751 * "a" 845 - "a"
752 * "b" 846 - "b"
753 * "c" 847 - "c"
754 848
755 objects: 849 objects:
756 * name: "a" 850 - name: "a"
757 value: 1 851 value: 1
758 func: => @value + 1 852 func: => @value + 1
759 tb: 853 tb:
760 fieldA: 1 854 fieldA: 1
761 855
762 * name: "b" 856 - name: "b"
763 value: 2 857 value: 2
764 func: => @value + 2 858 func: => @value + 2
765 tb: { } 859 tb: { }
@@ -767,32 +861,42 @@ tb =
767``` 861```
768<YueDisplay> 862<YueDisplay>
769<pre> 863<pre>
864-- assignment with implicit object
770list = 865list =
771 * 1 866 * 1
772 * 2 867 * 2
773 * 3 868 * 3
774 869
870-- function call with implicit object
775func 871func
776 * 1 872 * 1
777 * 2 873 * 2
778 * 3 874 * 3
779 875
876-- return with implicit object
877f = ->
878 return
879 * 1
880 * 2
881 * 3
882
883-- table with implicit object
780tb = 884tb =
781 name: "abc" 885 name: "abc"
782 886
783 values: 887 values:
784 * "a" 888 - "a"
785 * "b" 889 - "b"
786 * "c" 890 - "c"
787 891
788 objects: 892 objects:
789 * name: "a" 893 - name: "a"
790 value: 1 894 value: 1
791 func: => @value + 1 895 func: => @value + 1
792 tb: 896 tb:
793 fieldA: 1 897 fieldA: 1
794 898
795 * name: "b" 899 - name: "b"
796 value: 2 900 value: 2
797 func: => @value + 2 901 func: => @value + 2
798 tb: { } 902 tb: { }
@@ -860,7 +964,7 @@ do
860 964
861The export statement offers a concise way to define modules. 965The export statement offers a concise way to define modules.
862 966
863* **Named Export** 967* **Named Export**
864Named export will define a local variable as well as adding a field in the exported table. 968Named export will define a local variable as well as adding a field in the exported table.
865 969
866```moonscript 970```moonscript
@@ -924,7 +1028,7 @@ export["a-b-c"] = 123
924</pre> 1028</pre>
925</YueDisplay> 1029</YueDisplay>
926 1030
927* **Unnamed Export** 1031* **Unnamed Export**
928Unnamed export will add the target item into the array part of the exported table. 1032Unnamed export will add the target item into the array part of the exported table.
929 1033
930```moonscript 1034```moonscript
@@ -954,7 +1058,7 @@ export with tmp
954</pre> 1058</pre>
955</YueDisplay> 1059</YueDisplay>
956 1060
957* **Default Export** 1061* **Default Export**
958Using the **default** keyword in export statement to replace the exported table with any thing. 1062Using the **default** keyword in export statement to replace the exported table with any thing.
959 1063
960```moonscript 1064```moonscript
@@ -1202,7 +1306,7 @@ If the destructuring statement is complicated, feel free to spread it out over a
1202</pre> 1306</pre>
1203</YueDisplay> 1307</YueDisplay>
1204 1308
1205It’s common to extract values from at table and assign them the local variables that have the same name as the key. In order to avoid repetition we can use the **:** prefix operator: 1309It's common to extract values from at table and assign them the local variables that have the same name as the key. In order to avoid repetition we can use the **:** prefix operator:
1206 1310
1207```moonscript 1311```moonscript
1208{:concat, :insert} = table 1312{:concat, :insert} = table
@@ -1246,6 +1350,52 @@ You can use `_` as placeholder when doing a list destructuring:
1246</pre> 1350</pre>
1247</YueDisplay> 1351</YueDisplay>
1248 1352
1353### Range Destructuring
1354
1355You can use the spread operator `...` in list destructuring to capture a range of values. This is useful when you want to extract specific elements from the beginning and end of a list while collecting the rest in between.
1356
1357```moonscript
1358orders = ["first", "second", "third", "fourth", "last"]
1359[first, ...bulk, last] = orders
1360print first -- prints: first
1361print bulk -- prints: {"second", "third", "fourth"}
1362print last -- prints: last
1363```
1364<YueDisplay>
1365<pre>
1366orders = ["first", "second", "third", "fourth", "last"]
1367[first, ...bulk, last] = orders
1368print first -- prints: first
1369print bulk -- prints: {"second", "third", "fourth"}
1370print last -- prints: last
1371</pre>
1372</YueDisplay>
1373
1374The spread operator can be used in different positions to capture different ranges, and you can use `_` as a placeholder for the values you don't want to capture:
1375
1376```moonscript
1377-- Capture everything after first element
1378[first, ...rest] = orders
1379
1380-- Capture everything before last element
1381[...start, last] = orders
1382
1383-- Capture things except the middle elements
1384[first, ..._, last] = orders
1385```
1386<YueDisplay>
1387<pre>
1388-- Capture everything after first element
1389[first, ...rest] = orders
1390
1391-- Capture everything before last element
1392[...start, last] = orders
1393
1394-- Capture things except the middle elements
1395[first, ..._, last] = orders
1396</pre>
1397</YueDisplay>
1398
1249### Destructuring In Other Places 1399### Destructuring In Other Places
1250 1400
1251Destructuring can also show up in places where an assignment implicitly takes place. An example of this is a for loop: 1401Destructuring can also show up in places where an assignment implicitly takes place. An example of this is a for loop:
@@ -1475,6 +1625,47 @@ catch err
1475</pre> 1625</pre>
1476</YueDisplay> 1626</YueDisplay>
1477 1627
1628### Try?
1629
1630`try?` is a simplified use for error handling syntax that omit the boolean status from the `try` statement, and it will return the result from the try block when success, return nil instead of error object otherwise.
1631
1632```moonscript
1633a, b, c = try? func!
1634
1635-- with nil coalescing operator
1636a = (try? func!) ?? "default"
1637
1638-- as function argument
1639f try? func!
1640
1641-- with catch block
1642f try?
1643 print 123
1644 func!
1645catch e
1646 print e
1647 e
1648```
1649<YueDisplay>
1650<pre>
1651a, b, c = try? func!
1652
1653-- with nil coalescing operator
1654a = (try? func!) ?? "default"
1655
1656-- as function argument
1657f try? func!
1658
1659-- with catch block
1660f try?
1661 print 123
1662 func!
1663catch e
1664 print e
1665 e
1666</pre>
1667</YueDisplay>
1668
1478## Attributes 1669## Attributes
1479 1670
1480Syntax support for Lua 5.4 attributes. And you can still use both the `const` and `close` declaration and get constant check and scoped callback working when targeting Lua versions below 5.4. 1671Syntax support for Lua 5.4 attributes. And you can still use both the `const` and `close` declaration and get constant check and scoped callback working when targeting Lua versions below 5.4.
@@ -1503,6 +1694,19 @@ const {:a, :b, c, d} = tb
1503</pre> 1694</pre>
1504</YueDisplay> 1695</YueDisplay>
1505 1696
1697You can also declare a global variable to be `const`.
1698
1699```moonscript
1700global const Constant = 123
1701-- Constant = 1
1702```
1703<YueDisplay>
1704<pre>
1705global const Constant = 123
1706-- Constant = 1
1707</pre>
1708</YueDisplay>
1709
1506## Literals 1710## Literals
1507 1711
1508All of the primitive literals in Lua can be used. This applies to numbers, strings, booleans, and **nil**. 1712All of the primitive literals in Lua can be used. This applies to numbers, strings, booleans, and **nil**.
@@ -1535,12 +1739,73 @@ You can use underscores in a number literal to increase readability.
1535```moonscript 1739```moonscript
1536integer = 1_000_000 1740integer = 1_000_000
1537hex = 0xEF_BB_BF 1741hex = 0xEF_BB_BF
1742binary = 0B10011
1538``` 1743```
1539<YueDisplay> 1744<YueDisplay>
1540 1745
1541<pre> 1746<pre>
1542integer = 1_000_000 1747integer = 1_000_000
1543hex = 0xEF_BB_BF 1748hex = 0xEF_BB_BF
1749binary = 0B10011
1750</pre>
1751</YueDisplay>
1752
1753### YAML Multiline String
1754
1755The `|` prefix introduces a YAML-style multiline string literal:
1756
1757```moonscript
1758str = |
1759 key: value
1760 list:
1761 - item1
1762 - #{expr}
1763```
1764<YueDisplay>
1765<pre>
1766str = |
1767 key: value
1768 list:
1769 - item1
1770 - #{expr}
1771</pre>
1772</YueDisplay>
1773
1774This allows writing structured multiline text conveniently. All line breaks and indentation are preserved relative to the first non-empty line, and expressions inside `#{...}` are interpolated automatically as `tostring(expr)`.
1775
1776YAML Multiline String automatically detects the common leading whitespace prefix (minimum indentation across all non-empty lines) and removes it from all lines. This makes it easy to indent your code visually without affecting the resulting string content.
1777
1778```moonscript
1779fn = ->
1780 str = |
1781 foo:
1782 bar: baz
1783 return str
1784```
1785<YueDisplay>
1786<pre>
1787fn = ->
1788 str = |
1789 foo:
1790 bar: baz
1791 return str
1792</pre>
1793</YueDisplay>
1794
1795Internal indentation is preserved relative to the removed common prefix, allowing clean nested structures.
1796
1797All special characters like quotes (`"`) and backslashes (`\`) in the YAMLMultiline block are automatically escaped so that the generated Lua string is syntactically valid and behaves as expected.
1798
1799```moonscript
1800str = |
1801 path: "C:\Program Files\App"
1802 note: 'He said: "#{Hello}!"'
1803```
1804<YueDisplay>
1805<pre>
1806str = |
1807 path: "C:\Program Files\App"
1808 note: 'He said: "#{Hello}!"'
1544</pre> 1809</pre>
1545</YueDisplay> 1810</YueDisplay>
1546 1811
@@ -1902,22 +2167,22 @@ x * 2
1902</pre> 2167</pre>
1903</YueDisplay> 2168</YueDisplay>
1904 2169
1905If you wish to have further code after your backcalls, you can set them aside with a do statement. 2170If you wish to have further code after your backcalls, you can set them aside with a do statement. And the parentheses can be omitted with non-fat arrow functions.
1906 2171
1907```moonscript 2172```moonscript
1908result, msg = do 2173result, msg = do
1909 (data) <- readAsync "filename.txt" 2174 data <- readAsync "filename.txt"
1910 print data 2175 print data
1911 (info) <- processAsync data 2176 info <- processAsync data
1912 check info 2177 check info
1913print result, msg 2178print result, msg
1914``` 2179```
1915<YueDisplay> 2180<YueDisplay>
1916<pre> 2181<pre>
1917result, msg = do 2182result, msg = do
1918 (data) <- readAsync "filename.txt" 2183 data <- readAsync "filename.txt"
1919 print data 2184 print data
1920 (info) <- processAsync data 2185 info <- processAsync data
1921 check info 2186 check info
1922print result, msg 2187print result, msg
1923</pre> 2188</pre>
@@ -2096,13 +2361,11 @@ doubled = [item * 2 for i, item in ipairs items]
2096The items included in the new table can be restricted with a when clause: 2361The items included in the new table can be restricted with a when clause:
2097 2362
2098```moonscript 2363```moonscript
2099iter = ipairs items 2364slice = [item for i, item in ipairs items when i > 1 and i < 3]
2100slice = [item for i, item in iter when i > 1 and i < 3]
2101``` 2365```
2102<YueDisplay> 2366<YueDisplay>
2103<pre> 2367<pre>
2104iter = ipairs items 2368slice = [item for i, item in ipairs items when i > 1 and i < 3]
2105slice = [item for i, item in iter when i > 1 and i < 3]
2106</pre> 2369</pre>
2107</YueDisplay> 2370</YueDisplay>
2108 2371
@@ -2250,6 +2513,45 @@ slice = [item for item in *items[,,2]]
2250</pre> 2513</pre>
2251</YueDisplay> 2514</YueDisplay>
2252 2515
2516Both the minimum and maximum bounds can be negative, which means that the bounds are counted from the end of the table.
2517
2518```moonscript
2519-- take the last 4 items
2520slice = [item for item in *items[-4,-1]]
2521```
2522<YueDisplay>
2523<pre>
2524-- take the last 4 items
2525slice = [item for item in *items[-4,-1]]
2526</pre>
2527</YueDisplay>
2528
2529The step size can also be negative, which means that the items are taken in reverse order.
2530
2531```moonscript
2532reverse_slice = [item for item in *items[-1,1,-1]]
2533```
2534<YueDisplay>
2535<pre>
2536reverse_slice = [item for item in *items[-1,1,-1]]
2537</pre>
2538</YueDisplay>
2539
2540#### Slicing Expression
2541
2542Slicing can also be used as an expression. This is useful for getting a sub-list of a table.
2543
2544```moonscript
2545-- take the 2nd and 4th items as a new list
2546sub_list = items[2, 4]
2547```
2548<YueDisplay>
2549<pre>
2550-- take the 2nd and 4th items as a new list
2551sub_list = items[2, 4]
2552</pre>
2553</YueDisplay>
2554
2253## For Loop 2555## For Loop
2254 2556
2255There are two for loop forms, just like in Lua. A numeric one and a generic one: 2557There are two for loop forms, just like in Lua. A numeric one and a generic one:
@@ -2326,6 +2628,23 @@ doubled_evens = for i = 1, 20
2326</pre> 2628</pre>
2327</YueDisplay> 2629</YueDisplay>
2328 2630
2631In addition, for loops support break with a return value, allowing the loop itself to be used as an expression that exits early with a meaningful result.
2632
2633For example, to find the first number greater than 10:
2634
2635```moonscript
2636first_large = for n in *numbers
2637 break n if n > 10
2638```
2639<YueDisplay>
2640<pre>
2641first_large = for n in *numbers
2642 break n if n > 10
2643</pre>
2644</YueDisplay>
2645
2646This break-with-value syntax enables concise and expressive search or early-exit patterns directly within loop expressions.
2647
2329You can also filter values by combining the for loop expression with the continue statement. 2648You can also filter values by combining the for loop expression with the continue statement.
2330 2649
2331For loops at the end of a function body are not accumulated into a table for a return value (Instead the function will return nil). Either an explicit return statement can be used, or the loop can be converted into a list comprehension. 2650For loops at the end of a function body are not accumulated into a table for a return value (Instead the function will return nil). Either an explicit return statement can be used, or the loop can be converted into a list comprehension.
@@ -2347,7 +2666,7 @@ print func_b! -- prints table object
2347</pre> 2666</pre>
2348</YueDisplay> 2667</YueDisplay>
2349 2668
2350This is done to avoid the needless creation of tables for functions that don’t need to return the results of the loop. 2669This is done to avoid the needless creation of tables for functions that don't need to return the results of the loop.
2351 2670
2352## Repeat Loop 2671## Repeat Loop
2353 2672
@@ -2626,28 +2945,26 @@ reader\parse_line! until reader\eof!
2626 2945
2627## Switch 2946## Switch
2628 2947
2629The switch statement is shorthand for writing a series of if statements that check against the same value. Note that the value is only evaluated once. Like if statements, switches can have an else block to handle no matches. Comparison is done with the == operator. 2948The switch statement is shorthand for writing a series of if statements that check against the same value. Note that the value is only evaluated once. Like if statements, switches can have an else block to handle no matches. Comparison is done with the == operator. In switch statement, you can also use assignment expression to store temporary variable value.
2630 2949
2631```moonscript 2950```moonscript
2632name = "Dan" 2951switch name := "Dan"
2633switch name
2634 when "Robert" 2952 when "Robert"
2635 print "You are Robert" 2953 print "You are Robert"
2636 when "Dan", "Daniel" 2954 when "Dan", "Daniel"
2637 print "Your name, it's Dan" 2955 print "Your name, it's Dan"
2638 else 2956 else
2639 print "I don't know about your name" 2957 print "I don't know about you with name #{name}"
2640``` 2958```
2641<YueDisplay> 2959<YueDisplay>
2642<pre> 2960<pre>
2643name = "Dan" 2961switch name := "Dan"
2644switch name
2645 when "Robert" 2962 when "Robert"
2646 print "You are Robert" 2963 print "You are Robert"
2647 when "Dan", "Daniel" 2964 when "Dan", "Daniel"
2648 print "Your name, it's Dan" 2965 print "Your name, it's Dan"
2649 else 2966 else
2650 print "I don't know about your name" 2967 print "I don't know about you with name #{name}"
2651</pre> 2968</pre>
2652</YueDisplay> 2969</YueDisplay>
2653 2970
@@ -2678,7 +2995,7 @@ next_number = switch b
2678</pre> 2995</pre>
2679</YueDisplay> 2996</YueDisplay>
2680 2997
2681We can use the then keyword to write a switch’s when block on a single line. No extra keyword is needed to write the else block on a single line. 2998We can use the then keyword to write a switch's when block on a single line. No extra keyword is needed to write the else block on a single line.
2682 2999
2683```moonscript 3000```moonscript
2684msg = switch math.random(1, 5) 3001msg = switch math.random(1, 5)
@@ -2724,7 +3041,7 @@ else
2724</pre> 3041</pre>
2725</YueDisplay> 3042</YueDisplay>
2726 3043
2727It 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. 3044It 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.
2728 3045
2729### Table Matching 3046### Table Matching
2730 3047
@@ -2784,6 +3101,123 @@ switch item
2784</pre> 3101</pre>
2785</YueDisplay> 3102</YueDisplay>
2786 3103
3104You can also match against array elements, table fields, and even nested structures with array or table literals.
3105
3106Match against array elements.
3107
3108```moonscript
3109switch tb
3110 when [1, 2, 3]
3111 print "1, 2, 3"
3112 when [1, b, 3]
3113 print "1, #{b}, 3"
3114 when [1, 2, b = 3] -- b has a default value
3115 print "1, 2, #{b}"
3116```
3117<YueDisplay>
3118<pre>
3119switch tb
3120 when [1, 2, 3]
3121 print "1, 2, 3"
3122 when [1, b, 3]
3123 print "1, #{b}, 3"
3124 when [1, 2, b = 3] -- b has a default value
3125 print "1, 2, #{b}"
3126</pre>
3127</YueDisplay>
3128
3129Match against table fields with destructuring.
3130
3131```moonscript
3132switch tb
3133 when success: true, :result
3134 print "success", result
3135 when success: false
3136 print "failed", result
3137 else
3138 print "invalid"
3139```
3140<YueDisplay>
3141<pre>
3142switch tb
3143 when success: true, :result
3144 print "success", result
3145 when success: false
3146 print "failed", result
3147 else
3148 print "invalid"
3149</pre>
3150</YueDisplay>
3151
3152Match against nested table structures.
3153
3154```moonscript
3155switch tb
3156 when data: {type: "success", :content}
3157 print "success", content
3158 when data: {type: "error", :content}
3159 print "failed", content
3160 else
3161 print "invalid"
3162```
3163<YueDisplay>
3164<pre>
3165switch tb
3166 when data: {type: "success", :content}
3167 print "success", content
3168 when data: {type: "error", :content}
3169 print "failed", content
3170 else
3171 print "invalid"
3172</pre>
3173</YueDisplay>
3174
3175Match against array of tables.
3176
3177```moonscript
3178switch tb
3179 when [
3180 {a: 1, b: 2}
3181 {a: 3, b: 4}
3182 {a: 5, b: 6}
3183 fourth
3184 ]
3185 print "matched", fourth
3186```
3187<YueDisplay>
3188<pre>
3189switch tb
3190 when [
3191 {a: 1, b: 2}
3192 {a: 3, b: 4}
3193 {a: 5, b: 6}
3194 fourth
3195 ]
3196 print "matched", fourth
3197</pre>
3198</YueDisplay>
3199
3200Match against a list and capture a range of elements.
3201
3202```moonscript
3203segments = ["admin", "users", "logs", "view"]
3204switch segments
3205 when [...groups, resource, action]
3206 print "Group:", groups -- prints: {"admin", "users"}
3207 print "Resource:", resource -- prints: "logs"
3208 print "Action:", action -- prints: "view"
3209```
3210<YueDisplay>
3211<pre>
3212segments = ["admin", "users", "logs", "view"]
3213switch segments
3214 when [...groups, resource, action]
3215 print "Group:", groups -- prints: {"admin", "users"}
3216 print "Resource:", resource -- prints: "logs"
3217 print "Action:", action -- prints: "view"
3218</pre>
3219</YueDisplay>
3220
2787## Object Oriented Programming 3221## Object Oriented Programming
2788 3222
2789In 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. 3223In 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.
@@ -2915,7 +3349,7 @@ class BackPack extends Inventory
2915 3349
2916Here we extend our Inventory class, and limit the amount of items it can carry. 3350Here we extend our Inventory class, and limit the amount of items it can carry.
2917 3351
2918In this example, we don’t define a constructor on the subclass, so the parent class' constructor is called when we make a new instance. If we did define a constructor then we can use the super method to call the parent constructor. 3352In this example, we don't define a constructor on the subclass, so the parent class' constructor is called when we make a new instance. If we did define a constructor then we can use the super method to call the parent constructor.
2919 3353
2920Whenever a class inherits from another, it sends a message to the parent class by calling the method __inherited on the parent class if it exists. The function receives two arguments, the class that is being inherited and the child class. 3354Whenever a class inherits from another, it sends a message to the parent class by calling the method __inherited on the parent class if it exists. The function receives two arguments, the class that is being inherited and the child class.
2921 3355
@@ -3002,13 +3436,13 @@ print BackPack.size -- prints 10
3002 3436
3003The class object is what we create when we use a class statement. The class object is stored in a variable of the same name of the class. 3437The class object is what we create when we use a class statement. The class object is stored in a variable of the same name of the class.
3004 3438
3005The class object can be called like a function in order to create new instances. That’s how we created instances of classes in the examples above. 3439The class object can be called like a function in order to create new instances. That's how we created instances of classes in the examples above.
3006 3440
3007A class is made up of two tables. The class table itself, and the base table. The base is used as the metatable for all the instances. All properties listed in the class declaration are placed in the base. 3441A class is made up of two tables. The class table itself, and the base table. The base is used as the metatable for all the instances. All properties listed in the class declaration are placed in the base.
3008 3442
3009The class object’s metatable reads properties from the base if they don’t exist in the class object. This means we can access functions and properties directly from the class. 3443The class object's metatable reads properties from the base if they don't exist in the class object. This means we can access functions and properties directly from the class.
3010 3444
3011It is important to note that assigning to the class object does not assign into the base, so it’s not a valid way to add new methods to instances. Instead the base must explicitly be changed. See the __base field below. 3445It is important to note that assigning to the class object does not assign into the base, so it's not a valid way to add new methods to instances. Instead the base must explicitly be changed. See the __base field below.
3012 3446
3013The class object has a couple special properties: 3447The class object has a couple special properties:
3014 3448
@@ -3081,7 +3515,7 @@ print Counter.count -- prints 2
3081</pre> 3515</pre>
3082</YueDisplay> 3516</YueDisplay>
3083 3517
3084The calling semantics of @@ are similar to @. Calling a @@ name will pass the class in as the first argument using Lua’s colon syntax. 3518The calling semantics of @@ are similar to @. Calling a @@ name will pass the class in as the first argument using Lua's colon syntax.
3085 3519
3086```moonscript 3520```moonscript
3087@@hello 1,2,3,4 3521@@hello 1,2,3,4
@@ -3096,7 +3530,7 @@ The calling semantics of @@ are similar to @. Calling a @@ name will pass the cl
3096 3530
3097In the body of a class declaration, we can have normal expressions in addition to key/value pairs. In this context, self is equal to the class object. 3531In the body of a class declaration, we can have normal expressions in addition to key/value pairs. In this context, self is equal to the class object.
3098 3532
3099Here is an alternative way to create a class variable compared to what’s described above: 3533Here is an alternative way to create a class variable compared to what's described above:
3100 3534
3101```moonscript 3535```moonscript
3102class Things 3536class Things
@@ -3362,19 +3796,19 @@ In this usage, with can be seen as a special form of the K combinator.
3362The expression in the with statement can also be an assignment, if you want to give a name to the expression. 3796The expression in the with statement can also be an assignment, if you want to give a name to the expression.
3363 3797
3364```moonscript 3798```moonscript
3365with str = "Hello" 3799with str := "Hello"
3366 print "original:", str 3800 print "original:", str
3367 print "upper:", \upper! 3801 print "upper:", \upper!
3368``` 3802```
3369<YueDisplay> 3803<YueDisplay>
3370<pre> 3804<pre>
3371with str = "Hello" 3805with str := "Hello"
3372 print "original:", str 3806 print "original:", str
3373 print "upper:", \upper! 3807 print "upper:", \upper!
3374</pre> 3808</pre>
3375</YueDisplay> 3809</YueDisplay>
3376 3810
3377Accessing special keys with `[]` in a `with` statement. 3811You can access special keys with `[]` in a `with` statement.
3378 3812
3379```moonscript 3813```moonscript
3380with tb 3814with tb
@@ -3397,6 +3831,18 @@ with tb
3397</pre> 3831</pre>
3398</YueDisplay> 3832</YueDisplay>
3399 3833
3834`with?` is an enhanced version of `with` syntax, which introduces an existential check to safely access objects that may be nil without explicit null checks.
3835
3836```moonscript
3837with? obj
3838 print obj.name
3839```
3840<YueDisplay>
3841<pre>
3842with? obj
3843 print obj.name
3844</pre>
3845</YueDisplay>
3400 3846
3401## Do 3847## Do
3402 3848
@@ -3417,7 +3863,7 @@ print var -- nil here
3417</pre> 3863</pre>
3418</YueDisplay> 3864</YueDisplay>
3419 3865
3420YueScript’s **do** can also be used an expression . Allowing you to combine multiple lines into one. The result of the do expression is the last statement in its body. 3866YueScript's **do** can also be used an expression . Allowing you to combine multiple lines into one. The result of the do expression is the last statement in its body.
3421 3867
3422```moonscript 3868```moonscript
3423counter = do 3869counter = do
@@ -3543,7 +3989,7 @@ print i -- will print 0
3543</pre> 3989</pre>
3544</YueDisplay> 3990</YueDisplay>
3545 3991
3546In my_func, we've overwritten the value of i mistakenly. In this example it is quite obvious, but consider a large, or foreign code base where it isn’t clear what names have already been declared. 3992In my_func, we've overwritten the value of i mistakenly. In this example it is quite obvious, but consider a large, or foreign code base where it isn't clear what names have already been declared.
3547 3993
3548It would be helpful to say which variables from the enclosing scope we intend on change, in order to prevent us from changing others by accident. 3994It would be helpful to say which variables from the enclosing scope we intend on change, in order to prevent us from changing others by accident.
3549 3995
diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md
index 1a2da96..15f4768 100755
--- a/doc/docs/zh/doc/README.md
+++ b/doc/docs/zh/doc/README.md
@@ -16,19 +16,29 @@ Yue(月)是中文中“月亮â€çš„å称。
16### 月之脚本概览 16### 月之脚本概览
17```moonscript 17```moonscript
18-- 导入语法 18-- 导入语法
19import "yue" as :p, :to_lua 19import p, to_lua from "yue"
20 20
21-- éšå¼å¯¹è±¡ 21-- éšå¼å¯¹è±¡
22inventory = 22inventory =
23 equipment: 23 equipment:
24 * "sword" 24 - "sword"
25 * "shield" 25 - "shield"
26 items: 26 items:
27 * name: "potion" 27 - name: "potion"
28 count: 10 28 count: 10
29 * name: "bread" 29 - name: "bread"
30 count: 3 30 count: 3
31 31
32-- 列表推导
33map = (arr, action) ->
34 [action item for item in *arr]
35
36filter = (arr, cond) ->
37 [item for item in *arr when cond item]
38
39reduce = (arr, init, action): init ->
40 init = action init, item for item in *arr
41
32-- ç®¡é“æ“作符 42-- ç®¡é“æ“作符
33[1, 2, 3] 43[1, 2, 3]
34 |> map (x) -> x * 2 44 |> map (x) -> x * 2
@@ -51,19 +61,29 @@ export 🌛 = "月之脚本"
51<YueDisplay> 61<YueDisplay>
52<pre> 62<pre>
53-- 导入语法 63-- 导入语法
54import "yue" as :p, :to_lua 64import p, to_lua from "yue"
55 65
56-- éšå¼å¯¹è±¡ 66-- éšå¼å¯¹è±¡
57inventory = 67inventory =
58 equipment: 68 equipment:
59 * "sword" 69 - "sword"
60 * "shield" 70 - "shield"
61 items: 71 items:
62 * name: "potion" 72 - name: "potion"
63 count: 10 73 count: 10
64 * name: "bread" 74 - name: "bread"
65 count: 3 75 count: 3
66 76
77-- 列表推导
78map = (arr, action) ->
79 [action item for item in *arr]
80
81filter = (arr, cond) ->
82 [item for item in *arr when cond item]
83
84reduce = (arr, init, action): init ->
85 init = action init, item for item in *arr
86
67-- ç®¡é“æ“作符 87-- ç®¡é“æ“作符
68[1, 2, 3] 88[1, 2, 3]
69 |> map (x) -> x * 2 89 |> map (x) -> x * 2
@@ -122,7 +142,7 @@ export 🌛 = "月之脚本"
122 142
123* **下载预编译的二进制程åº** 143* **下载预编译的二进制程åº**
124 144
125&emsp;您å¯ä»¥ä¸‹è½½é¢„编译的二进制程åºï¼ŒåŒ…括兼容ä¸åŒ Lua ç‰ˆæœ¬çš„äºŒè¿›åˆ¶å¯æ‰§è¡Œæ–‡ä»¶å’Œåº“文件。 145&emsp;ä½ å¯ä»¥ä¸‹è½½é¢„编译的二进制程åºï¼ŒåŒ…括兼容ä¸åŒ Lua ç‰ˆæœ¬çš„äºŒè¿›åˆ¶å¯æ‰§è¡Œæ–‡ä»¶å’Œåº“文件。
126 146
127&emsp;在[这里](https://github.com/IppClub/YueScript/releases)下载预编译的二进制程åºã€‚ 147&emsp;在[这里](https://github.com/IppClub/YueScript/releases)下载预编译的二进制程åºã€‚
128 148
@@ -132,14 +152,14 @@ export 🌛 = "月之脚本"
132 152
133在Lua中使用月之脚本模å—: 153在Lua中使用月之脚本模å—:
134 154
135* **用法 1** 155* **用法 1**
136在Lua中引入 "ä½ çš„è„šæœ¬å…¥å£æ–‡ä»¶.yue"。 156在Lua中引入 "ä½ çš„è„šæœ¬å…¥å£æ–‡ä»¶.yue"。
137```Lua 157```Lua
138require("yue")("ä½ çš„è„šæœ¬å…¥å£æ–‡ä»¶") 158require("yue")("ä½ çš„è„šæœ¬å…¥å£æ–‡ä»¶")
139``` 159```
140当你在åŒä¸€è·¯å¾„下把 "ä½ çš„è„šæœ¬å…¥å£æ–‡ä»¶.yue" 编译æˆäº† "ä½ çš„è„šæœ¬å…¥å£æ–‡ä»¶.lua" 时,ä»ç„¶å¯ä»¥ä½¿ç”¨è¿™ä¸ªä»£ç åŠ è½½ .lua ä»£ç æ–‡ä»¶ã€‚在其余的月之脚本文件中,åªéœ€æ­£å¸¸ä½¿ç”¨ **require** 或 **import**进行脚本引用å³å¯ã€‚错误消æ¯ä¸­çš„代ç è¡Œå·ä¹Ÿä¼šè¢«æ­£ç¡®å¤„ç†ã€‚ 160当你在åŒä¸€è·¯å¾„下把 "ä½ çš„è„šæœ¬å…¥å£æ–‡ä»¶.yue" 编译æˆäº† "ä½ çš„è„šæœ¬å…¥å£æ–‡ä»¶.lua" 时,ä»ç„¶å¯ä»¥ä½¿ç”¨è¿™ä¸ªä»£ç åŠ è½½ .lua ä»£ç æ–‡ä»¶ã€‚在其余的月之脚本文件中,åªéœ€æ­£å¸¸ä½¿ç”¨ **require** 或 **import**进行脚本引用å³å¯ã€‚错误消æ¯ä¸­çš„代ç è¡Œå·ä¹Ÿä¼šè¢«æ­£ç¡®å¤„ç†ã€‚
141 161
142* **用法 2** 162* **用法 2**
143手动引入月之脚本模å—å¹¶é‡å†™é”™è¯¯æ¶ˆæ¯æ¥å¸®åŠ©è°ƒè¯•ã€‚ 163手动引入月之脚本模å—å¹¶é‡å†™é”™è¯¯æ¶ˆæ¯æ¥å¸®åŠ©è°ƒè¯•ã€‚
144```lua 164```lua
145local yue = require("yue") 165local yue = require("yue")
@@ -151,7 +171,7 @@ end, function(err)
151end) 171end)
152``` 172```
153 173
154* **用法 3** 174* **用法 3**
155在Lua中使用月之脚本编译器功能。 175在Lua中使用月之脚本编译器功能。
156```lua 176```lua
157local yue = require("yue") 177local yue = require("yue")
@@ -202,12 +222,12 @@ f!
202 䏿·»åŠ ä»»ä½•é€‰é¡¹æ‰§è¡Œå‘½ä»¤å¯ä»¥è¿›å…¥REPL模å¼ï¼Œ 222 䏿·»åŠ ä»»ä½•é€‰é¡¹æ‰§è¡Œå‘½ä»¤å¯ä»¥è¿›å…¥REPL模å¼ï¼Œ
203 在å•è¡Œè¾“å…¥ç¬¦å· '$' å¹¶æ¢è¡ŒåŽï¼Œå¯ä»¥å¼€å§‹æˆ–æ˜¯åœæ­¢å¤šè¡Œè¾“å…¥æ¨¡å¼ 223 在å•è¡Œè¾“å…¥ç¬¦å· '$' å¹¶æ¢è¡ŒåŽï¼Œå¯ä»¥å¼€å§‹æˆ–æ˜¯åœæ­¢å¤šè¡Œè¾“入模å¼
204``` 224```
205&emsp;&emsp;使用案例: 225&emsp;&emsp;使用案例:
206&emsp;&emsp;递归编译当å‰è·¯å¾„下扩展å为 **.yue** çš„æ¯ä¸ªæœˆä¹‹è„šæœ¬æ–‡ä»¶ï¼š **yue .** 226&emsp;&emsp;递归编译当å‰è·¯å¾„下扩展å为 **.yue** çš„æ¯ä¸ªæœˆä¹‹è„šæœ¬æ–‡ä»¶ï¼š **yue .**
207&emsp;&emsp;编译并将结果ä¿å­˜åˆ°ç›®æ ‡è·¯å¾„: **yue -t /target/path/ .** 227&emsp;&emsp;编译并将结果ä¿å­˜åˆ°ç›®æ ‡è·¯å¾„: **yue -t /target/path/ .**
208&emsp;&emsp;编译并ä¿ç•™è°ƒè¯•ä¿¡æ¯ï¼š **yue -l .** 228&emsp;&emsp;编译并ä¿ç•™è°ƒè¯•ä¿¡æ¯ï¼š **yue -l .**
209&emsp;&emsp;编译并生æˆåŽ‹ç¼©ä»£ç ï¼š **yue -m .** 229&emsp;&emsp;编译并生æˆåŽ‹ç¼©ä»£ç ï¼š **yue -m .**
210&emsp;&emsp;直接执行代ç ï¼š **yue -e 'print 123'** 230&emsp;&emsp;直接执行代ç ï¼š **yue -e 'print 123'**
211&emsp;&emsp;执行一个月之脚本文件: **yue -e main.yue** 231&emsp;&emsp;执行一个月之脚本文件: **yue -e main.yue**
212 232
213## å® 233## å®
@@ -333,7 +353,7 @@ end
333 353
334### å¯¼å‡ºå® 354### 导出å®
335 355
336å®å‡½æ•°å¯ä»¥ä»Žä¸€ä¸ªæ¨¡å—中导出,并在å¦ä¸€ä¸ªæ¨¡å—中导入。您必须将导出的å®å‡½æ•°æ”¾åœ¨ä¸€ä¸ªå•ç‹¬çš„æ–‡ä»¶ä¸­ä½¿ç”¨ï¼Œè€Œä¸”åªæœ‰å®å®šä¹‰ã€å®å¯¼å…¥å’Œå®å±•å¼€å¯ä»¥æ”¾å…¥è¿™ä¸ªå®å¯¼å‡ºæ¨¡å—中。 356å®å‡½æ•°å¯ä»¥ä»Žä¸€ä¸ªæ¨¡å—中导出,并在å¦ä¸€ä¸ªæ¨¡å—中导入。你必须将导出的å®å‡½æ•°æ”¾åœ¨ä¸€ä¸ªå•ç‹¬çš„æ–‡ä»¶ä¸­ä½¿ç”¨ï¼Œè€Œä¸”åªæœ‰å®å®šä¹‰ã€å®å¯¼å…¥å’Œå®å±•å¼€å¯ä»¥æ”¾å…¥è¿™ä¸ªå®å¯¼å‡ºæ¨¡å—中。
337```moonscript 357```moonscript
338-- 文件: utils.yue 358-- 文件: utils.yue
339export macro map = (items, action) -> "[#{action} for _ in *#{items}]" 359export macro map = (items, action) -> "[#{action} for _ in *#{items}]"
@@ -422,6 +442,54 @@ print "有效的枚举类型:", $BodyType Static
422</pre> 442</pre>
423</YueDisplay> 443</YueDisplay>
424 444
445### å®å‚数检查
446
447å¯ä»¥ç›´æŽ¥åœ¨å‚数列表中声明期望的 AST 节点类型,并在编译时检查传入的å®å‚数是å¦ç¬¦åˆé¢„期。
448
449```moonscript
450macro printNumAndStr = (num `Num, str `String) -> |
451 print(
452 #{num}
453 #{str}
454 )
455
456$printNumAndStr 123, "hello"
457```
458<YueDisplay>
459<pre>
460macro printNumAndStr = (num `Num, str `String) -> |
461 print(
462 #{num}
463 #{str}
464 )
465
466$printNumAndStr 123, "hello"
467</pre>
468</YueDisplay>
469
470如果需è¦åšæ›´åŠ çµæ´»çš„傿•°æ£€æŸ¥æ“作,å¯ä»¥ä½¿ç”¨å†…置的 `$is_ast` å®å‡½æ•°åœ¨åˆé€‚çš„ä½ç½®è¿›è¡Œæ‰‹åŠ¨æ£€æŸ¥ã€‚
471
472```moonscript
473macro printNumAndStr = (num, str) ->
474 error "expected Num as first argument" unless $is_ast Num, num
475 error "expected String as second argument" unless $is_ast String, str
476 "print(#{num}, #{str})"
477
478$printNumAndStr 123, "hello"
479```
480<YueDisplay>
481<pre>
482macro printNumAndStr = (num, str) ->
483 error "expected Num as first argument" unless $is_ast Num, num
484 error "expected String as second argument" unless $is_ast String, str
485 "print(#{num}, #{str})"
486
487$printNumAndStr 123, "hello"
488</pre>
489</YueDisplay>
490
491更多关于å¯ç”¨ AST 节点的详细信æ¯ï¼Œè¯·å‚考 [yue_parser.cpp](https://github.com/IppClub/YueScript/blob/main/src/yuescript/yue_parser.cpp) 中大写的规则定义。
492
425## æ“作符 493## æ“作符
426 494
427Lua的所有二元和一元æ“作符在月之脚本中都是å¯ç”¨çš„。此外,**!=** ç¬¦å·æ˜¯ **~=** 的别å,而 **\\** 或 **::** å‡å¯ç”¨äºŽç¼–写链å¼å‡½æ•°è°ƒç”¨ï¼Œå¦‚写作 `tb\func!` 或 `tb::func!`。此外月之脚本还æä¾›äº†ä¸€äº›å…¶ä»–特殊的æ“作符,以编写更具表达力的代ç ã€‚ 495Lua的所有二元和一元æ“作符在月之脚本中都是å¯ç”¨çš„。此外,**!=** ç¬¦å·æ˜¯ **~=** 的别å,而 **\\** 或 **::** å‡å¯ç”¨äºŽç¼–写链å¼å‡½æ•°è°ƒç”¨ï¼Œå¦‚写作 `tb\func!` 或 `tb::func!`。此外月之脚本还æä¾›äº†ä¸€äº›å…¶ä»–特殊的æ“作符,以编写更具表达力的代ç ã€‚
@@ -439,7 +507,7 @@ tb::func! if tb != nil
439 507
440### 链弿¯”较 508### 链弿¯”较
441 509
442您å¯ä»¥åœ¨æœˆä¹‹è„šæœ¬ä¸­è¿›è¡Œæ¯”è¾ƒè¡¨è¾¾å¼çš„链å¼ä¹¦å†™ï¼š 510ä½ å¯ä»¥åœ¨æœˆä¹‹è„šæœ¬ä¸­è¿›è¡Œæ¯”è¾ƒè¡¨è¾¾å¼çš„链å¼ä¹¦å†™ï¼š
443 511
444```moonscript 512```moonscript
445print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5 513print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
@@ -528,7 +596,7 @@ tab[] = "Value"
528 596
529### 表扩展 597### 表扩展
530 598
531您å¯ä»¥ä½¿ç”¨å‰ç½® `...` æ“作符在Lua表中æ’入数组表或哈希表。 599ä½ å¯ä»¥ä½¿ç”¨å‰ç½® `...` æ“作符在Lua表中æ’入数组表或哈希表。
532 600
533```moonscript 601```moonscript
534parts = 602parts =
@@ -565,11 +633,26 @@ merge = {...a, ...b}
565</pre> 633</pre>
566</YueDisplay> 634</YueDisplay>
567 635
636### 表åå‘索引
637
638ä½ å¯ä»¥ä½¿ç”¨ **#** æ“作符æ¥åå‘索引表中的元素。
639
640```moonscript
641last = data.items[#]
642second_last = data.items[#-1]
643```
644<YueDisplay>
645<pre>
646last = data.items[#]
647second_last = data.items[#-1]
648</pre>
649</YueDisplay>
650
568### 元表 651### 元表
569 652
570**<>** æ“ä½œç¬¦å¯æä¾›å…ƒè¡¨æ“ä½œçš„å¿«æ·æ–¹å¼ã€‚ 653**<>** æ“ä½œç¬¦å¯æä¾›å…ƒè¡¨æ“ä½œçš„å¿«æ·æ–¹å¼ã€‚
571 654
572* **元表创建** 655* **元表创建**
573ä½¿ç”¨ç©ºæ‹¬å· **<>** 或被 **<>** 包围的元方法键创建普通的Lua表。 656ä½¿ç”¨ç©ºæ‹¬å· **<>** 或被 **<>** 包围的元方法键创建普通的Lua表。
574 657
575```moonscript 658```moonscript
@@ -605,7 +688,7 @@ close _ = &lt;close&gt;: -> print "超出范围"
605</pre> 688</pre>
606</YueDisplay> 689</YueDisplay>
607 690
608* **元表访问** 691* **元表访问**
609使用 **<>** 或被 **<>** åŒ…å›´çš„å…ƒæ–¹æ³•åæˆ–在 **<>** 中编写æŸäº›è¡¨è¾¾å¼æ¥è®¿é—®å…ƒè¡¨ã€‚ 692使用 **<>** 或被 **<>** åŒ…å›´çš„å…ƒæ–¹æ³•åæˆ–在 **<>** 中编写æŸäº›è¡¨è¾¾å¼æ¥è®¿é—®å…ƒè¡¨ã€‚
610 693
611```moonscript 694```moonscript
@@ -629,7 +712,7 @@ print tb.item
629</pre> 712</pre>
630</YueDisplay> 713</YueDisplay>
631 714
632* **元表解构** 715* **元表解构**
633使用被 **<>** 包围的元方法键解构元表。 716使用被 **<>** 包围的元方法键解构元表。
634 717
635```moonscript 718```moonscript
@@ -680,7 +763,7 @@ with? io.open "test.txt", "w"
680 763
681### ç®¡é“ 764### 管é“
682 765
683与其使用一系列嵌套的函数调用,您还å¯ä»¥è€ƒè™‘使用è¿ç®—符 **|>** æ¥ä¼ é€’值。 766与其使用一系列嵌套的函数调用,你还å¯ä»¥è€ƒè™‘使用è¿ç®—符 **|>** æ¥ä¼ é€’值。
684 767
685```moonscript 768```moonscript
686"你好" |> print 769"你好" |> print
@@ -731,67 +814,87 @@ a ??= false
731 814
732### éšå¼å¯¹è±¡ 815### éšå¼å¯¹è±¡
733 816
734您å¯ä»¥åœ¨è¡¨æ ¼å—å†…ä½¿ç”¨ç¬¦å· **\*** 开始编写一系列éšå¼ç»“构。如果您正在创建éšå¼å¯¹è±¡ï¼Œå¯¹è±¡çš„字段必须具有相åŒçš„缩进。 817ä½ å¯ä»¥åœ¨è¡¨æ ¼å—å†…ä½¿ç”¨ç¬¦å· **\*** 或是 **-** 开始编写一系列éšå¼ç»“构。如果你正在创建éšå¼å¯¹è±¡ï¼Œå¯¹è±¡çš„字段必须具有相åŒçš„缩进。
818
735```moonscript 819```moonscript
820-- 赋值时使用éšå¼å¯¹è±¡
736list = 821list =
737 * 1 822 * 1
738 * 2 823 * 2
739 * 3 824 * 3
740 825
826-- 函数调用时使用éšå¼å¯¹è±¡
741func 827func
742 * 1 828 * 1
743 * 2 829 * 2
744 * 3 830 * 3
745 831
832-- 返回时使用éšå¼å¯¹è±¡
833f = ->
834 return
835 * 1
836 * 2
837 * 3
838
839-- 表格时使用éšå¼å¯¹è±¡
746tb = 840tb =
747 name: "abc" 841 name: "abc"
748 842
749 values: 843 values:
750 * "a" 844 - "a"
751 * "b" 845 - "b"
752 * "c" 846 - "c"
753 847
754 objects: 848 objects:
755 * name: "a" 849 - name: "a"
756 value: 1 850 value: 1
757 func: => @value + 1 851 func: => @value + 1
758 tb: 852 tb:
759 fieldA: 1 853 fieldA: 1
760 854
761 * name: "b" 855 - name: "b"
762 value: 2 856 value: 2
763 func: => @value + 2 857 func: => @value + 2
764 tb: { } 858 tb: { }
765
766``` 859```
767<YueDisplay> 860<YueDisplay>
768<pre> 861<pre>
862-- 赋值时使用éšå¼å¯¹è±¡
769list = 863list =
770 * 1 864 * 1
771 * 2 865 * 2
772 * 3 866 * 3
773 867
868-- 函数调用时使用éšå¼å¯¹è±¡
774func 869func
775 * 1 870 * 1
776 * 2 871 * 2
777 * 3 872 * 3
778 873
874-- 返回时使用éšå¼å¯¹è±¡
875f = ->
876 return
877 * 1
878 * 2
879 * 3
880
881-- 表格时使用éšå¼å¯¹è±¡
779tb = 882tb =
780 name: "abc" 883 name: "abc"
781 884
782 values: 885 values:
783 * "a" 886 - "a"
784 * "b" 887 - "b"
785 * "c" 888 - "c"
786 889
787 objects: 890 objects:
788 * name: "a" 891 - name: "a"
789 value: 1 892 value: 1
790 func: => @value + 1 893 func: => @value + 1
791 tb: 894 tb:
792 fieldA: 1 895 fieldA: 1
793 896
794 * name: "b" 897 - name: "b"
795 value: 2 898 value: 2
796 func: => @value + 2 899 func: => @value + 2
797 tb: { } 900 tb: { }
@@ -859,7 +962,7 @@ do
859 962
860å¯¼å‡ºè¯­å¥æä¾›äº†ä¸€ç§ç®€æ´çš„æ–¹å¼æ¥å®šä¹‰å½“å‰çš„æ¨¡å—。 963å¯¼å‡ºè¯­å¥æä¾›äº†ä¸€ç§ç®€æ´çš„æ–¹å¼æ¥å®šä¹‰å½“å‰çš„æ¨¡å—。
861 964
862* **命å导出** 965* **命å导出**
863带命å的导出将定义一个局部å˜é‡ï¼Œå¹¶åœ¨å¯¼å‡ºçš„表中添加一个åŒå的字段。 966带命å的导出将定义一个局部å˜é‡ï¼Œå¹¶åœ¨å¯¼å‡ºçš„表中添加一个åŒå的字段。
864 967
865```moonscript 968```moonscript
@@ -923,7 +1026,7 @@ export["a-b-c"] = 123
923</pre> 1026</pre>
924</YueDisplay> 1027</YueDisplay>
925 1028
926* **未命å导出** 1029* **未命å导出**
927未命å导出会将è¦å¯¼å‡ºçš„目标项目添加到导出表的数组部分。 1030未命å导出会将è¦å¯¼å‡ºçš„目标项目添加到导出表的数组部分。
928 1031
929```moonscript 1032```moonscript
@@ -953,7 +1056,7 @@ export with tmp
953</pre> 1056</pre>
954</YueDisplay> 1057</YueDisplay>
955 1058
956* **默认导出** 1059* **默认导出**
957在导出语å¥ä¸­ä½¿ç”¨ **default** å…³é”®å­—ï¼Œæ¥æ›¿æ¢å¯¼å‡ºçš„表为一个目标的对象。 1060在导出语å¥ä¸­ä½¿ç”¨ **default** å…³é”®å­—ï¼Œæ¥æ›¿æ¢å¯¼å‡ºçš„表为一个目标的对象。
958 1061
959```moonscript 1062```moonscript
@@ -1223,7 +1326,7 @@ print first, second, color
1223</pre> 1326</pre>
1224</YueDisplay> 1327</YueDisplay>
1225 1328
1226在进行解构时,您å¯ä»¥æŒ‡å®šé»˜è®¤å€¼ï¼Œå¦‚: 1329在进行解构时,你å¯ä»¥æŒ‡å®šé»˜è®¤å€¼ï¼Œå¦‚:
1227 1330
1228```moonscript 1331```moonscript
1229{:name = "nameless", :job = "jobless"} = person 1332{:name = "nameless", :job = "jobless"} = person
@@ -1234,7 +1337,7 @@ print first, second, color
1234</pre> 1337</pre>
1235</YueDisplay> 1338</YueDisplay>
1236 1339
1237在进行列表解构时,您å¯ä»¥ä½¿ç”¨`_`作为å ä½ç¬¦ï¼š 1340在进行列表解构时,你å¯ä»¥ä½¿ç”¨`_`作为å ä½ç¬¦ï¼š
1238 1341
1239```moonscript 1342```moonscript
1240[_, two, _, four] = items 1343[_, two, _, four] = items
@@ -1245,9 +1348,55 @@ print first, second, color
1245</pre> 1348</pre>
1246</YueDisplay> 1349</YueDisplay>
1247 1350
1248### 在其它地方的解构 1351### 范围解构
1352
1353ä½ å¯ä»¥ä½¿ç”¨å±•å¼€è¿ç®—符 `...` åœ¨åˆ—è¡¨è§£æž„ä¸­æ¥æ•获一个范围的值到å­åˆ—表中。这在当你想è¦ä»Žåˆ—表的开头和结尾æå–ç‰¹å®šå…ƒç´ ï¼ŒåŒæ—¶æ”¶é›†ä¸­é—´çš„元素时éžå¸¸æœ‰ç”¨ã€‚
1354
1355```moonscript
1356orders = ["first", "second", "third", "fourth", "last"]
1357[first, ...bulk, last] = orders
1358print first -- 打å°: first
1359print bulk -- 打å°: {"second", "third", "fourth"}
1360print last -- 打å°: last
1361```
1362<YueDisplay>
1363<pre>
1364orders = ["first", "second", "third", "fourth", "last"]
1365[first, ...bulk, last] = orders
1366print first -- 打å°: first
1367print bulk -- 打å°: {"second", "third", "fourth"}
1368print last -- 打å°: last
1369</pre>
1370</YueDisplay>
1371
1372展开è¿ç®—符å¯ä»¥ç”¨åœ¨ä¸åŒçš„ä½ç½®æ¥æ•获ä¸åŒçš„范围,并且你å¯ä»¥ä½¿ç”¨ `_` 作为å ä½ç¬¦æ¥è¡¨ç¤ºä½ æƒ³è·³è¿‡å¯¹åº”范围的æ•获:
1373
1374```moonscript
1375-- æ•获第一个元素之åŽçš„æ‰€æœ‰å…ƒç´ 
1376[first, ...rest] = orders
1377
1378-- æ•获最åŽä¸€ä¸ªå…ƒç´ ä¹‹å‰çš„æ‰€æœ‰å…ƒç´ 
1379[...start, last] = orders
1380
1381-- è·³è¿‡ä¸­é—´çš„å…ƒç´ ï¼Œåªæ•获第一个和最åŽä¸€ä¸ªå…ƒç´ 
1382[first, ..._, last] = orders
1383```
1384<YueDisplay>
1385<pre>
1386-- æ•获第一个元素之åŽçš„æ‰€æœ‰å…ƒç´ 
1387[first, ...rest] = orders
1388
1389-- æ•获最åŽä¸€ä¸ªå…ƒç´ ä¹‹å‰çš„æ‰€æœ‰å…ƒç´ 
1390[...start, last] = orders
1391
1392-- è·³è¿‡ä¸­é—´çš„å…ƒç´ ï¼Œåªæ•获第一个和最åŽä¸€ä¸ªå…ƒç´ 
1393[first, ..._, last] = orders
1394</pre>
1395</YueDisplay>
1396
1397### 在其它地方的解构赋值
1249 1398
1250解构也å¯ä»¥å‡ºçŽ°åœ¨å…¶å®ƒéšå¼è¿›è¡Œèµ‹å€¼çš„åœ°æ–¹ã€‚ä¸€ä¸ªä¾‹å­æ˜¯ç”¨åœ¨for循环: 1399解构赋值也å¯ä»¥å‡ºçŽ°åœ¨å…¶å®ƒéšå¼è¿›è¡Œèµ‹å€¼çš„åœ°æ–¹ã€‚ä¸€ä¸ªä¾‹å­æ˜¯ç”¨åœ¨for循环中:
1251 1400
1252```moonscript 1401```moonscript
1253tuples = [ 1402tuples = [
@@ -1322,7 +1471,7 @@ print "好的"
1322 1471
1323### While 赋值 1472### While 赋值
1324 1473
1325您å¯ä»¥åœ¨ while å¾ªçŽ¯ä¸­åŒæ ·ä½¿ç”¨èµ‹å€¼æ¥èŽ·å–循环æ¡ä»¶çš„值。 1474ä½ å¯ä»¥åœ¨ while å¾ªçŽ¯ä¸­åŒæ ·ä½¿ç”¨èµ‹å€¼æ¥èŽ·å–循环æ¡ä»¶çš„值。
1326```moonscript 1475```moonscript
1327while byte := stream\read_one! 1476while byte := stream\read_one!
1328 -- 对 byte åšä¸€äº›æ“作 1477 -- 对 byte åšä¸€äº›æ“作
@@ -1338,7 +1487,7 @@ while byte := stream\read_one!
1338 1487
1339## å¯å˜å‚数赋值 1488## å¯å˜å‚数赋值
1340 1489
1341您å¯ä»¥å°†å‡½æ•°è¿”回的结果赋值给一个å¯å˜å‚æ•°ç¬¦å· `...`。然åŽä½¿ç”¨Lua的方å¼è®¿é—®å…¶å†…容。 1490ä½ å¯ä»¥å°†å‡½æ•°è¿”回的结果赋值给一个å¯å˜å‚æ•°ç¬¦å· `...`。然åŽä½¿ç”¨Lua的方å¼è®¿é—®å…¶å†…容。
1342```moonscript 1491```moonscript
1343list = [1, 2, 3, 4, 5] 1492list = [1, 2, 3, 4, 5]
1344fn = (ok) -> ok, table.unpack list 1493fn = (ok) -> ok, table.unpack list
@@ -1360,7 +1509,7 @@ print ok, count, first
1360 1509
1361## 空白 1510## 空白
1362 1511
1363æœˆä¹‹è„šæœ¬æ˜¯ä¸€ä¸ªå¯¹ç©ºç™½æ•æ„Ÿçš„语言。您必须在相åŒçš„缩进中使用空格 **' '** 或制表符 **'\t'** æ¥ç¼–写一些代ç å—,如函数体ã€å€¼åˆ—表和一些控制å—。包å«ä¸åŒç©ºç™½çš„表达å¼å¯èƒ½æ„味ç€ä¸åŒçš„事情。制表符被视为4个空格,但最好ä¸è¦æ··åˆä½¿ç”¨ç©ºæ ¼å’Œåˆ¶è¡¨ç¬¦ã€‚ 1512æœˆä¹‹è„šæœ¬æ˜¯ä¸€ä¸ªå¯¹ç©ºç™½æ•æ„Ÿçš„语言。你必须在相åŒçš„缩进中使用空格 **' '** 或制表符 **'\t'** æ¥ç¼–写一些代ç å—,如函数体ã€å€¼åˆ—表和一些控制å—。包å«ä¸åŒç©ºç™½çš„表达å¼å¯èƒ½æ„味ç€ä¸åŒçš„事情。制表符被视为4个空格,但最好ä¸è¦æ··åˆä½¿ç”¨ç©ºæ ¼å’Œåˆ¶è¡¨ç¬¦ã€‚
1364 1513
1365### 多行链å¼è°ƒç”¨ 1514### 多行链å¼è°ƒç”¨
1366 1515
@@ -1474,6 +1623,47 @@ catch err
1474</pre> 1623</pre>
1475</YueDisplay> 1624</YueDisplay>
1476 1625
1626### 错误处ç†ç®€åŒ–
1627
1628`try?` 是 `try` 的功能简化语法,它ä¸å†è¿”回 `try` 语å¥çš„布尔状æ€ï¼Œå¹¶åœ¨æˆåŠŸæ—¶ç›´æŽ¥è¿”å›ž `try` 代ç å—的结果,失败时返回 `nil` 值而éžé”™è¯¯å¯¹è±¡ã€‚
1629
1630```moonscript
1631a, b, c = try? func!
1632
1633-- 与空值åˆå¹¶è¿ç®—符一起使用
1634a = (try? func!) ?? "default"
1635
1636-- ä½œä¸ºå‡½æ•°å‚æ•°
1637f try? func!
1638
1639-- 带 catch å—çš„ try!
1640f try?
1641 print 123
1642 func!
1643catch e
1644 print e
1645 e
1646```
1647<YueDisplay>
1648<pre>
1649a, b, c = try? func!
1650
1651-- 与空值åˆå¹¶è¿ç®—符一起使用
1652a = (try? func!) ?? "default"
1653
1654-- ä½œä¸ºå‡½æ•°å‚æ•°
1655f try? func!
1656
1657-- 带 catch å—çš„ try!
1658f try?
1659 print 123
1660 func!
1661catch e
1662 print e
1663 e
1664</pre>
1665</YueDisplay>
1666
1477## 属性 1667## 属性
1478 1668
1479月之脚本现在æä¾›äº†Lua 5.4新增的å«åšå±žæ€§çš„语法支æŒã€‚在月之脚本编译到的Lua目标版本低于5.4时,你ä»ç„¶å¯ä»¥åŒæ—¶ä½¿ç”¨`const`å’Œ`close`çš„å±žæ€§å£°æ˜Žè¯­æ³•ï¼Œå¹¶èŽ·å¾—å¸¸é‡æ£€æŸ¥å’Œä½œç”¨åŸŸå›žè°ƒçš„功能。 1669月之脚本现在æä¾›äº†Lua 5.4新增的å«åšå±žæ€§çš„语法支æŒã€‚在月之脚本编译到的Lua目标版本低于5.4时,你ä»ç„¶å¯ä»¥åŒæ—¶ä½¿ç”¨`const`å’Œ`close`çš„å±žæ€§å£°æ˜Žè¯­æ³•ï¼Œå¹¶èŽ·å¾—å¸¸é‡æ£€æŸ¥å’Œä½œç”¨åŸŸå›žè°ƒçš„功能。
@@ -1502,6 +1692,19 @@ const {:a, :b, c, d} = tb
1502</pre> 1692</pre>
1503</YueDisplay> 1693</YueDisplay>
1504 1694
1695你也å¯ä»¥å£°æ˜Žå…¨å±€å˜é‡ä¸ºå¸¸é‡ã€‚
1696
1697```moonscript
1698global const Constant = 123
1699-- Constant = 1
1700```
1701<YueDisplay>
1702<pre>
1703global const Constant = 123
1704-- Constant = 1
1705</pre>
1706</YueDisplay>
1707
1505## å­—é¢é‡ 1708## å­—é¢é‡
1506 1709
1507Lua中的所有基本字é¢é‡éƒ½å¯ä»¥åœ¨æœˆä¹‹è„šæœ¬ä¸­ä½¿ç”¨ã€‚åŒ…æ‹¬æ•°å­—ã€å­—符串ã€å¸ƒå°”值和**nil**。 1710Lua中的所有基本字é¢é‡éƒ½å¯ä»¥åœ¨æœˆä¹‹è„šæœ¬ä¸­ä½¿ç”¨ã€‚åŒ…æ‹¬æ•°å­—ã€å­—符串ã€å¸ƒå°”值和**nil**。
@@ -1529,17 +1732,78 @@ print "我有#{math.random! * 100}%的把æ¡ã€‚"
1529 1732
1530### æ•°å­—å­—é¢é‡ 1733### æ•°å­—å­—é¢é‡
1531 1734
1532您å¯ä»¥åœ¨æ•°å­—å­—é¢é‡ä¸­ä½¿ç”¨ä¸‹åˆ’线æ¥å¢žåŠ å¯è¯»æ€§ã€‚ 1735ä½ å¯ä»¥åœ¨æ•°å­—å­—é¢é‡ä¸­ä½¿ç”¨ä¸‹åˆ’线æ¥å¢žåŠ å¯è¯»æ€§ã€‚
1533 1736
1534```moonscript 1737```moonscript
1535integer = 1_000_000 1738integer = 1_000_000
1536hex = 0xEF_BB_BF 1739hex = 0xEF_BB_BF
1740binary = 0B10011
1537``` 1741```
1538<YueDisplay> 1742<YueDisplay>
1539 1743
1540<pre> 1744<pre>
1541integer = 1_000_000 1745integer = 1_000_000
1542hex = 0xEF_BB_BF 1746hex = 0xEF_BB_BF
1747binary = 0B10011
1748</pre>
1749</YueDisplay>
1750
1751### YAML 风格字符串
1752
1753使用 `|` å‰ç¼€æ ‡è®°ä¸€ä¸ªå¤šè¡Œ YAML 风格字符串:
1754
1755```moonscript
1756str = |
1757 key: value
1758 list:
1759 - item1
1760 - #{expr}
1761```
1762<YueDisplay>
1763<pre>
1764str = |
1765 key: value
1766 list:
1767 - item1
1768 - #{expr}
1769</pre>
1770</YueDisplay>
1771
1772其效果类似于原生 Lua çš„å¤šè¡Œæ‹¼æŽ¥ï¼Œæ‰€æœ‰æ–‡æœ¬ï¼ˆå«æ¢è¡Œï¼‰å°†è¢«ä¿ç•™ä¸‹æ¥ï¼Œå¹¶æ”¯æŒ `#{...}` 语法,通过 `tostring(expr)` æ’入表达å¼ç»“果。
1773
1774YAML é£Žæ ¼çš„å¤šè¡Œå­—ç¬¦ä¸²ä¼šè‡ªåŠ¨æ£€æµ‹é¦–è¡ŒåŽæœ€å°çš„公共缩进,并从所有行中删除该å‰ç¼€ç©ºç™½å­—符。这让你å¯ä»¥åœ¨ä»£ç ä¸­å¯¹é½æ–‡æœ¬ï¼Œä½†è¾“出字符串ä¸ä¼šå¸¦å¤šä½™ç¼©è¿›ã€‚
1775
1776```moonscript
1777fn = ->
1778 str = |
1779 foo:
1780 bar: baz
1781 return str
1782```
1783<YueDisplay>
1784<pre>
1785fn = ->
1786 str = |
1787 foo:
1788 bar: baz
1789 return str
1790</pre>
1791</YueDisplay>
1792
1793输出字符串中的 foo: 对é½åˆ°è¡Œé¦–,ä¸ä¼šå¸¦æœ‰å‡½æ•°ç¼©è¿›ç©ºæ ¼ã€‚ä¿ç•™å†…部缩进的相对结构,适åˆä¹¦å†™ç»“构化嵌套样å¼çš„内容。
1794
1795支æŒè‡ªåЍ处ç†å­—符中的引å·ã€åæ–œæ ç­‰ç‰¹æ®Šç¬¦å·ï¼Œæ— éœ€æ‰‹åŠ¨è½¬ä¹‰ï¼š
1796
1797```moonscript
1798str = |
1799 path: "C:\Program Files\App"
1800 note: 'He said: "#{Hello}!"'
1801```
1802<YueDisplay>
1803<pre>
1804str = |
1805 path: "C:\Program Files\App"
1806 note: 'He said: "#{Hello}!"'
1543</pre> 1807</pre>
1544</YueDisplay> 1808</YueDisplay>
1545 1809
@@ -1644,7 +1908,7 @@ print "数字的和是", sum 10, 20
1644</pre> 1908</pre>
1645</YueDisplay> 1909</YueDisplay>
1646 1910
1647如果您需è¦åšæ˜¾å¼è¿”回,å¯ä»¥ä½¿ç”¨return关键字: 1911如果你需è¦åšæ˜¾å¼è¿”回,å¯ä»¥ä½¿ç”¨return关键字:
1648 1912
1649```moonscript 1913```moonscript
1650sum = (x, y) -> return x + y 1914sum = (x, y) -> return x + y
@@ -1850,7 +2114,7 @@ print @value
1850</pre> 2114</pre>
1851</YueDisplay> 2115</YueDisplay>
1852 2116
1853您å¯ä»¥é€šè¿‡ä¸€ä¸ªå ä½ç¬¦æŒ‡å®šå›žè°ƒå‡½æ•°çš„ä¼ å‚ä½ç½®ã€‚ 2117ä½ å¯ä»¥é€šè¿‡ä¸€ä¸ªå ä½ç¬¦æŒ‡å®šå›žè°ƒå‡½æ•°çš„ä¼ å‚ä½ç½®ã€‚
1854 2118
1855```moonscript 2119```moonscript
1856(x) <- map _, [1, 2, 3] 2120(x) <- map _, [1, 2, 3]
@@ -1863,22 +2127,22 @@ x * 2
1863</pre> 2127</pre>
1864</YueDisplay> 2128</YueDisplay>
1865 2129
1866如果您希望在åå‘回调处ç†åŽç»§ç»­ç¼–写更多其它的代ç ï¼Œæ‚¨å¯ä»¥ä½¿ç”¨do语å¥å°†ä¸å½’属åå‘回调的代ç åˆ†å¼€ã€‚ 2130如果你希望在åå‘回调处ç†åŽç»§ç»­ç¼–写更多其它的代ç ï¼Œå¯ä»¥ä½¿ç”¨ do 语å¥å°†ä¸å±žäºŽåå‘回调的代ç åˆ†éš”开。对于éžç²—箭头函数的åå‘回调,回调返回值的括å·ä¹Ÿæ˜¯å¯ä»¥çœç•¥çš„。
1867 2131
1868```moonscript 2132```moonscript
1869result, msg = do 2133result, msg = do
1870 (data) <- readAsync "文件å.txt" 2134 data <- readAsync "文件å.txt"
1871 print data 2135 print data
1872 (info) <- processAsync data 2136 info <- processAsync data
1873 check info 2137 check info
1874print result, msg 2138print result, msg
1875``` 2139```
1876<YueDisplay> 2140<YueDisplay>
1877<pre> 2141<pre>
1878result, msg = do 2142result, msg = do
1879 (data) <- readAsync "文件å.txt" 2143 data <- readAsync "文件å.txt"
1880 print data 2144 print data
1881 (info) <- processAsync data 2145 info <- processAsync data
1882 check info 2146 check info
1883print result, msg 2147print result, msg
1884</pre> 2148</pre>
@@ -2037,7 +2301,7 @@ list_with_one_element = [ 1, ]
2037 2301
2038## æŽ¨å¯¼å¼ 2302## 推导å¼
2039 2303
2040推导å¼ä¸ºæˆ‘们æä¾›äº†ä¸€ç§ä¾¿æ·çš„语法,通过éåŽ†çŽ°æœ‰å¯¹è±¡å¹¶å¯¹å…¶å€¼åº”ç”¨è¡¨è¾¾å¼æ¥æž„é€ å‡ºæ–°çš„è¡¨æ ¼ã€‚æœˆä¹‹è„šæœ¬æœ‰ä¸¤ç§æŽ¨å¯¼å¼ï¼šåˆ—表推导å¼å’Œè¡¨æ ¼æŽ¨å¯¼å¼ã€‚它们最终都是产生Lua表格;列表推导å¼å°†å€¼ç´¯ç§¯åˆ°ç±»ä¼¼æ•°ç»„的表格中,而表格推导å¼å…è®¸æ‚¨åœ¨æ¯æ¬¡é历时设置新表格的键和值。 2304推导å¼ä¸ºæˆ‘们æä¾›äº†ä¸€ç§ä¾¿æ·çš„语法,通过éåŽ†çŽ°æœ‰å¯¹è±¡å¹¶å¯¹å…¶å€¼åº”ç”¨è¡¨è¾¾å¼æ¥æž„é€ å‡ºæ–°çš„è¡¨æ ¼ã€‚æœˆä¹‹è„šæœ¬æœ‰ä¸¤ç§æŽ¨å¯¼å¼ï¼šåˆ—表推导å¼å’Œè¡¨æ ¼æŽ¨å¯¼å¼ã€‚它们最终都是产生Lua表格;列表推导å¼å°†å€¼ç´¯ç§¯åˆ°ç±»ä¼¼æ•°ç»„的表格中,而表格推导å¼å…è®¸ä½ åœ¨æ¯æ¬¡é历时设置新表格的键和值。
2041 2305
2042### åˆ—è¡¨æŽ¨å¯¼å¼ 2306### 列表推导å¼
2043 2307
@@ -2057,13 +2321,11 @@ doubled = [item * 2 for i, item in ipairs items]
2057å¯ä»¥ä½¿ç”¨whenå­å¥ç­›é€‰æ–°è¡¨ä¸­åŒ…å«çš„项目: 2321å¯ä»¥ä½¿ç”¨whenå­å¥ç­›é€‰æ–°è¡¨ä¸­åŒ…å«çš„项目:
2058 2322
2059```moonscript 2323```moonscript
2060iter = ipairs items 2324slice = [item for i, item in ipairs items when i > 1 and i < 3]
2061slice = [item for i, item in iter when i > 1 and i < 3]
2062``` 2325```
2063<YueDisplay> 2326<YueDisplay>
2064<pre> 2327<pre>
2065iter = ipairs items 2328slice = [item for i, item in ipairs items when i > 1 and i < 3]
2066slice = [item for i, item in iter when i > 1 and i < 3]
2067</pre> 2329</pre>
2068</YueDisplay> 2330</YueDisplay>
2069 2331
@@ -2212,6 +2474,45 @@ slice = [item for item in *items[,,2]]
2212</pre> 2474</pre>
2213</YueDisplay> 2475</YueDisplay>
2214 2476
2477最å°å’Œæœ€å¤§è¾¹ç•Œéƒ½å¯ä»¥æ˜¯è´Ÿæ•°ï¼Œä½¿ç”¨è´Ÿæ•°æ„味ç€è¾¹ç•Œæ˜¯ä»Žè¡¨çš„æœ«å°¾å¼€å§‹è®¡ç®—的。
2478
2479```moonscript
2480-- å–æœ€åŽ4个元素
2481slice = [item for item in *items[-4,-1]]
2482```
2483<YueDisplay>
2484<pre>
2485-- å–æœ€åŽ4个元素
2486slice = [item for item in *items[-4,-1]]
2487</pre>
2488</YueDisplay>
2489
2490切片的步长也å¯ä»¥æ˜¯è´Ÿæ•°ï¼Œè¿™æ„味ç€å…ƒç´ ä¼šä»¥ç›¸å的顺åºè¢«å–出。
2491
2492```moonscript
2493reverse_slice = [item for item in *items[-1,1,-1]]
2494```
2495<YueDisplay>
2496<pre>
2497reverse_slice = [item for item in *items[-1,1,-1]]
2498</pre>
2499</YueDisplay>
2500
2501#### 切片表达å¼
2502
2503切片也å¯ä»¥ä½œä¸ºè¡¨è¾¾å¼æ¥ä½¿ç”¨ã€‚å¯ä»¥ç”¨äºŽèŽ·å–一个表包å«çš„å­åˆ—表。
2504
2505```moonscript
2506-- å–第2和第4个元素作为新的列表
2507sub_list = items[2, 4]
2508```
2509<YueDisplay>
2510<pre>
2511-- å–第2和第4个元素作为新的列表
2512sub_list = items[2, 4]
2513</pre>
2514</YueDisplay>
2515
2215## for 循环 2516## for 循环
2216 2517
2217Lua中有两ç§for循环形å¼ï¼Œæ•°å­—型和通用型: 2518Lua中有两ç§for循环形å¼ï¼Œæ•°å­—型和通用型:
@@ -2288,9 +2589,24 @@ doubled_evens = for i = 1, 20
2288</pre> 2589</pre>
2289</YueDisplay> 2590</YueDisplay>
2290 2591
2291您还å¯ä»¥ç»“åˆfor循环表达å¼ä¸Žcontinueè¯­å¥æ¥è¿‡æ»¤å€¼ã€‚ 2592此外,for循环还支æŒå¸¦è¿”回值的break语å¥ï¼Œè¿™æ ·å¾ªçŽ¯æœ¬èº«å°±å¯ä»¥ä½œä¸ºä¸€ä¸ªè¡¨è¾¾å¼ï¼Œåœ¨æ»¡è¶³æ¡ä»¶æ—¶æå‰é€€å‡ºå¹¶è¿”回有æ„义的结果。
2292 2593
2293注æ„出现在函数体末尾的for循环,ä¸ä¼šè¢«å½“作是一个表达å¼ï¼Œå¹¶å°†å¾ªçŽ¯ç»“æžœç´¯ç§¯åˆ°ä¸€ä¸ªåˆ—è¡¨ä¸­ä½œä¸ºè¿”å›žå€¼ï¼ˆç›¸å,函数将返回nil)。如果è¦å‡½æ•°æœ«å°¾çš„循环转æ¢ä¸ºåˆ—表表达å¼ï¼Œå¯ä»¥ä½¿ç”¨è¿”回语å¥åŠ for循环表达å¼ã€‚ 2594例如,查找第一个大于10的数字:
2595
2596```moonscript
2597first_large = for n in *numbers
2598 break n if n > 10
2599```
2600<YueDisplay>
2601<pre>
2602first_large = for n in *numbers
2603 break n if n > 10
2604</pre>
2605</YueDisplay>
2606
2607你还å¯ä»¥ç»“åˆfor循环表达å¼ä¸Žcontinueè¯­å¥æ¥è¿‡æ»¤å€¼ã€‚
2608
2609注æ„出现在函数体末尾的for循环,ä¸ä¼šè¢«å½“作是一个表达å¼å¹¶å°†å¾ªçŽ¯ç»“æžœç´¯ç§¯åˆ°ä¸€ä¸ªåˆ—è¡¨ä¸­ä½œä¸ºè¿”å›žå€¼ï¼ˆç›¸å,函数将返回nil)。如果è¦å‡½æ•°æœ«å°¾çš„循环转æ¢ä¸ºåˆ—表表达å¼ï¼Œå¯ä»¥æ˜¾å¼åœ°ä½¿ç”¨è¿”回语å¥åŠ for循环表达å¼ã€‚
2294 2610
2295```moonscript 2611```moonscript
2296func_a = -> for i = 1, 10 do print i 2612func_a = -> for i = 1, 10 do print i
@@ -2515,7 +2831,7 @@ print "你真幸è¿ï¼" unless math.random! > 0.1
2515 2831
2516### èŒƒå›´è¡¨è¾¾å¼ 2832### 范围表达å¼
2517 2833
2518您å¯ä»¥ä½¿ç”¨èŒƒå›´è¡¨è¾¾å¼æ¥ç¼–写进行范围检查的代ç ã€‚ 2834ä½ å¯ä»¥ä½¿ç”¨èŒƒå›´è¡¨è¾¾å¼æ¥ç¼–写进行范围检查的代ç ã€‚
2519 2835
2520```moonscript 2836```moonscript
2521a = 5 2837a = 5
@@ -2588,28 +2904,26 @@ reader\parse_line! until reader\eof!
2588 2904
2589## switch è¯­å¥ 2905## switch 语å¥
2590 2906
2591switchè¯­å¥æ˜¯ä¸ºäº†ç®€åŒ–检查一系列相åŒå€¼çš„if语å¥è€Œæä¾›çš„ç®€å†™è¯­æ³•ã€‚è¦æ³¨æ„用于比较检查的目标值åªä¼šè®¡ç®—一次。和if语å¥ä¸€æ ·ï¼Œswitch语å¥åœ¨æœ€åŽå¯ä»¥æŽ¥ä¸€ä¸ªelse代ç å—æ¥å¤„ç†æ²¡æœ‰åŒ¹é…的情况。在生æˆçš„Lua代ç ä¸­ï¼Œè¿›è¡Œæ¯”较是使用==æ“作符完æˆçš„。 2907switchè¯­å¥æ˜¯ä¸ºäº†ç®€åŒ–检查一系列相åŒå€¼çš„if语å¥è€Œæä¾›çš„ç®€å†™è¯­æ³•ã€‚è¦æ³¨æ„用于比较检查的目标值åªä¼šè®¡ç®—一次。和if语å¥ä¸€æ ·ï¼Œswitch语å¥åœ¨æœ€åŽå¯ä»¥æŽ¥ä¸€ä¸ªelse代ç å—æ¥å¤„ç†æ²¡æœ‰åŒ¹é…的情况。在生æˆçš„Lua代ç ä¸­ï¼Œè¿›è¡Œæ¯”较是使用==æ“作符完æˆçš„。switch语å¥ä¸­ä¹Ÿå¯ä»¥ä½¿ç”¨èµ‹å€¼è¡¨è¾¾å¼æ¥å‚¨å­˜ä¸´æ—¶å˜é‡å€¼ã€‚
2592 2908
2593```moonscript 2909```moonscript
2594name = "Dan" 2910switch name := "Dan"
2595switch name
2596 when "Robert" 2911 when "Robert"
2597 print "你是Robert" 2912 print "你是Robert"
2598 when "Dan", "Daniel" 2913 when "Dan", "Daniel"
2599 print "ä½ çš„å字是Dan" 2914 print "ä½ çš„å字是Dan"
2600 else 2915 else
2601 print "我ä¸çŸ¥é“ä½ çš„åå­—" 2916 print "我ä¸è®¤è¯†ä½ ï¼Œä½ çš„å字是#{name}"
2602``` 2917```
2603<YueDisplay> 2918<YueDisplay>
2604<pre> 2919<pre>
2605name = "Dan" 2920switch name := "Dan"
2606switch name
2607 when "Robert" 2921 when "Robert"
2608 print "你是Robert" 2922 print "你是Robert"
2609 when "Dan", "Daniel" 2923 when "Dan", "Daniel"
2610 print "ä½ çš„å字是Dan" 2924 print "ä½ çš„å字是Dan"
2611 else 2925 else
2612 print "我ä¸çŸ¥é“ä½ çš„åå­—" 2926 print "我ä¸è®¤è¯†ä½ ï¼Œä½ çš„å字是#{name}"
2613</pre> 2927</pre>
2614</YueDisplay> 2928</YueDisplay>
2615 2929
@@ -2686,7 +3000,7 @@ else
2686</pre> 3000</pre>
2687</YueDisplay> 3001</YueDisplay>
2688 3002
2689值得注æ„的是,在生æˆLuaä»£ç æ—¶ï¼Œæˆ‘们è¦åšæ£€æŸ¥çš„ç›®æ ‡å˜é‡ä¼šæ”¾åœ¨==表达å¼çš„å³ä¾§ã€‚当您希望给whenå­å¥çš„æ¯”较对象定义一个\_\_eq元方法æ¥é‡è½½åˆ¤æ–­é€»è¾‘时,å¯èƒ½ä¼šæœ‰ç”¨ã€‚ 3003值得注æ„的是,在生æˆLuaä»£ç æ—¶ï¼Œæˆ‘们è¦åšæ£€æŸ¥çš„ç›®æ ‡å˜é‡ä¼šæ”¾åœ¨==表达å¼çš„å³ä¾§ã€‚当你希望给whenå­å¥çš„æ¯”较对象定义一个\_\_eq元方法æ¥é‡è½½åˆ¤æ–­é€»è¾‘时,å¯èƒ½ä¼šæœ‰ç”¨ã€‚
2690 3004
2691### è¡¨æ ¼åŒ¹é… 3005### 表格匹é…
2692 3006
@@ -2746,9 +3060,126 @@ switch item
2746</pre> 3060</pre>
2747</YueDisplay> 3061</YueDisplay>
2748 3062
3063你也å¯ä»¥åŒ¹é…数组元素ã€è¡¨æ ¼å­—段,甚至使用数组或表格字é¢é‡æ¥åŒ¹é…嵌套的结构。
3064
3065åŒ¹é…æ•°ç»„元素。
3066
3067```moonscript
3068switch tb
3069 when [1, 2, 3]
3070 print "1, 2, 3"
3071 when [1, b, 3]
3072 print "1, #{b}, 3"
3073 when [1, 2, b = 3] -- å˜é‡b有默认值
3074 print "1, 2, #{b}"
3075```
3076<YueDisplay>
3077<pre>
3078switch tb
3079 when [1, 2, 3]
3080 print "1, 2, 3"
3081 when [1, b, 3]
3082 print "1, #{b}, 3"
3083 when [1, 2, b = 3] -- å˜é‡b有默认值
3084 print "1, 2, #{b}"
3085</pre>
3086</YueDisplay>
3087
3088匹é…表格字段。
3089
3090```moonscript
3091switch tb
3092 when success: true, :result
3093 print "æˆåŠŸ", result
3094 when success: false
3095 print "失败", result
3096 else
3097 print "无效值"
3098```
3099<YueDisplay>
3100<pre>
3101switch tb
3102 when success: true, :result
3103 print "æˆåŠŸ", result
3104 when success: false
3105 print "失败", result
3106 else
3107 print "无效值"
3108</pre>
3109</YueDisplay>
3110
3111匹é…嵌套的表格结构。
3112
3113```moonscript
3114switch tb
3115 when data: {type: "success", :content}
3116 print "æˆåŠŸ", content
3117 when data: {type: "error", :content}
3118 print "失败", content
3119 else
3120 print "无效值"
3121```
3122<YueDisplay>
3123<pre>
3124switch tb
3125 when data: {type: "success", :content}
3126 print "æˆåŠŸ", content
3127 when data: {type: "error", :content}
3128 print "失败", content
3129 else
3130 print "无效值"
3131</pre>
3132</YueDisplay>
3133
3134匹é…表格数组。
3135
3136```moonscript
3137switch tb
3138 when [
3139 {a: 1, b: 2}
3140 {a: 3, b: 4}
3141 {a: 5, b: 6}
3142 fourth
3143 ]
3144 print "åŒ¹é…æˆåŠŸ", fourth
3145```
3146<YueDisplay>
3147<pre>
3148switch tb
3149 when [
3150 {a: 1, b: 2}
3151 {a: 3, b: 4}
3152 {a: 5, b: 6}
3153 fourth
3154 ]
3155 print "åŒ¹é…æˆåŠŸ", fourth
3156</pre>
3157</YueDisplay>
3158
3159匹é…一个列表并æ•获特定范围内的元素。
3160
3161```moonscript
3162segments = ["admin", "users", "logs", "view"]
3163switch segments
3164 when [...groups, resource, action]
3165 print "Group:", groups -- 打å°: {"admin", "users"}
3166 print "Resource:", resource -- 打å°: "logs"
3167 print "Action:", action -- 打å°: "view"
3168```
3169<YueDisplay>
3170<pre>
3171segments = ["admin", "users", "logs", "view"]
3172switch segments
3173 when [...groups, resource, action]
3174 print "Group:", groups -- 打å°: {"admin", "users"}
3175 print "Resource:", resource -- 打å°: "logs"
3176 print "Action:", action -- 打å°: "view"
3177</pre>
3178</YueDisplay>
3179
2749## é¢å‘对象编程 3180## é¢å‘对象编程
2750 3181
2751在以下的示例中,月之脚本生æˆçš„Lua代ç å¯èƒ½çœ‹èµ·æ¥ä¼šå¾ˆå¤æ‚。所以最好主è¦å…³æ³¨æœˆä¹‹è„šæœ¬ä»£ç å±‚é¢çš„æ„ä¹‰ï¼Œç„¶åŽå¦‚果您想知é“关于é¢å‘å¯¹è±¡åŠŸèƒ½çš„å®žçŽ°ç»†èŠ‚ï¼Œå†æŸ¥çœ‹Lua代ç ã€‚ 3182在以下的示例中,月之脚本生æˆçš„Lua代ç å¯èƒ½çœ‹èµ·æ¥ä¼šå¾ˆå¤æ‚。所以最好主è¦å…³æ³¨æœˆä¹‹è„šæœ¬ä»£ç å±‚é¢çš„æ„ä¹‰ï¼Œç„¶åŽå¦‚果你想知é“关于é¢å‘å¯¹è±¡åŠŸèƒ½çš„å®žçŽ°ç»†èŠ‚ï¼Œå†æŸ¥çœ‹Lua代ç ã€‚
2752 3183
2753一个简å•的类: 3184一个简å•的类:
2754 3185
@@ -3216,7 +3647,7 @@ x = class
3216 3647
3217### ç±»æ··åˆ 3648### 类混åˆ
3218 3649
3219您å¯ä»¥é€šè¿‡ä½¿ç”¨ `using` 关键字æ¥å®žçŽ°ç±»æ··åˆã€‚è¿™æ„å‘³ç€æ‚¨å¯ä»¥ä»Žä¸€ä¸ªæ™®é€š Lua 表格或已定义的类对象中,å¤åˆ¶å‡½æ•°åˆ°æ‚¨åˆ›å»ºçš„æ–°ç±»ä¸­ã€‚当您使用普通 Lua è¡¨æ ¼è¿›è¡Œç±»æ··åˆæ—¶ï¼Œæ‚¨æœ‰æœºä¼šç”¨è‡ªå·±çš„实现æ¥é‡å†™ç±»çš„索引方法(例如元方法 `__index`ï¼‰ã€‚ç„¶è€Œï¼Œå½“æ‚¨ä»Žä¸€ä¸ªç±»å¯¹è±¡åšæ··åˆæ—¶ï¼Œéœ€è¦æ³¨æ„的是该类对象的元方法将ä¸ä¼šè¢«å¤åˆ¶åˆ°æ–°ç±»ã€‚ 3650ä½ å¯ä»¥é€šè¿‡ä½¿ç”¨ `using` 关键字æ¥å®žçŽ°ç±»æ··åˆã€‚è¿™æ„味ç€ä½ å¯ä»¥ä»Žä¸€ä¸ªæ™®é€š Lua 表格或已定义的类对象中,å¤åˆ¶å‡½æ•°åˆ°ä½ åˆ›å»ºçš„æ–°ç±»ä¸­ã€‚当你使用普通 Lua è¡¨æ ¼è¿›è¡Œç±»æ··åˆæ—¶ï¼Œä½ æœ‰æœºä¼šç”¨è‡ªå·±çš„实现æ¥é‡å†™ç±»çš„索引方法(例如元方法 `__index`ï¼‰ã€‚ç„¶è€Œï¼Œå½“ä½ ä»Žä¸€ä¸ªç±»å¯¹è±¡åšæ··åˆæ—¶ï¼Œéœ€è¦æ³¨æ„的是该类对象的元方法将ä¸ä¼šè¢«å¤åˆ¶åˆ°æ–°ç±»ã€‚
3220 3651
3221```moonscript 3652```moonscript
3222MyIndex = __index: var: 1 3653MyIndex = __index: var: 1
@@ -3318,22 +3749,22 @@ me = create_person "Leaf", [dad, mother, sister]
3318 3749
3319在此用法中,withå¯ä»¥è¢«è§†ä¸ºK组åˆå­ï¼ˆk-combinator)的一ç§ç‰¹æ®Šå½¢å¼ã€‚ 3750在此用法中,withå¯ä»¥è¢«è§†ä¸ºK组åˆå­ï¼ˆk-combinator)的一ç§ç‰¹æ®Šå½¢å¼ã€‚
3320 3751
3321如果您想给表达å¼å¦å¤–起一个åç§°çš„è¯ï¼Œwith语å¥ä¸­çš„表达å¼ä¹Ÿå¯ä»¥æ˜¯ä¸€ä¸ªèµ‹å€¼è¯­å¥ã€‚ 3752如果你想给表达å¼å¦å¤–起一个åç§°çš„è¯ï¼Œwith语å¥ä¸­çš„表达å¼ä¹Ÿå¯ä»¥æ˜¯ä¸€ä¸ªèµ‹å€¼è¯­å¥ã€‚
3322 3753
3323```moonscript 3754```moonscript
3324with str = "你好" 3755with str := "你好"
3325 print "原始:", str 3756 print "原始:", str
3326 print "大写:", \upper! 3757 print "大写:", \upper!
3327``` 3758```
3328<YueDisplay> 3759<YueDisplay>
3329<pre> 3760<pre>
3330with str = "你好" 3761with str := "你好"
3331 print "原始:", str 3762 print "原始:", str
3332 print "大写:", \upper! 3763 print "大写:", \upper!
3333</pre> 3764</pre>
3334</YueDisplay> 3765</YueDisplay>
3335 3766
3336在with语å¥ä¸­å¯ä»¥ä½¿ç”¨`[]`访问特殊键。 3767ä½ å¯ä»¥åœ¨ `with` 语å¥ä¸­ä½¿ç”¨ `[]` 访问特殊键。
3337 3768
3338```moonscript 3769```moonscript
3339with tb 3770with tb
@@ -3356,6 +3787,19 @@ with tb
3356</pre> 3787</pre>
3357</YueDisplay> 3788</YueDisplay>
3358 3789
3790`with?` 是 `with` è¯­æ³•çš„ä¸€ä¸ªå¢žå¼ºç‰ˆæœ¬ï¼Œå¼•å…¥äº†å­˜åœ¨æ€§æ£€æŸ¥ï¼Œç”¨äºŽåœ¨ä¸æ˜¾å¼åˆ¤ç©ºçš„æƒ…况下安全访问å¯èƒ½ä¸º nil 的对象。
3791
3792```moonscript
3793with? obj
3794 print obj.name
3795```
3796<YueDisplay>
3797<pre>
3798with? obj
3799 print obj.name
3800</pre>
3801</YueDisplay>
3802
3359## do è¯­å¥ 3803## do 语å¥
3360 3804
3361å½“ç”¨ä½œè¯­å¥æ—¶ï¼Œdo语å¥çš„作用就åƒåœ¨Lua中差ä¸å¤šã€‚ 3805å½“ç”¨ä½œè¯­å¥æ—¶ï¼Œdo语å¥çš„作用就åƒåœ¨Lua中差ä¸å¤šã€‚
@@ -3375,7 +3819,7 @@ print var -- 这里是nil
3375</pre> 3819</pre>
3376</YueDisplay> 3820</YueDisplay>
3377 3821
3378月之脚本的 **do** 也å¯ä»¥ç”¨ä½œè¡¨è¾¾å¼ã€‚å…许您将多行代ç çš„处ç†åˆå¹¶ä¸ºä¸€ä¸ªè¡¨è¾¾å¼ï¼Œå¹¶å°†do语å¥ä»£ç å—的最åŽä¸€ä¸ªè¯­å¥ä½œä¸ºè¡¨è¾¾å¼è¿”回的结果。 3822月之脚本的 **do** 也å¯ä»¥ç”¨ä½œè¡¨è¾¾å¼ã€‚å…许你将多行代ç çš„处ç†åˆå¹¶ä¸ºä¸€ä¸ªè¡¨è¾¾å¼ï¼Œå¹¶å°†do语å¥ä»£ç å—的最åŽä¸€ä¸ªè¯­å¥ä½œä¸ºè¡¨è¾¾å¼è¿”回的结果。
3379 3823
3380```moonscript 3824```moonscript
3381counter = do 3825counter = do
@@ -4334,8 +4778,8 @@ simplified: boolean
4334 4778
4335ç‰ˆæƒ (c) 2017-2025 æŽç‘¾ \<dragon-fly@qq.com\> 4779ç‰ˆæƒ (c) 2017-2025 æŽç‘¾ \<dragon-fly@qq.com\>
4336 4780
4337特此å…费授予任何获得本软件副本和相关文档文件(下称“软件â€ï¼‰çš„人ä¸å—é™åˆ¶åœ°å¤„置该软件的æƒåˆ©ï¼ŒåŒ…括ä¸å—é™åˆ¶åœ°ä½¿ç”¨ã€å¤åˆ¶ã€ä¿®æ”¹ã€åˆå¹¶ã€å‘布ã€åˆ†å‘ã€è½¬æŽˆè®¸å¯å’Œ/或出售该软件副本,以åŠå†æŽˆæƒè¢«é…å‘了本软件的人如上的æƒåˆ©ï¼Œé¡»åœ¨ä¸‹åˆ—æ¡ä»¶ä¸‹ï¼š 4781特此å…费授予任何获得本软件副本和相关文档文件(下称“软件â€ï¼‰çš„人ä¸å—é™åˆ¶åœ°å¤„置该软件的æƒåˆ©ï¼ŒåŒ…括ä¸å—é™åˆ¶åœ°ä½¿ç”¨ã€å¤åˆ¶ã€ä¿®æ”¹ã€åˆå¹¶ã€å‘布ã€åˆ†å‘ã€è½¬æŽˆè®¸å¯å’Œ/或出售该软件副本,以åŠå†æŽˆæƒè¢«é…å‘了本软件的人如上的æƒåˆ©ï¼Œé¡»åœ¨ä¸‹åˆ—æ¡ä»¶ä¸‹ï¼š
4338上述版æƒå£°æ˜Žå’Œæœ¬è®¸å¯å£°æ˜Žåº”包å«åœ¨è¯¥è½¯ä»¶çš„æ‰€æœ‰å‰¯æœ¬æˆ–实质æˆåˆ†ä¸­ã€‚ 4782上述版æƒå£°æ˜Žå’Œæœ¬è®¸å¯å£°æ˜Žåº”包å«åœ¨è¯¥è½¯ä»¶çš„æ‰€æœ‰å‰¯æœ¬æˆ–实质æˆåˆ†ä¸­ã€‚
4339æœ¬è½¯ä»¶æ˜¯â€œå¦‚æ­¤â€æä¾›çš„ï¼Œæ²¡æœ‰ä»»ä½•å½¢å¼çš„æ˜Žç¤ºæˆ–暗示的ä¿è¯ï¼ŒåŒ…括但ä¸é™äºŽå¯¹é€‚销性ã€ç‰¹å®šç”¨é€”的适用性和ä¸ä¾µæƒçš„ä¿è¯ã€‚åœ¨ä»»ä½•æƒ…å†µä¸‹ï¼Œä½œè€…æˆ–ç‰ˆæƒæŒæœ‰äººéƒ½ä¸å¯¹ä»»ä½•ç´¢èµ”ã€æŸå®³æˆ–其他责任负责,无论这些追责æ¥è‡ªåˆåŒã€ä¾µæƒæˆ–å…¶å®ƒè¡Œä¸ºä¸­ï¼Œè¿˜æ˜¯äº§ç”ŸäºŽã€æºäºŽæˆ–æœ‰å…³äºŽæœ¬è½¯ä»¶ä»¥åŠæœ¬è½¯ä»¶çš„使用或其它处置。 4783æœ¬è½¯ä»¶æ˜¯â€œå¦‚æ­¤â€æä¾›çš„ï¼Œæ²¡æœ‰ä»»ä½•å½¢å¼çš„æ˜Žç¤ºæˆ–暗示的ä¿è¯ï¼ŒåŒ…括但ä¸é™äºŽå¯¹é€‚销性ã€ç‰¹å®šç”¨é€”的适用性和ä¸ä¾µæƒçš„ä¿è¯ã€‚åœ¨ä»»ä½•æƒ…å†µä¸‹ï¼Œä½œè€…æˆ–ç‰ˆæƒæŒæœ‰äººéƒ½ä¸å¯¹ä»»ä½•ç´¢èµ”ã€æŸå®³æˆ–其他责任负责,无论这些追责æ¥è‡ªåˆåŒã€ä¾µæƒæˆ–å…¶å®ƒè¡Œä¸ºä¸­ï¼Œè¿˜æ˜¯äº§ç”ŸäºŽã€æºäºŽæˆ–æœ‰å…³äºŽæœ¬è½¯ä»¶ä»¥åŠæœ¬è½¯ä»¶çš„使用或其它处置。
4340 4784
4341<CompilerModal /> 4785<CompilerModal />
diff --git a/spec/inputs/backcall.yue b/spec/inputs/backcall.yue
index 8aadc71..e6b8c21 100644
--- a/spec/inputs/backcall.yue
+++ b/spec/inputs/backcall.yue
@@ -13,9 +13,9 @@ do
13 x > 2 13 x > 2
14 14
15do 15do
16 (data) <- http?.get "ajaxtest" 16 data <- http?.get "ajaxtest"
17 body[".result"]\html data 17 body[".result"]\html data
18 (processed) <- http.post "ajaxprocess", data 18 processed <- http.post "ajaxprocess", data
19 body[".result"]\append processed 19 body[".result"]\append processed
20 <- setTimeout 1000 20 <- setTimeout 1000
21 print "done" 21 print "done"
diff --git a/spec/inputs/destructure.yue b/spec/inputs/destructure.yue
index 674dfe4..b6250d0 100644
--- a/spec/inputs/destructure.yue
+++ b/spec/inputs/destructure.yue
@@ -94,7 +94,7 @@ do
94-- 94--
95 95
96do 96do
97 with {a,b} = thing 97 with {a,b} := thing
98 print a, b 98 print a, b
99 99
100 100
@@ -240,5 +240,43 @@ do
240 switch tb 240 switch tb
241 when {c: {<"abc">: meta_field = "def"}, <[[any string]]>: {d: abc = 123}, <'str'>: {e: def = {}}} 241 when {c: {<"abc">: meta_field = "def"}, <[[any string]]>: {d: abc = 123}, <'str'>: {e: def = {}}}
242 print meta_field, abc, def 242 print meta_field, abc, def
243
244do
245 clients = ["VIP_Alice", "User_Bob", "User_Clara", "VIP_Eva"]
246 [vipStart, ...regulars, vipEnd] = clients
247 print vipStart -- "VIP_Alice"
248 print regulars -- {"User_Bob", "User_Clara"}
249 print vipEnd -- "VIP_Eva"
250
251do
252 setupMeeting = (participants) ->
253 [chair, ..._, secretary] = participants
254 print chair, secretary
255
256 setupMeeting ["Alice", "Bob", "Charlie", "David"]
257 -- Output: Alice David
258
259do
260 getTransactions = ->
261 {
262 {id: "T1", amount: 100}
263 {id: "T2", amount: 200}
264 {id: "T3", amount: 300}
265 }
266
267 :id, :amount = getTransactions![#]
268 assert id == "T3"
269 assert amount == 300
270
271do
272 [
273 _
274 ...middle
275 _
276 ] = tb
277
278do
279 {a, :abc, b, :def, ...sub, d, e} = tb
280
243nil 281nil
244 282
diff --git a/spec/inputs/global.yue b/spec/inputs/global.yue
index ce1cc15..4e3b8aa 100644
--- a/spec/inputs/global.yue
+++ b/spec/inputs/global.yue
@@ -82,3 +82,9 @@ do
82 FooBar = "pascal case" 82 FooBar = "pascal case"
83 FOOBAR = "all uppercase" 83 FOOBAR = "all uppercase"
84 84
85do
86 global const class A
87 global const Flag = 1
88 global const const, x, y = "const", 1, 2
89 global const math, table
90
diff --git a/spec/inputs/import.yue b/spec/inputs/import.yue
index b8ffc24..8982e6c 100644
--- a/spec/inputs/import.yue
+++ b/spec/inputs/import.yue
@@ -139,3 +139,13 @@ do
139 import "m" as {c: d} 139 import "m" as {c: d}
140 import "m" as {g, {<close>: i}} 140 import "m" as {g, {<close>: i}}
141 141
142do
143 import require
144 import string as stringlib
145 import string.format
146 import io.read as io_read
147
148 type = ->
149 import type as tp
150 import 月 as yue
151
diff --git a/spec/inputs/lists.yue b/spec/inputs/lists.yue
index 921cae0..c493b68 100644
--- a/spec/inputs/lists.yue
+++ b/spec/inputs/lists.yue
@@ -87,4 +87,51 @@ do
87 [a, b] = hello 87 [a, b] = hello
88 [name = "nameless", job = "jobless"] = person 88 [name = "nameless", job = "jobless"] = person
89 89
90do
91 transactions = ["T001", "T002", "T003", "T004", "T005"]
92 middleTransactions = transactions[2, -2]
93 print middleTransactions -- => {"T002", "T003", "T004"}
94
95do
96 logs =
97 - start: 0, end: 100
98 - start: 100, end: 200
99 - start: 200, end: 123
100 print logs[#].end -- => 123
101
102do
103 pendingOrders = ["O001", "O002", "O003", "O004"]
104 print pendingOrders[# - 1] -- => "O003"
105
106do
107 getOrders = ->
108 {
109 { id: "O1001", status: "pending" }
110 { id: "O1002", status: "processing" }
111 { id: "O1003", status: "done" }
112 }
113
114 lastStatus = getOrders()[#].status
115 assert lastStatus == "done"
116
117do
118 cloneList1 = (list) -> list[,]
119 cloneList2 = (list) -> [...list,]
120 cloneTable = (tb) -> {...tb}
121
122do
123 print(
124 globalTB[#]
125 a.b.c[# - 2]
126 x?\y?!.z?[# - 3]
127 )
128
129do
130 f = ->
131 print(
132 globalTB[#]\end 123
133 a.b.c[5,-5][# - 2]
134 x?\y?!.z?[# - 3]?[, -3]
135 )
136
90nil 137nil
diff --git a/spec/inputs/literals.yue b/spec/inputs/literals.yue
index 6b666f0..32cf7e7 100644
--- a/spec/inputs/literals.yue
+++ b/spec/inputs/literals.yue
@@ -10,10 +10,19 @@ _ = {
10 0xfF2323 10 0xfF2323
11 0xabcdef 11 0xabcdef
12 0xABCDEF 12 0xABCDEF
13 0XFB_C4_00
13 0x123p-123 14 0x123p-123
14 0xABCP+321 15 0xABCP+321
15 0x.1p-111 16 0x.1p-111
16 0xA_B_CP-3_2_1 17 0xA_B_CP-3_2_1
18 0x0.1E
19 0xA23p-4
20 0X1.921FB54442D18P+1
21
22 0b01
23 0b00_00_10_00
24 0B1111
25 0B00_11_00_10_01
17 26
18 .2323 27 .2323
19 .2323e-1 28 .2323e-1
diff --git a/spec/inputs/loops.yue b/spec/inputs/loops.yue
index c5b28b3..5df10ca 100644
--- a/spec/inputs/loops.yue
+++ b/spec/inputs/loops.yue
@@ -213,3 +213,55 @@ do
213do 213do
214 until x := func 'a', b do 214 until x := func 'a', b do
215 print "false expected" 215 print "false expected"
216
217do
218 index = for i = 1, #tb
219 break i if tb[i]
220
221 f for i = 1, #tb
222 break i if tb[i]
223
224 f for i = 1, #tb
225 i if tb[i]
226
227 i = 1
228 ids = while tb[i]
229 i += 1
230 i - 1
231
232 i = 1
233 idx = while tb[i]
234 i += 1
235 break i - 1
236
237 f1 = ->
238 i = 1
239 f while tb[i]
240 i += 1
241 i - 1
242
243 i = 1
244 f while tb[i]
245 i += 1
246 break i - 1
247
248 list = for item in *items
249 switch item
250 when type: "A", :value
251 if value > 5
252 item
253
254do
255 repeat print 1 until true
256
257 x = repeat
258 a = func!
259 break a.x
260 until a.v
261
262 items = repeat
263 item = getItem!
264 break unless item
265 item if item.value > 0
266 until false
267
diff --git a/spec/inputs/macro.yue b/spec/inputs/macro.yue
index 5d5f1a9..191f09f 100644
--- a/spec/inputs/macro.yue
+++ b/spec/inputs/macro.yue
@@ -60,6 +60,11 @@ macro NumAndStr = (num, str) ->
60 60
61print $NumAndStr 123, 'xyz' 61print $NumAndStr 123, 'xyz'
62 62
63macro NumAndStr2 = (num`Num, str`SingleString) -> |
64 [#{num}, #{str}]
65
66print $NumAndStr2 456, 'abc'
67
63$asserts item == nil 68$asserts item == nil
64 69
65$myconfig false 70$myconfig false
@@ -100,13 +105,14 @@ macro filter = (items, action)->
100 $showMacro "filter", "[_ for _ in *#{items} when #{action}]" 105 $showMacro "filter", "[_ for _ in *#{items} when #{action}]"
101 106
102macro reduce = (items, def, action)-> 107macro reduce = (items, def, action)->
103 $showMacro "reduce", "if ##{items} == 0 108 $showMacro "reduce", |
104 #{def} 109 if ##{items} == 0
105else 110 #{def}
106 _1 = #{def} 111 else
107 for _2 in *#{items} 112 _1 = #{def}
108 _1 = #{action} 113 for _2 in *#{items}
109 _1" 114 _1 = #{action}
115 _1
110 116
111macro foreach = (items, action)-> 117macro foreach = (items, action)->
112 $showMacro "foreach", "for _ in *#{items} 118 $showMacro "foreach", "for _ in *#{items}
@@ -154,13 +160,15 @@ macro curry = (...)->
154f = $curry x,y,z,do 160f = $curry x,y,z,do
155 print x,y,z 161 print x,y,z
156 162
157macro get_inner = (var)-> "do 163macro get_inner = (var)-> |
158 a = 1 164 do
159 a + 1" 165 a = 1
166 a + 1
160 167
161macro get_inner_hygienic = (var)-> "(-> 168macro get_inner_hygienic = (var)-> |
162 local a = 1 169 (->
163 a + 1)!" 170 local a = 1
171 a + 1)!
164 172
165do 173do
166 a = 8 174 a = 8
@@ -196,6 +204,18 @@ end
196 204
197print x 205print x
198 206
207import "yue"
208macro lua = (code`YAMLMultiline) -> {
209 code: yue.loadstring(code)!
210 type: "lua"
211}
212
213$lua |
214 local function f2(a)
215 return a + 1
216 end
217 x = x + f2(3)
218
199macro def = (fname, ...)-> 219macro def = (fname, ...)->
200 args = {...} 220 args = {...}
201 last = table.remove args 221 last = table.remove args
@@ -317,7 +337,13 @@ $chainC(
317 Destroy! 337 Destroy!
318) 338)
319 339
320macro tb = -> "{'abc', a:123, <call>:=> 998}" 340macro tb = -> |
341 {
342 'abc'
343 a: 123
344 <call>: => 998
345 }
346
321print $tb[1], $tb.a, ($tb)!, $tb! 347print $tb[1], $tb.a, ($tb)!, $tb!
322 348
323print "current line: #{ $LINE }" 349print "current line: #{ $LINE }"
diff --git a/spec/inputs/macro_export.yue b/spec/inputs/macro_export.yue
index cc7d459..22905b5 100644
--- a/spec/inputs/macro_export.yue
+++ b/spec/inputs/macro_export.yue
@@ -8,13 +8,12 @@ export macro config = (debugging = true)->
8 "" 8 ""
9 9
10export macro showMacro = (name, res)-> 10export macro showMacro = (name, res)->
11 if debugMacro then " 11 if debugMacro then |
12do 12 do
13 txt = #{res} 13 txt = #{res}
14 print '[macro ' .. #{name} .. ']' 14 print '[macro #{name}]'
15 print txt 15 print txt
16 txt 16 txt
17"
18 else 17 else
19 res 18 res
20 19
@@ -35,14 +34,16 @@ export macro copy = (src, dst, ...)->
35 src != "_src_" and src != "_dst_" and dst != "_src_" and dst != "_dst_" 34 src != "_src_" and src != "_dst_" and dst != "_src_" and dst != "_dst_"
36 "copy targets can not be _src_ or _dst_" 35 "copy targets can not be _src_ or _dst_"
37 ) 36 )
38 " 37 copyFields = table.concat(
39do 38 ["_dst_.#{field} = _src_.#{field}" for field in *{...}]
40 local _src_, _dst_ 39 "\n\t\t\t"
41 with _dst_ = #{dst} 40 )
42 with _src_ = #{src} 41 |
43#{table.concat for field in *{...} do " 42 do
44 _dst_.#{field} = _src_.#{field} 43 local _src_, _dst_
45"}" 44 with _dst_ := #{dst}
45 with _src_ := #{src}
46 #{copyFields}
46 47
47export macro enum = (...) -> 48export macro enum = (...) ->
48 items = {...} 49 items = {...}
diff --git a/spec/inputs/macro_teal.yue b/spec/inputs/macro_teal.yue
index 0cfd862..e51bcd7 100644
--- a/spec/inputs/macro_teal.yue
+++ b/spec/inputs/macro_teal.yue
@@ -4,11 +4,16 @@ $ ->
4 options.target_extension = "tl" 4 options.target_extension = "tl"
5 package.path ..= ";./spec/lib/?.lua" 5 package.path ..= ";./spec/lib/?.lua"
6 6
7macro to_lua = (code)-> 7macro to_lua = (code)-> |
8 "require('yue').to_lua(#{code}, reserve_line_number:false, same_module:true)" 8 require('yue').to_lua #{code},
9 reserve_line_number: false
10 same_module: true
9 11
10macro trim = (name)-> 12macro trim = (name)-> |
11 "if result := #{name}\\match '[\\'\"](.*)[\\'\"]' then result else #{name}" 13 if result := #{name}\match '[\'"](.*)[\'"]'
14 result
15 else
16 #{name}
12 17
13export macro local = (decl, value = nil)-> 18export macro local = (decl, value = nil)->
14 import "yue" as {options:{:tl_enabled}} 19 import "yue" as {options:{:tl_enabled}}
diff --git a/spec/inputs/macro_todo.yue b/spec/inputs/macro_todo.yue
index 752c9cb..c9c8f77 100644
--- a/spec/inputs/macro_todo.yue
+++ b/spec/inputs/macro_todo.yue
@@ -5,9 +5,6 @@ export macro todoInner = (module, line, msg)->
5 type: "lua" 5 type: "lua"
6 } 6 }
7 7
8export macro todo = (msg)-> 8export macro todo = (msg)-> |
9 if msg 9 $todoInner $FILE, $LINE#{msg and ", #{msg}" or ""}
10 "$todoInner $FILE, $LINE, #{msg}"
11 else
12 "$todoInner $FILE, $LINE"
13 10
diff --git a/spec/inputs/props.yue b/spec/inputs/props.yue
new file mode 100644
index 0000000..bbb7aae
--- /dev/null
+++ b/spec/inputs/props.yue
@@ -0,0 +1,61 @@
1class Props
2 __index: (name): nil =>
3 cls = @.<>
4 if item := cls.__getter?[name] -- access properties
5 return item @
6 elseif item := rawget cls, name -- access member functions
7 return item
8 else
9 c = cls
10 while c := c.<> -- recursive to access base classes
11 if item := c.__getter?[name]
12 cls.__getter ??= {}
13 cls.__getter[name] = item -- cache base properties to class
14 return item @
15 elseif item := rawget c, name
16 rawset cls, name, item -- cache base member to class
17 return item
18
19 __newindex: (name, value) =>
20 cls = @.<>
21 if item := cls.__setter?[name] -- access properties
22 item @, value
23 else
24 c = cls
25 while c := c.<> -- recursive to access base classes
26 if item := c.__setter?[name]
27 cls.__setter ??= {}
28 cls.__setter[name] = item -- cache base property to class
29 item @, value
30 return
31 rawset @, name, value -- assign field to self
32
33 assignReadOnly = -> error "assigning a readonly property"
34
35 prop: (name, props) =>
36 {
37 :get
38 :set = assignReadOnly
39 } = props
40 if getter := rawget @__base, "__getter"
41 getter[name] = get
42 else
43 rawset @__base, "__getter", [name]: get
44 if setter := rawget @__base, "__setter"
45 setter[name] = set
46 else
47 rawset @__base, "__setter", [name]: set
48
49class A extends Props
50 @prop 'x'
51 get: => @_x + 1000
52 set: (v) => @_x = v
53 new: =>
54 @_x = 0
55
56class B extends A
57 @prop 'abc', get: => "hello"
58
59b = B!
60b.x = 999
61print b.x, b.abc
diff --git a/spec/inputs/string.yue b/spec/inputs/string.yue
index f91383e..1f0fba8 100644
--- a/spec/inputs/string.yue
+++ b/spec/inputs/string.yue
@@ -74,3 +74,76 @@ _ = "hello"
74something"hello"\world! 74something"hello"\world!
75something "hello"\world! 75something "hello"\world!
76 76
77do
78 str = |
79 key: value
80 str = |
81 config:
82 enabled: true
83 level: 5
84 str = |
85 header: start
86
87 footer: end
88 str = |
89 name: #{username}
90 str = |
91 count: #{total} items
92 str = |
93 user: #{name}
94 id: #{id}
95 str = |
96 path: "C:\\Program Files\\App"
97 desc: 'single "quote" test'
98 str = |
99 key: value
100 next: 123
101 str = |
102 list:
103 - "one"
104 - "two"
105 str = |
106 -- comment
107 content text
108 -- comment
109 str = |
110 #{1 + 2}
111 #{2 + 3}
112 #{"a" .. "b"}
113 obj =
114 settings: |
115 mode: #{mode}
116 flags:
117 - #{flag1}
118 - default
119 fn = -> |
120 Hello
121 name: #{userName}
122 str = |
123 result:
124 status: #{if ok then "pass" else "fail"}
125 code: #{code}
126 summary = |
127 date: #{os.date()}
128 values:
129 -
130 a: #{aVal}
131 b: #{bVal or defaultB}
132 msg = send |
133 Hello, #{user}!
134 Today is #{os.date("%A")}.
135 desc = do
136 prefix = "Result"
137 |
138 #{prefix}:
139 value: #{compute!}
140 (|
141 1
142 2
143 3
144 ) |> print
145
146export yaml = |
147 version: #{ver}
148 ok: true
149
diff --git a/spec/inputs/switch.yue b/spec/inputs/switch.yue
index 49d47f3..2b0669c 100644
--- a/spec/inputs/switch.yue
+++ b/spec/inputs/switch.yue
@@ -165,5 +165,128 @@ do
165 print item 165 print item
166 when [a = 1, b = "abc"] 166 when [a = 1, b = "abc"]
167 print a, b 167 print a, b
168nil
169 168
169do
170 switch tb
171 when [1, 2, 3]
172 print "1, 2, 3"
173 when [1, b, 3]
174 print "1, #{b}, 3"
175 when [1, 2, b = 3]
176 print "1, 2, #{b}"
177
178do
179 switch tb
180 when success: true, :result
181 print "success", result
182 when success: false
183 print "failed", result
184 else
185 print "invalid"
186
187do
188 switch tb
189 when {type: "success", :content}
190 print "success", content
191 when {type: "error", :content}
192 print "failed", content
193 else
194 print "invalid"
195
196do
197 switch tb
198 when [
199 {a: 1, b: 2}
200 {a: 3, b: 4}
201 {a: 5, b: 6}
202 fourth
203 ]
204 print "matched", fourth
205
206 switch tb
207 when [
208 {c: 1, d: 2}
209 {c: 3, d: 4}
210 {c: 5, d: 6}
211 ]
212 print "OK"
213 when [
214 _
215 _
216 {a: 1, b: 2}
217 {a: 3, b: 4}
218 {a: 5, b: 6}
219 sixth
220 ]
221 print "matched", sixth
222
223do
224 switch v := "hello"
225 when "hello"
226 print "matched hello"
227 else
228 print "not matched"
229 -- output: matched hello
230
231do
232 f = -> "ok"
233 switch val := f!
234 when "ok"
235 print "it's ok"
236 -- output: it's ok
237
238
239do
240 g = -> 42
241 switch result := g!
242 when 1, 2
243 print "small"
244 when 42
245 print "life universe everything"
246 else
247 print "other #{result}"
248 -- output: life universe everything
249
250do
251 check = ->
252 if true
253 "yes"
254 else
255 "no"
256
257 switch x := check!
258 when "yes"
259 print "affirmative"
260 else
261 print "negative"
262 -- output: affirmative
263
264do
265 t = (): tb ->
266 tb = {a: 1}
267 tb.a = 2
268
269 switch data := t!
270 when {a: 2}
271 print "matched"
272 else
273 print "not matched"
274
275do
276 clientData = ["Meta", "CUST_1001", "CHK123"]
277 switch clientData
278 when [...metadata, customerId, checksum]
279 print metadata -- {"Meta"}
280 print customerId -- "CUST_1001"
281 print checksum -- "CHK123"
282
283do
284 handlePath = (segments) ->
285 switch segments
286 when [..._, resource, action]
287 print "Resource:", resource
288 print "Action:", action
289
290 handlePath ["admin", "logs", "view"]
291
292nil
diff --git a/spec/inputs/tables.yue b/spec/inputs/tables.yue
index 0de8a8c..702e04a 100644
--- a/spec/inputs/tables.yue
+++ b/spec/inputs/tables.yue
@@ -245,6 +245,24 @@ menus =
245 click: -> 245 click: ->
246 } 246 }
247 247
248_ =
249 boolean:
250 - true
251 - false
252 float:
253 - 3.14
254 - -6.8523015e+5
255 int:
256 - 123
257 - -0b1010_0111_0100_1010_1110
258 null:
259 nodeName: 'node'
260 parent: nil
261 string:
262 - 'Hello world'
263 - "newline
264newline2"
265
248tb = {...other} 266tb = {...other}
249 267
250tbMix = { 268tbMix = {
diff --git a/spec/inputs/try_catch.yue b/spec/inputs/try_catch.yue
index 4e05bc6..6c29a52 100644
--- a/spec/inputs/try_catch.yue
+++ b/spec/inputs/try_catch.yue
@@ -62,6 +62,126 @@ f = ->
62 do 62 do
63 <- x 63 <- x
64 local tb, a, b, c 64 local tb, a, b, c
65 f = -> try tb.f a, b, c 65 f1 = -> try tb.f a, b, c
66
67 do
68 f1 = -> do
69 ok, ... = try func!
70 ...
71
72 do
73 local func
74 a, b, c = try? func!
75
76 do
77 a, b, c = try? func!
78
79 do
80 a = (try? func!) ?? "default"
81
82 do
83 f try? func!
84
85 do
86 f try?
87 print 123
88 func!
89 catch e
90 print e
91 e
66 92
67 nil 93 nil
94
95do
96 try
97 func 1, 2, 3
98 catch err
99 print err
100
101 try func 1, 2, 3
102 catch err
103 print err
104
105 try
106 print "trying"
107 func 1, 2, 3
108
109 do
110 success, result = try
111 func 1, 2, 3
112 catch err
113 print err
114
115 success, result = try func 1, 2, 3
116
117 tb = {}
118
119 try tb.func
120 try tb.func!
121 try tb.func()
122 try (tb.func!)
123 try (tb\func(1, 2, 3))
124
125 try tb.func 1
126 try tb.func(1)
127
128 if (try func 1
129 catch err
130 print err)
131 print "OK"
132
133 if try (func 1)
134 catch err
135 print err
136 print "OK"
137
138 do
139 if success, result := try func "abc", 123
140 print result
141
142 success, result = try func "abc", 123
143 catch err
144 print err
145
146 print result if success, result := try func "abc", 123
147 catch err
148 print err
149
150 do
151 try
152 func 1, 2, 3
153
154 try func 1, 2, 3
155
156 do
157 <- x
158 local tb, a, b, c
159 f1 = -> try tb.f a, b, c
160
161 do
162 f1 = -> do
163 ok, ... = try func!
164 ...
165
166 do
167 local func
168 a, b, c = try? func!
169
170 do
171 a, b, c = try? func!
172
173 do
174 a = (try? func!) ?? "default"
175
176 do
177 f try? func!
178
179 do
180 f try?
181 print 123
182 func!
183 catch e
184 print e
185 e
186
187nil
diff --git a/spec/inputs/unicode/destructure.yue b/spec/inputs/unicode/destructure.yue
index 3c3a369..a5ffd5d 100644
--- a/spec/inputs/unicode/destructure.yue
+++ b/spec/inputs/unicode/destructure.yue
@@ -84,7 +84,7 @@ do
84-- 84--
85 85
86do 86do
87 with {元素a,元素b} = 东西 87 with {元素a,元素b} := 东西
88 æ‰“å° å…ƒç´ a, 元素b 88 æ‰“å° å…ƒç´ a, 元素b
89 89
90 90
diff --git a/spec/inputs/unicode/macro_export.yue b/spec/inputs/unicode/macro_export.yue
index 3c9a942..56571cd 100644
--- a/spec/inputs/unicode/macro_export.yue
+++ b/spec/inputs/unicode/macro_export.yue
@@ -37,8 +37,8 @@ export macro å¤åˆ¶ = (æº, 目标, ...)->
37 " 37 "
38do 38do
39 local _æº_, _目标_ 39 local _æº_, _目标_
40 with _目标_ = #{目标} 40 with _目标_ := #{目标}
41 with _æº_ = #{æº} 41 with _æº_ := #{æº}
42#{table.concat for 字段 in *{...} do " 42#{table.concat for 字段 in *{...} do "
43 _目标_.#{字段} = _æº_.#{字段} 43 _目标_.#{字段} = _æº_.#{字段}
44"}" 44"}"
diff --git a/spec/inputs/unicode/with.yue b/spec/inputs/unicode/with.yue
index ecbfdab..3c15add 100644
--- a/spec/inputs/unicode/with.yue
+++ b/spec/inputs/unicode/with.yue
@@ -45,19 +45,19 @@ do
45 with å˜é‡a 45 with å˜é‡a
46 æ‰“å° .世界 46 æ‰“å° .世界
47 47
48 æ¨¡å— = with _æ¨¡å— = {} 48 æ¨¡å— = with _æ¨¡å— := {}
49 .事物 = "你好" 49 .事物 = "你好"
50 50
51 with å˜é‡a, å˜é‡b = 东西, 布 51 with å˜é‡a, å˜é‡b := 东西, 布
52 æ‰“å° .世界 52 æ‰“å° .世界
53 53
54 å˜é‡x = with å˜é‡a, å˜é‡b = 1, 2 54 å˜é‡x = with å˜é‡a, å˜é‡b := 1, 2
55 æ‰“å° å˜é‡a + å˜é‡b 55 æ‰“å° å˜é‡a + å˜é‡b
56 56
57 æ‰“å° with å˜é‡a, å˜é‡b = 1, 2 57 æ‰“å° with å˜é‡a, å˜é‡b := 1, 2
58 æ‰“å° å˜é‡a + å˜é‡b 58 æ‰“å° å˜é‡a + å˜é‡b
59 59
60 p = with 你好!.字段x, 世界!.字段y = 1, 2 60 p = with 你好!.字段x, 世界!.字段y := 1, 2
61 æ‰“å° å˜é‡a + å˜é‡b 61 æ‰“å° å˜é‡a + å˜é‡b
62 62
63-- 63--
@@ -68,16 +68,16 @@ do
68 å˜é‡x\大写! 68 å˜é‡x\大写!
69 69
70do 70do
71 with å˜é‡k = "ä¹”" 71 with å˜é‡k := "ä¹”"
72 æ‰“å° \大写! 72 æ‰“å° \大写!
73 73
74do 74do
75 with å˜é‡a,å˜é‡b,å˜é‡c = "", "", "" 75 with å˜é‡a,å˜é‡b,å˜é‡c := "", "", ""
76 æ‰“å° \大写! 76 æ‰“å° \大写!
77 77
78do 78do
79 å˜é‡a = "床铺" 79 å˜é‡a = "床铺"
80 with å˜é‡a,å˜é‡b,å˜é‡c = "", "", "" 80 with å˜é‡a,å˜é‡b,å˜é‡c := "", "", ""
81 æ‰“å° \大写! 81 æ‰“å° \大写!
82 82
83do 83do
@@ -85,7 +85,7 @@ do
85 æ‰“å° \大写! 85 æ‰“å° \大写!
86 86
87do 87do
88 with å˜é‡k.å˜é‡j = "ä¹”" 88 with å˜é‡k.å˜é‡j := "ä¹”"
89 æ‰“å° \大写! 89 æ‰“å° \大写!
90 90
91do 91do
@@ -96,7 +96,7 @@ do
96 96
97do 97do
98 with å˜é‡a 98 with å˜é‡a
99 with .b = 2 99 with .b := 2
100 æ‰“å° .c 100 æ‰“å° .c
101 101
102do 102do
@@ -131,12 +131,12 @@ do
131 131
132do 132do
133 global æŽ©ç  133 global 掩ç 
134 with? æŽ©ç  = 实心矩形 宽: w, 高: h, 颜色: 0x66000000 134 with? æŽ©ç  := 实心矩形 宽: w, 高: h, 颜色: 0x66000000
135 .触摸å¯ç”¨ = true 135 .触摸å¯ç”¨ = true
136 .åžå™¬è§¦æ‘¸ = true 136 .åžå™¬è§¦æ‘¸ = true
137 137
138do 138do
139 with? æŽ©ç  = 实心矩形 宽: w, 高: h, 颜色: 0x66000000 139 with? æŽ©ç  := 实心矩形 宽: w, 高: h, 颜色: 0x66000000
140 .触摸å¯ç”¨ = true 140 .触摸å¯ç”¨ = true
141 .åžå™¬è§¦æ‘¸ = true 141 .åžå™¬è§¦æ‘¸ = true
142 142
diff --git a/spec/inputs/with.yue b/spec/inputs/with.yue
index 19b7be1..2256833 100644
--- a/spec/inputs/with.yue
+++ b/spec/inputs/with.yue
@@ -48,21 +48,21 @@ do
48 with a -- only one value allowed 48 with a -- only one value allowed
49 print .world 49 print .world
50 50
51 mod = with _M = {} 51 mod = with _M := {}
52 .Thing = "hi" 52 .Thing = "hi"
53 53
54 -- operate on a only 54 -- operate on a only
55 with a, b = something, pooh 55 with a, b := something, pooh
56 print .world 56 print .world
57 57
58 x = with a, b = 1, 2 58 x = with a, b := 1, 2
59 print a + b 59 print a + b
60 60
61 print with a, b = 1, 2 61 print with a, b := 1, 2
62 print a + b 62 print a + b
63 63
64 -- assignment lhs must be evaluated in the order they appear 64 -- assignment lhs must be evaluated in the order they appear
65 p = with hello!.x, world!.y = 1, 2 65 p = with hello!.x, world!.y := 1, 2
66 print a + b 66 print a + b
67 67
68-- 68--
@@ -73,16 +73,16 @@ do
73 x\upper! 73 x\upper!
74 74
75do 75do
76 with k = "jo" 76 with k := "jo"
77 print \upper! 77 print \upper!
78 78
79do 79do
80 with a,b,c = "", "", "" 80 with a,b,c := "", "", ""
81 print \upper! 81 print \upper!
82 82
83do 83do
84 a = "bunk" 84 a = "bunk"
85 with a,b,c = "", "", "" 85 with a,b,c := "", "", ""
86 print \upper! 86 print \upper!
87 87
88do 88do
@@ -90,7 +90,7 @@ do
90 print \upper! 90 print \upper!
91 91
92do 92do
93 with k.j = "jo" 93 with k.j := "jo"
94 print \upper! 94 print \upper!
95 95
96do 96do
@@ -103,7 +103,7 @@ do
103do 103do
104 with a 104 with a
105 -- nested `with`s with assignments should change the scope correctly 105 -- nested `with`s with assignments should change the scope correctly
106 with .b = 2 106 with .b := 2
107 print .c 107 print .c
108 108
109do 109do
@@ -138,12 +138,12 @@ do
138 138
139do 139do
140 global mask 140 global mask
141 with? mask = SolidRect width: w, height: h, color: 0x66000000 141 with? mask := SolidRect width: w, height: h, color: 0x66000000
142 .touchEnabled = true 142 .touchEnabled = true
143 .swallowTouches = true 143 .swallowTouches = true
144 144
145do 145do
146 with? mask = SolidRect width: w, height: h, color: 0x66000000 146 with? mask := SolidRect width: w, height: h, color: 0x66000000
147 .touchEnabled = true 147 .touchEnabled = true
148 .swallowTouches = true 148 .swallowTouches = true
149 149
@@ -152,4 +152,22 @@ do
152 return with {} 152 return with {}
153 return [123] 153 return [123]
154 154
155do
156 f with item
157 if .id > 0
158 break .content
159
160 a = with tb
161 if .v
162 break .a
163
164 a = while true
165 break with? tb
166 break 1
167
168do
169 a = for i = 1, 100
170 with? x := tb[i]
171 break x if .id := 1
172
155nil 173nil
diff --git a/spec/outputs/5.1/attrib.lua b/spec/outputs/5.1/attrib.lua
index a156e84..bda24bc 100644
--- a/spec/outputs/5.1/attrib.lua
+++ b/spec/outputs/5.1/attrib.lua
@@ -136,9 +136,11 @@ do
136 end 136 end
137 local b 137 local b
138 if not false then 138 if not false then
139 if x then 139 b = ((function()
140 b = 1 140 if x then
141 end 141 return 1
142 end
143 end)())
142 end 144 end
143 local _close_0 145 local _close_0
144 if (function() 146 if (function()
@@ -164,10 +166,12 @@ do
164 end)(pcall(function() 166 end)(pcall(function()
165 local c 167 local c
166 if true then 168 if true then
167 local _exp_0 = x 169 c = ((function()
168 if "abc" == _exp_0 then 170 local _exp_0 = x
169 c = 998 171 if "abc" == _exp_0 then
170 end 172 return 998
173 end
174 end)())
171 end 175 end
172 local d 176 local d
173 if (function() 177 if (function()
diff --git a/spec/outputs/5.1/loops.lua b/spec/outputs/5.1/loops.lua
index 57b19be..e4f2871 100644
--- a/spec/outputs/5.1/loops.lua
+++ b/spec/outputs/5.1/loops.lua
@@ -60,8 +60,8 @@ do
60 local y = hello[_index_0] 60 local y = hello[_index_0]
61 if y % 2 == 0 then 61 if y % 2 == 0 then
62 _accum_0[_len_0] = y 62 _accum_0[_len_0] = y
63 _len_0 = _len_0 + 1
63 end 64 end
64 _len_0 = _len_0 + 1
65 end 65 end
66 x = _accum_0 66 x = _accum_0
67end 67end
@@ -132,13 +132,11 @@ do
132end 132end
133do 133do
134 local _accum_0 = { } 134 local _accum_0 = { }
135 local _len_0 = 1
136 local _list_2 = 3 135 local _list_2 = 3
137 for _index_0 = 1, #_list_2 do 136 for _index_0 = 1, #_list_2 do
138 local thing = _list_2[_index_0] 137 local thing = _list_2[_index_0]
139 y = "hello" 138 y = "hello"
140 break 139 break
141 _len_0 = _len_0 + 1
142 end 140 end
143 x = _accum_0 141 x = _accum_0
144end 142end
@@ -489,3 +487,131 @@ do
489 end 487 end
490 until false 488 until false
491end 489end
490local _anon_func_0 = function(i, tb)
491 local _accum_0 = { }
492 local _len_0 = 1
493 while tb[i] do
494 i = i + 1
495 _accum_0[_len_0] = i - 1
496 _len_0 = _len_0 + 1
497 end
498 return _accum_0
499end
500do
501 local index
502 do
503 local _accum_0
504 for i = 1, #tb do
505 if tb[i] then
506 _accum_0 = i
507 break
508 end
509 end
510 index = _accum_0
511 end
512 f((function()
513 local _accum_0
514 for i = 1, #tb do
515 if tb[i] then
516 _accum_0 = i
517 break
518 end
519 end
520 return _accum_0
521 end)())
522 f((function()
523 local _accum_0 = { }
524 local _len_0 = 1
525 for i = 1, #tb do
526 if tb[i] then
527 _accum_0[_len_0] = i
528 _len_0 = _len_0 + 1
529 end
530 end
531 return _accum_0
532 end)())
533 i = 1
534 local ids
535 do
536 local _accum_0 = { }
537 local _len_0 = 1
538 while tb[i] do
539 i = i + 1
540 _accum_0[_len_0] = i - 1
541 _len_0 = _len_0 + 1
542 end
543 ids = _accum_0
544 end
545 i = 1
546 local idx
547 do
548 local _accum_0
549 while tb[i] do
550 i = i + 1
551 _accum_0 = i - 1
552 break
553 end
554 idx = _accum_0
555 end
556 local f1
557 f1 = function()
558 i = 1
559 return f(_anon_func_0(i, tb))
560 end
561 i = 1
562 f((function()
563 local _accum_0
564 while tb[i] do
565 i = i + 1
566 _accum_0 = i - 1
567 break
568 end
569 return _accum_0
570 end)())
571 local _accum_0 = { }
572 local _len_0 = 1
573 local _list_3 = items
574 for _index_0 = 1, #_list_3 do
575 local item = _list_3[_index_0]
576 local _type_0 = type(item)
577 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
578 if _tab_0 then
579 local value = item.value
580 if "A" == item.type and value ~= nil then
581 if value > 5 then
582 _accum_0[_len_0] = item
583 _len_0 = _len_0 + 1
584 end
585 end
586 end
587 end
588 list = _accum_0
589end
590do
591 repeat
592 print(1)
593 until true
594 do
595 local _accum_0
596 repeat
597 a = func()
598 _accum_0 = a.x
599 break
600 until a.v
601 x = _accum_0
602 end
603 local items
604 local _accum_0 = { }
605 local _len_0 = 1
606 repeat
607 local item = getItem()
608 if not item then
609 break
610 end
611 if item.value > 0 then
612 _accum_0[_len_0] = item
613 _len_0 = _len_0 + 1
614 end
615 until false
616 items = _accum_0
617end
diff --git a/spec/outputs/5.1/try_catch.lua b/spec/outputs/5.1/try_catch.lua
index d4c80c1..d2b58bc 100644
--- a/spec/outputs/5.1/try_catch.lua
+++ b/spec/outputs/5.1/try_catch.lua
@@ -8,10 +8,10 @@ local _anon_func_2 = function(tb)
8 return tb.func() 8 return tb.func()
9end 9end
10local _anon_func_3 = function(tb) 10local _anon_func_3 = function(tb)
11 return tb.func() 11 return (tb.func())
12end 12end
13local _anon_func_4 = function(tb) 13local _anon_func_4 = function(tb)
14 return tb:func(1, 2, 3) 14 return (tb:func(1, 2, 3))
15end 15end
16local _anon_func_5 = function(tb) 16local _anon_func_5 = function(tb)
17 return tb.func(1) 17 return tb.func(1)
@@ -22,6 +22,43 @@ end
22local _anon_func_7 = function(a, b, c, tb) 22local _anon_func_7 = function(a, b, c, tb)
23 return tb.f(a, b, c) 23 return tb.f(a, b, c)
24end 24end
25local _anon_func_8 = function(_arg_0, ...)
26 local ok = _arg_0
27 return ...
28end
29local _anon_func_10 = function(_arg_0, ...)
30 local _ok_0 = _arg_0
31 if _ok_0 then
32 return ...
33 end
34end
35local _anon_func_9 = function(func, pcall)
36 return _anon_func_10(pcall(func))
37end
38local _anon_func_12 = function(_arg_0, ...)
39 local _ok_0 = _arg_0
40 if _ok_0 then
41 return ...
42 end
43end
44local _anon_func_11 = function(func, pcall)
45 return _anon_func_12(pcall(func))
46end
47local _anon_func_14 = function(_arg_0, ...)
48 local _ok_0 = _arg_0
49 if _ok_0 then
50 return ...
51 end
52end
53local _anon_func_13 = function(func, print, xpcall)
54 return _anon_func_14(xpcall(function()
55 print(123)
56 return func()
57 end, function(e)
58 print(e)
59 return e
60 end))
61end
25local f 62local f
26f = function() 63f = function()
27 xpcall(function() 64 xpcall(function()
@@ -64,7 +101,7 @@ f = function()
64 print("OK") 101 print("OK")
65 end 102 end
66 if xpcall(function() 103 if xpcall(function()
67 return func(1) 104 return (func(1))
68 end, function(err) 105 end, function(err)
69 return print(err) 106 return print(err)
70 end) then 107 end) then
@@ -104,10 +141,236 @@ f = function()
104 do 141 do
105 x(function() 142 x(function()
106 local tb, a, b, c 143 local tb, a, b, c
107 f = function() 144 local f1
145 f1 = function()
108 return pcall(_anon_func_7, a, b, c, tb) 146 return pcall(_anon_func_7, a, b, c, tb)
109 end 147 end
110 end) 148 end)
111 end 149 end
150 do
151 local f1
152 f1 = function()
153 do
154 return _anon_func_8(pcall(function()
155 return func()
156 end))
157 end
158 end
159 end
160 do
161 local func
162 local a, b, c
163 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(func)
164 if _ok_0 then
165 a, b, c = _ret_0, _ret_1, _ret_2
166 end
167 end
168 do
169 local a, b, c
170 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(function()
171 return func()
172 end)
173 if _ok_0 then
174 a, b, c = _ret_0, _ret_1, _ret_2
175 end
176 end
177 do
178 local a
179 local _exp_0 = (_anon_func_9(func, pcall))
180 if _exp_0 ~= nil then
181 a = _exp_0
182 else
183 a = "default"
184 end
185 end
186 do
187 f(_anon_func_11(func, pcall))
188 end
189 do
190 f(_anon_func_13(func, print, xpcall))
191 end
112 return nil 192 return nil
113end 193end
194local _anon_func_15 = function(a, b, c, tb)
195 return tb.f(a, b, c)
196end
197local _anon_func_16 = function(_arg_0, ...)
198 local ok = _arg_0
199 return ...
200end
201do
202 xpcall(function()
203 return func(1, 2, 3)
204 end, function(err)
205 return print(err)
206 end)
207 xpcall(function()
208 return func(1, 2, 3)
209 end, function(err)
210 return print(err)
211 end)
212 pcall(function()
213 print("trying")
214 return func(1, 2, 3)
215 end)
216 do
217 local success, result = xpcall(function()
218 return func(1, 2, 3)
219 end, function(err)
220 return print(err)
221 end)
222 success, result = pcall(function()
223 return func(1, 2, 3)
224 end)
225 end
226 local tb = { }
227 pcall(function()
228 return tb.func
229 end)
230 pcall(function()
231 return tb.func()
232 end)
233 pcall(function()
234 return tb.func()
235 end)
236 pcall(function()
237 return (tb.func())
238 end)
239 pcall(function()
240 return (tb:func(1, 2, 3))
241 end)
242 pcall(function()
243 return tb.func(1)
244 end)
245 pcall(function()
246 return tb.func(1)
247 end)
248 if (xpcall(function()
249 return func(1)
250 end, function(err)
251 return print(err)
252 end)) then
253 print("OK")
254 end
255 if xpcall(function()
256 return (func(1))
257 end, function(err)
258 return print(err)
259 end) then
260 print("OK")
261 end
262 do
263 do
264 local success, result = pcall(function()
265 return func("abc", 123)
266 end)
267 if success then
268 print(result)
269 end
270 end
271 local success, result = xpcall(function()
272 return func("abc", 123)
273 end, function(err)
274 return print(err)
275 end)
276 success, result = xpcall(function()
277 return func("abc", 123)
278 end, function(err)
279 return print(err)
280 end)
281 if success then
282 print(result)
283 end
284 end
285 do
286 pcall(function()
287 return func(1, 2, 3)
288 end)
289 pcall(function()
290 return func(1, 2, 3)
291 end)
292 end
293 do
294 x(function()
295 local tb, a, b, c
296 local f1
297 f1 = function()
298 return pcall(_anon_func_15, a, b, c, tb)
299 end
300 end)
301 end
302 do
303 local f1
304 f1 = function()
305 do
306 return _anon_func_16(pcall(function()
307 return func()
308 end))
309 end
310 end
311 end
312 do
313 local func
314 local a, b, c
315 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(func)
316 if _ok_0 then
317 a, b, c = _ret_0, _ret_1, _ret_2
318 end
319 end
320 do
321 local a, b, c
322 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(function()
323 return func()
324 end)
325 if _ok_0 then
326 a, b, c = _ret_0, _ret_1, _ret_2
327 end
328 end
329 do
330 local a
331 local _exp_0 = ((function()
332 return (function(_arg_0, ...)
333 local _ok_0 = _arg_0
334 if _ok_0 then
335 return ...
336 end
337 end)(pcall(function()
338 return func()
339 end))
340 end)())
341 if _exp_0 ~= nil then
342 a = _exp_0
343 else
344 a = "default"
345 end
346 end
347 do
348 f((function()
349 return (function(_arg_0, ...)
350 local _ok_0 = _arg_0
351 if _ok_0 then
352 return ...
353 end
354 end)(pcall(function()
355 return func()
356 end))
357 end)())
358 end
359 do
360 f((function()
361 return (function(_arg_0, ...)
362 local _ok_0 = _arg_0
363 if _ok_0 then
364 return ...
365 end
366 end)(xpcall(function()
367 print(123)
368 return func()
369 end, function(e)
370 print(e)
371 return e
372 end))
373 end)())
374 end
375end
376return nil
diff --git a/spec/outputs/assign.lua b/spec/outputs/assign.lua
index f889865..89c5f8a 100644
--- a/spec/outputs/assign.lua
+++ b/spec/outputs/assign.lua
@@ -36,17 +36,15 @@ local x
36do 36do
37 local f = getHandler() 37 local f = getHandler()
38 if f then 38 if f then
39 do 39 x = ((function()
40 f() 40 f()
41 x = 123 41 return 123
42 end 42 end)())
43 end 43 end
44end 44end
45local _anon_func_0 = function(print) 45local _anon_func_0 = function(print)
46 do 46 print(123)
47 print(123) 47 return { }
48 return { }
49 end
50end 48end
51return _(function() 49return _(function()
52 setmetatable(a, _anon_func_0(print)) 50 setmetatable(a, _anon_func_0(print))
diff --git a/spec/outputs/attrib.lua b/spec/outputs/attrib.lua
index e48963c..bb9916c 100644
--- a/spec/outputs/attrib.lua
+++ b/spec/outputs/attrib.lua
@@ -56,17 +56,21 @@ do
56 end 56 end
57 local b 57 local b
58 if not false then 58 if not false then
59 if x then 59 b = ((function()
60 b = 1 60 if x then
61 end 61 return 1
62 end
63 end)())
62 end 64 end
63 local _close_0 <close> = b 65 local _close_0 <close> = b
64 local c 66 local c
65 if true then 67 if true then
66 local _exp_0 = x 68 c = ((function()
67 if "abc" == _exp_0 then 69 local _exp_0 = x
68 c = 998 70 if "abc" == _exp_0 then
69 end 71 return 998
72 end
73 end)())
70 end 74 end
71 local d 75 local d
72 if (function() 76 if (function()
diff --git a/spec/outputs/codes_from_doc.lua b/spec/outputs/codes_from_doc.lua
index 27f8de5..b7d1236 100644
--- a/spec/outputs/codes_from_doc.lua
+++ b/spec/outputs/codes_from_doc.lua
@@ -20,6 +20,38 @@ local inventory = {
20 } 20 }
21 } 21 }
22} 22}
23local map
24map = function(arr, action)
25 local _accum_0 = { }
26 local _len_0 = 1
27 for _index_0 = 1, #arr do
28 local item = arr[_index_0]
29 _accum_0[_len_0] = action(item)
30 _len_0 = _len_0 + 1
31 end
32 return _accum_0
33end
34local filter
35filter = function(arr, cond)
36 local _accum_0 = { }
37 local _len_0 = 1
38 for _index_0 = 1, #arr do
39 local item = arr[_index_0]
40 if cond(item) then
41 _accum_0[_len_0] = item
42 _len_0 = _len_0 + 1
43 end
44 end
45 return _accum_0
46end
47local reduce
48reduce = function(arr, init, action)
49 for _index_0 = 1, #arr do
50 local item = arr[_index_0]
51 init = action(init, item)
52 end
53 return init
54end
23print(reduce(filter(map({ 55print(reduce(filter(map({
24 1, 56 1,
25 2, 57 2,
@@ -77,6 +109,12 @@ end
77print("yuescript") 109print("yuescript")
78print(3) 110print(3)
79print("Valid enum type:", "Static") 111print("Valid enum type:", "Static")
112do
113 print(123, "hello")
114end
115do
116 print(123, "hello")
117end
80if tb ~= nil then 118if tb ~= nil then
81 tb:func() 119 tb:func()
82end 120end
@@ -177,6 +215,16 @@ for _key_0, _value_0 in pairs(b) do
177 end 215 end
178end 216end
179merge = _tab_0 217merge = _tab_0
218local last
219do
220 local _item_0 = data.items
221 last = _item_0[#_item_0]
222end
223local second_last
224do
225 local _item_0 = data.items
226 second_last = _item_0[#_item_0 - 1]
227end
180local mt = { } 228local mt = { }
181local add 229local add
182add = function(self, right) 230add = function(self, right)
@@ -307,6 +355,14 @@ func({
307 2, 355 2,
308 3 356 3
309}) 357})
358local f
359f = function()
360 return {
361 1,
362 2,
363 3
364 }
365end
310local tb = { 366local tb = {
311 name = "abc", 367 name = "abc",
312 values = { 368 values = {
@@ -547,6 +603,59 @@ end
547local two, four 603local two, four
548local _obj_0 = items 604local _obj_0 = items
549two, four = _obj_0[2], _obj_0[4] 605two, four = _obj_0[2], _obj_0[4]
606local orders = {
607 "first",
608 "second",
609 "third",
610 "fourth",
611 "last"
612}
613local first, bulk, last = orders[1], (function()
614 local _accum_0 = { }
615 local _len_0 = 1
616 local _max_0 = #orders + -2 + 1
617 for _index_0 = 2, _max_0 do
618 local _item_0 = orders[_index_0]
619 _accum_0[_len_0] = _item_0
620 _len_0 = _len_0 + 1
621 end
622 return _accum_0
623end)(), orders[#orders]
624print(first)
625print(bulk)
626print(last)
627local first, rest
628do
629 local _obj_0 = orders
630 first, rest = _obj_0[1], (function()
631 local _accum_0 = { }
632 local _len_0 = 1
633 local _max_0 = #_obj_0
634 for _index_0 = 2, _max_0 do
635 local _item_0 = _obj_0[_index_0]
636 _accum_0[_len_0] = _item_0
637 _len_0 = _len_0 + 1
638 end
639 return _accum_0
640 end)()
641end
642local start, last
643do
644 local _obj_0 = orders
645 start, last = (function()
646 local _accum_0 = { }
647 local _len_0 = 1
648 local _max_0 = #_obj_0 + -2 + 1
649 for _index_0 = 1, _max_0 do
650 local _item_0 = _obj_0[_index_0]
651 _accum_0[_len_0] = _item_0
652 _len_0 = _len_0 + 1
653 end
654 return _accum_0
655 end)(), _obj_0[#_obj_0]
656end
657local _obj_0 = orders
658first, last = _obj_0[1], _obj_0[#_obj_0]
550local tuples = { 659local tuples = {
551 { 660 {
552 "hello", 661 "hello",
@@ -648,6 +757,56 @@ end)
648if success then 757if success then
649 print(result) 758 print(result)
650end 759end
760local a, b, c
761do
762 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(function()
763 return func()
764 end)
765 if _ok_0 then
766 a, b, c = _ret_0, _ret_1, _ret_2
767 end
768end
769do
770 local _exp_0 = ((function()
771 return (function(_arg_0, ...)
772 local _ok_0 = _arg_0
773 if _ok_0 then
774 return ...
775 end
776 end)(pcall(function()
777 return func()
778 end))
779 end)())
780 if _exp_0 ~= nil then
781 a = _exp_0
782 else
783 a = "default"
784 end
785end
786f((function()
787 return (function(_arg_0, ...)
788 local _ok_0 = _arg_0
789 if _ok_0 then
790 return ...
791 end
792 end)(pcall(function()
793 return func()
794 end))
795end)())
796f((function()
797 return (function(_arg_0, ...)
798 local _ok_0 = _arg_0
799 if _ok_0 then
800 return ...
801 end
802 end)(xpcall(function()
803 print(123)
804 return func()
805 end, function(e)
806 print(e)
807 return e
808 end))
809end)())
651local a <const> = 123 810local a <const> = 123
652local _ <close> = setmetatable({ }, { 811local _ <close> = setmetatable({ }, {
653 __close = function() 812 __close = function()
@@ -657,10 +816,19 @@ local _ <close> = setmetatable({ }, {
657local a, b, c, d 816local a, b, c, d
658local _obj_0 = tb 817local _obj_0 = tb
659a, b, c, d = _obj_0.a, _obj_0.b, _obj_0[1], _obj_0[2] 818a, b, c, d = _obj_0.a, _obj_0.b, _obj_0[1], _obj_0[2]
819Constant = 123
660local some_string = "Here is a string\n that has a line break in it." 820local some_string = "Here is a string\n that has a line break in it."
661print("I am " .. tostring(math.random() * 100) .. "% sure.") 821print("I am " .. tostring(math.random() * 100) .. "% sure.")
662local integer = 1000000 822local integer = 1000000
663local hex = 0xEFBBBF 823local hex = 0xEFBBBF
824local binary = 19
825local str = "key: value\nlist:\n - item1\n - " .. tostring(expr)
826local fn
827fn = function()
828 local str = "foo:\n bar: baz"
829 return str
830end
831local str = "path: \"C:\\Program Files\\App\"\nnote: 'He said: \"" .. tostring(Hello) .. "!\"'"
664local my_function 832local my_function
665my_function = function() end 833my_function = function() end
666my_function() 834my_function()
@@ -860,11 +1028,10 @@ for i, item in ipairs(items) do
860 _len_0 = _len_0 + 1 1028 _len_0 = _len_0 + 1
861end 1029end
862doubled = _accum_0 1030doubled = _accum_0
863local iter = ipairs(items)
864local slice 1031local slice
865local _accum_0 = { } 1032local _accum_0 = { }
866local _len_0 = 1 1033local _len_0 = 1
867for i, item in iter do 1034for i, item in ipairs(items) do
868 if i > 1 and i < 3 then 1035 if i > 1 and i < 3 then
869 _accum_0[_len_0] = item 1036 _accum_0[_len_0] = item
870 _len_0 = _len_0 + 1 1037 _len_0 = _len_0 + 1
@@ -971,8 +1138,7 @@ local slice
971local _accum_0 = { } 1138local _accum_0 = { }
972local _len_0 = 1 1139local _len_0 = 1
973local _list_0 = items 1140local _list_0 = items
974local _max_0 = 5 1141for _index_0 = 1, 5 do
975for _index_0 = 1, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
976 local item = _list_0[_index_0] 1142 local item = _list_0[_index_0]
977 _accum_0[_len_0] = item 1143 _accum_0[_len_0] = item
978 _len_0 = _len_0 + 1 1144 _len_0 = _len_0 + 1
@@ -982,7 +1148,8 @@ local slice
982local _accum_0 = { } 1148local _accum_0 = { }
983local _len_0 = 1 1149local _len_0 = 1
984local _list_0 = items 1150local _list_0 = items
985for _index_0 = 2, #_list_0 do 1151local _max_0 = #_list_0
1152for _index_0 = 2, _max_0 do
986 local item = _list_0[_index_0] 1153 local item = _list_0[_index_0]
987 _accum_0[_len_0] = item 1154 _accum_0[_len_0] = item
988 _len_0 = _len_0 + 1 1155 _len_0 = _len_0 + 1
@@ -992,12 +1159,46 @@ local slice
992local _accum_0 = { } 1159local _accum_0 = { }
993local _len_0 = 1 1160local _len_0 = 1
994local _list_0 = items 1161local _list_0 = items
995for _index_0 = 1, #_list_0, 2 do 1162local _max_0 = #_list_0
1163for _index_0 = 1, _max_0, 2 do
996 local item = _list_0[_index_0] 1164 local item = _list_0[_index_0]
997 _accum_0[_len_0] = item 1165 _accum_0[_len_0] = item
998 _len_0 = _len_0 + 1 1166 _len_0 = _len_0 + 1
999end 1167end
1000slice = _accum_0 1168slice = _accum_0
1169local slice
1170local _accum_0 = { }
1171local _len_0 = 1
1172local _list_0 = items
1173local _min_0 = #_list_0 + -4 + 1
1174local _max_0 = #_list_0 + -1 + 1
1175for _index_0 = _min_0, _max_0 do
1176 local item = _list_0[_index_0]
1177 _accum_0[_len_0] = item
1178 _len_0 = _len_0 + 1
1179end
1180slice = _accum_0
1181local reverse_slice
1182local _accum_0 = { }
1183local _len_0 = 1
1184local _list_0 = items
1185local _min_0 = #_list_0 + -1 + 1
1186for _index_0 = _min_0, 1, -1 do
1187 local item = _list_0[_index_0]
1188 _accum_0[_len_0] = item
1189 _len_0 = _len_0 + 1
1190end
1191reverse_slice = _accum_0
1192local sub_list
1193local _accum_0 = { }
1194local _len_0 = 1
1195local _list_0 = items
1196for _index_0 = 2, 4 do
1197 local _item_0 = _list_0[_index_0]
1198 _accum_0[_len_0] = _item_0
1199 _len_0 = _len_0 + 1
1200end
1201sub_list = _accum_0
1001for i = 10, 20 do 1202for i = 10, 20 do
1002 print(i) 1203 print(i)
1003end 1204end
@@ -1008,8 +1209,7 @@ for key, value in pairs(object) do
1008 print(key, value) 1209 print(key, value)
1009end 1210end
1010local _list_0 = items 1211local _list_0 = items
1011local _max_0 = 4 1212for _index_0 = 2, 4 do
1012for _index_0 = 2, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
1013 local item = _list_0[_index_0] 1213 local item = _list_0[_index_0]
1014 print(item) 1214 print(item)
1015end 1215end
@@ -1027,12 +1227,24 @@ local _len_0 = 1
1027for i = 1, 20 do 1227for i = 1, 20 do
1028 if i % 2 == 0 then 1228 if i % 2 == 0 then
1029 _accum_0[_len_0] = i * 2 1229 _accum_0[_len_0] = i * 2
1230 _len_0 = _len_0 + 1
1030 else 1231 else
1031 _accum_0[_len_0] = i 1232 _accum_0[_len_0] = i
1233 _len_0 = _len_0 + 1
1032 end 1234 end
1033 _len_0 = _len_0 + 1
1034end 1235end
1035doubled_evens = _accum_0 1236doubled_evens = _accum_0
1237local first_large
1238local _accum_0
1239local _list_0 = numbers
1240for _index_0 = 1, #_list_0 do
1241 local n = _list_0[_index_0]
1242 if n > 10 then
1243 _accum_0 = n
1244 break
1245 end
1246end
1247first_large = _accum_0
1036local func_a 1248local func_a
1037func_a = function() 1249func_a = function()
1038 for i = 1, 10 do 1250 for i = 1, 10 do
@@ -1181,7 +1393,7 @@ if "Robert" == name then
1181elseif "Dan" == name or "Daniel" == name then 1393elseif "Dan" == name or "Daniel" == name then
1182 print("Your name, it's Dan") 1394 print("Your name, it's Dan")
1183else 1395else
1184 print("I don't know about your name") 1396 print("I don't know about you with name " .. tostring(name))
1185end 1397end
1186local b = 1 1398local b = 1
1187local next_number 1399local next_number
@@ -1281,6 +1493,192 @@ if _tab_0 then
1281 end 1493 end
1282 print("Vec2 " .. tostring(x) .. ", " .. tostring(y)) 1494 print("Vec2 " .. tostring(x) .. ", " .. tostring(y))
1283end 1495end
1496local _exp_0 = tb
1497local _type_0 = type(_exp_0)
1498local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1499local _match_0 = false
1500if _tab_0 then
1501 if 1 == _exp_0[1] and 2 == _exp_0[2] and 3 == _exp_0[3] then
1502 _match_0 = true
1503 print("1, 2, 3")
1504 end
1505end
1506if not _match_0 then
1507 local _match_1 = false
1508 if _tab_0 then
1509 local b = _exp_0[2]
1510 if 1 == _exp_0[1] and b ~= nil and 3 == _exp_0[3] then
1511 _match_1 = true
1512 print("1, " .. tostring(b) .. ", 3")
1513 end
1514 end
1515 if not _match_1 then
1516 if _tab_0 then
1517 local b = _exp_0[3]
1518 if b == nil then
1519 b = 3
1520 end
1521 if 1 == _exp_0[1] and 2 == _exp_0[2] then
1522 print("1, 2, " .. tostring(b))
1523 end
1524 end
1525 end
1526end
1527local _exp_0 = tb
1528local _type_0 = type(_exp_0)
1529local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1530local _match_0 = false
1531if _tab_0 then
1532 local result = _exp_0.result
1533 if true == _exp_0.success and result ~= nil then
1534 _match_0 = true
1535 print("success", result)
1536 end
1537end
1538if not _match_0 then
1539 local _match_1 = false
1540 if _tab_0 then
1541 if false == _exp_0.success then
1542 _match_1 = true
1543 print("failed", result)
1544 end
1545 end
1546 if not _match_1 then
1547 print("invalid")
1548 end
1549end
1550local _exp_0 = tb
1551local _type_0 = type(_exp_0)
1552local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1553local _match_0 = false
1554if _tab_0 then
1555 local content
1556 do
1557 local _obj_0 = _exp_0.data
1558 local _type_1 = type(_obj_0)
1559 if "table" == _type_1 or "userdata" == _type_1 then
1560 content = _obj_0.content
1561 end
1562 end
1563 local _val_0
1564 do
1565 local _obj_0 = _exp_0.data
1566 if _obj_0 ~= nil then
1567 _val_0 = _obj_0.type
1568 end
1569 end
1570 if "success" == _val_0 and content ~= nil then
1571 _match_0 = true
1572 print("success", content)
1573 end
1574end
1575if not _match_0 then
1576 local _match_1 = false
1577 if _tab_0 then
1578 local content
1579 do
1580 local _obj_0 = _exp_0.data
1581 local _type_1 = type(_obj_0)
1582 if "table" == _type_1 or "userdata" == _type_1 then
1583 content = _obj_0.content
1584 end
1585 end
1586 local _val_0
1587 do
1588 local _obj_0 = _exp_0.data
1589 if _obj_0 ~= nil then
1590 _val_0 = _obj_0.type
1591 end
1592 end
1593 if "error" == _val_0 and content ~= nil then
1594 _match_1 = true
1595 print("failed", content)
1596 end
1597 end
1598 if not _match_1 then
1599 print("invalid")
1600 end
1601end
1602local _exp_0 = tb
1603local _type_0 = type(_exp_0)
1604local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1605if _tab_0 then
1606 local fourth = _exp_0[4]
1607 local _val_0
1608 do
1609 local _obj_0 = _exp_0[1]
1610 if _obj_0 ~= nil then
1611 _val_0 = _obj_0.a
1612 end
1613 end
1614 local _val_1
1615 do
1616 local _obj_0 = _exp_0[1]
1617 if _obj_0 ~= nil then
1618 _val_1 = _obj_0.b
1619 end
1620 end
1621 local _val_2
1622 do
1623 local _obj_0 = _exp_0[2]
1624 if _obj_0 ~= nil then
1625 _val_2 = _obj_0.a
1626 end
1627 end
1628 local _val_3
1629 do
1630 local _obj_0 = _exp_0[2]
1631 if _obj_0 ~= nil then
1632 _val_3 = _obj_0.b
1633 end
1634 end
1635 local _val_4
1636 do
1637 local _obj_0 = _exp_0[3]
1638 if _obj_0 ~= nil then
1639 _val_4 = _obj_0.a
1640 end
1641 end
1642 local _val_5
1643 do
1644 local _obj_0 = _exp_0[3]
1645 if _obj_0 ~= nil then
1646 _val_5 = _obj_0.b
1647 end
1648 end
1649 if 1 == _val_0 and 2 == _val_1 and 3 == _val_2 and 4 == _val_3 and 5 == _val_4 and 6 == _val_5 and fourth ~= nil then
1650 print("matched", fourth)
1651 end
1652end
1653local segments = {
1654 "admin",
1655 "users",
1656 "logs",
1657 "view"
1658}
1659local _type_0 = type(segments)
1660local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1661if _tab_0 then
1662 local groups
1663 do
1664 local _accum_0 = { }
1665 local _len_0 = 1
1666 local _max_0 = #segments + -3 + 1
1667 for _index_0 = 1, _max_0 do
1668 local _item_0 = segments[_index_0]
1669 _accum_0[_len_0] = _item_0
1670 _len_0 = _len_0 + 1
1671 end
1672 groups = _accum_0
1673 end
1674 local resource = segments[#segments - 1]
1675 local action = segments[#segments]
1676 if resource ~= nil and action ~= nil then
1677 print("Group:", groups)
1678 print("Resource:", resource)
1679 print("Action:", action)
1680 end
1681end
1284local Inventory 1682local Inventory
1285local _class_0 1683local _class_0
1286local _base_0 = { 1684local _base_0 = {
@@ -1937,6 +2335,10 @@ do
1937 _with_1["key-name"] = value 2335 _with_1["key-name"] = value
1938end 2336end
1939_with_0[#_with_0 + 1] = "abc" 2337_with_0[#_with_0 + 1] = "abc"
2338local _with_0 = obj
2339if _with_0 ~= nil then
2340 print(obj.name)
2341end
1940do 2342do
1941 local var = "hello" 2343 local var = "hello"
1942 print(var) 2344 print(var)
@@ -2027,6 +2429,38 @@ local inventory = {
2027 } 2429 }
2028 } 2430 }
2029} 2431}
2432local map
2433map = function(arr, action)
2434 local _accum_0 = { }
2435 local _len_0 = 1
2436 for _index_0 = 1, #arr do
2437 local item = arr[_index_0]
2438 _accum_0[_len_0] = action(item)
2439 _len_0 = _len_0 + 1
2440 end
2441 return _accum_0
2442end
2443local filter
2444filter = function(arr, cond)
2445 local _accum_0 = { }
2446 local _len_0 = 1
2447 for _index_0 = 1, #arr do
2448 local item = arr[_index_0]
2449 if cond(item) then
2450 _accum_0[_len_0] = item
2451 _len_0 = _len_0 + 1
2452 end
2453 end
2454 return _accum_0
2455end
2456local reduce
2457reduce = function(arr, init, action)
2458 for _index_0 = 1, #arr do
2459 local item = arr[_index_0]
2460 init = action(init, item)
2461 end
2462 return init
2463end
2030print(reduce(filter(map({ 2464print(reduce(filter(map({
2031 1, 2465 1,
2032 2, 2466 2,
@@ -2084,6 +2518,12 @@ end
2084print("yuescript") 2518print("yuescript")
2085print(3) 2519print(3)
2086print("Valid enum type:", "Static") 2520print("Valid enum type:", "Static")
2521do
2522 print(123, "hello")
2523end
2524do
2525 print(123, "hello")
2526end
2087if tb ~= nil then 2527if tb ~= nil then
2088 tb:func() 2528 tb:func()
2089end 2529end
@@ -2184,6 +2624,16 @@ for _key_0, _value_0 in pairs(b) do
2184 end 2624 end
2185end 2625end
2186merge = _tab_0 2626merge = _tab_0
2627local last
2628do
2629 local _item_0 = data.items
2630 last = _item_0[#_item_0]
2631end
2632local second_last
2633do
2634 local _item_0 = data.items
2635 second_last = _item_0[#_item_0 - 1]
2636end
2187local mt = { } 2637local mt = { }
2188local add 2638local add
2189add = function(self, right) 2639add = function(self, right)
@@ -2314,6 +2764,14 @@ func({
2314 2, 2764 2,
2315 3 2765 3
2316}) 2766})
2767local f
2768f = function()
2769 return {
2770 1,
2771 2,
2772 3
2773 }
2774end
2317local tb = { 2775local tb = {
2318 name = "abc", 2776 name = "abc",
2319 values = { 2777 values = {
@@ -2554,6 +3012,59 @@ end
2554local two, four 3012local two, four
2555local _obj_0 = items 3013local _obj_0 = items
2556two, four = _obj_0[2], _obj_0[4] 3014two, four = _obj_0[2], _obj_0[4]
3015local orders = {
3016 "first",
3017 "second",
3018 "third",
3019 "fourth",
3020 "last"
3021}
3022local first, bulk, last = orders[1], (function()
3023 local _accum_0 = { }
3024 local _len_0 = 1
3025 local _max_0 = #orders + -2 + 1
3026 for _index_0 = 2, _max_0 do
3027 local _item_0 = orders[_index_0]
3028 _accum_0[_len_0] = _item_0
3029 _len_0 = _len_0 + 1
3030 end
3031 return _accum_0
3032end)(), orders[#orders]
3033print(first)
3034print(bulk)
3035print(last)
3036local first, rest
3037do
3038 local _obj_0 = orders
3039 first, rest = _obj_0[1], (function()
3040 local _accum_0 = { }
3041 local _len_0 = 1
3042 local _max_0 = #_obj_0
3043 for _index_0 = 2, _max_0 do
3044 local _item_0 = _obj_0[_index_0]
3045 _accum_0[_len_0] = _item_0
3046 _len_0 = _len_0 + 1
3047 end
3048 return _accum_0
3049 end)()
3050end
3051local start, last
3052do
3053 local _obj_0 = orders
3054 start, last = (function()
3055 local _accum_0 = { }
3056 local _len_0 = 1
3057 local _max_0 = #_obj_0 + -2 + 1
3058 for _index_0 = 1, _max_0 do
3059 local _item_0 = _obj_0[_index_0]
3060 _accum_0[_len_0] = _item_0
3061 _len_0 = _len_0 + 1
3062 end
3063 return _accum_0
3064 end)(), _obj_0[#_obj_0]
3065end
3066local _obj_0 = orders
3067first, last = _obj_0[1], _obj_0[#_obj_0]
2557local tuples = { 3068local tuples = {
2558 { 3069 {
2559 "hello", 3070 "hello",
@@ -2655,6 +3166,56 @@ end)
2655if success then 3166if success then
2656 print(result) 3167 print(result)
2657end 3168end
3169local a, b, c
3170do
3171 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(function()
3172 return func()
3173 end)
3174 if _ok_0 then
3175 a, b, c = _ret_0, _ret_1, _ret_2
3176 end
3177end
3178do
3179 local _exp_0 = ((function()
3180 return (function(_arg_0, ...)
3181 local _ok_0 = _arg_0
3182 if _ok_0 then
3183 return ...
3184 end
3185 end)(pcall(function()
3186 return func()
3187 end))
3188 end)())
3189 if _exp_0 ~= nil then
3190 a = _exp_0
3191 else
3192 a = "default"
3193 end
3194end
3195f((function()
3196 return (function(_arg_0, ...)
3197 local _ok_0 = _arg_0
3198 if _ok_0 then
3199 return ...
3200 end
3201 end)(pcall(function()
3202 return func()
3203 end))
3204end)())
3205f((function()
3206 return (function(_arg_0, ...)
3207 local _ok_0 = _arg_0
3208 if _ok_0 then
3209 return ...
3210 end
3211 end)(xpcall(function()
3212 print(123)
3213 return func()
3214 end, function(e)
3215 print(e)
3216 return e
3217 end))
3218end)())
2658local a <const> = 123 3219local a <const> = 123
2659local _ <close> = setmetatable({ }, { 3220local _ <close> = setmetatable({ }, {
2660 __close = function() 3221 __close = function()
@@ -2664,10 +3225,19 @@ local _ <close> = setmetatable({ }, {
2664local a, b, c, d 3225local a, b, c, d
2665local _obj_0 = tb 3226local _obj_0 = tb
2666a, b, c, d = _obj_0.a, _obj_0.b, _obj_0[1], _obj_0[2] 3227a, b, c, d = _obj_0.a, _obj_0.b, _obj_0[1], _obj_0[2]
3228Constant = 123
2667local some_string = "Here is a string\n that has a line break in it." 3229local some_string = "Here is a string\n that has a line break in it."
2668print("I am " .. tostring(math.random() * 100) .. "% sure.") 3230print("I am " .. tostring(math.random() * 100) .. "% sure.")
2669local integer = 1000000 3231local integer = 1000000
2670local hex = 0xEFBBBF 3232local hex = 0xEFBBBF
3233local binary = 19
3234local str = "key: value\nlist:\n - item1\n - " .. tostring(expr)
3235local fn
3236fn = function()
3237 local str = "foo:\n bar: baz"
3238 return str
3239end
3240local str = "path: \"C:\\Program Files\\App\"\nnote: 'He said: \"" .. tostring(Hello) .. "!\"'"
2671local my_function 3241local my_function
2672my_function = function() end 3242my_function = function() end
2673my_function() 3243my_function()
@@ -2867,11 +3437,10 @@ for i, item in ipairs(items) do
2867 _len_0 = _len_0 + 1 3437 _len_0 = _len_0 + 1
2868end 3438end
2869doubled = _accum_0 3439doubled = _accum_0
2870local iter = ipairs(items)
2871local slice 3440local slice
2872local _accum_0 = { } 3441local _accum_0 = { }
2873local _len_0 = 1 3442local _len_0 = 1
2874for i, item in iter do 3443for i, item in ipairs(items) do
2875 if i > 1 and i < 3 then 3444 if i > 1 and i < 3 then
2876 _accum_0[_len_0] = item 3445 _accum_0[_len_0] = item
2877 _len_0 = _len_0 + 1 3446 _len_0 = _len_0 + 1
@@ -2978,8 +3547,7 @@ local slice
2978local _accum_0 = { } 3547local _accum_0 = { }
2979local _len_0 = 1 3548local _len_0 = 1
2980local _list_0 = items 3549local _list_0 = items
2981local _max_0 = 5 3550for _index_0 = 1, 5 do
2982for _index_0 = 1, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
2983 local item = _list_0[_index_0] 3551 local item = _list_0[_index_0]
2984 _accum_0[_len_0] = item 3552 _accum_0[_len_0] = item
2985 _len_0 = _len_0 + 1 3553 _len_0 = _len_0 + 1
@@ -2989,7 +3557,8 @@ local slice
2989local _accum_0 = { } 3557local _accum_0 = { }
2990local _len_0 = 1 3558local _len_0 = 1
2991local _list_0 = items 3559local _list_0 = items
2992for _index_0 = 2, #_list_0 do 3560local _max_0 = #_list_0
3561for _index_0 = 2, _max_0 do
2993 local item = _list_0[_index_0] 3562 local item = _list_0[_index_0]
2994 _accum_0[_len_0] = item 3563 _accum_0[_len_0] = item
2995 _len_0 = _len_0 + 1 3564 _len_0 = _len_0 + 1
@@ -2999,12 +3568,46 @@ local slice
2999local _accum_0 = { } 3568local _accum_0 = { }
3000local _len_0 = 1 3569local _len_0 = 1
3001local _list_0 = items 3570local _list_0 = items
3002for _index_0 = 1, #_list_0, 2 do 3571local _max_0 = #_list_0
3572for _index_0 = 1, _max_0, 2 do
3003 local item = _list_0[_index_0] 3573 local item = _list_0[_index_0]
3004 _accum_0[_len_0] = item 3574 _accum_0[_len_0] = item
3005 _len_0 = _len_0 + 1 3575 _len_0 = _len_0 + 1
3006end 3576end
3007slice = _accum_0 3577slice = _accum_0
3578local slice
3579local _accum_0 = { }
3580local _len_0 = 1
3581local _list_0 = items
3582local _min_0 = #_list_0 + -4 + 1
3583local _max_0 = #_list_0 + -1 + 1
3584for _index_0 = _min_0, _max_0 do
3585 local item = _list_0[_index_0]
3586 _accum_0[_len_0] = item
3587 _len_0 = _len_0 + 1
3588end
3589slice = _accum_0
3590local reverse_slice
3591local _accum_0 = { }
3592local _len_0 = 1
3593local _list_0 = items
3594local _min_0 = #_list_0 + -1 + 1
3595for _index_0 = _min_0, 1, -1 do
3596 local item = _list_0[_index_0]
3597 _accum_0[_len_0] = item
3598 _len_0 = _len_0 + 1
3599end
3600reverse_slice = _accum_0
3601local sub_list
3602local _accum_0 = { }
3603local _len_0 = 1
3604local _list_0 = items
3605for _index_0 = 2, 4 do
3606 local _item_0 = _list_0[_index_0]
3607 _accum_0[_len_0] = _item_0
3608 _len_0 = _len_0 + 1
3609end
3610sub_list = _accum_0
3008for i = 10, 20 do 3611for i = 10, 20 do
3009 print(i) 3612 print(i)
3010end 3613end
@@ -3015,8 +3618,7 @@ for key, value in pairs(object) do
3015 print(key, value) 3618 print(key, value)
3016end 3619end
3017local _list_0 = items 3620local _list_0 = items
3018local _max_0 = 4 3621for _index_0 = 2, 4 do
3019for _index_0 = 2, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
3020 local item = _list_0[_index_0] 3622 local item = _list_0[_index_0]
3021 print(item) 3623 print(item)
3022end 3624end
@@ -3034,12 +3636,24 @@ local _len_0 = 1
3034for i = 1, 20 do 3636for i = 1, 20 do
3035 if i % 2 == 0 then 3637 if i % 2 == 0 then
3036 _accum_0[_len_0] = i * 2 3638 _accum_0[_len_0] = i * 2
3639 _len_0 = _len_0 + 1
3037 else 3640 else
3038 _accum_0[_len_0] = i 3641 _accum_0[_len_0] = i
3642 _len_0 = _len_0 + 1
3039 end 3643 end
3040 _len_0 = _len_0 + 1
3041end 3644end
3042doubled_evens = _accum_0 3645doubled_evens = _accum_0
3646local first_large
3647local _accum_0
3648local _list_0 = numbers
3649for _index_0 = 1, #_list_0 do
3650 local n = _list_0[_index_0]
3651 if n > 10 then
3652 _accum_0 = n
3653 break
3654 end
3655end
3656first_large = _accum_0
3043local func_a 3657local func_a
3044func_a = function() 3658func_a = function()
3045 for i = 1, 10 do 3659 for i = 1, 10 do
@@ -3188,7 +3802,7 @@ if "Robert" == name then
3188elseif "Dan" == name or "Daniel" == name then 3802elseif "Dan" == name or "Daniel" == name then
3189 print("Your name, it's Dan") 3803 print("Your name, it's Dan")
3190else 3804else
3191 print("I don't know about your name") 3805 print("I don't know about you with name " .. tostring(name))
3192end 3806end
3193local b = 1 3807local b = 1
3194local next_number 3808local next_number
@@ -3288,6 +3902,192 @@ if _tab_0 then
3288 end 3902 end
3289 print("Vec2 " .. tostring(x) .. ", " .. tostring(y)) 3903 print("Vec2 " .. tostring(x) .. ", " .. tostring(y))
3290end 3904end
3905local _exp_0 = tb
3906local _type_0 = type(_exp_0)
3907local _tab_0 = "table" == _type_0 or "userdata" == _type_0
3908local _match_0 = false
3909if _tab_0 then
3910 if 1 == _exp_0[1] and 2 == _exp_0[2] and 3 == _exp_0[3] then
3911 _match_0 = true
3912 print("1, 2, 3")
3913 end
3914end
3915if not _match_0 then
3916 local _match_1 = false
3917 if _tab_0 then
3918 local b = _exp_0[2]
3919 if 1 == _exp_0[1] and b ~= nil and 3 == _exp_0[3] then
3920 _match_1 = true
3921 print("1, " .. tostring(b) .. ", 3")
3922 end
3923 end
3924 if not _match_1 then
3925 if _tab_0 then
3926 local b = _exp_0[3]
3927 if b == nil then
3928 b = 3
3929 end
3930 if 1 == _exp_0[1] and 2 == _exp_0[2] then
3931 print("1, 2, " .. tostring(b))
3932 end
3933 end
3934 end
3935end
3936local _exp_0 = tb
3937local _type_0 = type(_exp_0)
3938local _tab_0 = "table" == _type_0 or "userdata" == _type_0
3939local _match_0 = false
3940if _tab_0 then
3941 local result = _exp_0.result
3942 if true == _exp_0.success and result ~= nil then
3943 _match_0 = true
3944 print("success", result)
3945 end
3946end
3947if not _match_0 then
3948 local _match_1 = false
3949 if _tab_0 then
3950 if false == _exp_0.success then
3951 _match_1 = true
3952 print("failed", result)
3953 end
3954 end
3955 if not _match_1 then
3956 print("invalid")
3957 end
3958end
3959local _exp_0 = tb
3960local _type_0 = type(_exp_0)
3961local _tab_0 = "table" == _type_0 or "userdata" == _type_0
3962local _match_0 = false
3963if _tab_0 then
3964 local content
3965 do
3966 local _obj_0 = _exp_0.data
3967 local _type_1 = type(_obj_0)
3968 if "table" == _type_1 or "userdata" == _type_1 then
3969 content = _obj_0.content
3970 end
3971 end
3972 local _val_0
3973 do
3974 local _obj_0 = _exp_0.data
3975 if _obj_0 ~= nil then
3976 _val_0 = _obj_0.type
3977 end
3978 end
3979 if "success" == _val_0 and content ~= nil then
3980 _match_0 = true
3981 print("success", content)
3982 end
3983end
3984if not _match_0 then
3985 local _match_1 = false
3986 if _tab_0 then
3987 local content
3988 do
3989 local _obj_0 = _exp_0.data
3990 local _type_1 = type(_obj_0)
3991 if "table" == _type_1 or "userdata" == _type_1 then
3992 content = _obj_0.content
3993 end
3994 end
3995 local _val_0
3996 do
3997 local _obj_0 = _exp_0.data
3998 if _obj_0 ~= nil then
3999 _val_0 = _obj_0.type
4000 end
4001 end
4002 if "error" == _val_0 and content ~= nil then
4003 _match_1 = true
4004 print("failed", content)
4005 end
4006 end
4007 if not _match_1 then
4008 print("invalid")
4009 end
4010end
4011local _exp_0 = tb
4012local _type_0 = type(_exp_0)
4013local _tab_0 = "table" == _type_0 or "userdata" == _type_0
4014if _tab_0 then
4015 local fourth = _exp_0[4]
4016 local _val_0
4017 do
4018 local _obj_0 = _exp_0[1]
4019 if _obj_0 ~= nil then
4020 _val_0 = _obj_0.a
4021 end
4022 end
4023 local _val_1
4024 do
4025 local _obj_0 = _exp_0[1]
4026 if _obj_0 ~= nil then
4027 _val_1 = _obj_0.b
4028 end
4029 end
4030 local _val_2
4031 do
4032 local _obj_0 = _exp_0[2]
4033 if _obj_0 ~= nil then
4034 _val_2 = _obj_0.a
4035 end
4036 end
4037 local _val_3
4038 do
4039 local _obj_0 = _exp_0[2]
4040 if _obj_0 ~= nil then
4041 _val_3 = _obj_0.b
4042 end
4043 end
4044 local _val_4
4045 do
4046 local _obj_0 = _exp_0[3]
4047 if _obj_0 ~= nil then
4048 _val_4 = _obj_0.a
4049 end
4050 end
4051 local _val_5
4052 do
4053 local _obj_0 = _exp_0[3]
4054 if _obj_0 ~= nil then
4055 _val_5 = _obj_0.b
4056 end
4057 end
4058 if 1 == _val_0 and 2 == _val_1 and 3 == _val_2 and 4 == _val_3 and 5 == _val_4 and 6 == _val_5 and fourth ~= nil then
4059 print("matched", fourth)
4060 end
4061end
4062local segments = {
4063 "admin",
4064 "users",
4065 "logs",
4066 "view"
4067}
4068local _type_0 = type(segments)
4069local _tab_0 = "table" == _type_0 or "userdata" == _type_0
4070if _tab_0 then
4071 local groups
4072 do
4073 local _accum_0 = { }
4074 local _len_0 = 1
4075 local _max_0 = #segments + -3 + 1
4076 for _index_0 = 1, _max_0 do
4077 local _item_0 = segments[_index_0]
4078 _accum_0[_len_0] = _item_0
4079 _len_0 = _len_0 + 1
4080 end
4081 groups = _accum_0
4082 end
4083 local resource = segments[#segments - 1]
4084 local action = segments[#segments]
4085 if resource ~= nil and action ~= nil then
4086 print("Group:", groups)
4087 print("Resource:", resource)
4088 print("Action:", action)
4089 end
4090end
3291local Inventory 4091local Inventory
3292local _class_0 4092local _class_0
3293local _base_0 = { 4093local _base_0 = {
@@ -3944,6 +4744,10 @@ do
3944 _with_1["key-name"] = value 4744 _with_1["key-name"] = value
3945end 4745end
3946_with_0[#_with_0 + 1] = "abc" 4746_with_0[#_with_0 + 1] = "abc"
4747local _with_0 = obj
4748if _with_0 ~= nil then
4749 print(obj.name)
4750end
3947do 4751do
3948 local var = "hello" 4752 local var = "hello"
3949 print(var) 4753 print(var)
diff --git a/spec/outputs/codes_from_doc_zh.lua b/spec/outputs/codes_from_doc_zh.lua
index ffa0483..b54d6d7 100644
--- a/spec/outputs/codes_from_doc_zh.lua
+++ b/spec/outputs/codes_from_doc_zh.lua
@@ -20,6 +20,38 @@ local inventory = {
20 } 20 }
21 } 21 }
22} 22}
23local map
24map = function(arr, action)
25 local _accum_0 = { }
26 local _len_0 = 1
27 for _index_0 = 1, #arr do
28 local item = arr[_index_0]
29 _accum_0[_len_0] = action(item)
30 _len_0 = _len_0 + 1
31 end
32 return _accum_0
33end
34local filter
35filter = function(arr, cond)
36 local _accum_0 = { }
37 local _len_0 = 1
38 for _index_0 = 1, #arr do
39 local item = arr[_index_0]
40 if cond(item) then
41 _accum_0[_len_0] = item
42 _len_0 = _len_0 + 1
43 end
44 end
45 return _accum_0
46end
47local reduce
48reduce = function(arr, init, action)
49 for _index_0 = 1, #arr do
50 local item = arr[_index_0]
51 init = action(init, item)
52 end
53 return init
54end
23print(reduce(filter(map({ 55print(reduce(filter(map({
24 1, 56 1,
25 2, 57 2,
@@ -77,6 +109,12 @@ end
77print("yuescript") 109print("yuescript")
78print(3) 110print(3)
79print("有效的枚举类型:", "Static") 111print("有效的枚举类型:", "Static")
112do
113 print(123, "hello")
114end
115do
116 print(123, "hello")
117end
80if tb ~= nil then 118if tb ~= nil then
81 tb:func() 119 tb:func()
82end 120end
@@ -177,6 +215,16 @@ for _key_0, _value_0 in pairs(b) do
177 end 215 end
178end 216end
179merge = _tab_0 217merge = _tab_0
218local last
219do
220 local _item_0 = data.items
221 last = _item_0[#_item_0]
222end
223local second_last
224do
225 local _item_0 = data.items
226 second_last = _item_0[#_item_0 - 1]
227end
180local mt = { } 228local mt = { }
181local add 229local add
182add = function(self, right) 230add = function(self, right)
@@ -307,6 +355,14 @@ func({
307 2, 355 2,
308 3 356 3
309}) 357})
358local f
359f = function()
360 return {
361 1,
362 2,
363 3
364 }
365end
310local tb = { 366local tb = {
311 name = "abc", 367 name = "abc",
312 values = { 368 values = {
@@ -547,6 +603,59 @@ end
547local two, four 603local two, four
548local _obj_0 = items 604local _obj_0 = items
549two, four = _obj_0[2], _obj_0[4] 605two, four = _obj_0[2], _obj_0[4]
606local orders = {
607 "first",
608 "second",
609 "third",
610 "fourth",
611 "last"
612}
613local first, bulk, last = orders[1], (function()
614 local _accum_0 = { }
615 local _len_0 = 1
616 local _max_0 = #orders + -2 + 1
617 for _index_0 = 2, _max_0 do
618 local _item_0 = orders[_index_0]
619 _accum_0[_len_0] = _item_0
620 _len_0 = _len_0 + 1
621 end
622 return _accum_0
623end)(), orders[#orders]
624print(first)
625print(bulk)
626print(last)
627local first, rest
628do
629 local _obj_0 = orders
630 first, rest = _obj_0[1], (function()
631 local _accum_0 = { }
632 local _len_0 = 1
633 local _max_0 = #_obj_0
634 for _index_0 = 2, _max_0 do
635 local _item_0 = _obj_0[_index_0]
636 _accum_0[_len_0] = _item_0
637 _len_0 = _len_0 + 1
638 end
639 return _accum_0
640 end)()
641end
642local start, last
643do
644 local _obj_0 = orders
645 start, last = (function()
646 local _accum_0 = { }
647 local _len_0 = 1
648 local _max_0 = #_obj_0 + -2 + 1
649 for _index_0 = 1, _max_0 do
650 local _item_0 = _obj_0[_index_0]
651 _accum_0[_len_0] = _item_0
652 _len_0 = _len_0 + 1
653 end
654 return _accum_0
655 end)(), _obj_0[#_obj_0]
656end
657local _obj_0 = orders
658first, last = _obj_0[1], _obj_0[#_obj_0]
550local tuples = { 659local tuples = {
551 { 660 {
552 "hello", 661 "hello",
@@ -648,6 +757,56 @@ end)
648if success then 757if success then
649 print(result) 758 print(result)
650end 759end
760local a, b, c
761do
762 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(function()
763 return func()
764 end)
765 if _ok_0 then
766 a, b, c = _ret_0, _ret_1, _ret_2
767 end
768end
769do
770 local _exp_0 = ((function()
771 return (function(_arg_0, ...)
772 local _ok_0 = _arg_0
773 if _ok_0 then
774 return ...
775 end
776 end)(pcall(function()
777 return func()
778 end))
779 end)())
780 if _exp_0 ~= nil then
781 a = _exp_0
782 else
783 a = "default"
784 end
785end
786f((function()
787 return (function(_arg_0, ...)
788 local _ok_0 = _arg_0
789 if _ok_0 then
790 return ...
791 end
792 end)(pcall(function()
793 return func()
794 end))
795end)())
796f((function()
797 return (function(_arg_0, ...)
798 local _ok_0 = _arg_0
799 if _ok_0 then
800 return ...
801 end
802 end)(xpcall(function()
803 print(123)
804 return func()
805 end, function(e)
806 print(e)
807 return e
808 end))
809end)())
651local a <const> = 123 810local a <const> = 123
652local _ <close> = setmetatable({ }, { 811local _ <close> = setmetatable({ }, {
653 __close = function() 812 __close = function()
@@ -657,10 +816,19 @@ local _ <close> = setmetatable({ }, {
657local a, b, c, d 816local a, b, c, d
658local _obj_0 = tb 817local _obj_0 = tb
659a, b, c, d = _obj_0.a, _obj_0.b, _obj_0[1], _obj_0[2] 818a, b, c, d = _obj_0.a, _obj_0.b, _obj_0[1], _obj_0[2]
819Constant = 123
660local some_string = "这是一个字符串\n 并包括一个æ¢è¡Œã€‚" 820local some_string = "这是一个字符串\n 并包括一个æ¢è¡Œã€‚"
661print("我有" .. tostring(math.random() * 100) .. "%的把æ¡ã€‚") 821print("我有" .. tostring(math.random() * 100) .. "%的把æ¡ã€‚")
662local integer = 1000000 822local integer = 1000000
663local hex = 0xEFBBBF 823local hex = 0xEFBBBF
824local binary = 19
825local str = "key: value\nlist:\n - item1\n - " .. tostring(expr)
826local fn
827fn = function()
828 local str = "foo:\n bar: baz"
829 return str
830end
831local str = "path: \"C:\\Program Files\\App\"\nnote: 'He said: \"" .. tostring(Hello) .. "!\"'"
664local my_function 832local my_function
665my_function = function() end 833my_function = function() end
666my_function() 834my_function()
@@ -854,11 +1022,10 @@ for i, item in ipairs(items) do
854 _len_0 = _len_0 + 1 1022 _len_0 = _len_0 + 1
855end 1023end
856doubled = _accum_0 1024doubled = _accum_0
857local iter = ipairs(items)
858local slice 1025local slice
859local _accum_0 = { } 1026local _accum_0 = { }
860local _len_0 = 1 1027local _len_0 = 1
861for i, item in iter do 1028for i, item in ipairs(items) do
862 if i > 1 and i < 3 then 1029 if i > 1 and i < 3 then
863 _accum_0[_len_0] = item 1030 _accum_0[_len_0] = item
864 _len_0 = _len_0 + 1 1031 _len_0 = _len_0 + 1
@@ -965,8 +1132,7 @@ local slice
965local _accum_0 = { } 1132local _accum_0 = { }
966local _len_0 = 1 1133local _len_0 = 1
967local _list_0 = items 1134local _list_0 = items
968local _max_0 = 5 1135for _index_0 = 1, 5 do
969for _index_0 = 1, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
970 local item = _list_0[_index_0] 1136 local item = _list_0[_index_0]
971 _accum_0[_len_0] = item 1137 _accum_0[_len_0] = item
972 _len_0 = _len_0 + 1 1138 _len_0 = _len_0 + 1
@@ -976,7 +1142,8 @@ local slice
976local _accum_0 = { } 1142local _accum_0 = { }
977local _len_0 = 1 1143local _len_0 = 1
978local _list_0 = items 1144local _list_0 = items
979for _index_0 = 2, #_list_0 do 1145local _max_0 = #_list_0
1146for _index_0 = 2, _max_0 do
980 local item = _list_0[_index_0] 1147 local item = _list_0[_index_0]
981 _accum_0[_len_0] = item 1148 _accum_0[_len_0] = item
982 _len_0 = _len_0 + 1 1149 _len_0 = _len_0 + 1
@@ -986,12 +1153,46 @@ local slice
986local _accum_0 = { } 1153local _accum_0 = { }
987local _len_0 = 1 1154local _len_0 = 1
988local _list_0 = items 1155local _list_0 = items
989for _index_0 = 1, #_list_0, 2 do 1156local _max_0 = #_list_0
1157for _index_0 = 1, _max_0, 2 do
990 local item = _list_0[_index_0] 1158 local item = _list_0[_index_0]
991 _accum_0[_len_0] = item 1159 _accum_0[_len_0] = item
992 _len_0 = _len_0 + 1 1160 _len_0 = _len_0 + 1
993end 1161end
994slice = _accum_0 1162slice = _accum_0
1163local slice
1164local _accum_0 = { }
1165local _len_0 = 1
1166local _list_0 = items
1167local _min_0 = #_list_0 + -4 + 1
1168local _max_0 = #_list_0 + -1 + 1
1169for _index_0 = _min_0, _max_0 do
1170 local item = _list_0[_index_0]
1171 _accum_0[_len_0] = item
1172 _len_0 = _len_0 + 1
1173end
1174slice = _accum_0
1175local reverse_slice
1176local _accum_0 = { }
1177local _len_0 = 1
1178local _list_0 = items
1179local _min_0 = #_list_0 + -1 + 1
1180for _index_0 = _min_0, 1, -1 do
1181 local item = _list_0[_index_0]
1182 _accum_0[_len_0] = item
1183 _len_0 = _len_0 + 1
1184end
1185reverse_slice = _accum_0
1186local sub_list
1187local _accum_0 = { }
1188local _len_0 = 1
1189local _list_0 = items
1190for _index_0 = 2, 4 do
1191 local _item_0 = _list_0[_index_0]
1192 _accum_0[_len_0] = _item_0
1193 _len_0 = _len_0 + 1
1194end
1195sub_list = _accum_0
995for i = 10, 20 do 1196for i = 10, 20 do
996 print(i) 1197 print(i)
997end 1198end
@@ -1002,8 +1203,7 @@ for key, value in pairs(object) do
1002 print(key, value) 1203 print(key, value)
1003end 1204end
1004local _list_0 = items 1205local _list_0 = items
1005local _max_0 = 4 1206for _index_0 = 2, 4 do
1006for _index_0 = 2, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
1007 local item = _list_0[_index_0] 1207 local item = _list_0[_index_0]
1008 print(item) 1208 print(item)
1009end 1209end
@@ -1021,12 +1221,24 @@ local _len_0 = 1
1021for i = 1, 20 do 1221for i = 1, 20 do
1022 if i % 2 == 0 then 1222 if i % 2 == 0 then
1023 _accum_0[_len_0] = i * 2 1223 _accum_0[_len_0] = i * 2
1224 _len_0 = _len_0 + 1
1024 else 1225 else
1025 _accum_0[_len_0] = i 1226 _accum_0[_len_0] = i
1227 _len_0 = _len_0 + 1
1026 end 1228 end
1027 _len_0 = _len_0 + 1
1028end 1229end
1029doubled_evens = _accum_0 1230doubled_evens = _accum_0
1231local first_large
1232local _accum_0
1233local _list_0 = numbers
1234for _index_0 = 1, #_list_0 do
1235 local n = _list_0[_index_0]
1236 if n > 10 then
1237 _accum_0 = n
1238 break
1239 end
1240end
1241first_large = _accum_0
1030local func_a 1242local func_a
1031func_a = function() 1243func_a = function()
1032 for i = 1, 10 do 1244 for i = 1, 10 do
@@ -1175,7 +1387,7 @@ if "Robert" == name then
1175elseif "Dan" == name or "Daniel" == name then 1387elseif "Dan" == name or "Daniel" == name then
1176 print("ä½ çš„å字是Dan") 1388 print("ä½ çš„å字是Dan")
1177else 1389else
1178 print("我ä¸çŸ¥é“ä½ çš„åå­—") 1390 print("我ä¸è®¤è¯†ä½ ï¼Œä½ çš„å字是" .. tostring(name))
1179end 1391end
1180local b = 1 1392local b = 1
1181local next_number 1393local next_number
@@ -1275,6 +1487,192 @@ if _tab_0 then
1275 end 1487 end
1276 print("Vec2 " .. tostring(x) .. ", " .. tostring(y)) 1488 print("Vec2 " .. tostring(x) .. ", " .. tostring(y))
1277end 1489end
1490local _exp_0 = tb
1491local _type_0 = type(_exp_0)
1492local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1493local _match_0 = false
1494if _tab_0 then
1495 if 1 == _exp_0[1] and 2 == _exp_0[2] and 3 == _exp_0[3] then
1496 _match_0 = true
1497 print("1, 2, 3")
1498 end
1499end
1500if not _match_0 then
1501 local _match_1 = false
1502 if _tab_0 then
1503 local b = _exp_0[2]
1504 if 1 == _exp_0[1] and b ~= nil and 3 == _exp_0[3] then
1505 _match_1 = true
1506 print("1, " .. tostring(b) .. ", 3")
1507 end
1508 end
1509 if not _match_1 then
1510 if _tab_0 then
1511 local b = _exp_0[3]
1512 if b == nil then
1513 b = 3
1514 end
1515 if 1 == _exp_0[1] and 2 == _exp_0[2] then
1516 print("1, 2, " .. tostring(b))
1517 end
1518 end
1519 end
1520end
1521local _exp_0 = tb
1522local _type_0 = type(_exp_0)
1523local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1524local _match_0 = false
1525if _tab_0 then
1526 local result = _exp_0.result
1527 if true == _exp_0.success and result ~= nil then
1528 _match_0 = true
1529 print("æˆåŠŸ", result)
1530 end
1531end
1532if not _match_0 then
1533 local _match_1 = false
1534 if _tab_0 then
1535 if false == _exp_0.success then
1536 _match_1 = true
1537 print("失败", result)
1538 end
1539 end
1540 if not _match_1 then
1541 print("无效值")
1542 end
1543end
1544local _exp_0 = tb
1545local _type_0 = type(_exp_0)
1546local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1547local _match_0 = false
1548if _tab_0 then
1549 local content
1550 do
1551 local _obj_0 = _exp_0.data
1552 local _type_1 = type(_obj_0)
1553 if "table" == _type_1 or "userdata" == _type_1 then
1554 content = _obj_0.content
1555 end
1556 end
1557 local _val_0
1558 do
1559 local _obj_0 = _exp_0.data
1560 if _obj_0 ~= nil then
1561 _val_0 = _obj_0.type
1562 end
1563 end
1564 if "success" == _val_0 and content ~= nil then
1565 _match_0 = true
1566 print("æˆåŠŸ", content)
1567 end
1568end
1569if not _match_0 then
1570 local _match_1 = false
1571 if _tab_0 then
1572 local content
1573 do
1574 local _obj_0 = _exp_0.data
1575 local _type_1 = type(_obj_0)
1576 if "table" == _type_1 or "userdata" == _type_1 then
1577 content = _obj_0.content
1578 end
1579 end
1580 local _val_0
1581 do
1582 local _obj_0 = _exp_0.data
1583 if _obj_0 ~= nil then
1584 _val_0 = _obj_0.type
1585 end
1586 end
1587 if "error" == _val_0 and content ~= nil then
1588 _match_1 = true
1589 print("失败", content)
1590 end
1591 end
1592 if not _match_1 then
1593 print("无效值")
1594 end
1595end
1596local _exp_0 = tb
1597local _type_0 = type(_exp_0)
1598local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1599if _tab_0 then
1600 local fourth = _exp_0[4]
1601 local _val_0
1602 do
1603 local _obj_0 = _exp_0[1]
1604 if _obj_0 ~= nil then
1605 _val_0 = _obj_0.a
1606 end
1607 end
1608 local _val_1
1609 do
1610 local _obj_0 = _exp_0[1]
1611 if _obj_0 ~= nil then
1612 _val_1 = _obj_0.b
1613 end
1614 end
1615 local _val_2
1616 do
1617 local _obj_0 = _exp_0[2]
1618 if _obj_0 ~= nil then
1619 _val_2 = _obj_0.a
1620 end
1621 end
1622 local _val_3
1623 do
1624 local _obj_0 = _exp_0[2]
1625 if _obj_0 ~= nil then
1626 _val_3 = _obj_0.b
1627 end
1628 end
1629 local _val_4
1630 do
1631 local _obj_0 = _exp_0[3]
1632 if _obj_0 ~= nil then
1633 _val_4 = _obj_0.a
1634 end
1635 end
1636 local _val_5
1637 do
1638 local _obj_0 = _exp_0[3]
1639 if _obj_0 ~= nil then
1640 _val_5 = _obj_0.b
1641 end
1642 end
1643 if 1 == _val_0 and 2 == _val_1 and 3 == _val_2 and 4 == _val_3 and 5 == _val_4 and 6 == _val_5 and fourth ~= nil then
1644 print("åŒ¹é…æˆåŠŸ", fourth)
1645 end
1646end
1647local segments = {
1648 "admin",
1649 "users",
1650 "logs",
1651 "view"
1652}
1653local _type_0 = type(segments)
1654local _tab_0 = "table" == _type_0 or "userdata" == _type_0
1655if _tab_0 then
1656 local groups
1657 do
1658 local _accum_0 = { }
1659 local _len_0 = 1
1660 local _max_0 = #segments + -3 + 1
1661 for _index_0 = 1, _max_0 do
1662 local _item_0 = segments[_index_0]
1663 _accum_0[_len_0] = _item_0
1664 _len_0 = _len_0 + 1
1665 end
1666 groups = _accum_0
1667 end
1668 local resource = segments[#segments - 1]
1669 local action = segments[#segments]
1670 if resource ~= nil and action ~= nil then
1671 print("Group:", groups)
1672 print("Resource:", resource)
1673 print("Action:", action)
1674 end
1675end
1278local Inventory 1676local Inventory
1279local _class_0 1677local _class_0
1280local _base_0 = { 1678local _base_0 = {
@@ -1931,6 +2329,10 @@ do
1931 _with_1["key-name"] = value 2329 _with_1["key-name"] = value
1932end 2330end
1933_with_0[#_with_0 + 1] = "abc" 2331_with_0[#_with_0 + 1] = "abc"
2332local _with_0 = obj
2333if _with_0 ~= nil then
2334 print(obj.name)
2335end
1934do 2336do
1935 local var = "hello" 2337 local var = "hello"
1936 print(var) 2338 print(var)
@@ -2021,6 +2423,38 @@ local inventory = {
2021 } 2423 }
2022 } 2424 }
2023} 2425}
2426local map
2427map = function(arr, action)
2428 local _accum_0 = { }
2429 local _len_0 = 1
2430 for _index_0 = 1, #arr do
2431 local item = arr[_index_0]
2432 _accum_0[_len_0] = action(item)
2433 _len_0 = _len_0 + 1
2434 end
2435 return _accum_0
2436end
2437local filter
2438filter = function(arr, cond)
2439 local _accum_0 = { }
2440 local _len_0 = 1
2441 for _index_0 = 1, #arr do
2442 local item = arr[_index_0]
2443 if cond(item) then
2444 _accum_0[_len_0] = item
2445 _len_0 = _len_0 + 1
2446 end
2447 end
2448 return _accum_0
2449end
2450local reduce
2451reduce = function(arr, init, action)
2452 for _index_0 = 1, #arr do
2453 local item = arr[_index_0]
2454 init = action(init, item)
2455 end
2456 return init
2457end
2024print(reduce(filter(map({ 2458print(reduce(filter(map({
2025 1, 2459 1,
2026 2, 2460 2,
@@ -2078,6 +2512,12 @@ end
2078print("yuescript") 2512print("yuescript")
2079print(3) 2513print(3)
2080print("有效的枚举类型:", "Static") 2514print("有效的枚举类型:", "Static")
2515do
2516 print(123, "hello")
2517end
2518do
2519 print(123, "hello")
2520end
2081if tb ~= nil then 2521if tb ~= nil then
2082 tb:func() 2522 tb:func()
2083end 2523end
@@ -2178,6 +2618,16 @@ for _key_0, _value_0 in pairs(b) do
2178 end 2618 end
2179end 2619end
2180merge = _tab_0 2620merge = _tab_0
2621local last
2622do
2623 local _item_0 = data.items
2624 last = _item_0[#_item_0]
2625end
2626local second_last
2627do
2628 local _item_0 = data.items
2629 second_last = _item_0[#_item_0 - 1]
2630end
2181local mt = { } 2631local mt = { }
2182local add 2632local add
2183add = function(self, right) 2633add = function(self, right)
@@ -2308,6 +2758,14 @@ func({
2308 2, 2758 2,
2309 3 2759 3
2310}) 2760})
2761local f
2762f = function()
2763 return {
2764 1,
2765 2,
2766 3
2767 }
2768end
2311local tb = { 2769local tb = {
2312 name = "abc", 2770 name = "abc",
2313 values = { 2771 values = {
@@ -2548,6 +3006,59 @@ end
2548local two, four 3006local two, four
2549local _obj_0 = items 3007local _obj_0 = items
2550two, four = _obj_0[2], _obj_0[4] 3008two, four = _obj_0[2], _obj_0[4]
3009local orders = {
3010 "first",
3011 "second",
3012 "third",
3013 "fourth",
3014 "last"
3015}
3016local first, bulk, last = orders[1], (function()
3017 local _accum_0 = { }
3018 local _len_0 = 1
3019 local _max_0 = #orders + -2 + 1
3020 for _index_0 = 2, _max_0 do
3021 local _item_0 = orders[_index_0]
3022 _accum_0[_len_0] = _item_0
3023 _len_0 = _len_0 + 1
3024 end
3025 return _accum_0
3026end)(), orders[#orders]
3027print(first)
3028print(bulk)
3029print(last)
3030local first, rest
3031do
3032 local _obj_0 = orders
3033 first, rest = _obj_0[1], (function()
3034 local _accum_0 = { }
3035 local _len_0 = 1
3036 local _max_0 = #_obj_0
3037 for _index_0 = 2, _max_0 do
3038 local _item_0 = _obj_0[_index_0]
3039 _accum_0[_len_0] = _item_0
3040 _len_0 = _len_0 + 1
3041 end
3042 return _accum_0
3043 end)()
3044end
3045local start, last
3046do
3047 local _obj_0 = orders
3048 start, last = (function()
3049 local _accum_0 = { }
3050 local _len_0 = 1
3051 local _max_0 = #_obj_0 + -2 + 1
3052 for _index_0 = 1, _max_0 do
3053 local _item_0 = _obj_0[_index_0]
3054 _accum_0[_len_0] = _item_0
3055 _len_0 = _len_0 + 1
3056 end
3057 return _accum_0
3058 end)(), _obj_0[#_obj_0]
3059end
3060local _obj_0 = orders
3061first, last = _obj_0[1], _obj_0[#_obj_0]
2551local tuples = { 3062local tuples = {
2552 { 3063 {
2553 "hello", 3064 "hello",
@@ -2649,6 +3160,56 @@ end)
2649if success then 3160if success then
2650 print(result) 3161 print(result)
2651end 3162end
3163local a, b, c
3164do
3165 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(function()
3166 return func()
3167 end)
3168 if _ok_0 then
3169 a, b, c = _ret_0, _ret_1, _ret_2
3170 end
3171end
3172do
3173 local _exp_0 = ((function()
3174 return (function(_arg_0, ...)
3175 local _ok_0 = _arg_0
3176 if _ok_0 then
3177 return ...
3178 end
3179 end)(pcall(function()
3180 return func()
3181 end))
3182 end)())
3183 if _exp_0 ~= nil then
3184 a = _exp_0
3185 else
3186 a = "default"
3187 end
3188end
3189f((function()
3190 return (function(_arg_0, ...)
3191 local _ok_0 = _arg_0
3192 if _ok_0 then
3193 return ...
3194 end
3195 end)(pcall(function()
3196 return func()
3197 end))
3198end)())
3199f((function()
3200 return (function(_arg_0, ...)
3201 local _ok_0 = _arg_0
3202 if _ok_0 then
3203 return ...
3204 end
3205 end)(xpcall(function()
3206 print(123)
3207 return func()
3208 end, function(e)
3209 print(e)
3210 return e
3211 end))
3212end)())
2652local a <const> = 123 3213local a <const> = 123
2653local _ <close> = setmetatable({ }, { 3214local _ <close> = setmetatable({ }, {
2654 __close = function() 3215 __close = function()
@@ -2658,10 +3219,19 @@ local _ <close> = setmetatable({ }, {
2658local a, b, c, d 3219local a, b, c, d
2659local _obj_0 = tb 3220local _obj_0 = tb
2660a, b, c, d = _obj_0.a, _obj_0.b, _obj_0[1], _obj_0[2] 3221a, b, c, d = _obj_0.a, _obj_0.b, _obj_0[1], _obj_0[2]
3222Constant = 123
2661local some_string = "这是一个字符串\n 并包括一个æ¢è¡Œã€‚" 3223local some_string = "这是一个字符串\n 并包括一个æ¢è¡Œã€‚"
2662print("我有" .. tostring(math.random() * 100) .. "%的把æ¡ã€‚") 3224print("我有" .. tostring(math.random() * 100) .. "%的把æ¡ã€‚")
2663local integer = 1000000 3225local integer = 1000000
2664local hex = 0xEFBBBF 3226local hex = 0xEFBBBF
3227local binary = 19
3228local str = "key: value\nlist:\n - item1\n - " .. tostring(expr)
3229local fn
3230fn = function()
3231 local str = "foo:\n bar: baz"
3232 return str
3233end
3234local str = "path: \"C:\\Program Files\\App\"\nnote: 'He said: \"" .. tostring(Hello) .. "!\"'"
2665local my_function 3235local my_function
2666my_function = function() end 3236my_function = function() end
2667my_function() 3237my_function()
@@ -2855,11 +3425,10 @@ for i, item in ipairs(items) do
2855 _len_0 = _len_0 + 1 3425 _len_0 = _len_0 + 1
2856end 3426end
2857doubled = _accum_0 3427doubled = _accum_0
2858local iter = ipairs(items)
2859local slice 3428local slice
2860local _accum_0 = { } 3429local _accum_0 = { }
2861local _len_0 = 1 3430local _len_0 = 1
2862for i, item in iter do 3431for i, item in ipairs(items) do
2863 if i > 1 and i < 3 then 3432 if i > 1 and i < 3 then
2864 _accum_0[_len_0] = item 3433 _accum_0[_len_0] = item
2865 _len_0 = _len_0 + 1 3434 _len_0 = _len_0 + 1
@@ -2966,8 +3535,7 @@ local slice
2966local _accum_0 = { } 3535local _accum_0 = { }
2967local _len_0 = 1 3536local _len_0 = 1
2968local _list_0 = items 3537local _list_0 = items
2969local _max_0 = 5 3538for _index_0 = 1, 5 do
2970for _index_0 = 1, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
2971 local item = _list_0[_index_0] 3539 local item = _list_0[_index_0]
2972 _accum_0[_len_0] = item 3540 _accum_0[_len_0] = item
2973 _len_0 = _len_0 + 1 3541 _len_0 = _len_0 + 1
@@ -2977,7 +3545,8 @@ local slice
2977local _accum_0 = { } 3545local _accum_0 = { }
2978local _len_0 = 1 3546local _len_0 = 1
2979local _list_0 = items 3547local _list_0 = items
2980for _index_0 = 2, #_list_0 do 3548local _max_0 = #_list_0
3549for _index_0 = 2, _max_0 do
2981 local item = _list_0[_index_0] 3550 local item = _list_0[_index_0]
2982 _accum_0[_len_0] = item 3551 _accum_0[_len_0] = item
2983 _len_0 = _len_0 + 1 3552 _len_0 = _len_0 + 1
@@ -2987,12 +3556,46 @@ local slice
2987local _accum_0 = { } 3556local _accum_0 = { }
2988local _len_0 = 1 3557local _len_0 = 1
2989local _list_0 = items 3558local _list_0 = items
2990for _index_0 = 1, #_list_0, 2 do 3559local _max_0 = #_list_0
3560for _index_0 = 1, _max_0, 2 do
2991 local item = _list_0[_index_0] 3561 local item = _list_0[_index_0]
2992 _accum_0[_len_0] = item 3562 _accum_0[_len_0] = item
2993 _len_0 = _len_0 + 1 3563 _len_0 = _len_0 + 1
2994end 3564end
2995slice = _accum_0 3565slice = _accum_0
3566local slice
3567local _accum_0 = { }
3568local _len_0 = 1
3569local _list_0 = items
3570local _min_0 = #_list_0 + -4 + 1
3571local _max_0 = #_list_0 + -1 + 1
3572for _index_0 = _min_0, _max_0 do
3573 local item = _list_0[_index_0]
3574 _accum_0[_len_0] = item
3575 _len_0 = _len_0 + 1
3576end
3577slice = _accum_0
3578local reverse_slice
3579local _accum_0 = { }
3580local _len_0 = 1
3581local _list_0 = items
3582local _min_0 = #_list_0 + -1 + 1
3583for _index_0 = _min_0, 1, -1 do
3584 local item = _list_0[_index_0]
3585 _accum_0[_len_0] = item
3586 _len_0 = _len_0 + 1
3587end
3588reverse_slice = _accum_0
3589local sub_list
3590local _accum_0 = { }
3591local _len_0 = 1
3592local _list_0 = items
3593for _index_0 = 2, 4 do
3594 local _item_0 = _list_0[_index_0]
3595 _accum_0[_len_0] = _item_0
3596 _len_0 = _len_0 + 1
3597end
3598sub_list = _accum_0
2996for i = 10, 20 do 3599for i = 10, 20 do
2997 print(i) 3600 print(i)
2998end 3601end
@@ -3003,8 +3606,7 @@ for key, value in pairs(object) do
3003 print(key, value) 3606 print(key, value)
3004end 3607end
3005local _list_0 = items 3608local _list_0 = items
3006local _max_0 = 4 3609for _index_0 = 2, 4 do
3007for _index_0 = 2, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
3008 local item = _list_0[_index_0] 3610 local item = _list_0[_index_0]
3009 print(item) 3611 print(item)
3010end 3612end
@@ -3022,12 +3624,24 @@ local _len_0 = 1
3022for i = 1, 20 do 3624for i = 1, 20 do
3023 if i % 2 == 0 then 3625 if i % 2 == 0 then
3024 _accum_0[_len_0] = i * 2 3626 _accum_0[_len_0] = i * 2
3627 _len_0 = _len_0 + 1
3025 else 3628 else
3026 _accum_0[_len_0] = i 3629 _accum_0[_len_0] = i
3630 _len_0 = _len_0 + 1
3027 end 3631 end
3028 _len_0 = _len_0 + 1
3029end 3632end
3030doubled_evens = _accum_0 3633doubled_evens = _accum_0
3634local first_large
3635local _accum_0
3636local _list_0 = numbers
3637for _index_0 = 1, #_list_0 do
3638 local n = _list_0[_index_0]
3639 if n > 10 then
3640 _accum_0 = n
3641 break
3642 end
3643end
3644first_large = _accum_0
3031local func_a 3645local func_a
3032func_a = function() 3646func_a = function()
3033 for i = 1, 10 do 3647 for i = 1, 10 do
@@ -3176,7 +3790,7 @@ if "Robert" == name then
3176elseif "Dan" == name or "Daniel" == name then 3790elseif "Dan" == name or "Daniel" == name then
3177 print("ä½ çš„å字是Dan") 3791 print("ä½ çš„å字是Dan")
3178else 3792else
3179 print("我ä¸çŸ¥é“ä½ çš„åå­—") 3793 print("我ä¸è®¤è¯†ä½ ï¼Œä½ çš„å字是" .. tostring(name))
3180end 3794end
3181local b = 1 3795local b = 1
3182local next_number 3796local next_number
@@ -3276,6 +3890,192 @@ if _tab_0 then
3276 end 3890 end
3277 print("Vec2 " .. tostring(x) .. ", " .. tostring(y)) 3891 print("Vec2 " .. tostring(x) .. ", " .. tostring(y))
3278end 3892end
3893local _exp_0 = tb
3894local _type_0 = type(_exp_0)
3895local _tab_0 = "table" == _type_0 or "userdata" == _type_0
3896local _match_0 = false
3897if _tab_0 then
3898 if 1 == _exp_0[1] and 2 == _exp_0[2] and 3 == _exp_0[3] then
3899 _match_0 = true
3900 print("1, 2, 3")
3901 end
3902end
3903if not _match_0 then
3904 local _match_1 = false
3905 if _tab_0 then
3906 local b = _exp_0[2]
3907 if 1 == _exp_0[1] and b ~= nil and 3 == _exp_0[3] then
3908 _match_1 = true
3909 print("1, " .. tostring(b) .. ", 3")
3910 end
3911 end
3912 if not _match_1 then
3913 if _tab_0 then
3914 local b = _exp_0[3]
3915 if b == nil then
3916 b = 3
3917 end
3918 if 1 == _exp_0[1] and 2 == _exp_0[2] then
3919 print("1, 2, " .. tostring(b))
3920 end
3921 end
3922 end
3923end
3924local _exp_0 = tb
3925local _type_0 = type(_exp_0)
3926local _tab_0 = "table" == _type_0 or "userdata" == _type_0
3927local _match_0 = false
3928if _tab_0 then
3929 local result = _exp_0.result
3930 if true == _exp_0.success and result ~= nil then
3931 _match_0 = true
3932 print("æˆåŠŸ", result)
3933 end
3934end
3935if not _match_0 then
3936 local _match_1 = false
3937 if _tab_0 then
3938 if false == _exp_0.success then
3939 _match_1 = true
3940 print("失败", result)
3941 end
3942 end
3943 if not _match_1 then
3944 print("无效值")
3945 end
3946end
3947local _exp_0 = tb
3948local _type_0 = type(_exp_0)
3949local _tab_0 = "table" == _type_0 or "userdata" == _type_0
3950local _match_0 = false
3951if _tab_0 then
3952 local content
3953 do
3954 local _obj_0 = _exp_0.data
3955 local _type_1 = type(_obj_0)
3956 if "table" == _type_1 or "userdata" == _type_1 then
3957 content = _obj_0.content
3958 end
3959 end
3960 local _val_0
3961 do
3962 local _obj_0 = _exp_0.data
3963 if _obj_0 ~= nil then
3964 _val_0 = _obj_0.type
3965 end
3966 end
3967 if "success" == _val_0 and content ~= nil then
3968 _match_0 = true
3969 print("æˆåŠŸ", content)
3970 end
3971end
3972if not _match_0 then
3973 local _match_1 = false
3974 if _tab_0 then
3975 local content
3976 do
3977 local _obj_0 = _exp_0.data
3978 local _type_1 = type(_obj_0)
3979 if "table" == _type_1 or "userdata" == _type_1 then
3980 content = _obj_0.content
3981 end
3982 end
3983 local _val_0
3984 do
3985 local _obj_0 = _exp_0.data
3986 if _obj_0 ~= nil then
3987 _val_0 = _obj_0.type
3988 end
3989 end
3990 if "error" == _val_0 and content ~= nil then
3991 _match_1 = true
3992 print("失败", content)
3993 end
3994 end
3995 if not _match_1 then
3996 print("无效值")
3997 end
3998end
3999local _exp_0 = tb
4000local _type_0 = type(_exp_0)
4001local _tab_0 = "table" == _type_0 or "userdata" == _type_0
4002if _tab_0 then
4003 local fourth = _exp_0[4]
4004 local _val_0
4005 do
4006 local _obj_0 = _exp_0[1]
4007 if _obj_0 ~= nil then
4008 _val_0 = _obj_0.a
4009 end
4010 end
4011 local _val_1
4012 do
4013 local _obj_0 = _exp_0[1]
4014 if _obj_0 ~= nil then
4015 _val_1 = _obj_0.b
4016 end
4017 end
4018 local _val_2
4019 do
4020 local _obj_0 = _exp_0[2]
4021 if _obj_0 ~= nil then
4022 _val_2 = _obj_0.a
4023 end
4024 end
4025 local _val_3
4026 do
4027 local _obj_0 = _exp_0[2]
4028 if _obj_0 ~= nil then
4029 _val_3 = _obj_0.b
4030 end
4031 end
4032 local _val_4
4033 do
4034 local _obj_0 = _exp_0[3]
4035 if _obj_0 ~= nil then
4036 _val_4 = _obj_0.a
4037 end
4038 end
4039 local _val_5
4040 do
4041 local _obj_0 = _exp_0[3]
4042 if _obj_0 ~= nil then
4043 _val_5 = _obj_0.b
4044 end
4045 end
4046 if 1 == _val_0 and 2 == _val_1 and 3 == _val_2 and 4 == _val_3 and 5 == _val_4 and 6 == _val_5 and fourth ~= nil then
4047 print("åŒ¹é…æˆåŠŸ", fourth)
4048 end
4049end
4050local segments = {
4051 "admin",
4052 "users",
4053 "logs",
4054 "view"
4055}
4056local _type_0 = type(segments)
4057local _tab_0 = "table" == _type_0 or "userdata" == _type_0
4058if _tab_0 then
4059 local groups
4060 do
4061 local _accum_0 = { }
4062 local _len_0 = 1
4063 local _max_0 = #segments + -3 + 1
4064 for _index_0 = 1, _max_0 do
4065 local _item_0 = segments[_index_0]
4066 _accum_0[_len_0] = _item_0
4067 _len_0 = _len_0 + 1
4068 end
4069 groups = _accum_0
4070 end
4071 local resource = segments[#segments - 1]
4072 local action = segments[#segments]
4073 if resource ~= nil and action ~= nil then
4074 print("Group:", groups)
4075 print("Resource:", resource)
4076 print("Action:", action)
4077 end
4078end
3279local Inventory 4079local Inventory
3280local _class_0 4080local _class_0
3281local _base_0 = { 4081local _base_0 = {
@@ -3932,6 +4732,10 @@ do
3932 _with_1["key-name"] = value 4732 _with_1["key-name"] = value
3933end 4733end
3934_with_0[#_with_0 + 1] = "abc" 4734_with_0[#_with_0 + 1] = "abc"
4735local _with_0 = obj
4736if _with_0 ~= nil then
4737 print(obj.name)
4738end
3935do 4739do
3936 local var = "hello" 4740 local var = "hello"
3937 print(var) 4741 print(var)
diff --git a/spec/outputs/comprehension.lua b/spec/outputs/comprehension.lua
index 9a7c478..663bd44 100644
--- a/spec/outputs/comprehension.lua
+++ b/spec/outputs/comprehension.lua
@@ -243,8 +243,11 @@ end
243do 243do
244 local _accum_0 = { } 244 local _accum_0 = { }
245 local _len_0 = 1 245 local _len_0 = 1
246 local _min_0 = 1 + 2
246 local _max_0 = 3 + 4 247 local _max_0 = 3 + 4
247 for _index_0 = 1 + 2, _max_0 < 0 and #items + _max_0 or _max_0 do 248 _min_0 = _min_0 < 0 and #items + _min_0 + 1 or _min_0
249 _max_0 = _max_0 < 0 and #items + _max_0 + 1 or _max_0
250 for _index_0 = _min_0, _max_0 do
248 local item = items[_index_0] 251 local item = items[_index_0]
249 _accum_0[_len_0] = item 252 _accum_0[_len_0] = item
250 _len_0 = _len_0 + 1 253 _len_0 = _len_0 + 1
@@ -254,8 +257,11 @@ end
254do 257do
255 local _accum_0 = { } 258 local _accum_0 = { }
256 local _len_0 = 1 259 local _len_0 = 1
260 local _min_0 = hello() * 4
257 local _max_0 = 2 - thing[4] 261 local _max_0 = 2 - thing[4]
258 for _index_0 = hello() * 4, _max_0 < 0 and #items + _max_0 or _max_0 do 262 _min_0 = _min_0 < 0 and #items + _min_0 + 1 or _min_0
263 _max_0 = _max_0 < 0 and #items + _max_0 + 1 or _max_0
264 for _index_0 = _min_0, _max_0 do
259 local item = items[_index_0] 265 local item = items[_index_0]
260 _accum_0[_len_0] = item 266 _accum_0[_len_0] = item
261 _len_0 = _len_0 + 1 267 _len_0 = _len_0 + 1
diff --git a/spec/outputs/destructure.lua b/spec/outputs/destructure.lua
index 44da58b..ba216b5 100644
--- a/spec/outputs/destructure.lua
+++ b/spec/outputs/destructure.lua
@@ -621,4 +621,94 @@ do
621 print(meta_field, abc, def) 621 print(meta_field, abc, def)
622 end 622 end
623end 623end
624do
625 local clients = {
626 "VIP_Alice",
627 "User_Bob",
628 "User_Clara",
629 "VIP_Eva"
630 }
631 local vipStart, regulars, vipEnd = clients[1], (function()
632 local _accum_0 = { }
633 local _len_0 = 1
634 local _max_0 = #clients + -2 + 1
635 for _index_0 = 2, _max_0 do
636 local _item_0 = clients[_index_0]
637 _accum_0[_len_0] = _item_0
638 _len_0 = _len_0 + 1
639 end
640 return _accum_0
641 end)(), clients[#clients]
642 print(vipStart)
643 print(regulars)
644 print(vipEnd)
645end
646do
647 local setupMeeting
648 setupMeeting = function(participants)
649 local chair, secretary = participants[1], participants[#participants]
650 return print(chair, secretary)
651 end
652 setupMeeting({
653 "Alice",
654 "Bob",
655 "Charlie",
656 "David"
657 })
658end
659do
660 local getTransactions
661 getTransactions = function()
662 return {
663 {
664 id = "T1",
665 amount = 100
666 },
667 {
668 id = "T2",
669 amount = 200
670 },
671 {
672 id = "T3",
673 amount = 300
674 }
675 }
676 end
677 local id, amount
678 do
679 local _item_0 = getTransactions()
680 local _obj_0 = _item_0[#_item_0]
681 id, amount = _obj_0.id, _obj_0.amount
682 end
683 assert(id == "T3")
684 assert(amount == 300)
685end
686do
687 local middle
688 local _accum_0 = { }
689 local _len_0 = 1
690 local _list_0 = tb
691 local _max_0 = #_list_0 + -2 + 1
692 for _index_0 = 2, _max_0 do
693 local _item_0 = _list_0[_index_0]
694 _accum_0[_len_0] = _item_0
695 _len_0 = _len_0 + 1
696 end
697 middle = _accum_0
698end
699do
700 local a, abc, b, def, sub, d, e
701 local _obj_0 = tb
702 a, abc, b, def, sub, d, e = _obj_0[1], _obj_0.abc, _obj_0[2], _obj_0.def, (function()
703 local _accum_0 = { }
704 local _len_0 = 1
705 local _max_0 = #_obj_0 + -3 + 1
706 for _index_0 = 3, _max_0 do
707 local _item_0 = _obj_0[_index_0]
708 _accum_0[_len_0] = _item_0
709 _len_0 = _len_0 + 1
710 end
711 return _accum_0
712 end)(), _obj_0[#_obj_0 - 1], _obj_0[#_obj_0]
713end
624return nil 714return nil
diff --git a/spec/outputs/do.lua b/spec/outputs/do.lua
index 6473e03..96d1022 100644
--- a/spec/outputs/do.lua
+++ b/spec/outputs/do.lua
@@ -32,10 +32,10 @@ local t = {
32} 32}
33return function(y, k) 33return function(y, k)
34 if y == nil then 34 if y == nil then
35 do 35 y = ((function()
36 x = 10 + 2 36 x = 10 + 2
37 y = x 37 return x
38 end 38 end)())
39 end 39 end
40 if k == nil then 40 if k == nil then
41 do 41 do
diff --git a/spec/outputs/global.lua b/spec/outputs/global.lua
index 54a21a9..3918f85 100644
--- a/spec/outputs/global.lua
+++ b/spec/outputs/global.lua
@@ -93,3 +93,28 @@ do
93 FooBar = "pascal case" 93 FooBar = "pascal case"
94 FOOBAR = "all uppercase" 94 FOOBAR = "all uppercase"
95end 95end
96do
97 do
98 local _class_0
99 local _base_0 = { }
100 if _base_0.__index == nil then
101 _base_0.__index = _base_0
102 end
103 _class_0 = setmetatable({
104 __init = function() end,
105 __base = _base_0,
106 __name = "A"
107 }, {
108 __index = _base_0,
109 __call = function(cls, ...)
110 local _self_0 = setmetatable({ }, _base_0)
111 cls.__init(_self_0, ...)
112 return _self_0
113 end
114 })
115 _base_0.__class = _class_0
116 A = _class_0
117 end
118 Flag = 1
119 const, x, y = "const", 1, 2
120end
diff --git a/spec/outputs/import.lua b/spec/outputs/import.lua
index 83c99e2..7aa130f 100644
--- a/spec/outputs/import.lua
+++ b/spec/outputs/import.lua
@@ -166,3 +166,13 @@ do
166 local _obj_1 = require("m") 166 local _obj_1 = require("m")
167 g, i = _obj_1[1], getmetatable(_obj_1[2]).__close 167 g, i = _obj_1[1], getmetatable(_obj_1[2]).__close
168end 168end
169do
170 local require <const> = require
171 local stringlib <const> = string
172 local format <const> = string.format
173 local io_read <const> = io.read
174 local type
175 type = function() end
176 local tp <const> = _G.type
177 local yue <const> = _G["月"]
178end
diff --git a/spec/outputs/lists.lua b/spec/outputs/lists.lua
index 48ec9c8..2ed7b95 100644
--- a/spec/outputs/lists.lua
+++ b/spec/outputs/lists.lua
@@ -230,31 +230,36 @@ x = {
230 6, 230 6,
231 7 231 7
232} 232}
233local _max_0 = -5 233local _max_0 = #x + -5 + 1
234for _index_0 = 2, _max_0 < 0 and #x + _max_0 or _max_0, 2 do 234for _index_0 = 2, _max_0, 2 do
235 local y = x[_index_0] 235 local y = x[_index_0]
236 print(y) 236 print(y)
237end 237end
238local _max_1 = 3 238for _index_0 = 1, 3 do
239for _index_0 = 1, _max_1 < 0 and #x + _max_1 or _max_1 do
240 local y = x[_index_0] 239 local y = x[_index_0]
241 print(y) 240 print(y)
242end 241end
243for _index_0 = 2, #x do 242local _max_1 = #x
243for _index_0 = 2, _max_1 do
244 local y = x[_index_0] 244 local y = x[_index_0]
245 print(y) 245 print(y)
246end 246end
247for _index_0 = 1, #x, 2 do 247local _max_2 = #x
248for _index_0 = 1, _max_2, 2 do
248 local y = x[_index_0] 249 local y = x[_index_0]
249 print(y) 250 print(y)
250end 251end
251for _index_0 = 2, #x, 2 do 252local _max_3 = #x
253for _index_0 = 2, _max_3, 2 do
252 local y = x[_index_0] 254 local y = x[_index_0]
253 print(y) 255 print(y)
254end 256end
255local a, b, c = 1, 5, 2 257local a, b, c = 1, 5, 2
256local _max_2 = b 258local _min_0 = a
257for _index_0 = a, _max_2 < 0 and #x + _max_2 or _max_2, c do 259local _max_4 = b
260_min_0 = _min_0 < 0 and #x + _min_0 + 1 or _min_0
261_max_4 = _max_4 < 0 and #x + _max_4 + 1 or _max_4
262for _index_0 = _min_0, _max_4, c do
258 local y = x[_index_0] 263 local y = x[_index_0]
259 print(y) 264 print(y)
260end 265end
@@ -287,7 +292,10 @@ do
287 a 292 a
288 }) 293 })
289 local _list_0 = f 294 local _list_0 = f
290 for _index_0 = a, #_list_0 do 295 local _min_1 = a
296 local _max_5 = #_list_0
297 _min_1 = _min_1 < 0 and #_list_0 + _min_1 + 1 or _min_1
298 for _index_0 = _min_1, _max_5 do
291 local v = _list_0[_index_0] 299 local v = _list_0[_index_0]
292 print(v) 300 print(v)
293 end 301 end
@@ -327,4 +335,191 @@ do
327 job = "jobless" 335 job = "jobless"
328 end 336 end
329end 337end
338do
339 local transactions = {
340 "T001",
341 "T002",
342 "T003",
343 "T004",
344 "T005"
345 }
346 local middleTransactions
347 do
348 local _accum_0 = { }
349 local _len_0 = 1
350 local _max_5 = #transactions + -2 + 1
351 for _index_0 = 2, _max_5 do
352 local _item_0 = transactions[_index_0]
353 _accum_0[_len_0] = _item_0
354 _len_0 = _len_0 + 1
355 end
356 middleTransactions = _accum_0
357 end
358 print(middleTransactions)
359end
360do
361 local logs = {
362 {
363 start = 0,
364 ["end"] = 100
365 },
366 {
367 start = 100,
368 ["end"] = 200
369 },
370 {
371 start = 200,
372 ["end"] = 123
373 }
374 }
375 print(logs[#logs]["end"])
376end
377do
378 local pendingOrders = {
379 "O001",
380 "O002",
381 "O003",
382 "O004"
383 }
384 print(pendingOrders[#pendingOrders - 1])
385end
386do
387 local getOrders
388 getOrders = function()
389 return {
390 {
391 id = "O1001",
392 status = "pending"
393 },
394 {
395 id = "O1002",
396 status = "processing"
397 },
398 {
399 id = "O1003",
400 status = "done"
401 }
402 }
403 end
404 local lastStatus
405 do
406 local _item_0 = getOrders()
407 lastStatus = _item_0[#_item_0].status
408 end
409 assert(lastStatus == "done")
410end
411do
412 local cloneList1
413 cloneList1 = function(list)
414 local _accum_0 = { }
415 local _len_0 = 1
416 local _max_5 = #list
417 for _index_0 = 1, _max_5 do
418 local _item_0 = list[_index_0]
419 _accum_0[_len_0] = _item_0
420 _len_0 = _len_0 + 1
421 end
422 return _accum_0
423 end
424 local cloneList2
425 cloneList2 = function(list)
426 local _tab_0 = { }
427 local _idx_0 = #_tab_0 + 1
428 for _index_0 = 1, #list do
429 local _value_0 = list[_index_0]
430 _tab_0[_idx_0] = _value_0
431 _idx_0 = _idx_0 + 1
432 end
433 return _tab_0
434 end
435 local cloneTable
436 cloneTable = function(tb)
437 local _tab_0 = { }
438 local _idx_0 = 1
439 for _key_0, _value_0 in pairs(tb) do
440 if _idx_0 == _key_0 then
441 _tab_0[#_tab_0 + 1] = _value_0
442 _idx_0 = _idx_0 + 1
443 else
444 _tab_0[_key_0] = _value_0
445 end
446 end
447 return _tab_0
448 end
449end
450do
451 print((function()
452 local _item_0 = globalTB
453 return _item_0[#_item_0]
454 end)(), (function()
455 local _item_0 = a.b.c
456 return _item_0[#_item_0 - 2]
457 end)(), (function()
458 if x ~= nil then
459 local _obj_0 = x.y
460 if _obj_0 ~= nil then
461 local _obj_1 = _obj_0(x).z
462 if _obj_1 ~= nil then
463 return _obj_1[#_obj_1 - 3]
464 end
465 return nil
466 end
467 return nil
468 end
469 return nil
470 end)())
471end
472local _anon_func_0 = function(globalTB)
473 local _item_0 = globalTB
474 local _call_0 = _item_0[#_item_0]
475 return _call_0["end"](_call_0, 123)
476end
477local _anon_func_1 = function(a)
478 local _item_0
479 do
480 local _accum_0 = { }
481 local _len_0 = 1
482 local _list_0 = a.b.c
483 local _max_5 = #_list_0 + -5 + 1
484 for _index_0 = 5, _max_5 do
485 local _item_1 = _list_0[_index_0]
486 _accum_0[_len_0] = _item_1
487 _len_0 = _len_0 + 1
488 end
489 _item_0 = _accum_0
490 end
491 return _item_0[#_item_0 - 2]
492end
493local _anon_func_2 = function(x)
494 if x ~= nil then
495 local _obj_0 = x.y
496 if _obj_0 ~= nil then
497 local _obj_1 = _obj_0(x).z
498 if _obj_1 ~= nil then
499 local _obj_2 = _obj_1[#_obj_1 - 3]
500 if _obj_2 ~= nil then
501 local _accum_0 = { }
502 local _len_0 = 1
503 local _max_5 = #_obj_2 + -3 + 1
504 for _index_0 = 1, _max_5 do
505 local _item_0 = _obj_2[_index_0]
506 _accum_0[_len_0] = _item_0
507 _len_0 = _len_0 + 1
508 end
509 return _accum_0
510 end
511 return nil
512 end
513 return nil
514 end
515 return nil
516 end
517 return nil
518end
519do
520 local f
521 f = function()
522 return print(_anon_func_0(globalTB), _anon_func_1(a), _anon_func_2(x))
523 end
524end
330return nil 525return nil
diff --git a/spec/outputs/literals.lua b/spec/outputs/literals.lua
index a578d58..6de5411 100644
--- a/spec/outputs/literals.lua
+++ b/spec/outputs/literals.lua
@@ -8,10 +8,18 @@ local _ = {
8 0xfF2323, 8 0xfF2323,
9 0xabcdef, 9 0xabcdef,
10 0xABCDEF, 10 0xABCDEF,
11 0XFBC400,
11 0x123p-123, 12 0x123p-123,
12 0xABCP+321, 13 0xABCP+321,
13 0x.1p-111, 14 0x.1p-111,
14 0xABCP-321, 15 0xABCP-321,
16 0x0.1E,
17 0xA23p-4,
18 0X1.921FB54442D18P+1,
19 1,
20 8,
21 15,
22 201,
15 .2323, 23 .2323,
16 .2323e-1, 24 .2323e-1,
17 .2323e13434, 25 .2323e13434,
diff --git a/spec/outputs/loops.lua b/spec/outputs/loops.lua
index 8624d49..6ab4bbb 100644
--- a/spec/outputs/loops.lua
+++ b/spec/outputs/loops.lua
@@ -60,8 +60,8 @@ do
60 local y = hello[_index_0] 60 local y = hello[_index_0]
61 if y % 2 == 0 then 61 if y % 2 == 0 then
62 _accum_0[_len_0] = y 62 _accum_0[_len_0] = y
63 _len_0 = _len_0 + 1
63 end 64 end
64 _len_0 = _len_0 + 1
65 end 65 end
66 x = _accum_0 66 x = _accum_0
67end 67end
@@ -132,13 +132,11 @@ do
132end 132end
133do 133do
134 local _accum_0 = { } 134 local _accum_0 = { }
135 local _len_0 = 1
136 local _list_2 = 3 135 local _list_2 = 3
137 for _index_0 = 1, #_list_2 do 136 for _index_0 = 1, #_list_2 do
138 local thing = _list_2[_index_0] 137 local thing = _list_2[_index_0]
139 y = "hello" 138 y = "hello"
140 break 139 break
141 _len_0 = _len_0 + 1
142 end 140 end
143 x = _accum_0 141 x = _accum_0
144end 142end
@@ -370,3 +368,131 @@ do
370 end 368 end
371 until false 369 until false
372end 370end
371local _anon_func_0 = function(i, tb)
372 local _accum_0 = { }
373 local _len_0 = 1
374 while tb[i] do
375 i = i + 1
376 _accum_0[_len_0] = i - 1
377 _len_0 = _len_0 + 1
378 end
379 return _accum_0
380end
381do
382 local index
383 do
384 local _accum_0
385 for i = 1, #tb do
386 if tb[i] then
387 _accum_0 = i
388 break
389 end
390 end
391 index = _accum_0
392 end
393 f((function()
394 local _accum_0
395 for i = 1, #tb do
396 if tb[i] then
397 _accum_0 = i
398 break
399 end
400 end
401 return _accum_0
402 end)())
403 f((function()
404 local _accum_0 = { }
405 local _len_0 = 1
406 for i = 1, #tb do
407 if tb[i] then
408 _accum_0[_len_0] = i
409 _len_0 = _len_0 + 1
410 end
411 end
412 return _accum_0
413 end)())
414 i = 1
415 local ids
416 do
417 local _accum_0 = { }
418 local _len_0 = 1
419 while tb[i] do
420 i = i + 1
421 _accum_0[_len_0] = i - 1
422 _len_0 = _len_0 + 1
423 end
424 ids = _accum_0
425 end
426 i = 1
427 local idx
428 do
429 local _accum_0
430 while tb[i] do
431 i = i + 1
432 _accum_0 = i - 1
433 break
434 end
435 idx = _accum_0
436 end
437 local f1
438 f1 = function()
439 i = 1
440 return f(_anon_func_0(i, tb))
441 end
442 i = 1
443 f((function()
444 local _accum_0
445 while tb[i] do
446 i = i + 1
447 _accum_0 = i - 1
448 break
449 end
450 return _accum_0
451 end)())
452 local _accum_0 = { }
453 local _len_0 = 1
454 local _list_3 = items
455 for _index_0 = 1, #_list_3 do
456 local item = _list_3[_index_0]
457 local _type_0 = type(item)
458 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
459 if _tab_0 then
460 local value = item.value
461 if "A" == item.type and value ~= nil then
462 if value > 5 then
463 _accum_0[_len_0] = item
464 _len_0 = _len_0 + 1
465 end
466 end
467 end
468 end
469 list = _accum_0
470end
471do
472 repeat
473 print(1)
474 until true
475 do
476 local _accum_0
477 repeat
478 a = func()
479 _accum_0 = a.x
480 break
481 until a.v
482 x = _accum_0
483 end
484 local items
485 local _accum_0 = { }
486 local _len_0 = 1
487 repeat
488 local item = getItem()
489 if not item then
490 break
491 end
492 if item.value > 0 then
493 _accum_0[_len_0] = item
494 _len_0 = _len_0 + 1
495 end
496 until false
497 items = _accum_0
498end
diff --git a/spec/outputs/macro.lua b/spec/outputs/macro.lua
index 4d31574..89c6e63 100644
--- a/spec/outputs/macro.lua
+++ b/spec/outputs/macro.lua
@@ -26,6 +26,10 @@ print({
26 123, 26 123,
27 'xyz' 27 'xyz'
28}) 28})
29print({
30 456,
31 'abc'
32})
29do 33do
30 assert(item == nil) 34 assert(item == nil)
31end 35end
@@ -213,6 +217,13 @@ function tb:func()
213end 217end
214end 218end
215print(x) 219print(x)
220local yue = require("yue")
221do
222local function f2(a)
223 return a + 1
224end
225x = x + f2(3)
226end
216local sel 227local sel
217sel = function(a, b, c) 228sel = function(a, b, c)
218 if a then 229 if a then
@@ -317,7 +328,7 @@ print((setmetatable({
317 return 998 328 return 998
318 end 329 end
319})) 330}))
320print("current line: " .. tostring(323)) 331print("current line: " .. tostring(349))
321do 332do
322 do 333 do
323-- TODO 334-- TODO
@@ -330,10 +341,8 @@ local _1
330_1 = function() 341_1 = function()
331 print(1) 342 print(1)
332 local _accum_0 = { } 343 local _accum_0 = { }
333 local _len_0 = 1
334 while false do 344 while false do
335 break 345 break
336 _len_0 = _len_0 + 1
337 end 346 end
338 return _accum_0 347 return _accum_0
339end 348end
diff --git a/spec/outputs/props.lua b/spec/outputs/props.lua
new file mode 100644
index 0000000..2c282e0
--- /dev/null
+++ b/spec/outputs/props.lua
@@ -0,0 +1,240 @@
1local Props
2do
3 local _class_0
4 local assignReadOnly
5 local _base_0 = {
6 __index = function(self, name)
7 local cls = getmetatable(self)
8 do
9 local item
10 do
11 local _obj_0 = cls.__getter
12 if _obj_0 ~= nil then
13 item = _obj_0[name]
14 end
15 end
16 if item then
17 return item(self)
18 else
19 item = rawget(cls, name)
20 if item then
21 return item
22 else
23 local c = cls
24 repeat
25 c = getmetatable(c)
26 if c then
27 local _obj_0 = c.__getter
28 if _obj_0 ~= nil then
29 item = _obj_0[name]
30 end
31 if item then
32 if cls.__getter == nil then
33 cls.__getter = { }
34 end
35 cls.__getter[name] = item
36 return item(self)
37 else
38 item = rawget(c, name)
39 if item then
40 rawset(cls, name, item)
41 return item
42 end
43 end
44 else
45 break
46 end
47 until false
48 end
49 end
50 end
51 return nil
52 end,
53 __newindex = function(self, name, value)
54 local cls = getmetatable(self)
55 local item
56 local _obj_0 = cls.__setter
57 if _obj_0 ~= nil then
58 item = _obj_0[name]
59 end
60 if item then
61 return item(self, value)
62 else
63 local c = cls
64 repeat
65 c = getmetatable(c)
66 if c then
67 local _obj_1 = c.__setter
68 if _obj_1 ~= nil then
69 item = _obj_1[name]
70 end
71 if item then
72 if cls.__setter == nil then
73 cls.__setter = { }
74 end
75 cls.__setter[name] = item
76 item(self, value)
77 return
78 end
79 else
80 break
81 end
82 until false
83 return rawset(self, name, value)
84 end
85 end,
86 prop = function(self, name, props)
87 local get, set = props.get, props.set
88 if set == nil then
89 set = assignReadOnly
90 end
91 do
92 local getter = rawget(self.__base, "__getter")
93 if getter then
94 getter[name] = get
95 else
96 rawset(self.__base, "__getter", {
97 [name] = get
98 })
99 end
100 end
101 local setter = rawget(self.__base, "__setter")
102 if setter then
103 setter[name] = set
104 else
105 return rawset(self.__base, "__setter", {
106 [name] = set
107 })
108 end
109 end
110 }
111 if _base_0.__index == nil then
112 _base_0.__index = _base_0
113 end
114 _class_0 = setmetatable({
115 __init = function() end,
116 __base = _base_0,
117 __name = "Props"
118 }, {
119 __index = _base_0,
120 __call = function(cls, ...)
121 local _self_0 = setmetatable({ }, _base_0)
122 cls.__init(_self_0, ...)
123 return _self_0
124 end
125 })
126 _base_0.__class = _class_0
127 local self = _class_0;
128 assignReadOnly = function()
129 return error("assigning a readonly property")
130 end
131 Props = _class_0
132end
133local A
134do
135 local _class_0
136 local _parent_0 = Props
137 local _base_0 = { }
138 for _key_0, _val_0 in pairs(_parent_0.__base) do
139 if _base_0[_key_0] == nil and _key_0:match("^__") and not (_key_0 == "__index" and _val_0 == _parent_0.__base) then
140 _base_0[_key_0] = _val_0
141 end
142 end
143 if _base_0.__index == nil then
144 _base_0.__index = _base_0
145 end
146 setmetatable(_base_0, _parent_0.__base)
147 _class_0 = setmetatable({
148 __init = function(self)
149 self._x = 0
150 end,
151 __base = _base_0,
152 __name = "A",
153 __parent = _parent_0
154 }, {
155 __index = function(cls, name)
156 local val = rawget(_base_0, name)
157 if val == nil then
158 local parent = rawget(cls, "__parent")
159 if parent then
160 return parent[name]
161 end
162 else
163 return val
164 end
165 end,
166 __call = function(cls, ...)
167 local _self_0 = setmetatable({ }, _base_0)
168 cls.__init(_self_0, ...)
169 return _self_0
170 end
171 })
172 _base_0.__class = _class_0
173 local self = _class_0;
174 self:prop('x', {
175 get = function(self)
176 return self._x + 1000
177 end,
178 set = function(self, v)
179 self._x = v
180 end
181 })
182 if _parent_0.__inherited then
183 _parent_0.__inherited(_parent_0, _class_0)
184 end
185 A = _class_0
186end
187local B
188do
189 local _class_0
190 local _parent_0 = A
191 local _base_0 = { }
192 for _key_0, _val_0 in pairs(_parent_0.__base) do
193 if _base_0[_key_0] == nil and _key_0:match("^__") and not (_key_0 == "__index" and _val_0 == _parent_0.__base) then
194 _base_0[_key_0] = _val_0
195 end
196 end
197 if _base_0.__index == nil then
198 _base_0.__index = _base_0
199 end
200 setmetatable(_base_0, _parent_0.__base)
201 _class_0 = setmetatable({
202 __init = function(self, ...)
203 return _class_0.__parent.__init(self, ...)
204 end,
205 __base = _base_0,
206 __name = "B",
207 __parent = _parent_0
208 }, {
209 __index = function(cls, name)
210 local val = rawget(_base_0, name)
211 if val == nil then
212 local parent = rawget(cls, "__parent")
213 if parent then
214 return parent[name]
215 end
216 else
217 return val
218 end
219 end,
220 __call = function(cls, ...)
221 local _self_0 = setmetatable({ }, _base_0)
222 cls.__init(_self_0, ...)
223 return _self_0
224 end
225 })
226 _base_0.__class = _class_0
227 local self = _class_0;
228 self:prop('abc', {
229 get = function(self)
230 return "hello"
231 end
232 })
233 if _parent_0.__inherited then
234 _parent_0.__inherited(_parent_0, _class_0)
235 end
236 B = _class_0
237end
238local b = B()
239b.x = 999
240return print(b.x, b.abc)
diff --git a/spec/outputs/string.lua b/spec/outputs/string.lua
index febea62..b536e6d 100644
--- a/spec/outputs/string.lua
+++ b/spec/outputs/string.lua
@@ -1,3 +1,4 @@
1local _module_0 = { }
1local hi = "hello" 2local hi = "hello"
2local hello = "what the heckyes" 3local hello = "what the heckyes"
3print(hi) 4print(hi)
@@ -41,4 +42,42 @@ local _ = "hello";
41("hello"):format().hello(1, 2, 3); 42("hello"):format().hello(1, 2, 3);
42("hello"):format(1, 2, 3) 43("hello"):format(1, 2, 3)
43something("hello"):world() 44something("hello"):world()
44return something(("hello"):world()) 45something(("hello"):world())
46do
47 local str = "key: value"
48 str = "config:\n\tenabled: true\n\tlevel: 5"
49 str = "header: start\nfooter: end"
50 str = "name: " .. tostring(username)
51 str = "count: " .. tostring(total) .. " items"
52 str = "user: " .. tostring(name) .. "\nid: " .. tostring(id)
53 str = "path: \"C:\\\\Program Files\\\\App\"\ndesc: 'single \"quote\" test'"
54 str = "key: value \nnext: 123 "
55 str = "list:\n - \"one\"\n - \"two\""
56 str = "-- comment\ncontent text\n-- comment"
57 str = tostring(1 + 2) .. '\n' .. tostring(2 + 3) .. '\n' .. tostring("a" .. "b")
58 local obj = {
59 settings = "mode: " .. tostring(mode) .. "\nflags:\n\t- " .. tostring(flag1) .. "\n\t- default"
60 }
61 local fn
62 fn = function()
63 return "Hello\nname: " .. tostring(userName)
64 end
65 str = "result:\n\tstatus: " .. tostring((function()
66 if ok then
67 return "pass"
68 else
69 return "fail"
70 end
71 end)()) .. "\n\tcode: " .. tostring(code)
72 local summary = "date: " .. tostring(os.date()) .. "\nvalues:\n\t-\n\t\ta: " .. tostring(aVal) .. "\n\t\tb: " .. tostring(bVal or defaultB)
73 local msg = send("Hello, " .. tostring(user) .. "!\nToday is " .. tostring(os.date("%A")) .. ".")
74 local desc
75 do
76 local prefix = "Result"
77 desc = tostring(prefix) .. ":\nvalue: " .. tostring(compute())
78 end
79 print(("1\n2\n3"))
80end
81local yaml = "version: " .. tostring(ver) .. "\nok: true"
82_module_0["yaml"] = yaml
83return _module_0
diff --git a/spec/outputs/switch.lua b/spec/outputs/switch.lua
index e4dedc9..7c1004b 100644
--- a/spec/outputs/switch.lua
+++ b/spec/outputs/switch.lua
@@ -415,4 +415,366 @@ do
415 end 415 end
416 end 416 end
417end 417end
418do
419 local _exp_0 = tb
420 local _type_0 = type(_exp_0)
421 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
422 local _match_0 = false
423 if _tab_0 then
424 if 1 == _exp_0[1] and 2 == _exp_0[2] and 3 == _exp_0[3] then
425 _match_0 = true
426 print("1, 2, 3")
427 end
428 end
429 if not _match_0 then
430 local _match_1 = false
431 if _tab_0 then
432 local b = _exp_0[2]
433 if 1 == _exp_0[1] and b ~= nil and 3 == _exp_0[3] then
434 _match_1 = true
435 print("1, " .. tostring(b) .. ", 3")
436 end
437 end
438 if not _match_1 then
439 if _tab_0 then
440 local b = _exp_0[3]
441 if b == nil then
442 b = 3
443 end
444 if 1 == _exp_0[1] and 2 == _exp_0[2] then
445 print("1, 2, " .. tostring(b))
446 end
447 end
448 end
449 end
450end
451do
452 local _exp_0 = tb
453 local _type_0 = type(_exp_0)
454 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
455 local _match_0 = false
456 if _tab_0 then
457 local result = _exp_0.result
458 if true == _exp_0.success and result ~= nil then
459 _match_0 = true
460 print("success", result)
461 end
462 end
463 if not _match_0 then
464 local _match_1 = false
465 if _tab_0 then
466 if false == _exp_0.success then
467 _match_1 = true
468 print("failed", result)
469 end
470 end
471 if not _match_1 then
472 print("invalid")
473 end
474 end
475end
476do
477 local _exp_0 = tb
478 local _type_0 = type(_exp_0)
479 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
480 local _match_0 = false
481 if _tab_0 then
482 local content = _exp_0.content
483 if "success" == _exp_0.type and content ~= nil then
484 _match_0 = true
485 print("success", content)
486 end
487 end
488 if not _match_0 then
489 local _match_1 = false
490 if _tab_0 then
491 local content = _exp_0.content
492 if "error" == _exp_0.type and content ~= nil then
493 _match_1 = true
494 print("failed", content)
495 end
496 end
497 if not _match_1 then
498 print("invalid")
499 end
500 end
501end
502do
503 do
504 local _exp_0 = tb
505 local _type_0 = type(_exp_0)
506 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
507 if _tab_0 then
508 local fourth = _exp_0[4]
509 local _val_0
510 do
511 local _obj_0 = _exp_0[1]
512 if _obj_0 ~= nil then
513 _val_0 = _obj_0.a
514 end
515 end
516 local _val_1
517 do
518 local _obj_0 = _exp_0[1]
519 if _obj_0 ~= nil then
520 _val_1 = _obj_0.b
521 end
522 end
523 local _val_2
524 do
525 local _obj_0 = _exp_0[2]
526 if _obj_0 ~= nil then
527 _val_2 = _obj_0.a
528 end
529 end
530 local _val_3
531 do
532 local _obj_0 = _exp_0[2]
533 if _obj_0 ~= nil then
534 _val_3 = _obj_0.b
535 end
536 end
537 local _val_4
538 do
539 local _obj_0 = _exp_0[3]
540 if _obj_0 ~= nil then
541 _val_4 = _obj_0.a
542 end
543 end
544 local _val_5
545 do
546 local _obj_0 = _exp_0[3]
547 if _obj_0 ~= nil then
548 _val_5 = _obj_0.b
549 end
550 end
551 if 1 == _val_0 and 2 == _val_1 and 3 == _val_2 and 4 == _val_3 and 5 == _val_4 and 6 == _val_5 and fourth ~= nil then
552 print("matched", fourth)
553 end
554 end
555 end
556 local _exp_0 = tb
557 local _type_0 = type(_exp_0)
558 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
559 local _match_0 = false
560 if _tab_0 then
561 local _val_0
562 do
563 local _obj_0 = _exp_0[1]
564 if _obj_0 ~= nil then
565 _val_0 = _obj_0.c
566 end
567 end
568 local _val_1
569 do
570 local _obj_0 = _exp_0[1]
571 if _obj_0 ~= nil then
572 _val_1 = _obj_0.d
573 end
574 end
575 local _val_2
576 do
577 local _obj_0 = _exp_0[2]
578 if _obj_0 ~= nil then
579 _val_2 = _obj_0.c
580 end
581 end
582 local _val_3
583 do
584 local _obj_0 = _exp_0[2]
585 if _obj_0 ~= nil then
586 _val_3 = _obj_0.d
587 end
588 end
589 local _val_4
590 do
591 local _obj_0 = _exp_0[3]
592 if _obj_0 ~= nil then
593 _val_4 = _obj_0.c
594 end
595 end
596 local _val_5
597 do
598 local _obj_0 = _exp_0[3]
599 if _obj_0 ~= nil then
600 _val_5 = _obj_0.d
601 end
602 end
603 if 1 == _val_0 and 2 == _val_1 and 3 == _val_2 and 4 == _val_3 and 5 == _val_4 and 6 == _val_5 then
604 _match_0 = true
605 print("OK")
606 end
607 end
608 if not _match_0 then
609 if _tab_0 then
610 local sixth = _exp_0[6]
611 local _val_0
612 do
613 local _obj_0 = _exp_0[3]
614 if _obj_0 ~= nil then
615 _val_0 = _obj_0.a
616 end
617 end
618 local _val_1
619 do
620 local _obj_0 = _exp_0[3]
621 if _obj_0 ~= nil then
622 _val_1 = _obj_0.b
623 end
624 end
625 local _val_2
626 do
627 local _obj_0 = _exp_0[4]
628 if _obj_0 ~= nil then
629 _val_2 = _obj_0.a
630 end
631 end
632 local _val_3
633 do
634 local _obj_0 = _exp_0[4]
635 if _obj_0 ~= nil then
636 _val_3 = _obj_0.b
637 end
638 end
639 local _val_4
640 do
641 local _obj_0 = _exp_0[5]
642 if _obj_0 ~= nil then
643 _val_4 = _obj_0.a
644 end
645 end
646 local _val_5
647 do
648 local _obj_0 = _exp_0[5]
649 if _obj_0 ~= nil then
650 _val_5 = _obj_0.b
651 end
652 end
653 if 1 == _val_0 and 2 == _val_1 and 3 == _val_2 and 4 == _val_3 and 5 == _val_4 and 6 == _val_5 and sixth ~= nil then
654 print("matched", sixth)
655 end
656 end
657 end
658end
659do
660 local v = "hello"
661 if "hello" == v then
662 print("matched hello")
663 else
664 print("not matched")
665 end
666end
667do
668 local f
669 f = function()
670 return "ok"
671 end
672 local val = f()
673 if "ok" == val then
674 print("it's ok")
675 end
676end
677do
678 local g
679 g = function()
680 return 42
681 end
682 local result = g()
683 if 1 == result or 2 == result then
684 print("small")
685 elseif 42 == result then
686 print("life universe everything")
687 else
688 print("other " .. tostring(result))
689 end
690end
691do
692 local check
693 check = function()
694 if true then
695 return "yes"
696 else
697 return "no"
698 end
699 end
700 local x = check()
701 if "yes" == x then
702 print("affirmative")
703 else
704 print("negative")
705 end
706end
707do
708 local t
709 t = function()
710 local tb = {
711 a = 1
712 }
713 tb.a = 2
714 return tb
715 end
716 local data = t()
717 local _type_0 = type(data)
718 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
719 local _match_0 = false
720 if _tab_0 then
721 if 2 == data.a then
722 _match_0 = true
723 print("matched")
724 end
725 end
726 if not _match_0 then
727 print("not matched")
728 end
729end
730do
731 local clientData = {
732 "Meta",
733 "CUST_1001",
734 "CHK123"
735 }
736 local _type_0 = type(clientData)
737 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
738 if _tab_0 then
739 local metadata
740 do
741 local _accum_0 = { }
742 local _len_0 = 1
743 local _max_0 = #clientData + -3 + 1
744 for _index_0 = 1, _max_0 do
745 local _item_0 = clientData[_index_0]
746 _accum_0[_len_0] = _item_0
747 _len_0 = _len_0 + 1
748 end
749 metadata = _accum_0
750 end
751 local customerId = clientData[#clientData - 1]
752 local checksum = clientData[#clientData]
753 if customerId ~= nil and checksum ~= nil then
754 print(metadata)
755 print(customerId)
756 print(checksum)
757 end
758 end
759end
760do
761 local handlePath
762 handlePath = function(segments)
763 local _type_0 = type(segments)
764 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
765 if _tab_0 then
766 local resource = segments[#segments - 1]
767 local action = segments[#segments]
768 if resource ~= nil and action ~= nil then
769 print("Resource:", resource)
770 return print("Action:", action)
771 end
772 end
773 end
774 handlePath({
775 "admin",
776 "logs",
777 "view"
778 })
779end
418return nil 780return nil
diff --git a/spec/outputs/syntax.lua b/spec/outputs/syntax.lua
index 5fd1821..040a325 100644
--- a/spec/outputs/syntax.lua
+++ b/spec/outputs/syntax.lua
@@ -239,9 +239,11 @@ x = 0
239local _list_0 = values 239local _list_0 = values
240for _index_0 = 1, #_list_0 do 240for _index_0 = 1, #_list_0 do
241 local v = _list_0[_index_0] 241 local v = _list_0[_index_0]
242 if ntype(v) == "fndef" then 242 _ = ((function()
243 _ = x + 1 243 if ntype(v) == "fndef" then
244 end 244 return x + 1
245 end
246 end)())
245end 247end
246hello = { 248hello = {
247 something = world, 249 something = world,
diff --git a/spec/outputs/tables.lua b/spec/outputs/tables.lua
index f358811..3f851de 100644
--- a/spec/outputs/tables.lua
+++ b/spec/outputs/tables.lua
@@ -366,6 +366,28 @@ local menus = {
366 } 366 }
367 } 367 }
368} 368}
369_ = {
370 boolean = {
371 true,
372 false
373 },
374 float = {
375 3.14,
376 -6.8523015e+5
377 },
378 int = {
379 123,
380 -685230
381 },
382 null = {
383 nodeName = 'node',
384 parent = nil
385 },
386 string = {
387 'Hello world',
388 "newline\nnewline2"
389 }
390}
369local tb 391local tb
370do 392do
371 local _tab_0 = { } 393 local _tab_0 = { }
diff --git a/spec/outputs/try_catch.lua b/spec/outputs/try_catch.lua
index d4c80c1..edb2341 100644
--- a/spec/outputs/try_catch.lua
+++ b/spec/outputs/try_catch.lua
@@ -8,10 +8,10 @@ local _anon_func_2 = function(tb)
8 return tb.func() 8 return tb.func()
9end 9end
10local _anon_func_3 = function(tb) 10local _anon_func_3 = function(tb)
11 return tb.func() 11 return (tb.func())
12end 12end
13local _anon_func_4 = function(tb) 13local _anon_func_4 = function(tb)
14 return tb:func(1, 2, 3) 14 return (tb:func(1, 2, 3))
15end 15end
16local _anon_func_5 = function(tb) 16local _anon_func_5 = function(tb)
17 return tb.func(1) 17 return tb.func(1)
@@ -22,6 +22,44 @@ end
22local _anon_func_7 = function(a, b, c, tb) 22local _anon_func_7 = function(a, b, c, tb)
23 return tb.f(a, b, c) 23 return tb.f(a, b, c)
24end 24end
25local _anon_func_8 = function(_arg_0, ...)
26 local ok = _arg_0
27 return ...
28end
29local _anon_func_10 = function(_arg_0, ...)
30 local _ok_0 = _arg_0
31 if _ok_0 then
32 return ...
33 end
34end
35local _anon_func_9 = function(func, pcall)
36 return _anon_func_10(pcall(func))
37end
38local _anon_func_12 = function(_arg_0, ...)
39 local _ok_0 = _arg_0
40 if _ok_0 then
41 return ...
42 end
43end
44local _anon_func_11 = function(func, pcall)
45 return _anon_func_12(pcall(func))
46end
47local _anon_func_14 = function(_arg_0, ...)
48 local _ok_0 = _arg_0
49 if _ok_0 then
50 return ...
51 end
52end
53local _anon_func_15 = function(func, print)
54 print(123)
55 return func()
56end
57local _anon_func_13 = function(func, print, xpcall)
58 return _anon_func_14(xpcall(_anon_func_15, function(e)
59 print(e)
60 return e
61 end, func, print))
62end
25local f 63local f
26f = function() 64f = function()
27 xpcall(function() 65 xpcall(function()
@@ -64,7 +102,7 @@ f = function()
64 print("OK") 102 print("OK")
65 end 103 end
66 if xpcall(function() 104 if xpcall(function()
67 return func(1) 105 return (func(1))
68 end, function(err) 106 end, function(err)
69 return print(err) 107 return print(err)
70 end) then 108 end) then
@@ -104,10 +142,236 @@ f = function()
104 do 142 do
105 x(function() 143 x(function()
106 local tb, a, b, c 144 local tb, a, b, c
107 f = function() 145 local f1
146 f1 = function()
108 return pcall(_anon_func_7, a, b, c, tb) 147 return pcall(_anon_func_7, a, b, c, tb)
109 end 148 end
110 end) 149 end)
111 end 150 end
151 do
152 local f1
153 f1 = function()
154 do
155 return _anon_func_8(pcall(function()
156 return func()
157 end))
158 end
159 end
160 end
161 do
162 local func
163 local a, b, c
164 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(func)
165 if _ok_0 then
166 a, b, c = _ret_0, _ret_1, _ret_2
167 end
168 end
169 do
170 local a, b, c
171 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(function()
172 return func()
173 end)
174 if _ok_0 then
175 a, b, c = _ret_0, _ret_1, _ret_2
176 end
177 end
178 do
179 local a
180 local _exp_0 = (_anon_func_9(func, pcall))
181 if _exp_0 ~= nil then
182 a = _exp_0
183 else
184 a = "default"
185 end
186 end
187 do
188 f(_anon_func_11(func, pcall))
189 end
190 do
191 f(_anon_func_13(func, print, xpcall))
192 end
112 return nil 193 return nil
113end 194end
195local _anon_func_16 = function(a, b, c, tb)
196 return tb.f(a, b, c)
197end
198local _anon_func_17 = function(_arg_0, ...)
199 local ok = _arg_0
200 return ...
201end
202do
203 xpcall(function()
204 return func(1, 2, 3)
205 end, function(err)
206 return print(err)
207 end)
208 xpcall(function()
209 return func(1, 2, 3)
210 end, function(err)
211 return print(err)
212 end)
213 pcall(function()
214 print("trying")
215 return func(1, 2, 3)
216 end)
217 do
218 local success, result = xpcall(function()
219 return func(1, 2, 3)
220 end, function(err)
221 return print(err)
222 end)
223 success, result = pcall(function()
224 return func(1, 2, 3)
225 end)
226 end
227 local tb = { }
228 pcall(function()
229 return tb.func
230 end)
231 pcall(function()
232 return tb.func()
233 end)
234 pcall(function()
235 return tb.func()
236 end)
237 pcall(function()
238 return (tb.func())
239 end)
240 pcall(function()
241 return (tb:func(1, 2, 3))
242 end)
243 pcall(function()
244 return tb.func(1)
245 end)
246 pcall(function()
247 return tb.func(1)
248 end)
249 if (xpcall(function()
250 return func(1)
251 end, function(err)
252 return print(err)
253 end)) then
254 print("OK")
255 end
256 if xpcall(function()
257 return (func(1))
258 end, function(err)
259 return print(err)
260 end) then
261 print("OK")
262 end
263 do
264 do
265 local success, result = pcall(function()
266 return func("abc", 123)
267 end)
268 if success then
269 print(result)
270 end
271 end
272 local success, result = xpcall(function()
273 return func("abc", 123)
274 end, function(err)
275 return print(err)
276 end)
277 success, result = xpcall(function()
278 return func("abc", 123)
279 end, function(err)
280 return print(err)
281 end)
282 if success then
283 print(result)
284 end
285 end
286 do
287 pcall(function()
288 return func(1, 2, 3)
289 end)
290 pcall(function()
291 return func(1, 2, 3)
292 end)
293 end
294 do
295 x(function()
296 local tb, a, b, c
297 local f1
298 f1 = function()
299 return pcall(_anon_func_16, a, b, c, tb)
300 end
301 end)
302 end
303 do
304 local f1
305 f1 = function()
306 do
307 return _anon_func_17(pcall(function()
308 return func()
309 end))
310 end
311 end
312 end
313 do
314 local func
315 local a, b, c
316 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(func)
317 if _ok_0 then
318 a, b, c = _ret_0, _ret_1, _ret_2
319 end
320 end
321 do
322 local a, b, c
323 local _ok_0, _ret_0, _ret_1, _ret_2 = pcall(function()
324 return func()
325 end)
326 if _ok_0 then
327 a, b, c = _ret_0, _ret_1, _ret_2
328 end
329 end
330 do
331 local a
332 local _exp_0 = ((function()
333 return (function(_arg_0, ...)
334 local _ok_0 = _arg_0
335 if _ok_0 then
336 return ...
337 end
338 end)(pcall(function()
339 return func()
340 end))
341 end)())
342 if _exp_0 ~= nil then
343 a = _exp_0
344 else
345 a = "default"
346 end
347 end
348 do
349 f((function()
350 return (function(_arg_0, ...)
351 local _ok_0 = _arg_0
352 if _ok_0 then
353 return ...
354 end
355 end)(pcall(function()
356 return func()
357 end))
358 end)())
359 end
360 do
361 f((function()
362 return (function(_arg_0, ...)
363 local _ok_0 = _arg_0
364 if _ok_0 then
365 return ...
366 end
367 end)(xpcall(function()
368 print(123)
369 return func()
370 end, function(e)
371 print(e)
372 return e
373 end))
374 end)())
375 end
376end
377return nil
diff --git a/spec/outputs/unicode/assign.lua b/spec/outputs/unicode/assign.lua
index bf43953..e00d016 100644
--- a/spec/outputs/unicode/assign.lua
+++ b/spec/outputs/unicode/assign.lua
@@ -36,17 +36,15 @@ local _u53d8_u91cfx
36do 36do
37 local _u51fd_u6570 = _u83b7_u53d6_u5904_u7406_u51fd_u6570() 37 local _u51fd_u6570 = _u83b7_u53d6_u5904_u7406_u51fd_u6570()
38 if _u51fd_u6570 then 38 if _u51fd_u6570 then
39 do 39 _u53d8_u91cfx = ((function()
40 _u51fd_u6570() 40 _u51fd_u6570()
41 _u53d8_u91cfx = 123 41 return 123
42 end 42 end)())
43 end 43 end
44end 44end
45local _anon_func_0 = function(_u6253_u5370) 45local _anon_func_0 = function(_u6253_u5370)
46 do 46 _u6253_u5370(123)
47 _u6253_u5370(123) 47 return { }
48 return { }
49 end
50end 48end
51return __u65e0_u6548_u53d8_u91cf(function() 49return __u65e0_u6548_u53d8_u91cf(function()
52 setmetatable(a_u53d8_u91cf, _anon_func_0(_u6253_u5370)) 50 setmetatable(a_u53d8_u91cf, _anon_func_0(_u6253_u5370))
diff --git a/spec/outputs/unicode/attrib.lua b/spec/outputs/unicode/attrib.lua
index 1c48de4..5e5bb99 100644
--- a/spec/outputs/unicode/attrib.lua
+++ b/spec/outputs/unicode/attrib.lua
@@ -48,17 +48,21 @@ do
48 end 48 end
49 local _u5173_u95ed_u53d8_u91cfb 49 local _u5173_u95ed_u53d8_u91cfb
50 if not false then 50 if not false then
51 if _u6761_u4ef6x then 51 _u5173_u95ed_u53d8_u91cfb = ((function()
52 _u5173_u95ed_u53d8_u91cfb = 1 52 if _u6761_u4ef6x then
53 end 53 return 1
54 end
55 end)())
54 end 56 end
55 local _close_0 <close> = _u5173_u95ed_u53d8_u91cfb 57 local _close_0 <close> = _u5173_u95ed_u53d8_u91cfb
56 local _u5e38_u91cfc 58 local _u5e38_u91cfc
57 if true then 59 if true then
58 local _exp_0 = _u6761_u4ef6x 60 _u5e38_u91cfc = ((function()
59 if "abc" == _exp_0 then 61 local _exp_0 = _u6761_u4ef6x
60 _u5e38_u91cfc = 998 62 if "abc" == _exp_0 then
61 end 63 return 998
64 end
65 end)())
62 end 66 end
63 local _u5173_u95ed_u53d8_u91cfd 67 local _u5173_u95ed_u53d8_u91cfd
64 if (function() 68 if (function()
diff --git a/spec/outputs/unicode/comprehension.lua b/spec/outputs/unicode/comprehension.lua
index 60e490f..92bce69 100644
--- a/spec/outputs/unicode/comprehension.lua
+++ b/spec/outputs/unicode/comprehension.lua
@@ -243,8 +243,11 @@ end
243do 243do
244 local _accum_0 = { } 244 local _accum_0 = { }
245 local _len_0 = 1 245 local _len_0 = 1
246 local _min_0 = 1 + 2
246 local _max_0 = 3 + 4 247 local _max_0 = 3 + 4
247 for _index_0 = 1 + 2, _max_0 < 0 and #_u5217_u8868 + _max_0 or _max_0 do 248 _min_0 = _min_0 < 0 and #_u5217_u8868 + _min_0 + 1 or _min_0
249 _max_0 = _max_0 < 0 and #_u5217_u8868 + _max_0 + 1 or _max_0
250 for _index_0 = _min_0, _max_0 do
248 local _u9879_u76ee = _u5217_u8868[_index_0] 251 local _u9879_u76ee = _u5217_u8868[_index_0]
249 _accum_0[_len_0] = _u9879_u76ee 252 _accum_0[_len_0] = _u9879_u76ee
250 _len_0 = _len_0 + 1 253 _len_0 = _len_0 + 1
@@ -254,8 +257,11 @@ end
254do 257do
255 local _accum_0 = { } 258 local _accum_0 = { }
256 local _len_0 = 1 259 local _len_0 = 1
260 local _min_0 = _u4f60_u597d() * 4
257 local _max_0 = 2 - _u4e1c_u897f[4] 261 local _max_0 = 2 - _u4e1c_u897f[4]
258 for _index_0 = _u4f60_u597d() * 4, _max_0 < 0 and #_u5217_u8868 + _max_0 or _max_0 do 262 _min_0 = _min_0 < 0 and #_u5217_u8868 + _min_0 + 1 or _min_0
263 _max_0 = _max_0 < 0 and #_u5217_u8868 + _max_0 + 1 or _max_0
264 for _index_0 = _min_0, _max_0 do
259 local _u9879_u76ee = _u5217_u8868[_index_0] 265 local _u9879_u76ee = _u5217_u8868[_index_0]
260 _accum_0[_len_0] = _u9879_u76ee 266 _accum_0[_len_0] = _u9879_u76ee
261 _len_0 = _len_0 + 1 267 _len_0 = _len_0 + 1
diff --git a/spec/outputs/unicode/do.lua b/spec/outputs/unicode/do.lua
index f9c3079..7bf1da3 100644
--- a/spec/outputs/unicode/do.lua
+++ b/spec/outputs/unicode/do.lua
@@ -32,10 +32,10 @@ local _u53d8_u91cft = {
32} 32}
33return function(_u53c2_u6570y, _u53c2_u6570k) 33return function(_u53c2_u6570y, _u53c2_u6570k)
34 if _u53c2_u6570y == nil then 34 if _u53c2_u6570y == nil then
35 do 35 _u53c2_u6570y = ((function()
36 _u53d8_u91cfx = 10 + 2 36 _u53d8_u91cfx = 10 + 2
37 _u53c2_u6570y = _u53d8_u91cfx 37 return _u53d8_u91cfx
38 end 38 end)())
39 end 39 end
40 if _u53c2_u6570k == nil then 40 if _u53c2_u6570k == nil then
41 do 41 do
diff --git a/spec/outputs/unicode/lists.lua b/spec/outputs/unicode/lists.lua
index aafd516..3bf6f50 100644
--- a/spec/outputs/unicode/lists.lua
+++ b/spec/outputs/unicode/lists.lua
@@ -229,31 +229,36 @@ _u53d8_u91cfx = {
229 6, 229 6,
230 7 230 7
231} 231}
232local _max_0 = -5 232local _max_0 = #_u53d8_u91cfx + -5 + 1
233for _index_0 = 2, _max_0 < 0 and #_u53d8_u91cfx + _max_0 or _max_0, 2 do 233for _index_0 = 2, _max_0, 2 do
234 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0] 234 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0]
235 _u6253_u5370(_u53d8_u91cfy) 235 _u6253_u5370(_u53d8_u91cfy)
236end 236end
237local _max_1 = 3 237for _index_0 = 1, 3 do
238for _index_0 = 1, _max_1 < 0 and #_u53d8_u91cfx + _max_1 or _max_1 do
239 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0] 238 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0]
240 _u6253_u5370(_u53d8_u91cfy) 239 _u6253_u5370(_u53d8_u91cfy)
241end 240end
242for _index_0 = 2, #_u53d8_u91cfx do 241local _max_1 = #_u53d8_u91cfx
242for _index_0 = 2, _max_1 do
243 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0] 243 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0]
244 _u6253_u5370(_u53d8_u91cfy) 244 _u6253_u5370(_u53d8_u91cfy)
245end 245end
246for _index_0 = 1, #_u53d8_u91cfx, 2 do 246local _max_2 = #_u53d8_u91cfx
247for _index_0 = 1, _max_2, 2 do
247 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0] 248 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0]
248 _u6253_u5370(_u53d8_u91cfy) 249 _u6253_u5370(_u53d8_u91cfy)
249end 250end
250for _index_0 = 2, #_u53d8_u91cfx, 2 do 251local _max_3 = #_u53d8_u91cfx
252for _index_0 = 2, _max_3, 2 do
251 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0] 253 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0]
252 _u6253_u5370(_u53d8_u91cfy) 254 _u6253_u5370(_u53d8_u91cfy)
253end 255end
254local _u53d8_u91cfa, _u53d8_u91cfb, _u53d8_u91cfc = 1, 5, 2 256local _u53d8_u91cfa, _u53d8_u91cfb, _u53d8_u91cfc = 1, 5, 2
255local _max_2 = _u53d8_u91cfb 257local _min_0 = _u53d8_u91cfa
256for _index_0 = _u53d8_u91cfa, _max_2 < 0 and #_u53d8_u91cfx + _max_2 or _max_2, _u53d8_u91cfc do 258local _max_4 = _u53d8_u91cfb
259_min_0 = _min_0 < 0 and #_u53d8_u91cfx + _min_0 + 1 or _min_0
260_max_4 = _max_4 < 0 and #_u53d8_u91cfx + _max_4 + 1 or _max_4
261for _index_0 = _min_0, _max_4, _u53d8_u91cfc do
257 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0] 262 local _u53d8_u91cfy = _u53d8_u91cfx[_index_0]
258 _u6253_u5370(_u53d8_u91cfy) 263 _u6253_u5370(_u53d8_u91cfy)
259end 264end
diff --git a/spec/outputs/unicode/loops.lua b/spec/outputs/unicode/loops.lua
index 8379993..27bbe2e 100644
--- a/spec/outputs/unicode/loops.lua
+++ b/spec/outputs/unicode/loops.lua
@@ -60,8 +60,8 @@ do
60 local _u53d8_u91cfy = _u4f60_u597d[_index_0] 60 local _u53d8_u91cfy = _u4f60_u597d[_index_0]
61 if _u53d8_u91cfy % 2 == 0 then 61 if _u53d8_u91cfy % 2 == 0 then
62 _accum_0[_len_0] = _u53d8_u91cfy 62 _accum_0[_len_0] = _u53d8_u91cfy
63 _len_0 = _len_0 + 1
63 end 64 end
64 _len_0 = _len_0 + 1
65 end 65 end
66 _u53d8_u91cfx = _accum_0 66 _u53d8_u91cfx = _accum_0
67end 67end
@@ -132,13 +132,11 @@ do
132end 132end
133do 133do
134 local _accum_0 = { } 134 local _accum_0 = { }
135 local _len_0 = 1
136 local _list_2 = 3 135 local _list_2 = 3
137 for _index_0 = 1, #_list_2 do 136 for _index_0 = 1, #_list_2 do
138 local _u4e1c_u897f = _list_2[_index_0] 137 local _u4e1c_u897f = _list_2[_index_0]
139 _u53d8_u91cfy = "你好" 138 _u53d8_u91cfy = "你好"
140 break 139 break
141 _len_0 = _len_0 + 1
142 end 140 end
143 _u53d8_u91cfx = _accum_0 141 _u53d8_u91cfx = _accum_0
144end 142end
diff --git a/spec/outputs/unicode/macro.lua b/spec/outputs/unicode/macro.lua
index 099080f..b4e78cd 100644
--- a/spec/outputs/unicode/macro.lua
+++ b/spec/outputs/unicode/macro.lua
@@ -215,24 +215,8 @@ do
215-- 这有个注释 215-- 这有个注释
216end 216end
217local _ = require('下划线') 217local _ = require('下划线')
218local _call_0 = (_({ 218local _anon_func_0 = function(_)
219 1, 219 local _call_0 = (_({
220 2,
221 3,
222 4,
223 -2,
224 3
225}))
226_call_0 = _call_0["链"](_call_0)
227_call_0 = _call_0["映射"](_call_0, function(self)
228 return self * 2
229end)
230_call_0 = _call_0["过滤"](_call_0, function(self)
231 return self > 3
232end)
233local _u7ed3_u679ca = _call_0["å–值"](_call_0)
234do
235 local _call_1 = (_({
236 1, 220 1,
237 2, 221 2,
238 3, 222 3,
@@ -240,27 +224,70 @@ do
240 -2, 224 -2,
241 3 225 3
242 })) 226 }))
243 _call_1 = _call_1["链"](_call_1) 227 return _call_0["链"](_call_0)
244 _call_1 = _call_1["映射"](_call_1, function(self) 228end
245 return self * 2 229local _call_0 = ((function()
246 end) 230 local _call_0 = ((function()
247 _call_1 = _call_1["过滤"](_call_1, function(self) 231 local _call_0 = (_anon_func_0(_))
232 return _call_0["映射"](_call_0, function(self)
233 return self * 2
234 end)
235 end)())
236 return _call_0["过滤"](_call_0, function(self)
248 return self > 3 237 return self > 3
249 end) 238 end)
239end)())
240local _u7ed3_u679ca = _call_0["å–值"](_call_0)
241local _anon_func_1 = function(_)
242 local _call_1 = (_({
243 1,
244 2,
245 3,
246 4,
247 -2,
248 3
249 }))
250 return _call_1["链"](_call_1)
251end
252do
253 local _call_1 = ((function()
254 local _call_1 = ((function()
255 local _call_1 = (_anon_func_1(_))
256 return _call_1["映射"](_call_1, function(self)
257 return self * 2
258 end)
259 end)())
260 return _call_1["过滤"](_call_1, function(self)
261 return self > 3
262 end)
263 end)())
250 _call_1["æ¯ä¸€ä¸ª"](_call_1, function(self) 264 _call_1["æ¯ä¸€ä¸ª"](_call_1, function(self)
251 return _u6253_u5370(self) 265 return _u6253_u5370(self)
252 end) 266 end)
253end 267end
254local _call_1 = _u539f_u70b9["å˜æ¢"]["根节点"]["游æˆå¯¹è±¡"] 268local _anon_func_2 = function(_u539f_u70b9)
255_call_1 = _call_1["父节点"](_call_1) 269 local _call_1 = _u539f_u70b9["å˜æ¢"]["根节点"]["游æˆå¯¹è±¡"]
256_call_1 = _call_1["åŽä»£"](_call_1) 270 return _call_1["父节点"](_call_1)
257_call_1 = _call_1["选择å¯ç”¨"](_call_1) 271end
258_call_1 = _call_1["选择å¯è§"](_call_1) 272local _call_1 = ((function()
259_call_1 = _call_1["标签等于"](_call_1, "fx") 273 local _call_1 = ((function()
260_call_1 = _call_1["其中"](_call_1, function(x) 274 local _call_1 = ((function()
261 local _call_2 = x["åç§°"] 275 local _call_1 = ((function()
262 return _call_2["结尾为"](_call_2, "(克隆)") 276 local _call_1 = ((function()
263end) 277 local _call_1 = (_anon_func_2(_u539f_u70b9))
278 return _call_1["åŽä»£"](_call_1)
279 end)())
280 return _call_1["选择å¯ç”¨"](_call_1)
281 end)())
282 return _call_1["选择å¯è§"](_call_1)
283 end)())
284 return _call_1["标签等于"](_call_1, "fx")
285 end)())
286 return _call_1["其中"](_call_1, function(x)
287 local _call_2 = x["åç§°"]
288 return _call_2["结尾为"](_call_2, "(克隆)")
289 end)
290end)())
264_u7ed3_u679c = _call_1["æ‘§æ¯"](_call_1) 291_u7ed3_u679c = _call_1["æ‘§æ¯"](_call_1)
265do 292do
266 do 293 do
@@ -332,10 +359,8 @@ local _1
332_1 = function() 359_1 = function()
333 _u6253_u5370(1) 360 _u6253_u5370(1)
334 local _accum_0 = { } 361 local _accum_0 = { }
335 local _len_0 = 1
336 while false do 362 while false do
337 break 363 break
338 _len_0 = _len_0 + 1
339 end 364 end
340 return _accum_0 365 return _accum_0
341end 366end
diff --git a/spec/outputs/unicode/multiline_chain.lua b/spec/outputs/unicode/multiline_chain.lua
index c1da13f..61e7057 100644
--- a/spec/outputs/unicode/multiline_chain.lua
+++ b/spec/outputs/unicode/multiline_chain.lua
@@ -59,10 +59,8 @@ _u51fd_u6570 = function()
59 return _accum_0 59 return _accum_0
60end 60end
61local _anon_func_0 = function(_u53d8_u91cfa) 61local _anon_func_0 = function(_u53d8_u91cfa)
62 do 62 local _call_1 = _u53d8_u91cfa
63 local _call_1 = _u53d8_u91cfa 63 return (_call_1["å˜é‡b"](_call_1, 123))["å˜é‡c"]("abc")
64 return (_call_1["å˜é‡b"](_call_1, 123))["å˜é‡c"]("abc")
65 end
66end 64end
67local _u51fd_u65701 65local _u51fd_u65701
68_u51fd_u65701 = function() 66_u51fd_u65701 = function()
diff --git a/spec/outputs/unicode/syntax.lua b/spec/outputs/unicode/syntax.lua
index ea97bb9..a13302b 100644
--- a/spec/outputs/unicode/syntax.lua
+++ b/spec/outputs/unicode/syntax.lua
@@ -258,9 +258,11 @@ _u53d8_u91cfx = 0
258local _list_0 = _u503c 258local _list_0 = _u503c
259for _index_0 = 1, #_list_0 do 259for _index_0 = 1, #_list_0 do
260 local _u53d8_u91cfv = _list_0[_index_0] 260 local _u53d8_u91cfv = _list_0[_index_0]
261 if ntype(_u53d8_u91cfv) == "函数定义" then 261 _ = ((function()
262 _ = _u53d8_u91cfx + 1 262 if ntype(_u53d8_u91cfv) == "函数定义" then
263 end 263 return _u53d8_u91cfx + 1
264 end
265 end)())
264end 266end
265_u4f60_u597d = { 267_u4f60_u597d = {
266 ["æŸç‰©"] = _u4e16_u754c, 268 ["æŸç‰©"] = _u4e16_u754c,
@@ -284,10 +286,8 @@ _ = 5 - _u4ec0_u4e48(_u65e0_u804a)
284_u4ec0_u4e48(_u65e0_u804a - 5) 286_u4ec0_u4e48(_u65e0_u804a - 5)
285_u53d8_u91cfx = _u4f60_u597d - _u4e16_u754c - _u67d0_u7269 287_u53d8_u91cfx = _u4f60_u597d - _u4e16_u754c - _u67d0_u7269
286local _anon_func_0 = function(_u4ec0_u4e48) 288local _anon_func_0 = function(_u4ec0_u4e48)
287 do 289 local _call_8 = _u4ec0_u4e48
288 local _call_8 = _u4ec0_u4e48 290 return _call_8["é…·"](_call_8, 100)
289 return _call_8["é…·"](_call_8, 100)
290 end
291end 291end
292(function(_u67d0_u7269) 292(function(_u67d0_u7269)
293 if _u67d0_u7269 == nil then 293 if _u67d0_u7269 == nil then
diff --git a/spec/outputs/unicode/try_catch.lua b/spec/outputs/unicode/try_catch.lua
index 22f29f9..f8c7849 100644
--- a/spec/outputs/unicode/try_catch.lua
+++ b/spec/outputs/unicode/try_catch.lua
@@ -32,11 +32,13 @@ pcall(function()
32 return _u8868["函数"]() 32 return _u8868["函数"]()
33end) 33end)
34pcall(function() 34pcall(function()
35 return _u8868["函数"]() 35 return (_u8868["函数"]())
36end) 36end)
37pcall(function() 37pcall(function()
38 local _call_0 = _u8868 38 return ((function()
39 return _call_0["函数"](_call_0, 1, 2, 3) 39 local _call_0 = _u8868
40 return _call_0["函数"](_call_0, 1, 2, 3)
41 end)())
40end) 42end)
41pcall(function() 43pcall(function()
42 return _u8868["函数"](1) 44 return _u8868["函数"](1)
@@ -52,7 +54,7 @@ end)) then
52 _u6253_u5370("好的") 54 _u6253_u5370("好的")
53end 55end
54if xpcall(function() 56if xpcall(function()
55 return _u51fd_u6570(1) 57 return (_u51fd_u6570(1))
56end, function(_u9519_u8bef) 58end, function(_u9519_u8bef)
57 return _u6253_u5370(_u9519_u8bef) 59 return _u6253_u5370(_u9519_u8bef)
58end) then 60end) then
diff --git a/spec/outputs/unicode/vararg.lua b/spec/outputs/unicode/vararg.lua
index b837006..fc894ff 100644
--- a/spec/outputs/unicode/vararg.lua
+++ b/spec/outputs/unicode/vararg.lua
@@ -125,14 +125,10 @@ local _anon_func_11 = function(_u9879_u76ee, ...)
125 return _tbl_0 125 return _tbl_0
126end 126end
127local _anon_func_12 = function(_u51fd_u6570) 127local _anon_func_12 = function(_u51fd_u6570)
128 do 128 return _u51fd_u6570()
129 return _u51fd_u6570()
130 end
131end 129end
132local _anon_func_13 = function(_u51fd_u6570, ...) 130local _anon_func_13 = function(_u51fd_u6570, ...)
133 do 131 return _u51fd_u6570(...)
134 return _u51fd_u6570(...)
135 end
136end 132end
137local _anon_func_14 = function(_u51fd_u6570) 133local _anon_func_14 = function(_u51fd_u6570)
138 local _accum_0 = { } 134 local _accum_0 = { }
@@ -195,15 +191,11 @@ local _anon_func_23 = function(_u51fd_u6570, ...)
195 return nil 191 return nil
196end 192end
197local _anon_func_24 = function(_u6253_u5370, select, ...) 193local _anon_func_24 = function(_u6253_u5370, select, ...)
198 do 194 _u6253_u5370(select("#", ...))
199 _u6253_u5370(select("#", ...)) 195 return _u6253_u5370(...)
200 return _u6253_u5370(...)
201 end
202end 196end
203local _anon_func_25 = function(_u6253_u5370, ...) 197local _anon_func_25 = function(_u6253_u5370, ...)
204 do 198 return _u6253_u5370(...)
205 return _u6253_u5370(...)
206 end
207end 199end
208local _anon_func_26 = function(_u53d8_u91cfx, _u8868, _u88682) 200local _anon_func_26 = function(_u53d8_u91cfx, _u8868, _u88682)
209 if 1 == _u53d8_u91cfx then 201 if 1 == _u53d8_u91cfx then
@@ -214,9 +206,7 @@ local _anon_func_26 = function(_u53d8_u91cfx, _u8868, _u88682)
214 end 206 end
215end 207end
216local _anon_func_27 = function(_u6253_u5370, ...) 208local _anon_func_27 = function(_u6253_u5370, ...)
217 do 209 return _u6253_u5370(...)
218 return _u6253_u5370(...)
219 end
220end 210end
221local _anon_func_28 = function(_u6761_u4ef6) 211local _anon_func_28 = function(_u6761_u4ef6)
222 if _u6761_u4ef6 then 212 if _u6761_u4ef6 then
@@ -224,10 +214,8 @@ local _anon_func_28 = function(_u6761_u4ef6)
224 end 214 end
225end 215end
226local _anon_func_29 = function(_u6253_u5370, _arg_0, ...) 216local _anon_func_29 = function(_u6253_u5370, _arg_0, ...)
227 do 217 local _u8868 = _arg_0
228 local _u8868 = _arg_0 218 return _u6253_u5370(...)
229 return _u6253_u5370(...)
230 end
231end 219end
232local _u8fde_u63a5 220local _u8fde_u63a5
233_u8fde_u63a5 = function(...) 221_u8fde_u63a5 = function(...)
diff --git a/spec/outputs/upvalue_func.lua b/spec/outputs/upvalue_func.lua
index 3181adf..3e088be 100644
--- a/spec/outputs/upvalue_func.lua
+++ b/spec/outputs/upvalue_func.lua
@@ -214,10 +214,8 @@ local _anon_func_1 = function(valueB)
214 end 214 end
215end 215end
216local _anon_func_2 = function(print, select, _arg_0, ...) 216local _anon_func_2 = function(print, select, _arg_0, ...)
217 do 217 local ok = _arg_0
218 local ok = _arg_0 218 return print(select(3, ...))
219 return print(select(3, ...))
220 end
221end 219end
222local _anon_func_3 = function(tb) 220local _anon_func_3 = function(tb)
223 if tb ~= nil then 221 if tb ~= nil then
@@ -242,11 +240,9 @@ local _anon_func_5 = function(getmetatable, tb)
242 return _obj_0[1 + 1](_obj_0, "abc") 240 return _obj_0[1 + 1](_obj_0, "abc")
243end 241end
244local _anon_func_6 = function(tb) 242local _anon_func_6 = function(tb)
245 do 243 local _call_0 = tb
246 local _call_0 = tb 244 local _call_1 = _call_0["end"](_call_0)
247 local _call_1 = _call_0["end"](_call_0) 245 return _call_1["🤣"](_call_1, 123)
248 return _call_1["🤣"](_call_1, 123)
249 end
250end 246end
251local _anon_func_7 = function(itemA, listA) 247local _anon_func_7 = function(itemA, listA)
252 for _index_0 = 1, #listA do 248 for _index_0 = 1, #listA do
@@ -354,17 +350,13 @@ local _anon_func_16 = function(pairs, tb, tostring)
354 return _tbl_0 350 return _tbl_0
355end 351end
356local _anon_func_17 = function(print) 352local _anon_func_17 = function(print)
357 do 353 print(123)
358 print(123) 354 return "abc"
359 return "abc"
360 end
361end 355end
362local _anon_func_18 = function(print, select, _arg_0, ...) 356local _anon_func_18 = function(print, select, _arg_0, ...)
363 do 357 local success = _arg_0
364 local success = _arg_0 358 if success then
365 if success then 359 return print(select('#', ...))
366 return print(select('#', ...))
367 end
368 end 360 end
369end 361end
370local _anon_func_19 = function(cond, i) 362local _anon_func_19 = function(cond, i)
@@ -459,11 +451,9 @@ local _anon_func_25 = function(itemA, listA)
459 return false 451 return false
460end 452end
461local _anon_func_24 = function(itemA, listA, tb) 453local _anon_func_24 = function(itemA, listA, tb)
462 do 454 local _call_0 = tb
463 local _call_0 = tb 455 local _call_1 = _call_0["end"](_call_0)
464 local _call_1 = _call_0["end"](_call_0) 456 return _call_1["🤣"](_call_1, 123 and (#listA > 0 and _anon_func_25(itemA, listA)))
465 return _call_1["🤣"](_call_1, 123 and (#listA > 0 and _anon_func_25(itemA, listA)))
466 end
467end 457end
468GameEngine:onEvent("SomeEvent", function() 458GameEngine:onEvent("SomeEvent", function()
469 return func(value + (_anon_func_21(cond)) + (_anon_func_22(valueB)) > _anon_func_23(tb) + _anon_func_24(itemA, listA, tb)) 459 return func(value + (_anon_func_21(cond)) + (_anon_func_22(valueB)) > _anon_func_23(tb) + _anon_func_24(itemA, listA, tb))
@@ -503,13 +493,11 @@ local _anon_func_27 = function(char)
503 return nil 493 return nil
504end 494end
505local _anon_func_28 = function(os, _arg_0, ...) 495local _anon_func_28 = function(os, _arg_0, ...)
506 do 496 local ok = _arg_0
507 local ok = _arg_0 497 if ok then
508 if ok then 498 return ...
509 return ... 499 else
510 else 500 return os.exit(1)
511 return os.exit(1)
512 end
513 end 501 end
514end 502end
515local _anon_func_29 = function(debug_env_after, debug_env_before, env, func) 503local _anon_func_29 = function(debug_env_after, debug_env_before, env, func)
diff --git a/spec/outputs/vararg.lua b/spec/outputs/vararg.lua
index dabba44..254aa6a 100644
--- a/spec/outputs/vararg.lua
+++ b/spec/outputs/vararg.lua
@@ -125,14 +125,10 @@ local _anon_func_11 = function(items, ...)
125 return _tbl_0 125 return _tbl_0
126end 126end
127local _anon_func_12 = function(func) 127local _anon_func_12 = function(func)
128 do 128 return func()
129 return func()
130 end
131end 129end
132local _anon_func_13 = function(func, ...) 130local _anon_func_13 = function(func, ...)
133 do 131 return func(...)
134 return func(...)
135 end
136end 132end
137local _anon_func_14 = function(func) 133local _anon_func_14 = function(func)
138 local _accum_0 = { } 134 local _accum_0 = { }
@@ -195,15 +191,11 @@ local _anon_func_23 = function(func, ...)
195 return nil 191 return nil
196end 192end
197local _anon_func_24 = function(print, select, ...) 193local _anon_func_24 = function(print, select, ...)
198 do 194 print(select("#", ...))
199 print(select("#", ...)) 195 return print(...)
200 return print(...)
201 end
202end 196end
203local _anon_func_25 = function(print, ...) 197local _anon_func_25 = function(print, ...)
204 do 198 return print(...)
205 return print(...)
206 end
207end 199end
208local _anon_func_26 = function(tb, tb2, x) 200local _anon_func_26 = function(tb, tb2, x)
209 if 1 == x then 201 if 1 == x then
@@ -214,9 +206,7 @@ local _anon_func_26 = function(tb, tb2, x)
214 end 206 end
215end 207end
216local _anon_func_27 = function(print, ...) 208local _anon_func_27 = function(print, ...)
217 do 209 return print(...)
218 return print(...)
219 end
220end 210end
221local _anon_func_28 = function(cond) 211local _anon_func_28 = function(cond)
222 if cond then 212 if cond then
@@ -224,10 +214,8 @@ local _anon_func_28 = function(cond)
224 end 214 end
225end 215end
226local _anon_func_29 = function(print, _arg_0, ...) 216local _anon_func_29 = function(print, _arg_0, ...)
227 do 217 local tb = _arg_0
228 local tb = _arg_0 218 return print(...)
229 return print(...)
230 end
231end 219end
232local join 220local join
233join = function(...) 221join = function(...)
diff --git a/spec/outputs/with.lua b/spec/outputs/with.lua
index 1a795c1..530915e 100644
--- a/spec/outputs/with.lua
+++ b/spec/outputs/with.lua
@@ -187,4 +187,66 @@ do
187 return _with_0[123] 187 return _with_0[123]
188 end 188 end
189end 189end
190do
191 f((function()
192 local _with_0 = item
193 do
194 local _accum_0
195 repeat
196 if _with_0.id > 0 then
197 _accum_0 = _with_0.content
198 break
199 end
200 until true
201 _with_0 = _accum_0
202 end
203 return _with_0
204 end)())
205 local a
206 do
207 local _with_0 = tb
208 do
209 local _accum_0
210 repeat
211 if _with_0.v then
212 _accum_0 = _with_0.a
213 break
214 end
215 until true
216 _with_0 = _accum_0
217 end
218 a = _with_0
219 end
220 local _accum_0
221 while true do
222 local _with_0 = tb
223 local _accum_1
224 repeat
225 if _with_0 ~= nil then
226 _accum_1 = 1
227 break
228 end
229 until true
230 _with_0 = _accum_1
231 _accum_0 = _with_0
232 break
233 end
234 a = _accum_0
235end
236do
237 local a
238 local _accum_0
239 for i = 1, 100 do
240 local x = tb[i]
241 if x ~= nil then
242 local _des_0 = 1
243 if _des_0 then
244 x.id = _des_0
245 _accum_0 = x
246 break
247 end
248 end
249 end
250 a = _accum_0
251end
190return nil 252return nil
diff --git a/src/3rdParty/efsw/Atomic.hpp b/src/3rdParty/efsw/Atomic.hpp
index 4008dfc..9015c60 100755..100644
--- a/src/3rdParty/efsw/Atomic.hpp
+++ b/src/3rdParty/efsw/Atomic.hpp
@@ -3,9 +3,7 @@
3 3
4#include <efsw/base.hpp> 4#include <efsw/base.hpp>
5 5
6#ifdef EFSW_USE_CXX11
7#include <atomic> 6#include <atomic>
8#endif
9 7
10namespace efsw { 8namespace efsw {
11 9
@@ -14,36 +12,20 @@ template <typename T> class Atomic {
14 explicit Atomic( T set = false ) : set_( set ) {} 12 explicit Atomic( T set = false ) : set_( set ) {}
15 13
16 Atomic& operator=( T set ) { 14 Atomic& operator=( T set ) {
17#ifdef EFSW_USE_CXX11
18 set_.store( set, std::memory_order_release ); 15 set_.store( set, std::memory_order_release );
19#else
20 set_ = set;
21#endif
22 return *this; 16 return *this;
23 } 17 }
24 18
25 explicit operator T() const { 19 explicit operator T() const {
26#ifdef EFSW_USE_CXX11
27 return set_.load( std::memory_order_acquire ); 20 return set_.load( std::memory_order_acquire );
28#else
29 return set_;
30#endif
31 } 21 }
32 22
33 T load() const { 23 T load() const {
34#ifdef EFSW_USE_CXX11
35 return set_.load( std::memory_order_acquire ); 24 return set_.load( std::memory_order_acquire );
36#else
37 return set_;
38#endif
39 } 25 }
40 26
41 private: 27 private:
42#ifdef EFSW_USE_CXX11
43 std::atomic<T> set_; 28 std::atomic<T> set_;
44#else
45 volatile T set_;
46#endif
47}; 29};
48 30
49} // namespace efsw 31} // namespace efsw
diff --git a/src/3rdParty/efsw/Debug.cpp b/src/3rdParty/efsw/Debug.cpp
index 18cfd31..18cfd31 100755..100644
--- a/src/3rdParty/efsw/Debug.cpp
+++ b/src/3rdParty/efsw/Debug.cpp
diff --git a/src/3rdParty/efsw/Debug.hpp b/src/3rdParty/efsw/Debug.hpp
index 78d3557..fefaec4 100755..100644
--- a/src/3rdParty/efsw/Debug.hpp
+++ b/src/3rdParty/efsw/Debug.hpp
@@ -49,8 +49,10 @@ void efPRINTC( unsigned int cond, const char* format, ... );
49#define efDEBUGC( cond, format, args... ) \ 49#define efDEBUGC( cond, format, args... ) \
50 {} 50 {}
51#else 51#else
52#define efDEBUG 52#define efDEBUG( ... ) \
53#define efDEBUGC 53 {}
54#define efDEBUGC( ... ) \
55 {}
54#endif 56#endif
55 57
56#endif 58#endif
diff --git a/src/3rdParty/efsw/DirWatcherGeneric.cpp b/src/3rdParty/efsw/DirWatcherGeneric.cpp
index 8b6bc8a..8b6bc8a 100755..100644
--- a/src/3rdParty/efsw/DirWatcherGeneric.cpp
+++ b/src/3rdParty/efsw/DirWatcherGeneric.cpp
diff --git a/src/3rdParty/efsw/DirWatcherGeneric.hpp b/src/3rdParty/efsw/DirWatcherGeneric.hpp
index ca52de7..ca52de7 100755..100644
--- a/src/3rdParty/efsw/DirWatcherGeneric.hpp
+++ b/src/3rdParty/efsw/DirWatcherGeneric.hpp
diff --git a/src/3rdParty/efsw/DirectorySnapshot.cpp b/src/3rdParty/efsw/DirectorySnapshot.cpp
index 6049e4a..f78475f 100755..100644
--- a/src/3rdParty/efsw/DirectorySnapshot.cpp
+++ b/src/3rdParty/efsw/DirectorySnapshot.cpp
@@ -44,7 +44,7 @@ void DirectorySnapshot::initFiles() {
44 Files = FileSystem::filesInfoFromPath( DirectoryInfo.Filepath ); 44 Files = FileSystem::filesInfoFromPath( DirectoryInfo.Filepath );
45 45
46 FileInfoMap::iterator it = Files.begin(); 46 FileInfoMap::iterator it = Files.begin();
47 std::list<std::string> eraseFiles; 47 std::vector<std::string> eraseFiles;
48 48
49 /// Remove all non regular files and non directories 49 /// Remove all non regular files and non directories
50 for ( ; it != Files.end(); it++ ) { 50 for ( ; it != Files.end(); it++ ) {
@@ -53,7 +53,7 @@ void DirectorySnapshot::initFiles() {
53 } 53 }
54 } 54 }
55 55
56 for ( std::list<std::string>::iterator eit = eraseFiles.begin(); eit != eraseFiles.end(); 56 for ( std::vector<std::string>::iterator eit = eraseFiles.begin(); eit != eraseFiles.end();
57 eit++ ) { 57 eit++ ) {
58 Files.erase( *eit ); 58 Files.erase( *eit );
59 } 59 }
diff --git a/src/3rdParty/efsw/DirectorySnapshot.hpp b/src/3rdParty/efsw/DirectorySnapshot.hpp
index 0e60542..0e60542 100755..100644
--- a/src/3rdParty/efsw/DirectorySnapshot.hpp
+++ b/src/3rdParty/efsw/DirectorySnapshot.hpp
diff --git a/src/3rdParty/efsw/DirectorySnapshotDiff.cpp b/src/3rdParty/efsw/DirectorySnapshotDiff.cpp
index 37ee507..37ee507 100755..100644
--- a/src/3rdParty/efsw/DirectorySnapshotDiff.cpp
+++ b/src/3rdParty/efsw/DirectorySnapshotDiff.cpp
diff --git a/src/3rdParty/efsw/DirectorySnapshotDiff.hpp b/src/3rdParty/efsw/DirectorySnapshotDiff.hpp
index 26a29ec..26a29ec 100755..100644
--- a/src/3rdParty/efsw/DirectorySnapshotDiff.hpp
+++ b/src/3rdParty/efsw/DirectorySnapshotDiff.hpp
diff --git a/src/3rdParty/efsw/FileInfo.cpp b/src/3rdParty/efsw/FileInfo.cpp
index 072cd6d..707f617 100755..100644
--- a/src/3rdParty/efsw/FileInfo.cpp
+++ b/src/3rdParty/efsw/FileInfo.cpp
@@ -35,10 +35,6 @@
35#endif 35#endif
36#endif 36#endif
37 37
38#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
39#include <filesystem>
40#endif
41
42namespace efsw { 38namespace efsw {
43 39
44bool FileInfo::exists( const std::string& filePath ) { 40bool FileInfo::exists( const std::string& filePath ) {
@@ -186,12 +182,14 @@ bool FileInfo::isLink() const {
186std::string FileInfo::linksTo() { 182std::string FileInfo::linksTo() {
187#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32 183#if EFSW_PLATFORM != EFSW_PLATFORM_WIN32
188 if ( isLink() ) { 184 if ( isLink() ) {
189 std::error_code ec; 185 char* ch = realpath( Filepath.c_str(), NULL );
190 auto ch = std::filesystem::canonical( Filepath, ec ); 186
187 if ( NULL != ch ) {
188 std::string tstr( ch );
191 189
192 if ( !ec ) { 190 free( ch );
193 191
194 return ch.string(); 192 return tstr;
195 } 193 }
196 } 194 }
197#endif 195#endif
diff --git a/src/3rdParty/efsw/FileInfo.hpp b/src/3rdParty/efsw/FileInfo.hpp
index f1bcf4b..1b55c35 100755..100644
--- a/src/3rdParty/efsw/FileInfo.hpp
+++ b/src/3rdParty/efsw/FileInfo.hpp
@@ -2,7 +2,7 @@
2#define EFSW_FILEINFO_HPP 2#define EFSW_FILEINFO_HPP
3 3
4#include <efsw/base.hpp> 4#include <efsw/base.hpp>
5#include <list> 5#include <vector>
6#include <map> 6#include <map>
7#include <string> 7#include <string>
8 8
@@ -18,11 +18,11 @@ class FileInfo {
18 18
19 FileInfo(); 19 FileInfo();
20 20
21 explicit FileInfo( const std::string& filepath ); 21 FileInfo( const std::string& filepath );
22 22
23 FileInfo( const std::string& filepath, bool linkInfo ); 23 FileInfo( const std::string& filepath, bool linkInfo );
24 24
25 FileInfo( const FileInfo& ) = default; 25 FileInfo(const FileInfo&) = default;
26 26
27 bool operator==( const FileInfo& Other ) const; 27 bool operator==( const FileInfo& Other ) const;
28 28
@@ -58,8 +58,8 @@ class FileInfo {
58}; 58};
59 59
60typedef std::map<std::string, FileInfo> FileInfoMap; 60typedef std::map<std::string, FileInfo> FileInfoMap;
61typedef std::list<FileInfo> FileInfoList; 61typedef std::vector<FileInfo> FileInfoList;
62typedef std::list<std::pair<std::string, FileInfo>> MovedList; 62typedef std::vector<std::pair<std::string, FileInfo>> MovedList;
63 63
64} // namespace efsw 64} // namespace efsw
65 65
diff --git a/src/3rdParty/efsw/FileSystem.cpp b/src/3rdParty/efsw/FileSystem.cpp
index 867f120..1ed346c 100755..100644
--- a/src/3rdParty/efsw/FileSystem.cpp
+++ b/src/3rdParty/efsw/FileSystem.cpp
@@ -1,10 +1,19 @@
1#include <cstring>
1#include <efsw/FileSystem.hpp> 2#include <efsw/FileSystem.hpp>
2#include <efsw/platform/platformimpl.hpp> 3#include <efsw/platform/platformimpl.hpp>
4#include <climits>
3 5
4#if EFSW_OS == EFSW_OS_MACOSX 6#if EFSW_OS == EFSW_OS_MACOSX
5#include <CoreFoundation/CoreFoundation.h> 7#include <CoreFoundation/CoreFoundation.h>
6#endif 8#endif
7 9
10#if EFSW_OS == EFSW_OS_WIN
11#ifndef WIN32_LEAN_AND_MEAN
12#define WIN32_LEAN_AND_MEAN
13#endif
14#include <windows.h>
15#endif
16
8namespace efsw { 17namespace efsw {
9 18
10bool FileSystem::isDirectory( const std::string& path ) { 19bool FileSystem::isDirectory( const std::string& path ) {
@@ -26,13 +35,13 @@ bool FileSystem::slashAtEnd( std::string& dir ) {
26} 35}
27 36
28void FileSystem::dirAddSlashAtEnd( std::string& dir ) { 37void FileSystem::dirAddSlashAtEnd( std::string& dir ) {
29 if ( dir.size() > 1 && dir[dir.size() - 1] != getOSSlash() ) { 38 if ( dir.size() >= 1 && dir[dir.size() - 1] != getOSSlash() ) {
30 dir.push_back( getOSSlash() ); 39 dir.push_back( getOSSlash() );
31 } 40 }
32} 41}
33 42
34void FileSystem::dirRemoveSlashAtEnd( std::string& dir ) { 43void FileSystem::dirRemoveSlashAtEnd( std::string& dir ) {
35 if ( dir.size() > 1 && dir[dir.size() - 1] == getOSSlash() ) { 44 if ( dir.size() >= 1 && dir[dir.size() - 1] == getOSSlash() ) {
36 dir.erase( dir.size() - 1 ); 45 dir.erase( dir.size() - 1 );
37 } 46 }
38} 47}
@@ -91,13 +100,30 @@ std::string FileSystem::precomposeFileName( const std::string& name ) {
91 100
92 CFStringNormalize( cfMutable, kCFStringNormalizationFormC ); 101 CFStringNormalize( cfMutable, kCFStringNormalizationFormC );
93 102
94 char c_str[255 + 1]; 103 const char* c_str = CFStringGetCStringPtr( cfMutable, kCFStringEncodingUTF8 );
95 CFStringGetCString( cfMutable, c_str, sizeof( c_str ) - 1, kCFStringEncodingUTF8 ); 104 if ( c_str != NULL ) {
96 105 std::string result( c_str );
97 CFRelease( cfStringRef ); 106 CFRelease( cfStringRef );
98 CFRelease( cfMutable ); 107 CFRelease( cfMutable );
108 return result;
109 }
110 CFIndex length = CFStringGetLength( cfMutable );
111 CFIndex maxSize = CFStringGetMaximumSizeForEncoding( length, kCFStringEncodingUTF8 );
112 if ( maxSize == kCFNotFound ) {
113 CFRelease( cfStringRef );
114 CFRelease( cfMutable );
115 return std::string();
116 }
99 117
100 return std::string( c_str ); 118 std::string result( maxSize + 1, '\0' );
119 if ( CFStringGetCString( cfMutable, &result[0], result.size(), kCFStringEncodingUTF8 ) ) {
120 result.resize( std::strlen( result.c_str() ) );
121 CFRelease( cfStringRef );
122 CFRelease( cfMutable );
123 } else {
124 result.clear();
125 }
126 return result;
101#else 127#else
102 return name; 128 return name;
103#endif 129#endif
@@ -115,4 +141,21 @@ std::string FileSystem::getCurrentWorkingDirectory() {
115 return Platform::FileSystem::getCurrentWorkingDirectory(); 141 return Platform::FileSystem::getCurrentWorkingDirectory();
116} 142}
117 143
144std::string FileSystem::getRealPath( const std::string& path ) {
145 std::string realPath;
146#if defined( EFSW_PLATFORM_POSIX )
147 char dir[PATH_MAX];
148 realpath( path.c_str(), &dir[0] );
149 realPath = std::string( dir );
150#elif EFSW_OS == EFSW_OS_WIN
151 wchar_t dir[_MAX_PATH + 1];
152 GetFullPathNameW( String::fromUtf8( path ).toWideString().c_str(), _MAX_PATH, &dir[0],
153 nullptr );
154 realPath = String( dir ).toUtf8();
155#else
156#warning FileSystem::getRealPath() not implemented on this platform.
157#endif
158 return realPath;
159}
160
118} // namespace efsw 161} // namespace efsw
diff --git a/src/3rdParty/efsw/FileSystem.hpp b/src/3rdParty/efsw/FileSystem.hpp
index 6c24386..1d66ece 100755..100644
--- a/src/3rdParty/efsw/FileSystem.hpp
+++ b/src/3rdParty/efsw/FileSystem.hpp
@@ -3,7 +3,6 @@
3 3
4#include <efsw/FileInfo.hpp> 4#include <efsw/FileInfo.hpp>
5#include <efsw/base.hpp> 5#include <efsw/base.hpp>
6#include <map>
7 6
8namespace efsw { 7namespace efsw {
9 8
@@ -34,6 +33,9 @@ class FileSystem {
34 static bool changeWorkingDirectory( const std::string& path ); 33 static bool changeWorkingDirectory( const std::string& path );
35 34
36 static std::string getCurrentWorkingDirectory(); 35 static std::string getCurrentWorkingDirectory();
36
37 static std::string getRealPath( const std::string& path );
38
37}; 39};
38 40
39} // namespace efsw 41} // namespace efsw
diff --git a/src/3rdParty/efsw/FileWatcher.cpp b/src/3rdParty/efsw/FileWatcher.cpp
index 696a46f..f45b243 100755..100644
--- a/src/3rdParty/efsw/FileWatcher.cpp
+++ b/src/3rdParty/efsw/FileWatcher.cpp
@@ -1,119 +1,120 @@
1#include <efsw/FileSystem.hpp> 1#include <efsw/FileSystem.hpp>
2#include <efsw/FileWatcherGeneric.hpp> 2#include <efsw/FileWatcherGeneric.hpp>
3#include <efsw/FileWatcherImpl.hpp> 3#include <efsw/FileWatcherImpl.hpp>
4#include <efsw/efsw.hpp> 4#include <efsw/efsw.hpp>
5 5
6#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 6#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
7#include <efsw/FileWatcherWin32.hpp> 7#include <efsw/FileWatcherWin32.hpp>
8#define FILEWATCHER_IMPL FileWatcherWin32 8#define FILEWATCHER_IMPL FileWatcherWin32
9#define BACKEND_NAME "Win32" 9#define BACKEND_NAME "Win32"
10#elif EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY 10#elif EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY
11#include <efsw/FileWatcherInotify.hpp> 11#include <efsw/FileWatcherInotify.hpp>
12#define FILEWATCHER_IMPL FileWatcherInotify 12#define FILEWATCHER_IMPL FileWatcherInotify
13#define BACKEND_NAME "Inotify" 13#define BACKEND_NAME "Inotify"
14#elif EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE 14#elif EFSW_PLATFORM == EFSW_PLATFORM_KQUEUE
15#include <efsw/FileWatcherKqueue.hpp> 15#include <efsw/FileWatcherKqueue.hpp>
16#define FILEWATCHER_IMPL FileWatcherKqueue 16#define FILEWATCHER_IMPL FileWatcherKqueue
17#define BACKEND_NAME "Kqueue" 17#define BACKEND_NAME "Kqueue"
18#elif EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS 18#elif EFSW_PLATFORM == EFSW_PLATFORM_FSEVENTS
19#include <efsw/FileWatcherFSEvents.hpp> 19#include <efsw/FileWatcherFSEvents.hpp>
20#define FILEWATCHER_IMPL FileWatcherFSEvents 20#define FILEWATCHER_IMPL FileWatcherFSEvents
21#define BACKEND_NAME "FSEvents" 21#define BACKEND_NAME "FSEvents"
22#else 22#else
23#define FILEWATCHER_IMPL FileWatcherGeneric 23#define FILEWATCHER_IMPL FileWatcherGeneric
24#define BACKEND_NAME "Generic" 24#define BACKEND_NAME "Generic"
25#endif 25#endif
26 26
27#include <efsw/Debug.hpp> 27#include <efsw/Debug.hpp>
28 28
29namespace efsw { 29namespace efsw {
30 30
31FileWatcher::FileWatcher() : mFollowSymlinks( false ), mOutOfScopeLinks( false ) { 31FileWatcher::FileWatcher() : mFollowSymlinks( false ), mOutOfScopeLinks( false ) {
32 efDEBUG( "Using backend: %s\n", BACKEND_NAME ); 32 efDEBUG( "Using backend: %s\n", BACKEND_NAME );
33 33
34 mImpl = new FILEWATCHER_IMPL( this ); 34 mImpl = new FILEWATCHER_IMPL( this );
35 35
36 if ( !mImpl->initOK() ) { 36 if ( !mImpl->initOK() ) {
37 efSAFE_DELETE( mImpl ); 37 efSAFE_DELETE( mImpl );
38 38
39 efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME ); 39 efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME );
40 40
41 mImpl = new FileWatcherGeneric( this ); 41 mImpl = new FileWatcherGeneric( this );
42 } 42 }
43} 43}
44 44
45FileWatcher::FileWatcher( bool useGenericFileWatcher ) : 45FileWatcher::FileWatcher( bool useGenericFileWatcher ) :
46 mFollowSymlinks( false ), mOutOfScopeLinks( false ) { 46 mFollowSymlinks( false ), mOutOfScopeLinks( false ) {
47 if ( useGenericFileWatcher ) { 47 if ( useGenericFileWatcher ) {
48 efDEBUG( "Using backend: Generic\n" ); 48 efDEBUG( "Using backend: Generic\n" );
49 49
50 mImpl = new FileWatcherGeneric( this ); 50 mImpl = new FileWatcherGeneric( this );
51 } else { 51 } else {
52 efDEBUG( "Using backend: %s\n", BACKEND_NAME ); 52 efDEBUG( "Using backend: %s\n", BACKEND_NAME );
53 53
54 mImpl = new FILEWATCHER_IMPL( this ); 54 mImpl = new FILEWATCHER_IMPL( this );
55 55
56 if ( !mImpl->initOK() ) { 56 if ( !mImpl->initOK() ) {
57 efSAFE_DELETE( mImpl ); 57 efSAFE_DELETE( mImpl );
58 58
59 efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME ); 59 efDEBUG( "Falled back to backend: %s\n", BACKEND_NAME );
60 60
61 mImpl = new FileWatcherGeneric( this ); 61 mImpl = new FileWatcherGeneric( this );
62 } 62 }
63 } 63 }
64} 64}
65 65
66FileWatcher::~FileWatcher() { 66FileWatcher::~FileWatcher() {
67 efSAFE_DELETE( mImpl ); 67 efSAFE_DELETE( mImpl );
68} 68}
69 69
70WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher ) { 70WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher ) {
71 if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) ) { 71 return addWatch( directory, watcher, false, {} );
72 return mImpl->addWatch( directory, watcher, false ); 72}
73 } else { 73
74 return Errors::Log::createLastError( Errors::FileRemote, directory ); 74WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher,
75 } 75 bool recursive ) {
76} 76 return addWatch( directory, watcher, recursive, {} );
77 77}
78WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher, 78
79 bool recursive ) { 79WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher,
80 if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) ) { 80 bool recursive, const std::vector<WatcherOption>& options ) {
81 return mImpl->addWatch( directory, watcher, recursive ); 81 if ( mImpl->mIsGeneric || !FileSystem::isRemoteFS( directory ) ) {
82 } else { 82 return mImpl->addWatch( directory, watcher, recursive, options );
83 return Errors::Log::createLastError( Errors::FileRemote, directory ); 83 } else {
84 } 84 return Errors::Log::createLastError( Errors::FileRemote, directory );
85} 85 }
86 86}
87void FileWatcher::removeWatch( const std::string& directory ) { 87
88 mImpl->removeWatch( directory ); 88void FileWatcher::removeWatch( const std::string& directory ) {
89} 89 mImpl->removeWatch( directory );
90 90}
91void FileWatcher::removeWatch( WatchID watchid ) { 91
92 mImpl->removeWatch( watchid ); 92void FileWatcher::removeWatch( WatchID watchid ) {
93} 93 mImpl->removeWatch( watchid );
94 94}
95void FileWatcher::watch() { 95
96 mImpl->watch(); 96void FileWatcher::watch() {
97} 97 mImpl->watch();
98 98}
99std::list<std::string> FileWatcher::directories() { 99
100 return mImpl->directories(); 100std::vector<std::string> FileWatcher::directories() {
101} 101 return mImpl->directories();
102 102}
103void FileWatcher::followSymlinks( bool follow ) { 103
104 mFollowSymlinks = follow; 104void FileWatcher::followSymlinks( bool follow ) {
105} 105 mFollowSymlinks = follow;
106 106}
107const bool& FileWatcher::followSymlinks() const { 107
108 return mFollowSymlinks; 108const bool& FileWatcher::followSymlinks() const {
109} 109 return mFollowSymlinks;
110 110}
111void FileWatcher::allowOutOfScopeLinks( bool allow ) { 111
112 mOutOfScopeLinks = allow; 112void FileWatcher::allowOutOfScopeLinks( bool allow ) {
113} 113 mOutOfScopeLinks = allow;
114 114}
115const bool& FileWatcher::allowOutOfScopeLinks() const { 115
116 return mOutOfScopeLinks; 116const bool& FileWatcher::allowOutOfScopeLinks() const {
117} 117 return mOutOfScopeLinks;
118 118}
119} // namespace efsw 119
120} // namespace efsw
diff --git a/src/3rdParty/efsw/FileWatcherCWrapper.cpp b/src/3rdParty/efsw/FileWatcherCWrapper.cpp
index 5c49a66..8712d6e 100755..100644
--- a/src/3rdParty/efsw/FileWatcherCWrapper.cpp
+++ b/src/3rdParty/efsw/FileWatcherCWrapper.cpp
@@ -28,12 +28,12 @@ class Watcher_CAPI : public efsw::FileWatchListener {
28 */ 28 */
29static std::vector<Watcher_CAPI*> g_callbacks; 29static std::vector<Watcher_CAPI*> g_callbacks;
30 30
31Watcher_CAPI* find_callback( efsw_watcher watcher, efsw_pfn_fileaction_callback fn ) { 31Watcher_CAPI* find_callback( efsw_watcher watcher, efsw_pfn_fileaction_callback fn, void* param ) {
32 for ( std::vector<Watcher_CAPI*>::iterator i = g_callbacks.begin(); i != g_callbacks.end(); 32 for ( std::vector<Watcher_CAPI*>::iterator i = g_callbacks.begin(); i != g_callbacks.end();
33 ++i ) { 33 ++i ) {
34 Watcher_CAPI* callback = *i; 34 Watcher_CAPI* callback = *i;
35 35
36 if ( callback->mFn == fn && callback->mWatcher == watcher ) 36 if ( callback->mFn == fn && callback->mWatcher == watcher && callback->mParam == param )
37 return *i; 37 return *i;
38 } 38 }
39 39
@@ -71,17 +71,35 @@ const char* efsw_getlasterror() {
71 return log_str.c_str(); 71 return log_str.c_str();
72} 72}
73 73
74EFSW_API void efsw_clearlasterror() {
75 efsw::Errors::Log::clearLastError();
76}
77
74efsw_watchid efsw_addwatch( efsw_watcher watcher, const char* directory, 78efsw_watchid efsw_addwatch( efsw_watcher watcher, const char* directory,
75 efsw_pfn_fileaction_callback callback_fn, int recursive, void* param ) { 79 efsw_pfn_fileaction_callback callback_fn, int recursive, void* param ) {
76 Watcher_CAPI* callback = find_callback( watcher, callback_fn ); 80 return efsw_addwatch_withoptions( watcher, directory, callback_fn, recursive, 0, 0, param );
81}
82
83efsw_watchid efsw_addwatch_withoptions(efsw_watcher watcher, const char* directory,
84 efsw_pfn_fileaction_callback callback_fn, int recursive,
85 efsw_watcher_option *options, int options_number,
86 void* param) {
87 Watcher_CAPI* callback = find_callback( watcher, callback_fn, param );
77 88
78 if ( callback == NULL ) { 89 if ( callback == NULL ) {
79 callback = new Watcher_CAPI( watcher, callback_fn, param ); 90 callback = new Watcher_CAPI( watcher, callback_fn, param );
80 g_callbacks.push_back( callback ); 91 g_callbacks.push_back( callback );
81 } 92 }
82 93
94 std::vector<efsw::WatcherOption> watcher_options{};
95 for ( int i = 0; i < options_number; i++ ) {
96 efsw_watcher_option* option = &options[i];
97 watcher_options.emplace_back( efsw::WatcherOption{
98 static_cast<efsw::Option>(option->option), option->value } );
99 }
100
83 return ( (efsw::FileWatcher*)watcher ) 101 return ( (efsw::FileWatcher*)watcher )
84 ->addWatch( std::string( directory ), callback, TOBOOL( recursive ) ); 102 ->addWatch( std::string( directory ), callback, TOBOOL( recursive ), watcher_options );
85} 103}
86 104
87void efsw_removewatch( efsw_watcher watcher, const char* directory ) { 105void efsw_removewatch( efsw_watcher watcher, const char* directory ) {
diff --git a/src/3rdParty/efsw/FileWatcherFSEvents.cpp b/src/3rdParty/efsw/FileWatcherFSEvents.cpp
index bcfdbe6..70ec2b1 100755..100644
--- a/src/3rdParty/efsw/FileWatcherFSEvents.cpp
+++ b/src/3rdParty/efsw/FileWatcherFSEvents.cpp
@@ -41,6 +41,32 @@ bool FileWatcherFSEvents::isGranular() {
41 return getOSXReleaseNumber() >= 11; 41 return getOSXReleaseNumber() >= 11;
42} 42}
43 43
44static std::string convertCFStringToStdString( CFStringRef cfString ) {
45 // Try to get the C string pointer directly
46 const char* cStr = CFStringGetCStringPtr( cfString, kCFStringEncodingUTF8 );
47
48 if ( cStr ) {
49 // If the pointer is valid, directly return a std::string from it
50 return std::string( cStr );
51 } else {
52 // If not, manually convert it
53 CFIndex length = CFStringGetLength( cfString );
54 CFIndex maxSize = CFStringGetMaximumSizeForEncoding( length, kCFStringEncodingUTF8 ) +
55 1; // +1 for null terminator
56
57 char* buffer = new char[maxSize];
58
59 if ( CFStringGetCString( cfString, buffer, maxSize, kCFStringEncodingUTF8 ) ) {
60 std::string result( buffer );
61 delete[] buffer;
62 return result;
63 } else {
64 delete[] buffer;
65 return "";
66 }
67 }
68}
69
44void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/, void* userData, 70void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/, void* userData,
45 size_t numEvents, void* eventPaths, 71 size_t numEvents, void* eventPaths,
46 const FSEventStreamEventFlags eventFlags[], 72 const FSEventStreamEventFlags eventFlags[],
@@ -51,8 +77,24 @@ void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/,
51 events.reserve( numEvents ); 77 events.reserve( numEvents );
52 78
53 for ( size_t i = 0; i < numEvents; i++ ) { 79 for ( size_t i = 0; i < numEvents; i++ ) {
54 events.push_back( FSEvent( std::string( ( (char**)eventPaths )[i] ), (long)eventFlags[i], 80 if ( isGranular() ) {
55 (Uint64)eventIds[i] ) ); 81 CFDictionaryRef pathInfoDict =
82 static_cast<CFDictionaryRef>( CFArrayGetValueAtIndex( (CFArrayRef)eventPaths, i ) );
83 CFStringRef path = static_cast<CFStringRef>(
84 CFDictionaryGetValue( pathInfoDict, kFSEventStreamEventExtendedDataPathKey ) );
85 CFNumberRef cfInode = static_cast<CFNumberRef>(
86 CFDictionaryGetValue( pathInfoDict, kFSEventStreamEventExtendedFileIDKey ) );
87
88 if ( cfInode ) {
89 unsigned long inode = 0;
90 CFNumberGetValue( cfInode, kCFNumberLongType, &inode );
91 events.push_back( FSEvent( convertCFStringToStdString( path ), (long)eventFlags[i],
92 (Uint64)eventIds[i], inode ) );
93 }
94 } else {
95 events.push_back( FSEvent( std::string( ( (char**)eventPaths )[i] ),
96 (long)eventFlags[i], (Uint64)eventIds[i] ) );
97 }
56 } 98 }
57 99
58 watcher->handleActions( events ); 100 watcher->handleActions( events );
@@ -63,7 +105,7 @@ void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/,
63} 105}
64 106
65FileWatcherFSEvents::FileWatcherFSEvents( FileWatcher* parent ) : 107FileWatcherFSEvents::FileWatcherFSEvents( FileWatcher* parent ) :
66 FileWatcherImpl( parent ), mRunLoopRef( NULL ), mLastWatchID( 0 ), mThread( NULL ) { 108 FileWatcherImpl( parent ), mLastWatchID( 0 ) {
67 mInitOK = true; 109 mInitOK = true;
68 110
69 watch(); 111 watch();
@@ -72,10 +114,7 @@ FileWatcherFSEvents::FileWatcherFSEvents( FileWatcher* parent ) :
72FileWatcherFSEvents::~FileWatcherFSEvents() { 114FileWatcherFSEvents::~FileWatcherFSEvents() {
73 mInitOK = false; 115 mInitOK = false;
74 116
75 if ( mRunLoopRef.load() ) 117 mWatchCond.notify_all();
76 CFRunLoopStop( mRunLoopRef.load() );
77
78 efSAFE_DELETE( mThread );
79 118
80 WatchMap::iterator iter = mWatches.begin(); 119 WatchMap::iterator iter = mWatches.begin();
81 120
@@ -84,18 +123,11 @@ FileWatcherFSEvents::~FileWatcherFSEvents() {
84 123
85 efSAFE_DELETE( watch ); 124 efSAFE_DELETE( watch );
86 } 125 }
87
88 mWatches.clear();
89} 126}
90 127
91WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchListener* watcher, 128WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchListener* watcher,
92 bool recursive ) { 129 bool recursive, const std::vector<WatcherOption>& options ) {
93 /// Wait to the RunLoopRef to be ready 130 std::string dir( FileSystem::getRealPath( directory ) );
94 while ( NULL == mRunLoopRef.load() ) {
95 System::sleep( 1 );
96 }
97
98 std::string dir( directory );
99 131
100 FileInfo fi( dir ); 132 FileInfo fi( dir );
101 133
@@ -135,12 +167,18 @@ WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchLi
135 pWatch->Directory = dir; 167 pWatch->Directory = dir;
136 pWatch->Recursive = recursive; 168 pWatch->Recursive = recursive;
137 pWatch->FWatcher = this; 169 pWatch->FWatcher = this;
170 pWatch->ModifiedFlags =
171 getOptionValue( options, Option::MacModifiedFilter, efswFSEventsModified );
172 pWatch->SanitizeEvents = getOptionValue( options, Option::MacSanitizeEvents, 0 ) != 0;
138 173
139 pWatch->init(); 174 pWatch->init();
140 175
141 Lock lock( mWatchesLock ); 176 {
142 mWatches.insert( std::make_pair( mLastWatchID, pWatch ) ); 177 Lock lock( mWatchesLock );
178 mWatches.insert( std::make_pair( mLastWatchID, pWatch ) );
179 }
143 180
181 mWatchCond.notify_all();
144 return pWatch->ID; 182 return pWatch->ID;
145} 183}
146 184
@@ -174,50 +212,20 @@ void FileWatcherFSEvents::removeWatch( WatchID watchid ) {
174 efSAFE_DELETE( watch ); 212 efSAFE_DELETE( watch );
175} 213}
176 214
177void FileWatcherFSEvents::watch() { 215void FileWatcherFSEvents::watch() {}
178 if ( NULL == mThread ) {
179 mThread = new Thread( &FileWatcherFSEvents::run, this );
180 mThread->launch();
181 }
182}
183
184void FileWatcherFSEvents::run() {
185 mRunLoopRef = CFRunLoopGetCurrent();
186
187 while ( mInitOK ) {
188 mNeedInitMutex.lock();
189
190 if ( !mNeedInit.empty() ) {
191 for ( std::vector<WatcherFSEvents*>::iterator it = mNeedInit.begin();
192 it != mNeedInit.end(); ++it ) {
193 ( *it )->initAsync();
194 }
195
196 mNeedInit.clear();
197 }
198
199 mNeedInitMutex.unlock();
200
201 if ( mWatches.empty() ) {
202 System::sleep( 100 );
203 } else {
204 CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0.5, kCFRunLoopRunTimedOut );
205 }
206 }
207
208 mRunLoopRef = NULL;
209}
210 216
211void FileWatcherFSEvents::handleAction( Watcher* /*watch*/, const std::string& /*filename*/, 217void FileWatcherFSEvents::handleAction( Watcher* /*watch*/, const std::string& /*filename*/,
212 unsigned long /*action*/, std::string /*oldFilename*/ ) { 218 unsigned long /*action*/, std::string /*oldFilename*/ ) {
213 /// Not used 219 /// Not used
214} 220}
215 221
216std::list<std::string> FileWatcherFSEvents::directories() { 222std::vector<std::string> FileWatcherFSEvents::directories() {
217 std::list<std::string> dirs; 223 std::vector<std::string> dirs;
218 224
219 Lock lock( mWatchesLock ); 225 Lock lock( mWatchesLock );
220 226
227 dirs.reserve( mWatches.size() );
228
221 for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { 229 for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
222 dirs.push_back( std::string( it->second->Directory ) ); 230 dirs.push_back( std::string( it->second->Directory ) );
223 } 231 }
diff --git a/src/3rdParty/efsw/FileWatcherFSEvents.hpp b/src/3rdParty/efsw/FileWatcherFSEvents.hpp
index 5279847..daa538c 100755..100644
--- a/src/3rdParty/efsw/FileWatcherFSEvents.hpp
+++ b/src/3rdParty/efsw/FileWatcherFSEvents.hpp
@@ -7,33 +7,15 @@
7 7
8#include <CoreFoundation/CoreFoundation.h> 8#include <CoreFoundation/CoreFoundation.h>
9#include <CoreServices/CoreServices.h> 9#include <CoreServices/CoreServices.h>
10#include <dispatch/dispatch.h>
10#include <efsw/WatcherFSEvents.hpp> 11#include <efsw/WatcherFSEvents.hpp>
11#include <list>
12#include <map> 12#include <map>
13#include <vector> 13#include <vector>
14#include <condition_variable>
15#include <mutex>
14 16
15namespace efsw { 17namespace efsw {
16 18
17/* OSX < 10.7 has no file events */
18/* So i declare the events constants */
19enum FSEventEvents {
20 efswFSEventStreamCreateFlagFileEvents = 0x00000010,
21 efswFSEventStreamEventFlagItemCreated = 0x00000100,
22 efswFSEventStreamEventFlagItemRemoved = 0x00000200,
23 efswFSEventStreamEventFlagItemInodeMetaMod = 0x00000400,
24 efswFSEventStreamEventFlagItemRenamed = 0x00000800,
25 efswFSEventStreamEventFlagItemModified = 0x00001000,
26 efswFSEventStreamEventFlagItemFinderInfoMod = 0x00002000,
27 efswFSEventStreamEventFlagItemChangeOwner = 0x00004000,
28 efswFSEventStreamEventFlagItemXattrMod = 0x00008000,
29 efswFSEventStreamEventFlagItemIsFile = 0x00010000,
30 efswFSEventStreamEventFlagItemIsDir = 0x00020000,
31 efswFSEventStreamEventFlagItemIsSymlink = 0x00040000,
32 efswFSEventsModified = efswFSEventStreamEventFlagItemFinderInfoMod |
33 efswFSEventStreamEventFlagItemModified |
34 efswFSEventStreamEventFlagItemInodeMetaMod
35};
36
37/// Implementation for Win32 based on ReadDirectoryChangesW. 19/// Implementation for Win32 based on ReadDirectoryChangesW.
38/// @class FileWatcherFSEvents 20/// @class FileWatcherFSEvents
39class FileWatcherFSEvents : public FileWatcherImpl { 21class FileWatcherFSEvents : public FileWatcherImpl {
@@ -52,48 +34,43 @@ class FileWatcherFSEvents : public FileWatcherImpl {
52 34
53 /// Add a directory watch 35 /// Add a directory watch
54 /// On error returns WatchID with Error type. 36 /// On error returns WatchID with Error type.
55 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); 37 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
38 const std::vector<WatcherOption> &options ) override;
56 39
57 /// Remove a directory watch. This is a brute force lazy search O(nlogn). 40 /// Remove a directory watch. This is a brute force lazy search O(nlogn).
58 void removeWatch( const std::string& directory ); 41 void removeWatch( const std::string& directory ) override;
59 42
60 /// Remove a directory watch. This is a map lookup O(logn). 43 /// Remove a directory watch. This is a map lookup O(logn).
61 void removeWatch( WatchID watchid ); 44 void removeWatch( WatchID watchid ) override;
62 45
63 /// Updates the watcher. Must be called often. 46 /// Updates the watcher. Must be called often.
64 void watch(); 47 void watch() override;
65 48
66 /// Handles the action 49 /// Handles the action
67 void handleAction( Watcher* watch, const std::string& filename, unsigned long action, 50 void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
68 std::string oldFilename = "" ); 51 std::string oldFilename = "" ) override;
69 52
70 /// @return Returns a list of the directories that are being watched 53 /// @return Returns a list of the directories that are being watched
71 std::list<std::string> directories(); 54 std::vector<std::string> directories() override;
72 55
73 protected: 56 protected:
74 static void FSEventCallback( ConstFSEventStreamRef streamRef, void* userData, size_t numEvents, 57 static void FSEventCallback( ConstFSEventStreamRef streamRef, void* userData, size_t numEvents,
75 void* eventPaths, const FSEventStreamEventFlags eventFlags[], 58 void* eventPaths, const FSEventStreamEventFlags eventFlags[],
76 const FSEventStreamEventId eventIds[] ); 59 const FSEventStreamEventId eventIds[] );
77 60
78 Atomic<CFRunLoopRef> mRunLoopRef;
79
80 /// Vector of WatcherWin32 pointers 61 /// Vector of WatcherWin32 pointers
81 WatchMap mWatches; 62 WatchMap mWatches;
82 63
83 /// The last watchid 64 /// The last watchid
84 WatchID mLastWatchID; 65 WatchID mLastWatchID;
85 66
86 Thread* mThread;
87
88 Mutex mWatchesLock; 67 Mutex mWatchesLock;
89 68
90 bool pathInWatches( const std::string& path ); 69 bool pathInWatches( const std::string& path ) override;
91 70
92 std::vector<WatcherFSEvents*> mNeedInit; 71 std::mutex mWatchesMutex;
93 Mutex mNeedInitMutex; 72 std::condition_variable mWatchCond;
94 73
95 private:
96 void run();
97}; 74};
98 75
99} // namespace efsw 76} // namespace efsw
diff --git a/src/3rdParty/efsw/FileWatcherGeneric.cpp b/src/3rdParty/efsw/FileWatcherGeneric.cpp
index 074cff1..468d27c 100755..100644
--- a/src/3rdParty/efsw/FileWatcherGeneric.cpp
+++ b/src/3rdParty/efsw/FileWatcherGeneric.cpp
@@ -25,7 +25,7 @@ FileWatcherGeneric::~FileWatcherGeneric() {
25} 25}
26 26
27WatchID FileWatcherGeneric::addWatch( const std::string& directory, FileWatchListener* watcher, 27WatchID FileWatcherGeneric::addWatch( const std::string& directory, FileWatchListener* watcher,
28 bool recursive ) { 28 bool recursive, const std::vector<WatcherOption>& /*options*/ ) {
29 std::string dir( directory ); 29 std::string dir( directory );
30 30
31 FileSystem::dirAddSlashAtEnd( dir ); 31 FileSystem::dirAddSlashAtEnd( dir );
@@ -127,11 +127,13 @@ void FileWatcherGeneric::handleAction( Watcher*, const std::string&, unsigned lo
127 /// Not used 127 /// Not used
128} 128}
129 129
130std::list<std::string> FileWatcherGeneric::directories() { 130std::vector<std::string> FileWatcherGeneric::directories() {
131 std::list<std::string> dirs; 131 std::vector<std::string> dirs;
132 132
133 Lock lock( mWatchesLock ); 133 Lock lock( mWatchesLock );
134 134
135 dirs.reserve( mWatches.size() );
136
135 WatchList::iterator it = mWatches.begin(); 137 WatchList::iterator it = mWatches.begin();
136 138
137 for ( ; it != mWatches.end(); ++it ) { 139 for ( ; it != mWatches.end(); ++it ) {
diff --git a/src/3rdParty/efsw/FileWatcherGeneric.hpp b/src/3rdParty/efsw/FileWatcherGeneric.hpp
index 4cb0b67..47f7e04 100755..100644
--- a/src/3rdParty/efsw/FileWatcherGeneric.hpp
+++ b/src/3rdParty/efsw/FileWatcherGeneric.hpp
@@ -4,7 +4,7 @@
4#include <efsw/DirWatcherGeneric.hpp> 4#include <efsw/DirWatcherGeneric.hpp>
5#include <efsw/FileWatcherImpl.hpp> 5#include <efsw/FileWatcherImpl.hpp>
6#include <efsw/WatcherGeneric.hpp> 6#include <efsw/WatcherGeneric.hpp>
7#include <list> 7#include <vector>
8 8
9namespace efsw { 9namespace efsw {
10 10
@@ -12,7 +12,7 @@ namespace efsw {
12/// @class FileWatcherGeneric 12/// @class FileWatcherGeneric
13class FileWatcherGeneric : public FileWatcherImpl { 13class FileWatcherGeneric : public FileWatcherImpl {
14 public: 14 public:
15 typedef std::list<WatcherGeneric*> WatchList; 15 typedef std::vector<WatcherGeneric*> WatchList;
16 16
17 FileWatcherGeneric( FileWatcher* parent ); 17 FileWatcherGeneric( FileWatcher* parent );
18 18
@@ -20,23 +20,24 @@ class FileWatcherGeneric : public FileWatcherImpl {
20 20
21 /// Add a directory watch 21 /// Add a directory watch
22 /// On error returns WatchID with Error type. 22 /// On error returns WatchID with Error type.
23 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); 23 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
24 const std::vector<WatcherOption> &options ) override;
24 25
25 /// Remove a directory watch. This is a brute force lazy search O(nlogn). 26 /// Remove a directory watch. This is a brute force lazy search O(nlogn).
26 void removeWatch( const std::string& directory ); 27 void removeWatch( const std::string& directory ) override;
27 28
28 /// Remove a directory watch. This is a map lookup O(logn). 29 /// Remove a directory watch. This is a map lookup O(logn).
29 void removeWatch( WatchID watchid ); 30 void removeWatch( WatchID watchid ) override;
30 31
31 /// Updates the watcher. Must be called often. 32 /// Updates the watcher. Must be called often.
32 void watch(); 33 void watch() override;
33 34
34 /// Handles the action 35 /// Handles the action
35 void handleAction( Watcher* watch, const std::string& filename, unsigned long action, 36 void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
36 std::string oldFilename = "" ); 37 std::string oldFilename = "" ) override;
37 38
38 /// @return Returns a list of the directories that are being watched 39 /// @return Returns a list of the directories that are being watched
39 std::list<std::string> directories(); 40 std::vector<std::string> directories() override;
40 41
41 protected: 42 protected:
42 Thread* mThread; 43 Thread* mThread;
@@ -49,7 +50,7 @@ class FileWatcherGeneric : public FileWatcherImpl {
49 50
50 Mutex mWatchesLock; 51 Mutex mWatchesLock;
51 52
52 bool pathInWatches( const std::string& path ); 53 bool pathInWatches( const std::string& path ) override;
53 54
54 private: 55 private:
55 void run(); 56 void run();
diff --git a/src/3rdParty/efsw/FileWatcherImpl.cpp b/src/3rdParty/efsw/FileWatcherImpl.cpp
index f6b86a5..bf69a45 100755..100644
--- a/src/3rdParty/efsw/FileWatcherImpl.cpp
+++ b/src/3rdParty/efsw/FileWatcherImpl.cpp
@@ -1,23 +1,34 @@
1#include <efsw/FileWatcherImpl.hpp> 1#include <efsw/FileWatcherImpl.hpp>
2#include <efsw/String.hpp> 2#include <efsw/String.hpp>
3#include <efsw/System.hpp> 3#include <efsw/System.hpp>
4 4
5namespace efsw { 5namespace efsw {
6 6
7FileWatcherImpl::FileWatcherImpl( FileWatcher* parent ) : 7FileWatcherImpl::FileWatcherImpl( FileWatcher* parent ) :
8 mFileWatcher( parent ), mInitOK( false ), mIsGeneric( false ) { 8 mFileWatcher( parent ), mInitOK( false ), mIsGeneric( false ) {
9 System::maxFD(); 9 System::maxFD();
10} 10}
11 11
12FileWatcherImpl::~FileWatcherImpl() {} 12FileWatcherImpl::~FileWatcherImpl() {}
13 13
14bool FileWatcherImpl::initOK() { 14bool FileWatcherImpl::initOK() {
15 return static_cast<bool>( mInitOK ); 15 return static_cast<bool>( mInitOK );
16} 16}
17 17
18bool FileWatcherImpl::linkAllowed( const std::string& curPath, const std::string& link ) { 18bool FileWatcherImpl::linkAllowed( const std::string& curPath, const std::string& link ) {
19 return ( mFileWatcher->followSymlinks() && mFileWatcher->allowOutOfScopeLinks() ) || 19 return ( mFileWatcher->followSymlinks() && mFileWatcher->allowOutOfScopeLinks() ) ||
20 -1 != String::strStartsWith( curPath, link ); 20 -1 != String::strStartsWith( curPath, link );
21} 21}
22 22
23} // namespace efsw 23int FileWatcherImpl::getOptionValue( const std::vector<WatcherOption>& options, Option option,
24 int defaultValue ) {
25 for ( size_t i = 0; i < options.size(); i++ ) {
26 if ( options[i].mOption == option ) {
27 return options[i].mValue;
28 }
29 }
30
31 return defaultValue;
32}
33
34} // namespace efsw
diff --git a/src/3rdParty/efsw/FileWatcherImpl.hpp b/src/3rdParty/efsw/FileWatcherImpl.hpp
index ea1beb8..a6ec472 100755..100644
--- a/src/3rdParty/efsw/FileWatcherImpl.hpp
+++ b/src/3rdParty/efsw/FileWatcherImpl.hpp
@@ -1,57 +1,64 @@
1#ifndef EFSW_FILEWATCHERIMPL_HPP 1#ifndef EFSW_FILEWATCHERIMPL_HPP
2#define EFSW_FILEWATCHERIMPL_HPP 2#define EFSW_FILEWATCHERIMPL_HPP
3 3
4#include <efsw/Atomic.hpp> 4#include <efsw/Atomic.hpp>
5#include <efsw/Mutex.hpp> 5#include <efsw/Mutex.hpp>
6#include <efsw/Thread.hpp> 6#include <efsw/Thread.hpp>
7#include <efsw/Watcher.hpp> 7#include <efsw/Watcher.hpp>
8#include <efsw/base.hpp> 8#include <efsw/base.hpp>
9#include <efsw/efsw.hpp> 9#include <efsw/efsw.hpp>
10 10
11namespace efsw { 11namespace efsw {
12 12
13class FileWatcherImpl { 13class FileWatcherImpl {
14 public: 14 public:
15 FileWatcherImpl( FileWatcher* parent ); 15 FileWatcherImpl( FileWatcher* parent );
16 16
17 virtual ~FileWatcherImpl(); 17 virtual ~FileWatcherImpl();
18 18
19 /// Add a directory watch 19 /// Add a directory watch
20 /// On error returns WatchID with Error type. 20 /// On error returns WatchID with Error type.
21 virtual WatchID addWatch( const std::string& directory, FileWatchListener* watcher, 21 virtual WatchID addWatch( const std::string& directory, FileWatchListener* watcher,
22 bool recursive ) = 0; 22 bool recursive, const std::vector<WatcherOption>& options = {} ) = 0;
23 23
24 /// Remove a directory watch. This is a brute force lazy search O(nlogn). 24 /// Remove a directory watch. This is a brute force lazy search O(nlogn).
25 virtual void removeWatch( const std::string& directory ) = 0; 25 virtual void removeWatch( const std::string& directory ) = 0;
26 26
27 /// Remove a directory watch. This is a map lookup O(logn). 27 /// Remove a directory watch. This is a map lookup O(logn).
28 virtual void removeWatch( WatchID watchid ) = 0; 28 virtual void removeWatch( WatchID watchid ) = 0;
29 29
30 /// Updates the watcher. Must be called often. 30 /// Updates the watcher. Must be called often.
31 virtual void watch() = 0; 31 virtual void watch() = 0;
32 32
33 /// Handles the action 33 /// Handles the action
34 virtual void handleAction( Watcher* watch, const std::string& filename, unsigned long action, 34 virtual void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
35 std::string oldFilename = "" ) = 0; 35 std::string oldFilename = "" ) = 0;
36 36
37 /// @return Returns a list of the directories that are being watched 37 /// @return Returns a list of the directories that are being watched
38 virtual std::list<std::string> directories() = 0; 38 virtual std::vector<std::string> directories() = 0;
39 39
40 /// @return true if the backend init successfully 40 /// @return true if the backend init successfully
41 virtual bool initOK(); 41 virtual bool initOK();
42 42
43 /// @return If the link is allowed according to the current path and the state of out scope 43 /// @return If the link is allowed according to the current path and the state of out scope
44 /// links 44 /// links
45 virtual bool linkAllowed( const std::string& curPath, const std::string& link ); 45 virtual bool linkAllowed( const std::string& curPath, const std::string& link );
46 46
47 /// Search if a directory already exists in the watches 47 /// Search if a directory already exists in the watches
48 virtual bool pathInWatches( const std::string& path ) = 0; 48 virtual bool pathInWatches( const std::string& path ) = 0;
49 49
50 FileWatcher* mFileWatcher; 50 protected:
51 Atomic<bool> mInitOK; 51 friend class FileWatcher;
52 bool mIsGeneric; 52 friend class DirWatcherGeneric;
53}; 53
54 54 FileWatcher* mFileWatcher;
55} // namespace efsw 55 Atomic<bool> mInitOK;
56 56 bool mIsGeneric;
57#endif 57
58 int getOptionValue( const std::vector<WatcherOption>& options, Option option,
59 int defaultValue );
60};
61
62} // namespace efsw
63
64#endif
diff --git a/src/3rdParty/efsw/FileWatcherInotify.cpp b/src/3rdParty/efsw/FileWatcherInotify.cpp
index e0da76b..1ec3d48 100755..100644
--- a/src/3rdParty/efsw/FileWatcherInotify.cpp
+++ b/src/3rdParty/efsw/FileWatcherInotify.cpp
@@ -1,3 +1,4 @@
1#include <algorithm>
1#include <efsw/FileWatcherInotify.hpp> 2#include <efsw/FileWatcherInotify.hpp>
2 3
3#if EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY 4#if EFSW_PLATFORM == EFSW_PLATFORM_INOTIFY
@@ -26,7 +27,7 @@
26namespace efsw { 27namespace efsw {
27 28
28FileWatcherInotify::FileWatcherInotify( FileWatcher* parent ) : 29FileWatcherInotify::FileWatcherInotify( FileWatcher* parent ) :
29 FileWatcherImpl( parent ), mFD( -1 ), mThread( NULL ) { 30 FileWatcherImpl( parent ), mFD( -1 ), mThread( NULL ), mIsTakingAction( false ) {
30 mFD = inotify_init(); 31 mFD = inotify_init();
31 32
32 if ( mFD < 0 ) { 33 if ( mFD < 0 ) {
@@ -38,13 +39,20 @@ FileWatcherInotify::FileWatcherInotify( FileWatcher* parent ) :
38 39
39FileWatcherInotify::~FileWatcherInotify() { 40FileWatcherInotify::~FileWatcherInotify() {
40 mInitOK = false; 41 mInitOK = false;
41 42 // There is deadlock when release FileWatcherInotify instance since its handAction
43 // function is still running and hangs in requiring lock without init lock captured.
44 while ( mIsTakingAction ) {
45 // It'd use condition-wait instead of sleep. Actually efsw has no such
46 // implementation so we just skip and sleep while for that to avoid deadlock.
47 usleep( 1000 );
48 };
42 Lock initLock( mInitLock ); 49 Lock initLock( mInitLock );
43 50
44 efSAFE_DELETE( mThread ); 51 efSAFE_DELETE( mThread );
45 52
46 Lock l( mWatchesLock ); 53 Lock l( mWatchesLock );
47 Lock l2( mRealWatchesLock ); 54 Lock l2( mRealWatchesLock );
55
48 WatchMap::iterator iter = mWatches.begin(); 56 WatchMap::iterator iter = mWatches.begin();
49 WatchMap::iterator end = mWatches.end(); 57 WatchMap::iterator end = mWatches.end();
50 58
@@ -61,15 +69,17 @@ FileWatcherInotify::~FileWatcherInotify() {
61} 69}
62 70
63WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, 71WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher,
64 bool recursive ) { 72 bool recursive, const std::vector<WatcherOption>& options ) {
65 if ( !mInitOK ) 73 if ( !mInitOK )
66 return Errors::Log::createLastError( Errors::Unspecified, directory ); 74 return Errors::Log::createLastError( Errors::Unspecified, directory );
67 Lock initLock( mInitLock ); 75 Lock initLock( mInitLock );
68 return addWatch( directory, watcher, recursive, NULL ); 76 bool syntheticEvents = getOptionValue( options, Options::LinuxProduceSyntheticEvents, 0 ) != 0;
77 return addWatch( directory, watcher, recursive, syntheticEvents, NULL );
69} 78}
70 79
71WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, 80WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher,
72 bool recursive, WatcherInotify* parent ) { 81 bool recursive, bool syntheticEvents,
82 WatcherInotify* parent, bool fromInternalEvent ) {
73 std::string dir( directory ); 83 std::string dir( directory );
74 84
75 FileSystem::dirAddSlashAtEnd( dir ); 85 FileSystem::dirAddSlashAtEnd( dir );
@@ -133,6 +143,7 @@ WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchLis
133 { 143 {
134 Lock lock( mWatchesLock ); 144 Lock lock( mWatchesLock );
135 mWatches.insert( std::make_pair( wd, pWatch ) ); 145 mWatches.insert( std::make_pair( wd, pWatch ) );
146 mWatchesRef[pWatch->Directory] = wd;
136 } 147 }
137 148
138 if ( NULL == pWatch->Parent ) { 149 if ( NULL == pWatch->Parent ) {
@@ -151,7 +162,17 @@ WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchLis
151 const FileInfo& cfi = it->second; 162 const FileInfo& cfi = it->second;
152 163
153 if ( cfi.isDirectory() && cfi.isReadable() ) { 164 if ( cfi.isDirectory() && cfi.isReadable() ) {
154 addWatch( cfi.Filepath, watcher, recursive, pWatch ); 165 addWatch( cfi.Filepath, watcher, recursive, syntheticEvents, pWatch );
166 }
167 }
168
169 if ( fromInternalEvent && parent != NULL && syntheticEvents ) {
170 for ( const auto& file : files ) {
171 if ( file.second.isRegularFile() ) {
172 pWatch->Listener->handleFileAction(
173 pWatch->ID, pWatch->Directory,
174 FileSystem::fileNameFromPath( file.second.Filepath ), Actions::Add );
175 }
155 } 176 }
156 } 177 }
157 } 178 }
@@ -161,6 +182,8 @@ WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchLis
161 182
162void FileWatcherInotify::removeWatchLocked( WatchID watchid ) { 183void FileWatcherInotify::removeWatchLocked( WatchID watchid ) {
163 WatchMap::iterator iter = mWatches.find( watchid ); 184 WatchMap::iterator iter = mWatches.find( watchid );
185 if ( iter == mWatches.end() )
186 return;
164 187
165 WatcherInotify* watch = iter->second; 188 WatcherInotify* watch = iter->second;
166 189
@@ -173,22 +196,21 @@ void FileWatcherInotify::removeWatchLocked( WatchID watchid ) {
173 } 196 }
174 } 197 }
175 198
176 if ( watch->Recursive ) { 199 if ( watch->Recursive && NULL == watch->Parent ) {
177 WatchMap::iterator it = mWatches.begin(); 200 WatchMap::iterator it = mWatches.begin();
178 std::list<WatchID> eraseWatches; 201 std::vector<WatchID> eraseWatches;
179 202
180 for ( ; it != mWatches.end(); ++it ) { 203 for ( ; it != mWatches.end(); ++it )
181 if ( it->second != watch && it->second->inParentTree( watch ) ) { 204 if ( it->second != watch && it->second->inParentTree( watch ) )
182 eraseWatches.push_back( it->second->InotifyID ); 205 eraseWatches.push_back( it->second->InotifyID );
183 }
184 }
185 206
186 for ( std::list<WatchID>::iterator eit = eraseWatches.begin(); eit != eraseWatches.end(); 207 for ( std::vector<WatchID>::iterator eit = eraseWatches.begin(); eit != eraseWatches.end();
187 ++eit ) { 208 ++eit ) {
188 removeWatch( *eit ); 209 removeWatch( *eit );
189 } 210 }
190 } 211 }
191 212
213 mWatchesRef.erase( watch->Directory );
192 mWatches.erase( iter ); 214 mWatches.erase( iter );
193 215
194 if ( NULL == watch->Parent ) { 216 if ( NULL == watch->Parent ) {
@@ -217,52 +239,11 @@ void FileWatcherInotify::removeWatch( const std::string& directory ) {
217 Lock lock( mWatchesLock ); 239 Lock lock( mWatchesLock );
218 Lock l( mRealWatchesLock ); 240 Lock l( mRealWatchesLock );
219 241
220 WatchMap::iterator iter = mWatches.begin(); 242 std::unordered_map<std::string, WatchID>::iterator ref = mWatchesRef.find( directory );
221 243 if ( ref == mWatchesRef.end() )
222 for ( ; iter != mWatches.end(); ++iter ) { 244 return;
223 if ( directory == iter->second->Directory ) {
224 WatcherInotify* watch = iter->second;
225
226 if ( watch->Recursive ) {
227 WatchMap::iterator it = mWatches.begin();
228 std::list<WatchID> eraseWatches;
229
230 for ( ; it != mWatches.end(); ++it ) {
231 if ( it->second->inParentTree( watch ) ) {
232 eraseWatches.push_back( it->second->InotifyID );
233 }
234 }
235
236 for ( std::list<WatchID>::iterator eit = eraseWatches.begin();
237 eit != eraseWatches.end(); ++eit ) {
238 removeWatchLocked( *eit );
239 }
240 }
241
242 mWatches.erase( iter );
243
244 if ( NULL == watch->Parent ) {
245 WatchMap::iterator eraseit = mRealWatches.find( watch->InotifyID );
246
247 if ( eraseit != mRealWatches.end() ) {
248 mRealWatches.erase( eraseit );
249 }
250 }
251
252 int err = inotify_rm_watch( mFD, watch->InotifyID );
253
254 if ( err < 0 ) {
255 efDEBUG( "Error removing watch %d: %s\n", watch->InotifyID, strerror( errno ) );
256 } else {
257 efDEBUG( "Removed watch %s with id: %d\n", watch->Directory.c_str(),
258 watch->InotifyID );
259 }
260
261 efSAFE_DELETE( watch );
262 245
263 break; 246 removeWatchLocked( ref->second );
264 }
265 }
266} 247}
267 248
268void FileWatcherInotify::removeWatch( WatchID watchid ) { 249void FileWatcherInotify::removeWatch( WatchID watchid ) {
@@ -270,13 +251,6 @@ void FileWatcherInotify::removeWatch( WatchID watchid ) {
270 return; 251 return;
271 Lock initLock( mInitLock ); 252 Lock initLock( mInitLock );
272 Lock lock( mWatchesLock ); 253 Lock lock( mWatchesLock );
273
274 WatchMap::iterator iter = mWatches.find( watchid );
275
276 if ( iter == mWatches.end() ) {
277 return;
278 }
279
280 removeWatchLocked( watchid ); 254 removeWatchLocked( watchid );
281} 255}
282 256
@@ -295,10 +269,8 @@ Watcher* FileWatcherInotify::watcherContainsDirectory( std::string dir ) {
295 269
296 for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { 270 for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
297 Watcher* watcher = it->second; 271 Watcher* watcher = it->second;
298 272 if ( watcher->Directory == watcherPath )
299 if ( watcher->Directory == watcherPath ) {
300 return watcher; 273 return watcher;
301 }
302 } 274 }
303 275
304 return NULL; 276 return NULL;
@@ -422,7 +394,7 @@ void FileWatcherInotify::run() {
422 const std::string& oldFileName = ( *it ).second; 394 const std::string& oldFileName = ( *it ).second;
423 395
424 /// Check if the file move was a folder already being watched 396 /// Check if the file move was a folder already being watched
425 std::list<Watcher*> eraseWatches; 397 std::vector<Watcher*> eraseWatches;
426 398
427 { 399 {
428 Lock lock( mWatchesLock ); 400 Lock lock( mWatchesLock );
@@ -439,12 +411,15 @@ void FileWatcherInotify::run() {
439 } 411 }
440 412
441 /// Remove invalid watches 413 /// Remove invalid watches
442 eraseWatches.sort(); 414 std::stable_sort( eraseWatches.begin(), eraseWatches.end(),
415 []( const Watcher* left, const Watcher* right ) {
416 return left->Directory < right->Directory;
417 } );
443 418
444 if ( eraseWatches.empty() ) { 419 if ( eraseWatches.empty() ) {
445 handleAction( watch, oldFileName, IN_DELETE ); 420 handleAction( watch, oldFileName, IN_DELETE );
446 } else { 421 } else {
447 for ( std::list<Watcher*>::reverse_iterator eit = eraseWatches.rbegin(); 422 for ( std::vector<Watcher*>::reverse_iterator eit = eraseWatches.rbegin();
448 eit != eraseWatches.rend(); ++eit ) { 423 eit != eraseWatches.rend(); ++eit ) {
449 Watcher* rmWatch = *eit; 424 Watcher* rmWatch = *eit;
450 425
@@ -485,8 +460,9 @@ void FileWatcherInotify::checkForNewWatcher( Watcher* watch, std::string fpath )
485 } 460 }
486 461
487 if ( !found ) { 462 if ( !found ) {
488 addWatch( fpath, watch->Listener, watch->Recursive, 463 WatcherInotify* iWatch = static_cast<WatcherInotify*>( watch );
489 static_cast<WatcherInotify*>( watch ) ); 464 addWatch( fpath, watch->Listener, watch->Recursive, iWatch->syntheticEvents,
465 static_cast<WatcherInotify*>( watch ), true );
490 } 466 }
491 } 467 }
492} 468}
@@ -496,7 +472,7 @@ void FileWatcherInotify::handleAction( Watcher* watch, const std::string& filena
496 if ( !watch || !watch->Listener || !mInitOK ) { 472 if ( !watch || !watch->Listener || !mInitOK ) {
497 return; 473 return;
498 } 474 }
499 475 mIsTakingAction = true;
500 Lock initLock( mInitLock ); 476 Lock initLock( mInitLock );
501 477
502 std::string fpath( watch->Directory + filename ); 478 std::string fpath( watch->Directory + filename );
@@ -563,18 +539,20 @@ void FileWatcherInotify::handleAction( Watcher* watch, const std::string& filena
563 } 539 }
564 } 540 }
565 } 541 }
542 mIsTakingAction = false;
566} 543}
567 544
568std::list<std::string> FileWatcherInotify::directories() { 545std::vector<std::string> FileWatcherInotify::directories() {
569 std::list<std::string> dirs; 546 std::vector<std::string> dirs;
570 547
571 Lock l( mRealWatchesLock ); 548 Lock l( mRealWatchesLock );
572 549
550 dirs.reserve( mRealWatches.size() );
551
573 WatchMap::iterator it = mRealWatches.begin(); 552 WatchMap::iterator it = mRealWatches.begin();
574 553
575 for ( ; it != mRealWatches.end(); ++it ) { 554 for ( ; it != mRealWatches.end(); ++it )
576 dirs.push_back( it->second->Directory ); 555 dirs.push_back( it->second->Directory );
577 }
578 556
579 return dirs; 557 return dirs;
580} 558}
@@ -585,11 +563,9 @@ bool FileWatcherInotify::pathInWatches( const std::string& path ) {
585 /// Search in the real watches, since it must allow adding a watch already watched as a subdir 563 /// Search in the real watches, since it must allow adding a watch already watched as a subdir
586 WatchMap::iterator it = mRealWatches.begin(); 564 WatchMap::iterator it = mRealWatches.begin();
587 565
588 for ( ; it != mRealWatches.end(); ++it ) { 566 for ( ; it != mRealWatches.end(); ++it )
589 if ( it->second->Directory == path ) { 567 if ( it->second->Directory == path )
590 return true; 568 return true;
591 }
592 }
593 569
594 return false; 570 return false;
595} 571}
diff --git a/src/3rdParty/efsw/FileWatcherInotify.hpp b/src/3rdParty/efsw/FileWatcherInotify.hpp
index dc922ac..26d2c0b 100755..100644
--- a/src/3rdParty/efsw/FileWatcherInotify.hpp
+++ b/src/3rdParty/efsw/FileWatcherInotify.hpp
@@ -7,6 +7,7 @@
7 7
8#include <efsw/WatcherInotify.hpp> 8#include <efsw/WatcherInotify.hpp>
9#include <map> 9#include <map>
10#include <unordered_map>
10#include <vector> 11#include <vector>
11 12
12namespace efsw { 13namespace efsw {
@@ -24,23 +25,24 @@ class FileWatcherInotify : public FileWatcherImpl {
24 25
25 /// Add a directory watch 26 /// Add a directory watch
26 /// On error returns WatchID with Error type. 27 /// On error returns WatchID with Error type.
27 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); 28 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
29 const std::vector<WatcherOption>& options ) override;
28 30
29 /// Remove a directory watch. This is a brute force lazy search O(nlogn). 31 /// Remove a directory watch. This is a brute force lazy search O(nlogn).
30 void removeWatch( const std::string& directory ); 32 void removeWatch( const std::string& directory ) override;
31 33
32 /// Remove a directory watch. This is a map lookup O(logn). 34 /// Remove a directory watch. This is a map lookup O(logn).
33 void removeWatch( WatchID watchid ); 35 void removeWatch( WatchID watchid ) override;
34 36
35 /// Updates the watcher. Must be called often. 37 /// Updates the watcher. Must be called often.
36 void watch(); 38 void watch() override;
37 39
38 /// Handles the action 40 /// Handles the action
39 void handleAction( Watcher* watch, const std::string& filename, unsigned long action, 41 void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
40 std::string oldFilename = "" ); 42 std::string oldFilename = "" ) override;
41 43
42 /// @return Returns a list of the directories that are being watched 44 /// @return Returns a list of the directories that are being watched
43 std::list<std::string> directories(); 45 std::vector<std::string> directories() override;
44 46
45 protected: 47 protected:
46 /// Map of WatchID to WatchStruct pointers 48 /// Map of WatchID to WatchStruct pointers
@@ -49,6 +51,8 @@ class FileWatcherInotify : public FileWatcherImpl {
49 /// User added watches 51 /// User added watches
50 WatchMap mRealWatches; 52 WatchMap mRealWatches;
51 53
54 std::unordered_map<std::string, WatchID> mWatchesRef;
55
52 /// inotify file descriptor 56 /// inotify file descriptor
53 int mFD; 57 int mFD;
54 58
@@ -57,12 +61,14 @@ class FileWatcherInotify : public FileWatcherImpl {
57 Mutex mWatchesLock; 61 Mutex mWatchesLock;
58 Mutex mRealWatchesLock; 62 Mutex mRealWatchesLock;
59 Mutex mInitLock; 63 Mutex mInitLock;
64 bool mIsTakingAction;
60 std::vector<std::pair<WatcherInotify*, std::string>> mMovedOutsideWatches; 65 std::vector<std::pair<WatcherInotify*, std::string>> mMovedOutsideWatches;
61 66
62 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, 67 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
63 WatcherInotify* parent = NULL ); 68 bool syntheticEvents, WatcherInotify* parent = NULL,
69 bool fromInternalEvent = false );
64 70
65 bool pathInWatches( const std::string& path ); 71 bool pathInWatches( const std::string& path ) override;
66 72
67 private: 73 private:
68 void run(); 74 void run();
diff --git a/src/3rdParty/efsw/FileWatcherKqueue.cpp b/src/3rdParty/efsw/FileWatcherKqueue.cpp
index 38ffad9..ad03036 100755..100644
--- a/src/3rdParty/efsw/FileWatcherKqueue.cpp
+++ b/src/3rdParty/efsw/FileWatcherKqueue.cpp
@@ -45,7 +45,7 @@ FileWatcherKqueue::~FileWatcherKqueue() {
45} 45}
46 46
47WatchID FileWatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher, 47WatchID FileWatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher,
48 bool recursive ) { 48 bool recursive, const std::vector<WatcherOption>& /*options*/ ) {
49 static bool s_ug = false; 49 static bool s_ug = false;
50 50
51 std::string dir( directory ); 51 std::string dir( directory );
@@ -184,11 +184,13 @@ void FileWatcherKqueue::run() {
184void FileWatcherKqueue::handleAction( Watcher* /*watch*/, const std::string& /*filename*/, 184void FileWatcherKqueue::handleAction( Watcher* /*watch*/, const std::string& /*filename*/,
185 unsigned long /*action*/, std::string /*oldFilename*/ ) {} 185 unsigned long /*action*/, std::string /*oldFilename*/ ) {}
186 186
187std::list<std::string> FileWatcherKqueue::directories() { 187std::vector<std::string> FileWatcherKqueue::directories() {
188 std::list<std::string> dirs; 188 std::vector<std::string> dirs;
189 189
190 Lock lock( mWatchesLock ); 190 Lock lock( mWatchesLock );
191 191
192 dirs.reserve( mWatches.size() );
193
192 WatchMap::iterator it = mWatches.begin(); 194 WatchMap::iterator it = mWatches.begin();
193 195
194 for ( ; it != mWatches.end(); ++it ) { 196 for ( ; it != mWatches.end(); ++it ) {
diff --git a/src/3rdParty/efsw/FileWatcherKqueue.hpp b/src/3rdParty/efsw/FileWatcherKqueue.hpp
index 1bf3755..ff5327b 100755..100644
--- a/src/3rdParty/efsw/FileWatcherKqueue.hpp
+++ b/src/3rdParty/efsw/FileWatcherKqueue.hpp
@@ -21,23 +21,24 @@ class FileWatcherKqueue : public FileWatcherImpl {
21 21
22 /// Add a directory watch 22 /// Add a directory watch
23 /// On error returns WatchID with Error type. 23 /// On error returns WatchID with Error type.
24 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); 24 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
25 const std::vector<WatcherOption> &options ) override;
25 26
26 /// Remove a directory watch. This is a brute force lazy search O(nlogn). 27 /// Remove a directory watch. This is a brute force lazy search O(nlogn).
27 void removeWatch( const std::string& directory ); 28 void removeWatch( const std::string& directory ) override;
28 29
29 /// Remove a directory watch. This is a map lookup O(logn). 30 /// Remove a directory watch. This is a map lookup O(logn).
30 void removeWatch( WatchID watchid ); 31 void removeWatch( WatchID watchid ) override;
31 32
32 /// Updates the watcher. Must be called often. 33 /// Updates the watcher. Must be called often.
33 void watch(); 34 void watch() override;
34 35
35 /// Handles the action 36 /// Handles the action
36 void handleAction( Watcher* watch, const std::string& filename, unsigned long action, 37 void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
37 std::string oldFilename = "" ); 38 std::string oldFilename = "" ) override;
38 39
39 /// @return Returns a list of the directories that are being watched 40 /// @return Returns a list of the directories that are being watched
40 std::list<std::string> directories(); 41 std::vector<std::string> directories() override;
41 42
42 protected: 43 protected:
43 /// Map of WatchID to WatchStruct pointers 44 /// Map of WatchID to WatchStruct pointers
@@ -53,7 +54,7 @@ class FileWatcherKqueue : public FileWatcherImpl {
53 54
54 Mutex mWatchesLock; 55 Mutex mWatchesLock;
55 56
56 std::list<WatchID> mRemoveList; 57 std::vector<WatchID> mRemoveList;
57 58
58 long mFileDescriptorCount; 59 long mFileDescriptorCount;
59 60
@@ -61,7 +62,7 @@ class FileWatcherKqueue : public FileWatcherImpl {
61 62
62 bool isAddingWatcher() const; 63 bool isAddingWatcher() const;
63 64
64 bool pathInWatches( const std::string& path ); 65 bool pathInWatches( const std::string& path ) override;
65 66
66 void addFD(); 67 void addFD();
67 68
diff --git a/src/3rdParty/efsw/FileWatcherWin32.cpp b/src/3rdParty/efsw/FileWatcherWin32.cpp
index 963dc98..19b71d7 100755..100644
--- a/src/3rdParty/efsw/FileWatcherWin32.cpp
+++ b/src/3rdParty/efsw/FileWatcherWin32.cpp
@@ -1,257 +1,267 @@
1#include <efsw/FileSystem.hpp> 1#include <efsw/FileSystem.hpp>
2#include <efsw/FileWatcherWin32.hpp> 2#include <efsw/FileWatcherWin32.hpp>
3#include <efsw/Lock.hpp> 3#include <efsw/Lock.hpp>
4#include <efsw/String.hpp> 4#include <efsw/String.hpp>
5#include <efsw/System.hpp> 5#include <efsw/System.hpp>
6 6
7#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 7#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
8 8
9namespace efsw { 9namespace efsw {
10 10
11FileWatcherWin32::FileWatcherWin32( FileWatcher* parent ) : 11FileWatcherWin32::FileWatcherWin32( FileWatcher* parent ) :
12 FileWatcherImpl( parent ), mLastWatchID( 0 ), mThread( NULL ) { 12 FileWatcherImpl( parent ), mLastWatchID( 0 ), mThread( NULL ) {
13 mIOCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 1 ); 13 mIOCP = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 1 );
14 if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE ) 14 if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE )
15 mInitOK = true; 15 mInitOK = true;
16} 16}
17 17
18FileWatcherWin32::~FileWatcherWin32() { 18FileWatcherWin32::~FileWatcherWin32() {
19 mInitOK = false; 19 mInitOK = false;
20 20
21 if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE ) { 21 if ( mIOCP && mIOCP != INVALID_HANDLE_VALUE ) {
22 PostQueuedCompletionStatus( mIOCP, 0, reinterpret_cast<ULONG_PTR>( this ), NULL ); 22 PostQueuedCompletionStatus( mIOCP, 0, reinterpret_cast<ULONG_PTR>( this ), NULL );
23 } 23 }
24 24
25 efSAFE_DELETE( mThread ); 25 efSAFE_DELETE( mThread );
26 26
27 removeAllWatches(); 27 removeAllWatches();
28 28
29 CloseHandle( mIOCP ); 29 if ( mIOCP )
30} 30 CloseHandle( mIOCP );
31 31}
32WatchID FileWatcherWin32::addWatch( const std::string& directory, FileWatchListener* watcher, 32
33 bool recursive ) { 33WatchID FileWatcherWin32::addWatch( const std::string& directory, FileWatchListener* watcher,
34 std::string dir( directory ); 34 bool recursive, const std::vector<WatcherOption> &options ) {
35 35 std::string dir( directory );
36 FileInfo fi( dir ); 36
37 37 FileInfo fi( dir );
38 if ( !fi.isDirectory() ) { 38
39 return Errors::Log::createLastError( Errors::FileNotFound, dir ); 39 if ( !fi.isDirectory() ) {
40 } else if ( !fi.isReadable() ) { 40 return Errors::Log::createLastError( Errors::FileNotFound, dir );
41 return Errors::Log::createLastError( Errors::FileNotReadable, dir ); 41 } else if ( !fi.isReadable() ) {
42 } 42 return Errors::Log::createLastError( Errors::FileNotReadable, dir );
43 43 }
44 FileSystem::dirAddSlashAtEnd( dir ); 44
45 45 FileSystem::dirAddSlashAtEnd( dir );
46 Lock lock( mWatchesLock ); 46
47 47 Lock lock( mWatchesLock );
48 if ( pathInWatches( dir ) ) { 48
49 return Errors::Log::createLastError( Errors::FileRepeated, dir ); 49 if ( pathInWatches( dir ) ) {
50 } 50 return Errors::Log::createLastError( Errors::FileRepeated, dir );
51 51 }
52 WatchID watchid = ++mLastWatchID; 52
53 53 WatchID watchid = ++mLastWatchID;
54 WatcherStructWin32* watch = CreateWatch( 54
55 String::fromUtf8( dir ).toWideString().c_str(), recursive, 55 DWORD bufferSize = static_cast<DWORD>( getOptionValue(options, Option::WinBufferSize, 63 * 1024) );
56 FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | 56 DWORD notifyFilter = static_cast<DWORD>( getOptionValue(options, Option::WinNotifyFilter,
57 FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE, 57 FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_LAST_WRITE |
58 mIOCP ); 58 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
59 59 FILE_NOTIFY_CHANGE_SIZE) );
60 if ( NULL == watch ) { 60
61 return Errors::Log::createLastError( Errors::FileNotFound, dir ); 61 WatcherStructWin32* watch = CreateWatch( String::fromUtf8( dir ).toWideString().c_str(),
62 } 62 recursive, bufferSize, notifyFilter, mIOCP );
63 63
64 // Add the handle to the handles vector 64 if ( NULL == watch ) {
65 watch->Watch->ID = watchid; 65 return Errors::Log::createLastError( Errors::FileNotFound, dir );
66 watch->Watch->Watch = this; 66 }
67 watch->Watch->Listener = watcher; 67
68 watch->Watch->DirName = new char[dir.length() + 1]; 68 // Add the handle to the handles vector
69 strcpy( watch->Watch->DirName, dir.c_str() ); 69 watch->Watch->ID = watchid;
70 70 watch->Watch->Watch = this;
71 mWatches.insert( watch ); 71 watch->Watch->Listener = watcher;
72 72 watch->Watch->DirName = new char[dir.length() + 1];
73 return watchid; 73 strcpy( watch->Watch->DirName, dir.c_str() );
74} 74
75 75 mWatches.insert( watch );
76void FileWatcherWin32::removeWatch( const std::string& directory ) { 76
77 Lock lock( mWatchesLock ); 77 return watchid;
78 78}
79 Watches::iterator iter = mWatches.begin(); 79
80 80void FileWatcherWin32::removeWatch( const std::string& directory ) {
81 for ( ; iter != mWatches.end(); ++iter ) { 81 Lock lock( mWatchesLock );
82 if ( directory == ( *iter )->Watch->DirName ) { 82
83 removeWatch( *iter ); 83 Watches::iterator iter = mWatches.begin();
84 break; 84
85 } 85 for ( ; iter != mWatches.end(); ++iter ) {
86 } 86 if ( directory == ( *iter )->Watch->DirName ) {
87} 87 removeWatch( *iter );
88 88 break;
89void FileWatcherWin32::removeWatch( WatchID watchid ) { 89 }
90 Lock lock( mWatchesLock ); 90 }
91 91}
92 Watches::iterator iter = mWatches.begin(); 92
93 93void FileWatcherWin32::removeWatch( WatchID watchid ) {
94 for ( ; iter != mWatches.end(); ++iter ) { 94 Lock lock( mWatchesLock );
95 // Find the watch ID 95
96 if ( ( *iter )->Watch->ID == watchid ) { 96 Watches::iterator iter = mWatches.begin();
97 removeWatch( *iter ); 97
98 return; 98 for ( ; iter != mWatches.end(); ++iter ) {
99 } 99 // Find the watch ID
100 } 100 if ( ( *iter )->Watch->ID == watchid ) {
101} 101 removeWatch( *iter );
102 102 return;
103void FileWatcherWin32::removeWatch( WatcherStructWin32* watch ) { 103 }
104 Lock lock( mWatchesLock ); 104 }
105 105}
106 DestroyWatch( watch ); 106
107 mWatches.erase( watch ); 107void FileWatcherWin32::removeWatch( WatcherStructWin32* watch ) {
108} 108 Lock lock( mWatchesLock );
109 109
110void FileWatcherWin32::watch() { 110 DestroyWatch( watch );
111 if ( NULL == mThread ) { 111 mWatches.erase( watch );
112 mThread = new Thread( &FileWatcherWin32::run, this ); 112}
113 mThread->launch(); 113
114 } 114void FileWatcherWin32::watch() {
115} 115 if ( NULL == mThread ) {
116 116 mThread = new Thread( &FileWatcherWin32::run, this );
117void FileWatcherWin32::removeAllWatches() { 117 mThread->launch();
118 Lock lock( mWatchesLock ); 118 }
119 119}
120 Watches::iterator iter = mWatches.begin(); 120
121 121void FileWatcherWin32::removeAllWatches() {
122 for ( ; iter != mWatches.end(); ++iter ) { 122 Lock lock( mWatchesLock );
123 DestroyWatch( ( *iter ) ); 123
124 } 124 Watches::iterator iter = mWatches.begin();
125 125
126 mWatches.clear(); 126 for ( ; iter != mWatches.end(); ++iter ) {
127} 127 DestroyWatch( ( *iter ) );
128 128 }
129void FileWatcherWin32::run() { 129
130 do { 130 mWatches.clear();
131 if ( mInitOK && !mWatches.empty() ) { 131}
132 DWORD numOfBytes = 0; 132
133 OVERLAPPED* ov = NULL; 133void FileWatcherWin32::run() {
134 ULONG_PTR compKey = 0; 134 do {
135 BOOL res = FALSE; 135 if ( mInitOK && !mWatches.empty() ) {
136 136 DWORD numOfBytes = 0;
137 while ( ( res = GetQueuedCompletionStatus( mIOCP, &numOfBytes, &compKey, &ov, 137 OVERLAPPED* ov = NULL;
138 INFINITE ) ) != FALSE ) { 138 ULONG_PTR compKey = 0;
139 if ( compKey != 0 && compKey == reinterpret_cast<ULONG_PTR>( this ) ) { 139 BOOL res = FALSE;
140 break; 140
141 } else { 141 while ( ( res = GetQueuedCompletionStatus( mIOCP, &numOfBytes, &compKey, &ov,
142 Lock lock( mWatchesLock ); 142 INFINITE ) ) != FALSE ) {
143 WatchCallback( numOfBytes, ov ); 143 if ( compKey != 0 && compKey == reinterpret_cast<ULONG_PTR>( this ) ) {
144 } 144 break;
145 } 145 } else {
146 } else { 146 Lock lock( mWatchesLock );
147 System::sleep( 10 ); 147 if (mWatches.find( (WatcherStructWin32*)ov ) != mWatches.end())
148 } 148 WatchCallback( numOfBytes, ov );
149 } while ( mInitOK ); 149 }
150 150 }
151 removeAllWatches(); 151 } else {
152} 152 System::sleep( 10 );
153 153 }
154void FileWatcherWin32::handleAction( Watcher* watch, const std::string& filename, 154 } while ( mInitOK );
155 unsigned long action, std::string /*oldFilename*/ ) { 155
156 Action fwAction; 156 removeAllWatches();
157 157}
158 switch ( action ) { 158
159 case FILE_ACTION_RENAMED_OLD_NAME: 159void FileWatcherWin32::handleAction( Watcher* watch, const std::string& filename,
160 watch->OldFileName = filename; 160 unsigned long action, std::string /*oldFilename*/ ) {
161 return; 161 Action fwAction;
162 case FILE_ACTION_ADDED: 162
163 fwAction = Actions::Add; 163 switch ( action ) {
164 break; 164 case FILE_ACTION_RENAMED_OLD_NAME:
165 case FILE_ACTION_RENAMED_NEW_NAME: { 165 watch->OldFileName = filename;
166 fwAction = Actions::Moved; 166 return;
167 167 case FILE_ACTION_ADDED:
168 std::string fpath( watch->Directory + filename ); 168 fwAction = Actions::Add;
169 169 break;
170 // Update the directory path 170 case FILE_ACTION_RENAMED_NEW_NAME: {
171 if ( watch->Recursive && FileSystem::isDirectory( fpath ) ) { 171 fwAction = Actions::Moved;
172 // Update the new directory path 172
173 std::string opath( watch->Directory + watch->OldFileName ); 173 std::string fpath( watch->Directory + filename );
174 FileSystem::dirAddSlashAtEnd( opath ); 174
175 FileSystem::dirAddSlashAtEnd( fpath ); 175 // Update the directory path
176 176 if ( watch->Recursive && FileSystem::isDirectory( fpath ) ) {
177 for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { 177 // Update the new directory path
178 if ( ( *it )->Watch->Directory == opath ) { 178 std::string opath( watch->Directory + watch->OldFileName );
179 ( *it )->Watch->Directory = fpath; 179 FileSystem::dirAddSlashAtEnd( opath );
180 180 FileSystem::dirAddSlashAtEnd( fpath );
181 break; 181
182 } 182 for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
183 } 183 if ( ( *it )->Watch->Directory == opath ) {
184 } 184 ( *it )->Watch->Directory = fpath;
185 185
186 std::string folderPath( static_cast<WatcherWin32*>( watch )->DirName ); 186 break;
187 std::string realFilename = filename; 187 }
188 std::size_t sepPos = filename.find_last_of( "/\\" ); 188 }
189 std::string oldFolderPath = 189 }
190 static_cast<WatcherWin32*>( watch )->DirName + 190
191 watch->OldFileName.substr( 0, watch->OldFileName.find_last_of( "/\\" ) ); 191 std::string folderPath( static_cast<WatcherWin32*>( watch )->DirName );
192 192 std::string realFilename = filename;
193 if ( sepPos != std::string::npos ) { 193 std::size_t sepPos = filename.find_last_of( "/\\" );
194 folderPath += filename.substr( 0, sepPos ); 194 std::string oldFolderPath =
195 realFilename = filename.substr( sepPos + 1 ); 195 static_cast<WatcherWin32*>( watch )->DirName +
196 } 196 watch->OldFileName.substr( 0, watch->OldFileName.find_last_of( "/\\" ) );
197 197
198 if ( folderPath == oldFolderPath ) { 198 if ( sepPos != std::string::npos ) {
199 watch->Listener->handleFileAction( 199 folderPath +=
200 watch->ID, folderPath, realFilename, fwAction, 200 filename.substr( 0, sepPos + 1 < filename.size() ? sepPos + 1 : sepPos );
201 FileSystem::fileNameFromPath( watch->OldFileName ) ); 201 realFilename = filename.substr( sepPos + 1 );
202 } else { 202 }
203 watch->Listener->handleFileAction( watch->ID, 203
204 static_cast<WatcherWin32*>( watch )->DirName, 204 if ( folderPath == oldFolderPath ) {
205 filename, fwAction, watch->OldFileName ); 205 watch->Listener->handleFileAction(
206 } 206 watch->ID, folderPath, realFilename, fwAction,
207 return; 207 FileSystem::fileNameFromPath( watch->OldFileName ) );
208 } 208 } else {
209 case FILE_ACTION_REMOVED: 209 watch->Listener->handleFileAction( watch->ID,
210 fwAction = Actions::Delete; 210 static_cast<WatcherWin32*>( watch )->DirName,
211 break; 211 filename, fwAction, watch->OldFileName );
212 case FILE_ACTION_MODIFIED: 212 }
213 fwAction = Actions::Modified; 213 return;
214 break; 214 }
215 default: 215 case FILE_ACTION_REMOVED:
216 return; 216 fwAction = Actions::Delete;
217 }; 217 break;
218 218 case FILE_ACTION_MODIFIED:
219 std::string folderPath( static_cast<WatcherWin32*>( watch )->DirName ); 219 fwAction = Actions::Modified;
220 std::string realFilename = filename; 220 break;
221 std::size_t sepPos = filename.find_last_of( "/\\" ); 221 default:
222 222 return;
223 if ( sepPos != std::string::npos ) { 223 };
224 folderPath += filename.substr( 0, sepPos ); 224
225 realFilename = filename.substr( sepPos + 1 ); 225 std::string folderPath( static_cast<WatcherWin32*>( watch )->DirName );
226 } 226 std::string realFilename = filename;
227 227 std::size_t sepPos = filename.find_last_of( "/\\" );
228 watch->Listener->handleFileAction( watch->ID, folderPath, realFilename, fwAction ); 228
229} 229 if ( sepPos != std::string::npos ) {
230 230 folderPath += filename.substr( 0, sepPos + 1 < filename.size() ? sepPos + 1 : sepPos );
231std::list<std::string> FileWatcherWin32::directories() { 231 realFilename = filename.substr( sepPos + 1 );
232 std::list<std::string> dirs; 232 }
233 233
234 Lock lock( mWatchesLock ); 234 FileSystem::dirAddSlashAtEnd( folderPath );
235 235
236 for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { 236 watch->Listener->handleFileAction( watch->ID, folderPath, realFilename, fwAction );
237 dirs.push_back( std::string( ( *it )->Watch->DirName ) ); 237}
238 } 238
239 239std::vector<std::string> FileWatcherWin32::directories() {
240 return dirs; 240 std::vector<std::string> dirs;
241} 241
242 242 Lock lock( mWatchesLock );
243bool FileWatcherWin32::pathInWatches( const std::string& path ) { 243
244 Lock lock( mWatchesLock ); 244 dirs.reserve( mWatches.size() );
245 245
246 for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) { 246 for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
247 if ( ( *it )->Watch->DirName == path ) { 247 dirs.push_back( std::string( ( *it )->Watch->DirName ) );
248 return true; 248 }
249 } 249
250 } 250 return dirs;
251 251}
252 return false; 252
253} 253bool FileWatcherWin32::pathInWatches( const std::string& path ) {
254 254 Lock lock( mWatchesLock );
255} // namespace efsw 255
256 256 for ( Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it ) {
257#endif 257 if ( ( *it )->Watch->DirName == path ) {
258 return true;
259 }
260 }
261
262 return false;
263}
264
265} // namespace efsw
266
267#endif
diff --git a/src/3rdParty/efsw/FileWatcherWin32.hpp b/src/3rdParty/efsw/FileWatcherWin32.hpp
index 94439cf..3016aac 100755..100644
--- a/src/3rdParty/efsw/FileWatcherWin32.hpp
+++ b/src/3rdParty/efsw/FileWatcherWin32.hpp
@@ -1,70 +1,71 @@
1#ifndef EFSW_FILEWATCHERWIN32_HPP 1#ifndef EFSW_FILEWATCHERWIN32_HPP
2#define EFSW_FILEWATCHERWIN32_HPP 2#define EFSW_FILEWATCHERWIN32_HPP
3 3
4#include <efsw/base.hpp> 4#include <efsw/base.hpp>
5 5
6#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 6#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
7 7
8#include <efsw/WatcherWin32.hpp> 8#include <efsw/WatcherWin32.hpp>
9#include <map> 9#include <map>
10#include <set> 10#include <unordered_set>
11#include <vector> 11#include <vector>
12 12
13namespace efsw { 13namespace efsw {
14 14
15/// Implementation for Win32 based on ReadDirectoryChangesW. 15/// Implementation for Win32 based on ReadDirectoryChangesW.
16/// @class FileWatcherWin32 16/// @class FileWatcherWin32
17class FileWatcherWin32 : public FileWatcherImpl { 17class FileWatcherWin32 : public FileWatcherImpl {
18 public: 18 public:
19 /// type for a map from WatchID to WatcherWin32 pointer 19 /// type for a map from WatchID to WatcherWin32 pointer
20 typedef std::set<WatcherStructWin32*> Watches; 20 typedef std::unordered_set<WatcherStructWin32*> Watches;
21 21
22 FileWatcherWin32( FileWatcher* parent ); 22 FileWatcherWin32( FileWatcher* parent );
23 23
24 virtual ~FileWatcherWin32(); 24 virtual ~FileWatcherWin32();
25 25
26 /// Add a directory watch 26 /// Add a directory watch
27 /// On error returns WatchID with Error type. 27 /// On error returns WatchID with Error type.
28 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); 28 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
29 29 const std::vector<WatcherOption> &options ) override;
30 /// Remove a directory watch. This is a brute force lazy search O(nlogn). 30
31 void removeWatch( const std::string& directory ); 31 /// Remove a directory watch. This is a brute force lazy search O(nlogn).
32 32 void removeWatch( const std::string& directory ) override;
33 /// Remove a directory watch. This is a map lookup O(logn). 33
34 void removeWatch( WatchID watchid ); 34 /// Remove a directory watch. This is a map lookup O(logn).
35 35 void removeWatch( WatchID watchid ) override;
36 /// Updates the watcher. Must be called often. 36
37 void watch(); 37 /// Updates the watcher. Must be called often.
38 38 void watch() override;
39 /// Handles the action 39
40 void handleAction( Watcher* watch, const std::string& filename, unsigned long action, 40 /// Handles the action
41 std::string oldFilename = "" ); 41 void handleAction( Watcher* watch, const std::string& filename, unsigned long action,
42 42 std::string oldFilename = "" ) override;
43 /// @return Returns a list of the directories that are being watched 43
44 std::list<std::string> directories(); 44 /// @return Returns a list of the directories that are being watched
45 45 std::vector<std::string> directories() override;
46 protected: 46
47 HANDLE mIOCP; 47 protected:
48 Watches mWatches; 48 HANDLE mIOCP;
49 49 Watches mWatches;
50 /// The last watchid 50
51 WatchID mLastWatchID; 51 /// The last watchid
52 Thread* mThread; 52 WatchID mLastWatchID;
53 Mutex mWatchesLock; 53 Thread* mThread;
54 54 Mutex mWatchesLock;
55 bool pathInWatches( const std::string& path ); 55
56 56 bool pathInWatches( const std::string& path ) override;
57 /// Remove all directory watches. 57
58 void removeAllWatches(); 58 /// Remove all directory watches.
59 59 void removeAllWatches();
60 void removeWatch( WatcherStructWin32* watch ); 60
61 61 void removeWatch( WatcherStructWin32* watch );
62 private: 62
63 void run(); 63 private:
64}; 64 void run();
65 65};
66} // namespace efsw 66
67 67} // namespace efsw
68#endif 68
69 69#endif
70#endif 70
71#endif
diff --git a/src/3rdParty/efsw/LICENSE b/src/3rdParty/efsw/LICENSE
index 37f354a..37f354a 100755..100644
--- a/src/3rdParty/efsw/LICENSE
+++ b/src/3rdParty/efsw/LICENSE
diff --git a/src/3rdParty/efsw/Lock.hpp b/src/3rdParty/efsw/Lock.hpp
index e8c522a..e8c522a 100755..100644
--- a/src/3rdParty/efsw/Lock.hpp
+++ b/src/3rdParty/efsw/Lock.hpp
diff --git a/src/3rdParty/efsw/Log.cpp b/src/3rdParty/efsw/Log.cpp
index ddf7a62..6f32df7 100755..100644
--- a/src/3rdParty/efsw/Log.cpp
+++ b/src/3rdParty/efsw/Log.cpp
@@ -1,20 +1,31 @@
1#include <efsw/efsw.hpp> 1#include <efsw/efsw.hpp>
2#include <efsw/Debug.hpp>
2 3
3namespace efsw { namespace Errors { 4namespace efsw { namespace Errors {
4 5
5static std::string LastError; 6static std::string LastError = "";
7static Error LastErrorCode = NoError;
6 8
7std::string Log::getLastErrorLog() { 9std::string Log::getLastErrorLog() {
8 return LastError; 10 return LastError;
9} 11}
10 12
13Error Log::getLastErrorCode() {
14 return LastErrorCode;
15}
16
17void Log::clearLastError() {
18 LastErrorCode = NoError;
19 LastError = "";
20}
21
11Error Log::createLastError( Error err, std::string log ) { 22Error Log::createLastError( Error err, std::string log ) {
12 switch ( err ) { 23 switch ( err ) {
13 case FileNotFound: 24 case FileNotFound:
14 LastError = "File not found ( " + log + " )"; 25 LastError = "File not found ( " + log + " )";
15 break; 26 break;
16 case FileRepeated: 27 case FileRepeated:
17 LastError = "File reapeated in watches ( " + log + " )"; 28 LastError = "File repeated in watches ( " + log + " )";
18 break; 29 break;
19 case FileOutOfScope: 30 case FileOutOfScope:
20 LastError = "Symlink file out of scope ( " + log + " )"; 31 LastError = "Symlink file out of scope ( " + log + " )";
@@ -23,11 +34,15 @@ Error Log::createLastError( Error err, std::string log ) {
23 LastError = 34 LastError =
24 "File is located in a remote file system, use a generic watcher. ( " + log + " )"; 35 "File is located in a remote file system, use a generic watcher. ( " + log + " )";
25 break; 36 break;
37 case WatcherFailed:
38 LastError = "File system watcher failed ( " + log + " )";
39 break;
26 case Unspecified: 40 case Unspecified:
27 default: 41 default:
28 LastError = log; 42 LastError = log;
29 } 43 }
30 44
45 efDEBUG( "%s\n", LastError.c_str() );
31 return err; 46 return err;
32} 47}
33 48
diff --git a/src/3rdParty/efsw/Mutex.cpp b/src/3rdParty/efsw/Mutex.cpp
index c961db1..c961db1 100755..100644
--- a/src/3rdParty/efsw/Mutex.cpp
+++ b/src/3rdParty/efsw/Mutex.cpp
diff --git a/src/3rdParty/efsw/Mutex.hpp b/src/3rdParty/efsw/Mutex.hpp
index d98ad17..d98ad17 100755..100644
--- a/src/3rdParty/efsw/Mutex.hpp
+++ b/src/3rdParty/efsw/Mutex.hpp
diff --git a/src/3rdParty/efsw/String.cpp b/src/3rdParty/efsw/String.cpp
index e3ba68f..e3ba68f 100755..100644
--- a/src/3rdParty/efsw/String.cpp
+++ b/src/3rdParty/efsw/String.cpp
diff --git a/src/3rdParty/efsw/String.hpp b/src/3rdParty/efsw/String.hpp
index 65bce33..b42b945 100755..100644
--- a/src/3rdParty/efsw/String.hpp
+++ b/src/3rdParty/efsw/String.hpp
@@ -11,7 +11,6 @@
11#include <cstdlib> 11#include <cstdlib>
12#include <cstring> 12#include <cstring>
13#include <efsw/base.hpp> 13#include <efsw/base.hpp>
14#include <fstream>
15#include <iostream> 14#include <iostream>
16#include <locale> 15#include <locale>
17#include <sstream> 16#include <sstream>
@@ -24,7 +23,7 @@ namespace efsw {
24 * **/ 23 * **/
25class String { 24class String {
26 public: 25 public:
27 typedef Uint32 StringBaseType; 26 typedef char32_t StringBaseType;
28 typedef std::basic_string<StringBaseType> StringType; 27 typedef std::basic_string<StringBaseType> StringType;
29 typedef StringType::iterator Iterator; //! Iterator type 28 typedef StringType::iterator Iterator; //! Iterator type
30 typedef StringType::const_iterator ConstIterator; //! Constant iterator type 29 typedef StringType::const_iterator ConstIterator; //! Constant iterator type
diff --git a/src/3rdParty/efsw/System.cpp b/src/3rdParty/efsw/System.cpp
index ba68bf4..ba68bf4 100755..100644
--- a/src/3rdParty/efsw/System.cpp
+++ b/src/3rdParty/efsw/System.cpp
diff --git a/src/3rdParty/efsw/System.hpp b/src/3rdParty/efsw/System.hpp
index 498e121..498e121 100755..100644
--- a/src/3rdParty/efsw/System.hpp
+++ b/src/3rdParty/efsw/System.hpp
diff --git a/src/3rdParty/efsw/Thread.cpp b/src/3rdParty/efsw/Thread.cpp
index e3f0fa0..cfa88b4 100755..100644
--- a/src/3rdParty/efsw/Thread.cpp
+++ b/src/3rdParty/efsw/Thread.cpp
@@ -34,7 +34,8 @@ void Thread::terminate() {
34} 34}
35 35
36void Thread::run() { 36void Thread::run() {
37 mEntryPoint->run(); 37 if ( mEntryPoint )
38 mEntryPoint->run();
38} 39}
39 40
40} // namespace efsw 41} // namespace efsw
diff --git a/src/3rdParty/efsw/Thread.hpp b/src/3rdParty/efsw/Thread.hpp
index b60373c..b60373c 100755..100644
--- a/src/3rdParty/efsw/Thread.hpp
+++ b/src/3rdParty/efsw/Thread.hpp
diff --git a/src/3rdParty/efsw/Utf.hpp b/src/3rdParty/efsw/Utf.hpp
index 6e9ea71..1b042cd 100755..100644
--- a/src/3rdParty/efsw/Utf.hpp
+++ b/src/3rdParty/efsw/Utf.hpp
@@ -1,721 +1,721 @@
1/** NOTE: 1/** NOTE:
2 * This code is based on the Utf implementation from SFML2. License zlib/png ( 2 * This code is based on the Utf implementation from SFML2. License zlib/png (
3 *http://www.sfml-dev.org/license.php ) The class was modified to fit efsw own needs. This is not 3 *http://www.sfml-dev.org/license.php ) The class was modified to fit efsw own needs. This is not
4 *the original implementation from SFML2. 4 *the original implementation from SFML2.
5 * */ 5 * */
6 6
7#ifndef EFSW_UTF_HPP 7#ifndef EFSW_UTF_HPP
8#define EFSW_UTF_HPP 8#define EFSW_UTF_HPP
9 9
10//////////////////////////////////////////////////////////// 10////////////////////////////////////////////////////////////
11// Headers 11// Headers
12//////////////////////////////////////////////////////////// 12////////////////////////////////////////////////////////////
13#include <cstdlib> 13#include <cstdlib>
14#include <efsw/base.hpp> 14#include <efsw/base.hpp>
15#include <locale> 15#include <locale>
16#include <string> 16#include <string>
17 17
18namespace efsw { 18namespace efsw {
19 19
20template <unsigned int N> class Utf; 20template <unsigned int N> class Utf;
21 21
22//////////////////////////////////////////////////////////// 22////////////////////////////////////////////////////////////
23/// \brief Specialization of the Utf template for UTF-8 23/// \brief Specialization of the Utf template for UTF-8
24/// 24///
25//////////////////////////////////////////////////////////// 25////////////////////////////////////////////////////////////
26template <> class Utf<8> { 26template <> class Utf<8> {
27 public: 27 public:
28 //////////////////////////////////////////////////////////// 28 ////////////////////////////////////////////////////////////
29 /// \brief Decode a single UTF-8 character 29 /// \brief Decode a single UTF-8 character
30 /// 30 ///
31 /// Decoding a character means finding its unique 32-bits 31 /// Decoding a character means finding its unique 32-bits
32 /// code (called the codepoint) in the Unicode standard. 32 /// code (called the codepoint) in the Unicode standard.
33 /// 33 ///
34 /// \param begin Iterator pointing to the beginning of the input sequence 34 /// \param begin Iterator pointing to the beginning of the input sequence
35 /// \param end Iterator pointing to the end of the input sequence 35 /// \param end Iterator pointing to the end of the input sequence
36 /// \param output Codepoint of the decoded UTF-8 character 36 /// \param output Codepoint of the decoded UTF-8 character
37 /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid 37 /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid
38 /// 38 ///
39 /// \return Iterator pointing to one past the last read element of the input sequence 39 /// \return Iterator pointing to one past the last read element of the input sequence
40 /// 40 ///
41 //////////////////////////////////////////////////////////// 41 ////////////////////////////////////////////////////////////
42 template <typename In> 42 template <typename In>
43 static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 ); 43 static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 );
44 44
45 //////////////////////////////////////////////////////////// 45 ////////////////////////////////////////////////////////////
46 /// \brief Encode a single UTF-8 character 46 /// \brief Encode a single UTF-8 character
47 /// 47 ///
48 /// Encoding a character means converting a unique 32-bits 48 /// Encoding a character means converting a unique 32-bits
49 /// code (called the codepoint) in the target encoding, UTF-8. 49 /// code (called the codepoint) in the target encoding, UTF-8.
50 /// 50 ///
51 /// \param input Codepoint to encode as UTF-8 51 /// \param input Codepoint to encode as UTF-8
52 /// \param output Iterator pointing to the beginning of the output sequence 52 /// \param output Iterator pointing to the beginning of the output sequence
53 /// \param replacement Replacement for characters not convertible to UTF-8 (use 0 to skip them) 53 /// \param replacement Replacement for characters not convertible to UTF-8 (use 0 to skip them)
54 /// 54 ///
55 /// \return Iterator to the end of the output sequence which has been written 55 /// \return Iterator to the end of the output sequence which has been written
56 /// 56 ///
57 //////////////////////////////////////////////////////////// 57 ////////////////////////////////////////////////////////////
58 template <typename Out> static Out Encode( Uint32 input, Out output, Uint8 replacement = 0 ); 58 template <typename Out> static Out Encode( Uint32 input, Out output, Uint8 replacement = 0 );
59 59
60 //////////////////////////////////////////////////////////// 60 ////////////////////////////////////////////////////////////
61 /// \brief Advance to the next UTF-8 character 61 /// \brief Advance to the next UTF-8 character
62 /// 62 ///
63 /// This function is necessary for multi-elements encodings, as 63 /// This function is necessary for multi-elements encodings, as
64 /// a single character may use more than 1 storage element. 64 /// a single character may use more than 1 storage element.
65 /// 65 ///
66 /// \param begin Iterator pointing to the beginning of the input sequence 66 /// \param begin Iterator pointing to the beginning of the input sequence
67 /// \param end Iterator pointing to the end of the input sequence 67 /// \param end Iterator pointing to the end of the input sequence
68 /// 68 ///
69 /// \return Iterator pointing to one past the last read element of the input sequence 69 /// \return Iterator pointing to one past the last read element of the input sequence
70 /// 70 ///
71 //////////////////////////////////////////////////////////// 71 ////////////////////////////////////////////////////////////
72 template <typename In> static In Next( In begin, In end ); 72 template <typename In> static In Next( In begin, In end );
73 73
74 //////////////////////////////////////////////////////////// 74 ////////////////////////////////////////////////////////////
75 /// \brief Count the number of characters of a UTF-8 sequence 75 /// \brief Count the number of characters of a UTF-8 sequence
76 /// 76 ///
77 /// This function is necessary for multi-elements encodings, as 77 /// This function is necessary for multi-elements encodings, as
78 /// a single character may use more than 1 storage element, thus the 78 /// a single character may use more than 1 storage element, thus the
79 /// total size can be different from (begin - end). 79 /// total size can be different from (begin - end).
80 /// 80 ///
81 /// \param begin Iterator pointing to the beginning of the input sequence 81 /// \param begin Iterator pointing to the beginning of the input sequence
82 /// \param end Iterator pointing to the end of the input sequence 82 /// \param end Iterator pointing to the end of the input sequence
83 /// 83 ///
84 /// \return Iterator pointing to one past the last read element of the input sequence 84 /// \return Iterator pointing to one past the last read element of the input sequence
85 /// 85 ///
86 //////////////////////////////////////////////////////////// 86 ////////////////////////////////////////////////////////////
87 template <typename In> static std::size_t Count( In begin, In end ); 87 template <typename In> static std::size_t Count( In begin, In end );
88 88
89 //////////////////////////////////////////////////////////// 89 ////////////////////////////////////////////////////////////
90 /// \brief Convert an ANSI characters range to UTF-8 90 /// \brief Convert an ANSI characters range to UTF-8
91 /// 91 ///
92 /// The current global locale will be used by default, unless you 92 /// The current global locale will be used by default, unless you
93 /// pass a custom one in the \a locale parameter. 93 /// pass a custom one in the \a locale parameter.
94 /// 94 ///
95 /// \param begin Iterator pointing to the beginning of the input sequence 95 /// \param begin Iterator pointing to the beginning of the input sequence
96 /// \param end Iterator pointing to the end of the input sequence 96 /// \param end Iterator pointing to the end of the input sequence
97 /// \param output Iterator pointing to the beginning of the output sequence 97 /// \param output Iterator pointing to the beginning of the output sequence
98 /// \param locale Locale to use for conversion 98 /// \param locale Locale to use for conversion
99 /// 99 ///
100 /// \return Iterator to the end of the output sequence which has been written 100 /// \return Iterator to the end of the output sequence which has been written
101 /// 101 ///
102 //////////////////////////////////////////////////////////// 102 ////////////////////////////////////////////////////////////
103 template <typename In, typename Out> 103 template <typename In, typename Out>
104 static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() ); 104 static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() );
105 105
106 //////////////////////////////////////////////////////////// 106 ////////////////////////////////////////////////////////////
107 /// \brief Convert a wide characters range to UTF-8 107 /// \brief Convert a wide characters range to UTF-8
108 /// 108 ///
109 /// \param begin Iterator pointing to the beginning of the input sequence 109 /// \param begin Iterator pointing to the beginning of the input sequence
110 /// \param end Iterator pointing to the end of the input sequence 110 /// \param end Iterator pointing to the end of the input sequence
111 /// \param output Iterator pointing to the beginning of the output sequence 111 /// \param output Iterator pointing to the beginning of the output sequence
112 /// 112 ///
113 /// \return Iterator to the end of the output sequence which has been written 113 /// \return Iterator to the end of the output sequence which has been written
114 /// 114 ///
115 //////////////////////////////////////////////////////////// 115 ////////////////////////////////////////////////////////////
116 template <typename In, typename Out> static Out FromWide( In begin, In end, Out output ); 116 template <typename In, typename Out> static Out FromWide( In begin, In end, Out output );
117 117
118 //////////////////////////////////////////////////////////// 118 ////////////////////////////////////////////////////////////
119 /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-8 119 /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-8
120 /// 120 ///
121 /// \param begin Iterator pointing to the beginning of the input sequence 121 /// \param begin Iterator pointing to the beginning of the input sequence
122 /// \param end Iterator pointing to the end of the input sequence 122 /// \param end Iterator pointing to the end of the input sequence
123 /// \param output Iterator pointing to the beginning of the output sequence 123 /// \param output Iterator pointing to the beginning of the output sequence
124 /// \param locale Locale to use for conversion 124 /// \param locale Locale to use for conversion
125 /// 125 ///
126 /// \return Iterator to the end of the output sequence which has been written 126 /// \return Iterator to the end of the output sequence which has been written
127 /// 127 ///
128 //////////////////////////////////////////////////////////// 128 ////////////////////////////////////////////////////////////
129 template <typename In, typename Out> static Out FromLatin1( In begin, In end, Out output ); 129 template <typename In, typename Out> static Out FromLatin1( In begin, In end, Out output );
130 130
131 //////////////////////////////////////////////////////////// 131 ////////////////////////////////////////////////////////////
132 /// \brief Convert an UTF-8 characters range to ANSI characters 132 /// \brief Convert an UTF-8 characters range to ANSI characters
133 /// 133 ///
134 /// The current global locale will be used by default, unless you 134 /// The current global locale will be used by default, unless you
135 /// pass a custom one in the \a locale parameter. 135 /// pass a custom one in the \a locale parameter.
136 /// 136 ///
137 /// \param begin Iterator pointing to the beginning of the input sequence 137 /// \param begin Iterator pointing to the beginning of the input sequence
138 /// \param end Iterator pointing to the end of the input sequence 138 /// \param end Iterator pointing to the end of the input sequence
139 /// \param output Iterator pointing to the beginning of the output sequence 139 /// \param output Iterator pointing to the beginning of the output sequence
140 /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) 140 /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them)
141 /// \param locale Locale to use for conversion 141 /// \param locale Locale to use for conversion
142 /// 142 ///
143 /// \return Iterator to the end of the output sequence which has been written 143 /// \return Iterator to the end of the output sequence which has been written
144 /// 144 ///
145 //////////////////////////////////////////////////////////// 145 ////////////////////////////////////////////////////////////
146 template <typename In, typename Out> 146 template <typename In, typename Out>
147 static Out ToAnsi( In begin, In end, Out output, char replacement = 0, 147 static Out ToAnsi( In begin, In end, Out output, char replacement = 0,
148 const std::locale& locale = std::locale() ); 148 const std::locale& locale = std::locale() );
149 149
150#ifndef EFSW_NO_WIDECHAR 150#ifndef EFSW_NO_WIDECHAR
151 //////////////////////////////////////////////////////////// 151 ////////////////////////////////////////////////////////////
152 /// \brief Convert an UTF-8 characters range to wide characters 152 /// \brief Convert an UTF-8 characters range to wide characters
153 /// 153 ///
154 /// \param begin Iterator pointing to the beginning of the input sequence 154 /// \param begin Iterator pointing to the beginning of the input sequence
155 /// \param end Iterator pointing to the end of the input sequence 155 /// \param end Iterator pointing to the end of the input sequence
156 /// \param output Iterator pointing to the beginning of the output sequence 156 /// \param output Iterator pointing to the beginning of the output sequence
157 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) 157 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
158 /// 158 ///
159 /// \return Iterator to the end of the output sequence which has been written 159 /// \return Iterator to the end of the output sequence which has been written
160 /// 160 ///
161 //////////////////////////////////////////////////////////// 161 ////////////////////////////////////////////////////////////
162 template <typename In, typename Out> 162 template <typename In, typename Out>
163 static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 ); 163 static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 );
164#endif 164#endif
165 165
166 //////////////////////////////////////////////////////////// 166 ////////////////////////////////////////////////////////////
167 /// \brief Convert an UTF-8 characters range to latin-1 (ISO-5589-1) characters 167 /// \brief Convert an UTF-8 characters range to latin-1 (ISO-5589-1) characters
168 /// 168 ///
169 /// \param begin Iterator pointing to the beginning of the input sequence 169 /// \param begin Iterator pointing to the beginning of the input sequence
170 /// \param end Iterator pointing to the end of the input sequence 170 /// \param end Iterator pointing to the end of the input sequence
171 /// \param output Iterator pointing to the beginning of the output sequence 171 /// \param output Iterator pointing to the beginning of the output sequence
172 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) 172 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
173 /// 173 ///
174 /// \return Iterator to the end of the output sequence which has been written 174 /// \return Iterator to the end of the output sequence which has been written
175 /// 175 ///
176 //////////////////////////////////////////////////////////// 176 ////////////////////////////////////////////////////////////
177 template <typename In, typename Out> 177 template <typename In, typename Out>
178 static Out ToLatin1( In begin, In end, Out output, char replacement = 0 ); 178 static Out ToLatin1( In begin, In end, Out output, char replacement = 0 );
179 179
180 //////////////////////////////////////////////////////////// 180 ////////////////////////////////////////////////////////////
181 /// \brief Convert a UTF-8 characters range to UTF-8 181 /// \brief Convert a UTF-8 characters range to UTF-8
182 /// 182 ///
183 /// This functions does nothing more than a direct copy; 183 /// This functions does nothing more than a direct copy;
184 /// it is defined only to provide the same interface as other 184 /// it is defined only to provide the same interface as other
185 /// specializations of the efsw::Utf<> template, and allow 185 /// specializations of the efsw::Utf<> template, and allow
186 /// generic code to be written on top of it. 186 /// generic code to be written on top of it.
187 /// 187 ///
188 /// \param begin Iterator pointing to the beginning of the input sequence 188 /// \param begin Iterator pointing to the beginning of the input sequence
189 /// \param end Iterator pointing to the end of the input sequence 189 /// \param end Iterator pointing to the end of the input sequence
190 /// \param output Iterator pointing to the beginning of the output sequence 190 /// \param output Iterator pointing to the beginning of the output sequence
191 /// 191 ///
192 /// \return Iterator to the end of the output sequence which has been written 192 /// \return Iterator to the end of the output sequence which has been written
193 /// 193 ///
194 //////////////////////////////////////////////////////////// 194 ////////////////////////////////////////////////////////////
195 template <typename In, typename Out> static Out toUtf8( In begin, In end, Out output ); 195 template <typename In, typename Out> static Out toUtf8( In begin, In end, Out output );
196 196
197 //////////////////////////////////////////////////////////// 197 ////////////////////////////////////////////////////////////
198 /// \brief Convert a UTF-8 characters range to UTF-16 198 /// \brief Convert a UTF-8 characters range to UTF-16
199 /// 199 ///
200 /// \param begin Iterator pointing to the beginning of the input sequence 200 /// \param begin Iterator pointing to the beginning of the input sequence
201 /// \param end Iterator pointing to the end of the input sequence 201 /// \param end Iterator pointing to the end of the input sequence
202 /// \param output Iterator pointing to the beginning of the output sequence 202 /// \param output Iterator pointing to the beginning of the output sequence
203 /// 203 ///
204 /// \return Iterator to the end of the output sequence which has been written 204 /// \return Iterator to the end of the output sequence which has been written
205 /// 205 ///
206 //////////////////////////////////////////////////////////// 206 ////////////////////////////////////////////////////////////
207 template <typename In, typename Out> static Out ToUtf16( In begin, In end, Out output ); 207 template <typename In, typename Out> static Out ToUtf16( In begin, In end, Out output );
208 208
209 //////////////////////////////////////////////////////////// 209 ////////////////////////////////////////////////////////////
210 /// \brief Convert a UTF-8 characters range to UTF-32 210 /// \brief Convert a UTF-8 characters range to UTF-32
211 /// 211 ///
212 /// \param begin Iterator pointing to the beginning of the input sequence 212 /// \param begin Iterator pointing to the beginning of the input sequence
213 /// \param end Iterator pointing to the end of the input sequence 213 /// \param end Iterator pointing to the end of the input sequence
214 /// \param output Iterator pointing to the beginning of the output sequence 214 /// \param output Iterator pointing to the beginning of the output sequence
215 /// 215 ///
216 /// \return Iterator to the end of the output sequence which has been written 216 /// \return Iterator to the end of the output sequence which has been written
217 /// 217 ///
218 //////////////////////////////////////////////////////////// 218 ////////////////////////////////////////////////////////////
219 template <typename In, typename Out> static Out ToUtf32( In begin, In end, Out output ); 219 template <typename In, typename Out> static Out ToUtf32( In begin, In end, Out output );
220}; 220};
221 221
222//////////////////////////////////////////////////////////// 222////////////////////////////////////////////////////////////
223/// \brief Specialization of the Utf template for UTF-16 223/// \brief Specialization of the Utf template for UTF-16
224/// 224///
225//////////////////////////////////////////////////////////// 225////////////////////////////////////////////////////////////
226template <> class Utf<16> { 226template <> class Utf<16> {
227 public: 227 public:
228 //////////////////////////////////////////////////////////// 228 ////////////////////////////////////////////////////////////
229 /// \brief Decode a single UTF-16 character 229 /// \brief Decode a single UTF-16 character
230 /// 230 ///
231 /// Decoding a character means finding its unique 32-bits 231 /// Decoding a character means finding its unique 32-bits
232 /// code (called the codepoint) in the Unicode standard. 232 /// code (called the codepoint) in the Unicode standard.
233 /// 233 ///
234 /// \param begin Iterator pointing to the beginning of the input sequence 234 /// \param begin Iterator pointing to the beginning of the input sequence
235 /// \param end Iterator pointing to the end of the input sequence 235 /// \param end Iterator pointing to the end of the input sequence
236 /// \param output Codepoint of the decoded UTF-16 character 236 /// \param output Codepoint of the decoded UTF-16 character
237 /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid 237 /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid
238 /// 238 ///
239 /// \return Iterator pointing to one past the last read element of the input sequence 239 /// \return Iterator pointing to one past the last read element of the input sequence
240 /// 240 ///
241 //////////////////////////////////////////////////////////// 241 ////////////////////////////////////////////////////////////
242 template <typename In> 242 template <typename In>
243 static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 ); 243 static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 );
244 244
245 //////////////////////////////////////////////////////////// 245 ////////////////////////////////////////////////////////////
246 /// \brief Encode a single UTF-16 character 246 /// \brief Encode a single UTF-16 character
247 /// 247 ///
248 /// Encoding a character means converting a unique 32-bits 248 /// Encoding a character means converting a unique 32-bits
249 /// code (called the codepoint) in the target encoding, UTF-16. 249 /// code (called the codepoint) in the target encoding, UTF-16.
250 /// 250 ///
251 /// \param input Codepoint to encode as UTF-16 251 /// \param input Codepoint to encode as UTF-16
252 /// \param output Iterator pointing to the beginning of the output sequence 252 /// \param output Iterator pointing to the beginning of the output sequence
253 /// \param replacement Replacement for characters not convertible to UTF-16 (use 0 to skip them) 253 /// \param replacement Replacement for characters not convertible to UTF-16 (use 0 to skip them)
254 /// 254 ///
255 /// \return Iterator to the end of the output sequence which has been written 255 /// \return Iterator to the end of the output sequence which has been written
256 /// 256 ///
257 //////////////////////////////////////////////////////////// 257 ////////////////////////////////////////////////////////////
258 template <typename Out> static Out Encode( Uint32 input, Out output, Uint16 replacement = 0 ); 258 template <typename Out> static Out Encode( Uint32 input, Out output, Uint16 replacement = 0 );
259 259
260 //////////////////////////////////////////////////////////// 260 ////////////////////////////////////////////////////////////
261 /// \brief Advance to the next UTF-16 character 261 /// \brief Advance to the next UTF-16 character
262 /// 262 ///
263 /// This function is necessary for multi-elements encodings, as 263 /// This function is necessary for multi-elements encodings, as
264 /// a single character may use more than 1 storage element. 264 /// a single character may use more than 1 storage element.
265 /// 265 ///
266 /// \param begin Iterator pointing to the beginning of the input sequence 266 /// \param begin Iterator pointing to the beginning of the input sequence
267 /// \param end Iterator pointing to the end of the input sequence 267 /// \param end Iterator pointing to the end of the input sequence
268 /// 268 ///
269 /// \return Iterator pointing to one past the last read element of the input sequence 269 /// \return Iterator pointing to one past the last read element of the input sequence
270 /// 270 ///
271 //////////////////////////////////////////////////////////// 271 ////////////////////////////////////////////////////////////
272 template <typename In> static In Next( In begin, In end ); 272 template <typename In> static In Next( In begin, In end );
273 273
274 //////////////////////////////////////////////////////////// 274 ////////////////////////////////////////////////////////////
275 /// \brief Count the number of characters of a UTF-16 sequence 275 /// \brief Count the number of characters of a UTF-16 sequence
276 /// 276 ///
277 /// This function is necessary for multi-elements encodings, as 277 /// This function is necessary for multi-elements encodings, as
278 /// a single character may use more than 1 storage element, thus the 278 /// a single character may use more than 1 storage element, thus the
279 /// total size can be different from (begin - end). 279 /// total size can be different from (begin - end).
280 /// 280 ///
281 /// \param begin Iterator pointing to the beginning of the input sequence 281 /// \param begin Iterator pointing to the beginning of the input sequence
282 /// \param end Iterator pointing to the end of the input sequence 282 /// \param end Iterator pointing to the end of the input sequence
283 /// 283 ///
284 /// \return Iterator pointing to one past the last read element of the input sequence 284 /// \return Iterator pointing to one past the last read element of the input sequence
285 /// 285 ///
286 //////////////////////////////////////////////////////////// 286 ////////////////////////////////////////////////////////////
287 template <typename In> static std::size_t Count( In begin, In end ); 287 template <typename In> static std::size_t Count( In begin, In end );
288 288
289 //////////////////////////////////////////////////////////// 289 ////////////////////////////////////////////////////////////
290 /// \brief Convert an ANSI characters range to UTF-16 290 /// \brief Convert an ANSI characters range to UTF-16
291 /// 291 ///
292 /// The current global locale will be used by default, unless you 292 /// The current global locale will be used by default, unless you
293 /// pass a custom one in the \a locale parameter. 293 /// pass a custom one in the \a locale parameter.
294 /// 294 ///
295 /// \param begin Iterator pointing to the beginning of the input sequence 295 /// \param begin Iterator pointing to the beginning of the input sequence
296 /// \param end Iterator pointing to the end of the input sequence 296 /// \param end Iterator pointing to the end of the input sequence
297 /// \param output Iterator pointing to the beginning of the output sequence 297 /// \param output Iterator pointing to the beginning of the output sequence
298 /// \param locale Locale to use for conversion 298 /// \param locale Locale to use for conversion
299 /// 299 ///
300 /// \return Iterator to the end of the output sequence which has been written 300 /// \return Iterator to the end of the output sequence which has been written
301 /// 301 ///
302 //////////////////////////////////////////////////////////// 302 ////////////////////////////////////////////////////////////
303 template <typename In, typename Out> 303 template <typename In, typename Out>
304 static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() ); 304 static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() );
305 305
306 //////////////////////////////////////////////////////////// 306 ////////////////////////////////////////////////////////////
307 /// \brief Convert a wide characters range to UTF-16 307 /// \brief Convert a wide characters range to UTF-16
308 /// 308 ///
309 /// \param begin Iterator pointing to the beginning of the input sequence 309 /// \param begin Iterator pointing to the beginning of the input sequence
310 /// \param end Iterator pointing to the end of the input sequence 310 /// \param end Iterator pointing to the end of the input sequence
311 /// \param output Iterator pointing to the beginning of the output sequence 311 /// \param output Iterator pointing to the beginning of the output sequence
312 /// 312 ///
313 /// \return Iterator to the end of the output sequence which has been written 313 /// \return Iterator to the end of the output sequence which has been written
314 /// 314 ///
315 //////////////////////////////////////////////////////////// 315 ////////////////////////////////////////////////////////////
316 template <typename In, typename Out> static Out FromWide( In begin, In end, Out output ); 316 template <typename In, typename Out> static Out FromWide( In begin, In end, Out output );
317 317
318 //////////////////////////////////////////////////////////// 318 ////////////////////////////////////////////////////////////
319 /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-16 319 /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-16
320 /// 320 ///
321 /// \param begin Iterator pointing to the beginning of the input sequence 321 /// \param begin Iterator pointing to the beginning of the input sequence
322 /// \param end Iterator pointing to the end of the input sequence 322 /// \param end Iterator pointing to the end of the input sequence
323 /// \param output Iterator pointing to the beginning of the output sequence 323 /// \param output Iterator pointing to the beginning of the output sequence
324 /// \param locale Locale to use for conversion 324 /// \param locale Locale to use for conversion
325 /// 325 ///
326 /// \return Iterator to the end of the output sequence which has been written 326 /// \return Iterator to the end of the output sequence which has been written
327 /// 327 ///
328 //////////////////////////////////////////////////////////// 328 ////////////////////////////////////////////////////////////
329 template <typename In, typename Out> static Out FromLatin1( In begin, In end, Out output ); 329 template <typename In, typename Out> static Out FromLatin1( In begin, In end, Out output );
330 330
331 //////////////////////////////////////////////////////////// 331 ////////////////////////////////////////////////////////////
332 /// \brief Convert an UTF-16 characters range to ANSI characters 332 /// \brief Convert an UTF-16 characters range to ANSI characters
333 /// 333 ///
334 /// The current global locale will be used by default, unless you 334 /// The current global locale will be used by default, unless you
335 /// pass a custom one in the \a locale parameter. 335 /// pass a custom one in the \a locale parameter.
336 /// 336 ///
337 /// \param begin Iterator pointing to the beginning of the input sequence 337 /// \param begin Iterator pointing to the beginning of the input sequence
338 /// \param end Iterator pointing to the end of the input sequence 338 /// \param end Iterator pointing to the end of the input sequence
339 /// \param output Iterator pointing to the beginning of the output sequence 339 /// \param output Iterator pointing to the beginning of the output sequence
340 /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) 340 /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them)
341 /// \param locale Locale to use for conversion 341 /// \param locale Locale to use for conversion
342 /// 342 ///
343 /// \return Iterator to the end of the output sequence which has been written 343 /// \return Iterator to the end of the output sequence which has been written
344 /// 344 ///
345 //////////////////////////////////////////////////////////// 345 ////////////////////////////////////////////////////////////
346 template <typename In, typename Out> 346 template <typename In, typename Out>
347 static Out ToAnsi( In begin, In end, Out output, char replacement = 0, 347 static Out ToAnsi( In begin, In end, Out output, char replacement = 0,
348 const std::locale& locale = std::locale() ); 348 const std::locale& locale = std::locale() );
349 349
350#ifndef EFSW_NO_WIDECHAR 350#ifndef EFSW_NO_WIDECHAR
351 //////////////////////////////////////////////////////////// 351 ////////////////////////////////////////////////////////////
352 /// \brief Convert an UTF-16 characters range to wide characters 352 /// \brief Convert an UTF-16 characters range to wide characters
353 /// 353 ///
354 /// \param begin Iterator pointing to the beginning of the input sequence 354 /// \param begin Iterator pointing to the beginning of the input sequence
355 /// \param end Iterator pointing to the end of the input sequence 355 /// \param end Iterator pointing to the end of the input sequence
356 /// \param output Iterator pointing to the beginning of the output sequence 356 /// \param output Iterator pointing to the beginning of the output sequence
357 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) 357 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
358 /// 358 ///
359 /// \return Iterator to the end of the output sequence which has been written 359 /// \return Iterator to the end of the output sequence which has been written
360 /// 360 ///
361 //////////////////////////////////////////////////////////// 361 ////////////////////////////////////////////////////////////
362 template <typename In, typename Out> 362 template <typename In, typename Out>
363 static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 ); 363 static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 );
364#endif 364#endif
365 365
366 //////////////////////////////////////////////////////////// 366 ////////////////////////////////////////////////////////////
367 /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters 367 /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters
368 /// 368 ///
369 /// \param begin Iterator pointing to the beginning of the input sequence 369 /// \param begin Iterator pointing to the beginning of the input sequence
370 /// \param end Iterator pointing to the end of the input sequence 370 /// \param end Iterator pointing to the end of the input sequence
371 /// \param output Iterator pointing to the beginning of the output sequence 371 /// \param output Iterator pointing to the beginning of the output sequence
372 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) 372 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
373 /// 373 ///
374 /// \return Iterator to the end of the output sequence which has been written 374 /// \return Iterator to the end of the output sequence which has been written
375 /// 375 ///
376 //////////////////////////////////////////////////////////// 376 ////////////////////////////////////////////////////////////
377 template <typename In, typename Out> 377 template <typename In, typename Out>
378 static Out ToLatin1( In begin, In end, Out output, char replacement = 0 ); 378 static Out ToLatin1( In begin, In end, Out output, char replacement = 0 );
379 379
380 //////////////////////////////////////////////////////////// 380 ////////////////////////////////////////////////////////////
381 /// \brief Convert a UTF-16 characters range to UTF-8 381 /// \brief Convert a UTF-16 characters range to UTF-8
382 /// 382 ///
383 /// \param begin Iterator pointing to the beginning of the input sequence 383 /// \param begin Iterator pointing to the beginning of the input sequence
384 /// \param end Iterator pointing to the end of the input sequence 384 /// \param end Iterator pointing to the end of the input sequence
385 /// \param output Iterator pointing to the beginning of the output sequence 385 /// \param output Iterator pointing to the beginning of the output sequence
386 /// 386 ///
387 /// \return Iterator to the end of the output sequence which has been written 387 /// \return Iterator to the end of the output sequence which has been written
388 /// 388 ///
389 //////////////////////////////////////////////////////////// 389 ////////////////////////////////////////////////////////////
390 template <typename In, typename Out> static Out toUtf8( In begin, In end, Out output ); 390 template <typename In, typename Out> static Out toUtf8( In begin, In end, Out output );
391 391
392 //////////////////////////////////////////////////////////// 392 ////////////////////////////////////////////////////////////
393 /// \brief Convert a UTF-16 characters range to UTF-16 393 /// \brief Convert a UTF-16 characters range to UTF-16
394 /// 394 ///
395 /// This functions does nothing more than a direct copy; 395 /// This functions does nothing more than a direct copy;
396 /// it is defined only to provide the same interface as other 396 /// it is defined only to provide the same interface as other
397 /// specializations of the efsw::Utf<> template, and allow 397 /// specializations of the efsw::Utf<> template, and allow
398 /// generic code to be written on top of it. 398 /// generic code to be written on top of it.
399 /// 399 ///
400 /// \param begin Iterator pointing to the beginning of the input sequence 400 /// \param begin Iterator pointing to the beginning of the input sequence
401 /// \param end Iterator pointing to the end of the input sequence 401 /// \param end Iterator pointing to the end of the input sequence
402 /// \param output Iterator pointing to the beginning of the output sequence 402 /// \param output Iterator pointing to the beginning of the output sequence
403 /// 403 ///
404 /// \return Iterator to the end of the output sequence which has been written 404 /// \return Iterator to the end of the output sequence which has been written
405 /// 405 ///
406 //////////////////////////////////////////////////////////// 406 ////////////////////////////////////////////////////////////
407 template <typename In, typename Out> static Out ToUtf16( In begin, In end, Out output ); 407 template <typename In, typename Out> static Out ToUtf16( In begin, In end, Out output );
408 408
409 //////////////////////////////////////////////////////////// 409 ////////////////////////////////////////////////////////////
410 /// \brief Convert a UTF-16 characters range to UTF-32 410 /// \brief Convert a UTF-16 characters range to UTF-32
411 /// 411 ///
412 /// \param begin Iterator pointing to the beginning of the input sequence 412 /// \param begin Iterator pointing to the beginning of the input sequence
413 /// \param end Iterator pointing to the end of the input sequence 413 /// \param end Iterator pointing to the end of the input sequence
414 /// \param output Iterator pointing to the beginning of the output sequence 414 /// \param output Iterator pointing to the beginning of the output sequence
415 /// 415 ///
416 /// \return Iterator to the end of the output sequence which has been written 416 /// \return Iterator to the end of the output sequence which has been written
417 /// 417 ///
418 //////////////////////////////////////////////////////////// 418 ////////////////////////////////////////////////////////////
419 template <typename In, typename Out> static Out ToUtf32( In begin, In end, Out output ); 419 template <typename In, typename Out> static Out ToUtf32( In begin, In end, Out output );
420}; 420};
421 421
422//////////////////////////////////////////////////////////// 422////////////////////////////////////////////////////////////
423/// \brief Specialization of the Utf template for UTF-32 423/// \brief Specialization of the Utf template for UTF-32
424/// 424///
425//////////////////////////////////////////////////////////// 425////////////////////////////////////////////////////////////
426template <> class Utf<32> { 426template <> class Utf<32> {
427 public: 427 public:
428 //////////////////////////////////////////////////////////// 428 ////////////////////////////////////////////////////////////
429 /// \brief Decode a single UTF-32 character 429 /// \brief Decode a single UTF-32 character
430 /// 430 ///
431 /// Decoding a character means finding its unique 32-bits 431 /// Decoding a character means finding its unique 32-bits
432 /// code (called the codepoint) in the Unicode standard. 432 /// code (called the codepoint) in the Unicode standard.
433 /// For UTF-32, the character value is the same as the codepoint. 433 /// For UTF-32, the character value is the same as the codepoint.
434 /// 434 ///
435 /// \param begin Iterator pointing to the beginning of the input sequence 435 /// \param begin Iterator pointing to the beginning of the input sequence
436 /// \param end Iterator pointing to the end of the input sequence 436 /// \param end Iterator pointing to the end of the input sequence
437 /// \param output Codepoint of the decoded UTF-32 character 437 /// \param output Codepoint of the decoded UTF-32 character
438 /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid 438 /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid
439 /// 439 ///
440 /// \return Iterator pointing to one past the last read element of the input sequence 440 /// \return Iterator pointing to one past the last read element of the input sequence
441 /// 441 ///
442 //////////////////////////////////////////////////////////// 442 ////////////////////////////////////////////////////////////
443 template <typename In> 443 template <typename In>
444 static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 ); 444 static In Decode( In begin, In end, Uint32& output, Uint32 replacement = 0 );
445 445
446 //////////////////////////////////////////////////////////// 446 ////////////////////////////////////////////////////////////
447 /// \brief Encode a single UTF-32 character 447 /// \brief Encode a single UTF-32 character
448 /// 448 ///
449 /// Encoding a character means converting a unique 32-bits 449 /// Encoding a character means converting a unique 32-bits
450 /// code (called the codepoint) in the target encoding, UTF-32. 450 /// code (called the codepoint) in the target encoding, UTF-32.
451 /// For UTF-32, the codepoint is the same as the character value. 451 /// For UTF-32, the codepoint is the same as the character value.
452 /// 452 ///
453 /// \param input Codepoint to encode as UTF-32 453 /// \param input Codepoint to encode as UTF-32
454 /// \param output Iterator pointing to the beginning of the output sequence 454 /// \param output Iterator pointing to the beginning of the output sequence
455 /// \param replacement Replacement for characters not convertible to UTF-32 (use 0 to skip them) 455 /// \param replacement Replacement for characters not convertible to UTF-32 (use 0 to skip them)
456 /// 456 ///
457 /// \return Iterator to the end of the output sequence which has been written 457 /// \return Iterator to the end of the output sequence which has been written
458 /// 458 ///
459 //////////////////////////////////////////////////////////// 459 ////////////////////////////////////////////////////////////
460 template <typename Out> static Out Encode( Uint32 input, Out output, Uint32 replacement = 0 ); 460 template <typename Out> static Out Encode( Uint32 input, Out output, Uint32 replacement = 0 );
461 461
462 //////////////////////////////////////////////////////////// 462 ////////////////////////////////////////////////////////////
463 /// \brief Advance to the next UTF-32 character 463 /// \brief Advance to the next UTF-32 character
464 /// 464 ///
465 /// This function is trivial for UTF-32, which can store 465 /// This function is trivial for UTF-32, which can store
466 /// every character in a single storage element. 466 /// every character in a single storage element.
467 /// 467 ///
468 /// \param begin Iterator pointing to the beginning of the input sequence 468 /// \param begin Iterator pointing to the beginning of the input sequence
469 /// \param end Iterator pointing to the end of the input sequence 469 /// \param end Iterator pointing to the end of the input sequence
470 /// 470 ///
471 /// \return Iterator pointing to one past the last read element of the input sequence 471 /// \return Iterator pointing to one past the last read element of the input sequence
472 /// 472 ///
473 //////////////////////////////////////////////////////////// 473 ////////////////////////////////////////////////////////////
474 template <typename In> static In Next( In begin, In end ); 474 template <typename In> static In Next( In begin, In end );
475 475
476 //////////////////////////////////////////////////////////// 476 ////////////////////////////////////////////////////////////
477 /// \brief Count the number of characters of a UTF-32 sequence 477 /// \brief Count the number of characters of a UTF-32 sequence
478 /// 478 ///
479 /// This function is trivial for UTF-32, which can store 479 /// This function is trivial for UTF-32, which can store
480 /// every character in a single storage element. 480 /// every character in a single storage element.
481 /// 481 ///
482 /// \param begin Iterator pointing to the beginning of the input sequence 482 /// \param begin Iterator pointing to the beginning of the input sequence
483 /// \param end Iterator pointing to the end of the input sequence 483 /// \param end Iterator pointing to the end of the input sequence
484 /// 484 ///
485 /// \return Iterator pointing to one past the last read element of the input sequence 485 /// \return Iterator pointing to one past the last read element of the input sequence
486 /// 486 ///
487 //////////////////////////////////////////////////////////// 487 ////////////////////////////////////////////////////////////
488 template <typename In> static std::size_t Count( In begin, In end ); 488 template <typename In> static std::size_t Count( In begin, In end );
489 489
490 //////////////////////////////////////////////////////////// 490 ////////////////////////////////////////////////////////////
491 /// \brief Convert an ANSI characters range to UTF-32 491 /// \brief Convert an ANSI characters range to UTF-32
492 /// 492 ///
493 /// The current global locale will be used by default, unless you 493 /// The current global locale will be used by default, unless you
494 /// pass a custom one in the \a locale parameter. 494 /// pass a custom one in the \a locale parameter.
495 /// 495 ///
496 /// \param begin Iterator pointing to the beginning of the input sequence 496 /// \param begin Iterator pointing to the beginning of the input sequence
497 /// \param end Iterator pointing to the end of the input sequence 497 /// \param end Iterator pointing to the end of the input sequence
498 /// \param output Iterator pointing to the beginning of the output sequence 498 /// \param output Iterator pointing to the beginning of the output sequence
499 /// \param locale Locale to use for conversion 499 /// \param locale Locale to use for conversion
500 /// 500 ///
501 /// \return Iterator to the end of the output sequence which has been written 501 /// \return Iterator to the end of the output sequence which has been written
502 /// 502 ///
503 //////////////////////////////////////////////////////////// 503 ////////////////////////////////////////////////////////////
504 template <typename In, typename Out> 504 template <typename In, typename Out>
505 static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() ); 505 static Out FromAnsi( In begin, In end, Out output, const std::locale& locale = std::locale() );
506 506
507 //////////////////////////////////////////////////////////// 507 ////////////////////////////////////////////////////////////
508 /// \brief Convert a wide characters range to UTF-32 508 /// \brief Convert a wide characters range to UTF-32
509 /// 509 ///
510 /// \param begin Iterator pointing to the beginning of the input sequence 510 /// \param begin Iterator pointing to the beginning of the input sequence
511 /// \param end Iterator pointing to the end of the input sequence 511 /// \param end Iterator pointing to the end of the input sequence
512 /// \param output Iterator pointing to the beginning of the output sequence 512 /// \param output Iterator pointing to the beginning of the output sequence
513 /// 513 ///
514 /// \return Iterator to the end of the output sequence which has been written 514 /// \return Iterator to the end of the output sequence which has been written
515 /// 515 ///
516 //////////////////////////////////////////////////////////// 516 ////////////////////////////////////////////////////////////
517 template <typename In, typename Out> static Out FromWide( In begin, In end, Out output ); 517 template <typename In, typename Out> static Out FromWide( In begin, In end, Out output );
518 518
519 //////////////////////////////////////////////////////////// 519 ////////////////////////////////////////////////////////////
520 /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-32 520 /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-32
521 /// 521 ///
522 /// \param begin Iterator pointing to the beginning of the input sequence 522 /// \param begin Iterator pointing to the beginning of the input sequence
523 /// \param end Iterator pointing to the end of the input sequence 523 /// \param end Iterator pointing to the end of the input sequence
524 /// \param output Iterator pointing to the beginning of the output sequence 524 /// \param output Iterator pointing to the beginning of the output sequence
525 /// \param locale Locale to use for conversion 525 /// \param locale Locale to use for conversion
526 /// 526 ///
527 /// \return Iterator to the end of the output sequence which has been written 527 /// \return Iterator to the end of the output sequence which has been written
528 /// 528 ///
529 //////////////////////////////////////////////////////////// 529 ////////////////////////////////////////////////////////////
530 template <typename In, typename Out> static Out FromLatin1( In begin, In end, Out output ); 530 template <typename In, typename Out> static Out FromLatin1( In begin, In end, Out output );
531 531
532 //////////////////////////////////////////////////////////// 532 ////////////////////////////////////////////////////////////
533 /// \brief Convert an UTF-32 characters range to ANSI characters 533 /// \brief Convert an UTF-32 characters range to ANSI characters
534 /// 534 ///
535 /// The current global locale will be used by default, unless you 535 /// The current global locale will be used by default, unless you
536 /// pass a custom one in the \a locale parameter. 536 /// pass a custom one in the \a locale parameter.
537 /// 537 ///
538 /// \param begin Iterator pointing to the beginning of the input sequence 538 /// \param begin Iterator pointing to the beginning of the input sequence
539 /// \param end Iterator pointing to the end of the input sequence 539 /// \param end Iterator pointing to the end of the input sequence
540 /// \param output Iterator pointing to the beginning of the output sequence 540 /// \param output Iterator pointing to the beginning of the output sequence
541 /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) 541 /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them)
542 /// \param locale Locale to use for conversion 542 /// \param locale Locale to use for conversion
543 /// 543 ///
544 /// \return Iterator to the end of the output sequence which has been written 544 /// \return Iterator to the end of the output sequence which has been written
545 /// 545 ///
546 //////////////////////////////////////////////////////////// 546 ////////////////////////////////////////////////////////////
547 template <typename In, typename Out> 547 template <typename In, typename Out>
548 static Out ToAnsi( In begin, In end, Out output, char replacement = 0, 548 static Out ToAnsi( In begin, In end, Out output, char replacement = 0,
549 const std::locale& locale = std::locale() ); 549 const std::locale& locale = std::locale() );
550 550
551#ifndef EFSW_NO_WIDECHAR 551#ifndef EFSW_NO_WIDECHAR
552 //////////////////////////////////////////////////////////// 552 ////////////////////////////////////////////////////////////
553 /// \brief Convert an UTF-32 characters range to wide characters 553 /// \brief Convert an UTF-32 characters range to wide characters
554 /// 554 ///
555 /// \param begin Iterator pointing to the beginning of the input sequence 555 /// \param begin Iterator pointing to the beginning of the input sequence
556 /// \param end Iterator pointing to the end of the input sequence 556 /// \param end Iterator pointing to the end of the input sequence
557 /// \param output Iterator pointing to the beginning of the output sequence 557 /// \param output Iterator pointing to the beginning of the output sequence
558 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) 558 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
559 /// 559 ///
560 /// \return Iterator to the end of the output sequence which has been written 560 /// \return Iterator to the end of the output sequence which has been written
561 /// 561 ///
562 //////////////////////////////////////////////////////////// 562 ////////////////////////////////////////////////////////////
563 template <typename In, typename Out> 563 template <typename In, typename Out>
564 static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 ); 564 static Out ToWide( In begin, In end, Out output, wchar_t replacement = 0 );
565#endif 565#endif
566 566
567 //////////////////////////////////////////////////////////// 567 ////////////////////////////////////////////////////////////
568 /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters 568 /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters
569 /// 569 ///
570 /// \param begin Iterator pointing to the beginning of the input sequence 570 /// \param begin Iterator pointing to the beginning of the input sequence
571 /// \param end Iterator pointing to the end of the input sequence 571 /// \param end Iterator pointing to the end of the input sequence
572 /// \param output Iterator pointing to the beginning of the output sequence 572 /// \param output Iterator pointing to the beginning of the output sequence
573 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) 573 /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them)
574 /// 574 ///
575 /// \return Iterator to the end of the output sequence which has been written 575 /// \return Iterator to the end of the output sequence which has been written
576 /// 576 ///
577 //////////////////////////////////////////////////////////// 577 ////////////////////////////////////////////////////////////
578 template <typename In, typename Out> 578 template <typename In, typename Out>
579 static Out ToLatin1( In begin, In end, Out output, char replacement = 0 ); 579 static Out ToLatin1( In begin, In end, Out output, char replacement = 0 );
580 580
581 //////////////////////////////////////////////////////////// 581 ////////////////////////////////////////////////////////////
582 /// \brief Convert a UTF-32 characters range to UTF-8 582 /// \brief Convert a UTF-32 characters range to UTF-8
583 /// 583 ///
584 /// \param begin Iterator pointing to the beginning of the input sequence 584 /// \param begin Iterator pointing to the beginning of the input sequence
585 /// \param end Iterator pointing to the end of the input sequence 585 /// \param end Iterator pointing to the end of the input sequence
586 /// \param output Iterator pointing to the beginning of the output sequence 586 /// \param output Iterator pointing to the beginning of the output sequence
587 /// 587 ///
588 /// \return Iterator to the end of the output sequence which has been written 588 /// \return Iterator to the end of the output sequence which has been written
589 /// 589 ///
590 //////////////////////////////////////////////////////////// 590 ////////////////////////////////////////////////////////////
591 template <typename In, typename Out> static Out toUtf8( In begin, In end, Out output ); 591 template <typename In, typename Out> static Out toUtf8( In begin, In end, Out output );
592 592
593 //////////////////////////////////////////////////////////// 593 ////////////////////////////////////////////////////////////
594 /// \brief Convert a UTF-32 characters range to UTF-16 594 /// \brief Convert a UTF-32 characters range to UTF-16
595 /// 595 ///
596 /// \param begin Iterator pointing to the beginning of the input sequence 596 /// \param begin Iterator pointing to the beginning of the input sequence
597 /// \param end Iterator pointing to the end of the input sequence 597 /// \param end Iterator pointing to the end of the input sequence
598 /// \param output Iterator pointing to the beginning of the output sequence 598 /// \param output Iterator pointing to the beginning of the output sequence
599 /// 599 ///
600 /// \return Iterator to the end of the output sequence which has been written 600 /// \return Iterator to the end of the output sequence which has been written
601 /// 601 ///
602 //////////////////////////////////////////////////////////// 602 ////////////////////////////////////////////////////////////
603 template <typename In, typename Out> static Out ToUtf16( In begin, In end, Out output ); 603 template <typename In, typename Out> static Out ToUtf16( In begin, In end, Out output );
604 604
605 //////////////////////////////////////////////////////////// 605 ////////////////////////////////////////////////////////////
606 /// \brief Convert a UTF-32 characters range to UTF-32 606 /// \brief Convert a UTF-32 characters range to UTF-32
607 /// 607 ///
608 /// This functions does nothing more than a direct copy; 608 /// This functions does nothing more than a direct copy;
609 /// it is defined only to provide the same interface as other 609 /// it is defined only to provide the same interface as other
610 /// specializations of the efsw::Utf<> template, and allow 610 /// specializations of the efsw::Utf<> template, and allow
611 /// generic code to be written on top of it. 611 /// generic code to be written on top of it.
612 /// 612 ///
613 /// \param begin Iterator pointing to the beginning of the input sequence 613 /// \param begin Iterator pointing to the beginning of the input sequence
614 /// \param end Iterator pointing to the end of the input sequence 614 /// \param end Iterator pointing to the end of the input sequence
615 /// \param output Iterator pointing to the beginning of the output sequence 615 /// \param output Iterator pointing to the beginning of the output sequence
616 /// 616 ///
617 /// \return Iterator to the end of the output sequence which has been written 617 /// \return Iterator to the end of the output sequence which has been written
618 /// 618 ///
619 //////////////////////////////////////////////////////////// 619 ////////////////////////////////////////////////////////////
620 template <typename In, typename Out> static Out ToUtf32( In begin, In end, Out output ); 620 template <typename In, typename Out> static Out ToUtf32( In begin, In end, Out output );
621 621
622 //////////////////////////////////////////////////////////// 622 ////////////////////////////////////////////////////////////
623 /// \brief Decode a single ANSI character to UTF-32 623 /// \brief Decode a single ANSI character to UTF-32
624 /// 624 ///
625 /// This function does not exist in other specializations 625 /// This function does not exist in other specializations
626 /// of efsw::Utf<>, it is defined for convenience (it is used by 626 /// of efsw::Utf<>, it is defined for convenience (it is used by
627 /// several other conversion functions). 627 /// several other conversion functions).
628 /// 628 ///
629 /// \param input Input ANSI character 629 /// \param input Input ANSI character
630 /// \param locale Locale to use for conversion 630 /// \param locale Locale to use for conversion
631 /// 631 ///
632 /// \return Converted character 632 /// \return Converted character
633 /// 633 ///
634 //////////////////////////////////////////////////////////// 634 ////////////////////////////////////////////////////////////
635 template <typename In> 635 template <typename In>
636 static Uint32 DecodeAnsi( In input, const std::locale& locale = std::locale() ); 636 static Uint32 DecodeAnsi( In input, const std::locale& locale = std::locale() );
637 637
638 //////////////////////////////////////////////////////////// 638 ////////////////////////////////////////////////////////////
639 /// \brief Decode a single wide character to UTF-32 639 /// \brief Decode a single wide character to UTF-32
640 /// 640 ///
641 /// This function does not exist in other specializations 641 /// This function does not exist in other specializations
642 /// of efsw::Utf<>, it is defined for convenience (it is used by 642 /// of efsw::Utf<>, it is defined for convenience (it is used by
643 /// several other conversion functions). 643 /// several other conversion functions).
644 /// 644 ///
645 /// \param input Input wide character 645 /// \param input Input wide character
646 /// 646 ///
647 /// \return Converted character 647 /// \return Converted character
648 /// 648 ///
649 //////////////////////////////////////////////////////////// 649 ////////////////////////////////////////////////////////////
650 template <typename In> static Uint32 DecodeWide( In input ); 650 template <typename In> static Uint32 DecodeWide( In input );
651 651
652 //////////////////////////////////////////////////////////// 652 ////////////////////////////////////////////////////////////
653 /// \brief Encode a single UTF-32 character to ANSI 653 /// \brief Encode a single UTF-32 character to ANSI
654 /// 654 ///
655 /// This function does not exist in other specializations 655 /// This function does not exist in other specializations
656 /// of efsw::Utf<>, it is defined for convenience (it is used by 656 /// of efsw::Utf<>, it is defined for convenience (it is used by
657 /// several other conversion functions). 657 /// several other conversion functions).
658 /// 658 ///
659 /// \param codepoint Iterator pointing to the beginning of the input sequence 659 /// \param codepoint Iterator pointing to the beginning of the input sequence
660 /// \param output Iterator pointing to the beginning of the output sequence 660 /// \param output Iterator pointing to the beginning of the output sequence
661 /// \param replacement Replacement if the input character is not convertible to ANSI (use 0 to 661 /// \param replacement Replacement if the input character is not convertible to ANSI (use 0 to
662 /// skip it) \param locale Locale to use for conversion 662 /// skip it) \param locale Locale to use for conversion
663 /// 663 ///
664 /// \return Iterator to the end of the output sequence which has been written 664 /// \return Iterator to the end of the output sequence which has been written
665 /// 665 ///
666 //////////////////////////////////////////////////////////// 666 ////////////////////////////////////////////////////////////
667 template <typename Out> 667 template <typename Out>
668 static Out EncodeAnsi( Uint32 codepoint, Out output, char replacement = 0, 668 static Out EncodeAnsi( Uint32 codepoint, Out output, char replacement = 0,
669 const std::locale& locale = std::locale() ); 669 const std::locale& locale = std::locale() );
670 670
671#ifndef EFSW_NO_WIDECHAR 671#ifndef EFSW_NO_WIDECHAR
672 //////////////////////////////////////////////////////////// 672 ////////////////////////////////////////////////////////////
673 /// \brief Encode a single UTF-32 character to wide 673 /// \brief Encode a single UTF-32 character to wide
674 /// 674 ///
675 /// This function does not exist in other specializations 675 /// This function does not exist in other specializations
676 /// of efsw::Utf<>, it is defined for convenience (it is used by 676 /// of efsw::Utf<>, it is defined for convenience (it is used by
677 /// several other conversion functions). 677 /// several other conversion functions).
678 /// 678 ///
679 /// \param codepoint Iterator pointing to the beginning of the input sequence 679 /// \param codepoint Iterator pointing to the beginning of the input sequence
680 /// \param output Iterator pointing to the beginning of the output sequence 680 /// \param output Iterator pointing to the beginning of the output sequence
681 /// \param replacement Replacement if the input character is not convertible to wide (use 0 to 681 /// \param replacement Replacement if the input character is not convertible to wide (use 0 to
682 /// skip it) 682 /// skip it)
683 /// 683 ///
684 /// \return Iterator to the end of the output sequence which has been written 684 /// \return Iterator to the end of the output sequence which has been written
685 /// 685 ///
686 //////////////////////////////////////////////////////////// 686 ////////////////////////////////////////////////////////////
687 template <typename Out> 687 template <typename Out>
688 static Out EncodeWide( Uint32 codepoint, Out output, wchar_t replacement = 0 ); 688 static Out EncodeWide( Uint32 codepoint, Out output, wchar_t replacement = 0 );
689#endif 689#endif
690}; 690};
691 691
692#include "Utf.inl" 692#include "Utf.inl"
693 693
694// Make typedefs to get rid of the template syntax 694// Make typedefs to get rid of the template syntax
695typedef Utf<8> Utf8; 695typedef Utf<8> Utf8;
696typedef Utf<16> Utf16; 696typedef Utf<16> Utf16;
697typedef Utf<32> Utf32; 697typedef Utf<32> Utf32;
698 698
699} // namespace efsw 699} // namespace efsw
700#endif 700#endif
701 701
702//////////////////////////////////////////////////////////// 702////////////////////////////////////////////////////////////
703/// \class efsw::Utf 703/// \class efsw::Utf
704/// \ingroup system 704/// \ingroup system
705/// 705///
706/// Utility class providing generic functions for UTF conversions. 706/// Utility class providing generic functions for UTF conversions.
707/// 707///
708/// efsw::Utf is a low-level, generic interface for counting, iterating, 708/// efsw::Utf is a low-level, generic interface for counting, iterating,
709/// encoding and decoding Unicode characters and strings. It is able 709/// encoding and decoding Unicode characters and strings. It is able
710/// to handle ANSI, wide, UTF-8, UTF-16 and UTF-32 encodings. 710/// to handle ANSI, wide, UTF-8, UTF-16 and UTF-32 encodings.
711/// 711///
712/// efsw::Utf<X> functions are all static, these classes are not meant to 712/// efsw::Utf<X> functions are all static, these classes are not meant to
713/// be instanciated. All the functions are template, so that you 713/// be instanciated. All the functions are template, so that you
714/// can use any character / string type for a given encoding. 714/// can use any character / string type for a given encoding.
715/// 715///
716/// It has 3 specializations: 716/// It has 3 specializations:
717/// \li efsw::Utf<8> (typedef'd to efsw::Utf8) 717/// \li efsw::Utf<8> (typedef'd to efsw::Utf8)
718/// \li efsw::Utf<16> (typedef'd to efsw::Utf16) 718/// \li efsw::Utf<16> (typedef'd to efsw::Utf16)
719/// \li efsw::Utf<32> (typedef'd to efsw::Utf32) 719/// \li efsw::Utf<32> (typedef'd to efsw::Utf32)
720/// 720///
721//////////////////////////////////////////////////////////// 721////////////////////////////////////////////////////////////
diff --git a/src/3rdParty/efsw/Utf.inl b/src/3rdParty/efsw/Utf.inl
index 5b6c2e0..5c9d7a3 100755..100644
--- a/src/3rdParty/efsw/Utf.inl
+++ b/src/3rdParty/efsw/Utf.inl
@@ -1,576 +1,576 @@
1// References : 1// References :
2// http://www.unicode.org/ 2// http://www.unicode.org/
3// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c 3// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c
4// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.h 4// http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.h
5// http://people.w3.org/rishida/scripts/uniview/conversion 5// http://people.w3.org/rishida/scripts/uniview/conversion
6//////////////////////////////////////////////////////////// 6////////////////////////////////////////////////////////////
7 7
8template <typename In> In Utf<8>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) { 8template <typename In> In Utf<8>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) {
9 // Some useful precomputed data 9 // Some useful precomputed data
10 static const int trailing[256] = { 10 static const int trailing[256] = {
11 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
12 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
14 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
17 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
18 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 18 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
19 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; 19 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
20 static const Uint32 offsets[6] = { 0x00000000, 0x00003080, 0x000E2080, 20 static const Uint32 offsets[6] = { 0x00000000, 0x00003080, 0x000E2080,
21 0x03C82080, 0xFA082080, 0x82082080 }; 21 0x03C82080, 0xFA082080, 0x82082080 };
22 22
23 // Decode the character 23 // Decode the character
24 int trailingBytes = trailing[static_cast<Uint8>( *begin )]; 24 int trailingBytes = trailing[static_cast<Uint8>( *begin )];
25 if ( begin + trailingBytes < end ) { 25 if ( begin + trailingBytes < end ) {
26 output = 0; 26 output = 0;
27 switch ( trailingBytes ) { 27 switch ( trailingBytes ) {
28 case 5: 28 case 5:
29 output += static_cast<Uint8>( *begin++ ); 29 output += static_cast<Uint8>( *begin++ );
30 output <<= 6; 30 output <<= 6;
31 case 4: 31 case 4:
32 output += static_cast<Uint8>( *begin++ ); 32 output += static_cast<Uint8>( *begin++ );
33 output <<= 6; 33 output <<= 6;
34 case 3: 34 case 3:
35 output += static_cast<Uint8>( *begin++ ); 35 output += static_cast<Uint8>( *begin++ );
36 output <<= 6; 36 output <<= 6;
37 case 2: 37 case 2:
38 output += static_cast<Uint8>( *begin++ ); 38 output += static_cast<Uint8>( *begin++ );
39 output <<= 6; 39 output <<= 6;
40 case 1: 40 case 1:
41 output += static_cast<Uint8>( *begin++ ); 41 output += static_cast<Uint8>( *begin++ );
42 output <<= 6; 42 output <<= 6;
43 case 0: 43 case 0:
44 output += static_cast<Uint8>( *begin++ ); 44 output += static_cast<Uint8>( *begin++ );
45 } 45 }
46 output -= offsets[trailingBytes]; 46 output -= offsets[trailingBytes];
47 } else { 47 } else {
48 // Incomplete character 48 // Incomplete character
49 begin = end; 49 begin = end;
50 output = replacement; 50 output = replacement;
51 } 51 }
52 52
53 return begin; 53 return begin;
54} 54}
55 55
56template <typename Out> Out Utf<8>::Encode( Uint32 input, Out output, Uint8 replacement ) { 56template <typename Out> Out Utf<8>::Encode( Uint32 input, Out output, Uint8 replacement ) {
57 // Some useful precomputed data 57 // Some useful precomputed data
58 static const Uint8 firstBytes[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 58 static const Uint8 firstBytes[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
59 59
60 // Encode the character 60 // Encode the character
61 if ( ( input > 0x0010FFFF ) || ( ( input >= 0xD800 ) && ( input <= 0xDBFF ) ) ) { 61 if ( ( input > 0x0010FFFF ) || ( ( input >= 0xD800 ) && ( input <= 0xDBFF ) ) ) {
62 // Invalid character 62 // Invalid character
63 if ( replacement ) 63 if ( replacement )
64 *output++ = replacement; 64 *output++ = replacement;
65 } else { 65 } else {
66 // Valid character 66 // Valid character
67 67
68 // Get the number of bytes to write 68 // Get the number of bytes to write
69 int bytesToWrite = 1; 69 int bytesToWrite = 1;
70 if ( input < 0x80 ) 70 if ( input < 0x80 )
71 bytesToWrite = 1; 71 bytesToWrite = 1;
72 else if ( input < 0x800 ) 72 else if ( input < 0x800 )
73 bytesToWrite = 2; 73 bytesToWrite = 2;
74 else if ( input < 0x10000 ) 74 else if ( input < 0x10000 )
75 bytesToWrite = 3; 75 bytesToWrite = 3;
76 else if ( input <= 0x0010FFFF ) 76 else if ( input <= 0x0010FFFF )
77 bytesToWrite = 4; 77 bytesToWrite = 4;
78 78
79 // Extract the bytes to write 79 // Extract the bytes to write
80 Uint8 bytes[4]; 80 Uint8 bytes[4];
81 switch ( bytesToWrite ) { 81 switch ( bytesToWrite ) {
82 case 4: 82 case 4:
83 bytes[3] = static_cast<Uint8>( ( input | 0x80 ) & 0xBF ); 83 bytes[3] = static_cast<Uint8>( ( input | 0x80 ) & 0xBF );
84 input >>= 6; 84 input >>= 6;
85 case 3: 85 case 3:
86 bytes[2] = static_cast<Uint8>( ( input | 0x80 ) & 0xBF ); 86 bytes[2] = static_cast<Uint8>( ( input | 0x80 ) & 0xBF );
87 input >>= 6; 87 input >>= 6;
88 case 2: 88 case 2:
89 bytes[1] = static_cast<Uint8>( ( input | 0x80 ) & 0xBF ); 89 bytes[1] = static_cast<Uint8>( ( input | 0x80 ) & 0xBF );
90 input >>= 6; 90 input >>= 6;
91 case 1: 91 case 1:
92 bytes[0] = static_cast<Uint8>( input | firstBytes[bytesToWrite] ); 92 bytes[0] = static_cast<Uint8>( input | firstBytes[bytesToWrite] );
93 } 93 }
94 94
95 // Add them to the output 95 // Add them to the output
96 const Uint8* currentByte = bytes; 96 const Uint8* currentByte = bytes;
97 switch ( bytesToWrite ) { 97 switch ( bytesToWrite ) {
98 case 4: 98 case 4:
99 *output++ = *currentByte++; 99 *output++ = *currentByte++;
100 case 3: 100 case 3:
101 *output++ = *currentByte++; 101 *output++ = *currentByte++;
102 case 2: 102 case 2:
103 *output++ = *currentByte++; 103 *output++ = *currentByte++;
104 case 1: 104 case 1:
105 *output++ = *currentByte++; 105 *output++ = *currentByte++;
106 } 106 }
107 } 107 }
108 108
109 return output; 109 return output;
110} 110}
111 111
112template <typename In> In Utf<8>::Next( In begin, In end ) { 112template <typename In> In Utf<8>::Next( In begin, In end ) {
113 Uint32 codepoint; 113 Uint32 codepoint;
114 return Decode( begin, end, codepoint ); 114 return Decode( begin, end, codepoint );
115} 115}
116 116
117template <typename In> std::size_t Utf<8>::Count( In begin, In end ) { 117template <typename In> std::size_t Utf<8>::Count( In begin, In end ) {
118 std::size_t length = 0; 118 std::size_t length = 0;
119 while ( begin < end ) { 119 while ( begin < end ) {
120 begin = Next( begin, end ); 120 begin = Next( begin, end );
121 ++length; 121 ++length;
122 } 122 }
123 123
124 return length; 124 return length;
125} 125}
126 126
127template <typename In, typename Out> 127template <typename In, typename Out>
128Out Utf<8>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { 128Out Utf<8>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) {
129 while ( begin < end ) { 129 while ( begin < end ) {
130 Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale ); 130 Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale );
131 output = Encode( codepoint, output ); 131 output = Encode( codepoint, output );
132 } 132 }
133 133
134 return output; 134 return output;
135} 135}
136 136
137template <typename In, typename Out> Out Utf<8>::FromWide( In begin, In end, Out output ) { 137template <typename In, typename Out> Out Utf<8>::FromWide( In begin, In end, Out output ) {
138 while ( begin < end ) { 138 while ( begin < end ) {
139 Uint32 codepoint = Utf<32>::DecodeWide( *begin++ ); 139 Uint32 codepoint = Utf<32>::DecodeWide( *begin++ );
140 output = Encode( codepoint, output ); 140 output = Encode( codepoint, output );
141 } 141 }
142 142
143 return output; 143 return output;
144} 144}
145 145
146template <typename In, typename Out> Out Utf<8>::FromLatin1( In begin, In end, Out output ) { 146template <typename In, typename Out> Out Utf<8>::FromLatin1( In begin, In end, Out output ) {
147 // Latin-1 is directly compatible with Unicode encodings, 147 // Latin-1 is directly compatible with Unicode encodings,
148 // and can thus be treated as (a sub-range of) UTF-32 148 // and can thus be treated as (a sub-range of) UTF-32
149 while ( begin < end ) 149 while ( begin < end )
150 output = Encode( *begin++, output ); 150 output = Encode( *begin++, output );
151 151
152 return output; 152 return output;
153} 153}
154 154
155template <typename In, typename Out> 155template <typename In, typename Out>
156Out Utf<8>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { 156Out Utf<8>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) {
157 while ( begin < end ) { 157 while ( begin < end ) {
158 Uint32 codepoint; 158 Uint32 codepoint;
159 begin = Decode( begin, end, codepoint ); 159 begin = Decode( begin, end, codepoint );
160 output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale ); 160 output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale );
161 } 161 }
162 162
163 return output; 163 return output;
164} 164}
165 165
166#ifndef EFSW_NO_WIDECHAR 166#ifndef EFSW_NO_WIDECHAR
167template <typename In, typename Out> 167template <typename In, typename Out>
168Out Utf<8>::ToWide( In begin, In end, Out output, wchar_t replacement ) { 168Out Utf<8>::ToWide( In begin, In end, Out output, wchar_t replacement ) {
169 while ( begin < end ) { 169 while ( begin < end ) {
170 Uint32 codepoint; 170 Uint32 codepoint;
171 begin = Decode( begin, end, codepoint ); 171 begin = Decode( begin, end, codepoint );
172 output = Utf<32>::EncodeWide( codepoint, output, replacement ); 172 output = Utf<32>::EncodeWide( codepoint, output, replacement );
173 } 173 }
174 174
175 return output; 175 return output;
176} 176}
177#endif 177#endif
178 178
179template <typename In, typename Out> 179template <typename In, typename Out>
180Out Utf<8>::ToLatin1( In begin, In end, Out output, char replacement ) { 180Out Utf<8>::ToLatin1( In begin, In end, Out output, char replacement ) {
181 // Latin-1 is directly compatible with Unicode encodings, 181 // Latin-1 is directly compatible with Unicode encodings,
182 // and can thus be treated as (a sub-range of) UTF-32 182 // and can thus be treated as (a sub-range of) UTF-32
183 while ( begin < end ) { 183 while ( begin < end ) {
184 Uint32 codepoint; 184 Uint32 codepoint;
185 begin = Decode( begin, end, codepoint ); 185 begin = Decode( begin, end, codepoint );
186 *output++ = codepoint < 256 ? static_cast<char>( codepoint ) : replacement; 186 *output++ = codepoint < 256 ? static_cast<char>( codepoint ) : replacement;
187 } 187 }
188 188
189 return output; 189 return output;
190} 190}
191 191
192template <typename In, typename Out> Out Utf<8>::toUtf8( In begin, In end, Out output ) { 192template <typename In, typename Out> Out Utf<8>::toUtf8( In begin, In end, Out output ) {
193 while ( begin < end ) 193 while ( begin < end )
194 *output++ = *begin++; 194 *output++ = *begin++;
195 195
196 return output; 196 return output;
197} 197}
198 198
199template <typename In, typename Out> Out Utf<8>::ToUtf16( In begin, In end, Out output ) { 199template <typename In, typename Out> Out Utf<8>::ToUtf16( In begin, In end, Out output ) {
200 while ( begin < end ) { 200 while ( begin < end ) {
201 Uint32 codepoint; 201 Uint32 codepoint;
202 begin = Decode( begin, end, codepoint ); 202 begin = Decode( begin, end, codepoint );
203 output = Utf<16>::Encode( codepoint, output ); 203 output = Utf<16>::Encode( codepoint, output );
204 } 204 }
205 205
206 return output; 206 return output;
207} 207}
208 208
209template <typename In, typename Out> Out Utf<8>::ToUtf32( In begin, In end, Out output ) { 209template <typename In, typename Out> Out Utf<8>::ToUtf32( In begin, In end, Out output ) {
210 while ( begin < end ) { 210 while ( begin < end ) {
211 Uint32 codepoint; 211 Uint32 codepoint;
212 begin = Decode( begin, end, codepoint ); 212 begin = Decode( begin, end, codepoint );
213 *output++ = codepoint; 213 *output++ = codepoint;
214 } 214 }
215 215
216 return output; 216 return output;
217} 217}
218 218
219template <typename In> In Utf<16>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) { 219template <typename In> In Utf<16>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) {
220 Uint16 first = *begin++; 220 Uint16 first = *begin++;
221 221
222 // If it's a surrogate pair, first convert to a single UTF-32 character 222 // If it's a surrogate pair, first convert to a single UTF-32 character
223 if ( ( first >= 0xD800 ) && ( first <= 0xDBFF ) ) { 223 if ( ( first >= 0xD800 ) && ( first <= 0xDBFF ) ) {
224 if ( begin < end ) { 224 if ( begin < end ) {
225 Uint32 second = *begin++; 225 Uint32 second = *begin++;
226 if ( ( second >= 0xDC00 ) && ( second <= 0xDFFF ) ) { 226 if ( ( second >= 0xDC00 ) && ( second <= 0xDFFF ) ) {
227 // The second element is valid: convert the two elements to a UTF-32 character 227 // The second element is valid: convert the two elements to a UTF-32 character
228 output = static_cast<Uint32>( ( ( first - 0xD800 ) << 10 ) + ( second - 0xDC00 ) + 228 output = static_cast<Uint32>( ( ( first - 0xD800 ) << 10 ) + ( second - 0xDC00 ) +
229 0x0010000 ); 229 0x0010000 );
230 } else { 230 } else {
231 // Invalid character 231 // Invalid character
232 output = replacement; 232 output = replacement;
233 } 233 }
234 } else { 234 } else {
235 // Invalid character 235 // Invalid character
236 begin = end; 236 begin = end;
237 output = replacement; 237 output = replacement;
238 } 238 }
239 } else { 239 } else {
240 // We can make a direct copy 240 // We can make a direct copy
241 output = first; 241 output = first;
242 } 242 }
243 243
244 return begin; 244 return begin;
245} 245}
246 246
247template <typename Out> Out Utf<16>::Encode( Uint32 input, Out output, Uint16 replacement ) { 247template <typename Out> Out Utf<16>::Encode( Uint32 input, Out output, Uint16 replacement ) {
248 if ( input < 0xFFFF ) { 248 if ( input < 0xFFFF ) {
249 // The character can be copied directly, we just need to check if it's in the valid range 249 // The character can be copied directly, we just need to check if it's in the valid range
250 if ( ( input >= 0xD800 ) && ( input <= 0xDFFF ) ) { 250 if ( ( input >= 0xD800 ) && ( input <= 0xDFFF ) ) {
251 // Invalid character (this range is reserved) 251 // Invalid character (this range is reserved)
252 if ( replacement ) 252 if ( replacement )
253 *output++ = replacement; 253 *output++ = replacement;
254 } else { 254 } else {
255 // Valid character directly convertible to a single UTF-16 character 255 // Valid character directly convertible to a single UTF-16 character
256 *output++ = static_cast<Uint16>( input ); 256 *output++ = static_cast<Uint16>( input );
257 } 257 }
258 } else if ( input > 0x0010FFFF ) { 258 } else if ( input > 0x0010FFFF ) {
259 // Invalid character (greater than the maximum unicode value) 259 // Invalid character (greater than the maximum unicode value)
260 if ( replacement ) 260 if ( replacement )
261 *output++ = replacement; 261 *output++ = replacement;
262 } else { 262 } else {
263 // The input character will be converted to two UTF-16 elements 263 // The input character will be converted to two UTF-16 elements
264 input -= 0x0010000; 264 input -= 0x0010000;
265 *output++ = static_cast<Uint16>( ( input >> 10 ) + 0xD800 ); 265 *output++ = static_cast<Uint16>( ( input >> 10 ) + 0xD800 );
266 *output++ = static_cast<Uint16>( ( input & 0x3FFUL ) + 0xDC00 ); 266 *output++ = static_cast<Uint16>( ( input & 0x3FFUL ) + 0xDC00 );
267 } 267 }
268 268
269 return output; 269 return output;
270} 270}
271 271
272template <typename In> In Utf<16>::Next( In begin, In end ) { 272template <typename In> In Utf<16>::Next( In begin, In end ) {
273 Uint32 codepoint; 273 Uint32 codepoint;
274 return Decode( begin, end, codepoint ); 274 return Decode( begin, end, codepoint );
275} 275}
276 276
277template <typename In> std::size_t Utf<16>::Count( In begin, In end ) { 277template <typename In> std::size_t Utf<16>::Count( In begin, In end ) {
278 std::size_t length = 0; 278 std::size_t length = 0;
279 while ( begin < end ) { 279 while ( begin < end ) {
280 begin = Next( begin, end ); 280 begin = Next( begin, end );
281 ++length; 281 ++length;
282 } 282 }
283 283
284 return length; 284 return length;
285} 285}
286 286
287template <typename In, typename Out> 287template <typename In, typename Out>
288Out Utf<16>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { 288Out Utf<16>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) {
289 while ( begin < end ) { 289 while ( begin < end ) {
290 Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale ); 290 Uint32 codepoint = Utf<32>::DecodeAnsi( *begin++, locale );
291 output = Encode( codepoint, output ); 291 output = Encode( codepoint, output );
292 } 292 }
293 293
294 return output; 294 return output;
295} 295}
296 296
297template <typename In, typename Out> Out Utf<16>::FromWide( In begin, In end, Out output ) { 297template <typename In, typename Out> Out Utf<16>::FromWide( In begin, In end, Out output ) {
298 while ( begin < end ) { 298 while ( begin < end ) {
299 Uint32 codepoint = Utf<32>::DecodeWide( *begin++ ); 299 Uint32 codepoint = Utf<32>::DecodeWide( *begin++ );
300 output = Encode( codepoint, output ); 300 output = Encode( codepoint, output );
301 } 301 }
302 302
303 return output; 303 return output;
304} 304}
305 305
306template <typename In, typename Out> Out Utf<16>::FromLatin1( In begin, In end, Out output ) { 306template <typename In, typename Out> Out Utf<16>::FromLatin1( In begin, In end, Out output ) {
307 // Latin-1 is directly compatible with Unicode encodings, 307 // Latin-1 is directly compatible with Unicode encodings,
308 // and can thus be treated as (a sub-range of) UTF-32 308 // and can thus be treated as (a sub-range of) UTF-32
309 while ( begin < end ) 309 while ( begin < end )
310 *output++ = *begin++; 310 *output++ = *begin++;
311 311
312 return output; 312 return output;
313} 313}
314 314
315template <typename In, typename Out> 315template <typename In, typename Out>
316Out Utf<16>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { 316Out Utf<16>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) {
317 while ( begin < end ) { 317 while ( begin < end ) {
318 Uint32 codepoint; 318 Uint32 codepoint;
319 begin = Decode( begin, end, codepoint ); 319 begin = Decode( begin, end, codepoint );
320 output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale ); 320 output = Utf<32>::EncodeAnsi( codepoint, output, replacement, locale );
321 } 321 }
322 322
323 return output; 323 return output;
324} 324}
325 325
326#ifndef EFSW_NO_WIDECHAR 326#ifndef EFSW_NO_WIDECHAR
327template <typename In, typename Out> 327template <typename In, typename Out>
328Out Utf<16>::ToWide( In begin, In end, Out output, wchar_t replacement ) { 328Out Utf<16>::ToWide( In begin, In end, Out output, wchar_t replacement ) {
329 while ( begin < end ) { 329 while ( begin < end ) {
330 Uint32 codepoint; 330 Uint32 codepoint;
331 begin = Decode( begin, end, codepoint ); 331 begin = Decode( begin, end, codepoint );
332 output = Utf<32>::EncodeWide( codepoint, output, replacement ); 332 output = Utf<32>::EncodeWide( codepoint, output, replacement );
333 } 333 }
334 334
335 return output; 335 return output;
336} 336}
337#endif 337#endif
338 338
339template <typename In, typename Out> 339template <typename In, typename Out>
340Out Utf<16>::ToLatin1( In begin, In end, Out output, char replacement ) { 340Out Utf<16>::ToLatin1( In begin, In end, Out output, char replacement ) {
341 // Latin-1 is directly compatible with Unicode encodings, 341 // Latin-1 is directly compatible with Unicode encodings,
342 // and can thus be treated as (a sub-range of) UTF-32 342 // and can thus be treated as (a sub-range of) UTF-32
343 while ( begin < end ) { 343 while ( begin < end ) {
344 *output++ = *begin < 256 ? static_cast<char>( *begin ) : replacement; 344 *output++ = *begin < 256 ? static_cast<char>( *begin ) : replacement;
345 begin++; 345 begin++;
346 } 346 }
347 347
348 return output; 348 return output;
349} 349}
350 350
351template <typename In, typename Out> Out Utf<16>::toUtf8( In begin, In end, Out output ) { 351template <typename In, typename Out> Out Utf<16>::toUtf8( In begin, In end, Out output ) {
352 while ( begin < end ) { 352 while ( begin < end ) {
353 Uint32 codepoint; 353 Uint32 codepoint;
354 begin = Decode( begin, end, codepoint ); 354 begin = Decode( begin, end, codepoint );
355 output = Utf<8>::Encode( codepoint, output ); 355 output = Utf<8>::Encode( codepoint, output );
356 } 356 }
357 357
358 return output; 358 return output;
359} 359}
360 360
361template <typename In, typename Out> Out Utf<16>::ToUtf16( In begin, In end, Out output ) { 361template <typename In, typename Out> Out Utf<16>::ToUtf16( In begin, In end, Out output ) {
362 while ( begin < end ) 362 while ( begin < end )
363 *output++ = *begin++; 363 *output++ = *begin++;
364 364
365 return output; 365 return output;
366} 366}
367 367
368template <typename In, typename Out> Out Utf<16>::ToUtf32( In begin, In end, Out output ) { 368template <typename In, typename Out> Out Utf<16>::ToUtf32( In begin, In end, Out output ) {
369 while ( begin < end ) { 369 while ( begin < end ) {
370 Uint32 codepoint; 370 Uint32 codepoint;
371 begin = Decode( begin, end, codepoint ); 371 begin = Decode( begin, end, codepoint );
372 *output++ = codepoint; 372 *output++ = codepoint;
373 } 373 }
374 374
375 return output; 375 return output;
376} 376}
377 377
378template <typename In> In Utf<32>::Decode( In begin, In /*end*/, Uint32& output, Uint32 ) { 378template <typename In> In Utf<32>::Decode( In begin, In /*end*/, Uint32& output, Uint32 ) {
379 output = *begin++; 379 output = *begin++;
380 return begin; 380 return begin;
381} 381}
382 382
383template <typename Out> Out Utf<32>::Encode( Uint32 input, Out output, Uint32 /*replacement*/ ) { 383template <typename Out> Out Utf<32>::Encode( Uint32 input, Out output, Uint32 /*replacement*/ ) {
384 *output++ = input; 384 *output++ = input;
385 return output; 385 return output;
386} 386}
387 387
388template <typename In> In Utf<32>::Next( In begin, In /*end*/ ) { 388template <typename In> In Utf<32>::Next( In begin, In /*end*/ ) {
389 return ++begin; 389 return ++begin;
390} 390}
391 391
392template <typename In> std::size_t Utf<32>::Count( In begin, In end ) { 392template <typename In> std::size_t Utf<32>::Count( In begin, In end ) {
393 return begin - end; 393 return begin - end;
394} 394}
395 395
396template <typename In, typename Out> 396template <typename In, typename Out>
397Out Utf<32>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { 397Out Utf<32>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) {
398 while ( begin < end ) 398 while ( begin < end )
399 *output++ = DecodeAnsi( *begin++, locale ); 399 *output++ = DecodeAnsi( *begin++, locale );
400 400
401 return output; 401 return output;
402} 402}
403 403
404template <typename In, typename Out> Out Utf<32>::FromWide( In begin, In end, Out output ) { 404template <typename In, typename Out> Out Utf<32>::FromWide( In begin, In end, Out output ) {
405 while ( begin < end ) 405 while ( begin < end )
406 *output++ = DecodeWide( *begin++ ); 406 *output++ = DecodeWide( *begin++ );
407 407
408 return output; 408 return output;
409} 409}
410 410
411template <typename In, typename Out> Out Utf<32>::FromLatin1( In begin, In end, Out output ) { 411template <typename In, typename Out> Out Utf<32>::FromLatin1( In begin, In end, Out output ) {
412 // Latin-1 is directly compatible with Unicode encodings, 412 // Latin-1 is directly compatible with Unicode encodings,
413 // and can thus be treated as (a sub-range of) UTF-32 413 // and can thus be treated as (a sub-range of) UTF-32
414 while ( begin < end ) 414 while ( begin < end )
415 *output++ = *begin++; 415 *output++ = *begin++;
416 416
417 return output; 417 return output;
418} 418}
419 419
420template <typename In, typename Out> 420template <typename In, typename Out>
421Out Utf<32>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { 421Out Utf<32>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) {
422 while ( begin < end ) 422 while ( begin < end )
423 output = EncodeAnsi( *begin++, output, replacement, locale ); 423 output = EncodeAnsi( *begin++, output, replacement, locale );
424 424
425 return output; 425 return output;
426} 426}
427 427
428#ifndef EFSW_NO_WIDECHAR 428#ifndef EFSW_NO_WIDECHAR
429template <typename In, typename Out> 429template <typename In, typename Out>
430Out Utf<32>::ToWide( In begin, In end, Out output, wchar_t replacement ) { 430Out Utf<32>::ToWide( In begin, In end, Out output, wchar_t replacement ) {
431 while ( begin < end ) 431 while ( begin < end )
432 output = EncodeWide( *begin++, output, replacement ); 432 output = EncodeWide( *begin++, output, replacement );
433 433
434 return output; 434 return output;
435} 435}
436#endif 436#endif
437 437
438template <typename In, typename Out> 438template <typename In, typename Out>
439Out Utf<32>::ToLatin1( In begin, In end, Out output, char replacement ) { 439Out Utf<32>::ToLatin1( In begin, In end, Out output, char replacement ) {
440 // Latin-1 is directly compatible with Unicode encodings, 440 // Latin-1 is directly compatible with Unicode encodings,
441 // and can thus be treated as (a sub-range of) UTF-32 441 // and can thus be treated as (a sub-range of) UTF-32
442 while ( begin < end ) { 442 while ( begin < end ) {
443 *output++ = *begin < 256 ? static_cast<char>( *begin ) : replacement; 443 *output++ = *begin < 256 ? static_cast<char>( *begin ) : replacement;
444 begin++; 444 begin++;
445 } 445 }
446 446
447 return output; 447 return output;
448} 448}
449 449
450template <typename In, typename Out> Out Utf<32>::toUtf8( In begin, In end, Out output ) { 450template <typename In, typename Out> Out Utf<32>::toUtf8( In begin, In end, Out output ) {
451 while ( begin < end ) 451 while ( begin < end )
452 output = Utf<8>::Encode( *begin++, output ); 452 output = Utf<8>::Encode( *begin++, output );
453 453
454 return output; 454 return output;
455} 455}
456 456
457template <typename In, typename Out> Out Utf<32>::ToUtf16( In begin, In end, Out output ) { 457template <typename In, typename Out> Out Utf<32>::ToUtf16( In begin, In end, Out output ) {
458 while ( begin < end ) 458 while ( begin < end )
459 output = Utf<16>::Encode( *begin++, output ); 459 output = Utf<16>::Encode( *begin++, output );
460 460
461 return output; 461 return output;
462} 462}
463 463
464template <typename In, typename Out> Out Utf<32>::ToUtf32( In begin, In end, Out output ) { 464template <typename In, typename Out> Out Utf<32>::ToUtf32( In begin, In end, Out output ) {
465 while ( begin < end ) 465 while ( begin < end )
466 *output++ = *begin++; 466 *output++ = *begin++;
467 467
468 return output; 468 return output;
469} 469}
470 470
471template <typename In> Uint32 Utf<32>::DecodeAnsi( In input, const std::locale& locale ) { 471template <typename In> Uint32 Utf<32>::DecodeAnsi( In input, const std::locale& locale ) {
472 // On Windows, gcc's standard library (glibc++) has almost 472 // On Windows, gcc's standard library (glibc++) has almost
473 // no support for Unicode stuff. As a consequence, in this 473 // no support for Unicode stuff. As a consequence, in this
474 // context we can only use the default locale and ignore 474 // context we can only use the default locale and ignore
475 // the one passed as parameter. 475 // the one passed as parameter.
476 476
477#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \ 477#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \
478 ( defined( __GLIBCPP__ ) || \ 478 ( defined( __GLIBCPP__ ) || \
479 defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \ 479 defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \
480 !( defined( __SGI_STL_PORT ) || \ 480 !( defined( __SGI_STL_PORT ) || \
481 defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */ 481 defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */
482 482
483 wchar_t character = 0; 483 wchar_t character = 0;
484 mbtowc( &character, &input, 1 ); 484 mbtowc( &character, &input, 1 );
485 return static_cast<Uint32>( character ); 485 return static_cast<Uint32>( character );
486 486
487#else 487#else
488// Get the facet of the locale which deals with character conversion 488// Get the facet of the locale which deals with character conversion
489#ifndef EFSW_NO_WIDECHAR 489#ifndef EFSW_NO_WIDECHAR
490 const std::ctype<wchar_t>& facet = std::use_facet<std::ctype<wchar_t>>( locale ); 490 const std::ctype<wchar_t>& facet = std::use_facet<std::ctype<wchar_t>>( locale );
491#else 491#else
492 const std::ctype<char>& facet = std::use_facet<std::ctype<char>>( locale ); 492 const std::ctype<char>& facet = std::use_facet<std::ctype<char>>( locale );
493#endif 493#endif
494 494
495 // Use the facet to convert each character of the input string 495 // Use the facet to convert each character of the input string
496 return static_cast<Uint32>( facet.widen( input ) ); 496 return static_cast<Uint32>( facet.widen( input ) );
497 497
498#endif 498#endif
499} 499}
500 500
501template <typename In> Uint32 Utf<32>::DecodeWide( In input ) { 501template <typename In> Uint32 Utf<32>::DecodeWide( In input ) {
502 // The encoding of wide characters is not well defined and is left to the system; 502 // The encoding of wide characters is not well defined and is left to the system;
503 // however we can safely assume that it is UCS-2 on Windows and 503 // however we can safely assume that it is UCS-2 on Windows and
504 // UCS-4 on Unix systems. 504 // UCS-4 on Unix systems.
505 // In both cases, a simple copy is enough (UCS-2 is a subset of UCS-4, 505 // In both cases, a simple copy is enough (UCS-2 is a subset of UCS-4,
506 // and UCS-4 *is* UTF-32). 506 // and UCS-4 *is* UTF-32).
507 507
508 return input; 508 return input;
509} 509}
510 510
511template <typename Out> 511template <typename Out>
512Out Utf<32>::EncodeAnsi( Uint32 codepoint, Out output, char replacement, 512Out Utf<32>::EncodeAnsi( Uint32 codepoint, Out output, char replacement,
513 const std::locale& locale ) { 513 const std::locale& locale ) {
514 // On Windows, gcc's standard library (glibc++) has almost 514 // On Windows, gcc's standard library (glibc++) has almost
515 // no support for Unicode stuff. As a consequence, in this 515 // no support for Unicode stuff. As a consequence, in this
516 // context we can only use the default locale and ignore 516 // context we can only use the default locale and ignore
517 // the one passed as parameter. 517 // the one passed as parameter.
518 518
519#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \ 519#if EFSW_PLATFORM == EFSW_PLATFORM_WIN && /* if Windows ... */ \
520 ( defined( __GLIBCPP__ ) || \ 520 ( defined( __GLIBCPP__ ) || \
521 defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \ 521 defined( __GLIBCXX__ ) ) && /* ... and standard library is glibc++ ... */ \
522 !( defined( __SGI_STL_PORT ) || \ 522 !( defined( __SGI_STL_PORT ) || \
523 defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */ 523 defined( _STLPORT_VERSION ) ) /* ... and STLPort is not used on top of it */
524 524
525 char character = 0; 525 char character = 0;
526 if ( wctomb( &character, static_cast<wchar_t>( codepoint ) ) >= 0 ) 526 if ( wctomb( &character, static_cast<wchar_t>( codepoint ) ) >= 0 )
527 *output++ = character; 527 *output++ = character;
528 else if ( replacement ) 528 else if ( replacement )
529 *output++ = replacement; 529 *output++ = replacement;
530 530
531 return output; 531 return output;
532 532
533#else 533#else
534// Get the facet of the locale which deals with character conversion 534// Get the facet of the locale which deals with character conversion
535#ifndef EFSW_NO_WIDECHAR 535#ifndef EFSW_NO_WIDECHAR
536 const std::ctype<wchar_t>& facet = std::use_facet<std::ctype<wchar_t>>( locale ); 536 const std::ctype<wchar_t>& facet = std::use_facet<std::ctype<wchar_t>>( locale );
537#else 537#else
538 const std::ctype<char>& facet = std::use_facet<std::ctype<char>>( locale ); 538 const std::ctype<char>& facet = std::use_facet<std::ctype<char>>( locale );
539#endif 539#endif
540 540
541 // Use the facet to convert each character of the input string 541 // Use the facet to convert each character of the input string
542 *output++ = facet.narrow( static_cast<wchar_t>( codepoint ), replacement ); 542 *output++ = facet.narrow( static_cast<wchar_t>( codepoint ), replacement );
543 543
544 return output; 544 return output;
545 545
546#endif 546#endif
547} 547}
548 548
549#ifndef EFSW_NO_WIDECHAR 549#ifndef EFSW_NO_WIDECHAR
550template <typename Out> 550template <typename Out>
551Out Utf<32>::EncodeWide( Uint32 codepoint, Out output, wchar_t replacement ) { 551Out Utf<32>::EncodeWide( Uint32 codepoint, Out output, wchar_t replacement ) {
552 // The encoding of wide characters is not well defined and is left to the system; 552 // The encoding of wide characters is not well defined and is left to the system;
553 // however we can safely assume that it is UCS-2 on Windows and 553 // however we can safely assume that it is UCS-2 on Windows and
554 // UCS-4 on Unix systems. 554 // UCS-4 on Unix systems.
555 // For UCS-2 we need to check if the source characters fits in (UCS-2 is a subset of UCS-4). 555 // For UCS-2 we need to check if the source characters fits in (UCS-2 is a subset of UCS-4).
556 // For UCS-4 we can do a direct copy (UCS-4 *is* UTF-32). 556 // For UCS-4 we can do a direct copy (UCS-4 *is* UTF-32).
557 557
558 switch ( sizeof( wchar_t ) ) { 558 switch ( sizeof( wchar_t ) ) {
559 case 4: { 559 case 4: {
560 *output++ = static_cast<wchar_t>( codepoint ); 560 *output++ = static_cast<wchar_t>( codepoint );
561 break; 561 break;
562 } 562 }
563 563
564 default: { 564 default: {
565 if ( ( codepoint <= 0xFFFF ) && ( ( codepoint < 0xD800 ) || ( codepoint > 0xDFFF ) ) ) { 565 if ( ( codepoint <= 0xFFFF ) && ( ( codepoint < 0xD800 ) || ( codepoint > 0xDFFF ) ) ) {
566 *output++ = static_cast<wchar_t>( codepoint ); 566 *output++ = static_cast<wchar_t>( codepoint );
567 } else if ( replacement ) { 567 } else if ( replacement ) {
568 *output++ = replacement; 568 *output++ = replacement;
569 } 569 }
570 break; 570 break;
571 } 571 }
572 } 572 }
573 573
574 return output; 574 return output;
575} 575}
576#endif 576#endif
diff --git a/src/3rdParty/efsw/Watcher.cpp b/src/3rdParty/efsw/Watcher.cpp
index 913ae3c..913ae3c 100755..100644
--- a/src/3rdParty/efsw/Watcher.cpp
+++ b/src/3rdParty/efsw/Watcher.cpp
diff --git a/src/3rdParty/efsw/Watcher.hpp b/src/3rdParty/efsw/Watcher.hpp
index 84f0980..84f0980 100755..100644
--- a/src/3rdParty/efsw/Watcher.hpp
+++ b/src/3rdParty/efsw/Watcher.hpp
diff --git a/src/3rdParty/efsw/WatcherFSEvents.cpp b/src/3rdParty/efsw/WatcherFSEvents.cpp
index b562d3c..f963374 100755..100644
--- a/src/3rdParty/efsw/WatcherFSEvents.cpp
+++ b/src/3rdParty/efsw/WatcherFSEvents.cpp
@@ -8,22 +8,11 @@
8namespace efsw { 8namespace efsw {
9 9
10WatcherFSEvents::WatcherFSEvents() : 10WatcherFSEvents::WatcherFSEvents() :
11 Watcher(), FWatcher( NULL ), FSStream( NULL ), WatcherGen( NULL ), initializedAsync( false ) {} 11 Watcher(), FWatcher( NULL ), FSStream( NULL ), WatcherGen( NULL ) {}
12
13WatcherFSEvents::WatcherFSEvents( WatchID id, std::string directory, FileWatchListener* listener,
14 bool recursive, WatcherFSEvents* /*parent*/ ) :
15 Watcher( id, directory, listener, recursive ),
16 FWatcher( NULL ),
17 FSStream( NULL ),
18 WatcherGen( NULL ),
19 initializedAsync( false ) {}
20 12
21WatcherFSEvents::~WatcherFSEvents() { 13WatcherFSEvents::~WatcherFSEvents() {
22 if ( NULL != FSStream ) { 14 if ( NULL != FSStream ) {
23 if ( initializedAsync ) { 15 FSEventStreamStop( FSStream );
24 FSEventStreamStop( FSStream );
25 }
26
27 FSEventStreamInvalidate( FSStream ); 16 FSEventStreamInvalidate( FSStream );
28 FSEventStreamRelease( FSStream ); 17 FSEventStreamRelease( FSStream );
29 } 18 }
@@ -39,7 +28,9 @@ void WatcherFSEvents::init() {
39 Uint32 streamFlags = kFSEventStreamCreateFlagNone; 28 Uint32 streamFlags = kFSEventStreamCreateFlagNone;
40 29
41 if ( FileWatcherFSEvents::isGranular() ) { 30 if ( FileWatcherFSEvents::isGranular() ) {
42 streamFlags = efswFSEventStreamCreateFlagFileEvents; 31 streamFlags = efswFSEventStreamCreateFlagFileEvents | efswFSEventStreamCreateFlagNoDefer |
32 efswFSEventStreamCreateFlagUseExtendedData |
33 efswFSEventStreamCreateFlagUseCFTypes;
43 } else { 34 } else {
44 WatcherGen = new WatcherGeneric( ID, Directory, Listener, FWatcher.load(), Recursive ); 35 WatcherGen = new WatcherGeneric( ID, Directory, Listener, FWatcher.load(), Recursive );
45 } 36 }
@@ -52,49 +43,49 @@ void WatcherFSEvents::init() {
52 ctx.release = NULL; 43 ctx.release = NULL;
53 ctx.copyDescription = NULL; 44 ctx.copyDescription = NULL;
54 45
46 dispatch_queue_t queue = dispatch_queue_create( NULL, NULL );
47
55 FSStream = 48 FSStream =
56 FSEventStreamCreate( kCFAllocatorDefault, &FileWatcherFSEvents::FSEventCallback, &ctx, 49 FSEventStreamCreate( kCFAllocatorDefault, &FileWatcherFSEvents::FSEventCallback, &ctx,
57 CFDirectoryArray, kFSEventStreamEventIdSinceNow, 0.25, streamFlags ); 50 CFDirectoryArray, kFSEventStreamEventIdSinceNow, 0., streamFlags );
58 FWatcher.load()->mNeedInitMutex.lock();
59 FWatcher.load()->mNeedInit.push_back( this );
60 FWatcher.load()->mNeedInitMutex.unlock();
61 51
62 CFRelease( CFDirectoryArray ); 52 FSEventStreamSetDispatchQueue( FSStream, queue );
63 CFRelease( CFDirectory );
64}
65 53
66void WatcherFSEvents::initAsync() {
67 FSEventStreamScheduleWithRunLoop( FSStream, FWatcher.load()->mRunLoopRef.load(),
68 kCFRunLoopDefaultMode );
69 FSEventStreamStart( FSStream ); 54 FSEventStreamStart( FSStream );
70 initializedAsync = true; 55
56 CFRelease( CFDirectoryArray );
57 CFRelease( CFDirectory );
71} 58}
72 59
73void WatcherFSEvents::sendFileAction( WatchID watchid, const std::string& dir, 60void WatcherFSEvents::sendFileAction( WatchID watchid, const std::string& dir,
74 const std::string& filename, Action action, 61 const std::string& filename, Action action,
75 std::string oldFilename ) { 62 std::string oldFilename ) {
76 Listener->handleFileAction( watchid, FileSystem::precomposeFileName( dir ), 63 Listener->handleFileAction( watchid, FileSystem::precomposeFileName( dir ),
77 FileSystem::precomposeFileName( filename ), action, oldFilename ); 64 FileSystem::precomposeFileName( filename ), action,
65 FileSystem::precomposeFileName( oldFilename ) );
78} 66}
79 67
80void WatcherFSEvents::handleAddModDel( const Uint32& flags, const std::string& path, 68void WatcherFSEvents::handleAddModDel( const Uint32& flags, const std::string& path,
81 std::string& dirPath, std::string& filePath ) { 69 std::string& dirPath, std::string& filePath, Uint64 inode ) {
82 if ( flags & efswFSEventStreamEventFlagItemCreated ) { 70 if ( ( flags & efswFSEventStreamEventFlagItemCreated ) && FileInfo::exists( path ) &&
83 if ( FileInfo::exists( path ) ) { 71 ( !SanitizeEvents || FilesAdded.find( inode ) != FilesAdded.end() ) ) {
84 sendFileAction( ID, dirPath, filePath, Actions::Add ); 72 sendFileAction( ID, dirPath, filePath, Actions::Add );
85 } 73
74 if ( SanitizeEvents )
75 FilesAdded.insert( inode );
86 } 76 }
87 77
88 if ( flags & efswFSEventsModified ) { 78 if ( flags & ModifiedFlags ) {
89 sendFileAction( ID, dirPath, filePath, Actions::Modified ); 79 sendFileAction( ID, dirPath, filePath, Actions::Modified );
90 } 80 }
91 81
92 if ( flags & efswFSEventStreamEventFlagItemRemoved ) { 82 if ( ( flags & efswFSEventStreamEventFlagItemRemoved ) && !FileInfo::exists( path ) ) {
93 // Since i don't know the order, at least i try to keep the data consistent with the real 83 // Since i don't know the order, at least i try to keep the data consistent with the real
94 // state 84 // state
95 if ( !FileInfo::exists( path ) ) { 85 sendFileAction( ID, dirPath, filePath, Actions::Delete );
96 sendFileAction( ID, dirPath, filePath, Actions::Delete ); 86
97 } 87 if ( SanitizeEvents )
88 FilesAdded.erase( inode );
98 } 89 }
99} 90}
100 91
@@ -136,19 +127,20 @@ void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) {
136 // been added modified and erased, but i can't know if first was erased and then added 127 // been added modified and erased, but i can't know if first was erased and then added
137 // and modified, or added, then modified and then erased. I don't know what they were 128 // and modified, or added, then modified and then erased. I don't know what they were
138 // thinking by doing this... 129 // thinking by doing this...
139 efDEBUG( "Event in: %s - flags: %ld\n", event.Path.c_str(), event.Flags ); 130 efDEBUG( "Event in: %s - flags: 0x%x\n", event.Path.c_str(), event.Flags );
140 131
141 if ( event.Flags & efswFSEventStreamEventFlagItemRenamed ) { 132 if ( event.Flags & efswFSEventStreamEventFlagItemRenamed ) {
142 if ( ( i + 1 < esize ) && 133 if ( ( i + 1 < esize ) &&
143 ( events[i + 1].Flags & efswFSEventStreamEventFlagItemRenamed ) && 134 ( events[i + 1].Flags & efswFSEventStreamEventFlagItemRenamed ) &&
144 ( events[i + 1].Id == event.Id + 1 ) ) { 135 ( events[i + 1].inode == event.inode ) ) {
145 FSEvent& nEvent = events[i + 1]; 136 FSEvent& nEvent = events[i + 1];
146 std::string newDir( FileSystem::pathRemoveFileName( nEvent.Path ) ); 137 std::string newDir( FileSystem::pathRemoveFileName( nEvent.Path ) );
147 std::string newFilepath( FileSystem::fileNameFromPath( nEvent.Path ) ); 138 std::string newFilepath( FileSystem::fileNameFromPath( nEvent.Path ) );
148 139
149 if ( event.Path != nEvent.Path ) { 140 if ( event.Path != nEvent.Path ) {
150 if ( dirPath == newDir ) { 141 if ( dirPath == newDir ) {
151 if ( !FileInfo::exists( event.Path ) ) { 142 if ( !FileInfo::exists( event.Path ) ||
143 0 == strcasecmp( event.Path.c_str(), nEvent.Path.c_str() ) ) {
152 sendFileAction( ID, dirPath, newFilepath, Actions::Moved, 144 sendFileAction( ID, dirPath, newFilepath, Actions::Moved,
153 filePath ); 145 filePath );
154 } else { 146 } else {
@@ -159,12 +151,12 @@ void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) {
159 sendFileAction( ID, dirPath, filePath, Actions::Delete ); 151 sendFileAction( ID, dirPath, filePath, Actions::Delete );
160 sendFileAction( ID, newDir, newFilepath, Actions::Add ); 152 sendFileAction( ID, newDir, newFilepath, Actions::Add );
161 153
162 if ( nEvent.Flags & efswFSEventsModified ) { 154 if ( nEvent.Flags & ModifiedFlags ) {
163 sendFileAction( ID, newDir, newFilepath, Actions::Modified ); 155 sendFileAction( ID, newDir, newFilepath, Actions::Modified );
164 } 156 }
165 } 157 }
166 } else { 158 } else {
167 handleAddModDel( nEvent.Flags, nEvent.Path, dirPath, filePath ); 159 handleAddModDel( nEvent.Flags, nEvent.Path, dirPath, filePath, event.inode );
168 } 160 }
169 161
170 if ( nEvent.Flags & ( efswFSEventStreamEventFlagItemCreated | 162 if ( nEvent.Flags & ( efswFSEventStreamEventFlagItemCreated |
@@ -180,14 +172,14 @@ void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) {
180 } else if ( FileInfo::exists( event.Path ) ) { 172 } else if ( FileInfo::exists( event.Path ) ) {
181 sendFileAction( ID, dirPath, filePath, Actions::Add ); 173 sendFileAction( ID, dirPath, filePath, Actions::Add );
182 174
183 if ( event.Flags & efswFSEventsModified ) { 175 if ( event.Flags & ModifiedFlags ) {
184 sendFileAction( ID, dirPath, filePath, Actions::Modified ); 176 sendFileAction( ID, dirPath, filePath, Actions::Modified );
185 } 177 }
186 } else { 178 } else {
187 sendFileAction( ID, dirPath, filePath, Actions::Delete ); 179 sendFileAction( ID, dirPath, filePath, Actions::Delete );
188 } 180 }
189 } else { 181 } else {
190 handleAddModDel( event.Flags, event.Path, dirPath, filePath ); 182 handleAddModDel( event.Flags, event.Path, dirPath, filePath, event.inode );
191 } 183 }
192 } else { 184 } else {
193 efDEBUG( "Directory: %s changed\n", event.Path.c_str() ); 185 efDEBUG( "Directory: %s changed\n", event.Path.c_str() );
@@ -197,7 +189,7 @@ void WatcherFSEvents::handleActions( std::vector<FSEvent>& events ) {
197} 189}
198 190
199void WatcherFSEvents::process() { 191void WatcherFSEvents::process() {
200 std::set<std::string>::iterator it = DirsChanged.begin(); 192 std::unordered_set<std::string>::iterator it = DirsChanged.begin();
201 193
202 for ( ; it != DirsChanged.end(); it++ ) { 194 for ( ; it != DirsChanged.end(); it++ ) {
203 if ( !FileWatcherFSEvents::isGranular() ) { 195 if ( !FileWatcherFSEvents::isGranular() ) {
diff --git a/src/3rdParty/efsw/WatcherFSEvents.hpp b/src/3rdParty/efsw/WatcherFSEvents.hpp
index 4dbb231..f05b094 100755..100644
--- a/src/3rdParty/efsw/WatcherFSEvents.hpp
+++ b/src/3rdParty/efsw/WatcherFSEvents.hpp
@@ -9,51 +9,72 @@
9#include <CoreServices/CoreServices.h> 9#include <CoreServices/CoreServices.h>
10#include <efsw/FileInfo.hpp> 10#include <efsw/FileInfo.hpp>
11#include <efsw/WatcherGeneric.hpp> 11#include <efsw/WatcherGeneric.hpp>
12#include <set> 12#include <unordered_set>
13#include <vector> 13#include <vector>
14 14
15namespace efsw { 15namespace efsw {
16 16
17/* OSX < 10.7 has no file events */
18/* So i declare the events constants */
19enum FSEventEvents {
20 efswFSEventStreamCreateFlagUseCFTypes = 0x00000001,
21 efswFSEventStreamCreateFlagNoDefer = 0x00000002,
22 efswFSEventStreamCreateFlagFileEvents = 0x00000010,
23 efswFSEventStreamCreateFlagUseExtendedData = 0x00000040,
24 efswFSEventStreamEventFlagItemCreated = 0x00000100,
25 efswFSEventStreamEventFlagItemRemoved = 0x00000200,
26 efswFSEventStreamEventFlagItemInodeMetaMod = 0x00000400,
27 efswFSEventStreamEventFlagItemRenamed = 0x00000800,
28 efswFSEventStreamEventFlagItemModified = 0x00001000,
29 efswFSEventStreamEventFlagItemFinderInfoMod = 0x00002000,
30 efswFSEventStreamEventFlagItemChangeOwner = 0x00004000,
31 efswFSEventStreamEventFlagItemXattrMod = 0x00008000,
32 efswFSEventStreamEventFlagItemIsFile = 0x00010000,
33 efswFSEventStreamEventFlagItemIsDir = 0x00020000,
34 efswFSEventStreamEventFlagItemIsSymlink = 0x00040000,
35 efswFSEventsModified = efswFSEventStreamEventFlagItemFinderInfoMod |
36 efswFSEventStreamEventFlagItemModified |
37 efswFSEventStreamEventFlagItemInodeMetaMod
38};
39
17class FileWatcherFSEvents; 40class FileWatcherFSEvents;
18 41
19class FSEvent { 42class FSEvent {
20 public: 43 public:
21 FSEvent( std::string path, long flags, Uint64 id ) : Path( path ), Flags( flags ), Id( id ) {} 44 FSEvent( std::string path, long flags, Uint64 id, Uint64 inode = 0 ) :
45 Path( path ), Flags( flags ), Id( id ), inode( inode ) {}
22 46
23 std::string Path; 47 std::string Path;
24 long Flags; 48 long Flags{ 0 };
25 Uint64 Id; 49 Uint64 Id{ 0 };
50 Uint64 inode{ 0 };
26}; 51};
27 52
28class WatcherFSEvents : public Watcher { 53class WatcherFSEvents : public Watcher {
29 public: 54 public:
30 WatcherFSEvents(); 55 WatcherFSEvents();
31 56
32 WatcherFSEvents( WatchID id, std::string directory, FileWatchListener* listener, bool recursive,
33 WatcherFSEvents* parent = NULL );
34
35 ~WatcherFSEvents(); 57 ~WatcherFSEvents();
36 58
37 void init(); 59 void init();
38 60
39 void initAsync();
40
41 void handleActions( std::vector<FSEvent>& events ); 61 void handleActions( std::vector<FSEvent>& events );
42 62
43 void process(); 63 void process();
44 64
45 Atomic<FileWatcherFSEvents*> FWatcher; 65 Atomic<FileWatcherFSEvents*> FWatcher;
46 FSEventStreamRef FSStream; 66 FSEventStreamRef FSStream;
67 Uint64 ModifiedFlags{ efswFSEventsModified };
68 bool SanitizeEvents{ false };
47 69
48 protected: 70 protected:
49 void handleAddModDel( const Uint32& flags, const std::string& path, std::string& dirPath, 71 void handleAddModDel( const Uint32& flags, const std::string& path, std::string& dirPath,
50 std::string& filePath ); 72 std::string& filePath, Uint64 inode );
51 73
52 WatcherGeneric* WatcherGen; 74 WatcherGeneric* WatcherGen;
53 75
54 Atomic<bool> initializedAsync; 76 std::unordered_set<std::string> DirsChanged;
55 77 std::unordered_set<Uint64> FilesAdded;
56 std::set<std::string> DirsChanged;
57 78
58 void sendFileAction( WatchID watchid, const std::string& dir, const std::string& filename, 79 void sendFileAction( WatchID watchid, const std::string& dir, const std::string& filename,
59 Action action, std::string oldFilename = "" ); 80 Action action, std::string oldFilename = "" );
diff --git a/src/3rdParty/efsw/WatcherGeneric.cpp b/src/3rdParty/efsw/WatcherGeneric.cpp
index a6bb106..a6bb106 100755..100644
--- a/src/3rdParty/efsw/WatcherGeneric.cpp
+++ b/src/3rdParty/efsw/WatcherGeneric.cpp
diff --git a/src/3rdParty/efsw/WatcherGeneric.hpp b/src/3rdParty/efsw/WatcherGeneric.hpp
index 9cf8365..d11ec20 100755..100644
--- a/src/3rdParty/efsw/WatcherGeneric.hpp
+++ b/src/3rdParty/efsw/WatcherGeneric.hpp
@@ -17,7 +17,7 @@ class WatcherGeneric : public Watcher {
17 17
18 ~WatcherGeneric(); 18 ~WatcherGeneric();
19 19
20 void watch(); 20 void watch() override;
21 21
22 void watchDir( std::string dir ); 22 void watchDir( std::string dir );
23 23
diff --git a/src/3rdParty/efsw/WatcherInotify.cpp b/src/3rdParty/efsw/WatcherInotify.cpp
index 7259bb1..812ddae 100755..100644
--- a/src/3rdParty/efsw/WatcherInotify.cpp
+++ b/src/3rdParty/efsw/WatcherInotify.cpp
@@ -4,10 +4,6 @@ namespace efsw {
4 4
5WatcherInotify::WatcherInotify() : Watcher(), Parent( NULL ) {} 5WatcherInotify::WatcherInotify() : Watcher(), Parent( NULL ) {}
6 6
7WatcherInotify::WatcherInotify( WatchID id, std::string directory, FileWatchListener* listener,
8 bool recursive, WatcherInotify* parent ) :
9 Watcher( id, directory, listener, recursive ), Parent( parent ), DirInfo( directory ) {}
10
11bool WatcherInotify::inParentTree( WatcherInotify* parent ) { 7bool WatcherInotify::inParentTree( WatcherInotify* parent ) {
12 WatcherInotify* tNext = Parent; 8 WatcherInotify* tNext = Parent;
13 9
diff --git a/src/3rdParty/efsw/WatcherInotify.hpp b/src/3rdParty/efsw/WatcherInotify.hpp
index bf2ff5e..ec55ed0 100755..100644
--- a/src/3rdParty/efsw/WatcherInotify.hpp
+++ b/src/3rdParty/efsw/WatcherInotify.hpp
@@ -10,15 +10,13 @@ class WatcherInotify : public Watcher {
10 public: 10 public:
11 WatcherInotify(); 11 WatcherInotify();
12 12
13 WatcherInotify( WatchID id, std::string directory, FileWatchListener* listener, bool recursive,
14 WatcherInotify* parent = NULL );
15
16 bool inParentTree( WatcherInotify* parent ); 13 bool inParentTree( WatcherInotify* parent );
17 14
18 WatcherInotify* Parent; 15 WatcherInotify* Parent;
19 WatchID InotifyID; 16 WatchID InotifyID;
20 17
21 FileInfo DirInfo; 18 FileInfo DirInfo;
19 bool syntheticEvents{ false };
22}; 20};
23 21
24} // namespace efsw 22} // namespace efsw
diff --git a/src/3rdParty/efsw/WatcherKqueue.cpp b/src/3rdParty/efsw/WatcherKqueue.cpp
index 441948a..424b989 100755..100644
--- a/src/3rdParty/efsw/WatcherKqueue.cpp
+++ b/src/3rdParty/efsw/WatcherKqueue.cpp
@@ -139,7 +139,7 @@ void WatcherKqueue::addAll() {
139void WatcherKqueue::removeAll() { 139void WatcherKqueue::removeAll() {
140 efDEBUG( "removeAll(): Removing all child watchers\n" ); 140 efDEBUG( "removeAll(): Removing all child watchers\n" );
141 141
142 std::list<WatchID> erase; 142 std::vector<WatchID> erase;
143 143
144 for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); it++ ) { 144 for ( WatchMap::iterator it = mWatches.begin(); it != mWatches.end(); it++ ) {
145 efDEBUG( "removeAll(): Removed child watcher %s\n", it->second->Directory.c_str() ); 145 efDEBUG( "removeAll(): Removed child watcher %s\n", it->second->Directory.c_str() );
@@ -147,7 +147,7 @@ void WatcherKqueue::removeAll() {
147 erase.push_back( it->second->ID ); 147 erase.push_back( it->second->ID );
148 } 148 }
149 149
150 for ( std::list<WatchID>::iterator eit = erase.begin(); eit != erase.end(); eit++ ) { 150 for ( std::vector<WatchID>::iterator eit = erase.begin(); eit != erase.end(); eit++ ) {
151 removeWatch( *eit ); 151 removeWatch( *eit );
152 } 152 }
153} 153}
@@ -354,7 +354,8 @@ void WatcherKqueue::watch() {
354 bool needScan = false; 354 bool needScan = false;
355 355
356 // Then we get the the events of the current folder 356 // Then we get the the events of the current folder
357 while ( ( nev = kevent( mKqueue, &mChangeList[0], mChangeListCount + 1, &event, 1, 357 while ( !mChangeList.empty() &&
358 ( nev = kevent( mKqueue, mChangeList.data(), mChangeListCount + 1, &event, 1,
358 &mWatcher->mTimeOut ) ) != 0 ) { 359 &mWatcher->mTimeOut ) ) != 0 ) {
359 // An error ocurred? 360 // An error ocurred?
360 if ( nev == -1 ) { 361 if ( nev == -1 ) {
@@ -436,7 +437,6 @@ void WatcherKqueue::moveDirectory( std::string oldPath, std::string newPath, boo
436 437
437WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher, 438WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher,
438 bool recursive, WatcherKqueue* parent ) { 439 bool recursive, WatcherKqueue* parent ) {
439 static long s_fc = 0;
440 static bool s_ug = false; 440 static bool s_ug = false;
441 441
442 std::string dir( directory ); 442 std::string dir( directory );
@@ -478,8 +478,6 @@ WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener
478 478
479 watch->addAll(); 479 watch->addAll();
480 480
481 s_fc++;
482
483 // if failed to open the directory... erase the watcher 481 // if failed to open the directory... erase the watcher
484 if ( !watch->initOK() ) { 482 if ( !watch->initOK() ) {
485 int le = watch->lastErrno(); 483 int le = watch->lastErrno();
@@ -502,9 +500,8 @@ WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener
502 } 500 }
503 } else { 501 } else {
504 if ( !s_ug ) { 502 if ( !s_ug ) {
505 efDEBUG( "Started using WatcherGeneric, reached file descriptors limit: %ld. Folders " 503 efDEBUG( "Started using WatcherGeneric, reached file descriptors limit: %ld.\n",
506 "added: %ld\n", 504 mWatcher->mFileDescriptorCount );
507 mWatcher->mFileDescriptorCount, s_fc );
508 s_ug = true; 505 s_ug = true;
509 } 506 }
510 507
diff --git a/src/3rdParty/efsw/WatcherKqueue.hpp b/src/3rdParty/efsw/WatcherKqueue.hpp
index 87d898c..75c0f62 100755..100644
--- a/src/3rdParty/efsw/WatcherKqueue.hpp
+++ b/src/3rdParty/efsw/WatcherKqueue.hpp
@@ -49,7 +49,7 @@ class WatcherKqueue : public Watcher {
49 49
50 WatchID watchingDirectory( std::string dir ); 50 WatchID watchingDirectory( std::string dir );
51 51
52 void watch(); 52 void watch() override;
53 53
54 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive, 54 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
55 WatcherKqueue* parent ); 55 WatcherKqueue* parent );
diff --git a/src/3rdParty/efsw/WatcherWin32.cpp b/src/3rdParty/efsw/WatcherWin32.cpp
index 3e8bcc7..712419e 100755..100644
--- a/src/3rdParty/efsw/WatcherWin32.cpp
+++ b/src/3rdParty/efsw/WatcherWin32.cpp
@@ -1,34 +1,114 @@
1#include <efsw/Debug.hpp>
1#include <efsw/String.hpp> 2#include <efsw/String.hpp>
2#include <efsw/WatcherWin32.hpp> 3#include <efsw/WatcherWin32.hpp>
3 4
4#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 5#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
5 6
7#include <algorithm>
8
6namespace efsw { 9namespace efsw {
7 10
8/// Unpacks events and passes them to a user defined callback. 11struct EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX {
9void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) { 12 DWORD NextEntryOffset;
10 if ( dwNumberOfBytesTransfered == 0 || NULL == lpOverlapped ) { 13 DWORD Action;
11 return; 14 LARGE_INTEGER CreationTime;
15 LARGE_INTEGER LastModificationTime;
16 LARGE_INTEGER LastChangeTime;
17 LARGE_INTEGER LastAccessTime;
18 LARGE_INTEGER AllocatedLength;
19 LARGE_INTEGER FileSize;
20 DWORD FileAttributes;
21 DWORD ReparsePointTag;
22 LARGE_INTEGER FileId;
23 LARGE_INTEGER ParentFileId;
24 DWORD FileNameLength;
25 WCHAR FileName[1];
26};
27
28typedef EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX* EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX;
29
30typedef BOOL( WINAPI* EFSW_LPREADDIRECTORYCHANGESEXW )( HANDLE hDirectory, LPVOID lpBuffer,
31 DWORD nBufferLength, BOOL bWatchSubtree,
32 DWORD dwNotifyFilter, LPDWORD lpBytesReturned,
33 LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
34 DWORD ReadDirectoryNotifyInformationClass );
35
36static EFSW_LPREADDIRECTORYCHANGESEXW pReadDirectoryChangesExW = NULL;
37
38#define EFSW_ReadDirectoryNotifyExtendedInformation 2
39
40static void initReadDirectoryChangesEx() {
41 static bool hasInit = false;
42 if ( !hasInit ) {
43 hasInit = true;
44
45 HMODULE hModule = GetModuleHandleW( L"Kernel32.dll" );
46 if ( !hModule )
47 return;
48
49 pReadDirectoryChangesExW =
50 (EFSW_LPREADDIRECTORYCHANGESEXW)GetProcAddress( hModule, "ReadDirectoryChangesExW" );
12 } 51 }
52}
13 53
14 char szFile[MAX_PATH]; 54void WatchCallbackOld( WatcherWin32* pWatch ) {
15 PFILE_NOTIFY_INFORMATION pNotify; 55 PFILE_NOTIFY_INFORMATION pNotify;
16 WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped;
17 WatcherWin32* pWatch = tWatch->Watch;
18 size_t offset = 0; 56 size_t offset = 0;
19
20 do { 57 do {
21 bool skip = false; 58 bool skip = false;
22 59
23 pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->Buffer[offset]; 60 pNotify = (PFILE_NOTIFY_INFORMATION)&pWatch->Buffer[offset];
24 offset += pNotify->NextEntryOffset; 61 offset += pNotify->NextEntryOffset;
62 int count =
63 WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName,
64 pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL );
65 if ( count == 0 )
66 continue;
67
68 std::string nfile( count, '\0' );
69
70 count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName,
71 pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count,
72 NULL, NULL );
73
74 if ( FILE_ACTION_MODIFIED == pNotify->Action ) {
75 FileInfo fifile( std::string( pWatch->DirName ) + nfile );
76
77 if ( pWatch->LastModifiedEvent.file.ModificationTime == fifile.ModificationTime &&
78 pWatch->LastModifiedEvent.file.Size == fifile.Size &&
79 pWatch->LastModifiedEvent.fileName == nfile ) {
80 skip = true;
81 }
82
83 pWatch->LastModifiedEvent.fileName = nfile;
84 pWatch->LastModifiedEvent.file = fifile;
85 }
86
87 if ( !skip ) {
88 pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action );
89 }
90 } while ( pNotify->NextEntryOffset != 0 );
91}
92
93void WatchCallbackEx( WatcherWin32* pWatch ) {
94 EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX pNotify;
95 size_t offset = 0;
96 do {
97 bool skip = false;
98
99 pNotify = (EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX)&pWatch->Buffer[offset];
100 offset += pNotify->NextEntryOffset;
101 int count =
102 WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName,
103 pNotify->FileNameLength / sizeof( WCHAR ), NULL, 0, NULL, NULL );
104 if ( count == 0 )
105 continue;
25 106
26 int count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName, 107 std::string nfile( count, '\0' );
27 pNotify->FileNameLength / sizeof( WCHAR ), szFile,
28 MAX_PATH - 1, NULL, NULL );
29 szFile[count] = TEXT( '\0' );
30 108
31 std::string nfile( szFile ); 109 count = WideCharToMultiByte( CP_UTF8, 0, pNotify->FileName,
110 pNotify->FileNameLength / sizeof( WCHAR ), &nfile[0], count,
111 NULL, NULL );
32 112
33 if ( FILE_ACTION_MODIFIED == pNotify->Action ) { 113 if ( FILE_ACTION_MODIFIED == pNotify->Action ) {
34 FileInfo fifile( std::string( pWatch->DirName ) + nfile ); 114 FileInfo fifile( std::string( pWatch->DirName ) + nfile );
@@ -41,12 +121,59 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve
41 121
42 pWatch->LastModifiedEvent.fileName = nfile; 122 pWatch->LastModifiedEvent.fileName = nfile;
43 pWatch->LastModifiedEvent.file = fifile; 123 pWatch->LastModifiedEvent.file = fifile;
124 } else if ( FILE_ACTION_RENAMED_OLD_NAME == pNotify->Action ) {
125 pWatch->OldFiles.emplace_back( nfile, pNotify->FileId );
126 skip = true;
127 } else if ( FILE_ACTION_RENAMED_NEW_NAME == pNotify->Action ) {
128 std::string oldFile;
129 LARGE_INTEGER oldFileId{};
130
131 for ( auto it = pWatch->OldFiles.begin(); it != pWatch->OldFiles.end(); ++it ) {
132 if ( it->second.QuadPart == pNotify->FileId.QuadPart ) {
133 oldFile = it->first;
134 oldFileId = it->second;
135 it = pWatch->OldFiles.erase( it );
136 break;
137 }
138 }
139
140 if ( oldFile.empty() ) {
141 pWatch->Watch->handleAction( pWatch, nfile, FILE_ACTION_ADDED );
142 skip = true;
143 } else {
144 pWatch->Watch->handleAction( pWatch, oldFile, FILE_ACTION_RENAMED_OLD_NAME );
145 }
44 } 146 }
45 147
46 if ( !skip ) { 148 if ( !skip ) {
47 pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action ); 149 pWatch->Watch->handleAction( pWatch, nfile, pNotify->Action );
48 } 150 }
49 } while ( pNotify->NextEntryOffset != 0 ); 151 } while ( pNotify->NextEntryOffset != 0 );
152}
153
154/// Unpacks events and passes them to a user defined callback.
155void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ) {
156 if ( NULL == lpOverlapped ) {
157 return;
158 }
159
160 WatcherStructWin32* tWatch = (WatcherStructWin32*)lpOverlapped;
161 WatcherWin32* pWatch = tWatch->Watch;
162
163 if ( dwNumberOfBytesTransfered == 0 ) {
164 if ( nullptr != pWatch && !pWatch->StopNow ) {
165 RefreshWatch( tWatch );
166 } else {
167 return;
168 }
169 }
170
171 // Fork watch depending on the Windows API supported
172 if ( pWatch->Extended ) {
173 WatchCallbackEx( pWatch );
174 } else {
175 WatchCallbackOld( pWatch );
176 }
50 177
51 if ( !pWatch->StopNow ) { 178 if ( !pWatch->StopNow ) {
52 RefreshWatch( tWatch ); 179 RefreshWatch( tWatch );
@@ -54,11 +181,40 @@ void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOve
54} 181}
55 182
56/// Refreshes the directory monitoring. 183/// Refreshes the directory monitoring.
57bool RefreshWatch( WatcherStructWin32* pWatch ) { 184RefreshResult RefreshWatch( WatcherStructWin32* pWatch ) {
58 return ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer, 185 initReadDirectoryChangesEx();
59 sizeof( pWatch->Watch->Buffer ), pWatch->Watch->Recursive, 186
60 pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped, 187 bool bRet = false;
61 NULL ) != 0; 188 RefreshResult ret = RefreshResult::Failed;
189 pWatch->Watch->Extended = false;
190
191 if ( pReadDirectoryChangesExW ) {
192 bRet = pReadDirectoryChangesExW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(),
193 (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive,
194 pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped,
195 NULL, EFSW_ReadDirectoryNotifyExtendedInformation ) != 0;
196 if ( bRet ) {
197 ret = RefreshResult::SucessEx;
198 pWatch->Watch->Extended = true;
199 }
200 }
201
202 if ( !bRet ) {
203 bRet = ReadDirectoryChangesW( pWatch->Watch->DirHandle, pWatch->Watch->Buffer.data(),
204 (DWORD)pWatch->Watch->Buffer.size(), pWatch->Watch->Recursive,
205 pWatch->Watch->NotifyFilter, NULL, &pWatch->Overlapped,
206 NULL ) != 0;
207
208 if ( bRet )
209 ret = RefreshResult::Success;
210 }
211
212 if ( !bRet ) {
213 std::string error = std::to_string( GetLastError() );
214 Errors::Log::createLastError( Errors::WatcherFailed, error );
215 }
216
217 return ret;
62} 218}
63 219
64/// Stops monitoring a directory. 220/// Stops monitoring a directory.
@@ -70,19 +226,17 @@ void DestroyWatch( WatcherStructWin32* pWatch ) {
70 CloseHandle( pWatch->Watch->DirHandle ); 226 CloseHandle( pWatch->Watch->DirHandle );
71 efSAFE_DELETE_ARRAY( pWatch->Watch->DirName ); 227 efSAFE_DELETE_ARRAY( pWatch->Watch->DirName );
72 efSAFE_DELETE( pWatch->Watch ); 228 efSAFE_DELETE( pWatch->Watch );
229 efSAFE_DELETE( pWatch );
73 } 230 }
74} 231}
75 232
76/// Starts monitoring a directory. 233/// Starts monitoring a directory.
77WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, 234WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive,
78 HANDLE iocp ) { 235 DWORD bufferSize, DWORD notifyFilter, HANDLE iocp ) {
79 WatcherStructWin32* tWatch; 236 WatcherStructWin32* tWatch = new WatcherStructWin32();
80 size_t ptrsize = sizeof( *tWatch ); 237 WatcherWin32* pWatch = new WatcherWin32(bufferSize);
81 tWatch = static_cast<WatcherStructWin32*>( 238 if (tWatch)
82 HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize ) ); 239 tWatch->Watch = pWatch;
83
84 WatcherWin32* pWatch = new WatcherWin32();
85 tWatch->Watch = pWatch;
86 240
87 pWatch->DirHandle = CreateFileW( 241 pWatch->DirHandle = CreateFileW(
88 szDirectory, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, 242 szDirectory, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
@@ -90,17 +244,17 @@ WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD Noti
90 244
91 if ( pWatch->DirHandle != INVALID_HANDLE_VALUE && 245 if ( pWatch->DirHandle != INVALID_HANDLE_VALUE &&
92 CreateIoCompletionPort( pWatch->DirHandle, iocp, 0, 1 ) ) { 246 CreateIoCompletionPort( pWatch->DirHandle, iocp, 0, 1 ) ) {
93 pWatch->NotifyFilter = NotifyFilter; 247 pWatch->NotifyFilter = notifyFilter;
94 pWatch->Recursive = recursive; 248 pWatch->Recursive = recursive;
95 249
96 if ( RefreshWatch( tWatch ) ) { 250 if ( RefreshResult::Failed != RefreshWatch( tWatch ) ) {
97 return tWatch; 251 return tWatch;
98 } 252 }
99 } 253 }
100 254
101 CloseHandle( pWatch->DirHandle ); 255 CloseHandle( pWatch->DirHandle );
102 efSAFE_DELETE( pWatch->Watch ); 256 efSAFE_DELETE( pWatch->Watch );
103 HeapFree( GetProcessHeap(), 0, tWatch ); 257 efSAFE_DELETE( tWatch );
104 return NULL; 258 return NULL;
105} 259}
106 260
diff --git a/src/3rdParty/efsw/WatcherWin32.hpp b/src/3rdParty/efsw/WatcherWin32.hpp
index 71e13be..ea1e8e4 100755..100644
--- a/src/3rdParty/efsw/WatcherWin32.hpp
+++ b/src/3rdParty/efsw/WatcherWin32.hpp
@@ -3,6 +3,7 @@
3 3
4#include <efsw/FileInfo.hpp> 4#include <efsw/FileInfo.hpp>
5#include <efsw/FileWatcherImpl.hpp> 5#include <efsw/FileWatcherImpl.hpp>
6#include <vector>
6 7
7#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32 8#if EFSW_PLATFORM == EFSW_PLATFORM_WIN32
8 9
@@ -21,6 +22,8 @@ namespace efsw {
21 22
22class WatcherWin32; 23class WatcherWin32;
23 24
25enum RefreshResult { Failed, Success, SucessEx };
26
24/// Internal watch data 27/// Internal watch data
25struct WatcherStructWin32 { 28struct WatcherStructWin32 {
26 OVERLAPPED Overlapped; 29 OVERLAPPED Overlapped;
@@ -32,39 +35,41 @@ struct sLastModifiedEvent {
32 std::string fileName; 35 std::string fileName;
33}; 36};
34 37
35bool RefreshWatch( WatcherStructWin32* pWatch ); 38RefreshResult RefreshWatch( WatcherStructWin32* pWatch );
36 39
37void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ); 40void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped );
38 41
39void DestroyWatch( WatcherStructWin32* pWatch ); 42void DestroyWatch( WatcherStructWin32* pWatch );
40 43
41WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, 44WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive,
42 HANDLE iocp ); 45 DWORD bufferSize, DWORD notifyFilter, HANDLE iocp );
43 46
44class WatcherWin32 : public Watcher { 47class WatcherWin32 : public Watcher {
45 public: 48 public:
46 WatcherWin32() : 49 WatcherWin32(DWORD dwBufferSize) :
47 Struct( NULL ), 50 Struct( NULL ),
48 DirHandle( NULL ), 51 DirHandle( NULL ),
52 Buffer(),
49 lParam( 0 ), 53 lParam( 0 ),
50 NotifyFilter( 0 ), 54 NotifyFilter( 0 ),
51 StopNow( false ), 55 StopNow( false ),
56 Extended( false ),
52 Watch( NULL ), 57 Watch( NULL ),
53 DirName( NULL ) {} 58 DirName( NULL ) {
59 Buffer.resize(dwBufferSize);
60 }
54 61
55 WatcherStructWin32* Struct; 62 WatcherStructWin32* Struct;
56 HANDLE DirHandle; 63 HANDLE DirHandle;
57 BYTE Buffer 64 std::vector<BYTE> Buffer;
58 [63 *
59 1024]; // do NOT make this bigger than 64K because it will fail if the folder being watched
60 // is on the network! (see
61 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx)
62 LPARAM lParam; 65 LPARAM lParam;
63 DWORD NotifyFilter; 66 DWORD NotifyFilter;
64 bool StopNow; 67 bool StopNow;
68 bool Extended;
65 FileWatcherImpl* Watch; 69 FileWatcherImpl* Watch;
66 char* DirName; 70 char* DirName;
67 sLastModifiedEvent LastModifiedEvent; 71 sLastModifiedEvent LastModifiedEvent;
72 std::vector<std::pair<std::string, LARGE_INTEGER>> OldFiles;
68}; 73};
69 74
70} // namespace efsw 75} // namespace efsw
diff --git a/src/3rdParty/efsw/base.hpp b/src/3rdParty/efsw/base.hpp
index 43abc4f..43abc4f 100755..100644
--- a/src/3rdParty/efsw/base.hpp
+++ b/src/3rdParty/efsw/base.hpp
diff --git a/src/3rdParty/efsw/efsw.h b/src/3rdParty/efsw/efsw.h
index 28e63e2..30cf595 100755..100644
--- a/src/3rdParty/efsw/efsw.h
+++ b/src/3rdParty/efsw/efsw.h
@@ -1,7 +1,7 @@
1/** 1/**
2 @author Sepul Sepehr Taghdisian 2 @author Sepul Sepehr Taghdisian
3 3
4 Copyright (c) 2013 Martin Lucas Golini 4 Copyright (c) 2024 Martín Lucas Golini
5 5
6 Permission is hereby granted, free of charge, to any person obtaining a copy 6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal 7 of this software and associated documentation files (the "Software"), to deal
@@ -32,31 +32,31 @@
32extern "C" { 32extern "C" {
33#endif 33#endif
34 34
35#if defined(_WIN32) 35#if defined( _WIN32 )
36 #ifdef EFSW_DYNAMIC 36#ifdef EFSW_DYNAMIC
37 // Windows platforms 37// Windows platforms
38 #ifdef EFSW_EXPORTS 38#ifdef EFSW_EXPORTS
39 // From DLL side, we must export 39// From DLL side, we must export
40 #define EFSW_API __declspec(dllexport) 40#define EFSW_API __declspec( dllexport )
41 #else
42 // From client application side, we must import
43 #define EFSW_API __declspec(dllimport)
44 #endif
45 #else
46 // No specific directive needed for static build
47 #ifndef EFSW_API
48 #define EFSW_API
49 #endif
50 #endif
51#else 41#else
52 #if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS ) 42// From client application side, we must import
53 #define EFSW_API __attribute__ ((visibility("default"))) 43#define EFSW_API __declspec( dllimport )
54 #endif 44#endif
55 45#else
56 // Other platforms don't need to define anything 46// No specific directive needed for static build
57 #ifndef EFSW_API 47#ifndef EFSW_API
58 #define EFSW_API 48#define EFSW_API
59 #endif 49#endif
50#endif
51#else
52#if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS )
53#define EFSW_API __attribute__( ( visibility( "default" ) ) )
54#endif
55
56// Other platforms don't need to define anything
57#ifndef EFSW_API
58#define EFSW_API
59#endif
60#endif 60#endif
61 61
62/// Type for a watch id 62/// Type for a watch id
@@ -65,84 +65,127 @@ typedef long efsw_watchid;
65/// Type for watcher 65/// Type for watcher
66typedef void* efsw_watcher; 66typedef void* efsw_watcher;
67 67
68enum efsw_action 68enum efsw_action {
69{ 69 EFSW_ADD = 1, /// Sent when a file is created or renamed
70 EFSW_ADD = 1, /// Sent when a file is created or renamed 70 EFSW_DELETE = 2, /// Sent when a file is deleted or renamed
71 EFSW_DELETE = 2, /// Sent when a file is deleted or renamed 71 EFSW_MODIFIED = 3, /// Sent when a file is modified
72 EFSW_MODIFIED = 3, /// Sent when a file is modified 72 EFSW_MOVED = 4 /// Sent when a file is moved
73 EFSW_MOVED = 4 /// Sent when a file is moved 73};
74
75enum efsw_error {
76 EFSW_NOTFOUND = -1,
77 EFSW_REPEATED = -2,
78 EFSW_OUTOFSCOPE = -3,
79 EFSW_NOTREADABLE = -4,
80 EFSW_REMOTE = -5,
81 EFSW_WATCHER_FAILED = -6,
82 EFSW_UNSPECIFIED = -7
74}; 83};
75 84
76enum efsw_error 85enum efsw_option {
77{ 86 /// For Windows, the default buffer size of 63*1024 bytes sometimes is not enough and
78 EFSW_NOTFOUND = -1, 87 /// file system events may be dropped. For that, using a different (bigger) buffer size
79 EFSW_REPEATED = -2, 88 /// can be defined here, but note that this does not work for network drives,
80 EFSW_OUTOFSCOPE = -3, 89 /// because a buffer larger than 64K will fail the folder being watched, see
81 EFSW_NOTREADABLE = -4, 90 /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx)
82 EFSW_REMOTE = -5, 91 EFSW_OPT_WIN_BUFFER_SIZE = 1,
83 EFSW_UNSPECIFIED = -6 92 /// For Windows, per default all events are captured but we might only be interested
93 /// in a subset; the value of the option should be set to a bitwise or'ed set of
94 /// FILE_NOTIFY_CHANGE_* flags.
95 EFSW_OPT_WIN_NOTIFY_FILTER = 2,
96 /// For macOS (FSEvents backend), per default all modified event types are capture but we might
97 // only be interested in a subset; the value of the option should be set to a set of bitwise
98 // from:
99 // kFSEventStreamEventFlagItemFinderInfoMod
100 // kFSEventStreamEventFlagItemModified
101 // kFSEventStreamEventFlagItemInodeMetaMod
102 // Default configuration will set the 3 flags
103 EFSW_OPT_MAC_MODIFIED_FILTER = 3,
104 /// macOS sometimes informs incorrect or old file states that may confuse the consumer
105 /// The events sanitizer will try to sanitize incorrectly reported events in favor of reducing
106 /// the number of events reported. This will have an small performance and memory impact as a
107 /// consequence.
108 EFSW_OPT_MAC_SANITIZE_EVENTS = 4,
109 /// Linux does not support natively recursive watchers. This means that when using recursive
110 /// watches efsw registers new watchers for each directory. If new file are created between
111 /// the time efsw takes to register the new directory those events might be missed. To avoid
112 /// missing new file notifications efsw will trigger synthetic new file events for existing
113 /// files in the new directroy watched. This might have the unintended consequence of sending
114 /// duplicated created events due to the system also emitting this event.
115 LINUX_PRODUCE_SYNTHETIC_EVENTS = 5,
84}; 116};
85 117
86/// Basic interface for listening for file events. 118/// Basic interface for listening for file events.
87typedef void (*efsw_pfn_fileaction_callback) ( 119typedef void ( *efsw_pfn_fileaction_callback )( efsw_watcher watcher, efsw_watchid watchid,
88 efsw_watcher watcher, 120 const char* dir, const char* filename,
89 efsw_watchid watchid, 121 enum efsw_action action, const char* old_filename,
90 const char* dir, 122 void* param );
91 const char* filename, 123
92 enum efsw_action action, 124typedef struct {
93 const char* old_filename, 125 enum efsw_option option;
94 void* param 126 int value;
95); 127} efsw_watcher_option;
96 128
97/** 129/**
98 * Creates a new file-watcher 130 * Creates a new file-watcher
99 * @param generic_mode Force the use of the Generic file watcher 131 * @param generic_mode Force the use of the Generic file watcher
100 */ 132 */
101efsw_watcher EFSW_API efsw_create(int generic_mode); 133efsw_watcher EFSW_API efsw_create( int generic_mode );
102 134
103/// Release the file-watcher and unwatch any directories 135/// Release the file-watcher and unwatch any directories
104void EFSW_API efsw_release(efsw_watcher watcher); 136void EFSW_API efsw_release( efsw_watcher watcher );
105 137
106/// Retreive last error occured by file-watcher 138/// Retrieve last error occured by file-watcher
107EFSW_API const char* efsw_getlasterror(); 139EFSW_API const char* efsw_getlasterror();
108 140
109/// Add a directory watch. Same as the other addWatch, but doesn't have recursive option. 141/// Reset file-watcher last error
110/// For backwards compatibility. 142EFSW_API void efsw_clearlasterror();
143
144/// Add a directory watch
111/// On error returns WatchID with Error type. 145/// On error returns WatchID with Error type.
112efsw_watchid EFSW_API efsw_addwatch(efsw_watcher watcher, const char* directory, 146efsw_watchid EFSW_API efsw_addwatch( efsw_watcher watcher, const char* directory,
113 efsw_pfn_fileaction_callback callback_fn, int recursive, void* param); 147 efsw_pfn_fileaction_callback callback_fn, int recursive,
148 void* param );
149
150/// Add a directory watch, specifying options
151/// @param options Pointer to an array of watcher options
152/// @param nr_options Number of options referenced by \p options
153efsw_watchid EFSW_API efsw_addwatch_withoptions( efsw_watcher watcher, const char* directory,
154 efsw_pfn_fileaction_callback callback_fn,
155 int recursive, efsw_watcher_option* options,
156 int options_number, void* param );
114 157
115/// Remove a directory watch. This is a brute force search O(nlogn). 158/// Remove a directory watch. This is a brute force search O(nlogn).
116void EFSW_API efsw_removewatch(efsw_watcher watcher, const char* directory); 159void EFSW_API efsw_removewatch( efsw_watcher watcher, const char* directory );
117 160
118/// Remove a directory watch. This is a map lookup O(logn). 161/// Remove a directory watch. This is a map lookup O(logn).
119void EFSW_API efsw_removewatch_byid(efsw_watcher watcher, efsw_watchid watchid); 162void EFSW_API efsw_removewatch_byid( efsw_watcher watcher, efsw_watchid watchid );
120 163
121/// Starts watching ( in other thread ) 164/// Starts watching ( in other thread )
122void EFSW_API efsw_watch(efsw_watcher watcher); 165void EFSW_API efsw_watch( efsw_watcher watcher );
123 166
124/** 167/**
125 * Allow recursive watchers to follow symbolic links to other directories 168 * Allow recursive watchers to follow symbolic links to other directories
126 * followSymlinks is disabled by default 169 * followSymlinks is disabled by default
127 */ 170 */
128void EFSW_API efsw_follow_symlinks(efsw_watcher watcher, int enable); 171void EFSW_API efsw_follow_symlinks( efsw_watcher watcher, int enable );
129 172
130/** @return If can follow symbolic links to directorioes */ 173/** @return If can follow symbolic links to directorioes */
131int EFSW_API efsw_follow_symlinks_isenabled(efsw_watcher watcher); 174int EFSW_API efsw_follow_symlinks_isenabled( efsw_watcher watcher );
132 175
133/** 176/**
134 * When enable this it will allow symlinks to watch recursively out of the pointed directory. 177 * When enable this it will allow symlinks to watch recursively out of the pointed directory.
135 * follorSymlinks must be enabled to this work. 178 * follorSymlinks must be enabled to this work.
136 * For example, added symlink to /home/folder, and the symlink points to /, this by default is not allowed, 179 * For example, added symlink to /home/folder, and the symlink points to /, this by default is not
137 * it's only allowed to symlink anything from /home/ and deeper. This is to avoid great levels of recursion. 180 * allowed, it's only allowed to symlink anything from /home/ and deeper. This is to avoid great
138 * Enabling this could lead in infinite recursion, and crash the watcher ( it will try not to avoid this ). 181 * levels of recursion. Enabling this could lead in infinite recursion, and crash the watcher ( it
139 * Buy enabling out of scope links, it will allow this behavior. 182 * will try not to avoid this ). Buy enabling out of scope links, it will allow this behavior.
140 * allowOutOfScopeLinks are disabled by default. 183 * allowOutOfScopeLinks are disabled by default.
141 */ 184 */
142void EFSW_API efsw_allow_outofscopelinks(efsw_watcher watcher, int allow); 185void EFSW_API efsw_allow_outofscopelinks( efsw_watcher watcher, int allow );
143 186
144/// @return Returns if out of scope links are allowed 187/// @return Returns if out of scope links are allowed
145int EFSW_API efsw_outofscopelinks_isallowed(efsw_watcher watcher); 188int EFSW_API efsw_outofscopelinks_isallowed( efsw_watcher watcher );
146 189
147#ifdef __cplusplus 190#ifdef __cplusplus
148} 191}
diff --git a/src/3rdParty/efsw/efsw.hpp b/src/3rdParty/efsw/efsw.hpp
index 12af116..11a5dec 100755..100644
--- a/src/3rdParty/efsw/efsw.hpp
+++ b/src/3rdParty/efsw/efsw.hpp
@@ -1,195 +1,261 @@
1/** 1/**
2 @author Martín Lucas Golini 2 @author Martín Lucas Golini
3 3
4 Copyright (c) 2013 Martín Lucas Golini 4 Copyright (c) 2024 Martín Lucas Golini
5 5
6 Permission is hereby granted, free of charge, to any person obtaining a copy 6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal 7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights 8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is 10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions: 11 furnished to do so, subject to the following conditions:
12 12
13 The above copyright notice and this permission notice shall be included in 13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software. 14 all copies or substantial portions of the Software.
15 15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE. 22 THE SOFTWARE.
23 23
24 This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com) 24 This software is a fork of the "simplefilewatcher" by James Wynn (james@jameswynn.com)
25 http://code.google.com/p/simplefilewatcher/ also MIT licensed. 25 http://code.google.com/p/simplefilewatcher/ also MIT licensed.
26*/ 26*/
27 27
28#ifndef ESFW_HPP 28#ifndef ESFW_HPP
29#define ESFW_HPP 29#define ESFW_HPP
30 30
31#include <list> 31#include <string>
32#include <string> 32#include <vector>
33 33
34#if defined( _WIN32 ) 34#if defined( _WIN32 )
35#ifdef EFSW_DYNAMIC 35#ifdef EFSW_DYNAMIC
36// Windows platforms 36// Windows platforms
37#ifdef EFSW_EXPORTS 37#ifdef EFSW_EXPORTS
38// From DLL side, we must export 38// From DLL side, we must export
39#define EFSW_API __declspec( dllexport ) 39#define EFSW_API __declspec( dllexport )
40#else 40#else
41// From client application side, we must import 41// From client application side, we must import
42#define EFSW_API __declspec( dllimport ) 42#define EFSW_API __declspec( dllimport )
43#endif 43#endif
44#else 44#else
45// No specific directive needed for static build 45// No specific directive needed for static build
46#ifndef EFSW_API 46#ifndef EFSW_API
47#define EFSW_API 47#define EFSW_API
48#endif 48#endif
49#endif 49#endif
50#else 50#else
51#if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS ) 51#if ( __GNUC__ >= 4 ) && defined( EFSW_EXPORTS )
52#ifndef EFSW_API 52#ifndef EFSW_API
53#define EFSW_API __attribute__( ( visibility( "default" ) ) ) 53#define EFSW_API __attribute__( ( visibility( "default" ) ) )
54#endif 54#endif
55#endif 55#endif
56 56
57// Other platforms don't need to define anything 57// Other platforms don't need to define anything
58#ifndef EFSW_API 58#ifndef EFSW_API
59#define EFSW_API 59#define EFSW_API
60#endif 60#endif
61#endif 61#endif
62 62
63namespace efsw { 63namespace efsw {
64 64
65/// Type for a watch id 65/// Type for a watch id
66typedef long WatchID; 66typedef long WatchID;
67 67
68// forward declarations 68// forward declarations
69class FileWatcherImpl; 69class FileWatcherImpl;
70class FileWatchListener; 70class FileWatchListener;
71 71class WatcherOption;
72/// Actions to listen for. Rename will send two events, one for 72
73/// the deletion of the old file, and one for the creation of the 73/// Actions to listen for. Rename will send two events, one for
74/// new file. 74/// the deletion of the old file, and one for the creation of the
75namespace Actions { 75/// new file.
76enum Action { 76namespace Actions {
77 /// Sent when a file is created or renamed 77enum Action {
78 Add = 1, 78 /// Sent when a file is created or renamed
79 /// Sent when a file is deleted or renamed 79 Add = 1,
80 Delete = 2, 80 /// Sent when a file is deleted or renamed
81 /// Sent when a file is modified 81 Delete = 2,
82 Modified = 3, 82 /// Sent when a file is modified
83 /// Sent when a file is moved 83 Modified = 3,
84 Moved = 4 84 /// Sent when a file is moved
85}; 85 Moved = 4
86} 86};
87typedef Actions::Action Action; 87}
88 88typedef Actions::Action Action;
89/// Errors log namespace 89
90namespace Errors { 90/// Errors log namespace
91 91namespace Errors {
92enum Error { 92
93 FileNotFound = -1, 93enum Error {
94 FileRepeated = -2, 94 NoError = 0,
95 FileOutOfScope = -3, 95 FileNotFound = -1,
96 FileNotReadable = -4, 96 FileRepeated = -2,
97 FileRemote = -5, /** Directory in remote file system ( create a generic FileWatcher instance to 97 FileOutOfScope = -3,
98 watch this directory ). */ 98 FileNotReadable = -4,
99 Unspecified = -6 99 /// Directory in remote file system
100}; 100 /// ( create a generic FileWatcher instance to watch this directory ).
101 101 FileRemote = -5,
102class EFSW_API Log { 102 /// File system watcher failed to watch for changes.
103 public: 103 WatcherFailed = -6,
104 /// @return The last error logged 104 Unspecified = -7
105 static std::string getLastErrorLog(); 105};
106 106
107 /// Creates an error of the type specified 107class EFSW_API Log {
108 static Error createLastError( Error err, std::string log ); 108 public:
109}; 109 /// @return The last error logged
110 110 static std::string getLastErrorLog();
111} // namespace Errors 111
112typedef Errors::Error Error; 112 /// @return The code of the last error logged
113 113 static Error getLastErrorCode();
114/// Listens to files and directories and dispatches events 114
115/// to notify the listener of files and directories changes. 115 /// Reset last error
116/// @class FileWatcher 116 static void clearLastError();
117class EFSW_API FileWatcher { 117
118 public: 118 /// Creates an error of the type specified
119 /// Default constructor, will use the default platform file watcher 119 static Error createLastError( Error err, std::string log );
120 FileWatcher(); 120};
121 121
122 /// Constructor that lets you force the use of the Generic File Watcher 122} // namespace Errors
123 explicit FileWatcher( bool useGenericFileWatcher ); 123typedef Errors::Error Error;
124 124
125 virtual ~FileWatcher(); 125/// Optional file watcher settings.
126 126namespace Options {
127 /// Add a directory watch. Same as the other addWatch, but doesn't have recursive option. 127enum Option {
128 /// For backwards compatibility. 128 /// For Windows, the default buffer size of 63*1024 bytes sometimes is not enough and
129 /// On error returns WatchID with Error type. 129 /// file system events may be dropped. For that, using a different (bigger) buffer size
130 WatchID addWatch( const std::string& directory, FileWatchListener* watcher ); 130 /// can be defined here, but note that this does not work for network drives,
131 131 /// because a buffer larger than 64K will fail the folder being watched, see
132 /// Add a directory watch 132 /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx)
133 /// On error returns WatchID with Error type. 133 WinBufferSize = 1,
134 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive ); 134 /// For Windows, per default all events are captured but we might only be interested
135 135 /// in a subset; the value of the option should be set to a bitwise or'ed set of
136 /// Remove a directory watch. This is a brute force search O(nlogn). 136 /// FILE_NOTIFY_CHANGE_* flags.
137 void removeWatch( const std::string& directory ); 137 WinNotifyFilter = 2,
138 138 /// For macOS (FSEvents backend), per default all modified event types are capture but we might
139 /// Remove a directory watch. This is a map lookup O(logn). 139 /// only be interested in a subset; the value of the option should be set to a set of bitwise
140 void removeWatch( WatchID watchid ); 140 /// from:
141 141 /// kFSEventStreamEventFlagItemFinderInfoMod
142 /// Starts watching ( in other thread ) 142 /// kFSEventStreamEventFlagItemModified
143 void watch(); 143 /// kFSEventStreamEventFlagItemInodeMetaMod
144 144 /// Default configuration will set the 3 flags
145 /// @return Returns a list of the directories that are being watched 145 MacModifiedFilter = 3,
146 std::list<std::string> directories(); 146 /// macOS sometimes informs incorrect or old file states that may confuse the consumer
147 147 /// The events sanitizer will try to sanitize incorrectly reported events in favor of reducing
148 /** Allow recursive watchers to follow symbolic links to other directories 148 /// the number of events reported. This will have an small performance and memory impact as a
149 * followSymlinks is disabled by default 149 /// consequence.
150 */ 150 MacSanitizeEvents = 4,
151 void followSymlinks( bool follow ); 151 /// Linux does not support natively recursive watchers. This means that when using recursive
152 152 /// watches efsw registers new watchers for each directory. If new file are created between
153 /** @return If can follow symbolic links to directorioes */ 153 /// the time efsw takes to register the new directory those events might be missed. To avoid
154 const bool& followSymlinks() const; 154 /// missing new file notifications efsw will trigger synthetic created file events for existing
155 155 /// files in the new directroy watched. This might have the unintended consequence of sending
156 /** When enable this it will allow symlinks to watch recursively out of the pointed directory. 156 /// duplicated created events due to the system also emitting this event.
157 * follorSymlinks must be enabled to this work. 157 LinuxProduceSyntheticEvents = 5,
158 * For example, added symlink to /home/folder, and the symlink points to /, this by default is 158};
159 * not allowed, it's only allowed to symlink anything from /home/ and deeper. This is to avoid 159}
160 * great levels of recursion. Enabling this could lead in infinite recursion, and crash the 160typedef Options::Option Option;
161 * watcher ( it will try not to avoid this ). Buy enabling out of scope links, it will allow 161
162 * this behavior. allowOutOfScopeLinks are disabled by default. 162/// Listens to files and directories and dispatches events
163 */ 163/// to notify the listener of files and directories changes.
164 void allowOutOfScopeLinks( bool allow ); 164/// @class FileWatcher
165 165class EFSW_API FileWatcher {
166 /// @return Returns if out of scope links are allowed 166 public:
167 const bool& allowOutOfScopeLinks() const; 167 /// Default constructor, will use the default platform file watcher
168 168 FileWatcher();
169 private: 169
170 /// The implementation 170 /// Constructor that lets you force the use of the Generic File Watcher
171 FileWatcherImpl* mImpl; 171 explicit FileWatcher( bool useGenericFileWatcher );
172 bool mFollowSymlinks; 172
173 bool mOutOfScopeLinks; 173 virtual ~FileWatcher();
174}; 174
175 175 /// Add a directory watch. Same as the other addWatch, but doesn't have recursive option.
176/// Basic interface for listening for file events. 176 /// For backwards compatibility.
177/// @class FileWatchListener 177 /// On error returns WatchID with Error type.
178class FileWatchListener { 178 WatchID addWatch( const std::string& directory, FileWatchListener* watcher );
179 public: 179
180 virtual ~FileWatchListener() {} 180 /// Add a directory watch
181 181 /// On error returns WatchID with Error type.
182 /// Handles the action file action 182 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive );
183 /// @param watchid The watch id for the directory 183
184 /// @param dir The directory 184 /// Add a directory watch, allowing customization with options
185 /// @param filename The filename that was accessed (not full path) 185 /// @param directory The folder to be watched
186 /// @param action Action that was performed 186 /// @param watcher The listener to receive events
187 /// @param oldFilename The name of the file or directory moved 187 /// @param recursive Set this to true to include subdirectories
188 virtual void handleFileAction( WatchID watchid, const std::string& dir, 188 /// @param options Allows customization of a watcher
189 const std::string& filename, Action action, 189 /// @return Returns the watch id for the directory or, on error, a WatchID with Error type.
190 std::string oldFilename = "" ) = 0; 190 WatchID addWatch( const std::string& directory, FileWatchListener* watcher, bool recursive,
191}; 191 const std::vector<WatcherOption>& options );
192 192
193} // namespace efsw 193 /// Remove a directory watch. This is a brute force search O(nlogn).
194 194 void removeWatch( const std::string& directory );
195#endif 195
196 /// Remove a directory watch. This is a map lookup O(logn).
197 void removeWatch( WatchID watchid );
198
199 /// Starts watching ( in other thread )
200 void watch();
201
202 /// @return Returns a list of the directories that are being watched
203 std::vector<std::string> directories();
204
205 /** Allow recursive watchers to follow symbolic links to other directories
206 * followSymlinks is disabled by default
207 */
208 void followSymlinks( bool follow );
209
210 /** @return If can follow symbolic links to directorioes */
211 const bool& followSymlinks() const;
212
213 /** When enable this it will allow symlinks to watch recursively out of the pointed directory.
214 * follorSymlinks must be enabled to this work.
215 * For example, added symlink to /home/folder, and the symlink points to /, this by default is
216 * not allowed, it's only allowed to symlink anything from /home/ and deeper. This is to avoid
217 * great levels of recursion. Enabling this could lead in infinite recursion, and crash the
218 * watcher ( it will try not to avoid this ). Buy enabling out of scope links, it will allow
219 * this behavior. allowOutOfScopeLinks are disabled by default.
220 */
221 void allowOutOfScopeLinks( bool allow );
222
223 /// @return Returns if out of scope links are allowed
224 const bool& allowOutOfScopeLinks() const;
225
226 private:
227 /// The implementation
228 FileWatcherImpl* mImpl;
229 bool mFollowSymlinks;
230 bool mOutOfScopeLinks;
231};
232
233/// Basic interface for listening for file events.
234/// @class FileWatchListener
235class FileWatchListener {
236 public:
237 virtual ~FileWatchListener() {}
238
239 /// Handles the action file action
240 /// @param watchid The watch id for the directory
241 /// @param dir The directory
242 /// @param filename The filename that was accessed (not full path)
243 /// @param action Action that was performed
244 /// @param oldFilename The name of the file or directory moved
245 virtual void handleFileAction( WatchID watchid, const std::string& dir,
246 const std::string& filename, Action action,
247 std::string oldFilename = "" ) = 0;
248};
249
250/// Optional, typically platform specific parameter for customization of a watcher.
251/// @class WatcherOption
252class WatcherOption {
253 public:
254 WatcherOption( Option option, int value ) : mOption( option ), mValue( value ){};
255 Option mOption;
256 int mValue;
257};
258
259} // namespace efsw
260
261#endif
diff --git a/src/3rdParty/efsw/inotify-nosys.h b/src/3rdParty/efsw/inotify-nosys.h
index be1e627..be1e627 100755..100644
--- a/src/3rdParty/efsw/inotify-nosys.h
+++ b/src/3rdParty/efsw/inotify-nosys.h
diff --git a/src/3rdParty/efsw/platform/platformimpl.hpp b/src/3rdParty/efsw/platform/platformimpl.hpp
index 5442580..5442580 100755..100644
--- a/src/3rdParty/efsw/platform/platformimpl.hpp
+++ b/src/3rdParty/efsw/platform/platformimpl.hpp
diff --git a/src/3rdParty/efsw/platform/posix/FileSystemImpl.cpp b/src/3rdParty/efsw/platform/posix/FileSystemImpl.cpp
index 92eeb47..92eeb47 100755..100644
--- a/src/3rdParty/efsw/platform/posix/FileSystemImpl.cpp
+++ b/src/3rdParty/efsw/platform/posix/FileSystemImpl.cpp
diff --git a/src/3rdParty/efsw/platform/posix/FileSystemImpl.hpp b/src/3rdParty/efsw/platform/posix/FileSystemImpl.hpp
index 0bfba76..0bfba76 100755..100644
--- a/src/3rdParty/efsw/platform/posix/FileSystemImpl.hpp
+++ b/src/3rdParty/efsw/platform/posix/FileSystemImpl.hpp
diff --git a/src/3rdParty/efsw/platform/posix/MutexImpl.cpp b/src/3rdParty/efsw/platform/posix/MutexImpl.cpp
index 2233798..2233798 100755..100644
--- a/src/3rdParty/efsw/platform/posix/MutexImpl.cpp
+++ b/src/3rdParty/efsw/platform/posix/MutexImpl.cpp
diff --git a/src/3rdParty/efsw/platform/posix/MutexImpl.hpp b/src/3rdParty/efsw/platform/posix/MutexImpl.hpp
index a33d827..a33d827 100755..100644
--- a/src/3rdParty/efsw/platform/posix/MutexImpl.hpp
+++ b/src/3rdParty/efsw/platform/posix/MutexImpl.hpp
diff --git a/src/3rdParty/efsw/platform/posix/SystemImpl.cpp b/src/3rdParty/efsw/platform/posix/SystemImpl.cpp
index 37d4120..37d4120 100755..100644
--- a/src/3rdParty/efsw/platform/posix/SystemImpl.cpp
+++ b/src/3rdParty/efsw/platform/posix/SystemImpl.cpp
diff --git a/src/3rdParty/efsw/platform/posix/SystemImpl.hpp b/src/3rdParty/efsw/platform/posix/SystemImpl.hpp
index 9322b06..9322b06 100755..100644
--- a/src/3rdParty/efsw/platform/posix/SystemImpl.hpp
+++ b/src/3rdParty/efsw/platform/posix/SystemImpl.hpp
diff --git a/src/3rdParty/efsw/platform/posix/ThreadImpl.cpp b/src/3rdParty/efsw/platform/posix/ThreadImpl.cpp
index e0ae84f..772fbc9 100755..100644
--- a/src/3rdParty/efsw/platform/posix/ThreadImpl.cpp
+++ b/src/3rdParty/efsw/platform/posix/ThreadImpl.cpp
@@ -5,11 +5,10 @@
5 5
6#include <cassert> 6#include <cassert>
7#include <efsw/Debug.hpp> 7#include <efsw/Debug.hpp>
8#include <iostream>
9 8
10namespace efsw { namespace Platform { 9namespace efsw { namespace Platform {
11 10
12ThreadImpl::ThreadImpl( Thread* owner ) : mIsActive( false ) { 11ThreadImpl::ThreadImpl( efsw::Thread* owner ) : mIsActive( false ) {
13 mIsActive = pthread_create( &mThread, NULL, &ThreadImpl::entryPoint, owner ) == 0; 12 mIsActive = pthread_create( &mThread, NULL, &ThreadImpl::entryPoint, owner ) == 0;
14 13
15 if ( !mIsActive ) { 14 if ( !mIsActive ) {
@@ -17,14 +16,16 @@ ThreadImpl::ThreadImpl( Thread* owner ) : mIsActive( false ) {
17 } 16 }
18} 17}
19 18
19ThreadImpl::~ThreadImpl() {
20 terminate();
21}
22
20void ThreadImpl::wait() { 23void ThreadImpl::wait() {
21 // Wait for the thread to finish, no timeout 24 // Wait for the thread to finish, no timeout
22 if ( mIsActive ) { 25 if ( mIsActive ) {
23 assert( pthread_equal( pthread_self(), mThread ) == 0 ); 26 assert( pthread_equal( pthread_self(), mThread ) == 0 );
24 27
25 pthread_join( mThread, NULL ); 28 mIsActive = pthread_join( mThread, NULL ) != 0;
26
27 mIsActive = false; // Reset the thread state
28 } 29 }
29} 30}
30 31
@@ -41,14 +42,14 @@ void ThreadImpl::terminate() {
41} 42}
42 43
43void* ThreadImpl::entryPoint( void* userData ) { 44void* ThreadImpl::entryPoint( void* userData ) {
44 // The Thread instance is stored in the user data
45 Thread* owner = static_cast<Thread*>( userData );
46
47// Tell the thread to handle cancel requests immediatly 45// Tell the thread to handle cancel requests immediatly
48#ifdef PTHREAD_CANCEL_ASYNCHRONOUS 46#ifdef PTHREAD_CANCEL_ASYNCHRONOUS
49 pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL ); 47 pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );
50#endif 48#endif
51 49
50 // The Thread instance is stored in the user data
51 Thread* owner = static_cast<Thread*>( userData );
52
52 // Forward to the owner 53 // Forward to the owner
53 owner->run(); 54 owner->run();
54 55
diff --git a/src/3rdParty/efsw/platform/posix/ThreadImpl.hpp b/src/3rdParty/efsw/platform/posix/ThreadImpl.hpp
index ffc6da0..2e02f9a 100755..100644
--- a/src/3rdParty/efsw/platform/posix/ThreadImpl.hpp
+++ b/src/3rdParty/efsw/platform/posix/ThreadImpl.hpp
@@ -5,6 +5,7 @@
5 5
6#if defined( EFSW_PLATFORM_POSIX ) 6#if defined( EFSW_PLATFORM_POSIX )
7 7
8#include <efsw/Atomic.hpp>
8#include <pthread.h> 9#include <pthread.h>
9 10
10namespace efsw { 11namespace efsw {
@@ -15,7 +16,9 @@ namespace Platform {
15 16
16class ThreadImpl { 17class ThreadImpl {
17 public: 18 public:
18 ThreadImpl( Thread* owner ); 19 explicit ThreadImpl( efsw::Thread* owner );
20
21 ~ThreadImpl();
19 22
20 void wait(); 23 void wait();
21 24
@@ -25,7 +28,7 @@ class ThreadImpl {
25 static void* entryPoint( void* userData ); 28 static void* entryPoint( void* userData );
26 29
27 pthread_t mThread; 30 pthread_t mThread;
28 bool mIsActive; 31 Atomic<bool> mIsActive;
29}; 32};
30 33
31} // namespace Platform 34} // namespace Platform
diff --git a/src/3rdParty/efsw/platform/win/FileSystemImpl.cpp b/src/3rdParty/efsw/platform/win/FileSystemImpl.cpp
index 2b87513..2b87513 100755..100644
--- a/src/3rdParty/efsw/platform/win/FileSystemImpl.cpp
+++ b/src/3rdParty/efsw/platform/win/FileSystemImpl.cpp
diff --git a/src/3rdParty/efsw/platform/win/FileSystemImpl.hpp b/src/3rdParty/efsw/platform/win/FileSystemImpl.hpp
index e952efc..e952efc 100755..100644
--- a/src/3rdParty/efsw/platform/win/FileSystemImpl.hpp
+++ b/src/3rdParty/efsw/platform/win/FileSystemImpl.hpp
diff --git a/src/3rdParty/efsw/platform/win/MutexImpl.cpp b/src/3rdParty/efsw/platform/win/MutexImpl.cpp
index 62b7f83..62b7f83 100755..100644
--- a/src/3rdParty/efsw/platform/win/MutexImpl.cpp
+++ b/src/3rdParty/efsw/platform/win/MutexImpl.cpp
diff --git a/src/3rdParty/efsw/platform/win/MutexImpl.hpp b/src/3rdParty/efsw/platform/win/MutexImpl.hpp
index 7b06492..7b06492 100755..100644
--- a/src/3rdParty/efsw/platform/win/MutexImpl.hpp
+++ b/src/3rdParty/efsw/platform/win/MutexImpl.hpp
diff --git a/src/3rdParty/efsw/platform/win/SystemImpl.cpp b/src/3rdParty/efsw/platform/win/SystemImpl.cpp
index d1f2b21..d1f2b21 100755..100644
--- a/src/3rdParty/efsw/platform/win/SystemImpl.cpp
+++ b/src/3rdParty/efsw/platform/win/SystemImpl.cpp
diff --git a/src/3rdParty/efsw/platform/win/SystemImpl.hpp b/src/3rdParty/efsw/platform/win/SystemImpl.hpp
index 99b4867..99b4867 100755..100644
--- a/src/3rdParty/efsw/platform/win/SystemImpl.hpp
+++ b/src/3rdParty/efsw/platform/win/SystemImpl.hpp
diff --git a/src/3rdParty/efsw/platform/win/ThreadImpl.cpp b/src/3rdParty/efsw/platform/win/ThreadImpl.cpp
index d0fde8b..463934c 100755..100644
--- a/src/3rdParty/efsw/platform/win/ThreadImpl.cpp
+++ b/src/3rdParty/efsw/platform/win/ThreadImpl.cpp
@@ -8,7 +8,7 @@
8 8
9namespace efsw { namespace Platform { 9namespace efsw { namespace Platform {
10 10
11ThreadImpl::ThreadImpl( Thread* owner ) { 11ThreadImpl::ThreadImpl( efsw::Thread* owner ) {
12 mThread = reinterpret_cast<HANDLE>( 12 mThread = reinterpret_cast<HANDLE>(
13 _beginthreadex( NULL, 0, &ThreadImpl::entryPoint, owner, 0, &mThreadId ) ); 13 _beginthreadex( NULL, 0, &ThreadImpl::entryPoint, owner, 0, &mThreadId ) );
14 14
diff --git a/src/3rdParty/efsw/platform/win/ThreadImpl.hpp b/src/3rdParty/efsw/platform/win/ThreadImpl.hpp
index 1afb593..455f24c 100755..100644
--- a/src/3rdParty/efsw/platform/win/ThreadImpl.hpp
+++ b/src/3rdParty/efsw/platform/win/ThreadImpl.hpp
@@ -19,7 +19,7 @@ namespace Platform {
19 19
20class ThreadImpl { 20class ThreadImpl {
21 public: 21 public:
22 ThreadImpl( Thread* owner ); 22 explicit ThreadImpl( efsw::Thread* owner );
23 23
24 ~ThreadImpl(); 24 ~ThreadImpl();
25 25
diff --git a/src/3rdParty/efsw/sophist.h b/src/3rdParty/efsw/sophist.h
index 3a64504..82e5c36 100755..100644
--- a/src/3rdParty/efsw/sophist.h
+++ b/src/3rdParty/efsw/sophist.h
@@ -1,147 +1,147 @@
1/* sophist.h - 0.3 - public domain - Sean Barrett 2010 1/* sophist.h - 0.3 - public domain - Sean Barrett 2010
2** Knowledge drawn from Brian Hook's posh.h and http://predef.sourceforge.net 2** Knowledge drawn from Brian Hook's posh.h and http://predef.sourceforge.net
3** Sophist provides portable types; you typedef/#define them to your own names 3** Sophist provides portable types; you typedef/#define them to your own names
4** 4**
5** defines: 5** defines:
6** - SOPHIST_endian - either SOPHIST_little_endian or SOPHIST_big_endian 6** - SOPHIST_endian - either SOPHIST_little_endian or SOPHIST_big_endian
7** - SOPHIST_has_64 - either 0 or 1; if 0, int64 types aren't defined 7** - SOPHIST_has_64 - either 0 or 1; if 0, int64 types aren't defined
8** - SOPHIST_pointer64 - either 0 or 1; if 1, pointer is 64-bit 8** - SOPHIST_pointer64 - either 0 or 1; if 1, pointer is 64-bit
9** 9**
10** - SOPHIST_intptr, SOPHIST_uintptr - integer same size as pointer 10** - SOPHIST_intptr, SOPHIST_uintptr - integer same size as pointer
11** - SOPHIST_int8, SOPHIST_uint8, SOPHIST_int16, SOPHIST_uint16 11** - SOPHIST_int8, SOPHIST_uint8, SOPHIST_int16, SOPHIST_uint16
12** - SOPHIST_int32, SOPHIST_uint32, SOPHIST_int64, SOPHIST_uint64 12** - SOPHIST_int32, SOPHIST_uint32, SOPHIST_int64, SOPHIST_uint64
13** - SOPHIST_int64_constant(number) - macros for creating 64-bit 13** - SOPHIST_int64_constant(number) - macros for creating 64-bit
14** - SOPHIST_uint64_constant(number) integer constants 14** - SOPHIST_uint64_constant(number) integer constants
15** - SOPHIST_printf_format64 - string for printf format for int64 15** - SOPHIST_printf_format64 - string for printf format for int64
16*/ 16*/
17 17
18#ifndef __INCLUDE_SOPHIST_H__ 18#ifndef __INCLUDE_SOPHIST_H__
19#define __INCLUDE_SOPHIST_H__ 19#define __INCLUDE_SOPHIST_H__
20 20
21#define SOPHIST_compiletime_assert(name,val) \ 21#define SOPHIST_compiletime_assert(name,val) \
22 typedef int SOPHIST__assert##name[(val) ? 1 : -1] 22 typedef int SOPHIST__assert##name[(val) ? 1 : -1]
23 23
24/* define a couple synthetic rules to make code more readable */ 24/* define a couple synthetic rules to make code more readable */
25#if (defined(__sparc__) || defined(__sparc)) && \ 25#if (defined(__sparc__) || defined(__sparc)) && \
26 (defined(__arch64__) || defined(__sparcv9) || defined(__sparc_v9__)) 26 (defined(__arch64__) || defined(__sparcv9) || defined(__sparc_v9__))
27 #define SOPHIST_sparc64 27 #define SOPHIST_sparc64
28#endif 28#endif
29 29
30#if (defined(linux) || defined(__linux__)) && \ 30#if (defined(linux) || defined(__linux__)) && \
31 (defined(__alpha)||defined(__alpha__)||defined(__x86_64__)||defined(_M_X64)) 31 (defined(__alpha)||defined(__alpha__)||defined(__x86_64__)||defined(_M_X64))
32 #define SOPHIST_linux64 32 #define SOPHIST_linux64
33#endif 33#endif
34 34
35/* basic types */ 35/* basic types */
36typedef signed char SOPHIST_int8; 36typedef signed char SOPHIST_int8;
37typedef unsigned char SOPHIST_uint8; 37typedef unsigned char SOPHIST_uint8;
38 38
39typedef signed short SOPHIST_int16; 39typedef signed short SOPHIST_int16;
40typedef unsigned short SOPHIST_uint16; 40typedef unsigned short SOPHIST_uint16;
41 41
42#ifdef __palmos__ 42#ifdef __palmos__
43 typedef signed long SOPHIST_int32; 43 typedef signed long SOPHIST_int32;
44 typedef unsigned long SOPHIST_uint32; 44 typedef unsigned long SOPHIST_uint32;
45#else 45#else
46 typedef signed int SOPHIST_int32; 46 typedef signed int SOPHIST_int32;
47 typedef unsigned int SOPHIST_uint32; 47 typedef unsigned int SOPHIST_uint32;
48#endif 48#endif
49 49
50#ifndef SOPHIST_NO_64 50#ifndef SOPHIST_NO_64
51 #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) \ 51 #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) \
52 || (defined(__alpha) && defined(__DECC)) 52 || (defined(__alpha) && defined(__DECC))
53 53
54 typedef signed __int64 SOPHIST_int64; 54 typedef signed __int64 SOPHIST_int64;
55 typedef unsigned __int64 SOPHIST_uint64; 55 typedef unsigned __int64 SOPHIST_uint64;
56 #define SOPHIST_has_64 1 56 #define SOPHIST_has_64 1
57 #define SOPHIST_int64_constant(x) (x##i64) 57 #define SOPHIST_int64_constant(x) (x##i64)
58 #define SOPHIST_uint64_constant(x) (x##ui64) 58 #define SOPHIST_uint64_constant(x) (x##ui64)
59 #define SOPHIST_printf_format64 "I64" 59 #define SOPHIST_printf_format64 "I64"
60 60
61 #elif defined(__LP64__) || defined(__powerpc64__) || defined(SOPHIST_sparc64) 61 #elif defined(__LP64__) || defined(__powerpc64__) || defined(SOPHIST_sparc64)
62 62
63 typedef signed long SOPHIST_int64; 63 typedef signed long SOPHIST_int64;
64 typedef unsigned long SOPHIST_uint64; 64 typedef unsigned long SOPHIST_uint64;
65 65
66 #define SOPHIST_has_64 1 66 #define SOPHIST_has_64 1
67 #define SOPHIST_int64_constant(x) ((SOPHIST_int64) x) 67 #define SOPHIST_int64_constant(x) ((SOPHIST_int64) x)
68 #define SOPHIST_uint64_constant(x) ((SOPHIST_uint64) x) 68 #define SOPHIST_uint64_constant(x) ((SOPHIST_uint64) x)
69 #define SOPHIST_printf_format64 "l" 69 #define SOPHIST_printf_format64 "l"
70 70
71 #elif defined(_LONG_LONG) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) \ 71 #elif defined(_LONG_LONG) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) \
72 || defined(__GNUC__) || defined(__MWERKS__) || defined(__APPLE_CC__) \ 72 || defined(__GNUC__) || defined(__MWERKS__) || defined(__APPLE_CC__) \
73 || defined(sgi) || defined (__sgi) || defined(__sgi__) \ 73 || defined(sgi) || defined (__sgi) || defined(__sgi__) \
74 || defined(_CRAYC) 74 || defined(_CRAYC)
75 75
76 typedef signed long long SOPHIST_int64; 76 typedef signed long long SOPHIST_int64;
77 typedef unsigned long long SOPHIST_uint64; 77 typedef unsigned long long SOPHIST_uint64;
78 78
79 #define SOPHIST_has_64 1 79 #define SOPHIST_has_64 1
80 #define SOPHIST_int64_constant(x) (x##LL) 80 #define SOPHIST_int64_constant(x) (x##LL)
81 #define SOPHIST_uint64_constant(x) (x##ULL) 81 #define SOPHIST_uint64_constant(x) (x##ULL)
82 #define SOPHIST_printf_format64 "ll" 82 #define SOPHIST_printf_format64 "ll"
83 #endif 83 #endif
84#endif 84#endif
85 85
86#ifndef SOPHIST_has_64 86#ifndef SOPHIST_has_64
87#define SOPHIST_has_64 0 87#define SOPHIST_has_64 0
88#endif 88#endif
89 89
90SOPHIST_compiletime_assert( int8 , sizeof(SOPHIST_int8 ) == 1); 90SOPHIST_compiletime_assert( int8 , sizeof(SOPHIST_int8 ) == 1);
91SOPHIST_compiletime_assert(uint16, sizeof(SOPHIST_int16) == 2); 91SOPHIST_compiletime_assert(uint16, sizeof(SOPHIST_int16) == 2);
92SOPHIST_compiletime_assert( int32, sizeof(SOPHIST_int32 ) == 4); 92SOPHIST_compiletime_assert( int32, sizeof(SOPHIST_int32 ) == 4);
93SOPHIST_compiletime_assert(uint32, sizeof(SOPHIST_uint32) == 4); 93SOPHIST_compiletime_assert(uint32, sizeof(SOPHIST_uint32) == 4);
94 94
95#if SOPHIST_has_64 95#if SOPHIST_has_64
96 SOPHIST_compiletime_assert( int64, sizeof(SOPHIST_int64 ) == 8); 96 SOPHIST_compiletime_assert( int64, sizeof(SOPHIST_int64 ) == 8);
97 SOPHIST_compiletime_assert(uint64, sizeof(SOPHIST_uint64) == 8); 97 SOPHIST_compiletime_assert(uint64, sizeof(SOPHIST_uint64) == 8);
98#endif 98#endif
99 99
100/* determine whether pointers are 64-bit */ 100/* determine whether pointers are 64-bit */
101 101
102#if defined(SOPHIST_linux64) || defined(SOPHIST_sparc64) \ 102#if defined(SOPHIST_linux64) || defined(SOPHIST_sparc64) \
103 || defined(__osf__) || (defined(_WIN64) && !defined(_XBOX)) \ 103 || defined(__osf__) || (defined(_WIN64) && !defined(_XBOX)) \
104 || defined(__64BIT__) \ 104 || defined(__64BIT__) \
105 || defined(__LP64) || defined(__LP64__) || defined(_LP64) \ 105 || defined(__LP64) || defined(__LP64__) || defined(_LP64) \
106 || defined(_ADDR64) || defined(_CRAYC) \ 106 || defined(_ADDR64) || defined(_CRAYC) \
107 107
108 #define SOPHIST_pointer64 1 108 #define SOPHIST_pointer64 1
109 109
110 SOPHIST_compiletime_assert(pointer64, sizeof(void*) == 8); 110 SOPHIST_compiletime_assert(pointer64, sizeof(void*) == 8);
111 111
112 typedef SOPHIST_int64 SOPHIST_intptr; 112 typedef SOPHIST_int64 SOPHIST_intptr;
113 typedef SOPHIST_uint64 SOPHIST_uintptr; 113 typedef SOPHIST_uint64 SOPHIST_uintptr;
114#else 114#else
115 115
116 #define SOPHIST_pointer64 0 116 #define SOPHIST_pointer64 0
117 117
118 SOPHIST_compiletime_assert(pointer64, sizeof(void*) <= 4); 118 SOPHIST_compiletime_assert(pointer64, sizeof(void*) <= 4);
119 119
120 /* do we care about pointers that are only 16-bit? */ 120 /* do we care about pointers that are only 16-bit? */
121 typedef SOPHIST_int32 SOPHIST_intptr; 121 typedef SOPHIST_int32 SOPHIST_intptr;
122 typedef SOPHIST_uint32 SOPHIST_uintptr; 122 typedef SOPHIST_uint32 SOPHIST_uintptr;
123 123
124#endif 124#endif
125 125
126SOPHIST_compiletime_assert(intptr, sizeof(SOPHIST_intptr) == sizeof(char *)); 126SOPHIST_compiletime_assert(intptr, sizeof(SOPHIST_intptr) == sizeof(char *));
127 127
128/* enumerate known little endian cases; fallback to big-endian */ 128/* enumerate known little endian cases; fallback to big-endian */
129 129
130#define SOPHIST_little_endian 1 130#define SOPHIST_little_endian 1
131#define SOPHIST_big_endian 2 131#define SOPHIST_big_endian 2
132 132
133#if defined(__386__) || defined(i386) || defined(__i386__) \ 133#if defined(__386__) || defined(i386) || defined(__i386__) \
134 || defined(__X86) || defined(_M_IX86) \ 134 || defined(__X86) || defined(_M_IX86) \
135 || defined(_M_X64) || defined(__x86_64__) \ 135 || defined(_M_X64) || defined(__x86_64__) \
136 || defined(alpha) || defined(__alpha) || defined(__alpha__) \ 136 || defined(alpha) || defined(__alpha) || defined(__alpha__) \
137 || defined(_M_ALPHA) \ 137 || defined(_M_ALPHA) \
138 || defined(ARM) || defined(_ARM) || defined(__arm__) \ 138 || defined(ARM) || defined(_ARM) || defined(__arm__) \
139 || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ 139 || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \
140 || defined(_WIN32_WCE) || defined(__NT__) \ 140 || defined(_WIN32_WCE) || defined(__NT__) \
141 || defined(__MIPSEL__) 141 || defined(__MIPSEL__)
142 #define SOPHIST_endian SOPHIST_little_endian 142 #define SOPHIST_endian SOPHIST_little_endian
143#else 143#else
144 #define SOPHIST_endian SOPHIST_big_endian 144 #define SOPHIST_endian SOPHIST_big_endian
145#endif 145#endif
146 146
147#endif /* __INCLUDE_SOPHIST_H__ */ 147#endif /* __INCLUDE_SOPHIST_H__ */
diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp
index fe6e726..945e1d7 100644
--- a/src/yuescript/yue_ast.cpp
+++ b/src/yuescript/yue_ast.cpp
@@ -167,12 +167,11 @@ std::string ExistentialOp_t::to_string(void*) const {
167std::string TableAppendingOp_t::to_string(void*) const { 167std::string TableAppendingOp_t::to_string(void*) const {
168 return "[]"s; 168 return "[]"s;
169} 169}
170std::string PlainItem_t::to_string(void *) const { 170std::string PlainItem_t::to_string(void*) const {
171 return {}; 171 return {};
172} 172}
173std::string GlobalOp_t::to_string(void* ud) const { 173std::string GlobalOp_t::to_string(void*) const {
174 auto info = reinterpret_cast<YueFormat*>(ud); 174 return "*"s;
175 return info->convert(this);
176} 175}
177std::string ExportDefault_t::to_string(void*) const { 176std::string ExportDefault_t::to_string(void*) const {
178 return "default"s; 177 return "default"s;
@@ -188,9 +187,17 @@ std::string ConstValue_t::to_string(void* ud) const {
188std::string NotIn_t::to_string(void*) const { 187std::string NotIn_t::to_string(void*) const {
189 return {}; 188 return {};
190} 189}
190std::string Break_t::to_string(void*) const {
191 return "break"s;
192}
193std::string Continue_t::to_string(void*) const {
194 return "continue"s;
195}
191std::string BreakLoop_t::to_string(void* ud) const { 196std::string BreakLoop_t::to_string(void* ud) const {
192 auto info = reinterpret_cast<YueFormat*>(ud); 197 if (value) {
193 return info->convert(this); 198 return type->to_string(ud) + ' ' + value->to_string(ud);
199 }
200 return type->to_string(ud);
194} 201}
195std::string YueLineComment_t::to_string(void* ud) const { 202std::string YueLineComment_t::to_string(void* ud) const {
196 auto info = reinterpret_cast<YueFormat*>(ud); 203 auto info = reinterpret_cast<YueFormat*>(ud);
@@ -297,6 +304,17 @@ std::string ImportAs_t::to_string(void* ud) const {
297 } 304 }
298 return join(temp, " "s); 305 return join(temp, " "s);
299} 306}
307std::string ImportGlobal_t::to_string(void* ud) const {
308 str_list temp;
309 for (auto seg : segs.objects()) {
310 temp.emplace_back(seg->to_string(ud));
311 }
312 auto item = join(temp, "."s);
313 if (target) {
314 return item + " as "s + target->to_string(ud);
315 }
316 return item;
317}
300std::string Import_t::to_string(void* ud) const { 318std::string Import_t::to_string(void* ud) const {
301 if (ast_is<FromImport_t>(content)) { 319 if (ast_is<FromImport_t>(content)) {
302 return content->to_string(ud); 320 return content->to_string(ud);
@@ -324,6 +342,12 @@ std::string Backcall_t::to_string(void* ud) const {
324 temp.emplace_back(value->to_string(ud)); 342 temp.emplace_back(value->to_string(ud));
325 return join(temp, " "sv); 343 return join(temp, " "sv);
326} 344}
345std::string SubBackcall_t::to_string(void* ud) const {
346 str_list temp;
347 temp.emplace_back(arrow->to_string(ud));
348 temp.emplace_back(value->to_string(ud));
349 return join(temp, " "sv);
350}
327std::string PipeBody_t::to_string(void* ud) const { 351std::string PipeBody_t::to_string(void* ud) const {
328 auto info = reinterpret_cast<YueFormat*>(ud); 352 auto info = reinterpret_cast<YueFormat*>(ud);
329 str_list temp; 353 str_list temp;
@@ -359,8 +383,8 @@ std::string With_t::to_string(void* ud) const {
359 str_list temp{ 383 str_list temp{
360 eop ? "with?"s : "with"s, 384 eop ? "with?"s : "with"s,
361 valueList->to_string(ud)}; 385 valueList->to_string(ud)};
362 if (assigns) { 386 if (assign) {
363 temp.push_back(assigns->to_string(ud)); 387 temp.push_back(':' + assign->to_string(ud));
364 } 388 }
365 if (body.is<Statement_t>()) { 389 if (body.is<Statement_t>()) {
366 return join(temp, " "sv) + " do "s + body->to_string(ud); 390 return join(temp, " "sv) + " do "s + body->to_string(ud);
@@ -406,6 +430,9 @@ std::string SwitchCase_t::to_string(void* ud) const {
406std::string Switch_t::to_string(void* ud) const { 430std::string Switch_t::to_string(void* ud) const {
407 auto info = reinterpret_cast<YueFormat*>(ud); 431 auto info = reinterpret_cast<YueFormat*>(ud);
408 str_list temp{"switch "s + target->to_string(ud)}; 432 str_list temp{"switch "s + target->to_string(ud)};
433 if (assignment) {
434 temp.back().append(assignment->to_string(ud));
435 }
409 info->pushScope(); 436 info->pushScope();
410 for (auto branch : branches.objects()) { 437 for (auto branch : branches.objects()) {
411 temp.emplace_back(info->ind() + branch->to_string(ud)); 438 temp.emplace_back(info->ind() + branch->to_string(ud));
@@ -449,41 +476,75 @@ std::string If_t::to_string(void* ud) const {
449 temp.back() += " then"s; 476 temp.back() += " then"s;
450 } 477 }
451 ++it; 478 ++it;
452 bool condition = true; 479 enum class NType {
480 Cond,
481 Stat,
482 Block
483 };
484 NType lastType = NType::Cond;
453 for (; it != nodes.objects().end(); ++it) { 485 for (; it != nodes.objects().end(); ++it) {
454 auto node = *it; 486 auto node = *it;
455 switch (node->get_id()) { 487 switch (node->get_id()) {
456 case id<IfCond_t>(): 488 case id<IfCond_t>():
457 temp.emplace_back(info->ind() + "elseif "s + node->to_string(ud)); 489 temp.emplace_back(info->ind() + "elseif "s + node->to_string(ud));
458 condition = true; 490 lastType = NType::Cond;
459 break; 491 break;
460 case id<Statement_t>(): { 492 case id<Statement_t>(): {
461 if (condition) { 493 switch (lastType) {
462 temp.back() += " then "s + node->to_string(ud); 494 case NType::Cond:
463 } else { 495 temp.back() += " then "s + node->to_string(ud);
464 temp.emplace_back(info->ind() + "else "s + node->to_string(ud)); 496 break;
497 case NType::Stat:
498 if (temp.back().back() == '\n') {
499 temp.emplace_back(info->ind() + "else "s + node->to_string(ud));
500 } else {
501 temp.back() += " else "s + node->to_string(ud);
502 }
503 break;
504 case NType::Block:
505 temp.emplace_back(info->ind() + "else "s + node->to_string(ud));
506 break;
465 } 507 }
466 condition = false; 508 lastType = NType::Stat;
467 break; 509 break;
468 } 510 }
469 case id<Block_t>(): { 511 case id<Block_t>(): {
470 if (condition) { 512 switch (lastType) {
471 info->pushScope(); 513 case NType::Cond: {
472 temp.emplace_back(node->to_string(ud)); 514 info->pushScope();
473 if (temp.back().empty()) { 515 temp.emplace_back(node->to_string(ud));
474 temp.back() = info->ind() + "--"s; 516 if (temp.back().empty()) {
517 temp.back() = info->ind() + "--"s;
518 }
519 info->popScope();
520 break;
521 }
522 case NType::Stat: {
523 if (temp.back().back() == '\n') {
524 temp.emplace_back(info->ind() + "else"s);
525 } else {
526 temp.back() += " else"s;
527 }
528 info->pushScope();
529 temp.emplace_back(node->to_string(ud));
530 if (temp.back().empty()) {
531 temp.back() = info->ind() + "--"s;
532 }
533 info->popScope();
534 break;
475 } 535 }
476 info->popScope(); 536 case NType::Block: {
477 } else { 537 temp.emplace_back(info->ind() + "else"s);
478 temp.emplace_back(info->ind() + "else"s); 538 info->pushScope();
479 info->pushScope(); 539 temp.emplace_back(node->to_string(ud));
480 temp.emplace_back(node->to_string(ud)); 540 if (temp.back().empty()) {
481 if (temp.back().empty()) { 541 temp.back() = info->ind() + "--"s;
482 temp.back() = info->ind() + "--"s; 542 }
543 info->popScope();
544 break;
483 } 545 }
484 info->popScope();
485 } 546 }
486 condition = false; 547 lastType = NType::Block;
487 break; 548 break;
488 } 549 }
489 } 550 }
@@ -511,10 +572,10 @@ std::string While_t::to_string(void* ud) const {
511} 572}
512std::string Repeat_t::to_string(void* ud) const { 573std::string Repeat_t::to_string(void* ud) const {
513 auto info = reinterpret_cast<YueFormat*>(ud); 574 auto info = reinterpret_cast<YueFormat*>(ud);
514 str_list temp; 575 if (body.is<Statement_t>()) {
515 if (body->content.is<Statement_t>()) { 576 return "repeat "s + body->to_string(ud) + " until "s + condition->to_string(ud);
516 temp.emplace_back("repeat "s + body->to_string(ud));
517 } else { 577 } else {
578 str_list temp;
518 temp.emplace_back("repeat"s); 579 temp.emplace_back("repeat"s);
519 info->pushScope(); 580 info->pushScope();
520 temp.emplace_back(body->to_string(ud)); 581 temp.emplace_back(body->to_string(ud));
@@ -522,9 +583,9 @@ std::string Repeat_t::to_string(void* ud) const {
522 temp.back() = info->ind() + "--"s; 583 temp.back() = info->ind() + "--"s;
523 } 584 }
524 info->popScope(); 585 info->popScope();
586 temp.emplace_back(info->ind() + "until "s + condition->to_string(ud));
587 return join(temp, "\n"sv);
525 } 588 }
526 temp.emplace_back(info->ind() + "until "s + condition->to_string(ud));
527 return join(temp, "\n"sv);
528} 589}
529std::string ForStepValue_t::to_string(void* ud) const { 590std::string ForStepValue_t::to_string(void* ud) const {
530 return value->to_string(ud); 591 return value->to_string(ud);
@@ -596,10 +657,13 @@ std::string CatchBlock_t::to_string(void* ud) const {
596std::string Try_t::to_string(void* ud) const { 657std::string Try_t::to_string(void* ud) const {
597 auto info = reinterpret_cast<YueFormat*>(ud); 658 auto info = reinterpret_cast<YueFormat*>(ud);
598 str_list temp; 659 str_list temp;
660 temp.emplace_back("try"s);
661 if (eop) {
662 temp.back() += eop->to_string(ud);
663 }
599 if (func.is<Exp_t>()) { 664 if (func.is<Exp_t>()) {
600 temp.emplace_back("try "s + func->to_string(ud)); 665 temp.back() += (" "s + func->to_string(ud));
601 } else { 666 } else {
602 temp.emplace_back("try"s);
603 info->pushScope(); 667 info->pushScope();
604 temp.emplace_back(func->to_string(ud)); 668 temp.emplace_back(func->to_string(ud));
605 if (temp.back().empty()) { 669 if (temp.back().empty()) {
@@ -851,6 +915,12 @@ std::string Exp_t::to_string(void* ud) const {
851 } 915 }
852 return join(temp, " "sv); 916 return join(temp, " "sv);
853} 917}
918std::string ReversedIndex_t::to_string(void* ud) const {
919 if (modifier) {
920 return "[# - "s + modifier->to_string(ud) + ']';
921 }
922 return "[#]"s;
923}
854std::string Callable_t::to_string(void* ud) const { 924std::string Callable_t::to_string(void* ud) const {
855 return item->to_string(ud); 925 return item->to_string(ud);
856} 926}
@@ -937,6 +1007,51 @@ std::string DoubleString_t::to_string(void* ud) const {
937 } 1007 }
938 return '"' + join(temp) + '"'; 1008 return '"' + join(temp) + '"';
939} 1009}
1010std::string YAMLIndent_t::to_string(void* ud) const {
1011 auto info = reinterpret_cast<YueFormat*>(ud);
1012 return info->convert(this);
1013}
1014std::string YAMLLineInner_t::to_string(void* ud) const {
1015 auto info = reinterpret_cast<YueFormat*>(ud);
1016 return info->convert(this);
1017}
1018std::string YAMLLineContent_t::to_string(void* ud) const {
1019 if (content.is<Exp_t>()) {
1020 return "#{"s + content->to_string(ud) + '}';
1021 }
1022 return content->to_string(ud);
1023}
1024std::string YAMLLine_t::to_string(void* ud) const {
1025 str_list temp;
1026 for (auto seg : segments.objects()) {
1027 temp.emplace_back(seg->to_string(ud));
1028 }
1029 return join(temp);
1030}
1031std::string YAMLMultiline_t::to_string(void* ud) const {
1032 auto info = reinterpret_cast<YueFormat*>(ud);
1033 int currentIndent = info->indent;
1034 str_list temp;
1035 int lastIndent = -1;
1036 for (auto line_ : lines.objects()) {
1037 auto line = static_cast<YAMLLine_t*>(line_);
1038 auto indent = line->indent->to_string(ud);
1039 int ind = 0;
1040 for (auto c : indent) {
1041 if (c == ' ') ind++;
1042 if (c == '\t') ind += 4;
1043 }
1044 if (lastIndent < ind) {
1045 info->pushScope();
1046 } else if (lastIndent > ind) {
1047 info->popScope();
1048 }
1049 lastIndent = ind;
1050 temp.emplace_back(indent + line->to_string(ud));
1051 }
1052 info->indent = currentIndent;
1053 return "|\n" + join(temp, "\n"sv) + '\n';
1054}
940std::string String_t::to_string(void* ud) const { 1055std::string String_t::to_string(void* ud) const {
941 return str->to_string(ud); 1056 return str->to_string(ud);
942} 1057}
@@ -1125,7 +1240,7 @@ std::string ClassDecl_t::to_string(void* ud) const {
1125 return line; 1240 return line;
1126} 1241}
1127std::string GlobalValues_t::to_string(void* ud) const { 1242std::string GlobalValues_t::to_string(void* ud) const {
1128 auto line = nameList->to_string(ud); 1243 std::string line = nameList->to_string(ud);
1129 if (valueList) { 1244 if (valueList) {
1130 if (valueList.is<TableBlock_t>()) { 1245 if (valueList.is<TableBlock_t>()) {
1131 line += " =\n"s + valueList->to_string(ud); 1246 line += " =\n"s + valueList->to_string(ud);
@@ -1136,7 +1251,7 @@ std::string GlobalValues_t::to_string(void* ud) const {
1136 return line; 1251 return line;
1137} 1252}
1138std::string Global_t::to_string(void* ud) const { 1253std::string Global_t::to_string(void* ud) const {
1139 return "global "s + item->to_string(ud); 1254 return "global "s + (constAttrib ? "const "s : ""s) + item->to_string(ud);
1140} 1255}
1141std::string Export_t::to_string(void* ud) const { 1256std::string Export_t::to_string(void* ud) const {
1142 auto line = "export"s; 1257 auto line = "export"s;
@@ -1235,6 +1350,9 @@ std::string FnArgDef_t::to_string(void* ud) const {
1235 if (op) { 1350 if (op) {
1236 line += op->to_string(ud); 1351 line += op->to_string(ud);
1237 } 1352 }
1353 if (label) {
1354 line += '`' + label->to_string(ud);
1355 }
1238 if (defaultValue) { 1356 if (defaultValue) {
1239 line += " = "s + defaultValue->to_string(ud); 1357 line += " = "s + defaultValue->to_string(ud);
1240 } 1358 }
@@ -1257,6 +1375,9 @@ std::string FnArgDefList_t::to_string(void* ud) const {
1257 } 1375 }
1258 if (varArg) { 1376 if (varArg) {
1259 temp.emplace_back(info->ind() + varArg->to_string(ud)); 1377 temp.emplace_back(info->ind() + varArg->to_string(ud));
1378 if (label) {
1379 temp.back().append('`' + label->to_string(ud));
1380 }
1260 } 1381 }
1261 return join(temp, "\n"sv); 1382 return join(temp, "\n"sv);
1262 } else { 1383 } else {
@@ -1265,6 +1386,9 @@ std::string FnArgDefList_t::to_string(void* ud) const {
1265 } 1386 }
1266 if (varArg) { 1387 if (varArg) {
1267 temp.emplace_back(varArg->to_string(ud)); 1388 temp.emplace_back(varArg->to_string(ud));
1389 if (label) {
1390 temp.back().append('`' + label->to_string(ud));
1391 }
1268 } 1392 }
1269 return join(temp, ", "sv); 1393 return join(temp, ", "sv);
1270 } 1394 }
@@ -1546,3 +1670,4 @@ std::string File_t::to_string(void* ud) const {
1546} // namespace yue 1670} // namespace yue
1547 1671
1548} // namespace parserlib 1672} // namespace parserlib
1673
diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h
index e670126..1937eb8 100644
--- a/src/yuescript/yue_ast.h
+++ b/src/yuescript/yue_ast.h
@@ -233,8 +233,15 @@ AST_NODE(ImportAs)
233 AST_MEMBER(ImportAs, &literal, &target) 233 AST_MEMBER(ImportAs, &literal, &target)
234AST_END(ImportAs) 234AST_END(ImportAs)
235 235
236AST_NODE(ImportGlobal)
237 ast_ptr<true, Seperator_t> sep;
238 ast_list<true, UnicodeName_t> segs;
239 ast_ptr<false, Variable_t> target;
240 AST_MEMBER(ImportGlobal, &sep, &segs, &target)
241AST_END(ImportGlobal)
242
236AST_NODE(Import) 243AST_NODE(Import)
237 ast_sel<true, ImportAs_t, ImportFrom_t, FromImport_t> content; 244 ast_sel<true, ImportAs_t, ImportFrom_t, FromImport_t, ImportGlobal_t> content;
238 AST_MEMBER(Import, &content) 245 AST_MEMBER(Import, &content)
239AST_END(Import) 246AST_END(Import)
240 247
@@ -273,6 +280,8 @@ AST_NODE(ExpList)
273 ast_ptr<true, Seperator_t> sep; 280 ast_ptr<true, Seperator_t> sep;
274 ast_list<true, Exp_t> exprs; 281 ast_list<true, Exp_t> exprs;
275 AST_MEMBER(ExpList, &sep, &exprs) 282 AST_MEMBER(ExpList, &sep, &exprs)
283 bool followStmtProcessed = false;
284 Statement_t* followStmt = nullptr;
276AST_END(ExpList) 285AST_END(ExpList)
277 286
278AST_NODE(Return) 287AST_NODE(Return)
@@ -285,9 +294,9 @@ AST_END(Return)
285AST_NODE(With) 294AST_NODE(With)
286 ast_ptr<false, ExistentialOp_t> eop; 295 ast_ptr<false, ExistentialOp_t> eop;
287 ast_ptr<true, ExpList_t> valueList; 296 ast_ptr<true, ExpList_t> valueList;
288 ast_ptr<false, Assign_t> assigns; 297 ast_ptr<false, Assign_t> assign;
289 ast_sel<true, Block_t, Statement_t> body; 298 ast_sel<true, Block_t, Statement_t> body;
290 AST_MEMBER(With, &eop, &valueList, &assigns, &body) 299 AST_MEMBER(With, &eop, &valueList, &assign, &body)
291AST_END(With) 300AST_END(With)
292 301
293AST_NODE(SwitchList) 302AST_NODE(SwitchList)
@@ -302,20 +311,21 @@ AST_NODE(SwitchCase)
302 AST_MEMBER(SwitchCase, &condition, &body) 311 AST_MEMBER(SwitchCase, &condition, &body)
303AST_END(SwitchCase) 312AST_END(SwitchCase)
304 313
314AST_NODE(Assignment)
315 ast_ptr<false, ExpList_t> expList;
316 ast_ptr<true, Assign_t> assign;
317 AST_MEMBER(Assignment, &expList, &assign)
318AST_END(Assignment)
319
305AST_NODE(Switch) 320AST_NODE(Switch)
306 ast_ptr<true, Exp_t> target; 321 ast_ptr<true, Exp_t> target;
322 ast_ptr<false, Assignment_t> assignment;
307 ast_ptr<true, Seperator_t> sep; 323 ast_ptr<true, Seperator_t> sep;
308 ast_list<true, SwitchCase_t> branches; 324 ast_list<true, SwitchCase_t> branches;
309 ast_sel<false, Block_t, Statement_t> lastBranch; 325 ast_sel<false, Block_t, Statement_t> lastBranch;
310 AST_MEMBER(Switch, &target, &sep, &branches, &lastBranch) 326 AST_MEMBER(Switch, &target, &assignment, &sep, &branches, &lastBranch)
311AST_END(Switch) 327AST_END(Switch)
312 328
313AST_NODE(Assignment)
314 ast_ptr<false, ExpList_t> expList;
315 ast_ptr<true, Assign_t> assign;
316 AST_MEMBER(Assignment, &expList, &assign)
317AST_END(Assignment)
318
319AST_NODE(IfCond) 329AST_NODE(IfCond)
320 ast_ptr<true, Exp_t> condition; 330 ast_ptr<true, Exp_t> condition;
321 ast_ptr<false, Assignment_t> assignment; 331 ast_ptr<false, Assignment_t> assignment;
@@ -343,7 +353,7 @@ AST_NODE(While)
343AST_END(While) 353AST_END(While)
344 354
345AST_NODE(Repeat) 355AST_NODE(Repeat)
346 ast_ptr<true, Body_t> body; 356 ast_sel<true, Block_t, Statement_t> body;
347 ast_ptr<true, Exp_t> condition; 357 ast_ptr<true, Exp_t> condition;
348 AST_MEMBER(Repeat, &body, &condition) 358 AST_MEMBER(Repeat, &body, &condition)
349AST_END(Repeat) 359AST_END(Repeat)
@@ -381,9 +391,10 @@ AST_NODE(CatchBlock)
381AST_END(CatchBlock) 391AST_END(CatchBlock)
382 392
383AST_NODE(Try) 393AST_NODE(Try)
394 ast_ptr<false, ExistentialOp_t> eop;
384 ast_sel<true, Block_t, Exp_t> func; 395 ast_sel<true, Block_t, Exp_t> func;
385 ast_ptr<false, CatchBlock_t> catchBlock; 396 ast_ptr<false, CatchBlock_t> catchBlock;
386 AST_MEMBER(Try, &func, &catchBlock) 397 AST_MEMBER(Try, &eop, &func, &catchBlock)
387AST_END(Try) 398AST_END(Try)
388 399
389AST_NODE(Comprehension) 400AST_NODE(Comprehension)
@@ -547,8 +558,8 @@ AST_NODE(SimpleValue)
547 ast_sel<true, 558 ast_sel<true,
548 TableLit_t, ConstValue_t, 559 TableLit_t, ConstValue_t,
549 If_t, Switch_t, With_t, ClassDecl_t, 560 If_t, Switch_t, With_t, ClassDecl_t,
550 ForEach_t, For_t, While_t, Do_t, Try_t, 561 ForEach_t, For_t, While_t, Repeat_t,
551 UnaryValue_t, 562 Do_t, Try_t, UnaryValue_t,
552 TblComprehension_t, Comprehension_t, 563 TblComprehension_t, Comprehension_t,
553 FunLit_t, Num_t, VarArg_t> value; 564 FunLit_t, Num_t, VarArg_t> value;
554 AST_MEMBER(SimpleValue, &value) 565 AST_MEMBER(SimpleValue, &value)
@@ -587,8 +598,31 @@ AST_NODE(DoubleString)
587 AST_MEMBER(DoubleString, &sep, &segments) 598 AST_MEMBER(DoubleString, &sep, &segments)
588AST_END(DoubleString) 599AST_END(DoubleString)
589 600
601AST_LEAF(YAMLIndent)
602AST_END(YAMLIndent)
603
604AST_LEAF(YAMLLineInner)
605AST_END(YAMLLineInner)
606
607AST_NODE(YAMLLineContent)
608 ast_sel<true, YAMLLineInner_t, Exp_t> content;
609 AST_MEMBER(YAMLLineContent, &content)
610AST_END(YAMLLineContent)
611
612AST_NODE(YAMLLine)
613 ast_ptr<true, YAMLIndent_t> indent;
614 ast_list<true, YAMLLineContent_t> segments;
615 AST_MEMBER(YAMLLine, &indent, &segments)
616AST_END(YAMLLine)
617
618AST_NODE(YAMLMultiline)
619 ast_ptr<true, Seperator_t> sep;
620 ast_list<true, YAMLLine_t> lines;
621 AST_MEMBER(YAMLMultiline, &sep, &lines)
622AST_END(YAMLMultiline)
623
590AST_NODE(String) 624AST_NODE(String)
591 ast_sel<true, DoubleString_t, SingleString_t, LuaString_t> str; 625 ast_sel<true, DoubleString_t, SingleString_t, LuaString_t, YAMLMultiline_t> str;
592 AST_MEMBER(String, &str) 626 AST_MEMBER(String, &str)
593AST_END(String) 627AST_END(String)
594 628
@@ -620,6 +654,7 @@ AST_END(Slice)
620 654
621AST_NODE(Parens) 655AST_NODE(Parens)
622 ast_ptr<true, Exp_t> expr; 656 ast_ptr<true, Exp_t> expr;
657 bool extra = false;
623 AST_MEMBER(Parens, &expr) 658 AST_MEMBER(Parens, &expr)
624AST_END(Parens) 659AST_END(Parens)
625 660
@@ -638,9 +673,14 @@ AST_END(TableAppendingOp)
638AST_LEAF(PlainItem) 673AST_LEAF(PlainItem)
639AST_END(PlainItem) 674AST_END(PlainItem)
640 675
676AST_NODE(ReversedIndex)
677 ast_ptr<false, Exp_t> modifier;
678 AST_MEMBER(ReversedIndex, &modifier)
679AST_END(ReversedIndex)
680
641AST_NODE(ChainValue) 681AST_NODE(ChainValue)
642 ast_ptr<true, Seperator_t> sep; 682 ast_ptr<true, Seperator_t> sep;
643 ast_sel_list<true, Callable_t, Invoke_t, DotChainItem_t, ColonChainItem_t, Slice_t, Exp_t, String_t, InvokeArgs_t, ExistentialOp_t, TableAppendingOp_t, 683 ast_sel_list<true, Callable_t, Invoke_t, DotChainItem_t, ColonChainItem_t, Slice_t, Exp_t, String_t, InvokeArgs_t, ExistentialOp_t, TableAppendingOp_t, ReversedIndex_t,
644 /*non-syntax-rule*/ PlainItem_t> items; 684 /*non-syntax-rule*/ PlainItem_t> items;
645 AST_MEMBER(ChainValue, &sep, &items) 685 AST_MEMBER(ChainValue, &sep, &items)
646AST_END(ChainValue) 686AST_END(ChainValue)
@@ -724,8 +764,9 @@ AST_LEAF(GlobalOp)
724AST_END(GlobalOp) 764AST_END(GlobalOp)
725 765
726AST_NODE(Global) 766AST_NODE(Global)
767 ast_ptr<false, ConstAttrib_t> constAttrib;
727 ast_sel<true, ClassDecl_t, GlobalOp_t, GlobalValues_t> item; 768 ast_sel<true, ClassDecl_t, GlobalOp_t, GlobalValues_t> item;
728 AST_MEMBER(Global, &item) 769 AST_MEMBER(Global, &constAttrib, &item)
729AST_END(Global) 770AST_END(Global)
730 771
731AST_LEAF(ExportDefault) 772AST_LEAF(ExportDefault)
@@ -741,15 +782,17 @@ AST_END(Export)
741AST_NODE(FnArgDef) 782AST_NODE(FnArgDef)
742 ast_sel<true, Variable_t, SelfItem_t> name; 783 ast_sel<true, Variable_t, SelfItem_t> name;
743 ast_ptr<false, ExistentialOp_t> op; 784 ast_ptr<false, ExistentialOp_t> op;
785 ast_ptr<false, Name_t> label;
744 ast_ptr<false, Exp_t> defaultValue; 786 ast_ptr<false, Exp_t> defaultValue;
745 AST_MEMBER(FnArgDef, &name, &op, &defaultValue) 787 AST_MEMBER(FnArgDef, &name, &op, &label, &defaultValue)
746AST_END(FnArgDef) 788AST_END(FnArgDef)
747 789
748AST_NODE(FnArgDefList) 790AST_NODE(FnArgDefList)
749 ast_ptr<true, Seperator_t> sep; 791 ast_ptr<true, Seperator_t> sep;
750 ast_list<false, FnArgDef_t> definitions; 792 ast_list<false, FnArgDef_t> definitions;
751 ast_ptr<false, VarArg_t> varArg; 793 ast_ptr<false, VarArg_t> varArg;
752 AST_MEMBER(FnArgDefList, &sep, &definitions, &varArg) 794 ast_ptr<false, Name_t> label;
795 AST_MEMBER(FnArgDefList, &sep, &definitions, &varArg, &label)
753AST_END(FnArgDefList) 796AST_END(FnArgDefList)
754 797
755AST_NODE(OuterVarShadow) 798AST_NODE(OuterVarShadow)
@@ -837,9 +880,15 @@ AST_NODE(UnaryExp)
837 AST_MEMBER(UnaryExp, &ops, &expos, &inExp) 880 AST_MEMBER(UnaryExp, &ops, &expos, &inExp)
838AST_END(UnaryExp) 881AST_END(UnaryExp)
839 882
883AST_NODE(SubBackcall)
884 ast_ptr<true, FnArrowBack_t> arrow;
885 ast_ptr<true, ChainValue_t> value;
886 AST_MEMBER(SubBackcall, &arrow, &value)
887AST_END(SubBackcall)
888
840AST_NODE(ExpListAssign) 889AST_NODE(ExpListAssign)
841 ast_ptr<true, ExpList_t> expList; 890 ast_ptr<true, ExpList_t> expList;
842 ast_sel<false, Update_t, Assign_t> action; 891 ast_sel<false, Update_t, Assign_t, SubBackcall_t> action;
843 AST_MEMBER(ExpListAssign, &expList, &action) 892 AST_MEMBER(ExpListAssign, &expList, &action)
844AST_END(ExpListAssign) 893AST_END(ExpListAssign)
845 894
@@ -855,7 +904,17 @@ AST_NODE(WhileLine)
855 AST_MEMBER(WhileLine, &type, &condition) 904 AST_MEMBER(WhileLine, &type, &condition)
856AST_END(WhileLine) 905AST_END(WhileLine)
857 906
858AST_LEAF(BreakLoop) 907AST_LEAF(Break)
908AST_END(Break)
909
910AST_LEAF(Continue)
911AST_END(Continue)
912
913AST_NODE(BreakLoop)
914 ast_sel<true, Break_t, Continue_t> type;
915 ast_ptr<false, Exp_t> value;
916 AST_MEMBER(BreakLoop, &type, &value)
917 std::string varBWV;
859AST_END(BreakLoop) 918AST_END(BreakLoop)
860 919
861AST_NODE(PipeBody) 920AST_NODE(PipeBody)
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp
index a2a1864..d676750 100644
--- a/src/yuescript/yue_compiler.cpp
+++ b/src/yuescript/yue_compiler.cpp
@@ -7,9 +7,12 @@ The above copyright notice and this permission notice shall be included in all c
7THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ 7THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
8 8
9#include <chrono> 9#include <chrono>
10#include <cmath>
11#include <iomanip>
10#include <memory> 12#include <memory>
11#include <optional> 13#include <optional>
12#include <set> 14#include <set>
15#include <sstream>
13#include <stack> 16#include <stack>
14#include <string> 17#include <string>
15#include <unordered_map> 18#include <unordered_map>
@@ -75,7 +78,7 @@ static std::unordered_set<std::string> Metamethods = {
75 "close"s // Lua 5.4 78 "close"s // Lua 5.4
76}; 79};
77 80
78const std::string_view version = "0.27.3"sv; 81const std::string_view version = "0.29.3"sv;
79const std::string_view extension = "yue"sv; 82const std::string_view extension = "yue"sv;
80 83
81class CompileError : public std::logic_error { 84class CompileError : public std::logic_error {
@@ -162,12 +165,12 @@ public:
162 double compileTime = 0.0; 165 double compileTime = 0.0;
163 if (config.profiling) { 166 if (config.profiling) {
164 auto start = std::chrono::high_resolution_clock::now(); 167 auto start = std::chrono::high_resolution_clock::now();
165 _info = _parser.parse<File_t>(codes); 168 _info = _parser.parse<File_t>(codes, config.lax);
166 auto stop = std::chrono::high_resolution_clock::now(); 169 auto stop = std::chrono::high_resolution_clock::now();
167 std::chrono::duration<double> diff = stop - start; 170 std::chrono::duration<double> diff = stop - start;
168 parseTime = diff.count(); 171 parseTime = diff.count();
169 } else { 172 } else {
170 _info = _parser.parse<File_t>(codes); 173 _info = _parser.parse<File_t>(codes, config.lax);
171 } 174 }
172 std::unique_ptr<GlobalVars> globals; 175 std::unique_ptr<GlobalVars> globals;
173 std::unique_ptr<Options> options; 176 std::unique_ptr<Options> options;
@@ -426,8 +429,9 @@ private:
426 }; 429 };
427 enum class VarType { 430 enum class VarType {
428 Local = 0, 431 Local = 0,
429 Const = 1, 432 LocalConst = 1,
430 Global = 2 433 Global = 2,
434 GlobalConst = 3
431 }; 435 };
432 struct Scope { 436 struct Scope {
433 GlobalMode mode = GlobalMode::None; 437 GlobalMode mode = GlobalMode::None;
@@ -555,7 +559,7 @@ private:
555 for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { 559 for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) {
556 auto vars = it->vars.get(); 560 auto vars = it->vars.get();
557 auto vit = vars->find(name); 561 auto vit = vars->find(name);
558 if (vit != vars->end() && vit->second != VarType::Global) { 562 if (vit != vars->end() && (vit->second == VarType::Local || vit->second == VarType::LocalConst)) {
559 local = true; 563 local = true;
560 break; 564 break;
561 } 565 }
@@ -568,7 +572,7 @@ private:
568 for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { 572 for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) {
569 auto vars = it->vars.get(); 573 auto vars = it->vars.get();
570 auto vit = vars->find(name); 574 auto vit = vars->find(name);
571 if (vit != vars->end() && vit->second == VarType::Global) { 575 if (vit != vars->end() && (vit->second == VarType::Global || vit->second == VarType::GlobalConst)) {
572 global = true; 576 global = true;
573 break; 577 break;
574 } 578 }
@@ -590,7 +594,7 @@ private:
590 auto vars = it->vars.get(); 594 auto vars = it->vars.get();
591 auto vit = vars->find(name); 595 auto vit = vars->find(name);
592 if (vit != vars->end()) { 596 if (vit != vars->end()) {
593 isConst = (vit->second == VarType::Const); 597 isConst = (vit->second == VarType::LocalConst || vit->second == VarType::GlobalConst);
594 break; 598 break;
595 } 599 }
596 if (checkShadowScopeOnly && it->allows) break; 600 if (checkShadowScopeOnly && it->allows) break;
@@ -871,9 +875,9 @@ private:
871 return false; 875 return false;
872 } 876 }
873 877
874 void markVarConst(const std::string& name) { 878 void markVarLocalConst(const std::string& name) {
875 auto& scope = _scopes.back(); 879 auto& scope = _scopes.back();
876 scope.vars->insert_or_assign(name, VarType::Const); 880 scope.vars->insert_or_assign(name, VarType::LocalConst);
877 } 881 }
878 882
879 void markVarShadowed() { 883 void markVarShadowed() {
@@ -892,6 +896,11 @@ private:
892 scope.vars->insert_or_assign(name, VarType::Global); 896 scope.vars->insert_or_assign(name, VarType::Global);
893 } 897 }
894 898
899 void markVarGlobalConst(const std::string& name) {
900 auto& scope = _scopes.back();
901 scope.vars->insert_or_assign(name, VarType::GlobalConst);
902 }
903
895 void addToAllowList(const std::string& name) { 904 void addToAllowList(const std::string& name) {
896 auto& scope = _scopes.back(); 905 auto& scope = _scopes.back();
897 scope.allows->insert(name); 906 scope.allows->insert(name);
@@ -1080,8 +1089,8 @@ private:
1080 if (unary->ops.empty()) { 1089 if (unary->ops.empty()) {
1081 Value_t* value = static_cast<Value_t*>(unary->expos.back()); 1090 Value_t* value = static_cast<Value_t*>(unary->expos.back());
1082 if (auto chain = ast_cast<ChainValue_t>(value->item); chain && chain->items.size() == 1) { 1091 if (auto chain = ast_cast<ChainValue_t>(value->item); chain && chain->items.size() == 1) {
1083 if (auto exp = chain->get_by_path<Callable_t, Parens_t, Exp_t>()) { 1092 if (auto parens = chain->get_by_path<Callable_t, Parens_t>(); parens && parens->extra) {
1084 if (auto insideValue = singleValueFrom(exp)) { 1093 if (auto insideValue = singleValueFrom(parens->expr)) {
1085 return insideValue; 1094 return insideValue;
1086 } 1095 }
1087 } 1096 }
@@ -1249,7 +1258,7 @@ private:
1249 1258
1250 template <class T> 1259 template <class T>
1251 ast_ptr<false, T> toAst(std::string_view codes, ast_node* parent) { 1260 ast_ptr<false, T> toAst(std::string_view codes, ast_node* parent) {
1252 auto res = _parser.parse<T>(std::string(codes)); 1261 auto res = _parser.parse<T>(std::string(codes), false);
1253 if (res.error) { 1262 if (res.error) {
1254 throw CompileError(res.error.value().msg, parent); 1263 throw CompileError(res.error.value().msg, parent);
1255 } 1264 }
@@ -1272,6 +1281,8 @@ private:
1272 Common, 1281 Common,
1273 EndWithColon, 1282 EndWithColon,
1274 EndWithEOP, 1283 EndWithEOP,
1284 EndWithSlice,
1285 HasRIndex,
1275 HasEOP, 1286 HasEOP,
1276 HasKeyword, 1287 HasKeyword,
1277 HasUnicode, 1288 HasUnicode,
@@ -1290,6 +1301,9 @@ private:
1290 if (ast_is<ExistentialOp_t>(chainValue->items.back())) { 1301 if (ast_is<ExistentialOp_t>(chainValue->items.back())) {
1291 return ChainType::EndWithEOP; 1302 return ChainType::EndWithEOP;
1292 } 1303 }
1304 if (ast_is<Slice_t>(chainValue->items.back())) {
1305 return ChainType::EndWithSlice;
1306 }
1293 if (auto dot = ast_cast<DotChainItem_t>(chainValue->items.back())) { 1307 if (auto dot = ast_cast<DotChainItem_t>(chainValue->items.back())) {
1294 if (dot->name.is<Metatable_t>()) { 1308 if (dot->name.is<Metatable_t>()) {
1295 return ChainType::Metatable; 1309 return ChainType::Metatable;
@@ -1315,6 +1329,8 @@ private:
1315 } 1329 }
1316 } else if (ast_is<ExistentialOp_t>(item)) { 1330 } else if (ast_is<ExistentialOp_t>(item)) {
1317 return ChainType::HasEOP; 1331 return ChainType::HasEOP;
1332 } else if (ast_is<ReversedIndex_t>(item)) {
1333 return ChainType::HasRIndex;
1318 } 1334 }
1319 } 1335 }
1320 return type; 1336 return type;
@@ -1837,6 +1853,7 @@ private:
1837 case id<ForEach_t>(): transformForEach(static_cast<ForEach_t*>(value), out); break; 1853 case id<ForEach_t>(): transformForEach(static_cast<ForEach_t*>(value), out); break;
1838 case id<For_t>(): transformFor(static_cast<For_t*>(value), out); break; 1854 case id<For_t>(): transformFor(static_cast<For_t*>(value), out); break;
1839 case id<While_t>(): transformWhile(static_cast<While_t*>(value), out); break; 1855 case id<While_t>(): transformWhile(static_cast<While_t*>(value), out); break;
1856 case id<Repeat_t>(): transformRepeat(static_cast<Repeat_t*>(value), out); break;
1840 case id<Do_t>(): transformDo(static_cast<Do_t*>(value), out, ExpUsage::Common); break; 1857 case id<Do_t>(): transformDo(static_cast<Do_t*>(value), out, ExpUsage::Common); break;
1841 case id<Try_t>(): transformTry(static_cast<Try_t*>(value), out, ExpUsage::Common); break; 1858 case id<Try_t>(): transformTry(static_cast<Try_t*>(value), out, ExpUsage::Common); break;
1842 case id<Comprehension_t>(): { 1859 case id<Comprehension_t>(): {
@@ -2048,13 +2065,16 @@ private:
2048 if (item.targetVar.empty()) { 2065 if (item.targetVar.empty()) {
2049 throw CompileError("can only declare variable as const"sv, item.target); 2066 throw CompileError("can only declare variable as const"sv, item.target);
2050 } 2067 }
2051 markVarConst(item.targetVar); 2068 markVarLocalConst(item.targetVar);
2052 } 2069 }
2053 } 2070 }
2054 } 2071 }
2055 } 2072 }
2056 2073
2057 bool transformAssignment(ExpListAssign_t* assignment, str_list& out, bool optionalDestruct = false) { 2074 bool transformAssignment(ExpListAssign_t* assignment, str_list& out, bool optionalDestruct = false) {
2075 if (assignment->action.is<SubBackcall_t>()) {
2076 YUEE("AST node mismatch", assignment->action);
2077 }
2058 checkAssignable(assignment->expList); 2078 checkAssignable(assignment->expList);
2059 BLOCK_START 2079 BLOCK_START
2060 auto assign = ast_cast<Assign_t>(assignment->action); 2080 auto assign = ast_cast<Assign_t>(assignment->action);
@@ -2317,6 +2337,17 @@ private:
2317 out.back().insert(0, preDefine); 2337 out.back().insert(0, preDefine);
2318 return false; 2338 return false;
2319 } 2339 }
2340 case id<Try_t>(): {
2341 auto tryNode = static_cast<Try_t*>(value);
2342 if (tryNode->eop) {
2343 auto assignList = assignment->expList.get();
2344 std::string preDefine = getPreDefineLine(assignment);
2345 transformTry(tryNode, out, ExpUsage::Assignment, assignList);
2346 out.back().insert(0, preDefine);
2347 return false;
2348 }
2349 break;
2350 }
2320 case id<Switch_t>(): { 2351 case id<Switch_t>(): {
2321 auto switchNode = static_cast<Switch_t*>(value); 2352 auto switchNode = static_cast<Switch_t*>(value);
2322 auto assignList = assignment->expList.get(); 2353 auto assignList = assignment->expList.get();
@@ -2388,6 +2419,13 @@ private:
2388 out.back().insert(0, preDefine); 2419 out.back().insert(0, preDefine);
2389 return false; 2420 return false;
2390 } 2421 }
2422 case id<Repeat_t>(): {
2423 auto expList = assignment->expList.get();
2424 std::string preDefine = getPreDefineLine(assignment);
2425 transformRepeatInPlace(static_cast<Repeat_t*>(value), out, expList);
2426 out.back().insert(0, preDefine);
2427 return false;
2428 }
2391 case id<TableLit_t>(): { 2429 case id<TableLit_t>(): {
2392 auto tableLit = static_cast<TableLit_t*>(value); 2430 auto tableLit = static_cast<TableLit_t*>(value);
2393 if (hasSpreadExp(tableLit->values.objects())) { 2431 if (hasSpreadExp(tableLit->values.objects())) {
@@ -2440,12 +2478,14 @@ private:
2440 switch (type) { 2478 switch (type) {
2441 case ChainType::HasEOP: 2479 case ChainType::HasEOP:
2442 case ChainType::EndWithColon: 2480 case ChainType::EndWithColon:
2481 case ChainType::EndWithSlice:
2443 case ChainType::MetaFieldInvocation: { 2482 case ChainType::MetaFieldInvocation: {
2444 std::string preDefine = getPreDefineLine(assignment); 2483 std::string preDefine = getPreDefineLine(assignment);
2445 transformChainValue(chainValue, out, ExpUsage::Assignment, expList, false, optionalDestruct); 2484 transformChainValue(chainValue, out, ExpUsage::Assignment, expList, false, optionalDestruct);
2446 out.back().insert(0, preDefine); 2485 out.back().insert(0, preDefine);
2447 return false; 2486 return false;
2448 } 2487 }
2488 case ChainType::HasRIndex:
2449 case ChainType::HasKeyword: 2489 case ChainType::HasKeyword:
2450 case ChainType::HasUnicode: 2490 case ChainType::HasUnicode:
2451 case ChainType::Macro: 2491 case ChainType::Macro:
@@ -2461,6 +2501,10 @@ private:
2461 auto info = extractDestructureInfo(assignment, false, optionalDestruct); 2501 auto info = extractDestructureInfo(assignment, false, optionalDestruct);
2462 if (info.destructures.empty()) { 2502 if (info.destructures.empty()) {
2463 transformAssignmentCommon(assignment, out); 2503 transformAssignmentCommon(assignment, out);
2504 if (assignment->expList->followStmt) {
2505 transformStatement(assignment->expList->followStmt, out);
2506 assignment->expList->followStmtProcessed = true;
2507 }
2464 return true; 2508 return true;
2465 } else { 2509 } else {
2466 auto x = assignment; 2510 auto x = assignment;
@@ -2726,8 +2770,12 @@ private:
2726 temp.push_back(indent() + "end"s + nlr(x)); 2770 temp.push_back(indent() + "end"s + nlr(x));
2727 } 2771 }
2728 out.push_back(join(temp)); 2772 out.push_back(join(temp));
2773 if (assignment->expList->followStmt) {
2774 transformStatement(assignment->expList->followStmt, out);
2775 assignment->expList->followStmtProcessed = true;
2776 }
2777 return false;
2729 } 2778 }
2730 return false;
2731 } 2779 }
2732 2780
2733 void transformAssignItem(ast_node* value, str_list& out) { 2781 void transformAssignItem(ast_node* value, str_list& out) {
@@ -2793,20 +2841,46 @@ private:
2793 if (!tableItems) throw CompileError("invalid destructure value"sv, node); 2841 if (!tableItems) throw CompileError("invalid destructure value"sv, node);
2794 std::list<DestructItem> pairs; 2842 std::list<DestructItem> pairs;
2795 int index = 0; 2843 int index = 0;
2844 int count = 0;
2845 bool hasSpread = false;
2796 auto subMetaDestruct = node->new_ptr<TableLit_t>(); 2846 auto subMetaDestruct = node->new_ptr<TableLit_t>();
2797 for (auto pair : *tableItems) { 2847 for (auto pair : *tableItems) {
2798 switch (pair->get_id()) { 2848 switch (pair->get_id()) {
2799 case id<Exp_t>(): 2849 case id<Exp_t>():
2800 case id<NormalDef_t>(): { 2850 case id<NormalDef_t>(): {
2851 ++index;
2801 Exp_t* defVal = nullptr; 2852 Exp_t* defVal = nullptr;
2802 if (auto nd = ast_cast<NormalDef_t>(pair)) { 2853 if (auto nd = ast_cast<NormalDef_t>(pair)) {
2803 pair = nd->item.get(); 2854 pair = nd->item.get();
2804 defVal = nd->defVal.get(); 2855 defVal = nd->defVal.get();
2805 } 2856 }
2806 ++index; 2857 bool assignable = false;
2807 if (!varDefOnly && !isAssignable(static_cast<Exp_t*>(pair))) { 2858 try {
2859 assignable = isAssignable(static_cast<Exp_t*>(pair));
2860 } catch (const CompileError& e) {
2861 if (!varDefOnly) throw e;
2862 }
2863 if (!assignable && !varDefOnly) {
2864 if (optional) break;
2808 throw CompileError("can't destructure value"sv, pair); 2865 throw CompileError("can't destructure value"sv, pair);
2809 } 2866 }
2867 ast_ptr<true, ast_node> indexItem;
2868 if (hasSpread) {
2869 int rIndex = count - index;
2870 indexItem.set(toAst<ReversedIndex_t>('#' + (rIndex == 0 ? Empty : "-"s + std::to_string(rIndex)), pair));
2871 } else {
2872 indexItem.set(toAst<Exp_t>(std::to_string(index), pair));
2873 }
2874 if (optional && varDefOnly && !assignable) {
2875 if (defVal) {
2876 throw CompileError("default value is not supported here"sv, defVal);
2877 }
2878 auto exp = static_cast<Exp_t*>(pair);
2879 auto chain = exp->new_ptr<ChainValue_t>();
2880 chain->items.push_back(indexItem);
2881 pairs.push_back({exp, Empty, chain, nullptr});
2882 break;
2883 }
2810 auto value = singleValueFrom(pair); 2884 auto value = singleValueFrom(pair);
2811 auto item = value->item.get(); 2885 auto item = value->item.get();
2812 ast_node* subExp = ast_cast<SimpleTable_t>(item); 2886 ast_node* subExp = ast_cast<SimpleTable_t>(item);
@@ -2817,7 +2891,6 @@ private:
2817 throw CompileError("default value is not supported here"sv, defVal); 2891 throw CompileError("default value is not supported here"sv, defVal);
2818 } 2892 }
2819 } 2893 }
2820 auto indexItem = toAst<Exp_t>(std::to_string(index), value);
2821 for (auto& p : subPairs) { 2894 for (auto& p : subPairs) {
2822 if (sep) p.structure->items.push_front(sep); 2895 if (sep) p.structure->items.push_front(sep);
2823 p.structure->items.push_front(indexItem); 2896 p.structure->items.push_front(indexItem);
@@ -2828,7 +2901,6 @@ private:
2828 auto varName = singleVariableFrom(exp, AccessType::None); 2901 auto varName = singleVariableFrom(exp, AccessType::None);
2829 if (varName == "_"sv) break; 2902 if (varName == "_"sv) break;
2830 auto chain = exp->new_ptr<ChainValue_t>(); 2903 auto chain = exp->new_ptr<ChainValue_t>();
2831 auto indexItem = toAst<Exp_t>(std::to_string(index), exp);
2832 chain->items.push_back(indexItem); 2904 chain->items.push_back(indexItem);
2833 pairs.push_back({exp, 2905 pairs.push_back({exp,
2834 varName, 2906 varName,
@@ -2886,7 +2958,25 @@ private:
2886 } 2958 }
2887 } 2959 }
2888 if (auto exp = np->value.as<Exp_t>()) { 2960 if (auto exp = np->value.as<Exp_t>()) {
2889 if (!varDefOnly && !isAssignable(exp)) throw CompileError("can't do destructure value"sv, exp); 2961 bool assignable = false;
2962 try {
2963 assignable = isAssignable(exp);
2964 } catch (const CompileError& e) {
2965 if (!varDefOnly) throw e;
2966 }
2967 if (!assignable && !varDefOnly) {
2968 if (optional) break;
2969 throw CompileError("can't destructure value"sv, pair);
2970 }
2971 if (optional && varDefOnly && !assignable) {
2972 if (defVal) {
2973 throw CompileError("default value is not supported here"sv, defVal);
2974 }
2975 auto chain = exp->new_ptr<ChainValue_t>();
2976 if (keyIndex) chain->items.push_back(keyIndex);
2977 pairs.push_back({exp, Empty, chain, nullptr});
2978 break;
2979 }
2890 auto item = singleValueFrom(exp)->item.get(); 2980 auto item = singleValueFrom(exp)->item.get();
2891 ast_node* subExp = ast_cast<SimpleTable_t>(item); 2981 ast_node* subExp = ast_cast<SimpleTable_t>(item);
2892 if (subExp || (subExp = item->get_by_path<TableLit_t>()) || (subExp = item->get_by_path<Comprehension_t>())) { 2982 if (subExp || (subExp = item->get_by_path<TableLit_t>()) || (subExp = item->get_by_path<Comprehension_t>())) {
@@ -2935,7 +3025,13 @@ private:
2935 auto tb = static_cast<TableBlockIndent_t*>(pair); 3025 auto tb = static_cast<TableBlockIndent_t*>(pair);
2936 ++index; 3026 ++index;
2937 auto subPairs = destructFromExp(tb, varDefOnly, optional); 3027 auto subPairs = destructFromExp(tb, varDefOnly, optional);
2938 auto indexItem = toAst<Exp_t>(std::to_string(index), tb); 3028 ast_ptr<true, ast_node> indexItem;
3029 if (hasSpread) {
3030 int rIndex = count - index;
3031 indexItem.set(toAst<ReversedIndex_t>('#' + (rIndex == 0 ? Empty : "-"s + std::to_string(rIndex)), tb));
3032 } else {
3033 indexItem.set(toAst<Exp_t>(std::to_string(index), tb));
3034 }
2939 for (auto& p : subPairs) { 3035 for (auto& p : subPairs) {
2940 if (sep) p.structure->items.push_front(sep); 3036 if (sep) p.structure->items.push_front(sep);
2941 p.structure->items.push_front(indexItem); 3037 p.structure->items.push_front(indexItem);
@@ -2992,6 +3088,42 @@ private:
2992 subMetaDestruct->values.push_back(newPairDef); 3088 subMetaDestruct->values.push_back(newPairDef);
2993 break; 3089 break;
2994 } 3090 }
3091 case id<SpreadListExp_t>():
3092 case id<SpreadExp_t>(): {
3093 ++index;
3094 if (hasSpread) {
3095 throw CompileError("duplicated spread expression"sv, pair);
3096 }
3097 hasSpread = true;
3098 for (auto item : *tableItems) {
3099 if (ast_is<
3100 SpreadListExp_t, SpreadExp_t,
3101 TableBlockIndent_t,
3102 Exp_t, NormalDef_t>(item)) {
3103 count++;
3104 }
3105 }
3106 Exp_t* exp = nullptr;
3107 if (auto se = ast_cast<SpreadExp_t>(pair)) {
3108 exp = se->exp.get();
3109 } else {
3110 exp = ast_to<SpreadListExp_t>(pair)->exp.get();
3111 }
3112 auto varName = singleVariableFrom(exp, AccessType::None);
3113 if (varName == "_"sv) break;
3114 int start = index;
3115 int stop = index - count - 1;
3116 auto chain = exp->new_ptr<ChainValue_t>();
3117 auto slice = toAst<Slice_t>(
3118 '[' + (start == 1 ? Empty : std::to_string(start)) + ',' + (stop == -1 ? Empty : std::to_string(stop)) + ']', exp);
3119 chain->items.push_back(slice);
3120 auto nil = toAst<Exp_t>("nil"sv, slice);
3121 pairs.push_back({exp,
3122 varName,
3123 chain,
3124 nil.get()});
3125 break;
3126 }
2995 default: YUEE("AST node mismatch", pair); break; 3127 default: YUEE("AST node mismatch", pair); break;
2996 } 3128 }
2997 } 3129 }
@@ -3108,7 +3240,11 @@ private:
3108 break; 3240 break;
3109 default: YUEE("AST node mismatch", destructNode); break; 3241 default: YUEE("AST node mismatch", destructNode); break;
3110 } 3242 }
3111 if (dlist->empty()) throw CompileError("expect items to be destructured"sv, destructNode); 3243 if (dlist->empty()) {
3244 if (!optional) {
3245 throw CompileError("expect items to be destructured"sv, destructNode);
3246 }
3247 }
3112 for (auto item : *dlist) { 3248 for (auto item : *dlist) {
3113 switch (item->get_id()) { 3249 switch (item->get_id()) {
3114 case id<MetaVariablePairDef_t>(): { 3250 case id<MetaVariablePairDef_t>(): {
@@ -3244,7 +3380,9 @@ private:
3244 simpleValue->value.set(tab); 3380 simpleValue->value.set(tab);
3245 auto pairs = destructFromExp(newExp(simpleValue, expr), varDefOnly, optional); 3381 auto pairs = destructFromExp(newExp(simpleValue, expr), varDefOnly, optional);
3246 if (pairs.empty()) { 3382 if (pairs.empty()) {
3247 throw CompileError("expect items to be destructured"sv, tab); 3383 if (!optional) {
3384 throw CompileError("expect items to be destructured"sv, tab);
3385 }
3248 } 3386 }
3249 destruct.items = std::move(pairs); 3387 destruct.items = std::move(pairs);
3250 if (!varDefOnly) { 3388 if (!varDefOnly) {
@@ -3262,6 +3400,7 @@ private:
3262 } else if (destruct.items.size() == 1 && !singleValueFrom(*j)) { 3400 } else if (destruct.items.size() == 1 && !singleValueFrom(*j)) {
3263 auto p = destruct.value.get(); 3401 auto p = destruct.value.get();
3264 auto parens = p->new_ptr<Parens_t>(); 3402 auto parens = p->new_ptr<Parens_t>();
3403 parens->extra = true;
3265 if (auto tableBlock = ast_cast<TableBlock_t>(p)) { 3404 if (auto tableBlock = ast_cast<TableBlock_t>(p)) {
3266 auto tableLit = p->new_ptr<TableLit_t>(); 3405 auto tableLit = p->new_ptr<TableLit_t>();
3267 tableLit->values.dup(tableBlock->values); 3406 tableLit->values.dup(tableBlock->values);
@@ -3282,7 +3421,7 @@ private:
3282 destruct.valueVar.clear(); 3421 destruct.valueVar.clear();
3283 } 3422 }
3284 } 3423 }
3285 destructs.push_back(destruct); 3424 destructs.push_back(std::move(destruct));
3286 } 3425 }
3287 } 3426 }
3288 } else { 3427 } else {
@@ -4201,12 +4340,22 @@ private:
4201 4340
4202 std::optional<std::pair<std::string, str_list>> upValueFuncFromExp(Exp_t* exp, str_list* ensureArgListInTheEnd, bool blockRewrite) { 4341 std::optional<std::pair<std::string, str_list>> upValueFuncFromExp(Exp_t* exp, str_list* ensureArgListInTheEnd, bool blockRewrite) {
4203 if (checkUpValueFuncAvailable(exp)) { 4342 if (checkUpValueFuncAvailable(exp)) {
4343 auto block = exp->new_ptr<Block_t>();
4344 if (auto sVal = simpleSingleValueFrom(exp)) {
4345 if (auto doNode = sVal->value.as<Do_t>()) {
4346 if (auto blk = doNode->body->content.as<Block_t>()) {
4347 block->statements.dup(blk->statements);
4348 } else {
4349 block->statements.push_back(doNode->body->content.to<Statement_t>());
4350 }
4351 return getUpValueFuncFromBlock(block, ensureArgListInTheEnd, false, blockRewrite);
4352 }
4353 }
4204 auto returnNode = exp->new_ptr<Return_t>(); 4354 auto returnNode = exp->new_ptr<Return_t>();
4205 returnNode->explicitReturn = false; 4355 returnNode->explicitReturn = false;
4206 auto returnList = exp->new_ptr<ExpListLow_t>(); 4356 auto returnList = exp->new_ptr<ExpListLow_t>();
4207 returnList->exprs.push_back(exp); 4357 returnList->exprs.push_back(exp);
4208 returnNode->valueList.set(returnList); 4358 returnNode->valueList.set(returnList);
4209 auto block = exp->new_ptr<Block_t>();
4210 auto stmt = exp->new_ptr<Statement_t>(); 4359 auto stmt = exp->new_ptr<Statement_t>();
4211 stmt->content.set(returnNode); 4360 stmt->content.set(returnNode);
4212 block->statements.push_back(stmt); 4361 block->statements.push_back(stmt);
@@ -4283,7 +4432,9 @@ private:
4283 return false; 4432 return false;
4284 }; 4433 };
4285 switch (usage) { 4434 switch (usage) {
4286 case ExpUsage::Common: YUEE("AST node mismatch", x); return; 4435 case ExpUsage::Common:
4436 YUEE("AST node mismatch", x);
4437 return;
4287 case ExpUsage::Return: 4438 case ExpUsage::Return:
4288 case ExpUsage::Closure: { 4439 case ExpUsage::Closure: {
4289 prepareValue(); 4440 prepareValue();
@@ -4406,6 +4557,7 @@ private:
4406 case id<ForEach_t>(): transformForEachClosure(static_cast<ForEach_t*>(value), out); break; 4557 case id<ForEach_t>(): transformForEachClosure(static_cast<ForEach_t*>(value), out); break;
4407 case id<For_t>(): transformForClosure(static_cast<For_t*>(value), out); break; 4558 case id<For_t>(): transformForClosure(static_cast<For_t*>(value), out); break;
4408 case id<While_t>(): transformWhileClosure(static_cast<While_t*>(value), out); break; 4559 case id<While_t>(): transformWhileClosure(static_cast<While_t*>(value), out); break;
4560 case id<Repeat_t>(): transformRepeatClosure(static_cast<Repeat_t*>(value), out); break;
4409 case id<Do_t>(): transformDo(static_cast<Do_t*>(value), out, ExpUsage::Closure); break; 4561 case id<Do_t>(): transformDo(static_cast<Do_t*>(value), out, ExpUsage::Closure); break;
4410 case id<Try_t>(): transformTry(static_cast<Try_t*>(value), out, ExpUsage::Closure); break; 4562 case id<Try_t>(): transformTry(static_cast<Try_t*>(value), out, ExpUsage::Closure); break;
4411 case id<UnaryValue_t>(): transformUnaryValue(static_cast<UnaryValue_t*>(value), out); break; 4563 case id<UnaryValue_t>(): transformUnaryValue(static_cast<UnaryValue_t*>(value), out); break;
@@ -4725,11 +4877,7 @@ private:
4725 auto newBody = x->new_ptr<Body_t>(); 4877 auto newBody = x->new_ptr<Body_t>();
4726 newBody->content.set(followingBlock); 4878 newBody->content.set(followingBlock);
4727 { 4879 {
4728 auto doNode = x->new_ptr<Do_t>(); 4880 if (auto result = upValueFuncFromBlock(followingBlock.get(), &argNames, false, true)) {
4729 doNode->body.set(newBody);
4730 auto simpleValue = x->new_ptr<SimpleValue_t>();
4731 simpleValue->value.set(doNode);
4732 if (auto result = upValueFuncFromExp(newExp(simpleValue, x), &argNames, true)) {
4733 auto [funcName, args] = std::move(*result); 4881 auto [funcName, args] = std::move(*result);
4734 str_list finalArgs; 4882 str_list finalArgs;
4735 for (const auto& arg : args) { 4883 for (const auto& arg : args) {
@@ -4737,9 +4885,13 @@ private:
4737 finalArgs.push_back(arg); 4885 finalArgs.push_back(arg);
4738 } 4886 }
4739 } 4887 }
4740 newBlock->statements.push_back(toAst<Statement_t>(funcName + ' ' + join(finalArgs, ","sv), x)); 4888 newBlock->statements.push_back(toAst<Statement_t>(funcName + ' ' + (finalArgs.empty() ? "nil"s : join(finalArgs, ","sv)), x));
4741 auto sVal = singleValueFrom(static_cast<Statement_t*>(newBlock->statements.back())->content.to<ExpListAssign_t>()->expList); 4889 auto sVal = singleValueFrom(static_cast<Statement_t*>(newBlock->statements.back())->content.to<ExpListAssign_t>()->expList);
4742 ast_to<InvokeArgs_t>(sVal->item.to<ChainValue_t>()->items.back())->args.dup(newInvoke->args); 4890 auto invokArgs = ast_to<InvokeArgs_t>(sVal->item.to<ChainValue_t>()->items.back());
4891 if (finalArgs.empty()) {
4892 invokArgs->args.clear();
4893 }
4894 invokArgs->args.dup(newInvoke->args);
4743 transformBlock(newBlock, out, usage, assignList, isRoot); 4895 transformBlock(newBlock, out, usage, assignList, isRoot);
4744 return; 4896 return;
4745 } 4897 }
@@ -4750,6 +4902,7 @@ private:
4750 newSimpleValue->value.set(funLit); 4902 newSimpleValue->value.set(funLit);
4751 auto newExpInParens = newExp(newSimpleValue, x); 4903 auto newExpInParens = newExp(newSimpleValue, x);
4752 auto newParens = x->new_ptr<Parens_t>(); 4904 auto newParens = x->new_ptr<Parens_t>();
4905 newParens->extra = true;
4753 newParens->expr.set(newExpInParens); 4906 newParens->expr.set(newExpInParens);
4754 auto newCallable = x->new_ptr<Callable_t>(); 4907 auto newCallable = x->new_ptr<Callable_t>();
4755 newCallable->item.set(newParens); 4908 newCallable->item.set(newParens);
@@ -4795,7 +4948,7 @@ private:
4795 auto varName = variableToString(ast_to<Variable_t>(var)); 4948 auto varName = variableToString(ast_to<Variable_t>(var));
4796 auto closeVar = getUnusedName("_close_"sv); 4949 auto closeVar = getUnusedName("_close_"sv);
4797 addToScope(closeVar); 4950 addToScope(closeVar);
4798 getCloses.push_back(closeVar + "=if type("s + varName + ") in ['table', 'userdata'] then assert "s + varName + ".<> and "s + varName +".<close>, \""s + "variable '"s + varName + "' got a non-closable value\" elseif "s + varName + " == nil then nil else error \""s + "variable '"s + varName + "' got a non-closable value\""); 4951 getCloses.push_back(closeVar + "=if type("s + varName + ") in ['table', 'userdata'] then assert "s + varName + ".<> and "s + varName + ".<close>, \""s + "variable '"s + varName + "' got a non-closable value\" elseif "s + varName + " == nil then nil else error \""s + "variable '"s + varName + "' got a non-closable value\"");
4799 doCloses.push_front(closeVar + "? "s + varName); 4952 doCloses.push_front(closeVar + "? "s + varName);
4800 } 4953 }
4801 popScope(); 4954 popScope();
@@ -4815,6 +4968,38 @@ private:
4815 newBlock->statements.push_back(toAst<Statement_t>("if "s + okVar + " then return ... else error ..."s, x)); 4968 newBlock->statements.push_back(toAst<Statement_t>("if "s + okVar + " then return ... else error ..."s, x));
4816 transformBlock(newBlock, out, usage, assignList, isRoot); 4969 transformBlock(newBlock, out, usage, assignList, isRoot);
4817 return; 4970 return;
4971 } else if (auto expListAssign = stmt->content.as<ExpListAssign_t>();
4972 expListAssign && expListAssign->action && expListAssign->action.is<SubBackcall_t>()) {
4973 auto x = *nodes.begin();
4974 auto newBlock = x->new_ptr<Block_t>();
4975 if (it != nodes.begin()) {
4976 for (auto i = nodes.begin(); i != it; ++i) {
4977 newBlock->statements.push_back(*i);
4978 }
4979 }
4980 auto doBackcall = static_cast<SubBackcall_t*>(expListAssign->action.get());
4981 auto backcall = expListAssign->new_ptr<Backcall_t>();
4982 auto argsDef = backcall->new_ptr<FnArgsDef_t>();
4983 try {
4984 auto defList = toAst<FnArgDefList_t>(YueFormat{}.toString(expListAssign->expList), expListAssign->expList);
4985 argsDef->defList.set(defList);
4986 } catch (const std::exception&) {
4987 throw CompileError("backcall syntax error", backcall);
4988 }
4989 backcall->argsDef.set(argsDef);
4990 backcall->arrow.set(doBackcall->arrow);
4991 backcall->value.set(doBackcall->value);
4992 auto newStmt = backcall->new_ptr<Statement_t>();
4993 newStmt->content.set(backcall);
4994 newStmt->comments.dup(stmt->comments);
4995 newStmt->appendix.set(stmt->appendix);
4996 newBlock->statements.push_back(newStmt);
4997 auto ait = it;
4998 for (auto i = ++ait; i != nodes.end(); ++i) {
4999 newBlock->statements.push_back(*i);
5000 }
5001 transformBlock(newBlock, out, usage, assignList, isRoot);
5002 return;
4818 } 5003 }
4819 if (auto local = stmt->content.as<Local_t>()) { 5004 if (auto local = stmt->content.as<Local_t>()) {
4820 if (!local->collected) { 5005 if (!local->collected) {
@@ -4986,36 +5171,45 @@ private:
4986 if (!nodes.empty()) { 5171 if (!nodes.empty()) {
4987 str_list temp; 5172 str_list temp;
4988 for (auto node : nodes) { 5173 for (auto node : nodes) {
4989 currentScope().lastStatement = (node == nodes.back()) && currentScope().mode == GlobalMode::None; 5174 auto transformNode = [&]() {
4990 transformStatement(static_cast<Statement_t*>(node), temp); 5175 currentScope().lastStatement = (node == nodes.back()) && currentScope().mode == GlobalMode::None;
4991 if (isRoot && !_rootDefs.empty()) { 5176 transformStatement(static_cast<Statement_t*>(node), temp);
4992 auto last = std::move(temp.back()); 5177 if (isRoot && !_rootDefs.empty()) {
4993 temp.pop_back(); 5178 auto last = std::move(temp.back());
4994 temp.insert(temp.end(), _rootDefs.begin(), _rootDefs.end()); 5179 temp.pop_back();
4995 _rootDefs.clear(); 5180 temp.insert(temp.end(), _rootDefs.begin(), _rootDefs.end());
4996 temp.push_back(std::move(last)); 5181 _rootDefs.clear();
4997 } 5182 temp.push_back(std::move(last));
4998 if (!temp.empty() && _parser.startWith<StatementSep_t>(temp.back())) { 5183 }
4999 auto rit = ++temp.rbegin(); 5184 if (!temp.empty() && _parser.startWith<StatementSep_t>(temp.back())) {
5000 if (rit != temp.rend() && !rit->empty()) { 5185 auto rit = ++temp.rbegin();
5001 auto index = std::string::npos; 5186 if (rit != temp.rend() && !rit->empty()) {
5002 if (_config.reserveLineNumber) { 5187 auto index = std::string::npos;
5003 index = rit->rfind(" -- "sv); 5188 if (_config.reserveLineNumber) {
5004 } else { 5189 index = rit->rfind(" -- "sv);
5005 index = rit->find_last_not_of('\n'); 5190 } else {
5006 if (index != std::string::npos) index++; 5191 index = rit->find_last_not_of('\n');
5007 } 5192 if (index != std::string::npos) index++;
5008 if (index != std::string::npos) {
5009 auto ending = rit->substr(0, index);
5010 auto ind = ending.find_last_of(" \t\n"sv);
5011 if (ind != std::string::npos) {
5012 ending = ending.substr(ind + 1);
5013 } 5193 }
5014 if (LuaKeywords.find(ending) == LuaKeywords.end()) { 5194 if (index != std::string::npos) {
5015 rit->insert(index, ";"sv); 5195 auto ending = rit->substr(0, index);
5196 auto ind = ending.find_last_of(" \t\n"sv);
5197 if (ind != std::string::npos) {
5198 ending = ending.substr(ind + 1);
5199 }
5200 if (LuaKeywords.find(ending) == LuaKeywords.end()) {
5201 rit->insert(index, ";"sv);
5202 }
5016 } 5203 }
5017 } 5204 }
5018 } 5205 }
5206 };
5207 if (_config.lax) {
5208 try {
5209 transformNode();
5210 } catch (const CompileError&) { }
5211 } else {
5212 transformNode();
5019 } 5213 }
5020 } 5214 }
5021 out.push_back(join(temp)); 5215 out.push_back(join(temp));
@@ -5224,18 +5418,29 @@ private:
5224 auto macroLit = macro->decl.to<MacroLit_t>(); 5418 auto macroLit = macro->decl.to<MacroLit_t>();
5225 auto argsDef = macroLit->argsDef.get(); 5419 auto argsDef = macroLit->argsDef.get();
5226 str_list newArgs; 5420 str_list newArgs;
5421 str_list argChecks;
5422 bool hasCheck = false;
5227 if (argsDef) { 5423 if (argsDef) {
5228 for (auto def_ : argsDef->definitions.objects()) { 5424 for (auto def_ : argsDef->definitions.objects()) {
5229 auto def = static_cast<FnArgDef_t*>(def_); 5425 auto def = static_cast<FnArgDef_t*>(def_);
5230 if (def->name.is<SelfItem_t>()) { 5426 if (def->name.is<SelfItem_t>()) {
5231 throw CompileError("self name is not supported for macro function argument"sv, def->name); 5427 throw CompileError("self name is not supported for macro function argument"sv, def->name);
5232 } else { 5428 } else {
5429 if (def->op) throw CompileError("invalid existence checking"sv, def->op);
5430 if (def->label) {
5431 hasCheck = true;
5432 const auto& astName = argChecks.emplace_back(_parser.toString(def->label));
5433 if (!_parser.hasAST(astName)) {
5434 throw CompileError("invalid AST name"sv, def->label);
5435 }
5436 } else {
5437 argChecks.emplace_back();
5438 }
5233 std::string defVal; 5439 std::string defVal;
5234 if (def->defaultValue) { 5440 if (def->defaultValue) {
5235 defVal = _parser.toString(def->defaultValue); 5441 defVal = _parser.toString(def->defaultValue);
5236 Utils::trim(defVal); 5442 Utils::trim(defVal);
5237 defVal.insert(0, "=[==========["sv); 5443 defVal = '=' + Utils::toLuaDoubleString(defVal);
5238 defVal.append("]==========]"sv);
5239 } 5444 }
5240 newArgs.emplace_back(_parser.toString(def->name) + defVal); 5445 newArgs.emplace_back(_parser.toString(def->name) + defVal);
5241 } 5446 }
@@ -5243,6 +5448,14 @@ private:
5243 if (argsDef->varArg) { 5448 if (argsDef->varArg) {
5244 newArgs.emplace_back(_parser.toString(argsDef->varArg)); 5449 newArgs.emplace_back(_parser.toString(argsDef->varArg));
5245 } 5450 }
5451 if (argsDef->label) {
5452 hasCheck = true;
5453 const auto& astName = _parser.toString(argsDef->label);
5454 if (!_parser.hasAST(astName)) {
5455 throw CompileError("invalid AST name"sv, argsDef->label);
5456 }
5457 argChecks.emplace_back("..."s + astName);
5458 }
5246 } 5459 }
5247 std::string macroCodes = "_ENV=require('yue').macro_env\n("s + join(newArgs, ","sv) + ")->"s + _parser.toString(macroLit->body); 5460 std::string macroCodes = "_ENV=require('yue').macro_env\n("s + join(newArgs, ","sv) + ")->"s + _parser.toString(macroLit->body);
5248 auto chunkName = "=(macro "s + macroName + ')'; 5461 auto chunkName = "=(macro "s + macroName + ')';
@@ -5273,6 +5486,24 @@ private:
5273 throw CompileError("failed to generate macro function\n"s + err, macroLit); 5486 throw CompileError("failed to generate macro function\n"s + err, macroLit);
5274 } // cur true macro 5487 } // cur true macro
5275 lua_remove(L, -2); // cur macro 5488 lua_remove(L, -2); // cur macro
5489 if (hasCheck) {
5490 lua_createtable(L, 0, 0); // cur macro checks
5491 int i = 1;
5492 for (const auto& check : argChecks) {
5493 if (check.empty()) {
5494 lua_pushboolean(L, 0);
5495 lua_rawseti(L, -2, i);
5496 } else {
5497 lua_pushlstring(L, check.c_str(), check.size());
5498 lua_rawseti(L, -2, i);
5499 }
5500 i++;
5501 }
5502 lua_createtable(L, 2, 0); // cur macro checks macrotab
5503 lua_insert(L, -3); // cur macrotab macro checks
5504 lua_rawseti(L, -3, 1); // macrotab[1] = checks, cur macrotab macro
5505 lua_rawseti(L, -2, 2); // macrotab[2] = macro, cur macrotab
5506 } // cur macro
5276 if (exporting && _config.exporting && !_config.module.empty()) { 5507 if (exporting && _config.exporting && !_config.module.empty()) {
5277 pushModuleTable(_config.module); // cur macro module 5508 pushModuleTable(_config.module); // cur macro module
5278 lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro module name 5509 lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro module name
@@ -5347,6 +5578,9 @@ private:
5347 case id<While_t>(): 5578 case id<While_t>():
5348 transformWhileInPlace(static_cast<While_t*>(value), out); 5579 transformWhileInPlace(static_cast<While_t*>(value), out);
5349 return; 5580 return;
5581 case id<Repeat_t>():
5582 transformRepeatInPlace(static_cast<Repeat_t*>(value), out);
5583 return;
5350 case id<For_t>(): 5584 case id<For_t>():
5351 transformForInPlace(static_cast<For_t*>(value), out); 5585 transformForInPlace(static_cast<For_t*>(value), out);
5352 return; 5586 return;
@@ -5427,7 +5661,11 @@ private:
5427 auto def = static_cast<FnArgDef_t*>(_def); 5661 auto def = static_cast<FnArgDef_t*>(_def);
5428 auto& arg = argItems.emplace_back(); 5662 auto& arg = argItems.emplace_back();
5429 switch (def->name->get_id()) { 5663 switch (def->name->get_id()) {
5430 case id<Variable_t>(): arg.name = variableToString(static_cast<Variable_t*>(def->name.get())); break; 5664 case id<Variable_t>(): {
5665 if (def->op) throw CompileError("invalid existence checking"sv, def->op);
5666 arg.name = variableToString(static_cast<Variable_t*>(def->name.get()));
5667 break;
5668 }
5431 case id<SelfItem_t>(): { 5669 case id<SelfItem_t>(): {
5432 assignSelf = true; 5670 assignSelf = true;
5433 if (def->op) { 5671 if (def->op) {
@@ -5566,10 +5804,50 @@ private:
5566 } 5804 }
5567 } 5805 }
5568 5806
5807 bool transformChainEndWithSlice(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) {
5808 auto x = chainList.front();
5809 if (ast_is<Slice_t>(chainList.back())) {
5810 auto comp = x->new_ptr<Comprehension_t>();
5811 {
5812 auto chainValue = x->new_ptr<ChainValue_t>();
5813 for (auto item : chainList) {
5814 chainValue->items.push_back(item);
5815 }
5816 auto itemVar = getUnusedName("_item_"sv);
5817 auto expCode = YueFormat{}.toString(chainValue);
5818 auto compCode = '[' + itemVar + " for "s + itemVar + " in *"s + expCode + ']';
5819 comp.set(toAst<Comprehension_t>(compCode, x));
5820 }
5821 switch (usage) {
5822 case ExpUsage::Assignment: {
5823 auto simpleValue = x->new_ptr<SimpleValue_t>();
5824 simpleValue->value.set(comp);
5825 auto exp = newExp(simpleValue, x);
5826 auto assignment = x->new_ptr<ExpListAssign_t>();
5827 assignment->expList.set(assignList);
5828 auto assign = x->new_ptr<Assign_t>();
5829 assign->values.push_back(exp);
5830 assignment->action.set(assign);
5831 transformAssignment(assignment, out);
5832 break;
5833 }
5834 case ExpUsage::Return:
5835 transformComprehension(comp, out, ExpUsage::Return);
5836 break;
5837 default:
5838 transformComprehension(comp, out, ExpUsage::Closure);
5839 break;
5840 }
5841 return true;
5842 }
5843 return false;
5844 }
5845
5569 bool transformChainEndWithEOP(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { 5846 bool transformChainEndWithEOP(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) {
5570 auto x = chainList.front(); 5847 auto x = chainList.front();
5571 if (ast_is<ExistentialOp_t>(chainList.back())) { 5848 if (ast_is<ExistentialOp_t>(chainList.back())) {
5572 auto parens = x->new_ptr<Parens_t>(); 5849 auto parens = x->new_ptr<Parens_t>();
5850 parens->extra = true;
5573 { 5851 {
5574 auto chainValue = x->new_ptr<ChainValue_t>(); 5852 auto chainValue = x->new_ptr<ChainValue_t>();
5575 for (auto item : chainList) { 5853 for (auto item : chainList) {
@@ -6082,7 +6360,7 @@ private:
6082 case id<ColonChainItem_t>(): 6360 case id<ColonChainItem_t>():
6083 case id<Exp_t>(): 6361 case id<Exp_t>():
6084 if (_withVars.empty()) { 6362 if (_withVars.empty()) {
6085 throw CompileError("short dot/colon and indexing syntax must be called within a with block"sv, x); 6363 throw CompileError("short dot/colon/indexing syntax must be called within a with block"sv, x);
6086 } else { 6364 } else {
6087 temp.push_back(_withVars.top()); 6365 temp.push_back(_withVars.top());
6088 } 6366 }
@@ -6163,6 +6441,7 @@ private:
6163 ++next; 6441 ++next;
6164 if (next != chainList.end()) { 6442 if (next != chainList.end()) {
6165 auto paren = x->new_ptr<Parens_t>(); 6443 auto paren = x->new_ptr<Parens_t>();
6444 paren->extra = true;
6166 paren->expr.set(newExp(chainValue, x)); 6445 paren->expr.set(newExp(chainValue, x));
6167 auto ncallable = x->new_ptr<Callable_t>(); 6446 auto ncallable = x->new_ptr<Callable_t>();
6168 ncallable->item.set(paren); 6447 ncallable->item.set(paren);
@@ -6215,6 +6494,7 @@ private:
6215 simpleValue->value.set(funLit); 6494 simpleValue->value.set(funLit);
6216 auto exp = newExp(simpleValue, x); 6495 auto exp = newExp(simpleValue, x);
6217 auto paren = x->new_ptr<Parens_t>(); 6496 auto paren = x->new_ptr<Parens_t>();
6497 paren->extra = true;
6218 paren->expr.set(exp); 6498 paren->expr.set(exp);
6219 auto callable = x->new_ptr<Callable_t>(); 6499 auto callable = x->new_ptr<Callable_t>();
6220 callable->item.set(paren); 6500 callable->item.set(paren);
@@ -6226,6 +6506,93 @@ private:
6226 } 6506 }
6227 return; 6507 return;
6228 } 6508 }
6509 break;
6510 }
6511 case id<ReversedIndex_t>(): {
6512 auto rIndex = static_cast<ReversedIndex_t*>(*it);
6513 auto current = it;
6514 auto prevChain = x->new_ptr<ChainValue_t>();
6515 for (auto i = chainList.begin(); i != current; ++i) {
6516 prevChain->items.push_back(*i);
6517 }
6518 auto var = singleVariableFrom(prevChain, AccessType::None);
6519 if (!var.empty() && isLocal(var)) {
6520 auto indexNode = toAst<Exp_t>('#' + var, rIndex);
6521 if (rIndex->modifier) {
6522 auto opValue = rIndex->new_ptr<ExpOpValue_t>();
6523 opValue->op.set(toAst<BinaryOperator_t>("-"sv, rIndex));
6524 opValue->pipeExprs.dup(rIndex->modifier->pipeExprs);
6525 indexNode->opValues.push_back(opValue);
6526 indexNode->opValues.dup(rIndex->modifier->opValues);
6527 indexNode->nilCoalesed.set(rIndex->modifier->nilCoalesed);
6528 }
6529 prevChain->items.push_back(indexNode);
6530 auto next = current;
6531 ++next;
6532 for (auto i = next; i != chainList.end(); ++i) {
6533 prevChain->items.push_back(*i);
6534 }
6535 if (usage == ExpUsage::Assignment) {
6536 auto assignment = x->new_ptr<ExpListAssign_t>();
6537 assignment->expList.set(assignList);
6538 auto assign = x->new_ptr<Assign_t>();
6539 assign->values.push_back(newExp(prevChain, x));
6540 assignment->action.set(assign);
6541 transformAssignment(assignment, out);
6542 return;
6543 }
6544 transformChainValue(prevChain, out, usage, assignList);
6545 return;
6546 } else {
6547 auto itemVar = getUnusedName("_item_"sv);
6548 auto asmt = assignmentFrom(toAst<Exp_t>(itemVar, x), newExp(prevChain, x), x);
6549 auto stmt1 = x->new_ptr<Statement_t>();
6550 stmt1->content.set(asmt);
6551 auto newChain = x->new_ptr<ChainValue_t>();
6552 newChain->items.push_back(toAst<Callable_t>(itemVar, x));
6553 auto indexNode = toAst<Exp_t>('#' + itemVar, rIndex);
6554 if (rIndex->modifier) {
6555 auto opValue = rIndex->new_ptr<ExpOpValue_t>();
6556 opValue->op.set(toAst<BinaryOperator_t>("-"sv, rIndex));
6557 opValue->pipeExprs.dup(rIndex->modifier->pipeExprs);
6558 indexNode->opValues.push_back(opValue);
6559 indexNode->opValues.dup(rIndex->modifier->opValues);
6560 indexNode->nilCoalesed.set(rIndex->modifier->nilCoalesed);
6561 }
6562 newChain->items.push_back(indexNode);
6563 auto next = current;
6564 ++next;
6565 for (auto i = next; i != chainList.end(); ++i) {
6566 newChain->items.push_back(*i);
6567 }
6568 auto expList = x->new_ptr<ExpList_t>();
6569 expList->exprs.push_back(newExp(newChain, x));
6570 auto expListAssign = x->new_ptr<ExpListAssign_t>();
6571 expListAssign->expList.set(expList);
6572 auto stmt2 = x->new_ptr<Statement_t>();
6573 stmt2->content.set(expListAssign);
6574 auto block = x->new_ptr<Block_t>();
6575 block->statements.push_back(stmt1);
6576 block->statements.push_back(stmt2);
6577 auto body = x->new_ptr<Body_t>();
6578 body->content.set(block);
6579 auto doNode = x->new_ptr<Do_t>();
6580 doNode->body.set(body);
6581 if (usage == ExpUsage::Assignment) {
6582 auto assignment = x->new_ptr<ExpListAssign_t>();
6583 assignment->expList.set(assignList);
6584 auto assign = x->new_ptr<Assign_t>();
6585 auto sVal = x->new_ptr<SimpleValue_t>();
6586 sVal->value.set(doNode);
6587 assign->values.push_back(newExp(sVal, x));
6588 assignment->action.set(assign);
6589 transformAssignment(assignment, out);
6590 return;
6591 }
6592 transformDo(doNode, out, usage);
6593 return;
6594 }
6595 break;
6229 } 6596 }
6230 } 6597 }
6231 } 6598 }
@@ -6410,7 +6777,7 @@ private:
6410 } 6777 }
6411 } 6778 }
6412 } 6779 }
6413 int len = lua_objlen(L, -1); 6780 int len = static_cast<int>(lua_objlen(L, -1));
6414 lua_pushnil(L); // cur nil 6781 lua_pushnil(L); // cur nil
6415 for (int i = len; i >= 1; i--) { 6782 for (int i = len; i >= 1; i--) {
6416 lua_pop(L, 1); // cur 6783 lua_pop(L, 1); // cur
@@ -6422,7 +6789,25 @@ private:
6422 break; 6789 break;
6423 } 6790 }
6424 } 6791 }
6425 if (!lua_isfunction(L, -1)) { 6792 str_list checks;
6793 if (lua_istable(L, -1)) {
6794 lua_rawgeti(L, -1, 1); // cur macrotab checks
6795 int len = static_cast<int>(lua_objlen(L, -1));
6796 for (int i = 1; i <= len; i++) {
6797 lua_rawgeti(L, -1, i);
6798 if (lua_toboolean(L, -1) == 0) {
6799 checks.emplace_back();
6800 } else {
6801 size_t str_len = 0;
6802 auto str = lua_tolstring(L, -1, &str_len);
6803 checks.emplace_back(std::string{str, str_len});
6804 }
6805 lua_pop(L, 1);
6806 }
6807 lua_pop(L, 1);
6808 lua_rawgeti(L, -1, 2); // cur macrotab macroFunc
6809 lua_remove(L, -2); // cur macroFunc
6810 } else if (!lua_isfunction(L, -1)) {
6426 auto code = expandBuiltinMacro(macroName, x); 6811 auto code = expandBuiltinMacro(macroName, x);
6427 if (!code.empty()) return code; 6812 if (!code.empty()) return code;
6428 if (macroName == "is_ast"sv) { 6813 if (macroName == "is_ast"sv) {
@@ -6467,11 +6852,34 @@ private:
6467 } // cur macroFunc 6852 } // cur macroFunc
6468 pushYue("pcall"sv); // cur macroFunc pcall 6853 pushYue("pcall"sv); // cur macroFunc pcall
6469 lua_insert(L, -2); // cur pcall macroFunc 6854 lua_insert(L, -2); // cur pcall macroFunc
6470 if (!lua_checkstack(L, argStrs.size())) { 6855 if (!lua_checkstack(L, static_cast<int>(argStrs.size()))) {
6471 throw CompileError("too much macro params"s, x); 6856 throw CompileError("too much macro params"s, x);
6472 } 6857 }
6858 auto checkIt = checks.begin();
6859 node_container::const_iterator argIt;
6860 if (args) {
6861 argIt = args->begin();
6862 }
6473 for (const auto& arg : argStrs) { 6863 for (const auto& arg : argStrs) {
6864 if (checkIt != checks.end()) {
6865 if (checkIt->empty()) {
6866 ++checkIt;
6867 } else {
6868 if ((*checkIt)[0] == '.') {
6869 auto astName = checkIt->substr(3);
6870 if (!_parser.match(astName, arg)) {
6871 throw CompileError("expecting \""s + astName + "\", AST mismatch"s, *argIt);
6872 }
6873 } else {
6874 if (!_parser.match(*checkIt, arg)) {
6875 throw CompileError("expecting \""s + *checkIt + "\", AST mismatch"s, *argIt);
6876 }
6877 ++checkIt;
6878 }
6879 }
6880 }
6474 lua_pushlstring(L, arg.c_str(), arg.size()); 6881 lua_pushlstring(L, arg.c_str(), arg.size());
6882 ++argIt;
6475 } // cur pcall macroFunc args... 6883 } // cur pcall macroFunc args...
6476 bool success = lua_pcall(L, static_cast<int>(argStrs.size()), 1, 0) == 0; 6884 bool success = lua_pcall(L, static_cast<int>(argStrs.size()), 1, 0) == 0;
6477 if (!success) { // cur err 6885 if (!success) { // cur err
@@ -6601,14 +7009,14 @@ private:
6601 } else { 7009 } else {
6602 if (!codes.empty()) { 7010 if (!codes.empty()) {
6603 if (isBlock) { 7011 if (isBlock) {
6604 info = _parser.parse<BlockEnd_t>(codes); 7012 info = _parser.parse<BlockEnd_t>(codes, false);
6605 if (info.error) { 7013 if (info.error) {
6606 throw CompileError("failed to expand macro as block: "s + info.error.value().msg, x); 7014 throw CompileError("failed to expand macro as block: "s + info.error.value().msg, x);
6607 } 7015 }
6608 } else { 7016 } else {
6609 info = _parser.parse<Exp_t>(codes); 7017 info = _parser.parse<Exp_t>(codes, false);
6610 if (!info.node && allowBlockMacroReturn) { 7018 if (!info.node && allowBlockMacroReturn) {
6611 info = _parser.parse<BlockEnd_t>(codes); 7019 info = _parser.parse<BlockEnd_t>(codes, false);
6612 if (info.error) { 7020 if (info.error) {
6613 throw CompileError("failed to expand macro as expr or block: "s + info.error.value().msg, x); 7021 throw CompileError("failed to expand macro as expr or block: "s + info.error.value().msg, x);
6614 } 7022 }
@@ -6631,6 +7039,7 @@ private:
6631 exp.set(info.node); 7039 exp.set(info.node);
6632 if (!exp->opValues.empty() || (chainList.size() > 2 || (chainList.size() == 2 && !ast_is<Invoke_t, InvokeArgs_t>(chainList.back())))) { 7040 if (!exp->opValues.empty() || (chainList.size() > 2 || (chainList.size() == 2 && !ast_is<Invoke_t, InvokeArgs_t>(chainList.back())))) {
6633 auto paren = x->new_ptr<Parens_t>(); 7041 auto paren = x->new_ptr<Parens_t>();
7042 paren->extra = true;
6634 paren->expr.set(exp); 7043 paren->expr.set(exp);
6635 auto callable = x->new_ptr<Callable_t>(); 7044 auto callable = x->new_ptr<Callable_t>();
6636 callable->item.set(paren); 7045 callable->item.set(paren);
@@ -6743,6 +7152,9 @@ private:
6743 if (transformChainEndWithColonItem(chainList, out, usage, assignList)) { 7152 if (transformChainEndWithColonItem(chainList, out, usage, assignList)) {
6744 return; 7153 return;
6745 } 7154 }
7155 if (transformChainEndWithSlice(chainList, out, usage, assignList)) {
7156 return;
7157 }
6746 transformChainList(chainList, out, usage, assignList); 7158 transformChainList(chainList, out, usage, assignList);
6747 } 7159 }
6748 7160
@@ -7138,6 +7550,33 @@ private:
7138 void transformNum(Num_t* num, str_list& out) { 7550 void transformNum(Num_t* num, str_list& out) {
7139 std::string numStr = _parser.toString(num); 7551 std::string numStr = _parser.toString(num);
7140 numStr.erase(std::remove(numStr.begin(), numStr.end(), '_'), numStr.end()); 7552 numStr.erase(std::remove(numStr.begin(), numStr.end(), '_'), numStr.end());
7553 if (numStr.size() > 2 && numStr[0] == '0') {
7554 if (numStr[1] == 'b' || numStr[1] == 'B') {
7555 std::string binaryPart = numStr.substr(2);
7556 try {
7557 unsigned long long value = std::stoull(binaryPart, nullptr, 2);
7558 numStr = std::to_string(value);
7559 } catch (const std::exception&) {
7560 throw CompileError("invalid binary literal"sv, num);
7561 }
7562 } else if (getLuaTarget(num) < 502) {
7563 if (numStr[1] == 'x' || numStr[1] == 'X') {
7564 if (numStr.find_first_of(".-"sv) != std::string::npos) {
7565 std::stringstream ss(numStr);
7566 double v;
7567 ss >> std::hexfloat >> v;
7568 if (ss.fail() || !std::isfinite(v)) {
7569 throw CompileError("invalid hex‑float literal"sv, num);
7570 }
7571 std::ostringstream outSs;
7572 outSs << std::setprecision(17) << v;
7573 numStr = outSs.str();
7574 } else {
7575 numStr.erase(std::remove(numStr.begin(), numStr.end(), '+'), numStr.end());
7576 }
7577 }
7578 }
7579 }
7141 out.push_back(numStr); 7580 out.push_back(numStr);
7142 } 7581 }
7143 7582
@@ -7823,6 +8262,11 @@ private:
7823 } 8262 }
7824 8263
7825 bool transformForEachHead(AssignableNameList_t* nameList, ast_node* loopTarget, str_list& out, bool inClosure) { 8264 bool transformForEachHead(AssignableNameList_t* nameList, ast_node* loopTarget, str_list& out, bool inClosure) {
8265 enum class NumState {
8266 Unknown,
8267 Positive,
8268 Negtive
8269 };
7826 auto x = nameList; 8270 auto x = nameList;
7827 str_list temp; 8271 str_list temp;
7828 str_list vars; 8272 str_list vars;
@@ -7889,15 +8333,35 @@ private:
7889 for (auto item : chainList) { 8333 for (auto item : chainList) {
7890 chain->items.push_back(item); 8334 chain->items.push_back(item);
7891 } 8335 }
7892 std::string startValue("1"sv); 8336 std::string startValue;
8337 NumState startStatus = NumState::Unknown;
7893 if (auto exp = slice->startValue.as<Exp_t>()) { 8338 if (auto exp = slice->startValue.as<Exp_t>()) {
7894 transformExp(exp, temp, ExpUsage::Closure); 8339 transformExp(exp, temp, ExpUsage::Closure);
8340 if (temp.back().at(0) == '-') {
8341 if (_parser.match<Num_t>(temp.back().substr(1))) {
8342 startStatus = NumState::Negtive;
8343 }
8344 } else {
8345 if (_parser.match<Num_t>(temp.back())) {
8346 startStatus = NumState::Positive;
8347 }
8348 }
7895 startValue = std::move(temp.back()); 8349 startValue = std::move(temp.back());
7896 temp.pop_back(); 8350 temp.pop_back();
7897 } 8351 }
7898 std::string stopValue; 8352 std::string stopValue;
8353 NumState stopStatus = NumState::Unknown;
7899 if (auto exp = slice->stopValue.as<Exp_t>()) { 8354 if (auto exp = slice->stopValue.as<Exp_t>()) {
7900 transformExp(exp, temp, ExpUsage::Closure); 8355 transformExp(exp, temp, ExpUsage::Closure);
8356 if (temp.back().at(0) == '-') {
8357 if (_parser.match<Num_t>(temp.back().substr(1))) {
8358 stopStatus = NumState::Negtive;
8359 }
8360 } else {
8361 if (_parser.match<Num_t>(temp.back())) {
8362 stopStatus = NumState::Positive;
8363 }
8364 }
7901 stopValue = std::move(temp.back()); 8365 stopValue = std::move(temp.back());
7902 temp.pop_back(); 8366 temp.pop_back();
7903 } 8367 }
@@ -7919,8 +8383,33 @@ private:
7919 transformChainValue(chain, temp, ExpUsage::Closure); 8383 transformChainValue(chain, temp, ExpUsage::Closure);
7920 _buf << prefix << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); 8384 _buf << prefix << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList);
7921 } 8385 }
8386 if (startValue.empty()) {
8387 startValue = "1"s;
8388 startStatus = NumState::Positive;
8389 }
8390 std::string minVar;
8391 if (startStatus != NumState::Positive) {
8392 std::string prefix;
8393 if (!extraScope && !inClosure && needScope) {
8394 extraScope = true;
8395 prefix = indent() + "do"s + nll(x);
8396 pushScope();
8397 }
8398 minVar = getUnusedName("_min_"sv);
8399 varBefore.push_back(minVar);
8400 if (startStatus == NumState::Negtive) {
8401 _buf << prefix << indent() << "local "sv << minVar << " = "sv << "#"sv << listVar << " + "sv << startValue << " + 1"sv << nll(nameList);
8402 } else {
8403 _buf << prefix << indent() << "local "sv << minVar << " = "sv << startValue << nll(nameList);
8404 }
8405 }
8406 bool defaultStop = false;
8407 if (stopValue.empty()) {
8408 stopValue = "#"s + listVar;
8409 defaultStop = true;
8410 }
7922 std::string maxVar; 8411 std::string maxVar;
7923 if (!stopValue.empty()) { 8412 if (stopStatus != NumState::Positive) {
7924 std::string prefix; 8413 std::string prefix;
7925 if (!extraScope && !inClosure && needScope) { 8414 if (!extraScope && !inClosure && needScope) {
7926 extraScope = true; 8415 extraScope = true;
@@ -7929,14 +8418,45 @@ private:
7929 } 8418 }
7930 maxVar = getUnusedName("_max_"sv); 8419 maxVar = getUnusedName("_max_"sv);
7931 varBefore.push_back(maxVar); 8420 varBefore.push_back(maxVar);
7932 _buf << prefix << indent() << "local "sv << maxVar << " = "sv << stopValue << nll(nameList); 8421 if (stopStatus == NumState::Negtive) {
8422 _buf << indent() << "local "sv << maxVar << " = "sv << "#"sv << listVar << " + "sv << stopValue << " + 1"sv << nll(nameList);
8423 } else {
8424 _buf << prefix << indent() << "local "sv << maxVar << " = "sv << stopValue << nll(nameList);
8425 }
8426 }
8427 if (startStatus == NumState::Unknown) {
8428 _buf << indent() << minVar << " = "sv << minVar << " < 0 and #"sv << listVar << " + "sv << minVar << " + 1 or "sv << minVar << nll(nameList);
8429 }
8430 if (!defaultStop && stopStatus == NumState::Unknown) {
8431 _buf << indent() << maxVar << " = "sv << maxVar << " < 0 and #"sv << listVar << " + "sv << maxVar << " + 1 or "sv << maxVar << nll(nameList);
7933 } 8432 }
7934 _buf << indent() << "for "sv << indexVar << " = "sv; 8433 _buf << indent() << "for "sv << indexVar << " = "sv;
7935 _buf << startValue << ", "sv; 8434 if (startValue.empty()) {
8435 _buf << "1"sv;
8436 } else {
8437 switch (startStatus) {
8438 case NumState::Unknown:
8439 case NumState::Negtive:
8440 _buf << minVar;
8441 break;
8442 case NumState::Positive:
8443 _buf << startValue;
8444 break;
8445 }
8446 }
8447 _buf << ", "sv;
7936 if (stopValue.empty()) { 8448 if (stopValue.empty()) {
7937 _buf << "#"sv << listVar; 8449 _buf << "#"sv << listVar;
7938 } else { 8450 } else {
7939 _buf << maxVar << " < 0 and #"sv << listVar << " + "sv << maxVar << " or "sv << maxVar; 8451 switch (stopStatus) {
8452 case NumState::Unknown:
8453 case NumState::Negtive:
8454 _buf << maxVar;
8455 break;
8456 case NumState::Positive:
8457 _buf << stopValue;
8458 break;
8459 }
7940 } 8460 }
7941 if (!stepValue.empty()) { 8461 if (!stepValue.empty()) {
7942 _buf << ", "sv << stepValue; 8462 _buf << ", "sv << stepValue;
@@ -7976,7 +8496,7 @@ private:
7976 pushScope(); 8496 pushScope();
7977 for (const auto& var : vars) forceAddToScope(var); 8497 for (const auto& var : vars) forceAddToScope(var);
7978 for (const auto& var : varAfter) addToScope(var); 8498 for (const auto& var : varAfter) addToScope(var);
7979 if (!varConstAfter.empty()) markVarConst(varConstAfter); 8499 if (!varConstAfter.empty()) markVarLocalConst(varConstAfter);
7980 if (!destructPairs.empty()) { 8500 if (!destructPairs.empty()) {
7981 temp.clear(); 8501 temp.clear();
7982 for (auto& pair : destructPairs) { 8502 for (auto& pair : destructPairs) {
@@ -8061,7 +8581,7 @@ private:
8061 _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : ", "s + step) << " do"sv << nll(var); 8581 _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : ", "s + step) << " do"sv << nll(var);
8062 pushScope(); 8582 pushScope();
8063 forceAddToScope(varName); 8583 forceAddToScope(varName);
8064 markVarConst(varName); 8584 markVarLocalConst(varName);
8065 out.push_back(clearBuf()); 8585 out.push_back(clearBuf());
8066 } 8586 }
8067 8587
@@ -8069,26 +8589,59 @@ private:
8069 transformForHead(forNode->varName, forNode->startValue, forNode->stopValue, forNode->stepValue, out); 8589 transformForHead(forNode->varName, forNode->startValue, forNode->stopValue, forNode->stepValue, out);
8070 } 8590 }
8071 8591
8072 void transform_plain_body(ast_node* body, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { 8592 void transform_plain_body(ast_node* bodyOrStmt, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) {
8073 switch (body->get_id()) { 8593 switch (bodyOrStmt->get_id()) {
8074 case id<Block_t>(): 8594 case id<Block_t>():
8075 transformBlock(static_cast<Block_t*>(body), out, usage, assignList); 8595 transformBlock(static_cast<Block_t*>(bodyOrStmt), out, usage, assignList);
8076 break; 8596 break;
8077 case id<Statement_t>(): { 8597 case id<Statement_t>(): {
8078 auto newBlock = body->new_ptr<Block_t>(); 8598 auto newBlock = bodyOrStmt->new_ptr<Block_t>();
8079 newBlock->statements.push_back(body); 8599 newBlock->statements.push_back(bodyOrStmt);
8080 transformBlock(newBlock, out, usage, assignList); 8600 transformBlock(newBlock, out, usage, assignList);
8081 break; 8601 break;
8082 } 8602 }
8083 default: YUEE("AST node mismatch", body); break; 8603 default: YUEE("AST node mismatch", bodyOrStmt); break;
8084 } 8604 }
8085 } 8605 }
8086 8606
8087 bool hasContinueStatement(ast_node* body) { 8607 enum class BreakLoopType {
8088 return traversal::Stop == body->traverse([&](ast_node* node) { 8608 None = 0,
8609 Break = 1,
8610 BreakWithValue = 1 << 1,
8611 Continue = 1 << 2
8612 };
8613
8614 bool hasBreak(uint32_t breakLoopType) const {
8615 return (breakLoopType & int(BreakLoopType::Break)) != 0;
8616 }
8617
8618 bool hasBreakWithValue(uint32_t breakLoopType) const {
8619 return (breakLoopType & int(BreakLoopType::BreakWithValue)) != 0;
8620 }
8621
8622 bool hasContinue(uint32_t breakLoopType) const {
8623 return (breakLoopType & int(BreakLoopType::Continue)) != 0;
8624 }
8625
8626 uint32_t getBreakLoopType(ast_node* body, const std::string& varBWV) {
8627 uint32_t type = 0;
8628 body->traverse([&](ast_node* node) {
8089 if (auto stmt = ast_cast<Statement_t>(node)) { 8629 if (auto stmt = ast_cast<Statement_t>(node)) {
8090 if (stmt->content.is<BreakLoop_t>()) { 8630 if (auto breakLoop = stmt->content.as<BreakLoop_t>()) {
8091 return _parser.toString(stmt->content) == "continue"sv ? traversal::Stop : traversal::Return; 8631 if (breakLoop->type.is<Continue_t>()) {
8632 type |= int(BreakLoopType::Continue);
8633 return traversal::Return;
8634 } else {
8635 if (breakLoop->value) {
8636 if (varBWV.empty()) {
8637 throw CompileError("break with a value is not allowed here"sv, breakLoop->value);
8638 }
8639 type |= int(BreakLoopType::BreakWithValue);
8640 breakLoop->varBWV = varBWV;
8641 } else {
8642 type |= int(BreakLoopType::Break);
8643 }
8644 }
8092 } else if (auto expList = expListFrom(stmt)) { 8645 } else if (auto expList = expListFrom(stmt)) {
8093 BLOCK_START 8646 BLOCK_START
8094 auto value = singleValueFrom(expList); 8647 auto value = singleValueFrom(expList);
@@ -8099,40 +8652,30 @@ private:
8099 switch (sVal->get_id()) { 8652 switch (sVal->get_id()) {
8100 case id<With_t>(): { 8653 case id<With_t>(): {
8101 auto withNode = static_cast<With_t*>(sVal); 8654 auto withNode = static_cast<With_t*>(sVal);
8102 if (hasContinueStatement(withNode->body)) { 8655 type |= getBreakLoopType(withNode->body, varBWV);
8103 return traversal::Stop; 8656 return traversal::Return;
8104 }
8105 break;
8106 } 8657 }
8107 case id<Do_t>(): { 8658 case id<Do_t>(): {
8108 auto doNode = static_cast<Do_t*>(sVal); 8659 auto doNode = static_cast<Do_t*>(sVal);
8109 if (hasContinueStatement(doNode->body)) { 8660 type |= getBreakLoopType(doNode->body, varBWV);
8110 return traversal::Stop; 8661 return traversal::Return;
8111 }
8112 break;
8113 } 8662 }
8114 case id<If_t>(): { 8663 case id<If_t>(): {
8115 auto ifNode = static_cast<If_t*>(sVal); 8664 auto ifNode = static_cast<If_t*>(sVal);
8116 for (auto n : ifNode->nodes.objects()) { 8665 for (auto n : ifNode->nodes.objects()) {
8117 if (hasContinueStatement(n)) { 8666 type |= getBreakLoopType(n, varBWV);
8118 return traversal::Stop;
8119 }
8120 } 8667 }
8121 break; 8668 return traversal::Return;
8122 } 8669 }
8123 case id<Switch_t>(): { 8670 case id<Switch_t>(): {
8124 auto switchNode = static_cast<Switch_t*>(sVal); 8671 auto switchNode = static_cast<Switch_t*>(sVal);
8125 for (auto branch : switchNode->branches.objects()) { 8672 for (auto branch : switchNode->branches.objects()) {
8126 if (hasContinueStatement(static_cast<SwitchCase_t*>(branch)->body)) { 8673 type |= getBreakLoopType(static_cast<SwitchCase_t*>(branch)->body, varBWV);
8127 return traversal::Stop;
8128 }
8129 } 8674 }
8130 if (switchNode->lastBranch) { 8675 if (switchNode->lastBranch) {
8131 if (hasContinueStatement(switchNode->lastBranch)) { 8676 type |= getBreakLoopType(switchNode->lastBranch, varBWV);
8132 return traversal::Stop;
8133 }
8134 } 8677 }
8135 break; 8678 return traversal::Return;
8136 } 8679 }
8137 } 8680 }
8138 BLOCK_END 8681 BLOCK_END
@@ -8146,6 +8689,7 @@ private:
8146 } 8689 }
8147 return traversal::Return; 8690 return traversal::Return;
8148 }); 8691 });
8692 return type;
8149 } 8693 }
8150 8694
8151 void addDoToLastLineReturn(ast_node* body) { 8695 void addDoToLastLineReturn(ast_node* body) {
@@ -8169,10 +8713,10 @@ private:
8169 } 8713 }
8170 } 8714 }
8171 8715
8172 void transformLoopBody(ast_node* body, str_list& out, const std::string& appendContent, ExpUsage usage, ExpList_t* assignList = nullptr) { 8716 void transformLoopBody(ast_node* body, str_list& out, uint32_t breakLoopType, ExpUsage usage, ExpList_t* assignList = nullptr) {
8173 str_list temp; 8717 str_list temp;
8174 bool extraDo = false; 8718 bool extraDo = false;
8175 bool withContinue = hasContinueStatement(body); 8719 bool withContinue = hasContinue(breakLoopType);
8176 int target = getLuaTarget(body); 8720 int target = getLuaTarget(body);
8177 std::string extraLabel; 8721 std::string extraLabel;
8178 if (withContinue) { 8722 if (withContinue) {
@@ -8181,7 +8725,7 @@ private:
8181 if (!block->statements.empty()) { 8725 if (!block->statements.empty()) {
8182 auto stmt = static_cast<Statement_t*>(block->statements.back()); 8726 auto stmt = static_cast<Statement_t*>(block->statements.back());
8183 if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) { 8727 if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) {
8184 extraDo = _parser.toString(breakLoop) == "break"sv; 8728 extraDo = breakLoop->type.is<Break_t>();
8185 } 8729 }
8186 } 8730 }
8187 } 8731 }
@@ -8214,9 +8758,6 @@ private:
8214 popScope(); 8758 popScope();
8215 _buf << indent() << "end"sv << nll(body); 8759 _buf << indent() << "end"sv << nll(body);
8216 } 8760 }
8217 if (!appendContent.empty()) {
8218 _buf << indent() << appendContent;
8219 }
8220 _buf << indent() << _continueVars.top().var << " = true"sv << nll(body); 8761 _buf << indent() << _continueVars.top().var << " = true"sv << nll(body);
8221 popScope(); 8762 popScope();
8222 _buf << indent() << "until true"sv << nlr(body); 8763 _buf << indent() << "until true"sv << nlr(body);
@@ -8226,14 +8767,9 @@ private:
8226 temp.push_back(clearBuf()); 8767 temp.push_back(clearBuf());
8227 _continueVars.pop(); 8768 _continueVars.pop();
8228 } else { 8769 } else {
8229 if (!appendContent.empty()) {
8230 temp.push_back(indent() + appendContent);
8231 }
8232 temp.push_back(extraLabel); 8770 temp.push_back(extraLabel);
8233 _continueVars.pop(); 8771 _continueVars.pop();
8234 } 8772 }
8235 } else if (!appendContent.empty()) {
8236 temp.back().append(indent() + appendContent);
8237 } 8773 }
8238 out.push_back(join(temp)); 8774 out.push_back(join(temp));
8239 } 8775 }
@@ -8241,8 +8777,9 @@ private:
8241 std::string transformRepeatBody(Repeat_t* repeatNode, str_list& out) { 8777 std::string transformRepeatBody(Repeat_t* repeatNode, str_list& out) {
8242 str_list temp; 8778 str_list temp;
8243 bool extraDo = false; 8779 bool extraDo = false;
8244 auto body = repeatNode->body->content.get(); 8780 auto body = repeatNode->body.get();
8245 bool withContinue = hasContinueStatement(body); 8781 auto breakLoopType = getBreakLoopType(body, Empty);
8782 bool withContinue = hasContinue(breakLoopType);
8246 std::string conditionVar; 8783 std::string conditionVar;
8247 std::string extraLabel; 8784 std::string extraLabel;
8248 ast_ptr<false, ExpListAssign_t> condAssign; 8785 ast_ptr<false, ExpListAssign_t> condAssign;
@@ -8253,7 +8790,7 @@ private:
8253 if (!block->statements.empty()) { 8790 if (!block->statements.empty()) {
8254 auto stmt = static_cast<Statement_t*>(block->statements.back()); 8791 auto stmt = static_cast<Statement_t*>(block->statements.back());
8255 if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) { 8792 if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) {
8256 extraDo = _parser.toString(breakLoop) == "break"sv; 8793 extraDo = breakLoop->type.is<Break_t>();
8257 } 8794 }
8258 } 8795 }
8259 } 8796 }
@@ -8316,7 +8853,8 @@ private:
8316 void transformFor(For_t* forNode, str_list& out) { 8853 void transformFor(For_t* forNode, str_list& out) {
8317 str_list temp; 8854 str_list temp;
8318 transformForHead(forNode, temp); 8855 transformForHead(forNode, temp);
8319 transformLoopBody(forNode->body, temp, Empty, ExpUsage::Common); 8856 auto breakLoopType = getBreakLoopType(forNode->body, Empty);
8857 transformLoopBody(forNode->body, temp, breakLoopType, ExpUsage::Common);
8320 popScope(); 8858 popScope();
8321 out.push_back(join(temp) + indent() + "end"s + nlr(forNode)); 8859 out.push_back(join(temp) + indent() + "end"s + nlr(forNode));
8322 } 8860 }
@@ -8327,13 +8865,24 @@ private:
8327 addToScope(accum); 8865 addToScope(accum);
8328 std::string len = getUnusedName("_len_"sv); 8866 std::string len = getUnusedName("_len_"sv);
8329 addToScope(len); 8867 addToScope(len);
8330 _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); 8868 auto breakLoopType = getBreakLoopType(forNode->body, accum);
8869 _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(forNode);
8870 out.emplace_back(clearBuf());
8331 _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); 8871 _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode);
8332 out.push_back(clearBuf()); 8872 auto& lenAssign = out.emplace_back(clearBuf());
8333 transformForHead(forNode, out); 8873 transformForHead(forNode, out);
8334 auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x); 8874 if (hasBreakWithValue(breakLoopType)) {
8335 auto lenLine = len + " = "s + len + " + 1"s + nlr(forNode->body); 8875 lenAssign.clear();
8336 transformLoopBody(forNode->body, out, lenLine, ExpUsage::Assignment, expList); 8876 transformLoopBody(forNode->body, out, breakLoopType, ExpUsage::Common);
8877 } else {
8878 auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x);
8879 auto followStmt = toAst<Statement_t>(len + "+=1"s, forNode->body);
8880 expList->followStmt = followStmt.get();
8881 transformLoopBody(forNode->body, out, breakLoopType, ExpUsage::Assignment, expList);
8882 if (!expList->followStmtProcessed) {
8883 lenAssign.clear();
8884 }
8885 }
8337 popScope(); 8886 popScope();
8338 out.push_back(indent() + "end"s + nlr(forNode)); 8887 out.push_back(indent() + "end"s + nlr(forNode));
8339 return accum; 8888 return accum;
@@ -8412,7 +8961,8 @@ private:
8412 void transformForEach(ForEach_t* forEach, str_list& out) { 8961 void transformForEach(ForEach_t* forEach, str_list& out) {
8413 str_list temp; 8962 str_list temp;
8414 bool extraScoped = transformForEachHead(forEach->nameList, forEach->loopValue, temp, false); 8963 bool extraScoped = transformForEachHead(forEach->nameList, forEach->loopValue, temp, false);
8415 transformLoopBody(forEach->body, temp, Empty, ExpUsage::Common); 8964 auto breakLoopType = getBreakLoopType(forEach->body, Empty);
8965 transformLoopBody(forEach->body, temp, breakLoopType, ExpUsage::Common);
8416 popScope(); 8966 popScope();
8417 out.push_back(temp.front() + temp.back() + indent() + "end"s + nlr(forEach)); 8967 out.push_back(temp.front() + temp.back() + indent() + "end"s + nlr(forEach));
8418 if (extraScoped) { 8968 if (extraScoped) {
@@ -8427,13 +8977,24 @@ private:
8427 addToScope(accum); 8977 addToScope(accum);
8428 std::string len = getUnusedName("_len_"sv); 8978 std::string len = getUnusedName("_len_"sv);
8429 addToScope(len); 8979 addToScope(len);
8430 _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); 8980 auto breakLoopType = getBreakLoopType(forEach->body, accum);
8981 _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(forEach);
8982 out.emplace_back(clearBuf());
8431 _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); 8983 _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach);
8432 out.push_back(clearBuf()); 8984 auto& lenAssign = out.emplace_back(clearBuf());
8433 transformForEachHead(forEach->nameList, forEach->loopValue, out, true); 8985 transformForEachHead(forEach->nameList, forEach->loopValue, out, true);
8434 auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x); 8986 if (hasBreakWithValue(breakLoopType)) {
8435 auto lenLine = len + " = "s + len + " + 1"s + nlr(forEach->body); 8987 lenAssign.clear();
8436 transformLoopBody(forEach->body, out, lenLine, ExpUsage::Assignment, expList); 8988 transformLoopBody(forEach->body, out, breakLoopType, ExpUsage::Common);
8989 } else {
8990 auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x);
8991 auto followStmt = toAst<Statement_t>(len + "+=1"s, forEach->body);
8992 expList->followStmt = followStmt.get();
8993 transformLoopBody(forEach->body, out, breakLoopType, ExpUsage::Assignment, expList);
8994 if (!expList->followStmtProcessed) {
8995 lenAssign.clear();
8996 }
8997 }
8437 popScope(); 8998 popScope();
8438 out.push_back(indent() + "end"s + nlr(forEach)); 8999 out.push_back(indent() + "end"s + nlr(forEach));
8439 return accum; 9000 return accum;
@@ -8617,12 +9178,64 @@ private:
8617 out.push_back(temp.empty() ? "\"\""s : join(temp, " .. "sv)); 9178 out.push_back(temp.empty() ? "\"\""s : join(temp, " .. "sv));
8618 } 9179 }
8619 9180
9181 void transformYAMLMultiline(YAMLMultiline_t* multiline, str_list& out) {
9182 std::optional<std::string> indent;
9183 str_list temp;
9184 for (auto line_ : multiline->lines.objects()) {
9185 auto line = static_cast<YAMLLine_t*>(line_);
9186 auto indentStr = _parser.toString(line->indent);
9187 if (!indent) {
9188 indent = indentStr;
9189 }
9190 if (std::string_view{indentStr.c_str(), indent.value().size()} != indent.value()) {
9191 throw CompileError("inconsistent indent"sv, line);
9192 }
9193 indentStr = indentStr.substr(indent.value().size());
9194 str_list segs;
9195 bool firstSeg = true;
9196 for (auto seg_ : line->segments.objects()) {
9197 auto content = static_cast<YAMLLineContent_t*>(seg_)->content.get();
9198 switch (content->get_id()) {
9199 case id<YAMLLineInner_t>(): {
9200 auto seqStr = _parser.toString(content);
9201 Utils::replace(seqStr, "\\#"sv, "#"sv);
9202 if (firstSeg) {
9203 firstSeg = false;
9204 seqStr.insert(0, indentStr);
9205 }
9206 segs.push_back(Utils::toLuaDoubleString(seqStr));
9207 break;
9208 }
9209 case id<Exp_t>(): {
9210 if (firstSeg) {
9211 firstSeg = false;
9212 if (!indentStr.empty()) {
9213 segs.push_back(Utils::toLuaDoubleString(indentStr));
9214 }
9215 }
9216 transformExp(static_cast<Exp_t*>(content), segs, ExpUsage::Closure);
9217 segs.back() = globalVar("tostring"sv, content, AccessType::Read) + '(' + segs.back() + ')';
9218 break;
9219 }
9220 default: YUEE("AST node mismatch", content); break;
9221 }
9222 }
9223 temp.push_back(join(segs, " .. "sv));
9224 }
9225 auto str = join(temp, " .. '\\n' .. "sv);
9226 Utils::replace(str, "\" .. '\\n' .. \""sv, "\\n"sv);
9227 Utils::replace(str, "\" .. '\\n'"sv, "\\n\""sv);
9228 Utils::replace(str, "'\\n' .. \""sv, "\"\\n"sv);
9229 out.push_back(str);
9230 }
9231
8620 void transformString(String_t* string, str_list& out) { 9232 void transformString(String_t* string, str_list& out) {
8621 auto str = string->str.get(); 9233 auto str = string->str.get();
8622 switch (str->get_id()) { 9234 switch (str->get_id()) {
8623 case id<SingleString_t>(): transformSingleString(static_cast<SingleString_t*>(str), out); break; 9235 case id<SingleString_t>(): transformSingleString(static_cast<SingleString_t*>(str), out); break;
8624 case id<DoubleString_t>(): transformDoubleString(static_cast<DoubleString_t*>(str), out); break; 9236 case id<DoubleString_t>(): transformDoubleString(static_cast<DoubleString_t*>(str), out); break;
8625 case id<LuaString_t>(): transformLuaString(static_cast<LuaString_t*>(str), out); break; 9237 case id<LuaString_t>(): transformLuaString(static_cast<LuaString_t*>(str), out); break;
9238 case id<YAMLMultiline_t>(): transformYAMLMultiline(static_cast<YAMLMultiline_t*>(str), out); break;
8626 default: YUEE("AST node mismatch", str); break; 9239 default: YUEE("AST node mismatch", str); break;
8627 } 9240 }
8628 } 9241 }
@@ -8804,7 +9417,7 @@ private:
8804 auto names = transformAssignDefs(assignment->expList.get(), DefOp::Get); 9417 auto names = transformAssignDefs(assignment->expList.get(), DefOp::Get);
8805 for (const auto& name : names) { 9418 for (const auto& name : names) {
8806 forceAddToScope(name.first); 9419 forceAddToScope(name.first);
8807 markVarConst(name.first); 9420 markVarLocalConst(name.first);
8808 varDefs.push_back(name.first); 9421 varDefs.push_back(name.first);
8809 classConstVars.push_back(name.first); 9422 classConstVars.push_back(name.first);
8810 } 9423 }
@@ -8818,7 +9431,7 @@ private:
8818 for (const auto& item : destruct.items) { 9431 for (const auto& item : destruct.items) {
8819 if (!item.targetVar.empty()) { 9432 if (!item.targetVar.empty()) {
8820 forceAddToScope(item.targetVar); 9433 forceAddToScope(item.targetVar);
8821 markVarConst(item.targetVar); 9434 markVarLocalConst(item.targetVar);
8822 varDefs.push_back(item.targetVar); 9435 varDefs.push_back(item.targetVar);
8823 classConstVars.push_back(item.targetVar); 9436 classConstVars.push_back(item.targetVar);
8824 } 9437 }
@@ -9200,11 +9813,11 @@ private:
9200 std::string withVar; 9813 std::string withVar;
9201 bool needScope = !currentScope().lastStatement && !returnValue; 9814 bool needScope = !currentScope().lastStatement && !returnValue;
9202 bool extraScope = false; 9815 bool extraScope = false;
9203 if (with->assigns) { 9816 if (with->assign) {
9204 auto vars = getAssignVars(with); 9817 auto vars = getAssignVars(with);
9205 if (vars.front().empty() || isDeclaredAsGlobal(vars.front())) { 9818 if (vars.front().empty() || isDeclaredAsGlobal(vars.front())) {
9206 if (with->assigns->values.objects().size() == 1) { 9819 if (with->assign->values.objects().size() == 1) {
9207 auto var = singleVariableFrom(with->assigns->values.objects().front(), AccessType::Read); 9820 auto var = singleVariableFrom(with->assign->values.objects().front(), AccessType::Read);
9208 if (!var.empty() && isLocal(var)) { 9821 if (!var.empty() && isLocal(var)) {
9209 withVar = var; 9822 withVar = var;
9210 } 9823 }
@@ -9214,7 +9827,7 @@ private:
9214 auto assignment = x->new_ptr<ExpListAssign_t>(); 9827 auto assignment = x->new_ptr<ExpListAssign_t>();
9215 assignment->expList.set(toAst<ExpList_t>(withVar, x)); 9828 assignment->expList.set(toAst<ExpList_t>(withVar, x));
9216 auto assign = x->new_ptr<Assign_t>(); 9829 auto assign = x->new_ptr<Assign_t>();
9217 assign->values.push_back(with->assigns->values.objects().front()); 9830 assign->values.push_back(with->assign->values.objects().front());
9218 assignment->action.set(assign); 9831 assignment->action.set(assign);
9219 if (needScope) { 9832 if (needScope) {
9220 extraScope = true; 9833 extraScope = true;
@@ -9228,7 +9841,7 @@ private:
9228 auto assign = x->new_ptr<Assign_t>(); 9841 auto assign = x->new_ptr<Assign_t>();
9229 assign->values.push_back(toAst<Exp_t>(withVar, x)); 9842 assign->values.push_back(toAst<Exp_t>(withVar, x));
9230 bool skipFirst = true; 9843 bool skipFirst = true;
9231 for (auto value : with->assigns->values.objects()) { 9844 for (auto value : with->assign->values.objects()) {
9232 if (skipFirst) { 9845 if (skipFirst) {
9233 skipFirst = false; 9846 skipFirst = false;
9234 continue; 9847 continue;
@@ -9241,7 +9854,7 @@ private:
9241 withVar = vars.front(); 9854 withVar = vars.front();
9242 auto assignment = x->new_ptr<ExpListAssign_t>(); 9855 auto assignment = x->new_ptr<ExpListAssign_t>();
9243 assignment->expList.set(with->valueList); 9856 assignment->expList.set(with->valueList);
9244 assignment->action.set(with->assigns); 9857 assignment->action.set(with->assign);
9245 if (needScope) { 9858 if (needScope) {
9246 extraScope = true; 9859 extraScope = true;
9247 temp.push_back(indent() + "do"s + nll(with)); 9860 temp.push_back(indent() + "do"s + nll(with));
@@ -9319,15 +9932,57 @@ private:
9319 } 9932 }
9320 } 9933 }
9321 _withVars.push(withVar); 9934 _withVars.push(withVar);
9935 std::string breakWithVar;
9936 if (assignList || returnValue) {
9937 auto breakLoopType = getBreakLoopType(with->body, withVar);
9938 if (hasBreakWithValue(breakLoopType)) {
9939 breakWithVar = withVar;
9940 }
9941 }
9322 if (with->eop) { 9942 if (with->eop) {
9323 auto ifNode = x->new_ptr<If_t>(); 9943 auto ifNode = x->new_ptr<If_t>();
9324 ifNode->type.set(toAst<IfType_t>("if"sv, x)); 9944 ifNode->type.set(toAst<IfType_t>("if"sv, x));
9325 ifNode->nodes.push_back(toAst<IfCond_t>(withVar + "~=nil"s, x)); 9945 ifNode->nodes.push_back(toAst<IfCond_t>(withVar + "~=nil"s, x));
9326 ifNode->nodes.push_back(with->body); 9946 ifNode->nodes.push_back(with->body);
9327 transformIf(ifNode, temp, ExpUsage::Common); 9947 if (breakWithVar.empty()) {
9948 transformIf(ifNode, temp, ExpUsage::Common);
9949 } else {
9950 auto simpleValue = x->new_ptr<SimpleValue_t>();
9951 simpleValue->value.set(ifNode);
9952 auto exp = newExp(simpleValue, x);
9953 auto expList = x->new_ptr<ExpList_t>();
9954 expList->exprs.push_back(exp);
9955 auto expListAssign = x->new_ptr<ExpListAssign_t>();
9956 expListAssign->expList.set(expList);
9957 auto stmt = x->new_ptr<Statement_t>();
9958 stmt->content.set(expListAssign);
9959 auto repeatNode = toAst<Repeat_t>("repeat\n\t--\nuntil true"s, x);
9960 auto block = x->new_ptr<Block_t>();
9961 block->statements.push_back(stmt);
9962 repeatNode->body.set(block);
9963 auto sVal = x->new_ptr<SimpleValue_t>();
9964 sVal->value.set(repeatNode);
9965 auto asmt = assignmentFrom(toAst<Exp_t>(breakWithVar, x), newExp(sVal, x), x);
9966 transformAssignment(asmt, temp);
9967 }
9328 } else { 9968 } else {
9329 bool transformed = false; 9969 bool transformed = false;
9330 if (!extraScope && assignList) { 9970 if (!breakWithVar.empty()) {
9971 auto repeatNode = toAst<Repeat_t>("repeat\n\t--\nuntil true"s, x);
9972 auto block = x->new_ptr<Block_t>();
9973 if (auto blk = with->body.as<Block_t>()) {
9974 block->statements.dup(blk->statements);
9975 } else {
9976 auto stmt = with->body.to<Statement_t>();
9977 block->statements.push_back(stmt);
9978 }
9979 repeatNode->body.set(block);
9980 auto sVal = x->new_ptr<SimpleValue_t>();
9981 sVal->value.set(repeatNode);
9982 auto asmt = assignmentFrom(toAst<Exp_t>(breakWithVar, x), newExp(sVal, x), x);
9983 transformAssignment(asmt, temp);
9984 transformed = true;
9985 } else if (!extraScope && assignList) {
9331 if (auto block = with->body.as<Block_t>()) { 9986 if (auto block = with->body.as<Block_t>()) {
9332 if (!block->statements.empty()) { 9987 if (!block->statements.empty()) {
9333 Statement_t* stmt = static_cast<Statement_t*>(block->statements.back()); 9988 Statement_t* stmt = static_cast<Statement_t*>(block->statements.back());
@@ -9392,12 +10047,18 @@ private:
9392 switch (item->get_id()) { 10047 switch (item->get_id()) {
9393 case id<ClassDecl_t>(): { 10048 case id<ClassDecl_t>(): {
9394 auto classDecl = static_cast<ClassDecl_t*>(item); 10049 auto classDecl = static_cast<ClassDecl_t*>(item);
10050 std::string varName;
9395 if (classDecl->name) { 10051 if (classDecl->name) {
9396 if (auto var = classDecl->name->item.as<Variable_t>()) { 10052 if (auto var = classDecl->name->item.as<Variable_t>()) {
9397 addGlobalVar(variableToString(var), classDecl->name->item); 10053 varName = variableToString(var);
10054 addGlobalVar(varName, var);
9398 } 10055 }
9399 } 10056 }
10057 if (varName.empty()) {
10058 throw CompileError("missing name for class", classDecl);
10059 }
9400 transformClassDecl(classDecl, out, ExpUsage::Common); 10060 transformClassDecl(classDecl, out, ExpUsage::Common);
10061 markVarGlobalConst(varName);
9401 break; 10062 break;
9402 } 10063 }
9403 case id<GlobalOp_t>(): 10064 case id<GlobalOp_t>():
@@ -9411,9 +10072,11 @@ private:
9411 auto values = global->item.to<GlobalValues_t>(); 10072 auto values = global->item.to<GlobalValues_t>();
9412 if (values->valueList) { 10073 if (values->valueList) {
9413 auto expList = x->new_ptr<ExpList_t>(); 10074 auto expList = x->new_ptr<ExpList_t>();
10075 str_list varNames;
9414 for (auto name : values->nameList->names.objects()) { 10076 for (auto name : values->nameList->names.objects()) {
9415 auto var = static_cast<Variable_t*>(name); 10077 auto var = static_cast<Variable_t*>(name);
9416 addGlobalVar(variableToString(var), var); 10078 varNames.emplace_back(variableToString(var));
10079 addGlobalVar(varNames.back(), var);
9417 auto callable = x->new_ptr<Callable_t>(); 10080 auto callable = x->new_ptr<Callable_t>();
9418 callable->item.set(name); 10081 callable->item.set(name);
9419 auto chainValue = x->new_ptr<ChainValue_t>(); 10082 auto chainValue = x->new_ptr<ChainValue_t>();
@@ -9432,10 +10095,17 @@ private:
9432 } 10095 }
9433 assignment->action.set(assign); 10096 assignment->action.set(assign);
9434 transformAssignment(assignment, out); 10097 transformAssignment(assignment, out);
10098 for (const auto& name : varNames) {
10099 markVarGlobalConst(name);
10100 }
9435 } else { 10101 } else {
9436 for (auto name : values->nameList->names.objects()) { 10102 for (auto name : values->nameList->names.objects()) {
9437 auto var = static_cast<Variable_t*>(name); 10103 auto var = static_cast<Variable_t*>(name);
9438 addGlobalVar(variableToString(var), var); 10104 auto varName = variableToString(var);
10105 addGlobalVar(varName, var);
10106 if (global->constAttrib) {
10107 markVarGlobalConst(varName);
10108 }
9439 } 10109 }
9440 } 10110 }
9441 break; 10111 break;
@@ -9784,8 +10454,47 @@ private:
9784 out.push_back(join(temp)); 10454 out.push_back(join(temp));
9785 } 10455 }
9786 10456
9787 void transformTry(Try_t* tryNode, str_list& out, ExpUsage usage) { 10457 void transformTry(Try_t* tryNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) {
9788 auto x = tryNode; 10458 auto x = tryNode;
10459 if (tryNode->eop && usage == ExpUsage::Assignment) {
10460 str_list rets;
10461 pushScope();
10462 auto okVar = getUnusedName("_ok_"sv);
10463 for (size_t i = 0; i < assignList->exprs.size(); i++) {
10464 auto retVar = getUnusedName("_ret_"sv);
10465 rets.emplace_back(retVar);
10466 addToScope(retVar);
10467 }
10468 popScope();
10469 auto varList = join(rets, ","sv);
10470 auto ifNode = toAst<If_t>("if "s + okVar + ',' + varList + ":=try nil then "s + varList, x);
10471 auto exp = ast_to<IfCond_t>(ifNode->nodes.front())->assignment->assign->values.front();
10472 auto sVal = simpleSingleValueFrom(exp);
10473 auto newTry = sVal->value.to<Try_t>();
10474 newTry->func.set(tryNode->func);
10475 newTry->catchBlock.set(tryNode->catchBlock);
10476 auto assignment = x->new_ptr<ExpListAssign_t>();
10477 assignment->expList.set(assignList);
10478 auto assign = x->new_ptr<Assign_t>();
10479 assign->values.push_back(ifNode);
10480 assignment->action.set(assign);
10481 transformAssignment(assignment, out);
10482 return;
10483 }
10484 if (tryNode->eop && usage != ExpUsage::Common) {
10485 auto okVar = getUnusedName("_ok_"sv);
10486 auto code = "do\n\t"s + okVar + ", ... = try nil\n\t... if "s + okVar;
10487 auto doNode = toAst<Do_t>(code, x);
10488 auto block = doNode->body->content.to<Block_t>();
10489 auto asmt = static_cast<Statement_t*>(block->statements.front())->content.to<ExpListAssign_t>();
10490 auto assign = asmt->action.to<Assign_t>();
10491 auto sVal = simpleSingleValueFrom(assign->values.back());
10492 auto newTry = sVal->value.to<Try_t>();
10493 newTry->func.set(tryNode->func);
10494 newTry->catchBlock.set(tryNode->catchBlock);
10495 transformDo(doNode, out, usage);
10496 return;
10497 }
9789 ast_ptr<true, Exp_t> errHandler; 10498 ast_ptr<true, Exp_t> errHandler;
9790 if (tryNode->catchBlock) { 10499 if (tryNode->catchBlock) {
9791 auto catchBlock = tryNode->catchBlock.get(); 10500 auto catchBlock = tryNode->catchBlock.get();
@@ -10097,7 +10806,7 @@ private:
10097 out.push_back(join(temp)); 10806 out.push_back(join(temp));
10098 auto vars = getAssignVars(assignment); 10807 auto vars = getAssignVars(assignment);
10099 for (const auto& var : vars) { 10808 for (const auto& var : vars) {
10100 markVarConst(var); 10809 markVarLocalConst(var);
10101 } 10810 }
10102 } 10811 }
10103 10812
@@ -10325,12 +11034,36 @@ private:
10325 transformAssignment(assignment, out); 11034 transformAssignment(assignment, out);
10326 if (auto var = ast_cast<Variable_t>(target)) { 11035 if (auto var = ast_cast<Variable_t>(target)) {
10327 auto moduleName = variableToString(var); 11036 auto moduleName = variableToString(var);
10328 markVarConst(moduleName); 11037 markVarLocalConst(moduleName);
10329 } else { 11038 } else {
10330 markDestructureConst(assignment); 11039 markDestructureConst(assignment);
10331 } 11040 }
10332 } 11041 }
10333 11042
11043 void transformImportGlobal(ImportGlobal_t* importNode, str_list& out) {
11044 auto uname = static_cast<UnicodeName_t*>(importNode->segs.front());
11045 auto var = _parser.toString(uname);
11046 auto isNormal = _parser.match<Name_t>(var) && _parser.match<Variable_t>(var);
11047 auto varName = unicodeVariableFrom(uname);
11048 str_list temp;
11049 auto it = ++importNode->segs.objects().begin();
11050 for (; it != importNode->segs.objects().end(); ++it) {
11051 temp.emplace_back(_parser.toString(*it));
11052 }
11053 temp.emplace_front(var);
11054 if (isLocal(varName) || !isNormal) {
11055 temp.emplace_front("_G"s);
11056 }
11057 std::string stmt;
11058 if (importNode->target) {
11059 stmt = "const "s + _parser.toString(importNode->target) + '=' + join(temp, "."sv);
11060 } else {
11061 stmt = "const "s + temp.back() + '=' + join(temp, "."sv);
11062 }
11063 auto localAttrib = toAst<LocalAttrib_t>(stmt, importNode);
11064 transformLocalAttrib(localAttrib, out);
11065 }
11066
10334 void transformImport(Import_t* import, str_list& out) { 11067 void transformImport(Import_t* import, str_list& out) {
10335 auto content = import->content.get(); 11068 auto content = import->content.get();
10336 switch (content->get_id()) { 11069 switch (content->get_id()) {
@@ -10343,6 +11076,9 @@ private:
10343 case id<FromImport_t>(): 11076 case id<FromImport_t>():
10344 transformFromImport(static_cast<FromImport_t*>(content), out); 11077 transformFromImport(static_cast<FromImport_t*>(content), out);
10345 break; 11078 break;
11079 case id<ImportGlobal_t>():
11080 transformImportGlobal(static_cast<ImportGlobal_t*>(content), out);
11081 break;
10346 default: YUEE("AST node mismatch", content); break; 11082 default: YUEE("AST node mismatch", content); break;
10347 } 11083 }
10348 } 11084 }
@@ -10362,15 +11098,27 @@ private:
10362 addToScope(accumVar); 11098 addToScope(accumVar);
10363 auto lenVar = getUnusedName("_len_"sv); 11099 auto lenVar = getUnusedName("_len_"sv);
10364 addToScope(lenVar); 11100 addToScope(lenVar);
10365 temp.push_back(indent() + "local "s + accumVar + " = { }"s + nll(whileNode)); 11101 auto breakLoopType = getBreakLoopType(whileNode->body, accumVar);
10366 temp.push_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); 11102 _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(whileNode);
11103 temp.emplace_back(clearBuf());
11104 _buf << indent() << "local "s << lenVar << " = 1"s << nll(whileNode);
11105 auto& lenAssign = temp.emplace_back(clearBuf());
10367 bool isUntil = _parser.toString(whileNode->type) == "until"sv; 11106 bool isUntil = _parser.toString(whileNode->type) == "until"sv;
10368 auto condStr = transformCondExp(whileNode->condition, isUntil); 11107 auto condStr = transformCondExp(whileNode->condition, isUntil);
10369 temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); 11108 temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode));
10370 pushScope(); 11109 pushScope();
10371 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x); 11110 if (hasBreakWithValue(breakLoopType)) {
10372 auto lenLine = lenVar + " = "s + lenVar + " + 1"s + nlr(whileNode); 11111 lenAssign.clear();
10373 transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); 11112 transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Common);
11113 } else {
11114 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x);
11115 auto followStmt = toAst<Statement_t>(lenVar + "+=1"s, whileNode);
11116 assignLeft->followStmt = followStmt.get();
11117 transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft);
11118 if (!assignLeft->followStmtProcessed) {
11119 lenAssign.clear();
11120 }
11121 }
10374 popScope(); 11122 popScope();
10375 temp.push_back(indent() + "end"s + nlr(whileNode)); 11123 temp.push_back(indent() + "end"s + nlr(whileNode));
10376 if (expList) { 11124 if (expList) {
@@ -10406,15 +11154,26 @@ private:
10406 addToScope(accumVar); 11154 addToScope(accumVar);
10407 auto lenVar = getUnusedName("_len_"sv); 11155 auto lenVar = getUnusedName("_len_"sv);
10408 addToScope(lenVar); 11156 addToScope(lenVar);
10409 temp.push_back(indent() + "local "s + accumVar + " = { }"s + nll(whileNode)); 11157 auto breakLoopType = getBreakLoopType(whileNode->body, accumVar);
10410 temp.push_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); 11158 _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(whileNode);
11159 temp.emplace_back(clearBuf());
11160 auto& lenAssign = temp.emplace_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode));
10411 bool isUntil = _parser.toString(whileNode->type) == "until"sv; 11161 bool isUntil = _parser.toString(whileNode->type) == "until"sv;
10412 auto condStr = transformCondExp(whileNode->condition, isUntil); 11162 auto condStr = transformCondExp(whileNode->condition, isUntil);
10413 temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); 11163 temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode));
10414 pushScope(); 11164 pushScope();
10415 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x); 11165 if (hasBreakWithValue(breakLoopType)) {
10416 auto lenLine = lenVar + " = "s + lenVar + " + 1"s + nlr(whileNode); 11166 lenAssign.clear();
10417 transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); 11167 transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Common);
11168 } else {
11169 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x);
11170 auto followStmt = toAst<Statement_t>(lenVar + "+=1"s, whileNode);
11171 assignLeft->followStmt = followStmt.get();
11172 transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft);
11173 if (!assignLeft->followStmtProcessed) {
11174 lenAssign.clear();
11175 }
11176 }
10418 popScope(); 11177 popScope();
10419 temp.push_back(indent() + "end"s + nlr(whileNode)); 11178 temp.push_back(indent() + "end"s + nlr(whileNode));
10420 temp.push_back(indent() + "return "s + accumVar + nlr(whileNode)); 11179 temp.push_back(indent() + "return "s + accumVar + nlr(whileNode));
@@ -10449,9 +11208,7 @@ private:
10449 expListAssign->expList.set(expList); 11208 expListAssign->expList.set(expList);
10450 auto stmt = x->new_ptr<Statement_t>(); 11209 auto stmt = x->new_ptr<Statement_t>();
10451 stmt->content.set(expListAssign); 11210 stmt->content.set(expListAssign);
10452 auto body = x->new_ptr<Body_t>(); 11211 repeat->body.set(stmt);
10453 body->content.set(stmt);
10454 repeat->body.set(body);
10455 transformRepeat(repeat, out); 11212 transformRepeat(repeat, out);
10456 return; 11213 return;
10457 } 11214 }
@@ -10459,7 +11216,8 @@ private:
10459 pushScope(); 11216 pushScope();
10460 bool isUntil = _parser.toString(whileNode->type) == "until"sv; 11217 bool isUntil = _parser.toString(whileNode->type) == "until"sv;
10461 auto condStr = transformCondExp(whileNode->condition, isUntil); 11218 auto condStr = transformCondExp(whileNode->condition, isUntil);
10462 transformLoopBody(whileNode->body, temp, Empty, ExpUsage::Common); 11219 auto breakLoopType = getBreakLoopType(whileNode->body, Empty);
11220 transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Common);
10463 popScope(); 11221 popScope();
10464 _buf << indent() << "while "sv << condStr << " do"sv << nll(whileNode); 11222 _buf << indent() << "while "sv << condStr << " do"sv << nll(whileNode);
10465 _buf << temp.back(); 11223 _buf << temp.back();
@@ -10467,6 +11225,106 @@ private:
10467 out.push_back(clearBuf()); 11225 out.push_back(clearBuf());
10468 } 11226 }
10469 11227
11228 void transformRepeatInPlace(Repeat_t* repeatNode, str_list& out, ExpList_t* expList = nullptr) {
11229 auto x = repeatNode;
11230 str_list temp;
11231 bool extraScope = false;
11232 if (expList) {
11233 if (!currentScope().lastStatement) {
11234 extraScope = true;
11235 temp.push_back(indent() + "do"s + nll(repeatNode));
11236 pushScope();
11237 }
11238 }
11239 auto accumVar = getUnusedName("_accum_"sv);
11240 addToScope(accumVar);
11241 auto lenVar = getUnusedName("_len_"sv);
11242 addToScope(lenVar);
11243 auto breakLoopType = getBreakLoopType(repeatNode->body, accumVar);
11244 _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(repeatNode);
11245 temp.emplace_back(clearBuf());
11246 _buf << indent() << "local "s << lenVar << " = 1"s << nll(repeatNode);
11247 auto& lenAssign = temp.emplace_back(clearBuf());
11248 auto condStr = transformCondExp(repeatNode->condition, false);
11249 temp.push_back(indent() + "repeat"s + nll(repeatNode));
11250 pushScope();
11251 if (hasBreakWithValue(breakLoopType)) {
11252 lenAssign.clear();
11253 transformLoopBody(repeatNode->body, temp, breakLoopType, ExpUsage::Common);
11254 } else {
11255 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x);
11256 auto followStmt = toAst<Statement_t>(lenVar + "+=1"s, repeatNode);
11257 assignLeft->followStmt = followStmt.get();
11258 transformLoopBody(repeatNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft);
11259 if (!assignLeft->followStmtProcessed) {
11260 lenAssign.clear();
11261 }
11262 }
11263 popScope();
11264 temp.push_back(indent() + "until "s + condStr + nlr(repeatNode));
11265 if (expList) {
11266 auto assign = x->new_ptr<Assign_t>();
11267 assign->values.push_back(toAst<Exp_t>(accumVar, x));
11268 auto assignment = x->new_ptr<ExpListAssign_t>();
11269 assignment->expList.set(expList);
11270 assignment->action.set(assign);
11271 transformAssignment(assignment, temp);
11272 if (extraScope) popScope();
11273 } else {
11274 temp.push_back(indent() + "return "s + accumVar + nlr(repeatNode));
11275 }
11276 if (expList && extraScope) {
11277 temp.push_back(indent() + "end"s + nlr(repeatNode));
11278 }
11279 out.push_back(join(temp));
11280 }
11281
11282 void transformRepeatClosure(Repeat_t* repeatNode, str_list& out) {
11283 auto x = repeatNode;
11284 auto simpleValue = x->new_ptr<SimpleValue_t>();
11285 simpleValue->value.set(repeatNode);
11286 if (transformAsUpValueFunc(newExp(simpleValue, x), out)) {
11287 return;
11288 }
11289 str_list temp;
11290 pushAnonFunctionScope();
11291 pushAnonVarArg();
11292 std::string& funcStart = temp.emplace_back();
11293 pushScope();
11294 auto accumVar = getUnusedName("_accum_"sv);
11295 addToScope(accumVar);
11296 auto lenVar = getUnusedName("_len_"sv);
11297 addToScope(lenVar);
11298 auto breakLoopType = getBreakLoopType(repeatNode->body, accumVar);
11299 _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(repeatNode);
11300 temp.emplace_back(clearBuf());
11301 auto& lenAssign = temp.emplace_back(indent() + "local "s + lenVar + " = 1"s + nll(repeatNode));
11302 auto condStr = transformCondExp(repeatNode->condition, false);
11303 temp.push_back(indent() + "repeat"s + nll(repeatNode));
11304 pushScope();
11305 if (hasBreakWithValue(breakLoopType)) {
11306 lenAssign.clear();
11307 transformLoopBody(repeatNode->body, temp, breakLoopType, ExpUsage::Common);
11308 } else {
11309 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x);
11310 auto followStmt = toAst<Statement_t>(lenVar + "+=1"s, repeatNode);
11311 assignLeft->followStmt = followStmt.get();
11312 transformLoopBody(repeatNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft);
11313 if (!assignLeft->followStmtProcessed) {
11314 lenAssign.clear();
11315 }
11316 }
11317 popScope();
11318 temp.push_back(indent() + "until "s + condStr + nlr(repeatNode));
11319 temp.push_back(indent() + "return "s + accumVar + nlr(repeatNode));
11320 popScope();
11321 funcStart = anonFuncStart() + nll(repeatNode);
11322 temp.push_back(indent() + anonFuncEnd());
11323 popAnonVarArg();
11324 popFunctionScope();
11325 out.push_back(join(temp));
11326 }
11327
10470 void transformRepeat(Repeat_t* repeat, str_list& out) { 11328 void transformRepeat(Repeat_t* repeat, str_list& out) {
10471 str_list temp; 11329 str_list temp;
10472 pushScope(); 11330 pushScope();
@@ -10500,10 +11358,26 @@ private:
10500 pushScope(); 11358 pushScope();
10501 } 11359 }
10502 bool extraScope = false; 11360 bool extraScope = false;
11361 if (switchNode->assignment) {
11362 if (needScope) {
11363 extraScope = true;
11364 temp.push_back(indent() + "do"s + nll(x));
11365 pushScope();
11366 }
11367 auto asmt = x->new_ptr<ExpListAssign_t>();
11368 auto expList = x->new_ptr<ExpList_t>();
11369 expList->exprs.push_back(switchNode->target);
11370 if (switchNode->assignment->expList) {
11371 expList->exprs.dup(switchNode->assignment->expList->exprs);
11372 }
11373 asmt->expList.set(expList);
11374 asmt->action.set(switchNode->assignment->assign);
11375 transformAssignment(asmt, temp);
11376 }
10503 auto objVar = singleVariableFrom(switchNode->target, AccessType::Read); 11377 auto objVar = singleVariableFrom(switchNode->target, AccessType::Read);
10504 if (objVar.empty() || !isLocal(objVar)) { 11378 if (objVar.empty() || !isLocal(objVar)) {
10505 if (usage == ExpUsage::Common || usage == ExpUsage::Assignment) { 11379 if (usage == ExpUsage::Common || usage == ExpUsage::Assignment) {
10506 if (needScope) { 11380 if (needScope && !extraScope) {
10507 extraScope = true; 11381 extraScope = true;
10508 temp.push_back(indent() + "do"s + nll(x)); 11382 temp.push_back(indent() + "do"s + nll(x));
10509 pushScope(); 11383 pushScope();
@@ -10561,8 +11435,9 @@ private:
10561 } 11435 }
10562 temp.back().append(indent() + "if "s + tabCheckVar + " then"s + nll(branch)); 11436 temp.back().append(indent() + "if "s + tabCheckVar + " then"s + nll(branch));
10563 pushScope(); 11437 pushScope();
10564 auto assignment = assignmentFrom(static_cast<Exp_t*>(valueList->exprs.front()), toAst<Exp_t>(objVar, branch), branch); 11438 auto chainValue = toAst<ChainValue_t>(objVar, branch);
10565 auto info = extractDestructureInfo(assignment, true, false); 11439 auto assignment = assignmentFrom(static_cast<Exp_t*>(valueList->exprs.front()), newExp(chainValue, branch), branch);
11440 auto info = extractDestructureInfo(assignment, true, true);
10566 transformAssignment(assignment, temp, true); 11441 transformAssignment(assignment, temp, true);
10567 str_list conds; 11442 str_list conds;
10568 for (const auto& des : info.destructures) { 11443 for (const auto& des : info.destructures) {
@@ -10572,8 +11447,31 @@ private:
10572 const auto& destruct = std::get<Destructure>(des); 11447 const auto& destruct = std::get<Destructure>(des);
10573 for (const auto& item : destruct.items) { 11448 for (const auto& item : destruct.items) {
10574 if (!item.defVal) { 11449 if (!item.defVal) {
10575 transformExp(item.target, conds, ExpUsage::Closure); 11450 if (!isAssignable(item.target)) {
10576 conds.back().append(" ~= nil"s); 11451 auto callable = chainValue->items.front();
11452 auto chain = callable->new_ptr<ChainValue_t>();
11453 chain->items.push_back(callable);
11454 chain->items.dup(item.structure->items);
11455 if (specialChainValue(chain) == ChainType::Common) {
11456 transformChainValue(chain, conds, ExpUsage::Closure);
11457 auto vStr = conds.back();
11458 conds.pop_back();
11459 transformExp(item.target, conds, ExpUsage::Closure);
11460 conds.back().append(" == "s);
11461 conds.back().append(vStr);
11462 } else {
11463 auto varName = getUnusedName("_val_"sv);
11464 auto vExp = toAst<Exp_t>(varName, chain);
11465 auto asmt = assignmentFrom(vExp, newExp(chain, chain), chain);
11466 transformAssignment(asmt, temp);
11467 transformExp(item.target, conds, ExpUsage::Closure);
11468 conds.back().append(" == "s);
11469 conds.back().append(varName);
11470 }
11471 } else {
11472 transformExp(item.target, conds, ExpUsage::Closure);
11473 conds.back().append(" ~= nil"s);
11474 }
10577 } 11475 }
10578 } 11476 }
10579 } 11477 }
@@ -10871,7 +11769,7 @@ private:
10871 } 11769 }
10872 transformAssignment(assignment, temp); 11770 transformAssignment(assignment, temp);
10873 for (const auto& name : vars) { 11771 for (const auto& name : vars) {
10874 markVarConst(name); 11772 markVarLocalConst(name);
10875 } 11773 }
10876 if (localAttrib->attrib.is<CloseAttrib_t>()) { 11774 if (localAttrib->attrib.is<CloseAttrib_t>()) {
10877 str_list leftVars, rightVars; 11775 str_list leftVars, rightVars;
@@ -10943,7 +11841,7 @@ private:
10943 temp.push_back(indent() + "local "s + join(leftVars, ", "sv) + " = "s + join(items, ", "sv) + nll(x)); 11841 temp.push_back(indent() + "local "s + join(leftVars, ", "sv) + " = "s + join(items, ", "sv) + nll(x));
10944 } 11842 }
10945 for (const auto& var : vars) { 11843 for (const auto& var : vars) {
10946 markVarConst(var); 11844 markVarLocalConst(var);
10947 } 11845 }
10948 } 11846 }
10949 if (!listB->exprs.empty()) { 11847 if (!listB->exprs.empty()) {
@@ -10968,18 +11866,24 @@ private:
10968 temp.push_back(indent() + "local "s + join(vars, ", "sv) + nll(x)); 11866 temp.push_back(indent() + "local "s + join(vars, ", "sv) + nll(x));
10969 transformAssignment(assignment, temp); 11867 transformAssignment(assignment, temp);
10970 for (const auto& name : vars) { 11868 for (const auto& name : vars) {
10971 markVarConst(name); 11869 markVarLocalConst(name);
10972 } 11870 }
10973 } 11871 }
10974 out.push_back(join(temp)); 11872 out.push_back(join(temp));
10975 } 11873 }
10976 11874
10977 void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { 11875 void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) {
10978 auto keyword = _parser.toString(breakLoop); 11876 auto isBreak = breakLoop->type.is<Break_t>();
11877 auto keyword = isBreak ? "break"s : "continue"s;
10979 if (_enableBreakLoop.empty() || !_enableBreakLoop.top()) { 11878 if (_enableBreakLoop.empty() || !_enableBreakLoop.top()) {
10980 throw CompileError(keyword + " is not inside a loop"s, breakLoop); 11879 throw CompileError(keyword + " is not inside a loop"s, breakLoop);
10981 } 11880 }
10982 if (keyword == "break"sv) { 11881 if (isBreak) {
11882 if (breakLoop->value) {
11883 auto exp = toAst<Exp_t>(breakLoop->varBWV, breakLoop->value);
11884 auto assignment = assignmentFrom(exp, breakLoop->value, breakLoop);
11885 transformAssignment(assignment, out);
11886 }
10983 out.push_back(indent() + keyword + nll(breakLoop)); 11887 out.push_back(indent() + keyword + nll(breakLoop));
10984 return; 11888 return;
10985 } 11889 }
diff --git a/src/yuescript/yue_compiler.h b/src/yuescript/yue_compiler.h
index d352636..aff5978 100644
--- a/src/yuescript/yue_compiler.h
+++ b/src/yuescript/yue_compiler.h
@@ -31,6 +31,7 @@ struct YueConfig {
31 bool reserveLineNumber = true; 31 bool reserveLineNumber = true;
32 bool useSpaceOverTab = false; 32 bool useSpaceOverTab = false;
33 bool reserveComment = false; 33 bool reserveComment = false;
34 bool lax = false;
34 // internal options 35 // internal options
35 bool exporting = false; 36 bool exporting = false;
36 bool profiling = false; 37 bool profiling = false;
diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp
index ceb1f7c..1942e23 100644
--- a/src/yuescript/yue_parser.cpp
+++ b/src/yuescript/yue_parser.cpp
@@ -67,24 +67,28 @@ YueParser::YueParser() {
67 num_char = range('0', '9') >> *(range('0', '9') | '_' >> and_(range('0', '9'))); 67 num_char = range('0', '9') >> *(range('0', '9') | '_' >> and_(range('0', '9')));
68 num_char_hex = range('0', '9') | range('a', 'f') | range('A', 'F'); 68 num_char_hex = range('0', '9') | range('a', 'f') | range('A', 'F');
69 num_lit = num_char_hex >> *(num_char_hex | '_' >> and_(num_char_hex)); 69 num_lit = num_char_hex >> *(num_char_hex | '_' >> and_(num_char_hex));
70 num_bin_lit = set("01") >> *(set("01") | '_' >> and_(set("01")));
70 Num = 71 Num =
71 "0x" >> ( 72 '0' >> (
72 +num_lit >> ( 73 set("xX") >> (
73 '.' >> +num_lit >> -num_expo_hex | 74 num_lit >> (
74 num_expo_hex | 75 '.' >> num_lit >> -num_expo_hex |
75 lj_num | 76 num_expo_hex |
76 true_() 77 lj_num |
77 ) | ( 78 true_()
78 '.' >> +num_lit >> -num_expo_hex 79 ) | (
79 ) 80 '.' >> num_lit >> -num_expo_hex
81 )
82 ) |
83 set("bB") >> num_bin_lit
80 ) | 84 ) |
81 +num_char >> ( 85 num_char >> (
82 '.' >> +num_char >> -num_expo | 86 '.' >> num_char >> -num_expo |
83 num_expo | 87 num_expo |
84 lj_num | 88 lj_num |
85 true_() 89 true_()
86 ) | 90 ) |
87 '.' >> +num_char >> -num_expo; 91 '.' >> num_char >> -num_expo;
88 92
89 cut = false_(); 93 cut = false_();
90 Seperator = true_(); 94 Seperator = true_();
@@ -114,8 +118,8 @@ YueParser::YueParser() {
114 return false; 118 return false;
115 }); 119 });
116 120
117 if_assignment_syntax_error = pl::user(true_(), [](const item_t& item) { 121 assignment_expression_syntax_error = pl::user(true_(), [](const item_t& item) {
118 throw ParserError("use := for if-assignment expression"sv, item.begin); 122 throw ParserError("use := for assignment expression"sv, item.begin);
119 return false; 123 return false;
120 }); 124 });
121 125
@@ -158,6 +162,13 @@ YueParser::YueParser() {
158 ) \ 162 ) \
159 ) 163 )
160 164
165 #define disable_until_rule(patt) ( \
166 disable_until >> ( \
167 (patt) >> enable_until | \
168 enable_until >> cut \
169 ) \
170 )
171
161 #define body_with(str) ( \ 172 #define body_with(str) ( \
162 key(str) >> space >> (in_block | Statement) | \ 173 key(str) >> space >> (in_block | Statement) | \
163 in_block | \ 174 in_block | \
@@ -321,7 +332,7 @@ YueParser::YueParser() {
321 Exp; 332 Exp;
322 import_tab_list = import_tab_item >> *(space >> ',' >> space >> import_tab_item); 333 import_tab_list = import_tab_item >> *(space >> ',' >> space >> import_tab_item);
323 import_tab_line = ( 334 import_tab_line = (
324 push_indent_match >> (space >> import_tab_list >> pop_indent | pop_indent) 335 push_indent_match >> ensure(space >> import_tab_list, pop_indent)
325 ) | space; 336 ) | space;
326 import_tab_lines = space_break >> import_tab_line >> *(-(space >> ',') >> space_break >> import_tab_line) >> -(space >> ','); 337 import_tab_lines = space_break >> import_tab_line >> *(-(space >> ',') >> space_break >> import_tab_line) >> -(space >> ',');
327 import_tab_key_value = key_value | ':' >> MacroName | MacroNamePair | ImportAllMacro; 338 import_tab_key_value = key_value | ':' >> MacroName | MacroNamePair | ImportAllMacro;
@@ -338,7 +349,9 @@ YueParser::YueParser() {
338 349
339 ImportAs = ImportLiteral >> -(space >> key("as") >> space >> (ImportTabLit | Variable | ImportAllMacro)); 350 ImportAs = ImportLiteral >> -(space >> key("as") >> space >> (ImportTabLit | Variable | ImportAllMacro));
340 351
341 Import = key("import") >> space >> (ImportAs | ImportFrom) | FromImport; 352 ImportGlobal = Seperator >> UnicodeName >> *('.' >> UnicodeName) >> -(space >> key("as") >> space >> Variable);
353
354 Import = key("import") >> space >> (ImportAs | ImportFrom | ImportGlobal) | FromImport;
342 355
343 Label = "::" >> LabelName >> "::"; 356 Label = "::" >> LabelName >> "::";
344 357
@@ -346,11 +359,13 @@ YueParser::YueParser() {
346 359
347 ShortTabAppending = "[]" >> space >> Assign; 360 ShortTabAppending = "[]" >> space >> Assign;
348 361
349 BreakLoop = (expr("break") | "continue") >> not_alpha_num; 362 Break = key("break");
363 Continue = key("continue");
364 BreakLoop = (Break >> -(space >> Exp) | Continue) >> not_alpha_num;
350 365
351 Return = key("return") >> -(space >> (TableBlock | ExpListLow)); 366 Return = key("return") >> -(space >> (TableBlock | ExpListLow));
352 367
353 with_exp = ExpList >> -(space >> Assign); 368 with_exp = ExpList >> -(space >> (':' >> Assign | and_('=') >> assignment_expression_syntax_error));
354 369
355 With = key("with") >> -ExistentialOp >> space >> disable_do_chain_arg_table_block_rule(with_exp) >> space >> body_with("do"); 370 With = key("with") >> -ExistentialOp >> space >> disable_do_chain_arg_table_block_rule(with_exp) >> space >> body_with("do");
356 SwitchCase = key("when") >> space >> disable_chain_rule(disable_arg_table_block_rule(SwitchList)) >> space >> body_with("then"); 371 SwitchCase = key("when") >> space >> disable_chain_rule(disable_arg_table_block_rule(SwitchList)) >> space >> body_with("then");
@@ -366,7 +381,8 @@ YueParser::YueParser() {
366 and_(SimpleTable | TableLit) >> Exp | 381 and_(SimpleTable | TableLit) >> Exp |
367 exp_not_tab >> *(space >> ',' >> space >> exp_not_tab) 382 exp_not_tab >> *(space >> ',' >> space >> exp_not_tab)
368 ); 383 );
369 Switch = key("switch") >> space >> Exp >> 384 Switch = key("switch") >> space >>
385 Exp >> -(space >> Assignment) >>
370 space >> Seperator >> ( 386 space >> Seperator >> (
371 SwitchCase >> space >> ( 387 SwitchCase >> space >> (
372 switch_block | 388 switch_block |
@@ -375,20 +391,26 @@ YueParser::YueParser() {
375 +space_break >> advance_match >> space >> SwitchCase >> switch_block >> pop_indent 391 +space_break >> advance_match >> space >> SwitchCase >> switch_block >> pop_indent
376 ); 392 );
377 393
378 Assignment = -(',' >> space >> ExpList >> space) >> (':' >> Assign | and_('=') >> if_assignment_syntax_error); 394 Assignment = -(',' >> space >> ExpList >> space) >> (':' >> Assign | and_('=') >> assignment_expression_syntax_error);
379 IfCond = disable_chain_rule(disable_arg_table_block_rule(Exp >> -(space >> Assignment))); 395 IfCond = disable_chain_rule(disable_arg_table_block_rule(Exp >> -(space >> Assignment)));
380 if_else_if = -(line_break >> *space_break >> check_indent_match) >> space >> key("elseif") >> space >> IfCond >> space >> body_with("then"); 396 if_else_if = -(line_break >> *space_break >> check_indent_match) >> space >> key("elseif") >> space >> IfCond >> space >> body_with("then");
381 if_else = -(line_break >> *space_break >> check_indent_match) >> space >> key("else") >> space >> body; 397 if_else = -(line_break >> *space_break >> check_indent_match) >> space >> key("else") >> space >> body;
382 IfType = (expr("if") | "unless") >> not_alpha_num; 398 IfType = (expr("if") | "unless") >> not_alpha_num;
383 If = IfType >> space >> IfCond >> space >> opt_body_with("then") >> *if_else_if >> -if_else; 399 If = IfType >> space >> IfCond >> space >> opt_body_with("then") >> *if_else_if >> -if_else;
384 400
385 WhileType = (expr("while") | "until") >> not_alpha_num; 401 WhileType = (expr("while") | pl::user("until", [](const item_t& item) {
386 While = WhileType >> space >> disable_do_chain_arg_table_block_rule(Exp >> -(space >> Assignment)) >> space >> opt_body_with("do"); 402 State* st = reinterpret_cast<State*>(item.user_data);
387 Repeat = key("repeat") >> space >> Body >> line_break >> *space_break >> check_indent_match >> space >> key("until") >> space >> Exp; 403 return st->noUntilStack.empty() || !st->noUntilStack.back();
404 })) >> not_alpha_num;
405 While = key(WhileType) >> space >> disable_do_chain_arg_table_block_rule(Exp >> -(space >> Assignment)) >> space >> opt_body_with("do");
406 Repeat = key("repeat") >> space >> (
407 in_block >> line_break >> *space_break >> check_indent_match |
408 disable_until_rule(Statement)
409 ) >> space >> key("until") >> space >> Exp;
388 410
389 for_key = pl::user(key("for"), [](const item_t& item) { 411 for_key = pl::user(key("for"), [](const item_t& item) {
390 State* st = reinterpret_cast<State*>(item.user_data); 412 State* st = reinterpret_cast<State*>(item.user_data);
391 return st->noForStack.empty() || !st->noForStack.top(); 413 return st->noForStack.empty() || !st->noForStack.back();
392 }); 414 });
393 ForStepValue = ',' >> space >> Exp; 415 ForStepValue = ',' >> space >> Exp;
394 for_args = Variable >> space >> '=' >> space >> Exp >> space >> ',' >> space >> Exp >> space >> -ForStepValue; 416 for_args = Variable >> space >> '=' >> space >> Exp >> space >> ',' >> space >> Exp >> space >> -ForStepValue;
@@ -402,18 +424,18 @@ YueParser::YueParser() {
402 424
403 Do = pl::user(key("do"), [](const item_t& item) { 425 Do = pl::user(key("do"), [](const item_t& item) {
404 State* st = reinterpret_cast<State*>(item.user_data); 426 State* st = reinterpret_cast<State*>(item.user_data);
405 return st->noDoStack.empty() || !st->noDoStack.top(); 427 return st->noDoStack.empty() || !st->noDoStack.back();
406 }) >> space >> Body; 428 }) >> space >> Body;
407 429
408 disable_do = pl::user(true_(), [](const item_t& item) { 430 disable_do = pl::user(true_(), [](const item_t& item) {
409 State* st = reinterpret_cast<State*>(item.user_data); 431 State* st = reinterpret_cast<State*>(item.user_data);
410 st->noDoStack.push(true); 432 st->noDoStack.push_back(true);
411 return true; 433 return true;
412 }); 434 });
413 435
414 enable_do = pl::user(true_(), [](const item_t& item) { 436 enable_do = pl::user(true_(), [](const item_t& item) {
415 State* st = reinterpret_cast<State*>(item.user_data); 437 State* st = reinterpret_cast<State*>(item.user_data);
416 st->noDoStack.pop(); 438 st->noDoStack.pop_back();
417 return true; 439 return true;
418 }); 440 });
419 441
@@ -431,46 +453,58 @@ YueParser::YueParser() {
431 453
432 disable_do_chain_arg_table_block = pl::user(true_(), [](const item_t& item) { 454 disable_do_chain_arg_table_block = pl::user(true_(), [](const item_t& item) {
433 State* st = reinterpret_cast<State*>(item.user_data); 455 State* st = reinterpret_cast<State*>(item.user_data);
434 st->noDoStack.push(true); 456 st->noDoStack.push_back(true);
435 st->noChainBlockStack.push(true); 457 st->noChainBlockStack.push_back(true);
436 st->noTableBlockStack.push(true); 458 st->noTableBlockStack.push_back(true);
437 return true; 459 return true;
438 }); 460 });
439 461
440 enable_do_chain_arg_table_block = pl::user(true_(), [](const item_t& item) { 462 enable_do_chain_arg_table_block = pl::user(true_(), [](const item_t& item) {
441 State* st = reinterpret_cast<State*>(item.user_data); 463 State* st = reinterpret_cast<State*>(item.user_data);
442 st->noDoStack.pop(); 464 st->noDoStack.pop_back();
443 st->noChainBlockStack.pop(); 465 st->noChainBlockStack.pop_back();
444 st->noTableBlockStack.pop(); 466 st->noTableBlockStack.pop_back();
445 return true; 467 return true;
446 }); 468 });
447 469
448 disable_arg_table_block = pl::user(true_(), [](const item_t& item) { 470 disable_arg_table_block = pl::user(true_(), [](const item_t& item) {
449 State* st = reinterpret_cast<State*>(item.user_data); 471 State* st = reinterpret_cast<State*>(item.user_data);
450 st->noTableBlockStack.push(true); 472 st->noTableBlockStack.push_back(true);
451 return true; 473 return true;
452 }); 474 });
453 475
454 enable_arg_table_block = pl::user(true_(), [](const item_t& item) { 476 enable_arg_table_block = pl::user(true_(), [](const item_t& item) {
455 State* st = reinterpret_cast<State*>(item.user_data); 477 State* st = reinterpret_cast<State*>(item.user_data);
456 st->noTableBlockStack.pop(); 478 st->noTableBlockStack.pop_back();
457 return true; 479 return true;
458 }); 480 });
459 481
460 disable_for = pl::user(true_(), [](const item_t& item) { 482 disable_for = pl::user(true_(), [](const item_t& item) {
461 State* st = reinterpret_cast<State*>(item.user_data); 483 State* st = reinterpret_cast<State*>(item.user_data);
462 st->noForStack.push(true); 484 st->noForStack.push_back(true);
463 return true; 485 return true;
464 }); 486 });
465 487
466 enable_for = pl::user(true_(), [](const item_t& item) { 488 enable_for = pl::user(true_(), [](const item_t& item) {
467 State* st = reinterpret_cast<State*>(item.user_data); 489 State* st = reinterpret_cast<State*>(item.user_data);
468 st->noForStack.pop(); 490 st->noForStack.pop_back();
491 return true;
492 });
493
494 disable_until = pl::user(true_(), [](const item_t& item) {
495 State* st = reinterpret_cast<State*>(item.user_data);
496 st->noUntilStack.push_back(true);
497 return true;
498 });
499
500 enable_until = pl::user(true_(), [](const item_t& item) {
501 State* st = reinterpret_cast<State*>(item.user_data);
502 st->noUntilStack.pop_back();
469 return true; 503 return true;
470 }); 504 });
471 505
472 CatchBlock = line_break >> *space_break >> check_indent_match >> space >> key("catch") >> space >> Variable >> space >> in_block; 506 CatchBlock = line_break >> *space_break >> check_indent_match >> space >> key("catch") >> space >> Variable >> space >> in_block;
473 Try = key("try") >> space >> (in_block | Exp) >> -CatchBlock; 507 Try = key("try") >> -ExistentialOp >> space >> (in_block | Exp) >> -CatchBlock;
474 508
475 list_value = 509 list_value =
476 and_( 510 and_(
@@ -557,20 +591,20 @@ YueParser::YueParser() {
557 591
558 disable_chain = pl::user(true_(), [](const item_t& item) { 592 disable_chain = pl::user(true_(), [](const item_t& item) {
559 State* st = reinterpret_cast<State*>(item.user_data); 593 State* st = reinterpret_cast<State*>(item.user_data);
560 st->noChainBlockStack.push(true); 594 st->noChainBlockStack.push_back(true);
561 return true; 595 return true;
562 }); 596 });
563 597
564 enable_chain = pl::user(true_(), [](const item_t& item) { 598 enable_chain = pl::user(true_(), [](const item_t& item) {
565 State* st = reinterpret_cast<State*>(item.user_data); 599 State* st = reinterpret_cast<State*>(item.user_data);
566 st->noChainBlockStack.pop(); 600 st->noChainBlockStack.pop_back();
567 return true; 601 return true;
568 }); 602 });
569 603
570 chain_line = check_indent_match >> space >> (chain_dot_chain | colon_chain) >> -InvokeArgs; 604 chain_line = check_indent_match >> space >> (chain_dot_chain | colon_chain) >> -InvokeArgs;
571 chain_block = pl::user(true_(), [](const item_t& item) { 605 chain_block = pl::user(true_(), [](const item_t& item) {
572 State* st = reinterpret_cast<State*>(item.user_data); 606 State* st = reinterpret_cast<State*>(item.user_data);
573 return st->noChainBlockStack.empty() || !st->noChainBlockStack.top(); 607 return st->noChainBlockStack.empty() || !st->noChainBlockStack.back();
574 }) >> +space_break >> advance_match >> ensure( 608 }) >> +space_break >> advance_match >> ensure(
575 chain_line >> *(+space_break >> chain_line), pop_indent); 609 chain_line >> *(+space_break >> chain_line), pop_indent);
576 ChainValue = 610 ChainValue =
@@ -607,7 +641,15 @@ YueParser::YueParser() {
607 DoubleStringInner = +(not_("#{") >> double_string_plain); 641 DoubleStringInner = +(not_("#{") >> double_string_plain);
608 DoubleStringContent = DoubleStringInner | interp; 642 DoubleStringContent = DoubleStringInner | interp;
609 DoubleString = '"' >> Seperator >> *DoubleStringContent >> '"'; 643 DoubleString = '"' >> Seperator >> *DoubleStringContent >> '"';
610 String = DoubleString | SingleString | LuaString; 644
645 YAMLIndent = +set(" \t");
646 YAMLLineInner = +('\\' >> set("\"\\#") | not_("#{" | stop) >> any_char);
647 YAMLLineContent = YAMLLineInner | interp;
648 YAMLLine = check_indent_match >> YAMLIndent >> +(YAMLLineContent) |
649 advance_match >> YAMLIndent >> ensure(+YAMLLineContent, pop_indent);
650 YAMLMultiline = '|' >> space >> Seperator >> +(*set(" \t") >> line_break) >> advance_match >> ensure(YAMLLine >> *(+(*set(" \t") >> line_break) >> YAMLLine), pop_indent);
651
652 String = DoubleString | SingleString | LuaString | YAMLMultiline;
611 653
612 lua_string_open = '[' >> *expr('=') >> '['; 654 lua_string_open = '[' >> *expr('=') >> '[';
613 lua_string_close = ']' >> *expr('=') >> ']'; 655 lua_string_close = ']' >> *expr('=') >> ']';
@@ -635,7 +677,7 @@ YueParser::YueParser() {
635 fn_args_value_list = Exp >> *(space >> ',' >> space >> Exp); 677 fn_args_value_list = Exp >> *(space >> ',' >> space >> Exp);
636 678
637 fn_args_lit_line = ( 679 fn_args_lit_line = (
638 push_indent_match >> (space >> fn_args_value_list >> pop_indent | pop_indent) 680 push_indent_match >> ensure(space >> fn_args_value_list, pop_indent)
639 ) | ( 681 ) | (
640 space 682 space
641 ); 683 );
@@ -677,7 +719,8 @@ YueParser::YueParser() {
677 chain_with_colon = +chain_item >> -colon_chain; 719 chain_with_colon = +chain_item >> -colon_chain;
678 chain_items = chain_with_colon | colon_chain; 720 chain_items = chain_with_colon | colon_chain;
679 721
680 index = '[' >> not_('[') >> space >> Exp >> space >> ']'; 722 index = '[' >> not_('[') >> space >> (ReversedIndex >> and_(space >> ']') | Exp) >> space >> ']';
723 ReversedIndex = '#' >> space >> -('-' >> space >> Exp);
681 chain_item = 724 chain_item =
682 Invoke >> -ExistentialOp | 725 Invoke >> -ExistentialOp |
683 DotChainItem >> -ExistentialOp | 726 DotChainItem >> -ExistentialOp |
@@ -734,7 +777,7 @@ YueParser::YueParser() {
734 777
735 table_block_inner = Seperator >> key_value_line >> *(+space_break >> key_value_line); 778 table_block_inner = Seperator >> key_value_line >> *(+space_break >> key_value_line);
736 TableBlock = +space_break >> advance_match >> ensure(table_block_inner, pop_indent); 779 TableBlock = +space_break >> advance_match >> ensure(table_block_inner, pop_indent);
737 TableBlockIndent = '*' >> Seperator >> disable_arg_table_block_rule( 780 TableBlockIndent = ('*' | '-' >> space_one) >> Seperator >> disable_arg_table_block_rule(
738 space >> key_value_list >> -(space >> ',') >> 781 space >> key_value_list >> -(space >> ',') >>
739 -(+space_break >> advance_match >> space >> ensure(key_value_list >> -(space >> ',') >> *(+space_break >> key_value_line), pop_indent))); 782 -(+space_break >> advance_match >> space >> ensure(key_value_list >> -(space >> ',') >> *(+space_break >> key_value_line), pop_indent)));
740 783
@@ -755,7 +798,7 @@ YueParser::YueParser() {
755 798
756 GlobalValues = NameList >> -(space >> '=' >> space >> (TableBlock | ExpListLow)); 799 GlobalValues = NameList >> -(space >> '=' >> space >> (TableBlock | ExpListLow));
757 GlobalOp = expr('*') | '^'; 800 GlobalOp = expr('*') | '^';
758 Global = key("global") >> space >> (ClassDecl | GlobalOp | GlobalValues); 801 Global = key("global") >> space >> (-(ConstAttrib >> space) >> ClassDecl | GlobalOp | -(ConstAttrib >> space) >> GlobalValues);
759 802
760 ExportDefault = key("default"); 803 ExportDefault = key("default");
761 804
@@ -837,24 +880,24 @@ YueParser::YueParser() {
837 key_value_line = check_indent_match >> space >> ( 880 key_value_line = check_indent_match >> space >> (
838 key_value_list >> -(space >> ',') | 881 key_value_list >> -(space >> ',') |
839 TableBlockIndent | 882 TableBlockIndent |
840 '*' >> space >> (SpreadExp | Exp | TableBlock) 883 ('*' | '-' >> space_one) >> space >> (SpreadExp | Exp | TableBlock)
841 ); 884 );
842 885
843 fn_arg_def_list = FnArgDef >> *(space >> ',' >> space >> FnArgDef); 886 fn_arg_def_list = FnArgDef >> *(space >> ',' >> space >> FnArgDef);
844 887
845 fn_arg_def_lit_line = ( 888 fn_arg_def_lit_line = (
846 push_indent_match >> (space >> fn_arg_def_list >> pop_indent | pop_indent) 889 push_indent_match >> ensure(space >> fn_arg_def_list, pop_indent)
847 ) | ( 890 ) | (
848 space 891 space
849 ); 892 );
850 893
851 fn_arg_def_lit_lines = fn_arg_def_lit_line >> *(-(space >> ',') >> space_break >> fn_arg_def_lit_line); 894 fn_arg_def_lit_lines = fn_arg_def_lit_line >> *(-(space >> ',') >> space_break >> fn_arg_def_lit_line);
852 895
853 FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '=' >> space >> Exp); 896 FnArgDef = (Variable | SelfItem >> -ExistentialOp) >> -(space >> '`' >> space >> Name) >> -(space >> '=' >> space >> Exp);
854 897
855 FnArgDefList = Seperator >> ( 898 FnArgDefList = Seperator >> (
856 fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg) | 899 fn_arg_def_lit_lines >> -(-(space >> ',') >> white >> VarArg >> -(space >> '`' >> space >> Name)) |
857 white >> VarArg 900 white >> VarArg >> -(space >> '`' >> space >> Name)
858 ); 901 );
859 902
860 OuterVarShadow = key("using") >> space >> (NameList | key("nil")); 903 OuterVarShadow = key("using") >> space >> (NameList | key("nil"));
@@ -883,6 +926,7 @@ YueParser::YueParser() {
883 926
884 FnArrowBack = '<' >> set("-="); 927 FnArrowBack = '<' >> set("-=");
885 Backcall = -(FnArgsDef >> space) >> FnArrowBack >> space >> ChainValue; 928 Backcall = -(FnArgsDef >> space) >> FnArrowBack >> space >> ChainValue;
929 SubBackcall = FnArrowBack >> space >> ChainValue;
886 930
887 PipeBody = Seperator >> 931 PipeBody = Seperator >>
888 pipe_operator >> space >> UnaryExp >> 932 pipe_operator >> space >> UnaryExp >>
@@ -896,7 +940,7 @@ YueParser::YueParser() {
896 940
897 arg_table_block = pl::user(true_(), [](const item_t& item) { 941 arg_table_block = pl::user(true_(), [](const item_t& item) {
898 State* st = reinterpret_cast<State*>(item.user_data); 942 State* st = reinterpret_cast<State*>(item.user_data);
899 return st->noTableBlockStack.empty() || !st->noTableBlockStack.top(); 943 return st->noTableBlockStack.empty() || !st->noTableBlockStack.back();
900 }) >> TableBlock; 944 }) >> TableBlock;
901 945
902 invoke_args_with_table = 946 invoke_args_with_table =
@@ -936,11 +980,11 @@ YueParser::YueParser() {
936 980
937 SimpleValue = 981 SimpleValue =
938 TableLit | ConstValue | If | Switch | Try | With | 982 TableLit | ConstValue | If | Switch | Try | With |
939 ClassDecl | ForEach | For | While | Do | 983 ClassDecl | ForEach | For | While | Repeat | Do |
940 UnaryValue | TblComprehension | Comprehension | 984 UnaryValue | TblComprehension | Comprehension |
941 FunLit | Num | VarArg; 985 FunLit | Num | VarArg;
942 986
943 ExpListAssign = ExpList >> -(space >> (Update | Assign)) >> not_(space >> '='); 987 ExpListAssign = ExpList >> -(space >> (Update | Assign | SubBackcall)) >> not_(space >> '=');
944 988
945 IfLine = IfType >> space >> IfCond; 989 IfLine = IfType >> space >> IfCond;
946 WhileLine = WhileType >> space >> Exp; 990 WhileLine = WhileType >> space >> Exp;
@@ -997,11 +1041,16 @@ YueParser::YueParser() {
997 empty_line_break | 1041 empty_line_break |
998 advance_match >> ensure(space >> (indentation_error | Statement), pop_indent) 1042 advance_match >> ensure(space >> (indentation_error | Statement), pop_indent)
999 ); 1043 );
1000 Block = Seperator >> line >> *(+line_break >> line); 1044 Block = Seperator >> (pl::user(true_(), [](const item_t& item) {
1045 State* st = reinterpret_cast<State*>(item.user_data);
1046 return st->lax;
1047 }) >> lax_line >> *(+line_break >> lax_line) | line >> *(+line_break >> line));
1001 1048
1002 shebang = "#!" >> *(not_(stop) >> any_char); 1049 shebang = "#!" >> *(not_(stop) >> any_char);
1003 BlockEnd = Block >> white >> stop; 1050 BlockEnd = Block >> white >> stop;
1004 File = -shebang >> -Block >> white >> stop; 1051 File = -shebang >> -Block >> white >> stop;
1052
1053 lax_line = advance_match >> ensure(*(not_(stop) >> any()), pop_indent) | line >> and_(stop) | check_indent_match >> *(not_(stop) >> any());
1005} 1054}
1006// clang-format on 1055// clang-format on
1007 1056
@@ -1031,7 +1080,7 @@ bool YueParser::startWith(std::string_view codes, rule& r) {
1031 return true; 1080 return true;
1032} 1081}
1033 1082
1034ParseInfo YueParser::parse(std::string_view codes, rule& r) { 1083ParseInfo YueParser::parse(std::string_view codes, rule& r, bool lax) {
1035 ParseInfo res; 1084 ParseInfo res;
1036 if (codes.substr(0, 3) == "\xEF\xBB\xBF"sv) { 1085 if (codes.substr(0, 3) == "\xEF\xBB\xBF"sv) {
1037 codes = codes.substr(3); 1086 codes = codes.substr(3);
@@ -1049,6 +1098,7 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) {
1049 error_list errors; 1098 error_list errors;
1050 try { 1099 try {
1051 State state; 1100 State state;
1101 state.lax = lax;
1052 res.node.set(::yue::parse(*(res.codes), r, errors, &state)); 1102 res.node.set(::yue::parse(*(res.codes), r, errors, &state));
1053 if (state.exportCount > 0) { 1103 if (state.exportCount > 0) {
1054 int index = 0; 1104 int index = 0;
@@ -1086,19 +1136,21 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) {
1086 return res; 1136 return res;
1087} 1137}
1088 1138
1089ParseInfo YueParser::parse(std::string_view astName, std::string_view codes) { 1139ParseInfo YueParser::parse(std::string_view astName, std::string_view codes, bool lax) {
1090 auto it = _rules.find(astName); 1140 auto it = _rules.find(astName);
1091 if (it != _rules.end()) { 1141 if (it != _rules.end()) {
1092 return parse(codes, *it->second); 1142 return parse(codes, *it->second, lax);
1093 } 1143 }
1094 return {}; 1144 ParseInfo info{};
1145 info.error = ParseInfo::Error{"invalid rule: "s + std::string{astName}, 1, 1};
1146 return info;
1095} 1147}
1096 1148
1097bool YueParser::match(std::string_view astName, std::string_view codes) { 1149bool YueParser::match(std::string_view astName, std::string_view codes) {
1098 auto it = _rules.find(astName); 1150 auto it = _rules.find(astName);
1099 if (it != _rules.end()) { 1151 if (it != _rules.end()) {
1100 auto rEnd = rule(*it->second >> eof()); 1152 auto rEnd = rule(*it->second >> eof());
1101 return parse(codes, rEnd).node; 1153 return parse(codes, rEnd, false).node;
1102 } 1154 }
1103 return false; 1155 return false;
1104} 1156}
@@ -1134,6 +1186,24 @@ void trim(std::string& str) {
1134 str.erase(0, str.find_first_not_of(" \t\r\n")); 1186 str.erase(0, str.find_first_not_of(" \t\r\n"));
1135 str.erase(str.find_last_not_of(" \t\r\n") + 1); 1187 str.erase(str.find_last_not_of(" \t\r\n") + 1);
1136} 1188}
1189
1190std::string toLuaDoubleString(const std::string& input) {
1191 std::string luaStr = "\"";
1192 for (char c : input) {
1193 switch (c) {
1194 case '\"': luaStr += "\\\""; break;
1195 case '\\': luaStr += "\\\\"; break;
1196 case '\n': luaStr += "\\n"; break;
1197 case '\r': luaStr += "\\r"; break;
1198 case '\t': luaStr += "\\t"; break;
1199 default:
1200 luaStr += c;
1201 break;
1202 }
1203 }
1204 luaStr += "\"";
1205 return luaStr;
1206}
1137} // namespace Utils 1207} // namespace Utils
1138 1208
1139std::string ParseInfo::errorMessage(std::string_view msg, int errLine, int errCol, int lineOffset) const { 1209std::string ParseInfo::errorMessage(std::string_view msg, int errLine, int errCol, int lineOffset) const {
diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h
index 02292e1..c91e530 100644
--- a/src/yuescript/yue_parser.h
+++ b/src/yuescript/yue_parser.h
@@ -74,16 +74,16 @@ extern std::unordered_set<std::string> Keywords;
74class YueParser { 74class YueParser {
75public: 75public:
76 template <class AST> 76 template <class AST>
77 ParseInfo parse(std::string_view codes) { 77 ParseInfo parse(std::string_view codes, bool lax) {
78 return parse(codes, getRule<AST>()); 78 return parse(codes, getRule<AST>(), lax);
79 } 79 }
80 80
81 ParseInfo parse(std::string_view astName, std::string_view codes); 81 ParseInfo parse(std::string_view astName, std::string_view codes, bool lax);
82 82
83 template <class AST> 83 template <class AST>
84 bool match(std::string_view codes) { 84 bool match(std::string_view codes) {
85 auto rEnd = rule(getRule<AST>() >> eof()); 85 auto rEnd = rule(getRule<AST>() >> eof());
86 return parse(codes, rEnd).node; 86 return parse(codes, rEnd, false).node;
87 } 87 }
88 88
89 bool match(std::string_view astName, std::string_view codes); 89 bool match(std::string_view astName, std::string_view codes);
@@ -102,13 +102,14 @@ public:
102 102
103protected: 103protected:
104 YueParser(); 104 YueParser();
105 ParseInfo parse(std::string_view codes, rule& r); 105 ParseInfo parse(std::string_view codes, rule& r, bool lax);
106 bool startWith(std::string_view codes, rule& r); 106 bool startWith(std::string_view codes, rule& r);
107 107
108 struct State { 108 struct State {
109 State() { 109 State() {
110 indents.push(0); 110 indents.push(0);
111 } 111 }
112 bool lax = false;
112 bool exportDefault = false; 113 bool exportDefault = false;
113 bool exportMacro = false; 114 bool exportMacro = false;
114 bool exportMetatable = false; 115 bool exportMetatable = false;
@@ -119,10 +120,11 @@ protected:
119 size_t stringOpen = 0; 120 size_t stringOpen = 0;
120 std::string buffer; 121 std::string buffer;
121 std::stack<int> indents; 122 std::stack<int> indents;
122 std::stack<bool> noDoStack; 123 std::vector<bool> noDoStack;
123 std::stack<bool> noChainBlockStack; 124 std::vector<bool> noChainBlockStack;
124 std::stack<bool> noTableBlockStack; 125 std::vector<bool> noTableBlockStack;
125 std::stack<bool> noForStack; 126 std::vector<bool> noForStack;
127 std::vector<bool> noUntilStack;
126 std::unordered_set<std::string> usedNames; 128 std::unordered_set<std::string> usedNames;
127 }; 129 };
128 130
@@ -156,7 +158,7 @@ private:
156 NONE_AST_RULE(invalid_interpolation_error); 158 NONE_AST_RULE(invalid_interpolation_error);
157 NONE_AST_RULE(confusing_unary_not_error); 159 NONE_AST_RULE(confusing_unary_not_error);
158 NONE_AST_RULE(table_key_pair_error); 160 NONE_AST_RULE(table_key_pair_error);
159 NONE_AST_RULE(if_assignment_syntax_error); 161 NONE_AST_RULE(assignment_expression_syntax_error);
160 162
161 NONE_AST_RULE(inc_exp_level); 163 NONE_AST_RULE(inc_exp_level);
162 NONE_AST_RULE(dec_exp_level); 164 NONE_AST_RULE(dec_exp_level);
@@ -164,6 +166,7 @@ private:
164 NONE_AST_RULE(num_char); 166 NONE_AST_RULE(num_char);
165 NONE_AST_RULE(num_char_hex); 167 NONE_AST_RULE(num_char_hex);
166 NONE_AST_RULE(num_lit); 168 NONE_AST_RULE(num_lit);
169 NONE_AST_RULE(num_bin_lit);
167 NONE_AST_RULE(num_expo); 170 NONE_AST_RULE(num_expo);
168 NONE_AST_RULE(num_expo_hex); 171 NONE_AST_RULE(num_expo_hex);
169 NONE_AST_RULE(lj_num); 172 NONE_AST_RULE(lj_num);
@@ -216,6 +219,8 @@ private:
216 NONE_AST_RULE(enable_for); 219 NONE_AST_RULE(enable_for);
217 NONE_AST_RULE(enable_fun_lit); 220 NONE_AST_RULE(enable_fun_lit);
218 NONE_AST_RULE(disable_fun_lit); 221 NONE_AST_RULE(disable_fun_lit);
222 NONE_AST_RULE(disable_until);
223 NONE_AST_RULE(enable_until);
219 NONE_AST_RULE(switch_else); 224 NONE_AST_RULE(switch_else);
220 NONE_AST_RULE(switch_block); 225 NONE_AST_RULE(switch_block);
221 NONE_AST_RULE(if_else_if); 226 NONE_AST_RULE(if_else_if);
@@ -283,6 +288,7 @@ private:
283 NONE_AST_RULE(yue_line_comment); 288 NONE_AST_RULE(yue_line_comment);
284 NONE_AST_RULE(line); 289 NONE_AST_RULE(line);
285 NONE_AST_RULE(shebang); 290 NONE_AST_RULE(shebang);
291 NONE_AST_RULE(lax_line);
286 292
287 AST_RULE(Num); 293 AST_RULE(Num);
288 AST_RULE(Name); 294 AST_RULE(Name);
@@ -314,12 +320,14 @@ private:
314 AST_RULE(ImportAllMacro); 320 AST_RULE(ImportAllMacro);
315 AST_RULE(ImportTabLit); 321 AST_RULE(ImportTabLit);
316 AST_RULE(ImportAs); 322 AST_RULE(ImportAs);
323 AST_RULE(ImportGlobal);
317 AST_RULE(Import); 324 AST_RULE(Import);
318 AST_RULE(Label); 325 AST_RULE(Label);
319 AST_RULE(Goto); 326 AST_RULE(Goto);
320 AST_RULE(ShortTabAppending); 327 AST_RULE(ShortTabAppending);
321 AST_RULE(FnArrowBack); 328 AST_RULE(FnArrowBack);
322 AST_RULE(Backcall); 329 AST_RULE(Backcall);
330 AST_RULE(SubBackcall);
323 AST_RULE(PipeBody); 331 AST_RULE(PipeBody);
324 AST_RULE(ExpListLow); 332 AST_RULE(ExpListLow);
325 AST_RULE(ExpList); 333 AST_RULE(ExpList);
@@ -358,6 +366,7 @@ private:
358 AST_RULE(ExpOpValue); 366 AST_RULE(ExpOpValue);
359 AST_RULE(Exp); 367 AST_RULE(Exp);
360 AST_RULE(Callable); 368 AST_RULE(Callable);
369 AST_RULE(ReversedIndex);
361 AST_RULE(ChainValue); 370 AST_RULE(ChainValue);
362 AST_RULE(SimpleTable); 371 AST_RULE(SimpleTable);
363 AST_RULE(SimpleValue); 372 AST_RULE(SimpleValue);
@@ -370,6 +379,11 @@ private:
370 AST_RULE(DoubleStringInner); 379 AST_RULE(DoubleStringInner);
371 AST_RULE(DoubleStringContent); 380 AST_RULE(DoubleStringContent);
372 AST_RULE(DoubleString); 381 AST_RULE(DoubleString);
382 AST_RULE(YAMLIndent);
383 AST_RULE(YAMLLineInner);
384 AST_RULE(YAMLLineContent);
385 AST_RULE(YAMLLine);
386 AST_RULE(YAMLMultiline);
373 AST_RULE(String); 387 AST_RULE(String);
374 AST_RULE(Parens); 388 AST_RULE(Parens);
375 AST_RULE(DotChainItem); 389 AST_RULE(DotChainItem);
@@ -426,6 +440,8 @@ private:
426 AST_RULE(ExpListAssign); 440 AST_RULE(ExpListAssign);
427 AST_RULE(IfLine); 441 AST_RULE(IfLine);
428 AST_RULE(WhileLine); 442 AST_RULE(WhileLine);
443 AST_RULE(Break);
444 AST_RULE(Continue);
429 AST_RULE(BreakLoop); 445 AST_RULE(BreakLoop);
430 AST_RULE(StatementAppendix); 446 AST_RULE(StatementAppendix);
431 AST_RULE(Statement); 447 AST_RULE(Statement);
@@ -443,6 +459,7 @@ private:
443namespace Utils { 459namespace Utils {
444void replace(std::string& str, std::string_view from, std::string_view to); 460void replace(std::string& str, std::string_view from, std::string_view to);
445void trim(std::string& str); 461void trim(std::string& str);
462std::string toLuaDoubleString(const std::string& input);
446} // namespace Utils 463} // namespace Utils
447 464
448} // namespace yue 465} // namespace yue
diff --git a/src/yuescript/yuescript.cpp b/src/yuescript/yuescript.cpp
index 7e8e8b7..61e3949 100644
--- a/src/yuescript/yuescript.cpp
+++ b/src/yuescript/yuescript.cpp
@@ -93,6 +93,12 @@ static void get_config(lua_State* L, yue::YueConfig& config) {
93 config.useSpaceOverTab = lua_toboolean(L, -1) != 0; 93 config.useSpaceOverTab = lua_toboolean(L, -1) != 0;
94 } 94 }
95 lua_pop(L, 1); 95 lua_pop(L, 1);
96 lua_pushliteral(L, "lax");
97 lua_gettable(L, -2);
98 if (lua_isboolean(L, -1) != 0) {
99 config.lax = lua_toboolean(L, -1) != 0;
100 }
101 lua_pop(L, 1);
96 lua_pushliteral(L, "options"); 102 lua_pushliteral(L, "options");
97 lua_gettable(L, -2); 103 lua_gettable(L, -2);
98 if (lua_istable(L, -1) != 0) { 104 if (lua_istable(L, -1) != 0) {
@@ -180,7 +186,7 @@ static int yueformat(lua_State* L) {
180 tabSize = static_cast<int>(luaL_checkinteger(L, 2)); 186 tabSize = static_cast<int>(luaL_checkinteger(L, 2));
181 } 187 }
182 std::string_view codes(input, len); 188 std::string_view codes(input, len);
183 auto info = yue::YueParser::shared().parse<yue::File_t>(codes); 189 auto info = yue::YueParser::shared().parse<yue::File_t>(codes, false);
184 if (info.error) { 190 if (info.error) {
185 const auto& error = info.error.value(); 191 const auto& error = info.error.value();
186 if (!info.codes) { 192 if (!info.codes) {
@@ -201,6 +207,7 @@ static int yueformat(lua_State* L) {
201 formatter.spaceOverTab = false; 207 formatter.spaceOverTab = false;
202 } 208 }
203 auto result = formatter.toString(info.node.get()); 209 auto result = formatter.toString(info.node.get());
210 yue::Utils::replace(result, "\n\n", "\n");
204 lua_pushlstring(L, result.c_str(), result.size()); 211 lua_pushlstring(L, result.c_str(), result.size());
205 return 1; 212 return 1;
206} 213}
@@ -282,8 +289,13 @@ static int yuetoast(lua_State* L) {
282 ruleName = {name, nameSize}; 289 ruleName = {name, nameSize};
283 } 290 }
284 } 291 }
292 bool lax = false;
293 if (!lua_isnoneornil(L, 4)) {
294 luaL_checktype(L, 4, LUA_TBOOLEAN);
295 lax = lua_toboolean(L, 4) != 0;
296 }
285 auto& yueParser = yue::YueParser::shared(); 297 auto& yueParser = yue::YueParser::shared();
286 auto info = ruleName.empty() ? yueParser.parse<yue::File_t>({input, size}) : yueParser.parse(ruleName, {input, size}); 298 auto info = ruleName.empty() ? yueParser.parse<yue::File_t>({input, size}, lax) : yueParser.parse(ruleName, {input, size}, lax);
287 if (!info.error) { 299 if (!info.error) {
288 lua_createtable(L, 0, 0); 300 lua_createtable(L, 0, 0);
289 int tableIndex = lua_gettop(L); 301 int tableIndex = lua_gettop(L);
diff --git a/win-build/Yuescript/Yuescript.vcxproj b/win-build/Yuescript/Yuescript.vcxproj
index d073e70..87e13ce 100644
--- a/win-build/Yuescript/Yuescript.vcxproj
+++ b/win-build/Yuescript/Yuescript.vcxproj
@@ -215,6 +215,7 @@
215 <Link> 215 <Link>
216 <SubSystem>Console</SubSystem> 216 <SubSystem>Console</SubSystem>
217 <GenerateDebugInformation>true</GenerateDebugInformation> 217 <GenerateDebugInformation>true</GenerateDebugInformation>
218 <AdditionalOptions>/STACK:2097152 %(AdditionalOptions)</AdditionalOptions>
218 </Link> 219 </Link>
219 </ItemDefinitionGroup> 220 </ItemDefinitionGroup>
220 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug53|x64'"> 221 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug53|x64'">
@@ -230,6 +231,7 @@
230 <Link> 231 <Link>
231 <SubSystem>Console</SubSystem> 232 <SubSystem>Console</SubSystem>
232 <GenerateDebugInformation>true</GenerateDebugInformation> 233 <GenerateDebugInformation>true</GenerateDebugInformation>
234 <AdditionalOptions>/STACK:2097152 %(AdditionalOptions)</AdditionalOptions>
233 </Link> 235 </Link>
234 </ItemDefinitionGroup> 236 </ItemDefinitionGroup>
235 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> 237 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -287,6 +289,7 @@
287 <EnableCOMDATFolding>true</EnableCOMDATFolding> 289 <EnableCOMDATFolding>true</EnableCOMDATFolding>
288 <OptimizeReferences>true</OptimizeReferences> 290 <OptimizeReferences>true</OptimizeReferences>
289 <GenerateDebugInformation>true</GenerateDebugInformation> 291 <GenerateDebugInformation>true</GenerateDebugInformation>
292 <AdditionalOptions>/STACK:2097152 %(AdditionalOptions)</AdditionalOptions>
290 </Link> 293 </Link>
291 </ItemDefinitionGroup> 294 </ItemDefinitionGroup>
292 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release53|x64'"> 295 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release53|x64'">
@@ -306,6 +309,7 @@
306 <EnableCOMDATFolding>true</EnableCOMDATFolding> 309 <EnableCOMDATFolding>true</EnableCOMDATFolding>
307 <OptimizeReferences>true</OptimizeReferences> 310 <OptimizeReferences>true</OptimizeReferences>
308 <GenerateDebugInformation>true</GenerateDebugInformation> 311 <GenerateDebugInformation>true</GenerateDebugInformation>
312 <AdditionalOptions>/STACK:2097152 %(AdditionalOptions)</AdditionalOptions>
309 </Link> 313 </Link>
310 </ItemDefinitionGroup> 314 </ItemDefinitionGroup>
311 <ItemGroup> 315 <ItemGroup>