m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[2/18] argv_ref speedup: accessors into argv struct


From: Eric Blake
Subject: [2/18] argv_ref speedup: accessors into argv struct
Date: Tue, 27 Nov 2007 20:10:36 -0700
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071031 Thunderbird/2.0.0.9 Mnenhy/0.7.5.666

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I've ported the next patch in the series to head, as well as
cherry-picking it from the argv_ref branch into branch-1_4 (again, no //
comments, added a changelog, and some formatting touchups).

This patch replaces (almost) all direct access into the argv struct with
accessor methods.  The exceptions are in macro.c (which owns the contents
of the struct anyway) and the builtin and indir macros (which are
currently cloning the struct, but a later patch will fix that).  There
should be no memory impact, and only a slight speed decrease due to extra
function calls.  On the head, I added an inline accessor m4_arg_argc in
m4private.h, but not for any of the other functions, because their role
will be more complex in later patches.  On the branch, I altered the enum
value of TOKEN_EOF, so that the two separate enums token_data_type and
token_type don't overlap (during development of the series, I was bit a
few times when I used symbols from the wrong enum - with an overlap, my
bug wasn't detected by asserts. On head, these two enums had already been
consolidated into one some time ago).

2007-11-27  Eric Blake  <address@hidden>

        Stage 2: use accessors, not direct reference, into argv.
        * src/m4.h (TOKEN_EOF): Alter value, to ease debugging.
        (arg_argc, arg_type, arg_text, arg_len, arg_func): New
        prototypes.
        * src/macro.c (arg_argc, arg_type, arg_text, arg_len, arg_func):
        New accessor functions.
        * src/builtin.c (ARG, dump_args, define_macro, m4_builtin)
        (m4_indir): Use new accessors.
        * src/debug.c (trace_pre): Likewise.
        * src/format.c (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE):
        Likewise.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFHTNwj84KuGfSFAYARAmxsAKDIXb8f9jJ2tnxdyj1wUsTz3c+CFQCfadbw
QM8dw91kGQe08l7pCm3/yto=
=LFOx
-----END PGP SIGNATURE-----
>From 9743e462affaf7994c2b42b550a0f48befee85f0 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 27 Nov 2007 10:15:48 -0700
Subject: [PATCH] Stage 2: use accessors, not direct reference, into argv.

* m4/m4private.h (m4_arg_argc): New fast accessor.
* m4/m4module.h (m4_arg_argc, m4_arg_symbol, m4_is_arg_text)
(m4_is_arg_func, m4_arg_text, m4_arg_len, m4_arg_func): New
prototypes.
(m4_builtin_func, M4BUILTIN, M4BUILTIN_HANDLER, m4_dump_args)
(m4_macro_call): Make argc unsigned.
(M4ARG): Use new accessors.
* modules/m4.c (define, pushdef): Likewise.
(undefine, popdef, ifelse, m4_dump_symbols, defn, undivert)
(traceon, traceoff): Make argc unsigned.
* modules/m4.h (m4_dump_symbols_func): Likewise.
* modules/evalparse.c (m4_evaluate): Likewise.
* modules/gnu.c (builtin, indir): Use new accessors.
(changesyntax): Make argc unsigned.
* modules/perl.c (perleval): Likewise.
* m4/utility.c (m4_dump_args): Use new accessors.
* m4/macro.c (trace_pre): Likewise.
(m4_arg_symbol, m4_arg_argc, m4_is_arg_text, m4_is_arg_func)
(m4_arg_text, m4_arg_len, m4_arg_func): New functions.
(expand_macro, trace_pre, trace_post, m4_macro_call): Update argc
usage.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog           |   25 +++++++++++
 m4/m4module.h       |   30 ++++++++-----
 m4/m4private.h      |   28 ++++++++----
 m4/macro.c          |  119 +++++++++++++++++++++++++++++++++++++++++++--------
 m4/utility.c        |    7 ++-
 modules/evalparse.c |    3 +-
 modules/gnu.c       |   31 +++++++------
 modules/m4.c        |   31 +++++++------
 modules/m4.h        |    2 +-
 modules/perl.c      |   10 ++--
 10 files changed, 208 insertions(+), 78 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2fbe4ee..7cdf53b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2007-11-27  Eric Blake  <address@hidden>
+
+       Stage 2: use accessors, not direct reference, into argv.
+       * m4/m4private.h (m4_arg_argc): New fast accessor.
+       * m4/m4module.h (m4_arg_argc, m4_arg_symbol, m4_is_arg_text)
+       (m4_is_arg_func, m4_arg_text, m4_arg_len, m4_arg_func): New
+       prototypes.
+       (m4_builtin_func, M4BUILTIN, M4BUILTIN_HANDLER, m4_dump_args)
+       (m4_macro_call): Make argc unsigned.
+       (M4ARG): Use new accessors.
+       * modules/m4.c (define, pushdef): Likewise.
+       (undefine, popdef, ifelse, m4_dump_symbols, defn, undivert)
+       (traceon, traceoff): Make argc unsigned.
+       * modules/m4.h (m4_dump_symbols_func): Likewise.
+       * modules/evalparse.c (m4_evaluate): Likewise.
+       * modules/gnu.c (builtin, indir): Use new accessors.
+       (changesyntax): Make argc unsigned.
+       * modules/perl.c (perleval): Likewise.
+       * m4/utility.c (m4_dump_args): Use new accessors.
+       * m4/macro.c (trace_pre): Likewise.
+       (m4_arg_symbol, m4_arg_argc, m4_is_arg_text, m4_is_arg_func)
+       (m4_arg_text, m4_arg_len, m4_arg_func): New functions.
+       (expand_macro, trace_pre, trace_post, m4_macro_call): Update argc
+       usage.
+
 2007-11-24  Eric Blake  <address@hidden>
 
        Stage 1: convert m4_symbol_value** into new object.
diff --git a/m4/m4module.h b/m4/m4module.h
index 3f60cba..b0e9405 100644
--- a/m4/m4module.h
+++ b/m4/m4module.h
@@ -41,7 +41,8 @@ typedef struct m4_macro_args  m4_macro_args;
 
 typedef struct obstack         m4_obstack;
 
