<> <> <> 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], IO USING [EndOfStream, Error, GetCedarToken, GetChar, GetID, GetIndex, int, PeekChar, PutFR, rope, SetIndex, STREAM, TokenError, TokenKind], List USING [Kill], Real USING [Fix], 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] = { ENABLE { SyntaxError => {msg _ IO.PutFR["Syntax error at [%g], %g.", IO.int[s.GetIndex[]], IO.rope[message]]; CONTINUE}; DataError => {msg _ IO.PutFR["Error at [%g], %g.", IO.int[s.GetIndex[]], IO.rope[message]]; CONTINUE}; }; 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 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. SELECT tokenKind FROM tokenROPE => {key _ varsKey; get _ FALSE}; tokenID => ProcessTokenID[]; tokenREAL, tokenDECIMAL => { key _ valuesKey; get _ FALSE; [finalN, vars, yvalues] _ InitData[nVarsSpecified, specifiedN, nVars, vars]; }; ENDCASE => SIGNAL SyntaxError["key word, rope, or values expected"]; }; 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; rope: ROPE _ RefText.TrustTextAsRope[token]; r: REAL; r _ Convert.RealFromRope[rope ! 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"]; SELECT tokenKind FROM tokenSINGLE => 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[]; tokenSINGLE => 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 => { ok: BOOL _ TRUE; r: REAL; IF iCol = finalN OR iCol = 0 THEN {iCol _ 1; iRow _ iRow + 1} ELSE iCol _ iCol + 1; r _ IF RefText.Equal[token, "*"] THEN NtNan ELSE Convert.RealFromRope[RefText.TrustTextAsRope[token] ! Convert.Error => {ok _ FALSE; CONTINUE}]; IF NOT ok THEN SIGNAL DataError[status]; IF iCol = 1 THEN { xvalues _ CONS[r, xvalues]; tlvl _ yvalues; } ELSE { IF tlvl = NIL THEN SIGNAL DataError["gee!"]; -- incredible? tlvl.first _ CONS[r, tlvl.first]; tlvl _ tlvl.rest; }; valuesSpecified _ TRUE; }; tokenSINGLE => LOOP; 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]; 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] 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, 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};>> <