OILS / core / state.py View on Github | oilshell.org

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