日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

M4 宏处理器

發布時間:2025/3/8 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 M4 宏处理器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

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

總結

以上是生活随笔為你收集整理的M4 宏处理器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。