Preserve ordering of declarations in QtObject macro
This commit is contained in:
parent
7049988479
commit
d2fc0c3c00
|
@ -239,7 +239,7 @@ template prototypeOnSlotCalled(typ: expr): stmt {.dirty.} =
|
||||||
|
|
||||||
#FIXME: changed parent, typ from typedesc to expr to workaround Nim issue #1874
|
#FIXME: changed parent, typ from typedesc to expr to workaround Nim issue #1874
|
||||||
template prototypeCreate(typ: expr): stmt =
|
template prototypeCreate(typ: expr): stmt =
|
||||||
template create*(myQObject: var typ) =
|
proc create*(myQObject: var typ) =
|
||||||
var super = (typ.superType())(myQObject)
|
var super = (typ.superType())(myQObject)
|
||||||
procCall create(super)
|
procCall create(super)
|
||||||
|
|
||||||
|
@ -299,135 +299,29 @@ proc tryHandleSigSlot(def: PNimrodNode, signals: var seq[PNimrodNode], slots: va
|
||||||
signals.add def
|
signals.add def
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
macro QtObject*(qtDecl: stmt): stmt {.immediate.} =
|
proc genCreateDecl(typ: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
## Generates boiler plate code for registering signals, slots
|
## generates forward declaration for ``create`` procedure
|
||||||
## and properties.
|
expectKind typ, nnkTypeDef
|
||||||
##
|
|
||||||
## Currently generates:
|
|
||||||
## - create: a method to register signal, slots and properties
|
|
||||||
## - superType: a template that returns the super type of the
|
|
||||||
## object defined within the macro body
|
|
||||||
## - onSlotCalled: a method to dispatch an on slot call to the
|
|
||||||
## appropiate method.
|
|
||||||
##
|
|
||||||
## Current limitations:
|
|
||||||
## - only one type can be defined within the body of code sent to the
|
|
||||||
## the macro. It is assumed, but not checked, that somewhere in the
|
|
||||||
## inheritance hierarchy this object derives from ``QObject``.
|
|
||||||
## - generics are not currently supported
|
|
||||||
expectKind(qtDecl, nnkStmtList)
|
|
||||||
#echo treeRepr qtDecl
|
|
||||||
result = newStmtList()
|
|
||||||
var slots = newSeq[PNimrodNode]()
|
|
||||||
var properties = newSeq[PNimrodNode]()
|
|
||||||
var signals = newSeq[PNimrodNode]()
|
|
||||||
# holds all user defined procedures so we can add them after create
|
|
||||||
var userDefined = newSeq[PNimrodNode]()
|
|
||||||
# assume only one type per section for now
|
|
||||||
var typ: PNimrodNode
|
|
||||||
for it in qtDecl.children():
|
|
||||||
if it.kind == nnkTypeSection:
|
|
||||||
let typeDecl = it.findChild(it.kind == nnkTypeDef)
|
|
||||||
let superType = typeDecl.getSuperType()
|
|
||||||
if superType.kind == nnkEmpty:
|
|
||||||
# allow simple types and type aliases
|
|
||||||
result.add it
|
|
||||||
else:
|
|
||||||
# may come in useful if we want to check objects inherit from QObject
|
|
||||||
#let superName = if superType.kind == nnkIdent: superType
|
|
||||||
# else: superType.getNodeOf(nnkIdent)
|
|
||||||
if typ != nil:
|
|
||||||
error("you may not define more than one type " &
|
|
||||||
"within the code block passed to this macro")
|
|
||||||
else: # without this else, it fails to compile
|
|
||||||
typ = typeDecl
|
|
||||||
result.add it
|
|
||||||
result.add genSuperTemplate(typeDecl)
|
|
||||||
elif it.kind == nnkMethodDef:
|
|
||||||
if tryHandleSigSlot(it, signals, slots, result): continue
|
|
||||||
userDefined.add it
|
|
||||||
elif it.kind == nnkProcDef:
|
|
||||||
if tryHandleSigSlot(it, signals, slots, result): continue
|
|
||||||
userDefined.add it
|
|
||||||
elif it.kind == nnkCommand:
|
|
||||||
let bracket = it[0]
|
|
||||||
if bracket.kind != nnkBracketExpr:
|
|
||||||
error("do not know how to handle: \n" & repr(it))
|
|
||||||
# BracketExpr
|
|
||||||
# Ident !"QtProperty"
|
|
||||||
# Ident !"string"
|
|
||||||
let cmdIdent = bracket[0]
|
|
||||||
if cmdIdent == nil or cmdIdent.kind != nnkIdent or
|
|
||||||
($cmdIdent).toLower() != "qtproperty":
|
|
||||||
error("do not know how to handle: \n" & repr(it))
|
|
||||||
properties.add it
|
|
||||||
else:
|
|
||||||
# everything else should pass through unchanged
|
|
||||||
result.add it
|
|
||||||
if typ == nil:
|
|
||||||
error("you must declare an object that inherits from QObject")
|
|
||||||
let typeName = typ.getTypeName()
|
let typeName = typ.getTypeName()
|
||||||
|
result = (getAst prototypeCreate(typeName))[0]
|
||||||
## define onSlotCalled
|
result.body = newEmptyNode()
|
||||||
var slotProto = (getAst prototypeOnSlotCalled(typeName))[0]
|
|
||||||
var caseStmt = newNimNode(nnkCaseStmt)
|
|
||||||
caseStmt.add ident("slotName")
|
|
||||||
for slot in slots:
|
|
||||||
var ofBranch = newNimNode(nnkOfBranch)
|
|
||||||
# for exported procedures - strip * marker
|
|
||||||
let slotName = ($slot.name).replace("*","")
|
|
||||||
ofBranch.add newLit slotName
|
|
||||||
let params = slot.params
|
|
||||||
let hasReturn = not (params[0].kind == nnkEmpty)
|
|
||||||
var branchStmts = newStmtList()
|
|
||||||
var args = newSeq[PNimrodNode]()
|
|
||||||
# first params always the object
|
|
||||||
args.add ident "myQObject"
|
|
||||||
for i in 2.. <params.len:
|
|
||||||
let pType = getArgType params[i]
|
|
||||||
let argAccess = newNimNode(nnkBracketExpr)
|
|
||||||
.add (ident "args")
|
|
||||||
.add newIntLitNode(i-1)
|
|
||||||
if $pType == "QVariant":
|
|
||||||
args.add argAccess
|
|
||||||
else:
|
|
||||||
# function that maps QVariant type to nim type
|
|
||||||
let mapper = nimFromQtVariant[$pType]
|
|
||||||
let dot = newDotExpr(argAccess, ident mapper)
|
|
||||||
args.add dot
|
|
||||||
var call = newCall(ident slotName, args)
|
|
||||||
if hasReturn:
|
|
||||||
let retType = params[0]
|
|
||||||
let argAccess = newNimNode(nnkBracketExpr)
|
|
||||||
.add (ident "args")
|
|
||||||
.add newIntLitNode(0)
|
|
||||||
if $retType == "QVariant":
|
|
||||||
# eg: args[0].assign(getName(myQObject))
|
|
||||||
let dot = newDotExpr(argAccess, ident "assign")
|
|
||||||
call = newCall(dot, call)
|
|
||||||
else:
|
|
||||||
# eg: args[0].strVal = getName(myQObject)
|
|
||||||
let mapper = nimFromQtVariant[$retType]
|
|
||||||
let dot = newDotExpr(argAccess, ident mapper)
|
|
||||||
call = newAssignment(dot, call)
|
|
||||||
branchStmts.add call
|
|
||||||
ofBranch.add branchStmts
|
|
||||||
caseStmt.add ofBranch
|
|
||||||
# add else: discard
|
|
||||||
caseStmt.add newNimNode(nnkElse)
|
|
||||||
.add newStmtList().add newNimNode(nnkDiscardStmt).add newNimNode(nnkEmpty)
|
|
||||||
slotProto.body.add caseStmt
|
|
||||||
result.add slotProto
|
|
||||||
|
|
||||||
# generate create method
|
|
||||||
var createProto = (getAst prototypeCreate(typeName))[0]
|
|
||||||
# the template creates loads of openSyms - replace these with idents
|
|
||||||
createProto = doRemoveOpenSym(createProto)
|
|
||||||
if typ.isExported:
|
if typ.isExported:
|
||||||
createProto.exportDef()
|
result.exportDef()
|
||||||
else:
|
else:
|
||||||
createProto.unexportDef()
|
result.unexportDef()
|
||||||
var createBody = createProto.templateBody
|
|
||||||
|
proc genCreate(typ: PNimrodNode, signals: seq[PNimrodNode], slots: seq[PNimrodNode],
|
||||||
|
properties: seq[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||||
|
expectKind typ, nnkTypeDef
|
||||||
|
let typeName = typ.getTypeName()
|
||||||
|
result = (getAst prototypeCreate(typeName))[0]
|
||||||
|
# the template creates loads of openSyms - replace these with idents
|
||||||
|
result = doRemoveOpenSym(result)
|
||||||
|
if typ.isExported:
|
||||||
|
result.exportDef()
|
||||||
|
else:
|
||||||
|
result.unexportDef()
|
||||||
|
var createBody = result.body
|
||||||
for slot in slots:
|
for slot in slots:
|
||||||
let params = slot.params
|
let params = slot.params
|
||||||
let regSlotDot = newDotExpr(ident "myQObject", ident "registerSlot")
|
let regSlotDot = newDotExpr(ident "myQObject", ident "registerSlot")
|
||||||
|
@ -481,11 +375,129 @@ macro QtObject*(qtDecl: stmt): stmt {.immediate.} =
|
||||||
let call = newCall(regPropDot, newLit($propertyName), metaDot, readArg, writeArg, notifyArg)
|
let call = newCall(regPropDot, newLit($propertyName), metaDot, readArg, writeArg, notifyArg)
|
||||||
createBody.add call
|
createBody.add call
|
||||||
|
|
||||||
#echo repr createProto
|
proc genOnSlotCalled(typ: PNimrodNode, slots: seq[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||||
result.add createProto
|
expectKind typ, nnkTypeDef
|
||||||
|
let typeName = typ.getTypeName()
|
||||||
|
result = (getAst prototypeOnSlotCalled(typeName))[0]
|
||||||
|
var caseStmt = newNimNode(nnkCaseStmt)
|
||||||
|
caseStmt.add ident("slotName")
|
||||||
|
for slot in slots:
|
||||||
|
var ofBranch = newNimNode(nnkOfBranch)
|
||||||
|
# for exported procedures - strip * marker
|
||||||
|
let slotName = ($slot.name).replace("*","")
|
||||||
|
ofBranch.add newLit slotName
|
||||||
|
let params = slot.params
|
||||||
|
let hasReturn = not (params[0].kind == nnkEmpty)
|
||||||
|
var branchStmts = newStmtList()
|
||||||
|
var args = newSeq[PNimrodNode]()
|
||||||
|
# first params always the object
|
||||||
|
args.add ident "myQObject"
|
||||||
|
for i in 2.. <params.len:
|
||||||
|
let pType = getArgType params[i]
|
||||||
|
let argAccess = newNimNode(nnkBracketExpr)
|
||||||
|
.add (ident "args")
|
||||||
|
.add newIntLitNode(i-1)
|
||||||
|
if $pType == "QVariant":
|
||||||
|
args.add argAccess
|
||||||
|
else:
|
||||||
|
# function that maps QVariant type to nim type
|
||||||
|
let mapper = nimFromQtVariant[$pType]
|
||||||
|
let dot = newDotExpr(argAccess, ident mapper)
|
||||||
|
args.add dot
|
||||||
|
var call = newCall(ident slotName, args)
|
||||||
|
if hasReturn:
|
||||||
|
let retType = params[0]
|
||||||
|
let argAccess = newNimNode(nnkBracketExpr)
|
||||||
|
.add (ident "args")
|
||||||
|
.add newIntLitNode(0)
|
||||||
|
if $retType == "QVariant":
|
||||||
|
# eg: args[0].assign(getName(myQObject))
|
||||||
|
let dot = newDotExpr(argAccess, ident "assign")
|
||||||
|
call = newCall(dot, call)
|
||||||
|
else:
|
||||||
|
# eg: args[0].strVal = getName(myQObject)
|
||||||
|
let mapper = nimFromQtVariant[$retType]
|
||||||
|
let dot = newDotExpr(argAccess, ident mapper)
|
||||||
|
call = newAssignment(dot, call)
|
||||||
|
branchStmts.add call
|
||||||
|
ofBranch.add branchStmts
|
||||||
|
caseStmt.add ofBranch
|
||||||
|
# add else: discard
|
||||||
|
caseStmt.add newNimNode(nnkElse)
|
||||||
|
.add newStmtList().add newNimNode(nnkDiscardStmt).add newNimNode(nnkEmpty)
|
||||||
|
result.body.add caseStmt
|
||||||
|
|
||||||
for fn in userDefined:
|
macro QtObject*(qtDecl: stmt): stmt {.immediate.} =
|
||||||
result.add fn
|
## Generates boiler plate code for registering signals, slots
|
||||||
|
## and properties.
|
||||||
|
##
|
||||||
|
## Currently generates:
|
||||||
|
## - create: a method to register signal, slots and properties
|
||||||
|
## - superType: a template that returns the super type of the
|
||||||
|
## object defined within the macro body
|
||||||
|
## - onSlotCalled: a method to dispatch an on slot call to the
|
||||||
|
## appropiate method.
|
||||||
|
##
|
||||||
|
## Current limitations:
|
||||||
|
## - only one type can be defined within the body of code sent to the
|
||||||
|
## the macro. It is assumed, but not checked, that somewhere in the
|
||||||
|
## inheritance hierarchy this object derives from ``QObject``.
|
||||||
|
## - generics are not currently supported
|
||||||
|
expectKind(qtDecl, nnkStmtList)
|
||||||
|
#echo treeRepr qtDecl
|
||||||
|
result = newStmtList()
|
||||||
|
var slots = newSeq[PNimrodNode]()
|
||||||
|
var properties = newSeq[PNimrodNode]()
|
||||||
|
var signals = newSeq[PNimrodNode]()
|
||||||
|
# assume only one type per section for now
|
||||||
|
var typ: PNimrodNode
|
||||||
|
for it in qtDecl.children():
|
||||||
|
if it.kind == nnkTypeSection:
|
||||||
|
let typeDecl = it.findChild(it.kind == nnkTypeDef)
|
||||||
|
let superType = typeDecl.getSuperType()
|
||||||
|
if superType.kind == nnkEmpty:
|
||||||
|
# allow simple types and type aliases
|
||||||
|
result.add it
|
||||||
|
else:
|
||||||
|
# may come in useful if we want to check objects inherit from QObject
|
||||||
|
#let superName = if superType.kind == nnkIdent: superType
|
||||||
|
# else: superType.getNodeOf(nnkIdent)
|
||||||
|
if typ != nil:
|
||||||
|
error("you may not define more than one type " &
|
||||||
|
"within the code block passed to this macro")
|
||||||
|
else: # without this else, it fails to compile
|
||||||
|
typ = typeDecl
|
||||||
|
result.add it
|
||||||
|
result.add genSuperTemplate(typeDecl)
|
||||||
|
result.add genCreateDecl(typeDecl)
|
||||||
|
elif it.kind == nnkMethodDef:
|
||||||
|
if tryHandleSigSlot(it, signals, slots, result): continue
|
||||||
|
result.add it
|
||||||
|
elif it.kind == nnkProcDef:
|
||||||
|
if tryHandleSigSlot(it, signals, slots, result): continue
|
||||||
|
result.add it
|
||||||
|
elif it.kind == nnkCommand:
|
||||||
|
let bracket = it[0]
|
||||||
|
if bracket.kind != nnkBracketExpr:
|
||||||
|
error("do not know how to handle: \n" & repr(it))
|
||||||
|
# BracketExpr
|
||||||
|
# Ident !"QtProperty"
|
||||||
|
# Ident !"string"
|
||||||
|
let cmdIdent = bracket[0]
|
||||||
|
if cmdIdent == nil or cmdIdent.kind != nnkIdent or
|
||||||
|
($cmdIdent).toLower() != "qtproperty":
|
||||||
|
error("do not know how to handle: \n" & repr(it))
|
||||||
|
properties.add it
|
||||||
|
else:
|
||||||
|
# everything else should pass through unchanged
|
||||||
|
result.add it
|
||||||
|
if typ == nil:
|
||||||
|
error("you must declare an object that inherits from QObject")
|
||||||
|
let typeName = typ.getTypeName()
|
||||||
|
|
||||||
|
result.add genOnSlotCalled(typ, slots)
|
||||||
|
|
||||||
|
result.add genCreate(typ, signals, slots, properties)
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
echo repr result
|
echo repr result
|
||||||
|
|
Loading…
Reference in New Issue