|
| 1 | +# 编译原理 |
| 2 | +> 本文主要介绍编译的几个主要过程及周边工具的使用, 对于工具内部具体实现的算法不做分析, 感兴趣的可自行搜索 |
| 3 | +
|
| 4 | +## 词法分析 |
| 5 | + |
| 6 | +```mermaid |
| 7 | +sequenceDiagram |
| 8 | + Source code->>Token stream: Lexical analysis |
| 9 | + Note left of Source code: 1 + (2 - 3) * 4 / 5 |
| 10 | + Note left of Source code: SELECT * FROM TABLE1 LIMIT 1; |
| 11 | + Note left of Source code: {"key1": 1, "key2": "val", "key3": {}} |
| 12 | + #------ |
| 13 | + Note right of Token stream: 1<br/>+<br/>(<br/>2<br/>-<br/>3<br/>)<br/>*<br/>4<br/>/<br/>5 |
| 14 | + Note right of Token stream: SELECT<br/>*<br/>FROM<br/>TABLE1<br/>LIMIT<br/>1<br/>; |
| 15 | + Note right of Token stream: {<br/>"key1"<br/>:<br/>1<br/>,<br/>"key2"<br/>:<br/>"val"<br/>,<br/>"key3"<br/>:<br/>{<br/>,<br/>}<br/>,<br/>}<br/> |
| 16 | +``` |
| 17 | + |
| 18 | +第一步将源代码处理成为`token stream`, 这边的源代码可以是一段简单的`go`代码, `DML`, `DSL`, 甚至是`JSON`格式的文件或者其他文本内容等等, `Lexical analysis`的目的就是按照某个定义规则将文本处理成为一连串的`token stream` |
| 19 | +> 标记 / Token: 指处理好后的一个字串, 是构成源代码的最小单位, 比如我们可以归类 golang 中的关键字, 例如 var、const、import 等等, 或者一个字符串变量 "str" 或者操作符 :=、>=、== 等等,只要是符合我们定义的语法规则处理后出现的字串, 都可以称为一个 token |
| 20 | +
|
| 21 | +如上图, 左边框内的三条源代码案例, 经过词法分析后, 可能会(具体看自己对`token`的定义处理规则)输出右边的三块`token stream`(每一行代表一个`token`) |
| 22 | + |
| 23 | +### lex / flex |
| 24 | +lex / flex 是常用的词法分析器,支持正则表示某类 token |
| 25 | + |
| 26 | +flex 文件完整格式: |
| 27 | +```c |
| 28 | +%{ |
| 29 | +Declarations |
| 30 | +%} |
| 31 | +Definitions |
| 32 | +%% |
| 33 | +Rules |
| 34 | +%% |
| 35 | +User subroutines |
| 36 | +``` |
| 37 | +
|
| 38 | +例: |
| 39 | +test.l |
| 40 | +```c |
| 41 | +
|
| 42 | +/* Declarations */ |
| 43 | +%{ |
| 44 | +void yyerror(const char *msg); |
| 45 | +%} |
| 46 | +
|
| 47 | +
|
| 48 | +/* Definitions */ |
| 49 | +WHITESPACE ([ \t\r\a]+) |
| 50 | +OPERATOR ([+*-/%=,;!<>(){}]) |
| 51 | +INTEGER ([0-9]+) |
| 52 | +
|
| 53 | +
|
| 54 | +/* Rules */ |
| 55 | +%% |
| 56 | +
|
| 57 | +{WHITESPACE} { /* void */ } |
| 58 | +
|
| 59 | +{OPERATOR} { printf("%s\n", yytext); } |
| 60 | +
|
| 61 | +{INTEGER} { printf("%d\n", atoi(yytext)); } |
| 62 | +
|
| 63 | +\n { /* void */ } |
| 64 | +
|
| 65 | +. { printf("analysis error: unknow [%s]\n", yytext); exit(1); } |
| 66 | +
|
| 67 | +%% |
| 68 | + |
| 69 | +/* User subroutines */ |
| 70 | +int main(int argc, char* argv[]) { |
| 71 | + FILE *fp = NULL; |
| 72 | + if (argc == 2) { |
| 73 | + fp = fopen(argv[1], "r"); |
| 74 | + if (fp) { |
| 75 | + yyin = fp; |
| 76 | + } |
| 77 | + } |
| 78 | + yylex(); |
| 79 | + if (fp) { |
| 80 | + fclose(fp); |
| 81 | + } |
| 82 | + return 0; |
| 83 | +} |
| 84 | +
|
| 85 | +int yywrap(void) { |
| 86 | + return 1; |
| 87 | +} |
| 88 | +
|
| 89 | +void yyerror(const char *msg) { |
| 90 | + fprintf(stderr, "Error :\n\t%s\n", msg); |
| 91 | + exit(-1); |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | + |
| 96 | +以上小段词法分析代码定义了三种`token`:`WHITESPACE`, `OPERATOR`, `INTEGER`, 分别用正则定义了他们的规则, 而后在 `Rules` 规则阶段分别对这三种 `token` 进行了各自的处理 |
| 97 | +```shell |
| 98 | +# 编译 |
| 99 | +flex -o test.c test.l |
| 100 | +gcc -std=c89 -o flextest test.c |
| 101 | +./test.c test.txt |
| 102 | +``` |
| 103 | +而后用根据我们定义的规则生成的词法分析器`flextest`来处理一个简单的案例 |
| 104 | + |
| 105 | +```shell |
| 106 | +cat test.txt |
| 107 | +1 + (2 - 3) * 4 / 5 sss |
| 108 | + |
| 109 | +./flextest ./test.txt |
| 110 | +1 |
| 111 | ++ |
| 112 | +( |
| 113 | +2 |
| 114 | +- |
| 115 | +3 |
| 116 | +) |
| 117 | +* |
| 118 | +4 |
| 119 | +/ |
| 120 | +5 |
| 121 | +analysis error: unknow [s] |
| 122 | +``` |
| 123 | +根据输出的`token stream`可以看到, 能通过`token`规则处理的字串会完成输出一个成功处理的`token`, 规则之外的则处理失败 |
| 124 | + |
| 125 | +经过以上的小案例, 那么如果让我们自己来做一个`golang`的词法分析 `token` 的定义, 难度就不会特别大了 |
| 126 | + |
| 127 | +这边可以来简单看下`golang`编译器源码内的`token`定义 |
| 128 | + |
| 129 | +```go |
| 130 | +// src/go/token/token.go |
| 131 | +var tokens = [...]string{ |
| 132 | + ILLEGAL: "ILLEGAL", |
| 133 | + |
| 134 | + EOF: "EOF", |
| 135 | + COMMENT: "COMMENT", |
| 136 | + |
| 137 | + IDENT: "IDENT", |
| 138 | + INT: "INT", |
| 139 | + FLOAT: "FLOAT", |
| 140 | + IMAG: "IMAG", |
| 141 | + CHAR: "CHAR", |
| 142 | + STRING: "STRING", |
| 143 | + |
| 144 | + ADD: "+", |
| 145 | + SUB: "-", |
| 146 | + MUL: "*", |
| 147 | + QUO: "/", |
| 148 | + REM: "%", |
| 149 | + |
| 150 | + AND: "&", |
| 151 | + OR: "|", |
| 152 | + XOR: "^", |
| 153 | + SHL: "<<", |
| 154 | + SHR: ">>", |
| 155 | + AND_NOT: "&^", |
| 156 | + |
| 157 | + ADD_ASSIGN: "+=", |
| 158 | + SUB_ASSIGN: "-=", |
| 159 | + MUL_ASSIGN: "*=", |
| 160 | + QUO_ASSIGN: "/=", |
| 161 | + REM_ASSIGN: "%=", |
| 162 | + |
| 163 | + AND_ASSIGN: "&=", |
| 164 | + OR_ASSIGN: "|=", |
| 165 | + XOR_ASSIGN: "^=", |
| 166 | + SHL_ASSIGN: "<<=", |
| 167 | + SHR_ASSIGN: ">>=", |
| 168 | + AND_NOT_ASSIGN: "&^=", |
| 169 | + |
| 170 | + LAND: "&&", |
| 171 | + LOR: "||", |
| 172 | + ARROW: "<-", |
| 173 | + INC: "++", |
| 174 | + DEC: "--", |
| 175 | + |
| 176 | + EQL: "==", |
| 177 | + LSS: "<", |
| 178 | + GTR: ">", |
| 179 | + ASSIGN: "=", |
| 180 | + NOT: "!", |
| 181 | + |
| 182 | + NEQ: "!=", |
| 183 | + LEQ: "<=", |
| 184 | + GEQ: ">=", |
| 185 | + DEFINE: ":=", |
| 186 | + ELLIPSIS: "...", |
| 187 | + |
| 188 | + LPAREN: "(", |
| 189 | + LBRACK: "[", |
| 190 | + LBRACE: "{", |
| 191 | + COMMA: ",", |
| 192 | + PERIOD: ".", |
| 193 | + |
| 194 | + RPAREN: ")", |
| 195 | + RBRACK: "]", |
| 196 | + RBRACE: "}", |
| 197 | + SEMICOLON: ";", |
| 198 | + COLON: ":", |
| 199 | + |
| 200 | + BREAK: "break", |
| 201 | + CASE: "case", |
| 202 | + CHAN: "chan", |
| 203 | + CONST: "const", |
| 204 | + CONTINUE: "continue", |
| 205 | + |
| 206 | + DEFAULT: "default", |
| 207 | + DEFER: "defer", |
| 208 | + ELSE: "else", |
| 209 | + FALLTHROUGH: "fallthrough", |
| 210 | + FOR: "for", |
| 211 | + |
| 212 | + FUNC: "func", |
| 213 | + GO: "go", |
| 214 | + GOTO: "goto", |
| 215 | + IF: "if", |
| 216 | + IMPORT: "import", |
| 217 | + |
| 218 | + INTERFACE: "interface", |
| 219 | + MAP: "map", |
| 220 | + PACKAGE: "package", |
| 221 | + RANGE: "range", |
| 222 | + RETURN: "return", |
| 223 | + |
| 224 | + SELECT: "select", |
| 225 | + STRUCT: "struct", |
| 226 | + SWITCH: "switch", |
| 227 | + TYPE: "type", |
| 228 | + VAR: "var", |
| 229 | +} |
| 230 | +``` |
| 231 | + |
| 232 | +## 语法分析 |
| 233 | +根据第一步[词法分析](#词法分析)我们目前已经获取到了自源代码处理好之后的一个`token stream`, 在语法分析阶段主要负责的就是把这一串「看似毫无规则」的标记流进行语法结构上的处理 |
| 234 | +例如 |
| 235 | +1.判断某个赋值操作是否可以执行, 赋值号两边的变量及数据类型是否匹配 |
| 236 | +2.运算规则是否符合语法规则 |
| 237 | +3.语句优先级 |
| 238 | +…… |
| 239 | + 在这个阶段可以直接翻译成目标代码, 或者生成诸如语法树之类的数据结构以便后续语义分析,优化等阶段利用。 |
| 240 | +> 上下文无关文法: 文法中所有的产生式左边只有一个非终结符 |
| 241 | +> https://www.zhihu.com/question/21833944 |
| 242 | +
|
| 243 | +### bison |
| 244 | +### goyacc |
0 commit comments