home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d5xx / d524 / kamin.lha / Kamin / README.pl < prev    next >
Text File  |  1991-08-05  |  32KB  |  932 lines

  1. The directory distr contains the code associated with the book
  2. "Programming Languages: An Interpreter-based Approach," by Sam Kamin.
  3. Please report problems to:
  4.  
  5.     Sam Kamin            kamin@cs.uiuc.edu
  6.         Computer Science Dept.        (217) 333-8069
  7.         1304 W. Springfield
  8.         Urbana, IL 61801
  9.  
  10. The directory can be obtained either by copying the file distr.tar.Z,
  11. then uncompressing and de-tar'ing, or by copying the files directly
  12. out of the distr directory.  If copying the .Z file, be sure to set
  13. the "binary" flag.
  14.  
  15. CONTENTS OF THIS FILE
  16. =====================
  17.  
  18. This file contains a number of sections, delimited by a section
  19. name in capital letters and the "=====" underline.  The first
  20. several sections are notes about the distribution itself, the
  21. remainder contain code from various contributors to enhance the
  22. interpreters or fix portability bugs.
  23.  
  24. FILES: A description of each of the files in the distribution.
  25. TESTING THE CODE: How to test and install the interpreters on
  26.        Unix systems.
  27. PORTABILITY: Notes about where the interpreters run and where
  28.        they need (usually slight) changes.
  29. PROBLEMS: Possible things you might run into when testing/installing
  30. BUGS: Known bugs, mostly minor.
  31.  
  32. The following sections contain user-contributed code.  The first
  33. two relate to VAX VMS systems, the next three to Turbo Pascal, and
  34. the last provides an interpreter-independent load feature.  All
  35. are offered, needless to say, without warranty.
  36.  
  37. FIX TO VAX VMS CODE TO HANDLE LONG OUTPUT LINES
  38. TEST.INTERPS FOR VAX VMS
  39. THE PROCEDURE reader FOR TURBO PASCAL, VERSION 3.0
  40. THE PROCEDURE reader FOR TURBO PASCAL, VERSION 5.5
  41. A FIX FOR THE NON-LOCAL GOTO PROBLEM IN TURBO PASCAL FOR PC'S
  42. INTERPRETER-INDEPENDENT LOAD
  43. A FIX FOR MACINTOSH THINK PASCAL, AND MAYBE OTHERS
  44.  
  45. FILES
  46. =====
  47.  
  48. A copy of this file is contained in the distr directory, as well as
  49. a file called TEST.INTERPS, explained later.  Aside from these two
  50. files, there are three type of files in the directory:
  51.  
  52. 1. Interpreters (Pascal source files):
  53.  
  54.      chap1.p - Chapter 1
  55.      lisp.p - Lisp
  56.      apl.p - APL
  57.      scheme.p - Scheme
  58.      sasl.p - SASL
  59.      clu.p - CLU
  60.      smalltalk.p - Smalltalk
  61.      prolog.p - Prolog
  62.      lisp.stack.p - Lisp, using a stack (from Chapter 10)
  63.      lisp.ms-gc.p - Lisp, with mark-scan garbage collector (from Chapter 10)
  64.      lisp.ss-gc.p - Lisp, with semi-space garbage collector (from Chapter 10)
  65.      lisp.refcnt.p - Lisp, with reference-counting (from Chapter 10)
  66.  
  67. 2. Code files, including all code from chapters (plus some test cases
  68.    not appearing in text).  Note that you may be unable to run these
  69.    files as is due to memory limitations.  In that case, just split them
  70.    up and run the pieces separately.  (The Prolog code is given in two
  71.    pieces to avoid the problem of redefining predicates.)
  72.  
  73.      code.ch1 - Chapter 1
  74.      code.lsp - Lisp
  75.      code.apl - APL
  76.      code.sch - Scheme
  77.      code.ssl - SASL
  78.      code.clu - CLU
  79.      code.smt - Smalltalk
  80.      code1.pro - Prolog, part 1
  81.      code2.pro - Prolog, part 2
  82.      code.gc.lsp - Garbage-collecting Lisp's (This is the same as code.lsp,
  83.         but slightly shorter;  there is one example in code.lsp that needs
  84.         more memory than the garbage-collecting versions of Lisp allocate
  85.         as given.)
  86.         
  87. 3. Output of code files.  Use these to check that the interpreters
  88.    are running correctly.
  89.  
  90.      code.ch1.o - output from running chap1.p on code.ch1
  91.      code.lsp.o - output from running lisp.p on code.lsp
  92.      code.apl.o - output from running apl.p on code.apl
  93.      code.sch.o - output from running scheme.p on code.sch
  94.      code.ssl.o - output from running sasl.p on code.ssl
  95.      code.clu.o - output from running clu.p on code.clu
  96.      code.smt.o - output from running smalltalk.p on code.smt
  97.      code1.pro.o - output from running prolog.p on code1.pro
  98.      code2.pro.o - output from running prolog.p on code2.pro
  99.      code.stack.o - output from running lisp.stack.p on code.gc.lsp
  100.      code.ms-gc.o - output from running lisp.ms-gc.p on code.gc.lsp
  101.      code.ss-gc.o - output from running lisp.ss-gc.p on code.gc.lsp
  102.      code.refcnt.o - output from running lisp.refcnt.p on code.gc.lsp
  103.  
  104.  
  105. TESTING THE CODE
  106. ================
  107.  
  108. When running correctly, the result of executing the interpreter xxx
  109. on file code.xxx should be identical to file code.xxx.o.
  110.  
  111. On Unix systems, test the interpreters by running the shell procedure
  112. file TEST.INTERPS.  It compiles them, runs them on the code.xxx files, and
  113. compares the results to the code.xxx.o files.  Before running
  114. TEST.INTERPS, make sure the name of the Pascal compiler ("pc" by
  115. default) is correct.  TEST.INTERPS says what output to expect.
  116. It will leave the original files, plus the binaries of
  117. the various interpreters, with names: chap1, lisp, apl, scheme,
  118. sasl, clu, smalltalk, prolog, lisp-stack, lisp-msgc, lisp-ssgc,
  119. and lisp-refcnt.
  120.  
  121.  
  122. PORTABILITY
  123. ===========
  124.  
  125. The interpreters are written in standard Pascal, and should run
  126. without change in most systems, especially Unix systems.
  127. They have been tested on the following systems:
  128.  
  129.      Machine and O.S.: SUN 3/60, OS4.0
  130.      Compiler: "pc" (dated March, 1988)
  131.  
  132.      Machine and O.S.: Sequent Symmetry, Dynix V3.0.17.7
  133.      Compiler: "pascal" (V3.0.1, Silicon Valley Software, c. 1987)
  134.      (but see below)
  135.  
  136.      Machine and O.S.: DEC VAX 780, Unix 4.3bsd
  137.      Compiler: "pc" (v. 5.1, dated 6/5/85)
  138.  
  139.      Machine and O.S.: DEC VAX 780, VMS 5.3
  140.      Compiler: VAX Pascal V4.1
  141.  
  142.      Machine and O.S.: Encore Multimax
  143.      Compiler: "pc" (from Oregon Software, v. 1.5, dated 4/27/87)
  144.  
  145.      Machine and O.S.: Pyramid, OSx
  146.      Compiler: "pascal" ("man" page dated 1/20/87)
  147.  
  148.      Machine and O.S.: Mac II and SE/30, A/UX
  149.      Compiler: VP, from  Virginia Tech
  150.               (contact Mat Davis, davism@csgrad.cs.vt.edu)
  151.  
  152. However, the following portability problems have been noted:
  153.  
  154.      Sequent: Occurrences of "integer" should be changed to "longint"
  155.               (to give 32-bit integers), at least in smalltalk.p.  The
  156.               test cases in code.smt will not run correctly with the
  157.               default 16-bit integers.
  158.  
  159.      Multimax: The compiler (actually, the loader) reports an
  160.               error message on my system, but everything seems to
  161.               work fine anyway.  (This is probably a local problem.)
  162.  
  163.      VAX VMS (thanks to Mike Berman):  The default output buffer
  164.               is set too small for some programs.  In fact, the
  165.               problem only shows up in the Scheme test cases.
  166.               A fix is included below.  A minor problem is that
  167.               the filenames in this distribution are not legal,
  168.               as filenames in VMS can contain only one period.
  169.               A version of TEST.INTERPS for VMS is given below.
  170.  
  171.      HP9000 (thanks to Tim Budd):  The Pascal system automatically
  172.               breaks output lines at 256 characters and adds newlines
  173.               before closing files.  This causes two types of errors
  174.               when running the TEST.INTERPS script:  (1) Every
  175.               test gives a "missing newline" error;  (2) The Scheme
  176.               test produces a long list of errors, where code.sch.o
  177.               has long lines that are broken into two or three lines
  178.               by the interpreter (but are otherwise identical).  Both
  179.               types of errors are benign, and should be ignored.
  180.  
  181.      Turbo-Pascal on IBM PC's and compatibles:
  182.               There are two portability problems with this compiler:
  183.                 (1) It does not support non-local goto's, which are used
  184.                     in all the interpreters for aborting computations
  185.                     when errors occur.  Since they are only used in
  186.                     erroneous situations, one solution is just to replace
  187.                     them by "halt"s;  this is ugly, but a correct
  188.                     solution would be quite involved.
  189.                     (I have received a solution to this problem,
  190.                     which is given at the end of this file.)
  191.                 (2) The treatment of "eoln" is somewhat different from
  192.                     the Unix compilers, so the input routine "reader"
  193.                     has to be fixed in all interpreters (it is identical
  194.                     in all of them).  A correct version of reader
  195.                     for TP is given below (at least, I'm told it works).
  196.  
  197. PROBLEMS
  198. ========
  199.  
  200. The most likely source of problems when testing the interpreters is running
  201. out of memory.  To alleviate the problem, I have made the following
  202. adjustments:
  203.  
  204.     code.lsp: The "quit" has been inserted before
  205.                 line 414: "(r-e-p-loop '(", the first line of
  206.         an 86-line expression whose evaluation uses enormous
  207.         amounts of memory.
  208.  
  209.     code.sch: Line 305: "(differentiate '(Dx (+ x c)))" has
  210.                 been commented out.
  211.  
  212.     code2.pro: The second Prolog file, code2.pro, is not run by
  213.                 TEST.INTERPS.  It is very memory-hungry.
  214.  
  215. BUGS
  216. ====
  217.  
  218. There are several minor bugs that I am not intending to fix but
  219. which it might be good for you to know about.  (The reason I won't
  220. fix them is because I am very determined to keep the code in the book
  221. and the code actually used by students identical;  I believe it is
  222. pedagogically important that students not have the feeling that there
  223. are things going on behind their backs.)
  224.  
  225. 1. Normally, blank input is allowed.  The interpreters respond to
  226.   a newline entered at the -> prompt by ignoring it and issuing another
  227.   prompt.  However, if the very first thing typed to the interpreter is
  228.   a newline, it will not print the prompt.  (It will, however, accept
  229.   whatever is typed in at that point as legitimate input.  In other words,
  230.   the only problem is that the prompt is not typed.)  (Thanks to Tim Budd
  231.   for pointing this out.)
  232.  
  233. 2. The interpreters should respond more gracefully when the "quit" is
  234.   omitted from an input file.  In most versions of Pascal, the interpreter
  235.   aborts with an error message like "eof on input," but in some it loops,
  236.   which is really annoying.
  237.  
  238. 3. There are two places, one in clu.p and one in smalltalk.p, where
  239.   an incorrect error message is printed.  These errors are included in
  240.   the book on pages 550 and 563 (see the ERRATA file).  I have chosen
  241.   to fix these in the distributed code.
  242.  
  243. 4. In the scheme interpreter, the eval function, when applying a
  244.   function (in the APEXP case), does not check that the operator is
  245.   in fact a function.  Thus, an erroneous application like (3 4) will
  246.   cause the interpreter to crash rather than print an error message.
  247.   To fix this, lines 945-946, which currently read:
  248.  
  249.                else eval :=
  250.                       applyClosure(op, evalList(args))
  251.  
  252.   should be changed to:
  253.  
  254.                if op^.sxptype = CLOSXP
  255.                then eval := applyClosure(op, evalList(args))
  256.                else begin
  257.                        writeln('Attempted to apply a non-closure');
  258.                        goto 99;
  259.                     end
  260.  
  261.   (Thanks to Dirk Grunwald for this one.)
  262.  
  263.  
  264. FIX TO VAX VMS CODE TO HANDLE LONG OUTPUT LINES
  265. ===============================================
  266.  
  267. The following code was provided by Mike Berman of Glassboro State
  268. College, N.J.  Note that it goes at the very beginning of the
  269. top level.  Only the Scheme interpreter needs to be changed in
  270. order to run the test cases, but of course you may write programs
  271. with long output in any language.
  272.  
  273. begin (* scheme main *)
  274.  
  275. (* modification for VAX Pascal to allow long lines of output *)
  276. (*     Mike Berman, Glassboro State College, 10/29/90       *)
  277. (* set the value after record_length:= to whatever you think *)
  278. (* will be adequate for your environment; I chose 2^11 more  *)
  279. (* or less arbitrarily.                             *)
  280.  
  281.    open( output, record_length:=2048 );
  282.  
  283. (* end of modification *)
  284.  
  285.  
  286. TEST.INTERPS FOR VAX VMS
  287. ========================
  288.  
  289. This is also provided by Mike Berman.  He would like me to make
  290. it clear that this is strictly a hack and totally unsupported.
  291. However, it might save you some work, so here it is.  Note that he
  292. uses filenames that are different from those in this distribution.
  293. In particular, xxx.p becomes xxx.pas (required by VMS
  294. Pascal), and code.xxx.o becomes code-xxx.o (the former is not
  295. a legal filename in VMS).
  296.  
  297. $! test-interps.com
  298. $! translated from the unix shell script test-interps
  299. $! Michael Berman, Glassboro State College
  300. $! Berman@glassboro.edu
  301. $!
  302. $! There's a crude hack in here -- when you RUN in VMS with the /INPUT=
  303. $! parameter, it runs in the background and the COM file continues to
  304. $! execute.  Since the next thing that gets done is the DIFF of the
  305. $! output files, this fails because the program is still running and 
  306. $! hasn't finished creating the output files.  That's the reason for the
  307. $! WAIT 00:01 lines, which wait for 1 minute.  On my system, 1 minute was
  308. $! enough for all but the Smalltalk example, for which 2 minutes was adequate.
  309. $! Obviously, you may need to adjust these if your system is heavily loaded.
  310. $! I'm sure there's a better way to accomplish the same objective.
  311. $!
  312. $! This .com file also uses the convention that the files of the form 
  313. $! code.xxx.o (in Unix) have been renamed code-xxx.o, for VMS compatibility.
  314. $!
  315. $echo:==write sys$output
  316. $echo "This file compiles all interpreters, saving binaries, and"
  317. $echo "tests them by running them on files code.xxx (xxx the language name)"
  318. $echo "and comparing the output to the files code-xxx.o."
  319. $echo ""
  320. $echo "The expected output from running this file is the list of messages:"
  321. $echo ""
  322. $echo "Compiling CHAP1; binary is chap1"
  323. $echo "Running CHAP1"
  324. $echo "Number of difference sections found: 0"
  325. $echo "Number of difference records found: 0"
  326. $echo ".... etc. ..."
  327. $echo "Compiling LISP; binary is lisp"
  328. $echo "Running LISP"
  329. $echo ""
  330. $echo "and so on.  If the output from any interpreter differs from"
  331. $echo "what it should be, the DIFFERENCES command will report differences"
  332. $echo "in the files."
  333. $echo ""
  334. $echo "See the README file for more information"
  335. $echo ""
  336. $echo "Compiling CHAP1; binary is chap1"
  337. $pascal chap1.pas
  338. $link chap1
  339. $echo "Running CHAP1"
  340. $run/input=code.ch1/output=chap1.out chap1
  341. $wait 00:01
  342. $diff code-ch1.o chap1.out
  343. $del chap1.out;*
  344. $echo "Compiling LISP; binary is lisp"
  345. $pascal  lisp.pas
  346. $link lisp
  347. $echo "Running LISP"
  348. $run/input=code.lsp/output=lisp.out lisp  
  349. $wait 00:01
  350. $diff code-lsp.o lisp.out
  351. $del lisp.out;*
  352. $echo "Compiling APL; binary is apl"
  353. $pascal  apl.pas
  354. $link apl
  355. $echo "Running APL"
  356. $run /input=code.apl/output=apl.out apl 
  357. $wait 00:01
  358. $diff code-apl.o apl.out
  359. $del apl.out;1
  360. $echo "Compiling SCHEME; binary is scheme"
  361. $pascal  scheme.pas
  362. $link scheme
  363. $echo "Running SCHEME"
  364. $run /input=code.sch/output=scheme.out scheme 
  365. $wait 00:01
  366. $diff code-sch.o scheme.out
  367. $del scheme.out;1
  368. $echo "Compiling SASL; binary is sasl"
  369. $pascal  sasl.pas
  370. $link sasl
  371. $echo "Running SASL"
  372. $run /input=code.ssl /output=sasl.out sasl
  373. $wait 00:01
  374. $diff code-ssl.o sasl.out
  375. $del sasl.out;1
  376. $echo "Compiling CLU; binary is clu"
  377. $pascal  clu.pas
  378. $link clu
  379. $echo "Running CLU"
  380. $run/input=code.clu/output=clu.out clu 
  381. $wait 00:01
  382. $diff code-clu.o clu.out
  383. $del clu.out;1
  384. $echo "Compiling SMALLTALK; binary is smalltalk"
  385. $pascal  smalltalk.pas
  386. $link smalltalk
  387. $echo "Running SMALLTALK"
  388. $run /input=code.smt/output=smalltalk.out smalltalk 
  389. $wait 00:02
  390. $diff code-smt.o smalltalk.out
  391. $del smalltalk.out;1
  392. $echo "Compiling PROLOG; binary is prolog"
  393. $pascal  prolog.pas
  394. $link prolog
  395. $echo "Running PROLOG - file 1"
  396. $run/input=code1.pro/output=prolog1.out prolog 
  397. $wait 00:01
  398. $diff code1-pro.o prolog1.out
  399. $del prolog1.out;1
  400. $! echo "Running PROLOG - file 2"
  401. $! run /input=code2.pro/output=prolog2.out prolog 
  402. $! wait 00:01
  403. $! diff code2-pro.o prolog2.out
  404. $! del prolog2.out;1
  405. $echo "Compiling stacking version of LISP; binary is lisp-stack"
  406. $pascal  lisp-stack.pas
  407. $link lisp-stack
  408. $echo "Running stacking version of LISP"
  409. $run/input=code-gc.lsp/output=lisp-stack.out lisp-stack 
  410. $wait 00:01
  411. $diff code-stack.o lisp-stack.out
  412. $del lisp-stack.out;1
  413. $echo "Compiling mark-scan version of LISP; binary is lisp-msgc"
  414. $pascal  lisp-msgc.pas
  415. $link lisp-msgc
  416. $echo "Running mark-scan version of LISP"
  417. $run/input=code-gc.lsp/output=lisp-msgc.out lisp-msgc 
  418. $wait 00:01
  419. $diff code-msgc.o lisp-msgc.out
  420. $del lisp-msgc.out;1
  421. $echo "Compiling semi-space version of LISP; binary is lisp-ssgc"
  422. $pascal  lisp-ssgc.pas
  423. $link lisp-ssgc
  424. $echo "Running semi-space version of LISP"
  425. $run/input=code-gc.lsp/output=lisp-ssgc.out lisp-ssgc 
  426. $wait 00:01
  427. $diff code-ssgc.o lisp-ssgc.out
  428. $del lisp-ssgc.out;1
  429. $echo "Compiling reference-counting version of LISP; binary is lisp-refcnt"
  430. $pascal  lisp-refcnt.pas
  431. $link lisp-refcnt
  432. $echo "Running reference-counting version of LISP"
  433. $run /input=code-gc.lsp /output=lisp-refcnt.out lisp-refcnt 
  434. $wait 00:01
  435. $diff code-refcnt.o lisp-refcnt.out
  436. $del lisp-refcnt.out;1
  437.  
  438.  
  439. THE PROCEDURE reader FOR TURBO PASCAL, VERSION 3.0
  440. ==================================================
  441.  
  442. Simply replace the reader procedure in all interpreters by the
  443. following.  Together with replacing all occurrences of "goto 99"
  444. by "halt", the interpreters should run in TP.
  445.  
  446. (* reader - read char's into userinput; be sure input not blank  *)
  447. procedure reader;
  448.  
  449. (* readInput - read char's into userinput                        *)
  450.    procedure readInput;
  451.  
  452.    var c: char;
  453. (* nextchar - read next char - filter tabs and comments          *)
  454.       procedure nextchar (var c: char);
  455.       begin
  456.          read(con,c);
  457.          if c = chr(TABCODE)
  458.          then c := ' '
  459.          else if c = COMMENTCHAR
  460.               then begin
  461.                    while (c<>chr(13)) do read(con,c);
  462.                    read(con,c);
  463.                    c := ' ';
  464.                    end
  465.       end; (* nextchar *)
  466.  
  467. (* readParens - read char's, ignoring newlines, to matching ')'  *)
  468.       procedure readParens;
  469.       var
  470.          parencnt: integer; (* current depth of parentheses *)
  471.          c: char;
  472.       begin
  473.          parencnt := 1; (* '(' just read *)
  474.          repeat
  475.             nextchar(c);
  476.             if (c=chr(13)) then
  477.                begin
  478.                read(con,c);
  479.                write(PROMPT2);
  480.                c:=' ';
  481.                end;
  482.             pos := pos+1;
  483.             if pos = MAXINPUT
  484.             then begin
  485.                     writeln('User input too long');
  486.                     halt
  487.                  end;
  488.             userinput[pos] := c;
  489.             if c = '(' then parencnt := parencnt+1;
  490.             if c = ')' then parencnt := parencnt-1;
  491.          until parencnt = 0
  492.       end; (* readParens *)
  493.  
  494.    begin (* readInput *)
  495.       write(PROMPT);
  496.       pos := 0;
  497.       repeat
  498.          pos := pos+1;
  499.          if pos = MAXINPUT
  500.          then begin
  501.                  writeln('User input too long');
  502.                  halt
  503.               end;
  504.          nextchar(c);
  505.          if (c=chr(13)) then read(con,c);
  506.          userinput[pos] := c;
  507.          if userinput[pos] = '(' then readParens
  508.       until c=chr(10);
  509.       inputleng := pos;
  510.       userinput[pos+1] := COMMENTCHAR (* sentinel *);
  511.    end; (* readInput *)
  512.  
  513. begin (* reader *)
  514.     repeat
  515.        readInput;
  516.        pos := skipblanks(1);
  517.     until pos <= inputleng (* ignore blank lines *)
  518. end; (* reader *)
  519.  
  520.  
  521. THE PROCEDURE reader FOR TURBO PASCAL, VERSION 5.5
  522. ==================================================
  523.  
  524. Simply replace the reader procedure in all interpreters by the
  525. following.  Together with replacing all occurrences of "goto 99"
  526. by "halt", the interpreters should run in TP.  (Thanks to David
  527. Maharry of Wabash College in Indiana for this code.)
  528.  
  529. (* Chapter 1 interpreter from Kamin, modified for Turbo Pascal V5.5
  530.    using new Reader procedure
  531.  
  532.    By: David E. Maharry
  533.        Wabash College
  534.    Date: June 6, 1990
  535. *)
  536.  
  537. (* reader - read char's into userinput; be sure input not blank  *)
  538. procedure reader;
  539.  
  540. (* readInput - read char's into userinput                        *)
  541.    procedure readInput;
  542.  
  543.    const LINEMAX = 80;
  544.  
  545.    type line_type = String[LINEMAX];
  546.  
  547.    var line : line_type;
  548.        parencount : INTEGER;
  549.        inputdone : BOOLEAN;
  550.        i: INTEGER;
  551.  
  552. (* checkForComment - function to look and find commentchar in line *)
  553.    function checkforcomment(line : line_type):integer;
  554.  
  555.    (* returns 0 if no comment, else position of commentchar *)
  556.    var found : boolean;
  557.        i : integer;
  558.    begin
  559.      found := FALSE;
  560.      i := 0;
  561.      while (i < length(line)) and (not found) do
  562.        begin
  563.          i := i + 1;
  564.          found := (line[i] = COMMENTCHAR)
  565.        end;
  566.      if found then checkforcomment := i
  567.               else checkforcomment := 0
  568.    end; (* checkforcomment *)
  569.  
  570. (* filters out tabs and comments *)
  571.    procedure filter ( var line : line_type );
  572.  
  573.    var temp : line_type;
  574.        i : INTEGER;
  575.    begin
  576.        temp := '';
  577.        for i := 1 to length(line) do
  578.            if line[i] = chr(TABCODE)
  579.               then temp := temp + ' '
  580.               else temp := temp + line[i];
  581.        i := checkforcomment(line);
  582.        if i <> 0 then line := copy(temp, 1, i-1)
  583.                  else line := temp
  584.    end; (* filter *)
  585.  
  586. (* matches left and right parens in line *)
  587.    function countparen(line : line_type): INTEGER;
  588.  
  589.    var i, count : INTEGER;
  590.    begin
  591.       count := 0;
  592.       for i := 1 to length(line) do
  593.         begin
  594.           if line[i] = '(' then count := count + 1
  595.           else if line[i] = ')' then count := count - 1;
  596.         end;
  597.       countparen := count
  598.    end; (* countparen *)
  599.  
  600.    begin (* readInput *)
  601.       parencount := 0;
  602.       inputleng := 0;
  603.       repeat
  604.          if inputleng = 0 then write(PROMPT)
  605.                           else write(PROMPT2);
  606.          readln(line);
  607.          filter(line);
  608.          parencount := parencount + countparen(line);
  609.          if parencount < 0 then begin
  610.                                   writeln('Invalid parentheses.');
  611.                                   halt(1)
  612.                                 end;
  613.          inputdone := (parencount = 0);
  614.          if inputleng + length(line) >= MAXINPUT
  615.          then begin
  616.                  writeln('User input too long');
  617.                  halt(1)
  618.               end;
  619.          if inputleng > 0 then
  620. (* puts a space between input lines *)
  621.            begin
  622.              inputleng := inputleng + 1;
  623.              userinput[inputleng] := ' '
  624.            end;
  625.          for i := 1 to length(line) do
  626.            begin
  627.              inputleng := inputleng + 1;
  628.              userinput[inputleng] := line[i]
  629.            end
  630.       until inputdone;
  631.       userinput[inputleng+1] := COMMENTCHAR (* sentinel *)
  632.    end; (* readInput *)
  633.  
  634. begin (* reader *)
  635.     repeat
  636.        readInput;
  637.        pos := skipblanks(1);
  638.     until pos <= inputleng; (* ignore blank lines *)
  639. end; (* reader *)
  640.  
  641.  
  642. A FIX FOR THE NON-LOCAL GOTO PROBLEM IN TURBO PASCAL FOR PC'S
  643. =============================================================
  644.  
  645. I have received this fix from Norm Neff of Trenton State College
  646. (neff@pilot.njin.net).  He says it should work on TP versions 4.0
  647. and up, but adds "it's been tested [on TP 5.0], but not class-
  648. tested."
  649.  
  650. (* **BEGIN*** DECL99.PAS Declarations for goto ************** *)
  651. (* Steps to enable nonlocal goto, TP v. 5.0
  652.   1.  Insert these declarations, first in the main program's decl part
  653.   2.  Insert contents of DEF99.PAS in the main program's stmt part,
  654.       immediately prior to the statement labeled 99.
  655.   3.  Change each 'goto 99' statement of the original program to the
  656.       procedure call 'goto99'.  Also, use 'goto99' 's in the reader.
  657.  
  658.   Prepared by
  659.    Norman Neff
  660.    Computer Science Department
  661.    Trenton State College
  662.    Hillwood Lakes CN550
  663.    Ewing Township, NJ 08625-0550 
  664.    neff@pilot.njin.net                                          *)
  665.  
  666. var  cs99,ss99,ds99,ip99,sp99,bp99:integer;(*must be global*)
  667.  
  668. procedure goto99;
  669. var Dslocal:integer;
  670.     destination: record
  671.                  ip,cs: integer
  672.                  end;
  673. begin  (* Restores DS , CS, IP *)
  674.  DSlocal := ds99;
  675.  destination.ip := ip99;
  676.  destination.cs := cs99;
  677.   inline  (* ADDRESSING wrt SS:BP *)
  678.     ($8E/$9E/Dslocal/       (* MOV       DS,DsLocal[BP]   *)
  679.      $FF/$AE/destination ); (* JMP FAR  ( destination[BP])   *)
  680. end;
  681.  
  682. procedure SaveIP(var ipv:integer);
  683. (* ipv := return address *)
  684. var iphold:integer;
  685. begin
  686.   inline  (* ADDRESSING wrt SS:BP *)
  687.     ( $53/               (* PUSH BX *)
  688.       $8B/$9E/$02/$00/   (* MOV BX, 2[BP]      *)
  689.       $89/$9E/IPHOLD/    (* MOV iphold[BP],BX  *)
  690.       $5B                (* POP BX *) );
  691.   ipv := iphold;
  692. end;
  693. (* **END***** DECL99.PAS Declarations for goto ************** *)
  694.  
  695.  
  696.  (*  *****BEGIN**** DEF99.PAS ***************************  *)
  697.  (*  To be inserted immediately before the statement labeled 99
  698.    in the program's top level  *)
  699.  
  700.   inline   (*  save state *)
  701.     ($89/$2E/BP99/       (* MOV  BP99,BP *)
  702.      $89/$26/SP99/       (* MOV  SP99,SP *)
  703.      $8C/$16/SS99/       (* MOV  SS99,SS *)
  704.      $8C/$1E/DS99/       (* MOV  DS99,DS *)
  705.      $8C/$0E/CS99);      (* MOV  CS99,CS *)
  706.   SaveIP(ip99);
  707.   (*  call to goto99  jumps to this entry point *)
  708.   inline  (*  complete restoring state  *)
  709.     ($8E/$16/SS99/       (* MOV  SS,SS99 *)
  710.      $8B/$26/SP99/       (* MOV  SP,SP99 *)
  711.      $8B/$2E/BP99);      (* MOV  BP,BP99 *)
  712.  
  713.  (*               ENTRY POINT 99 FOLLOWS    :           *)
  714.  (*  *****END****** DEF99.PAS ************************  *)
  715.  
  716.  
  717. INTERPRETER-INDEPENDENT LOAD
  718. ============================
  719.  
  720. The following is from Paul Mullins of Youngstown State University.
  721. I tried it and it worked fine for me.  Remember that before using it,
  722. you have to compile the "frontend.c" file (naming the program "frontend")
  723. and modify the variable "$INTERPS" to give the directory containing
  724. the interpreters.  Also, when run with this system, if you terminate
  725. with "quit", you have to enter two carriage-returns before the program
  726. will stop;  you can also terminate with a single CTRL-D.
  727.  
  728. I might point out that on Unix systems a cheap way to load a single
  729. file at the beginning of an interpreter session, followed by input
  730. from the terminal, is this:
  731.  
  732. cat <input> - | <interpreter>
  733.  
  734. in which the file <input> is the program to be loaded (without a
  735. "quit" at the end!).  Again, you will find you have to enter
  736. carriage-return twice at the end of the session after typing "quit".
  737. Alternatively, you can create a file "quitfile" containing the
  738. single line "quit", and start it like this:
  739.  
  740. cat <input> - quitfile | <interpreter>
  741.  
  742. in which case the session is terminated by entering CTRL-D.
  743.  
  744. (Gary Leavens of Iowa State Univ. points out that this has to be
  745. done slightly differently on HP-UX, and possibly on other systems,
  746. because cat buffers its input.  On those systems, you should say:
  747.  
  748. cat -u <input> - | <interpreter>
  749. )
  750.  
  751. The remainder of this section is quoted directly from Professor Mullins:
  752.  
  753. The code which follows is provided without warranty or copyright.  Use it
  754. or not as you see fit.  Sample session:
  755.  
  756.     $ fe prolog in1 in2
  757.       reading in1
  758.       done reading in1
  759.       reading in2
  760.       done reading in2
  761.     -> > > > satisfied
  762.     -> > > not satisfied
  763.     -> load in3
  764.       reading in3
  765.       done reading in3
  766.     ->
  767.     satisfied
  768.     -> ^d
  769.     $
  770.  
  771. ----------------------------------------------------------------------------
  772. Paul M. Mullins                      mullins@macs.ysu.edu
  773. Dept of Mathematical                 FC137501@YUB
  774.  and Computer Sciences               fc137501@ysub.ysu.edu
  775. Youngstown State University
  776. Youngstown, OH 44555-0001            (216) 742-3796
  777.  
  778.  
  779. SHAR FILE - feed to sh
  780. -------------------------- cut here ------------------------------------------
  781. #! /bin/sh
  782. # This is a shell archive.  Remove anything before this line, then unpack
  783. # it by saving it into a file and typing "sh file".  To overwrite existing
  784. # files, type "sh file -c".  You can also feed this as standard input via
  785. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  786. # will see the following message at the end:
  787. #        "End of shell archive."
  788. # Contents:  fe frontend.c
  789. # Wrapped by mullins@ysumax on Wed Nov 28 13:20:34 1990
  790. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  791. if test -f 'fe' -a "${1}" != "-c" ; then 
  792.   echo shar: Will not clobber existing file \"'fe'\"
  793. else
  794. echo shar: Extracting \"'fe'\" \(793 characters\)
  795. sed "s/^X//" >'fe' <<'END_OF_FILE'
  796. X#! /bin/sh
  797. X# NOT copyright by Paul Mullins
  798. X# You may distribute, modify, remove my name, ...
  799. X# This is a front end for various interpreters which are located
  800. X# in $INTERPS.  It invokes a C frontend which allow command files
  801. X# to be sent to the interpreter.  A list of files to be preloaded
  802. X# may be specified on the command line.
  803. X
  804. X# site dependent
  805. XINTERPS=distr
  806. X
  807. X# check argument count
  808. Xcase $# in
  809. X0) echo usage: $0 interp_name \[file_list\]; exit 0;;
  810. X*) ;;
  811. Xesac
  812. X
  813. X# Modify CMD to the string most appropriate for the named interpreter
  814. Xcase $1 in
  815. Xchap1 | lisp | apl | scheme | sasl | clu | smalltalk) CMD=load;;
  816. Xprolog) CMD=consult;;
  817. Xlisp-msgc | lisp-ssgc | lisp-stack) CMD=load;;
  818. X*) echo unknown interpreter $1; exit 0;;
  819. Xesac
  820. X
  821. XINT=$1
  822. Xshift
  823. X
  824. X# invoke it
  825. X$INTERPS/frontend $CMD $@ | $INTERPS/$INT
  826. END_OF_FILE
  827. if test 793 -ne `wc -c <'fe'`; then
  828.     echo shar: \"'fe'\" unpacked with wrong size!
  829. fi
  830. chmod +x 'fe'
  831. # end of 'fe'
  832. fi
  833. if test -f 'frontend.c' -a "${1}" != "-c" ; then 
  834.   echo shar: Will not clobber existing file \"'frontend.c'\"
  835. else
  836. echo shar: Extracting \"'frontend.c'\" \(1098 characters\)
  837. sed "s/^X//" >'frontend.c' <<'END_OF_FILE'
  838. X/* invocation: frontend string [file_list] */
  839. X/* NOT copyright Paul M. Mullins
  840. X * You may copy, modify, remove my name, ...
  841. X */
  842. X#include <stdio.h>
  843. X#include <ctype.h>
  844. X#define MAXLINE 1024
  845. X/* cmd is a prefix string which is followed by a blank and a file name */
  846. X/* uses brain damaged approach - leading blanks aren't stripped, etc */
  847. Xchar *cmd;
  848. X
  849. Xvoid fileread (fn)
  850. Xchar *fn;
  851. X{
  852. XFILE *fp, *fopen();
  853. Xint c;
  854. X
  855. X fprintf(stderr,"\tReading file %s\n", fn);
  856. X if ((fp=fopen(fn,"r"))==NULL) {
  857. X     fprintf(stderr,"\tRead failed\n");
  858. X     exit(1);
  859. X     }
  860. X while ((c=getc(fp))!=EOF)
  861. X     putchar(c);
  862. X fflush(stdout);
  863. X fprintf(stderr,"\tDone reading %s\n",fn);
  864. X}
  865. X
  866. Xmain (argc, argv)
  867. Xint argc;
  868. Xchar **argv;
  869. X{
  870. Xint i;
  871. Xchar line[MAXLINE];
  872. X
  873. X if (argc < 2) {
  874. X    fprintf(stderr,"usage: %s cmd_string [file_list]\n",argv[0]);
  875. X    exit (1);
  876. X    }
  877. X else cmd = argv[1];
  878. X
  879. X for (i=2; i<argc; i++)
  880. X    fileread(argv[i]);
  881. X
  882. X while (gets(line) != NULL) {
  883. X    if (strncmp(cmd,line,strlen(cmd))==0) {
  884. X        for (i=strlen(cmd); isspace(line[i]) && (i < MAXLINE); i++);
  885. X        fileread (line+i);
  886. X        }
  887. X    else
  888. X        printf("%s\n",line);
  889. X    fflush(stdout);
  890. X    }
  891. X printf("quit\n");
  892. X}
  893. END_OF_FILE
  894. if test 1098 -ne `wc -c <'frontend.c'`; then
  895.     echo shar: \"'frontend.c'\" unpacked with wrong size!
  896. fi
  897. # end of 'frontend.c'
  898. fi
  899. echo shar: End of shell archive.
  900. exit 0
  901.  
  902.  
  903. A FIX FOR MACINTOSH THINK PASCAL, AND MAYBE OTHERS
  904. ==================================================
  905.  
  906. Walt Leipold (leipolw%esvax@dupont.com) points out that my input
  907. code (procedure nextchar) assumes you can "read" (as opposed to
  908. "readln") past end-of-line.  He has found that Symantec's Think
  909. Pascal for the Macintosh does not allow this.  He suggests
  910. replacing nextchar by the following code.  The change should
  911. be benign in any case, and may apply to other Pascal's, though
  912. I don't know any examples.
  913.  
  914. (* nextchar - read next char - filter tabs and comments *)
  915.     procedure nextchar (var c: char);
  916.     begin
  917.         if eoln then begin
  918.             readln;
  919.             c := ' '
  920.             end
  921.         else begin
  922.             read(c);
  923.             if c = chr(TABCODE) then
  924.                 c := ' '
  925.             else if c = COMMENTCHAR then begin
  926.                 while not eoln do
  927.                     read(c);
  928.                 c := ' '
  929.                 end
  930.             end;
  931.     end; (* nextchar *)
  932.