
scope {
    // make sure we can chain calls...
    const P = proc f(){return f};
    P.x = P;
    assert P === P('from P()');
    assert P === P.x('from P.x()');
    assert P === P.x['x']('from P.x["x"]()');

    // Demonstrate skip-mode's effect on function calls:
    false && P.x.y.z(foo);
    true || P.x.y.z(foo);

    assert P === (P.x)('from (P.x)()');

    var o = {
        p: P
    };
    assert P === o.p('from o.p()');

    if(s2.isCallable){
        assert s2.isCallable(s2.isCallable, false);
        assert s2.isCallable(s2.isCallable, true);
        assert s2.isCallable(s2.isCallable);
        o = {
            prototype: proc(){
                ++this.value;
                this.args = argv;
                return argv;
            },
            value: 0
        };
        assert 0 === o.value;
        assert undefined === o.args;
        assert o(1,2,3) === o.args;
        assert 1 === o.value;
        assert 3 === o.args.2;
        assert s2.isCallable(o) /* searches up through prototypes */;
        assert !s2.isCallable(o,false) /* does not search prototypes */;
        assert s2.isCallable(o,true) /* searches up through prototypes */;
    }
}

scope { // exploring "callable" objects...
    var obj = {
        foo: proc(){
            assert this === obj;
        }
    }.eachProperty(proc(k,v){
        //print(__FLC,'eachprop',k);
        v.importSymbols(nameof this);
    });

    // let's change up "this" a bit...
    var f = obj.foo;
    obj.foo();
    f();
    obj.bar = proc(name){
        //print(name,this);
        return this === eval -> name;
    };
    assert obj.bar(nameof obj);
    var x =obj.bar;
    assert x(nameof x);
}

scope {
    /* Prior to 20181015, completely empty functions were optimized
       away at call()-time. This led to side-effects in their
       parameter/argument lists not happening, e.g.: proc(){}(assert 0) did not
       fail. Make sure that's no longer the case...

       We now optimize away the call() part but still process the
       argument list, if any, so that side-effects can trigger.
    */
    var f = proc(){}, x = 3, y;
    f(y=x);
    assert x === y /* side-effects in argument list trigger */;
    assert (catch proc(){}(affirm 0)).message.indexOf('Affirmation') >= 0 /* affirmation must trigger */;

    /* Let's test some more complicated cases involving default
       parameter values which use call()-time imported symbols... */
    y = 0;
    f = proc callee(a=(y=Z), b=(z=callee.foo)){} using{Z:2};
    f.foo = 7;
    assert 0 === y;
    var z;
    f();
    assert 2 === y /* side effect of default param value using imported symbol */;
    assert 7 === z /* side effect of default param value using imported symbol */;
    y = 0;
    f.foo = 8;
    f(1);
    assert 0 === y /* skipped side effect of default param value */;
    assert 8 === z /* side effect of default param value using imported symbol */;
    z = 0;
    f(1,2);
    assert 0 === z /* skipped side effect of default param value */;

    z = 0;
    f = proc(a=(z=this.foo)){};
    f.foo = 7;
    f();
    assert 7 === z /* 'this' was set up before default parameter values were processed */;
}

scope {
    // Check for handling of errant continue/break...
    // Prior to 20181104 continue/break in call param lists
    // were handled incorrectly.

    const ar = [], f=proc(){};
    foreach(@[1,2,3]=>v) f(ar[] = v%2 ? v : continue);
    assert 2 === ar.#;
    assert 1===ar.0;
    assert 3===ar.1;

    ar.length(0);
    foreach(@[1,2,3]=>v) f(ar[] = v%2 ||| break);
    assert 1 === ar.#;
    assert 1===ar.0;

    var ex = catch foreach(@ar=>v) proc(){continue}();
    assert typeinfo(isexception ex);
    assert 0===ex.message.indexOf("Unhandled 'continue'");

    ex = catch foreach(@ar=>v) proc(){break}();
    assert typeinfo(isexception ex);
    assert 0===ex.message.indexOf("Unhandled 'break'");
}

scope {
    /**
       20190820: confirm fix of @-expansion bug when a newline preceeds
       the @expr in a function call. */
    const f = proc(){}, args = [1,2,3];
    f(1,2,3,
      @args);
    // ^^^ previously, that would fail with "'@' is not allowed here
}

scope {
    /**
       2020-02-18: var keyword now supports the := op to assign as
       const, but it is explicitly disabled for function parameters
       (which are handled by the same C-level code
       (s2_keyword_f_var_impl())) because of inconsistencies in
       parameters with and without default values. Namely, we can
       easily make the argument to proc(a:=2){...} const whether or
       not an argument is passed to the function call but we have no
       syntax to saying that it sould be const unless it has a default
       value. e.g. we could not make that same parameter const in
       proc(a){...}.
    */

    var f = proc(a:=1){} /* params are not parsed yet, so won't fail
                            here. */;
    var ex = catch f();
    assert 'CWAL_SCR_SYNTAX' === ex.codeString();
    assert ex.message.indexOf(':=') > 0;
}
