DIRECTORY Basics, BitOps, CardTab, Convert, FS, GList, IO, Rope, Sage, SymTab, TerminalIO; SageImpl: CEDAR PROGRAM IMPORTS Basics, BitOps, CardTab, Convert, FS, GList, IO, Rope, SymTab, TerminalIO EXPORTS Sage = BEGIN OPEN Sage; Error: PUBLIC ERROR [msg: ROPE _ NIL] = CODE; ParseData: TYPE = REF ParseDataRec; ParseDataRec: TYPE = RECORD [ streams: LIST OF IO.STREAM, names: LIST OF ROPE, noticeCR: BOOL, token: ROPE, char: CHAR, kind: IO.TokenKind, charsSkipped: INT]; ParseKeywordProc: TYPE = PROC [pd: ParseData, program: Programming, nextReserved: ROPE]; Parse: PUBLIC PROC [file: ROPE] RETURNS [program: Programming] = { keywords: LIST OF ROPE _ LIST ["TimingGroups", "NameDutPinTimingGroup", "DutPinFixturePin", "FixturePinChipChannel", "ColumnNames", "Vectors", "End"]; keywordProcs: LIST OF ParseKeywordProc _ LIST[TimingGroups, NameDutPinTimingGroup, DutPinFixturePin, FixturePinChipChannel, ColumnNames, Vectors]; pd: ParseData _ NEW[ParseDataRec]; pd.streams _ LIST[FS.StreamOpen[file]]; pd.names _ LIST[file]; pd.noticeCR _ FALSE; program _ NEW[ProgrammingRec]; program.timingGroups _ SymTab.Create[]; program.nameDutPinTimingGroup _ SymTab.Create[]; program.dutPinFixturePin _ CardTab.Create[]; program.fixturePinChipChannel _ CardTab.Create[]; program.columnNames _ CardTab.Create[]; ParseToken[pd]; -- prime the pump DO IF pd.kind#tokenID THEN ERROR Error["Missing keyword"]; IF NOT Rope.Equal[pd.token, keywords.first, FALSE] THEN ERROR Error["Unknown keyword"]; keywordProcs.first[pd, program, keywords.rest.first]; keywords _ keywords.rest; keywordProcs _ keywordProcs.rest; IF keywordProcs=NIL THEN EXIT; ENDLOOP; }; TimingGroups: ParseKeywordProc = { ParseToken[pd]; DO name: ROPE _ NIL; group: TimingGroup _ NEW[TimingGroupRec]; IF (name _ ParseName[pd, nextReserved])=NIL THEN EXIT; IF pd.kind#tokenID THEN ERROR Error["Expecting timing group format"]; group.format _ SELECT TRUE FROM Rope.Equal[pd.token, "NRZ", FALSE] => NRZ, Rope.Equal[pd.token, "RZ", FALSE] => RZ, Rope.Equal[pd.token, "RO", FALSE] => RO, Rope.Equal[pd.token, "RC", FALSE] => RC, Rope.Equal[pd.token, "RT", FALSE] => RT, ENDCASE => ERROR Error["Unknown timing group format"]; ParseToken[pd]; group.delay _ ParseNs[pd]; group.width _ ParseNs[pd]; group.sample _ ParseNs[pd]; group.variableThreshold _ ParseBool[pd]; group.variableLevels _ ParseBool[pd]; ParseSeperator[pd]; IF NOT SymTab.Store[program.timingGroups, name, group] THEN ERROR Error["Multiply defined timing group"]; ENDLOOP; }; NameDutPinTimingGroup: ParseKeywordProc = { ParseToken[pd]; DO name: ROPE _ NIL; group: DutPinTimingGroup _ NEW[DutPinTimingGroupRec]; groups: DutPinTimingGroups; IF (name _ ParseName[pd, nextReserved])=NIL THEN EXIT; IF pd.kind=tokenSINGLE THEN { IF pd.char#'[ THEN ERROR Error["Bad bus index"]; ParseToken[pd]; group.index _ ParseNumber[pd]; IF pd.char#'] THEN ERROR Error["Bad bus index"]; ParseToken[pd]; } ELSE group.index _ 0; group.dutPin _ ParseNumber[pd]; group.timingGroup _ ParseName[pd]; ParseSeperator[pd]; groups _ NARROW[SymTab.Fetch[program.nameDutPinTimingGroup, name].val]; FOR s: DutPinTimingGroups _ groups, s.rest UNTIL s=NIL DO IF s.first.index=group.index THEN ERROR Error["Multiply defined signal"]; ENDLOOP; groups _ CONS[group, groups]; [] _ SymTab.Store[program.nameDutPinTimingGroup, name, groups]; ENDLOOP; }; DutPinFixturePin: ParseKeywordProc = { ParseToken[pd]; DO dutPin: CARD _ 0; fixturePin: REF CARD _ NEW[CARD]; IF pd.kind#tokenDECIMAL THEN EXIT; dutPin _ ParseNumber[pd]; fixturePin^ _ ParseNumber[pd]; ParseSeperator[pd]; IF NOT CardTab.Store[program.dutPinFixturePin, dutPin, fixturePin] THEN ERROR Error["Multiply defined Dut Pin"]; ENDLOOP; }; FixturePinChipChannel: ParseKeywordProc = { ParseToken[pd]; DO fixturePin: CARD _ 0; chipChannel: ChannelAddress _ NEW[ChannelAddressRec]; IF pd.kind#tokenDECIMAL THEN EXIT; fixturePin _ ParseNumber[pd]; chipChannel.chip _ ParseNumber[pd]; chipChannel.channel _ ParseNumber[pd]; ParseSeperator[pd]; IF NOT CardTab.Store[program.fixturePinChipChannel, fixturePin, chipChannel] THEN ERROR Error["Multiply defined Fixture Pin"]; ENDLOOP; }; ColumnNames: ParseKeywordProc = { position: CARD _ 0; pd.noticeCR _ TRUE; ParseToken[pd]; DO SELECT pd.kind FROM tokenSINGLE => { IF pd.char#IO.CR THEN ERROR Error["Expecting carriage return in ColumnNames"]; position _ 0; ParseToken[pd]; }; tokenID => { name: ROPE; IF Rope.Equal[pd.token, nextReserved, FALSE] THEN EXIT; IF Rope.Length[pd.token]#1 THEN ERROR Error["Expecting only one character in ColumnNames"]; position _ position + pd.charsSkipped + 1; name _ NARROW[CardTab.Fetch[program.columnNames, position].val]; name _ Rope.Concat[name, Rope.FromChar[pd.char]]; [] _ CardTab.Store[program.columnNames, position, name]; ParseToken[pd]; }; ENDCASE => ERROR Error["Expecting character in ColumnNames"]; ENDLOOP; pd.noticeCR _ FALSE; }; Vectors: ParseKeywordProc = { ConsVector: PROC = { IF triples#NIL THEN { triplesLength: CARD _ GList.Length[triples]; vectors _ CONS[NEW[ParsedVectorRec[triplesLength]], vectors]; FOR index: CARD DECREASING IN [0..triplesLength) DO vectors.first[index] _ triples.first; triples _ triples.rest; ENDLOOP; }; }; vectors: LIST OF ParsedVector _ NIL; triples: LIST OF ParsedVectorTriple _ NIL; position: CARD _ 0; vectorLength: CARD _ 0; pd.noticeCR _ TRUE; ParseToken[pd]; DO SELECT pd.kind FROM tokenSINGLE => { IF pd.char#IO.CR THEN ERROR Error["Expecting carriage return in Vectors"]; position _ 1; ParseToken[pd]; ConsVector[]; }; tokenDECIMAL, tokenID => { triple: ParsedVectorTriple; skip: CARD; size: CARD; IF Rope.Equal[pd.token, nextReserved, FALSE] THEN EXIT; triple _ NEW[ParsedVectorTripleRec]; skip _ pd.charsSkipped + Rope.Length[pd.token]; size _ VectorColumnToBusSize[program, position + pd.charsSkipped]; triple.value _ ConvertBoolSequence[pd.token, size]; ParseToken[pd]; position _ position + skip; [triple.inhibit, skip] _ ParseBoolSequence[pd, size]; position _ position + skip; [triple.mask, skip] _ ParseBoolSequence[pd, size]; position _ position + skip; triples _ CONS[triple, triples]; }; ENDCASE => ERROR Error["Unknown token type in Vectors"]; ENDLOOP; pd.noticeCR _ FALSE; ConsVector[]; vectorLength _ GList.Length[vectors]; program.vectors _ NEW[ParsedVectorsRec[vectorLength]]; FOR index: CARD DECREASING IN [0..vectorLength) DO program.vectors[index] _ vectors.first; vectors _ vectors.rest; ENDLOOP; }; VectorColumnToBusSize: PROC [program: Programming, column: CARD] RETURNS [size: CARD] = { name: ROPE _ NARROW[CardTab.Fetch[program.columnNames, column].val]; groups: DutPinTimingGroups; max: CARD _ 0; min: CARD _ LAST[CARD]; IF name=NIL THEN ERROR Error["Some column is not properly named"]; groups _ NARROW[SymTab.Fetch[program.nameDutPinTimingGroup, name].val]; IF groups=NIL THEN ERROR Error["Some signal has vectors but is not defined in NameDutPinTimingGroup"]; UNTIL groups=NIL DO max _ MAX[groups.first.index, max]; min _ MIN[groups.first.index, min]; groups _ groups.rest; ENDLOOP; size _ max-min+1; }; ParseBool: PROC [pd: ParseData] RETURNS [bool: BOOL] = { IF pd.kind#tokenID THEN ERROR Error["Expecting boolean"]; IF Rope.Length[pd.token]#1 THEN ERROR Error["Expecting only T or F for boolean"]; bool _ SELECT pd.char FROM 'T, 't => TRUE, 'F, 'f => FALSE, ENDCASE => ERROR Error["Expecting only T or F for boolean"]; ParseToken[pd]; }; ParseBoolSequence: PROC [pd: ParseData, size: CARD] RETURNS [bools: BoolSequence, skip: CARD] = { IF NOT (pd.kind=tokenDECIMAL OR pd.kind=tokenID) THEN ERROR Error["Expecting hexadecimal number in Vectors"]; bools _ ConvertBoolSequence[pd.token, size]; ParseToken[pd]; skip _ pd.charsSkipped + Rope.Length[pd.token]; }; ConvertBoolSequence: PROC [token: ROPE, size: CARD] RETURNS [bools: BoolSequence] = { boolIndex: CARD _ size; bools _ NEW[BoolSequenceRec[size]]; FOR index: CARD DECREASING IN [0..LOOPHOLE[Rope.Length[token]]) DO char: CHAR _ Rope.Fetch[token, index]; charValue: CARD _ SELECT TRUE FROM char IN ['0..'9] => char-'0, char IN ['a..'f] => char-'a+10, char IN ['A..'F] => char-'A+10, ENDCASE => ERROR Error["Bad hexadecimal digit in Vectors"]; THROUGH [1..4] DO boolIndex _ boolIndex - 1; bools[boolIndex] _ IF charValue MOD 2=1 THEN TRUE ELSE FALSE; IF boolIndex=0 THEN EXIT; charValue _ charValue / 2; ENDLOOP; IF boolIndex=0 AND index#0 THEN ERROR Error["Too many digits"]; ENDLOOP; }; ParseName: PROC [pd: ParseData, nextReserved: ROPE _ NIL] RETURNS [name: ROPE _ NIL] = { IF pd.kind#tokenID THEN ERROR Error["Expecting name or keyword"]; IF nextReserved#NIL AND Rope.Equal[pd.token, nextReserved, FALSE] THEN RETURN; name _ pd.token; ParseToken[pd]; }; ParseNumber, ParseNs: PROC [pd: ParseData] RETURNS [ns: Ns] = { IF pd.kind#tokenDECIMAL THEN ERROR Error["Expecting number"]; ns _ Convert.CardFromRope[pd.token]; ParseToken[pd]; }; ParseSeperator: PROC [pd: ParseData] = { IF pd.kind#tokenSINGLE THEN ERROR Error["Expecting seperator"]; IF pd.char#'; THEN ERROR Error["Bad seperator"]; ParseToken[pd]; }; ParseToken: PROC [pd: ParseData] = { DO { ParseOneToken[pd ! IO.EndOfStream => GOTO PopFile]; EXIT; EXITS PopFile => { IO.Close[pd.streams.first]; pd.streams _ pd.streams.rest; pd.names _ pd.names.rest; IF pd.streams=NIL THEN ERROR; -- more pops than includes? LOOP; }; }; ENDLOOP; }; ParseOneToken: PROC [pd: ParseData] = { ParseOnlyOneToken[pd]; IF pd.kind=tokenID AND Rope.Equal[pd.token, "Include", FALSE] THEN { file: ROPE; [] _ IO.SkipWhitespace[pd.streams.first ! IO.EndOfStream => GOTO BadInclude]; file _ IO.GetLineRope[pd.streams.first ! IO.EndOfStream => GOTO BadInclude]; pd.streams _ CONS[FS.StreamOpen[file], pd.streams]; pd.names _ CONS[file, pd.names]; ParseOnlyOneToken[pd]; EXITS BadInclude => ERROR Error["No file name for include"]; }; }; ParseOnlyOneToken: PROC [pd: ParseData] = { [pd.token, pd.charsSkipped] _ IO.GetTokenRope[pd.streams.first, IF pd.noticeCR THEN BreaksCR ELSE Breaks]; pd.char _ Rope.Fetch[pd.token]; pd.kind _ SELECT TRUE FROM Rope.Length[pd.token]=1 AND (IF pd.noticeCR THEN BreaksCR ELSE Breaks)[pd.char]=break => tokenSINGLE, pd.char IN ['0..'9] => tokenDECIMAL, ENDCASE => tokenID; }; BreaksCR: IO.BreakProc = { RETURN[SELECT char FROM IO.CR => break, IN [IO.NUL .. IO.SP] => sepr, '[, '], ';, '. => break, IN ['0..'9], IN ['a..'z], IN ['A..'Z] => other, ENDCASE => ERROR Error["Illegal character"]]; }; Breaks: IO.BreakProc = { RETURN[SELECT char FROM IN [IO.NUL .. IO.SP] => sepr, '[, '], ';, '. => break, IN ['0..'9], IN ['a..'z], IN ['A..'Z] => other, ENDCASE => ERROR Error["Illegal character"]]; }; debug: BOOL _ FALSE; minCopyLength: CARDINAL = 3; minCopyLengthNoLiteral: CARDINAL = 2; maxCopyLength: CARDINAL = 15; maxLiteralLength: CARDINAL = 64; bufferSize: CARD = 64; BufferIndex: TYPE = [0..bufferSize); DoubleByte: TYPE = RECORD [first, second: Byte10]; ColumnNameList: TYPE = LIST OF ColumnName; ColumnName: TYPE = REF ColumnNameRec; ColumnNameRec: TYPE = RECORD [ column: CARD, name: ROPE]; TripleSequence: TYPE = REF TripleSequenceRec; TripleSequenceRec: TYPE = RECORD [elements: SEQUENCE size: CARDINAL OF ChipTripleSequence]; -- chip ChipTripleSequence: TYPE = ARRAY ChannelIndex OF TripleSequenceIndex; TripleSequenceIndex: TYPE = RECORD [ assigned: BOOL, triple: CARDINAL, sequence: CARDINAL]; Transform: PUBLIC PROC [program: Programming] RETURNS [fixture: Fixture] = { FindMaxChip: CardTab.EachPairAction = { ca: ChannelAddress _ NARROW[val]; chips _ MAX[ca.chip+1, chips]; }; BuildColumnNames: CardTab.EachPairAction = { columnNames _ CONS[NEW[ColumnNameRec], columnNames]; columnNames.first.column _ key; columnNames.first.name _ NARROW[val]; }; CompareColumns: GList.CompareProc = { first: ColumnName _ NARROW[ref1]; second: ColumnName _ NARROW[ref2]; RETURN[Basics.CompareCard[first.column, second.column]]; }; FillFormatTiming: SymTab.EachPairAction = { FOR groups: DutPinTimingGroups _ NARROW[val], groups.rest UNTIL groups=NIL DO group: DutPinTimingGroup _ groups.first; fixturePin: REF CARD _ NARROW[CardTab.Fetch[program.dutPinFixturePin, group.dutPin].val]; chipChannel: ChannelAddress; timingGroup: TimingGroup; triplet: CARD _ 0; IF fixturePin=NIL THEN ERROR Error["No fixture pin assigned for dut pin"]; chipChannel _ NARROW[CardTab.Fetch[program.fixturePinChipChannel, fixturePin^].val]; IF chipChannel=NIL THEN ERROR Error["No chip and channel assigned for fixture pin"]; timingGroup _ NARROW[SymTab.Fetch[program.timingGroups, group.timingGroup].val]; IF timingGroup=NIL THEN ERROR; fixture[chipChannel.chip].formatTiming[chipChannel.channel] _ timingGroup; FOR names: ColumnNameList _ columnNames, names.rest UNTIL names=NIL DO IF Rope.Equal[names.first.name, key] THEN EXIT; triplet _ triplet + 1; REPEAT FINISHED => ERROR; ENDLOOP; map[chipChannel.chip][chipChannel.channel] _ [TRUE, triplet, group.index]; ENDLOOP; }; chips: CARD _ 0; map: TripleSequence; columnNames: ColumnNameList _ NIL; [] _ CardTab.Pairs[program.fixturePinChipChannel, FindMaxChip]; fixture _ AllocateFixture[chips]; map _ NEW[TripleSequenceRec[chips]]; FOR chipIndex: CARD IN [0..chips) DO FOR channelIndex: CARD IN [0..channelsPerChip) DO map[chipIndex][channelIndex] _ [FALSE, 0, 0]; ENDLOOP; ENDLOOP; [] _ CardTab.Pairs[program.columnNames, BuildColumnNames]; columnNames _ NARROW[GList.Sort[columnNames, CompareColumns]]; [] _ SymTab.Pairs[program.nameDutPinTimingGroup, FillFormatTiming]; FOR chipIndex: CARD IN [0..chips) DO IF debug THEN TerminalIO.PutF["\nchip: %g", IO.int[chipIndex]]; FillMaskInhibitVector[program, fixture[chipIndex], map[chipIndex]]; ENDLOOP; }; AllocateFixture: PROC [chips: CARD] RETURNS [fixture: Fixture] = { fixture _ NEW[FixtureRec[chips]]; FOR chipIndex: CARD IN [0..chips) DO chip: Chip _ NEW[ChipRec[vectorAddressesPerChip]]; fixture[chipIndex] _ chip; FOR channel: ChannelIndex IN ChannelIndex DO FOR imIndex: InhibitMaskIndex IN InhibitMaskIndex DO chip.inhibitMask[imIndex][channel].inhibit _ FALSE; chip.inhibitMask[imIndex][channel].mask _ FALSE; ENDLOOP; ENDLOOP; FOR vectorIndex: CARD IN [0..vectorAddressesPerChip) DO FOR byteIndex: ByteIndex IN ByteIndex DO chip.vectors[vectorIndex][byteIndex] _ 0; ENDLOOP; ENDLOOP; ENDLOOP; }; FillMaskInhibitVector: PROC [program: Programming, chip: Chip, map: ChipTripleSequence] = { LiteralLength: PROC RETURNS [length: NAT] = { length _ LOOPHOLE[nextInsert-copyLength-literalPosition, CARDINAL] MOD bufferSize; }; EmitLiteral: PROC [length: NAT] = { IF length>0 THEN { IF debug THEN TerminalIO.PutF["\nLit(%g):", IO.card[length]]; EmitByte10[length-1]; FOR index: NAT IN [0..length) DO vector: BitOps.BitDWord _ buffer[literalPosition+index]; IF debug THEN TerminalIO.PutF[" %x", IO.int[LOOPHOLE[vector]]]; EmitByte10[BitOps.ECFD[vector, 0, 10, 20]]; EmitByte10[BitOps.ECFD[vector, 10, 10, 20]]; ENDLOOP; literalPosition _ (literalPosition+length) MOD bufferSize }; }; EmitCopy: PROC = { IF copyLength>0 THEN { position: NAT _ LOOPHOLE[matchPosition+1-copyLength, CARDINAL] MOD bufferSize; IF debug THEN TerminalIO.PutF["\nCopy l:%g, p: %g", IO.card[copyLength], IO.card[position]]; EmitByte10[BitOps.WordOR[BitOps.WordShift[copyLength, 6], position]]; copyLength _ 0; literalPosition _ nextInsert; }; }; EmitByte10: PROC [byte: Byte10] = { IF firstFreeAddress=vectorAddressesPerChip AND firstFreeByte=bytesPerAddress THEN ERROR Error["Not enough storage for this vector set"]; chip.vectors[firstFreeAddress][firstFreeByte] _ byte; firstFreeByte _ firstFreeByte + 1; IF firstFreeByte=bytesPerAddress THEN { firstFreeByte _ 0; firstFreeAddress _ firstFreeAddress + 1; }; }; firstFreeInhibitMask: CARDINAL _ 0; firstFreeAddress: CARDINAL _ 0; firstFreeByte: CARDINAL _ 0; inhibitMaskIndex: InhibitMaskIndex; buffer: ARRAY BufferIndex OF BitOps.BitDWord _ ALL[BitOps.BitDWordZero]; matchBits: ARRAY BufferIndex OF BOOL _ ALL[FALSE]; literalPosition: CARDINAL _ 0; matchPosition: CARDINAL _ 0; copyLength: CARDINAL _ 0; nextInsert: CARDINAL _ 0; FOR cycle: CARD IN [0..program.vectors.size) DO Match: PROC [ignore: BOOL] RETURNS [match: BOOL _ FALSE] = { address: NAT _ nextInsert; previousMatch: BOOL _ matchBits[nextInsert]; DO nextMatch: BOOL; address _ (address + 1) MOD bufferSize; nextMatch _ matchBits[address]; matchBits[address] _ value=buffer[address] AND (previousMatch OR ignore); IF address=nextInsert THEN EXIT; previousMatch _ nextMatch; ENDLOOP; DO IF matchBits[address] THEN { match _ TRUE; matchPosition _ address; EXIT; }; address _ IF address=0 THEN bufferSize-1 ELSE address - 1; IF address=nextInsert THEN EXIT; ENDLOOP; }; value: BitOps.BitDWord _ BitOps.BitDWordZero; inhibitMask: InhibitMaskArray _ ALL[[FALSE, FALSE]]; vector: ParsedVector _ program.vectors[cycle]; FOR channelIndex: ChannelIndex IN ChannelIndex DO tsi: TripleSequenceIndex _ map[channelIndex]; value _ BitOps.DoubleShift[value, 1]; IF tsi.assigned THEN { triple: ParsedVectorTriple _ vector[tsi.triple]; IF triple.value[tsi.sequence] THEN value _ BitOps.DoubleOR[value, 1]; inhibitMask[channelIndex].inhibit _ triple.inhibit[tsi.sequence]; inhibitMask[channelIndex].mask _ triple.mask[tsi.sequence]; }; ENDLOOP; FOR inhibitMaskIndex IN [0..firstFreeInhibitMask) DO IF inhibitMask=chip.inhibitMask[inhibitMaskIndex] THEN EXIT; REPEAT FINISHED => { IF firstFreeInhibitMask=inhibitMaskPerChip THEN ERROR Error["Not enough inhibit/mask entries for this vector set"]; chip.inhibitMask[firstFreeInhibitMask] _ inhibitMask; firstFreeInhibitMask _ firstFreeInhibitMask + 1; }; ENDLOOP; value _ BitOps.DoubleOR[value, BitOps.DoubleShift[inhibitMaskIndex, 16]]; IF debug THEN TerminalIO.PutF["\nvalue: %x", IO.int[value]]; SELECT TRUE FROM copyLength=0 => IF Match[TRUE] THEN copyLength _ 1; Match[FALSE] => copyLength _ copyLength+1; copyLength>=minCopyLength => { EmitLiteral[LiteralLength[]]; EmitCopy[]; IF Match[TRUE] THEN copyLength _ 1; }; LiteralLength[]=0 AND copyLength>=minCopyLengthNoLiteral => { EmitCopy[]; IF Match[TRUE] THEN copyLength _ 1; }; ENDCASE => { copyLength _ 0; IF LiteralLength[]>=maxLiteralLength THEN EmitLiteral[maxLiteralLength]; }; buffer[nextInsert] _ value; nextInsert _ (nextInsert + 1) MOD bufferSize; { emitCopy: BOOL _ copyLength=maxCopyLength; length: NAT _ LiteralLength[]; IF emitCopy OR length=maxLiteralLength THEN EmitLiteral[length]; IF emitCopy THEN EmitCopy[]; }; ENDLOOP; IF copyLength < minCopyLength THEN { litLen: NAT; copyLength _ 0; litLen _ LiteralLength[]; IF litLen # 0 THEN EmitLiteral[litLen]; } ELSE EmitCopy[]; }; Pickle: PUBLIC PROC [fixture: Fixture, file: ROPE] = { }; Unpickle: PUBLIC PROC [file: ROPE] RETURNS [fixture: Fixture] = { }; END. žSageImpl.mesa Copyright Σ 1985, 1987 by Xerox Corporation. All rights reserved. Barth, October 13, 1988 6:21:32 pm PDT Parse Format Transform Pickle ΚΫ– "cedar" style˜codešœ ™ KšœB™BKšœ&™&K™—KšΟk œ#œ œ!˜ZK˜•StartOfExpansion[]šΟnœœ˜Kšœ#œ œ˜QKšœ˜Kšœœ˜—K˜Kš žœœœœœœ˜-headšœ™Kšœ œœ˜#šœœœ˜Kš œ œœœœ˜Kšœœœœ˜Kšœ œ˜Kšœœ˜ Kšœœ˜ Kšœœ ˜Kšœœ˜K˜—šœœœ5œ˜XK˜—š žœœœœœ˜BKš œ œœœœy˜–Kšœœœœe˜’Kšœœ˜"Kšœ œœ˜'Kšœ œ˜Kšœœ˜Kšœ œ˜Kšœ'˜'Kšœ0˜0Kšœ,˜,Kšœ1˜1Kšœ'˜'KšœΟc˜"š˜Kšœœœ˜7Kš œœ&œœœ˜WKšœ5˜5Kšœ˜Kšœ!˜!Kšœœœœ˜Kšœ˜—K˜K˜—šž œ˜"Kšœ˜š˜Kšœœœ˜Kšœœ˜)Kšœ&œœœ˜6™Kšœœœ(˜Ešœœœ˜Kšœœœ˜*Kšœœœ˜(Kšœœœ˜(Kšœœœ˜(Kšœœœ˜(Kšœœ&˜6—Kšœ˜—Kšœ˜Kšœ˜Kšœ˜Kšœ(˜(Kšœ%˜%Kšœ˜Kšœœ1œœ(˜iKšœ˜—Kšœ˜K˜—šžœ˜+Kšœ˜š˜Kšœœœ˜Kšœœ˜5Kšœ˜Kšœ&œœœ˜6šœœ˜Kšœ œœ˜0K˜Kšœ˜Kšœ œœ˜0K˜K˜—Kšœ˜Kšœ˜Kšœ"˜"Kšœ˜Kšœ œ8˜Gšœ(œœ˜9Kšœœœ"˜IKšœ˜—Kšœ œ˜Kšœ?˜?Kšœ˜—Kšœ˜K˜—šžœ˜&Kšœ˜š˜Kšœœ˜Kš œ œœœœ˜!Kšœœœ˜"Kšœ˜Kšœ˜Kšœ˜Kšœœ=œœ#˜pKšœ˜—Kšœ˜K˜—šžœ˜+Kšœ˜š˜Kšœ œ˜Kšœœ˜5Kšœœœ˜"Kšœ˜Kšœ#˜#Kšœ&˜&Kšœ˜KšœœGœœ'˜~Kšœ˜—Kšœ˜K˜—šž œ˜!Kšœ œ˜Kšœœ˜Kšœ˜š˜šœ ˜šœ˜Kš œ œœœœ3˜NKšœ ˜ Kšœ˜K˜—šœ ˜ Kšœœ˜ Kšœ$œœœ˜7Kšœœœ6˜[Kšœ*˜*Kšœœ3˜@K˜1Kšœ8˜8Kšœ˜K˜—Kšœœ-˜=—Kšœ˜—Kšœœ˜Kšœ˜K˜—šžœ˜šž œœ˜šœ œœ˜Kšœœ˜,Kšœ œœ+˜=š œœ œœ˜3Kšœ%˜%Kšœ˜Kšœ˜—K˜—K˜—Kšœ œœœ˜$Kšœ œœœ˜*Kšœ œ˜Kšœœ˜Kšœœ˜Kšœ˜š˜šœ ˜šœ˜Kš œ œœœœ/˜JKšœ ˜ Kšœ˜Kšœ ˜ K˜—šœ˜Kšœ˜Kšœœ˜ Kšœœ˜ Kšœ$œœœ˜7Kšœ œ˜$Kšœ/˜/KšœB˜BKšœ3˜3Kšœ˜Kšœ˜Kšœ5˜5Kšœ˜Kšœ2˜2Kšœ˜Kšœ œ˜ K˜—Kšœœ(˜8—Kšœ˜—Kšœœ˜Kšœ ˜ Kšœ%˜%Kšœœ!˜6š œœ œœ˜2Kšœ'˜'Kšœ˜Kšœ˜—šœ˜K˜——š Πbnœœ œœœ˜YKšœœœ1˜DKšœ˜Kšœœ˜Kšœœœœ˜Kšœœœœ,˜BKšœ œ8˜GKšœœœœN˜fšœœ˜Kšœœ˜#Kšœœ˜#Kšœ˜Kšœ˜—K˜K˜K˜—šž œœœœ˜8Kšœœœ˜9Kšœœœ,˜Qšœœ ˜Kšœ œ˜Kšœ œ˜Kšœœ,˜<—Kšœ˜Kšœ˜K˜—š žœœœœœ˜aKš œœœœœ2˜mKšœ,˜,Kšœ˜Kšœ/˜/Kšœ˜K˜—š žœœ œœœ˜UKšœ œ˜Kšœœ˜#š œœ œœœ˜BKšœœ˜&šœ œœœ˜"Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ+˜;—šœ˜Kšœ˜Kš œœ œœœœœ˜=Kšœ œœ˜Kšœ˜Kšœ˜—Kšœ œ œœ˜?Kšœ˜—Kšœ˜K˜—šž œœœœœœœ˜XKšœœœ$˜AKš œœœ$œœœ˜NK˜Kšœ˜Kšœ˜K˜—šœ žœœœ ˜?Kšœœœ˜=Kšœ$˜$Kšœ˜Kšœ˜K˜—šžœœ˜(Kšœœœ˜?Kšœ œœ˜0Kšœ˜Kšœ˜K˜—šž œœ˜$š˜šœ˜Kšœœœ ˜3Kšœ˜šœ ˜Kšœ˜Kšœ˜Kšœ˜Kš œ œœœŸ˜:Kšœ˜K˜—K˜—Kšœ˜—Kšœ˜K˜—šž œœ˜'Kšœ˜šœœ!œœ˜DKšœœ˜ Kšœœ#œœ ˜MKšœœ œœ ˜LKšœ œœ˜3Kšœ œ˜ Kšœ˜Kšœœ#˜KšœC˜Cšœ œœ ˜$Jšœœœ˜?KšœC˜CKšœ˜—K˜K˜—šžœœ œœ˜BKšœ œ˜!šœ œœ ˜$Kšœ œ"˜2Kšœ˜šœœ˜,šœœ˜4Kšœ-œ˜3Kšœ*œ˜0Kšœ˜—Kšœ˜—šœœœ˜7šœœ ˜(Kšœ)˜)Kšœ˜—Kšœ˜—Kšœ˜—K˜K˜—šžœœ@˜[šž œœœ œ˜-Kšœ œ(œœ ˜RKšœ˜—šž œœ œ˜#šœ œ˜Kšœœœ˜=K˜šœœœ ˜ Kšœ8˜8Kšœœœœ ˜?Kšœœ˜+Kšœœ˜,Kšœ˜—Kšœ+œ ˜9K˜—Kšœ˜—šžœœ˜šœœ˜Kš œ œœœœ ˜NJšœœ'œœ˜\JšœE˜EKšœ˜Kšœ˜K˜—Kšœ˜—šž œœ˜#Kšœ)œœœ1˜ˆKšœ5˜5Kšœ"˜"šœœ˜'Kšœ˜Kšœ(˜(K˜—Kšœ˜—Kšœœ˜#Kšœœ˜Kšœœ˜Kšœ#˜#Kšœœ œœ˜HKš œ œ œœœœ˜2Kšœœ˜Kšœœ˜Kšœ œ˜Kšœ œ˜šœœœ˜/š žœœ œœ œœ˜˜sKšœ5˜5Kšœ0˜0K˜—Kšœ˜—KšœI˜IJšœœ œ ˜<šœœ˜Kšœœœœ˜3Kšœœ˜*šœ˜Kšœ˜K˜ Kšœœœ˜#Kšœ˜—šœœ(˜=K˜ Kšœœœ˜#Kšœ˜—šœ˜ Kšœ˜Kšœ#œ˜HK˜——Kšœ˜Kšœœ ˜-šœ˜Kšœ œ˜*Kšœœ˜Kšœ œœ˜@Kšœ œ ˜K˜—Kšœ˜—šœœ˜$Kšœœ˜ Kšœ˜Kšœ˜Kšœ œ˜'Kšœ˜—Kšœ ˜K˜K˜——šœ™šžœœœœ˜6K˜K˜—š žœœœœœ˜AK˜K˜——Kšœ˜K˜—…—Hjaγ