<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 宏 自定義一個 m4 宏所用的基本格式如下: ~~~ define(宏名, 宏體) ~~~ 上一節,我們定義的一個很簡單的?`say_hello_world`?宏: ~~~ define(say_hello_world, Hello World!) ~~~ `say_hello_world`?是宏名,`Hello World`?是宏體。如果在宏定義之后的文本中出現了?`say_hello_world`,例如: ~~~ define(say_hello_world, Hello World!) blab blab ... say_hello_world ~~~ 假設上述文本均處于非負號緩存,那么當 m4 從輸入流中讀取到?`say_hello_world`?時,它能夠檢測出該文本片段是一個被定義了的宏,于是它就將這個宏展開為?`Hello World`,并使用這個展開結果替換文本片段?`say_hello_world`,所以,上述文本經過 m4 處理后發送到輸出流,就變成: ~~~ blab blab ... Hello World! ~~~ 上述輸出結果中的空行,應該沒什么玄機可言了,只是需要注意:宏定義語句本身也會被 m4 展開,因為?`define`?本身就是一個宏,只不過它的展開結果是一個空的字符串。 ## 有參數的宏 宏可以有參數。遵循 POSIX 標準的 m4,允許一個宏最多有 9 個參數(似乎 Shell 腳本里的函數也最多支持 9 個參數),在宏體中可使用用?`$1, ..., $9`?來引用它們。GNU 的 m4 不限制宏的參數數量。 對于下面這段 C 語言的宏定義與調用: ~~~ #define DEF_PAIR_OF(dtype) \ typedef struct pair_of_##dtype { \ dtype first; \ dtype second; \ } pair_of_##dtype##_t DEF_PAIR_OF(int); DEF_PAIR_OF(double); DEF_PAIR_OF(MyStruct); ~~~ 用 m4 的有參數的宏可給出等價表示: ~~~ divert(-1) define(DEF_PAIR_OF, `typedef struct pair_of_$1 { $1 first; $1 second; } pair_of_$1') divert(0)dnl DEF_PAIR_OF(int); DEF_PAIR_OF(double); DEF_PAIR_OF(MyStruct); ~~~ 它們能夠展開為同樣的 C 代碼(C 語言宏由 C 預處理器展開,m4 宏由 m4 展開): ~~~ typedef struct pair_of_int { int first; int second; } pair_of_int; typedef struct pair_of_double { double first; double second; } pair_of_double; typedef struct pair_of_MyStruct { MyStruct first; MyStruct second; } pair_of_MyStruct; ~~~ 注意,C 宏與 m4 宏的調用有點區別。在 C 中,調用一個宏,宏名與其后的?`(`?可以有空格,而 m4 宏的調用不允許這樣。 m4 版本的?`DEF_PAIR_OF`?宏的宏體為: ~~~ `typedef struct pair_of_$1 { $1 first; $1 second; } pair_of_$1' ~~~ 這個宏體是一個帶引號的字符串,形如: ~~~ `... ... ...' ~~~ 注意左引號與右引號對應的符號。在大部分鍵盤上,左引號與?`~`?符號同鍵,右引號與?`"`?同鍵,它們是單引號。不要對引號掉以傾心,它在 m4 中的重要地位僅位列宏之下,如果沒有它,宏的世界會異常的混亂。后面,我會在單獨給引號的基本用法留出一節專門闡述。在此只需將引號理解為一段文本的封裝。 事實上,對于 m4 版本的?`DEF_PAIR_OF`?宏體,不用引號也不會出問題(可以去掉引號試一下)。但是,在復雜一些的宏體內,可能會出現?`,`?符號,如果這樣的宏體不用引號囊括起來,那么?`,`?會被 m4 誤認為宏參數的分隔符。所以,一定要記住:`,`?會被 m4 捕獲為宏參數分隔符,而引號可使之逃逸。 ## 小實踐:reStructuredText 插圖標記的簡化 reStructuredText 是一種輕量級的文本標記語言,與 Markdown 屬于同類,一般用于記筆記,然后以網頁的形式發布。之所以要用輕量級文本標記,是因為直接手寫 HTML 太繁瑣了。我在我的機器上搭建的 Nikola 靜態網站,默認用的就是 reStructuredText,我用它來整理我的一些筆記。 在使用 reSturcturedText 寫文檔時,我覺得它的插圖標記過于繁瑣。我常用的插圖標記如下: ~~~ .. figure:: 圖片文件路徑 :align: center :width: 寬度值 ~~~ 上述標記文本塊前后必須要留出空行,否則 reStructuredText 的解析器就會抱怨。`align`?與?`width`?前面的縮進也是必須的,否則 reStructuredText 的解析器就會抱怨…… 為了簡化這個標記,我用 m4 定義了一個宏: ~~~ divert(-1) define(place_figure, ` .. figure:: $1 :align: center :width: $2 ') divert(0)dnl ~~~ 然后我就可以愉快的像下面這樣在 reStructuredText 文本中插入一幅圖片了。 ~~~ place_figure(`/images/amenta-needles/0001.png', 480) ~~~ 用這種辦法可以簡化許多繁瑣的文本標記,甚至可以實現 reStructuredText 不具備的功能,例如參考文獻的管理。如果你不打算研究如何改造 reStructuredText 解析器來滿足自己的需求,在這種前提下,用 m4 簡單的 hack 一下,使得 reStructuredText 的易用性顯著增強,這就是宏語言最大的用途。 不妨將宏語言視為生活中的方便袋。 ## 宏的陷阱 m4 允許宏的重定義,結果是新的宏定義會覆蓋舊的。例如: ~~~ define(LEFT, [)dnl LEFT define(LEFT, {)dnl LEFT ~~~ 如果你按照我說『新的宏定義會覆蓋舊的』來判斷,可能會認為上述文本流經 m4 會變為: ~~~ [ { ~~~ 然而,事實上 m4 的處理結果是: ~~~ [ [ ~~~ 與理解這個詭異的結果是如何產生的,就需要認真的回顧一下 m4 的工作過程。 我將 m4 處理第一個?`LEFT`?宏定義的過程大致拆解為: 1. 在輸入流中,m4 遇到了?`define`,在它的記憶里,`define`?是一個宏; 2. 接下來它遇到了一個?`(`,它會認為這是?`define`?宏參數列表的左定界符; 3. 接下來,它遇到了?`LEFT,`,它會認為?`,`?之前的文本是?`define`?的第一個參數; 4. 接下來,它遇到了?`[)`,他會認為?`[`?是?`define`?的第二個參數,而?`)`?是?`define`?參數列表的右定界符; 5. 它現在終于明白了,`define(LEFT, [)`?是在調用?`define`?宏; 6. m4 對?`define(LEFT, [)`?進行展開,具體的展開過程,我們不得而知,因為?`define`?是 m4 內建的宏。我們只知道在`define(LEFT, [)`?的展開過程中,m4 會為我們定義?`LEFT`?宏,并且?`define(LEFT, [)`?宏展開完成后,m4 會向輸出流發送一個空字串。 當 m4 遇到第二個?`LEFT`?宏定義時,它的過程大致如下: 1. 在輸入流中,m4 遇到了?`define`,在它的記憶里,`define`?是一個宏; 2. 接下來它遇到了一個?`(`,它會認為這是?`define`?宏參數列表的左定界符; 3. 接下來,它遇到了?`LEFT,`,它會認為?`,`?之前的文本——`LEFT`是?`define`?的第一個參數。但是 m4 隨即發現?`LEFT`?是一個宏,于是它就將這個宏展開,結果為?`[`,它認為?`[`?才是真正的?`define`?的第一個參數; 4. 接下來,它遇到了?`[)`,他會認為?`[`?是?`define`?的第二個參數,而?`)`?是?`define`?參數列表的右定界符; 5. 它現在終于明白了,`define([, [)`?是在調用?`define`?宏; 6. m4 對?`define([, [)`?進行展開,具體的展開過程,我們不得而知,因為?`define`?是 m4 內建的宏。我們只知道在`define([, [)`?的展開過程中,m4 會為我們定義?`[`?宏,并且?`define([, [)`?宏展開完成后,m4 會向輸出流發送一個空字串。 m4 處理輸入流的過程,非常像人類,急功近利,目光短淺,一葉障目,不見泰山,管中窺豹,略見一斑……現在明白了吧!第二個?`LEFT`?宏定義,表面上看起來是重定義了?`LEFT`?宏,實際上定義的是?`[`?宏。 由于 m4 允許用任何符號作為宏名,所以定義一個?`[`?宏,這種行為是合法的,只不過 m4 不會真正的將它視為宏。我一直沒有提 m4 的宏命名規則,現在是談談它的最好的時機,但是沒什么好說的,在 m4 眼里,只有像 C 函數名的宏名才是真正的宏,也就是說,m4 的宏名名規則是:只允許使用字母、數字以及下劃線構造宏名,并且宏名只能以字母或下劃線開頭。只有符合宏名規則的宏,m4 才會將它視為真正的宏。不過,不符合宏名規則的宏,也是有辦法調用的,以后再講。 如果想真正的實現宏定義,需要借助引號,例如: ~~~ define(`LEFT', [)dnl LEFT define(`LEFT', {)dnl LEFT ~~~ 在 m4 語法中,單重引號具有逃逸的作用:當 m4 讀到帶單重引號的文本片段 S 時,它會將 S 的引號消除,然后繼續處理 S 之后的文本。 現在可以這樣來理解引號的作用: * m4 將一切沒有引號的文本都視為宏。對于已定義的宏,m4 會將其展開;對于未定義的宏,m4 會按其字面將其輸出。 * 加了引號的文本,m4 不再檢測它們是不是宏,而是將其作為普通文本按字面輸出。 也就是說,加了引號的文本,可以讓 m4 不需要判斷它是不是宏。 ## 記號 現在,我們繼續探究 m4 究竟對于輸入流都做了些什么。這件事,已經討論了 3 次了,雖然每一次都比前一次更深入一些,但是迄今為止,真相依然未能堪破。現在應該到堪破真相的時候了。 m4 對輸入流是以記號(Token)為單元進行讀取的。一般情況下,m4 會將讀取的每個記號直接發送到輸出流,但是當 m4 發現某個單詞是已定義的宏名時,它會將這個宏展開。在對宏進行展開的過程中,m4 可能會需要讀入更多的文本以獲取宏的參數。宏展開的結果會被插入到輸入流剩余部分的前端,也就是說,宏展開后所得到的文本會被 m4 重新讀取,解析為記號,繼續處理。 上面這段文字尤為重要。當 m4 不能如你預期的那樣展開你定義的宏,都應該重新理解上面這段文字。 什么樣的文本對于 m4 而言是一個記號?帶引號的字符串、宏名、宏參數列表、空白字符(包括換行符)、數字以及其他符號(包括標點符號),像這些類別的文本,對于 m4 而言都是記號。對于每種記號,m4 都有相應的處理機制。數字與標點符號(西文的),它們本身是記號,同時也是某些記號的邊界,除非它們出現于帶引號的字符串或者宏的參數列表中。 來看一個例子: ~~~ define(`definenum', `define(`num', `99')') num ~~~ 若這行文本流經 m4,那么 m4 讀到的第一個記號是?`define`。因為?`define`?后面尾隨的是?`(`。由于?`(`?即是記號,也是某些記號的邊界。m4 讀取?`define`?文本之后,就遇到了邊界,因此?`define`?是 m4 遇到的一個記號。 然后,m4 開始對?`define`?這個記號進行處理,它發現這個記號是一個帶參數的宏。所以它暫停對?`define`?的處理,繼續讀取并分析?`define`?之后的文本,看是否能獲得?`define`?宏的參數列表。 接下來, m4 讀取的是?`(`,這是個記號,而且是宏參數列表的左定界符。這對 m4 而言,已經開始經進入了一段可能是參數列表的文本。它期望接下來能遇到一個?`,`?或者?`)`,以得到完整的參數列表記號。 但是接下來,m4 讀到的是一個左引號。這時,對 m4 而言,已經開始進入了一個可能是帶引號的字符串文本,它期望接下來能遇到一些文本或右引號,以得到一個完整的字符串記號。 但是接下來,m4 讀到是文本片段?`definenum`,再讀下去,就讀到了右引號。這時, m4 很高興,它確定自己已經讀取了一個帶引號的字符串記號,然后它就將包圍這個字符串的引號消除,繼續讀取后面的文本。m4 之所以不在這時將?`definenum`發送到輸出端,因為它沒有忘記自己還有一個使命:為?`define`?宏搜尋完整的參數列表。 接下來,m4 讀到了?`,`——這是宏參數記號的邊界。m4 很高興,它終于得到了?`define`?宏的第一個參數,即`definenum`。此時,m4 認為剛才讀到的?`,`?就沒什么用了,于是就將?`,`?消除了,然后它認為后面也許還會有第二個參數,決定繼續前進。 接下來,m4 遇到了一個空格。在宏參數列表中,在?`,`?之后的空格是無意義的字符,m4 將這個空格扔掉,繼續前進。然后它遇到了左引號……于是就像剛才處理?`definenum`?一樣,m4 可以得到一個帶引號的字符串: ~~~ `define(`num', `99')` ~~~ m4 將這個字符串的引號消除,然后繼續前進,結果碰到了?`)`。此時,m4 吁了口氣,它終于為?`define`?宏獲得了一個完整的參數列表,盡管這個參數列表只含有兩個參數。 接下來,m4 對?`define`?宏進行展開。這個過程,我們無法得知,因為?`define`?是 m4 內建的宏,但是我們知道在?`define`的展開過程中肯定發生了一系列計算,然后?`definenum`?變成了一個宏,最終 ~~~ define(`definenum', `define(`num', `99')') ~~~ 的展開結果是一個空的字符串。由于宏展開的結果會被插入到輸入流剩余部分的前端,也就是說,宏展開后所得到的文本會被 m4 重新讀取,解析為記號,繼續處理,因此 m4 會將 ~~~ define(`definenum', `define(`num', `99')') ~~~ 的展開結果視為它下一步繼續要讀取并處理的文本。當 m4 繼續前進時,它就會讀到到一個空的字符串。空的字符串,雖然不具備被 m4 發送到輸出流的資格,但是它可以作為其他記號的邊界記號使用。 接下來,m4 遇到了一個空格字符。空格字符也是個記號,而且是其他記號的邊界。m4 將空格記號直接發送到輸出流,繼續前進。 接下來,m4 一口氣讀到了輸入流的末尾,得到了?`num`?記號。之所以說?`num`?是一個記號,是因為?`num`?的左側與右側都有邊界,左側是空格,右側是輸入流終止符。m4 將?`num`?這個記號視為宏,然后它確定這個宏沒有被定義,因此無法對其進行展開,所以只好將它作為字符串發送到輸出流。 ## 挑戰 對于以下 m4 文本 ~~~ define(`definenum', define(`num', `99')) definenum num ~~~ 推測一下 m4 的處理結果,然后執行 m4 命令檢驗所做的推測是否正確,然后再回顧一次 m4 的工作過程,最后用: ~~~ $ m4 -dV your-m4-file ~~~ 查看一下輸出,根據輸出信息再回顧一次 m4 的工作過程。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看