MLW_compiler.py 16.7 KB
Newer Older
di68kap's avatar
di68kap committed
1
#!/usr/bin/python
2
3
4
5
6
7
8

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

9

di68kap's avatar
di68kap committed
10
from functools import partial
di68kap's avatar
di68kap committed
11
import os
di68kap's avatar
di68kap committed
12
13
14
15
16
import sys
try:
    import regex as re
except ImportError:
    import re
di68kap's avatar
di68kap committed
17
from DHParser.toolkit import logging, is_filename, load_if_file    
di68kap's avatar
di68kap committed
18
19
20
from DHParser.parsers import GrammarBase, CompilerBase, nil_scanner, \
    Lookbehind, Lookahead, Alternative, Pop, Required, Token, \
    Optional, NegativeLookbehind, OneOrMore, RegExp, Retrieve, Sequence, RE, Capture, \
Eckhart Arnold's avatar
Eckhart Arnold committed
21
    ZeroOrMore, Forward, NegativeLookahead, mixin_comment, compile_source
22
23
24
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, \
25
    WHITESPACE_PTYPE, TOKEN_PTYPE, replace_parser
di68kap's avatar
di68kap committed
26

27

28
29
30
31
32
33
34

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

35
36
37
def MLWScanner(text):
    return text

di68kap's avatar
di68kap committed
38
39
40
def get_MLW_scanner():
    return MLWScanner

41

42
43
44
45
46
#######################################################################
#
# PARSER SECTION - Don't edit! CHANGES WILL BE OVERWRITTEN!
#
#######################################################################
47

