1# Licensed under a 3-clause BSD style license - see LICENSE.rst
2
3import importlib
4import secrets
5import sys
6from textwrap import dedent
7
8import pytest
9
10from astropy.utils.parsing import lex, yacc, _TAB_HEADER
11
12
13def _docstring_canary():
14    """Docstring that's here just to check for -OO."""
15
16
17@pytest.mark.skipif(not _docstring_canary.__doc__, reason="Test cannot be run with -OO")
18def test_generate_parser(tmp_path, monkeypatch):
19    # Write Python code into the temporary directory, so that the
20    # generated tables will also go into the temporary directory.
21    # We use a unique suffix so that the test can be run multiple times
22    # without weirdness due to module caching.
23    suffix = secrets.token_hex(16)
24    lexer_file = tmp_path / f'test_parsing_lexer_{suffix}.py'
25    lexer_file.write_text(dedent(fr"""
26        from astropy.utils.parsing import lex
27
28        def make_lexer():
29            tokens = ('NUMBER', 'PLUS')
30            t_PLUS = r'\+'
31
32            def t_NUMBER(t):
33                r'\d+'
34                t.value = int(t.value)
35                return t
36
37            return lex('test_parsing_lextab_{suffix}', 'test_parsing_lexer_{suffix}')
38        """))
39    parser_file = tmp_path / f'test_parsing_parser_{suffix}.py'
40    parser_file.write_text(dedent(fr"""
41        from astropy.utils.parsing import yacc
42
43        def make_parser():
44            tokens = ('NUMBER', 'PLUS')
45
46            def p_expression_number(p):
47                'expression : NUMBER'
48                p[0] = p[1]
49
50            def p_expression_plus(p):
51                'expression : expression PLUS NUMBER'
52                p[0] = p[1] + p[3]
53
54            return yacc('test_parsing_parsetab_{suffix}', 'test_parsing_parser_{suffix}')
55        """))
56
57    monkeypatch.syspath_prepend(tmp_path)
58
59    lexer_mod = importlib.import_module(f'test_parsing_lexer_{suffix}')
60    lexer = lexer_mod.make_lexer()
61    parser_mod = importlib.import_module(f'test_parsing_parser_{suffix}')
62    parser = parser_mod.make_parser()
63    result = parser.parse('1+2+3', lexer=lexer)
64    assert result == 6
65
66    lextab = (tmp_path / f'test_parsing_lextab_{suffix}.py').read_text()
67    assert lextab.startswith(_TAB_HEADER.format(package=f'test_parsing_lexer_{suffix}'))
68    parsetab = (tmp_path / f'test_parsing_parsetab_{suffix}.py').read_text()
69    assert parsetab.startswith(_TAB_HEADER.format(package=f'test_parsing_parser_{suffix}'))
70