LyrikCompiler.py 8.95 KB
Newer Older
1
2
3
4
5
6
7
8
9
#!/usr/bin/python

#######################################################################
#
# SYMBOLS SECTION - Can be edited. Changes will be preserved.
#
#######################################################################


10
from functools import partial
11
12
13
14
15
16
import os
import sys
try:
    import regex as re
except ImportError:
    import re
17
18
19
20
21
from DHParser.toolkit import logging, is_filename, load_if_file    
from DHParser.parsers import Grammar, Compiler, nil_scanner, \
    Lookbehind, Lookahead, Alternative, Pop, Required, Token, \
    Optional, NegativeLookbehind, OneOrMore, RegExp, Retrieve, Sequence, RE, Capture, \
    ZeroOrMore, Forward, NegativeLookahead, mixin_comment, compile_source, \
22
    nop_filter, counterpart_filter, accumulating_filter, ScannerFunc
23
24
25
from DHParser.syntaxtree import Node, traverse, remove_enclosing_delimiters, \
    remove_children_if, reduce_single_child, replace_by_single_child, remove_whitespace, \
    no_operation, remove_expendables, remove_tokens, flatten, is_whitespace, is_expendable, \
26
    collapse, map_content, WHITESPACE_PTYPE, TOKEN_PTYPE, TransformerFunc
27
28
29
30
31
32
33
34
35
36
37


#######################################################################
#
# SCANNER SECTION - Can be edited. Changes will be preserved.
#
#######################################################################

def LyrikScanner(text):
    return text

38
def get_scanner() -> ScannerFunc:
39
40
41
42
43
44
45
46
47
48
49
50
    return LyrikScanner


#######################################################################
#
# PARSER SECTION - Don't edit! CHANGES WILL BE OVERWRITTEN!
#
#######################################################################

class LyrikGrammar(Grammar):
    r"""Parser for a Lyrik source file, with this grammar:
    
51
    gedicht           = bibliographisches { LEERZEILE }+ [serie] §titel §text /\s*/ §ENDE
52
53
54
55
56
57
58
59
    
    bibliographisches = autor §"," [NZ] werk §"," [NZ] ort §"," [NZ] jahr §"."
    autor             = namenfolge [verknüpfung]
    werk              = wortfolge ["." §untertitel] [verknüpfung]
    untertitel        = wortfolge [verknüpfung]
    ort               = wortfolge [verknüpfung]
    jahr              = JAHRESZAHL
    
60
61
62
63
64
    wortfolge         = { WORT }+
    namenfolge        = { NAME }+
    verknüpfung       = "<" ziel ">"
    ziel              = ZEICHENFOLGE
    
65
    serie             = !(titel vers NZ vers) { NZ zeile }+ { LEERZEILE }+
66
67
    
    titel             = { NZ zeile}+ { LEERZEILE }+
68
69
    zeile             = { ZEICHENFOLGE }+
    
70
    text              = { strophe {LEERZEILE} }+
71
72
73
74
75
76
77
78
79
80
81
    strophe           = { NZ vers }+
    vers              = { ZEICHENFOLGE }+
    
    WORT              = /\w+/~
    NAME              = /\w+\.?/~
    ZEICHENFOLGE      = /[^ \n<>]+/~
    NZ                = /\n/~
    LEERZEILE         = /\n[ \t]*(?=\n)/~
    JAHRESZAHL        = /\d\d\d\d/~
    ENDE              = !/./
    """
82
    source_hash__ = "7a99fa77a7d2b81976293d54696eb4f3"
83
84
85
86
87
88
89
90
91
92
93
94
95
96
    parser_initialization__ = "upon instatiation"
    COMMENT__ = r''
    WSP__ = mixin_comment(whitespace=r'[\t ]*', comment=r'')
    wspL__ = ''
    wspR__ = WSP__
    ENDE = NegativeLookahead(RE('.', wR=''))
    JAHRESZAHL = RE('\\d\\d\\d\\d')
    LEERZEILE = RE('\\n[ \\t]*(?=\\n)')
    NZ = RE('\\n')
    ZEICHENFOLGE = RE('[^ \\n<>]+')
    NAME = RE('\\w+\\.?')
    WORT = RE('\\w+')
    vers = OneOrMore(ZEICHENFOLGE)
    strophe = OneOrMore(Sequence(NZ, vers))
97
    text = OneOrMore(Sequence(strophe, ZeroOrMore(LEERZEILE)))
98
    zeile = OneOrMore(ZEICHENFOLGE)
99
    titel = Sequence(OneOrMore(Sequence(NZ, zeile)), OneOrMore(LEERZEILE))
100
    serie = Sequence(NegativeLookahead(Sequence(titel, vers, NZ, vers)), OneOrMore(Sequence(NZ, zeile)), OneOrMore(LEERZEILE))
101
102
103
104
    ziel = ZEICHENFOLGE
    verknüpfung = Sequence(Token("<"), ziel, Token(">"))
    namenfolge = OneOrMore(NAME)
    wortfolge = OneOrMore(WORT)
105
106
107
108
109
    jahr = JAHRESZAHL
    ort = Sequence(wortfolge, Optional(verknüpfung))
    untertitel = Sequence(wortfolge, Optional(verknüpfung))
    werk = Sequence(wortfolge, Optional(Sequence(Token("."), Required(untertitel))), Optional(verknüpfung))
    autor = Sequence(namenfolge, Optional(verknüpfung))
