2.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

compile.py 22.1 KB
Newer Older
eckhart's avatar
mend    
eckhart committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# compile.py - Syntax driven compilation support for DHParser
#
# Copyright 2016  by Eckhart Arnold (arnold@badw.de)
#                 Bavarian Academy of Sciences an Humanities (badw.de)
#
# 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.

"""
Module ``compile`` contains a skeleton class for syntax
driven compilation support. Class ``Compiler`` can serve as base
class for a compiler. Compiler objects
are callable an receive the Abstract syntax tree (AST)
as argument and yield whatever output the compiler produces. In
most Digital Humanities applications this will be
XML-code. However, it can also be anything else, like binary
code or, as in the case of DHParser's EBNF-compiler, Python
source code.

Function ``compile_source`` invokes all stages of the compilation
eckhart's avatar
eckhart committed
30
process, i.e. pre-processing, parsing, CST to AST-transformation
eckhart's avatar
mend    
eckhart committed
31
32
33
34
35
36
and compilation.

See module ``ebnf`` for a sample of the implementation of a
compiler object.
"""

Eckhart Arnold's avatar
Eckhart Arnold committed
37
from collections import namedtuple
eckhart's avatar
eckhart committed
38
import copy
eckhart's avatar
eckhart committed
39
import functools
di68kap's avatar
di68kap committed
40
import os
41
import traceback
Eckhart Arnold's avatar
Eckhart Arnold committed
42
from typing import Any, Optional, Tuple, List, Set, Dict, Union, Callable, cast
eckhart's avatar
mend    
eckhart committed
43

eckhart's avatar
eckhart committed
44
from DHParser.configuration import get_config_value
di68kap's avatar
di68kap committed
45
from DHParser.preprocess import PreprocessorFunc
di68kap's avatar
di68kap committed
46
from DHParser.syntaxtree import Node, RootNode, EMPTY_PTYPE, TreeContext
Eckhart Arnold's avatar
Eckhart Arnold committed
47
from DHParser.transform import TransformerCallable
eckhart's avatar
mend    
eckhart committed
48
from DHParser.parse import Grammar
Eckhart Arnold's avatar
Eckhart Arnold committed
49
from DHParser.preprocess import gen_neutral_srcmap_func
50
51
from DHParser.error import is_error, is_fatal, Error, FATAL, \
    TREE_PROCESSING_CRASH, COMPILER_CRASH, AST_TRANSFORM_CRASH, has_errors
52
from DHParser.log import log_parsing_history, log_ST, is_logging
Eckhart Arnold's avatar
Eckhart Arnold committed
53
from DHParser.toolkit import load_if_file, is_filename
eckhart's avatar
mend    
eckhart committed
54
55


56
57
__all__ = ('CompilerError',
           'Compiler',
58
           'CompilerCallable',
Eckhart Arnold's avatar
Eckhart Arnold committed
59
           'CompilationResult',
60
61
           'compile_source',
           'visitor_name',
62
           'attr_visitor_name',
63
           'process_tree')
eckhart's avatar
eckhart committed
64
65
66


class CompilerError(Exception):
67
68
    """
    Exception raised when an error of the compiler itself is detected.
eckhart's avatar
eckhart committed
69
70
    Compiler errors are not to be confused with errors in the source
    code to be compiled, which do not raise Exceptions but are merely
71
72
    reported as an error.
    """
eckhart's avatar
eckhart committed
73
74
75
    pass


eckhart's avatar
eckhart committed
76
77
def visitor_name(node_name: str) -> str:
    """
78
    Returns the visitor_method name for `node_name`, e.g.::
eckhart's avatar
eckhart committed
79

80
81
    >>> visitor_name('expression')
    'on_expression'
eckhart's avatar
eckhart committed
82
83
84
85
86
    """
    # assert re.match(r'\w+$', node_name)
    return 'on_' + node_name


87
88
89
90
91
92
93
94
95
96
97
98
def attr_visitor_name(attr_name: str) -> str:
    """
    Returns the visitor_method name for `attr_name`, e.g.::

    >>> attr_visitor_name('class')
    'attr_class'
    """
    # assert re.match(r'\w+$', node_name)
    return 'attr_' + attr_name



