1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 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
|