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