Most basic macros work.
[pspp] / src / language / lexer / macro.c
index 98320ae6d97a7daa2ac28ff3b9cc93fdaf2c5b59..b94a04ad7f4be3455c796e7531e7b0f3f39a01fb 100644 (file)
@@ -67,19 +67,69 @@ macro_destroy (struct macro *m)
   free (m);
 }
 \f
+struct macro_set *
+macro_set_create (void)
+{
+  struct macro_set *set = xmalloc (sizeof *set);
+  *set = (struct macro_set) {
+    .macros = HMAP_INITIALIZER (set->macros),
+  };
+  return set;
+}
+
+void
+macro_set_destroy (struct macro_set *set)
+{
+  if (!set)
+    return;
+
+  struct macro *macro, *next;
+  HMAP_FOR_EACH_SAFE (macro, next, struct macro, hmap_node, &set->macros)
+    {
+      hmap_delete (&set->macros, &macro->hmap_node);
+      macro_destroy (macro);
+    }
+  hmap_destroy (&set->macros);
+  free (set);
+}
+
+static unsigned int
+hash_macro_name (const char *name)
+{
+  return utf8_hash_case_string (name, 0);
+}
+
+static struct macro *
+macro_set_find__ (struct macro_set *set, const char *name)
+{
+  struct macro *macro;
+  HMAP_FOR_EACH_WITH_HASH (macro, struct macro, hmap_node,
+                           hash_macro_name (name), &set->macros)
+    if (!utf8_strcasecmp (macro->name, name))
+      return macro;
+
+  return NULL;
+}
+
 const struct macro *
 macro_set_find (const struct macro_set *set, const char *name)
 {
-  struct macro *macro;
+  return macro_set_find__ (CONST_CAST (struct macro_set *, set), name);
+}
 
-  HMAP_FOR_EACH_WITH_HASH (macro, struct macro, hmap_node,
-                           utf8_hash_case_string (name, 0), &set->macros)
+/* Adds M to SET.  M replaces any existing macro with the same name.  Takes
+   ownership of M. */
+void
+macro_set_add (struct macro_set *set, struct macro *m)
+{
+  struct macro *victim = macro_set_find__ (set, m->name);
+  if (victim)
     {
-      if (!utf8_strcasecmp (macro->name, name))
-        return macro;
+      hmap_delete (&set->macros, &victim->hmap_node);
+      macro_destroy (victim);
     }
 
-  return NULL;
+  hmap_insert (&set->macros, &m->hmap_node, hash_macro_name (m->name));
 }
 \f
 enum me_state
@@ -310,19 +360,67 @@ me_equals (struct macro_expander *me, const struct token *token)
   return me_expected (me, token, &equals);
 }
 
+int
+macro_expander_create (const struct macro_set *macros,
+                       const struct token *token,
+                       struct macro_expander **mep)
+{
+  *mep = NULL;
+  if (macro_set_is_empty (macros))
+    return -1;
+  if (token->type != T_ID && token->type != T_MACRO_ID)
+    return -1;
+
+  const struct macro *macro = macro_set_find (macros, token->string.string);
+  if (!macro)
+    return -1;
+
+  struct macro_expander *me = xmalloc (sizeof *me);
+  *me = (struct macro_expander) {
+    .macros = macros,
+
+    .state = ME_START,
+    .n_tokens = 1,
+
+    .macro = macro,
+    .args = xcalloc (macro->n_params, sizeof *me->args),
+    .arg_index = 0,
+  };
+  *mep = me;
+  return me_next_arg (me);
+}
+
+void
+macro_expander_destroy (struct macro_expander *me)
+{
+  if (!me)
+    return;
+
+  for (size_t i = 0; i < me->macro->n_params; i++)
+    if (me->args[i])
+      {
+        tokens_uninit (me->args[i]);
+        free (me->args[i]);
+      }
+  free (me->args);
+  free (me);
+}
+
 /* Adds TOKEN to the collection of tokens in ME that potentially need to be
    macro expanded.
 
-   Return values:
-
-   * -1: The tokens added do not actually invoke a macro.  The caller should
-     consume the first token without expanding it.
+   Returns -1 if the tokens added do not actually invoke a macro.  The caller
+   should consume the first token without expanding it.
 
-   * 0: The macro expander needs more tokens, for macro arguments or to decide
-      whether this is actually a macro invocation.  The caller should call
-      macro_expander_add() again with the next token.
+   Returns 0 if the macro expander needs more tokens, for macro arguments or to
+   decide whether this is actually a macro invocation.  The caller should call
+   macro_expander_add() again with the next token.
 
-    * >0: Expand the given number of tokens. */
+   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_expander_get_expansion() to obtain the expansion. */
 int
 macro_expander_add (struct macro_expander *me, const struct token *token)
 {
@@ -379,22 +477,32 @@ macro_expander_get_expansion (struct macro_expander *me, struct tokens *exp)
 
           struct substring segment = ss_head (state.body, seg_len);
           ss_advance (&state.body, seg_len);
+          printf ("segment \"%.*s\" %s token.type=%d\n", (int) segment.length, segment.string, segment_type_to_string (type), token.type);
 
           enum scan_result result = scanner_push (&scanner, type, segment, &token);
           if (result == SCAN_SAVE)
             saved = state;
           else if (result == SCAN_BACK)
             {
+              printf ("back\n");
               state = saved;
               break;
             }
           else if (result == SCAN_DONE)
-            break;
+            {
+              printf ("done\n");
+              break;
+            }
         }
 
       /* We have a token in 'token'. */
-      tokens_add (exp, &token);
+      printf ("add token %d %s\n", token.type, token_type_to_name (token.type));
+      if (is_scan_type (token.type))
+        {
+          /* XXX report error if it's not SCAN_SKIP */
+        }
+      else
+        tokens_add (exp, &token);
       token_destroy (&token);
     }
 }
-