import py, StringIO, sys
import scfparse
from scfparse import Grammar, ParseFailed

#scfparse.debug = sys.stdout
scfparse.debug_print()

def cant_parse(gr, txt):
    py.test.raises(ParseFailed, lambda: gr.parse(txt))
    return True

def test_str():
    " Simplest possible test "
    g = Grammar()
    gr = g.atom('abc')
    assert gr.parse(u'abc') == 'abc'

def test_str_seq_and():
    g = Grammar()
    gr = 'a' & g.atom('b') & 'c'
    assert gr.parse(u'abc') == ['a', 'b', 'c']

def test_str_seq_atom():
    g = Grammar()
    gr = g.atom('a', 'b', 'c')
    assert gr.parse(u'abc') == ['a', 'b', 'c']

def test_rep_nows():
    g = Grammar()
    g.set_default_sep(None)
    gr = g.atom('a').rep()
    assert gr.parse(u'aaa') == ['a', 'a', 'a']
    assert cant_parse(gr, u'a a a')

def test_rep_nosep():
    g = Grammar()
    gr = g.atom('a').rep(sep=None)
    assert gr.parse(u'aaa') == ['a', 'a', 'a']
    assert cant_parse(gr, u'a a a')

def test_rep_ws():
    g = Grammar()
    gr = g.atom('a').rep()
    assert gr.parse(u'aaa') == ['a', 'a', 'a']
    assert gr.parse(u'a a a') == ['a', 'a', 'a']
    assert cant_parse(gr, u'a a a ')  # only SEP by ws

def test_rep_sep():
    g = Grammar()
    gr = g.atom('a').rep(sep=',')
    assert gr.parse(u'a,a,a') == ['a', 'a', 'a']
    assert cant_parse(gr, u'aaa')
    assert cant_parse(gr, u'a, a, a')

def test_str_choice():
    g = Grammar()
    gr = g.atom('a') | 'b' | 'c'
    assert gr.parse(u'a') == 'a'
    assert gr.parse(u'b') == 'b'
    assert gr.parse(u'c') == 'c'
    cant_parse(gr, u'd')

def test_getitem():
    g = Grammar()
    gr = ('(' & g.atom('a') & ')')[1]
    assert gr.parse(u'(a)') == 'a'
    
def test_and_ws():
    g = Grammar()
    gr = ('(' & g.atom('a') & ')')[1]
    assert gr.parse(u'( a )') == 'a'

def test_ws_not_nl():
    g = Grammar()
    g.nt.a = g.atom(['a', g.nt.uspacenotnl])
    assert g.nt.a.parse(u'a ') == ['a', ' ']
    assert cant_parse(g.nt.a, u'a\n')
    assert cant_parse(g.nt.a, u'ab')

def test_ws_not_nl_2():
    g = Grammar()
    g.nt.a = g.atom(['a', g.nt.uspace.but_not_char_in('\n')])
    assert g.nt.a.parse(u'a ') == ['a', ' ']
    assert cant_parse(g.nt.a, u'a\n')
    assert cant_parse(g.nt.a, u'ab')

def test_ws_not_nl_3():
    g = Grammar()
    g.nt.a = g.atom(['a', g.nt.uspace.but_not(g.nt.nl)])
    assert g.nt.a.parse(u'a ') == ['a', ' ']
    assert cant_parse(g.nt.a, u'a\n')
    assert cant_parse(g.nt.a, u'ab')

def test_getitem_mul():
    g = Grammar()
    gr = ('(' & g.atom('a') & ')')[0,1]
    assert gr.parse(u'(a)') == ['(', 'a']

def test_char_in_rep_yields_string():
    g = Grammar()
    gr = g.char_in('abc').rep()
    assert gr.parse(u'abcabcabc') == u'abcabcabc'

def test_or_cm_yields_cm():
    g = Grammar()
    gr = (g.nt.ualnum | '_').rep()
    assert gr.parse(u'abc_d') == u'abc_d'
    assert gr.parse(u'0123') == u'0123'

