1 #!/usr/bin/env python2
2 """
3 location.py - Library to get source location info from nodes.
4
5 This makes syntax errors nicer.
6 """
7 from __future__ import print_function
8
9 from _devbuild.gen.syntax_asdl import (
10 loc,
11 loc_t,
12 loc_e,
13 command,
14 command_e,
15 command_t,
16 sh_lhs_expr,
17 sh_lhs_expr_e,
18 sh_lhs_expr_t,
19 word,
20 word_e,
21 word_t,
22 word_part,
23 word_part_e,
24 word_part_t,
25 CompoundWord,
26 SimpleVarSub,
27 Token,
28 ShArrayLiteral,
29 SingleQuoted,
30 DoubleQuoted,
31 CommandSub,
32 BracedVarSub,
33 BraceGroup,
34 arith_expr,
35 arith_expr_e,
36 arith_expr_t,
37 )
38 from _devbuild.gen.runtime_asdl import lvalue
39 from mycpp.mylib import log
40 from mycpp.mylib import tagswitch
41
42 _ = log
43
44 from typing import cast, Optional
45
46
47 def LName(name):
48 # type: (str) -> lvalue.Named
49 """Wrapper for lvalue.Named() with location.
50
51 TODO: add locations and remove
52 this.
53 """
54 return lvalue.Named(name, loc.Missing)
55
56
57 def TokenFor(loc_):
58 # type: (loc_t) -> Optional[Token]
59 """Given a location, get a Token.
60
61 This is useful because a Token points to a single line.
62 """
63 UP_location = loc_
64 with tagswitch(loc_) as case:
65 if case(loc_e.Missing):
66 return None
67
68 elif case(loc_e.Token):
69 tok = cast(Token, UP_location)
70 if tok:
71 return tok
72 else:
73 return None
74
75 elif case(loc_e.ArgWord):
76 w = cast(CompoundWord, UP_location)
77 return LeftTokenForWord(w)
78
79 elif case(loc_e.WordPart):
80 loc_ = cast(loc.WordPart, UP_location)
81 if loc_.p:
82 return LeftTokenForWordPart(loc_.p)
83 else:
84 return None
85
86 elif case(loc_e.Word):
87 loc_ = cast(loc.Word, UP_location)
88 if loc_.w:
89 return LeftTokenForWord(loc_.w)
90 else:
91 return None
92
93 elif case(loc_e.Command):
94 loc_ = cast(loc.Command, UP_location)
95 if loc_.c:
96 return TokenForCommand(loc_.c)
97 else:
98 return None
99
100 elif case(loc_e.Arith):
101 loc_ = cast(loc.Arith, UP_location)
102 if loc_.a:
103 return TokenForArith(loc_.a)
104 else:
105 return None
106
107 else:
108 raise AssertionError()
109
110 raise AssertionError()
111
112
113 def TokenForCommand(node):
114 # type: (command_t) -> Optional[Token]
115 """Used directly in _CheckStatus()"""
116 UP_node = node # type: command_t
117 tag = node.tag()
118
119 if tag == command_e.Sentence:
120 node = cast(command.Sentence, UP_node)
121 #log("node.child %s", node.child)
122 return node.terminator # & or ;
123
124 if tag == command_e.Simple:
125 node = cast(command.Simple, UP_node)
126 return node.blame_tok
127
128 if tag == command_e.ShAssignment:
129 node = cast(command.ShAssignment, UP_node)
130 return node.left
131
132 if tag == command_e.Pipeline:
133 node = cast(command.Pipeline, UP_node)
134 if len(node.ops):
135 return node.ops[0] # first | or |&
136 else:
137 assert node.negated is not None
138 return node.negated # ! false
139
140 if tag == command_e.AndOr:
141 node = cast(command.AndOr, UP_node)
142 return node.ops[0] # first && or ||
143
144 if tag == command_e.DoGroup:
145 node = cast(command.DoGroup, UP_node)
146 return node.left # 'do' token
147 if tag == command_e.BraceGroup:
148 node = cast(BraceGroup, UP_node)
149 return node.left # { token
150 if tag == command_e.Subshell:
151 node = cast(command.Subshell, UP_node)
152 return node.left # ( token
153
154 if tag == command_e.WhileUntil:
155 node = cast(command.WhileUntil, UP_node)
156 return node.keyword # while
157 if tag == command_e.If:
158 node = cast(command.If, UP_node)
159 return node.if_kw
160 if tag == command_e.Case:
161 node = cast(command.Case, UP_node)
162 return node.case_kw
163 if tag == command_e.TimeBlock:
164 node = cast(command.TimeBlock, UP_node)
165 return node.keyword
166
167 # We never have this case?
168 #if node.tag == command_e.CommandList:
169 # pass
170
171 return None
172
173
174 def TokenForArith(node):
175 # type: (arith_expr_t) -> Optional[Token]
176 UP_node = node
177 with tagswitch(node) as case:
178 if case(arith_expr_e.VarSub):
179 vsub = cast(SimpleVarSub, UP_node)
180 # $(( x ))
181 return vsub.left
182
183 elif case(arith_expr_e.Word):
184 w = cast(CompoundWord, UP_node)
185 return LeftTokenForWord(w)
186
187 elif case(arith_expr_e.Unary):
188 node = cast(arith_expr.Unary, UP_node)
189 return TokenForArith(node.child)
190
191 elif case(arith_expr_e.Binary):
192 node = cast(arith_expr.Binary, UP_node)
193
194 # TODO: should blame op
195 # blaming left is arbitrary, but better than nothing
196 return TokenForArith(node.left)
197
198 elif case(arith_expr_e.TernaryOp):
199 node = cast(arith_expr.TernaryOp, UP_node)
200
201 # TODO: should blame op
202 # blaming cond is arbitrary, but better than nothing
203 return TokenForArith(node.cond)
204
205 return None
206
207
208 def LeftTokenForWordPart(part):
209 # type: (word_part_t) -> Optional[Token]
210 UP_part = part
211 with tagswitch(part) as case:
212 if case(word_part_e.ShArrayLiteral):
213 part = cast(ShArrayLiteral, UP_part)
214 return part.left
215
216 elif case(word_part_e.BashAssocLiteral):
217 part = cast(word_part.BashAssocLiteral, UP_part)
218 return part.left
219
220 elif case(word_part_e.Literal):
221 tok = cast(Token, UP_part)
222 return tok
223
224 elif case(word_part_e.EscapedLiteral):
225 part = cast(word_part.EscapedLiteral, UP_part)
226 return part.token
227
228 elif case(word_part_e.SingleQuoted):
229 part = cast(SingleQuoted, UP_part)
230 return part.left
231
232 elif case(word_part_e.DoubleQuoted):
233 part = cast(DoubleQuoted, UP_part)
234 return part.left
235
236 elif case(word_part_e.SimpleVarSub):
237 part = cast(SimpleVarSub, UP_part)
238 return part.left
239
240 elif case(word_part_e.BracedVarSub):
241 part = cast(BracedVarSub, UP_part)
242 return part.left
243
244 elif case(word_part_e.CommandSub):
245 part = cast(CommandSub, UP_part)
246 return part.left_token
247
248 elif case(word_part_e.TildeSub):
249 part = cast(word_part.TildeSub, UP_part)
250 return part.token
251
252 elif case(word_part_e.ArithSub):
253 part = cast(word_part.ArithSub, UP_part)
254 return part.left
255
256 elif case(word_part_e.ExtGlob):
257 part = cast(word_part.ExtGlob, UP_part)
258 return part.op
259
260 elif case(word_part_e.BracedRange):
261 part = cast(word_part.BracedRange, UP_part)
262 return part.blame_tok
263
264 elif case(word_part_e.BracedTuple):
265 part = cast(word_part.BracedTuple, UP_part)
266 # TODO: Derive token from part.words[0]
267 return None
268
269 elif case(word_part_e.Splice):
270 part = cast(word_part.Splice, UP_part)
271 return part.blame_tok
272
273 elif case(word_part_e.ExprSub):
274 part = cast(word_part.ExprSub, UP_part)
275 return part.left # $[
276
277 else:
278 raise AssertionError(part.tag())
279
280
281 def _RightTokenForWordPart(part):
282 # type: (word_part_t) -> Token
283 UP_part = part
284 with tagswitch(part) as case:
285 if case(word_part_e.ShArrayLiteral):
286 part = cast(ShArrayLiteral, UP_part)
287 return part.right
288
289 elif case(word_part_e.BashAssocLiteral):
290 part = cast(word_part.BashAssocLiteral, UP_part)
291 return part.right
292
293 elif case(word_part_e.Literal):
294 tok = cast(Token, UP_part)
295 # Just use the token
296 return tok
297
298 elif case(word_part_e.EscapedLiteral):
299 part = cast(word_part.EscapedLiteral, UP_part)
300 return part.token
301
302 elif case(word_part_e.SingleQuoted):
303 part = cast(SingleQuoted, UP_part)
304 return part.right # right '
305
306 elif case(word_part_e.DoubleQuoted):
307 part = cast(DoubleQuoted, UP_part)
308 return part.right # right "
309
310 elif case(word_part_e.SimpleVarSub):
311 part = cast(SimpleVarSub, UP_part)
312 # left and right are the same for $myvar
313 return part.left
314
315 elif case(word_part_e.BracedVarSub):
316 part = cast(BracedVarSub, UP_part)
317 return part.right
318
319 elif case(word_part_e.CommandSub):
320 part = cast(CommandSub, UP_part)
321 return part.right
322
323 elif case(word_part_e.TildeSub):
324 part = cast(word_part.TildeSub, UP_part)
325 return part.token
326
327 elif case(word_part_e.ArithSub):
328 part = cast(word_part.ArithSub, UP_part)
329 return part.right
330
331 elif case(word_part_e.ExtGlob):
332 part = cast(word_part.ExtGlob, UP_part)
333 return part.right
334
335 elif case(word_part_e.BracedRange):
336 part = cast(word_part.BracedRange, UP_part)
337 return part.blame_tok
338
339 elif case(word_part_e.BracedTuple):
340 part = cast(word_part.BracedTuple, UP_part)
341 # TODO: Derive token from part.words[0]
342 return None
343
344 elif case(word_part_e.Splice):
345 part = cast(word_part.Splice, UP_part)
346 return part.blame_tok
347
348 elif case(word_part_e.ExprSub):
349 part = cast(word_part.ExprSub, UP_part)
350 return part.right
351
352 else:
353 raise AssertionError(part.tag())
354
355
356 def LeftTokenForCompoundWord(w):
357 # type: (CompoundWord) -> Optional[Token]
358 if len(w.parts):
359 return LeftTokenForWordPart(w.parts[0])
360 else:
361 # This is possible for empty brace sub alternative {a,b,}
362 return None
363
364
365 def LeftTokenForWord(w):
366 # type: (word_t) -> Optional[Token]
367 if w is None:
368 return None # e.g. builtin_bracket word.String() EOF
369
370 UP_w = w
371 with tagswitch(w) as case:
372 if case(word_e.Compound):
373 w = cast(CompoundWord, UP_w)
374 return LeftTokenForCompoundWord(w)
375
376 elif case(word_e.Operator):
377 tok = cast(Token, UP_w)
378 return tok
379
380 elif case(word_e.BracedTree):
381 w = cast(word.BracedTree, UP_w)
382 # This should always have one part?
383 return LeftTokenForWordPart(w.parts[0])
384
385 elif case(word_e.String):
386 w = cast(word.String, UP_w)
387 # See _StringWordEmitter in osh/builtin_bracket.py
388 return LeftTokenForWord(w.blame_loc)
389
390 else:
391 raise AssertionError(w.tag())
392
393 raise AssertionError('for -Wreturn-type in C++')
394
395
396 def RightTokenForWord(w):
397 # type: (word_t) -> Token
398 """Used for alias expansion and history substitution.
399
400 and here doc delimiters?
401 """
402 UP_w = w
403 with tagswitch(w) as case:
404 if case(word_e.Compound):
405 w = cast(CompoundWord, UP_w)
406 if len(w.parts):
407 end = w.parts[-1]
408 return _RightTokenForWordPart(end)
409 else:
410 # This is possible for empty brace sub alternative {a,b,}
411 return None
412
413 elif case(word_e.Operator):
414 tok = cast(Token, UP_w)
415 return tok
416
417 elif case(word_e.BracedTree):
418 w = cast(word.BracedTree, UP_w)
419 # Note: this case may be unused
420 return _RightTokenForWordPart(w.parts[-1])
421
422 elif case(word_e.String):
423 w = cast(word.String, UP_w)
424 # Note: this case may be unused
425 return RightTokenForWord(w.blame_loc)
426
427 else:
428 raise AssertionError(w.tag())
429
430 raise AssertionError('for -Wreturn-type in C++')
431
432
433 def TokenForLhsExpr(node):
434 # type: (sh_lhs_expr_t) -> Token
435 """Currently unused?
436
437 Will be useful for translating YSH assignment
438 """
439 # This switch is annoying but we don't have inheritance from the sum type
440 # (because of diamond issue). We might change the schema later, which maeks
441 # it moot. See the comment in frontend/syntax.asdl.
442 UP_node = node
443 with tagswitch(node) as case:
444 if case(sh_lhs_expr_e.Name):
445 node = cast(sh_lhs_expr.Name, UP_node)
446 return node.left
447 elif case(sh_lhs_expr_e.IndexedName):
448 node = cast(sh_lhs_expr.IndexedName, UP_node)
449 return node.left
450 else:
451 # Should not see UnparsedIndex
452 raise AssertionError()
453
454 raise AssertionError()