macrosnim-lang

Using nnkDotExpr to create an ident in Nim


I am trying to use nnkDotExpr in a macro and am getting an error. Specifically I am trying to write a macro which will write a procedure that returns a specific field. I have tried three approaches all of which seem to fail and I am not sure why.

This is the code I have.

import macros 

type
    Test_Type = object of RootObj
        Name_HIDDEN: string

macro Name_MacroA*(vType: untyped): untyped =
    var tName = nnkDotExpr(ident("self"), ident("Name_HIDDEN"))
    
    quote do:
        proc Name*(self: var `vType`): string =
            return `tName`

macro Name_MacroB*(vType: untyped): untyped =
    var tName = newNimNode(nnkDotExpr)
    tName.add(ident("self"))
    tName.add(ident("Name_HIDDEN"))

    quote do:
        proc Name*(self: var `vType`): string =
            return `tName`

macro Name_MacroC*(vType: untyped): untyped =
    var tName = nnkDotExpr.newTree(ident("self"), ident("Name_HIDDEN"))

    quote do:
        proc Name*(self: var `vType`): string =
            return `tName`

Name_MacroB(Test_Type)

var tTest: Test_Type
tTest.Name_HIDDEN = "Hello"
echo tTest.Name

When I use Name_MacroA I am get the error:

Error: attempting to call routine: 'nnkDotExpr'
  found 'nnkDotExpr' [enumField declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/core/macros.nim(45, 5)]

Name_MacroA is an attempt to match the code from the nnkDotExpr reference in Nim Macros.

When I use Name_MacroB or Name_MacroC I get the error:

template/generic instantiation of `Name_MacroB` from here test4.nim(17, 20) Error: undeclared identifier: 'self'
candidates (edit distance, scope distance); see '--spellSuggest': 
 (2, 4): 'del'
 (2, 4): 'ref'
 (2, 4): 'send'

Name_MacroB is an attempt to match the code from Nim Macros

Name_MacroC is an attempt to match code from Nim Macros


Solution

  • The first issue is that you are trying to call nnkDotExpr when its an enum. For NimNodeKind's like nnkDotExpr you need to use newTree.

    The second issue is that quote do is generating a unique symbol for the self parameter so you need to make it use your own ident like so

        # Create your own ident
        let selfIdent = ident"self"
        # Can use genSym if you think there might be conflicts
        # let selfIdent = genSym(nskParam, "self")
        tName.add(selfIdent) 
        tName.add(ident("Name_HIDDEN"))
    
        quote do:
            # Now it uses your symbol here instead of making a new one
            proc Name*(`selfIdent`: var `vType`): string =
                return `tName`
    

    Also there is newDotExpr which makes creating dot expressions simplier