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

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