# generates various Prog8 files with a large amount of number equality tests,
# using various forms of the if statement (because these forms have their own code gen paths)

import sys

fail_index = 0


def header(dt):
    print(f"""
%import textio
%import floats
%import test_stack
%zeropage dontuse
%option no_sysinit

main {{
    ubyte success = 0
    str datatype = "{dt}"
    uword @shared comparison

    sub start() {{
        txt.print("\\n(in)equality tests for datatype: ")
        txt.print(datatype)
        txt.nl()
        test_stack.test()
        txt.print("==0: ")
        test_is_zero()
        txt.print("\\n!=0: ")
        test_not_zero()
        txt.print("\\n==number: ")
        test_is_number()
        txt.print("\\n!=number: ")
        test_not_number()
        txt.print("\\n==var: ")
        test_is_var()
        txt.print("\\n!=var: ")
        test_not_var()
        txt.print("\\n==array[]: ")
        test_is_array()
        txt.print("\\n!=array[]: ")
        test_not_array()
        txt.print("\\n==expr: ")
        test_is_expr()
        txt.print("\\n!=expr: ")
        test_not_expr()
        test_stack.test()
    }}
    
    sub verify_success(ubyte expected) {{
        if success==expected {{
            txt.print("ok")
        }} else {{
            txt.print(" **failed** ")
            txt.print_ub(success)
            txt.print(" success, expected ")
            txt.print_ub(expected)
        }}
    }}
    
    sub fail_byte(uword idx, byte v1) {{
        txt.print(" **fail#")
        txt.print_uw(idx)
        txt.chrout(':')
        txt.print_b(v1)
        txt.print(" **")
    }}

    sub fail_ubyte(uword idx, ubyte v1) {{
        txt.print(" **fail#")
        txt.print_uw(idx)
        txt.chrout(':')
        txt.print_ub(v1)
        txt.print(" **")
    }}
    
    sub fail_word(uword idx, word v1) {{
        txt.print(" **fail#")
        txt.print_uw(idx)
        txt.chrout(':')
        txt.print_w(v1)
        txt.print(" **")
    }}

    sub fail_uword(uword idx, uword v1) {{
        txt.print(" **fail#")
        txt.print_uw(idx)
        txt.chrout(':')
        txt.print_uw(v1)
        txt.print(" **")
    }}
    
    sub fail_float(uword idx, float v1) {{
        txt.print(" **fail#")
        txt.print_uw(idx)
        txt.chrout(':')
        floats.print(v1)
        txt.print(" **")
    }}    

""")


zero_values = {
    "byte": 0,
    "ubyte": 0,
    "word": 0,
    "uword": 0,
    "float": 0.0
}

nonzero_values = {
    "byte": -100,
    "ubyte": 100,
    "word": -9999,
    "uword": 9999,
    "float": 1234.56
}


def make_test_is_zero(datatype):
    print(f"""
    sub test_is_zero() {{
        {datatype} @shared x
        success = 0

        x={zero_values[datatype]}
        ; direct jump
        if x==0
            goto lbl1
        goto skip1
lbl1:   success++
skip1:
        ; indirect jump
        cx16.r3 = &lbl2
        if x==0
            goto cx16.r3
        goto skip2
lbl2:   success++
skip2:
        ; no else
        if x==0
            success++

        ; with else
        if x==0
            success++
        else
            cx16.r0L++     
            
        x = {nonzero_values[datatype]}
        ; direct jump
        if x==0
            goto skip3
        success++
skip3:
        ; indirect jump
        cx16.r3 = &skip4
        if x==0
            goto cx16.r3
        success++
skip4:
        ; no else
        success++
        if x==0
            success--

        ; with else
        if x==0
            cx16.r0L++                      
        else
            success++

        verify_success(8)
    }}
""")


def make_test_not_zero(datatype):
    print(f"""
    sub test_not_zero() {{
        {datatype} @shared x
        success = 0

        x={nonzero_values[datatype]}
        ; direct jump
        if x!=0
            goto lbl1
        goto skip1
lbl1:   success++
skip1:
        ; indirect jump
        cx16.r3 = &lbl2
        if x!=0
            goto cx16.r3
        goto skip2
lbl2:   success++
skip2:
        ; no else
        if x!=0
            success++

        ; with else
        if x!=0
            success++
        else
            cx16.r0L++     
            
        x = {zero_values[datatype]}
        ; direct jump
        if x!=0
            goto skip3
        success++
skip3:
        ; indirect jump
        cx16.r3 = &skip4
        if x!=0
            goto cx16.r3
        success++
skip4:
        ; no else
        success++
        if x!=0
            success--

        ; with else
        if x!=0
            cx16.r0L++                      
        else
            success++

        verify_success(8)
    }}
""")


