mirror of
https://github.com/status-im/dotherside.git
synced 2025-02-14 13:46:39 +00:00
Merge pull request #15 from cowboy-coders/fix/macro_ordering
Preserve ordering of declarations in QtObject macro
This commit is contained in:
commit
12479b928f
@ -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,77 +299,86 @@ 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]
|
||||||
|
result.body = newEmptyNode()
|
||||||
|
if typ.isExported:
|
||||||
|
result.exportDef()
|
||||||
|
else:
|
||||||
|
result.unexportDef()
|
||||||
|
|
||||||
## define onSlotCalled
|
proc genCreate(typ: PNimrodNode, signals: seq[PNimrodNode], slots: seq[PNimrodNode],
|
||||||
var slotProto = (getAst prototypeOnSlotCalled(typeName))[0]
|
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:
|
||||||
|
let params = slot.params
|
||||||
|
let regSlotDot = newDotExpr(ident "myQObject", ident "registerSlot")
|
||||||
|
let name = ($slot.name).replace("*","")
|
||||||
|
let argTypesArray = genArgTypeArray(params)
|
||||||
|
let call = newCall(regSlotDot, newLit name, argTypesArray)
|
||||||
|
createBody.add call
|
||||||
|
for signal in signals:
|
||||||
|
let params = signal.params
|
||||||
|
let regSigDot = newDotExpr(ident "myQObject", ident "registerSignal")
|
||||||
|
let name = ($signal.name).replace("*","")
|
||||||
|
let argTypesArray = genArgTypeArray(params)
|
||||||
|
let call = newCall(regSigDot, newLit name, argTypesArray)
|
||||||
|
createBody.add call
|
||||||
|
for property in properties:
|
||||||
|
let bracket = property[0]
|
||||||
|
expectKind bracket, nnkBracketExpr
|
||||||
|
#Command
|
||||||
|
# BracketExpr
|
||||||
|
# Ident !"QtProperty"
|
||||||
|
# Ident !"string"
|
||||||
|
# Ident !"name"
|
||||||
|
# StmtList
|
||||||
|
let nimPropType = bracket[1]
|
||||||
|
let qtPropMeta = nim2QtMeta[$nimPropType]
|
||||||
|
if qtPropMeta == nil: error($nimPropType & " not supported")
|
||||||
|
let metaDot = newDotExpr(ident "QMetaType", ident qtPropMeta)
|
||||||
|
let propertyName = property[1]
|
||||||
|
var read, write, notify: PNimrodNode
|
||||||
|
let stmtList = property[2]
|
||||||
|
# fields
|
||||||
|
# StmtList
|
||||||
|
# Asgn
|
||||||
|
# Ident !"read"
|
||||||
|
# Ident !"getName
|
||||||
|
for asgn in stmtList.children:
|
||||||
|
let name = asgn[0]
|
||||||
|
case $name
|
||||||
|
of "read":
|
||||||
|
read = asgn[1]
|
||||||
|
of "write":
|
||||||
|
write = asgn[1]
|
||||||
|
of "notify":
|
||||||
|
notify = asgn[1]
|
||||||
|
else:
|
||||||
|
error("unknown property field: " & $name)
|
||||||
|
let regPropDot = newDotExpr(ident "myQObject", ident "registerProperty")
|
||||||
|
let readArg = if read == nil: newNilLit() else: newLit($read)
|
||||||
|
let writeArg = if write == nil: newNilLit() else: newLit($write)
|
||||||
|
let notifyArg = if notify == nil: newNilLit() else: newLit($notify)
|
||||||
|
let call = newCall(regPropDot, newLit($propertyName), metaDot, readArg, writeArg, notifyArg)
|
||||||
|
createBody.add call
|
||||||
|
|
||||||
|
proc genOnSlotCalled(typ: PNimrodNode, slots: seq[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||||
|
expectKind typ, nnkTypeDef
|
||||||
|
let typeName = typ.getTypeName()
|
||||||
|
result = (getAst prototypeOnSlotCalled(typeName))[0]
|
||||||
var caseStmt = newNimNode(nnkCaseStmt)
|
var caseStmt = newNimNode(nnkCaseStmt)
|
||||||
caseStmt.add ident("slotName")
|
caseStmt.add ident("slotName")
|
||||||
for slot in slots:
|
for slot in slots:
|
||||||
@ -410,82 +419,84 @@ macro QtObject*(qtDecl: stmt): stmt {.immediate.} =
|
|||||||
let mapper = nimFromQtVariant[$retType]
|
let mapper = nimFromQtVariant[$retType]
|
||||||
let dot = newDotExpr(argAccess, ident mapper)
|
let dot = newDotExpr(argAccess, ident mapper)
|
||||||
call = newAssignment(dot, call)
|
call = newAssignment(dot, call)
|
||||||
branchStmts.add call
|
branchStmts.add call
|
||||||
ofBranch.add branchStmts
|
ofBranch.add branchStmts
|
||||||
caseStmt.add ofBranch
|
caseStmt.add ofBranch
|
||||||
# add else: discard
|
# add else: discard
|
||||||
caseStmt.add newNimNode(nnkElse)
|
caseStmt.add newNimNode(nnkElse)
|
||||||
.add newStmtList().add newNimNode(nnkDiscardStmt).add newNimNode(nnkEmpty)
|
.add newStmtList().add newNimNode(nnkDiscardStmt).add newNimNode(nnkEmpty)
|
||||||
slotProto.body.add caseStmt
|
result.body.add caseStmt
|
||||||
result.add slotProto
|
|
||||||
|
|
||||||
# generate create method
|
macro QtObject*(qtDecl: stmt): stmt {.immediate.} =
|
||||||
var createProto = (getAst prototypeCreate(typeName))[0]
|
## Generates boiler plate code for registering signals, slots
|
||||||
# the template creates loads of openSyms - replace these with idents
|
## and properties.
|
||||||
createProto = doRemoveOpenSym(createProto)
|
##
|
||||||
if typ.isExported:
|
## Currently generates:
|
||||||
createProto.exportDef()
|
## - create: a method to register signal, slots and properties
|
||||||
else:
|
## - superType: a template that returns the super type of the
|
||||||
createProto.unexportDef()
|
## object defined within the macro body
|
||||||
var createBody = createProto.templateBody
|
## - onSlotCalled: a method to dispatch an on slot call to the
|
||||||
for slot in slots:
|
## appropiate method.
|
||||||
let params = slot.params
|
##
|
||||||
let regSlotDot = newDotExpr(ident "myQObject", ident "registerSlot")
|
## Current limitations:
|
||||||
let name = ($slot.name).replace("*","")
|
## - only one type can be defined within the body of code sent to the
|
||||||
let argTypesArray = genArgTypeArray(params)
|
## the macro. It is assumed, but not checked, that somewhere in the
|
||||||
let call = newCall(regSlotDot, newLit name, argTypesArray)
|
## inheritance hierarchy this object derives from ``QObject``.
|
||||||
createBody.add call
|
## - generics are not currently supported
|
||||||
for signal in signals:
|
expectKind(qtDecl, nnkStmtList)
|
||||||
let params = signal.params
|
#echo treeRepr qtDecl
|
||||||
let regSigDot = newDotExpr(ident "myQObject", ident "registerSignal")
|
result = newStmtList()
|
||||||
let name = ($signal.name).replace("*","")
|
var slots = newSeq[PNimrodNode]()
|
||||||
let argTypesArray = genArgTypeArray(params)
|
var properties = newSeq[PNimrodNode]()
|
||||||
let call = newCall(regSigDot, newLit name, argTypesArray)
|
var signals = newSeq[PNimrodNode]()
|
||||||
createBody.add call
|
# assume only one type per section for now
|
||||||
for property in properties:
|
var typ: PNimrodNode
|
||||||
let bracket = property[0]
|
for it in qtDecl.children():
|
||||||
expectKind bracket, nnkBracketExpr
|
if it.kind == nnkTypeSection:
|
||||||
#Command
|
let typeDecl = it.findChild(it.kind == nnkTypeDef)
|
||||||
# BracketExpr
|
let superType = typeDecl.getSuperType()
|
||||||
# Ident !"QtProperty"
|
if superType.kind == nnkEmpty:
|
||||||
# Ident !"string"
|
# allow simple types and type aliases
|
||||||
# Ident !"name"
|
result.add it
|
||||||
# StmtList
|
|
||||||
let nimPropType = bracket[1]
|
|
||||||
let qtPropMeta = nim2QtMeta[$nimPropType]
|
|
||||||
if qtPropMeta == nil: error($nimPropType & " not supported")
|
|
||||||
let metaDot = newDotExpr(ident "QMetaType", ident qtPropMeta)
|
|
||||||
let propertyName = property[1]
|
|
||||||
var read, write, notify: PNimrodNode
|
|
||||||
let stmtList = property[2]
|
|
||||||
# fields
|
|
||||||
# StmtList
|
|
||||||
# Asgn
|
|
||||||
# Ident !"read"
|
|
||||||
# Ident !"getName
|
|
||||||
for asgn in stmtList.children:
|
|
||||||
let name = asgn[0]
|
|
||||||
case $name
|
|
||||||
of "read":
|
|
||||||
read = asgn[1]
|
|
||||||
of "write":
|
|
||||||
write = asgn[1]
|
|
||||||
of "notify":
|
|
||||||
notify = asgn[1]
|
|
||||||
else:
|
else:
|
||||||
error("unknown property field: " & $name)
|
# may come in useful if we want to check objects inherit from QObject
|
||||||
let regPropDot = newDotExpr(ident "myQObject", ident "registerProperty")
|
#let superName = if superType.kind == nnkIdent: superType
|
||||||
let readArg = if read == nil: newNilLit() else: newLit($read)
|
# else: superType.getNodeOf(nnkIdent)
|
||||||
let writeArg = if write == nil: newNilLit() else: newLit($write)
|
if typ != nil:
|
||||||
let notifyArg = if notify == nil: newNilLit() else: newLit($notify)
|
error("you may not define more than one type " &
|
||||||
let call = newCall(regPropDot, newLit($propertyName), metaDot, readArg, writeArg, notifyArg)
|
"within the code block passed to this macro")
|
||||||
createBody.add call
|
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")
|
||||||
|
|
||||||
#echo repr createProto
|
result.add genOnSlotCalled(typ, slots)
|
||||||
result.add createProto
|
|
||||||
|
|
||||||
for fn in userDefined:
|
result.add genCreate(typ, signals, slots, properties)
|
||||||
result.add fn
|
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
echo repr result
|
echo repr result
|
||||||
|
Loading…
x
Reference in New Issue
Block a user