diff --git a/tested/datatypes/__init__.py b/tested/datatypes/__init__.py index 746b267a..8627f0a4 100644 --- a/tested/datatypes/__init__.py +++ b/tested/datatypes/__init__.py @@ -15,6 +15,7 @@ from tested.datatypes.advanced import ( AdvancedNothingTypes, AdvancedNumericTypes, + AdvancedObjectTypes, AdvancedSequenceTypes, AdvancedStringTypes, AdvancedTypes, @@ -36,7 +37,7 @@ BooleanTypes = BasicBooleanTypes NothingTypes = BasicNothingTypes | AdvancedNothingTypes SequenceTypes = BasicSequenceTypes | AdvancedSequenceTypes -ObjectTypes = BasicObjectTypes +ObjectTypes = BasicObjectTypes | AdvancedObjectTypes SimpleTypes = NumericTypes | StringTypes | BooleanTypes | NothingTypes ComplexTypes = SequenceTypes | ObjectTypes diff --git a/tested/datatypes/advanced.py b/tested/datatypes/advanced.py index 8bcdf954..02ad8fed 100644 --- a/tested/datatypes/advanced.py +++ b/tested/datatypes/advanced.py @@ -10,6 +10,7 @@ from tested.datatypes.basic import ( BasicNothingTypes, BasicNumericTypes, + BasicObjectTypes, BasicSequenceTypes, BasicStringTypes, BasicTypes, @@ -110,9 +111,21 @@ class AdvancedNothingTypes(_AdvancedDataType): """ +class AdvancedObjectTypes(_AdvancedDataType): + DICTIONARY = "dictionary", BasicObjectTypes.MAP + """ + A proper map/dictionary/associative array data structure. + """ + OBJECT = "object", BasicObjectTypes.MAP + """ + An object like in JavaScript. + """ + + AdvancedTypes = Union[ AdvancedNumericTypes, AdvancedSequenceTypes, AdvancedStringTypes, AdvancedNothingTypes, + AdvancedObjectTypes, ] diff --git a/tested/features.py b/tested/features.py index fe5ad4af..c71f3a43 100644 --- a/tested/features.py +++ b/tested/features.py @@ -11,7 +11,14 @@ from attrs import define -from tested.datatypes import AllTypes, BasicObjectTypes, BasicSequenceTypes, NestedTypes +from tested.datatypes import ( + AllTypes, + BasicObjectTypes, + BasicSequenceTypes, + ComplexExpressionTypes, + NestedTypes, + resolve_to_basic, +) if TYPE_CHECKING: from tested.languages.config import Language @@ -166,20 +173,28 @@ def is_supported(language: "Language") -> bool: return False nested_types = [] for key, value_types in required.nested_types: - if key in (BasicSequenceTypes.SET, BasicObjectTypes.MAP): + # Skip these + if isinstance(key, ComplexExpressionTypes): + continue + basic_key = resolve_to_basic(key) + if basic_key in (BasicSequenceTypes.SET, BasicObjectTypes.MAP): nested_types.append((key, value_types)) - restricted = { - BasicSequenceTypes.SET: language.set_type_restrictions(), - BasicObjectTypes.MAP: language.map_type_restrictions(), - } + collection_restrictions = language.collection_restrictions() for key, value_types in nested_types: - if not (value_types <= restricted[key]): + basic_key = resolve_to_basic(key) + restrictions = collection_restrictions.get( + key, collection_restrictions.get(basic_key) + ) + # If None, skip as there are no restrictions. + if restrictions is None: + continue + if not (value_types <= restrictions): _logger.warning("This test suite is not compatible!") - _logger.warning(f"Required {key} types are {value_types}.") - _logger.warning(f"The language supports {restricted[key]}.") - missing = (value_types ^ restricted[key]) & value_types + _logger.warning(f"For {key}, used types are {value_types}.") + _logger.warning(f"Language restrictions are {restrictions}.") + missing = (value_types ^ restrictions) & value_types _logger.warning(f"Missing types are: {missing}.") return False diff --git a/tested/languages/config.py b/tested/languages/config.py index 1e8ba2cb..10b171c2 100644 --- a/tested/languages/config.py +++ b/tested/languages/config.py @@ -243,29 +243,24 @@ def supported_constructs(self) -> set[Construct]: """ return set() - def map_type_restrictions(self) -> set[ExpressionTypes] | None: + def collection_restrictions(self) -> dict[AllTypes, set[ExpressionTypes]]: """ - Get type restrictions that apply to map types in this language. + Get type restrictions for other types. - If you return None, all data types are assumed to be usable as the key in - a map data type, such as a dictionary or hashmap. Otherwise, you must return - a whitelist of the allowed types. + The exact interpretation varies by the type. Only some restrictions are + currently recognized: - :return: The whitelist of allowed types, or everything is allowed. - """ - return None - - def set_type_restrictions(self) -> set[ExpressionTypes] | None: - """ - Get type restrictions that apply to the set types in this language. + Restrictions on `BasicObjectTypes.Map` (or related advanced types) are + interpreted as restrictions on the type of the keys. The provided types + are a whitelist: the only allowed types. - If you return None, all data types are assumed to be usable as the key in - a set data type, such as a HashSet. Otherwise, you must return a whitelist - of the allowed types. + Restrictions on `BasicSetTypes.SET` (or related advanced types) are + interpreted as restrictions on the type of the elements. The provided + types are a whitelist: the only allowed types. - :return: The whitelist of allowed types, or everything is allowed. + :return: Mapping of types to their restrictions. Unmapped types are unrestricted. """ - return None + return dict() def datatype_support(self) -> dict[AllTypes, TypeSupport]: """ diff --git a/tested/languages/csharp/config.py b/tested/languages/csharp/config.py index 651d2ed0..8fe5951f 100644 --- a/tested/languages/csharp/config.py +++ b/tested/languages/csharp/config.py @@ -76,6 +76,8 @@ def datatype_support(self) -> dict[AllTypes, TypeSupport]: "sequence": "supported", "set": "supported", "map": "supported", + "dictionary": "supported", + "object": "reduced", "nothing": "supported", "undefined": "reduced", "null": "reduced", diff --git a/tested/languages/csharp/templates/Values.cs b/tested/languages/csharp/templates/Values.cs index 29afbedb..1a54a9be 100644 --- a/tested/languages/csharp/templates/Values.cs +++ b/tested/languages/csharp/templates/Values.cs @@ -88,7 +88,7 @@ class Values type = "set"; data = encodeSequence((IEnumerable) value); } else if (value is IDictionary) { - type = "map"; + type = "dictionary"; List entries = new List(); foreach (DictionaryEntry entry in (IDictionary) value) { diff --git a/tested/languages/java/config.py b/tested/languages/java/config.py index 2d67f641..a61c3fc6 100644 --- a/tested/languages/java/config.py +++ b/tested/languages/java/config.py @@ -3,7 +3,12 @@ from pathlib import Path from typing import TYPE_CHECKING -from tested.datatypes import AllTypes, ExpressionTypes +from tested.datatypes import ( + AllTypes, + BasicObjectTypes, + BasicSequenceTypes, + ExpressionTypes, +) from tested.dodona import AnnotateCode, Message from tested.features import Construct, TypeSupport from tested.languages.config import ( @@ -71,6 +76,8 @@ def datatype_support(self) -> dict[AllTypes, TypeSupport]: "sequence": "supported", "set": "supported", "map": "supported", + "dictionary": "supported", + "object": "reduced", "nothing": "supported", "undefined": "reduced", "null": "reduced", @@ -91,8 +98,8 @@ def datatype_support(self) -> dict[AllTypes, TypeSupport]: "list": "supported", } - def map_type_restrictions(self) -> set[ExpressionTypes] | None: - return { # type: ignore + def collection_restrictions(self) -> dict[AllTypes, set[ExpressionTypes]]: + restrictions = { "integer", "real", "char", @@ -113,9 +120,10 @@ def map_type_restrictions(self) -> set[ExpressionTypes] | None: "function_calls", "identifiers", } - - def set_type_restrictions(self) -> set[ExpressionTypes] | None: - return self.map_type_restrictions() + return { + BasicObjectTypes.MAP: restrictions, # type: ignore + BasicSequenceTypes.SET: restrictions, + } def compilation(self, files: list[str]) -> CallbackResult: def file_filter(file: Path) -> bool: @@ -171,6 +179,8 @@ def get_declaration_metadata(self) -> TypeDeclarationMetadata: "sequence": "List", "set": "Set", "map": "Map", + "dictionary": "Map", + "object": "Map", "nothing": "Void", "undefined": "Void", "int8": "byte", diff --git a/tested/languages/java/templates/Values.java b/tested/languages/java/templates/Values.java index bfcb54b0..e00e548e 100644 --- a/tested/languages/java/templates/Values.java +++ b/tested/languages/java/templates/Values.java @@ -112,7 +112,7 @@ private static List internalEncode(Object value) { type = "set"; data = encodeSequence((Iterable) value); } else if (value instanceof Map) { - type = "map"; + type = "dictionary"; var elements = new ArrayList(); for (Map.Entry entry : ((Map) value).entrySet()) { elements.add("{ \"key\":" + encode(entry.getKey()) + ",\"value\": " + encode(entry.getValue()) + "}"); @@ -163,7 +163,7 @@ private static String convertMessage(EvaluationResult.Message message) { var description = asJson(message.description); var format = asJson(message.format); var permission = asJson(message.permission); - + return """ { "description": %s, @@ -172,7 +172,7 @@ private static String convertMessage(EvaluationResult.Message message) { } """.formatted(description, format, permission); } - + private static String asJson(String value) { if (value == null) { return "null"; @@ -188,7 +188,7 @@ public static void sendEvaluated(PrintWriter writer, EvaluationResult r) { var dslExpected = asJson(r.dslExpected); var dslActual = asJson(r.dslActual); var messages = String.join(", ", converted); - + String result = """ { "result": %b, diff --git a/tested/languages/javascript/config.py b/tested/languages/javascript/config.py index 624374d5..f1cd8ffc 100644 --- a/tested/languages/javascript/config.py +++ b/tested/languages/javascript/config.py @@ -3,7 +3,12 @@ from pathlib import Path from typing import TYPE_CHECKING -from tested.datatypes import AllTypes, BasicStringTypes, ExpressionTypes +from tested.datatypes import ( + AdvancedObjectTypes, + AllTypes, + BasicStringTypes, + ExpressionTypes, +) from tested.dodona import AnnotateCode, Message from tested.features import Construct, TypeSupport from tested.languages.config import ( @@ -72,6 +77,8 @@ def datatype_support(self) -> dict[AllTypes, TypeSupport]: "sequence": "supported", "set": "supported", "map": "supported", + "dictionary": "supported", + "object": "supported", "nothing": "supported", "undefined": "supported", "null": "supported", @@ -92,21 +99,8 @@ def datatype_support(self) -> dict[AllTypes, TypeSupport]: "tuple": "reduced", } - def map_type_restrictions(self) -> set[ExpressionTypes] | None: - return {BasicStringTypes.TEXT} - - def set_type_restrictions(self) -> set[ExpressionTypes] | None: - return { # type: ignore - "integer", - "real", - "text", - "boolean", - "sequence", - "set", - "map", - "function_calls", - "identifiers", - } + def collection_restrictions(self) -> dict[AllTypes, set[ExpressionTypes]]: + return {AdvancedObjectTypes.OBJECT: {BasicStringTypes.TEXT}} def compilation(self, files: list[str]) -> CallbackResult: submission = submission_file(self) diff --git a/tested/languages/javascript/generators.py b/tested/languages/javascript/generators.py index 2993cf85..b1d53cf6 100644 --- a/tested/languages/javascript/generators.py +++ b/tested/languages/javascript/generators.py @@ -11,6 +11,7 @@ BasicSequenceTypes, BasicStringTypes, ) +from tested.datatypes.advanced import AdvancedObjectTypes from tested.languages.conventionalize import submission_file from tested.languages.preparation import ( PreparedContext, @@ -48,6 +49,17 @@ def convert_value(value: Value) -> str: raise AssertionError("Double extended values are not supported in js.") elif value.type == AdvancedNumericTypes.FIXED_PRECISION: raise AssertionError("Fixed precision values are not supported in js.") + elif value.type == AdvancedObjectTypes.OBJECT: + assert isinstance(value, ObjectType) + result = "{" + for i, pair in enumerate(value.data): + result += convert_statement(pair.key, True) + result += ": " + result += convert_statement(pair.value, True) + if i != len(value.data) - 1: + result += ", " + result += "}" + return result elif value.type in ( AdvancedNumericTypes.INT_64, AdvancedNumericTypes.U_INT_64, @@ -79,14 +91,16 @@ def convert_value(value: Value) -> str: return f"new Set([{convert_arguments(value.data)}])" elif value.type == BasicObjectTypes.MAP: assert isinstance(value, ObjectType) - result = "{" + result = "new Map([" for i, pair in enumerate(value.data): + result += "[" result += convert_statement(pair.key, True) - result += ": " + result += ", " result += convert_statement(pair.value, True) + result += "]" if i != len(value.data) - 1: result += ", " - result += "}" + result += "])" return result elif value.type == BasicStringTypes.UNKNOWN: assert isinstance(value, StringType) diff --git a/tested/languages/javascript/templates/values.js b/tested/languages/javascript/templates/values.js index 987fba72..82cacfaa 100644 --- a/tested/languages/javascript/templates/values.js +++ b/tested/languages/javascript/templates/values.js @@ -53,7 +53,7 @@ function encode(value) { type = "set"; value = Array.from(value).map(encode); } else if (value instanceof Map) { - type = "map"; + type = "dictionary"; value = Array .from(value) .map(([key, value]) => { @@ -65,7 +65,7 @@ function encode(value) { ); } else if (value?.constructor === Object) { // Plain objects - type = "map"; + type = "object"; // Process the elements of the object. value = Object.entries(value).map(([key, value]) => { return { diff --git a/tested/languages/kotlin/config.py b/tested/languages/kotlin/config.py index c3467605..99a32f47 100644 --- a/tested/languages/kotlin/config.py +++ b/tested/languages/kotlin/config.py @@ -4,7 +4,12 @@ from pathlib import Path from typing import TYPE_CHECKING -from tested.datatypes import AllTypes, ExpressionTypes +from tested.datatypes import ( + AllTypes, + BasicObjectTypes, + BasicSequenceTypes, + ExpressionTypes, +) from tested.dodona import AnnotateCode, Message, Status from tested.features import Construct, TypeSupport from tested.languages.config import ( @@ -80,6 +85,8 @@ def datatype_support(self) -> dict[AllTypes, TypeSupport]: "sequence": "supported", "set": "supported", "map": "supported", + "dictionary": "supported", + "object": "reduced", "nothing": "supported", "undefined": "reduced", "null": "reduced", @@ -101,8 +108,8 @@ def datatype_support(self) -> dict[AllTypes, TypeSupport]: "tuple": "reduced", } - def map_type_restrictions(self) -> set[ExpressionTypes] | None: - return { # type: ignore + def collection_restrictions(self) -> dict[AllTypes, set[ExpressionTypes]]: + restrictions = { "integer", "real", "char", @@ -124,9 +131,10 @@ def map_type_restrictions(self) -> set[ExpressionTypes] | None: "function_calls", "identifiers", } - - def set_type_restrictions(self) -> set[ExpressionTypes] | None: - return self.map_type_restrictions() + return { + BasicObjectTypes.MAP: restrictions, # type: ignore + BasicSequenceTypes.SET: restrictions, # type: ignore + } def compilation(self, files: list[str]) -> CallbackResult: def file_filter(file: Path) -> bool: diff --git a/tested/languages/kotlin/templates/Values.kt b/tested/languages/kotlin/templates/Values.kt index aef14142..c1399c65 100644 --- a/tested/languages/kotlin/templates/Values.kt +++ b/tested/languages/kotlin/templates/Values.kt @@ -124,7 +124,7 @@ private fun internalEncode(value: Any?): Array { type = "set" data = encodeSequence(value.asIterable()) } else if (value is Map<*, *>) { - type = "map" + type = "dictionary" data = value.asSequence() .map { e -> String.format("{\"key\": %s, \"value\": %s }", encode(e.key), diff --git a/tested/languages/python/config.py b/tested/languages/python/config.py index 91138f0a..e28e99a1 100644 --- a/tested/languages/python/config.py +++ b/tested/languages/python/config.py @@ -4,7 +4,12 @@ from pathlib import Path from typing import TYPE_CHECKING -from tested.datatypes import AllTypes, ExpressionTypes +from tested.datatypes import ( + AllTypes, + BasicObjectTypes, + BasicSequenceTypes, + ExpressionTypes, +) from tested.dodona import AnnotateCode, Message, Severity from tested.features import Construct, TypeSupport from tested.languages.config import ( @@ -77,6 +82,8 @@ def datatype_support(self) -> dict[AllTypes, TypeSupport]: "sequence": "supported", "set": "supported", "map": "supported", + "dictionary": "supported", + "object": "reduced", "nothing": "supported", "undefined": "reduced", "null": "reduced", @@ -98,8 +105,8 @@ def datatype_support(self) -> dict[AllTypes, TypeSupport]: "tuple": "supported", } - def map_type_restrictions(self) -> set[ExpressionTypes] | None: - return { # type: ignore + def collection_restrictions(self) -> dict[AllTypes, set[ExpressionTypes]]: + restrictions = { "integer", "real", "text", @@ -110,9 +117,10 @@ def map_type_restrictions(self) -> set[ExpressionTypes] | None: "function_calls", "identifiers", } - - def set_type_restrictions(self) -> set[ExpressionTypes] | None: - return self.map_type_restrictions() + return { + BasicObjectTypes.MAP: restrictions, # type: ignore + BasicSequenceTypes.SET: restrictions, # type: ignore + } def compilation(self, files: list[str]) -> CallbackResult: result = [x.replace(".py", ".pyc") for x in files] diff --git a/tested/languages/python/templates/values.py b/tested/languages/python/templates/values.py index ea38a0ac..4d81270c 100644 --- a/tested/languages/python/templates/values.py +++ b/tested/languages/python/templates/values.py @@ -51,7 +51,7 @@ def encode(value): type_ = "set" data_ = [encode(x) for x in value] elif isinstance(value, dict): - type_ = "map" + type_ = "dictionary" data_ = [{"key": encode(k), "value": encode(v)} for k, v in value.items()] else: type_ = "unknown" diff --git a/tests/exercises/objects/evaluation/missing_key_types_js_dictionary.yaml b/tests/exercises/objects/evaluation/missing_key_types_js_dictionary.yaml new file mode 100644 index 00000000..d299c4ee --- /dev/null +++ b/tests/exercises/objects/evaluation/missing_key_types_js_dictionary.yaml @@ -0,0 +1,4 @@ +- tab: "Feedback" + testcases: + - expression: 'dictionary({5: "a"})' + return: {5: "a"} diff --git a/tests/exercises/objects/evaluation/missing_key_types_js_object.yaml b/tests/exercises/objects/evaluation/missing_key_types_js_object.yaml new file mode 100644 index 00000000..a8689719 --- /dev/null +++ b/tests/exercises/objects/evaluation/missing_key_types_js_object.yaml @@ -0,0 +1,4 @@ +- tab: "Feedback" + testcases: + - expression: 'object({5: "a"})' + return: "invalid" diff --git a/tests/test_dsl_expression.py b/tests/test_dsl_expression.py index 2a8b4b05..1d8d9c9b 100644 --- a/tests/test_dsl_expression.py +++ b/tests/test_dsl_expression.py @@ -5,11 +5,13 @@ from tested.datatypes import ( AdvancedNothingTypes, AdvancedNumericTypes, + AdvancedObjectTypes, AdvancedSequenceTypes, AdvancedStringTypes, BasicBooleanTypes, BasicNothingTypes, BasicNumericTypes, + BasicObjectTypes, BasicSequenceTypes, BasicStringTypes, ObjectTypes, @@ -229,7 +231,49 @@ def test_parse_value_adv_sequence(): def test_parse_value_dict(): parsed = parse_string('{"ignore": True, 5: 0}') - assert parsed.type == ObjectTypes.MAP + assert parsed.type == BasicObjectTypes.MAP + parsed: ObjectType + assert len(parsed.data) == 2 + key, value = parsed.data[0].key, parsed.data[0].value + assert isinstance(key, StringType) + assert key.type == BasicStringTypes.TEXT + assert key.data == "ignore" + assert isinstance(value, BooleanType) + assert value.type == BasicBooleanTypes.BOOLEAN + assert value.data is True + key, value = parsed.data[1].key, parsed.data[1].value + assert isinstance(key, NumberType) + assert key.type == BasicNumericTypes.INTEGER + assert key.data == 5 + assert isinstance(value, NumberType) + assert value.type == BasicNumericTypes.INTEGER + assert value.data == 0 + + +def test_parse_value_dictionary(): + parsed = parse_string('dictionary({"ignore": True, 5: 0})') + assert parsed.type == AdvancedObjectTypes.DICTIONARY + parsed: ObjectType + assert len(parsed.data) == 2 + key, value = parsed.data[0].key, parsed.data[0].value + assert isinstance(key, StringType) + assert key.type == BasicStringTypes.TEXT + assert key.data == "ignore" + assert isinstance(value, BooleanType) + assert value.type == BasicBooleanTypes.BOOLEAN + assert value.data is True + key, value = parsed.data[1].key, parsed.data[1].value + assert isinstance(key, NumberType) + assert key.type == BasicNumericTypes.INTEGER + assert key.data == 5 + assert isinstance(value, NumberType) + assert value.type == BasicNumericTypes.INTEGER + assert value.data == 0 + + +def test_parse_value_object(): + parsed = parse_string('object({"ignore": True, 5: 0})') + assert parsed.type == AdvancedObjectTypes.OBJECT parsed: ObjectType assert len(parsed.data) == 2 key, value = parsed.data[0].key, parsed.data[0].value @@ -375,7 +419,7 @@ def test_parse_function(): assert function.name == "generate" assert len(function.arguments) == 1 arg = function.arguments[0] - assert arg.type == ObjectTypes.MAP + assert arg.type == BasicObjectTypes.MAP arg: ObjectType assert len(arg.data) == 1 pair: ObjectKeyValuePair = arg.data[0] diff --git a/tests/test_functionality.py b/tests/test_functionality.py index 361fc67f..37bf1122 100644 --- a/tests/test_functionality.py +++ b/tests/test_functionality.py @@ -511,10 +511,9 @@ def test_heterogeneous_arguments_are_detected(lang: str, tmp_path: Path, pytestc assert updates.find_status_enum() == ["internal error"] -@pytest.mark.parametrize("lang", ["python", "javascript"]) -def test_missing_key_types_detected(lang: str, tmp_path: Path, pytestconfig): +def test_missing_key_types_detected(tmp_path: Path, pytestconfig): conf = configuration( - pytestconfig, "objects", lang, tmp_path, "missing_key_types.yaml", "solution" + pytestconfig, "objects", "python", tmp_path, "missing_key_types.yaml", "correct" ) result = execute_config(conf) updates = assert_valid_output(result, pytestconfig) @@ -522,6 +521,36 @@ def test_missing_key_types_detected(lang: str, tmp_path: Path, pytestconfig): assert updates.find_status_enum() == ["internal error"] +def test_missing_key_types_detected_js_object(tmp_path: Path, pytestconfig): + conf = configuration( + pytestconfig, + "objects", + "javascript", + tmp_path, + "missing_key_types_js_object.yaml", + "correct", + ) + result = execute_config(conf) + updates = assert_valid_output(result, pytestconfig) + assert len(updates.find_all("start-testcase")) == 0 + assert updates.find_status_enum() == ["internal error"] + + +@pytest.mark.parametrize( + "suite", ["missing_key_types_js_dictionary", "missing_key_types"] +) +def test_missing_key_types_detected_js_dictionary( + suite: str, tmp_path: Path, pytestconfig +): + conf = configuration( + pytestconfig, "objects", "javascript", tmp_path, f"{suite}.yaml", "correct" + ) + result = execute_config(conf) + updates = assert_valid_output(result, pytestconfig) + assert len(updates.find_all("start-testcase")) == 1 + assert updates.find_status_enum() == ["correct"] + + @pytest.mark.parametrize("lang", ["python", "java", "kotlin", "javascript", "csharp"]) def test_programmed_evaluator_lotto(lang: str, tmp_path: Path, pytestconfig): conf = configuration( diff --git a/tests/test_serialisation.py b/tests/test_serialisation.py index 8eee9b1c..e23b575a 100644 --- a/tests/test_serialisation.py +++ b/tests/test_serialisation.py @@ -32,6 +32,7 @@ BasicTypes, resolve_to_basic, ) +from tested.datatypes.advanced import AdvancedObjectTypes from tested.features import TypeSupport, fallback_type_support_map from tested.judge.compilation import run_compilation from tested.judge.execution import execute_file, filter_files @@ -160,9 +161,26 @@ StringType(type=BasicStringTypes.TEXT, data="data"), ], ), - # Char StringType(type=AdvancedStringTypes.CHAR, data="h"), NothingType(type=AdvancedNothingTypes.UNDEFINED), + ObjectType( + type=AdvancedObjectTypes.DICTIONARY, + data=[ + ObjectKeyValuePair( + key=StringType(type=BasicStringTypes.TEXT, data="data"), + value=NumberType(type=BasicNumericTypes.INTEGER, data=5), + ) + ], + ), + ObjectType( + type=AdvancedObjectTypes.OBJECT, + data=[ + ObjectKeyValuePair( + key=StringType(type=BasicStringTypes.TEXT, data="data"), + value=NumberType(type=BasicNumericTypes.INTEGER, data=5), + ) + ], + ), ]