import unittest, sys
from simpleparse.parser import Parser
from simpleparse.stt.TextTools import TextTools
from .genericvalues import NullResult, AnyInt
from . import test_grammarparser
from . import test_erroronfail
        
class ParserGenerationTests(unittest.TestCase):
    def doBasicTest(self, definition, parserName, testValue, expected, ):
        result = Parser( definition).parse( testValue, parserName )
        assert result == expected, '''\nexpected:%s\n     got:%s\n'''%( expected, result )
    def testGenNegRange1( self ):
        self.doBasicTest(
            '''s := - something *
            <something> := [ab]''',
            's',
            'mmmab',
            (1,[],3)
        )
    def testGenNegRange2( self ):
        self.doBasicTest(
            '''s := - something
            <something> := [ab]''',
            's',
            'mmmab',
            (1,[],1)
        )
    def testGenNegLit1( self ):
        self.doBasicTest(
            '''s := - something *
            <something> := "a"''',
            's',
            'mmmab',
            (1,[],3)
        )
    def testGenPosReptOpt1( self ):
        self.doBasicTest(
            '''s := something *
            something := "a" ''',
            's',
            'aammmab',
            (1,[("something",0,1,NullResult),("something",1,2,NullResult)],2)
        )
    def testGenPosReptOpt2( self ):
        self.doBasicTest(
            '''s := something *
            something := "a" ''',
            's',
            'mmmab',
            (1,[],0)
        )
    def testGenPosRept1( self ):
        self.doBasicTest(
            '''s := something +
            something := "a" ''',
            's',
            'mmmab',
            (0,[],AnyInt)
        )

    def testLookaheadPositive( self ):
        self.doBasicTest(
            '''s := ?"b"
            ''',
            's',
            'bbbba',
            (1,[
            ],0)
        )
    def testLookaheadNeg( self ):
        self.doBasicTest(
            '''s := ?-"b"
            ''',
            's',
            'bbbba',
            (0,[
            ],AnyInt)
        )
    def testLookaheadNeg2( self ):
        self.doBasicTest(
            '''s := ?-"b"?
            ''',
            's',
            'bbbba',
            (1,[
            ],0)
        )
    def testLookaheadNeg3( self ):
        self.doBasicTest(
            '''s := "b", ?-"a"
            ''',
            's',
            'bbbba',
            (1,[
            ],1)
        )
    def testLookaheadNeg4( self ):
        self.doBasicTest(
            '''s := "b", ?-"a", "ba"
            ''',
            's',
            'bba',
            (1,[
            ],3)
        )
    def testLookaheadNeg5( self ):
        self.doBasicTest(
            '''s := ?-t, "ba"
            t := "bad"
            ''',
            's',
            'bac',
            (1,[
            ],2)
        )
    def testLookaheadNeg6( self ):
        self.doBasicTest(
            '''s := ?-t, "ba"
            t := "bad"
            ''',
            's',
            'bad',
            (0,[
            ],AnyInt)
        )
    def testLookahead2( self ):
        """Test lookahead on literals (more complex)"""
        self.doBasicTest(
            '''s := something+, "ba"
            something := "b",?-"a"
            ''',
            's',
            'bbbba',
            (1,[
                ("something",0,1,NullResult),
                ("something",1,2,NullResult),
                ("something",2,3,NullResult),
            ],5)
        )
    def testLookahead3( self ):
        """Test lookahead on reported positive productions"""
        self.doBasicTest(
            '''s := ?trailer
            trailer   := "bad"
            ''',
            's',
            'badba',
            (1,[
                ("trailer",0,3,NullResult),
            ],0)
        )
    def testLookahead4( self ):
        self.doBasicTest(
            '''s := ?-trailer?
            trailer   := "bad"
            ''',
            's',
            'badba',
            (1,[
            ],0)
        )

    def testLookahead5( self ):
        self.doBasicTest(
            '''s := ?-trailer, 'ba'
            trailer   := "bad"
            ''',
            's',
            'babba',
            (1,[
            ],2)
        )
    def testLookahead6( self ):
        self.doBasicTest(
            '''s := ?-trailer, 'ba'
            trailer   := "bad"
            ''',
            's',
            'badba',
            (0,[
            ],AnyInt)
        )

    def testGenPos1( self ):
        self.doBasicTest(
            '''s := something
            something := "a" ''',
            's',
            'mmmab',
            (0,[],AnyInt)
        )
    def testGenPos2( self ):
        self.doBasicTest(
            '''s := something
            something := "a" ''',
            's',
            'ammmab',
            (1,[('something',0,1,NullResult),],1)
        )

    def testOptionalGroupHitEOF( self ):
        """Test optional group hitting an EOF during success run"""
        self.doBasicTest(
            '''s := something*
            something := ("a"/"b") ''',
            's',
            'aa',
            (1,[
                ('something',0,1,NullResult),
                ('something',1,2,NullResult),
            ],2)
        )
    def testMultiLineDef( self ):
        """Test multi-line definitions"""
        self.doBasicTest(
            '''s :=
            something*
            something := (
                "a"/
                "b"
            ) ''',
            's',
            'aa',
            (1,[
                ('something',0,1,NullResult),
                ('something',1,2,NullResult),
            ],2)
        )
