# Licensed under a 3-clause BSD style license - see LICENSE.rst

import importlib
import secrets
from py_compile import compile
from textwrap import dedent

import pytest

from astropy.utils.parsing import _TAB_HEADER


def _docstring_canary():
    """Docstring that's here just to check for -OO."""


@pytest.mark.skipif(not _docstring_canary.__doc__, reason="Test cannot be run with -OO")
def test_generate_parser(tmp_path, monkeypatch):
    # Write Python code into the temporary directory, so that the
    # generated tables will also go into the temporary directory.
    # We use a unique suffix so that the test can be run multiple times
    # without weirdness due to module caching.
    suffix = secrets.token_hex(16)
    lexer_file = tmp_path / f"test_parsing_lexer_{suffix}.py"
    lexer_file.write_text(
        dedent(
            rf"""
        from astropy.utils.parsing import lex

        def make_lexer():
            tokens = ('NUMBER', 'PLUS')
            t_PLUS = r'\+'

            def t_NUMBER(t):
                r'\d+'
                t.value = int(t.value)
                return t

            def t_error(t):
                raise ValueError('Invalid character')

            return lex('test_parsing_lextab_{suffix}', 'test_parsing_lexer_{suffix}')
        """
        )
    )
    parser_file = tmp_path / f"test_parsing_parser_{suffix}.py"
    parser_file.write_text(
        dedent(
            rf"""
        from astropy.utils.parsing import yacc

        def make_parser():
            tokens = ('NUMBER', 'PLUS')

            def p_expression_number(p):
                'expression : NUMBER'
                p[0] = p[1]

            def p_expression_plus(p):
                'expression : expression PLUS NUMBER'
                p[0] = p[1] + p[3]

            return yacc('test_parsing_parsetab_{suffix}', 'test_parsing_parser_{suffix}')
        """
        )
    )

    monkeypatch.syspath_prepend(tmp_path)

    lexer_mod = importlib.import_module(f"test_parsing_lexer_{suffix}")
    lexer = lexer_mod.make_lexer()
    parser_mod = importlib.import_module(f"test_parsing_parser_{suffix}")
    parser = parser_mod.make_parser()
    result = parser.parse("1+2+3", lexer=lexer)
    assert result == 6

    lextab_path = tmp_path / f"test_parsing_lextab_{suffix}.py"
    lextab = lextab_path.read_text()
    assert lextab.startswith(_TAB_HEADER.format(package=f"test_parsing_lexer_{suffix}"))
    parsetab_path = tmp_path / f"test_parsing_parsetab_{suffix}.py"
    parsetab = parsetab_path.read_text()
    assert parsetab.startswith(
        _TAB_HEADER.format(package=f"test_parsing_parser_{suffix}")
    )

    # Now test bytecode-only distribution of the parsetab and lextab

    lexer_mod = importlib.reload(lexer_mod)
    parser_mod = importlib.reload(parser_mod)
    for path in lextab_path, parsetab_path:
        compile(path, cfile=path.with_suffix(".pyc"), doraise=True)
        path.unlink()

    lexer = lexer_mod.make_lexer()
    parser = parser_mod.make_parser()
    result = parser.parse("1+2+3", lexer=lexer)
    assert result == 6
