Macro arguments and the !length function work.
[pspp] / src / language / control / define.c
index 958ae5035cdb8890c6b4c6038fee43f693727132..2f5ffd9d6b7d0ed9b6b4516c2792a7bd5bd1f5a0 100644 (file)
@@ -68,9 +68,68 @@ parse_quoted_token (struct lexer *lexer, struct token *token)
       lex_error (lexer, _("String must contain exactly one token."));
       return false;
     }
+  lex_get (lexer);
   return true;
 }
 
+static void
+macro_tokenize (struct macro *m, struct lexer *lexer)
+{
+  struct state
+    {
+      struct segmenter segmenter;
+      struct substring body;
+    };
+
+  struct state state = {
+    .segmenter = SEGMENTER_INIT (lex_get_syntax_mode (lexer)),
+    .body = m->body,
+  };
+  struct state saved = state;
+
+  struct token token = { .type = T_STOP };
+
+  while (state.body.length > 0)
+    {
+      struct scanner scanner;
+      scanner_init (&scanner, &token);
+
+      for (;;)
+        {
+          enum segment_type type;
+          int seg_len = segmenter_push (&state.segmenter, state.body.string,
+                                        state.body.length, true, &type);
+          assert (seg_len >= 0);
+
+          struct substring segment = ss_head (state.body, seg_len);
+          ss_advance (&state.body, seg_len);
+
+          enum scan_result result = scanner_push (&scanner, type, segment, &token);
+          if (result == SCAN_SAVE)
+            saved = state;
+          else if (result == SCAN_BACK)
+            {
+              state = saved;
+              break;
+            }
+          else if (result == SCAN_DONE)
+            break;
+        }
+
+      /* We have a token in 'token'. */
+      if (is_scan_type (token.type))
+        {
+          if (token.type != SCAN_SKIP)
+            {
+              /* XXX report error */
+            }
+        }
+      else
+        tokens_add (&m->body_tokens, &token);
+      token_destroy (&token);
+    }
+}
+
 int
 cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
 {
@@ -92,19 +151,34 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
         m->params = x2nrealloc (m->params, &allocated_params,
                                 sizeof *m->params);
 
-      struct macro_param *p = &m->params[m->n_params++];
+      size_t param_index = m->n_params++;
+      struct macro_param *p = &m->params[param_index];
       *p = (struct macro_param) { .expand_arg = true };
 
       /* Parse parameter name. */
       if (match_macro_id (lexer, "!POSITIONAL"))
-        p->name = NULL;
+        {
+          if (param_index > 0 && !m->params[param_index - 1].positional)
+            {
+              lex_error (lexer, _("Positional parameters must precede "
+                                  "keyword parameters."));
+              goto error;
+            }
+
+          p->positional = true;
+          p->name = xasprintf ("!%zu", param_index + 1);
+        }
       else
         {
-          if (!lex_force_id (lexer) || !lex_force_match (lexer, T_EQUALS))
+          if (!lex_force_id (lexer))
             goto error;
 
-          p->name = ss_xstrdup (lex_tokss (lexer));
+          p->positional = false;
+          p->name = xasprintf ("!%s", lex_tokcstr (lexer));
           lex_get (lexer);
+
+          if (!lex_force_match (lexer, T_EQUALS))
+            goto error;
         }
 
       /* Parse default value. */
@@ -113,7 +187,6 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
           if (!lex_force_match (lexer, T_LPAREN))
             goto error;
 
-          size_t allocated_tokens = 0;
           /* XXX Should this handle balanced inner parentheses? */
           while (!lex_match (lexer, T_RPAREN))
             {
@@ -122,12 +195,7 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
                   lex_error_expecting (lexer, ")");
                   goto error;
                 }
-              if (allocated_tokens >= p->def.n)
-                p->def.tokens = x2nrealloc (p->def.tokens, &allocated_tokens,
-                                            sizeof *p->def.tokens);
-
-              struct token *token = &p->def.tokens[p->def.n++];
-              token_copy (token, lex_next (lexer, 0));
+              tokens_add (&p->def, lex_next (lexer, 0));
               lex_get (lexer);
             }
         }
@@ -181,20 +249,25 @@ cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
         goto error;
     }
 
-  size_t allocated_body = 0;
+  struct string body = DS_EMPTY_INITIALIZER;
   while (!match_macro_id (lexer, "!ENDDEFINE"))
     {
       if (lex_token (lexer) != T_STRING)
         {
           lex_error (lexer, _("Expecting macro body or !ENDDEFINE"));
+          ds_destroy (&body);
           goto error;
         }
 
-      if (allocated_body >= m->n_body)
-        m->body = x2nrealloc (m->body, &allocated_body, sizeof *m->body);
-      m->body[m->n_body] = ss_xstrdup (lex_tokss (lexer));
+      ds_put_substring (&body, lex_tokss (lexer));
+      ds_put_byte (&body, '\n');
       lex_get (lexer);
     }
+  m->body = ds_ss (&body);
+
+  macro_tokenize (m, lexer);
+
+  lex_define_macro (lexer, m);
 
   return CMD_SUCCESS;