| 1 | #!/usr/bin/env python2
 | 
| 2 | """expr_eval.py."""
 | 
| 3 | from __future__ import print_function
 | 
| 4 | 
 | 
| 5 | from _devbuild.gen.id_kind_asdl import Id
 | 
| 6 | from _devbuild.gen.syntax_asdl import (
 | 
| 7 |     loc,
 | 
| 8 |     loc_t,
 | 
| 9 |     re,
 | 
| 10 |     re_e,
 | 
| 11 |     re_t,
 | 
| 12 |     Token,
 | 
| 13 |     SimpleVarSub,
 | 
| 14 |     word_part,
 | 
| 15 |     SingleQuoted,
 | 
| 16 |     DoubleQuoted,
 | 
| 17 |     BracedVarSub,
 | 
| 18 |     ShArrayLiteral,
 | 
| 19 |     CommandSub,
 | 
| 20 |     expr,
 | 
| 21 |     expr_e,
 | 
| 22 |     expr_t,
 | 
| 23 |     y_lhs_e,
 | 
| 24 |     y_lhs_t,
 | 
| 25 |     Attribute,
 | 
| 26 |     Subscript,
 | 
| 27 |     class_literal_term,
 | 
| 28 |     class_literal_term_e,
 | 
| 29 |     class_literal_term_t,
 | 
| 30 |     char_class_term_t,
 | 
| 31 |     PosixClass,
 | 
| 32 |     PerlClass,
 | 
| 33 |     CharCode,
 | 
| 34 |     CharRange,
 | 
| 35 |     ArgList,
 | 
| 36 |     Eggex,
 | 
| 37 | )
 | 
| 38 | from _devbuild.gen.runtime_asdl import (
 | 
| 39 |     coerced_e,
 | 
| 40 |     coerced_t,
 | 
| 41 |     scope_e,
 | 
| 42 |     scope_t,
 | 
| 43 |     part_value,
 | 
| 44 |     part_value_t,
 | 
| 45 |     Piece,
 | 
| 46 | )
 | 
| 47 | from _devbuild.gen.value_asdl import (value, value_e, value_t, y_lvalue,
 | 
| 48 |                                       y_lvalue_e, y_lvalue_t, IntBox, LeftName)
 | 
| 49 | from core import error
 | 
| 50 | from core.error import e_die, e_die_status
 | 
| 51 | from core import num
 | 
| 52 | from core import pyutil
 | 
| 53 | from core import state
 | 
| 54 | from core import ui
 | 
| 55 | from core import vm
 | 
| 56 | from data_lang import j8
 | 
| 57 | from frontend import lexer
 | 
| 58 | from frontend import match
 | 
| 59 | from frontend import typed_args
 | 
| 60 | from osh import braces
 | 
| 61 | from mycpp import mops
 | 
| 62 | from mycpp.mylib import log, NewDict, switch, tagswitch, print_stderr
 | 
| 63 | from ysh import func_proc
 | 
| 64 | from ysh import val_ops
 | 
| 65 | 
 | 
| 66 | import libc
 | 
| 67 | 
 | 
| 68 | from typing import cast, Optional, Dict, List, Tuple, TYPE_CHECKING
 | 
| 69 | 
 | 
| 70 | if TYPE_CHECKING:
 | 
| 71 |     from osh import cmd_eval
 | 
| 72 |     from osh import word_eval
 | 
| 73 |     from osh import split
 | 
| 74 | 
 | 
| 75 | _ = log
 | 
| 76 | 
 | 
| 77 | 
 | 
| 78 | def LookupVar(mem, var_name, which_scopes, var_loc):
 | 
| 79 |     # type: (state.Mem, str, scope_t, loc_t) -> value_t
 | 
| 80 | 
 | 
| 81 |     # Lookup WITHOUT dynamic scope.
 | 
| 82 |     val = mem.GetValue(var_name, which_scopes=which_scopes)
 | 
| 83 |     if val.tag() == value_e.Undef:
 | 
| 84 |         e_die('Undefined variable %r' % var_name, var_loc)
 | 
| 85 | 
 | 
| 86 |     return val
 | 
| 87 | 
 | 
| 88 | 
 | 
| 89 | def _ConvertToInt(val, msg, blame_loc):
 | 
| 90 |     # type: (value_t, str, loc_t) -> mops.BigInt
 | 
| 91 |     UP_val = val
 | 
| 92 |     with tagswitch(val) as case:
 | 
| 93 |         if case(value_e.Int):
 | 
| 94 |             val = cast(value.Int, UP_val)
 | 
| 95 |             return val.i
 | 
| 96 | 
 | 
| 97 |         elif case(value_e.Str):
 | 
| 98 |             val = cast(value.Str, UP_val)
 | 
| 99 |             if match.LooksLikeInteger(val.s):
 | 
| 100 |                 # TODO: Handle ValueError
 | 
| 101 |                 return mops.FromStr(val.s)
 | 
| 102 | 
 | 
| 103 |     raise error.TypeErr(val, msg, blame_loc)
 | 
| 104 | 
 | 
| 105 | 
 | 
| 106 | def _ConvertToNumber(val):
 | 
| 107 |     # type: (value_t) -> Tuple[coerced_t, mops.BigInt, float]
 | 
| 108 |     UP_val = val
 | 
| 109 |     with tagswitch(val) as case:
 | 
| 110 |         if case(value_e.Int):
 | 
| 111 |             val = cast(value.Int, UP_val)
 | 
| 112 |             return coerced_e.Int, val.i, -1.0
 | 
| 113 | 
 | 
| 114 |         elif case(value_e.Float):
 | 
| 115 |             val = cast(value.Float, UP_val)
 | 
| 116 |             return coerced_e.Float, mops.MINUS_ONE, val.f
 | 
| 117 | 
 | 
| 118 |         elif case(value_e.Str):
 | 
| 119 |             val = cast(value.Str, UP_val)
 | 
| 120 |             if match.LooksLikeInteger(val.s):
 | 
| 121 |                 # TODO: Handle ValueError
 | 
| 122 |                 return coerced_e.Int, mops.FromStr(val.s), -1.0
 | 
| 123 | 
 | 
| 124 |             if match.LooksLikeFloat(val.s):
 | 
| 125 |                 return coerced_e.Float, mops.MINUS_ONE, float(val.s)
 | 
| 126 | 
 | 
| 127 |     return coerced_e.Neither, mops.MINUS_ONE, -1.0
 | 
| 128 | 
 | 
| 129 | 
 | 
| 130 | def _ConvertForBinaryOp(left, right):
 | 
| 131 |     # type: (value_t, value_t) -> Tuple[coerced_t, mops.BigInt, mops.BigInt, float, float]
 | 
| 132 |     """
 | 
| 133 |     Returns one of
 | 
| 134 |       value_e.Int or value_e.Float
 | 
| 135 |       2 ints or 2 floats
 | 
| 136 | 
 | 
| 137 |     To indicate which values the operation should be done on
 | 
| 138 |     """
 | 
| 139 |     c1, i1, f1 = _ConvertToNumber(left)
 | 
| 140 |     c2, i2, f2 = _ConvertToNumber(right)
 | 
| 141 | 
 | 
| 142 |     nope = mops.MINUS_ONE
 | 
| 143 | 
 | 
| 144 |     if c1 == coerced_e.Int and c2 == coerced_e.Int:
 | 
| 145 |         return coerced_e.Int, i1, i2, -1.0, -1.0
 | 
| 146 | 
 | 
| 147 |     elif c1 == coerced_e.Int and c2 == coerced_e.Float:
 | 
| 148 |         return coerced_e.Float, nope, nope, mops.ToFloat(i1), f2
 | 
| 149 | 
 | 
| 150 |     elif c1 == coerced_e.Float and c2 == coerced_e.Int:
 | 
| 151 |         return coerced_e.Float, nope, nope, f1, mops.ToFloat(i2)
 | 
| 152 | 
 | 
| 153 |     elif c1 == coerced_e.Float and c2 == coerced_e.Float:
 | 
| 154 |         return coerced_e.Float, nope, nope, f1, f2
 | 
| 155 | 
 | 
| 156 |     else:
 | 
| 157 |         # No operation is valid
 | 
| 158 |         return coerced_e.Neither, nope, nope, -1.0, -1.0
 | 
| 159 | 
 | 
| 160 | 
 | 
| 161 | class ExprEvaluator(object):
 | 
| 162 |     """Shared between arith and bool evaluators.
 | 
| 163 | 
 | 
| 164 |     They both:
 | 
| 165 | 
 | 
| 166 |     1. Convert strings to integers, respecting shopt -s strict_arith.
 | 
| 167 |     2. Look up variables and evaluate words.
 | 
| 168 |     """
 | 
| 169 | 
 | 
| 170 |     def __init__(
 | 
| 171 |             self,
 | 
| 172 |             mem,  # type: state.Mem
 | 
| 173 |             mutable_opts,  # type: state.MutableOpts
 | 
| 174 |             methods,  # type: Dict[int, Dict[str, vm._Callable]]
 | 
| 175 |             splitter,  # type: split.SplitContext
 | 
| 176 |             errfmt,  # type: ui.ErrorFormatter
 | 
| 177 |     ):
 | 
| 178 |         # type: (...) -> None
 | 
| 179 |         self.shell_ex = None  # type: vm._Executor
 | 
| 180 |         self.cmd_ev = None  # type: cmd_eval.CommandEvaluator
 | 
| 181 |         self.word_ev = None  # type: word_eval.AbstractWordEvaluator
 | 
| 182 | 
 | 
| 183 |         self.mem = mem
 | 
| 184 |         self.mutable_opts = mutable_opts
 | 
| 185 |         self.methods = methods
 | 
| 186 |         self.splitter = splitter
 | 
| 187 |         self.errfmt = errfmt
 | 
| 188 | 
 | 
| 189 |     def CheckCircularDeps(self):
 | 
| 190 |         # type: () -> None
 | 
| 191 |         assert self.shell_ex is not None
 | 
| 192 |         assert self.word_ev is not None
 | 
| 193 | 
 | 
| 194 |     def _LookupVar(self, name, var_loc):
 | 
| 195 |         # type: (str, loc_t) -> value_t
 | 
| 196 |         return LookupVar(self.mem, name, scope_e.LocalOrGlobal, var_loc)
 | 
