Skip to content

Commit

Permalink
Improve linting
Browse files Browse the repository at this point in the history
  • Loading branch information
jorg-vr committed Oct 16, 2024
1 parent 866c86f commit e5ff917
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 60 deletions.
2 changes: 1 addition & 1 deletion tested/languages/c/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ 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())

Expand Down
35 changes: 19 additions & 16 deletions tested/languages/c/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@
)
from tested.testsuite import MainInput


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)

return ", ".join(
self.convert_statement(cast(Expression, arg)) for arg in arguments
)

def convert_value(self, value: Value) -> str:
# Handle some advanced types.
Expand All @@ -67,7 +69,9 @@ def convert_value(self, value: Value) -> str:
else:
return str(value.data)
elif value.type == BasicNumericTypes.REAL:
suffix = "f" if original.type == AdvancedNumericTypes.SINGLE_PRECISION else ""
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:
Expand All @@ -94,14 +98,14 @@ def convert_value(self, value: Value) -> str:
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
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
Expand Down Expand Up @@ -144,7 +148,6 @@ def convert_declaration(self, tp: AllTypes | VariableType) -> str:
return "void"
raise AssertionError(f"Unknown type: {tp!r}")


def convert_statement(self, statement: Statement, full=False) -> str:
if isinstance(statement, Identifier):
return statement
Expand All @@ -162,7 +165,7 @@ def convert_statement(self, statement: Statement, full=False) -> str:
f"{self.convert_statement(statement.expression)};"
)
raise AssertionError(f"Unknown statement: {statement!r}")

def convert_testcase(self, tc: PreparedTestcase, pu: PreparedExecutionUnit) -> str:
result = ""
if tc.testcase.is_main_testcase():
Expand All @@ -184,13 +187,16 @@ def convert_testcase(self, tc: PreparedTestcase, pu: PreparedExecutionUnit) -> s
+ self.convert_statement(tc.input.unwrapped_input_statement())
+ ";\n"
)
result += " " * 4 + self.convert_statement(tc.input.no_value_call()) + ";\n"
result += (
" " * 4 + self.convert_statement(tc.input.no_value_call()) + ";\n"
)
else:
result += self.convert_statement(tc.input.input_statement()) + ";\n"
return result


def generate_internal_context(self, ctx: PreparedContext, pu: PreparedExecutionUnit) -> str:
def generate_internal_context(
self, ctx: PreparedContext, pu: PreparedExecutionUnit
) -> str:
result = f"""
{ctx.before}
Expand All @@ -202,11 +208,11 @@ def generate_internal_context(self, ctx: PreparedContext, pu: PreparedExecutionU
for tc in ctx.testcases:
result += f"{pu.unit.name}_write_separator();\n"
result += self.convert_testcase(tc, pu)

result += ctx.after + "\n"
result += "return exit_code;\n"
return result

def define_write_funtions(self, pu: PreparedExecutionUnit) -> str:
return f"""
static FILE* {pu.unit.name}_value_file = NULL;
Expand All @@ -233,7 +239,6 @@ def define_write_funtions(self, pu: PreparedExecutionUnit) -> str:
#define send_specific_value(value) write_evaluated({pu.unit.name}_value_file, value)
"""


def convert_execution_unit(self, pu: PreparedExecutionUnit) -> str:
result = f"""
#include <stdio.h>
Expand Down Expand Up @@ -283,7 +288,6 @@ def convert_execution_unit(self, pu: PreparedExecutionUnit) -> str:
"""
return result


def convert_selector(self, contexts: list[str]) -> str:
result = """
#include <string.h>
Expand Down Expand Up @@ -325,7 +329,6 @@ def convert_selector(self, contexts: list[str]) -> str:
"""
return result


def convert_encoder(self, values: list[Value]) -> str:
result = """
#include <stdio.h>
Expand Down
18 changes: 12 additions & 6 deletions tested/languages/cpp/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path
from tested.features import Construct, TypeSupport

from tested.datatypes import AllTypes
from tested.features import Construct, TypeSupport
from tested.languages.c.config import C
from tested.languages.conventionalize import Conventionable, NamingConventions
from tested.languages.cpp.generators import CPPGenerator
Expand All @@ -10,18 +11,23 @@

class CPP(C):
def initial_dependencies(self) -> list[str]:
return ["values.h", "values.cpp", "values.tpp", "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"

def naming_conventions(self) -> dict[Conventionable, NamingConventions]:
return {
"identifier": "camel_case",
"property": "camel_case",
"class": "pascal_case",
"global_identifier": "macro_case",

}

def supported_constructs(self) -> set[Construct]:
Expand All @@ -32,9 +38,9 @@ def supported_constructs(self) -> set[Construct]:
Construct.OBJECTS,
Construct.HETEROGENEOUS_COLLECTIONS,
Construct.DEFAULT_PARAMETERS,
Construct.HETEROGENEOUS_ARGUMENTS
Construct.HETEROGENEOUS_ARGUMENTS,
}

def datatype_support(self) -> dict[AllTypes, TypeSupport]:
return super().datatype_support() | { # type: ignore
"sequence": "supported",
Expand Down
110 changes: 76 additions & 34 deletions tested/languages/cpp/generators.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@

from tested.datatypes import AllTypes, resolve_to_basic
from tested.datatypes.advanced import AdvancedNumericTypes, AdvancedObjectTypes, AdvancedSequenceTypes, AdvancedStringTypes
from tested.datatypes.basic import BasicNumericTypes, BasicObjectTypes, BasicSequenceTypes, BasicStringTypes, BasicTypes
from tested.datatypes.advanced import (
AdvancedNumericTypes,
AdvancedObjectTypes,
AdvancedSequenceTypes,
AdvancedStringTypes,
)
from tested.datatypes.basic import (
BasicNumericTypes,
BasicObjectTypes,
BasicSequenceTypes,
BasicStringTypes,
BasicTypes,
)
from tested.languages.c.generators import CGenerator
from tested.languages.preparation import PreparedContext, PreparedExecutionUnit, PreparedFunctionCall, PreparedTestcase, PreparedTestcaseStatement
from tested.serialisation import FunctionCall, FunctionType, ObjectType, PropertyAssignment, SequenceType, Statement, Value, VariableAssignment, VariableType, WrappedAllTypes
from tested.languages.preparation import (
PreparedContext,
PreparedExecutionUnit,
PreparedFunctionCall,
PreparedTestcase,
PreparedTestcaseStatement,
)
from tested.serialisation import (
FunctionCall,
FunctionType,
ObjectType,
PropertyAssignment,
SequenceType,
Statement,
Value,
VariableAssignment,
VariableType,
WrappedAllTypes,
)


class CPPGenerator(CGenerator):
def unpack_wrapped_types(self, type_or_types: WrappedAllTypes) -> tuple[AllTypes, WrappedAllTypes]:
def unpack_wrapped_types(
self, type_or_types: WrappedAllTypes
) -> tuple[AllTypes, WrappedAllTypes]:
if isinstance(type_or_types, tuple):
return type_or_types
return type_or_types, None
Expand All @@ -26,8 +55,10 @@ def convert_sequence_subtype(self, value: Statement, subtype: AllTypes) -> str:

tp, subtype = self.unpack_wrapped_types(type_or_types)
return self.convert_declaration(tp, None, subtype)

def convert_map_subtypes(self, value: Statement, subtype: WrappedAllTypes) -> tuple[str, str] | None:

def convert_map_subtypes(
self, value: Statement, subtype: WrappedAllTypes
) -> tuple[str, str] | None:
if isinstance(value, ObjectType):
key_type = value.get_key_type()
value_type = value.get_value_type()
Expand All @@ -41,24 +72,32 @@ 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()) + "}"
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)



