153
|
1 from collections import namedtuple
|
312
|
2 import logging
|
153
|
3
|
312
|
4 """
|
|
5 Error handling routines
|
|
6 Diagnostic utils
|
|
7 Source location structures
|
|
8 """
|
287
|
9
|
191
|
10 # Token is used in the lexical analyzer:
|
194
|
11 class Token:
|
|
12 def __init__(self, typ, val, loc=None):
|
|
13 self.typ = typ
|
|
14 self.val = val
|
|
15 if loc is None:
|
287
|
16 loc = SourceLocation('', 0, 0, 0)
|
194
|
17 assert type(loc) is SourceLocation
|
|
18 self.loc = loc
|
292
|
19
|
194
|
20 def __repr__(self):
|
|
21 return 'Token({0}, {1})'.format(self.typ, self.val)
|
191
|
22
|
287
|
23
|
163
|
24 class SourceLocation:
|
287
|
25 def __init__(self, filename, row, col, ln):
|
|
26 self.filename = filename
|
249
|
27 self.row = row
|
|
28 self.col = col
|
|
29 self.length = ln
|
|
30
|
|
31 def __repr__(self):
|
293
|
32 return '{}, {}, {}'.format(self.filename, self.row, self.col)
|
163
|
33
|
287
|
34
|
153
|
35 SourceRange = namedtuple('SourceRange', ['p1', 'p2'])
|
312
|
36
|
|
37
|
|
38 class CompilerError(Exception):
|
|
39 def __init__(self, msg, loc=None):
|
|
40 self.msg = msg
|
|
41 self.loc = loc
|
|
42 if loc:
|
|
43 assert type(loc) is SourceLocation, \
|
|
44 '{0} must be SourceLocation'.format(type(loc))
|
|
45 self.row = loc.row
|
|
46 self.col = loc.col
|
|
47 else:
|
|
48 self.row = self.col = 0
|
|
49
|
|
50 def __repr__(self):
|
|
51 return '"{}"'.format(self.msg)
|
|
52
|
|
53
|
|
54 class DiagnosticsManager:
|
|
55 def __init__(self):
|
|
56 self.diags = []
|
|
57 self.sources = {}
|
|
58 self.logger = logging.getLogger('diagnostics')
|
|
59
|
|
60 def addSource(self, name, src):
|
334
|
61 self.logger.debug('Adding source {}'.format(name))
|
312
|
62 self.sources[name] = src
|
|
63
|
|
64 def addDiag(self, d):
|
|
65 #self.logger.warning(str(d.msg))
|
|
66 self.diags.append(d)
|
|
67
|
|
68 def error(self, msg, loc):
|
|
69 self.addDiag(CompilerError(msg, loc))
|
|
70
|
|
71 def clear(self):
|
|
72 del self.diags[:]
|
|
73 self.sources.clear()
|
|
74
|
|
75 def printErrors(self):
|
|
76 if len(self.diags) > 0:
|
|
77 print('{0} Errors'.format(len(self.diags)))
|
|
78 for d in self.diags:
|
|
79 self.printError(d)
|
|
80
|
|
81 def printError(self, e):
|
|
82 def printLine(row, txt):
|
|
83 print(str(row)+':'+txt)
|
|
84 print('==============')
|
|
85 if not e.loc:
|
|
86 print('Error: {0}'.format(e))
|
|
87 else:
|
|
88 if e.loc.filename not in self.sources:
|
|
89 print('Error: {0}'.format(e))
|
|
90 return
|
|
91 print("File: {}".format(e.loc.filename))
|
|
92 source = self.sources[e.loc.filename]
|
|
93 lines = source.split('\n')
|
|
94 ro, co = e.row, e.col
|
|
95 prerow = ro - 2
|
|
96 if prerow < 1:
|
|
97 prerow = 1
|
|
98 afterrow = ro + 3
|
|
99 if afterrow > len(lines):
|
|
100 afterrow = len(lines)
|
|
101
|
|
102 # print preceding source lines:
|
|
103 for r in range(prerow, ro):
|
|
104 printLine(r, lines[r-1])
|
|
105 # print source line containing error:
|
|
106 printLine(ro, lines[ro-1])
|
|
107 print(' '*(len(str(ro)+':')+co-1) + '^ Error: {0}'.format(e.msg))
|
|
108 # print trailing source line:
|
|
109 for r in range(ro+1, afterrow+1):
|
|
110 printLine(r, lines[r-1])
|
|
111 print('==============')
|