##	def testRepeatOptionalFail( self ):
##		"""Explicit test of the optional-repeating-child of repeating object
##		"""
##		self.doBasicTest(
##			r'''
##			controlword := '\\',('*','\\')?,[-a-zA-Z0-9]+
##			contents := -[\012}\\]*
##			file := (controlword/contents)+
##			''',
##			"file",
##			"\\*\\test sdf ff f f sdfff\\",
##			(1, [
##				("controlword", 0,7,[]),
##				("contents",7,24),
##			],24),
##		)

    def testGenCILiteral1( self ):
        self.doBasicTest(
            '''s := c"this"''',
            's',
            'this',
            (1,[],4)
        )
    def testGenCILiteral2( self ):
        self.doBasicTest(
            '''s := c"this"''',
            's',
            'This',
            (1,[],4)
        )
    def testGenCILiteral3( self ):
        self.doBasicTest(
            '''s := c"this"''',
            's',
            'THIS',
            (1,[],4)
        )
    def testGenCILiteral4( self ):
        self.doBasicTest(
            '''s := -c"this"''',
            's',
            ' THIS',
            (1,[],1)
        )
    def testGenCILiteral5( self ):
        self.doBasicTest(
            '''s := -c"this"''',
            's',
            ' thi',
            (1,[],1)
        )
    def testGenCILiteral6( self ):
        self.doBasicTest(
            '''s := -c"this"*''',
            's',
            ' thi',
            (1,[],4)
        )
    def testGenUnicodeRange( self ):
        self.doBasicTest(
            '''s := [\u0600-\u06ff]+''',
            's',
            u'\u0600\u06ff',
            (1,[],2)
        )
    if sys.version_info[0] < 3:
        def testGenUnicodeRangeBroken( self ):
            self.assertRaises(
                ValueError,
                self.doBasicTest,
                    '''s := [a-\u06ff]+''',
                    's',
                    u'\u0600\u06ff',
                    (1,[],2)
            )
        

class NameTests(unittest.TestCase):
    def doBasicTest(self, definition, parserName, testValue, expected, ):
        result = Parser( definition).parse( testValue, production=parserName )
        assert result == expected, '''\nexpected:%s\n     got:%s\n'''%( expected, result )
    def test_p( self ):
        self.doBasicTest(
            '''s := something
            something := "a" ''',
            's',
            'ammmab',
            (1,[('something',0,1,NullResult),],1)
        )
    def test_po( self ):
        self.doBasicTest(
            '''s := something?
            something := "a" ''',
            's',
            'ammmab',
            (1,[('something',0,1,NullResult),],1)
        )
    def test_por( self ):
        self.doBasicTest(
            '''s := something*
            something := "a" ''',
            's',
            'ammmab',
            (1,[('something',0,1,NullResult),],1)
        )
    def test_pr( self ):
        self.doBasicTest(
            '''s := something+
            something := "a" ''',
            's',
            'ammmab',
            (1,[('something',0,1,NullResult),],1)
        )

    def test_n( self ):
        self.doBasicTest(
            '''s := - something
            <something> := [ab]''',
            's',
            'mmmab',
            (1,[],1)
        )
    def test_no( self ):
        self.doBasicTest(
            '''s := - something?
            <something> := [ab]''',
            's',
            'mmmab',
            (1,[],1)
        )
    def test_nor( self ):
        self.doBasicTest(
            '''s := - something*
            <something> := [ab]''',
            's',
            'mmmab',
            (1,[],3)
        )
    def test_nr( self ):
        self.doBasicTest(
            '''s := - something+
            <something> := [ab]''',
            's',
            'mmmab',
            (1,[],3)
        )
    def test_n_f( self ):
        self.doBasicTest(
            '''s := - something
            <something> := [ab]''',
            's',
            'ammmab',
            (0,[],AnyInt)
        )
    def test_no_f( self ):
        self.doBasicTest(
            '''s := - something?
            <something> := [ab]''',
            's',
            'ammmab',
            (1,[],0)
        )
    def test_nor_f( self ):
        self.doBasicTest(
            '''s := - something*
            <something> := [ab]''',
            's',
            'ammmab',
            (1,[],0)
        )
    def test_nr_f( self ):
        self.doBasicTest(
            '''s := - something +
            <something> := [ab]''',
            's',
            'ammmab',
            (0,[],AnyInt)
        )
