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

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