예제 #1
0
 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()
예제 #2
0
 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)
예제 #3
0
 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()
예제 #4
0
 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(T.Any()), arg0ty)
     if isinstance(arg0ty.item_type, T.Any):
         # 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(T.Any()), arg1ty)
     if isinstance(arg1ty.item_type, T.Any):
         # TODO: error for 'indeterminate type'
         raise Error.EmptyArray(expr.arguments[1])
     return T.Array(
         T.Pair(arg0ty.item_type, arg1ty.item_type),
         nonempty=(arg0ty.nonempty or arg1ty.nonempty),
     )
예제 #5
0
 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))
예제 #6
0
 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
예제 #7
0
 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(T.Any()))
     # 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(T.Any())
     if not isinstance(expr.arguments[0].type.item_type, T.Array):
         raise Error.StaticTypeMismatch(
             expr.arguments[0], T.Array(T.Array(T.Any())), expr.arguments[0].type
         )
     return expr.arguments[0].type
예제 #8
0
 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()
예제 #9
0
 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)
예제 #10
0
    def typecheck(self, expected: T.Base) -> TVBase:
        """typecheck(self, expected : T.Base) -> WDL.Expr.Base

        Check that this expression's type is, or can be coerced to,
        ``expected``.

        :raise WDL.Error.StaticTypeMismatch:
        :return: `self`
        """
        expected2 = expected
        if not self._check_quant:
            if isinstance(expected, T.Array):
                expected2 = expected.copy(nonempty=False, optional=True)
            else:
                expected2 = expected.copy(optional=True)
        if not self.type.coerces(expected2):
            raise Error.StaticTypeMismatch(self, expected2, self.type)
        return self
예제 #11
0
 def infer_type(self, expr: E.Apply) -> T.Base:
     min_args = len(self.argument_types)
     for ty in reversed(self.argument_types):
         if ty.optional:
             min_args = min_args - 1
         else:
             break
     if len(expr.arguments) > len(self.argument_types) or len(expr.arguments) < min_args:
         raise Error.WrongArity(expr, len(self.argument_types))
     for i in range(len(expr.arguments)):
         try:
             expr.arguments[i].typecheck(self.argument_types[i])
         except Error.StaticTypeMismatch:
             raise Error.StaticTypeMismatch(
                 expr.arguments[i],
                 self.argument_types[i],
                 expr.arguments[i].type,
                 "for {} argument #{}".format(self.name, i + 1),
             ) from None
     return self.return_type
예제 #12
0
파일: Tree.py 프로젝트: hpobio-lab/miniwdl
def _build_workflow_type_env(
    doc: TVDocument,
    check_quant: bool,
    self: Optional[Union[Workflow, Scatter, Conditional]] = None,
    outer_type_env: Env.Types = [],
) -> None:
    # Populate each Workflow, Scatter, and Conditional object with its
    # _type_env attribute containing the type environment available in the body
    # of the respective section. This is tricky because:
    # - forward-references to any declaration or call output in the workflow
    #   are valid, except
    #   - circular dependencies, direct or indirect
    #   - (corollary) scatter and conditional expressions can't refer to
    #     anything within the respective section
    # - a scatter variable is visible only inside the scatter
    # - declarations & call outputs of type T within a scatter have type
    #   Array[T] outside of the scatter
    # - declarations & call outputs of type T within a conditional have type T?
    #   outside of the conditional
    #
    # preconditions:
    # - _resolve_calls()
    #
    # postconditions:
    # - typechecks scatter and conditional expressions (recursively)
    # - sets _type_env attributes on each Workflow/Scatter/Conditional
    self = self or doc.workflow
    if not self:
        return
    assert isinstance(self, (Scatter, Conditional)) or self is doc.workflow
    assert self._type_env is None

    # When we've been called recursively on a scatter or conditional section,
    # the 'outer' type environment has everything available in the workflow
    # -except- the body of self.
    type_env = outer_type_env
    if isinstance(self, Scatter):
        # typecheck scatter array
        self.expr.infer_type(type_env, check_quant)
        if not isinstance(self.expr.type, T.Array):
            raise Err.NotAnArray(self.expr)
        if self.expr.type.item_type is None:
            raise Err.EmptyArray(self.expr)
        # bind the scatter variable to the array item type within the body
        try:
            Env.resolve(type_env, [], self.variable)
            raise Err.MultipleDefinitions(
                self, "Name collision for scatter variable " + self.variable)
        except KeyError:
            pass
        type_env = Env.bind(self.variable,
                            self.expr.type.item_type,
                            type_env,
                            ctx=self)
    elif isinstance(self, Conditional):
        # typecheck the condition
        self.expr.infer_type(type_env, check_quant)
        if not self.expr.type.coerces(T.Boolean()):
            raise Err.StaticTypeMismatch(self.expr, T.Boolean(),
                                         self.expr.type)

    # descend into child scatter & conditional elements, if any.
    for child in self.elements:
        if isinstance(child, (Scatter, Conditional)):
            # prepare the 'outer' type environment for the child element, by
            # adding all its sibling declarations and call outputs
            child_outer_type_env = type_env
            for sibling in self.elements:
                if sibling is not child:
                    child_outer_type_env = sibling.add_to_type_env(
                        child_outer_type_env)
            _build_workflow_type_env(doc, check_quant, child,
                                     child_outer_type_env)

    # finally, populate self._type_env with all our children
    for child in self.elements:
        type_env = child.add_to_type_env(type_env)
    self._type_env = type_env