pio2.txt

来自「PL/0语言是Pascal语言的一个子集」· 文本 代码 · 共 1,001 行 · 第 1/5 页

TXT
1,001
字号
     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过程分析表达式,用于出错恢复的集合中加上右括号和逗号 *)
             gen(opr, 0, 14) (* 生成14号指令:向屏幕输出 *)
           until sym <> comma; (* 循环直到遇到的不再是逗号,这时应是右括号 *)
           if sym <> rparen then (* 如果不是右括号 *)
             error(33) (* 抛出33号错误 *)
           else
             getsym (* 正常情况下要获取下一个token,为后面准备好 *)
         end;
         gen(opr, 0, 15) (* 生成一个15号操作的目标代码,功能是输出一个换行 *)
         (* 由此可知PL/0中的write语句与Pascal中的writeln语句类似,是带有输出换行的 *)
       end
       else
         if sym = callsym then (* 如果是call语句 *)
         begin
           getsym; (* 获取token,应是过程名型标识符 *)
           if sym <> ident then (* 如果call后跟的不是标识符 *)
             error(14) (* 抛出14号错误 *)
           else
           begin
             i := position(id); (* 从符号表中找出相应的标识符 *)
             if i = 0 then (* 如果没找到 *)
               error(11) (* 抛出11号错误 *)
             else
               with table[i] do (* 如果找到标识符位于符号表第i位置 *)
                 if kind = procedur then (* 如果这个标识符为一个过程名 *)
                   gen(cal,lev-level,adr) (* 生成cal目标代码,呼叫这个过程 *)
                 else
                   error(15); (* 如果call后跟的不是过程名,抛出15号错误 *)
             getsym (* 获取下一token,为后面作准备 *)
           end
         end
       else
         if sym = ifsym then (* 如果是if语句 *)
         begin
           getsym; (* 获取一token应是一个逻辑表达式 *)
           condition([thensym, dosym] + fsys); (* 对逻辑表达式进行分析计算,出错恢复集中加入then和do语句 *)
           if sym = thensym then (* 表达式后应遇到then语句 *)
             getsym (* 获取then后的token,应是一语句 *)
           else
             error(16); (* 如果if后没有then,抛出16号错误 *)
           cx1 := cx; (* 记下当前代码分配指针位置 *)
           gen(jpc, 0, 0); (* 生成条件跳转指令,跳转位置暂时填0,分析完语句后再填写 *)
           statement(fsys); (* 分析then后的语句 *)
           code[cx1].a:=cx (* 上一行指令(cx1所指的)的跳转位置应为当前cx所指位置 *)
         end
         else
           if sym = beginsym then (* 如果遇到begin *)
           begin
             getsym; (* 获取下一个token *)
             statement([semicolon, endsym] + fsys);(* 对begin与end之间的语句进行分析处理 *)
             while sym in [semicolon] + statbegsys do (* 如果分析完一句后遇到分号或语句开始符循环分析下一句语句 *)
             begin
               if sym = semicolon then (* 如果语句是分号(可能是空语句) *)
                 getsym (* 获取下一token继续分析 *)
               else
                 error(10); (* 如果语句与语句间没有分号,出10号错 *)
               statement([semicolon, endsym] + fsys) (* 分析一个语句 *)
             end;
             if sym = endsym then (* 如果语句全分析完了,应该遇到end *)
               getsym (* 的确是end,读下一token *)
             else
               error(17) (* 如果不是end,抛出17号错 *)
           end
           else
             if sym = whilesym then (* 如果遇到while语句 *)
             begin
               cx1 := cx; (* 记下当前代码分配位置,这是while循环的开始位置 *)
               getsym; (* 获取下一token,应为一逻辑表达式 *)
               condition([dosym] + fsys); (* 对这个逻辑表达式进行分析计算 *)
               cx2 := cx; (* 记下当前代码分配位置,这是while的do中的语句的开始位置 *)
               gen(jpc, 0, 0); (* 生成条件跳转指令,跳转位置暂时填0 *)
               if sym = dosym then (* 逻辑表达式后应为do语句 *)
                 getsym (* 获取下一token *)
               else
                 error(18); (* if后缺少then,抛出18号错误 *)
               statement(fsys); (* 分析do后的语句块 *)
               gen(jmp, 0, cx1); (* 循环跳转到cx1位置,即再次进行逻辑判断 *)
               code[cx2].a := cx (* 把刚才填0的跳转位置改成当前位置,完成while语句的处理 *)
             end;
   test(fsys, [], 19) (* 至此一个语句处理完成,一定会遇到fsys集中的符号,如果没有遇到,就抛19号错 *)
 end(* statement *);
