[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/Peter-Greek/lua_enum/main/enum.lua [Back]  [Original]

local ENUM_RUN_TESTS = true
local function _pair(k, v, is_auto)
	return { __enum_key = k, __enum_val = v, __enum_auto = is_auto == true }
end
local function _symbol(name)
	return function(v)
		-- if user wrote NAME (x) then v is provided
		if v ~= nil then
			return _pair(name, v, false)
		end
		-- if user wrote NAME with no parentheses, we need a bare value in the list
		-- but Lua cannot place a function call-less identifier into a list without evaluating it
		-- so we return an auto marker by making the symbol itself evaluate to this marker:
		return _pair(name, nil, true)
	end
end
local function _build(list)
	local enum = {}
	local next_auto = 1

	for i = 1, #list do
		local item = list[i]
		if type(item) == "table" and item.__enum_key ~= nil then
			local key = item.__enum_key

			if item.__enum_auto then
				enum[key] = next_auto
				next_auto = next_auto + 1
			else
				local v = item.__enum_val
				enum[key] = v
				-- C++ style: after explicit value, next implicit continues from v+1
				if type(v) == "number" then
					next_auto = v + 1
				end
			end
		end
	end

	return enum
end
local function _stack_contains_func(target_func, max_depth)
	max_depth = max_depth or 12
	for level = 2, max_depth do
		local info = debug.getinfo(level, "f")
		if not info then break end
		if info.func == target_func then
			return true
		end
	end
	return false
end

ENUM = setmetatable({}, {
	__call = function(_, enumName)
		local prev_mt = getmetatable(_G)
		local prev_index = prev_mt and prev_mt.__index or nil

		-- function that invoked: ENUM "NAME"
		local caller = debug.getinfo(2, "f").func
		local caller_co = coroutine.running()

		local sym_cache = {}

		local function guarded_index(t, k)
			-- preserve any prior __index behavior
			if type(prev_index) == "function" then
				local v = prev_index(t, k)
				if v ~= nil then return v end
			elseif type(prev_index) == "table" then
				local v = prev_index[k]
				if v ~= nil then return v end
			end

			-- real globals
			local v = rawget(t, k)
			if v ~= nil then return v end

			-- only during the same call chain + same coroutine
			if coroutine.running() ~= caller_co then
				return nil
			end
			if not _stack_contains_func(caller) then
				return nil
			end

			v = sym_cache[k]
			if not v then
				-- IMPORTANT: return a value that can appear in the list without parentheses
				-- We make the global lookup itself return the AUTO marker table,
				-- but we also want parentheses support. So we return a callable table:
				local auto_marker = _pair(k, nil, true)
				v = setmetatable(auto_marker, {
					__call = function(_, arg)
						return _pair(k, arg, false)
					end
				})

				sym_cache[k] = v
			end

			return v
		end

		local tmp_mt = {}
		if prev_mt then
			for k, v in pairs(prev_mt) do
				tmp_mt[k] = v
			end
		end
		tmp_mt.__index = guarded_index

		setmetatable(_G, tmp_mt)

		return function(list)
			setmetatable(_G, prev_mt)

			local enum = _build(list)
			rawset(_G, enumName, enum)
			return enum
		end
	end
})

if ENUM_RUN_TESTS then
	-- note: use ENUM
	local x = ENUM "HOUSING_e" ({
		APARTMENT,        -- 1
		HOUSE,            -- 2
		MANSION,          -- 3
		SHOP(10),         -- 10
		OFFICE,           -- 11
		WAREHOUSE(20),    -- 20
		GARAGE,           -- 21
		TRAILER,          -- 22
		HIGHENDAPARTMENT, -- 23
	})

	print(HOUSING_e.APARTMENT, HOUSING_e.HIGHENDAPARTMENT, HOUSING_e.NONEXISTENT)
end

return ENUM

Web Proxy Viewer  |  New URL  |  Original Page