/src/lib/numeric/number_tools.e

http://github.com/tybor/Liberty · Specman e · 395 lines · 315 code · 24 blank · 56 comment · 28 complexity · e51b32e8ecbf444b4f94c689c31d58a9 MD5 · raw file

  1. -- This file is part of a Liberty Eiffel library.
  2. -- See the full copyright at the end.
  3. --
  4. expanded class NUMBER_TOOLS
  5. --
  6. -- This class provides abstract creation functions for NUMBERs as well as
  7. -- some other useful tools for NUMBERs.
  8. --
  9. -- Because this class is expanded, one may simply declare some entity of
  10. -- type NUMBER_TOOLS to use those NUMBER tools. One may also inherit this
  11. -- class in order to use those tools as well.
  12. --
  13. feature {ANY}
  14. from_integer (n: INTEGER): NUMBER
  15. do
  16. Result := from_integer_64(n)
  17. end
  18. from_integer_64 (n: INTEGER_64): NUMBER
  19. -- Uses value `n' to create a new NUMBER.
  20. do
  21. create {INTEGER_64_NUMBER} Result.make(n)
  22. ensure
  23. Result.to_integer_64 = n
  24. end
  25. from_string (formula: ABSTRACT_STRING): NUMBER
  26. -- Parse the contents of `formula' to create a new NUMBER. If some
  27. -- error occurs (like for example a division by zero), the `Result'
  28. -- is Void and the error report is left in the `parser_buffer'.
  29. require
  30. is_number(formula)
  31. do
  32. parser_buffer.initialize_with(formula)
  33. parser_buffer.skip_separators
  34. Result := parse_create_e0
  35. if parser_buffer.last_error /= Void then
  36. Result := Void
  37. end
  38. ensure
  39. Result /= Void xor parser_buffer.last_error /= Void
  40. end
  41. from_input_stream (input: INPUT_STREAM): NUMBER
  42. -- Create a number from a file or standard input
  43. require
  44. input.is_connected
  45. local
  46. string: STRING
  47. do
  48. if not input.end_of_input then
  49. create string.make(0)
  50. input.read_line_in(string)
  51. Result := from_string(string)
  52. end
  53. ensure
  54. Result /= Void xor parser_buffer.last_error /= Void
  55. end
  56. is_number (formula: ABSTRACT_STRING): BOOLEAN
  57. -- Is the `formula' a correct notation to create a NUMBER ?
  58. -- Actually, any correct `formula' using a combination of literal
  59. -- integer constants with + - * / () and ! is a correct notation to
  60. -- create a NUMBER. Traditional priority rules are used for
  61. -- operators and the ! character denote the factorial computation.
  62. -- Here is the BNF grammar used:
  63. --
  64. -- E0 = E1 R1
  65. -- E1 = E2 R2
  66. -- E2 = E3 R3
  67. -- E3 = "+" E3 | "-" E3 | "(" E0 ")" | "constant"
  68. -- R1 = "+" E1 R1 | "-" E1 R1 | ^
  69. -- R2 = "*" E2 R2 | "/" E2 R2 | ^
  70. -- R3 = "!" | ^
  71. require
  72. not formula.is_empty
  73. do
  74. parser_buffer.initialize_with(formula)
  75. parser_buffer.skip_separators
  76. Result := parse_e0
  77. if Result then
  78. if parser_buffer.current_index /= formula.count + 1 then
  79. Result := False
  80. parser_buffer.set_last_error_message(once "End of text expected.")
  81. end
  82. end
  83. ensure
  84. Result xor parser_buffer.last_error /= Void
  85. end
  86. parser_buffer: MINI_PARSER_BUFFER
  87. -- This once function gives access to the unique `parser_buffer' to
  88. -- allow the memorization of the `Current' position and the
  89. -- memorization of the last error message.
  90. once
  91. create Result
  92. end
  93. feature {}
  94. parse_e0: BOOLEAN
  95. do
  96. Result := parse_e1 and then parse_r1
  97. end
  98. parse_e1: BOOLEAN
  99. do
  100. Result := parse_e2 and then parse_r2
  101. end
  102. parse_e2: BOOLEAN
  103. do
  104. Result := parse_e3
  105. parse_r3
  106. end
  107. parse_e3: BOOLEAN
  108. do
  109. if parser_buffer.end_reached then
  110. parser_buffer.set_last_error_message(Integer_expected)
  111. else
  112. inspect
  113. parser_buffer.current_character
  114. when '+', '-' then
  115. parser_buffer.next
  116. parser_buffer.skip_separators
  117. Result := parse_e3
  118. when '(' then
  119. parser_buffer.next
  120. parser_buffer.skip_separators
  121. Result := parse_e0
  122. if Result then
  123. if parser_buffer.end_reached or else parser_buffer.current_character /= ')' then
  124. Result := False
  125. parser_buffer.set_last_error_message(Integer_expected)
  126. else
  127. parser_buffer.next
  128. parser_buffer.skip_separators
  129. end
  130. end
  131. else
  132. Result := parse_constant
  133. end
  134. end
  135. end
  136. parse_r1: BOOLEAN
  137. do
  138. if parser_buffer.end_reached then
  139. Result := True
  140. else
  141. inspect
  142. parser_buffer.current_character
  143. when '+', '-' then
  144. parser_buffer.next
  145. parser_buffer.skip_separators
  146. Result := parse_e1 and then parse_r1
  147. else
  148. Result := True
  149. end
  150. end
  151. end
  152. parse_r2: BOOLEAN
  153. do
  154. if parser_buffer.end_reached then
  155. Result := True
  156. else
  157. inspect
  158. parser_buffer.current_character
  159. when '*', '/' then
  160. parser_buffer.next
  161. parser_buffer.skip_separators
  162. Result := parse_e2 and then parse_r2
  163. else
  164. Result := True
  165. end
  166. end
  167. end
  168. parse_r3
  169. do
  170. if not parser_buffer.end_reached then
  171. if parser_buffer.current_character = '!' then
  172. parser_buffer.next
  173. parser_buffer.skip_separators
  174. end
  175. end
  176. end
  177. parse_constant: BOOLEAN
  178. local
  179. stop: BOOLEAN
  180. do
  181. if parser_buffer.end_reached or else not parser_buffer.current_character.is_digit then
  182. parser_buffer.set_last_error_message(Integer_expected)
  183. else
  184. Result := True
  185. from
  186. parser_buffer.next
  187. until
  188. stop
  189. loop
  190. if parser_buffer.end_reached then
  191. stop := True
  192. elseif parser_buffer.current_character.is_digit then
  193. parser_buffer.next
  194. else
  195. stop := True
  196. end
  197. end
  198. parser_buffer.skip_separators
  199. end
  200. end
  201. parse_create_e0: NUMBER
  202. do
  203. Result := parse_create_e1
  204. Result := parse_create_r1(Result)
  205. end
  206. parse_create_e1: NUMBER
  207. do
  208. Result := parse_create_e2
  209. Result := parse_create_r2(Result)
  210. end
  211. parse_create_e2: NUMBER
  212. do
  213. Result := parse_create_e3
  214. Result := parse_create_r3(Result)
  215. end
  216. parse_create_e3: NUMBER
  217. do
  218. inspect
  219. parser_buffer.current_character
  220. when '+' then
  221. parser_buffer.next
  222. parser_buffer.skip_separators
  223. Result := parse_create_e3
  224. when '-' then
  225. parser_buffer.next
  226. parser_buffer.skip_separators
  227. Result := -parse_create_e3
  228. when '(' then
  229. parser_buffer.next
  230. parser_buffer.skip_separators
  231. Result := parse_create_e0
  232. parser_buffer.next
  233. parser_buffer.skip_separators
  234. else
  235. Result := parse_create_constant
  236. end
  237. end
  238. parse_create_r1 (left: NUMBER): NUMBER
  239. do
  240. if parser_buffer.end_reached then
  241. Result := left
  242. else
  243. inspect
  244. parser_buffer.current_character
  245. when '+' then
  246. parser_buffer.next
  247. parser_buffer.skip_separators
  248. Result := left + parse_create_e1
  249. Result := parse_create_r1(Result)
  250. when '-' then
  251. parser_buffer.next
  252. parser_buffer.skip_separators
  253. Result := left - parse_create_e1
  254. Result := parse_create_r1(Result)
  255. else
  256. Result := left
  257. end
  258. end
  259. end
  260. parse_create_r2 (left: NUMBER): NUMBER
  261. do
  262. if parser_buffer.end_reached then
  263. Result := left
  264. else
  265. inspect
  266. parser_buffer.current_character
  267. when '*' then
  268. parser_buffer.next
  269. parser_buffer.skip_separators
  270. Result := left * parse_create_e2
  271. Result := parse_create_r2(Result)
  272. when '/' then
  273. parser_buffer.next
  274. parser_buffer.skip_separators
  275. Result := parse_create_e2
  276. if Result.is_zero then
  277. parser_buffer.set_last_error_message(once "Attempt to divide " + left.to_string + once " by zero.")
  278. else
  279. Result := left / Result
  280. end
  281. Result := parse_create_r2(Result)
  282. else
  283. Result := left
  284. end
  285. end
  286. end
  287. parse_create_r3 (left: NUMBER): NUMBER
  288. do
  289. Result := left
  290. if not parser_buffer.end_reached then
  291. if parser_buffer.current_character = '!' then
  292. parser_buffer.next
  293. parser_buffer.skip_separators
  294. if Result.is_integer_general_number then
  295. if Result.is_positive then
  296. Result := Result.factorial
  297. else
  298. parser_buffer.set_last_error_message(once "Attempt to compute %
  299. %factorial of a negative value (" + Result.to_string + once ").")
  300. end
  301. else
  302. parser_buffer.set_last_error_message(once "Attempt to compute %
  303. %factorial with a non integral value (" + Result.to_string + once ").")
  304. end
  305. end
  306. end
  307. end
  308. parse_create_constant: NUMBER
  309. local
  310. stop: BOOLEAN; c: CHARACTER; n, n_save: INTEGER
  311. do
  312. from
  313. until
  314. stop
  315. loop
  316. if parser_buffer.end_reached then
  317. stop := True
  318. else
  319. c := parser_buffer.current_character
  320. if c.is_digit then
  321. if Result /= Void then
  322. Result := Result @* 10 @+ c.decimal_value
  323. else
  324. --|*** BUG ***
  325. n_save := n
  326. n := n * 10 + c.decimal_value
  327. if n < 0 then
  328. Result := from_integer(n_save)
  329. Result := Result @* 10 @+ c.decimal_value
  330. end
  331. --|*** WORK AROUND ***
  332. if n >= 214748364 then
  333. Result := from_integer(n)
  334. end
  335. --|*** D.Colnet 17 nov. 2002 ***
  336. end
  337. parser_buffer.next
  338. else
  339. stop := True
  340. end
  341. end
  342. end
  343. parser_buffer.skip_separators
  344. if Result = Void then
  345. Result := from_integer(n)
  346. end
  347. ensure
  348. Result /= Void
  349. end
  350. Integer_expected: STRING "Integer constant expected."
  351. end -- class NUMBER_TOOLS
  352. --
  353. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  354. --
  355. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  356. -- of this software and associated documentation files (the "Software"), to deal
  357. -- in the Software without restriction, including without limitation the rights
  358. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  359. -- copies of the Software, and to permit persons to whom the Software is
  360. -- furnished to do so, subject to the following conditions:
  361. --
  362. -- The above copyright notice and this permission notice shall be included in
  363. -- all copies or substantial portions of the Software.
  364. --
  365. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  366. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  367. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  368. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  369. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  370. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  371. -- THE SOFTWARE.