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

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