├── .gitignore ├── CHANGELOG.md ├── Docs-zh ├── 000.PLoop.md ├── 001.prototype.md ├── 002.environment.md ├── 003.namespace.md ├── 004.enum.md ├── 005.struct.md ├── 006.class.md ├── 007.attribute.md ├── 008.threading.md ├── 009.serialization.md ├── 010.collections.md ├── 011.tools.md ├── 012.exception.md ├── 013.autoclose.md ├── 014.configuration.md ├── 015.unittest.md ├── 016.io.md ├── 017.io.resource.md ├── 018.encode.md ├── 019.data.md └── 020.web.md ├── Docs ├── 000.PLoop.md ├── 001.prototype.md ├── 002.environment.md ├── 003.namespace.md ├── 004.enum.md ├── 005.struct.md ├── 006.class.md ├── 007.attribute.md ├── 008.threading.md ├── 009.serialization.md ├── 010.collections.md ├── 011.tools.md ├── 012.exception.md ├── 013.autoclose.md ├── 014.configuration.md ├── 015.unittest.md ├── 016.io.md ├── 017.io.resource.md ├── 018.encode.md ├── 019.data.md ├── 020.web.md ├── 021.reactive.md ├── 022.socket.md ├── 023.mqtt.md ├── 024.dependencyinjection.md ├── 025.range.md └── 026.watch_reactive.md ├── LICENSE ├── PLDoc.md ├── Prototype.lua ├── README-zh.md ├── README.md ├── System ├── Collections.lua ├── Collections │ ├── Array.lua │ ├── Dictionary.lua │ ├── IIndexedListSorter.lua │ ├── List.lua │ ├── Proxy.lua │ ├── Queue.lua │ └── Range.lua ├── Configuration.lua ├── Context.lua ├── Data │ ├── Cache.lua │ ├── Core.lua │ ├── DataBase.lua │ ├── DataCache.lua │ └── init.lua ├── Date.lua ├── IO │ ├── Directory.lua │ ├── File.lua │ ├── FileReader.lua │ ├── FileWriter.lua │ ├── Gzip.lua │ ├── OperationSystem.lua │ ├── Path.lua │ ├── Resource │ │ ├── CaseSensitiveTest.lua │ │ ├── IResourceLoader.lua │ │ ├── IResourceManager.lua │ │ └── LuaLoader.lua │ └── init.lua ├── Logger.lua ├── Message.lua ├── Net │ ├── Core.lua │ ├── MQTT │ │ ├── Context.lua │ │ ├── Core.lua │ │ ├── MessagePublisher.lua │ │ └── init.lua │ ├── Modbus │ │ ├── Core.lua │ │ └── init.lua │ ├── OPC │ │ ├── Client.lua │ │ ├── Core.lua │ │ ├── DataTypes.lua │ │ ├── EventTypes.lua │ │ ├── NodeClasses.lua │ │ ├── ObjectTypes.lua │ │ ├── Reference.lua │ │ ├── Services.lua │ │ ├── VariableTypes.lua │ │ └── init.lua │ ├── Snap7 │ │ ├── Core.lua │ │ └── init.lua │ ├── Socket.lua │ └── init.lua ├── Observer.lua ├── Reactive │ ├── Observable.lua │ ├── Observer.lua │ ├── Operator.lua │ ├── Reactive.lua │ ├── ReactiveDictionary.lua │ ├── ReactiveField.lua │ ├── ReactiveList.lua │ ├── ReactiveValue.lua │ ├── Subject.lua │ └── Watch.lua ├── Recycle.lua ├── Scalar.lua ├── Serialization.lua ├── Serialization │ ├── JsonFormatProvider.lua │ ├── LuaFormatProvider.lua │ └── StringFormatProvider.lua ├── Service.lua ├── Text.lua ├── Text │ ├── Base64.lua │ ├── Crc.lua │ ├── Deflate.lua │ ├── TemplateString.lua │ ├── UTF16Encoding.lua │ ├── UTF8Encoding.lua │ └── XmlEntity.lua ├── Threading.lua ├── Threading │ ├── Lock.lua │ └── TaskScheduler.lua ├── UnitTest.lua ├── ValueWrapper.lua ├── Web │ ├── Attribute │ │ ├── ContextHandler.lua │ │ └── Validator.lua │ ├── Context │ │ ├── HttpContext.lua │ │ ├── HttpCookie.lua │ │ ├── HttpRequest.lua │ │ ├── HttpResponse.lua │ │ └── HttpSession.lua │ ├── ContextHandler │ │ ├── GuidSessionIDManager.lua │ │ └── Route.lua │ ├── Core.lua │ ├── Interface │ │ ├── IContextOutputHandler.lua │ │ ├── IHttpContextHandler.lua │ │ ├── IHttpOutput.lua │ │ ├── IOutputLoader.lua │ │ └── IRenderEngine.lua │ ├── MVC │ │ ├── Controller.lua │ │ └── ViewPageLoader.lua │ ├── Resource │ │ ├── CssLoader.lua │ │ ├── EmbedPageLoader.lua │ │ ├── HelperPageLoader.lua │ │ ├── JavaScriptLoader.lua │ │ ├── LuaServerPageLoader.lua │ │ ├── MasterPageLoader.lua │ │ ├── PageLoader.lua │ │ ├── PageRenderEngine.lua │ │ └── StaticFileLoader.lua │ ├── Xml │ │ └── Xml.lua │ └── init.lua └── Xml │ ├── Core.lua │ └── init.lua ├── UnitTest ├── Run.lua └── Tests │ ├── attribute.lua │ ├── class.lua │ ├── enum.lua │ ├── environment.lua │ ├── namespace.lua │ ├── prototype.lua │ ├── struct.lua │ └── variables.lua └── init.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # OS generated files # 2 | ###################### 3 | .DS_Store 4 | .DS_Store? 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | ehthumbs.db 9 | Thumbs.db -------------------------------------------------------------------------------- /Docs-zh/003.namespace.md: -------------------------------------------------------------------------------- 1 | # 命名空间(namespace) 2 | 3 | 命名空间用于以树状目录的方式保存和管理类型资源,同名的类型可以保存在不同的命名空间中,通常命名空间可以采用 `公司名.项目名.库名.类名` 的形式来确保各类库没有冲突。**PLoop**占用了**System**命名空间,请不要使用它作为自己项目的命名空间。 4 | 5 | 6 | ## 目录 7 | 8 | * [命名空间的访问](#命名空间的访问) 9 | * [命名空间和环境](#命名空间和环境) 10 | * [System.Namespace](#systemnamespace) 11 | * [ExportNamespace](#exportnamespace) 12 | * [GetNamespace](#getnamespace) 13 | * [GetNamespaces](#getnamespaces) 14 | * [GetNamespaceName](#getnamespacename) 15 | * [GetNamespaceForNext](#getnamespacefornext) 16 | * [IsAnonymousNamespace](#isanonymousnamespace) 17 | * [SaveNamespace](#savenamespace) 18 | * [SaveAnonymousNamespace](#saveanonymousnamespace) 19 | * [SetNamespaceForNext](#setnamespacefornext) 20 | * [Validate](#validate) 21 | 22 | 23 | ## 命名空间的访问 24 | 25 | 保存在`_G`的**PLoop**是根命名空间,我们可以通过它作为起点访问到所有保存在命名空间的类型。例如`PLoop.System.Date`是日期类型。 26 | 27 | ```lua 28 | require "PLoop" 29 | 30 | print(PLoop.System) 31 | print(PLoop.System.Collections) 32 | print(PLoop.System.Collections.List) 33 | ``` 34 | 35 | 默认情况下,**PLoop**不会将任何命名空间及其保存的类型保存到`_G`,需要直接使用的话,可以使用 `import "path"` 导入,`import`时无需在开头写明`PLoop`。 36 | 37 | ```lua 38 | require "PLoop" 39 | 40 | print(System) -- nil 41 | 42 | import "System" 43 | 44 | print(System) -- System 45 | print(Collections) -- System.Collections 46 | 47 | print(List) -- nil 48 | 49 | import "System.Collections" 50 | 51 | print(XList(100):Sum()) -- 5050 52 | ``` 53 | 54 | 55 | ## 命名空间和环境 56 | 57 | 通常**PLoop**不推荐直接在`_G`中运行代码,而是使用由**PLoop**系统提供的独立环境。 58 | 59 | ```lua 60 | require "PLoop" (function(_ENV) 61 | print(Collections) -- System.Collections 62 | print(XList(100):Sum()) -- 5050 63 | end) 64 | ``` 65 | 66 | 首先,最常用的几个命名空间会注册为公共命名空间,我们可以直接使用它们,而无需主动`import`: 67 | 68 | * System - 我们可以使用常规的结构体,特性,日志,日期等常见类型 69 | * System.Collections - 我们可以使用列表List, 集合Dictionary, 动态列表XList等集合类型和操作 70 | * System.Threading - 我们可以使用异步`__Async__`, 迭代器`__Iterator__`等协程控制特性 71 | 72 | 除了使用公共命名空间,在独立环境中,我们还有两种使用方式: 73 | 74 | * namespace(path) - 申明环境所属的命名空间,可以优先读取该命名空间中的类型,以及为该命名空间定义新的类型。 75 | * import(path) - 导入命名空间,访问级别高于公共命名空间,例如`import "System.Form"`后,使用`List`类型将是`System.Form.List`,而不是`System.Collections.List`。 76 | 77 | 一个环境只有一个所属命名空间,但导入没有限制。 78 | 79 | ```lua 80 | require "PLoop" 81 | 82 | PLoop(function(_ENV) 83 | namespace "System.Serialization" 84 | 85 | print(ISerializable) -- System.Serialization.ISerializable 86 | 87 | namespace "Test" 88 | 89 | print(ISerializable) -- nil 90 | 91 | import "System.Serialization" 92 | print(ISerializable) -- System.Serialization.ISerializable 93 | end) 94 | ``` 95 | 96 | 申明命名空间后,定义的新类型例如`class`都会被保存在这个命名空间中,这样我们就可以在任何其他环境中使用这些类型。 97 | 98 | ```lua 99 | require "PLoop" 100 | 101 | PLoop(function(_ENV) 102 | class "A" {} 103 | 104 | namespace "Test" -- create and set the environment's namespace 105 | 106 | class "B" {} -- B is saved as Test.B 107 | end) 108 | 109 | PLoop(function(_ENV) 110 | import "Test" -- import the Test namespace 111 | 112 | print(A) -- nil 113 | print(Test) -- Test 114 | print(B) -- Test.B 115 | end) 116 | ``` 117 | 118 | 119 | ## System.Namespace 120 | 121 | **System.Namespace** 用于提供API以便获取命名空间的所有信息。 122 | 123 | 124 | ### ExportNamespace 125 | 126 | 将一个命名空间及它保存的类型(含子命名空间)保存到一个环境中 127 | 128 | * Format: (env, ns[, override][, stack]) 129 | * Params: 130 | * env - 环境 131 | * ns - 命名空间或它的访问路径 132 | * override - 是否覆盖环境中的同名变量 133 | * stack - the stack level 134 | 135 | 136 | ### GetNamespace 137 | 138 | 从访问路径中获取命名空间实体 139 | 140 | * Format: ([root, ]path) 141 | * Params: 142 | * root - 父命名空间,默认PLoop 143 | * path - string, 命名空间访问路径 144 | * Return: 145 | * ns - 命名空间实体 146 | 147 | ```lua 148 | require "PLoop" (function(_ENV) 149 | -- System.Collections 150 | print(Namespace.GetNamespace("System.Collections")) 151 | 152 | -- System.Collections.List 153 | print(Namespace.GetNamespace(System, "Collections.List")) 154 | end) 155 | ``` 156 | 157 | 158 | ### GetNamespaces 159 | 160 | 获取命名空间中保存的所有类型(含子命名空间)。 161 | 162 | * Params: 163 | * ns - 命名空间 164 | * Return: 165 | * iter - function, 迭代器 166 | * ns - 命名空间 167 | 168 | ```lua 169 | require "PLoop" (function(_ENV) 170 | for name, ns in Namespace.GetNamespaces(System) do 171 | print(name, ns) 172 | end 173 | end) 174 | ``` 175 | 176 | 177 | ### GetNamespaceName 178 | 179 | 获取命名空间实体的访问路径或者名字 180 | 181 | * Format: (ns[, onlyname]) 182 | * Params: 183 | * ns - 命名空间 184 | * onlyname - boolean, 如果true仅返回名字 185 | * Return: 186 | * name - string, 访问路径或者名字 187 | 188 | 189 | ### IsAnonymousNamespace 190 | 191 | 目标是否是匿名命名空间 192 | 193 | * Params: 194 | * ns - 命名空间实体 195 | * Return: 196 | * isanony - boolean, 如果目标没有命名空间访问路径,那么返回true 197 | 198 | 实际而言,`class`, `inteface`等生成的类型也都是命名空间,可以用于保存其他类型,但我们经常需要构建一些匿名类型,仅自己使用,这些匿名类型就是匿名命名空间,类似: 199 | 200 | ```lua 201 | require "PLoop" (function(_ENV) 202 | A = class {} 203 | 204 | print(A) -- Anonymous 205 | print(Namespace.IsAnonymousNamespace(A)) -- true 206 | end) 207 | ``` 208 | 209 | 210 | ### SaveNamespace 211 | 212 | 保存一个资源到一个命名空间中作为子项。 213 | 214 | * Format: ([root, ]path, feature[, stack]) 215 | * Parmas: 216 | * root - 根命名空间,默认PLoop 217 | * path - string, 路径 218 | * feature - 资源,必须是table或者userdata,通常是创建的类型 219 | * stack - the stack level 220 | 221 | ```lua 222 | require "PLoop" (function(_ENV) 223 | Namespace.SaveNamespace("Test", { 224 | A = 1, B = 2, C = 3 225 | }) 226 | 227 | print(PLoop.Test.A) -- 1 228 | 229 | Test.D = 123 230 | 231 | print(D) -- nil 232 | 233 | namespace "Test" 234 | 235 | print(D) -- 123 236 | end) 237 | ``` 238 | 239 | 警告:虽然允许将table作为命名空间保留,但请尽可能不要用这种方式传递数据,在多线程平台上非常不安全。上面仅仅是举例。 240 | 241 | 242 | ### SaveAnonymousNamespace 243 | 244 | 保存匿名命名空间,任何类型,只有通过`SaveNamespace`或`SaveAnonymousNamespace`保存入命名空间系统后,才能用作命名空间保存其他类型。 245 | 246 | * Format: (feature[, stack]) 247 | * Parmas: 248 | * feature - 资源,必须是table或userdta,通常是类型 249 | * stack - the stack level 250 | 251 | ```lua 252 | require "PLoop" (function(_ENV) 253 | local Root = setmetatable({}, { __index = Namespace.GetNamespace }) 254 | Namespace.SaveAnonymousNamespace(Root) 255 | 256 | namespace (Root) 257 | 258 | class "A" {} 259 | class "B" {} 260 | 261 | print(Root.A) -- A 262 | print(Root.B) -- B 263 | end) 264 | ``` 265 | 266 | ### Validate 267 | 268 | 验证目标是否是命名空间 269 | 270 | * Params: 271 | * target - 目标 272 | * Return: 273 | * target - 如果目标是命名空间返回该目标,否则返回nil 274 | -------------------------------------------------------------------------------- /Docs-zh/008.threading.md: -------------------------------------------------------------------------------- 1 | # 线程(threading) 2 | 3 | 之前的章节完整的介绍了原型和原型构建出来的类型系统,从本章开始介绍基于类型系统构建各种功能类库。 4 | 5 | 本章介绍的是线程库(对于Lua里面就是协程库),它封装了Lua的协程操作,提供了**线程池**,**异步申明**,**迭代器**,**线程锁**几个功能模块。 线程锁将在[多线程平台](021.multi.os.thread.md)中讨论。 6 | 7 | 线程库定义在`System.Threading`命名空间下,它是公共命名空间,所以可以直接使用下面提到的类型。 8 | 9 | 10 | ## 目录 11 | * [线程池](#线程池) 12 | * [在协程下运行函数](#在协程下运行函数) 13 | * [安全的在协程下运行函数](#安全的在协程下运行函数) 14 | * [使用迭代器](#使用迭代器) 15 | * [从线程池获取协程或封装函数](#从线程池获取协程或封装函数) 16 | * [`__Async__`](#`__async__`) 17 | * [`__Iterator__`](#`__iterator__`) 18 | 19 | 20 | ## 线程池 21 | 22 | 线程池用于提供一组受控的协程,程序可以随时从里面获取新的协程,将自己的函数挂载进行执行,函数执行完毕,返回结果后,协程会被放回协程池,以备下一次使用。通常Lua运行在单线程环境中,我们只需要使用一个线程池即可满足需求,所以,系统默认提供了`System.ThreadPool.Default`作为默认用线程池。 23 | 24 | 在多线程平台上,通常,每个系统线程会运行一个Lua的协程,在同系统线程中的处理可以认为是单线程的, **PLoop**使用上下文来管理在一个系统线程中的处理,在每一个上下文对象中,都需要一个单独的协程池来避免访问冲突。这点上直接使用比较困难,不过通过`__Async__`和`__Iterator__`特性,它们会封装掉线程池的选择,所以,通常我们不需要在意自己具体使用的是哪个线程池。 25 | 26 | 27 | ### 在协程下运行函数 28 | 29 | 我们可以调用线程池对象的`ThreadCall`方法将函数挂载到协程中进行处理,处理结束后,协程回收,以备下一次使用: 30 | 31 | ```lua 32 | require "PLoop" (function(_ENV) 33 | function test(...) 34 | print(coroutine.running(), ...) 35 | end 36 | 37 | ThreadPool.Default:ThreadCall(test, 1, 2, 3) -- thread: 00EE3C90 1 2 3 38 | ThreadPool.Default:ThreadCall(test, 1, 2, 3) -- thread: 00EE3C90 1 2 3 39 | end) 40 | ``` 41 | 42 | 可以看到,协程被复用了,通常协程的构建和销毁都是很昂贵的操作,通过线程池我们可以很大的降低这一块的消耗。 43 | 44 | 45 | ### 安全的在协程下运行函数 46 | 47 | `ThreadCall`方法总是会使用一个新的协程(从协程池),如果仅仅是为了确保目标函数运行在协程上,可以使用`SafeThreadCall`方法: 48 | 49 | ```lua 50 | require "PLoop" (function(_ENV) 51 | function safeThreadCall() 52 | print("safe", coroutine.running()) 53 | end 54 | 55 | function normalThreadCall() 56 | print("normal", coroutine.running()) 57 | ThreadPool.Default:SafeThreadCall(safeThreadCall) 58 | end 59 | 60 | function test() 61 | print("test", coroutine.running()) 62 | ThreadPool.Default:ThreadCall(normalThreadCall) 63 | end 64 | 65 | -- test thread: 00D81C84 66 | -- normal thread: 00D81E1C 67 | -- safe thread: 00D81E1C 68 | ThreadPool.Default:ThreadCall(test) 69 | end) 70 | ``` 71 | 72 | 73 | ### 使用迭代器 74 | 75 | 在其他语言中,常使用`yield`返回数据的形式,来实现生成器,同样,Lua原生提供了`yield`,我们只需要利用线程池优化协程调用,就可以容易的生成各种迭代器: 76 | 77 | ```lua 78 | require "PLoop" (function(_ENV) 79 | function Fibonacci(maxn) 80 | local n0, n1 = 1, 1 81 | 82 | coroutine.yield(0, n0) 83 | coroutine.yield(1, n1) 84 | 85 | local n = 2 86 | 87 | while n <= maxn do 88 | n0, n1 = n1, n0 + n1 89 | coroutine.yield(n, n1) 90 | n = n + 1 91 | end 92 | end 93 | 94 | -- 0 1 95 | -- 1 1 96 | -- 2 2 97 | -- 3 3 98 | -- 4 5 99 | -- 5 8 100 | for k, v in ThreadPool.Default:GetIterator(Fibonacci, 5) do 101 | print(k, v) 102 | end 103 | 104 | -- we also can pass the argument later 105 | for k, v in ThreadPool.Default:GetIterator(Fibonacci), 5 do 106 | print(k, v) 107 | end 108 | end) 109 | ``` 110 | 111 | 这样的做法,协程可以被大量复用,避免了开发者对协程使用的顾虑,而且这样制作迭代器可以很好的保证代码运行在同一个函数中,避免普通迭代函数需要管理迭代状态的麻烦,当逻辑复杂到一定程度时,Lua原生的迭代器制作方式就很难使用了,例如**PLoop**的web框架在读取视图文件时,有大量的上下文标记,以协程制作的迭代器运行,只需要一次主函数调用,可一边解析出整个视图文件的结构,一边直接生成对应的视图类,而换成Lua的常规做法的话,只能先分析出结构,保存临时数据,再将临时数据转换成视图类,前者就省略了保存临时数据的麻烦。在集合操作中,我们还会看到更多的应用。 112 | 113 | 114 | ### 从线程池获取协程或封装函数 115 | 116 | 上面的代码都是将函数传给系统,由系统自行`resume`进行处理,在复杂的需要更多自控的情况下,我们也可以从线程池直接获取协程或封装协程的函数: 117 | 118 | ```lua 119 | require "PLoop" (function(_ENV) 120 | function test(...) 121 | print(coroutine.running(), ...) 122 | coroutine.yield() 123 | return ... 124 | end 125 | 126 | local thread = ThreadPool.Default:GetThread(test) 127 | print(thread) -- thread: 00F8D4C0 128 | coroutine.resume(thread, 1, 2, 3) -- thread: 00F8D4C0 1 2 3 129 | print(coroutine.resume(thread)) -- true 1 2 3 130 | 131 | local wrap = ThreadPool.Default:GetThread(test, true) 132 | print(wrap) -- function: 011F1058 133 | wrap(1, 2, 3) -- thread: 010C0E30 1 2 3 134 | print(wrap(1, 2, 3)) -- 1 2 3 135 | end) 136 | ``` 137 | 138 | 不过这样使用协程无法被回收,也很少有这样使用的需求。 139 | 140 | 上述的例子都是主动使用线程池,如果在多线程平台环境中,线程池的使用将会非常复杂,为了避免开发困难,也为了简化协程的使用,线程库提供了两个特性。 141 | 142 | ## `__Async__` 143 | 144 | `__Async__`特性用于将目标函数运行在协程中,可以实现异步的处理方式。 145 | 146 | Attribute Targets: 147 | * System.AttributeTargets.Function 148 | * System.AttributeTargets.Method 149 | 150 | ```lua 151 | require "PLoop" (function(_ENV) 152 | __Async__() 153 | function test(...) 154 | print(coroutine.running(), ...) 155 | end 156 | 157 | test(1, 2, 3) -- thread: 00F73208 1 2 3 158 | test(1, 2, 3) -- thread: 00F73208 1 2 3 159 | 160 | __Async__() 161 | function clock(...) 162 | for i = 1, 10 do 163 | -- Task是一个假的任务库,提供比如Delay延时的处理 164 | Task.Delay(1) 165 | print(i) 166 | end 167 | end 168 | end) 169 | ``` 170 | 171 | 以这种形式,我们将隐藏掉协程调用的所有细节。这个特性是使用`ThreadCall`方法来调用目标方法。 172 | 173 | 如果希望使用`SafeThreadCall`来调用,可以使用`__Async__(true)`来进行申明。 174 | 175 | 176 | ## `__Iterator__` 177 | 178 | `__Iterator__`用于封装目标为迭代器 179 | 180 | Attribute Targets: 181 | * System.AttributeTargets.Function 182 | * System.AttributeTargets.Method 183 | 184 | ```lua 185 | require "PLoop" (function(_ENV) 186 | __Iterator__() 187 | function Fibonacci(maxn) 188 | local n0, n1 = 1, 1 189 | 190 | coroutine.yield(0, n0) 191 | coroutine.yield(1, n1) 192 | 193 | local n = 2 194 | 195 | while n <= maxn do 196 | n0, n1 = n1, n0 + n1 197 | coroutine.yield(n, n1) 198 | n = n + 1 199 | end 200 | end 201 | 202 | -- 0 1 203 | -- 1 1 204 | -- 2 2 205 | -- 3 3 206 | -- 4 5 207 | -- 5 8 208 | for k, v in Fibonacci(5) do 209 | print(k, v) 210 | end 211 | 212 | -- 迭代器的参数可以分批传入,会自己拼接 213 | for k, v in Fibonacci(), 5 do 214 | print(k, v) 215 | end 216 | end) 217 | ``` 218 | 219 | 封装后的函数,使用类似`pairs`。 -------------------------------------------------------------------------------- /Docs-zh/012.exception.md: -------------------------------------------------------------------------------- 1 | # 异常处理(Exception) 2 | 3 | 在Lua中,我们通常会面对两种错误:预期的和非预期的。我们代码中使用`error`抛出的错误就是预期错误,用于通知调用者。而`prnit(123)`这样的拼写错误,是我们非预期的。 4 | 5 | 对于非预期错误而言,Lua的报错信息足够我们定位到它,并且直接进行修复。但预期错误,通常将错误定位在`error`调用的位置,而是实际代码被使用的位置,例如`__Arguments__`抛出的错误,会定位在函数被调用位置。这样开发者才能最快的找到错误点,进行修复。 6 | 7 | 但通常,定位错误点并不是容易的事情,我们通过`error(msg, stack)`指定stack level的方式,来指定实际错误点,之前介绍各个类型提供的反射API中,有大量的API可以传入stack level,也是为了便于计算抛出错误位置。 8 | 9 | 但很多情况下,定位stack level是比较困难的,例如一个函数如果使用`__Arguments__`, `__Async__`, `__Return__`同时封装,因为特性之间完全独立,推导stack level并不容易,而例如类的构造体中,如果抛出错误的话,因为存在继承,子类调用父类构造体方法等处理,stack level是完全无法确定的。 10 | 11 | 例如 : 12 | 13 | ```lua 14 | require "PLoop" (function(_ENV) 15 | class "A" { 16 | function (self) 17 | error("Something error", 5) 18 | end 19 | } 20 | 21 | o = A() 22 | end) 23 | ``` 24 | 25 | 数字5可以通过测试找出,但当我们继承这个类时: 26 | 27 | ```lua 28 | require "PLoop" (function(_ENV) 29 | class "A" { 30 | function (self) 31 | error("Something error", 5) 32 | end 33 | } 34 | 35 | class "B" (function(_ENV) 36 | inherit "A" 37 | 38 | function B(self) 39 | super(self) 40 | end 41 | end) 42 | 43 | o = B() 44 | end) 45 | ``` 46 | 47 | 这个stack level就完全不正确了。 48 | 49 | 但另一方面来说,这种情况只针对预期错误,非预期错误我们本来就希望直接定位。而预期错误是我们发起,实际是完全受控的。Lua中,并非只能用`error`抛出字符串,如果使用`pcall`调用处理,可以用table作为错误消息,这样我们可以在table中记录足够的信息,通常`pcall`在最外层使用,它捕获错误信息后,就可以在正确的调用位置,抛出错误了。 50 | 51 | 52 | ## System.Exception & throw 53 | 54 | `System.Exception`对象封装了错误信息,所有异常类型都需要继承它,不过,目前**PLoop**仅提供了两个异常类型,另一个是用于单元测试的`System.UnitTest.TestFailureException`,后面的章节会介绍。 55 | 56 | `Exception`类提供了以下的属性: 57 | 58 | 属性 |描述 59 | :--------------------|:-------------------------------- 60 | Message |用于描述异常内容的字符串 61 | StackTrace |使用debug.traceback读取到的堆栈信息 62 | TargetSite |抛出错误信息的函数,依赖debug.info 63 | Source |抛出错误的位置,依赖debug.info 64 | InnerException |导致该异常的内部异常对象,一般捕获异常后,可以再抛出新的异常对象,不过目前不提供大量异常类型,这个属性暂时无用 65 | Data |含有键值对,提供更多关于异常的信息,需要自行填入数据 66 | LocalVariables |含有键值对,对应抛出错误时函数的局部变量 67 | Upvalues |含有键值对,对应抛出错误时函数的闭包 68 | StackDataSaved |是否已经保存了堆栈数据,如果没保存,系统会尝试自行保存堆栈信息数据,如果通过平台设置调整为默认true,则系统不会尝试保存堆栈数据 69 | StackLevel |将被扫描的堆栈等级,默认1,即当前调用函数 70 | SaveVariables |是否保存局部变量和闭包,默认false 71 | 72 | `Exception`类的构造: 73 | 74 | ```lua 75 | __Arguments__{ String, Exception/nil, Boolean/nil } 76 | function Exception(self, message, inner, savevariables) 77 | self.Message = message 78 | self.InnerException = inner 79 | self.SaveVariables = savevariables 80 | end 81 | ``` 82 | 83 | 通常,我们仅需要传入错误信息即可,其他数据会在使用`throw`关键字抛出异常对象时,自动填入: 84 | 85 | ```lua 86 | require "PLoop" (function(_ENV) 87 | function test(a) 88 | local v = 2 89 | throw(Exception("Something error", nil, true)) 90 | end 91 | 92 | local ok, exp = pcall(test, 1) 93 | 94 | -- Something error 95 | print(exp.StackTrace:match("^[^\n]*")) 96 | 97 | -- xxxx.lua:4 98 | print(exp.Source) 99 | 100 | -- a 1 101 | -- v 2 102 | for k, v in pairs(exp.LocalVariables) do 103 | print(k, v) 104 | end 105 | end) 106 | ``` 107 | 108 | 如果我们只想抛出错误,可以用`throw`直接抛出错误字符串,它会自行构建出异常对象: 109 | 110 | ```lua 111 | require "PLoop" (function(_ENV) 112 | class "A" (function(_ENV) 113 | function A(self) 114 | throw("Something error") 115 | end 116 | end) 117 | 118 | -- xxxx.lua:9: Something error 119 | o = A() 120 | end) 121 | ``` 122 | 123 | 对象构造时,无论继承了多少层,使用`throw`抛出异常后,因为系统管理着对象的生成过程,所以,可以很方便的定位在对象被构造的位置。 124 | 125 | 另外,如果希望所有异常对象自动保存局部变量和闭包,可以通过平台配置开启`EXCEPTION_SAVE_VARIABLES`: 126 | 127 | ```lua 128 | PLOOP_PLATFORM_SETTINGS = { EXCEPTION_SAVE_VARIABLES = true } 129 | require "PLoop" (function(_ENV) 130 | function test(a) 131 | local v = 2 132 | throw("Something error") 133 | end 134 | 135 | local ok, exp = pcall(test, 1) 136 | 137 | -- Something error 138 | print(exp.StackTrace:match("^[^\n]*")) 139 | 140 | -- xxxx.lua:4 141 | print(exp.Source) 142 | 143 | -- a 1 144 | -- v 2 145 | for k, v in pairs(exp.LocalVariables) do 146 | print(k, v) 147 | end 148 | end) 149 | ``` 150 | 151 | **PLoop**系统中,`throw`已被对象构造,`__Arguments__`参数验证,属性(设置了`throw=true`)这些场景处理掉。其他时候,需要自行处理。 152 | 153 | 154 | ## Support throw 155 | 156 | 处理异常只需要使用`pcall`,然后判定下错误消息的类型即可,`throw`关键字可以在任何**PLoop**环境中被使用。 157 | 158 | ```lua 159 | require "PLoop" (function(_ENV) 160 | function task() 161 | throw("Something error") 162 | end 163 | 164 | function test() 165 | local ok, ret = pcall(task) 166 | 167 | if not ok then 168 | if type(ret) == "string" then 169 | error(ret, 0) -- 非预期错误,使用0保证它的定位在原位置 170 | else 171 | error(tostring(ret), 2) -- 预期错误,2代表了test函数被调用的位置 172 | end 173 | end 174 | end 175 | 176 | -- xxxx.lua:19: Something error 177 | test() 178 | end) 179 | ``` 180 | 181 | 182 | ## `__Arguments__` & throw 183 | 184 | `__Arguments__`对函数的封装较为复杂,特别如果作为重载用途,stack level相对要复杂,如果期望用`throw`抛出错误,那么我们需要通知`__Arguments__`特性: 185 | 186 | ```lua 187 | require "PLoop" (function(_ENV) 188 | __Arguments__{ Number }:Throwable() 189 | function div(num) 190 | if num == 0 then 191 | throw("zero is not allowed") 192 | end 193 | end 194 | 195 | -- xxxx.lua:10: zero is not allowed 196 | div(0) 197 | end) 198 | ``` 199 | 200 | 通过实用`Throwable`方法,特性可以确认自己要调用的处理,可能抛出异常对象,它会针对这种情况做出处理,将抛出的异常定位在调用位置。 201 | 202 | 203 | ## Property & throw 204 | 205 | 属性配置后,系统会根据配置情况自行生成优化的访问代码,但也会造成,抛出错误的堆栈级别无法固定,为了使用`throw`抛出错误,也需要在属性定义时注明: 206 | 207 | ```lua 208 | require "PLoop" (function(_ENV) 209 | class "Person" (function(_ENV) 210 | field { __name = "anonymous" } 211 | 212 | function SetName(self, name) 213 | if type(name) ~= "string" then 214 | throw("The name must be string") 215 | end 216 | self.__name = name 217 | end 218 | 219 | function GetName(self) 220 | return self.__name 221 | end 222 | 223 | property "Name" { 224 | get = "GetName", -- or getmethod = "GetName" 225 | set = "SetName", -- or setmethod = "SetName" 226 | throwable = true, 227 | } 228 | end) 229 | 230 | -- xxxx.lua:24: The name must be string 231 | Person().Name = 123 232 | end) 233 | ``` -------------------------------------------------------------------------------- /Docs-zh/013.autoclose.md: -------------------------------------------------------------------------------- 1 | # 资源自动关闭(AutoClose & with) 2 | 3 | 在我们操作系统资源时,例如操作文件,数据库等,都需要先打开,再进行处理,处理结束后,需要关闭资源,但如果中间操作出现问题,错误抛出后,资源没有关闭,就可能造成问题(文件会在回收时被关闭,数据库要等到超时)。 4 | 5 | 我们可以通过封装操作,将资源的开启和关闭都由系统控制,然后操作在匿名函数中进行的方式,可以有效避免操作出错时,无法关闭系统资源。 6 | 7 | 类似: 8 | 9 | ```lua 10 | require "PLoop" 11 | require "PLoop.System.IO" 12 | 13 | PLoop(function(_ENV) 14 | import "System.IO" 15 | 16 | with(FileReader[[test_file_path]], FileWriter[[file_to_write]])(function(reader, writer) 17 | for line in reader:ReadLines() 18 | writer:WriteLine(line) 19 | end 20 | end, function(err) 21 | print(err) 22 | end) 23 | end) 24 | ``` 25 | 26 | `FileReader`是IO库中读取文件用的类,它扩展了`System.IAutoClose`接口,可以被使用在`with`关键字中,`with`可以同时接受多个系统资源,一次性全部打开,在`with`调用后,可以接受两个函数,第一个就是封装了操作的匿名函数,之前传入`with`的资源都会作为参数传给这个匿名函数,继续操作,第二个函数是错误捕获,错误信息会被传入,如果不提供,默认使用`error`抛出错误,当然在此之前,两个文件都会被正常关闭。 27 | 28 | 29 | ## System.IAutoClose 30 | 31 | 这个接口申明了两个抽象方法,需要被子类实现: 32 | 33 | 抽象方法 |参数 |描述 34 | :---------------|:-------------------|:----------------------------- 35 | Open | |执行资源的打开操作 36 | Close |error |执行资源的关闭操作,并传入error错误消息如果错误存在 37 | 38 | 39 | ## with 40 | 41 | `with`关键字只接受扩展了`IAutoClose`接口的类的对象,它会自动打开和关闭资源: 42 | 43 | ```lua 44 | require "PLoop" (function(_ENV) 45 | class "A" { IAutoClose, 46 | 47 | Open = function(self) 48 | print( "Open " .. self.name ) 49 | end, 50 | 51 | Close = function(self, err) 52 | print("Close " .. self.name .. (err and (" with " .. err) or " without error")) 53 | end, 54 | } 55 | 56 | -- Open task 57 | -- process task 58 | -- Close task without error 59 | with(A{ name = "task"})(function(obj) 60 | print("process " .. obj.name) 61 | end) 62 | 63 | -- Open task 64 | -- Open task2 65 | -- process task, task2 66 | -- Close task with xxx.lua:23: 2333 67 | -- Close task2 with xxx.lua:23: 2333 68 | -- Catch error:xxx.lua:23: 2333 69 | with(A{ name = "task"}, A{ name = "task2"})(function(obj, obj2) 70 | print("process " .. obj.name .. ", " .. obj2.name) 71 | error("2333") 72 | end, function(err) 73 | print("Catch error:" .. err) 74 | end) 75 | end) 76 | ``` 77 | 78 | 另外,操作的返回值会被`with`处理返回。 79 | 80 | ```lua 81 | require "PLoop" (function(_ENV) 82 | class "A" { IAutoClose } 83 | 84 | -- 1 2 3 4 85 | print(with(A())(function(obj) 86 | return 1, 2, 3, 4 87 | end)) 88 | end) 89 | ``` 90 | 91 | 下面是一个实际使用的数据库处理的例子: 92 | 93 | ```lua 94 | function RecordLastLogin(id) 95 | -- 创建数据库处理上下文,打开数据库连接 96 | with(MyDBContext())(function(ctx) 97 | -- 启动一个事务,事务是一个单独的IAutoClose对象 98 | with(ctx.Transaction)(function(trans) 99 | -- 查询并锁定指定数据 100 | local user = ctx.Users:Lock{ id = id }:First() 101 | if user then 102 | user.LastLogin = Date.Now 103 | 104 | -- 保存数据库修改 105 | ctx:SaveChanges() 106 | else 107 | -- 取消事务 108 | trans:Rollback() 109 | end 110 | end) 111 | end) 112 | end 113 | ``` -------------------------------------------------------------------------------- /Docs-zh/017.io.resource.md: -------------------------------------------------------------------------------- 1 | # System.IO.Resource 2 | 3 | 本章探讨的是如果管理资源文件(例如web框架中使用的模板文件),包括加载,复用等,通常只适用于设计和定制框架。 4 | 5 | **没有定制框架需求的,请跳过本章** 6 | 7 | 我们可以将动态加载的内容,包括Lua代码文件都称为资源,`Resource`系统主要用于加载,管理这些资源。 8 | 9 | 10 | 11 | ## System.IO.Resource.IResourceLoader 12 | 13 | 每种类型的资源都需要有一个资源加载器,将它们以特定形式加载进入系统,例如,web框架中,可以将网页模板加载为视图类,生成网页时,只需要创建这个类的对象,然后渲染出结果即可。 14 | 15 | 资源的类型,通过后缀名作为区分,资源加载器则需要注册到对应的后缀名,以便系统根据资源类型选择的加载器,然后加载资源。 16 | 17 | `IResourceLoader`是资源加载器的基础接口,它提供加载资源的静态方法: 18 | 19 | 静态方法 |参数 |描述 20 | :----------------------|:-----------------------------------------------------------------------|:----------------------- 21 | LoadResource |path:String, reader:TextReader/nil, env:Table/nil, tryLock:Boolean/nil |从指定路径加载资源,可以指定读取对象,不然系统使用FileReader来读取,可以指定加载资源时的环境,也可以指定是否以加锁的方式来加载资源(同一时间只应该有一个类型被生成) 22 | 23 | `IResourceLoader`申明了需要被实现的方法: 24 | 25 | 抽象方法 |参数 |描述 26 | :----------------------|:-----------------------------------------------------------------------|:----------------------- 27 | Load |path:String, reader:TextReader/nil, env:Table/nil |从指定路径加载资源,可以指定读取对象,不然系统使用FileReader来读取,可以指定加载资源时的环境 28 | 29 | `Load`方法的返回值,就是资源的系统表达形式,可以时类型,也可以是对象。 30 | 31 | 为了绑定加载器对应的资源类型,我们可以使用`System.IO.Resource.__ResourceLoader__`特性来绑定后缀名: 32 | 33 | ```lua 34 | System.IO.Resource.__ResourceLoader__"lua" 35 | class "System.IO.Resource.LuaLoader" { System.IO.Resource.IResourceLoader } 36 | ``` 37 | 38 | 如果需要对应多后缀,可以使用`__ResourceLoader__("lua", "luax", "luac")`。 39 | 40 | 下面是使用的例子,注意Lua的加载器已经被PLoop实现为`System.IO.Resource.LuaLoader`,会返回和文件同名的类型(大小写无视) 41 | 42 | ```lua 43 | require "PLoop" 44 | require "PLoop.System.IO" 45 | 46 | PLoop(function(_ENV) 47 | import "System.IO" 48 | import "System.IO.Resource" 49 | 50 | code = [[ 51 | PLoop(function(_ENV) 52 | class "Test" {} 53 | end) 54 | ]] 55 | 56 | -- 需要指定假的路径用于提供后缀名和类型名,同时文件名也是资源管理时的区分 57 | cls = IResourceLoader.LoadResource("test.lua", StringReader(code)) 58 | 59 | print(Class.Validate(cls)) -- Test 60 | end) 61 | ``` 62 | 63 | 64 | ## System.IO.Resource.IResourceManager 65 | 66 | 在加载后,我们需要能够对加载后的资源进行管理,例如根据传入路径复用已经加载的资源,根据资源文件修改时间,重新加载资源等。 67 | 68 | `IResourceManager`接口提供了主要的资源管理: 69 | 70 | 静态方法 |参数 |描述 71 | :----------------------|:------------------------------|:---------------------- 72 | LoadResource |path:String, env:Table/nil |指定文件路径和环境,用于加载资源 73 | GetResourcePath |resource:Any |从资源表达获取它的定义文件路径 74 | AddRelatedPath |path:String, relative:String |为资源添加关联资源的路径,当验证资源是否修改时,关联资源也会被检查,关联资源可能是接口,超类等,所以,一旦修改,需要在当前资源反映出来 75 | SetReloadWhenModified |path:String, flag:Boolean/true |设置一个资源文件,是否在修改后重新加载 76 | 77 | 以web的模板系统为例,我们有母模版页,用于提供所有网页共享部分,然后继承它实现含有导航条的模板页,再继承导航页实现各个页面等,那么当母模版页修改后,就应该反映到所有的页面中,但在项目上线后,就需要关闭修改验证,否则大量验证文件修改时间会造成巨大的系统负担。 78 | 79 | 为了通用管理,这个接口也提供了静态属性用于调节全局行为: 80 | 81 | 静态属性 |描述 82 | :----------------------|:---------------------- 83 | ReloadWhenModified |是否对所有资源开启修改验证,默认false 84 | -------------------------------------------------------------------------------- /Docs-zh/018.encode.md: -------------------------------------------------------------------------------- 1 | # System.Text.Encoding 2 | 3 | `Encoding`用于将文本解析为Unicode代码(Unicode Code Points),或者将Unicode代码转为字文本。 4 | 5 | `Encoding`并非是类或者接口,它是一个独立的原型,我们无法用它构建对象实体,但可以用它创建新的编码原型。`Encoding`是可验证类型,可以用于校验其他编码原型: 6 | 7 | ```lua 8 | __Arguments__{ String, System.Text.Encoding/System.Text.UTF8Encoding } 9 | function getCodes(str, encoder) end 10 | ``` 11 | 12 | 13 | ## System.Text.ASCIIEncoding 14 | 15 | `ASCIIEncoding`用于提供对ASCII编码的支持,通过它,我们可以熟悉编码原型的用法: 16 | 17 | ```lua 18 | require "PLoop" (function(_ENV) 19 | import "System.Text" 20 | 21 | -- Decodes返回迭代器,遍历获取所有Unicode代码 22 | -- 116, 101, 115, 116 23 | print(List( ASCIIEncoding.Decodes("test") ):Join(", ")) 24 | 25 | -- 将一组代码转为文本 26 | -- test 27 | print( List(ASCIIEncoding.Encodes{ 116, 101, 115, 116 }):Join("") ) 28 | 29 | -- test 30 | print( List(ASCIIEncoding.Encodes(ASCIIEncoding.Decodes("test"))):Join("") ) 31 | 32 | -- 按位置获取Unicode代码 33 | -- 116 34 | print(ASCIIEncoding.Decode("test")) 35 | 36 | -- 101 37 | print(ASCIIEncoding.Decode("test", 2)) 38 | 39 | -- 将指定代码转为文本 40 | -- t 41 | print(ASCIIEncoding.Encode(116)) 42 | 43 | -- e 44 | print(ASCIIEncoding.Encode(101)) 45 | end) 46 | ``` 47 | 48 | 所有的编码原型都提供四种方法: 49 | 50 | 方法 |参数 |描述 51 | :--------------|:----------------------------|:------------------- 52 | Encode |code:Number |将Unicode代码转为字符 53 | Encodes |list:Table + IList |将传入的table或列表中的所有代码转换并返回迭代器 54 | Encodes |iter:Function, ...:Any * 0 |将迭代器返回的代码转换并返回迭代器 55 | Decode |str:String, index:Number/1 |将指定位置的字符转为Unicode代码,也会返回byte数,nil表示只占用1 byte 56 | Decodes |str:String |返回一个迭代器,按顺序返回所有的Unicode代码 57 | 58 | 59 | ## System.Text.UTF8Encoding 60 | 61 | `UTF8Encoding`用于提供UTF8编码的支持 62 | 63 | 64 | ## System.Text.UTF16EncodingLE 65 | 66 | `UTF16EncodingLE`用于提供UTF-16小端编码的支持 67 | 68 | 69 | ## System.Text.UTF16EncodingBE 70 | 71 | `UTF16EncodingLE`用于提供UTF-16大端编码的支持 72 | 73 | 74 | ## Define an encoder prototype 75 | 76 | 以`ASCIIEncoding`的定义为例: 77 | 78 | ```lua 79 | System.Text.Encoding "ASCIIEncoding" { 80 | encode = string.char, 81 | decode = string.byte, 82 | } 83 | ``` 84 | 85 | 定义编码原型时,仅需要实现`encode`和`decode`方法,而ascii的编码仅需要使用string库自带的api即可。所有,可以参考ascii这两个api的功能理解其他编码原型所需要提供的共。 86 | 87 | 为了统一起见,只提供名字不提供全路径时,生成的原型将按照名字指定保存在`System.Text`命名空间下,上述生成的原型访问路径即`System.Text.ASCIIEncoding`。 88 | 89 | 但请注意,和string.byte不同,作为`decode`方法需要返回byte数做为第二返回值,这样系统才能确定如果按照byte读取所有文本。 -------------------------------------------------------------------------------- /Docs/008.threading.md: -------------------------------------------------------------------------------- 1 | # threading 2 | 3 | The **System.Threading** contains several tools to easy the using of Lua's coroutines. Like thread pool, async declaration, iterator generator and lock mechanism(this will be discussed in the multi-os thread part). 4 | 5 | ## Table of Contents 6 | * [ThreadPool](#threadpool) 7 | * [Run a function as coroutine](#run-a-function-as-coroutine) 8 | * [Safe run a function in coroutine](#safe-run-a-function-in-coroutine) 9 | * [Run a function as an iterator](#run-a-function-as-an-iterator) 10 | * [Get the coroutine or wrap function](#get-the-coroutine-or-wrap-function) 11 | * [`__Async__`](#`__async__`) 12 | * [`__Iterator__`](#`__iterator__`) 13 | 14 | 15 | ## ThreadPool 16 | 17 | The thread pool is used to genreate coroutines and recycle them automatically when their tasks are finished. Nomrally the Lua run on a single OS thread, so there is no need to create multi thread pools, we can use **System.ThreadPool.Default** for our tasks directly. 18 | 19 | ### Run a function as coroutine 20 | 21 | We can process any function within a coroutine provided by the thread pool with **ThreadCall** method: 22 | 23 | ```lua 24 | require "PLoop" (function(_ENV) 25 | function test(...) 26 | print(coroutine.running(), ...) 27 | end 28 | 29 | ThreadPool.Default:ThreadCall(test, 1, 2, 3) -- thread: 00EE3C90 1 2 3 30 | ThreadPool.Default:ThreadCall(test, 1, 2, 3) -- thread: 00EE3C90 1 2 3 31 | end) 32 | ``` 33 | 34 | We can see the coroutine is re-used, in Lua, the creation of a coroutine is expensive, through the thread pool and special design of those coroutines we can save many cost. 35 | 36 | 37 | ### Safe run a function in coroutine 38 | 39 | The **ThreadCall** will always use a coroutine for the target function, if we only want to make sure the target function processed in the coroutine, we can use the **SafeThreadCall** method: 40 | 41 | ```lua 42 | require "PLoop" (function(_ENV) 43 | function safeThreadCall() 44 | print("safe", coroutine.running()) 45 | end 46 | 47 | function normalThreadCall() 48 | print("normal", coroutine.running()) 49 | ThreadPool.Default:SafeThreadCall(safeThreadCall) 50 | end 51 | 52 | function test() 53 | print("test", coroutine.running()) 54 | ThreadPool.Default:ThreadCall(normalThreadCall) 55 | end 56 | 57 | -- test thread: 00D81C84 58 | -- normal thread: 00D81E1C 59 | -- safe thread: 00D81E1C 60 | ThreadPool.Default:ThreadCall(test) 61 | end) 62 | ``` 63 | 64 | So the safe thread call will use the caller's coroutine. 65 | 66 | 67 | ### Run a function as an iterator 68 | 69 | In many scenario, we should keep the context of an iterator, it's hard to be done with the common iterator generator since we should always keep upvalues to control the iterators. With the coroutine, we can yield values for many times, it's easy to use them as iterator: 70 | 71 | ```lua 72 | require "PLoop" (function(_ENV) 73 | function Fibonacci(maxn) 74 | local n0, n1 = 1, 1 75 | 76 | coroutine.yield(0, n0) 77 | coroutine.yield(1, n1) 78 | 79 | local n = 2 80 | 81 | while n <= maxn do 82 | n0, n1 = n1, n0 + n1 83 | coroutine.yield(n, n1) 84 | n = n + 1 85 | end 86 | end 87 | 88 | -- 0 1 89 | -- 1 1 90 | -- 2 2 91 | -- 3 3 92 | -- 4 5 93 | -- 5 8 94 | for k, v in ThreadPool.Default:GetIterator(Fibonacci, 5) do 95 | print(k, v) 96 | end 97 | 98 | -- we also can pass the argument later 99 | for k, v in ThreadPool.Default:GetIterator(Fibonacci), 5 do 100 | print(k, v) 101 | end 102 | end) 103 | ``` 104 | 105 | 106 | ### Get the coroutine or wrap function 107 | 108 | In the previous examples, we let the system take controls of those coroutines. We also can get a coroutine or a wrap from the thread pool like the original coroutine.create or coroutine.wrap. 109 | 110 | ```lua 111 | require "PLoop" (function(_ENV) 112 | function test(...) 113 | print(coroutine.running(), ...) 114 | coroutine.yield() 115 | return ... 116 | end 117 | 118 | local thread = ThreadPool.Default:GetThread(test) 119 | print(thread) -- thread: 00F8D4C0 120 | coroutine.resume(thread, 1, 2, 3) -- thread: 00F8D4C0 1 2 3 121 | print(coroutine.resume(thread)) -- true 1 2 3 122 | 123 | local wrap = ThreadPool.Default:GetThread(test, true) 124 | print(wrap) -- function: 011F1058 125 | wrap(1, 2, 3) -- thread: 010C0E30 1 2 3 126 | print(wrap(1, 2, 3)) -- 1 2 3 127 | end) 128 | ``` 129 | 130 | The coroutine used here are not recyclable, the different between it to the coroutine.create(wrap) is the thread pool itself is an argument to the thread, so the thread know which thread pool its generated throught the call stack, it's very useful in multi-os thread platforms, the system'll able to track the context of the thread. 131 | 132 | 133 | ## `__Async__` 134 | 135 | It's not a good code style to keep using the **ThreadPool.Default**, and in multi-os thread platforms, we shouldn't use the default pool, we should use **ThreadPool.Current** to get a auto-created thread pool for current os-thread(we'll see more in Context chapter). 136 | 137 | It'd be difficult for the user to choose the pool, so to simple the job, the **Threading** also provide two attribute for the job. 138 | 139 | The `__Async__` attribute will use the **ThreadCall** method to process the target function. 140 | 141 | Attribute Targets: 142 | * System.AttributeTargets.Function 143 | * System.AttributeTargets.Method 144 | 145 | ```lua 146 | require "PLoop" (function(_ENV) 147 | __Async__() 148 | function test(...) 149 | print(coroutine.running(), ...) 150 | end 151 | 152 | test(1, 2, 3) -- thread: 00F73208 1 2 3 153 | test(1, 2, 3) -- thread: 00F73208 1 2 3 154 | end) 155 | ``` 156 | 157 | Now, the calling style will be a simple function call. 158 | 159 | If we need use the **SafeThreadCall** method to process the target function, we can use `__Async__(true)`. 160 | 161 | 162 | ## `__Iterator__` 163 | 164 | The `__Iterator__` attribute will use the **GetIterator** method to process the target function. 165 | 166 | Attribute Targets: 167 | * System.AttributeTargets.Function 168 | * System.AttributeTargets.Method 169 | 170 | ```lua 171 | require "PLoop" (function(_ENV) 172 | __Iterator__() 173 | function Fibonacci(maxn) 174 | local n0, n1 = 1, 1 175 | 176 | coroutine.yield(0, n0) 177 | coroutine.yield(1, n1) 178 | 179 | local n = 2 180 | 181 | while n <= maxn do 182 | n0, n1 = n1, n0 + n1 183 | coroutine.yield(n, n1) 184 | n = n + 1 185 | end 186 | end 187 | 188 | -- 0 1 189 | -- 1 1 190 | -- 2 2 191 | -- 3 3 192 | -- 4 5 193 | -- 5 8 194 | for k, v in Fibonacci(5) do 195 | print(k, v) 196 | end 197 | end) 198 | ``` 199 | 200 | The iterators will works just like **pairs**. -------------------------------------------------------------------------------- /Docs/013.autoclose.md: -------------------------------------------------------------------------------- 1 | # AutoClose & with 2 | 3 | When we need open some resources like file, data base, we must close them no matter how the operation processed. 4 | 5 | Take a file as example: 6 | 7 | ```lua 8 | function calcSum(path) 9 | local f = io.open(path, "r") 10 | local s = 0 11 | 12 | while l = f:lines() do 13 | s = tonumber(l) + s 14 | end 15 | 16 | f:close() 17 | 18 | return s 19 | end 20 | ``` 21 | 22 | If a line don't contains the number value, an error will be raised and the file won't be closed. 23 | 24 | 25 | ## System.IAutoClose 26 | 27 | The **IAutoClose** is an interface only contains two abstract method that need to be override: 28 | 29 | Abstract Method |Arguments |Description 30 | :---------------------------|:------------------------|:----------------------------- 31 | Open | |Process the open operation 32 | Close |error |Process the close operation with error message(if failed) 33 | 34 | 35 | ## with 36 | 37 | The **with** keyword can receive several IAutoClose objects to auto open and close them no matter if there are errors: 38 | 39 | ```lua 40 | require "PLoop" (function(_ENV) 41 | class "A" { IAutoClose, 42 | 43 | Open = function(self) 44 | print( "Open " .. self.name ) 45 | end, 46 | 47 | Close = function(self, err) 48 | print("Close " .. self.name .. (err and (" with " .. err) or " without error")) 49 | end, 50 | } 51 | 52 | -- Open task 53 | -- process task 54 | -- Close task without error 55 | with(A{ name = "task"})(function(obj) 56 | print("process " .. obj.name) 57 | end) 58 | 59 | -- Open task 60 | -- Open task2 61 | -- process task, task2 62 | -- Close task with xxx.lua:23: 2333 63 | -- Close task2 with xxx.lua:23: 2333 64 | -- Catch error:xxx.lua:23: 2333 65 | with(A{ name = "task"}, A{ name = "task2"})(function(obj, obj2) 66 | print("process " .. obj.name .. ", " .. obj2.name) 67 | error("2333") 68 | end, function(err) 69 | print("Catch error:" .. err) 70 | end) 71 | end) 72 | ``` 73 | 74 | So the second function would be used as error handler, if ommit, the *error* api will be used. 75 | 76 | You can get all return values from the inner function: 77 | 78 | ```lua 79 | require "PLoop" (function(_ENV) 80 | class "A" { IAutoClose } 81 | 82 | -- 1 2 3 4 83 | print(with(A())(function(obj) 84 | return 1, 2, 3, 4 85 | end)) 86 | end) 87 | 88 | ``` 89 | 90 | Here is a real project example for data base operation(see System.Data for more examples): 91 | 92 | ```lua 93 | function RecordLastLogin(id) 94 | -- New database context and open the connection 95 | with(MyDBContext())(function(ctx) 96 | -- Process a transaction 97 | with(ctx.Transaction)(function(trans) 98 | -- Query and lock the data row 99 | local user = ctx.Users:Lock{ id = id }:First() 100 | if user then 101 | user.LastLogin = Date.Now 102 | 103 | -- save to the data base 104 | ctx:SaveChanges() 105 | else 106 | -- cancel the transaction 107 | trans:Rollback() 108 | end 109 | end) 110 | end) 111 | end 112 | ``` -------------------------------------------------------------------------------- /Docs/017.io.resource.md: -------------------------------------------------------------------------------- 1 | # System.IO.Resource 2 | 3 | Besides the normal Lua files, we still need others, like a html template file to generate http response. They are resources, and the **Resource** system is used to provide support for it. 4 | 5 | **This is a topic only usable for framework creators, so you can just skip it if you don't need to know the details behind the systems like the web framework.** 6 | 7 | 8 | ## System.IO.Resource.IResourceLoader 9 | 10 | Every type resources should have a resource loader to load them into the system, like build a view class from html template. 11 | 12 | The **IResourceLoader** is the root interface for all resource loaders, also the initiator that start loading resources. 13 | 14 | 15 | Static Method |Arguments |Description 16 | :----------------------|:-----------------------------------------------------------------------|:----------------------- 17 | LoadResource |path:String, reader:TextReader/nil, env:Table/nil, tryLock:Boolean/nil |Load the resource of given path 18 | 19 | 20 | Abstract Method |Arguments |Description 21 | :----------------------|:-----------------------------------------------------------------------|:----------------------- 22 | Load |path:String, reader:TextReader/nil, env:Table/nil |Load the resource from the path 23 | 24 | 25 | To declare a resouece loader class, we could use the `System.IO.Resource.__ResourceLoader__` attribute, here is an example used by **System.IO.Resource.LuaLoader** which used to load the lua file and return the type with the file's name: 26 | 27 | ```lua 28 | System.IO.Resource.__ResourceLoader__"lua" 29 | class "System.IO.Resource.LuaLoader" { System.IO.Resource.IResourceLoader } 30 | ``` 31 | 32 | So we only need create class extend the **IResourceLoader** and bind with suffix, if you need the loader match several suffix, you can use `__ResourceLoader__("lua", "luax", "luac")`. 33 | 34 | Here is an example to load a resource: 35 | 36 | ```lua 37 | require "PLoop" 38 | require "PLoop.System.IO" 39 | 40 | PLoop(function(_ENV) 41 | import "System.IO" 42 | import "System.IO.Resource" 43 | 44 | code = [[ 45 | PLoop(function(_ENV) 46 | class "Test" {} 47 | end) 48 | ]] 49 | 50 | -- The path is needed so the system can get the suffix and diff it from others 51 | cls = IResourceLoader.LoadResource("test.lua", StringReader(code)) 52 | 53 | print(Class.Validate(cls)) -- Test 54 | end) 55 | ``` 56 | 57 | So we load a code named `test.lua`, and the class *Test* defined in it should be returned. You may read the **LuaLoader** for more details. 58 | 59 | 60 | ## System.IO.Resource.IResourceManager 61 | 62 | Besides loading resources, we also need to manager them, we should keep using the loaded resources, and may reload them when their file is modified. 63 | 64 | The **IResourceManager** provided the resource controls based on the **IResourceLoader**. 65 | 66 | Static Method |Arguments |Description 67 | :----------------------|:------------------------------|:---------------------- 68 | LoadResource |path:String, env:Table/nil |Load the file and return the resource 69 | GetResourcePath |resource:Any |Gets the file path from a resource 70 | AddRelatedPath |path:String, relative:String |Add a related path to the resource path for reload checking 71 | SetReloadWhenModified |path:String, flag:Boolean/true |Mark the path reload when modified 72 | 73 | 74 | Take the web template system as an example, a page class may use another page as it's master, so it don't need to set the sharable parts like the navigation bars, the master page is a related path to it, so when the master page is modified, when load the page class again, the related paths should also be checked, the master page'll be reloaded and the page class will get the update. 75 | 76 | 77 | Static Property |Description 78 | :----------------------|:---------------------- 79 | ReloadWhenModified |whether reload all the file when modified, default false 80 | 81 | If we turn on the property, all registered resources should be reloaded when modified, it's useful in development, should be turn off when released. -------------------------------------------------------------------------------- /Docs/018.encode.md: -------------------------------------------------------------------------------- 1 | # System.Text.Encoding 2 | 3 | The **Encoding** is used to decode text to unicode code points or encode the unicode code points to text. 4 | 5 | The **Encoding** is a prototype, so you can't used it to create objects, but can use it to generate encoder prototypes. 6 | 7 | The **Encoding** is a validatable type, but the encoder prototypes generated from it aren't validatable types, but they can pass the validation of the **Encoding**, so you can still use it like: 8 | 9 | ```lua 10 | __Arguments__{ String, System.Text.Encoding/System.Text.UTF8Encoding } 11 | function getCodes(str, encoder) end 12 | ``` 13 | 14 | 15 | ## System.Text.ASCIIEncoding 16 | 17 | The **ASCIIEncoding** is used to provide the support for ASCII encoding: 18 | 19 | ```lua 20 | require "PLoop" (function(_ENV) 21 | import "System.Text" 22 | 23 | -- 116, 101, 115, 116 24 | print(List( ASCIIEncoding.Decodes("test") ):Join(", ")) 25 | 26 | -- test 27 | print( List(ASCIIEncoding.Encodes{ 116, 101, 115, 116 }):Join("") ) 28 | 29 | -- test 30 | print( List(ASCIIEncoding.Encodes(ASCIIEncoding.Decodes("test"))):Join("") ) 31 | 32 | -- 116 33 | print(ASCIIEncoding.Decode("test")) 34 | 35 | -- 101 36 | print(ASCIIEncoding.Decode("test", 2)) 37 | 38 | -- t 39 | print(ASCIIEncoding.Encode(116)) 40 | 41 | -- e 42 | print(ASCIIEncoding.Encode(101)) 43 | end) 44 | 45 | ``` 46 | 47 | The encoder prototypes all have provided four methods: 48 | 49 | Method |Arguments |Description 50 | :--------------|:----------------------------|:------------------- 51 | Encode |code:Number |Encode the unicode code point to a char 52 | Encodes |list:Table + IList |Encode all codes in the list to an iterator 53 | Encodes |iter:Function, ...:Any * 0 |Encode all codes from the iterator to an iterator 54 | Decode |str:String, index:Number/1 |Decode a char at the index of the text to unicode code point, also will return the byte count, nil means 1-byte 55 | Decodes |str:String |Return an iterator to generate all unicode code points from the target text 56 | 57 | 58 | ## System.Text.UTF8Encoding 59 | 60 | The **UTF8Encoding** is used to provide the support for UTF8 encoding 61 | 62 | 63 | ## System.Text.UTF16EncodingLE 64 | 65 | The **UTF16EncodingLE** is used to provide the support for UTF-16 encoding with little-endian. 66 | 67 | 68 | ## System.Text.UTF16EncodingBE 69 | 70 | The **UTF16EncodingBE** is used to provide the support for UTF-16 encoding with big-endian. 71 | 72 | 73 | ## Define an encoder prototype 74 | 75 | To define the encoder prototype, only need provide the **Encode** and **Decode** functions, take the ASCIIEncoding as an example: 76 | 77 | ```lua 78 | System.Text.Encoding "ASCIIEncoding" { 79 | encode = string.char, 80 | decode = string.byte, 81 | } 82 | ``` 83 | 84 | If only provide the name of the prototype, it'll be saved to **System.Text** namespace, the **ASCIIEncoding** is using *string.char* as **Encode** and the *string.byte* as **Decode**, so you can create your own encoder like it. 85 | 86 | But keep in mind, the **Decode** should also return the byte count as the second return value, if omit, the system will use 1 as default byte count. 87 | -------------------------------------------------------------------------------- /Docs/024.dependencyinjection.md: -------------------------------------------------------------------------------- 1 | # DependencyInjection 2 | 3 | The dependency injection system require one platform settings to be enabled: `ENABLE_ARGUMENTS_ATTACHMENT`, so the system can figure out how to generate the instance based on the arguments settings of constructors and methods. 4 | 5 | The system support constructor injection, object method injection, static method injection, module function injection and property injection. 6 | 7 | 8 | ## Table of Contents 9 | 10 | * [Build The Service Provider](#build-the-service-provider) 11 | * [Constructor Injection](#constructor-injection) 12 | * [Module Function Injection](#module-function-injection) 13 | * [Object Method Injection](#object-method-injection) 14 | * [Static Method Injection](#static-method-injection) 15 | * [Property Injection](#property-injection) 16 | 17 | 18 | ## Build The Service Provider 19 | 20 | Save the code below as `provider.lua` to be used in other examples: 21 | 22 | ```lua 23 | PLOOP_PLATFORM_SETTINGS = { 24 | ENABLE_ARGUMENTS_ATTACHMENT = true 25 | } 26 | 27 | local provider 28 | 29 | require "PLoop" (function(_ENV) 30 | import "System.DependencyInjection" 31 | 32 | -- Use struct for config 33 | struct "AppConfig" { 34 | name = { type = String, require = true }, 35 | jwt = { require = true, type = { 36 | key = Guid, 37 | expireTime = NaturalNumber, 38 | } 39 | }, 40 | port = NaturalNumber, 41 | } 42 | 43 | -- Declare the service 44 | interface "IAppService" {} 45 | 46 | -- Implement the service 47 | class "AppService" (function(_ENV) 48 | extend "IAppService" "IAutoClose" 49 | 50 | function Open(self) 51 | print("[Open] AppService", self) 52 | end 53 | 54 | function Close(self) 55 | print("[Close] AppService", self) 56 | end 57 | 58 | property "Config" { type = AppConfig } 59 | 60 | __Arguments__{ AppConfig } 61 | function __ctor(self, config) 62 | self.Config = config 63 | end 64 | end) 65 | 66 | -- Build the service collection 67 | services = ServiceCollection() 68 | 69 | services:AddSingleton(AppConfig, { 70 | name = "Test", 71 | jwt = { 72 | key = Guid.New(), 73 | expireTime = 60, 74 | }, 75 | port = 5555 76 | }) 77 | 78 | services:AddScoped(IAppService, AppService) 79 | 80 | -- Build the service provider 81 | provider = services:BuildServiceProvider() 82 | end) 83 | 84 | return provider 85 | ``` 86 | 87 | 88 | ## Constructor Injection 89 | 90 | Here is a simple usage of the dependency injection system: 91 | 92 | ```lua 93 | local provider = require "provider" 94 | 95 | PLoop(function(_ENV) 96 | with(provider:CreateScope())(function(scope) 97 | local app = scope.ServiceProvider:GetService(IAppService) 98 | print(app.Config.jwt.key) 99 | app.Config.port = 666 100 | 101 | app = scope.ServiceProvider:GetService(IAppService) 102 | print(app.Config.port) 103 | end) 104 | end) 105 | ``` 106 | 107 | The result will be 108 | 109 | [Open] AppService table: 01118E58 110 | 42D308D9-AFC9-23CF-1197-5D83E15BA8E3 111 | 666 112 | [Close] AppService table: 01118E58 113 | 114 | So a new app service is created in the scope, it'll be auto open when it's created and auto close when the scope is closed. 115 | 116 | 117 | 118 | ## Module Function Injection 119 | 120 | We can declare module function with `__Arguments__`, they can be processed by the service provider: 121 | 122 | ```lua 123 | local provider = require "provider" 124 | 125 | print(provider) 126 | 127 | PLoop(function(_ENV) 128 | __Arguments__{ IAppService } 129 | function TestApp(app) 130 | return app.Config.jwt.key 131 | end 132 | 133 | with(provider:CreateScope())(function(scope) 134 | print( scope.ServiceProvider:CallMethod(TestApp) ) 135 | end) 136 | end) 137 | ``` 138 | 139 | The result will be 140 | 141 | [Open] AppService table: 01489E60 142 | 5B63E9CF-AFC3-52A7-32A7-C40359B1A1B9 143 | [Close] AppService table: 01489E60 144 | 145 | 146 | 147 | ## Object Method Injection 148 | 149 | We also can process the object method by service provider like 150 | 151 | ```lua 152 | local provider = require "provider" 153 | 154 | PLoop(function(_ENV) 155 | class "TestCase" (function(_ENV) 156 | __Arguments__{ IAppService } 157 | function TestApp(self, app) 158 | return app.Config.jwt.key 159 | end 160 | end) 161 | 162 | with(provider:CreateScope())(function(scope) 163 | case = TestCase() 164 | 165 | print( scope.ServiceProvider:CallMethod(case, "TestApp") ) 166 | end) 167 | end) 168 | ``` 169 | 170 | The result will be 171 | 172 | [Open] AppService table: 01212B20 173 | 5E53A295-6199-C609-65A7-8BEF46095BBF 174 | [Close] AppService table: 01212B20 175 | 176 | 177 | ## Static Method Injection 178 | 179 | Besides the object method, static method is also supported: 180 | 181 | ```lua 182 | local provider = require "provider" 183 | 184 | PLoop(function(_ENV) 185 | class "TestCase" (function(_ENV) 186 | __Static__() __Arguments__{ IAppService } 187 | function TestApp(app) 188 | return app.Config.jwt.key 189 | end 190 | end) 191 | 192 | with(provider:CreateScope())(function(scope) 193 | print( scope.ServiceProvider:CallMethod(TestCase, "TestApp") ) 194 | end) 195 | end) 196 | ``` 197 | 198 | The result will be 199 | 200 | [Open] AppService table: 00FF1C30 201 | 5F8D613B-8CAB-05C7-819D-4C75D5331205 202 | [Close] AppService table: 00FF1C30 203 | 204 | 205 | 206 | ## Property Injection 207 | 208 | If an object is generated by the service provider, it can have auto injection properties: 209 | 210 | ```lua 211 | local provider = require "provider" 212 | 213 | PLoop(function(_ENV) 214 | import "System.DependencyInjection" 215 | 216 | --- Redefine the service 217 | class "AppService" (function(_ENV) 218 | __AutoInjection__() 219 | property "AutoConfig" { type = AppConfig } 220 | end) 221 | 222 | with(provider:CreateScope())(function(scope) 223 | local service = scope.ServiceProvider:GetService(IAppService) 224 | print(service.AutoConfig.jwt.key) 225 | end) 226 | end) 227 | ``` 228 | 229 | The result will be 230 | 231 | [Open] AppService table: 014097A0 232 | 649953AD-7E4F-6CB7-D4F9-069FA3C761E3 233 | [Close] AppService table: 014097A0 -------------------------------------------------------------------------------- /Docs/025.range.md: -------------------------------------------------------------------------------- 1 | System.Collections.Range 2 | ==== 3 | 4 | The range object is used to represent the range of values like from 1 to 100 by step 2. 5 | 6 | So we can declare a range object like 7 | 8 | ```lua 9 | require "PLoop" (function(_ENV) 10 | r = Range(1, 100, 2) 11 | 12 | print(r) -- [1, 99, 2] 13 | 14 | print(r:Sum()) -- 2500 15 | 16 | print(r[30]) -- 59 17 | 18 | for i, v in r:GetIterator() do end 19 | end) 20 | ``` 21 | 22 | The range object extend `IIndexedList`, so we can access it's elments by index without really store those values. 23 | 24 | We can get iterator from it, also use all methods provided by the interface like `Map`, `Filter` and etc. 25 | 26 | 27 | Union 28 | ==== 29 | 30 | Besides the feature of list, the ranges are sets, so we can use set operations on them. 31 | 32 | For two given sets A and B, A ∪ B (read as A union B) is the set of distinct elements that belong to set A and set B or both. 33 | 34 | ```lua 35 | require "PLoop" (function(_ENV) 36 | print(Range(1, 5) + Range(3, 7)) -- [1, 7, 1] 37 | print(Range(1, 3) + Range(5, 7) + Range(10, 11)) -- [1, 3, 1] .. [5, 7, 1] .. [10, 11, 1] 38 | end) 39 | ``` 40 | 41 | So we can use `+` to combine two ranges into one, the values in one range can be discontinuous. 42 | 43 | 44 | Intersection 45 | ==== 46 | 47 | For two given sets A and B, A ∩ B (read as A intersection B) is the set of common elements that belong to set A and B. 48 | 49 | ```lua 50 | require "PLoop" (function(_ENV) 51 | print(Range(1, 5) * Range(3, 7)) -- [3, 5, 1] 52 | print(Range(1, 3) * Range(5, 7)) -- [5, 3, 1] 53 | end) 54 | ``` 55 | 56 | For the next result, since stop is less than start, so it's count is 0, we can't get any element from it. 57 | 58 | 59 | Difference between sets/Relative Complement 60 | ==== 61 | 62 | Relative complement is a term used to describe the set of elements contained in a given set that are not elements of another specified set. 63 | 64 | ```lua 65 | require "PLoop" (function(_ENV) 66 | print(Range(1, 5) - Range(3, 7)) -- [1, 2, 1] 67 | print(Range(1, 3) - Range(5, 7)) -- [1, 3, 1] 68 | end) 69 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2011- 2021 WangXH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /System/Collections.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Collections -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2014/10/13 -- 11 | -- Update Date : 2018/03/15 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Collections" 17 | 18 | Environment.RegisterGlobalNamespace("System.Collections") 19 | 20 | --- Represents the interface of collections 21 | __Sealed__() 22 | interface "Iterable" (function (_ENV) 23 | --- Return the iterator, maybe with obj and start index 24 | __Abstract__() function GetIterator(self) end 25 | end) 26 | end) -------------------------------------------------------------------------------- /System/Collections/Array.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Collections.Array -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2018/03/20 -- 11 | -- Update Date : 2018/03/20 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Collections" 17 | 18 | import "System.Serialization" 19 | 20 | --- The array of objects with event control 21 | -- @usage 22 | -- class "A" { 23 | -- -- Property 24 | -- Name = { Type = String, Event = "OnNameChanged", Default = "Anonymous" }; 25 | -- 26 | -- -- Constructor 27 | -- A = function(self, name) self.Name = name end; 28 | -- } 29 | -- 30 | -- -- The Array is a template class, you can specific the element class 31 | -- ar = Array[A](A("Ann"), A("Ben"), A("Coco")) 32 | -- 33 | -- -- You can set event handler to all objects by assign it to the array 34 | -- function ar:OnNameChanged(new, old) 35 | -- print( ("%s -> %s"):format(old, new) ) 36 | -- end 37 | __ObjFuncAttr__() __SuperObject__(false) __Serializable__() 38 | __NoRawSet__(false) __NoNilValue__(false) __Arguments__{ AnyType }( Any ) 39 | class "Array" (function(_ENV, eletype) 40 | inherit (List[eletype]) 41 | 42 | ----------------------------------------------------------- 43 | -- helper -- 44 | ----------------------------------------------------------- 45 | local addeventlistener = function() end 46 | 47 | export { 48 | parseindex = Toolset.parseindex, 49 | getErrorMessage = Struct.GetErrorMessage, 50 | } 51 | 52 | if Interface.Validate(eletype) or Class.Validate(eletype) then 53 | export { 54 | getfeature = Interface.GetFeature, 55 | validevent = Event.Validate, 56 | rawget = rawget, 57 | rawset = rawset, 58 | pairs = pairs, 59 | tinsert = table.insert, 60 | ARRAY_EVENT = "__PLOOP_ARRAY_EVENT", 61 | } 62 | 63 | export { Event } 64 | 65 | function addeventlistener(self, obj) 66 | local evts = rawget(self, ARRAY_EVENT) 67 | if evts then 68 | for k, v in pairs(evts) do 69 | obj[k] = v 70 | end 71 | end 72 | end 73 | end 74 | 75 | ----------------------------------------------------------- 76 | -- method -- 77 | ----------------------------------------------------------- 78 | if Interface.Validate(eletype) or Class.Validate(eletype) then 79 | __Arguments__{ Integer, eletype } 80 | function Insert(self, idx, obj) 81 | tinsert(self, idx, obj) 82 | addeventlistener(self, obj) 83 | end 84 | 85 | __Arguments__{ eletype } 86 | function Insert(self, obj) 87 | tinsert(self, obj) 88 | addeventlistener(self, obj) 89 | end 90 | end 91 | 92 | ----------------------------------------------------------- 93 | -- meta-method -- 94 | ----------------------------------------------------------- 95 | if Interface.Validate(eletype) or Class.Validate(eletype) then 96 | __Arguments__{ String, Callable } 97 | function __newindex(self, key, value) 98 | local evt = getfeature(eletype, key) 99 | if evt and validevent(evt) then 100 | local evts = rawget(self, ARRAY_EVENT) or {} 101 | rawset(self, ARRAY_EVENT, evts) 102 | evts[key] = value 103 | 104 | self:Each(key, value) 105 | else 106 | error("The " .. tostring(eletype) .. " don't have an event named " .. tostring(key), 2) 107 | end 108 | end 109 | 110 | __Arguments__{ Number, eletype } 111 | __newindex = rawset 112 | else 113 | __Arguments__{ String, Callable } 114 | function __newindex(self, key) 115 | error("The " .. tostring(eletype) .. " don't have an event named " .. tostring(key), 2) 116 | end 117 | 118 | __Arguments__{ Number, eletype } 119 | __newindex = rawset 120 | end 121 | end) 122 | end) -------------------------------------------------------------------------------- /System/Data/Core.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Data.Core -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2018/09/07 -- 11 | -- Update Date : 2018/09/07 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | __Sealed__() __Final__() 17 | interface "System.Data" (function(_ENV) 18 | export { safeset = Toolset.safeset } 19 | 20 | ----------------------------------------------------------- 21 | -- types -- 22 | ----------------------------------------------------------- 23 | __Sealed__() 24 | enum "ConnectionState" { 25 | Closed = 0, 26 | Open = 1, 27 | Connecting = 2, 28 | Executing = 3, 29 | Fetching = 4, 30 | } 31 | 32 | __Sealed__() 33 | struct "DBNull" { function(val) return val ~= DBNull end } 34 | 35 | ----------------------------------------------------------- 36 | -- methods -- 37 | ----------------------------------------------------------- 38 | local NULL_VALUE = { [DBNull] = true } 39 | 40 | --- Add Empty value for ParseString 41 | __Static__() 42 | function AddNullValue(value) 43 | if value == nil then return end 44 | NULL_VALUE = safeset(NULL_VALUE, value, true) 45 | end 46 | 47 | --- Parse the value so special null value can be changed to nil 48 | __Static__() 49 | function ParseValue(value) 50 | if value == nil or NULL_VALUE[value] then return nil end 51 | return value 52 | end 53 | end) 54 | end) -------------------------------------------------------------------------------- /System/Data/init.lua: -------------------------------------------------------------------------------- 1 | require "PLoop" 2 | require "PLoop.System.Context" 3 | 4 | require "PLoop.System.Data.Core" 5 | require "PLoop.System.Data.DataBase" 6 | require "PLoop.System.Data.Cache" 7 | require "PLoop.System.Data.DataCache" -------------------------------------------------------------------------------- /System/IO/Directory.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.IO.Directory -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2018/03/29 -- 11 | -- Update Date : 2019/09/20 -- 12 | -- Version : 1.1.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.IO" 17 | 18 | __Final__() __Sealed__() __Abstract__() 19 | class "Directory" (function (_ENV) 20 | 21 | export { 22 | strfind = string.find, 23 | strmatch = string.match, 24 | strgmatch = string.gmatch, 25 | strsub = string.sub, 26 | strgsub = string.gsub, 27 | strformat = string.format, 28 | select = select, 29 | yield = coroutine.yield, 30 | } 31 | 32 | if OperationSystem.Current == OperationSystemType.Windows then 33 | --- Get sub-directories 34 | __PipeRead__("dir /A:D /b \"%s\"", ".*", OperationSystemType.Windows) 35 | __Iterator__() 36 | __Static__() 37 | function GetDirectories(path, result) 38 | if result then 39 | for word in strgmatch(result, "[^\n]+") do 40 | yield(word) 41 | end 42 | end 43 | end 44 | 45 | --- Get files 46 | __PipeRead__("dir /A:-D /b \"%s\"", ".*", OperationSystemType.Windows) 47 | __Iterator__() 48 | __Static__() 49 | function GetFiles(path, result) 50 | if result then 51 | for word in strgmatch(result, "[^\n]+") do 52 | yield(word) 53 | end 54 | end 55 | end 56 | else 57 | --- Get sub-directories 58 | __PipeRead__("ls --file-type \"%s\"", ".*", OperationSystemType.Linux) 59 | __PipeRead__("export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin'\nls -F \"%s\"", ".*", OperationSystemType.MacOS) 60 | __Iterator__() 61 | __Static__() 62 | function GetDirectories(path, result) 63 | if result then 64 | for word in strgmatch(result, "%S+") do 65 | if strsub(word, -1) == "/" then 66 | yield(strsub(word, 1, -2)) 67 | end 68 | end 69 | end 70 | end 71 | 72 | --- Get files 73 | __PipeRead__("ls --file-type \"%s\"", ".*", OperationSystemType.Linux) 74 | __PipeRead__("export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin'\nls -F \"%s\"", ".*", OperationSystemType.MacOS) 75 | __Iterator__() 76 | __Static__() 77 | function GetFiles(path, result) 78 | if result then 79 | for word in strgmatch(result, "%S+") do 80 | if strsub(word, -1) ~= "/" then 81 | yield(word) 82 | end 83 | end 84 | end 85 | end 86 | end 87 | 88 | --- Whether the target directory existed 89 | __PipeRead__ ("IF EXIST \"%s\" (echo exist) ELSE (echo missing)", "exist", OperationSystemType.Windows) 90 | __PipeRead__ ("[ -d \"%s\" ] && echo \"exist\" || echo \"missing\"", "exist", OperationSystemType.MacOS + OperationSystemType.Linux) 91 | __Static__() 92 | function Exist(dir, result) 93 | return result == "exist" 94 | end 95 | 96 | --- Create directory if not existed 97 | __PipeRead__ (function(dir) return strformat("export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin'\n[ ! -d \"%s\" ] && mkdir -p \"%s\"", dir, dir) end, "", OperationSystemType.MacOS + OperationSystemType.Linux) 98 | __PipeRead__ (function(dir) return strformat("IF NOT EXIST \"%s\" (mkdir \"%s\")", dir, dir) end, "", OperationSystemType.Windows) 99 | __Static__() 100 | function Create(dir) 101 | end 102 | 103 | --- Delete the directory 104 | __PipeRead__ (function(dir) if dir:match("^%s*/%s*$") then return end return strformat("export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin'\nrm -rf \"%s\"", dir) end, "", OperationSystemType.MacOS + OperationSystemType.Linux) 105 | __PipeRead__ (function(dir) return strformat("rd /q /s \"%s\"", dir) end, "", OperationSystemType.Windows) 106 | __Static__() 107 | function Delete(dir) 108 | end 109 | end) 110 | end) 111 | -------------------------------------------------------------------------------- /System/IO/File.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.IO.File -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/01/28 -- 11 | -- Update Date : 2018/03/26 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.IO" 17 | 18 | __Final__() __Sealed__() __Abstract__() 19 | class "File" (function (_ENV) 20 | export { 21 | fopen = _G.io and _G.io.open or Toolset.fakefunc, 22 | strmatch = string.match, 23 | strformat = string.format, 24 | strgsub = string.gsub, 25 | type = type, 26 | error = error, 27 | Error = Logger.Default[Logger.LogLevel.Error], 28 | } 29 | 30 | local function formatMacTime(result) 31 | if result then 32 | local month, day, time, year = strmatch(result, "(%w+)%s+(%d+)%s+(%d+:%d+:%d+)%s+(%d+)") 33 | if month then 34 | return ("%s-%s-%s %s"):format(year, month, day, time) 35 | end 36 | end 37 | end 38 | 39 | --- Get the file's creation time 40 | __PipeRead__ (function(path) return strformat("dir /t:c \"%s\"", strgsub(path, "/", "\\")) end, "%d+/%d+/%d+%s+[%d:]+", OperationSystemType.Windows) 41 | __PipeRead__ ("export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin'\nls -l -T -U \"%s\"", formatMacTime, OperationSystemType.MacOS) 42 | __PipeRead__ ("ls --full-time \"%s\"", "%d+-%d+-%d+%s+[%d:]+", OperationSystemType.Linux) 43 | __Static__() 44 | function GetCreationTime(path, result) 45 | if not result then Error("[System.IO.File][GetCreationTime][Fail] %s - %s", path, result or "nil") end 46 | return result 47 | end 48 | 49 | --- Get the file's last modified time 50 | __PipeRead__ (function(path) return strformat("forfiles /p \"%s\" /M \"%s\" /C \"cmd /c echo @fdate @ftime\"", strmatch(strgsub(path, "/", "\\"), "^(.+)[\\/](.-)$")) end, "[^\n]+", OperationSystemType.Windows) 51 | __PipeRead__ ("export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin'\nls -l -T \"%s\"", formatMacTime, OperationSystemType.MacOS) 52 | __PipeRead__ ("ls --full-time \"%s\"", "%d+-%d+-%d+%s+[%d:]+", OperationSystemType.Linux) 53 | __Static__() 54 | function GetLastWriteTime(path, result) 55 | if not result then Error("[System.IO.File][GetLastWriteTime][Fail] %s - %s", path, result or "nil") end 56 | return result 57 | end 58 | 59 | --- Get the file's last access time 60 | __PipeRead__ (function(path) return strformat("dir /t:a \"%s\"", strgsub(path, "/", "\\")) end, "%d+/%d+/%d+%s+[%d:]+", OperationSystemType.Windows) 61 | __PipeRead__ ("export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin'\nls -l -T -u \"%s\"", formatMacTime, OperationSystemType.MacOS) 62 | __PipeRead__ ("ls --full-time -u \"%s\"", "%d+-%d+-%d+%s+[%d:]+", OperationSystemType.Linux) 63 | __Static__() 64 | function GetLastAccessTime(path, result) 65 | if not result then Error("[System.IO.File][GetLastAccessTime][Fail] %s - %s", path, result or "nil") end 66 | return result 67 | end 68 | 69 | --- Whether the file is existed 70 | __Static__() 71 | function Exist(path) local f = fopen(path, "r") if f then f:close() return true end return false end 72 | 73 | --- Delete the file 74 | __PipeRead__ (function(path) return strformat("export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin'\nrm -f \"%s\"", path) end, "", OperationSystemType.MacOS + OperationSystemType.Linux) 75 | __PipeRead__ (function(path) return strformat("del /f \"%s\"", path) end, "", OperationSystemType.Windows) 76 | __Static__() 77 | function Delete(path) 78 | end 79 | 80 | __Static__() 81 | function Copy(source, target, force) 82 | if type(source) ~= "string" then error("Usage: Path.Copy(source, target) - the source must be a string", 2) end 83 | if type(target) ~= "string" then error("Usage: Path.Copy(source, target) - the target must be a string", 2) end 84 | 85 | if not force then 86 | local f = fopen(target, "r") 87 | if f then f:close() error("Usage: Path.Copy(source, target) - the target already existed", 2) end 88 | end 89 | 90 | source = fopen(source, "rb") 91 | if not source then error("Usage: Path.Copy(source, target) - the source file can't be open", 2) end 92 | 93 | target = fopen(target, "wb") 94 | if not target then error("Usage: Path.Copy(source, target) - the target file is not valid", 2) end 95 | 96 | local line = source:read(100) 97 | while line do 98 | target:write(line) 99 | if #line == 100 then 100 | line = source:read(100) 101 | else 102 | break 103 | end 104 | end 105 | 106 | source:close() 107 | target:close() 108 | end 109 | 110 | __PipeRead__ (function(source, target) return strformat("export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin'\nmv \"%s\" \"%s\"", source, target) end, "", OperationSystemType.MacOS + OperationSystemType.Linux, 2) 111 | __PipeRead__ (function(source, target) return strformat("move /Y \"%s\" \"%s\"", source:gsub("/", "\\"), target:gsub("/", "\\")) end, "", OperationSystemType.Windows, 2) 112 | __Static__() 113 | function Move(source, target) 114 | end 115 | end) 116 | end) -------------------------------------------------------------------------------- /System/IO/FileReader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.IO.FileReader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/07/11 -- 11 | -- Update Date : 2018/03/26 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.IO" 17 | 18 | --- Represents a writer that can write a sequential series of characters to files 19 | __Sealed__() 20 | class "FileReader" (function(_ENV) 21 | inherit "System.Text.TextReader" 22 | 23 | export { 24 | fopen = _G.io and _G.io.open or Toolset.fakefunc, 25 | floor = math.floor, 26 | error = error, 27 | } 28 | 29 | --- the file read mode 30 | __Sealed__() __Default__("r") 31 | enum "FileReadMode" { 32 | Read = "r", 33 | ReadBinary = "rb", 34 | } 35 | 36 | -- Field 37 | field { 38 | [0] = false, 39 | [1] = false, 40 | [2] = false, 41 | } 42 | 43 | --- Gets or sets the file position. negative number means start from the end of the file. 44 | property "Position" { Type = Number, 45 | Get = function(self) return self[0]:seek() end, 46 | Set = function(self, pos) 47 | pos = floor(pos) 48 | if pos < 0 then 49 | return self[0]:seek("end", pos) 50 | else 51 | return self[0]:seek("set", pos) 52 | end 53 | end, 54 | } 55 | 56 | -- Method 57 | function Read(self) return self[0]:read(1) end 58 | 59 | function ReadLine(self) return self[0]:read("*l") end 60 | 61 | function ReadToEnd(self) return self[0]:read("*a") end 62 | 63 | function ReadBlock(self, count, index) 64 | if index then self[0]:seek("set", index) end 65 | return self[0]:read(count) 66 | end 67 | 68 | function Open(self) 69 | self[0] = fopen(self[1], self[2]) or false 70 | if not self[0] then error("Failed to open the file - " .. self[1], 2) end 71 | end 72 | 73 | function Close(self) return self[0]:close() end 74 | 75 | -- Constructor 76 | __Arguments__{ String, FileReadMode/FileReadMode.Read } 77 | function __new(_, file, mode) 78 | return { [1] = file, [2] = mode }, false 79 | end 80 | end) 81 | end) -------------------------------------------------------------------------------- /System/IO/FileWriter.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.IO.FileWriter -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/07/11 -- 11 | -- Update Date : 2018/03/26 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.IO" 17 | 18 | --- Represents a writer that can write a sequential series of characters to files 19 | __Sealed__() 20 | class "FileWriter" (function(_ENV) 21 | inherit "System.Text.TextWriter" 22 | 23 | export { 24 | fopen = _G.io and _G.io.open or Toolset.fakefunc 25 | } 26 | 27 | __Sealed__() __Default__"w" 28 | enum "FileWriteMode" { 29 | Write = "w", 30 | Append = "a", 31 | WritePlus = "w+", 32 | AppendPlus = "a+", 33 | WriteBinary = "wb", 34 | AppendBinary = "ab", 35 | WritePlusBinary = "w+b", 36 | AppendPlusBinary = "a+b", 37 | } 38 | 39 | -- Field 40 | field { 41 | [0] = false, 42 | [1] = false, 43 | [2] = false, 44 | } 45 | 46 | -- Method 47 | function Write(self, text) if text ~= nil then self[0]:write(text) end end 48 | 49 | function Flush(self) self[0]:flush() end 50 | 51 | function Open(self) 52 | self[0] = fopen(self[1], self[2]) or false 53 | if not self[0] then error("Failed to open the file - " .. self[1], 2) end 54 | end 55 | 56 | function Close(self) self[0]:close() end 57 | 58 | -- Constructor 59 | __Arguments__{ String, FileWriteMode/FileWriteMode.Write } 60 | function __new(_, file, mode) 61 | return { [1] = file, [2] = mode }, false 62 | end 63 | 64 | -- Meta-method 65 | __call = Write 66 | end) 67 | end) -------------------------------------------------------------------------------- /System/IO/Path.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.IO.Path -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/01/28 -- 11 | -- Update Date : 2018/03/26 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.IO" 17 | 18 | __Final__() __Sealed__() __Abstract__() 19 | class "Path" (function (_ENV) 20 | 21 | export { 22 | strfind = string.find, 23 | strmatch = string.match, 24 | strsub = string.sub, 25 | strgsub = string.gsub, 26 | select = select, 27 | } 28 | 29 | --- Get the current operation path 30 | __PipeRead__ ("echo %%cd%%", "[^\n]+", OperationSystemType.Windows, 0) 31 | __PipeRead__ ("pwd", "[^\n]+", OperationSystemType.MacOS + OperationSystemType.Linux, 0) 32 | __Static__() 33 | function GetCurrentPath(result) return result end 34 | 35 | --- Whether the path contains the root path 36 | __Static__() 37 | function IsPathRooted(path) return (strfind(path, "^[\\/]") or strfind(path, "^%a:[\\/]")) and true or false end 38 | 39 | --- Get the root of the path 40 | __Static__() 41 | function GetPathRoot(path) return (strmatch(path, "^[\\/]") or strmatch(path, "^%a:[\\/]")) end 42 | 43 | --- Get the directory of the path, if the path is a root, empty string would be returned. 44 | __Static__() 45 | function GetDirectory(path) return (strgsub(strsub(path, 1, -2), "[^\\/]*$", "")) end 46 | 47 | --- Combine the paths 48 | __Static__() 49 | function CombinePath(...) 50 | local path 51 | local dirSep 52 | local up 53 | for i = 1, select('#', ...) do 54 | local part = select(i, ...) 55 | if not path then 56 | local root = GetPathRoot(part) 57 | if root then 58 | path = part 59 | if #root == 1 then dirSep = root else dirSep = strsub(root, -1) end 60 | end 61 | else 62 | -- Check . & .. 63 | part, up = strgsub(part, "^%.%.[\\/]", "") 64 | while up > 0 do 65 | local updir = GetDirectory(path) 66 | if updir and #updir > 0 then path = updir end 67 | 68 | part, up = strgsub(part, "^%.%.[\\/]", "") 69 | end 70 | 71 | repeat part, up = strgsub(part, "^%.[\\/]", "") until up == 0 72 | 73 | local prevSep, nxtSep = strfind(path, "[\\/]$"), strfind(part, "^[[\\/]") 74 | if prevSep and nxtSep then 75 | path = path .. strsub(part, 2) 76 | elseif prevSep or nxtSep then 77 | path = path .. part 78 | else 79 | path = path .. dirSep .. part 80 | end 81 | end 82 | end 83 | return path 84 | end 85 | 86 | --- Get the suffix of the path, include the '.' 87 | __Static__() 88 | function GetSuffix(path) return strmatch(path, "%.[_%w]+$") end 89 | 90 | --- Get the file name 91 | __Static__() 92 | function GetFileName(path) return strmatch(path, "[^\\/]*$") end 93 | 94 | --- Get the file name without the suffix 95 | __Static__() 96 | function GetFileNameWithoutSuffix(path) return (strgsub(GetFileName(path), "%.[_%w]+$", "")) end 97 | 98 | export { 99 | GetFileName = Path.GetFileName, 100 | GetDirectory = Path.GetDirectory, 101 | GetPathRoot = Path.GetPathRoot, 102 | } 103 | end) 104 | end) 105 | -------------------------------------------------------------------------------- /System/IO/Resource/CaseSensitiveTest.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kurapica/PLoop/ece40c141421eea3bb708f325090dd6b6ed78b81/System/IO/Resource/CaseSensitiveTest.lua -------------------------------------------------------------------------------- /System/IO/Resource/LuaLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.IO.Resource.LuaLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/01/28 -- 11 | -- Update Date : 2018/03/16 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.IO.Resource" 17 | 18 | __Sealed__() __ResourceLoader__"lua" 19 | class "LuaLoader" (function (_ENV) 20 | extend "IResourceLoader" 21 | 22 | export { 23 | pairs = pairs, 24 | strlower = string.lower, 25 | getfilename = System.IO.Path.GetFileNameWithoutSuffix, 26 | getname = Namespace.GetNamespaceName, 27 | require = _G.require, 28 | loadfile = _G.loadfile, 29 | setfenv = _G.setfenv or false, 30 | loadsnippet = Toolset.loadsnippet, 31 | pcall = pcall, 32 | error = error, 33 | 34 | Runtime, 35 | } 36 | 37 | function Load(self, path, reader, env) 38 | local name = strlower(getfilename(path)) 39 | 40 | local type 41 | 42 | local ontypedefined = function(ftype, target) 43 | if strlower(getname(target, true)) == name then 44 | type = target 45 | end 46 | end 47 | 48 | Runtime.OnTypeDefined = Runtime.OnTypeDefined + ontypedefined 49 | 50 | local func, msg 51 | 52 | if reader then 53 | func, msg = loadsnippet(reader:ReadToEnd(), path, env) 54 | if func then 55 | func, msg = pcall(func) 56 | end 57 | else 58 | func, msg = loadfile(path, nil, env) 59 | if func then 60 | if setfenv and env then setfenv(func, env) end 61 | func, msg = pcall(func) 62 | end 63 | end 64 | 65 | Runtime.OnTypeDefined = Runtime.OnTypeDefined - ontypedefined 66 | 67 | if not func then error(msg, 0) end 68 | 69 | return type 70 | end 71 | end) 72 | end) -------------------------------------------------------------------------------- /System/IO/init.lua: -------------------------------------------------------------------------------- 1 | -- The init file of PLoop.System.IO 2 | require "PLoop" 3 | 4 | require "PLoop.System.Threading.Lock" 5 | 6 | require "PLoop.System.IO.OperationSystem" 7 | require "PLoop.System.IO.Path" 8 | require "PLoop.System.IO.Directory" 9 | require "PLoop.System.IO.File" 10 | require "PLoop.System.IO.FileWriter" 11 | require "PLoop.System.IO.FileReader" 12 | require "PLoop.System.IO.Gzip" 13 | 14 | -- System.IO.Resource 15 | require "PLoop.System.IO.Resource.IResourceLoader" 16 | require "PLoop.System.IO.Resource.IResourceManager" 17 | require "PLoop.System.IO.Resource.LuaLoader" -------------------------------------------------------------------------------- /System/Message.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System Message -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2020/09/01 -- 11 | -- Update Date : 2020/09/01 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Message" 17 | 18 | 19 | --- The interface that represents the publish–subscribe messaging pattern 20 | __Sealed__() 21 | interface "IPublisher" (function(_ENV) 22 | extend "System.IAutoClose" 23 | 24 | ----------------------------------------------------------------------- 25 | -- method -- 26 | ----------------------------------------------------------------------- 27 | --- Subscribe a message filter, topic-based, return true if successful, otherwise false and error code is needed 28 | __Abstract__() 29 | function SubscribeTopic(self, filter) return true end 30 | 31 | --- Unsubscribe a message filter, topic-based, return true if successful, otherwise false and error code is needed 32 | __Abstract__() 33 | function UnsubscribeTopic(self, filter) return true end 34 | 35 | --- Publish the msssage, it'd give a topic if it's topic-based message 36 | __Abstract__() 37 | function PublishMessage(self, topic, message) return true end 38 | 39 | --- Receive and return the published message 40 | __Abstract__() 41 | function ReceiveMessage(self) end 42 | end) 43 | 44 | --- A simple Message Publisher that provides the default implementation, only for testing 45 | __Sealed__() 46 | class "MessagePublisher" (function(_ENV) 47 | extend "IPublisher" 48 | 49 | export { "next", "pairs", Queue } 50 | 51 | local _Publisher = Toolset.newtable(true) 52 | 53 | ----------------------------------------------------------------------- 54 | -- property -- 55 | ----------------------------------------------------------------------- 56 | --- The topic filters 57 | property "TopicFilters" { set = false, default = Toolset.newtable } 58 | 59 | --- The received message queue 60 | property "ReceivedMessageQueue" { set = false, default = function() return Queue() end } 61 | 62 | ----------------------------------------------------------------------- 63 | -- method -- 64 | ----------------------------------------------------------------------- 65 | --- Subscribe a message filter, topic-based, return true if successful, otherwise false and error code is needed 66 | function SubscribeTopic(self, filter) 67 | self.TopicFilters[filter] = true 68 | _Publisher[self] = true 69 | 70 | return true 71 | end 72 | 73 | --- Unsubscribe a message filter, topic-based, return true if successful, otherwise false and error code is needed 74 | function UnsubscribeTopic(self, filter) 75 | self.TopicFilters[filter] = nil 76 | 77 | if not next(self.TopicFilters) then 78 | _Publisher[self] = nil 79 | end 80 | 81 | return true 82 | end 83 | 84 | --- Publish the msssage, it'd give a topic if it's topic-based message 85 | function PublishMessage(self, topic, message, ...) 86 | for publisher in pairs(_Publisher) do 87 | for filter in pairs(publisher.TopicFilters) do 88 | if topic:match(filter) then 89 | publisher.ReceivedMessageQueue:Enqueue(topic, message) 90 | break 91 | end 92 | end 93 | end 94 | end 95 | 96 | --- Receive and return the published message 97 | function ReceiveMessage(self) 98 | return self.ReceivedMessageQueue:Dequeue(2) 99 | end 100 | 101 | --- Close the publisher 102 | function Close(self) 103 | _Publisher[self] = nil 104 | end 105 | end) 106 | end) 107 | -------------------------------------------------------------------------------- /System/Net/MQTT/init.lua: -------------------------------------------------------------------------------- 1 | -- The init file of PLoop.System.Net.MQTT 2 | 3 | require "PLoop.System.Net" 4 | 5 | -- Core 6 | require "PLoop.System.Net.MQTT.Core" 7 | require "PLoop.System.Net.MQTT.MessagePublisher" 8 | require "PLoop.System.Net.MQTT.Context" 9 | -------------------------------------------------------------------------------- /System/Net/Modbus/init.lua: -------------------------------------------------------------------------------- 1 | -- The init file of PLoop.System.Net.Modbus(TCP) 2 | 3 | require "PLoop.System.Net" 4 | 5 | -- Core 6 | require "PLoop.System.Net.Modbus.Core" -------------------------------------------------------------------------------- /System/Net/OPC/EventTypes.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Net.Protocol.OPC.EventTypes -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2021/08/11 -- 11 | -- Update Date : 2021/08/1 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Net.OPC" 17 | 18 | 19 | --- BaseEventType 20 | __Sealed__() __Abstract__() __Node__{ NodeId = 2041 } 21 | class "BaseEventType" { BaseObjectType } 22 | 23 | __Sealed__() __Abstract__() __Node__{ NodeId = 2130 } 24 | class "SystemEventType" { BaseEventType } 25 | 26 | __Sealed__() __Abstract__() __Node__{ NodeId = 11436 } 27 | class "ProgressEventType" { BaseEventType } 28 | 29 | __Sealed__() __Abstract__() __Node__{ NodeId = 2052 } 30 | class "AuditEventType" { BaseEventType } 31 | 32 | __Sealed__() __Abstract__() __Node__{ NodeId = 2058 } 33 | class "AuditSecurityEventType" { AuditEventType } 34 | 35 | __Sealed__() __Abstract__() __Node__{ NodeId = 2059 } 36 | class "AuditChannelEventType" { AuditEventType } 37 | 38 | __Sealed__() __Abstract__() __Node__{ NodeId = 2060 } 39 | class "AuditOpenSecureChannelEventType" { AuditChannelEventType } 40 | 41 | __Sealed__() __Abstract__() __Node__{ NodeId = 2069 } 42 | class "AuditSessionEventType" { AuditSecurityEventType } 43 | 44 | __Sealed__() __Abstract__() __Node__{ NodeId = 2071 } 45 | class "AuditCreateSessionEventType" { AuditSessionEventType } 46 | 47 | __Sealed__() __Abstract__() __Node__{ NodeId = 2748 } 48 | class "AuditUrlMismatchEventType" { AuditCreateSessionEventType } 49 | 50 | __Sealed__() __Abstract__() __Node__{ NodeId = 2075 } 51 | class "AuditActivateSessionEventType" { AuditSessionEventType } 52 | 53 | __Sealed__() __Abstract__() __Node__{ NodeId = 2078 } 54 | class "AuditCancelEventType" { AuditSessionEventType } 55 | 56 | __Sealed__() __Abstract__() __Node__{ NodeId = 2080 } 57 | class "AuditCertificateEventType" { AuditSecurityEventType } 58 | 59 | __Sealed__() __Abstract__() __Node__{ NodeId = 2082 } 60 | class "AuditCertificateDataMismatchEventType" { AuditCertificateEventType } 61 | 62 | __Sealed__() __Abstract__() __Node__{ NodeId = 2085 } 63 | class "AuditCertificateExpiredEventType" { AuditCertificateEventType } 64 | 65 | __Sealed__() __Abstract__() __Node__{ NodeId = 2086 } 66 | class "AuditCertificateInvalidEventType" { AuditCertificateEventType } 67 | 68 | __Sealed__() __Abstract__() __Node__{ NodeId = 2087 } 69 | class "AuditCertificateUntrustedEventType" { AuditCertificateEventType } 70 | 71 | __Sealed__() __Abstract__() __Node__{ NodeId = 2088 } 72 | class "AuditCertificateRevokedEventType" { AuditCertificateEventType } 73 | 74 | __Sealed__() __Abstract__() __Node__{ NodeId = 2089 } 75 | class "AuditCertificateMismatchEventType" { AuditCertificateEventType } 76 | 77 | __Sealed__() __Abstract__() __Node__{ NodeId = 2090 } 78 | class "AuditNodeManagementEventType" { AuditEventType } 79 | 80 | __Sealed__() __Abstract__() __Node__{ NodeId = 2091 } 81 | class "AuditAddNodesEventType" { AuditNodeManagementEventType } 82 | 83 | __Sealed__() __Abstract__() __Node__{ NodeId = 2093 } 84 | class "AuditDeleteNodesEventType" { AuditNodeManagementEventType } 85 | 86 | __Sealed__() __Abstract__() __Node__{ NodeId = 2095 } 87 | class "AuditAddReferencesEventType" { AuditNodeManagementEventType } 88 | 89 | __Sealed__() __Abstract__() __Node__{ NodeId = 2097 } 90 | class "AuditDeleteReferencesEventType" { AuditNodeManagementEventType } 91 | 92 | __Sealed__() __Abstract__() __Node__{ NodeId = 2099 } 93 | class "AuditUpdateEventType" { AuditEventType } 94 | 95 | __Sealed__() __Abstract__() __Node__{ NodeId = 2100 } 96 | class "AuditWriteUpdateEventType" { AuditUpdateEventType } 97 | 98 | __Sealed__() __Abstract__() __Node__{ NodeId = 2104 } 99 | class "AuditHistoryUpdateEventType" { AuditUpdateEventType } 100 | 101 | __Sealed__() __Abstract__() __Node__{ NodeId = 2127 } 102 | class "AuditUpdateMethodEventType" { AuditEventType } 103 | 104 | __Sealed__() __Abstract__() __Node__{ NodeId = 2131 } 105 | class "DeviceFailureEventType" { SystemEventType } 106 | 107 | __Sealed__() __Abstract__() __Node__{ NodeId = 11446 } 108 | class "SystemStatusChangeEventType" { SystemEventType } 109 | 110 | __Sealed__() __Abstract__() __Node__{ NodeId = 2132 } 111 | class "BaseModelChangeEventType" { BaseEventType } 112 | 113 | __Sealed__() __Abstract__() __Node__{ NodeId = 2133 } 114 | class "GeneralModelChangeEventType" { BaseModelChangeEventType } 115 | 116 | __Sealed__() __Abstract__() __Node__{ NodeId = 2738 } 117 | class "SemanticChangeEventType" { BaseEventType } 118 | end) -------------------------------------------------------------------------------- /System/Net/OPC/Services.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Net.Protocol.OPC.Services -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2021/08/12 -- 11 | -- Update Date : 2021/08/12 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Net.OPC" 17 | 18 | class "Server" (function(_ENV) 19 | property "NamespaceArray" { 20 | default = { 21 | [0] = "http://opcfoundation.org/UA/", 22 | } 23 | } 24 | end) 25 | 26 | __Sealed__() 27 | class "Service" (function(_ENV) 28 | 29 | end) 30 | 31 | class "DiscoveryService" { Service } 32 | 33 | class "SecureChannelService" { Service } 34 | 35 | class "SessionService" { Service } 36 | 37 | class "NodeManagementService" { Service } 38 | 39 | class "ViewService" { Service } 40 | 41 | class "QueryService" { Service } 42 | 43 | class "AttributeService" { Service } 44 | 45 | class "CallService" { Service } 46 | 47 | class "MonitoredItemService" { Service } 48 | 49 | class "SubscriptionService" { Service } 50 | 51 | class "RequestResponseService" { Service } 52 | 53 | --- a set of related Services 54 | __Sealed__() 55 | class "ServiceSet" (function(_ENV) 56 | end) 57 | 58 | --- defines Services that allow a Client to discover the Endpoints implemented by a Server and to read the security configuration for each of those Endpoints 59 | class "DiscoveryServiceSet" { ServiceSet } 60 | 61 | --- defines Services that allow a Client to establish a communication channel to ensure the Confidentiality and Integrity of Messages exchanged with the Server 62 | class "SecureChannelServiceSet" { ServiceSet } 63 | 64 | --- defines Services that allow the Client to authenticate the user on whose behalf it is acting and to manage Sessions 65 | class "SessionServiceSet" { ServiceSet } 66 | 67 | --- defines Services that allow the Client to add, modify and delete Nodes in the AddressSpace 68 | class "NodeManagementServiceSet" { ServiceSet } 69 | 70 | --- defines Services that allow Clients to browse through the AddressSpace or subsets of the AddressSpace called Views. 71 | -- The Query Service Set allows Clients to get a subset of data from the AddressSpace or the View 72 | class "ViewServiceSet" { ServiceSet } 73 | 74 | --- defines Services that allow Clients to read and write Attributes of Nodes, including their historical values 75 | class "AttributeServiceSet" { ServiceSet } 76 | 77 | --- defines Services that allow Clients to call methods 78 | class "MethodServiceSet" { ServiceSet } 79 | 80 | --- defines Services that allow Clients to create, modify, and delete MonitoredItems used to monitor Attributes for value changes and Objects for Events 81 | class "MonitoredItemServiceSet" { ServiceSet } 82 | 83 | --- defines Services that allow Clients to create, modify and delete Subscriptions 84 | class "SubscriptionServiceSet" { ServiceSet } 85 | end) -------------------------------------------------------------------------------- /System/Net/OPC/VariableTypes.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Net.Protocol.OPC.VariableTypes -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2021/08/06 -- 11 | -- Update Date : 2021/08/06 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Net.OPC" 17 | 18 | __Sealed__() __Node__{ NodeId = 62 } 19 | class "BaseVariableType" { Variable } 20 | 21 | __Node__() __Node__{ NodeId = 68, ValueRank = -2 } 22 | class "PropertyType" (function(_ENV) 23 | inherit "BaseVariableType" 24 | 25 | --------------------------------------------------- 26 | -- property -- 27 | --------------------------------------------------- 28 | --- The owner of the property 29 | property "Owner" { type = Any } 30 | end) 31 | 32 | __Sealed__() __Node__{ NodeId = 63, ValueRank = -2, DataType = Any } 33 | class "BaseDataVariableType" { BaseVariableType } 34 | 35 | __Sealed__() __Node__{ NodeId = 2138, ValueRank = -1, DataType = ServerStatusDataType } 36 | class "ServerStatusType" { BaseDataVariableType } 37 | 38 | __Sealed__() __Abstract__() __Node__{ NodeId = 2137, ValueRank = -1, DataType = Any } 39 | class "ServerVendorCapabilityType" { BaseDataVariableType } 40 | end) -------------------------------------------------------------------------------- /System/Net/OPC/init.lua: -------------------------------------------------------------------------------- 1 | -- The init file of PLoop.System.Net.MQTT 2 | 3 | require "PLoop.System.Net" 4 | 5 | -- Core 6 | require "PLoop.System.Net.OPC.Core" 7 | require "PLoop.System.Net.OPC.DataTypes" 8 | require "PLoop.System.Net.OPC.Reference" 9 | require "PLoop.System.Net.OPC.NodeClasses" 10 | require "PLoop.System.Net.OPC.EventTypes" 11 | require "PLoop.System.Net.OPC.VariableTypes" 12 | require "PLoop.System.Net.OPC.ObjectTypes" 13 | require "PLoop.System.Net.OPC.Services" 14 | 15 | -- require "PLoop.System.Net.OPC.Client" -------------------------------------------------------------------------------- /System/Net/Snap7/init.lua: -------------------------------------------------------------------------------- 1 | -- The init file of PLoop.System.Net.Snap7 2 | 3 | require "PLoop.System.Net" 4 | 5 | -- Core 6 | require "PLoop.System.Net.Snap7.Core" -------------------------------------------------------------------------------- /System/Net/init.lua: -------------------------------------------------------------------------------- 1 | -- The init file of PLoop.System.Net 2 | 3 | require "PLoop" 4 | require "PLoop.System.IO" 5 | require "PLoop.System.Context" 6 | require "PLoop.System.Message" 7 | 8 | -- Core 9 | require "PLoop.System.Net.Core" 10 | require "PLoop.System.Net.Socket" 11 | -------------------------------------------------------------------------------- /System/Reactive/Observer.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Reactive.Observer -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2019/12/01 -- 11 | -- Update Date : 2024/05/09 -- 12 | -- Version : 2.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Reactive" 17 | 18 | --- The default observer 19 | __Sealed__() 20 | class "Observer" (function(_ENV) 21 | extend "System.IObserver" 22 | 23 | export { Subscription } 24 | 25 | ----------------------------------------------------------------------- 26 | -- constructor -- 27 | ----------------------------------------------------------------------- 28 | __Arguments__{ Callable/nil, Callable/nil, Callable/nil, Subscription/nil } 29 | function __ctor(self, onNext, onError, onCompleted, subscription) 30 | self.OnNext = onNext and function (self, ...) return onNext(...) end 31 | self.OnError = onError and function (self, ex) return onError(ex) end 32 | self.OnCompleted = onCompleted and function (self) self.Subscription = nil return onCompleted() end 33 | self.Subscription = subscription and Subscription(subscription) 34 | end 35 | end) 36 | 37 | -- The observable implementation 38 | class "Observable" (function(_ENV) 39 | extend "System.IObservable" 40 | 41 | export { 42 | -- the core subscribe 43 | subscribe = function (self, observer, subscription) 44 | subscription = subscription or observer.Subscription 45 | if not subscription.IsUnsubscribed then self[0](observer, subscription) end 46 | return subscription, observer 47 | end, 48 | 49 | Observer 50 | } 51 | 52 | ----------------------------------------------------------------------- 53 | -- method -- 54 | ----------------------------------------------------------------------- 55 | --- Subscribe the observer with subscription 56 | __Arguments__{ IObserver, Subscription/nil } 57 | Subscribe = subscribe 58 | 59 | __Arguments__{ Callable/nil, Callable/nil, Callable/nil, Subscription/nil } 60 | Subscribe = function (self, onNext, onError, onCompleted, subscription) 61 | return subscribe(self, Observer(onNext, onError, onCompleted, subscription)) 62 | end 63 | 64 | ----------------------------------------------------------------------- 65 | -- constructor -- 66 | ----------------------------------------------------------------------- 67 | __Arguments__{ Callable } 68 | function __new(_, subscribe) return { [0] = subscribe }, true end 69 | end) 70 | end) -------------------------------------------------------------------------------- /System/Reactive/ReactiveField.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Reactive.ReactiveField -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2024/05/22 -- 11 | -- Update Date : 2025/02/22 -- 12 | -- Version : 2.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- Represents the field in reactive objects 17 | __Sealed__() 18 | __Arguments__{ AnyType/nil } 19 | class"System.Reactive.ReactiveField"(function(_ENV, valtype) 20 | inherit "Subject" 21 | extend "IValueWrapper" "IReactive" 22 | 23 | export { 24 | rawset = rawset, 25 | rawget = rawget, 26 | pcall = pcall, 27 | error = error, 28 | subscribe = Subject.Subscribe, 29 | onnext = Subject.OnNext, 30 | onerror = Subject.OnError, 31 | geterrormessage = Struct.GetErrorMessage, 32 | issubtype = Class.ValidateValue, 33 | 34 | IObservable 35 | } 36 | 37 | local function refresh(self) 38 | -- clear the observable since it binds in this for the old container field 39 | self.Observable = nil 40 | 41 | -- update the value without data push 42 | local raw = self[1] 43 | local value 44 | if raw then 45 | value = raw[self[2]] 46 | else 47 | value = nil 48 | end 49 | if rawget(self, 3) == value then return end 50 | 51 | rawset(self, 3, value) 52 | return onnext(self, value) 53 | end 54 | 55 | ----------------------------------------------------------------------- 56 | -- method -- 57 | ----------------------------------------------------------------------- 58 | --- Subscribe the observer 59 | function Subscribe(self, ...) 60 | local ok, sub, observer = pcall(subscribe, self, ...) 61 | if not ok then error(sub, 2) end 62 | observer:OnNext(self[3]) 63 | return sub, observer 64 | end 65 | 66 | --- Provides the observer with new data 67 | if not valtype or Platform.TYPE_VALIDATION_DISABLED and getmetatable(valtype).IsImmutable(valtype) then 68 | function OnNext(self, val) 69 | local raw = self[1] 70 | if not raw then return end 71 | 72 | -- save 73 | self[3] = val 74 | raw[self[2]] = val 75 | return onnext(self, val) 76 | end 77 | else 78 | local valid = getmetatable(valtype).ValidateValue 79 | function OnNext(self, val) 80 | local raw = self[1] 81 | if not raw then return end 82 | 83 | local ret, msg = valid(valtype, val) 84 | if msg then return onerror(self, geterrormessage(msg, "value")) end 85 | 86 | -- save 87 | self[3] = ret 88 | raw[self[2]] = ret 89 | return onnext(self, ret) 90 | end 91 | end 92 | 93 | ----------------------------------------------------------------------- 94 | -- property -- 95 | ----------------------------------------------------------------------- 96 | --- Whether always connect the observable 97 | property "KeepAlive" { type = Boolean, default = true } 98 | 99 | --- The reactive container 100 | property "Container" { type = Table, field = 1, handler = refresh } 101 | 102 | --- The field 103 | property "Field" { type = String, field = 2, set = false } 104 | 105 | --- The current value, use handler not set to detect the value change 106 | property "Value" { type = valtype, field = 3, handler = "OnNext" } 107 | 108 | ----------------------------------------------------------------------- 109 | -- constructor -- 110 | ----------------------------------------------------------------------- 111 | -- Binding the behavior subject to a reactive object's field 112 | __Arguments__{ Table, String, IObservable/nil } 113 | function __ctor(self, container, field, observable) 114 | -- init without observable 115 | super(self) 116 | 117 | rawset(self, 1, container) 118 | rawset(self, 2, field) 119 | refresh(self) 120 | 121 | -- binding later 122 | self.Observable = observable 123 | end 124 | end) 125 | end) -------------------------------------------------------------------------------- /System/Reactive/ReactiveValue.lua: -------------------------------------------------------------------------------- 1 | 2 | --===========================================================================-- 3 | -- -- 4 | -- System.Reactive.ReactiveValue -- 5 | -- -- 6 | --===========================================================================-- 7 | 8 | --===========================================================================-- 9 | -- Author : kurapica125@outlook.com -- 10 | -- URL : http://github.com/kurapica/PLoop -- 11 | -- Create Date : 2024/05/22 -- 12 | -- Update Date : 2025/02/22 -- 13 | -- Version : 2.0.0 -- 14 | --===========================================================================-- 15 | 16 | PLoop(function(_ENV) 17 | --- Represents the reactive value 18 | __Sealed__() 19 | __Arguments__{ AnyType/nil } 20 | class"System.Reactive.ReactiveValue"(function(_ENV, valtype) 21 | inherit "Subject" 22 | extend "IValueWrapper" "IReactive" 23 | 24 | export { 25 | subscribe = Subject.Subscribe, 26 | onnext = Subject.OnNext, 27 | onerror = Subject.OnError, 28 | geterrormessage = Struct.GetErrorMessage, 29 | pcall = pcall, 30 | error = error, 31 | } 32 | 33 | ----------------------------------------------------------------------- 34 | -- method -- 35 | ----------------------------------------------------------------------- 36 | --- Subscribe the observer 37 | function Subscribe(self, ...) 38 | local ok, sub, observer = pcall(subscribe, self, ...) 39 | if not ok then error(sub, 2) end 40 | if self[1] ~= nil then observer:OnNext(self[1]) end 41 | return sub, observer 42 | end 43 | 44 | --- Provides the observer with new data 45 | if not valtype or Platform.TYPE_VALIDATION_DISABLED and getmetatable(valtype).IsImmutable(valtype) then 46 | function OnNext(self, val) 47 | self[1] = val 48 | return onnext(self, val) 49 | end 50 | else 51 | local valid = getmetatable(valtype).ValidateValue 52 | function OnNext(self, val) 53 | local ret, msg = valid(valtype, val) 54 | if msg then return onerror(self, geterrormessage(msg, "value")) end 55 | self[1] = ret 56 | return onnext(self, ret) 57 | end 58 | end 59 | 60 | ----------------------------------------------------------------------- 61 | -- property -- 62 | ----------------------------------------------------------------------- 63 | --- Whether always connect the observable 64 | property "KeepAlive" { type = Boolean, default = true } 65 | 66 | --- The current value, use handler not set to detect the value change 67 | property "Value" { type = valtype, field = 1, handler = "OnNext" } 68 | 69 | ----------------------------------------------------------------------- 70 | -- constructor -- 71 | ----------------------------------------------------------------------- 72 | -- Generate reactive value based on other observable 73 | __Arguments__{ IObservable } 74 | function __ctor(self, observable) 75 | super(self, observable) 76 | end 77 | 78 | -- Generate reactive value with init data 79 | __Arguments__{ (valtype or Any)/nil } 80 | function __ctor(self, val) 81 | super(self) 82 | self[1] = val 83 | end 84 | end) 85 | end) -------------------------------------------------------------------------------- /System/Scalar.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System Scalar Type -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2021/12/26 -- 11 | -- Update Date : 2021/12/26 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System" 17 | 18 | --- SByte 19 | __Sealed__() struct "SByte" { __base = Integer, function(val, onlyvalid) return (val > 127 or val < -128) and (onlyvalid or "the %s must be an 8 bytes integer") or nil end } 20 | 21 | --- Byte 22 | __Sealed__() struct "Byte" { __base = Integer, function(val, onlyvalid) return (val < 0 or val >= 2^8) and (onlyvalid or "the %s must be an 8 bytes unsigned integer") or nil end } 23 | 24 | --- Int16 25 | __Sealed__() struct "Int16" { __base = Integer, function(val, onlyvalid) return (val > 32767 or val < -32768) and (onlyvalid or "the %s must be an 16 bytes integer") or nil end } 26 | 27 | --- UInt16 28 | __Sealed__() struct "UInt16" { __base = Integer, function(val, onlyvalid) return (val < 0 or val >= 2^16) and (onlyvalid or "the %s must be an 16 bytes unsigned integer") or nil end } 29 | 30 | --- Int32 31 | __Sealed__() struct "Int32" { __base = Integer, function(val, onlyvalid) return (val > 2147483647 or val < -2147483648) and (onlyvalid or "the %s must be an 32 bytes integer") or nil end } 32 | 33 | --- UInt32 34 | __Sealed__() struct "UInt32" { __base = Integer, function(val, onlyvalid) return (val < 0 or val >= 2^32) and (onlyvalid or "the %s must be an 32 bytes unsigned integer") or nil end } 35 | 36 | --- Int64, no limit check 37 | __Sealed__() struct "Int64" { __base = Integer } 38 | 39 | --- UInt64, no limit check 40 | __Sealed__() struct "UInt64" { __base = NaturalNumber } 41 | 42 | --- Float, no check 43 | __Sealed__() struct "Float" { __base = Number } 44 | 45 | --- Double, no check 46 | __Sealed__() struct "Double" { __base = Number } 47 | 48 | --- Represents the positive number 49 | __Sealed__() 50 | struct "PositiveNumber" { __base = Number, function(val, onlyvalid) return val <= 0 and (onlyvalid or "the %s must be a positive number") or nil end } 51 | 52 | --- Represents the negative number 53 | __Sealed__() 54 | struct "NegativeNumber" { __base = Number, function(val, onlyvalid) return val >= 0 and (onlyvalid or "the %s must be a negative number") or nil end } 55 | 56 | --- Represents negative integer value 57 | __Sealed__() 58 | struct "NegativeInteger" { __base = Integer, function(val, onlyvalid) return val >= 0 and (onlyvalid or "the %s must be a negative integer") or nil end } 59 | end) -------------------------------------------------------------------------------- /System/Serialization/LuaFormatProvider.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Serialization.LuaFormatProvider -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2015/09/14 -- 11 | -- Update Date : 2018/03/16 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Serialization" 17 | 18 | --- Serialization format provider for common lua data 19 | __Sealed__() __Final__() 20 | class "LuaFormatProvider" (function(_ENV) 21 | inherit "FormatProvider" 22 | 23 | export { "type", "pairs", Serialization } 24 | 25 | local function removeObjType(data, fld) 26 | data[fld] = nil 27 | for k, v in pairs(data) do 28 | if type(v) == "table" then 29 | removeObjType(v, fld) 30 | end 31 | end 32 | end 33 | 34 | ----------------------------------------------------------------------- 35 | -- property -- 36 | ----------------------------------------------------------------------- 37 | --- Whether ignore the object's type for serialization 38 | property "ObjectTypeIgnored" { type = Boolean, default = false } 39 | 40 | ----------------------------------------------------------------------- 41 | -- Method -- 42 | ----------------------------------------------------------------------- 43 | function Serialize(self, data) 44 | if self.ObjectTypeIgnored and type(data) == "table" then 45 | removeObjType(data, Serialization.ObjectTypeField) 46 | end 47 | return data 48 | end 49 | 50 | function Deserialize(self, data) 51 | return data 52 | end 53 | end) 54 | end) -------------------------------------------------------------------------------- /System/Text/Base64.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Text.Base64 -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2021/01/24 -- 11 | -- Update Date : 2021/01/24 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | ----------------------------------------------------------------------- 17 | -- prepare -- 18 | ----------------------------------------------------------------------- 19 | export { 20 | strbyte = string.byte, 21 | strchar = string.char, 22 | band = Toolset.band, 23 | bor = Toolset.bor, 24 | rshift = Toolset.rshift, 25 | yield = coroutine.yield, 26 | encodeMap = List(("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"):gmatch(".")), 27 | 28 | List, System.Text.UTF8Encoding 29 | } 30 | 31 | export { 32 | decodeMap = Dictionary(encodeMap:Map(strbyte), XList(#encodeMap):Map("i=>i-1")) 33 | } 34 | 35 | encodeMap[0] = encodeMap[1] 36 | encodeMap:RemoveByIndex(1) 37 | 38 | ----------------------------------------------------------------------- 39 | -- Base64 -- 40 | ----------------------------------------------------------------------- 41 | System.Text.Encoder "Base64" { 42 | encode = function (text, noret) 43 | local yield = yield 44 | local total = 0 45 | local length = #text 46 | local i = 1 47 | 48 | while i <= length do 49 | if not noret and total > 0 and total % 76 == 0 then yield("\n") end 50 | 51 | local f, s, t = strbyte(text, i, i + 2) 52 | local cache = f * 0x10000 + (s or 0) * 0x100 + (t or 0) 53 | 54 | yield(encodeMap[rshift(cache, 18)]) 55 | yield(encodeMap[rshift(band(cache, 0x3FFFF), 12)]) 56 | 57 | if s then 58 | yield(encodeMap[rshift(band(cache, 0xFFF), 6)]) 59 | 60 | if t then 61 | yield(encodeMap[band(cache, 0x3F)]) 62 | total = total + 4 63 | else 64 | yield("=") 65 | end 66 | else 67 | yield("==") 68 | end 69 | 70 | i = i + 3 71 | end 72 | end, 73 | decode = function (text, encode) 74 | local yield = yield 75 | local cache = 0 76 | local clen = 0 77 | 78 | for _, byte in (encode or UTF8Encoding).Decodes(text) do 79 | local code = decodeMap[byte] 80 | if code then 81 | if clen == 0 then 82 | clen = 6 83 | cache = code 84 | else 85 | clen = clen + 6 86 | cache = cache * 0x40 + code 87 | 88 | if clen >= 8 then 89 | clen = clen - 8 90 | 91 | yield(strchar(rshift(cache, clen))) 92 | cache = band(cache, 2^clen - 1) 93 | end 94 | end 95 | end 96 | end 97 | end, 98 | strategy = Text.TextReaderStrategy.BLOCK, 99 | block = 4096, 100 | } 101 | end) -------------------------------------------------------------------------------- /System/Text/Crc.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Text.Crc -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2021/02/16 -- 11 | -- Update Date : 2021/02/16 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | export { 17 | strbyte = string.byte, 18 | strchar = string.char, 19 | band = Toolset.band, 20 | bor = Toolset.bor, 21 | bnot = Toolset.bnot, 22 | bxor = Toolset.bxor, 23 | lshift = Toolset.lshift, 24 | rshift = Toolset.rshift, 25 | 26 | crc32_table = {}, 27 | } 28 | 29 | do 30 | for i = 0, 255 do 31 | local crc = i 32 | for j = 1, 8 do crc = bxor(rshift(crc, 1), band(0xEDB88320, bnot(band(crc,1)-1))) end 33 | crc32_table[i] = crc 34 | end 35 | end 36 | 37 | __Static__() __Arguments__{ String, Number/nil } 38 | function System.Text.CRC32(text, crc) 39 | crc = bnot(crc or 0) 40 | for i = 1, #text do crc = bxor(crc32_table[bxor(text:byte(i), band(crc, 0xff))], rshift(crc,8)) end 41 | crc = bnot(crc) 42 | return crc < 0 and (crc + 4294967296) or crc 43 | end 44 | end) -------------------------------------------------------------------------------- /System/Text/UTF16Encoding.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Text.UTF16Encoding -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2015/06/24 -- 11 | -- Update Date : 2018/03/16 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Text" 17 | 18 | ----------------------------------------------------------------------- 19 | -- prepare -- 20 | ----------------------------------------------------------------------- 21 | export { 22 | strbyte = string.byte, 23 | strchar = string.char, 24 | tinsert = table.insert, 25 | tconcat = table.concat, 26 | floor = math.floor, 27 | LUA_VERSION = tonumber(_G._VERSION:match("[%d%.]+")) or 5.1, 28 | loadsnippet = Toolset.loadsnippet, 29 | error = error, 30 | 31 | band = Toolset.band, 32 | lshift = Toolset.lshift, 33 | rshift = Toolset.rshift, 34 | 35 | -- Declare here 36 | decodeLE = false, 37 | encodeLE = false, 38 | decodeBE = false, 39 | encodeBE = false, 40 | } 41 | 42 | function decodeLE(str, startp) 43 | startp = startp or 1 44 | 45 | local sbyte, obyte = strbyte(str, startp, startp + 1) 46 | if not obyte or not sbyte then return nil end 47 | 48 | if obyte <= 0xD7 or obyte >= 0xE0 then 49 | -- two bytes 50 | return lshift(obyte, 8) + sbyte, 2 51 | elseif obyte >= 0xD8 and obyte <= 0xDB then 52 | -- four byte 53 | local fbyte, tbyte = strbyte(str, startp + 2, startp + 3) 54 | if not tbyte or not fbyte then return nil end 55 | 56 | if tbyte >= 0xDC and tbyte <= 0xDF then 57 | return lshift((lshift((obyte - 0xD8), 8) + sbyte), 10) + (lshift((tbyte - 0xDC), 8) + fbyte) + 0x10000, 4 58 | else 59 | return nil 60 | end 61 | else 62 | return nil 63 | end 64 | end 65 | 66 | function encodeLE(code) 67 | if code >= 0 then 68 | -- 2 69 | if code <= 0xD7FF or (code >= 0xE000 and code <= 0xFFFF) then 70 | return strchar( 71 | band(code, 0xff), 72 | rshift(code, 8) 73 | ) 74 | end 75 | 76 | -- 4 surrogate pairs 77 | if code >= 0x10000 and code <= 0x10FFFF then 78 | code = code - 0x10000 79 | local high = rshift(code, 10) 80 | local low = band(code, 0x3ff) 81 | return strchar( 82 | band(high, 0xff), 83 | 0xD8 + rshift(high, 8), 84 | band(low, 0xff), 85 | 0xDC + rshift(low, 8) 86 | ) 87 | end 88 | end 89 | 90 | error(("%s is not a valid unicode."):format(code), 2) 91 | end 92 | 93 | function decodeBE(str, startp) 94 | startp = startp or 1 95 | 96 | local obyte, sbyte = strbyte(str, startp, startp + 1) 97 | if not obyte or not sbyte then return nil end 98 | 99 | if obyte <= 0xD7 or obyte >= 0xE0 then 100 | -- two bytes 101 | return lshift(obyte, 8) + sbyte, 2 102 | elseif obyte >= 0xD8 and obyte <= 0xDB then 103 | -- four byte 104 | local tbyte, fbyte = strbyte(str, startp + 2, startp + 3) 105 | if not tbyte or not fbyte then return nil end 106 | 107 | if tbyte >= 0xDC and tbyte <= 0xDF then 108 | return lshift((lshift((obyte - 0xD8), 8) + sbyte), 10) + (lshift((tbyte - 0xDC), 8) + fbyte) + 0x10000, 4 109 | else 110 | return nil 111 | end 112 | else 113 | return nil 114 | end 115 | end 116 | 117 | function encodeBE(code) 118 | if code >= 0 then 119 | -- 2 120 | if code <= 0xD7FF or (code >= 0xE000 and code <= 0xFFFF) then 121 | return strchar( 122 | rshift(code, 8), 123 | band(code, 0xff) 124 | ) 125 | end 126 | 127 | -- 4 surrogate pairs 128 | if code >= 0x10000 and code <= 0x10FFFF then 129 | code = code - 0x10000 130 | local high = rshift(code, 10) 131 | local low = band(code, 0x3ff) 132 | return strchar( 133 | 0xD8 + rshift(high, 8), 134 | band(high, 0xff), 135 | 0xDC + rshift(low, 8), 136 | band(low, 0xff) 137 | ) 138 | end 139 | end 140 | 141 | error(("%s is not a valid unicode."):format(code), 2) 142 | end 143 | 144 | --- Represents the utf-16 encoding with little-endian. 145 | System.Text.Encoding "UTF16EncodingLE" { 146 | encode = encodeLE, 147 | decode = decodeLE, 148 | } 149 | 150 | --- Represents the utf-16 encoding with big-endian. 151 | System.Text.Encoding "UTF16EncodingBE" { 152 | encode = encodeBE, 153 | decode = decodeBE, 154 | } 155 | end) 156 | -------------------------------------------------------------------------------- /System/Text/UTF8Encoding.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Text.UTF8Encoding -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2015/06/24 -- 11 | -- Update Date : 2018/03/16 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Text" 17 | 18 | ----------------------------------------------------------------------- 19 | -- prepare -- 20 | ----------------------------------------------------------------------- 21 | export { 22 | REPLACE_CHARACTER = "\0xFFFD", 23 | 24 | strbyte = string.byte, 25 | strchar = string.char, 26 | tinsert = table.insert, 27 | tconcat = table.concat, 28 | floor = math.floor, 29 | LUA_VERSION = tonumber(_G._VERSION:match("[%d%.]+")) or 5.1, 30 | loadsnippet = Toolset.loadsnippet, 31 | error = error, 32 | 33 | band = Toolset.band, 34 | lshift = Toolset.lshift, 35 | rshift = Toolset.rshift, 36 | } 37 | 38 | function decode(str, startp) 39 | startp = startp or 1 40 | 41 | local byte = strbyte(str, startp) 42 | if not byte then return nil end 43 | 44 | if byte < 0x80 then 45 | -- 1-byte 46 | return byte, 1 47 | elseif byte < 0xC2 then 48 | -- Error 49 | return byte + 0xDC00, 1 50 | elseif byte < 0xE0 then 51 | -- 2-byte 52 | local sbyte = strbyte(str, startp + 1) 53 | if not sbyte or band(sbyte, 0xC0) ~= 0x80 then 54 | -- Error 55 | return byte + 0xDC00, 1 56 | end 57 | return lshift(byte, 6) + sbyte - 0x3080, 2 58 | elseif byte < 0xF0 then 59 | -- 3-byte 60 | local sbyte, tbyte = strbyte(str, startp + 1, startp + 2) 61 | if not (sbyte and tbyte) or band(sbyte, 0xC0) ~= 0x80 or (byte == 0xE0 and sbyte < 0xA0) or band(tbyte, 0xC0) ~= 0x80 then 62 | -- Error 63 | return byte + 0xDC00, 1 64 | end 65 | return lshift(byte, 12) + lshift(sbyte, 6) + tbyte - 0xE2080, 3 66 | elseif byte < 0xF5 then 67 | -- 4-byte 68 | local sbyte, tbyte, fbyte = strbyte(str, startp + 1, startp + 3) 69 | if not (sbyte and tbyte and fbyte) or band(sbyte, 0xC0) ~= 0x80 or (byte == 0xF0 and sbyte < 0x90) or (byte == 0xF4 and sbyte >= 0x90) or band(tbyte, 0xC0) ~= 0x80 or band(fbyte, 0xC0) ~= 0x80 then 70 | -- Error 71 | return byte + 0xDC00, 1 72 | end 73 | return lshift(byte, 18) + lshift(sbyte, 12) + lshift(tbyte, 6) + fbyte - 0x3C82080, 4 74 | else 75 | return byte + 0xDC00, 1 76 | end 77 | end 78 | 79 | function encode(code) 80 | if code >= 0 then 81 | -- 1 82 | if code <= 0x7F then return strchar( code ) end 83 | 84 | -- 2 85 | if code <= 0x7FF then 86 | return strchar( 87 | rshift(code, 6) + 0xC0, 88 | band(code, 0x3F) + 0x80 89 | ) 90 | end 91 | 92 | -- 3 93 | if code <= 0xFFFF then 94 | return strchar( 95 | rshift(code, 12) + 0xE0, 96 | band(rshift(code, 6), 0x3F) + 0x80, 97 | band(code, 0x3F) + 0x80 98 | ) 99 | end 100 | 101 | -- 4 102 | if code <= 0x1FFFFF then 103 | return strchar( 104 | rshift(code, 18) + 0xF0, 105 | band(rshift(code, 12), 0x3F) + 0x80, 106 | band(rshift(code, 6), 0x3F) + 0x80, 107 | band(code, 0x3F) + 0x80 108 | ) 109 | end 110 | end 111 | 112 | error(("%s is not a valid code_point."):format(code), 2) 113 | end 114 | 115 | --[[ 116 | 7 U+0000 U+007F 1 0xxxxxxx 117 | 11 U+0080 U+07FF 2 110xxxxx 10xxxxxx 118 | 16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx 119 | 21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 120 | 26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 121 | 31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 122 | ]] 123 | --- Represents the utf-8 encoding. 124 | System.Text.Encoding "UTF8Encoding" { 125 | encode = encode, 126 | decode = decode, 127 | } 128 | end) -------------------------------------------------------------------------------- /System/Threading/TaskScheduler.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Threading.TaskScheduler -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | 8 | --===========================================================================-- 9 | -- Author : kurapica125@outlook.com -- 10 | -- URL : http://github.com/kurapica/PLoop -- 11 | -- Create Date : 2024/07/10 -- 12 | -- Update Date : 2024/07/10 -- 13 | -- Version : 1.0.0 -- 14 | --===========================================================================-- 15 | PLoop(function(_ENV) 16 | namespace "System.Threading" 17 | 18 | --- THe task scheduler system used to process the tasks 19 | __Sealed__() 20 | class "TaskScheduler" (function(_ENV) 21 | 22 | export { 23 | type = type, 24 | resume = coroutine.resume, 25 | 26 | ThreadPool 27 | } 28 | 29 | ----------------------------------------------------------- 30 | -- static property -- 31 | ----------------------------------------------------------- 32 | --- The default task scheduler 33 | __Static__() 34 | property "Default" { type = TaskScheduler, default = function() return TaskScheduler() end } 35 | 36 | ----------------------------------------------------------- 37 | -- method -- 38 | ----------------------------------------------------------- 39 | --- Queue a task(function|thread) to the scheduler 40 | __Abstract__() 41 | function QueueTask(self, task, ...) return self:ExecuteTask(task, ...) end 42 | 43 | --- Try dequeue a task 44 | __Abstract__() 45 | function TryDequeue(self, task) return true end 46 | 47 | __Abstract__() 48 | function ExecuteTask(self, task, ...) 49 | local t = type(task) 50 | 51 | if t == "function" then 52 | return ThreadPool.Current:ThreadCall(task, ...) 53 | elseif t == "thread" then 54 | return resume(task, ...) 55 | end 56 | end 57 | end) 58 | end) -------------------------------------------------------------------------------- /System/ValueWrapper.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System Value Wrapper -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2024/05/22 -- 11 | -- Update Date : 2024/05/22 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- Represents the value wrappers that share the same meta-methods so they can be used together 17 | __Sealed__() 18 | interface "System.IValueWrapper" (function(_ENV) 19 | 20 | export { 21 | isobjecttype = Class.IsObjectType, 22 | getvalue = function(self) 23 | if isobjecttype(self, IValueWrapper) then 24 | return self.Value 25 | else 26 | return self 27 | end 28 | end, 29 | 30 | IValueWrapper 31 | } 32 | 33 | ----------------------------------------------------------------------- 34 | -- property -- 35 | ----------------------------------------------------------------------- 36 | --- The wrapped value 37 | __Abstract__() 38 | property "Value" { type = Any } 39 | 40 | ----------------------------------------------------------------------- 41 | -- meta-method -- 42 | ----------------------------------------------------------------------- 43 | function __tostring(self) return tostring(self.Value) end 44 | 45 | -- the addition operation 46 | function __add(a, b) return getvalue(a) + getvalue(b) end 47 | 48 | -- the subtraction operation 49 | function __sub(a, b) return getvalue(a) - getvalue(b) end 50 | 51 | -- the multiplication operation 52 | function __mul(a, b) return getvalue(a) * getvalue(b) end 53 | 54 | -- the division operation 55 | function __div(a, b) return getvalue(a) / getvalue(b) end 56 | 57 | -- the modulo operation 58 | function __mod(a, b) return getvalue(a) % getvalue(b) end 59 | 60 | -- the exponentiation operation 61 | function __pow(a, b) return getvalue(a) ^ getvalue(b) end 62 | 63 | -- the negation operation 64 | function __unm(a) return -getvalue(a) end 65 | 66 | -- the concatenation operation 67 | function __concat(a, b) return getvalue(a) .. getvalue(b) end 68 | 69 | -- the length operation, those won't works in 5.1 70 | function __len(a) return #getvalue(a) end 71 | 72 | -- the equal operation 73 | function __eq(a, b) return getvalue(a) == getvalue(b) end 74 | 75 | -- the less than operation 76 | function __lt(a, b) return getvalue(a) < getvalue(b) end 77 | 78 | -- the less equal operation 79 | function __le(a, b) return getvalue(a) <= getvalue(b) end 80 | 81 | if _G._VERSION and tonumber(_G._VERSION:match("[%d%.]+$")) * 10 >= 53 then 82 | Toolset.loadsnippet([[ 83 | -- the floor division operation 84 | function __idiv(a, b) return getvalue(a) // getvalue(b) end 85 | 86 | -- the bitwise AND operation 87 | function __band(a, b) return getvalue(a) & getvalue(b) end 88 | 89 | -- the bitwise OR operation 90 | function __bor(a, b) return getvalue(a) | getvalue(b) end 91 | 92 | -- the bitwise exclusive OR operation 93 | function __bxor(a, b) return getvalue(a) ~ getvalue(b) end 94 | 95 | -- the bitwise NOToperation 96 | function __bnot(a) return ~getvalue(a) end 97 | 98 | -- the bitwise left shift operation 99 | function __shl(a, b) return getvalue(a) << getvalue(b) end 100 | 101 | -- the bitwise right shift operation 102 | function __shr(a, b) return getvalue(a) >> getvalue(b) end 103 | ]], "IValueWrapper_Patch_53", _ENV)() 104 | end 105 | end) 106 | end) -------------------------------------------------------------------------------- /System/Web/Context/HttpCookie.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.HttpCookie -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/03/11 -- 11 | -- Update Date : 2019/07/25 -- 12 | -- Version : 1.1.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | export { 17 | tostring = tostring, 18 | rawset = rawset, 19 | } 20 | 21 | --- the http cookie 22 | __Final__() __Sealed__() 23 | class "System.Web.HttpCookie" (function (_ENV) 24 | export { 25 | tinsert = table.insert, 26 | tblconcat = table.concat, 27 | type = type, 28 | pairs = pairs, 29 | } 30 | 31 | __Sealed__() enum "SameSiteValue" { 32 | Strict = "Strict", 33 | Lax = "Lax", 34 | } 35 | 36 | ----------------------------------------------------------------------- 37 | -- property -- 38 | ----------------------------------------------------------------------- 39 | --- Gets or sets the domain to associate the cookie with 40 | property "Domain" { type = String } 41 | 42 | --- Gets or sets the expiration date and time for the cookie 43 | __Set__(PropertySet.Clone) 44 | property "Expires" { type = Date } 45 | 46 | --- Gets or sets the max age for the cookie 47 | property "MaxAge" { type = Number } 48 | 49 | --- Gets a value indicating whether a cookie has subkeys 50 | property "HasKeys" { type = Boolean } 51 | 52 | --- Gets or sets a value that specifies whether a cookie is accessible by client-side script 53 | property "HttpOnly" { type = Boolean } 54 | 55 | --- Gets or sets whether the cookie should be restricted to a first-party or same-site context 56 | property "SameSite" { type = SameSiteValue } 57 | 58 | --- Gets or sets the name of a cookie 59 | property "Name" { type = String } 60 | 61 | --- Gets or sets the virtual path to transmit with the current cookie 62 | property "Path" { type = String, default = "/" } 63 | 64 | --- Gets or sets a value indicating whether to transmit the cookie using Secure Sockets Layer (SSL)--that is, over HTTPS only 65 | property "Secure" { type = Boolean } 66 | 67 | --- Gets or sets an individual cookie value 68 | property "Value" { type = String } 69 | 70 | --- Gets a collection of key/value pairs that are contained within a single cookie object 71 | property "Values" { type = Table, default = function(self) self.HasKeys = true return {} end } 72 | 73 | ----------------------------------------------------------------------- 74 | -- method -- 75 | ----------------------------------------------------------------------- 76 | --- Convert the cookie to string 77 | function ToString(self) 78 | if self.Value == nil then return "" end 79 | 80 | local cache = {} 81 | 82 | tinsert(cache, self.Name .. "=" .. self.Value) 83 | if self.HasKeys then 84 | for k, v in pairs(self.Values) do 85 | if type(k) == "string" and type(v) == "string" then 86 | tinsert(cache, k .. "=" .. v) 87 | end 88 | end 89 | end 90 | if self.Expires then tinsert(cache, "Expires=" .. self.Expires:ToUTCString("%a, %d-%b-%y %X GMT")) end 91 | if self.MaxAge then tinsert(cache, "Max-Age=" .. self.MaxAge) end 92 | if self.Domain then tinsert(cache, "Domain=" .. self.Domain) end 93 | if self.Path then tinsert(cache, "Path=" .. self.Path) end 94 | if self.SameSite then tinsert(cache, "SameSite=".. self.SameSite) end 95 | if self.Secure then tinsert(cache, "Secure") end 96 | if self.HttpOnly then tinsert(cache, "HttpOnly") end 97 | 98 | return tblconcat(cache, ";") 99 | end 100 | 101 | ----------------------------------------------------------------------- 102 | -- constructor -- 103 | ----------------------------------------------------------------------- 104 | __Arguments__{ String } 105 | function HttpCookie(self, name) self.Name = name end 106 | 107 | __Arguments__{ String, String } 108 | function HttpCookie(self, name, value) self.Name, self.Value = name, value end 109 | 110 | __Arguments__{ String, String, Number } 111 | function HttpCookie(self, name, value, maxAge) self.Name, self.Value, self.MaxAge = name, value, maxAge end 112 | 113 | __Arguments__{ String, String, Date } 114 | function HttpCookie(self, name, value, expires) self.Name, self.Value, self.Expires = name, value, expires end 115 | 116 | ----------------------------------------------------------------------- 117 | -- meta-method -- 118 | ----------------------------------------------------------------------- 119 | __tostring = ToString 120 | end) 121 | 122 | --- the http cookies 123 | __Final__() __Sealed__() 124 | class "System.Web.HttpCookies" { 125 | ----------------------------------------------------------------------- 126 | -- meta-method -- 127 | ----------------------------------------------------------------------- 128 | __index = function (self, key) 129 | key = tostring(key) 130 | if key then 131 | local cookie = HttpCookie(key) 132 | rawset(self, key, cookie) 133 | return cookie 134 | end 135 | end 136 | } 137 | end) -------------------------------------------------------------------------------- /System/Web/Context/HttpRequest.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.HttpRequest -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2015/05/26 -- 11 | -- Update Date : 2020/02/19 -- 12 | -- Version : 1.1.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- the http request prototype 17 | __Sealed__() 18 | class "System.Web.HttpRequest" (function (_ENV) 19 | local function defcache() return {} end 20 | 21 | --- Get the collection of the http headers 22 | __Abstract__() property "Headers" { default = defcache } 23 | 24 | --- Specifies the length, in bytes, of content sent by the client 25 | __Abstract__() property "ContentLength" { default = function(self) return self.Headers["content-length"] end } 26 | 27 | --- Gets the MIME content type of the incoming request 28 | __Abstract__() property "ContentType" { default = function(self) return self.Headers["content-type"] end } 29 | 30 | --- Gets a collection of cookies sent by the client 31 | __Abstract__() property "Cookies" { default = defcache } 32 | 33 | --- Gets a collection of form variables 34 | __Abstract__() property "Form" { default = defcache } 35 | 36 | --- Gets the HTTP data transfer method (such as GET, POST, or HEAD) used by the client 37 | __Abstract__() property "HttpMethod" { } 38 | 39 | --- Gets a value indicating whether the HTTP connection uses secure sockets (that is, HTTPS) 40 | __Abstract__() property "IsSecureConnection" { } 41 | 42 | --- Gets the collection of HTTP query string variables 43 | __Abstract__() property "QueryString" { default = defcache } 44 | 45 | --- Gets the raw URL of the current request 46 | __Abstract__() property "RawUrl" { } 47 | 48 | --- Get the root path of the query document 49 | __Abstract__() property "Root" { } 50 | 51 | --- Gets information about the URL of the current request 52 | __Abstract__() property "Url" { default = function(self) return self.Context.Application:Url2Path(self.RawUrl:match("^[^?#]+")) end } 53 | 54 | --- The accept mime type of the query 55 | __Abstract__() property "Accept" { default = function(self) return self.Headers["accept"] end } 56 | 57 | --- The http context 58 | __Final__() property "Context" { type = System.Web.HttpContext } 59 | 60 | --- Whether the request is handled 61 | __Final__() property "Handled" { Type = Boolean, default = false } 62 | 63 | ----------------------------------------------------------- 64 | -- method -- 65 | ----------------------------------------------------------- 66 | --- Whether the request accept html as result 67 | function IsHtmlAccepted(self) 68 | local accept = self.Accept 69 | return accept and (accept:match("text/html") or accept:match("%*/%*")) and true or false 70 | end 71 | 72 | --- Whether the request accept text as result 73 | function IsTextAccepted(self) 74 | local accept = self.Accept 75 | return accept and (accept:match("text/plain") or accept:match("%*/%*")) and true or false 76 | end 77 | 78 | --- Whether the request accept json as result 79 | function IsJsonAccepted(self) 80 | local accept = self.Accept 81 | return accept and (accept:match("application/json") or accept:match("%*/%*")) and true or false 82 | end 83 | 84 | --- Whether the request accept javascript as result 85 | function IsScriptAccepted(self) 86 | local accept = self.Accept 87 | return accept and ((accept:match("text/[%w-]+script") or accept:match("%*/%*")) or accept:match("application/[%w%-]+script")) and true or false 88 | end 89 | 90 | ----------------------------------------------------------- 91 | -- constructor -- 92 | ----------------------------------------------------------- 93 | __Arguments__{ System.Web.HttpContext } 94 | function __ctor(self, context) 95 | self.Context = context 96 | end 97 | end) 98 | end) -------------------------------------------------------------------------------- /System/Web/Context/HttpResponse.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.HttpResponse -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2015/05/26 -- 11 | -- Update Date : 2020/03/20 -- 12 | -- Version : 1.1.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- the http response prototype 17 | __Sealed__() 18 | class "System.Web.HttpResponse" (function (_ENV) 19 | export { 20 | Context, HttpCookies, 21 | ispathrooted = IO.Path.IsPathRooted, 22 | combinepath = IO.Path.CombinePath, 23 | getdirectory = IO.Path.GetDirectory, 24 | UrlEncode = UrlEncode, 25 | } 26 | 27 | --- The http context 28 | __Final__() property "Context" { type = System.Web.HttpContext } 29 | 30 | --- Set the http response header 31 | __Indexer__ () 32 | __Abstract__() property "Header" { set = function(self, key, value) end } 33 | 34 | --- Gets or sets the HTTP MIME type of the output stream. 35 | __Abstract__() property "ContentType" { handler = function(self, value) self.Header["Content-Type"] = value end } 36 | 37 | --- Gets or sets the value of the Http Location header. 38 | __Abstract__() property "RedirectLocation" { handler = function(self, value) self.Header.Location = value end } 39 | 40 | --- Whether the request is been redirected. 41 | __Abstract__() property "RequestRedirected" { type = Boolean } 42 | 43 | --- Gets or sets the response write function or callable writer. 44 | __Abstract__() property "Write" { type = Callable } 45 | 46 | --- Gets or sets the HTTP status code of the output returned to the client. 47 | __Abstract__() property "StatusCode" { type = HTTP_STATUS } 48 | 49 | --- Gets a collection of cookies sent to the client. 50 | __Abstract__() property "Cookies" { set = false, default = function(self) return HttpCookies() end } 51 | 52 | --- Sends all currently buffered output to the client. 53 | __Abstract__() function Flush(self) end 54 | 55 | --- Send the response headers 56 | __Abstract__() function SendHeaders(self) end 57 | 58 | --- Finish the response, used to close resources such like output wirter 59 | __Abstract__() function Close(self) end 60 | 61 | --- Redirects the client to a new URL. 62 | __Arguments__{ String, HTTP_STATUS/HTTP_STATUS.REDIRECT, Boolean/false } 63 | function Redirect(self, url, code, raw) 64 | if not url:match("^%s*%a+:") then 65 | -- Check the url 66 | if not ispathrooted(url) then 67 | url = combinepath(getdirectory(self.Context.Request.Url), url) 68 | end 69 | 70 | if not raw then 71 | url = self.Context.Application:Path2Url(url) 72 | end 73 | end 74 | 75 | self.StatusCode = code 76 | self.RedirectLocation = url 77 | self.RequestRedirected = true 78 | end 79 | 80 | ----------------------------------------------------------- 81 | -- constructor -- 82 | ----------------------------------------------------------- 83 | __Arguments__{ System.Web.HttpContext } 84 | function __ctor(self, context) 85 | self.Context = context 86 | end 87 | end) 88 | end) 89 | -------------------------------------------------------------------------------- /System/Web/ContextHandler/GuidSessionIDManager.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.GuidSessionIDManager -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/03/15 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- the default sesion id manager(auto guid generator) 17 | __Sealed__() class "System.Web.GuidSessionIDManager" (function (_ENV) 18 | extend "ISessionIDManager" 19 | 20 | export { validate = Struct.ValidateValue, HttpCookie.SameSiteValue } 21 | export { Guid, Date } 22 | 23 | ----------------------------------------------------------------------- 24 | -- property -- 25 | ----------------------------------------------------------------------- 26 | --- The cookie name that to be saved into cookies 27 | property "CookieName" { Type = String, Default = "_SessionID" } 28 | 29 | ----------------------------------------------------------------------- 30 | -- inherit method -- 31 | ----------------------------------------------------------------------- 32 | function GetSessionID(self, context) 33 | return context.Request.Cookies[self.CookieName] 34 | end 35 | 36 | function CreateSessionID(self, context) 37 | return Guid.New() 38 | end 39 | 40 | function RemoveSessionID(self, context) 41 | local cookie = context.Response.Cookies[self.CookieName] 42 | if not cookie.Value then cookie.Value = "none" end 43 | cookie.Expires = Date.Now:AddMinutes(-1) 44 | end 45 | 46 | function SaveSessionID(self, context, session) 47 | local cookie = context.Response.Cookies[self.CookieName] 48 | cookie.Value = session.SessionID 49 | cookie.HttpOnly = true 50 | cookie.SameSite = SameSiteValue.Lax 51 | cookie.Expires = not session.IsTemporary and (session.Timeout or Date.Now:AddMinutes(self.TimeoutMinutes)) or nil 52 | end 53 | 54 | function ValidateSessionID(self, id) 55 | return validate(Guid, id) and true or false 56 | end 57 | end) 58 | end) -------------------------------------------------------------------------------- /System/Web/Interface/IContextOutputHandler.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.IContextOutputHandler -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/04/08 -- 11 | -- Update Date : 2018/03/15 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- Represents the interface of context output handler 17 | __Sealed__() interface "System.Web.IContextOutputHandler" (function (_ENV) 18 | extend "IHttpOutput" "IHttpContextHandler" 19 | 20 | export { 21 | HeadPhase = IHttpContextHandler.ProcessPhase.Head, 22 | } 23 | 24 | ----------------------------------------------------------------------- 25 | -- property -- 26 | ----------------------------------------------------------------------- 27 | property "ContentType" { Type = String, Default = "text/html" } 28 | 29 | -- Override Method 30 | function Process(self, context, phase) 31 | if phase == HeadPhase then 32 | context.Response.ContentType = self.ContentType 33 | self.Context = context 34 | return self:OnLoad(context) 35 | else 36 | return self:SafeRender(context.Response.Write, "") 37 | end 38 | end 39 | end) 40 | end) -------------------------------------------------------------------------------- /System/Web/Interface/IHttpOutput.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.IHttpOutput -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/02/19 -- 11 | -- Update Date : 2018/03/15 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- Will be defined later 17 | interface "System.Web.IOutputLoader" {} 18 | 19 | __Sealed__() 20 | __NoNilValue__(false):AsInheritable() 21 | __NoRawSet__ (false):AsInheritable() 22 | interface "System.Web.IHttpOutput" (function (_ENV) 23 | extend (System.Web.IHttpContext) 24 | 25 | export { 26 | pcall = pcall, 27 | getmetatable = getmetatable, 28 | isclass = Class.Validate, 29 | issubtype = Class.IsSubType, 30 | GetRelativeResource = GetRelativeResource, 31 | 32 | IHttpOutput, IOutputLoader 33 | } 34 | 35 | ----------------------------------------------------------------------- 36 | -- method -- 37 | ----------------------------------------------------------------------- 38 | --- Init the for context's request, generate the head for response 39 | -- @param context the http context 40 | function OnLoad(self, context) end 41 | 42 | --- Safe render the body and convert the error to the real line 43 | -- @param write the response write 44 | -- @param indent the text indent 45 | function SafeRender(self, ...) 46 | local ok, err = pcall(self.Render, self, ...) 47 | if not ok then IOutputLoader.RaiseError(err) end 48 | end 49 | 50 | --- Render body to response, auto-generated from the file 51 | -- @param write the response write 52 | -- @param indent the text indent 53 | __Abstract__() function Render(self, write, indent) end 54 | 55 | --- Using other output object to generate contents to the response 56 | -- @param url the target resource's path 57 | -- @param write the response write 58 | -- @param indent the text indent 59 | -- @param default the default text if the target resource not existed 60 | -- @pram ... the arguments to generate the target object 61 | function RenderAnother(self, url, write, indent, default, ...) 62 | local cls = GetRelativeResource(self, url, self.Context) 63 | if cls then 64 | if isclass(cls) and issubtype(cls, IHttpOutput) then 65 | local page = cls(...) 66 | 67 | local context = self.Context 68 | page.Context = context 69 | page:OnLoad(context) 70 | return page:SafeRender(write, indent or "") 71 | end 72 | end 73 | 74 | -- Default 75 | write(indent) 76 | write(default) 77 | end 78 | end) 79 | end) -------------------------------------------------------------------------------- /System/Web/Interface/IRenderEngine.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.IRenderEngine -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/04/10 -- 11 | -- Update Date : 2018/03/15 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | namespace "System.Web" 17 | 18 | --- Represents the content type of the render 19 | __Sealed__() __AutoIndex__() 20 | enum "RenderContentType" { 21 | "RecordLine", 22 | "StaticText", 23 | "NewLine", 24 | "LuaCode", 25 | "Expression", 26 | "EncodeExpression", 27 | "MixMethodStart", 28 | "MixMethodEnd", 29 | "CallMixMethod", 30 | "RenderOther", 31 | "InnerRequest", 32 | } 33 | 34 | --- Represents the interface of render engine 35 | __Sealed__() __AnonymousClass__() 36 | interface "IRenderEngine"(function (_ENV) 37 | extend "Iterable" 38 | 39 | export { 40 | yield = coroutine.yield, 41 | RCT_RecordLine = RenderContentType.RecordLine, 42 | RCT_StaticText = RenderContentType.StaticText, 43 | RCT_NewLine = RenderContentType.NewLine, 44 | RCT_MixMethodStart = RenderContentType.MixMethodStart, 45 | RCT_MixMethodEnd = RenderContentType.MixMethodEnd, 46 | } 47 | 48 | ----------------------------------------------------------------------- 49 | -- method -- 50 | ----------------------------------------------------------------------- 51 | __Iterator__() 52 | function GetIterator(self, reader) return self:ParseLines(reader) end 53 | 54 | --- Init the engine with page loader and the page config. 55 | __Abstract__() function Init(self, loader, config) end 56 | 57 | --- Parse the lines and yield all content with type 58 | __Abstract__() function ParseLines(self, reader) 59 | -- Use yield not return to send back content and other informations 60 | yield(RCT_MixMethodStart, "Render") 61 | for line in reader:ReadLines() do 62 | line = line:gsub("%s+$", "") 63 | yield(RCT_RecordLine, line) 64 | yield(RCT_StaticText, line) 65 | yield(RCT_NewLine) 66 | end 67 | yield(RCT_MixMethodEnd) 68 | end 69 | end) 70 | end) -------------------------------------------------------------------------------- /System/Web/MVC/ViewPageLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.ViewPageLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/02/23 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | IO.Resource.__ResourceLoader__"view" 17 | __Sealed__() __PageRender__("ViewPage", PageLoader) 18 | class "System.Web.ViewPageLoader" { PageLoader } 19 | end) -------------------------------------------------------------------------------- /System/Web/Resource/CssLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.CssLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/04/08 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- The css file loader 17 | IO.Resource.__ResourceLoader__"css" 18 | __Sealed__() __PageRender__("CssFile", StaticFileLoader, { comment = "/* %s */" }) 19 | class "System.Web.CssLoader" { StaticFileLoader } 20 | end) -------------------------------------------------------------------------------- /System/Web/Resource/EmbedPageLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.EmbedPageLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/02/23 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | IO.Resource.__ResourceLoader__"embed" 17 | __Sealed__() __PageRender__("EmbedPage", PageLoader) 18 | class "System.Web.EmbedPageLoader" { PageLoader } 19 | end) -------------------------------------------------------------------------------- /System/Web/Resource/HelperPageLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.HelperPageLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/02/23 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | IO.Resource.__ResourceLoader__"helper" 17 | __Sealed__() __PageRender__("PageHelper", PageLoader, { asinterface = true }) 18 | class "System.Web.HelperPageLoader" { PageLoader } 19 | end) 20 | -------------------------------------------------------------------------------- /System/Web/Resource/JavaScriptLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.CssLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/04/08 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- The javascript file loader 17 | IO.Resource.__ResourceLoader__"js" 18 | __Sealed__() __PageRender__("JavascriptFile", StaticFileLoader, { comment = "/* %s */" }) 19 | class "System.Web.JavaScriptLoader" { StaticFileLoader } 20 | end) 21 | -------------------------------------------------------------------------------- /System/Web/Resource/LuaServerPageLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.HelperPageLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/02/23 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | IO.Resource.__ResourceLoader__"lsp" __SuperObject__(false) 17 | __Sealed__() __PageRender__("LuaServerPage", PageLoader) 18 | class "System.Web.LuaServerPageLoader" (function (_ENV) 19 | inherit "PageLoader" 20 | 21 | export { PageLoader, IOutputLoader, IContextOutputHandler } 22 | 23 | -- Method 24 | function Load(self, path) 25 | local target = IOutputLoader.Load(self, path) 26 | 27 | if target then class(target, { IContextOutputHandler }) end 28 | 29 | return target 30 | end 31 | end) 32 | end) -------------------------------------------------------------------------------- /System/Web/Resource/MasterPageLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.MasterPageLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/02/23 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | IO.Resource.__ResourceLoader__"master" 17 | __Sealed__() __PageRender__("MasterPage", PageLoader) 18 | class "System.Web.MasterPageLoader" { PageLoader } 19 | end) -------------------------------------------------------------------------------- /System/Web/Resource/PageLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.PageLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2015/05/10 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | __Sealed__() __PageRender__("HtmlPage", IOutputLoader, { engine = PageRenderEngine, comment = "" }) 17 | class "System.Web.PageLoader" { IOutputLoader } 18 | end) -------------------------------------------------------------------------------- /System/Web/Resource/StaticFileLoader.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- System.Web.StaticFileLoader -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2016/04/02 -- 11 | -- Update Date : 2018/04/02 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | PLoop(function(_ENV) 16 | --- The static file loader 17 | __Sealed__() __PageRender__("StaticFile", IOutputLoader) 18 | class "System.Web.StaticFileLoader" { IOutputLoader } 19 | end) -------------------------------------------------------------------------------- /System/Web/init.lua: -------------------------------------------------------------------------------- 1 | -- The init file of PLoop.System.Web 2 | 3 | require "PLoop" 4 | require "PLoop.System.Context" 5 | require "PLoop.System.IO" 6 | require "PLoop.System.Data" 7 | 8 | -- Core 9 | require "PLoop.System.Web.Core" 10 | 11 | -- Context 12 | require "PLoop.System.Web.Context.HttpCookie" 13 | require "PLoop.System.Web.Context.HttpRequest" 14 | require "PLoop.System.Web.Context.HttpResponse" 15 | 16 | -- Interface 17 | require "PLoop.System.Web.Interface.IHttpContextHandler" 18 | require "PLoop.System.Web.Interface.IHttpOutput" 19 | require "PLoop.System.Web.Interface.IContextOutputHandler" 20 | require "PLoop.System.Web.Interface.IRenderEngine" 21 | require "PLoop.System.Web.Interface.IOutputLoader" 22 | 23 | -- Http Context 24 | require "PLoop.System.Web.Context.HttpSession" 25 | require "PLoop.System.Web.Context.HttpContext" 26 | 27 | -- Resource 28 | require "PLoop.System.Web.Resource.StaticFileLoader" 29 | require "PLoop.System.Web.Resource.JavaScriptLoader" 30 | require "PLoop.System.Web.Resource.CssLoader" 31 | 32 | require "PLoop.System.Web.Resource.PageRenderEngine" 33 | require "PLoop.System.Web.Resource.PageLoader" 34 | require "PLoop.System.Web.Resource.MasterPageLoader" 35 | require "PLoop.System.Web.Resource.EmbedPageLoader" 36 | require "PLoop.System.Web.Resource.HelperPageLoader" 37 | require "PLoop.System.Web.Resource.LuaServerPageLoader" 38 | 39 | -- Context Worker 40 | require "PLoop.System.Web.ContextHandler.Route" 41 | 42 | -- SessionIDManager 43 | require "PLoop.System.Web.ContextHandler.GuidSessionIDManager" 44 | 45 | -- MVC 46 | require "PLoop.System.Web.MVC.Controller" 47 | require "PLoop.System.Web.MVC.ViewPageLoader" 48 | 49 | -- Web Attribute 50 | require "PLoop.System.Web.Attribute.ContextHandler" 51 | require "PLoop.System.Web.Attribute.Validator" -------------------------------------------------------------------------------- /System/Xml/init.lua: -------------------------------------------------------------------------------- 1 | -- The init file of PLoop.System.Xml 2 | 3 | require "PLoop" 4 | 5 | -- Core 6 | require "PLoop.System.Xml.Core" 7 | -------------------------------------------------------------------------------- /UnitTest/Run.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- UnitTest For PLoop -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2018/09/26 -- 11 | -- Update Date : 2018/09/26 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | PLOOP_PLATFORM_SETTINGS = { 15 | OBJECT_NO_RAWSEST = false, OBJECT_NO_NIL_ACCESS = false, 16 | CORE_LOG_LEVEL = 3, 17 | MULTI_OS_THREAD = false, 18 | TYPE_VALIDATION_DISABLED = false, 19 | ENV_ALLOW_GLOBAL_VAR_BE_NIL = true, 20 | } 21 | 22 | require "PLoop"(function(_ENV) 23 | require "PLoop.System.IO" 24 | require "PLoop.System.UnitTest" 25 | 26 | export { 27 | pcall = pcall, 28 | loadfile = loadfile, 29 | Info = Logger.Default[Logger.LogLevel.Info], 30 | Warn = Logger.Default[Logger.LogLevel.Warn], 31 | Error = Logger.Default[Logger.LogLevel.Error], 32 | 33 | System.IO.Path, System.IO.Directory, UnitTest 34 | } 35 | 36 | Logger.Default:AddHandler(print) 37 | 38 | local root = Path.CombinePath(Path.GetCurrentPath(), "Tests") 39 | 40 | for name in Directory.GetFiles(root) do 41 | local func, msg = loadfile(Path.CombinePath(root, name)) 42 | if not func then 43 | Error("[UnitTest]Failed to load test for %s - %s", name, msg) 44 | end 45 | local ok, msg = pcall(func) 46 | if not ok then 47 | Error("[UnitTest]Failed to load test for %s - %s", name, msg) 48 | end 49 | end 50 | 51 | UnitTest("PLoop"):Run() 52 | end) 53 | -------------------------------------------------------------------------------- /UnitTest/Tests/attribute.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- UnitTest For Attribute -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2019/02/08 -- 11 | -- Update Date : 2019/02/08 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | _ENV = UnitTest "PLoop.Attribute" "1.0.0" 16 | 17 | __Test__() function install() 18 | local module = prototype { 19 | __newindex = function(self, key, value) 20 | if type(value) == "function" and Attribute.HaveRegisteredAttributes() then 21 | Attribute.SaveAttributes(value, AttributeTargets.Function) 22 | 23 | -- Init the definition of the target, the definition is the function itself 24 | local newdef = Attribute.InitDefinition(value, AttributeTargets.Function, value, self, key) 25 | 26 | if newdef ~= value then 27 | Attribute.ToggleTarget(value, newdef) 28 | value = newdef 29 | end 30 | 31 | -- Apply the definition, for function just save it 32 | rawset(self, key, value) 33 | 34 | -- keep the manager nil, normally it only used by the class, interface and etc 35 | Attribute.ApplyAttributes(value, AttributeTargets.Function, nil, self, key) 36 | 37 | -- finish the call of the attribute system 38 | Attribute.AttachAttributes(value, AttributeTargets.Function, self, key) 39 | end 40 | end, 41 | } 42 | 43 | local obj = prototype.NewObject(module) 44 | 45 | -- We can use the attribute to run the function as an iterator 46 | __Iterator__() 47 | function obj:GetIter(n) 48 | for i = 1, n do coroutine.yield(i) end 49 | end 50 | 51 | -- All works fine now 52 | for i in obj:GetIter(10) do Assert.Step(i) end 53 | 54 | Assert.Same(List(10), List(Assert.GetSteps())) 55 | end 56 | 57 | __Test__() function initattr() 58 | local __SafeCall__ = class (function(_ENV) 59 | extend "IInitAttribute" 60 | 61 | local function checkret(ok, ...) 62 | if ok then return ... end 63 | end 64 | 65 | function InitDefinition(self, target, targettype, definition, owner, name, stack) 66 | return function(...) 67 | return checkret(pcall(definition, ...)) 68 | end 69 | end 70 | 71 | property "AttributeTarget" { default = AttributeTargets.Function + AttributeTargets.Method } 72 | end) 73 | 74 | __SafeCall__() 75 | function test1() 76 | return 1, 2, 3 77 | end 78 | 79 | __SafeCall__() 80 | function test2(i, j) 81 | return i/j 82 | end 83 | 84 | Assert.Nil(test2()) 85 | Assert.Same({ 1, 2, 3 }, { test1() }) 86 | end 87 | 88 | __Test__() function applyattr() 89 | local __Name__ = class (function(_ENV) 90 | extend "IApplyAttribute" 91 | 92 | function ApplyAttribute(self, target, targettype, manager, owner, name, stack) 93 | if manager then 94 | Environment.Apply(manager, function(_ENV) 95 | property "Name" { type = String } 96 | end) 97 | end 98 | end 99 | 100 | property "AttributeTarget" { default = AttributeTargets.Interface + AttributeTargets.Class } 101 | end) 102 | 103 | __Name__() 104 | local A = class {} 105 | 106 | Assert.Find("attribute.lua:109: the Name must be string, got number", 107 | Assert.Error( 108 | function() 109 | A().Name = 123 110 | end 111 | ) 112 | ) 113 | end 114 | 115 | __Test__() function attachattr() 116 | local __DataTable__ = class (function(_ENV) 117 | extend "IAttachAttribute" 118 | 119 | function AttachAttribute(self, target, targettype, owner, name, stack) 120 | return self.DataTable 121 | end 122 | 123 | property "AttributeTarget" { default = AttributeTargets.Class } 124 | 125 | property "DataTable" { type = String } 126 | end) 127 | 128 | __DataTable__{ DataTable = "Persons" } 129 | local Person = class {} 130 | 131 | Assert.Equal("Persons", Attribute.GetAttachedData(__DataTable__, Person)) 132 | end -------------------------------------------------------------------------------- /UnitTest/Tests/enum.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- UnitTest For Enum -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2018/09/26 -- 11 | -- Update Date : 2018/09/26 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | _ENV = UnitTest "PLoop.Enum" "1.0.0" 16 | 17 | namespace "UnitTest.EnumCase" 18 | 19 | __Test__() function enumeration() 20 | enum "Direction" { North = 1, East = 2, South = 3, West = 4 } 21 | 22 | Assert.Equal(1, North) 23 | Assert.Equal(1, Direction.North) 24 | Assert.Nil(Direction.north) 25 | Assert.Equal("North", Direction(1)) 26 | Assert.Nil(Direction(5)) 27 | end 28 | 29 | __Test__() function autoindex() 30 | 31 | __AutoIndex__{ North = 1, South = 5 } 32 | enum "Direction" { 33 | "North", 34 | "East", 35 | "South", 36 | "West", 37 | } 38 | 39 | Assert.Equal(1, Direction.North) 40 | Assert.Equal(6, Direction.West) 41 | Assert.Equal("South", Direction(5)) 42 | end 43 | 44 | __Test__() function flagsenum() 45 | __Flags__() 46 | enum "WeekDay" { 47 | All = 0, 48 | "SUNDAY", 49 | "MONDAY", 50 | "TUESDAY", 51 | "WEDNESDAY", 52 | "THURSDAY", 53 | "FRIDAY", 54 | "SATURDAY", 55 | } 56 | 57 | Assert.Equal(0, WeekDay.All) 58 | Assert.Equal(1, SUNDAY) 59 | Assert.Equal(2^4, THURSDAY) 60 | 61 | local day = SUNDAY + MONDAY + FRIDAY 62 | 63 | Assert.True(System.Enum.ValidateFlags(SUNDAY, day)) 64 | Assert.False(System.Enum.ValidateFlags(SATURDAY, day)) 65 | Assert.Same(Dictionary{ SUNDAY = SUNDAY, MONDAY = MONDAY, FRIDAY = FRIDAY }, Dictionary(WeekDay(day))) 66 | end -------------------------------------------------------------------------------- /UnitTest/Tests/environment.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- UnitTest For Environment -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2018/09/26 -- 11 | -- Update Date : 2018/09/26 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | _ENV = UnitTest "PLoop.Environment" "1.0.0" 16 | 17 | __Test__() function isolate() 18 | Test = 233 19 | PLoop(function(_ENV) 20 | Test = 123 21 | end) 22 | 23 | Assert.Equal(233, Test) 24 | end 25 | 26 | __Test__() function globalaccess() 27 | Assert.Equal(_G.print, print) 28 | Assert.NotNil(List) 29 | Assert.Equal(List, System.Collections.List) 30 | if not Platform.MULTI_OS_THREAD then 31 | Assert.NotNil(rawget(_M, "List")) 32 | end 33 | end 34 | 35 | __Test__() function exportvalue() 36 | export "print" 37 | export { "math", "pairs", List } 38 | export { ipairs = ipairs } 39 | export { 40 | "select", 41 | abs = math.abs, 42 | Dictionary, 43 | } 44 | 45 | Assert.Equal(_G.select, rawget(_ENV, "select")) 46 | end 47 | 48 | __Test__() function importns() 49 | Assert.Nil(StringFormatProvider) 50 | 51 | import "System.Serialization" 52 | 53 | Assert.NotNil(StringFormatProvider) 54 | end -------------------------------------------------------------------------------- /UnitTest/Tests/namespace.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- UnitTest For Namespace -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2018/09/26 -- 11 | -- Update Date : 2018/09/26 -- 12 | -- Version : 1.0.0 -- 13 | --===========================================================================-- 14 | 15 | _ENV = UnitTest "PLoop.Namespace" "1.0.0" 16 | 17 | __Test__() function usage() 18 | PLoop(function(_ENV) 19 | namespace "UnitTest.NamespaceCase" 20 | 21 | class "A" (function(_ENV) 22 | class "C" {} 23 | end) 24 | 25 | local B = class {} 26 | 27 | Assert.Equal("UnitTest.NamespaceCase.A", tostring(A)) 28 | Assert.Equal("Anonymous", tostring(B)) 29 | Assert.Equal("UnitTest.NamespaceCase.A.C", tostring(A.C)) 30 | end) 31 | 32 | PLoop(function(_ENV) 33 | Assert.Nil(A) 34 | 35 | import "UnitTest.NamespaceCase" 36 | 37 | Assert.NotNil(A) 38 | Assert.Nil(B) 39 | Assert.NotNil(A.C) 40 | end) 41 | 42 | PLoop.UnitTest.NamespaceCase(function(_ENV) 43 | class "D" {} 44 | 45 | Assert.Equal("UnitTest.NamespaceCase.D", tostring(D)) 46 | end) 47 | end -------------------------------------------------------------------------------- /UnitTest/Tests/prototype.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- UnitTest For Prototype -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2018/09/26 -- 11 | -- Update Date : 2019/02/08 -- 12 | -- Version : 1.1.0 -- 13 | --===========================================================================-- 14 | 15 | _ENV = UnitTest "PLoop.Prototype" "1.1.0" 16 | 17 | __Test__() function creation() 18 | local proxy = prototype { 19 | __index = function(self, key) return rawget(self, "__" .. key) end, 20 | __newindex = function(self, key, value) rawset(self, "__" .. key, value) end, 21 | __tostring = "myproxy" 22 | } 23 | 24 | local obj = prototype.NewObject(proxy) 25 | obj.Name = "Test" 26 | 27 | Assert.Equal("Test", obj.__Name) 28 | Assert.Equal("Test", obj.Name) 29 | Assert.Equal("myproxy", tostring(proxy)) 30 | 31 | Assert.True(Prototype.Validate(proxy)) 32 | Assert.False(Prototype.Validate(obj)) 33 | Assert.True(Prototype.ValidateValue(proxy, obj)) 34 | end 35 | 36 | __Test__() function inheritance() 37 | local proxy = prototype { 38 | __index = function(self, key) return rawget(self, "__" .. key) end, 39 | __newindex = function(self, key, value) rawset(self, "__" .. key, value) end, 40 | } 41 | 42 | local cproxy = prototype (proxy, { __call = function(self) return("Hi " .. self.Name) end }) 43 | 44 | local obj = prototype.NewObject(cproxy) 45 | obj.Name = "Test" 46 | 47 | Assert.Equal("Test", obj.__Name) 48 | Assert.Equal("Hi Test", obj()) 49 | end 50 | 51 | __Test__() function method() 52 | local person = prototype { 53 | __index = { 54 | setName = function(self, name) self[0] = name end, 55 | getName = function(self) return self[0] end, 56 | } 57 | } 58 | 59 | local student = prototype (person, { 60 | __index = { 61 | setScore= function(self, score) self[1] = score end, 62 | getScore= function(self) return self[1] end, 63 | } 64 | }) 65 | 66 | local obj = prototype.NewObject(student) 67 | 68 | obj:setName("Ann") 69 | obj:setScore(90) 70 | 71 | Assert.Equal(90, obj[1]) 72 | Assert.Equal("Ann", obj[0]) 73 | 74 | Assert.Equal("Ann", obj:getName()) 75 | end 76 | 77 | __Test__() function controllableIndex() 78 | local indextbl = { a = 1, b = 2 } 79 | 80 | local person = prototype ({ __index = indextbl }, true) 81 | 82 | indextbl.c = 3 83 | 84 | local obj = prototype.NewObject(person) 85 | 86 | Assert.Equal(3, obj.c) 87 | end -------------------------------------------------------------------------------- /UnitTest/Tests/struct.lua: -------------------------------------------------------------------------------- 1 | --===========================================================================-- 2 | -- -- 3 | -- UnitTest For Struct -- 4 | -- -- 5 | --===========================================================================-- 6 | 7 | --===========================================================================-- 8 | -- Author : kurapica125@outlook.com -- 9 | -- URL : http://github.com/kurapica/PLoop -- 10 | -- Create Date : 2018/09/26 -- 11 | -- Update Date : 2019/02/09 -- 12 | -- Version : 1.1.0 -- 13 | --===========================================================================-- 14 | 15 | _ENV = UnitTest "PLoop.Struct" "1.1.0" 16 | 17 | namespace "UnitTest.StructCase" 18 | 19 | __Test__() function custom() 20 | Assert.Find("struct.lua:23: the value must be number, got boolean", 21 | Assert.Error( 22 | function() 23 | local v = Number(true) 24 | end 25 | ) 26 | ) 27 | 28 | struct "Color" { __base = Number, __init = function(val) return math.max(0, math.min(val, 1)) end } 29 | 30 | Assert.Find("struct.lua:33: the value must be number, got boolean", 31 | Assert.Error( 32 | function() 33 | local v = Color(true) 34 | end 35 | ) 36 | ) 37 | 38 | Assert.Equal(1, Color(10)) 39 | Assert.True(Struct.IsSubType(Color, Number)) 40 | end 41 | 42 | __Test__() function member() 43 | struct "Pos" (function(_ENV) 44 | member "x" { type = Number, require = true } 45 | y = Number 46 | member "z" { type = Number, default = 0 } 47 | end) 48 | 49 | local v = Pos(1) 50 | 51 | Assert.Equal(1, v.x) 52 | Assert.Nil(v.y) 53 | Assert.Equal(0, v.z) 54 | 55 | Assert.Find("struct.lua:58: Usage: UnitTest.StructCase.Pos(x, y, z) - the x can't be nil", 56 | Assert.Error( 57 | function() 58 | local v = Pos() 59 | end 60 | ) 61 | ) 62 | 63 | Assert.Find("struct.lua:66: Usage: UnitTest.StructCase.Pos(x, y, z) - the y must be number, got boolean", 64 | Assert.Error( 65 | function() 66 | local v = Pos{ x = 2, y = true } 67 | end 68 | ) 69 | ) 70 | end 71 | 72 | __Test__() function array() 73 | struct "PosArray" { Pos } 74 | 75 | local v = PosArray{ { x = 1 }, { x = 2, z = 3 } } 76 | 77 | Assert.Equal(0, v[1].z) 78 | Assert.Equal(1, v[1].x) 79 | Assert.Nil(v[1].y) 80 | Assert.Equal(2, v[2].x) 81 | Assert.Equal(3, v[2].z) 82 | 83 | Assert.Find("struct.lua:86: Usage: UnitTest.StructCase.PosArray(...) - the [2].x can't be nil", 84 | Assert.Error( 85 | function() 86 | local v = PosArray{ { x = 1 }, { y = 3 } } 87 | end 88 | ) 89 | ) 90 | end 91 | 92 | __Test__() function combtype() 93 | Assert.Find("struct.lua:96: the value must be value of System.Number | System.String", 94 | Assert.Error( 95 | function() 96 | local v = (Number + String)(true) 97 | end 98 | ) 99 | ) 100 | 101 | Assert.Equal(Color, (-Number)(Color)) 102 | end 103 | 104 | __Test__() function template() 105 | __Arguments__{ Number, Number }(1, 4) 106 | struct "FixString" (function(_ENV, min, max) 107 | __base = String 108 | 109 | function __valid(val) 110 | return (#val > max or #val < min) and "the %s length must between [" .. min .. "," .. max .. "]" 111 | end 112 | end) 113 | 114 | V1_3 = FixString[{1, 3}] 115 | 116 | Assert.Equal(V1_3, FixString[{1, 3}]) 117 | Assert.Equal(FixString, Struct.GetTemplate(V1_3)) 118 | Assert.Same({ 1, 3 }, { Struct.GetTemplateParameters(V1_3) }) 119 | 120 | Assert.Find("struct.lua:123: the value length must between [1,3]", 121 | Assert.Error( 122 | function() 123 | local v = V1_3("Hello") 124 | end 125 | ) 126 | ) 127 | 128 | Assert.Find("struct.lua:131: the value length must between [1,4]", 129 | Assert.Error( 130 | function() 131 | local v = FixString("Hello") 132 | end 133 | ) 134 | ) 135 | end 136 | 137 | __Test__() function dict() 138 | struct "IDName" { [Number] = String } 139 | 140 | Assert.Find("struct.lua:143: the [key in value] must be number, got string", 141 | Assert.Error( 142 | function() 143 | local v = IDName{ [100] = "Ann", Ben = 2 } 144 | end 145 | ) 146 | ) 147 | end -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | -- The init file of PLoop Core 2 | local root = require "PLoop.Prototype" 3 | 4 | -- System 5 | require "PLoop.System.Scalar" 6 | require "PLoop.System.Collections" 7 | require "PLoop.System.Threading" 8 | require "PLoop.System.Text" 9 | require "PLoop.System.Serialization" 10 | require "PLoop.System.Date" 11 | require "PLoop.System.Logger" 12 | require "PLoop.System.Recycle" 13 | require "PLoop.System.ValueWrapper" 14 | 15 | -- System.Collections 16 | require "PLoop.System.Collections.List" 17 | require "PLoop.System.Collections.Dictionary" 18 | require "PLoop.System.Collections.Proxy" 19 | require "PLoop.System.Collections.IIndexedListSorter" 20 | require "PLoop.System.Collections.Array" 21 | require "PLoop.System.Collections.Queue" 22 | require "PLoop.System.Collections.Range" 23 | 24 | -- System.Threading 25 | require "PLoop.System.Threading.TaskScheduler" 26 | 27 | -- System Configuration 28 | require "PLoop.System.Configuration" 29 | 30 | -- System.Text.Encoding 31 | require "PLoop.System.Text.UTF8Encoding" 32 | require "PLoop.System.Text.UTF16Encoding" 33 | 34 | -- System.Serialization 35 | require "PLoop.System.Serialization.LuaFormatProvider" 36 | require "PLoop.System.Serialization.StringFormatProvider" 37 | require "PLoop.System.Serialization.JsonFormatProvider" 38 | 39 | -- System.Text.Encoder 40 | require "PLoop.System.Text.XmlEntity" 41 | require "PLoop.System.Text.Base64" 42 | require "PLoop.System.Text.Deflate" 43 | 44 | -- Syste.Text 45 | require "PLoop.System.Text.Crc" 46 | require "PLoop.System.Text.TemplateString" 47 | 48 | -- System.Reactive 49 | require "PLoop.System.Observer" 50 | require "PLoop.System.Reactive.Reactive" 51 | require "PLoop.System.Reactive.Observer" 52 | require "PLoop.System.Reactive.Subject" 53 | require "PLoop.System.Reactive.ReactiveValue" 54 | require "PLoop.System.Reactive.ReactiveField" 55 | require "PLoop.System.Reactive.ReactiveList" 56 | require "PLoop.System.Reactive.ReactiveDictionary" 57 | require "PLoop.System.Reactive.Observable" 58 | require "PLoop.System.Reactive.Operator" 59 | require "PLoop.System.Reactive.Watch" 60 | 61 | return root --------------------------------------------------------------------------------