99
100
101
ROOTNODE_PLACEHOLDER = RootNode()


eckhart's avatar
mend    
eckhart committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
class Compiler:
    """
    Class Compiler is the abstract base class for compilers. Compiler
    objects are callable and take the root node of the abstract
    syntax tree (AST) as argument and return the compiled code in a
    format chosen by the compiler itself.

    Subclasses implementing a compiler must define `on_XXX()`-methods
    for each node name that can occur in the AST where 'XXX' is the
    node's name(for unnamed nodes it is the node's ptype without the
    leading colon ':').

    These compiler methods take the node on which they are run as
    argument. Other than in the AST transformation, which runs depth-first,
    compiler methods are called forward moving starting with the root
    node, and they are responsible for compiling the child nodes
    themselves. This should be done by invoking the `compile(node)`-
    method which will pick the right `on_XXX`-method. It is not
    recommended to call the `on_XXX`-methods directly.

122
    :ivar source: The source text of the AST to be compiled. This needs to be
di68kap's avatar
di68kap committed
123
124
                assigned by the user of the Compiler object - as is done
                by function `compile_source()`
125
    :ivar context:  A list of parent nodes that ends with the currently
eckhart's avatar
mend    
eckhart committed
126
                compiled node.
127
128
    :ivar tree: The root of the abstract syntax tree.
    :ivar finalizers:  A stack of tuples (function, parameters) that will be
129
                called in reverse order after compilation.
eckhart's avatar
eckhart committed
130

131
132
133
134
135
136
    :ivar has_attribute_visitors:  A flag indicating that the class has
                attribute-visitor-methods which are named 'attr_ATTRIBUTENAME'
                and will be called if the currently processed node has one
                or more attributes for which such visitors exist.

    :ivar _dirty_flag:  A flag indicating that the compiler has already been
eckhart's avatar
mend    
eckhart committed
137
138
                called at least once and that therefore all compilation
                variables must be reset when it is called again.
139
    :ivar _debug: A flag indicating that debugging is turned on. The value
140
                for this flag is read before each call of the configuration
141
                (see debugging section in DHParser.configuration).
142
                If debugging is turned on the compiler class raises en
143
144
145
                error if there is an attempt to be compile one and the same
                node a second time..
    :ivar _debug_already_compiled: A set of nodes that have already been compiled.
eckhart's avatar
mend    
eckhart committed
146
147
    """

eckhart's avatar
eckhart committed
148
    def __init__(self):
149
150
        self.has_attribute_visitors = any(field[0:5] == 'attr_' and callable(getattr(self, field))
                                          for field in dir(self))
151
        self.reset()
eckhart's avatar
mend    
eckhart committed
152

153
    def reset(self):
di68kap's avatar
di68kap committed
154
        self.source = ''  # type: str
155
        self.tree = ROOTNODE_PLACEHOLDER   # type: RootNode
di68kap's avatar
di68kap committed
156
        self.context = []  # type: TreeContext
157
        self._None_check = True  # type: bool
eckhart's avatar
mend    
eckhart committed
158
        self._dirty_flag = False
eckhart's avatar
eckhart committed
159
160
        self._debug = get_config_value('debug_compiler')  # type: bool
        self._debug_already_compiled = set()              # type: Set[Node]
Eckhart Arnold's avatar
Eckhart Arnold committed
161
        self.finalizers = []  # type: List[Tuple[Callable, Tuple]]
162
163
164
165
166
167
168
169
170
171
172
173
174
175

    def prepare(self) -> None:
        """
        A preparation method that will be called after everything else has
        been initialized and immediately before compilation starts. This method
        can be overwritten in order to implement preparation tasks.
        """
        pass

    def finalize(self) -> None:
        """
        A finalization method that is called after compilation has finished and
        after all tasks from the finalizers stack have been executed
        """
di68kap's avatar
di68kap committed
176
        pass
eckhart's avatar
mend    
eckhart committed
177

178
    def __call__(self, root: RootNode) -> Any:
eckhart's avatar
mend    
eckhart committed
179
180
181
182
183
184
185
186
        """
        Compiles the abstract syntax tree with the root node `node` and
        returns the compiled code. It is up to subclasses implementing
        the compiler to determine the format of the returned data.
        (This very much depends on the kind and purpose of the
        implemented compiler.)
        """
        if self._dirty_flag:
