模版
模版是Nim語言中的抽象語法樹,它是一種簡單的替換機制,在編譯期被處理
這個特性使Nim語言可以和C語言很好的執行在一起
像呼叫一個方法一樣呼叫一個模版
請看如下程式碼:
template `!=` (a, b: expr): expr = # this definition exists in the System module not (a == b) assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
類似下面這些符號,其實都是模版
=,>,>=,in,notin
這一個好處,如果你過載==操作符,
!=運算子也就自動提供出來了
並可以做正確的事!
A>B被變換到b<a。 b in a被變換成含有(b,a)。 notin和IsNot運算有明顯的意義。
模板為懶人提供了很大幫助。考慮一個簡單的PROC進行日誌記錄:
const debug = true proc log(msg: string) {.inline.} = if debug: stdout.writeln(msg) var x = 4 log("x has the value: " & $x)
這段程式碼有個缺點,如果有一天把debug變數設定為了false
那麼&操作和$操作還是會執行的,而這些操作的資源消耗是非常大的。
(呼叫方法的時候,會先執行方法引數位置處的表示式)
這個時候就可以考慮用模版來解決這個問題:
const debug = true template log(msg: string) = if debug: stdout.writeln(msg) var x = 4 log("x has the value: " & $x)
模版的引數型別可以是普通的型別,也可以是表示式;
template withFile(f: expr, filename: string, mode: FileMode, body: stmt): stmt {.immediate.} = let fn = filename var f: File if open(f, fn, mode): try: body finally: close(f) else: quit("cannot open: " & fn) withFile(txt, "ttempl3.txt", fmWrite): txt.writeln("line 1") txt.writeln("line 2")
在這個例子中,兩個writeln語句繫結到的是body引數
這段程式碼可以幫助開發人員避免“忘記關閉檔案”的錯誤
巨集
Nim語言的巨集提供了一個高階的編譯期的替換功能
Nim語言的巨集不能替換語言本身的語法,
但這並不是什麼缺憾,因為Nim語言本身已經足夠靈活了。
如果外部介面在編譯期不可用,那麼你就必須用純Nim語言寫巨集
(這估計就是在說Nim和C混合程式設計的時候要注意的事情)
你可以使用Nim程式碼編寫任何形式的巨集,編譯器會在編譯期把他們翻譯成真正的Nim程式碼。
可以有兩種辦法寫一個巨集
用Nim程式碼編寫巨集,讓編譯器解析它
手動建立抽象語法樹AST,你告訴編譯器
如果你想建立抽象語法樹AST,那麼你一定要知道Nim語言的語法是怎麼轉換為抽象語法樹的
在N關於巨集的幫助說明文件,你可以找到關於AST的幫助說明
你一旦寫了一個巨集,
那麼你有兩種辦法可以使用這個巨集
像呼叫一個方法一樣呼叫一個巨集
通過一種特殊的語法呼叫巨集(macrostmt宣告巨集)
表示式巨集
下面的程式碼實現了一個可變引數數量的巨集
# to work with Nim syntax trees, we need an API that is defined in the # ``macros`` module: import macros macro debug(n: varargs[expr]): stmt = # `n` is a Nim AST that contains a list of expressions; # this macro returns a list of statements: result = newNimNode(nnkStmtList, n) # iterate over any argument that is passed to this macro: for i in 0..n.len-1: # add a call to the statement list that writes the expression; # `toStrLit` converts an AST to its string representation: result.add(newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) # add a call to the statement list that writes ": " result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) # add a call to the statement list that writes the expressions value: result.add(newCall("writeln", newIdentNode("stdout"), n[i])) var a: array[0..10, int] x = "some string" a[0] = 42 a[1] = 45 debug(a[0], a[1], x)
編譯完之後,最終展開的程式碼為:
write(stdout, "a[0]") write(stdout, ": ") writeln(stdout, a[0]) write(stdout, "a[1]") write(stdout, ": ") writeln(stdout, a[1]) write(stdout, "x") write(stdout, ": ") writeln(stdout, x)
宣告巨集
宣告巨集在某種意義上就是表示式巨集
宣告巨集是用冒號表示式呼叫的
下面的例子展示了正規表示式詞法分析巨集
macro case_token(n: stmt): stmt = # creates a lexical analyzer from regular expressions # ... (implementation is an exercise for the reader :-) discard case_token: # this colon tells the parser it is a macro statement of r"[A-Za-z_]+[A-Za-z_0-9]*": return tkIdentifier of r"0-9+": return tkInteger of r"[\+\-\*\?]+": return tkOperator else: return tkUnknown
後面還有個例子,不翻譯了
至此整個系列寫完了
喜歡的請點推薦