def tc(value):
    if value < 0x8000:
        return value
    else:
        return -(65536 - value)


testnumbers = {
    "byte": [-100, 0, 100],
    "ubyte": [0, 1, 255],
    "word": [tc(0xaabb), 0, 0x00aa, 0x7700, 0x7fff],
    "uword": [0, 1, 0x7700, 0xffff],
    "float": [0.0, 1234.56]
}


def make_test_is_number(datatype, equals):
    numbers = testnumbers[datatype]
    print("    sub test_is_number() {" if equals else "    sub test_not_number() {")
    print(f"""    {datatype} @shared x
        success = 0""")
    expected = 0
    test_index = 0
    global fail_index
    for x in numbers:
        print(f"    x={x}")
        for value in numbers:
            if value == 0:
                continue  # 0 already tested separately
            result = (x == value) if equals else (x != value)
            comp = "==" if equals else "!="
            test_index += 1
            if result:
                expected += 4  # there are 4 test types for every successful combo
                true_action1 = "success++"
                true_action2 = "success++"
                true_action3 = "success++"
                true_action4 = "success++"
            else:
                fail_index += 1
                true_action1 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action2 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action3 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action4 = f"fail_{datatype}({fail_index},{x})"
            print(f"""    ; direct jump
        if x{comp}{value}
            goto lbl{test_index}a
        goto skip{test_index}a
lbl{test_index}a:   {true_action1}
skip{test_index}a:
        ; indirect jump
        cx16.r3 = &lbl{test_index}b
        if x{comp}{value}
            goto cx16.r3
        goto skip{test_index}b
lbl{test_index}b:   {true_action2}
skip{test_index}b:
        ; no else
        if x{comp}{value}
            {true_action3}

        ; with else
        if x{comp}{value}
            {true_action4}
        else
            cx16.r0L++
""")
    print(f"    verify_success({expected})\n}}")


def make_test_is_var(datatype, equals):
    numbers = testnumbers[datatype]
    print("    sub test_is_var() {" if equals else "    sub test_not_var() {")
    print(f"""    {datatype} @shared x, value
        success = 0""")
    expected = 0
    test_index = 0
    global fail_index
    for x in numbers:
        print(f"    x={x}")
        for value in numbers:
            if value == 0:
                continue  # 0 already tested separately
            print(f"    value={value}")
            result = (x == value) if equals else (x != value)
            comp = "==" if equals else "!="
            test_index += 1
            if result:
                expected += 4  # there are 4 test types for every successful combo
                true_action1 = "success++"
                true_action2 = "success++"
                true_action3 = "success++"
                true_action4 = "success++"
            else:
                fail_index += 1
                true_action1 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action2 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action3 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action4 = f"fail_{datatype}({fail_index},{x})"
            print(f"""    ; direct jump
        if x{comp}value
            goto lbl{test_index}a
        goto skip{test_index}a
lbl{test_index}a:   {true_action1}
skip{test_index}a:
        ; indirect jump
        cx16.r3 = &lbl{test_index}b
        if x{comp}value
            goto cx16.r3
        goto skip{test_index}b
lbl{test_index}b:   {true_action2}
skip{test_index}b:
        ; no else
        if x{comp}value
            {true_action3}

        ; with else
        if x{comp}value
            {true_action4}
        else
            cx16.r0L++
""")
    print(f"    verify_success({expected})\n}}")