| 197 | 
 | 
| 198 |     def EvalAugmented(self, lval, rhs_val, op, which_scopes):
 | 
| 199 |         # type: (y_lvalue_t, value_t, Token, scope_t) -> None
 | 
| 200 |         """ setvar x +=1, setvar L[0] -= 1 
 | 
| 201 | 
 | 
| 202 |         Called by CommandEvaluator
 | 
| 203 |         """
 | 
| 204 |         # TODO: It might be nice to do auto d[x] += 1 too
 | 
| 205 | 
 | 
| 206 |         UP_lval = lval
 | 
| 207 |         with tagswitch(lval) as case:
 | 
| 208 |             if case(y_lvalue_e.Local):  # setvar x += 1
 | 
| 209 |                 lval = cast(LeftName, UP_lval)
 | 
| 210 |                 lhs_val = self._LookupVar(lval.name, lval.blame_loc)
 | 
| 211 |                 if op.id in (Id.Arith_PlusEqual, Id.Arith_MinusEqual,
 | 
| 212 |                              Id.Arith_StarEqual, Id.Arith_SlashEqual):
 | 
| 213 |                     new_val = self._ArithIntFloat(lhs_val, rhs_val, op)
 | 
| 214 |                 else:
 | 
| 215 |                     new_val = self._ArithIntOnly(lhs_val, rhs_val, op)
 | 
| 216 | 
 | 
| 217 |                 self.mem.SetNamed(lval, new_val, which_scopes)
 | 
| 218 | 
 | 
| 219 |             elif case(y_lvalue_e.Container):  # setvar d.key += 1
 | 
| 220 |                 lval = cast(y_lvalue.Container, UP_lval)
 | 
| 221 | 
 | 
| 222 |                 obj = lval.obj
 | 
| 223 |                 UP_obj = obj
 | 
| 224 | 
 | 
| 225 |                 lhs_val_ = None  # type: value_t
 | 
| 226 |                 # Similar to command_e.Mutation
 | 
| 227 |                 with tagswitch(obj) as case:
 | 
| 228 |                     if case(value_e.List):
 | 
| 229 |                         obj = cast(value.List, UP_obj)
 | 
| 230 |                         index = val_ops.ToInt(lval.index,
 | 
| 231 |                                               'List index should be Int',
 | 
| 232 |                                               loc.Missing)
 | 
| 233 |                         try:
 | 
| 234 |                             lhs_val_ = obj.items[index]
 | 
| 235 |                         except IndexError:
 | 
| 236 |                             raise error.Expr(
 | 
| 237 |                                 'List index out of range: %d' % index,
 | 
| 238 |                                 loc.Missing)
 | 
| 239 | 
 | 
| 240 |                     elif case(value_e.Dict):
 | 
| 241 |                         obj = cast(value.Dict, UP_obj)
 | 
| 242 |                         index = -1  # silence C++ warning
 | 
| 243 |                         key = val_ops.ToStr(lval.index,
 | 
| 244 |                                             'Dict index should be Str',
 | 
| 245 |                                             loc.Missing)
 | 
| 246 |                         try:
 | 
| 247 |                             lhs_val_ = obj.d[key]
 | 
| 248 |                         except KeyError:
 | 
| 249 |                             raise error.Expr('Dict entry not found: %r' % key,
 | 
| 250 |                                              loc.Missing)
 | 
| 251 | 
 | 
| 252 |                     else:
 | 
| 253 |                         raise error.TypeErr(
 | 
| 254 |                             obj, "obj[index] expected List or Dict",
 | 
| 255 |                             loc.Missing)
 | 
| 256 | 
 | 
| 257 |                 if op.id in (Id.Arith_PlusEqual, Id.Arith_MinusEqual,
 | 
| 258 |                              Id.Arith_StarEqual, Id.Arith_SlashEqual):
 | 
| 259 |                     new_val_ = self._ArithIntFloat(lhs_val_, rhs_val, op)
 | 
| 260 |                 else:
 | 
| 261 |                     new_val_ = self._ArithIntOnly(lhs_val_, rhs_val, op)
 | 
| 262 | 
 | 
| 263 |                 with tagswitch(obj) as case:
 | 
| 264 |                     if case(value_e.List):
 | 
| 265 |                         obj = cast(value.List, UP_obj)
 | 
| 266 |                         assert index != -1, 'Should have been initialized'
 | 
| 267 |                         obj.items[index] = new_val_
 | 
| 268 | 
 | 
| 269 |                     elif case(value_e.Dict):
 | 
| 270 |                         obj = cast(value.Dict, UP_obj)
 | 
| 271 |                         obj.d[key] = new_val_
 | 
| 272 | 
 | 
| 273 |             else:
 | 
| 274 |                 raise AssertionError()
 | 
| 275 | 
 | 
| 276 |     def _EvalLeftLocalOrGlobal(self, lhs, which_scopes):
 | 
| 277 |         # type: (expr_t, scope_t) -> value_t
 | 
| 278 |         """Evaluate the LEFT MOST part, respecting setvar/setglobal.
 | 
| 279 | 
 | 
| 280 |         Consider this statement:
 | 
| 281 | 
 | 
| 282 |             setglobal g[a[i]] = 42
 | 
| 283 | 
 | 
| 284 |         - The g is always global, never local.  It's the thing to be mutated.
 | 
| 285 |         - The a can be local or global
 | 
| 286 |         """
 | 
| 287 |         UP_lhs = lhs
 | 
| 288 |         with tagswitch(lhs) as case:
 | 
| 289 |             if case(expr_e.Var):
 | 
| 290 |                 lhs = cast(expr.Var, UP_lhs)
 | 
| 291 | 
 | 
| 292 |                 # respect setvar/setglobal with which_scopes
 | 
| 293 |                 return LookupVar(self.mem, lhs.name, which_scopes, lhs.left)
 | 
| 294 | 
 | 
| 295 |             elif case(expr_e.Subscript):
 | 
| 296 |                 lhs = cast(Subscript, UP_lhs)
 | 
| 297 | 
 | 
| 298 |                 # recursive call
 | 
| 299 |                 obj = self._EvalLeftLocalOrGlobal(lhs.obj, which_scopes)
 | 
| 300 |                 index = self._EvalExpr(lhs.index)
 | 
| 301 | 
 | 
| 302 |                 return self._EvalSubscript(obj, index)
 | 
| 303 | 
 | 
| 304 |             elif case(expr_e.Attribute):
 | 
| 305 |                 lhs = cast(Attribute, UP_lhs)
 | 
| 306 |                 assert lhs.op.id == Id.Expr_Dot
 | 
| 307 | 
 | 
| 308 |                 # recursive call
 | 
| 309 |                 obj = self._EvalLeftLocalOrGlobal(lhs.obj, which_scopes)
 | 
| 310 |                 return self._EvalDot(lhs, obj)
 | 
| 311 | 
 | 
| 312 |             else:
 | 
| 313 |                 # Shouldn't happen because of Transformer._CheckLhs
 | 
| 314 |                 raise AssertionError()
 | 
| 315 | 
 | 
| 316 |     def _EvalLhsExpr(self, lhs, which_scopes):
 | 
| 317 |         # type: (y_lhs_t, scope_t) -> y_lvalue_t
 | 
| 318 |         """
 | 
| 319 |         Handle setvar x, setvar a[i], ... setglobal x, setglobal a[i]
 | 
| 320 |         """
 | 
| 321 |         UP_lhs = lhs
 | 
| 322 |         with tagswitch(lhs) as case:
 | 
| 323 |             if case(y_lhs_e.Var):
 | 
| 324 |                 lhs = cast(Token, UP_lhs)
 | 
| 325 |                 return LeftName(lexer.LazyStr(lhs), lhs)
 | 
| 326 | 
 | 
| 327 |             elif case(y_lhs_e.Subscript):
 | 
| 328 |                 lhs = cast(Subscript, UP_lhs)
 | 
| 329 |                 # setvar mylist[0] = 42
 | 
| 330 |                 # setvar mydict['key'] = 42
 | 
| 331 | 
 | 
| 332 |                 lval = self._EvalLeftLocalOrGlobal(lhs.obj, which_scopes)
 | 
| 333 |                 index = self._EvalExpr(lhs.index)
 | 
| 334 |                 return y_lvalue.Container(lval, index)
 | 
| 335 | 
 | 
| 336 |             elif case(y_lhs_e.Attribute):
 | 
| 337 |                 lhs = cast(Attribute, UP_lhs)
 | 
| 338 |                 assert lhs.op.id == Id.Expr_Dot
 | 
| 339 | 
 | 
| 340 |                 # setvar mydict.key = 42
 | 
| 341 |                 lval = self._EvalLeftLocalOrGlobal(lhs.obj, which_scopes)
 | 
| 342 | 
 | 
| 343 |                 attr = value.Str(lhs.attr_name)
 | 
| 344 |                 return y_lvalue.Container(lval, attr)
 | 
| 345 | 
 | 
| 346 |             else:
 | 
| 347 |                 raise AssertionError()
 | 
| 348 | 
 | 
| 349 |     def EvalExpr(self, node, blame_loc):
 | 
| 350 |         # type: (expr_t, loc_t) -> value_t
 | 
| 351 |         """Public API for _EvalExpr to ensure command_sub_errexit"""
 | 
| 352 |         self.mem.SetLocationForExpr(blame_loc)
 | 
| 353 |         # Pure C++ won't need to catch exceptions
 | 
| 354 |         with state.ctx_YshExpr(self.mutable_opts):
 | 
| 355 |             val = self._EvalExpr(node)
 | 
| 356 |         return val
 | 
| 357 | 
 | 
| 358 |     def EvalLhsExpr(self, lhs, which_scopes):
 | 
| 359 |         # type: (y_lhs_t, scope_t) -> y_lvalue_t
 | 
| 360 |         """Public API for _EvalLhsExpr to ensure command_sub_errexit"""
 | 
| 361 |         with state.ctx_YshExpr(self.mutable_opts):
 | 
| 362 |             lval = self._EvalLhsExpr(lhs, which_scopes)
 | 
| 363 |         return lval
 | 
| 364 | 
 | 
| 365 |     def EvalExprSub(self, part):
 | 
| 366 |         # type: (word_part.ExprSub) -> part_value_t
 | 
| 367 | 
 | 
