/src/lib/io/low_level/input_stream_tools.e

http://github.com/tybor/Liberty · Specman e · 659 lines · 512 code · 34 blank · 113 comment · 41 complexity · 4813f3ce235c3b37e7676360dc792c61 MD5 · raw file

  1. -- This file is part of a Liberty Eiffel library.
  2. -- See the full copyright at the end.
  3. --
  4. deferred class INPUT_STREAM_TOOLS
  5. feature {ANY}
  6. read_character
  7. -- If `read_character' fail, `end_of_input' is set. Else, use `last_character'.
  8. -- Read tutorial or io cluster documentation to learn the `read_character' usage pattern.
  9. require
  10. is_connected
  11. not is_filtered and then can_read_character
  12. deferred
  13. ensure
  14. not end_of_input implies valid_last_character
  15. end
  16. read_line_in (buffer: STRING)
  17. -- Same job as `read_line' but storage is directly done in `buffer'.
  18. -- Read tutorial or io cluster documentation to learn the `read_line' usage pattern.
  19. require
  20. is_connected
  21. not is_filtered and then can_read_line
  22. buffer /= Void
  23. deferred
  24. end
  25. read_available_in (buffer: STRING; limit: INTEGER)
  26. -- Same job as `read_available' but storage is directly done in `buffer'.
  27. -- The usage pattern is the same as for `read_line'.
  28. require
  29. is_connected
  30. not is_filtered
  31. buffer /= Void
  32. limit > 0
  33. deferred
  34. end
  35. unread_character
  36. require
  37. is_connected
  38. not is_filtered and then can_unread_character
  39. deferred
  40. end
  41. last_character: CHARACTER
  42. require
  43. is_connected
  44. not end_of_input
  45. not is_filtered and then valid_last_character
  46. deferred
  47. end
  48. can_read_character: BOOLEAN
  49. -- Note that this state is usually temporary. Usually it is called until either it becomes True or
  50. -- the stream is disconnected:
  51. --
  52. -- from
  53. -- until
  54. -- stream.can_read_character or else not stream.is_connected
  55. -- loop
  56. -- -- some "still waiting..." code, maybe a sandglass?
  57. -- end
  58. -- if stream.is_connected then
  59. -- stream.read_character
  60. -- end
  61. --
  62. -- See also: `is_connected', `end_of_input'
  63. require
  64. is_connected
  65. deferred
  66. ensure
  67. Result implies not end_of_input
  68. end
  69. can_read_line: BOOLEAN
  70. require
  71. is_connected
  72. deferred
  73. end
  74. can_unread_character: BOOLEAN
  75. require
  76. is_connected
  77. valid_last_character
  78. deferred
  79. end
  80. valid_last_character: BOOLEAN
  81. require
  82. is_connected
  83. deferred
  84. end
  85. end_of_input: BOOLEAN
  86. -- `end_of_input' means the previous attempt in character reading failed because the end has been
  87. -- reached. So `last_character' is not valid and you are not allowed to do any read attempt
  88. -- anymore.
  89. --
  90. -- '''Notes:'''
  91. -- * Just after a successful connect, `end_of_input' is always False because you did not yet read
  92. -- anything).
  93. -- * Once the end of input has been reached, this is definitive in that no more characters will ever
  94. -- be provided (but of course you can still come back if `can_unread_character'; `end_of_input'
  95. -- will be changed if you `unread_character'). In that, it is different from `can_read_character'
  96. -- which is only a temporary state.
  97. --
  98. -- Please refer to the Liberty Eiffel FAQ or tutorial/io examples.
  99. deferred
  100. end
  101. is_filtered: BOOLEAN
  102. deferred
  103. end
  104. is_connected: BOOLEAN
  105. deferred
  106. end
  107. feature {ANY} -- Skipping separators:
  108. skip_separators
  109. -- Skip all separators (see `is_separator' of class CHARACTER) and make the first non-separator
  110. -- available in `last_character'. This non-separator character is pushed back into the stream (see
  111. -- `unread_character') to be available one more time (the next `read_character' will consider this
  112. -- non-separator). When `end_of_input' occurs, this process is automatically stopped.
  113. require
  114. is_connected
  115. not is_filtered
  116. do
  117. from
  118. read_character
  119. until
  120. end_of_input or else not last_character.is_separator
  121. loop
  122. read_character
  123. end
  124. if not end_of_input then
  125. unread_character
  126. end
  127. end
  128. skip_separators_using (separators: STRING)
  129. -- Same job as `skip_separators' using the `separators' set.
  130. require
  131. is_connected
  132. not is_filtered
  133. separators /= Void
  134. do
  135. from
  136. read_character
  137. until
  138. end_of_input or else not separators.has(last_character)
  139. loop
  140. read_character
  141. end
  142. if not end_of_input and then can_unread_character then
  143. unread_character
  144. end
  145. end
  146. skip_remainder_of_line
  147. -- Skip all the remainder of the line including the end of line delimiter itself.
  148. require
  149. is_connected
  150. not is_filtered
  151. local
  152. stop: BOOLEAN
  153. do
  154. from
  155. until
  156. stop
  157. loop
  158. if end_of_input then
  159. stop := True
  160. else
  161. inspect
  162. last_character -- See for example: http://www.mrx.net/c/introduction.html
  163. when '%N' then
  164. -- UNIX format
  165. stop := True
  166. when '%R' then
  167. read_character
  168. if not end_of_input then
  169. if last_character = '%N' then
  170. -- DOS format
  171. end
  172. end
  173. stop := True
  174. else
  175. read_character
  176. end
  177. end
  178. end
  179. end
  180. feature {ANY} -- To read one number at a time:
  181. read_integer
  182. -- Read an integer according to the Eiffel syntax. Make result available in `last_integer'. Heading
  183. -- separators are automatically skipped using `is_separator' of class CHARACTER. Trailing separators
  184. -- are not skipped.
  185. require
  186. is_connected
  187. not is_filtered
  188. local
  189. state: INTEGER; sign: BOOLEAN
  190. do
  191. -- state = 0 : waiting sign or first digit.
  192. -- state = 1 : sign read, waiting first digit.
  193. -- state = 2 : in the number.
  194. -- state = 3 : end state.
  195. -- state = 4 : error state.
  196. from
  197. until
  198. state > 2
  199. loop
  200. read_character
  201. if end_of_input then
  202. inspect
  203. state
  204. when 0 .. 1 then
  205. state := 4
  206. when 2 then
  207. state := 3
  208. -- no else, because state > 2 is impossible
  209. end
  210. else
  211. inspect
  212. state
  213. when 0 then
  214. if last_character.is_separator then
  215. elseif last_character.is_digit then
  216. last_integer := last_character.value
  217. state := 2
  218. elseif last_character = '-' then
  219. sign := True
  220. state := 1
  221. elseif last_character = '+' then
  222. state := 1
  223. else
  224. state := 4
  225. end
  226. when 1 then
  227. if last_character.is_separator then
  228. elseif last_character.is_digit then
  229. last_integer := last_character.value
  230. state := 2
  231. else
  232. state := 4
  233. end
  234. else
  235. if last_character.is_digit then
  236. last_integer := last_integer * 10 + last_character.value
  237. else
  238. state := 3
  239. end
  240. end
  241. end
  242. end
  243. if can_unread_character and then not end_of_input then
  244. unread_character
  245. end
  246. valid_last_integer := state /= 4
  247. if sign then
  248. last_integer := -last_integer
  249. end
  250. ensure
  251. old end_of_input implies end_of_input and then not valid_last_integer
  252. end
  253. last_integer: INTEGER
  254. -- Last integer read using `read_integer'.
  255. valid_last_integer: BOOLEAN
  256. -- Was the last call to `read_integer' successful ?
  257. last_real: REAL
  258. -- Last real read with `read_real'.
  259. valid_last_real: BOOLEAN
  260. -- Was the last call to `read_real' successful ?
  261. read_real
  262. -- Read a REAL and make the result available in `last_real'.
  263. require
  264. is_connected
  265. not is_filtered
  266. local
  267. state: INTEGER; sign: BOOLEAN
  268. do
  269. -- state = 0 : waiting sign or first digit.
  270. -- state = 1 : sign read, waiting first digit.
  271. -- state = 2 : in the integral part.
  272. -- state = 3 : in the fractional part.
  273. -- state = 4 : read 'E' or 'e' for scientific notation
  274. -- state = 5 : read sign of exponent
  275. -- state = 6 : in exponent
  276. -- state = 7 : end state.
  277. -- state = 8 : error state.
  278. from
  279. last_string.clear_count
  280. until
  281. state >= 7
  282. loop
  283. read_character
  284. if end_of_input then
  285. inspect
  286. state
  287. when 2, 3, 6, 7 then
  288. state := 7
  289. else
  290. state := 8
  291. end
  292. else
  293. inspect
  294. state
  295. when 0 then
  296. if last_character.is_separator then
  297. elseif last_character.is_digit then
  298. last_string.add_last(last_character)
  299. state := 2
  300. elseif last_character = '-' then
  301. sign := True
  302. state := 1
  303. elseif last_character = '+' then
  304. state := 1
  305. elseif last_character = '.' then
  306. last_string.add_last(last_character)
  307. state := 3
  308. else
  309. state := 8
  310. end
  311. when 1 then
  312. if last_character.is_separator then
  313. elseif last_character.is_digit then
  314. last_string.add_last(last_character)
  315. state := 2
  316. elseif last_character = '.' then
  317. last_string.add_last(last_character)
  318. state := 3
  319. else
  320. state := 8
  321. end
  322. when 2 then
  323. if last_character.is_digit then
  324. last_string.add_last(last_character)
  325. elseif last_character = '.' then
  326. last_string.add_last(last_character)
  327. state := 3
  328. elseif (once "eE").has(last_character) then
  329. last_string.add_last(last_character)
  330. state := 4
  331. else
  332. state := 7
  333. end
  334. when 3 then
  335. if last_character.is_digit then
  336. last_string.add_last(last_character)
  337. elseif (once "eE").has(last_character) then
  338. last_string.add_last(last_character)
  339. state := 4
  340. else
  341. state := 7
  342. end
  343. when 4 then
  344. if last_character.is_separator then
  345. state := 8
  346. elseif last_character.is_digit then
  347. last_string.add_last(last_character)
  348. state := 6
  349. elseif (once "+-").has(last_character) then
  350. last_string.add_last(last_character)
  351. state := 5
  352. else
  353. state := 8
  354. end
  355. when 5 then
  356. if last_character.is_digit then
  357. last_string.add_last(last_character)
  358. state := 6
  359. else
  360. state := 8
  361. end
  362. when 6 then
  363. if last_character.is_digit then
  364. last_string.add_last(last_character)
  365. else
  366. state := 7
  367. end
  368. end
  369. end
  370. end
  371. if not end_of_input and then can_unread_character then
  372. unread_character
  373. end
  374. valid_last_real := state /= 8
  375. if last_string.count > 0 then
  376. -- Wouldn't "if valid_last_real then" be better ? <FM-06/07/2005>
  377. last_real := last_string.to_real
  378. else
  379. last_real := 0 -- NaN
  380. end
  381. if sign then
  382. last_real := -last_real
  383. end
  384. ensure
  385. old end_of_input implies end_of_input and then not valid_last_real
  386. end
  387. feature {ANY} -- To read one line or one word at a time:
  388. last_string: STRING
  389. -- Access to the unique common buffer to get for example the result computed by `read_line',
  390. -- `read_word', `newline', etc. This is a once function (the same common buffer is used for all
  391. -- streams).
  392. once
  393. create Result.make(1024)
  394. end
  395. read_line
  396. -- Read a complete line ended by '%N' or `end_of_input'. Make the result available in `last_string'
  397. -- common buffer. The end of line character (usually '%N') is not added in the `last_string' buffer.
  398. -- Read tutorial or io cluster documentation to learn the read_line usage pattern.
  399. --
  400. -- See also `read_line_in'.
  401. require
  402. is_connected
  403. not is_filtered
  404. do
  405. last_string.clear_count
  406. read_line_in(last_string)
  407. end
  408. read_available (limit: INTEGER)
  409. -- Read as many characters as possible, as long as the stream does not block and up to the given limit.
  410. --
  411. -- See also `read_available_in'.
  412. require
  413. is_connected
  414. not is_filtered
  415. do
  416. last_string.clear_count
  417. read_available_in(last_string, limit)
  418. end
  419. read_word
  420. -- Read a word using `is_separator' of class CHARACTER. The result is available in the `last_string'
  421. -- common buffer. Heading separators are automatically skipped. Trailing separators are not skipped
  422. -- (`last_character' is left on the first one). If `end_of_input' is encountered, Result can be the
  423. -- empty string.
  424. require
  425. is_connected
  426. not is_filtered
  427. do
  428. skip_separators
  429. if not end_of_input then
  430. read_character
  431. end
  432. from
  433. last_string.clear_count
  434. until
  435. end_of_input or else last_character.is_separator
  436. loop
  437. last_string.extend(last_character)
  438. read_character
  439. end
  440. end
  441. newline
  442. -- Consume input until newline ('%N') is found. The corresponding STRING is stored in `last_string'
  443. -- common buffer.
  444. require
  445. is_connected
  446. not is_filtered
  447. local
  448. stop: BOOLEAN
  449. do
  450. from
  451. last_string.clear_count
  452. stop := end_of_input
  453. until
  454. stop
  455. loop
  456. read_character
  457. if end_of_input or else last_character = '%N' then
  458. stop := True
  459. else
  460. last_string.extend(last_character)
  461. end
  462. end
  463. end
  464. reach_and_skip (keyword: STRING)
  465. -- Try to skip enough characters in order to reach the `keyword' which is skipped too. If the
  466. -- `keyword' is not in the remainder of this stream, the process is stopped as soon as `end_of_input'
  467. -- occurs.
  468. require
  469. is_connected
  470. not is_filtered
  471. not keyword.is_empty
  472. local
  473. stop: BOOLEAN; i: INTEGER; first: CHARACTER
  474. do
  475. from
  476. last_string.clear_count
  477. first := keyword.first
  478. until
  479. end_of_input or else stop
  480. loop
  481. -- Reach the first character of the `keyword':
  482. from
  483. i := 2
  484. until
  485. i > last_string.count or else last_string.item(i) = first
  486. loop
  487. i := i + 1
  488. end
  489. if i <= last_string.count then
  490. last_string.remove_head(i - 1)
  491. else
  492. last_string.clear_count
  493. from
  494. if not end_of_input then
  495. read_character
  496. end
  497. until
  498. end_of_input or else last_character = first
  499. loop
  500. read_character
  501. end
  502. last_string.extend(first)
  503. end
  504. check
  505. not end_of_input implies last_string.item(1) = first
  506. last_string.count <= keyword.count
  507. end
  508. -- Now we need as many characters as in `keyword':
  509. if not (end_of_input or else last_string.count = keyword.count) then
  510. from
  511. read_character
  512. until
  513. end_of_input or else last_string.count = keyword.count - 1
  514. loop
  515. last_string.extend(last_character)
  516. read_character
  517. end
  518. if not end_of_input then
  519. last_string.extend(last_character)
  520. end
  521. end
  522. stop := last_string.is_equal(keyword)
  523. end
  524. if not end_of_input then
  525. read_character
  526. unread_character
  527. end
  528. ensure
  529. not end_of_input implies last_string.is_equal(keyword)
  530. end
  531. feature {ANY} -- Other features:
  532. read_word_using (separators: STRING)
  533. -- Same job as `read_word' using `separators'.
  534. require
  535. is_connected
  536. not is_filtered
  537. separators /= Void
  538. do
  539. skip_separators_using(separators)
  540. if not end_of_input then
  541. read_character
  542. end
  543. from
  544. last_string.clear_count
  545. until
  546. end_of_input or else separators.has(last_character)
  547. loop
  548. last_string.extend(last_character)
  549. read_character
  550. end
  551. end
  552. read_tail_in (str: STRING)
  553. -- Read all remaining character of the stream in `str'.
  554. require
  555. is_connected
  556. not is_filtered
  557. str /= Void
  558. do
  559. from
  560. until
  561. end_of_input
  562. loop
  563. read_character
  564. if not end_of_input then
  565. str.extend(last_character)
  566. end
  567. end
  568. ensure
  569. end_of_input
  570. end
  571. feature {}
  572. io_getc (stream: POINTER): INTEGER
  573. external "plug_in"
  574. alias "{
  575. location: "${sys}/plugins"
  576. module_name: "io"
  577. feature_name: "io_getc"
  578. }"
  579. end
  580. io_ungetc (byte: CHARACTER; stream: POINTER)
  581. external "plug_in"
  582. alias "{
  583. location: "${sys}/plugins"
  584. module_name: "io"
  585. feature_name: "io_ungetc"
  586. }"
  587. end
  588. io_fread (buf: NATIVE_ARRAY[CHARACTER]; size: INTEGER; stream: POINTER): INTEGER
  589. -- return size read or 0 if end of input (-1 on error => exception ?)
  590. external "plug_in"
  591. alias "{
  592. location: "${sys}/plugins"
  593. module_name: "io"
  594. feature_name: "io_fread"
  595. }"
  596. end
  597. eof: INTEGER
  598. external "plug_in"
  599. alias "{
  600. location: "${sys}/plugins"
  601. module_name: "io"
  602. feature_name: "EOF"
  603. }"
  604. end
  605. end -- class INPUT_STREAM_TOOLS
  606. --
  607. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  608. --
  609. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  610. -- of this software and associated documentation files (the "Software"), to deal
  611. -- in the Software without restriction, including without limitation the rights
  612. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  613. -- copies of the Software, and to permit persons to whom the Software is
  614. -- furnished to do so, subject to the following conditions:
  615. --
  616. -- The above copyright notice and this permission notice shall be included in
  617. -- all copies or substantial portions of the Software.
  618. --
  619. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  620. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  621. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  622. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  623. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  624. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  625. -- THE SOFTWARE.