RosettaCodeData/Task/Text-processing-2/ALGOL-68/text-processing-2.alg

165 lines
8.0 KiB
Plaintext

BEGIN # Test processing/2 - using thee file from Text processing/1, #
# verify the format, detect duplicate dates and #
# count the number of records with all instruments #
# functioning #
PR read "files.incl.a68" PR # include file utilities: EACHLINE, etc. #
CHAR tab = REPR 9;
INT instruments = 24; # expected number of instrument readings #
MODE READING = STRUCT( STRING date
, [ 1 : instruments ]REAL data
, [ 1 : instruments ]INT status
, BOOL format ok
, STRING message
);
OP INIT = ( REF READING r )VOID:
BEGIN
FOR n TO instruments DO ( data OF r )[ n ] := ( status OF r )[ n ] := 0 OD;
format ok OF r := TRUE;
date OF r := "";
message OF r := ""
END # INIT # ;
INT all good count := 0; # number of lines which have all instruments ok #
STRING last date := ""; # date of the previous line #
# return the date and instrument readings and status values on the line #
PROC parse readings = ( STRING line )READING:
BEGIN
BOOL data error := FALSE;
# returns v converted to a REAL, returns 0 and sets data error #
# to TRUE if v is invalid #
OP TOREAL = ( STRING v )REAL:
BEGIN
REAL value := 0;
FILE real value;
associate( real value, LOC STRING := v );
on value error
( real value
, ( REF FILE ef )BOOL: # "handles" invalid data and #
BEGIN # returns TRUE so processing #
value := 0; # can continue #
data error := TRUE
END
);
get( real value, value );
close( real value );
value
END # TOREAL # ;
# returns v converted to an INT, returns 0 and sets data error #
# to TRUE if v is invalid #
OP TOINT = ( STRING text )INT:
BEGIN
INT value := 0;
BOOL is numeric := TRUE;
FOR ch pos FROM UPB text BY -1 TO LWB text WHILE is numeric DO
CHAR c = text[ ch pos ];
IF c = "-"
THEN value := - value;
is numeric := ch pos = LWB text AND ch pos /= UPB text
ELSE is numeric := is numeric AND c >= "0" AND c <= "9";
IF is numeric THEN ( value *:= 10 ) +:= ABS c - ABS "0" FI
FI
OD;
IF NOT is numeric THEN data error := TRUE FI;
value
END # TOINT # ;
# superficially checks v is in the format yyyy-mm-dd #
PROC check date format = ( STRING v )BOOL:
BEGIN
STRING ymd := v[ AT 1 ];
IF NOT ( data error := UPB ymd /= 10 ) THEN
# length is correct for yyyy-mm-dd #
IF NOT ( data error := ymd[ 5 ] /= "-" OR ymd[ 8 ] /= "-" ) THEN
INT d;
d := ( TOINT ymd[ 1 : 4 ] * 10000 )
+ ( TOINT ymd[ 6 : 7 ] * 100 )
+ TOINT ymd[ 9 : 10 ]
FI
FI;
NOT data error
END # check date format # ;
READING result; INIT result;
INT c pos := LWB line;
INT max pos = UPB line;
INT field number := 0;
WHILE c pos <= max pos DO
# skip leading spaces and tabs #
WHILE IF c pos > max pos
THEN FALSE
ELSE CHAR ch = line[ c pos ]; ch = " " OR ch = tab
FI
DO c pos +:= 1 OD;
IF c pos <= max pos THEN # have a field #
INT start pos = c pos;
WHILE IF c pos > max pos
THEN FALSE
ELSE CHAR ch := line[ c pos ]; ch /= " " AND ch /= tab
FI
DO c pos +:= 1 OD;
STRING f := line[ start pos : c pos - 1 ];
IF ( field number +:= 1 ) = 1 THEN # the date #
IF NOT check date format( f ) THEN
format ok OF result := FALSE;
message OF result := "Invalid date: " + f
FI;
date OF result := f
ELIF INT instrument = field number OVER 2;
instrument > instruments THEN
format ok OF result := FALSE; # too many readings #
message OF result := "Too many instruments"
ELIF ODD field number THEN
# field 3, 5, 7... the instrument state of reading #
( status OF result )[ field number OVER 2 ] := TOINT f;
IF data error THEN
format ok OF result := FALSE;
message OF result := "Invalid status for instrument " + whole( instrument, 0 )
FI
ELSE # must be the instrument reading #
( data OF result )[ field number OVER 2 ] := TOREAL f;
IF data error THEN
format ok OF result := FALSE;
message OF result := "Invalid reading for instrument " + whole( instrument, 0 )
FI
FI
FI
OD;
IF format ok OF result AND field number /= 2 * instruments + 1 THEN
format ok OF result := FALSE;
message OF result := "Incorrect number of readings/status values"
FI;
result
END # parse readings # ;
# checks the readings and date on line #
PROC check readings = ( STRING line, INT line count )BOOL:
BEGIN
READING data := parse readings( line );
IF format ok OF data AND line count > 1 AND date OF data = last date THEN
format ok OF data := FALSE;
message OF data := "Duplicate date: " + date OF data
FI;
IF NOT format ok OF data THEN # line has invalid data #
print( ( "Line: ", whole( line count, 0 ), ": ", message OF data, newline ) )
ELSE # the line appears ok #
BOOL all good := TRUE;
FOR i TO instruments WHILE all good := ( status OF data )[ i ] > 0 DO SKIP OD;
IF all good THEN all good count +:= 1 FI;
last date := date OF data
FI;
TRUE
END # check readings # ;
IF STRING file name = "readings.txt";
INT total lines = file name EACHLINE check readings;
total lines < 0
THEN print( ( "Unable to open ", file name, newline ) )
ELSE print( ( whole( total lines, 0 ), " lines in ", file name, ", " ) );
print( ( whole( all good count, 0 ), " have all instruments in a good state.", newline ) )
FI
END