中山大学编译原理实验——实现PL0语言的编译程序(无坑有缩进版)

版权声明: https://blog.csdn.net/dickdick111/article/details/85285253

PL0-Compiler

代码传送门

ps:吐槽一下,老师给的代码实在是太烂了。又没缩进又多编译错误,除此之外还有很多细节漏掉关键字,总之就很多坑,所以这里发一个无坑带缩进版,方便大家学习。

中山大学编译原理项目:PL0语言的编译程序

  • 找到PASCAL编译系统;
  • 在PASCAL系统上运行PL0编译程序,需要对PL0编译程序作一些修改﹑调试;
  • 在PASCAL系统中,为PL0的编译程序建立输入文件和输出文件;
    • 在输入文件中存放PL0源程序;
    • 在输出文件中存放PL0源程序被编译后产生的中间代码和运行数据;
  • PL0的编译程序运行时, 通过输入文件输入PL0源程序, 在输出文件中产生源程序的中间代码, 然后运行该中间代码, 在输出文件中产生运行数据;
  • 如果上述工作成功, 则第一项实习任务完成.再做以下工作:
  • 在PL0语言中增加Read和Write语句;
  • 修改PL0编译程序, 使得PL0源程序可以使用Read和Write语句, 从文件(或键盘)输入数据,并可以向文件(或屏幕)写数据.

使用方法

测试PL0代码样例

  • PL0-sourse.txt( 不带read与write命令)
  • PL0-sourse-rw.txt(带read与write命令)

测试方法

  • 在命令行PL0文件夹目录下

    • 编译

      fpc PL0-compiler.pas
      
    • 执行

      PL0-compiler.exe
      
    • 输入PL0代码文件(PL0-sourse.txt)

    • 输入结果输出文件(PL0-dest.txt)

  • 最终结果显示在PL0-dest.txt中,包括栈中数据,最终结果,中间代码等

1

源代码

源代码跟测试代码都放到GITHUB仓库了,可以直接上仓库看,或者在这里看也可以。不过700多行的编译程序看起来还是有点痛苦的,csdn没有pascal高亮。

{PL0编译程序注释}

program  PL0 ( input, output);
{带有代码生成的PL0编译程序}
const
  norw = 13; {保留字的个数}
  txmax = 100; {标识符表长度}
  nmax = 14; {数字的最大位数}
  al = 10; {标识符的长度}
  amax = 2047; {最大地址}
  levmax = 3; {程序体嵌套的最大深度}
  cxmax = 200; {代码数组的大小}
type
  symbol = (nul, ident, number, plus, minus, times, slash, oddsym,
  eql, neq, lss, leq, gtr, geq, lparen, rparen, comma, semicolon,
  period, becomes, beginsym, endsym, ifsym, thensym,
  whilesym, dosym, callsym, constsym, varsym, procsym,
  readsym, writesym);
  alfa = packed array [1..al] of char; 
  object2 = (constant, variable, procedure2);
  symset = set of symbol;
  fct = (lit, opr, lod, sto, cal, int, jmp, jpc, red, wrt); {functions}
  instruction = packed record
    f : fct;  {功能码}
    l : 0..levmax; {相对层数}
    a : 0..amax; {相对地址}
end;
  {LIT 0,a : 取常数a
  OPR 0,a : 执行运算a
  LOD l,a : 取层差为l的层﹑相对地址为a的变量
  STO l,a : 存到层差为l的层﹑相对地址为a的变量
  CAL l,a : 调用层差为l的过程
  INT 0,a : t寄存器增加a
  JMP 0,a : 转移到指令地址a处
  JPC 0,a : 条件转移到指令地址a处 }
var
  ch : char; {最近读到的字符}
  sym : symbol; {最近读到的符号}
  id : alfa; {最近读到的标识符}
  num : integer; {最近读到的数}
  cc : integer; {当前行的字符计数}
  ll : integer; {当前行的长度}
  kk, err : integer;
  cx : integer; {代码数组的当前下标}
  line : array [1..81] of char; {当前行}
  a : alfa; {当前标识符的字符串}
  code : array [0..cxmax] of instruction; {中间代码数组}
  word : array [1..norw] of alfa; {存放保留字的字符串}
  wsym : array [1..norw] of symbol; {存放保留字的记号}
  ssym : array [char] of symbol; {存放算符和标点符号的记号}
  mnemonic : array [fct] of packed array [1..5] of char;
  {中间代码算符的字符串}
  declbegsys, statbegsys, facbegsys : symset;
  table : array [0..txmax] of record {符号表}
          name : alfa;
          case kind : object2 of constant : (val : integer);
  variable, procedure2 : (level, adr : integer)
          end;
  fin, fout : text; {定义fin, fout为文本文件变量}
  sfile, dfile : string;

