Old secrets of quick debugging: source code animation

    Friday evening often turns out to be an evening of memories, and not only about the past week, but about much earlier events. This Friday, I remembered one interesting program for MS DOS (as well as for Mac OS, UNIX and VAX / VMS) - Pascal interpreter and IDE Dr. Pascal. Detailed information about the opportunities and reviews can be found on the website of the manufacturer Visible Software (USA) stored in the archive , and I will confine myself to the most memorable ideas for me, and these ideas, in my opinion, have not lost their relevance even today. First of all, remember the picture:

    image

    Later we will return to this picture, but for now we will start from the beginning, i.e. from the main menu:

    image

    As you can see, the menu is rather ordinary for a DOS program. Load the file of a well-known task.

    "8 queens":
    program EightQueens(output);
    { Place 8 hostile queens on a chessboard, such that none can be captured. }{ From Wirth:  Algorithms + Data Structures = Programs, page 146. }var
        i: integer;
        RowFree: array [1..8] of boolean;
        UpDiag: array [2..16] of boolean; { diagonal to upper right }
        DownDiag: array [-7..7] of boolean; { diagonal to lower right }
        QueenIn: array [1..8] of integer;
      procedureprint;{ Write out one solution }var
          k: integer;
        begin{ print }for k := 1to8dowrite(QueenIn[k]: 4);
          writeln;
        end{ print };
      proceduretry(col: integer);{ Try to place a queen in this column }var
          row: integer;
        begin{ try }for row := 1to8doif RowFree[row] and UpDiag[col+row] and DownDiag[col-row] thenbegin
                QueenIn[col] := row;
                RowFree[row] := false;
                UpDiag[col+row] := false;
                DownDiag[col-row] := false;
                if col < 8thentry(col+1)
                  else print;
                RowFree[row] := true;
                UpDiag[col+row] := true;
                DownDiag[col-row] := true;
              end;
        end{ try };
      begin{ EightQueens }for i := 1to8do
          RowFree[i] := true;
        for i := 2to16do
          UpDiag[i] := true;
        for i := -7to7do
          DownDiag[i] := true;
        try(1)
      end{ EightQueens }.
    


    and press F9 (Run). The performance of the program is displayed on the screen:

    image

    Below are the results output by the program (output), above to the left is a fragment of the code of the procedure (or function) executed at this step, where the arrow indicates the executable operator, to the right - the values ​​of the variables currently relevant, above - the same for the calling procedure. At the same time, the transition from operator to operator occurs automatically with a user-defined delay — such a “movie” stops when the program loaded in the IDE (in our case, “8 queens”) stops or when the Freeze command is given, which can be given by pressing the corresponding function key. If the program is not over, then you can continue to move step by step, as in other debuggers, pressing the down arrow, or return to the "movie" by pressing F8 (Continue). The second line at the top of the screen displays the chain of procedure calls. It is worth noting that the above-mentioned “currently relevant variables” the Doctor chooses himself, the user only needs to load the program and press Run. Judging by the reviews, such an extremely simple control turned out to be very convenient for introductory student courses on the basics of programming, for which Dr. Pascal, in fact, intended. However, the user manual notes that for an advanced professional programmer, a quick opportunity to see how a small program works in one motion can be useful. This raises an interesting question: how small? and intended. However, the user manual notes that for an advanced professional programmer, a quick opportunity to see how a small program works in one motion can be useful. This raises an interesting question: how small? and intended. However, the user manual notes that for an advanced professional programmer, a quick opportunity to see how a small program works in one motion can be useful. This raises an interesting question: how small?

    I took Virtovsky interpreter Pascals , written in Pascal. Compared to queens, this program, with a volume of about 2000 lines of source code, is much more complicated. She was in the original form beyond the powers of the doctor, so I cut it into two parts.

    The first part prepares data files:
    {%F- no reformatting }{%O+}program Pas1 (input,output,paskey,pasksy,spsfile,enterf,symsetf{,textf});
    const
       nkw = 27;     (*no. of key words*)
       alng =  10;   (*no. of significant chars in identifiers*)type
       symbol = (intcon,realcon,charcon,string,
                      notsy,plus,minus,times,idiv,rdiv,imod,andsy,orsy,
                      eql,neq,gtr,geq,lss,leq,
                      lparent,rparent,lbrack,rbrack,comma,semicolon,period,
                      colon,becomes,constsy,typesy,varsy,functionsy,
                      proceduresy,arraysy,recordsy,programsy,ident,
                      beginsy,ifsy,casesy,repeatsy,whilesy,forsy,
                      endsy,elsesy,untilsy,ofsy,dosy,tosy,downtosy,thensy);
           alfa = packedarray [1..alng] of char;
           object = (konstant,variable,type1,prozedure,funktion);
           types = (notyp,ints,reals,bools,chars,arrays,records);
      keytype = array [1..nkw] of alfa;
      ksytype = array [1..nkw] of symbol;
      spstype = array [char] of symbol;
      symset = setof symbol;
      entertype = record
                   fx0: alfa; fx1: object;
                   fx2: types; fx3: integer;
                  end;
       var
           key: keytype;
           ksy: ksytype;
           sps: spstype; (*special symbols*)
           syset : symset;
           pasksy  : fileof ksytype;
           paskey  : fileof keytype;
           spsfile : fileof spstype;
           enterf  : fileof entertype;
           symsetf : fileof symset;
    {       textf   : text;}procedureenter(x0: alfa; x1: object;
                    x2: types; x3: integer);var
      EnterRec : EnterType;
    beginwith EnterRec dobegin  fx0 := x0; fx1 := x1;
            fx2 := x2;  fx3 := x3
         end;
      write ( enterf, EnterRec );
    end(*enter*) ;
    begin{main program}
       key[ 1] := 'and       '; key[ 2] := 'array     ';
       key[ 3] := 'begin     '; key[ 4] := 'case      ';
       key[ 5] := 'const     '; key[ 6] := 'div       ';
       key[ 7] := 'do        '; key[ 8] := 'downto    ';
       key[ 9] := 'else      '; key[10] := 'end       ';
       key[11] := 'for       '; key[12] := 'function  ';
       key[13] := 'if        '; key[14] := 'mod       ';
       key[15] := 'not       '; key[16] := 'of        ';
       key[17] := 'or        '; key[18] := 'procedure ';
       key[19] := 'program   '; key[20] := 'record    ';
       key[21] := 'repeat    '; key[22] := 'then      ';
       key[23] := 'to        '; key[24] := 'type      ';
       key[25] := 'until     '; key[26] := 'var       ';
       key[27] := 'while     ';
       ksy[ 1] := andsy;         ksy[ 2] := arraysy;
       ksy[ 3] := beginsy;       ksy[ 4] := casesy;
       ksy[ 5] := constsy;       ksy[ 6] := idiv;
       ksy[ 7] := dosy;          ksy[ 8] := downtosy;
       ksy[ 9] := elsesy;        ksy[10] := endsy;
       ksy[11] := forsy;         ksy[12] := functionsy;
       ksy[13] := ifsy;          ksy[14] := imod;
       ksy[15] := notsy;         ksy[16] := ofsy;
       ksy[17] := orsy;          ksy[18] := proceduresy;
       ksy[19] := programsy;     ksy[20] := recordsy;
       ksy[21] := repeatsy;      ksy[22] := thensy;
       ksy[23] := tosy;          ksy[24] := typesy;
       ksy[25] := untilsy;       ksy[26] := varsy;
       ksy[27] := whilesy;
       rewrite (paskey);
       write (paskey, key);
       ksy[ 1] := andsy;         ksy[ 2] := arraysy;
       ksy[ 3] := beginsy;       ksy[ 4] := casesy;
       ksy[ 5] := constsy;       ksy[ 6] := idiv;
       ksy[ 7] := dosy;          ksy[ 8] := downtosy;
       ksy[ 9] := elsesy;        ksy[10] := endsy;
       ksy[11] := forsy;         ksy[12] := functionsy;
       ksy[13] := ifsy;          ksy[14] := imod;
       ksy[15] := notsy;         ksy[16] := ofsy;
       ksy[17] := orsy;          ksy[18] := proceduresy;
       ksy[19] := programsy;     ksy[20] := recordsy;
       ksy[21] := repeatsy;      ksy[22] := thensy;
       ksy[23] := tosy;          ksy[24] := typesy;
       ksy[25] := untilsy;       ksy[26] := varsy;
       ksy[27] := whilesy;
       rewrite (pasksy);
       write (pasksy, ksy);
       sps['+'] := plus;         sps['-'] := minus;
       sps['*'] := times;        sps['/'] := rdiv;
       sps['('] := lparent;      sps[')'] := rparent;
       sps['='] := eql;          sps[','] := comma;
       sps['['] := lbrack;       sps[']'] := rbrack;
       sps['#'] := neq;          sps['&'] := andsy;
       sps[';'] := semicolon;
       rewrite (spsfile);
       write (spsfile, sps);
      rewrite (enterf);
      enter('          ', variable, notyp, 0);  (*sentinel*)
      enter('false     ', konstant, bools, 0);
      enter('true      ', konstant, bools, 1);
      enter('real      ', type1, reals, 1);
      enter('char      ', type1, chars, 1);
      enter('boolean   ', type1, bools, 1);
      enter('integer   ', type1, ints , 1);
      enter('abs       ', funktion, reals,0);
      enter('sqr       ', funktion, reals,2);
      enter('odd       ', funktion, bools,4);
      enter('chr       ', funktion, chars,5);
      enter('ord       ', funktion, ints, 6);
      enter('succ      ', funktion, chars,7);
      enter('pred      ', funktion, chars,8);
      enter('round     ', funktion, ints, 9);
      enter('trunc     ', funktion, ints, 10);
      enter('sin       ', funktion, reals, 11);
      enter('cos       ', funktion, reals, 12);
      enter('exp       ', funktion, reals, 13);
      enter('ln        ', funktion, reals, 14);
      enter('sqrt      ', funktion, reals, 15);
      enter('arctan    ', funktion, reals, 16);
      enter('eof       ', funktion, bools, 17);
      enter('eoln      ', funktion, bools, 18);
      enter('read      ', prozedure, notyp, 1);
      enter('readln    ', prozedure, notyp, 2);
      enter('write     ', prozedure, notyp, 3);
      enter('writeln   ', prozedure, notyp, 4);
      enter('          ', prozedure, notyp, 0);
      rewrite (symsetf);
      syset := [plus,minus,intcon,realcon,charcon,ident];
      write ( symsetf, syset );
      syset := [ident,arraysy,recordsy];
      write ( symsetf, syset );
      syset := [constsy,typesy,varsy,proceduresy,functionsy,beginsy];
      write ( symsetf, syset );
      syset := [intcon,realcon,charcon,ident,lparent,notsy];
      write ( symsetf, syset );
      syset := [beginsy,ifsy,whilesy,repeatsy,forsy,casesy];
      write ( symsetf, syset );
    end.
    


    Here it is necessary to clarify that the directives Dr. Pascal are enclosed in comment braces and begin with a “%” character. The directive {% O +} includes a simplified file name, in which, for example, an external file, defined as

    pasksy  : fileof ksytype;

    so it will be called "pasksy". Like any external file, it must be specified in the program header:

    program Pas1 (input,output,paskey,

    In the remaining part of PascalS we also specify the data files:

    {%D+}{%F- no reformatting }{%O+}program Pascals(input,output,paskey,pasksy,spsfile,enterf,symsetf);{1.6.75}

    The% D + directive allows you to programmatically stop the animation by calling the predefined Freeze procedure.

    The body of the PascalS program will look like this:

    begin{main program}
       assign (input,'QUEENS.PAS');
       reset  (input);
       init;
       block(blockbegsys+statbegsys, false, 1);
       finish;
    99: end.
    

    Where init and finish procedures:
    procedureinit;{%s-}var
      i : integer;
      EnterRec : EnterType;
    begin
     writeln;
     reset (paskey);   read  (paskey, key);
     reset (pasksy);   read  (pasksy, ksy);
     reset (spsfile);  read  (spsfile, sps);
     reset (symsetf);
     read  (symsetf,constbegsys,typebegsys,blockbegsys,facbegsys,statbegsys);
     stantyps := [notyp,ints,reals,bools,chars];
      lc := 0; ll := 0; cc := 0; ch := ' ';
      errpos := 0; errs := []; insymbol;
      t := -1; a := 0; b := 1; sx := 0; c2 := 0;
      display[0] := 1;
     iflag := false; oflag := false;
     if sy <> programsy then freeze{3}elsebegin insymbol;
       if sy <> ident then freeze{2}elsebegin progname := id; insymbol;
         if sy <> lparent then freeze{9}elserepeat insymbol;
     if sy <> ident then freeze{2}elsebeginif id = 'input     'then iflag := true elseif id = 'output    'then oflag := true else freeze{0};
       insymbol;
     enduntil sy <> comma;
          if sy = rparent then insymbol else freeze{4};
          ifnot oflag then freeze{20};
        endend ;
      reset (enterf);
      whilenot eof (enterf) dobeginread  (enterf,EnterRec );
        with EnterRec do
         enter (fx0,fx1,fx2,fx3);
       end;
      with btab[1] dobegin last := t; lastpar := 1; psize := 0; vsize := 0end ;
    end{init};
    procedurefinish;{%s-}beginif sy <> period then freeze{22};
      {emit(31)};   {halt}if btab[2].vsize > stacksize then freeze{49};
     end{finish};
    


    The% s- directive disables the animation and display of variable values ​​within the procedure in which it is specified.

    After making these changes, I downloaded and executed the first part (Pas1), and then the second. PascalS read the queens and began to broadcast them (see the picture at the beginning). It was difficult to follow the continuous animation of such a large code as PascalS, therefore it called freeze at key points and numbered the calls in the comments. Having understood the situation, continued the animation team Continue. I think that in modern IDE modern languages ​​such animators would be useful.

    The “games” described here did for a long time on 286 CPUs under MS DOS 3.2, now just launched old files to make pictures. In conclusion, I remembered an interesting fact about the distribution of Dr. Pascal. The basic delivery consisted of a user manual - a book of about 200 pages on good thick paper and a floppy disk. It cost $ 99.95US and was positioned as low cost software. Licenses for dozens of jobs in universities were much cheaper in terms of 1 copy. But besides the States and, npr., Australia, Dr. Pascal was popular in India. As far as I know, a local company was sold a license for distribution in India, and this company itself printed books (also in English 1: 1 with the original) and wrote floppy disks. The books were on newsprint with blind text, but the price was in terms of rupees about $ 4US. The same company replicated other products popular at the time, such asLOTUS 1-2-3 , dBase-4 , ChiWriter , etc. for about the same price.

    Also popular now: