lexer: Rename LEX_ERROR_INTERACTIVE to LEX_ERROR_TERMINAL, change default
[pspp] / src / language / lexer / lexer.c
index e3725681c9c6a207332987046e5e9bee721008ee..bbe16cbe17a2c02305f631e8a04cfed5355a805b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -129,7 +129,7 @@ lex_reader_init (struct lex_reader *reader,
 {
   reader->class = class;
   reader->syntax = LEX_SYNTAX_AUTO;
-  reader->error = LEX_ERROR_INTERACTIVE;
+  reader->error = LEX_ERROR_CONTINUE;
   reader->file_name = NULL;
   reader->line_number = 0;
 }
@@ -362,6 +362,24 @@ lex_sbc_missing (const char *sbc)
   msg (SE, _("Required subcommand %s was not specified."), sbc);
 }
 
+/* Reports an error to the effect that specification SPEC may only be specified
+   once within subcommand SBC. */
+void
+lex_spec_only_once (struct lexer *lexer, const char *sbc, const char *spec)
+{
+  lex_error (lexer, _("%s may only be specified once within subcommand %s"),
+             spec, sbc);
+}
+
+/* Reports an error to the effect that specification SPEC is missing within
+   subcommand SBC. */
+void
+lex_spec_missing (struct lexer *lexer, const char *sbc, const char *spec)
+{
+  lex_error (lexer, _("Required %s specification missing from %s subcommand"),
+             sbc, spec);
+}
+
 /* Prints a syntax error message containing the current token and
    given message MESSAGE (if non-null). */
 void
@@ -607,6 +625,21 @@ lex_force_string (struct lexer *lexer)
     }
 }
 
+/* If the current token is a string or an identifier, does nothing and returns
+   true.  Otherwise, reports an error and returns false.
+
+   This is meant for use in syntactic situations where we want to encourage the
+   user to supply a quoted string, but for compatibility we also accept
+   identifiers.  (One example of such a situation is file names.)  Therefore,
+   the error message issued when the current token is wrong only says that a
+   string is expected and doesn't mention that an identifier would also be
+   accepted. */
+bool
+lex_force_string_or_id (struct lexer *lexer)
+{
+  return lex_is_integer (lexer) || lex_force_string (lexer);
+}
+
 /* If the current token is an integer, does nothing and returns true.
    Otherwise, reports an error and returns false. */
 bool
@@ -797,99 +830,58 @@ lex_next_tokss (const struct lexer *lexer, int n)
   return lex_next (lexer, n)->string;
 }
 
