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

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