[编译原理] C语言语法规则 (C89)

C89文法

警告: 本文自拟译名.

0 通用词法

0.1 基本字符

数字                 ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
非0数字              ::=       '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
八进制数字           ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7'
十六进制数字         ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' |
                         'a' | 'b' | 'c' | 'd' | 'e' | 'f' |
                         'A' | 'B' | 'C' | 'D' | 'E' | 'F'

字母                 ::= '_' | 'a' | ... | 'z' | 'A' | ... | 'Z'
正负                 ::= '+' | '-'

常用转义组           ::= '\\' '\'' | '\\' '"' | '\\' '?' | '\\' '\\' |
                         '\\' 'a' | '\\' 'b' | '\\' 'f' | '\\' 'n' | '\\' 'r' | '\\' 't' | '\\' 'v'
八进制转义组         ::= '\\' 八进制数字 |
                         '\\' 八进制数字 八进制数字 |
                         '\\' 八进制数字 八进制数字 八进制数字
十六进制转义组       ::= '\\' 'x' 十六进制数字 |
                         十六进制转义组 十六进制数字
转义组               ::= 常用转义组 |
                         八进制转义组 |
                         十六进制转义组

字符值字符           ::= 除 '\'' '\\' 换行 外的任意字符 |
                         转义组
字符值字符组         ::= 字符值字符 {字符值字符}
字符值               ::=     '\'' 字符值字符组 '\'' |
                         'L' '\'' 字符值字符组 '\''

字符串字符           ::= 除 '\'' '\\' 换行 外的任意字符 |
                         转义组
字符串字符组         ::= 字符串字符 {字符串字符}
字符串               ::= '"' 字符串字符组 '"' |
                         'L' '"' 字符串字符组 '"'

注1: 非0数字 仅用于编译阶段.

0.2 单词

单词                 ::= 字母 |
                         单词 字母 |
                         单词 数字

1 预处理阶段

预处理阶段从 开始. 原名称为 preprocessing-file.

1.1 词法分析

换行                 ::= \r | \n | \r\n | \n\r

标准头文件字符       ::= 除 '>' 换行 外的任意字符
标准头文件字符组     ::= 标准头文件字符 {标准头文件字符}
自定义头文件字符     ::= 除 '"' 换行 外的任意字符
自定义头文件字符组   ::= 自定义头文件字符 {自定义头文件字符}
预处理头文件         ::= '<' 标准头文件字符组 '>' |
                         '"' 自定义头文件字符组 '"'

预处理可识别数       ::= 数字 |
                         '.' 数字 |
                         预处理可识别数 数字 |
                         预处理可识别数 字母 |
                         预处理可识别数 'e' 正负 |
                         预处理可识别数 'E' 正负 |
                         预处理可识别数 '.'

预处理可识别算符     ::= '[' | ']' | '(' | ')' | '.' | '-' '>' |
                         '+' '+' | '-' '-' | '&' | '*' | '+' | '-' | '!' | 's' 'i' 'z' 'e' 'o' 'f' |
                         '/' | '%' | '<' '<' | '>' '>' | '<' |' >' | '<' '=' | '>' '=' | '!' '=' |
                         '^' | '|' | '&' '&' | '|' '|' |
                         '?' | ':' |
                         '=' | '*' '=' | '/' '=' | '%' '=' | '+' '=' | '-' '=' | '<' '<' '=' |
                         '>' '>' '=' | '&' '=' | '^' '=' | '|' '=' |
                         ',' | '#' | '#' '#'

预处理可识别标点符号 ::= '[' | ']' | '(' | ')' | '{' | '}' | '*' | ',' |
                         ':' | '=' | ';' | '…' | '#'

预处理可识别词       ::= 单词 |
                         字符值 |
                         字符串 |
                         预处理头文件 |
                         预处理可识别数 |
                         预处理可识别算符 |
                         预处理可识别标点符号
                         除上述的非空白字符

1.2 语法分析

预处理可识别词组     ::= 预处理可识别词 {预处理可识别词}
替换词组             ::= [预处理可识别词组]
贴左括号             ::= 无前导空格的"("
一般指令             ::= "#" "include" 预处理可识别词组 换行 |
                         "#" "define" 单词 替换词组 换行 |
                         "#" "define" 单词 贴左括号 {单词} ")" 替换词组 换行 |
                         "#" "undef" 单词 换行 |
                         "#" "line" 预处理可识别词组 换行 |
                         "#" "error" [预处理可识别词组] 换行 |
                         "#" "pragma" [预处理可识别词组] 换行 |
                         "#" 换行

if节                 ::= "#" "if" 常数式 换行 {行}
                         "#" "ifdef" 单词 换行 {行}
                         "#" "ifndef" 单词 换行 {行}