procedure error (n : integer);
  begin 
    writeln('****', ' ' : cc-1, '^', n : 2);  
    err := err + 1
    {cc为当前行已读的字符数, n为错误号}{错误数err加1}
  end {error};
procedure getsym;
  var  i, j, k : integer;
  procedure  getch ; {取下一字符}
  begin if cc = ll then {如果cc指向行末}
    begin if eof(fin) then {如果已到文件尾}
        begin 
          write('PROGRAM INCOMPLETE'); 
          writeln(fout, 'PROGRAM INCOMPLETE');
          close(fin);
          close(fout);
          exit(); {实现goto 99 功能}
        end;
      {读新的一行}
      ll := 0; 
      cc := 0;
      write(cx : 5, ' ');
      write(fout, cx : 5, ' ');{cx : 5位数}
      while not eoln(fin) do {如果不是行末}
        begin
            ll := ll + 1; 
            read(fin, ch); 
            write(ch);
            write(fout, ch);
            line[ll] := ch  {一次读一行入line}
        end;
      writeln; 
      writeln(fout);
      readln(fin);
      ll := ll + 1;
      line[ll] := ' ' {line[ll]中是行末符}
    end;
  cc := cc + 1; 
  ch := line[cc]  {ch取line中下一个字符}
  end; {getch}

  begin {getsym} 
    while ch = ' ' do 
      getch; {跳过无用空白}
    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']);
      if k >= kk  
      then kk := k 
      else repeat 
        a[kk] := ' '; 
        kk := kk-1 {如果标识符长度不是最大长度, 后面补空白}
      until kk = k;                
      id := a;  
      i := 1;  
      j := norw;
      {id中存放当前标识符或保留字的字符串}
      repeat  
        k := (i+j) div 2; {用二分查找法在保留字表中找当前的标识符id}
        if id <= word[k] then j := k-1; 
        if id >= word[k] then i := k+1
      until i > j;
      if i-1 > j then sym := wsym[k] else sym := ident
      {如果找到, 当前记号sym为保留字, 否则sym为标识符}
      end 

    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和0ASCII码中的序号}
        k := k + 1;  
        getch;
      until not (ch in ['0'..'9']); {直到输入的不再是数字}
      if k > nmax then  error(30)
      {当前数字串的长度超过上界,则报告错误}
      end 
    else if ch = ':' then {处理赋值号}
      begin  
        getch;
        if ch = '=' then
          begin  
            sym := becomes; 
            getch 
          end
        else  
          sym := nul;
      end
    else {处理其它算符或标点符号}
      begin
        sym := ssym[ch];
        getch
      end
  end {getsym};

procedure gen(x : fct; y, z : integer); 
  begin 
    if cx > cxmax then {如果当前指令序号>代码的最大长度}
    begin 
      write('PROGRAM TOO LONG');
      writeln(fout);
      close(fin);
      exit(); {实现goto 99 功能}
    end;
    with code[cx] do {在代码数组cx位置生成一条新代码}
      begin  
        f := x; {功能码} l := y; {层号} a := z {地址}
      end;
    cx := cx + 1 {指令序号加1}
  end {gen};

procedure test(s1, s2 : symset; n : integer);
  begin
    if not (sym in s1) then 
  {如果当前记号不属于集合S1,则报告错误n}
      begin  
        error(n);  
        s1 := s1 + s2;
        while not(sym in s1) do 
          getsym 
        end
  {跳过一些记号, 直到当前记号属于S1S2}
  end {test};

