博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Nim教程【十五】【完结】
阅读量:6082 次
发布时间:2019-06-20

本文共 3115 字,大约阅读时间需要 10 分钟。

模版

模版是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 = trueproc log(msg: string) {.inline.} =  if debug: stdout.writeln(msg)var  x = 4log("x has the value: " & $x)

这段代码有个缺点,如果有一天把debug变量设置为了false

那么&操作和$操作还是会执行的,而这些操作的资源消耗是非常大的。

(调用方法的时候,会先执行方法参数位置处的表达式)

这个时候就可以考虑用模版来解决这个问题:

const  debug = truetemplate log(msg: string) =  if debug: stdout.writeln(msg)var  x = 4log("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 macrosmacro 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] = 42a[1] = 45debug(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 :-)  discardcase_token: # this colon tells the parser it is a macro statementof r"[A-Za-z_]+[A-Za-z_0-9]*":  return tkIdentifierof r"0-9+":  return tkIntegerof r"[\+\-\*\?]+":  return tkOperatorelse:  return tkUnknown

 

后面还有个例子,不翻译了

至此整个系列写完了

喜欢的请点推荐

转载地址:http://wzhgx.baihongyu.com/

你可能感兴趣的文章
[深入浅出Cocoa]iOS网络编程之NSStream
查看>>
HDOJ 4607 - Park Visit
查看>>
关于PHP 缓冲区
查看>>
分布式EventBus的Socket实现 - 发布订阅
查看>>
unity动态加载(翻译) .
查看>>
WIP_DISCRETE_JOBS.STATUS_TYPE
查看>>
一 VC2008环境中ICE的配置
查看>>
Win7无法添加用户的问题
查看>>
DCI:DCI学习总结
查看>>
- Shell - sort处理大文件(页 1) - ChinaUnix.net
查看>>
项目管理--执行过程组
查看>>
数据访问与sql语句的管理(一)
查看>>
前端开发框架
查看>>
风 记忆
查看>>
ARM中的PC和AXD的PC
查看>>
[转]关于ios 推送功能的终极解决
查看>>
C#中使用反射获取结构体实例
查看>>
GCT之语文细节知识
查看>>
【网站国际化必备】Asp.Net MVC 集成Paypal(贝宝)快速结账 支付接口 ,附源码demo...
查看>>
VC中使用GetModuleFileName获取应用程序路径
查看>>