improve macro error messages
[pspp] / src / language / lexer / macro.c
index ca32800f30a9dc5129d8b6f38f52a597da4398e5..bee23c999cb1be7f6254d69cdf315eea6206319c 100644 (file)
@@ -32,6 +32,7 @@
 #include "libpspp/str.h"
 #include "libpspp/string-array.h"
 #include "libpspp/string-map.h"
+#include "libpspp/stringi-set.h"
 
 #include "gl/c-ctype.h"
 #include "gl/ftoastr.h"
@@ -59,6 +60,41 @@ macro_token_to_representation (struct macro_token *mt, struct string *s)
   ds_put_substring (s, mt->representation);
 }
 
+bool
+is_macro_keyword (struct substring s)
+{
+  static struct stringi_set keywords = STRINGI_SET_INITIALIZER (keywords);
+  if (stringi_set_is_empty (&keywords))
+    {
+      static const char *kws[] = {
+        "BREAK",
+        "CHAREND",
+        "CMDEND",
+        "DEFAULT",
+        "DO",
+        "DOEND",
+        "ELSE",
+        "ENCLOSE",
+        "ENDDEFINE",
+        "IF",
+        "IFEND",
+        "IN",
+        "LET",
+        "NOEXPAND",
+        "OFFEXPAND",
+        "ONEXPAND",
+        "POSITIONAL",
+        "THEN",
+        "TOKENS",
+      };
+      for (size_t i = 0; i < sizeof kws / sizeof *kws; i++)
+        stringi_set_insert (&keywords, kws[i]);
+    }
+
+  ss_ltrim (&s, ss_cstr ("!"));
+  return stringi_set_contains_len (&keywords, s.string, s.length);
+}
+
 void
 macro_tokens_copy (struct macro_tokens *dst, const struct macro_tokens *src)
 {
@@ -251,28 +287,37 @@ classify_token (enum token_type type)
 }
 
 void
-macro_tokens_to_representation (struct macro_tokens *mts, struct string *s)
+macro_tokens_to_representation (struct macro_tokens *mts, struct string *s,
+                                size_t *ofs, size_t *len)
 {
+  assert ((ofs != NULL) == (len != NULL));
+
   if (!mts->n)
     return;
 
-  macro_token_to_representation (&mts->mts[0], s);
-  for (size_t i = 1; i < mts->n; i++)
+  for (size_t i = 0; i < mts->n; i++)
     {
-      enum token_type prev = mts->mts[i - 1].token.type;
-      enum token_type next = mts->mts[i].token.type;
-
-      if (prev == T_ENDCMD)
-        ds_put_byte (s, '\n');
-      else
+      if (i > 0)
         {
-          enum token_class pc = classify_token (prev);
-          enum token_class nc = classify_token (next);
-          if (needs_space (pc, nc))
-            ds_put_byte (s, ' ');
+          enum token_type prev = mts->mts[i - 1].token.type;
+          enum token_type next = mts->mts[i].token.type;
+
+          if (prev == T_ENDCMD)
+            ds_put_byte (s, '\n');
+          else
+            {
+              enum token_class pc = classify_token (prev);
+              enum token_class nc = classify_token (next);
+              if (needs_space (pc, nc))
+                ds_put_byte (s, ' ');
+            }
         }
 
+      if (ofs)
+        ofs[i] = s->ss.length;
       macro_token_to_representation (&mts->mts[i], s);
+      if (len)
+        len[i] = s->ss.length - ofs[i];
     }
 }
 
