DIRECTORY Convert USING [IntFromRope, RealFromRope], IO USING [Backup, EndOfStream, GetChar, GetIndex, PutFR, STREAM, text], IPBasic USING [Op, Rational], IPReader, IPWritten, RefText USING [Append, Find, InlineAppendChar, ObtainScratch, ReleaseScratch, TrustTextAsRope], Rope USING [ROPE], SymTab USING [Create, Fetch, Insert, Ref]; IPWrittenReaderImpl: CEDAR PROGRAM IMPORTS Convert, IO, IPReader, IPWritten, RefText, SymTab ~ BEGIN OPEN IPReader, IPWritten; ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; Op: TYPE ~ IPBasic.Op; Rational: TYPE ~ IPBasic.Rational; InvertEncodingTable: PROC[encode: EncodingTable] RETURNS[SymTab.Ref] = { decode: SymTab.Ref ~ SymTab.Create[mod: 101, case: TRUE]; FOR op: Op IN Op DO key: ROPE ~ encode[op]; IF key#NIL THEN { opRef: REF Op ~ NEW[Op _ op]; IF decode.Insert[key: key, val: opRef] THEN NULL ELSE ERROR; }; ENDLOOP; RETURN[decode]; }; decode: SymTab.Ref ~ InvertEncodingTable[GetEncodingTable[]]; Decode: PROC[rope: ROPE] RETURNS[Op] ~ { found: BOOL; val: REF; [found: found, val: val] _ decode.Fetch[key: rope]; IF found THEN { opRef: REF Op ~ NARROW[val]; RETURN[opRef^] } ELSE RETURN[nil]; }; writtenReader: Class ~ NEW[ClassRep _ [ encoding: $Written, getToken: WrittenGetToken, readInt: WrittenReadInt, readReal: WrittenReadReal, readRational: WrittenReadRational, readVector: WrittenReadVector ]]; WrittenGetToken: PROC[reader: Reader, flushComments: BOOL _ TRUE] ~ { stream: STREAM ~ reader.stream; token: Token _ nullToken; text: REF TEXT _ NIL; DO state: {begin, op, id, plus, minus, ast, int, den1, den2, den3, dot, frac, exp1, exp2, exp3, string, escape, com, com1, file, file1, ann, ann1} _ begin; err: {ok, illegalChar, invalidRational, invalidReal, invalidEscape} _ ok; char: CHAR; -- last character fetched got: BOOL _ TRUE; -- true if got char from stream, false if char is fake space at end esc: NAT; -- starting index of escape sequence BeginEscape: PROC ~ { esc _ text.length-1 }; EndEscape: PROC ~ { start: NAT ~ esc+1; stop: NAT ~ text.length-1; val: CARDINAL _ 0; IF stop<=start THEN err _ invalidEscape ELSE FOR i: NAT IN[start..stop) DO c: CHAR ~ text[i]; IF c IN['0..'9] THEN val _ val*10+(c-'0) ELSE { err _ invalidEscape; EXIT }; IF val>255 THEN { val _ 0; err _ invalidEscape; EXIT }; ENDLOOP; text[esc] _ 0C+val; text.length _ esc+1; }; reader.index _ stream.GetIndex[]; DO -- skip white space char _ stream.GetChar[! IO.EndOfStream => GOTO EndOfFile]; IF char IN['\041..'\176] THEN EXIT ELSE reader.index _ reader.index+1; ENDLOOP; text _ reader.buffer; text.length _ 0; DO -- scan a token text _ RefText.InlineAppendChar[text, char]; -- tentatively append char to token text { SELECT state FROM begin => SELECT char FROM IN['A..'Z] => { state _ op }; -- begin op IN['a..'z] => { state _ id }; -- begin identifier IN['0..'9] => { state _ int }; -- begin number '{ => { token.op _ beginBody; GOTO SingleOp }; '} => { token.op _ endBody; GOTO SingleOp }; '[ => { token.op _ beginVec; GOTO SingleOp }; '] => { token.op _ endVec; GOTO SingleOp }; ', => { token.op _ comma; GOTO SingleOp }; '< => { state _ string; GOTO Begin }; -- begin string '+ => { state _ plus }; -- might begin number or insertfile '- => { state _ minus }; -- might begin number or annotation '* => { state _ ast }; -- might begin comment '. => { state _ dot }; -- might begin real ENDCASE => { err _ illegalChar; GOTO Stop }; op => SELECT char FROM IN['A..'Z], IN['0..'9] => { }; -- extend op IN['a..'z], '- => { state _ id }; -- switch to id ENDCASE => { token.type _ op; GOTO Back }; -- op token id => SELECT char FROM IN['a..'z], IN['A..'Z], IN['0..'9], '- => { }; -- extend id ENDCASE => { token.type _ identifier; GOTO Back }; plus => SELECT char FROM IN['0..'9] => { state _ int }; -- first integer digit '+ => { state _ file; GOTO Begin }; -- begin insertfile ENDCASE => { err _ illegalChar; GOTO Back }; -- plus sign alone minus => SELECT char FROM IN['0..'9] => { state _ int }; -- first integer digit '- => { state _ ann; GOTO Begin }; -- begin annotation ENDCASE => { err _ illegalChar; GOTO Back }; -- minus sign alone ast => SELECT char FROM '* => { state _ com; GOTO Begin }; -- begin comment ENDCASE => { err _ illegalChar; GOTO Back }; -- asterisk alone int => SELECT char FROM IN['0..'9] => { }; -- integer digit '. => { state _ frac }; -- fraction follows '/ => { state _ den1 }; -- denominator follows 'E, 'e => { state _ exp1 }; -- exponent follows ENDCASE => { token.type _ integer; GOTO Back }; -- integer token den1 => SELECT char FROM IN['0..'9] => { state _ den3 }; -- first denominator digit '+, '- => { state _ den2 }; -- denominator sign ENDCASE => { err _ invalidRational; GOTO Back }; den2 => SELECT char FROM IN['0..'9] => { state _ den3 }; -- first denominator digit (after sign) ENDCASE => { err _ invalidRational; GOTO Back }; den3 => SELECT char FROM IN['0..'9] => { }; -- denominator digit ENDCASE => { token.type _ rational; GOTO Back }; -- rational token dot => SELECT char FROM IN['0..'9] => { state _ frac }; -- first fraction digit ENDCASE => { err _ illegalChar; GOTO Back }; -- dot alone frac => SELECT char FROM IN['0..'9] => { }; -- fraction digit 'E, 'e => { state _ exp1 }; -- exponent follows ENDCASE => { token.type _ real; GOTO Back }; -- real token (no exponent) exp1 => SELECT char FROM IN['0..'9] => { state _ exp3 }; -- first exponent digit '+, '- => { state _ exp2 }; -- exponent sign ENDCASE => { err _ invalidReal; GOTO Back }; exp2 => SELECT char FROM IN['0..'9] => { state _ exp3 }; -- first exponent digit (after sign) ENDCASE => { err _ invalidReal; GOTO Back }; exp3 => SELECT char FROM IN['0..'9] => { }; -- exponent digit ENDCASE => { token.type _ real; GOTO Back }; -- real token (with exponent) string => SELECT char FROM '> => { token.type _ string; GOTO Lop1 }; -- end string token '# => { BeginEscape[]; state _ escape }; -- begin escape sequence ENDCASE => { }; -- extend string escape => SELECT char FROM '# => { EndEscape[]; state _ string }; -- end escape sequence ENDCASE => { }; -- extend escape sequence com => SELECT char FROM '* => { state _ com1 }; -- look for second * ENDCASE => { }; -- extend comment com1 => SELECT char FROM '* => { token.type _ comment; GOTO Lop2 }; -- end comment ENDCASE => { state _ com }; -- continue comment file => SELECT char FROM '+ => { state _ file1 }; -- look for second + ENDCASE => { }; -- extend file name file1 => SELECT char FROM '+ => { token.type _ insertfile; GOTO Lop2 }; -- end insertfile ENDCASE => { state _ file }; -- continue file name ann => SELECT char FROM '- => { state _ ann1 }; -- look for second - ENDCASE => { }; -- extend annotation ann1 => SELECT char FROM '- => { token.type _ annotation; GOTO Lop2 }; -- end annotation ENDCASE => { state _ ann }; -- continue annotation ENDCASE => ERROR; -- unknown state EXITS Begin => text.length _ 0; SingleOp => { token.type _ op; EXIT }; Stop => { EXIT }; Lop1 => { text.length _ text.length-1; EXIT }; Lop2 => { text.length _ text.length-2; EXIT }; Back => { IF got THEN stream.Backup[char]; text.length _ text.length-1; EXIT }; }; char _ stream.GetChar[! IO.EndOfStream => IF got THEN { char _ ' ; got _ FALSE; CONTINUE }]; ENDLOOP; reader.text _ text; IF err#ok THEN ERROR Error[$illegalToken, SELECT err FROM illegalChar => "Illegal character.", invalidRational => "Invalid rational.", invalidReal => "Invalid real number.", invalidEscape => "Invalid escape sequence in string.", ENDCASE => NIL]; SELECT token.type FROM $nil => ERROR Error[$bug, "WrittenGetToken didn't set token type."]; $op => IF token.op=$nil THEN { token.op _ Decode[RefText.TrustTextAsRope[text]]; IF token.op=$nil THEN ERROR Error[$illegalToken, IO.PutFR["%g is not a primitive.", IO.text[text]]]; }; $comment, $annotation => IF flushComments THEN LOOP; ENDCASE; EXIT; REPEAT EndOfFile => token.type _ eof; ENDLOOP; reader.token _ token; }; ParseInt: PROC[text: REF TEXT, start: NAT _ 0, len: NAT _ NAT.LAST] RETURNS[x: INT] ~ { s: NAT ~ MIN[start, text.length]; n: NAT ~ MIN[len, text.length-s]; IF s=0 AND n=text.length THEN x _ Convert.IntFromRope[RefText.TrustTextAsRope[text]] ELSE { scratch: REF TEXT ~ RefText.ObtainScratch[n]; { ENABLE UNWIND => RefText.ReleaseScratch[scratch]; temp: REF TEXT ~ RefText.Append[to: scratch, from: text, start: s, len: n]; x _ Convert.IntFromRope[RefText.TrustTextAsRope[temp]]; }; RefText.ReleaseScratch[scratch]; }; }; ParseReal: PROC[text: REF TEXT, start: NAT _ 0, len: NAT _ NAT.LAST] RETURNS[x: REAL] ~ { s: NAT ~ MIN[start, text.length]; n: NAT ~ MIN[len, text.length-s]; IF s=0 AND n=text.length THEN x _ Convert.RealFromRope[RefText.TrustTextAsRope[text]] ELSE { scratch: REF TEXT ~ RefText.ObtainScratch[n]; { ENABLE UNWIND => RefText.ReleaseScratch[scratch]; temp: REF TEXT ~ RefText.Append[to: scratch, from: text, start: s, len: n]; x _ Convert.RealFromRope[RefText.TrustTextAsRope[temp]]; }; RefText.ReleaseScratch[scratch]; }; }; WrittenReadInt: PROC[reader: Reader] RETURNS[INT] ~ { SELECT reader.token.type FROM $shortNumber => RETURN[reader.shortNumber]; $integer => RETURN[ParseInt[reader.text]]; ENDCASE => ERROR Error[$wrongType, "Token is not an integer."]; }; WrittenReadRational: PROC[reader: Reader] RETURNS[Rational] ~ { text: REF TEXT ~ reader.text; SELECT reader.token.type FROM $rational => { slash: NAT ~ RefText.Find[s1: text, s2: "/"]; { ENABLE Error => IF code=$overflow THEN GOTO Overflow; num: INT ~ ParseInt[text: text, len: slash]; den: INT ~ ParseInt[text: text, start: slash+1]; RETURN[[num: num, den: den]]; EXITS Overflow => Error[$overflow, "Number is too big for an INT."]; }; }; ENDCASE => ERROR Error[$wrongType, "Token is not a rational."]; }; WrittenReadReal: PROC[reader: Reader] RETURNS[REAL] ~ { SELECT reader.token.type FROM $integer, $real => RETURN[ParseReal[reader.text]]; ENDCASE => ERROR Error[$wrongType, "Token type is not $integer or $real."]; }; WrittenReadVector: PROC[reader: Reader] RETURNS[EncodedVector] ~ { type: TokenType ~ reader.token.type; SELECT reader.token.type FROM $vector => ERROR Error[$unimplemented, "Can't handle $vector yet."]; ENDCASE => ERROR Error[$wrongType, "Token is not a vector."]; }; Register[writtenReader]; END. ZIPWrittenReaderImpl.mesa Last edited by: Doug Wyatt, March 9, 1984 4:59:23 pm PST Ê H˜Jšœ™J™šœ™Jšœ(™(—J˜šÏk ˜ Jšœœ˜*Jšœœ1œ˜GJšœœ˜J˜ J˜ JšœœR˜_Jšœœœ˜Jšœœ˜*J˜—Jšœœ˜"Jšœ œ&˜9Jšœœœ˜!J˜Jšœœœ˜Jšœœœœ˜J˜Jšœœ˜Jšœ œ˜"J˜šÏnœœœ˜HJšœ3œ˜9šœœ˜Jšœœ˜šœœœ˜Jšœœœ ˜Jš œ%œœœœ˜—šœœ˜JšœŸ˜#JšœŸ˜+JšœŸ˜.JšœŸ˜/Jšœœ Ÿ˜@—šœœ˜JšœŸ˜:JšœŸ˜/Jšœœ˜0—šœœ˜JšœŸ'˜GJšœœ˜0—šœœ˜JšœŸ˜'JšœœŸ˜B—šœœ˜JšœŸ˜7Jšœœ Ÿ ˜9—šœœ˜JšœŸ˜$JšœŸ˜/Jšœœ Ÿ˜H—šœœ˜JšœŸ˜7JšœŸ˜,Jšœœ˜,—šœœ˜JšœŸ$˜DJšœœ˜,—šœœ˜JšœŸ˜$Jšœœ Ÿ˜J—šœ œ˜Jšœœ Ÿ˜=Jšœ)Ÿ˜AJšœ Ÿ˜ —šœ œ˜Jšœ'Ÿ˜=Jšœ Ÿ˜)—šœœ˜JšœŸ˜,Jšœ Ÿ˜!—šœœ˜Jšœœ Ÿ˜9JšœŸ˜/—šœœ˜JšœŸ˜-Jšœ Ÿ˜#—šœ œ˜Jšœ!œ Ÿ˜?JšœŸ˜2—šœœ˜JšœŸ˜,Jšœ Ÿ˜$—šœœ˜Jšœ!œ Ÿ˜?JšœŸ˜2—JšœœŸ˜"—š˜J˜Jšœœ˜&Jšœ œ˜Jšœ'œ˜.Jšœ'œ˜.Jšœ œœ3œ˜O—J˜šœ˜Jš œœœœœ˜D—Jšœ˜—J˜š œœœœ˜9Jšœ$˜$Jšœ'˜'Jšœ&˜&Jšœ6˜6Jšœœ˜—šœ ˜Jšœœ7˜Dšœœœ˜Jšœ1˜1šœœœ˜0Jšœ!œ˜3—Jšœ˜—Jšœœœœ˜4Jšœ˜—Jšœ˜š˜Jšœ˜—Jšœ˜—J˜J˜—J˜šžœœœœ œ œœœœœ˜WJšœœœ˜!Jšœœœ˜!Jšœœœ7˜Tšœ˜Jšœ œœ˜-šœœœ$˜3Jšœœœ=˜KJšœ7˜7J˜—Jšœ ˜ J˜—J˜J˜—šž œœœœ œ œœœœœ˜YJšœœœ˜!Jšœœœ˜!Jšœœœ8˜Ušœ˜Jšœ œœ˜-šœœœ$˜3Jšœœœ=˜KJšœ8˜8J˜—Jšœ ˜ J˜—J˜J˜—šžœœœœ˜5šœ˜Jšœœ˜+Jšœ œ˜*Jšœœ/˜?—J˜J˜—šžœœœ˜?Jšœœœ˜šœ˜šœ˜Jšœœ#˜-š œœ œœœ ˜7Jšœœ$˜,Jšœœ(˜0Jšœ˜Jšœ?˜DJ˜—J˜—Jšœœ/˜?—J˜J˜—šžœœœœ˜7šœ˜Jšœœ˜2Jšœœ;˜K—J˜J˜—šžœœœ˜BJ˜$šœ˜Jšœ œ4˜DJšœœ-˜=—J˜J˜—J˜J˜J˜Jšœ˜—…—'¶4X