From 201d060cdf059677d12cab3e37b9ceb91dd948a3 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 18 Jul 2024 13:54:06 +0200 Subject: [PATCH] Minimize code duplication --- tested/languages/c/config.py | 22 +- tested/languages/c/generators.py | 541 +++++++++++++++-------------- tested/languages/cpp/config.py | 9 - tested/languages/cpp/generators.py | 170 --------- tested/manual.py | 2 +- 5 files changed, 285 insertions(+), 459 deletions(-) delete mode 100644 tested/languages/cpp/generators.py diff --git a/tested/languages/c/config.py b/tested/languages/c/config.py index cb7c8e6a..f6fa9ff4 100644 --- a/tested/languages/c/config.py +++ b/tested/languages/c/config.py @@ -6,6 +6,7 @@ from tested.datatypes import AllTypes from tested.dodona import AnnotateCode, Message from tested.features import Construct, TypeSupport +from tested.languages.c.generators import CGenerator from tested.languages.conventionalize import ( EXECUTION_PREFIX, Conventionable, @@ -103,7 +104,7 @@ def modify_solution(self, solution: Path): # First, check if we have a no-arg main function. # If so, replace it with a renamed main function that does have args. no_args = re.compile(r"(int|void)(\s+)main(\s*)\((\s*)\)(\s*{)") - replacement = r"int\2solution_main\3(\4int argc, char** argv)\5" + replacement = r"int\2solution_main\3(\4int argc, char* argv[])\5" contents, nr = re.subn(no_args, replacement, contents, count=1) if nr == 0: # There was no main function without arguments. Now we try a main @@ -139,26 +140,21 @@ def cleanup_stacktrace(self, stacktrace: str) -> str: def is_source_file(self, file: Path) -> bool: return file.suffix in (".c", ".h") + + def generator(self) -> CGenerator: + return CGenerator(self.file_extension()) def generate_statement(self, statement: Statement) -> str: - from tested.languages.c import generators - - return generators.convert_statement(statement, full=True) + return self.generator().convert_statement(statement, full=True) def generate_execution_unit(self, execution_unit: "PreparedExecutionUnit") -> str: - from tested.languages.c import generators - - return generators.convert_execution_unit(execution_unit) + return self.generator().convert_execution_unit(execution_unit) def generate_selector(self, contexts: list[str]) -> str: - from tested.languages.c import generators - - return generators.convert_selector(contexts) + return self.generator().convert_selector(contexts) def generate_encoder(self, values: list[Value]) -> str: - from tested.languages.c import generators - - return generators.convert_encoder(values) + return self.generator().convert_encoder(values) def get_declaration_metadata(self) -> TypeDeclarationMetadata: return { diff --git a/tested/languages/c/generators.py b/tested/languages/c/generators.py index f2d23cf3..8f39a1a8 100644 --- a/tested/languages/c/generators.py +++ b/tested/languages/c/generators.py @@ -34,294 +34,303 @@ ) from tested.testsuite import MainInput - -def convert_arguments(arguments: list[Expression | NamedArgument]) -> str: - return ", ".join(convert_statement(cast(Expression, arg)) for arg in arguments) - - -def convert_value(value: Value) -> str: - # Handle some advanced types. - if value.type == AdvancedStringTypes.CHAR: - assert isinstance(value, StringType) - return f"(char) '" + value.data.replace("'", "\\'") + "'" - elif value.type == AdvancedNumericTypes.INT_16: - return f"((short) {value.data})" - elif value.type == AdvancedNumericTypes.U_INT_16: - return f"((unsigned short) {value.data})" - elif value.type == AdvancedNumericTypes.INT_64: - return f"{value.data}L" - elif value.type == AdvancedNumericTypes.U_INT_64: - return f"{value.data}UL" - elif value.type == AdvancedNumericTypes.U_INT_32: - return f"{value.data}U" - # Handle basic types - original = value - value = as_basic_type(value) - if value.type == BasicNumericTypes.INTEGER: - # Basic heuristic for long numbers - if (value.data > (2**31 - 1)) or (value.data < -(2**31)): +class CGenerator: + def __init__(self, extension: str = "c"): + self.extension = extension + + def convert_arguments(self, arguments: list[Expression | NamedArgument]) -> str: + return ", ".join(self.convert_statement(cast(Expression, arg)) for arg in arguments) + + + def convert_value(self, value: Value) -> str: + # Handle some advanced types. + if value.type == AdvancedStringTypes.CHAR: + assert isinstance(value, StringType) + return f"(char) '" + value.data.replace("'", "\\'") + "'" + elif value.type == AdvancedNumericTypes.INT_16: + return f"((short) {value.data})" + elif value.type == AdvancedNumericTypes.U_INT_16: + return f"((unsigned short) {value.data})" + elif value.type == AdvancedNumericTypes.INT_64: return f"{value.data}L" - else: - return str(value.data) - elif value.type == BasicNumericTypes.REAL: - suffix = "f" if original.type == AdvancedNumericTypes.SINGLE_PRECISION else "" - if not isinstance(value.data, SpecialNumbers): - return str(value.data) + suffix - elif value.data == SpecialNumbers.NOT_A_NUMBER: - return "nan" + suffix + '("")' - elif value.data == SpecialNumbers.POS_INFINITY: - if original.type == AdvancedNumericTypes.DOUBLE_PRECISION: - return "((double) INFINITY)" + elif value.type == AdvancedNumericTypes.U_INT_64: + return f"{value.data}UL" + elif value.type == AdvancedNumericTypes.U_INT_32: + return f"{value.data}U" + # Handle basic types + original = value + value = as_basic_type(value) + if value.type == BasicNumericTypes.INTEGER: + # Basic heuristic for long numbers + if (value.data > (2**31 - 1)) or (value.data < -(2**31)): + return f"{value.data}L" else: - return "INFINITY" - else: - assert SpecialNumbers.NEG_INFINITY - if original.type == AdvancedNumericTypes.DOUBLE_PRECISION: - return "((double) -INFINITY)" + return str(value.data) + elif value.type == BasicNumericTypes.REAL: + suffix = "f" if original.type == AdvancedNumericTypes.SINGLE_PRECISION else "" + if not isinstance(value.data, SpecialNumbers): + return str(value.data) + suffix + elif value.data == SpecialNumbers.NOT_A_NUMBER: + return "nan" + suffix + '("")' + elif value.data == SpecialNumbers.POS_INFINITY: + if original.type == AdvancedNumericTypes.DOUBLE_PRECISION: + return "((double) INFINITY)" + else: + return "INFINITY" else: - return "(-INFINITY)" - elif value.type == BasicStringTypes.TEXT: - return json.dumps(value.data) - elif value.type == BasicBooleanTypes.BOOLEAN: - return f"(bool) " + str(value.data).lower() - elif value.type == BasicNothingTypes.NOTHING: - return "NULL" - elif value.type == BasicStringTypes.UNKNOWN: - assert isinstance(value, StringType) - return convert_unknown_type(value) - raise AssertionError(f"Invalid literal: {value!r}") - - -def convert_function_call(function: FunctionCall) -> str: - result = function.name - if function.type != FunctionType.PROPERTY: - result += f"({convert_arguments(function.arguments)})" # pyright: ignore - return result - - -def convert_declaration(tp: AllTypes | VariableType) -> str: - if isinstance(tp, VariableType): - return tp.data - elif tp == AdvancedNumericTypes.BIG_INT: - return "long long" - elif tp == AdvancedNumericTypes.U_INT_64: - return "unsigned long" - elif tp == AdvancedNumericTypes.INT_64: - return "long" - elif tp == AdvancedNumericTypes.U_INT_32: - return "unsigned int" - elif tp == AdvancedNumericTypes.INT_32: - return "int" - elif tp == AdvancedNumericTypes.U_INT_16: - return "unsigned short int" - elif tp == AdvancedNumericTypes.INT_16: - return "short int" - elif tp == AdvancedNumericTypes.U_INT_8: - return "unsigned char" - elif tp == AdvancedNumericTypes.INT_8: - return "signed char" - elif tp == AdvancedNumericTypes.DOUBLE_EXTENDED: - return "long double" - elif tp == AdvancedNumericTypes.DOUBLE_PRECISION: - return "double" - elif tp == AdvancedNumericTypes.SINGLE_PRECISION: - return "float" - elif tp == AdvancedStringTypes.CHAR: - return "char" - basic = resolve_to_basic(tp) - if basic == BasicBooleanTypes.BOOLEAN: - return "bool" - elif basic == BasicStringTypes.TEXT: - return "char*" - elif basic == BasicNumericTypes.INTEGER: - return "long long" - elif basic == BasicNumericTypes.REAL: - return "double" - elif basic == BasicNothingTypes.NOTHING: - return "void" - raise AssertionError(f"Unknown type: {tp!r}") - - -def convert_statement(statement: Statement, full=False) -> str: - if isinstance(statement, Identifier): - return statement - elif isinstance(statement, FunctionCall): - return convert_function_call(statement) - elif isinstance(statement, Value): - return convert_value(statement) - elif isinstance(statement, VariableAssignment): - if full: - prefix = convert_declaration(statement.type) + " " - else: - prefix = "" - return ( - f"{prefix}{statement.variable} = " - f"{convert_statement(statement.expression)};" - ) - raise AssertionError(f"Unknown statement: {statement!r}") - - -def _generate_internal_context(ctx: PreparedContext, pu: PreparedExecutionUnit) -> str: - result = f""" - {ctx.before} - - int exit_code; - """ - - # Generate code for each testcase - tc: PreparedTestcase - for tc in ctx.testcases: - result += f"{pu.unit.name}_write_separator();\n" - - if tc.testcase.is_main_testcase(): - assert isinstance(tc.input, MainInput) - wrapped = [json.dumps(a) for a in tc.input.arguments] - result += f'char* args[] = {{"{pu.submission_name}", ' - result += ", ".join(wrapped) - result += "};\n" - result += ( - f"exit_code = solution_main({len(tc.input.arguments) + 1}, args);\n" - ) - else: - assert isinstance(tc.input, PreparedTestcaseStatement) - result += "exit_code = 0;\n" - if is_special_void_call(tc.input, pu.language): - # The method has a "void" return type, so don't wrap it. - result += ( - " " * 4 - + convert_statement(tc.input.unwrapped_input_statement()) - + ";\n" - ) - result += " " * 4 + convert_statement(tc.input.no_value_call()) + ";\n" + assert SpecialNumbers.NEG_INFINITY + if original.type == AdvancedNumericTypes.DOUBLE_PRECISION: + return "((double) -INFINITY)" + else: + return "(-INFINITY)" + elif value.type == BasicStringTypes.TEXT: + return json.dumps(value.data) + elif value.type == BasicBooleanTypes.BOOLEAN: + return f"(bool) " + str(value.data).lower() + elif value.type == BasicNothingTypes.NOTHING: + return "NULL" + elif value.type == BasicStringTypes.UNKNOWN: + assert isinstance(value, StringType) + return convert_unknown_type(value) + raise AssertionError(f"Invalid literal: {value!r}") + + + def convert_function_call(self, function: FunctionCall) -> str: + result = function.name + if function.type != FunctionType.PROPERTY: + result += f"({self.convert_arguments(function.arguments)})" # pyright: ignore + return result + + + def convert_declaration(self, tp: AllTypes | VariableType) -> str: + if isinstance(tp, VariableType): + return tp.data + elif tp == AdvancedNumericTypes.BIG_INT: + return "long long" + elif tp == AdvancedNumericTypes.U_INT_64: + return "unsigned long" + elif tp == AdvancedNumericTypes.INT_64: + return "long" + elif tp == AdvancedNumericTypes.U_INT_32: + return "unsigned int" + elif tp == AdvancedNumericTypes.INT_32: + return "int" + elif tp == AdvancedNumericTypes.U_INT_16: + return "unsigned short int" + elif tp == AdvancedNumericTypes.INT_16: + return "short int" + elif tp == AdvancedNumericTypes.U_INT_8: + return "unsigned char" + elif tp == AdvancedNumericTypes.INT_8: + return "signed char" + elif tp == AdvancedNumericTypes.DOUBLE_EXTENDED: + return "long double" + elif tp == AdvancedNumericTypes.DOUBLE_PRECISION: + return "double" + elif tp == AdvancedNumericTypes.SINGLE_PRECISION: + return "float" + elif tp == AdvancedStringTypes.CHAR: + return "char" + basic = resolve_to_basic(tp) + if basic == BasicBooleanTypes.BOOLEAN: + return "bool" + elif basic == BasicStringTypes.TEXT: + return "char*" + elif basic == BasicNumericTypes.INTEGER: + return "long long" + elif basic == BasicNumericTypes.REAL: + return "double" + elif basic == BasicNothingTypes.NOTHING: + return "void" + raise AssertionError(f"Unknown type: {tp!r}") + + + def convert_statement(self, statement: Statement, full=False) -> str: + if isinstance(statement, Identifier): + return statement + elif isinstance(statement, FunctionCall): + return self.convert_function_call(statement) + elif isinstance(statement, Value): + return self.convert_value(statement) + elif isinstance(statement, VariableAssignment): + if full: + prefix = self.convert_declaration(statement.type) + " " else: - result += convert_statement(tc.input.input_statement()) + ";\n" + prefix = "" + return ( + f"{prefix}{statement.variable} = " + f"{self.convert_statement(statement.expression)};" + ) + raise AssertionError(f"Unknown statement: {statement!r}") - result += ctx.after + "\n" - result += "return exit_code;\n" - return result + def _generate_internal_context(self, ctx: PreparedContext, pu: PreparedExecutionUnit) -> str: + result = f""" + {ctx.before} + + int exit_code; + """ -def convert_execution_unit(pu: PreparedExecutionUnit) -> str: - result = f""" - #include - #include - - #include "values.h" - #include "{pu.submission_name}.c" - """ + # Generate code for each testcase + tc: PreparedTestcase + for tc in ctx.testcases: + result += f"{pu.unit.name}_write_separator();\n" + + if tc.testcase.is_main_testcase(): + assert isinstance(tc.input, MainInput) + wrapped = [json.dumps(a) for a in tc.input.arguments] + result += f'char* args[] = {{"{pu.submission_name}", ' + result += ", ".join(wrapped) + result += "};\n" + result += ( + f"exit_code = solution_main({len(tc.input.arguments) + 1}, args);\n" + ) + else: + assert isinstance(tc.input, PreparedTestcaseStatement) + result += "exit_code = 0;\n" + if is_special_void_call(tc.input, pu.language): + # The method has a "void" return type, so don't wrap it. + result += ( + " " * 4 + + self.convert_statement(tc.input.unwrapped_input_statement()) + + ";\n" + ) + result += " " * 4 + self.convert_statement(tc.input.no_value_call()) + ";\n" + else: + result += self.convert_statement(tc.input.input_statement()) + ";\n" + + result += ctx.after + "\n" + result += "return exit_code;\n" + return result + + + def convert_execution_unit(self, pu: PreparedExecutionUnit) -> str: + result = f""" + #include + #include + + #include "values.h" + #include "{pu.submission_name}.{self.extension}" + """ - # Import functions - for name in pu.evaluator_names: - result += f'#include "{name}.c"\n' - - result += f""" - static FILE* {pu.unit.name}_value_file = NULL; - static FILE* {pu.unit.name}_exception_file = NULL; - - static void {pu.unit.name}_write_separator() {{ - fprintf({pu.unit.name}_value_file, "--{pu.testcase_separator_secret}-- SEP"); - fprintf({pu.unit.name}_exception_file, "--{pu.testcase_separator_secret}-- SEP"); - fprintf(stdout, "--{pu.testcase_separator_secret}-- SEP"); - fprintf(stderr, "--{pu.testcase_separator_secret}-- SEP"); - }} - - static void {pu.unit.name}_write_context_separator() {{ - fprintf({pu.unit.name}_value_file, "--{pu.context_separator_secret}-- SEP"); - fprintf({pu.unit.name}_exception_file, "--{pu.context_separator_secret}-- SEP"); - fprintf(stdout, "--{pu.context_separator_secret}-- SEP"); - fprintf(stderr, "--{pu.context_separator_secret}-- SEP"); - }} - - #undef send_value - #define send_value(value) write_value({pu.unit.name}_value_file, value) - - #undef send_specific_value - #define send_specific_value(value) write_evaluated({pu.unit.name}_value_file, value) - """ + # Import functions + for name in pu.evaluator_names: + result += f'#include "{name}.{self.extension}"\n' - # Generate code for each context. - ctx: PreparedContext - for i, ctx in enumerate(pu.contexts): result += f""" - int {pu.unit.name}_context_{i}(void) {{ - {_generate_internal_context(ctx, pu)} + static FILE* {pu.unit.name}_value_file = NULL; + static FILE* {pu.unit.name}_exception_file = NULL; + + static void {pu.unit.name}_write_separator() {{ + fprintf({pu.unit.name}_value_file, "--{pu.testcase_separator_secret}-- SEP"); + fprintf({pu.unit.name}_exception_file, "--{pu.testcase_separator_secret}-- SEP"); + fprintf(stdout, "--{pu.testcase_separator_secret}-- SEP"); + fprintf(stderr, "--{pu.testcase_separator_secret}-- SEP"); + }} + + static void {pu.unit.name}_write_context_separator() {{ + fprintf({pu.unit.name}_value_file, "--{pu.context_separator_secret}-- SEP"); + fprintf({pu.unit.name}_exception_file, "--{pu.context_separator_secret}-- SEP"); + fprintf(stdout, "--{pu.context_separator_secret}-- SEP"); + fprintf(stderr, "--{pu.context_separator_secret}-- SEP"); }} + + #undef send_value + #define send_value(value) write_value({pu.unit.name}_value_file, value) + + #undef send_specific_value + #define send_specific_value(value) write_evaluated({pu.unit.name}_value_file, value) """ - result += f""" - int {pu.unit.name}() {{ - {pu.unit.name}_value_file = fopen("{pu.value_file}", "w"); - {pu.unit.name}_exception_file = fopen("{pu.exception_file}", "w"); - int exit_code; - """ + # Generate code for each context. + ctx: PreparedContext + for i, ctx in enumerate(pu.contexts): + result += f""" + int {pu.unit.name}_context_{i}(void) {{ + {self._generate_internal_context(ctx, pu)} + }} + """ - for i, ctx in enumerate(pu.contexts): - result += " " * 4 + f"{pu.unit.name}_write_context_separator();\n" - result += " " * 4 + f"exit_code = {pu.unit.name}_context_{i}();\n" - - result += f""" - fclose({pu.unit.name}_value_file); - fclose({pu.unit.name}_exception_file); - return exit_code; - }} - - #ifndef INCLUDED - int main() {{ - return {pu.unit.name}(); - }} - #endif - """ - return result + result += f""" + int {pu.unit.name}() {{ + {pu.unit.name}_value_file = fopen("{pu.value_file}", "w"); + {pu.unit.name}_exception_file = fopen("{pu.exception_file}", "w"); + int exit_code; + """ + for i, ctx in enumerate(pu.contexts): + result += " " * 4 + f"{pu.unit.name}_write_context_separator();\n" + result += " " * 4 + f"exit_code = {pu.unit.name}_context_{i}();\n" -def convert_selector(contexts: list[str]) -> str: - result = """ - #include - #include - - #define INCLUDED true - """ + result += f""" + fclose({pu.unit.name}_value_file); + fclose({pu.unit.name}_exception_file); + return exit_code; + }} + + #ifndef INCLUDED + int main() {{ + return {pu.unit.name}(); + }} + #endif + """ + return result - for ctx in contexts: - result += f'#include "{ctx}.c"\n' - result += """ - int main(int argc, const char* argv[]) { + def convert_selector(self, contexts: list[str]) -> str: + result = """ + #include + #include + + #define INCLUDED true + """ - if (argc < 1) { - fprintf(stderr, "No context selected."); - return -2; + for ctx in contexts: + result += f""" + #if __has_include("{ctx}.{self.extension}") + #include "{ctx}.{self.extension}" + #endif + """ + + result += """ + int main(int argc, const char* argv[]) { + + if (argc < 1) { + fprintf(stderr, "No context selected."); + return -2; + } + + const char* name = argv[1]; + """ + for ctx in contexts: + result += f""" + #if __has_include("{ctx}.{self.extension}") + if (strcmp("{ctx}", name) == 0) {{ + return {ctx}(); + }} + #endif + """ + + result += """ + fprintf(stderr, "Non-existing context '%s' selected.", name); + return -1; } - - const char* name = argv[1]; - """ - for ctx in contexts: - result += f""" - if (strcmp("{ctx}", name) == 0) {{ - return {ctx}(); - }} """ - - result += """ - fprintf(stderr, "Non-existing context '%s' selected.", name); - return -1; - } - """ - return result + return result -def convert_encoder(values: list[Value]) -> str: - result = """ -#include -#include -#include + def convert_encoder(self, values: list[Value]) -> str: + result = """ + #include + #include + #include -#include "values.h" + #include "values.h" -int main() { -""" - for value in values: - result += " " * 4 + f"write_value(stdout, {convert_value(value)});\n" - result += " " * 4 + 'printf("␞");\n' - result += "}\n" - return result + int main() { + """ + for value in values: + result += " " * 4 + f"write_value(stdout, {self.convert_value(value)});\n" + result += " " * 4 + 'printf("␞");\n' + result += "}\n" + return result diff --git a/tested/languages/cpp/config.py b/tested/languages/cpp/config.py index 9f7db54b..3c631ed1 100644 --- a/tested/languages/cpp/config.py +++ b/tested/languages/cpp/config.py @@ -3,7 +3,6 @@ from tested.languages.c.config import C from tested.languages.language import CallbackResult -from tested.languages.preparation import PreparedExecutionUnit from tested.languages.utils import executable_name @@ -33,11 +32,3 @@ def compilation(self, files: list[str]) -> CallbackResult: [result], ) - def generate_execution_unit(self, execution_unit: "PreparedExecutionUnit") -> str: - from tested.languages.cpp import generators - return generators.convert_execution_unit(execution_unit) - - def generate_selector(self, contexts: list[str]) -> str: - from tested.languages.cpp import generators - - return generators.convert_selector(contexts) diff --git a/tested/languages/cpp/generators.py b/tested/languages/cpp/generators.py deleted file mode 100644 index b5eba822..00000000 --- a/tested/languages/cpp/generators.py +++ /dev/null @@ -1,170 +0,0 @@ -import json - -from tested.languages.c.generators import convert_statement -from tested.languages.preparation import PreparedExecutionUnit, PreparedContext, \ - PreparedTestcase, PreparedTestcaseStatement -from tested.languages.utils import is_special_void_call -from tested.testsuite import MainInput - - -def _generate_internal_context(ctx: PreparedContext, pu: PreparedExecutionUnit) -> str: - result = f""" - {ctx.before} - - int exit_code; - """ - - # Generate code for each testcase - tc: PreparedTestcase - for tc in ctx.testcases: - result += f"{pu.unit.name}_write_separator();\n" - - if tc.testcase.is_main_testcase(): - assert isinstance(tc.input, MainInput) - wrapped = [json.dumps(a) for a in tc.input.arguments] - result += f'string string_args[] = {{"{pu.submission_name}", ' - result += ", ".join(wrapped) - result += "};\n" - result += f""" - // convert string to char** - const int argc = {len(tc.input.arguments) + 1}; - char** args = new char*[argc]; - for (int i = 0; i < argc; i++) {{ - args[i] = new char[string_args[i].size() + 1]; - strcpy(args[i], string_args[i].c_str()); - }} - """ - result += ( - f"exit_code = solution_main(argc, args);\n" - ) - else: - assert isinstance(tc.input, PreparedTestcaseStatement) - result += "exit_code = 0;\n" - if is_special_void_call(tc.input, pu.language): - # The method has a "void" return type, so don't wrap it. - result += ( - " " * 4 - + convert_statement(tc.input.unwrapped_input_statement()) - + ";\n" - ) - result += " " * 4 + convert_statement(tc.input.no_value_call()) + ";\n" - else: - result += convert_statement(tc.input.input_statement()) + ";\n" - - result += ctx.after + "\n" - result += "return exit_code;\n" - return result - - -def convert_execution_unit(pu: PreparedExecutionUnit) -> str: - result = f""" - #include "values.h" - #include "{pu.submission_name}.cpp" - - using namespace std; - """ - - # Import functions - for name in pu.evaluator_names: - result += f'#include "{name}.cpp"\n' - - result += f""" - static FILE* {pu.unit.name}_value_file = NULL; - static FILE* {pu.unit.name}_exception_file = NULL; - - static void {pu.unit.name}_write_separator() {{ - fprintf({pu.unit.name}_value_file, "--{pu.testcase_separator_secret}-- SEP"); - fprintf({pu.unit.name}_exception_file, "--{pu.testcase_separator_secret}-- SEP"); - fprintf(stdout, "--{pu.testcase_separator_secret}-- SEP"); - fprintf(stderr, "--{pu.testcase_separator_secret}-- SEP"); - }} - - static void {pu.unit.name}_write_context_separator() {{ - fprintf({pu.unit.name}_value_file, "--{pu.context_separator_secret}-- SEP"); - fprintf({pu.unit.name}_exception_file, "--{pu.context_separator_secret}-- SEP"); - fprintf(stdout, "--{pu.context_separator_secret}-- SEP"); - fprintf(stderr, "--{pu.context_separator_secret}-- SEP"); - }} - - #undef send_value - #define send_value(value) write_value({pu.unit.name}_value_file, value) - - #undef send_specific_value - #define send_specific_value(value) write_evaluated({pu.unit.name}_value_file, value) - """ - - # Generate code for each context. - ctx: PreparedContext - for i, ctx in enumerate(pu.contexts): - result += f""" - int {pu.unit.name}_context_{i}(void) {{ - {_generate_internal_context(ctx, pu)} - }} - """ - - result += f""" - int {pu.unit.name}() {{ - {pu.unit.name}_value_file = fopen("{pu.value_file}", "w"); - {pu.unit.name}_exception_file = fopen("{pu.exception_file}", "w"); - int exit_code; - """ - - for i, ctx in enumerate(pu.contexts): - result += " " * 4 + f"{pu.unit.name}_write_context_separator();\n" - result += " " * 4 + f"exit_code = {pu.unit.name}_context_{i}();\n" - - result += f""" - fclose({pu.unit.name}_value_file); - fclose({pu.unit.name}_exception_file); - return exit_code; - }} - - #ifndef INCLUDED - int main() {{ - return {pu.unit.name}(); - }} - #endif - """ - return result - - -def convert_selector(contexts: list[str]) -> str: - result = """ - #include - #include - - #define INCLUDED true - """ - - for ctx in contexts: - result += f""" - #if __has_include("{ctx}.cpp") - #include "{ctx}.cpp" - #endif - """ - - result += """ - int main(int argc, const char* argv[]) { - - if (argc < 1) { - fprintf(stderr, "No context selected."); - return -2; - } - - const char* name = argv[1]; - """ - for ctx in contexts: - result += f""" - #if __has_include("{ctx}.cpp") - if (strcmp("{ctx}", name) == 0) {{ - return {ctx}(); - }} - #endif - """ - - result += """ - fprintf(stderr, "Non-existing context '%s' selected.", name); - return -1; - } - """ - return result diff --git a/tested/manual.py b/tested/manual.py index 7f3707d1..78e6574d 100644 --- a/tested/manual.py +++ b/tested/manual.py @@ -24,7 +24,7 @@ def read_config() -> DodonaConfig: programming_language=SupportedLanguage("cpp"), natural_language="nl", resources=Path(exercise_dir, "evaluation"), - source=Path(exercise_dir, "solution/wrong.cpp"), + source=Path(exercise_dir, "solution/correct.cpp"), judge=Path("."), workdir=Path("workdir"), test_suite="two.tson",