这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
4. 语法分析
词法分析是一个词接一个词的识别,语法分析是在词法分析的基础上识别节目的语法结构。这个结构是树形结构。这个树被称为抽象语法树(AST)。树的每个节点(子树)都是一个语法单元,这个单元的规则称为“语法”。每个节点也可以有下级节点。
上下文无关语法 Context-Free Grammar
不用考虑上下文,就可以判断语言句子是否正确
...
a = 0;
...
这是一个赋值语句,无论此语句的前后是什么代码,此语句所代表的操作是确定的。即给变量a赋值等于0
复制代码
编程语言为什么不用自然语言,而是用上下文无关的文法呢? 因为
- 易于设计编译器。客观上,目前技术还无法实现。如果使用上下文依赖语法,将是真正的人工智能,将是NLP领域的重大突破。
- 促进代码开发和维护。如果说开发出来的代码就像高考的中文阅读理解,每个人都有不同的理解,那么,作者真正想表达的是哪一个呢?如果人类不能确定意义,计算机也不能确定意义,最终的结果是执行错误或未执行。
- 汇编/机器语言与上下文无关。当CPU执行一条指令时,它会读取并执行该指令。如果CPU需要考虑上下文来确定语句要做什么,那么CPU执行语句的速度将比现在慢数千倍。考虑到上下文的事情,完全可以在用户编程时使用算法来实现。由于机器语言与上下文无关,因此高级语言基本上与上下文无关,并且可能有一些单独的语法被设计为与上下文无关,以方便使用,例如脚本语言的弱类型。它易于使用,但也增加了解析器的复杂性。
上下文无关语法G:终结符集合T + 非终结符集合N + 产生式集合P + 起始符号S
G由T、N、S和P组成,由语法G推导出来的所有句子的集合称为G语言!
终结符: 组成串的基本符号。可以理解为词法分析器产生的token集合。比如 + Id ( ) 等
非终结符: 表示token的的集合的语法变量。比如 stmt varDecl 等等
start:blockStmts ; //起始
block : '{' blockStmts '}' ; //语句块
blockStmts : stmt* ; //语句块中的语句
stmt = varDecl | expStmt | returnStmt | block; //语句
varDecl : type Id varInitializer? ';' ; //变量声明
type : Int | Long ; //类型
varInitializer : '=' exp ; //变量初始化
expStmt : exp ';' ; //表达式语句
returnStmt : Return exp ';' ; //return语句
exp : add ; //表达式
add : add '+' mul | mul; //加法表达式
mul : mul '*' pri | pri; //乘法表达式
pri : IntLiteral | Id | '(' exp ')' ; //基础表达式
复制代码
产生式:表示形式,S : AB ,就是说S的含义可以用语法AB进行表达
S : AB
A : aA | ε
B : b | bB
复制代码
展开(expand):将P(A->u )应用到符号串vAw中,得到新串vu **w
折叠(reduce):将P(A->uu )应用到符号串vuu w中,得到新串vAw
推导(derivate):符号串u 应用一系列产生式,变成符号串v ,则u =>v:S => ab | b | bb
巴科斯范式
BNF是描述上下文无关理论的一种具体方法。通过BNF可以实现上下文无关语法的具体化、公式化和科学化,是代码解析的必要条件。
<expr> ::= <expr> + <term>
| <expr> - <term>
| <term>
<term> ::= <term> * <factor>
| <term> / <factor>
| <factor>
<factor> ::= ( <expr> )
| Num
复制代码
BNF本质上就是树形分解,分解成一棵抽象语法树
-
每个产生式就是一个子树,在写编译器时,每个子树对应一个解析函数。
-
叶子节点叫做 终结符,非叶子节点叫做 非终结符。