当前位置: 首页 > news >正文

go ast语义分析实现指标计算器

什么是AST

首先我们要知道AST是什么(Abstract Syntax Tree,AST),简称为语法树,是go语言源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

上demo代码

func main() {expr, _ := parser.ParseExpr("1 + 2 * (3 + 4) + 3 * 4.1")ast.Print(token.NewFileSet(), expr)
}
// 输出结果
0  *ast.BinaryExpr {
1  .  X: *ast.BinaryExpr {
2  .  .  X: *ast.BasicLit {
3  .  .  .  ValuePos: -
4  .  .  .  Kind: INT
5  .  .  .  Value: "1"
6  .  .  }
7  .  .  OpPos: -
8  .  .  Op: +
9  .  .  Y: *ast.BinaryExpr {
10  .  .  .  X: *ast.BasicLit {
11  .  .  .  .  ValuePos: -
12  .  .  .  .  Kind: INT
13  .  .  .  .  Value: "2"
14  .  .  .  }
15  .  .  .  OpPos: -
16  .  .  .  Op: *
17  .  .  .  Y: *ast.ParenExpr {
18  .  .  .  .  Lparen: -
19  .  .  .  .  X: *ast.BinaryExpr {
20  .  .  .  .  .  X: *ast.BasicLit {
21  .  .  .  .  .  .  ValuePos: -
22  .  .  .  .  .  .  Kind: INT
23  .  .  .  .  .  .  Value: "3"
24  .  .  .  .  .  }
25  .  .  .  .  .  OpPos: -
26  .  .  .  .  .  Op: +
27  .  .  .  .  .  Y: *ast.BasicLit {
28  .  .  .  .  .  .  ValuePos: -
29  .  .  .  .  .  .  Kind: INT
30  .  .  .  .  .  .  Value: "4"
31  .  .  .  .  .  }
32  .  .  .  .  }
33  .  .  .  .  Rparen: -
34  .  .  .  }
35  .  .  }
36  .  }
37  .  OpPos: -
38  .  Op: +
39  .  Y: *ast.BinaryExpr {
40  .  .  X: *ast.BasicLit {
41  .  .  .  ValuePos: -
42  .  .  .  Kind: INT
43  .  .  .  Value: "3"
44  .  .  }
45  .  .  OpPos: -
46  .  .  Op: *
47  .  .  Y: *ast.BasicLit {
48  .  .  .  ValuePos: -
49  .  .  .  Kind: FLOAT
50  .  .  .  Value: "4.1"
51  .  .  }
52  .  }
53  }

我们通过上面的代码输出了一砣子非常复杂的结构体,那么我们来看看这些都是些上面东西。
我们来看看源码

	// A BinaryExpr node represents a binary expression.type BinaryExpr struct {X     Expr        // left operandOpPos token.Pos   // position of OpOp    token.Token // operatorY     Expr        // right operand}

我们通过这个结构体就可以看出来X和Y分别是两个值,Op是存储的我们的操作符号,OpPos 代表操作符在表达式中的偏移,这里的 Op 类型是token.Token,实际上是个枚举值。


// The list of tokens.
const (// Special tokensILLEGAL Token = iotaEOFCOMMENTliteral_beg// Identifiers and basic type literals// (these tokens stand for classes of literals)IDENT  // mainINT    // 12345FLOAT  // 123.45IMAG   // 123.45iCHAR   // 'a'STRING // "abc"literal_endoperator_beg// Operators and delimitersADD // +SUB // -MUL // *QUO // /REM // %AND     // &OR      // |XOR     // ^SHL     // <<SHR     // >>AND_NOT // &^ADD_ASSIGN // +=SUB_ASSIGN // -=MUL_ASSIGN // *=QUO_ASSIGN // /=REM_ASSIGN // %=AND_ASSIGN     // &=OR_ASSIGN      // |=XOR_ASSIGN     // ^=SHL_ASSIGN     // <<=SHR_ASSIGN     // >>=AND_NOT_ASSIGN // &^=LAND  // &&LOR   // ||ARROW // <-INC   // ++DEC   // --EQL    // ==LSS    // <GTR    // >ASSIGN // =NOT    // !NEQ      // !=LEQ      // <=GEQ      // >=DEFINE   // :=ELLIPSIS // ...LPAREN // (LBRACK // [LBRACE // {COMMA  // ,PERIOD // .RPAREN    // )RBRACK    // ]RBRACE    // }SEMICOLON // ;COLON     // :operator_endkeyword_beg// KeywordsBREAKCASECHANCONSTCONTINUEDEFAULTDEFERELSEFALLTHROUGHFORFUNCGOGOTOIFIMPORTINTERFACEMAPPACKAGERANGERETURNSELECTSTRUCTSWITCHTYPEVARkeyword_endadditional_beg// additional tokens, handled in an ad-hoc mannerTILDEadditional_end
)

