(* BSD 2-Clause License Copyright (c) 2018, Anton Krotov All rights reserved. *) MODULE TEXTDRV; IMPORT FILES, C := COLLECTIONS; CONST CR = 0DX; LF = 0AX; CHUNK = 1024 * 256; TYPE TEXT* = POINTER TO RECORD (C.ITEM) chunk: ARRAY CHUNK OF BYTE; pos, size: INTEGER; file: FILES.FILE; utf8: BOOLEAN; CR: BOOLEAN; line*, col*: INTEGER; eof*: BOOLEAN; eol*: BOOLEAN; open*: PROCEDURE (text: TEXT; name: ARRAY OF CHAR): BOOLEAN; peak*: PROCEDURE (text: TEXT): CHAR; nextc*: PROCEDURE (text: TEXT) END; VAR texts: C.COLLECTION; PROCEDURE reset (text: TEXT); BEGIN text.chunk[0] := 0; text.pos := 0; text.size := 0; text.file := NIL; text.utf8 := FALSE; text.CR := FALSE; text.line := 1; text.col := 1; text.eof := FALSE; text.eol := FALSE END reset; PROCEDURE peak (text: TEXT): CHAR; RETURN CHR(text.chunk[text.pos]) END peak; PROCEDURE load (text: TEXT); BEGIN IF ~text.eof THEN text.size := FILES.read(text.file, text.chunk, LEN(text.chunk)); text.pos := 0; IF text.size = 0 THEN text.eof := TRUE; text.chunk[0] := 0 END END END load; PROCEDURE next (text: TEXT); VAR c: CHAR; BEGIN IF text.pos < text.size - 1 THEN INC(text.pos) ELSE load(text) END; IF ~text.eof THEN c := peak(text); IF c = CR THEN INC(text.line); text.col := 0; text.eol := TRUE; text.CR := TRUE ELSIF c = LF THEN IF ~text.CR THEN INC(text.line); text.col := 0; text.eol := TRUE ELSE text.eol := FALSE END; text.CR := FALSE ELSE text.eol := FALSE; IF text.utf8 THEN IF (c < 80X) OR (c > 0BFX) THEN INC(text.col) END ELSE INC(text.col) END; text.CR := FALSE END END END next; PROCEDURE init (text: TEXT); BEGIN IF (text.pos = 0) & (text.size >= 3) THEN IF (text.chunk[0] = 0EFH) & (text.chunk[1] = 0BBH) & (text.chunk[2] = 0BFH) THEN text.pos := 3; text.utf8 := TRUE END END; IF text.size = 0 THEN text.chunk[0] := 0; text.size := 1; text.eof := FALSE END; text.line := 1; text.col := 1 END init; PROCEDURE open (text: TEXT; name: ARRAY OF CHAR): BOOLEAN; BEGIN ASSERT(text # NIL); reset(text); text.file := FILES.open(name); IF text.file # NIL THEN load(text); init(text) END RETURN text.file # NIL END open; PROCEDURE NewText (): TEXT; VAR text: TEXT; citem: C.ITEM; BEGIN citem := C.pop(texts); IF citem = NIL THEN NEW(text) ELSE text := citem(TEXT) END RETURN text END NewText; PROCEDURE create* (): TEXT; VAR text: TEXT; BEGIN text := NewText(); reset(text); text.open := open; text.peak := peak; text.nextc := next RETURN text END create; PROCEDURE destroy* (VAR text: TEXT); BEGIN IF text # NIL THEN IF text.file # NIL THEN FILES.close(text.file) END; C.push(texts, text); text := NIL END END destroy; BEGIN texts := C.create() END TEXTDRV.