| 368 |         val = self.EvalExpr(part.child, part.left)
 | 
| 369 | 
 | 
| 370 |         with switch(part.left.id) as case:
 | 
| 371 |             if case(Id.Left_DollarBracket):  # $[join(x)]
 | 
| 372 |                 s = val_ops.Stringify(val, loc.WordPart(part))
 | 
| 373 |                 return Piece(s, False, False)
 | 
| 374 | 
 | 
| 375 |             elif case(Id.Lit_AtLBracket):  # @[split(x)]
 | 
| 376 |                 strs = val_ops.ToShellArray(val,
 | 
| 377 |                                             loc.WordPart(part),
 | 
| 378 |                                             prefix='Expr splice ')
 | 
| 379 |                 return part_value.Array(strs)
 | 
| 380 | 
 | 
| 381 |             else:
 | 
| 382 |                 raise AssertionError(part.left)
 | 
| 383 | 
 | 
| 384 |     def PluginCall(self, func_val, pos_args):
 | 
| 385 |         # type: (value.Func, List[value_t]) -> value_t
 | 
| 386 |         """For renderPrompt()
 | 
| 387 | 
 | 
| 388 |         Similar to
 | 
| 389 |         - WordEvaluator.EvalForPlugin(), which evaluates $PS1 outside main loop
 | 
| 390 |         - ReadlineCallback.__call__, which executes shell outside main loop
 | 
| 391 |         """
 | 
| 392 |         with state.ctx_YshExpr(self.mutable_opts):
 | 
| 393 |             with state.ctx_Registers(self.mem):  # to sandbox globals
 | 
| 394 |                 named_args = {}  # type: Dict[str, value_t]
 | 
| 395 |                 arg_list = ArgList.CreateNull()  # There's no call site
 | 
| 396 |                 rd = typed_args.Reader(pos_args, named_args, None, arg_list)
 | 
| 397 | 
 | 
| 398 |                 try:
 | 
| 399 |                     val = func_proc.CallUserFunc(func_val, rd, self.mem,
 | 
| 400 |                                                  self.cmd_ev)
 | 
| 401 |                 except error.FatalRuntime as e:
 | 
| 402 |                     val = value.Str('<Runtime error: %s>' %
 | 
| 403 |                                     e.UserErrorString())
 | 
| 404 | 
 | 
| 405 |                 except (IOError, OSError) as e:
 | 
| 406 |                     val = value.Str('<I/O error: %s>' % pyutil.strerror(e))
 | 
| 407 | 
 | 
| 408 |                 except KeyboardInterrupt:
 | 
| 409 |                     val = value.Str('<Ctrl-C>')
 | 
| 410 | 
 | 
| 411 |         return val
 | 
| 412 | 
 | 
| 413 |     def CallConvertFunc(self, func_val, arg, convert_tok, call_loc):
 | 
| 414 |         # type: (value_t, value_t, Token, loc_t) -> value_t
 | 
| 415 |         """ For Eggex captures """
 | 
| 416 |         with state.ctx_YshExpr(self.mutable_opts):
 | 
| 417 |             pos_args = [arg]
 | 
| 418 |             named_args = {}  # type: Dict[str, value_t]
 | 
| 419 |             arg_list = ArgList.CreateNull()  # There's no call site
 | 
| 420 |             rd = typed_args.Reader(pos_args, named_args, None, arg_list)
 | 
| 421 |             rd.SetFallbackLocation(convert_tok)
 | 
| 422 |             try:
 | 
| 423 |                 val = self._CallFunc(func_val, rd)
 | 
| 424 |             except error.FatalRuntime as e:
 | 
| 425 |                 func_name = lexer.TokenVal(convert_tok)
 | 
| 426 |                 self.errfmt.Print_(
 | 
| 427 |                     'Fatal error calling Eggex conversion func %r from this Match accessor'
 | 
| 428 |                     % func_name, call_loc)
 | 
| 429 |                 print_stderr('')
 | 
| 430 |                 raise
 | 
| 431 | 
 | 
| 432 |         return val
 | 
| 433 | 
 | 
| 434 |     def SpliceValue(self, val, part):
 | 
| 435 |         # type: (value_t, word_part.Splice) -> List[str]
 | 
| 436 |         """ write -- @myvar """
 | 
| 437 |         return val_ops.ToShellArray(val, loc.WordPart(part), prefix='Splice ')
 | 
| 438 | 
 | 
| 439 |     def _EvalConst(self, node):
 | 
| 440 |         # type: (expr.Const) -> value_t
 | 
| 441 |         return node.val
 | 
| 442 | 
 | 
| 443 |     def _EvalUnary(self, node):
 | 
| 444 |         # type: (expr.Unary) -> value_t
 | 
| 445 | 
 | 
| 446 |         val = self._EvalExpr(node.child)
 | 
| 447 | 
 | 
| 448 |         with switch(node.op.id) as case:
 | 
| 449 |             if case(Id.Arith_Minus):
 | 
| 450 |                 c1, i1, f1 = _ConvertToNumber(val)
 | 
| 451 |                 if c1 == coerced_e.Int:
 | 
| 452 |                     return value.Int(mops.Negate(i1))
 | 
| 453 |                 if c1 == coerced_e.Float:
 | 
| 454 |                     return value.Float(-f1)
 | 
| 455 |                 raise error.TypeErr(val, 'Negation expected Int or Float',
 | 
| 456 |                                     node.op)
 | 
| 457 | 
 | 
| 458 |             elif case(Id.Arith_Tilde):
 | 
| 459 |                 i = _ConvertToInt(val, '~ expected Int', node.op)
 | 
| 460 |                 return value.Int(mops.BitNot(i))
 | 
| 461 | 
 | 
| 462 |             elif case(Id.Expr_Not):
 | 
| 463 |                 b = val_ops.ToBool(val)
 | 
| 464 |                 return value.Bool(False if b else True)
 | 
| 465 | 
 | 
| 466 |             # &s  &a[0]  &d.key  &d.nested.other
 | 
| 467 |             elif case(Id.Arith_Amp):
 | 
| 468 |                 # Only 3 possibilities:
 | 
| 469 |                 # - expr.Var
 | 
| 470 |                 # - expr.Attribute with `.` operator (d.key)
 | 
| 471 |                 # - expr.SubScript
 | 
| 472 |                 #
 | 
| 473 |                 # See _EvalLhsExpr, which gives you y_lvalue
 | 
| 474 | 
 | 
| 475 |                 # TODO: &x, &a[0], &d.key, creates a value.Place?
 | 
| 476 |                 # If it's Attribute or SubScript, you don't evaluate them.
 | 
| 477 |                 # y_lvalue_t -> place_t
 | 
| 478 | 
 | 
| 479 |                 raise NotImplementedError(node.op)
 | 
| 480 | 
 | 
| 481 |             else:
 | 
| 482 |                 raise AssertionError(node.op)
 | 
| 483 | 
 | 
| 484 |         raise AssertionError('for C++ compiler')
 | 
| 485 | 
 | 
| 486 |     def _ArithIntFloat(self, left, right, op):
 | 
| 487 |         # type: (value_t, value_t, Token) -> value_t
 | 
| 488 |         """
 | 
| 489 |         Note: may be replaced with arithmetic on tagged integers, e.g. 60 bit
 | 
| 490 |         with overflow detection
 | 
| 491 |         """
 | 
| 492 |         c, i1, i2, f1, f2 = _ConvertForBinaryOp(left, right)
 | 
| 493 | 
 | 
| 494 |         op_id = op.id
 | 
| 495 | 
 | 
| 496 |         if c == coerced_e.Int:
 | 
| 497 |             with switch(op_id) as case:
 | 
| 498 |                 if case(Id.Arith_Plus, Id.Arith_PlusEqual):
 | 
| 499 |                     return value.Int(mops.Add(i1, i2))
 | 
| 500 |                 elif case(Id.Arith_Minus, Id.Arith_MinusEqual):
 | 
| 501 |                     return value.Int(mops.Sub(i1, i2))
 | 
| 502 |                 elif case(Id.Arith_Star, Id.Arith_StarEqual):
 | 
| 503 |                     return value.Int(mops.Mul(i1, i2))
 | 
| 504 |                 elif case(Id.Arith_Slash, Id.Arith_SlashEqual):
 | 
| 505 |                     if mops.Equal(i2, mops.ZERO):
 | 
| 506 |                         raise error.Expr('Divide by zero', op)
 | 
| 507 |                     return value.Float(mops.ToFloat(i1) / mops.ToFloat(i2))
 | 
| 508 |                 else:
 | 
| 509 |                     raise AssertionError()
 | 
| 510 | 
 | 
| 511 |         elif c == coerced_e.Float:
 | 
| 512 |             with switch(op_id) as case:
 | 
| 513 |                 if case(Id.Arith_Plus, Id.Arith_PlusEqual):
 | 
| 514 |                     return value.Float(f1 + f2)
 | 
| 515 |                 elif case(Id.Arith_Minus, Id.Arith_MinusEqual):
 | 
| 516 |                     return value.Float(f1 - f2)
 | 
| 517 |                 elif case(Id.Arith_Star, Id.Arith_StarEqual):
 | 
| 518 |                     return value.Float(f1 * f2)
 | 
| 519 |                 elif case(Id.Arith_Slash, Id.Arith_SlashEqual):
 | 
| 520 |                     if f2 == 0.0:
 | 
| 521 |                         raise error.Expr('Divide by zero', op)
 | 
| 522 |                     return value.Float(f1 / f2)
 | 
| 523 |                 else:
 | 
| 524 |                     raise AssertionError()
 | 
| 525 | 
 | 
| 526 |         else:
 | 
| 527 |             raise error.TypeErrVerbose(
 | 
| 528 |                 'Binary operator expected numbers, got %s and %s (OILS-ERR-201)'
 | 
| 529 |                 % (ui.ValType(left), ui.ValType(right)), op)
 | 
| 530 | 
 | 
| 531 |     def _ArithIntOnly(self, left, right, op):
 | 
| 532 |         # type: (value_t, value_t, Token) -> value_t
 | 
| 533 | 
 | 
| 534 |         i1 = _ConvertToInt(left, 'Left operand should be Int', op)
 | 
| 535 |         i2 = _ConvertToInt(right, 'Right operand should be Int', op)
 | 