187
            self.reset()
eckhart's avatar
mend    
eckhart committed
188
        self._dirty_flag = True
eckhart's avatar
eckhart committed
189
        self.tree = root
190
        # self.source = source  # type: str
191
        self.prepare()
192
        result = self.compile(root)
193
194
195
        while self.finalizers:
            task, parameters = self.finalizers.pop()
            task(*parameters)
di68kap's avatar
di68kap committed
196
        self.finalize()
eckhart's avatar
mend    
eckhart committed
197
198
        return result

199
    def fallback_compiler(self, node: Node, block_attribute_visitors: bool=False) -> Any:
eckhart's avatar
mend    
eckhart committed
200
201
202
        """This is a generic compiler function which will be called on
        all those node types for which no compiler method `on_XXX` has
        been defined."""
203
        replacements = {}  # type: Dict[Node, Node]
204
205
206
        if node.children:
            for child in node.children:
                nd = self.compile(child)
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
                if id(nd) != id(child):
                    replacements[id(child)] = nd
                if nd is not None and not isinstance(nd, Node):
                    tn = node.tag_name
                    raise TypeError(
                        'Fallback compiler for Node `%s` received a value of type '
                        '`%s` from child `%s` instead of the required return type `Node`. '
                        'Override `DHParser.compile.Compiler.fallback_compiler()` or add '
                        'method `on_%s(self, node)` in class `%s` to avoid this error!'
                        % (tn, str(type(nd)), child.tag_name, tn, self.__class__.__name__))
            if replacements:
                # replace Nodes the identity of which has been changed during transformation
                # and drop any returned None-results
                result = []
                for child in node.children:
                    nd = replacements.get(id(child), child)
                    if nd is not None and nd.tag_name != EMPTY_PTYPE:
                        result.append(nd)
                node.result = tuple(result)
226
        if self.has_attribute_visitors and not block_attribute_visitors and node.has_attr():
di68kap's avatar
di68kap committed
227
            for attribute, value in node.attr.items():
228
229
230
231
232
                try:
                    attribute_visitor = self.__getattribute__(attr_visitor_name(attribute))
                    node = attribute_visitor(node, value) or node
                except AttributeError:
                    pass
eckhart's avatar
mend    
eckhart committed
233
234
235
236
237
238
239
240
        return node

    def compile(self, node: Node) -> Any:
        """
        Calls the compilation method for the given node and returns the
        result of the compilation.

        The method's name is derived from either the node's parser
241
        name or, if the parser is disposable, the node's parser's class
eckhart's avatar
mend    
eckhart committed
242
243
244
245
246
247
        name by adding the prefix ``on_``.

        Note that ``compile`` does not call any compilation functions
        for the parsers of the sub nodes by itself. Rather, this should
        be done within the compilation methods.
        """
248
249
250
251
        if self._debug:
            assert node not in self._debug_already_compiled
            self._debug_already_compiled.add(node)

252
        elem = node.tag_name
253
        if elem[:1] == ':':
254
            elem = elem[1:] + '__'
eckhart's avatar
eckhart committed
255
256
        try:
            compiler = self.__getattribute__(visitor_name(elem))
di68kap's avatar
di68kap committed
257
            # print(self.__class__.__name__, elem, str(node)[:80])
eckhart's avatar
eckhart committed
258
259
260
261
262
        except AttributeError:
            compiler = self.fallback_compiler
        self.context.append(node)
        result = compiler(node)
        self.context.pop()
263
        if result is None and self._None_check:
264
265
266
267
268
269
270
            raise CompilerError(
                ('Method on_%s returned `None` instead of a valid compilation '
                 'result! It is recommended to use `syntaxtree.EMPTY_NODE as a '
                 'void value. This Error can be turn off by adding '
                 '`self._None_check = False` to the reset()-Method of your'
                 'compiler class, in case on_%s actually SHOULD be allowed to '
                 'return None.') % (elem, elem))
eckhart's avatar
eckhart committed
271
        return result
eckhart's avatar
mend    
eckhart committed
272
273


