forked from sunshaoce/rvcc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
preprocess.c
1470 lines (1283 loc) · 40.9 KB
/
preprocess.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "rvcc.h"
// 宏函数形参
typedef struct MacroParam MacroParam;
struct MacroParam {
MacroParam *Next; // 下一个
char *Name; // 名称
};
// 宏函数实参
typedef struct MacroArg MacroArg;
struct MacroArg {
MacroArg *Next; // 下一个
char *Name; // 名称
bool IsVaArg; // 是否为可变参数
Token *Tok; // 对应的终结符链表
};
// 宏处理函数
typedef Token *macroHandlerFn(Token *);
// 定义的宏变量
typedef struct Macro Macro;
struct Macro {
char *Name; // 名称
bool IsObjlike; // 宏变量为真,或者宏函数为假
MacroParam *Params; // 宏函数参数
char *VaArgsName; // 可变参数
Token *Body; // 对应的终结符
macroHandlerFn *Handler; // 宏处理函数
};
// 宏变量栈
static HashMap Macros;
// #if可以嵌套,所以使用栈来保存嵌套的#if
typedef struct CondIncl CondIncl;
struct CondIncl {
CondIncl *Next; // 下一个
enum { IN_THEN, IN_ELIF, IN_ELSE } Ctx; // 类型
Token *Tok; // 对应的终结符
bool Included; // 是否被包含
};
// 预处理语言的设计方式使得即使存在递归宏也可以保证停止。
// 一个宏只对每个终结符应用一次。
//
// 宏展开时的隐藏集
typedef struct Hideset Hideset;
struct Hideset {
Hideset *Next; // 下一个
char *Name; // 名称
};
// 全局的#if保存栈
static CondIncl *CondIncls;
// 记录含有 #pragma once 的文件
static HashMap PragmaOnce;
// 记录 #include_next 开始的索引值
static int IncludeNextIdx;
// 处理所有的宏和指示
static Token *preprocess2(Token *Tok);
// 查找宏
static Macro *findMacro(Token *tok);
// 是否行首是#号
static bool isHash(Token *Tok) { return Tok->AtBOL && equal(Tok, "#"); }
// 一些预处理器允许#include等指示,在换行前有多余的终结符
// 此函数跳过这些终结符
static Token *skipLine(Token *Tok) {
// 在行首,正常情况
if (Tok->AtBOL)
return Tok;
// 提示多余的字符
warnTok(Tok, "extra token");
// 跳过终结符,直到行首
while (!Tok->AtBOL)
Tok = Tok->Next;
return Tok;
}
static Token *copyToken(Token *Tok) {
Token *T = calloc(1, sizeof(Token));
*T = *Tok;
T->Next = NULL;
return T;
}
// 新建一个EOF终结符
static Token *newEOF(Token *Tok) {
Token *T = copyToken(Tok);
T->Kind = TK_EOF;
T->Len = 0;
return T;
}
// 新建一个隐藏集
static Hideset *newHideset(char *Name) {
Hideset *Hs = calloc(1, sizeof(Hideset));
Hs->Name = Name;
return Hs;
}
// 连接两个隐藏集
static Hideset *hidesetUnion(Hideset *Hs1, Hideset *Hs2) {
Hideset Head = {};
Hideset *Cur = &Head;
for (; Hs1; Hs1 = Hs1->Next)
Cur = Cur->Next = newHideset(Hs1->Name);
Cur->Next = Hs2;
return Head.Next;
}
// 是否已经处于宏变量当中
static bool hidesetContains(Hideset *Hs, char *S, int Len) {
// 遍历隐藏集
for (; Hs; Hs = Hs->Next)
if (strlen(Hs->Name) == Len && !strncmp(Hs->Name, S, Len))
return true;
return false;
}
// 取两个隐藏集的交集
static Hideset *hidesetIntersection(Hideset *Hs1, Hideset *Hs2) {
Hideset Head = {};
Hideset *Cur = &Head;
// 遍历Hs1,如果Hs2也有,那么就加入链表当中
for (; Hs1; Hs1 = Hs1->Next)
if (hidesetContains(Hs2, Hs1->Name, strlen(Hs1->Name)))
Cur = Cur->Next = newHideset(Hs1->Name);
return Head.Next;
}
// 遍历Tok之后的所有终结符,将隐藏集Hs都赋给每个终结符
static Token *addHideset(Token *Tok, Hideset *Hs) {
Token Head = {};
Token *Cur = &Head;
for (; Tok; Tok = Tok->Next) {
Token *T = copyToken(Tok);
T->Hideset = hidesetUnion(T->Hideset, Hs);
Cur = Cur->Next = T;
}
return Head.Next;
}
// 将Tok2放入Tok1的尾部
static Token *append(Token *Tok1, Token *Tok2) {
// Tok1为空时,直接返回Tok2
if (Tok1->Kind == TK_EOF)
return Tok2;
Token Head = {};
Token *Cur = &Head;
// 遍历Tok1,存入链表
for (; Tok1->Kind != TK_EOF; Tok1 = Tok1->Next)
Cur = Cur->Next = copyToken(Tok1);
// 链表后接Tok2
Cur->Next = Tok2;
// 返回下一个
return Head.Next;
}
// 跳过#if和#endif
static Token *skipCondIncl2(Token *Tok) {
while (Tok->Kind != TK_EOF) {
if (isHash(Tok) && (equal(Tok->Next, "if") || equal(Tok->Next, "ifdef") ||
equal(Tok->Next, "ifndef"))) {
Tok = skipCondIncl2(Tok->Next->Next);
continue;
}
if (isHash(Tok) && equal(Tok->Next, "endif"))
return Tok->Next->Next;
Tok = Tok->Next;
}
return Tok;
}
// #if为空时,一直跳过到#endif
// 其中嵌套的#if语句也一起跳过
static Token *skipCondIncl(Token *Tok) {
while (Tok->Kind != TK_EOF) {
// 跳过#if语句
if (isHash(Tok) && (equal(Tok->Next, "if") || equal(Tok->Next, "ifdef") ||
equal(Tok->Next, "ifndef"))) {
Tok = skipCondIncl2(Tok->Next->Next);
continue;
}
// #elif,#else和#endif
if (isHash(Tok) && (equal(Tok->Next, "elif") || equal(Tok->Next, "else") ||
equal(Tok->Next, "endif")))
break;
Tok = Tok->Next;
}
return Tok;
}
// 将给定的字符串用双引号包住
static char *quoteString(char *Str) {
// 两个引号,一个\0
int BufSize = 3;
// 如果有 \ 或 " 那么需要多留一个位置,存储转义用的 \ 符号
for (int I = 0; Str[I]; I++) {
if (Str[I] == '\\' || Str[I] == '"')
BufSize++;
BufSize++;
}
// 分配相应的空间
char *Buf = calloc(1, BufSize);
char *P = Buf;
// 开头的"
*P++ = '"';
for (int I = 0; Str[I]; I++) {
if (Str[I] == '\\' || Str[I] == '"')
// 加上转义用的 \ 符号
*P++ = '\\';
*P++ = Str[I];
}
// 结尾的"\0
*P++ = '"';
*P++ = '\0';
return Buf;
}
// 构建一个新的字符串的终结符
static Token *newStrToken(char *Str, Token *Tmpl) {
// 将字符串加上双引号
char *Buf = quoteString(Str);
// 将字符串和相应的宏名称传入词法分析,去进行解析
return tokenize(newFile(Tmpl->File->Name, Tmpl->File->FileNo, Buf));
}
static Token *copyLine(Token **Rest, Token *Tok) {
Token head = {};
Token *Cur = &head;
// 遍历复制终结符
for (; !Tok->AtBOL; Tok = Tok->Next)
Cur = Cur->Next = copyToken(Tok);
// 以EOF终结符结尾
Cur->Next = newEOF(Tok);
*Rest = Tok;
return head.Next;
}
// 构造数字终结符
static Token *newNumToken(int Val, Token *Tmpl) {
char *Buf = format("%d\n", Val);
return tokenize(newFile(Tmpl->File->Name, Tmpl->File->FileNo, Buf));
}
// 读取常量表达式
static Token *readConstExpr(Token **Rest, Token *Tok) {
// 复制当前行
Tok = copyLine(Rest, Tok);
Token Head = {};
Token *Cur = &Head;
// 遍历终结符
while (Tok->Kind != TK_EOF) {
// "defined(foo)" 或 "defined foo"如果存在foo为1否则为0
if (equal(Tok, "defined")) {
Token *Start = Tok;
// 消耗掉(
bool HasParen = consume(&Tok, Tok->Next, "(");
if (Tok->Kind != TK_IDENT)
errorTok(Start, "macro name must be an identifier");
// 查找宏
Macro *M = findMacro(Tok);
Tok = Tok->Next;
// 对应着的)
if (HasParen)
Tok = skip(Tok, ")");
// 构造一个相应的数字终结符
Cur = Cur->Next = newNumToken(M ? 1 : 0, Start);
continue;
}
// 将剩余的终结符存入链表
Cur = Cur->Next = Tok;
// 终结符前进
Tok = Tok->Next;
}
// 将剩余的终结符存入链表
Cur->Next = Tok;
return Head.Next;
}
// 读取并计算常量表达式
static long evalConstExpr(Token **Rest, Token *Tok) {
Token *Start = Tok;
// 解析#if后的常量表达式
Token *Expr = readConstExpr(Rest, Tok->Next);
// 对于宏变量进行解析
Expr = preprocess2(Expr);
// 空表达式报错
if (Expr->Kind == TK_EOF)
errorTok(Start, "no expression");
// 在计算常量表达式前,将遗留的标识符替换为0
for (Token *T = Expr; T->Kind != TK_EOF; T = T->Next) {
if (T->Kind == TK_IDENT) {
Token *Next = T->Next;
*T = *newNumToken(0, T);
T->Next = Next;
}
}
// 转换预处理数值到正常数值
convertPPTokens(Expr);
// 计算常量表达式的值
Token *Rest2;
long Val = constExpr(&Rest2, Expr);
// 计算后还有多余的终结符,则报错
if (Rest2->Kind != TK_EOF)
errorTok(Rest2, "extra token");
// 返回计算的值
return Val;
}
// 压入#if栈中
static CondIncl *pushCondIncl(Token *Tok, bool Included) {
CondIncl *CI = calloc(1, sizeof(CondIncl));
CI->Next = CondIncls;
CI->Ctx = IN_THEN;
CI->Tok = Tok;
CI->Included = Included;
CondIncls = CI;
return CI;
}
// 查找相应的宏变量
static Macro *findMacro(Token *Tok) {
// 如果不是标识符,直接报错
if (Tok->Kind != TK_IDENT)
return NULL;
// 如果匹配则返回相应的宏变量
return hashmapGet2(&Macros, Tok->Loc, Tok->Len);
}
// 新增宏变量,压入宏变量栈中
static Macro *addMacro(char *Name, bool IsObjlike, Token *Body) {
Macro *M = calloc(1, sizeof(Macro));
M->Name = Name;
M->IsObjlike = IsObjlike;
M->Body = Body;
// 插入宏变量
hashmapPut(&Macros, Name, M);
return M;
}
static MacroParam *readMacroParams(Token **Rest, Token *Tok,
char **VaArgsName) {
MacroParam Head = {};
MacroParam *Cur = &Head;
while (!equal(Tok, ")")) {
if (Cur != &Head)
Tok = skip(Tok, ",");
// 处理可变参数
if (equal(Tok, "...")) {
*VaArgsName = "__VA_ARGS__";
// "..."应为最后一个参数
*Rest = skip(Tok->Next, ")");
return Head.Next;
}
// 如果不是标识符报错
if (Tok->Kind != TK_IDENT)
errorTok(Tok, "expected an identifier");
// 处理GNU风格的可变参数
if (equal(Tok->Next, "...")) {
// 设置参数的名称
*VaArgsName = strndup(Tok->Loc, Tok->Len);
// "..."应为最后一个参数
*Rest = skip(Tok->Next->Next, ")");
return Head.Next;
}
// 开辟空间
MacroParam *M = calloc(1, sizeof(MacroParam));
// 设置名称
M->Name = strndup(Tok->Loc, Tok->Len);
// 加入链表
Cur = Cur->Next = M;
Tok = Tok->Next;
}
*Rest = Tok->Next;
return Head.Next;
}
// 读取宏定义
static void readMacroDefinition(Token **Rest, Token *Tok) {
// 如果匹配到的不是标识符就报错
if (Tok->Kind != TK_IDENT)
errorTok(Tok, "macro name must be an identifier");
// 复制名字
char *Name = strndup(Tok->Loc, Tok->Len);
Tok = Tok->Next;
// 判断是宏变量还是宏函数,括号前没有空格则为宏函数
if (!Tok->HasSpace && equal(Tok, "(")) {
// 构造形参
char *VaArgsName = NULL;
MacroParam *Params = readMacroParams(&Tok, Tok->Next, &VaArgsName);
// 增加宏函数
Macro *M = addMacro(Name, false, copyLine(Rest, Tok));
M->Params = Params;
// 传递是否为可变参数的值
M->VaArgsName = VaArgsName;
} else {
// 增加宏变量
addMacro(Name, true, copyLine(Rest, Tok));
}
}
// 读取单个宏实参
static MacroArg *readMacroArgOne(Token **Rest, Token *Tok, bool ReadRest) {
Token Head = {};
Token *Cur = &Head;
int Level = 0;
// 读取实参对应的终结符
while (true) {
// 终止条件
if (Level == 0 && equal(Tok, ")"))
break;
// 在这里ReadRest为真时,则可以读取多个终结符
if (Level == 0 && !ReadRest && equal(Tok, ","))
break;
if (Tok->Kind == TK_EOF)
errorTok(Tok, "premature end of input");
if (equal(Tok, "("))
Level++;
else if (equal(Tok, ")"))
Level--;
// 将标识符加入到链表中
Cur = Cur->Next = copyToken(Tok);
Tok = Tok->Next;
}
// 加入EOF终结
Cur->Next = newEOF(Tok);
MacroArg *Arg = calloc(1, sizeof(MacroArg));
// 赋值实参的终结符链表
Arg->Tok = Head.Next;
*Rest = Tok;
return Arg;
}
// 读取宏实参
static MacroArg *readMacroArgs(Token **Rest, Token *Tok, MacroParam *Params,
char *VaArgsName) {
Token *Start = Tok;
Tok = Tok->Next->Next;
MacroArg Head = {};
MacroArg *Cur = &Head;
// 遍历形参,然后对应着加入到实参链表中
MacroParam *PP = Params;
for (; PP; PP = PP->Next) {
if (Cur != &Head)
Tok = skip(Tok, ",");
// 读取单个实参
Cur = Cur->Next = readMacroArgOne(&Tok, Tok, false);
// 设置为对应的形参名称
Cur->Name = PP->Name;
}
// 剩余未匹配的实参,如果为可变参数
if (VaArgsName) {
MacroArg *Arg;
// 剩余实参为空
if (equal(Tok, ")")) {
Arg = calloc(1, sizeof(MacroArg));
Arg->Tok = newEOF(Tok);
} else {
// 处理对应可变参数的实参
// 跳过","
if (PP != Params)
Tok = skip(Tok, ",");
Arg = readMacroArgOne(&Tok, Tok, true);
}
Arg->Name = VaArgsName;
Arg->IsVaArg = true;
Cur = Cur->Next = Arg;
}
// 如果形参没有遍历完,就报错
else if (PP) {
errorTok(Start, "too many arguments");
}
skip(Tok, ")");
// 这里返回右括号
*Rest = Tok;
return Head.Next;
}
// 遍历查找实参
static MacroArg *findArg(MacroArg *Args, Token *Tok) {
for (MacroArg *AP = Args; AP; AP = AP->Next)
if (Tok->Len == strlen(AP->Name) && !strncmp(Tok->Loc, AP->Name, Tok->Len))
return AP;
return NULL;
}
// 将终结符链表中的所有终结符都连接起来,然后返回一个新的字符串
static char *joinTokens(Token *Tok, Token *End) {
// 计算最终终结符的长度
int Len = 1;
for (Token *T = Tok; T != End && T->Kind != TK_EOF; T = T->Next) {
// 非第一个,且前面有空格,计数加一
if (T != Tok && T->HasSpace)
Len++;
// 加上终结符的长度
Len += T->Len;
}
// 开辟相应的空间
char *Buf = calloc(1, Len);
// 复制终结符的文本
int Pos = 0;
for (Token *T = Tok; T != End && T->Kind != TK_EOF; T = T->Next) {
// 非第一个,且前面有空格,设为空格
if (T != Tok && T->HasSpace)
Buf[Pos++] = ' ';
// 拷贝相应的内容
strncpy(Buf + Pos, T->Loc, T->Len);
Pos += T->Len;
}
// 以'\0'结尾
Buf[Pos] = '\0';
return Buf;
}
// 将所有实参中的终结符连接起来,然后返回一个字符串的终结符
static Token *stringize(Token *Hash, Token *Arg) {
// 创建一个字符串的终结符
char *S = joinTokens(Arg, NULL);
// 我们需要一个位置用来报错,所以使用了宏的名字
return newStrToken(S, Hash);
}
// 拼接两个终结符构建一个新的终结符
static Token *paste(Token *LHS, Token *RHS) {
// 合并两个终结符
char *Buf = format("%.*s%.*s", LHS->Len, LHS->Loc, RHS->Len, RHS->Loc);
// 词法解析生成的字符串,转换为相应的终结符
Token *Tok = tokenize(newFile(LHS->File->Name, LHS->File->FileNo, Buf));
if (Tok->Next->Kind != TK_EOF)
errorTok(LHS, "pasting forms '%s', an invalid token", Buf);
return Tok;
}
// 判断是否具有预处理的可变参数
static bool hasVarArgs(MacroArg *Args) {
for (MacroArg *AP = Args; AP; AP = AP->Next)
// 判断是否存在__VA_ARGS__
if (!strcmp(AP->Name, "__VA_ARGS__"))
// 判断可变参数是否为空
return AP->Tok->Kind != TK_EOF;
return false;
}
// 将宏函数形参替换为指定的实参
static Token *subst(Token *Tok, MacroArg *Args) {
Token Head = {};
Token *Cur = &Head;
// 遍历将形参替换为实参的终结符链表
while (Tok->Kind != TK_EOF) {
// #宏实参 会被替换为相应的字符串
if (equal(Tok, "#")) {
// 查找实参
MacroArg *Arg = findArg(Args, Tok->Next);
if (!Arg)
errorTok(Tok->Next, "'#' is not followed by a macro parameter");
// 将实参的终结符字符化
Cur = Cur->Next = stringize(Tok, Arg->Tok);
Tok = Tok->Next->Next;
continue;
}
// 若__VA_ARGS__为空, 则 `,##__VA_ARGS__` 展开为空
// 若__VA_ARGS__不为空,则 `,##__VA_ARGS__` 展开为 `,` 和 __VA_ARGS__
if (equal(Tok, ",") && equal(Tok->Next, "##")) {
// 匹配__VA_ARGS__
MacroArg *Arg = findArg(Args, Tok->Next->Next);
if (Arg && Arg->IsVaArg) {
if (Arg->Tok->Kind == TK_EOF) {
// 如果__VA_ARGS__为空
Tok = Tok->Next->Next->Next;
} else {
// 若__VA_ARGS__不为空,则展开
Cur = Cur->Next = copyToken(Tok);
Tok = Tok->Next->Next;
}
continue;
}
}
// ##及右边,用于连接终结符
if (equal(Tok, "##")) {
if (Cur == &Head)
errorTok(Tok, "'##' cannot appear at start of macro expansion");
if (Tok->Next->Kind == TK_EOF)
errorTok(Tok, "'##' cannot appear at end of macro expansion");
// 查找下一个终结符
// 如果是(##右边)宏实参
MacroArg *Arg = findArg(Args, Tok->Next);
if (Arg) {
if (Arg->Tok->Kind != TK_EOF) {
// 拼接当前终结符和(##右边)实参
*Cur = *paste(Cur, Arg->Tok);
// 将(##右边)实参未参与拼接的剩余部分加入到链表当中
for (Token *T = Arg->Tok->Next; T->Kind != TK_EOF; T = T->Next)
Cur = Cur->Next = copyToken(T);
}
// 指向(##右边)实参的下一个
Tok = Tok->Next->Next;
continue;
}
// 如果不是(##右边)宏实参
// 直接拼接
*Cur = *paste(Cur, Tok->Next);
Tok = Tok->Next->Next;
continue;
}
// 查找实参
MacroArg *Arg = findArg(Args, Tok);
// 左边及##,用于连接终结符
if (Arg && equal(Tok->Next, "##")) {
// 读取##右边的终结符
Token *RHS = Tok->Next->Next;
// 实参(##左边)为空的情况
if (Arg->Tok->Kind == TK_EOF) {
// 查找(##右边)实参
MacroArg *Arg2 = findArg(Args, RHS);
if (Arg2) {
// 如果是实参,那么逐个遍历实参对应的终结符
for (Token *T = Arg2->Tok; T->Kind != TK_EOF; T = T->Next)
Cur = Cur->Next = copyToken(T);
} else {
// 如果不是实参,那么直接复制进链表
Cur = Cur->Next = copyToken(RHS);
}
// 指向(##右边)实参的下一个
Tok = RHS->Next;
continue;
}
// 实参(##左边)不为空的情况
for (Token *T = Arg->Tok; T->Kind != TK_EOF; T = T->Next)
// 复制此终结符
Cur = Cur->Next = copyToken(T);
Tok = Tok->Next;
continue;
}
// 如果__VA_ARGS__为空,则__VA_OPT__(x)也为空
// 如果__VA_ARGS__不为空,则__VA_OPT__(x)展开为x
if (equal(Tok, "__VA_OPT__") && equal(Tok->Next, "(")) {
// 读取__VA_OPT__(x)的参数x
MacroArg *Arg = readMacroArgOne(&Tok, Tok->Next->Next, true);
// 如果预处理的可变参数不为空
if (hasVarArgs(Args))
// 则__VA_OPT__(x)展开为x
for (Token *T = Arg->Tok; T->Kind != TK_EOF; T = T->Next)
Cur = Cur->Next = T;
Tok = skip(Tok, ")");
continue;
}
// 处理宏终结符,宏实参在被替换之前已经被展开了
if (Arg) {
// 解析实参对应的终结符链表
Token *T = preprocess2(Arg->Tok);
// 传递 是否为行首 和 前面是否有空格 的信息
T->AtBOL = Tok->AtBOL;
T->HasSpace = Tok->HasSpace;
for (; T->Kind != TK_EOF; T = T->Next)
Cur = Cur->Next = copyToken(T);
Tok = Tok->Next;
continue;
}
// 处理非宏的终结符
Cur = Cur->Next = copyToken(Tok);
Tok = Tok->Next;
continue;
}
Cur->Next = Tok;
// 将宏链表返回
return Head.Next;
}
// 如果是宏变量并展开成功,返回真
static bool expandMacro(Token **Rest, Token *Tok) {
// 判断是否处于隐藏集之中
if (hidesetContains(Tok->Hideset, Tok->Loc, Tok->Len))
return false;
// 判断是否为宏变量
Macro *M = findMacro(Tok);
if (!M)
return false;
// 如果宏设置了相应的处理函数,例如__LINE__
if (M->Handler) {
// 就使用相应的处理函数解析当前的宏
*Rest = M->Handler(Tok);
// 紧接着处理后续终结符
(*Rest)->Next = Tok->Next;
return true;
}
// 为宏变量时
if (M->IsObjlike) {
// 展开过一次的宏变量,就加入到隐藏集当中
Hideset *Hs = hidesetUnion(Tok->Hideset, newHideset(M->Name));
// 处理此宏变量之后,传递隐藏集给之后的终结符
Token *Body = addHideset(M->Body, Hs);
// 记录展开前的宏
for (Token *T = Body; T->Kind != TK_EOF; T = T->Next)
T->Origin = Tok;
*Rest = append(Body, Tok->Next);
// 传递 是否为行首 和 前面是否有空格 的信息
(*Rest)->AtBOL = Tok->AtBOL;
(*Rest)->HasSpace = Tok->HasSpace;
return true;
}
// 如果宏函数后面没有参数列表,就处理为正常的标识符
if (!equal(Tok->Next, "("))
return false;
// 处理宏函数,并连接到Tok之后
// 读取宏函数实参,这里是宏函数的隐藏集
Token *MacroToken = Tok;
MacroArg *Args = readMacroArgs(&Tok, Tok, M->Params, M->VaArgsName);
// 这里返回的是右括号,这里是宏参数的隐藏集
Token *RParen = Tok;
// 宏函数间可能具有不同的隐藏集,新的终结符就不知道应该使用哪个隐藏集。
// 我们取宏终结符和右括号的交集,并将其用作新的隐藏集。
Hideset *Hs = hidesetIntersection(MacroToken->Hideset, RParen->Hideset);
// 将当前函数名加入隐藏集
Hs = hidesetUnion(Hs, newHideset(M->Name));
// 替换宏函数内的形参为实参
Token *Body = subst(M->Body, Args);
// 为宏函数内部设置隐藏集
Body = addHideset(Body, Hs);
// 记录展开前的宏函数
for (Token *T = Body; T->Kind != TK_EOF; T = T->Next)
T->Origin = MacroToken;
// 将设置好的宏函数内部连接到终结符链表中
*Rest = append(Body, Tok->Next);
// 传递 是否为行首 和 前面是否有空格 的信息
(*Rest)->AtBOL = MacroToken->AtBOL;
(*Rest)->HasSpace = MacroToken->HasSpace;
return true;
}
// 搜索引入路径区
char *searchIncludePaths(char *Filename) {
// 以"/"开头的视为绝对路径
if (Filename[0] == '/')
return Filename;
// 文件搜索的缓存,被搜索的文件都会存入这里,方便快速查找
static HashMap Cache;
char *Cached = hashmapGet(&Cache, Filename);
if (Cached)
return Cached;
// 从引入路径区查找文件
for (int I = 0; I < IncludePaths.Len; I++) {
char *Path = format("%s/%s", IncludePaths.Data[I], Filename);
if (!fileExists(Path))
continue;
// 将搜索到的结果顺带存入文件搜索的缓存
hashmapPut(&Cache, Filename, Path);
// #include_next应从#include未遍历的路径开始
IncludeNextIdx = I + 1;
return Path;
}
return NULL;
}
// 搜索 #include_next 的文件路径
static char *searchIncludeNext(char *Filename) {
// #include_next 从 IncludeNextIdx 开始遍历
for (; IncludeNextIdx < IncludePaths.Len; IncludeNextIdx++) {
// 拼接路径和文件名
char *Path = format("%s/%s", IncludePaths.Data[IncludeNextIdx], Filename);
// 如果文件存在
if (fileExists(Path))
// 返回该路径
return Path;
}
// 否则返回空
return NULL;
}
// 读取#include参数
static char *readIncludeFilename(Token **Rest, Token *Tok, bool *IsDquote) {
// 匹配样式1: #include "foo.h"
if (Tok->Kind == TK_STR) {
// #include 的双引号文件名是一种特殊的终结符
// 不能转义其中的任何转义字符。
// 例如,“C:\foo”中的“\f”不是换页符,而是\和f。
// 所以此处不使用token->str。
*IsDquote = true;
*Rest = skipLine(Tok->Next);
return strndup(Tok->Loc + 1, Tok->Len - 2);
}
// 匹配样式2: #include <foo.h>
if (equal(Tok, "<")) {
// 从"<"和">"间构建文件名.
Token *Start = Tok;
// 查找">"
for (; !equal(Tok, ">"); Tok = Tok->Next)
if (Tok->AtBOL || Tok->Kind == TK_EOF)
errorTok(Tok, "expected '>'");
// 没有引号
*IsDquote = false;
// 跳到行首
*Rest = skipLine(Tok->Next);
// "<"到">"前拼接为字符串
return joinTokens(Start->Next, Tok);
}
// 匹配样式3: #include FOO
if (Tok->Kind == TK_IDENT) {
// FOO 必须宏展开为单个字符串标记或 "<" ... ">" 序列
Token *Tok2 = preprocess2(copyLine(Rest, Tok));
// 然后读取引入的文件名
return readIncludeFilename(&Tok2, Tok2, IsDquote);
}
errorTok(Tok, "expected a filename");
return NULL;
}
// 检测类似于下述的 引用防护(Include Guard) 优化
//
// #ifndef FOO_H
// #define FOO_H
// ...
// #endif
static char *detectIncludeGuard(Token *Tok) {
// 匹配 #ifndef
if (!isHash(Tok) || !equal(Tok->Next, "ifndef"))
return NULL;
Tok = Tok->Next->Next;
// 判断 FOO_H 是否为标识符
if (Tok->Kind != TK_IDENT)
return NULL;
// 复制 FOO_H 作为宏名称
char *Macro = strndup(Tok->Loc, Tok->Len);
Tok = Tok->Next;
// 匹配 #define FOO_H
if (!isHash(Tok) || !equal(Tok->Next, "define") ||
!equal(Tok->Next->Next, Macro))
return NULL;
// 读取到 文件结束
while (Tok->Kind != TK_EOF) {
// 如果不是 宏 相关的,则前进Tok
if (!isHash(Tok)) {
Tok = Tok->Next;
continue;
}
// 匹配 #endif 以及 TK_EOF ,之后返回宏名称
if (equal(Tok->Next, "endif") && Tok->Next->Next->Kind == TK_EOF)
return Macro;
// 匹配 #if 或 #ifdef 或 #ifndef,跳过其中不满足 宏条件 的语句
if (equal(Tok, "if") || equal(Tok, "ifdef") || equal(Tok, "ifndef"))
Tok = skipCondIncl(Tok->Next);
else
Tok = Tok->Next;
}
return NULL;
}
// 引入文件
static Token *includeFile(Token *Tok, char *Path, Token *FilenameTok) {
// 如果含有 "#pragma once",已经被读取过,那么就跳过文件
if (hashmapGet(&PragmaOnce, Path))
return Tok;
// 如果引用防护的文件,已经被读取过,那么就跳过文件
static HashMap IncludeGuards;
char *GuardName = hashmapGet(&IncludeGuards, Path);
if (GuardName && hashmapGet(&Macros, GuardName))
return Tok;
// 词法分析文件
Token *Tok2 = tokenizeFile(Path);
if (!Tok2)
errorTok(FilenameTok, "%s: cannot open file: %s", Path, strerror(errno));
// 判断文件是否使用了 引用防护
GuardName = detectIncludeGuard(Tok2);
if (GuardName)
hashmapPut(&IncludeGuards, Path, GuardName);
return append(Tok2, Tok);
}
// 读取#line的参数
static void readLineMarker(Token **Rest, Token *Tok) {
Token *Start = Tok;
// 对参数进行解析
Tok = preprocess(copyLine(Rest, Tok));
// 标记的行号
if (Tok->Kind != TK_NUM || Tok->Ty->Kind != TY_INT)
errorTok(Tok, "invalid line marker");
// 获取行标记的行号与原先的行号相减,设定二者间的差值
Start->File->LineDelta = Tok->Val - Start->LineNo;
Tok = Tok->Next;
// 处理无标记的文件名的情况
if (Tok->Kind == TK_EOF)
return;
if (Tok->Kind != TK_STR)
errorTok(Tok, "filename expected");
// 处理标记的文件名
Start->File->DisplayName = Tok->Str;
}
// 遍历终结符,处理宏和指示
static Token *preprocess2(Token *Tok) {
Token Head = {};
Token *Cur = &Head;
// 遍历终结符
while (Tok->Kind != TK_EOF) {
// 如果是个宏变量,那么就展开
if (expandMacro(&Tok, Tok))
continue;
// 如果不是#号开头则前进
if (!isHash(Tok)) {
// 设定标记的行号差值
Tok->LineDelta = Tok->File->LineDelta;
// 设定标记的文件名
Tok->Filename = Tok->File->DisplayName;
Cur->Next = Tok;
Cur = Cur->Next;
Tok = Tok->Next;
continue;
}
// 记录开始的终结符