48
class MLWGrammar(GrammarBase):
49
50
51
52
53
    r"""Parser for a MLW source file, with this grammar:
    
    # EBNF-Syntax für MLW-Artikel
    
    @ comment       =  /#.*(?:\n|$)/    # Kommentare beginnen mit '#' und reichen bis zum Zeilenende
54
    @ whitespace    =  /[\t ]*/         # Zeilensprünge zählen nicht als Leerraum
55
56
57
58
59
60
61
62
63
64
65
    @ literalws     =  both             # Leerraum vor und nach Literalen wird automatisch entfernt
    
    Artikel         = [LEER]
                      §LemmaPosition  [ArtikelKopf]  §BedeutungsPosition  §Autorinfo
                      [LEER]  DATEI_ENDE
    
    
    #### LEMMA-POSITION ##########################################################
    
    LemmaPosition   = "LEMMA"  §Lemma  [LemmaVarianten]  §GrammatikPosition
    
di68kap's avatar
di68kap committed
66
    Lemma           = [_tll]  WORT_KLEIN [LEER]
67
68
    _tll            = "*"
    
di68kap's avatar
di68kap committed
69
    LemmaVarianten  = "VARIANTEN" [LEER] §LVariante  { TRENNER LVariante }
70
                      [TRENNER LVZusatz] [TRENNER]
di68kap's avatar
di68kap committed
71
72
    LVariante       = ~/(?:[a-z]|-)+/~      # Buchstabenfolge mit Trennzeichen "-"
    LVZusatz        = "ZUSATZ" "sim."
73
74
75
76
77
    
    
    
    #### GRAMMATIK-POSITION ######################################################
    
78
79
    GrammatikPosition = "GRAMMATIK" [LEER] §_wortart §TRENNER §Flexionen [_genus]
                        {GrammatikVarianten} [TRENNER]
80
81
82
83
84
85
    
    _wortart        = "nomen"  | "n." |
                      "verb"   | "v." |
                      "adverb" | "adv." |
                      "adjektiv" | "adj."
    
86
    GrammatikVarianten = TRENNER GVariante
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    GVariante       = Flexionen  [_genus]  ":"  Beleg
    
    Flexionen       = Flexion { "," §Flexion }
    Flexion         = /-?[a-z]+/~
    
    _genus          = "maskulinum" | "m." |
                      "femininum" | "f." |
                      "neutrum" | "n."
    
    
    
    #### ARTIKEL-KOPF ############################################################
    
    ArtikelKopf     = SchreibweisenPosition
101
102
    SchreibweisenPosition =  "SCHREIBWEISE" [LEER] §SWTyp ":" [LEER]
                             §SWVariante { TRENNER SWVariante} [LEER]
103
104
105
106
107
    SWTyp           = "script." | "script. fat-"
    SWVariante      = Schreibweise ":" Beleg
    Schreibweise    = "vizreg-" | "festregel(a)" | "fezdregl(a)" | "fat-"
    
    Beleg           = Verweis
108
    Verweis         = ~/\w+/~
109
110
111
112
113
    VerweisZiel     = ~/<\w+>/~
    
    
    #### BEDEUTUNGS-POSITION #####################################################
    
di68kap's avatar
di68kap committed
114
    BedeutungsPosition = { "BEDEUTUNG" [LEER] §Bedeutung }+
115
    
116
    Bedeutung       = (Interpretamente | Bedeutungskategorie) [Belege]
117
118
    Bedeutungskategorie = /(?:(?![A-ZÄÖÜ][A-ZÄÖÜ]).)+/~ [LEER]
    Interpretamente = LateinischeBedeutung [LEER] §DeutscheBedeutung [LEER]
119
120
    LateinischeBedeutung = "LAT" /(?:(?![A-ZÄÖÜ][A-ZÄÖÜ]).)+/~
    DeutscheBedeutung = "DEU" /(?:(?![A-ZÄÖÜ][A-ZÄÖÜ]).)+/~
121
    Belege          = "BELEGE" [LEER] { "*" EinBeleg }
122
123
    EinBeleg        = { !([LEER] ("*" | "BEDEUTUNG" | "AUTOR" | "NAME" | "ZUSATZ"))
                        /\s*.*\s*/ }+
di68kap's avatar
di68kap committed
124
                      [Zusatz]
di68kap's avatar
di68kap committed
125
    Zusatz          = "ZUSATZ" /\s*.*/ TRENNER
126
127
128
129
130
    
    
    #### AUTOR/AUTORIN ###########################################################
    
    Autorinfo       = ("AUTORIN" | "AUTOR") Name
131
132
    Name            = WORT { WORT | NAMENS_ABKÜRZUNG }
    
133
    
134
    #### ATOMARE AUSDRÜCKE #######################################################
135
    
136
    NAMENS_ABKÜRZUNG = /[A-ZÄÖÜÁÀ]\./
137
    
138
139
140
141
142
    WORT             = /[A-ZÄÖÜ]?[a-zäöüß]+/~
    WORT_GROSS       = /[A-ZÄÖÜ][a-zäöüß]+/~
    WORT_KLEIN       = /[a-zäöüß]+/~
    LAT_WORT         = /[a-z]+/~
    GROSSSCHRIFT     = /[A-ZÄÖÜ]+/~
143
    
144
145
    TRENNER          = /\s*;\s*/ | { ZSPRUNG }+
    ZSPRUNG          = /\n/~
di68kap's avatar
di68kap committed
146
    
147
148
149
    LEER             = /\s+/        # horizontaler und(!) vertikaler Leerraum
    DATEI_ENDE       = !/./
    NIEMALS          = /(?!.)/
150
    """
151
    source_hash__ = "5e5f53f5ef36706df8dc1ec0ecd73859"
152
    parser_initialization__ = "upon instatiation"
153
154
155
156
    COMMENT__ = r'#.*(?:\n|$)'
    WSP__ = mixin_comment(whitespace=r'[\t ]*', comment=r'#.*(?:\n|$)')
    wspL__ = WSP__
    wspR__ = WSP__
157
158
    NIEMALS = RE('(?!.)', wR='', wL='')
    DATEI_ENDE = NegativeLookahead(RE('.', wR='', wL=''))
di68kap's avatar
di68kap committed
159
160
161
    LEER = RE('\\s+', wR='', wL='')
    ZSPRUNG = RE('\\n', wL='')
    TRENNER = Alternative(RE('\\s*;\\s*', wR='', wL=''), OneOrMore(ZSPRUNG))