274
275
276
277
278
279
280
281
def logfile_basename(filename_or_text, function_or_class_or_instance) -> str:
    """Generates a reasonable logfile-name (without extension) based on
    the given information.
    """
    if is_filename(filename_or_text):
        return os.path.basename(os.path.splitext(filename_or_text)[0])
    else:
        try:
di68kap's avatar
di68kap committed
282
            name = function_or_class_or_instance.__qualname__
283
284
285
286
287
288
        except AttributeError:
            name = function_or_class_or_instance.__class__.__name__
        i = name.find('.')
        return name[:i] + '_out' if i >= 0 else name


Eckhart Arnold's avatar
Eckhart Arnold committed
289
CompilerCallable = Union[Compiler, Callable[[RootNode], Any], functools.partial]
eckhart's avatar
eckhart committed
290
291


292
293
294
def filter_stacktrace(stacktrace: List[str]) -> List[str]:
    """Removes those frames from a formatted stacktrace that are located
    within the DHParser-code."""
295
    n = 0
296
297
298
299
300
301
302
303
    for n, frame in enumerate(stacktrace):
        i = frame.find('"')
        k = frame.find('"', i + 1)
        if frame.find("DHParser", i, k) < 0:
            break
    return stacktrace[n:]


304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
def process_tree(tp: CompilerCallable, tree: RootNode) -> Any:
    """Process a tree with the tree-processor `tp` only if no fatal error
    has occurred so far. Catch any Python-exceptions in case
    any normal errors have occurred earlier in the processing pipeline.
    Don't catch Python-exceptions if no errors have occurred earlier.

    This behaviour is based on the assumption that given any non-fatal
    errors have occurred earlier, the tree passed through the pipeline
    might not be in a state that is expected by the later stages, thus if
    an exception occurs it is not really to be considered a programming
    error. Processing stages should be written with possible errors
    occurring in earlier stages in mind, though. However, because it could
    be difficult to provide for all possible kinds of badly structured
    trees resulting from errors, exceptions occurring when processing
    potentially faulty trees will be dealt with gracefully.

    Tree processing should generally be assumed to change the tree
    in place. If the input tree shall be preserved, it is necessary to
    make a deep copy of the input tree, before calling process_tree.
    """
    assert isinstance(tree, RootNode)
    result = None
    if not is_fatal(tree.error_flag):
        if is_error(tree.error_flag):
            # assume Python crashes are merely a consequence of earlier
            # errors, so let's catch them
            try:
                result = tp(tree)
            except Exception as e:
                node = tp.context[-1] if tp.context else tree
                st = traceback.format_list(traceback.extract_tb(e.__traceback__))
                trace = ''.join(filter_stacktrace(st))
                tree.new_error(
                    node, "Tree-processing failed, most likely, due to errors earlier in "
                          "in the processing pipeline. Crash Message: %s: %s\n%s"
                          % (e.__class__.__name__, str(e), trace),
                    TREE_PROCESSING_CRASH)
        else:
            # assume Python crashes are programming mistakes, so let
            # the exceptions through
            result = tp(tree)
    return result


Junction = Tuple[str, CompilerCallable, str]


def process_pipeline(junctions: Set[Junction],
                     source_stages: Dict[str, RootNode],
                     target_stages: Set[str]) -> Dict[str, Any]:
    t_to_j = {j[-1]: j for j in junctions}
    steps = []
    targets = target_stages.copy()
    already_reached = targets.copy()
    while targets:
        steps.append([t_to_j[t] for t in targets if t not in source_stages])
        targets = { j[0] for j in steps[-1] if j[0] not in already_reached }
        already_reached |= targets
        for step in steps[:-1]:
            for j in steps[-1]:
                try:
                    step.remove(j)
                except ValueError:
                    pass
    if not (target_stages <= already_reached):
        raise ValueError(f'Target-stages: {trage_stages - already_reached} '
                         f'cannot be reached with junctions: {junctions}.')
    sources = [j[0] for step in steps for j in step]
    disposables = {s for s in set(sources) if s not in target_stages and souces.count(s) <= 1 }
    steps.reverse()
    results = source_stages.copy()
    for step in steps:
        for junction in step:
            t = junction[-1]
            if t not in results:
                s = junction[0]
                tree = results[s] if source in disposables else copy.deepcopy(results[s])
                if s not in target_stages:
                    sources.remove(s)
                    if sources.count(s) <= 1:
                        disposables.add(s)
                if tree is None:
                    results[t] = None
                else:
                    if not isinstance(tree, RootNode):
                        raise ValueError(f'Object in stage {s} is not a tree but a {type(tree)} '
                                         f'and, therefore, cannot be processed to {t}')
                    results[t] = process_tree(junction[1], tree)
    return {t: results[t] for t in target_stages}


