lexer: New function lex_force_int_range().
[pspp] / src / language / lexer / lexer.c
index 9c402e679a949837720ce3f2589b0306b44b05cb..6d3549a82782134f62c8603e76ac207f54fe6fef 100644 (file)
@@ -44,7 +44,7 @@
 #include "libpspp/str.h"
 #include "libpspp/u8-istream.h"
 #include "output/journal.h"
-#include "output/text-item.h"
+#include "output/output-item.h"
 
 #include "gl/c-ctype.h"
 #include "gl/minmax.h"
@@ -141,7 +141,7 @@ void
 lex_reader_set_file_name (struct lex_reader *reader, const char *file_name)
 {
   free (reader->file_name);
-  reader->file_name = file_name != NULL ? xstrdup (file_name) : NULL;
+  reader->file_name = xstrdup_if_nonnull (file_name);
 }
 \f
 /* Creates and returns a new lexer. */
@@ -269,23 +269,40 @@ lex_next_error (struct lexer *lexer, int n0, int n1, const char *format, ...)
   va_end (args);
 }
 
-/* Prints a syntax error message saying that OPTION0 or one of the other
-   strings following it, up to the first NULL, is expected. */
+/* Prints a syntax error message saying that one of the strings provided as
+   varargs, up to the first NULL, is expected. */
 void
-(lex_error_expecting) (struct lexer *lexer, const char *option0, ...)
+(lex_error_expecting) (struct lexer *lexer, ...)
 {
-  enum { MAX_OPTIONS = 8 };
-  const char *options[MAX_OPTIONS + 1];
   va_list args;
-  int n;
 
-  va_start (args, option0);
-  options[0] = option0;
-  n = 0;
-  while (n + 1 < MAX_OPTIONS && options[n] != NULL)
-    options[++n] = va_arg (args, const char *);
+  va_start (args, lexer);
+  lex_error_expecting_valist (lexer, args);
   va_end (args);
+}
+
+/* Prints a syntax error message saying that one of the options provided in
+   ARGS, up to the first NULL, is expected. */
+void
+lex_error_expecting_valist (struct lexer *lexer, va_list args)
+{
+  enum { MAX_OPTIONS = 9 };
+  const char *options[MAX_OPTIONS];
+  int n = 0;
+  while (n < MAX_OPTIONS)
+    {
+      const char *option = va_arg (args, const char *);
+      if (!option)
+        break;
+
+      options[n++] = option;
+    }
+  lex_error_expecting_array (lexer, options, n);
+}
 
+void
+lex_error_expecting_array (struct lexer *lexer, const char **options, size_t n)
+{
   switch (n)
     {
     case 0:
@@ -334,7 +351,7 @@ void
       break;
 
     default:
-      NOT_REACHED ();
+      lex_error (lexer, NULL);
     }
 }
 
@@ -662,6 +679,96 @@ lex_force_int (struct lexer *lexer)
     }
 }
 
