📄 pl-0语言编译程序分析.htm
字号:
if ch = '>' then (* 如果读到大于号,处理过程类似于处理小于号 *)
begin
getch; (* 再读一个字符 *)
if ch = '=' then (* 如果读到等号 *)
begin
sym := geq; (* 购成一个大于等于号 *)
getch (* 读一个字符 *)
end
else (* 如果大于号后不是跟的等号 *)
sym := gtr (* 那就是一个单独的大于号 *)
end
else(* 如果读到不是字母也不是数字也不是冒号也不是小于号也不是大于号 *)
begin (* 那就说明它不是标识符/保留字,也不是复杂的双字节操作符,应该是一个普通的符号 *)
sym := ssym[ch]; (* 直接成符号表中查到它的类型,赋给sym *)
getch (* 读下一个字符 *)
end
(* 整个if语句判断结束 *)
end (* getsym *);
(* 词法分析过程getsym总结:从源文件中读出若干有效字符,组成一个token串,识别它的类型
为保留字/标识符/数字或是其它符号。如果是保留字,把sym置成相应的保留字类型,如果是
标识符,把sym置成ident表示是标识符,于此同时,id变量中存放的即为保留字字符串或标识
符名字。如果是数字,把sym置为number,同时num变量中存放该数字的值。如果是其它的操作符,
则直接把sym置成相应类型。经过本过程后ch变量中存放的是下一个即将被识别的字符 *)
(* 目标代码生成过程gen *)
(* 参数:x:要生成的一行代码的助记符 *)
(* y, z:代码的两个操作数 *)
(* 本过程用于把生成的目标代码写入目标代码数组,供后面的解释器解释执行 *)
procedure gen(x: fct; y, z: integer);
begin
if cx > cxmax then (* 如果cx>cxmax表示当前生成的代码行号大于允许的最大代码行数 *)
begin
write('program too long'); (* 输出"程序太长",退出 *)
close(fa);
close(fa1);
close(fin);
halt(0)
{goto 99}
(* 我修改的代码,由于Turbo Pascal 7.0中不允许跨过程的goto,就只能用上面的方法退出程序了。 *)
end;
with code[cx] do (* 把代码写入目标代码数组的当前cx所指位置 *)
begin
f := x;
l := y;
a := z;
end;
cx := cx + 1 (* 移动cx指针指向下一个空位 *)
end (* gen *);
(* 测试当前单词是否合法过程test *)
(* 参数:s1:当语法分析进入或退出某一语法单元时当前单词符合应属于的集合 *)
(* s2:在某一出错状态下,可恢复语法分析正常工作的补充单词集合 *)
(* n:出错信息编号,当当前符号不属于合法的s1集合时发出的出错信息 *)
procedure test(s1, s2: symset; n: integer);
begin
if not (sym in s1) then (* 如果当前符号不在s1中 *)
begin
error(n); (* 发出n号错误 *)
s1 := s1 + s2; (* 把s2集合补充进s1集合 *)
while not (sym in s1) do (* 通过循环找到下一个合法的符号,以恢复语法分析工作 *)
getsym
end
end (* test *);
(* 语法分析过程block *)
(* 参数:lev:这一次语法分析所在的层次 *)
(* tx:符号表指针 *)
(* fsys:用于出错恢复的单词集合 *)
procedure block(lev, tx: integer; fsys: symset);
var
dx: integer; (* data allocation index *) (* 数据段内存分配指针,指向下一个被分配空间在数据段中的偏移位置 *)
tx0: integer; (* initial table index *) (* 记录本层开始时符号表位置 *)
cx0: integer; (* initial code index *) (* 记录本层开始时代码段分配位置 *)
(* 登陆符号表过程enter *)
(* 参数:k:欲登陆到符号表的符号类型 *)
procedure enter(k: object1);
begin (* enter object into table *)
tx := tx + 1; (* 符号表指针指向一个新的空位 *)
with table[tx] do (* 开始登录 *)
begin
name := id; (* name是符号的名字,对于标识符,这里就是标识符的名字 *)
kind := k; (* 符号类型,可能是常量、变量或过程名 *)
case k of (* 根据不同的类型进行不同的操作 *)
constant: (* 如果是常量名 *)
begin
if num > amax then (* 在常量的数值大于允许的最大值的情况下 *)
begin
error(31); (* 抛出31号错误 *)
num := 0; (* 实际登陆的数字以0代替 *)
end;
val := num (* 如是合法的数值,就登陆到符号表 *)
end;
variable: (* 如果是变量名 *)
begin
level := lev; (* 记下它所属的层次号 *)
adr := dx; (* 记下它在当前层中的偏移量 *)
dx := dx+1; (* 偏移量自增一,为下一次做好准备 *)
end;
procedur: (* 如果要登陆的是过程名 *)
level := lev (* 记录下这个过程所在层次 *)
end
end
end (* enter *);
(* 登录符号过程没有考虑到重复的定义的问题。如果出现重复定义,则以最后一次的定义为准。 *)
(* 在符号表中查找指定符号所在位置的函数position *)
(* 参数:id:要找的符号 *)
(* 返回值:要找的符号在符号表中的位置,如果找不到就返回0 *)
function position (id: alfa): integer;
var
i: integer;
begin (* find identifier in table *)
table[0].name := id; (* 先把id放入符号表0号位置 *)
i := tx; (* 从符号表中当前位置也即最后一个符号开始找 *)
while table[i].name <> id do (* 如果当前的符号与要找的不一致 *)
i := i - 1; (* 找前面一个 *)
position := i (* 返回找到的位置号,如果没找到则一定正好为0 *)
end(* position *);
(* 常量声明处理过程constdeclaration *)
procedure constdeclaration;
begin
if sym = ident then (* 常量声明过程开始遇到的第一个符号必然应为标识符 *)
begin
getsym; (* 获取下一个token *)
if sym in [eql, becomes] then (* 如果是等号或赋值号 *)
begin
if sym = becomes then (* 如果是赋值号(常量生明中应该是等号) *)
error(1); (* 抛出1号错误 *)
(* 这里其实自动进行了错误纠正使编译继续进行,把赋值号当作等号处理 *)
getsym; (* 获取下一个token,等号或赋值号后应接上数字 *)
if sym = number then (* 如果的确是数字 *)
begin
enter(constant); (* 把这个常量登陆到符号表 *)
getsym (* 获取下一个token,为后面作准备 *)
end
else
error(2) (* 如果等号后接的不是数字,抛出2号错误 *)
end
else
error(3) (* 如果常量标识符后接的不是等号或赋值号,抛出3号错误 *)
end
else
error(4) (* 如果常量声明过程遇到的第一个符号不为标识符,抛出4号错误 *)
end(* constdeclaration *);
(* 变量声明过程vardeclaration *)
procedure vardeclaration;
begin
if sym = ident then (* 变量声明过程开始遇到的第一个符号必然应为标识符 *)
begin
enter(variable); (* 将标识符登陆到符号表中 *)
getsym (* 获取下一个token,为后面作准备 *)
end
else
error(4) (* 如果变量声明过程遇到的第一个符号不是标识符,抛出4号错误 *)
end(* vardeclaration *);
(* 列出当前一层类PCODE目标代码过程listcode *)
procedure listcode;
var
i: integer;
begin (* list code generated for this block *)
if listswitch then (* 如果用户选择是要列出代码的情况下才列出代码 *)
begin
for i := cx0 to cx - 1 do (* 从当前层代码开始位置到当前代码位置-1处,即为本分程序块 *)
with code[i] do
begin
writeln(i: 4, mnemonic[f]: 5, l: 3, a: 5); (* 显示出第i行代码的助记符和L与A操作数 *)
(* 我修改的代码:原程序此处在输出i时,没有指定占4个字符宽度,不美观也与下面一句不配套。 *)
writeln(fa, i: 4, mnemonic[f]: 5, l: 3, a: 5) (* 同时把屏显打印到文件 *)
end;
end
end(* listcode *);
(* 语句处理过程statement *)
(* 参数说明:fsys: 如果出错可用来恢复语法分析的符号集合 *)
procedure statement(fsys: symset);
var
i, cx1, cx2: integer;
(* 表达式处理过程expression *)
(* 参数说明:fsys: 如果出错可用来恢复语法分析的符号集合 *)
procedure expression(fsys: symset);
var
addop: symbol;
(* 项处理过程term *)
(* 参数说明:fsys: 如果出错可用来恢复语法分析的符号集合 *)
procedure term(fsys: symset);
var
mulop: symbol;
(* 因子处理过程factor *)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -