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-09-21 08:34:35

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

PostgreSQL教程(十五):系统表详解_PostgreSQL

一.pg_class:     该系统表记录了数据表.索引(仍然需要参阅pg_index).序列.视图.复合类型和一些特殊关系类型的元数据.注意:不是所有字段对所有对象类型都有意义.   名字 类型 引用 描述 relname name   数据类型名字. relnamespace oid pg_namespace.oid 包含这个对象的名字空间(模式)的OI. reltype oid pg_type.oid 对应这个表的行类型的数据类型. relowner oid pg_authid.oid

Nim教程【五】

妈蛋,花了两天时间才搞定博客园的API, 比预期的时间整整多了1天, 不管怎么说,总算把博客园的客户端搞定了 这篇文章就是用博客园的客户端发布的, 先贴张图,给大家看看, 后面我会和博客园的领导商量一下,看看能不能发出来 (当然,如果能的话,我肯定会开源) (这个图片是直接黏贴上传的哦!) ------------------------------ 这是国内第一个关于Nim的系列教程   先说废话   业内的人认为能够直接操作系统硬件的语言才称得上系统级的编程语言 常见的系统级编程语言有:汇编

FrontPage 2003基础教程(十五) 共享边框

1.启用共享边框: >"工具">"网页选项">"创作">选中"共享边框" 2."格式"中选中"共享边框"在对话框中(注:若想损事,选:所有网页)设置选项"上.左.右.下"(可以自定) 3.在自动出现的共享边框中编辑链接栏属性 4.最后的效果 5.和上一讲的链接栏配合使用,当你每添加一个网页在链接栏中会自动生成上面的效果 查看全套FrontPag

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

在之前的博客中已经非常详细的介绍了Redis的各种操作命令.运行机制和服务器初始化参数配置.本篇博客是该系列博客中的最后一篇,在这里将给出基于Redis客户端组件访问并操作Redis服务器的代码示例.然而需要说明的是,由于Redis官方并未提供基于C接口的Windows平台客户端,因此下面的示例仅可运行于Linux/Unix平台.但是对于使用其它编程语言的开发者而言,如C#和Java,Redis则提供了针对这些语言的客户端组件,通过该方式,同样可以达到基于Windows平台与Redis服务器进行

Android简明开发教程十五:RadioButton多边形及路径绘制

这个例子是绘制多边形,多义形和路径,采用单选钮RadioButton来选择Polys 和Path示例: UI 设计为 上部分用来显示绘图内容,下部分为两个单选按钮 Polys ,Path.这样layout就和main.xml 不一样,main.xml 只含一个com.pstreets.graphics2d.GuidebeeGraphics2DView.因此需在res/layout下新建一个polys.xml: <?xml version="1.0″ encoding="utf-8

C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十五)

C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十五)制作精美的可任意拖放对象的物品栏及装备栏 在通常的网络游戏中,物品.装备.技能.快捷按钮等窗口中的图标都是可以相互拖放的,不同的栏目有着不同的限制,例如技能图标不能拖放到物品栏及装备栏中,且不是所有的魔法技能都可以拖放(如被动技能等):而非装备类的所有物品则无法拖放到角色的装备栏中.那么本节我将向大家讲解如何在本教程示例游戏中添加物品栏及装备栏,并实现它们之间双向物品交换的两种模式:拖放模式和双击模

C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(三十五)

C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(三十五)地图编辑器的初步使用 上一节我们制作好了地图编辑器,并初步实现了导出地图中的障碍物信息及实现A*模拟寻路.那么当我们得到了包含有障碍物数据信息的xml文件后,又该如何将之应用到本教程的示例游戏中呢? 本节还是以上一节的那幅地图为例,我们首先通过编辑器载入该地图并在上面描绘出所有障碍物: 然后点击导出障碍物按钮,我们将得到一个包含有类似如下信息的xml障碍物数据文件: <Item ID="Obstr

C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(二十五)

C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(二十五)完美捕捉精灵之神器 -- HitTest 怪物们都出现了,如何选中自己心仪的怪是主角目前首要做的事. 为了进行鼠标状态区别,我首先对鼠标变化规则进行约束:当鼠标在屏幕上空旷地图区域移动时,鼠标光标形态表现为默认光标 (0号光标图片),当鼠标经过精灵(悬停于其上方)时则变成发光光标(1号光标图片),如果指向的精灵对象为敌对状态时则鼠标光标变为攻击光标(2号光标图片),当使用魔法快捷键时,鼠标光标变成凝法状

C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(十五)

C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(十五) 精灵控件横空出世!② 紧接着上一节,我们打开QXSpirit.xaml.cs文件.在游戏设计中,为了能够轻易控制及管理精灵的各项属性及功能等,我赋予每个精灵一个专属线程,它在精灵的使用中起到关键作用: public QXSpirit() { InitializeComponent(); InitThread(); //初始化精灵线程 } DispatcherTimer Timer = new Dispa

ASP.NET 2.0数据教程之七十五: 用Managed Code创建存储过程和用户自定义函数

返回"ASP.NET 2.0数据教程目录" ASP.NET 2.0数据教程之七十五: 用Managed Code创建存储过程和用户自定义函数(下) 第八步:从表现层调用Managed Stored Procedures 当对数据访问层和 业务逻辑层进行扩充以支持调用GetDiscontinuedProducts 和 GetProductsWithPriceLessThan这2种managed stored procedures后,我们可以 在一个ASP.NET页面里展示这些存储过程的结