def make_test_is_array(datatype, equals):
    numbers = testnumbers[datatype]
    print("    sub test_is_array() {" if equals else "    sub test_not_array() {")
    print(f"""    {datatype} @shared x
        {datatype}[] values = [0, 0]
        {datatype}[] sources = [0, 0]
        success = 0""")
    expected = 0
    test_index = 0
    global fail_index
    for x in numbers:
        print(f"    x={x}")
        print(f"    sources[1]={x}")
        for value in numbers:
            if value == 0:
                continue  # 0 already tested separately
            print(f"    values[1]={value}")
            result = (x == value) if equals else (x != value)
            comp = "==" if equals else "!="
            test_index += 1
            if result:
                expected += 8  # there are 8 test types for every successful combo
                true_action1 = "success++"
                true_action2 = "success++"
                true_action3 = "success++"
                true_action4 = "success++"
                true_action5 = "success++"
                true_action6 = "success++"
                true_action7 = "success++"
                true_action8 = "success++"
            else:
                fail_index += 1
                true_action1 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action2 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action3 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action4 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action5 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action6 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action7 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action8 = f"fail_{datatype}({fail_index},{x})"
            print(f"""    ; direct jump
        if x{comp}values[1]
            goto lbl{test_index}a
        goto skip{test_index}a
lbl{test_index}a:   {true_action1}
skip{test_index}a:
        ; indirect jump
        cx16.r3 = &lbl{test_index}b
        if x{comp}values[1]
            goto cx16.r3
        goto skip{test_index}b
lbl{test_index}b:   {true_action2}
skip{test_index}b:
        ; no else
        if x{comp}values[1]
            {true_action3}

        ; with else
        if x{comp}values[1]
            {true_action4}
        else
            cx16.r0L++
""")
            print(f"""    ; direct jump
        if sources[1]{comp}values[1]
            goto lbl{test_index}c
        goto skip{test_index}c
lbl{test_index}c:   {true_action5}
skip{test_index}c:
        ; indirect jump
        cx16.r3 = &lbl{test_index}d
        if sources[1]{comp}values[1]
            goto cx16.r3
        goto skip{test_index}d
lbl{test_index}d:   {true_action6}
skip{test_index}d:
        ; no else
        if sources[1]{comp}values[1]
            {true_action7}

        ; with else
        if sources[1]{comp}values[1]
            {true_action8}
        else
            cx16.r0L++
""")
    print(f"    verify_success({expected})\n}}")


def make_test_is_expr(datatype, equals):
    numbers = testnumbers[datatype]
    print("    sub test_is_expr() {" if equals else "    sub test_not_expr() {")
    print(f"""    {datatype} @shared x
        cx16.r4 = 1
        cx16.r5 = 1
        float @shared f4 = 1.0
        float @shared f5 = 1.0
        success = 0""")
    expected = 0
    test_index = 0
    global fail_index
    for x in numbers:
        print(f"    x={x}")
        for value in numbers:
            if value == 0:
                continue  # 0 already tested separately
            if datatype=="byte":
                expr = f"cx16.r4sL+{value}-cx16.r5sL"
            elif datatype=="ubyte":
                expr = f"cx16.r4L+{value}-cx16.r5L"
            elif datatype=="word":
                expr = f"cx16.r4s+{value}-cx16.r5s"
            elif datatype=="uword":
                expr = f"cx16.r4+{value}-cx16.r5"
            elif datatype=="float":
                expr = f"f4+{value}-f5"
            result = (x == value) if equals else (x != value)
            comp = "==" if equals else "!="
            test_index += 1
            if result:
                expected += 4  # there are 4 test types for every successful combo
                true_action1 = "success++"
                true_action2 = "success++"
                true_action3 = "success++"
                true_action4 = "success++"
            else:
                fail_index += 1
                true_action1 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action2 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action3 = f"fail_{datatype}({fail_index},{x})"
                fail_index += 1
                true_action4 = f"fail_{datatype}({fail_index},{x})"
            print(f"""    ; direct jump
        if x{comp}{expr}
            goto lbl{test_index}a
        goto skip{test_index}a
lbl{test_index}a:   {true_action1}
skip{test_index}a:
        ; indirect jump
        cx16.r3 = &lbl{test_index}b
        if x{comp}{expr}
            goto cx16.r3
        goto skip{test_index}b
lbl{test_index}b:   {true_action2}
skip{test_index}b:
        ; no else
        if x{comp}{expr}
            {true_action3}

        ; with else
        if x{comp}{expr}
            {true_action4}
        else
            cx16.r0L++
""")
    print(f"    verify_success({expected})\n}}")


def generate(datatype):
    global fail_index
    fail_index = 0
    header(datatype)
    make_test_is_zero(datatype)
    make_test_not_zero(datatype)
    make_test_is_number(datatype, True)
    make_test_is_number(datatype, False)
    make_test_is_var(datatype, True)
    make_test_is_var(datatype, False)
    make_test_is_expr(datatype, True)
    make_test_is_expr(datatype, False)
    make_test_is_array(datatype, True)
    make_test_is_array(datatype, False)
    print("\n}\n")


if __name__ == '__main__':
    for dt in ["ubyte", "uword", "byte", "word", "float"]:
        sys.stdout = open(f"test_{dt}_equalities.p8", "wt")
        generate(dt)