elif节               ::= "#" "elif" 常数式 换行 {行}
else节               ::= "#" "else" 换行 {行}
条件指令结束         ::= "#" "endif" 换行
条件指令             ::= if节 {elif节} [else节] 条件指令结束

行                   ::= [预处理可识别词组] 换行 |
                         条件指令 |
                         一般指令
文                   ::= {行}

注1: 常数式 详见编译阶段.
注2: 并非实际按 换行 分隔的文本行, 只是一个非终结符, 原名称为 group-part.

2 编译阶段

编译阶段从 开始. 原名称为 translation-unit.

2.1 词法分析

2.1.1 常数
数字组               ::= 数字 {数字}
小数                 ::= [数字组] '.' 数字组 |
                         数字组 '.'
指数部               ::= 'e' [正负] 数字组 |
                         'E' [正负] 数字组
实数后缀             ::= 'f' | 'l' | 'F' | 'L'
实数                 ::= 小数 [指数部] [实数后缀] |
                         数字组 指数部 [实数后缀]

十进制整数           ::= 非0数字 |
                         十进制整数 数字
八进制整数           ::= '0' |
                         八进制整数 八进制数字
十六进制整数         ::= '0' 'x' 十六进制数字 |
                         '0' 'X' 十六进制数字 |
                         十六进制整数 十六进制数字
unsigned后缀         ::= 'u' | 'U'
long后缀             ::= 'l' | 'L'
整数后缀             ::= unsigned后缀 [long后缀] |
                         long后缀 [unsigned后缀]
整数                 ::= 十进制整数 [整数后缀] |
                         八进制整数 [整数后缀] |
                         十六进制整数 [整数后缀]

枚举词               ::= 单词

常数                 ::= 实数 | 整数 | 字符值 | 枚举词
常数式               ::= 第2式

注1: 字符值 见通用词法.
注2: 字符串 事实上也是常量, 但不是常数. 常数 要求能计算出数值并用于数组, 枚举值定义等处.
注3: 第2式 见式.
注4: 枚举词枚举体 见声明.

2.2 语法分析

2.2.1 式
第14算符             ::= "&" | "*" | "+" | "-" | "~" | "!"
第1算符              ::= "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|="
类型名               ::= 类型与优化标识组 [无名声明]

第16式               ::= 单词 |
                         常数 |
                         字符串 |
                         "(" 式 ")"
第15式               ::= 第16式 |
                         第15式 "[" 式 "]" |
                         第15式 "(" [参数式组] ")" |
                         第15式 "." 单词 |
                         第15式 "->" 单词 |
                         第15式 "++" |
                         第15式 "--"
第14式               ::= 第15式 |
                         "++" 第14式 |
                         "--" 第14式 |
                         第14算符 第13式 |
                         "sizeof" 第14式 |
                         "sizeof" "(" 类型名 ")"
第13式               ::= 第14式 |
                         "(" 类型名 ")" 第13式
第12式               ::= 第13式 |
                         第12式 "*" 第13式 |
                         第12式 "/" 第13式 |
                         第12式 "%" 第13式
第11式               ::= 第12式 |
                         第11式 "+" 第12式 |
                         第11式 "-" 第12式
第10式               ::= 第11式 |
                         第10式 "<<" 第11式 |
                         第10式 ">>" 第11式
第9式                ::= 第10式 |
                         第9式 "<" 第10式 |
                         第9式 ">" 第10式 |
                         第9式 "<=" 第10式 |
                         第9式 ">=" 第10式
第8式                ::= 第9式 |
                         第8式 "==" 第9式 |
                         第8式 "!=" 第9式
第7式                ::= 第8式 |
                         第7式 "&" 第8式
第6式                ::= 第7式 |
                         第6式 "^" 第7式
第5式                ::= 第6式 |
                         第5式 "|" 第6式
第4式                ::= 第5式 |
                         第4式 "&&" 第5式
第3式                ::= 第4式 |
                         第3式 "||" 第4式
第2式                ::= 第3式 |
                         第3式 "?" 式 ":" 第2式
第1式                ::= 第2式 |
                         第14式 第1算符 第1式
式                   ::= 第1式 {',' 第1式}

参数式组             ::= 第1式 {',' 第1式}
初值                 ::= 第1式 |
                         "{" 初值 ["," 初值] [","] "}"

注1: 类型与优化标识组无名声明 见声明.
注2: 中各第n式原名称为 assignment-expression, conditional-expression, logical-OR-expression 等等. 考虑到实际上多会使用左右优先级方式解析语法而且分析语法时更多考虑其运算符的优先级, 因而改用数字命名规则.

2.2.2 声明
存储与别名标识       ::= "typedef" | "extern" | "static" | "auto" | "register"
优化标识             ::= "const" | "volatile"
类型                 ::= "void" | "char" | "short" | "int" | "long" |
                         "float" | "double" | "signed" | "unsigned" |
                         结构体与共用体 |
                         枚举体 |
                         自定义类型
