"""
Tests for subpar.grammars.bootstrap
"""
from pathlib import Path

from nose.tools import assert_equals, assert_raises

from subpar.structs import Node, Trace, Result, Serialized

from utilities import NodePart, TracePart, compare_results


def test_unescaped():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['unescaped']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    unescaped = BOOTSTRAP._components['unescaped']

    compare_results(
        unescaped.parse('\\', 0, 0, 1, {}, {}),
        (
            Result(Node('unescaped', 0, 1, ()), {}),
            Trace('unescaped', 0, 1, (Node('unescaped', 0, 1, ()),), None),
        )
    )

    compare_results(
        unescaped.parse('n', 0, 0, 1, {}, {}),
        (
            Result(Node('unescaped', 0, 1, ()), {}),
            Trace('unescaped', 0, 1, (Node('unescaped', 0, 1, ()),), None),
        )
    )

    compare_results(
        unescaped.parse('.', 0, 0, 1, {}, {}),
        (
            Result(Node('unescaped', 0, 1, ()), {}),
            Trace('unescaped', 0, 1, (Node('unescaped', 0, 1, ()),), None),
        )
    )


def test_safe():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['single/double_safe']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    single = BOOTSTRAP._components['single_safe']

    compare_results(
        single.parse('n', 0, 0, 1, {}, {}),
        (
            Result(Node('safe', 0, 1, ()), {}),
            Trace('safe', 0, 1, (Node('safe', 0, 1, ()),), None),
        )
    )

    compare_results(
        single.parse('"', 0, 0, 1, {}, {}),
        (
            Result(Node('safe', 0, 1, ()), {}),
            Trace('safe', 0, 1, (Node('safe', 0, 1, ()),), None),
        )
    )

    compare_results(
        single.parse("'", 0, 0, 1, {}, {}),
        (Trace('safe', 0, 0, (), None),)
    )

    compare_results(
        single.parse("\\", 0, 0, 1, {}, {}),
        (Trace('safe', 0, 0, (), None),)
    )

    compare_results(
        single.parse("\r", 0, 0, 1, {}, {}),
        (Trace('safe', 0, 0, (), None),)
    )

    compare_results(
        single.parse("\n", 0, 0, 1, {}, {}),
        (Trace('safe', 0, 0, (), None),)
    )

    double = BOOTSTRAP._components['double_safe']

    compare_results(
        double.parse('n', 0, 0, 1, {}, {}),
        (
            Result(Node('safe', 0, 1, ()), {}),
            Trace('safe', 0, 1, (Node('safe', 0, 1, ()),), None),
        )
    )

    compare_results(
        double.parse("'", 0, 0, 1, {}, {}),
        (
            Result(Node('safe', 0, 1, ()), {}),
            Trace('safe', 0, 1, (Node('safe', 0, 1, ()),), None),
        )
    )

    compare_results(
        double.parse('"', 0, 0, 1, {}, {}),
        (Trace('safe', 0, 0, (), None),)
    )

    compare_results(
        double.parse("\\", 0, 0, 1, {}, {}),
        (Trace('safe', 0, 0, (), None),)
    )

    compare_results(
        double.parse("\r", 0, 0, 1, {}, {}),
        (Trace('safe', 0, 0, (), None),)
    )

    compare_results(
        double.parse("\n", 0, 0, 1, {}, {}),
        (Trace('safe', 0, 0, (), None),)
    )


def test_contents():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['single/double_contents']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    single = BOOTSTRAP._components['single_contents']
    double = BOOTSTRAP._components['double_contents']

    compare_results(
        single.parse('abc\\\\d\\n\n', 0, 0, 8, BOOTSTRAP._components, {}),
        (
            Result(Node('contents', 0, 0, ()), {}),
            Result(Node('contents', 0, 1, (Node('safe', 0, 1, ()),)), {}),
            Result(
                Node(
                    'contents', 0, 2, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'contents', 0, 3, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                        Node('safe', 2, 1, ()),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'contents', 0, 5, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                        Node('safe', 2, 1, ()),
                        Node('unescaped', 4, 1, ()),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'contents', 0, 6, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                        Node('safe', 2, 1, ()),
                        Node('unescaped', 4, 1, ()),
                        Node('safe', 5, 1, ()),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'contents', 0, 8, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                        Node('safe', 2, 1, ()),
                        Node('unescaped', 4, 1, ()),
                        Node('safe', 5, 1, ()),
                        Node('unescaped', 7, 1, ()),
                    )
                ),
                {}
            ),
            Trace(
                'contents', 0, 8,
                (
                    Node('safe', 0, 1, ()),
                    Node('safe', 1, 1, ()),
                    Node('safe', 2, 1, ()),
                    Node('unescaped', 4, 1, ()),
                    Node('safe', 5, 1, ()),
                    Node('unescaped', 7, 1, ()),
                ),
                Trace('safe', 8, 0, (), None)
            ),
        )
    )

    compare_results(
        single.parse('\'"', 0, 0, 2, BOOTSTRAP._components, {}),
        (
            Result(Node('contents', 0, 0, ()), {}),
            Trace('contents', 0, 0, (Node('contents', 0, 0, ()),), None)
        )
    )

    compare_results(
        single.parse('\'"', 1, 0, 1, BOOTSTRAP._components, {}),
        (
            Result(Node('contents', 1, 0, ()), {}),
            Result(Node('contents', 1, 1, (Node('safe', 1, 1, ()),)), {}),
            Trace(
                'contents', 1, 1, (Node('safe', 1, 1, ()),),
                TracePart('safe', 2)
            )
        )
    )

    compare_results(
        double.parse('abc\\\\d\\n\n', 0, 0, 8, BOOTSTRAP._components, {}),
        (
            Result(Node('contents', 0, 0, ()), {}),
            Result(Node('contents', 0, 1, (Node('safe', 0, 1, ()),)), {}),
            Result(
                Node(
                    'contents', 0, 2, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'contents', 0, 3, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                        Node('safe', 2, 1, ()),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'contents', 0, 5, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                        Node('safe', 2, 1, ()),
                        Node('unescaped', 4, 1, ()),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'contents', 0, 6, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                        Node('safe', 2, 1, ()),
                        Node('unescaped', 4, 1, ()),
                        Node('safe', 5, 1, ()),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'contents', 0, 8, (
                        Node('safe', 0, 1, ()),
                        Node('safe', 1, 1, ()),
                        Node('safe', 2, 1, ()),
                        Node('unescaped', 4, 1, ()),
                        Node('safe', 5, 1, ()),
                        Node('unescaped', 7, 1, ()),
                    )
                ),
                {}
            ),
            Trace(
                'contents', 0, 8,
                (
                    Node('safe', 0, 1, ()),
                    Node('safe', 1, 1, ()),
                    Node('safe', 2, 1, ()),
                    Node('unescaped', 4, 1, ()),
                    Node('safe', 5, 1, ()),
                    Node('unescaped', 7, 1, ()),
                ),
                Trace('safe', 8, 0, (), None)
            ),
        )
    )

    compare_results(
        double.parse('\'"', 0, 0, 2, BOOTSTRAP._components, {}),
        (
            Result(Node('contents', 0, 0, ()), {}),
            Result(Node('contents', 0, 1, (Node('safe', 0, 1, ()),)), {}),
            Trace(
                'contents', 0, 1, (Node('safe', 0, 1, ()),),
                TracePart('safe', 1)
            )
        )
    )

    compare_results(
        double.parse('\'"', 1, 0, 1, BOOTSTRAP._components, {}),
        (
            Result(Node('contents', 1, 0, ()), {}),
            Trace('contents', 1, 0, (Node('contents', 1, 0, ()),), None)
        )
    )


