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

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