模版

模版是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

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

至此整个系列写完了

喜欢的请点推荐

Nim教程【十五】【完结】的更多相关文章

  1. 无废话ExtJs 入门教程十五[员工信息表Demo:AddUser]

    无废话ExtJs 入门教程十五[员工信息表Demo:AddUser] extjs技术交流,欢迎加群(201926085) 前面我们共介绍过10种表单组件,这些组件是我们在开发过程中最经常用到的,所以一 ...

  2. webpack4 系列教程(十五):开发模式与webpack-dev-server

    作者按:因为教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<webpack4 系列教程(十五):开发模式与 webpack-dev-server>原文地址.更欢迎来我的 ...

  3. RabbitMQ入门教程(十五):普通集群和镜像集群

    原文:RabbitMQ入门教程(十五):普通集群和镜像集群 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.c ...

  4. Unity3D脚本中文系列教程(十五)

    http://dong2008hong.blog.163.com/blog/static/4696882720140322449780/ Unity3D脚本中文系列教程(十四) ◆ LightRend ...

  5. Nim教程【五】

    妈蛋,花了两天时间才搞定博客园的API, 比预期的时间整整多了1天, 不管怎么说,总算把博客园的客户端搞定了 这篇文章就是用博客园的客户端发布的, 先贴张图,给大家看看, 后面我会和博客园的领导商量一 ...

  6. WebGL简易教程(十五):加载gltf模型

    目录 1. 概述 2. 实例 2.1. 数据 2.2. 程序 2.2.1. 文件读取 2.2.2. glTF格式解析 2.2.3. 初始化顶点缓冲区 2.2.4. 其他 3. 结果 4. 参考 5. ...

  7. Wix 安装部署教程(十五) --CustomAction的七种用法

    在WIX中,CustomAction用来在安装过程中执行自定义行为.比如注册.修改文件.触发其他可执行文件等.这一节主要是介绍一下CustomAction的7种用法. 在此之前要了解InstallEx ...

  8. Spring Boot2 系列教程 (十五) | 服务端参数校验之一

    估计很多朋友都认为参数校验是客户端的职责,不关服务端的事.其实这是错误的,学过 Web 安全的都知道,客户端的验证只是第一道关卡.它的参数验证并不是安全的,一旦被有心人抓到可乘之机,他就可以有各种方法 ...

  9. Redis教程(十五):C语言连接操作代码实例

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/143.html 在之前的博客中已经非常详细的介绍了Redis的各种操作命令 ...

  10. SpringBoot入门教程(十五)集成Druid

    Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0.DBCP.PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB ...

随机推荐

  1. 锁的封装 读写锁、lock

    最近由于项目上面建议使用读写锁,而去除常见的lock锁.然后就按照需求封装了下锁.以简化锁的使用.但是开发C#的童鞋都知道lock关键字用起太方便了,但是lock关键字不支持超时处理.很无奈,为了实现 ...

  2. android系统启动

    首页 资讯 精华 论坛 问答 博客 专栏 群组 更多 ▼ 您还未登录 ! 登录 注册 Ant space   博客 微博 相册 收藏 留言 关于我     android启动过程再研   Androi ...

  3. Oracle数据导入导出

    Oracle数据导入导出imp/exp 在oracle安装目录下有EXP.EXE与IMP.EXE这2个文件,他们分别被用来执行数据库的导入导出.所以Oracle数据导入导出imp/exp就相当与ora ...

  4. CSS有三种基本的定位机制

    CSS有三种基本的定位机制:普通流,浮动和绝对定位. 普通流:在普通流中元素框的位置由元素在html中的位置决定, 1.元素position属性为static或继承来的static时就会按照普通流定位 ...

  5. 《uml大战需求分析》阅读笔记05

    <uml大战需求分析>阅读笔记05 这次我主要阅读了这本书的第九十章,通过看这章的知识了解了不少的知识开发某系统的重要前提是:这个系统有谁在用?这些人通过这个系统能做什么事? 一般搞清楚这 ...

  6. android免root兼容所有版本ui调试工具

    SwissArmyKnife是什么 SwissArmyKnife 是一款方便调试android UI的工具,可以兼容所有android版本,不需要root权限.可以直接在android手机屏幕上显示当 ...

  7. 使用jailkit chroot更改ssh用户根目录

    安装jailkit cd /tmp    wget http://olivier.sessink.nl/jailkit/jailkit-2.16.tar.gz    tar xzf jailkit-2 ...

  8. easyconf——基于AugularJS的配置管理系统开发框架

    目录 1 easyconf的诞生2 easyconf的设计理念 2.1 总体设计 2.2 细节设计 2.2.1 CRUD操作 2.2.2 即时校验 2.2.3 下拉框设计3 easyconf使用指南 ...

  9. 「2014-3-11」HTTP 初步探究

    网络上存在很多资源,也持续不断地生成新的资源.为了新建.获取和操作这些资源,引来了两个问题:如何定位资源,如何对他们进行操作.第一个问题引申出了 URI / URL 即 uniform resourc ...

  10. C# GetHashCode与Equals在HashTable表查找时的关系

    using System; using System.Collections.Generic; using System.Text; using Microsoft.Win32; using Syst ...