SkillAgentSearch skills...

PLoop

Prototype Lua object-oriented program system and frameworks.

Install / Use

/learn @kurapica/PLoop

README

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

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

View on GitHub
GitHub Stars239
CategoryDevelopment
Updated2mo ago
Forks34

Languages

Lua

Security Score

100/100

Audited on Jan 24, 2026

No findings