自定义类型           ::= 单词
标识组               ::= 存储与别名标识 [标识组] |
                         优化标识 [标识组]
                         类型 [标识组] |

struct_union         ::= "struct" | "union"
结构体变量定义       ::= 有名声明 |
                         [有名声明] ":" 常数式
类型与优化标识组     ::= 类型 [类型与优化标识组] |
                         优化标识 [类型与优化标识组]
结构体内声明         ::= 类型与优化标识组 结构体变量定义 {"," 结构体变量定义} ";"
结构体与共用体       ::= struct_union [单词] "{" {结构体内声明} "}" |
                         struct_union 单词

枚举词声明           ::= 枚举词 ["=" 常数式]
枚举词组             ::= 枚举词声明 {"," 枚举词声明}
枚举体               ::= "enum" [单词] "{" 枚举词组 "}" |
                         "enum" 单词

指针                 ::= "*" {优化标识} [指针]

有名子声明           ::= 单词 |
                         "(" 有名声明 ")" |
                         有名子声明 "[" [常数式] "]" |
                         有名子声明 "(" [形参组及变参] ")" |
                         有名子声明 "(" [单词 {"," 单词}] ")"
有名声明             ::= [指针] 有名子声明

无名子声明           ::= "(" 无名声明 ")" |
                         [无名子声明] "[" [常数式] "]"
                         [无名子声明] "(" [形参组及变参] ")"
无名声明             ::= 指针 |
                         [指针] 无名子声明

形参组               ::= 形参 {"," 形参}
形参组及变参         ::= 形参组 ["," "..."]
形参                 ::= 标识组 有名声明 |
                         标识组 [无名声明]

变量定义并初始化     ::= 有名声明 ["=" 初值]
声明                 ::= 标识组 [变量定义并初始化 {"," 变量定义并初始化}] ";"

注1: C语言常常为人诟病的一点是它的类型声明和变量定义过于复杂.
注2: 有名声明 有名子声明 无名声明 无名子声明 原名称为 declarator direct-declarator abstract-declarator direct-abstract-declarator.
注3: 关于规则 有名子声明 ::= 有名子声明 "(" [单词 {"," 单词}] ")", 见总定义注2.

2.2.3 句
段                   ::= "{" {声明} {句} "}"
式句                 ::= [式] ";"
标记句               ::= 单词 ":" 句 |
                         "case" 常数式 ":" 句 |
                         "default" ":" 句
分支句               ::= "if" "(" 式 ")" 句 |
                         "if" "(" 式 ")" 句 "else" 句 |
                         "switch" "(" 式 ")" 句
循环句               ::= "while" "(" 式 ")" 句
                         "do" 句 "while" "(" 式 ")" ";"
                         "for" "(" [式] ";" [式] ";" [式] ")" 句
跳转句               ::= "goto" 单词 ";" |
                         "continue" ";" |
                         "break" ";" |
                         "return" [式] ";"

句                   ::= 段 |
                         式句 |
                         标记句 |
                         分支句 |
                         循环句 |
                         跳转句
2.2.4 总定义
函数定义             ::= [标识组] 有名声明 {声明} 段
荒声明               ::= 函数定义 | 声明
文                   ::= 荒声明 {荒声明}

注1: 荒声明 原名称为 external-declaration(外部声明). “荒” 取其在野之意, 比喻不在任何函数内部的代码.
注2: 函数定义中 {声明} 为旧语法, 与 有名子声明 ::= 有名子声明 "(" [单词 {"," 单词}] ")" 配合使用. 例见后记例2.

3 后记

3.1 与C89的区别

截取自 GB/T 15272-94. 修复了部分错误(错误较多, 此处不一一指出). 同时修改了部分定义, 如下:

  1. 按个人喜好和理解更换了译名.
  2. 将规则按实际所服务的阶段, 按两大类 预编译阶段编译阶段 做区分.
  3. 同时为了方便理解, 调整了规则顺序, 并添加了逻辑分隔的空行.
  4. 简并部分规则.
  5. 换行 添加了定义.
  6. 参考部分 BNF范式 重新书写词法和语法规则.

3.2 BNF范式

本文采用的语法简述如下:

  1. [ ] 表示可选, 即出现0次或1次.
  2. {} 表示任意次, 即出现0次或任意多次.
  3. | 表示或者.
  4. ' ' 表示紧凑的字符. 同理, 词法分析中的符号均为紧凑的.
  5. " " 表示两侧可有空白字符间隔的字符串. 同理, 若无说明则语法分析中符号间可有任意空白字符.

3.3 一些例子

3.3.1 例1 函数
int add(int a, int b) {
  return a + b;
}

语法树如下:
在这里插入图片描述

3.3.2 例2 旧语法
int main(argc, argv)
int argc;
char **argv;
{
  return 0;
}

语法树如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值