Exemple #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()
Exemple #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)
Exemple #3
0
 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
         ),
     )
Exemple #4
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))
Exemple #5
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
Exemple #6
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()
Exemple #7
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),
     )
Exemple #8
0
 def array_type(self, items, meta):
     assert len(items) >= 1
     assert isinstance(items[0], T.Base)
     optional = False
     nonempty = False
     for c in "".join(items[1:]):
         if c == "?":
             optional = True
         if c == "+":
             nonempty = True
     return T.Array(items[0], optional, nonempty)
Exemple #9
0
    def type(self, items, meta):
        quantifiers = set()
        if len(items) > 1 and isinstance(items[-1], set):
            quantifiers = items.pop()
        param = items[1] if len(items) > 1 else None
        param2 = items[2] if len(items) > 2 else None

        if items[0].value == "Array":
            if not param or param2:
                raise Err.InvalidType(sp(self.filename, meta),
                                      "Array must have one type parameter")
            if quantifiers - set(["optional", "nonempty"]):
                raise Err.ValidationError(
                    sp(self.filename, meta),
                    "invalid type quantifier(s) for Array")
            return T.Array(param, "optional" in quantifiers, "nonempty"
                           in quantifiers)
        if "nonempty" in quantifiers:
            raise Err.InvalidType(
                sp(self.filename, meta),
                "invalid type quantifier(s) for " + items[0].value)

        atomic_types = {
            "Int": T.Int,
            "Float": T.Float,
            "Boolean": T.Boolean,
            "String": T.String,
            "File": T.File,
        }
        if items[0].value in atomic_types:
            if param or param2:
                raise Err.InvalidType(
                    sp(self.filename, meta),
                    items[0] + " type doesn't accept parameters")
            return atomic_types[items[0].value]("optional" in quantifiers)

        if items[0].value == "Map":
            if not (param and param2):
                raise Err.InvalidType(sp(self.filename, meta),
                                      "Map must have two type parameters")
            return T.Map((param, param2), "optional" in quantifiers)

        if items[0].value == "Pair":
            if not (param and param2):
                raise Err.InvalidType(sp(self.filename, meta),
                                      "Pair must have two type parameters")
            return T.Pair(param, param2, "optional" in quantifiers)

        if param or param2:
            raise Err.InvalidType(sp(self.filename, meta),
                                  "Unexpected type parameter(s)")

        return T.StructInstance(items[0].value, "optional" in quantifiers)
Exemple #10
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.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)
Exemple #11
0
def _arrayize_types(type_env: Env.Types, nonempty: bool = False) -> Env.Types:
    # Given a type environment, recursively promote each binding of type T to
    # Array[T] -- used in Scatter.add_to_type_env
    ans = []
    for node in type_env:
        if isinstance(node, Env.Binding):
            ans.append(
                Env.Binding(node.name, T.Array(node.rhs, nonempty=nonempty),
                            node.ctx))
        elif isinstance(node, Env.Namespace):
            ans.append(
                Env.Namespace(node.namespace,
                              _arrayize_types(node.bindings, nonempty)))
        else:
            assert False
    return ans
Exemple #12
0
 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))
Exemple #13
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()
Exemple #14
0
 def _call_eager(self, expr: E.Apply, arguments: List[V.Base]) -> V.Base:
     pfx = arguments[0].coerce(T.String()).value
     return V.Array(
         T.Array(T.String()),
         [V.String(pfx + s.coerce(T.String()).value) for s in arguments[1].value],
     )
