1# © 2021 Qualcomm Innovation Center, Inc. All rights reserved.
2#
3# 2019 Cog Systems Pty Ltd.
4#
5# SPDX-License-Identifier: BSD-3-Clause
6
7import re
8
9
10class BaseError(Exception):
11    def get_context(self, text, pos, span=40):
12        start = max(pos - span, 0)
13        end = pos + span
14        before = text[start:pos].rsplit('\n', 1)[-1]
15        after = text[pos:end].split('\n', 1)[0]
16        before_spaces = re.sub(r'\S', ' ', before)
17        return before + after + '\n' + before_spaces + '^\n'
18
19
20class DSLError(BaseError):
21    def __init__(self, message, token, expecting=None, state=None):
22        meta = getattr(token, 'meta', token)
23        self.line = getattr(meta, 'line', '?')
24        self.column = getattr(meta, 'column', '?')
25        self.pos_in_stream = getattr(meta, 'start_pos',
26                                     getattr(meta, 'pos_in_stream', None))
27        self.expecting = expecting
28        self.state = state
29
30        message = "\nError: %s\n" % message
31        message += "\nToken %s, at line %s col %s:\n" % (
32            str(token), self.line, self.column)
33        if isinstance(self.pos_in_stream, int):
34            message += '\n' + self.get_context(token.program,
35                                               self.pos_in_stream)
36        if expecting:
37            message += '\nExpecting: %s\n' % expecting
38
39        super(DSLError, self).__init__(message)
40
41
42class DSLErrorWithRefs(BaseError):
43    def __init__(self, message, token, refs, expecting=None, state=None):
44        meta = getattr(token, 'meta', token)
45        self.line = getattr(meta, 'line', '?')
46        self.column = getattr(meta, 'column', '?')
47        self.pos_in_stream = getattr(meta, 'start_pos',
48                                     getattr(meta, 'pos_in_stream', None))
49        self.expecting = expecting
50        self.state = state
51
52        message = "\nError: %s\n" % message
53        message += "\nAt line %d col %d:\n" % (self.line, self.column)
54        message += '\n' + self.get_context(token.program, self.pos_in_stream)
55
56        message += "\nRefs:"
57        for r in refs:
58            line = getattr(r, 'line', '?')
59            column = getattr(r, 'column', '?')
60            pos = getattr(r, 'pos_in_stream', None)
61            message += "\nAt line %d col %d:\n" % (line, column)
62            message += '\n' + self.get_context(token.program, pos)
63
64        if expecting:
65            message += '\nExpecting: %s\n' % expecting
66
67        super(DSLErrorWithRefs, self).__init__(message)
68
69
70class RangeError(DSLError):
71    pass
72