trackler 2.1.0.26 → 2.1.0.27
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/common/CONTRIBUTING.md +0 -15
- data/common/exercises/secret-handshake/canonical-data.json +14 -7
- data/lib/trackler/version.rb +1 -1
- data/tracks/dlang/config.json +18 -0
- data/tracks/dlang/exercises/leap/leap.d +8 -0
- data/tracks/dlang/exercises/leap/leap_example.d +15 -0
- data/tracks/dlang/exercises/react/react.d +211 -0
- data/tracks/dlang/exercises/react/react_example.d +291 -0
- data/tracks/factor/README.md +7 -7
- data/tracks/factor/config.json +13 -4
- data/tracks/factor/exercises/hello-world/hello-world-example.factor +2 -3
- data/tracks/factor/exercises/hello-world/hello-world-tests.factor +1 -5
- data/tracks/factor/exercises/hello-world/hello-world.factor +1 -1
- data/tracks/factor/exercises/two-fer/two-fer-example.factor +6 -0
- data/tracks/factor/exercises/two-fer/two-fer-tests.factor +8 -0
- data/tracks/fortran/Makefile +1 -2
- data/tracks/fortran/config.json +6 -0
- data/tracks/fortran/exercises/bob/bob.fun +18 -18
- data/tracks/fortran/exercises/hamming/example.f90 +25 -0
- data/tracks/fortran/exercises/hamming/hamming.fun +75 -0
- data/tracks/go/exercises/poker/.meta/gen.go +76 -0
- data/tracks/go/exercises/poker/cases_test.go +240 -0
- data/tracks/go/exercises/poker/example.go +10 -1
- data/tracks/go/exercises/poker/poker_test.go +1 -224
- data/tracks/go/exercises/sum-of-multiples/.meta/gen.go +43 -0
- data/tracks/go/exercises/sum-of-multiples/cases_test.go +24 -0
- data/tracks/go/exercises/sum-of-multiples/example.go +1 -1
- data/tracks/go/exercises/sum-of-multiples/sum_of_multiples_test.go +1 -18
- data/tracks/groovy/exercises/robot-name/RobotSpec.groovy +38 -0
- data/tracks/kotlin/config.json +10 -0
- data/tracks/kotlin/exercises/collatz-conjecture/build.gradle +28 -0
- data/tracks/kotlin/exercises/collatz-conjecture/src/example/kotlin/CollatzCalculator.kt +12 -0
- data/tracks/kotlin/exercises/collatz-conjecture/src/main/kotlin/.keep +0 -0
- data/tracks/kotlin/exercises/collatz-conjecture/src/test/kotlin/CollatzCalculatorTest.kt +51 -0
- data/tracks/kotlin/exercises/diamond/build.gradle +28 -0
- data/tracks/kotlin/exercises/diamond/src/example/kotlin/DiamondPrinter.kt +35 -0
- data/tracks/kotlin/exercises/diamond/src/main/kotlin/DiamondPrinter.kt +5 -0
- data/tracks/kotlin/exercises/diamond/src/test/kotlin/DiamondPrinterTest.kt +121 -0
- data/tracks/kotlin/exercises/settings.gradle +2 -0
- data/tracks/ocaml/.gitignore +1 -0
- data/tracks/ocaml/.vscode/launch.json +14 -0
- data/tracks/ocaml/tools/test-generator/src/controller.ml +15 -16
- data/tracks/ocaml/tools/test-generator/src/languages.ml +30 -0
- data/tracks/ocaml/tools/test-generator/src/ocaml_special_cases.ml +137 -0
- data/tracks/ocaml/tools/test-generator/src/purescript_special_cases.ml +13 -0
- data/tracks/ocaml/tools/test-generator/src/special_cases.ml +7 -132
- data/tracks/ocaml/tools/test-generator/src/template.ml +3 -3
- data/tracks/ocaml/tools/test-generator/src/test_gen.ml +13 -3
- data/tracks/ocaml/tools/test-generator/templates/{acronym → ocaml/acronym}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{all-your-base → ocaml/all-your-base}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{anagram → ocaml/anagram}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{atbash-cipher → ocaml/atbash-cipher}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{beer-song → ocaml/beer-song}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{binary-search → ocaml/binary-search}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{bob → ocaml/bob}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{bowling → ocaml/bowling}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{bracket-push → ocaml/bracket-push}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{change → ocaml/change}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{connect → ocaml/connect}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{difference-of-squares → ocaml/difference-of-squares}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{dominoes → ocaml/dominoes}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{etl → ocaml/etl}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{forth → ocaml/forth}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{hamming → ocaml/hamming}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{hello-world → ocaml/hello-world}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{leap → ocaml/leap}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{luhn → ocaml/luhn}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{minesweeper → ocaml/minesweeper}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{pangram → ocaml/pangram}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{phone-number → ocaml/phone-number}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{prime-factors → ocaml/prime-factors}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{raindrops → ocaml/raindrops}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{roman-numerals → ocaml/roman-numerals}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{run-length-encoding → ocaml/run-length-encoding}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{say → ocaml/say}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{space-age → ocaml/space-age}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{triangle → ocaml/triangle}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/{word-count → ocaml/word-count}/template.ml +0 -0
- data/tracks/ocaml/tools/test-generator/templates/purescript/hamming/Main.purs +18 -0
- data/tracks/ocaml/tools/test-generator/test/all_tests.ml +2 -2
- data/tracks/ocaml/tools/test-generator/test/{special_cases_test.ml → ocaml_special_cases_test.ml} +3 -3
- data/tracks/ocaml/tools/test-generator/test/template_test.ml +1 -0
- metadata +59 -34
- data/tracks/groovy/exercises/robot-name/RobotTest.groovy +0 -34
@@ -0,0 +1,12 @@
|
|
1
|
+
object CollatzCalculator {
|
2
|
+
|
3
|
+
fun computeStepCount(start: Int): Int {
|
4
|
+
require(start > 0) { "Only natural numbers are allowed" }
|
5
|
+
|
6
|
+
if (start == 1) return 0
|
7
|
+
|
8
|
+
val next = if (start % 2 == 0) start / 2 else 3 * start + 1
|
9
|
+
return 1 + computeStepCount(next)
|
10
|
+
}
|
11
|
+
|
12
|
+
}
|
File without changes
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import org.junit.Ignore
|
2
|
+
import org.junit.Rule
|
3
|
+
import org.junit.Test
|
4
|
+
import org.junit.rules.ExpectedException
|
5
|
+
import kotlin.test.assertEquals
|
6
|
+
|
7
|
+
/*
|
8
|
+
* version: 1.0.0
|
9
|
+
*/
|
10
|
+
class CollatzCalculatorTest {
|
11
|
+
|
12
|
+
@Rule
|
13
|
+
@JvmField
|
14
|
+
var expectedException: ExpectedException = ExpectedException.none()
|
15
|
+
|
16
|
+
@Test
|
17
|
+
fun testZeroStepsRequiredWhenStartingFrom1() {
|
18
|
+
assertEquals(0, CollatzCalculator.computeStepCount(1))
|
19
|
+
}
|
20
|
+
|
21
|
+
@Ignore
|
22
|
+
@Test
|
23
|
+
fun testCorrectNumberOfStepsWhenAllStepsAreDivisions() {
|
24
|
+
assertEquals(4, CollatzCalculator.computeStepCount(16))
|
25
|
+
}
|
26
|
+
|
27
|
+
@Ignore
|
28
|
+
@Test
|
29
|
+
fun testCorrectNumberOfStepsWhenBothStepTypesAreNeeded() {
|
30
|
+
assertEquals(9, CollatzCalculator.computeStepCount(12))
|
31
|
+
}
|
32
|
+
|
33
|
+
@Ignore
|
34
|
+
@Test
|
35
|
+
fun testZeroIsConsideredInvalidInput() {
|
36
|
+
expectedException.expect(IllegalArgumentException::class.java)
|
37
|
+
expectedException.expectMessage("Only natural numbers are allowed")
|
38
|
+
|
39
|
+
CollatzCalculator.computeStepCount(0)
|
40
|
+
}
|
41
|
+
|
42
|
+
@Ignore
|
43
|
+
@Test
|
44
|
+
fun testNegativeIntegerIsConsideredInvalidInput() {
|
45
|
+
expectedException.expect(IllegalArgumentException::class.java)
|
46
|
+
expectedException.expectMessage("Only natural numbers are allowed")
|
47
|
+
|
48
|
+
CollatzCalculator.computeStepCount(-15)
|
49
|
+
}
|
50
|
+
|
51
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
buildscript {
|
2
|
+
ext.kotlin_version = '1.1.1'
|
3
|
+
repositories {
|
4
|
+
mavenCentral()
|
5
|
+
}
|
6
|
+
dependencies {
|
7
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
apply plugin: 'kotlin'
|
12
|
+
|
13
|
+
repositories {
|
14
|
+
mavenCentral()
|
15
|
+
}
|
16
|
+
|
17
|
+
dependencies {
|
18
|
+
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
19
|
+
|
20
|
+
testCompile 'junit:junit:4.12'
|
21
|
+
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
|
22
|
+
}
|
23
|
+
test {
|
24
|
+
testLogging {
|
25
|
+
exceptionFormat = 'full'
|
26
|
+
events = ["passed", "failed", "skipped"]
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import java.util.*
|
2
|
+
|
3
|
+
class DiamondPrinter {
|
4
|
+
|
5
|
+
companion object {
|
6
|
+
private val A_INT = 'A'.toInt()
|
7
|
+
|
8
|
+
private fun blank(length: Int): String {
|
9
|
+
return Collections.nCopies(length, " ").joinToString("")
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
fun printToList(chr: Char): List<String> {
|
14
|
+
val nRows = 2 * (chr.toInt() - A_INT) + 1
|
15
|
+
|
16
|
+
val result = mutableListOf<String>()
|
17
|
+
|
18
|
+
// Populate the top rows.
|
19
|
+
for (nRow in 0..(nRows + 1) / 2 - 1) {
|
20
|
+
val rowChr = (A_INT + nRow).toChar()
|
21
|
+
|
22
|
+
val leftHalfOfRow = blank((nRows - 1) / 2 - nRow) + rowChr + blank(nRow)
|
23
|
+
val rightHalfOfRow = leftHalfOfRow.reversed().drop(1)
|
24
|
+
val fullRow = "$leftHalfOfRow$rightHalfOfRow"
|
25
|
+
|
26
|
+
result.add(fullRow)
|
27
|
+
}
|
28
|
+
|
29
|
+
// Populate the bottom rows by 'reflecting' all rows above the middle row.
|
30
|
+
result.addAll(result.reversed().drop(1))
|
31
|
+
|
32
|
+
return result
|
33
|
+
}
|
34
|
+
|
35
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
import org.hamcrest.CoreMatchers.`is`
|
2
|
+
import org.junit.Assert.assertThat
|
3
|
+
import org.junit.Before
|
4
|
+
import org.junit.Ignore
|
5
|
+
import org.junit.Test
|
6
|
+
|
7
|
+
/*
|
8
|
+
* version: 1.0.0
|
9
|
+
*/
|
10
|
+
class DiamondPrinterTest {
|
11
|
+
|
12
|
+
private lateinit var diamondPrinter: DiamondPrinter
|
13
|
+
|
14
|
+
@Before
|
15
|
+
fun setUp() {
|
16
|
+
diamondPrinter = DiamondPrinter()
|
17
|
+
}
|
18
|
+
|
19
|
+
@Test
|
20
|
+
fun testOneByOneDiamond() {
|
21
|
+
val output = diamondPrinter.printToList('A')
|
22
|
+
assertThat(output, `is`(listOf("A")))
|
23
|
+
}
|
24
|
+
|
25
|
+
@Ignore
|
26
|
+
@Test
|
27
|
+
fun testTwoByTwoDiamond() {
|
28
|
+
val output = diamondPrinter.printToList('B')
|
29
|
+
assertThat(output, `is`(listOf(
|
30
|
+
" A ",
|
31
|
+
"B B",
|
32
|
+
" A ")))
|
33
|
+
}
|
34
|
+
|
35
|
+
@Ignore
|
36
|
+
@Test
|
37
|
+
fun testThreeByThreeDiamond() {
|
38
|
+
val output = diamondPrinter.printToList('C')
|
39
|
+
assertThat(output, `is`(listOf(
|
40
|
+
" A ",
|
41
|
+
" B B ",
|
42
|
+
"C C",
|
43
|
+
" B B ",
|
44
|
+
" A ")))
|
45
|
+
}
|
46
|
+
|
47
|
+
@Ignore
|
48
|
+
@Test
|
49
|
+
fun testFiveByFiveDiamond() {
|
50
|
+
val output = diamondPrinter.printToList('E')
|
51
|
+
assertThat(output, `is`(listOf(
|
52
|
+
" A ",
|
53
|
+
" B B ",
|
54
|
+
" C C ",
|
55
|
+
" D D ",
|
56
|
+
"E E",
|
57
|
+
" D D ",
|
58
|
+
" C C ",
|
59
|
+
" B B ",
|
60
|
+
" A ")))
|
61
|
+
}
|
62
|
+
|
63
|
+
@Ignore
|
64
|
+
@Test
|
65
|
+
fun testFullDiamond() {
|
66
|
+
val output = diamondPrinter.printToList('Z')
|
67
|
+
assertThat(output, `is`(listOf(
|
68
|
+
" A ",
|
69
|
+
" B B ",
|
70
|
+
" C C ",
|
71
|
+
" D D ",
|
72
|
+
" E E ",
|
73
|
+
" F F ",
|
74
|
+
" G G ",
|
75
|
+
" H H ",
|
76
|
+
" I I ",
|
77
|
+
" J J ",
|
78
|
+
" K K ",
|
79
|
+
" L L ",
|
80
|
+
" M M ",
|
81
|
+
" N N ",
|
82
|
+
" O O ",
|
83
|
+
" P P ",
|
84
|
+
" Q Q ",
|
85
|
+
" R R ",
|
86
|
+
" S S ",
|
87
|
+
" T T ",
|
88
|
+
" U U ",
|
89
|
+
" V V ",
|
90
|
+
" W W ",
|
91
|
+
" X X ",
|
92
|
+
" Y Y ",
|
93
|
+
"Z Z",
|
94
|
+
" Y Y ",
|
95
|
+
" X X ",
|
96
|
+
" W W ",
|
97
|
+
" V V ",
|
98
|
+
" U U ",
|
99
|
+
" T T ",
|
100
|
+
" S S ",
|
101
|
+
" R R ",
|
102
|
+
" Q Q ",
|
103
|
+
" P P ",
|
104
|
+
" O O ",
|
105
|
+
" N N ",
|
106
|
+
" M M ",
|
107
|
+
" L L ",
|
108
|
+
" K K ",
|
109
|
+
" J J ",
|
110
|
+
" I I ",
|
111
|
+
" H H ",
|
112
|
+
" G G ",
|
113
|
+
" F F ",
|
114
|
+
" E E ",
|
115
|
+
" D D ",
|
116
|
+
" C C ",
|
117
|
+
" B B ",
|
118
|
+
" A ")))
|
119
|
+
}
|
120
|
+
|
121
|
+
}
|
data/tracks/ocaml/.gitignore
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"version": "0.2.0",
|
3
|
+
"configurations": [
|
4
|
+
{
|
5
|
+
"name": "OCaml",
|
6
|
+
"type": "ocamldebug",
|
7
|
+
"request": "launch",
|
8
|
+
"program": "${workspaceRoot}/tools/test-generator/test_gen.byte",
|
9
|
+
"console": "internalConsole",
|
10
|
+
"stopOnEntry": false,
|
11
|
+
"args": ["-l", "purescript" ,"-o", "/home/steve/dev/exercism/xpurescript/exercises"]
|
12
|
+
}
|
13
|
+
]
|
14
|
+
}
|
@@ -6,6 +6,7 @@ open Codegen
|
|
6
6
|
open Special_cases
|
7
7
|
open Template
|
8
8
|
open Files
|
9
|
+
open Languages
|
9
10
|
|
10
11
|
type content = string
|
11
12
|
|
@@ -15,8 +16,8 @@ let find_nested_files (name: string) (base: string): (string * content) list =
|
|
15
16
|
|> List.filter ~f:(fun slug -> Sys.file_exists_exn (base ^ "/" ^ slug ^ "/" ^ name))
|
16
17
|
|> List.map ~f:(fun slug -> (slug, In_channel.read_all (base ^ "/" ^ slug ^ "/" ^ name)))
|
17
18
|
|
18
|
-
let find_template_files base filter =
|
19
|
-
let all_files = find_nested_files
|
19
|
+
let find_template_files base filter template_filename =
|
20
|
+
let all_files = find_nested_files template_filename base in
|
20
21
|
let filter = Option.value filter ~default:"" in
|
21
22
|
List.filter all_files ~f:(fun (slug,_) -> String.is_substring slug ~substring:filter)
|
22
23
|
|
@@ -32,18 +33,19 @@ let simplify_single_test_suite (canonical_data: canonical_data): canonical_data
|
|
32
33
|
| _ -> canonical_data
|
33
34
|
|
34
35
|
|
35
|
-
let prepend_version (v: string option) (str: string): string = match v with
|
36
|
+
let prepend_version (version_printer: string -> string) (v: string option) (str: string): string = match v with
|
36
37
|
| None -> str
|
37
|
-
| Some v ->
|
38
|
+
| Some v -> (version_printer v) ^ str
|
38
39
|
|
39
|
-
let generate_code ~(slug: string) ~(template_file: content) ~(canonical_data_file: content): (content, string) Result.t =
|
40
|
+
let generate_code ~(lc: language_config) ~(slug: string) ~(template_file: content) ~(canonical_data_file: content): (content, string) Result.t =
|
40
41
|
let open Result.Monad_infix in
|
41
|
-
Result.of_option ~error:("cannot recognize file for " ^ slug ^ " as a template") @@ find_template template_file >>= fun template ->
|
42
|
-
let edit_expected = edit_expected ~stringify:json_to_string ~slug in
|
42
|
+
Result.of_option ~error:("cannot recognize file for " ^ slug ^ " as a template") @@ find_template template_file lc.test_start_marker lc.test_end_marker >>= fun template ->
|
43
|
+
let edit_expected = edit_expected ~language:lc.name ~stringify:json_to_string ~slug in
|
43
44
|
let edit_parameters = edit_parameters ~slug in
|
44
45
|
let fill_in_template = fill_in_template edit_expected edit_parameters in
|
45
46
|
let file_text = template.file_text in
|
46
47
|
let file_lines = String.split_lines file_text |> List.to_array in
|
48
|
+
let prepend_version = prepend_version lc.version_printer in
|
47
49
|
parse_json_text canonical_data_file (expected_key_name slug) (cases_name slug)
|
48
50
|
|> Result.map_error ~f:show_error >>| simplify_single_test_suite >>= fun cd -> (match cd.tests with
|
49
51
|
| Single cases ->
|
@@ -65,10 +67,10 @@ let generate_code ~(slug: string) ~(template_file: content) ~(canonical_data_fil
|
|
65
67
|
|> Result.map ~f:(prepend_version cd.version)
|
66
68
|
)
|
67
69
|
|
68
|
-
let output_tests (files: (string * content * content) list) (output_folder: string) ~(generated_folder: string): unit =
|
69
|
-
let output_filepath name = output_folder ^ "/" ^ name ^ "/
|
70
|
+
let output_tests (lc: language_config) (files: (string * content * content) list) (output_folder: string) ~(generated_folder: string): unit =
|
71
|
+
let output_filepath name = output_folder ^ "/" ^ name ^ "/" ^ lc.template_file_name in
|
70
72
|
let output1 (slug,t,c) =
|
71
|
-
match generate_code slug t c with
|
73
|
+
match generate_code lc slug t c with
|
72
74
|
| Ok code ->
|
73
75
|
if backup ~base_folder:generated_folder ~slug ~contents:code
|
74
76
|
then Out_channel.write_all (output_filepath slug) code
|
@@ -76,14 +78,11 @@ let output_tests (files: (string * content * content) list) (output_folder: stri
|
|
76
78
|
| Error e -> print_endline ("Failed when generating " ^ slug ^ ", error: " ^ e) in
|
77
79
|
List.iter files ~f:output1
|
78
80
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
let run ~(templates_folder: string) ~(canonical_data_folder: string) ~(output_folder: string) ~(generated_folder: string) (filter: string option) =
|
83
|
-
let template_files = find_template_files templates_folder filter in
|
81
|
+
let run ~(language_config: language_config) ~(templates_folder: string) ~(canonical_data_folder: string) ~(output_folder: string) ~(generated_folder: string) (filter: string option) =
|
82
|
+
let template_files = find_template_files templates_folder filter language_config.template_file_name in
|
84
83
|
let canonical_data_files = find_canonical_data_files canonical_data_folder in
|
85
84
|
let combined = combine_files template_files canonical_data_files in
|
86
|
-
output_tests combined output_folder generated_folder
|
85
|
+
output_tests language_config combined output_folder generated_folder
|
87
86
|
|
88
87
|
let check_canonical_data canonical_data_folder =
|
89
88
|
let ok_count = ref 0 in
|
@@ -0,0 +1,30 @@
|
|
1
|
+
open Core
|
2
|
+
|
3
|
+
type language_config = {
|
4
|
+
name: string;
|
5
|
+
template_file_name: string;
|
6
|
+
default_base_folder: string;
|
7
|
+
test_start_marker: string;
|
8
|
+
test_end_marker: string;
|
9
|
+
version_printer: string -> string
|
10
|
+
}
|
11
|
+
|
12
|
+
let default_language_config = function
|
13
|
+
| "ocaml" -> {
|
14
|
+
name = "ocaml";
|
15
|
+
template_file_name = "template.ml";
|
16
|
+
default_base_folder = "../..";
|
17
|
+
test_start_marker = "(* TEST";
|
18
|
+
test_end_marker = "END TEST";
|
19
|
+
version_printer = fun v -> "(* Test/exercise version: \"" ^ v ^ "\" *)\n\n";
|
20
|
+
}
|
21
|
+
| "purescript" -> {
|
22
|
+
name = "purescript";
|
23
|
+
template_file_name = "Main.purs";
|
24
|
+
default_base_folder = "../../../xpurescript";
|
25
|
+
test_start_marker = "--TEST";
|
26
|
+
test_end_marker = "--END TEST";
|
27
|
+
version_printer = fun v -> "-- Test/exercise version: \"" ^ v ^ "\"\n\n";
|
28
|
+
}
|
29
|
+
| x -> failwith @@ "unknown language " ^ x
|
30
|
+
|
@@ -0,0 +1,137 @@
|
|
1
|
+
open Core
|
2
|
+
|
3
|
+
open Model
|
4
|
+
open Yojson.Basic
|
5
|
+
|
6
|
+
let strip_quotes s = String.drop_prefix s 1 |> Fn.flip String.drop_suffix 1
|
7
|
+
|
8
|
+
let map_elements (to_str: json -> string) (parameters: (string * json) list): (string * string) list =
|
9
|
+
List.map parameters ~f:(fun (k,j) -> (k,to_str j))
|
10
|
+
|
11
|
+
let optional_int ~(none: int) = function
|
12
|
+
| `Int n when n = none -> "None"
|
13
|
+
| `Int n -> "(Some " ^ Int.to_string n ^ ")"
|
14
|
+
| x -> json_to_string x
|
15
|
+
|
16
|
+
let optional_int_list = function
|
17
|
+
| `List xs -> "(Some [" ^ String.concat ~sep:"; " (List.map ~f:json_to_string xs) ^ "])"
|
18
|
+
| _ -> "None"
|
19
|
+
|
20
|
+
let optional_int_or_string ~(none: int) = function
|
21
|
+
| `String s -> "(Some \"" ^ s ^ "\")"
|
22
|
+
| `Int n when n = none -> "None"
|
23
|
+
| x -> json_to_string x
|
24
|
+
|
25
|
+
let default_value ~(key: string) ~(value: string) (parameters: (string * string) list): (string * string) list =
|
26
|
+
if List.exists ~f:(fun (k, _) -> k = key) parameters
|
27
|
+
then parameters
|
28
|
+
else (key, value) :: parameters
|
29
|
+
|
30
|
+
let optional_strings ~(f: string -> bool) (parameters: (string * json) list): (string * string) list =
|
31
|
+
let replace parameter =
|
32
|
+
let (k, v) = parameter in
|
33
|
+
if f k
|
34
|
+
then (k, "(Some " ^ json_to_string v ^ ")")
|
35
|
+
else (k, json_to_string v) in
|
36
|
+
List.map ~f:replace parameters
|
37
|
+
|
38
|
+
let option_of_null (value: json): string = match value with
|
39
|
+
| `Null -> "None"
|
40
|
+
| `String s -> "(Some \"" ^ s ^ "\")"
|
41
|
+
| `List xs as l -> "(Some " ^ (json_to_string l) ^ ")"
|
42
|
+
| _ -> failwith "cannot handle this type"
|
43
|
+
|
44
|
+
let is_empty_string (value: json): bool = match value with
|
45
|
+
| `String s -> String.is_empty s
|
46
|
+
| _ -> false
|
47
|
+
|
48
|
+
let edit_connect_expected = function
|
49
|
+
| `String "X" -> "(Some X)"
|
50
|
+
| `String "O" -> "(Some O)"
|
51
|
+
| `String "" -> "None"
|
52
|
+
| x -> failwith "Bad json value in connect " ^ json_to_string x
|
53
|
+
|
54
|
+
let edit_change_expected (value: json) = match value with
|
55
|
+
| `List xs -> "(Some [" ^ (String.concat ~sep:"; " (List.map ~f:json_to_string xs)) ^ "])"
|
56
|
+
| `Int (-1) -> "None"
|
57
|
+
| _ -> failwith "Bad json value in change"
|
58
|
+
|
59
|
+
let edit_bowling_expected (value: json) = match value with
|
60
|
+
| `Int n -> "(Ok " ^ (Int.to_string n) ^ ")"
|
61
|
+
| `Assoc [(k, v)] ->
|
62
|
+
if k = "error" then "(Error " ^ json_to_string v ^ ")" else failwith ("Can only handle error value but got " ^ k)
|
63
|
+
| _ -> failwith "Bad json value in bowling"
|
64
|
+
|
65
|
+
let ocaml_edit_expected ~(stringify: json -> string) ~(slug: string) ~(value: json) = match slug with
|
66
|
+
| "hamming" -> optional_int ~none:(-1) value
|
67
|
+
| "all-your-base" -> optional_int_list value
|
68
|
+
| "say" -> optional_int_or_string ~none:(-1) value
|
69
|
+
| "phone-number" -> option_of_null value
|
70
|
+
| "connect" -> edit_connect_expected value
|
71
|
+
| "change" -> edit_change_expected value
|
72
|
+
| "bowling" -> edit_bowling_expected value
|
73
|
+
| "binary-search" -> optional_int ~none:(-1) value
|
74
|
+
| "forth" -> option_of_null value
|
75
|
+
| _ -> stringify value
|
76
|
+
|
77
|
+
let edit_say (ps: (string * json) list) =
|
78
|
+
let edit = function
|
79
|
+
| ("input", v) -> ("input", let v = json_to_string v in if Int.of_string v >= 0 then "(" ^ v ^ "L)" else v ^ "L")
|
80
|
+
| (k, ps) -> (k, json_to_string ps) in
|
81
|
+
List.map ps ~f:edit
|
82
|
+
|
83
|
+
let edit_all_your_base (ps: (string * json) list): (string * string) list =
|
84
|
+
let edit = function
|
85
|
+
| ("output_base", v) -> let v = json_to_string v in ("output_base", if Int.of_string v >= 0 then v else "(" ^ v ^ ")")
|
86
|
+
| ("input_base", v) -> let v = json_to_string v in ("input_base", if Int.of_string v >= 0 then v else "(" ^ v ^ ")")
|
87
|
+
| (k, v) -> (k, json_to_string v) in
|
88
|
+
List.map ps ~f:edit
|
89
|
+
|
90
|
+
let edit_dominoes (ps: (string * json) list): (string * string) list =
|
91
|
+
let two_elt_list_to_tuple (j: json): string = match j with
|
92
|
+
| `List [`Int x1; `Int x2] -> sprintf "(%d,%d)" x1 x2
|
93
|
+
| _ -> failwith "two element list expected, but got " ^ (json_to_string j) in
|
94
|
+
let edit (p: (string * json)) = match p with
|
95
|
+
| ("input", `List j) -> ("input", "[" ^ (List.map ~f:two_elt_list_to_tuple j |> String.concat ~sep:"; ") ^ "]")
|
96
|
+
| (k, v) -> (k, json_to_string v) in
|
97
|
+
List.map ps ~f:edit
|
98
|
+
|
99
|
+
|
100
|
+
let edit_space_age (ps: (string * json) list): (string * string) list =
|
101
|
+
let edit = function
|
102
|
+
| ("planet", v) -> ("planet", json_to_string v |> strip_quotes)
|
103
|
+
| (k, v) -> (k, json_to_string v) in
|
104
|
+
List.map ps ~f:edit
|
105
|
+
|
106
|
+
let edit_bowling (ps: (string * json) list): (string * string) list =
|
107
|
+
let edit = function
|
108
|
+
| ("property", v) -> ("property", json_to_string v |> strip_quotes)
|
109
|
+
| ("roll", `Int n) -> ("roll", let s = Int.to_string n in if n < 0 then ("(" ^ s ^ ")") else s)
|
110
|
+
| (k, v) -> (k, json_to_string v) in
|
111
|
+
List.map ps ~f:edit
|
112
|
+
|
113
|
+
let edit_binary_search (ps: (string * json) list): (string * string) list =
|
114
|
+
let open Yojson.Basic.Util in
|
115
|
+
let as_array_string xs =
|
116
|
+
let xs = to_list xs |> List.map ~f:to_int |> List.map ~f:Int.to_string in
|
117
|
+
"[|" ^ String.concat ~sep:"; " xs ^ "|]" in
|
118
|
+
let edit = function
|
119
|
+
| ("array", v) -> ("array", as_array_string v)
|
120
|
+
| (k, v) -> (k, json_to_string v) in
|
121
|
+
List.map ps ~f:edit
|
122
|
+
|
123
|
+
let edit_parameters ~(slug: string) (parameters: (string * json) list) = match (slug, parameters) with
|
124
|
+
| ("hello-world", ps) -> default_value ~key:"name" ~value:"None" (optional_strings ~f:(fun _x -> true) parameters)
|
125
|
+
| ("say", ps) -> edit_say ps
|
126
|
+
| ("all-your-base", ps) -> edit_all_your_base ps
|
127
|
+
| ("dominoes", ps) -> edit_dominoes ps
|
128
|
+
| ("space-age", ps) -> edit_space_age ps
|
129
|
+
| ("bowling", ps) -> edit_bowling ps
|
130
|
+
| ("binary-search", ps) -> edit_binary_search ps
|
131
|
+
| (_, ps) -> map_elements json_to_string ps
|
132
|
+
|
133
|
+
let expected_key_name slug = match slug with
|
134
|
+
| "dominoes" -> "can_chain"
|
135
|
+
| _ -> "expected"
|
136
|
+
|
137
|
+
let cases_name _slug = "cases"
|