lexer: Factor out functions for counting columns.
[pspp] / src / language / lexer / lexer.c
index 2218caadd7e4c525453b3d3d855bc0f0daf5a377..bad24d3f9baeca4e4be9e5c32c95a3a9c509d130 100644 (file)
@@ -28,7 +28,6 @@
 #include <unictype.h>
 #include <unistd.h>
 #include <unistr.h>
-#include <uniwidth.h>
 
 #include "language/command.h"
 #include "language/lexer/macro.h"
@@ -379,6 +378,14 @@ lex_get (struct lexer *lexer)
           return;
       }
 }
+
+/* Advances LEXER by N tokens. */
+void
+lex_get_n (struct lexer *lexer, size_t n)
+{
+  while (n-- > 0)
+    lex_get (lexer);
+}
 \f
 /* Issuing errors. */
 
@@ -564,7 +571,8 @@ lex_next_error_valist (struct lexer *lexer, 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, '.');
       msg (SE, "%s", ds_cstr (&s));
       ds_destroy (&s);
     }
@@ -821,9 +829,14 @@ lex_force_int (struct lexer *lexer)
 bool
 lex_force_int_range (struct lexer *lexer, const char *name, long min, long max)
 {
+  bool is_number = lex_is_number (lexer);
   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;
+  bool too_small = (is_integer ? lex_integer (lexer) < min
+                    : is_number ? lex_number (lexer) < min
+                    : false);
+  bool too_big = (is_integer ? lex_integer (lexer) > max
+                  : is_number ? lex_number (lexer) > max
+                  : false);
   if (is_integer && !too_small && !too_big)
     return true;
 
@@ -883,6 +896,14 @@ lex_force_int_range (struct lexer *lexer, const char *name, long min, long max)
               else
                 lex_error (lexer, _("Expected positive integer."));
             }
+          else
+            {
+              if (name)
+                lex_error (lexer, _("Expected integer %ld or greater for %s."),
+                           min, name);
+              else
+                lex_error (lexer, _("Expected integer %ld or greater."), min);
+            }
         }
       else if (report_upper_bound)
         {
@@ -1123,32 +1144,49 @@ lex_tokens_match (const struct token *actual, const struct token *expected)
     }
 }
 
-/* If LEXER is positioned at the sequence of tokens that may be parsed from S,
-   skips it and returns true.  Otherwise, returns false.
-
-   S may consist of an arbitrary sequence of tokens, e.g. "KRUSKAL-WALLIS",
-   "2SLS", or "END INPUT PROGRAM".  Identifiers may be abbreviated to their
-   first three letters. */
-bool
-lex_match_phrase (struct lexer *lexer, const char *s)
+static size_t
+lex_at_phrase__ (struct lexer *lexer, const char *s)
 {
   struct string_lexer slex;
   struct token token;
-  int i;
 
-  i = 0;
+  size_t i = 0;
   string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE, true);
   while (string_lexer_next (&slex, &token))
     {
       bool match = lex_tokens_match (lex_next (lexer, i++), &token);
       token_uninit (&token);
       if (!match)
-        return false;
+        return 0;
     }
+  return i;
+}
 
-  while (i-- > 0)
-    lex_get (lexer);
-  return true;
+/* If LEXER is positioned at the sequence of tokens that may be parsed from S,
+   returns true.  Otherwise, returns false.
+
+   S may consist of an arbitrary sequence of tokens, e.g. "KRUSKAL-WALLIS",
+   "2SLS", or "END INPUT PROGRAM".  Identifiers may be abbreviated to their
+   first three letters. */
+bool
+lex_at_phrase (struct lexer *lexer, const char *s)
+{
+  return lex_at_phrase__ (lexer, s) > 0;
+}
+
+/* If LEXER is positioned at the sequence of tokens that may be parsed from S,
+   skips it and returns true.  Otherwise, returns false.
+
+   S may consist of an arbitrary sequence of tokens, e.g. "KRUSKAL-WALLIS",
+   "2SLS", or "END INPUT PROGRAM".  Identifiers may be abbreviated to their
+   first three letters. */
+bool
+lex_match_phrase (struct lexer *lexer, const char *s)
+{
+  size_t n = lex_at_phrase__ (lexer, s);
+  if (n > 0)
+    lex_get_n (lexer, n);
+  return n > 0;
 }
 
 static int
@@ -1180,39 +1218,12 @@ lex_token_get_last_line_number (const struct lex_source *src,
     }
 }
 
-static int
-count_columns (const char *s_, size_t length)
-{
-  const uint8_t *s = CHAR_CAST (const uint8_t *, s_);
-  int columns;
-  size_t ofs;
-  int mblen;
-
-  columns = 0;
-  for (ofs = 0; ofs < length; ofs += mblen)
-    {
-      ucs4_t uc;
-
-      mblen = u8_mbtouc (&uc, s + ofs, length - ofs);
-      if (uc != '\t')
-        {
-          int width = uc_width (uc, "UTF-8");
-          if (width > 0)
-            columns += width;
-        }
-      else
-        columns = ROUND_UP (columns + 1, 8);
-    }
-
-  return columns + 1;
-}
-
 static int
 lex_token_get_first_column (const struct lex_source *src,
                             const struct lex_token *token)
 {
-  return count_columns (&src->buffer[token->line_pos - src->tail],
-                        token->token_pos - token->line_pos);
+  return utf8_count_columns (&src->buffer[token->line_pos - src->tail],
+                             token->token_pos - token->line_pos) + 1;
 }
 
 static int
@@ -1226,7 +1237,7 @@ lex_token_get_last_column (const struct lex_source *src,
   newline = memrchr (start, '\n', end - start);
   if (newline != NULL)
     start = newline + 1;
-  return count_columns (start, end - start);
+  return utf8_count_columns (start, end - start) + 1;
 }
 
 static struct msg_location