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

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