Files
Digital-Research-Source-Code/ASSEMBLY & COMPILE TOOLS/Basic-E/source/BASIC.PLM
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

997 lines
37 KiB
Plaintext

BASCOM:
DO;
/* BASIC - E COMPILER MODIFIED FOR RESIDENT OPERATION
BY GARY A. KILDALL, DIGITAL RESEARCH, PACIFIC GROVE, CA.
VERSION 1.4 - ORIGINAL DISTRIBUTION BY EUBANKS, NPS,
VERSION 1.5 - FIXES UNINITIALIZED STORAGE PROBLEMS
WFCB(32) = 0, RFCB(12) = 0, CONT = 0
HASHTABLE = 0...0
ALLOWS $ PARAMETERS IN CALL, RATHER THAN PROGRAM
*/
DECLARE JMPTOMAIN (3) BYTE DATA(0C3H,0,0); /* FILLED WITH DDT */
/*
*********************************************************
* *
* BASIC-E COMPILER *
* *
* U. S. NAVY POSTGRADUATE SCHOOL *
* MONTEREY, CALIFORNIA *
* *
* WRITTEN BY GORDON EUBANKS, JR. *
* *
* CPM VERSION 1.4 *
* *
* DECEMBER 1976 *
* *
*********************************************************
*/
/*
*********************************************************
* *
* THE BASIC-E COMPILER IS DIVIDED INTO THE FOLLOW- *
* ING MAJOR SECTIONS: *
* (1) GLOBAL DECLERATIONS AND LITERAL *
* DEFINITIONS *
* (2) SYSTEM INPUT OUTPUT ROUTINES AND *
* ASSOCIATED VARIABLE DECLERATIONS *
* (3) SCANNER *
* (4) SYMBOL TABLE ROUTINES *
* (5) PARSER AND CODE GENERATION *
* *
* BASIC-E REQUIRES A SOURCE PROGRAM AVAILABLE ON *
* AN INPUT DEVICE AND WILL WRITE A BINARY OUTPUT *
* FILE WHICH MAY BE EXECUTED BY THE RUN TIME *
* MONITOR. THE SOURCE MUST BE READ TWICE. *
* THE NORMAL OUTPUT DEVICE IS THE CONSOLE. *
* *
* MODIFICATION OF THE COMPILER FOR OTHER OPERATING *
* SYSTEMS WILL REQUIRE MODIFICATIONS TO SECTION *
* (2) AND IN SECTION 1 REDEFINITION OF LITERALS IN *
* SECTIONS "SYSTEM PARAMETERS WHICH MAY REQUIRE *
* MODIFICATION BY USERS" AND "EXTERNAL ENTRY *
* POINTS". OTHER CHANGES SHOULD NOT BE REQUIRED *
* *
*********************************************************
*/
/*
**********************************************************
* *
* **** SECTION 1 **** *
* *
**********************************************************
*/
/*
*********************************************************
* *
* GLOBAL LITERALS *
* *
*********************************************************
*/
$INCLUDE(:F1:BASCOM.LIT)
/*
*********************************************************
* *
* EXTERNAL ENTRY POINTS *
* THESE ENTRY POINTS ALLOW INTERFACING WITH CP/M *
* *
*********************************************************
*/
DECLARE
BDOS LIT '05H', /* ENTRY POINT TO CP/M */
PARMS LIT '6DH', /* $ PARAMETERS */
BOOT LIT '0H'; /* RETURN TO SYSTEM */
MON1: PROCEDURE(F,A);
DECLARE F BYTE, A ADDRESS;
/* PATCHED WITH JMP 0005 */
L: GO TO L;
END MON1;
MON2: PROCEDURE(F,A) BYTE;
DECLARE F BYTE, A ADDRESS;
/* PATCHED WITH JMP 0005 */
L: GO TO L;
RETURN 0;
END MON2;
MON3: PROCEDURE PUBLIC;
/* USED TO RETURN TO CP/M */
DECLARE A ADDRESS; A = BOOT;
CALL A;
END MON3;
/*
*********************************************************
* *
* GLOBAL VARIABLES *
* *
*********************************************************
*/
DECLARE
PASS1 BYTE PUBLIC INITIAL(TRUE), /* PASS1 FLAG */
PASS2 BYTE PUBLIC INITIAL(FALSE), /* PASS2 FLAG */
/*
COMPILER TOGGLES
*/
LISTPROD BYTE PUBLIC INITIAL(FALSE),
LISTSOURCE BYTE PUBLIC INITIAL(FALSE),
DEBUGLN BYTE PUBLIC INITIAL(FALSE),
LOWERTOUPPER BYTE INITIAL(TRUE),
NOINTFILE BYTE INITIAL(FALSE),
LSTFLAG BYTE INITIAL(FALSE), /* LST DEVICE IF 'F' */
ERRSET BYTE INITIAL(FALSE),
ERRORCOUNT ADDRESS PUBLIC INITIAL(0),
COMPILING BYTE PUBLIC,
DATACT ADDRESS PUBLIC, /* COUNTS SIZE OF DATA AREA */
/* FLAGS USED DURING CODE GENERATION */
FORSTMT BYTE PUBLIC,
RANDOMFILE BYTE PUBLIC,
FILEIO BYTE PUBLIC,
INPUTSTMT BYTE PUBLIC,
GOSUBSTMT BYTE PUBLIC,
/* THE FOLLOWING GLOBAL VARIABLES ARE USED BY THE SCANNER */
TOKEN BYTE PUBLIC, /* TYPE OF TOKEN JUST SCANNED */
SUBTYPE BYTE PUBLIC, /* SUBTYPE OF CURRENT TOKEN */
FUNCOP BYTE PUBLIC, /* IF TOKEN FUNC THEN THIS IS FUNC NUMBER */
HASHCODE BYTE PUBLIC, /* HASH VALUE OF CURRENT TOKEN */
NEXTCHAR BYTE PUBLIC, /* CURRENT CHARACTER FROM GETCHAR */
ACCUM(IDENTSIZE) BYTE PUBLIC, /* HOLDS CURRENT TOKEN */
ACCLEN BYTE PUBLIC AT(.ACCUM(0)), /* ACCUM 0 IS LENGTH */
CONT BYTE PUBLIC, /* INDICATES ACCUM WAS FULL, STILL MORE */
COLUMN BYTE INITIAL(0), /* CURRENT COLUMN */
/*
**************************************************
* *
* THE FOLLOWING LITERAL DEFINITIONS ESTABLISH *
* MNEMONIC NAMES FOR THE TOKENS WHICH ARE THE *
* OUTPUT OF THE LALR PARSER PROGRAM. *
* *
**************************************************
*/
POUND LIT '12', LPARN LIT '02', RPARN LIT '05',
ASTRK LIT '04', TPLUS LIT '03', TMINUS LIT '07',
LESST LIT '01', TCOLIN LIT '11', SCOLN LIT '06',
EXPON LIT '14', EQUAL LIT '13', GTRT LIT '10',
TDATA LIT '99', TAND LIT '24', TCR LIT '23',
TELSE LIT '34', TDEF LIT '25', TDIM LIT '26',
TFOR LIT '28', TEND LIT '27', TFILE LIT '35',
TIF LIT '17', TGOSB LIT '43', TGOTO LIT '36',
TNEXT LIT '37', TINPT LIT '44', TLET LIT '29',
SLASH LIT '08', TNOT LIT '30', TON LIT '20',
TOR LIT '21', TPRNT LIT '45', TREAD LIT '38',
TREST LIT '48', TRETN LIT '46', TSTEP LIT '39',
TSTOP LIT '40', TTHEN LIT '41', TTO LIT '22',
FUNCT LIT '53', TGEQ LIT '15', TSUB LIT '32',
TLEQ LIT '18', COMMA LIT '09', TGO LIT '16',
TNE LIT '19', TCLOS LIT '42', TXOR LIT '33',
TOUT LIT '31', TIRN LIT '51', STRING LIT '50',
IDENTIFIER LIT '52', FLOATPT LIT '49',
UDFUNCT LIT '54', TREM LIT '0';
/*
*********************************************************
* *
* **** SECTION 2 **** *
* *
* SYSTEM DEPENDENT ROUTINES AND VARIABLES *
* *
* THE FOLLOWING ROUTINES ARE USED *
* BY THE COMPILER TO ACCESS DISK *
* FILES AND THE CONSOLE. THESE *
* ROUTINES ASSUME THE USE OF THE *
* CP/M DISK OPERATING SYSTEM. *
* *
* THE FCB'S ARE USED BY THE SYSTEM TO MAINTAIN *
* INFORMATION ON OPEN FILES. THEY ARE ONLY USED *
* BY PROCEDURES IN THIS SECTION. THE BUFFERS *
* AND POINTERS TO THE BUFFERS ARE USED BY THE *
* REMAINDER OF THE PROGRAM BUT THEIR SIZE MAY *
* BE VARIED TO SUIT THE DISK SYSTEM BEING USED *
* *
*********************************************************
*/
DECLARE
PARMLIST(9) BYTE INITIAL(' '), /* $ PARAMS SAVED HERE */
RFCBADDR ADDRESS INITIAL(5CH),
/* NOTE: CP/M PROVIES 5CH AS FCB AREA AND 80H AS A
BUFFER FOR PROGRAM USE */
RFCB BASED RFCBADDR(33) BYTE, /* SOURCE FCB */
WFCB(33) BYTE /* INTERMEDIATE FILE FCB */
INITIAL(0,' ','INT',0,0,0,0),
SBLOC ADDRESS INITIAL(80H),
SOURCEBUFF BASED SBLOC(SOURCERECSIZE)BYTE, /* SOURCE BUFFER */
SOURCEPTR BYTE INITIAL(SOURCERECSIZE), /* BUFFER INDEX */
CURSOURCERECSIZE BYTE INITIAL(SOURCERECSIZE),
DISKOUTBUFF(INTRECSIZE) BYTE,
BUFFPTR BYTE INITIAL(255), /* BUFFER INDEX */
LINEBUFF(CONBUFFSIZE) BYTE, /* CONSOLE OUT BUFFER */
LINEPTR BYTE INITIAL(0), /* BUFFER INDEX */
LINENO ADDRESS PUBLIC, /* CURRENT LINE NUMBER */
SEPARATOR BYTE PUBLIC INITIAL(COLIN);
DECLARE
PCHAR LIT '2', /* CHAR TO CONSOLE */
PBUFF LIT '9', /* BUFFER TO CONSOLE */
RCHAR LIT '1', /* CHAR FROM CONSOLE */
RBUFF LIT '10', /* BUFFER FROM CONSOLE */
OFILE LIT '15', /* OPEN FILE */
CFILE LIT '16', /* CLOSE FILE */
DFILE LIT '19', /* DELETE FILE */
RFILE LIT '20', /* READ FILE */
WFILE LIT '21', /* WRITE FILE */
MFILE LIT '22', /* MAKE FILE */
SDMA LIT '26', /* SET DMA */
FILEERR LIT '255', /* ERROR RTN CODE */
FILEEOF LIT '1'; /* EOF RTN CODE */
MOVE: PROCEDURE (SOURCE,DEST,COUNT) PUBLIC;
DECLARE
SOURCE ADDRESS,
DEST ADDRESS,
COUNT BYTE,
SCHAR BASED SOURCE BYTE,
DCHAR BASED DEST BYTE;
DO WHILE(COUNT := COUNT -1) <> 255;
DCHAR = SCHAR;
SOURCE = SOURCE + 1;
DEST = DEST + 1;
END;
RETURN;
END MOVE;
FILL: PROCEDURE (DEST,CHAR,COUNT) PUBLIC;
/* MOVE CHAR TO A N TIMES */
DECLARE
DEST ADDRESS,
CHAR BYTE,
COUNT BYTE,
DCHAR BASED DEST BYTE;
DO WHILE (COUNT := COUNT -1) <> 255;
DCHAR = CHAR;
DEST = DEST + 1;
END;
RETURN;
END FILL;
CHAROUT: PROCEDURE(CHAR);
DECLARE CHAR BYTE;
IF LSTFLAG THEN /* GO TO THE LIST DEVICE */
CALL MON1(5,CHAR); ELSE CALL MON1(2,CHAR);
END CHAROUT;
PRINTCHAR: PROCEDURE(CHAR);
DECLARE CHAR BYTE;
/* CHECK FOR TABS AND END OF LINE */
IF CHAR = TAB THEN /* EXPAND TO NEXT COLUMN */
DO WHILE ((COLUMN := COLUMN + 1) AND 7) <> 0;
CALL CHAROUT(' ');
END; ELSE
DO; COLUMN = COLUMN + 1; CALL CHAROUT(CHAR);
IF CHAR = LF THEN COLUMN = 0;
END;
END PRINTCHAR;
PRINT: PROCEDURE(A) PUBLIC;
DECLARE A ADDRESS;
DECLARE MSG BASED A BYTE;
DO WHILE MSG <> '$';
CALL PRINTCHAR(MSG);
A = A + 1;
END;
END PRINT;
DISKERR: PROCEDURE;
CALL PRINT(.('DE $'));
CALL MON3; /* RETURN TO SYSTEM */
RETURN;
END DISKERR;
OPEN$SOURCEFILE: PROCEDURE;
/* SETS UP THE FCB FOR THE SOURCE PROGRAM
WHICH MUST BE OF TYPE 'BAS' AND THEN OPENS
THE FILE. CP/M PUTS THE NAME USED AS A
PARAMETER WHEN THE COMPILER IS EXECUTED, AT
5CH.
*/
CALL MOVE(.('BAS'),RFCBADDR+9,3);
RFCB(32),RFCB(12) = 0;
IF MON2(OFILE,RFCBADDR) = FILEERR THEN
DO;
CALL PRINT(.('NS $'));
CALL MON3; /* RETURN TO SYSTEM */
END;
END OPEN$SOURCEFILE;
REWIND$SOURCE$FILE: PROCEDURE PUBLIC;
/* CP/M DOES NOT REQUIRE ANY ACTION PRIOR TO REOPENING */
RETURN;
END REWIND$SOURCE$FILE;
CLOSE$INT$FILE: PROCEDURE PUBLIC;
IF MON2(CFILE,.WFCB) = FILEERR THEN
CALL DISKERR;
END CLOSE$INT$FILE;
SETUP$INT$FILE: PROCEDURE PUBLIC;
/* MAKES A NEW FILE */
IF NOINTFILE THEN /* ONLY MAKE FILE IF THIS TOGGLE IS OFF */
RETURN;
CALL MOVE(.RFCB,.WFCB,9);
CALL MON1(DFILE,.WFCB);
IF MON2(MFILE,.WFCB) = FILEERR THEN
CALL DISKERR;
WFCB(32) = 0; /* ZERO NEXT RECORD */
END SETUP$INT$FILE;
READ$SOURCE$FILE: PROCEDURE BYTE;
DECLARE DCNT BYTE;
IF(DCNT := MON2(RFILE,RFCBADDR)) > FILEEOF THEN
CALL DISKERR;
RETURN DCNT; /* ZERO IF READ ELSE 1 IF EOF - ERRORS > 1 */
END READ$SOURCE$FILE;
WRITE$INT$FILE: PROCEDURE PUBLIC;
IF NOINTFILE THEN
RETURN;
CALL MON1(SDMA,.DISKOUTBUFF);
IF MON2(WFILE,.WFCB) <> 0 THEN
CALL DISKERR;
CALL MON1(SDMA,80H); /* RESET DMA ADDRESS */
END WRITE$INT$FILE;
CRLF: PROCEDURE PUBLIC;
CALL PRINTCHAR(EOLCHAR);
CALL PRINTCHAR(LF);
RETURN;
END CRLF;
PRINT$DEC: PROCEDURE(VALUE) PUBLIC;
/*
CONVERTS VALUE TO A DECIMAL NUMBER WHICH IS PRINTED
ON THE CONSOLE. USED FOR LINENUMBERING STATEMENTS
AND TO PRINT PRODUCTIONS.
*/
DECLARE
VALUE ADDRESS,
I BYTE,
FLAG BYTE,
COUNT BYTE;
DECLARE DECIMAL(4) ADDRESS DATA(1000,100,10,1);
FLAG = FALSE;
DO I = 0 TO 3;
COUNT = 30H;
DO WHILE VALUE >= DECIMAL(I);
VALUE = VALUE - DECIMAL(I);
FLAG = TRUE;
COUNT = COUNT + 1;
END;
IF FLAG OR (I >= 3) THEN
CALL PRINTCHAR(COUNT);
ELSE
CALL PRINTCHAR(' ');
END;
RETURN;
END PRINTDEC;
SETFLAGS: PROCEDURE PUBLIC;
/*
RESET COMPILER FLAGS USED DURING PARSING
*/
RANDOMFILE,FILEIO,
INPUTSTMT, FORSTMT, GOSUBSTMT = FALSE;
END SETFLAGS;
/*
*********************************************************
* *
* THE FOLLOWING ROUTINE GENERATES THE INTERMEDIATE *
* LANGUAGE FILE. EMIT IS THE ONLY ROUTINE TO *
* ACTUALLY WRITE TO THE DISK. GENERATE, EMITDAT, *
* AND EMITCON CALL EMIT. *
* *
*********************************************************
*/
EMIT: PROCEDURE(OBJCODE) PUBLIC;
DECLARE OBJCODE BYTE;
IF (BUFFPTR:=BUFFPTR + 1) >= INTRECSIZE THEN /* WRITE TO DISK */
DO;
CALL WRITE$INT$FILE;
BUFFPTR = 0;
END;
DISKOUTBUFF(BUFFPTR) = OBJCODE;
RETURN;
END EMIT;
/*
*********************************************************
* *
* *** SCANNER SECTION *** *
* *
*********************************************************
*/
CLEAR$LINE$BUFF: PROCEDURE;
CALL FILL(.LINEBUFF,' ',CONBUFFSIZE);
END CLEAR$LINE$BUFF;
LIST$LINE: PROCEDURE(LENGTH);
DECLARE
LENGTH BYTE,
I BYTE;
CALL PRINT$DEC(LINENO);
CALL PRINT$CHAR(SEPARATOR);
CALL PRINT$CHAR(' ');
DO I = 0 TO LENGTH;
CALL PRINT$CHAR(LINEBUFF(I));
END;
CALL CRLF;
CALL CLEAR$LINE$BUFF;
SEPARATOR = COLIN;
END LIST$LINE;
/*
**********************************************************
* *
* GETCHAR SETS THE GLOBAL VARIABLE NEXTCHAR TO THE *
* NEXT SOURCEFILE CHARACTER AND RETURNS NEXTCHAR TO *
* THE CALLING ROUTINE. *
* *
* TABS ARE REPLACED WITH A BLANK AND IF EITHER *
* LISTSOURCE IS TRUE OR AN ERROR HAS OCCURED LINES *
* ARE OUTPUT TO THE CONSOLE. *
* *
**********************************************************
*/
GETCHAR: PROCEDURE BYTE PUBLIC;
DECLARE ADDEND(*) BYTE
DATA ('END',EOLCHAR,LF); /*TO ADD END IF LEFT OFF */
NEXT$SOURCE$CHAR: PROCEDURE BYTE;
RETURN SOURCEBUFF(SOURCEPTR);
END NEXT$SOURCE$CHAR;
CHECKFILE: PROCEDURE BYTE;
/*
CHECKFILE MAINTAINS THE SOURCE BUFFER FULL AND
CHECKS FOR END OF FILE ON THE SOURCE FILE.
IF A LINE FEED IS FOUND IT IS SKIPPED.
IF END OF FILE IS DETECTED THEN TRUE IS RETURNED
ELSE FALSE IS RETURNED.
*/
DO FOREVER; /* ALLOW US TO SKIP LINE FEEDS */
IF (SOURCEPTR := SOURCEPTR + 1) >= CURSOURCERECSIZE THEN
DO;
SOURCEPTR = 0;
IF READ$SOURCE$FILE = FILEEOF THEN
RETURN TRUE;
END;
IF (NEXTCHAR := NEXT$SOURCE$CHAR) <> LF THEN
RETURN FALSE;
END; /* OF DO FOREVER */
END CHECKFILE;
IF CHECKFILE OR (NEXTCHAR = EOFFILLER) THEN
DO; /* EOF REACHED */
CALL MOVE(.ADDEND,SBLOC,5);
SOURCEPTR = 0;
NEXTCHAR = NEXT$SOURCE$CHAR;
END;
IF LINEPTR < CONBUFFSIZE THEN
LINEBUFF(LINEPTR := LINEPTR + 1) = NEXTCHAR; /* OUTPUT LINE */
IF NEXTCHAR = EOLCHAR THEN
DO;
LINENO = LINENO + 1;
IF LISTSOURCE OR ERRSET THEN
CALL LISTLINE(LINEPTR - 1); /* NOT EOLCHAR */
LINEPTR = 0;
END;
IF NEXTCHAR = TAB THEN
NEXTCHAR = ' '; /* ONLY NEED REPLACE WITH 1 BLANK */
RETURN NEXTCHAR;
END GETCHAR;
GETNOBLANK: PROCEDURE;
DO WHILE((GETCHAR = ' ') OR (NEXTCHAR = EOFFILLER));
END;
RETURN;
END GETNOBLANK;
CHECK$CONTINUATION: PROCEDURE;
/*
CHECK FOR CONTINUATION CHAR. IF FOUND SET NEXTCHAR
TO FIRST CHARACTER ON NEXT LINE. IT THEN LOOKS TO
THE PARSER AS IF IT WAS ALL ONE LINE.
*/
IF NEXTCHAR = CONTCHAR THEN
DO;
DO WHILE GETCHAR <> EOLCHAR;
END;
CALL GETNOBLANK;
END;
RETURN;
END CHECK$CONTINUATION;
/*
**********************************************************
* *
* ERROR IS THE COMPILER ERROR HANDLING ROUTINE *
* IF AN ERROR IS DETECTED WHILE PARSING A STATEMENT *
* THE REMAINDER OF THE STATEMENT IS SKIPPED AND THE *
* STATEMENT IS WRITTEN ON THE CONSOLE FOLLOWED BY A *
* TWO LETTER DISCRIPTION OF THE ERROR. AN UP ARROR *
* INDICATES WHERE IN THE LINE THE ERROR WAS DETECTED *
* THE PARSER IS RESET AND COMPILATION CONTINUES WITH *
* THE NEXT STATEMENT. *
* *
**********************************************************
*/
ERROR: PROCEDURE(ERRCODE) PUBLIC;
DECLARE
ERRCODE ADDRESS,
POINTER BYTE;
POINTER = LINEPTR + 2;
IF PASS2 THEN
ERRSET = TRUE; /* SO SOURCE LINE WILL BE LISTED */
IF TOKEN <> TCR THEN
DO; /* SKIP REMAINDER OF LINE */
DO WHILE NEXTCHAR <> EOLCHAR;
CALL CHECK$CONTINUATION;
NEXTCHAR = GETCHAR;
END;
CALL GET$NO$BLANK;
END;
IF PASS2 THEN
DO; /* PRINT ERROR MESSAGE */
ERRORCOUNT = ERRORCOUNT + 1;
CALL PRINTCHAR(HIGH(ERRCODE));
CALL PRINTCHAR(LOW(ERRCODE));
CALL PRINTCHAR(QUESTIONMARK);
DO WHILE(POINTER:=POINTER - 1) >= 1;
CALL PRINTCHAR(' ');
END;
CALL PRINTCHAR(UPARROW);
CALL CRLF;
END;
ERRSET, COMPILING = FALSE;
CALL SETFLAGS;
RETURN;
END ERROR;
/*
*********************************************************
* *
* INITIALIZE$SCANNER SETS NEXTCHAR TO THE FIRST *
* NON-BLANK CHARACTER ON THE INPUT FILE AND *
* INITIALIZES THE OUTPUTLINE COUNTER AND POINTER *
* *
* INITIALIZE$SCANNER IS CALLED AT THE BEGINNING OF *
* PASS ONE AND PASS TWO. *
* *
*********************************************************
*/
IN$SCANNER: PROCEDURE PUBLIC;
DECLARE COUNT BYTE;
DECLARE I BYTE;
IF PASS1 THEN /* GET PARAMETER LIST */
CALL MOVE(PARMS,.PARMLIST,8); /* LAST BLANK IS LEFT UNFILLED */
CALL OPEN$SOURCEFILE;
CONT,COLUMN,LINENO,LINEPTR = 0;
CALL CLEAR$LINE$BUFF;
SOURCEPTR = SOURCERECSIZE;
SEPARATOR = COLIN;
CALL GETNOBLANK;
IF PARMLIST(0) = '$' THEN
DO; I = 0;
DO WHILE (COUNT := PARMLIST(I:=I+1)) <> ' ';
IF(COUNT := COUNT - 'A') <= 5 THEN
DO CASE COUNT;
LISTPROD = TRUE;
LISTSOURCE = FALSE;
NOINTFILE = TRUE;
LOWERTOUPPER = FALSE;
DEBUGLN = TRUE;
LSTFLAG = TRUE;
END; /* OF CASE */
END;
END;
END IN$SCANNER;
/*
*********************************************************
* *
* THE SCANNER ACCEPTS INPUT CHARACTERS FROM THE *
* SOURCE FILE RETURNING TOKENS TO THE PARSER. *
* CONVERSION TO UPPERCASE IS PERFORMED WHEN SCAN- *
* NING IDENTIFIERS UNLESS LOWERTOUPPER IS FALSE. *
* BLANKS ARE IGNORED. EACH TOKEN IS PLACED IN *
* ACCUM. ACCLEN IS THE LENGTH OF THE TOKEN. *
* THE TOKEN IS HASHCODED BY SUMMING EACH ASCII *
* CHARACTER MODULO HASHTBLSIZE AND THE RESULT IS *
* RETURNED IN HASHCODE. SUBTYPE AND FUNCOP ARE *
* SET IF THE TOKEN IS A PREDEFINED FUNCTION. *
* REM AND DATA STATEMENTS ARE HANDLED COMPLETELY *
* BY THE SCANNER. IF THE RESERVED WORD REM OR *
* REMARK IS DETECTED THE INPUT IS SCANNED UNTIL *
* THE END OF THE CURRENT INPUT LINE IS LOCATED. *
* THE NEXT TOKEN (A CARRIAGE RETURN) IS THEN *
* SCANNED AND RTURNED. DATA STATEMENTS ARE SIMILAR *
* EXCEPT THE DATA IS WRITTEN OUT USEING EMITDAT *
* *
*********************************************************
*/
SCANNER: PROCEDURE PUBLIC;
/*
**********************************************************
* *
* THE FOLLOWING UTILITY PROCEDURES ARE USED BY THE *
* SCANNER. *
* *
**********************************************************
*/
PUTINACCUM: PROCEDURE;
IF NOT CONT THEN
DO;
ACCUM(ACCLEN := ACCLEN + 1) = NEXTCHAR;
HASHCODE = (HASHCODE + NEXTCHAR) AND HASHMASK;
IF ACCLEN >= (IDENTSIZE - 1) THEN
CONT = TRUE;
END;
RETURN;
END PUTINACCUM;
PUTANDGET: PROCEDURE;
CALL PUTINACCUM;
CALL GETNOBLANK;
RETURN;
END PUTANDGET;
PUTANDCHAR: PROCEDURE;
CALL PUTINACCUM;
NEXTCHAR = GETCHAR;
RETURN;
END PUTANDCHAR;
NUMERIC: PROCEDURE BYTE;
RETURN(NEXTCHAR - '0') <= 9;
END NUMERIC;
LOWERCASE: PROCEDURE BYTE;
RETURN (NEXTCHAR >= 61H) AND (NEXTCHAR <= 7AH);
END LOWER$CASE;
DECIMALPT: PROCEDURE BYTE;
RETURN NEXTCHAR = '.';
END DECIMALPT;
CONV$TO$UPPER: PROCEDURE;
IF LOWERCASE AND LOWERTOUPPER THEN
NEXTCHAR = NEXTCHAR AND 5FH;
RETURN;
END CONV$TO$UPPER;
LETTER: PROCEDURE BYTE;
CALL CONV$TO$UPPER;
RETURN ((NEXTCHAR - 'A') <= 25) OR LOWERCASE;
END LETTER;
ALPHANUM: PROCEDURE BYTE;
RETURN NUMERIC OR LETTER OR DECIMALPT;
END ALPHANUM;
SPOOLNUMERIC: PROCEDURE;
DO WHILE NUMERIC;
CALL PUTANDCHAR;
END;
RETURN;
END SPOOLNUMERIC;
SETUP$NEXT$CALL: PROCEDURE;
IF NEXTCHAR = ' ' THEN
CALL GETNOBLANK;
CONT = FALSE;
RETURN;
END SETUP$NEXT$CALL;
EMITDAT: PROCEDURE(OBJCODE);
/*
WRITES DATA STATEMENTS DURING PASS2 AND
COUNTS SIZE OF DATA AREA.
*/
DECLARE OBJCODE BYTE;
DATACT = DATACT + 1;
IF PASS2 THEN
CALL EMIT(OBJCODE);
RETURN;
END EMITDAT;
/*
*********************************************************
* *
* LOOKUP IS CALLED BY THE SCANNER WITH THE *
* PRINTNAME OF THE CURRENT TOKEN IN *
* THE ACCUMULATOR. LOOKUP DETERMINES IF THIS *
* TOKEN IS A RESERVED WORD AND SETS THE *
* VALUE OF TOKEN. IF THE TOKEN IS A PREDEFINED *
* FUNCTION THEN THE SUBTYPE AND FUNCOP ARE ALSO *
* SET. *
* THE RESERVED WORD TABLE IS DIVIDED INTO 7 *
* TABLES FOR RESERVED WORDS OF LENGTH 1 TO 7. *
* THE FOLLOWING VECTORS ARE ALSO USED: *
* TK - TOKEN ASSOCIATED WITH RESERVED WORD *
* OFFSET - INDEX INTO LNG VECTOR FOR A GIVEN *
* R/W LENGTH *
* COUNT - NUMBER OF R/W OF A GIVEN LENGTH *
* TKOS - INDEX INTO TK FOR A GIVEN R/W LENGTH *
* ST - SPECIAL DATA FOR PREDEFINED FUNCTIONS *
* *
* PREDEFINED FUNCTIONS HAVE TOKEN VALUES >64. *
* THIS NUMBER BECOMES THE FUNCOP AND THE TOKEN *
* IS FUNCT. FUNCOP IS THE MACHINE CODE FOR THE *
* PARTICULAR PREDEFINED FUNCTION. *
* *
*********************************************************
*/
LOOKUP: PROCEDURE BYTE;
DECLARE MAXRWLNG LIT '9'; /* MAX LENGTH OF A RESERVED WORD */
DECLARE LNG1(*) BYTE
DATA(EOLCHAR,'<','(','+','*',')','-',',','=','/',
';','>',':',POUNDSIGN,UPARROW), /* 15 */
LNG2(*) BYTE
DATA('IF','TO','GO','ON','OR','EQ','LT','GT',
'LE','GE','NE'), /* 11 */
LNG3(*) BYTE
DATA('FOR','LET','REM','DIM','DEF','NOT','AND',
'TAN','SIN','COS','SQR','TAB','LOG','LEN',
'FRE','ATN','ABS','EXP','INT','END','POS',
'RND','SGN','INP','ASC','VAL','XOR','SUB',
'OUT'),
/* 29 */
LNG4(*) BYTE DATA('THEN','READ','GOTO','ELSE','NEXT',
'STOP','DATA','FILE','CHR$','MID$',
'STEP','STR$','COSH','SINH'), /* 14 */
LNG5(*) BYTE DATA('PRINT','INPUT','GOSUB','CLOSE',
'LEFT$'), /* 5 */
LNG6(*) BYTE DATA('RETURN','RIGHT$','REMARK'), /* 3 */
LNG7(*) BYTE DATA('RESTORE'), /* 1 */
LNG9(*) BYTE DATA('RANDOMIZE'),
TK(*) BYTE DATA(0,TCR,LESST,LPARN,TPLUS,ASTRK,RPARN,TMINUS,
COMMA,EQUAL,SLASH,SCOLN,GTRT,TCOLIN,POUND,
EXPON, /* LNG 1 */
TIF,TTO,TGO,TON,TOR,EQUAL,LESST,GTRT,TLEQ,
TGEQ,TNE, /* LNG2 */
TFOR,TLET,TREM,TDIM,TDEF,TNOT,TAND,
72,69,70,73,74,78,84,76,71,65,75,
66,TEND,79,67,68,80,81,88,TXOR,TSUB,TOUT,
/* LNG 3 */
TTHEN,TREAD,TGOTO,TELSE,TNEXT,TSTOP,TDATA,
TFILE,82,85,TSTEP,87,89,90, /* LNG 4 */
TPRNT,TINPT,TGOSB,TCLOS,83, /* LNG 5 */
TRETN,86,TREM, /* LNG 6 */
TREST,TIRN),
OFFSET(*) BYTE DATA(0,0,15,37,124,180,205,223,230,230),
COUNT(*) BYTE DATA(0,15,11,29,14,5,3,1,0,1),
TKOS(*) BYTE DATA(0,0,15,26,55,69,74,77,78,78),
ST(*) BYTE DATA(1,1,0,1,1,1,1,1,1,1,1,0,0,1,0,1,
5,65,70,5,71,70,65,5,1,1);
DECLARE
PTR ADDRESS,
FIELD BASED PTR (1) BYTE,
I BYTE;
COMPARE: PROCEDURE BYTE;
DECLARE I BYTE;
I = 0;
DO WHILE (FIELD(I) = ACCUM(I := I + 1)) AND I <= ACCLEN;
END;
RETURN I > ACCLEN;
END COMPARE;
IF ACCLEN > MAXRWLNG THEN
RETURN FALSE;
PTR = OFFSET(ACCLEN) + .LNG1;
DO I = 1 TO COUNT(ACCLEN);
IF COMPARE THEN
DO;
IF((TOKEN := TK(TKOS(ACCLEN) + I)) > 64) AND
(TOKEN <> TDATA) THEN
DO;
SUBTYPE = ST(TOKEN - 65);
FUNCOP = TOKEN;
TOKEN = FUNCT;
END;
RETURN TRUE;
END;
PTR = PTR + ACCLEN;
END;
RETURN FALSE;
END LOOKUP;
DO FOREVER; /* TO HANDLE REM, DAT AND CONTINUATION */
ACCLEN, HASHCODE, TOKEN, SUBTYPE = 0;
/* FIRST CASE - IS THIS A STRING OR THE CONTINUATION
OF A STRING? (ONLY STRINGS MAY BE CONTINUED)
*/
IF(NEXTCHAR = STRINGDELIM) OR CONT THEN
DO; /* FOUND STRING */
TOKEN = STRING;
CONT = FALSE;
DO FOREVER; /* ALLOWS "" IN STRING TO BE " */
DO WHILE GETCHAR <> STRINGDELIM;
IF NEXTCHAR = EOLCHAR THEN CALL ERROR('US');
CALL PUTINACCUM;
IF CONT THEN RETURN;
END;
CALL GETNOBLANK;
IF NEXTCHAR <> STRINGDELIM THEN
RETURN;
CALL PUT$IN$ACCUM;
END; /* OF DO FOREVER */
END; /* OF RECOGNIZING A STRING */
/*
NEXT CASE IS A NUMERIC WHICH MUST START WITH A
NUMBER OR WITH A PERIOD
ONLY FIRST IDENTSIZE CHARACTERS ARE RETAINED
*/
ELSE IF NUMERIC OR DECIMALPT THEN
DO; /* HAVE DIGIT */
TOKEN = FLOATPT;
DO WHILE NEXTCHAR = '0'; /* ELIM LEADING ZEROS */
NEXTCHAR = GETCHAR;
END;
CALL SPOOLNUMERIC; /* GET ALL THE NUMBERS */
IF DECIMALPT THEN
DO;
CALL PUTANDCHAR;
CALL SPOOLNUMERIC;
END;
CALL CONV$TO$UPPER;
IF NEXTCHAR = 'E' THEN
DO; /* A FLOATING POINT NUMBER */
CALL PUTANDGET;
IF (NEXTCHAR = '+') OR (NEXTCHAR='-') THEN
CALL PUTANDGET;
IF NOT NUMERIC THEN
CALL ERROR('IF');
CALL SPOOL$NUMERIC;
END;
IF ACCLEN = 0 THEN
HASHCODE, ACCUM(ACCLEN := 1) = '0';
CALL SETUP$NEXT$CALL;
RETURN;
END; /* OF RECOGNIZING NUMERIC CONSTANT */
/*
NEXT CASE IS IDENTIFIER. MAY BE RESERVED WORD
IN WHICH CASE MAY BE REM, OR DATA. THESE STATEMENTS
ARE HANDLED BY THE SCANNER VICE THE PARSER AND THEN
ANOTHER LOOP THROUGH THE SCANNER IS MADE.
ONLY IDENTSIZE-1 CHARACTERS ARE RETAINED
*/
ELSE IF LETTER THEN
DO; /* HAVE A LETTER */
DO WHILE ALPHANUM;
CALL PUTANDCHAR;
END;
IF NEXTCHAR = '$' THEN
DO;
SUBTYPE = STRING;
CALL PUTANDCHAR;
END;
ELSE
SUBTYPE = FLOATPT;
IF NOT LOOKUP THEN
DO;
IF ACCUM(1) = 'F' AND ACCUM(2) = 'N'
AND ACCLEN <> 1 THEN
TOKEN = UDFUNCT;
ELSE
TOKEN = IDENTIFIER;
CALL SETUP$NEXT$CALL;
RETURN;
END;
ELSE /* IS A RW */
IF TOKEN = TREM THEN
DO WHILE NEXTCHAR <> EOLCHAR;
NEXTCHAR = GETCHAR;
CALL CHECK$CONTINUATION;
END;
ELSE
IF TOKEN = TDATA THEN
DO;
DECLARE DAT LIT '51';
CALL EMITDAT(DAT);
CALL EMITDAT(NEXTCHAR);
DO WHILE GETCHAR <> EOLCHAR;
CALL CHECK$CONTINUATION;
CALL EMITDAT(NEXTCHAR);
END;
CALL EMITDAT(',');
CALL EMITDAT(0);
DATACT = DATACT - 1;
END;
ELSE
DO;
CALL SETUP$NEXT$CALL;
RETURN;
END;
END; /* OF RECOGNIZING RW OR IDENT */
/*
LAST CASE IS A SPECIAL CHARACTER - IT MAY BE
THE CONTINUATION CHARACTER IN WHICH CASE JUST
GO TO NEXT LINE AND SCAN SOMEMORE.
*/
ELSE
DO; /* SPECIAL CHARACTER */
IF NEXTCHAR = CONTCHAR THEN
CALL CHECK$CONTINUATION;
ELSE
DO;
CALL PUTANDGET;
IF NOT LOOKUP THEN
CALL ERROR('IC');
RETURN;
END;
END; /* OF RECOGNIZING SPECIAL CHAR */
END; /* OF DO FOREVER */
END SCANNER;
END;