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

581 lines, 400 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 TokenForArith(node):
186 # type: (arith_expr_t) -> Optional[Token]
187 UP_node = node
188 with tagswitch(node) as case:
189 if case(arith_expr_e.VarSub):
190 vsub = cast(Token, UP_node)
191 # $(( x ))
192 return vsub
193
194 elif case(arith_expr_e.Word):
195 w = cast(CompoundWord, UP_node)
196 return LeftTokenForWord(w)
197
198 elif case(arith_expr_e.Unary):
199 node = cast(arith_expr.Unary, UP_node)
200 return TokenForArith(node.child)
201
202 elif case(arith_expr_e.Binary):
203 node = cast(arith_expr.Binary, UP_node)
204 return TokenForArith(node.op)
205
206 elif case(arith_expr_e.TernaryOp):
207 node = cast(arith_expr.TernaryOp, UP_node)
208
209 # TODO: should blame op
210 # blaming cond is arbitrary, but better than nothing
211 return TokenForArith(node.cond)
212
213 return None
214
215
216def LeftTokenForWordPart(part):
217 # type: (word_part_t) -> Optional[Token]
218 UP_part = part
219 with tagswitch(part) as case:
220 if case(word_part_e.YshArrayLiteral):
221 part = cast(YshArrayLiteral, UP_part)
222 return part.left
223
224 elif case(word_part_e.InitializerLiteral):
225 part = cast(word_part.InitializerLiteral, UP_part)
226 return part.left
227
228 elif case(word_part_e.Literal):
229 tok = cast(Token, UP_part)
230 return tok
231
232 elif case(word_part_e.EscapedLiteral):
233 part = cast(word_part.EscapedLiteral, UP_part)
234 return part.token
235
236 elif case(word_part_e.SingleQuoted):
237 part = cast(SingleQuoted, UP_part)
238 return part.left
239
240 elif case(word_part_e.DoubleQuoted):
241 part = cast(DoubleQuoted, UP_part)
242 return part.left
243
244 elif case(word_part_e.SimpleVarSub):
245 part = cast(SimpleVarSub, UP_part)
246 return part.tok
247
248 elif case(word_part_e.BracedVarSub):
249 part = cast(BracedVarSub, UP_part)
250 return part.left
251
252 elif case(word_part_e.CommandSub):
253 part = cast(CommandSub, UP_part)
254 return part.left_token
255
256 elif case(word_part_e.TildeSub):
257 part = cast(word_part.TildeSub, UP_part)
258 return part.left
259
260 elif case(word_part_e.ArithSub):
261 part = cast(word_part.ArithSub, UP_part)
262 return part.left
263
264 elif case(word_part_e.ExtGlob):
265 part = cast(word_part.ExtGlob, UP_part)
266 return part.op
267
268 elif case(word_part_e.BracedRange):
269 part = cast(word_part.BracedRange, UP_part)
270 return part.blame_tok
271
272 elif case(word_part_e.BracedRangeDigit):
273 part = cast(word_part.BracedRangeDigit, UP_part)
274 return part.orig_tok
275
276 elif case(word_part_e.BracedTuple):
277 part = cast(word_part.BracedTuple, UP_part)
278 # TODO: Derive token from part.words[0]
279 return None
280
281 elif case(word_part_e.Splice):
282 part = cast(word_part.Splice, UP_part)
283 return part.blame_tok
284
285 elif case(word_part_e.ExprSub):
286 part = cast(word_part.ExprSub, UP_part)
287 return part.left # $[
288
289 else:
290 raise AssertionError(part.tag())
291
292
293def _RightTokenForWordPart(part):
294 # type: (word_part_t) -> Token
295 UP_part = part
296 with tagswitch(part) as case:
297 if case(word_part_e.YshArrayLiteral):
298 part = cast(YshArrayLiteral, UP_part)
299 return part.right
300
301 elif case(word_part_e.InitializerLiteral):
302 part = cast(word_part.InitializerLiteral, UP_part)
303 return part.right
304
305 elif case(word_part_e.Literal):
306 tok = cast(Token, UP_part)
307 # Just use the token
308 return tok
309
310 elif case(word_part_e.EscapedLiteral):
311 part = cast(word_part.EscapedLiteral, UP_part)
312 return part.token
313
314 elif case(word_part_e.SingleQuoted):
315 part = cast(SingleQuoted, UP_part)
316 return part.right # right '
317
318 elif case(word_part_e.DoubleQuoted):
319 part = cast(DoubleQuoted, UP_part)
320 return part.right # right "
321
322 elif case(word_part_e.SimpleVarSub):
323 part = cast(SimpleVarSub, UP_part)
324 # left and right are the same for $myvar
325 return part.tok
326
327 elif case(word_part_e.BracedVarSub):
328 part = cast(BracedVarSub, UP_part)
329 return part.right
330
331 elif case(word_part_e.CommandSub):
332 part = cast(CommandSub, UP_part)
333 return part.right
334
335 elif case(word_part_e.TildeSub):
336 part = cast(word_part.TildeSub, UP_part)
337 if part.name is not None:
338 return part.name # ~bob/
339 else:
340 return part.left # ~/
341
342 elif case(word_part_e.ArithSub):
343 part = cast(word_part.ArithSub, UP_part)
344 return part.right
345
346 elif case(word_part_e.ExtGlob):
347 part = cast(word_part.ExtGlob, UP_part)
348 return part.right
349
350 elif case(word_part_e.BracedRange):
351 part = cast(word_part.BracedRange, UP_part)
352 return part.blame_tok
353
354 elif case(word_part_e.BracedTuple):
355 part = cast(word_part.BracedTuple, UP_part)
356 # TODO: Derive token from part.words[0]
357 return None
358
359 elif case(word_part_e.Splice):
360 part = cast(word_part.Splice, UP_part)
361 return part.blame_tok
362
363 elif case(word_part_e.ExprSub):
364 part = cast(word_part.ExprSub, UP_part)
365 return part.right
366
367 else:
368 raise AssertionError(part.tag())
369
370
371def LeftTokenForCompoundWord(w):
372 # type: (CompoundWord) -> Optional[Token]
373 if len(w.parts):
374 return LeftTokenForWordPart(w.parts[0])
375 else:
376 # This is possible for empty brace sub alternative {a,b,}
377 return None
378
379
380def LeftTokenForWord(w):
381 # type: (word_t) -> Optional[Token]
382 if w is None:
383 return None # e.g. builtin_bracket word.String() EOF
384
385 UP_w = w
386 with tagswitch(w) as case:
387 if case(word_e.Compound):
388 w = cast(CompoundWord, UP_w)
389 return LeftTokenForCompoundWord(w)
390
391 elif case(word_e.Operator):
392 tok = cast(Token, UP_w)
393 return tok
394
395 elif case(word_e.BracedTree):
396 w = cast(word.BracedTree, UP_w)
397 # This should always have one part?
398 return LeftTokenForWordPart(w.parts[0])
399
400 elif case(word_e.String):
401 w = cast(word.String, UP_w)
402 # See _StringWordEmitter in osh/builtin_bracket.py
403 return LeftTokenForWord(w.blame_loc)
404
405 else:
406 raise AssertionError(w.tag())
407
408 raise AssertionError('for -Wreturn-type in C++')
409
410
411def RightTokenForWord(w):
412 # type: (word_t) -> Token
413 """Used for alias expansion and history substitution.
414
415 and here doc delimiters?
416 """
417 UP_w = w
418 with tagswitch(w) as case:
419 if case(word_e.Compound):
420 w = cast(CompoundWord, UP_w)
421 if len(w.parts):
422 end = w.parts[-1]
423 return _RightTokenForWordPart(end)
424 else:
425 # This is possible for empty brace sub alternative {a,b,}
426 return None
427
428 elif case(word_e.Operator):
429 tok = cast(Token, UP_w)
430 return tok
431
432 elif case(word_e.BracedTree):
433 w = cast(word.BracedTree, UP_w)
434 # Note: this case may be unused
435 return _RightTokenForWordPart(w.parts[-1])
436
437 elif case(word_e.String):
438 w = cast(word.String, UP_w)
439 # Note: this case may be unused
440 return RightTokenForWord(w.blame_loc)
441
442 else:
443 raise AssertionError(w.tag())
444
445 raise AssertionError('for -Wreturn-type in C++')
446
447
448def TokenForLhsExpr(node):
449 # type: (sh_lhs_t) -> Token
450 """Currently unused?
451
452 Will be useful for translating YSH assignment
453 """
454 # This switch is annoying but we don't have inheritance from the sum type
455 # (because of diamond issue). We might change the schema later, which maeks
456 # it moot. See the comment in frontend/syntax.asdl.
457 UP_node = node
458 with tagswitch(node) as case:
459 if case(sh_lhs_e.Name):
460 node = cast(sh_lhs.Name, UP_node)
461 return node.left
462 elif case(sh_lhs_e.IndexedName):
463 node = cast(sh_lhs.IndexedName, UP_node)
464 return node.left
465 else:
466 # Should not see UnparsedIndex
467 raise AssertionError()
468
469 raise AssertionError()
470
471
472# TODO: Token instead of loc_t once all cases are implemented
473def TokenForExpr(node):
474 # type: (expr_t) -> loc_t
475 """Returns the token associated with the given expression."""
476
477 UP_node = node # type: expr_t
478 with tagswitch(node) as case:
479 if case(expr_e.Const):
480 node = cast(expr.Const, UP_node)
481 return node.c
482
483 elif case(expr_e.Var):
484 node = cast(expr.Var, UP_node)
485 return node.left
486
487 elif case(expr_e.Place):
488 node = cast(expr.Place, UP_node)
489 return node.blame_tok
490
491 elif case(expr_e.CommandSub):
492 node = cast(CommandSub, UP_node)
493 return node.left_token
494
495 elif case(expr_e.YshArrayLiteral):
496 node = cast(YshArrayLiteral, UP_node)
497 return node.left
498
499 elif case(expr_e.DoubleQuoted):
500 node = cast(DoubleQuoted, UP_node)
501 return node.left
502
503 elif case(expr_e.SingleQuoted):
504 node = cast(SingleQuoted, UP_node)
505 return node.left
506
507 elif case(expr_e.BracedVarSub):
508 node = cast(BracedVarSub, UP_node)
509 return node.left
510
511 elif case(expr_e.SimpleVarSub):
512 node = cast(SimpleVarSub, UP_node)
513 return node.tok
514
515 elif case(expr_e.Unary):
516 node = cast(expr.Unary, UP_node)
517 return node.op
518
519 elif case(expr_e.Binary):
520 node = cast(expr.Binary, UP_node)
521 return node.op
522
523 elif case(expr_e.Slice):
524 node = cast(expr.Slice, UP_node)
525 return node.op
526
527 elif case(expr_e.Range):
528 node = cast(expr.Range, UP_node)
529 return node.op
530
531 elif case(expr_e.Compare):
532 node = cast(expr.Compare, UP_node)
533 # TODO: use operator instead?
534 return TokenForExpr(node.left)
535
536 elif case(expr_e.IfExp):
537 # TODO
538 return loc.Missing
539
540 elif case(expr_e.List):
541 node = cast(expr.List, UP_node)
542 return node.left
543
544 elif case(expr_e.Tuple):
545 node = cast(expr.Tuple, UP_node)
546 return node.left
547
548 elif case(expr_e.Dict):
549 node = cast(expr.Dict, UP_node)
550 return node.left
551
552 elif case(expr_e.ListComp):
553 node = cast(expr.ListComp, UP_node)
554 return node.left
555
556 elif case(expr_e.GeneratorExp):
557 # TODO
558 return loc.Missing
559
560 elif case(expr_e.Lambda):
561 # TODO
562 return loc.Missing
563
564 elif case(expr_e.FuncCall):
565 node = cast(expr.FuncCall, UP_node)
566 return node.args.left
567
568 elif case(expr_e.Subscript):
569 node = cast(Subscript, UP_node)
570 return node.left
571
572 elif case(expr_e.Attribute):
573 node = cast(Attribute, UP_node)
574 return node.op
575
576 elif case(expr_e.Eggex):
577 node = cast(Eggex, UP_node)
578 return node.left
579
580 else:
581 raise AssertionError(node.__class__.__name__)