// S C A N (PREPRESS) //BCPL.Run/f Scan.bcpl //Last modified February 28, 1980 12:48 PM (by LaPrade) // TypeForm now types carriage return without line feed. // Reverted to old definition (groan) of StrCop. // //Last modified January 12, 1980 3:35 PM by Kerry A. LaPrade, XEOS // TypeForm handles chars in range 0-#377. // //Modified November 8, 1979 12:09 PM (by LaPrade) // ScanInit(b,file) (b= vec SCANIlen). Sets up a scan // control block, using specified file to read from. // ScanClose() Closes current file. // ScanSet(b) (b = vec SCANIlen) use this file for scanner. // b is set up with ScanInit // Returns old pointer, if any, so you may restore. // Scan() => token identifier (numbers defined in scan.defs) // ScanFor(token) scans to be sure next thing is "token" // ScanUntil(token) scans until token detected // (if token=RPAREN, must be at this "level") // ScanBack(token) arranges to have next token be token // ScanGiveID() returns pointer to string last scanned as ID. // ScanCh() returns a single character from the file. // ReadNumber(STR) parses a number in STR format // result in FPAC 1; integer part is result of fcn // PrintNumber(STR,num [,radix]) // PrintFloat(str,lvnum) Prints floating point number. // AppendChar(char, string) Appends char to string // SearchChar(string, char) true if char appears in string // StrEq(a,b) => true if two strings equal // StrCop(f,t) copy STR f to STR t // StringToValue(string, radix [10]) resultis (unsigned) value // Type(STR) type string on terminal // TypeIn(STR) get a string from the terminal, terminated by CR // TypeForm(xxxxx) // Types a formatted message. For each entry in the call, // If it is not in the range 0-#377, type it as a string ptr. // Otherwise if it is: // 0 -- type carriage-return // 1 -- use the next entry as a string pointer to accept typein // 2 -- print the next entry as a floating point number // 3 -- Double precision (fixed,fraction) // 4 -- Double integer // 8,10 -- print the next entry as a number in corresonding // radix // default -- print it as a single character. // ReadCom(str,sw) =res // Reads command file and returns true if more // there. STR will contain string; sw if present // is a list of switches (sw!0= # of sw's) // ReadComInit() starts it off get "GoodFoo.d" get "scan.dfs" //get "streams.d" // outgoing procedures external [ AppendChar PauseForCR PrintFloat PrintNumber ReadComInit ReadCom ReadNumber Scan ScanFor ScanUntil ScanInit ScanClose ScanSet ScanBack ScanGiveID ScanCh SearchChar StrCop StrEq StringToValue Type TypeForm // TypeIn ] // outgoing statics external [ @outstream //If non-zero, use for typing. @ScanSavedLetter ] static [ @outstream @ScanSavedLetter ] // incoming procedures external [ Scream //This is for reporting errors //OS // Gets // Puts // Endofs // OpenFile // Closes // Wss // Zero //FLOAT FLD;FST;FTR;FLDI;FNEG;FAD;FSB;FML;FDV;FCM;FSN FLDV;FSTV;FLDDP;FSTDP;DPAD;DPSB //SDialog // DlgInit // DlgStr //StringUtil CopyString StringCompare ] // incoming statics external [ fpComCm // keys // dsp ] // internal statics static [ @coms @sc ] // File-wide structure and manifest declarations. //structure STR: [ // length byte // char↑1,255 byte //] manifest [ strlen = 10 //number of words DEL = #177 CONTROLA = 1 BACKSPACE = $H - $A + 1 ] // Procedures //Scanner routines. //********************************************************* let ScanInit(b,s) be //********************************************************* [ Zero(b, SCANIlen) let str = OpenFile(s, ksTypeReadOnly, 1) if str eq 0 then Scream("File not found") b>> SCANI.stream = str ] //********************************************************* and ScanClose() be Closes(sc>>SCANI.stream) //********************************************************* //********************************************************* and Scan() = valof //********************************************************* [ let ins = sc>> SCANI.stream let lastch = sc>> SCANI.lastch let ch = sc>> SCANI.backtoken if ch ne 0 then [ sc>> SCANI.backtoken=0 resultis ch ] test lastch eq 0 ifso ch = Gets(ins) ifnot ch = lastch let idname=lv sc>>SCANI.idname sc>>SCANI.lastch = 0 [ if Endofs(ins) then resultis EOF let c = getcharclass(ch) switchon c into [ case 0: //separator... endcase case 1: // left parenthesis. if Endofs(ins) then resultis LPAREN ch = Gets(ins) if getcharclass(ch) eq 2 then resultis SNIL sc>> SCANI.lastch = ch resultis LPAREN case 2: resultis RPAREN case 3: // " [ let cn = 0 [ if Endofs(ins) then break ch = Gets(ins) if getcharclass(ch) eq 3 then break cn = cn + 1 idname>> STRING.char↑cn = ch ] repeat idname>> STRING.length = cn ] resultis STRING case 4: resultis SLASH case 5: case 6: //Scan into an identifier. [ let firstclass = c let cn = 0 [ ScanSavedLetter=ch if $a le ch & ch le $z then ch = ch - $a + $A cn = cn + 1 idname>> STRING.char↑cn=ch if Endofs(ins) then break ch = Gets(ins) let c = getcharclass(ch) if c ls 5 then break //out of bounds. ] repeat idname>> STRING.length = cn sc>>SCANI.lastch = ch let failflg = true //try number, but may not be one if firstclass eq 5 then ReadNumber(idname, 1, lv failflg) resultis (failflg ? ID, NUMBER) ] case 7: resultis EQUAL ] ch = Gets(ins) ] repeat ] //********************************************************* and ScanFor(token) be //********************************************************* [ let c = Scan() if c ne token then Scream("Format") ] //********************************************************* and ScanUntil(token) be //********************************************************* [ let level = 0 [ let c=Scan() if c eq token then if token ne RPAREN % level eq 0 then return if c eq LPAREN then level=level+1 if c eq RPAREN then level=level-1 ] repeat ] //********************************************************* and ScanBack(token) be sc>>SCANI.backtoken = token //********************************************************* //********************************************************* and ScanSet(b) = valof //********************************************************* [ let c = sc sc = b resultis c ] //********************************************************* and ScanGiveID() = lv sc>>SCANI.idname //********************************************************* //********************************************************* and ScanCh() = valof //********************************************************* [ let ch=sc>>SCANI.lastch sc>>SCANI.lastch=0 if ch then resultis ch let ins=sc>>SCANI.stream if Endofs(ins) then resultis EOF ch=Gets(ins) resultis ch ] //********************************************************* and getcharclass(ch) = //********************************************************* selecton ch into [ case $*s: case $*l: case $*n: case #11: 0 case $(: 1 case $): 2 case $": 3 case $/: 4 case $-: case $.: case $0: case $1: case $2: case $3: case $4: case $5: case $6: case $7: case $8: case $9: 5 case $=: 7 default: 6 ] //Number reading and printing.... //********************************************************* and ReadNumber (str,x,fail;numargs na) = valof //********************************************************* // Read a number from str and return it in FPAC 1 // uses FPAC's 2,3,4 // Set @fail if it turns out not to be a number. // [ let a = nil DefaultArgs(lv na, 1, 1, lv a) // if na eq 1 then x = 1 // if na ls 3 then fail = lv a @fail=false let octn=0 let sign=false FLDI(1,0); FLDI(4,10); FLDI(2,1) let pseen=false for i = x to str>> STRING.length do [ let ch=str>> STRING.char↑i test ch eq $. ifso pseen = true ifnot [ test ch eq $- ifso sign=not sign ifnot [ test $0 le ch & ch le $9 ifso [ FLDI(3,ch-$0) test pseen ifso [ FDV(2,4); FML(3,2) ] ifnot FML(1,4) FAD(1,3) octn=(octn lshift 3)+ch-$0 ] ifnot [ test ch eq $E ifso [ //exponent... let flg=nil let s=vec 2; FST(1,s); ReadNumber(str,i+1,lv flg) if flg then [ @fail=true; break ] let exp=FTR(1) FLD(1,s) FLDI(4,10) while exp gr 0 do [ FML(1,4); exp=exp-1 ] while exp ls 0 do [ FDV(1,4); exp=exp+1 ] break ] ifnot [ test ch eq $Q ifso FLDI(1,octn) ifnot [ @fail=true break //Don't try to parse any more ] ] ] ] ] ] if @fail ne 0 & na ls 3 then Scream("ReadNumber: format") if sign then FNEG(1) resultis(FTR(1)) ] //********************************************************* and PrintNumber(str, n, radix, pos; numargs na) be //********************************************************* [ // DefaultArgs(lv na, 2, 10, 0) if na ls 4 then str>> STRING.length = 0 if na ls 3 then radix = 10 if n ls 0 then [ n = -n AppendChar($-, str) ] printnumber2(str, n, radix) ] //********************************************************* and printnumber2(str, n, radix) be //********************************************************* [ let f = n / radix if f ne 0 then printnumber2(str, f, radix) AppendChar($0 + (n rem radix), str) ] //********************************************************* and PrintFloat(s, lvnum) be //********************************************************* [ let v = vec 4*5 for i = 1 to 4 do FSTV(i, v + 4 * i) @s = 0 FLD(1,lvnum) let p = FSN(1) test p eq 0 ifso AppendChar($0, s) ifnot [ //Really work if p eq -1 then [ FNEG(1); AppendChar($-, s) ] FLDV(2, table [ 0; 1; #100000; 4 ]) //Fuzz1= 1 + 2E-9 FML(1, 2) //n←fuzz1*number FLDI(3, 1);FLDI(2,10) FLD(4, 1) //number p = 0 while FCM(4, 2) eq 1 do [ FDV(4, 2); p = p + 1 ] while FCM(4, 3) eq -1 do [ FML(4, 2); p = p - 1 ] // 4 has number between 1 and 10, and p has power FLD(3, table [ #031325; #163073 ]) //Fuzz2 = 5E-9 FML(3, 1) //s←fuzz2 * n let q = p test p gr 7 % p ls -3 then p = 0 or q = 0 test p ls 0 ifso [ AppendChar($0, s); AppendChar($., s) for i = p to -2 do AppendChar($0, s) for i = 1 to -p do FDV(3, 2) //s = s E P ] ifnot for i = 1 to p do FML(3,2) //now print (s suppresses trailing zeroes) for i = 1 to 9 do [ let ipart = FTR(4) AppendChar($0 + ipart, s) p = p - 1 FLDI(1,ipart); FSB(4, 1); FML(4, 2) if p ls 0 then [ if p eq -1 then AppendChar($., s) FML(3, 2) if FCM(4, 3) eq -1 then break //fuzz ] ] if q ne 0 then [ AppendChar($E, s); PrintNumber(s,q,10,nil) ] ] //Really work for i = 1 to 4 do FLDV(i,v+4*i) ] //Type in and out routines. //********************************************************* and TypeForm(m,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil; numargs na) be //********************************************************* [ let lvm = lv m let i = 0; let str = vec 20 while i ls na do [ let x = lvm!i let i1 = i + 1 if (x & #177400) eq 0 then switchon x into [ case 8: case 10: i = i1 PrintNumber(str,lvm!i,x) x = str endcase case 0: // x = "*N*L" x = "*N" endcase case 1: i = i1 TypeIn(lvm!i) x = "" endcase case 2: i = i1 PrintFloat(str, lvm!i) x = str endcase case 3: case 4: [ i = i1 let v = vec 4 FSTV(1, v) FLDDP(1, lvm!i) if x eq 4 then [ let s = vec 4 FSTV(1, s); s!1 = s!1 + 16; FLDV(1, s) ] PrintFloat(str,1) FLDV(1, v) x = str ] endcase default: str!0 = x + #400 x = str endcase ] Type(x) i = i + 1 ] ] //********************************************************* and Type(str) be //********************************************************* Wss(((outstream eq 0)? dsp, outstream), str) //********************************************************* and TypeIn(str) be //********************************************************* [ // DlgInit() // DlgStr("", str) //The following code is evidently an alternative to the SDialog //package let count=0 let ch = Gets(keys) until ch eq $*N do [ switchon ch into [ case BACKSPACE: case CONTROLA: if count eq 0 then endcase Puts(dsp,$/) Puts(dsp,str>> STRING.char↑count) count = count - 1 endcase case DEL: Type("XXX");count=0 endcase default: count = count + 1 str>> STRING.char↑count = ch Puts(dsp,ch) endcase ] //end of switchon ch = Gets(keys) ] //end of wait for *n str>> STRING.length=count Puts(dsp,$*n) ] //String stuff //********************************************************* //and AppendChar(string, char) be and AppendChar(char, string) be //********************************************************* [ let newLength = string>> STRING.length + 1 string>> STRING.char↑newLength = char string>> STRING.length = newLength ] //********************************************************* and SearchChar(string, char) = valof //********************************************************* [ for I = 1 to string>> STRING.length do if string>> STRING.char↑I eq char then resultis true resultis false ] //********************************************************* and StrEq(a,b) = StringCompare(a, b) eq 0 //********************************************************* // [ // if a>> STRING.length ne b>> STRING.length then resultis false // for i = 1 to a>> STRING.length do // [ // let c1=a>> STRING.char↑let c2=b>> STRING.char↑i // if (c1 ge $a)&(c1 le $z) then c1=c1+$A-$a // if (c2 ge $a)&(c2 le $z) then c2=c2+$A-$a // unless c1 eq c2 then resultis false // ] // resultis true // ] //********************************************************* and StrCop(source, destination) be //********************************************************* [ for i = 1 to source>> STRING.length do destination>> STRING.char↑i = source>> STRING.char↑i destination>> STRING.length = source>> STRING.length ] ////********************************************************* //and StrCop(source, destination) be ////********************************************************* //Commented out because CopyString does a move block, // which sometimes puts garbage into // destination>> STRING.char↑(source>> STRING.length + 1) // // CopyString(destination, source) //Command line reader and processor. Uses the main routine SCAN above. //************************************************ and StringToValue(string, radix; numargs na) = valof //************************************************ [ DefaultArgs(lv na, 0, "", 10) let N = 0 for I = 1 to string>> STRING.length do N = N * radix + (string>> STRING.char↑I - $0) resultis N ] //********************************************************* and ReadComInit() be //********************************************************* [ coms=table [ 0;0;0;0;0;0;0;0;0;0;0;0;0;0 ] compileif SCANIlen gr 14 then [ foo = nil ] Zero(coms, SCANIlen) coms>>SCANI.stream = OpenFile("Com.Cm", ksTypeReadOnly, 1, 0, fpComCm) ] //********************************************************* and ReadCom(str,sw; numargs na) = valof //********************************************************* [ if na eq 2 then sw!0 = 0 let old = ScanSet(coms) let ans = valof [ let c = Scan() if c eq EOF then [ ScanBack(EOF); resultis 0 ] if c eq ID % c eq NUMBER then [ StrCop(lv coms>> SCANI.idname,str) while coms>> SCANI.lastch eq $/ do [ //switches Scan() //To pick up / Scan() if na eq 2 then [ let s = (lv coms>>SCANI.idname) for i = 1 to s>> STRING.length do [ sw!0 = sw!0+1 sw!(sw!0) = s>> STRING.char↑i ] ] ] resultis c ] Scream("Invalid command line") ] ScanSet(old) resultis ans ] //************************************************ and PauseForCR(string) be //************************************************ [ Wl(" Pause for CR") Ws(string) let char = nil [ char = Gets(keys) ] repeatuntil char eq $*n % char eq $b % char eq $B unless char eq $*n do CallSwat() ]