##	def test_por_big( self ):
##		"""This test creates 1,000,000 result tuples (very inefficiently, I might add)...
##		on my machine that takes a long time, so I do not bother with the test
##		(note that with a recursive mx.TextTools, this should actually blow up
##		 long before you get into memory problems :) ).
##		"""
##		self.doBasicTest(
##			'''s := something*
##			something := "a" ''',
##			's',
##			'a'*1000000,
##			(1,[
##			],1000000)
##		)

    def test_expanded_name( self ):
        """Non-reporting (expanded) name test

        Tests new feature, a name whose children
        are reported, but which is not itself reported,
        basically this lets you create anonymous
        groups which can be referenced from other
        productions.
        """
        self.doBasicTest(
            '''s := something +
            >something< := r
            r := [ab]
            v := [c]
            ''',
            's',
            'abammmab',
            (1,[
                ('r',0,1, NullResult),
                ('r',1,2, NullResult),
                ('r',2,3, NullResult),
            ],3)
        )
        
    def test_expanded_SingleNameChild( self ):
        """Expanded group with single child which is a Name itself

        This originally failed when the Name object's report value
        was changed to 0 (redundant information for the "expanded" code),
        resulting in the child production not getting reported.
        """
        self.doBasicTest(
            '''s := something +
            something := r
            r := [ab]''',
            'something',
            'abammmab',
            (1,[
                ('r',0,1, NullResult),
            ],1)
        )

class BasicMethodSource:
    def __init__( self ):
        self.results = []
    def _m_a( self, taglist,text,l,r,subtags ):
        self.results.append( ('a',text[l:r]))
    def _m_b( self, taglist, text, l,r,subtags):
        self.results.append( ('b',l,r) )
    _m_c = TextTools.AppendMatch
    _m_d = TextTools.AppendTagobj
    _o_d = "hello world"
class AppendToTagobjMethodSource:
    def __init__( self ):
        self._o_d = []
    _m_d = TextTools.AppendToTagobj

class CallTests(unittest.TestCase):
    """Tests semantics of calling objects from a method source during parsing"""
    def parse( self, definition, parserName, testValue, source):
        result = Parser(
            definition,
        ).parse(testValue, production=parserName, processor = source)
        return result
    def test_basic_call( self ):
        """Test basic ability to call a method instead of regular functioning"""
        source = BasicMethodSource()
        self.parse( """
            x := (a/b)*
            a := "a"
            b := "b"
        """, 'x', 'abba', source)
        assert source.results == [ ('a','a'),('b',1,2),('b',2,3),('a','a'),], """Method source methods were not called, or called improperly:\n%s"""%(source.results,)
        
    def test_AppendMatch( self ):
        """Test ability to append the text-string match to the results list"""
        source = BasicMethodSource()
        result = self.parse( """
            x := c*
            c := 'c'
        """, 'x', 'ccc', source)
        assert result == (1,[
            'c','c','c',
        ],3), """Result was %s"""%( result, )
        
    def test_AppendTagObj( self ):
        """Test appending the tagobject to the results list"""
        source = BasicMethodSource()
        result = self.parse( """
            x := d*
            d := 'd'
        """, 'x', 'ddd', source)
        assert result == (1,[
            "hello world","hello world","hello world",
        ],3)

    def test_AppendToTagObj( self ):
        """Test basic ability to call a method instead of regular functioning"""
        source = AppendToTagobjMethodSource()
        self.parse( """
            x := d*
            d := 'd'
        """, 'x', 'ddd', source)
        assert source._o_d == [ (None,0,1,NullResult),(None,1,2,NullResult),(None,2,3,NullResult)], """Method source methods were not called, or called improperly:\n%s"""%(source._o_d,)


def getSuite():
    return unittest.TestSuite((
        test_grammarparser.getSuite(),
        test_erroronfail.getSuite(),
        unittest.makeSuite(ParserGenerationTests, 'test'),
        unittest.makeSuite(NameTests, 'test'),
        unittest.makeSuite(CallTests, 'test'),
    ))

if __name__ == "__main__":
    unittest.main(defaultTest="getSuite")
