Skip to content

Commit

Permalink
Add support for objects
Browse files Browse the repository at this point in the history
  • Loading branch information
jorg-vr committed Jul 19, 2024
1 parent d73aad9 commit d454083
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 123 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,9 @@ venv-*/
.venv/
node_modules/
result/

# Visual Studio Code
universal-judge.sln
.data/current/python
.data/current/python-packages
.vscode/
56 changes: 30 additions & 26 deletions tested/languages/c/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,35 @@ 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():
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"
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 @@ -175,30 +201,8 @@ def _generate_internal_context(self, ctx: PreparedContext, pu: PreparedExecution
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 += self.convert_testcase(tc, pu)

result += ctx.after + "\n"
result += "return exit_code;\n"
return result
Expand Down Expand Up @@ -247,7 +251,7 @@ def convert_execution_unit(self, pu: PreparedExecutionUnit) -> str:
for i, ctx in enumerate(pu.contexts):
result += f"""
int {pu.unit.name}_context_{i}(void) {{
{self._generate_internal_context(ctx, pu)}
{self.generate_internal_context(ctx, pu)}
}}
"""

Expand Down
22 changes: 21 additions & 1 deletion tested/languages/cpp/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from pathlib import Path
import re
from tested.features import Construct

from tested.languages.c.config import C
from tested.languages.conventionalize import Conventionable, NamingConventions
from tested.languages.cpp.generators import CPPGenerator
from tested.languages.language import CallbackResult
from tested.languages.utils import executable_name

Expand All @@ -12,6 +14,22 @@ def initial_dependencies(self) -> list[str]:

def file_extension(self) -> str:
return "cpp"

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

def supported_constructs(self) -> set[Construct]:
return {
Construct.FUNCTION_CALLS,
Construct.ASSIGNMENTS,
Construct.GLOBAL_VARIABLES,
Construct.OBJECTS,
}

def compilation(self, files: list[str]) -> CallbackResult:
main_file = files[-1]
Expand All @@ -32,3 +50,5 @@ def compilation(self, files: list[str]) -> CallbackResult:
[result],
)

def generator(self) -> CPPGenerator:
return CPPGenerator(self.file_extension())
123 changes: 123 additions & 0 deletions tested/languages/cpp/generators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@

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.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


class CPPGenerator(CGenerator):
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

def convert_sequence_subtype(self, value: Statement, subtype: AllTypes) -> str:
if value and isinstance(value, SequenceType):
# if the value is a sequence, we need to know the types of it's elements
type_or_types = value.get_content_type()
elif subtype:
# we might already have a subtype extracted from a previous recursive call
type_or_types = subtype
else:
# c++ has no default type such as Object in java, so we can't infer the type
return None

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:
if isinstance(value, ObjectType):
key_type = value.get_key_type()
value_type = value.get_value_type()
elif subtype:
key_type, value_type = subtype
else:
return None
key_base_type, key_sub_type = self.extract_type_tuple(key_type)
value_base_type, value_sub_type = self.extract_type_tuple(value_type)
key_type_str = self.convert_declaration(key_base_type, None, key_sub_type)
value_type_str = self.convert_declaration(value_base_type, None, value_sub_type)

return key_type_str, value_type_str



def convert_declaration(self, tp: AllTypes | VariableType,
value: Statement | None = None,
subtype: WrappedAllTypes| None = None) -> str:
if isinstance(tp, VariableType):
return tp.data + "*"
if tp == AdvancedSequenceTypes.LIST:
subtype = self.convert_sequence_subtype(value, subtype)
return f"std::list<{subtype}>"
elif tp == AdvancedSequenceTypes.TUPLE:
# this method does not support tuples within sequences such as list<tuple<int, int>>
# as value won't be defined in that case and we cant't infer the tuple's length
# we also don't support tuples with different types, as we can only extract one type
assert value is not None and isinstance(value, SequenceType)
tuple_length = len(value.data)
subtype = self.convert_sequence_subtype(value, subtype)
return f"std::tuple<{", ".join(subtype for _ in range(tuple_length))}>"
elif tp == AdvancedSequenceTypes.ARRAY:
subtype = self.convert_sequence_subtype(value, subtype)
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)
return f"std::map<{key_type}, {value_type}>"
elif basic == BasicSequenceTypes.SET:
subtype = self.convert_sequence_subtype(value, subtype)
return f"std::set<{subtype}>"

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)};"
)
# 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)

# add the namespace to the function call if it is not a constructor
if function.namespace and not (
(isinstance(function, PreparedFunctionCall) and function.has_root_namespace)
and 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
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)
result += f"{prefix} {tc.input.statement.variable};\n"

result += super().convert_testcase(tc, pu)
return result
61 changes: 21 additions & 40 deletions tested/languages/cpp/templates/values.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,81 +31,66 @@ string escape(const string &buffer) {
return dest;
}

