From ac273e67dca41278b10f3ad9e27492b51c1637d8 Mon Sep 17 00:00:00 2001 From: Marko Ristin-Kaufmann Date: Mon, 2 Nov 2026 11:06:57 +0100 Subject: [PATCH] Resolve negative indices at run time In C++, C#, Java and Go, a negative list index known only at run time was not counted from the back as in Python, and C++ ``xs[-1]`` on an empty list was undefined behavior via ``.back()``. We now route all list and JSON-array indices, literals included, through a per-target helper which resolves a negative index and throws when out of range so that the behavior in all targets is equal to that of Python: * C++: ``common::At(xs, i)``, * C#: ``Common.ListHelpers.At(xs, i)`` and ``SetAt(xs, i, v)``, * Java: ``ListHelpers.at(xs, i)`` and ``ListHelpers.setAt(xs, i, v)``, * Go: ``ourcommon.At(xs, i)`` and ``*ourcommon.PointerAt(xs, i) = v``. Go writes go through a pointer since Go can not infer ``T`` in ``SetAt[T]([]T, int64, T)`` for a ``*Class`` written into an ``[]IClass``. TypeScript ``OurCommon.at`` now throws a ``RangeError`` instead of returning ``undefined``. The helpers are generated only if the meta-model indexes a list. We test reads and writes at negative run-time indices in the common meta-model ``int_length_interplay``. --- aas_core_codegen/cpp/lib/_generate_common.py | 35 +++++ aas_core_codegen/cpp/transpilation.py | 27 ++-- .../csharp/lib/_generate_common.py | 120 ++++++++++++++ aas_core_codegen/csharp/transpilation.py | 147 +++++++++++------- .../golang/lib/_generate_common.py | 39 +++++ aas_core_codegen/golang/transpilation.py | 120 ++++++++++---- aas_core_codegen/intermediate/uses.py | 50 ++++++ aas_core_codegen/java/lib/_generate_common.py | 130 ++++++++++++++++ aas_core_codegen/java/transpilation.py | 133 +++++++--------- .../typescript/lib/_generate_common.py | 20 ++- .../int_length_interplay.py | 19 +++ .../expected_symbol_table.txt | 63 ++++++++ .../Something/numbers_not_ascending.json | 12 ++ .../something/numbers_not_ascending.xml | 1 + .../include/aas_core/aas_3_0/common.hpp | 28 ++++ .../expected_output/src/verification.cpp | 28 ++-- .../expected_output/include/dummy/common.hpp | 32 ++++ .../expected_output/src/verification.cpp | 34 ++-- .../expected_output/include/dummy/common.hpp | 28 ++++ .../expected_output/src/verification.cpp | 10 +- .../expected_output/include/dummy/common.hpp | 28 ++++ .../include/dummy/verification.hpp | 8 + .../expected_output/src/types.cpp | 2 +- .../expected_output/src/verification.cpp | 45 +++++- .../expected_output/include/dummy/common.hpp | 28 ++++ .../expected_output/src/verification.cpp | 6 +- .../expected_output/include/dummy/common.hpp | 28 ++++ .../expected_output/src/verification.cpp | 2 +- .../expected_output/include/dummy/common.hpp | 28 ++++ .../expected_output/src/verification.cpp | 6 +- .../expected_output/include/dummy/common.hpp | 28 ++++ .../expected_output/src/types.cpp | 8 +- .../expected_output/include/dummy/common.hpp | 28 ++++ .../include/dummy/verification.hpp | 4 +- .../expected_output/AasCore.Aas3_0/common.cs | 57 +++++++ .../AasCore.Aas3_0/verification.cs | 28 ++-- .../expected_output/dummy/common.cs | 57 +++++++ .../expected_output/dummy/verification.cs | 46 ++++-- .../expected_output/dummy/common.cs | 57 +++++++ .../expected_output/dummy/verification.cs | 10 +- .../expected_output/dummy/common.cs | 57 +++++++ .../expected_output/dummy/types.cs | 2 +- .../expected_output/dummy/verification.cs | 47 +++++- .../expected_output/dummy/common.cs | 74 +++++++++ .../expected_output/dummy/verification.cs | 6 +- .../expected_output/dummy/common.cs | 57 +++++++ .../expected_output/dummy/verification.cs | 2 +- .../expected_output/dummy/common.cs | 57 +++++++ .../expected_output/dummy/verification.cs | 6 +- .../expected_output/dummy/common.cs | 57 +++++++ .../expected_output/dummy/types.cs | 8 +- .../expected_output/dummy/common.cs | 57 +++++++ .../expected_output/dummy/verification.cs | 4 +- .../expected_output/common/common.go | 22 +++ .../verification/verification.go | 28 ++-- .../expected_output/common/common.go | 22 +++ .../verification/verification.go | 34 ++-- .../expected_output/common/common.go | 22 +++ .../verification/verification.go | 10 +- .../expected_output/common/common.go | 22 +++ .../expected_output/types/types.go | 6 +- .../verification/verification.go | 46 +++++- .../expected_output/common/common.go | 22 +++ .../verification/verification.go | 6 +- .../expected_output/common/common.go | 22 +++ .../verification/verification.go | 2 +- .../expected_output/common/common.go | 22 +++ .../verification/verification.go | 6 +- .../expected_output/common/common.go | 22 +++ .../expected_output/types/types.go | 8 +- .../expected_output/common/common.go | 22 +++ .../verification/verification.go | 4 +- .../aas_core/aas3_0/common/ListHelpers.java | 63 ++++++++ .../aas3_0/verification/Verification.java | 28 ++-- .../main/java/dummy/common/ListHelpers.java | 63 ++++++++ .../java/dummy/verification/Verification.java | 36 ++--- .../main/java/dummy/common/ListHelpers.java | 63 ++++++++ .../java/dummy/verification/Verification.java | 10 +- .../main/java/dummy/common/ListHelpers.java | 63 ++++++++ .../main/java/dummy/types/impl/Something.java | 2 +- .../java/dummy/verification/Verification.java | 42 +++-- .../main/java/dummy/common/ListHelpers.java | 83 ++++++++++ .../java/dummy/verification/Verification.java | 6 +- .../main/java/dummy/common/ListHelpers.java | 63 ++++++++ .../java/dummy/verification/Verification.java | 2 +- .../main/java/dummy/common/ListHelpers.java | 63 ++++++++ .../java/dummy/verification/Verification.java | 6 +- .../main/java/dummy/common/ListHelpers.java | 63 ++++++++ .../src/main/java/dummy/types/impl/First.java | 4 +- .../main/java/dummy/types/impl/Second.java | 4 +- .../main/java/dummy/common/ListHelpers.java | 63 ++++++++ .../java/dummy/verification/Verification.java | 4 +- .../expected_output/dummy/verification.py | 26 ++++ .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../arithmetic/expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../comparisons/expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../empty_class/expected_output/src/common.ts | 20 ++- .../enum/expected_output/src/common.ts | 20 ++- .../float_only/expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/verification.ts | 31 ++++ .../int_parsing/expected_output/src/common.ts | 20 ++- .../is_instance/expected_output/src/common.ts | 20 ++- .../json_types/expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../sets/expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../tuples/expected_output/src/common.ts | 20 ++- .../unions/expected_output/src/common.ts | 20 ++- .../expected_output/src/common.ts | 20 ++- .../visibility/expected_output/src/common.ts | 20 ++- .../expected_symbol_table.txt | 141 +++++++++++++++++ 136 files changed, 3364 insertions(+), 747 deletions(-) create mode 100644 dev/test_data/live_tests/common_test_data/int_length_interplay/Json/Unexpected/Invalid/InvariantViolation/Something/numbers_not_ascending.json create mode 100644 dev/test_data/live_tests/common_test_data/int_length_interplay/Xml/Unexpected/Invalid/InvariantViolation/something/numbers_not_ascending.xml create mode 100644 dev/test_data/main/java/expected/aas_core_meta.v3/expected_output/src/main/java/aas_core/aas3_0/common/ListHelpers.java create mode 100644 dev/test_data/main/java/expected/assignment_targets/expected_output/src/main/java/dummy/common/ListHelpers.java create mode 100644 dev/test_data/main/java/expected/for_statements/expected_output/src/main/java/dummy/common/ListHelpers.java create mode 100644 dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/common/ListHelpers.java create mode 100644 dev/test_data/main/java/expected/json_types_in_invariants/expected_output/src/main/java/dummy/common/ListHelpers.java create mode 100644 dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/common/ListHelpers.java create mode 100644 dev/test_data/main/java/expected/nested_collections/expected_output/src/main/java/dummy/common/ListHelpers.java create mode 100644 dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/common/ListHelpers.java create mode 100644 dev/test_data/main/java/expected/variance_of_containers/expected_output/src/main/java/dummy/common/ListHelpers.java diff --git a/aas_core_codegen/cpp/lib/_generate_common.py b/aas_core_codegen/cpp/lib/_generate_common.py index 8cbad2db8..c975f1715 100644 --- a/aas_core_codegen/cpp/lib/_generate_common.py +++ b/aas_core_codegen/cpp/lib/_generate_common.py @@ -1291,6 +1291,38 @@ def _generate_string_helper_definitions( ) +#: Define the access to an item of a list or a JSON array as Python does it +_AT_DEFINITION: Final[Stripped] = Stripped( + f"""\ +/** + * \\brief Access the item of \\p container at \\p index as Python does. + * + * A negative \\p index counts from the back of the \\p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \\param container to be indexed, *e.g.*, a list or a JSON array + * \\param index of the item; negative counts from the back + * \\return reference to the item + * \\throw std::out_of_range if \\p index is out of range + */ +template +auto At( +{I}ContainerT&& container, +{I}int64_t index +) -> decltype(container.at(std::size_t())) {{ +{I}if (index < 0) {{ +{II}index += static_cast(container.size()); +{I}}} + +{I}// NOTE (mristin): +{I}// An index which is still negative wraps around to a huge size, so that +{I}// `at` throws. +{I}return container.at(static_cast(index)); +}}""" +) + # fmt: off @ensure( lambda result: @@ -1420,6 +1452,7 @@ def generate_header( # and the transpiled code converts the result of ``LenStr`` to ``int64_t``. # The helpers ``FloorMod`` and ``ParseSafeInt`` return ``int64_t``'s as well. uses_string_helpers = _uses_string_helpers(type_inference) + uses_list_indexing = intermediate_uses.list_indexing(type_inference) extra_std_includes: List[str] = [] if ( @@ -1428,6 +1461,7 @@ def generate_header( or intermediate_uses.string_find_call(type_inference) or intermediate_uses.modulo(symbol_table) or intermediate_uses.safe_integer_checks(symbol_table) + or uses_list_indexing ): extra_std_includes.append("#include ") @@ -1671,6 +1705,7 @@ def generate_header( {I}) != container_end; }}""" ), + *([_AT_DEFINITION] if uses_list_indexing else []), Stripped( """\ /** diff --git a/aas_core_codegen/cpp/transpilation.py b/aas_core_codegen/cpp/transpilation.py index b6d9c9b5a..8200ec042 100644 --- a/aas_core_codegen/cpp/transpilation.py +++ b/aas_core_codegen/cpp/transpilation.py @@ -544,36 +544,31 @@ def _declaration(self, definition: parse_tree.Node) -> cpp_aliasing.Declaration: def _transform_item_access( self, collection: Stripped, index_node: parse_tree.Expression ) -> Tuple[Optional[Stripped], Optional[Error]]: - """Access the item of the list ``collection`` at ``index_node``.""" + """ + Access the item of the list or JSON array ``collection`` at ``index_node``. + + We always go through ``common::At``, which resolves a negative index from + the back as Python does, so that we need not distinguish the literal indices + from the indices known only at run time. + """ index, error = self._transform_and_value_if_necessary(index_node) if error is not None: return None, error assert index is not None - index_as_int: Optional[int] = None - try: - index_as_int = int(index) - except ValueError: - pass - - if index_as_int is not None and index_as_int == -1: - return Stripped(f"{collection}.back()"), None - - if index_as_int is not None and index_as_int < -1: - index = Stripped(f"{collection}.size() - {-index_as_int}") - - if "\n" in index: + if "\n" in index or "\n" in collection: return ( Stripped( f"""\ -{collection}.at( +common::At( +{I}{indent_but_first_line(collection, I)}, {I}{indent_but_first_line(index, I)} )""" ), None, ) - return Stripped(f"{collection}.at({index})"), None + return Stripped(f"common::At({collection}, {index})"), None @ensure(lambda result: (result[0] is not None) ^ (result[1] is not None)) def _transform_mutable_path( diff --git a/aas_core_codegen/csharp/lib/_generate_common.py b/aas_core_codegen/csharp/lib/_generate_common.py index 440033350..ec550d09a 100644 --- a/aas_core_codegen/csharp/lib/_generate_common.py +++ b/aas_core_codegen/csharp/lib/_generate_common.py @@ -40,6 +40,119 @@ ) +def _generate_list_helpers(with_json_arrays: bool) -> Stripped: + """ + Generate the access to the items of lists which follows the Python implementation. + + If ``with_json_arrays`` is set, we also generate the access to the items of + JSON arrays. + """ + # NOTE (mristin): + # We read from an ``IReadOnlyList``, since both the lists and the covariant + # generic sequences implement it, while ``Nodes.JsonArray`` implements only + # ``IList``. An overload for ``IList`` would be ambiguous for a ``List``, which + # implements both. We write only into the lists, so ``SetAt`` takes + # an ``IList``. + methods: List[Stripped] = [ + Stripped( + f"""\ +/// +/// Resolve in a list of items. +/// +/// +/// Thrown if the resolved index does not fit into an int. +/// +private static int ResolveIndex(long index, int count) +{{ +{I}return checked((int)(index < 0 ? index + count : index)); +}}""" + ), + Stripped( + f"""\ +/// +/// Get the item of at . +/// +/// to get the item from +/// of the item; negative counts from the back +/// the item +/// +/// Thrown if is out of range. +/// +public static T At( +{I}System.Collections.Generic.IReadOnlyList list, +{I}long index +) +{{ +{I}return list[ResolveIndex(index, list.Count)]; +}}""" + ), + ] + + if with_json_arrays: + methods.append( + Stripped( + f"""\ +/// +/// Get the item of at . +/// +/// to get the item from +/// of the item; negative counts from the back +/// the item +/// +/// Thrown if is out of range. +/// +public static System.Text.Json.Nodes.JsonNode? At( +{I}System.Text.Json.Nodes.JsonArray array, +{I}long index +) +{{ +{I}return array[ResolveIndex(index, array.Count)]; +}}""" + ) + ) + + methods.append( + Stripped( + f"""\ +/// +/// Set the item of at +/// to . +/// +/// to set the item in +/// of the item; negative counts from the back +/// to be set +/// +/// Thrown if is out of range. +/// +public static void SetAt( +{I}System.Collections.Generic.IList list, +{I}long index, +{I}T value +) +{{ +{I}list[ResolveIndex(index, list.Count)] = value; +}}""" + ) + ) + + methods_joined = "\n\n".join(methods) + + return Stripped( + f"""\ +/// +/// Provide the access to the items of lists which follows the Python +/// implementation, since Python is the language of the meta-model specifications. +/// +/// +/// A negative index counts from the back of the list. +/// +public static class ListHelpers +{{ +{I}{indent_but_first_line(methods_joined, I)} +}} // public static class ListHelpers""" + ) + + def _generate_string_helpers( symbol_table: intermediate.SymbolTable, type_inference: intermediate_type_inference.TypeInference, @@ -809,6 +922,13 @@ def generate( ): blocks.append(_generate_string_helpers(symbol_table, type_inference)) + if intermediate_uses.list_indexing(type_inference): + blocks.append( + _generate_list_helpers( + with_json_arrays=intermediate_uses.json_array_indexing(type_inference) + ) + ) + if intermediate_uses.set_properties(symbol_table): blocks.append(_generate_set_helpers(symbol_table)) diff --git a/aas_core_codegen/csharp/transpilation.py b/aas_core_codegen/csharp/transpilation.py index 26ac04877..07e837cb1 100644 --- a/aas_core_codegen/csharp/transpilation.py +++ b/aas_core_codegen/csharp/transpilation.py @@ -62,6 +62,30 @@ def _is_value_type( return isinstance(type_annotation, intermediate_type_inference.TupleTypeAnnotation) +def _call_list_helper(method: str, arguments: Sequence[Stripped]) -> Stripped: + """ + Call the ``method`` of ``Common.ListHelpers`` on the ``arguments``. + + We put the arguments on separate lines if any of them spans multiple lines, + or if the call would be too long. + """ + callee = f"{csharp_common.COMMON_CLASS}.ListHelpers.{method}" + + arguments_joined = ", ".join(arguments) + if all("\n" not in argument for argument in arguments) and ( + len(callee) + len(arguments_joined) < 70 + ): + return Stripped(f"{callee}({arguments_joined})") + + arguments_joined = ",\n".join(arguments) + return Stripped( + f"""\ +{callee}( +{I}{indent_but_first_line(arguments_joined, I)} +)""" + ) + + def generate_type( type_annotation: intermediate_type_inference.TypeAnnotationUnion, ) -> Tuple[Optional[Stripped], Optional[str]]: @@ -401,66 +425,48 @@ def transform_index( collection, error = self.transform(node.collection) if error is not None: return None, error + assert collection is not None index, error = self.transform(node.index) if error is not None: return None, error assert index is not None - index_as_int: Optional[int] = None - try: - index_as_int = int(index) - except ValueError: - pass - - if index_as_int is not None and index_as_int < 0: - index = Stripped(f"^{-index_as_int}") - - elif ( - index_as_int is None - and intermediate_type_inference.try_primitive_type( - self.type_map[node.index] - ) - is intermediate_type_inference.PrimitiveType.INT + if isinstance( + intermediate_type_inference.beneath_optional(collection_type), + intermediate_type_inference.JsonObjectTypeAnnotation, ): - # NOTE (mristin): - # The integers of the meta-model are ``long``'s in C#, while the lists - # and the JSON arrays are indexed by ``int``'s. We narrow the index - # explicitly, and fail loudly instead of silently overflowing. - if isinstance( - node.index, - ( - parse_tree.Member, - parse_tree.FunctionCall, - parse_tree.MethodCall, - parse_tree.Name, - parse_tree.Index, - ), - ): - index = Stripped(f"checked((int){index})") - else: - index = Stripped(f"checked((int)({index}))") + no_parentheses_types = ( + parse_tree.Member, + parse_tree.FunctionCall, + parse_tree.MethodCall, + parse_tree.Name, + parse_tree.Constant, + parse_tree.Index, + parse_tree.Slice, + parse_tree.IsIn, + ) - no_parentheses_types = ( - parse_tree.Member, - parse_tree.FunctionCall, - parse_tree.MethodCall, - parse_tree.Name, - parse_tree.Constant, - parse_tree.Index, - parse_tree.Slice, - parse_tree.IsIn, - ) + if not isinstance(node.collection, no_parentheses_types): + collection = Stripped(f"({collection})") - if not isinstance(node.collection, no_parentheses_types): - collection = Stripped(f"({collection})") + # NOTE (mristin): + # The indexer of a ``Nodes.JsonObject`` gives a nullable node. This is + # exactly our ``JSONValue``, which might be a JSON null even if + # the meta-model guarded the key with a membership check, so there is + # nothing to forgive. + return Stripped(f"{collection}[{index}]"), None # NOTE (mristin): - # The indexer of a ``Nodes.JsonObject`` and of a ``Nodes.JsonArray`` - # gives a nullable node. This is exactly our ``JSONValue``, which might - # be a JSON null even if the meta-model guarded the index with - # a membership or a length check, so there is nothing to forgive. - return Stripped(f"{collection}[{index}]"), None + # We always go through the helper, which resolves a negative index from + # the back as Python does, so that we need not distinguish the literal + # indices from the indices known only at run time. The indexer of + # a ``Nodes.JsonArray`` gives a nullable node, which is exactly our + # ``JSONValue``. + return ( + _call_list_helper(method="At", arguments=[collection, index]), + None, + ) @ensure(lambda result: (result[0] is not None) ^ (result[1] is not None)) def transform_slice( @@ -1930,6 +1936,12 @@ def transform_assignment( errors.append(error) target: Optional[Stripped] = None + + # NOTE (mristin): + # We set the item of a list with a call instead of an assignment, so we + # keep the collection and the index apart from the other targets. + item_location: Optional[Tuple[Stripped, Stripped]] = None + if ( isinstance(node.target, parse_tree.Name) and self._environment.find(identifier=node.target.identifier) is None @@ -1982,11 +1994,26 @@ def transform_assignment( target = Stripped(f"long {target}") else: target = Stripped(f"var {target}") + elif isinstance(node.target, parse_tree.Index): + # NOTE (mristin): + # The type inference allows only the items of a list as index targets. + # We set them with the helper, which resolves a negative index from + # the back as Python does. + collection, error = self.transform(node.target.collection) + if error is not None: + errors.append(error) + + index, error = self.transform(node.target.index) + if error is not None: + errors.append(error) + + if collection is not None and index is not None: + item_location = (collection, index) + else: # NOTE (mristin): - # The properties are settable in C#, and the index access resolves - # the negative literal indices, so we can assign to the members and - # the items of the lists directly. + # The properties are settable in C#, so we can assign to the members + # directly. target, error = self.transform(node=node.target) if error is not None: errors.append(error) @@ -1996,7 +2023,7 @@ def transform_assignment( node.original_node, "Failed to transpile the assignment", errors ) - assert target is not None + assert (target is not None) ^ (item_location is not None) assert value is not None target_type = self.type_map[node.target] @@ -2021,6 +2048,20 @@ def transform_assignment( ) value = Stripped(f"Our.{union_name}.{from_method_name}({value})") + if item_location is not None: + collection, index = item_location + return ( + Stripped( + _call_list_helper( + method="SetAt", arguments=[collection, index, value] + ) + + ";" + ), + None, + ) + + assert target is not None + # NOTE (mristin): # This is a rudimentary heuristic for basic line breaks, but works well in # practice. diff --git a/aas_core_codegen/golang/lib/_generate_common.py b/aas_core_codegen/golang/lib/_generate_common.py index 420187b22..f0895da46 100644 --- a/aas_core_codegen/golang/lib/_generate_common.py +++ b/aas_core_codegen/golang/lib/_generate_common.py @@ -501,6 +501,45 @@ def generate( for arity in range(1, golang_common.MAX_TUPLE_ARITY + 1) ) + # NOTE (mristin): + # The native index panics on a negative index, while Python counts it from + # the back, so we always index the lists and the JSON arrays with the helpers. + # + # We write through a pointer instead of passing the value to a generic setter, + # since Go 1.18 can not infer the type parameter from a slice of interfaces + # and an instance of a concrete type, *e.g.*, ``[]IClass`` and ``*Class``. + if intermediate_uses.list_indexing(type_inference): + blocks.extend( + [ + Stripped( + f"""\ +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T {{ +{I}if index < 0 {{ +{II}index += int64(len(slice)) +{I}}} +{I}return slice[index] +}}""" + ), + Stripped( + f"""\ +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T {{ +{I}if index < 0 {{ +{II}index += int64(len(slice)) +{I}}} +{I}return &slice[index] +}}""" + ), + ] + ) + # NOTE (mristin): # The Go strings count the UTF-8 bytes, while Python counts the characters # (code points). Moreover, the native slicing panics on the positions out of diff --git a/aas_core_codegen/golang/transpilation.py b/aas_core_codegen/golang/transpilation.py index 61f9053df..39da45f12 100644 --- a/aas_core_codegen/golang/transpilation.py +++ b/aas_core_codegen/golang/transpilation.py @@ -395,6 +395,38 @@ def _as_int64_position(self, node: parse_tree.Node, code: Stripped) -> Stripped: return code + def _call_list_helper( + self, + function: str, + collection: Stripped, + index_node: parse_tree.Expression, + index: Stripped, + ) -> Tuple[Optional[Stripped], Optional[Error]]: + """ + Call the list helper ``function`` on the ``collection`` at the ``index``. + + The helpers resolve a negative index from the back as Python does, so that + we need not distinguish the literal indices from the indices known only at + run time. They take the index as an ``int64``, our integers, while + the lengths are ``int``'s. + """ + index = self._as_int64_position(index_node, index) + + callee = f"ourcommon.{function}" + if "\n" not in collection and "\n" not in index: + return Stripped(f"{callee}({collection}, {index})"), None + + return ( + Stripped( + f"""\ +{callee}( +{I}{indent_but_first_line(collection, I)}, +{I}{indent_but_first_line(index, I)}, +)""" + ), + None, + ) + @ensure(lambda result: (result[0] is not None) ^ (result[1] is not None)) def _transform_as_pointer( self, node: parse_tree.Expression @@ -597,38 +629,6 @@ def transform_index( return None, error assert index is not None - index_as_int: Optional[int] = None - try: - index_as_int = int(index) - except ValueError: - pass - - if index_as_int is not None and index_as_int < 0: - if "\n" in collection: - index = Stripped( - f"""\ -len( -{I}{indent_but_first_line(collection, I)} -) - {-index_as_int}""" - ) - else: - index = Stripped(f"len({collection}) - {-index_as_int}") - - no_parentheses_types = ( - parse_tree.Member, - parse_tree.FunctionCall, - parse_tree.IsInstance, - parse_tree.MethodCall, - parse_tree.Name, - parse_tree.Constant, - parse_tree.Index, - parse_tree.Slice, - parse_tree.IsIn, - ) - - if not isinstance(node.collection, no_parentheses_types): - collection = Stripped(f"({collection})") - # NOTE (mristin): # We explicitly do *not* dereference index access. Make sure you use # :py:meth:`_transform_and_dereference_if_necessary` where appropriate. Notably, @@ -636,7 +636,29 @@ def transform_index( # references instead of values. That is why we can not simply de-reference # all the index access. - return Stripped(f"{collection}[{index}]"), None + if isinstance( + collection_type, intermediate_type_inference.JsonObjectTypeAnnotation + ): + no_parentheses_types = ( + parse_tree.Member, + parse_tree.FunctionCall, + parse_tree.IsInstance, + parse_tree.MethodCall, + parse_tree.Name, + parse_tree.Constant, + parse_tree.Index, + parse_tree.Slice, + parse_tree.IsIn, + ) + + if not isinstance(node.collection, no_parentheses_types): + collection = Stripped(f"({collection})") + + return Stripped(f"{collection}[{index}]"), None + + return self._call_list_helper( + function="At", collection=collection, index_node=node.index, index=index + ) @ensure(lambda result: (result[0] is not None) ^ (result[1] is not None)) def transform_slice( @@ -2456,6 +2478,38 @@ def transform_assignment( target, error = self.transform_name(node=node.target) if error is not None: errors.append(error) + elif ( + isinstance(node.target, parse_tree.Index) + and not self._is_pointer_map[node.target] + ): + # NOTE (mristin): + # The slices share their underlying arrays, so the assignment to an item + # of a list is visible through all the references to the list, as in + # Python. We write through the pointer to the item, which resolves + # a negative index from the back as Python does. + # + # An item which is a pointer itself is written through as any other + # pointer below, so we need no pointer to it. + collection, error = self.transform(node.target.collection) + if error is not None: + errors.append(error) + + index, error = self.transform(node.target.index) + if error is not None: + errors.append(error) + + if collection is not None and index is not None: + pointer, error = self._call_list_helper( + function="PointerAt", + collection=collection, + index_node=node.target.index, + index=index, + ) + if error is not None: + errors.append(error) + else: + assert pointer is not None + target = Stripped(f"*{pointer}") else: # NOTE (mristin): # The slices share their underlying arrays, so the assignment to an item diff --git a/aas_core_codegen/intermediate/uses.py b/aas_core_codegen/intermediate/uses.py index 0f1370175..7fccd4ed4 100644 --- a/aas_core_codegen/intermediate/uses.py +++ b/aas_core_codegen/intermediate/uses.py @@ -256,6 +256,56 @@ def string_slicing(type_inference: intermediate_type_inference.TypeInference) -> ) +def _indexes( + type_inference: intermediate_type_inference.TypeInference, + collection_types: Tuple[type, ...], +) -> bool: + """ + Check whether any transpiled code indexes a collection of ``collection_types``. + + The annotations of the annotated assignments, *e.g.*, ``List[int]``, are + indices as well, but they have no inferred type, so we skip them. + """ + for node, type_map in _over_inferred_nodes(type_inference): + if not isinstance(node, parse_tree.Index): + continue + + collection_type = type_map.get(node.collection, None) + if collection_type is not None and isinstance( + intermediate_type_inference.beneath_optional(collection_type), + collection_types, + ): + return True + + return False + + +def list_indexing(type_inference: intermediate_type_inference.TypeInference) -> bool: + """ + Check whether any transpiled code indexes a list or a JSON array. + + The transpilers resolve the negative indices from the back with a helper, + as Python does, so the index need not be known at the time of the code + generation. + """ + return _indexes( + type_inference, + ( + intermediate_type_inference.ListTypeAnnotation, + intermediate_type_inference.JsonArrayTypeAnnotation, + ), + ) + + +def json_array_indexing( + type_inference: intermediate_type_inference.TypeInference, +) -> bool: + """Check whether any transpiled code indexes a JSON array.""" + return _indexes( + type_inference, (intermediate_type_inference.JsonArrayTypeAnnotation,) + ) + + def string_find_call(type_inference: intermediate_type_inference.TypeInference) -> bool: """Check whether any transpiled code calls ``str.find``.""" return _calls_builtin_method(type_inference, intermediate_type_inference.STR_FIND) diff --git a/aas_core_codegen/java/lib/_generate_common.py b/aas_core_codegen/java/lib/_generate_common.py index 34d95ffc9..a25eaa86a 100644 --- a/aas_core_codegen/java/lib/_generate_common.py +++ b/aas_core_codegen/java/lib/_generate_common.py @@ -447,6 +447,129 @@ def _generate_string_helpers( return Stripped("\n\n".join(blocks)) +def _generate_list_helpers( + package: java_common.PackageIdentifier, with_json_arrays: bool +) -> Stripped: + """ + Generate the access to the items of lists which follows the Python implementation. + + If ``with_json_arrays`` is set, we also generate the access to the items of + JSON arrays. + """ + methods: List[Stripped] = [ + Stripped( + f"""\ +/** + * Resolve {{@code index}} in a list of {{@code size}} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ +private static int resolveIndex(long index, int size) {{ +{I}return Math.toIntExact(index < 0 ? index + size : index); +}}""" + ), + Stripped( + f"""\ +/** + * Get the item of {{@code list}} at {{@code index}}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {{@code index}} is out of range + */ +public static T at(List list, long index) {{ +{I}return list.get(resolveIndex(index, list.size())); +}}""" + ), + Stripped( + f"""\ +/** + * Set the item of {{@code list}} at {{@code index}} to {{@code value}}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {{@code index}} is out of range + */ +public static void setAt(List list, long index, T value) {{ +{I}list.set(resolveIndex(index, list.size()), value); +}}""" + ), + ] + + if with_json_arrays: + # NOTE (mristin): + # ``ArrayNode.get`` gives null for an index out of range, while Python + # raises, so we check the range ourselves. + methods.append( + Stripped( + f"""\ +/** + * Get the item of {{@code array}} at {{@code index}}. + * + * @param array to get the item from + * @param index of the item; negative counts from the back + * @return the item + * @throws IndexOutOfBoundsException if {{@code index}} is out of range + */ +public static JsonNode at(ArrayNode array, long index) {{ +{I}final int resolved = resolveIndex(index, array.size()); +{I}if (resolved < 0 || resolved >= array.size()) {{ +{II}throw new IndexOutOfBoundsException( +{III}"Index " + index + " out of range for a JSON array of size " + array.size() +{II}); +{I}}} +{I}return array.get(resolved); +}}""" + ) + ) + + methods_joined = "\n\n".join(methods) + + code = Stripped( + f"""\ +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers {{ +{I}private ListHelpers() {{ +{II}// Prevent instantiation +{I}}} + +{I}{indent_but_first_line(methods_joined, I)} +}}""" + ) + + imports: List[str] = [] + if with_json_arrays: + imports.extend( + [ + "import com.fasterxml.jackson.databind.JsonNode;", + "import com.fasterxml.jackson.databind.node.ArrayNode;", + ] + ) + imports.append("import java.util.List;") + + blocks: List[Stripped] = [ + java_common.WARNING, + Stripped(f"package {package}.common;"), + Stripped("\n".join(imports)), + code, + java_common.WARNING, + ] + + return Stripped("\n\n".join(blocks)) + + def _generate_set_helpers( package: java_common.PackageIdentifier, with_operations: bool, @@ -732,6 +855,13 @@ def generate( ) ) + if intermediate_uses.list_indexing(type_inference): + list_helpers = _generate_list_helpers( + package=package, + with_json_arrays=intermediate_uses.json_array_indexing(type_inference), + ) + files.append(java_common.JavaFile("ListHelpers.java", f"{list_helpers}\n")) + with_operations = intermediate_uses.set_operations(symbol_table) with_sorting = java_common.has_set_properties(symbol_table) if with_operations or with_sorting: diff --git a/aas_core_codegen/java/transpilation.py b/aas_core_codegen/java/transpilation.py index bb601718f..44024ed81 100644 --- a/aas_core_codegen/java/transpilation.py +++ b/aas_core_codegen/java/transpilation.py @@ -386,43 +386,46 @@ def transform_index( return None, error assert index is not None - index_as_int: Optional[int] = None - try: - index_as_int = int(index) - except ValueError: - pass - - if index_as_int is not None and index_as_int < 0: - index = Stripped(f"{collection}.size() - {abs(index_as_int)}") + assert collection is not None - elif ( - index_as_int is None - and intermediate_type_inference.try_primitive_type( - self.type_map[node.index] - ) - is intermediate_type_inference.PrimitiveType.INT + if isinstance( + collection_type, intermediate_type_inference.JsonObjectTypeAnnotation ): - # NOTE (mristin): - # We represent the integers as ``long``, while the lists are indexed - # by ``int``'s in Java. We narrow the index explicitly, and fail loudly - # instead of silently overflowing. - index = Stripped(f"Math.toIntExact({index})") + no_parentheses_types = ( + parse_tree.Member, + parse_tree.FunctionCall, + parse_tree.MethodCall, + parse_tree.Name, + parse_tree.Constant, + parse_tree.Index, + parse_tree.Slice, + parse_tree.IsIn, + ) - no_parentheses_types = ( - parse_tree.Member, - parse_tree.FunctionCall, - parse_tree.MethodCall, - parse_tree.Name, - parse_tree.Constant, - parse_tree.Index, - parse_tree.Slice, - parse_tree.IsIn, - ) + if not isinstance(node.collection, no_parentheses_types): + collection = Stripped(f"({collection})") - if not isinstance(node.collection, no_parentheses_types): - collection = Stripped(f"({collection})") + return Stripped(f"{collection}.get({index})"), None - return Stripped(f"{collection}.get({index})"), None + # NOTE (mristin): + # We always go through the helper, which resolves a negative index from + # the back as Python does, so that we need not distinguish the literal + # indices from the indices known only at run time. The helper also narrows + # the index, since we represent the integers as ``long``, while the lists + # are indexed by ``int``'s in Java. + if "\n" not in collection and "\n" not in index: + return Stripped(f"ListHelpers.at({collection}, {index})"), None + + return ( + Stripped( + f"""\ +ListHelpers.at( +{I}{indent_but_first_line(collection, I)}, +{I}{indent_but_first_line(index, I)} +)""" + ), + None, + ) @ensure(lambda result: (result[0] is not None) ^ (result[1] is not None)) def _transform_as_long( @@ -2044,11 +2047,9 @@ def transform_assignment( if error is not None: errors.append(error) - index_node: Optional[parse_tree.Expression] = None index: Optional[Stripped] = None if isinstance(node.target, parse_tree.Index): - index_node = node.target.index - index, error = self.transform(index_node) + index, error = self.transform(node.target.index) if error is not None: errors.append(error) @@ -2066,60 +2067,44 @@ def transform_assignment( value_type=self.type_map[node.value], ) - if not isinstance( - receiver_node, - ( - parse_tree.Member, - parse_tree.FunctionCall, - parse_tree.MethodCall, - parse_tree.Name, - parse_tree.Index, - ), - ): - receiver = Stripped(f"({receiver})") - - arguments: List[Stripped] = [value] + callee: str + arguments: List[Stripped] if isinstance(node.target, parse_tree.Member): - method_name = java_naming.setter_name(node.target.name) + if not isinstance( + receiver_node, + ( + parse_tree.Member, + parse_tree.FunctionCall, + parse_tree.MethodCall, + parse_tree.Name, + parse_tree.Index, + ), + ): + receiver = Stripped(f"({receiver})") + + callee = f"{receiver}.{java_naming.setter_name(node.target.name)}" + arguments = [value] else: - assert index_node is not None assert index is not None - index_as_int: Optional[int] = None - try: - index_as_int = int(index) - except ValueError: - pass - - if index_as_int is not None and index_as_int < 0: - index = Stripped(f"{receiver}.size() - {-index_as_int}") - - elif ( - index_as_int is None - and intermediate_type_inference.try_primitive_type( - self.type_map[index_node] - ) - is intermediate_type_inference.PrimitiveType.INT - ): - # NOTE (mristin): - # See the corresponding note in :py:meth:`transform_index`. - index = Stripped(f"Math.toIntExact({index})") - - method_name = Identifier("set") - arguments.insert(0, index) + # NOTE (mristin): + # The helper resolves a negative index from the back as Python + # does, and narrows the index to an ``int``. + callee = "ListHelpers.setAt" + arguments = [receiver, index, value] # NOTE (mristin): # This is a rudimentary heuristic for basic line breaks, but works well in # practice. joined_arguments = ", ".join(arguments) if "\n" not in joined_arguments and len(joined_arguments) <= 50: - return Stripped(f"{receiver}.{method_name}({joined_arguments});"), None + return Stripped(f"{callee}({joined_arguments});"), None arguments_block = ",\n".join(arguments) return ( Stripped( f"""\ -{receiver}.{method_name}( +{callee}( {I}{indent_but_first_line(arguments_block, I)});""" ), None, diff --git a/aas_core_codegen/typescript/lib/_generate_common.py b/aas_core_codegen/typescript/lib/_generate_common.py index b261db57a..055fe21dc 100644 --- a/aas_core_codegen/typescript/lib/_generate_common.py +++ b/aas_core_codegen/typescript/lib/_generate_common.py @@ -322,23 +322,27 @@ def generate( * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {{@link RangeError}} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( {I}array: Array, {I}index: number -) {{ -{I}if (index < 0) {{ -{II}return array[array.length + index]; -{I}}} else {{ -{II}return array[index]; +): T {{ +{I}const resolved = index < 0 ? array.length + index : index; +{I}if (resolved < 0 || resolved >= array.length) {{ +{II}throw new RangeError( +{III}`The index ${{index}} is out of bound ` + +{III}`for the array of length ${{array.length}}` +{II}); {I}}} +{I}return array[resolved]; }}""" ), Stripped( diff --git a/dev/test_data/common_meta_models/int_length_interplay.py b/dev/test_data/common_meta_models/int_length_interplay.py index 1e10e964c..d209010d4 100644 --- a/dev/test_data/common_meta_models/int_length_interplay.py +++ b/dev/test_data/common_meta_models/int_length_interplay.py @@ -64,12 +64,27 @@ def no_seven_from(numbers: Sequence[int], start: int) -> bool: return all(numbers[i] != 7 for i in range(start, len(numbers))) +@verification +def numbers_ascend_from_back(numbers: Sequence[int], count: int) -> bool: + """ + Check reading at the negative indices which are known only at run time. + + A negative index counts from the back of the list, as in Python. + """ + for i in range(1, count): + if numbers[-i] <= numbers[-i - 1]: + return False + + return True + + @verification def fill_texts_from_both_ends(texts: List[str], count: int, text: str) -> bool: """Check writing at the indices which are integers rather than lengths.""" for i in range(0, count): texts[i] = text texts[len(texts) - 1 - i] = text + texts[-1 - i] = text return True @@ -95,6 +110,10 @@ def set_counts(something: Mutable["Something"], texts: Sequence[str]) -> bool: return True +@invariant( + lambda self: numbers_ascend_from_back(self.numbers, len(self.numbers)), + "Numbers ascend", +) @invariant( lambda self: no_number_is_seven(self.numbers, len(self.numbers)), "No number is seven", diff --git a/dev/test_data/intermediate/common_meta_models/int_length_interplay/expected_symbol_table.txt b/dev/test_data/intermediate/common_meta_models/int_length_interplay/expected_symbol_table.txt index d36d4fb35..03994e19d 100644 --- a/dev/test_data/intermediate/common_meta_models/int_length_interplay/expected_symbol_table.txt +++ b/dev/test_data/intermediate/common_meta_models/int_length_interplay/expected_symbol_table.txt @@ -502,6 +502,31 @@ SymbolTable( original_node=...)], original_node=...)"""), specified_for='Reference to ConcreteClass Something', + parsed=...), + Invariant( + description='Numbers ascend', + body=textwrap.dedent("""\ + FunctionCall( + name='numbers_ascend_from_back', + args=[ + Member( + instance=Name( + identifier='self', + original_node=...), + name='numbers', + original_node=...), + FunctionCall( + name='len', + args=[ + Member( + instance=Name( + identifier='self', + original_node=...), + name='numbers', + original_node=...)], + original_node=...)], + original_node=...)"""), + specified_for='Reference to ConcreteClass Something', parsed=...)], serialization=Serialization( with_model_type=False), @@ -661,6 +686,44 @@ SymbolTable( postconditions=[]), parsed=..., arguments_by_name=...), + TranspilableVerification( + name='numbers_ascend_from_back', + visibility='PUBLIC', + arguments=[ + Argument( + name='numbers', + type_annotation=ListTypeAnnotation( + items=PrimitiveTypeAnnotation( + a_type='INT', + parsed=...), + parsed=...), + default=None, + mutable=False, + parsed=...), + Argument( + name='count', + type_annotation=PrimitiveTypeAnnotation( + a_type='INT', + parsed=...), + default=None, + mutable=False, + parsed=...)], + returns=PrimitiveTypeAnnotation( + a_type='BOOL', + parsed=...), + description=DescriptionOfSignature( + summary='Check reading at the negative indices which are known only at run time.', + remarks=[ + 'A negative index counts from the back of the list, as in Python.'], + arguments_by_name=[], + returns=None, + parsed=...), + contracts=Contracts( + preconditions=[], + snapshots=[], + postconditions=[]), + parsed=..., + arguments_by_name=...), TranspilableVerification( name='fill_texts_from_both_ends', visibility='PUBLIC', diff --git a/dev/test_data/live_tests/common_test_data/int_length_interplay/Json/Unexpected/Invalid/InvariantViolation/Something/numbers_not_ascending.json b/dev/test_data/live_tests/common_test_data/int_length_interplay/Json/Unexpected/Invalid/InvariantViolation/Something/numbers_not_ascending.json new file mode 100644 index 000000000..4608ffe36 --- /dev/null +++ b/dev/test_data/live_tests/common_test_data/int_length_interplay/Json/Unexpected/Invalid/InvariantViolation/Something/numbers_not_ascending.json @@ -0,0 +1,12 @@ +{ + "numbers": [ + 3, + 5, + 4 + ], + "texts": [ + "hello", + "world" + ], + "count": 2 +} diff --git a/dev/test_data/live_tests/common_test_data/int_length_interplay/Xml/Unexpected/Invalid/InvariantViolation/something/numbers_not_ascending.xml b/dev/test_data/live_tests/common_test_data/int_length_interplay/Xml/Unexpected/Invalid/InvariantViolation/something/numbers_not_ascending.xml new file mode 100644 index 000000000..82ec7b88c --- /dev/null +++ b/dev/test_data/live_tests/common_test_data/int_length_interplay/Xml/Unexpected/Invalid/InvariantViolation/something/numbers_not_ascending.xml @@ -0,0 +1 @@ +354helloworld2 \ No newline at end of file diff --git a/dev/test_data/main/cpp/expected/aas_core_meta.v3/expected_output/include/aas_core/aas_3_0/common.hpp b/dev/test_data/main/cpp/expected/aas_core_meta.v3/expected_output/include/aas_core/aas_3_0/common.hpp index fcdbba8ad..e3304f028 100644 --- a/dev/test_data/main/cpp/expected/aas_core_meta.v3/expected_output/include/aas_core/aas_3_0/common.hpp +++ b/dev/test_data/main/cpp/expected/aas_core_meta.v3/expected_output/include/aas_core/aas_3_0/common.hpp @@ -10066,6 +10066,34 @@ bool Contains( ) != container_end; } +/** + * \brief Access the item of \p container at \p index as Python does. + * + * A negative \p index counts from the back of the \p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \param container to be indexed, *e.g.*, a list or a JSON array + * \param index of the item; negative counts from the back + * \return reference to the item + * \throw std::out_of_range if \p index is out of range + */ +template +auto At( + ContainerT&& container, + int64_t index +) -> decltype(container.at(std::size_t())) { + if (index < 0) { + index += static_cast(container.size()); + } + + // NOTE (mristin): + // An index which is still negative wraps around to a huge size, so that + // `at` throws. + return container.at(static_cast(index)); +} + /** * Convert platform-independent the wide string to a UTF-8 string. * diff --git a/dev/test_data/main/cpp/expected/aas_core_meta.v3/expected_output/src/verification.cpp b/dev/test_data/main/cpp/expected/aas_core_meta.v3/expected_output/src/verification.cpp index 233086cb7..c625a4b55 100644 --- a/dev/test_data/main/cpp/expected/aas_core_meta.v3/expected_output/src/verification.cpp +++ b/dev/test_data/main/cpp/expected/aas_core_meta.v3/expected_output/src/verification.cpp @@ -1600,7 +1600,7 @@ bool IsModelReferenceTo( ( reference->type() == types::ReferenceTypes::kModelReference && reference->keys().size() != 0 - && reference->keys().back()->type() == expected_type + && common::At(reference->keys(), -1)->type() == expected_type ) ); } @@ -1613,7 +1613,7 @@ bool IsModelReferenceToReferable( reference->type() == types::ReferenceTypes::kModelReference && reference->keys().size() != 0 && constants::kAasReferables.find( - reference->keys().back()->type() + common::At(reference->keys(), -1)->type() ) != constants::kAasReferables.end() ) ); @@ -5493,7 +5493,7 @@ bool Reference_1( ); return !(that->keys().size() >= 1) || constants::kGloballyIdentifiables.find( - that->keys().at(0)->type() + common::At(that->keys(), 0)->type() ) != constants::kGloballyIdentifiables.end(); } @@ -5508,7 +5508,7 @@ bool Reference_2( && that->keys().size() >= 1 )) || constants::kGenericGloballyIdentifiables.find( - that->keys().at(0)->type() + common::At(that->keys(), 0)->type() ) != constants::kGenericGloballyIdentifiables.end(); } @@ -5523,7 +5523,7 @@ bool Reference_3( && that->keys().size() >= 1 )) || constants::kAasIdentifiables.find( - that->keys().at(0)->type() + common::At(that->keys(), 0)->type() ) != constants::kAasIdentifiables.end(); } @@ -5539,10 +5539,10 @@ bool Reference_4( )) || (( constants::kGenericGloballyIdentifiables.find( - that->keys().back()->type() + common::At(that->keys(), -1)->type() ) != constants::kGenericGloballyIdentifiables.end() || constants::kGenericFragmentKeys.find( - that->keys().back()->type() + common::At(that->keys(), -1)->type() ) != constants::kGenericFragmentKeys.end() )); } @@ -5560,7 +5560,7 @@ bool Reference_5( || common::AllRange( [&](size_t i) -> bool { return constants::kFragmentKeys.find( - that->keys().at(i)->type() + common::At(that->keys(), i)->type() ) != constants::kFragmentKeys.end(); }, 1, @@ -5581,7 +5581,7 @@ bool Reference_6( || common::AllRange( [&](size_t i) -> bool { return !(constants::kGenericFragmentKeys.find( - that->keys().at(i)->type() + common::At(that->keys(), i)->type() ) != constants::kGenericFragmentKeys.end()); }, 0, @@ -5598,11 +5598,11 @@ bool Reference_7( return !(( that->type() == types::ReferenceTypes::kModelReference && that->keys().size() > 1 - && that->keys().back()->type() == types::KeyTypes::kFragmentReference + && common::At(that->keys(), -1)->type() == types::KeyTypes::kFragmentReference )) || (( - that->keys().at(that->keys().size() - 2)->type() == types::KeyTypes::kFile - || that->keys().at(that->keys().size() - 2)->type() == types::KeyTypes::kBlob + common::At(that->keys(), -2)->type() == types::KeyTypes::kFile + || common::At(that->keys(), -2)->type() == types::KeyTypes::kBlob )); } @@ -5618,9 +5618,9 @@ bool Reference_8( )) || common::AllRange( [&](size_t i) -> bool { - return !(that->keys().at(i)->type() == types::KeyTypes::kSubmodelElementList) + return !(common::At(that->keys(), i)->type() == types::KeyTypes::kSubmodelElementList) || verification::MatchesXsNonNegativeInteger( - that->keys().at(i + (1))->value() + common::At(that->keys(), i + (1))->value() ); }, 0, diff --git a/dev/test_data/main/cpp/expected/assignment_targets/expected_output/include/dummy/common.hpp b/dev/test_data/main/cpp/expected/assignment_targets/expected_output/include/dummy/common.hpp index 6336e407c..d5640efdc 100644 --- a/dev/test_data/main/cpp/expected/assignment_targets/expected_output/include/dummy/common.hpp +++ b/dev/test_data/main/cpp/expected/assignment_targets/expected_output/include/dummy/common.hpp @@ -48,6 +48,10 @@ #pragma warning(pop) #endif +#pragma warning(push, 0) +#include +#pragma warning(pop) + namespace dummy { /** @@ -10060,6 +10064,34 @@ bool Contains( ) != container_end; } +/** + * \brief Access the item of \p container at \p index as Python does. + * + * A negative \p index counts from the back of the \p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \param container to be indexed, *e.g.*, a list or a JSON array + * \param index of the item; negative counts from the back + * \return reference to the item + * \throw std::out_of_range if \p index is out of range + */ +template +auto At( + ContainerT&& container, + int64_t index +) -> decltype(container.at(std::size_t())) { + if (index < 0) { + index += static_cast(container.size()); + } + + // NOTE (mristin): + // An index which is still negative wraps around to a huge size, so that + // `at` throws. + return container.at(static_cast(index)); +} + /** * Convert platform-independent the wide string to a UTF-8 string. * diff --git a/dev/test_data/main/cpp/expected/assignment_targets/expected_output/src/verification.cpp b/dev/test_data/main/cpp/expected/assignment_targets/expected_output/src/verification.cpp index 4ac175184..a9f45e178 100644 --- a/dev/test_data/main/cpp/expected/assignment_targets/expected_output/src/verification.cpp +++ b/dev/test_data/main/cpp/expected/assignment_targets/expected_output/src/verification.cpp @@ -85,8 +85,8 @@ bool SetFirstAndLastText( const std::shared_ptr& item, const std::wstring& text ) { - item->mutable_texts().at(0) = text; - item->mutable_texts().back() = text; + common::At(item->mutable_texts(), 0) = text; + common::At(item->mutable_texts(), -1) = text; return true; } @@ -95,7 +95,7 @@ bool SetTextThroughListAlias( const std::wstring& text ) { auto& texts = item->mutable_texts(); - texts.at(1) = text; + common::At(texts, 1) = text; return true; } @@ -103,8 +103,8 @@ bool SetNumbers( std::vector& numbers, int64_t number ) { - numbers.at(0) = number; - numbers.at(numbers.size() - 2) = numbers.back() + (1); + common::At(numbers, 0) = number; + common::At(numbers, -2) = common::At(numbers, -1) + (1); return true; } @@ -114,8 +114,8 @@ bool SetNestedText( >& items, const std::wstring& text ) { - items.at(0)->mutable_texts().back() = text; - items.back()->set_text(text); + common::At(common::At(items, 0)->mutable_texts(), -1) = text; + common::At(items, -1)->set_text(text); return true; } @@ -125,7 +125,7 @@ bool ReplaceFirstItem( >& items, const std::shared_ptr& item ) { - items.at(0) = item; + common::At(items, 0) = item; return true; } @@ -139,7 +139,7 @@ bool SetTextsInLoops( item->set_text(text); } for (size_t i = 0; i < items.size(); ++i) { - items.at(i)->mutable_texts().at(0) = text; + common::At(common::At(items, i)->mutable_texts(), 0) = text; } return true; } @@ -149,7 +149,7 @@ bool FillTexts( const std::wstring& text ) { for (size_t i = 0; i < texts.size(); ++i) { - texts.at(i) = text; + common::At(texts, i) = text; } return true; } @@ -224,7 +224,7 @@ bool SetFirstTextsOfLists( const std::wstring& text ) { for (std::vector& texts : lists) { - texts.at(0) = text; + common::At(texts, 0) = text; } return true; } @@ -240,11 +240,11 @@ bool SetFirstTextsInSiblingLoops( ) { for (const std::shared_ptr& item : items) { auto& texts = item->mutable_texts(); - texts.at(0) = text; + common::At(texts, 0) = text; } for (const std::shared_ptr& other : others) { auto& texts = other->mutable_texts(); - texts.at(0) = text; + common::At(texts, 0) = text; } return true; } @@ -261,9 +261,9 @@ bool TextCopyIsIndependent( bool NumberCopyIsIndependent( std::vector& numbers ) { - int64_t first = numbers.at(0); - numbers.at(0) = first + (1); - return (first + (1)) == (numbers.at(0)); + int64_t first = common::At(numbers, 0); + common::At(numbers, 0) = first + (1); + return (first + (1)) == (common::At(numbers, 0)); } bool SetMaybeTexts( @@ -298,7 +298,7 @@ bool FirstTextThroughAliasIs( const std::wstring& text ) { const auto& texts = item->texts(); - return texts.at(0) == text; + return common::At(texts, 0) == text; } bool TextsAreNotEmpty( diff --git a/dev/test_data/main/cpp/expected/for_statements/expected_output/include/dummy/common.hpp b/dev/test_data/main/cpp/expected/for_statements/expected_output/include/dummy/common.hpp index 5cf1c32bc..0c543f121 100644 --- a/dev/test_data/main/cpp/expected/for_statements/expected_output/include/dummy/common.hpp +++ b/dev/test_data/main/cpp/expected/for_statements/expected_output/include/dummy/common.hpp @@ -10065,6 +10065,34 @@ bool Contains( ) != container_end; } +/** + * \brief Access the item of \p container at \p index as Python does. + * + * A negative \p index counts from the back of the \p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \param container to be indexed, *e.g.*, a list or a JSON array + * \param index of the item; negative counts from the back + * \return reference to the item + * \throw std::out_of_range if \p index is out of range + */ +template +auto At( + ContainerT&& container, + int64_t index +) -> decltype(container.at(std::size_t())) { + if (index < 0) { + index += static_cast(container.size()); + } + + // NOTE (mristin): + // An index which is still negative wraps around to a huge size, so that + // `at` throws. + return container.at(static_cast(index)); +} + /** * Convert platform-independent the wide string to a UTF-8 string. * diff --git a/dev/test_data/main/cpp/expected/for_statements/expected_output/src/verification.cpp b/dev/test_data/main/cpp/expected/for_statements/expected_output/src/verification.cpp index ec8569cc0..6e32215d9 100644 --- a/dev/test_data/main/cpp/expected/for_statements/expected_output/src/verification.cpp +++ b/dev/test_data/main/cpp/expected/for_statements/expected_output/src/verification.cpp @@ -73,7 +73,7 @@ bool NoNumberIsMinusOne( const std::vector& numbers ) { for (size_t i = 0; i < numbers.size(); ++i) { - int64_t number = numbers.at(i); + int64_t number = common::At(numbers, i); if (number == -1) { return false; } @@ -85,7 +85,7 @@ bool NoNumberAfterTheFirstIsOne( const std::vector& numbers ) { for (size_t i = 1; i < numbers.size(); ++i) { - if (numbers.at(i) == 1) { + if (common::At(numbers, i) == 1) { return false; } } @@ -169,7 +169,7 @@ bool ItemsAreFewAndTextsExpected( int64_t count = 0; for (const std::shared_ptr& item : items) { for (size_t i = 0; i < item->texts().size(); ++i) { - std::wstring text = item->texts().at(i); + std::wstring text = common::At(item->texts(), i); if (text == L"unexpected") { return false; } else if (text == L"a" || text == L"b") { @@ -234,10 +234,10 @@ bool TextsBeforeStopAreFew( for (const std::shared_ptr& item : items) { for (size_t i = 0; i < item->texts().size(); ++i) { if (kind == types::Kind::kAlpha) { - if (item->texts().at(i) == L"stop") { + if (common::At(item->texts(), i) == L"stop") { break; } - } else if (common::LenStr(item->texts().at(i)) > 10) { + } else if (common::LenStr(common::At(item->texts(), i)) > 10) { break; } count = count + (1); diff --git a/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/include/dummy/common.hpp b/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/include/dummy/common.hpp index b09108de1..9a2420927 100644 --- a/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/include/dummy/common.hpp +++ b/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/include/dummy/common.hpp @@ -10064,6 +10064,34 @@ bool Contains( ) != container_end; } +/** + * \brief Access the item of \p container at \p index as Python does. + * + * A negative \p index counts from the back of the \p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \param container to be indexed, *e.g.*, a list or a JSON array + * \param index of the item; negative counts from the back + * \return reference to the item + * \throw std::out_of_range if \p index is out of range + */ +template +auto At( + ContainerT&& container, + int64_t index +) -> decltype(container.at(std::size_t())) { + if (index < 0) { + index += static_cast(container.size()); + } + + // NOTE (mristin): + // An index which is still negative wraps around to a huge size, so that + // `at` throws. + return container.at(static_cast(index)); +} + /** * Convert platform-independent the wide string to a UTF-8 string. * diff --git a/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/include/dummy/verification.hpp b/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/include/dummy/verification.hpp index 7d47dfcde..812e066c1 100644 --- a/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/include/dummy/verification.hpp +++ b/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/include/dummy/verification.hpp @@ -253,6 +253,14 @@ bool NoSevenFrom( int64_t start ); +/// \brief Check reading at the negative indices which are known only at run time. +/// +/// A negative index counts from the back of the list, as in Python. +bool NumbersAscendFromBack( + const std::vector& numbers, + int64_t count +); + /// \brief Check writing at the indices which are integers rather than lengths. bool FillTextsFromBothEnds( std::vector& texts, diff --git a/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/src/types.cpp b/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/src/types.cpp index 1be2c48b5..36f91a140 100644 --- a/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/src/types.cpp +++ b/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/src/types.cpp @@ -98,7 +98,7 @@ bool Something::IsValidIndex( std::wstring Something::TextAt( int64_t index ) const { - return this->texts().at(index); + return common::At(this->texts(), index); } // endregion Something diff --git a/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/src/verification.cpp b/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/src/verification.cpp index 0951bb21e..30a280f32 100644 --- a/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/src/verification.cpp +++ b/dev/test_data/main/cpp/expected/int_length_interplay/expected_output/src/verification.cpp @@ -66,18 +66,18 @@ bool NoNumberIsSeven( int64_t count ) { for (int64_t i = 0; i < count; ++i) { - int64_t number = numbers.at(i); + int64_t number = common::At(numbers, i); if (number == 7) { return false; } int64_t mirrored = ( - numbers.at(static_cast(numbers.size() - (1)) - i) + common::At(numbers, static_cast(numbers.size() - (1)) - i) ); if (mirrored == 7) { return false; } int64_t rotated = ( - numbers.at(common::FloorMod(i + (1), static_cast(numbers.size()))) + common::At(numbers, common::FloorMod(i + (1), static_cast(numbers.size()))) ); if (rotated == 7) { return false; @@ -97,7 +97,7 @@ bool NoSevenFrom( return true; } for (int64_t i = start; i < static_cast(numbers.size()); ++i) { - int64_t number = numbers.at(i); + int64_t number = common::At(numbers, i); if (number == 7) { return false; } @@ -105,7 +105,7 @@ bool NoSevenFrom( return ( common::AllRange( [&](int64_t i) -> bool { - return numbers.at(i) != 7; + return common::At(numbers, i) != 7; }, start, numbers.size() @@ -113,14 +113,27 @@ bool NoSevenFrom( ); } +bool NumbersAscendFromBack( + const std::vector& numbers, + int64_t count +) { + for (int64_t i = 1; i < count; ++i) { + if (common::At(numbers, -i) <= common::At(numbers, (-i) - (1))) { + return false; + } + } + return true; +} + bool FillTextsFromBothEnds( std::vector& texts, int64_t count, const std::wstring& text ) { for (int64_t i = 0; i < count; ++i) { - texts.at(i) = text; - texts.at(static_cast(texts.size() - (1)) - i) = text; + common::At(texts, i) = text; + common::At(texts, static_cast(texts.size() - (1)) - i) = text; + common::At(texts, (-1) - i) = text; } return true; } @@ -141,7 +154,7 @@ bool SetCounts( ) { something->set_count(texts.size()); something->set_maybe_count(texts.size()); - something->mutable_numbers().at(0) = texts.size(); + common::At(something->mutable_numbers(), 0) = texts.size(); return true; } @@ -268,6 +281,18 @@ bool Something_7( ); } +bool Something_8( + const void* value +) { + const types::ISomething* that = ( + static_cast(value) + ); + return verification::NumbersAscendFromBack( + that->numbers(), + that->numbers().size() + ); +} + /** * Give out the checks of the values of the \p shape. */ @@ -308,6 +333,10 @@ const std::vector& ChecksOf(Shape shape) { { &Something_7, L"No number is seven" + }, + { + &Something_8, + L"Numbers ascend" } }; return checks; diff --git a/dev/test_data/main/cpp/expected/json_types_in_invariants/expected_output/include/dummy/common.hpp b/dev/test_data/main/cpp/expected/json_types_in_invariants/expected_output/include/dummy/common.hpp index fc3a01e72..9bd6ca511 100644 --- a/dev/test_data/main/cpp/expected/json_types_in_invariants/expected_output/include/dummy/common.hpp +++ b/dev/test_data/main/cpp/expected/json_types_in_invariants/expected_output/include/dummy/common.hpp @@ -10064,6 +10064,34 @@ bool Contains( ) != container_end; } +/** + * \brief Access the item of \p container at \p index as Python does. + * + * A negative \p index counts from the back of the \p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \param container to be indexed, *e.g.*, a list or a JSON array + * \param index of the item; negative counts from the back + * \return reference to the item + * \throw std::out_of_range if \p index is out of range + */ +template +auto At( + ContainerT&& container, + int64_t index +) -> decltype(container.at(std::size_t())) { + if (index < 0) { + index += static_cast(container.size()); + } + + // NOTE (mristin): + // An index which is still negative wraps around to a huge size, so that + // `at` throws. + return container.at(static_cast(index)); +} + /** * Convert platform-independent the wide string to a UTF-8 string. * diff --git a/dev/test_data/main/cpp/expected/json_types_in_invariants/expected_output/src/verification.cpp b/dev/test_data/main/cpp/expected/json_types_in_invariants/expected_output/src/verification.cpp index 832e19c50..4be079123 100644 --- a/dev/test_data/main/cpp/expected/json_types_in_invariants/expected_output/src/verification.cpp +++ b/dev/test_data/main/cpp/expected/json_types_in_invariants/expected_output/src/verification.cpp @@ -114,7 +114,7 @@ bool Something_1( ); return !(( that->values().size() > 0 - && (that->values().at(0).is_null()) + && (common::At(that->values(), 0).is_null()) )); } @@ -145,7 +145,7 @@ bool Something_3( ); return !(that->values().size() >= 1) || verification::IsAcceptable( - that->values().back() + common::At(that->values(), -1) ); } @@ -157,7 +157,7 @@ bool Something_4( ); return !(that->values().size() > 0) || verification::IsAcceptable( - that->values().at(0) + common::At(that->values(), 0) ); } diff --git a/dev/test_data/main/cpp/expected/list_of_primitives/expected_output/include/dummy/common.hpp b/dev/test_data/main/cpp/expected/list_of_primitives/expected_output/include/dummy/common.hpp index 1436d2228..f67e4ca61 100644 --- a/dev/test_data/main/cpp/expected/list_of_primitives/expected_output/include/dummy/common.hpp +++ b/dev/test_data/main/cpp/expected/list_of_primitives/expected_output/include/dummy/common.hpp @@ -10065,6 +10065,34 @@ bool Contains( ) != container_end; } +/** + * \brief Access the item of \p container at \p index as Python does. + * + * A negative \p index counts from the back of the \p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \param container to be indexed, *e.g.*, a list or a JSON array + * \param index of the item; negative counts from the back + * \return reference to the item + * \throw std::out_of_range if \p index is out of range + */ +template +auto At( + ContainerT&& container, + int64_t index +) -> decltype(container.at(std::size_t())) { + if (index < 0) { + index += static_cast(container.size()); + } + + // NOTE (mristin): + // An index which is still negative wraps around to a huge size, so that + // `at` throws. + return container.at(static_cast(index)); +} + /** * Convert platform-independent the wide string to a UTF-8 string. * diff --git a/dev/test_data/main/cpp/expected/list_of_primitives/expected_output/src/verification.cpp b/dev/test_data/main/cpp/expected/list_of_primitives/expected_output/src/verification.cpp index c1a2f0053..f6a0e2537 100644 --- a/dev/test_data/main/cpp/expected/list_of_primitives/expected_output/src/verification.cpp +++ b/dev/test_data/main/cpp/expected/list_of_primitives/expected_output/src/verification.cpp @@ -119,7 +119,7 @@ bool FloatsAreUnique( ) { for (size_t i = 0; i < numbers.size(); ++i) { for (size_t j = i + (1); j < numbers.size(); ++j) { - if (numbers.at(i) == numbers.at(j)) { + if (common::At(numbers, i) == common::At(numbers, j)) { return false; } } diff --git a/dev/test_data/main/cpp/expected/nested_collections/expected_output/include/dummy/common.hpp b/dev/test_data/main/cpp/expected/nested_collections/expected_output/include/dummy/common.hpp index 5d51b79d4..96a69e5c1 100644 --- a/dev/test_data/main/cpp/expected/nested_collections/expected_output/include/dummy/common.hpp +++ b/dev/test_data/main/cpp/expected/nested_collections/expected_output/include/dummy/common.hpp @@ -10093,6 +10093,34 @@ bool Contains( ) != container_end; } +/** + * \brief Access the item of \p container at \p index as Python does. + * + * A negative \p index counts from the back of the \p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \param container to be indexed, *e.g.*, a list or a JSON array + * \param index of the item; negative counts from the back + * \return reference to the item + * \throw std::out_of_range if \p index is out of range + */ +template +auto At( + ContainerT&& container, + int64_t index +) -> decltype(container.at(std::size_t())) { + if (index < 0) { + index += static_cast(container.size()); + } + + // NOTE (mristin): + // An index which is still negative wraps around to a huge size, so that + // `at` throws. + return container.at(static_cast(index)); +} + /** * Convert platform-independent the wide string to a UTF-8 string. * diff --git a/dev/test_data/main/cpp/expected/nested_collections/expected_output/src/verification.cpp b/dev/test_data/main/cpp/expected/nested_collections/expected_output/src/verification.cpp index 0dd019bfc..88abfb23a 100644 --- a/dev/test_data/main/cpp/expected/nested_collections/expected_output/src/verification.cpp +++ b/dev/test_data/main/cpp/expected/nested_collections/expected_output/src/verification.cpp @@ -57,7 +57,7 @@ bool GridIsRectangular( if (grid.size() < 1) { return true; } - const std::vector& first_row = grid.at(0); + const std::vector& first_row = common::At(grid, 0); for (const std::vector& row : grid) { if (row.size() != first_row.size()) { return false; @@ -125,10 +125,10 @@ bool ResetCorner( if ( ( grid.size() > 0 - && grid.at(0).size() > 0 + && common::At(grid, 0).size() > 0 ) ) { - grid.at(0).at(0) = 0; + common::At(common::At(grid, 0), 0) = 0; } return true; } diff --git a/dev/test_data/main/cpp/expected/transpiled_methods/expected_output/include/dummy/common.hpp b/dev/test_data/main/cpp/expected/transpiled_methods/expected_output/include/dummy/common.hpp index baac79a4a..18b57086e 100644 --- a/dev/test_data/main/cpp/expected/transpiled_methods/expected_output/include/dummy/common.hpp +++ b/dev/test_data/main/cpp/expected/transpiled_methods/expected_output/include/dummy/common.hpp @@ -10065,6 +10065,34 @@ bool Contains( ) != container_end; } +/** + * \brief Access the item of \p container at \p index as Python does. + * + * A negative \p index counts from the back of the \p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \param container to be indexed, *e.g.*, a list or a JSON array + * \param index of the item; negative counts from the back + * \return reference to the item + * \throw std::out_of_range if \p index is out of range + */ +template +auto At( + ContainerT&& container, + int64_t index +) -> decltype(container.at(std::size_t())) { + if (index < 0) { + index += static_cast(container.size()); + } + + // NOTE (mristin): + // An index which is still negative wraps around to a huge size, so that + // `at` throws. + return container.at(static_cast(index)); +} + /** * Convert platform-independent the wide string to a UTF-8 string. * diff --git a/dev/test_data/main/cpp/expected/transpiled_methods/expected_output/src/types.cpp b/dev/test_data/main/cpp/expected/transpiled_methods/expected_output/src/types.cpp index dcf144b7b..9eb6f9eea 100644 --- a/dev/test_data/main/cpp/expected/transpiled_methods/expected_output/src/types.cpp +++ b/dev/test_data/main/cpp/expected/transpiled_methods/expected_output/src/types.cpp @@ -129,7 +129,7 @@ void First::FillNumbers( std::vector& numbers ) const { for (size_t i = 0; i < numbers.size(); ++i) { - numbers.at(i) = this->count(); + common::At(numbers, i) = this->count(); } } @@ -143,7 +143,7 @@ void First::FillTexts( const std::wstring& text ) { for (size_t i = 0; i < this->texts().size(); ++i) { - this->mutable_texts().at(i) = text; + common::At(this->mutable_texts(), i) = text; } } @@ -301,7 +301,7 @@ void Second::FillNumbers( std::vector& numbers ) const { for (size_t i = 0; i < numbers.size(); ++i) { - numbers.at(i) = this->count(); + common::At(numbers, i) = this->count(); } } @@ -315,7 +315,7 @@ void Second::FillTexts( const std::wstring& text ) { for (size_t i = 0; i < this->texts().size(); ++i) { - this->mutable_texts().at(i) = text; + common::At(this->mutable_texts(), i) = text; } } diff --git a/dev/test_data/main/cpp/expected/variance_of_containers/expected_output/include/dummy/common.hpp b/dev/test_data/main/cpp/expected/variance_of_containers/expected_output/include/dummy/common.hpp index 1436d2228..f67e4ca61 100644 --- a/dev/test_data/main/cpp/expected/variance_of_containers/expected_output/include/dummy/common.hpp +++ b/dev/test_data/main/cpp/expected/variance_of_containers/expected_output/include/dummy/common.hpp @@ -10065,6 +10065,34 @@ bool Contains( ) != container_end; } +/** + * \brief Access the item of \p container at \p index as Python does. + * + * A negative \p index counts from the back of the \p container. + * + * We return the reference given by `at`, so that the item can be both read and + * written. + * + * \param container to be indexed, *e.g.*, a list or a JSON array + * \param index of the item; negative counts from the back + * \return reference to the item + * \throw std::out_of_range if \p index is out of range + */ +template +auto At( + ContainerT&& container, + int64_t index +) -> decltype(container.at(std::size_t())) { + if (index < 0) { + index += static_cast(container.size()); + } + + // NOTE (mristin): + // An index which is still negative wraps around to a huge size, so that + // `at` throws. + return container.at(static_cast(index)); +} + /** * Convert platform-independent the wide string to a UTF-8 string. * diff --git a/dev/test_data/main/cpp/expected/variance_of_containers/expected_output/include/dummy/verification.hpp b/dev/test_data/main/cpp/expected/variance_of_containers/expected_output/include/dummy/verification.hpp index 3ef3749f5..28e0f167c 100644 --- a/dev/test_data/main/cpp/expected/variance_of_containers/expected_output/include/dummy/verification.hpp +++ b/dev/test_data/main/cpp/expected/variance_of_containers/expected_output/include/dummy/verification.hpp @@ -574,7 +574,7 @@ bool FirstIsSpecial( if (items.size() == 0) { return false; } - auto first = items.at(0); + auto first = common::At(items, 0); if (types::IsSpecialItem(*first)) { return ( std::dynamic_pointer_cast(first)->level() > 0 @@ -646,7 +646,7 @@ bool LastIsSpecial( if (items.size() == 0) { return false; } - const std::shared_ptr last = items.at(items.size() - (1)); + const std::shared_ptr last = common::At(items, items.size() - (1)); if (types::IsSpecialItem(*last)) { return ( std::dynamic_pointer_cast(last)->level() > 0 diff --git a/dev/test_data/main/csharp/expected/aas_core_meta.v3/expected_output/AasCore.Aas3_0/common.cs b/dev/test_data/main/csharp/expected/aas_core_meta.v3/expected_output/AasCore.Aas3_0/common.cs index e13ba8524..0f91d98f9 100644 --- a/dev/test_data/main/csharp/expected/aas_core_meta.v3/expected_output/AasCore.Aas3_0/common.cs +++ b/dev/test_data/main/csharp/expected/aas_core_meta.v3/expected_output/AasCore.Aas3_0/common.cs @@ -84,6 +84,63 @@ public static int Len(string text) return CountCharacters(text, 0, text.Length); } } // public static class StringHelpers + + ///

+ /// Provide the access to the items of lists which follows the Python + /// implementation, since Python is the language of the meta-model specifications. + /// + /// + /// A negative index counts from the back of the list. + /// + public static class ListHelpers + { + /// + /// Resolve in a list of items. + /// + /// + /// Thrown if the resolved index does not fit into an int. + /// + private static int ResolveIndex(long index, int count) + { + return checked((int)(index < 0 ? index + count : index)); + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static T At( + System.Collections.Generic.IReadOnlyList list, + long index + ) + { + return list[ResolveIndex(index, list.Count)]; + } + + /// + /// Set the item of at + /// to . + /// + /// to set the item in + /// of the item; negative counts from the back + /// to be set + /// + /// Thrown if is out of range. + /// + public static void SetAt( + System.Collections.Generic.IList list, + long index, + T value + ) + { + list[ResolveIndex(index, list.Count)] = value; + } + } // public static class ListHelpers } // public static class Common } // namespace AasCore.Aas3_0 diff --git a/dev/test_data/main/csharp/expected/aas_core_meta.v3/expected_output/AasCore.Aas3_0/verification.cs b/dev/test_data/main/csharp/expected/aas_core_meta.v3/expected_output/AasCore.Aas3_0/verification.cs index bfde32333..18f09744f 100644 --- a/dev/test_data/main/csharp/expected/aas_core_meta.v3/expected_output/AasCore.Aas3_0/verification.cs +++ b/dev/test_data/main/csharp/expected/aas_core_meta.v3/expected_output/AasCore.Aas3_0/verification.cs @@ -1957,7 +1957,7 @@ KeyTypes expectedType { return reference.Type == ReferenceTypes.ModelReference && reference.Keys.Count != 0 - && reference.Keys[^1].Type == expectedType; + && Common.ListHelpers.At(reference.Keys, -1).Type == expectedType; } // public static bool IsModelReferenceTo /// @@ -1969,7 +1969,7 @@ IReference reference { return reference.Type == ReferenceTypes.ModelReference && reference.Keys.Count != 0 - && Our.Constants.AasReferables.Contains(reference.Keys[^1].Type); + && Our.Constants.AasReferables.Contains(Common.ListHelpers.At(reference.Keys, -1).Type); } // public static bool IsModelReferenceToReferable /// @@ -7840,7 +7840,7 @@ Our.IReference that if (!( !(that.Keys.Count >= 1) - || Our.Constants.GloballyIdentifiables.Contains(that.Keys[0].Type))) + || Our.Constants.GloballyIdentifiables.Contains(Common.ListHelpers.At(that.Keys, 0).Type))) { yield return new Reporting.Error( "Invariant violated:\n" + @@ -7854,7 +7854,7 @@ Our.IReference that that.Type == ReferenceTypes.ExternalReference && that.Keys.Count >= 1 ) - || Our.Constants.GenericGloballyIdentifiables.Contains(that.Keys[0].Type))) + || Our.Constants.GenericGloballyIdentifiables.Contains(Common.ListHelpers.At(that.Keys, 0).Type))) { yield return new Reporting.Error( "Invariant violated:\n" + @@ -7868,7 +7868,7 @@ Our.IReference that that.Type == ReferenceTypes.ModelReference && that.Keys.Count >= 1 ) - || Our.Constants.AasIdentifiables.Contains(that.Keys[0].Type))) + || Our.Constants.AasIdentifiables.Contains(Common.ListHelpers.At(that.Keys, 0).Type))) { yield return new Reporting.Error( "Invariant violated:\n" + @@ -7882,8 +7882,8 @@ Our.IReference that && that.Keys.Count >= 1 ) || ( - Our.Constants.GenericGloballyIdentifiables.Contains(that.Keys[^1].Type) - || Our.Constants.GenericFragmentKeys.Contains(that.Keys[^1].Type) + Our.Constants.GenericGloballyIdentifiables.Contains(Common.ListHelpers.At(that.Keys, -1).Type) + || Our.Constants.GenericFragmentKeys.Contains(Common.ListHelpers.At(that.Keys, -1).Type) ))) { yield return new Reporting.Error( @@ -7903,7 +7903,7 @@ Our.IReference that 1, System.Math.Max(0, that.Keys.Count - 1) ).All( - i => Our.Constants.FragmentKeys.Contains(that.Keys[i].Type)) + i => Our.Constants.FragmentKeys.Contains(Common.ListHelpers.At(that.Keys, i).Type)) ))) { yield return new Reporting.Error( @@ -7924,7 +7924,7 @@ Our.IReference that 0, that.Keys.Count - 1 ).All( - i => !Our.Constants.GenericFragmentKeys.Contains(that.Keys[i].Type)) + i => !Our.Constants.GenericFragmentKeys.Contains(Common.ListHelpers.At(that.Keys, i).Type)) ))) { yield return new Reporting.Error( @@ -7940,11 +7940,11 @@ Our.IReference that !( that.Type == ReferenceTypes.ModelReference && that.Keys.Count > 1 - && that.Keys[^1].Type == KeyTypes.FragmentReference + && Common.ListHelpers.At(that.Keys, -1).Type == KeyTypes.FragmentReference ) || ( - that.Keys[^2].Type == KeyTypes.File - || that.Keys[^2].Type == KeyTypes.Blob + Common.ListHelpers.At(that.Keys, -2).Type == KeyTypes.File + || Common.ListHelpers.At(that.Keys, -2).Type == KeyTypes.Blob ))) { yield return new Reporting.Error( @@ -7964,8 +7964,8 @@ Our.IReference that 0, that.Keys.Count - 1 ).All( - i => !(that.Keys[i].Type == KeyTypes.SubmodelElementList) - || Verification.MatchesXsNonNegativeInteger(that.Keys[i + 1].Value)) + i => !(Common.ListHelpers.At(that.Keys, i).Type == KeyTypes.SubmodelElementList) + || Verification.MatchesXsNonNegativeInteger(Common.ListHelpers.At(that.Keys, i + 1).Value)) ))) { yield return new Reporting.Error( diff --git a/dev/test_data/main/csharp/expected/assignment_targets/expected_output/dummy/common.cs b/dev/test_data/main/csharp/expected/assignment_targets/expected_output/dummy/common.cs index a0b3ba772..324a5dfcd 100644 --- a/dev/test_data/main/csharp/expected/assignment_targets/expected_output/dummy/common.cs +++ b/dev/test_data/main/csharp/expected/assignment_targets/expected_output/dummy/common.cs @@ -24,6 +24,63 @@ public static int Len(System.Runtime.CompilerServices.ITuple tuple) return tuple.Length; } } // public static class TupleHelpers + + /// + /// Provide the access to the items of lists which follows the Python + /// implementation, since Python is the language of the meta-model specifications. + /// + /// + /// A negative index counts from the back of the list. + /// + public static class ListHelpers + { + /// + /// Resolve in a list of items. + /// + /// + /// Thrown if the resolved index does not fit into an int. + /// + private static int ResolveIndex(long index, int count) + { + return checked((int)(index < 0 ? index + count : index)); + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static T At( + System.Collections.Generic.IReadOnlyList list, + long index + ) + { + return list[ResolveIndex(index, list.Count)]; + } + + /// + /// Set the item of at + /// to . + /// + /// to set the item in + /// of the item; negative counts from the back + /// to be set + /// + /// Thrown if is out of range. + /// + public static void SetAt( + System.Collections.Generic.IList list, + long index, + T value + ) + { + list[ResolveIndex(index, list.Count)] = value; + } + } // public static class ListHelpers } // public static class Common } // namespace dummy diff --git a/dev/test_data/main/csharp/expected/assignment_targets/expected_output/dummy/verification.cs b/dev/test_data/main/csharp/expected/assignment_targets/expected_output/dummy/verification.cs index bf5c52298..1ec26918a 100644 --- a/dev/test_data/main/csharp/expected/assignment_targets/expected_output/dummy/verification.cs +++ b/dev/test_data/main/csharp/expected/assignment_targets/expected_output/dummy/verification.cs @@ -101,8 +101,8 @@ public static bool SetFirstAndLastText( string text ) { - item.Texts[0] = text; - item.Texts[^1] = text; + Common.ListHelpers.SetAt(item.Texts, 0, text); + Common.ListHelpers.SetAt(item.Texts, -1, text); return true; } // public static bool SetFirstAndLastText @@ -115,7 +115,7 @@ string text ) { var texts = item.Texts; - texts[1] = text; + Common.ListHelpers.SetAt(texts, 1, text); return true; } // public static bool SetTextThroughListAlias @@ -127,8 +127,12 @@ public static bool SetNumbers( long number ) { - numbers[0] = number; - numbers[^2] = numbers[^1] + 1; + Common.ListHelpers.SetAt(numbers, 0, number); + Common.ListHelpers.SetAt( + numbers, + -2, + Common.ListHelpers.At(numbers, -1) + 1 + ); return true; } // public static bool SetNumbers @@ -140,8 +144,12 @@ public static bool SetNestedText( string text ) { - items[0].Texts[^1] = text; - items[^1].Text = text; + Common.ListHelpers.SetAt( + Common.ListHelpers.At(items, 0).Texts, + -1, + text + ); + Common.ListHelpers.At(items, -1).Text = text; return true; } // public static bool SetNestedText @@ -153,7 +161,7 @@ public static bool ReplaceFirstItem( IItem item ) { - items[0] = item; + Common.ListHelpers.SetAt(items, 0, item); return true; } // public static bool ReplaceFirstItem @@ -171,7 +179,11 @@ string text } for (int i = 0; i < items.Count; i++) { - items[i].Texts[0] = text; + Common.ListHelpers.SetAt( + Common.ListHelpers.At(items, i).Texts, + 0, + text + ); } return true; } // public static bool SetTextsInLoops @@ -186,7 +198,7 @@ string text { for (int i = 0; i < texts.Count; i++) { - texts[i] = text; + Common.ListHelpers.SetAt(texts, i, text); } return true; } // public static bool FillTexts @@ -263,7 +275,7 @@ string text { foreach (var texts in lists) { - texts[0] = text; + Common.ListHelpers.SetAt(texts, 0, text); } return true; } // public static bool SetFirstTextsOfLists @@ -280,12 +292,12 @@ string text foreach (var item in items) { var texts = item.Texts; - texts[0] = text; + Common.ListHelpers.SetAt(texts, 0, text); } foreach (var other in others) { var texts = other.Texts; - texts[0] = text; + Common.ListHelpers.SetAt(texts, 0, text); } return true; } // public static bool SetFirstTextsInSiblingLoops @@ -310,9 +322,9 @@ public static bool NumberCopyIsIndependent( List numbers ) { - long first = numbers[0]; - numbers[0] = first + 1; - return (first + 1) == (numbers[0]); + long first = Common.ListHelpers.At(numbers, 0); + Common.ListHelpers.SetAt(numbers, 0, first + 1); + return (first + 1) == (Common.ListHelpers.At(numbers, 0)); } // public static bool NumberCopyIsIndependent /// @@ -363,7 +375,7 @@ string text ) { var texts = item.Texts; - return texts[0] == text; + return Common.ListHelpers.At(texts, 0) == text; } // public static bool FirstTextThroughAliasIs /// diff --git a/dev/test_data/main/csharp/expected/for_statements/expected_output/dummy/common.cs b/dev/test_data/main/csharp/expected/for_statements/expected_output/dummy/common.cs index ca91a0ae6..d2f180ef7 100644 --- a/dev/test_data/main/csharp/expected/for_statements/expected_output/dummy/common.cs +++ b/dev/test_data/main/csharp/expected/for_statements/expected_output/dummy/common.cs @@ -85,6 +85,63 @@ public static int Len(string text) } } // public static class StringHelpers + /// + /// Provide the access to the items of lists which follows the Python + /// implementation, since Python is the language of the meta-model specifications. + /// + /// + /// A negative index counts from the back of the list. + /// + public static class ListHelpers + { + /// + /// Resolve in a list of items. + /// + /// + /// Thrown if the resolved index does not fit into an int. + /// + private static int ResolveIndex(long index, int count) + { + return checked((int)(index < 0 ? index + count : index)); + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static T At( + System.Collections.Generic.IReadOnlyList list, + long index + ) + { + return list[ResolveIndex(index, list.Count)]; + } + + /// + /// Set the item of at + /// to . + /// + /// to set the item in + /// of the item; negative counts from the back + /// to be set + /// + /// Thrown if is out of range. + /// + public static void SetAt( + System.Collections.Generic.IList list, + long index, + T value + ) + { + list[ResolveIndex(index, list.Count)] = value; + } + } // public static class ListHelpers + /// /// Compute the remainder of the floored division of /// by . diff --git a/dev/test_data/main/csharp/expected/for_statements/expected_output/dummy/verification.cs b/dev/test_data/main/csharp/expected/for_statements/expected_output/dummy/verification.cs index 487526fad..87ff90f30 100644 --- a/dev/test_data/main/csharp/expected/for_statements/expected_output/dummy/verification.cs +++ b/dev/test_data/main/csharp/expected/for_statements/expected_output/dummy/verification.cs @@ -71,7 +71,7 @@ List numbers { for (int i = 0; i < numbers.Count; i++) { - long number = numbers[i]; + long number = Common.ListHelpers.At(numbers, i); if (number == -1) { return false; @@ -89,7 +89,7 @@ List numbers { for (int i = 1; i < numbers.Count; i++) { - if (numbers[i] == 1) + if (Common.ListHelpers.At(numbers, i) == 1) { return false; } @@ -209,7 +209,7 @@ List items { for (int i = 0; i < item.Texts.Count; i++) { - var text = item.Texts[i]; + var text = Common.ListHelpers.At(item.Texts, i); switch (text) { case "unexpected": @@ -296,12 +296,12 @@ List items { if (kind == Kind.Alpha) { - if (item.Texts[i] == "stop") + if (Common.ListHelpers.At(item.Texts, i) == "stop") { break; } } - else if (Common.StringHelpers.Len(item.Texts[i]) > 10) + else if (Common.StringHelpers.Len(Common.ListHelpers.At(item.Texts, i)) > 10) { break; } diff --git a/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/common.cs b/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/common.cs index 44c4c96e8..dd892c24a 100644 --- a/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/common.cs +++ b/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/common.cs @@ -25,6 +25,63 @@ public static int Len(System.Runtime.CompilerServices.ITuple tuple) } } // public static class TupleHelpers + /// + /// Provide the access to the items of lists which follows the Python + /// implementation, since Python is the language of the meta-model specifications. + /// + /// + /// A negative index counts from the back of the list. + /// + public static class ListHelpers + { + /// + /// Resolve in a list of items. + /// + /// + /// Thrown if the resolved index does not fit into an int. + /// + private static int ResolveIndex(long index, int count) + { + return checked((int)(index < 0 ? index + count : index)); + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static T At( + System.Collections.Generic.IReadOnlyList list, + long index + ) + { + return list[ResolveIndex(index, list.Count)]; + } + + /// + /// Set the item of at + /// to . + /// + /// to set the item in + /// of the item; negative counts from the back + /// to be set + /// + /// Thrown if is out of range. + /// + public static void SetAt( + System.Collections.Generic.IList list, + long index, + T value + ) + { + list[ResolveIndex(index, list.Count)] = value; + } + } // public static class ListHelpers + /// /// Compute the remainder of the floored division of /// by . diff --git a/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/types.cs b/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/types.cs index a82076f51..29b7218ef 100644 --- a/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/types.cs +++ b/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/types.cs @@ -105,7 +105,7 @@ public string TextAt( long index ) { - return this.Texts[checked((int)index)]; + return Common.ListHelpers.At(this.Texts, index); } /// diff --git a/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/verification.cs b/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/verification.cs index 29869e1c4..df44ed2c3 100644 --- a/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/verification.cs +++ b/dev/test_data/main/csharp/expected/int_length_interplay/expected_output/dummy/verification.cs @@ -61,18 +61,19 @@ long count { for (long i = 0; i < count; i++) { - long number = numbers[checked((int)i)]; + long number = Common.ListHelpers.At(numbers, i); if (number == 7) { return false; } - long mirrored = numbers[checked((int)((numbers.Count - 1) - i))]; + long mirrored = ( + Common.ListHelpers.At(numbers, (numbers.Count - 1) - i)); if (mirrored == 7) { return false; } long rotated = ( - numbers[checked((int)(Common.FloorMod(i + 1, numbers.Count)))]); + Common.ListHelpers.At(numbers, Common.FloorMod(i + 1, numbers.Count))); if (rotated == 7) { return false; @@ -105,7 +106,7 @@ long start } for (long i = start; i < numbers.Count; i++) { - long number = numbers[checked((int)i)]; + long number = Common.ListHelpers.At(numbers, i); if (number == 7) { return false; @@ -115,9 +116,30 @@ long start checked((int)start), checked((int)System.Math.Max(0L, numbers.Count - start)) ).All( - i => numbers[checked((int)i)] != 7); + i => Common.ListHelpers.At(numbers, i) != 7); } // public static bool NoSevenFrom + /// + /// Check reading at the negative indices which are known only at run time. + /// + /// + /// A negative index counts from the back of the list, as in Python. + /// + public static bool NumbersAscendFromBack( + List numbers, + long count + ) + { + for (long i = 1; i < count; i++) + { + if (Common.ListHelpers.At(numbers, -i) <= Common.ListHelpers.At(numbers, (-i) - 1)) + { + return false; + } + } + return true; + } // public static bool NumbersAscendFromBack + /// /// Check writing at the indices which are integers rather than lengths. /// @@ -129,8 +151,9 @@ string text { for (long i = 0; i < count; i++) { - texts[checked((int)i)] = text; - texts[checked((int)((texts.Count - 1) - i))] = text; + Common.ListHelpers.SetAt(texts, i, text); + Common.ListHelpers.SetAt(texts, (texts.Count - 1) - i, text); + Common.ListHelpers.SetAt(texts, -1 - i, text); } return true; } // public static bool FillTextsFromBothEnds @@ -159,7 +182,7 @@ List texts { something.Count = texts.Count; something.MaybeCount = texts.Count; - something.Numbers[0] = texts.Count; + Common.ListHelpers.SetAt(something.Numbers, 0, texts.Count); return true; } // public static bool SetCounts @@ -246,6 +269,14 @@ Our.ISomething that "No number is seven"); } + if (!( + Verification.NumbersAscendFromBack(that.Numbers, that.Numbers.Count))) + { + yield return new Reporting.Error( + "Invariant violated:\n" + + "Numbers ascend"); + } + foreach (var error in Verification.Verify_ListOf_long(that.Numbers)) { error.PrependSegment( diff --git a/dev/test_data/main/csharp/expected/json_types_in_invariants/expected_output/dummy/common.cs b/dev/test_data/main/csharp/expected/json_types_in_invariants/expected_output/dummy/common.cs index d50d971c9..e26301681 100644 --- a/dev/test_data/main/csharp/expected/json_types_in_invariants/expected_output/dummy/common.cs +++ b/dev/test_data/main/csharp/expected/json_types_in_invariants/expected_output/dummy/common.cs @@ -25,6 +25,80 @@ public static int Len(System.Runtime.CompilerServices.ITuple tuple) } } // public static class TupleHelpers + /// + /// Provide the access to the items of lists which follows the Python + /// implementation, since Python is the language of the meta-model specifications. + /// + /// + /// A negative index counts from the back of the list. + /// + public static class ListHelpers + { + /// + /// Resolve in a list of items. + /// + /// + /// Thrown if the resolved index does not fit into an int. + /// + private static int ResolveIndex(long index, int count) + { + return checked((int)(index < 0 ? index + count : index)); + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static T At( + System.Collections.Generic.IReadOnlyList list, + long index + ) + { + return list[ResolveIndex(index, list.Count)]; + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static System.Text.Json.Nodes.JsonNode? At( + System.Text.Json.Nodes.JsonArray array, + long index + ) + { + return array[ResolveIndex(index, array.Count)]; + } + + /// + /// Set the item of at + /// to . + /// + /// to set the item in + /// of the item; negative counts from the back + /// to be set + /// + /// Thrown if is out of range. + /// + public static void SetAt( + System.Collections.Generic.IList list, + long index, + T value + ) + { + list[ResolveIndex(index, list.Count)] = value; + } + } // public static class ListHelpers + /// /// Check whether is a safe integer, i.e., /// whether it lies in the range [-2^53 + 1, 2^53 - 1]. diff --git a/dev/test_data/main/csharp/expected/json_types_in_invariants/expected_output/dummy/verification.cs b/dev/test_data/main/csharp/expected/json_types_in_invariants/expected_output/dummy/verification.cs index fc8514638..b70610c31 100644 --- a/dev/test_data/main/csharp/expected/json_types_in_invariants/expected_output/dummy/verification.cs +++ b/dev/test_data/main/csharp/expected/json_types_in_invariants/expected_output/dummy/verification.cs @@ -88,7 +88,7 @@ Our.ISomething that if (!( !(that.Values.Count > 0 - && (that.Values[0] == null)))) + && (Common.ListHelpers.At(that.Values, 0) == null)))) { yield return new Reporting.Error( "Invariant violated:\n" + @@ -109,7 +109,7 @@ Our.ISomething that if (!( !(that.Values.Count >= 1) - || Verification.IsAcceptable(that.Values[^1]))) + || Verification.IsAcceptable(Common.ListHelpers.At(that.Values, -1)))) { yield return new Reporting.Error( "Invariant violated:\n" + @@ -118,7 +118,7 @@ Our.ISomething that if (!( !(that.Values.Count > 0) - || Verification.IsAcceptable(that.Values[0]))) + || Verification.IsAcceptable(Common.ListHelpers.At(that.Values, 0)))) { yield return new Reporting.Error( "Invariant violated:\n" + diff --git a/dev/test_data/main/csharp/expected/list_of_primitives/expected_output/dummy/common.cs b/dev/test_data/main/csharp/expected/list_of_primitives/expected_output/dummy/common.cs index ec0d5f773..31e93a2bb 100644 --- a/dev/test_data/main/csharp/expected/list_of_primitives/expected_output/dummy/common.cs +++ b/dev/test_data/main/csharp/expected/list_of_primitives/expected_output/dummy/common.cs @@ -85,6 +85,63 @@ public static int Len(string text) } } // public static class StringHelpers + /// + /// Provide the access to the items of lists which follows the Python + /// implementation, since Python is the language of the meta-model specifications. + /// + /// + /// A negative index counts from the back of the list. + /// + public static class ListHelpers + { + /// + /// Resolve in a list of items. + /// + /// + /// Thrown if the resolved index does not fit into an int. + /// + private static int ResolveIndex(long index, int count) + { + return checked((int)(index < 0 ? index + count : index)); + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static T At( + System.Collections.Generic.IReadOnlyList list, + long index + ) + { + return list[ResolveIndex(index, list.Count)]; + } + + /// + /// Set the item of at + /// to . + /// + /// to set the item in + /// of the item; negative counts from the back + /// to be set + /// + /// Thrown if is out of range. + /// + public static void SetAt( + System.Collections.Generic.IList list, + long index, + T value + ) + { + list[ResolveIndex(index, list.Count)] = value; + } + } // public static class ListHelpers + /// /// Check whether is a safe integer, i.e., /// whether it lies in the range [-2^53 + 1, 2^53 - 1]. diff --git a/dev/test_data/main/csharp/expected/list_of_primitives/expected_output/dummy/verification.cs b/dev/test_data/main/csharp/expected/list_of_primitives/expected_output/dummy/verification.cs index 3d2dcc8ec..7b91e782c 100644 --- a/dev/test_data/main/csharp/expected/list_of_primitives/expected_output/dummy/verification.cs +++ b/dev/test_data/main/csharp/expected/list_of_primitives/expected_output/dummy/verification.cs @@ -139,7 +139,7 @@ List numbers { for (int j = i + 1; j < numbers.Count; j++) { - if (numbers[i] == numbers[j]) + if (Common.ListHelpers.At(numbers, i) == Common.ListHelpers.At(numbers, j)) { return false; } diff --git a/dev/test_data/main/csharp/expected/nested_collections/expected_output/dummy/common.cs b/dev/test_data/main/csharp/expected/nested_collections/expected_output/dummy/common.cs index 1362641b5..b0a855bf9 100644 --- a/dev/test_data/main/csharp/expected/nested_collections/expected_output/dummy/common.cs +++ b/dev/test_data/main/csharp/expected/nested_collections/expected_output/dummy/common.cs @@ -154,6 +154,63 @@ public static int CompareByCodePoints(string that, string other) } } // public static class StringHelpers + /// + /// Provide the access to the items of lists which follows the Python + /// implementation, since Python is the language of the meta-model specifications. + /// + /// + /// A negative index counts from the back of the list. + /// + public static class ListHelpers + { + /// + /// Resolve in a list of items. + /// + /// + /// Thrown if the resolved index does not fit into an int. + /// + private static int ResolveIndex(long index, int count) + { + return checked((int)(index < 0 ? index + count : index)); + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static T At( + System.Collections.Generic.IReadOnlyList list, + long index + ) + { + return list[ResolveIndex(index, list.Count)]; + } + + /// + /// Set the item of at + /// to . + /// + /// to set the item in + /// of the item; negative counts from the back + /// to be set + /// + /// Thrown if is out of range. + /// + public static void SetAt( + System.Collections.Generic.IList list, + long index, + T value + ) + { + list[ResolveIndex(index, list.Count)] = value; + } + } // public static class ListHelpers + /// /// Sort the items of the sets, so that they are serialized in the same order /// in all the SDKs. diff --git a/dev/test_data/main/csharp/expected/nested_collections/expected_output/dummy/verification.cs b/dev/test_data/main/csharp/expected/nested_collections/expected_output/dummy/verification.cs index ad297afed..a1dfde0cd 100644 --- a/dev/test_data/main/csharp/expected/nested_collections/expected_output/dummy/verification.cs +++ b/dev/test_data/main/csharp/expected/nested_collections/expected_output/dummy/verification.cs @@ -42,7 +42,7 @@ List> grid { return true; } - List firstRow = grid[0]; + List firstRow = Common.ListHelpers.At(grid, 0); foreach (var row in grid) { if (row.Count != firstRow.Count) @@ -97,10 +97,10 @@ List> grid { if ( grid.Count > 0 - && (grid[0]).Count > 0 + && (Common.ListHelpers.At(grid, 0)).Count > 0 ) { - grid[0][0] = 0; + Common.ListHelpers.SetAt(Common.ListHelpers.At(grid, 0), 0, 0); } return true; } // public static bool ResetCorner diff --git a/dev/test_data/main/csharp/expected/transpiled_methods/expected_output/dummy/common.cs b/dev/test_data/main/csharp/expected/transpiled_methods/expected_output/dummy/common.cs index 9945bbb62..20f083be9 100644 --- a/dev/test_data/main/csharp/expected/transpiled_methods/expected_output/dummy/common.cs +++ b/dev/test_data/main/csharp/expected/transpiled_methods/expected_output/dummy/common.cs @@ -149,6 +149,63 @@ public static string Slice(string text, long start, long? end = null) } } // public static class StringHelpers + /// + /// Provide the access to the items of lists which follows the Python + /// implementation, since Python is the language of the meta-model specifications. + /// + /// + /// A negative index counts from the back of the list. + /// + public static class ListHelpers + { + /// + /// Resolve in a list of items. + /// + /// + /// Thrown if the resolved index does not fit into an int. + /// + private static int ResolveIndex(long index, int count) + { + return checked((int)(index < 0 ? index + count : index)); + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static T At( + System.Collections.Generic.IReadOnlyList list, + long index + ) + { + return list[ResolveIndex(index, list.Count)]; + } + + /// + /// Set the item of at + /// to . + /// + /// to set the item in + /// of the item; negative counts from the back + /// to be set + /// + /// Thrown if is out of range. + /// + public static void SetAt( + System.Collections.Generic.IList list, + long index, + T value + ) + { + list[ResolveIndex(index, list.Count)] = value; + } + } // public static class ListHelpers + /// /// Compute the remainder of the floored division of /// by . diff --git a/dev/test_data/main/csharp/expected/transpiled_methods/expected_output/dummy/types.cs b/dev/test_data/main/csharp/expected/transpiled_methods/expected_output/dummy/types.cs index a8143876c..747e4f0ca 100644 --- a/dev/test_data/main/csharp/expected/transpiled_methods/expected_output/dummy/types.cs +++ b/dev/test_data/main/csharp/expected/transpiled_methods/expected_output/dummy/types.cs @@ -239,7 +239,7 @@ List numbers { for (int i = 0; i < numbers.Count; i++) { - numbers[i] = this.Count; + Common.ListHelpers.SetAt(numbers, i, this.Count); } } @@ -267,7 +267,7 @@ string text { for (int i = 0; i < this.Texts.Count; i++) { - this.Texts[i] = text; + Common.ListHelpers.SetAt(this.Texts, i, text); } } @@ -470,7 +470,7 @@ List numbers { for (int i = 0; i < numbers.Count; i++) { - numbers[i] = this.Count; + Common.ListHelpers.SetAt(numbers, i, this.Count); } } @@ -498,7 +498,7 @@ string text { for (int i = 0; i < this.Texts.Count; i++) { - this.Texts[i] = text; + Common.ListHelpers.SetAt(this.Texts, i, text); } } diff --git a/dev/test_data/main/csharp/expected/variance_of_containers/expected_output/dummy/common.cs b/dev/test_data/main/csharp/expected/variance_of_containers/expected_output/dummy/common.cs index ec0d5f773..31e93a2bb 100644 --- a/dev/test_data/main/csharp/expected/variance_of_containers/expected_output/dummy/common.cs +++ b/dev/test_data/main/csharp/expected/variance_of_containers/expected_output/dummy/common.cs @@ -85,6 +85,63 @@ public static int Len(string text) } } // public static class StringHelpers + /// + /// Provide the access to the items of lists which follows the Python + /// implementation, since Python is the language of the meta-model specifications. + /// + /// + /// A negative index counts from the back of the list. + /// + public static class ListHelpers + { + /// + /// Resolve in a list of items. + /// + /// + /// Thrown if the resolved index does not fit into an int. + /// + private static int ResolveIndex(long index, int count) + { + return checked((int)(index < 0 ? index + count : index)); + } + + /// + /// Get the item of at . + /// + /// to get the item from + /// of the item; negative counts from the back + /// the item + /// + /// Thrown if is out of range. + /// + public static T At( + System.Collections.Generic.IReadOnlyList list, + long index + ) + { + return list[ResolveIndex(index, list.Count)]; + } + + /// + /// Set the item of at + /// to . + /// + /// to set the item in + /// of the item; negative counts from the back + /// to be set + /// + /// Thrown if is out of range. + /// + public static void SetAt( + System.Collections.Generic.IList list, + long index, + T value + ) + { + list[ResolveIndex(index, list.Count)] = value; + } + } // public static class ListHelpers + /// /// Check whether is a safe integer, i.e., /// whether it lies in the range [-2^53 + 1, 2^53 - 1]. diff --git a/dev/test_data/main/csharp/expected/variance_of_containers/expected_output/dummy/verification.cs b/dev/test_data/main/csharp/expected/variance_of_containers/expected_output/dummy/verification.cs index e202b5a64..5481a92ed 100644 --- a/dev/test_data/main/csharp/expected/variance_of_containers/expected_output/dummy/verification.cs +++ b/dev/test_data/main/csharp/expected/variance_of_containers/expected_output/dummy/verification.cs @@ -102,7 +102,7 @@ IReadOnlyList items { return false; } - var first = items[0]; + var first = Common.ListHelpers.At(items, 0); if (first is Our.ISpecialItem) { return ((Our.ISpecialItem)first).Level > 0; @@ -195,7 +195,7 @@ IReadOnlyList items { return false; } - Our.IItem last = items[items.Count - 1]; + Our.IItem last = Common.ListHelpers.At(items, items.Count - 1); if (last is Our.ISpecialItem) { return ((Our.ISpecialItem)last).Level > 0; diff --git a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/common/common.go b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/common/common.go index 64ff930a0..ec4822f85 100644 --- a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/common/common.go +++ b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/common/common.go @@ -222,6 +222,28 @@ func (tuple Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Len() int { return 8 } +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T { + if index < 0 { + index += int64(len(slice)) + } + return slice[index] +} + +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T { + if index < 0 { + index += int64(len(slice)) + } + return &slice[index] +} + // Count the characters (code points) of `text`. // // We follow the Python implementation of `len`, since Python is the language of diff --git a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/verification/verification.go index d2f385fed..9114f018d 100644 --- a/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/verification/verification.go +++ b/dev/test_data/main/golang/expected/aas_core_meta.v3/expected_output/verification/verification.go @@ -2323,7 +2323,7 @@ func IsModelReferenceTo( ) bool { return reference.Type() == ourtypes.ReferenceTypesModelReference && len(reference.Keys()) != 0 && - reference.Keys()[len(reference.Keys()) - 1].Type() == expectedType + ourcommon.At(reference.Keys(), -1).Type() == expectedType } // Check that the target of the reference matches a [ourconstants.AASReferables]. @@ -2334,7 +2334,7 @@ func IsModelReferenceToReferable( len(reference.Keys()) != 0 && ourcommon.MapContains( ourconstants.AASReferables, - reference.Keys()[len(reference.Keys()) - 1].Type(), + ourcommon.At(reference.Keys(), -1).Type(), ) } @@ -10672,7 +10672,7 @@ func VerifyReference( !(len(that.Keys()) >= 1) || ourcommon.MapContains( ourconstants.GloballyIdentifiables, - that.Keys()[0].Type(), + ourcommon.At(that.Keys(), 0).Type(), )) { abort = onError( newVerificationError( @@ -10691,7 +10691,7 @@ func VerifyReference( len(that.Keys()) >= 1) || ourcommon.MapContains( ourconstants.GenericGloballyIdentifiables, - that.Keys()[0].Type(), + ourcommon.At(that.Keys(), 0).Type(), )) { abort = onError( newVerificationError( @@ -10710,7 +10710,7 @@ func VerifyReference( len(that.Keys()) >= 1) || ourcommon.MapContains( ourconstants.AASIdentifiables, - that.Keys()[0].Type(), + ourcommon.At(that.Keys(), 0).Type(), )) { abort = onError( newVerificationError( @@ -10728,11 +10728,11 @@ func VerifyReference( len(that.Keys()) >= 1) || (ourcommon.MapContains( ourconstants.GenericGloballyIdentifiables, - that.Keys()[len(that.Keys()) - 1].Type(), + ourcommon.At(that.Keys(), -1).Type(), ) || ourcommon.MapContains( ourconstants.GenericFragmentKeys, - that.Keys()[len(that.Keys()) - 1].Type(), + ourcommon.At(that.Keys(), -1).Type(), ))) { abort = onError( newVerificationError( @@ -10753,7 +10753,7 @@ func VerifyReference( func(i int) bool { return ourcommon.MapContains( ourconstants.FragmentKeys, - that.Keys()[i].Type(), + ourcommon.At(that.Keys(), int64(i)).Type(), ) }, 1, @@ -10779,7 +10779,7 @@ func VerifyReference( func(i int) bool { return !ourcommon.MapContains( ourconstants.GenericFragmentKeys, - that.Keys()[i].Type(), + ourcommon.At(that.Keys(), int64(i)).Type(), ) }, 0, @@ -10802,9 +10802,9 @@ func VerifyReference( if !( !(that.Type() == ourtypes.ReferenceTypesModelReference && len(that.Keys()) > 1 && - that.Keys()[len(that.Keys()) - 1].Type() == ourtypes.KeyTypesFragmentReference) || - (that.Keys()[len(that.Keys()) - 2].Type() == ourtypes.KeyTypesFile || - that.Keys()[len(that.Keys()) - 2].Type() == ourtypes.KeyTypesBlob)) { + ourcommon.At(that.Keys(), -1).Type() == ourtypes.KeyTypesFragmentReference) || + (ourcommon.At(that.Keys(), -2).Type() == ourtypes.KeyTypesFile || + ourcommon.At(that.Keys(), -2).Type() == ourtypes.KeyTypesBlob)) { abort = onError( newVerificationError( "Constraint AASd-127: For model references, with more than " + @@ -10822,9 +10822,9 @@ func VerifyReference( len(that.Keys()) > 2) || ourcommon.AllRange( func(i int) bool { - return !(that.Keys()[i].Type() == ourtypes.KeyTypesSubmodelElementList) || + return !(ourcommon.At(that.Keys(), int64(i)).Type() == ourtypes.KeyTypesSubmodelElementList) || MatchesXsNonNegativeInteger( - that.Keys()[i + 1].Value(), + ourcommon.At(that.Keys(), int64(i + 1)).Value(), ) }, 0, diff --git a/dev/test_data/main/golang/expected/assignment_targets/expected_output/common/common.go b/dev/test_data/main/golang/expected/assignment_targets/expected_output/common/common.go index 9cd0eae53..8e1939d3a 100644 --- a/dev/test_data/main/golang/expected/assignment_targets/expected_output/common/common.go +++ b/dev/test_data/main/golang/expected/assignment_targets/expected_output/common/common.go @@ -221,5 +221,27 @@ func (tuple Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Len() int { return 8 } +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T { + if index < 0 { + index += int64(len(slice)) + } + return slice[index] +} + +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T { + if index < 0 { + index += int64(len(slice)) + } + return &slice[index] +} + // This code has been automatically generated by aas-core-codegen. // Do NOT edit or append. diff --git a/dev/test_data/main/golang/expected/assignment_targets/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/assignment_targets/expected_output/verification/verification.go index 252087562..7c6c83bfc 100644 --- a/dev/test_data/main/golang/expected/assignment_targets/expected_output/verification/verification.go +++ b/dev/test_data/main/golang/expected/assignment_targets/expected_output/verification/verification.go @@ -97,8 +97,8 @@ func SetFirstAndLastText( item ourtypes.IItem, text string, ) bool { - item.Texts()[0] = text - item.Texts()[len(item.Texts()) - 1] = text + *ourcommon.PointerAt(item.Texts(), 0) = text + *ourcommon.PointerAt(item.Texts(), -1) = text return true } @@ -108,7 +108,7 @@ func SetTextThroughListAlias( text string, ) bool { texts := item.Texts() - texts[1] = text + *ourcommon.PointerAt(texts, 1) = text return true } @@ -117,8 +117,8 @@ func SetNumbers( numbers []int64, number int64, ) bool { - numbers[0] = number - numbers[len(numbers) - 2] = numbers[len(numbers) - 1] + 1 + *ourcommon.PointerAt(numbers, 0) = number + *ourcommon.PointerAt(numbers, -2) = ourcommon.At(numbers, -1) + 1 return true } @@ -127,8 +127,8 @@ func SetNestedText( items []ourtypes.IItem, text string, ) bool { - items[0].Texts()[len(items[0].Texts()) - 1] = text - items[len(items) - 1].SetText(text) + *ourcommon.PointerAt(ourcommon.At(items, 0).Texts(), -1) = text + ourcommon.At(items, -1).SetText(text) return true } @@ -137,7 +137,7 @@ func ReplaceFirstItem( items []ourtypes.IItem, item ourtypes.IItem, ) bool { - items[0] = item + *ourcommon.PointerAt(items, 0) = item return true } @@ -150,7 +150,7 @@ func SetTextsInLoops( item.SetText(text) } for i := 0; i < len(items); i++ { - items[i].Texts()[0] = text + *ourcommon.PointerAt(ourcommon.At(items, int64(i)).Texts(), 0) = text } return true } @@ -161,7 +161,7 @@ func FillTexts( text string, ) bool { for i := 0; i < len(texts); i++ { - texts[i] = text + *ourcommon.PointerAt(texts, int64(i)) = text } return true } @@ -218,7 +218,7 @@ func SetFirstTextsOfLists( text string, ) bool { for _, texts := range lists { - texts[0] = text + *ourcommon.PointerAt(texts, 0) = text } return true } @@ -231,11 +231,11 @@ func SetFirstTextsInSiblingLoops( ) bool { for _, item := range items { texts := item.Texts() - texts[0] = text + *ourcommon.PointerAt(texts, 0) = text } for _, other := range others { texts := other.Texts() - texts[0] = text + *ourcommon.PointerAt(texts, 0) = text } return true } @@ -254,9 +254,9 @@ func TextCopyIsIndependent( func NumberCopyIsIndependent( numbers []int64, ) bool { - first := numbers[0] - numbers[0] = first + 1 - return (first + 1) == numbers[0] + first := ourcommon.At(numbers, 0) + *ourcommon.PointerAt(numbers, 0) = first + 1 + return (first + 1) == ourcommon.At(numbers, 0) } // Check the assignment of a copy of a list to an optional property. @@ -295,7 +295,7 @@ func FirstTextThroughAliasIs( text string, ) bool { texts := item.Texts() - return texts[0] == text + return ourcommon.At(texts, 0) == text } // Check that no text is empty through a read-only list argument. diff --git a/dev/test_data/main/golang/expected/for_statements/expected_output/common/common.go b/dev/test_data/main/golang/expected/for_statements/expected_output/common/common.go index dcc57c0bb..df28b87ce 100644 --- a/dev/test_data/main/golang/expected/for_statements/expected_output/common/common.go +++ b/dev/test_data/main/golang/expected/for_statements/expected_output/common/common.go @@ -223,6 +223,28 @@ func (tuple Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Len() int { return 8 } +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T { + if index < 0 { + index += int64(len(slice)) + } + return slice[index] +} + +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T { + if index < 0 { + index += int64(len(slice)) + } + return &slice[index] +} + // Count the characters (code points) of `text`. // // We follow the Python implementation of `len`, since Python is the language of diff --git a/dev/test_data/main/golang/expected/for_statements/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/for_statements/expected_output/verification/verification.go index ac687c9d5..8ecc94a42 100644 --- a/dev/test_data/main/golang/expected/for_statements/expected_output/verification/verification.go +++ b/dev/test_data/main/golang/expected/for_statements/expected_output/verification/verification.go @@ -72,7 +72,7 @@ func NoNumberIsMinusOne( numbers []int64, ) bool { for i := 0; i < len(numbers); i++ { - number := numbers[i] + number := ourcommon.At(numbers, int64(i)) if number == -1 { return false } @@ -85,7 +85,7 @@ func NoNumberAfterTheFirstIsOne( numbers []int64, ) bool { for i := 1; i < len(numbers); i++ { - if numbers[i] == 1 { + if ourcommon.At(numbers, int64(i)) == 1 { return false } } @@ -171,7 +171,7 @@ func ItemsAreFewAndTextsExpected( count := int64(0) for _, item := range items { for i := 0; i < len(item.Texts()); i++ { - text := item.Texts()[i] + text := ourcommon.At(item.Texts(), int64(i)) switch text { case "unexpected": return false @@ -232,10 +232,10 @@ func TextsBeforeStopAreFew( for _, item := range items { for i := 0; i < len(item.Texts()); i++ { if kind == ourtypes.KindAlpha { - if item.Texts()[i] == "stop" { + if ourcommon.At(item.Texts(), int64(i)) == "stop" { break } - } else if ourcommon.LenStr(item.Texts()[i]) > 10 { + } else if ourcommon.LenStr(ourcommon.At(item.Texts(), int64(i))) > 10 { break } count = count + 1 diff --git a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/common/common.go b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/common/common.go index b79c48c12..c89359824 100644 --- a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/common/common.go +++ b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/common/common.go @@ -222,6 +222,28 @@ func (tuple Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Len() int { return 8 } +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T { + if index < 0 { + index += int64(len(slice)) + } + return slice[index] +} + +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T { + if index < 0 { + index += int64(len(slice)) + } + return &slice[index] +} + // FloorMod computes the remainder of the floored division of dividend // by divisor. // diff --git a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/types/types.go b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/types/types.go index 25b7b30b3..57353770a 100644 --- a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/types/types.go +++ b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/types/types.go @@ -9,6 +9,10 @@ package types // This code has been automatically generated by aas-core-codegen. // Do NOT edit or append. +import ( + ourcommon "github.com/dummy-works/dummy/common" +) + // Enumerate the model types for faster type switches. // // For example, you can use an array of function pointers to @@ -165,7 +169,7 @@ func (s *Something) IsValidIndex( func (s *Something) TextAt( index int64, ) string { - return s.Texts()[index] + return ourcommon.At(s.Texts(), index) } // Apply the action on the instances referenced from s. diff --git a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/verification/verification.go index 25fa8abab..1941aaced 100644 --- a/dev/test_data/main/golang/expected/int_length_interplay/expected_output/verification/verification.go +++ b/dev/test_data/main/golang/expected/int_length_interplay/expected_output/verification/verification.go @@ -65,16 +65,16 @@ func NoNumberIsSeven( count int64, ) bool { for i := int64(0); i < count; i++ { - number := numbers[i] + number := ourcommon.At(numbers, i) if number == 7 { return false } - mirrored := numbers[int64(len(numbers) - 1) - i] + mirrored := ourcommon.At(numbers, int64(len(numbers) - 1) - i) if mirrored == 7 { return false } rotated := - numbers[ourcommon.FloorMod(i + 1, int64(len(numbers)))] + ourcommon.At(numbers, ourcommon.FloorMod(i + 1, int64(len(numbers)))) if rotated == 7 { return false } @@ -99,20 +99,35 @@ func NoSevenFrom( return true } for i := start; i < int64(len(numbers)); i++ { - number := numbers[i] + number := ourcommon.At(numbers, i) if number == 7 { return false } } return ourcommon.AllRange( func(i int64) bool { - return numbers[i] != 7 + return ourcommon.At(numbers, i) != 7 }, start, int64(len(numbers)), ) } +// Check reading at the negative indices which are known only at run time. +// +// A negative index counts from the back of the list, as in Python. +func NumbersAscendFromBack( + numbers []int64, + count int64, +) bool { + for i := int64(1); i < count; i++ { + if ourcommon.At(numbers, -i) <= ourcommon.At(numbers, (-i) - 1) { + return false + } + } + return true +} + // Check writing at the indices which are integers rather than lengths. func FillTextsFromBothEnds( texts []string, @@ -120,8 +135,9 @@ func FillTextsFromBothEnds( text string, ) bool { for i := int64(0); i < count; i++ { - texts[i] = text - texts[int64(len(texts) - 1) - i] = text + *ourcommon.PointerAt(texts, i) = text + *ourcommon.PointerAt(texts, int64(len(texts) - 1) - i) = text + *ourcommon.PointerAt(texts, -1 - i) = text } return true } @@ -144,7 +160,7 @@ func SetCounts( ) bool { something.SetCount(int64(len(texts))) something.SetMaybeCount(ourcommon.NewAndPointTo(int64(len(texts)))) - something.Numbers()[0] = int64(len(texts)) + *ourcommon.PointerAt(something.Numbers(), 0) = int64(len(texts)) return true } @@ -265,6 +281,20 @@ func VerifySomething( } } + if !( + NumbersAscendFromBack( + that.Numbers(), + int64(len(that.Numbers())), + )) { + abort = onError( + newVerificationError( + "Numbers ascend",), + ) + if abort { + return + } + } + if that.Numbers() == nil { abort = onError( newVerificationError( diff --git a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/common/common.go b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/common/common.go index 3e1d9b5df..5d2ff0cb4 100644 --- a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/common/common.go +++ b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/common/common.go @@ -223,6 +223,28 @@ func (tuple Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Len() int { return 8 } +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T { + if index < 0 { + index += int64(len(slice)) + } + return slice[index] +} + +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T { + if index < 0 { + index += int64(len(slice)) + } + return &slice[index] +} + // Check that `value` lies in the safe integer range [-2^53 + 1, 2^53 - 1]. // // JSON knows a single numeric type, which the SDKs read into a float64. diff --git a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/verification/verification.go index e2671a4b7..7c118bab6 100644 --- a/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/verification/verification.go +++ b/dev/test_data/main/golang/expected/json_types_in_invariants/expected_output/verification/verification.go @@ -229,7 +229,7 @@ func VerifySomething( if !( !(len(that.Values()) > 0 && - (that.Values()[0] == nil))) { + (ourcommon.At(that.Values(), 0) == nil))) { abort = onError( newVerificationError( "The first value must not be a JSON null",), @@ -257,7 +257,7 @@ func VerifySomething( if !( !(len(that.Values()) >= 1) || - IsAcceptable(that.Values()[len(that.Values()) - 1])) { + IsAcceptable(ourcommon.At(that.Values(), -1))) { abort = onError( newVerificationError( "The last value must be acceptable",), @@ -269,7 +269,7 @@ func VerifySomething( if !( !(len(that.Values()) > 0) || - IsAcceptable(that.Values()[0])) { + IsAcceptable(ourcommon.At(that.Values(), 0))) { abort = onError( newVerificationError( "The first value must be acceptable",), diff --git a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/common/common.go b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/common/common.go index c2047c736..946317585 100644 --- a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/common/common.go +++ b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/common/common.go @@ -223,6 +223,28 @@ func (tuple Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Len() int { return 8 } +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T { + if index < 0 { + index += int64(len(slice)) + } + return slice[index] +} + +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T { + if index < 0 { + index += int64(len(slice)) + } + return &slice[index] +} + // Count the characters (code points) of `text`. // // We follow the Python implementation of `len`, since Python is the language of diff --git a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/verification/verification.go index 907a2ae91..da42cb621 100644 --- a/dev/test_data/main/golang/expected/list_of_primitives/expected_output/verification/verification.go +++ b/dev/test_data/main/golang/expected/list_of_primitives/expected_output/verification/verification.go @@ -127,7 +127,7 @@ func FloatsAreUnique( ) bool { for i := 0; i < len(numbers); i++ { for j := i + 1; j < len(numbers); j++ { - if numbers[i] == numbers[j] { + if ourcommon.At(numbers, int64(i)) == ourcommon.At(numbers, int64(j)) { return false } } diff --git a/dev/test_data/main/golang/expected/nested_collections/expected_output/common/common.go b/dev/test_data/main/golang/expected/nested_collections/expected_output/common/common.go index f9e57115a..5bf57105b 100644 --- a/dev/test_data/main/golang/expected/nested_collections/expected_output/common/common.go +++ b/dev/test_data/main/golang/expected/nested_collections/expected_output/common/common.go @@ -224,6 +224,28 @@ func (tuple Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Len() int { return 8 } +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T { + if index < 0 { + index += int64(len(slice)) + } + return slice[index] +} + +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T { + if index < 0 { + index += int64(len(slice)) + } + return &slice[index] +} + // Count the characters (code points) of `text`. // // We follow the Python implementation of `len`, since Python is the language of diff --git a/dev/test_data/main/golang/expected/nested_collections/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/nested_collections/expected_output/verification/verification.go index e743b75d1..3cf9a26fe 100644 --- a/dev/test_data/main/golang/expected/nested_collections/expected_output/verification/verification.go +++ b/dev/test_data/main/golang/expected/nested_collections/expected_output/verification/verification.go @@ -53,7 +53,7 @@ func GridIsRectangular( if len(grid) < 1 { return true } - var firstRow []int64 = grid[0] + var firstRow []int64 = ourcommon.At(grid, 0) for _, row := range grid { if len(row) != len(firstRow) { return false @@ -109,8 +109,8 @@ func ResetCorner( ) bool { if ( len(grid) > 0 && - len(grid[0]) > 0) { - grid[0][0] = 0 + len(ourcommon.At(grid, 0)) > 0) { + *ourcommon.PointerAt(ourcommon.At(grid, 0), 0) = 0 } return true } diff --git a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/common/common.go b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/common/common.go index d6209808e..59ca5d004 100644 --- a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/common/common.go +++ b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/common/common.go @@ -223,6 +223,28 @@ func (tuple Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Len() int { return 8 } +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T { + if index < 0 { + index += int64(len(slice)) + } + return slice[index] +} + +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T { + if index < 0 { + index += int64(len(slice)) + } + return &slice[index] +} + // Resolve `position` in a string of `length` as Python does in slicing. // // A negative position counts from the end, and the positions out of range are diff --git a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/types/types.go b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/types/types.go index 510eaa801..12e15d3e9 100644 --- a/dev/test_data/main/golang/expected/transpiled_methods/expected_output/types/types.go +++ b/dev/test_data/main/golang/expected/transpiled_methods/expected_output/types/types.go @@ -341,7 +341,7 @@ func (f *First) FillNumbers( numbers []int64, ) { for i := 0; i < len(numbers); i++ { - numbers[i] = f.Count() + *ourcommon.PointerAt(numbers, int64(i)) = f.Count() } } @@ -361,7 +361,7 @@ func (f *First) FillTexts( text string, ) { for i := 0; i < len(f.Texts()); i++ { - f.Texts()[i] = text + *ourcommon.PointerAt(f.Texts(), int64(i)) = text } } @@ -640,7 +640,7 @@ func (s *Second) FillNumbers( numbers []int64, ) { for i := 0; i < len(numbers); i++ { - numbers[i] = s.Count() + *ourcommon.PointerAt(numbers, int64(i)) = s.Count() } } @@ -660,7 +660,7 @@ func (s *Second) FillTexts( text string, ) { for i := 0; i < len(s.Texts()); i++ { - s.Texts()[i] = text + *ourcommon.PointerAt(s.Texts(), int64(i)) = text } } diff --git a/dev/test_data/main/golang/expected/variance_of_containers/expected_output/common/common.go b/dev/test_data/main/golang/expected/variance_of_containers/expected_output/common/common.go index 27b8b0c32..7a79dc5f3 100644 --- a/dev/test_data/main/golang/expected/variance_of_containers/expected_output/common/common.go +++ b/dev/test_data/main/golang/expected/variance_of_containers/expected_output/common/common.go @@ -223,6 +223,28 @@ func (tuple Tuple8[T1, T2, T3, T4, T5, T6, T7, T8]) Len() int { return 8 } +// Get the item of the `slice` at the `index`. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func At[T any](slice []T, index int64) T { + if index < 0 { + index += int64(len(slice)) + } + return slice[index] +} + +// Point to the item of the `slice` at the `index` so that it can be set. +// +// A negative `index` counts from the back of the `slice`, as in Python. +// Panics if the `index` is out of range. +func PointerAt[T any](slice []T, index int64) *T { + if index < 0 { + index += int64(len(slice)) + } + return &slice[index] +} + // Count the characters (code points) of `text`. // // We follow the Python implementation of `len`, since Python is the language of diff --git a/dev/test_data/main/golang/expected/variance_of_containers/expected_output/verification/verification.go b/dev/test_data/main/golang/expected/variance_of_containers/expected_output/verification/verification.go index 177b1c15d..83e723d7b 100644 --- a/dev/test_data/main/golang/expected/variance_of_containers/expected_output/verification/verification.go +++ b/dev/test_data/main/golang/expected/variance_of_containers/expected_output/verification/verification.go @@ -106,7 +106,7 @@ func FirstIsSpecial[ItemsT ourtypes.IItem]( if len(items) == 0 { return false } - first := items[0] + first := ourcommon.At(items, 0) if ourtypes.IsSpecialItem(first) { return any(first).(ourtypes.ISpecialItem).Level() > 0 } @@ -174,7 +174,7 @@ func LastIsSpecial[ItemsT ourtypes.IItem]( if len(items) == 0 { return false } - var last ourtypes.IItem = items[len(items) - 1] + var last ourtypes.IItem = ourcommon.At(items, int64(len(items) - 1)) if ourtypes.IsSpecialItem(last) { return last.(ourtypes.ISpecialItem).Level() > 0 } diff --git a/dev/test_data/main/java/expected/aas_core_meta.v3/expected_output/src/main/java/aas_core/aas3_0/common/ListHelpers.java b/dev/test_data/main/java/expected/aas_core_meta.v3/expected_output/src/main/java/aas_core/aas3_0/common/ListHelpers.java new file mode 100644 index 000000000..cbab6620b --- /dev/null +++ b/dev/test_data/main/java/expected/aas_core_meta.v3/expected_output/src/main/java/aas_core/aas3_0/common/ListHelpers.java @@ -0,0 +1,63 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package aas_core.aas3_0.common; + +import java.util.List; + +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers { + private ListHelpers() { + // Prevent instantiation + } + + /** + * Resolve {@code index} in a list of {@code size} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ + private static int resolveIndex(long index, int size) { + return Math.toIntExact(index < 0 ? index + size : index); + } + + /** + * Get the item of {@code list} at {@code index}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static T at(List list, long index) { + return list.get(resolveIndex(index, list.size())); + } + + /** + * Set the item of {@code list} at {@code index} to {@code value}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static void setAt(List list, long index, T value) { + list.set(resolveIndex(index, list.size()), value); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/aas_core_meta.v3/expected_output/src/main/java/aas_core/aas3_0/verification/Verification.java b/dev/test_data/main/java/expected/aas_core_meta.v3/expected_output/src/main/java/aas_core/aas3_0/verification/Verification.java index 2049679c2..970d92c25 100644 --- a/dev/test_data/main/java/expected/aas_core_meta.v3/expected_output/src/main/java/aas_core/aas3_0/verification/Verification.java +++ b/dev/test_data/main/java/expected/aas_core_meta.v3/expected_output/src/main/java/aas_core/aas3_0/verification/Verification.java @@ -1415,7 +1415,7 @@ public static Boolean isModelReferenceTo( KeyTypes expectedType) { return reference.getType() == ReferenceTypes.MODEL_REFERENCE && reference.getKeys().size() != 0 - && reference.getKeys().get(reference.getKeys().size() - 1).getType() == expectedType; + && ListHelpers.at(reference.getKeys(), -1).getType() == expectedType; } /** @@ -1425,7 +1425,7 @@ public static Boolean isModelReferenceToReferable( IReference reference) { return reference.getType() == ReferenceTypes.MODEL_REFERENCE && reference.getKeys().size() != 0 - && Constants.aasReferables.contains(reference.getKeys().get(reference.getKeys().size() - 1).getType()); + && Constants.aasReferables.contains(ListHelpers.at(reference.getKeys(), -1).getType()); } /** @@ -7338,7 +7338,7 @@ public Stream transformReference( if (!( !(that.getKeys().size() >= 1) - || Constants.globallyIdentifiables.contains(that.getKeys().get(0).getType()))) { + || Constants.globallyIdentifiables.contains(ListHelpers.at(that.getKeys(), 0).getType()))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( "Invariant violated:\n" + @@ -7352,7 +7352,7 @@ public Stream transformReference( that.getType() == ReferenceTypes.EXTERNAL_REFERENCE && that.getKeys().size() >= 1 ) - || Constants.genericGloballyIdentifiables.contains(that.getKeys().get(0).getType()))) { + || Constants.genericGloballyIdentifiables.contains(ListHelpers.at(that.getKeys(), 0).getType()))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( "Invariant violated:\n" + @@ -7366,7 +7366,7 @@ public Stream transformReference( that.getType() == ReferenceTypes.MODEL_REFERENCE && that.getKeys().size() >= 1 ) - || Constants.aasIdentifiables.contains(that.getKeys().get(0).getType()))) { + || Constants.aasIdentifiables.contains(ListHelpers.at(that.getKeys(), 0).getType()))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( "Invariant violated:\n" + @@ -7380,8 +7380,8 @@ public Stream transformReference( && that.getKeys().size() >= 1 ) || ( - Constants.genericGloballyIdentifiables.contains(that.getKeys().get(that.getKeys().size() - 1).getType()) - || Constants.genericFragmentKeys.contains(that.getKeys().get(that.getKeys().size() - 1).getType()) + Constants.genericGloballyIdentifiables.contains(ListHelpers.at(that.getKeys(), -1).getType()) + || Constants.genericFragmentKeys.contains(ListHelpers.at(that.getKeys(), -1).getType()) ))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( @@ -7401,7 +7401,7 @@ public Stream transformReference( 1, that.getKeys().size() ).allMatch( - i -> Constants.fragmentKeys.contains(that.getKeys().get(i).getType())) + i -> Constants.fragmentKeys.contains(ListHelpers.at(that.getKeys(), i).getType())) ))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( @@ -7422,7 +7422,7 @@ public Stream transformReference( 0, that.getKeys().size() - 1 ).allMatch( - i -> !Constants.genericFragmentKeys.contains(that.getKeys().get(i).getType())) + i -> !Constants.genericFragmentKeys.contains(ListHelpers.at(that.getKeys(), i).getType())) ))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( @@ -7438,11 +7438,11 @@ public Stream transformReference( !( that.getType() == ReferenceTypes.MODEL_REFERENCE && that.getKeys().size() > 1 - && that.getKeys().get(that.getKeys().size() - 1).getType() == KeyTypes.FRAGMENT_REFERENCE + && ListHelpers.at(that.getKeys(), -1).getType() == KeyTypes.FRAGMENT_REFERENCE ) || ( - that.getKeys().get(that.getKeys().size() - 2).getType() == KeyTypes.FILE - || that.getKeys().get(that.getKeys().size() - 2).getType() == KeyTypes.BLOB + ListHelpers.at(that.getKeys(), -2).getType() == KeyTypes.FILE + || ListHelpers.at(that.getKeys(), -2).getType() == KeyTypes.BLOB ))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( @@ -7462,8 +7462,8 @@ public Stream transformReference( 0, that.getKeys().size() - 1 ).allMatch( - i -> !(that.getKeys().get(i).getType() == KeyTypes.SUBMODEL_ELEMENT_LIST) - || matchesXsNonNegativeInteger(that.getKeys().get(i + 1).getValue())) + i -> !(ListHelpers.at(that.getKeys(), i).getType() == KeyTypes.SUBMODEL_ELEMENT_LIST) + || matchesXsNonNegativeInteger(ListHelpers.at(that.getKeys(), i + 1).getValue())) ))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( diff --git a/dev/test_data/main/java/expected/assignment_targets/expected_output/src/main/java/dummy/common/ListHelpers.java b/dev/test_data/main/java/expected/assignment_targets/expected_output/src/main/java/dummy/common/ListHelpers.java new file mode 100644 index 000000000..e075e8257 --- /dev/null +++ b/dev/test_data/main/java/expected/assignment_targets/expected_output/src/main/java/dummy/common/ListHelpers.java @@ -0,0 +1,63 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.common; + +import java.util.List; + +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers { + private ListHelpers() { + // Prevent instantiation + } + + /** + * Resolve {@code index} in a list of {@code size} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ + private static int resolveIndex(long index, int size) { + return Math.toIntExact(index < 0 ? index + size : index); + } + + /** + * Get the item of {@code list} at {@code index}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static T at(List list, long index) { + return list.get(resolveIndex(index, list.size())); + } + + /** + * Set the item of {@code list} at {@code index} to {@code value}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static void setAt(List list, long index, T value) { + list.set(resolveIndex(index, list.size()), value); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/assignment_targets/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/assignment_targets/expected_output/src/main/java/dummy/verification/Verification.java index ccd51a27f..7280b633c 100644 --- a/dev/test_data/main/java/expected/assignment_targets/expected_output/src/main/java/dummy/verification/Verification.java +++ b/dev/test_data/main/java/expected/assignment_targets/expected_output/src/main/java/dummy/verification/Verification.java @@ -88,8 +88,8 @@ public static Boolean setTexts( public static Boolean setFirstAndLastText( IItem item, String text) { - item.getTexts().set(0, text); - item.getTexts().set(item.getTexts().size() - 1, text); + ListHelpers.setAt(item.getTexts(), 0, text); + ListHelpers.setAt(item.getTexts(), -1, text); return true; } @@ -100,7 +100,7 @@ public static Boolean setTextThroughListAlias( IItem item, String text) { var texts = item.getTexts(); - texts.set(1, text); + ListHelpers.setAt(texts, 1, text); return true; } @@ -110,10 +110,8 @@ public static Boolean setTextThroughListAlias( public static Boolean setNumbers( List numbers, Long number) { - numbers.set(0, number); - numbers.set( - numbers.size() - 2, - numbers.get(numbers.size() - 1) + 1); + ListHelpers.setAt(numbers, 0, number); + ListHelpers.setAt(numbers, -2, ListHelpers.at(numbers, -1) + 1); return true; } @@ -123,8 +121,8 @@ public static Boolean setNumbers( public static Boolean setNestedText( List items, String text) { - items.get(0).getTexts().set(items.get(0).getTexts().size() - 1, text); - items.get(items.size() - 1).setText(text); + ListHelpers.setAt(ListHelpers.at(items, 0).getTexts(), -1, text); + ListHelpers.at(items, -1).setText(text); return true; } @@ -134,7 +132,7 @@ public static Boolean setNestedText( public static Boolean replaceFirstItem( List items, IItem item) { - items.set(0, item); + ListHelpers.setAt(items, 0, item); return true; } @@ -148,7 +146,7 @@ public static Boolean setTextsInLoops( item.setText(text); } for (int i = 0; i < items.size(); i++) { - items.get(i).getTexts().set(0, text); + ListHelpers.setAt(ListHelpers.at(items, i).getTexts(), 0, text); } return true; } @@ -160,7 +158,7 @@ public static Boolean fillTexts( List texts, String text) { for (int i = 0; i < texts.size(); i++) { - texts.set(i, text); + ListHelpers.setAt(texts, i, text); } return true; } @@ -223,7 +221,7 @@ public static Boolean setFirstTextsOfLists( List> lists, String text) { for (var texts : lists) { - texts.set(0, text); + ListHelpers.setAt(texts, 0, text); } return true; } @@ -237,11 +235,11 @@ public static Boolean setFirstTextsInSiblingLoops( String text) { for (var item : items) { var texts = item.getTexts(); - texts.set(0, text); + ListHelpers.setAt(texts, 0, text); } for (var other : others) { var texts = other.getTexts(); - texts.set(0, text); + ListHelpers.setAt(texts, 0, text); } return true; } @@ -262,9 +260,9 @@ public static Boolean textCopyIsIndependent( */ public static Boolean numberCopyIsIndependent( List numbers) { - long first = numbers.get(0); - numbers.set(0, first + 1); - return (first + 1) == (numbers.get(0)); + long first = ListHelpers.at(numbers, 0); + ListHelpers.setAt(numbers, 0, first + 1); + return (first + 1) == (ListHelpers.at(numbers, 0)); } /** @@ -307,7 +305,7 @@ public static Boolean firstTextThroughAliasIs( IItem item, String text) { var texts = item.getTexts(); - return Objects.equals(texts.get(0), text); + return Objects.equals(ListHelpers.at(texts, 0), text); } /** diff --git a/dev/test_data/main/java/expected/for_statements/expected_output/src/main/java/dummy/common/ListHelpers.java b/dev/test_data/main/java/expected/for_statements/expected_output/src/main/java/dummy/common/ListHelpers.java new file mode 100644 index 000000000..e075e8257 --- /dev/null +++ b/dev/test_data/main/java/expected/for_statements/expected_output/src/main/java/dummy/common/ListHelpers.java @@ -0,0 +1,63 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.common; + +import java.util.List; + +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers { + private ListHelpers() { + // Prevent instantiation + } + + /** + * Resolve {@code index} in a list of {@code size} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ + private static int resolveIndex(long index, int size) { + return Math.toIntExact(index < 0 ? index + size : index); + } + + /** + * Get the item of {@code list} at {@code index}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static T at(List list, long index) { + return list.get(resolveIndex(index, list.size())); + } + + /** + * Set the item of {@code list} at {@code index} to {@code value}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static void setAt(List list, long index, T value) { + list.set(resolveIndex(index, list.size()), value); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/for_statements/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/for_statements/expected_output/src/main/java/dummy/verification/Verification.java index 65ff5a367..ab508cfa0 100644 --- a/dev/test_data/main/java/expected/for_statements/expected_output/src/main/java/dummy/verification/Verification.java +++ b/dev/test_data/main/java/expected/for_statements/expected_output/src/main/java/dummy/verification/Verification.java @@ -60,7 +60,7 @@ public static Boolean noNumberIsZero( public static Boolean noNumberIsMinusOne( List numbers) { for (int i = 0; i < numbers.size(); i++) { - long number = numbers.get(i); + long number = ListHelpers.at(numbers, i); if (number == -1) { return false; } @@ -74,7 +74,7 @@ public static Boolean noNumberIsMinusOne( public static Boolean noNumberAfterTheFirstIsOne( List numbers) { for (int i = 1; i < numbers.size(); i++) { - if (numbers.get(i) == 1) { + if (ListHelpers.at(numbers, i) == 1) { return false; } } @@ -166,7 +166,7 @@ public static Boolean itemsAreFewAndTextsExpected( long count = 0; for (var item : items) { for (int i = 0; i < item.getTexts().size(); i++) { - var text = item.getTexts().get(i); + var text = ListHelpers.at(item.getTexts(), i); switch (text) { case "unexpected" -> { return false; @@ -235,10 +235,10 @@ public static Boolean textsBeforeStopAreFew( for (var item : items) { for (int i = 0; i < item.getTexts().size(); i++) { if (kind == Kind.ALPHA) { - if (Objects.equals(item.getTexts().get(i), "stop")) { + if (Objects.equals(ListHelpers.at(item.getTexts(), i), "stop")) { break; } - } else if (StringHelpers.len(item.getTexts().get(i)) > 10) { + } else if (StringHelpers.len(ListHelpers.at(item.getTexts(), i)) > 10) { break; } count = count + 1; diff --git a/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/common/ListHelpers.java b/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/common/ListHelpers.java new file mode 100644 index 000000000..e075e8257 --- /dev/null +++ b/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/common/ListHelpers.java @@ -0,0 +1,63 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.common; + +import java.util.List; + +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers { + private ListHelpers() { + // Prevent instantiation + } + + /** + * Resolve {@code index} in a list of {@code size} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ + private static int resolveIndex(long index, int size) { + return Math.toIntExact(index < 0 ? index + size : index); + } + + /** + * Get the item of {@code list} at {@code index}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static T at(List list, long index) { + return list.get(resolveIndex(index, list.size())); + } + + /** + * Set the item of {@code list} at {@code index} to {@code value}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static void setAt(List list, long index, T value) { + list.set(resolveIndex(index, list.size()), value); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/types/impl/Something.java b/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/types/impl/Something.java index f14d59c3a..80c02f17a 100644 --- a/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/types/impl/Something.java +++ b/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/types/impl/Something.java @@ -127,7 +127,7 @@ public Boolean isValidIndex( public String textAt( Long index ) { - return this.getTexts().get(Math.toIntExact(index)); + return ListHelpers.at(this.getTexts(), index); } /** diff --git a/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/verification/Verification.java index c756236b9..791f35b7d 100644 --- a/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/verification/Verification.java +++ b/dev/test_data/main/java/expected/int_length_interplay/expected_output/src/main/java/dummy/verification/Verification.java @@ -53,17 +53,16 @@ public static Boolean noNumberIsSeven( List numbers, Long count) { for (long i = 0; i < count; i++) { - long number = numbers.get(Math.toIntExact(i)); + long number = ListHelpers.at(numbers, i); if (number == 7) { return false; } - long mirrored = ( - numbers.get(Math.toIntExact((numbers.size() - 1) - i))); + long mirrored = ListHelpers.at(numbers, (numbers.size() - 1) - i); if (mirrored == 7) { return false; } long rotated = ( - numbers.get(Math.toIntExact(Math.floorMod(i + 1, (long) numbers.size())))); + ListHelpers.at(numbers, Math.floorMod(i + 1, (long) numbers.size()))); if (rotated == 7) { return false; } @@ -89,7 +88,7 @@ public static Boolean noSevenFrom( return true; } for (long i = start; i < numbers.size(); i++) { - long number = numbers.get(Math.toIntExact(i)); + long number = ListHelpers.at(numbers, i); if (number == 7) { return false; } @@ -98,7 +97,23 @@ public static Boolean noSevenFrom( start, numbers.size() ).allMatch( - i -> numbers.get(Math.toIntExact(i)) != 7); + i -> ListHelpers.at(numbers, i) != 7); + } + + /** + * Check reading at the negative indices which are known only at run time. + * + *

