OILS / frontend / location.py View on Github | oilshell.org

571 lines, 392 significant
1#!/usr/bin/env python2
2"""
3location.py - Library to get source location info from nodes.
4
5This makes syntax errors nicer.
6"""
7from __future__ import print_function
8
9from _devbuild.gen.syntax_asdl import (
10 expr,
11 expr_t,
12 expr_e,
13 loc,
14 loc_t,
15 loc_e,
16 command,
17 command_e,
18 command_t,
19 sh_lhs,
20 sh_lhs_e,
21 sh_lhs_t,
22 word,
23 word_e,
24 word_t,
25 word_part,
26 word_part_e,
27 word_part_t,
28 CompoundWord,
29 Token,
30 SimpleVarSub,
31 ShArrayLiteral,
32 SingleQuoted,
33 DoubleQuoted,
34 CommandSub,
35 BracedVarSub,
36 BraceGroup,
37 Subscript,
38 Attribute,
39 arith_expr,
40 arith_expr_e,
41 arith_expr_t,
42 Eggex,
43)
44from _devbuild.gen.value_asdl import LeftName
45from mycpp.mylib import log
46from mycpp.mylib import tagswitch
47
48_ = log
49
50from typing import cast, Optional
51
52
53def LName(name):
54 # type: (str) -> LeftName
55 """Wrapper for LeftName() with location.
56
57 TODO: add locations and remove this.
58 """
59 return LeftName(name, loc.Missing)
60
61
62def TokenFor(loc_):
63 # type: (loc_t) -> Optional[Token]
64 """Given a location, get a Token.
65
66 This is useful because a Token points to a single line.
67 """
68 UP_location = loc_
69 with tagswitch(loc_) as case:
70 if case(loc_e.Missing):
71 return None
72
73 elif case(loc_e.Token):
74 tok = cast(Token, UP_location)
75 if tok:
76 return tok
77 else:
78 return None
79
80 elif case(loc_e.ArgWord):
81 w = cast(CompoundWord, UP_location)
82 return LeftTokenForWord(w)
83
84 elif case(loc_e.WordPart):
85 loc_ = cast(loc.WordPart, UP_location)
86 if loc_.p:
87 return LeftTokenForWordPart(loc_.p)
88 else:
89 return None
90
91 elif case(loc_e.Word):
92 loc_ = cast(loc.Word, UP_location)
93 if loc_.w:
94 return LeftTokenForWord(loc_.w)
95 else:
96 return None
97
98 elif case(loc_e.Command):
99 loc_ = cast(loc.Command, UP_location)
100 if loc_.c:
101 return TokenForCommand(loc_.c)
102 else:
103 return None
104
105 elif case(loc_e.Arith):
106 loc_ = cast(loc.Arith, UP_location)
107 if loc_.a:
108 return TokenForArith(loc_.a)
109 else:
110 return None
111
112 else:
113 raise AssertionError()
114
115 raise AssertionError()
116
117
118def TokenForCommand(node):
119 # type: (command_t) -> Optional[Token]
120 """Used directly in _CheckStatus()"""
121 UP_node = node # type: command_t
122 tag = node.tag()
123
124 if tag == command_e.Sentence:
125 node = cast(command.Sentence, UP_node)
126 #log("node.child %s", node.child)
127 return node.terminator # & or ;
128
129 if tag == command_e.Simple:
130 node = cast(command.Simple, UP_node)
131 return node.blame_tok
132
133 if tag == command_e.ShAssignment:
134 node = cast(command.ShAssignment, UP_node)
135 return node.left
136
137 if tag == command_e.Pipeline:
138 node = cast(command.Pipeline, UP_node)
139 if len(node.ops):
140 return node.ops[0] # first | or |&
141 else:
142 assert node.negated is not None
143 return node.negated # ! false
144
145 if tag == command_e.AndOr:
146 node = cast(command.AndOr, UP_node)
147 return node.ops[0] # first && or ||
148
149 if tag == command_e.DoGroup:
150 node = cast(command.DoGroup, UP_node)
151 return node.left # 'do' token
152 if tag == command_e.BraceGroup:
153 node = cast(BraceGroup, UP_node)
154 return node.left # { token
155 if tag == command_e.Subshell:
156 node = cast(command.Subshell, UP_node)
157 return node.left # ( token
158
159 if tag == command_e.WhileUntil:
160 node = cast(command.WhileUntil, UP_node)
161 return node.keyword # while
162 if tag == command_e.If:
163 node = cast(command.If, UP_node)
164 return node.if_kw
165 if tag == command_e.Case:
166 node = cast(command.Case, UP_node)
167 return node.case_kw
168 if tag == command_e.TimeBlock:
169 node = cast(command.TimeBlock, UP_node)
170 return node.keyword
171
172 # We never have this case?
173 #if node.tag == command_e.CommandList:
174 # pass
175
176 return None
177
178
179def TokenForArith(node):
180 # type: (arith_expr_t) -> Optional[Token]
181 UP_node = node
182 with tagswitch(node) as case:
183 if case(arith_expr_e.VarSub):
184 vsub = cast(Token, UP_node)
185 # $(( x ))
186 return vsub
187
188 elif case(arith_expr_e.Word):
189 w = cast(CompoundWord, UP_node)
190 return LeftTokenForWord(w)
191
192 elif case(arith_expr_e.Unary):
193 node = cast(arith_expr.Unary, UP_node)
194 return TokenForArith(node.child)
195
196 elif case(arith_expr_e.Binary):
197 node = cast(arith_expr.Binary, UP_node)
198 return TokenForArith(node.op)
199
200 elif case(arith_expr_e.TernaryOp):
201 node = cast(arith_expr.TernaryOp, UP_node)
202
203 # TODO: should blame op
204 # blaming cond is arbitrary, but better than nothing
205 return TokenForArith(node.cond)
206
207 return None
208
209
210def LeftTokenForWordPart(part):
211 # type: (word_part_t) -> Optional[Token]
212 UP_part = part
213 with tagswitch(part) as case:
214 if case(word_part_e.ShArrayLiteral):
215 part = cast(ShArrayLiteral, UP_part)
216 return part.left
217
218 elif case(word_part_e.BashAssocLiteral):
219 part = cast(word_part.BashAssocLiteral, UP_part)
220 return part.left
221
222 elif case(word_part_e.Literal):
223 tok = cast(Token, UP_part)
224 return tok
225
226 elif case(word_part_e.EscapedLiteral):
227 part = cast(word_part.EscapedLiteral, UP_part)
228 return part.token
229
230 elif case(word_part_e.SingleQuoted):
231 part = cast(SingleQuoted, UP_part)
232 return part.left
233
234 elif case(word_part_e.DoubleQuoted):
235 part = cast(DoubleQuoted, UP_part)
236 return part.left
237
238 elif case(word_part_e.SimpleVarSub):
239 part = cast(SimpleVarSub, UP_part)
240 return part.tok
241
242 elif case(word_part_e.BracedVarSub):
243 part = cast(BracedVarSub, UP_part)
244 return part.left
245
246 elif case(word_part_e.CommandSub):
247 part = cast(CommandSub, UP_part)
248 return part.left_token
249
250 elif case(word_part_e.TildeSub):
251 part = cast(word_part.TildeSub, UP_part)
252 return part.left
253
254 elif case(word_part_e.ArithSub):
255 part = cast(word_part.ArithSub, UP_part)
256 return part.left
257
258 elif case(word_part_e.ExtGlob):
259 part = cast(word_part.ExtGlob, UP_part)
260 return part.op
261
262 elif case(word_part_e.BracedRange):
263 part = cast(word_part.BracedRange, UP_part)
264 return part.blame_tok
265
266 elif case(word_part_e.BracedTuple):
267 part = cast(word_part.BracedTuple, UP_part)
268 # TODO: Derive token from part.words[0]
269 return None
270
271 elif case(word_part_e.Splice):
272 part = cast(word_part.Splice, UP_part)
273 return part.blame_tok
274
275 elif case(word_part_e.ExprSub):
276 part = cast(word_part.ExprSub, UP_part)
277 return part.left # $[
278
279 else:
280 raise AssertionError(part.tag())
281
282
283def _RightTokenForWordPart(part):
284 # type: (word_part_t) -> Token
285 UP_part = part
286 with tagswitch(part) as case:
287 if case(word_part_e.ShArrayLiteral):
288 part = cast(ShArrayLiteral, UP_part)
289 return part.right
290
291 elif case(word_part_e.BashAssocLiteral):
292 part = cast(word_part.BashAssocLiteral, UP_part)
293 return part.right
294
295 elif case(word_part_e.Literal):
296 tok = cast(Token, UP_part)
297 # Just use the token
298 return tok
299
300 elif case(word_part_e.EscapedLiteral):
301 part = cast(word_part.EscapedLiteral, UP_part)
302 return part.token
303
304 elif case(word_part_e.SingleQuoted):
305 part = cast(SingleQuoted, UP_part)
306 return part.right # right '
307
308 elif case(word_part_e.DoubleQuoted):
309 part = cast(DoubleQuoted, UP_part)
310 return part.right # right "
311
312 elif case(word_part_e.SimpleVarSub):
313 part = cast(SimpleVarSub, UP_part)
314 # left and right are the same for $myvar
315 return part.tok
316
317 elif case(word_part_e.BracedVarSub):
318 part = cast(BracedVarSub, UP_part)
319 return part.right
320
321 elif case(word_part_e.CommandSub):
322 part = cast(CommandSub, UP_part)
323 return part.right
324
325 elif case(word_part_e.TildeSub):
326 part = cast(word_part.TildeSub, UP_part)
327 if part.name is not None:
328 return part.name # ~bob/
329 else:
330 return part.left # ~/
331
332 elif case(word_part_e.ArithSub):
333 part = cast(word_part.ArithSub, UP_part)
334 return part.right
335
336 elif case(word_part_e.ExtGlob):
337 part = cast(word_part.ExtGlob, UP_part)
338 return part.right
339
340 elif case(word_part_e.BracedRange):
341 part = cast(word_part.BracedRange, UP_part)
342 return part.blame_tok
343
344 elif case(word_part_e.BracedTuple):
345 part = cast(word_part.BracedTuple, UP_part)
346 # TODO: Derive token from part.words[0]
347 return None
348
349 elif case(word_part_e.Splice):
350 part = cast(word_part.Splice, UP_part)
351 return part.blame_tok
352
353 elif case(word_part_e.ExprSub):
354 part = cast(word_part.ExprSub, UP_part)
355 return part.right
356
357 else:
358 raise AssertionError(part.tag())
359
360
361def LeftTokenForCompoundWord(w):
362 # type: (CompoundWord) -> Optional[Token]
363 if len(w.parts):
364 return LeftTokenForWordPart(w.parts[0])
365 else:
366 # This is possible for empty brace sub alternative {a,b,}
367 return None
368
369
370def LeftTokenForWord(w):
371 # type: (word_t) -> Optional[Token]
372 if w is None:
373 return None # e.g. builtin_bracket word.String() EOF
374
375 UP_w = w
376 with tagswitch(w) as case:
377 if case(word_e.Compound):
378 w = cast(CompoundWord, UP_w)
379 return LeftTokenForCompoundWord(w)
380
381 elif case(word_e.Operator):
382 tok = cast(Token, UP_w)
383 return tok
384
385 elif case(word_e.BracedTree):
386 w = cast(word.BracedTree, UP_w)
387 # This should always have one part?
388 return LeftTokenForWordPart(w.parts[0])
389
390 elif case(word_e.String):
391 w = cast(word.String, UP_w)
392 # See _StringWordEmitter in osh/builtin_bracket.py
393 return LeftTokenForWord(w.blame_loc)
394
395 else:
396 raise AssertionError(w.tag())
397
398 raise AssertionError('for -Wreturn-type in C++')
399
400
401def RightTokenForWord(w):
402 # type: (word_t) -> Token
403 """Used for alias expansion and history substitution.
404
405 and here doc delimiters?
406 """
407 UP_w = w
408 with tagswitch(w) as case:
409 if case(word_e.Compound):
410 w = cast(CompoundWord, UP_w)
411 if len(w.parts):
412 end = w.parts[-1]
413 return _RightTokenForWordPart(end)
414 else:
415 # This is possible for empty brace sub alternative {a,b,}
416 return None
417
418 elif case(word_e.Operator):
419 tok = cast(Token, UP_w)
420 return tok
421
422 elif case(word_e.BracedTree):
423 w = cast(word.BracedTree, UP_w)
424 # Note: this case may be unused
425 return _RightTokenForWordPart(w.parts[-1])
426
427 elif case(word_e.String):
428 w = cast(word.String, UP_w)
429 # Note: this case may be unused
430 return RightTokenForWord(w.blame_loc)
431
432 else:
433 raise AssertionError(w.tag())
434
435 raise AssertionError('for -Wreturn-type in C++')
436
437
438def TokenForLhsExpr(node):
439 # type: (sh_lhs_t) -> Token
440 """Currently unused?
441
442 Will be useful for translating YSH assignment
443 """
444 # This switch is annoying but we don't have inheritance from the sum type
445 # (because of diamond issue). We might change the schema later, which maeks
446 # it moot. See the comment in frontend/syntax.asdl.
447 UP_node = node
448 with tagswitch(node) as case:
449 if case(sh_lhs_e.Name):
450 node = cast(sh_lhs.Name, UP_node)
451 return node.left
452 elif case(sh_lhs_e.IndexedName):
453 node = cast(sh_lhs.IndexedName, UP_node)
454 return node.left
455 else:
456 # Should not see UnparsedIndex
457 raise AssertionError()
458
459 raise AssertionError()
460
461
462# TODO: Token instead of loc_t once all cases are implemented
463def TokenForExpr(node):
464 # type: (expr_t) -> loc_t
465 """Returns the token associated with the given expression."""
466
467 UP_node = node # type: expr_t
468 with tagswitch(node) as case:
469 if case(expr_e.Const):
470 node = cast(expr.Const, UP_node)
471 return node.c
472
473 elif case(expr_e.Var):
474 node = cast(expr.Var, UP_node)
475 return node.left
476
477 elif case(expr_e.Place):
478 node = cast(expr.Place, UP_node)
479 return node.blame_tok
480
481 elif case(expr_e.CommandSub):
482 node = cast(CommandSub, UP_node)
483 return node.left_token
484
485 elif case(expr_e.ShArrayLiteral):
486 node = cast(ShArrayLiteral, UP_node)
487 return node.left
488
489 elif case(expr_e.DoubleQuoted):
490 node = cast(DoubleQuoted, UP_node)
491 return node.left
492
493 elif case(expr_e.SingleQuoted):
494 node = cast(SingleQuoted, UP_node)
495 return node.left
496
497 elif case(expr_e.BracedVarSub):
498 node = cast(BracedVarSub, UP_node)
499 return node.left
500
501 elif case(expr_e.SimpleVarSub):
502 node = cast(SimpleVarSub, UP_node)
503 return node.tok
504
505 elif case(expr_e.Unary):
506 node = cast(expr.Unary, UP_node)
507 return node.op
508
509 elif case(expr_e.Binary):
510 node = cast(expr.Binary, UP_node)
511 return node.op
512
513 elif case(expr_e.Slice):
514 node = cast(expr.Slice, UP_node)
515 return node.op
516
517 elif case(expr_e.Range):
518 node = cast(expr.Range, UP_node)
519 return node.op
520
521 elif case(expr_e.Compare):
522 node = cast(expr.Compare, UP_node)
523 # TODO: use operator instead?
524 return TokenForExpr(node.left)
525
526 elif case(expr_e.IfExp):
527 # TODO
528 return loc.Missing
529
530 elif case(expr_e.List):
531 node = cast(expr.List, UP_node)
532 return node.left
533
534 elif case(expr_e.Tuple):
535 node = cast(expr.Tuple, UP_node)
536 return node.left
537
538 elif case(expr_e.Dict):
539 node = cast(expr.Dict, UP_node)
540 return node.left
541
542 elif case(expr_e.ListComp):
543 node = cast(expr.ListComp, UP_node)
544 return node.left
545
546 elif case(expr_e.GeneratorExp):
547 # TODO
548 return loc.Missing
549
550 elif case(expr_e.Lambda):
551 # TODO
552 return loc.Missing
553
554 elif case(expr_e.FuncCall):
555 node = cast(expr.FuncCall, UP_node)
556 return node.args.left
557
558 elif case(expr_e.Subscript):
559 node = cast(Subscript, UP_node)
560 return node.left
561
562 elif case(expr_e.Attribute):
563 node = cast(Attribute, UP_node)
564 return node.op
565
566 elif case(expr_e.Eggex):
567 node = cast(Eggex, UP_node)
568 return node.left
569
570 else:
571 raise AssertionError(node.__class__.__name__)