| 536 | 
 | 
| 537 |         with switch(op.id) as case:
 | 
| 538 | 
 | 
| 539 |             # a % b   setvar a %= b
 | 
| 540 |             if case(Id.Arith_Percent, Id.Arith_PercentEqual):
 | 
| 541 |                 if mops.Equal(i2, mops.ZERO):
 | 
| 542 |                     raise error.Expr('Divide by zero', op)
 | 
| 543 |                 if mops.Greater(mops.ZERO, i2):
 | 
| 544 |                     # Disallow this to remove confusion between modulus and remainder
 | 
| 545 |                     raise error.Expr("Divisor can't be negative", op)
 | 
| 546 | 
 | 
| 547 |                 return value.Int(num.IntRemainder(i1, i2))
 | 
| 548 | 
 | 
| 549 |             # a // b   setvar a //= b
 | 
| 550 |             elif case(Id.Expr_DSlash, Id.Expr_DSlashEqual):
 | 
| 551 |                 if mops.Equal(i2, mops.ZERO):
 | 
| 552 |                     raise error.Expr('Divide by zero', op)
 | 
| 553 |                 return value.Int(num.IntDivide(i1, i2))
 | 
| 554 | 
 | 
| 555 |             # a ** b   setvar a **= b (ysh only)
 | 
| 556 |             elif case(Id.Arith_DStar, Id.Expr_DStarEqual):
 | 
| 557 |                 # Same as sh_expr_eval.py
 | 
| 558 |                 if mops.Greater(mops.ZERO, i2):
 | 
| 559 |                     raise error.Expr("Exponent can't be a negative number", op)
 | 
| 560 |                 return value.Int(num.Exponent(i1, i2))
 | 
| 561 | 
 | 
| 562 |             # Bitwise
 | 
| 563 |             elif case(Id.Arith_Amp, Id.Arith_AmpEqual):  # &
 | 
| 564 |                 return value.Int(mops.BitAnd(i1, i2))
 | 
| 565 | 
 | 
| 566 |             elif case(Id.Arith_Pipe, Id.Arith_PipeEqual):  # |
 | 
| 567 |                 return value.Int(mops.BitOr(i1, i2))
 | 
| 568 | 
 | 
| 569 |             elif case(Id.Arith_Caret, Id.Arith_CaretEqual):  # ^
 | 
| 570 |                 return value.Int(mops.BitXor(i1, i2))
 | 
| 571 | 
 | 
| 572 |             elif case(Id.Arith_DGreat, Id.Arith_DGreatEqual):  # >>
 | 
| 573 |                 return value.Int(mops.RShift(i1, i2))
 | 
| 574 | 
 | 
| 575 |             elif case(Id.Arith_DLess, Id.Arith_DLessEqual):  # <<
 | 
| 576 |                 return value.Int(mops.LShift(i1, i2))
 | 
| 577 | 
 | 
| 578 |             else:
 | 
| 579 |                 raise AssertionError(op.id)
 | 
| 580 | 
 | 
| 581 |     def _Concat(self, left, right, op):
 | 
| 582 |         # type: (value_t, value_t, Token) -> value_t
 | 
| 583 |         UP_left = left
 | 
| 584 |         UP_right = right
 | 
| 585 | 
 | 
| 586 |         if left.tag() == value_e.Str and right.tag() == value_e.Str:
 | 
| 587 |             left = cast(value.Str, UP_left)
 | 
| 588 |             right = cast(value.Str, UP_right)
 | 
| 589 | 
 | 
| 590 |             return value.Str(left.s + right.s)
 | 
| 591 | 
 | 
| 592 |         elif left.tag() == value_e.List and right.tag() == value_e.List:
 | 
| 593 |             left = cast(value.List, UP_left)
 | 
| 594 |             right = cast(value.List, UP_right)
 | 
| 595 | 
 | 
| 596 |             c = list(left.items)  # mycpp rewrite of L1 + L2
 | 
| 597 |             c.extend(right.items)
 | 
| 598 |             return value.List(c)
 | 
| 599 | 
 | 
| 600 |         else:
 | 
| 601 |             raise error.TypeErrVerbose(
 | 
| 602 |                 'Expected Str ++ Str or List ++ List, got %s ++ %s' %
 | 
| 603 |                 (ui.ValType(left), ui.ValType(right)), op)
 | 
| 604 | 
 | 
| 605 |     def _EvalBinary(self, node):
 | 
| 606 |         # type: (expr.Binary) -> value_t
 | 
| 607 | 
 | 
| 608 |         left = self._EvalExpr(node.left)
 | 
| 609 | 
 | 
| 610 |         # Logical and/or lazily evaluate
 | 
| 611 |         with switch(node.op.id) as case:
 | 
| 612 |             if case(Id.Expr_And):
 | 
| 613 |                 if val_ops.ToBool(left):  # no errors
 | 
| 614 |                     return self._EvalExpr(node.right)
 | 
| 615 |                 else:
 | 
| 616 |                     return left
 | 
| 617 | 
 | 
| 618 |             elif case(Id.Expr_Or):
 | 
| 619 |                 if val_ops.ToBool(left):
 | 
| 620 |                     return left
 | 
| 621 |                 else:
 | 
| 622 |                     return self._EvalExpr(node.right)
 | 
| 623 | 
 | 
| 624 |         # These operators all eagerly evaluate
 | 
| 625 |         right = self._EvalExpr(node.right)
 | 
| 626 | 
 | 
| 627 |         with switch(node.op.id) as case:
 | 
| 628 |             if case(Id.Arith_DPlus):  # a ++ b to concat Str or List
 | 
| 629 |                 return self._Concat(left, right, node.op)
 | 
| 630 | 
 | 
| 631 |             elif case(Id.Arith_Plus, Id.Arith_Minus, Id.Arith_Star,
 | 
| 632 |                       Id.Arith_Slash):
 | 
| 633 |                 return self._ArithIntFloat(left, right, node.op)
 | 
| 634 | 
 | 
| 635 |             else:
 | 
| 636 |                 return self._ArithIntOnly(left, right, node.op)
 | 
| 637 | 
 | 
| 638 |     def _CompareNumeric(self, left, right, op):
 | 
| 639 |         # type: (value_t, value_t, Token) -> bool
 | 
| 640 |         c, i1, i2, f1, f2 = _ConvertForBinaryOp(left, right)
 | 
| 641 | 
 | 
| 642 |         if c == coerced_e.Int:
 | 
| 643 |             with switch(op.id) as case:
 | 
| 644 |                 if case(Id.Arith_Less):
 | 
| 645 |                     return mops.Greater(i2, i1)
 | 
| 646 |                 elif case(Id.Arith_Great):
 | 
| 647 |                     return mops.Greater(i1, i2)
 | 
| 648 |                 elif case(Id.Arith_LessEqual):
 | 
| 649 |                     return mops.Greater(i2, i1) or mops.Equal(i1, i2)
 | 
| 650 |                 elif case(Id.Arith_GreatEqual):
 | 
| 651 |                     return mops.Greater(i1, i2) or mops.Equal(i1, i2)
 | 
| 652 |                 else:
 | 
| 653 |                     raise AssertionError()
 | 
| 654 | 
 | 
| 655 |         elif c == coerced_e.Float:
 | 
| 656 |             with switch(op.id) as case:
 | 
| 657 |                 if case(Id.Arith_Less):
 | 
| 658 |                     return f1 < f2
 | 
| 659 |                 elif case(Id.Arith_Great):
 | 
| 660 |                     return f1 > f2
 | 
| 661 |                 elif case(Id.Arith_LessEqual):
 | 
| 662 |                     return f1 <= f2
 | 
| 663 |                 elif case(Id.Arith_GreatEqual):
 | 
| 664 |                     return f1 >= f2
 | 
| 665 |                 else:
 | 
| 666 |                     raise AssertionError()
 | 
| 667 | 
 | 
| 668 |         else:
 | 
| 669 |             raise error.TypeErrVerbose(
 | 
| 670 |                 'Comparison operator expected numbers, got %s and %s' %
 | 
| 671 |                 (ui.ValType(left), ui.ValType(right)), op)
 | 
| 672 | 
 | 
| 673 |     def _EvalCompare(self, node):
 | 
| 674 |         # type: (expr.Compare) -> value_t
 | 
| 675 | 
 | 
| 676 |         left = self._EvalExpr(node.left)
 | 
| 677 |         result = True  # Implicit and
 | 
| 678 |         for i, op in enumerate(node.ops):
 | 
| 679 |             right_expr = node.comparators[i]
 | 
| 680 | 
 | 
| 681 |             right = self._EvalExpr(right_expr)
 | 
| 682 | 
 | 
| 683 |             if op.id in (Id.Arith_Less, Id.Arith_Great, Id.Arith_LessEqual,
 | 
| 684 |                          Id.Arith_GreatEqual):
 | 
| 685 |                 result = self._CompareNumeric(left, right, op)
 | 
| 686 | 
 | 
| 687 |             elif op.id == Id.Expr_TEqual:
 | 
| 688 |                 if left.tag() != right.tag():
 | 
| 689 |                     result = False
 | 
| 690 |                 else:
 | 
| 691 |                     result = val_ops.ExactlyEqual(left, right, op)
 | 
| 692 |             elif op.id == Id.Expr_NotDEqual:
 | 
| 693 |                 if left.tag() != right.tag():
 | 
| 694 |                     result = True
 | 
| 695 |                 else:
 | 
| 696 |                     result = not val_ops.ExactlyEqual(left, right, op)
 | 
| 697 | 
 | 
| 698 |             elif op.id == Id.Expr_In:
 | 
| 699 |                 result = val_ops.Contains(left, right)
 | 
| 700 |             elif op.id == Id.Node_NotIn:
 | 
| 701 |                 result = not val_ops.Contains(left, right)
 | 
| 702 | 
 | 
| 703 |             elif op.id == Id.Expr_Is:
 | 
| 704 |                 if left.tag() != right.tag():
 | 
| 705 |                     raise error.TypeErrVerbose('Mismatched types', op)
 | 
| 706 |                 result = left is right
 | 
| 707 | 
 | 
| 708 |             elif op.id == Id.Node_IsNot:
 | 
| 709 |                 if left.tag() != right.tag():
 | 
| 710 |                     raise error.TypeErrVerbose('Mismatched types', op)
 | 