-/* If LEXER is positioned at the (pseudo)identifier S, skips it and returns
-   true.  Otherwise, returns false.
-
-   S may consist of an arbitrary number of identifiers, integers, and
-   punctuation e.g. "KRUSKAL-WALLIS", "2SLS", or "END INPUT PROGRAM".
-   Identifiers may be abbreviated to their first three letters.  Currently only
-   hyphens, slashes, and equals signs are supported as punctuation (but it
-   would be easy to add more).
-
-   S must be an ASCII string. */
-bool
-lex_match_phrase (struct lexer *lexer, const char *s)
+static bool
+lex_tokens_match (const struct token *actual, const struct token *expected)
 {
-  int tok_idx;
+  if (actual->type != expected->type)
+    return false;
 
-  for (tok_idx = 0; ; tok_idx++)
+  switch (actual->type)
     {
-      enum token_type token;
-      unsigned char c;
-
-      while (c_isspace (*s))
-        s++;
-
-      c = *s;
-      if (c == '\0')
-        {
-          int i;
-
-          for (i = 0; i < tok_idx; i++)
-            lex_get (lexer);
-          return true;
-        }
+    case T_POS_NUM:
+    case T_NEG_NUM:
+      return actual->number == expected->number;
 
-      token = lex_next_token (lexer, tok_idx);
-      switch (c)
-        {
-        case '-':
-          if (token != T_DASH)
-            return false;
-          s++;
-          break;
-
-        case '/':
-          if (token != T_SLASH)
-            return false;
-          s++;
-          break;
-
-        case '=':
-          if (token != T_EQUALS)
-            return false;
-          s++;
-          break;
+    case T_ID:
+      return lex_id_match (expected->string, actual->string);
 
-        case '0': case '1': case '2': case '3': case '4':
-        case '5': case '6': case '7': case '8': case '9':
-          {
-            unsigned int value;
+    case T_STRING:
+      return (actual->string.length == expected->string.length
+              && !memcmp (actual->string.string, expected->string.string,
+                          actual->string.length));
 
-            if (token != T_POS_NUM)
-              return false;
-
-            value = 0;
-            do
-              {
-                value = value * 10 + (*s++ - '0');
-              }
-            while (c_isdigit (*s));
-
-            if (lex_next_tokval (lexer, tok_idx) != value)
-              return false;
-          }
-          break;
+    default:
+      return true;
+    }
+}
 
-        default:
-          if (lex_is_id1 (c))
-            {
-              int len;
+/* If LEXER is positioned at the sequence of tokens that may be parsed from S,
+   skips it and returns true.  Otherwise, returns false.
 
-              if (token != T_ID)
-                return 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)
+{
+  struct string_lexer slex;
+  struct token token;
+  int i;
 
-              len = lex_id_get_length (ss_cstr (s));
-              if (!lex_id_match (ss_buffer (s, len),
-                                 lex_next_tokss (lexer, tok_idx)))
-                return false;
+  i = 0;
+  string_lexer_init (&slex, s, SEG_MODE_INTERACTIVE);
+  while (string_lexer_next (&slex, &token))
+    if (token.type != SCAN_SKIP)
+      {
+        bool match = lex_tokens_match (lex_next (lexer, i++), &token);
+        token_destroy (&token);
+        if (!match)
+          return false;
+      }
 
-              s += len;
-            }
-          else
-            NOT_REACHED ();
-        }
-    }
+  while (i-- > 0)
+    lex_get (lexer);
+  return true;
 }
 
 static int
@@ -1061,7 +1053,7 @@ lex_get_syntax_mode (const struct lexer *lexer)
 }
 
 /* Returns the error mode for the syntax file from which the current drawn is
-   drawn.  Returns LEX_ERROR_INTERACTIVE for a T_STOP token or if the command's
+   drawn.  Returns LEX_ERROR_TERMINAL for a T_STOP token or if the command's
    source does not have line numbers.
 
    There is no version of this function that takes an N argument because
@@ -1071,13 +1063,12 @@ enum lex_error_mode
 lex_get_error_mode (const struct lexer *lexer)
 {
   struct lex_source *src = lex_source__ (lexer);
-  return src == NULL ? LEX_ERROR_INTERACTIVE : src->reader->error;
+  return src == NULL ? LEX_ERROR_TERMINAL : src->reader->error;
 }
 
 /* If the source that LEXER is currently reading has error mode
-   LEX_ERROR_INTERACTIVE, discards all buffered input and tokens, so that the
-   next token to be read comes directly from whatever is next read from the
-   stream.
+   LEX_ERROR_TERMINAL, discards all buffered input and tokens, so that the next
+   token to be read comes directly from whatever is next read from the stream.
 
    It makes sense to call this function after encountering an error in a
    command entered on the console, because usually the user would prefer not to
@@ -1086,7 +1077,7 @@ void
 lex_interactive_reset (struct lexer *lexer)
 {
   struct lex_source *src = lex_source__ (lexer);
-  if (src != NULL && src->reader->error == LEX_ERROR_INTERACTIVE)
+  if (src != NULL && src->reader->error == LEX_ERROR_TERMINAL)
     {
       src->head = src->tail = 0;
       src->journal_pos = src->seg_pos = src->line_pos = 0;
@@ -1108,7 +1099,7 @@ lex_discard_rest_of_command (struct lexer *lexer)
 }
 
 /* Discards all lookahead tokens in LEXER, then discards all input sources
-   until it encounters one with error mode LEX_ERROR_INTERACTIVE or until it
+   until it encounters one with error mode LEX_ERROR_TERMINAL or until it
    runs out of input sources. */
 void
 lex_discard_noninteractive (struct lexer *lexer)
@@ -1120,7 +1111,7 @@ lex_discard_noninteractive (struct lexer *lexer)
       while (!deque_is_empty (&src->deque))
         lex_source_pop__ (src);
 
-      for (; src != NULL && src->reader->error != LEX_ERROR_INTERACTIVE;
+      for (; src != NULL && src->reader->error != LEX_ERROR_TERMINAL;
            src = lex_source__ (lexer))
         lex_source_destroy (src);
     }
@@ -1176,14 +1167,18 @@ lex_source_read__ (struct lex_source *src)
   do
     {
       size_t head_ofs;
+      size_t space;
       size_t n;
 
       lex_source_expand__ (src);
 
       head_ofs = src->head - src->tail;
+      space = src->allocated - head_ofs;
       n = src->reader->class->read (src->reader, &src->buffer[head_ofs],
-                                    src->allocated - head_ofs,
+                                    space,
                                     segmenter_get_prompt (&src->segmenter));
+      assert (n <= space);
+
       if (n == 0)
         {
           /* End of input.
@@ -1650,7 +1645,7 @@ lex_reader_for_substring_nocopy (struct substring s)
 
   r = xmalloc (sizeof *r);
   lex_reader_init (&r->reader, &lex_string_reader_class);
-  r->reader.syntax = LEX_SYNTAX_INTERACTIVE;
+  r->reader.syntax = LEX_SYNTAX_AUTO;
   r->s = s;
   r->offset = 0;