Most basic macros work.
[pspp] / src / language / lexer / lexer.c
index bdbce65a8a0480f55179420e6091e4ba76fe2d62..1b5ba2244f02c71245a649dd9d42a4bfacccd950 100644 (file)
@@ -31,6 +31,7 @@
 #include <uniwidth.h>
 
 #include "language/command.h"
+#include "language/lexer/macro.h"
 #include "language/lexer/scan.h"
 #include "language/lexer/segment.h"
 #include "language/lexer/token.h"
@@ -40,7 +41,6 @@
 #include "libpspp/i18n.h"
 #include "libpspp/ll.h"
 #include "libpspp/message.h"
-#include "libpspp/macro.h"
 #include "libpspp/misc.h"
 #include "libpspp/str.h"
 #include "libpspp/u8-istream.h"
@@ -68,6 +68,7 @@ struct lex_token
     size_t token_len;           /* Length of source for token in bytes. */
     size_t line_pos;            /* Start of line containing token_pos. */
     int first_line;             /* Line number at token_pos. */
+    bool from_macro;
   };
 
 /* A source of tokens, corresponding to a syntax file.
@@ -101,13 +102,15 @@ struct lex_source
     struct lex_token *tokens;   /* Lookahead tokens for parser. */
   };
 
-static struct lex_source *lex_source_create (struct lex_reader *);
+static struct lex_source *lex_source_create (struct lexer *,
+                                             struct lex_reader *);
 static void lex_source_destroy (struct lex_source *);
 
 /* Lexer. */
 struct lexer
   {
     struct ll_list sources;     /* Contains "struct lex_source"s. */
+    struct macro_set *macros;
   };
 
 static struct lex_source *lex_source__ (const struct lexer *);
@@ -150,8 +153,11 @@ lex_reader_set_file_name (struct lex_reader *reader, const char *file_name)
 struct lexer *
 lex_create (void)
 {
-  struct lexer *lexer = xzalloc (sizeof *lexer);
-  ll_init (&lexer->sources);
+  struct lexer *lexer = xmalloc (sizeof *lexer);
+  *lexer = (struct lexer) {
+    .sources = LL_INITIALIZER (lexer->sources),
+    .macros = macro_set_create (),
+  };
   return lexer;
 }
 
@@ -165,10 +171,19 @@ lex_destroy (struct lexer *lexer)
 
       ll_for_each_safe (source, next, struct lex_source, ll, &lexer->sources)
         lex_source_destroy (source);
+      macro_set_destroy (lexer->macros);
       free (lexer);
     }
 }
 
+/* Adds M to LEXER's set of macros.  M replaces any existing macro with the
+   same name.  Takes ownership of M. */
+void
+lex_define_macro (struct lexer *lexer, struct macro *m)
+{
+  macro_set_add (lexer->macros, m);
+}
+
 /* Inserts READER into LEXER so that the next token read by LEXER comes from
    READER.  Before the caller, LEXER must either be empty or at a T_ENDCMD
    token. */
@@ -866,10 +881,10 @@ lex_next__ (const struct lexer *lexer_, int n)
     }
 }
 
-static const struct token *
+static const struct lex_token *
 lex_source_front (const struct lex_source *src)
 {
-  return &src->tokens[deque_front (&src->deque, 0)].token;
+  return &src->tokens[deque_front (&src->deque, 0)];
 }
 
 static const struct lex_token *
@@ -879,8 +894,8 @@ lex_source_next__ (const struct lex_source *src, int n)
     {
       if (!deque_is_empty (&src->deque))
         {
-          const struct token *front = lex_source_front (src);
-          if (front->type == T_STOP || front->type == T_ENDCMD)
+          const struct lex_token *front = lex_source_front (src);
+          if (front->token.type == T_STOP || front->token.type == T_ENDCMD)
             return front;
         }
 
@@ -1381,6 +1396,16 @@ lex_source_error_valist (struct lex_source *src, int n0, int n1,
   token = lex_source_next__ (src, n0);
   if (token->token.type == T_ENDCMD)
     ds_put_cstr (&s, _("Syntax error at end of command"));
+  else if (token->from_macro)
+    {
+      /* XXX this isn't ideal, we should get the actual syntax */
+      char *syntax = token_to_string (&token->token);
+      if (syntax)
+        ds_put_format (&s, _("Syntax error at `%s'"), syntax);
+      else
+        ds_put_cstr (&s, _("Syntax error"));
+      free (syntax);
+    }
   else
     {
       struct substring syntax = lex_source_get_syntax__ (src, n0, n1);
@@ -1625,11 +1650,12 @@ lex_source_try_get (struct lex_source *src)
 static bool
 lex_source_get__ (struct lex_source *src)
 {
-  for (;;) (
-    if (src->eof)
-      return false;
-    else if (lex_source_try_get (src))
-      return true;
+  for (;;)
+    {
+      if (src->eof)
+        return false;
+      else if (lex_source_try_get (src))
+        return true;
     }
 }
 
@@ -1638,17 +1664,16 @@ lex_source_get (const struct lex_source *src_)
 {
   struct lex_source *src = CONST_CAST (struct lex_source *, src_);
 
-  if (!lex_source_get (src))
+  size_t old_count = deque_count (&src->deque);
+  if (!lex_source_get__ (src))
     return false;
 
-  struct macro_expander *me = macro_expander_create (src->lexer,
-                                                     lex_source_front (src));
-  if (!me)
-    return true;
-
-  for (;;)
+  struct macro_expander *me;
+  int retval = macro_expander_create (src->lexer->macros,
+                                      &lex_source_front (src)->token, &me);
+  while (!retval)
     {
-      if (!lex_source_get (src))
+      if (!lex_source_get__ (src))
         {
           /* This should not be reachable because we always get a T_STOP at the
              end of input and the macro_expander should always terminate
@@ -1656,12 +1681,35 @@ lex_source_get (const struct lex_source *src_)
           NOT_REACHED ();
         }
 
-      int retval = macro_expander_add (me, lex_source_front (src));
-      
+      retval = macro_expander_add (me, &lex_source_front (src)->token);
+    }
+  if (retval < 0)
+    {
+      /* XXX handle case where there's a macro invocation starting from some
+         later token we've already obtained */
+      macro_expander_destroy (me);
+      return true;
+    }
+
+  /* XXX handle case where the macro invocation doesn't use all the tokens */
+  while (deque_count (&src->deque) > old_count)
+    lex_source_pop_front (src);
 
+  struct tokens expansion = { .tokens = NULL };
+  macro_expander_get_expansion (me, &expansion);
+  macro_expander_destroy (me);
+
+  for (size_t i = 0; i < expansion.n; i++)
+    {
+      *lex_push_token__ (src) = (struct lex_token) {
+        .token = expansion.tokens[i],
+        .from_macro = true,
+        /* XXX the rest */
+      };
     }
-  
+  free (expansion.tokens);
 
+  return true;
 }
 \f
 static void