去除 import t, c

This commit is contained in:
TermiNexus committed 2026-10-17 21:57:43 +08:00
1 parent 85b89b6e8d
commit b90a8a7bfb
17 files changed
+163 -862

No files matched your search

+1 -1
View File
@@ -27,7 +27,7 @@ import vthreading
import requests
import requests.https as rhttps
import json
import w32.win32memory as win32mem
import win32.win32memory as win32mem
# ============================================================
+51 -57
View File
@@ -1,22 +1,22 @@
# ============================================================
# main.vp — ViperCPW:CreateProcessA 独立复现工程
#
# 目的:把原先塞在 TransPyV\App\main.vp 里的 [TEMP-PROBE] 探针整体搬到
# 一个独立 Viper 工程里,脱开 TPV 单独复现 / 二分。
# 目的:把原先塞在 ViperC\App\main.vp 里的 [TEMP-PROBE] 探针整体搬到
# 一个独立 Viper 工程里,脱开 VPC 单独复现 / 二分。
#
# 待复现现象:自举出的 TransPyV.exe 内 CreateProcessA 恒返回 0 且
# 待复现现象:自举出的 viperc.exe 内 CreateProcessA 恒返回 0 且
# GetLastError=87(ERROR_INVALID_PARAMETER);同参数在 Python/ctypes
# 下 ok=1。
#
# TPV 侧已逐项实测 ok=0/err=87 的维度(本工程用于在"非 TPV 宿主"上复核):
# VPC 侧已逐项实测 ok=0/err=87 的维度(本工程用于在"非 VPC 宿主"上复核):
# IR/机器码栈传参(逐槽核对正确)、si.cb=104 与结构体布局、命令行内容
# (逐字节回读正确、手工复制不用 strcpy 也 87)、应用名(含绝对路径)、
# lpCurrentDirectory、lpEnvironment、bInheritHandles(0/1)、
# dwCreationFlags(0/CREATE_NO_WINDOW)。
#
# 本工程要回答的问题:这是"仅 TPV.exe 特有",还是"任何 Viper 程序都如此"。
# 本工程要回答的问题:这是"仅 VPC.exe 特有",还是"任何 Viper 程序都如此"。
#
# 五组用例(与 TPV 探针同参数、同 si 构造),每组打印 ok 与 GetLastError:
# 五组用例(与 VPC 探针同参数、同 si 构造),每组打印 ok 与 GetLastError:
# #1 lpApplicationName=NULL + "llc --version",inh=1,flags=0
# #2 同 #1 但 inh=0
# #3 同 #1 但 flags=CREATE_NO_WINDOW
@@ -25,19 +25,18 @@
# 对照组:打印本进程 GetCurrentDirectoryA 的结果(证明本进程能正常调 Win32)。
#
# 判定:
# 五组全 ok=0/err=87 ⇒ 与 TransPyV.exe 同因,问题在 Viper 生成代码/运行期,
# 与 TPV 自身逻辑无关(任何 Viper 程序都如此)。
# 任一组 ok=1 ⇒ 该维度即是 TPV 特有问题,按组二分定位。
# 五组全 ok=0/err=87 ⇒ 与 viperc.exe 同因,问题在 Viper 生成代码/运行期,
# 与 VPC 自身逻辑无关(任何 Viper 程序都如此)。
# 任一组 ok=1 ⇒ 该维度即是 VPC 特有问题,按组二分定位。
# ============================================================
import t, c
from stdint import *
import stdio
import stdlib
import string
import w32.win32base
import w32.win32file
import w32.win32process
import w32.win32console
import win32.win32base
import win32.win32file
import win32.win32process
import win32.win32console
# 控制台代码页:UTF-8
@@ -47,7 +46,7 @@ define PAGE_CODE = 65001
# ============================================================
# 单组复现:构造 si/pi → 调 CreateProcessA → 打印 ok / GetLastError / si 字段
# si 构造与 TPV 的 _run_cmd_wait 逐字一致:
# si 构造与 VPC 的 _run_cmd_wait 逐字一致:
# T() 构造 → string.memset 显式清零 → si.cb = STARTUPINFOA.__sizeof__()
# ============================================================
def ProbeCreateProcessA(Tag: str, AppName: str, CommandLine: str,
@@ -59,7 +58,7 @@ def ProbeCreateProcessA(Tag: str, AppName: str, CommandLine: str,
CommandLine : lpCommandLine
InheritHandles : 第 5 实参 bInheritHandles
CreationFlags : 第 6 实参 dwCreationFlags
lpEnvironment / lpCurrentDirectory 恒传 NULL(TPV 侧已实测与 87 无关)。
lpEnvironment / lpCurrentDirectory 恒传 NULL(VPC 侧已实测与 87 无关)。
"""
# 命令行必须放进可写堆缓冲:CreateProcessA 会原地改写 lpCommandLine,
# 不能传只读字面量。
@@ -76,16 +75,16 @@ def ProbeCreateProcessA(Tag: str, AppName: str, CommandLine: str,
string.strcpy(CmdBuffer, CommandLine)
# si/pi:T() 构造只 alloca 不 memset,必须显式清零
# 先例:TransPyV/App/lib/Phase/BuildPipeline.vp:530-532
# (string.memset(t.CPtr(c.Addr(si)), 0, STARTUPINFOA.__sizeof__()))
# 先例:ViperC/App/lib/Phase/BuildPipeline.vp:530-532
# (string.memset(rawptr(c.Addr(si)), 0, STARTUPINFOA.__sizeof__()))
# 类型引用写法:includes/subprocess.vp:99-100
# c.Addr() 取址:includes/condition.vp:26;t.CPtr(c.Addr(x)):includes/subprocess.vp:121
StartupInfo: w32.win32process.STARTUPINFOA = w32.win32process.STARTUPINFOA()
ProcessInfo: w32.win32process.PROCESS_INFORMATION = w32.win32process.PROCESS_INFORMATION()
string.memset(t.CPtr(&StartupInfo), 0, w32.win32process.STARTUPINFOA.__sizeof__())
string.memset(t.CPtr(&ProcessInfo), 0, w32.win32process.PROCESS_INFORMATION.__sizeof__())
# c.Addr() 取址:includes/condition.vp:26;rawptr(c.Addr(x)):includes/subprocess.vp:121
StartupInfo: win32.win32process.STARTUPINFOA = win32.win32process.STARTUPINFOA()
ProcessInfo: win32.win32process.PROCESS_INFORMATION = win32.win32process.PROCESS_INFORMATION()
string.memset(rawptr(&StartupInfo), 0, win32.win32process.STARTUPINFOA.__sizeof__())
string.memset(rawptr(&ProcessInfo), 0, win32.win32process.PROCESS_INFORMATION.__sizeof__())
# si.cb = 结构体大小:includes/subprocess.vp:100
StartupInfo.cb = w32.win32process.STARTUPINFOA.__sizeof__()
StartupInfo.cb = win32.win32process.STARTUPINFOA.__sizeof__()
# 应用名描述:None 显式打印成 NULL(不臆造内容,也不兜底成别的路径)
AppDesc: str = "NULL"
@@ -93,24 +92,24 @@ def ProbeCreateProcessA(Tag: str, AppName: str, CommandLine: str,
AppDesc = AppName
# GetLastError 基线:调用前读取,用于判定 err 是否为残留值
# GetLastError 声明:includes/w32/win32base.vp:62
# GetLastError 声明:includes/win32/win32base.vp:62
# 调用后紧跟读取的先例:ViperApps/ViperZipfileProject/App/main.vp:116
PreError: uint32 = w32.win32base.GetLastError()
PreError: uint32 = win32.win32base.GetLastError()
# 单条调用:10 实参,lpEnvironment / lpCurrentDirectory 恒 NULL
# 先例:includes/subprocess.vp:112-123(t.CPtr(0) 表 NULL 见 :113/:115/:119/:120)
OkValue: int = w32.win32process.CreateProcessA(
AppName, CmdBuffer, t.CPtr(0), t.CPtr(0), InheritHandles, CreationFlags,
t.CPtr(0), t.CPtr(0), t.CPtr(&StartupInfo), t.CPtr(&ProcessInfo))
# 先例:includes/subprocess.vp:112-123(None 表 NULL 见 :113/:115/:119/:120)
OkValue: int = win32.win32process.CreateProcessA(
AppName, CmdBuffer, None, None, InheritHandles, CreationFlags,
None, None, rawptr(&StartupInfo), rawptr(&ProcessInfo))
# GetLastError 必须紧跟调用读取(printf 等都会覆盖它)
ErrorValue: uint32 = w32.win32base.GetLastError()
ErrorValue: uint32 = win32.win32base.GetLastError()
# --- 字段转储:整数字段与指针字段统一 uint64() 后按 %llu 打印 ---
# uint64(结构体字段) 先例:TransPyV/App/lib/Stub/StubMerger/StubMergerStore.vp:79
# uint64(结构体字段) 先例:ViperC/App/lib/Stub/StubMerger/StubMergerStore.vp:79
# (uint64(scan_result.Entries))
# uint64(c.Addr(x)) 先例:ViperApps/ViperOS/VKernel/Kernel/sched/sched.vp:217
# (entry_addr: uint64 = uint64(c.Addr(_thread_entry)))
# uint64(malloc 缓冲) 先例:TransPyV/App/lib/Stub/StubMerger/StubMergerBuild.vp:1313 段
# uint64(malloc 缓冲) 先例:ViperC/App/lib/Stub/StubMerger/StubMergerBuild.vp:1313 段
# (uint64(ext_ptr))
# %llu 先例:includes/memhub.vp:792
CbValue: uint64 = uint64(StartupInfo.cb)
@@ -129,7 +128,7 @@ def ProbeCreateProcessA(Tag: str, AppName: str, CommandLine: str,
Tag, OkValue, uint64(ErrorValue), uint64(PreError),
AppDesc, CommandLine, CmdAddress)
# si 关键字段:cb / dwFlags / cbReserved2 / lpReserved2 / 三个 hStd* + si/pi 地址
# 字段定义:includes/w32/win32process.vp:42-60(STARTUPINFOA)
# 字段定义:includes/win32/win32process.vp:42-60(STARTUPINFOA)
stdio.printf("[CPW] %s si.cb=%llu si.dwFlags=%llu si.cbReserved2=%llu si.lpReserved2=%llu si.hStdIn=%llu si.hStdOut=%llu si.hStdErr=%llu si.addr=%llu pi.addr=%llu\n",
Tag, CbValue, FlagsValue, CbReserved2Value, Reserved2Value,
StdInputValue, StdOutputValue, StdErrorValue, SiAddress, PiAddress)
@@ -137,8 +136,8 @@ def ProbeCreateProcessA(Tag: str, AppName: str, CommandLine: str,
# 成功时关闭子进程句柄(避免句柄泄漏)
# 先例:includes/subprocess.vp:179-180
if OkValue != 0:
w32.win32base.CloseHandle(ProcessInfo.hThread)
w32.win32base.CloseHandle(ProcessInfo.hProcess)
win32.win32base.CloseHandle(ProcessInfo.hThread)
win32.win32base.CloseHandle(ProcessInfo.hProcess)
stdlib.free(CmdBuffer)
# stdout 全缓冲,逐组刷新以保证崩溃前输出可见
# 先例:ViperApps/ViperZipfileProject/App/main.vp:33(stdio.fflush(None))
@@ -152,22 +151,22 @@ def ProbeCreateProcessA(Tag: str, AppName: str, CommandLine: str,
def PrintCurrentDirectory() -> int:
"""[CPW] 对照组:打印本进程当前目录与错误码"""
# 缓冲与调用形态先例:includes/os/_win32.vp:76-91(getcwd:malloc → 调用 →
# buf[length] = 0 → str(buf));MAX_PATH 声明:includes/w32/win32base.vp:18
# buf[length] = 0 → str(buf));MAX_PATH 声明:includes/win32/win32base.vp:18
CwdLength: ULONG = 0
CwdError: uint32 = 0
CwdDesc: str = "<empty>"
CwdBuffer: bytes = stdlib.malloc(w32.win32base.MAX_PATH + 1)
CwdBuffer: bytes = stdlib.malloc(win32.win32base.MAX_PATH + 1)
if CwdBuffer is None:
stdio.printf("[CPW] control GetCurrentDirectoryA MALLOC-FAILED\n")
return 1
# GetCurrentDirectoryA 声明:includes/w32/win32file.vp:129
CwdLength = w32.win32file.GetCurrentDirectoryA(w32.win32base.MAX_PATH + 1, CwdBuffer)
CwdError = w32.win32base.GetLastError() # 必须紧跟调用读取
# GetCurrentDirectoryA 声明:includes/win32/win32file.vp:129
CwdLength = win32.win32file.GetCurrentDirectoryA(win32.win32base.MAX_PATH + 1, CwdBuffer)
CwdError = win32.win32base.GetLastError() # 必须紧跟调用读取
if CwdLength > 0:
if CwdLength < w32.win32base.MAX_PATH + 1:
if CwdLength < win32.win32base.MAX_PATH + 1:
CwdBuffer[CwdLength] = 0
else:
CwdBuffer[w32.win32base.MAX_PATH] = 0 # 缓冲区不足:截断显示
CwdBuffer[win32.win32base.MAX_PATH] = 0 # 缓冲区不足:截断显示
CwdDesc = str(CwdBuffer)
stdio.printf("[CPW] control GetCurrentDirectoryA len=%llu err=%llu cwd=%s\n",
uint64(CwdLength), uint64(CwdError), CwdDesc)
@@ -176,20 +175,15 @@ def PrintCurrentDirectory() -> int:
return 0
# ============================================================
# main: 入口
# @t.CExport + main() -> int 先例:ViperApps/ViperZipfileProject/App/main.vp:305-306
# ============================================================
@t.CExport
def main() -> int:
export def main() -> int:
# 控制台 UTF-8(中文注释/标签不乱码)
# 声明:includes/w32/win32console.vp:81-82
# 声明:includes/win32/win32console.vp:81-82
# 先例:ViperApps/ViperQtHelloWorld/App/main.vp:18+34-35
w32.win32console.SetConsoleOutputCP(PAGE_CODE)
w32.win32console.SetConsoleCP(PAGE_CODE)
win32.win32console.SetConsoleOutputCP(PAGE_CODE)
win32.win32console.SetConsoleCP(PAGE_CODE)
# stdio.printf 声明:includes/stdio.vp:4
stdio.printf("ViperCPW: CreateProcessA standalone repro (Viper host, not TransPyV.exe)\n")
stdio.printf("ViperCPW: CreateProcessA standalone repro (Viper host, not viperc.exe)\n")
PrintCurrentDirectory()
# #1 基线:NULL 应用名 + inh=1 + flags=0
@@ -197,14 +191,14 @@ def main() -> int:
# #2 同 #1 但 bInheritHandles=0
ProbeCreateProcessA("#2 NULL-app inh=0 flags=0", None, "llc --version", 0, 0)
# #3 同 #1 但 dwCreationFlags=CREATE_NO_WINDOW
# CREATE_NO_WINDOW 定义:includes/w32/win32process.vp:23
# CREATE_NO_WINDOW 定义:includes/win32/win32process.vp:23
ProbeCreateProcessA("#3 NULL-app inh=1 flags=CREATE_NO_WINDOW", None, "llc --version",
1, w32.win32process.CREATE_NO_WINDOW)
1, win32.win32process.CREATE_NO_WINDOW)
# #4 绝对路径应用名
ProbeCreateProcessA("#4 abs-app", "D:\\msys64\\clang64\\bin\\llc.exe", "llc --version", 1, 0)
# #5 报错里那条原样命令行(正斜杠相对路径,与 TPV 探针 #3 逐字相同)
# #5 报错里那条原样命令行(正斜杠相对路径,与 VPC 探针 #3 逐字相同)
ProbeCreateProcessA("#5 raw-cmdline(fwd slashes)", None,
"llc -filetype=obj -relocation-model=pic -o ./TransPyV/.tpv_build/output/xx/xxx.obj ./TransPyV/.tpv_build/temp/xx/xxx.ll",
"llc -filetype=obj -relocation-model=pic -o ./ViperC/__vpcbuild__/output/xx/xxx.obj ./ViperC/__vpcbuild__/temp/xx/xxx.ll",
1, 0)
stdio.printf("ViperCPW: DONE\n")
-13
View File
@@ -1,13 +0,0 @@
*.o
*.obj
*.exe
*.img
*.iso
**/__pycache__/
.cache/
*.pyc
# 编译
**/output/
**/temp/
**/__vpcbuild__/
-158
View File
@@ -1,158 +0,0 @@
# main2.vp — ViperFLProject fltk GUI 测试(回调 + 控件展示)
#
# 构建需 VPC/TPC 支持 project.vpj linker.static_libs 静态链接
# (本工程已配置链接 ../includes/fltk/cfltk/build 下的 cfltk2 + FLTK 静态库)。
#
# 回调说明:Viper 模块级函数可直接作为 Fl_Callback 传入(VPC 自动把函数名
# bitcast 为函数指针)。回调签名统一为 def cb(w: bytes, data: bytes),
# 其中 w 是触发回调的控件指针,data 是注册时传入的用户数据。
import t, c
import stdlib
import viperlib
import fltk
# 模块级全局:供回调访问 main() 中创建的控件指针(回调是 C 层直接调用,
# 拿不到 main 的局部变量);空指针用 None(C null),不能用整数 0。
term_ptr: t.CPtr = None
status_ptr: t.CPtr = None
scheme_idx: int = 0
theme_idx: int = 0
def on_btn_click(w: bytes, data: bytes):
"""按钮点击回调:改按钮文字 + 状态栏显示"""
fltk.Fl_Widget_set_label(w, "Clicked!")
fltk.Fl_Widget_redraw(w)
fltk.Fl_Output_set_value(status_ptr, "Button clicked")
def on_check(w: bytes, data: bytes):
"""复选框回调:状态栏显示勾选状态"""
v: int = fltk.Fl_Check_Button_is_checked(w)
if v != 0:
fltk.Fl_Output_set_value(status_ptr, "Checked")
else:
fltk.Fl_Output_set_value(status_ptr, "Unchecked")
def on_slider(w: bytes, data: bytes):
"""滑动条回调:状态栏实时显示数值"""
v: double = fltk.Fl_Slider_value(w)
buf: bytes = stdlib.malloc(32)
if buf is not None:
viperlib.snprintf(buf, 32, "Slider = %.2f", v)
fltk.Fl_Output_set_value(status_ptr, buf)
stdlib.free(buf)
def on_go(w: bytes, data: bytes):
"""按钮回调:读取输入框文本 → 终端打印"""
txt: str = fltk.Fl_Input_value(data)
fltk.Fl_Terminal_append(term_ptr, "input: ")
fltk.Fl_Terminal_append(term_ptr, txt)
fltk.Fl_Terminal_append(term_ptr, "\n")
def on_menu_open(w: bytes, data: bytes):
fltk.Fl_Output_set_value(status_ptr, "Menu: File/Open")
def on_menu_about(w: bytes, data: bytes):
fltk.Fl_Output_set_value(status_ptr, "Menu: Help/About")
def on_style(w: bytes, data: bytes):
"""风格切换回调:base→plastic→gtk+→gleam→oxy 循环"""
global scheme_idx
scheme_idx = scheme_idx + 1
if scheme_idx == 1:
fltk.Fl.set_scheme("plastic")
elif scheme_idx == 2:
fltk.Fl.set_scheme("gtk+")
elif scheme_idx == 3:
fltk.Fl.set_scheme("gleam")
elif scheme_idx == 4:
fltk.Fl.set_scheme("oxy")
else:
fltk.Fl.set_scheme("base")
scheme_idx = 0
fltk.Fl_Output_set_value(status_ptr, f"Scheme: {fltk.Fl.scheme_string()}")
def on_theme(w: bytes, data: bytes):
"""Fl_Theme 主题切换:base→Dark→Metro 循环"""
global theme_idx
theme_idx = theme_idx + 1
if theme_idx == 1:
fltk.Fl.set_theme_dark()
elif theme_idx == 2:
fltk.Fl.set_theme_metro()
else:
fltk.Fl.set_scheme("base")
fltk.Fl.reload_scheme()
theme_idx = 0
fltk.Fl_Output_set_value(status_ptr, f"Theme: {theme_idx}")
def on_tick(w: bytes, data: bytes):
"""定时器回调:每秒终端打印一次并续订"""
fltk.Fl_Terminal_append(term_ptr, "tick\n")
fltk.Fl.add_timeout(1.0, on_tick, 0)
def main() -> int:
global term_ptr, status_ptr
fltk.Fl_init_all()
win = fltk.Fl_Window(600, 460, "Viper FLTK Demo")
menubar = fltk.Fl_Menu_Bar(0, 0, 600, 25)
status = fltk.Fl_Output(10, 30, 580, 25)
btn = fltk.Fl_Button(10, 70, 120, 30, "Click Me")
check = fltk.Fl_Check_Button(150, 70, 100, 30, "Enable")
slider = fltk.Fl_Slider(280, 70, 200, 30)
input_ = fltk.Fl_Input(10, 110, 210, 30, "Type here")
go = fltk.Fl_Button(230, 110, 55, 30, "Go")
style = fltk.Fl_Button(295, 110, 55, 30, "Style")
theme = fltk.Fl_Button(360, 110, 55, 30, "Theme")
spinner = fltk.Fl_Spinner(425, 110, 100, 30)
browser = fltk.Fl_Browser(10, 150, 280, 140)
tree = fltk.Fl_Tree(300, 150, 280, 140)
term = fltk.Fl_Terminal(10, 300, 570, 120, "Terminal")
win.end()
# 保存全局指针(回调用)
term_ptr = t.CPtr(term._ptr)
status_ptr = t.CPtr(status._ptr)
# 菜单(回调 + 快捷键 Ctrl+O:0x4000 | 'o'(111))
menubar.add("File/Open", 0x4000 | 111, 0, on_menu_open)
menubar.add("Help/About", 0, 0, on_menu_about)
# 回调注册
btn.set_callback(on_btn_click, 0)
check.set_callback(on_check, 0)
slider.set_callback(on_slider, 0)
go.set_callback(on_go, input_._ptr)
style.set_callback(on_style, 0)
theme.set_callback(on_theme, 0)
# 数值微调框
spinner.set_minimum(0.0)
spinner.set_maximum(100.0)
spinner.set_step(1.0)
spinner.set_value(42.0)
# 列表 + 树
browser.add("Item 1")
browser.add("Item 2")
browser.add("Item 3")
tree.add("Root/Child A/Leaf 1")
tree.add("Root/Child B")
# 定时器
fltk.Fl.add_timeout(1.0, on_tick, 0)
win.show()
fltk.Fl.run()
return 0
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2026 TermiNexus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-289
View File
@@ -1,289 +0,0 @@
# Viper Template Project
[English](#english) | [中文](#中文)
A minimal, ready-to-use template for bootstrapping a new [Viper](../wiki/01-overview.md) language project on **Windows (x86_64-pc-windows-gnu)**.
Windows 平台(x86_64-pc-windows-gnu)下 [Viper](../wiki/01-overview.md) 语言新项目的最小可用模板。
---
<a id="中文"></a>
## 中文
### 这是什么
`ViperTemplateProject` 是基于 `Viper` 语言的 **Windows 桌面应用模板**。克隆本仓库后,你可以立即得到一个能够:
- 设置 Windows 控制台为 UTF-8 编码(避免中文乱码)
- 打印一行 `你好,世界`
- 通过 `llc + clang++`(mingw 工具链)编译为原生 `app.exe`
的最小 Viper 项目。
模板对应的语言编译器是 [TransPyC](https://git.gvsds.com/GVSDS/TransPyC);关于 Viper 语言本身的概览,参见 [TransPyC 仓库 wiki](../wiki/01-overview.md)。
### 目录结构
```
ViperTemplateProject/
├── .gitignore # 忽略 build/、*.o、*.exe、缓存等
├── README.md # 本文件
├── project.vpj # 项目配置(编译器、链接器、目标三元组等)
└── App/
└── main.py # 入口文件
```
### 前提条件
| 工具 | 说明 |
|------|------|
| `python` ≥ 3.10 | 用于运行 TransPyC 编译器 |
| `llc` | LLVM 静态编译器,编译 `.ll` → `.o` |
| `clang++` | mingw 工具链下的链接驱动(`x86_64-w64-mingw32`) |
| `TransPyC` 仓库 | 本编译器,克隆在 `ViperTemplateProject/..` 同一目录下 |
环境变量建议(PowerShell):
```powershell
$env:PATH = "D:\msys64\mingw64\bin;D:\LLVM\bin;$env:PATH"
```
### 快速开始
```powershell
# 1. 确保目录布局正确
# D:\Users\TermiNexus\Desktop\TransPyC\
# ├── TransPyC\ # 编译器仓库
# └── ViperTemplateProject\ # 本仓库
cd D:\Users\TermiNexus\Desktop\TransPyC\ViperTemplateProject
# 2. 编译并运行
python ..\TransPyC\Projectrans.py `
--project .\project.vpj `
--clean-cache=false
# 3. 产物位于 output\app.exe
.\output\app.exe
```
预期输出:
```
你好,世界
```
### 编写你的第一个程序
打开 `App/main.py`,替换 `print("你好,世界")` 为任意合法 Viper 表达式。例如:
```python
import w32.win32console as w32cmd
import t, c
pagecode: t.CDefine = 65001
@t.CExport
def main() -> int:
w32cmd.SetConsoleOutputCP(pagecode)
w32cmd.SetConsoleCP(pagecode)
# 你的代码从这里开始
a: t.CInt = 1
b: t.CInt = 2
print("a + b =", a + b)
return 0
```
### 修改项目配置
打开 `project.vpj`,按需调整:
| 字段 | 含义 | 模板默认值 |
|------|------|-----------|
| `name` | 项目名(仅显示用) | `Standard Template Project` |
| `source_dir` | 源文件根目录 | `./App` |
| `temp_dir` | 中间产物目录(`.pyi` / `.stub.ll`) | `./temp` |
| `output_dir` | 最终产物目录 | `./output` |
| `compiler.cmd` | 汇编器 | `llc` |
| `compiler.flags` | 传给 `llc` 的参数 | `["-filetype=obj", "-relocation-model=pic"]` |
| `linker.cmd` | 链接器 | `clang++` |
| `linker.flags` | 链接器参数 | mingw 标准库 + `kernel32` |
| `linker.output` | 产物名 | `app.exe` |
| `includes` | 标准库搜索路径 | `["../includes"]` |
| `target.triple` | LLVM 目标三元组 | `x86_64-pc-windows-gnu` |
| `target.datalayout` | LLVM 数据布局 | mingw 默认 |
| `options.target` | 后端目标 | `llvm` |
| `options.strict_mode` | 严格类型检查 | `true` |
| `options.slice_level` | 切片级别 | `3` |
### 跨平台使用
本模板默认针对 **Windows + mingw**。若需在其他平台使用,修改 `project.vpj` 的 `compiler.cmd` / `linker.cmd` / `target.triple`:
- **Linux (x86_64)**:`triple = "x86_64-unknown-linux-gnu"`,`linker.cmd = "clang++"`
- **macOS (x86_64)**:`triple = "x86_64-apple-darwin*"`,`linker.cmd = "clang++"`
- **裸机 x86_64 (ViperOS)**:`triple = "x86_64-none-elf"`,`linker.cmd = "ld.lld"`,需要 `isr.s` 启动文件
### 常见问题
**Q: 报错 `cannot find includes/os` / `cannot find includes/atom` 之类?**
A: 检查 `project.vpj` 中的 `includes` 字段是否指向 `TransPyC/includes`。`../includes` 假设本仓库与 `TransPyC` 仓库同级。
**Q: 输出 `app.exe` 双击闪退?**
A: 在终端中运行 `.\output\app.exe` 而非双击,并确认 mingw 与 LLVM 在 `PATH` 中。
**Q: 想完全清理构建产物?**
A: 使用 `python ..\TransPyC\Projectrans.py --project .\project.vpj --clean-cache` 清理缓存;删除 `output/` 和 `temp/` 即可彻底重置。
**Q: `StandardTemplateProject` 和本仓库是什么关系?**
A: 本仓库 `ViperTemplateProject` 是 `StandardTemplateProject` 的独立仓库版本。如果你仍想保留 `StandardTemplateProject` 放在 `TransPyC` 仓库内,只需把它加入 `TransPyC/.gitignore` 的 `# 排除独立仓库的目录` 小节。
### 进一步阅读
- [Viper 语言概览](../wiki/01-overview.md)
- [类型系统](../wiki/02-type-system.md)
- [OOP 与对象模型](../wiki/06-oop.md)
- [自举路线图](../wiki/13-bootstrapping.md)
---
<a id="english"></a>
## English
### What is this
`ViperTemplateProject` is a **Windows desktop application template** based on the [Viper](../wiki/01-overview.md) language. After cloning, you immediately have a minimal Viper project that:
- Sets the Windows console to UTF-8 (avoids mojibake for non-ASCII output)
- Prints `你好,世界`
- Compiles to a native `app.exe` via `llc + clang++` (mingw toolchain)
The corresponding compiler is [TransPyC](https://git.gvsds.com/GVSDS/TransPyC). For a Viper language overview, see the [TransPyC wiki](../wiki/01-overview.md).
### Directory layout
```
ViperTemplateProject/
├── .gitignore # ignores build/, *.o, *.exe, caches, etc.
├── README.md # this file
├── project.vpj # project config (compiler, linker, target triple, ...)
└── App/
└── main.py # entry point
```
### Prerequisites
| Tool | Notes |
|------|-------|
| `python` ≥ 3.10 | runs the TransPyC compiler |
| `llc` | LLVM static compiler, `.ll` → `.o` |
| `clang++` | mingw toolchain driver (`x86_64-w64-mingw32`) |
| `TransPyC` repo | the compiler, cloned as a sibling of `ViperTemplateProject` |
Suggested `PATH` (PowerShell):
```powershell
$env:PATH = "D:\msys64\mingw64\bin;D:\LLVM\bin;$env:PATH"
```
### Quick start
```powershell
# 1. Make sure the layout is right
# D:\Users\TermiNexus\Desktop\TransPyC\
# ├── TransPyC\ # compiler repo
# └── ViperTemplateProject\ # this repo
cd D:\Users\TermiNexus\Desktop\TransPyC\ViperTemplateProject
# 2. Build and run
python ..\TransPyC\Projectrans.py `
--project .\project.vpj `
--clean-cache=false
# 3. Output is at output\app.exe
.\output\app.exe
```
Expected output:
```
你好,世界
```
### Writing your first program
Open `App/main.py` and replace `print("你好,世界")` with any valid Viper expression. Example:
```python
import w32.win32console as w32cmd
import t, c
pagecode: t.CDefine = 65001
@t.CExport
def main() -> int:
w32cmd.SetConsoleOutputCP(pagecode)
w32cmd.SetConsoleCP(pagecode)
# your code goes here
a: t.CInt = 1
b: t.CInt = 2
print("a + b =", a + b)
return 0
```
### Editing project.vpj
| Field | Meaning | Template default |
|-------|---------|------------------|
| `name` | display name | `Standard Template Project` |
| `source_dir` | source root | `./App` |
| `temp_dir` | intermediate artifacts (`.pyi` / `.stub.ll`) | `./temp` |
| `output_dir` | final artifacts | `./output` |
| `compiler.cmd` | assembler | `llc` |
| `compiler.flags` | flags passed to `llc` | `["-filetype=obj", "-relocation-model=pic"]` |
| `linker.cmd` | linker | `clang++` |
| `linker.flags` | linker flags | mingw stdlib + `kernel32` |
| `linker.output` | output name | `app.exe` |
| `includes` | standard library search paths | `["../includes"]` |
| `target.triple` | LLVM target triple | `x86_64-pc-windows-gnu` |
| `target.datalayout` | LLVM data layout | mingw default |
| `options.target` | backend target | `llvm` |
| `options.strict_mode` | strict type checking | `true` |
| `options.slice_level` | slice level | `3` |
### Cross-platform use
This template targets **Windows + mingw** by default. To use on another platform, adjust `compiler.cmd` / `linker.cmd` / `target.triple` in `project.vpj`:
- **Linux (x86_64)**: `triple = "x86_64-unknown-linux-gnu"`, `linker.cmd = "clang++"`
- **macOS (x86_64)**: `triple = "x86_64-apple-darwin*"`, `linker.cmd = "clang++"`
- **Bare-metal x86_64 (ViperOS)**: `triple = "x86_64-none-elf"`, `linker.cmd = "ld.lld"`, requires `isr.s` startup
### FAQ
**Q: Error like `cannot find includes/os` or `cannot find includes/atom`?**
A: Check that the `includes` field in `project.vpj` points to the `TransPyC/includes` directory. `../includes` assumes this repo lives next to `TransPyC`.
**Q: `app.exe` flashes and closes on double-click?**
A: Run `.\output\app.exe` from a terminal, not by double-clicking. Make sure mingw and LLVM are on `PATH`.
**Q: How to fully clean build artifacts?**
A: `python ..\TransPyC\Projectrans.py --project .\project.vpj --clean-cache` clears the cache. To reset everything, also delete `output/` and `temp/`.
**Q: What is the relationship with `StandardTemplateProject`?**
A: This repo `ViperTemplateProject` is the standalone-repo version of `StandardTemplateProject`. If you still want `StandardTemplateProject` to live inside the `TransPyC` repo, add it to the `# 排除独立仓库的目录` section in `TransPyC/.gitignore`.
### Further reading
- [Viper language overview](../wiki/01-overview.md)
- [Type system](../wiki/02-type-system.md)
- [OOP and object model](../wiki/06-oop.md)
- [Bootstrapping roadmap](../wiki/13-bootstrapping.md)
-58
View File
@@ -1,58 +0,0 @@
{
"name": "ViperFLProject - FLTK GUI Test",
"version": "1.0.0",
"source_dir": "./App",
"build_dir": "./__vpcbuild__",
"compiler": {
"cmd": "llc",
"flags": [
"-filetype=obj",
"-relocation-model=pic"
]
},
"linker": {
"cmd": "clang++.exe",
"flags": [
"-fuse-ld=lld",
"-lmsvcrt",
"-lucrt",
"-lpthread",
"-lmingwex",
"-lkernel32",
"-lcomdlg32",
"-lcomctl32",
"-lole32",
"-luuid",
"-lwinmm",
"-lgdi32",
"-lgdiplus",
"-ladvapi32",
"-lshell32",
"-lws2_32",
"-luser32",
"-lwinspool"
],
"static_libs": [
"../../includes/fltk/cfltk/build/libcfltk2.a",
"../../includes/fltk/cfltk/build/libfltk_theme.a",
"../../includes/fltk/cfltk/build/fltk/lib/libfltk_images.a",
"../../includes/fltk/cfltk/build/fltk/lib/libfltk.a",
"../../includes/fltk/cfltk/build/fltk/lib/libfltk_png.a",
"../../includes/fltk/cfltk/build/fltk/lib/libfltk_jpeg.a",
"../../includes/fltk/cfltk/build/fltk/lib/libfltk_z.a"
],
"output": "app.exe"
},
"includes": [
"../../includes"
],
"target": {
"triple": "x86_64-pc-windows-gnu",
"datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}
+33 -35
View File
@@ -14,7 +14,6 @@
# 线程模型: 训练在 vthreading 后台线程跑, 只写 MnistNet 的 prog_* 进度总线;
# 主线程用 QTimer 轮询刷新界面, 不与之共享 Qt 对象。
# ============================================================
import t, c
from stdint import *
import stdlib
import string
@@ -23,8 +22,8 @@ import vthreading
import memhub
import torch
import viperlib
import w32.fileio as fileio
import w32.win32file as win32file
import win32.fileio as fileio
import win32.win32file as win32file
import lib.MnistData as MnistData
import lib.MnistNet as MnistNet
@@ -39,8 +38,7 @@ define CHART_KEEP = 800
define ANIM_SPAN = 16
@t.NoVTable
class AppGui:
novirtual class AppGui:
# ---- 应用与主窗 ----
App: vqt6.QApplication*
Win: vqt6.QMainWindow*
@@ -78,12 +76,12 @@ class AppGui:
Pbar: vqt6.QProgressBar*
Log: vqt6.QPlainTextEdit*
Chart: vqt6.ChartView*
LineLoss: t.CPtr
LineAcc: t.CPtr
LineLoss: rawptr
LineAcc: rawptr
ChartSynced: int
# 验证集曲线(每 epoch 一个点, 与训练曲线分开同步)
LineValLoss: t.CPtr
LineValAcc: t.CPtr
LineValLoss: rawptr
LineValAcc: rawptr
ChartSyncedVal: int
# ---- 推理页 ----
@@ -122,7 +120,7 @@ class AppGui:
# 只存线程句柄: Thread 对象本身是创建帧的栈 alloca, 跨帧持有会悬垂;
# 句柄由 on_start_train 在 Thread 尚存活时取出, 之后用模块级
# wait_handle/close_handle 操作。
ThreadH: t.CPtr
ThreadH: rawptr
XBuf: double*
YBuf: int*
NTrain: sizet
@@ -138,8 +136,8 @@ class AppGui:
Img28: bytes
Sample28: bytes
HaveSample: int
TestRaw: t.CPtr
TestLbl: t.CPtr
TestRaw: rawptr
TestLbl: rawptr
TestN: sizet
Probs: double*
HiddenBuf: double*
@@ -337,9 +335,9 @@ class AppGui:
self.LblStatus = self.make_label("池: 512MB 数据目录: data", "color:#8a93a1;")
# statusBar() 返回裸指针: 返回包装类对象(按值 struct)的方法在跨模块
# 调用时返回类型无法精确解析(编译器 fail-fast), 故此处直连 FFI。
# 注解写 t.CPtr 而非 vqt6.QtStatusBarPtr —— 跨模块 typedef 注解
# 注解写 rawptr 而非 vqt6.QtStatusBarPtr —— 跨模块 typedef 注解
# 不在解析白名单内, 会走"类型注解解析失败"诊断路径。
sb: t.CPtr = self.Win.statusBar()
sb: rawptr = self.Win.statusBar()
vqt6.vqt6_qstatusbar_add_widget(sb, self.LblStatus._ptr, 0)
self.TimerPoll = vqt6.QTimer()
@@ -561,8 +559,8 @@ class AppGui:
buf: bytes = stdlib.malloc(1024)
if buf is None:
return "."
# hModule 传空指针用 t.CPtr(0): HANDLE 是 VOIDPTR, 整数 0 不能隐式转指针
win32file.GetModuleFileNameA(t.CPtr(0), buf, ULONG(1024))
# hModule 传空指针用 None: HANDLE 是 VOIDPTR, 整数 0 不能隐式转指针
win32file.GetModuleFileNameA(None, buf, ULONG(1024))
n: sizet = string.strlen(buf)
# 第一步: 从右往左找最后一个路径分隔符, 在那里截断(去掉文件名本身)
cut: sizet = 0
@@ -862,7 +860,7 @@ class AppGui:
self.BtnTrain.setEnabled(0)
self.BtnStop.setEnabled(1)
self.Training = 1
th: vthreading.Thread* = vthreading.Thread(WorkerTrain, t.CPtr(0))
th: vthreading.Thread* = vthreading.Thread(WorkerTrain, None)
th.start()
self.ThreadH = th.handle # 帧内取出句柄, 之后不再持 Thread 对象
self.TimerPoll.start(120)
@@ -1024,7 +1022,7 @@ class AppGui:
return
idx: sizet = MnistNet.rand_idx(self.TestN)
lb: uchar* = self.TestLbl
src: t.CPtr = self.TestRaw + 16 + idx * MnistData.N_IN
src: rawptr = self.TestRaw + 16 + idx * MnistData.N_IN
string.memcpy(self.Sample28, src, MnistData.N_IN)
self.HaveSample = 1
self.Canvas.setImage28(self.Sample28)
@@ -1144,87 +1142,87 @@ class AppGui:
_g_ui: AppGui*
def CbDownload(slot_id: int, data: t.CPtr):
def CbDownload(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_download()
def CbCopyLink(slot_id: int, data: t.CPtr):
def CbCopyLink(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_copy_link()
def CbPickDir(slot_id: int, data: t.CPtr):
def CbPickDir(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_pick_dir()
def CbCheckData(slot_id: int, data: t.CPtr):
def CbCheckData(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.check_data()
def CbPickLocal(slot_id: int, data: t.CPtr):
def CbPickLocal(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_pick_local()
def CbArchChanged(slot_id: int, data: t.CPtr):
def CbArchChanged(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_arch_changed()
def CbStartTrain(slot_id: int, data: t.CPtr):
def CbStartTrain(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_start_train()
def CbStopTrain(slot_id: int, data: t.CPtr):
def CbStopTrain(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_stop_train()
def CbZoomChart(slot_id: int, data: t.CPtr):
def CbZoomChart(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_zoom_chart()
def CbPollTraining(slot_id: int, data: t.CPtr):
def CbPollTraining(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_poll_training()
def CbLoadModel(slot_id: int, data: t.CPtr):
def CbLoadModel(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_load_model()
def CbPickSample(slot_id: int, data: t.CPtr):
def CbPickSample(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_pick_sample()
def CbClearCanvas(slot_id: int, data: t.CPtr):
def CbClearCanvas(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_clear_canvas()
def CbCanvasChanged(slot_id: int, data: t.CPtr):
def CbCanvasChanged(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_canvas_changed()
def CbUse(slot_id: int, data: t.CPtr):
def CbUse(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_use()
def CbAnimTick(slot_id: int, data: t.CPtr):
def CbAnimTick(slot_id: int, data: rawptr):
if _g_ui is not None:
_g_ui.on_anim_tick()
def WorkerTrain(arg: t.CPtr) -> t.CPtr:
def WorkerTrain(arg: rawptr) -> rawptr:
"""后台训练线程入口"""
if _g_ui is not None:
_g_ui.worker_train()
+8 -9
View File
@@ -11,14 +11,13 @@
# 说明: fileio.File.read_all 契约是"最多读 max_count-1 字节并补 NUL",
# 故所有整读调用都按 目标长度+1 申请与请求, 否则会丢最后一字节。
# ============================================================
import t, c
from stdint import *
import stdlib
import string
import stdio
import viperlib
import os
import w32.fileio as fileio
import win32.fileio as fileio
import gzip
import requests
import requests.https as reqhttps
@@ -34,20 +33,20 @@ MNIST_BASE: str = "https://ossci-datasets.s3.amazonaws.com/mnist"
# ============================================================
# 基础读写
# ============================================================
def be32(buf: t.CPtr, off: sizet) -> uint32:
def be32(buf: rawptr, off: sizet) -> uint32:
"""IDX 大端 u32"""
ub: uchar* = buf
return (uint32(ub[off]) << 24) | (uint32(ub[off + 1]) << 16) \
| (uint32(ub[off + 2]) << 8) | uint32(ub[off + 3])
def read_file_all(path: str, fsize: sizet) -> t.CPtr:
def read_file_all(path: str, fsize: sizet) -> rawptr:
"""整读文件到 malloc 缓冲, 失败返回 None
注意: read_all 最多读 max_count-1 字节并补 NUL, 故按 fsize+1 申请/请求"""
f: fileio.File* = fileio.File(path, fileio.MODE.R)
if f.closed:
return None
buf: t.CPtr = stdlib.malloc(fsize + 1)
buf: rawptr = stdlib.malloc(fsize + 1)
if buf is None:
f.close()
return None
@@ -102,7 +101,7 @@ def idx_usable(path: str, is_lb: int) -> int:
if sz < 16:
f.close()
return 0
hdr: t.CPtr = stdlib.malloc(64)
hdr: rawptr = stdlib.malloc(64)
if hdr is None:
f.close()
return 0
@@ -161,7 +160,7 @@ def download_inflate(url: str, out_path: str) -> int:
if cap < 1024:
cap = 1024
# 保险起见: ISIZE 与实际不符时报"目标缓冲不足", 就翻倍重试(正常情况下一次成功)
dst: t.CPtr = None
dst: rawptr = None
n: LONG = 0
while True:
dst = stdlib.malloc(cap)
@@ -378,7 +377,7 @@ def load_mnist_images(path: str, dst: double*,
f.close()
return 0
f.close()
whole: t.CPtr = read_file_all(path, sizet(sz))
whole: rawptr = read_file_all(path, sizet(sz))
if whole is None:
stdio.printf("[mnist] 读取失败: %s\n", path)
return 0
@@ -426,7 +425,7 @@ def load_mnist_labels(path: str, dst: int*,
f.close()
return 0
f.close()
whole: t.CPtr = read_file_all(path, sizet(sz))
whole: rawptr = read_file_all(path, sizet(sz))
if whole is None:
return 0
# IDX 标签魔数: 00 00 08 01
+55 -57
View File
@@ -12,7 +12,6 @@
# 进度总线: train_run 每个记录点写入 prog_* 数组, UI 线程只读轮询;
# prog_stop 置 1 可请求提前结束(训练线程在下一个 step 边界退出)。
# ============================================================
import t, c
from stdint import *
import stdlib
import string
@@ -20,7 +19,7 @@ import stdio
import math
import memhub
import torch
import w32.fileio as fileio
import win32.fileio as fileio
# 输入/输出维度与数据层保持单一定义, 避免两处各写一份导致漂移
from lib.MnistData import N_IN, N_OUT
@@ -80,16 +79,15 @@ define GRAPH_IN_SIDE = 8
define NET_BYTES = 480
def _zeros_raw(pool: memhub.MemBuddy*, nb: sizet) -> t.CPtr:
def _zeros_raw(pool: memhub.MemBuddy*, nb: sizet) -> rawptr:
"""从内存池分配并清零一块裸缓冲(动量缓冲用; pool.alloc 不保证清零)"""
p: t.CPtr = pool.alloc(nb)
p: rawptr = pool.alloc(nb)
if p is not None:
string.memset(p, 0, nb)
return p
@t.NoVTable
class Net:
novirtual class Net:
# --- 通用 (1-4) ---
Arch: sizet # 1 ARCH_MLP / ARCH_CNN
Hidden: sizet # 2 MLP 隐藏层宽度; CNN 的 FC1 宽度
@@ -110,28 +108,28 @@ class Net:
wf2: torch.Tensor* # 15
bf2: torch.Tensor* # 16
# --- CNN 前向激活 (17-26) ---
BZ1: t.CPtr # 17 conv1 输出 c1*28*28
BA1: t.CPtr # 18 relu(BZ1)
BP1: t.CPtr # 19 pool1 输出 c1*14*14
BZ2: t.CPtr # 20 conv2 输出 c2*14*14
BA2: t.CPtr # 21 relu(BZ2)
BP2: t.CPtr # 22 pool2 输出 c2*7*7(FC1 的输入)
BZ3: t.CPtr # 23 fc1 输出 h
BA3: t.CPtr # 24 relu(BZ3)
BLG: t.CPtr # 25 logits 10
BDA3: t.CPtr # 26 fc1 输出的梯度 h
BZ1: rawptr # 17 conv1 输出 c1*28*28
BA1: rawptr # 18 relu(BZ1)
BP1: rawptr # 19 pool1 输出 c1*14*14
BZ2: rawptr # 20 conv2 输出 c2*14*14
BA2: rawptr # 21 relu(BZ2)
BP2: rawptr # 22 pool2 输出 c2*7*7(FC1 的输入)
BZ3: rawptr # 23 fc1 输出 h
BA3: rawptr # 24 relu(BZ3)
BLG: rawptr # 25 logits 10
BDA3: rawptr # 26 fc1 输出的梯度 h
# --- CNN 反向梯度 (27-34) ---
BDLG: t.CPtr # 27 dlogits 10
BGZ3: t.CPtr # 28 fc1 反传的预激活梯度 h
BGP2: t.CPtr # 29 fc1 反传的输入梯度 49*c2
BGA2: t.CPtr # 30 relu2 的输入梯度 c2*14*14
BGZ2: t.CPtr # 31 pool2 反传结果 c2*14*14
BGP1: t.CPtr # 32 conv2 反传的输入梯度 c1*14*14
BGA1: t.CPtr # 33 relu1 的输入梯度 c1*28*28
BGZ1: t.CPtr # 34 pool1 反传结果 c1*28*28
BDLG: rawptr # 27 dlogits 10
BGZ3: rawptr # 28 fc1 反传的预激活梯度 h
BGP2: rawptr # 29 fc1 反传的输入梯度 49*c2
BGA2: rawptr # 30 relu2 的输入梯度 c2*14*14
BGZ2: rawptr # 31 pool2 反传结果 c2*14*14
BGP1: rawptr # 32 conv2 反传的输入梯度 c1*14*14
BGA1: rawptr # 33 relu1 的输入梯度 c1*28*28
BGZ1: rawptr # 34 pool1 反传结果 c1*28*28
# --- CNN 池化 argmax (35-36) ---
ARG1: t.CPtr # 35 pool1 窗口内下标(int) c1*14*14
ARG2: t.CPtr # 36 pool2 窗口内下标(int) c2*7*7
ARG1: rawptr # 35 pool1 窗口内下标(int) c1*14*14
ARG2: rawptr # 36 pool2 窗口内下标(int) c2*7*7
# --- FC 层 2D 视图壳 (37-44, 每步复用不再分配) ---
VF: torch.Tensor* # 37 (1, 49*c2) 复用 BP2
VZ3: torch.Tensor* # 38 (1, h) 复用 BZ3
@@ -148,27 +146,27 @@ class Net:
GBF2: torch.Tensor* # 48
# --- MLP 动量缓冲 (49-52) ---
# 重球法的速度项, 与参数同元素个数, 训练开始前清零; 只训练时用
Mw1: t.CPtr # 49
Mb1: t.CPtr # 50
Mw2: t.CPtr # 51
Mb2: t.CPtr # 52
Mw1: rawptr # 49
Mb1: rawptr # 50
Mw2: rawptr # 51
Mb2: rawptr # 52
# --- CNN 动量缓冲 (53-60) ---
Mwc1: t.CPtr # 53
Mbc1: t.CPtr # 54
Mwc2: t.CPtr # 55
Mbc2: t.CPtr # 56
Mwf1: t.CPtr # 57
Mbf1: t.CPtr # 58
Mwf2: t.CPtr # 59
Mbf2: t.CPtr # 60
Mwc1: rawptr # 53
Mbc1: rawptr # 54
Mwc2: rawptr # 55
Mbc2: rawptr # 56
Mwf1: rawptr # 57
Mbf1: rawptr # 58
Mwf2: rawptr # 59
Mbf2: rawptr # 60
def __new__(self, pool: memhub.MemBuddy*, hidden: sizet,
arch: sizet = ARCH_MLP, c1: sizet = 0,
c2: sizet = 0) -> t.CPtr:
c2: sizet = 0) -> rawptr:
# 网络对象要跨帧存活(Qt 面板里存在 AppGui.Net 字段上, 训练线程还要读),
# 而类实例默认是创建函数的栈 alloca —— 必须堆分配。
# 整体清零: 未用到的架构字段保持 NULL, 由 ok()/前向按 arch 判断。
p: t.CPtr = pool.alloc(NET_BYTES)
p: rawptr = pool.alloc(NET_BYTES)
if p is not None:
string.memset(p, 0, NET_BYTES)
return p
@@ -413,7 +411,7 @@ def batch_accuracy(logits: torch.Tensor*,
# 反向顺序: logits -> fc2 -> relu3 -> fc1 -> pool2 -> relu2 -> conv2
# -> pool1 -> relu1 -> conv1
# ============================================================
def relu_raw(x: t.CPtr, out: t.CPtr, n: sizet):
def relu_raw(x: rawptr, out: rawptr, n: sizet):
"""out = max(0, x) 逐元素(裸缓冲版, 免去给 4D 缓冲造 2D 视图壳)"""
if x is None or out is None:
return
@@ -427,7 +425,7 @@ def relu_raw(x: t.CPtr, out: t.CPtr, n: sizet):
od[i] = double(0.0)
def relu_raw_bwd(za: t.CPtr, g: t.CPtr, gz: t.CPtr, n: sizet):
def relu_raw_bwd(za: rawptr, g: rawptr, gz: rawptr, n: sizet):
"""gz = g * (za > 0) —— za 是 relu 的输入(预激活)缓冲"""
if za is None or g is None or gz is None:
return
@@ -441,7 +439,7 @@ def relu_raw_bwd(za: t.CPtr, g: t.CPtr, gz: t.CPtr, n: sizet):
od[i] = double(0.0)
def cnn_forward(net: Net*, img: t.CPtr):
def cnn_forward(net: Net*, img: rawptr):
"""img: 784 个 0..1 归一化灰度(double 缓冲); logits 落在 net.BLG"""
if net is None or img is None:
return
@@ -485,7 +483,7 @@ def cnn_zero_grads(net: Net*):
torch.zero_grad(net.bf2)
def cnn_backward(net: Net*, img: t.CPtr, label: int) -> double:
def cnn_backward(net: Net*, img: rawptr, label: int) -> double:
"""反向累积一个样本的参数梯度, 返回该样本交叉熵损失
img 必须与 cnn_forward 传入的是同一块缓冲(conv1 反向要用它)"""
if net is None:
@@ -500,7 +498,7 @@ def cnn_backward(net: Net*, img: t.CPtr, label: int) -> double:
s1: sizet = sizet(CNN_SIDE1)
n1: sizet = c1 * s * s
n2: sizet = c2 * s1 * s1
loss_buf: t.CPtr = stdlib.malloc(8)
loss_buf: rawptr = stdlib.malloc(8)
lab: int* = stdlib.malloc(4)
if loss_buf is None or lab is None:
if loss_buf is not None:
@@ -576,7 +574,7 @@ def _write_tensor(f: fileio.File*, x: torch.Tensor*) -> sizet:
return nb
def _read_tensor(whole: t.CPtr, off: sizet,
def _read_tensor(whole: rawptr, off: sizet,
x: torch.Tensor*) -> sizet:
"""从整块缓冲的 off 处读一个张量, 返回新偏移(0 表示失败)"""
nb: sizet = _bytes_of(x)
@@ -656,7 +654,7 @@ def load_model_mk1(path: str, w1: torch.Tensor*,
stdio.printf("[model] 文件过小: %s\n", path)
return 1
# read_all 契约: 最多读 max_count-1 字节, 故 +1
whole: t.CPtr = stdlib.malloc(sizet(sz) + 1)
whole: rawptr = stdlib.malloc(sizet(sz) + 1)
if whole is None:
return 1
fr: fileio.File* = fileio.File(path, fileio.MODE.R)
@@ -756,7 +754,7 @@ def load_model_mk2(path: str, net: Net*) -> int:
stdio.printf("[model] 尺寸不匹配(期望字节=%zu, 实得=%zu)\n",
expect, sizet(sz))
return 1
whole: t.CPtr = stdlib.malloc(sizet(sz) + 1)
whole: rawptr = stdlib.malloc(sizet(sz) + 1)
if whole is None:
return 1
fr: fileio.File* = fileio.File(path, fileio.MODE.R)
@@ -994,11 +992,11 @@ def ce_from_logits(lg: double*, off: sizet, c: sizet,
mx = d[off + j]
s: double = double(0.0)
for j in range(c):
s = s + vipermath.exp(d[off + j] - mx)
s = s + math.exp(d[off + j] - mx)
li: sizet = sizet(label)
if li >= c:
li = 0
return -(d[off + li] - mx - vipermath.log(s))
return -(d[off + li] - mx - math.log(s))
def epoch_lr(lr: double, epoch: sizet, epochs: sizet) -> double:
@@ -1307,7 +1305,7 @@ def _train_cnn(pool: memhub.MemBuddy*,
# 单样本输入缓冲: MnistData.load_mnist_images 已把像素归一化到 0..1,
# 这里只做搬运(可选平移增强), 不再二次除以 255 —— conv 只吃 double,
# 且反向 conv1 要用与 forward 同一块缓冲。
in_buf: t.CPtr = stdlib.malloc(N_IN * 8)
in_buf: rawptr = stdlib.malloc(N_IN * 8)
if in_buf is None:
prog_fail = 1
prog_done = 1
@@ -1513,11 +1511,11 @@ def predict_full(w1: torch.Tensor*, b1: torch.Tensor*,
mx = lgl[ci]
s: double = double(0.0)
for ci in range(N_OUT):
s = s + vipermath.exp(lgl[ci] - mx)
s = s + math.exp(lgl[ci] - mx)
best: int = 0
bv: double = double(-1.0)
for ci in range(N_OUT):
p: double = vipermath.exp(lgl[ci] - mx) / s
p: double = math.exp(lgl[ci] - mx) / s
if out_probs is not None:
op: double* = out_probs
op[ci] = p
@@ -1541,7 +1539,7 @@ def predict_cnn(net: Net*, img28: bytes,
if net is None or img28 is None:
return -1
h: sizet = net.Hidden
xbuf: t.CPtr = stdlib.malloc(N_IN * 8)
xbuf: rawptr = stdlib.malloc(N_IN * 8)
if xbuf is None:
return -1
x: double* = xbuf
@@ -1558,13 +1556,13 @@ def predict_cnn(net: Net*, img28: bytes,
mx = ld[ci]
s: double = double(0.0)
for ci in range(N_OUT):
s = s + vipermath.exp(ld[ci] - mx)
s = s + math.exp(ld[ci] - mx)
if s <= double(0.0):
return -1
best: int = 0
bv: double = double(-1.0)
for ci in range(N_OUT):
p: double = vipermath.exp(ld[ci] - mx) / s
p: double = math.exp(ld[ci] - mx) / s
if out_probs is not None:
op: double* = out_probs
op[ci] = p
@@ -1600,7 +1598,7 @@ def evaluate_cnn(net: Net*,
return double(0.0)
xd: double* = xbuf
yd: int* = ybuf
in_buf: t.CPtr = stdlib.malloc(N_IN * 8)
in_buf: rawptr = stdlib.malloc(N_IN * 8)
if in_buf is None:
return double(0.0)
ind: double* = in_buf
+3 -4
View File
@@ -21,7 +21,6 @@
# 权重形状同 PyTorch: Conv(out,in,k,k) / Linear(out,in), b(out)
# 计算: torch-lite 线性前向走 vipersimd.simd_dot4d, relu 走 simd_max4d
# ============================================================
import t, c
from stdint import *
import stdlib
import string
@@ -31,7 +30,7 @@ import memhub
import argparse
import requests
import torch
import w32.win32console as w32cmd
import win32.win32console as win32cmd
import lib.MnistData as MnistData
import lib.MnistNet as MnistNet
import lib.AppGui as AppGui
@@ -270,8 +269,8 @@ def run_cli(pool: memhub.MemBuddy*) -> int:
# ============================================================
def main() -> int:
# UTF-8 控制台(与 ViperC main 一致)
w32cmd.SetConsoleOutputCP(CODE_PAGE)
w32cmd.SetConsoleCP(CODE_PAGE)
win32cmd.SetConsoleOutputCP(CODE_PAGE)
win32cmd.SetConsoleCP(CODE_PAGE)
# 主内存池: CLI 与 GUI 共用, 先建池再解析 argv(GUI 分支也需要池)
arena: bytes = stdlib.malloc(POOL_SIZE)
+4 -6
View File
@@ -6,12 +6,10 @@
#
# 链接目标尚未定(msys2 预装 3.6.2 动态库 或 openssl_358_C 自编译),
# 配好 project.vpj 的 -lcrypto/-lssl(或 static_libs)后才可链接运行。
import t, c
import stdlib
import binascii
import openssl
import w32.win32console as w32cmd
import viperlib
import win32.win32console as win32cmd
def print_digest(name: str, buf: bytes, length: int):
@@ -20,8 +18,8 @@ def print_digest(name: str, buf: bytes, length: int):
def main() -> int:
w32cmd.SetConsoleOutputCP(65001)
w32cmd.SetConsoleCP(65001)
win32cmd.SetConsoleOutputCP(65001)
win32cmd.SetConsoleCP(65001)
# ---- 1) 版本 ----
ver: str = openssl.OpenSSL_version(openssl.OpenSslVersion)
@@ -31,7 +29,7 @@ def main() -> int:
# ---- 2) 摘要 ----
data: str = "hello openssl"
dlen: int = len(data)
dlen: int = int(len(data))
md5_buf: bytes = stdlib.malloc(openssl.Md5DigestLength)
openssl.MD5(data, dlen, md5_buf)
+3 -3
View File
@@ -15,7 +15,7 @@ import c
import vqt6
import stdlib
import memhub
import w32.win32console as w32cmd
import win32.win32console as win32cmd
import viperlib
define pagecode = 65001
@@ -31,8 +31,8 @@ define POOL_SIZE = 1 * 1024 * 1024
def main() -> int:
global _mbuddy
w32cmd.SetConsoleOutputCP(pagecode)
w32cmd.SetConsoleCP(pagecode)
win32cmd.SetConsoleOutputCP(pagecode)
win32cmd.SetConsoleCP(pagecode)
# 模块内存池 (QApplication([]) 里的 [] 要在这里分配)
arena: bytes = stdlib.malloc(POOL_SIZE)
+3 -4
View File
@@ -7,7 +7,6 @@
# 4) rhttps.post - HTTPS POST
#
# Usage: requests._mbuddy = my_mbuddy (https reuses the same memory pool)
import t, c
from stdint import *
import stdio
import stdlib
@@ -18,8 +17,8 @@ import requests
import requests.https as rhttps
import requests.download as rd
import zlib.pyzlib as pyzlib
import w32.win32file as wf
import w32.win32base as wb
import win32.win32file as wf
import win32.win32base as wb
# Global MBuddy pointer
@@ -298,4 +297,4 @@ def main() -> int:
socket.SocketCleanup()
stdio.printf("\nViperRequestsTest: DONE\n")
return 0
-143
View File
@@ -1,143 +0,0 @@
import w32.win32console as w32cmd
import stdio
import t, c
from stdint import *
from t import CInt as MyInt
from stdint import BYTE as MyByte
import main2
pagecode: t.CDefine = 65001
# 类型系统收敛测试:typedef 值引用 from-import 别名(阶段1a 别名链)
MyWord: t.CTypedef = MyInt
MyDword: t.CTypedef = DWORD
class TestStruct():
a: t.CInt
b: t.CInt
class Obj():
val: t.CInt
export def main() -> int:
w32cmd.SetConsoleOutputCP(pagecode)
w32cmd.SetConsoleCP(pagecode)
print("=== Unary * / & (Deref/AddrOf) Test ===")
# --- Test 1: 标量 & 取地址 + * 解引用 ---
x: int = 42
px: int* = &x
d: int = *px
print(f"T1: x={x} *(&x)={d} (expect 42, 42)")
if d == 42:
print("T1 OK")
else:
print("T1 FAIL")
# --- Test 2: 通过指针写回(c.DerefAs 兼容形式) ---
#*px = 100
#k: int**
#l = *k
c.DerefAs(px, 100)
print(f"T2: after c.DerefAs(px, 100) x={x} (expect 100)")
if x == 100:
print("T2 OK")
else:
print("T2 FAIL")
# --- Test 3: 数组元素地址 &arr[i] + * 解引用 ---
a: t.CArray[t.CInt, 4]
a[0] = 7
pa: t.CInt | t.CPtr = &a[0]
v3: int = *pa
print(f"T3: a[0]={a[0]} *&a[0]={v3} (expect 7, 7)")
if v3 == 7:
print("T3 OK")
else:
print("T3 FAIL")
# --- Test 4: 结构体字段地址 &s.f + * 解引用 ---
s: TestStruct
s.a = 10
pf: t.CInt | t.CPtr = &s.a
v4: int = *pf
print(f"T4: s.a={s.a} *&s.a={v4} (expect 10, 10)")
if v4 == 10:
print("T4 OK")
else:
print("T4 FAIL")
# --- Test 5: &obj 对象指针(对象变量即指针) ---
o: Obj
o.val = 99
po: Obj | t.CPtr = &o
print(f"T5: o.val={o.val} &o->val={po.val} (expect 99, 99)")
if po.val == 99:
print("T5 OK")
else:
print("T5 FAIL")
print("=== Unary * / & Test Done ===")
# ============================================================
# 类型系统收敛测试(阶段1a/1b/2/3)
# 验证:正常库分析优先(from-import 别名、库 typedef 动态表),
# t/c 库内部特判兜底(CInt/CBool/CPtr 等核心原语保留硬编码)。
# ============================================================
print("=== Type System Convergence Test ===")
# --- T6: from-import 别名反查(阶段1a)---
# from t import CInt as MyInt → 裸名 MyInt 注解经别名反查 → i32
a6: MyInt = 123
# MyWord: t.CTypedef = MyInt → typedef 值引用别名 → 链式解析
b6: MyWord = 456
# from stdint import BYTE as MyByte → 裸名别名 → t.CUnsignedChar
m6: MyByte = 65
print(f"T6: MyInt={a6} MyWord={b6} MyByte={m6} (expect 123, 456, 65)")
if a6 == 123 and b6 == 456 and m6 == 65:
print("T6 OK")
else:
print("T6 FAIL")
# --- T7: 库 typedef 裸名解析(阶段1b/2)---
# 裸名 BYTE/DWORD 走 CTypedef 动态表(stdint.vp 声明权威)
bb7: BYTE = 200
dd7: DWORD = 0x12345678
# typedef 引用 typedef(MyDword = DWORD → CUInt32T)
jj7: MyDword = 0xABCD
# t/c 核心原语内部特判保留(t.CBool → i1 硬编码兜底)
kk7: t.CBool = True
print(f"T7: BYTE={bb7} DWORD={dd7} MyDword={jj7} CBool={kk7} (expect 200, 305419896, 43981, 1)")
if bb7 == 200 and dd7 == 0x12345678 and jj7 == 0xABCD and kk7 == 1:
print("T7 OK")
else:
print("T7 FAIL")
# --- T8: __sizeof__ 库 typedef(阶段3 尺寸联动)---
s8a: int = BYTE.__sizeof__()
s8b: int = DWORD.__sizeof__()
s8c: int = MyWord.__sizeof__()
s8d: int = t.CBool.__sizeof__()
print(f"T8: sizeof(BYTE)={s8a} sizeof(DWORD)={s8b} sizeof(MyWord)={s8c} sizeof(CBool)={s8d} (expect 1, 4, 4, 1)")
if s8a == 1 and s8b == 4 and s8c == 4 and s8d == 1:
print("T8 OK")
else:
print("T8 FAIL")
# --- T9: typedef 联合(BYTE | t.CPtr → 指针包装,* 解引用)---
p9: BYTE | t.CPtr = &bb7
v9: BYTE = *p9
print(f"T9: *(BYTE|CPtr)={v9} (expect 200)")
if v9 == 200:
print("T9 OK")
else:
print("T9 FAIL")
print("=== Type System Convergence Test Done ===")
main2.m()
return 0
-1
View File
@@ -1,4 +1,3 @@
import t, c
import stdio
+2 -3
View File
@@ -5,14 +5,13 @@
# 2) ZipFile("r") reads back: entry count / name / size / compress_size checks + per-member content compare
# (CRC32 verification is done inside zipfile.read)
# 3) Corrupt one payload byte of a stored member -> a second read must return None with error ZIP_ERR_CRC
import t, c
from stdint import *
import stdio
import stdlib
import string
import zipfile
import w32.win32base as wb
import w32.win32file as wf
import win32.win32base as wb
import win32.win32file as wf
ZIP_PATH: str = "zip_roundtrip.zip"