Make error reporting more consistent and shared.
Chris Pressey
10 years ago
12 | 12 | |
13 | 13 | | hello = "h". |
14 | 14 | | % |
15 | ? expected 'identifiable character' but found '%' at line 2, column 5 in '/tmp/tmp | |
15 | ? expected identifiable character but found '%' at line 2, column 5 in '/tmp/tmp | |
16 | 16 | |
17 | 17 | When a parsing error occurs in a Tamsin source, the filename, line number, |
18 | 18 | and column number are reported. |
20 | 20 | | slough = "h" & ("o" | "p"). |
21 | 21 | | maidenhead = "h" & ("o" | "p"). |
22 | 22 | | reading = "h" ("o" | "p"). |
23 | ? expected ''.'' but found '(' at line 3, column 16 in '/tmp/tmp | |
23 | ? expected '.' but found '(' at line 3, column 16 in '/tmp/tmp | |
24 | ||
25 | | pasta = "h" & «hop() & "p". | |
26 | ? expected '>>' but found '&' at line 1, column 22 in '/tmp/tmp | |
27 | ||
28 | | pasta = "h" & «hop() | |
29 | ? expected '>>' but found EOF at line 1, column 22 in '/tmp/tmp | |
24 | 30 | |
25 | 31 | When a scanning error occurs in the input to a Tamsin program, the filename, |
26 | 32 | line number, and column number are reported. |
27 | 33 | |
28 | 34 | | main = "h" & "o" & "x". |
29 | 35 | + hop |
30 | ? expected 'x' found 'p' at line 1, column 3 in '<stdin>' | |
36 | ? expected 'x' but found 'p' at line 1, column 3 in '<stdin>' | |
31 | 37 | |
32 | 38 | | main = "h" & "o" & {"\n"} & "0" & "x". |
33 | 39 | + ho |
34 | 40 | + |
35 | 41 | + 0p |
36 | ? expected 'x' found 'p' at line 3, column 2 in '<stdin>' | |
42 | ? expected 'x' but found 'p' at line 3, column 2 in '<stdin>' | |
43 | ||
44 | | main = "h" & "o" & "x". | |
45 | + ho | |
46 | ? expected 'x' but found EOF at line 1, column 3 in '<stdin>' |
154 | 154 | if prodref.module == '$': |
155 | 155 | return tamsin.sysmod.call(name, self, args) |
156 | 156 | prod = self.program.find_production(prodref) |
157 | if prod is None: | |
158 | raise ValueError("internal error: unresolved: " + repr(prodref)) | |
157 | assert prod is not None, "unresolved: " + repr(prodref) | |
159 | 158 | self.event('call_candidates', prod) |
160 | 159 | return self.interpret(prod, args=args) |
161 | 160 | elif isinstance(ast, Send): |
46 | 46 | def isalnum(self): |
47 | 47 | return self.scanner.isalnum() |
48 | 48 | def error(self, expected): |
49 | return self.scanner.error(expected) | |
49 | return self.scanner.error(expected, self.peek()) | |
50 | 50 | def peek(self): |
51 | 51 | return self.scanner.peek() |
52 | 52 | def consume(self, t): |
203 | 203 | """ |
204 | 204 | return self.state.report_buffer(position, length) |
205 | 205 | |
206 | def error_message(self, expected, found): | |
207 | if found is EOF: | |
208 | found = 'EOF' | |
209 | else: | |
210 | found = "'%s'" % found | |
211 | return ( | |
212 | "expected %s but found %s at line %s, column %s in '%s'" % | |
213 | (expected, found, | |
214 | self.state.line_number, | |
215 | self.state.column_number, | |
216 | self.state.filename) | |
217 | ) | |
218 | ||
206 | 219 | def error(self, expected, found): |
207 | raise ValueError(u"expected '%s' but found '%s' at " | |
208 | "line %s, column %s in '%s'" % | |
209 | (expected, found, | |
210 | self.state.line_number, | |
211 | self.state.column_number, | |
212 | self.state.filename)) | |
220 | raise ValueError(self.error_message(expected, found)) | |
213 | 221 | |
214 | 222 | def scan(self): |
215 | 223 | """Returns the next token from the buffer. |
379 | 387 | def scan_impl(self, scanner): |
380 | 388 | if scanner.is_at_eof(): |
381 | 389 | return EOF |
382 | # if we ever go back to exceptions, we would have a try/catch here | |
383 | ||
384 | # this will cause the scanner to have another engine pushed onto | |
385 | # it. we rely on that engine to actually get us the token, and it | |
390 | ||
391 | # This will cause the scanner to have another engine pushed onto | |
392 | # it. We rely on that engine to actually get us the token, and it | |
386 | 393 | # will update the scanner for us. |
387 | # | |
388 | # BUT the subsidiary scanner may have commited, while WE want to | |
389 | # leave the scanner in a divergent state. So we save the reset | |
390 | # position, and restore it when the subsidiary scan is done. | |
391 | 394 | |
392 | 395 | assert scanner is self.interpreter.scanner |
393 | 396 |
44 | 44 | if self.scanner.consume(token): |
45 | 45 | return (True, term) |
46 | 46 | else: |
47 | if upcoming_token is EOF: | |
48 | upcoming_token = 'EOF' | |
49 | s = ("expected '%s' found '%s' at line %s, column %s in '%s'" % | |
50 | (token, upcoming_token, | |
51 | self.scanner.state.line_number, | |
52 | self.scanner.state.column_number, | |
53 | self.scanner.state.filename)) | |
54 | return (False, Atom(s)) | |
47 | return (False, | |
48 | Atom(self.scanner.error_message("'%s'" % token, upcoming_token)) | |
49 | ) | |
55 | 50 | expect.arity = 1 |
56 | 51 | |
57 | 52 |