macro: Implement uniformly in terms of character strings.
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 10 Jun 2021 05:20:12 +0000 (22:20 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 10 Jun 2021 05:20:12 +0000 (22:20 -0700)
src/language/lexer/macro.c
tests/language/control/define.at

index 628b83af01bd3d5e80a67c88baf8c5963463c5ee..d4d7b322b9218b28636e165bfcd0b80f6c088825 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "language/lexer/macro.h"
 
+#include <errno.h>
 #include <limits.h>
 #include <stdlib.h>
 
@@ -28,6 +29,7 @@
 #include "libpspp/i18n.h"
 #include "libpspp/message.h"
 #include "libpspp/str.h"
+#include "libpspp/string-array.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -716,12 +718,11 @@ macro_expand (const struct macro_tokens *,
 
 static bool
 expand_macro_function (struct parse_macro_function_ctx *ctx,
-                       struct macro_token *output,
-                       size_t *input_consumed);
+                       struct string *output, size_t *input_consumed);
 
 static size_t
 parse_function_arg (struct parse_macro_function_ctx *ctx,
-                    size_t i, struct macro_token *farg)
+                    size_t i, struct string *farg)
 {
   struct macro_token *tokens = ctx->input;
   const struct token *token = &tokens[i].token;
@@ -733,25 +734,11 @@ parse_function_arg (struct parse_macro_function_ctx *ctx,
         {
           size_t param_idx = param - ctx->me->macro->params;
           const struct macro_tokens *marg = ctx->me->args[param_idx];
-          if (marg->n == 1)
-            macro_token_copy (farg, &marg->mts[0]);
-          else
+          for (size_t i = 0; i < marg->n; i++)
             {
-              struct string s = DS_EMPTY_INITIALIZER;
-              for (size_t i = 0; i < marg->n; i++)
-                {
-                  if (i)
-                    ds_put_byte (&s, ' ');
-                  ds_put_substring (&s, marg->mts[i].representation);
-                }
-
-              struct substring s_copy;
-              ss_alloc_substring (&s_copy, s.ss);
-
-              *farg = (struct macro_token) {
-                .token = { .type = T_MACRO_ID, .string = s.ss },
-                .representation = s_copy,
-              };
+              if (i)
+                ds_put_byte (farg, ' ');
+              ds_put_substring (farg, marg->mts[i].representation);
             }
           return 1;
         }
@@ -769,13 +756,13 @@ parse_function_arg (struct parse_macro_function_ctx *ctx,
         return subinput_consumed;
     }
 
-  macro_token_copy (farg, &tokens[i]);
+  ds_put_substring (farg, tokens[i].representation);
   return 1;
 }
 
 static bool
 parse_macro_function (struct parse_macro_function_ctx *ctx,
-                      struct macro_tokens *args,
+                      struct string_array *args,
                       struct substring function,
                       int min_args, int max_args,
                       size_t *input_consumed)
@@ -794,7 +781,7 @@ parse_macro_function (struct parse_macro_function_ctx *ctx,
       return false;
     }
 
-  *args = (struct macro_tokens) { .n = 0 };
+  string_array_init (args);
 
   for (size_t i = 2;; )
     {
@@ -811,9 +798,14 @@ parse_macro_function (struct parse_macro_function_ctx *ctx,
           return true;
         }
 
-      i += parse_function_arg (ctx, i, macro_tokens_add_uninit (args));
+      struct string s = DS_EMPTY_INITIALIZER;
+      i += parse_function_arg (ctx, i, &s);
       if (i >= n_tokens)
-        goto unexpected_end;
+        {
+          ds_destroy (&s);
+          goto unexpected_end;
+        }
+      string_array_append_nocopy (args, ds_steal_cstr (&s));
 
       if (tokens[i].token.type == T_COMMA)
         i++;
@@ -829,105 +821,106 @@ unexpected_end:
           function.string);
   /* Fall through. */
 error:
-  macro_tokens_uninit (args);
+  string_array_destroy (args);
   return false;
 }
 