CompilationResult = namedtuple('CompilationResult',
    ['result',      # type: Optional[Any]
     'messages',    # type: List[Error]
     'AST'],        # type: RootNode
    module=__name__)


eckhart's avatar
mend    
eckhart committed
402
def compile_source(source: str,
Eckhart Arnold's avatar
Eckhart Arnold committed
403
404
405
406
                   preprocessor: Optional[PreprocessorFunc],
                   parser: Grammar,
                   transformer: TransformerCallable,
                   compiler: CompilerCallable,
Eckhart Arnold's avatar
Eckhart Arnold committed
407
                   *, preserve_AST: bool = False) -> CompilationResult:
eckhart's avatar
eckhart committed
408
409
    """Compiles a source in four stages:

eckhart's avatar
eckhart committed
410
    1. Pre-Processing (if needed)
eckhart's avatar
mend    
eckhart committed
411
412
413
414
    2. Parsing
    3. AST-transformation
    4. Compiling.

415
416
417
    The later stages AST-transformation, compilation will only be invoked if
    no fatal errors occurred in any of the earlier stages of the processing
    pipeline.
eckhart's avatar
mend    
eckhart committed
418

419
    :param source: The input text for compilation or a the name of a
eckhart's avatar
mend    
eckhart committed
420
            file containing the input text.
421
    :param preprocessor:  text -> text. A preprocessor function
eckhart's avatar
mend    
eckhart committed
422
            or None, if no preprocessor is needed.
423
424
    :param parser:  A parsing function or grammar class
    :param transformer:  A transformation function that takes
eckhart's avatar
mend    
eckhart committed
425
426
            the root-node of the concrete syntax tree as an argument and
            transforms it (in place) into an abstract syntax tree.
427
    :param compiler: A compiler function or compiler class
eckhart's avatar
mend    
eckhart committed
428
            instance
429
    :param preserve_AST: Preserves the AST-tree.
eckhart's avatar
mend    
eckhart committed
430

431
    :returns: The result of the compilation as a 3-tuple
eckhart's avatar
mend    
eckhart committed
432
        (result, errors, abstract syntax tree). In detail:
433

eckhart's avatar
mend    
eckhart committed
434
435
        1. The result as returned by the compiler or ``None`` in case of failure
        2. A list of error or warning messages
eckhart's avatar
eckhart committed
436
        3. The root-node of the abstract syntax tree if `preserve_ast` is True
di68kap's avatar
di68kap committed
437
           or `None` otherwise.
eckhart's avatar
mend    
eckhart committed
438
    """
eckhart's avatar
eckhart committed
439
440
    ast = None  # type: Optional[Node]
    original_text = load_if_file(source)  # type: str
441
    source_name = source if is_filename(source) else 'source'
Eckhart Arnold's avatar
Eckhart Arnold committed
442
443
    if isinstance(compiler, Compiler):
        compiler.source = original_text
444
    log_file_name = logfile_basename(source, compiler) if is_logging() else ''  # type: str
445
446
447
448
449
    if not hasattr(parser, 'free_char_parsefunc__') or parser.history_tracking__:
        # log only for custom parser/transformer/compilers
        log_syntax_trees = get_config_value('log_syntax_trees')
    else:
        log_syntax_trees = set()
450
451
452

    # preprocessing

453
    errors = []
eckhart's avatar
mend    
eckhart committed
454
    if preprocessor is None:
eckhart's avatar
eckhart committed
455
        source_text = original_text  # type: str
Eckhart Arnold's avatar
Eckhart Arnold committed
456
457
        source_mapping = gen_neutral_srcmap_func(source_text, source_name)
            # lambda i: SourceLocation(source_name, 0, i)    # type: SourceMapFunc
