C++中switch语句的BNF 否极泰来 ——《周易》
先定义后使用 BNF(巴科斯-瑙耳范式)是一种用于表示上下文无关文法(2型文法)的语言 最早用于描述ALGOL 60 编程语言 文法(形式文法)是描述一种语言的方法。 语言是一个包含所有合法字符串的集合。 语言 = 语法(文法) + 语义。 2
文法 文法是四元组 G=(V,Σ, R, S) Σ 是终结符集。又称为字母表。 V是非终结符(中间变量)集。 非终结符可以根据产生式“展开”。 终结符不能再分割。(但可以转化) R是产生式(即转化规则)。 S是起始变量。
产生式 通俗地,长这样 X → Y BNF中这么表示 X ::= Y X和Y是字符串。只能包含字母表中的字符(终结符)和非终结符。 任何一个合法句子,都能从S开始,按一定顺序应用某些产生式得到。 请注意:某一语言的合法字符串只包含终结符。含非终结符的都是中间量。
烤鸭菜谱文法 非终结符V = {普通法,酒渍法} 终结符Σ = {杀,洗,盐腌,酒渍,烤} 产生式R: S→杀、洗、普通法 普通法→盐腌、烤 酒渍法→盐腌、酒渍、烤 S在这里相当于“烤鸭方法” 5
关于非终结符 一般放在引号里 “for” “(“ stat “;” cond “;” stat “)” stat PPT里对眼睛不好。
BNF中扩充了一些符号 [ ] 可选项。 { } 可重复0至无数次的项。 | 任选其一,相当于"OR" ::= 定义,相当于“→” [ ] 可选项。 { } 可重复0至无数次的项。 | 任选其一,相当于"OR" ::= 定义,相当于“→” () 表示分组、改变优先级等 可以证明,前三个符号能通过添加非终结符、添加相关的产生式来得到。 是否使用扩充符号,文法表达能力不变。
我知道你们要念《西江月·证明》 S ::= 炭烤鸭 | 啤酒鸭 | …… S→炭烤鸭 S→啤酒鸭 …… S ::= 烤鸭[真]好吃 S→烤鸭好吃 S→烤鸭X好吃 X→真X X→真
BNF懂了,查文献交差喽 GNU有C++的规范BNF http://www.nongnu.org/hcb/ switch_block ::= switch ( condition ) statement 蛤??? It’s too simple. Somewhat naive.
不着急 switch_block ::= switch ( condition ) statement 剖析一下两个非终结符
condition Condition的具体定义很复杂。(一大堆产生式、非终结符)。简而言之: 符合C++语法的,任何返回值为整形(或char/bool)的东西都是condition,例子如下: 1==2 3==true ‘x’ a=1 a=1,b=2 ++(++x) 11
statement statement ::= labeled-statement| 带标签语句 expression-statement| compound-statement| selection-statement| iteration-statement| jump-statement| declaration-statement| try-block
labeled-statement labeled-statement ::= (identifier : statement ) | (case constant-expression : statement) | (default : statement) 另外,compound_statement里面有大括号 *constant-expression是常量表达式
另外一件重要的事情 statement → compound_statement → { statement_seq } 再利用这两条规则 statement_seq ::= statement (请类比) X ::= 真 statement_seq ::= statement_seq statement (请类比) X ::= X 真 可以递归生成多个statement且确保在大括号内
否极泰来 GNU是著名的基建大师 对condition和statement进行良好定义后 switch的BNF就是如此简单 某微积分老师:GNU开创非终结符condition和statement之详尽定义,C++文法由此严密。 千古绝学!真神技也!
再来一盘烤鸭 用文法生成下列语句: switch(1){ case 1: a = 1; break; case 2: a = 2; default: a = -1; } switch_block → switch(condition) statement → switch( 1 ) compound_ { statement_seq }
再来一盘烤鸭-2 → switch( 1 ) { statement statement statement { labeled-statement jump-statement labeled-statement }
再来一盘烤鸭-3 → switch( 1 ) { case const-expression : statement jump-statement case const-expression : statement default : statement }
再来一盘烤鸭-4 → switch( 1 ) { case 1 : a = 1; break; case 2 : a = 2; default : a = -1; }
从而可知 这是你眼中的case语句块 case 1 : a = 1; 这是编译器眼中的 case 1 : a = 1;
请对比 goto语句标号 3: if (x>1) x = 1; case语句块 case 1: a = 1; 再看看定义 labeled-statement ::= (identifier : statement ) | (case constant-expression : statement) | (default : statement)
编译器的眼中 goto的标号 == case case的本质是标号 编译器面前一律平等
还有个重要情况没给你透露 switch_block 这个非终结符根本不存在 这是我自己命名的 原文: selection-statement ::= ( if ( condition ) statement ) | ( if ( condition ) statement else statement ) | ( switch ( condition ) statement ) switch君:原来我和if是平等的。。蛤??
其实一开始让我讲,我是拒绝的 三张PPT就能讲完了,有什么好讲的呢? switch的BNF,不就长这副样子吗? switch_block ::= switch( condition ) { { case : constant-expression statement } [ default : statement ] } 然而,其实长这样?还与if是平等的? switch_block ::= switch( condition ) statement
编译结果:0错误,0警告
否极泰来^2 0错误,0警告,里面代码其实不会运行 switch(x)的本质是goto,goto的目标行具有case value(x)标签 这就完美解释了为什么“未break就继续运行”并且“无视其他case标签” 把case看成goto标签,一切都解决了。 这不过是“顺序结构”的体现 至于上例,没标签当然不会goto,直接跳过。
大师手笔,目的何在? 请各位回忆PPT第二张 语言 = 语法 + 语义 如果提前限定switch里面有什么 这就是“语法对语义的入侵” 语义的定义又严格依赖于语法 二者分工相当重要 Switch如此简单的BNF,是语法和语义的相互妥协
总结:大师的抉择 道法自然 ——《道德经》 否极泰来 ——《周易》
Fin. Great Appreciations