Added 'new_class' instead of 'new' for the macro.

This commit is contained in:
Joey Payne 2015-05-14 18:19:59 -06:00
commit 321be06f40
2 changed files with 95 additions and 20 deletions

View file

@ -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
```

View file

@ -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