Jason McKesson avatar Jason McKesson committed 7ef35c4

Most option processing done.
Still need to handle file loading.

Comments (0)

Files changed (3)

_CmdLineOptions.lua

 local function CallProcessor(func, option, value, param, iter)
 	local status, nargs = pcall(func, value, param, iter)
 	if(not status) then
-		error("The option " .. option .. "' had an error:\n" .. nargs)
+		error("The option '" .. option .. "' had an error:\n" .. nargs)
 	end
 	
 	return nargs or CountNumOptions(iter)
 end
 
-function CmdLineOptions(cmd_line, processors, value)
+local modTbl = {}
+
+function modTbl.CmdLineOptions(cmd_line, processors, value)
 	local posArgs = {}
 	local optIx = 1
 	local numOpts = #cmd_line
 	return posArgs
 end
 
+local group = {}
 
+local function ExtractDescArray(desc)
+	if(type(desc) == "table") then
+		local descArray = {}
+		for i, val in ipairs(desc) do
+			descArray[#descArray + 1] = val
+		end
+		return descArray
+	else
+		return { desc }
+	end
+end
 
+function group:value(optName, tblName, desc, default, optional)
+	table.insert(self._doc_order, optName)
+	self._procs[optName] = {
+		desc = desc,
+		tableName = tblName,
+		default = default,
+		optional = optional,
+		 --self is the destination table, where the data goes
+		proc = function(self, param, iter)
+			assert(param, "This option needs a parameter")
+			assert(not self[tblName], "Cannot specify the option twice")
+			self[tblName] = param
+			return 1
+		end,
+		
+		document = function(self)
+			local docs = ExtractDescArray(self.desc)
+			if(self.default) then
+				docs[#docs + 1] = "Default value: " .. self.default
+			else
+				if(self.optional) then
+					docs[#docs + 1] = "This option is not required."
+				end
+			end
+			
+			return docs
+		end,
+	}
+end
 
+local function InvertTable(tbl)
+	local ret = {}
+	for i, val in ipairs(tbl) do
+		ret[val] = true
+	end
+	return ret
+end
+
+function group:enum(optName, tblName, desc, values, defaultIx, optional)
+	table.insert(self._doc_order, optName)
+	local valuesInv = InvertTable(values)
+	self._procs[optName] = {
+		desc = desc,
+		tableName = tblName,
+		values = values,
+		valuesInv = valuesInv,
+		optional = optional,
+		proc = function(self, param, iter)
+			assert(param, "This option needs a parameter")
+			assert(valuesInv[param], param .. " is not a valid value.")
+			assert(not self[tblName], "Cannot specify this option twice.");
+			self[tblName] = param
+			return 1
+		end,
+		
+		document = function(self)
+			local docs = ExtractDescArray(self.desc)
+			docs[#docs + 1] = "Allowed values:"
+			docs[#docs + 1] = table.concat(self.values, ", ")
+			if(self.default) then
+				docs[#docs + 1] = "Default value: " .. self.default
+			else
+				if(self.optional) then
+					docs[#docs + 1] = "This option is not required."
+				end
+			end
+			
+			return docs
+		end,
+	}
+	
+	if(defaultIx) then
+		self._procs[optName].default = values[defaultIx]
+	end
+end
+
+function group:array(optName, tblName, desc, modifier)
+	table.insert(self._doc_order, optName)
+	self._procs[optName] = {
+		desc = desc,
+		tableName = tblName,
+		proc = function(self, param, iter)
+			self[tblName] = self[tblName] or {}
+			
+			local bFound = false
+			for ext in iter() do
+				if(modifier) then
+					ext = modifier(ext)
+				end
+				table.insert(self[tblName], ext)
+				bFound = true
+			end
+			
+			assert(bFound, "Must provide at least one value.");
+		end,
+		
+		document = function(self)
+			local docs = ExtractDescArray(self.desc)
+			return docs
+		end,
+	}
+end
+
+function group:pos_opt(index, tblName, desc, optName, default, optional)
+	assert(not self._pos_opts[index],
+		"Positional argument " .. index .. " is already in use")
+
+	self._pos_opts[index] = {
+		desc = desc,
+		tableName = tblName,
+		optName = optName,
+		default = default,
+		optional = optional,
+	}
+end
+
+function group:AssertParse(test, msg)
+	if(not test) then
+		io.stdout:write(msg, "\n")
+		self:DisplayHelp()
+		error("")
+	end
+end
+
+function group:ProcessCmdLine(cmd_line)
+	local procs = {}
+	
+	for option, data in pairs(self._procs) do
+		procs[option] = data.proc
+	end
+	
+	local options = {}
+	
+	local status, posOpts = 
+		pcall(modTbl.CmdLineOptions, cmd_line, procs, options)
+
+	self:AssertParse(status, posOpts)
+	
+	--Apply positional arguments.
+	for ix, pos_arg in pairs(self._pos_opts) do
+		if(posOpts[ix]) then
+			options[pos_arg.tableName] = posOpts[ix]
+		elseif(pos_arg.default) then
+			options[pos_arg.tableName] = default
+		else
+			self:AssertParse(pos_arg.optional,
+				"Missing positional argument " .. pos_arg.optName)
+		end
+	end
+
+	--Apply defaults.
+	for option, data in pairs(self._procs) do
+		if(not options[data.tableName]) then
+			if(data.default) then
+				options[data.tableName] = data.default
+			else
+				self:AssertParse(data.optional,
+					"Option " .. option .. " was not specified.")
+			end
+		end
+	end
+	
+	return options, posOpts
+end
+
+function group:DisplayHelp()
+	local hFile = io.stdout
+	
+	local function MaxVal(tbl)
+		local maxval = 0
+		for ix, val in pairs(tbl) do
+			if(ix > maxval) then
+				maxval = ix
+			end
+		end
+		
+		return maxval
+	end
+
+	--Write the command-line, including positional arguments.
+	hFile:write("Command Line:")
+	local maxPosArg = MaxVal(self._pos_opts)
+	
+	for i = 1, maxPosArg do
+		if(self._pos_opts[i]) then
+			hFile:write(" <", self._pos_opts[i].optName, ">")
+		else
+			hFile:write(" <something>")
+		end
+	end
+	
+	hFile:write(" <options>\n")
+	
+	--Write each option.
+	hFile:write("Options:\n")
+	for _, option in ipairs(self._doc_order) do
+		local data = self._procs[option]
+		hFile:write("-", option, ":\n")
+		
+		local docs = data:document()
+		
+		for _, str in ipairs(docs) do
+			hFile:write("\t", str, "\n")
+		end
+	end
+end
+
+function modTbl.CreateOptionGroup()
+	local optGroup = {}
+	
+	for key, func in pairs(group) do
+		optGroup[key] = func
+	end
+	
+	optGroup._procs = {}
+	optGroup._pos_opts = {}
+	optGroup._doc_order = {}
+	
+	return optGroup
+end
+
+
+
+
+
+
+return modTbl
+--[[ The function GetOptions retrieves the list of extensions and other command-line options. It pulls data from a multitude of sources, though it begins with the command-line.
+
+It takes the following parameters:
+- An array of the command-line options.
+
+It returns a table containing the following entries:
+- spec: What specification will be generated. One of the following:
+-		gl: Uses the OpenGL spec. Default
+-		glX: Uses the glX spec.
+-		wgl: Uses the WGL "spec".
+- version: OpenGL version to export. All core features from that version and below will be exported. Will only be present when exporting "gl" loaders.
+- profile: OpenGL profile to use. Default is chosen based on GL version. One of the following:
+-		core:
+-		compatibility:
+- extensions: A table of OpenGL extensions to export.
+- outname: The base filename of the file to create.
+- style: A string containing the particular style of binding. This can be:
+-		pointer-c: The default. The functions will be stored in pointers exposed to the user. #defines will be used to rename the pointers to the core GL function names.
+-		pointer-c++: The functions will be stored in pointers, but the pointers and enumerators will be placed in the namespace "gl".
+- prefix: A prefix to be added to the names of identifiers that must be global, while avoiding name clashes. This is useful if you want to have different sets of bindings to different APIs (like a GL 3.3 and 2.1 binding). Defaults to the empty string.
+]]
+
+local cmd = require "_CmdLineOptions"
+
+local function FixupExtensionName(ext)
+	return ext
+end
+
+local parseOpts = cmd.CreateOptionGroup()
+parseOpts:enum(
+	"spec",
+	"spec",
+	"Specification to use. One of the following:",
+	{"gl", "glX", "wgl"},
+	1)
+parseOpts:value(
+	"version",
+	"version",
+	{"OpenGL version to export.", "Only use this with the 'gl' spec."},
+	nil,
+	true)
+parseOpts:enum(
+	"profile",
+	"profile",
+	{"OpenGL profile to use.", "Only use this with the 'gl' spec."},
+	{"core", "compatibility"},
+	1,
+	true)
+parseOpts:enum(
+	"style",
+	"style",
+	{"Export style."},
+	{"pointer-c", "pointer-c++"},
+	1)
+parseOpts:array(
+	"exts",
+	"extensions",
+	{"A list of extensions to export."},
+	FixupExtensionName)
+parseOpts:value(
+	"prefix",
+	"prefix",
+	{
+		"String to prefix to various globals. Set this to ",
+		"prevent interference with multiple loaders."
+	},
+	"")
+parseOpts:pos_opt(
+	1,
+	"outname",
+	"Base filename (sans extension",
+	"outname",
+	nil,
+	true)
+	
+function GetOptions(cmd_line)
+	local options, pos_args = parseOpts:ProcessCmdLine(cmd_line)
+	
+	if(options.spec == "gl") then
+		--Check version/profile.
+		parseOpts:AssertParse(options.version, "You must specify an OpenGL version to export.")
+	else
+		parseOpts:AssertParse(not options.version, "Versions cannot be specified for wgl/glX")
+		parseOpts:AssertParse(not options.profile, "Profiles cannot be specified for wgl/glX")
+	end
+
+	return options
+
+--[[
+	local options = {}
+	
+	local status, posOpts = 
+		pcall(cmd.CmdLineOptions, cmd_line, procs, options)
+		
+	AssertParse(status, posOpts)
+
+	AssertParse(posOpts[1], "You did not specify an output filename.")
+	
+	options.spec = options.spec or "gl"
+	options.func_style = options.func_style or "pointer"
+	options.language = options.language or "c"
+	
+	
+	return options
+	]]
+end
+--This file exports a function, WriteTable, which takes a file stream
+-- and a table to write.
+
+local function WriteTabs(hFile, iRecursion)
+	hFile:write(string.rep("\t", iRecursion));
+end
+
+local writeKey = {};
+
+function writeKey.string(hFile, value, iRecursion)
+	hFile:write("[\"", value, "\"]")
+end
+
+function writeKey.number(hFile, value, iRecursion)
+	hFile:write("[", value, "]")
+end
+
+local writeValue = {};
+
+function writeValue.string(hFile, value, iRecursion)
+	hFile:write("[==[", value, "]==]")
+end
+
+function writeValue.number(hFile, value, iRecursion)
+	hFile:write(value)
+end
+
+function writeValue.boolean(hFile, value, iRecursion)
+	if(value) then hFile:write("true"); else hFile:write("false"); end;
+end
+
+function writeValue.table(hFile, outTable, iRecursion)
+	if(iRecursion == nil) then iRecursion = 1; end
+	
+	hFile:write("{\n");
+	
+	local bHasArray = false;
+	local arraySize = 0;
+	
+	if(#outTable > 0) then bHasArray = true; arraySize = #outTable; end;
+	
+	for key, value in pairs(outTable) do
+		if(writeKey[type(key)] == nil) then print("Malformed table key."); return; end
+		if(writeValue[type(value)] == nil) then
+			print( string.format("Bad value in table: key: '%s' value type '%s'.", key, type(value)));
+			return;
+		end
+		
+		--If the key is not an array index, process it.
+		if((not bHasArray) or
+				(type(key) ~= "number") or
+				not((1 <= key) and (key <= arraySize))) then
+			WriteTabs(hFile, iRecursion);
+			writeKey[type(key)](hFile, key, iRecursion + 1);
+			hFile:write(" = ");
+			writeValue[type(value)](hFile, value, iRecursion + 1);
+			
+			hFile:write(",\n");
+		end
+	end
+	
+	if(bHasArray) then
+		for i, value in ipairs(outTable) do
+			WriteTabs(hFile, iRecursion);
+			writeValue[type(value)](hFile, value, iRecursion + 1);
+			hFile:write(",\n");
+		end
+	end
+
+	WriteTabs(hFile, iRecursion - 1);
+	hFile:write("}");
+end
+
+return { WriteTable = function(hFile, outTable) writeValue.table(hFile, outTable) end, }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.