#!/usr/bin/env qore
# -*- mode: qore; indent-tabs-mode: nil -*-

%new-style
%enable-all-warnings
%require-types
%strict-args
%no-child-restrictions

%requires ../../../../qlib/QUnit.qm

%exec-class BreakAndContinueTest

class  BreakAndContinueTest inherits QUnit::Test {
    constructor() : QUnit::Test("break and continue test", "1.0") {
        addTestCase("continue in switch", \continueInSwitch());
        addTestCase("break allowed", \breakAllowed());
        addTestCase("break not allowed", \breakNotAllowed());
        addTestCase("continue allowed", \continueAllowed());
        addTestCase("continue not allowed", \continueNotAllowed());
        addTestCase("legacy continue in switch", \legacyContinueInSwitch());
        addTestCase("legacy break outside of a loop", \legacyBreakAllowed());
        addTestCase("legacy continue outside of a loop", \legacyContinueAllowed());
        set_return_value(main());
    }

    continueInSwitch() {
        string s = "";
        for (int i = 0; i < 2; ++i) {
            s += i;
            switch (i) {
                case 0:
                    continue;
            }
            s += "x";
        }
        assertEq("01x", s);
    }

    breakAllowed() {
        switch(0) { case 0: break; }
        while (True) { break; }
        for (;;) { break; }
        summarize ({'a': (1)}) by (%a) { break; }
        do { break; } while (True);
        foreach int i in (0, 1) { if (i == 0) { break; } }
        context ({'a': (1)}) { break; }
    }

    breakNotAllowed() {
        {
            Program p();
            assertThrows("BREAK-NOT-ALLOWED", \p.parse(), ("sub f() { break; }", ""));
        }
        {
            Program p();
            assertThrows("BREAK-NOT-ALLOWED", \p.parse(), ("sub f() { do { sub() {break;}(); } while (False); }", ""));
        }
        {
            Program p();
            assertThrows("BREAK-NOT-ALLOWED", \p.parse(), ("sub f() { do { on_exit break; } while (False); }", ""));
        }
    }

    continueAllowed() {
        while (!(int i)) { i = 1; continue; }
        for (int i; !i; ++i) { continue; }
        summarize ({'a': (1)}) by (%a) { continue; }
        do { continue; } while (False);
        foreach int i in (0, 1) { if (i == 0) { continue; } }
        context ({'a': (1)}) { continue; }
    }

    continueNotAllowed() {
        {
            Program p();
            assertThrows("CONTINUE-NOT-ALLOWED", \p.parse(), ("sub f() { continue; }", ""));
            assertThrows("CONTINUE-NOT-ALLOWED", \p.parse(), ("sub f() { switch(0) {case 0: continue; }}", ""));
            assertThrows("CONTINUE-NOT-ALLOWED", \p.parse(), ("sub f() { do { sub() {continue;}(); } while (False); }", ""));
            assertThrows("CONTINUE-NOT-ALLOWED", \p.parse(), ("sub f() { do { on_exit continue; } while (False); }", ""));

            p.setParseOptions(PO_BROKEN_LOOP_STATEMENT);
            assertEq(NOTHING, p.parse("sub f_x() { continue; }", ""));
        }
        {
            Program p(PO_BROKEN_LOOP_STATEMENT);

            assertThrows("CONTINUE-NOT-ALLOWED", \p.parse(), ("%correct-loop-statement\nsub f() { continue; }", ""));
            p.setParseOptions(PO_BROKEN_LOOP_STATEMENT);
            assertThrows("CONTINUE-NOT-ALLOWED", \p.parse(), ("%correct-loop-statement\nsub f() { switch(0) {case 0: continue; }}", ""));
            p.setParseOptions(PO_BROKEN_LOOP_STATEMENT);
            assertThrows("CONTINUE-NOT-ALLOWED", \p.parse(), ("%correct-loop-statement\nsub f() { do { sub() {continue;}(); } while (False); }", ""));
            p.setParseOptions(PO_BROKEN_LOOP_STATEMENT);
            assertThrows("CONTINUE-NOT-ALLOWED", \p.parse(), ("%correct-loop-statement\nsub f() { do { on_exit continue; } while (False); }", ""));
        }
    }

    legacyContinueInSwitch() {
        Program p();
        p.parse('%new-style
%broken-loop-statement
string s = "";
for (int i = 0; i < 2; ++i) {
    s += i;
    switch (i) {
        case 0:
            continue;
    }
    s += "x";
}
return s;', "");
        assertEq("0x1x", p.run());
    }

    legacyBreakAllowed() {
        Program p();
        p.parse('%new-style
%broken-loop-statement
sub f(int i) {
    if (i) break;
    throw "NOT EXECUTED";
}
f(1);
', "");
        p.run();
    }

    legacyContinueAllowed() {
        Program p();
        p.parse('%new-style
%broken-loop-statement
sub f(int i) {
    if (i) continue;
    throw "NOT EXECUTED";
}
f(1);
', "");
        p.run();
    }
}
