OILS / ysh / expr_eval.py View on Github | oils.pub

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