OILS / core / state.py View on Github | oils.pub

2864 lines, 1472 significant
1# Copyright 2016 Andy Chu. All rights reserved.
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7"""
8state.py - Interpreter state
9"""
10from __future__ import print_function
11import time as time_ # avoid name conflict
12
13from _devbuild.gen.id_kind_asdl import Id
14from _devbuild.gen.option_asdl import option_i
15from _devbuild.gen.runtime_asdl import (error_code_e, scope_e, scope_t, Cell)
16from _devbuild.gen.syntax_asdl import (CompoundWord, loc, loc_t, Token,
17 debug_frame, debug_frame_e,
18 debug_frame_t)
19from _devbuild.gen.types_asdl import opt_group_i
20from _devbuild.gen.value_asdl import (value, value_e, value_t, Obj, sh_lvalue,
21 sh_lvalue_e, sh_lvalue_t, LeftName,
22 y_lvalue_e, regex_match, regex_match_e,
23 regex_match_t, RegexMatch)
24from core import bash_impl
25from core import error
26from core.error import e_usage, e_die
27from core import num
28from core import optview
29from display import ui
30from core import util
31from frontend import consts
32from frontend import location
33from frontend import match
34from mycpp import mops
35from mycpp import mylib
36from mycpp.mylib import (log, print_stderr, str_switch, tagswitch, iteritems,
37 NewDict)
38from pylib import os_path
39
40from libc import HAVE_GLOB_PERIOD
41import posix_ as posix
42
43from typing import Tuple, List, Dict, Optional, Any, cast, TYPE_CHECKING
44
45if TYPE_CHECKING:
46 from _devbuild.gen.option_asdl import option_t
47 from core import alloc
48 from osh import sh_expr_eval
49
50_ = log
51
52# flags for mem.SetValue()
53SetReadOnly = 1 << 0
54ClearReadOnly = 1 << 1
55SetExport = 1 << 2
56ClearExport = 1 << 3
57SetNameref = 1 << 4
58ClearNameref = 1 << 5
59
60
61class ctx_Source(object):
62 """For source builtin."""
63
64 def __init__(self, mem, source_name, argv, source_loc):
65 # type: (Mem, str, List[str], CompoundWord) -> None
66 mem.PushSource(source_name, argv, source_loc)
67 self.mem = mem
68 self.argv = argv
69
70 # Whenever we're sourcing, the 'is-main' builtin will return 1 (false)
71 self.to_restore = self.mem.is_main
72 self.mem.is_main = False
73
74 def __enter__(self):
75 # type: () -> None
76 pass
77
78 def __exit__(self, type, value, traceback):
79 # type: (Any, Any, Any) -> None
80 self.mem.PopSource(self.argv)
81
82 self.mem.is_main = self.to_restore
83
84
85class ctx_DebugTrap(object):
86 """For trap DEBUG."""
87
88 def __init__(self, mem):
89 # type: (Mem) -> None
90 mem.running_debug_trap = True
91 self.mem = mem
92
93 def __enter__(self):
94 # type: () -> None
95 pass
96
97 def __exit__(self, type, value, traceback):
98 # type: (Any, Any, Any) -> None
99 self.mem.running_debug_trap = False
100
101
102class ctx_ErrTrap(object):
103 """For trap ERR."""
104
105 def __init__(self, mem):
106 # type: (Mem) -> None
107 mem.running_err_trap = True
108 self.mem = mem
109
110 def __enter__(self):
111 # type: () -> None
112 pass
113
114 def __exit__(self, type, value, traceback):
115 # type: (Any, Any, Any) -> None
116 self.mem.running_err_trap = False
117
118
119class ctx_Option(object):
120 """Shopt --unset errexit { false }"""
121
122 def __init__(self, mutable_opts, opt_nums, b):
123 # type: (MutableOpts, List[int], bool) -> None
124 for opt_num in opt_nums:
125 mutable_opts.Push(opt_num, b)
126 if opt_num == option_i.errexit:
127 # it wasn't disabled
128 mutable_opts.errexit_disabled_tok.append(None)
129
130 self.mutable_opts = mutable_opts
131 self.opt_nums = opt_nums
132
133 def __enter__(self):
134 # type: () -> None
135 pass
136
137 def __exit__(self, type, value, traceback):
138 # type: (Any, Any, Any) -> None
139 for opt_num in self.opt_nums: # don't bother to do it in reverse order
140 if opt_num == option_i.errexit:
141 self.mutable_opts.errexit_disabled_tok.pop()
142 self.mutable_opts.Pop(opt_num)
143
144
145class ctx_AssignBuiltin(object):
146 """Local x=$(false) is disallowed."""
147
148 def __init__(self, mutable_opts):
149 # type: (MutableOpts) -> None
150 self.strict = False
151 if mutable_opts.Get(option_i.strict_errexit):
152 mutable_opts.Push(option_i._allow_command_sub, False)
153 mutable_opts.Push(option_i._allow_process_sub, False)
154 self.strict = True
155
156 self.mutable_opts = mutable_opts
157
158 def __enter__(self):
159 # type: () -> None
160 pass
161
162 def __exit__(self, type, value, traceback):
163 # type: (Any, Any, Any) -> None
164 if self.strict:
165 self.mutable_opts.Pop(option_i._allow_command_sub)
166 self.mutable_opts.Pop(option_i._allow_process_sub)
167
168
169class ctx_YshExpr(object):
170 """Command sub must fail in 'mystring' ++ $(false)"""
171
172 def __init__(self, mutable_opts):
173 # type: (MutableOpts) -> None
174
175 # Similar to $LIB_OSH/bash-strict.sh
176
177 # TODO: consider errexit:all group, or even ysh:all
178 # It would be nice if this were more efficient
179 mutable_opts.Push(option_i.command_sub_errexit, True)
180 mutable_opts.Push(option_i.errexit, True)
181 mutable_opts.Push(option_i.pipefail, True)
182 mutable_opts.Push(option_i.inherit_errexit, True)
183 mutable_opts.Push(option_i.strict_errexit, True)
184
185 # What about nounset? This has a similar pitfall -- it's not running
186 # like YSH.
187 # e.g. var x = $(echo $zz)
188
189 self.mutable_opts = mutable_opts
190
191 def __enter__(self):
192 # type: () -> None
193 pass
194
195 def __exit__(self, type, value, traceback):
196 # type: (Any, Any, Any) -> None
197 self.mutable_opts.Pop(option_i.command_sub_errexit)
198 self.mutable_opts.Pop(option_i.errexit)
199 self.mutable_opts.Pop(option_i.pipefail)
200 self.mutable_opts.Pop(option_i.inherit_errexit)
201 self.mutable_opts.Pop(option_i.strict_errexit)
202
203
204class ctx_ErrExit(object):
205 """Manages the errexit setting.
206
207 - The user can change it with builtin 'set' at any point in the code.
208 - These constructs implicitly disable 'errexit':
209 - if / while / until conditions
210 - ! (part of pipeline)
211 - && ||
212 """
213
214 def __init__(self, mutable_opts, b, disabled_tok):
215 # type: (MutableOpts, bool, Optional[Token]) -> None
216
217 # If we're disabling it, we need a span ID. If not, then we should NOT
218 # have one.
219 assert b == (disabled_tok is None)
220
221 mutable_opts.Push(option_i.errexit, b)
222 mutable_opts.errexit_disabled_tok.append(disabled_tok)
223
224 self.strict = False
225 if mutable_opts.Get(option_i.strict_errexit):
226 mutable_opts.Push(option_i._allow_command_sub, False)
227 mutable_opts.Push(option_i._allow_process_sub, False)
228 self.strict = True
229
230 self.mutable_opts = mutable_opts
231
232 def __enter__(self):
233 # type: () -> None
234 pass
235
236 def __exit__(self, type, value, traceback):
237 # type: (Any, Any, Any) -> None
238 self.mutable_opts.errexit_disabled_tok.pop()
239 self.mutable_opts.Pop(option_i.errexit)
240
241 if self.strict:
242 self.mutable_opts.Pop(option_i._allow_command_sub)
243 self.mutable_opts.Pop(option_i._allow_process_sub)
244
245
246class OptHook(object):
247 """Interface for option hooks."""
248
249 def __init__(self):
250 # type: () -> None
251 """Empty constructor for mycpp."""
252 pass
253
254 def OnChange(self, opt0_array, opt_name, b):
255 # type: (List[bool], str, bool) -> bool
256 """This method is called whenever an option is changed.
257
258 Returns success or failure.
259 """
260 return True
261
262
263def InitOpts():
264 # type: () -> List[bool]
265
266 opt0_array = [False] * option_i.ARRAY_SIZE
267 for opt_num in consts.DEFAULT_TRUE:
268 opt0_array[opt_num] = True
269 return opt0_array
270
271
272def MakeOpts(
273 mem, # type: Mem
274 environ, # type: Dict[str, str]
275 opt_hook, # type: OptHook
276):
277 # type: (...) -> Tuple[optview.Parse, optview.Exec, MutableOpts]
278
279 # Unusual representation: opt0_array + opt_stacks. For two features:
280 #
281 # - POSIX errexit disable semantics
282 # - YSH shopt --set nullglob { ... }
283 #
284 # We could do it with a single List of stacks. But because shopt --set
285 # random_option { ... } is very uncommon, we optimize and store the ZERO
286 # element of the stack in a flat array opt0_array (default False), and then
287 # the rest in opt_stacks, where the value could be None. By allowing the
288 # None value, we save ~50 or so list objects in the common case.
289
290 opt0_array = InitOpts()
291 # Overrides, including errexit
292 no_stack = None # type: List[bool] # for mycpp
293 opt_stacks = [no_stack] * option_i.ARRAY_SIZE # type: List[List[bool]]
294
295 parse_opts = optview.Parse(opt0_array, opt_stacks)
296 exec_opts = optview.Exec(opt0_array, opt_stacks)
297 mutable_opts = MutableOpts(mem, environ, opt0_array, opt_stacks, opt_hook)
298
299 return parse_opts, exec_opts, mutable_opts
300
301
302def _SetGroup(opt0_array, opt_nums, b):
303 # type: (List[bool], List[int], bool) -> None
304 for opt_num in opt_nums:
305 b2 = not b if opt_num in consts.DEFAULT_TRUE else b
306 opt0_array[opt_num] = b2
307
308
309def MakeYshParseOpts():
310 # type: () -> optview.Parse
311 opt0_array = InitOpts()
312 _SetGroup(opt0_array, consts.YSH_ALL, True)
313
314 no_stack = None # type: List[bool]
315 opt_stacks = [no_stack] * option_i.ARRAY_SIZE # type: List[List[bool]]
316
317 parse_opts = optview.Parse(opt0_array, opt_stacks)
318 return parse_opts
319
320
321def _AnyOptionNum(opt_name, ignore_shopt_not_impl):
322 # type: (str, bool) -> option_t
323 opt_num = consts.OptionNum(opt_name)
324 if opt_num == 0:
325 if ignore_shopt_not_impl:
326 opt_num = consts.UnimplOptionNum(opt_name)
327 if opt_num == 0:
328 e_usage('got invalid option %r' % opt_name, loc.Missing)
329
330 # Note: we relaxed this for YSH so we can do 'shopt --unset errexit' consistently
331 #if opt_num not in consts.SHOPT_OPTION_NUMS:
332 # e_usage("doesn't own option %r (try 'set')" % opt_name)
333
334 return opt_num
335
336
337def _SetOptionNum(opt_name):
338 # type: (str) -> option_t
339 opt_num = consts.OptionNum(opt_name)
340 if opt_num == 0:
341 e_usage('got invalid option %r' % opt_name, loc.Missing)
342
343 if opt_num not in consts.SET_OPTION_NUMS:
344 e_usage("invalid option %r (try shopt)" % opt_name, loc.Missing)
345
346 return opt_num
347
348
349def _MaybeWarnDotglob():
350 # type: () -> None
351 if HAVE_GLOB_PERIOD == 0:
352 # GNU libc and musl libc have GLOB_PERIOD, but Android doesn't
353 print_stderr(
354 "osh warning: GLOB_PERIOD wasn't found in libc, so 'shopt -s dotglob' won't work"
355 )
356
357
358class MutableOpts(object):
359
360 def __init__(self, mem, environ, opt0_array, opt_stacks, opt_hook):
361 # type: (Mem, Dict[str, str], List[bool], List[List[bool]], OptHook) -> None
362 self.mem = mem
363 self.environ = environ
364 self.opt0_array = opt0_array
365 self.opt_stacks = opt_stacks
366 self.errexit_disabled_tok = [] # type: List[Token]
367
368 # Used for 'set -o vi/emacs'
369 self.opt_hook = opt_hook
370
371 def Init(self):
372 # type: () -> None
373
374 # This comes after all the 'set' options.
375 shellopts = self.mem.GetValue('SHELLOPTS')
376
377 # True in OSH, but not in YSH (no_init_globals)
378 if shellopts.tag() == value_e.Str:
379 s = cast(value.Str, shellopts).s
380 self._InitOptionsFromEnv(s)
381
382 def _InitOptionsFromEnv(self, shellopts):
383 # type: (str) -> None
384 # e.g. errexit:nounset:pipefail
385 lookup = shellopts.split(':')
386 for opt_num in consts.SET_OPTION_NUMS:
387 name = consts.OptionName(opt_num)
388 if name in lookup:
389 self._SetOldOption(name, True)
390
391 def Push(self, opt_num, b):
392 # type: (int, bool) -> None
393 if opt_num == option_i.dotglob:
394 _MaybeWarnDotglob()
395
396 overlay = self.opt_stacks[opt_num]
397 if overlay is None or len(overlay) == 0:
398 self.opt_stacks[opt_num] = [b] # Allocate a new list
399 else:
400 overlay.append(b)
401
402 def Pop(self, opt_num):
403 # type: (int) -> bool
404 overlay = self.opt_stacks[opt_num]
405 assert overlay is not None
406 return overlay.pop()
407
408 def PushDynamicScope(self, b):
409 # type: (bool) -> None
410 """
411 Args
412 b: dynamic scope? False if it's a proc, and True if it's a shell
413 function.
414 """
415 # If it's already disabled, keep it disabled
416 if not self.Get(option_i.dynamic_scope):
417 b = False
418 self.Push(option_i.dynamic_scope, b)
419
420 def PopDynamicScope(self):
421 # type: () -> None
422 self.Pop(option_i.dynamic_scope)
423
424 def Get(self, opt_num):
425 # type: (int) -> bool
426 # Like _Getter in core/optview.py
427 overlay = self.opt_stacks[opt_num]
428 if overlay is None or len(overlay) == 0:
429 return self.opt0_array[opt_num]
430 else:
431 return overlay[-1] # the top value
432
433 def _Set(self, opt_num, b):
434 # type: (int, bool) -> None
435 """Used to disable errexit.
436
437 For bash compatibility in command sub.
438 """
439 if opt_num == option_i.dotglob:
440 _MaybeWarnDotglob()
441
442 # Like _Getter in core/optview.py
443 overlay = self.opt_stacks[opt_num]
444 if overlay is None or len(overlay) == 0:
445 self.opt0_array[opt_num] = b
446 else:
447 overlay[-1] = b # The top value
448
449 def set_interactive(self):
450 # type: () -> None
451 self._Set(option_i.interactive, True)
452
453 def set_redefine_const(self):
454 # type: () -> None
455 """For interactive shells."""
456 self._Set(option_i.redefine_const, True)
457
458 def set_redefine_source(self):
459 # type: () -> None
460 """For interactive shells. For source-guard"""
461 self._Set(option_i.redefine_source, True)
462
463 def set_emacs(self):
464 # type: () -> None
465 self._Set(option_i.emacs, True)
466
467 def _SetArrayByNum(self, opt_num, b):
468 # type: (int, bool) -> None
469 if (opt_num in consts.PARSE_OPTION_NUMS and
470 not self.mem.ParsingChangesAllowed()):
471 e_die('Syntax options must be set at the top level '
472 '(outside any function)')
473
474 self._Set(opt_num, b)
475
476 def SetDeferredErrExit(self, b):
477 # type: (bool) -> None
478 """Set the errexit flag, possibly deferring it.
479
480 Implements the unusual POSIX "defer" behavior. Callers: set -o
481 errexit, shopt -s ysh:all, ysh:upgrade
482 """
483 #log('Set %s', b)
484
485 # Defer it until we pop by setting the BOTTOM OF THE STACK.
486 self.opt0_array[option_i.errexit] = b
487
488 def DisableErrExit(self):
489 # type: () -> None
490 """Called by core/process.py to implement bash quirks."""
491 self._Set(option_i.errexit, False)
492
493 def ErrExitDisabledToken(self):
494 # type: () -> Optional[Token]
495 """If errexit is disabled by POSIX rules, return Token for construct.
496
497 e.g. the Token for 'if' or '&&' etc.
498 """
499 # Bug fix: The errexit disabling inherently follows a STACK DISCIPLINE.
500 # But we run trap handlers in the MAIN LOOP, which break this. So just
501 # declare that it's never disabled in a trap.
502 if self.Get(option_i._running_trap):
503 return None
504
505 if len(self.errexit_disabled_tok) == 0:
506 return None
507
508 return self.errexit_disabled_tok[-1]
509
510 def ErrExitIsDisabled(self):
511 # type: () -> bool
512 """
513 Similar to ErrExitDisabledToken, for ERR trap
514 """
515 if len(self.errexit_disabled_tok) == 0:
516 return False
517
518 return self.errexit_disabled_tok[-1] is not None
519
520 def _SetOldOption(self, opt_name, b):
521 # type: (str, bool) -> None
522 """Private version for synchronizing from SHELLOPTS."""
523 assert '_' not in opt_name
524 assert opt_name in consts.SET_OPTION_NAMES
525
526 opt_num = consts.OptionNum(opt_name)
527 assert opt_num != 0, opt_name
528
529 if opt_num == option_i.errexit:
530 self.SetDeferredErrExit(b)
531 else:
532 if opt_num == option_i.verbose and b:
533 print_stderr('osh warning: set -o verbose not implemented')
534 self._SetArrayByNum(opt_num, b)
535
536 # note: may FAIL before we get here.
537
538 success = self.opt_hook.OnChange(self.opt0_array, opt_name, b)
539
540 def SetOldOption(self, opt_name, b):
541 # type: (str, bool) -> None
542 """For set -o, set +o, or shopt -s/-u -o."""
543 unused = _SetOptionNum(opt_name) # validate it
544 self._SetOldOption(opt_name, b)
545
546 if not self.Get(option_i.no_init_globals):
547 UP_val = self.mem.GetValue('SHELLOPTS')
548 assert UP_val.tag() == value_e.Str, UP_val
549 val = cast(value.Str, UP_val)
550 shellopts = val.s
551
552 # Now check if SHELLOPTS needs to be updated. It may be exported.
553 #
554 # NOTE: It might be better to skip rewriting SEHLLOPTS in the common case
555 # where it is not used. We could do it lazily upon GET.
556
557 # Also, it would be slightly more efficient to update SHELLOPTS if
558 # settings were batched, Examples:
559 # - set -eu
560 # - shopt -s foo bar
561 if b:
562 if opt_name not in shellopts:
563 new_val = value.Str('%s:%s' % (shellopts, opt_name))
564 self.mem.InternalSetGlobal('SHELLOPTS', new_val)
565 else:
566 if opt_name in shellopts:
567 names = [n for n in shellopts.split(':') if n != opt_name]
568 new_val = value.Str(':'.join(names))
569 self.mem.InternalSetGlobal('SHELLOPTS', new_val)
570
571 def SetAnyOption(self, opt_name, b, ignore_shopt_not_impl=False):
572 # type: (str, bool, bool) -> None
573 """For shopt -s/-u and sh -O/+O."""
574
575 # shopt -s ysh:all turns on all YSH options, which includes all strict
576 # options
577 opt_group = consts.OptionGroupNum(opt_name)
578 if opt_group == opt_group_i.YshUpgrade:
579 _SetGroup(self.opt0_array, consts.YSH_UPGRADE, b)
580 self.SetDeferredErrExit(b) # Special case
581 if b: # ENV dict
582 self.mem.MaybeInitEnvDict(self.environ)
583 return
584
585 if opt_group == opt_group_i.YshAll:
586 _SetGroup(self.opt0_array, consts.YSH_ALL, b)
587 self.SetDeferredErrExit(b) # Special case
588 if b: # ENV dict
589 self.mem.MaybeInitEnvDict(self.environ)
590 return
591
592 if opt_group == opt_group_i.StrictAll:
593 _SetGroup(self.opt0_array, consts.STRICT_ALL, b)
594 return
595
596 opt_num = _AnyOptionNum(opt_name, ignore_shopt_not_impl)
597
598 if opt_num == option_i.errexit:
599 self.SetDeferredErrExit(b)
600 return
601
602 self._SetArrayByNum(opt_num, b)
603
604
605class _ArgFrame(object):
606 """Stack frame for arguments array."""
607
608 def __init__(self, argv):
609 # type: (List[str]) -> None
610 self.argv = argv
611 self.num_shifted = 0
612
613 def __repr__(self):
614 # type: () -> str
615 return '<_ArgFrame %s %d at %x>' % (self.argv, self.num_shifted,
616 id(self))
617
618 def Dump(self):
619 # type: () -> Dict[str, value_t]
620 items = [value.Str(s) for s in self.argv] # type: List[value_t]
621 argv = value.List(items)
622 return {
623 'argv': argv,
624 'num_shifted': num.ToBig(self.num_shifted),
625 }
626
627 def GetArgNum(self, arg_num):
628 # type: (int) -> value_t
629
630 # $0 is handled elsewhere
631 assert 1 <= arg_num, arg_num
632
633 index = self.num_shifted + arg_num - 1
634 if index >= len(self.argv):
635 return value.Undef
636
637 return value.Str(self.argv[index])
638
639 def GetArgv(self):
640 # type: () -> List[str]
641 return self.argv[self.num_shifted:]
642
643 def GetNumArgs(self):
644 # type: () -> int
645 return len(self.argv) - self.num_shifted
646
647 def SetArgv(self, argv):
648 # type: (List[str]) -> None
649 self.argv = argv
650 self.num_shifted = 0
651
652
653def _DumpVarFrame(frame):
654 # type: (Dict[str, Cell]) -> Dict[str, value_t]
655 """Dump the stack frame as reasonably compact and readable JSON."""
656
657 vars_json = {} # type: Dict[str, value_t]
658 for name, cell in iteritems(frame):
659 cell_json = {} # type: Dict[str, value_t]
660
661 buf = mylib.BufWriter()
662 if cell.exported:
663 buf.write('x')
664 if cell.readonly:
665 buf.write('r')
666 flags = buf.getvalue()
667 if len(flags):
668 cell_json['flags'] = value.Str(flags)
669
670 # TODO:
671 # - Use packle for crash dumps! Then we can represent object cycles
672 # - Right now the JSON serializer will probably crash
673 # - although BashArray and BashAssoc may need 'type' tags
674 # - they don't round trip correctly
675 # - maybe add value.Tombstone here or something?
676 # - value.{Func,Eggex,...} may have value.Tombstone and
677 # vm.ValueIdString()?
678
679 with tagswitch(cell.val) as case:
680 if case(value_e.Undef):
681 cell_json['val'] = value.Null
682
683 elif case(value_e.Str, value_e.BashArray, value_e.BashAssoc,
684 value_e.SparseArray):
685 cell_json['val'] = cell.val
686
687 else:
688 # TODO: should we show the object ID here?
689 pass
690
691 vars_json[name] = value.Dict(cell_json)
692
693 return vars_json
694
695
696def _LineNumber(tok):
697 # type: (Optional[Token]) -> str
698 """ For $BASH_LINENO """
699 if tok is None:
700 return '-1'
701 return str(tok.line.line_num)
702
703
704def _AddCallToken(d, token):
705 # type: (Dict[str, value_t], Optional[Token]) -> None
706 if token is None:
707 return
708 d['call_source'] = value.Str(ui.GetLineSourceString(token.line))
709 d['call_line_num'] = num.ToBig(token.line.line_num)
710 d['call_line'] = value.Str(token.line.content)
711
712
713class ctx_FuncCall(object):
714 """For func calls."""
715
716 def __init__(self, mem, func):
717 # type: (Mem, value.Func) -> None
718
719 self.saved_globals = mem.var_stack[0]
720
721 assert func.module_frame is not None
722 mem.var_stack[0] = func.module_frame
723
724 frame = NewDict() # type: Dict[str, Cell]
725 mem.var_stack.append(frame)
726
727 # TODO: Use location of (
728 mem.debug_stack.append(debug_frame.Func(mem.token_for_line))
729
730 self.mem = mem
731
732 def __enter__(self):
733 # type: () -> None
734 pass
735
736 def __exit__(self, type, value, traceback):
737 # type: (Any, Any, Any) -> None
738 self.mem.debug_stack.pop()
739 self.mem.var_stack.pop()
740
741 self.mem.var_stack[0] = self.saved_globals
742
743
744class ctx_ProcCall(object):
745 """For proc calls, including shell functions."""
746
747 def __init__(self, mem, mutable_opts, proc, argv, invoke_loc):
748 # type: (Mem, MutableOpts, value.Proc, List[str], CompoundWord) -> None
749
750 # TODO:
751 # should we separate procs and shell functions?
752 # - dynamic scope is one difference
753 # - '$@" shift etc. are another difference
754
755 self.saved_globals = mem.var_stack[0]
756
757 assert proc.module_frame is not None
758 mem.var_stack[0] = proc.module_frame
759
760 frame = NewDict() # type: Dict[str, Cell]
761
762 assert argv is not None
763 if proc.sh_compat:
764 # shell function
765 mem.argv_stack.append(_ArgFrame(argv))
766 else:
767 # procs
768 # - open: is equivalent to ...ARGV
769 # - closed: ARGV is empty list
770 frame['ARGV'] = _MakeArgvCell(argv)
771
772 mem.var_stack.append(frame)
773
774 mem.debug_stack.append(
775 debug_frame.ProcLike(invoke_loc, proc.name_tok, proc.name))
776
777 # Dynamic scope is only for shell functions
778 mutable_opts.PushDynamicScope(proc.sh_compat)
779
780 # It may have been disabled with ctx_ErrExit for 'if echo $(false)', but
781 # 'if p' should be allowed.
782 self.mem = mem
783 self.mutable_opts = mutable_opts
784 self.sh_compat = proc.sh_compat
785
786 def __enter__(self):
787 # type: () -> None
788 pass
789
790 def __exit__(self, type, value, traceback):
791 # type: (Any, Any, Any) -> None
792 self.mutable_opts.PopDynamicScope()
793 self.mem.debug_stack.pop()
794 self.mem.var_stack.pop()
795
796 if self.sh_compat:
797 self.mem.argv_stack.pop()
798
799 self.mem.var_stack[0] = self.saved_globals
800
801
802class ctx_Temp(object):
803 """ POSIX shell FOO=bar mycommand """
804
805 def __init__(self, mem):
806 # type: (Mem) -> None
807 self.mem = mem
808 mem.PushTemp()
809
810 def __enter__(self):
811 # type: () -> None
812 pass
813
814 def __exit__(self, type, value, traceback):
815 # type: (Any, Any, Any) -> None
816 self.mem.PopTemp()
817
818
819class ctx_EnvObj(object):
820 """YSH FOO=bar my-command"""
821
822 def __init__(self, mem, bindings):
823 # type: (Mem, Dict[str, value_t]) -> None
824 self.mem = mem
825 mem.PushEnvObj(bindings)
826
827 def __enter__(self):
828 # type: () -> None
829 pass
830
831 def __exit__(self, type, value, traceback):
832 # type: (Any, Any, Any) -> None
833 self.mem.PopEnvObj()
834
835
836class ctx_Registers(object):
837 """For $PS1, $PS4, $PROMPT_COMMAND, traps, and headless EVAL.
838
839 This is tightly coupled to state.Mem, so it's not in builtin/pure_ysh.
840 """
841
842 def __init__(self, mem):
843 # type: (Mem) -> None
844
845 # Because some prompts rely on the status leaking. See issue #853.
846 # PS1 also does.
847 last = mem.last_status[-1]
848 mem.last_status.append(last)
849 mem.try_status.append(0)
850 mem.try_error.append(value.Dict({}))
851
852 # TODO: We should also copy these values! Turn the whole thing into a
853 # frame.
854 mem.pipe_status.append([])
855 mem.process_sub_status.append([])
856
857 mem.regex_match.append(regex_match.No)
858
859 self.mem = mem
860
861 def __enter__(self):
862 # type: () -> None
863 pass
864
865 def __exit__(self, type, value, traceback):
866 # type: (Any, Any, Any) -> None
867 self.mem.regex_match.pop()
868
869 self.mem.process_sub_status.pop()
870 self.mem.pipe_status.pop()
871
872 self.mem.try_error.pop()
873 self.mem.try_status.pop()
874 self.mem.last_status.pop()
875
876
877class ctx_ThisDir(object):
878 """For $_this_dir."""
879
880 def __init__(self, mem, filename):
881 # type: (Mem, Optional[str]) -> None
882 self.do_pop = False
883 if filename is not None: # script_name in main() may be -c, etc.
884 d = os_path.dirname(os_path.abspath(filename))
885 mem.this_dir.append(d)
886 self.do_pop = True
887
888 self.mem = mem
889
890 def __enter__(self):
891 # type: () -> None
892 pass
893
894 def __exit__(self, type, value, traceback):
895 # type: (Any, Any, Any) -> None
896 if self.do_pop:
897 self.mem.this_dir.pop()
898
899
900def _MakeArgvCell(argv):
901 # type: (List[str]) -> Cell
902 items = [value.Str(a) for a in argv] # type: List[value_t]
903 return Cell(False, False, False, value.List(items))
904
905
906class ctx_LoopFrame(object):
907
908 def __init__(self, mem, do_new_frame):
909 # type: (Mem, bool) -> None
910 self.mem = mem
911 self.do_new_frame = do_new_frame
912
913 if self.do_new_frame:
914 to_enclose = self.mem.var_stack[-1]
915 self.new_frame = NewDict() # type: Dict[str, Cell]
916 self.new_frame['__E__'] = Cell(False, False, False,
917 value.Frame(to_enclose))
918 mem.var_stack.append(self.new_frame)
919
920 def __enter__(self):
921 # type: () -> None
922 pass
923
924 def __exit__(self, type, value, traceback):
925 # type: (Any, Any, Any) -> None
926 if self.do_new_frame:
927 self.mem.var_stack.pop()
928
929
930class ctx_EnclosedFrame(object):
931 """
932 Usages:
933
934 - io->evalToDict(), which is a primitive used for Hay and the Dict proc
935 - lexical scope aka static scope for block args to user-defined procs
936 - Including the "closures in a loop" problem, which will be used for Hay
937
938 var mutated = 'm'
939 var shadowed = 's'
940
941 Dict (&d) {
942 shadowed = 42
943 mutated = 'new' # this is equivalent to var mutated
944
945 setvar mutated = 'new'
946 }
947 echo $shadowed # restored to 's'
948 echo $mutated # new
949
950 Or maybe we disallow the setvar lookup?
951 """
952
953 def __init__(
954 self,
955 mem, # type: Mem
956 to_enclose, # type: Dict[str, Cell]
957 module_frame, # type: Dict[str, Cell]
958 out_dict, # type: Optional[Dict[str, value_t]]
959 ):
960 # type: (...) -> None
961 self.mem = mem
962 self.to_enclose = to_enclose
963 self.module_frame = module_frame
964 self.out_dict = out_dict
965
966 if module_frame is not None:
967 self.saved_globals = self.mem.var_stack[0]
968 self.mem.var_stack[0] = module_frame
969
970 # __E__ gets a lookup rule
971 self.new_frame = NewDict() # type: Dict[str, Cell]
972 self.new_frame['__E__'] = Cell(False, False, False,
973 value.Frame(to_enclose))
974
975 mem.var_stack.append(self.new_frame)
976
977 def __enter__(self):
978 # type: () -> None
979 pass
980
981 def __exit__(self, type, value, traceback):
982 # type: (Any, Any, Any) -> None
983
984 if self.out_dict is not None:
985 for name, cell in iteritems(self.new_frame):
986 #log('name %r', name)
987 #log('cell %r', cell)
988
989 # User can hide variables with _ suffix
990 # e.g. for i_ in foo bar { echo $i_ }
991 if name.endswith('_'):
992 continue
993
994 self.out_dict[name] = cell.val
995
996 # Restore
997 self.mem.var_stack.pop()
998
999 if self.module_frame is not None:
1000 self.mem.var_stack[0] = self.saved_globals
1001
1002
1003class ctx_CompoundWordDebugFrame(object):
1004
1005 def __init__(self, mem, w):
1006 # type: (Mem, CompoundWord) -> None
1007 mem.debug_stack.append(w)
1008 self.mem = mem
1009
1010 def __enter__(self):
1011 # type: () -> None
1012 pass
1013
1014 def __exit__(self, type, value_, traceback):
1015 # type: (Any, Any, Any) -> None
1016 self.mem.debug_stack.pop()
1017
1018
1019class ctx_TokenDebugFrame(object):
1020
1021 def __init__(self, mem, tok):
1022 # type: (Mem, Token) -> None
1023 mem.debug_stack.append(tok)
1024 self.mem = mem
1025
1026 def __enter__(self):
1027 # type: () -> None
1028 pass
1029
1030 def __exit__(self, type, value_, traceback):
1031 # type: (Any, Any, Any) -> None
1032 self.mem.debug_stack.pop()
1033
1034
1035class ctx_ModuleEval(object):
1036 """Evaluate a module with a new global stack frame.
1037
1038 e.g. setglobal in the new module doesn't leak
1039
1040 Different from ctx_EnclosedFrame because the new code can't see variables in
1041 the old frame.
1042 """
1043
1044 def __init__(self, mem, use_loc, out_dict, out_errors):
1045 # type: (Mem, CompoundWord, Dict[str, value_t], List[str]) -> None
1046 self.mem = mem
1047 self.out_dict = out_dict
1048 self.out_errors = out_errors
1049
1050 self.new_frame = NewDict() # type: Dict[str, Cell]
1051 self.saved_frame = mem.var_stack[0]
1052
1053 # Somewhat of a hack for tracing within a module.
1054 # Other solutions:
1055 # - PS4 can be __builtin__, but that would break shell compatibility
1056 # - We can have a separate YSH mechanism that uses a different settings
1057 # - We probably still want it to be scoped, like shvar PS4=z { ... }
1058 #
1059 # Note: there's a similar issue with HOSTNAME UID EUID etc. But those
1060 # could be io.hostname() io.getuid(), or lazy constants, etc.
1061
1062 ps4 = self.saved_frame.get('PS4')
1063 if ps4:
1064 self.new_frame['PS4'] = ps4
1065 # ENV is not in __builtins__ because it's mutable -- we want
1066 # 'setglobal' to work
1067 env = self.saved_frame.get('ENV')
1068 if env:
1069 self.new_frame['ENV'] = env
1070
1071 assert len(mem.var_stack) == 1
1072 mem.var_stack[0] = self.new_frame
1073
1074 # Whenever we're use-ing, the 'is-main' builtin will return 1 (false)
1075 self.to_restore = self.mem.is_main
1076 self.mem.is_main = False
1077
1078 # Equivalent of PushSource()
1079 mem.debug_stack.append(use_loc)
1080
1081 def __enter__(self):
1082 # type: () -> None
1083 pass
1084
1085 def __exit__(self, type, value_, traceback):
1086 # type: (Any, Any, Any) -> None
1087
1088 self.mem.debug_stack.pop()
1089
1090 self.mem.is_main = self.to_restore
1091
1092 assert len(self.mem.var_stack) == 1
1093 self.mem.var_stack[0] = self.saved_frame
1094
1095 # Now look in __export__ for the list of names to expose
1096
1097 cell = self.new_frame.get('__provide__')
1098 if cell is None:
1099 self.out_errors.append("Module is missing __provide__ List")
1100 return
1101
1102 provide_val = cell.val
1103 with tagswitch(provide_val) as case:
1104 if case(value_e.List):
1105 for val in cast(value.List, provide_val).items:
1106 if val.tag() == value_e.Str:
1107 name = cast(value.Str, val).s
1108
1109 cell = self.new_frame.get(name)
1110 if cell is None:
1111 self.out_errors.append(
1112 "Name %r was provided, but not defined" % name)
1113 continue
1114
1115 self.out_dict[name] = cell.val
1116 else:
1117 self.out_errors.append(
1118 "Expected Str in __provide__ List, got %s" %
1119 ui.ValType(val))
1120
1121 else:
1122 self.out_errors.append("__provide__ should be a List, got %s" %
1123 ui.ValType(provide_val))
1124
1125
1126class ctx_Eval(object):
1127 """Push temporary set of variables, $0, $1, $2, etc."""
1128
1129 def __init__(
1130 self,
1131 mem, # type: Mem
1132 dollar0, # type: Optional[str]
1133 pos_args, # type: Optional[List[str]]
1134 vars, # type: Optional[Dict[str, value_t]]
1135 ):
1136 # type: (...) -> None
1137 self.mem = mem
1138 self.dollar0 = dollar0
1139 self.pos_args = pos_args
1140 self.vars = vars
1141
1142 # $0 needs to have lexical scoping. So we store it with other locals.
1143 # As "0" cannot be parsed as an lvalue, we can safely store dollar0 there.
1144 if dollar0 is not None:
1145 #assert mem.GetValue("0", scope_e.LocalOnly).tag() == value_e.Undef
1146 #self.dollar0_lval = LeftName("0", loc.Missing)
1147 #mem.SetLocalName(self.dollar0_lval, value.Str(dollar0))
1148
1149 self.restore_dollar0 = self.mem.dollar0
1150 self.mem.dollar0 = dollar0
1151
1152 if pos_args is not None:
1153 mem.argv_stack.append(_ArgFrame(pos_args))
1154
1155 if vars is not None:
1156 self.restore = [] # type: List[Tuple[LeftName, value_t]]
1157 self._Push(vars)
1158
1159 def __enter__(self):
1160 # type: () -> None
1161 pass
1162
1163 def __exit__(self, type, value_, traceback):
1164 # type: (Any, Any, Any) -> None
1165 if self.vars is not None:
1166 self._Pop()
1167
1168 if self.pos_args is not None:
1169 self.mem.argv_stack.pop()
1170
1171 if self.dollar0 is not None:
1172 #self.mem.SetLocalName(self.dollar0_lval, value.Undef)
1173 self.mem.dollar0 = self.restore_dollar0
1174
1175 # Note: _Push and _Pop are separate methods because the C++ translation
1176 # doesn't like when they are inline in __init__ and __exit__.
1177 def _Push(self, vars):
1178 # type: (Dict[str, value_t]) -> None
1179 for name in vars:
1180 lval = location.LName(name)
1181 # LocalOnly because we are only overwriting the current scope
1182 old_val = self.mem.GetValue(name, scope_e.LocalOnly)
1183 self.restore.append((lval, old_val))
1184 self.mem.SetNamed(lval, vars[name], scope_e.LocalOnly)
1185
1186 def _Pop(self):
1187 # type: () -> None
1188 for lval, old_val in self.restore:
1189 if old_val.tag() == value_e.Undef:
1190 self.mem.Unset(lval, scope_e.LocalOnly)
1191 else:
1192 self.mem.SetNamed(lval, old_val, scope_e.LocalOnly)
1193
1194
1195def _FrameLookup(frame, name):
1196 # type: (Dict[str, Cell], str) -> Tuple[Optional[Cell], Dict[str, Cell]]
1197 """
1198 Look for a name in the frame, then recursively into the enclosing __E__
1199 frame, if it exists
1200 """
1201 cell = frame.get(name)
1202 if cell:
1203 return cell, frame
1204
1205 rear_cell = frame.get('__E__') # ctx_EnclosedFrame() sets this
1206 if rear_cell:
1207 rear_val = rear_cell.val
1208 assert rear_val, rear_val
1209 if rear_val.tag() == value_e.Frame:
1210 to_enclose = cast(value.Frame, rear_val).frame
1211 return _FrameLookup(to_enclose, name) # recursive call
1212
1213 return None, None
1214
1215
1216class Mem(object):
1217 """For storing variables.
1218
1219 Callers:
1220 User code: assigning and evaluating variables, in command context or
1221 arithmetic context.
1222 Completion engine: for COMP_WORDS, etc.
1223 Builtins call it implicitly: read, cd for $PWD, $OLDPWD, etc.
1224
1225 Modules: cmd_eval, word_eval, expr_eval, completion
1226 """
1227
1228 def __init__(
1229 self,
1230 dollar0, # type: str
1231 argv, # type: List[str]
1232 arena, # type: alloc.Arena
1233 debug_stack, # type: List[debug_frame_t]
1234 env_dict, # type: Dict[str, value_t]
1235 defaults=None, # type: Dict[str, value_t]
1236 ):
1237 # type: (...) -> None
1238 """
1239 Args:
1240 arena: currently unused
1241 """
1242 # circular dep initialized out of line
1243 self.exec_opts = None # type: optview.Exec
1244 self.unsafe_arith = None # type: sh_expr_eval.UnsafeArith
1245
1246 self.dollar0 = dollar0
1247 # If you only use YSH procs and funcs, this will remain at length 1.
1248 self.argv_stack = [_ArgFrame(argv)]
1249
1250 frame = NewDict() # type: Dict[str, Cell]
1251
1252 frame['ARGV'] = _MakeArgvCell(argv)
1253
1254 self.var_stack = [frame]
1255
1256 # The debug_stack isn't strictly necessary for execution. We use it
1257 # for crash dumps and for 3 parallel arrays: BASH_SOURCE, FUNCNAME, and
1258 # BASH_LINENO.
1259 self.debug_stack = debug_stack
1260
1261 self.env_dict = env_dict
1262 self.env_object = Obj(None, env_dict) # initial state
1263
1264 if defaults is None: # for unit tests only
1265 self.defaults = NewDict() # type: Dict[str, value_t]
1266 else:
1267 self.defaults = defaults
1268
1269 self.pwd = None # type: Optional[str]
1270 self.seconds_start = time_.time()
1271
1272 self.token_for_line = None # type: Optional[Token]
1273 self.loc_for_expr = loc.Missing # type: loc_t
1274
1275 self.last_arg = '' # $_ is initially empty, NOT unset
1276 self.line_num = value.Str('')
1277
1278 # Done ONCE on initialization
1279 self.root_pid = posix.getpid()
1280
1281 # TODO:
1282 # - These are REGISTERS mutated by user code.
1283 # - Call it self.reg_stack? with ctx_Registers
1284 # - push-registers builtin
1285 self.last_status = [0] # type: List[int] # a stack
1286 self.try_status = [0] # type: List[int] # a stack
1287 self.try_error = [value.Dict({})] # type: List[value.Dict] # a stack
1288 self.pipe_status = [[]] # type: List[List[int]] # stack
1289 self.process_sub_status = [[]] # type: List[List[int]] # stack
1290
1291 # A stack but NOT a register?
1292 self.this_dir = [] # type: List[str]
1293 self.regex_match = [regex_match.No] # type: List[regex_match_t]
1294
1295 self.last_bg_pid = -1 # Uninitialized value mutable public variable
1296
1297 self.running_debug_trap = False # set by ctx_DebugTrap()
1298 self.running_err_trap = False # set by ctx_ErrTrap
1299 self.is_main = True # we start out in main
1300
1301 # For the ctx builtin
1302 self.ctx_stack = [] # type: List[Dict[str, value_t]]
1303
1304 self.builtins = NewDict() # type: Dict[str, value_t]
1305
1306 # Note: Python 2 and 3 have __builtins__
1307 # This is just for inspection
1308 builtins_module = Obj(None, self.builtins)
1309
1310 # Code in any module can see __builtins__
1311 self.builtins['__builtins__'] = builtins_module
1312
1313 self.did_ysh_env = False # only initialize ENV once per process
1314
1315 from core import sh_init
1316 self.env_config = sh_init.EnvConfig(self, defaults)
1317
1318 def __repr__(self):
1319 # type: () -> str
1320 parts = [] # type: List[str]
1321 parts.append('<Mem')
1322 for i, frame in enumerate(self.var_stack):
1323 parts.append(' -- %d --' % i)
1324 for n, v in frame.iteritems():
1325 parts.append(' %s %s' % (n, v))
1326 parts.append('>')
1327 return '\n'.join(parts) + '\n'
1328
1329 def AddBuiltin(self, name, val):
1330 # type: (str, value_t) -> None
1331 self.builtins[name] = val
1332
1333 def SetPwd(self, pwd):
1334 # type: (str) -> None
1335 """Used by builtins."""
1336 self.pwd = pwd
1337
1338 def ParsingChangesAllowed(self):
1339 # type: () -> bool
1340 """For checking that syntax options are only used at the top level."""
1341
1342 # DISALLOW proc calls : they push argv_stack, var_stack, debug_stack
1343 # ALLOW source foo.sh arg1: pushes argv_stack, debug_stack
1344 # ALLOW FOO=bar : pushes var_stack
1345 return len(self.var_stack) == 1 or len(self.argv_stack) == 1
1346
1347 def Dump(self):
1348 # type: () -> Tuple[List[value_t], List[value_t], List[value_t]]
1349 """Copy state before unwinding the stack."""
1350 var_stack = [
1351 value.Dict(_DumpVarFrame(frame)) for frame in self.var_stack
1352 ] # type: List[value_t]
1353 argv_stack = [value.Dict(frame.Dump())
1354 for frame in self.argv_stack] # type: List[value_t]
1355
1356 debug_stack = [] # type: List[value_t]
1357
1358 # Reuse these immutable objects
1359 t_call = value.Str('Call')
1360 t_source = value.Str('Source')
1361
1362 for frame in reversed(self.debug_stack):
1363 UP_frame = frame
1364 d = None # type: Optional[Dict[str, value_t]]
1365 with tagswitch(frame) as case:
1366 if case(debug_frame_e.ProcLike):
1367 frame = cast(debug_frame.ProcLike, UP_frame)
1368 d = {
1369 'type': t_call,
1370 'func_name': value.Str(frame.proc_name)
1371 }
1372
1373 invoke_token = location.LeftTokenForCompoundWord(
1374 frame.invoke_loc)
1375 assert invoke_token is not None, frame.invoke_loc
1376 _AddCallToken(d, invoke_token)
1377 # TODO: Add def_tok
1378
1379 elif case(debug_frame_e.Source):
1380 frame = cast(debug_frame.Source, UP_frame)
1381 d = {
1382 'type': t_source,
1383 'source_name': value.Str(frame.source_name)
1384 }
1385 invoke_token = location.LeftTokenForCompoundWord(
1386 frame.source_loc)
1387 assert invoke_token is not None, frame.source_loc
1388 _AddCallToken(d, invoke_token)
1389
1390 # Note: Skip debug_frame.MainFile
1391 if d is not None:
1392 debug_stack.append(value.Dict(d))
1393
1394 return var_stack, argv_stack, debug_stack
1395
1396 def SetLastArgument(self, s):
1397 # type: (str) -> None
1398 """For $_"""
1399 self.last_arg = s
1400
1401 def SetTokenForLine(self, tok):
1402 # type: (Token) -> None
1403 """Set a token to compute $LINENO
1404
1405 This means it should be set on SimpleCommand, ShAssignment, ((, [[,
1406 case, etc. -- anything that evaluates a word. Example: there was a bug
1407 with 'case $LINENO'
1408
1409 This token also used as a "least-specific" / fallback location for
1410 errors in ExecuteAndCatch().
1411
1412 Although most of that should be taken over by 'with ui.ctx_Location()`,
1413 for the errfmt.
1414 """
1415 if self.running_debug_trap or self.running_err_trap:
1416 return
1417
1418 #if tok.span_id == runtime.NO_SPID:
1419 # NOTE: This happened in the osh-runtime benchmark for yash.
1420 #log('Warning: span_id undefined in SetTokenForLine')
1421
1422 #import traceback
1423 #traceback.print_stack()
1424 #return
1425
1426 # Reset the expression fallback location on every line. It REFINES the
1427 # line-based fallback location.
1428 self.loc_for_expr = loc.Missing
1429
1430 self.token_for_line = tok
1431
1432 def SetLocationForExpr(self, blame_loc):
1433 # type: (loc_t) -> None
1434 """
1435 A more specific fallback location, like the $[ in
1436
1437 echo $[len(42)]
1438 """
1439 self.loc_for_expr = blame_loc
1440
1441 def GetFallbackLocation(self):
1442 # type: () -> loc_t
1443
1444 if self.loc_for_expr != loc.Missing: # more specific
1445 return self.loc_for_expr
1446
1447 if self.token_for_line: # less specific
1448 return self.token_for_line
1449
1450 return loc.Missing
1451
1452 #
1453 # Status Variable Stack (for isolating $PS1 and $PS4)
1454 #
1455
1456 def LastStatus(self):
1457 # type: () -> int
1458 return self.last_status[-1]
1459
1460 def TryStatus(self):
1461 # type: () -> int
1462 return self.try_status[-1]
1463
1464 def TryError(self):
1465 # type: () -> value.Dict
1466 return self.try_error[-1]
1467
1468 def PipeStatus(self):
1469 # type: () -> List[int]
1470 return self.pipe_status[-1]
1471
1472 def SetLastStatus(self, x):
1473 # type: (int) -> None
1474 self.last_status[-1] = x
1475
1476 def SetTryStatus(self, x):
1477 # type: (int) -> None
1478 self.try_status[-1] = x
1479
1480 def SetTryError(self, x):
1481 # type: (value.Dict) -> None
1482 self.try_error[-1] = x
1483
1484 def SetPipeStatus(self, x):
1485 # type: (List[int]) -> None
1486 self.pipe_status[-1] = x
1487
1488 def SetSimplePipeStatus(self, status):
1489 # type: (int) -> None
1490
1491 # Optimization to avoid allocations
1492 top = self.pipe_status[-1]
1493 if len(top) == 1:
1494 top[0] = status
1495 else:
1496 self.pipe_status[-1] = [status]
1497
1498 def SetProcessSubStatus(self, x):
1499 # type: (List[int]) -> None
1500 self.process_sub_status[-1] = x
1501
1502 #
1503 # Call Stack
1504 #
1505
1506 def ShouldRunDebugTrap(self):
1507 # type: () -> bool
1508
1509 # TODO: RunLastPart of pipeline can disable this
1510
1511 # Don't recursively run DEBUG trap
1512 if self.running_debug_trap:
1513 return False
1514
1515 # Don't run it inside functions
1516 if len(self.var_stack) > 1:
1517 return False
1518
1519 return True
1520
1521 def IsGlobalScope(self):
1522 # type: () -> bool
1523 """
1524 local -g uses this, probably because bash does the wrong thing and
1525 prints LOCALS, not globals.
1526 """
1527 return len(self.var_stack) == 1
1528
1529 def InsideFunction(self):
1530 # type: () -> bool
1531 """For the ERR trap, and use builtin"""
1532
1533 # TODO: Should this be unified with ParsingChangesAllowed()? Slightly
1534 # different logic.
1535
1536 # Don't run it inside functions
1537 return len(self.var_stack) > 1
1538
1539 def GlobalFrame(self):
1540 # type: () -> Dict[str, Cell]
1541 """For defining the global scope of modules.
1542
1543 It's affected by ctx_ModuleEval()
1544 """
1545 return self.var_stack[0]
1546
1547 def CurrentFrame(self):
1548 # type: () -> Dict[str, Cell]
1549 """For attaching a stack frame to a value.Block"""
1550 return self.var_stack[-1]
1551
1552 def PushSource(self, source_name, argv, source_loc):
1553 # type: (str, List[str], CompoundWord) -> None
1554 """ For 'source foo.sh 1 2 3' """
1555 if len(argv):
1556 self.argv_stack.append(_ArgFrame(argv))
1557
1558 self.debug_stack.append(debug_frame.Source(source_loc, source_name))
1559
1560 def PopSource(self, argv):
1561 # type: (List[str]) -> None
1562 self.debug_stack.pop()
1563
1564 if len(argv):
1565 self.argv_stack.pop()
1566
1567 def PushTemp(self):
1568 # type: () -> None
1569 """For the temporary scope in 'FOO=bar BAR=baz echo'.
1570
1571 Also for PS4 evaluation with more variables.
1572 """
1573 # We don't want the 'read' builtin to write to this frame!
1574 frame = NewDict() # type: Dict[str, Cell]
1575 self.var_stack.append(frame)
1576
1577 def PopTemp(self):
1578 # type: () -> None
1579 self.var_stack.pop()
1580
1581 def _BindEnvObj(self):
1582 # type: () -> None
1583 self.SetNamed(location.LName('ENV'), self.env_object,
1584 scope_e.GlobalOnly)
1585
1586 def MaybeInitEnvDict(self, environ):
1587 # type: (Dict[str, str]) -> None
1588 if self.did_ysh_env:
1589 return
1590
1591 for name, s in iteritems(environ):
1592 self.env_dict[name] = value.Str(s)
1593
1594 self._BindEnvObj()
1595 self.did_ysh_env = True
1596
1597 def PushEnvObj(self, bindings):
1598 # type: (Dict[str, value_t]) -> None
1599 """Push "bindings" as the MOST visible part of the ENV Obj
1600
1601 i.e. first() / propView()
1602 """
1603 self.env_object = Obj(self.env_object, bindings)
1604 self._BindEnvObj()
1605
1606 def PopEnvObj(self):
1607 # type: () -> None
1608 """Pop a Dict of bindings."""
1609 self.env_object = self.env_object.prototype
1610 if self.env_object is None:
1611 # Note: there isn't a way to hit this now, but let's be defensive.
1612 # See test case in spec/ysh-env.test.sh.
1613 e_die('PopEnvObj: env.prototype is null', loc.Missing)
1614
1615 self._BindEnvObj()
1616
1617 #
1618 # Argv
1619 #
1620
1621 def Shift(self, n):
1622 # type: (int) -> int
1623 frame = self.argv_stack[-1]
1624 num_args = len(frame.argv)
1625
1626 if (frame.num_shifted + n) <= num_args:
1627 frame.num_shifted += n
1628 return 0 # success
1629 else:
1630 return 1 # silent error
1631
1632 def GetArg0(self):
1633 # type: () -> value.Str
1634 """Like GetArgNum(0) but with a more specific type."""
1635 return value.Str(self.dollar0)
1636
1637 def GetArgNum(self, arg_num):
1638 # type: (int) -> value_t
1639 if arg_num == 0:
1640 # Disabled
1641 if 0:
1642 # Problem: Doesn't obey enclosing frame?
1643 # Yeah it needs FrameLookup
1644 cell, _ = _FrameLookup(self.var_stack[-1], '0')
1645 if cell is not None:
1646 val = cell.val
1647 if val.tag() != value_e.Undef:
1648 return val
1649
1650 return value.Str(self.dollar0)
1651
1652 return self.argv_stack[-1].GetArgNum(arg_num)
1653
1654 def GetArgv(self):
1655 # type: () -> List[str]
1656 """For $* and $@."""
1657 return self.argv_stack[-1].GetArgv()
1658
1659 def SetArgv(self, argv):
1660 # type: (List[str]) -> None
1661 """For set -- 1 2 3."""
1662 # from set -- 1 2 3
1663 self.argv_stack[-1].SetArgv(argv)
1664
1665 #
1666 # Special Vars
1667 #
1668
1669 def GetSpecialVar(self, op_id):
1670 # type: (int) -> value_t
1671 if op_id == Id.VSub_Bang: # $!
1672 n = self.last_bg_pid
1673 if n == -1:
1674 return value.Undef # could be an error
1675
1676 elif op_id == Id.VSub_QMark: # $?
1677 # External commands need WIFEXITED test. What about subshells?
1678 n = self.last_status[-1]
1679
1680 elif op_id == Id.VSub_Pound: # $#
1681 n = self.argv_stack[-1].GetNumArgs()
1682
1683 elif op_id == Id.VSub_Dollar: # $$
1684 n = self.root_pid
1685
1686 else:
1687 raise NotImplementedError(op_id)
1688
1689 return value.Str(str(n))
1690
1691 #
1692 # Named Vars
1693 #
1694
1695 def _ResolveNameOnly(self, name, which_scopes):
1696 # type: (str, scope_t) -> Tuple[Optional[Cell], Dict[str, Cell]]
1697 """Helper for getting and setting variable.
1698
1699 Returns:
1700 cell: The cell corresponding to looking up 'name' with the given mode, or
1701 None if it's not found.
1702 var_frame: The frame it should be set to or deleted from.
1703 """
1704 if which_scopes == scope_e.Dynamic:
1705 for i in xrange(len(self.var_stack) - 1, -1, -1):
1706 var_frame = self.var_stack[i]
1707 cell, result_frame = _FrameLookup(var_frame, name)
1708 if cell:
1709 return cell, result_frame
1710 return None, self.var_stack[0] # set in global var_frame
1711
1712 if which_scopes == scope_e.LocalOnly:
1713 var_frame = self.var_stack[-1]
1714 cell, result_frame = _FrameLookup(var_frame, name)
1715 if cell:
1716 return cell, result_frame
1717 return None, var_frame
1718
1719 if which_scopes == scope_e.GlobalOnly:
1720 var_frame = self.var_stack[0]
1721 cell, result_frame = _FrameLookup(var_frame, name)
1722 if cell:
1723 return cell, result_frame
1724
1725 return None, var_frame
1726
1727 if which_scopes == scope_e.LocalOrGlobal:
1728 # Local
1729 var_frame = self.var_stack[-1]
1730 cell, result_frame = _FrameLookup(var_frame, name)
1731 if cell:
1732 return cell, result_frame
1733
1734 # Global
1735 var_frame = self.var_stack[0]
1736 cell, result_frame = _FrameLookup(var_frame, name)
1737 if cell:
1738 return cell, result_frame
1739
1740 return None, var_frame
1741
1742 raise AssertionError()
1743
1744 def _ResolveNameOrRef(
1745 self,
1746 name, # type: str
1747 which_scopes, # type: scope_t
1748 ref_trail=None, # type: Optional[List[str]]
1749 ):
1750 # type: (...) -> Tuple[Optional[Cell], Dict[str, Cell], str]
1751 """Look up a cell and namespace, but respect the nameref flag.
1752
1753 Resolving namerefs does RECURSIVE calls.
1754 """
1755 cell, var_frame = self._ResolveNameOnly(name, which_scopes)
1756
1757 if cell is None or not cell.nameref:
1758 return cell, var_frame, name # not a nameref
1759
1760 val = cell.val
1761 UP_val = val
1762 with tagswitch(val) as case:
1763 if case(value_e.Undef):
1764 # This is 'local -n undef_ref', which is kind of useless, because the
1765 # more common idiom is 'local -n ref=$1'. Note that you can mutate
1766 # references themselves with local -n ref=new.
1767 if self.exec_opts.strict_nameref():
1768 e_die('nameref %r is undefined' % name)
1769 else:
1770 return cell, var_frame, name # fallback
1771
1772 elif case(value_e.Str):
1773 val = cast(value.Str, UP_val)
1774 new_name = val.s
1775
1776 else:
1777 # SetValue() protects the invariant that nameref is Undef or Str
1778 raise AssertionError(val.tag())
1779
1780 # TODO: Respect eval_unsafe_arith here (issue 881). See how it's done in
1781 # 'printf -v' with MakeArithParser
1782 if not match.IsValidVarName(new_name):
1783 # e.g. '#' or '1' or ''
1784 if self.exec_opts.strict_nameref():
1785 e_die('nameref %r contains invalid variable name %r' %
1786 (name, new_name))
1787 else:
1788 # Bash has this odd behavior of clearing the nameref bit when
1789 # ref=#invalid#. strict_nameref avoids it.
1790 cell.nameref = False
1791 return cell, var_frame, name # fallback
1792
1793 # Check for circular namerefs.
1794 if ref_trail is None:
1795 ref_trail = [name]
1796 else:
1797 if new_name in ref_trail:
1798 e_die('Circular nameref %s' % ' -> '.join(ref_trail))
1799 ref_trail.append(new_name)
1800
1801 # 'declare -n' uses dynamic scope.
1802 cell, var_frame, cell_name = self._ResolveNameOrRef(
1803 new_name, scope_e.Dynamic, ref_trail=ref_trail)
1804 return cell, var_frame, cell_name
1805
1806 def IsBashAssoc(self, name):
1807 # type: (str) -> bool
1808 """Returns whether a name resolve to a cell with an associative array.
1809
1810 We need to know this to evaluate the index expression properly
1811 -- should it be coerced to an integer or not?
1812 """
1813 cell, _, _ = self._ResolveNameOrRef(name, self.ScopesForReading())
1814 # foo=([key]=value)
1815 return cell is not None and cell.val.tag() == value_e.BashAssoc
1816
1817 def SetPlace(self, place, val, blame_loc):
1818 # type: (value.Place, value_t, loc_t) -> None
1819
1820 yval = place.lval
1821 UP_yval = yval
1822 with tagswitch(yval) as case:
1823 if case(y_lvalue_e.Local):
1824 yval = cast(LeftName, UP_yval)
1825
1826 if 0:
1827 # Check that the frame is still alive
1828 # Note: Disabled because it doesn't work with modules. the
1829 # Place captures a frame in def-test.ysh, which we want to
1830 # mutate while Dict is executing in the module_frame for
1831 # def.ysh. See ctx_ModuleEval
1832 found = False
1833 for i in xrange(len(self.var_stack) - 1, -1, -1):
1834 frame = self.var_stack[i]
1835 if frame is place.frame:
1836 found = True
1837 #log('FOUND %s', found)
1838 break
1839 if not found:
1840 e_die(
1841 "Can't assign to place that's no longer on the call stack.",
1842 blame_loc)
1843
1844 frame = place.frame
1845 cell = frame.get(yval.name)
1846 if cell is None:
1847 cell = Cell(False, False, False, val)
1848 frame[yval.name] = cell
1849 else:
1850 cell.val = val
1851
1852 elif case(y_lvalue_e.Container):
1853 e_die('Container place not implemented', blame_loc)
1854
1855 else:
1856 raise AssertionError()
1857
1858 def SetLocalName(self, lval, val):
1859 # type: (LeftName, value_t) -> None
1860 """
1861 Set a name in the local scope - used for func/proc param binding, etc.
1862 """
1863
1864 # Equivalent to
1865 # self._ResolveNameOnly(lval.name, scope_e.LocalOnly)
1866 var_frame = self.var_stack[-1]
1867 cell = var_frame.get(lval.name)
1868
1869 if cell:
1870 if cell.readonly:
1871 e_die("Can't assign to readonly value %r" % lval.name,
1872 lval.blame_loc)
1873 cell.val = val # Mutate value_t
1874 else:
1875 cell = Cell(False, False, False, val)
1876 var_frame[lval.name] = cell
1877
1878 def SetNamed(self, lval, val, which_scopes, flags=0):
1879 # type: (LeftName, value_t, scope_t, int) -> None
1880 if flags & SetNameref or flags & ClearNameref:
1881 # declare -n ref=x # refers to the ref itself
1882 cell, var_frame = self._ResolveNameOnly(lval.name, which_scopes)
1883 cell_name = lval.name
1884 else:
1885 # ref=x # mutates THROUGH the reference
1886
1887 # Note on how to implement declare -n ref='a[42]'
1888 # 1. Call _ResolveNameOnly()
1889 # 2. If cell.nameref, call self.unsafe_arith.ParseVarRef() ->
1890 # BracedVarSub
1891 # 3. Turn BracedVarSub into an sh_lvalue, and call
1892 # self.unsafe_arith.SetValue() wrapper with ref_trail
1893 cell, var_frame, cell_name = self._ResolveNameOrRef(
1894 lval.name, which_scopes)
1895
1896 if cell:
1897 # Clear before checking readonly bit.
1898 # NOTE: Could be cell.flags &= flag_clear_mask
1899 if flags & ClearExport:
1900 cell.exported = False
1901 if flags & ClearReadOnly:
1902 cell.readonly = False
1903 if flags & ClearNameref:
1904 cell.nameref = False
1905
1906 if val is not None: # e.g. declare -rx existing
1907 # Note: this DYNAMIC check means we can't have 'const' in a loop.
1908 # But that's true for 'readonly' too, and hoisting it makes more
1909 # sense anyway.
1910 if cell.readonly:
1911 e_die("Can't assign to readonly value %r" % lval.name,
1912 lval.blame_loc)
1913 cell.val = val # CHANGE VAL
1914
1915 # NOTE: Could be cell.flags |= flag_set_mask
1916 if flags & SetExport:
1917 cell.exported = True
1918 if flags & SetReadOnly:
1919 cell.readonly = True
1920 if flags & SetNameref:
1921 cell.nameref = True
1922
1923 else:
1924 if val is None: # declare -rx nonexistent
1925 # set -o nounset; local foo; echo $foo # It's still undefined!
1926 val = value.Undef # export foo, readonly foo
1927
1928 cell = Cell(bool(flags & SetExport), bool(flags & SetReadOnly),
1929 bool(flags & SetNameref), val)
1930 var_frame[cell_name] = cell
1931
1932 # Maintain invariant that only strings and undefined cells can be
1933 # exported.
1934 assert cell.val is not None, cell
1935
1936 if cell.val.tag() not in (value_e.Undef, value_e.Str):
1937 if cell.exported:
1938 if self.exec_opts.strict_array():
1939 e_die("Only strings can be exported (strict_array)",
1940 lval.blame_loc)
1941 if cell.nameref:
1942 e_die("nameref must be a string", lval.blame_loc)
1943
1944 def SetValue(self, lval, val, which_scopes, flags=0):
1945 # type: (sh_lvalue_t, value_t, scope_t, int) -> None
1946 """
1947 Args:
1948 lval: sh_lvalue
1949 val: value, or None if only changing flags
1950 which_scopes:
1951 Local | Global | Dynamic - for builtins, PWD, etc.
1952 flags: packed pair (keyword_id, bit mask of set/clear flags)
1953
1954 Note: in bash, PWD=/ changes the directory. But not in dash.
1955 """
1956 # STRICTNESS / SANENESS:
1957 #
1958 # 1) Don't create arrays automatically, e.g. a[1000]=x
1959 # 2) Never change types? yeah I think that's a good idea, at least for YSH
1960 # (not sh, for compatibility). set -o strict_types or something. That
1961 # means arrays have to be initialized with let arr = [], which is fine.
1962 # This helps with stuff like IFS. It starts off as a string, and assigning
1963 # it to a list is an error. I guess you will have to turn this no for
1964 # bash?
1965 #
1966 # TODO:
1967 # - COMPUTED vars can't be set
1968 # - What about PWD / OLDPWD / UID / EUID ? You can simply make them
1969 # readonly.
1970 # - Maybe PARSE $PS1 and $PS4 when they're set, to avoid the error on use?
1971 # - Other validity: $HOME could be checked for existence
1972
1973 UP_lval = lval
1974 with tagswitch(lval) as case:
1975 if case(sh_lvalue_e.Var):
1976 lval = cast(LeftName, UP_lval)
1977
1978 self.SetNamed(lval, val, which_scopes, flags=flags)
1979
1980 elif case(sh_lvalue_e.Indexed):
1981 lval = cast(sh_lvalue.Indexed, UP_lval)
1982
1983 # There is no syntax 'declare a[x]'
1984 assert val is not None, val
1985
1986 # TODO: relax this for YSH
1987 assert val.tag() == value_e.Str, val
1988 rval = cast(value.Str, val)
1989
1990 # Note: location could be a[x]=1 or (( a[ x ] = 1 ))
1991 left_loc = lval.blame_loc
1992
1993 # bash/mksh have annoying behavior of letting you do LHS assignment to
1994 # Undef, which then turns into an INDEXED array. (Undef means that set
1995 # -o nounset fails.)
1996 cell, var_frame, _ = self._ResolveNameOrRef(
1997 lval.name, which_scopes)
1998 if not cell:
1999 self._BindNewArrayWithEntry(var_frame, lval, rval, flags)
2000 return
2001
2002 if cell.readonly:
2003 e_die("Can't assign to readonly array", left_loc)
2004
2005 UP_cell_val = cell.val
2006 # undef[0]=y is allowed
2007 with tagswitch(UP_cell_val) as case2:
2008 if case2(value_e.Undef):
2009 self._BindNewArrayWithEntry(var_frame, lval, rval,
2010 flags)
2011 return
2012
2013 elif case2(value_e.Str):
2014 # s=x
2015 # s[1]=y # invalid
2016 e_die("Can't assign to items in a string", left_loc)
2017
2018 elif case2(value_e.BashArray):
2019 cell_val = cast(value.BashArray, UP_cell_val)
2020 error_code = bash_impl.BashArray_SetElement(
2021 cell_val, lval.index, rval.s)
2022 if error_code == error_code_e.IndexOutOfRange:
2023 n = bash_impl.BashArray_Length(cell_val)
2024 e_die(
2025 "Index %d is out of bounds for array of length %d"
2026 % (lval.index, n), left_loc)
2027 return
2028
2029 elif case2(value_e.SparseArray):
2030 lhs_sp = cast(value.SparseArray, UP_cell_val)
2031 error_code = bash_impl.SparseArray_SetElement(
2032 lhs_sp, mops.IntWiden(lval.index), rval.s)
2033 if error_code == error_code_e.IndexOutOfRange:
2034 n_big = bash_impl.SparseArray_Length(lhs_sp)
2035 e_die(
2036 "Index %d is out of bounds for array of length %s"
2037 % (lval.index, mops.ToStr(n_big)), left_loc)
2038 return
2039
2040 # This could be an object, eggex object, etc. It won't be
2041 # BashAssoc shouldn because we query IsBashAssoc before evaluating
2042 # sh_lhs. Could conslidate with s[i] case above
2043 e_die(
2044 "Value of type %s can't be indexed" % ui.ValType(cell.val),
2045 left_loc)
2046
2047 elif case(sh_lvalue_e.Keyed):
2048 lval = cast(sh_lvalue.Keyed, UP_lval)
2049
2050 # There is no syntax 'declare A["x"]'
2051 assert val is not None, val
2052 assert val.tag() == value_e.Str, val
2053 rval = cast(value.Str, val)
2054
2055 left_loc = lval.blame_loc
2056
2057 cell, var_frame, _ = self._ResolveNameOrRef(
2058 lval.name, which_scopes)
2059 if cell.readonly:
2060 e_die("Can't assign to readonly associative array",
2061 left_loc)
2062
2063 # We already looked it up before making the sh_lvalue
2064 assert cell.val.tag() == value_e.BashAssoc, cell
2065 cell_val2 = cast(value.BashAssoc, cell.val)
2066 bash_impl.BashAssoc_SetElement(cell_val2, lval.key, rval.s)
2067
2068 else:
2069 raise AssertionError(lval.tag())
2070
2071 def _BindNewArrayWithEntry(self, var_frame, lval, val, flags):
2072 # type: (Dict[str, Cell], sh_lvalue.Indexed, value.Str, int) -> None
2073 """Fill 'var_frame' with a new indexed array entry."""
2074 no_str = None # type: Optional[str]
2075 items = [no_str] * lval.index
2076 items.append(val.s)
2077 new_value = value.BashArray(items)
2078
2079 # arrays can't be exported; can't have BashAssoc flag
2080 readonly = bool(flags & SetReadOnly)
2081 var_frame[lval.name] = Cell(False, readonly, False, new_value)
2082
2083 def InternalSetGlobal(self, name, new_val):
2084 # type: (str, value_t) -> None
2085 """For setting read-only globals internally.
2086
2087 Args:
2088 name: string (not Lhs)
2089 new_val: value
2090
2091 The variable must already exist.
2092
2093 Use case: SHELLOPTS.
2094 """
2095 cell = self.var_stack[0][name]
2096 cell.val = new_val
2097
2098 def GetValue(self, name, which_scopes=scope_e.Shopt):
2099 # type: (str, scope_t) -> value_t
2100 """Used by the WordEvaluator, ArithEvaluator, ExprEvaluator, etc."""
2101 assert isinstance(name, str), name
2102
2103 if which_scopes == scope_e.Shopt:
2104 which_scopes = self.ScopesForReading()
2105
2106 with str_switch(name) as case:
2107 # "Registers"
2108 if case('_status'): # deprecated in favor of _error.code
2109 return num.ToBig(self.TryStatus())
2110
2111 elif case('_error'):
2112 return self.TryError()
2113
2114 elif case('_this_dir'):
2115 if len(self.this_dir) == 0:
2116 # e.g. osh -c '' doesn't have it set
2117 # Should we give a custom error here?
2118 # If you're at the interactive shell, 'source mymodule.ysh' will still
2119 # work because 'source' sets it.
2120 return value.Undef
2121 else:
2122 return value.Str(self.this_dir[-1]) # top of stack
2123
2124 elif case('PIPESTATUS'):
2125 strs2 = [str(i)
2126 for i in self.pipe_status[-1]] # type: List[str]
2127 return value.BashArray(strs2)
2128
2129 elif case('_pipeline_status'):
2130 items = [num.ToBig(i)
2131 for i in self.pipe_status[-1]] # type: List[value_t]
2132 return value.List(items)
2133
2134 elif case('_process_sub_status'): # YSH naming convention
2135 items = [num.ToBig(i) for i in self.process_sub_status[-1]]
2136 return value.List(items)
2137
2138 elif case('BASH_REMATCH'):
2139 top_match = self.regex_match[-1]
2140 with tagswitch(top_match) as case2:
2141 if case2(regex_match_e.No):
2142 groups = [] # type: List[str]
2143 elif case2(regex_match_e.Yes):
2144 m = cast(RegexMatch, top_match)
2145 groups = util.RegexGroupStrings(m.s, m.indices)
2146 return value.BashArray(groups)
2147
2148 # Do lookup of system globals before looking at user variables. Note: we
2149 # could optimize this at compile-time like $?. That would break
2150 # ${!varref}, but it's already broken for $?.
2151
2152 elif case('FUNCNAME'):
2153 # bash wants it in reverse order. This is a little inefficient but we're
2154 # not depending on deque().
2155 strs = [] # type: List[str]
2156 for frame in reversed(self.debug_stack):
2157 UP_frame = frame
2158 with tagswitch(frame) as case2:
2159 if case2(debug_frame_e.ProcLike):
2160 frame = cast(debug_frame.ProcLike, UP_frame)
2161 strs.append(frame.proc_name)
2162
2163 elif case2(debug_frame_e.Source):
2164 # bash doesn't tell you the filename sourced
2165 strs.append('source')
2166
2167 elif case2(debug_frame_e.MainFile):
2168 strs.append('main') # also bash behavior
2169
2170 else: # ignore
2171 pass
2172
2173 return value.BashArray(strs) # TODO: Reuse this object too?
2174
2175 # $BASH_SOURCE and $BASH_LINENO have OFF BY ONE design bugs:
2176 #
2177 # ${BASH_LINENO[$i]} is the line number in the source file
2178 # (${BASH_SOURCE[$i+1]}) where ${FUNCNAME[$i]} was called (or
2179 # ${BASH_LINENO[$i-1]} if referenced within another shell function).
2180 #
2181 # https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
2182
2183 elif case('BASH_SOURCE'):
2184 strs = []
2185 for frame in reversed(self.debug_stack):
2186 UP_frame = frame
2187 with tagswitch(frame) as case2:
2188 if case2(debug_frame_e.ProcLike):
2189 frame = cast(debug_frame.ProcLike, UP_frame)
2190
2191 # Weird bash behavior
2192 assert frame.def_tok.line is not None
2193 source_str = ui.GetLineSourceString(
2194 frame.def_tok.line)
2195 strs.append(source_str)
2196
2197 elif case2(debug_frame_e.Source):
2198 frame = cast(debug_frame.Source, UP_frame)
2199 # Is this right?
2200 strs.append(frame.source_name)
2201
2202 elif case2(debug_frame_e.MainFile):
2203 frame = cast(debug_frame.MainFile, UP_frame)
2204 strs.append(frame.main_filename)
2205
2206 else: # ignore
2207 pass
2208
2209 return value.BashArray(strs) # TODO: Reuse this object too?
2210
2211 elif case('BASH_LINENO'):
2212 strs = []
2213 for frame in reversed(self.debug_stack):
2214 UP_frame = frame
2215 with tagswitch(frame) as case2:
2216 if case2(debug_frame_e.ProcLike):
2217 frame = cast(debug_frame.ProcLike, UP_frame)
2218 invoke_token = location.LeftTokenForCompoundWord(
2219 frame.invoke_loc)
2220 assert invoke_token is not None, frame.invoke_loc
2221 strs.append(_LineNumber(invoke_token))
2222
2223 elif case2(debug_frame_e.Source):
2224 frame = cast(debug_frame.Source, UP_frame)
2225 invoke_token = location.LeftTokenForCompoundWord(
2226 frame.source_loc)
2227 assert invoke_token is not None, frame.source_loc
2228 strs.append(_LineNumber(invoke_token))
2229
2230 elif case2(debug_frame_e.MainFile):
2231 # Bash does this to line up with 'main'
2232 strs.append('0')
2233
2234 else: # ignore
2235 pass
2236
2237 return value.BashArray(strs) # TODO: Reuse this object too?
2238
2239 elif case('LINENO'):
2240 assert self.token_for_line is not None
2241 # Reuse object with mutation
2242 # TODO: maybe use interned GetLineNumStr?
2243 self.line_num.s = str(self.token_for_line.line.line_num)
2244 return self.line_num
2245
2246 elif case('BASHPID'): # TODO: YSH io->getpid()
2247 return value.Str(str(posix.getpid()))
2248
2249 elif case('_'):
2250 return value.Str(self.last_arg)
2251
2252 elif case('SECONDS'):
2253 f = time_.time() - self.seconds_start
2254 ok, big_int = mops.FromFloat(f)
2255 assert ok, f # should never be NAN or INFINITY
2256 return value.Int(big_int)
2257
2258 else:
2259 # In the case 'declare -n ref='a[42]', the result won't be a cell. Idea to
2260 # fix this:
2261 # 1. Call self.unsafe_arith.ParseVarRef() -> BracedVarSub
2262 # 2. Call self.unsafe_arith.GetNameref(bvs_part), and get a value_t
2263 # We still need a ref_trail to detect cycles.
2264 cell, _, _ = self._ResolveNameOrRef(name, which_scopes)
2265 if cell:
2266 return cell.val
2267
2268 builtin_val = self.builtins.get(name)
2269 if builtin_val:
2270 return builtin_val
2271
2272 # TODO: Can look in the builtins module, which is a value.Obj
2273 return value.Undef
2274
2275 def GetCell(self, name, which_scopes=scope_e.Shopt):
2276 # type: (str, scope_t) -> Cell
2277 """Get both the value and flags.
2278
2279 Usages:
2280 - the 'pp' builtin.
2281 - declare -p
2282 - ${x@a}
2283 - to test of 'TZ' is exported in printf? Why?
2284
2285 Note: consulting __builtins__ doesn't see necessary for any of these
2286 """
2287 if which_scopes == scope_e.Shopt:
2288 which_scopes = self.ScopesForReading()
2289
2290 cell, _ = self._ResolveNameOnly(name, which_scopes)
2291 return cell
2292
2293 def Unset(self, lval, which_scopes):
2294 # type: (sh_lvalue_t, scope_t) -> bool
2295 """
2296 Returns:
2297 Whether the cell was found.
2298 """
2299 # TODO: Refactor sh_lvalue type to avoid this
2300 UP_lval = lval
2301
2302 with tagswitch(lval) as case:
2303 if case(sh_lvalue_e.Var): # unset x
2304 lval = cast(LeftName, UP_lval)
2305 var_name = lval.name
2306 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2307 lval = cast(sh_lvalue.Indexed, UP_lval)
2308 var_name = lval.name
2309 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2310 lval = cast(sh_lvalue.Keyed, UP_lval)
2311 var_name = lval.name
2312 else:
2313 raise AssertionError()
2314
2315 if which_scopes == scope_e.Shopt:
2316 which_scopes = self.ScopesForWriting()
2317
2318 cell, var_frame, cell_name = self._ResolveNameOrRef(
2319 var_name, which_scopes)
2320 if not cell:
2321 return False # 'unset' builtin falls back on functions
2322 if cell.readonly:
2323 raise error.Runtime("Can't unset readonly variable %r" % var_name)
2324
2325 with tagswitch(lval) as case:
2326 if case(sh_lvalue_e.Var): # unset x
2327 # Make variables in higher scopes visible.
2328 # example: test/spec.sh builtin-vars -r 24 (ble.sh)
2329 mylib.dict_erase(var_frame, cell_name)
2330
2331 # alternative that some shells use:
2332 # var_frame[cell_name].val = value.Undef
2333 # cell.exported = False
2334
2335 # This should never happen because we do recursive lookups of namerefs.
2336 assert not cell.nameref, cell
2337
2338 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2339 lval = cast(sh_lvalue.Indexed, UP_lval)
2340 # Note: Setting an entry to None and shifting entries are pretty
2341 # much the same in shell.
2342
2343 val = cell.val
2344 UP_val = val
2345 if val.tag() == value_e.BashArray:
2346 val = cast(value.BashArray, UP_val)
2347 error_code = bash_impl.BashArray_UnsetElement(
2348 val, lval.index)
2349 if error_code == error_code_e.IndexOutOfRange:
2350 n = bash_impl.BashArray_Length(val)
2351 raise error.Runtime(
2352 "%s[%d]: Index is out of bounds for array of length %d"
2353 % (var_name, lval.index, n))
2354 elif val.tag() == value_e.SparseArray:
2355 val = cast(value.SparseArray, UP_val)
2356 error_code = bash_impl.SparseArray_UnsetElement(
2357 val, mops.IntWiden(lval.index))
2358 if error_code == error_code_e.IndexOutOfRange:
2359 big_length = bash_impl.SparseArray_Length(val)
2360 raise error.Runtime(
2361 "%s[%d]: Index is out of bounds for array of length %s"
2362 % (var_name, lval.index, mops.ToStr(big_length)))
2363 else:
2364 raise error.Runtime("%r isn't an array" % var_name)
2365
2366 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2367 lval = cast(sh_lvalue.Keyed, UP_lval)
2368
2369 val = cell.val
2370 UP_val = val
2371
2372 # note: never happens because of mem.IsBashAssoc test for sh_lvalue.Keyed
2373 #if val.tag() != value_e.BashAssoc:
2374 # raise error.Runtime("%r isn't an associative array" % lval.name)
2375
2376 val = cast(value.BashAssoc, UP_val)
2377 bash_impl.BashAssoc_UnsetElement(val, lval.key)
2378
2379 else:
2380 raise AssertionError(lval)
2381
2382 return True
2383
2384 def ScopesForReading(self):
2385 # type: () -> scope_t
2386 """Read scope."""
2387 return (scope_e.Dynamic
2388 if self.exec_opts.dynamic_scope() else scope_e.LocalOrGlobal)
2389
2390 def ScopesForWriting(self):
2391 # type: () -> scope_t
2392 """Write scope."""
2393 return (scope_e.Dynamic
2394 if self.exec_opts.dynamic_scope() else scope_e.LocalOnly)
2395
2396 def ClearFlag(self, name, flag):
2397 # type: (str, int) -> bool
2398 """Used for export -n.
2399
2400 We don't use SetValue() because even if rval is None, it will make an
2401 Undef value in a scope.
2402 """
2403 cell, var_frame = self._ResolveNameOnly(name, self.ScopesForReading())
2404 if cell:
2405 if flag & ClearExport:
2406 cell.exported = False
2407 if flag & ClearNameref:
2408 cell.nameref = False
2409 return True
2410 else:
2411 return False
2412
2413 def _FillWithExported(self, new_env):
2414 # type: (Dict[str, str]) -> None
2415
2416 # Search from globals up. Names higher on the stack will overwrite
2417 # names lower on the stack.
2418 for scope in self.var_stack:
2419 for name, cell in iteritems(scope):
2420 if cell.exported and cell.val.tag() == value_e.Str:
2421 val = cast(value.Str, cell.val)
2422 new_env[name] = val.s
2423
2424 def _FillEnvObj(self, new_env, env_object):
2425 # type: (Dict[str, str], Obj) -> None
2426
2427 # Do the LEAST visible parts first
2428 if env_object.prototype is not None:
2429 self._FillEnvObj(new_env, env_object.prototype)
2430
2431 # Overwrite with MOST visible parts
2432 for name, val in iteritems(env_object.d):
2433 if val.tag() != value_e.Str:
2434 continue
2435 new_env[name] = cast(value.Str, val).s
2436
2437 def GetEnv(self):
2438 # type: () -> Dict[str, str]
2439 """
2440 Get the environment that should be used for launching processes.
2441
2442 Note: This is run on every SimpleCommand. Should we have a dirty
2443 flag? We could notice these things:
2444
2445 - If an exported variable is changed
2446 - If the set of exported variables changes.
2447 """
2448 new_env = {} # type: Dict[str, str]
2449
2450 # Note: ysh:upgrade has both of these behaviors
2451
2452 # OSH: Consult exported vars
2453 if not self.exec_opts.no_exported():
2454 self._FillWithExported(new_env)
2455
2456 # YSH: Consult the ENV dict
2457 if self.exec_opts.env_obj():
2458 self._FillEnvObj(new_env, self.env_object)
2459
2460 return new_env
2461
2462 def VarNames(self):
2463 # type: () -> List[str]
2464 """For internal OSH completion and compgen -A variable.
2465
2466 NOTE: We could also add $? $$ etc.?
2467 """
2468 ret = [] # type: List[str]
2469 # Look up the stack, yielding all variables. Bash seems to do this.
2470 for scope in self.var_stack:
2471 for name in scope:
2472 ret.append(name)
2473 return ret
2474
2475 def VarNamesStartingWith(self, prefix):
2476 # type: (str) -> List[str]
2477 """For ${!prefix@}"""
2478 # Look up the stack, yielding all variables. Bash seems to do this.
2479 names = [] # type: List[str]
2480 for scope in self.var_stack:
2481 for name in scope:
2482 if name.startswith(prefix):
2483 names.append(name)
2484 return names
2485
2486 def GetAllVars(self):
2487 # type: () -> Dict[str, str]
2488 """Get all variables and their values, for 'set' builtin."""
2489 result = {} # type: Dict[str, str]
2490 for scope in self.var_stack:
2491 for name, cell in iteritems(scope):
2492 # TODO: Show other types?
2493 val = cell.val
2494 if val.tag() == value_e.Str:
2495 str_val = cast(value.Str, val)
2496 result[name] = str_val.s
2497 return result
2498
2499 def GetAllCells(self, which_scopes):
2500 # type: (scope_t) -> Dict[str, Cell]
2501 """Get all variables and their values, for 'set' builtin."""
2502 result = {} # type: Dict[str, Cell]
2503
2504 if which_scopes == scope_e.Dynamic:
2505 scopes = self.var_stack
2506 elif which_scopes == scope_e.LocalOnly:
2507 scopes = self.var_stack[-1:]
2508 elif which_scopes == scope_e.GlobalOnly:
2509 scopes = self.var_stack[0:1]
2510 elif which_scopes == scope_e.LocalOrGlobal:
2511 scopes = [self.var_stack[0]]
2512 if len(self.var_stack) > 1:
2513 scopes.append(self.var_stack[-1])
2514 else:
2515 raise AssertionError()
2516
2517 for scope in scopes:
2518 for name, cell in iteritems(scope):
2519 result[name] = cell
2520 return result
2521
2522 def SetRegexMatch(self, match):
2523 # type: (regex_match_t) -> None
2524 self.regex_match[-1] = match
2525
2526 def GetRegexMatch(self):
2527 # type: () -> regex_match_t
2528 return self.regex_match[-1]
2529
2530 def PushContextStack(self, context):
2531 # type: (Dict[str, value_t]) -> None
2532 self.ctx_stack.append(context)
2533
2534 def GetContext(self):
2535 # type: () -> Optional[Dict[str, value_t]]
2536 if len(self.ctx_stack):
2537 return self.ctx_stack[-1]
2538 return None
2539
2540 def PopContextStack(self):
2541 # type: () -> Dict[str, value_t]
2542 assert self.ctx_stack, "Empty context stack"
2543 return self.ctx_stack.pop()
2544
2545
2546def ValueIsInvokableObj(val):
2547 # type: (value_t) -> Tuple[Optional[value_t], Optional[Obj]]
2548 """
2549 Returns:
2550 (__invoke__ Proc or BuiltinProc, self Obj) if the value is invokable
2551 (None, None) otherwise
2552 """
2553 if val.tag() != value_e.Obj:
2554 return None, None
2555
2556 obj = cast(Obj, val)
2557 if not obj.prototype:
2558 return None, None
2559
2560 invoke_val = obj.prototype.d.get('__invoke__')
2561 if invoke_val is None:
2562 return None, None
2563
2564 # TODO: __invoke__ of wrong type could be fatal error?
2565 if invoke_val.tag() in (value_e.Proc, value_e.BuiltinProc):
2566 return invoke_val, obj
2567
2568 return None, None
2569
2570
2571def _AddNames(unique, frame):
2572 # type: (Dict[str, bool], Dict[str, Cell]) -> None
2573 for name in frame:
2574 val = frame[name].val
2575 if val.tag() == value_e.Proc:
2576 unique[name] = True
2577 proc, _ = ValueIsInvokableObj(val)
2578 if proc is not None:
2579 unique[name] = True
2580
2581
2582class Procs(object):
2583 """
2584 Terminology:
2585
2586 - invokable - these are INTERIOR
2587 - value.Proc - which can be shell function in __sh_funcs__ namespace, or
2588 YSH proc
2589 - value.Obj with __invoke__
2590 - exterior - external commands, extern builtin
2591
2592 Note: the YSH 'invoke' builtin can generalize YSH 'runproc' builtin, shell command/builtin,
2593 and also type / type -a
2594 """
2595
2596 def __init__(self, mem):
2597 # type: (Mem) -> None
2598 self.mem = mem
2599 self.sh_funcs = {} # type: Dict[str, value.Proc]
2600
2601 def DefineShellFunc(self, name, proc):
2602 # type: (str, value.Proc) -> None
2603 self.sh_funcs[name] = proc
2604
2605 def IsShellFunc(self, name):
2606 # type: (str) -> bool
2607 return name in self.sh_funcs
2608
2609 def GetShellFunc(self, name):
2610 # type: (str) -> Optional[value.Proc]
2611 return self.sh_funcs.get(name)
2612
2613 def EraseShellFunc(self, to_del):
2614 # type: (str) -> None
2615 """Undefine a sh-func with name `to_del`, if it exists."""
2616 mylib.dict_erase(self.sh_funcs, to_del)
2617
2618 def ShellFuncNames(self):
2619 # type: () -> List[str]
2620 """Returns a *sorted* list of all shell function names
2621
2622 Callers:
2623 declare -f -F
2624 """
2625 names = self.sh_funcs.keys()
2626 names.sort()
2627 return names
2628
2629 def DefineProc(self, name, proc):
2630 # type: (str, value.Proc) -> None
2631 """
2632 procs are defined in the local scope.
2633 """
2634 self.mem.var_stack[-1][name] = Cell(False, False, False, proc)
2635
2636 def IsProc(self, name):
2637 # type: (str) -> bool
2638
2639 maybe_proc = self.mem.GetValue(name)
2640 # Could be Undef
2641 return maybe_proc.tag() == value_e.Proc
2642
2643 def IsInvokableObj(self, name):
2644 # type: (str) -> bool
2645
2646 val = self.mem.GetValue(name)
2647 proc, _ = ValueIsInvokableObj(val)
2648 return proc is not None
2649
2650 def InvokableNames(self):
2651 # type: () -> List[str]
2652 """Returns a *sorted* list of all invokable names
2653
2654 Callers:
2655 complete -A function
2656 pp proc - should deprecate this
2657 """
2658 unique = NewDict() # type: Dict[str, bool]
2659 for name in self.sh_funcs:
2660 unique[name] = True
2661
2662 top_frame = self.mem.var_stack[-1]
2663 _AddNames(unique, top_frame)
2664
2665 global_frame = self.mem.var_stack[0]
2666 #log('%d %d', id(top_frame), id(global_frame))
2667 if global_frame is not top_frame:
2668 _AddNames(unique, global_frame)
2669
2670 #log('%s', unique)
2671
2672 names = unique.keys()
2673 names.sort()
2674
2675 return names
2676
2677 def GetInvokable(self, name):
2678 # type: (str) -> Tuple[Optional[value_t], Optional[Obj]]
2679 """Find a proc, invokable Obj, or sh-func, in that order
2680
2681 Callers:
2682 executor.py: to actually run
2683 meta_oils.py runproc lookup - this is not 'invoke', because it is
2684 INTERIOR shell functions, procs, invokable Obj
2685 """
2686 val = self.mem.GetValue(name)
2687
2688 if val.tag() == value_e.Proc:
2689 return cast(value.Proc, val), None
2690
2691 proc, self_val = ValueIsInvokableObj(val)
2692 if proc:
2693 return proc, self_val
2694
2695 if name in self.sh_funcs:
2696 return self.sh_funcs[name], None
2697
2698 return None, None
2699
2700
2701#
2702# Wrappers to Set Variables
2703#
2704
2705
2706def OshLanguageSetValue(mem, lval, val, flags=0):
2707 # type: (Mem, sh_lvalue_t, value_t, int) -> None
2708 """Like 'setvar' (scope_e.LocalOnly), unless dynamic scope is on.
2709
2710 That is, it respects shopt --unset dynamic_scope.
2711
2712 Used for assignment builtins, (( a = b )), {fd}>out, ${x=}, etc.
2713 """
2714 which_scopes = mem.ScopesForWriting()
2715 mem.SetValue(lval, val, which_scopes, flags=flags)
2716
2717
2718def BuiltinSetValue(mem, lval, val):
2719 # type: (Mem, sh_lvalue_t, value_t) -> None
2720 """Equivalent of x=$y
2721
2722 Called by BuiltinSetString and BuiltinSetArray Used directly by
2723 printf -v because it can mutate an array
2724 """
2725 mem.SetValue(lval, val, mem.ScopesForWriting())
2726
2727
2728def BuiltinSetString(mem, name, s):
2729 # type: (Mem, str, str) -> None
2730 """Set a string by looking up the stack.
2731
2732 Used for 'read', 'getopts', completion builtins, etc.
2733 """
2734 assert isinstance(s, str)
2735 BuiltinSetValue(mem, location.LName(name), value.Str(s))
2736
2737
2738def BuiltinSetArray(mem, name, a):
2739 # type: (Mem, str, List[str]) -> None
2740 """Set an array by looking up the stack.
2741
2742 Used by compadjust, read -a, etc.
2743 """
2744 assert isinstance(a, list)
2745 BuiltinSetValue(mem, location.LName(name), value.BashArray(a))
2746
2747
2748def SetGlobalString(mem, name, s):
2749 # type: (Mem, str, str) -> None
2750 """Helper for completion, etc."""
2751 assert isinstance(s, str)
2752 val = value.Str(s)
2753 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2754
2755
2756def SetGlobalArray(mem, name, a):
2757 # type: (Mem, str, List[str]) -> None
2758 """Used by completion, shell initialization, etc."""
2759 assert isinstance(a, list)
2760 mem.SetNamed(location.LName(name), value.BashArray(a), scope_e.GlobalOnly)
2761
2762
2763def SetGlobalValue(mem, name, val):
2764 # type: (Mem, str, value_t) -> None
2765 """Helper for completion, etc."""
2766 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2767
2768
2769def SetLocalValue(mem, name, val):
2770 # type: (Mem, str, value_t) -> None
2771 """For 'use' builtin."""
2772 mem.SetNamed(location.LName(name), val, scope_e.LocalOnly)
2773
2774
2775def ExportGlobalString(mem, name, s):
2776 # type: (Mem, str, str) -> None
2777 """Helper for completion, $PWD, $OLDPWD, etc."""
2778 assert isinstance(s, str)
2779 val = value.Str(s)
2780 mem.SetNamed(location.LName(name),
2781 val,
2782 scope_e.GlobalOnly,
2783 flags=SetExport)
2784
2785
2786# TODO: remove in favor of EnvConfig
2787def SetStringInEnv(mem, var_name, s):
2788 # type: (Mem, str, str) -> None
2789 if mem.exec_opts.env_obj(): # e.g. ENV.YSH_HISTFILE
2790 mem.env_dict[var_name] = value.Str(s)
2791 else: # e.g. $YSH_HISTFILE
2792 SetGlobalString(mem, var_name, s)
2793
2794
2795#
2796# Wrappers to Get Variables
2797#
2798
2799
2800def DynamicGetVar(mem, name, which_scopes):
2801 # type: (Mem, str, scope_t) -> value_t
2802 """
2803 For getVar() and shvarGet()
2804 """
2805 val = mem.GetValue(name, which_scopes=which_scopes)
2806
2807 # Undef is not a user-visible value!
2808 # There's no way to distinguish null from undefined.
2809 if val.tag() == value_e.Undef:
2810 return value.Null
2811
2812 return val
2813
2814
2815def GetString(mem, name):
2816 # type: (Mem, str) -> str
2817 """Wrapper around GetValue().
2818
2819 Check that HOME, PWD, OLDPWD, etc. are strings. bash doesn't have these
2820 errors because ${array} is ${array[0]}.
2821
2822 TODO: We could also check this when you're storing variables?
2823 """
2824 val = mem.GetValue(name)
2825 UP_val = val
2826 with tagswitch(val) as case:
2827 if case(value_e.Undef):
2828 raise error.Runtime("$%s isn't defined" % name)
2829 elif case(value_e.Str):
2830 return cast(value.Str, UP_val).s
2831 else:
2832 # User would have to 'unset HOME' to get rid of exported flag
2833 raise error.Runtime("$%s should be a string" % name)
2834
2835
2836def MaybeString(mem, name):
2837 # type: (Mem, str) -> Optional[str]
2838 """Like GetString(), but doesn't throw an exception."""
2839 try:
2840 return GetString(mem, name)
2841 except error.Runtime:
2842 return None
2843
2844
2845def GetInteger(mem, name):
2846 # type: (Mem, str) -> int
2847 """For OPTIND variable used in getopts builtin.
2848
2849 TODO: it could be value.Int() ?
2850 """
2851 val = mem.GetValue(name)
2852 if val.tag() != value_e.Str:
2853 raise error.Runtime('$%s should be a string, got %s' %
2854 (name, ui.ValType(val)))
2855 s = cast(value.Str, val).s
2856 try:
2857 i = int(s)
2858 except ValueError:
2859 raise error.Runtime("$%s doesn't look like an integer, got %r" %
2860 (name, s))
2861 return i
2862
2863
2864# vim: sw=4