parsav  cmdparse.t at [d4ecea913f]

File cmdparse.t artifact dec7841af5 part of check-in d4ecea913f


-- vim: ft=terra
return function(tbl,opts)
	opts = opts or {}
	local options = terralib.types.newstruct('options') do
		local flags = '' for _,d in pairs(tbl) do flags = flags .. d[1] end
		local flagstr = '[-' .. flags .. ']'
		local helpstr = '\n'
		options.entries = {
			{field = 'arglist', type = lib.mem.ptr(rawstring)}
		}
		local shortcases, longcases, init, verifiers = {}, {}, {}, {}
		local self = symbol(&options)
		local arg = symbol(rawstring)
		local idx = symbol(uint)
		local argv = symbol(&rawstring)
		local argc = symbol(int)
		local optstack = symbol(intptr)
		local subcmd = symbol(intptr)
		local skip = label()
		local sanitize = function(s) return s:gsub('_','-') end
		for o,desc in pairs(tbl) do
			local consume = desc.consume or 0
			local incr = desc.inc or 0
			options.entries[#options.entries + 1] = {
				field = o, type = (consume > 0) and &rawstring or
				                  (incr    > 0) and uint       or bool
			}
			if desc[1] then
				helpstr = helpstr .. string.format('    -%s --%s: %s\n',
					desc[1], sanitize(o), desc[2])
			else
				helpstr = helpstr .. string.format('       --%s: %s\n',
					sanitize(o), desc[2])
			end
		end
		for o,desc in pairs(tbl) do
			local flag = desc[1]
			local consume = desc.consume or 0
			local incr = desc.inc or 0
			init[#init + 1] = quote [self].[o] = [
				(consume > 0 and `nil) or 
				(incr    > 0 and `0  ) or false
			] end
			local ch if consume > 0 then
				ch = quote
					[self].[o] = argv+(idx+1+optstack)
					optstack = optstack + consume
				end
				verifiers[#verifiers+1] = quote
					var terminus = argv + argc
					if [self].[o] ~= nil and [self].[o] >= terminus then
						lib.bail(['missing argument for command line option ' .. sanitize(o)])
					end
				end
			elseif incr > 0 then
				ch = quote [self].[o] = [self].[o] + incr end
			else ch = quote
				[self].[o] = true
			end end
			if flag ~= nil then
				shortcases[#shortcases + 1] = quote
					case [int8]([string.byte(flag)]) then [ch] end
				end
			end
			longcases[#longcases + 1] = quote
				if lib.str.cmp([arg]+2, [sanitize(o)]) == 0 then [ch] goto [skip] end
			end
		end
		terra options:free() self.arglist:free() end
		options.methods.parse = terra([self], [argc], [argv])
			[init]
			var parseopts = true
			var [optstack] = 0
			var [subcmd] = [ opts.subcmd or 0 ]
			self.arglist = lib.mem.heapa(rawstring, argc + 1)
			var finalargc = 0
			for [idx]=1,argc do
				var [arg] = argv[idx]
				if optstack > 0 then optstack = optstack - 1 goto [skip] end
				if arg[0] == @'-' and parseopts then
					if arg[1] == @'-' then -- long option
						if arg[2] == 0 then -- last option
							parseopts = false
						else [longcases] end
					else -- short options
						var j = 1 while arg[j] ~= 0 do
							switch arg[j] do [shortcases] end
							j = j + 1
						end
					end
				else
					self.arglist.ptr[finalargc] = arg
					finalargc = finalargc + 1
					if subcmd > 0 then
						subcmd = subcmd - 1
						if subcmd == 0 then parseopts = false end
					end
				end
				::[skip]::
			end
			[verifiers]
			self.arglist.ptr[finalargc] = nil -- for lazy-ass argv compat
			if finalargc == 0 then self.arglist:free()
							  else self.arglist:resize(finalargc) end
		end
		options.helptxt = { opts = helpstr, flags = flagstr }
	end
	return options
end