In.Mod 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. MODULE In;
  2. (** Module In provides a set of basic routines for formatted input of
  3. characters, character sequences, numbers, and names. It assumes a standard
  4. input stream with a current position that can be reset to the beginning of
  5. the stream (but may not always do so on Linux/Unix or Windows).
  6. Module In as in Oakwood Guidlines for Oberon-2 Compiler Developers, 1995.
  7. With the following changes:
  8. LongInt, Int, Int16 read and parse minus signs first.
  9. Char reads a 2-byte character. On Linux it decodes input as UTF-8.
  10. On Windows it uses ReadConsoleW WinAPI call if console is attached,
  11. otherwise ReadFile is used and input is decoded from UTF-8.
  12. Open may not rewind.
  13. Extra procedures: Line, HugeInt, Int16.
  14. %RU Модуль In предоставляет набор основных процедур для форматированного ввода
  15. литер, последовательностей литер, чисел и имён. Он предполагает наличие
  16. стандартного потока ввода с текущей позицией, которая может быть сброшена
  17. в начало потока (но не всегда это возможно в Linux/Unix или Windows).
  18. Модуль In как в Oakwood Guidlines for Oberon-2 Compiler Developers, 1995.
  19. Со следующими изменениями:
  20. LongInt, Int, Int16 сначала читают и распознают знаки минус.
  21. Char читает 2-байтовую литеру. В Linux он декодирует вводимые данные как
  22. UTF-8. В Windows используется вызов ReadConsoleW из WinAPI если консоль
  23. подключена, в противном случае используется ReadFile и ввод автоматически
  24. декодируется из UTF-8.
  25. Open не может перематывать.
  26. Дополнительные процедуры: Line, HugeInt, Int16. *)
  27. IMPORT Platform, SYSTEM, Reals, Out, Utf8;
  28. CONST
  29. pending = 0; (* readState when at start of input or end of line.
  30. Implies nextch undefined. *)
  31. ready = 1; (* readState when nextch is defined and contains
  32. next character on current line. *)
  33. eof = 2; (* readState when at end of file. *)
  34. (** Codepages, values of cp
  35. %RU Кодовые страницы, значения cp **)
  36. singleByte = 1;
  37. utf8 = 2; (*!TODO also add UTF16 *)
  38. TYPE
  39. SBYTE = BYTE;
  40. BYTE* = UBYTE; (** 8-bit unsigned integer, 0..255
  41. %RU 8-битное беззнаковое целое, 0..255 *)
  42. VAR
  43. (** TRUE after every Open, FALSE after the first error.
  44. Done indicates the success of an input operation. If Done is TRUE after
  45. an input operation, the operation was successful and its result is
  46. valid. An unsuccessful input operation sets Done to FALSE; it remains
  47. FALSE until the next call to Open. In particular, Done is set to FALSE
  48. if an attempt is made to read beyond the end of the input stream.
  49. %RU TRUE после каждого Open, FALSE после первой ошибки.
  50. Done выражает успешность операции ввода. Если Done равно TRUE после
  51. операции ввода, то операция была успешной и её результат считается
  52. действительным. Неудачная операция ввода устанавливает значение Done в
  53. FALSE; оно остается FALSE до следующего вызова Open. В частности, Done
  54. устанавливается в FALSE, если была предпринята попытка чтения за пределы
  55. конца входного потока. *)
  56. Done-: BOOLEAN;
  57. nextch: CHAR; (** Maintains 1 character read ahaead except at end of line *)
  58. readState: INTEGER;
  59. cp: INTEGER; (** Input Code Page *)
  60. (** Initialization
  61. %RU Инициализация **)
  62. (** Open sets the current position to the beginning of the input stream.
  63. Done indicates if the operation was successful.
  64. Note that on Windows or Linux/Unix rewind may not be possible. Also on
  65. these OS Open is not strictly required before any other operation.
  66. %RU Open устанавливает текущую позицию в начало входного потока.
  67. Done указывает, была ли операция успешной.
  68. Обратите внимание, что на Windows или Linux/Unix перемотка может быть
  69. невозможна. Кроме того, на этих ОС нет строгого требования вызывать Open
  70. перед любой другой операцией. *)
  71. PROCEDURE Open*;
  72. VAR error: Platform.ErrorCode;
  73. BEGIN
  74. (* Rewind STDIN to beginning of file. *)
  75. error := Platform.Seek(Platform.StdIn, 0, Platform.SeekSet);
  76. cp := utf8;
  77. nextch := 0X;
  78. readState := pending;
  79. Done := error = 0
  80. END Open;
  81. (** Input operations
  82. %RU Операции ввода **)
  83. (** The following operations require Done = TRUE and guarantee (Done = TRUE
  84. and the result is valid) or (Done = FALSE). All operations except Char
  85. skip leading blanks, tabs or end-of-line characters. *)
  86. PROCEDURE GetByte(): INTEGER;
  87. VAR error: Platform.ErrorCode; x, n: INTEGER;
  88. m: ARRAY 1 OF SBYTE;
  89. BEGIN
  90. error := Platform.ReadBuf(Platform.StdIn, m, n); x := m[0] MOD 256;
  91. IF (error = 0) & (n = 1) THEN readState := ready
  92. ELSE readState := eof; x := 0
  93. END;
  94. RETURN x
  95. END GetByte;
  96. PROCEDURE GetChar(VAR x: CHAR): BOOLEAN;
  97. VAR error: Platform.ErrorCode; n: INTEGER; ok: BOOLEAN;
  98. m: ARRAY 1 OF CHAR;
  99. BEGIN
  100. error := Platform.ReadBufW(Platform.StdIn, m, n); x := m[0];
  101. IF (error = 0) & (n = 1) THEN readState := ready; ok := TRUE
  102. ELSE readState := eof; x := 0X; ok := FALSE
  103. END ;
  104. RETURN ok END GetChar;
  105. (** Puts in `x` the byte at the current position
  106. %RU Помещает в `x` байт в текущей позиции *)
  107. PROCEDURE Byte*(VAR x: BYTE);
  108. BEGIN x := SYSTEM.VAL(BYTE, SHORT(SHORT(GetByte())))
  109. END Byte;
  110. PROCEDURE ReadChar;
  111. VAR x, y: INTEGER;
  112. c: CHAR;
  113. BEGIN
  114. IF GetChar(c) THEN nextch := c
  115. ELSE x := GetByte();
  116. IF readState = ready THEN
  117. IF cp = utf8 THEN
  118. IF x >= 80H THEN y := GetByte() MOD 64; (* Not 1 byte *)
  119. IF x DIV 32 = 6 THEN (* 2 bytes *)
  120. x := x MOD 32 * 64 + y
  121. ELSIF y DIV 16 = 14 THEN (* 3 bytes *)
  122. x := (x MOD 16 * 64 + y) * 64 + GetByte() MOD 64
  123. ELSIF y DIV 8 = 30 THEN (* 4 bytes *)
  124. x := ((x MOD 8 * 64 + y) * 64 + GetByte() MOD 64) * 64 + GetByte() MOD 64
  125. ELSE x := 0
  126. END
  127. END
  128. END;
  129. nextch := CHR(x)
  130. END
  131. END
  132. END ReadChar;
  133. PROCEDURE StartRead; (* Ensure either nextch is valid or we are at EOF. *)
  134. BEGIN Out.Flush; IF readState = pending THEN ReadChar END
  135. END StartRead;
  136. PROCEDURE StartAndSkip; (* Like StartRead, but also skip over blanks, CR, LF, tab. *)
  137. BEGIN StartRead;
  138. WHILE (readState = ready) & (nextch <= ' ') DO ReadChar END
  139. END StartAndSkip;
  140. (** Puts in `ch` the character at the current position. May read 1 to 4 bytes
  141. if decoding from UTF-8 (on Linux/Unix and on Windows if input
  142. is redirected).
  143. %RU Помещает в `ch` литеру в текущей позиции. Может считывать от 1 до 4 байт
  144. при декодировании из UTF-8 (в Linux/Unix и в Windows, если ввод
  145. перенаправляется). *)
  146. PROCEDURE Char*(VAR ch: CHAR);
  147. BEGIN StartRead;
  148. IF readState = ready THEN ch := nextch;
  149. IF ch = 0AX THEN readState := pending ELSE ReadChar END
  150. ELSE Done := FALSE; ch := 0X
  151. END
  152. END Char;
  153. (** Returns 64-bit integer at the current position according to the format:
  154. IntConst = [-] (digit {digit} | digit {hexDigit} "H").
  155. %RU Возвращает 64-битное целое число в текущей позиции согласно формату:
  156. IntConst = [-] (digit {digit} | digit {hexDigit} "H"). *)
  157. PROCEDURE HugeInt*(VAR h: LONGINT);
  158. VAR ok, neg, hex, endofnum: BOOLEAN;
  159. decacc, hexacc, digit: LONGINT;
  160. BEGIN StartAndSkip;
  161. ok := FALSE;
  162. IF readState = ready THEN
  163. neg := nextch = '-'; IF neg THEN ReadChar END;
  164. hex := FALSE;
  165. endofnum := FALSE;
  166. decacc := 0;
  167. hexacc := 0;
  168. WHILE (readState = ready) & ~endofnum DO
  169. digit := -1;
  170. IF (nextch >= '0') & (nextch <= '9') THEN
  171. digit := ORD(nextch) MOD 16
  172. ELSIF (nextch >= 'a') & (nextch <= 'f') OR
  173. (nextch >= 'A') & (nextch <= 'F') THEN
  174. digit := ORD(nextch) MOD 16 + 9; hex := TRUE
  175. END;
  176. IF digit >= 0 THEN
  177. ok := TRUE;
  178. decacc := decacc * 10 + digit;
  179. hexacc := hexacc * 16 + digit;
  180. ReadChar
  181. ELSIF nextch = 'H' THEN
  182. hex := TRUE; endofnum := TRUE; ReadChar
  183. ELSE
  184. endofnum := TRUE
  185. END
  186. END;
  187. IF ok THEN
  188. IF hex THEN h := hexacc ELSE h := decacc END;
  189. IF neg THEN h := -h END
  190. ELSE h := 0
  191. END
  192. END;
  193. WHILE (readState = ready) & (nextch <= ' ') & (nextch # 0AX) DO ReadChar END;
  194. IF (readState = ready) & (nextch = 0AX) THEN readState := pending END;
  195. IF ~ok THEN Done := FALSE END
  196. END HugeInt;
  197. (** Returns 16-bit integer in the same way as HugeInt does
  198. %RU Возвращает 16-битное целое число так же, как это делает HugeInt *)
  199. PROCEDURE Int16*(VAR i: SHORTINT);
  200. VAR h: LONGINT;
  201. BEGIN HugeInt(h); i := SHORT(SHORT(h)) (*!FIXME check range, update Done*)
  202. END Int16;
  203. (** Returns 32-bit integer in the same way as HugeInt does
  204. %RU Возвращает 32-битное целое число так же, как это делает HugeInt *)
  205. PROCEDURE Int*(VAR i: INTEGER); (*32-bit INTEGER alias*)
  206. VAR h: LONGINT;
  207. BEGIN HugeInt(h); i := SHORT(h) (*!FIXME check range, update Done*)
  208. END Int;
  209. (** Alias for Int. Does the same thing
  210. %RU Псевдоним для Int. Делает то же самое *)
  211. PROCEDURE LongInt*(VAR i: INTEGER);
  212. BEGIN Int(i)
  213. END LongInt;
  214. (** Reads a line of characters until CR, LF or end of file
  215. %RU Считывает строку литер до первого CR, LF или конца файла *)
  216. PROCEDURE Line*(VAR line: ARRAY OF CHAR);
  217. VAR i: INTEGER;
  218. BEGIN StartRead; i := 0;
  219. IF readState # ready THEN Done := FALSE END;
  220. WHILE (readState = ready) & (nextch # 0DX) & (nextch # 0AX) DO
  221. IF i < LEN(line) - 1 THEN line[i] := nextch; INC(i) ELSE Done := FALSE END;
  222. ReadChar
  223. END;
  224. line[i] := 0X;
  225. IF (readState = ready) & (nextch = 0DX) THEN ReadChar END;
  226. IF (readState = ready) & (nextch = 0AX) THEN readState := pending END
  227. END Line;
  228. (** Reads a word and put it in `s`.
  229. Skips whitespaces, reads characters until the next whitespace
  230. and puts the read word in `s`, then skips whitespaces until the next
  231. non-whitespace or a new line character. Skips the new line character
  232. %RU Читает слово и помещает его в `s`.
  233. Пропускает пробельные литеры, считывает литеры до следующей пробельной
  234. литеры и помещает прочитанное слово в `s`, затем пропускает литеры до
  235. следующей непробельной литеры или литеры новой строки.
  236. Пропускает символ новой строки *)
  237. PROCEDURE Word*(VAR s: ARRAY OF CHAR);
  238. VAR i: INTEGER;
  239. BEGIN StartRead; i := 0;
  240. IF readState # ready THEN Done := FALSE END;
  241. WHILE (readState = ready) & (nextch > ' ') DO
  242. IF i < LEN(s) - 1 THEN s[i] := nextch; INC(i) ELSE Done := FALSE END;
  243. ReadChar
  244. END;
  245. s[i] := 0X;
  246. WHILE (readState = ready) & (nextch <= ' ') & (nextch # 0AX) DO ReadChar END;
  247. IF (readState = ready) & (nextch = 0AX) THEN readState := pending END
  248. END Word;
  249. (** Reads a string literal and puts it in `s`.
  250. A string literal is a quoted sequence of characters that may include
  251. spaces but not other whitespaces (tabs, new lines etc.). The quotes can
  252. be single or double quotes. The string must begin and end with the same
  253. quotation marks.
  254. %RU Считывает строковый литерал и помещает его в `s`.
  255. Строковый литерал -- это заключенная в кавычки последовательность литер,
  256. которая может включать в себя пробелы, но не другие пробельные литеры
  257. (табуляции, новые строки и т. п.). Кавычки могут быть одинарные или
  258. двойные. Строка должна начинаться и заканчиваться одинаковыми кавычками. *)
  259. PROCEDURE String*(VAR s: ARRAY OF CHAR);
  260. VAR i: INTEGER;
  261. q: CHAR; (* ' or " *)
  262. BEGIN StartAndSkip; i := 0;
  263. IF (readState = ready) & ((nextch = '"') OR (nextch = "'")) THEN
  264. q := nextch; ReadChar;
  265. WHILE (readState = ready) & (nextch >= ' ') & (nextch # q) DO
  266. IF i < LEN(s) - 1 THEN s[i] := nextch; INC(i) ELSE Done := FALSE END;
  267. ReadChar
  268. END;
  269. IF (readState = ready) & (nextch = q) THEN ReadChar
  270. ELSE Done := FALSE
  271. END
  272. END;
  273. s[i] := 0X
  274. END String;
  275. (** Reads the name `s` at the current position according to the file name
  276. format of the operating system (e.g. "lib/My.Mod" under Unix).
  277. Skips the 0AX in the end (if any).
  278. %RU Считывает имя `s` в текущей позиции в соответствии с форматом имени файла
  279. операционной системы (например, "lib/My.Mod" под Unix).
  280. Пропускает 0AX в конце (если есть). *)
  281. PROCEDURE Name*(VAR s: ARRAY OF CHAR);
  282. VAR c: CHAR;
  283. i: INTEGER;
  284. BEGIN i := 0; Char(c);
  285. WHILE c > ' ' DO
  286. IF i < LEN(s) - 1 THEN s[i] := c; INC(i) ELSE Done := FALSE END;
  287. Char(c)
  288. END;
  289. s[i] := 0X;
  290. IF c = 0AX THEN Char(c) END
  291. END Name;
  292. (** Reads and puts in `x` a 32-bit real number (REAL) in format:
  293. ["-"] digit {digit} [{digit} ["E" ("+" | "-") digit {digit}]].
  294. %RU Считывает и помещает в `x` 32-битное вещественное число (REAL)
  295. согласно формату:
  296. ["-"] digit {digit} [{digit} ["E" ("+" | "-") digit {digit}]]. *)
  297. PROCEDURE Real*(VAR x: SHORTREAL);
  298. VAR s: ARRAY 16 OF CHAR;
  299. BEGIN StartAndSkip; Word(s);
  300. x := Reals.Val(s);
  301. IF ~Reals.Done THEN Done := FALSE END
  302. END Real;
  303. (** Reads and puts in `x` a 64-bit real number (LONGREAL) in format:
  304. ["-"] digit {digit} [{digit} ["E" ("+" | "-") digit {digit}]].
  305. %RU Считывает и помещает в `x` 64-битное вещественное число (LONGREAL)
  306. согласно формату:
  307. ["-"] digit {digit} [{digit} ["E" ("+" | "-") digit {digit}]]. *)
  308. PROCEDURE LongReal*(VAR x: REAL);
  309. VAR s: ARRAY 16 OF CHAR;
  310. BEGIN StartAndSkip; Word(s);
  311. x := Reals.LongVal(s);
  312. IF ~Reals.Done THEN Done := FALSE END
  313. END LongReal;
  314. BEGIN
  315. cp := utf8;
  316. nextch := 0X;
  317. readState := pending;
  318. Done := TRUE
  319. END In.