| 711 |                 result = left is not right
 | 
| 712 | 
 | 
| 713 |             elif op.id == Id.Expr_DTilde:
 | 
| 714 |                 # no extglob in Oil language; use eggex
 | 
| 715 |                 if left.tag() != value_e.Str:
 | 
| 716 |                     raise error.TypeErrVerbose('LHS must be Str', op)
 | 
| 717 | 
 | 
| 718 |                 if right.tag() != value_e.Str:
 | 
| 719 |                     raise error.TypeErrVerbose('RHS must be Str', op)
 | 
| 720 | 
 | 
| 721 |                 UP_left = left
 | 
| 722 |                 UP_right = right
 | 
| 723 |                 left = cast(value.Str, UP_left)
 | 
| 724 |                 right = cast(value.Str, UP_right)
 | 
| 725 |                 return value.Bool(libc.fnmatch(right.s, left.s))
 | 
| 726 | 
 | 
| 727 |             elif op.id == Id.Expr_NotDTilde:
 | 
| 728 |                 if left.tag() != value_e.Str:
 | 
| 729 |                     raise error.TypeErrVerbose('LHS must be Str', op)
 | 
| 730 | 
 | 
| 731 |                 if right.tag() != value_e.Str:
 | 
| 732 |                     raise error.TypeErrVerbose('RHS must be Str', op)
 | 
| 733 | 
 | 
| 734 |                 UP_left = left
 | 
| 735 |                 UP_right = right
 | 
| 736 |                 left = cast(value.Str, UP_left)
 | 
| 737 |                 right = cast(value.Str, UP_right)
 | 
| 738 |                 return value.Bool(not libc.fnmatch(right.s, left.s))
 | 
| 739 | 
 | 
| 740 |             elif op.id == Id.Expr_TildeDEqual:
 | 
| 741 |                 # Approximate equality
 | 
| 742 |                 UP_left = left
 | 
| 743 |                 if left.tag() != value_e.Str:
 | 
| 744 |                     e_die('~== expects a string on the left', op)
 | 
| 745 | 
 | 
| 746 |                 left = cast(value.Str, UP_left)
 | 
| 747 |                 left2 = left.s.strip()
 | 
| 748 | 
 | 
| 749 |                 UP_right = right
 | 
| 750 |                 with tagswitch(right) as case:
 | 
| 751 |                     if case(value_e.Str):
 | 
| 752 |                         right = cast(value.Str, UP_right)
 | 
| 753 |                         return value.Bool(left2 == right.s)
 | 
| 754 | 
 | 
| 755 |                     elif case(value_e.Bool):
 | 
| 756 |                         right = cast(value.Bool, UP_right)
 | 
| 757 |                         left2 = left2.lower()
 | 
| 758 |                         lb = False
 | 
| 759 |                         if left2 == 'true':
 | 
| 760 |                             lb = True
 | 
| 761 |                         elif left2 == 'false':
 | 
| 762 |                             lb = False
 | 
| 763 |                         else:
 | 
| 764 |                             return value.Bool(False)
 | 
| 765 | 
 | 
| 766 |                         #log('left %r left2 %r', left, left2)
 | 
| 767 |                         return value.Bool(lb == right.b)
 | 
| 768 | 
 | 
| 769 |                     elif case(value_e.Int):
 | 
| 770 |                         right = cast(value.Int, UP_right)
 | 
| 771 |                         if not left2.isdigit():
 | 
| 772 |                             return value.Bool(False)
 | 
| 773 | 
 | 
| 774 |                         eq = mops.Equal(mops.FromStr(left2), right.i)
 | 
| 775 |                         return value.Bool(eq)
 | 
| 776 | 
 | 
| 777 |                 e_die('~== expects Str, Int, or Bool on the right', op)
 | 
| 778 | 
 | 
| 779 |             else:
 | 
| 780 |                 try:
 | 
| 781 |                     if op.id == Id.Arith_Tilde:
 | 
| 782 |                         result = val_ops.MatchRegex(left, right, self.mem)
 | 
| 783 | 
 | 
| 784 |                     elif op.id == Id.Expr_NotTilde:
 | 
| 785 |                         # don't pass self.mem to not set a match
 | 
| 786 |                         result = not val_ops.MatchRegex(left, right, None)
 | 
| 787 | 
 | 
| 788 |                     else:
 | 
| 789 |                         raise AssertionError(op)
 | 
| 790 |                 except ValueError as e:
 | 
| 791 |                     # Status 2 indicates a regex parse error, as with [[ in OSH
 | 
| 792 |                     e_die_status(2, e.message, op)
 | 
| 793 | 
 | 
| 794 |             if not result:
 | 
| 795 |                 return value.Bool(result)
 | 
| 796 | 
 | 
| 797 |             left = right
 | 
| 798 | 
 | 
| 799 |         return value.Bool(result)
 | 
| 800 | 
 | 
| 801 |     def _CallFunc(self, to_call, rd):
 | 
| 802 |         # type: (value_t, typed_args.Reader) -> value_t
 | 
| 803 | 
 | 
| 804 |         # Now apply args to either builtin or user-defined function
 | 
| 805 |         UP_to_call = to_call
 | 
| 806 |         with tagswitch(to_call) as case:
 | 
| 807 |             if case(value_e.Func):
 | 
| 808 |                 to_call = cast(value.Func, UP_to_call)
 | 
| 809 | 
 | 
| 810 |                 return func_proc.CallUserFunc(to_call, rd, self.mem,
 | 
| 811 |                                               self.cmd_ev)
 | 
| 812 | 
 | 
| 813 |             elif case(value_e.BuiltinFunc):
 | 
| 814 |                 to_call = cast(value.BuiltinFunc, UP_to_call)
 | 
| 815 | 
 | 
| 816 |                 # C++ cast to work around ASDL 'any'
 | 
| 817 |                 f = cast(vm._Callable, to_call.callable)
 | 
| 818 |                 return f.Call(rd)
 | 
| 819 |             else:
 | 
| 820 |                 raise AssertionError("Shouldn't have been bound")
 | 
| 821 | 
 | 
| 822 |     def _EvalFuncCall(self, node):
 | 
| 823 |         # type: (expr.FuncCall) -> value_t
 | 
| 824 | 
 | 
| 825 |         func = self._EvalExpr(node.func)
 | 
| 826 |         UP_func = func
 | 
| 827 | 
 | 
| 828 |         # The () operator has a 2x2 matrix of
 | 
| 829 |         #   (free, bound) x (builtin, user-defined)
 | 
| 830 | 
 | 
| 831 |         # Eval args first
 | 
| 832 |         with tagswitch(func) as case:
 | 
| 833 |             if case(value_e.Func, value_e.BuiltinFunc):
 | 
| 834 |                 to_call = func
 | 
| 835 |                 pos_args, named_args = func_proc._EvalArgList(self, node.args)
 | 
| 836 |                 rd = typed_args.Reader(pos_args, named_args, None, node.args)
 | 
| 837 | 
 | 
| 838 |             elif case(value_e.BoundFunc):
 | 
| 839 |                 func = cast(value.BoundFunc, UP_func)
 | 
| 840 | 
 | 
| 841 |                 to_call = func.func
 | 
| 842 |                 pos_args, named_args = func_proc._EvalArgList(self,
 | 
| 843 |                                                               node.args,
 | 
| 844 |                                                               me=func.me)
 | 
| 845 |                 rd = typed_args.Reader(pos_args,
 | 
| 846 |                                        named_args,
 | 
| 847 |                                        None,
 | 
| 848 |                                        node.args,
 | 
| 849 |                                        is_bound=True)
 | 
| 850 |             else:
 | 
| 851 |                 raise error.TypeErr(func, 'Expected a function or method',
 | 
| 852 |                                     node.args.left)
 | 
| 853 | 
 | 
| 854 |         return self._CallFunc(to_call, rd)
 | 
| 855 | 
 | 
| 856 |     def _EvalSubscript(self, obj, index):
 | 
| 857 |         # type: (value_t, value_t) -> value_t
 | 
| 858 | 
 | 
| 859 |         UP_obj = obj
 | 
| 860 |         UP_index = index
 | 
| 861 | 
 | 
| 862 |         with tagswitch(obj) as case:
 | 
| 863 |             if case(value_e.Str):
 | 
| 864 |                 # Note: s[i] and s[i:j] are like Go, on bytes.  We may provide
 | 
| 865 |                 # s->numBytes(), s->countRunes(), and iteration over runes.
 | 
| 866 |                 obj = cast(value.Str, UP_obj)
 | 
| 867 |                 with tagswitch(index) as case2:
 | 
| 868 |                     if case2(value_e.Slice):
 | 
| 869 |                         index = cast(value.Slice, UP_index)
 | 
| 870 | 
 | 
| 871 |                         lower = index.lower.i if index.lower else 0
 | 
| 872 |                         upper = index.upper.i if index.upper else len(obj.s)
 | 
| 873 |                         return value.Str(obj.s[lower:upper])
 | 
| 874 | 
 | 
| 875 |                     elif case2(value_e.Int):
 | 
| 876 |                         index = cast(value.Int, UP_index)
 | 
| 877 |                         i = mops.BigTruncate(index.i)
 | 
| 878 |                         try:
 | 
| 879 |                             return value.Str(obj.s[i])
 | 
| 880 |                         except IndexError:
 | 
| 881 |                             # TODO: expr.Subscript has no error location
 | 
| 882 |                             raise error.Expr('index out of range', loc.Missing)
 | 
| 883 | 
 | 
| 884 |                     else:
 | 
| 885 |                         raise error.TypeErr(index,
 | 
| 886 |                                             'Str index expected Int or Slice',
 | 
| 887 |                                             loc.Missing)
 | 
| 888 | 
 | 
| 889 |             elif case(value_e.List):
 | 
| 890 |                 obj = cast(value.List, UP_obj)
 | 
| 891 |                 with tagswitch(index) as case2:
 | 
| 892 |                     if case2(value_e.Slice):
 | 
| 893 |                         index = cast(value.Slice, UP_index)
 | 
| 894 | 
 | 
| 895 |                         lower = index.lower.i if index.lower else 0
 | 
| 896 |                         upper = index.upper.i if index.upper else len(
 | 
| 897 |                             obj.items)
 | 
