Identifier construction in nim macros (instead of templates)

53 Views Asked by At

In nim, if I am writing a macro, how do I explicitly construct a name from an argument. Specifically in the nim manual (see here) we have the following example of "identifier construction"

template typedef(name: untyped, typ: typedesc) =
  type
    `T name`* {.inject.} = typ
    `P name`* {.inject.} = ref `T name`

typedef(myint, int)
var x: PMyInt

How can I accomplish the same thing if I want typedef to be a macro instead of a template?

1

There are 1 best solutions below

0
shirleyquirk On

any template can trivially become a macro. here:

template typedef(name: untyped, typ: typedesc) =
  type
    `T name`* {.inject.} = typ
    `P name`* {.inject.} = ref `T name`

typedef(myint, int)
var x: PMyInt

import macros
import std/genasts
macro typedef2(name:untyped, typ:typedesc) =
  result = genast(name,typ):
    type
      `T name`* {.inject.} = typ
      `P name`* {.inject.} = ref `T name`

typedef2(yourint, int)
var y: PYourInt

x = new int
x[] = 3
y = x
assert (y[] == 3)

but that's not helpful.

if you find yourself needing to step up from template to macro-land, it's because you need to manipulate the ast directly. so the first thing you do is figure out what the ast of your desired output is:

import macros
dumpTree: # dumpAstGen is also helpful
  type
    TMytype* = int
    PMytype* = ref TMytype
  TypeSection
    TypeDef
      Postfix
        Ident "*"
        Ident "Tmyname"
      Empty
      Ident "int"
    TypeDef
      Postfix
        Ident "*"
        Ident "Pmyname"
      Empty
      RefTy
        Ident "Tmyname"

dumpAstGen will output the exact code you'd need to put into your macro but it's less clear visually, so i'm using dumpTree.

now we make a macro that constructs that exact ast:

macro typedef3(name: untyped, typ: typedesc) =
  result = nnkTypeSection.newNimNode()
  result.add nnkTypeDef.newTree(
    nnkPostfix.newTree(
      ident"*",
      ident "T" & name.strVal),
    newEmptyNode(),
    typ)

  result.add nnkTypeDef.newTree(
    nnkPostfix.newTree(
      ident"*",
      ident "P" & name.strVal),
    newEmptyNode(),
    nnkRefTy.newTree(typ))

  #echo result.treerepr

typedef3(yourtype, float)

at the end we echo the ast we just made so we can compare it against what we were expecting

here it is as echo result.repr just for clarity:

type
  Tyourtype* = float
  Pyourtype* = ref float