CN111381814A - 生成代码文件的语法树的方法、装置及电子设备 - Google Patents
生成代码文件的语法树的方法、装置及电子设备 Download PDFInfo
- Publication number
- CN111381814A CN111381814A CN201811638965.8A CN201811638965A CN111381814A CN 111381814 A CN111381814 A CN 111381814A CN 201811638965 A CN201811638965 A CN 201811638965A CN 111381814 A CN111381814 A CN 111381814A
- Authority
- CN
- China
- Prior art keywords
- lexical
- symbol
- target
- state
- stack
- Prior art date
- Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
- Pending
Links
- 238000000034 method Methods 0.000 title claims abstract description 89
- 238000012545 processing Methods 0.000 claims abstract description 157
- 238000011084 recovery Methods 0.000 claims abstract description 103
- 238000004458 analytical method Methods 0.000 claims abstract description 12
- 238000003825 pressing Methods 0.000 claims description 12
- 238000003860 storage Methods 0.000 claims description 11
- 238000004590 computer program Methods 0.000 claims description 8
- 230000008569 process Effects 0.000 description 42
- 230000006870 function Effects 0.000 description 30
- 230000009467 reduction Effects 0.000 description 28
- 230000009471 action Effects 0.000 description 27
- 238000004519 manufacturing process Methods 0.000 description 24
- 238000010586 diagram Methods 0.000 description 18
- 230000006399 behavior Effects 0.000 description 10
- 238000003379 elimination reaction Methods 0.000 description 10
- 241000282326 Felis catus Species 0.000 description 9
- 230000008030 elimination Effects 0.000 description 9
- 230000007246 mechanism Effects 0.000 description 5
- 238000013507 mapping Methods 0.000 description 4
- 230000003287 optical effect Effects 0.000 description 3
- 238000011946 reduction process Methods 0.000 description 3
- 230000003068 static effect Effects 0.000 description 3
- 238000005034 decoration Methods 0.000 description 2
- 238000012986 modification Methods 0.000 description 2
- 230000004048 modification Effects 0.000 description 2
- 238000007630 basic procedure Methods 0.000 description 1
- 150000001875 compounds Chemical class 0.000 description 1
- 238000011161 development Methods 0.000 description 1
- 238000005516 engineering process Methods 0.000 description 1
- 238000000802 evaporation-induced self-assembly Methods 0.000 description 1
- 238000004321 preservation Methods 0.000 description 1
- 230000001105 regulatory effect Effects 0.000 description 1
Images
Classifications
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F8/00—Arrangements for software engineering
- G06F8/30—Creation or generation of source code
- G06F8/31—Programming languages or programming paradigms
- G06F8/315—Object-oriented languages
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F8/00—Arrangements for software engineering
- G06F8/40—Transformation of program code
- G06F8/41—Compilation
- G06F8/42—Syntactic analysis
- G06F8/425—Lexical analysis
Landscapes
- Engineering & Computer Science (AREA)
- General Engineering & Computer Science (AREA)
- Software Systems (AREA)
- Theoretical Computer Science (AREA)
- Physics & Mathematics (AREA)
- General Physics & Mathematics (AREA)
- Computing Systems (AREA)
- Devices For Executing Special Programs (AREA)
Abstract
本申请涉及计算机软件开发技术领域,公开了一种生成代码文件的语法树的方法、装置及电子设备,其中,生成代码文件的语法树的方法包括:在接收到预定编程语言的待解析代码文件时,通过词法解析模块对待解析代码文件中的各个词法符号进行解析并生成相应的线性链表;接着基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理;接着根据错误恢复处理的处理结果,生成待解析代码文件的语法树。本申请实施例的方法,使得可以通过语法树对编写完成的代码文件进行静态分析,从而检查代码文件中的语法错误、编写错误等。
Description
技术领域
本申请涉及计算机软件开发技术领域,具体而言,本申请涉及一种生成代码文件的语法树的方法、装置及电子设备。
背景技术
在当前的计算机领域,C/C++/JAVA等高级编程语言对应的编译器开发技术越来越成熟,应用高级语言编译器能够实现将程序语言转化为机器语言的操作。但是,目前的编译器并不能逐一对代码文件中存在的代码编写错误进行全面、详尽分析,也无法准确对代码文件中存在的代码编写错误进行提示或纠正。因此,在对代码文件进行编译之前,需要程序开发人员手动检查代码文件中存在的错误。
本申请的发明人在具体实施过程中发现:当代码文件中的代码量很大时,将会给程序开发人员造成极大工作量,不仅导致程序开发人员花费大量时间、精力等,检查代码文件中存在的错误,而且检查效率极低。同时,本申请的发明人发现:根据代码文件中各个词法符号的词性,查找相应的查找表,生成代码文件的语法树(syntaxtree),能够自动实现代码文件中语法错误、编写错误等的检查及纠正,极大提高检查效率,然而在查找相应查找表的过程中,会出现错误,即任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空,导致没有可执行行为,基于此本申请的发明人提供了一种对错误进行恢复处理并生成语法树的方法。
发明内容
本申请的目的旨在至少能解决上述的技术缺陷之一,特提出以下技术方案:
第一方面,提供了一种生成代码文件的语法树的方法,包括:
在接收到预定编程语言的待解析代码文件时,通过词法解析模块对待解析代码文件中的各个词法符号进行解析并生成相应的线性链表;
基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理;
根据错误恢复处理的处理结果,生成待解析代码文件的语法树。
具体地,在对任一词法符号进行错误恢复处理之前,还包括:
生成提醒用户任一词法符号发生错误的提示信息,并显示提示信息。
进一步地,对任一词法符号进行错误恢复处理,包括:
确定满足预定恢复条件的至少一组预定组合形式的目标词法符号、目标状态及目标非终结符;
基于任一组预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理。
进一步地,预定恢复条件为目标词法符号与第二表项在第一查找表中对应的第三表项为非空;
第二表项为目标状态与目标非终结符在第二查找表中对应的表项。
进一步地,确定满足预定恢复条件的至少一组预定组合形式的目标词法符号、目标状态及目标非终结符,包括:
将从当前状态栈的栈顶状态开始跳过预定个数的状态作为目标状态;
将从任一词法符号开始跳过预定个数的词法符号作为目标词法符号;
基于第二查找表中状态与非终结符之间的对应关系,将目标状态对应的非终结符确定为目标非终结符。
进一步地,基于任一组预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理,包括:
在状态栈中,对位于目标状态之上的状态进行出栈处理;
在符号栈中,对位于任一词法符号之上的词法符号进行出栈处理,且出栈的词法符号的数目与状态栈中出栈的状态数目相同;
通过将目标非终结符压入符号栈,并将第二表项压入状态栈,对任一词法符号进行错误恢复处理。
进一步地,还包括:
当存在多组预定组合形式的目标词法符号、目标状态及目标非终结符满足预定恢复条件时,基于第一组满足预定恢复条件的预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理;或者,基于多组预定组合形式中最高优先级的预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理。
进一步地,基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,包括:
对线性链表中的各个词法符号依次进行词性确定,且每当确定任一词法符号的词性时,根据任一词法符号的词性,查找第一查找表和第二查找表,得到对应的查找结果;
根据查找结果,对任一词法符号进行分析。
进一步地,预定编程语言为C++编程语言与C语言中的任一种。
第二方面,提供了一种生成代码文件的语法树的装置,包括:
解析模块,用于在接收到预定编程语言的待解析代码文件时,通过词法解析模块对待解析代码文件中的各个词法符号进行解析并生成相应的线性链表;
处理模块,用于基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理;
语法树生成模块,用于根据错误恢复处理的处理结果,生成待解析代码文件的语法树。
进一步地,还包括提示模块;
提示模块,用于生成提醒用户任一词法符号发生错误的提示信息,并显示提示信息。
进一步地,处理模块包括确定子模块与错误恢复子模块;
确定子模块,用于确定满足预定恢复条件的至少一组预定组合形式的目标词法符号、目标状态及目标非终结符;
错误恢复子模块,用于基于任一组预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理。
进一步地,预定恢复条件为目标词法符号与第二表项在第一查找表中对应的第三表项为非空;
第二表项为目标状态与目标非终结符在第二查找表中对应的表项。
进一步地,确定子模块具体用于将从当前状态栈的栈顶状态开始跳过预定个数的状态作为目标状态;以及用于将从任一词法符号开始跳过预定个数的词法符号作为目标词法符号;以及用于基于第二查找表中状态与非终结符之间的对应关系,将目标状态对应的非终结符确定为目标非终结符。
进一步地,错误恢复子模块具体用于在状态栈中,对位于目标状态之上的状态进行出栈处理;以及用于在符号栈中,对位于任一词法符号之上的词法符号进行出栈处理,且出栈的词法符号的数目与状态栈中出栈的状态数目相同;以及用于通过将目标非终结符压入符号栈,并将第二表项压入状态栈,对任一词法符号进行错误恢复处理。
进一步地,还包括选择子模块;
选择子模块,用于当存在多组预定组合形式的目标词法符号、目标状态及目标非终结符满足预定恢复条件时,基于第一组满足预定恢复条件的预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理;或者,基于多组预定组合形式中最高优先级的预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理。
进一步地,处理模块包括词性确定子模块与分析子模块;
词性确定子模块,用于对线性链表中的各个词法符号依次进行词性确定,且每当确定任一词法符号的词性时,根据任一词法符号的词性,查找第一查找表和第二查找表,得到对应的查找结果;
分析子模块,用于根据查找结果,对任一词法符号进行分析。
进一步地,预定编程语言为C++编程语言与C语言中的任一种。
第三方面,提供了一种电子设备,包括存储器、处理器及存储在存储器上并可在处理器上运行的计算机程序,处理器执行所述程序时实现上述的生成代码文件的语法树的方法。
第四方面,提供了一种计算机可读存储介质,计算机可读存储介质上存储有计算机程序,该程序被处理器执行时实现上述的生成代码文件的语法树的方法。
本申请实施例提供的生成代码文件的语法树的方法,基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理,从而有效解决了根据查找表查找不到可执行行为时,导致无法继续进行后续操作的情况,奠定后续生成待解析代码文件的语法树的必要基础;根据错误恢复处理的处理结果,生成待解析代码文件的语法树,提供了一种生成代码文件的语法树的方法,使得可以通过语法树对编写完成的代码文件进行静态分析,从而准确、高效地对编写完成的代码文件中的语法错误、编写错误等进行检查及纠正,极大节省程序开发人员的时间、精力等。
本申请附加的方面和优点将在下面的描述中部分给出,这些将从下面的描述中变得明显,或通过本申请的实践了解到。
附图说明
本申请上述的和/或附加的方面和优点从下面结合附图对实施例的描述中将变得明显和容易理解,其中:
图1为本申请实施例的生成代码文件的语法树的方法的流程示意图;
图2为本申请实施例的错误恢复处理的基本过程示意图;
图3为本申请实施例的词性确定过程的基本过程示意图;
图4为本申请实施例的词性确定过程的获取关键字与运算符的词性的基本过程示意图;
图5为本申请实施例的通过作用域信息查找确定词性的过程示意图;
图6为本申请实施例的词性猜测的基本过程示意图;
图7为本申请实施例的冲突消除处理的基本过程示意图;
图8为本申请实施例的生成代码文件的语法树的整体过程示意图;
图9为本申请实施例的进行移进过程的流程示意图;
图10为本申请实施例的生成代码文件的语法树的装置的基本结构示意图;
图11为本申请实施例的生成代码文件的语法树的装置的详细结构示意图;
图12为本申请实施例的电子设备的结构示意图。
具体实施方式
下面详细描述本申请的实施例,所述实施例的示例在附图中示出,其中自始至终相同或类似的标号表示相同或类似的元件或具有相同或类似功能的元件。下面通过参考附图描述的实施例是示例性的,仅用于解释本申请,而不能解释为对本申请的限制。
本技术领域技术人员可以理解,除非特意声明,这里使用的单数形式“一”、“一个”、“所述”和“该”也可包括复数形式。应该进一步理解的是,本申请的说明书中使用的措辞“包括”是指存在所述特征、整数、步骤、操作、元件和/或组件,但是并不排除存在或添加一个或多个其他特征、整数、步骤、操作、元件、组件和/或它们的组。应该理解,当我们称元件被“连接”或“耦接”到另一元件时,它可以直接连接或耦接到其他元件,或者也可以存在中间元件。此外,这里使用的“连接”或“耦接”可以包括无线连接或无线耦接。这里使用的措辞“和/或”包括一个或更多个相关联的列出项的全部或任一单元和全部组合。
为使本申请的目的、技术方案和优点更加清楚,下面将结合附图对本申请实施方式作进一步地详细描述。
下面以具体地实施例对本申请的技术方案以及本申请的技术方案如何解决上述技术问题进行详细说明。下面这几个具体的实施例可以相互结合,对于相同或相似的概念或过程可能在某些实施例中不再赘述。下面将结合附图,对本申请的实施例进行描述。
实施例一
本申请实施例提供了一种生成代码文件的语法树的方法,如图1所示,包括:
步骤S110,在接收到预定编程语言的待解析代码文件时,通过词法解析模块对待解析代码文件中的各个词法符号进行解析并生成相应的线性链表。
具体地,预定编程语言的代码文件通常是由关键字、标识符、数学运算符、作用域标识符及语句标点符等代码元素组成,这些代码元素又称为词法符号(token)。通过词法解析模块,可以将待解析代码文件中的各个词法符号,按次序组成线性链表(token-list),为后续生成代码文件的语法树提供前提保障。
其中,下文将数学运算符、作用域标识符、语句标点符等符号统称为运算符,并将链表中的第一个元素记作token-list.first。
进一步地,词法符号(token)的基本属性如下表1所示,表1中的左侧为各词法符号对应英文名称,右侧为各词法符号分别对应的中文说明或解释。
表1-词法符号(token)的基本属性
其中,在某些情况下,为简化文法,在词法解析时可以合并一些词法符号(token),比如将“::new”、“::delete”、“.template”、“->template”等词法符号(token)合并为一个词法符号(token)。
步骤S120,基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理。
具体地,当在根据词法符号的词性查找第一查找表和第二查找表的过程中,出现错误时,即任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空,导致没有可执行行为时,对当前词法符号进错误恢复处理。
步骤S130,根据错误恢复处理的处理结果,生成待解析代码文件的语法树。
具体地,在对词法符号进行错误恢复处理后,可以根据错误恢复处理后的处理结果,生成待解析代码文件的语法树。
本申请实施例提供的生成代码文件的语法树的方法,与现有技术相比,基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理,从而有效解决了根据查找表查找不到可执行行为时,导致无法继续进行后续操作的情况,奠定后续生成待解析代码文件的语法树的必要基础;根据错误恢复处理的处理结果,生成待解析代码文件的语法树,提供了一种生成代码文件的语法树的方法,使得可以通过语法树对编写完成的代码文件进行静态分析,从而准确、高效地对编写完成的代码文件中的语法错误、编写错误等进行检查及纠正,极大节省程序开发人员的时间、精力等。
本申请实施例提供了另一种可能的实现方式,其中:
预定编程语言为C++编程语言与C语言中的任一种。下文将以C++编程语言为例进行介绍,其中,C语言的处理过程与C++编程语言相同,即本申请实施例所提供的方法对C++编程语言与C语言是兼容的。
具体地,在对任一词法符号进行错误恢复处理之前,还包括:
生成提醒用户任一词法符号发生错误的提示信息,并显示提示信息。
进一步地,对任一词法符号进行错误恢复处理,包括:
确定满足预定恢复条件的至少一组预定组合形式的目标词法符号、目标状态及目标非终结符;
基于任一组预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理。
进一步地,预定恢复条件为目标词法符号与第二表项在第一查找表中对应的第三表项为非空;
第二表项为目标状态与目标非终结符在第二查找表中对应的表项。
进一步地,确定满足预定恢复条件的至少一组预定组合形式的目标词法符号、目标状态及目标非终结符,包括:
将从当前状态栈的栈顶状态开始跳过预定个数的状态作为目标状态;
将从任一词法符号开始跳过预定个数的词法符号作为目标词法符号;
基于第二查找表中状态与非终结符之间的对应关系,将目标状态对应的非终结符确定为目标非终结符。
进一步地,基于任一组预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理,包括:
在状态栈中,对位于目标状态之上的状态进行出栈处理;
在符号栈中,对位于任一词法符号之上的词法符号进行出栈处理,且出栈的词法符号的数目与状态栈中出栈的状态数目相同;
通过将目标非终结符压入符号栈,并将第二表项压入状态栈,对任一词法符号进行错误恢复处理。
进一步地,还包括:
当存在多组预定组合形式的目标词法符号、目标状态及目标非终结符满足预定恢复条件时,基于第一组满足预定恢复条件的预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理;或者,基于多组预定组合形式中最高优先级的预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理。
进一步地,基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,包括:
对线性链表中的各个词法符号依次进行词性确定,且每当确定任一词法符号的词性时,根据任一词法符号的词性,查找第一查找表和第二查找表,得到对应的查找结果;
根据查找结果,对任一词法符号进行分析。
下面对本实现方式涉及到的错误恢复处理进行如下具体介绍:
当读入的词法符号(token)与当前状态栈栈顶状态在第一查找表(即Action表)中对应的表项为空时,即Action(state-stack.top(),token.cat)为(NULL,NULL),则说明词法符号(token)不被当前模型接受,存在语法错误,这样的词法符号称为error-token(错误词法符号)。
其中,可从当前状态栈栈顶开始向下寻找,找到一个状态s,从当前输入符号序列向后寻找,找到一个词法符号t,再找到一个第二查找表Goto(s)中的非终结符A,这些要寻找的目标(s,A,t)须符合如下条件:Action(Goto(s,A),t)!=NULL,NULL(即根据第一查找表查找到的表项非空),该条件可以称为“恢复条件”。找到符合恢复条件的(s,A,t)之后,将s之上的状态全部出栈,并从符号栈出栈相同个数的符号,再将A压入符号栈,将Goto(s,A)压入状态栈,这样就可以“跳过”错误继续执行,种跳过错误的过程称为“错误恢复”。
其中,对于(s,A,t)的查找可能有多种结果,可以在找到第一个结果时就根据该结果执行错误恢复,这种模式可以称为“慌乱模式”,也可以将多个结果进行比较,选出一个最优(例如优先级最高)的结果,根据该最优的结果执行错误恢复,这种模式可以称为“优选模式”。
需要说明的是,本申请实施例将发生的错误分为“代码结构性错误”和“普通语法错误”。其中,“代码结构性错误”是最严重的错误,比如括号不配对错误,遇到这类错误时不予包容,直接进行报错提示并结束执行,而对于一般语法错误则进行一定的包容。
根据上述关于错误恢复的描述,当遇到错误符号“error-token”时,可以找到一组(s,A,t)进行错误恢复(s为状态,A为非终结符,t为error-token或其之后的某个符号),本申请实施例先找到所有可以进行错误恢复的(s,A,t),并按“优选模式”选出最优结果(将该最优结果记作优先级最高的结果),如果找不到最优结果,则进入“慌乱模式”。其中,基于如下优选原则,进行最优结果(即优先级最高的结果)的选取:
1、对t的查找,尽量不跳出当前作用域,也不在当前作用域嵌套的作用域内查找,这样可以尽量不扰乱作用域,使出错代码之后的代码得以正确处理。
2、s距离状态栈栈顶应尽量近,t距离error-token也应尽量近,这样可以尽量少丢弃代码信息。
3、A应该选可以代表主要程序结构的非终结符,即在语法树中距离根结点近的非终结符,如表达式(expression)、语句(expression-statement、declaration-statement)等。如果A是靠近叶结点的非终结符,则有更大可能引入新错误。
其中,图2给出了错误恢复处理的过程示意图,图2中的A表示目标非终结符,t表示目标词法符号,get-tolerant-end-token表示确定t的查找边界,栈中有些符号会将作用域边界符号作为属性,可将作用域边界作为t的查找边界。另外,由符号栈栈顶向栈底查找,如果找到了尚未配对的左括号(“{”,“(”,“[”),那么t的查找边界就是与其配对的右括号。get-tolerant-symbol-and-token表示优选(s,A,t)的过程,stack-balance表示在寻找s的过程中,如果右括号需要出栈,那么与之配对的左括号以及这两个括号之间的符号也应该出栈,否则会扰乱作用域。
SYMBOL-WEIGHT-MAP为“名称-值”二元映射表,记录着所有可以被选择的非终结符名称和与之对应的权重,权重反映了非终结符语法层级的高或低,即与根结点的远近关系,其数值为经验值,权重越大表示越靠近语法树的叶结点。如“translation-unit”为开始符号,即语法树的根,其权重为1,“translation-unit”由“declaration-seq”组成,故“declaration-seq”权重为2。SYMBOL-WEIGHT-MAP这一“名称-值”二元映射表中的重要非终结符例举如下表2所示,在表2中,左侧为重要非终结符的英文名称,右侧为重要非终结符分别对应的中文说明以及权重,其中,括号中为权重。
表2-重要非终结符分别对应的中文说明以及权重
其中,“慌乱模式”与“优选模式”大体流程相似,但不受SYMBOL-WEIGHT-MAP表的限制,也不作结果比较,只要找到符合“恢复条件”的(s,A,t)即执行错误恢复。虽然慌乱模式一定可以从错误中恢复过来,但有可能扰乱了作用域也丢弃了大量的代码信息。
本申请实施例提供了另一种可能的实现方式,其中:
在根据所述任一词法符号的词性,查找第一查找表和第二查找表,得到对应的查找结果之前,可以预先对预定编程语言的文法进行分析生成第一查找表和第二查找表,其中,对预定编程语言的文法进行分析生成第一查找表和第二查找表的方式,包括:
确定预定编程语言中各种词法符号各自对应的类别和次序;
对文法符号进行划分,得到终结符号和非终结符号,文法符号包括各种词法符号及各种词法符号各自对应的类别,词法符号属于终结符号,类别属于非终结符号,非终结符号表示预定编程语言的层次结构;
依据各种词法符号分别对应的次序,确定各种词法符号分别归属的非终结符;
依据预生成的预定编程语言的状态以及终结符号,生成第一查找表,并依据预生成的预定编程语言的状态以及非终结符号,生成第二查找表。
其中,文法包括开始符号和产生式列表;开始符号为预定义的非终结符号,产生式列表中的任一产生式表征终结符和非终结符之间的关系。
上述任一产生式包括左部与右部,左部为右部归属的非终结符,右部为终结符的序列和/或非终结符的序列。
上述左部的非终结符对应语法树中的结点,右部中的任一终结符或任一非终结符为该结点的子结点。
具体地,文法是对预定编程语言结构的精确描述,指明了各种代码元素(即词法符号token)的类别和次序。比如,文法指明了C++编程语言的代码文件是由类声明、函数定义、命名空间声明等类别组成的,其中,命名空间声明又可以由类声明、函数定义、命名空间声明等类别组成,这种类别以及代码元素(即词法符号token)统称为“文法符号(symbol)”。
进一步地,文法符号可以分为“非终结符”和“终结符”,“非终结符”给出了编程语言的层次结构,这种层次结构是语法解析的关键也是本申请实施例的目标。直观地说,类别对应“非终结符”,直接组成代码的各个词法符号(token)为“终结符”。词法符号(token)可以直接作为终结符转为文法符号(symbol),而非终结符在归类后会对应多个词法符号(token)或非终结符。
以代码“struct A{int a;};”为例,该段代码由"struct"、"A"、"{"、"int"、"a"、";"、"}"、";"等词法符号(token)组成,这些词法符号(token)即终结符,文法指明这种次序下的终结符,可以归类为非终结符"class-specifier"(即类的声明)。其中,“归类”按编译学领域的术语也可以称为“归约”,下文的“归类”与“归约”具有相同的意思。
其中,文法符号的通用属性如下表3所示,表3中的左侧为各属性的英文名称,右侧为各英文名称分别对应的中文说明或解释。
表3-文法符号(symbol)的通用属性
此外,具体符号有具体的属性记录着模型在运行时需要的信息。
进一步地,文法由开始符号和产生式列表组成,如下所示:
1、开始符号,开始符号为一个非终结符,该非终结符对应着整个编程语言的顶层描述。本申请实施例中的开始符号为"translation-unit",其中,开始符号对应语法树的根。
2、产生式列表(production-list),产生式列表描述了终结符与非终结符的组成关系,一个产生式由“左部”和“右部”组成,左部为非终结符,右部为终结符或非终结符的序列。其中,产生式列表中的任一产生式可以形式化的表示为:“左部→右部”。
例如:selection-statement→if(condition)statement。其中,“selection-statement”为左部,是一个非终结符,右部由关键字“if”、终结符“(”、非终结符“condition”、终结符“)”、非终结符“statement”的序列组成,表示该右部的序列“if(condition)statement”可以归类为左部的非终结符“selection-statement”。
其中,在语法树中,产生式左部的非终结符对应一个结点,右部中的终结符和非终结符是其子结点。产生工的基本属性如下表4所示,表4中的左侧为各属性的英文名称,右侧为各英文名称分别对应的中文说明或解释。
表4-产生式基本属性
left | 左部 |
right | 右部 |
length() | 右部各符号的个数 |
进一步地,基于对预定编程语言的文法进行分析生成的第一查找表可以为Action(行为)表,第二查找表可以为Goto(转到)表。其中,Action表为“状态-终结符词性”二维映射表,即一个状态和一个终结符的词性对应一个表项,Action表项可以形式化地表示为Action(state,cat),state为状态,cat为词性。Action(state,cat)的值可以表示为一个二元组(m,x),m的取值有五种情况,如下所示:
1、移进(S),表示可以将一个新的状态移进状态栈,并将当前读入的词法符号(token)转为文法符号(symbol)移进符号栈。其中,当m为S(移进)时,x的值表示新状态。
2、归约(R),表示符号栈栈顶某个长度的连续符号序列,可以按某个产生式进行归约,这个连续符号序列即为产生式的右部。其中,归约具体可以是:将这个符号序列从符号栈出栈,同时将相同长度的状态序列从状态栈出栈,接着将相应产生式的左部压入符号栈,接着将当前符号栈栈顶状态和刚入栈的左部,在Goto表查找新的状态,并将该查找到的新的状态压入状态栈。当m为R(归约)时,x表示要归约的产生式在产生式列表中的序号,该产生式可以表示为production-list(x)。
3、归约-移进冲突(CSR),表示当前即可以移进也可以归约。当m为CSR时,x为二元组,其第一个分量为要移进的状态,第二个分量为要归约的产生式序号。
4、归约-归约冲突(CRR),表示当前可按不同的产生式进行归约,当m为CRR时,x为二元组,其两个分量分别表示要规约的产生式序号。
5、空(NULL),表式错误,即模型不接受当前词法符号(token),当m为空时,x没有意义,约定其也为NULL(空)。
其中,Action表是由文法中的“产生式列表文件”经LR1算法得到的。为便于讨论,本申请实施例用Action(state,cat)表示Action表项,当没有cat参数时,Action(state)表示Action表中,state对应的所有词性集合。
进一步地,Goto表为“状态-非终结符名称”二维映射表,即一个状态和一个非终结符对应一个表项,每个表项表示一个状态,Goto表中的表项可以表示为Goto(state,symbol-name),其中state为状态,symbol-name为非终结符名称。Goto表在对产生式进行归约时用到,表示规约之后解析器应处的状态。当前状态栈栈顶状态在Goto表中对应的各个非终结符,分别表示接下来要归约的各种可能。
其中,Goto表是由文法中的“产生式列表文件”经LR1算法得出的。为便于讨论,本申请实施例用Goto(state,symbol-name)表示Goto表项,当没有参数symbol-name时,Goto(state)表示在Goto表中state对应的所有非终结符。
进一步地,符号栈(symbol-stack)与状态栈(state-stack),是以栈为结构保存模型运行时符号关系和状态关系的,其中,符号栈与状态栈的基本属性如下表5所示,在表5中,左侧为各属性的英文名称,右侧为各英文名称分别对应的中文说明或解释。
表5-产生式基本属性
top() | 栈顶元素 |
pop() | 出栈,个数由参数n指定 |
push() | 将参数x指定的元素入栈 |
size() | 栈内元素个数 |
进一步地,依次对线性链表中的各个词法符号进行分析,包括:
当线性链表中当前的词法符号为表示作用域开始的词法符号时,在作用域栈的栈顶创建相应的栈顶作用域,并将表示作用域开始的词法符号之后的词法符号写入所述栈顶作用域;以及,当线性链表中当前的词法符号为表示作用域结束的词法符号时,对栈顶作用域进行出栈处理。
其中,作用域栈的栈底作用域为全局作用域,除栈底作用域外的作用域为局部作用域。
具体地,若当前从线性链表中读入的词法符号为表示作用域开始的词法符号(例如“{”)时,在作用域栈的栈顶创建相应的栈顶作用域,并将表示作用域开始的词法符号之后的词法符号(例如变量、关键字等)写入该创建的栈顶作用域中。若该表示作用域开始的词法符号(例如“{”},为对线性链表中的词法符号进行解析的过程中首次遇到的,则此时作用域栈的栈底即为作用域的栈顶,该栈底作用域为全局作用域。若该表示作用域开始的词法符号(例如“{”),不是在对线性链表中的词法符号进行解析的过程中首次遇到的,则此时在作用域栈的栈顶创建新的作用域,即栈顶作用域,该栈顶作用域为局部作用域,接着将该表示作用域开始的词法符号之后的词法符号写入该创建的栈顶作用域中。
进一步地,若当前从线性链表中读入的词法符号为表示作用域结束的词法符号(例如“}”)时,表示作用域结束,此时对栈顶作用域进行出栈处理,即将写入该栈顶作用域中的词法符号读出,同时将该创建的栈顶作用域删除。
进一步地,在C++编程语言中,命名空间、类型和对象在语义上按“作用域(scope)”组织,作用域可呈嵌套关系。命名空间、类型、函数体等拥有自己的作用域,其中,不同作用域中的标识符可以重名,而且同一作用域中的类型名和对象名也可以重名。本申请实施例用栈来分析各作用域,其中,作用域栈是“动态词性确定”的重要参照依据。
进一步地,作用域可分为命名空间作用域、类作用域、函数体作用域等,不同作用域有其具体属性,作用域(scope)的通用属性如下表6所示,在表6中,左侧为各属性的英文名称,右侧为各英文名称分别对应的中文说明或解释。
表6-作用域的通用属性
parent() | 直接包含当前作用域的作用域 |
types | 在当前作用域中定义的类型作用域集合 |
declarators | 在当前作用域中定义的对象集合 |
usingNameSpaces | 当前作域引用的命名空间作用域集合 |
bases | 当前作域的基类作用域集合 |
本申请实施例提供了另一种可能的实现方式,其中:
在实际应用中,可以基于对预定编程语言的文法进行分析,预先生成该预定编程语言的第一查找表与第二查找表。后续在生成该预定编程语言的待解析代码文件时,可以基于该预先生成的第一查找表和第二查找表,依次对待解析代码文件对应的线性链表中的各个词法符号进行分析,从而生成待解析代码文件的语法树,使得可以通过该语法树对待解析代码文件进行静态分析。
其中,线性链表中的各个词法符号具有各自对应的词性,而且在基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析的过程中,实际上是依据各个词法符号各自对应的词性,来查找第一查找表和第二查找表的。其中,第一查找表是根据词法符号的词性,确定读入该词法符号后对应执行的操作或动作的,同时第一查找表会嵌套调用第二查找表。因此在基于第一查找表和第二查找表进行查找之前,需要先确定读入的词法符号的词性,从而根据词法符号的词性查找第一查找表和第二查找表,得到对应的查找结果。
假如当前读入的词法符号为“int”,经词性确定其词性为“类型名”,于是可以根据该词法符号“int”的词性“类型名”,查找第一查找表和第二查找表,得到对应的查找结果。
具体地,确定线性链表中任一词法符号的词性,包括:
根据上下文对任一词法符号进行词性判定,确定任一词法符号的词性;和/或,
根据上下文对任一词法符号进行词性猜测,确定任一词法符号的词性。
具体地,根据上下文对任一词法符号进行词性判定,确定任一词法符号的词性,包括以下任一种情形:
当任一词法符号为类型名、对象名以及关键字中的任一项,则将其作为任一词法符号的词性;
当任一词法符号为预设类型的词法符号,判定任一词法符号为模板列表符号或者运算符号,将判定结果作为任一词法符号的词性;
在作用域中进行信息查找,并将查找结果作为任一词法符号的词性。
具体地,在作用域中进行信息查找,并将查找结果作为任一词法符号的词性,包括:
在作用域中进行横向查找,并将横向查找的查找结果作为任一词法符号的词性;
横向查找为在当前作用域及引用的命名空间中进行查找,或者在当前作用域的基类作用域中进行查找。
具体地,还包括:
若通过横向查找未查找到查找结果,则在作用域中进行纵向查找,并将纵向查找的查找结果作为任一词法符号的词性;
纵向查找为在包括当前作用域的作用域中进行查找。
具体地,根据上下文对任一词法符号进行词性猜测,确定任一词法符号的词性,包括:
根据上下文猜测任一词法符号的词性是否为命名空间名称;
如果猜测不是命名空间名称,且当猜测任一词法符号的词性是类型名且非对象名时,若任一词法符号之后的第一个词法符号不是预设类型词法符号,则确定任一词法符号的词性是类型名,若任一词法符号之后的第一个词法符号是预设类型的词法符号,则通过根据任一词法符号进行第一预设处理,确定任一词法符号的词性。
具体地,该方法还包括:
如果猜测不是命名空间名称,且当猜测任一词法符号的词性是对象名且非类型名时,确定任一词法符号的词性是对象名。
具体地,该方法还包括:
如果猜测不是命名空间名称,且当猜测任一词法符号的词性是对象名且是类型名时,根据任一词法符号的前后关系确定任一词法符号是否为类型名,如果是类型名,则确定任一词法符号的词性是类型名,如果不是类型名,则通过根据任一词法符号进行第二预设处理,确定任一词法符号的词性。
下面对本实现方式中涉及到的具体内容进行如下详细说明:
在本申请实施例的上述文法中,类型名(例如类名、结构体名、联合体名、枚举类名等)和对象名(例如变量名、函数名、自定义类的对象名等)是作为终结符参与文法制定的,故在读入标识符时需要判定该标识符是类型名(TYPE-NAME)还是对象名(IDENTIFIER)。其中,可以从之前读入的词法符号和已构建的语法树中查找到当前词法符号的信息,从而判断当前词法符号是类型名还是对象名。
例如如下代码:
对于final(最终)、override(重写)等C++98之后引入的关键字,也可以作为对象名,需要在语法分析时根据上下文判断其是否为关键字。换言之,当任一词法符号为类型名、对象名以及关键字中的任一项,则将其作为任一词法符号的词性。即,如果任一词法为类型名,则将该类型名作为该任一词法符号的词性,如果任一词法符号为对象名,则将该对象名作为该任一词法符号的词性,如果任一词法符号为关键字,则将该关键字作为该任一词法符号的词性。
又例如如下代码:
对于“<”、“>”等预设类型的词法符号,需要判断其是否为模板列表的开始或结束,还是大于号和小于号,对于“>>”这一预设类型的词法符号,需要判断其为移位运算符,还是两个模板列表结束符写在了一起,如果是写在一起需要将“>>”在线性链表(token-list)中将其替换成两个“>”。换言之,当任一词法符号为预设类型的词法符号,判定该任一词法符号为模板列表符号或者运算符号,将判定结果作为该任一词法符号的词性。
其中,产生式列表文件中的所有需要作词性确定的特殊终结符如下表7所示,在表7中,左侧为各特殊终结符的英文名称,右侧为各特殊终结符分别对应的中文说明或解释。
表7-所有需要作词性确定的特殊终结符
另外,由于本申请实施例生成的语法树用于对代码文件进行静态分析,故一些不完整的代码也是可以接受的。其中,当无法确定某些词法符号的词性时,利用“词性猜测机制”猜测符号的词性。
换言之,确定线性链表中任一词法符号的词性的整体过程可以包括如下三类子过程:(1)关键字、运算符词性确定;(2)通过作用域信息查找确定词性;(3)通过词性猜测确定词性。下面先对上述的(1)与(2)进行如下具体的举例介绍,后续通过另一实现方式对上述的(3)进行举例介绍。
针对上述(1)与(2),例如如下代码:
struct A
{};
int A=0;
int fun()
{
struct A a;//#1
return A;//#2
}
在此示例中,在分析#1处的“A”时,进行作用域信息查找,发现“A”即是类名也是变量名,而#1处有关键字“struct”限定,只接受类名,故#1处“A”的词性为TYPE-NAME(类型名)。在分析#2处的“A”时,发现当前即可接受类名也可以接受变量名,优先按变量名算,故#2处的“A”词性为IDENTIFIER(变量名)。其中,当通过“关键字、运算符词性确定定”和“作用域信息查找”无法获取到词性信息时,则开启“词性猜测”机制,即通过词性猜测确定词性。图3为按照上述代码示例确定词性的过程示意图。
另外,对于在类声明中定义的成员函数,需要特殊处理,因为成员函数的实现可以先于类成员的声明,例如如下代码:
其中,成员变量m的声明晚于成员函数fun的定义,这是合法的,但如果直接解析“fun”函数体内的“m”,则无法获取到词性信息,故在读入“fun()”后的“{”时,将函数体转移到类声明之外,并将函数体替换成“;”,转移之后的函数声明信息存放在终结符“TRIMED-DECL-INFO”中。图4给出了获取关键字与运算符的词性的过程示意图。
另外,作用域信息查找分为“横向查找(find-transverely)”和“纵向查找(find)”。“横向查找”即在当前作用域及引用的命名空间或当前作用域的基类作用域中查找,“纵向查找”即“横向查找”无结果时,到包含当前作用域的作用域中查找。图5给出了在作用域中进行信息查找确定词性的过程示意图。
在根据上下文对任一词法符号进行词性猜测,确定任一词法符号的词性的过程中,包括如下几个关键子过程:(1)like-simple-type-name:通过符号的前后关系,判断词性是否可以为TYPE-NAME(类型名),不判断词法符号(token)之后为“<”的情况。(2)match-parametric-type-name:当词法符号(token)之后为“<”时,判断“<”是否为模版参数列表的起始符。(3)find-template-list-end:如果“<”为模版参数列表的起始,返回与之对应的“>”。(4)handle-sticky-template-list-end:处理写在一起的模版参数列表终止符“>>”。
其中,图6给出了通过词性猜测确定词法符号的词性的过程示意图,图6的accept(x)函数中的x表示词性(例如类型名、对象名等),accept(x)函数表示模型当前是否可以接受以x为词性的词法符号token,accept(x)函数等价于Action(state-stack.top(),x)!=(NULL,NULL),表示根据第一查找表Action表查找到的表项非空,x表示词性,state-stack.top()表示状态栈的顶部。另外,图6中的acc-tp与acc-id为表示变量名的字符,第一预设处理为上述的find-template-list-end处理与handle-sticky-template-list-end,第二预设处理为上述的match-parametric-type-name处理与handle-sticky-template-list-end处理,在此不再赘述。
本申请实施例提供了另一种可能的实现方式,其中:
在根据每次词性确定后的词法符号的词性,查找第一查找表和第二查找表之后,还包括:根据查找结果进行冲突消除处理。
当在根据词法符号的词性查找第一查找表和第二查找表的过程中,出现冲突时,即同时查找到了多种可能的处理行为,无法确定应该选择哪种行为时,根据查找结果进行冲突消除处理。接着根据冲突消除处理后的结果生成语法树。
下面对冲突消除处理进行如下详细介绍:
具体地,基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并确定任一词法符号属于预定冲突类型时,对任一词法符号进行相应的冲突消除处理;根据冲突消除处理的处理结果,生成待解析代码文件的语法树。其中,当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理;根据错误恢复处理的处理结果,生成待解析代码文件的语法树。
具体地,预定冲突类型,包括以下任一种:
任一词法符号的处理属于移进处理与归约处理的冲突;
任一词法符号的处理属于第一归约处理与第二归约处理的冲突。
具体地,在对任一词法符号进行冲突消除处理之前,还包括:
对当前的处理状态进行保存,得到第一保存结果。
具体地,对任一词法符号进行冲突消除处理,包括:
根据上下文对任一词法符号进行第一目标处理,并基于第一目标处理对任一词法符号之后的词法符号依次进行相应处理;
若直至完成重试结束符的处理,均未发生处理错误,则删除第一保存结果,并继续对后续词法符号进行相应处理。
具体地,还包括:
若在对任一词法符号之后的词法符号依次进行相应处理的过程中发生处理错误,则按照第一保存结果进行恢复处理,并对任一词法符号进行第二目标处理,并对任一词法符号之后的词法符号依次进行相应处理。
具体地,对任一词法符号进行冲突消除处理的情形,包括以下任一种:
当任一词法符号属于第一预定类型时,若任一词法符号发生归约处理与移进处理之间的冲突,则对任一词法符号进行移进处理;
当任一词法符号属于第二预定类型时,若任一词法符号发生移进处理与归约处理之间的冲突,则根据词法符号之间的线性关系,确定对任一词法符号进行移进处理或者归约处理;
当任一词法符号属于第三预定类型时,若任一词法符号发生第一归约处理与第二归约处理之间的冲突,则根据词法符号之间的线性关系,确定对任一词法符号进行第一归约处理或者第二归约处理。
下面对上述的冲突消除处理进行如下具体介绍:
因为存在冲突,本申请实施例采用的产生式列表不属于经典的LR1文法,而构造C++编程语言的标准LR1文法模型较为困难,故本申请实施例实现了冲突消除处理模块。
例如,在产生式列表文件中,函数参数声明是非终结符declarator的一部分,而declarator之后可以接初始化列表,始初化列表也可以由“(”开始,比如:
int a(0);//声明变量a,其初值为0,"(0)"为初始化列表
int a(int);//声明函数a,其有一个int型的参数,"(int)"为参数列表
这便造成了冲突,在读入int和a两个词法符号后,符号栈顶为noptr-declarator,而当读入“(”时,无法确定该“(”是参数列表的开始,还是初始化列表的开始,即无法确定是将栈顶符号归约成ptr-declarator,还是继续移进状态到匹配参数列表。于是,这时需要根据上下文来判断“(”之后的内容是参数列表还是初始化列表。在有些情况下可以根据符号之间的线性关系得出确定性的结论,而有些情况无法得出确定性结论,当无法得出确定性结论时,可以先按参数列表进行解析,如果可以通过(即不出现解析错误),则确定为参数列表,如果发生解析错误(即不通过),则按初始化列表解析,即提供了一种“可重试机制”。
第一:以下产生式当面临“规约-移进冲突”时,强制归定了必须进行移进。即当任一词法符号属于第一预定类型时,若任一词法符号发生归约处理与移进处理之间的冲突,则对任一词法符号进行移进处理。其中,任一词法符号属于第一预定类型的情形如下所示:
(1)selection-statement→if(condition)statement
读入“else”时,强制规定不进行规约,而是移进,即每个“else”与距其最近的“if”配对。
(2)class-specifier→class-head
在定义类的成员类时,读入“:”,强制规定不进行规约,而是移进。
冲突的原因是“:”可以是基类列表的开始,也可以是位域的开始,从语义上来说class-head不应受到位域限制,故可强制直接移进。
(3)exception-specification→noexcept
读入“(”时,“(”可以是noexcept声明的一部分,也可以初始化列表的一部分,强制规定为移进。
例如:int(*pf)(int)noexcept(0);
其中,“(0)”可以和noexcept组合,指定pf指向一个可以抛出任何异常的函数,而也可以作为pf的初始值,这是文法上的歧义,强制规定与noexcept组合。
(4)enum-base→type-name与enum-base→type-modifier-seq type-name
读入“const(常量)”、“volatile(变量)”时,强制规定移进,冲突的原因是因为枚举变量可以这样定义:
enum E:int const e=xxx;
其中,const有歧义,存在两种解释:
1、enum E:int规约为enum-specifier,const规约为type-modifier-seq
2、int const规约为enum-base,enum E:int const整体规约为enum-specifier
基于标准强制按第2种方式进行规约。
(5)new-type-id→trailing-type-specifier与new-declarator→new-ptr-operator
在读入“*”时,强制规定移进,冲突的原因是new-expression可以作为基本表达式,“*”可以解释为指针也可以解释为乘号。
按C++标准,new-expression作乘法没有意义,故强制规定为移进。
(6)conversion-type-id→trailing-type-specifier与conversion-declarator→ptr-operator
在读入“*”、“&”、“&&”时,强制规定移进,冲突的原因是conversion-type-id可以作为基本表达式的一部份,所以“*”、“&”、“&&”既可以是ptr-operator,也可以是运算符,强制规定这些符号为ptr-operator。
第二:以下产生式在面临“规约-移进冲突”时,可根据符号栈以及符号的线性关系来判断具体需要进行“规约”还是“移进”,如果判断不出来,则可开启重试模式。即,当任一词法符号属于第二预定类型时,若任一词法符号发生移进处理与归约处理之间的冲突,则根据词法符号之间的线性关系,确定对任一词法符号进行移进处理或者归约处理。其中,任一词法符号属于第二预定类型的情形如下所示:
(1)ptr-declarator→noptr-declarator
在读入“(”时,需要判断“(”是参数列表的开始,还是初始化列表的开始。如果是参数列表的开始,则移进。如果是初始化列表的开始,将noptr-declarator归约为ptr-declarator。如果无法确定,则开启重试机制。
(2)type-name→nested-class-name
在读入“(”时,需要判断“(”是参数列表的开始,还是declarator的开始。如果是参数列表的开始,则移进。如果是declarator的开始,则将nested-class-name归约为type-name。如果无法确定,则开启重试机制。
例如(设A为某类名):
A(a);//对象定义,"(a)"为declarator
A::A(int);//构造函数,"(int)"为参数列表
(3)simple-type-specifier→type-name
在读入“(”时,需要判断“(”之后为表达式,还是对象定义。如果为表达式,则移进。如果为对象定义,则将type-name归约为simple-type-specifier。如果无法确定,则进入重试模式。
例如:
(4)enum-specifier→enum-head
在定义类的成员枚举类时,读入“:”,该产生式会面临归约-移进冲突,需要判断“:”之后是位域定义,还是枚举基类定义。如果“:”的后面不是类型名,则按将enum-head归约为enum-specifier,否则移进。
(5)ptr-abstract-declarator→ptr-operator
在定义trailing-return-type时,读入“(”,该产生式会面临归约-移进冲突,需要判断“(”是trailing-type-id的一部分,还是初始化列表的开始。如果是则将ptr-operator规约成ptr-abstract-declarator。各主流编译器对此均无支持,故可直接移进。
(6)ptr-abstract-declarator→noptr-abstract-declarator
在定义trailing-return-type时,读入“(”,该产生式会面临归约-移进冲突,需要判断“(”是trailing-type-id的一部分,还是初始化列表的开始。如果是则将noptr-abstract-declarator规约成ptr-abstract-declarator。各主流编译器对此均无支持,故可直接移进。
(7)trailing-type-id→trailing-type-specifier
在定义trailing-return-type时,读入“(”,该产生式会面临归约-移进冲突,需要判断“(”是trailing-type-id的一部分,还是初始化列表的开始。如果是则将trailing-type-specifier规约成trailing-type-id。各主流编译器对此均无支持,故可直接移进。
(8)unary-operator→~
读入TYPE-NAME或decltype时,需要判断“~”是取反运算符还是析构函数标识符。如果是取反运算符,则将栈顶“~”规约为unary-operator。如果是析构函数标识符,则移进。
例如:
第三:规约-规约冲突,以下产生式在面临“规约-规约冲突”时,可根据符号栈以及符号的线性关系来判断具体需要进行规约为哪个产生式。即当任一词法符号属于第三预定类型时,若任一词法符号发生第一归约处理与第二归约处理之间的冲突,则根据词法符号之间的线性关系,确定对任一词法符号进行第一归约处理或者第二归约处理。其中,任一词法符号属于第三预定类型的情形如下所示:
(1)braced-init-list→{}与compound-statement→{}
在读入“;”时,需要判断符号栈顶是否为函数,如果是函数则按compound-statement→{}规约,否则按braced-init-list→{}规约。
(2)initializer-clause→assignment-expression与expression→assignment-expression
在读入“,”时,需要判断符号栈顶是否为函数,如果是函数则按expression→assignment-expression规约,否则按initializer-clause→assignment-expression规约。
另外,当无法判断当前应该“移进”还是“规约”时,可以将模型当前的情况保存为“快照(snapshot)”,然后试验性地按其中一种动作执行,如果发生错误,则将模型按之前保存的快照恢复,再按另一种动作执行,即“重试机制”。每一个快照对应一个“重试结束符(trial end)”,如果保存快照后一直到读入重试结束符都没有发生错误,即可以取消快照继续执行。
例如在如下列代码中:int fun(int(*)(int(x)));
若读入第一个“(”无法判断之后是否为参数列表,则需要保存模型快照,该快照对应的重试结束符为最后一个“)”,然后试验性地按参数列表执行,如果从保存快照开始到读入重试结束符都没有发生错误,则认为括号内就是参数列表,之前保存的快照可以取消。其中,图7给出了冲突消除处理的过程示意图。
图7中的judge-CSR表示根据当前冲突选择应该执行的动作,save-snapshot表示将符号栈、状态栈、作用域栈、当前输入符号等存入快照,snapshot-stack表示快照之间用栈来组织,即在重试过程中也可以发生重试,roll-back表示使模型恢复到以前的状态,set-trial-end(x)表示将符号x设为重试结束符,cancel-trial-end(x)表示将符号x设为非重试结束符,roll-back-flag用于指明之前是否执行了roll-back,如果roll-back-flag为true(真),表示执行roll-back,如果roll-back-flag为false(假),表示不执行roll-back。另外,图2中的token为词法符号,m与x分别为查找第一查找表(Action表)得到的表项,R表示归约,S表示移进,x=x(0)表示第一归约处理或第二归约处理,x=x(1)表第二归约处理或第一归约处理。
本申请实施例提供了另一种可能的实现方式,其中:
图8给出了本申请实施例的基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,生成待解析代码文件的语法树的过程示意图,该过程中包括词性确定、查找第一查找表和第二查找、冲突消除处理及错误恢复处理等各个子过程。其中,图8中的“token”表示当前读入的词法符号,“token-list.first”表示线性链表的第一个符号,“state-stack.top()”表示根据第二查找表查找到的状态,“token.cat”表示当前读入的词法符号的词性,Action(state,cat)的值可以表示为一个二元组(m,x),m的取值为R表示归约行为,m的取值为S表示移进行为,state表示状态,cat表示词性,token=token.next()表示将当前词法符号之后的词法符号作为当前词法符号。
进一步地,移进过程是由第一查找表(Action表)指示的,在将读入符号及相关状态移进符号栈和状态栈时,执行“移进过程”,如图9所示,
图9给出以词法符号(token)为例,详细介绍了移进过程的处理流程。其中,如果当前读入词法符号(token)和作用域相关,先进行作用域操作,然后将词法符号(token)转为文法符号移进符号栈,并将新状态移进状态栈,此过程即“移进过程”。和作用域相关的符号有“{”、“}”、“if”、“for”、“while”、“switch”、“catch”等。例如,读入词法符号(token)为“{”时,需要根据符号栈和作用域栈判断相关作用域的类型及操作:(1)如果符号栈栈顶为“named-namespace-head”,说明“{”是命名空间声明的开始,如果符号栈栈顶为“class-head”,说明“{”是类成员声明的开始,需要创建相关作用域,并将新作用域压入作用域栈,在读入“{”对应的“}”时,说明新作用域已完毕,这时将新作用域出栈。(2)如果符号栈栈顶为“declarator”、“constructor”、“destructor”、“convertor”等符号,说明“{”是函数定义的开始,需要进行函数体作用域的相关操作。(3)如果符号栈栈顶不在预期之内,而当前作用域栈栈顶为函数体作用域,说明“{”是复合语句“compound-statement”的开始,复合语句也有自己的作用域。
由于C++0x标准允许if、while、for等语句的条件或始初化过程中定义新对象,所以在读入这种符号时需要创建作用域。
例如如下代码:
其中,读入“if”时创建一个新作用域(设为“if-scope”),“if(int a=x)”中的“a”在相关产生式规约时会将“a”的变量定义信息写入“if-scope”,在后面分析“if-else”语句中的“a”时,可由“词性确定”得出其为局部变量。“if”语句中的“{a=1;}”仍然作为一个作用域嵌入“if-scope”。可在产生式:selection-statement→if(condition)statementelse statement归约时将“if-scope”出栈。
对于“(”,有时也与作用域相关,例如如下代码:
其中,类的成员函数在类声明作用域之外实现时,其参数列表所在的作用域是类声明作用域,在分析“int A::fun(type*p)”中的“type”时,由“词性确定”可以直接得出其为“A”中定义的类型名,且按C++标准,不必写成“int A::fun(A::type*p)”。
进一步地,归约过程由Action表指示,符号栈栈顶的符号序列可按某产生式规约时,执行“规约过程”。将属于产生式右部的符号序列从符号栈出栈,左部符号入栈,并将右部符号序列记入左部符号中,执行所有归约过程之后,就将各符号组成了语法树。其中,还需要在规约过程中记录或推导各符号的属性以供本申请实施例中的各种机制使用。
实施例二
图10为本申请实施例提供的一种生成代码文件的语法树的装置的结构示意图,如图10所示,该装置100可以包括解析模块101、处理模块102及语法树生成模块103,其中:
解析模块101用于在接收到预定编程语言的待解析代码文件时,通过词法解析模块对待解析代码文件中的各个词法符号进行解析并生成相应的线性链表;
处理模块102用于基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理;
语法树生成模块103用于根据错误恢复处理的处理结果,生成待解析代码文件的语法树。
进一步地,还包括提示模块104,如图11所示,其中:
提示模块104用于生成提醒用户任一词法符号发生错误的提示信息,并显示提示信息。
进一步地,处理模块102包括确定子模块1021与错误恢复子模块1022,如图11所示,其中:
确定子模块1021用于确定满足预定恢复条件的至少一组预定组合形式的目标词法符号、目标状态及目标非终结符;
错误恢复子模块1022用于基于任一组预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理。
进一步地,预定恢复条件为目标词法符号与第二表项在第一查找表中对应的第三表项为非空;
第二表项为目标状态与目标非终结符在第二查找表中对应的表项。
进一步地,确定子模块1021具体用于将从当前状态栈的栈顶状态开始跳过预定个数的状态作为目标状态;以及用于将从任一词法符号开始跳过预定个数的词法符号作为目标词法符号;以及用于基于第二查找表中状态与非终结符之间的对应关系,将目标状态对应的非终结符确定为目标非终结符。
进一步地,错误恢复子模块1022具体用于在状态栈中,对位于目标状态之上的状态进行出栈处理;以及用于在符号栈中,对位于任一词法符号之上的词法符号进行出栈处理,且出栈的词法符号的数目与状态栈中出栈的状态数目相同;以及用于通过将目标非终结符压入符号栈,并将第二表项压入状态栈,对任一词法符号进行错误恢复处理。
进一步地,还包括选择子模块1023,如图11所示,其中:
选择子模块1023用于当存在多组预定组合形式的目标词法符号、目标状态及目标非终结符满足预定恢复条件时,基于第一组满足预定恢复条件的预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理;或者,基于多组预定组合形式中最高优先级的预定组合形式的目标词法符号、目标状态及目标非终结符,对任一词法符号进行错误恢复处理。
进一步地,处理模块102包括词性确定子模块1024与分析子模块1025,如图11所示,其中:
词性确定子模块1024用于对线性链表中的各个词法符号依次进行词性确定,且每当确定任一词法符号的词性时,根据任一词法符号的词性,查找第一查找表和第二查找表,得到对应的查找结果;
分析子模块1025用于根据查找结果,对任一词法符号进行分析。
进一步地,预定编程语言为C++编程语言与C语言中的任一种。
本申请实施例提供的装置,与现有技术相比,基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理,从而有效解决了根据查找表查找不到可执行行为时,导致无法继续进行后续操作的情况,奠定后续生成待解析代码文件的语法树的必要基础;根据错误恢复处理的处理结果,生成待解析代码文件的语法树,提供了一种生成代码文件的语法树的方法,使得可以通过语法树对编写完成的代码文件进行静态分析,从而准确、高效地对编写完成的代码文件中的语法错误、编写错误等进行检查及纠正,极大节省程序开发人员的时间、精力等。
实施例三
本申请实施例提供了一种电子设备,如图12所示,图12所示的电子设备1200包括:处理器1201和存储器1203。其中,处理器1201和存储器1203相连,如通过总线1202相连。进一步地,电子设备1200还可以包括收发器1204。需要说明的是,实际应用中收发器1204不限于一个,该电子设备1200的结构并不构成对本申请实施例的限定。
其中,处理器1201应用于本申请实施例中,用于实现图10或图11所示的解析模块、处理模块及语法树生成模块的功能,以及图11所示的提示模块的功能。
处理器1201可以是CPU,通用处理器,DSP,ASIC,FPGA或者其他可编程逻辑器件、晶体管逻辑器件、硬件部件或者其任意组合。其可以实现或执行结合本申请公开内容所描述的各种示例性的逻辑方框,模块和电路。处理器1201也可以是实现计算功能的组合,例如包含一个或多个微处理器组合,DSP和微处理器的组合等。
总线1202可包括一通路,在上述组件之间传送信息。总线1202可以是PCI总线或EISA总线等。总线1202可以分为地址总线、数据总线、控制总线等。为便于表示,图12中仅用一条粗线表示,但并不表示仅有一根总线或一种类型的总线。
存储器1203可以是ROM或可存储静态信息和指令的其他类型的静态存储设备,RAM或者可存储信息和指令的其他类型的动态存储设备,也可以是EEPROM、CD-ROM或其他光盘存储、光碟存储(包括压缩光碟、激光碟、光碟、数字通用光碟、蓝光光碟等)、磁盘存储介质或者其他磁存储设备、或者能够用于携带或存储具有指令或数据结构形式的期望的程序代码并能够由计算机存取的任何其他介质,但不限于此。
存储器1203用于存储执行本申请方案的应用程序代码,并由处理器1201来控制执行。处理器1201用于执行存储器1203中存储的应用程序代码,以实现图10或图11所示实施例提供的生成代码文件的语法树的装置的动作。
本申请实施例提供的电子设备,包括存储器、处理器及存储在存储器上并可在处理器上运行的计算机程序,处理器执行程序时,与现有技术相比,可实现:基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理,从而有效解决了根据查找表查找不到可执行行为时,导致无法继续进行后续操作的情况,奠定后续生成待解析代码文件的语法树的必要基础;根据错误恢复处理的处理结果,生成待解析代码文件的语法树,提供了一种生成代码文件的语法树的方法,使得可以通过语法树对编写完成的代码文件进行静态分析,从而准确、高效地对编写完成的代码文件中的语法错误、编写错误等进行检查及纠正,极大节省程序开发人员的时间、精力等。
本申请实施例提供了一种计算机可读存储介质,该计算机可读存储介质上存储有计算机程序,该程序被处理器执行时实现实施例一所示的方法。与现有技术相比,基于第一查找表和第二查找表,依次对线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在第一查找表中对应的第一表项为空时,对任一词法符号进行错误恢复处理,从而有效解决了根据查找表查找不到可执行行为时,导致无法继续进行后续操作的情况,奠定后续生成待解析代码文件的语法树的必要基础;根据错误恢复处理的处理结果,生成待解析代码文件的语法树,提供了一种生成代码文件的语法树的方法,使得可以通过语法树对编写完成的代码文件进行静态分析,从而准确、高效地对编写完成的代码文件中的语法错误、编写错误等进行检查及纠正,极大节省程序开发人员的时间、精力等。
本申请实施例提供的计算机可读存储介质适用于上述方法的任一实施例。在此不再赘述。
应该理解的是,虽然附图的流程图中的各个步骤按照箭头的指示依次显示,但是这些步骤并不是必然按照箭头指示的顺序依次执行。除非本文中有明确的说明,这些步骤的执行并没有严格的顺序限制,其可以以其他的顺序执行。而且,附图的流程图中的至少一部分步骤可以包括多个子步骤或者多个阶段,这些子步骤或者阶段并不必然是在同一时刻执行完成,而是可以在不同的时刻执行,其执行顺序也不必然是依次进行,而是可以与其他步骤或者其他步骤的子步骤或者阶段的至少一部分轮流或者交替地执行。
以上所述仅是本申请的部分实施方式,应当指出,对于本技术领域的普通技术人员来说,在不脱离本申请原理的前提下,还可以做出若干改进和润饰,这些改进和润饰也应视为本申请的保护范围。
Claims (10)
1.一种生成代码文件的语法树的方法,其特征在于,包括:
在接收到预定编程语言的待解析代码文件时,通过词法解析模块对所述待解析代码文件中的各个词法符号进行解析并生成相应的线性链表;
基于第一查找表和第二查找表,依次对所述线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在所述第一查找表中对应的第一表项为空时,对所述任一词法符号进行错误恢复处理;
根据错误恢复处理的处理结果,生成所述待解析代码文件的语法树。
2.根据权利要求1所述的方法,其特征在于,在对所述任一词法符号进行错误恢复处理之前,还包括:
生成提醒用户所述任一词法符号发生错误的提示信息,并显示所述提示信息。
3.根据权利要求1所述的方法,其特征在于,所述对所述任一词法符号进行错误恢复处理,包括:
确定满足预定恢复条件的至少一组预定组合形式的目标词法符号、目标状态及目标非终结符;
基于任一组预定组合形式的目标词法符号、目标状态及目标非终结符,对所述任一词法符号进行错误恢复处理。
4.根据权利要求3所述的方法,其特征在于,所述预定恢复条件为目标词法符号与第二表项在所述第一查找表中对应的第三表项为非空;
所述第二表项为所述目标状态与所述目标非终结符在所述第二查找表中对应的表项。
5.根据权利要求3或4所述的方法,其特征在于,确定满足预定恢复条件的至少一组预定组合形式的目标词法符号、目标状态及目标非终结符,包括:
将从所述当前状态栈的栈顶状态开始跳过预定个数的状态作为目标状态;
将从所述任一词法符号开始跳过所述预定个数的词法符号作为目标词法符号;
基于所述第二查找表中状态与非终结符之间的对应关系,将所述目标状态对应的非终结符确定为目标非终结符。
6.根据权利要求3-5任一项所述的方法,其特征在于,所述基于任一组预定组合形式的目标词法符号、目标状态及目标非终结符,对所述任一词法符号进行错误恢复处理,包括:
在所述状态栈中,对位于所述目标状态之上的状态进行出栈处理;
在符号栈中,对位于所述任一词法符号之上的词法符号进行出栈处理,且出栈的词法符号的数目与所述状态栈中出栈的状态数目相同;
通过将所述目标非终结符压入所述符号栈,并将第二表项压入所述状态栈,对所述任一词法符号进行错误恢复处理。
7.根据权利要求3所述的方法,其特征在于,还包括:
当存在多组预定组合形式的目标词法符号、目标状态及目标非终结符满足预定恢复条件时,基于第一组满足预定恢复条件的预定组合形式的目标词法符号、目标状态及目标非终结符,对所述任一词法符号进行错误恢复处理;或者,基于多组预定组合形式中最高优先级的预定组合形式的目标词法符号、目标状态及目标非终结符,对所述任一词法符号进行错误恢复处理。
8.一种生成代码文件的语法树的装置,其特征在于,包括:
解析模块,用于在接收到预定编程语言的待解析代码文件时,通过词法解析模块对所述待解析代码文件中的各个词法符号进行解析并生成相应的线性链表;
处理模块,用于基于第一查找表和第二查找表,依次对所述线性链表中的各个词法符号进行分析,并当任一词法符号与当前状态栈的栈顶状态在所述第一查找表中对应的第一表项为空时,对所述任一词法符号进行错误恢复处理;
语法树生成模块,用于根据错误恢复处理的处理结果,生成所述待解析代码文件的语法树。
9.一种电子设备,包括存储器、处理器及存储在存储器上并可在处理器上运行的计算机程序,其特征在于,所述处理器执行所述程序时实现权利要求1-7任一项所述的生成代码文件的语法树的方法。
10.一种计算机可读存储介质,其特征在于,所述计算机可读存储介质上存储有计算机程序,该程序被处理器执行时实现权利要求1-7任一项所述的生成代码文件的语法树的方法。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201811638965.8A CN111381814A (zh) | 2018-12-29 | 2018-12-29 | 生成代码文件的语法树的方法、装置及电子设备 |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201811638965.8A CN111381814A (zh) | 2018-12-29 | 2018-12-29 | 生成代码文件的语法树的方法、装置及电子设备 |
Publications (1)
Publication Number | Publication Date |
---|---|
CN111381814A true CN111381814A (zh) | 2020-07-07 |
Family
ID=71214838
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
CN201811638965.8A Pending CN111381814A (zh) | 2018-12-29 | 2018-12-29 | 生成代码文件的语法树的方法、装置及电子设备 |
Country Status (1)
Country | Link |
---|---|
CN (1) | CN111381814A (zh) |
Cited By (1)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN116909572A (zh) * | 2023-08-04 | 2023-10-20 | 上海安般信息科技有限公司 | 一种基于自定义文法的解析器及推导树代码生成系统 |
Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN106227668A (zh) * | 2016-07-29 | 2016-12-14 | 腾讯科技(深圳)有限公司 | 数据处理方法和装置 |
CN106790108A (zh) * | 2016-12-26 | 2017-05-31 | 东软集团股份有限公司 | 协议数据解析方法、装置和系统 |
-
2018
- 2018-12-29 CN CN201811638965.8A patent/CN111381814A/zh active Pending
Patent Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN106227668A (zh) * | 2016-07-29 | 2016-12-14 | 腾讯科技(深圳)有限公司 | 数据处理方法和装置 |
CN106790108A (zh) * | 2016-12-26 | 2017-05-31 | 东软集团股份有限公司 | 协议数据解析方法、装置和系统 |
Non-Patent Citations (2)
Title |
---|
CALVIN\'S MARBLES: "语法分析实战", pages 1 - 60, Retrieved from the Internet <URL:《https://www.calvinneo.com/2016/07/22/%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90/》> * |
WADE&LUFFY: "Javac 编译原理", pages 1 - 9, Retrieved from the Internet <URL:《https://www.cnblogs.com/wade-luffy/p/5925728.html》> * |
Cited By (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN116909572A (zh) * | 2023-08-04 | 2023-10-20 | 上海安般信息科技有限公司 | 一种基于自定义文法的解析器及推导树代码生成系统 |
CN116909572B (zh) * | 2023-08-04 | 2024-03-12 | 上海安般信息科技有限公司 | 一种基于自定义文法的解析器及推导树代码生成系统 |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
CN112100054B (zh) | 一种面向数据管控的程序静态分析方法和系统 | |
US7958493B2 (en) | Type inference system and method | |
Wirth et al. | Compiler construction | |
CN110502227B (zh) | 代码补全的方法及装置、存储介质、电子设备 | |
US9122540B2 (en) | Transformation of computer programs and eliminating errors | |
JP2007521568A (ja) | 複数の例外処理モデルの中間表現 | |
CN109491658A (zh) | 计算机可执行代码数据的生成方法及装置 | |
US10891117B2 (en) | Method and system for using subroutine graphs for formal language processing | |
CN113031967B (zh) | 一种代码转换方法及装置 | |
CN111381828A (zh) | 生成代码文件的语法树的方法、装置及电子设备 | |
CN110096264A (zh) | 一种代码运行方法及装置 | |
CN108563561B (zh) | 一种程序隐性约束提取方法及系统 | |
Uhl et al. | An attribute grammar for the semantic analysis of Ada | |
CN116166236A (zh) | 代码推荐方法、装置、计算机设备及存储介质 | |
CN111381826A (zh) | 生成代码文件的语法树的方法、装置及电子设备 | |
Benzinger | Automated complexity analysis of Nuprl extracted programs | |
CN111381814A (zh) | 生成代码文件的语法树的方法、装置及电子设备 | |
CN111381827A (zh) | 生成代码文件的语法树的方法、装置及电子设备 | |
Teufel et al. | C2 compiler concepts | |
Grammes et al. | SDL Profiles–Formal Semantics and Tool Support | |
US20240232666A9 (en) | Replacing lambda expressions in a rete network with corresponding code classes | |
US20240202522A1 (en) | Device and method for generating deep learning model graph and abstract syntax tree for integrated compiler | |
Miraldo et al. | An Efficient Algorithm for Type-Directed Structural Diffing | |
CN117472776A (zh) | 一种基于ast的框架自适应c++单元测试用例自动化生成方法及系统 | |
CN117850797A (zh) | 基于Clang的浮点程序中间表示生成方法及系统 |
Legal Events
Date | Code | Title | Description |
---|---|---|---|
PB01 | Publication | ||
PB01 | Publication | ||
SE01 | Entry into force of request for substantive examination | ||
SE01 | Entry into force of request for substantive examination |