Skip to content

Instantly share code, notes, and snippets.

@PhilipWitte
Last active February 8, 2016 15:49
Show Gist options
  • Save PhilipWitte/ff8cc26d76e962591a48 to your computer and use it in GitHub Desktop.
Save PhilipWitte/ff8cc26d76e962591a48 to your computer and use it in GitHub Desktop.
Nim composition "multi-inheritance" auto-dispatch
import macros
# --- --- --- #
type
Base* {.pure inheritable.} = object
Comp* {.pure inheritable.} = object
proc addCall(result:var NimNode, comp:NimNode, call:string) =
let callNode = newCall(ident(call), comp)
result.add newTree(nnkWhenStmt,
newTree(nnkElifBranch,
newCall(ident"compiles", callNode),
newTree(nnkStmtList, callNode)
)
)
proc addCalls(result:var NimNode, comp:NimNode, call:string, baseType,compType:NimNode) =
let compRecs =
if comp.kind == nnkSym:
getType(comp)[1]
elif comp.kind == nnkDotExpr:
getType(comp[1])[1]
else:
nil # TODO: error handling
for rec in compRecs.children:
let recType = getType(rec)[0]
let dotExpr = newDotExpr(comp, rec)
if sameType(recType, baseType): result.addCall(dotExpr, call)
elif sameType(recType, compType): result.addCalls(dotExpr, call, baseType, compType)
macro `.`*(comp:Comp, call:string): untyped =
result = newStmtList()
result.addCalls(comp, call.strVal, bindSym("Base"), bindSym("Comp"))
# --- --- --- #
type
Robot = object of Base
fuel: float
Animal = object of Base
name: string
type
Cat = object of Comp
animal: Animal
Dog = object of Comp
animal: Animal
type
KillerRobot = object of Comp
robot: Robot
CleanerRobot = object of Comp
robot: Robot
type
KillerRobotDog = object of Comp
dog: Dog
killerRobot: KillerRobot
# ---
proc newCat(name:string): Cat =
result.animal.name = name
proc newDog(name:string): Dog =
result.animal.name = name
proc newCleanerRobot(fuel:float): CleanerRobot =
result.robot.fuel = fuel
proc newKillerRobot(fuel:float): KillerRobot =
result.robot.fuel = fuel
proc newKillerRobotDog(name:string, fuel:float): KillerRobotDog =
result.dog = newDog(name)
result.killerRobot = newKillerRobot(fuel)
# ---
proc drive(r:Robot) = echo r.fuel, " gas left"
proc speak(a:Animal) = echo a.name, " talks"
# --- --- --- #
proc main =
let d = newDog("sparky")
let c = newCleanerRobot(1.3)
let k = newKillerRobotDog("grumpy", 2.5)
d.speak()
c.drive()
k.speak()
k.drive()
# ---
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment