diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2022-03-29 08:10:54 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2022-03-29 08:10:54 -0700 |
commit | 86d864b874e6c2ef45a7a208ed69a5d156406324 (patch) | |
tree | 7f19c7f93217d1e0afe7146db6d24f6ad6ca88a5 | |
parent | 4955c31e0b97fa5d0ac866df2db08bb390845a30 (diff) | |
download | cppawk-86d864b874e6c2ef45a7a208ed69a5d156406324.tar.gz cppawk-86d864b874e6c2ef45a7a208ed69a5d156406324.tar.bz2 cppawk-86d864b874e6c2ef45a7a208ed69a5d156406324.zip |
Add cppawk-narg man page.
-rw-r--r-- | cppawk-narg.1 | 295 | ||||
-rw-r--r-- | cppawk.1 | 6 |
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. @@ -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 |