@@ -543,8 +588,7 @@ me_enclose (struct macro_expander *me, const struct macro_token *mt)
 static const struct macro_param *
 macro_find_parameter_by_name (const struct macro *m, struct substring name)
 {
-  if (ss_first (name) == '!')
-    ss_advance (&name, 1);
+  ss_ltrim (&name, ss_cstr ("!"));
 
   for (size_t i = 0; i < m->n_params; i++)
     {
@@ -722,7 +766,7 @@ static void
 macro_expand (const struct macro_tokens *,
               int nesting_countdown, const struct macro_set *,
               const struct macro_expander *, struct string_map *vars,
-              bool *expand, struct macro_tokens *exp);
+              bool *expand, bool *break_, struct macro_tokens *exp);
 
 static bool
 expand_macro_function (struct parse_macro_function_ctx *ctx,
@@ -879,7 +923,8 @@ static bool
 unquote_string (const char *s, struct string *content)
 {
   struct string_lexer slex;
-  string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE /* XXX */);
+  string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE /* XXX */,
+                     true);
 
   struct token token1;
   if (!string_lexer_next (&slex, &token1))
@@ -1024,7 +1069,7 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
       if (mts.n > 1)
         {
           struct macro_tokens tail = { .mts = mts.mts + 1, .n = mts.n - 1 };
-          macro_tokens_to_representation (&tail, output);
+          macro_tokens_to_representation (&tail, output, NULL, NULL);
         }
       macro_tokens_uninit (&mts);
       ds_destroy (&tmp);
@@ -1053,8 +1098,8 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
                                 SEG_MODE_INTERACTIVE /* XXX */);
       struct macro_tokens exp = { .n = 0 };
       macro_expand (&mts, ctx->nesting_countdown - 1, ctx->macros, ctx->me,
-                    ctx->vars, ctx->expand, &exp);
-      macro_tokens_to_representation (&exp, output);
+                    ctx->vars, ctx->expand, NULL, &exp);
+      macro_tokens_to_representation (&exp, output, NULL, NULL);
       macro_tokens_uninit (&exp);
       macro_tokens_uninit (&mts);
     }
@@ -1377,7 +1422,7 @@ static size_t
 macro_expand_if (const struct macro_token *tokens, size_t n_tokens,
                  int nesting_countdown, const struct macro_set *macros,
                  const struct macro_expander *me, struct string_map *vars,
-                 bool *expand, struct macro_tokens *exp)
+                 bool *expand, bool *break_, struct macro_tokens *exp)
 {
   const struct macro_token *p = tokens;
   const struct macro_token *end = tokens + n_tokens;
@@ -1452,7 +1497,8 @@ macro_expand_if (const struct macro_token *tokens, size_t n_tokens,
         .mts = CONST_CAST (struct macro_token *, start),
         .n = n,
       };
-      macro_expand (&mts, nesting_countdown, macros, me, vars, expand, exp);
+      macro_expand (&mts, nesting_countdown, macros, me, vars, expand,
+                    break_, exp);
     }
   return (end_if + 1) - tokens;
 }
@@ -1476,6 +1522,12 @@ macro_parse_let (const struct macro_token *tokens, size_t n_tokens,
       return 0;
     }
   const struct substring var_name = p->token.string;
+  if (is_macro_keyword (var_name)
+      || macro_find_parameter_by_name (me->macro, var_name))
+    {
+      printf ("cannot use argument name or macro keyword as !LET variable\n");
+      return 0;
+    }
   p++;
 
   if (p >= end || p->token.type != T_EQUALS)
@@ -1536,8 +1588,15 @@ macro_expand_do (const struct macro_token *tokens, size_t n_tokens,
       return 0;
     }
   const struct substring var_name = p->token.string;
+  if (is_macro_keyword (var_name)
+      || macro_find_parameter_by_name (me->macro, var_name))
+    {
+      printf ("cannot use argument name or macro keyword as !DO variable\n");
+      return 0;
+    }
   p++;
 
+  int miterate = settings_get_miterate ();
   if (p < end && p->token.type == T_MACRO_ID
       && ss_equals_case (p->token.string, ss_cstr ("!IN")))
     {
@@ -1566,10 +1625,19 @@ macro_expand_do (const struct macro_token *tokens, size_t n_tokens,
       };
       for (size_t i = 0; i < items.n; i++)
         {
+          if (i >= miterate)
+            {
+              printf ("exceeded maximum number of iterations %d\n", miterate);
+              break;
+            }
           string_map_replace_nocopy (vars, ss_xstrdup (var_name),
                                      ss_xstrdup (items.mts[i].representation));
+
+          bool break_ = false;
           macro_expand (&inner, nesting_countdown, macros,
-                        me, vars, expand, exp);
+                        me, vars, expand, &break_, exp);
+          if (break_)
+            break;
         }
       return do_end - tokens + 1;
     }
@@ -1619,17 +1687,31 @@ macro_expand_do (const struct macro_token *tokens, size_t n_tokens,
       };
 
       if ((by > 0 && first <= last) || (by < 0 && first >= last))
