DIRECTORY Atom, Basics, IO, Rope, RuntimeError, TerminalIO, TokenIO, UserProfile; TokenIOImpl: CEDAR MONITOR IMPORTS Atom, IO, Rope, RuntimeError, TerminalIO, UserProfile EXPORTS TokenIO = BEGIN xTokenIOCode: CARDINAL = 237; -- this is really a TokenIO file xVersionCode: CARDINAL = 1; -- this is really a TokenIO file of correct version xLongInt: CARDINAL = 255; xPushFlag: CARDINAL = 254; xRope: CARDINAL = 253; xPopFlag: CARDINAL = 252; xFillTable: CARDINAL = 0; xReadTableMin: CARDINAL = 1; -- >0 ! dummy: INTEGER = xReadTableMin-1; xReadTableMax: CARDINAL = 100; xFirstShortNat: CARDINAL = xReadTableMax+1; xLastShortNat: CARDINAL = 247; shortNatLast: CARDINAL = xLastShortNat-xFirstShortNat; xTwoByteCard: CARDINAL = 251; xTwoByteNegative: CARDINAL = 250; xOneBytePos: CARDINAL = 249; xOneByteNeg: CARDINAL = 248; Mark: TYPE = REF MarkRep; MarkRep: PUBLIC TYPE = RECORD [index: INT]; TokenType: TYPE = TokenIO.TokenType; Token: TYPE = TokenIO.Token; writer: IO.STREAM_NIL; stopWriting: PUBLIC BOOL _ FALSE; LinkRecord: TYPE = RECORD[older, younger: INTEGER]; accessList: REF ARRAY [dummy..xReadTableMax] OF LinkRecord = NEW[ARRAY [dummy..xReadTableMax] OF LinkRecord]; wAtomTable: REF ARRAY [xReadTableMin..xReadTableMax] OF ATOM = NEW[ARRAY [xReadTableMin..xReadTableMax] OF ATOM]; freeEntry: INTEGER; lastEntry: INTEGER = xReadTableMax; reader: IO.STREAM _ NIL; readAgain: BOOL _ FALSE; token: Token; rTtable: REF ARRAY [xReadTableMin..xReadTableMax] OF Token = NEW[ARRAY [xReadTableMin..xReadTableMax] OF Token]; WritingStopped: PUBLIC SIGNAL = CODE; PutByte: PROC [c: CARDINAL] = INLINE { IO.PutChar[writer, LOOPHOLE[c, CHAR]]; }; PutInt: PROC [i: INT] = INLINE { Long: Basics.LongNumber = Basics.LongNumber[li[li: i]]; Put2Bytes[Long.lowbits]; Put2Bytes[Long.highbits]; }; Put2Bytes: PROC [c: CARDINAL] = INLINE { IO.PutChar[writer, LOOPHOLE[c / 256, CHAR]]; IO.PutChar[writer, LOOPHOLE[c MOD 256, CHAR]]; }; PutRope: PROC [r: Rope.ROPE] = INLINE { leng: INT = Rope.Length[r]; IF leng<255 THEN PutByte[leng] ELSE {PutByte[255]; PutInt[leng]}; IO.PutRope[writer, r]; }; PutAtom: PROC [a: ATOM] = INLINE { IF a=NIL THEN PutByte[0] ELSE PutRope[Atom.GetPName[a]]; }; GetChar: PROC [] RETURNS[CHAR] = INLINE { RETURN [IO.GetChar[reader]]; }; XGetChar: PROC [] RETURNS[CHAR] = { --not inline; used as formal procedure RETURN [GetChar[]]; }; GetByte: PROC [] RETURNS[c: [0..255]] = INLINE { c _ LOOPHOLE[IO.GetChar[reader], [0..255]]; }; Get2Bytes: PROC [] RETURNS [c: CARDINAL] = INLINE { h: [0..255] = GetByte[]; c _ h*256+GetByte[]; }; GetInt: PROC [] RETURNS[INT] = INLINE { long: Basics.LongNumber = Basics.LongNumber[num[ lowbits: Get2Bytes[], highbits: Get2Bytes[] ]]; RETURN [long.li] }; GetRope: PROC [] RETURNS[Rope.ROPE] = INLINE { leng: INT _ GetByte[]; IF leng=255 THEN leng _ GetInt[]; IF leng=0 THEN RETURN [""]; RETURN [ Rope.FromProc[len: leng, p: XGetChar] ]; }; GetAtom: PROC [] RETURNS [a: ATOM] = INLINE { r: Rope.ROPE = GetRope[]; IF Rope.InlineIsEmpty[r] THEN RETURN [NIL] ELSE RETURN [ Atom.MakeAtom[r] ]; }; FillTable: PROC[] RETURNS [t: Token] = INLINE BEGIN c: CARDINAL = GetByte[]; SELECT TRUE FROM c IN [xReadTableMin..xReadTableMax] => { t _ rTtable[c] _ Token[kind: atom, ref: GetAtom[]] }; ENDCASE => ERROR END; WriteInt: PUBLIC PROC [i: INT] = BEGIN IF stopWriting THEN SIGNAL WritingStopped; SELECT TRUE FROM i IN [0..shortNatLast] => {PutByte[i+xFirstShortNat]}; i IN [0..255] => {PutByte[xOneBytePos]; PutByte[i]}; i IN [0..INT[LAST[CARDINAL]]] => {PutByte[xTwoByteCard]; Put2Bytes[i]}; i IN [-256..-1] => {PutByte[xOneByteNeg]; PutByte[-i-1]}; i IN [-INT[LAST[CARDINAL]]..-1] => {PutByte[xTwoByteNegative]; Put2Bytes[-i-1]}; ENDCASE => {PutByte[xLongInt]; PutInt[i]}; END; WriteAtom: PUBLIC PROC [a: ATOM] = BEGIN i: INTEGER; BEGIN IF stopWriting THEN SIGNAL WritingStopped; FOR i _ accessList[dummy].older, accessList[i].older WHILE i#dummy DO IF a=wAtomTable[i] THEN { PutByte[i]; accessList[accessList[i].older].younger _ accessList[i].younger; accessList[accessList[i].younger].older _ accessList[i].older; GOTO touchI }; ENDLOOP; IF freeEntry<=xReadTableMax THEN { i _ freeEntry; freeEntry _ freeEntry + 1; } ELSE { i _ accessList[dummy].younger; accessList[dummy].younger _ accessList[i].younger; accessList[accessList[i].younger].older _ dummy; }; wAtomTable[i] _ a; PutByte[xFillTable]; PutByte[i]; PutAtom[a]; GOTO touchI; EXITS touchI --i is touched atom-- => { accessList[i].older _ accessList[dummy].older; accessList[i].younger _ dummy; accessList[accessList[dummy].older].younger _ i; accessList[dummy].older _ i; }; END; END; WriteRope: PUBLIC PROC [r: Rope.ROPE] = BEGIN IF stopWriting THEN SIGNAL WritingStopped; PutByte[xRope]; PutRope[r]; END; WritePushFlag: PUBLIC PROC [a: ATOM_NIL] = BEGIN IF stopWriting THEN SIGNAL WritingStopped; PutByte[xPushFlag]; WriteAtom[a]; END; WritePopFlag: PUBLIC PROC [] = BEGIN IF stopWriting THEN SIGNAL WritingStopped; PutByte[xPopFlag]; END; MarkAndWriteInt: PUBLIC PROC [value: INT] RETURNS [Mark] = BEGIN mark: Mark = NEW[MarkRep]; IF stopWriting THEN SIGNAL WritingStopped; PutByte[xLongInt]; mark.index _ IO.GetIndex[writer]; PutInt[value]; RETURN [mark] END; UpdateMark: PUBLIC PROC [mark: Mark, value: INT] = BEGIN pos: INT = IO.GetIndex[writer]; IO.SetIndex[writer, mark.index]; PutInt[value]; IO.SetIndex[writer, pos]; END; InternalReadToken: PROC [] RETURNS [Token] = BEGIN ENABLE { IO.EndOfStream => IF NotDebugging[] THEN GOTO endOfStreamReturn; IO.Error => IF NotDebugging[] THEN GOTO endOfStreamReturn; RuntimeError.UNCAUGHT => { TerminalIO.WriteRope["** unknown TokenIO error\n"]; IF NotDebugging[] THEN GOTO errorReturn; } }; b: [0..255]; b _ GetByte[]; IF b<=xLastShortNat THEN {--this if statement for speed only SELECT TRUE FROM b IN [xFirstShortNat..xLastShortNat] => {RETURN [Token[kind: int, ref: NEW[INT_ b-xFirstShortNat]]]}; b IN [xReadTableMin..xReadTableMax] => {RETURN [rTtable[b]]}; b=xFillTable => {RETURN FillTable[]}; ENDCASE => ERROR; } ELSE { SELECT TRUE FROM b=xPopFlag => {RETURN [Token[kind: popFlag, ref: NIL]]}; b=xPushFlag => { t: Token _ InternalReadToken[]; IF t.kind#atom THEN RETURN [Token[kind: error, ref: NIL]]; RETURN [Token[kind: pushFlag, ref: t.ref]] }; b=xTwoByteCard => {RETURN [Token[kind: int, ref: NEW[INT_Get2Bytes[]]]]}; b=xRope => {RETURN [Token[kind: rope, ref: GetRope[]]]}; b=xOneBytePos => {RETURN [Token[kind: int, ref: NEW[INT_GetByte[]]]]}; b=xOneByteNeg => {i: INT = GetByte[]; RETURN [Token[kind: int, ref: NEW[INT_-i-1]]]}; b=xLongInt => {RETURN [Token[kind: int, ref: NEW[INT_GetInt[]]]]}; b=xTwoByteNegative => {i: INT = Get2Bytes[]; RETURN [Token[kind: int, ref: NEW[INT_-i-1]]]}; ENDCASE => ERROR; } EXITS errorReturn => RETURN [Token[kind: error, ref: NIL]]; endOfStreamReturn => { TerminalIO.WriteRope["** end of TokenIO stream\n"]; RETURN [Token[kind: endOfStream, ref: NIL]] }; END; debugMax: NAT = 10; debugBuffer: REF ARRAY [0..debugMax) OF Token _ NEW[ARRAY [0..debugMax) OF Token]; debugIndex: [0..debugMax] _ 0; doDebug: BOOL _ FALSE; NotDebugging: PROC RETURNS [BOOL] = { RETURN [ UserProfile.Boolean["ChipNDale.CatchLowLevelErrors", TRUE] ] }; ReadToken: PUBLIC PROC [] RETURNS [Token] = BEGIN IF readAgain THEN {readAgain_FALSE; RETURN [token]}; token _ InternalReadToken[]; IF doDebug THEN { debugBuffer[debugIndex] _ token; debugIndex _ (debugIndex+1) MOD debugMax; }; RETURN [token] END; ReadAgain: PUBLIC PROC [] = BEGIN readAgain _ TRUE END; Error: PUBLIC ERROR[why: TokenIO.Err, explanation: REF_NIL] = CODE; ReleaseReader: PUBLIC PROC [] = BEGIN reader _ NIL END; DoAttachR: ENTRY PROC [stream: IO.STREAM] RETURNS [ok: BOOL] = BEGIN ENABLE UNWIND => NULL; IF (ok _ reader=NIL) THEN reader _ stream END; AttachReader: PUBLIC PROC [stream: IO.STREAM] = BEGIN ok: BOOL; ok _ DoAttachR[stream]; IF ~ok THEN ERROR Error[alreadyAttached, Rope.FromRefText["file is already attached"]]; IF GetByte[ ! IO.EndOfStream => GOTO empty ]#xTokenIOCode THEN { reader _ NIL; ERROR Error[wrongVersion, Rope.FromRefText["file not produced with TokenIO"]]; }; IF GetByte[]#xVersionCode THEN { reader _ NIL; ERROR Error[wrongVersion, Rope.FromRefText["TokenIO version missmatch"]]; }; EXITS empty =>{ reader _ NIL; ERROR Error[why: other, explanation: Rope.FromRefText["empty file"]]; }; END; DoAttachW: ENTRY PROC [stream: IO.STREAM] RETURNS [ok: BOOL] = BEGIN ENABLE UNWIND => NULL; IF (ok _ writer=NIL) THEN writer _ stream END; AttachWriter: PUBLIC PROC [stream: IO.STREAM] = BEGIN ok: BOOL; ok _ DoAttachW[stream]; IF ~ok THEN ERROR Error[alreadyAttached, Rope.FromRefText["file is already attached"]]; [] _ IO.GetIndex[stream! IO.Error => GOTO NoGetIndex]; stopWriting _ FALSE; freeEntry _ xReadTableMin; writer _ stream; accessList[dummy] _ LinkRecord[dummy, dummy]; freeEntry _ xReadTableMin; PutByte[xTokenIOCode]; PutByte[xVersionCode]; EXITS NoGetIndex => { writer _ NIL; Error[other, Rope.FromRefText["file uses stream without GetIndex"]]; } END; ReleaseWriter: PUBLIC PROC [] = BEGIN writer _ NIL END; EncodingError: PUBLIC SIGNAL = CODE; --For normal users it is an ERROR ReadInt: PUBLIC PROC [] RETURNS [INT] = BEGIN t: Token _ ReadToken[]; IF t.kind#int THEN SIGNAL EncodingError; RETURN [NARROW[t.ref, REF INT]^] END; ReadAtom: PUBLIC PROC [] RETURNS [ATOM] = BEGIN t: Token _ ReadToken[]; IF t.kind#atom THEN SIGNAL EncodingError; RETURN [NARROW[t.ref, ATOM]] END; ReadRope: PUBLIC PROC [] RETURNS [Rope.ROPE] = BEGIN t: Token _ ReadToken[]; IF t.kind#rope THEN SIGNAL EncodingError; RETURN [NARROW[t.ref, Rope.ROPE]] END; ReadPushFlag: PUBLIC PROC [] RETURNS [ATOM] = BEGIN t: Token _ ReadToken[]; IF t.kind#pushFlag THEN SIGNAL EncodingError; RETURN [NARROW[t.ref, ATOM]] END; ReadPopFlag: PUBLIC PROC [] = BEGIN t: Token _ ReadToken[]; IF t.kind#TokenType[popFlag] THEN SIGNAL EncodingError; END; END.  TokenIOImpl.mesa Copyright c 1983, 1985 by Xerox Corporation. All rights reserved. by Christian Jacobi August 24, 1983 3:10 pm last edited by Christian Jacobi, December 18, 1985 3:32:49 pm PST -- we use read, write for encoded, (exported) IO -- we use get/put for actual unencoded IO -- codes -- types {atom, int, rope, pushFlag, popFlag, endOfStream, error}; RECORD [ kind: TokenType, ref: REF_NIL - - either ATOM, Rope.ROPE, REF INT or NIL (Rope.ROPE or NIL if error) ]; --kind = atom => ISTYPE[ref, ATOM] --kind = int => ISTYPE[ref, REF INT] --kind = rope => ISTYPE[ref, Rope.ROPE] --kind = pushFlag => ref=NIL or ISTYPE[ref, ATOM] --kind = popFlag => ref=NIL --kind = endOfStream => ref=NIL --kind = error => ref=NIL OR ISTYPE[ref, Rope.ROPE] -- output tables --doubly linked LRU list --input tables -- Remove atom i from access list --not found --Determine where atom will go in table --reuse oldest entry --Add atom to atom table --Put atom most recently touched (i) at the front --features used to debug TokenIO and other ChipNDale IO --debugging optional; the MOD operation costs 5% runtime on read! --cause next call of ReadToken to get the same value as the last read --works only one level deep (reads again only the last value returned by ReadToken) --Attaching TokenIo to STREAMS --while TokenIo is attached to a stream no other operations on this --stream should occur only on Attach or Release --also invalidates marks --making a signal allows proceeding with the debugger, which is usefull --on emergency cases... ʾ˜šœ™Jšœ Ïmœ7™BJšœ+™+JšœB™BJ˜—šÏk ˜ Jšœžœ7˜G—J˜šÐbl œžœžœ˜Jšžœžœ.˜>Jšžœ ˜—Jšž˜J˜Jšœ0™0Jšœ)™)J˜Jšœ™Jšœžœ Ïc ˜?Jšœžœ 3˜PJ˜Jšœ žœ˜Jšœ žœ˜Jšœžœ˜Jšœ žœ˜Jšœ žœ˜šœžœ ˜&Jšœžœ˜!—Jšœžœ˜Jšœžœ˜+Jšœžœžœ!˜WJšœžœ˜Jšœžœ˜!Jšœ žœ˜Jšœ žœ˜J˜J™Jšœ™Jšœžœžœ ˜Jš œ žœžœžœ žœ˜+šœ žœ˜%Jšœ9™9—šœžœ˜šœ™Jšœ™JšœU™UJšœ™Jšœ"™"Jšœ$™$Jšœ(™(Jšœ1™1Jšœ™Jšœ™Jšœ4™4——J˜Jšœ™Jšœžœžœžœ˜Jšœ žœžœžœ˜!Jšœ žœžœžœ˜3šœ žœžœžœ˜=Jšžœžœžœ ˜0Jšœ™—š œ žœžœ žœžœ˜>Jšžœžœ žœžœ˜2—Jšœ žœ˜Jšœ žœ˜#J˜Jšœ™Jšœžœžœžœ˜Jšœ žœžœ˜Jšœ˜šœ žœžœ žœ ˜=Jšžœžœ žœ˜3—J˜Jšœžœžœžœ˜%J˜šÏnœžœžœžœ˜&Jšžœžœžœ˜&Jšœ˜—J˜š¡œžœžœžœ˜ Jšœ8˜8Jšœ˜Jšœ˜Jšœ˜—J˜š¡ œžœžœžœ˜(Jšžœžœ žœ˜,Jšžœžœžœžœ˜.Jšœ˜—J˜š¡œžœ žœžœ˜'Jšœžœ˜Jšžœ žœžœ˜AJšžœ˜Jšœ˜J˜—š¡œžœžœžœ˜"Jšžœžœžœ žœ˜8Jšœ˜J˜—š ¡œžœžœžœžœ˜*Jšžœžœ˜Jšœ˜J˜—š ¡œžœžœžœ &˜JJšžœ ˜Jšœ˜J˜—š¡œžœžœžœ˜0Jšœžœžœ˜+Jšœ˜—J˜š ¡ œžœžœžœžœ˜3Jšœ˜Jšœ˜J˜J˜—š ¡œžœžœžœžœ˜'šœ1˜1Jšœ˜Jšœ˜J˜—Jšžœ ˜Jšœ˜J˜—š ¡œžœžœžœžœ˜.Jšœžœ ˜Jšžœ žœ˜!Jšžœžœžœ˜Jšžœ+˜1Jšœ˜J˜—š ¡œžœžœžœžœ˜-Jšœžœ ˜Jšžœžœžœžœ˜*Jšžœžœ˜!Jšœ˜—J˜š¡ œžœžœž˜-Jšž˜Jšœžœ ˜šžœžœž˜šœžœ$˜(Jšœ2˜2Jšœ˜—Jšžœž˜—Jšžœ˜—J˜š¡œžœžœžœ˜ Jšž˜Jšžœ žœžœ˜*šžœžœž˜Jšœžœ2˜6Jšœžœ1˜5Jš œžœžœžœžœ-˜GJšœžœ6˜:Jš œžœžœžœžœ8˜PJšžœ#˜*—Jšžœ˜—J˜š¡ œžœžœžœ˜"Jšž˜šœžœ˜ Jšž˜Jšžœ žœžœ˜*šžœ2žœ ž˜Ešžœžœ˜J˜ J™!J˜@J˜>Jšžœ˜ Jšœ˜—Jšžœ˜—Jšœ ™ J™'šžœžœ˜"Jšœ˜Jšœ˜Jšœ˜—šžœ˜J™Jšœ˜Jšœ2˜2Jšœ0˜0Jšœ˜—J™J˜J˜J˜ Jšœ ˜ Jšžœ˜ šž˜šœ œ˜!Jšœ1™1Jšœ.˜.Jšœ˜Jšœ0˜0Jšœ˜J˜——Jšžœ˜—Jšžœ˜J˜—š¡ œžœžœ žœ˜'Jšž˜Jšžœ žœžœ˜*Jšœ˜Jšœ ˜ Jšžœ˜J˜—š ¡ œžœžœžœžœ˜*Jšž˜Jšžœ žœžœ˜*Jšœ˜Jšœ ˜ Jšžœ˜J˜—š¡ œžœžœ˜Jšž˜Jšžœ žœžœ˜*Jšœ˜Jšžœ˜J˜—š ¡œžœžœ žœžœ ˜:Jšž˜Jšœ˜Jšžœ žœžœ˜*Jšœ˜Jšœ žœ˜!Jšœ˜Jšžœ˜ Jšžœ˜J˜—š¡ œžœžœžœ˜2Jšž˜Jšœžœžœ˜Jšžœ˜ Jšœ˜Jšžœ˜Jšžœ˜—J˜š¡œžœžœ ˜,šž˜šžœ˜ Jšžœžœžœžœ˜@Jšžœ žœžœžœ˜:šœ žœ˜J˜3Jšžœžœžœ ˜(J˜—J˜——Jšœ ˜ Jšœ˜šžœžœ "˜<šžœžœž˜šœžœ$˜(Jšœžœžœžœ˜=—Jšœžœ$žœ˜=Jšœžœ˜%Jšžœžœ˜—J˜—šžœ˜šžœžœž˜Jšœžœžœ˜8šœ˜Jšœ˜Jšžœ žœžœžœ˜:Jšžœ$˜*Jšœ˜—Jšœžœžœžœ˜IJšœ žœ&˜8Jšœžœžœžœ˜Fšœ˜Jš œžœžœžœžœ ˜D—Jšœžœžœžœ˜Bšœ˜Jš œžœžœžœžœ ˜F—Jšžœžœ˜—J˜—šž˜Jšœžœžœ˜5šœ˜J˜3Jšžœ žœ˜+Jšœ˜——Jšžœ˜—J˜J˜Jšœ7™7J˜Jšœ žœ˜Jš œ žœžœžœ žœžœžœ˜RJšœ˜Jšœ žœžœ˜J˜š¡ œžœžœžœ˜%Jšžœ8žœ˜EJšœ˜—J˜š¡ œžœžœžœ ˜+Jšž˜Jšžœ žœ žœžœ ˜4Jšœ˜šžœ žœ˜Jšœžœ$™AJšœ!˜!Jšœžœ ˜)J˜—Jšžœ˜Jšžœ˜—J˜š¡ œžœžœ˜JšœE™EJšœS™SJšž˜Jšœ ž˜Jšžœ˜J˜—Jšœ™JšœC™CJšœ™J˜š œžœžœ žœžœžœ˜EJšœ™—J˜š¡ œžœžœ˜Jšž˜Jšœ ž˜ Jšžœ˜—J˜š¡ œžœžœ žœžœžœžœ˜>Jšžœžœžœžœ˜Jšžœžœžœ˜)Jšžœ˜—J˜š ¡ œžœžœ žœžœ˜/Jšž˜Jšœžœ˜ Jšœ˜JšžœžœžœF˜Wšžœ žœžœžœ˜AJšœ žœ˜ JšžœI˜NJ˜—šžœžœ˜!Jšœ žœ˜ JšžœD˜IJ˜—šžœ˜šœ ˜ Jšœ žœ˜ Jšžœ@˜EJ˜——Jšžœ˜—J˜J˜š¡ œžœžœ žœžœžœžœ˜>Jšžœžœžœžœ˜Jšžœžœžœ˜)Jšžœ˜—J˜š ¡ œžœžœ žœžœ˜/Jšž˜Jšœžœ˜ Jšœ˜JšžœžœžœF˜WJšœžœžœ žœ ˜6Jšœžœ˜Jšœ˜Jšœ˜Jšœ-˜-Jšœ˜Jšœ˜Jšœ˜šž˜šœ˜Jšœ žœ˜ JšœD˜DJ˜——Jšžœ˜—J˜š¡ œžœžœ˜Jšœ™Jšž˜Jšœ ž˜ Jšžœ˜—J˜J˜šœžœžœžœ !˜GJšœG™GJšœ™—J˜š ¡œžœžœžœžœ˜'Jšž˜Jšœ˜Jšžœ žœžœ˜(Jšžœžœžœžœ˜ Jšžœ˜J˜—š ¡œžœžœžœžœ˜)Jšž˜Jšœ˜Jšžœ žœžœ˜)Jšžœžœžœ˜Jšžœ˜J˜—š ¡œžœžœžœžœ˜.Jšž˜Jšœ˜Jšžœ žœžœ˜)Jšžœžœ žœ˜!Jšžœ˜J˜—š ¡ œžœžœžœžœ˜-Jšž˜Jšœ˜Jšžœžœžœ˜-Jšžœžœžœ˜Jšžœ˜J˜—š¡ œžœžœ˜Jšž˜Jšœ˜Jšžœžœžœ˜7Jšžœ˜—J˜Jšžœ˜J˜J˜—…—&–>^