Implemented all the functions except !EVAL and !UPCASE.
[pspp] / src / language / lexer / macro.c
index d4d7b322b9218b28636e165bfcd0b80f6c088825..b17d467faab6506649c7e8e62325a07e8035332c 100644 (file)
@@ -826,7 +826,7 @@ error:
 }
 
 static bool
-string_is_quoted_string (const char *s, struct string *content)
+unquote_string (const char *s, struct string *content)
 {
   struct string_lexer slex;
   string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE /* XXX */);
@@ -854,6 +854,18 @@ string_is_quoted_string (const char *s, struct string *content)
   return true;
 }
 
+static bool
+parse_integer (const char *s, int *np)
+{
+  errno = 0;
+
+  char *tail;
+  long int n = strtol (s, &tail, 10);
+  *np = n < INT_MIN ? INT_MIN : n > INT_MAX ? INT_MAX : n;
+  tail += strspn (tail, CC_SPACES);
+  return *tail == '\0' && errno != ERANGE && n == *np;
+}
+
 static bool
 expand_macro_function (struct parse_macro_function_ctx *ctx,
                        struct string *output,
@@ -867,10 +879,8 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
   else if (parse_macro_function (ctx, &args, ss_cstr ("!blanks"), 1, 1,
                                  input_consumed))
     {
-      char *tail;
-      errno = 0;
-      int n = strtol (args.strings[0], &tail, 10);
-      if (*tail != '\0' || n < 0 || errno == ERANGE)
+      int n;
+      if (!parse_integer (args.strings[0], &n))
         {
           printf ("argument to !BLANKS must be non-negative integer (not \"%s\")\n", args.strings[0]);
           string_array_destroy (&args);
@@ -883,13 +893,34 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
                                  input_consumed))
     {
       for (size_t i = 0; i < args.n; i++)
-        if (!string_is_quoted_string (args.strings[i], output))
+        if (!unquote_string (args.strings[i], output))
           ds_put_cstr (output, args.strings[i]);
     }
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!head"), 1, 1,
+                                 input_consumed))
+    {
+      struct string content = DS_EMPTY_INITIALIZER;
+      const char *s = (unquote_string (args.strings[0], &content)
+                       ? ds_cstr (&content) : args.strings[0]);
+
+      struct macro_tokens mts = { .n = 0 };
+      macro_tokens_from_string (&mts, ss_cstr (s), SEG_MODE_INTERACTIVE /* XXX */);
+      if (mts.n > 0)
+        ds_put_substring (output, mts.mts[0].representation);
+      macro_tokens_uninit (&mts);
+      ds_destroy (&content);
+    }
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!index"), 2, 2,
+                                 input_consumed))
+    {
+      const char *haystack = args.strings[0];
+      const char *needle = strstr (haystack, args.strings[1]);
+      ds_put_format (output, "%zu", needle ? needle - haystack + 1 : 0);
+    }
   else if (parse_macro_function (ctx, &args, ss_cstr ("!quote"), 1, 1,
                                  input_consumed))
     {
-      if (string_is_quoted_string (args.strings[0], NULL))
+      if (unquote_string (args.strings[0], NULL))
         ds_put_cstr (output, args.strings[0]);
       else
         {
@@ -904,10 +935,49 @@ expand_macro_function (struct parse_macro_function_ctx *ctx,
           ds_put_byte (output, '\'');
         }
     }
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!substr"), 2, 3,
+                                 input_consumed))
+    {
+      int start;
+      if (!parse_integer (args.strings[1], &start) || start < 1)
+        {
+          printf ("second argument to !SUBSTR must be positive integer (not \"%s\")\n", args.strings[1]);
+          string_array_destroy (&args);
+          return false;
+        }
+
+      int count = INT_MAX;
+      if (args.n > 2 && (!parse_integer (args.strings[2], &count) || count < 0))
+        {
+          printf ("third argument to !SUBSTR must be non-negative integer (not \"%s\")\n", args.strings[1]);
+          string_array_destroy (&args);
+          return false;
+        }
+
+      struct substring s = ss_cstr (args.strings[0]);
+      ds_put_substring (output, ss_substr (s, start - 1, count));
+    }
+  else if (parse_macro_function (ctx, &args, ss_cstr ("!tail"), 1, 1,
+                                 input_consumed))
+    {
+      struct string content = DS_EMPTY_INITIALIZER;
+      const char *s = (unquote_string (args.strings[0], &content)
+                       ? ds_cstr (&content) : args.strings[0]);
+
+      struct macro_tokens mts = { .n = 0 };
+      macro_tokens_from_string (&mts, ss_cstr (s), SEG_MODE_INTERACTIVE /* XXX */);
+      if (mts.n > 1)
+        {
+          struct macro_tokens tail = { .mts = mts.mts + 1, .n = mts.n - 1 };
+          macro_tokens_to_representation (&tail, output);
+        }
+      macro_tokens_uninit (&mts);
+      ds_destroy (&content);
+    }
   else if (parse_macro_function (ctx, &args, ss_cstr ("!unquote"), 1, 1,
                                  input_consumed))
     {
-      if (!string_is_quoted_string (args.strings[0], output))
+      if (!unquote_string (args.strings[0], output))
         ds_put_cstr (output, args.strings[0]);
     }
   else if (ctx->n_input > 0