aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2022-03-29 08:10:54 -0700
committerKaz Kylheku <kaz@kylheku.com>2022-03-29 08:10:54 -0700
commit86d864b874e6c2ef45a7a208ed69a5d156406324 (patch)
tree7f19c7f93217d1e0afe7146db6d24f6ad6ca88a5
parent4955c31e0b97fa5d0ac866df2db08bb390845a30 (diff)
downloadcppawk-86d864b874e6c2ef45a7a208ed69a5d156406324.tar.gz
cppawk-86d864b874e6c2ef45a7a208ed69a5d156406324.tar.bz2
cppawk-86d864b874e6c2ef45a7a208ed69a5d156406324.zip
Add cppawk-narg man page.
-rw-r--r--cppawk-narg.1295
-rw-r--r--cppawk.16
2 files changed, 301 insertions, 0 deletions
diff --git a/cppawk-narg.1 b/cppawk-narg.1
new file mode 100644
index 0000000..f802f39
--- /dev/null
+++ b/cppawk-narg.1
@@ -0,0 +1,295 @@
+.TH CPPAWK-NARG 1 "29 March 2022" "cppawk Libraries" "Case Macro"
+
+.SH NAME
+narg \- macros for writing variable argument macros
+
+.SH SYNOPSIS
+ #include <narg.h>
+
+ #define narg(...)
+ #define splice(args)
+ #define varexpand(first_mac, rest_mac, ...)
+ #define revarg(...)
+
+.SH DESCRIPTION
+The
+.B <narg.h>
+header provides several macros which are useful to macro writers.
+In particular, these macros make it easy to develop variable argument
+macros which take one or more argument, and have complex expansions.
+
+In this manual, the
+.BI ->
+(arrow) notation means "expands to". For instance
+
+ foo(bar) -> 42 // the macro call foo(bar) expands to 42
+
+A description of each macro follows:
+
+.IP narg
+This macro takes one or more arguments, and expands to a decimal integer which
+indicates how many arguments there are.
+
+ narg(x) -> 1
+ narg(x, y) -> 2
+ narg(x, y, z) -> 3
+
+The
+.BI narg
+macro can be called with up to 32 (thirty-two) arguments. If it is called
+with between 33 to 48 arguments, it expands to an unspecified token
+sequence which generates a syntax error in Awk. The token sequence begins
+with an identifier and therefore may appear as the right operand of the
+token-pasting
+.BI ##
+operator, opposite to an identifier token.
+
+If more than 48 arguments are given, the behavior is unspecified.
+
+.IP splice
+The
+.BI splice
+macro provides a shim for inserting a parenthesized argument into
+a macro expansion, such that the argument turns into individual
+arguments. Suppose we have a macro like this:
+
+ #define vmac(a, b, ...) ...
+
+For some reason, we need to write fixed a macro like this:
+
+ #define fmac(x, y, args) vmac(x, y, ???)
+
+where the
+.BI args
+argument is a parenthesized list of arguments that must become the
+.BI ...
+argument of the
+.BI vmac
+macro. That is to say,
+.BI fmac
+is to be invoked like this, with the indicated expansion:
+
+ fmac(1, 2, (3, 4, 5)) -> vmac(1, 2, 3, 4, 5)
+
+The
+.BI splice
+macro solves the question of what to write into the position
+indicated by the ??? question marks to achieve this:
+
+ #define fmac(x, y, args) vmac(x, y, splice(args))
+
+Example: produce the following macro:
+
+ csum((a, b, c), (x, y)) -> (sqrt(sumsq(a, b, c)) +
+ sqrt(sumsq(x, y)))
+
+This is a trick example:
+.BI splice
+is not required at all:
+
+ #define csum(left, right) (sqrt(sumsq left) + \e
+ sqrt(sumsq right))
+
+The
+.BI splice
+macro is not required because the parenthesized arguments constitute
+the entire argument list of
+.BI sumsq.
+However, suppose the requirement is this, requiring the parenthesized
+arguments to be inserted into an argument list containing other arguments:
+
+ csum(t, (a, b, c), (x, y)) -> (sqrt(sumsq(t, a, b, c)) +
+ sqrt(sumsq(t, x, y)))
+
+Now we need:
+
+ #define csum(parm, left, right) (sqrt(sumsq(parm, \e
+ splice(left)) + \e
+ sumsq(parm, \e
+ splice(right))))
+
+.IP revarg
+This macro expands to a comma-separated list of its arguments, which
+appear in reverse.
+
+ revarg(1) -> 1
+ revarg(1, 2) -> 2, 1
+ revarg(1, 2, 3) -> 3, 2, 1
+
+Like
+.BI narg,
+the
+.BI revarg
+macro can be called with up to 32 arguments, beyond which there is
+some overflow detection up to 48 arguments, followed by unspecified behavior
+for 49 or more arguments.
+
+.IP varexpand
+The most complex macro in the
+.B <narg.h>
+header is
+.BI varexpand.
+
+This macro is used for writing variadic macros with complex expansions,
+using a compact specification.
+
+The
+.BI varexpand
+macro uses "higher order macro" programming: it has arguments which are
+themselves macros. To understand
+.BI varexpand
+it helps to understand the Lisp
+.BI reduce
+function, or the similar
+.BI fold
+function found in functional languages. Recall that the prototype of the
+.BI varexpand
+macro is this:
+
+ #define varexpand(first_mac, rest_mac, ...)
+
+To use
+.BI varexpand
+you must first write two macros: a one-argument macro whose name is passed
+as the
+.BI first_mac
+argument, and two argument macro to be used as the
+.BI rest_mac
+argument.
+
+Most variadic macros written with
+.BI varexpand
+will pass through their
+.BI __VA_ARGS__
+list as the
+.BI ...
+parameter; however, the
+.BI splice
+macro can also be used to place parenthesized argument lists
+into that position
+
+Up to 32 variadic arguments are accepted by
+.BI varexpand
+beyond which there is overflow detection up to 48 arguments,
+followed by unspecified behavior for 49 or more arguments.
+
+Example: suppose we want to write a macro with an expansion like this:
+
+ add(1) -> 1
+ add(1, 2) -> 1 + 2
+ add(1, 2, 3) -> 1 + 2 + 3
+
+First, we must write a macro for handling the base case of the induction, which
+is used for the leftmost argument. The expansion is trivial:
+
+ #define add_first(x) x
+
+The second macro is more complex. It takes two arguments. The left argument is
+the accumulated expansion so far, of all the arguments previous to that
+argument. The right argument is the next argument to be added to the expansion.
+
+ #define add_next(acc, x) acc + x
+
+For instance, if the arguments 1, 2 have already been expanded to 1 + 2
+and the next argument is 3, then
+.BI acc
+takes on the tokens 1 + 2, and
+.BI x
+takes on 3. Thus the expansion is:
+
+ add_next(1 + 2, 3) -> 1 + 2 + 3
+
+With these two macros, we can then write
+.BI add
+like this:
+
+ #define add(...) varexpand(add_first, add_next, __VA_ARGS__)
+
+More complex example: suppose we want an inline sum-of-squares macro
+which works like this:
+
+ sumsq(x) -> ((x)*(x))
+ sumsq(x, y, z) -> ((x)*(x) + (y)*(y) + (z)*(z))
+
+Note the detail that there are outer parentheses around the entire
+expansion, but the individual terms are not parenthesized, only
+the products. We write the helper macros like this:
+
+ #define sumsq_first(x) (x)*(x)
+ #define sumsq_next(a, x) a + sumsq_first(x)
+
+Note that
+.BI sumsq_next
+reuses
+.BI sumsq_first
+to avoid repeating the (x)*(x) term. Then we complete the implementation:
+
+ #define sumsq(...) (varexpand(sumsq_first, \e
+ sumsq_next,\e
+ __VA_ARGS__))
+
+The outer parentheses are written around the
+.BI varexpand
+call. In general,
+.BI varexpand
+can be just a small component of a larger macro expansion, and
+can be used more than one time in a macro expansion.
+
+Example:
+.BI rlist
+macro which generates a left-associative nested expression, like this:
+
+ rlist(1) -> cons(1, nil)
+ rlist(1, 2) -> cons(2, cons(1, nil))
+ rlist(1, 2, 3) -> cons(3, cons(2, cons(1, nil)))
+
+Implementation:
+
+ #define rlist_first(x) cons(x, nil)
+ #define rlist_next(a, x) cons(x, a)
+
+ #define rlist(...) varexpand(rlist_first, rlist_next, \e
+ __VA_ARGS__)
+
+What if we want the consing to produce the list in order via right
+association, rather than in reverse? So that is to say:
+
+ list(1, 2, 3) -> cons(1, cons(2, cons(3, nil)))
+
+Here we simply take advantage of the
+.BI revarg
+macro to reverse the arguments:
+
+ #define list(...) rlist(revarg(__VA_ARG__))
+
+.SH BUGS
+As noted in the DESCRIPTION, the
+.BI narg,
+.BI revarg,
+and
+.BI varexpand
+macros are limited to handling 32 variadic arguments, beyond
+which there is a 16 argument safety margin with error detection,
+followed by unspecified behavior.
+
+The C preprocessor doesn't support macro recursion, which forbids
+some complex uses of
+.BI varexpand
+whereby the
+.BI first_mac
+and
+.BI next_mac
+macros themselves make use of
+.BI varexpand.
+A possible workaround is to clone the implementation of
+.BI varexpand
+to produce an identical macro called
+.BI varexpand2.
+This then allows for two "recursion" levels, whereby each one uses
+the macro under a different name.
+
+.SH AUTHOR
+Kaz Kylheku <kaz@kylheku.com>
+
+.SH COPYRIGHT
+Copyright 2022, BSD2 License.
diff --git a/cppawk.1 b/cppawk.1
index aa64501..d2da42a 100644
--- a/cppawk.1
+++ b/cppawk.1
@@ -190,6 +190,12 @@ points the preprocessor to look for
files in its own directory, which contains a library of header files that accompany
.BR cppawk .
+.IR <narg.h>
+This header provides macros which make it easy to write variable-argument
+macros with complex expansions. This is documented in the
+.B cppawk-narg
+manual page.
+
.IR <case.h>
This header provides macros for writing a
.BI case