Giter VIP home page Giter VIP logo

nim-classes's Introduction

Classes

A collection of macros which add class support to Nim. To install, run nimble install classes. Features include:

  • Simple class syntax
  • Class variables with default values
  • Inheritance and super.calls()
  • Default constructors
  • Static and abstract methods
  • Methods and variables defined in any order
  • Mixins

Examples

import classes

# A base class
class Shape:

    ## The position of the shape
    var x = 0
    var y = 0

    ## Optional constructor
    method init() =
        echo "Creating a Shape"

    ## Optional destructor (not supported in JS)
    method deinit() =
        echo "Destroying a Shape"

    ## Abstract draw function
    method draw()

    ## Calculate size
    method calculateSize(): float = return 0

    ## Static method
    method defaultSize(): float {.static.} = 5


# A subclass
class Square of Shape:

    # Radius
    var radius = 5

    ## Override constructor
    method init() =
        super.init()
        echo "Creating a Square"

    ## Draw it
    method draw() =
        echo "Drawing a square of size " & $this.calculateSize()

    ## Calculate size
    method calculateSize(): float = super.calculateSize() + this.radius


# Many ways of creating class instances:
let obj = Square.init()
let obj = Square().init()
let obj = newSquare()

# Call static methods
# NOTE: Static methods don't always work if the name of your nim file is the exact same as the class name. 
# You may get a `type mismatch: got <>` error in that case.
Shape.defaultSize()


# Data only classes
class DataOnly:
    var v0 = 7
    var v1: int
    var v2: string

# Constructing it this way allows you to pass in values for the variables that don't have values set
let obj = DataOnly(v1: 10, v2: "20").init()

# Using the mixin keyword will copy the variables and methods from one class to another
class CustomNote:
    var note = ""

class Circle of Shape:
    mixin CustomNote

let circle = Circle.init()
circle.note = "My custom note"

# Singleton classes act the same way as standard classes, but use a .shared() accessor instead of constructors.
# The default constructor is called the first time the singleton class is accessed.
singleton MySingletonClass:
    var v1 = 7
    method init() = echo "Singleton accessed for the first time!"

# Access the singleton, this also triggers the init() the first time
echo MySingletonClass.shared.v1

Issues

  • JS backend: As of Nim 1.4.8 (when I last tested), the internal dom library is exposing a class() function, and this function always takes precedent over this library's class macro. I don't know how to fix this. In the mean time, you can replace the word class with classes.class

nim-classes's People

Contributors

jjv360 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

nim-classes's Issues

Method overloads

Are method overloads possible?

import classes

class Foo:
  var foo: float

  method init() =
    discard this

  method init(foo: float) =
    this.foo = foo

let foo1 = Foo(foo: 1).init()
let foo2 = Foo.init(foo: 2)

echo foo1.repr
echo foo2.repr

That example produces this error:

~/foo.nim(13, 15) Error: type expected

new operator

Hey,
Awesome you pursue this idea! I tried this a while in the past as well, although i tried to make automagic new operator for standard nim objects, allocating non-ref with init() and ref with new(). That was a limited success. I spent a while mucking in nim's ast trees. Since then i no longer use nim for anything. Since you are interested in this featured i figured i will share what i cobbled together. If you find any of this useful - treat this snippet as public domain code. And if it is useless - off to ๐Ÿ—‘๏ธ then ๐Ÿ‘๐Ÿป

import macros
import strutils
# 12:43 <Araq> compiler/semstmts, edit semOverride
# 12:44 <Araq> add a new field finalizer* to the TType in ast.nim
# 12:44 <Araq> look at how deepcopy is implemented
# 12:44 <Araq> where it's used and instantiated
# 12:45 <Araq> cause `=finalize` needs to works with generics too
# 12:45 <Araq> so that's a slight complication
# 12:45 <Araq> but actually just grep for "deepcopy"
# 12:45 <Araq> it's all there for it
# 12:45 <Araq> generic instantation magic
# 12:46 <Araq> codegen type info slot

macro ctor*(none: untyped): auto =
    let args = callsite()
    var prc: NimNode
    var allocator: NimNode
    if args.len == 3:
        if args[1].kind != nnkBracket:
            error("Usage: {.ctor: [allocator: prc]}")
        if args[2].kind != nnkProcDef:
            error("ctor pragma is used only with procedures")
        prc = args[2]
        var margs = args[1]
        for i in 0 ..< margs.len:
            if $margs[i][0] == "allocator":
                allocator = margs[i][1]
            else:
                error("Unknown {.ctor.} paramater " & $margs[i][0])
    else:
        if args[1].kind != nnkProcDef:
            error("ctor pragma is used only with procedures")
        prc = args[1]
        allocator = new_ident_node("new")

    if prc[3][1][1].kind != nnkVarTy:
        error("Constructor must have var non-ref type as first parameter")
    if prc[3][0].kind != nnkEmpty:
        error("Constructor must not have return type")
    var proc_name = $prc[0]
    if proc_name notin ["init", "init*"]:
        error("Constructor name must be `init` but is `" & proc_name & "`")

    var export_proc = proc_name.ends_with("*")
    var type_identifier = prc[3][1][1][0]

    var ctor = quote do:
        proc init(_: typedesc[`type_identifier`]): `type_identifier` =
            init(result)
    ctor = ctor[0]
    ctor[2] = prc[2]

    if export_proc:
        ctor[0] = new_nim_node(nnkPostfix).add(
            new_ident_node("*"),
            ctor[0]
        )

    # Extend ctor with parameters of constructor
    for i in 2 ..< prc[3].len:
        ctor[3].add(prc[3][i])

    # Passes ctor params to main init proc
    ctor[6][0][1] = new_ident_node("result")    # otherwise result is taken from macro context. weird!
    for i in 2 ..< prc[3].len:
        ctor[6][0].add(prc[3][i][0])

    var allc = quote do:
        proc new(_: typedesc[`type_identifier`]): ref `type_identifier` =
            `allocator`(result)
            init(result[])
    allc = allc[0]
    allc[2] = prc[2]

    if export_proc:
        allc[0] = new_nim_node(nnkPostfix).add(
            new_ident_node("*"),
            allc[0]
        )

    # Extend allc with parameters of constructor
    for i in 2 ..< prc[3].len:
        allc[3].add(prc[3][i])

    # Passes allc params to main init proc
    allc[6][0][1] = new_ident_node("result")    # otherwise result is taken from macro context. weird!
    allc[6][1][1][0] = new_ident_node("result")
    for i in 2 ..< prc[3].len:
        allc[6][1].add(prc[3][i][0])

    result = new_stmt_list(prc, ctor, allc)


when is_main_module:
    type
        Foo = object
            a: int
        Bar = object
            a: int

    proc destroy(self: ref Foo) =
        echo "done"
    
    proc custom_new(res: var ref Foo) =
        echo "allocating with custom allocator"
        var x: ref Foo
        new(x)
        res = x

    # var x: ref Foo
    # custom_new(x)

    # proc init(self: var Foo, n: int) =
    #     self.a = n

    # proc init(_: typedesc[Foo], n: int): Foo =
    #     init(result, n)

    # proc new(_: typedesc[Foo], n: int): ref Foo =
    #     custom_new(result, destroy)
    #     init(result[], n)

    proc init(self: var Foo, n: int) {.ctor.} =
        self.a = n

    # proc init(self: var Bar, n: int) {.ctor: [allocator: custom_new].} =
    #     self.a = n

    var f: Foo
    f.init(1)
    echo f.repr
    #echo Foo.init(2).repr
    echo Foo.new(3).repr

Compatibility with Tuples?

Hello,
I planned to use this library with Nimgame, as it quite helps me in understanding my own code, as I come from Java. Sadly, I found that Nimgame (and reasonably so) uses tuples. My quick question is, can a class from this library extend a tuple?
Thank you, -Knedlik

Can this library work with parrallel?

Hi,

I'm a nim newbie and I was just wondering if you might be able to help me getting this working with the spawn command in parallel execution?

class FunctionApp:

    var faName: string
    var faResgroup: string
    var newDockerTag: string

    method init() =
        echo "Creating FA Class"

    method routineSwitchFaTag(): string =
        return fmt("hallo! {this.faName}, {this.faResgroup}, {this.newdockertag}")

launching it via:

proc faLauncher(faName, faResgroup, newdockertag: string): string =
    let fa = FunctionApp(faName: faName,
            faResgroup: faResgroup,
            newDockerTag: newDockerTag).init()
    let resultx = fa.routineSwitchFaTag()
    return resultx

proc parrallelController(argTup: tuple[faNameList: seq[string], faName,
        faResgroup, newdockertag: string]) =
    echo "running parallel baby!"

    var responses = newSeq[FlowVar[string]]()
    #var result = newSeq[string]()


    # let fA = FunctionApp(faName: faNamel, faResgroup: faResgroup,
    #     newDockerTag: newDockerTag).init()
    parallel:
        for faName in argTup.faNameList:
            # var fa = FunctionApp(faName: faName,
            #         faResgroup: argTup.faResgroup,
            #         newDockerTag: newDockerTag).init()

            var myResult = spawn faLauncher(faName, argTup.faResgroup,
                    argTup.newdockertag)
            responses.add(myResult)
    #responses.add(spawn FunctionApp(faName: faNamel, faResgroup: faResgroup, newDockerTag: newDockerTag).init())
    for k in 0..responses.high:
        echo k

#############
## main
let argHolder = (faNameList, faName, faResgroup,
        newdockertag)
parrallelController(argHolder)

best,
gir

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.