#!/usr/bin/env python3
import os
import sys
import inspect

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.dirname(SCRIPT_DIR))

from stringreader import StringReader
import lex
import parse

def testSyntaxError(src, msg):
    caller = inspect.getframeinfo(inspect.stack()[1][0])
    loc = "{}(line {})".format(caller.filename, caller.lineno)
    parser = parse.Parser(lex.Lexer(StringReader(src, "")))
    nodes = []
    try:
        parser.parseInto(nodes)
        sys.exit("{}: expected the following code to fail with '{}' but it didn't:\n{}".format(loc, msg, src))
    except lex.SyntaxError as err:
        if str(err) != msg:
            sys.exit("{}: the following code did not fail with the expected message:\n--- CODE ---\n{}\n--------------\nExpected: {}\nActual  : {}\n".format(loc, src, msg, str(err)))

def testParse(src):
    caller = inspect.getframeinfo(inspect.stack()[1][0])
    parser = parse.Parser(lex.Lexer(StringReader(src, "{}(line {})".format(caller.filename, caller.lineno))))
    nodes = []
    parser.parseInto(nodes)
    
def main():
    testParse("void a = 0;")
    testParse("void a = 90;")
    testParse("void a = 0x0;")
    testParse("void a = 0x09afAF;")
    testSyntaxError("@!", "line 1 col 2: expected an ID char after '@' but got '!' (ascii code 33)")
    testParse("void _a = 0;")

    testSyntaxError("typedef", "line 1 col 8: expected type to start with ID but got: EOF")
    testSyntaxError("typedef int", "line 1 col 12: expected ID after 'typedef TYPE' but got EOF")
    testSyntaxError("typedef int a", "line 1 col 14: expected ; to finish typedef but got EOF")
    testParse("typedef int a;")
    testParse("typedef int* a;")
    testSyntaxError("typedef int[", "line 1 col 13: expected '*' or NUMBER after '[' but got EOF")
    testSyntaxError("typedef int[*", "line 1 col 14: expected ] to finish array pointer '[*' type but got EOF")
    testSyntaxError("typedef int[100", "line 1 col 16: expected ] to finish static array type but got EOF")
    testParse("typedef int[*] a;")
    testParse("typedef int[100] a;")

    testParse("typedef const int a;")
    testParse("typedef const int* a;")
    testParse("typedef const int[*] a;")
    testSyntaxError("typedef const int[100] a;", "line 1 col 9: static array types cannot be const")

    testSyntaxError("struct", "line 1 col 7: expected ID after 'struct' but got EOF")
    testSyntaxError("struct a", "line 1 col 9: expected { after 'struct ID' but got EOF")
    testSyntaxError("struct a{", "line 1 col 10: expected a struct field but got EOF")
    testParse("struct a{}")
    testSyntaxError("struct a{int}", "line 1 col 13: expected ID after field type but got }")
    testSyntaxError("struct a{int x}", "line 1 col 15: expected ; to finish field declaration but got }")
    testParse("struct a{int x;}")
    testParse("struct a{int x;int y;}")
    testParse("struct a{int x;int y;const int[*] arr;}")
    testParse("struct a{int x;int y;const int[*] arr;const int cx;}")

    testSyntaxError("int", "line 1 col 4: expected ID after 'TYPE' (could be function or const) but got EOF")
    testSyntaxError("int a", "line 1 col 1: expected '=' or '(' after 'TYPE ID' but got EOF")
    testSyntaxError("int a =", "line 1 col 8: expected a constant value but got EOF")
    testSyntaxError("int a = 0", "line 1 col 10: expected ; to finish const but got EOF")
    testParse("int a = 0;")
    testParse("int* a = 0;")
    testParse("int[*] a = 0;")
    testParse("const int a = 0;")
    testParse("const int* a = 0;")
    testParse("const int[*] a = 0;")

    testSyntaxError("int a(", "line 1 col 7: expected a function argument or ) but got EOF")
    testSyntaxError("int a()", "line 1 col 8: expected ; to finish function declaration but got EOF")
    testParse("int a();")
    testSyntaxError("int a(,);", "line 1 col 7: expected type to start with ID but got: ,")
    testSyntaxError("int a(int", "line 1 col 10: expected ID after argument type but got EOF")
    testSyntaxError("int a(int b", "line 1 col 12: expected , or ) to finish function arguments but got EOF")
    testSyntaxError("int a(int b,", "line 1 col 13: expected a function argument or ) but got EOF")
    testSyntaxError("int a(int b)", "line 1 col 13: expected ; to finish function declaration but got EOF")
    testSyntaxError("int a(int b,)", "line 1 col 14: expected ; to finish function declaration but got EOF")
    testParse("int a(int b);")
    testParse("int a(int b,);")
    testParse("int a(int b, int c);")
    testParse("int a(int b, int c,);")
    testParse("int* a(int b, int c,);")
    testParse("int[*] a(int b, int c,);")
    testParse("const int a(int b, int c,);")
    testParse("const int* a(int b, int c,);")
    testParse("const int[*] a(int b, int c,);")

    testSyntaxError("typedef funcptr", "line 1 col 16: expected type to start with ID but got: EOF")
    testSyntaxError("typedef funcptr void", "line 1 col 21: expected ( to delimit funcptr args but got EOF")
    testSyntaxError("typedef funcptr void(", "line 1 col 22: expected a function argument or ) but got EOF")
    testSyntaxError("typedef funcptr void()", "line 1 col 23: expected ID after 'typedef TYPE' but got EOF")
    testSyntaxError("typedef funcptr void() a", "line 1 col 25: expected ; to finish typedef but got EOF")
    testParse("typedef funcptr void() a;")
    testSyntaxError("typedef funcptr void(int", "line 1 col 25: expected ID after argument type but got EOF")
    testSyntaxError("typedef funcptr void(int a", "line 1 col 27: expected , or ) to finish function arguments but got EOF")
    testSyntaxError("typedef funcptr void(int a)", "line 1 col 28: expected ID after 'typedef TYPE' but got EOF")
    testSyntaxError("typedef funcptr void(int a) a", "line 1 col 30: expected ; to finish typedef but got EOF")
    testParse("typedef funcptr void(int a) a;")

    testSyntaxError("@unicode", "line 1 col 9: expected ID after @unicode but got EOF")
    testSyntaxError("@unicode a", "line 1 col 11: expected ; after @unicode ID but got EOF")
    testParse("@unicode a;")

    testSyntaxError("@include", "line 1 col 9: expected STRING after @include but got EOF")
    testSyntaxError("@include \"what\"", "line 1 col 10: @include filenames must end with '.h' but got 'what'")
    testParse("@include \"what.h\"")

    print("Success")

main()