begin (* block *)
 dx := 3; (* 地址指示器给出每层局部量当前已分配到的相对位置。
             置初始值为3的原因是:每一层最开始的位置有三个空间用于存放静态链SL、动态链DL和返回地址RA *)
 tx0 := tx; (* 初始符号表指针指向当前层的符号在符号表中的开始位置 *)
 table[tx].adr := cx; (* 符号表当前位置记下当前层代码的开始位置 *)
 gen(jmp, 0, 0); (* 产生一行跳转指令,跳转位置暂时未知填0 *)
 if lev > levmax then (* 如果当前过程嵌套层数大于最大允许的套层数 *)
   error(32); (* 发出32号错误 *)
 repeat (* 开始循环处理源程序中所有的声明部分 *)
   if sym = constsym then (* 如果当前token是const保留字,开始进行常量声明 *)
   begin
     getsym; (* 获取下一个token,正常应为用作常量名的标识符 *)
     repeat (* 反复进行常量声明 *)
       constdeclaration; (* 声明以当前token为标识符的常量 *)
       while sym = comma do (* 如果遇到了逗号则反复声明下一个常量 *)
       begin
         getsym; (* 获取下一个token,这里正好应该是标识符 *)
         constdeclaration (* 声明以当前token为标识符的常量 *)
       end;
       if sym = semicolon then (* 如果常量声明结束,应遇到分号 *)
         getsym (* 获取下一个token,为下一轮循环做好准备 *)
       else
         error(5) (* 如果常量声明语句结束后没有遇到分号则发出5号错误 *)
     until sym <> ident (* 如果遇到非标识符,则常量声明结束 *)
   end;
   (* 此处的常量声明的语法与课本上的EBNF范式有不同之处:
      它可以接受像下面的声明方法,而根据课本上的EBNF范式不可得出下面的语法:
      const a = 3, b = 3; c = 6; d = 7, e = 8; 
      即它可以接受分号或逗号隔开的常量声明,而根据EBNF范式只可接受用逗号隔开的声明 *)
   if sym = varsym then (* 如果当前token是var保留字,开始进行变量声明,与常量声明类似 *)
   begin
     getsym; (* 获取下一个token,此处正常应为用作变量名的一个标识符 *)
     repeat (* 反复进行变量声明 *)
       vardeclaration; (* 以当前token为标识符声明一个变量 *)
       while sym = comma do (* 如果遇到了逗号则反复声明下一个变量 *)
       begin
         getsym; (* 获取下一个token,这里正好应该是标识符 *)
         vardeclaration; (* 声明以当前token为标识符的变量 *)
       end;
       if sym = semicolon then (* 如果变量声明结束,应遇到分号 *)
         getsym (* 获取下一个token,为下一轮循环做好准备 *)
       else
         error(5) (* 如果变量声明语句结束后没有遇到分号则发出5号错误 *)
     until sym <> ident; (* 如果遇到非标识符,则变量声明结束 *)
     (* 这里也存在与上面的常量声明一样的毛病:与PL/0的语法规范有冲突。 *)
   end;
   while sym = procsym do (* 循环声明各子过程 *)
   begin
     getsym; (* 获取下一个token,此处正常应为作为过程名的标识符 *)
     if sym = ident then (* 如果token确为标识符 *)
     begin
       enter(procedur); (* 把这个过程登录到名字表中 *)
       getsym (* 获取下一个token,正常情况应为分号 *)
     end
     else
       error(4); (* 否则抛出4号错误 *)
     if sym = semicolon then (* 如果当前token为分号 *)
       getsym (* 获取下一个token,准备进行语法分析的递归调用 *)
     else
       error(5); (* 否则抛出5号错误 *)
     block(lev + 1, tx, [semicolon] + fsys); (* 递归调用语法分析过程,当前层次加一,同时传递表头索引、合法单词符 *)
     if sym = semicolon then (* 递归返回后当前token应为递归调用时的最后一个end后的分号 *)
     begin
       getsym; (* 获取下一个token *)
       test(statbegsys + [ident, procsym], fsys, 6); (* 检查当前token是否合法,不合法则用fsys恢复语法分析同时抛6号错 *)
     end
     else
       error(5) (* 如果过程声明后的符号不是分号,抛出5号错误 *)
   end;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?