diff --git a/.gitignore b/.gitignore index fca52d7c..06e07f0b 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,4 @@ venv-*/ .venv/ node_modules/ result/ +.data/ diff --git a/tested/languages/haskell/config.py b/tested/languages/haskell/config.py index 7b45168d..fdf9f5af 100644 --- a/tested/languages/haskell/config.py +++ b/tested/languages/haskell/config.py @@ -1,7 +1,8 @@ import re from pathlib import Path -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional +from tested.configs import GlobalConfig from tested.datatypes import AllTypes from tested.dodona import AnnotateCode, Message from tested.features import Construct, TypeSupport @@ -28,6 +29,10 @@ class Haskell(Language): + def __init__(self, config: Optional["GlobalConfig"]): + super().__init__(config) + self.clean_types_regex = re.compile(r"\(([^:\s]*)\s*::\s*([A-Z][a-zA-Z0-9]*)\)") + def initial_dependencies(self) -> list[str]: return ["Values.hs", "EvaluationUtils.hs"] @@ -79,6 +84,7 @@ def supported_constructs(self) -> set[Construct]: Construct.ASSIGNMENTS, Construct.EVALUATION, Construct.GLOBAL_VARIABLES, + Construct.HETEROGENEOUS_ARGUMENTS, } def compilation(self, files: list[str]) -> CallbackResult: @@ -110,7 +116,7 @@ def linter(self, remaining: float) -> tuple[list[Message], list[AnnotateCode]]: return linter.run_hlint(self.config.dodona, remaining) def cleanup_description(self, statement: str) -> str: - return cleanup_description(self, statement) + return cleanup_description(self, self.clean_types_regex.sub(r"\1", statement)) def cleanup_stacktrace(self, stacktrace: str) -> str: filename = submission_file(self) @@ -164,7 +170,9 @@ def cleanup_stacktrace(self, stacktrace: str) -> str: def generate_statement(self, statement: Statement) -> str: from tested.languages.haskell import generators - return generators.convert_statement(statement) + return self.clean_types_regex.sub( + r"\1", generators.convert_statement(statement) + ) def generate_execution_unit(self, execution_unit: "PreparedExecutionUnit") -> str: from tested.languages.haskell import generators diff --git a/tested/languages/haskell/generators.py b/tested/languages/haskell/generators.py index c2897db7..672b5a63 100644 --- a/tested/languages/haskell/generators.py +++ b/tested/languages/haskell/generators.py @@ -48,37 +48,37 @@ def convert_value(value: Value) -> str: return f"({convert_arguments(value.data)})" elif isinstance(value.type, AdvancedNumericTypes): if not isinstance(value.data, SpecialNumbers): - return f"{value.data} :: {convert_declaration(value.type)}" + return f"({value.data} :: {convert_declaration(value.type)})" elif value.data == SpecialNumbers.NOT_A_NUMBER: - return f"(0/0) :: {convert_declaration(value.type)}" + return f"((0/0) :: {convert_declaration(value.type)})" elif value.data == SpecialNumbers.POS_INFINITY: - return f"(1/0) :: {convert_declaration(value.type)}" + return f"((1/0) :: {convert_declaration(value.type)})" else: assert value.data == SpecialNumbers.NEG_INFINITY - return f"(-1/0) :: {convert_declaration(value.type)}" + return f"((-1/0) :: {convert_declaration(value.type)})" elif value.type == AdvancedStringTypes.CHAR: assert isinstance(value, StringType) return "'" + value.data.replace("'", "\\'") + "'" # Handle basic types value = as_basic_type(value) if value.type == BasicNumericTypes.INTEGER: - return f"{value.data} :: Int" + return f"({value.data} :: Int)" elif value.type == BasicNumericTypes.REAL: if not isinstance(value.data, SpecialNumbers): - return f"{value.data} :: Double" + return f"({value.data} :: Double)" elif value.data == SpecialNumbers.NOT_A_NUMBER: - return "(0/0) :: Double" + return "((0/0) :: Double)" elif value.data == SpecialNumbers.POS_INFINITY: - return "(1/0) :: Double" + return "((1/0) :: Double)" else: assert SpecialNumbers.NEG_INFINITY - return "(-1/0) :: Double" + return "((-1/0) :: Double)" elif value.type == BasicStringTypes.TEXT: return json.dumps(value.data) elif value.type == BasicBooleanTypes.BOOLEAN: return str(value.data) elif value.type == BasicNothingTypes.NOTHING: - return "Nothing :: Maybe Integer" + return "(Nothing :: Maybe Integer)" elif value.type == BasicSequenceTypes.SEQUENCE: assert isinstance(value, SequenceType) return f"[{convert_arguments(value.data)}]" diff --git a/tested/manual.py b/tested/manual.py index eab988a1..d7d64e6d 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/niko/Ontwikkeling/universal-judge/tests/exercises/echo-function" +exercise_dir = "/Users/tibvdm/PycharmProjects/universal-judge/tests/exercises/haskell" def read_config() -> DodonaConfig: @@ -24,10 +24,10 @@ def read_config() -> DodonaConfig: programming_language=SupportedLanguage("haskell"), natural_language="nl", resources=Path(exercise_dir, "evaluation"), - source=Path(exercise_dir, "solution/correct.hs"), + source=Path(exercise_dir, "solution/ndeElement.hs"), judge=Path("."), workdir=Path("workdir"), - test_suite="two-specific.tson", + test_suite="ndeElement.yaml", options=Options( linter=False, ), diff --git a/tests/exercises/haskell/evaluation/ndeElement.yaml b/tests/exercises/haskell/evaluation/ndeElement.yaml new file mode 100644 index 00000000..b4ec836f --- /dev/null +++ b/tests/exercises/haskell/evaluation/ndeElement.yaml @@ -0,0 +1,52 @@ +tabs: + - tab: geefNde (Int) + testcases: + - expression: geefNde(0, [1, 2, 3, 4, 5]) + return: 1 + - expression: geefNde(1, [1, 2, 3, 4, 5]) + return: 2 + - expression: geefNde(2, [1, 2, 3, 4, 5]) + return: 3 + - expression: geefNde(3, [1, 2, 3, 4, 5]) + return: 4 + - expression: geefNde(4, [1, 2, 3, 4, 5]) + return: 5 + + - tab: geefNde (Double) + testcases: + - expression: geefNde(0, [1.0, 2.0, 3.0, 4.0, 5.0]) + return: 1.0 + - expression: geefNde(1, [1.0, 2.0, 3.0, 4.0, 5.0]) + return: 2.0 + - expression: geefNde(2, [1.0, 2.0, 3.0, 4.0, 5.0]) + return: 3.0 + - expression: geefNde(3, [1.0, 2.0, 3.0, 4.0, 5.0]) + return: 4.0 + - expression: geefNde(4, [1.0, 2.0, 3.0, 4.0, 5.0]) + return: 5.0 + + - tab: geefNde (Char) + testcases: + - expression: geefNde(0, ['a', 'b', 'c', 'd', 'e']) + return: a + - expression: geefNde(1, ['a', 'b', 'c', 'd', 'e']) + return: b + - expression: geefNde(2, ['a', 'b', 'c', 'd', 'e']) + return: c + - expression: geefNde(3, ['a', 'b', 'c', 'd', 'e']) + return: d + - expression: geefNde(4, ['a', 'b', 'c', 'd', 'e']) + return: e + + - tab: geefNde (Tuple) + testcases: + - expression: geefNde(0, [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]) + return: [1, 5] + - expression: geefNde(1, [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]) + return: [2, 4] + - expression: geefNde(2, [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]) + return: [3, 3] + - expression: geefNde(3, [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]) + return: [4, 2] + - expression: geefNde(4, [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]) + return: [5, 1] diff --git a/tests/exercises/haskell/evaluation/voorlaatste.yaml b/tests/exercises/haskell/evaluation/voorlaatste.yaml new file mode 100644 index 00000000..38c362bb --- /dev/null +++ b/tests/exercises/haskell/evaluation/voorlaatste.yaml @@ -0,0 +1,43 @@ +tabs: + - tab: 'voorlaatste (Int)' + testcases: + - expression: voorlaatste([1, 2]) + return: 1 + - expression: voorlaatste([1, 2, 3]) + return: 2 + - expression: voorlaatste([1, 2, 3, 4]) + return: 3 + - expression: voorlaatste([1, 2, 3, 4, 5]) + return: 4 + - expression: voorlaatste([1, 0, 1, 0, 1]) + return: 0 + - expression: voorlaatste([9, 81, 1, 2, 4, 1, 42, 1]) + return: 42 + - tab: 'voorlaatste (Double)' + testcases: + - expression: voorlaatste([1.0, 2.0]) + return: 1.0 + - expression: voorlaatste([1.0, 2.0, 3.0]) + return: 2.0 + - expression: voorlaatste([1.0, 2.0, 3.0, 4.0]) + return: 3.0 + - expression: voorlaatste([1.0, 2.0, 3.0, 4.0, 5.0]) + return: 4.0 + - expression: voorlaatste([1.0, 0.0, 1.0, 0.0, 1.0]) + return: 0.0 + - expression: voorlaatste([9.0, 81.0, 1.0, 2.0, 4.0, 1.0, 42.0, 1.0]) + return: 42.0 + - tab: 'voorlaatste (Char)' + testcases: + - expression: voorlaatste(['a', 'b']) + return: 'a' + - expression: voorlaatste(['a', 'b', 'c']) + return: 'b' + - expression: voorlaatste(['a', 'b', 'c', 'd']) + return: 'c' + - expression: voorlaatste(['a', 'b', 'c', 'd', 'e']) + return: 'd' + - expression: voorlaatste(['a', 'b', 'a', 'b', 'a']) + return: 'b' + - expression: voorlaatste(['c', 'd', 'a', 'b', 'g']) + return: 'b' diff --git a/tests/exercises/haskell/solution/ndeElement.hs b/tests/exercises/haskell/solution/ndeElement.hs new file mode 100644 index 00000000..f7072810 --- /dev/null +++ b/tests/exercises/haskell/solution/ndeElement.hs @@ -0,0 +1,2 @@ +geefNde :: Int -> [a] -> a +geefNde n l = l !! n diff --git a/tests/exercises/haskell/solution/voorlaatste.hs b/tests/exercises/haskell/solution/voorlaatste.hs new file mode 100644 index 00000000..3ea94e05 --- /dev/null +++ b/tests/exercises/haskell/solution/voorlaatste.hs @@ -0,0 +1,2 @@ +voorlaatste :: [a] -> a +voorlaatste = last . init diff --git a/tests/test_language_quircks.py b/tests/test_language_quircks.py index 9af65f88..9401bae3 100644 --- a/tests/test_language_quircks.py +++ b/tests/test_language_quircks.py @@ -71,9 +71,7 @@ def test_haskell_function_arguments_without_brackets( ) result = generate_statement(bundle, statement) - assert ( - result == f'{submission_name(bundle.language)}.test 5.5 :: Double "hallo" True' - ) + assert result == f'{submission_name(bundle.language)}.test 5.5 "hallo" True' def test_javascript_exception_correct(tmp_path: Path, pytestconfig: pytest.Config):