代码及结果:task1
任务二博客:编译原理 | 实验任务二:扩展Read和Write语句
1 找到PASCAL编译系统
下载安装free pascal,设置环境变量,然后用vscode编辑。
参考资料:在windows命令行下编译运行Pascal程序
2 在PASCAL系统上运行PL0编译程序,需要对PL0编译程序作一些修改、调试
1.改格式(缩进、注释)
2.符号修改
~ -> not//~这个符号打不出来,反正就是非的意思
//procedure和object在pascal中是关键字,所以分别使用proceduree和objectt替代
object -> objectt;
procedure -> proceduree;
cc—1 -> cc-1;
↑ -> ^;
≤ -> <=;
≠ -> <>;
3. 注释掉不必要的(其实是报错的)语句,如
直接注释掉(或删掉)
4.标号99原来是用于退出程序的,由于不支持跨过程的跳转,所以直接退出
//procedure gen(x : fct; y, z : integer);
if cx > cxmax then
begin
write('PROGRAM TOO LONG');
exit
//goto 99
end;
//procedure getch ;
if eof(fin) then {如果已到文件尾}
begin
writeln('PROGRAM INCOMPLETE');
exit;
//goto 99
end;
//主程序
//99 : writeln
writeln;
5.现在暂时没有错误了
3 建立输入文件和输出文件
在输入文件中存放PL0源程序
在输出文件中存放PL0源程序被编译后产生的中间代码和运行数据
4 通过输入文件输入PL0源程序,在输出文件中产生源程序的中间代码, 然后运行该中间代码, 在输出文件中产生运行数据
1.增加变量fin和fout,以命令行形式读取和写入文件
//{全局变量定义}
//var
fin : text; {源代码文件}
fout : text; {输出文件}
...
begin {主程序}
assign(fin,paramstr(1));
assign(fout,paramstr(2)); {将命令行参数str变量赋值给文件变量}
reset(fin);
rewrite(fout); {打开输入输出文件}
...
//99 : writeln
writeln;
close(fin);
close(fout);
2.exit时关闭文件
//getch
begin
writeln('PROGRAM INCOMPLETE');
close(fin);
close(fout);
exit;
//goto 99
end;
//gen
begin
write('PROGRAM TOO LONG');
close(fin);
close(fout);
exit
//goto 99
end;
3.将所有input改成fin,有read的地方都改成从fin文件读入,有write的地方都改成写到fout文件:
//getch
if eof(fin) then {如果已到文件尾}
...
while not eoln(fin) do {如果不是行末}
begin
ll := ll + 1; {将行缓冲区的长度+1}
read(fin, ch); {从源文件中读取一个字符到ch中}
write(fout, ch);{输出ch到输出文件中}
line[ll] := ch {把这个字符放到当前行末尾}
end;
writeln(fout); {换行}
readln(fin);{从源文件下一行开始读取}
ll := ll + 1; {将行缓冲区的长度+1}
line[ll] := ' ' { process end-line } {行数组最后一个元素为空格}
end;
//gen
if cx > cxmax then
begin
write(fout, 'PROGRAM TOO LONG');
close(fin);
close(fout);
exit
//goto 99
end;
//listcode
with code[i] do {打印第i条代码}
{i: 代码序号; mnemonic[f]: 功能码的字符串; l: 相对层号(层差); a: 相对地址或运算号码}
{格式化输出}
writeln(fout, i:5, mnemonic[f]:7, l:3, a:5)
//interpret
begin
writeln(fout, 'START PL/0');
...
sto :
begin {当前指令是保存变量值(sto, l, a)指令}
s[base(l) + a] := s[t];
writeln(fout, s[t] : 4);
{根据静态链SL,将栈顶的值存入层差为l,相对地址为a的变量中}
t := t-1 {栈顶指针减1}
end;
...
write(fout, 'END PL/0');
end {interpret};
4.在getsym词法分析程序中增加对">","<",">=","<=","<>"记号的识别
{处理'<'}
else if ch = '<' then
begin
getch;
{'<='}
if ch = '=' then
begin
sym := leq; {表示小于等于}
getch {读下一个字符}
end
{'<>'}
else if ch = '>' then
begin
sym := neq; {表示不等于}
getch
end
{'<'}
else sym := lss {表示小于}
end
{处理'<'}
else if ch = '>' then
begin
getch;
{'>='}
if ch = '=' then
begin
sym := geq; {表示大于等于}
getch {读下一个字符}
end
{'>'}
else sym := gtr {表示大于}
end
5.保留字表改为小写字母,所有字符都预留的相同的长度
word[1] := 'begin ';
word[2] := 'call ';
word[3] := 'const ';
word[4] := 'do ';
word[5] := 'end ';
word[6] := 'if ';
word[7] := 'odd ';
word[8] := 'procedure ';
word[9] := 'then ';
word[10] := 'var ';
word[11] := 'while ';
getsym里面的‘A’…'Z’也改成‘a’…‘z’:
{标识符或保留字}
if ch in ['a'..'z'] then
begin
k := 0;
repeat {处理字母开头的字母﹑数字串}
if k < al then
begin
k:= k + 1;
a[k] := ch
end;
getch
until not (ch in ['a'..'z', '0'..'9']);
6.第一步基本完成:
7.输出结果,包括:
- 输入pl0源文件的字符
- 生成的中间代码(栈机器代码)
- 数据栈顶的运行结果(START PL/0和END PL/0之间的interpret,其中sto指令会输出栈顶数据)
0 const m = 7, n = 85;
1
1 var x, y, z, q, r;
1
1 procedure multiply;
1 var a, b;
2 begin
3 a := x;
5 b := y;
7 z := 0;
9 while b > 0 do
13 begin
13 if odd b then
15 z := z + a;
20 a := 2*a ;
24 b := b/2 ;
28 end
28 end;
2 INT 0 5
3 LOD 1 3
4 STO 0 3
5 LOD 1 4
6 STO 0 4
7 LIT 0 48
8 STO 1 5
9 LOD 0 4
10 LIT 0 48
11 OPR 0 12
12 JPC 0 29
13 LOD 0 4
14 OPR 0 6
15 JPC 0 20
16 LOD 1 5
17 LOD 0 3
18 OPR 0 2
19 STO 1 5
20 LIT 0 50
21 LOD 0 3
22 OPR 0 4
23 STO 0 3
24 LOD 0 4
25 LIT 0 50
26 OPR 0 5
27 STO 0 4
28 JMP 0 9
29 OPR 0 0
30
30 procedure divide;
30 var w;
31 begin
32 r := x;
34 q := 0;
36 w := y;
38 while w <= r do w := 2*w ;
47 while w > y do
51 begin
51 q := 2*q;
55 w := w/2;
59 if w <= r then
62 begin
63 r := r-w;
67 q := q+1
69 end
71 end
71 end;
31 INT 0 4
32 LOD 1 3
33 STO 1 7
34 LIT 0 48
35 STO 1 6
36 LOD 1 4
37 STO 0 3
38 LOD 0 3
39 LOD 1 7
40 OPR 0 13
41 JPC 0 47
42 LIT 0 50
43 LOD 0 3
44 OPR 0 4
45 STO 0 3
46 JMP 0 38
47 LOD 0 3
48 LOD 1 4
49 OPR 0 12
50 JPC 0 72
51 LIT 0 50
52 LOD 1 6
53 OPR 0 4
54 STO 1 6
55 LOD 0 3
56 LIT 0 50
57 OPR 0 5
58 STO 0 3
59 LOD 0 3
60 LOD 1 7
61 OPR 0 13
62 JPC 0 71
63 LOD 1 7
64 LOD 0 3
65 OPR 0 3
66 STO 1 7
67 LOD 1 6
68 LIT 0 49
69 OPR 0 2
70 STO 1 6
71 JMP 0 47
72 OPR 0 0
73
73 procedure gcd;
73 var f, g ;
74 begin
75 f := x;
77 g := y;
79 while f <> g do
83 begin
83 if f < g then g := g-f;
91 if g < f then f := f-g;
99 end;
100 z := f
101 end;
74 INT 0 5
75 LOD 1 3
76 STO 0 3
77 LOD 1 4
78 STO 0 4
79 LOD 0 3
80 LOD 0 4
81 OPR 0 9
82 JPC 0 100
83 LOD 0 3
84 LOD 0 4
85 OPR 0 10
86 JPC 0 91
87 LOD 0 4
88 LOD 0 3
89 OPR 0 3
90 STO 0 4
91 LOD 0 4
92 LOD 0 3
93 OPR 0 10
94 JPC 0 99
95 LOD 0 3
96 LOD 0 4
97 OPR 0 3
98 STO 0 3
99 JMP 0 79
100 LOD 0 3
101 STO 1 5
102 OPR 0 0
103
103 begin
104 x := m;
106 y := n;
108 call multiply;
109 x := 25;
111 y:= 3;
113 call divide;
114 x := 84;
116 y := 36;
118 call gcd;
119 end.
103 INT 0 8
104 LIT 0 55
105 STO 0 3
106 LIT 0 613
107 STO 0 4
108 CAL 0 2
109 LIT 0 553
110 STO 0 3
111 LIT 0 51
112 STO 0 4
113 CAL 0 31
114 LIT 0 612
115 STO 0 3
116 LIT 0 564
117 STO 0 4
118 CAL 0 74
119 OPR 0 0
START PL/0
55
613
55
613
48
103
2750
12
553
51
553
48
51
2550
2400
51
502
2449
612
564
612
564
48
516
468
420
372
324
276
228
180
132
84
36
12
24
12
12
END PL/0
8.emmm??发现这个结果不对,LIT的第二项怎么那么大呢?找了好久,发现是getsym判断数字里面写错了:
应该把ord(0)改成ord(‘0’):
//getsym
else if ch in ['0'..'9'] then
begin
k := 0;
num := 0;
sym := number; {当前记号sym为数字}
repeat {计算数字串的值}
num := 10*num + (ord(ch)-ord('0'));
{ord(ch)和ord(0)是ch和0在ASCII码中的序号}
k := k + 1;
getch;
until not(ch in ['0'..'9']);
{当前数字串的长度超过上界,则报告错误}
if k > nmax then error(30)
end