-typedef void   m4_builtin_func  (m4 *, m4_obstack *, int, m4_macro_args *);
+typedef void   m4_builtin_func  (m4 *, m4_obstack *, unsigned int,
+                                m4_macro_args *);
 
 /* The value of m4_builtin flags is built from these:  */
 enum {
@@ -97,11 +98,11 @@ struct m4_macro_args
 
 #define M4BUILTIN(name)                                                        
\
   static void CONC (builtin_, name)                                    \
-   (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv);
+   (m4 *context, m4_obstack *obs, unsigned int argc, m4_macro_args *argv);
 
 #define M4BUILTIN_HANDLER(name)                                                
\
   static void CONC (builtin_, name)                                    \
-   (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv)
+   (m4 *context, m4_obstack *obs, unsigned int argc, m4_macro_args *argv)
 
 #define M4INIT_HANDLER(name)                                           \
   void CONC (name, CONC (_LTX_, m4_init_module))                       \
@@ -119,14 +120,14 @@ struct m4_macro_args
   CONC (S, _func) *S = (CONC (S, _func) *)                             \
        m4_module_import (context, STR (M), STR (S), obs)
 
-#define M4ARG(i)                                                       \
-  ((i) == 0 ? argv->argv0                                              \
-   : argv->argc > (i) ? m4_get_symbol_value_text (argv->array[(i) - 1]) : "")
+/* Grab the text contents of argument I, or abort if the argument is
+   not text.  Assumes that `m4_macro_args *argv' is in scope.  */
+#define M4ARG(i) m4_arg_text (argv, i)
 
 extern bool    m4_bad_argc        (m4 *, int, const char *,
                                    unsigned int, unsigned int, bool);
 extern bool    m4_numeric_arg     (m4 *, const char *, const char *, int *);
-extern void    m4_dump_args       (m4 *, m4_obstack *, int,
+extern void    m4_dump_args       (m4 *, m4_obstack *, unsigned int,
                                    m4_macro_args *, const char *, bool);
 extern bool    m4_parse_truth_arg (m4 *, const char *, const char *, bool);
 
@@ -306,10 +307,17 @@ extern const m4_builtin   *m4_builtin_find_by_func 
(m4_module *,
 
 /* --- MACRO MANAGEMENT --- */
 
-extern void       m4_macro_expand_input (m4 *);
-extern void       m4_macro_call         (m4 *, m4_symbol_value *,
-                                         m4_obstack *, int, m4_macro_args *);
-
+extern void    m4_macro_expand_input   (m4 *);
+extern void    m4_macro_call           (m4 *, m4_symbol_value *,
+                                        m4_obstack *, unsigned int,
+                                        m4_macro_args *);
+extern unsigned int m4_arg_argc                (m4_macro_args *);
+extern m4_symbol_value *m4_arg_symbol  (m4_macro_args *, unsigned int);
+extern bool    m4_is_arg_text          (m4_macro_args *, unsigned int);
+extern bool    m4_is_arg_func          (m4_macro_args *, unsigned int);
+extern const char *m4_arg_text         (m4_macro_args *, unsigned int);
+extern size_t  m4_arg_len              (m4_macro_args *, unsigned int);
+extern m4_builtin_func *m4_arg_func    (m4_macro_args *, unsigned int);
 
 
 /* --- RUNTIME DEBUGGING --- */
diff --git a/m4/m4private.h b/m4/m4private.h
index 63a9bc4..10d82c9 100644
--- a/m4/m4private.h
+++ b/m4/m4private.h
@@ -346,24 +346,32 @@ struct m4_syntax_table {
 #endif
 
 
+/* --- MACRO MANAGEMENT --- */
+
 /* Various different token types.  */
 typedef enum {
-  M4_TOKEN_EOF,                        /* end of file */
-  M4_TOKEN_NONE,               /* discardable token */
-  M4_TOKEN_STRING,             /* a quoted string */
-  M4_TOKEN_SPACE,              /* whitespace */
-  M4_TOKEN_WORD,               /* an identifier */
-  M4_TOKEN_OPEN,               /* argument list start */
-  M4_TOKEN_COMMA,              /* argument separator */
-  M4_TOKEN_CLOSE,              /* argument list end */
-  M4_TOKEN_SIMPLE,             /* a single character */
-  M4_TOKEN_MACDEF              /* a macro's definition (see "defn") */
+  M4_TOKEN_EOF,                /* End of file, M4_SYMBOL_VOID.  */
+  M4_TOKEN_NONE,       /* Discardable token, M4_SYMBOL_VOID.  */
+  M4_TOKEN_STRING,     /* Quoted string or comment, M4_SYMBOL_TEXT or
+                          M4_SYMBOL_COMP.  */
+  M4_TOKEN_SPACE,      /* Whitespace, M4_SYMBOL_TEXT.  */
+  M4_TOKEN_WORD,       /* An identifier, M4_SYMBOL_TEXT.  */
+  M4_TOKEN_OPEN,       /* Argument list start, M4_SYMBOL_TEXT.  */
+  M4_TOKEN_COMMA,      /* Argument separator, M4_SYMBOL_TEXT.  */
+  M4_TOKEN_CLOSE,      /* Argument list end, M4_SYMBOL_TEXT.  */
+  M4_TOKEN_SIMPLE,     /* Single character, M4_SYMBOL_TEXT.  */
+  M4_TOKEN_MACDEF      /* Macro's definition (see "defn"), M4_SYMBOL_FUNC.  */
 } m4__token_type;
 
 extern m4__token_type  m4__next_token (m4 *, m4_symbol_value *, int *,
                                        const char *);
 extern bool            m4__next_token_is_open (m4 *);
 
+/* Fast macro versions of macro argv accessor functions,
+   that also have an identically named function exported in m4module.h.  */
+#ifdef NDEBUG
+# define m4_arg_argc(A)                (A)->argc
+#endif /* NDEBUG */
 
 
 /* --- PATH MANAGEMENT --- */
diff --git a/m4/macro.c b/m4/macro.c
index c4ba1af..449f160 100644
--- a/m4/macro.c
+++ b/m4/macro.c
@@ -42,9 +42,8 @@ static void    process_macro   (m4 *, m4_symbol_value *, 
m4_obstack *, int,
 
 static void    trace_prepre     (m4 *, const char *, size_t,
                                  m4_symbol_value *);
-static void    trace_pre        (m4 *, const char *, size_t, int,
-                                 m4_macro_args *);
-static void    trace_post       (m4 *, const char *, size_t, int,
+static void    trace_pre        (m4 *, const char *, size_t, m4_macro_args *);
+static void    trace_post       (m4 *, const char *, size_t,
                                  m4_macro_args *, m4_input_block *, bool);
 
 static void    trace_format     (m4 *, const char *, ...)
@@ -250,7 +249,6 @@ expand_macro (m4 *context, const char *name, m4_symbol 
*symbol)
   unsigned int argc_size;      /* Size of argc_stack on entry.  */
   unsigned int argv_size;      /* Size of argv_stack on entry.  */
   m4_macro_args *argv;
-  int argc;
   m4_obstack *expansion;
   m4_input_block *expanded;
   bool traced;
@@ -300,7 +298,6 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
 
   argv = collect_arguments (context, name, symbol, &argv_stack, argv_size,
                            &argc_stack);
-  argc = argv->argc;
   /* Calling collect_arguments invalidated name, but we copied it as
      argv[0].  */
   name = argv->argv0;
@@ -311,15 +308,14 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
   m4_set_current_line (context, loc_open_line);
 
   if (traced)
-    trace_pre (context, name, my_call_id, argc, argv);
+    trace_pre (context, name, my_call_id, argv);
 
   expansion = m4_push_string_init (context);
-  m4_macro_call (context, value, expansion, argc, argv);
+  m4_macro_call (context, value, expansion, argv->argc, argv);
   expanded = m4_push_string_finish ();
 
   if (traced)
-    trace_post (context, name, my_call_id, argc, argv, expanded,
-               trace_expansion);
+    trace_post (context, name, my_call_id, argv, expanded, trace_expansion);
 
   m4_set_current_file (context, loc_close_file);
   m4_set_current_line (context, loc_close_line);
@@ -344,7 +340,7 @@ recursion limit of %zu exceeded, use -L<N> to change it"),
    Return the object describing all of the macro arguments.  */
 static m4_macro_args *
 collect_arguments (m4 *context, const char *name, m4_symbol *symbol,
-                  m4_obstack *argptr, unsigned argv_base,
+                  m4_obstack *argptr, unsigned int argv_base,
                   m4_obstack *arguments)
 {
   m4_symbol_value token;
@@ -398,7 +394,7 @@ collect_arguments (m4 *context, const char *name, m4_symbol 
*symbol,
    the obstack EXPANSION.  Macro tracing is also handled here.  */
 void
 m4_macro_call (m4 *context, m4_symbol_value *value, m4_obstack *expansion,
-              int argc, m4_macro_args *argv)
+              unsigned int argc, m4_macro_args *argv)
 {
   if (m4_bad_argc (context, argc, argv->argv0,
                   VALUE_MIN_ARGS (value), VALUE_MAX_ARGS (value),
@@ -653,15 +649,15 @@ trace_prepre (m4 *context, const char *name, size_t id, 
m4_symbol_value *value)
 /* Format the parts of a trace line, that can be made before the macro is
    actually expanded.  Used from expand_macro ().  */
 static void
-trace_pre (m4 *context, const char *name, size_t id,
-          int argc, m4_macro_args *argv)
+trace_pre (m4 *context, const char *name, size_t id, m4_macro_args *argv)
 {
-  int i;
+  unsigned int i;
+  unsigned int argc = m4_arg_argc (argv);
 
   trace_header (context, id);
   trace_format (context, "%s", name);
 
-  if ((argc > 1) && m4_is_debug_bit (context, M4_DEBUG_TRACE_ARGS))
+  if (1 < argc && m4_is_debug_bit (context, M4_DEBUG_TRACE_ARGS))
     {
       bool quote = m4_is_debug_bit (context, M4_DEBUG_TRACE_QUOTE);
       const char *lquote = m4_get_syntax_lquote (M4SYNTAX);
@@ -675,8 +671,9 @@ trace_pre (m4 *context, const char *name, size_t id,
          if (i != 1)
            trace_format (context, ", ");
 
-         m4_symbol_value_print (argv->array[i - 1], &context->trace_messages,
-                                quote, lquote, rquote, arg_length, module);
+         m4_symbol_value_print (m4_arg_symbol (argv, i),
+                                &context->trace_messages, quote, lquote,
+                                rquote, arg_length, module);
        }
       trace_format (context, ")");
     }
@@ -686,7 +683,7 @@ trace_pre (m4 *context, const char *name, size_t id,
    expand_macro ().  */
 static void
 trace_post (m4 *context, const char *name, size_t id,
-           int argc, m4_macro_args *argv, m4_input_block *expanded,
+           m4_macro_args *argv, m4_input_block *expanded,
            bool trace_expansion)
 {
   if (trace_expansion)
@@ -697,3 +694,89 @@ trace_post (m4 *context, const char *name, size_t id,
 
   trace_flush (context);
 }
+
+
+/* Accessors into m4_macro_args.  */
+
+/* Given ARGV, return the symbol value at the specified INDEX, which
+   must be non-zero and less than argc.  */
+m4_symbol_value *
+m4_arg_symbol (m4_macro_args *argv, unsigned int index)
+{
+  assert (index && index < argv->argc);
+  return argv->array[index - 1];
+}
+
+/* Given ARGV, return true if argument INDEX is text.  Index 0 is
+   always text, as are indices beyond argc.  */
+bool
+m4_is_arg_text (m4_macro_args *argv, unsigned int index)
+{
+  if (index == 0 || argv->argc <= index)
+    return true;
+  return m4_is_symbol_value_text (m4_arg_symbol (argv, index));
+}
+
+/* Given ARGV, return true if argument INDEX is a builtin function.
+   Only non-zero indices less than argc can return true.  */
+bool
+m4_is_arg_func (m4_macro_args *argv, unsigned int index)
+{
+  if (index == 0 || argv->argc <= index)
+    return false;
+  return m4_is_symbol_value_func (m4_arg_symbol (argv, index));
+}
+
+/* Given ARGV, return the text at argument INDEX, or NULL if the
+   argument is not text.  Index 0 is always text, and indices beyond
+   argc return the empty string.  */
+const char *
+m4_arg_text (m4_macro_args *argv, unsigned int index)
+{
+  if (index == 0)
+    return argv->argv0;
+  if (argv->argc <= index)
+    return "";
+  if (!m4_is_symbol_value_text (argv->array[index - 1]))
+    return NULL;
+  return m4_get_symbol_value_text (argv->array[index - 1]);
+}
+
+/* Given ARGV, return the length of argument INDEX, or SIZE_MAX if the
+   argument is not text.  Indices beyond argc return 0.  */
+size_t
+m4_arg_len (m4_macro_args *argv, unsigned int index)
+{
+  /* TODO - update m4_macro_args to cache this.  */
+  if (index == 0)
+    return strlen (argv->argv0);
+  if (argv->argc <= index)
+    return 0;
+  if (!m4_is_symbol_value_text (argv->array[index - 1]))
+    return SIZE_MAX;
+  return strlen (m4_get_symbol_value_text (argv->array[index - 1]));
+}
+
+/* Given ARGV, return the builtin function referenced by argument
+   INDEX, or NULL if it is not a builtin.  Index 0, and indices beyond
+   argc, return NULL.  */
+m4_builtin_func *
+m4_arg_func (m4_macro_args *argv, unsigned int index)
+{
+  if (index == 0 || argv->argc <= index
+      || !m4_is_symbol_value_func (argv->array[index - 1]))
+    return NULL;
+  return m4_get_symbol_value_func (argv->array[index - 1]);
+}
+
+/* Define these last, so that earlier uses can benefit from the macros
+   in m4private.h.  */
+
+/* Given ARGV, return one greater than the number of arguments it
+   describes.  */
+#undef m4_arg_argc
+unsigned int
+m4_arg_argc (m4_macro_args *argv)
+{
+  return argv->argc;
+}
diff --git a/m4/utility.c b/m4/utility.c
index 851da31..53d2a18 100644
--- a/m4/utility.c
+++ b/m4/utility.c
@@ -99,14 +99,15 @@ m4_numeric_arg (m4 *context, const char *caller, const char 
*arg, int *valuep)
    index START, separated by SEP, and quoted by the current quotes, if
    QUOTED is true.  */
 void
-m4_dump_args (m4 *context, m4_obstack *obs, int start,
+m4_dump_args (m4 *context, m4_obstack *obs, unsigned int start,
              m4_macro_args *argv, const char *sep, bool quoted)
 {
-  int i;
+  unsigned int i;
   size_t len = strlen (sep);
   bool need_sep = false;
+  unsigned int argc = m4_arg_argc (argv);
 
-  for (i = start; i < argv->argc; i++)
+  for (i = start; i < argc; i++)
     {
       if (need_sep)
        obstack_grow (obs, sep, len);
diff --git a/modules/evalparse.c b/modules/evalparse.c
index 31e1e4b..e21a081 100644
--- a/modules/evalparse.c
+++ b/modules/evalparse.c
@@ -885,7 +885,8 @@ simple_term (m4 *context, eval_token et, number *v1)
 
 /* Main entry point, called from "eval" and "mpeval" builtins.  */
 void
-m4_evaluate (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv)
+m4_evaluate (m4 *context, m4_obstack *obs, unsigned int argc,
+             m4_macro_args *argv)
 {
   const char * me      = M4ARG (0);
   const char * str     = M4ARG (1);
diff --git a/modules/gnu.c b/modules/gnu.c
index 6fd6623..70c7cf6 100644
--- a/modules/gnu.c
+++ b/modules/gnu.c
@@ -409,14 +409,14 @@ M4BUILTIN_HANDLER (builtin)
   const char *name;
   m4_symbol_value *value;
 
-  if (!m4_is_symbol_value_text (argv->array[1 - 1]))
+  if (!m4_is_arg_text (argv, 1))
     {
-      if (m4_is_symbol_value_func (argv->array[1 - 1])
-         && m4_get_symbol_value_func (argv->array[1 - 1]) == builtin_builtin)
+      assert (m4_is_arg_func (argv, 1));
+      if (m4_arg_func (argv, 1) == builtin_builtin)
        {
          if (m4_bad_argc (context, argc, me, 2, 2, false))
            return;
-         if (!m4_is_symbol_value_text (argv->array[2 - 1]))
+         if (!m4_is_arg_text (argv, 2))
            {
              m4_warn (context, 0, me, _("invalid macro name ignored"));
              return;
@@ -447,8 +447,9 @@ M4BUILTIN_HANDLER (builtin)
                            bp->min_args, bp->max_args,
                            (bp->flags & M4_BUILTIN_SIDE_EFFECT) != 0))
            {
-             int i;
-             /* TODO - make use of $@ reference */
+             unsigned int i;
+             /* TODO - make use of $@ reference.  */
+             /* TODO - add accessor that performs this construction.  */
              m4_macro_args *new_argv;
              new_argv = xmalloc (offsetof (m4_macro_args, array)
                                  + ((argc - 2) * sizeof (m4_symbol_value *)));
@@ -460,8 +461,9 @@ M4BUILTIN_HANDLER (builtin)
                      (argc - 2) * sizeof (m4_symbol_value *));
              if ((bp->flags & M4_BUILTIN_GROKS_MACRO) == 0)
                for (i = 2; i < argc; i++)
-                 if (!m4_is_symbol_value_text (argv->array[i - 1]))
-                   m4_set_symbol_value_text (new_argv->array[i - 2], "");
+                 if (!m4_is_arg_text (argv, i))
+                   m4_set_symbol_value_text (m4_arg_symbol (new_argv, i - 1),
+                                             "");
              bp->func (context, obs, argc - 1, new_argv);
              free (new_argv);
            }
@@ -512,7 +514,7 @@ M4BUILTIN_HANDLER (changesyntax)
 
   if (m4_expand_ranges)
     {
-      int i;
+      unsigned int i;
       for (i = 1; i < argc; i++)
        {
          const char *spec = M4ARG (i);
@@ -679,7 +681,7 @@ M4BUILTIN_HANDLER (format)
 M4BUILTIN_HANDLER (indir)
 {
   const char *me = M4ARG (0);
-  if (!m4_is_symbol_value_text (argv->array[1 - 1]))
+  if (!m4_is_arg_text (argv, 1))
     m4_warn (context, 0, me, _("invalid macro name ignored"));
   else
     {
@@ -690,8 +692,9 @@ M4BUILTIN_HANDLER (indir)
        m4_warn (context, 0, me, _("undefined macro `%s'"), name);
       else
        {
-         int i;
-         /* TODO - make use of $@ reference */
+         unsigned int i;
+         /* TODO - make use of $@ reference.  */
+         /* TODO - add accessor that performs this construction.  */
          m4_macro_args *new_argv;
          new_argv = xmalloc (offsetof (m4_macro_args, array)
                              + ((argc - 2) * sizeof (m4_symbol_value *)));
@@ -703,8 +706,8 @@ M4BUILTIN_HANDLER (indir)
                  (argc - 2) * sizeof (m4_symbol_value *));
          if (!m4_symbol_groks_macro (symbol))
            for (i = 2; i < argc; i++)
-             if (!m4_is_symbol_value_text (argv->array[i - 1]))
-               m4_set_symbol_value_text (new_argv->array[i - 2], "");
+             if (!m4_is_arg_text (argv, i))
+               m4_set_symbol_value_text (m4_arg_symbol (new_argv, i - 1), "");
          m4_macro_call (context, m4_get_symbol_value (symbol), obs,
                         argc - 1, new_argv);
          free (new_argv);
diff --git a/modules/m4.c b/modules/m4.c
index 7c0d8ed..37497e6 100644
--- a/modules/m4.c
+++ b/modules/m4.c
@@ -49,8 +49,9 @@
 
 extern void m4_set_sysval    (int value);
 extern void m4_sysval_flush  (m4 *context, bool report);
-extern void m4_dump_symbols  (m4 *context, m4_dump_symbol_data *data, int argc,
-                             m4_macro_args *argv, bool complain);
+extern void m4_dump_symbols  (m4 *context, m4_dump_symbol_data *data,
+                             unsigned int argc, m4_macro_args *argv,
+                             bool complain);
 extern const char *m4_expand_ranges (const char *s, m4_obstack *obs);
 extern void m4_make_temp     (m4 *context, m4_obstack *obs, const char *macro,
                              const char *name, bool dir);
@@ -160,14 +161,14 @@ M4INIT_HANDLER (m4)
 
 M4BUILTIN_HANDLER (define)
 {
-  if (m4_is_symbol_value_text (argv->array[1 - 1]))
+  if (m4_is_arg_text (argv, 1))
     {
       m4_symbol_value *value = m4_symbol_value_create ();
 
       if (argc == 2)
        m4_set_symbol_value_text (value, xstrdup (""));
       else
-       m4_symbol_value_copy (value, argv->array[2 - 1]);
+       m4_symbol_value_copy (value, m4_arg_symbol (argv, 2));
 
       m4_symbol_define (M4SYMTAB, M4ARG (1), value);
     }
@@ -177,7 +178,7 @@ M4BUILTIN_HANDLER (define)
 
 M4BUILTIN_HANDLER (undefine)
 {
-  int i;
+  unsigned int i;
   for (i = 1; i < argc; i++)
     {
       const char *name = M4ARG (i);
@@ -191,14 +192,14 @@ M4BUILTIN_HANDLER (undefine)
 
 M4BUILTIN_HANDLER (pushdef)
 {
-  if (m4_is_symbol_value_text (argv->array[1 - 1]))
+  if (m4_is_arg_text (argv, 1))
     {
       m4_symbol_value *value = m4_symbol_value_create ();
 
       if (argc == 2)
        m4_set_symbol_value_text (value, xstrdup (""));
       else
-       m4_symbol_value_copy (value, argv->array[2 - 1]);
+       m4_symbol_value_copy (value, m4_arg_symbol (argv, 2));
 
       m4_symbol_pushdef (M4SYMTAB, M4ARG (1), value);
     }
@@ -208,7 +209,7 @@ M4BUILTIN_HANDLER (pushdef)
 
 M4BUILTIN_HANDLER (popdef)
 {
-  int i;
+  unsigned int i;
   for (i = 1; i < argc; i++)
     {
       const char *name = M4ARG (i);
@@ -248,7 +249,7 @@ M4BUILTIN_HANDLER (ifelse)
 {
   const char *me = M4ARG (0);
   const char *result;
-  int index;
+  unsigned int index;
 
   /* The valid ranges of argc for ifelse is discontinuous, we cannot
      rely on the regular mechanisms.  */
@@ -327,7 +328,7 @@ dump_symbol_CB (m4_symbol_table *ignored, const char *name, 
m4_symbol *symbol,
 /* If there are no arguments, build a sorted list of all defined
    symbols, otherwise, only the specified symbols.  */
 void
-m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, int argc,
+m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, unsigned int argc,
                 m4_macro_args *argv, bool complain)
 {
   assert (obstack_object_size (data->obs) == 0);
@@ -337,7 +338,7 @@ m4_dump_symbols (m4 *context, m4_dump_symbol_data *data, 
int argc,
     m4_symtab_apply (M4SYMTAB, false, dump_symbol_CB, data);
   else
     {
-      int i;
+      unsigned int i;
       m4_symbol *symbol;
 
       for (i = 1; i < argc; i++)
@@ -396,7 +397,7 @@ M4BUILTIN_HANDLER (dumpdef)
 M4BUILTIN_HANDLER (defn)
 {
   const char *me = M4ARG (0);
-  int i;
+  unsigned int i;
 
   for (i = 1; i < argc; i++)
     {
@@ -602,7 +603,7 @@ M4BUILTIN_HANDLER (divnum)
 
 M4BUILTIN_HANDLER (undivert)
 {
-  int i = 0;
+  unsigned int i = 0;
   const char *me = M4ARG (0);
 
   if (argc == 1)
@@ -874,7 +875,7 @@ M4BUILTIN_HANDLER (m4wrap)
 
 M4BUILTIN_HANDLER (traceon)
 {
-  int i;
+  unsigned int i;
 
   if (argc == 1)
     m4_set_debug_level_opt (context, (m4_get_debug_level_opt (context)
@@ -887,7 +888,7 @@ M4BUILTIN_HANDLER (traceon)
 /* Disable tracing of all specified macros, or all, if none is specified.  */
 M4BUILTIN_HANDLER (traceoff)
 {
-  int i;
+  unsigned int i;
 
   if (argc == 1)
     m4_set_debug_level_opt (context, (m4_get_debug_level_opt (context)
diff --git a/modules/m4.h b/modules/m4.h
index 4214aed..4783d0a 100644
--- a/modules/m4.h
+++ b/modules/m4.h
@@ -39,7 +39,7 @@ typedef struct
 typedef void m4_sysval_flush_func (m4 *context, bool report);
 typedef void m4_set_sysval_func (int value);
 typedef void m4_dump_symbols_func (m4 *context, m4_dump_symbol_data *data,
-                                  int argc, m4_macro_args *argv,
+                                  unsigned int argc, m4_macro_args *argv,
                                   bool complain);
 typedef const char *m4_expand_ranges_func (const char *s, m4_obstack *obs);
 typedef void m4_make_temp_func (m4 *context, m4_obstack *obs,
diff --git a/modules/perl.c b/modules/perl.c
index 9c28b0a..58161b1 100644
--- a/modules/perl.c
+++ b/modules/perl.c
@@ -41,14 +41,14 @@
   BUILTIN (perleval,   false,  false,  false,  0,      -1  )   \
 
 
-#define BUILTIN(handler, macros, blind, side, min, max)  M4BUILTIN(handler)
+#define BUILTIN(handler, macros, blind, side, min, max)  M4BUILTIN (handler)
   builtin_functions
 #undef BUILTIN
 
 m4_builtin m4_builtin_table[] =
 {
 #define BUILTIN(handler, macros, blind, side, min, max)        \
-  { CONC(builtin_, handler), STR(handler),             \
+  { CONC (builtin_, handler), STR (handler),           \
     ((macros ? M4_BUILTIN_GROKS_MACRO : 0)             \
      | (blind ? M4_BUILTIN_BLIND : 0)                  \
      | (side ? M4_BUILTIN_SIDE_EFFECT : 0)),           \
@@ -114,15 +114,15 @@ M4FINISH_HANDLER (perl)
 M4BUILTIN_HANDLER (perleval)
 {
   SV *val;
-  int i;
+  unsigned int i;
 
   for (i = 1; i < argc; i++)
     {
       if (i > 1)
        obstack_1grow (obs, ',');
 
-      val = perl_eval_pv(M4ARG(i), true);
+      val = perl_eval_pv (M4ARG (i), true);
 
-      m4_shipout_string(context, obs, SvPV(val,PL_na), 0, false);
+      m4_shipout_string (context, obs, SvPV (val, PL_na), 0, false);
     }
 }
-- 
1.5.3.5

>From 07450b93026ee774ae5e13f98ebd6e61d8021245 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Fri, 19 Oct 2007 08:21:56 -0600
Subject: [PATCH] Stage 2: use accessors, not direct reference, into argv.

* src/m4.h (TOKEN_EOF): Alter value, to ease debugging.
(arg_argc, arg_type, arg_text, arg_len, arg_func): New
prototypes.
* src/macro.c (arg_argc, arg_type, arg_text, arg_len, arg_func):
New accessor functions.
* src/builtin.c (ARG, dump_args, define_macro, m4_builtin)
(m4_indir): Use new accessors.
* src/debug.c (trace_pre): Likewise.
* src/format.c (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE):
Likewise.

(cherry picked from commit 2b4dfe438ea9f2fc8722e6e5d94a28d3f6913060)

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog     |   14 ++++++++++++
 src/builtin.c |   38 +++++++++++++++++++--------------
 src/debug.c   |    6 ++--
 src/format.c  |    8 +++---
 src/m4.h      |   56 ++++++++++++++++++++++++++++++-------------------
 src/macro.c   |   64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 6 files changed, 139 insertions(+), 47 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 14deb9f..3301ac0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2007-11-27  Eric Blake  <address@hidden>
+
+       Stage 2: use accessors, not direct reference, into argv.
+       * src/m4.h (TOKEN_EOF): Alter value, to ease debugging.
+       (arg_argc, arg_type, arg_text, arg_len, arg_func): New
+       prototypes.
+       * src/macro.c (arg_argc, arg_type, arg_text, arg_len, arg_func):
+       New accessor functions.
+       * src/builtin.c (ARG, dump_args, define_macro, m4_builtin)
+       (m4_indir): Use new accessors.
+       * src/debug.c (trace_pre): Likewise.
+       * src/format.c (ARG_INT, ARG_LONG, ARG_STR, ARG_DOUBLE):
+       Likewise.
+
 2007-11-24  Eric Blake  <address@hidden>
 
        Stage 1: convert token_data** into new object.
diff --git a/src/builtin.c b/src/builtin.c
index eb66465..fbfc2fe 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -30,9 +30,9 @@
 # include <sys/wait.h>
 #endif
 
-#define ARG(i)                                                         \
-  ((i) == 0 ? argv->argv0                                              \
-   : argv->argc > (i) ? TOKEN_DATA_TEXT (argv->array[(i) - 1]) : "")
+/* Grab the text at argv index I.  Assumes a macro_argument *argv is
+   in scope.  */
+#define ARG(i) arg_text (argv, i)
 
 /* Initialization of builtin and predefined macros.  The table
    "builtin_tab" is both used for initialization, and by the "builtin"
@@ -598,20 +598,22 @@ shipout_int (struct obstack *obs, int val)
   obstack_grow (obs, s, strlen (s));
 }
 
-/*----------------------------------------------------------------------.
-| Print ARGC arguments from the table ARGV to obstack OBS, separated by |
-| SEP, and quoted by the current quotes, if QUOTED is true.            |
-`----------------------------------------------------------------------*/
+/*------------------------------------------------------------------.
+| Print arguments from the table ARGV to obstack OBS, starting with |
+| START, separated by SEP, and quoted by the current quotes if     |
+| QUOTED is true.                                                  |
+`------------------------------------------------------------------*/
 
 static void
 dump_args (struct obstack *obs, int start, macro_arguments *argv,
           const char *sep, bool quoted)
 {
-  int i;
+  unsigned int i;
   bool dump_sep = false;
   size_t len = strlen (sep);
+  unsigned int argc = arg_argc (argv);
 
-  for (i = start; i < argv->argc; i++)
+  for (i = start; i < argc; i++)
     {
       if (dump_sep)
        obstack_grow (obs, sep, len);
@@ -655,7 +657,7 @@ define_macro (int argc, macro_arguments *argv, 
symbol_lookup mode)
   if (bad_argc (me, argc, 1, 2))
     return;
 
-  if (TOKEN_DATA_TYPE (argv->array[0]) != TOKEN_TEXT)
+  if (arg_type (argv, 1) != TOKEN_TEXT)
     {
       m4_warn (0, me, _("invalid macro name ignored"));
       return;
@@ -667,14 +669,14 @@ define_macro (int argc, macro_arguments *argv, 
symbol_lookup mode)
       return;
     }
 
-  switch (TOKEN_DATA_TYPE (argv->array[1]))
+  switch (arg_type (argv, 2))
     {
     case TOKEN_TEXT:
       define_user_macro (ARG (1), ARG (2), mode);
       break;
 
     case TOKEN_FUNC:
-      bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv->array[1]));
+      bp = find_builtin_by_addr (arg_func (argv, 2));
       if (bp == NULL)
        return;
       else
@@ -915,7 +917,7 @@ m4_builtin (struct obstack *obs, int argc, macro_arguments 
*argv)
 
   if (bad_argc (me, argc, 1, -1))
     return;
-  if (TOKEN_DATA_TYPE (argv->array[0]) != TOKEN_TEXT)
+  if (arg_type (argv, 1) != TOKEN_TEXT)
     {
       m4_warn (0, me, _("invalid macro name ignored"));
       return;
@@ -929,6 +931,8 @@ m4_builtin (struct obstack *obs, int argc, macro_arguments 
*argv)
     {
       int i;
       /* TODO make use of $@ reference, instead of copying argv.  */
+      /* TODO make accessor in macro.c that performs this
+        construction, so that argv can be opaque type.  */
       macro_arguments *new_argv = xmalloc (offsetof (macro_arguments, array)
                                           + ((argc - 2)
                                              * sizeof (token_data *)));
@@ -940,7 +944,7 @@ m4_builtin (struct obstack *obs, int argc, macro_arguments 
*argv)
              (argc - 2) * sizeof (token_data *));
       if (!bp->groks_macro_args)
        for (i = 2; i < argc; i++)
-         if (TOKEN_DATA_TYPE (new_argv->array[i - 2]) != TOKEN_TEXT)
+         if (arg_type (argv, i) != TOKEN_TEXT)
            {
              TOKEN_DATA_TYPE (new_argv->array[i - 2]) = TOKEN_TEXT;
              TOKEN_DATA_TEXT (new_argv->array[i - 2]) = (char *) "";
@@ -966,7 +970,7 @@ m4_indir (struct obstack *obs, int argc, macro_arguments 
*argv)
 
   if (bad_argc (me, argc, 1, -1))
     return;
-  if (TOKEN_DATA_TYPE (argv->array[0]) != TOKEN_TEXT)
+  if (arg_type (argv, 1) != TOKEN_TEXT)
     {
       m4_warn (0, me, _("invalid macro name ignored"));
       return;
@@ -980,6 +984,8 @@ m4_indir (struct obstack *obs, int argc, macro_arguments 
*argv)
     {
       int i;
       /* TODO make use of $@ reference, instead of copying argv.  */
+      /* TODO make accessor in macro.c that performs this
+        construction, so that argv can be opaque type.  */
       macro_arguments *new_argv = xmalloc (offsetof (macro_arguments, array)
                                           + ((argc - 2)
                                              * sizeof (token_data *)));
@@ -991,7 +997,7 @@ m4_indir (struct obstack *obs, int argc, macro_arguments 
*argv)
              (argc - 2) * sizeof (token_data *));
       if (!SYMBOL_MACRO_ARGS (s))
        for (i = 2; i < argc; i++)
-         if (TOKEN_DATA_TYPE (new_argv->array[i - 2]) != TOKEN_TEXT)
+         if (arg_type (argv, i) != TOKEN_TEXT)
            {
              TOKEN_DATA_TYPE (new_argv->array[i - 2]) = TOKEN_TEXT;
              TOKEN_DATA_TEXT (new_argv->array[i - 2]) = (char *) "";
diff --git a/src/debug.c b/src/debug.c
index e5bd280..c4f701d 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -379,14 +379,14 @@ trace_pre (const char *name, int id, int argc, 
macro_arguments *argv)
          if (i != 1)
            trace_format (", ");
 
-         switch (TOKEN_DATA_TYPE (argv->array[i - 1]))
+         switch (arg_type (argv, i))
            {
            case TOKEN_TEXT:
-             trace_format ("%l%S%r", TOKEN_DATA_TEXT (argv->array[i - 1]));
+             trace_format ("%l%S%r", arg_text (argv, i));
              break;
 
            case TOKEN_FUNC:
-             bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv->array[i - 1]));
+             bp = find_builtin_by_addr (arg_func (argv, i));
              if (bp == NULL)
                {
                  assert (!"trace_pre");
diff --git a/src/format.c b/src/format.c
index 4c2b60a..7fc8fb1 100644
--- a/src/format.c
+++ b/src/format.c
@@ -28,16 +28,16 @@
    same size; likewise for long and unsigned long.  */
 
 #define ARG_INT(i, argc, argv)                                         \
-       ((i == argc) ? 0 : atoi (TOKEN_DATA_TEXT (argv->array[i++ - 1])))
+  ((i == argc) ? 0 : atoi (arg_text (argv, i++)))
 
 #define ARG_LONG(i, argc, argv)                                                
\
-       ((i == argc) ? 0L : atol (TOKEN_DATA_TEXT (argv->array[i++ - 1])))
+  ((i == argc) ? 0L : atol (arg_text (argv, i++)))
 
 #define ARG_STR(i, argc, argv)                                         \
-       ((i == argc) ? "" : TOKEN_DATA_TEXT (argv->array[i++ - 1]))
+  ((i == argc) ? "" : arg_text (argv, i++))
 
 #define ARG_DOUBLE(i, argc, argv)                                      \
-       ((i == argc) ? 0.0 : atof (TOKEN_DATA_TEXT (argv->array[i++ - 1])))
+  ((i == argc) ? 0.0 : atof (arg_text (argv, i++)))
 
 
 /*------------------------------------------------------------------.
diff --git a/src/m4.h b/src/m4.h
index 94276e9..522bda2 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -178,7 +178,7 @@ extern FILE *debug;
 /* default flags -- equiv: aeq */
 #define DEBUG_TRACE_DEFAULT 7
 
-#define DEBUG_PRINT1(Fmt, Arg1) \
+#define DEBUG_PRINT1(Fmt, Arg1)                                 \
   do                                                           \
     {                                                          \
       if (debug != NULL)                                       \
@@ -186,7 +186,7 @@ extern FILE *debug;
     }                                                          \
   while (0)
 
-#define DEBUG_PRINT3(Fmt, Arg1, Arg2, Arg3) \
+#define DEBUG_PRINT3(Fmt, Arg1, Arg2, Arg3)                     \
   do                                                           \
     {                                                          \
       if (debug != NULL)                                       \
@@ -194,7 +194,7 @@ extern FILE *debug;
     }                                                          \
   while (0)
 
-#define DEBUG_MESSAGE(Fmt) \
+#define DEBUG_MESSAGE(Fmt)                                      \
   do                                                           \
     {                                                          \
       if (debug != NULL)                                       \
@@ -206,7 +206,7 @@ extern FILE *debug;
     }                                                          \
   while (0)
 
-#define DEBUG_MESSAGE1(Fmt, Arg1) \
+#define DEBUG_MESSAGE1(Fmt, Arg1)                               \
   do                                                           \
     {                                                          \
       if (debug != NULL)                                       \
@@ -218,7 +218,7 @@ extern FILE *debug;
     }                                                          \
   while (0)
 
-#define DEBUG_MESSAGE2(Fmt, Arg1, Arg2) \
+#define DEBUG_MESSAGE2(Fmt, Arg1, Arg2)                         \
   do                                                           \
     {                                                          \
       if (debug != NULL)                                       \
@@ -244,37 +244,39 @@ void trace_post (const char *, int, int, macro_arguments 
*, const char *);
 
 typedef struct token_chain token_chain;
 
-/* Various different token types.  */
+/* Various different token types.  Avoid overlap with token_data_type,
+   since the shared prefix of the enumerators is a bit confusing.  */
 enum token_type
 {
-  TOKEN_EOF,                   /* end of file */
-  TOKEN_STRING,                        /* a quoted string or comment */
-  TOKEN_WORD,                  /* an identifier */
-  TOKEN_OPEN,                  /* ( */
-  TOKEN_COMMA,                 /* , */
-  TOKEN_CLOSE,                 /* ) */
-  TOKEN_SIMPLE,                        /* any other single character */
-  TOKEN_MACDEF                 /* a macro's definition (see "defn") */
+  TOKEN_EOF = 4,/* End of file, TOKEN_VOID.  */
+  TOKEN_STRING,        /* Quoted string or comment, TOKEN_TEXT or TOKEN_COMP.  
*/
+  TOKEN_WORD,  /* An identifier, TOKEN_TEXT.  */
+  TOKEN_OPEN,  /* Active character `(', TOKEN_TEXT.  */
+  TOKEN_COMMA, /* Active character `,', TOKEN_TEXT.  */
+  TOKEN_CLOSE, /* Active character `)', TOKEN_TEXT.  */
+  TOKEN_SIMPLE,        /* Any other single character, TOKEN_TEXT.  */
+  TOKEN_MACDEF /* A macro's definition (see "defn"), TOKEN_FUNC.  */
 };
 
 /* The data for a token, a macro argument, and a macro definition.  */
 enum token_data_type
 {
-  TOKEN_VOID, /* Token still being constructed, u is invalid.  */
-  TOKEN_TEXT, /* Straight text, u.u_t is valid.  */
-  TOKEN_FUNC, /* Builtin function definition, u.func is valid.  */
-  TOKEN_COMP  /* Composite argument, u.chain is valid.  */
+  TOKEN_VOID,  /* Token still being constructed, u is invalid.  */
+  TOKEN_TEXT,  /* Straight text, u.u_t is valid.  */
+  TOKEN_FUNC,  /* Builtin function definition, u.func is valid.  */
+  TOKEN_COMP   /* Composite argument, u.chain is valid.  */
 };
 
 /* Composite tokens are built of a linked list of chains.  */
 struct token_chain
 {
-  token_chain *next; /* Pointer to next link of chain.  */
-  char *str; /* NUL-terminated string if text, else NULL.  */
-  macro_arguments *argv; /* Reference to earlier address@hidden  */
-  unsigned int index; /* Index within argv to start reading from.  */
+  token_chain *next;   /* Pointer to next link of chain.  */
+  char *str;           /* NUL-terminated string if text, else NULL.  */
+  macro_arguments *argv;/* Reference to earlier address@hidden  */
+  unsigned int index;  /* Argument index within argv.  */
 };
 
+/* The content of a token or macro argument.  */
 struct token_data
 {
   enum token_data_type type;
@@ -297,6 +299,9 @@ struct token_data
   u;
 };
 
+/* TODO - make this struct opaque, and move definition to macro.c.  */
+/* Opaque structure describing all arguments to a macro, including the
+   macro name at index 0.  */
 struct macro_arguments
 {
   /* Number of arguments owned by this object, may be larger than
@@ -427,6 +432,13 @@ extern int expansion_level;
 
 void expand_input (void);
 void call_macro (symbol *, int, macro_arguments *, struct obstack *);
+
+unsigned int arg_argc (macro_arguments *);
+token_data_type arg_type (macro_arguments *, unsigned int);
+const char *arg_text (macro_arguments *, unsigned int);
+size_t arg_len (macro_arguments *, unsigned int);
+builtin_func *arg_func (macro_arguments *, unsigned int);
+
 
 /* File: builtin.c  --- builtins.  */
 
diff --git a/src/macro.c b/src/macro.c
index d2f2cb7..9997ce1 100644
--- a/src/macro.c
+++ b/src/macro.c
@@ -242,7 +242,7 @@ expand_argument (struct obstack *obs, token_data *argp, 
const char *caller)
 `-------------------------------------------------------------------------*/
 
 static macro_arguments *
-collect_arguments (symbol *sym, struct obstack *argptr, unsigned argv_base,
+collect_arguments (symbol *sym, struct obstack *argptr, unsigned int argv_base,
                   struct obstack *arguments)
 {
   token_data td;
@@ -327,7 +327,7 @@ static void
 expand_macro (symbol *sym)
 {
   struct obstack arguments;    /* Alternate obstack if argc_stack is busy.  */
-  unsigned argv_base;          /* Size of argv_stack on entry.  */
+  unsigned int argv_base;      /* Size of argv_stack on entry.  */
   void *argc_start;            /* Start of argc_stack, else NULL if unsafe.  */
   macro_arguments *argv;
   int argc;
@@ -409,3 +409,63 @@ expand_macro (symbol *sym)
     obstack_free (&arguments, NULL);
   obstack_blank (&argv_stack, argv_base - obstack_object_size (&argv_stack));
 }
+
+
+/* Given ARGV, return how many arguments it refers to.  */
+unsigned int
+arg_argc (macro_arguments *argv)
+{
+  return argv->argc;
+}
+
+/* Given ARGV, return the type of argument INDEX.  Index 0 is always
+   text, and indices beyond argc are likewise treated as text.  */
+token_data_type
+arg_type (macro_arguments *argv, unsigned int index)
+{
+  if (index == 0 || index >= argv->argc)
+    return TOKEN_TEXT;
+  return TOKEN_DATA_TYPE (argv->array[index - 1]);
+}
+
+/* Given ARGV, return the text at argument INDEX, or NULL if the
+   argument is not text.  Index 0 is always text, and indices beyond
+   argc return the empty string.  */
+const char *
+arg_text (macro_arguments *argv, unsigned int index)
+{
+  if (index == 0)
+    return argv->argv0;
+  if (index >= argv->argc)
+    return "";
+  if (TOKEN_DATA_TYPE (argv->array[index - 1]) != TOKEN_TEXT)
+    return NULL;
+  return TOKEN_DATA_TEXT (argv->array[index - 1]);
+}
+
+/* Given ARGV, return the length of argument INDEX, or SIZE_MAX if the
+   argument is not text.  Indices beyond argc return 0.  */
+size_t
+arg_len (macro_arguments *argv, unsigned int index)
+{
+  /* TODO - update macro_arguments to cache this.  */
+  if (index == 0)
+    return strlen (argv->argv0);
+  if (index >= argv->argc)
+    return 0;
+  if (TOKEN_DATA_TYPE (argv->array[index - 1]) != TOKEN_TEXT)
+    return SIZE_MAX;
+  return strlen (TOKEN_DATA_TEXT (argv->array[index - 1]));
+}
+
+/* Given ARGV, return the builtin function referenced by argument
+   INDEX, or NULL if it is not a builtin.  Index 0, and indices beyond
+   argc, return NULL.  */
+builtin_func *
+arg_func (macro_arguments *argv, unsigned int index)
+{
+  if (index == 0 || index >= argv->argc
+      || TOKEN_DATA_TYPE (argv->array[index - 1]) != TOKEN_FUNC)
+    return NULL;
+  return TOKEN_DATA_FUNC (argv->array[index - 1]);
+}
-- 
1.5.3.5


reply via email to

[Prev in Thread] Current Thread [Next in Thread]