Files
TransPyC/includes/argparse.py
2026-07-18 19:25:40 +08:00

459 lines
17 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import string
import memhub
import stdio
import hashtable
# ============================================================
# argparse.py — 命令行参数解析库Python argparse 风格)
#
# 用法:
# argparse._mbuddy = mb
# parser = argparse.ArgumentParser("myprog", "A test", pool=mb)
# parser.add_argument("--input", "-i", arg_type=argparse.STRING,
# required=True, help="input file")
# parser.add_argument("--count", "-c", arg_type=argparse.INT,
# default=0, help="repeat count")
# parser.add_argument("--verbose", "-v", action=argparse.STORE_TRUE,
# help="verbose mode")
# parser.add_argument("output", arg_type=argparse.STRING,
# help="output path")
# args = parser.parse_args(argc, argv)
# if args is not None:
# inp: str = args.get_str("input")
# cnt: INT = args.get_int("count")
# verb: bool = args.get_bool("verbose")
# out: str = args.get_str("output")
# argparse.release(args)
# ============================================================
# 类型常量
STRING: t.CDefine = 0
INT: t.CDefine = 1
BOOL: t.CDefine = 2
# 动作常量
STORE: t.CDefine = 0
STORE_TRUE: t.CDefine = 1
STORE_FALSE: t.CDefine = 2
COUNT: t.CDefine = 3
# 限制
MAX_ARGS: t.CDefine = 32
# 模块级 mbuddy 指针(由用户在 main 中赋值)
_mbuddy: memhub.MemBuddy | t.CPtr
# ============================================================
# Argument — 参数定义结构体
# ============================================================
class Argument:
name: str # "--input" 或 "output"(位置参数)
short_name: str # "-i" 或 None
help_text: str # 帮助文本
arg_type: INT # STRING, INT, BOOL
action: INT # STORE, STORE_TRUE, STORE_FALSE, COUNT
default_int: INT # INT 类型默认值
default_str: str # STRING 类型默认值
required: bool # 是否必须
is_positional: bool # 是否位置参数
dest: str # 查找键名("--input" → "input"
# ============================================================
# ParsedArgs — 解析结果
# ============================================================
class ParsedArgs:
_ht: hashtable.HashTable | t.CPtr
__mbuddy__: memhub.MemBuddy | t.CPtr
def __new__(self, mb: memhub.MemBuddy | t.CPtr) -> t.CPtr:
return mb.alloc(16)
def __init__(self, mb: memhub.MemBuddy | t.CPtr):
self.__mbuddy__ = mb
self._ht = hashtable.HashTable(mb)
def get_str(self, name: str) -> str:
"""获取字符串值。键不存在返回 None。"""
if self._ht.__contains__(name) == 0: return None
return self._ht[name]
def get_int(self, name: str) -> INT:
"""获取整数值。键不存在返回 0。"""
if self._ht.__contains__(name) == 0: return 0
ptr: t.CPtr = self._ht[name]
return c.Deref(t.CInt(t.CUInt64T(ptr), t.CPtr))
def get_bool(self, name: str) -> INT:
"""获取布尔值(返回 1 或 0。键不存在返回 0。"""
if self._ht.__contains__(name) == 0: return 0
ptr: t.CPtr = self._ht[name]
return 1 if c.Deref(t.CInt(t.CUInt64T(ptr), t.CPtr)) != 0 else 0
def has(self, name: str) -> bool:
"""检查键是否存在。"""
return self._ht.__contains__(name) != 0
def _set_str(self, name: str, val: str):
self._ht.set_str(name, val)
def _set_int(self, name: str, val: INT):
self._ht.set_int(name, val)
def _set_bool(self, name: str, val: bool):
if val:
self._ht.set_int(name, 1)
else:
self._ht.set_int(name, 0)
# ============================================================
# ArgumentParser — 命令行参数解析器
# ============================================================
class ArgumentParser:
_prog: str
_description: str
_args: Argument | t.CPtr
_arg_count: INT
__mbuddy__: memhub.MemBuddy | t.CPtr
def __new__(self, prog: str = "", description: str = "",
pool: memhub.MemBuddy | t.CPtr = None) -> t.CPtr:
mb: memhub.MemBuddy | t.CPtr = pool
if mb is None: mb = _mbuddy
return mb.alloc(40)
def __init__(self, prog: str = "", description: str = "",
pool: memhub.MemBuddy | t.CPtr = None):
mb: memhub.MemBuddy | t.CPtr = pool
if mb is None: mb = _mbuddy
self.__mbuddy__ = mb
self._prog = prog
self._description = description
self._arg_count = 0
self._args = mb.alloc(MAX_ARGS * Argument.__sizeof__())
def add_argument(self, name: str = "", short: str = None,
arg_type: INT = 0, default: INT = 0,
default_str: str = None, required: bool = False,
action: INT = 0, help: str = None):
"""注册一个命令行参数。
Args:
name: 参数名("--input""output"
short: 短选项("-i"),可选
arg_type: 类型STRING/INT/BOOL
default: INT 默认值
default_str: STRING 默认值
required: 是否必须
action: 动作STORE/STORE_TRUE/STORE_FALSE/COUNT
help: 帮助文本
"""
if self._arg_count >= MAX_ARGS: return
idx: INT = self._arg_count
arg: Argument | t.CPtr = t.CPtr(t.CUInt64T(self._args) + idx * Argument.__sizeof__())
arg.name = name
arg.short_name = short
arg.help_text = help
arg.arg_type = arg_type
arg.action = action
arg.default_int = default
arg.default_str = default_str
arg.required = required
# 判断是否位置参数
if name is not None and name[0] != '-':
arg.is_positional = True
arg.dest = name
else:
arg.is_positional = False
if name is not None and name[0] == '-' and name[1] == '-':
arg.dest = name + 2
else:
arg.dest = name + 1
self._arg_count = idx + 1
def _get_arg(self, idx: INT) -> Argument | t.CPtr:
return t.CPtr(t.CUInt64T(self._args) + idx * Argument.__sizeof__())
def _find_long(self, token: str) -> INT:
"""精确匹配长选项名"""
i: INT = 0
while i < self._arg_count:
arg: Argument | t.CPtr = self._get_arg(i)
if string.strcmp(arg.name, token) == 0:
return i
i += 1
return -1
def _find_long_prefix(self, token: str) -> INT:
"""匹配 --name=value 中的 name 前缀"""
i: INT = 0
while i < self._arg_count:
arg: Argument | t.CPtr = self._get_arg(i)
name_len: INT = string.strlen(arg.name)
if string.strncmp(token, arg.name, name_len) == 0:
if token[name_len] == '=':
return i
i += 1
return -1
def _find_short(self, token: str) -> INT:
i: INT = 0
while i < self._arg_count:
arg: Argument | t.CPtr = self._get_arg(i)
if arg.short_name is not None:
if string.strcmp(arg.short_name, token) == 0:
return i
i += 1
return -1
def _find_positional(self, pos_idx: INT) -> INT:
count: INT = 0
i: INT = 0
while i < self._arg_count:
arg: Argument | t.CPtr = self._get_arg(i)
if arg.is_positional:
if count == pos_idx:
return i
count += 1
i += 1
return -1
def _store_value(self, args: ParsedArgs | t.CPtr,
arg: Argument | t.CPtr, value: str):
if arg.arg_type == INT:
args._set_int(arg.dest, string.atoi(value))
elif arg.arg_type == STRING:
args._set_str(arg.dest, value)
elif arg.arg_type == BOOL:
v: INT = string.atoi(value)
if v != 0:
args._set_bool(arg.dest, True)
else:
args._set_bool(arg.dest, False)
def _apply_action(self, args: ParsedArgs | t.CPtr, arg: Argument | t.CPtr):
if arg.action == STORE_TRUE:
args._set_bool(arg.dest, True)
elif arg.action == STORE_FALSE:
args._set_bool(arg.dest, False)
elif arg.action == COUNT:
cur: INT = args.get_int(arg.dest)
args._set_int(arg.dest, cur + 1)
def parse_args(self, argc: INT, argv: str | t.CPtr) -> ParsedArgs | t.CPtr:
"""解析命令行参数。
Args:
argc: 参数个数
argv: 参数数组char**argv[0] 为程序名)
Returns:
ParsedArgs 指针,失败返回 None
"""
if self.__mbuddy__ is None: return None
args: ParsedArgs | t.CPtr = ParsedArgs(self.__mbuddy__)
if args is None: return None
# 初始化默认值
i: INT = 0
while i < self._arg_count:
arg: Argument | t.CPtr = self._get_arg(i)
if arg.action == STORE:
if arg.arg_type == INT:
args._set_int(arg.dest, arg.default_int)
elif arg.arg_type == STRING:
args._set_str(arg.dest, arg.default_str)
elif arg.action == STORE_TRUE:
args._set_bool(arg.dest, False)
elif arg.action == STORE_FALSE:
args._set_bool(arg.dest, True)
elif arg.action == COUNT:
args._set_int(arg.dest, 0)
i += 1
# 解析 argv
idx: INT = 1
pos_idx: INT = 0
token: str = None
found: INT = 0
arg: Argument | t.CPtr = None
value: str = None
entry: str | t.CPtr = None
name_len: INT = 0
while idx < argc:
entry = t.CPtr(t.CUInt64T(argv) + idx * 8)
token = entry[0]
if token is None: break
# "--" 结束符:后续全部为位置参数
if token[0] == '-' and token[1] == '-' and token[2] == 0:
idx += 1
while idx < argc:
entry = t.CPtr(t.CUInt64T(argv) + idx * 8)
token = entry[0]
found = self._find_positional(pos_idx)
if found < 0:
stdio.printf("error: unexpected argument '%s'\n", token)
release(args)
return None
arg = self._get_arg(found)
self._store_value(args, arg, token)
pos_idx += 1
idx += 1
break
# 长选项 --name 或 --name=value
if token[0] == '-' and token[1] == '-':
# 检查是否含 =
has_eq: bool = False
p: str = token
while p[0] != 0:
if p[0] == '=':
has_eq = True
break
p += 1
if has_eq:
# --name=value
found = self._find_long_prefix(token)
if found < 0:
stdio.printf(
"error: unrecognized argument '%s'\n", token)
release(args)
return None
arg = self._get_arg(found)
# 提取 = 后的值
arg_name_len: INT = string.strlen(arg.name)
value = token + arg_name_len + 1
self._store_value(args, arg, value)
idx += 1
else:
# --name值在下一个 argv 或标志)
found = self._find_long(token)
if found < 0:
stdio.printf(
"error: unrecognized argument '%s'\n", token)
release(args)
return None
arg = self._get_arg(found)
if arg.action == STORE:
if idx + 1 >= argc:
stdio.printf(
"error: %s requires a value\n", arg.name)
release(args)
return None
entry = t.CPtr(t.CUInt64T(argv) + (idx + 1) * 8)
value = entry[0]
self._store_value(args, arg, value)
idx += 2
else:
self._apply_action(args, arg)
idx += 1
# 短选项 -x
elif token[0] == '-':
found = self._find_short(token)
if found < 0:
stdio.printf(
"error: unrecognized argument '%s'\n", token)
release(args)
return None
arg = self._get_arg(found)
if arg.action == STORE:
if idx + 1 >= argc:
stdio.printf(
"error: %s requires a value\n", arg.name)
release(args)
return None
entry = t.CPtr(t.CUInt64T(argv) + (idx + 1) * 8)
value = entry[0]
self._store_value(args, arg, value)
idx += 2
else:
self._apply_action(args, arg)
idx += 1
# 位置参数
else:
found = self._find_positional(pos_idx)
if found < 0:
stdio.printf(
"error: unexpected argument '%s'\n", token)
release(args)
return None
arg = self._get_arg(found)
self._store_value(args, arg, token)
pos_idx += 1
idx += 1
# 检查必须参数
i = 0
while i < self._arg_count:
arg = self._get_arg(i)
if arg.required:
if not args.has(arg.dest):
stdio.printf(
"error: missing required argument '%s'\n",
arg.name)
release(args)
return None
i += 1
return args
def print_help(self):
"""打印帮助信息"""
stdio.printf("usage: %s [options]\n", self._prog)
if self._description is not None and self._description[0] != 0:
stdio.printf("\n%s\n", self._description)
stdio.printf("\npositional arguments:\n")
i: INT = 0
while i < self._arg_count:
arg: Argument | t.CPtr = self._get_arg(i)
if arg.is_positional:
ht: str = arg.help_text
if ht is None: ht = ""
stdio.printf(" %-20s %s\n", arg.name, ht)
i += 1
stdio.printf("\noptional arguments:\n")
i = 0
while i < self._arg_count:
arg = self._get_arg(i)
if not arg.is_positional:
ht: str = arg.help_text
if ht is None: ht = ""
if arg.short_name is not None:
stdio.printf(" %s, %-18s %s\n",
arg.short_name, arg.name, ht)
else:
stdio.printf(" %-22s %s\n", arg.name, ht)
i += 1
def print_usage(self):
"""打印用法简述"""
stdio.printf("usage: %s [options]\n", self._prog)
# ============================================================
# release — 释放 ParsedArgs 及其内部资源
# ============================================================
def release(args: ParsedArgs | t.CPtr):
"""释放 ParsedArgs 对象及其内部 HashTable。"""
if args is None: return
if args.__mbuddy__ is None: return
if args._ht is not None:
if args._ht.__slots__ is not None:
args.__mbuddy__.free(args._ht.__slots__)
args.__mbuddy__.free(t.CVoid(t.CUInt64T(args._ht), t.CPtr))
args.__mbuddy__.free(t.CVoid(t.CUInt64T(args), t.CPtr))