直接操作ScriptBlock AST与使用Parser调用Find为何结果不同?
直接调用ScriptBlock.Ast的Find方法与Parser解析后调用结果不同的原因
问题重现
以下测试代码中,两种查找VariableExpressionAst节点的方式返回的偏移量存在差异:
$ScriptBlock = { function Write-Hello { Param ($name) Write-Host $name } } $Predicate = { $Args[0] -is [System.Management.Automation.Language.VariableExpressionAst] } $Found = $ScriptBlock.Ast.Find($Predicate, $true) Write-Host 'ScriptBlock:' $Found.Extent.StartOffset $Ast = [System.Management.Automation.Language.Parser]::ParseInput($ScriptBlock, [ref]$null, [ref]$null) $Found = $Ast.Find($Predicate, $true) Write-Host 'ParseInput:' $Found.Extent.StartOffset
执行输出:
ScriptBlock: 61 ParseInput: 45
其中通过ParseInput解析得到的结果符合预期,提取对应位置的文本能正确获取目标变量:
$ScriptBlock.ToString().SubString($Found.Extent.StartOffset, ($Found.Extent.EndOffset - $Found.Extent.StartOffset)) # 输出结果:$name
核心原因
直接访问$ScriptBlock.Ast得到的AST并非独立的脚本块语法树——它的顶层节点包含了承载该脚本块的父级上下文内容,所有节点的偏移量都是基于父级脚本的起始位置计算,而非当前脚本块自身的文本范围。
可以通过遍历AST的父节点验证这一点:
$Top = $ScriptBlock.Ast while ($Top.Parent) { $Top = $Top.Parent } $Top.Extent
执行后会发现顶层节点的范围包含了超出当前脚本块的内容,这就是偏移量计算出现偏差的根源。
而使用[System.Management.Automation.Language.Parser]::ParseInput()解析时,是将脚本块的文本作为独立输入处理,生成的AST完全基于当前脚本块的文本内容,因此节点偏移量的计算是准确的。
内容的提问来源于Stack Exchange,提问作者iRon
相关产品推荐
相关产品推荐