| 898 |                         return value.List(obj.items[lower:upper])
 | 
| 899 | 
 | 
| 900 |                     elif case2(value_e.Int):
 | 
| 901 |                         index = cast(value.Int, UP_index)
 | 
| 902 |                         i = mops.BigTruncate(index.i)
 | 
| 903 |                         try:
 | 
| 904 |                             return obj.items[i]
 | 
| 905 |                         except IndexError:
 | 
| 906 |                             # TODO: expr.Subscript has no error location
 | 
| 907 |                             raise error.Expr('List index out of range: %d' % i,
 | 
| 908 |                                              loc.Missing)
 | 
| 909 | 
 | 
| 910 |                     else:
 | 
| 911 |                         raise error.TypeErr(
 | 
| 912 |                             index, 'List index expected Int or Slice',
 | 
| 913 |                             loc.Missing)
 | 
| 914 | 
 | 
| 915 |             elif case(value_e.Dict):
 | 
| 916 |                 obj = cast(value.Dict, UP_obj)
 | 
| 917 |                 if index.tag() != value_e.Str:
 | 
| 918 |                     raise error.TypeErr(index, 'Dict index expected Str',
 | 
| 919 |                                         loc.Missing)
 | 
| 920 | 
 | 
| 921 |                 index = cast(value.Str, UP_index)
 | 
| 922 |                 try:
 | 
| 923 |                     return obj.d[index.s]
 | 
| 924 |                 except KeyError:
 | 
| 925 |                     # TODO: expr.Subscript has no error location
 | 
| 926 |                     raise error.Expr('Dict entry not found: %r' % index.s,
 | 
| 927 |                                      loc.Missing)
 | 
| 928 | 
 | 
| 929 |         raise error.TypeErr(obj, 'Subscript expected Str, List, or Dict',
 | 
| 930 |                             loc.Missing)
 | 
| 931 | 
 | 
| 932 |     def _EvalDot(self, node, obj):
 | 
| 933 |         # type: (Attribute, value_t) -> value_t
 | 
| 934 |         """ obj.attr on RHS or LHS
 | 
| 935 | 
 | 
| 936 |         setvar x = obj.attr
 | 
| 937 |         setglobal g[obj.attr] = 42
 | 
| 938 |         """
 | 
| 939 |         UP_obj = obj
 | 
| 940 |         with tagswitch(obj) as case:
 | 
| 941 |             if case(value_e.Dict):
 | 
| 942 |                 obj = cast(value.Dict, UP_obj)
 | 
| 943 |                 attr_name = node.attr_name
 | 
| 944 |                 try:
 | 
| 945 |                     result = obj.d[attr_name]
 | 
| 946 |                 except KeyError:
 | 
| 947 |                     raise error.Expr('Dict entry %r not found' % attr_name,
 | 
| 948 |                                      node.op)
 | 
| 949 | 
 | 
| 950 |             else:
 | 
| 951 |                 raise error.TypeErr(obj, 'Dot operator expected Dict', node.op)
 | 
| 952 | 
 | 
| 953 |         return result
 | 
| 954 | 
 | 
| 955 |     def _EvalAttribute(self, node):
 | 
| 956 |         # type: (Attribute) -> value_t
 | 
| 957 | 
 | 
| 958 |         o = self._EvalExpr(node.obj)
 | 
| 959 |         UP_o = o
 | 
| 960 | 
 | 
| 961 |         with switch(node.op.id) as case:
 | 
| 962 |             # Right now => is a synonym for ->
 | 
| 963 |             # Later we may enforce that => is pure, and -> is for mutation and
 | 
| 964 |             # I/O.
 | 
| 965 |             if case(Id.Expr_RArrow, Id.Expr_RDArrow):
 | 
| 966 |                 name = node.attr_name
 | 
| 967 |                 # Look up builtin methods
 | 
| 968 |                 type_methods = self.methods.get(o.tag())
 | 
| 969 |                 vm_callable = (type_methods.get(name)
 | 
| 970 |                                if type_methods is not None else None)
 | 
| 971 |                 if vm_callable:
 | 
| 972 |                     func_val = value.BuiltinFunc(vm_callable)
 | 
| 973 |                     return value.BoundFunc(o, func_val)
 | 
| 974 | 
 | 
| 975 |                 # If the operator is ->, fail because we don't have any
 | 
| 976 |                 # user-defined methods
 | 
| 977 |                 if node.op.id == Id.Expr_RArrow:
 | 
| 978 |                     raise error.TypeErrVerbose(
 | 
| 979 |                         'Method %r does not exist on type %s' %
 | 
| 980 |                         (name, ui.ValType(o)), node.attr)
 | 
| 981 | 
 | 
| 982 |                 # Operator is =>, so try function chaining.
 | 
| 983 | 
 | 
| 984 |                 # Instead of str(f()) => upper()
 | 
| 985 |                 #         or str(f()).upper() as in Pythohn
 | 
| 986 |                 #
 | 
| 987 |                 # It's more natural to write
 | 
| 988 |                 #     f() => str() => upper()
 | 
| 989 | 
 | 
| 990 |                 # Could improve error message: may give "Undefined variable"
 | 
| 991 |                 val = self._LookupVar(name, node.attr)
 | 
| 992 | 
 | 
| 993 |                 with tagswitch(val) as case2:
 | 
| 994 |                     if case2(value_e.Func, value_e.BuiltinFunc):
 | 
| 995 |                         return value.BoundFunc(o, val)
 | 
| 996 |                     else:
 | 
| 997 |                         raise error.TypeErr(
 | 
| 998 |                             val, 'Fat arrow => expects method or function',
 | 
| 999 |                             node.attr)
 | 
| 1000 | 
 | 
| 1001 |             elif case(Id.Expr_Dot):  # d.key is like d['key']
 | 
| 1002 |                 return self._EvalDot(node, o)
 | 
| 1003 | 
 | 
| 1004 |             else:
 | 
| 1005 |                 raise AssertionError(node.op)
 | 
| 1006 | 
 | 
| 1007 |     def _EvalExpr(self, node):
 | 
| 1008 |         # type: (expr_t) -> value_t
 | 
| 1009 |         """Turn an expression into a value."""
 | 
| 1010 |         if 0:
 | 
| 1011 |             print('_EvalExpr()')
 | 
| 1012 |             node.PrettyPrint()
 | 
| 1013 |             print('')
 | 
| 1014 | 
 | 
| 1015 |         UP_node = node
 | 
| 1016 |         with tagswitch(node) as case:
 | 
| 1017 |             if case(expr_e.Const):
 | 
| 1018 |                 node = cast(expr.Const, UP_node)
 | 
| 1019 |                 return self._EvalConst(node)
 | 
| 1020 | 
 | 
| 1021 |             elif case(expr_e.Var):
 | 
| 1022 |                 node = cast(expr.Var, UP_node)
 | 
| 1023 |                 return self._LookupVar(node.name, node.left)
 | 
| 1024 | 
 | 
| 1025 |             elif case(expr_e.Place):
 | 
| 1026 |                 node = cast(expr.Place, UP_node)
 | 
| 1027 |                 frame = self.mem.TopNamespace()
 | 
| 1028 |                 return value.Place(LeftName(node.var_name, node.blame_tok),
 | 
| 1029 |                                    frame)
 | 
| 1030 | 
 | 
| 1031 |             elif case(expr_e.CommandSub):
 | 
| 1032 |                 node = cast(CommandSub, UP_node)
 | 
| 1033 | 
 | 
| 1034 |                 id_ = node.left_token.id
 | 
| 1035 |                 if id_ == Id.Left_CaretParen:  # ^(echo block literal)
 | 
| 1036 |                     # TODO: Propgate location info?
 | 
| 1037 |                     return value.Command(node.child)
 | 
| 1038 |                 else:
 | 
| 1039 |                     stdout_str = self.shell_ex.RunCommandSub(node)
 | 
| 1040 |                     if id_ == Id.Left_AtParen:  # @(seq 3)
 | 
| 1041 |                         # YSH splitting algorithm: does not depend on IFS
 | 
| 1042 |                         try:
 | 
| 1043 |                             strs = j8.SplitJ8Lines(stdout_str)
 | 
| 1044 |                         except error.Decode as e:
 | 
| 1045 |                             # status code 4 is special, for encode/decode errors.
 | 
| 1046 |                             raise error.Structured(4, e.Message(),
 | 
| 1047 |                                                    node.left_token)
 | 
| 1048 | 
 | 
| 1049 |                         #strs = self.splitter.SplitForWordEval(stdout_str)
 | 
| 1050 | 
 | 
| 1051 |                         items = [value.Str(s)
 | 
| 1052 |                                  for s in strs]  # type: List[value_t]
 | 
| 1053 |                         return value.List(items)
 | 
| 1054 |                     else:
 | 
| 1055 |                         return value.Str(stdout_str)
 | 
| 1056 | 
 | 
| 1057 |             elif case(expr_e.ShArrayLiteral):  # var x = :| foo *.py |
 | 
| 1058 |                 node = cast(ShArrayLiteral, UP_node)
 | 
| 1059 |                 words = braces.BraceExpandWords(node.words)
 | 
| 1060 |                 strs = self.word_ev.EvalWordSequence(words)
 | 
| 1061 |                 #log('ARRAY LITERAL EVALUATED TO -> %s', strs)
 | 
| 1062 |                 #return value.BashArray(strs)
 | 
| 1063 | 
 | 
| 1064 |                 # It's equivalent to ['foo', 'bar']
 | 
| 1065 |                 items = [value.Str(s) for s in strs]
 | 
| 1066 |                 return value.List(items)
 | 
| 1067 | 
 | 
| 1068 |             elif case(expr_e.DoubleQuoted):
 | 
| 1069 |                 node = cast(DoubleQuoted, UP_node)
 | 
| 1070 |                 # In an ideal world, YSH would *statically* disallow:
 | 
| 1071 |                 #
 | 
| 1072 |                 # - "$@" and "${array[@]}"
 | 
| 1073 |                 # - backticks like `echo hi`
 | 
| 1074 |                 # - $(( 1+2 )) and $[] -- although useful for refactoring
 | 
| 1075 |                 #   - not sure: ${x%%} -- could disallow this
 | 
| 1076 |                 #     - these enters the ArgDQ state: "${a:-foo bar}" ?
 | 
