📄 pl-0语言编译程序分析.htm
字号:
(* 参数说明:fsys: 如果出错可用来恢复语法分析的符号集合 *)
procedure factor(fsys: symset);
var
i: integer;
begin
test(facbegsys, fsys, 24); (* 开始因子处理前,先检查当前token是否在facbegsys集合中。 *)
(* 如果不是合法的token,抛24号错误,并通过fsys集恢复使语法处理可以继续进行 *)
while sym in facbegsys do (* 循环处理因子 *)
begin
if sym = ident then (* 如果遇到的是标识符 *)
begin
i := position(id); (* 查符号表,找到当前标识符在符号表中的位置 *)
if i = 0 then (* 如果查符号表返回为0,表示没有找到标识符 *)
error(11) (* 抛出11号错误 *)
else
with table[i] do (* 如果在符号表中找到了当前标识符的位置,开始生成相应代码 *)
case kind of
constant: gen(lit, 0, val); (* 如果这个标识符对应的是常量,值为val,生成lit指令,把val放到栈顶 *)
variable: gen(lod, lev - level, adr); (* 如果标识符是变量名,生成lod指令, *)
(* 把位于距离当前层level的层的偏移地址为adr的变量放到栈顶 *)
procedur: error(21) (* 如果在因子处理中遇到的标识符是过程名,出错了,抛21号错 *)
end;
getsym (* 获取下一token,继续循环处理 *)
end
else
if sym = number then (* 如果因子处理时遇到数字 *)
begin
if num > amax then (* 如果数字的大小超过允许最大值amax *)
begin
error(31); (* 抛出31号错 *)
num := 0 (* 把数字按0值处理 *)
end;
gen(lit, 0, num); (* 生成lit指令,把这个数值字面常量放到栈顶 *)
getsym (* 获取下一token *)
end
else
if sym = lparen then (* 如果遇到的是左括号 *)
begin
getsym; (* 获取一个token *)
expression( [rparen] + fsys ); (* 递归调用expression子程序分析一个子表达式 *)
if sym = rparen then (* 子表达式分析完后,应遇到右括号 *)
getsym (* 如果的确遇到右括号,读取下一个token *)
else
error(22) (* 否则抛出22号错误 *)
end;
test(fsys, facbegsys, 23) (* 一个因子处理完毕,遇到的token应在fsys集合中 *)
(* 如果不是,抛23号错,并找到下一个因子的开始,使语法分析可以继续运行下去 *)
end
end(* factor *);
begin (* term *)
factor([times, slash] + fsys); (* 每一个项都应该由因子开始,因此调用factor子程序分析因子 *)
while sym in [times, slash] do (* 一个因子后应当遇到乘号或除号 *)
begin
mulop := sym; (* 保存当前运算符 *)
getsym; (* 获取下一个token *)
factor(fsys + [times, slash]); (* 运算符后应是一个因子,故调factor子程序分析因子 *)
if mulop = times then (* 如果刚才遇到乘号 *)
gen(opr, 0, 4) (* 生成乘法指令 *)
else
gen(opr, 0, 5) (* 不是乘号一定是除号,生成除法指令 *)
end
end (* term *);
begin (* expression *)
if sym in [plus, minus] then (* 一个表达式可能会由加号或减号开始,表示正负号 *)
begin
addop := sym; (* 把当前的正号或负号保存起来,以便下面生成相应代码 *)
getsym; (* 获取一个token *)
term(fsys + [plus, minus]); (* 正负号后面应该是一个项,调term子程序分析 *)
if addop = minus then (* 如果保存下来的符号是负号 *)
gen(opr, 0, 1) (* 生成一条1号操作指令:取反运算 *)
(* 如果不是负号就是正号,不需生成相应的指令 *)
end
else (* 如果不是由正负号开头,就应是一个项开头 *)
term(fsys + [plus, minus]); (* 调用term子程序分析项 *)
while sym in [plus, minus] do (* 项后应是加运算或减运算 *)
begin
addop := sym; (* 把运算符保存下来 *)
getsym; (* 获取下一个token,加减运算符后应跟的是一个项 *)
term(fsys + [plus, minus]); (* 调term子程序分析项 *)
if addop = plus then (* 如果项与项之间的运算符是加号 *)
gen(opr, 0, 2) (* 生成2号操作指令:加法 *)
else (* 否则是减法 *)
gen(opr, 0, 3) (* 生成3号操作指令:减法 *)
end
end (* expression *);
(* 条件处理过程condition *)
(* 参数说明:fsys: 如果出错可用来恢复语法分析的符号集合 *)
procedure condition(fsys: symset);
var
relop: symbol; (* 用于临时记录token(这里一定是一个二元逻辑运算符)的内容 *)
begin
if sym = oddsym then (* 如果是odd运算符(一元) *)
begin
getsym; (* 获取下一个token *)
expression(fsys); (* 对odd的表达式进行处理计算 *)
gen(opr, 0, 6); (* 生成6号操作指令:奇偶判断运算 *)
end
else (* 如果不是odd运算符(那就一定是二元逻辑运算符) *)
begin
expression([eql, neq, lss, leq, gtr, geq] + fsys); (* 对表达式左部进行处理计算 *)
if not (sym in [eql, neq, lss, leq, gtr, geq]) then (* 如果token不是逻辑运算符中的一个 *)
error(20) (* 抛出20号错误 *)
else
begin
relop := sym; (* 记录下当前的逻辑运算符 *)
getsym; (* 获取下一个token *)
expression(fsys); (* 对表达式右部进行处理计算 *)
case relop of (* 如果刚才的运算符是下面的一种 *)
eql: gen(opr, 0, 8); (* 等号:产生8号判等指令 *)
neq: gen(opr, 0, 9); (* 不等号:产生9号判不等指令 *)
lss: gen(opr, 0, 10); (* 小于号:产生10号判小指令 *)
geq: gen(opr, 0, 11); (* 大于等号号:产生11号判不小于指令 *)
gtr: gen(opr, 0, 12); (* 大于号:产生12号判大于指令 *)
leq: gen(opr, 0, 13); (* 小于等于号:产生13号判不大于指令 *)
end
end
end
end (* condition *);
begin (* statement *)
if sym = ident then (* 所谓"语句"可能是赋值语句,以标识符开头 *)
begin
i := position(id); (* 在符号表中查到该标识符所在位置 *)
if i = 0 then (* 如果没找到 *)
error(11) (* 抛出11号错误 *)
else
if table[i].kind <> variable then (* 如果在符号表中找到该标识符,但该标识符不是变量名 *)
begin
error(12); (* 抛出12号错误 *)
i := 0 (* i置0作为错误标志 *)
end;
getsym; (* 获得下一个token,正常应为赋值号 *)
if sym = becomes then (* 如果的确为赋值号 *)
getsym (* 获取下一个token,正常应为一个表达式 *)
else
error(13); (* 如果赋值语句的左部标识符号后所接不是赋值号,抛出13号错误 *)
expression(fsys); (* 处理表达式 *)
if i <> 0 then (* 如果不曾出错,i将不为0,i所指为当前语名左部标识符在符号表中的位置 *)
with table[i] do
gen(sto, lev - level, adr) (* 产生一行把表达式值写往指定内存的sto目标代码 *)
end
else
if sym = readsym then (* 如果不是赋值语句,而是遇到了read语句 *)
begin
getsym; (* 获得下一token,正常情况下应为左括号 *)
if sym <> lparen then (* 如果read语句后跟的不是左括号 *)
error(34) (* 抛出34号错误 *)
else
repeat (* 循环得到read语句括号中的参数表,依次产生相应的“从键盘读入”目标代码 *)
getsym; (* 获得一个token,正常应是一个变量名 *)
if sym = ident then (* 如果确为一个标识符 *)
(* 这里略有问题,还应判断一下这个标识符是不是变量名,如果是常量名或过程名应出错 *)
i := position(id) (* 查符号表,找到它所在位置给i,找不到时i会为0 *)
else
i := 0; (* 不是标识符则有问题,i置0作为出错标志 *)
if i = 0 then (* 如果有错误 *)
error(35) (* 抛出35号错误 *)
else (* 否则生成相应的目标代码 *)
with table[i] do
begin
gen(opr, 0, 16); (* 生成16号操作指令:从键盘读入数字 *)
gen(sto, lev - level, adr) (* 生成sto指令,把读入的值存入指定变量所在的空间 *)
end;
getsym (* 获取下一个token,如果是逗号,则read语还没完,否则应当是右括号 *)
until sym <> comma; (* 不断生成代码直到read语句的参数表中的变量遍历完为止,这里遇到不是逗号,应为右括号 *)
if sym <> rparen then (* 如果不是我们预想中的右括号 *)
begin
error(33); (* 抛出33号错误 *)
while not (sym in fsys) do (* 依靠fsys集,找到下一个合法的token,恢复语法分析 *)
getsym
end
else
getsym (* 如果read语句正常结束,得到下一个token,一般为分号或end *)
end
else
if sym = writesym then (* 如果遇到了write语句 *)
begin
getsym; (* 获取下一token,应为左括号 *)
if sym = lparen then (* 如确为左括号 *)
begin
repeat (* 依次获取括号中的每一个值,进行输出 *)
getsym; (* 获得一个token,这里应是一个标识符 *)
expression([rparen, comma] + fsys); (* 调用expression过程分析
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -