From 7eaad24c0587eda69f88b9d5cda2e479ad8e692f Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 30 Jul 2024 17:29:21 +0200 Subject: [PATCH] Fix isbn exercise --- tested/languages/cpp/config.py | 23 +- tested/languages/cpp/generators.py | 24 ++- tested/languages/cpp/templates/values.cpp | 106 +--------- tested/languages/cpp/templates/values.h | 46 ++-- tested/languages/cpp/templates/values.tpp | 197 ++++++++++++++++++ tested/manual.py | 6 +- .../one-with-crashing-assignment-cpp.tson | 89 ++++++++ tests/exercises/isbn/solution/solution.cpp | 96 +++++++++ 8 files changed, 452 insertions(+), 135 deletions(-) create mode 100644 tested/languages/cpp/templates/values.tpp create mode 100644 tests/exercises/isbn/evaluation/one-with-crashing-assignment-cpp.tson create mode 100644 tests/exercises/isbn/solution/solution.cpp diff --git a/tested/languages/cpp/config.py b/tested/languages/cpp/config.py index 457fa428..88c3ed42 100644 --- a/tested/languages/cpp/config.py +++ b/tested/languages/cpp/config.py @@ -1,6 +1,6 @@ from pathlib import Path -from tested.features import Construct - +from tested.features import Construct, TypeSupport +from tested.datatypes import AllTypes from tested.languages.c.config import C from tested.languages.conventionalize import Conventionable, NamingConventions from tested.languages.cpp.generators import CPPGenerator @@ -10,7 +10,7 @@ class CPP(C): def initial_dependencies(self) -> list[str]: - return ["values.h", "values.cpp", "evaluation_result.h", "evaluation_result.cpp"] + return ["values.h", "values.cpp", "values.tpp", "evaluation_result.h", "evaluation_result.cpp"] def file_extension(self) -> str: return "cpp" @@ -29,6 +29,21 @@ def supported_constructs(self) -> set[Construct]: Construct.ASSIGNMENTS, Construct.GLOBAL_VARIABLES, Construct.OBJECTS, + Construct.HETEROGENEOUS_COLLECTIONS, + Construct.DEFAULT_PARAMETERS, + Construct.HETEROGENEOUS_ARGUMENTS + } + + def datatype_support(self) -> dict[AllTypes, TypeSupport]: + return super().datatype_support() | { # type: ignore + "sequence": "supported", + "set": "supported", + "map": "supported", + "dictionary": "supported", + "object": "reduced", + "array": "supported", + "list": "supported", + "tuple": "supported", } def compilation(self, files: list[str]) -> CallbackResult: @@ -38,7 +53,7 @@ def compilation(self, files: list[str]) -> CallbackResult: return ( [ "g++", - "-std=c++11", + "-std=c++17", "-Wall", "-O3" if self.config.options.compiler_optimizations else "-O0", "evaluation_result.cpp", diff --git a/tested/languages/cpp/generators.py b/tested/languages/cpp/generators.py index a45b4930..00abfee0 100644 --- a/tested/languages/cpp/generators.py +++ b/tested/languages/cpp/generators.py @@ -1,10 +1,10 @@ from tested.datatypes import AllTypes, resolve_to_basic from tested.datatypes.advanced import AdvancedObjectTypes, AdvancedSequenceTypes, AdvancedStringTypes -from tested.datatypes.basic import BasicObjectTypes, BasicSequenceTypes, BasicTypes +from tested.datatypes.basic import BasicObjectTypes, BasicSequenceTypes, BasicStringTypes, BasicTypes from tested.languages.c.generators import CGenerator from tested.languages.preparation import PreparedExecutionUnit, PreparedFunctionCall, PreparedTestcase, PreparedTestcaseStatement -from tested.serialisation import FunctionCall, FunctionType, ObjectType, PropertyAssignment, SequenceType, Statement, VariableAssignment, VariableType, WrappedAllTypes +from tested.serialisation import FunctionCall, FunctionType, ObjectType, PropertyAssignment, SequenceType, Statement, Value, VariableAssignment, VariableType, WrappedAllTypes class CPPGenerator(CGenerator): @@ -41,6 +41,18 @@ def convert_map_subtypes(self, value: Statement, subtype: WrappedAllTypes) -> tu value_type_str = self.convert_declaration(value_base_type, None, value_sub_type) return key_type_str, value_type_str + + def convert_value(self, value: Value) -> str: + tp = value.type + basic = resolve_to_basic(tp) + if basic == BasicObjectTypes.MAP: + return "{" + ", ".join(f"{self.convert_value(k), self.convert_value(v)}" for k, v in value.data.items()) + "}" + elif basic == BasicSequenceTypes.SEQUENCE or basic == BasicSequenceTypes.SET: + return "{" + ", ".join(self.convert_value(v) for v in value.data) + "}" + elif basic == BasicStringTypes.TEXT: + return f'std::string("{value.data}")' + + return super().convert_value(value) @@ -73,6 +85,14 @@ def convert_declaration(self, tp: AllTypes | VariableType, elif basic == BasicSequenceTypes.SET: subtype = self.convert_sequence_subtype(value, subtype) return f"std::set<{subtype}>" + elif basic == BasicSequenceTypes.SEQUENCE: + subtype = self.convert_sequence_subtype(value, subtype) + return f"std::vector<{subtype}>" + elif basic == BasicStringTypes.TEXT: + return "std::string" + elif basic == BasicStringTypes.ANY: + return "std::any" + return super().convert_declaration(tp) diff --git a/tested/languages/cpp/templates/values.cpp b/tested/languages/cpp/templates/values.cpp index 8d859000..8db48ff6 100644 --- a/tested/languages/cpp/templates/values.cpp +++ b/tested/languages/cpp/templates/values.cpp @@ -1,16 +1,10 @@ #include -#include -#include -#include -#include -#include -#include -#include +#include #include "values.h" -using namespace std; +using namespace std; // Function to escape special characters in a string string escape(const string &buffer) { @@ -31,7 +25,8 @@ string escape(const string &buffer) { return dest; } -#define FORMAT(name, x) "{\"type\": \"" name "\", \"data\":" x "}" + + void write_formatted(FILE* out, const char* format, ...) { va_list args; @@ -40,99 +35,6 @@ void write_formatted(FILE* out, const char* format, ...) { va_end(args); } -void write_value(FILE* out, bool value) { - write_formatted(out, FORMAT("boolean", "%s"), value ? "true" : "false"); -} - -void write_value(FILE* out, char value) { - string buffer(1, value); - string result = escape(buffer); - write_formatted(out, FORMAT("char", "\"%s\""), result.c_str()); -} - -void write_value(FILE* out, signed char value) { - write_formatted(out, FORMAT("int8", "%d"), static_cast(value)); -} - -void write_value(FILE* out, unsigned char value) { - write_formatted(out, FORMAT("uint8", "%u"), static_cast(value)); -} - -void write_value(FILE* out, short int value) { - write_formatted(out, FORMAT("int16", "%d"), value); -} - -void write_value(FILE* out, unsigned short int value) { - write_formatted(out, FORMAT("uint16", "%u"), value); -} - -void write_value(FILE* out, int value) { - write_formatted(out, FORMAT("int32", "%d"), value); -} - -void write_value(FILE* out, unsigned int value) { - write_formatted(out, FORMAT("uint32", "%u"), value); -} - -void write_value(FILE* out, long value) { - write_formatted(out, FORMAT("int64", "%ld"), value); -} - -void write_value(FILE* out, unsigned long value) { - write_formatted(out, FORMAT("uint64", "%lu"), value); -} - -void write_value(FILE* out, long long value) { - write_formatted(out, FORMAT("integer", "%lld"), value); -} - -void write_value(FILE* out, unsigned long long value) { - write_formatted(out, FORMAT("bigint", "%llu"), value); -} - -void write_value(FILE* out, float value) { - if (isnan(value)) { - write_formatted(out, FORMAT("single_precision", "\"%s\""), "nan"); - } else if (isinf(value)) { - write_formatted(out, FORMAT("single_precision", "\"%s\""), value < 0 ? "-inf" : "inf"); - } else { - write_formatted(out, FORMAT("single_precision", "%f"), value); - } -} - -void write_value(FILE* out, double value) { - if (isnan(value)) { - write_formatted(out, FORMAT("double_precision", "\"%s\""), "nan"); - } else if (isinf(value)) { - write_formatted(out, FORMAT("double_precision", "\"%s\""), value < 0 ? "-inf" : "inf"); - } else { - write_formatted(out, FORMAT("double_precision", "%lf"), value); - } -} - -void write_value(FILE* out, long double value) { - if (isnan(value)) { - write_formatted(out, FORMAT("double_extended", "\"%s\""), "nan"); - } else if (isinf(value)) { - write_formatted(out, FORMAT("double_extended", "\"%s\""), value < 0 ? "-inf" : "inf"); - } else { - write_formatted(out, FORMAT("double_extended", "%Lf"), value); - } -} - -void write_value(FILE* out, const string &value) { - string result = escape(value); - write_formatted(out, FORMAT("text", "\"%s\""), result.c_str()); -} - -void write_value(FILE* out, void* value) { - write_formatted(out, FORMAT("nothing", "\"%s\""), "null"); -} - -template void write_value(FILE* out, T value) -{ - write_formatted(out, FORMAT("unknown", "%p"), "?"); -} // Function to write evaluated results void write_evaluated(FILE* out, EvaluationResult* result) { diff --git a/tested/languages/cpp/templates/values.h b/tested/languages/cpp/templates/values.h index c208e1f8..136431fd 100644 --- a/tested/languages/cpp/templates/values.h +++ b/tested/languages/cpp/templates/values.h @@ -5,35 +5,33 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "evaluation_result.h" -// Function to escape special characters in a string -std::string escape(const std::string &buffer); +string escape(const string &buffer); -// Function to write a formatted string to an output stream -void write_formatted(FILE* out, const char* format, ...); - -template void write_value(FILE* out, T value); -void write_value(FILE* out, bool value); -void write_value(FILE* out, char value); -void write_value(FILE* out, signed char value); -void write_value(FILE* out, unsigned char value); -void write_value(FILE* out, short int value); -void write_value(FILE* out, unsigned short int value); -void write_value(FILE* out, int value); -void write_value(FILE* out, unsigned int value); -void write_value(FILE* out, long value); -void write_value(FILE* out, unsigned long value); -void write_value(FILE* out, long long value); -void write_value(FILE* out, unsigned long long value); -void write_value(FILE* out, float value); -void write_value(FILE* out, double value); -void write_value(FILE* out, long double value); -void write_value(FILE* out, const std::string &value); -void write_value(FILE* out, void* value); -void write_value(FILE* out, void* value); +// Function to write a value as a json object with type information to a file +template void write_value(FILE* out, const T& value); // Function to write evaluated results void write_evaluated(FILE* out, EvaluationResult* result); + +// Include the implementation file for template functions +#include "values.tpp" + #endif //WRITER_VALUES_H diff --git a/tested/languages/cpp/templates/values.tpp b/tested/languages/cpp/templates/values.tpp new file mode 100644 index 00000000..1fdc0087 --- /dev/null +++ b/tested/languages/cpp/templates/values.tpp @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +string to_json(const T& value); + + +template +string getTypeName(const T&) { + if (is_same::value) return "integer"; + if (is_same::value) return "double_precision"; + if (is_same::value) return "single_precision"; + if (is_same::value) return "char"; + if (is_same::value) return "string"; + if (is_same::value) return "boolean"; + if (is_same::value) return "null"; + if (is_same::value) return "int8"; + if (is_same::value) return "uint8"; + if (is_same::value) return "int16"; + if (is_same::value) return "uint16"; + if (is_same::value) return "int32"; + if (is_same::value) return "uint32"; + if (is_same::value) return "int64"; + if (is_same::value) return "uint64"; + if (is_same::value) return "double_extended"; + if (is_same::value) return "nothing"; + return "undefined"; +} + +// Specialization for vector +template +string getTypeName(const vector&) { + return "sequence"; +} + +// Specialization for set +template +string getTypeName(const set&) { + return "set"; +} + +// Specialization for map +template +string getTypeName(const map&) { + return "map"; +} + +// Specialization for array +template +string getTypeName(const array&) { + return "array"; +} + +// Specialization for list +template +string getTypeName(const list&) { + return "list"; +} + +// Specialization for tuple +template +string getTypeName(const tuple&) { + return "tuple"; +} + +template +string to_json_value(const T& value) { + if constexpr (is_same::value) { + return "\"" + escape(value) + "\""; + } else if constexpr (is_same::value) { + return "\"" + string(1, value) + "\""; + } else if constexpr (is_same::value) { + return value ? "true" : "false"; + } else if constexpr (is_same::value) { + return "null"; + } else if constexpr (is_same::value) { + return "\"" + string(value) + "\""; + } else if constexpr (is_same::value || is_same::value || is_same::value) { + ostringstream oss; + oss << value; + return oss.str(); + } else { + return to_string(value); + } +} + + +template +string to_json_value(const vector& vec) { + string result = "["; + for (const auto& item : vec) { + result += to_json(item) + ","; + } + if (!vec.empty()) { + result.pop_back(); // remove trailing comma + } + result += "]"; + return result; +} + +template +string to_json_value(const set& set) { + string result = "["; + for (const auto& item : set) { + result += to_json(item) + ","; + } + if (!set.empty()) { + result.pop_back(); // remove trailing comma + } + result += "]"; + return result; +} + +template +string to_json_value(const map& map) { + string result = "{"; + for (const auto& item : map) { + result += "\"" + to_json_value(item.first) + "\": " + to_json(item.second) + ","; + } + if (!map.empty()) { + result.pop_back(); // remove trailing comma + } + result += "}"; + return result; +} + +template +string to_json_value(const array& arr) { + string result = "["; + for (const auto& item : arr) { + result += to_json(item) + ","; + } + if (N > 0) { + result.pop_back(); // remove trailing comma + } + result += "]"; + return result; +} + +template +string to_json_value(const tuple& tup); + +template +typename enable_if::type +to_json_value_helper(const tuple& tup, string& result) {} + +template +typename enable_if::type +to_json_value_helper(const tuple& tup, string& result) { + result += to_json(get(tup)) + ","; + to_json_value_helper(tup, result); +} + +template +string to_json_value(const tuple& tup) { + string result = "["; + to_json_value_helper(tup, result); + if (sizeof...(Args) > 0) { + result.pop_back(); // remove trailing comma + } + result += "]"; + return result; +} + +template +string to_json(const T& value) { + string json = "{ \"type\" : \"" + getTypeName(value) + "\", \"data\" : " + to_json_value(value) + " }"; + return json; +} + +template void write_value(FILE* out, const T& value) +{ + string json = to_json(value); + fprintf(out, "%s", json.c_str()); +} \ No newline at end of file diff --git a/tested/manual.py b/tested/manual.py index f7e08666..7bdef537 100644 --- a/tested/manual.py +++ b/tested/manual.py @@ -13,7 +13,7 @@ from tested.main import run from tested.testsuite import SupportedLanguage -exercise_dir = "/home/jorg/Documents/universal-judge/tests/exercises/echo-function-file" +exercise_dir = "/home/jorg/Documents/universal-judge/tests/exercises/isbn" def read_config() -> DodonaConfig: @@ -24,10 +24,10 @@ def read_config() -> DodonaConfig: programming_language=SupportedLanguage("cpp"), natural_language="nl", resources=Path(exercise_dir, "evaluation"), - source=Path(exercise_dir, "solution/correct.cpp"), + source=Path(exercise_dir, "solution/solution.cpp"), judge=Path("."), workdir=Path("workdir"), - test_suite="one.tson", + test_suite="one-with-assignment.tson", options=Options( linter=False, ), diff --git a/tests/exercises/isbn/evaluation/one-with-crashing-assignment-cpp.tson b/tests/exercises/isbn/evaluation/one-with-crashing-assignment-cpp.tson new file mode 100644 index 00000000..24406376 --- /dev/null +++ b/tests/exercises/isbn/evaluation/one-with-crashing-assignment-cpp.tson @@ -0,0 +1,89 @@ +{ + "tabs": [ + { + "name": "are_isbn", + "runs": [ + { + "contexts": [ + { + "before": { + "csharp": { + "data": "#include " + "data": "String Ex = () => {throw "An error occurred!";};" + } + }, + "testcases": [ + { + "input": { + "type": "sequence", + "variable": "codes01", + "expression": { + "type": "function", + "name": "ex", + "namespace": "", + "arguments": [] + } + } + }, + { + "input": { + "type": "function", + "name": "are_isbn", + "arguments": [ + "codes01" + ] + }, + "output": { + "result": { + "value": { + "data": [ + { + "data": false, + "type": "boolean" + }, + { + "data": true, + "type": "boolean" + }, + { + "data": true, + "type": "boolean" + }, + { + "data": true, + "type": "boolean" + }, + { + "data": false, + "type": "boolean" + }, + { + "data": false, + "type": "boolean" + }, + { + "data": false, + "type": "boolean" + }, + { + "data": true, + "type": "boolean" + }, + { + "data": false, + "type": "boolean" + } + ], + "type": "sequence" + } + } + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/tests/exercises/isbn/solution/solution.cpp b/tests/exercises/isbn/solution/solution.cpp new file mode 100644 index 00000000..71ab9f43 --- /dev/null +++ b/tests/exercises/isbn/solution/solution.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include + +bool is_isbn10_st(const std::string &code) { + // Helper function for computing ISBN-10 check digit + auto check_digit = [](const std::string &code) -> char { + int check = 0; + for (size_t i = 0; i < 9; ++i) { + check += (i + 1) * (code[i] - '0'); + } + check %= 11; + return (check == 10) ? 'X' : (check + '0'); + }; + + // Check whether given code contains 10 characters + if (code.length() != 10) return false; + + // Check whether first nine characters of given code are digits + for (size_t i = 0; i < 9; ++i) { + if (!isdigit(code[i])) return false; + } + + // Check the check digit + return check_digit(code) == code[9]; +} + +template +bool is_isbn10(const T &code) { + if(code.type() != typeid(std::string)) return false; + + return is_isbn10_st(std::any_cast(code)); +} + +bool is_isbn13_st(const std::string &code) { + // Helper function for computing ISBN-13 check digit + auto check_digit = [](const std::string &code) -> char { + int check = 0; + for (size_t i = 0; i < 12; ++i) { + check += ((i % 2 == 0) ? 1 : 3) * (code[i] - '0'); + } + check = (10 - (check % 10)) % 10; + return check + '0'; + }; + + // Check whether given code contains 13 characters + if (code.length() != 13) return false; + + // Check whether first twelve characters of given code are digits + for (size_t i = 0; i < 12; ++i) { + if (!isdigit(code[i])) return false; + } + + // Check the check digit + return check_digit(code) == code[12]; +} + +template +bool is_isbn13(const T &code) { + if(code.type() != typeid(std::string)) return false; + + return is_isbn13_st(std::any_cast(code)); +} + +template +bool is_isbn(const T &code, bool isbn13 = true) { + return isbn13 ? is_isbn13(code) : is_isbn10(code); +} + + +template +std::vector are_isbn(const std::vector &codes) { + std::vector checks; + for (const auto &code : codes) { + if(code.type() != typeid(std::string)){ + checks.push_back(false); + continue; + } + std::string isbn = std::any_cast(code); + bool isbn13 = isbn.length() == 13; + + checks.push_back(is_isbn(code, isbn13)); + } + return checks; +} + +template +std::vector are_isbn(const std::vector &codes, bool isbn13) { + std::vector checks; + for (const auto &code : codes) { + checks.push_back(is_isbn(code, isbn13)); + } + return checks; +}