抽象语法树(AST)输出功能 - 将表达式转换为结构化的语法树,支持静态分析、代码生成、可视化编辑等高级应用场景
import { ExpressionEngine } from "@choiceform/expression-engine"
// 创建引擎并设置AST输出模式
const engine = new ExpressionEngine({
output: { format: "ast", includeMetadata: true },
})
// 或者运行时切换
engine.setOutputFormat("ast")
// 求值并获取AST
const result = engine.evaluate("Hello {{ $json.name }}!", context)
console.log(result.ast) // AST结构// 不求值,仅分析语法结构
const ast = engine.generateAST('{{ $json.age > 18 ? "成年" : "未成年" }}')
console.log(ast.type) // 'Template'
console.log(ast.dependencies) // ['$json']| 特性 | 字符串输出 | AST输出 |
|---|---|---|
| 用途 | 模板渲染、用户显示 | 静态分析、代码生成 |
| 返回值 | 计算结果字符串 | 语法树结构 |
| 性能 | 需要求值计算 | 仅解析语法 |
| 分析能力 | 无 | 依赖分析、复杂度评估 |
const template = "{{ $json.user.name }} ({{ $json.user.age }}岁)"
// 字符串输出 - 用于显示
engine.setOutputFormat("string")
const stringResult = engine.evaluate(template, context)
// => { value: "张三 (28岁)", type: "string" }
// AST输出 - 用于分析
engine.setOutputFormat("ast")
const astResult = engine.evaluate(template, context)
// => { ast: { type: "Template", parts: [...], dependencies: ["$json"] } }// 方法1:构造时设置
const engine = new ExpressionEngine({
output: {
format: "ast", // 'string' | 'ast'
includeMetadata: true, // 是否包含元数据
},
})
// 方法2:运行时切换
engine.setOutputFormat("ast")
console.log(engine.getOutputFormat()) // 'ast'const result = engine.evaluate("{{ $json.items.length }} 个商品", context)
if (result.success && result.ast) {
const ast = result.ast
console.log("AST类型:", ast.type)
if (ast.type === "Template") {
console.log("组件数量:", ast.parts.length)
console.log("依赖变量:", ast.dependencies)
console.log("复杂度:", ast.complexity)
}
}// 字符串输出 + AST元数据
const engine = new ExpressionEngine({
output: { format: "string", includeMetadata: true },
})
const result = engine.evaluate("{{ $json.name }}", context)
console.log("字符串结果:", result.value) // "张三"
console.log("同时包含AST:", result.ast) // AST结构function analyzeExpression(template: string) {
const ast = engine.generateAST(template)
if (ast.type === "Template") {
return {
dependencies: ast.dependencies, // 依赖的变量
complexity: ast.complexity, // 复杂度评分
componentCount: ast.parts.length, // 组件数量
hasExpressions: ast.parts.some((p) => p.type === "TemplateExpression"),
}
}
}
// 使用示例
const analysis = analyzeExpression("{{ $json.user.name }} 在 {{ $json.user.city }} 工作")
console.log(analysis)
// {
// dependencies: ['$json'],
// complexity: 8,
// componentCount: 5,
// hasExpressions: true
// }function validateDependencies(template: string, availableVars: string[]) {
const ast = engine.generateAST(template)
if (ast.type === "Template") {
const missing = ast.dependencies.filter((dep) => !availableVars.includes(dep))
return {
isValid: missing.length === 0,
missingVars: missing,
requiredVars: ast.dependencies,
}
}
}
// 使用示例
const validation = validateDependencies(
"{{ $json.name }} - {{ $vars.title }}",
["$json"], // 只有$json可用
)
console.log(validation.missingVars) // ['$vars']function convertToJavaScript(ast: ASTNode): string {
if (ast.type === "Template") {
return ast.parts
.map((part) => {
if (part.type === "TemplateText") {
return `"${part.value}"`
} else {
// 简化:将$json.name转换为data.name
const expr = part.value.raw?.replace(/\$json\./g, "data.")
return expr || ""
}
})
.join(" + ")
}
return ""
}
// 使用示例
const ast = engine.generateAST("Hello {{ $json.name }}!")
const jsCode = convertToJavaScript(ast)
console.log(jsCode) // "Hello " + data.name + "!"function traverseAST(ast: ASTNode, visitor: (node: any) => void) {
visitor(ast)
if (ast.type === "Template") {
ast.parts.forEach((part) => {
visitor(part)
if (part.type === "TemplateExpression") {
traverseAST(part.value, visitor)
}
})
}
}
// 使用示例:收集所有标识符
const identifiers: string[] = []
traverseAST(ast, (node) => {
if (node.type === "Identifier") {
identifiers.push(node.name)
}
})interface TemplateNode {
type: "Template"
parts: TemplatePart[] // 模板组件
dependencies: string[] // 依赖变量
complexity?: number // 复杂度评分
start: number // 起始位置
end: number // 结束位置
raw: string // 原始代码
}interface ProgramNode {
type: "Program"
body: StatementNode[] // 语句列表
sourceType: "expression" | "template"
complexity?: number
dependencies?: string[]
}interface TemplateTextPart {
type: "TemplateText"
value: string // 文本内容
start: number
end: number
}interface TemplateExpressionPart {
type: "TemplateExpression"
value: ExpressionNode // 表达式AST
start: number
end: number
}interface IdentifierNode {
type: "Identifier"
name: string // 标识符名称
isVariable?: boolean // 是否是n8n变量($json等)
}interface MemberExpressionNode {
type: "MemberExpression"
object: AnyExpressionNode // 对象
property: AnyExpressionNode // 属性
computed: boolean // true: obj[prop], false: obj.prop
}interface BinaryExpressionNode {
type: "BinaryExpression"
left: AnyExpressionNode // 左操作数
operator: BinaryOperator // 运算符
right: AnyExpressionNode // 右操作数
}interface ConditionalExpressionNode {
type: "ConditionalExpression"
test: AnyExpressionNode // 条件
consequent: AnyExpressionNode // 真值分支
alternate: AnyExpressionNode // 假值分支
}interface CallExpressionNode {
type: "CallExpression"
callee: AnyExpressionNode // 函数
arguments: AnyExpressionNode[] // 参数列表
}// 复杂度分析工具
function analyzeComplexity(templates: string[]) {
return templates
.map((template) => {
const ast = engine.generateAST(template)
return {
template,
complexity: ast.type === "Template" ? ast.complexity : 1,
dependencies: ast.type === "Template" ? ast.dependencies : [],
}
})
.sort((a, b) => (b.complexity || 0) - (a.complexity || 0))
}
// 依赖关系图
function buildDependencyGraph(templates: string[]) {
const graph = new Map<string, string[]>()
templates.forEach((template) => {
const ast = engine.generateAST(template)
if (ast.type === "Template") {
graph.set(template, ast.dependencies)
}
})
return graph
}// 表达式结构可视化
function createExpressionTree(template: string) {
const ast = engine.generateAST(template)
if (ast.type === "Template") {
return ast.parts.map((part, index) => ({
id: `part-${index}`,
type: part.type,
content: part.type === "TemplateText" ? part.value : "表达式",
editable: part.type === "TemplateExpression",
ast: part.type === "TemplateExpression" ? part.value : null,
}))
}
return []
}
// 拖拽式编辑器数据结构
interface EditorNode {
id: string
type: "text" | "variable" | "function" | "condition"
properties: Record<string, unknown>
children?: EditorNode[]
}
function astToEditorNodes(ast: ASTNode): EditorNode[] {
// 将AST转换为编辑器可用的节点结构
// 实现略...
}// 多语言代码生成
class CodeGenerator {
generateJavaScript(ast: ASTNode): string {
// 生成JavaScript代码
}
generatePython(ast: ASTNode): string {
// 生成Python代码
}
generateSQL(ast: ASTNode): string {
// 生成SQL查询
}
}
// 模板优化器
function optimizeTemplate(template: string): string {
const ast = engine.generateAST(template)
// 基于AST进行优化:
// - 合并相邻的静态文本
// - 简化表达式
// - 删除不必要的括号
return reconstructTemplate(ast)
}// 代码补全
function getCompletions(template: string, position: number) {
const ast = engine.generateAST(template)
// 根据光标位置和AST结构提供智能补全
const context = findContextAtPosition(ast, position)
if (context.type === "MemberExpression") {
// 提供对象属性补全
return getObjectProperties(context.object)
} else if (context.type === "CallExpression") {
// 提供函数参数提示
return getFunctionSignature(context.callee)
}
return getGlobalCompletions()
}
// 语法错误检测
function validateSyntax(template: string) {
try {
const ast = engine.generateAST(template)
return { isValid: true, ast }
} catch (error) {
return {
isValid: false,
error: error.message,
suggestions: generateFixSuggestions(error),
}
}
}// AST缓存比字符串缓存更高效
const engine = new ExpressionEngine({
cache: {
enabled: true,
maxSize: 1000,
ttl: 5 * 60 * 1000, // 5分钟
},
})
// 不同输出格式分别缓存
engine.setOutputFormat("string")
engine.evaluate(template, context) // 缓存字符串结果
engine.setOutputFormat("ast")
engine.evaluate(template, context) // 缓存AST结果// 根据需要选择输出格式
function smartEvaluate(template: string, context: any, needAnalysis: boolean) {
if (needAnalysis) {
// 需要分析时使用AST模式
engine.setOutputFormat("ast")
const result = engine.evaluate(template, context)
return {
analysis: extractAnalysis(result.ast),
ast: result.ast,
}
} else {
// 仅需要结果时使用字符串模式
engine.setOutputFormat("string")
const result = engine.evaluate(template, context)
return { value: result.value }
}
}// 批量AST生成
function batchGenerateAST(templates: string[]) {
return templates.map((template) => ({
template,
ast: engine.generateAST(template),
analysis: quickAnalyze(template),
}))
}// ✅ 正确使用场景
// 用户界面显示 -> 字符串模式
engine.setOutputFormat("string")
const displayText = engine.evaluate(template, context).value
// 代码分析工具 -> AST模式
engine.setOutputFormat("ast")
const dependencies = engine.evaluate(template, context).ast?.dependencies
// 同时需要结果和分析 -> 字符串模式 + includeMetadata
const engine = new ExpressionEngine({
output: { format: "string", includeMetadata: true },
})// ✅ 类型安全的AST处理
function processAST(ast: ASTNode) {
if (ast.type === "Template") {
// TypeScript会正确推断ast为TemplateNode
ast.parts.forEach((part) => {
if (part.type === "TemplateText") {
console.log("静态文本:", part.value)
} else if (part.type === "TemplateExpression") {
console.log("表达式:", part.value.type)
}
})
}
}// ✅ 完善的错误处理
function safeEvaluateAST(template: string) {
try {
const result = engine.evaluate(template, context)
if (!result.success) {
console.error("求值失败:", result.error)
return null
}
if (!result.ast) {
console.warn("未获取到AST结构")
return null
}
return result.ast
} catch (error) {
console.error("引擎错误:", error)
return null
}
}// ✅ 合理的缓存配置
const engine = new ExpressionEngine({
cache: {
enabled: true,
maxSize: 500, // 适中的缓存大小
ttl: 2 * 60 * 1000, // 较短的TTL避免内存泄漏
},
})
// 定期清理缓存
setInterval(
() => {
engine.clearCache()
},
10 * 60 * 1000,
) // 10分钟清理一次💡 提示: AST功能为表达式引擎开启了无限可能,从静态分析到可视化编程,让你的应用更智能、更强大!