我想做个网站怎么做的,常用的网络营销方法及效果,网站icp备案新规,潮州网站搭建找谁2019独角兽企业重金招聘Python工程师标准 M4 宏处理器 Brian W. KernighanDennis M. Ritchie Bell LaboratoriesMurray Hill, New Jersey 07974 翻译#xff1a;寒蝉退士 译者声明#xff1a;译者对译文不做任何担保#xff0c;译者对译文不拥有任何权利并且不… 2019独角兽企业重金招聘Python工程师标准 M4 宏处理器 Brian W. KernighanDennis M. Ritchie Bell LaboratoriesMurray Hill, New Jersey 07974 翻译寒蝉退士 译者声明译者对译文不做任何担保译者对译文不拥有任何权利并且不负担任何责任和义务。原文http://cm.bell-labs.com/7thEdMan/vol2/m4 摘要 M4 是在 UNIX ® 和 GCOS 上可用的宏处理器。它的主要用途是在无参数宏不够强力的情况下用做 Ratfor 的前端。它也被用于各异的语言如 C 和 Cobol。M4 特别适合于函数式语言如 Fortran、PL/I 和 C因为宏是以函数表示法指定的。 M4 提供了即使在更大的宏处理器中都少见的特征它包括了 参数条件测试算术功能字符串和子串函数文件操纵本文是 M4 用户手册。 July 1, 1977 介绍 宏处理器是增强一门编程语言使它更加美味或更加可读或者把它裁剪得适合特定应用的有用的方式。C 语言中的 #define 语句和 Ratfor 中类似的 define 就是任何宏处理器都提供的基本设施的例子 — 它把文本替代为其他文本。 M4 宏处理器是对叫做 M3 的宏处理器的扩展它是 D. M. Ritchie 为 AP-3 小型机写的M3 基于的是为 [1] 实现的宏处理器。不熟悉宏处理基本概念的读者可以读一下其中的某些讨论。 M4 适合于做 Ratfor 和 C 的前端而且还成功的用于 Cobol。除了直接把一个字符串替代为另一个字符串之外它还提供带有参数的宏、有条件的宏扩展、算术、文件操纵和某些特 殊的字符串处理函数。 M4 的基本操作把输入复制到输出。但在读入输入的时候检查每个字母数字(alphanumeric)的“记号”(token) (就是说字母和数字的字符串)。如果它是一个宏的名字则把这个宏的名字替代为它所定义的文本把结果的字符串压回到输入中被重新扫描。调用宏可以带有参数在这种情况下参数被收集并在重新扫描之前替换入定义的文本中正确的位置上。 M4 提供了一组大约二十个内置宏来进行各种有用的操作此外用户可以定义新宏。内置和用户定义的宏以完全一样的方式工作除了某些内置宏有对处理器状态的副作用之外。 用法 在 UNIX 上使用 m4 [files] 依次处理每个参数文件如果没有参数或参数是‘−’则在此位置上读取标准输入。处理后的文本写到标准输入它可以被后续处理用下列方式捕获 m4 [files] 输出文件 在 GCOS 上用法是一样的但程序叫做 ./m4。 定义宏 M4 的首要内置函数是 define它被用来定义新宏。输入 define(name, stuff) 导致把字符串 name 定义为 stuff。name 的所有后续出现都被替代为 stuff。name 必须是字母数字的并且必须开始于字母(下划线 _ 被当作字母)。stuff 是包含配对的圆括号的任何文本它可以伸展到多行之上。 所以作为典型的例子 define(N, 100)
...
if (i N) 定义 N 为 100并在后面的 if 语句中使用了这个“符号常量”。 define 必须立即跟随着左圆括号它通知 define 有参数。如果一个宏或内置的名字没有立即跟随着‘(’则假定它没有参数。这是上面 N 的情况它实际上是一个没有参数的宏所以在后面用到它的时候不要跟随着 (...)。 ---- 第 1 页完 ---- 你还应注意宏名字只在它出现被非字母数字围绕的时候才被识别。例如在 define(N, 100)
...
if (NNN 100) 变量 NNN 绝对与定义的宏 N 无关即使它包含很多个 N。 可以把事物定义为另一个事物。例如 define(N, 100)
define(M, N) 定义 M 和 N 二者都是 100。 如果 N 被重新定义了会怎样呢? 或者换个方式说M 被定义为 N 还是 100? 在 M4 中后者是真的 — M 是 100所以即使 N 随后改变了M 也不变。 引起这种行为的原因是M4 尽可能早的把宏名字展开为它们的定义文本。这意味着在收集 define 的参数时见到字符串 N 的时候他立即被替代为 100如同你首先写的是 define(M, 100) 如果这不是你实际上想要的还有两种方式可用。首先特定于这种情况可以对换两个定义的次序: define(M, N)
define(N, 100) 现在 M 被定义为字符串 N所以当你以后查找 M 的时候你将总是得到那时 N 的值(因此 M 会被替代为 N它又会被替代为 100)。 引用 更一般的解决方式是通过引用上 define 的参数来延迟它们的展开。围绕着单引号 和 ´ 的任何文本都不被立即展开而是剥除这些引号。如果你写 define(N, 100)
define(M, N´) 围绕着 N 的引号在收集参数的时候被剥除它们已经完成了使命M 被定义为字符串 N而不是 100。M4 的普遍规则是在它求值某个东西的时候总是剥除一层单引号。即使是在宏外部也是这样。如果你希望字 define 出现在输出中你必须在输入中引用它比如 define´ 1; 作为同一事情的有些令人惊讶的另一个实例考虑重定义 N: define(N, 100)
...
define(N, 200) 可能有些遗憾在第二个定义中的 N 在被见到之后马上就被求值就是说它被替代为 100所以如同你写的是 define(100, 200) 这个语句被 M4 忽略因为你只能定义看起来是名字的东西并且这明显的不是你想要的效果。要真正的重定义 N你必须通过引用延迟这个求值: define(N, 100)
...
define(N´, 200) 在 M4 中引用宏的第一个参数经常是明智的。 如果出于某种原因使用 和 ´ 不方便可以用内置 changequote 改变引用字符 changequote([, ]) 使左右方括号成为引号字符。恢复最初的字符只需要 changequote 有两个增补的与 define 有关的内置。undefine 去除某个宏或内置的定义: undefine(N´) 去除 N 的定义。(为什么引号是绝对必须的?) 内置可以用 undefine 去除比如 ---- 第 2 页完 ---- undefine(define´) 但是一旦你去除了它就再也不能让它回来了。 内置 ifdef 提供了确定一个宏是否定义了的方式。特别是M4 在相应的操作系统上有预定义的名字 unix 和 gcos你可以如下这样确定正在使用的是哪个: ifdef(unix´, define(wordsize,16)´ )
ifdef(gcos´, define(wordsize,36)´ ) 使一个定义适合特定的机器。不要忘记引号! ifdef 实际上允许三个参数如果这个名字未定义ifdef 的值就是第三个参数比如 ifdef(unix´, on UNIX, not on UNIX) 参数 迄今为止我们已经讨论了宏处理的最简单形式 — 把一个字符串替代为另一个(固定的)字符串。用户定义的宏也可以有参数因此不同的调用可以有不同的结果。 在一个宏的替代文本内(define 的第二个参数)$n 的任何出现都被替代为实际使用这个宏的时候的第 n 个参数。所以如下定义的宏 bump define(bump, $1 $1 1) 生成把它的参数加上 1 的代码: bump(x) 是 x x 1 宏可以有同你想要的一样多的参数但只有前九个是可以访问的从 $1 到 $9。(宏名字自身是 $0但不常用)。没有被提供的参数被替代为空字符串所以可以通过简单的串联它的参数来定义一个宏 cat比如: define(cat, $1$2$3$4$5$6$7$8$9) 所以 cat(x, y, z) 等价于 xyz $4 到 $9 是空的因为没有提供相应的参数。 丢弃在参数收集期间出现的前导的未引用的空格、tab 或换行。保留所有其他空白。所以 define(a, b c) 定义 a 为 b c。 参数用逗号分隔但是圆括号可以被正确处理所以逗号可以用圆括号“保护”起来而不终止一个参数。就是说如下之中 define(a, (b,c)) 只有两个参数第二个参数是文字的 (b,c)。当然可以通过引用它来插入裸露的逗号或圆括号。 算术内置 M4 (只)提供两个做整数算术的内置函数。最简单的是 incr,它向数值参数增加 1。要处理常见的编程情况你希望定义一个变量为“比 N 多一”可以写 define(N, 100)
define(N1, incr(N)´) 则定义 N1 为比 N 的当前值多一。 更一般的算术机制是叫做 eval 的内置函数它有能力做关于整数的任意算术。它提供如下算符(按优先级递减的次序) 一元 和 或 (指数)(模)(非) 或 (逻辑与) 或 (逻辑或) 可以用圆括号在需要的地方组合算符。给 eval 的所有操作数(operand)最终必须是数值。真关系(如 10)的数值值为 1而假为 0。eval 的精度在 UNIX 上是 32 位在 GCOS 上是 36 位。 ---- 第 3 页完 ---- 作为一个简单例子假设我们希望 M 是 2**N1。则 define(N, 3)
define(M, eval(2**N1)´) 作为法则建议你引用对宏定义的文本除非它确实非常简单(就是说是个数)这通常会给出你想要的结果这是个好习惯。 文件操纵 你可以通过内置 include 在任何时候包含一个新文件到输入中: include(filename) 把 filename 的内容插入到 include 命令的位置中。这个文件的内容经常是一组定义。include 的值(就是它的替代文本)是这个文件的内容它可以 捕获于这些定义中。 在 include 中的文件不能被访问是致命的错误。要获得对这种情况的某种控制可以使用替代形式的 sincludesinclude (“silent include”)在这个文件不能被访问的时候什么都不说并继续。 还可以在处理期间把 M4 的输出转向到临时文件输出紧随这个命令后收集到的材料。M4 维护九个这种转向文件编号从 1 到 9。如果你写 divert(n) 所有后续的输出被添加到 n 所引用的临时文件的结束处。通过另一个 divert 停止转向到这个文件特别是 divert 或 divert(0) 恢复到正常的输出处理。 被转向的文本通常都在处理结束时按数字次序串联起来输出。但是也可以在任何时间取回被转向的文本就是说把它们添加到当前转向文件之上。 undivert 按数字次序取回所有转向文本带有参数的 undivert 按给定的次序取回选择的转向文本。去转向的活动丢弃已被转向的材料转向到编号不在 0 到 9 之间的转向文件的东西也被丢弃。 undivert 的值不是被转向的材料。此外被转向的材料不被宏所重新扫描。 内置 divnum 返回当前活跃的转向文件的编号在正常处理期间它是零。 系统命令 你可以通过 syscmd 内置运行在本地操作系统中任何程序。例如 syscmd(date) 在 UNIX 上运行 date 命令。通常使用 syscmd 来为随后的 include 建立文件。 为了实现制作唯一的文件名字提供了内置的 maketemp带有同系统函数 mktemp 一致的规定: 在参数中的字符串 XXXXX 被替代为当前进程的进程 id。 条件 有一个叫做 ifelse 的内置使你能进行任意的条件测试。在最简单的形式中 ifelse(a, b, c, d) 比较两个字符串 a 和 b。如果它们是同样的ifelse 返回字符串 c否则返回 d。因此我们可以定义叫做 compare 的一个宏它比较两个字符串并返回“yes”或“no”分别在它们为相同的或不同的的时候。 define(compare, ifelse($1, $2, yes, no)´) 注意引号它防止 ifelse 的过早求值。 如果省略第四个参数它被当作空。 ifelse 实际上可以用任何数目的参数因此提供了有限形式的多路判断的能力。在输入中 ifelse(a, b, c, d, e, f, g) 如果字符串 a 匹配字符串 b结果为 c。否则如果 d 同于 e结果为 f。否则结果为 g。如果省略了最后的参数结果为空所以 ifelse(a, b, c) 如果 a 匹配 b 则为 c否则为空。 ---- 第 4 页完 ---- 字符串操纵 内置 len 返回构成它的参数的字符串的长度。因此 len(abcdef) 是 6而 len((a,b)) 是 5。 可以使用内置 substr 来生成字符串的子串。substr(s, i, n) 返回 s 的从第 i 个位置(起始于零)开始的 n 个字符长的子串。如果省略了 n返回余下的字符串所以 substr(now is the time´, 1) 是 ow is the time 如果 i 或 n 超出了范围各种偶然的事情都可能发生。 index(s1, s2) 返回在 s1 中字符串 s2 出现的索引(位置)或者 −1 如果它没有出现的话。同于 substr字符串起始于 0。 内置 translit 进行字符转换。 translit(s, f, t) 通过把在 f 中找到的任何字符都替换为 t 中对应的字符修改 s。比如 translit(s, aeiou, 12345) 替代元音为相应的数字。如果 t 比 f 短在 t 中没有条目的字符会被删除掉作为极限情况如果 t 更本就不存在把来自 f 的字符都从 s 中删除掉。所以 translit(s, aeiou) 删除 s 中的元音。 还有一个内置叫做 dnl它删除跟随它的直到并包括下一个换行的所有字符它主要用于丢弃空行否则会使 M4 输出混乱。例如如果你写 define(N, 100)
define(M, 200)
define(L, 300) 在每一行结束处的换行不是定义的一部分所以它们被复制到输出而这可能是我们不希望的。如果你在这些行上添加 dnl这些换行就不见了。 完成这个目的的另一种方式是 J. E. Weythman 提出的 divert(-1)
define(...)
...
divert 打印 内置的 errprint 把它的参数写出到标准错误文件。所以你可以写出 errprint(fatal error´) dumpdef 是个调试辅助函数它转储定义的项的当前定义。如果它没有参数则打印所有的项否则你将得到同参数同名的项。不要忘记引用上这些名字! 内置总结 每个条目都前导上描述它的页号。 3 changequote(L, R)1 define(name, replacement)4 divert(number)4 divnum5 dnl5 dumpdef(name´, name´, ...)5 errprint(s, s, ...)4 eval(numeric expression)3 ifdef(name´, this if true, this if false)5 ifelse(a, b, c, d)4 include(file)3 incr(number)5 index(s1, s2)5 len(string)4 maketemp(...XXXXX...)4 sinclude(file)5 substr(string, position, number)4 syscmd(s)5 translit(str, from, to)3 undefine(name´)4 undivert(number,number,...) 致谢 我们感谢 Rick Becker、John Chambers、Doug McIlroy特别是 Jim Weythman他率先使用 M4 导致了很多有价值的改进。我们还要感谢 Weythman 对代码做的实质贡献。 ---- 第 5 页完 ---- 引用 [1] B. W. Kernighan and P. J. Plauger, Software Tools, Addison-Wesley, Inc., 1976. 转载于:https://my.oschina.net/zhoukuo/blog/330097