def _SetToArg(action, suffix, arg_r, out): # type: (SetToArgAction, Optional[str], Reader, _Attributes) -> bool """ Perform the action. """ if suffix: # for the ',' in -d, arg = suffix else: arg_r.Next() arg = arg_r.Peek() if arg is None: e_usage('expected argument to %r' % ('-' + action.name), span_id=arg_r.SpanId()) # e.g. spec.LongFlag('--format', ['text', 'html']) # Should change to arg.Enum([...]) with tagswitch(action.flag_type) as case: if case(flag_type_e.Enum): alts = cast(flag_type__Enum, action.flag_type).alts if arg not in alts: e_usage('got invalid argument %r to %r, expected one of: %s' % (arg, ('-' + action.name), ', '.join(alts)), span_id=arg_r.SpanId()) val = value.Str(arg) # type: value_t elif case(flag_type_e.Str): val = value.Str(arg) elif case(flag_type_e.Int): try: i = int(arg) except ValueError: e_usage('expected integer after %s, got %r' % ('-' + action.name, arg), span_id=arg_r.SpanId()) # So far all our int values are > 0, so use -1 as the 'unset' value if i < 0: e_usage('got invalid integer for %s: %s' % ('-' + action.name, arg), span_id=arg_r.SpanId()) val = value.Int(i) elif case(flag_type_e.Float): try: val = value.Float(float(arg)) except ValueError: e_usage('expected number after %r, got %r' % ('-' + action.name, arg), span_id=arg_r.SpanId()) else: raise AssertionError() out.Set(action.name, val) return action.quit_parsing_flags
def _Value(self, arg, span_id): # type: (str, int) -> value_t try: i = int(arg) except ValueError: e_usage('expected integer after %s, got %r' % ('-' + self.name, arg), span_id=span_id) # So far all our int values are > 0, so use -1 as the 'unset' value # corner case: this treats -0 as 0! if i < 0: e_usage('got invalid integer for %s: %s' % ('-' + self.name, arg), span_id=span_id) return value.Int(i)
def OnMatch(self, prefix, suffix, arg_r, out): # type: (Optional[str], Optional[str], Reader, _Attributes) -> bool """Called when the flag matches.""" if suffix: # for the ',' in -d, arg = suffix else: arg_r.Next() arg = arg_r.Peek() if arg is None: e_usage('expected argument to %r' % ('-' + self.name), span_id=arg_r.SpanId()) # e.g. spec.LongFlag('--format', ['text', 'html']) # Should change to arg.Enum([...]) with tagswitch(self.flag_type) as case: if case(flag_type_e.Enum): alts = cast(flag_type__Enum, self.flag_type).alts if arg not in alts: e_usage( 'got invalid argument %r to %r, expected one of: %s' % (arg, ('-' + self.name), ', '.join(alts)), span_id=arg_r.SpanId()) val = value.Str(arg) # type: value_t elif case(flag_type_e.Str): val = value.Str(arg) elif case(flag_type_e.Int): try: val = value.Int(int(arg)) except ValueError: e_usage('expected integer after %r, got %r' % ('-' + self.name, arg), span_id=arg_r.SpanId()) elif case(flag_type_e.Float): try: val = value.Float(float(arg)) except ValueError: e_usage('expected number after %r, got %r' % ('-' + self.name, arg), span_id=arg_r.SpanId()) else: raise AssertionError() out.Set(self.name, val) return self.quit_parsing_flags
def PyToValue(py_val): # type: (Any) -> value_t if py_val is None: val = value.Undef() # type: value_t elif isinstance(py_val, bool): val = value.Bool(py_val) elif isinstance(py_val, int): val = value.Int(py_val) elif isinstance(py_val, float): val = value.Float() # TODO: ASDL needs float primitive elif isinstance(py_val, str): val = value.Str(py_val) else: raise AssertionError(py_val) return val
def ShortFlag(self, short_name, arg_type=None, help=None): # type: (str, Optional[int], Optional[str]) -> None """ This is very similar to ShortFlag for FlagSpecAndMore, except we have separate arity0 and arity1 dicts. """ assert short_name.startswith('-'), short_name assert len(short_name) == 2, short_name char = short_name[1] if arg_type is None: self.arity0[char] = True else: self.arity1[char] = args.SetToArg(char, _FlagType(arg_type)) # TODO: callers should pass flag_type if arg_type is None: typ = flag_type.Bool() # type: flag_type_t default = value.Bool(False) # type: value_t elif arg_type == args.Int: typ = flag_type.Int() default = value.Int(-1) elif arg_type == args.Float: typ = flag_type.Float() default = value.Float(0.0) elif arg_type == args.String: typ = flag_type.Str() default = value.Str('') elif isinstance(arg_type, list): typ = flag_type.Enum(arg_type) default = value.Str('') # This isn't valid else: raise AssertionError(arg_type) if self.typed: self.defaults[char] = default else: # TODO: remove when all builtins converted self.defaults[char] = value.Undef() self.fields[char] = typ
def _Default(arg_type, arg_default=None): # type: (Union[None, int, List[str]], Optional[str]) -> value_t # for enum or string # note: not using this for integers yet if arg_default is not None: return value.Str(arg_default) # early return if arg_type is None: default = value.Bool(False) # type: value_t elif arg_type == args.Int: default = value.Int(-1) # positive values aren't allowed now elif arg_type == args.Float: default = value.Float(0.0) elif arg_type == args.String: default = value.Undef() # e.g. read -d '' is NOT the default elif isinstance(arg_type, list): default = value.Str('') # This isn't valid else: raise AssertionError(arg_type) return default
def Eval(self, node): # type: (arith_expr_t) -> value_t """ Args: node: arith_expr_t Returns: None for Undef (e.g. empty cell) TODO: Don't return 0! int for Str List[int] for MaybeStrArray Dict[str, str] for AssocArray (TODO: Should we support this?) NOTE: (( A['x'] = 'x' )) and (( x = A['x'] )) are syntactically valid in bash, but don't do what you'd think. 'x' sometimes a variable name and sometimes a key. """ # OSH semantics: Variable NAMES cannot be formed dynamically; but INTEGERS # can. ${foo:-3}4 is OK. $? will be a compound word too, so we don't have # to handle that as a special case. UP_node = node with tagswitch(node) as case: if case(arith_expr_e.VarRef): # $(( x )) (can be array) node = cast(arith_expr__VarRef, UP_node) tok = node.token return _LookupVar(tok.val, self.mem, self.exec_opts) elif case( arith_expr_e.ArithWord): # $(( $x )) $(( ${x}${y} )), etc. node = cast(arith_expr__ArithWord, UP_node) return self.word_ev.EvalWordToString(node.w) elif case(arith_expr_e.UnaryAssign): # a++ node = cast(arith_expr__UnaryAssign, UP_node) op_id = node.op_id old_int, lval = self._EvalLhsAndLookupArith(node.child) if op_id == Id.Node_PostDPlus: # post-increment new_int = old_int + 1 ret = old_int elif op_id == Id.Node_PostDMinus: # post-decrement new_int = old_int - 1 ret = old_int elif op_id == Id.Arith_DPlus: # pre-increment new_int = old_int + 1 ret = new_int elif op_id == Id.Arith_DMinus: # pre-decrement new_int = old_int - 1 ret = new_int else: raise AssertionError(op_id) #log('old %d new %d ret %d', old_int, new_int, ret) self._Store(lval, new_int) return value.Int(ret) elif case(arith_expr_e.BinaryAssign): # a=1, a+=5, a[1]+=5 node = cast(arith_expr__BinaryAssign, UP_node) op_id = node.op_id if op_id == Id.Arith_Equal: lval = _EvalLhsArith(node.left, self.mem, self) # Disallowing (( a = myarray )) # It has to be an integer rhs_int = self.EvalToInt(node.right) self._Store(lval, rhs_int) return value.Int(rhs_int) old_int, lval = self._EvalLhsAndLookupArith(node.left) rhs = self.EvalToInt(node.right) if op_id == Id.Arith_PlusEqual: new_int = old_int + rhs elif op_id == Id.Arith_MinusEqual: new_int = old_int - rhs elif op_id == Id.Arith_StarEqual: new_int = old_int * rhs elif op_id == Id.Arith_SlashEqual: if rhs == 0: e_die('Divide by zero') # TODO: location new_int = old_int / rhs elif op_id == Id.Arith_PercentEqual: if rhs == 0: e_die('Divide by zero') # TODO: location new_int = old_int % rhs elif op_id == Id.Arith_DGreatEqual: new_int = old_int >> rhs elif op_id == Id.Arith_DLessEqual: new_int = old_int << rhs elif op_id == Id.Arith_AmpEqual: new_int = old_int & rhs elif op_id == Id.Arith_PipeEqual: new_int = old_int | rhs elif op_id == Id.Arith_CaretEqual: new_int = old_int ^ rhs else: raise AssertionError(op_id) # shouldn't get here self._Store(lval, new_int) return value.Int(new_int) elif case(arith_expr_e.Unary): node = cast(arith_expr__Unary, UP_node) op_id = node.op_id i = self.EvalToInt(node.child) if op_id == Id.Node_UnaryPlus: ret = i elif op_id == Id.Node_UnaryMinus: ret = -i elif op_id == Id.Arith_Bang: # logical negation ret = 1 if i == 0 else 0 elif op_id == Id.Arith_Tilde: # bitwise complement ret = ~i else: raise AssertionError(op_id) # shouldn't get here return value.Int(ret) elif case(arith_expr_e.Binary): node = cast(arith_expr__Binary, UP_node) op_id = node.op_id # Short-circuit evaluation for || and &&. if op_id == Id.Arith_DPipe: lhs = self.EvalToInt(node.left) if lhs == 0: rhs = self.EvalToInt(node.right) ret = int(rhs != 0) else: ret = 1 # true return value.Int(ret) if op_id == Id.Arith_DAmp: lhs = self.EvalToInt(node.left) if lhs == 0: ret = 0 # false else: rhs = self.EvalToInt(node.right) ret = int(rhs != 0) return value.Int(ret) if op_id == Id.Arith_LBracket: # NOTE: Similar to bracket_op_e.ArrayIndex in osh/word_eval.py left = self.Eval(node.left) UP_left = left with tagswitch(left) as case: if case(value_e.MaybeStrArray): left = cast(value__MaybeStrArray, UP_left) rhs_int = self.EvalToInt(node.right) try: # could be None because representation is sparse s = left.strs[rhs_int] except IndexError: s = None elif case(value_e.AssocArray): left = cast(value__AssocArray, UP_left) key = self.EvalWordToString(node.right) s = left.d.get(key) else: # TODO: Add error context e_die( 'Expected array or assoc in index expression, got %s', ui.ValType(left)) if s is None: val = value.Undef() # type: value_t else: val = value.Str(s) return val if op_id == Id.Arith_Comma: self.Eval(node.left) # throw away result return self.Eval(node.right) # Rest are integers lhs = self.EvalToInt(node.left) rhs = self.EvalToInt(node.right) if op_id == Id.Arith_Plus: ret = lhs + rhs elif op_id == Id.Arith_Minus: ret = lhs - rhs elif op_id == Id.Arith_Star: ret = lhs * rhs elif op_id == Id.Arith_Slash: if rhs == 0: # TODO: Could also blame / e_die('Divide by zero', span_id=location.SpanForArithExpr(node.right)) ret = lhs / rhs elif op_id == Id.Arith_Percent: if rhs == 0: # TODO: Could also blame / e_die('Divide by zero', span_id=location.SpanForArithExpr(node.right)) ret = lhs % rhs elif op_id == Id.Arith_DStar: # OVM is stripped of certain functions that are somehow necessary for # exponentiation. # Python/ovm_stub_pystrtod.c:21: PyOS_double_to_string: Assertion `0' # failed. if rhs < 0: e_die("Exponent can't be less than zero" ) # TODO: error location ret = 1 for i in xrange(rhs): ret *= lhs elif op_id == Id.Arith_DEqual: ret = int(lhs == rhs) elif op_id == Id.Arith_NEqual: ret = int(lhs != rhs) elif op_id == Id.Arith_Great: ret = int(lhs > rhs) elif op_id == Id.Arith_GreatEqual: ret = int(lhs >= rhs) elif op_id == Id.Arith_Less: ret = int(lhs < rhs) elif op_id == Id.Arith_LessEqual: ret = int(lhs <= rhs) elif op_id == Id.Arith_Pipe: ret = lhs | rhs elif op_id == Id.Arith_Amp: ret = lhs & rhs elif op_id == Id.Arith_Caret: ret = lhs ^ rhs # Note: how to define shift of negative numbers? elif op_id == Id.Arith_DLess: ret = lhs << rhs elif op_id == Id.Arith_DGreat: ret = lhs >> rhs else: raise AssertionError(op_id) return value.Int(ret) elif case(arith_expr_e.TernaryOp): node = cast(arith_expr__TernaryOp, UP_node) cond = self.EvalToInt(node.cond) if cond: # nonzero return self.Eval(node.true_expr) else: return self.Eval(node.false_expr) else: raise AssertionError(node.tag_())