-- JaMStringImpl.mesa -- Original version John Warnock, January, 1979 -- Last changed by Bill Paxton, February 6, 1981 9:31 AM -- Last changed by Doug Wyatt, 7-Oct-81 17:17:49 DIRECTORY JaMBasic USING [Object, StringLength, Tag], JaMInternal USING [Frame], JaMOps USING [Error, Install, InstallReason, NameToString, Pop, PopCardinal, PopInteger, PopString, Push, PushBoolean, PushInteger, rangechk, RegisterExplicit, Text], JaMVM USING [AllocString, CopyString, GetChar, GetText, PutChar, PutText], Inline USING [LowByte, LowHalf], Real USING [AppendReal], String USING [AppendLongDecimal, AppendLongNumber, StringToLongNumber]; JaMStringImpl: PROGRAM IMPORTS JaMOps, JaMVM, Inline, Real, String EXPORTS JaMOps = { OPEN S:String, VM:JaMVM, JaMOps, JaMInternal, JaMBasic; -- Constants lengthLimit: CARDINAL = LAST[StringLength]; -- Globals true,false,novalue: string Object; -- The following routines implement a set of string manipulations. Head: PROC[s: string Object, n: CARDINAL] RETURNS[string Object] = INLINE { IF n<s.length THEN s.length ← n; RETURN[s] }; Tail: PROC[s: string Object, n: CARDINAL] RETURNS[string Object] = INLINE { IF n>s.length THEN n ← s.length; s.length ← s.length - n; n ← s.offset + n; s.text ← s.text + n/2; s.offset ← n MOD 2; RETURN[s] }; String: PUBLIC PROC[length: StringLength] RETURNS[string Object] = { s: string Object ← VM.AllocString[length]; s.tag ← L; RETURN[s]; }; SCopy: PUBLIC PROC[string: string Object, expand: CARDINAL ← 0] RETURNS[string Object] = { oldlen: CARDINAL = string.length; newlen: CARDINAL = oldlen + MIN[expand,lengthLimit-oldlen]; new: string Object ← VM.AllocString[newlen]; VM.CopyString[src: string, dst: new]; RETURN[new]; }; SubString: PUBLIC PROC[s: string Object, beg,len: CARDINAL] RETURNS[string Object] = { IF beg>s.length OR len>(s.length-beg) THEN ERROR Error[rangechk]; RETURN[Head[Tail[s,beg],len]]; }; PutString: PUBLIC PROC[from: string Object, beg: CARDINAL, into: string Object] = { IF beg>into.length OR from.length>(into.length-beg) THEN ERROR Error[rangechk]; VM.CopyString[from,Tail[into,beg]]; }; StringCompare: PUBLIC PROC[a,b: string Object] RETURNS[INTEGER] = { FOR i: CARDINAL IN[0..MIN[a.length,b.length]) DO ca: CHARACTER ← VM.GetChar[a,i]; cb: CHARACTER ← VM.GetChar[b,i]; IF ca#cb THEN RETURN[IF ca<cb THEN -1 ELSE 1]; ENDLOOP; IF a.length=b.length THEN RETURN[0] ELSE RETURN[IF a.length<b.length THEN -1 ELSE 1]; }; StringMatch: PROC[s,t: string Object, j: CARDINAL] RETURNS[BOOLEAN] = INLINE { FOR i: CARDINAL IN[0..s.length) DO IF VM.GetChar[s,i] # VM.GetChar[t,j+i] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; MakeString: PUBLIC PROC[text: Text, tag: Tag ← L] RETURNS[string Object] = { string: string Object ← String[text.length]; VM.PutText[string,text]; string.tag ← tag; RETURN[string]; }; StringText: PUBLIC PROC[string: string Object, text: Text] = { VM.GetText[string,text]; }; StringForAll: PUBLIC PROC[string: string Object, proc: PROC[CHARACTER] RETURNS[BOOLEAN]] = { FOR i: CARDINAL IN[0..string.length) DO c: CHARACTER ← VM.GetChar[string,i]; IF proc[c] THEN EXIT; ENDLOOP; }; -- Intrinsics JString: PUBLIC PROC[frame: Frame] = { n: CARDINAL ← PopCardinal[frame.opstk,lengthLimit]; Push[frame.opstk,String[n]]; }; JCopyString: PUBLIC PROC[frame: Frame] = { s: string Object ← PopString[frame.opstk]; Push[frame.opstk,SCopy[s]]; }; JSubString: PUBLIC PROC[frame: Frame] = { len: CARDINAL ← PopCardinal[frame.opstk]; beg: CARDINAL ← PopCardinal[frame.opstk]; s: string Object ← PopString[frame.opstk]; t: string Object ← SubString[s,beg,len]; Push[frame.opstk,t]; }; JPutString: PUBLIC PROC[frame: Frame] = { s: string Object ← PopString[frame.opstk]; beg: CARDINAL ← PopCardinal[frame.opstk]; t: string Object ← PopString[frame.opstk]; PutString[s,beg,t]; Push[frame.opstk,t]; }; JSGet: PUBLIC PROC[frame: Frame] = { i: CARDINAL ← PopCardinal[frame.opstk]; s: string Object ← PopString[frame.opstk]; item: INTEGER; IF i NOT IN[0..s.length) THEN ERROR Error[rangechk]; item ← LOOPHOLE[VM.GetChar[s,i]]; PushInteger[frame.opstk,item]; }; JSPut: PUBLIC PROC[frame: Frame] = { item: INTEGER ← Inline.LowHalf[PopInteger[frame.opstk]]; i: CARDINAL ← PopCardinal[frame.opstk]; s: string Object ← PopString[frame.opstk]; c: CHARACTER; IF i NOT IN[0..s.length) THEN ERROR Error[rangechk]; c ← LOOPHOLE[Inline.LowByte[item]]; VM.PutChar[s,i,c]; }; StringSearch: PUBLIC PROC[frame: Frame] = { s: string Object ← PopString[frame.opstk]; t: string Object ← PopString[frame.opstk]; FOR j: CARDINAL IN[0..t.length) WHILE (t.length - j) < s.length DO IF StringMatch[s,t,j] THEN { -- j is match position in t r: string Object ← Tail[t,j]; Push[frame.opstk,Tail[r,s.length]]; -- part of t following match Push[frame.opstk,Head[r,s.length]]; -- part of t matching s Push[frame.opstk,Head[t,j]]; -- part of t preceding match PushBoolean[frame.opstk,TRUE]; RETURN }; ENDLOOP; Push[frame.opstk,t]; -- no match, just push t PushBoolean[frame.opstk,FALSE]; }; StringAnchorSearch: PUBLIC PROC[frame: Frame] = { s: string Object ← PopString[frame.opstk]; t: string Object ← PopString[frame.opstk]; IF (t.length >= s.length) AND StringMatch[s,t,0] THEN { Push[frame.opstk,Tail[t,s.length]]; -- remainder of t following match Push[frame.opstk,Head[t,s.length]]; -- part of t matching s PushBoolean[frame.opstk,TRUE] } ELSE { Push[frame.opstk,t]; -- no match, just push t PushBoolean[frame.opstk,FALSE] }; }; AppendInteger: PROC[s: STRING, i: LONG INTEGER, rdx: CARDINAL ← 10] = INLINE { IF rdx=10 THEN S.AppendLongDecimal[s,i] ELSE S.AppendLongNumber[s,i,rdx] }; AppendReal: PROC[s: STRING, r: REAL] = INLINE { Real.AppendReal[s,r] }; ConvertToString: PUBLIC PROC[frame: Frame] = { ob: Object ← Pop[frame.opstk]; string: string Object ← novalue; s: STRING ← [50]; WITH ob:ob SELECT FROM integer => { AppendInteger[s,ob.ivalue]; string ← MakeString[s] }; real => { AppendReal[s,ob.rvalue]; string ← MakeString[s] }; boolean => string ← (IF ob.bvalue THEN true ELSE false); string => string ← ob; name => string ← SCopy[NameToString[ob]]; -- make a copy! ENDCASE; Push[frame.opstk,string]; }; ConvertToRadixString: PUBLIC PROC[frame: Frame] = { rdx: CARDINAL ← PopCardinal[frame.opstk]; ob: Object ← Pop[frame.opstk]; string: string Object ← novalue; s: STRING ← [50]; IF rdx NOT IN[2..36] THEN ERROR Error[rangechk]; WITH ob:ob SELECT FROM integer => { AppendInteger[s,ob.ivalue,rdx]; string ← MakeString[s] }; real => { AppendInteger[s,LOOPHOLE[ob.rvalue],rdx]; string ← MakeString[s] }; ENDCASE; Push[frame.opstk,string]; }; TextIntoString: PROC[from: Text, into: string Object] RETURNS[string Object] = { IF from.length>into.length THEN ERROR Error[rangechk]; into.length ← from.length; VM.PutText[into,from]; RETURN[into] }; StringIntoString: PROC[from,into: string Object] RETURNS[string Object] = { PutString[from,0,into]; into.length ← from.length; RETURN[into] }; ConvertIntoString: PUBLIC PROC[frame: Frame] = { string: string Object ← PopString[frame.opstk]; ob: Object ← Pop[frame.opstk]; result: string Object ← novalue; s: STRING ← [50]; WITH ob:ob SELECT FROM integer => { AppendInteger[s,ob.ivalue]; result ← TextIntoString[s,string] }; real => { AppendReal[s,ob.rvalue]; result ← TextIntoString[s,string] }; boolean => result ← (IF ob.bvalue THEN true ELSE false); string => result ← ob; name => result ← StringIntoString[NameToString[ob],string]; ENDCASE; Push[frame.opstk,result]; }; ConvertIntoRadixString: PUBLIC PROC[frame: Frame] = { string: string Object ← PopString[frame.opstk]; rdx: CARDINAL ← PopCardinal[frame.opstk]; ob: Object ← Pop[frame.opstk]; result: string Object ← novalue; s: STRING ← [50]; IF rdx NOT IN[2..36] THEN ERROR Error[rangechk]; WITH ob:ob SELECT FROM integer => { AppendInteger[s,ob.ivalue,rdx]; result ← TextIntoString[s,string] }; real => { AppendInteger[s,LOOPHOLE[ob.rvalue],rdx]; result ← TextIntoString[s,string] }; ENDCASE; Push[frame.opstk,result]; }; ConvertOctalString: PUBLIC PROC[frame: Frame] = { string: string Object ← PopString[frame.opstk]; s: STRING ← [50]; i: LONG INTEGER; IF string.length>s.maxlength THEN ERROR Error[rangechk]; VM.GetText[string,s]; i ← S.StringToLongNumber[s,8]; PushInteger[frame.opstk,i]; }; -- Initialization InstallString: PROC[why: InstallReason, frame: Frame] = { SELECT why FROM register => { true ← MakeString[".true"L]; false ← MakeString[".false"L]; novalue ← MakeString["--nostringval--"L]; RegisterExplicit[frame,".string"L,JString]; RegisterExplicit[frame,".copystring"L,JCopyString]; RegisterExplicit[frame,".substring"L,JSubString]; RegisterExplicit[frame,".putstring"L,JPutString]; RegisterExplicit[frame,".sget"L,JSGet]; RegisterExplicit[frame,".sput"L,JSPut]; RegisterExplicit[frame,".search"L,StringSearch]; RegisterExplicit[frame,".asearch"L,StringAnchorSearch]; RegisterExplicit[frame,".cvs"L,ConvertToString]; RegisterExplicit[frame,".cvis"L,ConvertIntoString]; RegisterExplicit[frame,".cvrs"L,ConvertToRadixString]; RegisterExplicit[frame,".cvos"L,ConvertOctalString]; RegisterExplicit[frame,".cvirs"L,ConvertIntoRadixString]; }; ENDCASE; }; Install[InstallString]; }.