eckhart's avatar
mend    
eckhart committed
458
    else:
459
460
        _, source_text, source_mapping, errors = preprocessor(original_text, source_name)

461
    if has_errors(errors, FATAL):
Eckhart Arnold's avatar
Eckhart Arnold committed
462
        return CompilationResult(None, errors, None)
463
464
465

    # parsing

466
    syntax_tree = parser(source_text, source_mapping=source_mapping)  # type: RootNode
467
    for e in errors:  syntax_tree.add_error(None, e)
468
469
    syntax_tree.source = original_text
    syntax_tree.source_mapping = source_mapping
eckhart's avatar
eckhart committed
470
    if 'cst' in log_syntax_trees:
eckhart's avatar
mend    
eckhart committed
471
        log_ST(syntax_tree, log_file_name + '.cst')
472
    if parser.history_tracking__:
eckhart's avatar
mend    
eckhart committed
473
474
        log_parsing_history(parser, log_file_name)

Eckhart Arnold's avatar
Eckhart Arnold committed
475
476
477
    # assert is_error(syntax_tree.error_flag) or str(syntax_tree) == strip_tokens(source_text), \
    #     str(syntax_tree) # Ony valid if neither tokens or whitespace are dropped early

eckhart's avatar
mend    
eckhart committed
478
    result = None
479
480
481
482
483
    if not is_fatal(syntax_tree.error_flag):

        # AST-transformation

        if is_error(syntax_tree.error_flag):
484
            # catch Python exception, because if an error has occurred
485
486
487
488
            # earlier, the syntax tree might not look like expected,
            # which could (fatally) break AST transformations.
            try:
                transformer(syntax_tree)
489
            except Exception as e:
490
491
                syntax_tree.new_error(syntax_tree,
                                      "AST-Transformation failed due to earlier parser errors. "
492
                                      "Crash Message: %s: %s" % (e.__class__.__name__, str(e)),
493
                                      AST_TRANSFORM_CRASH)
494
495
496
        else:
            transformer(syntax_tree)

eckhart's avatar
eckhart committed
497
        if 'ast' in log_syntax_trees:
eckhart's avatar
mend    
eckhart committed
498
            log_ST(syntax_tree, log_file_name + '.ast')
499
500

        if not is_fatal(syntax_tree.error_flag):
501
            if preserve_AST:
eckhart's avatar
eckhart committed
502
                ast = copy.deepcopy(syntax_tree)
503
504
505

            # Compilation

506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
            result = process_tree(compiler, syntax_tree)

            # if is_error(syntax_tree.error_flag):
            #     # assume Python crashes are merely a consequence of earlier
            #     # errors, so let's catch them
            #     try:
            #         result = compiler(syntax_tree)
            #     except Exception as e:
            #         # raise e
            #         node = syntax_tree  # type: Node
            #         if isinstance(compiler, Compiler) and compiler.context:
            #             node = compiler.context[-1]
            #         st = traceback.format_list(traceback.extract_tb(e.__traceback__))
            #         trace = ''.join(filter_stacktrace(st))
            #         syntax_tree.new_error(
            #             node, "Compilation failed, most likely, due to errors earlier "
            #                   "in the processing pipeline. Crash Message: %s: %s\n%s"
            #                   % (e.__class__.__name__, str(e), trace),
            #             COMPILER_CRASH)
            # else:
            #     # assume Python crashes are programming mistakes, so let
            #     # the exceptions through
            #     result = compiler(syntax_tree)
eckhart's avatar
mend    
eckhart committed
529

eckhart's avatar
eckhart committed
530
    messages = syntax_tree.errors_sorted  # type: List[Error]
531
532
    # Obsolete, because RootNode adjusts error locations whenever an error is added:
    # adjust_error_locations(messages, original_text, source_mapping)
Eckhart Arnold's avatar
Eckhart Arnold committed
533
    return CompilationResult(result, messages, ast)
di68kap's avatar
di68kap committed
534
535


eckhart's avatar
eckhart committed
536
537
# TODO: Verify compiler against grammar,
#       i.e. make sure that for all on_X()-methods, `X` is the name of a parser
538
# TODO: AST validation against an ASDL-Specification