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

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