AST
Workflow
源码 (string)
│ Lexical Analysis (词法分析)
▼
Token 流 (List)
│ Syntactic Analysis (语法分析 / parsing)
▼
Parse Tree / CST
│ (简化、抽象)
▼
AST
│ Semantic Analysis (语义分析: 类型检查、符号表…)
▼
后端 (代码生成 / 或者对你来说: 校验、分析、重写)

调用TSql160Parser.Parse()时,这一整条流水线都会跑一遍。ScriptDom内部先有一个T-SQL lexer把脚本切成token,再由parser建成TSqlFragment树。
Lexer (Tokenizer/Scanner)
Lexer从左到右扫描字符,按规则切成一个一个token,也就是语言的最小有意义单位。每个token通常包含:
C#
record Token(TokenType Type, string Text, int Position);
// 例如: (TokenType.Number, "42", 17)Lexer只负责分词,parser负责判断句子语法对不对。空白和注释会被lexer舍弃。
Parser
Parser的工作就是按照语言的grammar把线性token序列组织成树。树可以是AST也可以是CST,但这里我们只focus on AST。
AST不是普通的树,也就是所有节点是同一类型的树。AST的节点是几百个不同的类组成的继承体系:ScriptDom里的TSqlFragment里约有1000个子类,每种节点结构还不一样,例如BinaryExpr有左右孩子,IfStatement有条件,then快,else块,Literal没有孩子。
验证AST
两种方式。第一种,也是比较符合直觉的:
C#
void Check(TSqlFragment node)
{
if (node == null) return; // 很多孩子字段是可选的, 比如没有 WHERE 就是 null
switch (node)
{
case SelectStatement s:
// 1. 处理当前节点(你的检查逻辑)
// 2. 手动递归进【每一个】孩子字段——一个都不能漏
Check(s.WithCtesAndXmlNamespaces);
Check(s.QueryExpression);
foreach (var opt in s.OptimizerHints) Check(opt);
break;
case QuerySpecification q: // SELECT 的主体
foreach (var sel in q.SelectElements) Check(sel);
Check(q.FromClause);
Check(q.WhereClause);
Check(q.GroupByClause);
Check(q.HavingClause);
Check(q.OrderByClause);
Check(q.TopRowFilter);
break;
case WhereClause w:
Check(w.SearchCondition);
break;
case BooleanBinaryExpression b: // AND / OR
Check(b.FirstExpression);
Check(b.SecondExpression);
break;
case BooleanComparisonExpression c: // = > < 等
Check(c.FirstExpression);
Check(c.SecondExpression);
break;
case ScalarSubquery sub: // 子查询: 里面又是完整的 QueryExpression
Check(sub.QueryExpression);
break;
case ExecuteStatement e:
ReportViolation(e); // 叶子级检查, EXEC 下面我们不关心了
break;
// ...... ScriptDom 有约 1000 种节点
// 每种的孩子字段名字、数量、是单个还是 List 全都不一样
// 这些知识只能靠翻文档/源码, 编译器不会提醒你漏了哪个
}
}
// 入口就是:
Check(parsedFragment); // 从根开始, 递归自然铺满整棵树非常的逆天。第二种,就是用visitor pattern:
C#
// 节点这边: 每个节点类实现 Accept,只干一件事——"报出自己的真实类型"
abstract class Expr { public abstract void Accept(IExprVisitor v); }
class BinaryExpr : Expr
{
public Expr Left, Right; public string Op;
public override void Accept(IExprVisitor v) => v.Visit(this); // ← this 是 BinaryExpr
}
class LiteralExpr : Expr
{
public double Value;
public override void Accept(IExprVisitor v) => v.Visit(this); // ← this 是 LiteralExpr
}
// 操作这边: 每种操作是一个 visitor 类,每种节点类型一个 Visit 重载
interface IExprVisitor
{
void Visit(BinaryExpr node);
void Visit(LiteralExpr node);
}