-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpl0c_extend.c
2220 lines (2131 loc) · 81.7 KB
/
pl0c_extend.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
/*
* PL/0 complier program for win32 platform (implemented in C)
*
* The program has been test on Visual C++ 6.0, Visual C++.NET and
* Visual C++.NET 2003, on Win98, WinNT, Win2000, WinXP and Win2003
*
* 使用方法:
* 运行后输入PL/0源程序文件?
* 回答是否输出虚拟机代码
* 回答是否输出名字表
* fa.tmp输出虚拟机代码
* fa1.tmp输出源文件及其各行对应的首地址
* fa2.tmp输出结?
* fas.tmp输出名字表
*/
#include <stdio.h>
#include <stdlib.h>
#include "pl0_extend.h"
#include "string.h"
/* 解释执行时使用的栈 */
#define stacksize 500
int main(int argc,char** argv)
{
// symnum为48, 代表48类符号
bool nxtlev[symnum];
// 打开一个文件, 返回文件指针: FILE类型的指针变量, 指向该文件
Pl0_SourceCode = fopen(argv[1], "r");
// 如果这文件存在则ok
if (Pl0_SourceCode)
{
init(); /* 初始化 */
err = 0; // 错误计数器
// cc、ll: getch使用的计数器, cc表示当前字符
// cx虚拟机代码指针, 取值范围[0, cxmax-1]
cc = cx = ll = 0;
// 缓冲区中获取字符,getch 使用, 初始化为一个空格
ch = ' ';
// 首次读一个字符, 后面的在block里完成, 其是递归程序
if(-1 != getsym())
{
// nxtlev:长度为32的bool数组, declbegsys:代表一个声明开始的符号集合,statbegsys代表语句开始的符号集合
// 最终nxtlev里就存了3种声明和6种语句的开始符号, 是其中一种就是true
addset(nxtlev, declbegsys, statbegsys, symnum);
// 再加一个程序结束符 .
nxtlev[period] = true;
// 4个参数, 1: 当前分程序所在层, 2: 名字表当前尾指针, 第三个就是标识了3种声明和6种语句的bool数组
// 4: param_num参数个数
if(-1 == block(0, 0, nxtlev, 0)) /* 调用编译程序 */
{
fclose(Pl0_SourceCode);
printf("\n");
return 0;
}
// 如果最后一个符号不是period, 就表示没有正确结束
if (sym != period)
{
error(9); // 程序结尾不是 . 非正常结束
}
// 如果只有0个错误, 就可以调用解释程序开始运行了
if (err == 0)
{
// 将虚拟机代码存入文件
StoreToFile();
}
else // 否则报错, 无法用解释程序来运行
{
printf("Errors in pl/0 program");
}
}
fclose(Pl0_SourceCode); // 关闭源文件
}
else // 文件都无法正常打开
{
printf("Can't open file!\n");
}
printf("\n");
// 程序结束暂停一下
// system("pause");
return 0;
}
/*
* 初始化,设置符号值——符号辨识,类型归类——类型辨识
*/
void init()
{
int i;
/* 设置单字符符号 */
for (i=0; i<=255; i++)
{
ssym[i] = nul; // nul = 0
}
for(i=0 ; i<cxmax; i++){
code[i].f = nan;
}
// 设置14个 单字符的符号值 以ASCII码为索引
ssym['+'] = plus; // 3
ssym['-'] = minus; // 4
ssym['*'] = times; // 5
ssym['/'] = slash; // 6
ssym['('] = lparen; // 14
ssym[')'] = rparen; // 15
ssym['='] = eql; // 8
ssym[','] = comma; // 16
ssym['.'] = period; // 18
ssym['#'] = neq; // 9 不等于
ssym[';'] = semicolon; // 17 分号
ssym[':'] = colon; // 33 冒号
ssym['['] = lbrakt; // 39 左中括号
ssym[']'] = rbrakt; // 40 右中括号
/* 设置保留字名字,按照字母顺序abcdefghijklmnoprstuvwxyz,便于折半查找 */
// 共22个保留字,word即代表保留字,word的第2个[]是存放保留字中一个个的字母,最多10个
// 增加break、else 、case、of、exit、step、until、real
strcpy(&(word[0][0]), "begin");
strcpy(&(word[1][0]), "break"); // break语句声明标志
strcpy(&(word[2][0]), "call");
strcpy(&(word[3][0]), "case"); // case语句声明标志
strcpy(&(word[4][0]), "const");
strcpy(&(word[5][0]), "do");
strcpy(&(word[6][0]), "else"); // else语句声明标志
strcpy(&(word[7][0]), "end");
strcpy(&(word[8][0]), "exit"); // exit语句声明标志
strcpy(&(word[9][0]), "for"); // for语句声明的标志
strcpy(&(word[10][0]), "if");
strcpy(&(word[11][0]), "odd");
strcpy(&(word[12][0]), "of"); // of的标志
strcpy(&(word[13][0]), "procedure");
strcpy(&(word[14][0]), "read");
strcpy(&(word[15][0]), "real"); // 浮点声明的标志
strcpy(&(word[16][0]), "step"); // step的标志
strcpy(&(word[17][0]), "then");
strcpy(&(word[18][0]), "until"); // until的标志
strcpy(&(word[19][0]), "var");
strcpy(&(word[20][0]), "while");
strcpy(&(word[21][0]), "write");
/* 设置保留字符号 */
// 设置22个保留字的符号值
wsym[0] = beginsym; // 20 begin
wsym[1] = breaksym; // 41 break声明
wsym[2] = callsym; // 28 call
wsym[3] = casesym; // 45 case
wsym[4] = constsym; // 29 const
wsym[5] = dosym; // 27 do
wsym[6] = elsesym; // 47 else
wsym[7] = endsym; // 21 end
wsym[8] = exitsym; // 44 exit声明
wsym[9] = forsym; // 36 for声明
wsym[10] = ifsym; // 22 if
wsym[11] = oddsym; // 7 odd
wsym[12] = ofsym; // 46 of
wsym[13] = procsym; // 31 procedure
wsym[14] = readsym; // 26 read
wsym[15] = realsym; // 34 real声明
wsym[16] = stepsym; // 37 step
wsym[17] = thensym; // 23 then
wsym[18] = untilsym; // 38 until
wsym[19] = varsym; // 30 var
wsym[20] = whilesym; // 24 while
wsym[21] = writesym; // 25 write
/* 设置符号集 */
// symnum = 32,代表32个符号(除去类P_CODE指令)
for (i=0; i<symnum; i++)
{
declbegsys[i] = false; // 表示声明开始的符号集合
statbegsys[i] = false; // 表示语句开始的符号集合
facbegsys[i] = false; // 表示因子开始的符号集合
}
// 9个保留字
/* 设置声明开始符号集 */
declbegsys[constsym] = true; // 常数声明
declbegsys[varsym] = true; // 变量声明
declbegsys[procsym] = true; // 过程声明
/* 设置语句开始符号集 */
statbegsys[beginsym] = true; // 复合语句的开始begin
statbegsys[callsym] = true; // 过程调用语句
statbegsys[ifsym] = true; // if条件语句
statbegsys[whilesym] = true; // while循环语句
statbegsys[readsym] = true; // 读语句
statbegsys[writesym] = true; // 写语句
statbegsys[forsym] = true; // for循环语句
statbegsys[exitsym] = true; // exit语句
statbegsys[casesym] = true; // case选择语句
statbegsys[breaksym] = true; // break语句
/* 设置因子(即等式右边的部分)开始符号集 */
facbegsys[ident] = true; // 标识符
facbegsys[number] = true; // 无符号整数
facbegsys[lparen] = true; // 左括号,left parenthesis 左圆括号
facbegsys[real] = true; // 浮点数real
}
/*
* 用数组实现集合的集合运算
*/
int inset(int e, bool* s)
{
return s[e];
}
// sr: 长度为32的bool数组, s1: 代表一个声明开始的符号集合,s2: 代表语句开始的符号集合, n = 32
// 最终sr里就存了3种声明和6种语句的开始符号, 是其中一种就是true
int addset(bool* sr, bool* s1, bool* s2, int n)
{
int i;
for (i=0; i<n; i++)
{
sr[i] = s1[i]||s2[i];
}
return 0;
}
int subset(bool* sr, bool* s1, bool* s2, int n)
{
int i;
for (i=0; i<n; i++)
{
sr[i] = s1[i]&&(!s2[i]);
}
return 0;
}
int mulset(bool* sr, bool* s1, bool* s2, int n)
{
int i;
for (i=0; i<n; i++)
{
sr[i] = s1[i]&&s2[i];
}
return 0;
}
/*
* 出错处理,打印出错位置 和 错误编码n
*/
void error(int n)
{
char space[81]; // 这个81和读取行缓冲区是一致的, 也是81个字符
// 初始化space, 用于表示错误在一个Line里出现的位置
memset(space,32,81); // 将这81个char(占1个字节)的位置全部填上32: 二进制为0010 0000, 其ASCII代表 空格
int i;
for(i=0;i<cc-1;i++)
{
space[i] = line[i];
}
// 赋值为0, 表示'\0'结束, 因此空格的长度(因为第一个空格位置是0)就是出错的位置(在最后一个空格后面, 为cc-1)
space[cc-1]=0; //出错时当前符号已经读完,所以cc-1
printf("****%d行的%s的之前的非空行\n错误%d:",hs, space, n);
switch(n)
{
case 1 :printf("常量说明中不可以用\":=\". \n");break;
case 2 :printf("常量说明中的\"=\"后应该是数字. \n");break;
case 3 :printf("缺少\"=\". \n");break;
case 4 :printf("const ,var ,procedure后应为标识符. \n");break;
case 5 :printf("漏掉了\",\"或者是\";\". \n");break;
case 6 :printf("过程说明后的符号不正确(应是句子的开始符,过程定义符)。\n");break;
case 7 :printf("声明顺序有误,应为[<变量说明部分>][<常量说明部分>] [<过程说明部分>]<语句>。\n");break;
case 8 :printf("程序体内的语句部分的符号不正确。\n");break;
case 9 :printf("程序的末尾丢掉了句号\".\"。\n");break;
case 10 :printf("句子之间漏掉了\";\"。\n");break;
case 11 :printf("标识符未说明.\n");break;
case 12 :printf("赋值号左端应为变量。\n");break;
case 13 :printf("应为 \":=/+=/-=\" 之一。\n");break;
case 14 :printf("call 后应为标识符。\n");break;
case 15 :printf("call 后标识符属性应是过程。\n");break;
case 16 :printf("缺少\"then\"。\n");break;
case 17 :printf("缺少\"end\"或\";\"。\n");break;
case 18 :printf("do while 型循环语句缺少do。\n");break;
case 19 :printf("语句后的标号不正确。\n");break;
case 20 :printf("应为关系运算符。\n");break;
case 21 :printf("表达式内的标识符属性不能是无返回值的过程。\n");break;
case 22 :printf("表达式中漏掉右括号。\n");break;
case 23 :printf("非法符号。\n");break;
case 24 :printf("表达式的开始符为非法符号符号。\n");break;
case 25 :printf("运算符的后边是常量。\n");break;
case 26 :printf("不存在此操作符。\n");break;
case 27 :printf("变量定义的长度应为常量或者是常数。\n");break;
case 28 :printf("变量定义重复。\n");break;
case 29 :printf("未找到对应过程名。\n");break;
case 30 :printf("不支持过程的判断。\n");break;
case 31 :printf("超过地址上界。\n");break;
case 32 :printf("超过最大允许过程嵌套声明层数。\n");break;
case 33 :printf("缺少)。");break;
case 34 :printf("read内应当为变量值。\n");break;
case 35 :printf("read里没标识符,或标识符未声明,或者标识符不是变量。\n");break;
case 36 :printf("未知变量类型。\n");break;
case 37 :printf("字符串过长。\n");break;
case 38 :printf("write中字符串后应为逗号。\n");break;
case 39 :printf("write格式化输出不对应。\n");break;
case 40 :printf("块注释未正常结束。\n");break;
case 41 :printf("for后 应为标识符。\n");break;
case 42 :printf("缺少step。\n");break;
case 43 :printf("缺少until。\n");break;
case 44 :printf("缺少do。\n");break;
case 45 :printf("数组大小必须是常量或立即数。\n");break;
case 46 :printf("数组声明中括号不完整。\n");break;
case 47 :printf("数组访问错误。\n");break;
case 48 :printf("数组访问缺少右括号。\n");break;
case 49 :printf("break不在循环语句内。\n");break;
case 50 :printf("循环超过规定最大层数。\n");break;
case 51 :printf("格式化输入参数错误。\n");break;
case 52 :printf("read后缺少左括号 (。\n");break;
case 53 :printf("未知格式化类型。\n");break;
case 54 :printf("read读入变量超过限制。\n");break;
case 55 :printf("过程传参语法错误。\n");break;
case 56 :printf("过程调用参数不一致。\n");break;
default :printf("找不到这种错误。\n");
}
err++;
}
/*
* 中间代码记录写到virCode.tmp文件中
*/
void StoreToFile(){
virCode = fopen("virCode.tmp", "w");
int length = sizeof(code)/sizeof(code[0]);
int iter;
for(iter=0 ; iter<length ; iter++){
switch(code[iter].f){
case 0:
if(code[iter].a/10>0)
fprintf(virCode,"lit %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"lit %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 1:
if(code[iter].a/10>0){
if(code[iter].a==17 || code[iter].a==19){
fprintf(virCode,"opr %d %d %s\n", code[iter].l, code[iter].a, code[iter].str);
}else{
fprintf(virCode,"opr %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
}
}
else
fprintf(virCode,"opr %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 2:
if(code[iter].a/10>0)
fprintf(virCode,"lod %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"lod %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 3:
if(code[iter].a/10>0)
fprintf(virCode,"sto %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"sto %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 4:
if(code[iter].a/10>0)
fprintf(virCode,"cal %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"cal %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 5:
if(code[iter].a/10>0)
fprintf(virCode,"int %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"int %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 6:
if(code[iter].a/10>0)
fprintf(virCode,"jmp %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"jmp %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 7:
if(code[iter].a/10>0)
fprintf(virCode,"jpc %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"jpc %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 9:
if(code[iter].a/10>0)
fprintf(virCode,"lda %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"lda %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 10:
if(code[iter].a/10>0)
fprintf(virCode,"sta %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"sta %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 11:
if(code[iter].a/10>0)
fprintf(virCode,"lpl %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"lpl %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 12:
if(code[iter].a/10>0)
fprintf(virCode,"lmi %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"lmi %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 13:
fprintf(virCode,"end %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
case 14:
if(code[iter].a/10>0)
fprintf(virCode,"mov %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
else
fprintf(virCode,"mov %d %d %f\n", code[iter].l, code[iter].a, code[iter].real);
break;
default:
break;
}
}
fclose(virCode);
}
/*
* 打印名字表
*/
void print_nameTable(int tx0, int tx){
printf("TABLE:\n");
int i;
if (tx0+1 > tx)
{
printf(" NULL\n");
}
printf("number \tkind \tname \tval/lev \taddr \ttype \tsize");
for (i=1; i<=tx; i++)
{
switch (table[i].kind)
{
case constant:
printf("\n%d \tconst \t%s \t", i, table[i].name);
printf("val=%d \taddr=%d \ttype=%c", table[i].val, table[i].adr, table[i].type);
break;
case variable:
printf("\n%d \tvar \t%s \t", i, table[i].name);
printf("lev=%d \taddr=%d \ttype=%c", table[i].level, table[i].adr, table[i].type);
break;
case procedur:
printf("\n%d \tproc \t%s \t", i, table[i].name);
printf("lev=%d \taddr=%d \tsize=%d", table[i].level, table[i].adr, table[i].size);
break;
case array:
printf("\n%d \tarray \t%s \t", i, table[i].name);
printf("lev=%d \taddr=%d \ttype=%c \tsize=%d", table[i].level, table[i].adr, table[i].type, table[i].size);
break;
}
}
printf("\n\n");
}
/*
* 漏掉空格,读取一个字符。
*
* 每次读一行,存入line缓冲区,line被getsym取空后再读一行
*
* 被函数getsym调用。
*/
int getch()
{
// ll、cc 均初始化为0, cc表示当前字符(ch)的位置
// 如果相等了, 说明上次从流中读出的一行字符(在line里)已经被读完了, 就得再读一行出来
if (cc == ll)
{
// feof: 检测流上的文件是否已结束,如果光标后啥也没有,则返回非0值,光标后有东西(包括EOF), 返回0
// Pl0_SourceCode: 源代码文件的指针
if (feof(Pl0_SourceCode))
{
printf("program incomplete");
return -1;
}
// 重新赋值为0
ll=0;
cc=0;
hs++; /* 读取下一行,行数+1 */
// printf("%d ", cx); // cx, 虚拟机代码指针(其实是个数字, 表示这是第几行虚拟机代码)
ch = ' '; // 空格, 对应ASCII 32
while (ch != 10) // 刚开始32肯定不等于10, 则进入循环, 10是换行符line feed(意思是把整行读完)
{
// 如果是EOF文件结束符, 则line中该位置为'\0'串尾
if (EOF == fscanf(Pl0_SourceCode,"%c", &ch)) // 从流中读取一个%c即单个字符到ch
{
line[ll] = 0;
break;
}
// 代表是行注释
if(ch=='/' && ll!=0 && line[ll-1]=='/'){
ll--;
// 这里进来的肯定也不等于10,只为了把剩下的读完
while (ch != 10){
// EOF的意思是,读取错误,即下一个没读到东西
if (EOF == fscanf(Pl0_SourceCode,"%c", &ch)) // 从流中读取一个%c即单个字符到ch
{
// 这里我们可以让line[0]的位置为0,反正是注释
line[ll] = 0;
break;
}
}
break;
}else if(ch=='*' && ll!=0 && line[ll-1]=='/'){ // 代表是块注释
ll--;
char tempch; // tempch作为当前ch的前一个值
fscanf(Pl0_SourceCode,"%c", &ch);
// 读到 */ 为止
do{
tempch = ch;
if (EOF == fscanf(Pl0_SourceCode,"%c", &ch)) // 从流中读取一个%c即单个字符到ch
{
line[ll] = 0;
break;
}
}while(!(ch == '/' && tempch=='*'));
break;
}
//end richard
line[ll] = ch;
ll++;
}
}
// line: 读取行缓冲区(存着源代码中的一行), 最多81个char字符(包括'\0')
ch = line[cc]; // 把当前cc指向的读取行缓冲区中的字符给ch
cc++; // cc 加1, 指向下一个位置, 以便下次读取
// 如果ll=0代表是行注释或块注释且在第一个位置,则把这个 ch也得消掉,不然会错读
if(ll==0){
ch = ' ';
cc=0;
}
return 0;
}
/*
* 词法分析,获取一个符号(词)
*/
int getsym()
{
int i,j,k;
/* the original version lacks "\r", thanks to foolevery */
while (ch==' ' || ch==10 || ch==13 || ch==9) /* 忽略空格、10:换行、13:回车(Carriage Return)和9:TAB(水平制表符) */
{
getchdo; // 抓取一个字符到ch
}
/* 名字(标识符)或保留字以a..z开头 */
if (ch>='a' && ch<='z')
{
k = 0; // 表示当前这个词已经有多少个字符了
do {
if(k<al) // al: 符号的最大长度, 为10
{
// a: 用于临时读取的一个词(符号)
a[k] = ch;
k++;
}
// else: 如果后面还有字母数字, k却已经>=al, 则读来丢(目前如此认为)
getchdo; // 再取一个字符
//printf("\n-%c ",ch);
} while (ch>='a' && ch<='z' || ch>='0' && ch<='9');
a[k] = 0; // 最后以'\0'结尾
strcpy(id, a); // 暂时copy到id中, id: 存放当前标识符
i = 0;
j = norw-1; // norw: 关键字个数, 13个
do { /* 搜索当前符号是否为保留字, 折半查找 */
k = (i+j)/2;
if (strcmp(id,word[k]) <= 0) // 当id<word[k]时,返回为负数(按ASCII值大小比较)
{
j = k - 1;
}
if (strcmp(id,word[k]) >= 0) // 当id>word[k]时,返回为正数
{
i = k + 1;
}
} while (i <= j);
// 如果i比j大至少2, 说明是保留字
if (i-1 > j)
{
// sym记录为相应保留字的标识
sym = wsym[k]; // sym, 存放当前符号的标识(数字)
}
else // 是名字
{
// sym记录为相应标识符(ident)的标识, 为ident=1
sym = ident; /* 搜索失败,是名字 */
}
}
else // 不是字母开头, 则只能是数字或符号
{
if (ch>='0' && ch<='9')
{ /* 检测是否为数字:以0..9开头 */
k = 0; // 表示这个数字已有几位数
num = 0; // 记录该数字
sym = number; // 数字的标识, 为2
do {
num = 10*num + ch - '0'; // 累加
k++;
getchdo; // 再读一个, 不是数字则退出循环
} while (ch>='0' && ch<='9'); /* 获取数字的值 */
// 继续判断是不是浮点数
if (ch=='.'){
// 进入则表示是浮点数
sym = real;
getchdo;
float temp = 0.1;
float_ = (float)num;
while(ch>='0'&& ch<='9'){
float_ = float_ + temp*(ch - '0'); //float_记录该浮点数
temp = temp*0.1;
k++;
getchdo;
}
}
// k--; 删掉,不合理
if (k > nmax) // nmax: number的最大位数, 为14
{
error(30); // 错误编码为30, 数字过长!!!
}
}
else // 是符号, 可能是单字符, 也可能是双字符
{
if (ch == ':') /* 检测赋值符号 */
{
getchdo; // 读取一个字符
if (ch == '=') // 如果是=, 说明整个是 := 赋值
{
sym = becomes; // 赋值符号的标识, 为19
getchdo; // 再读一个, 下次直接检测, 和上面的同步
}
else
{
sym = colon; /* 为冒号 */
}
}
else // 非赋值符号
{
if (ch == '<') /* 检测小于或小于等于符号 */
{
getchdo;
if (ch == '=')
{
sym = leq; // 小于等于符号标识为11
getchdo; // 再读一个, 下次直接检测, 和上面的同步
}
else
{
sym = lss; // 小于符号标识为10
}
}
else // 不是小于开头的符号
{
if (ch=='>') /* 检测大于或大于等于符号 */
{
getchdo;
if (ch == '=')
{
sym = geq; // 大于等于符号标识为13
getchdo; // 再读一个, 下次直接检测, 和上面的同步
}
else
{
sym = gtr; // 大于, 标识为12
}
}
else
{
// 检查字符串
if (ch=='"')
{
for(k=0 ; k<strmax ; k++){
str_[k] = '\0';
}
k=0;
sym = str;
getchdo;
while(ch!='"')
{
str_[k] = ch;
k++;
getchdo;
if(k>(strmax-1)){
break;
}
}
getchdo;
if(k>(strmax-1)){
error(37); /* 字符串过长 */
}
}
else
{
if(ch == '+') /* 检测+/+=符号 */
{
getchdo;
if (ch == '=')
{
sym = pluseq; // +=符号标识为42
getchdo; // 再读一个, 下次直接检测, 和上面的同步
}
else
{
sym = plus; // +, 标识为3
}
}
else
{
if(ch=='-') /* 检测-/-=符号 */
{
getchdo;
if (ch == '=')
{
sym = minuseq; // -=符号标识为43
getchdo; // 再读一个, 下次直接检测, 和上面的同步
}
else
{
sym = minus; // -, 标识为4
}
}
else // 剩下的只能是单字符
{
// 如果初始化时有该单字符, 则赋值对应标识, 否则为初始化时的nul标识(0,即unknown)
sym = ssym[ch]; /* 当符号不满足上述条件时,全部按照单字符符号处理 */
//getchdo;
//richard
if (sym != period) // 只要不是整个程序的最后一个结束符(.),则都得再读一个以供下次使用
{
getchdo;
}
//end richard
}
}
}
}
}
}
}
}
return 0;
}
/*
* 生成虚拟机代码
*
* x: instruction.f;
* y: instruction.l;
* z: instruction.a;
*/
int gen(enum fct x, int y, int z , float real)
{
if (cx >= cxmax)
{
printf("Program too long"); /* 程序过长 */
return -1;
}
code[cx].f = x;
code[cx].l = y;
code[cx].a = z;
code[cx].real = real;
// if ((int)(real*10)%10==0)
// printf("%d", code[cx].real);
// else{
// printf("代码:%d", code[cx].f);
// printf("%f--", code[cx].real);
// }
cx++;
return 0;
}
/*
* 测试当前符号是否合法
*
* 在某一部分(如一条语句,一个表达式)将要结束时时我们希望下一个符号属于某集?
* (该部分的后跟符号),test负责这项检测,并且负责当检测不通过时的补救措施,
* 程序在需要检测时指定当前需要的符号集合和补救用的集合(如之前未完成部分的后跟
* 符号),以及检测不通过时的错误号。
*
* s1: 我们需要的符号
* s2: 如果不是我们需要的,则需要一个补救用的集?
* n: 错误号
*/
int test(bool* s1, bool* s2, int n)
{
if (!inset(sym, s1))
{
error(n);
/* 当检测不通过时,不停获取符号,直到它属于需要的集合或补救的集合 */
// 就是排除所有不属于FIRST和FOLLOW的符号
while ((!inset(sym,s1)) && (!inset(sym,s2)))
{
getsymdo;
}
// *************************这里要加东西,即检测属于FIRST还是FOLLOW
// 如果是FOLLOW则该句当作结束,getsymdo读取下一个,开始新的分析
// 如果是FIRST,则不用再读,直接开始新一轮的分析
}
return 0;
}
/*
* 编译程序主?
*
* lev: 当前分程序所在层
* tx: 名字表当前尾指针
* fsys: 从main进来的block是:3种声明 + 6种语句的开始符号 + .结束符(是当前模块的FIRST集合)
* param_num: 传参参数个数
*/
int block(int lev, int tx, bool* fsys, int thisParamNum)
{
int i;
int dx; /* 名字分配到的相对地址 */
int tx0; /* 保留初始tx, tx是名字表中最后一个名字的指针, 不是空位的指针 */
// 此外 cx是虚拟机代码指针,指向的是空位置
bool nxtlev[symnum]; /* 下级函数的FIRST集合 */
dx = 3 + thisParamNum;
tx0 = tx; /* 记录本层名字的初始位置 */
// table这个位置不会存放其他名字,就是main或者procedure,先用于存放jmp在code里的位置,等声明分析完了后,才好改;然后再改成inte的位置
table[tx-thisParamNum].adr = cx;
// 首先就生成无条件跳转jmp 0 0, 为了跳到主函数虚拟机代码处,后面会改
gendo(jmp, 0, 0, 0);
if (lev > levmax)
{
error(32);
}
do {
if (sym == constsym) /* 收到常量声明符号,开始处理常量声明 */
{
getsymdo;
/* the original do...while(sym == ident) is problematic, thanks to calculous */
/* do { */
constdeclarationdo(&tx, lev, &dx); /* dx的值会被constdeclaration改变,使用指针 */
while (sym == comma)
{
getsymdo;
constdeclarationdo(&tx, lev, &dx);
}
if (sym == semicolon)
{
getsymdo;
}
else
{
error(5); /*漏掉了逗号或者分号*/
}
/* } while (sym == ident); */
}
if (sym == varsym) /* 收到变量声明符号,开始处理变量声明 */
{
getsymdo;
/* the original do...while(sym == ident) is problematic, thanks to calculous */
/* do { */
int tx_cp = tx+1; // 用于遍历每个var
int dx_cp = dx; // 用于array里addr的再赋值
vardeclarationdo(&tx, lev, &dx);
while (sym == comma)
{
getsymdo;
vardeclarationdo(&tx, lev, &dx);
}
// 判断有无real浮点标志
if (sym == colon)
{
getsymdo;
if (sym == realsym)
{
for(i=tx_cp ; i<=tx ; i++){
table[i].type = 'f';
}
getsymdo;
}
else
{
error(36); // 未知变量类型
}
}
// 如果没有指明,则默认是integer
else
{
for(i=tx_cp ; i<=tx ; i++){
table[i].type = 'i';
}
}
if (sym == lbrakt) /* 标识符之后是'[', 则识别为数组 */
{
getsymdo;
// 这个函数里找出开辟的空间大小
arraydeclarationdo(&tx, lev, &dx);
// 全部修改成array类型, 记录array的size, 调整运行栈里开辟的空间
for(i=tx_cp ; i<=tx ; i++){
table[i].kind = array;
table[i].size = arrSize;
table[i].adr = dx_cp;
dx_cp += table[i].size;
dx += (table[i].size - 1);
}
}
if (sym == semicolon)
{
getsymdo;
}
else
{
error(5); // 漏掉了分号
}
/* } while (sym == ident); */
}
while (sym == procsym) /* 收到过程声明符号,开始处理过程声明 */
{
getsymdo;
if (sym == ident)
{
enter(procedur, &tx, lev, &dx); /* 记录过程名字 */
getsymdo;
}
else
{
error(4); /* procedure后应为标识符 */
}
// 暂存dx
save_dx = dx;
// dx变成3, 使得传参变量都从过程中dx=3的位置开始记录
dx = 3;
int tx_cp = tx+1;
if (sym==lparen){
param_num = 0;
do{
getsymdo;
if(sym==ident){
vardeclarationdo(&tx, lev+1, &dx);
param_num++;
}
}while(sym == comma);
if (sym==rparen)
{
getsymdo;
}else{
error(55); // 过程传参语法错误
}
// 暂时默认为浮点数
for(i=tx_cp ; i<=tx ; i++){
table[i].type = 'f';
}
// 记录过程参数个数到val
table[tx_cp-1].val = param_num;
}
// 赋值回来
dx = save_dx;
// procedure的 标识符后应该有个 ;
if (sym == semicolon)
{
getsymdo;
}
else
{
error(5); /* 漏掉了分号 */
}
// fsys: 当前模块的FIRST集合
memcpy(nxtlev, fsys, sizeof(bool)*symnum);
// nxtlev下层的FIRST集合,继承了这一层的 3种声明 + 6种语句的开始符号 + .结束符,外加一个;
nxtlev[semicolon] = true; // ***********************这个;不应该存在于FIRST
if (-1 == block(lev+1, tx, nxtlev, param_num))
{
return -1; /* 递归调用 */
}
// tx也回归本层的
tx = tx_cp-1;