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