+/* If the current token is an integer in the range MIN...MAX (inclusive), does
+   nothing and returns true.  Otherwise, reports an error and returns false.
+   If NAME is nonnull, then it is used in the error message. */
+bool
+lex_force_int_range (struct lexer *lexer, const char *name, long min, long max)
+{
+  bool is_integer = lex_is_integer (lexer);
+  bool too_small = is_integer && lex_integer (lexer) < min;
+  bool too_big = is_integer && lex_integer (lexer) > max;
+  if (is_integer && !too_small && !too_big)
+    return true;
+
+  if (min > max)
+    {
+      /* Weird, maybe a bug in the caller.  Just report that we needed an
+         integer. */
+      if (name)
+        lex_error (lexer, _("Integer expected for %s."), name);
+      else
+        lex_error (lexer, _("Integer expected."));
+    }
+  else if (min == max)
+    {
+      if (name)
+        lex_error (lexer, _("Expected %ld for %s."), min, name);
+      else
+        lex_error (lexer, _("Expected %ld."), min);
+    }
+  else if (min + 1 == max)
+    {
+      if (name)
+        lex_error (lexer, _("Expected %ld or %ld for %s."), min, min + 1, name);
+      else
+        lex_error (lexer, _("Expected %ld or %ld."), min, min + 1);
+    }
+  else
+    {
+      bool report_lower_bound = (min > INT_MIN / 2) || too_small;
+      bool report_upper_bound = (max < INT_MAX / 2) || too_big;
+
+      if (report_lower_bound && report_upper_bound)
+        {
+          if (name)
+            lex_error (lexer,
+                       _("Expected integer between %ld and %ld for %s."),
+                       min, max, name);
+          else
+            lex_error (lexer, _("Expected integer between %ld and %ld."),
+                       min, max);
+        }
+      else if (report_lower_bound)
+        {
+          if (min == 0)
+            {
+              if (name)
+                lex_error (lexer, _("Expected non-negative integer for %s."),
+                           name);
+              else
+                lex_error (lexer, _("Expected non-negative integer."));
+            }
+          else if (min == 1)
+            {
+              if (name)
+                lex_error (lexer, _("Expected positive integer for %s."),
+                           name);
+              else
+                lex_error (lexer, _("Expected positive integer."));
+            }
+        }
+      else if (report_upper_bound)
+        {
+          if (name)
+            lex_error (lexer,
+                       _("Expected integer less than or equal to %ld for %s."),
+                       max, name);
+          else
+            lex_error (lexer, _("Expected integer less than or equal to %ld."),
+                       max);
+        }
+      else
+        {
+          if (name)
+            lex_error (lexer, _("Integer expected for %s."), name);
+          else
+            lex_error (lexer, _("Integer expected."));
+        }
+    }
+  return false;
+}
+
 /* If the current token is a number, does nothing and returns true.
    Otherwise, reports an error and returns false. */
 bool
@@ -1231,7 +1338,10 @@ lex_ellipsize__ (struct substring in, char *out, size_t out_size)
   int mblen;
 
   assert (out_size >= 16);
-  out_maxlen = out_size - (in.length >= out_size ? 3 : 0) - 1;
+  out_maxlen = out_size - 1;
+  if (in.length > out_maxlen - 3)
+    out_maxlen -= 3;
+
   for (out_len = 0; out_len < in.length; out_len += mblen)
     {
       if (in.string[out_len] == '\n'
@@ -1243,6 +1353,10 @@ lex_ellipsize__ (struct substring in, char *out, size_t out_size)
 
       mblen = u8_mblen (CHAR_CAST (const uint8_t *, in.string + out_len),
                         in.length - out_len);
+
+      if (mblen < 0)
+        break;
+
       if (out_len + mblen > out_maxlen)
         break;
     }
@@ -1282,7 +1396,8 @@ lex_source_error_valist (struct lex_source *src, int n0, int n1,
       ds_put_cstr (&s, ": ");
       ds_put_vformat (&s, format, args);
     }
-  ds_put_byte (&s, '.');
+  if (ds_last (&s) != '.')
+    ds_put_byte (&s, '.');
 
   struct msg m = {
     .category = MSG_C_SYNTAX,
@@ -1436,8 +1551,9 @@ lex_source_get__ (const struct lex_source *src_)
         copy_len--;
 
       /* Submit the line as syntax. */
-      text_item_submit (text_item_create_nocopy (TEXT_ITEM_SYNTAX,
-                                                 xmemdup0 (line, copy_len)));
+      output_item_submit (text_item_create_nocopy (TEXT_ITEM_SYNTAX,
+                                                   xmemdup0 (line, copy_len),
+                                                   NULL));
 
       src->journal_pos += line_len;
     }
@@ -1602,7 +1718,7 @@ lex_reader_for_file (const char *file_name, const char *encoding,
   r->reader.syntax = syntax;
   r->reader.error = error;
   r->reader.file_name = xstrdup (file_name);
-  r->reader.encoding = encoding ? xstrdup (encoding) : NULL;
+  r->reader.encoding = xstrdup_if_nonnull (encoding);
   r->reader.line_number = 1;
   r->istream = istream;
 
@@ -1671,7 +1787,7 @@ lex_reader_for_substring_nocopy (struct substring s, const char *encoding)
   r = xmalloc (sizeof *r);
   lex_reader_init (&r->reader, &lex_string_reader_class);
   r->reader.syntax = LEX_SYNTAX_AUTO;
-  r->reader.encoding = encoding ? xstrdup (encoding) : NULL;
+  r->reader.encoding = xstrdup_if_nonnull (encoding);
   r->s = s;
   r->offset = 0;