110
111
    bibliographisches = Sequence(autor, Required(Token(",")), Optional(NZ), werk, Required(Token(",")), Optional(NZ), ort, Required(Token(",")), Optional(NZ), jahr, Required(Token(".")))
    gedicht = Sequence(bibliographisches, OneOrMore(LEERZEILE), Optional(serie), Required(titel), Required(text), RE('\\s*', wR=''), Required(ENDE))
112
    root__ = gedicht
113
    
114
def get_grammar() -> LyrikGrammar:
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
    global thread_local_Lyrik_grammar_singleton
    try:
        grammar = thread_local_Lyrik_grammar_singleton
        return grammar
    except NameError:
        thread_local_Lyrik_grammar_singleton = LyrikGrammar()
        return thread_local_Lyrik_grammar_singleton


#######################################################################
#
# AST SECTION - Can be edited. Changes will be preserved.
#
#######################################################################

Lyrik_AST_transformation_table = {
    # AST Transformations for the Lyrik-grammar
    "gedicht": no_operation,
    "bibliographisches": no_operation,
    "autor": no_operation,
    "werk": no_operation,
    "untertitel": no_operation,
    "ort": no_operation,
    "jahr": no_operation,
139
140
141
142
    "wortfolge": no_operation,
    "namenfolge": no_operation,
    "verknüpfung": no_operation,
    "ziel": no_operation,
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
    "serie": no_operation,
    "titel": no_operation,
    "zeile": no_operation,
    "text": no_operation,
    "strophe": no_operation,
    "vers": no_operation,
    "WORT": no_operation,
    "NAME": no_operation,
    "ZEICHENFOLGE": no_operation,
    "NZ": no_operation,
    "LEERZEILE": no_operation,
    "JAHRESZAHL": no_operation,
    "ENDE": no_operation,
    "*": no_operation
}

LyrikTransform = partial(traverse, processing_table=Lyrik_AST_transformation_table)


162
def get_transformer() -> TransformerFunc:
163
164
165
166
167
168
169
170
171
    return LyrikTransform


#######################################################################
#
# COMPILER SECTION - Can be edited. Changes will be preserved.
#
#######################################################################

172
class LyrikCompiler(Compiler):
173
174
175
176
177
178
179
    """Compiler for the abstract-syntax-tree of a Lyrik source file.
    """

    def __init__(self, grammar_name="Lyrik", grammar_source=""):
        super(LyrikCompiler, self).__init__(grammar_name, grammar_source)
        assert re.match('\w+\Z', grammar_name)

180
    def on_gedicht(self, node):
181
182
        return node

183
    def on_bibliographisches(self, node):
184
185
        pass

186
    def on_autor(self, node):
187
188
        pass

189
    def on_werk(self, node):
190
191
        pass

192
    def on_untertitel(self, node):
193
194
        pass

195
    def on_ort(self, node):
196
197
        pass

198
    def on_jahr(self, node):
199
200
        pass

201
    def on_wortfolge(self, node):
202
203
        pass

204
    def on_namenfolge(self, node):
205
206
        pass

207
    def on_verknüpfung(self, node):
208
209
        pass

210
    def on_ziel(self, node):
211
212
        pass

213
    def on_serie(self, node):
214
215
        pass

216
    def on_titel(self, node):
217
218
        pass

219
    def on_zeile(self, node):
220
221
        pass

222
    def on_text(self, node):
223
224
        pass

225
    def on_strophe(self, node):
226
227
        pass

228
    def on_vers(self, node):
229
230
        pass

231
    def on_WORT(self, node):
232
233
        pass

234
    def on_NAME(self, node):
235
236
        pass

237
    def on_ZEICHENFOLGE(self, node):
238
239
        pass

240
    def on_NZ(self, node):
241
242
        pass

243
    def on_LEERZEILE(self, node):
244
245
        pass

246
    def on_JAHRESZAHL(self, node):
247
248
        pass

249
    def on_ENDE(self, node):
250
251
252
        pass


253
def get_compiler(grammar_name="Lyrik", grammar_source="") -> LyrikCompiler:
254
255
256
257
258
259
260
261
    global thread_local_Lyrik_compiler_singleton
    try:
        compiler = thread_local_Lyrik_compiler_singleton
        compiler.set_grammar_name(grammar_name, grammar_source)
        return compiler
    except NameError:
        thread_local_Lyrik_compiler_singleton = \
            LyrikCompiler(grammar_name, grammar_source)
262
263
        return thread_local_Lyrik_compiler_singleton 

264

265
#######################################################################
266
267
268
269
270
271
272
273
274
275
276
277
278
#
# END OF DHPARSER-SECTIONS
#
#######################################################################


def compile_src(source):
    """Compiles ``source`` and returns (result, errors, ast).
    """
    with logging("LOGS"):
        compiler = get_compiler()
        cname = compiler.__class__.__name__
        log_file_name = os.path.basename(os.path.splitext(source)[0]) \
279
280
            if is_filename(source) < 0 else cname[:cname.find('.')] + '_out'    
        result = compile_source(source, get_scanner(), 
281
282
283
284
285
286
                                get_grammar(),
                                get_transformer(), compiler)
    return result


if __name__ == "__main__":
287
    if len(sys.argv) == 1:
288
        sys.argv.append("Lyrisches_Intermezzo_IV.txt")
289
290
291
292
293
294
295
296
297
298
    if len(sys.argv) > 1:
        result, errors, ast = compile_src(sys.argv[1])
        if errors:
            for error in errors:
                print(error)
            sys.exit(1)
        else:
            print(result)
    else:
        print("Usage: LyrikCompiler.py [FILENAME]")