Files
Viper_VRTE/argparse.vp
T
TermiNexus 27121f56a7 refactor(includes): R12 下标 while 循环改 for range,R13 c.Set 迁移为赋值
R12(wiki/新规范 L27-39):把「i: int = 0 与 while i < N: … i += 1」形式的下标循环改写为「for i in range(N):」。保守判定:仅当条件为裸 NAME < EXPR、循环体无 continue、NAME 在体内无其它赋值、无嵌套重绑定、循环后同层不再引用 NAME 时才改写。共 269 处 / 48 文件。

R13:c.Set 是正在退化的路径——c.Set(*p, v) 期望写作 *p = v,c.Set(a, b) 等于 a = b。本次把 includes 内 45 处 c.Set 全部迁移为赋值,includes 已无 c.Set 调用。

验证:bootstrap 三重重编 SUCCESS(无 vast_c 解析回退);全量 77/77 顺序回归通过;ZlibTest 内部断言 79 passed / 0 failed。
2026-10-05 15:33:52 +08:00

445 lines
16 KiB
Plaintext
Raw 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
import string
import memhub
import stdio
import hashtable
# ============================================================
# argparse.vp — Command line argument parsing library, inspired by Python argparse
#
# Usage:
# 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)
# ============================================================
# Type constants
define STRING = 0
define INT = 1
define BOOL = 2
# Action constants
define STORE = 0
define STORE_TRUE = 1
define STORE_FALSE = 2
define COUNT = 3
# Limits
define MAX_ARGS = 32
# Module-level membuddy pointer (assigned inside main by user)
_mbuddy: memhub.MemBuddy*
# ============================================================
# Argument — Argument definition structure
# ============================================================
class Argument:
name: str # "--input" or positional "output"
short_name: str # "-i" or null
help_text: str # Help description text
arg_type: int # STRING, INT, BOOL
action: int # STORE, STORE_TRUE, STORE_FALSE, COUNT
default_int: int # Default value for integer types
default_str: str # Default value for string types
required: bool # Whether this argument is mandatory
is_positional: bool # True for positional parameters
dest: str # Lookup key ("--input" → "input")
# ============================================================
# ParsedArgs — Parsing result container
# ============================================================
class ParsedArgs:
_ht: hashtable.HashTable*
__mbuddy__: memhub.MemBuddy*
def __new__(self, mb: memhub.MemBuddy*) -> t.CPtr:
return mb.alloc(16)
def __init__(self, mb: memhub.MemBuddy*):
self.__mbuddy__ = mb
self._ht = hashtable.HashTable(mb)
def get_str(self, name: str) -> str:
"""Fetch string value. Returns None if key not found."""
if self._ht.__contains__(name) == 0:
return None
r: str = self._ht[name]
return r
def get_int(self, name: str) -> int:
"""Fetch integer value. Returns 0 if key not found."""
if self._ht.__contains__(name) == 0:
return 0
ptr: t.CPtr = self._ht[name]
return c.Deref(int(uint64(ptr), t.CPtr))
def get_bool(self, name: str) -> bool:
"""Fetch boolean value. Returns false if key not found."""
if self._ht.__contains__(name) == 0:
return False
ptr: t.CPtr = self._ht[name]
return c.Deref(int(uint64(ptr), t.CPtr)) != 0
def has(self, name: str) -> bool:
"""Check whether specified key exists."""
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 — Command line argument parser
# ============================================================
class ArgumentParser:
_prog: str
_description: str
_args: Argument*
_arg_count: int
__mbuddy__: memhub.MemBuddy*
def __new__(self, prog: str = "", description: str = "",
pool: memhub.MemBuddy* = None) -> t.CPtr:
mb: memhub.MemBuddy* = pool
if mb is None:
mb = _mbuddy
ret: t.CPtr = mb.alloc(40)
return ret
def __init__(self, prog: str = "", description: str = "",
pool: memhub.MemBuddy* = None):
mb: memhub.MemBuddy* = 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):
"""Register a command line argument.
Args:
name: Argument name ("--input" or positional "output")
short: Short option ("-i"), optional
arg_type: Data type (STRING/INT/BOOL)
default: Default integer value
default_str: Default string value
required: Mandatory flag
action: Action mode (STORE/STORE_TRUE/STORE_FALSE/COUNT)
help: Help description text
"""
if self._arg_count >= MAX_ARGS:
return
idx: int = self._arg_count
arg: Argument* = t.CPtr(uint64(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
# Determine positional flag
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*:
return t.CPtr(uint64(self._args) + idx * Argument.__sizeof__())
def _find_long(self, token: str) -> int:
"""Exact match for long option name"""
for i in range(self._arg_count):
arg: Argument* = self._get_arg(i)
if string.strcmp(arg.name, token) == 0:
return i
return -1
def _find_long_prefix(self, token: str) -> int:
"""Match prefix for --name=value syntax"""
for i in range(self._arg_count):
arg: Argument* = 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
return -1
def _find_short(self, token: str) -> int:
for i in range(self._arg_count):
arg: Argument* = self._get_arg(i)
if arg.short_name is not None:
if string.strcmp(arg.short_name, token) == 0:
return i
return -1
def _find_positional(self, pos_idx: int) -> int:
count: int = 0
for i in range(self._arg_count):
arg: Argument* = self._get_arg(i)
if arg.is_positional:
if count == pos_idx:
return i
count += 1
return -1
def _store_value(self, args: ParsedArgs*,
arg: Argument*, 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)
args._set_bool(arg.dest, v != 0)
def _apply_action(self, args: ParsedArgs*, arg: Argument*):
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*) -> ParsedArgs*:
"""Parse command line arguments.
Args:
argc: Argument count
argv: Argument array (char**, argv[0] = program name)
Returns:
Pointer to ParsedArgs; returns None on failure
"""
if self.__mbuddy__ is None:
return None
args: ParsedArgs* = ParsedArgs(self.__mbuddy__)
if args is None:
return None
# Initialize default values
i: int = 0
while i < self._arg_count:
arg: Argument* = 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
# Traverse argv
idx: int = 1
pos_idx: int = 0
token: str = None
found: int = 0
arg: Argument* = None
value: str = None
# argv 是 char**(`str | t.CPtr`),argv[idx] 直接给出该位置的 char*。
# 不要写成 `str*(uint64(argv) + idx*8)`:那样右值是 i8**,TPC 的赋值路径
# 会对"指针的指针"右值再解引用一次(多 load 一层)→ 取到野指针。
while idx < argc:
token = argv[idx]
if token is None:
break
# "--" terminator: all subsequent tokens become positional arguments
if token[0] == '-' and token[1] == '-' and token[2] == 0:
idx += 1
while idx < argc:
token = argv[idx]
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
# Long option --name or --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 syntax
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 with value in next token or flag action
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
value = argv[idx + 1]
self._store_value(args, arg, value)
idx += 2
else:
self._apply_action(args, arg)
idx += 1
# Short option -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
value = argv[idx + 1]
self._store_value(args, arg, value)
idx += 2
else:
self._apply_action(args, arg)
idx += 1
# Positional argument
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
# Validate required arguments
for i in range(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
return args
def print_help(self):
"""Print complete help message"""
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* = 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")
for i in range(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)
def print_usage(self):
"""Print brief usage summary"""
stdio.printf("usage: %s [options]\n", self._prog)
# ============================================================
# release — Release ParsedArgs and all internal resources
# ============================================================
def release(args: rawptr[ParsedArgs]):
"""Free ParsedArgs object and its inner HashTable resources."""
if args is None: return
if args.__mbuddy__ is None: return
if args._ht is not None:
# 用临时变量拆分链式属性访问,避免 args._ht.__slots__ 跨模块类型推断失败
# 注意:类型注解必须用 hashtable.HashTable(带模块前缀),
# 否则编译器无法解析 HashTable 类型,导致 ht.__slots__ 属性访问失败
ht: hashtable.HashTable* = args._ht
if ht.__slots__ is not None:
args.__mbuddy__.free(ht.__slots__)
args.__mbuddy__.free(ht)
args.__mbuddy__.free(args)