예제 #1
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)
예제 #2
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