rs@uunet.UU.NET (Rich Salz) (07/30/87)
Submitted-by: Per Bergsten <mcvax!enea!chalmers!holtec!perb> Posting-number: Volume 10, Issue 76 Archive-name: ptoc/Part12 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 12 (of 12)." # Contents: ptc.p.1 if test -f 'ptc.p.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ptc.p.1'\" else echo shar: Extracting \"'ptc.p.1'\" \(59347 characters\) sed "s/^X//" >'ptc.p.1' <<'END_OF_FILE' X(***************************************************************************) X(***************************************************************************) X(** **) X(** Copyright (C) 1987 by Per Bergsten, Gothenburg, Sweden **) X(** **) X(** No part of this program, or parts derived from this program, **) X(** may be sold, hired or otherwise exploited without the author's **) X(** written consent. **) X(** **) X(** The program may be freely redistributed provided that: **) X(** **) X(** 1) the original program text, including this notice, **) X(** is reproduced unaltered, **) X(** 2) no charge (other than a nominal media cost) is **) X(** demanded for the copy. **) X(** **) X(** The program may be included in a package only on the condition **) X(** that the package as a whole is distributed at media cost. **) X(** **) X(***************************************************************************) X(***************************************************************************) X(** **) X(** The program ptc is a Pascal-to-C translator. **) X(** It accepts a correct Pascal program and creates a C program **) X(** with the same behaviour. It is not a complete compiler in the **) X(** sense that it does NOT do complete typechecking or error- **) X(** reporting. Only a minimal typecheck is done so that the meaning **) X(** of each construct can be determined. Therefore, an incorrect **) X(** Pascal program can easily cause the translator to malfunction. **) X(** **) X(***************************************************************************) X(***************************************************************************) X(** **) X(** Things which are known to be dependent on the underlying cha- **) X(** racterset are marked with a comment containing the word CHAR. **) X(** Things that are known to be dependent on the host operating **) X(** system are marked with a comment containing the word OS. **) X(** Things known to be dependent on the cpu and/or the target C- **) X(** implementation are marked with the word CPU. **) X(** Things dependent on the target C-library are marked with LIB. **) X(** **) X(** The code generated by the translator assumes that there is a **) X(** C-implementation with at least a reasonable <stdio> library **) X(** since all input/output is implemented in terms of C functions **) X(** like fprintf(), getc(), fopen(), rewind() etc. **) X(** If the source-program uses Pascal functions like sin(), sqrt() **) X(** etc, there must also exist such functions in the C-library. **) X(** **) X(***************************************************************************) X(***************************************************************************) X Xprogram ptc(input, output); X Xlabel 9999; (* end of program *) X Xconst version = '@(#)ptc.p 1.5 Date 87/05/01'; X X keytablen = 38; (* nr of keywords *) X keywordlen = 10; (* length of a keyword *) X othersym = 'otherwise '; (* keyword for others *) X externsym = 'external '; (* keyword for external *) X dummysym = ' '; (* dummy keyword *) X X (* a Pascal set is implemented as an array of "wordtype" where *) X (* each element contains bits numbered from 0 to "setbits" *) X wordtype = 'unsigned short'; (* CPU *) X setbits = 15; (* CPU *) X X (* a Pascal file is implemented as a struct which (among other *) X (* things) contain a flag-field, currently 3 bits are used *) X filebits = 'unsigned short'; (* flags for files *) X filefill = 12; (* 16 less used 3 bits *) X X maxsetrange = 15; (* nr of words in a set *) X scalbase = 0; (* ordinal value of first scalar member *) X X maxprio = 7; X X maxmachdefs = 8; (* max nr of machine integer types *) X machdeflen = 16; (* max length of machine int type name *) X X (* limit of identifier table, identifiers and strings are saved *) X (* in an array 0 .. maxblkcnt of ^ array 0 .. maxstrblk of char *) X maxstrblk = 1023; X maxblkcnt = 63; X maxstrstor = 65535; (* maxstrstor should be == X (maxblkcnt+1) * (maxstrblk+1) - 1 *) X X maxtoknlen = 127; (* max size of token (i.e. identifier, X string or number); must be > keywordlen X and should be <= 256, see hashtokn() *) X X hashmax = 64; (* size of hashtable - 1 *) X X null = 0; (* "impossible" character value, CHAR; X a char with this value is used as delimiter X of strings in "strstor" and in toknbuffers; X it is also used as end-of-input marker by X the input procedures in lexical analysis *) X X minchar = null; X maxchar = 127; (* greatest possible character, CHAR; limits X the number of elements in type "char" *) X X (* tmpfilename is used in the generated code to obtain names of X temporary files for reset/rewrite, the last character is supplied X by the reset/rewrite routine *) X tmpfilename = '"/tmp/ptc%d%c", getpid(), '; (* OS *) X X (* some frequently used characters *) X space = ' '; X tab1 = ' '; X tab2 = ' '; X tab3 = ' '; X tab4 = ' '; X bslash = '\'; X nlchr = '''\n'''; X ffchr = '''\f'''; X nulchr = '''\0'''; X spchr = ''' '''; X quote = ''''; X cite = '"'; X xpnent = 'e'; (* exponent char in output. CPU *) X percent = '%'; X uscore = '_'; X badchr = '?'; (* CHAR *) X okchr = quote; (* CHAR *) X X tabwidth = 8; (* width of a tab-stop. OS *) X X echo = false; (* echo input as read *) X diffcomm = false; (* comment delimiters different *) X lazyfor = false; (* compile for-stmts a la C *) X unionnew = true; (* malloc unions for variants *) X X inttyp = 'int'; (* for predefined functions *) X chartyp = 'char'; X setwtyp = 'setword'; X setptyp = 'setptr'; X floattyp = 'float'; X doubletyp = 'double'; X dblcast = '(double)'; (* for predefined functions *) X X realtyp = doubletyp; (* user real-vars and functions *) X X voidtyp = 'void'; (* for procedures *) X voidcast = '(void)'; X X intlen = 10; (* length of written integer *) X fixlen = 20; (* length of written real *) X Xtype X hashtyp = 0 .. hashmax; (* index to hash-tables *) X X strindx = 0 .. maxstrstor; (* index to "strstor" *) X X (* string-table "strstor" is implemented as an array that is grown X dynamically by adding blocks when needed *) X strbidx = 0 .. maxstrblk; X strblk = array [ strbidx ] of char; X strptr = ^ strblk; X strbcnt = 0 .. maxblkcnt; X X (* table for stored identifiers *) X (* an identifier in any scope is represented by an idnode which is X hooked to a slot in "idtab" as determined by a hash-function. X whenever the input procedures find an identifier its idnode is X immediately located, or created, if none was found; the identifier X is then always handled though a pointer to the idnode. the actual X text of the identifier is stored in "strstor". *) X idptr = ^ idnode; X idnode = record X inext : idptr; (* chain of idnode's *) X inref : 0 .. 127; (* # of refs to this id *) X ihash : hashtyp; (* its hash value *) X istr : strindx; (* index to "strstor" *) X end; X X (* toknbuf is used to handle identifiers and strings in those situations X where the actual text is of intrest *) X toknidx = 1 .. maxtoknlen; X toknbuf = array [ toknidx ] of char; X X (* a type to hold Pascal keywords *) X keyword = packed array [ 1 .. keywordlen ] of char; X X (* predefined identifier enumeration *) X predefs = ( X dabs, darctan, dargc, dargv, X dboolean, dchar, dchr, dclose, X dcos, ddispose, deof, deoln, X dexit, dexp, dfalse, dflush, X dget, dhalt, dinput, dinteger, X dln, dmaxint, dmessage, dnew, X dodd, dord, doutput, dpage, X dpack, dpred, dput, dread, X dreadln, dreal, dreset, drewrite, X dround, dsin, dsqr, dsqrt, X dsucc, dtext, dtrue, dtrunc, X dtan, dwrite, dwriteln, dunpack, X dzinit, dztring X ); X X (* lexical symbol enumeration *) X symtyp = ( X (* keywords and eof are sorted alphabetically ...... *) X sand, sarray, sbegin, scase, X sconst, sdiv, sdo, sdownto, X selse, send, sextern, sfile, X sfor, sforward, sfunc, sgoto, X sif, sinn, slabel, smod, X snil, snot, sof, sor, X sother, spacked, sproc, spgm, X srecord, srepeat, sset, sthen, X sto, stype, suntil, svar, X swhile, swith, seof, X (* ...... sorted *) X sinteger, X sreal, sstring, schar, sid, X splus, sminus, smul, squot, X sarrow, slpar, srpar, slbrack, X srbrack, seq, sne, slt, X sle, sgt, sge, scomma, X scolon, ssemic, sassign, sdotdot, X sdot X ); X symset = set of symtyp; X X (* lexical symbol definition *) X (* the lexical symbol holds a descriptor and the value of a symbol X read by the input procedures; note that real values are represented X as strings saved in "strstor" like ordinary strings to avoid using X float-variables and float-arithmetic in the translator *) X lexsym = X record X case st : symtyp of X sid: (vid : idptr); X schar: (vchr : char); X sinteger: (vint : integer); X sreal: (vflt : strindx); X sstring: (vstr : strindx); X end; X X (* enumeration of symnode variants *) X ltypes = ( X lpredef, lidentifier, lfield, lforward, X lpointer, lstring, llabel, lforwlab, X linteger, lreal, lcharacter X ); X X declptr = ^ declnode; X treeptr = ^ treenode; X symptr = ^ symnode; X (* identifier/literal symbol definition *) X (* in a given scope an identifier or a label is uniquely represented X by a "symnode"; in order to have a uniform treatment of all objects X occurring in the same syntactical positions (and hence in the parse- X tree) the literal constants are represented in a similar manner *) X symnode = X record X lsymdecl : treeptr; (* symbol decl. point *) X lnext : symptr; (* symtab chain pointer *) X ldecl : declptr; (* backptr to symtab *) X case lt : ltypes of X lpredef, (* a predefined id *) X lfield, (* a record field *) X lpointer, (* a pointer id *) X lidentifier, (* an identifier *) X lforward: X ( X lid : idptr; (* ptr to its idnode *) X lused : boolean (* true if symbol used *) X ); X lstring: (* a string literal *) X ( X lstr : strindx (* index to "strstor" *) X ); X lreal: (* a real literal *) X ( X lfloat : strindx (* index to "strstor" *) X ); X lforwlab, (* a declared label *) X llabel: (* label decl & defined *) X ( X lno : integer; (* label number *) X lgo : boolean (* non-local usage *) X ); X linteger: (* an integer literal *) X ( X linum : integer (* its value *) X ); X lcharacter: (* a character literal *) X ( X lchar : char (* its value *) X ) X end; X X (* symbol table definition *) X (* the symbol table consists of symnodes chained along the lnext X field; the nodes are connected in reverse order of occurence (last X declared, first in chain) in the slot in the declnode determined X by the hashfunction; when a new scope is entered a new declnode is X manufactured and the previous one is hooked to the dprev field, thus X nested scopes are represented by a list of declnodes *) X declnode = record X dprev : declptr; X ddecl : array [ hashtyp ] of symptr X end; X X (* enumeration of nodes in parse tree *) X (* NOTE: the subrange [ assignment .. nil ] have priorities *) X treetyp = ( X npredef, npgm, nfunc, nproc, X nlabel, nconst, ntype, nvar, X nvalpar, nvarpar, nparproc, nparfunc, X nsubrange, nvariant, nfield, nrecord, X narray, nconfarr, nfileof, nsetof, X nbegin, nptr, nscalar, nif, X nwhile, nrepeat, nfor, ncase, X nchoise, ngoto, nwith, nwithvar, X nempty, nlabstmt, nassign, nformat, X nin, neq, nne, nlt, X nle, ngt, nge, nor, X nplus, nminus, nand, nmul, X ndiv, nmod, nquot, nnot, X numinus, nuplus, nset, nrange, X nindex, nselect, nderef, ncall, X nid, nchar, ninteger, nreal, X nstring, nnil, npush, npop, X nbreak X ); X X (* enumeration of predefined types *) X pretyps = ( X tnone, tboolean, tchar, tinteger, X treal, tstring, tnil, tset, X ttext, tpoly, terror X ); X X (* enumeration of some special attributes *) X attributes = ( X anone, aregister, aextern, areference X ); X X (* parse tree definition *) X (* the sourceprogram is represented by a treestructure built from X treenodes where each node corresponds to one syntactic form from X the pascal program *) X treenode = X record X tnext, (* ptr to next node in a list *) X ttype, (* pointer to nodes type *) X tup : treeptr; (* ptr to parent node *) X case tt : treetyp of X npredef: (* predefined object decl *) X ( X tdef: (* predefined object descr. *) X predefs; X tobtyp: (* object type *) X pretyps X ); X npgm, (* program declaration *) X nproc, (* procedure declaration *) X nfunc: (* function declaration *) X ( X tsubid, (* subr. identifier (nid) *) X tsubpar, (* parameter list *) X tfuntyp, (* function type (nid) *) X tsublab, (* label decl list (nlabel) *) X tsubconst, (* const decl list (nconst) *) X tsubtype, (* type decl list (ntype) *) X tsubvar, (* var decl list (nvar) *) X tsubsub, (* subr. decl (nproc/nfunc) *) X tsubstmt: (* stmt. list (NOT nbegin) *) X treeptr; X tstat: (* static declaration level *) X integer; X tscope: (* symbol table for local id's *) X declptr X ); X nvalpar, (* value parameter declaration *) X nvarpar, (* var parameter declaration *) X nconst, (* constant declaration *) X ntype, (* type declaration *) X nfield, (* record field declaration *) X nvar: (* var declaration declaration *) X ( X tidl, (* list of declared id's (nid) *) X tbind: (* var/type-type, const-value *) X treeptr; X tattr: (* special attributes for vars *) X attributes X ); X nparproc, (* parameter procedure *) X nparfunc: (* parameter function *) X ( X tparid, (* parm proc/func id (nid) *) X tparparm, (* parm proc/func parm decl *) X tpartyp: (* parm func type (nid) *) X treeptr X ); X nptr: (* pointer constructor *) X ( X tptrid: (* referenced type (nid) *) X treeptr; X tptrflag: (* have seen node before *) X boolean X ); X nscalar: (* scalar type constructor *) X ( X tscalid: (* list of scalar ids (nid) *) X treeptr X ); X nfileof, (* file type constructor *) X nsetof: (* set type constructor *) X ( X tof: (* set/file component type *) X treeptr X ); X nsubrange: (* subrange type constructor *) X ( X tlo, thi: (* subrange limits *) X treeptr X ); X nvariant: (* record variant constructor *) X ( X tselct, (* selector list (constants) *) X tvrnt: (* variant field decl (nrecord) *) X treeptr X ); X X (* the tuid field is used to attach a name to variants since X C requires all union members to have names *) X nrecord: (* record/variant constructor *) X ( X tflist, (* fixed field list (nfield) *) X tvlist: (* variant list (nvariant) *) X treeptr; X tuid: (* variant name *) X idptr; X trscope: (* symbol table for local id's *) X declptr X ); X nconfarr: (* conformant array constructor *) X ( X tcindx, (* index declaration *) X tindtyp, (* conf. arr. index type (nid) *) X tcelem: (* array element type decl *) X treeptr; X tcuid: (* variant name *) X idptr X ); X narray: (* array type constructor *) X ( X taindx, (* index declaration *) X taelem: (* array element type decl *) X treeptr X ); X nbegin: (* begin statement *) X ( X tbegin: (* statement list *) X treeptr X ); X nlabstmt: (* labeled statement *) X ( X tlabno, (* label number (nlabel) *) X tstmt: (* statement *) X treeptr X ); X ngoto: (* goto statement *) X ( X tlabel: (* label to go to (nlabel) *) X treeptr X ); X X nassign: (* assignment statement *) X ( X tlhs, (* variable *) X trhs: (* value *) X treeptr X ); X X (* npush/npop is used in proc/func which have local variables X used in local proc/funcs; those variables are converted to X global ptrs initialized to reference the local variable *) X npush, (* init code for proc/func *) X npop: (* exit code for proc/func *) X ( X tglob, (* global identifier (nid) *) X tloc, (* local identifier (nid) *) X ttmp: (* temp store for global (nid) *) X treeptr X ); X X nbreak: X ( X tbrkid, (* for-variable *) X tbrkxp: (* value for break *) X treeptr X ); X X ncall: (* procedure/function call *) X ( X tcall, (* called identifier *) X taparm: (* actual paramters *) X treeptr X ); X nif: (* if statement *) X ( X tifxp, (* conditional expression *) X tthen, (* stmt execd if true condition *) X telse: (* stmt execd if true condition *) X treeptr X ); X nwhile: (* while statemnet *) X ( X twhixp, (* conditional expression *) X twhistmt: (* stmt execd if true condition *) X treeptr X ); X nrepeat: (* repeat statement *) X ( X treptstmt, (* statement list *) X treptxp: (* conditional expression *) X treeptr X ); X nfor: (* for statement *) X ( X tforid, (* loop control variable (nid) *) X tfrom, (* initial value *) X tto, (* final value *) X tforstmt: (* stmt execd in loop *) X treeptr; X tincr: (* to/downto flag true <==> to *) X boolean X ); X ncase: (* case statement *) X ( X tcasxp, (* selecting expression *) X tcaslst, (* list of choises *) X tcasother: (* default action *) X treeptr X ); X nchoise: (* a choise in a case-stmt *) X ( X tchocon, (* list of constants *) X tchostmt: (* execd statement *) X treeptr X ); X nwith: (* with statment *) X ( X twithvar, (* list of variables (nwithvar) *) X twithstmt: (* statement execd in new scope *) X treeptr X ); X X (* the local symbol table holds identifiers, picked from X the record fields, temporarily declared during parsing X of remainder of with-statement; these identifiers are X later converted into fields referenced through a ptr *) X nwithvar: (* variable in with statement *) X ( X texpw: (* record variable *) X treeptr; X tenv: (* symbol table for local scope *) X declptr X ); X X nindex: (* array indexing expression *) X ( X tvariable, (* indexed variable *) X toffset: (* index expression *) X treeptr X ); X nselect: (* record field selection expr *) X ( X trecord, (* record variable *) X tfield: (* selected field (nid) *) X treeptr X ); X X (* binary operators or constructors *) X nrange, (* .. (set range) *) X nformat, (* : (write format) *) X nin, (* in *) X neq, (* = *) X nne, (* <> *) X nlt, (* < *) X nle, (* <= *) X ngt, (* > *) X nge, (* >= *) X nor, (* or *) X nplus, (* + *) X nminus, (* - *) X nand, (* and *) X nmul, (* * *) X ndiv, (* div *) X nmod, (* mod *) X nquot: (* / *) X ( X texpl, (* left operand expr *) X texpr: (* right operand expr *) X treeptr X ); X X (* unary operators or constructors; note that uplus is X used to represent any parenthesized expression *) X nderef, (* ^ (ptr dereference) *) X nnot, (* not *) X nset, (* [ ] (set constr) *) X nuplus, (* + *) X numinus: (* - *) X ( X texps: (* operand expression *) X treeptr X ); X X nid, (* identifier in decl or stmt *) X nreal, (* literal real (decl or stmt) *) X ninteger, (* literal int ( - " - ) *) X nchar, (* literal char ( - " - ) *) X nstring, (* literal string ( - " - ) *) X nlabel: (* label (decl, defpt or use) *) X ( X tsym: X symptr X ); X X nnil, (* nil (pointer constant) *) X nempty: (* empty statement *) X ( ); X end; X X (* "reserved" words and standard identifiers from C, C LIB and X OS environment excluding those reserved in Pascal *) X cnames = ( X cabort, cbreak, ccontinue, cdefine, X cdefault, cdouble, cedata, cenum, X cetext, cextern, cfgetc, cfclose, X cfflush, cfloat, cfloor, cfprintf, X cfputc, cfread, cfscanf, cfwrite, X cgetc, cgetpid, cint, cinclude, X clong, clog, cmain, cmalloc, X cprintf, cpower, cputc, cread, X creturn, cregister, crewind, cscanf, X csetbits, csetword, csetptr, cshort, X csigned, csizeof, csprintf, cstdin, X cstdout, cstderr, cstrncmp, cstrncpy, X cstruct, cstatic, cswitch, ctypedef, X cundef, cungetc, cunion, cunlink, X cunsigned, cwrite X ); X X (* these are the detected errors. some are user-errors, X some are internal problems and some are host system errors *) X errors = ( X ebadsymbol, elongstring, elongtokn, erange, X emanytokn, enotdeclid, emultdeclid, enotdecllab, X emultdecllab, emuldeflab, ebadstring, enulchr, X ebadchar, eeofcmnt, eeofstr, evarpar, X enew, esetbase, esetsize, eoverflow, X etree, etag, euprconf, easgnconf, X ecmpconf, econfconf, evrntfile, evarfile, X emanymachs, ebadmach X ); X X machdefstr = packed array [ 1 .. machdeflen ] of char; X Xvar X usemax, (* program needs max-function *) X usejmps, (* source program uses non-local gotos *) X usecase, (* source program has case-statement *) X usesets, (* source program uses set-operations *) X useunion, X usediff, X usemksub, X useintr, X usesge, X usesle, X useseq, X usesne, X usememb, X useins, X usescpy, X usecomp, (* source program uses string-compare *) X usefopn, (* source program uses reset/rewrite *) X usescan, X usegetl, X usenilp, (* source program uses nil-pointer *) X usebool : boolean; (* source program writes boolean-values *) X X top : treeptr; (* top of parsetree, result from parse *) X X setlst : treeptr; (* list of set-initializations *) X setcnt : integer; (* counter for setlst length *) X X currsym : lexsym; (* current lexical symbol *) X X keytab : array [ 0 .. keytablen ] of (* table of keywords *) X record X wrd : keyword; (* keyword text *) X sym : symtyp (* corresponding symbol *) X end; X X strstor : array [ strbcnt ] of strptr; (* store for strings *) X strfree : strindx; (* first free position *) X strleft : strbidx; (* room in last blk *) X X idtab : array [ hashtyp ] of idptr; (* hashed table of id's *) X X symtab : declptr; (* table of symbols *) X X statlvl, (* static decl. level *) X maxlevel : integer; (* - " - maximum value *) X X deftab : array [ predefs ] of treeptr; (* predefined idents. *) X defnams : array [ predefs ] of symptr; (* - " - *) X typnods : array [ pretyps ] of treeptr; (* predef. types. *) X X pprio, X cprio : array [ nassign .. nnil ] of 0 .. maxprio; X X ctable : array [ cnames ] of idptr; (* table of C-keywords *) X X nmachdefs : 0 .. maxmachdefs; X machdefs : array [ 1 .. maxmachdefs ] of (* table of C-types *) X record X lolim, hilim : integer; X typstr : strindx X end; X X lineno, (* input line number *) X colno, (* input column number *) X lastcol, (* last OK input column *) X lastline : integer; (* last OK input line *) X X lasttok : toknbuf; (* last input token *) X X varno : integer; (* counter for unique id's *) X X hexdig : packed array [ 0 .. 15 ] of char; X X(* Prtmsg produces an error message. It asssumes that procedure *) X(* "message" (predefined) will "writeln" to user tty. OS *) Xprocedure prtmsg(m : errors); X Xconst user = 'Error: '; X restr = 'Implementation restriction: '; X inter = '* Internal error * '; X xtoklen = 64; (* should be <= maxtoklen *) X Xvar i : toknidx; X xtok : packed array [ 1 .. xtoklen ] of char; X Xbegin X case m of X ebadsymbol: X message(user, 'Unexpected symbol'); X ebadchar: X message(user, 'Bad character'); X elongstring: X message(restr, 'Too long string'); X ebadstring: X message(user, 'Newline in string or character'); X eeofstr: X message(user, 'End of file in string or character'); X eeofcmnt: X message(user, 'End of file in comment'); X elongtokn: X message(restr, 'Too long identfier'); X emanytokn: X message(restr, 'Too many strings, identifiers or real numbers'); X enotdeclid: X message(user, 'Identifier not declared'); X emultdeclid: X message(user, 'Identifier declared twice'); X enotdecllab: X message(user, 'Label not declared'); X emultdecllab: X message(user, 'Label declared twice'); X emuldeflab: X message(user, 'Label defined twice'); X evarpar: X message(user, 'Actual parameter not a variable'); X enulchr: X message(restr, 'Cannot handle nul-character in strings'); X enew: X message(restr, 'New returned a nil-pointer'); X eoverflow: X message(restr, 'Token buffer overflowed'); X esetbase: X message(restr, 'Cannot handle sets with base >> 0'); X esetsize: X message(restr, 'Cannot handle sets with very large range'); X etree: X message(inter, 'Bad tree structure'); X etag: X message(inter, 'Cannot find tag'); X evrntfile: X message(restr, 'Cannot initialize files in record variants'); X evarfile: X message(restr, 'Cannot handle files in structured variables'); X euprconf: X message(inter, 'No upper bound on conformant arrays'); X easgnconf: X message(inter, 'Cannot assign conformant arrays'); X ecmpconf: X message(inter, 'Cannot compare conformant arrays'); X econfconf: X message(restr, 'Cannot handle nested conformat arrays'); X erange: X message(inter, 'Cannot find C-type for integer-subrange'); X emanymachs: X message(restr, 'Too many machine integer types'); X ebadmach: X message(inter, 'Bad name for machine integer type'); X end;(* case *) X if lastline <> 0 then X begin X (* error detected during parsing, X report line/column and print the offending symbol *) X message('Line ', lastline:1, ', col ', lastcol:1, ':'); X if m in [enulchr, ebadchar, ebadstring, ebadsymbol, X emuldeflab, emultdecllab, enotdecllab, emultdeclid, X enotdeclid, elongtokn, elongstring] then X begin X i := 1; X while (i < xtoklen) and (lasttok[i] <> chr(null)) do X begin X xtok[i] := lasttok[i]; X i := i + 1 X end; X while i < xtoklen do X begin X xtok[i] := ' '; X i := i + 1 X end; X xtok[xtoklen] := ' '; X message('Current symbol: ', xtok) X end X end Xend; X Xprocedure fatal(m : errors); forward; Xprocedure error(m : errors); forward; X X(* Map letters to upper-case. *) X(* This function assumes a machine collating sequence where the *) X(* letters of either case form a contigous sequence, CHAR. *) Xfunction uppercase(c : char) : char; X Xbegin X if (c >= 'a') and (c <= 'z') then X uppercase := chr(ord(c) + ord('A') - ord('a')) X else X uppercase := c Xend; X X X(* Map letters to lower-case. *) X(* This function assumes a machine collating sequence where the *) X(* letters of either case form a contigous sequence, CHAR. *) Xfunction lowercase(c : char) : char; X Xbegin X if (c >= 'A') and (c <= 'Z') then X lowercase := chr(ord(c) - ord('A') + ord('a')) X else X lowercase := c Xend; X X(* Retrieve a string from strstor. *) Xprocedure gettokn(i : strindx; var t : toknbuf); X Xvar c : char; X k : toknidx; X j : strbidx; X p : strptr; X Xbegin X k := 1; X (* compute block and offset in block *) X p := strstor[i div (maxstrblk + 1)]; X j := i mod (maxstrblk + 1); X (* retrieve text up to null *) X repeat X c := p^[j]; X t[k] := c; X j := j + 1; X k := k + 1; X if k = maxtoknlen then X begin X c := chr(null); X t[maxtoknlen] := chr(null); X prtmsg(eoverflow) X end X until c = chr(null) Xend; X X(* Deposit a string into strstor at a given start-position. *) Xprocedure puttokn(i : strindx; var t : toknbuf); X Xvar c : char; X k : toknidx; X j : strbidx; X p : strptr; X Xbegin X k := 1; X p := strstor[i div (maxstrblk + 1)]; X j := i mod (maxstrblk + 1); X repeat X c := t[k]; X p^[j] := c; X k := k + 1; X j := j + 1 X until c = chr(null) Xend; X X(* Write a token on standard output. *) Xprocedure writetok(var w : toknbuf); X Xvar j : toknidx; X Xbegin X j := 1; X while w[j] <> chr(null) do X begin X write(w[j]); X j := j + 1 X end Xend; X X(* Print a float number on standard output. *) Xprocedure printtok(i : strindx); X Xvar w : toknbuf; X Xbegin X gettokn(i, w); X writetok(w) Xend; X X(* Print an identifier on standard output. *) Xprocedure printid(ip : idptr); X Xbegin X printtok(ip^.istr) Xend; X X(* Print a character on standard output with proper C-quoting. *) Xprocedure printchr(c : char); X Xbegin X if (c = quote) or (c = bslash) then X write(quote, bslash, c, quote) X else X write(quote, c, quote) Xend; X X(* Print a string on standard output with proper C-quoting. *) Xprocedure printstr(i : strindx); X Xvar k : toknidx; X c : char; X w : toknbuf; X Xbegin X gettokn(i, w); X write(cite); X k := 1; X while w[k] <> chr(null) do X begin X c := w[k]; X k := k + 1; X if (c = cite) or (c = bslash) then X write(bslash); X write(c) X end; X write(cite) Xend; X X(* Return a pointer to the declarationpoint of an identifier. *) Xfunction idup(ip : treeptr) : treeptr; X Xbegin X idup := ip^.tsym^.lsymdecl^.tup Xend; X X(* Compute a hashvalue for an identifier or a string. *) Xfunction hashtokn(var id : toknbuf) : hashtyp; X Xvar h : integer; X i : toknidx; X Xbegin X i := 1; X h := 0; X while id[i] <> chr(null) do X begin X (* if ord() of a character ranges from 0 to 127 then we can loop X 256 times without causing h to exceed 32767, this is safe as X both strings and identifiers are limited in length *) X h := h + ord(id[i]); (* CHAR, CPU *) X i := i + 1 X end; X hashtokn := h mod hashmax Xend; X X(* Global string table update. *) X(* This function accepts a string and stores it in strstor. *) X(* It returns the id-number for the new string. *) Xfunction savestr(var t : toknbuf) : strindx; X Xvar k : toknidx; X i : strindx; X j : strbcnt; X Xbegin X (* find length of new string including null-char *) X k := 1; X while t[k] <> chr(null) do X k := k + 1; X if k > strleft then X begin X (* out of space in strstore *) X if strstor[maxblkcnt] <> nil then (* last slot used *) X error(emanytokn); X (* allocate a new block *) X j := (strfree + maxstrblk) div (maxstrblk + 1); X new(strstor[j]); X if strstor[j] = nil then X error(enew); X strfree := j * (maxstrblk + 1); X strleft := maxstrblk X end; X (* copy new str, update location of last used cell, X return starting location for new str *) X i := strfree; X strfree := strfree + k; X strleft := strleft - k; X puttokn(i, t); X savestr := i Xend; X X(* Global id table lookup. *) X(* This procedure accepts an identifier and determines if it has *) X(* been seen before. If that is the case a pointer to its idnode *) X(* is returned, otherwise the identifier is saved and a pointer to *) X(* a new node is returned. *) Xfunction saveid(var id : toknbuf) : idptr; X Xlabel 999; X Xvar k : toknidx; X ip : idptr; X h : hashtyp; X t : toknbuf; X Xbegin X h := hashtokn(id); X ip := idtab[h]; (* scan hashlist for id *) X while ip <> nil do X begin X gettokn(ip^.istr, t); (* look at saved token *) X k := 1; X while id[k] = t[k] do X if id[k] = chr(null) then X goto 999 (* found it! *) X else X k := k + 1; (* look at next char *) X ip := ip^.inext X end; X X (* identifier wasn't previously seen, manufacture a new idnode, X save index to strstor and hashvalue, insert idnode in idtab *) X new(ip); X if ip = nil then X error(enew); X ip^.inref := 0; X ip^.istr := savestr(id); X ip^.ihash := h; X ip^.inext := idtab[h]; X idtab[h] := ip; X X999: X (* return the idnode *) X saveid := ip Xend; X X(* This function creates a new variable by concatenating one name *) X(* with another injecting a given separator. *) Xfunction mkconc(sep : char; p, q : idptr) : idptr; X Xvar w, x : toknbuf; X i, j : toknidx; X Xbegin X (* fetch second part and determine its length *) X gettokn(q^.istr, x); X j := 1; X while x[j] <> chr(null) do X j := j + 1; X (* fetch first part and locate its end *) X w[1] := chr(null); X if p <> nil then X gettokn(p^.istr, w); X i := 1; X while w[i] <> chr(null) do X i := i + 1; X (* check total length *) X if i + j + 2 >= maxtoknlen then X error(eoverflow); X X (* add separators *) X if sep = '>' then X begin X (* special case 1: > gives arrow: a->b *) X w[i] := '-'; X i := i + 1 X end; X if sep <> space then X begin X (* special case 2: space gives nothing: ab *) X w[i] := sep; X i := i + 1 X end; X (* add second part *) X j := 1; X repeat X w[i] := x[j]; X i := i + 1; X j := j + 1 X until w[i-1] = chr(null); X (* save new identifier *) X mkconc := saveid(w) Xend; X X(* Create a new id with name-prefix from w. *) Xfunction mkuniqname(var t : toknbuf) : idptr; X Xvar i : toknidx; X X procedure dig(n : integer); X begin X if n > 0 then X begin X dig(n div 10); X if i = maxtoknlen then X error(eoverflow); X t[i] := chr(n mod 10 + ord('0')); (* CHAR *) X i := i + 1 X end X end; X Xbegin X i := 1; X while t[i] <> chr(null) do X i := i + 1; X varno := varno + 1; X dig(varno); X t[i] := chr(null); X mkuniqname := saveid(t) Xend; X X(* Make a new unique variable with given char as prefix. *) Xfunction mkvariable(c : char) : idptr; X Xvar t : toknbuf; X Xbegin X t[1] := c; X t[2] := chr(null); X mkvariable := mkuniqname(t) Xend; X X(* Make a new unique variable with given char as prefix and *) X(* with a given id as tail. Commonly used for renaming id's. *) Xfunction mkrename(c : char; ip : idptr) : idptr; X Xbegin X mkrename := mkconc(uscore, mkvariable(c), ip) Xend; X X(* Make a name for a variant. Variants are mapped onto C unions, *) X(* which we always give the name "U", thus the name of the variant *) X(* becomes "U.Vnnn" where "nnn" is a unique number. *) Xfunction mkvrnt : idptr; X Xvar t : toknbuf; X Xbegin X t[1] := 'U'; X t[2] := '.'; X t[3] := 'V'; X t[4] := chr(null); X mkvrnt := mkuniqname(t) Xend; X Xprocedure checksymbol(ss : symset); Xbegin X if not (currsym.st in ss) then X error(ebadsymbol); Xend; X X(* Lexical analysis routine. *) X(* This procedure reads and classifies the next lexical token in *) X(* the input stream. The token is saved in the global variable *) X(* "currsym". The found symbol should be one of the symbols given *) X(* in the parameter "ss" otherwise the error routine is called. *) Xprocedure nextsymbol(ss : symset); X Xvar lastchr : 0 .. maxtoknlen; X X (* This function reads the next character from the input *) X (* and updates "lineno" and "colno" accordingly. *) X function nextchar : char; X X var c : char; X X begin X if eof then X c := chr(null) X else begin X colno := colno + 1; X if eoln then X begin X lineno := lineno + 1; X colno := 0 X end; X read(c); X if echo then X if colno = 0 then X writeln X else X write(c); X if c = tab1 then X colno := ((colno div tabwidth) + 1) * tabwidth X end; X if lastchr > 0 then X begin X lasttok[lastchr] := c; X lastchr := lastchr + 1 X end; X nextchar := c X end; X X (* This function looks at the next input character. *) X function peekchar : char; X X begin X if eof then X peekchar := chr(null) X else X peekchar := input^ X end; X X (* Read and classify the next token. *) X procedure nexttoken(realok : boolean); X X var c : char; X n : integer; X X ready : boolean; X X wl : toknidx; X wb : toknbuf; X X (* Determine if c is valid in an identifier. *) X (* This function assumes a machine collating *) X (* sequence where letters and digits form conti- *) X (* gous sequences, CHAR. *) X function idchar(c : char) : boolean; X X begin X idchar := X (c >= 'a') and (c <= 'z') or X (c >= '0') and (c <= '9') or X (c >= 'A') and (c <= 'Z') or X (c = uscore) X end; X X (* Determine if c is valid in a number. CHAR. *) X function numchar(c : char) : boolean; X X begin X numchar := (c >= '0') and (c <= '9') X end; X X (* Convert a digit to its numeric value. CHAR *) X function numval(c : char) : integer; X X begin X numval := ord(c) - ord('0') X end; X X (* Determine if the current token is a keyword. *) X function keywordcheck(var w : toknbuf; l : toknidx) : symtyp; X X var n : 1 .. keywordlen; X i, j, k : 0 .. keytablen; X wrd : keyword; X kwc : symtyp; X X begin X (* quick check on token length, X pascal keywords range from 2 to 9 chars in length *) X if (l > 1) and (l < keywordlen) then X begin X (* could be a keyword, initialize wrd *) X wrd := keytab[keytablen].wrd; X (* copy w to wrd *) X for n := 1 to l do X wrd[n] := w[n]; X X (* binary search for tokn, X relies on symtyp being sorted *) X i := 0; X j := keytablen; X while j > i do X begin X k := (i + j) div 2; X if keytab[k].wrd >= wrd then X j := k X else X i := k + 1 X end; X if keytab[j].wrd = wrd then X kwc := keytab[j].sym X else X kwc := sid X end X else X kwc := sid; X keywordcheck := kwc X end; X X begin (* nexttoken *) X (* don't save blanks/comments *) X lastchr := 0; X (* read non-blank character *) X repeat X c := nextchar; X (* skip comments, the two comment delimiters of pascal X are treated as different if "diffcomm" is true *) X if c = '{' then X begin X repeat X c := nextchar; X if diffcomm then X ready := c = '}' X else X ready := ((c = '*') and X (peekchar = ')')) X or (c = '}') X until ready or eof; X if eof and not ready then X error(eeofcmnt); X if (c = '*') and not eof then X c := nextchar; X c := space X end X else if (c = '(') and (peekchar = '*') then X begin X c := nextchar; X repeat X c := nextchar; X if diffcomm then X ready := (c = '*') and X (peekchar = ')') X else X ready := ((c = '*') and X (peekchar = ')')) X or (c = '}') X until ready or eof; X if eof and not ready then X error(eeofcmnt); X if (c = '*') and not eof then X c := nextchar; X c := space X end X until (c <> space) and (c <> tab1); X X (* save characters from this token and save line- and column- X numbers for errormessages *) X lasttok[1] := c; X lastchr := 2; X lastcol := colno; X lastline := lineno; X X (* map all CHAR control characters onto "badchr" *) X if c < okchr then X c := badchr; X X (* decode symbol *) X with currsym do X if eof then X begin X lasttok[1] := '*'; X lasttok[2] := 'E'; X lasttok[3] := 'O'; X lasttok[4] := 'F'; X lasttok[5] := '*'; X lastchr := 6; X st := seof X end X else X case c of X X X (* CHAR, chars not in Pascal *) X '|', '`', '~', '}', X bslash, uscore, badchr: X error(ebadchar); X X (* identifiers or keywords *) X 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', X 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', X 'u', 'v', 'w', 'x', 'y', 'z', X 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', X 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', X 'U', 'V', 'W', 'X', 'Y', 'Z': X begin X (* read token into buffer *) X wb[1] := lowercase(c); X wl := 2; X while (wl < maxtoknlen) and idchar(peekchar) do X begin X wb[wl] := lowercase(nextchar); X wl := wl + 1 X end; X if wl >= maxtoknlen then X begin X lasttok[lastchr] := chr(null); X error(elongtokn) X end; X (* terminate token and match *) X wb[wl] := chr(null); X (* check if keyword/identifier *) X st := keywordcheck(wb, wl-1); X if st = sid then X vid := saveid(wb) X end; X X (* integer or real numbers *) X '0', '1', '2', '3', '4', '5', '6', '7' ,'8', '9': X begin X (* assume integer number, save it in buffer *) X wb[1] := c; X wl := 2; X n := numval(c); X while numchar(peekchar) do X begin X c := nextchar; X n := n * 10 + numval(c); X wb[wl] := c; X wl := wl + 1 X end; X st := sinteger; X vint := n; X if realok then X begin X (* accept real numbers *) X if peekchar = '.' then X begin X (* this is a real number *) X st := sreal; X wb[wl] := nextchar; X wl := wl + 1; X while numchar(peekchar) do X begin X wb[wl] := nextchar; X wl := wl + 1 X end X end; X c := peekchar; X if (c = 'e') or (c = 'E') then X begin X (* this is a real number *) X st := sreal; X c := nextchar; X wb[wl] := xpnent; X wl := wl + 1; X c := peekchar; X if (c = '-') or (c = '+') then X begin X wb[wl] := nextchar; X wl := wl + 1 X end; X while numchar(peekchar) do X begin X wb[wl] := nextchar; X wl := wl + 1 X end X end; X if st = sreal then X begin X wb[wl] := chr(null); X vflt := savestr(wb) X end X end X end; X X '(': X if peekchar = '.' then X begin X (* some compilers on non-ascii systems X use (. for [ and .) for ] *) X c := nextchar; X st := slbrack X end X else X st := slpar; X ')': X st := srpar; X '[': X st := slbrack; X ']': X st := srbrack; X '.': X if peekchar = '.' then X begin X c := nextchar; X st := sdotdot X end X else if peekchar = ')' then X begin X c := nextchar; X st := srbrack X end X else X st := sdot; X ';': X st := ssemic; X ':': X if peekchar = '=' then X begin X c := nextchar; X st := sassign X end X else X st := scolon; X ',': X st := scomma; X '@', X '^': X st := sarrow; X '=': X st := seq; X '<': X if peekchar = '=' then X begin X c := nextchar; X st := sle X end X else if peekchar = '>' then X begin X c := nextchar; X st := sne X end X else X st := slt; X '>': X if peekchar = '=' then X begin X c := nextchar; X st := sge X end X else X st := sgt; X '+': X st := splus; X '-': X st := sminus; X '*': X st := smul; X '/': X st := squot; X quote: X begin X (* assume the symbol is a literal string *) X wl := 0; X ready := false; X repeat X if eoln then X begin X lasttok[lastchr] := chr(null); X error(ebadstring) X end; X c := nextchar; X if c = quote then X if peekchar = quote then X c := nextchar X else X ready := true; X if c = chr(null) then X begin X if eof then X error(eeofstr); X lasttok[lastchr] := chr(null); X error(enulchr) X end; X if not ready then X begin X wl := wl + 1; X if wl >= maxtoknlen then X begin X lasttok[lastchr] := X chr(null); X error(elongstring) X end; X wb[wl] := c X end X until ready; X if wl = 1 then X begin X (* only 1 character => not a string *) X st := schar; X vchr := wb[1] X end X else begin X (* > 1 character => its a string *) X wl := wl + 1; X if wl >= maxtoknlen then X begin X lasttok[lastchr] := chr(null); X error(elongstring) X end; X wb[wl] := chr(null); X st := sstring; X vstr := savestr(wb) X end X end X X end;(* case *) X if lastchr = 0 then X lastchr := 1; X lasttok[lastchr] := chr(null) X end; (* nexttoken *) X Xbegin (* nextsymbol *) X nexttoken(sreal in ss); X checksymbol(ss) Xend; (* nextsymbol *) X X(* Return a pointer to the node describing the type of tp. This *) X(* function also stores the result in the node for future ref. *) Xfunction typeof(tp : treeptr) : treeptr; X Xvar tf, tq : treeptr; X Xbegin X tq := tp; X tf := tq^.ttype; X (* keep working until a type is found *) X while tf = nil do X begin X case tq^.tt of X nchar: X tf := typnods[tchar]; X X ninteger: X tf := typnods[tinteger]; X X nreal: X tf := typnods[treal]; X X nstring: X tf := typnods[tstring]; X X nnil: X tf := typnods[tnil]; X X nid: X begin X tq := idup(tq); X if tq = nil then X fatal(etree) X end; X X ntype, X nvar, X nconst, X nfield, X nvalpar, X nvarpar: X tq := tq^.tbind; X X npredef, X nptr, X nscalar, X nrecord, X nconfarr, X narray, X nfileof, X nsetof: X tf := tq; (* these nodetypes represent types *) X X nsubrange: X if tq^.tup^.tt = nconfarr then X tf := tq^.tup^.tindtyp X else X tf := tq; X X ncall: X begin X tf := typeof(tq^.tcall); X if tf = typnods[tpoly] then X tf := typeof(tq^.taparm) X end; X X nfunc: X tq := tq^.tfuntyp; X X nparfunc: X tq := tq^.tpartyp; X X nproc, X nparproc: X tf := typnods[tnone]; X X nvariant, X nlabel, X npgm, X nempty, X nbegin, X nlabstmt, X nassign, X npush, X npop, X nif, X nwhile, X nrepeat, X nfor, X ncase, X nchoise, X ngoto, X nwith, X nwithvar: X fatal(etree); X X nformat, X nrange: X tq := tq^.texpl; X X nplus, X nminus, X nmul: X begin X tf := typeof(tq^.texpl); X if tf = typnods[tinteger] then X tf := typeof(tq^.texpr) X else if tf^.tt = nsetof then X tf := typnods[tset] X end; X X numinus, X nuplus: X tq := tq^.texps; X X nmod, X ndiv: X tf := typnods[tinteger]; X X nquot: X tf := typnods[treal]; X X neq, X nne, X nlt, X nle, X ngt, X nge, X nin, X nor, X nand, X nnot: X tf := typnods[tboolean]; X X nset: X tf := typnods[tset]; X X nselect: X tq := tq^.tfield; X X nderef: X begin X tq := typeof(tq^.texps); X case tq^.tt of X nptr: X tq := tq^.tptrid; X nfileof: X tq := tq^.tof; X npredef: X tf := typnods[tchar] (* textfile *) X end (* case *) X end; X X nindex: X begin X tq := typeof(tq^.tvariable); X if tq^.tt = nconfarr then X tq := tq^.tcelem X else if tq = typnods[tstring] then X tf := typnods[tchar] X else X tq := tq^.taelem X end; X X end (* case *) X end; X if tp^.ttype = nil then X tp^.ttype := tf; (* remember type for future reference *) X typeof := tf Xend; (* typeof *) X X(* Connect all nodes to their fathers. *) Xprocedure linkup(up, tp : treeptr); X Xbegin X while tp <> nil do X begin X if tp^.tup = nil then X begin X tp^.tup := up; X case tp^.tt of X npgm, X nfunc, X nproc: X begin X linkup(tp, tp^.tsubid); X linkup(tp, tp^.tsubpar); X linkup(tp, tp^.tfuntyp); X linkup(tp, tp^.tsublab); X linkup(tp, tp^.tsubconst); X linkup(tp, tp^.tsubtype); X linkup(tp, tp^.tsubvar); X linkup(tp, tp^.tsubsub); X linkup(tp, tp^.tsubstmt) X end; X X X nvalpar, X nvarpar, X nconst, X ntype, X nfield, X nvar: X begin X linkup(tp, tp^.tidl); X linkup(tp, tp^.tbind) X end; X X nparproc, X nparfunc: X begin X linkup(tp, tp^.tparid); X linkup(tp, tp^.tparparm); X linkup(tp, tp^.tpartyp) X end; X X nptr: X linkup(tp, tp^.tptrid); X nscalar: X linkup(tp, tp^.tscalid); X X nsubrange: X begin X linkup(tp, tp^.tlo); X linkup(tp, tp^.thi) X end; X nvariant: X begin X linkup(tp, tp^.tselct); X linkup(tp, tp^.tvrnt) X end; X nrecord: X begin X linkup(tp, tp^.tflist); X linkup(tp, tp^.tvlist) X end; X nconfarr: X begin X linkup(tp, tp^.tcindx); X linkup(tp, tp^.tcelem); X linkup(tp, tp^.tindtyp) X end; X narray: X begin X linkup(tp, tp^.taindx); X linkup(tp, tp^.taelem) X end; X nfileof, X nsetof: X linkup(tp, tp^.tof); X nbegin: X linkup(tp, tp^.tbegin); X nlabstmt: X begin X linkup(tp, tp^.tlabno); X linkup(tp, tp^.tstmt) X end; X nassign: X begin X linkup(tp, tp^.tlhs); X linkup(tp, tp^.trhs) X end; X npush, X npop: X begin X linkup(tp, tp^.tglob); X linkup(tp, tp^.tloc); X linkup(tp, tp^.ttmp) X end; X ncall: X begin X linkup(tp, tp^.tcall); X linkup(tp, tp^.taparm ) X end; X nif: X begin X linkup(tp, tp^.tifxp); X linkup(tp, tp^.tthen); X linkup(tp, tp^.telse) X end; X nwhile: X begin X linkup(tp, tp^.twhixp); X linkup(tp, tp^.twhistmt) X end; X nrepeat: X begin X linkup(tp, tp^.treptstmt); X linkup(tp, tp^.treptxp) X end; X nfor: X begin X linkup(tp, tp^.tforid); X linkup(tp, tp^.tfrom); X linkup(tp, tp^.tto); X linkup(tp, tp^.tforstmt) X end; X ncase: X begin X linkup(tp, tp^.tcasxp); X linkup(tp, tp^.tcaslst); X linkup(tp, tp^.tcasother) X end; X nchoise: X begin X linkup(tp, tp^.tchocon); X linkup(tp, tp^.tchostmt) X end; X nwith: X begin X linkup(tp, tp^.twithvar); X linkup(tp, tp^.twithstmt) X end; X nwithvar: X linkup(tp, tp^.texpw); X nindex: X begin X linkup(tp, tp^.tvariable); X linkup(tp, tp^.toffset) X end; X nselect: X begin X linkup(tp, tp^.trecord); X linkup(tp, tp^.tfield) X end; X X ngoto: X linkup(tp, tp^.tlabel); X X nrange, nformat, X nin, neq, X nne, nlt, nle, X ngt, nge, nor, X nplus, nminus, X nand, nmul, X ndiv, nmod, X nquot: X begin X linkup(tp, tp^.texpl); X linkup(tp, tp^.texpr) X end; X X nderef, X nnot, nset, X numinus, X nuplus: X linkup(tp, tp^.texps); X X nid, X nnil, ninteger, X nreal, nchar, X nstring, npredef, X nlabel, nempty: X (* no op *) X end (* case *) X end; X tp := tp^.tnext X end Xend; (* linkup *) X X(* Allocate a new symbol node. *) Xfunction mksym(vt : ltypes) : symptr; X Xvar mp : symptr; X Xbegin X new(mp); X if mp = nil then X error(enew); X mp^.lt := vt; X mp^.lnext := nil; X mp^.lsymdecl := nil; X mp^.ldecl := nil; X mksym := mp Xend; X X(* Enter a symbol at current declarationlevel. *) Xprocedure declsym(sp : symptr); X Xvar h : hashtyp; X Xbegin X if sp^.lt in [lpredef, lidentifier, lfield, lforward, lpointer] then X h := sp^.lid^.ihash X else X h := hashmax; X sp^.lnext := symtab^.ddecl[h]; X symtab^.ddecl[h] := sp; X sp^.ldecl := symtab Xend; X X(* Create a node of selected type. *) Xfunction mknode(nt : treetyp) : treeptr; X Xvar tp : treeptr; X Xbegin X tp := nil; X case nt of X npredef: new(tp, npredef); X npgm: new(tp, npgm); X nfunc: new(tp, nfunc); X nproc: new(tp, nproc); X nlabel: new(tp, nlabel); X nconst: new(tp, nconst); X ntype: new(tp, ntype); X nvar: new(tp, nvar); X nvalpar: new(tp, nvalpar); X nvarpar: new(tp, nvarpar); X nparproc: new(tp, nparproc); X nparfunc: new(tp, nparfunc); X nsubrange: new(tp, nsubrange); X nvariant: new(tp, nvariant); X nfield: new(tp, nfield); X nrecord: new(tp, nrecord); X nconfarr: new(tp, nconfarr); X narray: new(tp, narray); X nfileof: new(tp, nfileof); X nsetof: new(tp, nsetof); X nbegin: new(tp, nbegin); X nptr: new(tp, nptr); X nscalar: new(tp, nscalar); X nif: new(tp, nif); X nwhile: new(tp, nwhile); X nrepeat: new(tp, nrepeat); X nfor: new(tp, nfor); X ncase: new(tp, ncase); X nchoise: new(tp, nchoise); X ngoto: new(tp, ngoto); X nwith: new(tp, nwith); X nwithvar: new(tp, nwithvar); X nempty: new(tp, nempty); X nlabstmt: new(tp, nlabstmt); X nassign: new(tp, nassign); X nformat: new(tp, nformat); X nin: new(tp, nin); X neq: new(tp, neq); X nne: new(tp, nne); X nlt: new(tp, nlt); X nle: new(tp, nle); X ngt: new(tp, ngt); X nge: new(tp, nge); X nor: new(tp, nor); X nplus: new(tp, nplus); X nminus: new(tp, nminus); X nand: new(tp, nand); X nmul: new(tp, nmul); X ndiv: new(tp, ndiv); X nmod: new(tp, nmod); X nquot: new(tp, nquot); X nnot: new(tp, nnot); X numinus: new(tp, numinus); X nuplus: new(tp, nuplus); X nset: new(tp, nset); X nrange: new(tp, nrange); X nindex: new(tp, nindex); X nselect: new(tp, nselect); X nderef: new(tp, nderef); X ncall: new(tp, ncall); X nid: new(tp, nid); X nchar: new(tp, nchar); X ninteger: new(tp, ninteger); X nreal: new(tp, nreal); X nstring: new(tp, nstring); X nnil: new(tp, nnil); X npush: new(tp, npush); X npop: new(tp, npop); X nbreak: new(tp, nbreak) X end;(* case *) X if tp = nil then X error(enew); X tp^.tt := nt; X tp^.tnext := nil; X tp^.tup := nil; X tp^.ttype := nil; X mknode := tp Xend; X X(* Create a node with a literal value. *) Xfunction mklit : treeptr; X Xvar sp : symptr; X tp : treeptr; X Xbegin X case currsym.st of X sinteger: X begin X sp := mksym(linteger); X sp^.linum := currsym.vint; X tp := mknode(ninteger); X end; X sreal: X begin X sp := mksym(lreal); X sp^.lfloat := currsym.vflt; X tp := mknode(nreal); X end; X schar: X begin X sp := mksym(lcharacter); X sp^.lchar := currsym.vchr; X tp := mknode(nchar); X end; X sstring: X begin X sp := mksym(lstring); X sp^.lstr := currsym.vstr; X tp := mknode(nstring); X end X end;(* case *) X tp^.tsym := sp; X sp^.lsymdecl := tp; X mklit := tp Xend; X X(* Look up an identifier among declared symbols. *) Xfunction lookupid(ip : idptr; fieldok : boolean) : symptr; X Xlabel 999; X Xvar sp : symptr; X dp : declptr; X vs : set of ltypes; X Xbegin X lookupid := nil; X if fieldok then X vs := [lidentifier, lforward, lpointer, lfield] X else X vs := [lidentifier, lforward, lpointer]; X sp := nil; X X (* pick up symboltable from innermost scope *) X dp := symtab; X while dp <> nil do X begin X (* scan linked symbols with same hasvalue *) X sp := dp^.ddecl[ip^.ihash]; X while sp <> nil do X begin X (* break out when proper id found *) X if (sp^.lt in vs) and (sp^.lid = ip) then X goto 999; X sp := sp^.lnext X end; X (* proceed to enclosing scope *) X dp := dp^.dprev X end; X999: X lookupid := sp Xend; X X(* Look up a label. *) Xfunction lookuplabel(i : integer) : symptr; X Xlabel 999; X Xvar sp : symptr; X dp : declptr; X Xbegin X sp := nil; X dp := symtab; X while dp <> nil do X begin X sp := dp^.ddecl[hashmax]; X while sp <> nil do X begin X if (sp^.lt in [lforwlab, llabel]) and (sp^.lno = i) then X goto 999; X sp := sp^.lnext X end; X dp := dp^.dprev X end; X999: X lookuplabel := sp Xend; X X(* Create a new declaration level (a new scope) link declnode to *) X(* previous node. dp is non-nil when a procedure/function body *) X(* is encountered for which we have seen a forward declaration. *) Xprocedure enterscope(dp : declptr); X Xvar h : hashtyp; X Xbegin X if dp = nil then X begin X new(dp); X for h := 0 to hashmax do X dp^.ddecl[h] := nil X end; X dp^.dprev := symtab; X symtab := dp Xend; X X(* Return current scope (as a pointer to symbol-table). *) Xfunction currscope : declptr; X Xbegin X currscope := symtab Xend; X X(* Drop innermost declaration scope. *) Xprocedure leavescope; X Xbegin X symtab := symtab^.dprev Xend; X X(* Create a new identifier symbol. *) Xfunction mkid(ip : idptr) : symptr; X Xvar sp : symptr; X Xbegin X sp := mksym(lidentifier); X sp^.lid := ip; X sp^.lused := false; X declsym(sp); X ip^.inref := ip^.inref + 1; X mkid := sp Xend; X X(* Check that the current identifier is new then save it in the *) X(* current scope. Create and return a new node representing this *) X(* instance of the identifier. *) Xfunction newid(ip : idptr) : treeptr; X Xvar sp : symptr; X tp : treeptr; X Xbegin X sp := lookupid(ip, false); X if sp <> nil then X if sp^.ldecl <> symtab then X sp := nil; X if sp = nil then X begin X (* new identifier *) X tp := mknode(nid); X sp := mkid(ip); X sp^.lsymdecl := tp; X tp^.tsym := sp X end X else if sp^.lt = lpointer then X begin X (* previously declared as a pointer type *) X tp := mknode(nid); X tp^.tsym := sp; X sp^.lt := lidentifier; X sp^.lsymdecl := tp X end X else if sp^.lt = lforward then X begin X (* previously forward declared *) X sp^.lt := lidentifier; X tp := sp^.lsymdecl X end X else X error(emultdeclid); X newid := tp Xend; X X(* Check that the current identifier is already declared, *) X(* we fail unless l in [lforward, lpointer]. *) X(* Create and return a new node referencing it. *) Xfunction oldid(ip : idptr; l : ltypes) : treeptr; X Xvar sp : symptr; X tp : treeptr; X Xbegin X sp := lookupid(ip, true); X if sp = nil then X begin X if l in [lforward, lpointer] then X begin X tp := newid(ip); X tp^.tsym^.lt := l X end X else X error(enotdeclid) X end X else begin X sp^.lused := true; X tp := mknode(nid); X tp^.tsym := sp; X if (sp^.lt = lpointer) and (l = lidentifier) then X begin X sp^.lt := lidentifier; X sp^.lsymdecl := tp X end X end; X oldid := tp Xend; X X(* Look up a field in a record declaration. *) X(* Return nil if field isn't declared in "tp" or its variants. *) Xfunction oldfield(tp : treeptr; ip : idptr) : treeptr; X Xlabel 999; X Xvar tq, ti, X fp : treeptr; X Xbegin X fp := nil; X tq := tp^.tflist; X while tq <> nil do X begin X ti := tq^.tidl; X while ti <> nil do X begin X if ti^.tsym^.lid = ip then X begin X fp := mknode(nid); X fp^.tsym := ti^.tsym; X goto 999 X end; X ti := ti^.tnext X end; X tq := tq^.tnext X end; X tq := tp^.tvlist; X while tq <> nil do X begin X fp := oldfield(tq^.tvrnt, ip); X if fp <> nil then X tq := nil X else X tq := tq^.tnext X end; X999: X oldfield := fp Xend; X X(* This is the main parsing routine. It parses a correct pascal- *) X(* program and builds a parsetree which is left in the global *) X(* variable top. *) X(* Parsing is done through recursive descent using a set of *) X(* mutually recursive functions. *) Xprocedure parse; X X function plabel : treeptr; forward; X function pidlist(l : ltypes) : treeptr; forward; X function pconst : treeptr; forward; X function pconstant(realok : boolean) : treeptr; forward; X function precord(cs : symtyp; dp : declptr) : treeptr; forward; X function ptypedef : treeptr; forward; X function ptype : treeptr; forward; X function pvar : treeptr; forward; X function psubs : treeptr; forward; X function psubpar : treeptr; forward; X function plabstmt : treeptr; forward; X function pstmt : treeptr; forward; X function psimple : treeptr; forward; X function pvariable(varptr : treeptr) : treeptr; forward; X function pexpr(tnp : treeptr) : treeptr; forward; X function pcase : treeptr; forward; X function pif : treeptr; forward; X function pwhile : treeptr; forward; X function prepeat : treeptr; forward; X function pfor : treeptr; forward; X function pwith : treeptr; forward; X function pgoto : treeptr; forward; X function pbegin(retain : boolean) : treeptr; forward; X X (* Open scope of a record variable. *) X procedure scopeup(tp : treeptr); X X (* Scan a record-declaration and add all fields to *) X (* current scope. *) X procedure addfields(rp : treeptr); X X var fp, ip, vp : treeptr; X sp : symptr; X X begin X fp := rp^.tflist; X while fp <> nil do X begin X ip := fp^.tidl; X while ip <> nil do X begin X sp := mksym(lfield); X sp^.lid := ip^.tsym^.lid; X sp^.lused := false; X sp^.lsymdecl := ip; X declsym(sp); X ip := ip^.tnext X end; X fp := fp^.tnext X end; X vp := rp^.tvlist; X while vp <> nil do X begin X addfields(vp^.tvrnt); X vp := vp^.tnext X end X end; X begin X addfields(typeof(tp)) X end; X X (* Check that the current label is new then save it in the *) X (* current scope. Create and return a new node referencing *) X (* the label. *) X function newlbl : treeptr; X X var sp : symptr; X tp : treeptr; X X begin X tp := mknode(nlabel); X sp := lookuplabel(currsym.vint); X if sp <> nil then X if sp^.ldecl <> symtab then X sp := nil; X if sp = nil then X begin X sp := mksym(lforwlab); X sp^.lno := currsym.vint; X sp^.lgo := false; X sp^.lsymdecl := tp; X declsym(sp) X end X else X error(emultdecllab); X tp^.tsym := sp; X newlbl := tp X end; X X (* Check that the current label is already declared. *) X (* Create and return a new node referencing it. *) X function oldlbl(defpt : boolean) : treeptr; X X var sp : symptr; X tp : treeptr; X X begin X sp := lookuplabel(currsym.vint); X if sp = nil then X begin X prtmsg(enotdecllab); X tp := newlbl; X sp := tp^.tsym X end X else begin X tp := mknode(nlabel); X tp^.tsym := sp X end; X if defpt then X begin X END_OF_FILE if test 59347 -ne `wc -c <'ptc.p.1'`; then echo shar: \"'ptc.p.1'\" unpacked with wrong size! fi # end of 'ptc.p.1' fi echo shar: End of archive 12 \(of 12\). cp /dev/null ark12isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 12 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Rich $alz "Anger is an energy" Cronus Project, BBN Labs rsalz@bbn.com Moderator, comp.sources.unix sources@uunet.uu.net