Skip to content

Instantly share code, notes, and snippets.

@gmpreussner
Last active October 6, 2015 01:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gmpreussner/927349ef5a5b0d4294c1 to your computer and use it in GitHub Desktop.
Save gmpreussner/927349ef5a5b0d4294c1 to your computer and use it in GitHub Desktop.
MIDL interface glue code generator
import macros
macro midlInterface*(head: expr; body: stmt): stmt {.immediate.} =
## Generates the glue code required for MIDL interface types.
##
## For example, the following declaration...
##
## midlInterface IUnknown:
## proc queryInterface*(riid: Iid; outObj: ptr pointer): HResult
## proc addRef*(): int
## proc release*(): int
##
## ... will be rewritten as:
##
## type
## IUnknownVtable* {.inheritable.} = object
## queryInterface: proc (this: pointer; riid: Iid;
## outObj: ptr pointer): HResult {.stdcall.}
## addRef: proc (this: ptr IUnknown): int {.stdcall.}
## release: proc (this: ptr IUnknown): int {.stdcall.}
##
## IUnknown* = object
## vtable: ptr IUnknownVtable
##
## IUnknownConcept* = concept x
## x.vtable of ptr IUnknownVtable
##
## proc queryInterface*(this: ptr IUnknownConcept; riid: Iid;
## outObj: ptr pointer): HResult =
## this.vtable.queryInterface(this, riid, outObj)
##
## It is also possible to declare derived interfaces via:
##
## midlInterface IDerived of IUnknown
##
## head
## The macro's head expression containing the interface type and,
## optionally, a base type.
## body
## The statement body containing the interface procedures.
## result
## The interface implementation statement.
var typeName, baseName: NimNode
let procPointers = newNimNode(nnkRecList)
result = newStmtList()
# extract interface and base type
if head.kind == nnkIdent:
# `head` is expression `typeName`
baseName = nil
typeName = head
elif head.kind == nnkInfix and $head[0] == "of":
# `head` is expression `typeName of baseName`
typeName = head[1]
baseName = head[2]
else:
quit "Invalid node: " & head.lispRepr
# process statements in the body
for node in body.children:
case node.kind:
of nnkMethodDef, nnkProcDef:
# generate procedure pointer for vtable
#
# dumpTree:
# procName: proc (this: ptr typeName): ResultType {.stdcall.}
# -------------------------------------------------------------
# IdentDefs
# Ident !"procName"
# ProcTy
# FormalParams
# Ident !"ResultType"
# IdentDefs
# Ident !"this"
# Ident !"pointer"
# Empty
# ...
# Pragma
# Ident !"stdcall"
let pointerParams = copyNimTree(node.params)
pointerParams.insert(1,
newIdentDefs(
newIdentNode("this"),
newIdentNode("pointer")
)
)
procPointers.add(
newIdentDefs(
copyNimNode(node.name[1]),
newNimNode(nnkProcTy).add(
copyNimTree(pointerParams),
newNimNode(nnkPragma).add(
newIdentNode("stdcall")
)
)
)
)
# inject `this: typeNameConcept` pointer into procedure arguments
#
# dumpTree:
# proc procName*(this: ptr typeNameConcept, ...): ResultType
# -------------------------------------------------------------
# ProcDef
# Postfix
# Ident !"*"
# Ident !"procName"
# Empty
# Empty
# FormalParams
# Ident !"ResultType"
# IdentDefs <----
# Ident !"this"
# PtrTy
# Ident !"typeNameConcept"
# Empty
# ...
# Empty
# Empty
# Empty
let procParams = copyNimTree(node.params)
procParams.insert(1,
newIdentDefs(
newIdentNode("this"),
newNimNode(nnkPtrTy).add(
newIdentNode($typeName & "Concept")
)
)
)
node.params = procParams
# inject `this` pointer and procedure implementation
#
# dumpTree:
# proc procName*(...): ResultType
# this.vtable.procName(this, param1, param2, paramN)
# ----------------------------------------------------
# ProcDef
# Postfix
# Ident !"*"
# Ident !"procName"
# Empty
# Empty
# FormalParams
# ...
# Empty
# Empty
# StmtList <----
# Call
# DotExpr
# DotExpr
# Ident !"this"
# Ident !"vtable"
# Ident !"procName"
# Ident !"this"
# Ident !"param1"
# Ident !"param2"
# Ident !"paramN"
let call = newCall(
newDotExpr(
newDotExpr(
newIdentNode("this"),
newIdentNode("vtable")
),
copyNimNode(node.name[1])
)
)
for i in 1..<procParams.len:
call.add(procParams[i][0])
node.body = newStmtList(call)
else:
discard
result.add(node)
# generate virtual function table
#
# dumpTree:
# typeNameVtable* {.inheritable.} = object of baseNameVtable
# ...
# ----------------------------------------------------------------------
# TypeDef
# PragmaDef
# PragmaExpr
# Postfix
# Ident !"*"
# Ident !"typeName"
# Pragma
# Ident !"inheritable"
# Empty
# ObjectTy
# Empty
# OfInherit | or Empty if no base type
# Ident !"baseNameVtable" |
# RecList
# ...
let vtableNode = newNimNode(nnkTypeDef).add(
newNimNode(nnkPragmaExpr).add(
newIdentNode($typeName & "Vtable").postfix("*"),
newNimNode(nnkPragma).add(
newIdentNode("inheritable")
)
),
newNimNode(nnkEmpty),
newNimNode(nnkObjectTy).add(
newNimNode(nnkEmpty),
if baseName == nil:
newNimNode(nnkEmpty)
else:
newNimNode(nnkOfInherit).add(
newIdentNode($baseName & "Vtable")
),
procPointers
)
)
# generate interface declaration
#
# dumpTree:
# type
# typeName* = object
# vtable: typeNameVtable
# ----------------------------
# TypeDef
# Postfix
# Ident !"*"
# Ident !"typeName"
# Empty
# ObjectTy
# Empty
# Empty
# RecList
# IdentDefs
# Ident !"vtable"
# PtrTy
# Ident !"typeNameVtable"
# Empty
let interfaceNode = newNimNode(nnkTypeDef).add(
typeName.postfix("*"),
newNimNode(nnkEmpty),
newNimNode(nnkObjectTy).add(
newNimNode(nnkEmpty),
newNimNode(nnkEmpty),
newNimNode(nnkRecList).add(
newIdentDefs(
newIdentNode("vtable"),
newNimNode(nnkPtrTy).add(
newIdentNode($typeName & "Vtable")
),
newNimNode(nnkEmpty),
)
)
)
)
# generate concept declaration
#
# dumpTree:
# typeNameConcept* = concept x
# x.vtable of ptr typeNameVtable
# ----------------------------------
# TypeDef
# Postfix
# Ident !"*"
# Ident !"typeNameConcept"
# Empty
# TypeClassTy
# Arglist
# Ident !"x"
# Empty
# Empty
# StmtList
# Infix
# Ident !"of"
# DotExpr
# Ident !"x"
# Ident !"vtable"
# PtrTy
# Ident !"typeNameVtable"
let conceptNode = newNimNode(nnkTypeDef).add(
newIdentNode($typeName & "Concept").postfix("*"),
newNimNode(nnkEmpty),
newNimNode(nnkTypeClassTy).add(
newNimNode(nnkArglist).add(
newIdentNode("x")
),
newNimNode(nnkEmpty),
newNimNode(nnkEmpty),
newStmtList(
newDotExpr(
newIdentNode("x"),
newIdentNode("vtable")
).infix("of",
newNimNode(nnkPtrTy).add(
newIdentNode($typeName & "Vtable")
)
)
)
)
)
# assemble type section
result.insert(0,
newNimNode(nnkTypeSection).add(
vtableNode,
interfaceNode,
conceptNode
)
)
@gmpreussner
Copy link
Author

Example usage:

import winlean

HResult* = cint
Iid* = ptr GUID

midlInterface IUnknown:
  proc queryInterface*(riid: Iid; outObj: ptr pointer): HResult
  proc addRef*(): int
  proc release*(): int

midlInterface IDerived of IUnknown:
  proc foo*: int

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment