M4 宏处理器

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

M4 宏处理器

Brian W. Kernighan
Dennis M. Ritchie

Bell Laboratories
Murray 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 定义为 stuffname 的所有后续出现都被替代为 stuffname 必须是字母数字的并且必须开始于字母(下划线 _ 被当作字母)。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 在相应的操作系统上有预定义的名字 unixgcos,你可以如下这样确定正在使用的是哪个:

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)

定义 ab    c

参数用逗号分隔,但是圆括号可以被正确处理,所以逗号可以用圆括号“保护”起来而不终止一个参数。就是说如下之中

define(a, (b,c))

只有两个参数;第二个参数是文字的 (b,c)。当然可以通过引用它来插入裸露的逗号或圆括号。

算术内置

M4 (只)提供两个做整数算术的内置函数。最简单的是 incr,它向数值参数增加 1。要处理常见的编程情况,你希望定义一个变量为“比 N 多一”,可以写

define(N, 100)
define(N1, `incr(N)´)

则定义 N1 为比 N 的当前值多一。

更一般的算术机制是叫做 eval 的内置函数,它有能力做关于整数的任意算术。它提供如下算符(按优先级递减的次序)

一元  和  或 (指数)(模)(非) 或 (逻辑与) 或 (逻辑或)

可以用圆括号在需要的地方组合算符。给 eval 的所有操作数(operand)最终必须是数值。真关系(如 1>0)的数值值为 1,而假为 0。eval 的精度在 UNIX 上是 32 位在 GCOS 上是 36 位。

---- 第 3 页完 ----

作为一个简单例子,假设我们希望 M2**N+1

define(N, 3)
define(M, `eval(2**N+1)´)

作为法则,建议你引用对宏定义的文本,除非它确实非常简单(就是说是个数);这通常会给出你想要的结果,这是个好习惯。

文件操纵

你可以通过内置 include 在任何时候包含一个新文件到输入中:

include(filename)

filename 的内容插入到 include 命令的位置中。这个文件的内容经常是一组定义。include 的值(就是它的替代文本)是这个文件的内容;它可以 捕获于这些定义中。

include 中的文件不能被访问是致命的错误。要获得对这种情况的某种控制,可以使用替代形式的 sincludesinclude (“silent include”)在这个文件不能被访问的时候什么都不说并继续。

还可以在处理期间把 M4 的输出转向到临时文件,输出紧随这个命令后收集到的材料。M4 维护九个这种转向文件,编号从 1 到 9。如果你写

divert(n)

所有后续的输出被添加到 n 所引用的临时文件的结束处。通过另一个 divert 停止转向到这个文件;特别是 divertdivert(0) 恢复到正常的输出处理。

被转向的文本通常都在处理结束时按数字次序串联起来输出。但是也可以在任何时间取回被转向的文本,就是说把它们添加到当前转向文件之上。

undivert

按数字次序取回所有转向文本,带有参数的 undivert 按给定的次序取回选择的转向文本。去转向的活动丢弃已被转向的材料,转向到编号不在 0 到 9 之间的转向文件的东西也被丢弃。

undivert 的值是被转向的材料。此外,被转向的材料被宏所重新扫描。

内置 divnum 返回当前活跃的转向文件的编号,在正常处理期间它是零。

系统命令

你可以通过 syscmd 内置运行在本地操作系统中任何程序。例如

syscmd(date)

UNIX 上运行 date 命令。通常使用 syscmd 来为随后的 include 建立文件。

为了实现制作唯一的文件名字,提供了内置的 maketemp,带有同系统函数 mktemp 一致的规定: 在参数中的字符串 XXXXX 被替代为当前进程的进程 id。

条件

有一个叫做 ifelse 的内置使你能进行任意的条件测试。在最简单的形式中

ifelse(a, b, c, d)

比较两个字符串 ab。如果它们是同样的,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

如果 in 超出了范围,各种偶然的事情都可能发生。

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 divnum

  • 5 dnl

  • 5 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545964.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

绝了,几款主流的 JSON 库性能对比!

本篇通过JMH(Oracle官方测试框架)来测试一下Java中几种常见的JSON解析库的性能。每次都在网上看到别人说什么某某库性能是如何如何的好,碾压其他的库。但是百闻不如一见,只有自己亲手测试过的才是最值得相信的。JSON不管是在Web开…

DWZ使用笔记

DWZ使用笔记 一、前言 在近期的一个项目中,引入了DWZ这个富client框架,算是一次尝试吧。期间也遇到不少问题,总算一一攻克了。特以此文记之。本人用的是dwz-ria-1.4.5Asp.net webform,写这篇笔记时最新版本号已经是1.4.6了。DWZ官…

阿里的简历多久可以投递一次?次数多了有没有影响?可以同时进行吗?

最近,无论是读者群,还是公众号后台,很多人都比较关注以下几个问题:阿里的简历是半年只能投递一次吗?阿里的面试可以多个部门同时进行吗?面试没过,又被系统捞起来了,我该怎么办&#…

记一次蚂蚁金服面试被虐经历

本文来自作者投稿,原作者:yes面试前的小姐姐来说说前不久蚂蚁金服一面的情况。说来也是巧合,当时在群里有位蚂蚁金服的小姐姐发了个内推,看了下JD感觉可以试试于是就私聊了小姐姐发简历内推了。我16年也就是大三上就开始实习了&am…