def test_string():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['string']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    string = BOOTSTRAP._components['string']

    compare_results(
        string.parse('"a\\\\nn"', 0, 0, 7, BOOTSTRAP._components, {}),
        (
            Result(
                Node(
                    'string', 0, 7, (
                        Node(
                            'contents', 1, 5, (
                                Node('safe', 1, 1, ()),
                                Node('unescaped', 3, 1, ()),
                                Node('safe', 4, 1, ()),
                                Node('safe', 5, 1, ()),
                            )
                        ),
                    )
                ),
                {}
            ),
            Trace('string', 0, 7, (NodePart('contents'),), None)
        )
    )


def test_number():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['number']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    number = BOOTSTRAP._components['number']

    compare_results(
        number.parse('-123', 0, 0, 4, BOOTSTRAP._components, {}),
        (
            Result(Node('number', 0, 2, ()), {}),
            Result(Node('number', 0, 3, ()), {}),
            Result(Node('number', 0, 4, ()), {}),
            Trace('number', 0, 4, (), None),
        )
    )

    compare_results(
        number.parse('-123', 1, 0, 3, BOOTSTRAP._components, {}),
        (
            Result(Node('number', 1, 1, ()), {}),
            Result(Node('number', 1, 2, ()), {}),
            Result(Node('number', 1, 3, ()), {}),
            Trace('number', 1, 3, (), None),
        )
    )


def test_boolean():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['boolean']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    boolean = BOOTSTRAP._components['boolean']

    compare_results(
        boolean.parse('TrueFalse', 0, 0, 9, BOOTSTRAP._components, {}),
        (
            Result(Node('boolean', 0, 4, (Node('true', 0, 4, ()),)), {}),
            Trace('boolean', 0, 4, (), Trace('true', 0, 4, (), None)),
        )
    )

    compare_results(
        boolean.parse('TrueFalse', 1, 0, 8, BOOTSTRAP._components, {}),
        (
            Trace('boolean', 1, 0, (), Trace('true', 1, 0, (), None)),
        )
    )

    compare_results(
        boolean.parse('TrueFalse', 4, 0, 5, BOOTSTRAP._components, {}),
        (
            Result(Node('boolean', 4, 5, (Node('false', 4, 5, ()),)), {}),
            Trace('boolean', 4, 5, (), Trace('false', 4, 5, (), None)),
        )
    )


def test_none():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['none']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    none = BOOTSTRAP._components['none']

    compare_results(
        none.parse('Nonenone', 0, 0, 8, BOOTSTRAP._components, {}),
        (
            Result(Node('none', 0, 4, ()), {}),
            Trace('none', 0, 4, (), None),
        )
    )

    compare_results(
        none.parse('Nonenone', 4, 0, 4, BOOTSTRAP._components, {}),
        (Trace('none', 4, 0, (), None),)
    )


def test_value():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['value']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    value = BOOTSTRAP._components['value']

    compare_results(
        value.parse('None', 0, 0, 4, BOOTSTRAP._components, {}),
        (
            Result(Node('value', 0, 4, (Node('none', 0, 4, ()),)), {}),
            Trace('value', 0, 4, (), TracePart('none', 0, 4)),
        )
    )

    compare_results(
        value.parse('True', 0, 0, 4, BOOTSTRAP._components, {}),
        (
            Result(Node('value', 0, 4, (NodePart('boolean'),)), {}),
            Trace('value', 0, 4, (), TracePart('boolean', 0, 4)),
        )
    )

    compare_results(
        value.parse('False', 0, 0, 6, BOOTSTRAP._components, {}),
        (
            Result(Node('value', 0, 5, (NodePart('boolean'),)), {}),
            Trace('value', 0, 5, (), TracePart('boolean', 0, 5)),
        )
    )

    compare_results(
        value.parse('-123', 0, 0, 4, BOOTSTRAP._components, {}),
        (
            Result(Node('value', 0, 2, (Node('number', 0, 2, ()),)), {}),
            Result(Node('value', 0, 3, (Node('number', 0, 3, ()),)), {}),
            Result(Node('value', 0, 4, (Node('number', 0, 4, ()),)), {}),
            Trace('value', 0, 4, (), TracePart('number', 0, 4)),
        )
    )

    compare_results(
        value.parse('123', 0, 0, 3, BOOTSTRAP._components, {}),
        (
            Result(Node('value', 0, 1, (Node('number', 0, 1, ()),)), {}),
            Result(Node('value', 0, 2, (Node('number', 0, 2, ()),)), {}),
            Result(Node('value', 0, 3, (Node('number', 0, 3, ()),)), {}),
            Trace('value', 0, 3, (), TracePart('number', 0, 3)),
        )
    )

    compare_results(
        value.parse("'True'", 0, 0, 6, BOOTSTRAP._components, {}),
        (
            Result(
                Node(
                    'value', 0, 6,
                    (Node('string', 0, 6, (NodePart('contents', 1, 4),)),)
                ),
                {}
            ),
            Trace('value', 0, 6, (), TracePart('string', 0, 6)),
        )
    )


