Compare commits
6
Commits
83eecd612e
...
094554cb72
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
094554cb72
|
||
|
|
40bda81c32
|
||
|
|
25c11c3a53
|
||
|
|
f3dec414cc
|
||
|
|
48be2d454c
|
||
|
|
5958e3612b
|
@@ -8,6 +8,7 @@ from midas.typing import Column, cast, unsafe_cast
|
|||||||
|
|
||||||
|
|
||||||
def load_data(path: Path) -> RawData:
|
def load_data(path: Path) -> RawData:
|
||||||
|
# Check base types and dataframe structure
|
||||||
return cast(RawData, pd.read_csv(path))
|
return cast(RawData, pd.read_csv(path))
|
||||||
|
|
||||||
|
|
||||||
@@ -17,10 +18,15 @@ def convert_data(raw_df: RawData) -> Data:
|
|||||||
Column[object],
|
Column[object],
|
||||||
pd.to_datetime(new_df["timestamp"]),
|
pd.to_datetime(new_df["timestamp"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check types and constraints at runtime, catches out-of-range values and
|
||||||
|
# invalid types / malformed data
|
||||||
return cast(Data, new_df)
|
return cast(Data, new_df)
|
||||||
|
|
||||||
|
|
||||||
def compute_heat_index(df: Data):
|
def compute_heat_index(df: Data):
|
||||||
|
# The computation's result can only be typed as `Column[float]`
|
||||||
|
# Casting is necessary to bring back semantic
|
||||||
df["heat_index"] = cast(
|
df["heat_index"] = cast(
|
||||||
Column[HeatIndex],
|
Column[HeatIndex],
|
||||||
(
|
(
|
||||||
@@ -33,6 +39,10 @@ def compute_heat_index(df: Data):
|
|||||||
|
|
||||||
|
|
||||||
def daily_avg(df: DataWithHI):
|
def daily_avg(df: DataWithHI):
|
||||||
|
# Group-by and aggregation methods keep the structure of the dataframe but
|
||||||
|
# may erase the exact types
|
||||||
|
# The type checker is still very conservative and often the result of most
|
||||||
|
# aggregation methods as `Column[Any]`
|
||||||
return cast(
|
return cast(
|
||||||
DailyAverages,
|
DailyAverages,
|
||||||
df.groupby(
|
df.groupby(
|
||||||
@@ -47,21 +57,23 @@ def daily_avg(df: DataWithHI):
|
|||||||
|
|
||||||
|
|
||||||
def plot(df: DailyAverages):
|
def plot(df: DailyAverages):
|
||||||
|
# Some operations are not implemented in Midas but the user can still use
|
||||||
|
# them, they will just not be fully type-checked
|
||||||
|
# `unsafe_cast` can also be used to avoid trivial, redundant or costly checks
|
||||||
stations = unsafe_cast(list[str], list(df.index.get_level_values(0).unique()))
|
stations = unsafe_cast(list[str], list(df.index.get_level_values(0).unique()))
|
||||||
for station in stations:
|
for station in stations:
|
||||||
sub_df = unsafe_cast(DailyAverages, df.loc[station])
|
sub_df = unsafe_cast(DailyAverages, df.loc[station])
|
||||||
# plt.plot(sub_df["timestamp"], sub_df["temperature"])
|
|
||||||
plt.plot(sub_df["timestamp"], sub_df["heat_index"])
|
plt.plot(sub_df["timestamp"], sub_df["heat_index"])
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# Assigning to annotated variables help catch errors
|
||||||
raw_df: RawData = load_data(Path("data.csv"))
|
raw_df: RawData = load_data(Path("data.csv"))
|
||||||
df: Data = convert_data(raw_df)
|
df: Data = convert_data(raw_df)
|
||||||
|
|
||||||
with_hi = compute_heat_index(df)
|
with_hi = compute_heat_index(df)
|
||||||
dailies = daily_avg(with_hi)
|
dailies = daily_avg(with_hi)
|
||||||
print(dailies)
|
|
||||||
plot(dailies)
|
plot(dailies)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ from midas.checker.types import (
|
|||||||
ColumnType,
|
ColumnType,
|
||||||
Function,
|
Function,
|
||||||
ParamSpec,
|
ParamSpec,
|
||||||
TopType,
|
|
||||||
Type,
|
Type,
|
||||||
|
UnknownType,
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -49,21 +49,18 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
def _aggregate(
|
def _aggregate(
|
||||||
self,
|
self,
|
||||||
call: Call,
|
call: Call,
|
||||||
|
method: str,
|
||||||
params: list[str | tuple[str, str, bool]] = [],
|
params: list[str | tuple[str, str, bool]] = [],
|
||||||
*,
|
|
||||||
preserve_inner_type: bool = False,
|
|
||||||
) -> Type:
|
) -> Type:
|
||||||
"""Compute the result type of an aggregate method call
|
"""Compute the result type of an aggregate method call
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
call (Call): the call object
|
call (Call): the call object
|
||||||
|
method (str): the method name to delegate on :class:`Column`
|
||||||
params (list[str | tuple[str, str, bool], optional): a list of extra
|
params (list[str | tuple[str, str, bool], optional): a list of extra
|
||||||
mixed parameters. The list can contain strings to include
|
mixed parameters. The list can contain strings to include
|
||||||
parameters predefined in `NAMED_ARGS`, or tuples containing the
|
parameters predefined in `NAMED_ARGS`, or tuples containing the
|
||||||
parameter's name, type and required flag. Defaults to [].
|
parameter's name, type and required flag. Defaults to [].
|
||||||
preserve_inner_type (bool, optional): If `True`, the result type
|
|
||||||
will preserve the column's inner type (e.g. for `min`/`max`),
|
|
||||||
otherwise the inner type is widened to `TopType`. Defaults to False.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Type: the result type
|
Type: the result type
|
||||||
@@ -87,13 +84,21 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
)
|
)
|
||||||
real_params.append(param)
|
real_params.append(param)
|
||||||
|
|
||||||
|
# TODO: maybe better to filter arguments and pass some, in case the
|
||||||
|
# return type depends on them
|
||||||
|
returns: Type = self.typer.call_method(
|
||||||
|
location=call.location,
|
||||||
|
call_expr=call.call_expr,
|
||||||
|
obj=(call.groupby_expr, call.groupby.column),
|
||||||
|
method_name=method,
|
||||||
|
positional=[],
|
||||||
|
keywords={},
|
||||||
|
)
|
||||||
|
if not isinstance(returns, ColumnType):
|
||||||
|
returns = ColumnType(type=UnknownType())
|
||||||
signature = Function(
|
signature = Function(
|
||||||
params=ParamSpec(mixed=real_params),
|
params=ParamSpec(mixed=real_params),
|
||||||
returns=(
|
returns=returns,
|
||||||
call.groupby.column
|
|
||||||
if preserve_inner_type
|
|
||||||
else ColumnType(type=TopType())
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
result: CallResult = self.dispatcher.get_result(
|
result: CallResult = self.dispatcher.get_result(
|
||||||
@@ -108,6 +113,7 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
def kurt(self, call: Call) -> Type:
|
def kurt(self, call: Call) -> Type:
|
||||||
return self._aggregate(
|
return self._aggregate(
|
||||||
call,
|
call,
|
||||||
|
"kurt",
|
||||||
["skipna", "numeric_only"],
|
["skipna", "numeric_only"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -115,6 +121,7 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
def max(self, call: Call) -> Type:
|
def max(self, call: Call) -> Type:
|
||||||
return self._aggregate(
|
return self._aggregate(
|
||||||
call,
|
call,
|
||||||
|
"max",
|
||||||
[
|
[
|
||||||
"numeric_only",
|
"numeric_only",
|
||||||
(
|
(
|
||||||
@@ -126,13 +133,13 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
"engine",
|
"engine",
|
||||||
"engine_kwargs",
|
"engine_kwargs",
|
||||||
],
|
],
|
||||||
preserve_inner_type=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def mean(self, call: Call) -> Type:
|
def mean(self, call: Call) -> Type:
|
||||||
return self._aggregate(
|
return self._aggregate(
|
||||||
call,
|
call,
|
||||||
|
"mean",
|
||||||
["numeric_only", "skipna", "engine", "engine_kwargs"],
|
["numeric_only", "skipna", "engine", "engine_kwargs"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -140,14 +147,15 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
def median(self, call: Call) -> Type:
|
def median(self, call: Call) -> Type:
|
||||||
return self._aggregate(
|
return self._aggregate(
|
||||||
call,
|
call,
|
||||||
|
"median",
|
||||||
["numeric_only", "skipna"],
|
["numeric_only", "skipna"],
|
||||||
preserve_inner_type=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def min(self, call: Call) -> Type:
|
def min(self, call: Call) -> Type:
|
||||||
return self._aggregate(
|
return self._aggregate(
|
||||||
call,
|
call,
|
||||||
|
"min",
|
||||||
[
|
[
|
||||||
"numeric_only",
|
"numeric_only",
|
||||||
(
|
(
|
||||||
@@ -159,13 +167,13 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
"engine",
|
"engine",
|
||||||
"engine_kwargs",
|
"engine_kwargs",
|
||||||
],
|
],
|
||||||
preserve_inner_type=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def prod(self, call: Call) -> Type:
|
def prod(self, call: Call) -> Type:
|
||||||
return self._aggregate(
|
return self._aggregate(
|
||||||
call,
|
call,
|
||||||
|
"prod",
|
||||||
[
|
[
|
||||||
"numeric_only",
|
"numeric_only",
|
||||||
(
|
(
|
||||||
@@ -181,6 +189,7 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
def std(self, call: Call) -> Type:
|
def std(self, call: Call) -> Type:
|
||||||
return self._aggregate(
|
return self._aggregate(
|
||||||
call,
|
call,
|
||||||
|
"std",
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
"ddof",
|
"ddof",
|
||||||
@@ -198,6 +207,7 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
def sum(self, call: Call) -> Type:
|
def sum(self, call: Call) -> Type:
|
||||||
return self._aggregate(
|
return self._aggregate(
|
||||||
call,
|
call,
|
||||||
|
"sum",
|
||||||
[
|
[
|
||||||
"numeric_only",
|
"numeric_only",
|
||||||
(
|
(
|
||||||
@@ -215,6 +225,7 @@ class ColumnGroupByMethodRegistry(MethodRegistry[Call]):
|
|||||||
def var(self, call: Call) -> Type:
|
def var(self, call: Call) -> Type:
|
||||||
return self._aggregate(
|
return self._aggregate(
|
||||||
call,
|
call,
|
||||||
|
"var",
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
"var",
|
"var",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import ast
|
import ast
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Callable, Optional, TypeAlias, Union
|
||||||
|
|
||||||
import midas.ast.python as p
|
import midas.ast.python as p
|
||||||
from midas.ast.location import Location
|
from midas.ast.location import Location
|
||||||
@@ -24,6 +24,31 @@ from midas.checker.types import (
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from midas.checker.python import TypedExpr
|
from midas.checker.python import TypedExpr
|
||||||
|
|
||||||
|
FormulaOperand: TypeAlias = Union["Formula", str, Type]
|
||||||
|
"""
|
||||||
|
A operand type in a :data:`Formula`
|
||||||
|
|
||||||
|
Must be one of the following:
|
||||||
|
- a nested formula
|
||||||
|
- a type name (a string)
|
||||||
|
- a type instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
Formula: TypeAlias = Union[Type, tuple[FormulaOperand, str, FormulaOperand]]
|
||||||
|
"""
|
||||||
|
A formula to compute the output type of a function
|
||||||
|
|
||||||
|
Must be either a type, or a tuple containing:
|
||||||
|
- a left operand
|
||||||
|
- an operation / method name (e.g. `"__add__"`)
|
||||||
|
- a right operand
|
||||||
|
|
||||||
|
For example, to compute the result of a `mean` function, given the input type `T`:
|
||||||
|
```python
|
||||||
|
mean_formula = ((T, "__add__", T), "__truediv__", "int")
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
class Call:
|
class Call:
|
||||||
@@ -44,6 +69,52 @@ class Call:
|
|||||||
class ColumnMethodRegistry(MethodRegistry[Call]):
|
class ColumnMethodRegistry(MethodRegistry[Call]):
|
||||||
"""The method registry for column types"""
|
"""The method registry for column types"""
|
||||||
|
|
||||||
|
def _resolve_formula_operand(self, call: Call, operand: FormulaOperand) -> Type:
|
||||||
|
"""Resolve the type of a formula operand
|
||||||
|
|
||||||
|
See :data:`FormulaOperand` for more information on the accepted format
|
||||||
|
|
||||||
|
Args:
|
||||||
|
call (Call): the call that triggered this resolution
|
||||||
|
operand (FormulaOperand): the formula operand
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Type: the type of the operand
|
||||||
|
"""
|
||||||
|
match operand:
|
||||||
|
case str():
|
||||||
|
return self.types.get_type(operand)
|
||||||
|
case (_, _, _):
|
||||||
|
return self._resolve_formula_type(call, operand)
|
||||||
|
case _:
|
||||||
|
return operand
|
||||||
|
|
||||||
|
def _resolve_formula_type(self, call: Call, formula: Formula) -> Type:
|
||||||
|
"""Resolve the return type of a formula
|
||||||
|
|
||||||
|
See :data:`Formula` for more information on the accepted format
|
||||||
|
|
||||||
|
Args:
|
||||||
|
call (Call): the call that triggered this resolution
|
||||||
|
formula (Formula): the formula to evaluate
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Type: the return type of the formula
|
||||||
|
"""
|
||||||
|
if not isinstance(formula, tuple):
|
||||||
|
return formula
|
||||||
|
|
||||||
|
op1, operator, op2 = formula
|
||||||
|
op1_type: Type = self._resolve_formula_operand(call, op1)
|
||||||
|
op2_type: Type = self._resolve_formula_operand(call, op2)
|
||||||
|
return self.typer.result_of_binary_op(
|
||||||
|
location=call.location,
|
||||||
|
expr=call.call_expr,
|
||||||
|
left=(call.column_expr, op1_type),
|
||||||
|
right=(call.column_expr, op2_type),
|
||||||
|
method=operator,
|
||||||
|
)
|
||||||
|
|
||||||
def _simple_call(self, call: Call, function: Type) -> Type:
|
def _simple_call(self, call: Call, function: Type) -> Type:
|
||||||
"""Get the result of calling a simple method
|
"""Get the result of calling a simple method
|
||||||
|
|
||||||
@@ -274,7 +345,7 @@ class ColumnMethodRegistry(MethodRegistry[Call]):
|
|||||||
call: Call,
|
call: Call,
|
||||||
kwargs: list[Function.Parameter] = [],
|
kwargs: list[Function.Parameter] = [],
|
||||||
*,
|
*,
|
||||||
preserve_inner_type: bool = False,
|
formula: Optional[Callable[[Type], Formula]] = None,
|
||||||
) -> Type:
|
) -> Type:
|
||||||
"""Compute the result type of an aggregate method call
|
"""Compute the result type of an aggregate method call
|
||||||
|
|
||||||
@@ -282,13 +353,23 @@ class ColumnMethodRegistry(MethodRegistry[Call]):
|
|||||||
call (Call): the call object
|
call (Call): the call object
|
||||||
kwargs (list[Function.Parameter], optional): a list of extra
|
kwargs (list[Function.Parameter], optional): a list of extra
|
||||||
keyword-only parameters. Defaults to [].
|
keyword-only parameters. Defaults to [].
|
||||||
preserve_inner_type (bool, optional): If `True`, the result type
|
formula (Callable[[Type], Formula], optional): optional formula
|
||||||
will preserve the column's inner type (e.g. for `min`/`max`),
|
builder function to compute the return type. If set, the function
|
||||||
otherwise the inner type is widened to `TopType`. Defaults to False.
|
should accept the inner column type and return a formula.
|
||||||
|
If `None`, the result is typed as `Column[Any]`. Defaults to None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Type: the result type
|
Type: the result type
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
returns: Type = ColumnType(type=TopType())
|
||||||
|
if formula:
|
||||||
|
returns = ColumnType(
|
||||||
|
type=self._resolve_formula_type(
|
||||||
|
call,
|
||||||
|
formula(call.column.type),
|
||||||
|
)
|
||||||
|
)
|
||||||
signature = Function(
|
signature = Function(
|
||||||
params=ParamSpec(
|
params=ParamSpec(
|
||||||
kw=[
|
kw=[
|
||||||
@@ -301,7 +382,7 @@ class ColumnMethodRegistry(MethodRegistry[Call]):
|
|||||||
*kwargs,
|
*kwargs,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
returns=call.column if preserve_inner_type else ColumnType(type=TopType()),
|
returns=returns,
|
||||||
)
|
)
|
||||||
|
|
||||||
result: CallResult = self.dispatcher.get_result(
|
result: CallResult = self.dispatcher.get_result(
|
||||||
@@ -318,27 +399,29 @@ class ColumnMethodRegistry(MethodRegistry[Call]):
|
|||||||
|
|
||||||
@method()
|
@method()
|
||||||
def max(self, call: Call) -> Type:
|
def max(self, call: Call) -> Type:
|
||||||
return self._aggregate(call, preserve_inner_type=True)
|
return self._aggregate(call, formula=lambda t: t)
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def mean(self, call: Call) -> Type:
|
def mean(self, call: Call) -> Type:
|
||||||
return self._aggregate(call)
|
return self._aggregate(
|
||||||
|
call, formula=lambda t: ((t, "__add__", t), "__truediv__", "int")
|
||||||
|
)
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def median(self, call: Call) -> Type:
|
def median(self, call: Call) -> Type:
|
||||||
return self._aggregate(call, preserve_inner_type=True)
|
return self._aggregate(call, formula=lambda t: t)
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def min(self, call: Call) -> Type:
|
def min(self, call: Call) -> Type:
|
||||||
return self._aggregate(call, preserve_inner_type=True)
|
return self._aggregate(call, formula=lambda t: t)
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def mode(self, call: Call) -> Type:
|
def mode(self, call: Call) -> Type:
|
||||||
return self._aggregate(call, preserve_inner_type=True)
|
return self._aggregate(call, formula=lambda t: t)
|
||||||
|
|
||||||
@method("product", "prod")
|
@method("product", "prod")
|
||||||
def product(self, call: Call) -> Type:
|
def product(self, call: Call) -> Type:
|
||||||
return self._aggregate(call)
|
return self._aggregate(call, formula=lambda t: (t, "__mul__", t))
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def std(self, call: Call) -> Type:
|
def std(self, call: Call) -> Type:
|
||||||
@@ -356,7 +439,7 @@ class ColumnMethodRegistry(MethodRegistry[Call]):
|
|||||||
|
|
||||||
@method()
|
@method()
|
||||||
def sum(self, call: Call) -> Type:
|
def sum(self, call: Call) -> Type:
|
||||||
return self._aggregate(call)
|
return self._aggregate(call, formula=lambda t: (t, "__add__", t))
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def var(self, call: Call) -> Type:
|
def var(self, call: Call) -> Type:
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ span {
|
|||||||
&.error {
|
&.error {
|
||||||
--col: 255, 0, 0;
|
--col: 255, 0, 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.warning {
|
&.warning {
|
||||||
--col: 250, 160, 0;
|
--col: 250, 160, 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.info {
|
&.info {
|
||||||
--col: 150, 190, 250;
|
--col: 150, 190, 250;
|
||||||
}
|
}
|
||||||
@@ -19,12 +21,12 @@ span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover:not(:has(.with-msg:hover)) {
|
&:hover:not(:has(.with-msg:hover)) {
|
||||||
.message {
|
&>.message {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.message {
|
&>.message {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(100% + 0.2em);
|
top: calc(100% + 0.2em);
|
||||||
left: -.2em;
|
left: -.2em;
|
||||||
@@ -33,7 +35,8 @@ span {
|
|||||||
padding: 0.2em 0.4em;
|
padding: 0.2em 0.4em;
|
||||||
border-radius: .2em;
|
border-radius: .2em;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
width: 300%;
|
width: max-content;
|
||||||
|
max-width: 60vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2296,14 +2296,18 @@
|
|||||||
"index": 0,
|
"index": 0,
|
||||||
"name": "a",
|
"name": "a",
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "float"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"index": 1,
|
"index": 1,
|
||||||
"name": "b",
|
"name": "b",
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "float"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -2517,14 +2521,18 @@
|
|||||||
"index": 0,
|
"index": 0,
|
||||||
"name": "a",
|
"name": "a",
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "int"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"index": 1,
|
"index": 1,
|
||||||
"name": "b",
|
"name": "b",
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "float"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -2659,14 +2667,18 @@
|
|||||||
"index": 0,
|
"index": 0,
|
||||||
"name": "a",
|
"name": "a",
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "int"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"index": 1,
|
"index": 1,
|
||||||
"name": "b",
|
"name": "b",
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "float"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -3687,7 +3699,9 @@
|
|||||||
"keywords": {}
|
"keywords": {}
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "float"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3841,7 +3855,9 @@
|
|||||||
"keywords": {}
|
"keywords": {}
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "int"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3878,7 +3894,9 @@
|
|||||||
"keywords": {}
|
"keywords": {}
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "int"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3952,7 +3970,9 @@
|
|||||||
"keywords": {}
|
"keywords": {}
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "int"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -4167,7 +4187,9 @@
|
|||||||
"keywords": {}
|
"keywords": {}
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "float"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -4288,7 +4310,9 @@
|
|||||||
"keywords": {}
|
"keywords": {}
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "int"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -4366,7 +4390,9 @@
|
|||||||
"keywords": {}
|
"keywords": {}
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": {}
|
"type": {
|
||||||
|
"name": "int"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user