Exemple #15
0
    def __init__(self):
        # language built-ins
        self._at = _At()
        self._land = _And()
        self._lor = _Or()
        self._negate = StaticFunction(
            "_negate", [T.Boolean()], T.Boolean(), lambda x: V.Boolean(not x.value)
        )
        self._add = _AddOperator()
        self._sub = _ArithmeticOperator("-", lambda l, r: l - r)
        self._mul = _ArithmeticOperator("*", lambda l, r: l * r)
        self._div = _ArithmeticOperator("/", lambda l, r: l // r)
        self._rem = StaticFunction(
            "_rem", [T.Int(), T.Int()], T.Int(), lambda l, r: V.Int(l.value % r.value)
        )
        self._eqeq = _ComparisonOperator("==", lambda l, r: l == r)
        self._neq = _ComparisonOperator("!=", lambda l, r: l != r)
        self._lt = _ComparisonOperator("<", lambda l, r: l < r)
        self._lte = _ComparisonOperator("<=", lambda l, r: l <= r)
        self._gt = _ComparisonOperator(">", lambda l, r: l > r)
        self._gte = _ComparisonOperator(">=", lambda l, r: l >= r)

        # static stdlib functions
        for (name, argument_types, return_type, F) in [
            ("floor", [T.Float()], T.Int(), lambda v: V.Int(math.floor(v.value))),
            ("ceil", [T.Float()], T.Int(), lambda v: V.Int(math.ceil(v.value))),
            ("round", [T.Float()], T.Int(), lambda v: V.Int(round(v.value))),
            ("length", [T.Array(T.Any())], T.Int(), lambda v: V.Int(len(v.value))),
            ("sub", [T.String(), T.String(), T.String()], T.String(), _sub),
            ("basename", [T.String(), T.String(optional=True)], T.String(), _basename),
            (
                "defined",
                [T.Any(optional=True)],
                T.Boolean(),
                lambda v: V.Boolean(not isinstance(v, V.Null)),
            ),
            # context-dependent:
            ("write_lines", [T.Array(T.String())], T.File(), _notimpl),
            ("write_tsv", [T.Array(T.Array(T.String()))], T.File(), _notimpl),
            ("write_map", [T.Map((T.Any(), T.Any()))], T.File(), _notimpl),
            ("write_json", [T.Any()], T.File(), _notimpl),
            ("stdout", [], T.File(), _notimpl),
            ("stderr", [], T.File(), _notimpl),
            ("glob", [T.String()], T.Array(T.File()), _notimpl),
            ("read_int", [T.File()], T.Int(), _notimpl),
            ("read_boolean", [T.File()], T.Boolean(), _notimpl),
            ("read_string", [T.File()], T.String(), _notimpl),
            ("read_float", [T.File()], T.Float(), _notimpl),
            ("read_array", [T.File()], T.Array(T.Any()), _notimpl),
            ("read_map", [T.File()], T.Map((T.Any(), T.Any())), _notimpl),
            ("read_lines", [T.File()], T.Array(T.Any()), _notimpl),
            ("read_tsv", [T.File()], T.Array(T.Array(T.String())), _notimpl),
            ("read_json", [T.File()], T.Any(), _notimpl),
        ]:
            setattr(self, name, StaticFunction(name, argument_types, return_type, F))

        # polymorphically typed stdlib functions which require specialized
        # infer_type logic
        self.range = _Range()
        self.prefix = _Prefix()
        self.size = _Size()
        self.select_first = _SelectFirst()
        self.select_all = _SelectAll()
        self.zip = _Zip()
        self.cross = _Zip()  # FIXME
        self.flatten = _Flatten()
        self.transpose = _Transpose()
Exemple #16
0
 def _call_eager(self, expr: E.Apply, arguments: List[V.Base]) -> V.Base:
     arg0 = arguments[0]
     assert isinstance(arg0, V.Int)
     if arg0.value < 0:
         raise Error.EvalError(expr, "range() got negative argument")
     return V.Array(T.Array(T.Int()), [V.Int(x) for x in range(arg0.value)])
Exemple #17
0

_static_functions: List[Tuple[str, List[T.Base], T.Base, Any]] = [
    ("_negate", [T.Boolean()], T.Boolean(),
     lambda x: V.Boolean(not x.value)),  # pyre-fixme
    ("_rem", [T.Int(), T.Int()], T.Int(),
     lambda l, r: V.Int(l.value % r.value)),  # pyre-fixme
    ("stdout", [], T.File(), _notimpl),
    ("basename", [T.String(), T.String(optional=True)], T.String(), _notimpl),
    # note: size() can take an empty value and probably returns 0 in that case.
    #       e.g. https://github.com/DataBiosphere/topmed-workflows/blob/31ba8a714b36ada929044f2ba3d130936e6c740e/CRAM-no-header-md5sum/md5sum/CRAM_md5sum.wdl#L39
    ("size", [T.File(optional=True),
              T.String(optional=True)], T.Float(), _notimpl),
    ("ceil", [T.Float()], T.Int(), _notimpl),
    ("round", [T.Float()], T.Int(), _notimpl),
    ("glob", [T.String()], T.Array(T.File()), _notimpl),
    ("read_int", [T.String()], T.Int(), _notimpl),
    ("read_boolean", [T.String()], T.Boolean(), _notimpl),
    ("read_string", [T.String()], T.String(), _notimpl),
    ("read_float", [T.String()], T.Float(), _notimpl),
    ("read_array", [T.String()], T.Array(None), _notimpl),
    ("read_map", [T.String()], T.Map(None), _notimpl),
    ("read_lines", [T.String()], T.Array(None), _notimpl),
    ("read_tsv", [T.String()], T.Array(T.Array(T.String())), _notimpl),
    ("write_lines", [T.Array(T.String())], T.File(), _notimpl),
    ("write_tsv", [T.Array(T.Array(T.String()))], T.File(), _notimpl),
    ("write_map", [T.Map(None)], T.File(), _notimpl),
    ("range", [T.Int()], T.Array(T.Int()), _notimpl),
    ("sub", [T.String(), T.String(), T.String()], T.String(), _notimpl),
]
for name, argument_types, return_type, F in _static_functions: