SET: Improve error messages for SET CCx.
[pspp] / src / language / lexer / macro.c
index d20b74c9fe20e9f2c8b6d8ec71078313d9ead6fd..9536727a0f78d08b047feaeb1b639597462934cc 100644 (file)
@@ -136,7 +136,7 @@ void
 macro_token_copy (struct macro_token *dst, const struct macro_token *src)
 {
   token_copy (&dst->token, &src->token);
-  ss_alloc_substring (&dst->syntax, src->syntax);
+  dst->syntax = ss_clone (src->syntax);
 }
 
 void
@@ -220,31 +220,29 @@ macro_tokens_add (struct macro_tokens *mts, const struct macro_token *mt)
   macro_token_copy (macro_tokens_add_uninit (mts), mt);
 }
 
-/* Tokenizes SRC according to MODE and appends the tokens to MTS.  Uses STACK,
-   if nonull, for error reporting. */
+/* Tokenizes SRC according to MODE and appends the tokens to MTS, using STACK
+   for error reporting. */
 static void
-macro_tokens_from_string__ (struct macro_tokens *mts, const struct substring src,
-                            enum segmenter_mode mode,
-                            const struct macro_expansion_stack *stack)
+macro_tokens_from_string (struct macro_tokens *mts, const struct substring src,
+                          enum segmenter_mode mode,
+                          const struct macro_expansion_stack *stack)
 {
   struct segmenter segmenter = segmenter_init (mode, true);
   struct substring body = src;
 
   while (body.length > 0)
     {
-      struct macro_token mt = {
-        .token = { .type = T_STOP },
-        .syntax = { .string = body.string },
-      };
-      struct token *token = &mt.token;
-
       enum segment_type type;
       int seg_len = segmenter_push (&segmenter, body.string,
                                     body.length, true, &type);
       assert (seg_len >= 0);
 
-      struct substring segment = ss_head (body, seg_len);
-      enum tokenize_result result = token_from_segment (type, segment, token);
+      struct macro_token mt = {
+        .token = { .type = T_STOP },
+        .syntax = ss_head (body, seg_len),
+      };
+      enum tokenize_result result
+        = token_from_segment (type, mt.syntax, &mt.token);
       ss_advance (&body, seg_len);
 
       switch (result)
@@ -253,28 +251,18 @@ macro_tokens_from_string__ (struct macro_tokens *mts, const struct substring src
           break;
 
         case TOKENIZE_TOKEN:
-          mt.syntax.length = body.string - mt.syntax.string;
           macro_tokens_add (mts, &mt);
           break;
 
         case TOKENIZE_ERROR:
-          mt.syntax.length = body.string - mt.syntax.string;
-          macro_error (stack, &mt, "%s", token->string.string);
+          macro_error (stack, &mt, "%s", mt.token.string.string);
           break;
         }
 
-      token_uninit (token);
+      token_uninit (&mt.token);
     }
 }
 
-/* Tokenizes SRC according to MODE and appends the tokens to MTS. */
-void
-macro_tokens_from_string (struct macro_tokens *mts, const struct substring src,
-                          enum segmenter_mode mode)
-{
-  macro_tokens_from_string__ (mts, src, mode, NULL);
-}
-
 void
 macro_tokens_print (const struct macro_tokens *mts, FILE *stream)
 {
@@ -334,6 +322,8 @@ classify_token (enum token_type type)
     case T_RPAREN:
     case T_LBRACK:
     case T_RBRACK:
+    case T_LCURLY:
+    case T_RCURLY:
       return TC_PUNCT;
 
     case T_PLUS:
@@ -341,6 +331,7 @@ classify_token (enum token_type type)
     case T_ASTERISK:
     case T_SLASH:
     case T_EQUALS:
+    case T_COLON:
     case T_AND:
     case T_OR:
     case T_NOT:
@@ -359,6 +350,7 @@ classify_token (enum token_type type)
       return TC_BINOP;
 
     case T_COMMA:
+    case T_SEMICOLON:
       return TC_COMMA;
     }
 
@@ -418,24 +410,8 @@ macro_destroy (struct macro *m)
       free (p->name);
 
       macro_tokens_uninit (&p->def);
-
-      switch (p->arg_type)
-        {
-        case ARG_N_TOKENS:
-          break;
-
-        case ARG_CHAREND:
-          token_uninit (&p->charend);
-          break;
-
-        case ARG_ENCLOSE:
-          token_uninit (&p->enclose[0]);
-          token_uninit (&p->enclose[1]);
-          break;
-
-        case ARG_CMDEND:
-          break;
-        }
+      token_uninit (&p->start);
+      token_uninit (&p->end);
     }
   free (m->params);
   macro_tokens_uninit (&m->body);
@@ -514,9 +490,6 @@ macro_set_add (struct macro_set *set, struct macro *m)
 
 enum mc_state
   {
-    /* Error state. */
-    MC_ERROR,
-
     /* Accumulating tokens in mc->params toward the end of any type of
        argument. */
     MC_ARG,
@@ -620,39 +593,74 @@ mc_add_arg (struct macro_call *mc, const struct macro_token *mt,
             const struct msg_location *loc)
 {
   const struct macro_param *p = mc->param;
+  struct macro_tokens **argp = &mc->args[p - mc->macro->params];
 
   const struct token *token = &mt->token;
-  if ((token->type == T_ENDCMD || token->type == T_STOP)
-      && p->arg_type != ARG_CMDEND)
+  if (token->type == T_ENDCMD || token->type == T_STOP)
     {
-      mc_error (mc, loc,
-                _("Unexpected end of command reading argument %s "
-                  "to macro %s."), mc->param->name, mc->macro->name);
+      if (*argp)
+        {
+          switch (p->arg_type)
+            {
+            case ARG_CMDEND:
+              /* This is OK, it's the expected way to end the argument. */
+              break;
 
-      mc->state = MC_ERROR;
-      return -1;
+            case ARG_N_TOKENS:
+              mc_error (mc, loc,
+                        ngettext (_("Reached end of command expecting %zu "
+                                    "more token in argument %s to macro %s."),
+                                  _("Reached end of command expecting %zu "
+                                    "more tokens in argument %s to macro %s."),
+                                  p->n_tokens - (*argp)->n),
+                        p->n_tokens - (*argp)->n, p->name, mc->macro->name);
+              break;
+
+            case ARG_CHAREND:
+            case ARG_ENCLOSE:
+              {
+                char *end = token_to_string (&p->end);
+                mc_error (mc, loc, _("Reached end of command expecting \"%s\" "
+                                     "in argument %s to macro %s."),
+                          end, p->name, mc->macro->name);
+                free (end);
+              }
+              break;
+            }
+        }
+
+      /* The end of a command ends the current argument, precludes any further
+         arguments, and is not itself part of the argument. */
+      return mc_finished (mc);
     }
 
   mc->n_tokens++;
 
-  struct macro_tokens **argp = &mc->args[p - mc->macro->params];
   if (!*argp)
     *argp = xzalloc (sizeof **argp);
 
   bool add_token;               /* Should we add 'mt' to the current arg? */
   bool next_arg;                /* Should we advance to the next arg? */
-  if (p->arg_type == ARG_N_TOKENS)
+  switch (p->arg_type)
     {
+    case ARG_N_TOKENS:
       next_arg = (*argp)->n + 1 >= p->n_tokens;
       add_token = true;
-    }
-  else
-    {
-      next_arg = (p->arg_type == ARG_CMDEND
-                  ? token->type == T_ENDCMD || token->type == T_STOP
-                  : token_equal (token, (p->arg_type == ARG_CHAREND
-                                         ? &p->charend : &p->enclose[1])));
+      break;
+
+    case ARG_CHAREND:
+    case ARG_ENCLOSE:
+      next_arg = token_equal (token, &p->end);
       add_token = !next_arg;
+      break;
+
+    case ARG_CMDEND:
+      next_arg = false;
+      add_token = true;
+      break;
+
+    default:
+      NOT_REACHED ();
     }
 
   if (add_token)
@@ -677,8 +685,7 @@ mc_expected (struct macro_call *mc, const struct macro_token *actual,
             mc->param->name, mc->macro->name);
   free (expected_s);
 
-  mc->state = MC_ERROR;
-  return -1;
+  return mc_finished (mc);
 }
 
 static int
@@ -686,15 +693,21 @@ mc_enclose (struct macro_call *mc, const struct macro_token *mt,
             const struct msg_location *loc)
 {
   const struct token *token = &mt->token;
-  mc->n_tokens++;
-
-  if (token_equal (&mc->param->enclose[0], token))
+  const struct macro_param *p = mc->param;
+  if (token_equal (&p->start, token))
     {
+      mc->n_tokens++;
+
+      struct macro_tokens **argp = &mc->args[p - mc->macro->params];
+      if (!*argp)
+        *argp = xzalloc (sizeof **argp);
       mc->state = MC_ARG;
       return 0;
     }
-
-  return mc_expected (mc, mt, loc, &mc->param->enclose[0]);
+  else if (p->positional && (token->type == T_ENDCMD || token->type == T_STOP))
+    return mc_finished (mc);
+  else
+    return mc_expected (mc, mt, loc, &p->start);
 }
 
 static const struct macro_param *
@@ -728,17 +741,14 @@ mc_keyword (struct macro_call *mc, const struct macro_token *mt,
                                                               token->string);
   if (p)
     {
-      size_t arg_index = p - mc->macro->params;
-      mc->param = p;
-      if (mc->args[arg_index])
-        {
-          mc_error (mc, loc,
-                    _("Argument %s multiply specified in call to macro %s."),
-                    p->name, mc->macro->name);
-          mc->state = MC_ERROR;
-          return -1;
-        }
+      struct macro_tokens **argp = &mc->args[p - mc->macro->params];
+      if (*argp)
+        mc_error (mc, loc,
+                  _("Argument %s multiply specified in call to macro %s."),
+                  p->name, mc->macro->name);
 
+      *argp = xzalloc (sizeof **argp);
+      mc->param = p;
       mc->n_tokens++;
       mc->state = MC_EQUALS;
       return 0;
@@ -751,11 +761,9 @@ static int
 mc_equals (struct macro_call *mc, const struct macro_token *mt,
            const struct msg_location *loc)
 {
-  const struct token *token = &mt->token;
-  mc->n_tokens++;
-
-  if (token->type == T_EQUALS)
+  if (mt->token.type == T_EQUALS)
     {
+      mc->n_tokens++;
       mc->state = mc->param->arg_type == ARG_ENCLOSE ? MC_ENCLOSE : MC_ARG;
       return 0;
     }
@@ -801,7 +809,7 @@ macro_call_create__ (const struct macro_set *macros,
 /* If TOKEN is the first token of a call to a macro in MACROS, create a new
    macro expander, initializes *MCP to it.  Returns 0 if more tokens are needed
    and should be added via macro_call_add() or 1 if the caller should next call
-   macro_call_get_expansion().
+   macro_call_expand().
 
    If TOKEN is not the first token of a macro call, returns -1 and sets *MCP to
    NULL. */
@@ -846,17 +854,14 @@ macro_call_destroy (struct macro_call *mc)
    Returns a positive number to indicate that the returned number of tokens
    invoke a macro.  The number returned might be less than the number of tokens
    added because it can take a few tokens of lookahead to determine whether the
-   macro invocation is finished.  The caller should call
-   macro_call_get_expansion() to obtain the expansion. */
+   macro invocation is finished.  The caller should call macro_call_expand() to
+   obtain the expansion. */
 int
 macro_call_add (struct macro_call *mc, const struct macro_token *mt,
                 const struct msg_location *loc)
 {
   switch (mc->state)
     {
-    case MC_ERROR:
-      return -1;
-
     case MC_ARG:
       return mc_add_arg (mc, mt, loc);
 
@@ -974,12 +979,7 @@ parse_function_args (const struct macro_expander *me,
                      const char *function,
                      struct string_array *args)
 {
-  if (n < 2 || mts[1].token.type != T_LPAREN)
-    {
-      macro_error (me->stack, n > 1 ? &mts[1] : NULL,
-                   _("`(' expected following %s."), function);
-      return 0;
-    }
+  assert (n >= 2 && mts[1].token.type == T_LPAREN);
 
   for (size_t i = 2; i < n; )
     {
@@ -1031,7 +1031,8 @@ unquote_string (const char *s, enum segmenter_mode segmenter_mode,
       return false;
     }
 
-  ds_put_substring (content, token1.string);
+  if (content)
+    ds_put_substring (content, token1.string);
   token_uninit (&token1);
   return true;
 }
@@ -1078,7 +1079,6 @@ expand_macro_function (const struct macro_expander *me,
       MF_HEAD,
       MF_INDEX,
       MF_LENGTH,
-      MF_NULL,
       MF_QUOTE,
       MF_SUBSTR,
       MF_TAIL,
@@ -1092,7 +1092,6 @@ expand_macro_function (const struct macro_expander *me,
     [MF_HEAD]    = { "!HEAD",    1, 1 },
     [MF_INDEX]   = { "!INDEX",   2, 2 },
     [MF_LENGTH]  = { "!LENGTH",  1, 1 },
-    [MF_NULL]    = { "!NULL",    0, 0 },
     [MF_QUOTE]   = { "!QUOTE",   1, 1 },
     [MF_SUBSTR]  = { "!SUBSTR",  2, 3 },
     [MF_TAIL]    = { "!TAIL",    1, 1 },
@@ -1100,7 +1099,16 @@ expand_macro_function (const struct macro_expander *me,
     [MF_UPCASE]  = { "!UPCASE",  1, 1 },
   };
 
-  /* Is this a macro function? */
+  if (lex_id_match_n (ss_cstr ("!NULL"), input[0].token.string, 4))
+    return 1;
+
+  if (n_input < 2 || input[1].token.type != T_LPAREN)
+    {
+      /* Only consider macro functions when the name is followed by '('. */
+      return 0;
+    }
+
+  /* Is this a macro function name? */
   const struct macro_function *mf;
   for (mf = mfs; ; mf++)
     {
@@ -1115,13 +1123,14 @@ expand_macro_function (const struct macro_expander *me,
     }
 
   enum macro_function_id id = mf - mfs;
-  if (id == MF_NULL)
-    return 1;
 
   struct string_array args = STRING_ARRAY_INITIALIZER;
   size_t n_consumed = parse_function_args (me, input, n_input, mf->name, &args);
   if (!n_consumed)
-    return 0;
+    {
+      string_array_destroy (&args);
+      return 0;
+    }
 
   if (args.n < mf->min_args || args.n > mf->max_args)
     {
@@ -1144,6 +1153,7 @@ expand_macro_function (const struct macro_expander *me,
                      mf->name);
       else
         NOT_REACHED ();
+      string_array_destroy (&args);
       return 0;
     }
 
@@ -1182,8 +1192,8 @@ expand_macro_function (const struct macro_expander *me,
                                                  me->segmenter_mode, &tmp);
 
         struct macro_tokens mts = { .n = 0 };
-        macro_tokens_from_string__ (&mts, ss_cstr (s), me->segmenter_mode,
-                                    me->stack);
+        macro_tokens_from_string (&mts, ss_cstr (s), me->segmenter_mode,
+                                  me->stack);
         if (mts.n > 0)
           ds_put_substring (output, mts.mts[0].syntax);
         macro_tokens_uninit (&mts);
@@ -1252,8 +1262,8 @@ expand_macro_function (const struct macro_expander *me,
                                                  me->segmenter_mode, &tmp);
 
         struct macro_tokens mts = { .n = 0 };
-        macro_tokens_from_string__ (&mts, ss_cstr (s), me->segmenter_mode,
-                                    me->stack);
+        macro_tokens_from_string (&mts, ss_cstr (s), me->segmenter_mode,
+                                  me->stack);
         if (mts.n > 1)
           {
             struct macro_tokens tail = { .mts = mts.mts + 1, .n = mts.n - 1 };
@@ -1284,8 +1294,8 @@ expand_macro_function (const struct macro_expander *me,
     case MF_EVAL:
       {
         struct macro_tokens mts = { .n = 0 };
-        macro_tokens_from_string__ (&mts, ss_cstr (args.strings[0]),
-                                    me->segmenter_mode, me->stack);
+        macro_tokens_from_string (&mts, ss_cstr (args.strings[0]),
+                                  me->segmenter_mode, me->stack);
         struct macro_tokens exp = { .n = 0 };
         struct macro_expansion_stack stack = {
           .name = "!EVAL",
@@ -1553,7 +1563,7 @@ macro_evaluate_number (const struct macro_token **tokens, size_t n_tokens,
     return false;
 
   struct macro_tokens mts = { .n = 0 };
-  macro_tokens_from_string__ (&mts, ss_cstr (s), me->segmenter_mode, me->stack);
+  macro_tokens_from_string (&mts, ss_cstr (s), me->segmenter_mode, me->stack);
   if (mts.n != 1 || !token_is_number (&mts.mts[0].token))
     {
       macro_error (me->stack, mts.n > 0 ? &mts.mts[0] : NULL,
@@ -1795,8 +1805,8 @@ macro_expand_do (const struct macro_token *tokens, size_t n_tokens,
         return 0;
 
       struct macro_tokens items = { .n = 0 };
-      macro_tokens_from_string__ (&items, ss_cstr (list), me->segmenter_mode,
-                                  me->stack);
+      macro_tokens_from_string (&items, ss_cstr (list), me->segmenter_mode,
+                                me->stack);
       free (list);
 
       const struct macro_token *do_end = find_doend (subme.stack, p, end);
@@ -1822,6 +1832,7 @@ macro_expand_do (const struct macro_token *tokens, size_t n_tokens,
 
           macro_expand (p, do_end - p, &subme, exp);
         }
+      macro_tokens_uninit (&items);
       return do_end - tokens + 1;
     }
   else if (p < end && p->token.type == T_EQUALS)
@@ -1961,8 +1972,8 @@ macro_expand_arg (const struct token *token, const struct macro_expander *me,
                                         token->string.length);
   if (var)
     {
-      macro_tokens_from_string__ (exp, ss_cstr (var),
-                                  me->segmenter_mode, me->stack);
+      macro_tokens_from_string (exp, ss_cstr (var),
+                                me->segmenter_mode, me->stack);
       return true;
     }
 
@@ -2032,8 +2043,8 @@ macro_expand__ (const struct macro_token *mts, size_t n,
   size_t n_function = expand_macro_function (me, mts, n, &function_output);
   if (n_function)
     {
-      macro_tokens_from_string__ (exp, function_output.ss,
-                                  me->segmenter_mode, me->stack);
+      macro_tokens_from_string (exp, function_output.ss,
+                                me->segmenter_mode, me->stack);
       ds_destroy (&function_output);
 
       return n_function;