/**
    Experimenting with C++-like operator overloading...
*/
scope {
    //const AP = api.prototypes.Array
    const AP = array[].prototype
    AP.'operator+' = proc{lhs,rhs}{
        assert this === lhs
        [print "operator+" this rhs]
        [this.push rhs]
        return this
    }

    AP.'operator-' = proc{lhs,rhs}{
        assert this === lhs
        var i = 0, max = 0+rhs
        [print "operator-" this max]
        if(max<0){ /* Pop from the end. */
            var len = [this.length] - -max;
            if(len<0) { len = 0 }
             // bug: broken: ((len>=0) ? len : 0) evals to boolean
            [this.length len]
            //[print 'this.length =' [this.length]]
        }
        else if(max>0){ /* remove from the front */
            while{i<max}{
                [this.shift]
                i += 1
            }
        }
        return this
    }

    AP.'operator/' = proc{lhs,rhs}{
        assert this === lhs
        [print "What should operator/ do for an array?"]
        return true
    }
    AP.'operator%' = proc{lhs,rhs}{
        assert this === lhs
        [print "What should operator% do for an array?"]
        return true
    }

    /**
        operator* is the only commutative operator for
        client-specified operators, meaning it is triggered
        whether 'this' is on the left or right of its
        operator. the (lhs,rhs) parameters will reflect their
        given order and 'this' will be set to the object
        who's operator was triggered. So 'this' will ===
        (at least) one of lhs or rhs.
    */
    AP.'operator*' = proc{lhs,rhs}{
        assert (this===lhs) || (this===rhs)
        assert 'array'===typename this
        if(this===rhs) {
            throw [api.string.concat
                    "Non-sensical operation: "
                    typename lhs
                    ' * '
                    typename rhs]
        }
        //$print 'op* args:' typename lhs typename rhs
        const other = (lhs===this) ? rhs : lhs
        assert other !== this
        [print "What should operator* do for an array?"]
        return true
    }


    AP
}

1 && scope {
    const a = array[]

    $print 'operator+ =' a.'operator+'
    assert 'function' === typename a.'operator+'
    a += "hi" // const var assignment to self is a no-op here
    //a + "hi" // same as a+="hi"
    a + "!!!"
    a + ":-D"
    //a + "again," + "and again," + "and again!" // evals all +'s first
    //(a + "again,") + "and again," + "and again!" // but this works
    a + "again," + "and again," + "and again!" // and this works
    $print 'a =' a
    a - -1
    $print 'a =' a
    a - 1
    $print 'a =' a
    assert true === a * 3
    assert 0 === 2 * a // NON-commutative!
    assert true === a % 1
    assert true === a / 7
    assert 2 === 2 - a // NON-commutative!
    $print 'a =' a

}

1 && scope {

    var x = array[]
    x.'operator<<' = x.'operator+'
    x.'operator>>' = x.'operator-'

    x + "foo" << "bar" << "baz"
    assert 3 === [x.length]
    assert "foo" === x.0
    assert "baz" === x.2
    x >> 1
    assert 2 === [x.length]
    assert 'bar' === x.0
    x >> 2
    assert ![x.length]
}