162
163
164
165
166
    GROSSSCHRIFT = RE('[A-ZÄÖÜ]+', wL='')
    LAT_WORT = RE('[a-z]+', wL='')
    WORT_KLEIN = RE('[a-zäöüß]+', wL='')
    WORT_GROSS = RE('[A-ZÄÖÜ][a-zäöüß]+', wL='')
    WORT = RE('[A-ZÄÖÜ]?[a-zäöüß]+', wL='')
167
168
    NAMENS_ABKÜRZUNG = RE('[A-ZÄÖÜÁÀ]\\.', wR='', wL='')
    Name = Sequence(WORT, ZeroOrMore(Alternative(WORT, NAMENS_ABKÜRZUNG)))
169
    Autorinfo = Sequence(Alternative(Token("AUTORIN"), Token("AUTOR")), Name)
di68kap's avatar
di68kap committed
170
    Zusatz = Sequence(Token("ZUSATZ"), RE('\\s*.*', wR='', wL=''), TRENNER)
171
    EinBeleg = Sequence(OneOrMore(Sequence(NegativeLookahead(Sequence(Optional(LEER), Alternative(Token("*"), Token("BEDEUTUNG"), Token("AUTOR"), Token("NAME"), Token("ZUSATZ")))), RE('\\s*.*\\s*', wR='', wL=''))), Optional(Zusatz))
172
    Belege = Sequence(Token("BELEGE"), Optional(LEER), ZeroOrMore(Sequence(Token("*"), EinBeleg)))
173
174
    DeutscheBedeutung = Sequence(Token("DEU"), RE('(?:(?![A-ZÄÖÜ][A-ZÄÖÜ]).)+', wL=''))
    LateinischeBedeutung = Sequence(Token("LAT"), RE('(?:(?![A-ZÄÖÜ][A-ZÄÖÜ]).)+', wL=''))
175
176
    Interpretamente = Sequence(LateinischeBedeutung, Optional(LEER), Required(DeutscheBedeutung), Optional(LEER))
    Bedeutungskategorie = Sequence(RE('(?:(?![A-ZÄÖÜ][A-ZÄÖÜ]).)+', wL=''), Optional(LEER))
177
    Bedeutung = Sequence(Alternative(Interpretamente, Bedeutungskategorie), Optional(Belege))
di68kap's avatar
di68kap committed
178
    BedeutungsPosition = OneOrMore(Sequence(Token("BEDEUTUNG"), Optional(LEER), Required(Bedeutung)))
179
    VerweisZiel = RE('<\\w+>')
180
    Verweis = RE('\\w+')
181
    Beleg = Verweis
182
183
184
    Schreibweise = Alternative(Token("vizreg-"), Token("festregel(a)"), Token("fezdregl(a)"), Token("fat-"))
    SWVariante = Sequence(Schreibweise, Token(":"), Beleg)
    SWTyp = Alternative(Token("script."), Token("script. fat-"))
185
    SchreibweisenPosition = Sequence(Token("SCHREIBWEISE"), Optional(LEER), Required(SWTyp), Token(":"), Optional(LEER), Required(SWVariante), ZeroOrMore(Sequence(TRENNER, SWVariante)), Optional(LEER))
186
    ArtikelKopf = SchreibweisenPosition
187
    _genus = Alternative(Token("maskulinum"), Token("m."), Token("femininum"), Token("f."), Token("neutrum"), Token("n."))
188
    Flexion = RE('-?[a-z]+', wL='')
189
190
    Flexionen = Sequence(Flexion, ZeroOrMore(Sequence(Token(","), Required(Flexion))))
    GVariante = Sequence(Flexionen, Optional(_genus), Token(":"), Beleg)
191
    GrammatikVarianten = Sequence(TRENNER, GVariante)
