hi guys, I’m working on a runtime version of std/macros using the compiler’s PNode directly: https://github.com/thing-king/macros2/blob/main/src/macros2.nim (adapted from https://github.com/elcritch/cdecl/blob/main/src/cdecl/compiler/macros2.nim)
My issue is that a macro requires a type of system.NimNode
import macros2
# Goal: allow `myProc` to work both at compile and runtime without `nimvm` switches or both macro libs imported at once
proc myProc() =
result = newIntLitNode(42)
macro something() =
result = myProc() # incorrect type
echo myProc().repr
echo $something
I'm happy to patch and recompile Nim itself if needed! Any suggestions would be hugely appreciated!
Here is some ugly impelmentation I can think of:
lib.nim # this is your lib.nim to define some functions to process NimNode
proc myProc() : NimNode =
result = newIntLitNode(42)
rt.nim # this is your run time code to use functions defined in lib.nim
import system except NimNode
import macros2
include ./lib
proc runtimeCall() =
echo myProc().repr
runtimeCall()
ct.nim # this is your compile time generation to use function defined in lib.nim
import macros
include ./lib
macro compiletimeGen(): untyped =
result = myProc()
echo compiletimeGen()
I'm happy to patch and recompile Nim itself if needed! Any suggestions would be hugely appreciated!
Use koch temp c myfile.nim to get a stack trace from the compiler. Then you can see where the error messages comes from. Add` magic: "PNimrodNode"` to the compiler's PNode declaration.
Wow... thanks!
include is exactly what I'm missing!
Pardon me, I'm referencing constructing NimNode's at runtime alone. std/macros contains core utility in that regard.
My motive is to convert my macros to procedures utilizing the above, then setup a public "fake" macro which communicates via FFI for "transformation" to protect source.
Well, I'm never executing Nim AST at runtime, I'm not sure that's possible in vanilla Nim (without modification).
I'm simply doing as stated above.
# macro_lib.nim
import macros2/ffi
proc myProtectedMacro(b: NimNode, n: NimNode): NimNode =
result = nnkStmtList.newTree(b, n)
exportMacro myProtectedMacro
# Generates FFI wrapper that:
# 1. Accepts cstring params (serialized AST via .repr)
# 2. Converts to NimNode via parseStmt
# 3. Calls original proc
# 4. Returns result as cstring via .repr
# Compile as .so
Then import and use:
# main.nim
import macros2/ffi
importMacro myProtectedMacro, (b, n)
# Generates macro wrapper that:
# 1. Serializes untyped NimNode params to cstring via .repr
# 2. Calls FFI function across library boundary
# 3. Deserializes result via parseStmt
myProtectedMacro:
echo "Hello"
echo "World"
See here for full code.
Well, I'm never executing Nim AST at runtime, I'm not sure that's possible in vanilla Nim (without modification).
Sure that’s possible as the compiler has an interpreter built in. You just use evalCode or nimscripter. You pull in the entire Nim compiler into your binary though.
However, it looks like what you’re trying to create is a compiler plugin system so you can do proprietary code gen.?
Nimony’s entire design relies on compiler plugins IIRC. If you’re really interested in the plugin route you might try Nimony.
Oh... duh !
Could this help me here, or in general? I don't think so, but maybe I'm missing something? I am indeed simply doing proprietary code gen- I'd like to eventually sell & protect my "web framework" I'm devising.
I'm extremely looking forward to Nimony... waiting for it to be ""stable"" AFAIK it's not there yet? Having no IC makes VSCode syntax validation via nim check unusable (coding blind not good), and iterative testing annoying... I sit at ~30s compile times, I've tried what I think is- all the tricks in the book :/