;PARSING 8/26/79 ;XYBASIC Interpreter Source Module ;Copyright (C) 1978, 1979 by Mark Williams Company, Chicago ;parsing routines ;Register use for all parsing routines, except as noted. ;Call: (textp) address of next text char ;Retn: A clobbered ; BC,DE preserved ; HL address of next unparsed char ; (textp) ditto ; Carry set iff failure ;GTCHA fetches the next nonspace character. gtcha: lhld textp gtch1: mov a,m ;fetch character inx h ;point to next cpi ' ' jz gtch1 ;try again if space shld textp ;store new text pointer ret ;GTCHO looks ahead to the next nonspace char. gtcho: call gtcha bakup: dcx h ;back up textp shld textp ret ;GTALP fetches the next char if alphabetic, returns Carry if not. gtalp: call gtcho call isalp rc ;not a letter read1: inx h shld textp ;else read it ret ;GTCOM gets a comma. gtcom: call gtcha cpi ',' rz jmp bkupc ;back up textp and return Carry ;GTCND gets a comma not followed by a delimiter. gtcnd: call gtcom rc ;no comma call dtst0 ;look for delimiter cmc ;carry set iff delimiter follows comma ret ;GTD returns carry reset and moves up textp if next char matches (D), else ;returns carry set and leaves (textp) unchanged. ;GTDSN gets a char which must match (D), else SN error is issued. ;GTDTR does a GTDSN and echoes the char if trace print is desired. gtd: call gtcha cmp d rz bkupc: stc ;return carry set if no match jmp bakup ;back up textp and return if not compl gtdtr: lda inlhs ora a jz gtdsn ;do not echo unless INLHS lda vtrac ral ;carry set iff trace print desired mov a,d cc writc ;write it if so endif ;and fall through to GTDSN gtdsn: call gtd rnc jmp snerr if compl gtdtr equ gtdsn endif gtreq: mvi d,')' call gtdsn ;skip ) and fall through to skip = gtequ: mvi d,eqult jmp gtdsn ;skip = token ;GTDEL scans text until delimiter is found. gtdel: call dtst0 ;test if current is delimiter rnc ;delimiter, done call read1 ;no, read the current cpi '"' cz gtclq ;watch for quoted strings jmp gtdel ;and try the next ;GTCLQ scans to close quote or . ;Call: HL next char addr ;Retn: HL addr of " or cr ; textp reset gtclq: mov a,m ;fetch next cpi cr rz ;done if cr call read1 ;else read it cpi '"' jnz gtclq ;and keep reading if not close " dcx h ;point to " ret ;ISDIG presrves A and returns Carry set iff (A) is not ASCII digit. isdig: cpi '0' rc ;too small cpi '9'+1 cmc ret ;ISHEX converts an ASCII hex digit to binary. ;Both upper and lower case alphabetic characters are allowed. ;Call: A character ;Retn: Carry set iff not a hex digit ; A binary value if Carry reset, clobbered if Carry ; BCDEHL preseved ishex: sui '0' ;subtract bias cpi 'G'+20H-'0' cmc rc ;return Carry set if < '0' or > 'f' cpi 10 cmc ;Carry reset for '0' to '9' rnc ;decimal digit ani 1FH ;mask to convert lower case to upper sui 'A'-'0' ;'A' becomes 0, 'B' becomes 1... cpi 6 cmc rc ;return Carry if not legal hex digit adi 10 ;'A' becomes 10, 'B' becomes 11... ret ;GTILD gets the next letter or digit without skipping spaces, and is ;called by GTNAM and GTFIL to build a variable or file name. ;Call: HL address of next char to parse ;Retn: A next char ; BC,DE preserved ; HL incremented ; (textp) value of HL when called ; Carry set iff next not letter or digit gtild: mov a,m shld textp ;leave textp set for failure inx h ldtst: call isdig rnc ;digit isalp: cpi 'A' rc ;neither cpi 'Z'+1 cmc ;carry iff not letter ret ;GTDEC scans an unsigned decimal integer and returns its value. ;An unsigned decimal integer is a string of decimal digits. ;The digit count (not including leading 0s) is used for overflow detection. ;1-4 digits indicates no overflow. ;6+ digits indicates overflow. ;5 digits indicates overflow iff (value of 4 digits)*10 > 65535 or ; (value of 4 digits)*10 + (value of digit 5) > 65535. ; Note that 9999*5 is always < 65535. ;Retn: Carry set iff no decimmal digit ; Zero set if overflow, i.e. integer > 65535 ; A clobbered ; BC preserved ; DE integer value 0 <= n <= 65535 if Carry and Zero reset ; DE 0 if Carry or Zero ; HL address of next nondigit ;GTDE0 is called from GTDEC to scan through leading 0s. gtde0: inx h shld textp ;read leading 0 mov a,m ;fetch next char sui '0' jz gtde0 ;another leading 0 cpi 10 ;carry set iff decimal digit jc gtde1 ;digit follows leading 0s retnc: ora a ;nondigit, return C,Z reset and result 0 ret gtdec: lxi d,0 ;default value to DE call gtcho ;next char to A, textp to HL sui '0' ;subtract ASCII bias jz gtde0 ;strip off leading 0s cpi 10 jc gtde1 ;digit mov a,m ;refetch next ora a ;clear Zero stc ;and set Carry ret gtde1: mov e,a ;first digit value to DE push b ;save BC mvi b,-6 and 255 ;-(max # digits + 1) to B gtde2: inr b ;# digits read - max # - 1 inx h mov a,m ;fetch next char sui '0' ;subtract ASCII bias cpi 10 jnc gtde3 ;nondigit, done push h ;save textp mov h,d mov l,e ;copy value to HL call hl10a ;10 * old value + current digit = new value xchg ;to DE pop h ;restore textp jnc gtde2 ;get next if no overflow inr b ;bump digit count to assure > 5 on overflow jmp gtde2 ;and get next char gtde3: shld textp ;update textp mov a,b ;fetch digit count - max - 1 pop b ;restore BC ora a rm ;count <= max #, return both C and Z reset xra a ;overflow, return C reset and Z set mov d,a mov e,a ;and 0 in DE ret ;GTLNO returns a legal line # in DE, using GTDEC. ;Brances to fatal US error if GTDEC returns overflow or 0. ;Otherwise (1) nonexistent or (2) 0 < n < 2 ^ 16. ;Retn: A next non-space char clobbered ; BC preserved preserved ; DE 0 n ; HL addr of next nondigit address of next nondigit ; Carry set reset gtlno: call gtdec ;look for decimal integer rc ;none, return Carry mov a,d ora e rnz ;legal line #, return Carry reset userr: error f, U, S ;fatal US error -- illegal line # ;GTLIT gets a literal, and is called by EVAL, READ, INPUT and VAL. ;The literal may be preceeded by optional spaces and untokenized + and - signs. ;Call: HL (textp) ;Retn: Carry set iff no literal found ; A type token ; BC value if integer ; C,DE value if string ; (TEMP)BCD value if floating gtlit: mov a,m ;fetch next inx h ;and point to following gtlt1: push psw xra a sta temp ;sign = positive pop psw gtlt2: call isdig ;check if char is digit jnc gtlid if float cpi '.' jz gtlif ;get floating literal endif shld textp ;else read the char if strng cpi '"' jz gtlis ;get string literal endif lxi b,intst shl 8 ;overflow flag to B, 0 to C mov d,c mov e,c ;embryo value 0 to DE cpi '#' jz gtlih ;get hex literal cpi '&' jz gtlib ;get binary literal cpi ' ' jz gtlt3 ;leading space -- ignore cpi '+' jz gtlt3 ;unary + cpi '-' ;check for unary - jnz bkupc ;else back up TEXTP and return Carry lda temp ;unary minus -- complement sign cma sta temp gtlt3: mov a,m inx h ;point to next jmp gtlt2 ;and keep looking ;get a string literal, i.e. quoted string if strng gtlis: push h ;save string location call gtclq ;scan to close " or cr pop d ;location to DE gtls1: mov a,l ;last+1 -- EVUNQ entry point sub e ;last+1-first = length mov c,a ;to C mvi a,strst ;type = string ora a ;return Carry reset ret endif ;get binary integer literal gtlib: xchg ;textp to DE, value to HL gtlb1: ldax d ;fetch char sui '0' ;subtract ASCII bias cpi 2 jnc ovtst ;not a binary digit, done inx d inr c ;bump count dad h ;shift value left one bit cc ovset ;set overflow flag rar ;current digit to Carry jnc gtlb1 ;try next inx h ;add current digit to value jmp gtlb1 ;and try next ;get hex integer literal gtlih: xchg ;textp to DE, value to HL gtlh1: ldax d ;fetch next call ishex jc ovtst ;not a hex digit, done inx d inr c ;bump count push psw mov a,h ani 0F0H ;Zero reset if overflow will occur cnz ovset ;set overflow flag pop psw ;restore current nibble dad h dad h dad h dad h ;value left four bits ora l mov l,a ;add in current nibble jmp gtlh1 ;common exit for binary and hex integer literals ovtst: xchg ;textp to HL shld textp ;and reset mov a,c ;digit count to A ora a jz bkupc ;no digits after # or &, return Carry mov a,b ;fetch result type mov b,d mov c,e ;result to DE cpi intst rz ;return type integer if no overflow ;issue nonfatal OV error and return max integer value in BC iover: error n, O, V ;else issue nonfatal OV error lxi b,7FFFH ;max positive value to BC mvi a,intst ;result is integer ora a ;carry reset ret ovset: dcr b ret ;get numeric literal with first char digit gtlid: push h ;save textp call gtdec ;get decimal number mov a,m ;fetch next char pop h ;old textp to HL if float jz gtlif ;get floating literal if too big cpi '.' jz gtlif ;or if next is . cpi 'E' jz gtlif ;or if next is E cpi 'E'+20H jz gtlif ;also allow lower case e in case untokenized mov a,d ora a jm gtlif ;or if value is > 32767 but < 65536 else jz iover ;OV error if too big in nonfloating version endif lda temp ;fetch sign ora a cnz cplde ;complement value if negative desired mov b,d mov c,e ;value to BC mvi a,intst rnc ;return unless cannot complement if float gtlif: dcx h call finp ;get floating literal sta temp ;save A mvi a,sngst ora a ;return Carry reset ret else jmp iover endif ;GTNAM gets a variable name into buffer at BUFAD, its type into VARTY, ; and its length into A. ;A name is [ | ]* [$ | | %] without spaces. ;After MAXNL characters, additional chars are scanned but ignored. ;The first letter determines the variable type according to TYBUF, ; unless the trailing character $ | | % is specified. ;Call: (textp) current text pointer ;Retn: A length of variable name ; HL address of first char after name ; (bufad) symbol name string ; (varty) variable type ; Carry set iff no name, i.e. first char not letter gtnam: call gtalp ;get letter rc push b push d mov b,a ;save first char if strng or float ;must find var type if noninteger version xchg ;save HL in DE lxi h,tybuf-'A' call adahl ;address default type byte mov a,m ;fetch it xchg ;restore HL else mvi a,intst ;else type is integer endif sta varty ;store type in VARTY mov a,b ;restore char lxi b,bufad mvi d,1 ;char count to D gtnm1: stax b ;store a char gtnm2: call gtild ;get next char jc gtnm3 ;no more mov e,a ;save new char mov a,d cpi maxnl ;compare count to max name length jnc gtnm2 ;count >= length, so don't insert mov a,e ;restore char inx b inr d ;bump count and pointer jmp gtnm1 ;and insert gtnm3: cpi '%' cz gtnm4 ;type is integer if float cpi '!' cz gtnm6 ;type is floating endif if strng cpi '$' cz gtnm7 ;type is string endif if camac cpi '#' cz gtnm8 endif ldax b ori 80H stax b ;end of string bit on mov a,d pop d pop b ret gtnm4: mvi a,intst ;integer token to A gtnm5: shld textp ;move textp past trailing char inx h sta varty ;reset VARTY to specified type xra a ;clear A for following compares ret if float gtnm6: mvi a,sngst ;single precision token to A jmp gtnm5 endif if strng gtnm7: mvi a,strst ;string token to A jmp gtnm5 endif if camac gtnm8: mvi a,camst jmp gtnm5 endif ;FDVAR looks for variable name, returns carry set if none. ;If var not found in symbol table, assumes its type to be simple and ;builds a new symbol table entry for it. ;Retn: DE entry type address ; HL address following name in entry fdvar: call gtnam rc mov c,a ;save length call stlku ;look up symbol in symbol table rnc ;found it fdva1: lda varty ;fetch var type -- unary user fn entry point if camac cpi camst ;check if camvar jz snerr ;SN error if undefined camvar endif call bytsd ;# bytes per entry to DE xchg ;and to HL mvi b,0 ;length to BC inx h ;+length inx h ;+type inx h ;+dims dad b ;entry length to HL call stpsh ;build the entry dcx d ;DE points to type byte mvi m,0 ;zero the dim byte ret ;GTVAR performs a variable reference. ;Gets a variable name from text, returns with carry set if none. ;If variable name not found in symbol table, assumes its type to be simple ;and builds a new symbol table entry for it. ;Prints trace info if INLHS and either TRACE or variable break bit on, ;and sets VTRAC accordingly. ;Issues BS error if subscript out of bounds. ;Retn: A type token of referenced variable ; DE type byte address of referenced var ; HL address of referenced variable ; Carry set iff no name found ; (vtrac) bit 7 set iff trace desired gtvar: call fdvar rc ;no name found if not compl lda inlhs ora a cnz trset ;set VTRAC if in lhs endif mov a,m ;fetch # dims mov c,a ;and save in C inx h ora a ;zero set iff simple var lda varty ;value to return to A rz ;simple var if camac cpi camst ;check if camvar reference jnz gtva0 ;no mvi a,intst ;yes, value of camvar ref is integer gtva0: ora a ;reset Carry for return endif push psw ;save value to return push d call bytsd ;find bytes per entry mov b,e ;and save in B push b ;save bytes per entry and # dims lxi b,1 ;product to BC lxi d,0 ;sum to DE push d ;and saved mvi d,'(' ;GTVA1 is executed for each subscript of the variable. The stack contains ;the result type, type byte address, bytes per entry/# dimensions, and sum ;of subscripts thus far. BC contains the product of bounds thus far, and ;HL points to the next bound. gtva1: push h ;save pointer call gtdtr ;skip ( pop h ;restore pointer mov e,m inx h mov d,m ;next bound to DE inx h ;point to next xthl ;save pointer, sum to HL push b ;save product push d ;save dim push h ;save sum push b ;save product call gtsub ;get subscript to BC pop d ;product to DE call mulbd ;subscript * product to HL pop d ;sum to DE dad d ;new sum = sum + subscript * product to HL pop d ;bound to DE pop b ;product to BC inx d ;bound+1 push h ;save sum call mulbd ;new product = product * (bound+1) to HL pop d ;sum to DE xthl ;product to stack, pointer to HL pop b ;product to BC xthl ;pointer to stack, counts to HL dcr l ;decrement # dims count jz gtva2 ;done xthl ;save counts, restore pointer push d ;save sum mvi d,',' jmp gtva1 ;and do some more dimensions gtva2: mov a,h ;bytes per entry to A pop h ;pointer to HL gtva3: dad d ;add sum once for each value byte dcr a jnz gtva3 ;pointer * sum = location push h mvi d,')' call gtdtr ;skip ) pop h pop d ;restore type byte address pop psw ;and value to return ret ;GTSUB gets a subscript expression, issuing nonfatal BS error if out of bounds. ;The subscript value is printed if VTRAC is negative. ;Call: DE bound ;Retn: BC subscript, 0 <= (BC) <= (DE) gtsub: if not compl lda inlhs ora a ;check if INLHS jm gtsu2 ;yes -- fix so no extraneous tracing endif gtsu0: push d ;save bound lxi d,stack+stakm+10-stakl call cplde xchg dad sp ;number of bytes left to HL jnc bserr ;fatal BS error if too little room left call gtexp ;expr to BC lxi d,0 mov a,b ora a ;check sign of subscript cm gtsu1 ;negative pop d ;restore bound call cmbds rc ;< bound rz ;= bound gtsu1: mov b,d mov c,e ;replace expr with bound error n, B, S ;nonfatal BS error and return if not compl gtsu2: xra a sta inlhs ;reset INLHS call gtsu0 ;get the subscript mvi a,255 sta inlhs ;turn INLHS back on lda vtrac ora a rp jmp wrtbu ;print subscript val if tracing endif bserr: error f, B, S ;fatal BS error ;GTIVA is called from SCALL to perform an integer variable reference. ;Retn: Carry Set if not integer var ref ; BC value of integer var ; DE preserved gtiva: push d call gtvar ;look for var ref pop d rc ;not found mov c,m inx h mov b,m ;value to BC if strng or float cpi intst rz ;return Carry reset if integer stc ;and Carry set if not endif ret if not compl ;TRSET determines if trace printing is desired, setting VTRAC accordingly ;and echoing the variable name if so. ;A trailing type char is printed if the type is not the default type. ;Call: DE addr of var type byte ;Retn: A clobbered ; BC,DE,HL preserved ; Carry reset ; (vtrac) minus iff trace printing desired trset: push h lhld trace ;INLHS to H, TRACE to L ldax d ;type byte to A -- A7 set iff var break ora l ;minus iff var break or TRACE on ana h ;minus iff tracing desired sta vtrac jp trst1 ;done if not tracing call bprnt ;echo line number if not done already mov h,d mov l,e inx h ;name addr to HL if strng or float push h ;and saved call prtst ;print it pop h ;restore addr mov a,m ;refetch first char ani 7FH ;mask off possible high bit lxi h,tybuf-'A' call adahl ;address default type buffer location ldax d ;fetch type byte ani 1FH ;mask to type cmp m ;compare to default jz trst1 ;same as default, no trailing char call tycha ;type char to H mov a,h call writc ;and printed else ;NOT STRNG and NOT FLOAT call prtst ;just print the name endif trst1: pop h ret ;TYCHA returns type char in H for type in A. if strng or float tycha: if strng mvi h,'$' cpi strst rz ;string endif if float mvi h,'!' cpi sngst rz ;floating endif if camac mvi h,'#' cpi camst rz ;camvar endif mvi h,'%' ret ;integer endif ;end of STRNG or FLOAT conditional endif ;end of NOT COMPL conditional ;CMPST compares the strings at (DE) and M. Zero set iff match. cmpst: ldax d cmp m inx d inx h rnz ;return zero reset if no match ora a jp cmpst ;keep comparing xra a ;match -- set zero and return ret ;CPYST copies a string from (DE) to M. cpys0: lxi d,bufad cpyst: ldax d mov m,a inx d inx h ora a jp cpyst ret ;end of PARSING page