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表示实际程序语句。注意这个文法中的分号的使用,非常严谨,不多不少。

  • 相关阅读:
    win7下利用VM8安装CentOS6.3配置静态IP上网
    laravel的门面模式
    Redis主从在线互相切换
    phpcmsv9的评论分表策略
    phpcms v9的url优化
    php rsa加密解密实例
    centos 支持中文,安装输入法
    yii2 页面上编写js代码,并注册到页面底部
    yii2 GridView 简要解析
    yii2 migration使用
  • 原文地址:https://www.cnblogs.com/friedCoder/p/12762089.html
Copyright © 2011-2022 走看看