fix: noconfirm auto-selects first AUR match
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
The files in this directory provide example uses of GNU M4.
|
||||
The following copyright notice applies to each of these
|
||||
description files, except for barem4.m4 and testbarem4.m4
|
||||
(which have their own license included).
|
||||
|
||||
Copyright (C) 2006, 2010-2012 Free Software Foundation, Inc.
|
||||
This file is free software; the Free Software Foundation
|
||||
gives unlimited permission to copy and/or distribute it,
|
||||
with or without modifications, as long as this notice is preserved.
|
||||
@@ -0,0 +1,71 @@
|
||||
## Makefile.am - template for generating Makefile via Automake
|
||||
##
|
||||
## Copyright (C) 2006-2014, 2016-2017, 2020-2026 Free Software
|
||||
## Foundation, Inc.
|
||||
##
|
||||
## This file is part of GNU M4.
|
||||
##
|
||||
## GNU M4 is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
## the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## GNU M4 is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
##
|
||||
## This file written by Eric Blake <ebb9@byu.net>
|
||||
|
||||
EXTRA_DIST =\
|
||||
barem4.m4 \
|
||||
capitalize.m4 \
|
||||
capitalize2.m4 \
|
||||
comments.m4 \
|
||||
curry.m4 \
|
||||
ddivert.m4 \
|
||||
debug.m4 \
|
||||
esyscmd.m4 \
|
||||
exp.m4 \
|
||||
file.m4 \
|
||||
foo \
|
||||
foreach.m4 \
|
||||
foreach2.m4 \
|
||||
foreachq.m4 \
|
||||
foreachq2.m4 \
|
||||
foreachq3.m4 \
|
||||
foreachq4.m4 \
|
||||
forloop.m4 \
|
||||
forloop2.m4 \
|
||||
forloop3.m4 \
|
||||
fstab.m4 \
|
||||
hanoi.m4 \
|
||||
incl-test.m4 \
|
||||
incl.m4 \
|
||||
include.m4 \
|
||||
indir.m4 \
|
||||
join.m4 \
|
||||
loop.m4 \
|
||||
misc.m4 \
|
||||
multiquotes.m4 \
|
||||
patsubst.m4 \
|
||||
pushpop.m4 \
|
||||
quote.m4 \
|
||||
regexp.m4 \
|
||||
reverse.m4 \
|
||||
stack.m4 \
|
||||
stack_sep.m4 \
|
||||
sync-lines.m4 \
|
||||
sysv-args.m4 \
|
||||
testbarem4.m4 \
|
||||
trace.m4 \
|
||||
translit.m4 \
|
||||
undivert.incl \
|
||||
undivert.m4 \
|
||||
wrap.m4 \
|
||||
wrapfifo.m4 \
|
||||
wraplifo.m4 \
|
||||
wraplifo2.m4
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,939 @@
|
||||
define(`_',`dnl')_ unobtrusive dnl, used for readability
|
||||
define(`__',`')_ indentation macros
|
||||
define(`____',`')_ think of them as spaces that can't
|
||||
define(`______',`')_ stick around and get in the way
|
||||
_
|
||||
_ This program is distributed under a Creative Commons
|
||||
_ Share-alike license. An accompanying suite of tests
|
||||
_ may be run thus: "m4 barem4.m4 testbarem4.m4".
|
||||
_
|
||||
_
|
||||
_ Pure macros as a programming language
|
||||
_
|
||||
_ m4 is Turing complete even when stripped to the bare minimum
|
||||
_ of one builtin: `define'. This is not news; Christopher
|
||||
_ Strachey demonstrated it in his ancestral GPM, described in
|
||||
_ "A general- purpose macrogenerator", The Computer Journal 8
|
||||
_ (1965) 225-241.
|
||||
_
|
||||
_ This m4 program more fully illustrates universality by
|
||||
_ building familiar programming capabilities: unlimited
|
||||
_ precision integer arithmetic, boolean algebra, conditional
|
||||
_ execution, case-switching, and some higher-level operators
|
||||
_ from functional programming. In support of these normal
|
||||
_ facilities, however, the program exploits some unusual
|
||||
_ programming idioms:
|
||||
_
|
||||
_ 1. Case-switching via macro names constructed on the fly.
|
||||
_ 2. Equality testing by redefining macros.
|
||||
_ 3. Representing data structures by nested parenthesized lists.
|
||||
_ 4. Using macros as associative memory.
|
||||
_ 5. Inserting nested parameter symbols on the fly.
|
||||
_
|
||||
_ Idioms 2 and 5 are "reflective": the program writes code
|
||||
_ for itself.
|
||||
_
|
||||
_ Note. There is a `dnl' on every line of this program. The
|
||||
_ indulgence of using this builtin is logically unnecessary.
|
||||
_ Stripped of `dnl's and indentation macros, the program would
|
||||
_ consist entirely of `define's with no comments or line breaks.
|
||||
_ It would be less than 1/4 the current size and would yield the
|
||||
_ same results, but would be inscrutable.
|
||||
_
|
||||
_ In examples of stepwise macro expansion, all occurrences of
|
||||
_ `dnl' and indentation have been elided.
|
||||
_
|
||||
_ For consistency, we quote all arguments of `define' unless
|
||||
_ they contain macro calls that must be expanded at the
|
||||
_ time of definition.
|
||||
_
|
||||
_ Case switch (Idiom 1)
|
||||
_
|
||||
_ An essential feature of programming is conditional execution.
|
||||
_ Suppose there are predicates that yield `T' or `F' for true
|
||||
_ or false. A conditional switch of the form
|
||||
_
|
||||
_ if(<T or F>, `<T action>', `<F action>')
|
||||
_
|
||||
_ constructs calls for `if_T', or `if_F' that select the
|
||||
_ appropriate action:
|
||||
_
|
||||
define(`if', `if_$1(`$2', `$3')')_
|
||||
define(`if_T', `$1')_
|
||||
define(`if_F', `$2')_
|
||||
_
|
||||
_ Example
|
||||
_
|
||||
_ if(T,`yes',`no') => if_T(`yes',`no') => yes
|
||||
_ if(F,`yes',`no') => if_F(`yes',`no') => no
|
||||
_
|
||||
_ (An arrow => signifies a single macro-expansion step.)
|
||||
_
|
||||
_ Again for consistency, we quote the second and third arguments
|
||||
_ of `if' unless necessity dictates otherwise.
|
||||
_
|
||||
_ Basic Boolean functions are easy to define in terms of `if'.
|
||||
_
|
||||
define(`not', `if(`$1', `F', `T')')_
|
||||
define(`and', `if(`$1', `$2', `F')')_
|
||||
define(`or', `if(`$1', `T', `$2')')_
|
||||
define(`nor', `not(or($1,$2))')_
|
||||
_
|
||||
_ List representation (Idiom 3)
|
||||
_
|
||||
_ In order to provide access to individual members, sequences
|
||||
_ of data values may be represented as right-associated lists.
|
||||
_ In particular, binary integers may be represented in this
|
||||
_ manner:
|
||||
_
|
||||
_ (1,(0,(1,(1,()))))
|
||||
_
|
||||
_ To facilitate arithmetic algorithms, the presentation is
|
||||
_ little-endian. In customary base-2 notation the example
|
||||
_ becomes 1101 (decimal 13). An empty list `()' acts as a
|
||||
_ terminator. Zero is normally represented by the empty
|
||||
_ list. Other values normally end in (1,()). Although
|
||||
_ insignificant 0 bits after the last 1 in a list are
|
||||
_ generally harmless, algorithms should not gratuitously
|
||||
_ create them.
|
||||
_
|
||||
_ Data may be saved under arbitrary names (Idiom 4). Here
|
||||
_ we give more readable names for some small numbers.
|
||||
_ Quotes have been omitted from the definition for `two',
|
||||
_ so that `one' gets expanded at definition time rather
|
||||
_ than at each call of `two', paying a small amount of
|
||||
_ space to save time at each call.
|
||||
_
|
||||
define(`zero', `()')_
|
||||
define(`one', `(1,())')_
|
||||
define(`two', (0,one))_
|
||||
_
|
||||
_ Individual list elements may be accessed by a pun, in which
|
||||
_ the outer parentheses of a list are taken to be the
|
||||
_ argument-list delimiters in a macro call. Two basic macros
|
||||
_ use this scheme to extract "head" and "tail" parts, <h>
|
||||
_ and <t>, from a nonempty list:
|
||||
_
|
||||
_ head((<h>,<t>)) ==> <h>
|
||||
_ tail((<h>,<t>)) ==> <t>
|
||||
_
|
||||
_ (A long arrow ==> signifies more than one macro expansion.)
|
||||
_
|
||||
define(`head', `_head$1')_
|
||||
define(`_head', `$1')_
|
||||
_
|
||||
define(`tail', `_tail$1')_
|
||||
define(`_tail', `$2')_
|
||||
_
|
||||
_ Examples
|
||||
_
|
||||
_ head(two) => head((0,(1,()))) => _head(0,(1,())) => 0
|
||||
_ tail(two) => tail((0,(1,()))) => _tail(0,(1,())) => (1,())
|
||||
_
|
||||
_ According to the rules of m4, `head' and `tail' also work on
|
||||
_ the empty list, `()', in which case both functions yield an
|
||||
_ empty string, `'. This behavior will be exploited.
|
||||
_
|
||||
_ Testing equality of primitive values (Idiom 2)
|
||||
_
|
||||
_ The `if' macro checks which of two possible values (`T' or `F')
|
||||
_ appears as its first argument. More trickery is required to
|
||||
_ check equality of arbitrary primitive values--sequences of
|
||||
_ characters that can occur in macro names, e.g; `Abc', `3_2_1'.
|
||||
_ Following Lisp, we call such a sequence an "atom".
|
||||
_
|
||||
_ We first define the outcome of the test of the given atoms to
|
||||
_ be F. Then we define the test of one of the atoms against
|
||||
_ itself to be T. If the two atoms are the same, the second
|
||||
_ definition replaces the first. Finally we call the first.
|
||||
_
|
||||
define(`eq',_
|
||||
__`define(`eq_$1_$2', `F')'_
|
||||
__`define(`eq_$1_$1', `T')'_
|
||||
__`eq_$1_$2')_
|
||||
_
|
||||
_ Examples
|
||||
_
|
||||
_ eq(a,b) => define(`eq_a_b',`F')define(`eq_a_a',`T')eq_a_b
|
||||
_ ==> eq_a_b => F
|
||||
_ eq(a,a) => define(`eq_a_a',`F')define(`eq_a_a',`T')eq_a_a
|
||||
_ ==> eq_a_a => T
|
||||
_
|
||||
_ If the T or F result of `eq' or other boolean-valued macro
|
||||
_ is to be juxtaposed with succeeding text, care must be
|
||||
_ taken to prevent unwanted catenation. An intervening
|
||||
_ quoted empty string suffices to make the separation.
|
||||
_
|
||||
_ eq(a,b)`'eq(c,d) ==> FF
|
||||
_ eq(a,b)eq(c,d) ==> Feq(c,d)
|
||||
_
|
||||
_ Unfortunately the temporary definitions stick around as
|
||||
_ "ghosts" that waste memory. We could relax our notion of
|
||||
_ purity and use the builtin macro `undefine' to clean up.
|
||||
_
|
||||
_ A test for the empty list:
|
||||
_
|
||||
define(`isempty', `eq(head($1), `')')_
|
||||
_
|
||||
_ Equality of lists of atoms can be tested by `eql',
|
||||
_ which uses `eq' recursively. The indentation in this
|
||||
_ definition and others like it is intended to imitate a
|
||||
_ sequence of else-less if's.
|
||||
_
|
||||
define(`eql',_
|
||||
__`if(isempty($1), `isempty($2)',_
|
||||
__`if(eq(head($1),head($2)), `eql(tail($1),tail($2))',_
|
||||
__`F')')')_
|
||||
_
|
||||
_ Example
|
||||
_
|
||||
_ eql((W,(H,(O,()))), (W,(H,(O,(M,())))))
|
||||
_ => if(isempty((W,(H,(O,())))), ...)
|
||||
_ => if(eq(W,`'), ...)
|
||||
_ ==> if(eq(W,W), `eql((H,(O,())),(H,(O,(M())))), ...)
|
||||
_ ==> if(isempty(()), `isempty((M,())),, ...)
|
||||
_ ==> F
|
||||
_
|
||||
_ Here we use `eq' to define a predicate for testing whether
|
||||
_ a number, possibly with insignificant zero digits, is zero.
|
||||
_
|
||||
define(`iszero',_
|
||||
__`if(isempty($1), `T',_
|
||||
__`if(eq(head($1),0), `iszero(tail($1))',_
|
||||
__`F')')')_
|
||||
_
|
||||
_ Example
|
||||
_
|
||||
_ iszero(())
|
||||
_ ==> if(eq(,),`T',`if(...)')
|
||||
_ ==> if(T,`T',`if(...)' ==> T
|
||||
_
|
||||
_ iszero((0,(1,())))
|
||||
_ ==> if(eq(0,),...,`if(eq(head((0,(1,()))),0),...)')
|
||||
_ ==> iszero(tail(0,(1,())))
|
||||
_ ==> iszero((1,())) ==> F
|
||||
_
|
||||
_ Basic to all arithmeic is the successor function.
|
||||
_
|
||||
define(`succ',_
|
||||
__`if(iszero($1), one,_
|
||||
__`if(eq(head($1),0), `(1,tail($1))',_
|
||||
__`(0,succ(tail($1)))')')')_
|
||||
_
|
||||
define(`three', succ(two))_
|
||||
define(`four', succ(three))_
|
||||
_
|
||||
_ In the definition of `succ', the behavior for each of the
|
||||
_ three possible values of the head digit of the argument
|
||||
_ (empty, `0' and `1') is presented in a different context:
|
||||
_ as the first branch of an `if', as the first branch of
|
||||
_ a nested `if', and as the second branch of a nested `if'.
|
||||
_ Parentheses become hard to match. It would be cleaner to
|
||||
_ use a 3-way switch.
|
||||
_
|
||||
_ Case-switching on attributes of arguments
|
||||
_
|
||||
_ Unfortunately, overt switch code relies on Idiom 1, a
|
||||
_ glaring deviation from the mainstream functional style
|
||||
_ used to define `iszero' and `succ'. A remedy is at hand:
|
||||
_ higher-level switch-defining macros that hide Idiom 1.
|
||||
_
|
||||
_ Switching is based on one or more "decision attributes",
|
||||
_ atomic values derived from the arguments. The type of
|
||||
_ a switch depends on the numbers of decision attributes
|
||||
_ and parameters.
|
||||
_
|
||||
_ switch decision parameters
|
||||
_ type attributes
|
||||
_ switch1of1 1 1
|
||||
_ switch1of2 1 2
|
||||
_ switch2of2 2 2
|
||||
_
|
||||
_ Other switch types are named similarly.
|
||||
_
|
||||
_ Warning. The discussion from here through the definition
|
||||
_ of `switch1of1' may be heavy going. You may wish to skim
|
||||
_ it then skip around to bring the whole into focus.
|
||||
_
|
||||
_ Each switch declaration defines a function and the
|
||||
_ decision attributes expressed as if they were in
|
||||
_ the body of the macro to be declared. A switch
|
||||
_ declaration must be accompanied by `case' macros
|
||||
_ for all valid sets of values of the decision
|
||||
_ attributes. (`case', is merely a distinctive
|
||||
_ synonym for `define'.)
|
||||
_
|
||||
define(`case', `define(`$1', `$2')')_
|
||||
_
|
||||
_ The name of a case macro comprises the name of the
|
||||
_ original function and values of the decision
|
||||
_ attributes separated by underscores. For example,
|
||||
_ to define `iszero' in switch style, the decision
|
||||
_ attribute is the head of the argument; and we must
|
||||
_ define cases, `iszero_', `iszero_0' and `iszero_1'
|
||||
_ for the three possible values of the attribute
|
||||
_
|
||||
_ switch1of1(`iszero',`head($1)')_
|
||||
_ case(`iszero_', `T')_
|
||||
_ case(`iszero_0', `iszero(tail($1))')_
|
||||
_ case(`iszero_1', `F')_
|
||||
_
|
||||
_ In the definition of the switch-declaring macro,
|
||||
_ `switch1of1', $1 is the name of the function to be
|
||||
_ declared, notionally like this
|
||||
_
|
||||
_ define(`switch1of1',_
|
||||
_ __`define(`$1', `<casename>(`$1',<attr>)(<arg>)')')
|
||||
_
|
||||
_ where <casename> pastes together the function name and a
|
||||
_ value of the decision attribute, <attr>. The constructed name
|
||||
_ will be called with argument <arg>. When $1 is `iszero', say,
|
||||
_ <attr> is `head($1)' and <arg> is $1. Unfortunately $1
|
||||
_ already means `iszero'. To resolve the clash, we arrange
|
||||
_ for the appearance of $1 in <arg> to be inserted only when
|
||||
_ the inner `define' is executed. Thus such instances of $1
|
||||
_ do not occur in the replacement text of `switch1of1'.
|
||||
_
|
||||
_ The trick (Idiom 5) is to replace $<n> by a "dollar macro",
|
||||
_ `DOL(<n>)', which expands to `$<n>'.
|
||||
_
|
||||
define(`DOL',``$$1'')_
|
||||
_
|
||||
_ The doubled quotes will be explained later. For `DOL' to be
|
||||
_ expanded when the inner `define' is executed, the two
|
||||
_ constructs must appear at the same quotation level. This
|
||||
_ typically entails flanking `DOL' with outward-facing quotes
|
||||
_ as in the definition of `switch1of1' below.
|
||||
_
|
||||
_ As the number of decision attributes differs among switch
|
||||
_ types, <casename> needs multiple realizations. We call the
|
||||
_ macro that expands to a <casename> for one decision
|
||||
_ attribute `_switch1'.
|
||||
_
|
||||
define(`switch1of1',_
|
||||
__`define(`$1', `_switch1(`$1',$2)('DOL(1)`)')')_
|
||||
define(`_switch1', `$1_$2')_
|
||||
_
|
||||
_ Here is the successor function programmed in switch style.
|
||||
_
|
||||
switch1of1(`succ', `head($1)')_
|
||||
case(`succ_', one)_
|
||||
case(`succ_0', `(1,tail($1))')_
|
||||
case(`succ_1', `(0,succ(tail($1)))')_
|
||||
_
|
||||
_ Examples
|
||||
_
|
||||
_ switch1of1(`succ', `head($1)')
|
||||
_ ==> define(`succ', `_switch1(`succ',head($1))($1)')
|
||||
_
|
||||
_ succ((1,()))
|
||||
_ => _switch1(`succ', head((1,()))((1,())))
|
||||
_ => _switch1(`succ', 1)((1,())))
|
||||
_ => succ_1((1,()))
|
||||
_ => (0,succ(tail(1,())))
|
||||
_ ==> (0,succ(()))
|
||||
_ ==> (0,(1,()))
|
||||
_
|
||||
_ Different numbers of switching attributes and parameters
|
||||
_ are needed for defining other functions. The following
|
||||
_ definitions are routine variations on `switch1of1'.
|
||||
_
|
||||
define(`switch2of2',_
|
||||
__`define(`$1',_
|
||||
____`_switch2(`$1',$2,$3)('DOL(1)`,'DOL(2)`)')')_
|
||||
define(`_switch2', `$1_$2_$3')_
|
||||
_
|
||||
define(`switch1of2',_
|
||||
__`define(`$1',_
|
||||
____`_switch1(`$1',$2)('DOL(1)`,'DOL(2)`)')')_
|
||||
_
|
||||
define(`switch1of3',_
|
||||
__`define(`$1',_
|
||||
____`_switch1(`$1',$2)('DOL(1)`,'DOL(2)`,'DOL(3)`)')')_
|
||||
_
|
||||
define(`switch2of3',_
|
||||
__`define(`$1',_
|
||||
____`_switch2(`$1',$2,$3)('DOL(1)`,'DOL(2)`,'DOL(3)`)')')_
|
||||
_
|
||||
_ Here we define some basic arithmetic functions. For
|
||||
_ illustrative purposes, addition is defined by switching
|
||||
_ on both arguments, while multiplication is defined by
|
||||
_ switching on only the first argument. Both definitions
|
||||
_ can produce insignificant zero bits when inputs have
|
||||
_ insignificant zero bits.
|
||||
_
|
||||
switch2of2(`add', `head($1)',`head($2)')_
|
||||
case(`add__', `()')_
|
||||
case(`add__0', `$2')_
|
||||
case(`add__1', `$2')_
|
||||
case(`add_0_', `$1')_
|
||||
case(`add_0_0', `(0,add(tail($1),tail($2)))')_
|
||||
case(`add_0_1', `(1,add(tail($1),tail($2)))')_
|
||||
case(`add_1_', `$1')_
|
||||
case(`add_1_0', `(1,add(tail($1),tail($2)))')_
|
||||
case(`add_1_1', `(0,succ(add(tail($1),tail($2))))')_
|
||||
_
|
||||
define(`mul', `if(iszero($2), `()', `_mul($1,$2)')')_
|
||||
switch1of2(`_mul', `head($1)')_
|
||||
case(`_mul_', `()')_
|
||||
case(`_mul_0', `(0,_mul(tail($1),$2))')_
|
||||
case(`_mul_1', `add($2,_mul_0($1,$2))')_
|
||||
_
|
||||
_ Subtraction. Because of the lack of negative numbers,
|
||||
_ we provide a "diminish" function, `dim', that clamps
|
||||
_ negative results at zero.
|
||||
_
|
||||
_ dim(a,b) = max(a-b,0)
|
||||
_
|
||||
_ The definition of `_dim' exploits the fact that $1>$2
|
||||
_ when it is called from `dim'. Thus at every recursive
|
||||
_ level $1>=$2. The last line of `_dim', where $1=(0,...)
|
||||
_ and $2=(1,...), implements traditonal "borrowing" from
|
||||
_ the tail of $1 by incrementing the tail of $2.
|
||||
_
|
||||
define(`dim', `if(le($1,$2), `()', `trim(_dim($1,$2))')')_
|
||||
define(`_dim',_
|
||||
__`if(iszero($2), `$1',_
|
||||
__`if(eq(head($1),head($2)), `(0,_dim(tail($1),tail($2)))',_
|
||||
__`if(eq(head($1),1), `(1,_dim(tail($1),tail($2)))'_
|
||||
__,`(1,_dim(tail($1),succ(tail($2))))')')')')_
|
||||
_
|
||||
_ `if' can be defined via `switch1of3', however the resulting
|
||||
_ implementation is less efficient than the original.
|
||||
_
|
||||
_ switch1of3(`if',`$1')_
|
||||
_ case(`if_T', `$2')_
|
||||
_ case(`if_F', `$3')_
|
||||
_
|
||||
_ The newer definition depends crucially on the doubled
|
||||
_ quotes in the definition of `DOL' to protect the
|
||||
_ arguments of a generated call of a case macro from
|
||||
_ being scanned for macro calls, which would result
|
||||
_ in unintended executions.
|
||||
_
|
||||
_ `rev' reverses the order of elements in a list. The
|
||||
_ result is accumulated in the second argument of `_rev'.
|
||||
_
|
||||
define(`rev', `_rev($1,())')_
|
||||
define(`_rev',_
|
||||
__`if(isempty($1), $2,_
|
||||
__`_rev(tail($1),(head($1),$2))')')_
|
||||
_
|
||||
_ `trim' deletes insignifcant zero bits from a number.
|
||||
_
|
||||
define(`trim',_
|
||||
__`if(eq(head($1),`'), `()',_
|
||||
__`_trim(head($1),trim(tail($1)))')')_
|
||||
define(`_trim',_
|
||||
__`if(and(isempty($2),eq($1,0)), `()',_
|
||||
__`($1,$2)')')_
|
||||
_
|
||||
_ Example
|
||||
_
|
||||
_ trim(rev((0,(0,(1,())))))
|
||||
_ ==> trim(_rev((0,(0,(1,()))),()))
|
||||
_ ==> trim(_rev((0,(1,())),(0,())))
|
||||
_ ==> trim((1,(0,(0,()))))
|
||||
_ => _trim(1,(0,(0,())))
|
||||
_ ==> (1,())
|
||||
_
|
||||
_ For human consumption, `base2' converts results from
|
||||
_ lugubrious list form to ordinary binary notation.
|
||||
_
|
||||
define(`base2',_
|
||||
__`if(eq(head($1),`'), `0', `_base2($1)')')_
|
||||
define(`_base2',_
|
||||
__`if(eq(head($1),`'), `', `_base2(tail($1))head($1)')')_
|
||||
_
|
||||
_ A counter value kept in a macro (per Idiom 4) may be
|
||||
_ updated by `incr' or read out by expanding it.
|
||||
_
|
||||
define(`incr', `define(`$1',succ($1))')_
|
||||
_
|
||||
_ Examples
|
||||
_
|
||||
_ base2((0,(1,(0,())))) ==> 010
|
||||
_ base2(trim((0,(1,(0,())))))
|
||||
_ ==> base2((0,(1,()))) ==> 10
|
||||
_
|
||||
_ define(mycounter,zero)
|
||||
_ incr(`mycounter')
|
||||
_ incr(`mycounter')
|
||||
_ base2(mycounter) ==> 10
|
||||
_
|
||||
_ `nth(<n>,<a>)' yields item number <n> (counted from 0) in list
|
||||
_ <a>, or `' if <n> is too big.
|
||||
_
|
||||
define(`nth',_
|
||||
__`if(isempty($2), `',_
|
||||
__`if(iszero($1), `head($2)',_
|
||||
__`nth(dim($1,one),tail($2))')')')_
|
||||
_
|
||||
_ Some functional programming staples are `map', `fold' and
|
||||
_ `zip'. `map' applies a given function to every element of a
|
||||
_ list. Its use with m4 is hampered by the restriction of list
|
||||
_ elements to atoms.
|
||||
_
|
||||
_ map(<f>, (<x0>,(<x1>,...(<xk>,())))) =
|
||||
_ (<f>(<x0>),(<f>(<x1>),...(<f>(<xk>),())...))
|
||||
_
|
||||
define(`map',_
|
||||
__`if(isempty($2), `()',_
|
||||
__`($1(head($2)),map(`$1',tail($2)))')')_
|
||||
_
|
||||
_ Folds combine all the elements of a list into a single value by
|
||||
_ applying a binary operation <op> iteratively, beginning with <z>,
|
||||
_ and proceeding in right-associative order for `foldr' and left-
|
||||
_ associative for `foldl'.
|
||||
_
|
||||
_ foldr(<op>, <z>, (<x0>,(<x1>,...(<xk>,())))) =
|
||||
_ <x0><op>(<x1><op>...(<xk><op><z>)...)
|
||||
_ <op> is a function of two variables, written here in infix form
|
||||
_
|
||||
define(`foldr',_
|
||||
__`if(isempty($3), $2,_
|
||||
__`$1(head($3),foldr(`$1',`$2',tail($3)))')')_
|
||||
_
|
||||
_ foldl(<op>, <z>, (<x0>,(<x1>,...(<xk>,())))) =
|
||||
_ (...((<z><op><x0>)<op><x1>)...)<op><xk>
|
||||
_
|
||||
define(`foldl',_
|
||||
__`if(isempty($3), $2,_
|
||||
__`foldl(`$1',$1($2,head($3)),tail($3))')')_
|
||||
_
|
||||
_ `trim', `rev' and the `base2' workhorse function `_base2'
|
||||
_ can be defined as folds. `_trim' is as before.
|
||||
_
|
||||
define(`trim', `foldr(`_trim',`()',`$1')')_
|
||||
_
|
||||
define(`rev', `foldl(`_rev',`()',`$1')')_
|
||||
define(`_rev',`($2,$1)')_
|
||||
_
|
||||
define(`_base2', `foldr(`_base2_',`',`$1')')_
|
||||
define(`_base2_',`$2$1')_
|
||||
_
|
||||
_ Length of a list defined as a fold. The fold calls `succ'
|
||||
_ with two arguments; `succ' uses only the first one.
|
||||
_
|
||||
define(`length', `foldl(`succ',zero,$1)')_
|
||||
_
|
||||
_ zip(<f>, <xs>, <ys>) makes a list of <f>(<x>,<y>) where <x>,<y>
|
||||
_ are corresponding elements of lists <xs> and <ys>
|
||||
_
|
||||
define(`zip',_
|
||||
__`if(isempty($2), `()',_
|
||||
__`if(isempty($3), `()',_
|
||||
__`($1(head($2),head($3)),zip(`$1',tail($2),tail($3)))')')')_
|
||||
_
|
||||
_ Iteration
|
||||
_
|
||||
_ We define an iterative "for statement" that repeatedly calls
|
||||
_ a macro with an indexed argument. Its meaning may be described
|
||||
_ informally thus:
|
||||
_
|
||||
_ for(lo,hi,`func') = for i from lo to hi do func(i)
|
||||
_
|
||||
define(`for',_
|
||||
__`if(gt($1,$2), `',_
|
||||
__`$3($1)'_
|
||||
__`for(succ($1),$2,`$3')')')_
|
||||
_
|
||||
_ `forA' allows a further argument to be passed to `func'.
|
||||
_ A hack for passing multiple arguments is to hide the
|
||||
_ separating commas in quotes.
|
||||
_
|
||||
_ forA(lo,hi,`func',arg) = for i from low to high do func(arg,i)
|
||||
_
|
||||
define(`forA',_
|
||||
__`if(gt($1,$2), `',_
|
||||
__`$3($4,$1)'_
|
||||
__`forA(succ($1),$2,`$3',`$4')')')_
|
||||
_
|
||||
_ The double loop below prints a binary addition table.
|
||||
_
|
||||
_ define(`table', `for(zero,one,`row')')
|
||||
_ define(`row', `forA(zero,one,`cell',$1)newline()')
|
||||
_ define(`cell', `base2(add($1,$2))tab()')
|
||||
_ define(`tab', ` ')
|
||||
_ define(`newline',`
|
||||
_ ')_
|
||||
_
|
||||
_ Example
|
||||
_
|
||||
_ table() ==>
|
||||
_ 0 1
|
||||
_ 1 10
|
||||
_
|
||||
_
|
||||
_ Comparison functions
|
||||
_
|
||||
_ Numerical comparison operators may be built on `compare',
|
||||
_ a function that yields `LT', `EQ', or `GT' according as
|
||||
_ its first argument is less than, equal to, or greater
|
||||
_ than the second. The auxiliary function `_compare' breaks
|
||||
_ ties between tails.
|
||||
_
|
||||
switch2of2(`compare', `head($1)',`head($2)')_
|
||||
case(`compare__', `EQ')_
|
||||
case(`compare__0', `if(iszero($2), `EQ', `LT')')_
|
||||
case(`compare__1', `LT')_
|
||||
case(`compare_0_', `if(iszero($1), `EQ', `GT')')_
|
||||
case(`compare_0_0', `compare(tail($1),tail($2))')_
|
||||
case(`compare_0_1', `_compare(compare(tail($1),tail($2)), LT)')_
|
||||
case(`compare_1_', `GT')_
|
||||
case(`compare_1_0', `_compare(compare(tail($1),tail($2)), GT)')_
|
||||
case(`compare_1_1', `compare(tail($1),tail($2))')_
|
||||
_
|
||||
define(`_compare', `if(eq($1,EQ), `$2', `$1')')_
|
||||
_
|
||||
_ The customary two-way comparison predicates can be defined
|
||||
_ in terms of `compare'. A typical example is the "less than"
|
||||
_ predicate, `lt'.
|
||||
_
|
||||
switch1of2(`lt',`compare($1,$2)')_
|
||||
case(`lt_LT', `T')_
|
||||
case(`lt_EQ', `F')_
|
||||
case(`lt_GT', `F')_
|
||||
_
|
||||
_ To avoid the tedium of six such declarations, we
|
||||
_ capture their pattern in `mkcomp'. `mkcomp' is a
|
||||
_ third-order function; it defines definers of
|
||||
_ numerical comparison predicates.
|
||||
_
|
||||
define(`mkcomp',_
|
||||
__`switch1of2(`$1',`compare($'`1,$'`2)')'_
|
||||
__`case(`$1_LT', `$2')'_
|
||||
__`case(`$1_EQ', `$3')'_
|
||||
__`case(`$1_GT', `$4')')_
|
||||
_
|
||||
mkcomp(`lt', `T', `F', `F')_
|
||||
mkcomp(`le', `T', `T', `F')_
|
||||
mkcomp(`et', `F', `T', `F')_ equal to, differs from eq
|
||||
mkcomp(`ne', `T', `F', `T')_
|
||||
mkcomp(`ge', `F', `T', `T')_
|
||||
mkcomp(`gt', `F', `F', `T')_
|
||||
_
|
||||
_ Example
|
||||
_
|
||||
_ et((0,()),()) ==> T
|
||||
_ eql((0,()),()) ==> F
|
||||
_
|
||||
_ Comparing atoms for, say, alphabetic order is not a
|
||||
_ simple task; we need somehow to decide the question
|
||||
_ consistently for every possible pair of values. The
|
||||
_ function `cmp' does so for single alphanumerics by
|
||||
_ finding which member of the pair comes first in a
|
||||
_ list of the alphabet.
|
||||
_
|
||||
define(`cmp',_
|
||||
__`if(eq(`$1',`$2'), `EQ',_
|
||||
__`_cmp(`$1',`$2',head(alphabet),tail(alphabet))')')_
|
||||
_
|
||||
define(`_cmp',_
|
||||
__`if(eq(`$1', `$3'), `LT',_
|
||||
__`if(eq(`$2', `$3'), `GT',_
|
||||
__`if(isempty($4), `?',_
|
||||
__`_cmp(`$1',`$2',head(`$4'),tail(`$4'))')')')')_
|
||||
_
|
||||
define(`alphabet',_
|
||||
`(`0',(`1',(`2',(`3',(`4',(`5',(`6',(`7',(`8',(`9',_
|
||||
(`A',(`a',(`B',(`b',(`C',(`c',(`D',(`d',(`E',(`e',_
|
||||
(`F',(`f',(`G',(`g',(`H',(`h',(`I',(`i',(`J',(`j',_
|
||||
(`K',(`k',(`L',(`l',(`M',(`m',(`N',(`n',(`O',(`o',_
|
||||
(`P',(`p',(`Q',(`q',(`R',(`r',(`S',(`s',(`T',(`t',_
|
||||
(`U',(`u',(`V',(`v',(`W',(`w',(`X',(`x',(`Y',(`y',_
|
||||
(`Z',(`z',()))))))))))))))))))))))))))))))))))))))_
|
||||
))))))))))))))))))))))))')_
|
||||
_
|
||||
_ `alphabet' is fragile. The quotes in `alphabet'
|
||||
_ disappear from the tail copied as an argument of
|
||||
_ `_cmp'. Then one-letter macro names will affect
|
||||
_ the alphabet and ruin comparisons as here, where
|
||||
_ A has effectively been eliminated from the alphabet.
|
||||
_
|
||||
_ define(`A',`B')cmp(`z',`A') ==> LT
|
||||
_
|
||||
_ Although it is possible with more processing to
|
||||
_ retain quotes in each copied tail, `cmp' is already
|
||||
_ costly in both time and space. Iteration over the
|
||||
_ alphabet involves copying N tails of decreasing
|
||||
_ length, for a total volume of O(N^2) list elements,
|
||||
_ where N is the size of the alphabet. Each such
|
||||
_ iteration creates ghost definitions, up to N^2
|
||||
_ of which can accumulate over multiple executions
|
||||
_ of `cmp'.
|
||||
_
|
||||
_ Ghost definitions are not only numerous; they are
|
||||
_ repeatedy redefined. If all alphanumeric pairs are
|
||||
_ equally likely, an average of about 120 redefinitions
|
||||
_ and 60 copies of tails will occur for each execution
|
||||
_ of `cmp'.
|
||||
_
|
||||
_ The overheads of redefinition and alphabet-copying can
|
||||
_ be avoided by using an N^2-way switch instead of linear
|
||||
_ search in the alphabet. We automate the construction
|
||||
_ of the N^2 case declarations with `mkcmp' and a few
|
||||
_ helper functions defined below.
|
||||
_
|
||||
_ Making the case macros all at once will in the long
|
||||
_ run beat making a lot of ghost definitions for every
|
||||
_ character comparison. Moreover, the noted fragility
|
||||
_ of `alphabet' may be mitigated, for no comparison
|
||||
_ macros refer to `alphabet' after the 3844 case
|
||||
_ macros for `cmp' have been defined.
|
||||
_
|
||||
switch2of2(`cmp', `$1', `$2')_
|
||||
_
|
||||
_ The declaration of case macros is automated by`mkcmp',
|
||||
_ defined below after some helper macros.
|
||||
_
|
||||
_ `mkcmp0' makes comparison functions for $1 against
|
||||
_ $2, which is known to follow $1 in alphabetic order
|
||||
_
|
||||
define(`_mkcmp0',_
|
||||
__`case(`cmp_$1_$2', `LT')'_
|
||||
__`case(`cmp_$2_$1', `GT')')_
|
||||
_
|
||||
_ `_mkcmp1' makes comparison functions for $1 against all
|
||||
_ characters in list $2.
|
||||
_
|
||||
define(`_mkcmp1',_
|
||||
__`if(isempty($2), `',_
|
||||
____`_mkcmp0($1,head($2))'_
|
||||
____`_mkcmp1($1,tail($2))')')_
|
||||
_
|
||||
_ `_mkcmp2' makes comparison functions for all pairs
|
||||
_ constructible from $1 and elements of list $2 of
|
||||
_ alphabetically following characters.
|
||||
_
|
||||
define(`_mkcmp2',_
|
||||
__`case(`cmp_$1_$1', `EQ')'_
|
||||
__`if(isempty($2), `',_
|
||||
____`_mkcmp1($1,$2)'_
|
||||
____`_mkcmp2(head($2),tail($2))')')_
|
||||
_
|
||||
define(`mkcmp', `_mkcmp2(head($1),tail($1))')_
|
||||
mkcmp(alphabet)_
|
||||
_
|
||||
_ Strings can be defined as lists of characters, and
|
||||
_ compared lexicographically by a list-comparison
|
||||
_ function, `cmpl'. For the benefit of string
|
||||
_ manipulation, one-character macro names should be
|
||||
_ avoided.
|
||||
_
|
||||
define(`cmpl',_
|
||||
__`if(and(isempty($1),isempty($2)), `EQ',_
|
||||
__`if(isempty($1), `LT',_
|
||||
__`if(isempty($2), `GT',_
|
||||
__`_cmpl($1,$2)')')')')_
|
||||
_
|
||||
switch1of2(`_cmpl',`cmp(head($1),head($2))')_
|
||||
case(`_cmpl_LT', `LT')_
|
||||
case(`_cmpl_EQ', `cmpl(tail($1),tail($2))')_
|
||||
case(`_cmpl_GT', `GT')_
|
||||
_
|
||||
_ Example
|
||||
_
|
||||
_ cmpl((A,(N,())), (A,(M,())))
|
||||
_ ==> _cmpl((A,(N,())), (A,(M,())))
|
||||
_ ==> switch1(`_cmpl', cmp(A,A))((A,(N,())), (A,(M,())))
|
||||
_ ==> _cmpl_EQ((A,(N,())), (A,(M,())))
|
||||
_ ==> cmpl(tail((A,(N,()))), tail((A,(M,()))))
|
||||
_ ==> GT
|
||||
_
|
||||
_ For An N-character alphabet, `mkcmp' creates N^2 macros,
|
||||
_ and `cmp' takes O(1) time. A set of N macros, `ord_<c>',
|
||||
_ that give the ordinal position of each character <c>
|
||||
_ in the alphabet would achieve a different time-space
|
||||
_ tradeoff, in which `cmp' becomes `compare(ord_$1,ord_$2)'
|
||||
_ and takes O(log N) time.
|
||||
_
|
||||
_
|
||||
_ Universality
|
||||
_
|
||||
_ It's not hard to envision simulating a simple computer. Thus,
|
||||
_ aside from resource limitations, bare m4 is Turing complete.
|
||||
_ Here's a workable plan:
|
||||
_
|
||||
_ The program counter is maintained by `incr', and converted
|
||||
_ to string form by `base2'.
|
||||
_
|
||||
_ Word addresses are binary numbers.
|
||||
_
|
||||
_ The contents of word n are held in a macro named W<n>, where
|
||||
_ <n> is the base-2 form of the address.
|
||||
_
|
||||
_ Words need not be limited in size. Numbers may be kept
|
||||
_ in the form (sign,magnitude). (Caveat: the sign can't
|
||||
_ be `+' or `-', because programs can only switch on
|
||||
_ alphanumerics.)
|
||||
_
|
||||
_ Instructions may be of variable length--one word of opcode,
|
||||
_ and following words that contain immediate operands or
|
||||
_ addresses expressed in binary form.
|
||||
_
|
||||
_ Instruction codes are a limited set of binary numbers <op>,
|
||||
_ and may be switched on by converting them to base-2 form I<op>.
|
||||
_
|
||||
_ An assignment redefines the designated word.
|
||||
_
|
||||
_ Other instructions are programmed in styles illustrated above.
|
||||
_
|
||||
_ Each instruction updates the location counter appropriately.
|
||||
_
|
||||
_ A control macro fetches an opcode according to the location
|
||||
_ counter. If the opcode is a halt instruction, the control
|
||||
_ terminates. Otherwise it calls the designated operation,
|
||||
_ then calls the control recursively.
|
||||
_
|
||||
_ Eric Blake has carried out this general plan for an
|
||||
_ "intcode" computer and has been able to run impressive
|
||||
_ demos; see https://adventofcode.com/2019/day/2 and
|
||||
_ https://repo.or.cz/aoc_eblake.git/commitdiff/
|
||||
_ 798d9cd09c7b499316713bbe064b420544fbc0f8
|
||||
_
|
||||
_
|
||||
_ Footnotes
|
||||
_
|
||||
_ Some efficiency may be gained by replacing separate calls
|
||||
_ of `head' and `tail' with a single call of a `split' macro,
|
||||
_ at some loss of clarity. In the version of the successor
|
||||
_ function below, `_succ' has head and tail arguments, yet
|
||||
_ no commas are visible in calls for `_succ'.
|
||||
_
|
||||
define(`split', `_split$1')_
|
||||
define(`_split', `$1,$2')_
|
||||
_
|
||||
define(`succ', `_succ(split($1))')_
|
||||
switch1of2(`_succ', `$1')_
|
||||
case(`_succ_', one)_
|
||||
case(`_succ_0', `(1,$2)')_
|
||||
case(`_succ_1', `(0,_succ(split($2)))')_
|
||||
_
|
||||
_
|
||||
_ Acknowledgement and Colophon
|
||||
_
|
||||
_ I am indebted to Eric Blake for his careful reading of, and
|
||||
_ insightful comments on a previous version of this program.
|
||||
_ He inspired the current implementation of the critical
|
||||
_ dollar macro and suggested `split'.
|
||||
_
|
||||
_ M. Douglas McIlroy
|
||||
_ Dartmouth College
|
||||
_ August 2020
|
||||
_ January 2021: comments tweaked
|
||||
_ September 2024: replace eq to work on arbitrary atoms
|
||||
_ June-July 2025: revise switch declarations; add iteration,
|
||||
_ comparison functions, map, fold, zip and appendix
|
||||
_
|
||||
_ Appendix
|
||||
_
|
||||
_ This appendix rounds out the usual complement of
|
||||
_ arithmetic operators on natural numbers. No new
|
||||
_ programming techniques are introduced. Like `add' and
|
||||
_ `mul', these operators yield "normal" numbers with
|
||||
_ no insignificant zeros when given normal inputs, but
|
||||
_ are not guaranteed to do so otherwise.
|
||||
_
|
||||
_ Square.
|
||||
_
|
||||
define(`square', `mul($1,$1)')_
|
||||
_
|
||||
_ Exponentiation. pow(a,n) returns a^n. If n=b+2*n', where
|
||||
_ b is 0 or 1, pow(a,n) = a^b*pow(a^n')^2. As the code is
|
||||
_ written, 0^0 is taken to be 1.
|
||||
_
|
||||
define(`pow',_
|
||||
__`if(iszero($2), `one',_
|
||||
__`if(eq(head($2),0), `square(pow($1,tail($2)))',_
|
||||
__`mul($1,square(pow($1,tail($2))))')')')_
|
||||
_
|
||||
_ Division. The customary integer operators `div' and `mod'_
|
||||
_ are defined in terms of a quotient-remainder operator, `qr'.
|
||||
_
|
||||
define(`div',`head(qr($1,$2))')_
|
||||
define(`mod',`tail(qr($1,$2))')_
|
||||
_
|
||||
_ qr(n,d) yields a pair (q,r), the integer quotient and
|
||||
_ remainder for the division operation n/d, where d>0.
|
||||
_ The result is expressed in "strict (q-r) form".
|
||||
_
|
||||
_ For expository purposes, we assume d fixed and write n
|
||||
_ in "q-r form", [q,r], where n = q*d+r. When r<d the
|
||||
_ representation is called "strict".
|
||||
_
|
||||
_ If n<d, qr(n,d) yields [0,n]. Otherwise, let the binary
|
||||
_ representation of n be (b,n'), where b is 0 or 1 and
|
||||
_ n = b+2*n'. Recursively call qr(n',d) to get [q',r'],
|
||||
_ the strict q-r form of n'.
|
||||
_
|
||||
_ Now n-b = 2*n' = 2*[q',r'] = [2*q',2*r'], whence
|
||||
_ [2*q',b+2*r'] is a [q-r] representation of n. Since
|
||||
_ b+2*r' can be as large as 2*d-1, this representation
|
||||
_ is not strict, but can be made so by the function
|
||||
_
|
||||
_ _qrnorm(d,q,r) = {if r<d then [q,r] else [q+1,r-d]}
|
||||
_
|
||||
_ `trim' removes insignificant zero bits that `_qr'
|
||||
_ produces for some normal inputs. (0,q) is a shortcut
|
||||
_ for mul(two,q).
|
||||
_
|
||||
define(`qr',_
|
||||
__`define(`_qrt', _qr($1,$2))'_
|
||||
__`(trim(head(_qrt)),trim(tail(_qrt)))')_
|
||||
define(`_qr',_
|
||||
__`if(lt($1,$2), `(zero,$1)',_
|
||||
__`define(`_qrt', _qr(tail($1),$2))'_
|
||||
__`_qrnorm($2,`(0,head(_qrt))',_
|
||||
____`if(eq(head($1),0), `(0,tail(_qrt))',_ b=0
|
||||
____`(1,tail(_qrt))')')')')_ b=1
|
||||
define(`_qrnorm',_
|
||||
__`if(lt($3,$1), `($2,$3)',_
|
||||
__`(succ($2),dim($3,$1))')')_
|
||||
_
|
||||
_
|
||||
_ Function summary
|
||||
_
|
||||
_ if conditional
|
||||
_ not, and, or, xor
|
||||
_ boolean operators
|
||||
_ eq test equality of atoms
|
||||
_ cmp compare atoms
|
||||
_ case, switch[12]of[123]
|
||||
_ switch definers
|
||||
_
|
||||
_ Numerics
|
||||
_
|
||||
_ succ successor
|
||||
_ incr increment a named number
|
||||
_ iszero test for zero
|
||||
_ add addition
|
||||
_ mul multiplication
|
||||
_ dim "diminish", subtraction clamped at zero
|
||||
_ qr quotient and remainder
|
||||
_ div integer quotient
|
||||
_ mod integer remainder
|
||||
_ pow integer power
|
||||
_ square
|
||||
_ compare comparison yielding LT, EQ, GT
|
||||
_ lt, le, et, ne, ge, gt
|
||||
_ numeric predicates (et = equal to)
|
||||
_ trim delete insignificant zeros
|
||||
_ for iterate a function over numerical range
|
||||
_ forA ditto with extra arguments
|
||||
_ zero, one, two, three, four
|
||||
_ simple constants
|
||||
_
|
||||
_ List handling
|
||||
_
|
||||
_ head, tail decomposition
|
||||
_ isempty test emptiness
|
||||
_ eql test equality of atom lists
|
||||
_ cmpl compare lists of atoms lexicographically
|
||||
_ nth get nth element
|
||||
_ map apply function elementwise
|
||||
_ foldr reduce to scalar by right-associated operator
|
||||
_ foldl reduce to scalar by left-assocciated operator
|
||||
_ rev reverse
|
||||
_ length count elements
|
||||
_ zip apply function to combine lists elementwise
|
||||
@@ -0,0 +1,12 @@
|
||||
divert(`-1')
|
||||
# upcase(text)
|
||||
# downcase(text)
|
||||
# capitalize(text)
|
||||
# change case of text, simple version
|
||||
define(`upcase', `translit(`$*', `a-z', `A-Z')')
|
||||
define(`downcase', `translit(`$*', `A-Z', `a-z')')
|
||||
define(`_capitalize',
|
||||
`regexp(`$1', `^\(\w\)\(\w*\)',
|
||||
`upcase(`\1')`'downcase(`\2')')')
|
||||
define(`capitalize', `patsubst(`$1', `\w+', `_$0(`\&')')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,19 @@
|
||||
divert(`-1')
|
||||
# upcase(text)
|
||||
# downcase(text)
|
||||
# capitalize(text)
|
||||
# change case of text, improved version
|
||||
define(`upcase', `translit(`$*', `a-z', `A-Z')')
|
||||
define(`downcase', `translit(`$*', `A-Z', `a-z')')
|
||||
define(`_arg1', `$1')
|
||||
define(`_to_alt', `changequote(`<<[', `]>>')')
|
||||
define(`_from_alt', `changequote(<<[`]>>, <<[']>>)')
|
||||
define(`_upcase_alt', `translit(<<[$*]>>, <<[a-z]>>, <<[A-Z]>>)')
|
||||
define(`_downcase_alt', `translit(<<[$*]>>, <<[A-Z]>>, <<[a-z]>>)')
|
||||
define(`_capitalize_alt',
|
||||
`regexp(<<[$1]>>, <<[^\(\w\)\(\w*\)]>>,
|
||||
<<[_upcase_alt(<<[<<[\1]>>]>>)_downcase_alt(<<[<<[\2]>>]>>)]>>)')
|
||||
define(`capitalize',
|
||||
`_arg1(_to_alt()patsubst(<<[<<[$*]>>]>>, <<[\w+]>>,
|
||||
_from_alt()`]>>_$0_alt(<<[\&]>>)<<['_to_alt())_from_alt())')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,7 @@
|
||||
# An ordinary comment
|
||||
define(`foo', # A comment in a macro
|
||||
`Macro `foo' expansion')
|
||||
foo
|
||||
define(`comment', `*** Macro `comment' expansion ***')
|
||||
changecom(`@', `@')
|
||||
foo
|
||||
@@ -0,0 +1,23 @@
|
||||
divert(`-1')
|
||||
# curry(macro, args...)
|
||||
# Perform partial argument application on the given macro. This
|
||||
# expands to an unspecified macro name that accepts one or more extra
|
||||
# arguments, and appends those to the args supplied to the original
|
||||
# curry call as the overall set of arguments to pass to macro. That
|
||||
# is, curry(`macro', args1...)(args2...) is the same as invoking
|
||||
# macro(args1..., args2...).
|
||||
#
|
||||
# Most often, argument currying comes in handy when given a context
|
||||
# that normally takes a macro name to call with one argument, but
|
||||
# where you want to combine that variable argument with other fixed
|
||||
# arguments to forward to a macro that takes multiple arguments. For
|
||||
# example, given a "foreach" macro that calls its first argument once
|
||||
# for each successive argument, "foreach(`curry(`mult', 3)', 1, 2, 3)"
|
||||
# would behave the same as "mult(3, 1), mult(3, 2), mult(3, 3)".
|
||||
#
|
||||
# It is also possible to create a named function curry. For example:
|
||||
# define(`mult3', `curry(`mult', 3)($1)')
|
||||
# Later use of mult3(value) will compute the same as mult(3, value).
|
||||
define(`curry', `$1(shift($@,)_curry')
|
||||
define(`_curry', `$@)')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,4 @@
|
||||
divert(1)Text diverted a first time.
|
||||
divert(0)undivert(1)dnl
|
||||
divert(1)Text diverted a second time.
|
||||
divert(0)undivert(1)dnl
|
||||
@@ -0,0 +1,4 @@
|
||||
define(`countdown', `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Liftoff')')
|
||||
debugmode(`aeqc')
|
||||
traceon(`countdown')
|
||||
countdown(2)
|
||||
@@ -0,0 +1,5 @@
|
||||
define(`hostname', esyscmd(`hostname'))dnl
|
||||
`hostname = >>'hostname`<<'
|
||||
define(`hostname',
|
||||
pushdef(`_tmp', `$1')_tmp(translit(esyscmd(`hostname'), `.', `,'))`'popdef(`_tmp'))dnl
|
||||
`hostname = >>'hostname`<<'
|
||||
@@ -0,0 +1 @@
|
||||
define(`countdown', `$1 ifelse(eval($1 > 0), 1, `countdown(decr($1))', `Done')')
|
||||
@@ -0,0 +1,5 @@
|
||||
changequote([[,]])dnl
|
||||
define([[quoteall]], [[patsubst([[[[$*]]]], [[,[ ]+]], [[,]])]])dnl
|
||||
define([[group]], quoteall(include([[/etc/group]])))dnl
|
||||
dnl
|
||||
group()dnl
|
||||
@@ -0,0 +1 @@
|
||||
bar
|
||||
@@ -0,0 +1,8 @@
|
||||
divert(`-1')
|
||||
# foreach(x, (item_1, item_2, ..., item_n), stmt)
|
||||
# parenthesized list, simple version
|
||||
define(`foreach', `pushdef(`$1')_foreach($@)popdef(`$1')')
|
||||
define(`_arg1', `$1')
|
||||
define(`_foreach', `ifelse(`$2', `()', `',
|
||||
`define(`$1', _arg1$2)$3`'$0(`$1', (shift$2), `$3')')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,10 @@
|
||||
include(`quote.m4')dnl
|
||||
divert(`-1')
|
||||
# foreach(x, (item_1, item_2, ..., item_n), stmt)
|
||||
# parenthesized list, improved version
|
||||
define(`foreach', `pushdef(`$1')_$0(`$1',
|
||||
(dquote(dquote_elt$2)), `$3')popdef(`$1')')
|
||||
define(`_arg1', `$1')
|
||||
define(`_foreach', `ifelse(`$2', `(`')', `',
|
||||
`define(`$1', _arg1$2)$3`'$0(`$1', (dquote(shift$2)), `$3')')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,9 @@
|
||||
include(`quote.m4')dnl
|
||||
divert(`-1')
|
||||
# foreachq(x, `item_1, item_2, ..., item_n', stmt)
|
||||
# quoted list, simple version
|
||||
define(`foreachq', `pushdef(`$1')_foreachq($@)popdef(`$1')')
|
||||
define(`_arg1', `$1')
|
||||
define(`_foreachq', `ifelse(quote($2), `', `',
|
||||
`define(`$1', `_arg1($2)')$3`'$0(`$1', `shift($2)', `$3')')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,10 @@
|
||||
include(`quote.m4')dnl
|
||||
divert(`-1')
|
||||
# foreachq(x, `item_1, item_2, ..., item_n', stmt)
|
||||
# quoted list, improved version
|
||||
define(`foreachq', `pushdef(`$1')_$0($@)popdef(`$1')')
|
||||
define(`_arg1q', ``$1'')
|
||||
define(`_rest', `ifelse(`$#', `1', `', `dquote(shift($@))')')
|
||||
define(`_foreachq', `ifelse(`$2', `', `',
|
||||
`define(`$1', _arg1q($2))$3`'$0(`$1', _rest($2), `$3')')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,9 @@
|
||||
divert(`-1')
|
||||
# foreachq(x, `item_1, item_2, ..., item_n', stmt)
|
||||
# quoted list, alternate improved version
|
||||
define(`foreachq', `ifelse(`$2', `', `',
|
||||
`pushdef(`$1')_$0(`$1', `$3', `', $2)popdef(`$1')')')
|
||||
define(`_foreachq', `ifelse(`$#', `3', `',
|
||||
`define(`$1', `$4')$2`'$0(`$1', `$2',
|
||||
shift(shift(shift($@))))')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,13 @@
|
||||
include(`forloop2.m4')dnl
|
||||
divert(`-1')
|
||||
# foreachq(x, `item_1, item_2, ..., item_n', stmt)
|
||||
# quoted list, version based on forloop
|
||||
define(`foreachq',
|
||||
`ifelse(`$2', `', `', `_$0(`$1', `$3', $2)')')
|
||||
define(`_foreachq',
|
||||
`pushdef(`$1', forloop(`$1', `3', `$#',
|
||||
`$0_(`1', `2', indir(`$1'))')`popdef(
|
||||
`$1')')indir(`$1', $@)')
|
||||
define(`_foreachq_',
|
||||
``define(`$$1', `$$3')$$2`''')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,6 @@
|
||||
divert(`-1')
|
||||
# forloop(var, from, to, stmt) - simple version
|
||||
define(`forloop', `pushdef(`$1', `$2')_forloop($@)popdef(`$1')')
|
||||
define(`_forloop',
|
||||
`$4`'ifelse($1, `$3', `', `define(`$1', incr($1))$0($@)')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,12 @@
|
||||
divert(`-1')
|
||||
# forloop(var, from, to, stmt) - improved version:
|
||||
# works even if VAR is not a strict macro name
|
||||
# performs sanity check that FROM is larger than TO
|
||||
# allows complex numerical expressions in TO and FROM
|
||||
define(`forloop', `ifelse(eval(`($2) <= ($3)'), `1',
|
||||
`pushdef(`$1')_$0(`$1', eval(`$2'),
|
||||
eval(`$3'), `$4')popdef(`$1')')')
|
||||
define(`_forloop',
|
||||
`define(`$1', `$2')$4`'ifelse(`$2', `$3', `',
|
||||
`$0(`$1', incr(`$2'), `$3', `$4')')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,13 @@
|
||||
divert(`-1')
|
||||
# forloop_arg(from, to, macro) - invoke MACRO(value) for
|
||||
# each value between FROM and TO, without define overhead
|
||||
define(`forloop_arg', `ifelse(eval(`($1) <= ($2)'), `1',
|
||||
`_forloop(`$1', eval(`$2'), `$3(', `)')')')
|
||||
# forloop(var, from, to, stmt) - refactored to share code
|
||||
define(`forloop', `ifelse(eval(`($2) <= ($3)'), `1',
|
||||
`pushdef(`$1')_forloop(eval(`$2'), eval(`$3'),
|
||||
`define(`$1',', `)$4')popdef(`$1')')')
|
||||
define(`_forloop',
|
||||
`$3`$1'$4`'ifelse(`$1', `$2', `',
|
||||
`$0(incr(`$1'), `$2', `$3', `$4')')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,6 @@
|
||||
define(`concat', `translit(``$*'', ` ')')
|
||||
define(`fsent', `format(`%-25s %-16s nfs %-16s 0 0', `$1:$2', `$3', concat$4)')
|
||||
|
||||
fsent(freja, /home/gevn, /home/gevn, (rw, soft, bg, grpid))
|
||||
fsent(freja, /home/freja, /home/freja, (rw, soft, grpid))
|
||||
fsent(rimfaxe, /home/rimfaxe, /home/rimfaxe, (rw, soft, bg))
|
||||
@@ -0,0 +1,15 @@
|
||||
divert(-1)
|
||||
|
||||
# move(from, to)
|
||||
define(`move', `Move one disk from `$1' to `$2'.
|
||||
')
|
||||
|
||||
# _hanoi (cnt, from, to, aux)
|
||||
define(`_hanoi', `ifelse(eval(`$1'<=1), 1, `move($2, $3)',
|
||||
`$0(decr($1), $2, $4, $3)move($2, $3)$0(decr($1), $4, $3, $2)')')
|
||||
|
||||
# hanoi (cnt)
|
||||
define(`hanoi', `_$0(`$1', source, destination, auxiliary)')
|
||||
|
||||
# traceon(`move', `_hanoi', `decr')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,2 @@
|
||||
`include test file.'
|
||||
define()
|
||||
@@ -0,0 +1,3 @@
|
||||
Include file start
|
||||
foo
|
||||
Include file end
|
||||
@@ -0,0 +1,7 @@
|
||||
Beginning.
|
||||
include(`NOFILE')
|
||||
Intermediate
|
||||
include(`test/incl-test.m4')
|
||||
After
|
||||
include(`NOFILE')
|
||||
very late
|
||||
@@ -0,0 +1,10 @@
|
||||
define(`%%$$##', `>>>$0<<< cnt $#')
|
||||
|
||||
# indir(`%%$$##', nonsense, nonsense)
|
||||
indir(`%%$$##', nonsense, nonsense)
|
||||
|
||||
# indir(`indir', `%%$$##', nonsense)
|
||||
indir(`indir', `%%$$##', nonsense)
|
||||
|
||||
# indir(`indir', `indir', `indir', `indir', `%%$$##')
|
||||
indir(`indir', `indir', `indir', `indir', `%%$$##')
|
||||
@@ -0,0 +1,15 @@
|
||||
divert(`-1')
|
||||
# join(sep, args) - join each non-empty ARG into a single
|
||||
# string, with each element separated by SEP
|
||||
define(`join',
|
||||
`ifelse(`$#', `2', ``$2'',
|
||||
`ifelse(`$2', `', `', ``$2'_')$0(`$1', shift(shift($@)))')')
|
||||
define(`_join',
|
||||
`ifelse(`$#$2', `2', `',
|
||||
`ifelse(`$2', `', `', ``$1$2'')$0(`$1', shift(shift($@)))')')
|
||||
# joinall(sep, args) - join each ARG, including empty ones,
|
||||
# into a single string, with each element separated by SEP
|
||||
define(`joinall', ``$2'_$0(`$1', shift($@))')
|
||||
define(`_joinall',
|
||||
`ifelse(`$#', `2', `', ``$1$3'$0(`$1', shift(shift($@)))')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,18 @@
|
||||
dnl Stress test for recursion algorithms. Usage:
|
||||
dnl m4 -Ipath/to/examples [-Doptions] loop.m4
|
||||
dnl Options include:
|
||||
dnl -Dalt[=<n>] - test with foreachq<n> instead of foreachq2, default 3
|
||||
dnl -Dlimit=<num> - set upper limit of sequence to <num>, default 1000
|
||||
dnl -Dverbose - print the sequence to the screen, rather than discarding
|
||||
dnl -Ddebug[=<code>] - execute <code> after forloop but before foreach
|
||||
dnl -Dsleep=<num> - sleep for <num> seconds before exit, to allow time
|
||||
dnl to examine peak process memory usage
|
||||
include(`forloop2.m4')dnl
|
||||
include(`quote.m4')dnl
|
||||
ifelse(alt, `alt', `define(`alt', `2')', alt, `', `define(`alt', `3')')dnl
|
||||
include(`foreachq'alt`.m4')dnl
|
||||
ifdef(`limit', `', `define(`limit', `1000')')dnl
|
||||
ifdef(`verbose', `', `divert(`-1')')dnl
|
||||
ifdef(`debug', `', `define(`debug')')dnl
|
||||
foreachq(`i', dquote(1forloop(`i', `2', limit, `,i'))debug, ` i')
|
||||
ifdef(`sleep',`syscmd(`echo done>/dev/tty;sleep 'sleep)')dnl
|
||||
@@ -0,0 +1,9 @@
|
||||
divert(-1)
|
||||
define(`HOST', `vale')
|
||||
define(`TMP', maketemp(`/tmp/hejXXXXXX'))
|
||||
syscmd(`ypmatch' HOST `hosts | awk "{print \$1}"' > TMP)
|
||||
define(`IP', include(TMP))
|
||||
syscmd(`rm -f' TMP)
|
||||
divert
|
||||
|
||||
IP
|
||||
@@ -0,0 +1,17 @@
|
||||
traceon
|
||||
changequote([,])dnl
|
||||
changequote([``], [''])dnl
|
||||
````traceon''''
|
||||
define(``foo'', ````FOO'''')dnl
|
||||
dumpdef(``foo'')dnl
|
||||
changequote(``!'', ``!'')dnl
|
||||
!foo!
|
||||
foo
|
||||
dumpdef(!foo!)dnl
|
||||
define(!bar!, !BAR!)
|
||||
bar
|
||||
changequote(!>*>*>*>*>!, !<*<*<*<*<!)dnl five of each
|
||||
>*>*>*>*>foo bar<*<*<*<*<
|
||||
foo bar
|
||||
>*>*>*>*>*>*><*<*<*<*<*<*<
|
||||
dumpdef(>*>*>*>*>foo<*<*<*<*<, >*>*>*>*>bar<*<*<*<*<)dnl
|
||||
@@ -0,0 +1,8 @@
|
||||
# traceon(`patsubst')
|
||||
patsubst(`GNUs not Unix', `^', `OBS: ')
|
||||
patsubst(`GNUs not Unix', `\<', `OBS: ')
|
||||
patsubst(`GNUs not Unix', `\<\w', `\0=')
|
||||
patsubst(`GNUs not Unix', `\w*', `(\0)')
|
||||
patsubst(`GNUs not Unix', `\w+', `(\0)')
|
||||
patsubst(`GNUs not Unix', `\w+')
|
||||
patsubst(`GNUs not '` Unix', `[ ]+', ` ')
|
||||
@@ -0,0 +1,25 @@
|
||||
divert(-1)
|
||||
pushdef(`hej', `def 1.')
|
||||
dumpdef(`hej')
|
||||
pushdef(`hej', `def 2.')
|
||||
dumpdef(`hej')
|
||||
pushdef(`hej', `def 3.')
|
||||
dumpdef(`hej')
|
||||
pushdef(`hej', `def 4.')
|
||||
dumpdef(`hej')
|
||||
|
||||
popdef(`hej')
|
||||
dumpdef(`hej')
|
||||
popdef(`hej')
|
||||
dumpdef(`hej')
|
||||
popdef(`hej')
|
||||
dumpdef(`hej')
|
||||
popdef(`hej')
|
||||
dumpdef(`hej')
|
||||
popdef(`hej')
|
||||
dumpdef(`hej')
|
||||
popdef(`hej')
|
||||
|
||||
dumpdef(`mac2')
|
||||
popdef(`mac2')
|
||||
dumpdef(`mac2')
|
||||
@@ -0,0 +1,9 @@
|
||||
divert(`-1')
|
||||
# quote(args) - convert args to single-quoted string
|
||||
define(`quote', `ifelse(`$#', `0', `', ``$*'')')
|
||||
# dquote(args) - convert args to quoted list of quoted strings
|
||||
define(`dquote', ``$@'')
|
||||
# dquote_elt(args) - convert args to list of double-quoted strings
|
||||
define(`dquote_elt', `ifelse(`$#', `0', `', `$#', `1', ```$1''',
|
||||
```$1'',$0(shift($@))')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,12 @@
|
||||
traceon(`regexp')dnl
|
||||
regexp(`hej med dig', `.*', `>>\0<<')
|
||||
regexp(`hej med dig', `\w*', `>>\0<<')
|
||||
regexp(`hej med dig', `.+', `>>\0<<')
|
||||
regexp(`hej med dig', `m\w+', `>>\0<<')
|
||||
regexp(`hej med dig', `m\(.*\)', `>>\0<< >>\1<<')
|
||||
|
||||
regexp(`hej med dig', `.*')
|
||||
regexp(`hej med dig', `\w*')
|
||||
regexp(`hej med dig', `.+')
|
||||
regexp(`hej med dig', `m\w+')
|
||||
regexp(`hej med dig', `m\(.*\)')
|
||||
@@ -0,0 +1,4 @@
|
||||
define(`reverse', `ifelse(eval($# > 1), 1, `reverse(shift($@)), `$1'', ``$1'')')
|
||||
``'' => reverse
|
||||
``hej'' => reverse(hej)
|
||||
``hej, med, dig'' => reverse(hej, med, dig)
|
||||
@@ -0,0 +1,16 @@
|
||||
divert(`-1')
|
||||
# stack_foreach(macro, action)
|
||||
# Invoke ACTION with a single argument of each definition
|
||||
# from the definition stack of MACRO, starting with the oldest.
|
||||
define(`stack_foreach',
|
||||
`_stack_reverse(`$1', `tmp-$1')'dnl
|
||||
`_stack_reverse(`tmp-$1', `$1', `$2(defn(`$1'))')')
|
||||
# stack_foreach_lifo(macro, action)
|
||||
# Invoke ACTION with a single argument of each definition
|
||||
# from the definition stack of MACRO, starting with the newest.
|
||||
define(`stack_foreach_lifo',
|
||||
`_stack_reverse(`$1', `tmp-$1', `$2(defn(`$1'))')'dnl
|
||||
`_stack_reverse(`tmp-$1', `$1')')
|
||||
define(`_stack_reverse',
|
||||
`ifdef(`$1', `pushdef(`$2', defn(`$1'))$3`'popdef(`$1')$0($@)')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,17 @@
|
||||
divert(`-1')
|
||||
# stack_foreach_sep(macro, pre, post, sep)
|
||||
# Invoke PRE`'defn`'POST with a single argument of each definition
|
||||
# from the definition stack of MACRO, starting with the oldest, and
|
||||
# separated by SEP between definitions.
|
||||
define(`stack_foreach_sep',
|
||||
`_stack_reverse_sep(`$1', `tmp-$1')'dnl
|
||||
`_stack_reverse_sep(`tmp-$1', `$1', `$2`'defn(`$1')$3', `$4`'')')
|
||||
# stack_foreach_sep_lifo(macro, pre, post, sep)
|
||||
# Like stack_foreach_sep, but starting with the newest definition.
|
||||
define(`stack_foreach_sep_lifo',
|
||||
`_stack_reverse_sep(`$1', `tmp-$1', `$2`'defn(`$1')$3', `$4`'')'dnl
|
||||
`_stack_reverse_sep(`tmp-$1', `$1')')
|
||||
define(`_stack_reverse_sep',
|
||||
`ifdef(`$1', `pushdef(`$2', defn(`$1'))$3`'popdef(`$1')$0(
|
||||
`$1', `$2', `$4$3')')')
|
||||
divert`'dnl
|
||||
@@ -0,0 +1,11 @@
|
||||
# Several input lines, expanding to one
|
||||
define(`foo', ``foo' line one.
|
||||
`foo' line two.
|
||||
`foo' line three.') xyz
|
||||
foo
|
||||
# Several input lines, expanding to none
|
||||
define(`foo', ``foo' line one.
|
||||
`foo' line two.
|
||||
`foo' line three.')dnl
|
||||
# one input line, expanding to several output lines
|
||||
foo foo
|
||||
@@ -0,0 +1,14 @@
|
||||
divert(-1)
|
||||
define(`nargs', `$#')
|
||||
define(`concat', `ifelse(1, $#, `$1', `$1` 'concat(shift($@))')')
|
||||
traceon(`concat', `nargs')
|
||||
divert
|
||||
|
||||
nargs
|
||||
nargs()
|
||||
nargs(1,2,3,4,5,6)
|
||||
|
||||
concat()
|
||||
concat(`hej', `med', `dig')
|
||||
concat(`hej', `med', `dig', `en gang igen')
|
||||
concat(an, awful, lot, of, argument, at, least, more, that, ten, silly, arguments)
|
||||
@@ -0,0 +1,167 @@
|
||||
_ Tests for barem4.m4, July 2025, distributed under Creative
|
||||
_ Commons Share-alike license. -Doug McIlroy
|
||||
_
|
||||
_ Usage: m4 barem4.m4 testbarem4.m4
|
||||
_
|
||||
_ test(`x',`y') tests whether expressions x and y have identical values.
|
||||
_ If not, print x != y, otherwise do nothing.
|
||||
_ Uses the builtin ifelse to compare (nearly) arbitrary strings.
|
||||
_
|
||||
define(test,`ifelse($1,$2,`',``$1' != `$2'
|
||||
')')_
|
||||
_
|
||||
_
|
||||
`t0: here a deliberate error should print just "one != two"'
|
||||
_
|
||||
test(`one',`two')_
|
||||
`From here on test descriptions will be printed.
|
||||
Some tests will announce expected output. Otherwise
|
||||
the only test results printed will be errors.'
|
||||
_
|
||||
define(`five', succ(four))_
|
||||
define(`ten', (0,five))_
|
||||
define(`fifteen', add(five,ten))_
|
||||
define(`twenty', (0,ten))_
|
||||
test(`ten',(0,(1,(0,(1,())))))_
|
||||
_
|
||||
_
|
||||
t1: i+j-i = j, 0<=i,j<=10
|
||||
_
|
||||
define(`t1', `test(`dim(add($1,$2),$1)',`$2')')_
|
||||
define(`_t1', `forA(zero,ten,`t1',$1)')_
|
||||
for(zero,ten, `_t1')_
|
||||
_
|
||||
_
|
||||
t2:(i-j)(i+j) = i^2-j^2, 0<=j<=i<=3
|
||||
_
|
||||
define(`t2',_
|
||||
__`test(`mul(dim($1,$2),add($1,$2))',`dim(square($1),square($2))')')_
|
||||
define(`_t2', `forA(zero,$1,`t2',$1)')_
|
||||
for(zero,three,`_t2')_
|
||||
_
|
||||
_
|
||||
t3: (n/d)*d + n%d = n, 0<=n<=15, 1<=d<=20
|
||||
_
|
||||
define(`t3', `test(`add(mul(div($1,$2),$2),mod($1,$2))',`$1')')_
|
||||
define(`t3', `test(mod($1,$2), trim(mod($1,$2)))')_
|
||||
define(`_t3', `forA(one,twenty,`t3',$1)')_
|
||||
for(zero,fifteen, `_t3')_
|
||||
_
|
||||
_
|
||||
t4: a^(n-1) = a^n/a, 1<=a<=4, 1<=n<=5
|
||||
_
|
||||
define(`t4', `test(`pow($1,dim($2,one))',`div(pow($1,$2),$1)')')_
|
||||
define(`_t4', `forA(one,five,`t4',$1)')_
|
||||
for(one,four,`_t4')_
|
||||
_
|
||||
_
|
||||
t5: numeric comparisons 0<=i,j<=5
|
||||
_
|
||||
define(`t5',_
|
||||
__`test(`lt($1,$2)',`not(ge($1,$2))')'_
|
||||
__`test(`le($1,$2)',`not(gt($1,$2))')'_
|
||||
__`test(`et($1,$2)',`not(ne($1,$2))')'_
|
||||
__`test(`lt($1,$2)',`gt($2,$1)')'_
|
||||
__`test(`le($1,$2)',`ge($2,$1)')'_
|
||||
__`test(`et($1,$2)',`et($2,$1)')'_
|
||||
__`test(`ne($1,$2)',`ne($2,$1)')')_
|
||||
define(`_t5', `forA(zero,five,`t5',$1)')_
|
||||
for(zero,five,`_t5')_
|
||||
_
|
||||
_
|
||||
t6: octal addition table should be printed in base 2
|
||||
_
|
||||
define(`seven', add(three,four))_
|
||||
define(`table', `for(zero,seven,`row')')_
|
||||
define(`row', `forA(zero,seven,`cell',$1)newline()')_
|
||||
define(`cell', `base2(add($1,$2))tab()')_
|
||||
define(`tab', ` ')_
|
||||
define(`newline',`
|
||||
')_
|
||||
table()_
|
||||
_
|
||||
_
|
||||
`t7: alphanumeric comparisons, map, fold, zip'
|
||||
_
|
||||
_ ordered(<c>,<list>) tests whether all characters that precede
|
||||
_ <c> in a list of characters compare low to <c> and all
|
||||
_ characters after <c> compare high. if <c> is absent, all
|
||||
_ characters are deemed to precede it. If <c> is duplicated,
|
||||
_ the second instance is deemed to come after the first. `ttab'
|
||||
_ (transition table) tells whether adjacent judgements conform
|
||||
_ to the specification.
|
||||
_
|
||||
switch2of2(`ttab',`$1',`$2')_
|
||||
case(`ttab_LT_LT', `T')_
|
||||
case(`ttab_LT_EQ', `T')_
|
||||
case(`ttab_LT_GT', `F')_
|
||||
case(`ttab_EQ_LT', `F')_
|
||||
case(`ttab_EQ_EQ', `F')_
|
||||
case(`ttab_EQ_GT', `T')_
|
||||
case(`ttab_GT_LT', `F')_
|
||||
case(`ttab_GT_EQ', `F')_
|
||||
case(`ttab_GT_GT', `T')_
|
||||
_
|
||||
define(`ordered',_
|
||||
__`define(`ordcmp', `cmp($'`1,$1)')'_
|
||||
__`define(`judged', map(`ordcmp',$2))'_
|
||||
__`foldl(`and',T,zip(`ttab',(LT,judged),judged))')_
|
||||
_
|
||||
test(`ordered(`0',`alphabet')',T)_
|
||||
test(`ordered(`L',`alphabet')',T)_
|
||||
test(`ordered(`z',`alphabet')',T)_
|
||||
test(`ordered(2,(3,(2,(3,()))))',F)_
|
||||
test(`ordered(2,(1,(1,(2,()))))',T)_
|
||||
test(`ordered(2,(1,(1,(1,()))))',T)_
|
||||
test(`ordered(2,(1,(2,(2,()))))',F)_
|
||||
test(`ordered(2,(1,(2,(1,()))))',F)_
|
||||
_
|
||||
_
|
||||
`p1-p5 are same as t1-t5 with zero-padded operands.'
|
||||
_ Helper functions _t1-_t5 are unchanged
|
||||
_
|
||||
define(`pad',`rev((0,(0,rev($1))))')_
|
||||
_
|
||||
define(`test', `if(et($1,$2), `', ``$1' != `$2'
|
||||
')')_
|
||||
_
|
||||
_
|
||||
p1: i+j-i = j, 0<=i,j<=10
|
||||
_
|
||||
define(`t1', `test(`dim(add(pad($1),pad($2)),pad($1))',`$2')')_
|
||||
for(zero,ten, `_t1')_
|
||||
_
|
||||
_
|
||||
p2:(i-j)(i+j) = i^2-j^2, 0<=j<=i<=3
|
||||
_
|
||||
define(`t2', `test(`mul(dim(pad($1),$2),add(pad($1),pad($2)))',`dim(square($1),square($2))')')_
|
||||
for(zero,three,`_t2')_
|
||||
_
|
||||
_
|
||||
p3: (n/d)*d + n%d = n, 0<=n<=15, 1<=d<=20
|
||||
_
|
||||
define(`t3', `test(`add(mul(div(pad($1),pad($2)),pad($2)),mod(pad($1),pad($2)))',`$1')')_
|
||||
for(zero,fifteen, `_t3')_
|
||||
_
|
||||
_
|
||||
p4: a^(n-1) = a^n/a, 1<=a<=4, 1<=n<=5
|
||||
_
|
||||
define(`t4', `test(`pow(pad($1),dim(pad($2),one))',`div(pow($1,$2),$1)')')_
|
||||
for(one,four,`_t4')_
|
||||
_
|
||||
_
|
||||
p5: numeric comparisons 0<=i,j<=5
|
||||
_
|
||||
define(`test', `if(eq($1,$2), `', ``$1' != `$2'
|
||||
')')_
|
||||
_
|
||||
define(`t5',_
|
||||
__`test(`lt($1,pad($2))',`not(ge($1,pad($2)))')'_
|
||||
__`test(`le($1,pad($2))',`not(gt($1,pad($2)))')'_
|
||||
__`test(`et($1,pad($2))',`not(ne($1,pad($2)))')'_
|
||||
__`test(`lt($1,pad($2))',`gt(pad($2),$1)')'_
|
||||
__`test(`le($1,pad($2))',`ge(pad($2),$1)')'_
|
||||
__`test(`et($1,pad($2))',`et(pad($2),$1)')'_
|
||||
__`test(`ne($1,pad($2))',`ne(pad($2),$1)')')_
|
||||
define(`_t5', `forA(zero,five,`t5',$1)')_
|
||||
for(zero,five,`_t5')_
|
||||
@@ -0,0 +1,30 @@
|
||||
divert(-1)
|
||||
|
||||
# move(from, to)
|
||||
define(`move', `Move one disk from `$1' to `$2'.
|
||||
')
|
||||
|
||||
# _hanoi (cnt, from, to, aux)
|
||||
define(`_hanoi', `ifelse(eval(`$1'<=1), 1, `move($2, $3)',
|
||||
`$0(decr($1), $2, $4, $3)move($2, $3)$0(decr($1), $4, $3, $2)')')
|
||||
|
||||
# hanoi (cnt)
|
||||
define(`hanoi', `_$0(`$1', source, destination, auxiliary)')
|
||||
divert`'dnl
|
||||
|
||||
# Debugmode t
|
||||
debugmode(`t')
|
||||
hanoi(2)
|
||||
|
||||
# Debugmode taeq
|
||||
debugmode(`taeq')
|
||||
hanoi(2)
|
||||
|
||||
# Debugmode OFF
|
||||
debugmode
|
||||
hanoi(2)
|
||||
|
||||
# Debugmode ae
|
||||
debugmode(`ae')
|
||||
traceon(`move', `_hanoi')
|
||||
hanoi(2)
|
||||
@@ -0,0 +1,8 @@
|
||||
# traceon(`translit')dnl
|
||||
translit(`GNUs not Unix', `a-z')
|
||||
translit(`GNUs not Unix', `a-z', `A-Z')
|
||||
translit(`GNUs not Unix', `A-Z', `a-z')
|
||||
translit(`GNUs not Unix', `A-Z')
|
||||
translit(`a-z', `a-')
|
||||
translit(`A-Z', `A-Z-', `-A-Z')
|
||||
translit(`GNUs not Unix', `Z-A', `a-z')
|
||||
@@ -0,0 +1 @@
|
||||
This is to be undiverted soon.
|
||||
@@ -0,0 +1,5 @@
|
||||
define(`undiverted', `UNDIVERTED')
|
||||
# undiverted file.
|
||||
undivert(`undivert.incl')
|
||||
# included file.
|
||||
include(`undivert.incl')
|
||||
@@ -0,0 +1,10 @@
|
||||
divert(-1)
|
||||
m4wrap(`Wrapper no. 1
|
||||
')
|
||||
|
||||
m4wrap(`Wrapper no. 2
|
||||
m4wrap(`Wrapper no. 3
|
||||
m4wrap(`Wrapper no. 4
|
||||
')')')
|
||||
divert
|
||||
No. 33: The End.
|
||||
@@ -0,0 +1,10 @@
|
||||
dnl Redefine m4wrap to have FIFO semantics.
|
||||
define(`_m4wrap_level', `0')dnl
|
||||
define(`m4wrap',
|
||||
`ifdef(`m4wrap'_m4wrap_level,
|
||||
`define(`m4wrap'_m4wrap_level,
|
||||
defn(`m4wrap'_m4wrap_level)`$1')',
|
||||
`builtin(`m4wrap', `define(`_m4wrap_level',
|
||||
incr(_m4wrap_level))dnl
|
||||
m4wrap'_m4wrap_level)dnl
|
||||
define(`m4wrap'_m4wrap_level, `$1')')')dnl
|
||||
@@ -0,0 +1,10 @@
|
||||
dnl Redefine m4wrap to have LIFO semantics.
|
||||
define(`_m4wrap_level', `0')dnl
|
||||
define(`_m4wrap', defn(`m4wrap'))dnl
|
||||
define(`m4wrap',
|
||||
`ifdef(`m4wrap'_m4wrap_level,
|
||||
`define(`m4wrap'_m4wrap_level,
|
||||
`$1'defn(`m4wrap'_m4wrap_level))',
|
||||
`_m4wrap(`define(`_m4wrap_level', incr(_m4wrap_level))dnl
|
||||
m4wrap'_m4wrap_level)dnl
|
||||
define(`m4wrap'_m4wrap_level, `$1')')')dnl
|
||||
@@ -0,0 +1,9 @@
|
||||
dnl Redefine m4wrap to have LIFO semantics, improved example.
|
||||
include(`join.m4')dnl
|
||||
define(`_m4wrap', defn(`m4wrap'))dnl
|
||||
define(`_arg1', `$1')dnl
|
||||
define(`m4wrap',
|
||||
`ifdef(`_$0_text',
|
||||
`define(`_$0_text', joinall(` ', $@)defn(`_$0_text'))',
|
||||
`_$0(`_arg1(defn(`_$0_text')undefine(`_$0_text'))')dnl
|
||||
define(`_$0_text', joinall(` ', $@))')')dnl
|
||||
Reference in New Issue
Block a user