aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
81 files changed, 4443 insertions, 2916 deletions
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);