按规范「注释只讲解作用,不掺杂历史原因,尽量英文」:把 includes 下 75 个 .vp 的中文 # 注释与三引号 docstring 改写为英文,每个注释块压到 2 行以内。 保留要点:作用、约束、危险警告(不得/必须/注意)、ABI 与类型约定(如 BYTEPTR | t.CPtr = i8** 的跨模块坍缩坑)、位宽/长度约定、fail-fast 要求、内存与句柄所有权。 未动:文件头注释(规范豁免,仍是中文)、运行时中文字符串常量(printf / fatal_error / MlirFail 等用户可见消息,属代码字面量)。 验证:独立脚本比对 HEAD 与工作区的代码骨架(剥离 # 注释与三引号 docstring 后的非空行)——75 个文件逐字节一致;行尾 CRLF/LF 未被改动;全文剩余中文仅 547 行头注释、0 条行尾注释。bootstrap 三重重编 SUCCESS;全量 77/77 顺序回归通过。
316 lines
9.2 KiB
Plaintext
316 lines
9.2 KiB
Plaintext
import t, c
|
|
from stdint import *
|
|
import string
|
|
import memhub
|
|
import platmacro
|
|
|
|
|
|
# Module-level global MBuddy pointer (initialized by the user in main)
|
|
_mbuddy: memhub.MemBuddy*
|
|
|
|
|
|
# =============================================================================
|
|
# Path Separators
|
|
# =============================================================================
|
|
|
|
def sep() -> char:
|
|
"""Return the platform path separator (Windows: \\, POSIX: /)"""
|
|
if platmacro.IS_WINDOWS:
|
|
return '\\'
|
|
return '/'
|
|
|
|
|
|
def _is_sep(ch: char) -> bool:
|
|
"""Return True if ch is a path separator (/ or \\, cross-platform)"""
|
|
return ch == '/' or ch == '\\'
|
|
|
|
|
|
# =============================================================================
|
|
# Internal Helpers
|
|
# =============================================================================
|
|
|
|
def _dup_str(s: str) -> str:
|
|
"""Duplicate a string; memory allocated via the global MBuddy"""
|
|
if s == None: return None
|
|
length: sizet = len(s)
|
|
buf: bytes = _mbuddy.alloc(length + 1)
|
|
if buf == None: return None
|
|
i: sizet = 0
|
|
for ch in s:
|
|
buf[i] = ch
|
|
i += 1
|
|
buf[length] = 0
|
|
return str(buf)
|
|
|
|
|
|
def _substr(s: str, start: sizet, end: sizet) -> str:
|
|
"""Extract substring s[start:end]; memory allocated via the global MBuddy"""
|
|
if s == None: return None
|
|
if start >= end: return _dup_str("")
|
|
result_len: sizet = end - start
|
|
buf: bytes = _mbuddy.alloc(result_len + 1)
|
|
if buf == None: return None
|
|
i: sizet = 0
|
|
k: sizet = start
|
|
while k < end:
|
|
buf[i] = s[k]
|
|
i += 1
|
|
k += 1
|
|
buf[i] = 0
|
|
return str(buf)
|
|
|
|
|
|
# =============================================================================
|
|
# Path Predicates
|
|
# =============================================================================
|
|
|
|
def isabs(path: str) -> bool:
|
|
"""Return True if path is absolute.
|
|
POSIX: starts with /. Windows: starts with \\ (UNC) or X:\\ / X:/ (drive)."""
|
|
if path == None: return False
|
|
if path[0] == 0: return False
|
|
if path[0] == '/' or path[0] == '\\': return True
|
|
if platmacro.IS_WINDOWS:
|
|
# Check for X:\ or X:/ pattern
|
|
c0: char = path[0]
|
|
if (c0 >= 'A' and c0 <= 'Z') or (c0 >= 'a' and c0 <= 'z'):
|
|
if path[1] == ':':
|
|
if path[2] == '\\' or path[2] == '/':
|
|
return True
|
|
return False
|
|
|
|
|
|
# =============================================================================
|
|
# Path Join and Split
|
|
# =============================================================================
|
|
|
|
def join(path1: str, path2: str) -> str:
|
|
"""Join two paths. If path2 is absolute, return a copy of path2."""
|
|
if path1 == None or path1[0] == 0:
|
|
return _dup_str(path2)
|
|
if path2 == None or path2[0] == 0:
|
|
return _dup_str(path1)
|
|
if isabs(path2):
|
|
return _dup_str(path2)
|
|
|
|
len1: sizet = len(path1)
|
|
len2: sizet = len(path2)
|
|
|
|
# Check whether path1 ends with a separator
|
|
need_sep: bool = True
|
|
if _is_sep(path1[len1 - 1]):
|
|
need_sep = False
|
|
|
|
total: sizet = len1 + len2 + 1
|
|
if need_sep:
|
|
total += 1
|
|
|
|
buf: bytes = _mbuddy.alloc(total)
|
|
if buf == None: return None
|
|
|
|
# Copy path1
|
|
i: sizet = 0
|
|
for ch in path1:
|
|
buf[i] = ch
|
|
i += 1
|
|
|
|
# Add separator
|
|
if need_sep:
|
|
buf[i] = sep()
|
|
i += 1
|
|
|
|
# Copy path2
|
|
for ch in path2:
|
|
buf[i] = ch
|
|
i += 1
|
|
|
|
buf[i] = 0
|
|
return str(buf)
|
|
|
|
|
|
def basename(path: str) -> str:
|
|
"""Return the filename part of path (content after the last separator)"""
|
|
if path == None: return None
|
|
length: sizet = len(path)
|
|
if length == 0: return _dup_str("")
|
|
|
|
# Scan backward for a separator
|
|
i: sizet = length
|
|
while i > 0:
|
|
i -= 1
|
|
if _is_sep(path[i]):
|
|
return _substr(path, i + 1, length)
|
|
|
|
# No separator; return a copy of the whole path
|
|
return _dup_str(path)
|
|
|
|
|
|
def dirname(path: str) -> str:
|
|
"""Return the directory part of path (content before the last separator)"""
|
|
if path == None: return None
|
|
length: sizet = len(path)
|
|
if length == 0: return _dup_str("")
|
|
|
|
# Scan backward for a separator
|
|
i: sizet = length
|
|
while i > 0:
|
|
i -= 1
|
|
if _is_sep(path[i]):
|
|
if i == 0:
|
|
# Path starts with a separator; return the root directory
|
|
return _dup_str("/")
|
|
return _substr(path, 0, i)
|
|
|
|
# No separator; return an empty string
|
|
return _dup_str("")
|
|
|
|
|
|
# =============================================================================
|
|
# Extension Handling
|
|
# =============================================================================
|
|
|
|
def splitext_name(path: str) -> str:
|
|
"""Return the stem of path with the extension removed.
|
|
The extension starts at the last '.' (not the first char of the last component)."""
|
|
if path == None: return None
|
|
length: sizet = len(path)
|
|
if length == 0: return _dup_str("")
|
|
|
|
# Scan backward for '.'; stop at a separator
|
|
i: sizet = length
|
|
while i > 0:
|
|
i -= 1
|
|
if path[i] == '.':
|
|
return _substr(path, 0, i)
|
|
if _is_sep(path[i]):
|
|
break
|
|
|
|
# No extension; return a copy of the whole path
|
|
return _dup_str(path)
|
|
|
|
|
|
def splitext_ext(path: str) -> str:
|
|
"""Return the extension part of path (including '.'). Return an empty string if none."""
|
|
if path == None: return None
|
|
length: sizet = len(path)
|
|
if length == 0: return _dup_str("")
|
|
|
|
# Scan backward for '.'; stop at a separator
|
|
i: sizet = length
|
|
while i > 0:
|
|
i -= 1
|
|
if path[i] == '.':
|
|
return _substr(path, i, length)
|
|
if _is_sep(path[i]):
|
|
break
|
|
|
|
# No extension; return an empty string
|
|
return _dup_str("")
|
|
|
|
|
|
# =============================================================================
|
|
# Path Normalization
|
|
# =============================================================================
|
|
|
|
def normpath(path: str) -> str:
|
|
"""Normalize path: unify separators, remove redundant . and .. components, merge consecutive separators.
|
|
Note: does not resolve symlinks or call system APIs."""
|
|
if path == None: return None
|
|
length: sizet = len(path)
|
|
if length == 0: return _dup_str(".")
|
|
|
|
# Allocate output buffer (worst case: same length + leading /)
|
|
buf: bytes = _mbuddy.alloc(length + 2)
|
|
if buf == None: return None
|
|
|
|
out_pos: sizet = 0
|
|
i: sizet = 0
|
|
s: char = sep()
|
|
|
|
# Handle the absolute path prefix
|
|
is_absolute: bool = False
|
|
if _is_sep(path[0]):
|
|
is_absolute = True
|
|
buf[out_pos] = s
|
|
out_pos += 1
|
|
i = 1
|
|
|
|
# Windows drive prefix (X:\ or X:/)
|
|
drive_prefix_len: sizet = 0
|
|
if platmacro.IS_WINDOWS:
|
|
if length >= 3:
|
|
c0: char = path[0]
|
|
if (c0 >= 'A' and c0 <= 'Z') or (c0 >= 'a' and c0 <= 'z'):
|
|
if path[1] == ':':
|
|
if _is_sep(path[2]):
|
|
buf[out_pos] = path[0]
|
|
out_pos += 1
|
|
buf[out_pos] = ':'
|
|
out_pos += 1
|
|
buf[out_pos] = s
|
|
out_pos += 1
|
|
drive_prefix_len = 3
|
|
i = 3
|
|
is_absolute = True
|
|
|
|
# Process component by component
|
|
while i < length:
|
|
# Skip consecutive separators
|
|
while i < length and _is_sep(path[i]):
|
|
i += 1
|
|
if i >= length:
|
|
break
|
|
|
|
# Extract component
|
|
comp_start: sizet = i
|
|
while i < length and not _is_sep(path[i]):
|
|
i += 1
|
|
comp_len: sizet = i - comp_start
|
|
|
|
# Handle "." component: skip
|
|
if comp_len == 1 and path[comp_start] == '.':
|
|
continue
|
|
|
|
# Handle ".." component: go up one level
|
|
if comp_len == 2 and path[comp_start] == '.' and path[comp_start + 1] == '.':
|
|
if out_pos > drive_prefix_len:
|
|
# Rewind to just before the previous separator
|
|
out_pos -= 1
|
|
while out_pos > drive_prefix_len and not _is_sep(buf[out_pos - 1]):
|
|
out_pos -= 1
|
|
# If rewound to the root prefix, keep the prefix
|
|
if out_pos == drive_prefix_len and is_absolute:
|
|
out_pos = drive_prefix_len
|
|
if drive_prefix_len == 0:
|
|
# POSIX root: keep /
|
|
buf[out_pos] = s
|
|
out_pos += 1
|
|
continue
|
|
|
|
# Normal component: copy to output
|
|
if out_pos > drive_prefix_len and not (drive_prefix_len == 0 and out_pos == 1 and is_absolute):
|
|
buf[out_pos] = s
|
|
out_pos += 1
|
|
elif out_pos == 0 and not is_absolute:
|
|
pass # First component, no leading separator needed
|
|
elif is_absolute and out_pos == drive_prefix_len:
|
|
# Leading separator already present, do not add again
|
|
pass
|
|
else:
|
|
buf[out_pos] = s
|
|
out_pos += 1
|
|
|
|
j: sizet = comp_start
|
|
while j < comp_start + comp_len:
|
|
buf[out_pos] = path[j]
|
|
out_pos += 1
|
|
j += 1
|
|
|
|
# Handle empty result
|
|
if out_pos == 0:
|
|
buf[out_pos] = '.'
|
|
out_pos += 1
|
|
|
|
buf[out_pos] = 0
|
|
return str(buf)
|