- Config.vp 解析 includes 数组全部元素为空格分隔 IncludesDirs(首元素仍存 IncludesDir 兼容单目录) - IncludesScanner 逐目录递归扫描合并,FileEntry.IncDir 记录所属 includes 根目录 - StubMerger 存储新增 IncDir 并行数组(MAX_INC_DIR_LEN=512),容量 256→1024 - Phase2 预注册/3.5 按需编译改用逐条目 IncDir 拼接源路径(空串回退 includes_dir) - HandlesImports import 未注册错误显示行内内容 + App 兄弟模块文件豁免 - 新增 TransPyV 自身 project.vpj
762 lines
32 KiB
Plaintext
762 lines
32 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import memhub
|
||
import string
|
||
import stdlib
|
||
import viperlib
|
||
import stdio
|
||
import sys
|
||
import w32.fileio as fileio
|
||
import lib.core.VLogger as VLogger
|
||
import lib.core.Handles.HandlesBase as HandlesBase
|
||
import lib.core.Handles.HandlesTranslator as HT
|
||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||
import lib.core.StubMerger as StubMerger
|
||
|
||
|
||
# ============================================================
|
||
# HandlesImports - 导入语句处理(Mixin 继承模式)
|
||
#
|
||
# 管理 _imported_modules 和 _from_imports 字符串
|
||
# 注意:str = bytes = t.CChar | t.CPtr = i8*
|
||
# ============================================================
|
||
|
||
|
||
# ============================================================
|
||
# 模块级工具函数(保留供外部调用)
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
# 添加已导入模块名
|
||
# ============================================================
|
||
def add_imported_module(pool: memhub.MemBuddy | t.CPtr,
|
||
imported_modules: str,
|
||
name: str) -> str:
|
||
"""记录已导入的模块名,返回新的 imported_modules 字符串指针"""
|
||
if name is None:
|
||
return imported_modules
|
||
if imported_modules is None:
|
||
nlen: t.CSizeT = string.strlen(name)
|
||
buf: t.CChar | t.CPtr = pool.alloc(nlen + 1)
|
||
if buf is not None:
|
||
string.strcpy(buf, name)
|
||
return buf
|
||
return None
|
||
else:
|
||
old_len: t.CSizeT = string.strlen(imported_modules)
|
||
name_len: t.CSizeT = string.strlen(name)
|
||
new_len: t.CSizeT = old_len + 1 + name_len
|
||
buf2: t.CChar | t.CPtr = pool.alloc(new_len + 1)
|
||
if buf2 is not None:
|
||
string.strcpy(buf2, imported_modules)
|
||
buf2[old_len] = ' '
|
||
string.strcpy(buf2 + old_len + 1, name)
|
||
return buf2
|
||
return imported_modules
|
||
|
||
|
||
# ============================================================
|
||
# 检查模块是否已导入
|
||
# ============================================================
|
||
def is_module_imported(imported_modules: str, name: str) -> int:
|
||
"""检查模块是否已导入(词边界精确匹配,避免子串误匹配)"""
|
||
if name is None or imported_modules is None:
|
||
return 0
|
||
name_len: t.CSizeT = string.strlen(name)
|
||
cur: t.CChar | t.CPtr = imported_modules
|
||
total_len: t.CSizeT = string.strlen(imported_modules)
|
||
ci: t.CSizeT = 0
|
||
while ci < total_len:
|
||
while ci < total_len and cur[ci] == ' ':
|
||
ci += 1
|
||
if ci >= total_len:
|
||
break
|
||
word_start: t.CSizeT = ci
|
||
while ci < total_len and cur[ci] != ' ':
|
||
ci += 1
|
||
word_len: t.CSizeT = ci - word_start
|
||
if word_len == name_len:
|
||
match: int = 1
|
||
for ei in range(name_len):
|
||
if cur[word_start + ei] != name[ei]:
|
||
match = 0
|
||
break
|
||
if match == 1:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# 添加 from-import 名称映射
|
||
# ============================================================
|
||
def add_from_import(pool: memhub.MemBuddy | t.CPtr,
|
||
from_imports: str,
|
||
local_name: str,
|
||
module_name: str,
|
||
original_name: str = None) -> str:
|
||
"""添加 from-import 映射,返回新的 from_imports 字符串
|
||
|
||
格式:
|
||
无别名: "local_name:module_name"
|
||
有别名: "local_name:module_name:original_name"
|
||
(original_name 为源模块中的真实函数名,用于跨模块 SHA1 修饰)
|
||
"""
|
||
if local_name is None or module_name is None:
|
||
return from_imports
|
||
entry: t.CChar | t.CPtr = pool.alloc(256)
|
||
if entry is None:
|
||
return from_imports
|
||
if original_name is not None and string.strcmp(original_name, local_name) != 0:
|
||
viperlib.snprintf(entry, 256, "%s:%s:%s", local_name, module_name, original_name)
|
||
else:
|
||
viperlib.snprintf(entry, 256, "%s:%s", local_name, module_name)
|
||
if from_imports is None:
|
||
return entry
|
||
else:
|
||
old_len: t.CSizeT = string.strlen(from_imports)
|
||
entry_len: t.CSizeT = string.strlen(entry)
|
||
new_len: t.CSizeT = old_len + 1 + entry_len
|
||
buf: t.CChar | t.CPtr = pool.alloc(new_len + 1)
|
||
if buf is not None:
|
||
string.strcpy(buf, from_imports)
|
||
buf[old_len] = ' '
|
||
string.strcpy(buf + old_len + 1, entry)
|
||
return buf
|
||
return from_imports
|
||
|
||
|
||
# ============================================================
|
||
# 查找 from-import 名称 → 返回原始函数名(别名场景)
|
||
#
|
||
# 对于 from X import Y as Z,返回 Y(源模块中的真实函数名)。
|
||
# 无别名时返回 None(local_name 即为原始名)。
|
||
# ============================================================
|
||
def lookup_from_import_original(from_imports: str,
|
||
local_name: str) -> str:
|
||
"""查找 from-import 别名对应的原始函数名,返回 None=无别名或未找到"""
|
||
if local_name is None or from_imports is None:
|
||
return None
|
||
name_len: t.CSizeT = string.strlen(local_name)
|
||
cur: t.CChar | t.CPtr = from_imports
|
||
ci: t.CSizeT = 0
|
||
total_len: t.CSizeT = string.strlen(from_imports)
|
||
while ci < total_len:
|
||
# 跳过前导空格
|
||
while ci < total_len and cur[ci] == ' ':
|
||
ci += 1
|
||
if ci >= total_len:
|
||
break
|
||
# 找到第一个 ':' 的位置
|
||
colon1: t.CSizeT = ci
|
||
while colon1 < total_len and cur[colon1] != ':' and cur[colon1] != ' ':
|
||
colon1 += 1
|
||
if colon1 >= total_len or cur[colon1] != ':':
|
||
break
|
||
entry_name_len: t.CSizeT = colon1 - ci
|
||
# 跳过 star import
|
||
if entry_name_len == 1 and cur[ci] == '*':
|
||
ci = colon1
|
||
while ci < total_len and cur[ci] != ' ':
|
||
ci += 1
|
||
continue
|
||
# 比较名称
|
||
if entry_name_len == name_len:
|
||
match: int = 1
|
||
ei: t.CSizeT = 0
|
||
while ei < name_len:
|
||
if cur[ci + ei] != local_name[ei]:
|
||
match = 0
|
||
break
|
||
ei += 1
|
||
if match == 1:
|
||
# 找到匹配,检查是否有第三个字段(原始名)
|
||
pos: t.CSizeT = colon1 + 1
|
||
# 跳过模块名
|
||
while pos < total_len and cur[pos] != ':' and cur[pos] != ' ' and cur[pos] != '\0':
|
||
pos += 1
|
||
if pos < total_len and cur[pos] == ':':
|
||
# 有第三个字段: original_name
|
||
orig_start: t.CSizeT = pos + 1
|
||
orig_end: t.CSizeT = orig_start
|
||
while orig_end < total_len and cur[orig_end] != ' ' and cur[orig_end] != '\0':
|
||
orig_end += 1
|
||
orig_len: t.CSizeT = orig_end - orig_start
|
||
if orig_len > 0:
|
||
orig_buf: str = cur + orig_start
|
||
# 返回指向内部的指针(调用方需在使用期内保持 from_imports 有效)
|
||
return orig_buf
|
||
return None
|
||
return None
|
||
# 跳到下一个条目
|
||
ci = colon1
|
||
while ci < total_len and cur[ci] != ' ':
|
||
ci += 1
|
||
return None
|
||
|
||
|
||
# ============================================================
|
||
# 查找 from-import 名称 → 返回模块名或 None
|
||
#
|
||
# allow_star_fallback: 是否允许 star import 回退(默认 1=允许)。
|
||
# 当查询明确模块名(如 "BuildPipeline")时,应传 0 禁用回退,
|
||
# 避免被 star import 模块名误导(如 from stdint import * 后
|
||
# 查询 "BuildPipeline" 错误回退到 "stdint")。
|
||
# ============================================================
|
||
def lookup_from_import(from_imports: str, name: str,
|
||
allow_star_fallback: int = 1) -> str:
|
||
"""查找 from-import 名称,返回模块名或 None
|
||
|
||
支持 star import: 如果 from_imports 中有 "*:module" 条目,
|
||
且未找到精确名称匹配,且 allow_star_fallback != 0,则返回 star import 的模块名。
|
||
"""
|
||
if name is None or from_imports is None:
|
||
return None
|
||
name_len: t.CSizeT = string.strlen(name)
|
||
cur: t.CChar | t.CPtr = from_imports
|
||
ci: t.CSizeT = 0
|
||
total_len: t.CSizeT = string.strlen(from_imports)
|
||
star_mod: str = None
|
||
while ci < total_len:
|
||
# 跳过前导空格
|
||
while ci < total_len and cur[ci] == ' ':
|
||
ci += 1
|
||
if ci >= total_len:
|
||
break
|
||
# 找到 ':' 的位置
|
||
colon_pos: t.CSizeT = ci
|
||
while colon_pos < total_len and cur[colon_pos] != ':' and cur[colon_pos] != ' ':
|
||
colon_pos += 1
|
||
if colon_pos >= total_len or cur[colon_pos] != ':':
|
||
break
|
||
entry_name_len: t.CSizeT = colon_pos - ci
|
||
# 检测 star import ("*:module")
|
||
if entry_name_len == 1 and cur[ci] == '*':
|
||
mod_start: t.CSizeT = colon_pos + 1
|
||
mod_end: t.CSizeT = mod_start
|
||
while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0' and cur[mod_end] != ':':
|
||
mod_end += 1
|
||
star_mod = cur + mod_start
|
||
# 比较名称
|
||
elif entry_name_len == name_len:
|
||
match: int = 1
|
||
ei: t.CSizeT = 0
|
||
while ei < name_len:
|
||
if cur[ci + ei] != name[ei]:
|
||
match = 0
|
||
break
|
||
ei += 1
|
||
if match == 1:
|
||
mod_start2: t.CSizeT = colon_pos + 1
|
||
mod_end2: t.CSizeT = mod_start2
|
||
# 模块名结束于: 空格、null、或第二个':'(别名格式的分隔符)
|
||
while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0' and cur[mod_end2] != ':':
|
||
mod_end2 += 1
|
||
return cur + mod_start2
|
||
# 跳到下一个条目
|
||
ci = colon_pos
|
||
while ci < total_len and cur[ci] != ' ':
|
||
ci += 1
|
||
# 未找到精确匹配,仅在允许时回退到 star import
|
||
if allow_star_fallback == 0:
|
||
return None
|
||
return star_mod
|
||
|
||
|
||
# ============================================================
|
||
# 全局 re-export 映射表(module_sha1 + func_name → source_module)
|
||
#
|
||
# 当 ast/__init__.vp 中有 from .lexer import _lexer_init 时,
|
||
# _lexer_init 被 re-export 为 ast._lexer_init。
|
||
# 跨模块调用 ast._lexer_init(...) 需使用 ast.lexer 的 SHA1 混淆,
|
||
# 而非 ast 的 SHA1。
|
||
#
|
||
# 数据布局:
|
||
# _g_reexport_sha1s: 每条 17 字节(SHA1 16字符 + null)
|
||
# _g_reexport_funcs: 每条 64 字节(函数名 + null)
|
||
# _g_reexport_srcs: 每条 64 字节(源模块名 + null)
|
||
# ============================================================
|
||
MAX_REEXPORT: t.CDefine = 512
|
||
|
||
_g_reexport_sha1s: bytes = None
|
||
_g_reexport_funcs: bytes = None
|
||
_g_reexport_srcs: bytes = None
|
||
_g_reexport_count: int = 0
|
||
|
||
|
||
# ============================================================
|
||
# register_reexport - 注册 re-export 映射
|
||
#
|
||
# 在 HandleImportFromNames 中调用:当处理 from .X import Y 时,
|
||
# 记录当前模块(通过 ModuleSha1)re-export 了函数 Y,源模块为 X。
|
||
#
|
||
# 幂等:重复注册相同映射不会增加条目。
|
||
# 内存用 stdlib.malloc 分配(全局存储器,跨 Phase 持久化)。
|
||
# ============================================================
|
||
def register_reexport(mod_sha1: str, func_name: str, source_module: str) -> int:
|
||
"""注册 re-export 映射(幂等),返回 0 成功,-1 失败"""
|
||
global _g_reexport_sha1s, _g_reexport_funcs, _g_reexport_srcs, _g_reexport_count
|
||
if mod_sha1 is None or func_name is None or source_module is None:
|
||
return -1
|
||
# 懒初始化
|
||
if _g_reexport_sha1s is None:
|
||
_g_reexport_sha1s = stdlib.malloc(MAX_REEXPORT * 17)
|
||
_g_reexport_funcs = stdlib.malloc(MAX_REEXPORT * 64)
|
||
_g_reexport_srcs = stdlib.malloc(MAX_REEXPORT * 64)
|
||
if _g_reexport_sha1s is None or _g_reexport_funcs is None or _g_reexport_srcs is None:
|
||
return -1
|
||
string.memset(_g_reexport_sha1s, 0, MAX_REEXPORT * 17)
|
||
string.memset(_g_reexport_funcs, 0, MAX_REEXPORT * 64)
|
||
string.memset(_g_reexport_srcs, 0, MAX_REEXPORT * 64)
|
||
if _g_reexport_count >= MAX_REEXPORT:
|
||
return -1
|
||
# 幂等检查:查找是否已存在相同映射
|
||
for i in range(_g_reexport_count):
|
||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||
fidx: t.CSizeT = t.CSizeT(i) * 64
|
||
if string.strcmp(_g_reexport_sha1s + sidx, mod_sha1) == 0:
|
||
if string.strcmp(_g_reexport_funcs + fidx, func_name) == 0:
|
||
# 已存在,更新 source_module(以防变化)
|
||
string.strcpy(_g_reexport_srcs + fidx, source_module)
|
||
return 0
|
||
# 添加新条目
|
||
idx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 17
|
||
fidx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 64
|
||
string.strcpy(_g_reexport_sha1s + idx2, mod_sha1)
|
||
string.strcpy(_g_reexport_funcs + fidx2, func_name)
|
||
string.strcpy(_g_reexport_srcs + fidx2, source_module)
|
||
_g_reexport_count += 1
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# lookup_reexport - 查找 re-export 映射
|
||
#
|
||
# 给定模块 SHA1 和函数名,返回源模块名(如 "ast.lexer")或 None。
|
||
# 调用方通过 _lookup_module_sha1(source_module) 获取源模块的 SHA1。
|
||
# ============================================================
|
||
def lookup_reexport(mod_sha1: str, func_name: str) -> str:
|
||
"""查找 re-export 映射,返回源模块名或 None"""
|
||
if mod_sha1 is None or func_name is None:
|
||
return None
|
||
if _g_reexport_sha1s is None or _g_reexport_count <= 0:
|
||
return None
|
||
for i in range(_g_reexport_count):
|
||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||
fidx: t.CSizeT = t.CSizeT(i) * 64
|
||
if string.strcmp(_g_reexport_sha1s + sidx, mod_sha1) == 0:
|
||
if string.strcmp(_g_reexport_funcs + fidx, func_name) == 0:
|
||
return _g_reexport_srcs + fidx
|
||
return None
|
||
|
||
|
||
# ============================================================
|
||
# _resolve_relative_module - 解析相对导入为完整模块名
|
||
#
|
||
# 对于 from .X import Y(level=1, module="X")在包 pkg 中:
|
||
# 解析为 "pkg.X"
|
||
# 对于 from . import Y(level=1, module=None)在包 pkg 中:
|
||
# 解析为 "pkg"
|
||
# 对于 from ..X import Y(level=2, module="X")在包 pkg.sub 中:
|
||
# 解析为 "pkg.X"(先从 pkg.sub 上溯一级到 pkg,再追加 .X)
|
||
#
|
||
# Args:
|
||
# pool: 内存池
|
||
# current_package: 当前文件所属包名(如 "llvmlite"),None 表示无包
|
||
# level: 相对导入级别(0=绝对,1=., 2=..)
|
||
# module: ImportFrom 的 module 字段(可能为 None)
|
||
#
|
||
# Returns:
|
||
# 解析后的完整模块名;绝对导入(level<=0)直接返回 module;
|
||
# 无法解析时返回 module(回退到原始值)
|
||
# ============================================================
|
||
def _resolve_relative_module(pool: memhub.MemBuddy | t.CPtr,
|
||
current_package: str,
|
||
level: t.CInt,
|
||
module: str) -> str:
|
||
"""解析相对导入为完整模块名"""
|
||
if level <= 0:
|
||
return module
|
||
if current_package is None:
|
||
return module
|
||
|
||
# 从 current_package 开始,上溯 (level-1) 级
|
||
pkg: str = current_package
|
||
up: t.CInt = level - 1
|
||
while up > 0:
|
||
pkg_len: t.CSizeT = string.strlen(pkg)
|
||
last_dot: t.CSizeT = 0
|
||
found: int = 0
|
||
i: t.CSizeT = 0
|
||
while i < pkg_len:
|
||
if pkg[i] == '.':
|
||
last_dot = i
|
||
found = 1
|
||
i += 1
|
||
if found == 0:
|
||
# 无更多父级,包变为空
|
||
pkg = None
|
||
break
|
||
# 截断到最后一个 '.' 处
|
||
new_pkg: str = pool.alloc(last_dot + 1)
|
||
if new_pkg is None:
|
||
return module
|
||
string.strncpy(new_pkg, pkg, last_dot)
|
||
new_pkg[last_dot] = '\0'
|
||
pkg = new_pkg
|
||
up -= 1
|
||
|
||
if module is None:
|
||
# from . import Y → 模块就是包本身
|
||
return pkg
|
||
if pkg is None:
|
||
# 包已上溯到空,直接用 module
|
||
return module
|
||
# 拼接 pkg + "." + module
|
||
pkg_len2: t.CSizeT = string.strlen(pkg)
|
||
mod_len: t.CSizeT = string.strlen(module)
|
||
buf: str = pool.alloc(pkg_len2 + 1 + mod_len + 1)
|
||
if buf is None:
|
||
return module
|
||
string.strcpy(buf, pkg)
|
||
buf[pkg_len2] = '.'
|
||
string.strcpy(buf + pkg_len2 + 1, module)
|
||
return buf
|
||
|
||
|
||
# ============================================================
|
||
# compute_package_from_relpath - 从相对路径计算包名
|
||
#
|
||
# 包名 = 文件所在目录路径,将 / 和 \ 替换为 .
|
||
# 对于顶级文件(无目录分隔符),返回 None
|
||
#
|
||
# 示例:
|
||
# "llvmlite/__init__.vp" → "llvmlite"
|
||
# "llvmlite/__types.vp" → "llvmlite"
|
||
# "ast/parser.vp" → "ast"
|
||
# "ast.vp" → None(顶级文件)
|
||
# ============================================================
|
||
def compute_package_from_relpath(pool: memhub.MemBuddy | t.CPtr,
|
||
rel_path: str) -> str:
|
||
"""从相对路径计算包名(目录部分,分隔符替换为 .)"""
|
||
if rel_path is None:
|
||
return None
|
||
rlen: t.CSizeT = string.strlen(rel_path)
|
||
# 找最后一个 / 或 \
|
||
last_sep: t.CSizeT = 0
|
||
found: int = 0
|
||
i: t.CSizeT = 0
|
||
while i < rlen:
|
||
ch: t.CChar = rel_path[i]
|
||
if ch == '/' or ch == '\\':
|
||
last_sep = i
|
||
found = 1
|
||
i += 1
|
||
if found == 0:
|
||
# 无目录分隔符 → 顶级文件,无包
|
||
return None
|
||
# 复制目录部分,将 / 和 \ 替换为 .
|
||
dir_len: t.CSizeT = last_sep
|
||
buf: str = pool.alloc(dir_len + 1)
|
||
if buf is None:
|
||
return None
|
||
j: t.CSizeT = 0
|
||
while j < dir_len:
|
||
ch2: t.CChar = rel_path[j]
|
||
if ch2 == '/' or ch2 == '\\':
|
||
buf[j] = '.'
|
||
else:
|
||
buf[j] = ch2
|
||
j += 1
|
||
buf[dir_len] = '\0'
|
||
return buf
|
||
|
||
|
||
# ============================================================
|
||
# 全局当前文件名(镜像 HandlesType._g_current_file)
|
||
#
|
||
# HandlesImports 因循环依赖(HandlesType import 了本模块)不能
|
||
# import HandlesType,无法读取其 _g_current_file。因此由
|
||
# BuildPipeline/Phase1 在调用 HandlesType.set_current_file 时
|
||
# 同步调用本模块的 set_current_file,供导入时 SHA1 检查报错使用。
|
||
# ============================================================
|
||
_g_current_file: str = None
|
||
|
||
|
||
def set_current_file(file_path: str) -> None:
|
||
"""设置当前正在编译的文件名(与 HandlesType.set_current_file 同步调用)"""
|
||
global _g_current_file
|
||
_g_current_file = file_path
|
||
|
||
|
||
# ============================================================
|
||
# _report_import_error - 本地实现 Python 风格报错(避免循环依赖)
|
||
#
|
||
# 无法访问 HandlesType.fatal_error(HandlesType import 了本模块,
|
||
# 若本模块再 import HandlesType 会形成循环依赖),这里用简化格式:
|
||
# Traceback (most recent call last):
|
||
# File "<当前文件>", line <N>
|
||
# ImportError: <msg>
|
||
# ============================================================
|
||
def _report_import_error(node: ast.AST | t.CPtr, msg: str) -> None:
|
||
"""以 Python 风格报错并退出(本地实现,避免循环依赖)"""
|
||
stdio.printf("Traceback (most recent call last):\n")
|
||
lineno: int = 0
|
||
if node is not None:
|
||
lineno = node.lineno
|
||
file_name: str = _g_current_file
|
||
if file_name is None:
|
||
file_name = "<unknown>"
|
||
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
||
# 显示行内内容(读源文件第 lineno 行的实际代码,便于快速定位)
|
||
if lineno > 0 and file_name is not None:
|
||
f_err: fileio.File | t.CPtr = fileio.File(file_name, fileio.MODE.R)
|
||
if not f_err.closed:
|
||
line_buf_err: bytes = stdlib.malloc(512)
|
||
if line_buf_err is not None:
|
||
cur_ln: int = 0
|
||
while cur_ln < lineno:
|
||
r_err: LONG = f_err.readline(line_buf_err, 511)
|
||
if r_err <= 0:
|
||
break
|
||
cur_ln += 1
|
||
if cur_ln == lineno:
|
||
ll_err: t.CSizeT = string.strlen(line_buf_err)
|
||
while ll_err > 0 and (line_buf_err[ll_err - 1] == '\n' or line_buf_err[ll_err - 1] == '\r'):
|
||
ll_err -= 1
|
||
line_buf_err[ll_err] = '\0'
|
||
stdio.printf(" %s\n", line_buf_err)
|
||
break
|
||
stdlib.free(line_buf_err)
|
||
f_err.close()
|
||
stdio.printf("ImportError: %s\n", msg)
|
||
sys.exit(1)
|
||
|
||
|
||
# ============================================================
|
||
# _check_import_sha1 - 导入时校验模块 SHA1 是否已注册(fast-fail)
|
||
#
|
||
# 背景:若 includes 路径配置错误(如 project.json 指向不存在的
|
||
# ./includes),includes 扫描结果为 0,模块不会注册 SHA1。此前
|
||
# 错误延迟到实际调用 module.func() 时才报误导性的
|
||
# "跨模块调用 'bd.alloc' 找不到模块 SHA1"。
|
||
# 现在在 import 语句处立即检测并报错,快速定位配置问题。
|
||
#
|
||
# t/c 是内部库,不参与 includes 扫描,豁免检查。
|
||
# ============================================================
|
||
def _is_local_module_file(mod_name: str) -> int:
|
||
"""当前文件同目录存在 {mod_name}.vp/.py → 项目内兄弟模块
|
||
|
||
多文件项目(如 Test/G4/App 的 caller1.vp import g4node)的兄弟模块
|
||
不在 includes 扫描存储器中(SHA1 在项目源文件列表),检查应豁免。
|
||
"""
|
||
cur: str = _g_current_file
|
||
if cur is None or mod_name is None:
|
||
return 0
|
||
cur_len: t.CSizeT = string.strlen(cur)
|
||
if cur_len == 0:
|
||
return 0
|
||
sep_pos: t.CSizeT = cur_len
|
||
while sep_pos > 0:
|
||
sep_pos -= 1
|
||
ch: t.CChar = cur[sep_pos]
|
||
if ch == '/' or ch == '\\':
|
||
sep_pos += 1
|
||
break
|
||
mod_len: t.CSizeT = string.strlen(mod_name)
|
||
path_len: t.CSizeT = sep_pos + mod_len + 5
|
||
path_buf: bytes = stdlib.malloc(path_len + 1)
|
||
if path_buf is None:
|
||
return 0
|
||
# .vp 文件
|
||
string.strncpy(path_buf, cur, sep_pos)
|
||
path_buf[sep_pos] = '\0'
|
||
string.strcat(path_buf, mod_name)
|
||
string.strcat(path_buf, ".vp")
|
||
fex: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
|
||
if not fex.closed:
|
||
fex.close()
|
||
stdlib.free(path_buf)
|
||
return 1
|
||
# .py 文件(Python 模块可能无 .vp)
|
||
string.strncpy(path_buf, cur, sep_pos)
|
||
path_buf[sep_pos] = '\0'
|
||
string.strcat(path_buf, mod_name)
|
||
string.strcat(path_buf, ".py")
|
||
fex2: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
|
||
if not fex2.closed:
|
||
fex2.close()
|
||
stdlib.free(path_buf)
|
||
return 1
|
||
stdlib.free(path_buf)
|
||
return 0
|
||
|
||
|
||
def _check_import_sha1(node: ast.AST | t.CPtr, mod_name: str) -> int:
|
||
"""导入时校验模块 SHA1 是否已注册,未注册则立即报错退出
|
||
|
||
Args:
|
||
node: Import/ImportFrom AST 节点(提供行号)
|
||
mod_name: 导入的模块名(如 "memhub"、"w32.fileio")
|
||
|
||
Returns:
|
||
1=检查通过;模块未注册时直接报错退出(不会返回 0)
|
||
"""
|
||
if mod_name is None:
|
||
return 1
|
||
# t/c 是内部库,不参与 includes 扫描,豁免检查
|
||
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0:
|
||
return 1
|
||
mod_sha1: str = StubMerger.LookupSha1ByModName(mod_name)
|
||
if mod_sha1 is None:
|
||
# App 兄弟模块豁免:当前文件同目录存在 {mod_name}.vp/.py
|
||
# (如 Test/G4/App/caller1.vp import g4node——同项目源文件,
|
||
# SHA1 在项目文件列表而非 includes 扫描存储器,不应报错)
|
||
if _is_local_module_file(mod_name) != 0:
|
||
return 1
|
||
err_buf: t.CChar | t.CPtr = stdlib.malloc(256)
|
||
if err_buf is not None:
|
||
viperlib.snprintf(err_buf, 256,
|
||
"模块 '%s' 未注册 SHA1(includes 扫描未找到该模块,请检查 project.json 的 includes 路径配置)",
|
||
mod_name)
|
||
_report_import_error(node, err_buf)
|
||
else:
|
||
_report_import_error(node, "导入模块 SHA1 未注册(includes 扫描未找到该模块)")
|
||
return 1
|
||
|
||
|
||
# ============================================================
|
||
# ImportsHandle - 导入语句处理器(Mixin 继承模式)
|
||
#
|
||
# 方法版本:直接更新 trans._imported_modules / trans._from_imports
|
||
# ============================================================
|
||
@t.NoVTable
|
||
class ImportsHandle(HandlesBase.Mixin):
|
||
"""导入语句处理器:继承 Mixin 获得 Trans 回指针"""
|
||
|
||
def __init__(self, trans: HT.Translator | t.CPtr):
|
||
self.Trans = trans
|
||
|
||
# ============================================================
|
||
# HandleImport - 处理 import 语句,更新 trans._imported_modules
|
||
# ============================================================
|
||
def HandleImport(self, node: ast.AST | t.CPtr) -> int:
|
||
"""处理 import 语句,返回 0"""
|
||
imp: ast.Import | t.CPtr = (ast.Import | t.CPtr)(node)
|
||
if imp is None:
|
||
return 0
|
||
names: list[ast.AST | t.CPtr] | t.CPtr = imp.names
|
||
if names is None:
|
||
return 0
|
||
nn: t.CSizeT = names.__len__()
|
||
for ni in range(nn):
|
||
alias: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(names.get(ni))
|
||
if alias is not None and alias.name is not None:
|
||
# 导入时立即校验模块 SHA1 是否注册(fast-fail,仅全量翻译时检查)
|
||
# declare_only=1/2 是 struct 预注册/import 扫描阶段:此时 SHA1 表
|
||
# 尚未包含后续才追加的模块(如 App 源文件),检查会造成误报
|
||
if self.Trans._declare_only == 0:
|
||
_check_import_sha1(node, alias.name)
|
||
pool_val: memhub.MemBuddy | t.CPtr = self.Trans.Pool
|
||
im_val: str = self.Trans._imported_modules
|
||
self.Trans._imported_modules = add_imported_module(
|
||
pool_val, im_val, alias.name)
|
||
# 别名也加入导入模块列表(用于模块限定构造器检查 Module.Class())
|
||
if alias.asname is not None:
|
||
self.Trans._imported_modules = add_imported_module(
|
||
pool_val, self.Trans._imported_modules, alias.asname)
|
||
# 别名也加入 from_imports(asname:fullname),
|
||
# 使 HT.func() 跨模块调用能通过 from_imports 查找别名→完整模块名→SHA1
|
||
# 否则 _lookup_module_sha1("HT") 找不到(mod_arr 只存文件名 HandlesTranslator)
|
||
self.Trans._from_imports = add_from_import(
|
||
pool_val, self.Trans._from_imports, alias.asname, alias.name)
|
||
else:
|
||
# 无别名时(import a.b.c),将顶层包名 "a" 也加入 _imported_modules,
|
||
# 使 a.b.c 作为属性访问的 base 被翻译时能通过 is_module_imported("a") 检查
|
||
dot_ptr: str = string.strstr(alias.name, '.')
|
||
if dot_ptr is not None:
|
||
pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_ptr) - t.CUInt64T(alias.name))
|
||
if pkg_len > 0 and pkg_len < 256:
|
||
pkg_buf: bytes = pool_val.alloc(pkg_len + 1)
|
||
if pkg_buf is not None:
|
||
string.strncpy(pkg_buf, alias.name, pkg_len)
|
||
pkg_buf[pkg_len] = '\0'
|
||
self.Trans._imported_modules = add_imported_module(
|
||
pool_val, self.Trans._imported_modules, pkg_buf)
|
||
return 0
|
||
|
||
# ============================================================
|
||
# HandleImportFromModule - 处理 from X import Y 的模块部分
|
||
#
|
||
# 对相对导入(level > 0),使用 trans.CurrentPackage 解析为
|
||
# 完整模块名(如 __types → llvmlite.__types),确保 .deps.txt
|
||
# 记录的模块名与 SHA1 映射表一致。
|
||
# ============================================================
|
||
def HandleImportFromModule(self, node: ast.AST | t.CPtr) -> int:
|
||
"""处理 from X import Y 的模块部分,更新 trans._imported_modules"""
|
||
impf: ast.ImportFrom | t.CPtr = (ast.ImportFrom | t.CPtr)(node)
|
||
if impf is None:
|
||
return 0
|
||
# 解析模块名:相对导入需补充包前缀
|
||
resolved: str = _resolve_relative_module(
|
||
self.Trans.Pool, self.Trans.CurrentPackage,
|
||
impf.level, impf.module)
|
||
if resolved is not None:
|
||
# 导入时立即校验模块 SHA1 是否注册(fast-fail,仅全量翻译时检查)
|
||
if self.Trans._declare_only == 0:
|
||
_check_import_sha1(node, resolved)
|
||
self.Trans._imported_modules = add_imported_module(
|
||
self.Trans.Pool, self.Trans._imported_modules, resolved)
|
||
return 0
|
||
|
||
# ============================================================
|
||
# HandleImportFromNames - 处理 from X import Y 的名称部分
|
||
#
|
||
# 同样使用解析后的完整模块名,确保 from-import 映射
|
||
# (如 LLVMType:llvmlite.__types)能正确查到 SHA1。
|
||
# ============================================================
|
||
def HandleImportFromNames(self, node: ast.AST | t.CPtr) -> int:
|
||
"""处理 from X import Y 的名称部分,更新 trans._from_imports"""
|
||
impf: ast.ImportFrom | t.CPtr = (ast.ImportFrom | t.CPtr)(node)
|
||
if impf is None:
|
||
return 0
|
||
# 解析模块名:相对导入需补充包前缀
|
||
resolved: str = _resolve_relative_module(
|
||
self.Trans.Pool, self.Trans.CurrentPackage,
|
||
impf.level, impf.module)
|
||
if resolved is not None:
|
||
names: list[ast.AST | t.CPtr] | t.CPtr = impf.names
|
||
if names is not None:
|
||
nn: t.CSizeT = names.__len__()
|
||
for ni in range(nn):
|
||
alias: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(names.get(ni))
|
||
if alias is not None and alias.name is not None:
|
||
local_name: str = alias.name
|
||
orig_name: str = None
|
||
if alias.asname is not None:
|
||
local_name = alias.asname
|
||
orig_name = alias.name
|
||
self.Trans._from_imports = add_from_import(
|
||
self.Trans.Pool, self.Trans._from_imports,
|
||
local_name, resolved, orig_name)
|
||
# 命名空间隔离:from-import 的名称标记为可见结构体
|
||
HandlesStruct.add_visible_struct(self.Trans.Pool, local_name)
|
||
# 注册 re-export 映射:当前模块 re-export 了 local_name,源模块为 resolved
|
||
# 使跨模块调用 module.func() 能解析到正确的源模块 SHA1
|
||
if self.Trans.ModuleSha1 is not None:
|
||
register_reexport(self.Trans.ModuleSha1, local_name, resolved)
|
||
return 0
|
||
|
||
|
||
# ============================================================
|
||
# NewImportsHandle - 工厂函数
|
||
# ============================================================
|
||
def NewImportsHandle(pool: memhub.MemBuddy | t.CPtr,
|
||
trans: HT.Translator | t.CPtr) -> ImportsHandle | t.CPtr:
|
||
h: ImportsHandle | t.CPtr = pool.alloc(ImportsHandle.__sizeof__())
|
||
if h is None:
|
||
return None
|
||
string.memset(h, 0, ImportsHandle.__sizeof__())
|
||
h.Trans = trans
|
||
return h
|