758 lines
19 KiB
Lua
758 lines
19 KiB
Lua
|
|
local util = require "util"
|
|
local TabbedFile = require "TabbedFile"
|
|
|
|
local actionTypes = {}
|
|
local conditionals = {}
|
|
|
|
-------------------------------
|
|
-- Action base-class
|
|
local action = {}
|
|
|
|
function action:Process(context)
|
|
--Allow start-of-iteration only data to parse.
|
|
if(self.first) then
|
|
--Note that it's *specifically* equal to false. Being 'nil' isn't enough.
|
|
if(context._first == false) then
|
|
return
|
|
end
|
|
end
|
|
|
|
--Allow end-if-iteration only data to parse.
|
|
if(self.last) then
|
|
--Note that it's *specifically* equal to false. Being 'nil' isn't enough.
|
|
if(context._last == false) then
|
|
return
|
|
end
|
|
end
|
|
|
|
--Conditional
|
|
if(self._cond) then
|
|
if(not conditionals[self._cond](context)) then
|
|
return
|
|
end
|
|
end
|
|
|
|
--NO MORE RETURNS FROM THIS POINT FORWARD!
|
|
if(self.newStyle) then
|
|
context:PushStyle(self.newStyle)
|
|
end
|
|
|
|
local noChildren = nil
|
|
if(self.PreProcess) then
|
|
noChildren = self:PreProcess(context)
|
|
end
|
|
|
|
if(not noChildren) then
|
|
self:ProcessChildren(context)
|
|
end
|
|
|
|
if(self.PostProcess) then
|
|
self:PostProcess(context)
|
|
end
|
|
|
|
if(self.newStyle) then
|
|
context:PopStyle()
|
|
end
|
|
end
|
|
|
|
function action:ProcessChildren(context)
|
|
for _, action in ipairs(self) do
|
|
--Preserve the first value.
|
|
local oldFirst = context._first
|
|
local oldLast = context._last
|
|
action:Process(context)
|
|
context._first = oldFirst
|
|
context._last = oldLast
|
|
end
|
|
end
|
|
|
|
local valueResolvers =
|
|
{
|
|
enum = function(enum) return enum.name end,
|
|
func = function(func) return func.name end,
|
|
spec = function(spec) return spec.FuncNamePrefix() end,
|
|
}
|
|
|
|
local function ResolveValue(context, value)
|
|
--Find every occurrance of %chars, and try to turn that into a context variable.
|
|
local possibleVars = {}
|
|
for var in value:gmatch("%%([_%a][_%w]*)") do
|
|
possibleVars[var] = true
|
|
end
|
|
|
|
for var, _ in pairs(possibleVars) do
|
|
if(not context[var]) then
|
|
return nil, "The variable " .. var .. " from the value string was not found.\n" .. value
|
|
end
|
|
|
|
local replace = context[var]
|
|
if(type(replace) ~= "string") then
|
|
local str = tostring(replace)
|
|
if(str) then
|
|
replace = str
|
|
elseif(valueResolvers[var]) then
|
|
replace = valueResolvers[var](replace)
|
|
elseif(type(replace) == "table" and replace._ValueResolve) then
|
|
replace = replace:_ValueResolve()
|
|
end
|
|
end
|
|
|
|
if(type(replace) ~= "string") then
|
|
return nil, "Could not convert the variable " .. var .. " into a string."
|
|
end
|
|
|
|
value = value:gsub("%%" .. var, replace)
|
|
end
|
|
|
|
return value
|
|
end
|
|
|
|
function action:CallFunction(context, name)
|
|
name = name or self.name
|
|
self:Assert(name, "Unknown function name.")
|
|
local style = context:FindStyleForFunc(name)
|
|
|
|
if(not style) then
|
|
if(self.optional) then
|
|
return
|
|
else
|
|
self:Assert(nil, "The style does not have a function " .. name)
|
|
end
|
|
end
|
|
|
|
if(self.value) then
|
|
context.value = self:Assert(ResolveValue(context, self.value))
|
|
end
|
|
|
|
local paramList = {}
|
|
for _, param in ipairs(self.params) do
|
|
assert(context[param], "The function " .. name ..
|
|
" need a parameter " .. param .. " which doesn't exist at this point")
|
|
paramList[#paramList + 1] = context[param]
|
|
end
|
|
|
|
local rets = { style[name](unpack(paramList)) }
|
|
|
|
if(self.value) then
|
|
context.value = nil
|
|
end
|
|
|
|
return unpack(rets)
|
|
end
|
|
|
|
function action:Assert(...)
|
|
local test, text = ...
|
|
if(not test) then
|
|
local msg = ": " .. text
|
|
if(self.name) then
|
|
msg = self._actionType .. "." .. self.name .. msg
|
|
else
|
|
msg = self._actionType .. msg
|
|
end
|
|
assert(test, msg)
|
|
end
|
|
|
|
return ...
|
|
end
|
|
|
|
--Iterates over the list, setting the second element returned from the iterator
|
|
--as the given context table key.
|
|
function action:IterateChildren(context, list, key, PostProc)
|
|
PostProc = PostProc or function() end
|
|
|
|
local oldVal = context[key]
|
|
for _, val in ipairs(list) do
|
|
context[key] = val
|
|
context._first = (_ == 1)
|
|
context._last = (_ == #list)
|
|
self:ProcessChildren(context)
|
|
PostProc(context, val)
|
|
end
|
|
context[key] = oldVal
|
|
end
|
|
|
|
local function CreateAction(data, actionType)
|
|
local act = {}
|
|
util.DeepCopyTable(action, act)
|
|
|
|
assert(actionType, "No name given for action type")
|
|
|
|
--Create custom param list.
|
|
if(data.name) then
|
|
local name, params = data.name:match("([_%w]+)%s*%((.*)%)")
|
|
if(name) then
|
|
local paramList = {}
|
|
for param in params:gmatch("([_%a][_%w]*)") do
|
|
paramList[#paramList + 1] = param
|
|
end
|
|
params = paramList
|
|
else
|
|
name = data.name
|
|
end
|
|
|
|
act.name = name
|
|
act.params = params
|
|
end
|
|
|
|
if(data.cond) then
|
|
assert(conditionals[data.cond], "Unknown conditional " .. data.cond)
|
|
act._cond = data.cond
|
|
end
|
|
|
|
act.newStyle = data.style
|
|
act.optional = data.optional
|
|
act.value = data.value
|
|
|
|
--Make child actions recursively.
|
|
for _, child in ipairs(data) do
|
|
assert(actionTypes[child.type], "Unknown command type " .. child.type)
|
|
act[#act + 1] = actionTypes[child.type](child)
|
|
end
|
|
|
|
if(data.first) then
|
|
act.first = true
|
|
end
|
|
|
|
if(data.last) then
|
|
act.last = true
|
|
end
|
|
|
|
act._actionType = actionType
|
|
|
|
return act
|
|
end
|
|
|
|
local function MakeActionType(typeName, typeTable, PostInitFunc)
|
|
actionTypes[typeName] = function(data)
|
|
local act = CreateAction(data, typeName)
|
|
util.DeepCopyTable(typeTable, act)
|
|
|
|
PostInitFunc(act, data)
|
|
|
|
return act
|
|
end
|
|
end
|
|
|
|
|
|
-------------------------------------
|
|
-- Group Action
|
|
local groupAction = {}
|
|
|
|
MakeActionType("group", groupAction, function(self, data)
|
|
end)
|
|
|
|
|
|
-------------------------------------
|
|
-- Call Action
|
|
local callAction = {}
|
|
|
|
function callAction:PreProcess(context)
|
|
self:CallFunction(context, self.name)
|
|
end
|
|
|
|
MakeActionType("call", callAction, function(self, data)
|
|
self.params = self.params or {}
|
|
end)
|
|
|
|
|
|
-------------------------------------
|
|
-- Context Action
|
|
local contextAction = {}
|
|
|
|
function contextAction:PreProcess(context)
|
|
self:Assert(context[self.key] == nil,
|
|
"Attempt to nest the context variable " .. self.key)
|
|
|
|
if(self.data) then
|
|
context[self.key] = self.data
|
|
else
|
|
context[self.key] = self:CallFunction(context, self.name)
|
|
end
|
|
end
|
|
|
|
function contextAction:PostProcess(context)
|
|
if(self.dispose) then
|
|
local style = context:FindStyleForFunc(self.dispose)
|
|
self:Assert(style,
|
|
string.format("Could not find the disposal function %s for %s.",
|
|
self.dispose, self.key))
|
|
|
|
style[self.dispose](context[self.key])
|
|
end
|
|
context[self.key] = nil
|
|
end
|
|
|
|
MakeActionType("context", contextAction, function(self, data)
|
|
assert(data.key, "Context actions must have a `key`.")
|
|
assert(data.key:match("%_$"), "Context action keys must end in `_`.")
|
|
self.key = data.key
|
|
self.data = data.data
|
|
if(self.name) then
|
|
self.name = "State" .. self.name
|
|
end
|
|
self.dispose = data.dispose
|
|
if(self.dispose) then
|
|
self.dispose = "Dispose" .. self.dispose
|
|
end
|
|
|
|
assert(self.data or self.name, "Context actions must have either `data` or `name`.")
|
|
|
|
self.params = self.params or {}
|
|
end)
|
|
|
|
|
|
-------------------------------------------
|
|
-- Filter Action
|
|
local filterAction = {}
|
|
|
|
function filterAction:PreProcess(context)
|
|
local shouldFilter = self:CallFunction(context, self.name)
|
|
if(self.neg) then
|
|
shouldFilter = not shouldFilter
|
|
end
|
|
return not shouldFilter
|
|
end
|
|
|
|
MakeActionType("filter", filterAction, function(self, data)
|
|
assert(data.name, "Filter actions must have a `name`")
|
|
self.name = "Filter" .. self.name
|
|
self.neg = data.neg
|
|
self.params = self.params or {}
|
|
end)
|
|
|
|
|
|
----------------------------
|
|
-- File Action
|
|
local fileAction = {}
|
|
|
|
function fileAction:PreProcess(context)
|
|
self:Assert(context.hFile == nil, "You cannot nest `file` blocks.")
|
|
|
|
local filename = self:CallFunction(context)
|
|
|
|
context.hFile = util.CreateFile(filename, context.options.indent)
|
|
end
|
|
|
|
function fileAction:PostProcess(context)
|
|
context.hFile:close()
|
|
context.hFile = nil
|
|
end
|
|
|
|
MakeActionType("file", fileAction, function(self, data)
|
|
assert(data.style, "File actions must have a `style`")
|
|
assert(data.name, "File actions need a name to call.")
|
|
|
|
self.params = self.params or {"basename", "options"}
|
|
end)
|
|
|
|
|
|
-------------------------------------
|
|
-- Block Action
|
|
local blockAction = {}
|
|
|
|
function blockAction:PreProcess(context)
|
|
assert(context.hFile, "Cannot write a block outside of a file. " .. self.name)
|
|
self:CallFunction(context, "WriteBlockBegin" .. self.name)
|
|
end
|
|
|
|
function blockAction:PostProcess(context)
|
|
self:CallFunction(context, "WriteBlockEnd" .. self.name)
|
|
end
|
|
|
|
MakeActionType("block", blockAction, function(self, data)
|
|
assert(data.name, "Block actions must have a `name`")
|
|
|
|
self.params = self.params or {"hFile", "spec", "options"}
|
|
end)
|
|
|
|
|
|
------------------------------------------
|
|
-- Write Action
|
|
local writeAction = {}
|
|
|
|
function writeAction:PreProcess(context)
|
|
assert(context.hFile, "Cannot write data outside of a file.")
|
|
self:CallFunction(context)
|
|
end
|
|
|
|
MakeActionType("write", writeAction, function(self, data)
|
|
assert(data.name, "Write actions must have a `name`")
|
|
self.name = "Write" .. self.name
|
|
self.params = self.params or {"hFile", "specData", "spec", "options"}
|
|
end)
|
|
|
|
|
|
------------------------------------------
|
|
-- Blank Action
|
|
local blankAction = {}
|
|
|
|
function blankAction:PreProcess(context)
|
|
self:Assert(context.hFile, "Blanks must be in files.")
|
|
context.hFile:write("\n")
|
|
end
|
|
|
|
MakeActionType("blank", blankAction, function(self, data)
|
|
end)
|
|
|
|
|
|
---------------------------------------------
|
|
-- Extension Iterator Action
|
|
local extIterAction = {}
|
|
|
|
function extIterAction:PreProcess(context)
|
|
self:Assert(context.extName == nil, "Cannot nest ext-iter actions.")
|
|
self:IterateChildren(context, context.options.extensions, "extName")
|
|
return true --Stops regular child processing.
|
|
end
|
|
|
|
MakeActionType("ext-iter", extIterAction, function(self, data)
|
|
end)
|
|
|
|
conditionals["ext-iter"] = function(context)
|
|
return #context.options.extensions ~= 0
|
|
end
|
|
|
|
|
|
-----------------------------------------------
|
|
-- Version Iterator
|
|
local versionIterAction = {}
|
|
|
|
function versionIterAction:PreProcess(context)
|
|
self:Assert(context.version == nil, "Cannot nest version-iter actions.")
|
|
local rawVersionList = context.specData.versions or {}
|
|
local versionList = {}
|
|
for _, version in ipairs(rawVersionList) do
|
|
if(tonumber(version) <= tonumber(context.options.version)) then
|
|
versionList[#versionList + 1] = version
|
|
end
|
|
end
|
|
|
|
self:IterateChildren(context, versionList, "version")
|
|
return true --Stops regular child processing.
|
|
end
|
|
|
|
MakeActionType("version-iter", versionIterAction, function(self, data)
|
|
end)
|
|
|
|
conditionals["version-iter"] = function(context)
|
|
return context.specData.versions ~= nil
|
|
end
|
|
|
|
|
|
-----------------------------------------------
|
|
-- Sub-Version Iterator
|
|
local subVersionIterAction = {}
|
|
|
|
function subVersionIterAction:PreProcess(context)
|
|
self:Assert(context.sub_version == nil, "Cannot nest sub-version-iter actions.")
|
|
self:Assert(context.version, "Must put sub-version-iter inside versions.")
|
|
local rawVersionList = context.specData.versions or {}
|
|
local versionList = {}
|
|
for _, version in ipairs(rawVersionList) do
|
|
if(tonumber(version) <= tonumber(context.version)) then
|
|
versionList[#versionList + 1] = version
|
|
end
|
|
end
|
|
|
|
self:IterateChildren(context, versionList, "sub_version")
|
|
return true --Stops regular child processing.
|
|
end
|
|
|
|
MakeActionType("sub-version-iter", subVersionIterAction, function(self, data)
|
|
end)
|
|
|
|
---------------------------------------------
|
|
-- Core Extension Iterator Action
|
|
local coreExtIterAction = {}
|
|
|
|
function coreExtIterAction:PreProcess(context)
|
|
self:Assert(context.version, "Must put this in a version iterator")
|
|
self:Assert(context.extName == nil, "Cannot nest core-ext-iter actions.")
|
|
local coreExts = context._coreExts
|
|
if(coreExts[context.version]) then
|
|
self:IterateChildren(context, coreExts[context.version], "extName")
|
|
end
|
|
return true --Stops regular child processing.
|
|
end
|
|
|
|
MakeActionType("core-ext-iter", coreExtIterAction, function(self, data)
|
|
end)
|
|
|
|
conditionals["core-ext-iter"] = function(context)
|
|
assert(context.version, "Cannot have a core-ext-iter conditional outside of a version.")
|
|
return context._coreExts[context.version] ~= nil
|
|
end
|
|
|
|
|
|
--[==[
|
|
---------------------------------------------
|
|
-- Core Extension Iterator Action, culled against the requested extensions.
|
|
local coreExtCullIterAction = {}
|
|
|
|
local function BuildCulledExtList(context)
|
|
local coreExts = context._coreExts
|
|
if(coreExts[context.version]) then
|
|
local extList = {}
|
|
for _, ext in ipairs(coreExts[context.version]) do
|
|
if(not context._extTbl[ext]) then
|
|
extList[#extList + 1] = ext
|
|
end
|
|
end
|
|
return extList
|
|
else
|
|
return {}
|
|
end
|
|
end
|
|
|
|
function coreExtCullIterAction:PreProcess(context)
|
|
self:Assert(context.version, "Must put core-ext-cull-iters in a version")
|
|
self:Assert(context.extName == nil, "Cannot nest core-ext-cull-iter actions.")
|
|
local extList = BuildCulledExtList(context)
|
|
if(#extList > 0) then
|
|
self:IterateChildren(context, extList, "extName")
|
|
end
|
|
return true --Stops regular child processing.
|
|
end
|
|
|
|
MakeActionType("core-ext-cull-iter", coreExtCullIterAction, function(self, data)
|
|
end)
|
|
|
|
conditionals["core-ext-cull-iter"] = function(context)
|
|
assert(context.version, "Cannot have a core-ext-cull-iter conditional outside of a version.")
|
|
return #BuildCulledExtList(context) > 0
|
|
end
|
|
|
|
]==]
|
|
----------------------------------------------
|
|
-- Enum Seen Action
|
|
local enumSeenAction = {}
|
|
|
|
function enumSeenAction:PreProcess(context)
|
|
self:Assert(context.enumSeen == nil, "Cannot nest enum-seen actions.")
|
|
context.enumSeen = {}
|
|
end
|
|
|
|
function enumSeenAction:PostProcess(context)
|
|
context.enumSeen = nil
|
|
end
|
|
|
|
MakeActionType("enum-seen", enumSeenAction, function(self, data)
|
|
end)
|
|
|
|
|
|
-----------------------------------------------
|
|
-- Enumerator Iterator
|
|
local enumIterAction = {}
|
|
|
|
local function GetEnumList(context)
|
|
if(context.extName) then
|
|
--Get enum list for the extension.
|
|
return context.specData.extdefs[context.extName].enums, context.extName
|
|
else
|
|
--Get enum list from core version.
|
|
if(context.options.profile ~= "core") then
|
|
return context.specData.coredefs[context.version].enums, context.version
|
|
end
|
|
|
|
local defList = {}
|
|
local targetVersion = tonumber(context.options.version)
|
|
|
|
for _, def in ipairs(context.specData.coredefs[context.version].enums) do
|
|
for ix = #def.core, 1, -1 do
|
|
if(tonumber(def.core[ix][1]) <= targetVersion) then
|
|
if(def.core[ix][2] == "core") then
|
|
table.insert(defList, def)
|
|
end
|
|
break;
|
|
end
|
|
end
|
|
end
|
|
|
|
return defList, context.version
|
|
end
|
|
end
|
|
|
|
function enumIterAction:PreProcess(context)
|
|
self:Assert(context.version or context.extName, "Enumeration iterators must go within a version or extension iterator.")
|
|
|
|
local enumList, source = GetEnumList(context)
|
|
|
|
if(not source) then
|
|
print(context.version, context.extName)
|
|
end
|
|
|
|
context.enumTable = context.specData.enumtable
|
|
self:IterateChildren(context, enumList, "enum",
|
|
function(context, enum)
|
|
if(context.enumSeen) then
|
|
context.enumSeen[enum.name] = source
|
|
end
|
|
end)
|
|
context.enumTable = nil
|
|
return true --Stops regular child processing.
|
|
end
|
|
|
|
MakeActionType("enum-iter", enumIterAction, function(self, data)
|
|
end)
|
|
|
|
conditionals["enum-iter"] = function(context)
|
|
assert(context.version or context.extName, "Cannot have an enum-iter conditional outside of a version or extension iterator.")
|
|
|
|
return #GetEnumList(context) > 0
|
|
end
|
|
|
|
----------------------------------------------
|
|
-- Func Seen Action
|
|
local funcSeenAction = {}
|
|
|
|
function funcSeenAction:PreProcess(context)
|
|
self:Assert(context.funcSeen == nil, "Cannot nest func-seen actions.")
|
|
context.funcSeen = {}
|
|
end
|
|
|
|
function funcSeenAction:PostProcess(context)
|
|
context.funcSeen = nil
|
|
end
|
|
|
|
MakeActionType("func-seen", funcSeenAction, function(self, data)
|
|
end)
|
|
|
|
|
|
-----------------------------------------------
|
|
-- Function Iterator
|
|
local funcIterAction = {}
|
|
|
|
local function GetFuncList(context)
|
|
if(context.extName) then
|
|
--Get function list for the extension.
|
|
return context.specData.extdefs[context.extName].funcs, context.extName
|
|
else
|
|
--Get function list from core version.
|
|
if(context.options.profile ~= "core") then
|
|
return context.specData.coredefs[context.version].funcs, context.version
|
|
end
|
|
|
|
local defList = {}
|
|
local targetVersion = tonumber(context.options.version)
|
|
|
|
for _, def in ipairs(context.specData.coredefs[context.version].funcs) do
|
|
for ix = #def.core, 1, -1 do
|
|
if(tonumber(def.core[ix][1]) <= targetVersion) then
|
|
if(def.core[ix][2] == "core") then
|
|
table.insert(defList, def)
|
|
end
|
|
break;
|
|
end
|
|
end
|
|
end
|
|
|
|
return defList, context.version
|
|
|
|
end
|
|
end
|
|
|
|
function funcIterAction:PreProcess(context)
|
|
self:Assert(context.version or context.extName, "Function iterators must go within a version or extension iterator.")
|
|
|
|
local funcList, source = GetFuncList(context)
|
|
|
|
self:IterateChildren(context, funcList, "func",
|
|
function(context, func)
|
|
if(context.funcSeen) then
|
|
context.funcSeen[func.name] = source
|
|
end
|
|
end)
|
|
return true --Stops regular child processing.
|
|
end
|
|
|
|
MakeActionType("func-iter", funcIterAction, function(self, data)
|
|
end)
|
|
|
|
conditionals["func-iter"] = function(context)
|
|
assert(context.version or context.extName, "Cannot have a func-iter conditional outside of a version or extension iterator.")
|
|
|
|
return #GetFuncList(context) > 0
|
|
end
|
|
|
|
conditionals["core-funcs"] = function(context)
|
|
return context.options.spec == "gl"
|
|
end
|
|
|
|
|
|
|
|
local struct = {}
|
|
|
|
function struct.BuildStructure(structure)
|
|
local actions = {}
|
|
for _, data in ipairs(structure) do
|
|
assert(actionTypes[data.type], "Unknown command type " .. data.type)
|
|
actions[#actions + 1] = actionTypes[data.type](data)
|
|
end
|
|
|
|
actions.Proc = function(basename, style, specData, spec, options)
|
|
local context = {}
|
|
context.basename = basename
|
|
context.style = style
|
|
context.specData = specData
|
|
context.spec = spec
|
|
context.options = options
|
|
|
|
context._coreExts = spec.GetCoreExts()
|
|
context._extTbl = util.InvertTable(options.extensions)
|
|
context._styles = { style }
|
|
|
|
function context:GetStyle()
|
|
return context._styles[#context._styles]
|
|
end
|
|
|
|
function context:FindStyleForFunc(funcName)
|
|
for i = #context._styles, 1, -1 do
|
|
if(context._styles[i][funcName]) then
|
|
return context._styles[i]
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
function context:PushStyle(newStyleName)
|
|
--Find the style in the stack, from top to bottom.
|
|
local ix = nil
|
|
for styleIx = #context._styles, 1, -1 do
|
|
if(context._styles[styleIx][newStyleName]) then
|
|
ix = styleIx
|
|
break;
|
|
end
|
|
end
|
|
assert(ix, "Could not find a style named " .. newStyleName)
|
|
|
|
table.insert(context._styles, context._styles[ix][newStyleName])
|
|
context.style = context._styles[#context._styles]
|
|
|
|
if(context.style._init) then
|
|
context.style._init()
|
|
end
|
|
end
|
|
|
|
function context:PopStyle()
|
|
local ret = context._styles[#context._styles]
|
|
context._styles[#context._styles] = nil
|
|
context.style = context._styles[#context._styles]
|
|
|
|
if(ret._exit) then
|
|
ret._exit()
|
|
end
|
|
return ret
|
|
end
|
|
|
|
for _, action in ipairs(actions) do
|
|
action:Process(context)
|
|
end
|
|
end
|
|
|
|
return actions
|
|
end
|
|
|
|
return struct
|