2026新年伊始,由于 UfqiFina 有福金融 ( https://ufqi.com/finance ) 有些功能依赖 Hanjst 汉吉斯特 模板语言呈现,我们在2026年元旦刚过去的这个周末对 Hanjst 尝试进行重写。
在这个尝试重写/重构的过程中,我们使用 OpenAI 的 ChatGPT 进行协助。
先说结果,在使用 ChatGPT 尝试进行重写/重构 Hanjst 时,任务失败了,起初它雄心壮志地将 Hanjst 拆解地非常轻量化,允诺了性能大概有 3x 的提升。
可以随着新代码的实现及落地测试,ChatGPT 在多轮测试仍无法通过的情况下,也是在深入理解了 Hanjst 的深层逻辑之后,它对我提出的是否放弃重写/重构建议,深表赞同,并对 Hanjst 进行了确切地定义:
Hanjst 是“正则驱动 + 容错型拼接引擎”, 而不是 AST ( Abstract Syntax Tree, 抽象语法树 ) / 编译器。
于是,我请 ChatGPT 按照能跑能用/工程级的设计思路,从零重新规划设计 Hanjst 会是怎样的? 同时,也借这个机会展现实际能用/运行中的 Hanjst 距离殿堂级学院派的理想的完美的 Hanjst 有多少差距,这些差异体现在哪些地方。同样地,我们也是先说结论:
如果按 AST / 编译器思路设计,Hanjst 会变成:
- 一个 “模板语言 → AST → JS 函数” 的编译器,
{}不再靠正则,而是靠词法 + 语法规则,
但代价是:实现复杂度 ×5,代码量 ×3,调试成本 ×10 。
A1. 原版 Hanjst 的本质是:
HTML
└─ 正则扫描
└─ 字符串拼接 JS
└─ new Function()
它的设计哲学是:“能跑就行,JS 自己兜底”, 这在 2015 年左右是完全正确的设计。
原版 Hanjst 在重构/重写过程中必然遇到的问题,不可回避且无法妥善解决:
1️⃣ {} 单分隔符是“语法层灾难”
- HTML / CSS / JS 都天然用
{,原版靠 “关键词前缀 + 宽松匹配” 苟活。
2️⃣ tpl2js ≠ AST
- 它是 字符串启发式翻译 ,任何“语义化重构”都会破坏隐含假设。
3️⃣ Hanjst 的稳定性来自:Bug × 时间 × 兼容性,
Hanjst 的能用能跑,依靠的内核不是来自设计。
下面,我们将对 Hanjst 按 抽象语法树 编译器 的标准从零开始进行重构。尽管付出的代价是它将变得体积更大更臃肿结构更复杂运行更慢,但是,新版的 Hanjst 将是 殿堂级学院派的水平!
B2. 抽象语法树 AST 版本的 Hanjst:整体架构图
Template String
│
▼
[ Lexer ]
│ Token
▼
[ Parser ]
│ AST
▼
[ Code Generator ]
│ JS Source
▼
new Function(data) → HTML
1️⃣ Lexer(词法分析器)
目标:把模板拆成最小语义单元,Token 类型(示例)
TEXT "<div>"
OPEN_TAG "{"
CLOSE_TAG "}"
IDENTIFIER "if"
VARIABLE "$user"
STRING "'abc'"
NUMBER 123
OPERATOR ">", "=="
KEYWORD if / else / for / foreach / while
2️⃣ Parser(语法分析器)
Parser 不再关心字符串,而是语法结构,AST 节点示例
IfNode {
test: BinaryExpression,
consequent: BlockNode,
alternate: BlockNode
}
{if} 的 AST 表达
{if $user.age > 18}
adult
{else}
child
{/if}
➡ AST:
{
"type": "If",
"test": {
"type": "Binary",
"left": "$user.age",
"op": ">",
"right": 18
},
"then": [...],
"else": [...]
}
3️⃣ HTML 是 AST 的一等公民(关键点)
在 AST 模式下:
<span class="{if $x>1}big{else}small{/if}">
会变成:
AttributeNode {
name: "class",
value: [
Text(""),
IfNode(...),
Text("")
]
}
👉 原版 Hanjst 最高级也是最痛苦的 embedded attribute,在 AST 世界是“天然支持”的。
4️⃣ Code Generator(生成 JS)
AST → JS 的核心规则:
| AST 节点 | JS 输出 |
|---|---|
| TextNode | out.push("xxx") |
| ExprNode | out.push(expr) |
| IfNode | if(...) { ... } |
| ForEachNode | for(...) { ... } |
示例
function render(ctx){
let out = [];
if(ctx.user.age > 18){
out.push("adult");
} else {
out.push("child");
}
return out.join("");
}
C3. 一句非常重要的工程判断:
Hanjst 能跑到今天,本身就说明它的设计在当年是成功的。
你( 重构/重写者 )现在遇到的“痛苦”,恰恰说明:
你在把一个 “脚本拼接器”,升级成一个 “语言编译器”,
👉 这不是重构,这是换物种。
Hanjst, Han JavaScript Template Engine 汉吉斯特 模板语言及引擎
https://ufqi.com/dev/hanjst/Hanjst.demo.html










此前的反思与回顾,语法描述的黑天鹅就在远方:
https://ufqi.com/go/?mod=blog&id=2640&tit=Hanjst汉吉斯特🙋与龙书编译原理