Alex Craft
Alex Craft

Reputation: 15336

How to make pragma to work like a standalone macro?

How can I make macro that works when used as pragma to also work if used as plain call?

Please note that it has to be untyped as for some reason I can't use typed macro.

Run

import macros

proc fn_signature(fn: NimNode): string =
  let fn_impl = case fn.kind
  of nnk_ident:    fn.get_impl
  of nnk_proc_def: fn
  else:            return "invalid usage"

  fn_impl.tree_repr()

macro fn_signature_macro(fn: untyped) =
  echo fn.fn_signature

proc a: string {.fn_signature_macro.} = ""
fn_signature_macro a

Error:

stack trace: (most recent call last)
/usercode/in.nim(12, 10) fn_signature_macro
/usercode/in.nim(5, 22)  fn_signature
/usercode/in.nim(15, 20) template/generic instantiation of `fn_signature_macro` from here
/usercode/in.nim(5, 22) Error: node is not a symbol

Upvotes: 0

Views: 428

Answers (2)

shirleyquirk
shirleyquirk

Reputation: 1598

Since you asked for how to do this with an untyped macro, and making no claims of whether this is good design or not:

import macros
{.experimental: "dynamicBindSym".}
proc fn_signature(fn: NimNode): string =

  let fn_impl = case fn.kind

  of nnk_ident:    fn.bindSym.getImpl()

  of nnk_proc_def: fn

  else:            return "invalid usage"

  fn_impl.tree_repr()

macro fn_signature_macro(fn: untyped) =

  echo fn.fn_signature

proc a: string {.fn_signature_macro.}
proc b:int 
fn_signature_macro b

since the second call to fn_signature_macro is just the un-looked-up identifier b, we need to look it up with bindSym. Look at the docs for more information, bindSym allows you to specify whether the resulting symbol is open or closed, and here I've had to use an experimental feature for this to work.

Also, it's not necessary for the procs to have bodies for the macro to rewrite them

Upvotes: 1

Jason
Jason

Reputation: 495

It works fine, your error is calling getImpl on a nnkIdent. You cannot look at the implementation of a identifier since it's not looked up. You may want something like the following.

import macros

proc fn_signature(fn: NimNode): string =
  let fn_impl = case fn.kind
  of nnk_ident:    fn.get_impl
  of nnk_proc_def: fn
  else:            return "invalid usage"

  fn_impl.tree_repr()

macro fn_signature_macro(fn: typed): untyped =
  case fn.kind:
  of nnkProcDef:
    result = fn
  else: discard
  echo fn.fn_signature

proc a: string {.fn_signature_macro.} = ""
fn_signature_macro a

Upvotes: 1

Related Questions