前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Nim教程【十五】【完结】

Nim教程【十五】【完结】

作者头像
liulun
发布2018-01-12 15:36:03
9110
发布2018-01-12 15:36:03
举报
文章被收录于专栏:liulunliulun

模版

模版是Nim语言中的抽象语法树,它是一种简单的替换机制,在编译期被处理

这个特性使Nim语言可以和C语言很好的运行在一起

像调用一个方法一样调用一个模版

请看如下代码:

代码语言:javascript
复制
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进行日志记录:

代码语言:javascript
复制
const
  debug = true

proc log(msg: string) {.inline.} =
  if debug: stdout.writeln(msg)

var
  x = 4
log("x has the value: " & $x)

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

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

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

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

代码语言:javascript
复制
const
  debug = true

template log(msg: string) =
  if debug: stdout.writeln(msg)

var
  x = 4
log("x has the value: " & $x)

模版的参数类型可以是普通的类型,也可以是表达式;

代码语言:javascript
复制
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声明宏)

表达式宏

下面的代码实现了一个可变参数数量的宏

代码语言:javascript
复制
# 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)

编译完之后,最终展开的代码为:

代码语言:javascript
复制
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)

声明宏

声明宏在某种意义上就是表达式宏

声明宏是用冒号表达式调用的

下面的例子展示了正则表达式词法分析宏

代码语言:javascript
复制
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

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

至此整个系列写完了

喜欢的请点推荐

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-07-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档