def test_fixed_string_rep_yields_list():
    g = Grammar()
    gr = g.atom('abc').rep()
    assert gr.parse(u'abcabcabc') == [u'abc', u'abc', u'abc']

def test_pred():
    g = Grammar()
    a = g.atom('a')
    b = g.atom('b')
    gr = (a & b.pred_not())[0].rep() & a & b
    assert gr.parse(u'aaab') == [['a', 'a'], 'a', 'b']
    assert gr.parse(u'ab') == [[], 'a', 'b']
    cant_parse(gr, u'aa')

def test_pred_infb():
    g = Grammar()
    a = g.atom('a')
    b = g.atom('b')
    gr = a.if_not_followed_by(b).rep() & a & b
    assert gr.parse(u'aaab') == [['a', 'a'], 'a', 'b']
    assert gr.parse(u'ab') == [[], 'a', 'b']
    cant_parse(gr, u'aa')

def build_re():
    g = Grammar()
    nt = g.nt
    re = nt.re = g.stub()
    re_crange = nt.re_crange = g.stub()
    re_trailer = nt.re_trailer = g.char_in('?*+')

    re.atom = (g.choice(
        g.atom('(', re, ')'),
        g.atom('[', re_crange, ']'),
        re_trailer.inv()) & re_trailer.opt()).rep(1)
    re_crange.atom = g.char_not_in(']')
    return re

def test_re_simple():
    " Grammar for regular expressions "
    re = build_re()
    assert re.parse(u'a') == [['a', None]]
    assert re.parse(u'a?') == [['a', '?']]
    assert re.parse(u'a?b?') == [['a', '?'], ['b','?']]
    assert re.parse(u'ab?b?') == [['a', None], ['b', '?'], ['b','?']]
#
#def test_re_complex():
#    re = build_re()
#    assert re.parse(u'ef(ab?)?cd') == [['ef', None],
#                                       [['(', [['ab', '?']], ')'],'?'],
#                                       ['cd',None]]
#    

def test_err():
    g = Grammar()
    nt = g.nt
    nt.a = g.atom('a')
    nt.b = g.atom('b')
    nt.c = g.atom('c')
    nt.e = g.atom('e')
    nt.g = nt.a | ((nt.b & nt.e) | (nt.b & nt.c & nt.e))
    try:
        nt.g.parse(u'bcd')
        py.test.fail('parse should not succeed')
    except ParseFailed, e:
        # we should give an error at the point that got farthest
        print repr(e)
        assert e.cause.cause.pnt.index == 2

def test_debug():
    """
    Test to make sure that those items (such as g or a) which are
    named in the debug_names dictionary are properly named in the
    debug printouts.  Also test that the debug machinery works in
    general.
    """
    g = Grammar()
    nt = g.nt
    old_debug = scfparse.debug
    old_debug_names = scfparse.debug_names
    try:
        nt.a = g.atom('a')
        nt.g = nt.a | 'b' | 'c'
        strio = StringIO.StringIO()
        scfparse.debug = strio.write
        nt.g.parse('b')
        res = strio.getvalue().split('\n')
        print "\n".join(repr(r) for r in res)
        exp_res = [
            "Parsing:\t'b'",
            "\tPnt=None\tAs=None\tRes=None",
            "\t\tPnt=(0,'b')\tAs=g\tRes=(u'b', (1,''))",
            "\t\t\tPnt=(0,'b')\tAs=(a | u'b')\tRes=(u'b', (1,''))",
            "\t\t\t\tPnt=(0,'b')\tAs=a\tRes=ParsedFailed(Expected: u'a' but found 'b' at (0,'b'))",
            "\t\t\t\tPnt=(0,'b')\tAs=u'b'\tRes=(u'b', (1,''))",
            '']
        print [i for i in range(len(exp_res)) if res[i] != exp_res[i]]
        assert res == exp_res
    finally:
        scfparse.debug = old_debug
        scfparse.debug_names = old_debug_names

