OILS / frontend / location.py View on Github | oils.pub

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