procedure block(lev, tx : integer; fsys : symset);
  var dx : integer; {本过程数据空间分配下标}
      tx0 : integer; {本过程标识表起始下标}
      cx0 : integer; {本过程代码起始下标}
  
  procedure enter(k : object2);
    begin {把object2填入符号表中}
      tx := tx +1; {符号表指针加1}
      with table[tx] do{在符号表中增加新的一个条目}
      begin  
        name := id; {当前标识符的名字} 
        kind := k; {当前标识符的种类}
        case k of
          constant : 
            begin {当前标识符是常数名}
              if num > amax then {当前常数值大于上界,则出错}
                begin error(30); num := 0 end;
                val := num
            end;
          variable : 
            begin {当前标识符是变量名}
              level := lev; {定义该变量的过程的嵌套层数} 
              adr := dx; {变量地址为当前过程数据空间栈顶} 
              dx := dx +1; {栈顶指针加1}
            end;
          procedure2 : level := lev {本过程的嵌套层数}
        end
      end
    end {enter};

  function  position(id : alfa) : integer; {返回id在符号表的入口}
    var  i : integer; 
    begin {在标识符表中查标识符id}
      table[0].name := id; {在符号表栈的最下方预填标识符id} 
      i := tx; {符号表栈顶指针}
      while table[i].name <> id do i := i-1;
      {从符号表栈顶往下查标识符id}
      position := i {若查到,i为id的入口,否则i=0 } 
    end {position};

  procedure constdeclaration;
    begin
      if sym = ident then {当前记号是常数名}
        begin  
          getsym;
          if sym in [eql, becomes] then {当前记号是等号或赋值号}
            begin
              if sym = becomes then error(1);
                {如果当前记号是赋值号,则出错}
              getsym;
              if sym = number then {等号后面是常数}
                begin  
                  enter(constant); {将常数名加入符号表}
                  getsym
                end
              else error(2) {等号后面不是常数出错}
            end 
          else error(3) {标识符后不是等号或赋值号出错}
        end 
      else error(4) {常数说明中没有常数名标识符}
    end; {constdeclaration}

  procedure vardeclaration;
    begin
      if sym = ident then {如果当前记号是标识符}
      begin  
        enter(variable); {将该变量名加入符号表的下一条目} 
        getsym
      end 
      else error(4) {如果变量说明未出现标识符,则出错}
    end; {vardeclaration}

  procedure listcode;
    var i : integer;
    begin  {列出本程序体生成的代码}
      for i := cx0 to cx-1 do 
      {cx0: 本过程第一个代码的序号,cx-1: 本过程最后一个代码的序号}
        with code[i] do {打印第i条代码}
          writeln(fout, i:3,mnemonic[f] : 5, l : 3, a : 5)
          {writeln(i, mnemonic[f] : 5, l : 3, a : 5)}
   {i: 代码序号; 
    mnemonic[f]: 功能码的字符串;
    l: 相对层号(层差);
    a: 相对地址或运算号码}
    end {listcode};

  procedure  statement(fsys : symset);
    var i, cx1, cx2 : integer;
    procedure  expression(fsys : symset);
      var  addop : symbol;
      procedure  term(fsys : symset);
        var  mulop : symbol;
        procedure  factor(fsys : symset);
          var i : integer;
          begin  
            test(facbegsys, fsys, 24); 
            {测试当前的记号是否因子的开始符号, 
              否则出错, 跳过一些记号}
            while sym in facbegsys do 
            {如果当前的记号是否因子的开始符号}
              begin
                if sym = ident then {当前记号是标识符}
                begin
                  i := position(id); {查符号表,返回id的入口}
                  if i = 0 
                    then error(11) 
                  else
                  {若在符号表中查不到id, 则出错, 否则,做以下工作}
                    with table[i] do
                      case kind of 
                        constant : gen(lit, 0, val); 
                        {若id是常数, 生成指令,将常数val取到栈顶}
                        variable : gen(lod, lev-level, adr);
                        {若id是变量, 生成指令,将该变量取到栈顶;
                          lev: 当前语句所在过程的层号;
                          level: 定义该变量的过程层号;
                          adr: 变量在其过程的数据空间的相对地址}
                        procedure2 : error(21)
                      {若id是过程名, 则出错}
                      end;
                    getsym {取下一记号}
                  end 
                else if sym = number then {当前记号是数字}
                  begin
                    if num > amax then {若数值越界,则出错}
                    begin 
                      error(30); 
                      num := 0 
                    end;
                    gen(lit, 0, num); 
                    {生成一条指令, 将常数num取到栈顶}
                    getsym {取下一记号}
                  end 
                else if sym = lparen then {如果当前记号是左括号}
                  begin 
                    getsym; {取下一记号}
                    expression([rparen]+fsys); {处理表达式}
                    if sym = rparen then getsym
                    {如果当前记号是右括号, 则取下一记号,否则出错}
                    else error(22)
                 end;
            test(fsys, [lparen], 23) 
            {测试当前记号是否同步, 否则出错, 跳过一些记号}
            end {while}
          end {factor};

        begin {term}
          factor(fsys+[times, slash]); {处理项中第一个因子}
          while sym in [times, slash] do 
          {当前记号是“乘”或“除”号}
          begin
            mulop := sym; {运算符存入mulop} 
            getsym; {取下一记号}
            factor(fsys+[times, slash]); {处理一个因子}
            if mulop = times 
            then gen(opr, 0, 4)
            {若mulop是“乘”号,生成一条乘法指令}
            else gen(opr, 0, 5)
            {否则, mulop是除号, 生成一条除法指令}
          end
        end {term};

      begin {expression}
        if sym in [plus, minus] then {若第一个记号是加号或减号}
        begin 
          addop := sym;  {+”或“-”存入addop}
          getsym; 
          term(fsys+[plus, minus]); {处理一个项}
          if addop = minus then gen(opr, 0, 1)
          {若第一个项前是负号, 生成一条“负运算”指令}
        end 
        else 
          term(fsys+[plus, minus]);
          {第一个记号不是加号或减号, 则处理一个项}
          while sym in [plus, minus] do {若当前记号是加号或减号}
            begin
              addop := sym; {当前算符存入addop} 
              getsym; {取下一记号}
              term(fsys+[plus, minus]); {处理一个项}
              if addop = plus 
              then gen(opr, 0, 2)
              {若addop是加号, 生成一条加法指令}
              else gen(opr, 0, 3)
              {否则, addop是减号, 生成一条减法指令}
            end
        end {expression};

      procedure condition(fsys : symset);
        var relop : symbol;
        begin
          if sym = oddsym then {如果当前记号是“odd”}
          begin
            getsym;  {取下一记号}
            expression(fsys); {处理算术表达式}
            gen(opr, 0, 6) {生成指令,判定表达式的值是否为奇数,,则取“真”;不是, 则取“假”}
          end 
          else {如果当前记号不是“odd”}
            begin
              expression([eql, neq, lss, gtr, leq, geq] + fsys); 
              {处理算术表达式}
            if not (sym in [eql, neq, lss, leq, gtr, geq]) 
            {如果当前记号不是关系符, 则出错; 否则,做以下工作}
            then error(20)  
            else
              begin
                relop := sym; {关系符存入relop} 
                getsym; {取下一记号} 
                expression(fsys); {处理关系符右边的算术表达式}
                case relop of
                  eql : gen(opr, 0, 8); 
                  {生成指令, 判定两个表达式的值是否相等}
                  neq : gen(opr, 0, 9);
                  {生成指令, 判定两个表达式的值是否不等}
                  lss : gen(opr, 0, 10);
                  {生成指令,判定前一表达式是否小于后一表达式}
                  geq : gen(opr, 0, 11);
                  {生成指令,判定前一表达式是否大于等于后一表达式}
                  gtr : gen(opr, 0, 12);
                  {生成指令,判定前一表达式是否大于后一表达式}
                  leq : gen(opr, 0, 13);
                  {生成指令,判定前一表达式是否小于等于后一表达式}
                end
              end
            end
        end; {condition}

    begin {statement}
      if sym = ident then {处理赋值语句}
      begin  
        i := position(id); 
        {在符号表中查id, 返回id在符号表中的入口}
        if i = 0 
          then error(11) 
        {若在符号表中查不到id, 则出错, 否则做以下工作}
        else if table[i].kind <> variable then
        {若标识符id不是变量, 则出错}
        begin {对非变量赋值} 
          error(12); 
          i := 0; 
        end;
        getsym; {取下一记号}
        if sym = becomes 
        then getsym 
        else error(13);
        {若当前是赋值号, 取下一记号, 否则出错}
        expression(fsys); {处理表达式}
        if i <> 0 then {若赋值号左边的变量id有定义}
          with table[i] do gen(sto, lev-level, adr)
          {生成一条存数指令, 将栈顶(表达式)的值存入变量id中;
           lev: 当前语句所在过程的层号;
           level: 定义变量id的过程的层号;
           adr: 变量id在其过程的数据空间的相对地址}
        end 
        else if sym = callsym then {处理过程调用语句}
        begin  
          getsym; {取下一记号}
          if sym <> ident 
          then error(14) 
          else
          {如果下一记号不是标识符(过程名),则出错,
           否则做以下工作}
          begin 
            i := position(id); {查符号表,返回id在表中的位置}
            if i = 0 
              then error(11) 
            else
              {如果在符号表中查不到, 则出错; 否则,做以下工作}
              with table[i] do
                if kind = procedure2 then 
                  {如果在符号表中id是过程名}
                  gen(cal, lev-level, adr)
                  {生成一条过程调用指令;
                   lev: 当前语句所在过程的层号
                   level: 定义过程名id的层号;
                   adr: 过程id的代码中第一条指令的地址}
                else error(15); {若id不是过程名,则出错}
              getsym {取下一记号}
            end
          end 
          else if sym = ifsym then {处理条件语句}
          begin
            getsym; {取下一记号} 
            condition([thensym, dosym]+fsys); {处理条件表达式}
            if sym = thensym 
              then getsym 
            else error(16);
            {如果当前记号是“then”,则取下一记号; 否则出错}
            cx1 := cx; {cx1记录下一代码的地址} 
            gen(jpc, 0, 0); {生成指令,表达式为“假”转到某地址(待填),
                              否则顺序执行}
            statement(fsys); {处理一个语句}
            code[cx1].a := cx 
            {将下一个指令的地址回填到上面的jpc指令地址栏}
          end 
          else if sym = beginsym then {处理语句序列}
          begin
            getsym;  
            statement([semicolon, endsym]+fsys);
            {取下一记号, 处理第一个语句}
            while sym in ([semicolon]+statbegsys) do 
            {如果当前记号是分号或语句的开始符号,则做以下工作}
            begin
              if sym = semicolon 
                then getsym 
              else error(10);
              {如果当前记号是分号,则取下一记号, 否则出错}
              statement([semicolon, endsym]+fsys) {处理下一个语句}
            end;
            if sym = endsym 
              then getsym 
            else error(17)
          {如果当前记号是“end”,则取下一记号,否则出错}
          end
          else if sym = whilesym then {处理循环语句}
          begin
            cx1 := cx; {cx1记录下一指令地址,即条件表达式的
                        第一条代码的地址} 
            getsym; {取下一记号}
            condition([dosym]+fsys); {处理条件表达式}
            cx2 := cx; {记录下一指令的地址} 
            gen(jpc, 0, 0); {生成一条指令,表达式为“假”转到某地
                              (待回填), 否则顺序执行}
            if sym = dosym 
              then getsym 
            else error(18);
            {如果当前记号是“do,则取下一记号, 否则出错}
            statement(fsys); {处理“do”后面的语句}
            gen(jmp, 0, cx1); {生成无条件转移指令, 转移到“while”后的
            条件表达式的代码的第一条指令处} 
            code[cx2].a := cx 
            {把下一指令地址回填到前面生成的jpc指令的地址栏}
          end
          else if sym = readsym then {处理read关键字}
          begin
            getsym;
            if sym = lparen then{如果read后是左括号}
              repeat
                getsym;
                if sym = ident
                then begin
                  i := position(id);
                  if i = 0
                    then error(11)
                  else if table[i].kind <> variable
                  then begin
                    error(12);
                    i := 0
                  end
                  else with table[i] do
                    gen(red,lev-level,adr)
                end
                else  error(4);
              getsym;
              until sym <> comma
            else
              error(40);
            if sym <> rparen
            then error(22);
            getsym
          end
          else if sym = writesym
            then begin
              getsym;
            if sym = lparen
            then begin
              repeat
                getsym;
                expression([rparen,comma]+fsys);
                gen(wrt,0,0);
              until sym <> comma;
              if sym <> rparen
              then error(22);
            getsym
            end
            else
              error(40)
          end;
        test(fsys, [ ], 19) 
      {测试下一记号是否正常, 否则出错, 跳过一些记号}
      end {statement};
  begin {block}
    dx := 3; {本过程数据空间栈顶指针} 
    tx0 := tx; {标识符表的长度(当前指针)} 
    table[tx].adr := cx; {本过程名的地址, 即下一条指令的序号}
    gen(jmp, 0, 0); {生成一条转移指令}
    if lev > levmax then error(32);
    {如果当前过程层号>最大层数, 则出错}
    repeat
      if sym = constsym then {处理常数说明语句}
      begin  
        getsym;
        repeat 
          constdeclaration; {处理一个常数说明}
          while sym = comma do {如果当前记号是逗号}
          begin 
            getsym; 
            constdeclaration 
          end; {处理下一个常数说明}
          if sym = semicolon 
            then getsym 
          else error(5)
            {如果当前记号是分号,则常数说明已处理完, 否则出错}
        until sym <> ident 
        {跳过一些记号, 直到当前记号不是标识符(出错时才用到)}
      end;

    if sym = varsym then {当前记号是变量说明语句开始符号}
      begin getsym;
        repeat 
          vardeclaration; {处理一个变量说明}
          while sym = comma do {如果当前记号是逗号}
          begin  
            getsym;  
            vardeclaration 
          end; 
          {处理下一个变量说明}
          if sym = semicolon 
            then getsym 
          else error(5)
          {如果当前记号是分号,则变量说明已处理完, 否则出错}
        until sym <> ident; 
        {跳过一些记号, 直到当前记号不是标识符(出错时才用到)}
      end;
    while sym = procsym do {处理过程说明}
    begin  
      getsym;
      if sym = ident then {如果当前记号是过程名}
      begin  
        enter(procedure2);  
        getsym  
      end 
      {把过程名填入符号表}
      else 
        error(4); {否则, 缺少过程名出错}
      if sym = semicolon 
        then getsym 
      else error(5);
      {当前记号是分号, 则取下一记号,否则,过程名后漏掉分号出错}
      block(lev+1, tx, [semicolon]+fsys); {处理过程体}
      {lev+1: 过程嵌套层数加1; tx: 符号表当前栈顶指针,
      也是新过程符号表起始位置; [semicolon]+fsys: 过程体开始和末尾符号集}
      if sym = semicolon then {如果当前记号是分号}
      begin 
        getsym; {取下一记号}
        test(statbegsys+[ident, procsym], fsys, 6)
        {测试当前记号是否语句开始符号或过程说明开始符号,
         否则报告错误6, 并跳过一些记号}
      end
      else error(5) {如果当前记号不是分号,则出错}
    end; {while}
  test(statbegsys+[ident], declbegsys, 7)
  {检测当前记号是否语句开始符号, 否则出错, 并跳过一些
  记号}
  until not (sym in declbegsys); 
  {回到说明语句的处理(出错时才用),直到当前记号不是说明语句
   的开始符号}
  code[table[tx0].adr].a := cx;  {table[tx0].adr是本过程名的第1代码(jmp, 0, 0)的地址,本语句即是将下一代码(本过程语句的第
1条代码)的地址回填到该jmp指令中,(jmp, 0, cx)}
  with table[tx0] do {本过程名的第1条代码的地址改为下一指令地址cx}
  begin  
    adr := cx; {代码开始地址}
  end;
  cx0 := cx; {cx0记录起始代码地址}
  gen(int, 0, dx); {生成一条指令, 在栈顶为本过程留出数据空间}
  statement([semicolon, endsym]+fsys); {处理一个语句}
  gen(opr, 0, 0); {生成返回指令}
  test(fsys, [ ], 8); {测试过程体语句后的符号是否正常,否则出错}
  listcode; {打印本过程的中间代码序列}
