陈颂光
全栈工程师,能够独立开发从解释器到网站和桌面/移动端应用的各类软件。
关注我的 GitHub

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以通用性压倒可靠性。

关键词 m4 unix 编程语言