Texts.Mod.txt 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. MODULE Texts; (*JG 21.11.90 / NW 11.7.90 / 24.12.95 / 22.11.10 / 18.11.2014 / 10.1.2019*)
  2. IMPORT Files, Fonts;
  3. CONST (*scanner symbol classes*)
  4. Inval* = 0; (*invalid symbol*)
  5. Name* = 1; (*name s (length len)*)
  6. String* = 2; (*literal string s (length len)*)
  7. Int* = 3; (*integer i (decimal or hexadecimal)*)
  8. Real* = 4; (*real number x*)
  9. Char* = 6; (*special character c*)
  10. (* TextBlock = TextTag offset run {run} "0" len {AsciiCode}.
  11. run = fnt [name] col voff len. *)
  12. TAB = 9X; CR = 0DX;
  13. TextTag = 0F1X;
  14. replace* = 0; insert* = 1; delete* = 2; unmark* = 3; (*op-codes*)
  15. TYPE Piece = POINTER TO PieceDesc;
  16. PieceDesc = RECORD
  17. f: Files.File;
  18. off, len: LONGINT;
  19. fnt: Fonts.Font;
  20. col, voff: INTEGER;
  21. prev, next: Piece
  22. END;
  23. Text* = POINTER TO TextDesc;
  24. Notifier* = PROCEDURE (T: Text; op: INTEGER; beg, end: LONGINT);
  25. TextDesc* = RECORD
  26. len*: LONGINT;
  27. changed*: BOOLEAN;
  28. notify*: Notifier;
  29. trailer: Piece;
  30. pce: Piece; (*cache*)
  31. org: LONGINT (*cache*)
  32. END;
  33. Reader* = RECORD
  34. eot*: BOOLEAN;
  35. fnt*: Fonts.Font;
  36. col*, voff*: INTEGER;
  37. ref: Piece;
  38. org: LONGINT;
  39. off: LONGINT;
  40. rider: Files.Rider
  41. END;
  42. Scanner* = RECORD (Reader)
  43. nextCh*: CHAR;
  44. line*, class*: INTEGER;
  45. i*: LONGINT;
  46. x*: REAL;
  47. y*: LONGREAL;
  48. c*: CHAR;
  49. len*: INTEGER;
  50. s*: ARRAY 32 OF CHAR
  51. END;
  52. Buffer* = POINTER TO BufDesc;
  53. BufDesc* = RECORD
  54. len*: LONGINT;
  55. header, last: Piece
  56. END;
  57. Writer* = RECORD
  58. buf*: Buffer;
  59. fnt*: Fonts.Font;
  60. col*, voff*: INTEGER;
  61. rider: Files.Rider
  62. END;
  63. VAR TrailerFile: Files.File;
  64. (* -------------------- Filing ------------------------*)
  65. PROCEDURE Trailer(): Piece;
  66. VAR Q: Piece;
  67. BEGIN NEW(Q);
  68. Q.f := TrailerFile; Q.off := -1; Q.len := 1; Q.fnt := NIL; Q.col := 0; Q.voff := 0; RETURN Q
  69. END Trailer;
  70. PROCEDURE Load* (VAR R: Files.Rider; T: Text);
  71. VAR Q, q, p: Piece;
  72. off: LONGINT;
  73. N, fno: INTEGER; bt: BYTE;
  74. f: Files.File;
  75. FName: ARRAY 32 OF CHAR;
  76. Dict: ARRAY 32 OF Fonts.Font;
  77. BEGIN f := Files.Base(R); N := 1; Q := Trailer(); p := Q;
  78. Files.ReadInt(R, off); Files.ReadByte(R, bt); fno := bt;
  79. WHILE fno # 0 DO
  80. IF fno = N THEN
  81. Files.ReadString(R, FName);
  82. Dict[N] := Fonts.This(FName); INC(N)
  83. END;
  84. NEW(q); q.fnt := Dict[fno];
  85. Files.ReadByte(R, bt); q.col := bt;
  86. Files.ReadByte(R, bt); q.voff := ASR(LSL(bt, -24), 24);
  87. Files.ReadInt(R, q.len);
  88. Files.ReadByte(R, bt); fno := bt;
  89. q.f := f; q.off := off; off := off + q.len;
  90. p.next := q; q.prev := p; p := q
  91. END;
  92. p.next := Q; Q.prev := p;
  93. T.trailer := Q; Files.ReadInt(R, T.len); (*Files.Set(R, f, Files.Pos(R) + T.len)*)
  94. END Load;
  95. PROCEDURE Open* (T: Text; name: ARRAY OF CHAR);
  96. VAR f: Files.File; R: Files.Rider; Q, q: Piece;
  97. tag: CHAR; len: LONGINT;
  98. BEGIN f := Files.Old(name);
  99. IF f # NIL THEN
  100. Files.Set(R, f, 0); Files.Read(R, tag);
  101. IF tag = TextTag THEN Load(R, T)
  102. ELSE (*Ascii file*)
  103. len := Files.Length(f); Q := Trailer();
  104. NEW(q); q.fnt := Fonts.Default; q.col := 1; q.voff := 0; q.f := f; q.off := 0; q.len := len;
  105. Q.next := q; q.prev := Q; q.next := Q; Q.prev := q; T.trailer := Q; T.len := len
  106. END
  107. ELSE (*create new text*)
  108. Q := Trailer(); Q.next := Q; Q.prev := Q; T.trailer := Q; T.len := 0
  109. END ;
  110. T.changed := FALSE; T.org := -1; T.pce := T.trailer (*init cache*)
  111. END Open;
  112. PROCEDURE Store* (VAR W: Files.Rider; T: Text);
  113. VAR p, q: Piece;
  114. R: Files.Rider;
  115. off, rlen, pos: LONGINT;
  116. N, n: INTEGER;
  117. ch: CHAR;
  118. Dict: ARRAY 32, 32 OF CHAR;
  119. BEGIN pos := Files.Pos(W); Files.WriteInt(W, 0); (*place holder*)
  120. N := 1; p := T.trailer.next;
  121. WHILE p # T.trailer DO
  122. rlen := p.len; q := p.next;
  123. WHILE (q # T.trailer) & (q.fnt = p.fnt) & (q.col = p.col) & (q.voff = p.voff) DO
  124. rlen := rlen + q.len; q := q.next
  125. END;
  126. Dict[N] := p.fnt.name;
  127. n := 1;
  128. WHILE Dict[n] # p.fnt.name DO INC(n) END;
  129. Files.WriteByte(W, n);
  130. IF n = N THEN Files.WriteString(W, p.fnt.name); INC(N) END;
  131. Files.WriteByte(W, p.col); Files.WriteByte(W, p.voff); Files.WriteInt(W, rlen);
  132. p := q
  133. END;
  134. Files.WriteByte(W, 0); Files.WriteInt(W, T.len);
  135. off := Files.Pos(W); p := T.trailer.next;
  136. WHILE p # T.trailer DO
  137. rlen := p.len; Files.Set(R, p.f, p.off);
  138. WHILE rlen > 0 DO Files.Read(R, ch); Files.Write(W, ch); DEC(rlen) END ;
  139. p := p.next
  140. END ;
  141. Files.Set(W, Files.Base(W), pos); Files.WriteInt(W, off); (*fixup*)
  142. T.changed := FALSE;
  143. IF T.notify # NIL THEN T.notify(T, unmark, 0, 0) END
  144. END Store;
  145. PROCEDURE Close*(T: Text; name: ARRAY OF CHAR);
  146. VAR f: Files.File; w: Files.Rider;
  147. BEGIN f := Files.New(name); Files.Set(w, f, 0);
  148. Files.Write(w, TextTag); Store(w, T); Files.Register(f)
  149. END Close;
  150. (* -------------------- Editing ----------------------- *)
  151. PROCEDURE OpenBuf* (B: Buffer);
  152. BEGIN NEW(B.header); (*null piece*)
  153. B.last := B.header; B.len := 0
  154. END OpenBuf;
  155. PROCEDURE FindPiece (T: Text; pos: LONGINT; VAR org: LONGINT; VAR pce: Piece);
  156. VAR p: Piece; porg: LONGINT;
  157. BEGIN p := T.pce; porg := T.org;
  158. IF pos >= porg THEN
  159. WHILE pos >= porg + p.len DO INC(porg, p.len); p := p.next END
  160. ELSE p := p.prev; DEC(porg, p.len);
  161. WHILE pos < porg DO p := p.prev; DEC(porg, p.len) END
  162. END ;
  163. T.pce := p; T.org := porg; (*update cache*)
  164. pce := p; org := porg
  165. END FindPiece;
  166. PROCEDURE SplitPiece (p: Piece; off: LONGINT; VAR pr: Piece);
  167. VAR q: Piece;
  168. BEGIN
  169. IF off > 0 THEN NEW(q);
  170. q.fnt := p.fnt; q.col := p.col; q.voff := p.voff;
  171. q.len := p.len - off;
  172. q.f := p.f; q.off := p.off + off;
  173. p.len := off;
  174. q.next := p.next; p.next := q;
  175. q.prev := p; q.next.prev := q;
  176. pr := q
  177. ELSE pr := p
  178. END
  179. END SplitPiece;
  180. PROCEDURE Save* (T: Text; beg, end: LONGINT; B: Buffer);
  181. VAR p, q, qb, qe: Piece; org: LONGINT;
  182. BEGIN
  183. IF end > T.len THEN end := T.len END;
  184. FindPiece(T, beg, org, p);
  185. NEW(qb); qb^ := p^;
  186. qb.len := qb.len - (beg - org);
  187. qb.off := qb.off + (beg - org);
  188. qe := qb;
  189. WHILE end > org + p.len DO
  190. org := org + p.len; p := p.next;
  191. NEW(q); q^ := p^; qe.next := q; q.prev := qe; qe := q
  192. END;
  193. qe.next := NIL; qe.len := qe.len - (org + p.len - end);
  194. B.last.next := qb; qb.prev := B.last; B.last := qe;
  195. B.len := B.len + (end - beg)
  196. END Save;
  197. PROCEDURE Copy* (SB, DB: Buffer);
  198. VAR Q, q, p: Piece;
  199. BEGIN p := SB.header; Q := DB.last;
  200. WHILE p # SB.last DO p := p.next;
  201. NEW(q); q^ := p^; Q.next := q; q.prev := Q; Q := q
  202. END;
  203. DB.last := Q; DB.len := DB.len + SB.len
  204. END Copy;
  205. PROCEDURE Insert* (T: Text; pos: LONGINT; B: Buffer);
  206. VAR pl, pr, p, qb, qe: Piece; org, end: LONGINT;
  207. BEGIN
  208. FindPiece(T, pos, org, p); SplitPiece(p, pos - org, pr);
  209. IF T.org >= org THEN T.org := org - p.prev.len; T.pce := p.prev END ;
  210. pl := pr.prev; qb := B.header.next;
  211. IF (qb # NIL) & (qb.f = pl.f) & (qb.off = pl.off + pl.len)
  212. & (qb.fnt = pl.fnt) & (qb.col = pl.col) & (qb.voff = pl.voff) THEN
  213. pl.len := pl.len + qb.len; qb := qb.next
  214. END;
  215. IF qb # NIL THEN qe := B.last;
  216. qb.prev := pl; pl.next := qb; qe.next := pr; pr.prev := qe
  217. END;
  218. T.len := T.len + B.len; end := pos + B.len;
  219. B.last := B.header; B.last.next := NIL; B.len := 0;
  220. T.changed := TRUE;
  221. IF T.notify # NIL THEN T.notify(T, insert, pos, end) END
  222. END Insert;
  223. PROCEDURE Append* (T: Text; B: Buffer);
  224. BEGIN Insert(T, T.len, B)
  225. END Append;
  226. PROCEDURE Delete* (T: Text; beg, end: LONGINT; B: Buffer);
  227. VAR pb, pe, pbr, per: Piece; orgb, orge: LONGINT;
  228. BEGIN
  229. IF end > T.len THEN end := T.len END;
  230. FindPiece(T, beg, orgb, pb); SplitPiece(pb, beg - orgb, pbr);
  231. FindPiece(T, end, orge, pe);
  232. SplitPiece(pe, end - orge, per);
  233. IF T.org >= orgb THEN (*adjust cache*)
  234. T.org := orgb - pb.prev.len; T.pce := pb.prev
  235. END;
  236. B.header.next := pbr; B.last := per.prev;
  237. B.last.next := NIL; B.len := end - beg;
  238. per.prev := pbr.prev; pbr.prev.next := per;
  239. T.len := T.len - B.len;
  240. T.changed := TRUE;
  241. IF T.notify # NIL THEN T.notify(T, delete, beg, end) END
  242. END Delete;
  243. PROCEDURE ChangeLooks* (T: Text; beg, end: LONGINT; sel: SET; fnt: Fonts.Font; col, voff: INTEGER);
  244. VAR pb, pe, p: Piece; org: LONGINT;
  245. BEGIN
  246. IF end > T.len THEN end := T.len END;
  247. FindPiece(T, beg, org, p); SplitPiece(p, beg - org, pb);
  248. FindPiece(T, end, org, p); SplitPiece(p, end - org, pe);
  249. p := pb;
  250. REPEAT
  251. IF 0 IN sel THEN p.fnt := fnt END;
  252. IF 1 IN sel THEN p.col := col END;
  253. IF 2 IN sel THEN p.voff := voff END;
  254. p := p.next
  255. UNTIL p = pe;
  256. T.changed := TRUE;
  257. IF T.notify # NIL THEN T.notify(T, replace, beg, end) END
  258. END ChangeLooks;
  259. PROCEDURE Attributes*(T: Text; pos: LONGINT; VAR fnt: Fonts.Font; VAR col, voff: INTEGER);
  260. VAR p: Piece; org: LONGINT;
  261. BEGIN FindPiece(T, pos, org, p); fnt := p.fnt; col := p.col; voff := p.voff
  262. END Attributes;
  263. (* ------------------ Access: Readers ------------------------- *)
  264. PROCEDURE OpenReader* (VAR R: Reader; T: Text; pos: LONGINT);
  265. VAR p: Piece; org: LONGINT;
  266. BEGIN FindPiece(T, pos, org, p);
  267. R.ref := p; R.org := org; R.off := pos - org;
  268. Files.Set(R.rider, p.f, p.off + R.off); R.eot := FALSE
  269. END OpenReader;
  270. PROCEDURE Read* (VAR R: Reader; VAR ch: CHAR);
  271. BEGIN Files.Read(R.rider, ch);
  272. R.fnt := R.ref.fnt; R.col := R.ref.col; R.voff := R.ref.voff;
  273. INC(R.off);
  274. IF R.off = R.ref.len THEN
  275. IF R.ref.f = TrailerFile THEN R.eot := TRUE END;
  276. R.ref := R.ref.next; R.org := R.org + R.off; R.off := 0;
  277. Files.Set(R.rider, R.ref.f, R.ref.off)
  278. END
  279. END Read;
  280. PROCEDURE Pos* (VAR R: Reader): LONGINT;
  281. BEGIN RETURN R.org + R.off
  282. END Pos;
  283. (* ------------------ Access: Scanners (NW) ------------------------- *)
  284. PROCEDURE OpenScanner* (VAR S: Scanner; T: Text; pos: LONGINT);
  285. BEGIN OpenReader(S, T, pos); S.line := 0; S.nextCh := " "
  286. END OpenScanner;
  287. (*floating point formats:
  288. x = 1.m * 2^(e-127) bit 0: sign, bits 1- 8: e, bits 9-31: m
  289. x = 1.m * 2^(e-1023) bit 0: sign, bits 1-11: e, bits 12-63: m *)
  290. PROCEDURE Ten(n: INTEGER): REAL;
  291. VAR t, p: REAL;
  292. BEGIN t := 1.0; p := 10.0; (*compute 10^n *)
  293. WHILE n > 0 DO
  294. IF ODD(n) THEN t := p * t END ;
  295. p := p*p; n := n DIV 2
  296. END ;
  297. RETURN t
  298. END Ten;
  299. PROCEDURE Scan* (VAR S: Scanner);
  300. CONST maxExp = 38; maxM = 16777216; (*2^24*)
  301. VAR ch: CHAR;
  302. neg, negE, hex: BOOLEAN;
  303. i, j, h, d, e, n, s: INTEGER;
  304. x: REAL;
  305. BEGIN ch := S.nextCh; i := 0;
  306. WHILE (ch = " ") OR (ch = TAB) OR (ch = CR) DO
  307. IF ch = CR THEN INC(S.line) END ;
  308. Read(S, ch)
  309. END ;
  310. IF ("A" <= ch) & (ch <= "Z") OR ("a" <= ch) & (ch <= "z") THEN (*name*)
  311. REPEAT S.s[i] := ch; INC(i); Read(S, ch)
  312. UNTIL ((ch < "0") & (ch # ".") OR ("9" < ch) & (ch < "A") OR ("Z" < ch) & (ch < "a") OR ("z" < ch)) OR (i = 31);
  313. S.s[i] := 0X; S.len := i; S.class := Name
  314. ELSIF ch = 22X THEN (*string*)
  315. Read(S, ch);
  316. WHILE (ch # 22X) & (ch >= " ") & (i # 31) DO S.s[i] := ch; INC(i); Read(S, ch) END;
  317. S.s[i] := 0X; S.len := i+1; Read(S, ch); S.class := String
  318. ELSE hex := FALSE;
  319. IF ch = "-" THEN neg := TRUE; Read(S, ch) ELSE neg := FALSE END ;
  320. IF ("0" <= ch) & (ch <= "9") THEN (*number*)
  321. n := ORD(ch) - 30H; h := n; Read(S, ch);
  322. WHILE ("0" <= ch) & (ch <= "9") OR ("A" <= ch) & (ch <= "F") DO
  323. IF ch <= "9" THEN d := ORD(ch) - 30H ELSE d := ORD(ch) - 37H; hex := TRUE END ;
  324. n := 10*n + d; h := 10H*h + d; Read(S, ch)
  325. END ;
  326. IF ch = "H" THEN (*hex integer*) Read(S, ch); S.i := h; S.class := Int (*neg?*)
  327. ELSIF ch = "." THEN (*real number*)
  328. Read(S, ch); x := 0.0; e := 0; j := 0;
  329. WHILE ("0" <= ch) & (ch <= "9") DO (*fraction*)
  330. h := 10*n + (ORD(ch) - 30H);
  331. IF h < maxM THEN n := h; INC(j) END ;
  332. Read(S, ch)
  333. END ;
  334. IF ch = "E" THEN (*scale factor*)
  335. s := 0; Read(S, ch);
  336. IF ch = "-" THEN negE := TRUE; Read(S, ch)
  337. ELSE negE := FALSE;
  338. IF ch = "+" THEN Read(S, ch) END
  339. END ;
  340. WHILE ("0" <= ch) & (ch <= "9") DO
  341. s := s*10 + ORD(ch) - 30H; Read(S, ch)
  342. END ;
  343. IF negE THEN DEC(e, s) ELSE INC(e, s) END ;
  344. END ;
  345. x := FLT(n); DEC(e, j);
  346. IF e < 0 THEN
  347. IF e >= -maxExp THEN x := x / Ten(-e) ELSE x := 0.0 END
  348. ELSIF e > 0 THEN
  349. IF e <= maxExp THEN x := Ten(e) * x ELSE x := 0.0 END
  350. END ;
  351. IF neg THEN S.x := -x ELSE S.x := x END ;
  352. IF hex THEN S.class := 0 ELSE S.class := Real END
  353. ELSE (*decimal integer*)
  354. IF neg THEN S.i := -n ELSE S.i := n END;
  355. IF hex THEN S.class := Inval ELSE S.class := Int END
  356. END
  357. ELSE (*spectal character*) S.class := Char;
  358. IF neg THEN S.c := "-" ELSE S.c := ch; Read(S, ch) END
  359. END
  360. END ;
  361. S.nextCh := ch
  362. END Scan;
  363. (* --------------- Access: Writers (NW) ------------------ *)
  364. PROCEDURE OpenWriter* (VAR W: Writer);
  365. BEGIN NEW(W.buf);
  366. OpenBuf(W.buf); W.fnt := Fonts.Default; W.col := 15; W.voff := 0;
  367. Files.Set(W.rider, Files.New(""), 0)
  368. END OpenWriter;
  369. PROCEDURE SetFont* (VAR W: Writer; fnt: Fonts.Font);
  370. BEGIN W.fnt := fnt
  371. END SetFont;
  372. PROCEDURE SetColor* (VAR W: Writer; col: INTEGER);
  373. BEGIN W.col := col
  374. END SetColor;
  375. PROCEDURE SetOffset* (VAR W: Writer; voff: INTEGER);
  376. BEGIN W.voff := voff
  377. END SetOffset;
  378. PROCEDURE Write* (VAR W: Writer; ch: CHAR);
  379. VAR p: Piece;
  380. BEGIN
  381. IF (W.buf.last.fnt # W.fnt) OR (W.buf.last.col # W.col) OR (W.buf.last.voff # W.voff) THEN
  382. NEW(p); p.f := Files.Base(W.rider); p.off := Files.Pos(W.rider); p.len := 0;
  383. p.fnt := W.fnt; p.col := W.col; p.voff:= W.voff;
  384. p.next := NIL; W.buf.last.next := p;
  385. p.prev := W.buf.last; W.buf.last := p
  386. END;
  387. Files.Write(W.rider, ch);
  388. INC(W.buf.last.len); INC(W.buf.len)
  389. END Write;
  390. PROCEDURE WriteLn* (VAR W: Writer);
  391. BEGIN Write(W, CR)
  392. END WriteLn;
  393. PROCEDURE WriteString* (VAR W: Writer; s: ARRAY OF CHAR);
  394. VAR i: INTEGER;
  395. BEGIN i := 0;
  396. WHILE s[i] >= " " DO Write(W, s[i]); INC(i) END
  397. END WriteString;
  398. PROCEDURE WriteInt* (VAR W: Writer; x, n: LONGINT);
  399. VAR i: INTEGER; x0: LONGINT;
  400. a: ARRAY 10 OF CHAR;
  401. BEGIN
  402. IF ROR(x, 31) = 1 THEN WriteString(W, " -2147483648")
  403. ELSE i := 0;
  404. IF x < 0 THEN DEC(n); x0 := -x ELSE x0 := x END;
  405. REPEAT
  406. a[i] := CHR(x0 MOD 10 + 30H); x0 := x0 DIV 10; INC(i)
  407. UNTIL x0 = 0;
  408. WHILE n > i DO Write(W, " "); DEC(n) END;
  409. IF x < 0 THEN Write(W, "-") END;
  410. REPEAT DEC(i); Write(W, a[i]) UNTIL i = 0
  411. END
  412. END WriteInt;
  413. PROCEDURE WriteHex* (VAR W: Writer; x: LONGINT);
  414. VAR i: INTEGER; y: LONGINT;
  415. a: ARRAY 10 OF CHAR;
  416. BEGIN i := 0; Write(W, " ");
  417. REPEAT y := x MOD 10H;
  418. IF y < 10 THEN a[i] := CHR(y + 30H) ELSE a[i] := CHR(y + 37H) END;
  419. x := x DIV 10H; INC(i)
  420. UNTIL i = 8;
  421. REPEAT DEC(i); Write(W, a[i]) UNTIL i = 0
  422. END WriteHex;
  423. PROCEDURE WriteReal* (VAR W: Writer; x: REAL; n: INTEGER);
  424. VAR e, i, k, m: INTEGER;
  425. d: ARRAY 16 OF CHAR;
  426. BEGIN e := ASR(ORD(x), 23) MOD 100H; (*binary exponent*)
  427. IF e = 0 THEN
  428. WriteString(W, " 0 ");
  429. WHILE n >= 3 DO Write(W, " "); DEC(n) END
  430. ELSIF e = 255 THEN WriteString(W, " NaN ")
  431. ELSE Write(W, " ");
  432. WHILE n >= 15 DO DEC(n); Write(W, " ") END ;
  433. (* 2 < n < 9 digits to be written*)
  434. IF x < 0.0 THEN Write(W, "-"); x := -x ELSE Write(W, " ") END ;
  435. e := (e - 127) * 77 DIV 256 - 6; (*decimal exponent*)
  436. IF e >= 0 THEN x := x / Ten(e) ELSE x := Ten(-e) * x END ;
  437. m := FLOOR(x + 0.5);
  438. IF m >= 10000000 THEN INC(e); m := m DIV 10 END ;
  439. i := 0; k := 13-n;
  440. REPEAT
  441. IF i = k THEN INC(m, 5) END ; (*rounding*)
  442. d[i] := CHR(m MOD 10 + 30H); m := m DIV 10; INC(i)
  443. UNTIL m = 0;
  444. DEC(i); Write(W, d[i]); Write(W, ".");
  445. IF i < n-7 THEN n := 0 ELSE n := 14 - n END ;
  446. WHILE i > n DO DEC(i); Write(W, d[i]) END ;
  447. Write(W, "E"); INC(e, 6);
  448. IF e < 0 THEN Write(W, "-"); e := -e ELSE Write(W, "+") END ;
  449. Write(W, CHR(e DIV 10 + 30H)); Write(W, CHR(e MOD 10 + 30H))
  450. END
  451. END WriteReal;
  452. PROCEDURE WriteRealFix* (VAR W: Writer; x: REAL; n, k: INTEGER);
  453. VAR i, m: INTEGER; neg: BOOLEAN;
  454. d: ARRAY 12 OF CHAR;
  455. BEGIN
  456. IF x = 0.0 THEN WriteString(W, " 0")
  457. ELSE
  458. IF x < 0.0 THEN x := -x; neg := TRUE ELSE neg := FALSE END ;
  459. IF k > 7 THEN k := 7 END ;
  460. x := Ten(k) * x; m := FLOOR(x + 0.5);
  461. i := 0;
  462. REPEAT d[i] := CHR(m MOD 10 + 30H); m := m DIV 10; INC(i) UNTIL m = 0;
  463. Write(W, " ");
  464. WHILE n > i+3 DO Write(W, " "); DEC(n) END ;
  465. IF neg THEN Write(W, "-"); DEC(n) ELSE Write(W, " ") END ;
  466. WHILE i > k DO DEC(i); Write(W, d[i]) END ;
  467. Write(W, ".");
  468. WHILE k > i DO DEC(k); Write(W, "0") END ;
  469. WHILE i > 0 DO DEC(i); Write(W, d[i]) END
  470. END
  471. END WriteRealFix;
  472. PROCEDURE WritePair(VAR W: Writer; ch: CHAR; x: LONGINT);
  473. BEGIN Write(W, ch);
  474. Write(W, CHR(x DIV 10 + 30H)); Write(W, CHR(x MOD 10 + 30H))
  475. END WritePair;
  476. PROCEDURE WriteClock* (VAR W: Writer; d: LONGINT);
  477. BEGIN
  478. WritePair(W, " ", d DIV 20000H MOD 20H); (*day*)
  479. WritePair(W, ".", d DIV 400000H MOD 10H); (*month*)
  480. WritePair(W, ".", d DIV 4000000H MOD 40H); (*year*)
  481. WritePair(W, " ", d DIV 1000H MOD 20H); (*hour*)
  482. WritePair(W, ":", d DIV 40H MOD 40H); (*min*)
  483. WritePair(W, ":", d MOD 40H) (*sec*)
  484. END WriteClock;
  485. BEGIN TrailerFile := Files.New("")
  486. END Texts.