m4宏语言概览
m4是一个宏处理器,虽然你可能没听过它,但你很可能无意地用过它,例如autoconf工具就依赖这种宏。
m4的原理很简单,m4把输入分解成一系列标记:
- 注释(从注释符(默认
#
)到换行)保持不动输出。 - 名字(由若干字母、数字和下划线组成,不以数字开始),当碰到名字是宏名字(多参数宏还要求马上紧接开括号),就触发宏展开,否则保持不动输出。
- 引用字符串(默认由配对的|和’包围,但这可以动态设置),会被剥去一层包装后输出。
- 其它字符,保持不动输出。
宏展开会尽早进行并且是递归的,即展开后结果会被重新扫描,其中有宏调用的话会继续展开。
宏调用形如宏名
(无参数的)或宏名(参数1, 参数2, ..., 参数N)
(有至少一个参数的,name()
视为以空字符串为参数调用一个单参数宏),多余的参数被忽略,未指定的参数视为空字符串。
由于参数也会被展开,必须注意是否需引用。
宏调用 | 效果 |
---|---|
define (NAME, [EXPANSION]) | 定义名为NAME的宏,它展开为EXPANSION,其中可用$1 等表示其参数。 |
undefine (NAME…) | 取消定义名为各NAME的宏 |
defn (NAME…) | 展开为各NAME指定的宏的定义(被引用) |
pushdef (NAME, [EXPANSION]) | 暂时名为NAME的宏,它展开为EXPANSION,其中可用$1 等表示其参数,掩盖原来的。 |
popdef (NAME…) | 恢复被掩盖的宏 |
indir (NAME, [ARGS…]) | 以ARGS为参数调用名为NAME的宏 |
builtin (NAME, [ARGS…]) | 以ARGS为参数调用名为NAME的内置宏 |
ifdef (NAME, STRING-1, [STRING-2]) | 若定义了名为NAME的宏则展开为STRING-1,否则STRING-2 |
ifelse (COMMENT) | 展开为空 |
ifelse (STRING-1, STRING-2, EQUAL, [NOT-EQUAL]) | STRING-1与STRING-2相等时展开为EQUAL,否则NOT-EQUAL |
ifelse (STRING-1, STRING-2, EQUAL-1, STRING-3, STRING-4,EQUAL-2, …, [NOT-EQUAL]) | STRING-1与STRING-2相等时展开为EQUAL-1,否则抛弃前三参数继续 |
shift (ARG1, …) | 展开为各参数的引用形式按逗号分隔的连接结果 |
dumpdef ([NAMES…]) | 把各NAME的定义(不指定则所有)写到调试文件 |
debugmode ([FLAGS]) | 设置调试模式 |
debugfile ([FILE]) | 把调试信息写到FILE |
dnl | 删去本行余下字符 |
changequote ([START],[END]) | 设置引用开始和结束分隔符 |
changecom ([START],[END]) | 设置注释开始和结束分隔符 |
changeword (REGEX) | 设置宏名满足的正则表达式 |
m4wrap (STRING, …) | 把STRING留待输入结束再读 |
include (FILE) | 展开为导入文件FILE |
sinclude (FILE) | 展开为导入文件FILE(出错时空) |
divert ([NUMBER = ‘0’]) | 把输出放到指定暂存区 |
undivert ([DIVERSIONS…]) | 展开为把暂存区或文件DIVERSION内容 |
divnum | 展开为当前暂存区号 |
len (STRING) | 展开为STRING的长度 |
index (STRING, SUBSTRING) | 展开为SUBSTRING在STRING中首次出现位置(没有则-1) |
regexp (STRING, REGEXP, [REPLACEMENT]) | 展开为REGEXP在STRING中首次出现位置(没有则-1),或者替换结果(没有出现则空) |
substr (STRING, FROM, [LENGTH]) | 展开为STRING的从FROM开始长为LENGTH的子字符串 |
translit (STRING, CHARS, [REPLACEMENT]) | 展开为STRING中CHARS里出现字符换成REPLACEMENT中对应字符(多余的删) |
patsubst (STRING, REGEXP, [REPLACEMENT]) | 展开为STRING中匹配REGEXP的每个部分换成REPLACEMENT,其中可用\N引用捕获组,用\&引用匹配部分 |
format (FORMAT-STRING, …) | 展开为各参数的printf风格格式化 |
incr (NUMBER) | 展开为NUMBER+1 |
decr (NUMBER) | 展开为NUMBER-1 |
eval (EXPRESSION, [RADIX = ‘10’]) | 展开为整数表达式EXPRESSION的值,运算以32位有符号整数进行,其中可用运算符+、-、~、!、*、、/、%、+、-、«、»、>、>=、<、<=、==、!=、&、^、|、&&、|| |
syscmd (SHELL-COMMAND) | 运行SHELL命令并展开为空 |
esyscmd (SHELL-COMMAND) | 运行SHELL命令并展开为它产生的标准输出 |
sysval | 上一个shell命令的返回值 |
mkstemp (TEMPLATE) | 展开为新建临时文件的名字 |
maketemp (TEMPLATE) | 展开为新建临时文件的名字 |
errprint (MESSAGE, …) | 把MESSAGE和其它参数由空格分隔写到标准错误 |
file | 展开为当前文件 |
line | 展开为当前文本行号 |
program | 展开为调用m4的输入文件名 |
m4exit ([CODE = ‘0’]) | 以CODE为返回值退出m4 |
其中没说明展开结果的展开为空。另外,下列宏的存在可指示系统信息:__gnu__
、__os2__
、os2
、__unix__
、unix
、__windows__
、windows
通过用宏避免重复和魔数,可能可以提高可读性和减少错误,也可能使代码更难读。与scheme中的健康宏展开不同,m4和C语言的宏是基于文本的,有可能意外生成破坏作用域甚至不合语法的各种代码,并且因不总出问题而极难调试,而且由于本质上m4也做不到更好。m4以通用性压倒可靠性。