-- DataInput.mesa Edited by Sweet, 15-Jan-81 16:11:51
DIRECTORY
IODefs: FROM "iodefs" USING [ControlZ, CR, SP, WriteString, TAB],
LedgerDefs: FROM "ledgerdefs",
StreamDefs: FROM "streamdefs",
StringDefs: FROM "stringdefs",
SystemDefs: FROM "systemdefs";
DataInput: PROGRAM
IMPORTS IODefs, LedgerDefs, StreamDefs, StringDefs, SystemDefs
EXPORTS LedgerDefs =
BEGIN OPEN IODefs, StringDefs, LedgerDefs;
SkipBlanks: PUBLIC PROCEDURE [ss: SubString, notTab: BOOLEAN ← FALSE] =
BEGIN
DO
IF Exhausted[ss] THEN RETURN;
SELECT CurrentChar[ss] FROM
TAB => IF notTab THEN RETURN ELSE Bump[ss];
SP => Bump[ss];
CR => WarnSS["Unexpected end of line", ss];
ENDCASE => RETURN;
ENDLOOP;
END;
GetNumber: PUBLIC PROCEDURE [ss: SubString, defaultOK: BOOLEAN ← FALSE, default: INTEGER ← 0] RETURNS [val: INTEGER] =
BEGIN
neg: BOOLEAN ← FALSE;
c: CHARACTER;
SkipBlanks[ss];
DO
IF Exhausted[ss] THEN GO TO noNum;
SELECT (c ← CurrentChar[ss]) FROM
'- => neg ← TRUE;
IN ['0..'9] => EXIT;
ENDCASE => GO TO noNum;
Bump[ss];
REPEAT
noNum =>
BEGIN
IF ~defaultOK THEN WarnSS["Missing number", ss];
RETURN [default];
END;
ENDLOOP;
val ← 0;
WHILE c IN ['0..'9] DO
val ← 10*val+(c-'0);
Bump[ss];
IF Exhausted[ss] THEN EXIT;
c ← CurrentChar[ss];
ENDLOOP;
IF neg THEN val ← -val;
RETURN
END;
GetLongNumber: PROCEDURE [ss: SubString, defaultOK: BOOLEAN ← FALSE, default: LONG INTEGER ← 0] RETURNS [val: LONG INTEGER] =
BEGIN
neg: BOOLEAN ← FALSE;
c: CHARACTER;
SkipBlanks[ss];
DO
IF Exhausted[ss] THEN GO TO noNum;
SELECT (c ← CurrentChar[ss]) FROM
'- => neg ← TRUE;
IN ['0..'9] => EXIT;
ENDCASE => GO TO noNum;
Bump[ss];
REPEAT
noNum =>
BEGIN
IF ~defaultOK THEN WarnSS["Missing number", ss];
RETURN [default];
END;
ENDLOOP;
val ← 0;
WHILE c IN ['0..'9] DO
val ← 10*val+(c-'0);
Bump[ss];
IF Exhausted[ss] THEN EXIT;
c ← CurrentChar[ss];
ENDLOOP;
IF neg THEN val ← -val;
RETURN
END;
GetMoney: PUBLIC PROCEDURE [ss: SubString] RETURNS [val: Money, deductable: BOOLEAN] =
BEGIN
neg, brkt: BOOLEAN ← FALSE;
SkipBlanks[ss];
IF CurrentChar[ss] = '< THEN
BEGIN neg ← brkt ← TRUE; Bump[ss] END;
IF CurrentChar[ss] = '- THEN BEGIN neg ← TRUE; Bump[ss]; END;
val ← IF CurrentChar[ss] = '. THEN 0 ELSE GetLongNumber[ss]*100;
IF CurrentChar[ss] = '. THEN
BEGIN
Bump[ss];
val ← val + GetLongNumber[ss, TRUE, 0];
END;
IF brkt THEN
IF CurrentChar[ss] # '> THEN WarnSS["Missing <",ss]
ELSE Bump[ss];
IF neg THEN val ← -val;
SELECT CurrentChar[ss] FROM
'T, 't => BEGIN Bump[ss]; deductable ← TRUE; END;
ENDCASE => deductable ← FALSE;
RETURN
END;
defaultYear: [60..99] ← 60;
yearDefaultable: BOOLEAN ← FALSE;
lastDate: PUBLIC SmallDate ← NullDate;
GetDate: PROCEDURE [ss: SubString] RETURNS [dd: SmallDate] =
BEGIN
m: CARDINAL;
SkipBlanks[ss, TRUE];
IF CurrentChar[ss] = TAB THEN
IF lastDate = NullDate THEN WarnSS["Invalid date",ss]
ELSE RETURN [lastDate];
dd.cv ← 0;
m ← GetNumber[ss];
IF CurrentChar[ss] # '/ THEN
IF lastDate = NullDate THEN WarnSS["Invalid date",ss]
ELSE {lastDate.day ← m; dd ← lastDate; RETURN};
dd.month ← m;
Bump[ss];
dd.day ← GetNumber[ss];
IF CurrentChar[ss] = '/ THEN
BEGIN
Bump[ss];
dd.year ← GetNumber[ss, yearDefaultable, defaultYear];
END
ELSE IF yearDefaultable THEN dd.year ← defaultYear
ELSE WarnSS["Invalid date",ss];
lastDate ← dd;
RETURN
END;
lastCheck: PUBLIC CARDINAL ← NullCheck;
GetCheck: PROC [ss: SubString] RETURNS [CARDINAL] =
BEGIN
SkipBlanks[ss, TRUE];
IF CurrentChar[ss] = TAB THEN
IF lastCheck = NullCheck THEN WarnSS["Missing Check number",ss]
ELSE RETURN [lastCheck ← lastCheck + 1];
RETURN[lastCheck ← GetNumber[ss]];
END;
GetUpTo: PUBLIC PROCEDURE [ss: SubString, stop: CHARACTER] RETURNS [ans: STRING] =
BEGIN
adesc: SubStringDescriptor ← ss↑;
DO
IF Exhausted[ss] THEN
BEGIN
ws: STRING ← [15];
AppendString[ws, "Missing "];
IF stop = IODefs.TAB THEN AppendString[ws, "<TAB>"]
ELSE AppendChar[ws, stop];
WarnSS[ws, ss];
END;
IF CurrentChar[ss] = stop THEN EXIT;
Bump[ss];
ENDLOOP;
adesc.length ← ss.offset - adesc.offset;
ans ← SystemDefs.AllocateHeapString[adesc.length];
AppendSubString[ans, @adesc];
END;
GetCategory: PUBLIC PROCEDURE [ss: SubString] RETURNS [col: Column, note: STRING] =
BEGIN
key: STRING ← [10];
c: CHARACTER;
SkipBlanks[ss];
IF Exhausted[ss] THEN WarnSS["Missing Category", ss];
DO
IF Exhausted[ss] THEN EXIT;
SELECT (c ← CurrentChar[ss]) FROM
IN ['a..'z], IN ['A..'Z] => StringDefs.AppendChar[key, c];
ENDCASE => EXIT;
Bump[ss];
ENDLOOP;
FOR col IN Column DO
IF StringDefs.EquivalentString[categoryKey[col], key] THEN EXIT;
REPEAT
FINISHED => WarnSS["Invalid category", ss];
ENDLOOP;
SkipBlanks[ss];
IF ~Exhausted[ss] AND CurrentChar[ss] = '( THEN
BEGIN
Bump[ss];
note ← GetUpTo[ss, ')];
Bump[ss];
END
ELSE note ← NIL;
END;
dateFirst: PUBLIC BOOLEAN;
instring: STRING;
InputLine: PUBLIC PROCEDURE [p: POINTER] RETURNS [CARDINAL] =
BEGIN
sip: POINTER TO SortItem = p;
adesc: StringDefs.SubStringDescriptor ← [base: instring, offset: NULL, length: 1];
ss: StringDefs.SubString = @adesc;
DO
ENABLE BadData => LOOP;
GetLine[instring];
IF EndOfFile AND instring.length = 0 THEN
BEGIN IODefs.WriteString["Sorting..."]; RETURN[0]; END;
ss.offset ← 0;
SkipBlanks[ss, TRUE];
IF Exhausted[ss] THEN LOOP;
SELECT CurrentChar[ss] FROM
TAB, IN ['0..'9] =>
BEGIN
IF dateFirst THEN
BEGIN
sip.date ← GetDate[ss];
SkipBlanks[ss, TRUE];
IF CurrentChar[ss] # TAB THEN WarnSS["missing TAB", ss];
Bump[ss];
sip.check ← GetCheck[ss];
END
ELSE
BEGIN
sip.check ← GetCheck[ss];
SkipBlanks[ss, TRUE];
IF CurrentChar[ss] # TAB THEN WarnSS["missing TAB", ss];
Bump[ss];
sip.date ← GetDate[ss];
END;
SkipBlanks[ss];
ss.length ← instring.length - ss.offset;
sip.string ← [length: 0, maxlength: ss.length, text:];
StringDefs.AppendSubString[@sip.string, ss];
RETURN [SIZE[SortItem]+(ss.length+1)/2]
END;
'y, 'Y =>
BEGIN
SkipToken[ss];
yearDefaultable ← TRUE;
defaultYear ← GetNumber[ss];
END;
'd, 'D => dateFirst ← TRUE;
'c, 'C => dateFirst ← FALSE;
't, 'T =>
BEGIN
col: Column;
val: Money;
SkipToken[ss];
DO
val ← GetMoney[ss].val;
col ← GetCategory[ss].col;
ytdTotal[col] ← val;
SkipBlanks[ss];
IF Exhausted[ss] THEN EXIT;
IF CurrentChar[ss] # '/ THEN WarnSS["Expected /", ss];
Bump[ss];
ENDLOOP;
END;
IODefs.ControlZ => NULL; -- ignore lines that start with ↑Z
ENDCASE => WarnSS["Bad input data",ss];
ENDLOOP;
END;
SkipToken: PROCEDURE [ss: StringDefs.SubString] =
BEGIN
DO
Bump[ss];
SELECT CurrentChar[ss] FROM
SP, TAB => EXIT;
CR => WarnSS["Unexpected end of line",ss];
ENDCASE => NULL;
ENDLOOP;
END;
EndOfFile: PUBLIC BOOLEAN;
instream: PUBLIC StreamDefs.StreamHandle ← NIL;
GetLine: PROCEDURE [instring: STRING] =
BEGIN
ch: CHARACTER;
instring.length ← 0;
IF EndOfFile THEN RETURN;
DO
ch ← instream.get[instream !StreamDefs.StreamError =>
IF error = StreamAccess
THEN BEGIN EndOfFile ← TRUE; EXIT END];
IF ch = CR THEN EXIT;
AppendChar[instring, ch];
ENDLOOP;
RETURN
END;
instring ← SystemDefs.AllocateResidentPages[2];
instring↑ ← [length: 0, maxlength: (512-2)*2, text: NULL];
END.