def EvalArithLhs(self, anode, span_id): # type: (arith_expr_t, int) -> lvalue_t """ For (( a[x] = 1 )) etc. """ UP_anode = anode if anode.tag_() == arith_expr_e.Binary: anode = cast(arith_expr__Binary, UP_anode) if anode.op_id == Id.Arith_LBracket: var_name, span_id = self._VarRefOrWord(anode.left) if var_name is not None: if self.mem.IsAssocArray(var_name, scope_e.Dynamic): key = self.EvalWordToString(anode.right) lval2 = lvalue.Keyed(var_name, key) lval2.spids.append(span_id) lval = lval2 # type: lvalue_t return lval else: index = self.EvalToInt(anode.right) lval3 = lvalue.Indexed(var_name, index) lval3.spids.append(span_id) lval = lval3 return lval var_name, span_id = self._VarRefOrWord(anode) if var_name is not None: lval1 = lvalue.Named(var_name) lval1.spids.append(span_id) lval = lval1 return lval # e.g. unset 'x-y'. status 2 for runtime parse error e_die('Invalid place to modify', span_id=span_id, status=2)
def Replace(self, s, op): # type: (str, suffix_op__PatSub) -> str regex = '(%s)' % self.regex # make it a group if op.replace_mode == Id.Lit_Slash: try: return _PatSubAll(s, regex, self.replace_str) # loop over matches except RuntimeError as e: # libc.regex_first_group_match raises RuntimeError. # TODO: We want errors with e_die('Error matching regex %r: %s', regex, e, span_id=self.slash_spid) if op.replace_mode == Id.Lit_Pound: regex = '^' + regex elif op.replace_mode == Id.Lit_Percent: regex = regex + '$' m = libc.regex_first_group_match(regex, s, 0) #log('regex = %r, s = %r, match = %r', regex, s, m) if m is None: return s start, end = m return s[:start] + self.replace_str + s[end:]
def _LookupVar(name, mem, exec_opts): val = mem.GetVar(name) # By default, undefined variables are the ZERO value. TODO: Respect # nounset and raise an exception. if val.tag == value_e.Undef and exec_opts.nounset: e_die('Undefined variable %r', name) # TODO: need token return val
def _MakeProcess(self, node, job_state=None, disable_errexit=False): """ Assume we will run the node in another process. Return a process. """ if node.tag == command_e.ControlFlow: # Pipeline or subshells with control flow are invalid, e.g.: # - break | less # - continue | less # - ( return ) # NOTE: This could be done at parse time too. e_die('Invalid control flow %r in pipeline / subshell / background', node.token.val, token=node.token) # NOTE: If ErrExit(), we could be verbose about subprogram errors? This # only really matters when executing 'exit 42', because the child shell # inherits errexit and will be verbose. Other notes: # # - We might want errors to fit on a single line so they don't get # interleaved. # - We could turn the `exit` builtin into a FatalRuntimeError exception and # get this check for "free". thunk = process.SubProgramThunk(self, node, disable_errexit=disable_errexit) p = process.Process(thunk, job_state=job_state) return p
def _ValToArith(self, val, span_id): """Convert value_t to a Python int or list of strings.""" assert isinstance(val, value_t), '%r %r' % (val, type(val)) if val.tag == value_e.Undef: # 'nounset' already handled before got here # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42. #log('blame_word %s arena %s', blame_word, self.arena) e_die('Undefined value in arithmetic context', span_id=span_id) return 0 if val.tag == value_e.Str: return _StringToInteger(val.s, span_id=span_id) # calls e_die if val.tag == value_e.MaybeStrArray: # array is valid on RHS, but not LHS return val.strs if val.tag == value_e.AssocArray: return val.d if val.tag == value_e.Obj: if isinstance(val.obj, int): return val.obj # NOTE: This doesn't happen because we convert it. #elif isinstance(val.obj, str): # return _StringToInteger(val.obj, span_id=span_id) # calls e_die else: e_die("Object %r can't be used in shell arirhmetic", val.obj) raise AssertionError(val)
def _ValToArith(self, val, span_id, int_coerce=True): """Convert value_t to a Python int or list of strings.""" assert isinstance(val, value_t), '%r %r' % (val, type(val)) if int_coerce: if val.tag == value_e.Undef: # 'nounset' already handled before got here # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42. #log('blame_word %s arena %s', blame_word, self.arena) e_die('Coercing undefined value to 0 in arithmetic context', span_id=span_id) return 0 if val.tag == value_e.Str: # may raise FatalRuntimeError return _StringToInteger(val.s, span_id=span_id) if val.tag == value_e.StrArray: # array is valid on RHS, but not on left return val.strs if val.tag == value_e.AssocArray: return val.d raise AssertionError(val) if val.tag == value_e.Undef: # 'nounset' already handled before got here return '' # I think nounset is handled elsewhere if val.tag == value_e.Str: return val.s if val.tag == value_e.StrArray: # array is valid on RHS, but not on left return val.strs if val.tag == value_e.AssocArray: return val.d
def isatty(fd, s, blame_word): # type: (int, str, word_t) -> bool try: return posix.isatty(fd) # fd is user input, and causes this exception in the binding. except OverflowError: e_die('File descriptor %r is too big', s, word=blame_word)
def _MakeProcess(self, node, parent_pipeline=None, inherit_errexit=True): # type: (command_t, process.Pipeline, bool) -> process.Process """ Assume we will run the node in another process. Return a process. """ UP_node = node if node.tag_() == command_e.ControlFlow: node = cast(command__ControlFlow, UP_node) # Pipeline or subshells with control flow are invalid, e.g.: # - break | less # - continue | less # - ( return ) # NOTE: This could be done at parse time too. if node.token.id != Id.ControlFlow_Exit: e_die( 'Invalid control flow %r in pipeline / subshell / background', node.token.val, token=node.token) # NOTE: If ErrExit(), we could be verbose about subprogram errors? This # only really matters when executing 'exit 42', because the child shell # inherits errexit and will be verbose. Other notes: # # - We might want errors to fit on a single line so they don't get # interleaved. # - We could turn the `exit` builtin into a FatalRuntimeError exception and # get this check for "free". thunk = process.SubProgramThunk(self.cmd_ev, node, inherit_errexit=inherit_errexit) p = process.Process(thunk, self.job_state, parent_pipeline=parent_pipeline) return p
def Set(self, b): """User code calls this.""" if True in self.stack: # are we in a temporary state? # TODO: Add error context. e_die("Can't set 'errexit' in a context where it's disabled " "(if, !, && ||, while/until conditions)") self.errexit = b
def _ValToIntOrError(self, val, blame_word=None, span_id=runtime.NO_SPID): # type: (value_t, word_t, int) -> int if span_id == runtime.NO_SPID and blame_word: span_id = word_.LeftMostSpanForWord(blame_word) #log('_ValToIntOrError span=%s blame=%s', span_id, blame_word) try: if val.tag == value_e.Undef: # 'nounset' already handled before got here # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42. #log('blame_word %s arena %s', blame_word, self.arena) e_die('Undefined value in arithmetic context', span_id=span_id) if val.tag == value_e.Int: return val.i if val.tag == value_e.Str: return _StringToInteger(val.s, span_id=span_id) # calls e_die except error.FatalRuntime as e: if self.exec_opts.strict_arith: raise else: span_id = word_.SpanIdFromError(e) self.errfmt.PrettyPrintError(e, prefix='warning: ') return 0 # Arrays and associative arrays always fail -- not controlled by strict_arith. # In bash, (( a )) is like (( a[0] )), but I don't want that. # And returning '0' gives different results. e_die("Expected a value convertible to integer, got %s", val, span_id=span_id)
def EvalWordToString(self, node): # type: (arith_expr_t) -> str """ Args: node: arith_expr_t Returns: str Raises: error.FatalRuntime if the expression isn't a string Or if it contains a bare variable like a[x] These are allowed because they're unambiguous, unlike a[x] a[$x] a["$x"] a["x"] a['x'] """ UP_node = node if node.tag_( ) == arith_expr_e.ArithWord: # $(( $x )) $(( ${x}${y} )), etc. node = cast(arith_expr__ArithWord, UP_node) val = self.word_ev.EvalWordToString(node.w) return val.s else: # TODO: location info for orginal e_die("Associative array keys must be strings: $x 'x' \"$x\" etc.")
def EvalPlusEquals(self, lval, rhs_py): lhs_py = self.LookupVar(lval.name) if not isinstance(lhs_py, (int, float)): # TODO: Could point at the variable name e_die("Object of type %r doesn't support +=", lhs_py.__class__.__name__) return lhs_py + rhs_py
def _MutateClassLiteral(self, node): # type: (re_t) -> None for i, term in enumerate(node.terms): s = None if term.tag == class_literal_term_e.SingleQuoted: s = word_eval.EvalSingleQuoted(term) spid = term.left.span_id elif term.tag == class_literal_term_e.DoubleQuoted: s = self.word_ev.EvalDoubleQuotedToString(term) spid = term.left.span_id elif term.tag == class_literal_term_e.BracedVarSub: s = self.word_ev.EvalBracedVarSubToString(term) spid = term.spids[0] elif term.tag == class_literal_term_e.SimpleVarSub: s = self.word_ev.EvalSimpleVarSubToString(term.token) spid = term.token.span_id elif term.tag == class_literal_term_e.CharLiteral: # What about \0? # At runtime, ERE should disallow it. But we can also disallow it here. node.terms[i] = word_compile.EvalCharLiteralForRegex(term.tok) if s is not None: # A string like '\x7f\xff' should be presented like if len(s) > 1: for c in s: if ord(c) > 128: e_die("Express these bytes as character literals to avoid " "confusing them with encoded characters", span_id=spid) node.terms[i] = class_literal_term.ByteSet(s, spid)
def _LookupVar(name, mem, exec_opts): # type: (str, Mem, optview.Exec) -> value_t val = mem.GetVar(name) # By default, undefined variables are the ZERO value. TODO: Respect # nounset and raise an exception. if val.tag_() == value_e.Undef and exec_opts.nounset(): e_die('Undefined variable %r', name) # TODO: need token return val
def EvalWordToString(self, word, do_fnmatch=False, do_ere=False): """ Args: word: CompoundWord Used for redirect arg, ControlFlow arg, ArithWord, BoolWord, etc. do_fnmatch is true for case $pat and RHS of [[ == ]]. pat="*.py" case $x in $pat) echo 'matches glob pattern' ;; "$pat") echo 'equal to glob string' ;; # must be glob escaped esac TODO: Raise AssertionError if it has ExtGlobPart. """ if word.tag == word_e.EmptyWord: return value.Str('') part_vals = [] for p in word.parts: self._EvalWordPart(p, part_vals, quoted=False) strs = [] for part_val in part_vals: if part_val.tag == part_value_e.String: # [[ foo == */"*".py ]] or case *.py) ... esac if do_fnmatch and not part_val.do_split_glob: s = glob_.GlobEscape(part_val.s) elif do_ere and not part_val.do_split_glob: s = glob_.ExtendedRegexEscape(part_val.s) else: s = part_val.s else: if self.exec_opts.strict_array: # Examples: echo f > "$@"; local foo="$@" # TODO: This attributes too coarsely, to the word rather than the # parts. Problem: the word is a TREE of parts, but we only have a # flat list of part_vals. The only case where we really get arrays # is "$@", "${a[@]}", "${a[@]//pat/replace}", etc. e_die( "This word should evaluate to a string, but part of it was an " "array", word=word) # TODO: Maybe add detail like this. #e_die('RHS of assignment should only have strings. ' # 'To assign arrays, use b=( "${a[@]}" )') else: # It appears to not respect IFS s = ' '.join(s for s in part_val.strs if s is not None) strs.append(s) return value.Str(''.join(strs))
def RunSimpleCommand(self, cmd_val, do_fork, call_procs=True): # type: (cmd_value__Argv, bool, bool) -> int argv = cmd_val.argv span_id = cmd_val.arg_spids[0] if cmd_val.arg_spids else runtime.NO_SPID arg0 = argv[0] builtin_id = consts.LookupSpecialBuiltin(arg0) if builtin_id != consts.NO_INDEX: return self.RunBuiltin(builtin_id, cmd_val) func_node = self.procs.get(arg0) if func_node is not None: if (self.exec_opts.strict_errexit() and self.mutable_opts.errexit.SpidIfDisabled() != runtime.NO_SPID): # NOTE: This would be checked below, but this gives a better error # message. e_die("can't disable errexit running a function. " "Maybe wrap the function in a process with the at-splice " "pattern.", span_id=span_id) # NOTE: Functions could call 'exit 42' directly, etc. status = self.cmd_ev.RunProc(func_node, argv[1:]) return status builtin_id = consts.LookupNormalBuiltin(arg0) if builtin_id != consts.NO_INDEX: return self.RunBuiltin(builtin_id, cmd_val) # See how many tests will pass if mylib.PYTHON: import subprocess try: status = subprocess.call(cmd_val.argv) except OSError as e: log('Error running %s: %s', cmd_val.argv, e) return 1 return status log('Unhandled SimpleCommand') f = mylib.Stdout() #ast_f = fmt.DetectConsoleOutput(f) # Stupid Eclipse debugger doesn't display ANSI ast_f = fmt.TextOutput(f) tree = cmd_val.PrettyTree() ast_f.FileHeader() fmt.PrintTree(tree, ast_f) ast_f.FileFooter() ast_f.write('\n') return 0
def RunSimpleCommand(self, argv, fork_external, span_id, funcs=True): """ Args: fork_external: for subshell ( ls / ) or ( command ls / ) """ # This happens when you write "$@" but have no arguments. if not argv: if self.exec_opts.strict_argv: e_die("Command evaluated to an empty argv array", span_id=span_id) else: return 0 # status 0, or skip it? arg0 = argv[0] builtin_id = builtin.ResolveSpecial(arg0) if builtin_id != builtin_e.NONE: try: status = self._RunBuiltin(builtin_id, argv, fork_external, span_id) except args.UsageError as e: ui.usage('osh %r usage error: %s', arg0, e) status = 2 # consistent error code for usage error return status # Builtins like 'true' can be redefined as functions. if funcs: func_node = self.funcs.get(arg0) if func_node is not None: # NOTE: Functions could call 'exit 42' directly, etc. status = self._RunFunc(func_node, argv[1:]) return status builtin_id = builtin.Resolve(arg0) if builtin_id != builtin_e.NONE: try: status = self._RunBuiltin(builtin_id, argv, fork_external, span_id) except args.UsageError as e: ui.usage('osh %r usage error: %s', arg0, e) status = 2 # consistent error code for usage error return status environ = self.mem.GetExported() # Include temporary variables if fork_external: thunk = process.ExternalThunk(self.ext_prog, argv, environ) p = process.Process(thunk) status = p.Run(self.waiter) return status self.ext_prog.Exec(argv, environ) # NEVER RETURNS
def _EmptyStrOrError(self, val, token=None): assert isinstance(val, value_t), val if val.tag == value_e.Undef: if self.exec_opts.nounset: if token is None: e_die('Undefined variable') else: name = token.val[1:] if token.val.startswith( '$') else token.val e_die('Undefined variable %r', name, token=token) else: return value.Str('') else: return val
def _CheckOilKeyword(self, keyword_id, lval, cell): """Check that 'var' and setvar/set are used correctly. NOTE: These are dynamic checks, but the syntactic difference between definition and mutation will help translate the Oil subset of OSH to static languages. """ if cell and keyword_id == Id.KW_Var: # TODO: Point at the ORIGINAL declaration! e_die("%r has already been declared", lval.name) if cell is None and keyword_id in (Id.KW_Set, Id.KW_SetVar): # NOTE: all 3 variants of 'lvalue' have 'name' e_die("%r hasn't been declared", lval.name)
def _UnsetVar(self, arg, spid, proc_fallback): # type: (str, int, bool) -> bool """ Returns: bool: whether the 'unset' builtin should succeed with code 0. """ arena = self.parse_ctx.arena a_parser = self.parse_ctx.MakeArithParser(arg) arena.PushSource(source.ArgvWord(spid)) try: anode = a_parser.Parse() except error.Parse as e: # show parse error ui.PrettyPrintError(e, arena) # point to word e_usage('Invalid unset expression', span_id=spid) finally: arena.PopSource() lval = self.arith_ev.EvalArithLhs(anode, spid) # Prevent attacks like these by default: # # unset -v 'A["$(echo K; rm *)"]' if not self.exec_opts.eval_unsafe_arith( ) and lval.tag_() != lvalue_e.Named: e_die( 'Expected a variable name. Expressions are allowed with shopt -s eval_unsafe_arith', span_id=spid) #log('lval %s', lval) found = False try: # not strict found = self.mem.Unset(lval, scope_e.Dynamic, False) except error.Runtime as e: # note: in bash, myreadonly=X fails, but declare myreadonly=X doens't # fail because it's a builtin. So I guess the same is true of 'unset'. e.span_id = spid ui.PrettyPrintError(e, arena) return False if proc_fallback and not found: if arg in self.funcs: del self.funcs[arg] return True
def _SetOption(self, opt_name, b): """Private version for synchronizing from SHELLOPTS.""" assert '_' not in opt_name if opt_name not in SET_OPTION_NAMES: raise args.UsageError('got invalid option %r' % opt_name) if opt_name == 'errexit': self.errexit.Set(b) elif opt_name in ('vi', 'emacs'): if self.readline: self.readline.parse_and_bind("set editing-mode " + opt_name); else: e_die("Can't set option %r because Oil wasn't built with the readline " "library.", opt_name) else: if opt_name == 'verbose' and b: log('Warning: set -o verbose not implemented') setattr(self, opt_name, b)
def _EvalLhsAndLookupArith(self, node): """ Args: node: lhs_expr Returns: int or list of strings, lvalue_t """ val, lval = EvalLhsAndLookup(node, self, self.mem, self.exec_opts) if val.tag == value_e.StrArray: e_die("Can't use assignment like ++ or += on arrays") # TODO: attribute a span ID here. There are a few cases, like UnaryAssign # and BinaryAssign. span_id = word.SpanForLhsExpr(node) i = self._ValToArithOrError(val, span_id=span_id) return i, lval
def _EvalLhsAndLookupArith(self, node): """ Args: node: sh_lhs_expr Returns: (Python object, lvalue_t) """ val, lval = EvalLhsAndLookup(node, self, self.mem, self.exec_opts) if val.tag == value_e.MaybeStrArray: e_die("Can't use assignment like ++ or += on arrays") # TODO: attribute a span ID here. There are a few cases, like UnaryAssign # and BinaryAssign. span_id = word_.SpanForLhsExpr(node) i = self._ValToIntOrError(val, span_id=span_id) return i, lval
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # NOTE: If first char is a colon, error reporting is different. Alpine # might not use that? spec_str = arg_r.ReadRequired('requires an argspec') var_name, var_spid = arg_r.ReadRequired2( 'requires the name of a variable to set') try: spec = self.spec_cache[spec_str] except KeyError: spec = _ParseOptSpec(spec_str) self.spec_cache[spec_str] = spec # These errors are fatal errors, not like the builtin exiting with code 1. # Because the invariants of the shell have been violated! v = self.mem.GetVar('OPTIND') if v.tag != value_e.Str: e_die('OPTIND should be a string, got %r', v) try: optind = int(v.s) except ValueError: e_die("OPTIND doesn't look like an integer, got %r", v.s) user_argv = arg_r.Rest() or self.mem.GetArgv() #util.log('user_argv %s', user_argv) status, opt_char, optarg, optind = _GetOpts(spec, user_argv, optind, self.errfmt) # Bug fix: bash-completion uses a *local* OPTIND ! Not global. state.SetStringDynamic(self.mem, 'OPTARG', optarg) state.SetStringDynamic(self.mem, 'OPTIND', str(optind)) if match.IsValidVarName(var_name): state.SetStringDynamic(self.mem, var_name, opt_char) else: # NOTE: The builtin has PARTIALLY filed. This happens in all shells # except mksh. raise error.Usage('got invalid variable name %r' % var_name, span_id=var_spid) return status
def LookupVar(self, var_name): """Convert to a Python object so we can calculate on it natively.""" # Lookup WITHOUT dynamic scope. val = self.mem.GetVar(var_name, lookup_mode=scope_e.LocalOnly) if val.tag == value_e.Undef: val = self.mem.GetVar(var_name, lookup_mode=scope_e.GlobalOnly) if val.tag == value_e.Undef: # TODO: Location info e_die('Undefined variable %r', var_name) if val.tag == value_e.Str: return val.s if val.tag == value_e.MaybeStrArray: return val.strs # node: has None if val.tag == value_e.AssocArray: return val.d if val.tag == value_e.Obj: return val.obj
def _ValToIntOrError(self, val, span_id=runtime.NO_SPID): # type: (value_t, int) -> int try: UP_val = val with tagswitch(val) as case: if case(value_e.Undef ): # 'nounset' already handled before got here # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42. #log('blame_word %s arena %s', blame_word, self.arena) e_die('Undefined value in arithmetic context', span_id=span_id) elif case(value_e.Int): val = cast(value__Int, UP_val) return val.i elif case(value_e.Str): val = cast(value__Str, UP_val) return _StringToInteger(val.s, span_id=span_id) # calls e_die elif case(value_e.Obj): # Note: this handles var x = 42; echo $(( x > 2 )). if mylib.PYTHON: val = cast(value__Obj, UP_val) if isinstance(val.obj, int): return val.obj raise AssertionError() # not in C++ except error.FatalRuntime as e: if self.exec_opts.strict_arith(): raise else: span_id = word_.SpanIdFromError(e) self.errfmt.PrettyPrintError(e, prefix='warning: ') return 0 # Arrays and associative arrays always fail -- not controlled by strict_arith. # In bash, (( a )) is like (( a[0] )), but I don't want that. # And returning '0' gives different results. e_die("Expected a value convertible to integer, got %s", ui.ValType(val), span_id=span_id)
def GetOpts(argv, mem): """ Vars to set: OPTIND - initialized to 1 at startup OPTARG - argument Vars used: OPTERR: disable printing of error messages """ # TODO: need to handle explicit args. try: # NOTE: If first char is a colon, error reporting is different. Alpine # might not use that? spec_str = argv[0] var_name = argv[1] except IndexError: raise args.UsageError('getopts optstring name [arg]') try: spec = _GETOPTS_CACHE[spec_str] except KeyError: spec = _ParseOptSpec(spec_str) _GETOPTS_CACHE[spec_str] = spec # These errors are fatal errors, not like the builtin exiting with code 1. # Because the invariants of the shell have been violated! v = mem.GetVar('OPTIND') if v.tag != value_e.Str: e_die('OPTIND should be a string, got %r', v) try: optind = int(v.s) except ValueError: e_die("OPTIND doesn't look like an integer, got %r", v.s) user_argv = argv[2:] or mem.GetArgv() status, opt_char, optarg, optind = _GetOpts(spec, user_argv, optind) # Bug fix: bash-completion uses a *local* OPTIND ! Not global. state.SetStringDynamic(mem, var_name, opt_char) state.SetStringDynamic(mem, 'OPTARG', optarg) state.SetStringDynamic(mem, 'OPTIND', str(optind)) return status
def _ValToArith(self, val, span_id): """Convert value_t to a Python int or list of strings.""" assert isinstance(val, value_t), '%r %r' % (val, type(val)) if val.tag == value_e.Undef: # 'nounset' already handled before got here # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42. #log('blame_word %s arena %s', blame_word, self.arena) e_die('Undefined value in arithmetic context ' '(0 if shopt -u strict-arith)', span_id=span_id) return 0 if val.tag == value_e.Str: return _StringToInteger(val.s, span_id=span_id) # calls e_die if val.tag == value_e.StrArray: # array is valid on RHS, but not on left return val.strs if val.tag == value_e.AssocArray: return val.d raise AssertionError(val)
def _ClassLiteralToPosixEre(term, parts): # type: (class_literal_term_t, List[str]) -> None UP_term = term tag = term.tag_() if tag == class_literal_term_e.Range: term = cast(class_literal_term__Range, UP_term) # \\ \^ \- can be used in ranges? start = glob_.EreCharClassEscape(term.start) end = glob_.EreCharClassEscape(term.end) parts.append('%s-%s' % (start, end)) return if tag == class_literal_term_e.ByteSet: term = cast(class_literal_term__ByteSet, UP_term) # This escaping is different than ExtendedRegexEscape. parts.append(glob_.EreCharClassEscape(term.bytes)) return if tag == class_literal_term_e.CodePoint: term = cast(class_literal_term__CodePoint, UP_term) code_point = term.i if code_point < 128: parts.append(chr(code_point)) else: e_die("ERE can't express code point %d", code_point, span_id=term.spid) return if tag == class_literal_term_e.PerlClass: term = cast(perl_class, UP_term) n = term.name chars = PERL_CLASS[term.name] # looks like '[:digit:]' if term.negated: e_die("Perl classes can't be negated in ERE", span_id=term.negated.span_id) else: pat = '%s' % chars parts.append(pat) return if tag == class_literal_term_e.PosixClass: term = cast(posix_class, UP_term) n = term.name # looks like 'digit' if term.negated: e_die("POSIX classes can't be negated in ERE", span_id=term.negated.span_id) else: pat = '[:%s:]' % n parts.append(pat) return if tag == class_literal_term_e.CharLiteral: term = cast(class_literal_term__CharLiteral, UP_term) parts.append(term.tok.val) return raise NotImplementedError(tag)
def _RunFunc(self, func_node, argv): """Used to run SimpleCommand and to run registered completion hooks.""" # These are redirects at DEFINITION SITE. You can also have redirects at # the CALL SITE. For example: # # f() { echo hi; } 1>&2 # f 2>&1 try: def_redirects = self._EvalRedirects(func_node) except util.RedirectEvalError as e: ui.PrettyPrintError(e, self.arena) return 1 if def_redirects: if not self.fd_state.Push(def_redirects, self.waiter): return 1 # error self.mem.PushCall(func_node.name, func_node.spids[0], argv) # Redirects still valid for functions. # Here doc causes a pipe and Process(SubProgramThunk). try: status = self._Execute(func_node.body) except _ControlFlow as e: if e.IsReturn(): status = e.StatusCode() else: # break/continue used in the wrong place. e_die('Unexpected %r (in function call)', e.token.val, token=e.token) except (util.FatalRuntimeError, util.ParseError) as e: self.dumper.MaybeCollect(self, e) # Do this before unwinding stack raise finally: self.mem.PopCall() if def_redirects: self.fd_state.Pop() return status