#!/usr/bin/env nomsu -V6.15.13.8
#
    A library for simple object oriented programming.

use "core/metaprogramming"
use "core/operators"
use "core/control_flow"
use "core/collections"
use "core/errors"

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

test:
    (an Empty) is (a thing) with []
    (a Buffer) is (a thing) with [$bits]:
        $self.is_a_buffer = (yes)
        ($self, set up) means:
            $bits or= []
        ($self, as text) means ($bits, joined)
        [($self, add $bit), ($self, append $bit)] all mean: $bits, add $bit
    assume $(a Buffer).is_a_buffer
    $b = (a Buffer)
    assume (type of $b) == "a Buffer"
    assume ($b is "a Buffer")
    assume $b.is_a_buffer
    assume "\$b" == ""
    assume ($b, as text) == ""
    $b = (a Buffer {.bits = ["x"]})
    $b, 
        add "y"
        append "z"
    assume "\$b" == "xyz"
    assume $b == (a Buffer {.bits = ["x", "y", "z"]})
    assume $b != (a Buffer {.bits = []})
    (a Comma Buffer) is (a Buffer) with [$bits]:
        ($self, as text) means ($bits, joined with ",")
        ($self, number of commas) means ((#$bits) - 1)
    $csv = (a Comma Buffer)
    assume $csv.is_a_buffer
    assume "\$csv" == ""
    $csv, add "x"
    $csv, add "y"
    assume "\$csv" == "x,y"
    assume ($csv, number of commas) == 1
    (a Vec) is (a thing) with [$x, $y]:
        ($self, + $other) means (Vec ($x + $other.x) ($y + $other.y))
        ($self, length) means (sqrt ($x * $x + $y * $y))
    (Vec $x $y) means (a Vec {.x = $x, .y = $y})
    assume ((Vec 1 2) + (Vec 10 10)) == (Vec 11 12)
    assume (((Vec 1 2) + (Vec 10 10)) != (Vec 0 0))
    assume (Vec 3 4, length) == 5

$METAMETHOD_MAP = {
    ."as text" = "__tostring", ."clean up" = "__gc", ."+" = "__add", ."-" = "__sub"
    ."*" = "__mul", ."/" = "__div", .negative = "__unm", ."//" = "__idiv"
    .mod = "__mod", ."^" = "__pow", ."&" = "__band", ."|" = "__bor", ."~" = "__bxor"
    ."~" = "__bnot", ."<<" = "__bshl", .">>" = "__bshr", ."==" = "__eq"
    ."<" = "__lt", ."<=" = "__le", ."set 1 =" = "__newindex"
    .size = "__len", .iterate = "__ipairs", ."iterate all" = "__pairs"
}

$($ as text like a dict) = ({}'s metatable).__tostring
external:
    ($parent class named $classname $(initialize $)) means:
        $class = {.__type = $classname}
        $class.__index = $class
        $class.class = $class
        $class.__tostring = ($ -> "\($.__type) \($ as text like a dict)")
        $class.__eq = ({}'s metatable).__eq
        $class.__len = ({}'s metatable).__len
        
        set $class's metatable to {
            .__index = $parent, .__tostring = ($class -> $class.__type)
            .__call =
                for ($class with $initial_values):
                    $initial_values or= {}
                    set $initial_values's metatable to $class
                    if $initial_values.set_up:
                        $initial_values, set up
                    return $initial_values
        }
        
        if $(initialize $):
            initialize $class
            for $stub = $metamethod in $METAMETHOD_MAP:
                if $class.($stub, as lua id):
                    $class.$metamethod = $class.($stub, as lua id)
        
        return $class

    $(a thing) = ((nil) class named "thing")

    ($classname is $parent with $vars $class_body) compiles to:
        unless ($vars.type == "List"):
            at $vars fail ("
                Compile error: This is not a list of variables.
            ")
        $class_id = ($classname.stub, as lua id)
        if $class_body:
            $class_body =
                $class_body with vars {
                    : for $v in $vars:
                        add ($v as lua expr, text) =
                            "IndexChain" tree with ("Var" tree with "self")
                                "Index" tree with ("Text" tree with $v.1)
                }
        $lua =
            Lua ("
                \$class_id = _1_class_named(\($parent as lua), \(quote $classname.stub)\(
                    (
                        Lua ("
                            , function(\$class_id)
                                local self = \$class_id
                                \($class_body as lua)
                            end
                        ")
                    ) if $class_body else ""
                ))
            ")
        $lua, add free vars [$class_id]
        return $lua