zoukankan      html  css  js  c++  java
  • 文法产生式

    语法和语义的区别

    • 语法:描述该语言的程序的正确形式
    • 语义:定义了程序的含义,即每个程序在运行时做什么

    抽象语法树和三地址指令

    三地址指令可以理解为只有3个成分的指令:2个操作数和一个操作符,最多执行一个操作。恰好对应一颗二叉树的2个子节点和其父亲节点。

    抽象语法树如下:

    2.1

    笔记后续更新,可以关注github https://github.com/dslu7733/Compilers

    2.1

    对于抽象语法树的“翻译”,是从叶子节点开始向上翻译的。


    文法产生式

    ->表示推导,可由前者推导出后者,也可以理解为某种“等价替换”

    • 加减运算的文法产生式 (且该文法是左递归的,且仅针对个位数加减)
    //list叫开始符,总是从它开始
    list -> list + digit
    list -> list - digit
    list -> digit
    digit -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 
    

    左递归指,该文法可以一直向左边推导,等价成一颗不断向左子树延申的抽象语法树。

    • 二义性

    同样,对于上面的例子,可能有人会想到下面的文法生成式

    string -> string + string
    string -> string - string
    string -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
    

    这种文法的缺点在于它对应的语法树是二义性的,即存在2颗不同的语法树对应着同一句话,在某些情况下,这是含糊的。

    2.1

    2.2.png

    但是细心可以发现这2颗树还是有所不同的,前一颗是左递归,后一颗是右递归。可以人为的赋予一层解释给它们,子树的运算级别更高,即添加了一层“隐性的括号”。

    于是对于前一颗语法树,它将被翻译为 (9-5)+2 = 6
    后一颗语法树被翻译为 9-(5+2) = 2

    • 左结合与右结合

    看了上面对于左递归和右递归的解释,人们会想第一个例子能不能写成右递归呢?答案是可以的

    list -> digit suffix | digit suffix
    suffix -> - digit suffix | + digit suffix | epsilon
    digit -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
        
    //epsilon表示空字符串, ’|‘表示‘或’
    

    那它们有什么区别吗?就像上面那个string的例子,解释不同,对于右递归的文法,是右结合的。

    例如加减运算是左结合的,所以3+2-1=(3+2)-1; 而等号是右结合的,所以a=b=2等同于a=(b=2)

    • 函数调用中参数列表的文法产生式
    call -> id (optparams)
    optparams -> epsilon | params
    params -> params , param | param
    

    上面的参数列表文法产生式中用id表示标识符,epsilon表示空字符串,param表示形参。
    这个文法产生式某种意义上来说是”不完整“的, 因为它没有将id与params推导到字符级别,但有时候为了方便理解,就这么写了。

    除此之外,文法产生式是很严谨的,连参数之间的逗号和函数调用的括号都要考虑在内,因为param仅代表一个字符串,不包含逗号在内。

    • 运算符的优先级

    到现在可以看到文法生成式算得上是一种严谨的”数学推导“了,而且它还能表示运算符号的优先级。

    考虑
    +-:左结合
    */:左结合
    优先级 */ > +-

    expr -> expr - term | expr + term | term
    term -> term * factor | term / factor | factor
    factor -> digit | (expr)
    

    语法树的翻译是从叶子节点开始的,因此可以区分出优先级,子树的运算优先级比其父节点高。

    factor是最小的运算单位,表示不能被其它任何运算符分开的表达式。

    上面这个文法可以实现*/优先级比+-高,但是他也能表示2 *(1+3)这类语句。

    • 后缀表达式

    上面的文法都是中缀形式,后缀可以写成E -> E E op | digit


    C语句的子集的文法

    说了这么多,来看一个涉及C语言部分语句的文法

    stmt -> id = expression;
    	  | if(expression) stmt
          | if(expression) stmt else stmt
          | do stmt while (expression);
          | while(expression) stmt
          | {stmts}
    stmts -> stmts stmt | epsilon
    

    上面用id表示标识符,expression表示实际程序语句。注意这个文法中的分号的使用,非常严谨,不多不少。

  • 相关阅读:
    [DB] 数据库的连接
    JS leetcode 翻转字符串里的单词 题解分析
    JS leetcode 拥有最多糖果的孩子 题解分析,六一快乐。
    JS leetcode 搜索插入位置 题解分析
    JS leetcode 杨辉三角Ⅱ 题解分析
    JS leetcode 寻找数组的中心索引 题解分析
    JS leetcode 移除元素 题解分析
    JS leetcode 最大连续1的个数 题解分析
    JS leetcode 两数之和 II
    JS leetcode 反转字符串 题解分析
  • 原文地址:https://www.cnblogs.com/friedCoder/p/12762089.html
Copyright © 2011-2022 走看看