Added new files resulting from directory restructuring.
[pspp-builds.git] / src / language / lexer / range-parser.c
diff --git a/src/language/lexer/range-parser.c b/src/language/lexer/range-parser.c
new file mode 100644 (file)
index 0000000..41d0198
--- /dev/null
@@ -0,0 +1,111 @@
+#include <config.h>
+#include "range-parser.h"
+#include <stdbool.h>
+#include "data-in.h"
+#include "message.h"
+#include "lexer.h"
+#include "magic.h"
+#include "str.h"
+#include "value.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+static bool parse_number (double *, const struct fmt_spec *);
+
+/* Parses and stores a numeric value, or a range of the form "x
+   THRU y".  Open-ended ranges may be specified as "LO(WEST) THRU
+   y" or "x THRU HI(GHEST)".  Sets *X and *Y to the range or the
+   value and returns success.
+
+   Numeric values are always accepted.  If F is nonnull, then
+   string values are also accepted, and converted to numeric
+   values using the specified format. */
+bool
+parse_num_range (double *x, double *y, const struct fmt_spec *f) 
+{
+  if (lex_match_id ("LO") || lex_match_id ("LOWEST"))
+    *x = LOWEST;
+  else if (!parse_number (x, f))
+    return false;
+
+  if (lex_match_id ("THRU")) 
+    {
+      if (lex_match_id ("HI") || lex_match_id ("HIGHEST"))
+        *y = HIGHEST;
+      else if (!parse_number (y, f))
+        return false;
+
+      if (*y < *x) 
+        {
+          double t;
+          msg (SW, _("Low end of range (%g) is below high end (%g).  "
+                     "The range will be treated as reversed."),
+               *x, *y);
+          t = *x;
+          *x = *y;
+          *y = t;
+        }
+      else if (*x == *y) 
+        msg (SW, _("Ends of range are equal (%g)."), *x);
+
+      return true;
+    }
+  else
+    {
+      if (*x == LOWEST) 
+        {
+          msg (SE, _("LO or LOWEST must be part of a range."));
+          return false;
+        }
+      *y = *x;
+    }
+  
+  return true;
+}
+
+/* Parses a number and stores it in *X.  Returns success.
+
+   Numeric values are always accepted.  If F is nonnull, then
+   string values are also accepted, and converted to numeric
+   values using the specified format. */
+static bool
+parse_number (double *x, const struct fmt_spec *f)
+{
+  if (lex_is_number ()) 
+    {
+      *x = lex_number ();
+      lex_get ();
+      return true;
+    }
+  else if (token == T_STRING && f != NULL) 
+    {
+      struct data_in di;
+      union value v;
+      di.s = ds_data (&tokstr);
+      di.e = ds_end (&tokstr);
+      di.v = &v;
+      di.flags = 0;
+      di.f1 = 1;
+      di.f2 = ds_length (&tokstr);
+      di.format = *f;
+      data_in (&di);
+      lex_get ();
+      *x = v.f;
+      if (*x == SYSMIS)
+        {
+          lex_error (_("System-missing value is not valid here."));
+          return false;
+        }
+      return true;
+    }
+  else 
+    {
+      if (f != NULL)
+        lex_error (_("expecting number or data string"));
+      else
+        lex_force_num ();
+      return false; 
+    }
+}