192
    _wortart = Alternative(Token("nomen"), Token("n."), Token("verb"), Token("v."), Token("adverb"), Token("adv."), Token("adjektiv"), Token("adj."))
193
    GrammatikPosition = Sequence(Token("GRAMMATIK"), Optional(LEER), Required(_wortart), Required(TRENNER), Required(Flexionen), Optional(_genus), ZeroOrMore(GrammatikVarianten), Optional(TRENNER))
di68kap's avatar
di68kap committed
194
    LVZusatz = Sequence(Token("ZUSATZ"), Token("sim."))
195
    LVariante = RE('(?:[a-z]|-)+')
196
    LemmaVarianten = Sequence(Token("VARIANTEN"), Optional(LEER), Required(LVariante), ZeroOrMore(Sequence(TRENNER, LVariante)), Optional(Sequence(TRENNER, LVZusatz)), Optional(TRENNER))
197
    _tll = Token("*")
di68kap's avatar
di68kap committed
198
    Lemma = Sequence(Optional(_tll), WORT_KLEIN, Optional(LEER))
199
    LemmaPosition = Sequence(Token("LEMMA"), Required(Lemma), Optional(LemmaVarianten), Required(GrammatikPosition))
200
201
202
    Artikel = Sequence(Optional(LEER), Required(LemmaPosition), Optional(ArtikelKopf), Required(BedeutungsPosition), Required(Autorinfo), Optional(LEER), DATEI_ENDE)
    root__ = Artikel
    
di68kap's avatar
di68kap committed
203
204
205
206
207
208
209
210
211
def get_MLW_grammar():
    global thread_local_MLW_grammar_singleton
    try:
        grammar = thread_local_MLW_grammar_singleton
        return grammar
    except NameError:
        thread_local_MLW_grammar_singleton = MLWGrammar()
        return thread_local_MLW_grammar_singleton

212

213
214
215
216
217
#######################################################################
#
# AST SECTION - Can be edited. Changes will be preserved.
#
#######################################################################
218

Eckhart Arnold's avatar
Eckhart Arnold committed
219
220
221
222
223
224
225
226
227
228
229
def join_strings(node, delimiter='\n'):
    new_result = []
    n = 0
    while n < len(node.result):
        nd = node.result[n]
        if not nd.children:
            a = n
            n += 1
            while n < len(node.result) and not node.result[n].children:
                n += 1
            nd.result = delimiter.join((r.result for r in node.result[a:n]))
di68kap's avatar
di68kap committed
230
231
        elif nd.parser.name != "Zusatz":
            raise AssertionError(nd.as_sexpr())
232
        else:
di68kap's avatar
di68kap committed
233
            n += 1
Eckhart Arnold's avatar
Eckhart Arnold committed
234
235
236
        new_result.append(nd)
    node.result = tuple(new_result)

