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, Kind
6 from _devbuild.gen.syntax_asdl import (
7 loc,
8 loc_t,
9 re,
10 re_e,
11 re_t,
12 Token,
13 word_part,
14 SingleQuoted,
15 DoubleQuoted,
16 BracedVarSub,
17 SimpleVarSub,
18 ShArrayLiteral,
19 CommandSub,
20 expr,
21 expr_e,
22 expr_t,
23 place_expr,
24 place_expr_e,
25 place_expr_t,
26 Attribute,
27 Subscript,
28 class_literal_term,
29 class_literal_term_e,
30 class_literal_term_t,
31 char_class_term,
32 char_class_term_t,
33 PosixClass,
34 PerlClass,
35 CharCode,
36 )
37 from _devbuild.gen.runtime_asdl import (
38 coerced_e,
39 coerced_t,
40 scope_e,
41 scope_t,
42 part_value,
43 part_value_t,
44 lvalue,
45 lvalue_e,
46 lvalue_t,
47 value,
48 value_e,
49 value_t,
50 IntBox,
51 )
52 from core import error
53 from core.error import e_die, e_die_status
54 from core import state
55 from core import ui
56 from core import vm
57 from frontend import consts
58 from frontend import match
59 from frontend import location
60 from frontend import typed_args
61 from osh import braces
62 from osh import word_compile
63 from mycpp.mylib import log, NewDict, switch, tagswitch
64 from ysh import val_ops
65
66 import libc
67
68 from typing import cast, Any, Optional, Dict, List, Tuple, TYPE_CHECKING
69
70 if TYPE_CHECKING:
71 from _devbuild.gen.syntax_asdl import ArgList
72 from core.state import Mem
73 from osh.word_eval import AbstractWordEvaluator
74 from osh import split
75
76 _ = log
77
78
79 def LookupVar(mem, var_name, which_scopes, var_loc):
80 # type: (Mem, str, scope_t, loc_t) -> value_t
81
82 # Lookup WITHOUT dynamic scope.
83 val = mem.GetValue(var_name, which_scopes=which_scopes)
84 if val.tag() == value_e.Undef:
85 e_die('Undefined variable %r' % var_name, var_loc)
86
87 return val
88
89
90 def _ConvertToInt(val, msg, blame_loc):
91 # type: (value_t, str, loc_t) -> int
92 UP_val = val
93 with tagswitch(val) as case:
94 if case(value_e.Int):
95 val = cast(value.Int, UP_val)
96 return val.i
97
98 elif case(value_e.Str):
99 val = cast(value.Str, UP_val)
100 if match.LooksLikeInteger(val.s):
101 return int(val.s)
102
103 raise error.TypeErr(val, msg, blame_loc)
104
105
106 def _ConvertToNumber(val):
107 # type: (value_t) -> Tuple[coerced_t, int, 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, -1, val.f
117
118 elif case(value_e.Str):
119 val = cast(value.Str, UP_val)
120 if match.LooksLikeInteger(val.s):
121 return coerced_e.Int, int(val.s), -1.0
122
123 if match.LooksLikeFloat(val.s):
124 return coerced_e.Float, -1, float(val.s)
125
126 return coerced_e.Neither, -1, -1.0
127
128
129 def _ConvertForBinaryOp(left, right):
130 # type: (value_t, value_t) -> Tuple[coerced_t, int, int, float, float]
131 """
132 Returns one of
133 value_e.Int or value_e.Float
134 2 ints or 2 floats
135
136 To indicate which values the operation should be done on
137 """
138 c1, i1, f1 = _ConvertToNumber(left)
139 c2, i2, f2 = _ConvertToNumber(right)
140
141 if c1 == coerced_e.Int and c2 == coerced_e.Int:
142 return coerced_e.Int, i1, i2, -1.0, -1.0
143
144 elif c1 == coerced_e.Int and c2 == coerced_e.Float:
145 return coerced_e.Float, -1, -1, float(i1), f2
146
147 elif c1 == coerced_e.Float and c2 == coerced_e.Int:
148 return coerced_e.Float, -1, -1, f1, float(i2)
149
150 elif c1 == coerced_e.Float and c2 == coerced_e.Float:
151 return coerced_e.Float, -1, -1, f1, f2
152
153 else:
154 # No operation is valid
155 return coerced_e.Neither, -1, -1, -1.0, -1.0
156
157
158 class ExprEvaluator(object):
159 """Shared between arith and bool evaluators.
160
161 They both:
162
163 1. Convert strings to integers, respecting shopt -s strict_arith.
164 2. Look up variables and evaluate words.
165 """
166
167 def __init__(
168 self,
169 mem, # type: Mem
170 mutable_opts, # type: state.MutableOpts
171 methods, # type: Dict[int, Dict[str, vm._Callable]]
172 splitter, # type: split.SplitContext
173 errfmt, # type: ui.ErrorFormatter
174 ):
175 # type: (...) -> None
176 self.shell_ex = None # type: vm._Executor
177 self.word_ev = None # type: AbstractWordEvaluator
178
179 self.mem = mem
180 self.mutable_opts = mutable_opts
181 self.methods = methods
182 self.splitter = splitter
183 self.errfmt = errfmt
184
185 def CheckCircularDeps(self):
186 # type: () -> None
187 assert self.shell_ex is not None
188 assert self.word_ev is not None
189
190 def _LookupVar(self, name, var_loc):
191 # type: (str, loc_t) -> value_t
192 return LookupVar(self.mem, name, scope_e.LocalOrGlobal, var_loc)
193
194 def EvalPlusEquals(self, lval, rhs_val, op):
195 # type: (lvalue_t, value_t, Token) -> value_t
196 """Called by CommandEvaluator."""
197
198 # TODO: Handle other augmented assignment
199 #
200 # It might be nice to do auto d[x] += 1 too
201
202 UP_lval = lval
203 with tagswitch(lval) as case:
204 if case(lvalue_e.Named):
205 lval = cast(lvalue.Named, UP_lval)
206 lhs_val = self._LookupVar(lval.name, lval.blame_loc)
207 return self._ArithNumeric(lhs_val, rhs_val, op)
208 else:
209 # TODO: Handle other lvalue, like sh_expr_eval.OldValue() But
210 # this is for YSH values, not value.BashArray etc.
211 raise AssertionError()
212
213 def EvalLHS(self, node):
214 # type: (expr_t) -> lvalue_t
215 if 0:
216 print('EvalLHS()')
217 node.PrettyPrint()
218 print('')
219
220 UP_node = node
221 with tagswitch(node) as case:
222 if case(expr_e.Var):
223 node = cast(expr.Var, UP_node)
224 return location.LName(node.name.tval)
225 else:
226 # TODO:
227 # subscripts, tuple unpacking, starred expressions, etc.
228 raise NotImplementedError(node.__class__.__name__)
229
230 def _EvalPlaceExpr(self, place):
231 # type: (place_expr_t) -> lvalue_t
232
233 UP_place = place
234 with tagswitch(place) as case:
235 if case(place_expr_e.Var):
236 place = cast(place_expr.Var, UP_place)
237
238 return location.LName(place.name.tval)
239
240 elif case(place_expr_e.Subscript):
241 place = cast(Subscript, UP_place)
242 # setvar mylist[0] = 42
243 # setvar mydict['key'] = 42
244
245 lval = self._EvalExpr(place.obj)
246 index = self._EvalExpr(place.index)
247 #log('index %s', index)
248 return lvalue.ObjIndex(lval, index)
249
250 elif case(place_expr_e.Attribute):
251 place = cast(Attribute, UP_place)
252 # setvar mydict.key = 42
253
254 lval = self._EvalExpr(place.obj)
255 if place.op.id == Id.Expr_Dot:
256 attr = value.Str(place.attr.tval)
257 return lvalue.ObjIndex(lval, attr)
258 else:
259 raise AssertionError()
260
261 else:
262 raise NotImplementedError(place)
263
264 raise AssertionError() # silence C++ compiler
265
266 def EvalPlaceExpr(self, place):
267 # type: (place_expr_t) -> lvalue_t
268 """Public API for _EvalPlaceExpr to ensure command_sub_errexit"""
269 # Pure C++ won't need to catch exceptions
270 with state.ctx_OilExpr(self.mutable_opts):
271 lval = self._EvalPlaceExpr(place)
272 return lval
273
274 def EvalExprSub(self, part):
275 # type: (word_part.ExprSub) -> part_value_t
276
277 val = self.EvalExpr(part.child, part.left)
278
279 if part.left.id == Id.Left_DollarBracket: # $[join(x)]
280 s = val_ops.Stringify(val, loc.WordPart(part))
281 return part_value.String(s, False, False)
282
283 elif part.left.id == Id.Lit_AtLBracket: # @[split(x)]
284 strs = val_ops.ToShellArray(val,
285 loc.WordPart(part),
286 prefix='Expr splice ')
287 return part_value.Array(strs)
288
289 else:
290 raise AssertionError(part.left)
291
292 def SpliceValue(self, val, part):
293 # type: (value_t, word_part.Splice) -> List[str]
294 """ write -- @myvar """
295 return val_ops.ToShellArray(val, loc.WordPart(part), prefix='Splice ')
296
297 def EvalExpr(self, node, blame_loc):
298 # type: (expr_t, loc_t) -> value_t
299 """Public API for _EvalExpr to ensure command_sub_errexit is on."""
300 self.mem.SetLocationForExpr(blame_loc)
301 # Pure C++ won't need to catch exceptions
302 with state.ctx_OilExpr(self.mutable_opts):
303 val = self._EvalExpr(node)
304 return val
305
306 # Note: IndexError and KeyError are handled in more specific places
307
308 def _EvalConst(self, node):
309 # type: (expr.Const) -> value_t
310
311 # Remove underscores from 1_000_000. The lexer is responsible for
312 # validation. TODO: Do this at PARSE TIME / COMPILE TIME.
313
314 c = node.c.tval.replace('_', '')
315
316 id_ = node.c.id
317 if id_ == Id.Expr_DecInt:
318 return value.Int(int(c))
319 if id_ == Id.Expr_BinInt:
320 return value.Int(int(c, 2))
321 if id_ == Id.Expr_OctInt:
322 return value.Int(int(c, 8))
323 if id_ == Id.Expr_HexInt:
324 return value.Int(int(c, 16))
325
326 if id_ == Id.Expr_Float:
327 return value.Float(float(c))
328
329 if id_ == Id.Expr_Null:
330 return value.Null
331 if id_ == Id.Expr_True:
332 return value.Bool(True)
333 if id_ == Id.Expr_False:
334 return value.Bool(False)
335
336 if id_ == Id.Expr_Name:
337 # for {name: 'bob'}
338 # Maybe also :Symbol?
339 return value.Str(node.c.tval)
340
341 # These calculations could also be done at COMPILE TIME
342 if id_ == Id.Char_OneChar:
343 # TODO: look up integer directly?
344 return value.Int(ord(consts.LookupCharC(node.c.tval[1])))
345 if id_ == Id.Char_UBraced:
346 s = node.c.tval[3:-1] # \u{123}
347 return value.Int(int(s, 16))
348 if id_ == Id.Char_Pound:
349 # TODO: accept UTF-8 code point instead of single byte
350 byte = node.c.tval[2] # the a in #'a'
351 return value.Int(ord(byte)) # It's an integer
352
353 # NOTE: We could allow Ellipsis for a[:, ...] here, but we're not using it
354 # yet.
355 raise AssertionError(id_)
356
357 def _EvalUnary(self, node):
358 # type: (expr.Unary) -> value_t
359 val = self._EvalExpr(node.child)
360 if node.op.id == Id.Arith_Minus:
361 c1, i1, f1 = _ConvertToNumber(val)
362 if c1 == coerced_e.Int:
363 return value.Int(-i1)
364 if c1 == coerced_e.Float:
365 return value.Float(-f1)
366 raise error.TypeErr(val, 'Negation expected Int or Float', node.op)
367
368 if node.op.id == Id.Arith_Tilde:
369 i = _ConvertToInt(val, '~ expected Int', node.op)
370 return value.Int(~i)
371
372 if node.op.id == Id.Expr_Not:
373 b = val_ops.ToBool(val)
374 return value.Bool(False if b else True)
375
376 raise NotImplementedError(node.op.id)
377
378 def _ArithNumeric(self, left, right, op):
379 # type: (value_t, value_t, Token) -> value_t
380 """
381 Note: may be replaced with arithmetic on tagged integers, e.g. 60 bit
382 with overflow detection
383 """
384 c, i1, i2, f1, f2 = _ConvertForBinaryOp(left, right)
385
386 op_id = op.id
387
388 if c == coerced_e.Int:
389 with switch(op_id) as case:
390 if case(Id.Arith_Plus, Id.Arith_PlusEqual):
391 return value.Int(i1 + i2)
392 elif case(Id.Arith_Minus, Id.Arith_MinusEqual):
393 return value.Int(i1 - i2)
394 elif case(Id.Arith_Star, Id.Arith_StarEqual):
395 return value.Int(i1 * i2)
396 elif case(Id.Arith_Slash, Id.Arith_SlashEqual):
397 if i2 == 0:
398 raise error.Expr('Divide by zero', op)
399 return value.Float(float(i1) / float(i2))
400 else:
401 raise AssertionError()
402
403 elif c == coerced_e.Float:
404 with switch(op_id) as case:
405 if case(Id.Arith_Plus, Id.Arith_PlusEqual):
406 return value.Float(f1 + f2)
407 elif case(Id.Arith_Minus, Id.Arith_MinusEqual):
408 return value.Float(f1 - f2)
409 elif case(Id.Arith_Star, Id.Arith_StarEqual):
410 return value.Float(f1 * f2)
411 elif case(Id.Arith_Slash, Id.Arith_SlashEqual):
412 if f2 == 0.0:
413 raise error.Expr('Divide by zero', op)
414 return value.Float(f1 / f2)
415 else:
416 raise AssertionError()
417
418 else:
419 raise error.TypeErrVerbose(
420 'Binary operator expected numbers, got %s and %s' %
421 (ui.ValType(left), ui.ValType(right)), op)
422
423 def _Concat(self, left, right, op):
424 # type: (value_t, value_t, Token) -> value_t
425 UP_left = left
426 UP_right = right
427
428 if left.tag() == value_e.Str and right.tag() == value_e.Str:
429 left = cast(value.Str, UP_left)
430 right = cast(value.Str, UP_right)
431
432 return value.Str(left.s + right.s)
433
434 elif left.tag() == value_e.List and right.tag() == value_e.List:
435 left = cast(value.List, UP_left)
436 right = cast(value.List, UP_right)
437
438 c = list(left.items) # mycpp rewrite of L1 + L2
439 c.extend(right.items)
440 return value.List(c)
441
442 else:
443 raise error.TypeErrVerbose(
444 'Expected Str ++ Str or List ++ List, got %s ++ %s' %
445 (ui.ValType(left), ui.ValType(right)), op)
446
447 def _EvalBinary(self, node):
448 # type: (expr.Binary) -> value_t
449
450 left = self._EvalExpr(node.left)
451 right = self._EvalExpr(node.right)
452
453 op_id = node.op.id
454
455 # Logical
456 if op_id == Id.Expr_And:
457 if val_ops.ToBool(left): # no errors
458 return right
459 else:
460 return left
461
462 if op_id == Id.Expr_Or:
463 if val_ops.ToBool(left):
464 return left
465 else:
466 return right
467
468 # Str or List
469 if op_id == Id.Arith_DPlus: # a ++ b to concat
470 return self._Concat(left, right, node.op)
471
472 # Int or Float
473 if op_id in (Id.Arith_Plus, Id.Arith_Minus, Id.Arith_Star,
474 Id.Arith_Slash):
475 return self._ArithNumeric(left, right, node.op)
476
477 # Everything below has 2 integer operands
478 i1 = _ConvertToInt(left, 'Left operand should be Int', node.op)
479 i2 = _ConvertToInt(right, 'Right operand should be Int', node.op)
480
481 if op_id == Id.Expr_DSlash: # a // b
482 if i2 == 0:
483 raise error.Expr('Divide by zero', node.op)
484 return value.Int(i1 // i2)
485
486 if op_id == Id.Arith_Percent: # a % b
487 if i2 == 0:
488 raise error.Expr('Divide by zero', node.op)
489 return value.Int(i1 % i2)
490
491 if op_id == Id.Arith_DStar: # a ** b
492 # Same as sh_expr_eval.py
493 if i2 < 0:
494 raise error.Expr("Exponent can't be a negative number",
495 node.op)
496 ret = 1
497 for i in xrange(i2):
498 ret *= i1
499 return value.Int(ret)
500
501 # Bitwise
502 if op_id == Id.Arith_Amp:
503 return value.Int(i1 & i2)
504
505 if op_id == Id.Arith_Pipe:
506 return value.Int(i1 | i2)
507
508 if op_id == Id.Arith_Caret:
509 return value.Int(i1 ^ i2)
510
511 if op_id == Id.Arith_DGreat:
512 return value.Int(i1 >> i2)
513
514 if op_id == Id.Arith_DLess:
515 return value.Int(i1 << i2)
516
517 raise NotImplementedError(op_id)
518
519 def _CompareNumeric(self, left, right, op):
520 # type: (value_t, value_t, Token) -> bool
521 c, i1, i2, f1, f2 = _ConvertForBinaryOp(left, right)
522
523 op_id = op.id
524 if c == coerced_e.Int:
525 with switch(op_id) as case:
526 if case(Id.Arith_Less):
527 return i1 < i2
528 elif case(Id.Arith_Great):
529 return i1 > i2
530 elif case(Id.Arith_LessEqual):
531 return i1 <= i2
532 elif case(Id.Arith_GreatEqual):
533 return i1 >= i2
534 else:
535 raise AssertionError()
536
537 elif c == coerced_e.Float:
538 with switch(op_id) as case:
539 if case(Id.Arith_Less):
540 return f1 < f2
541 elif case(Id.Arith_Great):
542 return f1 > f2
543 elif case(Id.Arith_LessEqual):
544 return f1 <= f2
545 elif case(Id.Arith_GreatEqual):
546 return f1 >= f2
547 else:
548 raise AssertionError()
549
550 else:
551 raise error.TypeErrVerbose(
552 'Comparison operator expected numbers, got %s and %s' %
553 (ui.ValType(left), ui.ValType(right)), op)
554
555 def _EvalCompare(self, node):
556 # type: (expr.Compare) -> value_t
557
558 left = self._EvalExpr(node.left)
559 result = True # Implicit and
560 for i, op in enumerate(node.ops):
561 right_expr = node.comparators[i]
562
563 right = self._EvalExpr(right_expr)
564
565 if op.id in (Id.Arith_Less, Id.Arith_Great, Id.Arith_LessEqual,
566 Id.Arith_GreatEqual):
567 result = self._CompareNumeric(left, right, op)
568
569 elif op.id == Id.Expr_TEqual:
570 if left.tag() != right.tag():
571 result = False
572 else:
573 result = val_ops.ExactlyEqual(left, right)
574 elif op.id == Id.Expr_NotDEqual:
575 if left.tag() != right.tag():
576 result = True
577 else:
578 result = not val_ops.ExactlyEqual(left, right)
579
580 elif op.id == Id.Expr_In:
581 result = val_ops.Contains(left, right)
582 elif op.id == Id.Node_NotIn:
583 result = not val_ops.Contains(left, right)
584
585 elif op.id == Id.Expr_Is:
586 if left.tag() != right.tag():
587 raise error.TypeErrVerbose('Mismatched types', op)
588 result = left is right
589
590 elif op.id == Id.Node_IsNot:
591 if left.tag() != right.tag():
592 raise error.TypeErrVerbose('Mismatched types', op)
593 result = left is not right
594
595 elif op.id == Id.Expr_DTilde:
596 # no extglob in Oil language; use eggex
597 if left.tag() != value_e.Str:
598 raise error.TypeErrVerbose('LHS must be Str', op)
599
600 if right.tag() != value_e.Str:
601 raise error.TypeErrVerbose('RHS must be Str', op)
602
603 UP_left = left
604 UP_right = right
605 left = cast(value.Str, UP_left)
606 right = cast(value.Str, UP_right)
607 return value.Bool(libc.fnmatch(right.s, left.s))
608
609 elif op.id == Id.Expr_NotDTilde:
610 if left.tag() != value_e.Str:
611 raise error.TypeErrVerbose('LHS must be Str', op)
612
613 if right.tag() != value_e.Str:
614 raise error.TypeErrVerbose('RHS must be Str', op)
615
616 UP_left = left
617 UP_right = right
618 left = cast(value.Str, UP_left)
619 right = cast(value.Str, UP_right)
620 return value.Bool(not libc.fnmatch(right.s, left.s))
621
622 elif op.id == Id.Expr_TildeDEqual:
623 # Approximate equality
624 UP_left = left
625 if left.tag() != value_e.Str:
626 e_die('~== expects a string on the left', op)
627
628 left = cast(value.Str, UP_left)
629 left2 = left.s.strip()
630
631 UP_right = right
632 with tagswitch(right) as case:
633 if case(value_e.Str):
634 right = cast(value.Str, UP_right)
635 return value.Bool(left2 == right.s)
636
637 elif case(value_e.Bool):
638 right = cast(value.Bool, UP_right)
639 left2 = left2.lower()
640 lb = False
641 if left2 == 'true':
642 lb = True
643 elif left2 == 'false':
644 lb = False
645 else:
646 return value.Bool(False)
647
648 log('left %r left2 %r', left, left2)
649 return value.Bool(lb == right.b)
650
651 elif case(value_e.Int):
652 right = cast(value.Int, UP_right)
653 if not left2.isdigit():
654 return value.Bool(False)
655
656 return value.Bool(int(left2) == right.i)
657
658 e_die('~== expects Str, Int, or Bool on the right', op)
659
660 else:
661 try:
662 if op.id == Id.Arith_Tilde:
663 result = val_ops.RegexMatch(left, right, self.mem)
664
665 elif op.id == Id.Expr_NotTilde:
666 # don't pass self.mem to not set a match
667 result = not val_ops.RegexMatch(left, right, None)
668
669 else:
670 raise AssertionError(op)
671 except RuntimeError as e:
672 # Status 2 indicates a regex parse error. This is fatal in OSH but
673 # not in bash, which treats [[ like a command with an exit code.
674 e_die_status(2, 'Invalid regex %r' % right, op)
675
676 if not result:
677 return value.Bool(result)
678
679 left = right
680
681 return value.Bool(result)
682
683 def _EvalArgListUntyped(self, args):
684 # type: (ArgList) -> Tuple[List[Any], Dict[str, Any]]
685 """For procs and funcs - UNTYPED"""
686 pos_args = [] # type: List[Any]
687 for arg in args.pos_args:
688 UP_arg = arg
689
690 if arg.tag() == expr_e.Spread:
691 arg = cast(expr.Spread, UP_arg)
692 # assume it returns a list
693 #pos_args.extend(self._EvalExpr(arg.child))
694 else:
695 pos_args.append(self._EvalExpr(arg))
696
697 kwargs = {} # type: Dict[str, Any]
698
699 # NOTE: Keyword args aren't tested
700 if 0:
701 for named in args.named:
702 if named.name:
703 kwargs[named.name.tval] = self._EvalExpr(named.value)
704 else:
705 # ...named
706 kwargs.update(self._EvalExpr(named.value))
707
708 return pos_args, kwargs
709
710 def _EvalArgList(self, args, me=None):
711 # type: (ArgList, Optional[value_t]) -> Tuple[List[value_t], Dict[str, value_t]]
712 """For procs and args - TYPED """
713
714 # TODO: CommandEvaluator.RunProc is similar
715
716 pos_args = [] # type: List[value_t]
717
718 if me: # self/this argument
719 pos_args.append(me)
720
721 for arg in args.pos_args:
722 UP_arg = arg
723
724 if arg.tag() == expr_e.Spread:
725 arg = cast(expr.Spread, UP_arg)
726 # assume it returns a list
727 #pos_args.extend(self._EvalExpr(arg.child))
728 pass
729 else:
730 pos_args.append(self._EvalExpr(arg))
731
732 kwargs = {} # type: Dict[str, value_t]
733
734 # NOTE: Keyword args aren't tested
735 if 0:
736 for named in args.named:
737 if named.name:
738 kwargs[named.name.tval] = self._EvalExpr(named.value)
739 else:
740 # ...named
741 kwargs.update(self._EvalExpr(named.value))
742
743 return pos_args, kwargs
744
745 def _EvalFuncCall(self, node):
746 # type: (expr.FuncCall) -> value_t
747
748 func = self._EvalExpr(node.func)
749 UP_func = func
750
751 with tagswitch(func) as case:
752 if case(value_e.Func):
753 func = cast(value.Func, UP_func)
754 # C++ cast to work around ASDL 'any'
755 f = cast(vm._Callable, func.callable)
756 pos_args, named_args = self._EvalArgList(node.args)
757 #log('pos_args %s', pos_args)
758
759 ret = f.Call(typed_args.Reader(pos_args, named_args))
760
761 #log('ret %s', ret)
762 return ret
763
764 elif case(value_e.BoundFunc):
765 func = cast(value.BoundFunc, UP_func)
766
767 #assert isinstance(func.callable, vm._Callable), "Bound funcs must be typed"
768 # Cast to work around ASDL limitation for now
769 f = cast(vm._Callable, func.callable)
770
771 pos_args, named_args = self._EvalArgList(node.args, me=func.me)
772
773 ret = f.Call(typed_args.Reader(pos_args, named_args))
774
775 return ret
776
777 else:
778 raise error.TypeErr(func, 'Expected a function or method',
779 node.args.left)
780
781 raise AssertionError()
782
783 def _EvalSubscript(self, node):
784 # type: (Subscript) -> value_t
785
786 obj = self._EvalExpr(node.obj)
787 index = self._EvalExpr(node.index)
788
789 UP_obj = obj
790 UP_index = index
791
792 with tagswitch(obj) as case:
793 if case(value_e.Str):
794 # Note: s[i] and s[i:j] are like Go, on bytes. We may provide
795 # s->numBytes(), s->countRunes(), and iteration over runes.
796 obj = cast(value.Str, UP_obj)
797 with tagswitch(index) as case2:
798 if case2(value_e.Slice):
799 index = cast(value.Slice, UP_index)
800
801 lower = index.lower.i if index.lower else 0
802 upper = index.upper.i if index.upper else len(obj.s)
803 return value.Str(obj.s[lower:upper])
804
805 elif case2(value_e.Int):
806 index = cast(value.Int, UP_index)
807 try:
808 return value.Str(obj.s[index.i])
809 except IndexError:
810 # TODO: expr.Subscript has no error location
811 raise error.Expr('index out of range', loc.Missing)
812
813 else:
814 raise error.TypeErr(index,
815 'Str index expected Int or Slice',
816 loc.Missing)
817
818 elif case(value_e.List):
819 obj = cast(value.List, UP_obj)
820 with tagswitch(index) as case2:
821 if case2(value_e.Slice):
822 index = cast(value.Slice, UP_index)
823
824 lower = index.lower.i if index.lower else 0
825 upper = index.upper.i if index.upper else len(
826 obj.items)
827 return value.List(obj.items[lower:upper])
828
829 elif case2(value_e.Int):
830 index = cast(value.Int, UP_index)
831 try:
832 return obj.items[index.i]
833 except IndexError:
834 # TODO: expr.Subscript has no error location
835 raise error.Expr('index out of range', loc.Missing)
836
837 else:
838 raise error.TypeErr(
839 index, 'List index expected Int or Slice',
840 loc.Missing)
841
842 elif case(value_e.Dict):
843 obj = cast(value.Dict, UP_obj)
844 if index.tag() != value_e.Str:
845 raise error.TypeErr(index, 'Dict index expected Str',
846 loc.Missing)
847
848 index = cast(value.Str, UP_index)
849 try:
850 return obj.d[index.s]
851 except KeyError:
852 # TODO: expr.Subscript has no error location
853 raise error.Expr('dict entry not found', loc.Missing)
854
855 raise error.TypeErr(obj, 'Subscript expected Str, List, or Dict',
856 loc.Missing)
857
858 def _EvalAttribute(self, node):
859 # type: (Attribute) -> value_t
860
861 o = self._EvalExpr(node.obj)
862 UP_o = o
863
864 op_id = node.op.id
865 if op_id == Id.Expr_RArrow:
866 name = node.attr.tval
867 ty = o.tag()
868
869 recv = self.methods.get(ty)
870 method = recv.get(name) if recv is not None else None
871 if not method:
872 raise error.TypeErrVerbose(
873 'Method %r does not exist on type %s' %
874 (name, ui.ValType(o)), node.attr)
875
876 return value.BoundFunc(o, method)
877
878 if op_id == Id.Expr_Dot: # d.key is like d['key']
879 name = node.attr.tval
880 with tagswitch(o) as case:
881 if case(value_e.Dict):
882 o = cast(value.Dict, UP_o)
883 try:
884 result = o.d[name]
885 except KeyError:
886 raise error.Expr('dict entry not found', node.op)
887
888 else:
889 raise error.TypeErr(o, 'Dot operator expected Dict',
890 node.op)
891
892 return result
893
894 if op_id == Id.Expr_DColon: # StaticName::member
895 raise NotImplementedError(op_id)
896
897 # TODO: We should prevent virtual lookup here? This is a pure static
898 # namespace lookup?
899 # But Python doesn't any hook for this.
900 # Maybe we can just check that it's a module? And modules don't lookup
901 # in a supertype or __class__, etc.
902
903 raise AssertionError(op_id)
904
905 def _EvalExpr(self, node):
906 # type: (expr_t) -> value_t
907 """Turn an expression into a value."""
908 if 0:
909 print('_EvalExpr()')
910 node.PrettyPrint()
911 print('')
912
913 UP_node = node
914 with tagswitch(node) as case:
915 if case(expr_e.Const):
916 node = cast(expr.Const, UP_node)
917 return self._EvalConst(node)
918
919 elif case(expr_e.Var):
920 node = cast(expr.Var, UP_node)
921 return self._LookupVar(node.name.tval, node.name)
922
923 elif case(expr_e.CommandSub):
924 node = cast(CommandSub, UP_node)
925
926 id_ = node.left_token.id
927 if id_ == Id.Left_CaretParen: # ^(echo block literal)
928 # TODO: Propgate location info?
929 return value.Block(node.child)
930 else:
931 stdout_str = self.shell_ex.RunCommandSub(node)
932 if id_ == Id.Left_AtParen: # @(seq 3)
933 # TODO: Should use QSN8 lines
934 strs = self.splitter.SplitForWordEval(stdout_str)
935 items = [value.Str(s)
936 for s in strs] # type: List[value_t]
937 return value.List(items)
938 else:
939 return value.Str(stdout_str)
940
941 elif case(expr_e.ShArrayLiteral): # var x = :| foo *.py |
942 node = cast(ShArrayLiteral, UP_node)
943 words = braces.BraceExpandWords(node.words)
944 strs = self.word_ev.EvalWordSequence(words)
945 #log('ARRAY LITERAL EVALUATED TO -> %s', strs)
946 #return value.BashArray(strs)
947
948 # It's equivalent to ['foo', 'bar']
949 items = [value.Str(s) for s in strs]
950 return value.List(items)
951
952 elif case(expr_e.DoubleQuoted):
953 node = cast(DoubleQuoted, UP_node)
954 # In an ideal world, YSH would *statically* disallow:
955 #
956 # - "$@" and "${array[@]}"
957 # - backticks like `echo hi`
958 # - $(( 1+2 )) and $[] -- although useful for refactoring
959 # - not sure: ${x%%} -- could disallow this
960 # - these enters the ArgDQ state: "${a:-foo bar}" ?
961 #
962 # But that would complicate the parser/evaluator. So just rely
963 # on runtime strict_array to disallow the bad parts.
964 return value.Str(self.word_ev.EvalDoubleQuotedToString(node))
965
966 elif case(expr_e.SingleQuoted):
967 node = cast(SingleQuoted, UP_node)
968 return value.Str(word_compile.EvalSingleQuoted(node))
969
970 elif case(expr_e.BracedVarSub):
971 node = cast(BracedVarSub, UP_node)
972 return value.Str(self.word_ev.EvalBracedVarSubToString(node))
973
974 elif case(expr_e.SimpleVarSub):
975 node = cast(SimpleVarSub, UP_node)
976 return value.Str(self.word_ev.EvalSimpleVarSubToString(node))
977
978 elif case(expr_e.Unary):
979 node = cast(expr.Unary, UP_node)
980 return self._EvalUnary(node)
981
982 elif case(expr_e.Binary):
983 node = cast(expr.Binary, UP_node)
984 return self._EvalBinary(node)
985
986 elif case(expr_e.Slice): # a[:0]
987 node = cast(expr.Slice, UP_node)
988
989 lower = None # type: Optional[IntBox]
990 upper = None # type: Optional[IntBox]
991
992 if node.lower:
993 msg = 'Slice begin should be Int'
994 i = val_ops.ToInt(self._EvalExpr(node.lower), msg,
995 loc.Missing)
996 lower = IntBox(i)
997
998 if node.upper:
999 msg = 'Slice end should be Int'
1000 i = val_ops.ToInt(self._EvalExpr(node.upper), msg,
1001 loc.Missing)
1002 upper = IntBox(i)
1003
1004 return value.Slice(lower, upper)
1005
1006 elif case(expr_e.Range):
1007 node = cast(expr.Range, UP_node)
1008
1009 assert node.lower is not None
1010 assert node.upper is not None
1011
1012 msg = 'Range begin should be Int'
1013 i = val_ops.ToInt(self._EvalExpr(node.lower), msg, loc.Missing)
1014
1015 msg = 'Range end should be Int'
1016 j = val_ops.ToInt(self._EvalExpr(node.upper), msg, loc.Missing)
1017
1018 return value.Range(i, j)
1019
1020 elif case(expr_e.Compare):
1021 node = cast(expr.Compare, UP_node)
1022 return self._EvalCompare(node)
1023
1024 elif case(expr_e.IfExp):
1025 node = cast(expr.IfExp, UP_node)
1026 b = val_ops.ToBool(self._EvalExpr(node.test))
1027 if b:
1028 return self._EvalExpr(node.body)
1029 else:
1030 return self._EvalExpr(node.orelse)
1031
1032 elif case(expr_e.List):
1033 node = cast(expr.List, UP_node)
1034 items = [self._EvalExpr(e) for e in node.elts]
1035 return value.List(items)
1036
1037 elif case(expr_e.Tuple):
1038 node = cast(expr.Tuple, UP_node)
1039 # YSH language: Tuple syntax evaluates to LIST !
1040 items = [self._EvalExpr(e) for e in node.elts]
1041 return value.List(items)
1042
1043 elif case(expr_e.Dict):
1044 node = cast(expr.Dict, UP_node)
1045
1046 kvals = [self._EvalExpr(e) for e in node.keys]
1047 values = [] # type: List[value_t]
1048
1049 for i, value_expr in enumerate(node.values):
1050 if value_expr.tag() == expr_e.Implicit: # {key}
1051 # Enforced by parser. Key is expr.Const
1052 assert kvals[i].tag() == value_e.Str, kvals[i]
1053 key = cast(value.Str, kvals[i])
1054 v = self._LookupVar(key.s, loc.Missing)
1055 else:
1056 v = self._EvalExpr(value_expr)
1057
1058 values.append(v)
1059
1060 d = NewDict() # type: Dict[str, value_t]
1061 for i, kval in enumerate(kvals):
1062 k = val_ops.ToStr(kval, 'Dict keys must be strings',
1063 loc.Missing)
1064 d[k] = values[i]
1065
1066 return value.Dict(d)
1067
1068 elif case(expr_e.ListComp):
1069 e_die_status(
1070 2, 'List comprehension reserved but not implemented')
1071
1072 elif case(expr_e.GeneratorExp):
1073 e_die_status(
1074 2, 'Generator expression reserved but not implemented')
1075
1076 elif case(expr_e.Lambda): # |x| x+1 syntax is reserved
1077 # TODO: Location information for |, or func
1078 # Note: anonymous functions also evaluate to a Lambda, but they shouldn't
1079 e_die_status(2, 'Lambda reserved but not implemented')
1080
1081 elif case(expr_e.FuncCall):
1082 node = cast(expr.FuncCall, UP_node)
1083 return self._EvalFuncCall(node)
1084
1085 elif case(expr_e.Subscript):
1086 node = cast(Subscript, UP_node)
1087 return self._EvalSubscript(node)
1088
1089 elif case(expr_e.Attribute): # obj->method or mydict.key
1090 node = cast(Attribute, UP_node)
1091 return self._EvalAttribute(node)
1092
1093 elif case(expr_e.RegexLiteral):
1094 node = cast(expr.RegexLiteral, UP_node)
1095 return value.Eggex(self.EvalRegex(node.regex), None)
1096
1097 else:
1098 raise NotImplementedError(node.__class__.__name__)
1099
1100 def _EvalClassLiteralTerm(self, term, out):
1101 # type: (class_literal_term_t, List[char_class_term_t]) -> None
1102 UP_term = term
1103
1104 # These 2 vars will be initialized if we don't return early
1105 s = None # type: str
1106 char_code_tok = None # type: Token
1107
1108 with tagswitch(term) as case:
1109
1110 if case(class_literal_term_e.CharLiteral):
1111 term = cast(class_literal_term.CharLiteral, UP_term)
1112
1113 # What about \0?
1114 # At runtime, ERE should disallow it. But we can also disallow it here.
1115 out.append(word_compile.EvalCharLiteralForRegex(term.tok))
1116 return
1117
1118 elif case(class_literal_term_e.Range):
1119 term = cast(class_literal_term.Range, UP_term)
1120
1121 cp_start = word_compile.EvalCharLiteralForRegex(term.start)
1122 cp_end = word_compile.EvalCharLiteralForRegex(term.end)
1123 out.append(char_class_term.Range(cp_start, cp_end))
1124 return
1125
1126 elif case(class_literal_term_e.PosixClass):
1127 term = cast(PosixClass, UP_term)
1128 out.append(term)
1129 return
1130
1131 elif case(class_literal_term_e.PerlClass):
1132 term = cast(PerlClass, UP_term)
1133 out.append(term)
1134 return
1135
1136 elif case(class_literal_term_e.SingleQuoted):
1137 term = cast(SingleQuoted, UP_term)
1138
1139 s = word_compile.EvalSingleQuoted(term)
1140 char_code_tok = term.left
1141
1142 elif case(class_literal_term_e.Splice):
1143 term = cast(class_literal_term.Splice, UP_term)
1144
1145 val = self._LookupVar(term.var_name, term.name)
1146 s = val_ops.ToStr(val, 'Eggex char class splice expected Str',
1147 term.name)
1148 char_code_tok = term.name
1149
1150 assert s is not None, term
1151 for ch in s:
1152 char_int = ord(ch)
1153 if char_int >= 128:
1154 # / [ '\x7f\xff' ] / is better written as / [ \x7f \xff ] /
1155 e_die(
1156 "Use unquoted char literal for byte %d, which is >= 128"
1157 " (avoid confusing a set of bytes with a sequence)" %
1158 char_int, char_code_tok)
1159 out.append(CharCode(char_int, False, char_code_tok))
1160
1161 def _EvalRegex(self, node):
1162 # type: (re_t) -> re_t
1163 """Resolve references and eval constants in an Eggex
1164
1165 Rules:
1166 Splice => re_t # like Hex and @const in / Hex '.' @const /
1167 Speck/Token (syntax) => Primitive (logical)
1168 Chars and Strings => LiteralChars
1169 """
1170 UP_node = node
1171
1172 with tagswitch(node) as case:
1173 if case(re_e.Seq):
1174 node = cast(re.Seq, UP_node)
1175 new_children = [
1176 self._EvalRegex(child) for child in node.children
1177 ]
1178 return re.Seq(new_children)
1179
1180 elif case(re_e.Alt):
1181 node = cast(re.Alt, UP_node)
1182 new_children = [
1183 self._EvalRegex(child) for child in node.children
1184 ]
1185 return re.Alt(new_children)
1186
1187 elif case(re_e.Repeat):
1188 node = cast(re.Repeat, UP_node)
1189 return re.Repeat(self._EvalRegex(node.child), node.op)
1190
1191 elif case(re_e.Group):
1192 node = cast(re.Group, UP_node)
1193 return re.Group(self._EvalRegex(node.child))
1194
1195 elif case(re_e.Capture): # Identical to Group
1196 node = cast(re.Capture, UP_node)
1197 return re.Capture(self._EvalRegex(node.child), node.var_name)
1198
1199 elif case(re_e.CharClassLiteral):
1200 node = cast(re.CharClassLiteral, UP_node)
1201
1202 new_terms = [] # type: List[char_class_term_t]
1203 for t in node.terms:
1204 # can get multiple char_class_term.CharCode for a
1205 # class_literal_term_t
1206 self._EvalClassLiteralTerm(t, new_terms)
1207 return re.CharClass(node.negated, new_terms)
1208
1209 elif case(re_e.Token):
1210 node = cast(Token, UP_node)
1211
1212 id_ = node.id
1213 tval = node.tval
1214
1215 if id_ == Id.Expr_Dot:
1216 return re.Primitive(Id.Re_Dot)
1217
1218 if id_ == Id.Arith_Caret: # ^
1219 return re.Primitive(Id.Re_Start)
1220
1221 if id_ == Id.Expr_Dollar: # $
1222 return re.Primitive(Id.Re_End)
1223
1224 if id_ == Id.Expr_Name:
1225 if tval == 'dot':
1226 return re.Primitive(Id.Re_Dot)
1227 raise NotImplementedError(tval)
1228
1229 if id_ == Id.Expr_Symbol:
1230 if tval == '%start':
1231 return re.Primitive(Id.Re_Start)
1232 if tval == '%end':
1233 return re.Primitive(Id.Re_End)
1234 raise NotImplementedError(tval)
1235
1236 # Must be Id.Char_{OneChar,Hex,Unicode4,Unicode8}
1237 kind = consts.GetKind(id_)
1238 assert kind == Kind.Char, id_
1239 s = word_compile.EvalCStringToken(node)
1240 return re.LiteralChars(s, node)
1241
1242 elif case(re_e.SingleQuoted):
1243 node = cast(SingleQuoted, UP_node)
1244
1245 s = word_compile.EvalSingleQuoted(node)
1246 return re.LiteralChars(s, node.left)
1247
1248 elif case(re_e.Splice):
1249 node = cast(re.Splice, UP_node)
1250
1251 val = self._LookupVar(node.var_name, node.name)
1252 UP_val = val
1253 with tagswitch(val) as case:
1254 if case(value_e.Str):
1255 val = cast(value.Str, UP_val)
1256 to_splice = re.LiteralChars(val.s,
1257 node.name) # type: re_t
1258
1259 elif case(value_e.Eggex):
1260 val = cast(value.Eggex, UP_val)
1261 # Note: we only splice the regex, and ignore flags.
1262 # Should we warn about this?
1263 to_splice = val.expr
1264
1265 else:
1266 raise error.TypeErr(
1267 val, 'Eggex splice expected Str or Eggex',
1268 node.name)
1269 return to_splice
1270
1271 else:
1272 # These are evaluated at translation time
1273
1274 # case(re_e.PosixClass)
1275 # case(re_e.PerlClass)
1276 return node
1277
1278 def EvalRegex(self, node):
1279 # type: (re_t) -> re_t
1280 """Trivial wrapper."""
1281 new_node = self._EvalRegex(node)
1282
1283 # View it after evaluation
1284 if 0:
1285 log('After evaluation:')
1286 new_node.PrettyPrint()
1287 print()
1288 return new_node
1289
1290
1291 # vim: sw=4