def test_parameter_name():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['parameter_name']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    parameter_name = BOOTSTRAP._components['parameter_name']

    compare_results(
        parameter_name.parse('_aA1-', 0, 0, 5, BOOTSTRAP._components, {}),
        (
            Result(Node('parameter_name', 0, 1, ()), {}),
            Result(Node('parameter_name', 0, 2, ()), {}),
            Result(Node('parameter_name', 0, 3, ()), {}),
            Result(Node('parameter_name', 0, 4, ()), {}),
            Trace('parameter_name', 0, 4, (), None),
        )
    )


def test_named_parameters():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['named_parameters']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    named_parameters = BOOTSTRAP._components['named_parameters']

    compare_results(
        named_parameters.parse(
            'a=12, bee="", c=None', 0, 0, 20, BOOTSTRAP._components, {}
        ),
        (
            Result(
                Node(
                    None, 0, 3, (
                        Node('parameter_name', 0, 1, ()),
                        NodePart('value', 2, 1)
                    )
                ),
                {}
            ),
            Result(
                Node(
                    None, 0, 4, (
                        Node('parameter_name', 0, 1, ()),
                        NodePart('value', 2, 2)
                    )
                ),
                {}
            ),
            Result(
                Node(
                    None, 0, 12, (
                        Node('parameter_name', 0, 1, ()),
                        NodePart('value', 2, 2),
                        Node('parameter_name', 6, 3, ()),
                        NodePart('value', 10, 2)
                    )
                ),
                {}
            ),
            Result(
                Node(
                    None, 0, 20, (
                        Node('parameter_name', 0, 1, ()),
                        NodePart('value', 2, 2),
                        Node('parameter_name', 6, 3, ()),
                        NodePart('value', 10, 2),
                        Node('parameter_name', 14, 1, ()),
                        NodePart('value', 16, 4)
                    )
                ),
                {}
            ),
            Trace(
                None, 0, 20,
                (
                    Node('parameter_name', 0, 1, ()),
                    NodePart('value', 2, 2),
                    Node('parameter_name', 6, 3, ()),
                    NodePart('value', 10, 2),
                    Node('parameter_name', 14, 1, ())
                ),
                TracePart('value', 16, 4)
            )
        )
    )


