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