那么token 连存储的就是这些, 他会根据我们输入的表达式,给去OpPos解析匹配这些符号。
请添加图片描述
我们可以从上图可以看出ast 代码树,因为表达式中有() ,我们发现了一个新的结构体ParenExpr ,我们查看源码,发现这个结构体是专门用于计算时如果有()时使用的。我们通过该树就可以看出来计算的先后顺序,就完美的完成了一个计算器功能。

	// A ParenExpr node represents a parenthesized expression.type ParenExpr struct {Lparen token.Pos // position of "("X      Expr      // parenthesized expressionRparen token.Pos // position of ")"}

我们了解了ast代码树以后,我们就可以借助该解析代码树做很多东西,例如我们可以做一个计算器。


type AstParser struct {ExpStr string
}func NewAstParser(expStr string) *AstParser {return &AstParser{ExpStr: expStr}
}func (a *AstParser) Parse() (float64, error) {expr, err := parser.ParseExpr(a.ExpStr)if err != nil {return 0, err}val, err := a.Eval(expr)if err != nil {return 0, err}return val, nil
}func (a *AstParser) Eval(expr ast.Expr) (float64, error) {switch e := expr.(type) {case *ast.BinaryExpr:return a.EvalBinaryExpr(e)case *ast.ParenExpr:return a.Eval(e.X)case *ast.BasicLit:switch e.Kind {case token.INT, token.FLOAT:i, err := strconv.ParseFloat(e.Value, 64)if err != nil {return 0, err}return i, nil}}return 0, nil
}func (a *AstParser) EvalBinaryExpr(e *ast.BinaryExpr) (float64, error) {left, err := a.Eval(e.X)if err != nil {return 0, err}right, err := a.Eval(e.Y)if err != nil {return 0, err}switch e.Op {case token.ADD:return left + right, nilcase token.SUB:return left - right, nilcase token.MUL:return left * right, nilcase token.QUO:return left / right, nil}return 0.0, nil
}

总结:
我们了解了ast代码树后,利用它可以做成一个非常简单的规则引擎。
其实利用 ast 包还可以做更多有意思的事情。例如将文件转成proto,或者解析sql语句并做一些审计等等

http://www.lryc.cn/news/354171.html

相关文章:

  • 【Vue】组件间传参与方法调用
  • 类和对象2
  • Linux系统命令traceroute详解(语法、选项、原理和实例)
  • 中兴通讯助力中国移动,推动SPN AI节能技术于23省规模部署
  • SQL Server--死锁
  • 中科蓝讯AB32VG1中文寄存器说明GPIO端口操作
  • 如何查看热门GPT应用?
  • C++中的各种定义
  • Java面向对象-常用类(日期时间类)
  • Shell环境变量深入:自定义系统环境变量
  • 【C++课程学习】:命名空间的理解(图文详解)
  • 鸿蒙ArkUI-X平台差异化:【运行态差异化(@ohos.deviceInfo)】
  • 蓝牙Mesh模块组网时无线回程影响速率吗?
  • 将3D检测的box框投影到BEV图片上
  • Flutter 中的 ClipOval 小部件:全面指南
  • ubuntu 硬盘转移
  • three.js中使用CameraHelper来可视化调整阴影相机的范围
  • Golang发送GET请求并设置查询参数
  • c++笔记3
  • 唠唠叨叨,每日进度
  • Vulhub——CAS 4.1、AppWeb、apisix
  • Python Beautiful Soup 使用详解
  • Java进阶学习笔记29——Math、System、Runtime
  • TOTP 算法实现:双因素认证的基石(C/C++代码实现)
  • aws eks理解和使用podidentity为pod授权
  • 面向可复用性和可维护性的设计模式 课程学习总结
  • 修复谷歌 AdSense 的 Ads.Txt 无效的有收益损失风险提示
  • 使用向量叉乘,来计算一个点到一条线的距离
  • 学习笔记——交通安全分析02
  • pytest-sugar插件:对自动化测试用例加入进度条