def test_positional_parameters():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['positional_parameters']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    positional_parameters = BOOTSTRAP._components['positional_parameters']

    compare_results(
        positional_parameters.parse(
            '12, True, None', 0, 0, 14, BOOTSTRAP._components, {}
        ),
        (
            Result(Node(None, 0, 1, (NodePart('value', 0, 1),)), {}),
            Result(Node(None, 0, 2, (NodePart('value', 0, 2),)), {}),
            Result(
                Node(
                    None, 0, 8,
                    (
                        NodePart('value', 0, 2),
                        NodePart('value', 4, 4),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    None, 0, 14,
                    (
                        NodePart('value', 0, 2),
                        NodePart('value', 4, 4),
                        NodePart('value', 10, 4),
                    )
                ),
                {}
            ),
            Trace(
                None, 0, 14, (
                    NodePart('value', 0, 2),
                    NodePart('value', 4, 4)
                ),
                TracePart('value', 10, 4, ())
            )
        )
    )


def test_parameters():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['parameters']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    parameters = BOOTSTRAP._components['parameters']

    compare_results(
        parameters.parse(
            '12, name=None', 0, 0, 13, BOOTSTRAP._components, {}
        ),
        (
            Result(Node('parameters', 0, 0, ()), {}),
            Result(Node('parameters', 0, 1, (NodePart('value', 0, 1),)), {}),
            Result(Node('parameters', 0, 2, (NodePart('value', 0, 2),)), {}),
            Result(
                Node(
                    'parameters', 0, 13,
                    (
                        NodePart('value', 0, 2),
                        Node('parameter_name', 4, 4, ()),
                        NodePart('value', 9, 4)
                    )
                ),
                {}
            ),
            Trace(
                'parameters', 0, 13,
                (
                    NodePart('value', 0, 2),
                    Node('parameter_name', 4, 4, ()),
                    NodePart('value', 9, 4)
                ),
                None
            )
        )
    )


def test_component():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['component']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    component = BOOTSTRAP._components['component']

    compare_results(
        component.parse('aB1C_', 0, 0, 5, BOOTSTRAP._components, {}),
        (Trace('component', 0, 0, (), None),)
    )

    compare_results(
        component.parse('aB1C_', 1, 0, 4, BOOTSTRAP._components, {}),
        (
            Result(Node('component', 1, 1, ()), {}),
            Result(Node('component', 1, 2, ()), {}),
            Result(Node('component', 1, 3, ()), {}),
            Result(Node('component', 1, 4, ()), {}),
            Trace('component', 1, 4, (), None),
        )
    )


def test_id():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['id']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    id = BOOTSTRAP._components['id']

    compare_results(
        id.parse('_1a2_B', 0, 0, 6, BOOTSTRAP._components, {}),
        (Trace('id', 0, 0, (), None),)
    )

    compare_results(
        id.parse('_1a2_B', 1, 0, 5, BOOTSTRAP._components, {}),
        (Trace('id', 1, 0, (), None),)
    )

    compare_results(
        id.parse('_1a2_B', 2, 0, 4, BOOTSTRAP._components, {}),
        (
            Result(Node('id', 2, 1, ()), {}),
            Result(Node('id', 2, 2, ()), {}),
            Result(Node('id', 2, 3, ()), {}),
            Trace('id', 2, 3, (), None),
        )
    )


def test_entrypoint():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['entrypoint']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    entrypoint = BOOTSTRAP._components['entrypoint']

    compare_results(
        entrypoint.parse('_1a2_b', 0, 0, 6, BOOTSTRAP._components, {}),
        (Trace('entrypoint', 0, 0, (), None),)
    )

    compare_results(
        entrypoint.parse('(_1a2_b)', 0, 0, 7, BOOTSTRAP._components, {}),
        (Trace('entrypoint', 0, 1, (), TracePart('id', 1, 0)),)
    )

    compare_results(
        entrypoint.parse('(a2_b)', 0, 0, 6, BOOTSTRAP._components, {}),
        (
            Result(Node('entrypoint', 0, 6, (NodePart('id', 1, 4),)), {}),
            Trace('entrypoint', 0, 6, (NodePart('id', 1, 4),), None),
        )
    )


def test_statement():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['statement']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    statement = BOOTSTRAP._components['statement']

    compare_results(
        statement.parse('(g) = A()', 0, 0, 9, BOOTSTRAP._components, {}),
        (
            Result(
                Node(
                    'statement', 0, 9, (
                        Node('entrypoint', 0, 3, (Node('id', 1, 1, ()),)),
                        Node('component', 6, 1, ()),
                        Node('parameters', 8, 0, ())
                    )
                ),
                {}
            ),
            Trace(
                'statement', 0, 9, (
                    Node('entrypoint', 0, 3, (Node('id', 1, 1, ()),)),
                    Node('component', 6, 1, ()),
                    Node('parameters', 8, 0, ())
                ),
                None
            )
        )
    )

    compare_results(
        statement.parse('abc = Abc(123)', 0, 0, 14, BOOTSTRAP._components, {}),
        (
            Result(
                Node(
                    'statement', 0, 14, (
                        Node('id', 0, 3, ()),
                        Node('component', 6, 3, ()),
                        NodePart('parameters', 10, 3)
                    )
                ),
                {}
            ),
            Trace(
                'statement', 0, 14, (
                    Node('id', 0, 3, ()),
                    Node('component', 6, 3, ()),
                    NodePart('parameters', 10, 3)
                ),
                None
            )
        )
    )


def test_gammar():
    """
    subpar.grammars.bootstrap.BOOTSTRAP['grammar']
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    grammar = BOOTSTRAP._components['grammar']

    compare_results(
        grammar.parse('g = A()', 0, 0, 7, BOOTSTRAP._components, {}),
        (
            Result(
                Node(
                    'grammar', 0, 7, (
                        Node(
                            'statement', 0, 7, (
                                Node('id', 0, 1, ()),
                                Node('component', 4, 1, ()),
                                Node('parameters', 6, 0, ())
                            )
                        ),
                    )
                ),
                {}
            ),
            Trace(
                'grammar', 0, 7,
                (
                    Node(
                            'statement', 0, 7, (
                                Node('id', 0, 1, ()),
                                Node('component', 4, 1, ()),
                                Node('parameters', 6, 0, ())
                            )
                        ),
                ),
                None
            )
        )
    )

    compare_results(
        grammar.parse(
            '\n\ng = A()\na = B(1)', 0, 0, 18, BOOTSTRAP._components, {}
        ),
        (
            Result(
                Node(
                    'grammar', 0, 9, (
                        Node(
                            'statement', 2, 7, (
                                Node('id', 2, 1, ()),
                                Node('component', 6, 1, ()),
                                Node('parameters', 8, 0, ())
                            )
                        ),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'grammar', 0, 10, (
                        Node(
                            'statement', 2, 7, (
                                Node('id', 2, 1, ()),
                                Node('component', 6, 1, ()),
                                Node('parameters', 8, 0, ())
                            )
                        ),
                    )
                ),
                {}
            ),
            Result(
                Node(
                    'grammar', 0, 18, (
                        Node(
                            'statement', 2, 7, (
                                Node('id', 2, 1, ()),
                                Node('component', 6, 1, ()),
                                Node('parameters', 8, 0, ())
                            )
                        ),
                        Node(
                            'statement', 10, 8, (
                                Node('id', 10, 1, ()),
                                Node('component', 14, 1, ()),
                                Node(
                                    'parameters', 16, 1,
                                    (
                                        Node(
                                            'value', 16, 1,
                                            (Node('number', 16, 1, ()),)
                                        ),
                                    )
                                ),
                            )
                        ),
                    )
                ),
                {}
            ),
            TracePart(
                'grammar', 0, 18,
                (
                    Node(
                        'statement', 2, 7, (
                            Node('id', 2, 1, ()),
                            Node('component', 6, 1, ()),
                            Node('parameters', 8, 0, ())
                        )
                    ),
                    Node(
                        'statement', 10, 8, (
                            Node('id', 10, 1, ()),
                            Node('component', 14, 1, ()),
                            Node(
                                'parameters', 16, 1,
                                (
                                    Node(
                                        'value', 16, 1,
                                        (Node('number', 16, 1, ()),)
                                    ),
                                )
                            )
                        )
                    )
                )
            )
        )
    )

    compare_results(
        grammar.parse(
            '''single_quote = Symbol('"')''',
            0, 0, 26, BOOTSTRAP._components, {}
        ),
        (
            Result(
                Node(
                    'grammar', 0, 26, (
                        Node(
                            'statement', 0, 26, (
                                Node('id', 0, 12, ()),
                                Node('component', 15, 6, ()),
                                NodePart('parameters', 22, 3)
                            )
                        ),
                    )
                ),
                {}
            ),
            TracePart('grammar', 0, 26,)
        )
    )


def test_self():
    """
    Have the bootstrap parser parse its own grammar
    """
    from subpar.grammars.bootstrap import BOOTSTRAP

    grammar_file = (
        Path(__file__).absolute().parent.parent /
        'grammars' /
        'bootstrap.bootstrap'
    )

    grammar = grammar_file.read_text()

    compare_results(
        BOOTSTRAP.parse(grammar),
        (NodePart('grammar'), TracePart('grammar'))
    )


def test_load():
    """
    subpar.grammars.bootstrap.load
    """
    from subpar.components import Symbol, Repeat
    from subpar.grammars import bootstrap
    from subpar.grammars.bootstrap import load
    from subpar.parser import Parser

    parser = Parser()
    parser.add(Symbol('a', invert=False), 'not_a')
    parser.add(Repeat('a', minimum=1, maximum=None), 'a_plus', entrypoint=True)

    assert_equals(
        load(
            'not_a = Symbol("a", invert=False)\n'
            '(a_plus) = Repeat("a", minimum=1, maximum=None)'
        ),
        parser
    )

    with assert_raises(ValueError):
        load('a = Symbol("a"')

    grammar_file = (
        Path(__file__).absolute().parent.parent /
        'grammars' /
        'bootstrap.bootstrap'
    )

    grammar = grammar_file.read_text()

    parser = load(grammar)

    assert_equals(bootstrap.BOOTSTRAP, parser)

    bootstrap.BOOTSTRAP = parser

    parser = bootstrap.load(grammar)

    assert_equals(bootstrap.BOOTSTRAP, parser)


def test_store():
    """
    subpar.grammars.bootstrap.store
    """
    from subpar.components import Symbol, Repeat
    from subpar.grammars.bootstrap import BOOTSTRAP, load, store
    from subpar.parser import Parser

    parser = Parser()

    assert_equals(store(parser), '')

    parser.add(Symbol('a', invert=False), 'a')
    assert_equals(
        store(parser),
        "a = Symbol('a', invert=False, name=None)"
    )

    parser.add(
        Repeat('a', minimum=1, maximum=None, name='a_plus'),
        'a_plus', entrypoint=True
    )
    assert_equals(
        store(parser),
        (
            "a = Symbol('a', invert=False, name=None)\n"
            "(a_plus) = Repeat('a', minimum=1, maximum=None, name='a_plus')"
        )
    )

    assert_equals(parser, load(store(parser)))

    parser.add(Symbol('!', name='!'), '!', entrypoint=True)
    assert_equals(next(load(store(parser)).parse('!')), Node('!', 0, 1, ()))

    class _Symbol(Symbol):
        pass

    bad_parser = Parser()
    bad_parser.add(_Symbol('a'))

    with assert_raises(ValueError):
        store(bad_parser)

    class BadParameter(Symbol):
        def serialize(self):
            return Serialized('Bad', (), {'bad!': True})

    bad_parser = Parser()
    bad_parser.add(BadParameter('a'))

    with assert_raises(ValueError):
        store(bad_parser)

    bad_parser = Parser()
    bad_parser.add(Symbol(b'a'))

    with assert_raises(TypeError):
        store(bad_parser)

    bad_parser = Parser()
    bad_parser.add(Symbol('a', invert=()))

    with assert_raises(TypeError):
        store(bad_parser)

    grammar_file = (
        Path(__file__).absolute().parent.parent /
        'grammars' /
        'bootstrap.bootstrap'
    )

    grammar = grammar_file.read_text()

    assert_equals(BOOTSTRAP, load(store(load(grammar))))