用python + openpyxl处理excel(07+)文档 + 一些中文处理的技巧

2019独角兽企业重金招聘Python工程师标准>>> 寻觅工具 确定任务之后第一步就是找个趁手的库来干活。 Python Excel上列出了xlrd、xlwt、xlutils这几个包,但是 它们都比较老,xlwt甚至不支持07版以后的excel它们的文档不太友好,都可…

Spring Boot 2.3.3 正式发布!

Spring Boot 2.3.3 稳定版已发布&#xff0c;可从 repo.spring.io 和 Maven Central 获取。<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.3.RELEASE</versio…

木板最优切割利润最大_最多进行K笔交易的股票最大买卖利润

木板最优切割利润最大This is a very popular interview problem to find maximum profit in stock buying and selling with at most K transactions. This problem has been featured in the interview rounds of Amazon. 这是一个非常受欢迎的面试问题&#xff0c;目的是在最…

[数据库]Oracle和mysql中的分页总结

Mysql中的分页物理分页•在sql查询时&#xff0c;从数据库只检索分页需要的数据•通常不同的数据库有着不同的物理分页语句•mysql物理分页&#xff0c;采用limit关键字•例如&#xff1a;检索11-20条 select * from user limit 10,10 ;* 每次只查询10条记录.当点击下一页的时候…

List 集合去重的 3 种方法

问题由来在实际开发的时候&#xff0c;我们经常会碰到这么一个困难&#xff1a;一个集合容器里面有很多重复的对象&#xff0c;里面的对象没有主键&#xff0c;但是根据业务的需求&#xff0c;实际上我们需要根据条件筛选出没有重复的对象。比较暴力的方法&#xff0c;就是根据…

C语言入门——排序

排序的方法有很多种比较常见的便为&#xff1a;冒泡排序、选择排序、插入排序、快速排序。 今天我们就围绕着四种排序来说&#xff0c;如果有兴趣的话可以去查找一下其他排序。 在排序这方面我们主要讨论&#xff1a; 稳定&#xff1a;如果a原本在b前面&#xff0c;而ab&…

【转】eclipse技巧1

2019独角兽企业重金招聘Python工程师标准>>> 俗话说的好啊&#xff0c;“工于利启事&#xff0c;必先善其器”&#xff0c;如果说你的编程功底是一个枪法的话&#xff0c;那么强大的eclipse就是android战士们最好的武器。 这里&#xff0c;我们来总结eclipse的使用技…

定时任务最简单的3种实现方法(超好用)

这是我的第 86 篇原创文章作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;定时任务在实际的开发中特别常见&#xff0c;比如电商平台 30 分钟后自动取消未支付的订单&#x…

C语言入门基础——Brute-Force算法

Brute-Force算法的基本思想是&#xff1a; 1) 从目标串s 的第一个字符起和模式串t的第一个字符进行比较&#xff0c;若相等&#xff0c;则继续逐个比较后续字符&#xff0c;否则从串s 的第二个字符起再重新和串t进行比较。 2) 依此类推&#xff0c;直至串t 中的每个字符依次和…

为什么劝你放弃Maven?看看Gradle的这些优点就知道了

相信使用Java的同学都用过Maven&#xff0c;这是一个非常经典好用的项目构建工具。但是如果你经常使用Maven&#xff0c;可能会发现Maven有一些地方用的让人不太舒服&#xff1a;Maven的配置文件是XML格式的&#xff0c;假如你的项目依赖的包比较多&#xff0c;那么XML文件就会…

css中的换行符_如何使用CSS防止项目列表中的换行符?

css中的换行符Introduction: 介绍&#xff1a; Dealing with various items in CSS sometimes pose very different problems. The problem could be anything, it could be related to positioning, arrangement, and whatnot, therefore all such kinds of problems require…

Java中的一些坑,汇总篇(2万字)

Photo Drew Farwell 文 | 常意1.前言

6款html5模板下载

http://www.100sucai.com/code/1316.htmlhttp://www.100sucai.com/code/1318.htmlhttp://www.100sucai.com/code/1310.htmlhttp://www.100sucai.com/code/1309.htmlhttp://www.100sucai.com/code/1303.htmlhttp://www.100sucai.com/code/1301.html转载于:https://blog.51cto.co…

高并发系统 3 大利器之缓存

引言随着互联网的高速发展&#xff0c;市面上也出现了越来越多的网站和app。我们判断一个软件是否好用&#xff0c;用户体验就是一个重要的衡量标准。比如说我们经常用的微信&#xff0c;打开一个页面要十几秒&#xff0c;发个语音要几分钟对方才能收到。相信这样的软件大家肯定…

QTimer与事件循环理解

问题分析 最近在使用QT的时候发现了某些问题&#xff0c;查阅资料最后总结一下。我起初是想用QT在界面还在加载时加载一副动画&#xff0c;然后动画下面有加载的滚动条代表时间&#xff0c;由于测试所以界面加载没写很多东西很快就加载完成了。我就想让他加载慢点我看看效果。…

MYSQL 数学运算符问题

背景&#xff1a; 在mysql中 ’stringA stringB 这种类型的操作&#xff0c;在mysql内部会自动转化为两个double 数进行运算。 -------------------------------------------------------------------------------------------------------------------------------- 例子&a…