mirror of
https://github.com/TangentFoxy/argparse.git
synced 2025-07-28 02:52:20 +00:00
fixed too many mixins
This commit is contained in:
264
src/argparse.lua
264
src/argparse.lua
@@ -3,8 +3,11 @@ local argparse = {}
|
|||||||
local class = require "30log"
|
local class = require "30log"
|
||||||
|
|
||||||
local State = class {
|
local State = class {
|
||||||
context = {}, -- {alias -> element}
|
opt_context = {},
|
||||||
result = {}
|
result = {},
|
||||||
|
stack = {},
|
||||||
|
invocations = {},
|
||||||
|
top_is_opt = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function State:__init(parser)
|
function State:__init(parser)
|
||||||
@@ -12,34 +15,114 @@ function State:__init(parser)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function State:switch(parser)
|
function State:switch(parser)
|
||||||
self.parser = parser
|
self.parser = parser:prepare()
|
||||||
self.parser:make_targets()
|
self.charset = parser.charset
|
||||||
|
|
||||||
for _, option in ipairs(parser.options) do
|
for _, option in ipairs(parser.options) do
|
||||||
table.insert(self.options, option)
|
table.insert(self.options, option)
|
||||||
|
|
||||||
|
for _, alias in ipairs(option.aliases) do
|
||||||
|
self.opt_context[alias] = option
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.arguments = parser.arguments
|
self.arguments = parser.arguments
|
||||||
self.commands = parser.commands
|
self.commands = parser.commands
|
||||||
end
|
self.com_context = {}
|
||||||
|
|
||||||
function Parser:make_target(element)
|
for _, command in ipairs(parser.commands) do
|
||||||
if not element.target then
|
for _, alias in ipairs(command.aliases) do
|
||||||
for _, alias in ipairs(element.aliases) do
|
self.com_context[][alias] = command
|
||||||
if alias:match "^%-%-" then
|
|
||||||
element.target = alias:sub(3)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
element.target = element.aliases[1]:match "^%-*(.*)"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function State:invoke(element)
|
||||||
|
if not self.invocatons then
|
||||||
|
|
||||||
|
|
||||||
|
function State:push(option)
|
||||||
|
if self.top_is_opt then
|
||||||
|
self:pop()
|
||||||
|
end
|
||||||
|
|
||||||
|
self:invoke(option)
|
||||||
|
|
||||||
|
if option.maxargs ~= 0 then
|
||||||
|
table.insert(self.stack, option)
|
||||||
|
self.top_is_opt = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function State:pop()
|
||||||
|
if self.top_is_opt
|
||||||
|
|
||||||
|
function State:iterargs(args)
|
||||||
|
return coroutine.wrap(function()
|
||||||
|
local handle_options = true
|
||||||
|
|
||||||
|
for _, data in ipairs(args) do
|
||||||
|
local plain = true
|
||||||
|
local first, name, option
|
||||||
|
|
||||||
|
if handle_options then
|
||||||
|
first = data:sub(1, 1)
|
||||||
|
if self.charset[first] then
|
||||||
|
if #data > 1 then
|
||||||
|
if data:sub(2, 2):match "[a-zA-Z]" then
|
||||||
|
plain = false
|
||||||
|
|
||||||
|
for i = 2, #data do
|
||||||
|
name = first .. data:sub(i, i)
|
||||||
|
option = self:assert(self.opt_context[name], "unknown option " .. name)
|
||||||
|
coroutine.yield(nil, name)
|
||||||
|
|
||||||
|
if i ~= #data and not (options.minargs == 0 and self.opt_context[first .. data:sub(i+1, i+1)]) then
|
||||||
|
coroutine.yield(data:sub(i+1), nil)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif data:sub(2, 2) == first then
|
||||||
|
if #data == 2 then
|
||||||
|
plain = false
|
||||||
|
handle_options = false
|
||||||
|
elseif data:sub(3, 3):match "[a-zA-Z]" then
|
||||||
|
plain = false
|
||||||
|
|
||||||
|
local equal = data:find "="
|
||||||
|
if equal then
|
||||||
|
name = data:sub(1, equal-1)
|
||||||
|
option = self:assert(self.opt_context[name], "unknown option " .. name)
|
||||||
|
self:assert(option.maxargs > 0, "option " .. name .. " doesn't take arguments")
|
||||||
|
|
||||||
|
coroutine.yield(nil, data:sub(1, equal-1))
|
||||||
|
coroutine.yield(data:sub(equal+1), nil)
|
||||||
|
else
|
||||||
|
coroutine.yield(nil, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if plain then
|
||||||
|
coroutine.yield(data, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
function State:parse(args)
|
function State:parse(args)
|
||||||
args = args or arg
|
for arg, opt in self:iterargs(args) do
|
||||||
|
if arg then
|
||||||
|
|
||||||
|
elseif opt then
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -204,23 +287,6 @@ function State:_pass(element, data)
|
|||||||
table.insert(passed, data)
|
table.insert(passed, data)
|
||||||
end
|
end
|
||||||
|
|
||||||
function State:_switch(command)
|
|
||||||
self._parser = command
|
|
||||||
self._arguments = command.arguments
|
|
||||||
self._commands = command.commands
|
|
||||||
|
|
||||||
for _, element in ipairs(command.elements) do
|
|
||||||
table.insert(self._all_elements, element)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, group in ipairs(command.groups) do
|
|
||||||
table.insert(self._all_groups, group)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.context = setmetatable(command.context, {__index = self.context})
|
|
||||||
self._next_arg_i = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function State:_error(...)
|
function State:_error(...)
|
||||||
return self._parser:error(...)
|
return self._parser:error(...)
|
||||||
end
|
end
|
||||||
@@ -245,7 +311,13 @@ function Declarative:__call(...)
|
|||||||
name_or_options = select(i, ...)
|
name_or_options = select(i, ...)
|
||||||
|
|
||||||
if type(name_or_options) == "string" then
|
if type(name_or_options) == "string" then
|
||||||
self:set_name(name_or_options)
|
if self.aliases then
|
||||||
|
table.insert(self.aliases, name_or_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.name then
|
||||||
|
self.name = name_or_options
|
||||||
|
end
|
||||||
elseif type(name_or_options) == "table" then
|
elseif type(name_or_options) == "table" then
|
||||||
for _, field in ipairs(self.fields) do
|
for _, field in ipairs(self.fields) do
|
||||||
if name_or_options[field] ~= nil then
|
if name_or_options[field] ~= nil then
|
||||||
@@ -258,50 +330,33 @@ function Declarative:__call(...)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
local Aliased = {}
|
|
||||||
|
|
||||||
function Aliased:set_name(name)
|
|
||||||
table.insert(self.aliases, name)
|
|
||||||
|
|
||||||
if not self.name then
|
|
||||||
self.name = name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local Named = {}
|
|
||||||
|
|
||||||
function Named:set_name(name)
|
|
||||||
self.name = name
|
|
||||||
end
|
|
||||||
|
|
||||||
local Parser = class {
|
local Parser = class {
|
||||||
arguments = {},
|
arguments = {},
|
||||||
options = {},
|
options = {},
|
||||||
commands = {},
|
commands = {},
|
||||||
charset = {"-"},
|
|
||||||
fields = {"name", "description", "target"}
|
fields = {"name", "description", "target"}
|
||||||
}:include(Declarative):include(Named)
|
}:include(Declarative)
|
||||||
|
|
||||||
local Command = Parser:extends {
|
local Command = Parser:extends {
|
||||||
aliases = {}
|
aliases = {}
|
||||||
}:include(Declarative):include(Aliased)
|
}:include(Declarative)
|
||||||
|
|
||||||
local Argument = class {
|
local Argument = class {
|
||||||
args = 1,
|
args = 1,
|
||||||
count = 1,
|
count = 1,
|
||||||
fields = {"name", "description", "target", "args", "default", "convert"}
|
fields = {"name", "description", "target", "args", "default", "convert"}
|
||||||
}:include(Declarative):include(Named)
|
}:include(Declarative)
|
||||||
|
|
||||||
local Option = class {
|
local Option = class {
|
||||||
aliases = {},
|
aliases = {},
|
||||||
args = 1,
|
args = 1,
|
||||||
count = "?",
|
count = "?",
|
||||||
fields = {"name", "aliases", "description", "target", "args", "count", "default", "convert"}
|
fields = {"name", "aliases", "description", "target", "args", "count", "default", "convert"}
|
||||||
}:include(Declarative):include(Aliased)
|
}:include(Declarative)
|
||||||
|
|
||||||
local Flag = Option:extends {
|
local Flag = Option:extends {
|
||||||
args = 0
|
args = 0
|
||||||
}:include(Declarative):include(Aliased)
|
}:include(Declarative)
|
||||||
|
|
||||||
function Parser:argument(...)
|
function Parser:argument(...)
|
||||||
local argument = Argument(...)
|
local argument = Argument(...)
|
||||||
@@ -337,23 +392,26 @@ function Parser:assert(assertion, ...)
|
|||||||
return assertion or self:error(...)
|
return assertion or self:error(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Parser:get_charset()
|
function Parser:make_charset()
|
||||||
for _, command in ipairs(self.commands) do
|
if not self.charset then
|
||||||
for char in command:get_charset() do
|
self.charset = {}
|
||||||
self.charset[char] = true
|
|
||||||
|
for _, command in ipairs(self.commands) do
|
||||||
|
command:make_charset()
|
||||||
|
|
||||||
|
for char in pairs(command.charset) do
|
||||||
|
self.charset[char] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, option in ipairs(self.options) do
|
||||||
|
for _, alias in ipairs(option.aliases) do
|
||||||
|
self.charset[alias:sub(1, 1)] = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, option in ipairs(self.options) do
|
|
||||||
for _, alias in ipairs(option.aliases) do
|
|
||||||
self.charset[alias:sub(1, 1)] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return self.charset
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- to be called from State
|
|
||||||
function Parser:make_targets()
|
function Parser:make_targets()
|
||||||
for _, option in ipairs(self.options) do
|
for _, option in ipairs(self.options) do
|
||||||
if not option.target then
|
if not option.target then
|
||||||
@@ -377,71 +435,23 @@ function Parser:make_targets()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Parser:parse(args)
|
function self:make_command_names()
|
||||||
self:get_charset()
|
for _, command in ipairs(self.commands) do
|
||||||
return State(self):parse(args)
|
command.name = self.name .. " " .. command.name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Parser:prepare()
|
||||||
|
self:make_charset()
|
||||||
|
self:make_targets()
|
||||||
|
self:make_command_names()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
function Parser:parse(args)
|
function Parser:parse(args)
|
||||||
args = args or arg
|
args = args or arg
|
||||||
self.name = self.name or args[0]
|
self.name = self.name or args[0]
|
||||||
|
return State(self):parse(args)
|
||||||
local state = State(self)
|
|
||||||
|
|
||||||
local handle_options = true
|
|
||||||
for _, data in ipairs(args) do
|
|
||||||
local plain = true
|
|
||||||
|
|
||||||
if handle_options then
|
|
||||||
if data:sub(1, 1) == "-" then
|
|
||||||
if #data > 1 then
|
|
||||||
if data:sub(2, 2):match "[a-zA-Z]" then
|
|
||||||
plain = false
|
|
||||||
|
|
||||||
local name, element
|
|
||||||
for i = 2, #data do
|
|
||||||
name = "-" .. data:sub(i, i)
|
|
||||||
element = self:assert(state.context[name], "unknown option " .. name)
|
|
||||||
state:handle_option(name)
|
|
||||||
|
|
||||||
if i ~= #data and not (element.minargs == 0 and state.context["-" .. data:sub(i+1, i+1)]) then
|
|
||||||
state:handle_argument(data:sub(i+1))
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif data:sub(2, 2) == "-" then
|
|
||||||
if #data == 2 then
|
|
||||||
plain = false
|
|
||||||
handle_options = false
|
|
||||||
elseif data:sub(3, 3):match "[a-zA-Z]" then
|
|
||||||
plain = false
|
|
||||||
|
|
||||||
local equal = data:find "="
|
|
||||||
if equal then
|
|
||||||
local name = data:sub(1, equal-1)
|
|
||||||
local element = self:assert(state.context[name], "unknown option " .. name)
|
|
||||||
self:assert(element.maxargs > 0, "option " .. name .. " doesn't take arguments")
|
|
||||||
|
|
||||||
state:handle_option(data:sub(1, equal-1))
|
|
||||||
state:handle_argument(data:sub(equal+1))
|
|
||||||
else
|
|
||||||
state:handle_option(data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if plain then
|
|
||||||
state:handle_argument(data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local result = state:get_result()
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
argparse.parser = Parser
|
argparse.parser = Parser
|
||||||
|
Reference in New Issue
Block a user