<> <> DIRECTORY Ascii, Basics, BasicTime, Convert, FS, IO, OrderedSymbolTableRef, Real, Rope, TermCaps; TermCapsImpl: CEDAR PROGRAM IMPORTS Ascii, BasicTime, Convert, FS, IO, OrderedSymbolTableRef, Real, Rope EXPORTS TermCaps = {OPEN TermCaps; cancel: PUBLIC Val _ NEW [ROPE _ "cancel"]; Bitch: SIGNAL [fmt: ROPE, v1: IO.Value _ [null[]]] = CODE; Busted: ERROR = CODE; termCaps: SymbolTable _ NIL; defaultTerm: Term _ NIL; EnumerateCaps: PUBLIC PROC [term: Term, consume: PROC [cap: Cap] RETURNS [stop: BOOL]] = { ExceptNone: PROC [cap: Cap] RETURNS [e: BOOL] = {e _ FALSE}; Work: PROC [term: Term, except: PROC [cap: Cap] RETURNS [BOOL]] = { ExceptMine: PROC [cap: Cap] RETURNS [e: BOOL] = { IF term.caps.Lookup[cap] # NIL THEN RETURN [TRUE]; e _ except[cap]}; PerCap: PROC [ra: REF ANY] RETURNS [stop: BOOL] = { cap: Cap _ NARROW[ra]; stop _ FALSE; IF cap.val = cancel THEN RETURN; IF except[cap] THEN RETURN; stop _ consume[cap]; }; term.caps.EnumerateIncreasing[PerCap]; IF term.default # NIL THEN Work[term.default, ExceptMine]; }; Work[term, ExceptNone]; }; GetCap: PUBLIC PROC [term: Term, name: ROPE, default: Val _ NIL] RETURNS [val: Val] = { cap: Cap _ NARROW[term.caps.Lookup[name]]; IF cap # NIL THEN RETURN [IF cap.val = cancel THEN default ELSE cap.val]; IF term.default # NIL THEN val _ GetCap[term.default, name, default] ELSE val _ default; }; GetTerm: PUBLIC PROC [tc: TermCap, name: ROPE] RETURNS [term: Term] = { term _ AntiAlias[tc.contents.Lookup[name]]; }; TCTerm: PROC [tc: TermCap, name: ROPE] RETURNS [term: Term] = { term _ AntiAlias[tc.contents.Lookup[name]]; IF term = NIL THEN { a: Alias _ NEW [AliasRep _ [ name: name, val: term _ NEW [TermRep _ [ names: LIST[name], caps: OrderedSymbolTableRef.CreateTable[CompareCaps], default: defaultTerm]] ]]; tc.contents.Insert[a]; }; }; EnumerateTerms: PUBLIC PROC [tc: TermCap, consume: PROC [term: Term] RETURNS [stop: BOOL]] = { PerAlias: PROC [ra: REF ANY] RETURNS [stop: BOOL] = { a: Alias _ NARROW[ra]; IF NOT (a.name = a.val.names.first) THEN RETURN [FALSE]; stop _ consume[a.val]; }; tc.contents.EnumerateIncreasing[PerAlias]; }; GetTermCap: PUBLIC PROC [fileName: ROPE, wDir: ROPE _ NIL] RETURNS [tc: TermCap] = { gName: ROPE; created: GMT; exists: BOOL _ TRUE; rfi: REF FileID; from: IO.STREAM; [fullFName: gName, created: created] _ FS.FileInfo[name: fileName, wDir: wDir !FS.Error => {exists _ FALSE; CONTINUE}]; IF NOT exists THEN RETURN [NIL]; rfi _ NEW [FileID _ [gName, created]]; tc _ NARROW[termCaps.Lookup[rfi]]; IF tc # NIL THEN RETURN [tc]; tc _ NEW [TermCapRep _ [ fileID: rfi^, contents: OrderedSymbolTableRef.CreateTable[CompareTerms] ]]; from _ FS.StreamOpen[gName]; termCaps.Insert[tc]; ParseTermCap[from, tc ! Bitch => { tc.complaints _ tc.complaints.Cat[ IO.PutFR[ Rope.Cat["At %g[%g]: ", fmt, "\n"], IO.rope[gName], IO.int[from.GetIndex[]], v1] ]; RESUME; }; IO.EndOfStream => CONTINUE; ]; }; ParseTermCap: PROC [from: IO.STREAM, tc: TermCap] = { atStart: BOOL _ TRUE; DO ENABLE Busted => {Bitch["busted"]; LOOP}; term: Term _ NIL; names: RopeList _ NIL; nameCount: NAT _ 0; bestName: ROPE _ NIL; <> DO char: CHAR; IF atStart THEN atStart _ FALSE ELSE [] _ from.GetLineRope[]; char _ from.PeekChar[]; SELECT char FROM IN ['a .. 'z], IN ['A .. 'Z] => EXIT; ENDCASE; ENDLOOP; <> DO name, sep: ROPE; namedTerm: Term; name _ from.GetTokenRope[TermNameBuster].token; IF name.Equal["|"] THEN {Bitch["empty name"]; LOOP}; IF name.Equal[":"] THEN {Bitch["empty name"]; EXIT}; nameCount _ nameCount + 1; IF nameCount <= 2 THEN bestName _ name; namedTerm _ GetTerm[tc, name]; IF namedTerm = term THEN NULL ELSE IF term = NIL THEN term _ namedTerm ELSE IF namedTerm # NIL THEN Bitch["term name collision on %g", IO.rope[name]]; IF namedTerm = NIL THEN names _ CONS[name, names]; sep _ from.GetTokenRope[TermNameBuster].token; IF sep.Equal[":"] THEN EXIT; IF NOT sep.Equal["|"] THEN ERROR; ENDLOOP; IF term = NIL THEN term _ NEW [TermRep _ [ caps: OrderedSymbolTableRef.CreateTable[CompareCaps], default: defaultTerm ]]; term.bestName _ bestName; FOR names _ names, names.rest WHILE names # NIL DO a: Alias _ NEW [AliasRep _ [names.first, term]]; term.names _ CONS[names.first, term.names]; tc.contents.Insert[a !OrderedSymbolTableRef.DuplicateKey => { CONTINUE--can only collide with same term--}]; ENDLOOP; <> DO capName: ROPE; cap: Cap; cancelIt: BOOL _ FALSE; char: CHAR; <> IF from.PeekChar[] = '\\ THEN { char: CHAR; IF (char _ from.GetChar[]) # '\\ THEN ERROR; IF NOT White[from.PeekChar[]] THEN Busted[]; [] _ from.SkipWhitespace[]; IF (char _ from.GetChar[]) # ': THEN Busted[]; LOOP; }; IF White[from.PeekChar[]] THEN EXIT--done with caps--; capName _ from.GetTokenRope[CapNameBuster].token; IF capName.Length[] = 1 THEN Busted[]; cap _ NEW [CapRep _ [capName, NIL]]; char _ from.GetChar[]; IF char = '@ THEN { cancelIt _ TRUE; char _ from.GetChar[]; }; SELECT char FROM ': => cap.val _ NEW [BOOL _ TRUE]; '# => { cap.val _ NEW [INT _ from.GetInt[!IO.Error => Busted[]]]; IF from.GetChar[] # ': THEN Busted[]; }; '= => cap.val _ ParseString[from]; ENDCASE => Busted[]; IF cancelIt THEN cap.val _ cancel; IF cap.name.Equal["tc"] THEN { defName: String _ NIL; default: Term; WITH cap.val SELECT FROM s: String => defName _ s; ENDCASE => defName _ NIL; IF defName = NIL THEN Bitch["tc not a string"] ELSE { default _ defaultTerm; IF defName.str.Length[] > 0 THEN default _ TCTerm[tc, defName.str] ELSE { i: INT _ Real.RoundLI[defName.pad]; IF i # 0 THEN { name: ROPE _ Convert.RopeFromInt[i]; default _ TCTerm[tc, name] }; }; term.default _ default }; } ELSE { term.caps.Insert[cap !OrderedSymbolTableRef.DuplicateKey => CONTINUE --left value overrides right--]; }; ENDLOOP; ENDLOOP; }; TermNameBuster: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = { cc _ SELECT char FROM '|, ': => break, ENDCASE => other}; CapNameBuster: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = { cc _ SELECT char FROM ':, '#, '=, '@ => break, IN [0C .. Ascii.SP] => sepr, ENDCASE => other}; White: PROC [c: CHAR] RETURNS [white: BOOL] = {white _ c IN [0C .. Ascii.SP]}; Numeric: PROC [c: CHAR] RETURNS [numeric: BOOL] = {numeric _ (c IN ['0 .. '9]) OR (c = '.)}; ParseString: PROC [from: IO.STREAM] RETURNS [s: String] = { padChars, strVal: ROPE _ NIL; char: CHAR; dotSeen, proportional: BOOL _ FALSE; FOR char _ from.PeekChar[], from.PeekChar[] WHILE Numeric[char] DO IF from.GetChar[] # char THEN ERROR; IF char = '. THEN dotSeen _ TRUE; padChars _ padChars.Cat[Rope.FromChar[char]]; ENDLOOP; IF from.PeekChar[] = '* THEN { IF from.GetChar[] # '* THEN ERROR; proportional _ TRUE}; DO char _ from.GetChar[]; IF char = ': THEN EXIT; SELECT char FROM '^ => {char _ from.GetChar[]; char _ Ascii.Control[char]}; '\\ => {char _ from.GetChar[]; SELECT char FROM 'E => char _ Ascii.ESC; 'n => char _ '\012; 'r => char _ '\015; 't => char _ '\t; 'b => char _ '\010; 'f => char _ '\014; IN ['0 .. '7] => { octChars: ROPE _ Rope.FromChar[char]; IF from.PeekChar[] IN ['0 .. '7] THEN { octChars _ octChars.Concat[Rope.FromChar[from.GetChar[]]]; IF from.PeekChar[] IN ['0 .. '7] THEN { octChars _ octChars.Concat[Rope.FromChar[from.GetChar[]]]; }; }; char _ '\000 + Convert.CardFromRope[octChars, 8]; }; '^, '\\ => char _ char; ENDCASE => char _ char --really should Bitch, but UNIX is so sloppy, this saves a lot of headaches--; }; ENDCASE; strVal _ strVal.Cat[Rope.FromChar[char]]; ENDLOOP; s _ NEW [StringRep _ [ pad: IF padChars = NIL THEN 0.0 ELSE IF dotSeen THEN Convert.RealFromRope[padChars] ELSE REAL[Convert.IntFromRope[padChars]], proportional: proportional, str: strVal]]; }; CompareCaps: PROC [r1, r2: REF ANY] RETURNS [c: Basics.Comparison] = { Key: PROC [r: REF ANY] RETURNS [name: ROPE] = { WITH r SELECT FROM c: Cap => name _ c.name; r: ROPE => name _ r; ENDCASE => ERROR}; c _ Key[r1].Compare[Key[r2]]; }; CompareTerms: PROC [r1, r2: REF ANY] RETURNS [c: Basics.Comparison] = { Key: PROC [r: REF ANY] RETURNS [name: ROPE] = { WITH r SELECT FROM a: Alias => name _ a.name; r: ROPE => name _ r; ENDCASE => ERROR}; c _ Key[r1].Compare[Key[r2]]; }; CompareTermCaps: PROC [r1, r2: REF ANY] RETURNS [c: Basics.Comparison] = { Key: PROC [r: REF ANY] RETURNS [fi: FileID] = { WITH r SELECT FROM tc: TermCap => fi _ tc.fileID; rfi: REF FileID => fi _ rfi^; ENDCASE => ERROR}; fi1: FileID _ Key[r1]; fi2: FileID _ Key[r2]; c _ fi1.gName.Compare[fi2.gName, FALSE]; IF c = equal THEN c _ SELECT BasicTime.Period[from: fi2.create, to: fi1.create] FROM <0 => less, =0 => equal, >0 => greater, ENDCASE => ERROR; }; AntiAlias: PROC [ra: REF ANY] RETURNS [t: Term] = { IF ra = NIL THEN RETURN [NIL]; WITH ra SELECT FROM a: Alias => RETURN [a.val]; ENDCASE => ERROR}; Add: PROC [name: ROPE, val: Val] = { c: Cap_ NEW [CapRep _ [name, val]]; defaultTerm.caps.Insert[c]; }; Start: PROC = { termCaps _ OrderedSymbolTableRef.CreateTable[CompareTermCaps]; defaultTerm _ NEW [TermRep _ [ names: NIL, caps: OrderedSymbolTableRef.CreateTable[CompareCaps] ]]; Add["cr", NEW [StringRep _ [str: "\015"]]]; Add["ff", NEW [StringRep _ [str: "\014"]]]; Add["nl", NEW [StringRep _ [str: "\012"]]]; Add["pc", NEW [StringRep _ [str: "\000"]]]; }; Start[]; }.