PageRenderTime 24ms CodeModel.GetById 13ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/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--
  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.