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

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