-        for (double index = first;
-             by > 0 ? (index <= last) : (index >= last);
-             index += by)
-          {
-            char index_s[DBL_BUFSIZE_BOUND];
-            c_dtoastr (index_s, sizeof index_s, 0, 0, index);
-            string_map_replace_nocopy (vars, ss_xstrdup (var_name),
-                                       xstrdup (index_s));
-            macro_expand (&inner, nesting_countdown, macros,
-                          me, vars, expand, exp);
-          }
+        {
+          int i = 0;
+          for (double index = first;
+               by > 0 ? (index <= last) : (index >= last);
+               index += by)
+            {
+              if (i++ > miterate)
+                {
+                  printf ("exceeded maximum number of iterations %d\n",
+                          miterate);
+                  break;
+                }
+
+              char index_s[DBL_BUFSIZE_BOUND];
+              c_dtoastr (index_s, sizeof index_s, 0, 0, index);
+              string_map_replace_nocopy (vars, ss_xstrdup (var_name),
+                                         xstrdup (index_s));
+
+              bool break_ = false;
+              macro_expand (&inner, nesting_countdown, macros,
+                            me, vars, expand, &break_, exp);
+              if (break_)
+                break;
+            }
+        }
 
       return do_end - tokens + 1;
     }
@@ -1644,7 +1726,7 @@ static void
 macro_expand (const struct macro_tokens *mts,
               int nesting_countdown, const struct macro_set *macros,
               const struct macro_expander *me, struct string_map *vars,
-              bool *expand, struct macro_tokens *exp)
+              bool *expand, bool *break_, struct macro_tokens *exp)
 {
   if (nesting_countdown <= 0)
     {
@@ -1657,7 +1739,8 @@ macro_expand (const struct macro_tokens *mts,
   struct string_map own_vars = STRING_MAP_INITIALIZER (own_vars);
   if (!vars)
     vars = &own_vars;
-  for (size_t i = 0; i < mts->n; i++)
+
+  for (size_t i = 0; i < mts->n && (!break_ || !*break_); i++)
     {
       const struct macro_token *mt = &mts->mts[i];
       const struct token *token = &mt->token;
@@ -1671,7 +1754,7 @@ macro_expand (const struct macro_tokens *mts,
               //macro_tokens_print (arg, stdout);
               if (*expand && param->expand_arg)
                 macro_expand (arg, nesting_countdown, macros, NULL, NULL,
-                              expand, exp);
+                              expand, break_, exp);
               else
                 for (size_t i = 0; i < arg->n; i++)
                   macro_tokens_add (exp, &arg->mts[i]);
@@ -1689,7 +1772,7 @@ macro_expand (const struct macro_tokens *mts,
                   const struct macro_tokens *arg = me->args[j];
                   if (*expand && param->expand_arg)
                     macro_expand (arg, nesting_countdown, macros, NULL, NULL,
-                                  expand, exp);
+                                  expand, break_, exp);
                   else
                     for (size_t k = 0; k < arg->n; k++)
                       macro_tokens_add (exp, &arg->mts[k]);
@@ -1700,7 +1783,7 @@ macro_expand (const struct macro_tokens *mts,
 
           size_t n = macro_expand_if (&mts->mts[i], mts->n - i,
                                       nesting_countdown, macros, me, vars,
-                                      expand, exp);
+                                      expand, break_, exp);
           if (n > 0)
             {
               i += n - 1;
@@ -1734,7 +1817,7 @@ macro_expand (const struct macro_tokens *mts,
             {
               i += retval - 1;
               macro_expand (&subme->macro->body, nesting_countdown - 1, macros,
-                            subme, NULL, expand, exp);
+                            subme, NULL, expand, break_, exp);
               macro_expander_destroy (subme);
               continue;
             }
@@ -1748,6 +1831,17 @@ macro_expand (const struct macro_tokens *mts,
           continue;
         }
 
+      if (ss_equals_case (token->string, ss_cstr ("!break")))
+        {
+          if (!break_)
+            printf ("!BREAK outside !DO\n");
+          else
+            {
+              *break_ = true;
+              break;
+            }
+        }
+
       struct parse_macro_function_ctx ctx = {
         .input = &mts->mts[i],
         .n_input = mts->n - i,
@@ -1812,7 +1906,7 @@ macro_expander_get_expansion (struct macro_expander *me, struct macro_tokens *ex
 
   bool expand = true;
   macro_expand (&me->macro->body, settings_get_mnest (),
-                me->macros, me, NULL, &expand, exp);
+                me->macros, me, NULL, &expand, NULL, exp);
 
 #if 0
   printf ("expansion:\n");