Learning GNU Make Functions with Arithmetic

# The following operators return a non-empty string if their result
# is true:
# gt First argument greater than second argument
# gte First argument greater than or equal to second argument
# lt First argument less than second argument

# lte First argument less than or equal to second argument
# eq First argument is numerically equal to the second argument
# ne First argument is not numerically equal to the second argument

gt = $(filter-out $(words $2),$(words $(call max,$1,$2)))
lt = $(filter-out $(words $1),$(words $(call max,$1,$2)))
eq = $(filter $(words $1),$(words $2))
ne = $(filter-out $(words $1),$(words $2))
gte = $(call gt,$1,$2)$(call eq,$1,$2)
lte = $(call lt,$1,$2)$(call eq,$1,$2)

# increment adds 1 to its argument, decrement subtracts 1. Note that
# decrement does not range check and hence will not underflow, but
# will incorrectly say that 0 - 1 = 0

increment = $1 x
decrement = $(wordlist 2,$(words $1),$1)

# double doubles its argument, and halve halves it

double = $1 $1
halve = $(subst xx,x,$(filter-out xy x y,$(join $1,$(foreach a,$1,y x))))

# This code implements a reverse polish notation calculator by
# transforming a comma-separated list of operators (+ - * /) and
# numbers stored in the calc variable into the appropriate calls to
# the arithmetic functions defined in this Makefile

# This is the current stack of numbers entered into the
# calculator. The push function puts an item onto the top of the stack
# (the start of the list) and pop removes the top item.

stack :=

push = $(eval stack := $$1 $(stack))
pop = $(word 1,$(stack))$(eval stack := $(wordlist 2,$(words $(stack)),$(stack)))

# pope and pushd are used to pop a number off the stack and encode it
# and push a number onto the stack after decoding

pope = $(call encode,$(call pop))
pushd = $(call push,$(call decode,$1))

# calculate runs through the input numbers and operations and either
# pushes a number on the stack, or pops two numbers off and does a
# calculation followed by pushing the result back. When calculate
# finished there will be one item on the stack which is the result.

comma := ,
calculate=$(foreach t,$(subst $(comma), ,$1),$(call handle,$t))$(stack)

# seq is a string equality operator that returns true (a non-empty
# string) if the two strings are equal

seq = $(filter $1,$2)

# handle is used by calculate to handle a single token. If it's an
# operator then the appropriate operator function is called, if it's a
# number then it is pushed.

handle = $(call pushd, \
$(if $(call seq,+,$1), \
$(call plus,$(call pope),$(call pope)), \
$(if $(call seq,-,$1), \
$(call subtract,$(call pope),$(call pope)), \
$(if $(call seq,*,$1), \
$(call multiply,$(call pope),$(call pope)), \
$(if $(call seq,/,$1), \
$(call divide,$(call pope),$(call pope)), \
$(call encode,$1))))))

.PHONY: calc
calc: ; @echo $(call calculate,$(calc))

About the author

John Graham-Cumming's picture John Graham-Cumming

John Graham-Cumming is Co-Founder at Electric Cloud, Inc . Prior to joining Electric Cloud, John was a Venture Consultant with Accel Partners, VP of Internet Technology at Interwoven, Inc. (IWOV), VP of Engineering at Scriptics Corporation (acquired by Interwoven), and Chief Architect at Optimal Networks, Inc. John holds BA and MA degrees in Mathematics and Computation and a Doctorate in Computer Security from Oxford University. John is the creator of the highly acclaimed open source POPFile project. He also holds two patents in network analysis and has others pending.

AgileConnection is one of the growing communities of the TechWell network.

Featuring fresh, insightful stories, TechWell.com is the place to go for what is happening in software development and delivery.  Join the conversation now!

Upcoming Events

Sep 22
Sep 24
Oct 12
Nov 09