end  {block};

procedure interpret;
  const stacksize = 500; {运行时数据空间()的上界}
  var p, b, t : integer; {程序地址寄存器, 基地址寄存器,栈顶地址寄存器}
      i : instruction; {指令寄存器}
      s : array [1..stacksize] of integer; {数据存储栈}

  function  base(l : integer) : integer;
  var  b1 : integer;
  begin
    b1 := b; {顺静态链求层差为l的外层的基地址}
    while l > 0 do
    begin  
      b1 := s[b1];  
      l := l-1 
    end;
    base := b1
  end {base};
  begin  
    writeln('START PL/0');
    writeln(fout, 'START PL/0');
    t := 0; {栈顶地址寄存器}
    b := 1; {基地址寄存器}
    p := 0; {程序地址寄存器}
    s[1] := 0;  
    s[2] := 0;  
    s[3] := 0; 
    {最外层主程序数据空间栈最下面预留三个单元}
    {每个过程运行时的数据空间的前三个单元是:SL, DL, RA;
    SL: 指向本过程静态直接外层过程的SL单元;
    DL: 指向调用本过程的过程的最新数据空间的第一个单元;
    RA: 返回地址 }
    repeat
      i := code[p]; {i取程序地址寄存器p指示的当前指令}
      p := p+1; {程序地址寄存器p加1,指向下一条指令}
      with i do
        case f of
          lit : 
          begin {当前指令是取常数指令(lit, 0, a)}
            t := t+1;  
            s[t] := a
          end;
          {栈顶指针加1, 把常数a取到栈顶}
          opr : case a of {当前指令是运算指令(opr, 0, a)}
            0 : 
              begin {a=0,是返回调用过程指令}
                t := b-1; {恢复调用过程栈顶} 
                p := s[t+3]; {程序地址寄存器p取返回地址} 
                b := s[t+2]; 
                {基地址寄存器b指向调用过程的基地址}
              end;
            1 : s[t] := -s[t]; {一元负运算, 栈顶元素的值反号}
            2 : begin {加法}
                  t := t-1;  
                  s[t] := s[t] + s[t+1] 
                end;
            3 : begin {减法}
                  t := t-1;  
                  s[t] := s[t]-s[t+1]
                end;
            4 : begin {乘法}
                  t := t-1;  
                  s[t] := s[t] * s[t+1]
                end;
            5 : begin {整数除法}
                  t := t-1;  
                  s[t] := s[t] div s[t+1]
                end;
            6 : s[t] := ord(odd(s[t])); 
            {算s[t]是否奇数, 是则s[t]=1, 否则s[t]=0}
            8 : begin  
                  t := t-1;
                  s[t] := ord(s[t] = s[t+1])
                end; {判两个表达式的值是否相等,
                     是则s[t]=1, 否则s[t]=0}
            9: begin  
                  t := t-1;
                  s[t] := ord(s[t] <> s[t+1])
                end; {判两个表达式的值是否不等,
                     是则s[t]=1, 否则s[t]=0}
            10 : begin  
                  t := t-1;
                  s[t] := ord(s[t] < s[t+1])
                end; {判前一表达式是否小于后一表达式,
                     是则s[t]=1, 否则s[t]=0}
            11: begin  
                  t := t-1;
                  s[t] := ord(s[t] >= s[t+1])
                end; {判前一表达式是否大于或等于后一表达式,
                   是则s[t]=1, 否则s[t]=0}
            12 : begin  
                  t := t-1;
                  s[t] := ord(s[t] > s[t+1])
                end; {判前一表达式是否大于后一表达式,
                     是则s[t]=1, 否则s[t]=0}
            13 : begin  
                  t := t-1;
                  s[t] := ord(s[t] <= s[t+1])
                end; {判前一表达式是否小于或等于后一表达式,
                    是则s[t]=1, 否则s[t]=0}
          end;
          lod : 
          begin {当前指令是取变量指令(lod, l, a)}
            t := t + 1;  
            s[t] := s[base(l) + a]
            {栈顶指针加1, 根据静态链SL,将层差为l,相对地址
             为a的变量值取到栈顶}
          end;
          sto : 
          begin {当前指令是保存变量值(sto, l, a)指令}
            s[base(l) + a] := s[t];  
            writeln(s[t]); 
            writeln(fout, s[t]);
            {根据静态链SL,将栈顶的值存入层差为l,相对地址
              为a的变量中}
            t := t-1 {栈顶指针减1}
          end;
          cal : 
          begin {当前指令是(cal, l, a)}
          {为被调用过程数据空间建立连接数据}
            s[t+1] := base( l ); 
            {根据层差l找到本过程的静态直接外层过程的数据空间的SL单元,
              将其地址存入本过程新的数据空间的SL单元} 
            s[t+2] := b; 
            {调用过程的数据空间的起始地址存入本过程DL单元}
            s[t+3] := p;
            {调用过程cal指令的下一条的地址存入本过程RA单元}
            b := t+1; {b指向被调用过程新的数据空间起始地址} 
            p := a {指令地址寄存储器指向被调用过程的地址a}
          end;
          int : t := t + a; 
          {若当前指令是(int, 0, a), 则数据空间栈顶留出a大小的空间}
          jmp : p := a; 
          {若当前指令是(jmp, 0, a), 则程序转到地址a执行}
          jpc : 
          begin {当前指令是(jpc, 0, a)}
            if s[t] = 0 then p := a;
            {如果当前运算结果为“假”(0), 程序转到地址a
             执行, 否则顺序执行}
            t := t-1 {数据栈顶指针减1}
          end;
          red : 
          begin {对red指令}
            writeln('Please input your number:'); 
            readln(s[base(l)+a]); {读一行数据,读入到相差l层,层内偏移为a的数据栈中的数据的信息}
          end;
          wrt : 
          begin {对wrt指令}
            writeln('your output: ', s[t]);
            writeln(fout, 'your output: ', s[t]);
            t := t+1  {栈顶上移}
          end
        end {with, case}
    until p = 0; 
    {程序一直执行到p取最外层主程序的返回地址0时为止}
    write('END PL/0');
    write(fout, 'END PL/0');
  end; {interpret}


