In the previous section, I noted that it is not possible to perform arithmetic in * make * using only its native features. I then showed how you could implement a simple counter by appending words to a list and returning the length of the list. Soon after I discovered the increment trick, Michael Mounteney posted a cool trick for performing a limited form of addition on integers in * make * . His trick manipulates the number line to compute the sum of two integers of size one or greater. To see how this works, imagine the number line: 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Now, notice that (if we could get the subscripts right), we could add, say 4 plus 5, by first taking a subset of the line from the fourth element to the end then selecting the fifth element of the subset. We can do this with native * make * functions: number_line = 2 3 4 5 6 7 8 9 10 11 12 13 14 15 plus = $(word , $(wordlist , 15, $(number_line))) four+five = $(call plus, 4, 5) Very clever, Michael! Notice that the number line starts with 2 rather than 0 or 1. You can see that this is necessary if you run the * plus * function with 1 and 1. Both subscripts will yield the first element and the answer must be 2, therefore, the first element of the list must be 2. The reason for this is that, for the * word * and * wordlist * functions, the first element of the list has subscript 1 rather than (but I haven't bothered to prove it). Now, given a number line, we can perform addition, but how do we create a number line in * make * without typing it in by hand or using a shell program? We can create all numbers between 00 and 99 by combining all possible values in the tens place with all possible values in the ones place. For example: make -f - <<< '$(warning $(foreach i, 0 1 2, $(addprefix $i, 0 1 2)))' /c/TEMP/Gm002568:1: 00 01 02 10 11 12 20 21 22 By including all digits 0 through 9, we would produce all numbers from 00 to 99. By combining the ` foreach ` again with a hundreds column, we would get the numbers from 000 to 999, etc. All that is left is to strip the leading zeros where necessary. Here is a modified form of Mr. Mounteney's code to generate a number line and define the * plus * and * gt * operations: # combine - concatentate one sequence of numbers with another combine = $(foreach i, , $(addprefix $i, )) # stripzero - Remove one leading zero from each word stripzero = $(patsubst 0%,%,) # generate - Produce all permutations of three elements from the word list generate = $(call stripzero, \ $(call stripzero, \ $(call combine, , \ $(call combine, , )))) # number_line - Create a number line from 0 to 999 number_line := $(call generate,0 1 2 3 4 5 6 7 8 9) length := $(word $(words $(number_line)), $(number_line)) # plus - Use the number line to add two integers plus = $(word , \ $(wordlist , $(length), \ $(wordlist 3, $(length), $(number_line)))) # gt - Use the number line to determine if is greater than gt = $(filter , \ $(wordlist 3, $(length), \ $(wordlist , $(length), $(number_line)))) all: @echo $(call plus,4,7) @echo $(if $(call gt,4,7),is,is not) @echo $(if $(call gt,7,4),is,is not) @echo $(if $(call gt,7,7),is,is not) When run, the * makefile * yields: $ make 11 is not is is not We can extend this code to include subtraction by noting that subscripting a reversed list is just like counting backwards . For example, to compute 7 minus 4, first create the number line subset 0 to 6, reverse it, then select the fourth element: number_line := 0 1 2 3 4 5 6 7 8 9... 1through6 := 0 1 2 3 4 5 6 reverse_it := 6 5 4 3 2 1 0 fourth_item := 3 Here is the algorithm in * make * syntax: # backwards - a reverse number line backwards := $(call generate, 9 8 7 6 5 4 3 2 1 0) # reverse - reverse a list of words reverse = $(strip \ $(foreach f, \ $(wordlist 1, $(length), $(backwards)), \ $(word $f, ))) # minus - compute minus minus = $(word , \ $(call reverse, \ $(wordlist 1, , $(number_line)))) minus: # $(call minus, 7, 4) Multiplication and division are left as an exercise for the reader. |