<> <> <> <> DIRECTORY Ascii USING [CR, FF, LF, NUL, SP, TAB], Atom USING [GetPropFromList], Convert USING [Error, RealFromRope], Graph USING [CaretIndex, CaretSpec, ColorIndex, Entity, FontIndex, GraphHandle, NtNan, ROPE, TargetSpec, ValueList, XY], GraphOps USING [AddCurve, NewGraph, SetXValues], GraphPrivate USING [DataError, RopeList, SyntaxError], GraphUtil USING [BlinkMsg, LengthOfVL], Imager USING [Box], IO USING [Backup, EndOfStream, Error, GetCedarToken, GetChar, GetID, int, PeekChar, PutFR, SetIndex, STREAM, TokenError, TokenKind], List USING [Kill], Real USING [Fix, LargestNumber], RefText USING [Equal, ObtainScratch, ReleaseScratch, TrustTextAsRope], Rope USING [Equal, FromRefText, Substr]; GraphReadAscii: CEDAR PROGRAM IMPORTS Atom, Convert, GraphOps, GraphPrivate, IO, GraphUtil, List, Real, RefText, Rope EXPORTS GraphPrivate = { OPEN Graph, GraphOps, GraphPrivate, GraphUtil; TextDataKeys: TYPE = {varsKey, nVarsKey, valuesKey, noneKey}; LVL: TYPE = LIST OF ValueList; ReadAsciiFile: PUBLIC PROC [handle: GraphHandle, s: IO.STREAM] RETURNS [msg: ROPE _ NIL] = { ok: BOOL _ TRUE; key: ROPE _ s.GetID[ ! IO.EndOfStream, IO.Error => ok _ FALSE]; IF ok THEN ok _ key.Equal["graph", FALSE]; IF ok THEN msg _ ReadAsciiComplex[handle, s] ELSE { s.SetIndex[0]; msg _ ReadAsciiSimple[handle, s]; }; }; -- ReadAsciiFile ReadAsciiComplex: PROC [handle: GraphHandle, s: IO.STREAM] RETURNS [msg: ROPE _ NIL] = { <> msg _ "Not implemented yet." }; -- ReadAsciiComplex <> ReadAsciiSimple: PROC [handle: GraphHandle, s: IO.STREAM] RETURNS [msg: ROPE _ NIL] = { buffer: REF TEXT _ RefText.ObtainScratch[512]; tokenKind: IO.TokenKind _ tokenCOMMENT; token: REF TEXT; charsSkipped: INT; error: IO.TokenError; get: BOOL _ TRUE; endOfFile: BOOL _ FALSE; key: TextDataKeys _ noneKey; nVars, specifiedN, guessedN, finalN, iRow, iCol: INT; -- iRow, iCol: last row/col processed so far. varsSpecified, nVarsSpecified, valuesSpecified, nVarsGuessed, nToBeGuessed, endOfGroup: BOOL; vars, lastVar: RopeList; xvalues: ValueList; yvalues, tlvl: LVL; status: ROPE; AppendIDtoNames: PROC [] = { nl: RopeList _ CONS[Rope.FromRefText[token], NIL]; IF lastVar = NIL THEN {vars _ lastVar _ nl} ELSE {lastVar.rest _ nl; lastVar _ nl}; nVars _ nVars + 1; varsSpecified _ TRUE; }; -- AppendIDtoNames AppendROPEtoNames: PROC [] = { -- othere input/output: vars and lastVar name: ROPE _ IF token.length <= 2 THEN "" ELSE Rope.FromRefText[token].Substr[1, token.length - 2]; nl: RopeList _ CONS[name, NIL]; IF lastVar = NIL THEN {vars _ lastVar _ nl} ELSE {lastVar.rest _ nl; lastVar _ nl}; nVars _ nVars + 1; varsSpecified _ TRUE; }; -- AppendROPEtoNames <<>> ProcessTokenID: PROC [] = { <> <> currKey: TextDataKeys _ key; nextChar: CHAR; [nextChar, endOfFile] _ PeekNextNonBlankChar[s]; IF endOfFile THEN SELECT currKey FROM varsKey, nVarsKey, noneKey => SIGNAL DataError["can't find values"]; valuesKey => endOfGroup _ TRUE; ENDCASE ELSE IF nextChar = ': THEN { [] _ s.GetChar[]; key _ GetKeyWord[token, s]; SELECT key FROM varsKey => { IF currKey = varsKey THEN SIGNAL SyntaxError["duplicate vars key"]; IF valuesSpecified THEN endOfGroup _ TRUE ELSE IF varsSpecified THEN SIGNAL SyntaxError["duplicate vars key"]; <> }; nVarsKey => { IF currKey = nVarsKey THEN SIGNAL SyntaxError["duplicate nVars key"]; IF valuesSpecified THEN endOfGroup _ TRUE ELSE IF nVarsSpecified THEN SIGNAL SyntaxError["duplicate nVars key"]; <> }; valuesKey => { IF currKey = valuesKey THEN endOfGroup _ TRUE; -- values must have been specified. [finalN, vars, yvalues] _ InitData[nVarsSpecified, specifiedN, nVars, vars]; }; ENDCASE; -- nothing else after GetKeyWord. } ELSE SELECT key FROM varsKey => AppendIDtoNames[]; valuesKey => { -- values must have been specified. endOfGroup _ TRUE; key _ varsKey; get _ FALSE; }; nVarsKey => { -- nVarsSpecified must have been specified. IF varsSpecified THEN { IF valuesSpecified THEN { endOfGroup _ TRUE; key _ varsKey; get _ FALSE; } ELSE SIGNAL SyntaxError["possibly missing `:' after keyword"]; } ELSE { -- vars not specified. key _ varsKey; get _ FALSE; }; }; ENDCASE => { -- noneKey, so values, vars, and nName are not specified. key _ varsKey; get _ FALSE; }; }; -- ProcessTokenID UNTIL endOfFile DO xmin, ymin: REAL _ Real.LargestNumber; xmax, ymax: REAL _ -xmin; nVars _ specifiedN _ guessedN _ iRow _ iCol _ 0; finalN _ -1; varsSpecified _ nVarsSpecified _ nVarsGuessed _ nToBeGuessed _ valuesSpecified _ endOfGroup _ FALSE; vars _ lastVar _ NIL; xvalues _ NIL; yvalues _ tlvl _ NIL; status _ NIL; UNTIL endOfGroup OR endOfFile DO IF get THEN [tokenKind, token, charsSkipped, error] _ s.GetCedarToken[buffer] ELSE get _ TRUE; SELECT tokenKind FROM tokenCOMMENT => LOOP; tokenERROR => SIGNAL SyntaxError[]; tokenEOF => { IF NOT valuesSpecified THEN SIGNAL DataError["can't find values"]; endOfFile _ TRUE; EXIT; }; ENDCASE => SELECT key FROM noneKey => { -- can happen only at first time in the loop. errorMsg: ROPE _ "key word, rope, or values expected"; SELECT tokenKind FROM tokenROPE => {key _ varsKey; get _ FALSE}; tokenID => ProcessTokenID[]; tokenREAL, tokenDECIMAL => { key _ valuesKey; get _ FALSE; [finalN, vars, yvalues] _ InitData[nVarsSpecified, specifiedN, nVars, vars]; }; tokenSINGLE => SELECT token[0] FROM '-, '+, '* => { IO.Backup[s, token[0]]; key _ valuesKey; [finalN, vars, yvalues] _ InitData[nVarsSpecified, specifiedN, nVars, vars]; }; ENDCASE => SIGNAL SyntaxError[errorMsg]; ENDCASE => SIGNAL SyntaxError[errorMsg]; }; nVarsKey => { status _ "after nVars key"; SELECT tokenKind FROM tokenDECIMAL, tokenREAL => { IF nVarsSpecified THEN { key _ valuesKey; get _ FALSE; [finalN, vars, yvalues] _ InitData[nVarsSpecified, specifiedN, nVars, vars]; } ELSE { ok: BOOL _ TRUE; r: REAL; IF tokenKind = tokenREAL THEN SIGNAL DataError["nVars should be an integer"]; r _ Convert.RealFromRope[RefText.TrustTextAsRope[token] ! Convert.Error => {ok _ FALSE; CONTINUE}]; IF ok THEN { IF r > LAST[INT] THEN SIGNAL DataError["nVars too big"]; IF r < 1 THEN SIGNAL DataError["nVars < 1"]; specifiedN _ Real.Fix[r]; nVarsSpecified _ TRUE; } ELSE SIGNAL DataError[status]; }; }; ENDCASE => { IF NOT nVarsSpecified THEN SIGNAL DataError["can't find the number of variables"]; -- currently doesn't allow '+ in front of the number of vars. SELECT tokenKind FROM tokenSINGLE => SELECT token[0] FROM '-, '+, '* => { IO.Backup[s, token[0]]; key _ valuesKey; [finalN, vars, yvalues] _ InitData[nVarsSpecified, specifiedN, nVars, vars]; }; ENDCASE => LOOP; tokenID => ProcessTokenID[]; tokenROPE => { IF varsSpecified THEN SIGNAL DataError["names have been listed"]; key _ varsKey; get _ FALSE; }; ENDCASE => SIGNAL SyntaxError[status]; }; }; varsKey => { status _ "getting vars"; SELECT tokenKind FROM tokenROPE => AppendROPEtoNames[]; tokenID => ProcessTokenID[]; ENDCASE => { IF NOT varsSpecified THEN SIGNAL DataError["can't find the names of variables"]; SELECT tokenKind FROM tokenSINGLE => SELECT token[0] FROM '-, '+, '* => { IO.Backup[s, token[0]]; key _ valuesKey; [finalN, vars, yvalues] _ InitData[nVarsSpecified, specifiedN, nVars, vars]; }; ENDCASE => LOOP; tokenDECIMAL, tokenREAL => { key _ valuesKey; get _ FALSE; [finalN, vars, yvalues] _ InitData[nVarsSpecified, specifiedN, nVars, vars]; }; ENDCASE => SIGNAL SyntaxError[status]; }; }; -- varsKey valuesKey => { status _ "parsing values"; SELECT tokenKind FROM tokenDECIMAL, tokenREAL, tokenSINGLE => { ok, normal: BOOL _ TRUE; negative: BOOL _ FALSE; r: REAL; IF tokenKind # tokenSINGLE THEN r _ Convert.RealFromRope[RefText.TrustTextAsRope[token] ! Convert.Error => {ok _ FALSE; CONTINUE}] ELSE SELECT token[0] FROM '-, '+ => { negative _ token[0] = '-; [tokenKind, token, charsSkipped, error] _ s.GetCedarToken[buffer]; SELECT tokenKind FROM tokenDECIMAL, tokenREAL => r _ Convert.RealFromRope[ RefText.TrustTextAsRope[token] ! Convert.Error => {ok _ FALSE; CONTINUE}]; ENDCASE => ok _ FALSE; IF negative THEN r _ -r; }; '* => {r _ NtNan; normal _ FALSE} ENDCASE => LOOP; IF NOT ok THEN SIGNAL DataError[status]; IF iCol = finalN OR iCol = 0 THEN {iCol _ 1; iRow _ iRow + 1} ELSE iCol _ iCol + 1; IF iCol = 1 THEN { xvalues _ CONS[r, xvalues]; tlvl _ yvalues; IF normal THEN {xmin _ MIN[r, xmin]; xmax _ MAX[r, xmax]}; } ELSE { IF tlvl = NIL THEN SIGNAL DataError["gee!"]; -- incredible? tlvl.first _ CONS[r, tlvl.first]; tlvl _ tlvl.rest; IF normal THEN {ymin _ MIN[r, ymin]; ymax _ MAX[r, ymax]}; }; valuesSpecified _ TRUE; }; tokenID => IF valuesSpecified THEN ProcessTokenID[] ELSE SIGNAL DataError["can't find values"]; tokenROPE => { IF NOT valuesSpecified THEN SIGNAL DataError["can't find values"]; endOfGroup _ TRUE; key _ varsKey; get _ FALSE; }; ENDCASE => SIGNAL SyntaxError[status]; }; -- valuesKey ENDCASE; ENDLOOP; IF valuesSpecified THEN handle _ AddNewGraph[oldGraph: handle, fileName: NARROW[Atom.GetPropFromList[s.propList, $Name]], vars: vars, xvalues: xvalues, yvalues: yvalues, bounds: [xmin, ymin, xmax, ymax] ]; ENDLOOP; RefText.ReleaseScratch[token]; }; -- ReadAsciiSimple KeyFromToken: PROC [token: REF TEXT] RETURNS [key: TextDataKeys _ noneKey] = { <> IF token # NIL THEN RETURN [SELECT TRUE FROM RefText.Equal[token, "nVars", FALSE], RefText.Equal[token, "nVariables", FALSE] => nVarsKey, RefText.Equal[token, "values", FALSE] => valuesKey, RefText.Equal[token, "vars", FALSE], RefText.Equal[token, "variables", FALSE] => varsKey, ENDCASE => noneKey]; }; -- KeyFromToken GetKeyWord: PROC [token: REF TEXT, s: IO.STREAM] RETURNS [key: TextDataKeys] = { [] _ s.GetChar[]; -- skip the colon. key _ KeyFromToken[token]; <> IF key = noneKey THEN SIGNAL SyntaxError["unknown key word"]; }; -- GetKeyWord InitData: PROC [nVarsSpecified: BOOL, specifiedN, nVars: INT, names: RopeList] RETURNS [finalN: INT, vars: RopeList, lvl: LVL _ NIL] = { -- , toGuess: BOOL _ FALSE] = { finalN _ nVars; vars _ names; IF vars = NIL AND NOT nVarsSpecified THEN SIGNAL DataError["number of variables unknown."]; IF vars = NIL AND nVarsSpecified THEN finalN _ specifiedN <> ELSE IF nVarsSpecified AND vars # NIL THEN { IF specifiedN # nVars THEN BlinkMsg["number of vars listed # specified nVars. The latter is discarded."]; }; <> <> <> IF finalN <= 1 THEN SIGNAL DataError["number of variables <= 1"]; IF vars = NIL THEN FOR i: INT IN [1..finalN] DO vars _ CONS[NIL, vars]; ENDLOOP; FOR j: INT IN [2..finalN] DO lvl _ CONS[NIL, lvl]; ENDLOOP; }; -- InitData BlankChar: PROC [char: CHAR] RETURNS [BOOL] = { OPEN Ascii; RETURN [SELECT char FROM SP, TAB, LF, CR, FF, NUL => TRUE, ENDCASE => FALSE] }; -- BlankChar PeekNextNonBlankChar: PROC [s: IO.STREAM] RETURNS [char: CHAR, eof: BOOL _ FALSE] = { ENABLE IO.EndOfStream => {eof _ TRUE; CONTINUE}; WHILE BlankChar[char _ s.PeekChar[]] DO [] _ s.GetChar[] ENDLOOP; }; -- PeekNextNonBlankChar AddNewGraph: PROC [oldGraph: GraphHandle, fileName: ROPE, vars: RopeList, xvalues: ValueList, yvalues: LVL, bounds: Imager.Box] RETURNS [newGraph: GraphHandle] = { tlvl: LVL; lengthX, lengthY, iCurve, groupId: INT _ 0; IF oldGraph = NIL OR vars = NIL OR xvalues = NIL OR yvalues = NIL THEN RETURN; lengthX _ LengthOfVL[xvalues]; FOR tlvl _ yvalues, tlvl.rest UNTIL tlvl = NIL DO iCurve _ iCurve + 1; IF (lengthY _ LengthOfVL[tlvl.first]) # lengthX THEN SIGNAL DataError[ IO.PutFR["%g-th variable has %g values but x has %g values", IO.int[iCurve], IO.int[lengthY], IO.int[lengthX]]]; ENDLOOP; [newGraph, groupId] _ NewGraph[ fileName: fileName, comment: fileName, xName: vars.first, <> bounds: bounds, oldGraph: oldGraph ]; [] _ SetXValues[handle: newGraph, values: xvalues, groupId: groupId]; -- xvalues is reversed, and it will be cleaned up. tlvl _ yvalues; FOR tn: RopeList _ vars.rest, tn.rest UNTIL tn = NIL DO [] _ AddCurve[handle: newGraph, values: tlvl.first, groupId: groupId, name: tn.first]; tlvl _ tlvl.rest; ENDLOOP; TRUSTED { List.Kill[LOOPHOLE[yvalues]]; List.Kill[LOOPHOLE[vars]]; }; }; -- AddNewGraph }. LOG. SChen, November 16, 1985 9:40:08 pm PST, created. <> <> <> <> <> <> <> <> <> <> <> <> <> < {endOfFile _ TRUE; GOTO found};>> <