+static bool
+string_is_quoted_string (const char *s, struct string *content)
+{
+  struct string_lexer slex;
+  string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE /* XXX */);
+
+  struct token token1;
+  if (!string_lexer_next (&slex, &token1))
+    return false;
+
+  if (token1.type != T_STRING)
+    {
+      token_uninit (&token1);
+      return false;
+    }
+
+  struct token token2;
+  if (string_lexer_next (&slex, &token2))
+    {
+      token_uninit (&token1);
+      token_uninit (&token2);
+      return false;
+    }
+
+  ds_put_substring (content, token1.string);
+  token_uninit (&token1);
+  return true;
+}
+
 static bool
 expand_macro_function (struct parse_macro_function_ctx *ctx,
-                       struct macro_token *output,
+                       struct string *output,
                        size_t *input_consumed)
 {
-  struct macro_tokens args;
+  struct string_array args;
 
   if (parse_macro_function (ctx, &args, ss_cstr ("!length"), 1, 1,
                             input_consumed))
-    {
-      size_t length = args.mts[0].representation.length;
-      *output = (struct macro_token) {
-        .token = { .type = T_POS_NUM, .number = length },
-        .representation = ss_cstr (xasprintf ("%zu", length)),
-      };
-    }
+    ds_put_format (output, "%zu", strlen (args.strings[0]));
   else if (parse_macro_function (ctx, &args, ss_cstr ("!blanks"), 1, 1,
                                  input_consumed))
     {
-      /* XXX this isn't right, it might be a character string containing a
-         positive integer, e.g. via !CONCAT. */
-      if (args.mts[0].token.type != T_POS_NUM)
+      char *tail;
+      errno = 0;
+      int n = strtol (args.strings[0], &tail, 10);
+      if (*tail != '\0' || n < 0 || errno == ERANGE)
         {
-          printf ("argument to !BLANKS must be positive integer\n");
-          macro_tokens_uninit (&args);
+          printf ("argument to !BLANKS must be non-negative integer (not \"%s\")\n", args.strings[0]);
+          string_array_destroy (&args);
           return false;
         }
 
-      struct string s = DS_EMPTY_INITIALIZER;
-      ds_put_byte_multiple (&s, ' ', args.mts[0].token.number);
-
-      struct substring s_copy;
-      ss_alloc_substring (&s_copy, s.ss);
-
-      *output = (struct macro_token) {
-        .token = { .type = T_ID, .string = s.ss },
-        .representation = s_copy,
-      };
+      ds_put_byte_multiple (output, ' ', n);
     }
   else if (parse_macro_function (ctx, &args, ss_cstr ("!concat"), 1, INT_MAX,
                                  input_consumed))
     {
-      struct string s = DS_EMPTY_INITIALIZER;
       for (size_t i = 0; i < args.n; i++)
-        {
-          if (args.mts[i].token.type == T_STRING)
-            ds_put_substring (&s, args.mts[i].token.string);
-          else
-            ds_put_substring (&s, args.mts[i].representation);
-        }
-
-      *output = (struct macro_token) {
-        .token = { .type = T_MACRO_ID /*XXX*/, .string = s.ss },
-      };
-      ss_alloc_substring (&output->representation, s.ss);
+        if (!string_is_quoted_string (args.strings[i], output))
+          ds_put_cstr (output, args.strings[i]);
     }
   else if (parse_macro_function (ctx, &args, ss_cstr ("!quote"), 1, 1,
                                  input_consumed))
     {
-      if (args.mts[0].token.type == T_STRING)
-        macro_token_copy (output, &args.mts[0]);
+      if (string_is_quoted_string (args.strings[0], NULL))
+        ds_put_cstr (output, args.strings[0]);
       else
         {
-          *output = (struct macro_token) { .token = { .type = T_STRING } };
-          ss_alloc_substring (&output->token.string, args.mts[0].representation);
-          output->representation = ss_cstr (token_to_string (&output->token));
+          ds_extend (output, strlen (args.strings[0]) + 2);
+          ds_put_byte (output, '\'');
+          for (const char *p = args.strings[0]; *p; p++)
+            {
+              if (*p == '\'')
+                ds_put_byte (output, '\'');
+              ds_put_byte (output, *p);
+            }
+          ds_put_byte (output, '\'');
         }
     }
   else if (parse_macro_function (ctx, &args, ss_cstr ("!unquote"), 1, 1,
                                  input_consumed))
     {
-      if (args.mts[0].token.type == T_STRING)
-        {
-          *output = (struct macro_token) { .token = { .type = T_MACRO_ID } };
-          ss_alloc_substring (&output->token.string, args.mts[0].token.string);
-          output->representation = ss_cstr (token_to_string (&output->token));
-        }
-      else
-        macro_token_copy (output, &args.mts[0]);
+      if (!string_is_quoted_string (args.strings[0], output))
+        ds_put_cstr (output, args.strings[0]);
     }
   else if (ctx->n_input > 0
            && ctx->input[0].token.type == T_MACRO_ID
            && ss_equals_case (ctx->input[0].token.string, ss_cstr ("!null")))
     {
       *input_consumed = 1;
-      *output = (struct macro_token) {
-        .token = { .type = T_MACRO_ID /* XXX*/ },
-      };
-      ss_alloc_substring (&output->token.string, ss_cstr (""));
       return true;
     }
   else
     return false;
 
-  macro_tokens_uninit (&args);
+  string_array_destroy (&args);
   return true;
 }
 
@@ -1011,18 +1004,15 @@ macro_expand (const struct macro_tokens *mts,
         .me = me,
         .expand = expand,
       };
-      struct macro_token function_output;
+      struct string function_output = DS_EMPTY_INITIALIZER;
       size_t function_consumed;
       if (expand_macro_function (&ctx, &function_output, &function_consumed))
         {
           i += function_consumed - 1;
 
-          if (function_output.token.type == T_MACRO_ID)
-            macro_tokens_from_string (exp, function_output.token.string,
-                                      SEG_MODE_INTERACTIVE /* XXX */);
-          else
-            macro_tokens_add (exp, &function_output);
-          macro_token_uninit (&function_output);
+          macro_tokens_from_string (exp, function_output.ss,
+                                    SEG_MODE_INTERACTIVE /* XXX */);
+          ds_destroy (&function_output);
 
           continue;
         }
index 7a005fc8059c43c4db40de80da7ca6697e84fd49..0d4ef96b8240841c5832d902a5b8d6d6c0766ce1 100644 (file)
@@ -370,11 +370,11 @@ PSPP_CHECK_MACRO_EXPANSION([!BLANKS],
   [!b.],
   [.
 ''.
- .
+.
 ' '.
-  .
+.
 '  '.
-     .
+.
 '     '.])
 
 dnl Keep this test in sync with the examples for !CONCAT in the manual.
@@ -390,3 +390,5 @@ PSPP_CHECK_MACRO_EXPANSION([!CONCAT],
 xy.
 1234.
 123.])
+
+dnl