DIRECTORY Ascii USING [BS, CR, FF, LF, SP, TAB], Atom USING [MakeAtom], BasicTime USING [GMT, nullGMT, Update], DefaultRemoteNames USING [Get], FS USING [ComponentPositions, Error, ExpandName, FileInfo, nullOpenFile, Open, OpenFile, OpenFileFromStream, SetByteCountAndCreatedTime, StreamFromOpenFile, StreamOpen], GPM USING [Close, Error, GetChar, GetIndex, Handle, Open], Intime USING [EventTime], IO USING [STREAM, Close, Error, GetIndex, int, Put, PutRope, SetLength], Rope USING [Cat, Concat, FromRefText, ROPE, Substr], TerminalDefs USING [BW, FL3, FL4, KeyName], TIPPrivate USING [BadTable, EqualTables, KeyOption, nrOfErrors, ReadTIPTable, Symbol, WriteTIPTable], TIPTables USING [TimeoutFlavor, TIPChoice, TIPChoiceSeries, TIPKeyState, TIPTableImplRep, TIPTerm, TIPTime], TIPUser USING [TIPScreenCoords, TIPScreenCoordsRec, TIPTable, TIPTableImplRep, TIPTableRep]; TIPTableBuilder: CEDAR MONITOR IMPORTS Atom, BasicTime, DefaultRemoteNames, FS, GPM, IO, Rope, TIPPrivate EXPORTS TIPPrivate, TIPUser = BEGIN OPEN TIPUser, TIPPrivate, TIPTables; TIPTableImplRep: PUBLIC TYPE ~ TIPTables.TIPTableImplRep; KeyName: TYPE ~ TerminalDefs.KeyName; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; LORA: TYPE = LIST OF REF ANY; errorText: PUBLIC REF ARRAY [0..nrOfErrors] OF ROPE _ NEW[ARRAY [0..nrOfErrors] OF ROPE]; keyNames: PUBLIC REF ARRAY KeyName OF ROPE _ NEW[ARRAY KeyName OF ROPE]; GData: TYPE = RECORD [ ReservedWord: REF PACKED ARRAY Symbol OF BOOL _ NIL, fh: GPM.Handle _ NIL, filename: ROPE _ NIL, errlogfh: STREAM _ NIL, fastOption: BOOL _ FALSE, fastMouseOption: BOOL _ FALSE, keyOption: KeyOption _ none, ch: CHAR _ 0C, nextch: CHAR _ 0C, havenext: BOOL _ FALSE, symbol: Symbol _ Illegal, atom: ATOM _ NIL, keyName: KeyName _ x0, symPos: INT _ 0, number: CARDINAL _ 0, ident: REF TEXT _ NIL, errcnt: CARDINAL _ 0, printKeyTable: TIPTable _ NIL, defaultKeyTable: TIPTable _ NIL ]; gData: REF GData _ NEW[GData _ [ ReservedWord: NEW[PACKED ARRAY Symbol OF BOOL], ident: NEW[TEXT[100]] ]]; DefaultTable: PROC [printKeys: BOOL] RETURNS [table: TIPTable] = TRUSTED { SELECT TRUE FROM printKeys AND gData.printKeyTable # NIL => RETURN [gData.printKeyTable]; NOT printKeys AND gData.defaultKeyTable # NIL => RETURN [gData.defaultKeyTable]; ENDCASE => { key: KeyName; enableTerm: keyEnable TIPTerm _ [keyEnable[[Ctrl, up]]]; charTerm: char TIPTerm _ [char[stdChar]]; resultTerm: result TIPTerm _ [result[LIST[charTerm.ch]]]; normalDefault: TIPChoice _ ConsTerm[charTerm, ConsTerm[resultTerm]]; ctrlUpDefault: TIPChoice _ ConsTerm[enableTerm, normalDefault]; default: TIPChoice _ IF printKeys THEN ctrlUpDefault ELSE normalDefault; impl: REF fast TIPTableImplRep _ NEW[fast TIPTableImplRep]; impl.ignore.down _ FALSE; FOR key IN KeyName DO -- includes CR and Space! SELECT key FROM A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Dash, Slash, BackSlash, Comma, Quote, RightBracket, Period, SemiColon, Return, Arrow, Space, LeftBracket, Equal, DEL, LF, BS, ESC, TAB => impl.keyDown[key] _ default; ENDCASE; ENDLOOP; IF printKeys THEN impl.keyDown[TAB] _ ctrlUpDefault; -- Also include TAB table _ NEW[TIPTableRep _ [impl: impl]]; }; IF printKeys THEN gData.printKeyTable _ table ELSE gData.defaultKeyTable _ table; RETURN[table]; }; -- DefaultTable GetChar: PROC = { GetGPMChar: PROC RETURNS [ch: CHAR] = INLINE { ch _ GPM.GetChar[gData.fh ! GPM.Error => { IF ec=EndOfStream THEN ch _ 0C; CONTINUE }]; }; IF gData.havenext THEN { gData.havenext _ FALSE; gData.ch _ gData.nextch } ELSE gData.ch _ GetGPMChar[]; IF gData.ch = '- THEN { IF (gData.nextch _ GetGPMChar[]) = '- THEN { DO SELECT GetGPMChar[] FROM Ascii.CR, 0C => EXIT; '- => SELECT GetGPMChar[] FROM '-, Ascii.CR => EXIT; ENDCASE; ENDCASE; ENDLOOP; gData.ch _ Ascii.SP } ELSE gData.havenext _ TRUE; }; }; NCONC: PROC [list1, list2: TIPChoice] RETURNS[TIPChoice] = { l: TIPChoice _ list1; IF l = NIL THEN RETURN[list2]; UNTIL l.rest = NIL DO l _ l.rest; ENDLOOP; l.rest _ list2; RETURN[list1]; }; ForceDirectory: PROC [name: ROPE, dir: ROPE] RETURNS [ROPE] = { cp: FS.ComponentPositions _ FS.ExpandName[name].cp; short: ROPE _ Rope.Substr[name, cp.base.start, cp.base.length + 1 + cp.ext.length]; RETURN [FS.ExpandName[short, dir].fullFName]; }; TrySeveralDirectories: PROC [name: ROPE] RETURNS [fullName: ROPE, date: BasicTime.GMT] = { date _ BasicTime.nullGMT; fullName _ name; [created: date, fullFName: fullName] _ FS.FileInfo[name: name, remoteCheck: FALSE ! FS.Error => CONTINUE]; IF date # BasicTime.nullGMT THEN RETURN; name _ ForceDirectory[name, "///"]; [created: date, fullFName: fullName] _ FS.FileInfo[name: name, remoteCheck: FALSE ! FS.Error => CONTINUE]; IF date # BasicTime.nullGMT THEN RETURN; name _ ForceDirectory[name, Rope.Concat[DefaultRemoteNames.Get[].current, "Tip>"]]; [created: date, fullFName: fullName] _ FS.FileInfo[name: name, remoteCheck: FALSE ! FS.Error => CONTINUE]; }; InstantiateNewTIPTable: PUBLIC PROC [file: ROPE _ NIL] RETURNS [table: TIPTable _ NIL] = { comp: ROPE; -- name of the compiled file tryC: BOOL _ TRUE; stream: STREAM; fileCD, compCD: BasicTime.GMT _ BasicTime.nullGMT; option, newKeyOption: KeyOption; newTable: TIPTable; cp: FS.ComponentPositions; file _ FS.ExpandName[file].fullFName; [cp: cp] _ FS.ExpandName[file]; comp _ Rope.Cat["///TipC/", Rope.Substr[file, cp.base.start, cp.base.length], ".tipC"]; { fullComp: ROPE _ comp; [file, fileCD] _ TrySeveralDirectories[file]; IF fileCD = BasicTime.nullGMT THEN GO TO noComp; fileCD _ BasicTime.Update[fileCD, 1]; [created: compCD, fullFName: fullComp] _ FS.FileInfo[name: comp, wantedCreatedTime: fileCD, remoteCheck: FALSE ! FS.Error => GO TO noComp]; stream _ FS.StreamOpen[fullComp, $read ! FS.Error => GO TO noComp]; [table, option] _ ReadTIPTable[stream ! BadTable => CONTINUE]; IO.Close[stream]; IF table # NIL THEN { IF option # none THEN { table.link _ DefaultTable[option=printKeys]; table.opaque _ FALSE }; RETURN }; EXITS noComp => {}}; [table, option] _ BuildNewTIPTable[file]; stream _ FS.StreamOpen[comp, $create]; FS.SetByteCountAndCreatedTime[file: FS.OpenFileFromStream[stream], created: fileCD]; WriteTIPTable[table, option, stream]; IO.Close[stream]; stream _ FS.StreamOpen[comp, $read]; [newTable, newKeyOption] _ ReadTIPTable[stream]; IO.Close[stream]; IF newKeyOption # option THEN ERROR; EqualTables[table, newTable]; }; BuildNewTIPTable: ENTRY PROC [file: ROPE] RETURNS [table: TIPTable _ NIL, option: KeyOption] = { ENABLE UNWIND => IF gData.errcnt#0 THEN { GPM.Close[gData.fh]; TruncateErrorLog[]; IO.Close[gData.errlogfh]}; GetFile: PROC [file: ROPE] RETURNS [fh: STREAM _ NIL] = CHECKED { openFile: FS.OpenFile _ FS.nullOpenFile; file _ FS.ExpandName[file].fullFName; openFile _ FS.Open[name: file, remoteCheck: FALSE]; fh _ FS.StreamFromOpenFile[openFile]; }; errMsg: ROPE; { -- fake begin to get around bug where double catch phrase fails ENABLE { GPM.Error => {errMsg _ errorMsg; GOTO MacroCleanup}; TIPError => GOTO Cleanup; }; statement: TIPChoiceSeries; gData.filename _ file; gData.fh _ GPM.Open[GetFile[file]]; gData.fh.startCall _ '[; gData.fh.endCall _ ']; gData.fh.singleQuote _ '; -- 004 octal gData.fh.startQuote _ '(; gData.fh.endQuote _ '); gData.fh.sepArg _ ',; gData.fh.numArg _ '~; gData.errcnt _ 0; gData.havenext _ FALSE; gData.fastOption _ FALSE; gData.fastMouseOption _ FALSE; gData.keyOption _ none; GetChar; GetSymbol; IF gData.symbol = OptionSym THEN Options; option _ gData.keyOption; IF gData.symbol = Select THEN { GetSymbol; IF gData.symbol = Trigger THEN { GetSymbol; statement _ TriggerStmt[]; } ELSE Error[5]; IF gData.symbol # Dot THEN Error[3]; } ELSE Error[1]; GPM.Close[gData.fh]; IF gData.errcnt=0 THEN table _ CreateTable[statement] ELSE ErrorFinish; -- finish the error log and raise signal EXITS Cleanup => { ErrorFinish }; MacroCleanup => { IF gData.errcnt=0 THEN OpenErrorLog; gData.errcnt _ gData.errcnt+1; IO.PutRope[gData.errlogfh, "Error from macro package\n\n"]; IO.PutRope[gData.errlogfh, errMsg]; ErrorFinish; }; }; -- fake block (see above) }; -- InstantiateNewTIPTable TruncateErrorLog: PROC = { ENABLE IO.Error => GOTO Exit; IO.SetLength[gData.errlogfh, IO.GetIndex[gData.errlogfh]]; EXITS Exit => {}; }; ErrorFinish: PROC = { TruncateErrorLog[]; IO.Close[gData.errlogfh]; SIGNAL InvalidTable[Rope.Concat[gData.filename," errors on TIP.ERRORS"]]; }; ConsTerm: PROC [term: TIPTerm, list: TIPChoice _ NIL] RETURNS [TIPChoice] = { RETURN [CONS[term, list]]; }; ConsAny: PROC [x: REF, list: LORA _ NIL] RETURNS [LORA] = { RETURN [CONS[x, list]]; }; CreateTable: PROC[series: TIPChoiceSeries] RETURNS[table: TIPTable] = TRUSTED { IF gData.fastOption THEN { impl: REF fast TIPTableImplRep ~ NEW[fast TIPTableImplRep]; FOR choiceSeries: TIPChoiceSeries _ series, choiceSeries.rest UNTIL choiceSeries=NIL DO choice: TIPChoice _ choiceSeries.first; WITH choice.first SELECT FROM keyTrigger => IF keyState.state = up THEN { impl.ignore.up _ FALSE; IF impl.keyUp[keyState.key] # NIL THEN DoubleDef[keyState.key]; impl.keyUp[keyState.key] _ choice.rest; } ELSE { impl.ignore.down _ FALSE; IF impl.keyDown[keyState.key] # NIL THEN DoubleDef[keyState.key]; impl.keyDown[keyState.key] _ choice.rest; }; mouseTrigger => { impl.ignore.move _ FALSE; IF impl.mouse # NIL THEN Error[25]; impl.mouse _ choice.rest }; timeTrigger => Error[]; -- to be detected earlier !!! ENDCASE; ENDLOOP; table _ NEW[TIPTableRep _ [impl: impl]]; } ELSE { impl: REF small TIPTableImplRep ~ NEW[small TIPTableImplRep]; FOR choiceSeries: TIPChoiceSeries _ series, choiceSeries.rest UNTIL choiceSeries = NIL DO choice: TIPChoice _ choiceSeries.first; WITH choice.first SELECT FROM keyTrigger => IF keyState.state = up THEN impl.ignore.up _ FALSE ELSE impl.ignore.down _ FALSE; mouseTrigger => impl.ignore.move _ FALSE; timeTrigger => Error[]; -- to be detected earlier !!! ENDCASE => ERROR; ENDLOOP; impl.all _ series; table _ NEW[TIPTableRep _ [impl: impl]]; }; IF gData.keyOption # none THEN { table.link _ DefaultTable[gData.keyOption=printKeys]; table.opaque _ FALSE; }; IF gData.fastMouseOption THEN table.mouseTicks _ 0; }; -- CreateTable Options: PROC = { GetSymbol; DO -- until see Semicolon SELECT gData.symbol FROM Fast => gData.fastOption _ TRUE; Small => gData.fastOption _ FALSE; DefaultKeys => gData.keyOption _ defaultKeys; PrintKeys => gData.keyOption _ printKeys; FastMouse => gData.fastMouseOption _ TRUE; SlowMouse => gData.fastMouseOption _ FALSE; ENDCASE => Error[18]; GetSymbol; SELECT gData.symbol FROM Semicolon => EXIT; Comma => NULL; ENDCASE => Error[19]; GetSymbol; ENDLOOP; GetSymbol; }; -- Options TriggerStmt: PROC RETURNS[choiceSeries: TIPChoiceSeries] = { usedSymbols: LIST OF ATOM _ NIL; IF gData.symbol = From THEN GetSymbol ELSE { Error[6]; }; choiceSeries _ TriggerChoiceSeries[]; }; -- TriggerStmt EnableStmt: PROC RETURNS[choiceSeries: TIPChoiceSeries] = { usedSymbols: LIST OF ATOM _ NIL; IF gData.symbol = From THEN GetSymbol ELSE { Error[20]; }; choiceSeries _ EnableChoiceSeries[]; }; -- EnableStmt TriggerChoiceSeries: PROC RETURNS [choiceSeries: TIPChoiceSeries _ NIL] = { tail: TIPChoiceSeries _ NIL; IF gData.symbol = Endcase THEN RETURN[FinalChoice[]]; DO choice: TIPChoice = TriggerChoice[]; temp: TIPChoiceSeries = LIST[choice]; IF choiceSeries = NIL THEN choiceSeries _ temp ELSE tail.rest _ temp; tail _ temp; IF gData.symbol = Semicolon THEN { GetSymbol; IF gData.symbol # Endcase THEN LOOP; }; IF gData.symbol = Endcase THEN { tail.rest _ FinalChoice[]; RETURN; }; Error[2]; ENDLOOP; }; -- TriggerChoiceSeries EnableChoiceSeries: PROC RETURNS[choiceSeries: TIPChoiceSeries _ NIL] = { tail: TIPChoiceSeries _ NIL; IF gData.symbol = Endcase THEN RETURN[FinalChoice[]]; DO choice: TIPChoice = EnableChoice[]; temp: TIPChoiceSeries _ LIST[choice]; IF choiceSeries = NIL THEN choiceSeries _ temp ELSE tail.rest _ temp; tail _ temp; IF gData.symbol = Semicolon THEN { GetSymbol; IF gData.symbol # Endcase THEN LOOP; }; IF gData.symbol = Endcase THEN { tail.rest _ FinalChoice[]; RETURN; }; Error[2]; ENDLOOP; }; -- EnableChoiceSeries TriggerChoice: PROC RETURNS[triggerChoice: TIPChoice] = { term: TIPChoice _ TriggerTerm[]; triggerChoice _ NCONC[term, Expression[]]; }; -- TriggerChoice EnableChoice: PROC RETURNS[enableChoice: TIPChoice] = { term: TIPChoice _ EnableTerm[]; enableChoice _ NCONC[term, Expression[]]; }; -- EnableChoice TriggerTerm: PROC RETURNS[triggerTerm: TIPChoice] = { SELECT gData.symbol FROM KeyIdent => { keyTerm: keyTrigger TIPTerm; keyTerm.keyState _ Key[]; triggerTerm _ LIST[keyTerm]; }; Mouse => { mouseTerm: mouseTrigger TIPTerm; triggerTerm _ LIST[mouseTerm]; GetSymbol; }; ENDCASE => { Error[8]; }; IF gData.symbol = Before OR gData.symbol = After THEN triggerTerm _ ConsTerm[TimeOut[], triggerTerm]; }; -- TriggerTerm EnableTerm: PROC RETURNS[enableTerm: TIPChoice] = { IF gData.symbol = KeyIdent THEN enableTerm _ Keys[] ELSE IF gData.symbol = Ident THEN { predTerm: predEnable TIPTerm; predTerm.predicate _ gData.atom; enableTerm _ LIST[predTerm]; GetSymbol; } ELSE { Error[21] }; }; -- EnableTerm Keys: PROC RETURNS[enableTerm: TIPChoice] = TRUSTED { first: TIPKeyState _ Key[]; SELECT gData.symbol FROM VertBar => { rest: TIPChoice; GetSymbol; IF gData.symbol # KeyIdent THEN Error[21]; rest _ Keys[]; WITH x:rest.first SELECT FROM keyEnable => { keyTerm: key2Enable TIPTerm; keyTerm.keyState1 _ first; keyTerm.keyState2 _ x.keyState; enableTerm _ LIST[keyTerm]; }; key2Enable => { keyTerm: keyEnableList TIPTerm; keyTerm.lst _ LIST[first, x.keyState1, x.keyState2]; enableTerm _ ConsTerm[keyTerm]; }; keyEnableList => { keyTerm: keyEnableList TIPTerm; keyTerm.lst _ CONS[first, x.lst]; enableTerm _ ConsTerm[keyTerm]; }; ENDCASE => ERROR; }; ENDCASE => { keyTerm: keyEnable TIPTerm; keyTerm.keyState _ first; enableTerm _ ConsTerm[keyTerm]; }; }; -- Keys Key: PROC RETURNS[keySt: TIPKeyState] = { name: KeyName _ gData.keyName; GetSymbol; IF gData.symbol = Up OR gData.symbol = Down THEN { keySt _ [key: name, state: IF gData.symbol = Up THEN up ELSE down]; GetSymbol; } ELSE Error[12]; }; -- Key TimeOut: PROC RETURNS[timeoutExpr: timeTrigger TIPTerm] = { fl: TimeoutFlavor _ IF gData.symbol = Before THEN lt ELSE gt; GetSymbol; IF gData.symbol = Number THEN { timeoutExpr.flavor _ fl; timeoutExpr.mSecs _ gData.number; GetSymbol; } ELSE { Error[10]; }; }; -- TimeOut Expression: PROC RETURNS [expression: TIPChoice] = { SELECT gData.symbol FROM And => { GetSymbol; expression _ TriggerChoice[]; }; While => { GetSymbol; expression _ EnableChoice[]; }; RightArrow => { GetSymbol; expression _ Statement[]; }; ENDCASE => Error[22]; }; -- Expression Results: PROC RETURNS [resultList: LORA, resultChoice: TIPChoice] = { resultItem: REF; resultExpr: REF TIPTerm; IF gData.symbol = LeftCurly THEN { GetSymbol; [resultList, resultChoice] _ ResultItems[]; RETURN; }; [resultItem, resultExpr] _ ResultItem[]; SELECT gData.symbol FROM Comma => { resultItemList: LORA; resultExprList: TIPChoice; GetSymbol; [resultItemList, resultExprList] _ Results[]; resultList _ ConsAny[resultItem, resultItemList]; resultChoice _ IF resultExpr = NIL THEN resultExprList ELSE ConsTerm[resultExpr^, resultExprList]; }; ENDCASE => { userResultList: result TIPTerm; resultList _ ConsAny[resultItem]; resultChoice _ ConsTerm[userResultList]; IF resultExpr # NIL THEN resultChoice _ ConsTerm[resultExpr^, resultChoice]; }; }; -- Results Store: PROC [resultList: LORA, tree: TIPChoice] = { nestedChoice: TIPChoiceSeries; FOR choice: TIPChoice _ tree, choice.rest UNTIL choice=NIL DO TRUSTED { WITH term: choice.first SELECT FROM nested => FOR nestedChoice _ term.statement, nestedChoice.rest UNTIL nestedChoice=NIL DO Store[resultList, nestedChoice.first]; ENDLOOP; result => { IF term.list = NIL THEN term.list _ resultList ELSE { Error[24]; term.list _ resultList; -- !!! }; }; ENDCASE}; ENDLOOP; }; -- Store ResultItems: PROC RETURNS[resultList: LORA, resultChoice: TIPChoice] = { resultItem: REF; resultExpr: REF TIPTerm; [resultItem, resultExpr] _ ResultItem[]; SELECT gData.symbol FROM RightCurly => { userResultList: result TIPTerm; GetSymbol; resultList _ LIST[resultItem]; resultChoice _ IF resultExpr = NIL THEN ConsTerm[userResultList] ELSE ConsTerm[resultExpr^, ConsTerm[userResultList]]; }; ENDCASE => { resultItemList: LORA; resultExprList: TIPChoice; [resultItemList, resultExprList] _ ResultItems[]; resultList _ ConsAny[resultItem, resultItemList]; resultChoice _ IF resultExpr = NIL THEN resultExprList ELSE ConsTerm[resultExpr^, resultExprList]; }; }; ResultItem: PROC RETURNS[resultItem: REF, resultExpr: REF TIPTerm _ NIL] = { SELECT gData.symbol FROM Char => { resultExpr _ NEW[char TIPTerm _ [char[resultItem _ stdChar]]]; GetSymbol; }; Coords => { resultExpr _ NEW[coords TIPTerm _ [coords[resultItem _ stdCoords]]]; GetSymbol; }; Time => { resultExpr _ NEW[time TIPTerm _ [time[resultItem _ stdTime]]]; GetSymbol; }; KeyIdent, -- result names might be key names Ident => { resultItem _ gData.atom; GetSymbol; }; Number => { resultItem _ NEW[INT]; WITH resultItem SELECT FROM z: REF INT => z^ _ gData.number; ENDCASE; GetSymbol; }; String => { resultItem _ NEW[TEXT[gData.ident.length]]; WITH resultItem SELECT FROM z: REF TEXT => { FOR i: CARDINAL IN [0..gData.ident.length) DO z[i] _ gData.ident[i]; ENDLOOP; z.length _ gData.ident.length; }; ENDCASE; GetSymbol; }; ENDCASE => IF gData.ReservedWord[gData.symbol] THEN { resultItem _ gData.atom; GetSymbol; } ELSE Error[9]; }; -- ResultItem FinalChoice: PROC RETURNS [finalChoice: TIPChoiceSeries] = { GetSymbol; -- we always get here with a pending ENDCASE IF gData.symbol = RightArrow THEN { GetSymbol; finalChoice _ LIST[Statement[]]; }; }; -- FinalChoice Statement: PROC RETURNS[stmt: TIPChoice] = { IF gData.symbol = Select THEN { term: nested TIPTerm; GetSymbol; IF gData.symbol = Trigger OR gData.symbol = Enable THEN { sy: Symbol _ gData.symbol; GetSymbol; term.statement _ IF sy = Trigger THEN TriggerStmt[] ELSE EnableStmt[]; stmt _ LIST[term]; } ELSE { Error[13]; }; } ELSE { userResults: LORA; [userResults, stmt] _ Results[]; Store[userResults, stmt]; }; }; -- Statement stdChar: PUBLIC REF CHAR _ NEW[CHAR]; stdCoords: PUBLIC TIPScreenCoords _ NEW[TIPScreenCoordsRec]; stdTime: PUBLIC TIPTables.TIPTime _ NEW[Intime.EventTime]; InvalidTable: PUBLIC SIGNAL [errorMsg: ROPE] = CODE; OpenErrorLog: PROC = { gData.errlogfh _ FS.StreamOpen["tip.errors", $create]; IO.PutRope[gData.errlogfh, gData.filename]; IO.PutRope[gData.errlogfh, " TIP TABLE error log.\n\n"]; }; DoubleDef: PROC[key: KeyName] = { IF gData.errcnt=0 THEN OpenErrorLog[]; gData.errcnt _ gData.errcnt+1; IO.PutRope[gData.errlogfh, keyNames[key]]; IO.PutRope[gData.errlogfh, " entry must not occur more than once in table.\n\n"]; }; TIPError: ERROR = CODE; Error: PROC[nr: CARDINAL _ 0] = { OPEN IO; IF gData.errcnt=0 THEN OpenErrorLog; gData.errcnt _ gData.errcnt+1; PutRope[gData.errlogfh, errorText[nr]]; PutRope[gData.errlogfh, " at "]; Put[gData.errlogfh, int[gData.symPos]]; PutRope[gData.errlogfh, "\n\n"]; ERROR TIPError; }; GetSymbol: PROC = { GetNumber: PROC = { gData.symbol _ Number; gData.number _ 0; WHILE gData.ch IN ['0..'9] DO gData.number _ 10*gData.number + gData.ch-'0; GetChar[]; ENDLOOP; }; GetWord: PROC = { dummy: KeyName = KeyName.LAST; i: CARDINAL _ 0; WHILE gData.ch IN ['0..'9] OR gData.ch IN ['a..'z] OR gData.ch IN ['A..'Z] DO gData.ident[i] _ gData.ch; i _ i + 1; GetChar[]; ENDLOOP; gData.ident.length _ i; gData.atom _ Atom.MakeAtom[Rope.FromRefText[gData.ident]]; gData.symbol _ KeyIdent; gData.keyName _ dummy; SELECT gData.atom FROM $OPTIONS => gData.symbol _ OptionSym; $SELECT => gData.symbol _ Select; $TRIGGER => gData.symbol _ Trigger; $ENABLE => gData.symbol _ Enable; $FROM => gData.symbol _ From; $ENDCASE => gData.symbol _ Endcase; $END => gData.symbol _ End; $AND => gData.symbol _ And; $WHILE => gData.symbol _ While; $AFTER => gData.symbol _ After; $BEFORE => gData.symbol _ Before; $Up => gData.symbol _ Up; $Down => gData.symbol _ Down; $Mouse => gData.symbol _ Mouse; $Char => gData.symbol _ Char; $Coords => gData.symbol _ Coords; $TIME => gData.symbol _ Time; $Small => gData.symbol _ Small; $Fast => gData.symbol _ Fast; $FastMouse => gData.symbol _ FastMouse; $SlowMouse => gData.symbol _ SlowMouse; $PrintKeys => gData.symbol _ PrintKeys; $DefaultKeys => gData.symbol _ DefaultKeys; $x0 => gData.keyName _ x0; $x1 => gData.keyName _ x1; $x2 => gData.keyName _ x2; $x3 => gData.keyName _ x3; $x4 => gData.keyName _ x4; $x5 => gData.keyName _ x5; $x6 => gData.keyName _ x6; $Pen => gData.keyName _ Pen; $Keyset1 => gData.keyName _ Keyset1; $Keyset2 => gData.keyName _ Keyset2; $Keyset3 => gData.keyName _ Keyset3; $Keyset4 => gData.keyName _ Keyset4; $Keyset5 => gData.keyName _ Keyset5; $Red => gData.keyName _ Red; $Blue => gData.keyName _ Blue; $Yellow => gData.keyName _ Yellow; $Five => gData.keyName _ Five; $Four => gData.keyName _ Four; $Six => gData.keyName _ Six; $E => gData.keyName _ E; $Seven => gData.keyName _ Seven; $D => gData.keyName _ D; $U => gData.keyName _ U; $V => gData.keyName _ V; $Zero => gData.keyName _ Zero; $K => gData.keyName _ K; $Dash => gData.keyName _ Dash; $P => gData.keyName _ P; $Slash => gData.keyName _ Slash; $BackSlash => gData.keyName _ BackSlash; $Defaults => gData.keyName _ BackSlash; -- synonym $LF => gData.keyName _ LF; $Copy => gData.keyName _ LF; -- synonym $BS => gData.keyName _ BS; $Three => gData.keyName _ Three; $Two => gData.keyName _ Two; $W => gData.keyName _ W; $Q => gData.keyName _ Q; $S => gData.keyName _ S; $A => gData.keyName _ A; $Nine => gData.keyName _ Nine; $I => gData.keyName _ I; $X => gData.keyName _ X; $O => gData.keyName _ O; $L => gData.keyName _ L; $Comma => gData.keyName _ Comma; $Quote => gData.keyName _ Quote; $RightBracket => gData.keyName _ RightBracket; $Spare2 => gData.keyName _ Spare2; $Keyboard => gData.keyName _ Spare2; -- synonym (DLion) $Next => gData.keyName _ Spare2; -- synonym $Spare1 => gData.keyName _ Spare1; $BW => gData.keyName _ Spare1; -- synonym $Undo => gData.keyName _ Spare1; -- synonym (DLion) $Look => gData.keyName _ Spare1; -- synonym $One => gData.keyName _ One; $ESC => gData.keyName _ ESC; $Center => gData.keyName _ ESC; -- synonym (DLion) $TAB => gData.keyName _ TAB; $F => gData.keyName _ F; $Ctrl => gData.keyName _ Ctrl; $Open => gData.keyName _ Ctrl; -- synonym (DLion) $C => gData.keyName _ C; $J => gData.keyName _ J; $B => gData.keyName _ B; $Z => gData.keyName _ Z; $LeftShift => gData.keyName _ LeftShift; $Period => gData.keyName _ Period; $SemiColon => gData.keyName _ SemiColon; $Return => gData.keyName _ Return; $Arrow => gData.keyName _ Arrow; $DEL => gData.keyName _ DEL; $Move => gData.keyName _ Move; $FL3 => gData.keyName _ Move; -- synonym $R => gData.keyName _ R; $T => gData.keyName _ T; $G => gData.keyName _ G; $Y => gData.keyName _ Y; $H => gData.keyName _ H; $Eight => gData.keyName _ Eight; $N => gData.keyName _ N; $M => gData.keyName _ M; $Lock => gData.keyName _ Lock; $Space => gData.keyName _ Space; $LeftBracket => gData.keyName _ LeftBracket; $Equal => gData.keyName _ Equal; $RightShift => gData.keyName _ RightShift; $Spare3 => gData.keyName _ Spare3; $Swat => gData.keyName _ Spare3; -- synonym $Stop => gData.keyName _ Spare3; -- synonym (DLion) $Props => gData.keyName _ Props; $FL4 => gData.keyName _ Props; -- synonym $SkipNext => gData.keyName _ SkipNext; -- DLion only $Margins => gData.keyName _ Margins; -- DLion only $Same => gData.keyName _ Same; -- DLion only $Find => gData.keyName _ Find; -- DLion only $Again => gData.keyName _ Again; -- DLion only $Help => gData.keyName _ Help; -- DLion only $DefnExpand => gData.keyName _ DefnExpand; -- DLion only $RightArrow => gData.keyName _ RightArrow; -- DLion only $Bold => gData.keyName _ Bold; -- DLion only $Italics => gData.keyName _ Italics; -- DLion only $Underline => gData.keyName _ Underline; -- DLion only $Superscript => gData.keyName _ Superscript; -- DLion only $Subscript => gData.keyName _ Subscript; -- DLion only $LargerSmaller => gData.keyName _ LargerSmaller; -- DLion only $Font => gData.keyName _ Font; -- DLion only ENDCASE => gData.symbol _ Ident; }; GetString: PROC = { i: CARDINAL _ 0; DO -- process the characters of the string SELECT gData.ch _ GPM.GetChar[gData.fh] FROM '" => EXIT; '\\ => SELECT gData.ch _ GPM.GetChar[gData.fh] FROM 'n, 'N, 'r, 'R => gData.ch _ Ascii.CR; 't, 'T => gData.ch _ Ascii.TAB; 'b, 'B => gData.ch _ Ascii.BS; 'f, 'F => gData.ch _ Ascii.FF; 'l, 'L => gData.ch _ Ascii.LF; '\\, '', '" => NULL; IN ['0..'3] => { d: CARDINAL _ gData.ch-'0; IF (gData.ch _ GPM.GetChar[gData.fh]) NOT IN ['0..'7] THEN Error[26]; d _ d*8 + gData.ch-'0; IF (gData.ch _ GPM.GetChar[gData.fh]) NOT IN ['0..'7] THEN Error[26]; d _ d*8 + gData.ch-'0; gData.ch _ LOOPHOLE[d] }; ENDCASE => Error[26]; ENDCASE; gData.ident[i] _ gData.ch; i _ i + 1; ENDLOOP; gData.ident.length _ i; GetChar; gData.symbol _ String; }; GetPunctuation: PROC = { SELECT gData.ch FROM '; => gData.symbol _ Semicolon; ', => gData.symbol _ Comma; '> => gData.symbol _ Greater; '. => gData.symbol _ Dot; '| => gData.symbol _ VertBar; '= => { GetChar; IF gData.ch = '> THEN gData.symbol _ RightArrow ELSE gData.symbol _ Illegal; }; '{ => gData.symbol _ LeftCurly; '} => gData.symbol _ RightCurly; ENDCASE => gData.symbol _ Illegal; GetChar[]; }; WHILE gData.ch = Ascii.SP OR gData.ch = Ascii.TAB OR gData.ch = Ascii.CR DO GetChar[]; ENDLOOP; gData.symPos _ GPM.GetIndex[gData.fh]-1; SELECT gData.ch FROM IN ['0..'9] => GetNumber[]; IN ['a..'z], IN ['A..'Z] => GetWord[]; = '" => GetString[]; ENDCASE => GetPunctuation[]; }; InitBuilder: PUBLIC PROC = { errorText[0] _ "Error"; errorText[1] _ "OPTIONS or CASE expected"; errorText[2] _ "ENDCASE or; expected"; errorText[3] _ "'.' expected"; errorText[4] _ "Ident expected"; errorText[5] _ "TRIGGER expected"; errorText[6] _ "OF expected"; errorText[7] _ "': expected"; errorText[8] _ "KeyIdent or Mouse expected"; errorText[9] _ "error in Action"; errorText[10] _ "Number expected"; errorText[11] _ "'> expected"; errorText[12] _ "Up or Down expected"; errorText[13] _ "TRIGGER or ENABLED expected"; errorText[14] _ "illegal option"; errorText[15] _ "enable procedures as CONDITION only"; errorText[16] _ "mouse motion as TRIGGER only"; errorText[17] _ "label used twice"; errorText[18] _ "; expected"; errorText[19] _ ", expected"; errorText[20] _ "BY expected"; errorText[21] _ "Key or Ident expected"; errorText[22] _ "AND, WHILE, =>, or : expected"; errorText[23] _ "CASE expected"; errorText[24] _ "sorry, results only at leaves implemented"; errorText[25] _ "Mouse movement entry must not occur more than once in table."; errorText[26] _ "Illegal character following \\ in string"; errorText[27] _ ""; errorText[28] _ ""; errorText[29] _ ""; errorText[30] _ ""; keyNames[x0] _ "x0"; keyNames[x1] _ "x1"; keyNames[x2] _ "x2"; keyNames[x3] _ "x3"; keyNames[x4] _ "x4"; keyNames[x5] _ "x5"; keyNames[x6] _ "x6"; keyNames[Pen] _ "Pen"; keyNames[Keyset1] _ "Keyset1"; keyNames[Keyset2] _ "Keyset2"; keyNames[Keyset3] _ "Keyset3"; keyNames[Keyset4] _ "Keyset4"; keyNames[Keyset5] _ "Keyset5"; keyNames[Red] _ "Red"; keyNames[Blue] _ "Blue"; keyNames[Yellow] _ "Yellow"; keyNames[Five] _ "Five"; keyNames[Four] _ "Four"; keyNames[Six] _ "Six"; keyNames[E] _ "E"; keyNames[Seven] _ "Seven"; keyNames[D] _ "D"; keyNames[U] _ "U"; keyNames[V] _ "V"; keyNames[Zero] _ "Zero"; keyNames[K] _ "K"; keyNames[Dash] _ "Dash"; keyNames[P] _ "P"; keyNames[Slash] _ "Slash"; keyNames[BackSlash] _ "BackSlash"; keyNames[LF] _ "LF"; keyNames[BS] _ "BS"; keyNames[Three] _ "Three"; keyNames[Two] _ "Two"; keyNames[W] _ "W"; keyNames[Q] _ "Q"; keyNames[S] _ "S"; keyNames[A] _ "A"; keyNames[Nine] _ "Nine"; keyNames[I] _ "I"; keyNames[X] _ "X"; keyNames[O] _ "O"; keyNames[L] _ "L"; keyNames[Comma] _ "Comma"; keyNames[Quote] _ "Quote"; keyNames[RightBracket] _ "RightBracket"; keyNames[Spare2] _ "Spare2"; keyNames[TerminalDefs.BW] _ "BW"; keyNames[One] _ "One"; keyNames[ESC] _ "ESC"; keyNames[TAB] _ "TAB"; keyNames[F] _ "F"; keyNames[Ctrl] _ "Ctrl"; keyNames[C] _ "C"; keyNames[J] _ "J"; keyNames[B] _ "B"; keyNames[Z] _ "Z"; keyNames[LeftShift] _ "LeftShift"; keyNames[Period] _ "Period"; keyNames[SemiColon] _ "SemiColon"; keyNames[Return] _ "Return"; keyNames[Arrow] _ "Arrow"; keyNames[DEL] _ "DEL"; keyNames[TerminalDefs.FL3] _ "FL3"; keyNames[R] _ "R"; keyNames[T] _ "T"; keyNames[G] _ "G"; keyNames[Y] _ "Y"; keyNames[H] _ "H"; keyNames[Eight] _ "Eight"; keyNames[N] _ "N"; keyNames[M] _ "M"; keyNames[Lock] _ "Lock"; keyNames[Space] _ "Space"; keyNames[LeftBracket] _ "LeftBracket"; keyNames[Equal] _ "Equal"; keyNames[RightShift] _ "RightShift"; keyNames[Spare3] _ "Spare3"; keyNames[TerminalDefs.FL4] _ "FL4"; }; InitBuilder[]; { FOR s: Symbol IN Symbol DO gData.ReservedWord[s] _ TRUE; ENDLOOP; gData.ReservedWord[String] _ FALSE; gData.ReservedWord[Semicolon] _ FALSE; gData.ReservedWord[Comma] _ FALSE; gData.ReservedWord[Greater] _ FALSE; gData.ReservedWord[Dot] _ FALSE; gData.ReservedWord[RightArrow] _ FALSE; gData.ReservedWord[Illegal] _ FALSE; gData.ReservedWord[LeftCurly] _ FALSE; gData.ReservedWord[RightCurly] _ FALSE; gData.ReservedWord[VertBar] _ FALSE; gData.ReservedWord[Number] _ FALSE; gData.ReservedWord[KeyIdent] _ FALSE; }; END. ¬TIPTableBuilder.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Doug Wyatt, April 14, 1985 10:06:17 pm PST Russ Atkinson (RRA) October 21, 1985 9:44:16 pm PDT global scanner variables: *** the scanner: *** scan over comment go to CR or double dash construct the tipC name from the tip name Try to open and read the compiled (tipC) version of the tip file. The tipC file is always stored on the ///TipC/ directory to avoid multiple copies. The create date of the tipC file is one second later than the create date of the tip file so we can determine correspondence between the two. At this point there is no tipC file, so we will have to make one. We start from the tip file, and any FS.Error will propagate from here. FS.Error will percolate up TriggerStmt ::= SELECT TRIGGER FROM TriggerChoiceSeries skip until choice-begin EnableStmt ::= SELECT ENABLE FROM EnableChoiceSeries skip until (enable)choice-begin TriggerChoiceSeries ::= TriggerChoice; TriggerChoiceSeries | TriggerChoice ENDCASE FinalChoice | ENDCASE FinalChoice skip until choice-begin or else EnableChoiceSeries ::= EnableChoice; EnableChoiceSeries | EnableChoice ENDCASE FinalChoice | ENDCASE FinalChoice skip until choice-begin or else TriggerChoice ::= TriggerTerm Expression EnableChoice ::= EnableTerm Expression TriggerTerm ::= Key TimeOut | MOUSE TimeOut skip EnableTerm ::= Keys | PredicateIdent Keys ::= Key | Key "|" Keys KeyIdent UP | KeyIdent DOWN TimeOut ::= empty | BEFORE Number | AFTER Number skip Expression ::= AND TriggerChoice | WHILE EnableChoice | => Statement Results ::= ResultItem | ResultItem , Results | { ResultItem* } find all leaves l:[result TIPTermRec] of the tree, append the list found there to a copy of resultList, and store the resulting list as l.list ResultItems ::= ResultItem } | ResultItem ResultItems ResultItem ::= COORDS | CHAR | TIME | String | Number | ResultIdent FinalChoice ::= empty | => Statement Statement ::= TriggerStmt | EnableStmt | Results note that all parameters share the same variable for notification users must copy parameter if they want to save value after returning from notify find next symbol classify symbol main code: Reserved word initialization Κ0ϊ– "Mesa" style˜codešœ™Kšœ Οmœ7™BK™*K™3—K˜šΟk ˜ Kšœžœžœžœžœžœžœžœ˜&Kšœžœ ˜Kšœ žœžœ˜'Kšœžœ˜Kšžœžœ‘˜©Kšžœžœ1˜:Kšœžœ ˜Kšžœžœžœ8˜HKšœžœžœ ˜4Kšœ žœžœ˜+Kšœ žœU˜eKšœ žœ]˜lKšœžœO˜\—K˜šΠblœžœž˜Kšžœ&žœžœžœ˜JKšžœ˜Kšœžœžœ ˜,K˜Kšœžœžœ˜9K˜Kšœ žœ˜%K˜Kšžœžœžœ˜Kšžœžœžœžœ˜K˜Kš žœžœžœžœžœžœ˜K˜š œ žœžœžœžœžœ˜5Kšžœžœžœžœ˜#K˜—š œ žœžœžœ žœžœ˜,Kšžœžœ žœžœ˜K˜—šœžœžœ˜Kš œžœžœžœžœžœžœ˜4Kšœžœ žœ˜Kšœ žœžœ˜Kšœ žœžœ˜K˜Kšœ žœžœ˜Kšœžœžœ˜K˜K˜šœ™Kšœžœ˜Kšœžœ˜Kšœ žœžœ˜Kšœ˜Kšœžœžœ˜Kšœ˜Kšœžœ˜Kšœžœ˜Kšœžœžœžœ˜Kšœžœ˜Kšœžœ˜Kšœž˜—K˜—K˜šœžœ žœ ˜ Kš œžœžœžœžœžœ˜/Kšœžœžœ˜K˜—K˜—š Οn œžœ žœžœžœ˜Jšžœžœž˜Kšœ žœžœžœ˜HKšžœ žœžœžœ˜Pšžœ˜ K˜ K˜8K˜)Kšœ%žœ˜9KšœD˜DKšœ?˜?Kšœžœ žœžœ˜HKšœžœžœ˜;Kšœžœ˜K˜šžœžœ žœΟc˜/šžœž˜š>žœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœžœ―žœžœžœžœžœ˜“Kšœ˜—Kšžœ˜—Kšžœ˜—K˜Kšžœ žœžœ‘˜HKšœžœ˜(K˜——Kšžœ žœžœ˜QKšžœ˜Kšœ‘˜K˜K˜—Kšœ™K˜š œžœ˜š   œžœžœžœžœ˜.šœžœ˜Kš œžœ žœžœ žœ˜=—Kšœ˜—Kšžœžœžœžœ˜hšžœžœ˜šžœ#˜%šžœ˜Kšœ™šž˜Kšœ™šžœž˜Kšœžœžœ˜šœžœž˜Kšœ žœžœ˜Kšžœ˜—Kšžœ˜—Kšžœ˜ —Kšœžœ˜—Kšžœžœ˜—Kšœ˜—Kšœ˜K˜—šžœžœžœ˜ž˜GKšœžœ žœžœ ˜——Kš œ žœžœ žœžœ ˜CKšœ4žœ˜>Kšžœ˜šžœ žœžœ˜šžœžœ˜K˜,Kšœžœ˜—Kšžœ˜ —Kšžœ˜—Kšœ‰™‰K˜)Kšœ žœ˜&Kšžœ"žœ.˜TK˜%Kšžœ˜Kšœ žœ˜$K˜0Kšžœ˜Kšžœžœžœ˜$K˜K˜K˜—š  œžœžœžœžœžœ˜`K˜šžœžœžœžœ˜)Kšžœ&žœ˜CK˜—š œžœžœžœžœžœžœ˜AKšœ™Kšœ žœ žœ˜(Kšœžœ˜%Kšœ žœžœ˜3Kšœžœ˜%Kšœ˜K˜—šœžœ˜ K˜—šœ‘?˜AK˜šžœ˜Kšžœžœ˜4Kšœ žœ ˜Kšœ˜K˜—K˜K˜Kšœ˜Kšœ žœ˜#Kšœ˜Kšœ˜Kšœ‘ ˜'Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ˜K˜K˜K˜Kšžœžœ ˜)Kšœ˜K˜šžœžœ˜K˜ šžœžœ˜ K˜ K˜Kšœ˜—Kšžœ ˜Kšžœžœ ˜$Kšœ˜—Kšžœ ˜K˜Kšžœ˜šžœ˜Kšžœ˜#Kšžœ‘(˜:—K˜šž˜K˜˜Kšžœžœ˜$Kšœ˜Kšžœ9˜;Kšžœ!˜#K˜ ˜K˜———Kšœ‘˜—K˜Kšœ‘˜K˜—š œžœ˜Kšžœžœ žœ˜Kšžœžœ˜:Kšžœ ˜Kšœ˜K˜—š  œžœ˜K˜Kšžœ˜KšžœC˜IKšœ˜K˜—š œžœ#žœžœ˜MKšžœžœ˜K˜K˜—š œžœžœžœžœžœžœ˜;Kšžœžœ ˜K˜K˜—š  œžœžœžœ˜Ošžœžœ˜Kšœžœžœ˜;šžœ;žœžœž˜WK˜'šžœžœž˜˜ šžœžœ˜Kšœžœ˜Kšžœžœžœ˜?K˜'Kšœ˜—šžœ˜Kšœžœ˜Kšžœžœžœ˜AK˜)Kšœ˜——šœ˜Kšœžœ˜Kšžœžœžœ ˜#K˜Kšœ˜—Kšœ‘˜5Kšžœ˜—Kšžœ˜—Kšœžœ˜(Kšœ˜—šžœ˜Kšœžœžœ˜=šžœ:˜=šžœžœž˜K˜'šžœžœž˜šœžœžœž˜@Kšžœžœ˜—Kšœ#žœ˜)Kšœ‘˜5Kšžœžœ˜—Kšžœ˜——K˜Kšœžœ˜(Kšœ˜—šžœžœ˜ Kšœ5˜5Kšœžœ˜Kšœ˜—Kšžœžœ˜3Kšœ‘˜K˜—š œžœ˜K˜ šžœ‘˜šžœž˜Kšœžœ˜ Kšœžœ˜"Kšœ-˜-Kšœ)˜)Kšœ%žœ˜*Kšœ%žœ˜+Kšžœ˜—K˜ šžœž˜Kšœ žœ˜Kšœ žœ˜Kšžœ˜—K˜ Kšžœ˜—K˜ Kšœ‘ ˜ K˜—š  œžœžœ#˜K˜ —Kšœ˜—šœ ˜ šœ žœ4˜DK˜ —Kšœ˜—šœ ˜ šœ žœ.˜>K˜ —Kšœ˜—Kšœ ‘"˜,šœ ˜ Kšœ˜K˜ Kšœ˜—šœ ˜ šœ žœžœ˜šžœ žœž˜Kšœžœžœ˜ Kšžœ˜——K˜ Kšœ˜—šœ ˜ šœ žœžœ˜+šžœ žœž˜šœžœžœ˜šžœžœžœž˜-Kšœ˜Kšžœ˜—Kšœ˜——šœ˜Kšžœ˜——˜ Kšœ˜——šžœ˜ šžœ"žœ˜*Kšœ˜K˜ šœ˜Kšžœ ˜————Kšœ‘ ˜K˜K˜—š  œžœžœ#˜K–1.4 in tabStopsšœ‘ ˜,K–1.4 in tabStopsšžœ˜ —Kšœ˜—K˜š  œžœ˜Kšœžœ˜šžœ‘'˜+šžœ žœž˜,Kšœžœ˜ šœžœ žœž˜3Kšœ#žœ˜&Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜šžœ˜Kšœžœ˜Kš žœ žœžœžœ žœ ˜EKšœ˜Kš žœ žœžœžœ žœ ˜EKšœ˜Kšœ žœ˜—Kšžœ˜—Kšžœ˜—Kšœ˜K˜ Kšžœ˜—Kšœ˜K˜Kšœ˜Kšœ˜K˜—š œžœ˜šžœ ž˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜šœ˜K˜ Kšžœžœ˜0Kšžœ˜Kšœ˜—Kšœ˜Kšœ ˜ Kšžœ˜"—K˜ Kšœ˜K˜—Kšœ™š žœžœžœžœžœžœž˜KK˜ Kšžœ˜—Kšœžœ˜(K˜Kšœ™šžœ ž˜Kšžœ˜Kšžœ žœ˜&K˜Kšžœ˜—Kšœ˜K˜—šœ ™ š  œžœžœ˜K˜K˜*K˜&K˜K˜ K˜"K˜K˜K˜,K˜!K˜"K˜K˜&K˜.K˜!K˜6K˜/K˜#K˜K˜K˜K˜(K˜0K˜ K˜