Skip to content

Commit

Permalink
number type should no longer differentiate between integer and decima…
Browse files Browse the repository at this point in the history
…l notation when used in indexes. (Issue #6526, PR #6686)

# Description

- make number behave like float. The number(x) cast should be verified to work so it can be used to assign int values to number types.

- create the float alias for number because it more clearly conveys what it means. Update documentation to use float rather than number.

- add testcases for following scenario and fix it
```
entity A:
    number x
end

implement A using std::none

index A(x)

std::print(A(x=0) == A(x=0.0))
```
-  index lookup on float with int should work

closes #6526

# Self Check:

Strike through any lines that are not applicable (`~~line~~`) then check the box

- [x] Attached issue to pull request
- [x] Changelog entry
- [x] Type annotations are present
- [x] Code is clear and sufficiently documented
- [ ] No (preventable) type errors (check using make mypy or make mypy-diff)
- [x] Sufficient test cases (reproduces the bug/tests the requested feature)
- [x] Correct, in line with design
- [x] End user documentation is included or an issue is created for end-user documentation (add ref to issue here: )
- [ ] If this PR fixes a race condition in the test suite, also push the fix to the relevant stable branche(s) (see [test-fixes](https://internal.inmanta.com/development/core/tasks/build-master.html#test-fixes) for more info)
  • Loading branch information
FloLey authored and inmantaci committed Nov 22, 2023
1 parent 42e87ea commit e026d74
Show file tree
Hide file tree
Showing 29 changed files with 378 additions and 85 deletions.
@@ -0,0 +1,8 @@
---
description: number type should no longer differentiate between integer and decimal notation when used in indexes.
issue-nr: 6526
issue-repo: inmanta-core
change-type: minor
destination-branches: [master]
sections:
feature: "Introduce an alias 'float' for the 'number' type in the inmanta language"
24 changes: 18 additions & 6 deletions docs/language.rst
Expand Up @@ -92,8 +92,8 @@ Literal values can be assigned to variables

.. code-block:: inmanta
var1 = 1 # assign an integer, var1 contains now a number
var2 = 3.14 # assign a float, var2 also contains a number
var1 = 1 # assign an integer, var1 contains now an integer
var2 = 3.14 # assign a float, var2 now contains a float
var3 = "This is a string" # var3 contains a string
var4 = r"This is a raw string" # var4 contains a raw string
Expand Down Expand Up @@ -125,14 +125,18 @@ Literal values can be assigned to variables
Primitive types
==============================

The basic primitive types are ``string``, ``number``, ``int`` or ``bool``. These basic types also support type casts:
The basic primitive types are ``string``, ``float``, ``int`` or ``bool``. These basic types also support type casts:

.. note::
For legacy reason the ``number`` type exists. It is just an alias for ``float``


.. code-block:: inmanta
assert = true
assert = int("1") == 1
assert = number("1.2") == 1.2
assert = number(true) == 1
assert = float("1.2") == 1.2
assert = float(true) == 1
assert = bool(1.2) == true
assert = bool(0) == false
assert = bool(null) == false
Expand Down Expand Up @@ -164,7 +168,7 @@ For example
typedef mac_addr as string matching /([0-9a-fA-F]{2})(:[0-9a-fA-F]{2}){5}$/
Lists of primitive types are also primitive types: ``string[]``, ``number[]``, ``bool[]`` or ``mac_addr[]``
Lists of primitive types are also primitive types: ``string[]``, ``float[]``, ``bool[]`` or ``mac_addr[]``

``dict`` is the primitive type that represents a dictionary, with string keys. Dict values can be accessed using the ``[]`` operator. All members of a dict have to be set when the dict is constructed. e.g.

Expand Down Expand Up @@ -701,6 +705,14 @@ For indices on relations (instead of attributes) an alternative syntax can be us
b = vm1.files[path="/etc/passwd"] # selector style index lookup
# a == b
.. note::
The use of ``float`` (or ``number``) as part of index properties is
generally discouraged. This is due to the reliance of index matching on precise equality,
while floating-point numbers are represented with an inherent imprecision.
If floating-point attributes are used in an index, it is crucial to handle arithmetic
operations with caution to ensure the accuracy of the attribute values for index operations.



For loop
=========
Expand Down
2 changes: 1 addition & 1 deletion docs/lsm/attributes_metadata/attributes_metadata.rst
Expand Up @@ -161,7 +161,7 @@ The example below illustrates how the annotation ``annotation=value`` can be att
end
entity Port extends lsm::EmbeddedEntity:
number id
int id
end
__annotations__ = lsm::RelationAnnotations(
Expand Down
14 changes: 7 additions & 7 deletions docs/model_developers/model_debugging.rst
Expand Up @@ -173,7 +173,7 @@ since any data that flows to the equivalence will also flow to ``x``.
:linenos:
entity A:
number n
int n
end
implement A using std::none
Expand Down Expand Up @@ -230,7 +230,7 @@ Let's have a look at an implementation:
:linenos:
entity A:
number n
int n
end
implement A using i
Expand Down Expand Up @@ -270,8 +270,8 @@ And finally, an index:
:linenos:
entity A:
number n
number m
int n
int m
end
index A(n)
Expand Down Expand Up @@ -315,7 +315,7 @@ For example, compiling the model below results in three errors, one for each of
:linenos:
entity A:
number n
int n
end
implement A using std::none
Expand Down Expand Up @@ -369,15 +369,15 @@ Let's have a look at the model below:
entity Port:
string host
number portn
int portn
end
index Port(host, portn)
entity Service:
string name
string host
number portn
int portn
end
Service.port [0:1] -- Port.service [0:1]
Expand Down
14 changes: 12 additions & 2 deletions src/inmanta/ast/entity.py
Expand Up @@ -33,7 +33,7 @@
)
from inmanta.ast.blocks import BasicBlock
from inmanta.ast.statements.generator import SubConstructor
from inmanta.ast.type import NamedType, Type
from inmanta.ast.type import Float, NamedType, Number, Type
from inmanta.execute.runtime import Instance, QueueScheduler, Resolver, dataflow
from inmanta.execute.util import AnyType

Expand Down Expand Up @@ -382,6 +382,7 @@ def add_to_index(self, instance: Instance) -> None:
been set
"""
attributes = {k: repr(v.get_value()) for (k, v) in instance.slots.items() if v.is_ready()}

# check if an index entry can be added
for index_attributes in self.get_indices():
index_ok = True
Expand Down Expand Up @@ -428,7 +429,16 @@ def lookup_index(
stmt, self.get_full_name(), "No index defined on %s for this lookup: " % self.get_full_name() + str(params)
)

key = ", ".join(["%s=%s" % (k, repr(v)) for (k, v) in sorted(params, key=lambda x: x[0])])
key = ", ".join(
[
"%s=%s"
% (
k,
repr(self.get_attribute(k).type.cast(v) if isinstance(self.get_attribute(k).type, (Float, Number)) else v),
)
for k, v in sorted(params, key=lambda x: x[0])
]
)

if target is None:
if key in self._index:
Expand Down
21 changes: 15 additions & 6 deletions src/inmanta/ast/type.py
Expand Up @@ -229,15 +229,12 @@ def __eq__(self, other: object) -> bool:
@stable_api
class Number(Primitive):
"""
This class represents an integer or float in the configuration model. On
these numbers the following operations are supported:
+, -, /, *
This class represents a float in the configuration model.
"""

def __init__(self) -> None:
Primitive.__init__(self)
self.try_cast_functions: Sequence[Callable[[Optional[object]], numbers.Number]] = [int, float]
self.try_cast_functions: Sequence[Callable[[Optional[object]], numbers.Number]] = [float]

def validate(self, value: Optional[object]) -> bool:
"""
Expand Down Expand Up @@ -265,6 +262,17 @@ def type_string_internal(self) -> str:
return self.type_string()


@stable_api
class Float(Number):
"""
This class is an alias for the Number class and represents a float in
the configuration model.
"""

def type_string(self) -> str:
return "float"


@stable_api
class Integer(Number):
"""
Expand Down Expand Up @@ -584,7 +592,7 @@ class Literal(Union):
"""

def __init__(self) -> None:
Union.__init__(self, [NullableType(Number()), Bool(), String(), TypedList(self), TypedDict(self)])
Union.__init__(self, [NullableType(Integer()), Float(), Bool(), String(), TypedList(self), TypedDict(self)])

def type_string_internal(self) -> str:
return "Literal"
Expand Down Expand Up @@ -688,6 +696,7 @@ def function(*args, **kwargs):

TYPES: typing.Dict[str, Type] = { # Part of the stable API
"string": String(),
"float": Float(),
"number": Number(),
"int": Integer(),
"bool": Bool(),
Expand Down
4 changes: 3 additions & 1 deletion src/inmanta/execute/runtime.py
Expand Up @@ -32,7 +32,7 @@
OptionalValueException,
RuntimeException,
)
from inmanta.ast.type import Type
from inmanta.ast.type import Float, Number, Type
from inmanta.execute import dataflow, proxy
from inmanta.execute.dataflow import DataflowGraph
from inmanta.execute.tracking import Tracker
Expand Down Expand Up @@ -267,6 +267,8 @@ def set_value(self, value: T, location: Location, recur: bool = True) -> None:
return
if not isinstance(value, Unknown) and self.type is not None:
self.type.validate(value)
if isinstance(self.type, (Float, Number)):
value = self.type.cast(value)
self.value = value
self.location = location
self.hasValue = True
Expand Down
8 changes: 4 additions & 4 deletions tests/compiler/dataflow/test_model_assignment.py
Expand Up @@ -104,7 +104,7 @@ def test_dataflow_model_attribute_assignment_responsible(dataflow_test_helper: D
dataflow_test_helper.compile(
"""
entity Test:
number n
int n
end
implement Test using std::none
Expand Down Expand Up @@ -204,7 +204,7 @@ def test_dataflow_model_assignment_from_attribute(dataflow_test_helper: Dataflow
dataflow_test_helper.compile(
"""
entity A:
number n
int n
end
implement A using std::none
Expand Down Expand Up @@ -232,7 +232,7 @@ def test_dataflow_model_assignment_outside_constructor(dataflow_test_helper: Dat
dataflow_test_helper.compile(
"""
entity A:
number n
int n
end
implement A using std::none
Expand Down Expand Up @@ -268,7 +268,7 @@ def test_dataflow_model_result_variable(dataflow_test_helper: DataflowTestHelper
dataflow_test_helper.compile(
"""
entity A:
number n
int n
end
index A(n)
Expand Down
8 changes: 4 additions & 4 deletions tests/compiler/dataflow/test_model_datatrace.py
Expand Up @@ -192,11 +192,11 @@
"implementation",
"""
entity A:
number n
int n
end
entity B:
number n
int n
end
implementation ia for A:
Expand Down Expand Up @@ -260,8 +260,8 @@
"index match double assignment",
"""
entity A:
number n
number m
int n
int m
end
index A(n)
Expand Down
4 changes: 2 additions & 2 deletions tests/compiler/dataflow/test_model_dynamic_scope.py
Expand Up @@ -31,11 +31,11 @@ def test_dataflow_model_implementation_assignment_from_self(dataflow_test_helper
dataflow_test_helper.compile(
"""
entity A:
number n
int n
end
entity B:
number n
int n
end
A.b [1] -- B
Expand Down
14 changes: 7 additions & 7 deletions tests/compiler/dataflow/test_model_graphic.py
Expand Up @@ -193,9 +193,9 @@ def test_dataflow_graphic_instance(graphic_asserter: GraphicAsserter) -> None:
graphic_asserter(
"""
entity A:
number l
number m
number n
int l
int m
int n
end
implement A using std::none
Expand Down Expand Up @@ -329,8 +329,8 @@ def test_dataflow_graphic_implementation(graphic_asserter: GraphicAsserter) -> N
graphic_asserter(
"""
entity A:
number m
number n
int m
int n
end
implement A using i
Expand Down Expand Up @@ -393,8 +393,8 @@ def test_dataflow_graphic_index(graphic_asserter: GraphicAsserter) -> None:
graphic_asserter(
"""
entity A:
number m
number n
int m
int n
end
index A(n)
Expand Down
4 changes: 2 additions & 2 deletions tests/compiler/dataflow/test_model_index.py
Expand Up @@ -29,8 +29,8 @@ def test_dataflow_model_index_resultvariable_binding(dataflow_test_helper: Dataf
dataflow_test_helper.compile(
"""
entity A:
number n
number m
int n
int m
end
index A(n)
Expand Down

0 comments on commit e026d74

Please sign in to comment.