begin  {主程序}
  writeln('please input source program file name : ');
  readln(sfile);
  assign(fin,sfile);
  reset(fin);
  writeln('please input the file name to save result : ');
  readln(dfile);
  assign(fout,dfile);
  rewrite(fout);

  for ch := 'a' to ';' do  ssym[ch] := nul; 
  {ASCII码的顺序}
  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] := 'read      '; word[10]:= 'then      ';
  word[11]:= 'var       '; word[12]:= 'while     ';
  word[13]:= 'write     ';
  {保留字}
  wsym[1] := beginsym;   wsym[2] := callsym;
  wsym[3] := constsym;   wsym[4] := dosym;
  wsym[5] := endsym;     wsym[6] := ifsym;
  wsym[7] := oddsym;     wsym[8] := procsym;
  wsym[9] := readsym;    wsym[10] := thensym;
  wsym[11] := varsym;  wsym[12] := whilesym;
  wsym[13] := writesym;
  {保留字的记号}
  ssym['+'] := plus;      ssym['-'] := minus;
  ssym['*'] := times;     ssym['/'] := slash;
  ssym['('] := lparen;     ssym[')'] := rparen;
  ssym['='] := eql;       ssym[','] := comma;
  ssym['.'] := period;     ssym['!'] := neq; {不等于 用!表示}
  ssym['<'] := lss;       ssym['>'] := gtr;
  ssym['@'] := leq;      ssym['#'] := geq; {小于等于 用@表示, 大于等于 用#表示}
  ssym[';'] := semicolon;
  {算符和标点符号的记号}
  mnemonic[lit] := '  LIT  ';    mnemonic[opr] := '  OPR  ';
  mnemonic[lod] := '  LOD  ';    mnemonic[sto] := '  STO  ';
  mnemonic[cal] := '  CAL  ';    mnemonic[int] := '  INT  ';
  mnemonic[jmp] := '  JMP  ';    mnemonic[jpc] := '  JPC  ';
  mnemonic[red] := '  RED  ';    mnemonic[wrt] := '  WRT  ';
  {中间代码指令的字符串}
  declbegsys := [constsym, varsym, procsym];
  {说明语句的开始符号}
  statbegsys := [beginsym, callsym, ifsym, whilesym];
  {语句的开始符号}
  facbegsys := [ident, number, lparen];
  {因子的开始符号}
  {page(output);} {为找到id page标识符}
  err := 0; {发现错误的个数}
  cc := 0; {当前行中输入字符的指针} 
  cx := 0; {代码数组的当前指针} 
  ll := 0; {输入当前行的长度} 
  ch := ' '; {当前输入的字符}
  kk := al; {标识符的长度}
  getsym; {取下一个记号}
  block(0, 0, [period]+declbegsys+statbegsys); {处理程序体}
  if sym <> period then error(9);
  {如果当前记号不是句号, 则出错}
  if err = 0 then interpret
  {如果编译无错误, 则解释执行中间代码}
  else 
    begin
      write('ERRORS IN PL/0 PROGRAM');
      write(fout, 'ERRORS IN PL/0 PROGRAM');
    end;
  writeln;
  close(fin);
  readln(sfile);
  close(fout);
end.

猜你喜欢

转载自blog.csdn.net/dickdick111/article/details/85285253