This repository was archived by the owner on Jul 11, 2026. It is now read-only.
forked from darrellanderson/CrLua
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCrLua.ttslua
More file actions
173 lines (157 loc) · 6.16 KB
/
Copy pathCrLua.ttslua
File metadata and controls
173 lines (157 loc) · 6.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
-------------------------------------------------------------------------------
--- Create the base CrLua table, with require function for dependencies.
-- @author Darrell
--
-- Suggested use: at the END of the object script (so #include does not affect
-- line number reporting in the above portion), include CrLua and dependencies:
--
-- #include <~/CrLua/CrLua>
-- #include <~/CrLua/LockGlobals>
--
-- #include <~/CrLua/[OTHER_DEPENDENCY]> (repeat)
--
-- CrLua.assertRequired() -- do this BEFORE lock
-- CrLua.lock()
--
-- -- Optionally enable logging.
-- CrLua.Log.setLogLevel(CrLua.Log.LOG_LEVEL.DEBUG)
-------------------------------------------------------------------------------
local TAG = 'CrLua'
-- Create a GLOBAL CrLua table.
CrLua = CrLua or {} -- global, <include> wraps in a do .. end block
CrLua._require = { 'LockTable' }
-------------------------------------------------------------------------------
--- Assert all required packages exist.
-- @param extraRequires table : list of additional require entries.
-- @param table : table to search for _require entries, or CrLua if nil.
--
-- Modules add dependencies by having a _require item in their root table,
-- with a list of module names.
-- e.g. CrLua.MyModule = { _require = { 'OtherModule1', 'OtherModule2' } }.
-------------------------------------------------------------------------------
function CrLua.assertRequired(extraRequires, table)
local seen = {}
local requireSet = {}
local haveSet = {}
if extraRequires then
for _, name in ipairs(extraRequires) do
requireSet[name] = true
end
end
local function addRequireEntries(table, packagePath)
assert(type(table) == 'table')
if seen[table] then
return
end
seen[table] = true
if table._require then
for _, name in ipairs(table._require) do
requireSet[name] = true
end
if packagePath then
haveSet[packagePath] = true
end
end
for k, v in pairs(table) do
if type(v) == 'table' and not string.match(k, '^[_%l]') then
local recurse = type(k) == 'string' and string.match(k, '^%u')
if recurse then
local childPackagePath = (packagePath and (packagePath .. '.') or '') .. k
addRequireEntries(v, childPackagePath)
end
end
end
end
addRequireEntries(table or CrLua)
-- Make sure all required packages are present.
local message = false
for package, _ in pairs(requireSet) do
assert(type(package) == 'string')
-- Descend dot-delimited sub-package names.
local entry = CrLua
for subPackage in string.gmatch(package, '[^%.]+') do
entry = entry and entry[subPackage]
end
if not entry then
message = (message and message .. ', ' or '') .. package
end
end
if message then
error(TAG .. ': assertRequired missing ' .. message)
end
-- Make sure all present packages are required.
local message = false
for package, _ in pairs(haveSet) do
if not requireSet[package] then
message = (message and message .. ', ' or '') .. package
end
end
if message then
error(TAG .. ': assertRequired have not-required ' .. message)
end
end
-------------------------------------------------------------------------------
--- Lock CrLua packages.
--
-- @param tableName string : name of table being locked, or nil for CrLua.
-- @param table : table being locked, or nil for CrLua.
--
-- Make CrLua and nested packages read-only, with access requiring keys exist.
--
-- Locking rules:
--
-- (1.) '_data' is a reserved table name for mutable tables. Do not lock.
--
-- (2.) If the table starts with a capital letter, make it read-only and
-- require keys exist. These are packages or constants, attempting to access
-- a missing field is probably a typo. (Override by adding an
-- _allowMissingKeys = { 'tableName1', 'tableName2'} entry at the same level
-- as the referenced tables. Such tables allow reads to missing keys, as well
-- as all sub-tables in them.)
--
-- (2.) Otherwise make it read-only but allow reads to missing keys (value nil).
-- One can query membership by checking if a key exists.
--
-- This helps catch typos calling non-existent functions, or overwriting
-- a constant value by mistake.
-------------------------------------------------------------------------------
function CrLua.lock(tableName, table)
local seen = {}
local function lockRecursive(tableName, table, allowMissingKeys)
assert(type(tableName) == 'string' and type(table) == 'table' and type(allowMissingKeys) == 'boolean')
-- Watch out for cycles! This might happen if a script defines
-- an enum table, then later uses one of them in a different table.
if seen[table] then
return
end
seen[table] = true
-- Leave _data tables alone.
local isData = tableName == '_data'
if isData then
return
end
-- Get any missing keys entry BEFORE locking this table.
local allowMissingKeysTableNameSet = {}
if table._allowMissingKeys then
for _, tableName in ipairs(table._allowMissingKeys) do
allowMissingKeysTableNameSet[tableName] = true
end
end
-- Lock the given table.
local firstLetter = string.match(tableName, '%a')
local isUpper = firstLetter and firstLetter == string.upper(firstLetter)
if isUpper and not allowMissingKeys then
CrLua.LockTable.readOnlyRequireKey(tableName, table)
else
CrLua.LockTable.readOnly(tableName, table)
end
-- Lock sub-tables, respecting _allowMissingKeys values.
for k, v in pairs(table) do
if type(v) == 'table' then
local entryAllowMissingKeys = allowMissingKeys or allowMissingKeysTableNameSet[k] or false
lockRecursive(tostring(k), v, entryAllowMissingKeys)
end
end
end
lockRecursive(tableName or 'CrLua', table or CrLua, false)
end