blob: 2f8d5ba6dc8ad25ab3250b4afc24e6ca8d1c7d08 [file] [log] [blame]
# Hand crafted tests for GNU M4. -*- Autotest -*-
# Copyright (C) 2001, 2006-2008, 2010, 2013-2014, 2017 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 <http://www.gnu.org/licenses/>.
AT_BANNER([Macro definitions.])
# Checking everything related to macro definitions: the expansion of
# user macros, the propagation of various bits (tracing, number of
# arguments and so on).
## ---------------- ##
## Arity and defn. ##
## ---------------- ##
AT_SETUP([Arity and defn])
# Check that the arity checking of define is correctly propagated.
AT_DATA([[input.m4]],
[[define(`defun', defn(`define'))
define
define(`foo')
define(`foo', `bar')
define(`foo', `bar', `baz')
defun
defun(`foo')
defun(`foo', `bar')
defun(`foo', `bar', `baz')
]])
AT_DATA([[expout]],
[[
define
defun
]])
AT_CHECK_M4([input.m4], 0, [expout],
[[m4:input.m4:5: warning: define: extra arguments ignored: 3 > 2
m4:input.m4:10: warning: defun: extra arguments ignored: 3 > 2
]])
AT_CLEANUP
## ------------------------- ##
## Arity, defn, and freeze. ##
## ------------------------- ##
AT_SETUP([Arity, defn, and freeze])
AT_KEYWORDS([frozen])
AT_DATA([[freezeme.m4]],
[[define(`defun', defn(`define'))dnl
undefine(`define')dnl
]])
AT_CHECK_M4([--freeze-state=frozen.m4f freezeme.m4], 0)
AT_DATA([[input.m4]],
[[defun
defun(`foo')
defun(`foo', `bar')
defun(`foo', `bar', `baz')
]])
AT_DATA([[expout]],
[[defun
]])
AT_CHECK_M4([--reload-state=frozen.m4f input.m4], 0, expout,
[[m4:input.m4:4: warning: defun: extra arguments ignored: 3 > 2
]])
AT_CLEANUP(freezeme.m4 frozen.m4f)
## ------------------- ##
## Command line define ##
## ------------------- ##
AT_SETUP([Command line define])
dnl Test that -D after last file still affects m4wrap'd text.
AT_DATA([in1], [[m4wrap(`foo
')foo
]])
AT_DATA([in2], [[foo
]])
AT_CHECK_M4([-Dfoo=1 in1 -Dfoo=2 in2 -Dfoo=3], [0],
[[1
2
3
]])
dnl Test that -D only affects top-most definition.
AT_DATA([in1], [[define(`foo', `1')pushdef(`foo', `2')dnl
]])
AT_DATA([in2], [[foo
popdef(`foo')foo
popdef(`foo')foo
]])
AT_CHECK_M4([in1 -Dfoo=3 in2], [0],
[[3
1
foo
]])
dnl Test that -D and -U interact in correct order
AT_DATA([in], [[foo
]])
AT_CHECK_M4([-Dfoo=bar in -Ufoo in], [0], [[bar
foo
]])
AT_CHECK_M4([-Ufoo in -Dfoo=bar in], [0], [[foo
bar
]])
dnl Test macro arguments defined via -D
AT_DATA([in], [[-foo-foo(1)-foo(1,2)-
-bar-bar(1)-bar(1,2)-
]])
AT_CHECK_M4([-Dfoo -Dbar='$@' in], [0],
[[----
--1-1,2-
]])
AT_CLEANUP
## -------------------- ##
## Command line pushdef ##
## -------------------- ##
AT_SETUP([Command line pushdef])
dnl Test that -p after last file still affects m4wrap'd text.
AT_DATA([in1], [[m4wrap(`foo
')foo
]])
AT_DATA([in2], [[foo
]])
AT_CHECK_M4([-pfoo=1 in1 -pfoo=2 in2 -pfoo=3], [0],
[[1
2
3
]])
dnl Test that -p adds a definition.
AT_DATA([in1], [[define(`foo', `1')pushdef(`foo', `2')dnl
]])
AT_DATA([in2], [[foo
popdef(`foo')foo
popdef(`foo')foo
]])
AT_CHECK_M4([in1 -pfoo=3 in2], [0],
[[3
2
1
]])
dnl Test that --pushdef and --popdef interact in correct order
AT_DATA([in], [[foo
]])
AT_CHECK_M4([-Dfoo=1 --pushdef=foo=2 in --popdef=foo in], [0],
[[2
1
]])
AT_CHECK_M4([--popdef=foo in --pushdef=foo=1 in], [0],
[[foo
1
]])
AT_CLEANUP
## ---------------- ##
## pushdef/popdef. ##
## ---------------- ##
AT_SETUP([pushdef/popdef])
AT_DATA([[pushpop.m4]],
[[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')
dumpdef(`mac2')
popdef(`mac2')
]])
AT_CHECK_M4([pushpop.m4], 0, [],
[[hej: `def 1.'
hej: `def 2.'
hej: `def 3.'
hej: `def 4.'
hej: `def 3.'
hej: `def 2.'
hej: `def 1.'
m4:pushpop.m4:18: warning: dumpdef: undefined macro 'hej'
m4:pushpop.m4:20: warning: dumpdef: undefined macro 'mac2'
m4:pushpop.m4:21: warning: popdef: undefined macro 'mac2'
]])
AT_CLEANUP
## ---------------------- ##
## Tracing Hanoi Towers. ##
## ---------------------- ##
AT_SETUP([Tracing Hanoi Towers])
AT_DATA([[trace.m4]],
[[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)',
`_hanoi(decr($1), $2, $4, $3)move($2, $3)_hanoi(decr($1), $4, $3, $2)')')
# hanoi (cnt)
define(`hanoi', `_hanoi(`$1', source, destination, auxilliary)')
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)
]])
AT_DATA([[expout]],
[[
# Debugmode t
Move one disk from source to auxilliary.
Move one disk from source to destination.
Move one disk from auxilliary to destination.
# Debugmode taeq
Move one disk from source to auxilliary.
Move one disk from source to destination.
Move one disk from auxilliary to destination.
# Debugmode OFF
Move one disk from source to auxilliary.
Move one disk from source to destination.
Move one disk from auxilliary to destination.
# Debugmode ae
Move one disk from source to auxilliary.
Move one disk from source to destination.
Move one disk from auxilliary to destination.
]])
AT_DATA([[experr]],
[[m4trace: -1- hanoi
m4trace: -1- _hanoi
m4trace: -2- eval
m4trace: -1- ifelse
m4trace: -2- decr
m4trace: -1- _hanoi
m4trace: -2- eval
m4trace: -1- ifelse
m4trace: -1- move
m4trace: -1- move
m4trace: -2- decr
m4trace: -1- _hanoi
m4trace: -2- eval
m4trace: -1- ifelse
m4trace: -1- move
m4trace: -1- debugmode
m4trace: -1- hanoi(`2') -> `_hanoi(`2', source, destination, auxilliary)'
m4trace: -1- _hanoi(`2', `source', `destination', `auxilliary') -> `ifelse(eval(`2'<=1), 1, `move(source, destination)',
`_hanoi(decr(2), source, auxilliary, destination)move(source, destination)_hanoi(decr(2), auxilliary, destination, source)')'
m4trace: -2- eval(`2<=1') -> `0'
m4trace: -1- ifelse(`0', `1', `move(source, destination)', `_hanoi(decr(2), source, auxilliary, destination)move(source, destination)_hanoi(decr(2), auxilliary, destination, source)') -> `_hanoi(decr(2), source, auxilliary, destination)move(source, destination)_hanoi(decr(2), auxilliary, destination, source)'
m4trace: -2- decr(`2') -> `1'
m4trace: -1- _hanoi(`1', `source', `auxilliary', `destination') -> `ifelse(eval(`1'<=1), 1, `move(source, auxilliary)',
`_hanoi(decr(1), source, destination, auxilliary)move(source, auxilliary)_hanoi(decr(1), destination, auxilliary, source)')'
m4trace: -2- eval(`1<=1') -> `1'
m4trace: -1- ifelse(`1', `1', `move(source, auxilliary)', `_hanoi(decr(1), source, destination, auxilliary)move(source, auxilliary)_hanoi(decr(1), destination, auxilliary, source)') -> `move(source, auxilliary)'
m4trace: -1- move(`source', `auxilliary') -> `Move one disk from `source' to `auxilliary'.
'
m4trace: -1- move(`source', `destination') -> `Move one disk from `source' to `destination'.
'
m4trace: -2- decr(`2') -> `1'
m4trace: -1- _hanoi(`1', `auxilliary', `destination', `source') -> `ifelse(eval(`1'<=1), 1, `move(auxilliary, destination)',
`_hanoi(decr(1), auxilliary, source, destination)move(auxilliary, destination)_hanoi(decr(1), source, destination, auxilliary)')'
m4trace: -2- eval(`1<=1') -> `1'
m4trace: -1- ifelse(`1', `1', `move(auxilliary, destination)', `_hanoi(decr(1), auxilliary, source, destination)move(auxilliary, destination)_hanoi(decr(1), source, destination, auxilliary)') -> `move(auxilliary, destination)'
m4trace: -1- move(`auxilliary', `destination') -> `Move one disk from `auxilliary' to `destination'.
'
m4trace: -1- debugmode -> `'
m4trace: -1- _hanoi(2, source, destination, auxilliary) -> ifelse(eval(`2'<=1), 1, `move(source, destination)',
`_hanoi(decr(2), source, auxilliary, destination)move(source, destination)_hanoi(decr(2), auxilliary, destination, source)')
m4trace: -1- _hanoi(1, source, auxilliary, destination) -> ifelse(eval(`1'<=1), 1, `move(source, auxilliary)',
`_hanoi(decr(1), source, destination, auxilliary)move(source, auxilliary)_hanoi(decr(1), destination, auxilliary, source)')
m4trace: -1- move(source, auxilliary) -> Move one disk from `source' to `auxilliary'.
m4trace: -1- move(source, destination) -> Move one disk from `source' to `destination'.
m4trace: -1- _hanoi(1, auxilliary, destination, source) -> ifelse(eval(`1'<=1), 1, `move(auxilliary, destination)',
`_hanoi(decr(1), auxilliary, source, destination)move(auxilliary, destination)_hanoi(decr(1), source, destination, auxilliary)')
m4trace: -1- move(auxilliary, destination) -> Move one disk from `auxilliary' to `destination'.
]])
AT_CHECK_M4([trace.m4], 0, expout, experr)
AT_CLEANUP
## ------------------------------- ##
## Propagation of trace requests. ##
## ------------------------------- ##
AT_SETUP([Propagation of traceon])
AT_DATA([[trace2.m4]],
[[traceon(`define')
debugmode(`aeq')
# copy the `define' builtin definition to another symbol
define(`my_define', defn(`define'))
# delete the original
undefine(`define')
# Does it work?
my_define(`foo', `bar')
# Use the new definition to redefine the original symbol
my_define(`define', defn(`my_define'))
# Torture the flag propogation
undefine(`my_define')
define(`my_define', defn(`define'))
# There are now 2 symbols pointing to the same builtin function
my_define(`foo', `bar')
define(`foo', `bar')
]])
AT_DATA([[expout]],
[[
# copy the `define' builtin definition to another symbol
# delete the original
# Does it work?
# Use the new definition to redefine the original symbol
# Torture the flag propogation
# There are now 2 symbols pointing to the same builtin function
]])
AT_DATA([[experr]],
[[m4trace: -1- define(`my_define', <define>) -> `'
m4trace: -1- define(`my_define', <define>) -> `'
m4trace: -1- define(`foo', `bar') -> `'
]])
AT_CHECK_M4([trace2.m4], 0, expout, experr)
AT_CLEANUP
## ------------------------ ##
## Propagation of --trace. ##
## ------------------------ ##
AT_SETUP([Propagation of --trace])
AT_DATA([[trace3.m4]],
[[# copy the `define' builtin definition to another symbol
define(`my_define', defn(`define'))
# delete the original
undefine(`define')
# Does it work?
my_define(`foo', `bar')
# Use the new definition to redefine the original symbol
my_define(`define', defn(`my_define'))
# Torture the flag propogation
undefine(`my_define')
define(`my_define', defn(`define'))
# There are now 2 symbols pointing to the same builtin function
my_define(`foo', `bar')
define(`foo', `bar')
]])
AT_DATA([[expout]],
[[# copy the `define' builtin definition to another symbol
# delete the original
# Does it work?
# Use the new definition to redefine the original symbol
# Torture the flag propogation
# There are now 2 symbols pointing to the same builtin function
]])
AT_DATA([[experr]],
[[m4trace: -1- define(`my_define', <define>) -> `'
m4trace: -1- define(`my_define', <define>) -> `'
m4trace: -1- define(`foo', `bar') -> `'
]])
AT_CHECK_M4([-t define -daeq trace3.m4], 0, expout, experr)
AT_CLEANUP
## --------------------- ##
## Renamesyms collisions ##
## --------------------- ##
AT_SETUP([Renamesyms collisions])
dnl FIXME - We should gracefully detect rename collisions, rather than
dnl violating the invariants of the symbol table.
AT_XFAIL_IF([:])
AT_DATA([in], [[define(`bar', `1')define(`baz', `2')dnl
renamesyms(`^ba.$', `baa')
]])
AT_CHECK_M4([in], [0], [[
]], [ignore])
AT_CLEANUP
## ----------------- ##
## Rescanning macros ##
## ----------------- ##
AT_SETUP([Rescanning macros])
dnl This is a series of tests that used to be included as undocumented tests
dnl in the branch m4.texinfo. They exercise rescanning issues not stressed
dnl anywhere else in the suite, but which are used by autoconf.
AT_DATA([in], [[define(`x1', `len(`$1'')dnl
define(`y1', ``$1')')dnl
x1(`01234567890123456789')y1(`98765432109876543210')
]])
AT_CHECK_M4([in], [0], [[40
]])
AT_DATA([in], [[define(`echo', `$@')dnl
define(`foo', echo(`01234567890123456789')echo(`98765432109876543210'))dnl
foo
]])
AT_CHECK_M4([in], [0], [[0123456789012345678998765432109876543210
]])
AT_DATA([in], [[define(`a', `A')define(`echo', `$@')define(`join', `$1$2')dnl
define(`abcdefghijklmnopqrstuvwxyz', `Z')dnl
join(`a', `bcdefghijklmnopqrstuvwxyz')
join(`a', echo(`bcdefghijklmnopqrstuvwxyz'))
]])
AT_CHECK_M4([in], [0], [[Z
Z
]])
AT_DATA([in], [[define(`echo', `$@')dnl
echo(echo(`01234567890123456789', `01234567890123456789')
echo(`98765432109876543210', `98765432109876543210'))
len((echo(`01234567890123456789',
`01234567890123456789')echo(`98765432109876543210',
`98765432109876543210')))
indir(`echo', indir(`echo', `01234567890123456789',
`01234567890123456789')
indir(`echo', `98765432109876543210', `98765432109876543210'))
define(`argn', `$#')dnl
define(`echo1', `-$@-')define(`echo2', `,$@,')dnl
echo1(`1', `2', `3') argn(echo1(`1', `2', `3'))
echo2(`1', `2', `3') argn(echo2(`1', `2', `3'))
]])
AT_CHECK_M4([in], [0], [[01234567890123456789,01234567890123456789
98765432109876543210,98765432109876543210
84
01234567890123456789,01234567890123456789
98765432109876543210,98765432109876543210
-1,2,3- 3
,1,2,3, 5
]])
AT_CLEANUP