| 1077 |                 #
 | 
| 1078 |                 # But that would complicate the parser/evaluator.  So just rely
 | 
| 1079 |                 # on runtime strict_array to disallow the bad parts.
 | 
| 1080 |                 return value.Str(self.word_ev.EvalDoubleQuotedToString(node))
 | 
| 1081 | 
 | 
| 1082 |             elif case(expr_e.SingleQuoted):
 | 
| 1083 |                 node = cast(SingleQuoted, UP_node)
 | 
| 1084 |                 return value.Str(node.sval)
 | 
| 1085 | 
 | 
| 1086 |             elif case(expr_e.BracedVarSub):
 | 
| 1087 |                 node = cast(BracedVarSub, UP_node)
 | 
| 1088 |                 return value.Str(self.word_ev.EvalBracedVarSubToString(node))
 | 
| 1089 | 
 | 
| 1090 |             elif case(expr_e.SimpleVarSub):
 | 
| 1091 |                 node = cast(SimpleVarSub, UP_node)
 | 
| 1092 |                 return value.Str(self.word_ev.EvalSimpleVarSubToString(node))
 | 
| 1093 | 
 | 
| 1094 |             elif case(expr_e.Unary):
 | 
| 1095 |                 node = cast(expr.Unary, UP_node)
 | 
| 1096 |                 return self._EvalUnary(node)
 | 
| 1097 | 
 | 
| 1098 |             elif case(expr_e.Binary):
 | 
| 1099 |                 node = cast(expr.Binary, UP_node)
 | 
| 1100 |                 return self._EvalBinary(node)
 | 
| 1101 | 
 | 
| 1102 |             elif case(expr_e.Slice):  # a[:0]
 | 
| 1103 |                 node = cast(expr.Slice, UP_node)
 | 
| 1104 | 
 | 
| 1105 |                 lower = None  # type: Optional[IntBox]
 | 
| 1106 |                 upper = None  # type: Optional[IntBox]
 | 
| 1107 | 
 | 
| 1108 |                 if node.lower:
 | 
| 1109 |                     msg = 'Slice begin should be Int'
 | 
| 1110 |                     i = val_ops.ToInt(self._EvalExpr(node.lower), msg,
 | 
| 1111 |                                       loc.Missing)
 | 
| 1112 |                     lower = IntBox(i)
 | 
| 1113 | 
 | 
| 1114 |                 if node.upper:
 | 
| 1115 |                     msg = 'Slice end should be Int'
 | 
| 1116 |                     i = val_ops.ToInt(self._EvalExpr(node.upper), msg,
 | 
| 1117 |                                       loc.Missing)
 | 
| 1118 |                     upper = IntBox(i)
 | 
| 1119 | 
 | 
| 1120 |                 return value.Slice(lower, upper)
 | 
| 1121 | 
 | 
| 1122 |             elif case(expr_e.Range):
 | 
| 1123 |                 node = cast(expr.Range, UP_node)
 | 
| 1124 | 
 | 
| 1125 |                 assert node.lower is not None
 | 
| 1126 |                 assert node.upper is not None
 | 
| 1127 | 
 | 
| 1128 |                 msg = 'Range begin should be Int'
 | 
| 1129 |                 i = val_ops.ToInt(self._EvalExpr(node.lower), msg, loc.Missing)
 | 
| 1130 | 
 | 
| 1131 |                 msg = 'Range end should be Int'
 | 
| 1132 |                 j = val_ops.ToInt(self._EvalExpr(node.upper), msg, loc.Missing)
 | 
| 1133 | 
 | 
| 1134 |                 return value.Range(i, j)
 | 
| 1135 | 
 | 
| 1136 |             elif case(expr_e.Compare):
 | 
| 1137 |                 node = cast(expr.Compare, UP_node)
 | 
| 1138 |                 return self._EvalCompare(node)
 | 
| 1139 | 
 | 
| 1140 |             elif case(expr_e.IfExp):
 | 
| 1141 |                 node = cast(expr.IfExp, UP_node)
 | 
| 1142 |                 b = val_ops.ToBool(self._EvalExpr(node.test))
 | 
| 1143 |                 if b:
 | 
| 1144 |                     return self._EvalExpr(node.body)
 | 
| 1145 |                 else:
 | 
| 1146 |                     return self._EvalExpr(node.orelse)
 | 
| 1147 | 
 | 
| 1148 |             elif case(expr_e.List):
 | 
| 1149 |                 node = cast(expr.List, UP_node)
 | 
| 1150 |                 items = [self._EvalExpr(e) for e in node.elts]
 | 
| 1151 |                 return value.List(items)
 | 
| 1152 | 
 | 
| 1153 |             elif case(expr_e.Tuple):
 | 
| 1154 |                 node = cast(expr.Tuple, UP_node)
 | 
| 1155 |                 # YSH language: Tuple syntax evaluates to LIST !
 | 
| 1156 |                 items = [self._EvalExpr(e) for e in node.elts]
 | 
| 1157 |                 return value.List(items)
 | 
| 1158 | 
 | 
| 1159 |             elif case(expr_e.Dict):
 | 
| 1160 |                 node = cast(expr.Dict, UP_node)
 | 
| 1161 | 
 | 
| 1162 |                 kvals = [self._EvalExpr(e) for e in node.keys]
 | 
| 1163 |                 values = []  # type: List[value_t]
 | 
| 1164 | 
 | 
| 1165 |                 for i, value_expr in enumerate(node.values):
 | 
| 1166 |                     if value_expr.tag() == expr_e.Implicit:  # {key}
 | 
| 1167 |                         # Enforced by parser.  Key is expr.Const
 | 
| 1168 |                         assert kvals[i].tag() == value_e.Str, kvals[i]
 | 
| 1169 |                         key = cast(value.Str, kvals[i])
 | 
| 1170 |                         v = self._LookupVar(key.s, loc.Missing)
 | 
| 1171 |                     else:
 | 
| 1172 |                         v = self._EvalExpr(value_expr)
 | 
| 1173 | 
 | 
| 1174 |                     values.append(v)
 | 
| 1175 | 
 | 
| 1176 |                 d = NewDict()  # type: Dict[str, value_t]
 | 
| 1177 |                 for i, kval in enumerate(kvals):
 | 
| 1178 |                     k = val_ops.ToStr(kval, 'Dict keys must be strings',
 | 
| 1179 |                                       loc.Missing)
 | 
| 1180 |                     d[k] = values[i]
 | 
| 1181 | 
 | 
| 1182 |                 return value.Dict(d)
 | 
| 1183 | 
 | 
| 1184 |             elif case(expr_e.ListComp):
 | 
| 1185 |                 e_die_status(
 | 
| 1186 |                     2, 'List comprehension reserved but not implemented')
 | 
| 1187 | 
 | 
| 1188 |             elif case(expr_e.GeneratorExp):
 | 
| 1189 |                 e_die_status(
 | 
| 1190 |                     2, 'Generator expression reserved but not implemented')
 | 
| 1191 | 
 | 
| 1192 |             elif case(expr_e.Literal):  # ^[1 + 2]
 | 
| 1193 |                 node = cast(expr.Literal, UP_node)
 | 
| 1194 |                 return value.Expr(node.inner)
 | 
| 1195 | 
 | 
| 1196 |             elif case(expr_e.Lambda):  # |x| x+1 syntax is reserved
 | 
| 1197 |                 # TODO: Location information for |, or func
 | 
| 1198 |                 # Note: anonymous functions also evaluate to a Lambda, but they shouldn't
 | 
| 1199 |                 e_die_status(2, 'Lambda reserved but not implemented')
 | 
| 1200 | 
 | 
| 1201 |             elif case(expr_e.FuncCall):
 | 
| 1202 |                 node = cast(expr.FuncCall, UP_node)
 | 
| 1203 |                 return self._EvalFuncCall(node)
 | 
| 1204 | 
 | 
| 1205 |             elif case(expr_e.Subscript):
 | 
| 1206 |                 node = cast(Subscript, UP_node)
 | 
| 1207 |                 obj = self._EvalExpr(node.obj)
 | 
| 1208 |                 index = self._EvalExpr(node.index)
 | 
| 1209 |                 return self._EvalSubscript(obj, index)
 | 
| 1210 | 
 | 
| 1211 |             elif case(expr_e.Attribute):  # obj->method or mydict.key
 | 
| 1212 |                 node = cast(Attribute, UP_node)
 | 
| 1213 |                 return self._EvalAttribute(node)
 | 
| 1214 | 
 | 
| 1215 |             elif case(expr_e.Eggex):
 | 
| 1216 |                 node = cast(Eggex, UP_node)
 | 
| 1217 |                 return self.EvalEggex(node)
 | 
| 1218 | 
 | 
| 1219 |             else:
 | 
| 1220 |                 raise NotImplementedError(node.__class__.__name__)
 | 
| 1221 | 
 | 
| 1222 |     def EvalEggex(self, node):
 | 
| 1223 |         # type: (Eggex) -> value.Eggex
 | 
| 1224 | 
 | 
| 1225 |         # Splice, check flags consistency, and accumulate convert_funcs indexed
 | 
| 1226 |         # by capture group
 | 
| 1227 |         ev = EggexEvaluator(self.mem, node.canonical_flags)
 | 
| 1228 |         spliced = ev.EvalE(node.regex)
 | 
| 1229 | 
 | 
| 1230 |         # as_ere and capture_names filled by ~ operator or Str method
 | 
| 1231 |         return value.Eggex(spliced, node.canonical_flags, ev.convert_funcs,
 | 
| 1232 |                            ev.convert_toks, None, [])
 | 
| 1233 | 
 | 
| 1234 | 
 | 
| 1235 | class EggexEvaluator(object):
 | 
| 1236 | 
 | 
| 1237 |     def __init__(self, mem, canonical_flags):
 | 
| 1238 |         # type: (state.Mem, str) -> None
 | 
| 1239 |         self.mem = mem
 | 
| 1240 |         self.canonical_flags = canonical_flags
 | 
| 1241 |         self.convert_funcs = []  # type: List[Optional[value_t]]
 | 
| 1242 |         self.convert_toks = []  # type: List[Optional[Token]]
 | 
| 1243 | 
 | 
| 1244 |     def _LookupVar(self, name, var_loc):
 | 
| 1245 |         # type: (str, loc_t) -> value_t
 | 
