diff options
Diffstat (limited to 'src')
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 | ||
10 | namespace efsw { | 8 | namespace 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 | |||
42 | namespace efsw { | 38 | namespace efsw { |
43 | 39 | ||
44 | bool FileInfo::exists( const std::string& filePath ) { | 40 | bool FileInfo::exists( const std::string& filePath ) { |
@@ -186,12 +182,14 @@ bool FileInfo::isLink() const { | |||
186 | std::string FileInfo::linksTo() { | 182 | std::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 | ||
60 | typedef std::map<std::string, FileInfo> FileInfoMap; | 60 | typedef std::map<std::string, FileInfo> FileInfoMap; |
61 | typedef std::list<FileInfo> FileInfoList; | 61 | typedef std::vector<FileInfo> FileInfoList; |
62 | typedef std::list<std::pair<std::string, FileInfo>> MovedList; | 62 | typedef 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 | |||
8 | namespace efsw { | 17 | namespace efsw { |
9 | 18 | ||
10 | bool FileSystem::isDirectory( const std::string& path ) { | 19 | bool FileSystem::isDirectory( const std::string& path ) { |
@@ -26,13 +35,13 @@ bool FileSystem::slashAtEnd( std::string& dir ) { | |||
26 | } | 35 | } |
27 | 36 | ||
28 | void FileSystem::dirAddSlashAtEnd( std::string& dir ) { | 37 | void 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 | ||
34 | void FileSystem::dirRemoveSlashAtEnd( std::string& dir ) { | 43 | void 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 | ||
144 | std::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 | ||
8 | namespace efsw { | 7 | namespace 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 | ||
29 | namespace efsw { | 29 | namespace efsw { |
30 | 30 | ||
31 | FileWatcher::FileWatcher() : mFollowSymlinks( false ), mOutOfScopeLinks( false ) { | 31 | FileWatcher::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 | ||
45 | FileWatcher::FileWatcher( bool useGenericFileWatcher ) : | 45 | FileWatcher::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 | ||
66 | FileWatcher::~FileWatcher() { | 66 | FileWatcher::~FileWatcher() { |
67 | efSAFE_DELETE( mImpl ); | 67 | efSAFE_DELETE( mImpl ); |
68 | } | 68 | } |
69 | 69 | ||
70 | WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher ) { | 70 | WatchID 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 ); | 74 | WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher, |
75 | } | 75 | bool recursive ) { |
76 | } | 76 | return addWatch( directory, watcher, recursive, {} ); |
77 | 77 | } | |
78 | WatchID FileWatcher::addWatch( const std::string& directory, FileWatchListener* watcher, | 78 | |
79 | bool recursive ) { | 79 | WatchID 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 | } | |
87 | void FileWatcher::removeWatch( const std::string& directory ) { | 87 | |
88 | mImpl->removeWatch( directory ); | 88 | void FileWatcher::removeWatch( const std::string& directory ) { |
89 | } | 89 | mImpl->removeWatch( directory ); |
90 | 90 | } | |
91 | void FileWatcher::removeWatch( WatchID watchid ) { | 91 | |
92 | mImpl->removeWatch( watchid ); | 92 | void FileWatcher::removeWatch( WatchID watchid ) { |
93 | } | 93 | mImpl->removeWatch( watchid ); |
94 | 94 | } | |
95 | void FileWatcher::watch() { | 95 | |
96 | mImpl->watch(); | 96 | void FileWatcher::watch() { |
97 | } | 97 | mImpl->watch(); |
98 | 98 | } | |
99 | std::list<std::string> FileWatcher::directories() { | 99 | |
100 | return mImpl->directories(); | 100 | std::vector<std::string> FileWatcher::directories() { |
101 | } | 101 | return mImpl->directories(); |
102 | 102 | } | |
103 | void FileWatcher::followSymlinks( bool follow ) { | 103 | |
104 | mFollowSymlinks = follow; | 104 | void FileWatcher::followSymlinks( bool follow ) { |
105 | } | 105 | mFollowSymlinks = follow; |
106 | 106 | } | |
107 | const bool& FileWatcher::followSymlinks() const { | 107 | |
108 | return mFollowSymlinks; | 108 | const bool& FileWatcher::followSymlinks() const { |
109 | } | 109 | return mFollowSymlinks; |
110 | 110 | } | |
111 | void FileWatcher::allowOutOfScopeLinks( bool allow ) { | 111 | |
112 | mOutOfScopeLinks = allow; | 112 | void FileWatcher::allowOutOfScopeLinks( bool allow ) { |
113 | } | 113 | mOutOfScopeLinks = allow; |
114 | 114 | } | |
115 | const bool& FileWatcher::allowOutOfScopeLinks() const { | 115 | |
116 | return mOutOfScopeLinks; | 116 | const 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 | */ |
29 | static std::vector<Watcher_CAPI*> g_callbacks; | 29 | static std::vector<Watcher_CAPI*> g_callbacks; |
30 | 30 | ||
31 | Watcher_CAPI* find_callback( efsw_watcher watcher, efsw_pfn_fileaction_callback fn ) { | 31 | Watcher_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 | ||
74 | EFSW_API void efsw_clearlasterror() { | ||
75 | efsw::Errors::Log::clearLastError(); | ||
76 | } | ||
77 | |||
74 | efsw_watchid efsw_addwatch( efsw_watcher watcher, const char* directory, | 78 | efsw_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 | |||
83 | efsw_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 | ||
87 | void efsw_removewatch( efsw_watcher watcher, const char* directory ) { | 105 | void 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 | ||
44 | static 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 | |||
44 | void FileWatcherFSEvents::FSEventCallback( ConstFSEventStreamRef /*streamRef*/, void* userData, | 70 | void 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 | ||
65 | FileWatcherFSEvents::FileWatcherFSEvents( FileWatcher* parent ) : | 107 | FileWatcherFSEvents::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 ) : | |||
72 | FileWatcherFSEvents::~FileWatcherFSEvents() { | 114 | FileWatcherFSEvents::~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 | ||
91 | WatchID FileWatcherFSEvents::addWatch( const std::string& directory, FileWatchListener* watcher, | 128 | WatchID 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 | ||
177 | void FileWatcherFSEvents::watch() { | 215 | void FileWatcherFSEvents::watch() {} |
178 | if ( NULL == mThread ) { | ||
179 | mThread = new Thread( &FileWatcherFSEvents::run, this ); | ||
180 | mThread->launch(); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | void 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 | ||
211 | void FileWatcherFSEvents::handleAction( Watcher* /*watch*/, const std::string& /*filename*/, | 217 | void 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 | ||
216 | std::list<std::string> FileWatcherFSEvents::directories() { | 222 | std::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 | ||
15 | namespace efsw { | 17 | namespace efsw { |
16 | 18 | ||
17 | /* OSX < 10.7 has no file events */ | ||
18 | /* So i declare the events constants */ | ||
19 | enum 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 |
39 | class FileWatcherFSEvents : public FileWatcherImpl { | 21 | class 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 | ||
27 | WatchID FileWatcherGeneric::addWatch( const std::string& directory, FileWatchListener* watcher, | 27 | WatchID 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 | ||
130 | std::list<std::string> FileWatcherGeneric::directories() { | 130 | std::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 | ||
9 | namespace efsw { | 9 | namespace efsw { |
10 | 10 | ||
@@ -12,7 +12,7 @@ namespace efsw { | |||
12 | /// @class FileWatcherGeneric | 12 | /// @class FileWatcherGeneric |
13 | class FileWatcherGeneric : public FileWatcherImpl { | 13 | class 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 | ||
5 | namespace efsw { | 5 | namespace efsw { |
6 | 6 | ||
7 | FileWatcherImpl::FileWatcherImpl( FileWatcher* parent ) : | 7 | FileWatcherImpl::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 | ||
12 | FileWatcherImpl::~FileWatcherImpl() {} | 12 | FileWatcherImpl::~FileWatcherImpl() {} |
13 | 13 | ||
14 | bool FileWatcherImpl::initOK() { | 14 | bool FileWatcherImpl::initOK() { |
15 | return static_cast<bool>( mInitOK ); | 15 | return static_cast<bool>( mInitOK ); |
16 | } | 16 | } |
17 | 17 | ||
18 | bool FileWatcherImpl::linkAllowed( const std::string& curPath, const std::string& link ) { | 18 | bool 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 | 23 | int 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 | ||
11 | namespace efsw { | 11 | namespace efsw { |
12 | 12 | ||
13 | class FileWatcherImpl { | 13 | class 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 @@ | |||
26 | namespace efsw { | 27 | namespace efsw { |
27 | 28 | ||
28 | FileWatcherInotify::FileWatcherInotify( FileWatcher* parent ) : | 29 | FileWatcherInotify::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 | ||
39 | FileWatcherInotify::~FileWatcherInotify() { | 40 | FileWatcherInotify::~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 | ||
63 | WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, | 71 | WatchID 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 | ||
71 | WatchID FileWatcherInotify::addWatch( const std::string& directory, FileWatchListener* watcher, | 80 | WatchID 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 | ||
162 | void FileWatcherInotify::removeWatchLocked( WatchID watchid ) { | 183 | void 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 | ||
268 | void FileWatcherInotify::removeWatch( WatchID watchid ) { | 249 | void 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 | ||
568 | std::list<std::string> FileWatcherInotify::directories() { | 545 | std::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 | ||
12 | namespace efsw { | 13 | namespace 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 | ||
47 | WatchID FileWatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher, | 47 | WatchID 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() { | |||
184 | void FileWatcherKqueue::handleAction( Watcher* /*watch*/, const std::string& /*filename*/, | 184 | void FileWatcherKqueue::handleAction( Watcher* /*watch*/, const std::string& /*filename*/, |
185 | unsigned long /*action*/, std::string /*oldFilename*/ ) {} | 185 | unsigned long /*action*/, std::string /*oldFilename*/ ) {} |
186 | 186 | ||
187 | std::list<std::string> FileWatcherKqueue::directories() { | 187 | std::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 | ||
9 | namespace efsw { | 9 | namespace efsw { |
10 | 10 | ||
11 | FileWatcherWin32::FileWatcherWin32( FileWatcher* parent ) : | 11 | FileWatcherWin32::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 | ||
18 | FileWatcherWin32::~FileWatcherWin32() { | 18 | FileWatcherWin32::~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 | } | |
32 | WatchID FileWatcherWin32::addWatch( const std::string& directory, FileWatchListener* watcher, | 32 | |
33 | bool recursive ) { | 33 | WatchID 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 ); | |
76 | void FileWatcherWin32::removeWatch( const std::string& directory ) { | 76 | |
77 | Lock lock( mWatchesLock ); | 77 | return watchid; |
78 | 78 | } | |
79 | Watches::iterator iter = mWatches.begin(); | 79 | |
80 | 80 | void 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; | |
89 | void FileWatcherWin32::removeWatch( WatchID watchid ) { | 89 | } |
90 | Lock lock( mWatchesLock ); | 90 | } |
91 | 91 | } | |
92 | Watches::iterator iter = mWatches.begin(); | 92 | |
93 | 93 | void 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; | |
103 | void FileWatcherWin32::removeWatch( WatcherStructWin32* watch ) { | 103 | } |
104 | Lock lock( mWatchesLock ); | 104 | } |
105 | 105 | } | |
106 | DestroyWatch( watch ); | 106 | |
107 | mWatches.erase( watch ); | 107 | void FileWatcherWin32::removeWatch( WatcherStructWin32* watch ) { |
108 | } | 108 | Lock lock( mWatchesLock ); |
109 | 109 | ||
110 | void 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 | } | 114 | void FileWatcherWin32::watch() { |
115 | } | 115 | if ( NULL == mThread ) { |
116 | 116 | mThread = new Thread( &FileWatcherWin32::run, this ); | |
117 | void FileWatcherWin32::removeAllWatches() { | 117 | mThread->launch(); |
118 | Lock lock( mWatchesLock ); | 118 | } |
119 | 119 | } | |
120 | Watches::iterator iter = mWatches.begin(); | 120 | |
121 | 121 | void 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 | } | |
129 | void FileWatcherWin32::run() { | 129 | |
130 | do { | 130 | mWatches.clear(); |
131 | if ( mInitOK && !mWatches.empty() ) { | 131 | } |
132 | DWORD numOfBytes = 0; | 132 | |
133 | OVERLAPPED* ov = NULL; | 133 | void 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 | } | |
154 | void 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: | 159 | void 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 ); | |
231 | std::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 | 239 | std::vector<std::string> FileWatcherWin32::directories() { | |
240 | return dirs; | 240 | std::vector<std::string> dirs; |
241 | } | 241 | |
242 | 242 | Lock lock( mWatchesLock ); | |
243 | bool 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 | } | 253 | bool 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 | ||
13 | namespace efsw { | 13 | namespace efsw { |
14 | 14 | ||
15 | /// Implementation for Win32 based on ReadDirectoryChangesW. | 15 | /// Implementation for Win32 based on ReadDirectoryChangesW. |
16 | /// @class FileWatcherWin32 | 16 | /// @class FileWatcherWin32 |
17 | class FileWatcherWin32 : public FileWatcherImpl { | 17 | class 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 | ||
3 | namespace efsw { namespace Errors { | 4 | namespace efsw { namespace Errors { |
4 | 5 | ||
5 | static std::string LastError; | 6 | static std::string LastError = ""; |
7 | static Error LastErrorCode = NoError; | ||
6 | 8 | ||
7 | std::string Log::getLastErrorLog() { | 9 | std::string Log::getLastErrorLog() { |
8 | return LastError; | 10 | return LastError; |
9 | } | 11 | } |
10 | 12 | ||
13 | Error Log::getLastErrorCode() { | ||
14 | return LastErrorCode; | ||
15 | } | ||
16 | |||
17 | void Log::clearLastError() { | ||
18 | LastErrorCode = NoError; | ||
19 | LastError = ""; | ||
20 | } | ||
21 | |||
11 | Error Log::createLastError( Error err, std::string log ) { | 22 | Error 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 | * **/ |
25 | class String { | 24 | class 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 | ||
36 | void Thread::run() { | 36 | void 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 | ||
18 | namespace efsw { | 18 | namespace efsw { |
19 | 19 | ||
20 | template <unsigned int N> class Utf; | 20 | template <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 | //////////////////////////////////////////////////////////// |
26 | template <> class Utf<8> { | 26 | template <> 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 | //////////////////////////////////////////////////////////// |
226 | template <> class Utf<16> { | 226 | template <> 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 | //////////////////////////////////////////////////////////// |
426 | template <> class Utf<32> { | 426 | template <> 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 |
695 | typedef Utf<8> Utf8; | 695 | typedef Utf<8> Utf8; |
696 | typedef Utf<16> Utf16; | 696 | typedef Utf<16> Utf16; |
697 | typedef Utf<32> Utf32; | 697 | typedef 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 | ||
8 | template <typename In> In Utf<8>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) { | 8 | template <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 | ||
56 | template <typename Out> Out Utf<8>::Encode( Uint32 input, Out output, Uint8 replacement ) { | 56 | template <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 | ||
112 | template <typename In> In Utf<8>::Next( In begin, In end ) { | 112 | template <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 | ||
117 | template <typename In> std::size_t Utf<8>::Count( In begin, In end ) { | 117 | template <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 | ||
127 | template <typename In, typename Out> | 127 | template <typename In, typename Out> |
128 | Out Utf<8>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { | 128 | Out 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 | ||
137 | template <typename In, typename Out> Out Utf<8>::FromWide( In begin, In end, Out output ) { | 137 | template <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 | ||
146 | template <typename In, typename Out> Out Utf<8>::FromLatin1( In begin, In end, Out output ) { | 146 | template <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 | ||
155 | template <typename In, typename Out> | 155 | template <typename In, typename Out> |
156 | Out Utf<8>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { | 156 | Out 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 |
167 | template <typename In, typename Out> | 167 | template <typename In, typename Out> |
168 | Out Utf<8>::ToWide( In begin, In end, Out output, wchar_t replacement ) { | 168 | Out 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 | ||
179 | template <typename In, typename Out> | 179 | template <typename In, typename Out> |
180 | Out Utf<8>::ToLatin1( In begin, In end, Out output, char replacement ) { | 180 | Out 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 | ||
192 | template <typename In, typename Out> Out Utf<8>::toUtf8( In begin, In end, Out output ) { | 192 | template <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 | ||
199 | template <typename In, typename Out> Out Utf<8>::ToUtf16( In begin, In end, Out output ) { | 199 | template <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 | ||
209 | template <typename In, typename Out> Out Utf<8>::ToUtf32( In begin, In end, Out output ) { | 209 | template <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 | ||
219 | template <typename In> In Utf<16>::Decode( In begin, In end, Uint32& output, Uint32 replacement ) { | 219 | template <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 | ||
247 | template <typename Out> Out Utf<16>::Encode( Uint32 input, Out output, Uint16 replacement ) { | 247 | template <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 | ||
272 | template <typename In> In Utf<16>::Next( In begin, In end ) { | 272 | template <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 | ||
277 | template <typename In> std::size_t Utf<16>::Count( In begin, In end ) { | 277 | template <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 | ||
287 | template <typename In, typename Out> | 287 | template <typename In, typename Out> |
288 | Out Utf<16>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { | 288 | Out 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 | ||
297 | template <typename In, typename Out> Out Utf<16>::FromWide( In begin, In end, Out output ) { | 297 | template <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 | ||
306 | template <typename In, typename Out> Out Utf<16>::FromLatin1( In begin, In end, Out output ) { | 306 | template <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 | ||
315 | template <typename In, typename Out> | 315 | template <typename In, typename Out> |
316 | Out Utf<16>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { | 316 | Out 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 |
327 | template <typename In, typename Out> | 327 | template <typename In, typename Out> |
328 | Out Utf<16>::ToWide( In begin, In end, Out output, wchar_t replacement ) { | 328 | Out 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 | ||
339 | template <typename In, typename Out> | 339 | template <typename In, typename Out> |
340 | Out Utf<16>::ToLatin1( In begin, In end, Out output, char replacement ) { | 340 | Out 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 | ||
351 | template <typename In, typename Out> Out Utf<16>::toUtf8( In begin, In end, Out output ) { | 351 | template <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 | ||
361 | template <typename In, typename Out> Out Utf<16>::ToUtf16( In begin, In end, Out output ) { | 361 | template <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 | ||
368 | template <typename In, typename Out> Out Utf<16>::ToUtf32( In begin, In end, Out output ) { | 368 | template <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 | ||
378 | template <typename In> In Utf<32>::Decode( In begin, In /*end*/, Uint32& output, Uint32 ) { | 378 | template <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 | ||
383 | template <typename Out> Out Utf<32>::Encode( Uint32 input, Out output, Uint32 /*replacement*/ ) { | 383 | template <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 | ||
388 | template <typename In> In Utf<32>::Next( In begin, In /*end*/ ) { | 388 | template <typename In> In Utf<32>::Next( In begin, In /*end*/ ) { |
389 | return ++begin; | 389 | return ++begin; |
390 | } | 390 | } |
391 | 391 | ||
392 | template <typename In> std::size_t Utf<32>::Count( In begin, In end ) { | 392 | template <typename In> std::size_t Utf<32>::Count( In begin, In end ) { |
393 | return begin - end; | 393 | return begin - end; |
394 | } | 394 | } |
395 | 395 | ||
396 | template <typename In, typename Out> | 396 | template <typename In, typename Out> |
397 | Out Utf<32>::FromAnsi( In begin, In end, Out output, const std::locale& locale ) { | 397 | Out 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 | ||
404 | template <typename In, typename Out> Out Utf<32>::FromWide( In begin, In end, Out output ) { | 404 | template <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 | ||
411 | template <typename In, typename Out> Out Utf<32>::FromLatin1( In begin, In end, Out output ) { | 411 | template <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 | ||
420 | template <typename In, typename Out> | 420 | template <typename In, typename Out> |
421 | Out Utf<32>::ToAnsi( In begin, In end, Out output, char replacement, const std::locale& locale ) { | 421 | Out 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 |
429 | template <typename In, typename Out> | 429 | template <typename In, typename Out> |
430 | Out Utf<32>::ToWide( In begin, In end, Out output, wchar_t replacement ) { | 430 | Out 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 | ||
438 | template <typename In, typename Out> | 438 | template <typename In, typename Out> |
439 | Out Utf<32>::ToLatin1( In begin, In end, Out output, char replacement ) { | 439 | Out 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 | ||
450 | template <typename In, typename Out> Out Utf<32>::toUtf8( In begin, In end, Out output ) { | 450 | template <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 | ||
457 | template <typename In, typename Out> Out Utf<32>::ToUtf16( In begin, In end, Out output ) { | 457 | template <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 | ||
464 | template <typename In, typename Out> Out Utf<32>::ToUtf32( In begin, In end, Out output ) { | 464 | template <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 | ||
471 | template <typename In> Uint32 Utf<32>::DecodeAnsi( In input, const std::locale& locale ) { | 471 | template <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 | ||
501 | template <typename In> Uint32 Utf<32>::DecodeWide( In input ) { | 501 | template <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 | ||
511 | template <typename Out> | 511 | template <typename Out> |
512 | Out Utf<32>::EncodeAnsi( Uint32 codepoint, Out output, char replacement, | 512 | Out 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 |
550 | template <typename Out> | 550 | template <typename Out> |
551 | Out Utf<32>::EncodeWide( Uint32 codepoint, Out output, wchar_t replacement ) { | 551 | Out 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 @@ | |||
8 | namespace efsw { | 8 | namespace efsw { |
9 | 9 | ||
10 | WatcherFSEvents::WatcherFSEvents() : | 10 | WatcherFSEvents::WatcherFSEvents() : |
11 | Watcher(), FWatcher( NULL ), FSStream( NULL ), WatcherGen( NULL ), initializedAsync( false ) {} | 11 | Watcher(), FWatcher( NULL ), FSStream( NULL ), WatcherGen( NULL ) {} |
12 | |||
13 | WatcherFSEvents::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 | ||
21 | WatcherFSEvents::~WatcherFSEvents() { | 13 | WatcherFSEvents::~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 | ||
66 | void 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 | ||
73 | void WatcherFSEvents::sendFileAction( WatchID watchid, const std::string& dir, | 60 | void 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 | ||
80 | void WatcherFSEvents::handleAddModDel( const Uint32& flags, const std::string& path, | 68 | void 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 | ||
199 | void WatcherFSEvents::process() { | 191 | void 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 | ||
15 | namespace efsw { | 15 | namespace efsw { |
16 | 16 | ||
17 | /* OSX < 10.7 has no file events */ | ||
18 | /* So i declare the events constants */ | ||
19 | enum 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 | |||
17 | class FileWatcherFSEvents; | 40 | class FileWatcherFSEvents; |
18 | 41 | ||
19 | class FSEvent { | 42 | class 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 | ||
28 | class WatcherFSEvents : public Watcher { | 53 | class 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 | ||
5 | WatcherInotify::WatcherInotify() : Watcher(), Parent( NULL ) {} | 5 | WatcherInotify::WatcherInotify() : Watcher(), Parent( NULL ) {} |
6 | 6 | ||
7 | WatcherInotify::WatcherInotify( WatchID id, std::string directory, FileWatchListener* listener, | ||
8 | bool recursive, WatcherInotify* parent ) : | ||
9 | Watcher( id, directory, listener, recursive ), Parent( parent ), DirInfo( directory ) {} | ||
10 | |||
11 | bool WatcherInotify::inParentTree( WatcherInotify* parent ) { | 7 | bool 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() { | |||
139 | void WatcherKqueue::removeAll() { | 139 | void 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 | ||
437 | WatchID WatcherKqueue::addWatch( const std::string& directory, FileWatchListener* watcher, | 438 | WatchID 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 | |||
6 | namespace efsw { | 9 | namespace efsw { |
7 | 10 | ||
8 | /// Unpacks events and passes them to a user defined callback. | 11 | struct EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX { |
9 | void 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 | |||
28 | typedef EFSW_FILE_NOTIFY_EXTENDED_INFORMATION_EX* EFSW_PFILE_NOTIFY_EXTENDED_INFORMATION_EX; | ||
29 | |||
30 | typedef 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 | |||
36 | static EFSW_LPREADDIRECTORYCHANGESEXW pReadDirectoryChangesExW = NULL; | ||
37 | |||
38 | #define EFSW_ReadDirectoryNotifyExtendedInformation 2 | ||
39 | |||
40 | static 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]; | 54 | void 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 | |||
93 | void 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. | ||
155 | void 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. |
57 | bool RefreshWatch( WatcherStructWin32* pWatch ) { | 184 | RefreshResult 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. |
77 | WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, | 234 | WatcherStructWin32* 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 | ||
22 | class WatcherWin32; | 23 | class WatcherWin32; |
23 | 24 | ||
25 | enum RefreshResult { Failed, Success, SucessEx }; | ||
26 | |||
24 | /// Internal watch data | 27 | /// Internal watch data |
25 | struct WatcherStructWin32 { | 28 | struct 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 | ||
35 | bool RefreshWatch( WatcherStructWin32* pWatch ); | 38 | RefreshResult RefreshWatch( WatcherStructWin32* pWatch ); |
36 | 39 | ||
37 | void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ); | 40 | void CALLBACK WatchCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped ); |
38 | 41 | ||
39 | void DestroyWatch( WatcherStructWin32* pWatch ); | 42 | void DestroyWatch( WatcherStructWin32* pWatch ); |
40 | 43 | ||
41 | WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, DWORD NotifyFilter, | 44 | WatcherStructWin32* CreateWatch( LPCWSTR szDirectory, bool recursive, |
42 | HANDLE iocp ); | 45 | DWORD bufferSize, DWORD notifyFilter, HANDLE iocp ); |
43 | 46 | ||
44 | class WatcherWin32 : public Watcher { | 47 | class 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 @@ | |||
32 | extern "C" { | 32 | extern "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 |
66 | typedef void* efsw_watcher; | 66 | typedef void* efsw_watcher; |
67 | 67 | ||
68 | enum efsw_action | 68 | enum 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 | |||
75 | enum 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 | ||
76 | enum efsw_error | 85 | enum 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. |
87 | typedef void (*efsw_pfn_fileaction_callback) ( | 119 | typedef 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, | 124 | typedef 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 | */ |
101 | efsw_watcher EFSW_API efsw_create(int generic_mode); | 133 | efsw_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 |
104 | void EFSW_API efsw_release(efsw_watcher watcher); | 136 | void 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 |
107 | EFSW_API const char* efsw_getlasterror(); | 139 | EFSW_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. | 142 | EFSW_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. |
112 | efsw_watchid EFSW_API efsw_addwatch(efsw_watcher watcher, const char* directory, | 146 | efsw_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 | ||
153 | efsw_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). |
116 | void EFSW_API efsw_removewatch(efsw_watcher watcher, const char* directory); | 159 | void 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). |
119 | void EFSW_API efsw_removewatch_byid(efsw_watcher watcher, efsw_watchid watchid); | 162 | void 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 ) |
122 | void EFSW_API efsw_watch(efsw_watcher watcher); | 165 | void 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 | */ |
128 | void EFSW_API efsw_follow_symlinks(efsw_watcher watcher, int enable); | 171 | void 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 */ |
131 | int EFSW_API efsw_follow_symlinks_isenabled(efsw_watcher watcher); | 174 | int 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 | */ |
142 | void EFSW_API efsw_allow_outofscopelinks(efsw_watcher watcher, int allow); | 185 | void 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 |
145 | int EFSW_API efsw_outofscopelinks_isallowed(efsw_watcher watcher); | 188 | int 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 | ||
63 | namespace efsw { | 63 | namespace efsw { |
64 | 64 | ||
65 | /// Type for a watch id | 65 | /// Type for a watch id |
66 | typedef long WatchID; | 66 | typedef long WatchID; |
67 | 67 | ||
68 | // forward declarations | 68 | // forward declarations |
69 | class FileWatcherImpl; | 69 | class FileWatcherImpl; |
70 | class FileWatchListener; | 70 | class FileWatchListener; |
71 | 71 | class 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 |
75 | namespace Actions { | 75 | /// new file. |
76 | enum Action { | 76 | namespace Actions { |
77 | /// Sent when a file is created or renamed | 77 | enum 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 | }; |
87 | typedef Actions::Action Action; | 87 | } |
88 | 88 | typedef Actions::Action Action; | |
89 | /// Errors log namespace | 89 | |
90 | namespace Errors { | 90 | /// Errors log namespace |
91 | 91 | namespace Errors { | |
92 | enum Error { | 92 | |
93 | FileNotFound = -1, | 93 | enum 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, | |
102 | class 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 | 107 | class 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 | |
112 | typedef 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(); |
117 | class 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 ); | 123 | typedef Errors::Error Error; |
124 | 124 | ||
125 | virtual ~FileWatcher(); | 125 | /// Optional file watcher settings. |
126 | 126 | namespace Options { | |
127 | /// Add a directory watch. Same as the other addWatch, but doesn't have recursive option. | 127 | enum 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 | 160 | typedef 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 | 165 | class 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. |
178 | class 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 | ||
235 | class 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 | ||
252 | class 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 | ||
10 | namespace efsw { namespace Platform { | 9 | namespace efsw { namespace Platform { |
11 | 10 | ||
12 | ThreadImpl::ThreadImpl( Thread* owner ) : mIsActive( false ) { | 11 | ThreadImpl::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 | ||
19 | ThreadImpl::~ThreadImpl() { | ||
20 | terminate(); | ||
21 | } | ||
22 | |||
20 | void ThreadImpl::wait() { | 23 | void 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 | ||
43 | void* ThreadImpl::entryPoint( void* userData ) { | 44 | void* 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 | ||
10 | namespace efsw { | 11 | namespace efsw { |
@@ -15,7 +16,9 @@ namespace Platform { | |||
15 | 16 | ||
16 | class ThreadImpl { | 17 | class 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 | ||
9 | namespace efsw { namespace Platform { | 9 | namespace efsw { namespace Platform { |
10 | 10 | ||
11 | ThreadImpl::ThreadImpl( Thread* owner ) { | 11 | ThreadImpl::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 | ||
20 | class ThreadImpl { | 20 | class 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 */ |
36 | typedef signed char SOPHIST_int8; | 36 | typedef signed char SOPHIST_int8; |
37 | typedef unsigned char SOPHIST_uint8; | 37 | typedef unsigned char SOPHIST_uint8; |
38 | 38 | ||
39 | typedef signed short SOPHIST_int16; | 39 | typedef signed short SOPHIST_int16; |
40 | typedef unsigned short SOPHIST_uint16; | 40 | typedef 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 | ||
90 | SOPHIST_compiletime_assert( int8 , sizeof(SOPHIST_int8 ) == 1); | 90 | SOPHIST_compiletime_assert( int8 , sizeof(SOPHIST_int8 ) == 1); |
91 | SOPHIST_compiletime_assert(uint16, sizeof(SOPHIST_int16) == 2); | 91 | SOPHIST_compiletime_assert(uint16, sizeof(SOPHIST_int16) == 2); |
92 | SOPHIST_compiletime_assert( int32, sizeof(SOPHIST_int32 ) == 4); | 92 | SOPHIST_compiletime_assert( int32, sizeof(SOPHIST_int32 ) == 4); |
93 | SOPHIST_compiletime_assert(uint32, sizeof(SOPHIST_uint32) == 4); | 93 | SOPHIST_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 | ||
126 | SOPHIST_compiletime_assert(intptr, sizeof(SOPHIST_intptr) == sizeof(char *)); | 126 | SOPHIST_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 { | |||
167 | std::string TableAppendingOp_t::to_string(void*) const { | 167 | std::string TableAppendingOp_t::to_string(void*) const { |
168 | return "[]"s; | 168 | return "[]"s; |
169 | } | 169 | } |
170 | std::string PlainItem_t::to_string(void *) const { | 170 | std::string PlainItem_t::to_string(void*) const { |
171 | return {}; | 171 | return {}; |
172 | } | 172 | } |
173 | std::string GlobalOp_t::to_string(void* ud) const { | 173 | std::string GlobalOp_t::to_string(void*) const { |
174 | auto info = reinterpret_cast<YueFormat*>(ud); | 174 | return "*"s; |
175 | return info->convert(this); | ||
176 | } | 175 | } |
177 | std::string ExportDefault_t::to_string(void*) const { | 176 | std::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 { | |||
188 | std::string NotIn_t::to_string(void*) const { | 187 | std::string NotIn_t::to_string(void*) const { |
189 | return {}; | 188 | return {}; |
190 | } | 189 | } |
190 | std::string Break_t::to_string(void*) const { | ||
191 | return "break"s; | ||
192 | } | ||
193 | std::string Continue_t::to_string(void*) const { | ||
194 | return "continue"s; | ||
195 | } | ||
191 | std::string BreakLoop_t::to_string(void* ud) const { | 196 | std::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 | } |
195 | std::string YueLineComment_t::to_string(void* ud) const { | 202 | std::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 | } |
307 | std::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 | } | ||
300 | std::string Import_t::to_string(void* ud) const { | 318 | std::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 | } |
345 | std::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 | } | ||
327 | std::string PipeBody_t::to_string(void* ud) const { | 351 | std::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 { | |||
406 | std::string Switch_t::to_string(void* ud) const { | 430 | std::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 | } |
512 | std::string Repeat_t::to_string(void* ud) const { | 573 | std::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 | } |
529 | std::string ForStepValue_t::to_string(void* ud) const { | 590 | std::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 { | |||
596 | std::string Try_t::to_string(void* ud) const { | 657 | std::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 | } |
918 | std::string ReversedIndex_t::to_string(void* ud) const { | ||
919 | if (modifier) { | ||
920 | return "[# - "s + modifier->to_string(ud) + ']'; | ||
921 | } | ||
922 | return "[#]"s; | ||
923 | } | ||
854 | std::string Callable_t::to_string(void* ud) const { | 924 | std::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 | } |
1010 | std::string YAMLIndent_t::to_string(void* ud) const { | ||
1011 | auto info = reinterpret_cast<YueFormat*>(ud); | ||
1012 | return info->convert(this); | ||
1013 | } | ||
1014 | std::string YAMLLineInner_t::to_string(void* ud) const { | ||
1015 | auto info = reinterpret_cast<YueFormat*>(ud); | ||
1016 | return info->convert(this); | ||
1017 | } | ||
1018 | std::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 | } | ||
1024 | std::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 | } | ||
1031 | std::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 | } | ||
940 | std::string String_t::to_string(void* ud) const { | 1055 | std::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 | } |
1127 | std::string GlobalValues_t::to_string(void* ud) const { | 1242 | std::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 | } |
1138 | std::string Global_t::to_string(void* ud) const { | 1253 | std::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 | } |
1141 | std::string Export_t::to_string(void* ud) const { | 1256 | std::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) |
234 | AST_END(ImportAs) | 234 | AST_END(ImportAs) |
235 | 235 | ||
236 | AST_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) | ||
241 | AST_END(ImportGlobal) | ||
242 | |||
236 | AST_NODE(Import) | 243 | AST_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) |
239 | AST_END(Import) | 246 | AST_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; | ||
276 | AST_END(ExpList) | 285 | AST_END(ExpList) |
277 | 286 | ||
278 | AST_NODE(Return) | 287 | AST_NODE(Return) |
@@ -285,9 +294,9 @@ AST_END(Return) | |||
285 | AST_NODE(With) | 294 | AST_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) |
291 | AST_END(With) | 300 | AST_END(With) |
292 | 301 | ||
293 | AST_NODE(SwitchList) | 302 | AST_NODE(SwitchList) |
@@ -302,20 +311,21 @@ AST_NODE(SwitchCase) | |||
302 | AST_MEMBER(SwitchCase, &condition, &body) | 311 | AST_MEMBER(SwitchCase, &condition, &body) |
303 | AST_END(SwitchCase) | 312 | AST_END(SwitchCase) |
304 | 313 | ||
314 | AST_NODE(Assignment) | ||
315 | ast_ptr<false, ExpList_t> expList; | ||
316 | ast_ptr<true, Assign_t> assign; | ||
317 | AST_MEMBER(Assignment, &expList, &assign) | ||
318 | AST_END(Assignment) | ||
319 | |||
305 | AST_NODE(Switch) | 320 | AST_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) |
311 | AST_END(Switch) | 327 | AST_END(Switch) |
312 | 328 | ||
313 | AST_NODE(Assignment) | ||
314 | ast_ptr<false, ExpList_t> expList; | ||
315 | ast_ptr<true, Assign_t> assign; | ||
316 | AST_MEMBER(Assignment, &expList, &assign) | ||
317 | AST_END(Assignment) | ||
318 | |||
319 | AST_NODE(IfCond) | 329 | AST_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) | |||
343 | AST_END(While) | 353 | AST_END(While) |
344 | 354 | ||
345 | AST_NODE(Repeat) | 355 | AST_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) |
349 | AST_END(Repeat) | 359 | AST_END(Repeat) |
@@ -381,9 +391,10 @@ AST_NODE(CatchBlock) | |||
381 | AST_END(CatchBlock) | 391 | AST_END(CatchBlock) |
382 | 392 | ||
383 | AST_NODE(Try) | 393 | AST_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) |
387 | AST_END(Try) | 398 | AST_END(Try) |
388 | 399 | ||
389 | AST_NODE(Comprehension) | 400 | AST_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) |
588 | AST_END(DoubleString) | 599 | AST_END(DoubleString) |
589 | 600 | ||
601 | AST_LEAF(YAMLIndent) | ||
602 | AST_END(YAMLIndent) | ||
603 | |||
604 | AST_LEAF(YAMLLineInner) | ||
605 | AST_END(YAMLLineInner) | ||
606 | |||
607 | AST_NODE(YAMLLineContent) | ||
608 | ast_sel<true, YAMLLineInner_t, Exp_t> content; | ||
609 | AST_MEMBER(YAMLLineContent, &content) | ||
610 | AST_END(YAMLLineContent) | ||
611 | |||
612 | AST_NODE(YAMLLine) | ||
613 | ast_ptr<true, YAMLIndent_t> indent; | ||
614 | ast_list<true, YAMLLineContent_t> segments; | ||
615 | AST_MEMBER(YAMLLine, &indent, &segments) | ||
616 | AST_END(YAMLLine) | ||
617 | |||
618 | AST_NODE(YAMLMultiline) | ||
619 | ast_ptr<true, Seperator_t> sep; | ||
620 | ast_list<true, YAMLLine_t> lines; | ||
621 | AST_MEMBER(YAMLMultiline, &sep, &lines) | ||
622 | AST_END(YAMLMultiline) | ||
623 | |||
590 | AST_NODE(String) | 624 | AST_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) |
593 | AST_END(String) | 627 | AST_END(String) |
594 | 628 | ||
@@ -620,6 +654,7 @@ AST_END(Slice) | |||
620 | 654 | ||
621 | AST_NODE(Parens) | 655 | AST_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) |
624 | AST_END(Parens) | 659 | AST_END(Parens) |
625 | 660 | ||
@@ -638,9 +673,14 @@ AST_END(TableAppendingOp) | |||
638 | AST_LEAF(PlainItem) | 673 | AST_LEAF(PlainItem) |
639 | AST_END(PlainItem) | 674 | AST_END(PlainItem) |
640 | 675 | ||
676 | AST_NODE(ReversedIndex) | ||
677 | ast_ptr<false, Exp_t> modifier; | ||
678 | AST_MEMBER(ReversedIndex, &modifier) | ||
679 | AST_END(ReversedIndex) | ||
680 | |||
641 | AST_NODE(ChainValue) | 681 | AST_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) |
646 | AST_END(ChainValue) | 686 | AST_END(ChainValue) |
@@ -724,8 +764,9 @@ AST_LEAF(GlobalOp) | |||
724 | AST_END(GlobalOp) | 764 | AST_END(GlobalOp) |
725 | 765 | ||
726 | AST_NODE(Global) | 766 | AST_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) |
729 | AST_END(Global) | 770 | AST_END(Global) |
730 | 771 | ||
731 | AST_LEAF(ExportDefault) | 772 | AST_LEAF(ExportDefault) |
@@ -741,15 +782,17 @@ AST_END(Export) | |||
741 | AST_NODE(FnArgDef) | 782 | AST_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) |
746 | AST_END(FnArgDef) | 788 | AST_END(FnArgDef) |
747 | 789 | ||
748 | AST_NODE(FnArgDefList) | 790 | AST_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) | ||
753 | AST_END(FnArgDefList) | 796 | AST_END(FnArgDefList) |
754 | 797 | ||
755 | AST_NODE(OuterVarShadow) | 798 | AST_NODE(OuterVarShadow) |
@@ -837,9 +880,15 @@ AST_NODE(UnaryExp) | |||
837 | AST_MEMBER(UnaryExp, &ops, &expos, &inExp) | 880 | AST_MEMBER(UnaryExp, &ops, &expos, &inExp) |
838 | AST_END(UnaryExp) | 881 | AST_END(UnaryExp) |
839 | 882 | ||
883 | AST_NODE(SubBackcall) | ||
884 | ast_ptr<true, FnArrowBack_t> arrow; | ||
885 | ast_ptr<true, ChainValue_t> value; | ||
886 | AST_MEMBER(SubBackcall, &arrow, &value) | ||
887 | AST_END(SubBackcall) | ||
888 | |||
840 | AST_NODE(ExpListAssign) | 889 | AST_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) |
844 | AST_END(ExpListAssign) | 893 | AST_END(ExpListAssign) |
845 | 894 | ||
@@ -855,7 +904,17 @@ AST_NODE(WhileLine) | |||
855 | AST_MEMBER(WhileLine, &type, &condition) | 904 | AST_MEMBER(WhileLine, &type, &condition) |
856 | AST_END(WhileLine) | 905 | AST_END(WhileLine) |
857 | 906 | ||
858 | AST_LEAF(BreakLoop) | 907 | AST_LEAF(Break) |
908 | AST_END(Break) | ||
909 | |||
910 | AST_LEAF(Continue) | ||
911 | AST_END(Continue) | ||
912 | |||
913 | AST_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; | ||
859 | AST_END(BreakLoop) | 918 | AST_END(BreakLoop) |
860 | 919 | ||
861 | AST_NODE(PipeBody) | 920 | AST_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 | |||
7 | THE 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.*/ | 7 | THE 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 | ||
78 | const std::string_view version = "0.27.3"sv; | 81 | const std::string_view version = "0.29.3"sv; |
79 | const std::string_view extension = "yue"sv; | 82 | const std::string_view extension = "yue"sv; |
80 | 83 | ||
81 | class CompileError : public std::logic_error { | 84 | class 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 | ||
1034 | ParseInfo YueParser::parse(std::string_view codes, rule& r) { | 1083 | ParseInfo 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 | ||
1089 | ParseInfo YueParser::parse(std::string_view astName, std::string_view codes) { | 1139 | ParseInfo 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 | ||
1097 | bool YueParser::match(std::string_view astName, std::string_view codes) { | 1149 | bool 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 | |||
1190 | std::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 | ||
1139 | std::string ParseInfo::errorMessage(std::string_view msg, int errLine, int errCol, int lineOffset) const { | 1209 | std::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; | |||
74 | class YueParser { | 74 | class YueParser { |
75 | public: | 75 | public: |
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 | ||
103 | protected: | 103 | protected: |
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: | |||
443 | namespace Utils { | 459 | namespace Utils { |
444 | void replace(std::string& str, std::string_view from, std::string_view to); | 460 | void replace(std::string& str, std::string_view from, std::string_view to); |
445 | void trim(std::string& str); | 461 | void trim(std::string& str); |
462 | std::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); |