test_ebnf.py 6.33 KB
Newer Older
1
2
#!/usr/bin/python3

3
"""test_ebnf.py - tests of the EBNFcompiler-module of DHParser 
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
                             

Author: Eckhart Arnold <arnold@badw.de>

Copyright 2017 Bavarian Academy of Sciences and Humanities

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import os
import sys
sys.path.append(os.path.abspath('../../'))
26
from DHParser.syntaxtree import traverse
27
from DHParser.parsers import full_compilation, WHITESPACE_KEYWORD
28
29
from DHParser.ebnf import EBNFGrammar, EBNF_ASTPipeline, EBNFCompiler
from DHParser.dsl import compileEBNF
30
31
32
33
34


WRITE_LOGS = True


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class TestDirectives:
    mini_language = """
        expression =  term  { ("+" | "-") term }
        term       =  factor  { ("*" | "/") factor }
        factor     =  constant | "("  expression  ")"
        constant   =  digit { digit } [ //~ ]
        digit      = /0/ | /1/ | /2/ | /3/ | /4/ | /5/ | /6/ | /7/ | /8/ | /9/ 
        """

    def test_whitespace_linefeed(self):
        lang = "@ whitespace = linefeed\n" + self.mini_language
        MinilangParser = compileEBNF(lang)
        parser = MinilangParser()
        assert parser
        syntax_tree = parser.parse("3 + 4 * 12")
50
        # parser.log_parsing_history("WSP")
51
52
        assert not syntax_tree.collect_errors()
        syntax_tree = parser.parse("3 + 4 \n * 12")
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
        # parser.log_parsing_history("WSPLF")
        assert not syntax_tree.collect_errors()
        syntax_tree = parser.parse("3 + 4 \n \n * 12")
        assert syntax_tree.collect_errors()
        syntax_tree = parser.parse("3 + 4 \n\n * 12")
        assert syntax_tree.collect_errors()

    def test_whitespace_vertical(self):
        lang = "@ whitespace = vertical\n" + self.mini_language
        parser = compileEBNF(lang)()
        assert parser
        syntax_tree = parser.parse("3 + 4 * 12")
        assert not syntax_tree.collect_errors()
        syntax_tree = parser.parse("3 + 4 \n * 12")
        assert not syntax_tree.collect_errors()
        syntax_tree = parser.parse("3 + 4 \n \n * 12")
        assert not syntax_tree.collect_errors()
        syntax_tree = parser.parse("3 + 4 \n\n * 12")
71
72
        assert not syntax_tree.collect_errors()

73
74
    def test_whitespace_horizontal(self):
        lang = "@ whitespace = horizontal\n" + self.mini_language
75
76
77
78
79
80
81
        parser = compileEBNF(lang)()
        assert parser
        syntax_tree = parser.parse("3 + 4 * 12")
        assert not syntax_tree.collect_errors()
        syntax_tree = parser.parse("3 + 4 \n * 12")
        assert syntax_tree.collect_errors()

82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
class TestEBNFParser:
    def setup(self):
        self.EBNF = EBNFGrammar()

    def test_literal(self):
        snippet = '"literal" '
        result = self.EBNF.parse(snippet, 'literal')
        assert not result.error_flag
        assert str(result) == snippet
        assert result.find(lambda node: str(node) == WHITESPACE_KEYWORD)

        result = self.EBNF.parse(' "literal"', 'literal')
        assert result.error_flag  # literals catch following, but not leading whitespace


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
class TestPopRetrieve:
    mini_language = """
        document       = { text | codeblock }
        codeblock      = delimiter { text | (!:delimiter delimiter_sign) } ::delimiter
        delimiter      = delimiter_sign
        delimiter_sign = /`+/
        text           = /[^`]+/ 
        """

    def setup(self):
        self.minilang_parser = compileEBNF(self.mini_language)()

    def test_compile_mini_language(self):
        assert self.minilang_parser

    def test_single_line(self):
        teststr = "Anfang ```code block `` <- keine Ende-Zeichen ! ``` Ende"
        syntax_tree = self.minilang_parser.parse(teststr)
        assert not syntax_tree.collect_errors()
117
118
119
        delim = str(next(syntax_tree.find(lambda node: node.tag_name == "delimiter")))
        pop = str(next(syntax_tree.find(lambda node: node.tag_name == "Pop")))
        assert delim == pop
120
121
        if WRITE_LOGS:
            syntax_tree.log("test_PopRetrieve_single_line", '.cst')
Eckhart Arnold's avatar
Eckhart Arnold committed
122
            # self.minilang_parser.log_parsing_history("test_PopRetrieve_single_line")
123
124
125
126
127
128
129
130
131
132
133
134

    def test_multi_line(self):
        teststr = """
            Anfang ```code block `` <- keine Ende-Zeichen ! ``` Ebde

            Absatz ohne ``` codeblock, aber
            das stellt sich erst am Ende herause...

            Mehrzeliger ```code block
            """
        syntax_tree = self.minilang_parser.parse(teststr)
        assert not syntax_tree.collect_errors()
135
136
137
        delim = str(next(syntax_tree.find(lambda node: node.tag_name == "delimiter")))
        pop = str(next(syntax_tree.find(lambda node: node.tag_name == "Pop")))
        assert delim == pop
138
139
        if WRITE_LOGS:
            syntax_tree.log("test_PopRetrieve_multi_line", '.cst')
Eckhart Arnold's avatar
Eckhart Arnold committed
140
            # self.minilang_parser.log_parsing_history("test_PopRetrieve_multi_line")
141
142


143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
class TestSemanticValidation:
    def check(self, minilang, bool_filter=lambda x: x):
        grammar = EBNFGrammar()
        st = grammar.parse(minilang)
        assert not st.collect_errors()
        for table in EBNF_ASTPipeline:
            traverse(st, table)
        assert bool_filter(st.collect_errors())

    def test_illegal_nesting(self):
        self.check('impossible = { [ "an optional requirement" ] }')

    def test_illegal_nesting_option_required(self):
        self.check('impossible = [ §"an optional requirement" ]')

    def test_illegal_nesting_oneormore_option(self):
        self.check('impossible = { [ "no use"] }+')

    def test_legal_nesting(self):
        self.check('possible = { [ "+" ] "1" }', lambda x: not x)


class TestCompilerErrors:
    def test_error_propagation(self):
167
168
        ebnf = "@ literalws = wrongvalue  # testing error propagation\n"
        result, messages, st = full_compilation(ebnf, None, EBNFGrammar(), EBNF_ASTPipeline,
169
170
171
172
                                                EBNFCompiler('ErrorPropagationTest'))
        assert messages


173
if __name__ == "__main__":
174
175
    from run import runner
    runner("TestEBNFParser", globals())