| 1246 |         """
 | 
| 1247 |         Duplicated from ExprEvaluator
 | 
| 1248 |         """
 | 
| 1249 |         return LookupVar(self.mem, name, scope_e.LocalOrGlobal, var_loc)
 | 
| 1250 | 
 | 
| 1251 |     def _EvalClassLiteralTerm(self, term, out):
 | 
| 1252 |         # type: (class_literal_term_t, List[char_class_term_t]) -> None
 | 
| 1253 |         UP_term = term
 | 
| 1254 | 
 | 
| 1255 |         # These 2 vars will be initialized if we don't return early
 | 
| 1256 |         s = None  # type: str
 | 
| 1257 |         char_code_tok = None  # type: Token
 | 
| 1258 | 
 | 
| 1259 |         with tagswitch(term) as case:
 | 
| 1260 | 
 | 
| 1261 |             if case(class_literal_term_e.CharCode):
 | 
| 1262 |                 term = cast(CharCode, UP_term)
 | 
| 1263 | 
 | 
| 1264 |                 # What about \0?  At runtime, ERE should disallow it.  But we
 | 
| 1265 |                 # can also disallow it here.
 | 
| 1266 |                 out.append(term)
 | 
| 1267 |                 return
 | 
| 1268 | 
 | 
| 1269 |             elif case(class_literal_term_e.CharRange):
 | 
| 1270 |                 term = cast(CharRange, UP_term)
 | 
| 1271 |                 out.append(term)
 | 
| 1272 |                 return
 | 
| 1273 | 
 | 
| 1274 |             elif case(class_literal_term_e.PosixClass):
 | 
| 1275 |                 term = cast(PosixClass, UP_term)
 | 
| 1276 |                 out.append(term)
 | 
| 1277 |                 return
 | 
| 1278 | 
 | 
| 1279 |             elif case(class_literal_term_e.PerlClass):
 | 
| 1280 |                 term = cast(PerlClass, UP_term)
 | 
| 1281 |                 out.append(term)
 | 
| 1282 |                 return
 | 
| 1283 | 
 | 
| 1284 |             elif case(class_literal_term_e.SingleQuoted):
 | 
| 1285 |                 term = cast(SingleQuoted, UP_term)
 | 
| 1286 | 
 | 
| 1287 |                 s = term.sval
 | 
| 1288 |                 char_code_tok = term.left
 | 
| 1289 | 
 | 
| 1290 |             elif case(class_literal_term_e.Splice):
 | 
| 1291 |                 term = cast(class_literal_term.Splice, UP_term)
 | 
| 1292 | 
 | 
| 1293 |                 val = self._LookupVar(term.var_name, term.name)
 | 
| 1294 |                 s = val_ops.ToStr(val, 'Eggex char class splice expected Str',
 | 
| 1295 |                                   term.name)
 | 
| 1296 |                 char_code_tok = term.name
 | 
| 1297 | 
 | 
| 1298 |         assert s is not None, term
 | 
| 1299 |         for ch in s:
 | 
| 1300 |             char_int = ord(ch)
 | 
| 1301 |             if char_int >= 128:
 | 
| 1302 |                 # / [ '\x7f\xff' ] / is better written as / [ \x7f \xff ] /
 | 
| 1303 |                 e_die(
 | 
| 1304 |                     "Use unquoted char literal for byte %d, which is >= 128"
 | 
| 1305 |                     " (avoid confusing a set of bytes with a sequence)" %
 | 
| 1306 |                     char_int, char_code_tok)
 | 
| 1307 |             out.append(CharCode(char_code_tok, char_int, False))
 | 
| 1308 | 
 | 
| 1309 |     def EvalE(self, node):
 | 
| 1310 |         # type: (re_t) -> re_t
 | 
| 1311 |         """Resolve references and eval constants in an Eggex
 | 
| 1312 | 
 | 
| 1313 |         Rules:
 | 
| 1314 |           Splice => re_t   # like Hex and @const in  / Hex '.' @const /
 | 
| 1315 |           Speck/Token (syntax) => Primitive (logical)
 | 
| 1316 |           Chars and Strings => LiteralChars
 | 
| 1317 |         """
 | 
| 1318 |         UP_node = node
 | 
| 1319 | 
 | 
| 1320 |         with tagswitch(node) as case:
 | 
| 1321 |             if case(re_e.Seq):
 | 
| 1322 |                 node = cast(re.Seq, UP_node)
 | 
| 1323 |                 new_children = [self.EvalE(child) for child in node.children]
 | 
| 1324 |                 return re.Seq(new_children)
 | 
| 1325 | 
 | 
| 1326 |             elif case(re_e.Alt):
 | 
| 1327 |                 node = cast(re.Alt, UP_node)
 | 
| 1328 |                 new_children = [self.EvalE(child) for child in node.children]
 | 
| 1329 |                 return re.Alt(new_children)
 | 
| 1330 | 
 | 
| 1331 |             elif case(re_e.Repeat):
 | 
| 1332 |                 node = cast(re.Repeat, UP_node)
 | 
| 1333 |                 return re.Repeat(self.EvalE(node.child), node.op)
 | 
| 1334 | 
 | 
| 1335 |             elif case(re_e.Group):
 | 
| 1336 |                 node = cast(re.Group, UP_node)
 | 
| 1337 | 
 | 
| 1338 |                 # placeholder for non-capturing group
 | 
| 1339 |                 self.convert_funcs.append(None)
 | 
| 1340 |                 self.convert_toks.append(None)
 | 
| 1341 |                 return re.Group(self.EvalE(node.child))
 | 
| 1342 | 
 | 
| 1343 |             elif case(re_e.Capture):  # Identical to Group
 | 
| 1344 |                 node = cast(re.Capture, UP_node)
 | 
| 1345 |                 convert_func = None  # type: Optional[value_t]
 | 
| 1346 |                 convert_tok = None  # type: Optional[Token]
 | 
| 1347 |                 if node.func_name:
 | 
| 1348 |                     func_name = lexer.LazyStr(node.func_name)
 | 
| 1349 |                     func_val = self.mem.GetValue(func_name)
 | 
| 1350 |                     with tagswitch(func_val) as case:
 | 
| 1351 |                         if case(value_e.Func, value_e.BuiltinFunc):
 | 
| 1352 |                             convert_func = func_val
 | 
| 1353 |                             convert_tok = node.func_name
 | 
| 1354 |                         else:
 | 
| 1355 |                             raise error.TypeErr(
 | 
| 1356 |                                 func_val,
 | 
| 1357 |                                 "Expected %r to be a func" % func_name,
 | 
| 1358 |                                 node.func_name)
 | 
| 1359 | 
 | 
| 1360 |                 self.convert_funcs.append(convert_func)
 | 
| 1361 |                 self.convert_toks.append(convert_tok)
 | 
| 1362 |                 return re.Capture(self.EvalE(node.child), node.name,
 | 
| 1363 |                                   node.func_name)
 | 
| 1364 | 
 | 
| 1365 |             elif case(re_e.CharClassLiteral):
 | 
| 1366 |                 node = cast(re.CharClassLiteral, UP_node)
 | 
| 1367 | 
 | 
| 1368 |                 new_terms = []  # type: List[char_class_term_t]
 | 
| 1369 |                 for t in node.terms:
 | 
| 1370 |                     # can get multiple char_class_term.CharCode for a
 | 
| 1371 |                     # class_literal_term_t
 | 
| 1372 |                     self._EvalClassLiteralTerm(t, new_terms)
 | 
| 1373 |                 return re.CharClass(node.negated, new_terms)
 | 
| 1374 | 
 | 
| 1375 |             elif case(re_e.SingleQuoted):
 | 
| 1376 |                 node = cast(SingleQuoted, UP_node)
 | 
| 1377 | 
 | 
| 1378 |                 s = node.sval
 | 
| 1379 |                 return re.LiteralChars(node.left, s)
 | 
| 1380 | 
 | 
| 1381 |             elif case(re_e.Splice):
 | 
| 1382 |                 node = cast(re.Splice, UP_node)
 | 
| 1383 | 
 | 
| 1384 |                 val = self._LookupVar(node.var_name, node.name)
 | 
| 1385 |                 UP_val = val
 | 
| 1386 |                 with tagswitch(val) as case:
 | 
| 1387 |                     if case(value_e.Str):
 | 
| 1388 |                         val = cast(value.Str, UP_val)
 | 
| 1389 |                         to_splice = re.LiteralChars(node.name,
 | 
| 1390 |                                                     val.s)  # type: re_t
 | 
| 1391 | 
 | 
| 1392 |                     elif case(value_e.Eggex):
 | 
| 1393 |                         val = cast(value.Eggex, UP_val)
 | 
| 1394 | 
 | 
| 1395 |                         # Splicing means we get the conversion funcs too.
 | 
| 1396 |                         self.convert_funcs.extend(val.convert_funcs)
 | 
| 1397 |                         self.convert_toks.extend(val.convert_toks)
 | 
| 1398 | 
 | 
| 1399 |                         # Splicing requires flags to match.  This check is
 | 
| 1400 |                         # transitive.
 | 
| 1401 |                         to_splice = val.spliced
 | 
| 1402 | 
 | 
| 1403 |                         if val.canonical_flags != self.canonical_flags:
 | 
| 1404 |                             e_die(
 | 
| 1405 |                                 "Expected eggex flags %r, but got %r" %
 | 
| 1406 |                                 (self.canonical_flags, val.canonical_flags),
 | 
| 1407 |                                 node.name)
 | 
| 1408 | 
 | 
| 1409 |                     else:
 | 
| 1410 |                         raise error.TypeErr(
 | 
| 1411 |                             val, 'Eggex splice expected Str or Eggex',
 | 
| 1412 |                             node.name)
 | 
| 1413 |                 return to_splice
 | 
| 1414 | 
 | 
| 1415 |             else:
 | 
| 1416 |                 # These are evaluated at translation time
 | 
| 1417 | 
 | 
| 1418 |                 # case(re_e.Primitive)
 | 
| 1419 |                 # case(re_e.PosixClass)
 | 
| 1420 |                 # case(re_e.PerlClass)
 | 
| 1421 |                 return node
 | 
| 1422 | 
 | 
| 1423 | 
 | 
| 1424 | # vim: sw=4
 |