def convert_declaration(self, tp: AllTypes | VariableType,
value: Statement | None = None,
subtype: WrappedAllTypes| None = None) -> str:
def convert_declaration(
self,
tp: AllTypes | VariableType,
value: Statement | None = None,
subtype: WrappedAllTypes | None = None,
) -> str:
if isinstance(tp, VariableType):
return tp.data + "*"
elif tp == AdvancedNumericTypes.BIG_INT:
Expand Down Expand Up @@ -95,7 +134,7 @@ def convert_declaration(self, tp: AllTypes | VariableType,
return f"std::vector<{subtype}>"
elif tp == AdvancedStringTypes.STRING:
return "std::string"

basic = resolve_to_basic(tp)
if basic == BasicObjectTypes.MAP:
key_type, value_type = self.convert_map_subtypes(value, subtype)
Expand All @@ -113,61 +152,64 @@ def convert_declaration(self, tp: AllTypes | VariableType,
elif basic == BasicNumericTypes.INTEGER:
return "std::intmax_t"


return super().convert_declaration(tp)

def convert_statement(self, statement: Statement, full=False) -> str:
# support for property assignments
if isinstance(statement, PropertyAssignment):
return (
f"{self.convert_statement(statement.property)} = "
f"{self.convert_statement(statement.expression)};"
)
return (
f"{self.convert_statement(statement.property)} = "
f"{self.convert_statement(statement.expression)};"
)
# overwrite the default implementation for variable assignments to allow for
# object declarations
elif full and isinstance(statement, VariableAssignment):

prefix = self.convert_declaration(statement.type, statement.expression)
return (
f"{prefix} {statement.variable} = "
f"{self.convert_statement(statement.expression)}"
)

return super().convert_statement(statement, full)

def convert_function_call(self, function: FunctionCall) -> str:
result = super().convert_function_call(function)

# if the function has a namespace, that is not the root namespace we assume it is a method call
if (function.namespace
if (
function.namespace
and not function.has_root_namespace
and not function.type == FunctionType.CONSTRUCTOR
):
):
result = self.convert_statement(function.namespace) + "->" + result
# add the new keyword to constructors
if function.type == FunctionType.CONSTRUCTOR:
result = "new " + result
return result

def convert_testcase(self, tc: PreparedTestcase, pu: PreparedExecutionUnit) -> str:
result = ""
# Define variables before asignment outside the try block
if ( not tc.testcase.is_main_testcase()
and isinstance(tc.input, PreparedTestcaseStatement)
and isinstance(tc.input.statement, VariableAssignment)
if (
not tc.testcase.is_main_testcase()
and isinstance(tc.input, PreparedTestcaseStatement)
and isinstance(tc.input.statement, VariableAssignment)
):
prefix = self.convert_declaration(tc.input.statement.type, tc.input.statement.expression)
prefix = self.convert_declaration(
tc.input.statement.type, tc.input.statement.expression
)
result += f"{prefix} {tc.input.statement.variable};\n"

# catch exceptions and write them to the output
result += "try {" + "\n"
result += super().convert_testcase(tc, pu)
result += "\n} catch (std::exception_ptr e) {\n"
result += self.convert_statement(tc.exception_statement('e'))+ ";\n"
result += self.convert_statement(tc.exception_statement("e")) + ";\n"
result += "exit_code = 1;\n"
result += "}\n"
return result

def define_write_funtions(self, pu: PreparedExecutionUnit) -> str:
result = super().define_write_funtions(pu)

Expand Down
6 changes: 3 additions & 3 deletions tested/manual.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from tested.main import run
from tested.testsuite import SupportedLanguage

exercise_dir = "/home/jorg/Documents/universal-judge/tests/exercises/echo"
exercise_dir = "/home/jorg/Documents/universal-judge/tests/exercises/echo-function"


def read_config() -> DodonaConfig:
Expand All @@ -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/run-error.cpp"),
source=Path(exercise_dir, "solution/correct.cpp"),
judge=Path("."),
workdir=Path("workdir"),
test_suite="two.tson",
test_suite="expected_no_return_and_got_none.yaml",
options=Options(
linter=False,
),
Expand Down

0 comments on commit e5ff917

Please sign in to comment.