SageImpl.mesa
Copyright Ó 1985, 1987 by Xerox Corporation. All rights reserved.
Barth, October 13, 1988 6:21:32 pm PDT
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: ROPENIL] = CODE;
Parse
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 ROPELIST ["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: ROPENIL;
group: TimingGroup ← NEW[TimingGroupRec];
IF (name ← ParseName[pd, nextReserved])=NIL THEN EXIT;
Format
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: ROPENIL;
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 CARDNEW[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: ROPENARROW[CardTab.Fetch[program.columnNames, column].val];
groups: DutPinTimingGroups;
max: CARD ← 0;
min: CARDLAST[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: CARDSELECT 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: ROPENIL] RETURNS [name: ROPENIL] = {
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"]];
};
Transform
debug: BOOLFALSE;
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 CARDNARROW[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: NATLOOPHOLE[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 BOOLALL[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: BOOLFALSE] = {
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
Pickle: PUBLIC PROC [fixture: Fixture, file: ROPE] = {
};
Unpickle: PUBLIC PROC [file: ROPE] RETURNS [fixture: Fixture] = {
};
END.