Skip to content

Instantly share code, notes, and snippets.

@amagura
Created January 10, 2015 13:58
Show Gist options
  • Save amagura/b3fc9ccab6e5433e7d40 to your computer and use it in GitHub Desktop.
Save amagura/b3fc9ccab6e5433e7d40 to your computer and use it in GitHub Desktop.
M4 - 99 bottles of beer song
dnl GNU M4 - A macro language
dnl by Alexej Magura
divert(-1)
dnl Removes all the trailing newlines and
dnl prevents unnecessary usage of `dnl'
dnl (i.e. using `dnl' to remove trailing newlines)
dnl The `expand` macros are from Autoconf.
dnl Everything else is me.
define(`expand', `_$0(-=<{($1)}>=-)')
define(`_expand',`changequote(`-=<{(', `)}>=-')$1changequote(`, ')')
dnl Return the expansion of ARG as a single string.
define(`cmp',dnl
`ifelse(eval((`$1' == `$2')), 1, 0, ifelse(eval((`$1' > `$2')), 1, 1, -1))')
dnl expands to:
dnl 0: `$1' and `$2' are equal
dnl 1: `$1' is greater than `$2'
dnl -1: `$2' is greater than `$1'
define(`for_', `pushdef(`hdx', `incr($1)')define(`$1', decr(expand($1)))dnl
hdx sing_and_drink(`hdx', `$2')
ifelse(cmp($1, $2), 1, `$0(`$1', `$2', `sing_and_drink(`$1', `$2')')')')
dnl Not a true generic for-loop, since
dnl it calls `song_and_drink' directly from with-in.
dnl NOTE that the `dnl' in `for' is required
dnl since we are inside of a macro definition.
define(`beer_left', 99)
define(`__drink__', `beer')
dnl since M4 is a preprocessing macro language
dnl it can be used to make simple textual replacements
define(`__bottle__',
`ifelse(`$2', `suffix',pushdef(`idx', decr(`$1'))ifelse(cmp($1, 1), 0, `idx bottle',
cmp($1, 0), 0, `no more bottles',
cmp($1, -1), 0, `99 bottles',
cmp($1, 2), 1, `idx bottles',
cmp($1, 2), 0, `idx bottle'),
cmp($1, 1), 1, `bottles',
cmp($1, 1), 0, `1 bottle',
cmp($1, 0), 0, `No more bottles')')
dnl `ifelse' works like a `switch' statement if it is given 6 or more args
define(`sing_and_drink',
`ifelse(cmp($1, $2), 1,`__bottle__($1) of __drink__ on the wall, __bottle__(incr($1), `suffix') of __drink__.
Take one down and pass it around, __bottle__(ifelse($1, 1, 0, $1), `suffix') of __drink__ on the wall.
',cmp($1, $2), 0, `__bottle__($1) of __drink__ on the wall, __bottle__($1, `suffix') of __drink__.
Go to the store and buy some more, __bottle__(decr($1), `suffix') of __drink__ on the wall.
')')
divert(0)dnl
for_(`beer_left', 1)dnl
sing_and_drink(1, 0)
sing_and_drink(0, 0)dnl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment