example/objects_and_types.primi

Summary

Maintainability
Test Coverage
import std.types: func
import ._helpers: assert_error

//
// Some sanity checks.
//

_ = "Type of type 'type' should be 'type'"
assert(type(type) == type, _)
_ = "Result of bool(0) should be false"
assert(bool(0) == false, _)
_ = "Result of bool(1) should be true"
assert(bool(1) == true, _)

//
// Creating custom classes/types with "class" keyword.
//

// Create base class "Greeting" (will extend class "object" by default).

class Greeting {

    attr_a = 'Some attribute A from class Greeting'
    attr_b = 'Some attribute B from class Greeting'
    variants = {"a": "Hey", "b": "Hello"}

    function get_greeting(self, name, variant) {
        return f"{self.variants[variant]} {name}!"
    }

}

//
// Creating custom classes/types that extend an existing class/type.
//

// Create class "Ahojky" which extends class "Greeting".

class Ahojky(Greeting) {

    attr_b = 'Overridden attribute B by class Ahojky'
    attr_c = 'Some attribute C from class Ahojky'

    class InnerAhojky {
        sub_attr_a = string
        sub_attr_b = Greeting
    }

    function get_greeting(self, name) {
        return f"Ahojky {name}!"
    }

}

//
// Test behavior and properties of class "Greeting" and its instance.
//

_ = "Type of type 'Greeting' should be 'type'"
assert(type(Greeting) == type, _)
greeting = Greeting()
assert(type(greeting) == Greeting)

assert(Greeting.attr_a == 'Some attribute A from class Greeting')
assert(Greeting.attr_b == 'Some attribute B from class Greeting')

assert(greeting.attr_a == 'Some attribute A from class Greeting')
assert(greeting.attr_b == 'Some attribute B from class Greeting')

assert(greeting.variants.get('a') == 'Hey')
assert(greeting.variants.get('b') == 'Hello')

assert(greeting.get_greeting('Jeremiáš', 'a') == 'Hey Jeremiáš!')
assert(greeting.get_greeting('Alžběta', 'b') == 'Hello Alžběta!')

//
// Test behavior and properties of class "Ahojky" and its instance.
//

_ = "Type of type 'Ahojky' should be 'type'"
assert(type(Ahojky) == type, _)
ahojky = Ahojky()
assert(type(ahojky) == Ahojky)

assert(Ahojky.attr_a == 'Some attribute A from class Greeting')
assert(Ahojky.attr_b == 'Overridden attribute B by class Ahojky')
assert(Ahojky.attr_c == 'Some attribute C from class Ahojky')

assert(ahojky.attr_a == 'Some attribute A from class Greeting')
assert(ahojky.attr_b == 'Overridden attribute B by class Ahojky')
assert(ahojky.attr_c == 'Some attribute C from class Ahojky')

assert(ahojky.variants.get('a') == 'Hey')
assert(ahojky.variants.get('b') == 'Hello')

assert(ahojky.get_greeting('Padishah Emperor') == 'Ahojky Padishah Emperor!')

//
// Using type() to create new classes/types.
//

custom_type = type('CustomType', object, {
    'some_attr_x': 'no value',
    '__init__': (self, arg) => {
        print(f'self: {self}, arg: {arg}')
        print(f'setting self.some_attr_x to "{arg}"')
        self.some_attr_x = arg
    },
    'try_me': (self, what) => {
        print(f'self: {self}, what: {what}, some_attr_x: {self.some_attr_x}')
    },
})

assert(type(custom_type) == type)
assert(custom_type.some_attr_x == 'no value')

instance = custom_type('init arg')
assert(type(instance) == custom_type)
assert(custom_type.some_attr_x == 'no value')
assert(instance.some_attr_x == 'init arg')
instance.try_me('anything')

//
// Dict specifying attrs of the new type must have valid variable names
// as keys.
//

assert_error(() => {
    custom_type = type('CustomType', object, {
        '123': 'this is invalid variable name'
    })
})

assert_error(() => {
    custom_type = type('CustomType', object, {
        123: 'this also is invalid variable name'
    })
})

assert_error(() => {
    custom_type = type('CustomType', object, {
        ('lol', 'a', 'tuple'): 'this is completely invalid variable name'
    })
})

//
// Attr access.
//

_ = "Getting non-existent attr from object throws error"
assert_error(() => {
    print(instance.bogus_attr)
}, _)

_ = "Setting some attr under a non-existent attr throws error"
assert_error(() => {
    instance.bogus_attr_a.bogus_attr_b = 1
}, _)

//
// Deeply nested objects.
//

class Struct {

    function __init__(self, a, b = null) {
        print(f"Initing with a:{a} and b:{b}")
        self.substruct = b
    }

    function get_tree(self, message = "NOTHING") {

        tree = [self]
        new = self

        print(f"Getting tree with message: '{message}'")

        while (hasattr(new, 'substruct') and new.substruct) {
            tree.push(new.substruct)
            new = new.substruct
        }

        return tree

    }

}

o1 = Struct('aaa')
o2 = Struct('bbb', o1)
o3 = Struct('ccc', o2)
o4 = Struct('ddd', o3)

print(o4.get_tree())
print(o4.get_tree("SOMETHING"))

o4.substruct.substruct.substruct = "hey!"
print(o4.get_tree("SOMETHING"))