精华内容
下载资源
问答
  • 抽象语法树

    2012-01-08 21:46:32
    如何构建抽象语法树的讲解ppt,很详细。
  • python抽象语法树Abstract Syntax Tree is a very strong features in Python. Python AST module allows us to interact with Python code itself and modify it. 抽象语法树是Python中非常强大的功能。 Python AST...

    python抽象语法树

    Abstract Syntax Tree is a very strong features in Python. Python AST module allows us to interact with Python code itself and modify it.

    抽象语法树是Python中非常强大的功能。 Python AST模块允许我们与Python代码本身进行交互并对其进行修改。

    Python AST模块 (Python AST Module)

    With the Python AST module, we can do a lot of things like modifying Python code and inspect it. The code can be parsed and modified before it is compiled to bytecode form. It is important to understand that each Abstract Syntax Tree represents each element in our Python code as an object. We will understand this in detail in the coming sections. Let’s try the real code.

    使用Python AST模块,我们可以做很多事情,例如修改Python代码并检查它。 在将代码编译为bytecode形式之前,可以对其进行解析和修改。 重要的是要理解,每个抽象语法树都将Python代码中的每个元素表示为一个对象。 我们将在接下来的部分中详细了解这一点。 让我们尝试真正的代码。

    代码编译模式 (Modes for Code Compilation)

    As we mentioned mode in the last script above, there are three modes in which Python code can be compiled. They are:

    正如我们在上面的最后一个脚本中提到的mode一样,可以在三种模式下编译Python代码。 他们是:

    • exec: We can execute normal Python code using this mode.

      exec :我们可以使用这种模式执行普通的Python代码。
    • eval: To evaluate Python’s expressions, this mode will return the result fo the expression after evaluation.

      eval :要求值Python的表达式,此模式将在求值后返回表达式的结果。
    • single: This mode works just like Python shell which execute one statement at a time.

      single :此模式的工作方式类似于Python Shell,一次执行一个语句。

    执行代码 (Executing code)

    We can use AST module to execute Python code. Here is a sample program:

    我们可以使用AST模块执行Python代码。 这是一个示例程序:

    import ast
    
    code = ast.parse("print('Hello world!')")
    print(code)
    
    exec(compile(code, filename="", mode="exec"))

    Let’s see the output for this program:

    python ast exec command

    As mentioned above, we used exec mode here.

    让我们看一下该程序的输出:

    如上所述,我们在这里使用了exec模式。

    评估Python表达式 (Evaluating Python Expression)

    Based on the second mode we mentioned above, AST can be used to evaluate a Python expression and get the response of the expression. Let’s look at a code snippet:

    基于我们上面提到的第二种模式,AST可用于评估Python表达式并获取该表达式的响应。 让我们看一下代码片段:

    import ast
    
    expression = '6 + 8'
    code = ast.parse(expression, mode='eval')
    
    print(eval(compile(code, '', mode='eval')))

    Let’s see the output for this program:

    python ast eval

    It is also possible to see the AST which was formed for the above expression, just add this line with above script:

    让我们看一下该程序的输出:

    也可以看到为上述表达式形成的AST,只需在上面的脚本中添加以下行即可:

    print(ast.dump(code))

    This is what it gives:

    python ast dump

    这就是它的作用:

    构造多行AST (Constructing multi-line ASTs)

    Till now, we made a single line ASTs and in the last example, we also saw how they look using the dump. Now, we will make a transform multi-line Python code to an AST. Here is a sample program:

    到现在为止,我们只做了一行AST,在最后一个示例中,我们还看到了使用转储的外观。 现在,我们将多行Python代码转换为AST。 这是一个示例程序:

    import ast
    
    tree = ast.parse('''
    fruits = ['grapes', 'mango']
    name = 'peter'
    
    for fruit in fruits:
        print('{} likes {}'.format(name, fruit))
    ''')
    
    print(ast.dump(tree))

    Let’s see the output for this program:

    python ast multiline

    We can visit each node by modifying the script:

    让我们看一下该程序的输出:

    我们可以通过修改脚本来访问每个节点:

    import ast
    
    class NodeVisitor(ast.NodeVisitor):
        def visit_Str(self, tree_node):
            print('{}'.format(tree_node.s))
    
    
    class NodeTransformer(ast.NodeTransformer):
        def visit_Str(self, tree_node):
            return ast.Str('String: ' + tree_node.s)
    
    
    tree_node = ast.parse('''
    fruits = ['grapes', 'mango']
    name = 'peter'
    
    for fruit in fruits:
        print('{} likes {}'.format(name, fruit))
    ''')
    
    NodeTransformer().visit(tree_node)
    NodeVisitor().visit(tree_node)

    Let’s see the output for this program:

    python ast visit

    The Visitor class we made above implement methods that are called for each AST nodes whereas with Transformer class, it first calls the corresponding method for node and finally replaces it with the return value of the method. We can execute the methods here by adding this line:

    让我们看一下该程序的输出:

    我们上面制作的Visitor 实现了为每个AST节点调用的方法,而对于Transformer类,它首先为节点调用相应的方法,最后将其替换为该方法的返回值。 我们可以通过添加以下行来在此处执行方法:

    tree_node = ast.fix_missing_locations(tree_node)
    exec(compile(tree_node, '', 'exec'))

    Now the output will be:

    python ast compile

    现在的输出将是:

    何时使用Python AST模块? (When to use Python AST Module?)

    Many automation testing tools, code coverage tools rely on the power of the Abstract Syntax Trees to parse the source code and find the possible flaws and errors in the code. Apart from this, ASTs are also used in:

    许多自动化测试工具,代码覆盖率工具依靠抽象语法树的功能来解析源代码并查找代码中可能存在的缺陷和错误。 除此之外,AST还用于:

    • Making IDEs intelligent and making a feature everyone knows as intellisense.

      制作的IDE智能化和制作特点大家都知道智能感知
    • Tools like Pylint uses ASTs to perform static code analysis

      像Pylint这样的工具使用AST执行静态代码分析
    • Custom Python interpreters

      自定义Python解释器

    结论 (Conclusion)

    In this lesson, we studied the AST module which is used to evaluate and modify the Python’s code in your program.

    在本课程中,我们研究了AST模块,该模块用于评估和修改程序中的Python代码。

    Reference: API Doc

    参考: API文档

    翻译自: https://www.journaldev.com/19243/python-ast-abstract-syntax-tree

    python抽象语法树

    展开全文
  • 主要介绍了Powershell ISE的抽象语法树编程示例,本文讲解了抽象语法树的一些概念,并给出了代码实例,需要的朋友可以参考下
  • AST抽象语法树

    千次阅读 2018-09-16 13:54:23
    抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套...

    转自:http://blog.csdn.net/philosophyatmath/article/details/38170131

     

    抽象语法树简介

    (一)简介

    抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无文文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响,甚至会使合个阶段变得混乱。因些,很多编译器经常要独立地构造语法分析树,为前端,后端建立一个清晰的接口。

    抽象语法树在很多领域有广泛的应用,比如浏览器,智能编辑器,编译器。

     

    (二)抽象语法树实例

     

    (1)四则运算表达式

    表达式: 1+3*(4-1)+2

    抽象语法树为:

     

    (2)xml

    代码2.1:

     

    1. <letter>
    2.   <address>
    3.     <city>ShiChuang</city>
    4.   </address>
    5.   <people>
    6.     <id>12478</id>
    7.     <name>Nosic</name>
    8.   </people>
    9. </letter>

     

    抽象语法树

     

     

     

     

    (3)程序1

    代码2.2

     

    1. while b != 0
    2. {
    3.     if a > b
    4.         a = a-b
    5.     else
    6.         b = b-a
    7. }
    8. return a

     

    抽象语法树

     

    (4)程序2

    代码2.3

     

    1. sum=0
    2. for i in range(0,100)
    3.     sum=sum+i
    4. end

     

    抽象语法树

     

    (三)为什么需要抽象语法树

    当在源程序语法分析工作时,是在相应程序设计语言的语法规则指导下进行的。语法规则描述了该语言的各种语法成分的组成结构,通常可以用所谓的前后文无关文法或与之等价的Backus-Naur范式(BNF)将一个程序设计语言的语法规则确切的描述出来。前后文无关文法有分为这么几类:LL(1),LR(0),LR(1), LR(k) ,LALR(1)等。每一种文法都有不同的要求,如LL(1)要求文法无二义性和不存在左递归。当把一个文法改为LL(1)文法时,需要引入一些隔外的文法符号与产生式。

    例如,四则运算表达式的文法为:

    文法1.1

     

    1. E->T|EAT
    2. T->F|TMF
    3. F->(E)|i
    4. A->+|-
    5. M->*|/

     

    改为LL(1)后为:

    文法1.2

     

    1. E->TE'
    2. E'->ATE'|e_symbol
    3. T->FT'
    4. T'->MFT'|e_symbol
    5. F->(E)|i
    6. A->+|-
    7. M->*|/

    例如,当在开发语言时,可能在开始的时候,选择LL(1)文法来描述语言的语法规则,编译器前端生成LL(1)语法树,编译器后端对LL(1)语法树进行处理,生成字节码或者是汇编代码。但是随着工程的开发,在语言中加入了更多的特性,用LL(1)文法描述时,感觉限制很大,并且编写文法时很吃力,所以这个时候决定采用LR(1)文法来描述语言的语法规则,把编译器前端改生成LR(1)语法树,但在这个时候,你会发现很糟糕,因为以前编译器后端是对LL(1)语树进行处理,不得不同时也修改后端的代码。

    抽象语法树的第一个特点为:不依赖于具体的文法。无论是LL(1)文法,还是LR(1),或者还是其它的方法,都要求在语法分析时候,构造出相同的语法树,这样可以给编译器后端提供了清晰,统一的接口。即使是前端采用了不同的文法,都只需要改变前端代码,而不用连累到后端。即减少了工作量,也提高的编译器的可维护性。

    抽象语法树的第二个特点为:不依赖于语言的细节。在编译器家族中,大名鼎鼎的gcc算得上是一个老大哥了,它可以编译多种语言,例如c,c++,java,ADA,Object C, FORTRAN, PASCAL, COBOL等等。在前端gcc对不同的语言进行词法,语法分析和语义分析后,产生抽象语法树形成中间代码作为输出,供后端处理。要做到这一点,就必须在构造语法树时,不依赖于语言的细节,例如在不同的语言中,类似于if-condition-then这样的语句有不同的表示方法

    在c中为:

     

    1. if(condition)
    2. {
    3.     do_something();
    4. }

     

         在fortran中为:

     

    1. If condition then
    2.     do_somthing()
    3. end if

     

    在构造if-condition-then语句的抽象语法树时,只需要用两个分支节点来表于,一个为condition,一个为if_body。如下图:

    在源程序中出现的括号,或者是关键字,都会被丢掉。

    最近学习GA遗传算法用到了一点语法树的知识(类似list语言中表达式的语法树一样,以前学习堆栈的时候也接触过一些这方面类似的东西),因此选择了一篇很好的博客介绍给大家,同时也供自己学习,有兴趣的同学可以看看编译原理,里面也有讲到

    展开全文
  • Abstract Syntax Tree抽象语法树简写为ATS,是相当于用树结构将代码程式表现出来的一种数据结构,这里我们就来浅析AST抽象语法树及Python代码实现
  • 自定义抽象语法树JSON模板
  • JavaScript抽象语法树AST.pdf
  • 编译原理c语言的抽象语法树 在本文中,我们将看到如何处理和转换从解析器获得的信息。 ANTLR解析器识别源代码中存在的元素,并构建一个解析树 。 从语法分析树中,我们将获得抽象语法树 ,该语法树将用于执行验证并...

    编译原理c语言的抽象语法树

    在本文中,我们将看到如何处理和转换从解析器获得的信息。 ANTLR解析器识别源代码中存在的元素,并构建一个解析树 。 从语法分析树中,我们将获得抽象语法树 ,该语法树将用于执行验证并生成已编译的代码。

    请注意,术语可能会有所不同:许多人会将从ANTLR获得的树称为抽象语法树。 我更喜欢标记这两个步骤的区别。 对我而言, 解析树是对解析器有意义的信息, 抽象语法树是经过重组以更好地支持后续步骤的信息。

    建立自己的语言的系列

    以前的帖子:

    1. 建立一个词法分析器
    2. 建立一个解析器
    3. 创建带有语法突出显示的编辑器
    4. 使用自动补全功能构建编辑器

    代码在GitHub上的标签为05_ ast

    丰富我们的语言

    在本系列文章中,我们一直在研究一种非常简单的表达语言。 现在是时候让我们的语言稍微复杂一些了:

    • 例如, 强制转换为: 10作为十进制(1 * 2.45)作为Int
    • 打印声明   例如: print(3 + 11)

    为此,我们需要修改我们的词法分析器和解析器语法。 我们在之前的文章中构建的语法高亮和自动完成功能将继续起作用。

    新的词法分析器语法:

    lexer grammar SandyLexer;
     
    // Whitespace
    NEWLINE            : '\r\n' | 'r' | '\n' ;
    WS                 : [\t ]+ -> skip ;
     
    // Keywords
    VAR                : 'var' ;
    PRINT              : 'print';
    AS                 : 'as';
    INT                : 'Int';
    DECIMAL            : 'Decimal';
     
    // Literals
    INTLIT             : '0'|[1-9][0-9]* ;
    DECLIT             : '0'|[1-9][0-9]* '.' [0-9]+ ;
     
    // Operators
    PLUS               : '+' ;
    MINUS              : '-' ;
    ASTERISK           : '*' ;
    DIVISION           : '/' ;
    ASSIGN             : '=' ;
    LPAREN             : '(' ;
    RPAREN             : ')' ;
     
    // Identifiers
    ID                 : [_]*[a-z][A-Za-z0-9_]* ;

    以及新的解析器语法:

    parser grammar SandyParser;
     
    options { tokenVocab=SandyLexer; }
     
    sandyFile : lines=line+ ;
     
    line      : statement (NEWLINE | EOF) ;
     
    statement : varDeclaration # varDeclarationStatement
              | assignment     # assignmentStatement
              | print          # printStatement ;
     
    print : PRINT LPAREN expression RPAREN ;
     
    varDeclaration : VAR assignment ;
     
    assignment : ID ASSIGN expression ;
     
    expression : left=expression operator=(DIVISION|ASTERISK) right=expression # binaryOperation
               | left=expression operator=(PLUS|MINUS) right=expression        # binaryOperation
               | value=expression AS targetType=type                           # typeConversion
               | LPAREN expression RPAREN                                      # parenExpression
               | ID                                                            # varReference
               | MINUS expression                                              # minusExpression
               | INTLIT                                                        # intLiteral
               | DECLIT                                                        # decimalLiteral ;
     
    type : INT     # integer
         | DECIMAL # decimal ;

    抽象语法树元模型

    抽象语法树元模型只是我们要用于抽象语法树(AST)的数据结构。 在这种情况下,我们通过定义将用于AST的类来定义它。

    AST元模型看起来与解析树元模型相当类似,即ANTLR生成的包含节点的类集。

    将有一些主要区别:

    • 它会比ANTLR生成的类(因此构成解析树的类)具有更简单,更好的API。 在下一节中,我们将看到此API如何允许在AST上执行转换
    • 我们将删除仅在解析时才有意义但在逻辑上是无用的元素:例如括号表达式或行节点
    • 我们在解析树中具有单独实例的某些节点可以对应于AST中的单个实例。 这是类型引用IntDecimal的情况,它们在AST中使用单例对象定义
    • 我们可以为相关节点类型(例如BinaryExpression)定义通用接口
    • 为了定义如何解析变量声明,我们重用分配规则。 在AST中,这两个概念是完全分开的
    • 某些操作在解析树中具有相同的节点类型,但在AST中是分开的。 不同类型的二进制表达式就是这种情况

    让我们看看如何使用Kotlin定义AST元模型。

    interface Node
     
    //
    // Sandy specific part
    //
     
    data class SandyFile(val statements : List) : Node
     
    interface Statement : Node { }
     
    interface Expression : Node { }
     
    interface Type : Node { }
     
    //
    // Types
    //
     
    object IntType : Type
     
    object DecimalType : Type
     
    //
    // Expressions
    //
     
    interface BinaryExpression : Expression {
        val left: Expression
        val right: Expression
    }
     
    data class SumExpression(override val left: Expression, override val right: Expression) : BinaryExpression
     
    data class SubtractionExpression(override val left: Expression, override val right: Expression) : BinaryExpression
     
    data class MultiplicationExpression(override val left: Expression, override val right: Expression) : BinaryExpression
     
    data class DivisionExpression(override val left: Expression, override val right: Expression) : BinaryExpression
     
    data class UnaryMinusExpression(val value: Expression) : Expression
     
    data class TypeConversion(val value: Expression, val targetType: Type) : Expression
     
    data class VarReference(val varName: String) : Expression
     
    data class IntLit(val value: String) : Expression
     
    data class DecLit(val value: String) : Expression
     
    //
    // Statements
    //
     
    data class VarDeclaration(val varName: String, val value: Expression) : Statement
     
    data class Assignment(val varName: String, val value: Expression) : Statement
     
    data class Print(val value: Expression) : Statement

    我们首先定义Node 。 节点代表AST的每个可能节点,它是通用的。 它也可以重用于其他语言。 其余所有语言都是特定于语言的(本例中为Sandy)。 在我们的特定语言中,我们需要三个重要的界面:

    • 声明
    • 表达
    • 类型

    这些接口均扩展Node

    然后,我们声明我们在语言中使用的两种类型。 它们被定义为单例对象。 这意味着我们只有这些类的一个实例。

    然后我们有了BinaryExpression接口该接口扩展了Expression 。 对于类实现它,每个基本算术表达式一个。

    大多数表达式具有其他节点作为子节点。 有些具有简单的值。 他们是VarReference(其中有一个String类型的属性varName中 ),以及Intlit和DecLit(都有一个String类型的属性 )。

    最后,我们有三个实现Statement的类。

    请注意,我们正在使用数据类,因此我们可以免费使用hashCode,equals和toString方法。 Kotlin还为我们生成了构造函数和获取方法。 试想一下,用Java编写多少代码。

    将解析树映射到抽象语法树

    让我们看看如何获​​取由ANTLR生成的解析树,并将其映射到我们的AST类中。

    fun SandyFileContext.toAst() : SandyFile = SandyFile(this.line().map { it.statement().toAst() })
     
    fun StatementContext.toAst() : Statement = when (this) {
        is VarDeclarationStatementContext -> VarDeclaration(varDeclaration().assignment().ID().text, varDeclaration().assignment().expression().toAst())
        is AssignmentStatementContext -> Assignment(assignment().ID().text, assignment().expression().toAst())
        is PrintStatementContext -> Print(print().expression().toAst())
        else -> throw UnsupportedOperationException(this.javaClass.canonicalName)
    }
     
    fun  ExpressionContext.toAst() : Expression = when (this) {
        is BinaryOperationContext -> toAst()
        is IntLiteralContext -> IntLit(text)
        is DecimalLiteralContext -> DecLit(text)
        is ParenExpressionContext -> expression().toAst()
        is VarReferenceContext -> VarReference(text)
        is TypeConversionContext -> TypeConversion(expression().toAst(), targetType.toAst())
        else -> throw UnsupportedOperationException(this.javaClass.canonicalName)
    }
     
    fun TypeContext.toAst() : Type = when (this) {
        is IntegerContext -> IntType
        is DecimalContext -> DecimalType
        else -> throw UnsupportedOperationException(this.javaClass.canonicalName)
    }
     
    fun  BinaryOperationContext.toAst() : Expression = when (operator.text) {
        "+" -> SumExpression(left.toAst(), right.toAst())
        "-" -> SubtractionExpression(left.toAst(), right.toAst())
        "*" -> MultiplicationExpression(left.toAst(), right.toAst())
        "/" -> DivisionExpression(left.toAst(), right.toAst())
        else -> throw UnsupportedOperationException(this.javaClass.canonicalName)
    }

    为了实现这一点,我们利用了Kotlin的三个非常有用的功能:

    • 扩展方法:我们将方法toAst添加到了几个现有的类中
    • when构造,它是switch的更强大的版本
    • 智能转换:在检查对象是否具有某个类之后,编译器会将其隐式转换为该类型,以便我们可以使用该类的特定方法

    我们可以提出一种机制,自动为大多数规则导出此映射,并仅在分析树和AST不同的地方对其进行自定义。 为了避免使用过多的反射黑魔法,我们暂时不这样做。 如果我使用Java,那么我会走上一条反思之路,以避免不得不手动编写大量多余和无聊的代码。 但是,使用Kotlin可以使这段代码紧凑而清晰。

    测试映射

    当然,我们需要测试这些东西。 让我们看看对于某段代码获得的AST是否符合我们的期望。

    class MappingTest {
     
        @test fun mapSimpleFile() {
            val code = """var a = 1 + 2
                         |a = 7 * (2 / 3)""".trimMargin("|")
            val ast = SandyParserFacade.parse(code).root!!.toAst()
            val expectedAst = SandyFile(listOf(
                    VarDeclaration("a", SumExpression(IntLit("1"), IntLit("2"))),
                    Assignment("a", MultiplicationExpression(
                            IntLit("7"),
                            DivisionExpression(
                                    IntLit("2"),
                                    IntLit("3"))))))
            assertEquals(expectedAst, ast)
        }
     
        @test fun mapCastInt() {
            val code = "a = 7 as Int"
            val ast = SandyParserFacade.parse(code).root!!.toAst()
            val expectedAst = SandyFile(listOf(Assignment("a", TypeConversion(IntLit("7"), IntType))))
            assertEquals(expectedAst, ast)
        }
     
        @test fun mapCastDecimal() {
            val code = "a = 7 as Decimal"
            val ast = SandyParserFacade.parse(code).root!!.toAst()
            val expectedAst = SandyFile(listOf(Assignment("a", TypeConversion(IntLit("7"), DecimalType))))
            assertEquals(expectedAst, ast)
        }
     
        @test fun mapPrint() {
            val code = "print(a)"
            val ast = SandyParserFacade.parse(code).root!!.toAst()
            val expectedAst = SandyFile(listOf(Print(VarReference("a"))))
            assertEquals(expectedAst, ast)
        }
     
    }

    那就太好了:我们有了代码中存在的信息的清晰模型。 元模型和映射代码看起来非常简单清晰。 但是,我们需要添加一些细节:节点在源代码中的位置。 向用户显示错误时将需要这样做。 我们希望有可能指定AST节点的位置,但我们不想被迫这样做。 这样,根据我们需要执行的操作,我们可以忽略或不忽略这些头寸。 考虑一下我们到目前为止编写的测试:是否必须为所有节点指定虚假位置会很麻烦又烦人? 我认同。

    这是新的Node定义和一些支持类:

    interface Node {
        val position: Position?
    }
     
    data class Point(val line: Int, val column: Int)
     
    data class Position(val start: Point, val end: Point)
     
    fun pos(startLine:Int, startCol:Int, endLine:Int, endCol:Int) = Position(Point(startLine,startCol),Point(endLine,endCol))

    我们还需要将位置作为可选参数添加到所有类中。 它将具有默认值null 。 例如,这是SandyFile现在的外观:

    data class SandyFile(val statements : List<Statement>, override val position: Position? = null) : Node

    映射只是稍微复杂一点:

    fun SandyFileContext.toAst(considerPosition: Boolean = false) : SandyFile = SandyFile(this.line().map { it.statement().toAst(considerPosition) }, toPosition(considerPosition))
     
    fun Token.startPoint() = Point(line, charPositionInLine)
     
    fun Token.endPoint() = Point(line, charPositionInLine + text.length)
     
    fun ParserRuleContext.toPosition(considerPosition: Boolean) : Position? {
        return if (considerPosition) Position(start.startPoint(), stop.endPoint()) else null
    }
     
    fun StatementContext.toAst(considerPosition: Boolean = false) : Statement = when (this) {
        is VarDeclarationStatementContext -> VarDeclaration(varDeclaration().assignment().ID().text, varDeclaration().assignment().expression().toAst(considerPosition), toPosition(considerPosition))
        is AssignmentStatementContext -> Assignment(assignment().ID().text, assignment().expression().toAst(considerPosition), toPosition(considerPosition))
        is PrintStatementContext -> Print(print().expression().toAst(considerPosition), toPosition(considerPosition))
        else -> throw UnsupportedOperationException(this.javaClass.canonicalName)
    }
     
    fun  ExpressionContext.toAst(considerPosition: Boolean = false) : Expression = when (this) {
        is BinaryOperationContext -> toAst(considerPosition)
        is IntLiteralContext -> IntLit(text, toPosition(considerPosition))
        is DecimalLiteralContext -> DecLit(text, toPosition(considerPosition))
        is ParenExpressionContext -> expression().toAst(considerPosition)
        is VarReferenceContext -> VarReference(text, toPosition(considerPosition))
        is TypeConversionContext -> TypeConversion(expression().toAst(considerPosition), targetType.toAst(considerPosition), toPosition(considerPosition))
        else -> throw UnsupportedOperationException(this.javaClass.canonicalName)
    }
     
    fun TypeContext.toAst(considerPosition: Boolean = false) : Type = when (this) {
        is IntegerContext -> IntType(toPosition(considerPosition))
        is DecimalContext -> DecimalType(toPosition(considerPosition))
        else -> throw UnsupportedOperationException(this.javaClass.canonicalName)
    }
     
    fun  BinaryOperationContext.toAst(considerPosition: Boolean = false) : Expression = when (operator.text) {
        "+" -> SumExpression(left.toAst(considerPosition), right.toAst(considerPosition), toPosition(considerPosition))
        "-" -> SubtractionExpression(left.toAst(considerPosition), right.toAst(considerPosition), toPosition(considerPosition))
        "*" -> MultiplicationExpression(left.toAst(considerPosition), right.toAst(considerPosition), toPosition(considerPosition))
        "/" -> DivisionExpression(left.toAst(considerPosition), right.toAst(considerPosition), toPosition(considerPosition))
        else -> throw UnsupportedOperationException(this.javaClass.canonicalName)
    }

    在这一点上,所有以前的测试都一直通过,但是我们想添加一个测试来验证位置是否正确定义:

    @test fun mapSimpleFileWithPositions() {
            val code = """var a = 1 + 2
                         |a = 7 * (2 / 3)""".trimMargin("|")
            val ast = SandyParserFacade.parse(code).root!!.toAst(considerPosition = true)
            val expectedAst = SandyFile(listOf(
                    VarDeclaration("a",
                            SumExpression(
                                    IntLit("1", pos(1,8,1,9)),
                                    IntLit("2", pos(1,12,1,13)),
                                    pos(1,8,1,13)),
                            pos(1,0,1,13)),
                    Assignment("a",
                            MultiplicationExpression(
                                IntLit("7", pos(2,4,2,5)),
                                DivisionExpression(
                                        IntLit("2", pos(2,9,2,10)),
                                        IntLit("3", pos(2,13,2,14)),
                                        pos(2,9,2,14)),
                                pos(2,4,2,15)),
                            pos(2,0,2,15))),
                    pos(1,0,2,15))
            assertEquals(expectedAst, ast)
        }

    解析树包含以最方便的方式为解析器组织的信息。 通常,这不是执行以下步骤的最方便的方法。 考虑一下通过重用赋值规则来实现的变量声明规则:当然,这会使语法更短,并且对解析树有意义。 但是,从逻辑角度看,这两个元素是分开的,并且在AST中它们确实是分开的。

    我们其余的大多数工具都可以在AST上运行,因此最好花一些时间在有意义的AST上工作。

    参考: 为您自己的语言构建编译器:从 Federico Tomassetti博客上的JCG合作伙伴 Federico Tomassetti 的解析树到抽象语法树

    翻译自: https://www.javacodegeeks.com/2016/08/building-compiler-language-parse-tree-abstract-syntax-tree.html

    编译原理c语言的抽象语法树

    展开全文
  • react-ast使用react渲染抽象语法树。如果您发现它有用,请★此仓库★★★本质上很难使用抽象语法树。 这是一个React渲染器,它可以使用react与Abstrac react-ast交互来渲染抽象语法树。如果您发现它有用,请★此仓库...
  • 简述 AST 抽象语法树

    2020-02-04 22:36:40
    AST 全称为 Abstract Syntax Tree,译为抽象语法树。在 JavaScript 中,任何一个对象(变量、函数、表达式等)都可以转化为一个抽象语法树的格式。抽象语法树本质就是一个树形结构的对象。

    一、概念

    AST 全称为 Abstract Syntax Tree,译为抽象语法树。在 JavaScript 中,任何一个对象(变量、函数、表达式等)都可以转化为一个抽象语法树的形式。抽象语法树本质就是一个树形结构的对象。

    使用 astexplorer 可以在线将任意对象,表达式转换为 AST 语法树。
    在这里插入图片描述
    如果到这里你还是不太理解 AST 是什么东西。那么假设你的电脑主板坏掉了。维修人员的修理过程是:先把电脑各个零件拆开,找到坏掉的主板,检查主板是否还可以修好。如果不行,则换一个新的主板,再把电脑一步步重新组装好。那么 AST 就是被拆解的一个个电脑零件。

    二、特点

    一个对象生成 AST 的关键所在是词法分析和语法分析。
    词法分析指的是将对象逐个扫描,获取每一个字母的信息,生成由对象组成的一维数组。

    const a = 5;
    //词法分析
    [{value:'const',type:'keyword'},{value:'a',type:'identifier'}...]
    

    语法分析指的是将有关联的对象整合成树形结构的表达形式。

    const a = 5;
    //语法分析
    {
      "type": "Program",
      "start": 0,
      "end": 12,
      "body": [
        {
          "type": "VariableDeclaration",
          "start": 0,
          "end": 12,
          "declarations": [
            {
              "type": "VariableDeclarator",
              "start": 6,
              "end": 11,
              "id": {
                "type": "Identifier",
                "start": 6,
                "end": 7,
                "name": "a"
              },
              "init": {
                "type": "Literal",
                "start": 10,
                "end": 11,
                "value": 5,
                "raw": "5"
              }
            }
          ],
          "kind": "const"
        }
      ],
      "sourceType": "module"
    }
    

    三、用途

    1. 常用各类转义、编译的插件中。比如最典型的 ES6 转换为 ES6 工具 、JSX 语法转换为 JavaScript 语法。即 babel 模块。
    2. 代码语法的检查,比如代码规范工具 ESLint 模块。
    3. 各类 JS/CSS/HTML 压缩工具。
    4. 代码的格式化、高亮。
    5. 代码错误提示。
    6. 代码自动补全。

    四、案例

    现在,我们使用 AST 的概念来拆解一个最简单的函数。

    function exp(a){return a}
    

    使用 astexplorer 在线拆解得到结果如下:
    在这里插入图片描述

    1. 在最外层的 type、start、end、body、sourceType 中,我们主要看中间 body 部分。把每一个 body 当成一个块来拆解。
    2. 第一个 body(块) 就是 function exp(a) 这段。这是一个 type 为 FunctionDeclaration (函数定义)类型的对象。每一个函数定义的块中都有一个 id 对象。如上所示,它的 id 对象信息中 type 为 Identifier,name 为 exp。它不是表达式,也不是异步。所以 express、generator、async 皆为 false。最后它有一个参数 params 对象集,在该参数对象集中参数名 name 为 a
    3. 再看第二个 body(块) 就是 return a。这是一个 type 为 BlockStatement (块语句)类型的对象。在其内部有一个 type 为 ReturnStatement (返回语句)类型的语句。在返回的 argument 集合中参数名 name 为形参 a
    4. 可以看到上面的拆解中,每一个块都有 type、start、end、body 这几个字段。
      其中 type 表达当前块的类型。比如 FunctionDeclaration 表示函数定义,Identifier 表示标识符、BlockStatement 表示块语句、ReturnStatement 表示返回语句等。
      start 表示该块开始的位置,end 表示该块结束的位置,body 表示子块。
      其他的字段根据块的性质不同有所不同。
    展开全文
  • AST(抽象语法树)超详细

    万次阅读 多人点赞 2019-07-15 17:15:10
    首先来一个比较形象的,转载自:AST-抽象语法树,讲述了为什么需要讲源代码转化为AST,总结就是:AST不依赖于具体的文法,不依赖于语言的细节,我们将源代码转化为AST后,可以对AST做很多的操作,...
  • AST 抽象语法树

    2021-06-17 14:12:42
    一、什么是抽象语法树 在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都表示源...
  • 接着(抽象语法树(上))讲,上次讲了怎么查看抽象语法树,这次讲如何修改抽象语法树。 ast模块有两个类可以修改抽象语法树,分别是NodeVisitor和NodeTransformer。讲一下两者的区别,NodeVisitor可以修改抽象语法...
  • xast:可扩展抽象语法树
  • mdast:Markdown抽象语法树格式
  • 抽象语法树简介

    2019-04-09 14:17:00
    抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套...
  • 抽象语法树概念认知

    2020-07-06 16:46:36
    抽象语法树 概念: 抽象语法树(abstract syntax code,AST)是源代码抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,之所以说语法是’抽象’的,是因为这里的语法不会表示出真实语法中出现的每...
  • DelphiAST, Delphi抽象语法树生成器 Delphi的抽象语法树生成器使用 DelphiAST,你可以使用真正的Delphi代码并获得抽象语法树。 一个单位,但没有符号表。FreePascal和and兼容。示例输入unit Unit1;interfac

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 62,861
精华内容 25,144
关键字:

抽象语法树