diff liboctave/util/oct-string.cc @ 26110:b543cf12c63f

Move octave_str2double to liboctave (patch #9084). * libinterp/corefcn/strfns.cc: Move "Fstr2double" from "str2double.cc". * liboctave/util/oct-string.cc: Move functions from "str2double.cc". * libinterp/util/oct-string.h: Add function "octave_str2double". * libinterp/corefcn/str2double.cc: Delete file.
author Markus Mützel <markus.muetzel@gmx.de>
date Sun, 18 Nov 2018 15:44:36 +0100
parents 8b548f2f8086
children 3e44ed9d50b6
line wrap: on
line diff
--- a/liboctave/util/oct-string.cc	Wed Nov 21 10:35:43 2018 -0800
+++ b/liboctave/util/oct-string.cc	Sun Nov 18 15:44:36 2018 +0100
@@ -25,6 +25,7 @@
 
 #include "oct-string.h"
 
+#include <algorithm>
 #include <cctype>
 #include <cstring>
 
@@ -224,3 +225,259 @@
 INSTANTIATE_OCTAVE_STRING(Array<char>)
 
 #undef INSTANTIATE_OCTAVE_STRING
+
+static inline bool
+is_imag_unit (int c)
+{ return c == 'i' || c == 'j'; }
+
+static double
+single_num (std::istringstream& is)
+{
+  double num = 0.0;
+
+  char c = is.peek ();
+
+  // Skip spaces.
+  while (isspace (c))
+    {
+      is.get ();
+      c = is.peek ();
+    }
+
+  if (std::toupper (c) == 'I')
+    {
+      // It's infinity.
+      is.get ();
+      char c1 = is.get ();
+      char c2 = is.get ();
+      if (std::tolower (c1) == 'n' && std::tolower (c2) == 'f')
+        {
+          num = octave::numeric_limits<double>::Inf ();
+          is.peek (); // May set EOF bit.
+        }
+      else
+        is.setstate (std::ios::failbit); // indicate that read has failed.
+    }
+  else if (c == 'N')
+    {
+      // It's NA or NaN
+      is.get ();
+      char c1 = is.get ();
+      if (c1 == 'A')
+        {
+          num = octave_NA;
+          is.peek (); // May set EOF bit.
+        }
+      else
+        {
+          char c2 = is.get ();
+          if (c1 == 'a' && c2 == 'N')
+            {
+              num = octave::numeric_limits<double>::NaN ();
+              is.peek (); // May set EOF bit.
+            }
+          else
+            is.setstate (std::ios::failbit); // indicate that read has failed.
+        }
+    }
+  else
+    is >> num;
+
+  return num;
+}
+
+static std::istringstream&
+extract_num (std::istringstream& is, double& num, bool& imag, bool& have_sign)
+{
+  have_sign = imag = false;
+
+  char c = is.peek ();
+
+  // Skip leading spaces.
+  while (isspace (c))
+    {
+      is.get ();
+      c = is.peek ();
+    }
+
+  bool negative = false;
+
+  // Accept leading sign.
+  if (c == '+' || c == '-')
+    {
+      have_sign = true;
+      negative = c == '-';
+      is.get ();
+      c = is.peek ();
+    }
+
+  // Skip spaces after sign.
+  while (isspace (c))
+    {
+      is.get ();
+      c = is.peek ();
+    }
+
+  // Imaginary number (i*num or just i), or maybe 'inf'.
+  if (c == 'i')
+    {
+      // possible infinity.
+      is.get ();
+      c = is.peek ();
+
+      if (is.eof ())
+        {
+          // just 'i' and string is finished.  Return immediately.
+          imag = true;
+          num = (negative ? -1.0 : 1.0);
+          return is;
+        }
+      else
+        {
+          if (std::tolower (c) != 'n')
+            imag = true;
+          is.unget ();
+        }
+    }
+  else if (c == 'j')
+    imag = true;
+
+  // It's i*num or just i
+  if (imag)
+    {
+      is.get ();
+      c = is.peek ();
+      // Skip spaces after imaginary unit.
+      while (isspace (c))
+        {
+          is.get ();
+          c = is.peek ();
+        }
+
+      if (c == '*')
+        {
+          // Multiplier follows, we extract it as a number.
+          is.get ();
+          num = single_num (is);
+          if (is.good ())
+            c = is.peek ();
+        }
+      else
+        num = 1.0;
+    }
+  else
+    {
+      // It's num, num*i, or numi.
+      num = single_num (is);
+      if (is.good ())
+        {
+          c = is.peek ();
+
+          // Skip spaces after number.
+          while (isspace (c))
+            {
+              is.get ();
+              c = is.peek ();
+            }
+
+          if (c == '*')
+            {
+              is.get ();
+              c = is.peek ();
+
+              // Skip spaces after operator.
+              while (isspace (c))
+                {
+                  is.get ();
+                  c = is.peek ();
+                }
+
+              if (is_imag_unit (c))
+                {
+                  imag = true;
+                  is.get ();
+                  c = is.peek ();
+                }
+              else
+                is.setstate (std::ios::failbit); // indicate read has failed.
+            }
+          else if (is_imag_unit (c))
+            {
+              imag = true;
+              is.get ();
+              c = is.peek ();
+            }
+        }
+    }
+
+  if (is.good ())
+    {
+      // Skip trailing spaces.
+      while (isspace (c))
+        {
+          is.get ();
+          c = is.peek ();
+        }
+    }
+
+  if (negative)
+    num = -num;
+
+  return is;
+}
+
+static inline void
+set_component (Complex& c, double num, bool imag)
+{
+#if defined (HAVE_CXX_COMPLEX_SETTERS)
+  if (imag)
+    c.imag (num);
+  else
+    c.real (num);
+#elif defined (HAVE_CXX_COMPLEX_REFERENCE_ACCESSORS)
+  if (imag)
+    c.imag () = num;
+  else
+    c.real () = num;
+#else
+  if (imag)
+    c = Complex (c.real (), num);
+  else
+    c = Complex (num, c.imag ());
+#endif
+}
+
+Complex
+octave_str2double (const std::string& str_arg)
+{
+  Complex val (0.0, 0.0);
+
+  std::string str = str_arg;
+
+  // FIXME: removing all commas doesn't allow actual parsing.
+  //        Example: "1,23.45" is wrong, but passes Octave.
+  str.erase (std::remove (str.begin (), str.end(), ','), str.end ());
+  std::istringstream is (str);
+
+  double num;
+  bool i1, i2, s1, s2;
+
+  if (is.eof ())
+    val = octave::numeric_limits<double>::NaN ();
+  else if (! extract_num (is, num, i1, s1))
+    val = octave::numeric_limits<double>::NaN ();
+  else
+    {
+      set_component (val, num, i1);
+
+      if (! is.eof ())
+        {
+          if (! extract_num (is, num, i2, s2) || i1 == i2 || ! s2)
+            val = octave::numeric_limits<double>::NaN ();
+          else
+            set_component (val, num, i2);
+        }
+    }
+
+  return val;
+}