PLoop
Prototype Lua object-oriented program system and frameworks.
Install / Use
/learn @kurapica/PLoopREADME
Prototype Lua Object-Oriented Program System
中文版请点击README-zh.md
PLoop is a C# like style object-oriented program system for lua. It support Lua 5.1 and above versions, also include the luajit. It's also designed to be used on multi-os thread platforms.
It provide the usage of enum, struct, interface and class. Also provide common useful features like thread, collection, serialization , data entity framework, web framework and etc.
You can also find useful features for enterprise development like code organization, type validation and etc.
You can find more details in the Docs
Table of Contents
- Install
- Using the collection
- Attribute and Thread Pool
- Spell Error Checks And More
- Type Validation
- enum
- struct
- Class
- Interface
- Serialization
- Reactive & Watch
- Data Entity
- Web FrameWork
Install
After install the Lua, download the PLoop and save it to LUA_PATH, or you can use
package.path = package.path .. ";PATH TO PLOOP PARENT FOLDER/?/init.lua;PATH TO PLOOP PARENT FOLDER/?.lua"
require "PLoop"
to load the PLoop. If you need to load files by yourself, you could check the PLoop/init.lua for more details.
Using the collection
The collection classes will provide useful stream works like
require "PLoop"
-- Generate a list from 1 to 10, choose all even numbers, square them and join them by ","
-- The result is "4,16,36,64,100"
print( PLoop.System.Collections.List(10):Range(2, -1, 2):Map("x=>x^2"):Join(",") )
-- Get all the keys from the _G whose value is a table, sort them and join them by ","
-- The result is "_G,arg,coroutine,debug,io,math,os,package,string,table"
print( PLoop.System.Collections.XDictionary(_G):Filter("k,v=>type(v)=='table'").Keys:ToList():Sort():Join(",") )
Attribute and Thread Pool
We have see how to use classes in the previous example, for the second example, I'll show special usage of the PLoop:
require "PLoop"
PLoop(function(_ENV)
__Iterator__()
function iter(i, j)
for k = i, j do
coroutine.yield(k)
end
end
-- print 1-10 for each line
for i in iter(1, 10) do
print(i)
end
end)
The PLoop can used to call a function with _ENV as its first arguments, this is used to make sure the code in the function will be processed in a special environment provided by the PLoop, in here, you can use List instead the PLoop.System.Collections.List. The best part is you can use the attribute for functions.
Unlike the _G, the PLoop environments are very sensitive about new variables, when the iter is defiend, the system will check if there is any attributes should be applied on the function, here we have the __Iterator__().
The __Iterator__ is an attribute class defined in System.Threading, when we use it to create an object, the object is registered to the system, and waiting for the next attribute target(like function, class and etc) that should be defined. The attributes are used to modify or attach data to the attribute targets.
The __Iterator__ is used to wrap the target function, so it'll be used as an iterator that runs in a corotuine, and we can use coroutine.yield to return values:
require "PLoop"
PLoop(function(_ENV)
-- Calculate the Fibonacci sequence
__Iterator__()
function Fibonacci(maxn)
local n0, n1 = 1, 1
coroutine.yield(0, n0)
coroutine.yield(1, n1)
local n = 2
while n <= maxn do
n0, n1 = n1, n0 + n1
coroutine.yield(n, n1)
n = n + 1
end
end
-- 1, 1, 2, 3, 5, 8
for i, v in Fibonacci(5) do print(v) end
-- you also can pass the argument later
-- the iterator will combine all arguments
-- 1, 1, 2, 3, 5, 8
for i, v in Fibonacci(), 5 do print(v) end
end)
The collection object method also using the coroutines, so it don't need to generate any cache or anonymous function to do the jobs, since those coroutines are recycled automatically, there is no cost compares to other solutions.
Spell Error Checks And More
There are a lots of troubles in the Lua debugging, if the lua error can be triggered, it's still easy to fix it, but for codes like if a == ture then, ture is a non-existent variable, Lua treate it as nil so the checking will still working, but the result can't be right.
We'll see how to solve it in the PLoop.
Read un-existed global variables
Before rquire the PLoop, we can create a PLOOP_PLATFORM_SETTINGS table to toggle the PLoop's system settings:
PLOOP_PLATFORM_SETTINGS = { ENV_ALLOW_GLOBAL_VAR_BE_NIL = false }
require "PLoop"
PLoop(function(_ENV)
local a = ture -- Error: The global variable "ture" can't be nil.
if a then
print("ok")
end
end)
Turn off the ENV_ALLOW_GLOBAL_VAR_BE_NIL will apply a strict mode for all PLoop private environment, so no nil variables can be accessed, so you can locate those errors.
Write to illegal global variables
If we missing the local, we may create unwanted global variables. But the system can't diff the wanted and unwanted global variable, we can add filter in the platform settings to do the job, so we can remove the filter when we don't need it:
PLOOP_PLATFORM_SETTINGS = {
GLOBAL_VARIABLE_FILTER = function(key, value)
-- Don't allow the lowercase key with non-function value
if type(key) == "string" and key:match("^%l") and type(value) ~= "function" then
return true
end
end,
}
require "PLoop"
PLoop(function(_ENV)
Test = 1
class "A" (function(_ENV)
function Test(self)
ch = 2 -- error: There is an illegal assignment for "ch"
end
end)
A():Test()
end)
If the filter return true, the assignment will trigger an error, so the code'll be stopped, if we only need a warning, we can add a setting like:
PLOOP_PLATFORM_SETTINGS = {
GLOBAL_VARIABLE_FILTER = function(key, value)
-- Don't allow the lowercase key with non-function value
if type(key) == "string" and key:match("^%l") and type(value) ~= "function" then
return true
end
end,
GLOBAL_VARIABLE_FILTER_USE_WARN = true,
}
require "PLoop"
PLoop(function(_ENV)
Test = 1
class "A" (function(_ENV)
function Test(self)
ch = 2 -- [PLoop: Warn]There is an illegal assignment for "ch"@path_to_file\file.lua:18
end
end)
A():Test()
end)
You also can use the filter as a record, with another setting, the call line'll be passed in as the 3rd argument:
PLOOP_PLATFORM_SETTINGS = {
GLOBAL_VARIABLE_FILTER = function(key, value, path)
print("Assign '" .. key .. "'" .. path )
end,
GLOBAL_VARIABLE_FILTER_GET_CALLLINE = true,
}
require "PLoop"
PLoop(function(_ENV)
Test = 1 -- Assign 'Test'@path_to_file\file.lua:11
class "A" (function(_ENV)
function Test(self)
ch = 2 -- Assign 'ch'@path_to_file\file.lua:15
end
end)
A():Test()
end)
To use the get call line, the debug.getinfo must exist.
Access un-existed object fields
We also can block the accessing of un-existed object fields:
PLOOP_PLATFORM_SETTINGS = { OBJECT_NO_RAWSEST = true, OBJECT_NO_NIL_ACCESS = true }
require "PLoop"
PLoop(function(_ENV)
-- Define a class with Name and Age property
class "Person" (function(_ENV)
property "Name" { type = String }
property "Age" { type = Number }
end)
o = Person()
o.Name = "King" -- Ok
o.name = "Ann" -- Error: The object can't accept field that named "name"
print(o.name) -- Error: The object don't have any field that named "name"
end)
This three settings will help authors to avoid many spell errors during the development. You shouldn't use those settings when you release the project since the access speeding should be slightly increased.
Type Validation
PLoop make the Lua as a strong type language, there are many type validation features to stop the errors spread to far so too hard to be tracked.
The function validation is always a complex part, we need to do many checks before the function's main logic for the arguments so we can tell the caller where and what is failed. And when the project is released, those check should be removed since we already test them.
Within the PLoop, it'll be a small problem:
require "PLoop"
PLoop(function(_ENV)
__Arguments__{ String, Number }
function SetInfo(name, age)
end
-- Error: Usage: SetInfo(System.String, System.Number) - the 2nd argument must be number, got boolean
SetInfo("Ann", true)
end)
The __Arguments__ is an attribute class defined in the System, it associated the argument name, type, default value and etc to the argument, also wrap those functions with the argument validation.
The String and Number are struct types used to validate values, we'll see them at the introduction of struct.
If we need to release the project, there is also no need to remove those __Arguments__, you can change the platform setting( not all type validation would be removed, but just leave them to the system):
PLOOP_PLATFORM_SETTINGS = { TYPE_VALIDATION_DISABLED = true }
require "PLoop"
PLoop(function(_ENV)
__Arguments__{ String, Number }
function SetInfo(name, age)
end
-- No error now
SetInfo("Ann", true)
end)
To achieve a whole type validation system, we need more types to describe the datas. In PLoop, there are four types: enum, struc
Related Skills
node-connect
347.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.0kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