237
MLW_AST_transformation_table = {
238
    # AST Transformations for the MLW-grammar
di68kap's avatar
di68kap committed
239
    "Artikel": no_operation,
240
241
    "LemmaPosition":
        [partial(remove_tokens, tokens={'LEMMA'})],
di68kap's avatar
di68kap committed
242
    "Lemma": no_operation,
243
    "_tll, _wortart, _genus":
244
245
246
247
        [remove_expendables, reduce_single_child],
    "LemmaVarianten":
        [partial(remove_tokens, tokens={'VARIANTEN'}), flatten,
         partial(remove_tokens, tokens={',', ';'})],
Eckhart Arnold's avatar
Eckhart Arnold committed
248
    "LVariante, LVZusatz, Schreibweise, Name":
249
        [remove_expendables, reduce_single_child],
250
251
    "SWVariante":
        [remove_expendables, partial(remove_tokens, tokens={':'})],
252
253
254
255
256
257
258
259
260
261
    "GrammatikPosition":
        [partial(remove_tokens, tokens={'GRAMMATIK', ';'}), flatten],
    "GrammatikVarianten":
        [partial(remove_tokens, tokens={';'}), replace_by_single_child],
    "GVariante":
        [partial(remove_tokens, tokens={':'})],
    "Flexionen":
        [flatten, partial(remove_tokens, tokens={',', ';'})],
    "Flexion, Verweis":
        [remove_expendables, reduce_single_child],
262
263
    "Zusatz":
        [remove_expendables, remove_tokens, reduce_single_child],
di68kap's avatar
di68kap committed
264
    "ArtikelKopf": no_operation,
265
266
267
    "SchreibweisenPosition":
        [partial(remove_tokens, tokens={'SCHREIBWEISE', ':'}),
         flatten, partial(remove_tokens, tokens={','})],
di68kap's avatar
di68kap committed
268
    "SWTyp": no_operation,
269
270
    "BedeutungsPosition":
        [flatten, partial(remove_tokens, tokens={'BEDEUTUNG'})],
di68kap's avatar
di68kap committed
271
272
273
    "Bedeutung": no_operation,
    "Bedeutungskategorie": no_operation,
    "Interpretamente": no_operation,
Eckhart Arnold's avatar
Eckhart Arnold committed
274
275
276
277
278
    "LateinischeBedeutung, DeutscheBedeutung":
        [remove_expendables, remove_tokens, reduce_single_child],
    "Belege":
        [flatten, remove_tokens],
    "EinBeleg":
279
        [flatten, remove_expendables, join_strings, reduce_single_child],
di68kap's avatar
di68kap committed
280
281
    "Beleg": no_operation,
    "VerweisZiel": no_operation,
282
283
    "Autorinfo":
        [partial(remove_tokens, tokens={'AUTORIN', 'AUTOR'})],
284
285
    "WORT, WORT_KLEIN, WORT_GROSS, GROSSSCHRIFT":
        [remove_expendables, reduce_single_child],
286
    "LEER, TRENNER, ZSPRUNG": partial(replace_parser, name='', ptype=WHITESPACE_PTYPE),
di68kap's avatar
di68kap committed
287
288
    "DATEI_ENDE": no_operation,
    "NIEMALS": no_operation,
289
    (TOKEN_PTYPE, WHITESPACE_PTYPE):
290
        [remove_expendables, reduce_single_child],
291
    "+":
292
293
294
        remove_expendables,
    "~":
        partial(remove_tokens, tokens={',', ';'}),
295
    "*":
296
297
298
        [remove_expendables, replace_by_single_child]
}

di68kap's avatar
di68kap committed
299

300
301
MLWTransform = partial(traverse, processing_table=MLW_AST_transformation_table)

di68kap's avatar
di68kap committed
302

di68kap's avatar
di68kap committed
303
304
305
306
def get_MLW_transformer():
    return MLWTransform


307
308
309
310
311
#######################################################################
#
# COMPILER SECTION - Can be edited. Changes will be preserved.
#
#######################################################################
312
313
314
315
316

class MLWCompiler(CompilerBase):
    """Compiler for the abstract-syntax-tree of a MLW source file.
    """

di68kap's avatar
di68kap committed
317
318
    def __init__(self, grammar_name="MLW", grammar_source=""):
        super(MLWCompiler, self).__init__(grammar_name, grammar_source)
319
320
        assert re.match('\w+\Z', grammar_name)

321
    def on_Artikel(self, node):
322
323
        return node

324
325
326
327
    def on_LemmaPosition(self, node):
        pass

    def on_Lemma(self, node):
328
329
        pass

330
    def on__tll(self, node):
331
332
        pass

333
    def on_LemmaVarianten(self, node):
334
335
        pass

336
    def on_LVariante(self, node):
337
338
        pass

339
    def on_LVZusatz(self, node):
340
341
        pass

342
    def on_GrammatikPosition(self, node):
343
344
        pass

345
    def on__wortart(self, node):
346
347
        pass

348
    def on_GrammatikVarianten(self, node):
349
350
        pass

351
    def on_GVariante(self, node):
352
353
        pass

354
    def on_Flexionen(self, node):
355
356
        pass

357
    def on_Flexion(self, node):
