Skip to content

Instantly share code, notes, and snippets.

@bucho666
Last active March 10, 2024 15:44
Show Gist options
  • Save bucho666/b7e241b51d8e7a78c7c63c3ca2226e4c to your computer and use it in GitHub Desktop.
Save bucho666/b7e241b51d8e7a78c7c63c3ca2226e4c to your computer and use it in GitHub Desktop.
nim class macro
import macros
proc typeName(head: NimNode): NimNode =
if head.len == 0: head else: head[1]
proc baseName(head: NimNode): NimNode =
if head.len == 0: newIdentNode("RootObj") else: head[2]
proc isObjectDef(head: NimNode): bool =
head.len == 0 or head[2].kind == nnkIdent
proc buildObjectTypeDecl(head: NimNode): NimNode =
template typeDecl(a, b): untyped =
type a* = ref object of b
result = getAst(typeDecl(head.typeName, head.baseName))
proc buildBasicTypeDecl(head: NimNode): NimNode =
result = newNimNode(nnkTypeSection)
result.add(newNimNode(nnkTypeDef))
result[0].add(newIdentNode($head[1]))
result[0].add(newNimNode(nnkEmpty))
result[0].add(head[2])
proc buildTypeDecl(head: NimNode): NimNode =
if head.isObjectDef:
head.buildObjectTypeDecl
else:
head.buildBasicTypeDecl
macro class*(head, body: untyped): untyped =
result = newStmtList()
result.add(head.buildTypeDecl)
let
typeName = head.typeName
ctorName = newIdentNode("new" & $typeName)
var recList = newNimNode(nnkRecList)
for node in body.children:
case node.kind:
of nnkMethodDef, nnkProcDef, nnkIteratorDef:
if node.name.kind != nnkAccQuoted and node.name == ctorName:
node.params[0] = typeName
else:
node.params.insert(1, newIdentDefs(ident("self"), typeName))
result.add(node)
of nnkVarSection:
if not head.isObjectDef:
error "Invalid node: " & node.lispRepr
for n in node.children:
recList.add(n)
else:
result.add(node)
if head.isObjectDef:
result[0][0][2][0][2] = recList
import class
class Animal:
var name: string
var age: int
method vocalize: string {.base.} =
"..."
method age_human_yrs: int {.base.} =
self.age
class Dog of Animal:
method vocalize: string =
"woof"
method age_human_yrs: int =
self.age * 7
class Cat of Animal:
method vocalize: string =
"meow"
class Rabbit of Animal:
proc newRabbit(name: string, age: int) =
result = Rabbit(name: name, age: age)
method vocalize: string =
"meep"
proc `$`: string =
"rabbit:" & self.name & ":" & $self.age
class Range:
var min, max: int
iterator items(): int =
for n in self.min..self.max:
yield n
class Point of tuple[x, y: int]:
proc newPoint(x, y: int) =
(x: x, y: y)
proc `$`():string =
"x:" & $self.x & " y:" & $self.y
class Main:
proc animalDemo() =
var animals: seq[Animal] = @[]
animals.add(Dog(name: "Sparky", age: 10))
animals.add(Cat(name: "Mitten", age: 10))
for a in animals:
echo a.vocalize()
echo a.age_human_yrs()
let r = newRabbit("Fluffy", 3)
echo r.vocalize()
echo r.age_human_yrs()
echo r
proc iteratorDemo() =
for n in Range(min:3, max:12):
echo n
proc tupleClassDemo() =
echo (x:1, y:2)
echo (3, 4)
echo newPoint(5, 6)
proc run() =
self.animalDemo
self.iteratorDemo
self.tupleClassDemo
Main().run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment