Added 'new_class' instead of 'new' for the macro.
This commit is contained in:
parent
e3d9812afe
commit
321be06f40
2 changed files with 95 additions and 20 deletions
|
|
@ -6,9 +6,13 @@ These extensions aim to improve the usability of nim in practical applications.
|
||||||
Extensions so far include:
|
Extensions so far include:
|
||||||
|
|
||||||
## OOP macro
|
## OOP macro
|
||||||
|
|
||||||
|
This module was modified from the OOP section on the excellent website http://nim-by-example.github.io/
|
||||||
|
|
||||||
usage:
|
usage:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
|
import oop_macro
|
||||||
class BaseObject: # inherits from RootObj
|
class BaseObject: # inherits from RootObj
|
||||||
# attributes/properties
|
# attributes/properties
|
||||||
var
|
var
|
||||||
|
|
@ -26,3 +30,8 @@ Extensions so far include:
|
||||||
method override_me(argx: float): int=
|
method override_me(argx: float): int=
|
||||||
result = int(self.y) + int(argx) + self.x
|
result = int(self.y) + int(argx) + self.x
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There's also an example in the code that demonstrates the inheritance that can be run by executing:
|
||||||
|
```bash
|
||||||
|
nim c -r oop_macro.nim
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,26 @@
|
||||||
import macros
|
import macros
|
||||||
|
import strutils
|
||||||
|
|
||||||
{.hint[XDeclaredButNotUsed]: off.}
|
{.hint[XDeclaredButNotUsed]: off.}
|
||||||
|
|
||||||
macro new*(obj: expr): expr {.immediate.}=
|
macro new_class*(obj: untyped): untyped=
|
||||||
## Replaces the new keyword to call an init
|
## Creates a new instance of the class
|
||||||
## function if it exists
|
## and calls the init() method on it
|
||||||
quote do:
|
quote do:
|
||||||
var init_obj = `obj`
|
var init_obj = `obj`
|
||||||
when compiles(init_obj.init()):
|
when compiles(init_obj.init()):
|
||||||
init_obj.init()
|
init_obj.init()
|
||||||
init_obj
|
init_obj
|
||||||
|
|
||||||
macro class*(head: expr, body: stmt): stmt {.immediate.} =
|
|
||||||
|
macro class*(head: untyped, body: untyped): untyped=
|
||||||
# The macro is immediate so that it doesn't
|
# The macro is immediate so that it doesn't
|
||||||
# resolve identifiers passed to it
|
# resolve identifiers passed to it
|
||||||
|
|
||||||
# object reference name inside methods.
|
# object reference name inside methods.
|
||||||
# ie: self, self
|
# ie: self, self
|
||||||
let obj_reference = "self"
|
let obj_reference = "self"
|
||||||
|
var export_class: bool = false # whether or not to export the class to other modules
|
||||||
|
|
||||||
var typeName, baseName: PNimrodNode
|
var typeName, baseName: PNimrodNode
|
||||||
|
|
||||||
|
|
@ -39,6 +42,21 @@ macro class*(head: expr, body: stmt): stmt {.immediate.} =
|
||||||
typeName = head[1]
|
typeName = head[1]
|
||||||
baseName = head[2]
|
baseName = head[2]
|
||||||
|
|
||||||
|
elif head.kind == nnkInfix and $head[0] == "*" and $head[1] == "of":
|
||||||
|
# echo head.treeRepr
|
||||||
|
# -----------
|
||||||
|
# Infix
|
||||||
|
# Ident !"*"
|
||||||
|
# Ident !"Animal"
|
||||||
|
# Prefix
|
||||||
|
# Ident !"of"
|
||||||
|
# Ident !"RootObj"
|
||||||
|
export_class = true
|
||||||
|
typeName = head[1]
|
||||||
|
baseName = head[2][1]
|
||||||
|
elif head.kind == nnkInfix and $head[0] == "*":
|
||||||
|
export_class = true
|
||||||
|
typeName = head[1]
|
||||||
else:
|
else:
|
||||||
quit "Invalid node: " & head.lispRepr
|
quit "Invalid node: " & head.lispRepr
|
||||||
|
|
||||||
|
|
@ -106,10 +124,22 @@ macro class*(head: expr, body: stmt): stmt {.immediate.} =
|
||||||
|
|
||||||
# forward declare the inheritable method
|
# forward declare the inheritable method
|
||||||
let n2 = copyNimTree(n)
|
let n2 = copyNimTree(n)
|
||||||
let proc_name = $(n2.name.toStrLit())
|
|
||||||
let type_name = $(typeName.toStrLit())
|
let type_name = $(typeName.toStrLit())
|
||||||
let new_name = ident(proc_name & type_name)
|
var proc_name = ""
|
||||||
n2.name = new_name
|
|
||||||
|
if n2.name.kind == nnkIdent:
|
||||||
|
proc_name = $(n2.name.toStrLit())
|
||||||
|
n2.name = ident(proc_name & type_name)
|
||||||
|
elif n2.name.kind == nnkPostFix:
|
||||||
|
if n2.name[1].kind == nnkIdent:
|
||||||
|
proc_name = $(n2.name[1].toStrLit())
|
||||||
|
n2.name[1] = ident(proc_name & type_name)
|
||||||
|
elif n2.name[1].kind == nnkAccQuoted:
|
||||||
|
proc_name = $(n2.name[1][0].toStrLit())
|
||||||
|
n2.name[1][0] = ident(proc_name & type_name)
|
||||||
|
elif n2.name.kind == nnkAccQuoted:
|
||||||
|
proc_name = $(n2.name[0].toStrLit())
|
||||||
|
n2.name[0] = ident(proc_name & type_name)
|
||||||
result.add(n2)
|
result.add(n2)
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
@ -127,10 +157,25 @@ macro class*(head: expr, body: stmt): stmt {.immediate.} =
|
||||||
# ie: procName_ClassName()
|
# ie: procName_ClassName()
|
||||||
let n2 = copyNimTree(node)
|
let n2 = copyNimTree(node)
|
||||||
n2.params.insert(1, newIdentDefs(ident(obj_reference), typeName))
|
n2.params.insert(1, newIdentDefs(ident(obj_reference), typeName))
|
||||||
let proc_name = $(n2.name.toStrLit())
|
|
||||||
let type_name = $(typeName.toStrLit())
|
let type_name = $(typeName.toStrLit())
|
||||||
let new_name = ident(proc_name & type_name)
|
var proc_name = $(n2.name.toStrLit())
|
||||||
n2.name = new_name
|
var is_assignment = proc_name.contains("=")
|
||||||
|
|
||||||
|
if n2.name.kind == nnkIdent:
|
||||||
|
proc_name = $(n2.name.toStrLit())
|
||||||
|
n2.name = ident(proc_name & type_name)
|
||||||
|
elif n2.name.kind == nnkPostFix:
|
||||||
|
if n2.name[1].kind == nnkIdent:
|
||||||
|
proc_name = $(n2.name[1].toStrLit())
|
||||||
|
n2.name[1] = ident(proc_name & type_name)
|
||||||
|
elif n2.name[1].kind == nnkAccQuoted:
|
||||||
|
proc_name = $(n2.name[1][0].toStrLit())
|
||||||
|
n2.name[1][0] = ident(proc_name & type_name)
|
||||||
|
elif n2.name.kind == nnkAccQuoted:
|
||||||
|
proc_name = $(n2.name[0].toStrLit())
|
||||||
|
n2.name[0] = ident(proc_name & type_name)
|
||||||
|
|
||||||
result.add(n2)
|
result.add(n2)
|
||||||
|
|
||||||
# simply call the class method from here
|
# simply call the class method from here
|
||||||
|
|
@ -139,6 +184,10 @@ macro class*(head: expr, body: stmt): stmt {.immediate.} =
|
||||||
var p: seq[PNimrodNode] = @[]
|
var p: seq[PNimrodNode] = @[]
|
||||||
for i in 1..n.params.len-1:
|
for i in 1..n.params.len-1:
|
||||||
p.add(n.params[i][0])
|
p.add(n.params[i][0])
|
||||||
|
if is_assignment:
|
||||||
|
let dot = newDotExpr(ident(obj_reference), ident(proc_name & type_name))
|
||||||
|
n.body = newStmtList(newAssignment(dot, p[1]))
|
||||||
|
else:
|
||||||
n.body = newStmtList(newCall(proc_name & type_name, p))
|
n.body = newStmtList(newCall(proc_name & type_name, p))
|
||||||
|
|
||||||
result.add(n)
|
result.add(n)
|
||||||
|
|
@ -174,8 +223,16 @@ macro class*(head: expr, body: stmt): stmt {.immediate.} =
|
||||||
|
|
||||||
var type_decl: PNimrodNode
|
var type_decl: PNimrodNode
|
||||||
if baseName == nil:
|
if baseName == nil:
|
||||||
|
if export_class:
|
||||||
|
type_decl = quote do:
|
||||||
|
type `typeName`* = ref object of RootObj
|
||||||
|
else:
|
||||||
type_decl = quote do:
|
type_decl = quote do:
|
||||||
type `typeName` = ref object of RootObj
|
type `typeName` = ref object of RootObj
|
||||||
|
else:
|
||||||
|
if export_class:
|
||||||
|
type_decl = quote do:
|
||||||
|
type `typeName`* = ref object of `baseName`
|
||||||
else:
|
else:
|
||||||
type_decl = quote do:
|
type_decl = quote do:
|
||||||
type `typeName` = ref object of `baseName`
|
type `typeName` = ref object of `baseName`
|
||||||
|
|
@ -199,7 +256,7 @@ macro class*(head: expr, body: stmt): stmt {.immediate.} =
|
||||||
result.insert(0, type_decl)
|
result.insert(0, type_decl)
|
||||||
|
|
||||||
|
|
||||||
class Animal of RootObj:
|
class Animal* of RootObj: #exports the class. Need to use RootObj for exports
|
||||||
var name: string
|
var name: string
|
||||||
var age: int
|
var age: int
|
||||||
method stuff(s:string): string = s
|
method stuff(s:string): string = s
|
||||||
|
|
@ -220,14 +277,23 @@ class Tiger of Cat:
|
||||||
echo "I am a new tiger"
|
echo "I am a new tiger"
|
||||||
method vocalize: string =
|
method vocalize: string =
|
||||||
# no need for super.super!
|
# no need for super.super!
|
||||||
self.vocalize_animal() & "Rawr!"
|
self.vocalize_animal() & "Rawr!" & self.vocalize_cat()
|
||||||
|
|
||||||
if isMainModule:
|
if isMainModule:
|
||||||
var animals: seq[Animal] = @[]
|
var animals: seq[Animal] = @[]
|
||||||
animals.add(new(Dog(name: "Sparky", age: 10)))
|
animals.add(new_class(Dog(name: "Sparky", age: 10)))
|
||||||
animals.add(new(Cat(name: "Mitten", age: 10)))
|
animals.add(new_class(Cat(name: "Mitten", age: 10)))
|
||||||
animals.add(new(Tiger(name: "Jean", age: 2)))
|
animals.add(new_class(Tiger(name: "Jean", age: 2)))
|
||||||
|
|
||||||
for a in animals:
|
for a in animals:
|
||||||
echo a.name, " says ", a.vocalize()
|
echo a.name, " says ", a.vocalize()
|
||||||
echo a.age_human_yrs()
|
echo a.age_human_yrs()
|
||||||
|
|
||||||
|
# prints:
|
||||||
|
# I am a new tiger
|
||||||
|
# Sparky says woof
|
||||||
|
# 70
|
||||||
|
# Mitten says ...meow
|
||||||
|
# 10
|
||||||
|
# Jean says ...Rawr!...meow
|
||||||
|
# 2
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue