ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

如何用 ANTLR4 注入动作生成 symbol table

2022-05-29 23:31:17  阅读:246  来源: 互联网

标签:作用域 symbol list ANTLR4 scope table self


什么是 semantic actions

当 Parser 处理输入的代码的时候不仅要判断是否语法和句法都正确,还可以执行一些有用的操作,这些操作就叫做 Semantic actions。其实也就是一段代码,一般嵌入在在语法文件的规则里面。那么当 parser 应用这个规则的时候就会执行你设置的这段代码。换个角度理解,semantic actions 其实就是“触发器”,触发条件就是 parser 应用了对应的规则。

今天这篇文章要探讨的就是一个关于 semantic actions 的应用——实现一个简单的 symbol table,用到的工具是 ANTLR4

什么是 symbol table

编译器在处理我们的代码的时候会在内部维护一个 symbol table,用来存储程序里面所有关于变量的信息:变量名、数据类型、变量所属的作用域等。symbol table 可以是下面这种形式:

Symbol name Type Scope
bar function, double extern
x double function parameter
foo function, double global
count int function parameter
sum double block local
i int for-loop statement

如何在 ANTLR 的语法文件里面注入动作

ANTLR4 是一个强大的 parse generator,我们只要编写好语法文件,就能让它帮我们自动生成 Parser,生成的 Parser 可以支持多种目标语言,比如我自己用的是 Python,那么最后生成的 Parser 就是 Python 文件。

ANTLR4 也提供了方法让我们可以在语法文件里面插入动作,这些动作最后都会被 ANTLR4 注入到生成的 Parser 文件里面。因此,动作用什么语言写取决于你输出 Parser 的目标语言是什么

⚠️这里假定你对如何编写 ANTLR4 的语法文件(*.g4)有基本了解,不会进行赘述,本文只集中介绍如何在语法文件里面插入动作

动作注入的位置和方法

下面是简化的生成的 Parser 文件的代码模板。总体上来看,我们能注入的位置如下所示:

<header>
class xxxParser(Parser):    
    ...
    <member>
    def rule(self):
        ...
        <action>

下面我对每个位置的不同语法进行说明:

ANTLR4 本来就是用 Java 写的,所以如果你用的目标语言也是 Java 的话,这个会比较用得到。一般就是用来放 import 语句的。如果是 python 的话哪里都能 import 就比较没差。要往这个位置注入代码的格式如下(放在 *.g4 文件里面,下同)

@header{
    everything here will go to <header>
}

<member>

这个放的就是类的成员,可以是字段也可以是方法。ANTLR4 支持往 Lexer 和 Parser 分别或者同时注入代码。要往这个位置注入代码的格式如下:

@members {
    everything here will go to <member> in xxxLexer && xxxParser
}
@Lexer::members {
    everything here will go to <member> in xxxLexer
}
@Parser::members {
    everything here will go to <member> in xxxParser
}

⚠️在我使用的 antlr4-python3-runtime 中(4.10),还无法在插入类的字段进行注释

<action>

在 ANTLR4 中,动作是用花括号 {<specific-language-here>} 括起来的代码。正如前面提到的,看你最后输出的 Parser 想要是什么语言,你就用什么语言写动作。

动作一般会放在一条规则的某个 symbol 之后,意思是说在 parser 应用这条规则的时候执行到这里就执行相应的动作。这里的 symbol 可以是 terminal 也可以是 nonterminal。

我们可以用 $symbol.attr 的方式访问到对应的属性,有下面这几个:

$terminal.text           # origin text
$terminal.type           # an integer stands for type
$terminal.attributes
$terminal.line           # the line number
$terminal.pos            # the position of the first char in the line,0-based
$terminal.index          
$terminal.channel        # the channel of this terminal, won't discuss in this post
$terminal.int            # return an interger if this terminal is an integer

$nonterminal.text            # origin text
$nonterminal.start           # 1st Token
$nonterminal.stop            # last Token
$nonterminal.ctx             # return context object

举个例子

⚠️下面都只会给出部分代码,完整的代码在我的 Github 项目

⚠️因为这个项目要求把 warning 输出在其他输出前面,所以我用 warning_listoutput_list 来存储,最后再一起输出

下面以普渡大学 2015 年的开设的编译器课程的项目作业为例,整个项目要实现一个 Micro 语言的编译器,关于 Micro 语言的语法,可以在这里看到。下面我会对步骤三的作业要求进行一个简单的介绍。

在这个作业中我们要构建一个 symbol table,并在相应的时刻输出相关的信息:

  • 每当我们进入一个新的作用域之前(可以是函数,也可以是代码块),要进行相关的输出
  • 如果遇到变量声明,就输出变量名和变量类型,有值的话也要输出。
  • 如果声明的变量在外部作用域已经声明过的话要输出:SHADOW WARNING <var_name>,适用于有嵌套的作用域出现的情况
  • 如果在当前作用域已经有同名的变量,就要输出:DECLARATION ERROR <var_name>。如果出现了这种情况,那么最后程序只输出这个信息
Symbol table <scope_name>
name <var_name> type <type_name>
name <var_name> type <type_name> value <string_value>

数据结构的选取及对应的方法

标签:作用域,symbol,list,ANTLR4,scope,table,self
来源: https://www.cnblogs.com/MartinLwx/p/16325297.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有