Nim教程【十五】【完结】

模版

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

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

至此整个系列写完了

喜欢的请点推荐

时间: 2024-12-12 11:53:10

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

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

无废话ExtJs 入门教程十五[员工信息表Demo:AddUser] extjs技术交流,欢迎加群(201926085) 前面我们共介绍过10种表单组件,这些组件是我们在开发过程中最经常用到的,所以一定要用到非常熟练才可以,今天我们会通过一个员工信息表实例,再把这些组件串一下. (1)TextField  (2)Botton  (3)NumberField (4)Hidden (5)DataFiedl (6)RadioGroup (7)CheckBoxGroup (8)Combobox (9)F

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

原文:RabbitMQ入门教程(十五):普通集群和镜像集群 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/vbirdbest/article/details/78740346 分享一个朋友的人工智能教程(请以"右键"->"在新标签页中打开连接"的方式访问).比较通俗易懂,风趣幽默,感兴趣的朋友可以去看看. 普通集群 推荐一篇优秀的文章: RabbitM

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

在WIX中,CustomAction用来在安装过程中执行自定义行为.比如注册.修改文件.触发其他可执行文件等.这一节主要是介绍一下CustomAction的7种用法. 在此之前要了解InstallExecuteSequence,它是一个Action的执行序列. Installer会按照默认顺序来执行这些Action.通过字面意思也大概知道这些Action的目的.这些方法不是每次一都执行,分安装和卸载.如果CustomAction没有指定,很可能会安装卸载的时候都会执行一次. • AppSearc

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

估计很多朋友都认为参数校验是客户端的职责,不关服务端的事.其实这是错误的,学过 Web 安全的都知道,客户端的验证只是第一道关卡.它的参数验证并不是安全的,一旦被有心人抓到可乘之机,他就可以有各种方法来摸拟系统的 Http 请求,访问数据库的关键数据.轻则导致服务器宕机,重则泄露数据.所以,这时就需要设置第二道关卡,服务端验证了. 老项目的服务端校验 @RestController @RequestMapping("/student") public class ValidateOne

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

转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/143.html 在之前的博客中已经非常详细的介绍了Redis的各种操作命令.运行机制和服务器初始化参数配置.本篇博客是该系列博客中的最后一篇,在这里将给出基于Redis客户端组件访问并操作Redis服务器的代码示例.然而需要说明的是,由于Redis官方并未提供基于C接口的Windows平台客户端,因此下面的示例仅可运行于Linux/Unix平台.但是对于使用其它编程语言的

Webpack入门教程十五

81.在webpack.config.js文件配置collapseWhitespace项,用于删除空白字符与换行符,修改内容如下 var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry:  __dirname + "/app/Greeter.js", output: { path: __dirname + &quo

Oracle Coherence中文教程十五:序列化分页缓存

序列化分页缓存 本章提供了大量的二进制数据的离堆缓存信息. 本章包含以下各节: 了解序列化分页缓存 分页缓存配置序列化 优化分区的高速缓存服务 配置高可用性 配置负载平衡和故障转移 支持巨大的缓存 15.1了解序列化分页缓存 Coherence提供高效巨额自动过期数据使用潜在的高延迟的存储机制,如磁盘文件缓存的明确支持.其优点包括支持更大的数据集可以管理在内存中,同时保留出了管理时序的一个有效的终止机制(和自动释放的资源管理有关的) ,该数据.最佳使用场景包括许多大型对象存储能力,很少访问XML

Spring Boot2 系列教程(十五)定义系统启动任务的两种方式

在 Servlet/Jsp 项目中,如果涉及到系统任务,例如在项目启动阶段要做一些数据初始化操作,这些操作有一个共同的特点,只在项目启动时进行,以后都不再执行,这里,容易想到web基础中的三大组件( Servlet.Filter.Listener )之一 Listener ,这种情况下,一般定义一个 ServletContextListener,然后就可以监听到项目启动和销毁,进而做出相应的数据初始化和销毁操作,例如下面这样: public class MyListener implements

Unity3D脚本中文系列教程(十六)

Unity3D脚本中文系列教程(十五) ◆ function OnPostprocessAudio (clip:AudioClip):void 描述:◆  function OnPostprocessGameObjectWithUserProperties (root : GameObject, propNames : string[], values : object[]) : void 描述:在导入文件时,为每个至少附加了一个用户属性的游戏物体调用propNames是一个string[ ],

Senparc.Weixin.MP SDK 微信公众平台开发教程(十五):消息加密

原文:Senparc.Weixin.MP SDK 微信公众平台开发教程(十五):消息加密 前不久,微信的企业号使用了强制的消息加密方式,随后公众号也加入了可选的消息加密选项.目前企业号和公众号的加密方式是一致的(格式会有少许差别). 加密设置 进入公众号后台的“开发者中心”,我们可以看到Url对接的设置: 点击[修改设置],可以进入到修改页面: 加密的方式一共有3种: 明文模式,即原始的消息格式 兼容模式,明文.密文将共存,正式发布的产品不建议使用(因为仍然包含了明文,达不到加密的效果) 安全模