// Format macro equivalent
#define FORMAT(name, x) "{\"type\": \"" name "\", \"data\":" x "}"

// Function to write a formatted string to an output stream
void write_formatted(FILE* out, const char* format, ...) {
va_list args;
va_start(args, format);
vfprintf(out, format, args);
va_end(args);
}

// Function to write boolean values
void write_bool(FILE* out, bool value) {
void write_value(FILE* out, bool value) {
write_formatted(out, FORMAT("boolean", "%s"), value ? "true" : "false");
}

// Function to write char values
void write_char(FILE* out, char value) {
void write_value(FILE* out, char value) {
string buffer(1, value);
string result = escape(buffer);
write_formatted(out, FORMAT("char", "\"%s\""), result.c_str());
}

// Function to write signed char values
void write_signed_char(FILE* out, signed char value) {
void write_value(FILE* out, signed char value) {
write_formatted(out, FORMAT("int8", "%d"), static_cast<int>(value));
}

// Function to write unsigned char values
void write_unsigned_char(FILE* out, unsigned char value) {
void write_value(FILE* out, unsigned char value) {
write_formatted(out, FORMAT("uint8", "%u"), static_cast<unsigned int>(value));
}

// Function to write short int values
void write_sint(FILE* out, short int value) {
void write_value(FILE* out, short int value) {
write_formatted(out, FORMAT("int16", "%d"), value);
}

// Function to write unsigned short int values
void write_usint(FILE* out, unsigned short int value) {
void write_value(FILE* out, unsigned short int value) {
write_formatted(out, FORMAT("uint16", "%u"), value);
}

// Function to write int values
void write_int(FILE* out, int value) {
void write_value(FILE* out, int value) {
write_formatted(out, FORMAT("int32", "%d"), value);
}

// Function to write unsigned int values
void write_uint(FILE* out, unsigned int value) {
void write_value(FILE* out, unsigned int value) {
write_formatted(out, FORMAT("uint32", "%u"), value);
}

// Function to write long values
void write_long(FILE* out, long value) {
void write_value(FILE* out, long value) {
write_formatted(out, FORMAT("int64", "%ld"), value);
}

// Function to write unsigned long values
void write_ulong(FILE* out, unsigned long value) {
void write_value(FILE* out, unsigned long value) {
write_formatted(out, FORMAT("uint64", "%lu"), value);
}

// Function to write long long values
void write_llong(FILE* out, long long value) {
void write_value(FILE* out, long long value) {
write_formatted(out, FORMAT("integer", "%lld"), value);
}

// Function to write unsigned long long values
void write_ullong(FILE* out, unsigned long long value) {
void write_value(FILE* out, unsigned long long value) {
write_formatted(out, FORMAT("bigint", "%llu"), value);
}

// Function to write float values
void write_float(FILE* out, float value) {
void write_value(FILE* out, float value) {
if (isnan(value)) {
write_formatted(out, FORMAT("single_precision", "\"%s\""), "nan");
} else if (isinf(value)) {
Expand All @@ -115,8 +100,7 @@ void write_float(FILE* out, float value) {
}
}

// Function to write double values
void write_double(FILE* out, double value) {
void write_value(FILE* out, double value) {
if (isnan(value)) {
write_formatted(out, FORMAT("double_precision", "\"%s\""), "nan");
} else if (isinf(value)) {
Expand All @@ -126,8 +110,7 @@ void write_double(FILE* out, double value) {
}
}

// Function to write long double values
void write_ldouble(FILE* out, long double value) {
void write_value(FILE* out, long double value) {
if (isnan(value)) {
write_formatted(out, FORMAT("double_extended", "\"%s\""), "nan");
} else if (isinf(value)) {
Expand All @@ -137,20 +120,18 @@ void write_ldouble(FILE* out, long double value) {
}
}

// Function to write string values
void write_string(FILE* out, const string &value) {
void write_value(FILE* out, const string &value) {
string result = escape(value);
write_formatted(out, FORMAT("text", "\"%s\""), result.c_str());
}

// Function to write unknown values
void write_unknown(FILE* out, void* value) {
write_formatted(out, FORMAT("unknown", "\"%s\""), "?");
void write_value(FILE* out, void* value) {
write_formatted(out, FORMAT("nothing", "\"%s\""), "null");
}

// Function to write void values
void write_void(FILE* out, void* value) {
write_formatted(out, FORMAT("nothing", "\"%s\""), "null");
template <typename T> void write_value(FILE* out, T value)
{
write_formatted(out, FORMAT("unknown", "%p"), "?");
}

// Function to write evaluated results
Expand Down
Loading

0 comments on commit d454083

Please sign in to comment.