def infer_type(self, expr: E.Apply) -> T.Base: if len(expr.arguments) != 1: raise Error.WrongArity(expr, 1) if not isinstance(expr.arguments[0].type, T.Array): raise Error.StaticTypeMismatch(expr, T.Array(None), expr.arguments[0].type) return T.Int()
def _infer_type(self, type_env: Env.Types) -> T.Base: self.expr.infer_type(type_env, self._check_quant) if isinstance(self.expr.type, T.Array): if "sep" not in self.options: raise Error.StaticTypeMismatch( self, T.Array(None), self.expr.type, "array command placeholder must have 'sep'") # if sum(1 for t in [T.Int, T.Float, T.Boolean, T.String, T.File] if isinstance(self.expr.type.item_type, t)) == 0: # raise Error.StaticTypeMismatch(self, T.Array(None), self.expr.type, "cannot use array of complex types for command placeholder") elif "sep" in self.options: raise Error.StaticTypeMismatch( self, T.Array(None), self.expr.type, "command placeholder has 'sep' option for non-Array expression", ) if "true" in self.options or "false" in self.options: if not isinstance(self.expr.type, T.Boolean): raise Error.StaticTypeMismatch( self, T.Boolean(), self.expr.type, "command placeholder 'true' and 'false' options used with non-Boolean expression", ) if not ("true" in self.options and "false" in self.options): raise Error.StaticTypeMismatch( self, T.Boolean(), self.expr.type, "command placeholder with only one of 'true' and 'false' options", ) return T.String()
def _infer_type(self, type_env: Env.Types) -> T.Base: if not self.items: return T.Array(None) for item in self.items: item.infer_type(type_env, self._check_quant) # Start by assuming the type of the first item is the item type item_type: T.Base = self.items[0].type # Allow a mixture of Int and Float to construct Array[Float] if isinstance(item_type, T.Int): for item in self.items: if isinstance(item.type, T.Float): item_type = T.Float() # If any item is String, assume item type is String # If any item has optional quantifier, assume item type is optional # If all items have nonempty quantifier, assume item type is nonempty all_nonempty = len(self.items) > 0 for item in self.items: if isinstance(item.type, T.String): item_type = T.String(optional=item_type.optional) if item.type.optional: item_type = item_type.copy(optional=True) if isinstance(item.type, T.Array) and not item.type.nonempty: all_nonempty = False if isinstance(item_type, T.Array): item_type = item_type.copy(nonempty=all_nonempty) # Check all items are coercible to item_type for item in self.items: try: item.typecheck(item_type) except Error.StaticTypeMismatch: self._type = T.Array(item_type, optional=False, nonempty=True) raise Error.StaticTypeMismatch( self, item_type, item.type, "(inconsistent types within array)") from None return T.Array(item_type, optional=False, nonempty=True)
def _call_eager(self, expr: E.Apply, arguments: List[V.Base]) -> V.Base: ans_type = self.infer_type(expr) if not isinstance(ans_type, T.String): return super()._call_eager(expr, arguments) # TODO: in a command interpolation, return missing if either operand is missing ans = self.op( str(arguments[0].coerce(T.String()).value), str(arguments[1].coerce(T.String()).value) ) assert isinstance(ans, str) return V.String(ans)
def infer_type(self, expr: E.Apply) -> T.Base: if len(expr.arguments) != 2: raise Error.WrongArity(expr, 2) expr.arguments[0].typecheck(T.String()) expr.arguments[1].typecheck(T.Array(T.String())) return T.Array( T.String(), nonempty=( isinstance(expr.arguments[1].type, T.Array) and expr.arguments[1].type.nonempty ), )
def __call__(self, expr: E.Apply, env: E.Env) -> V.Base: ans_type = self.infer_type(expr) if not isinstance(ans_type, T.String): return super().__call__(expr, env) # TODO: return missing if either operand is missing ans = self.op( str(expr.arguments[0].eval(env).coerce(T.String()).value), str(expr.arguments[1].eval(env).coerce(T.String()).value), ) assert isinstance(ans, str) return V.String(ans)
def infer_type(self, expr: E.Apply) -> T.Base: if len(expr.arguments) != 1: raise Error.WrongArity(expr, 1) if not isinstance(expr.arguments[0].type, T.Array): raise Error.StaticTypeMismatch(expr.arguments[0], T.Array(None), expr.arguments[0].type) if expr.arguments[0].type.item_type is None: # TODO: error for 'indeterminate type' raise Error.EmptyArray(expr.arguments[0]) ty = expr.arguments[0].type.item_type assert isinstance(ty, T.Base) return T.Array(ty.copy(optional=False))
def infer_type(self, expr: E.Apply) -> T.Base: if len(expr.arguments) != 1: raise Error.WrongArity(expr, 1) expr.arguments[0].typecheck(T.Array(None)) # TODO: won't handle implicit coercion from T to Array[T] assert isinstance(expr.arguments[0].type, T.Array) if expr.arguments[0].type.item_type is None: return T.Array(None) if not isinstance(expr.arguments[0].type.item_type, T.Array): raise Error.StaticTypeMismatch(expr.arguments[0], T.Array(T.Array(None)), expr.arguments[0].type) return expr.arguments[0].type
def infer_type(self, expr: E.Apply) -> T.Base: if len(expr.arguments) != 1: raise Error.WrongArity(expr, 1) expr.arguments[0].typecheck(T.Int()) nonempty = False arg0 = expr.arguments[0] if isinstance(arg0, E.Int) and arg0.value > 0: nonempty = True if isinstance(arg0, E.Apply) and arg0.function_name == "length": arg00ty = arg0.arguments[0].type if isinstance(arg00ty, T.Array) and arg00ty.nonempty: nonempty = True return T.Array(T.Int(), nonempty=nonempty)
def infer_type(self, expr: E.Apply) -> T.Base: assert len(expr.arguments) == 2 rt = T.Int() if isinstance(expr.arguments[0].type, T.Float) or isinstance( expr.arguments[1].type, T.Float): rt = T.Float() try: expr.arguments[0].typecheck(rt) expr.arguments[1].typecheck(rt) except Error.StaticTypeMismatch: raise Error.IncompatibleOperand( expr, "Non-numeric operand to " + self.name + " operator") from None return rt
def infer_type(self, expr: E.Apply) -> T.Base: assert len(expr.arguments) == 2 if ( ( expr._check_quant and expr.arguments[0].type.optional != expr.arguments[1].type.optional ) or ( self.name not in ["==", "!="] and (expr.arguments[0].type.optional or expr.arguments[1].type.optional) ) or ( not ( expr.arguments[0].type.copy(optional=False) == expr.arguments[1].type.copy(optional=False) or ( isinstance(expr.arguments[0].type, T.Int) and isinstance(expr.arguments[1].type, T.Float) ) or ( isinstance(expr.arguments[0].type, T.Float) and isinstance(expr.arguments[1].type, T.Int) ) ) ) ): raise Error.IncompatibleOperand( expr, "Cannot compare {} and {}".format( str(expr.arguments[0].type), str(expr.arguments[1].type) ), ) return T.Boolean()
def __call__(self, expr: E.Apply, env: E.Env) -> V.Base: assert len(expr.arguments) == 2 lhs = expr.arguments[0] rhs = expr.arguments[1] if isinstance(lhs.type, T.Array): arr = lhs.eval(env) assert isinstance(arr, V.Array) assert isinstance(arr.type, T.Array) assert isinstance(arr.value, list) idx = rhs.eval(env).expect(T.Int()).value if idx < 0 or idx >= len(arr.value): raise Error.OutOfBounds(rhs) return arr.value[idx] # pyre-fixme if isinstance(lhs.type, T.Map): mp = lhs.eval(env) assert isinstance(mp, V.Map) assert isinstance(mp.type, T.Map) assert mp.type.item_type is not None assert isinstance(mp.value, list) ans = None key = rhs.eval(env).expect(mp.type.item_type[0]) for k, v in mp.value: if key == k: ans = v.expect(mp.type.item_type[1]) if ans is None: raise Error.OutOfBounds(rhs) # TODO: KeyNotFound return ans # pyre-fixme assert False # pyre-fixme
def infer_type(self, expr: E.Apply) -> T.Base: assert len(expr.arguments) == 2 t2 = None if isinstance(expr.arguments[0].type, T.String): t2 = expr.arguments[1].type elif isinstance(expr.arguments[1].type, T.String): t2 = expr.arguments[0].type if t2 is None: # neither operand is a string; defer to _ArithmeticOperator return super().infer_type(expr) if not t2.coerces(T.String(optional=True)): raise Error.IncompatibleOperand( expr, "Cannot add/concatenate {} and {}".format( str(expr.arguments[0].type), str(expr.arguments[1].type)), ) return T.String()
def infer_type(self, expr: E.Apply) -> T.Base: assert len(expr.arguments) == 2 for arg in expr.arguments: if not isinstance(arg.type, T.Boolean): raise Error.IncompatibleOperand(arg, "non-Boolean operand to ||") if expr._check_quant and arg.type.optional: raise Error.IncompatibleOperand(arg, "optional Boolean? operand to ||") return T.Boolean()
def _infer_type(self, type_env: Env.Types) -> T.Base: kty = None vty = None for k, v in self.items: k.infer_type(type_env, self._check_quant) if kty is None: kty = k.type else: k.typecheck(kty) v.infer_type(type_env, self._check_quant) if vty is None or vty == T.Array(None) or vty == T.Map(None): vty = v.type else: v.typecheck(vty) if kty is None: return T.Map(None) assert vty is not None return T.Map((kty, vty))
def eval(self, env: Env.Values) -> V.Base: "" try: if self.condition.eval(env).expect(T.Boolean()).value: ans = self.consequent.eval(env) else: ans = self.alternative.eval(env) return ans except ReferenceError: raise Error.NullValue(self) from None
def infer_type(self, expr: E.Apply) -> T.Base: if len(expr.arguments) != 2: raise Error.WrongArity(expr, 2) arg0ty: T.Base = expr.arguments[0].type if not isinstance(arg0ty, T.Array) or (expr._check_quant and arg0ty.optional): raise Error.StaticTypeMismatch(expr.arguments[0], T.Array(None), arg0ty) if arg0ty.item_type is None: # TODO: error for 'indeterminate type' raise Error.EmptyArray(expr.arguments[0]) arg1ty: T.Base = expr.arguments[1].type if not isinstance(arg1ty, T.Array) or (expr._check_quant and arg1ty.optional): raise Error.StaticTypeMismatch(expr.arguments[1], T.Array(None), arg1ty) if arg1ty.item_type is None: # TODO: error for 'indeterminate type' raise Error.EmptyArray(expr.arguments[1]) return T.Array(T.Pair(arg0ty.item_type, arg1ty.item_type))
def _call_eager(self, expr: E.Apply, arguments: List[V.Base]) -> V.Base: ans_type = self.infer_type(expr) try: ans = self.op(arguments[0].coerce(ans_type).value, arguments[1].coerce(ans_type).value) except ZeroDivisionError: # TODO: different runtime error? raise Error.IncompatibleOperand(expr.arguments[1], "Division by zero") from None if ans_type == T.Int(): assert isinstance(ans, int) return V.Int(ans) assert isinstance(ans, float) return V.Float(ans)
def _infer_type(self, type_env: Env.Types) -> T.Base: # check for Boolean condition if self.condition.infer_type(type_env, self._check_quant).type != T.Boolean(): raise Error.StaticTypeMismatch(self, T.Boolean(), self.condition.type, "in if condition") # Unify consequent & alternative types. Subtleties: # 1. If either is optional, unify to optional # 2. If one is Int and the other is Float, unify to Float # 3. If one is a nonempty array and the other is a possibly empty # array, unify to possibly empty array self_type = self.consequent.infer_type(type_env, self._check_quant).type assert isinstance(self_type, T.Base) self.alternative.infer_type(type_env, self._check_quant) if isinstance(self_type, T.Int) and isinstance(self.alternative.type, T.Float): self_type = T.Float(optional=self_type.optional) if self.alternative.type.optional: self_type = self_type.copy(optional=True) if (isinstance(self_type, T.Array) and isinstance(self.consequent.type, T.Array) and isinstance(self.alternative.type, T.Array)): self_type = self_type.copy(nonempty=( # pyre-ignore self.consequent.type.nonempty and self.alternative.type.nonempty # pyre-ignore )) try: self.consequent.typecheck(self_type) self.alternative.typecheck(self_type) except Error.StaticTypeMismatch: raise Error.StaticTypeMismatch( self, self.consequent.type, # pyre-ignore self.alternative.type, " (if consequent & alternative must have the same type)", ) from None return self_type
def infer_type(self, expr: E.Apply) -> T.Base: assert len(expr.arguments) == 2 lhs = expr.arguments[0] rhs = expr.arguments[1] if isinstance(lhs.type, T.Array): if isinstance(lhs, E.Array) and not lhs.items: # the user wrote: [][idx] raise Error.OutOfBounds(expr) try: rhs.typecheck(T.Int()) except Error.StaticTypeMismatch: raise Error.StaticTypeMismatch(rhs, T.Int(), rhs.type, "Array index") from None return lhs.type.item_type if isinstance(lhs.type, T.Map): if lhs.type.item_type is None: raise Error.OutOfBounds(expr) try: rhs.typecheck(lhs.type.item_type[0]) except Error.StaticTypeMismatch: raise Error.StaticTypeMismatch( rhs, lhs.type.item_type[0], rhs.type, "Map key" ) from None return lhs.type.item_type[1] raise Error.NotAnArray(lhs)
def infer_type(self, expr: E.Apply) -> T.Base: if not expr.arguments: raise Error.WrongArity(expr, 1) if not expr.arguments[0].type.coerces(T.File(optional=True)): if isinstance(expr.arguments[0].type, T.Array): if expr.arguments[0].type.optional or not expr.arguments[0].type.item_type.coerces( T.File(optional=True) ): raise Error.StaticTypeMismatch( expr.arguments[0], T.Array(T.File(optional=True)), expr.arguments[0].type ) else: raise Error.StaticTypeMismatch( expr.arguments[0], T.File(optional=True), expr.arguments[0].type ) if len(expr.arguments) == 2: if expr.arguments[1].type != T.String(): raise Error.StaticTypeMismatch( expr.arguments[1], T.String(), expr.arguments[1].type ) elif len(expr.arguments) > 2: raise Error.WrongArity(expr, 2) return T.Float()