A negative index counts from the back of the list, as in Python. + */ + public static Boolean numbersAscendFromBack( + List numbers, + Long count) { + for (long i = 1; i < count; i++) { + if (ListHelpers.at(numbers, -i) <= ListHelpers.at(numbers, (-i) - 1)) { + return false; + } + } + return true; } /** @@ -109,8 +124,9 @@ public static Boolean fillTextsFromBothEnds( Long count, String text) { for (long i = 0; i < count; i++) { - texts.set(Math.toIntExact(i), text); - texts.set(Math.toIntExact((texts.size() - 1) - i), text); + ListHelpers.setAt(texts, i, text); + ListHelpers.setAt(texts, (texts.size() - 1) - i, text); + ListHelpers.setAt(texts, -1 - i, text); } return true; } @@ -135,7 +151,7 @@ public static Boolean setCounts( List texts) { something.setCount((long) texts.size()); something.setMaybeCount((long) texts.size()); - something.getNumbers().set(0, (long) texts.size()); + ListHelpers.setAt(something.getNumbers(), 0, (long) texts.size()); return true; } @@ -281,6 +297,14 @@ public Stream transformSomething( "No number is seven"))); } + if (!( + numbersAscendFromBack(that.getNumbers(), (long) that.getNumbers().size()))) { + errorStream = Stream.concat(errorStream, + Stream.of(new Reporting.Error( + "Invariant violated:\n" + + "Numbers ascend"))); + } + errorStream = Stream.concat(errorStream, Stream.of(that.getNumbers()) .flatMap(Verification::verifyListOf_long) diff --git a/dev/test_data/main/java/expected/json_types_in_invariants/expected_output/src/main/java/dummy/common/ListHelpers.java b/dev/test_data/main/java/expected/json_types_in_invariants/expected_output/src/main/java/dummy/common/ListHelpers.java new file mode 100644 index 000000000..469840c22 --- /dev/null +++ b/dev/test_data/main/java/expected/json_types_in_invariants/expected_output/src/main/java/dummy/common/ListHelpers.java @@ -0,0 +1,83 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.common; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import java.util.List; + +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers { + private ListHelpers() { + // Prevent instantiation + } + + /** + * Resolve {@code index} in a list of {@code size} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ + private static int resolveIndex(long index, int size) { + return Math.toIntExact(index < 0 ? index + size : index); + } + + /** + * Get the item of {@code list} at {@code index}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static T at(List list, long index) { + return list.get(resolveIndex(index, list.size())); + } + + /** + * Set the item of {@code list} at {@code index} to {@code value}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static void setAt(List list, long index, T value) { + list.set(resolveIndex(index, list.size()), value); + } + + /** + * Get the item of {@code array} at {@code index}. + * + * @param array to get the item from + * @param index of the item; negative counts from the back + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static JsonNode at(ArrayNode array, long index) { + final int resolved = resolveIndex(index, array.size()); + if (resolved < 0 || resolved >= array.size()) { + throw new IndexOutOfBoundsException( + "Index " + index + " out of range for a JSON array of size " + array.size() + ); + } + return array.get(resolved); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/json_types_in_invariants/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/json_types_in_invariants/expected_output/src/main/java/dummy/verification/Verification.java index be90a9601..bd51e4557 100644 --- a/dev/test_data/main/java/expected/json_types_in_invariants/expected_output/src/main/java/dummy/verification/Verification.java +++ b/dev/test_data/main/java/expected/json_types_in_invariants/expected_output/src/main/java/dummy/verification/Verification.java @@ -284,7 +284,7 @@ public Stream transformSomething( if (!( !(that.getValues().size() > 0 - && (that.getValues().get(0).isNull())))) { + && (ListHelpers.at(that.getValues(), 0).isNull())))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( "Invariant violated:\n" + @@ -305,7 +305,7 @@ public Stream transformSomething( if (!( !(that.getValues().size() >= 1) - || isAcceptable(that.getValues().get(that.getValues().size() - 1)))) { + || isAcceptable(ListHelpers.at(that.getValues(), -1)))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( "Invariant violated:\n" + @@ -314,7 +314,7 @@ public Stream transformSomething( if (!( !(that.getValues().size() > 0) - || isAcceptable(that.getValues().get(0)))) { + || isAcceptable(ListHelpers.at(that.getValues(), 0)))) { errorStream = Stream.concat(errorStream, Stream.of(new Reporting.Error( "Invariant violated:\n" + diff --git a/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/common/ListHelpers.java b/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/common/ListHelpers.java new file mode 100644 index 000000000..e075e8257 --- /dev/null +++ b/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/common/ListHelpers.java @@ -0,0 +1,63 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.common; + +import java.util.List; + +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers { + private ListHelpers() { + // Prevent instantiation + } + + /** + * Resolve {@code index} in a list of {@code size} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ + private static int resolveIndex(long index, int size) { + return Math.toIntExact(index < 0 ? index + size : index); + } + + /** + * Get the item of {@code list} at {@code index}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static T at(List list, long index) { + return list.get(resolveIndex(index, list.size())); + } + + /** + * Set the item of {@code list} at {@code index} to {@code value}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static void setAt(List list, long index, T value) { + list.set(resolveIndex(index, list.size()), value); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/verification/Verification.java index c7c0f3f84..090b0d2cc 100644 --- a/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/verification/Verification.java +++ b/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/verification/Verification.java @@ -115,7 +115,7 @@ public static Boolean floatsAreUnique( List numbers) { for (int i = 0; i < numbers.size(); i++) { for (int j = i + 1; j < numbers.size(); j++) { - if ((double) numbers.get(i) == numbers.get(j)) { + if ((double) ListHelpers.at(numbers, i) == ListHelpers.at(numbers, j)) { return false; } } diff --git a/dev/test_data/main/java/expected/nested_collections/expected_output/src/main/java/dummy/common/ListHelpers.java b/dev/test_data/main/java/expected/nested_collections/expected_output/src/main/java/dummy/common/ListHelpers.java new file mode 100644 index 000000000..e075e8257 --- /dev/null +++ b/dev/test_data/main/java/expected/nested_collections/expected_output/src/main/java/dummy/common/ListHelpers.java @@ -0,0 +1,63 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.common; + +import java.util.List; + +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers { + private ListHelpers() { + // Prevent instantiation + } + + /** + * Resolve {@code index} in a list of {@code size} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ + private static int resolveIndex(long index, int size) { + return Math.toIntExact(index < 0 ? index + size : index); + } + + /** + * Get the item of {@code list} at {@code index}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static T at(List list, long index) { + return list.get(resolveIndex(index, list.size())); + } + + /** + * Set the item of {@code list} at {@code index} to {@code value}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static void setAt(List list, long index, T value) { + list.set(resolveIndex(index, list.size()), value); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/nested_collections/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/nested_collections/expected_output/src/main/java/dummy/verification/Verification.java index 780bb9546..ee33b7e44 100644 --- a/dev/test_data/main/java/expected/nested_collections/expected_output/src/main/java/dummy/verification/Verification.java +++ b/dev/test_data/main/java/expected/nested_collections/expected_output/src/main/java/dummy/verification/Verification.java @@ -38,7 +38,7 @@ public static Boolean gridIsRectangular( if (grid.size() < 1) { return true; } - final List firstRow = grid.get(0); + final List firstRow = ListHelpers.at(grid, 0); for (var row : grid) { if (row.size() != firstRow.size()) { return false; @@ -82,9 +82,9 @@ public static Boolean resetCorner( List> grid) { if ( grid.size() > 0 - && (grid.get(0)).size() > 0 + && (ListHelpers.at(grid, 0)).size() > 0 ) { - grid.get(0).set(0, 0L); + ListHelpers.setAt(ListHelpers.at(grid, 0), 0, 0L); } return true; } diff --git a/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/common/ListHelpers.java b/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/common/ListHelpers.java new file mode 100644 index 000000000..e075e8257 --- /dev/null +++ b/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/common/ListHelpers.java @@ -0,0 +1,63 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.common; + +import java.util.List; + +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers { + private ListHelpers() { + // Prevent instantiation + } + + /** + * Resolve {@code index} in a list of {@code size} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ + private static int resolveIndex(long index, int size) { + return Math.toIntExact(index < 0 ? index + size : index); + } + + /** + * Get the item of {@code list} at {@code index}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static T at(List list, long index) { + return list.get(resolveIndex(index, list.size())); + } + + /** + * Set the item of {@code list} at {@code index} to {@code value}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static void setAt(List list, long index, T value) { + list.set(resolveIndex(index, list.size()), value); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/types/impl/First.java b/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/types/impl/First.java index cc9755923..a36440cbc 100644 --- a/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/types/impl/First.java +++ b/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/types/impl/First.java @@ -161,7 +161,7 @@ public void fillNumbers( List numbers ) { for (int i = 0; i < numbers.size(); i++) { - numbers.set(i, this.getCount()); + ListHelpers.setAt(numbers, i, this.getCount()); } } @@ -187,7 +187,7 @@ public void fillTexts( String text ) { for (int i = 0; i < this.getTexts().size(); i++) { - this.getTexts().set(i, text); + ListHelpers.setAt(this.getTexts(), i, text); } } diff --git a/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/types/impl/Second.java b/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/types/impl/Second.java index b48e1aa5d..e77ee9dba 100644 --- a/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/types/impl/Second.java +++ b/dev/test_data/main/java/expected/transpiled_methods/expected_output/src/main/java/dummy/types/impl/Second.java @@ -186,7 +186,7 @@ public void fillNumbers( List numbers ) { for (int i = 0; i < numbers.size(); i++) { - numbers.set(i, this.getCount()); + ListHelpers.setAt(numbers, i, this.getCount()); } } @@ -212,7 +212,7 @@ public void fillTexts( String text ) { for (int i = 0; i < this.getTexts().size(); i++) { - this.getTexts().set(i, text); + ListHelpers.setAt(this.getTexts(), i, text); } } diff --git a/dev/test_data/main/java/expected/variance_of_containers/expected_output/src/main/java/dummy/common/ListHelpers.java b/dev/test_data/main/java/expected/variance_of_containers/expected_output/src/main/java/dummy/common/ListHelpers.java new file mode 100644 index 000000000..e075e8257 --- /dev/null +++ b/dev/test_data/main/java/expected/variance_of_containers/expected_output/src/main/java/dummy/common/ListHelpers.java @@ -0,0 +1,63 @@ +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ + +package dummy.common; + +import java.util.List; + +/** + * Provide the access to the items of lists which follows the Python + * implementation, since Python is the language of the meta-model specifications. + * + *

A negative index counts from the back of the list. + */ +public final class ListHelpers { + private ListHelpers() { + // Prevent instantiation + } + + /** + * Resolve {@code index} in a list of {@code size} items as Python does. + * + * @param index of the item; negative counts from the back + * @param size of the list + * @return the resolved index + * @throws ArithmeticException if the resolved index does not fit into an int + */ + private static int resolveIndex(long index, int size) { + return Math.toIntExact(index < 0 ? index + size : index); + } + + /** + * Get the item of {@code list} at {@code index}. + * + * @param list to get the item from + * @param index of the item; negative counts from the back + * @param type of the items + * @return the item + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static T at(List list, long index) { + return list.get(resolveIndex(index, list.size())); + } + + /** + * Set the item of {@code list} at {@code index} to {@code value}. + * + * @param list to set the item in + * @param index of the item; negative counts from the back + * @param value to be set + * @param type of the items + * @throws IndexOutOfBoundsException if {@code index} is out of range + */ + public static void setAt(List list, long index, T value) { + list.set(resolveIndex(index, list.size()), value); + } +} + +/* + * This code has been automatically generated by aas-core-codegen. + * Do NOT edit or append. + */ diff --git a/dev/test_data/main/java/expected/variance_of_containers/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/variance_of_containers/expected_output/src/main/java/dummy/verification/Verification.java index d475124a9..f1f3bbd1f 100644 --- a/dev/test_data/main/java/expected/variance_of_containers/expected_output/src/main/java/dummy/verification/Verification.java +++ b/dev/test_data/main/java/expected/variance_of_containers/expected_output/src/main/java/dummy/verification/Verification.java @@ -84,7 +84,7 @@ public static Boolean firstIsSpecial( if (items.size() == 0) { return false; } - var first = items.get(0); + var first = ListHelpers.at(items, 0); if (first instanceof ISpecialItem) { return ((ISpecialItem) first).getLevel() > 0; } @@ -151,7 +151,7 @@ public static Boolean lastIsSpecial( if (items.size() == 0) { return false; } - final IItem last = items.get(items.size() - 1); + final IItem last = ListHelpers.at(items, items.size() - 1); if (last instanceof ISpecialItem) { return ((ISpecialItem) last).getLevel() > 0; } diff --git a/dev/test_data/main/python/expected/int_length_interplay/expected_output/dummy/verification.py b/dev/test_data/main/python/expected/int_length_interplay/expected_output/dummy/verification.py index 78540657b..517c43ee0 100644 --- a/dev/test_data/main/python/expected/int_length_interplay/expected_output/dummy/verification.py +++ b/dev/test_data/main/python/expected/int_length_interplay/expected_output/dummy/verification.py @@ -127,6 +127,21 @@ def no_seven_from( )) +def numbers_ascend_from_back( + numbers: Sequence[int], + count: int +) -> bool: + """ + Check reading at the negative indices which are known only at run time. + + A negative index counts from the back of the list, as in Python. + """ + for i in range(1, count): + if numbers[-i] <= numbers[(-i) - 1]: + return False + return True + + def fill_texts_from_both_ends( texts: List[str], count: int, @@ -138,6 +153,7 @@ def fill_texts_from_both_ends( for i in range(0, count): texts[i] = text texts[(len(texts) - 1) - i] = text + texts[-1 - i] = text return True @@ -225,6 +241,16 @@ def transform_something( 'No number is seven' ) + if not ( + numbers_ascend_from_back( + that.numbers, + len(that.numbers) + ) + ): + yield Error( + 'Numbers ascend' + ) + for error in _verify_list_of__int(that.numbers): error.path._prepend( PropertySegment( diff --git a/dev/test_data/main/typescript/expected/aas_core_meta.v3/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/aas_core_meta.v3/expected_output/src/common.ts index 11a40830a..32b6a51be 100644 --- a/dev/test_data/main/typescript/expected/aas_core_meta.v3/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/aas_core_meta.v3/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/annotated_assignments/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/annotated_assignments/expected_output/src/common.ts index 5405db1c9..470587346 100644 --- a/dev/test_data/main/typescript/expected/annotated_assignments/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/annotated_assignments/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/arithmetic/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/arithmetic/expected_output/src/common.ts index 182707a14..ed524392c 100644 --- a/dev/test_data/main/typescript/expected/arithmetic/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/arithmetic/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/assert_statements/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/assert_statements/expected_output/src/common.ts index 208c38cc4..8ea62f415 100644 --- a/dev/test_data/main/typescript/expected/assert_statements/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/assert_statements/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/assignment_targets/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/assignment_targets/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/assignment_targets/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/assignment_targets/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/collections_in_returns/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/collections_in_returns/expected_output/src/common.ts index 250f4b1de..f22191dd5 100644 --- a/dev/test_data/main/typescript/expected/collections_in_returns/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/collections_in_returns/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/comparisons/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/comparisons/expected_output/src/common.ts index 208c38cc4..8ea62f415 100644 --- a/dev/test_data/main/typescript/expected/comparisons/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/comparisons/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/conflict_with_utility_types/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/conflict_with_utility_types/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/conflict_with_utility_types/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/conflict_with_utility_types/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/constrained_primitives/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/constrained_primitives/expected_output/src/common.ts index 208c38cc4..8ea62f415 100644 --- a/dev/test_data/main/typescript/expected/constrained_primitives/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/constrained_primitives/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/custom_serialization_names/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/custom_serialization_names/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/custom_serialization_names/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/custom_serialization_names/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/common.ts index 4e8a7101c..5607156b2 100644 --- a/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/deep_class_hierarchy/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/empty_class/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/empty_class/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/empty_class/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/empty_class/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/enum/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/enum/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/enum/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/enum/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/float_only/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/float_only/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/float_only/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/float_only/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/for_statements/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/for_statements/expected_output/src/common.ts index 182707a14..ed524392c 100644 --- a/dev/test_data/main/typescript/expected/for_statements/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/for_statements/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/if_statements/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/if_statements/expected_output/src/common.ts index 182707a14..ed524392c 100644 --- a/dev/test_data/main/typescript/expected/if_statements/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/if_statements/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/implementation_specific/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/implementation_specific/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/implementation_specific/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/implementation_specific/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/int_length_interplay/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/int_length_interplay/expected_output/src/common.ts index f5d71a34a..14cc4dde7 100644 --- a/dev/test_data/main/typescript/expected/int_length_interplay/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/int_length_interplay/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/int_length_interplay/expected_output/src/verification.ts b/dev/test_data/main/typescript/expected/int_length_interplay/expected_output/src/verification.ts index c6704d081..292cef36b 100644 --- a/dev/test_data/main/typescript/expected/int_length_interplay/expected_output/src/verification.ts +++ b/dev/test_data/main/typescript/expected/int_length_interplay/expected_output/src/verification.ts @@ -275,6 +275,25 @@ export function noSevenFrom( ); } +/** + * Check reading at the negative indices which are known only at run time. + * + * @remarks + * + * A negative index counts from the back of the list, as in Python. + */ +export function numbersAscendFromBack( + numbers: Array, + count: number +): boolean { + for (let i = 1; i < count; i++) { + if (OurCommon.at(numbers, -i) <= OurCommon.at(numbers, (-i) - 1)) { + return false; + } + } + return true; +} + /** * Check writing at the indices which are integers rather than lengths. */ @@ -286,6 +305,7 @@ export function fillTextsFromBothEnds( for (let i = 0; i < count; i++) { OurCommon.setAt(texts, i, text); OurCommon.setAt(texts, (texts.length - 1) - i, text); + OurCommon.setAt(texts, -1 - i, text); } return true; } @@ -416,6 +436,17 @@ class Verifier ) } + if (!( + numbersAscendFromBack( + that.numbers, + that.numbers.length + ) + )) { + yield new VerificationError( + "Numbers ascend" + ) + } + for (const error of verifyInteger(that.count)) { error.path.prepend( new PropertySegment( diff --git a/dev/test_data/main/typescript/expected/int_parsing/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/int_parsing/expected_output/src/common.ts index c34358b04..f36253328 100644 --- a/dev/test_data/main/typescript/expected/int_parsing/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/int_parsing/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/is_instance/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/is_instance/expected_output/src/common.ts index 11a40830a..32b6a51be 100644 --- a/dev/test_data/main/typescript/expected/is_instance/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/is_instance/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/json_types/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/json_types/expected_output/src/common.ts index 11a40830a..32b6a51be 100644 --- a/dev/test_data/main/typescript/expected/json_types/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/json_types/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/json_types_in_collections/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/json_types_in_collections/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/json_types_in_collections/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/json_types_in_collections/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/json_types_in_invariants/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/json_types_in_invariants/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/json_types_in_invariants/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/json_types_in_invariants/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/common.ts index 208c38cc4..8ea62f415 100644 --- a/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/common.ts index 4d4e1e52b..eebb17ea3 100644 --- a/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/common.ts index 208c38cc4..8ea62f415 100644 --- a/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/method_references/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/method_references/expected_output/src/common.ts index 208c38cc4..8ea62f415 100644 --- a/dev/test_data/main/typescript/expected/method_references/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/method_references/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/nested_any_in_all/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/nested_any_in_all/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/nested_any_in_all/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/nested_any_in_all/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/nested_collections/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/nested_collections/expected_output/src/common.ts index d3721acc6..1db4f95f2 100644 --- a/dev/test_data/main/typescript/expected/nested_collections/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/nested_collections/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/optional_arguments/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/optional_arguments/expected_output/src/common.ts index 208c38cc4..8ea62f415 100644 --- a/dev/test_data/main/typescript/expected/optional_arguments/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/optional_arguments/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/primitive_types/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/primitive_types/expected_output/src/common.ts index 4e8a7101c..5607156b2 100644 --- a/dev/test_data/main/typescript/expected/primitive_types/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/primitive_types/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/problematic_keywords/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/problematic_keywords/expected_output/src/common.ts index 1ad845117..850e4e44a 100644 --- a/dev/test_data/main/typescript/expected/problematic_keywords/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/problematic_keywords/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/sets/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/sets/expected_output/src/common.ts index f48fba020..62baa32bb 100644 --- a/dev/test_data/main/typescript/expected/sets/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/sets/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/string_operations/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/string_operations/expected_output/src/common.ts index 91c972806..e5ecf603c 100644 --- a/dev/test_data/main/typescript/expected/string_operations/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/string_operations/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/switch_statements/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/switch_statements/expected_output/src/common.ts index 208c38cc4..8ea62f415 100644 --- a/dev/test_data/main/typescript/expected/switch_statements/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/switch_statements/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/transpiled_methods/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/transpiled_methods/expected_output/src/common.ts index 229d1ba35..e2f313bdf 100644 --- a/dev/test_data/main/typescript/expected/transpiled_methods/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/transpiled_methods/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/tuples/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/tuples/expected_output/src/common.ts index 4e8a7101c..5607156b2 100644 --- a/dev/test_data/main/typescript/expected/tuples/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/tuples/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/unions/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/unions/expected_output/src/common.ts index 11a40830a..32b6a51be 100644 --- a/dev/test_data/main/typescript/expected/unions/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/unions/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/variance_of_containers/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/variance_of_containers/expected_output/src/common.ts index 208c38cc4..8ea62f415 100644 --- a/dev/test_data/main/typescript/expected/variance_of_containers/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/variance_of_containers/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/main/typescript/expected/visibility/expected_output/src/common.ts b/dev/test_data/main/typescript/expected/visibility/expected_output/src/common.ts index 11a40830a..32b6a51be 100644 --- a/dev/test_data/main/typescript/expected/visibility/expected_output/src/common.ts +++ b/dev/test_data/main/typescript/expected/visibility/expected_output/src/common.ts @@ -26,23 +26,27 @@ export function *range(start: number, end: number): IterableIterator { * Retrieve the `index`-th item from the `array`. * * @remarks - * This is a fill for `Array.prototype.at`. - * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at + * Unlike `Array.prototype.at`, we do not give `undefined` on an out-of-bound + * `index`, but throw so that we follow the semantics of the meta-model. * * @param array - to get the element from * @param index - zero-based index of the `array`. Negative index counts back. - * @returns item, or `undefined` if `index` out-of-bound + * @returns item at the `index` + * @throws {@link RangeError} if `index` out-of-bound * @typeParam T - type of the array items */ export function at( array: Array, index: number -) { - if (index < 0) { - return array[array.length + index]; - } else { - return array[index]; +): T { + const resolved = index < 0 ? array.length + index : index; + if (resolved < 0 || resolved >= array.length) { + throw new RangeError( + `The index ${index} is out of bound ` + + `for the array of length ${array.length}` + ); } + return array[resolved]; } diff --git a/dev/test_data/parse/common_meta_models/int_length_interplay/expected_symbol_table.txt b/dev/test_data/parse/common_meta_models/int_length_interplay/expected_symbol_table.txt index 74879127f..9b019a700 100644 --- a/dev/test_data/parse/common_meta_models/int_length_interplay/expected_symbol_table.txt +++ b/dev/test_data/parse/common_meta_models/int_length_interplay/expected_symbol_table.txt @@ -458,6 +458,30 @@ UnverifiedSymbolTable( original_node=...)], original_node=...)], original_node=...)"""), + node=...), + Invariant( + description='Numbers ascend', + body=textwrap.dedent("""\ + FunctionCall( + name='numbers_ascend_from_back', + args=[ + Member( + instance=Name( + identifier='self', + original_node=...), + name='numbers', + original_node=...), + FunctionCall( + name='len', + args=[ + Member( + instance=Name( + identifier='self', + original_node=...), + name='numbers', + original_node=...)], + original_node=...)], + original_node=...)"""), node=...)], serialization=None, description=None, @@ -916,6 +940,104 @@ UnverifiedSymbolTable( original_node=...)""")], node=..., arguments_by_name=...), + UnderstoodMethod( + name='numbers_ascend_from_back', + verification=True, + visibility='PUBLIC', + arguments=[ + Argument( + name='numbers', + type_annotation=SubscriptedTypeAnnotation( + identifier='Sequence', + subscripts=[ + AtomicTypeAnnotation( + identifier='int', + node=...)], + node=...), + default=None, + node=...), + Argument( + name='count', + type_annotation=AtomicTypeAnnotation( + identifier='int', + node=...), + default=None, + node=...)], + returns=AtomicTypeAnnotation( + identifier='bool', + node=...), + description=Description( + document=..., + node=...), + contracts=Contracts( + preconditions=[], + snapshots=[], + postconditions=[]), + non_mutating=False, + body=[ + textwrap.dedent("""\ + For( + generator=ForRange( + variable=Name( + identifier='i', + original_node=...), + start=Constant( + value=1, + original_node=...), + end=Name( + identifier='count', + original_node=...), + original_node=...), + body=[ + If( + branches=[ + IfBranch( + condition=Comparison( + left=Index( + collection=Name( + identifier='numbers', + original_node=...), + index=Neg( + operand=Name( + identifier='i', + original_node=...), + original_node=...), + original_node=...), + op='LE', + right=Index( + collection=Name( + identifier='numbers', + original_node=...), + index=Sub( + left=Neg( + operand=Name( + identifier='i', + original_node=...), + original_node=...), + right=Constant( + value=1, + original_node=...), + original_node=...), + original_node=...), + original_node=...), + body=[ + Return( + value=Constant( + value=False, + original_node=...), + original_node=...)], + original_node=...)], + default=None, + original_node=...)], + original_node=...)"""), + textwrap.dedent("""\ + Return( + value=Constant( + value=True, + original_node=...), + original_node=...)""")], + node=..., + arguments_by_name=...), UnderstoodMethod( name='fill_texts_from_both_ends', verification=True, @@ -1013,6 +1135,25 @@ UnverifiedSymbolTable( identifier='text', original_node=...), annotation=None, + original_node=...), + Assignment( + target=Index( + collection=Name( + identifier='texts', + original_node=...), + index=Sub( + left=Constant( + value=-1, + original_node=...), + right=Name( + identifier='i', + original_node=...), + original_node=...), + original_node=...), + value=Name( + identifier='text', + original_node=...), + annotation=None, original_node=...)], original_node=...)"""), textwrap.dedent("""\