358
359
        pass

360
    def on__genus(self, node):
361
362
        pass

363
    def on_ArtikelKopf(self, node):
364
365
        pass

366
    def on_SchreibweisenPosition(self, node):
367
368
        pass

369
    def on_SWTyp(self, node):
370
371
        pass

372
    def on_SWVariante(self, node):
373
374
        pass

375
    def on_Schreibweise(self, node):
376
377
        pass

378
    def on_Beleg(self, node):
379
380
        pass

381
    def on_Verweis(self, node):
382
383
        pass

384
    def on_VerweisZiel(self, node):
385
386
        pass

387
    def on_BedeutungsPosition(self, node):
388
389
        pass

390
    def on_Bedeutung(self, node):
391
392
        pass

393
    def on_Bedeutungskategorie(self, node):
394
395
        pass

396
    def on_Interpretamente(self, node):
397
398
        pass

399
    def on_LateinischeBedeutung(self, node):
400
401
        pass

402
    def on_DeutscheBedeutung(self, node):
403
404
        pass

405
    def on_Belege(self, node):
406
407
        pass

408
    def on_EinBeleg(self, node):
409
410
        pass

411
    def on_Zusatz(self, node):
412
413
        pass

414
    def on_Autorinfo(self, node):
415
416
        pass

417
    def on_Name(self, node):
418
419
        pass

420
    def on_NAMENS_ABKÜRZUNG(self, node):
421
422
        pass

423
    def on_WORT(self, node):
424
425
        pass

426
    def on_WORT_GROSS(self, node):
427
428
        pass

429
    def on_WORT_KLEIN(self, node):
430
431
        pass

432
    def on_LAT_WORT(self, node):
433
434
        pass

435
    def on_GROSSSCHRIFT(self, node):
436
437
        pass

438
    def on_TRENNER(self, node):
di68kap's avatar
di68kap committed
439
440
        pass

441
    def on_ZSPRUNG(self, node):
di68kap's avatar
di68kap committed
442
443
        pass

444
    def on_LEER(self, node):
445
446
        pass

447
    def on_DATEI_ENDE(self, node):
448
449
        pass

450
    def on_NIEMALS(self, node):
451
452
        pass

453

di68kap's avatar
di68kap committed
454
455
456
457
458
459
460
461
462
463
464
465
466
def get_MLW_compiler(grammar_name="MLW", 
                        grammar_source=""):
    global thread_local_MLW_compiler_singleton
    try:
        compiler = thread_local_MLW_compiler_singleton
        compiler.set_grammar_name(grammar_name, grammar_source)
        return compiler
    except NameError:
        thread_local_MLW_compiler_singleton = \
            MLWCompiler(grammar_name, grammar_source)
        return thread_local_MLW_compiler_singleton 


467
468
#######################################################################
#
di68kap's avatar
di68kap committed
469
# END OF DHPARSER-SECTIONS
470
471
472
#
#######################################################################

473

di68kap's avatar
di68kap committed
474
475
476
def compile_MLW(source):
    """Compiles ``source`` and returns (result, errors, ast).
    """
di68kap's avatar
di68kap committed
477
478
479
480
481
482
483
484
485
486
    with logging("LOGS"):
        compiler = get_MLW_compiler()
        cname = compiler.__class__.__name__
        log_file_name = os.path.basename(os.path.splitext(source)[0]) \
            if is_filename(source) < 0 else cname[:cname.find('.')] + '_out'    
        result = compile_source(source, get_MLW_scanner(), 
                                get_MLW_grammar(),
                                get_MLW_transformer(), compiler)
    return result

di68kap's avatar
di68kap committed
487
488
489
490
491
492
493
494
495
496
497

if __name__ == "__main__":
    if len(sys.argv) > 1:
        result, errors, ast = compile_MLW(sys.argv[1])
        if errors:
            for error in errors:
                print(error)
                sys.exit(1)
        else:
            print(result)
    else:
498
        print("Usage: MLW_compiler.py [FILENAME]")