From 0f398c576686c04dd5d52e5de50571e1afc2d6d7 Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Wed, 22 Apr 2026 15:42:57 +1000 Subject: [PATCH] bin/verify-exercises We move .factor files to match ```text "files": { "solution": [ "%{kebab_slug}/%{kebab_slug}.factor" ], "test": [ "%{kebab_slug}/%{kebab_slug}-tests.factor" ], "example": [ ".meta/example.factor" ], "exemplar": [ ".meta/exemplar.factor" ] }, ``` All tests are now generated. bin/verify-exercises tests exercises using local factor install. --- bin/verify-exercises | 107 ++++++++++++++++++ config.json | 8 +- .../practice/accumulate/.meta/config.json | 6 +- ...cumulate-example.factor => example.factor} | 5 +- .../practice/accumulate/.meta/generator.jl | 6 +- .../{ => accumulate}/accumulate-tests.factor | 17 +-- .../{ => accumulate}/accumulate.factor | 3 +- exercises/practice/acronym/.meta/config.json | 6 +- ...{acronym-example.factor => example.factor} | 6 +- exercises/practice/acronym/.meta/generator.jl | 5 +- .../practice/acronym/acronym-tests.factor | 35 ------ .../acronym/acronym/acronym-tests.factor | 44 +++++++ .../acronym/{ => acronym}/acronym.factor | 3 +- .../.meta/all-your-base-example.factor | 46 -------- .../practice/all-your-base/.meta/config.json | 6 +- .../all-your-base/.meta/example.factor | 23 ++++ .../practice/all-your-base/.meta/generator.jl | 7 +- .../all-your-base/all-your-base-tests.factor | 72 ------------ .../all-your-base/all-your-base-tests.factor | 92 +++++++++++++++ .../{ => all-your-base}/all-your-base.factor | 3 +- .../armstrong-numbers/.meta/config.json | 6 +- ...-numbers-example.factor => example.factor} | 3 +- .../armstrong-numbers/.meta/generator.jl | 4 +- .../armstrong-numbers-tests.factor | 32 ------ .../armstrong-numbers-tests.factor | 35 ++++++ .../armstrong-numbers.factor | 3 +- .../practice/hello-world/.meta/config.json | 6 +- ...lo-world-example.factor => example.factor} | 2 +- .../practice/hello-world/.meta/generator.jl | 4 +- .../hello-world/hello-world-tests.factor | 4 - .../hello-world/hello-world-tests.factor | 8 ++ .../{ => hello-world}/hello-world.factor | 2 +- exercises/practice/isogram/.meta/config.json | 6 +- .../practice/isogram/.meta/example.factor | 5 + exercises/practice/isogram/.meta/generator.jl | 2 - .../isogram/.meta/isogram-example.factor | 5 - .../{ => isogram}/isogram-tests.factor | 23 +++- .../isogram/{ => isogram}/isogram.factor | 3 +- exercises/practice/leap/.meta/config.json | 6 +- .../{leap-example.factor => example.factor} | 2 +- exercises/practice/leap/.meta/generator.jl | 2 - exercises/practice/leap/leap-tests.factor | 32 ------ .../practice/leap/leap/leap-tests.factor | 35 ++++++ .../practice/leap/{ => leap}/leap.factor | 3 +- .../practice/raindrops/.meta/config.json | 6 +- ...aindrops-example.factor => example.factor} | 3 +- .../practice/raindrops/.meta/generator.jl | 2 - .../{ => raindrops}/raindrops-tests.factor | 47 ++++---- .../{ => raindrops}/raindrops.factor | 3 +- .../practice/resistor-color/.meta/config.json | 6 +- ...or-color-example.factor => example.factor} | 3 +- .../resistor-color/.meta/generator.jl | 6 +- .../resistor-color-tests.factor | 8 +- .../resistor-color.factor | 3 +- .../practice/reverse-string/.meta/config.json | 6 +- ...e-string-example.factor => example.factor} | 3 +- .../reverse-string/.meta/generator.jl | 3 - .../practice/reverse-string/.meta/tests.toml | 12 ++ .../reverse-string-tests.factor | 9 +- .../reverse-string.factor | 3 +- exercises/practice/two-fer/.meta/config.json | 6 +- ...{two-fer-example.factor => example.factor} | 0 exercises/practice/two-fer/.meta/generator.jl | 6 +- .../practice/two-fer/two-fer-tests.factor | 8 -- .../two-fer/two-fer/two-fer-tests.factor | 17 +++ .../two-fer/{ => two-fer}/two-fer.factor | 3 +- generator/generate.jl | 37 +++--- 67 files changed, 532 insertions(+), 401 deletions(-) create mode 100755 bin/verify-exercises rename exercises/practice/accumulate/.meta/{accumulate-example.factor => example.factor} (75%) rename exercises/practice/accumulate/{ => accumulate}/accumulate-tests.factor (51%) rename exercises/practice/accumulate/{ => accumulate}/accumulate.factor (56%) rename exercises/practice/acronym/.meta/{acronym-example.factor => example.factor} (72%) delete mode 100644 exercises/practice/acronym/acronym-tests.factor create mode 100644 exercises/practice/acronym/acronym/acronym-tests.factor rename exercises/practice/acronym/{ => acronym}/acronym.factor (61%) delete mode 100644 exercises/practice/all-your-base/.meta/all-your-base-example.factor create mode 100644 exercises/practice/all-your-base/.meta/example.factor delete mode 100644 exercises/practice/all-your-base/all-your-base-tests.factor create mode 100644 exercises/practice/all-your-base/all-your-base/all-your-base-tests.factor rename exercises/practice/all-your-base/{ => all-your-base}/all-your-base.factor (52%) rename exercises/practice/armstrong-numbers/.meta/{armstrong-numbers-example.factor => example.factor} (94%) delete mode 100644 exercises/practice/armstrong-numbers/armstrong-numbers-tests.factor create mode 100644 exercises/practice/armstrong-numbers/armstrong-numbers/armstrong-numbers-tests.factor rename exercises/practice/armstrong-numbers/{ => armstrong-numbers}/armstrong-numbers.factor (69%) rename exercises/practice/hello-world/.meta/{hello-world-example.factor => example.factor} (74%) delete mode 100644 exercises/practice/hello-world/hello-world-tests.factor create mode 100644 exercises/practice/hello-world/hello-world/hello-world-tests.factor rename exercises/practice/hello-world/{ => hello-world}/hello-world.factor (74%) create mode 100644 exercises/practice/isogram/.meta/example.factor delete mode 100644 exercises/practice/isogram/.meta/isogram-example.factor rename exercises/practice/isogram/{ => isogram}/isogram-tests.factor (56%) rename exercises/practice/isogram/{ => isogram}/isogram.factor (67%) rename exercises/practice/leap/.meta/{leap-example.factor => example.factor} (78%) delete mode 100644 exercises/practice/leap/leap-tests.factor create mode 100644 exercises/practice/leap/leap/leap-tests.factor rename exercises/practice/leap/{ => leap}/leap.factor (56%) rename exercises/practice/raindrops/.meta/{raindrops-example.factor => example.factor} (90%) rename exercises/practice/raindrops/{ => raindrops}/raindrops-tests.factor (53%) rename exercises/practice/raindrops/{ => raindrops}/raindrops.factor (68%) rename exercises/practice/resistor-color/.meta/{resistor-color-example.factor => example.factor} (85%) rename exercises/practice/resistor-color/{ => resistor-color}/resistor-color-tests.factor (70%) rename exercises/practice/resistor-color/{ => resistor-color}/resistor-color.factor (79%) rename exercises/practice/reverse-string/.meta/{reverse-string-example.factor => example.factor} (72%) rename exercises/practice/reverse-string/{ => reverse-string}/reverse-string-tests.factor (74%) rename exercises/practice/reverse-string/{ => reverse-string}/reverse-string.factor (65%) rename exercises/practice/two-fer/.meta/{two-fer-example.factor => example.factor} (100%) delete mode 100644 exercises/practice/two-fer/two-fer-tests.factor create mode 100644 exercises/practice/two-fer/two-fer/two-fer-tests.factor rename exercises/practice/two-fer/{ => two-fer}/two-fer.factor (51%) diff --git a/bin/verify-exercises b/bin/verify-exercises new file mode 100755 index 00000000..9364320d --- /dev/null +++ b/bin/verify-exercises @@ -0,0 +1,107 @@ +#!/usr/bin/env bash + +# Synopsis: +# Verify that each exercise's example/exemplar solution passes the tests. +# You can either verify all exercises or a single exercise. + +# Example: verify all exercises +# bin/verify-exercises + +# Example: verify single exercise +# bin/verify-exercises two-fer + +set -eo pipefail + +die() { echo "$*" >&2; exit 1; } + +required_tool() { + command -v "${1}" >/dev/null 2>&1 || + die "${1} is required but not installed. Please install it and make sure it's in your PATH." +} + +required_tool jq + +FACTOR="${FACTOR:-factor}" +"${FACTOR}" -e='1 2 +' >/dev/null 2>&1|| + die "Factor is required but '${FACTOR}' does not appear to be the Factor language. +Set the FACTOR environment variable to the path of the Factor executable." + +copy_example_or_examplar_to_solution() { + jq -c '[.files.solution, .files.exemplar // .files.example] | transpose | map({src: .[1], dst: .[0]}) | .[]' .meta/config.json \ + | while read -r src_and_dst; do + cp "$(jq -r '.src' <<< "${src_and_dst}")" "$(jq -r '.dst' <<< "${src_and_dst}")" + done +} + +unskip_tests() { + jq -r '.files.test[]' .meta/config.json | while read -r test_file; do + sed -i '/STOP-HERE/d' "${test_file}" + done +} + +run_tests() { + local slug + slug="${1}" + "${FACTOR}" -e="USING: vocabs.loader tools.test tools.test.private namespaces kernel system ; \".\" add-vocab-root \"${slug}\" require \"${slug}\" test test-failures get empty? [ 0 exit ] [ :test-failures 1 exit ] if" +} + +verify_exercise() { + local dir + local slug + local tmp_dir + + dir=$(realpath "${1}") + slug=$(basename "${dir}") + tmp_dir="$(pwd)/test-exercises/${slug}" + + echo "Verifying ${slug} exercise..." + + rm -rf "${tmp_dir}" 2>/dev/null || true + mkdir -p "${tmp_dir}" + cp -r "${dir}/." "${tmp_dir}" + local rc=0 + ( + cd "${tmp_dir}" + copy_example_or_examplar_to_solution + unskip_tests + run_tests "${slug}" + ) || rc=$? + rm -rf "${tmp_dir}" 2>/dev/null || true + return "${rc}" +} + +verify_exercises() { + local exercise_slug + + exercise_slug="${1}" + + local rc=0 + local -a failed=() + + shopt -s nullglob + count=0 + for exercise_dir in ./exercises/{concept,practice}/${exercise_slug}/; do + if [[ -d "${exercise_dir}" ]]; then + slug=$(basename "${exercise_dir}") + if jq -e --arg s "${slug}" '.exercises.practice[] | select(.slug == $s) | .status == "wip"' config.json > /dev/null 2>&1; then + echo "Skipping ${slug} (wip)..." + ((++count)) + continue + fi + if ! verify_exercise "${exercise_dir}"; then + rc=1 + failed+=("$(basename "${exercise_dir}")") + fi + ((++count)) + fi + done + ((count > 0)) || die 'no matching exercises found!' + if (( ${#failed[@]} > 0 )); then + echo >&2 "" + echo >&2 "FAILED: ${failed[*]}" + fi + return "${rc}" +} + +exercise_slug="${1:-*}" +verify_exercises "${exercise_slug}" diff --git a/config.json b/config.json index b108f1c1..79354773 100644 --- a/config.json +++ b/config.json @@ -17,16 +17,16 @@ }, "files": { "solution": [ - "%{kebab_slug}.factor" + "%{kebab_slug}/%{kebab_slug}.factor" ], "test": [ - "%{kebab_slug}-tests.factor" + "%{kebab_slug}/%{kebab_slug}-tests.factor" ], "example": [ - ".meta/%{kebab_slug}-example.factor" + ".meta/example.factor" ], "exemplar": [ - ".meta/%{kebab_slug}-exemplar.factor" + ".meta/exemplar.factor" ] }, "exercises": { diff --git a/exercises/practice/accumulate/.meta/config.json b/exercises/practice/accumulate/.meta/config.json index cf1be41b..e0616a26 100644 --- a/exercises/practice/accumulate/.meta/config.json +++ b/exercises/practice/accumulate/.meta/config.json @@ -4,13 +4,13 @@ ], "files": { "solution": [ - "accumulate.factor" + "accumulate/accumulate.factor" ], "test": [ - "accumulate-tests.factor" + "accumulate/accumulate-tests.factor" ], "example": [ - ".meta/accumulate-example.factor" + ".meta/example.factor" ] }, "blurb": "Implement the `accumulate` operation, which, given a collection and an operation to perform on each element of the collection, returns a new collection containing the result of applying that operation to each element of the input collection.", diff --git a/exercises/practice/accumulate/.meta/accumulate-example.factor b/exercises/practice/accumulate/.meta/example.factor similarity index 75% rename from exercises/practice/accumulate/.meta/accumulate-example.factor rename to exercises/practice/accumulate/.meta/example.factor index b3ae3162..4442fb8d 100644 --- a/exercises/practice/accumulate/.meta/accumulate-example.factor +++ b/exercises/practice/accumulate/.meta/example.factor @@ -1,14 +1,13 @@ USING: arrays kernel locals sequences sequences.extras ; IN: accumulate -:: accum ( seq xform: ( a -- b ) -- newseq ) +:: accum ( seq quot: ( x -- y ) -- newseq ) seq >resizable :> seq seq length seq new-resizable :> newseq [ seq empty? ] [ - seq pop xform call + seq pop quot call newseq push ] until newseq reverse >array ; inline - diff --git a/exercises/practice/accumulate/.meta/generator.jl b/exercises/practice/accumulate/.meta/generator.jl index 03354289..26223067 100644 --- a/exercises/practice/accumulate/.meta/generator.jl +++ b/exercises/practice/accumulate/.meta/generator.jl @@ -1,11 +1,9 @@ module Accumulate -const HEADER = "USING: accumulate math unicode tools.test ;\nEXCLUDE: sequences => accumulate ;" - const ACCUMULATORS = Dict( "(x) => x * x" => "[ dup * ]", "(x) => upcase(x)" => "[ >upper ]", - "(x) => reverse(x)" => "[ reverse ]", + "(x) => reverse(x)" => "[ string-reverse ]", """(x) => accumulate(["1", "2", "3"], (y) => x + y)""" => """[ [ swap append ] curry { "1" "2" "3" } swap accumulate ]""", ) @@ -40,7 +38,7 @@ function gen_test_case(case) list = format_input_list(case["input"]["list"]) acc = ACCUMULATORS[case["input"]["accumulator"]] expected = format_value(case["expected"]) - return "{ $(expected) } [ $(list) $(acc) accumulate ] unit-test" + return "{ $(expected) }\n[ $(list) $(acc) accum ] unit-test" end end diff --git a/exercises/practice/accumulate/accumulate-tests.factor b/exercises/practice/accumulate/accumulate/accumulate-tests.factor similarity index 51% rename from exercises/practice/accumulate/accumulate-tests.factor rename to exercises/practice/accumulate/accumulate/accumulate-tests.factor index 3e655dee..f8c215f0 100644 --- a/exercises/practice/accumulate/accumulate-tests.factor +++ b/exercises/practice/accumulate/accumulate/accumulate-tests.factor @@ -1,13 +1,19 @@ -USING: accumulate io kernel math sequences tools.test unicode ; +USING: accumulate io kernel lexer tools.test unicode ; IN: accumulate.tests +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + "Accumulate:" print "accumulate empty" print -{ { } } [ { } [ sq ] accum ] unit-test +{ { } } +[ { } [ dup * ] accum ] unit-test + +STOP-HERE "accumulate squares" print -{ { 1 4 9 } } [ { 1 2 3 } [ sq ] accum ] unit-test +{ { 1 4 9 } } +[ { 1 2 3 } [ dup * ] accum ] unit-test "accumulate upcases" print { { "HELLO" "WORLD" } } @@ -16,8 +22,3 @@ IN: accumulate.tests "accumulate reversed strings" print { { "eht" "kciuq" "nworb" "xof" "cte" } } [ { "the" "quick" "brown" "fox" "etc" } [ string-reverse ] accum ] unit-test - -"accumulate recursively" print -{ { { "a1" "a2" "a3" } { "b1" "b2" "b3" } { "c1" "c2" "c3" } } } -[ { "a" "b" "c" } [ dup dup { "1" "2" "3" } [ append ] accum ] accum ] unit-test - diff --git a/exercises/practice/accumulate/accumulate.factor b/exercises/practice/accumulate/accumulate/accumulate.factor similarity index 56% rename from exercises/practice/accumulate/accumulate.factor rename to exercises/practice/accumulate/accumulate/accumulate.factor index 34a74f96..5ea83ac6 100644 --- a/exercises/practice/accumulate/accumulate.factor +++ b/exercises/practice/accumulate/accumulate/accumulate.factor @@ -1,6 +1,5 @@ USING: kernel ; IN: accumulate -: accum ( seq xform: ( a -- b ) -- newseq ) +:: accum ( seq quot: ( x -- y ) -- newseq ) "unimplemented" throw ; - diff --git a/exercises/practice/acronym/.meta/config.json b/exercises/practice/acronym/.meta/config.json index 23819592..78f58cca 100644 --- a/exercises/practice/acronym/.meta/config.json +++ b/exercises/practice/acronym/.meta/config.json @@ -4,13 +4,13 @@ ], "files": { "solution": [ - "acronym.factor" + "acronym/acronym.factor" ], "test": [ - "acronym-tests.factor" + "acronym/acronym-tests.factor" ], "example": [ - ".meta/acronym-example.factor" + ".meta/example.factor" ] }, "blurb": "Convert a long phrase to its acronym.", diff --git a/exercises/practice/acronym/.meta/acronym-example.factor b/exercises/practice/acronym/.meta/example.factor similarity index 72% rename from exercises/practice/acronym/.meta/acronym-example.factor rename to exercises/practice/acronym/.meta/example.factor index 121e1715..3d41fd68 100644 --- a/exercises/practice/acronym/.meta/acronym-example.factor +++ b/exercises/practice/acronym/.meta/example.factor @@ -1,11 +1,9 @@ USING: ascii kernel sequences splitting ; IN: acronym -: acronym ( str -- abbr ) +: abbreviate ( phrase -- acronym ) " -_" split [ empty? ] reject [ 1 head ] map "" join - >upper -; - + >upper ; diff --git a/exercises/practice/acronym/.meta/generator.jl b/exercises/practice/acronym/.meta/generator.jl index a54daaf9..2ce6c9c5 100644 --- a/exercises/practice/acronym/.meta/generator.jl +++ b/exercises/practice/acronym/.meta/generator.jl @@ -1,12 +1,9 @@ module Acronym -const HEADER = "USING: acronym tools.test ;" - - function gen_test_case(case) phrase = escape_factor(case["input"]["phrase"]) expected = case["expected"] - return """{ "$(expected)" } [ "$(phrase)" abbreviate ] unit-test""" + return """{ "$(expected)" }\n[ "$(phrase)" abbreviate ] unit-test""" end end diff --git a/exercises/practice/acronym/acronym-tests.factor b/exercises/practice/acronym/acronym-tests.factor deleted file mode 100644 index aaef7757..00000000 --- a/exercises/practice/acronym/acronym-tests.factor +++ /dev/null @@ -1,35 +0,0 @@ -USING: acronym io kernel math sequences tools.test unicode ; -IN: acronym.tests - -"Acronym:" print - -"basic" print -{ "PNG" } [ "Portable Network Graphics" acronym ] unit-test - -"lowercase words" print -{ "ROR" } [ "Ruby on Rails" acronym ] unit-test - -"punctuation" print -{ "FIFO" } [ "First In, First Out" acronym ] unit-test - -"all caps word" print -{ "GIMP" } [ "GNU Image Manipulation Program" acronym ] unit-test - -"punctuation without whitespace" print -{ "CMOS" } [ "Complementary metal-oxide semiconductor" acronym ] unit-test - -"very long abbreviation" print -{ "ROTFLSHTMDCOALM" } [ - "Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me" - acronym -] unit-test - -"consecutive delimiters" print -{ "SIMUFTA" } [ "Something - I made up from thin air" acronym ] unit-test - -"apostrophes" print -{ "HC" } [ "Halley's Comet" acronym ] unit-test - -"underscore emphasis" print -{ "TRNT" } [ "The Road _Not_ Taken" acronym ] unit-test - diff --git a/exercises/practice/acronym/acronym/acronym-tests.factor b/exercises/practice/acronym/acronym/acronym-tests.factor new file mode 100644 index 00000000..74252570 --- /dev/null +++ b/exercises/practice/acronym/acronym/acronym-tests.factor @@ -0,0 +1,44 @@ +USING: acronym io kernel lexer tools.test unicode ; +IN: acronym.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Acronym:" print + +"basic" print +{ "PNG" } +[ "Portable Network Graphics" abbreviate ] unit-test + +STOP-HERE + +"lowercase words" print +{ "ROR" } +[ "Ruby on Rails" abbreviate ] unit-test + +"punctuation" print +{ "FIFO" } +[ "First In, First Out" abbreviate ] unit-test + +"all caps word" print +{ "GIMP" } +[ "GNU Image Manipulation Program" abbreviate ] unit-test + +"punctuation without whitespace" print +{ "CMOS" } +[ "Complementary metal-oxide semiconductor" abbreviate ] unit-test + +"very long abbreviation" print +{ "ROTFLSHTMDCOALM" } +[ "Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me" abbreviate ] unit-test + +"consecutive delimiters" print +{ "SIMUFTA" } +[ "Something - I made up from thin air" abbreviate ] unit-test + +"apostrophes" print +{ "HC" } +[ "Halley's Comet" abbreviate ] unit-test + +"underscore emphasis" print +{ "TRNT" } +[ "The Road _Not_ Taken" abbreviate ] unit-test diff --git a/exercises/practice/acronym/acronym.factor b/exercises/practice/acronym/acronym/acronym.factor similarity index 61% rename from exercises/practice/acronym/acronym.factor rename to exercises/practice/acronym/acronym/acronym.factor index ecb1d9a6..8c1642a5 100644 --- a/exercises/practice/acronym/acronym.factor +++ b/exercises/practice/acronym/acronym/acronym.factor @@ -1,6 +1,5 @@ USING: kernel ; IN: acronym -: acronym ( str -- abbr ) +: abbreviate ( phrase -- acronym ) "unimplemented" throw ; - diff --git a/exercises/practice/all-your-base/.meta/all-your-base-example.factor b/exercises/practice/all-your-base/.meta/all-your-base-example.factor deleted file mode 100644 index 223c7925..00000000 --- a/exercises/practice/all-your-base/.meta/all-your-base-example.factor +++ /dev/null @@ -1,46 +0,0 @@ -USING: combinators kernel locals math math.functions math.ranges sequences ; -IN: all-your-base - -:: powers ( base n -- seq ) - n [0,b) reverse - [ base swap ^ ] map ; - -:: from-digits ( digits base -- x ) - digits length :> n - - base n powers - digits [ * ] 2map sum ; - -:: (to-digits) ( digits x base -- newdigits ) - x 0 <= - [ digits ] - [ digits x base mod suffix - x base /i - base - (to-digits) - ] - if ; - -:: to-digits ( x base -- digits ) - { } x base (to-digits) reverse :> newdigits - - newdigits length 0 = - [ { 0 } ] - [ newdigits ] if ; - -:: invalid-digits? ( digits base -- ? ) - digits - [ [ 0 < ] any? ] - [ [ base >= ] any? ] - bi or ; - -:: rebase ( digits base1 base2 -- newdigits ) - { - { [ base1 2 < ] [ "input base must be >= 2" throw ] } - { [ base2 2 < ] [ "output base must be >= 2" throw ] } - { [ digits base1 invalid-digits? ] - [ "all digits must satisfy 0 <= d < input base" throw ] - } - [ digits base1 from-digits base2 to-digits ] - } cond ; - diff --git a/exercises/practice/all-your-base/.meta/config.json b/exercises/practice/all-your-base/.meta/config.json index 981ce63f..f0528cc9 100644 --- a/exercises/practice/all-your-base/.meta/config.json +++ b/exercises/practice/all-your-base/.meta/config.json @@ -4,13 +4,13 @@ ], "files": { "solution": [ - "all-your-base.factor" + "all-your-base/all-your-base.factor" ], "test": [ - "all-your-base-tests.factor" + "all-your-base/all-your-base-tests.factor" ], "example": [ - ".meta/all-your-base-example.factor" + ".meta/example.factor" ] }, "blurb": "Convert a number, represented as a sequence of digits in one base, to any other base." diff --git a/exercises/practice/all-your-base/.meta/example.factor b/exercises/practice/all-your-base/.meta/example.factor new file mode 100644 index 00000000..fce18b99 --- /dev/null +++ b/exercises/practice/all-your-base/.meta/example.factor @@ -0,0 +1,23 @@ +USING: kernel locals math sequences ; +IN: all-your-base + +:: digits>integer ( digits base -- n ) + 0 digits [ [ base * ] dip + ] each ; + +:: integer>digits ( n base -- digits ) + n [ dup 0 > ] [ base /mod ] produce nip reverse + dup empty? [ drop { 0 } ] when ; + +:: validate-base ( base msg -- ) + base 2 < [ msg throw ] when ; + +:: validate-digits ( digits base -- ) + digits [ dup 0 < swap base >= or ] any? + [ "all digits must satisfy 0 <= d < input base" throw ] when ; + +:: rebase ( digits input-base output-base -- digits' ) + input-base "input base must be >= 2" validate-base + output-base "output base must be >= 2" validate-base + digits input-base validate-digits + digits input-base digits>integer + output-base integer>digits ; diff --git a/exercises/practice/all-your-base/.meta/generator.jl b/exercises/practice/all-your-base/.meta/generator.jl index c2cc7085..15579300 100644 --- a/exercises/practice/all-your-base/.meta/generator.jl +++ b/exercises/practice/all-your-base/.meta/generator.jl @@ -1,8 +1,5 @@ module AllYourBase -const HEADER = "USING: all-your-base tools.test ;" - - function gen_test_case(case) digits = format_int_array(case["input"]["digits"]) ib = case["input"]["inputBase"] @@ -10,10 +7,10 @@ function gen_test_case(case) expected = case["expected"] if expected isa Dict msg = expected["error"] - return """[ $(digits) $(ib) $(ob) rebase ] [ "$(msg)" = ] must-fail-with""" + return """[ $(digits) $(ib) $(ob) rebase ]\n[ "$(msg)" = ] must-fail-with""" else exp_str = format_int_array(expected) - return "{ $(exp_str) } [ $(digits) $(ib) $(ob) rebase ] unit-test" + return "{ $(exp_str) }\n[ $(digits) $(ib) $(ob) rebase ] unit-test" end end diff --git a/exercises/practice/all-your-base/all-your-base-tests.factor b/exercises/practice/all-your-base/all-your-base-tests.factor deleted file mode 100644 index 6db3365d..00000000 --- a/exercises/practice/all-your-base/all-your-base-tests.factor +++ /dev/null @@ -1,72 +0,0 @@ -USING: all-your-base io kernel math sequences tools.test unicode ; -IN: all-your-base.tests - -"All Your Base:" print - -"single bit one to decimal" print -{ { 1 } } [ { 1 } 2 10 rebase ] unit-test - -"binary to single decimal" print -{ { 5 } } [ { 1 0 1 } 2 10 rebase ] unit-test - -"single decimal to binary" print -{ { 1 0 1 } } [ { 5 } 10 2 rebase ] unit-test - -"binary to multiple decimal" print -{ { 4 2 } } [ { 1 0 1 0 1 0 } 2 10 rebase ] unit-test - -"decimal to binary" print -{ { 1 0 1 0 1 0 } } [ { 4 2 } 10 2 rebase ] unit-test - -"trinary to hexadecimal" print -{ { 2 10 } } [ { 1 1 2 0 } 3 16 rebase ] unit-test - -"hexadecimal to trinary" print -{ { 1 1 2 0 } } [ { 2 10 } 16 3 rebase ] unit-test - -"15-bit integer" print -{ { 6 10 45 } } [ { 3 46 60 } 97 73 rebase ] unit-test - -"empty list" print -{ { 0 } } [ { } 2 10 rebase ] unit-test - -"single zero" print -{ { 0 } } [ { 0 } 10 2 rebase ] unit-test - -"multiple zeros" print -{ { 0 } } [ { 0 0 0 } 10 2 rebase ] unit-test - -"leading zeros" print -{ { 4 2 } } [ { 0 6 0 } 7 10 rebase ] unit-test - -"input base is one" print -[ { 0 } 1 10 rebase ] [ "input base must be >= 2" = ] must-fail-with - -"input base is zero" print -[ { } 0 10 rebase ] [ "input base must be >= 2" = ] must-fail-with - -"input base is negative" print -[ { 1 } -2 10 rebase ] [ "input base must be >= 2" = ] must-fail-with - -"negative digit" print -[ { 1 -1 1 0 1 0 } 2 10 rebase ] [ - "all digits must satisfy 0 <= d < input base" = -] must-fail-with - -"invalid positive digit" print -[ { 1 2 1 0 1 0 } 2 10 rebase ] [ - "all digits must satisfy 0 <= d < input base" = -] must-fail-with - -"output base is one" print -[ { 1 0 1 0 1 0 } 2 1 rebase ] [ "output base must be >= 2" = ] must-fail-with - -"output base is zero" print -[ { 7 } 10 0 rebase ] [ "output base must be >= 2" = ] must-fail-with - -"output base is negative" print -[ { 1 } 2 -7 rebase ] [ "output base must be >= 2" = ] must-fail-with - -"both bases are negative" print -[ { 1 } -2 -7 rebase ] [ "input base must be >= 2" = ] must-fail-with - diff --git a/exercises/practice/all-your-base/all-your-base/all-your-base-tests.factor b/exercises/practice/all-your-base/all-your-base/all-your-base-tests.factor new file mode 100644 index 00000000..7e1e07fa --- /dev/null +++ b/exercises/practice/all-your-base/all-your-base/all-your-base-tests.factor @@ -0,0 +1,92 @@ +USING: all-your-base io kernel lexer tools.test unicode ; +IN: all-your-base.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"All Your Base:" print + +"single bit one to decimal" print +{ { 1 } } +[ { 1 } 2 10 rebase ] unit-test + +STOP-HERE + +"binary to single decimal" print +{ { 5 } } +[ { 1 0 1 } 2 10 rebase ] unit-test + +"single decimal to binary" print +{ { 1 0 1 } } +[ { 5 } 10 2 rebase ] unit-test + +"binary to multiple decimal" print +{ { 4 2 } } +[ { 1 0 1 0 1 0 } 2 10 rebase ] unit-test + +"decimal to binary" print +{ { 1 0 1 0 1 0 } } +[ { 4 2 } 10 2 rebase ] unit-test + +"trinary to hexadecimal" print +{ { 2 10 } } +[ { 1 1 2 0 } 3 16 rebase ] unit-test + +"hexadecimal to trinary" print +{ { 1 1 2 0 } } +[ { 2 10 } 16 3 rebase ] unit-test + +"15-bit integer" print +{ { 6 10 45 } } +[ { 3 46 60 } 97 73 rebase ] unit-test + +"empty list" print +{ { 0 } } +[ { } 2 10 rebase ] unit-test + +"single zero" print +{ { 0 } } +[ { 0 } 10 2 rebase ] unit-test + +"multiple zeros" print +{ { 0 } } +[ { 0 0 0 } 10 2 rebase ] unit-test + +"leading zeros" print +{ { 4 2 } } +[ { 0 6 0 } 7 10 rebase ] unit-test + +"input base is one" print +[ { 0 } 1 10 rebase ] +[ "input base must be >= 2" = ] must-fail-with + +"input base is zero" print +[ { } 0 10 rebase ] +[ "input base must be >= 2" = ] must-fail-with + +"input base is negative" print +[ { 1 } -2 10 rebase ] +[ "input base must be >= 2" = ] must-fail-with + +"negative digit" print +[ { 1 -1 1 0 1 0 } 2 10 rebase ] +[ "all digits must satisfy 0 <= d < input base" = ] must-fail-with + +"invalid positive digit" print +[ { 1 2 1 0 1 0 } 2 10 rebase ] +[ "all digits must satisfy 0 <= d < input base" = ] must-fail-with + +"output base is one" print +[ { 1 0 1 0 1 0 } 2 1 rebase ] +[ "output base must be >= 2" = ] must-fail-with + +"output base is zero" print +[ { 7 } 10 0 rebase ] +[ "output base must be >= 2" = ] must-fail-with + +"output base is negative" print +[ { 1 } 2 -7 rebase ] +[ "output base must be >= 2" = ] must-fail-with + +"both bases are negative" print +[ { 1 } -2 -7 rebase ] +[ "input base must be >= 2" = ] must-fail-with diff --git a/exercises/practice/all-your-base/all-your-base.factor b/exercises/practice/all-your-base/all-your-base/all-your-base.factor similarity index 52% rename from exercises/practice/all-your-base/all-your-base.factor rename to exercises/practice/all-your-base/all-your-base/all-your-base.factor index c6b5e363..38fb2c81 100644 --- a/exercises/practice/all-your-base/all-your-base.factor +++ b/exercises/practice/all-your-base/all-your-base/all-your-base.factor @@ -1,6 +1,5 @@ USING: kernel ; IN: all-your-base -: rebase ( seq n m -- newseq ) +:: rebase ( digits input-base output-base -- digits' ) "unimplemented" throw ; - diff --git a/exercises/practice/armstrong-numbers/.meta/config.json b/exercises/practice/armstrong-numbers/.meta/config.json index 25e12b0c..f79dd449 100644 --- a/exercises/practice/armstrong-numbers/.meta/config.json +++ b/exercises/practice/armstrong-numbers/.meta/config.json @@ -4,13 +4,13 @@ ], "files": { "solution": [ - "armstrong-numbers.factor" + "armstrong-numbers/armstrong-numbers.factor" ], "test": [ - "armstrong-numbers-tests.factor" + "armstrong-numbers/armstrong-numbers-tests.factor" ], "example": [ - ".meta/armstrong-numbers-example.factor" + ".meta/example.factor" ] }, "blurb": "Determine if a number is an Armstrong number.", diff --git a/exercises/practice/armstrong-numbers/.meta/armstrong-numbers-example.factor b/exercises/practice/armstrong-numbers/.meta/example.factor similarity index 94% rename from exercises/practice/armstrong-numbers/.meta/armstrong-numbers-example.factor rename to exercises/practice/armstrong-numbers/.meta/example.factor index 3c6932e0..dc3b5b6b 100644 --- a/exercises/practice/armstrong-numbers/.meta/armstrong-numbers-example.factor +++ b/exercises/practice/armstrong-numbers/.meta/example.factor @@ -20,6 +20,5 @@ IN: armstrong digs [ len ^ ] map sum ; -: armstrong? ( x -- ? ) +: armstrong? ( n -- ? ) [ ] [ powsum ] bi = ; - diff --git a/exercises/practice/armstrong-numbers/.meta/generator.jl b/exercises/practice/armstrong-numbers/.meta/generator.jl index 7ec2de33..ae3c5140 100644 --- a/exercises/practice/armstrong-numbers/.meta/generator.jl +++ b/exercises/practice/armstrong-numbers/.meta/generator.jl @@ -1,11 +1,9 @@ module ArmstrongNumbers -const HEADER = "USING: armstrong-numbers tools.test ;" - function gen_test_case(case) number = case["input"]["number"] expected = case["expected"] ? "t" : "f" - return "{ $(expected) } [ $(Int(number)) armstrong-number? ] unit-test" + return "{ $(expected) } [ $(Int(number)) armstrong? ] unit-test" end end diff --git a/exercises/practice/armstrong-numbers/armstrong-numbers-tests.factor b/exercises/practice/armstrong-numbers/armstrong-numbers-tests.factor deleted file mode 100644 index 6b798e23..00000000 --- a/exercises/practice/armstrong-numbers/armstrong-numbers-tests.factor +++ /dev/null @@ -1,32 +0,0 @@ -USING: armstrong io kernel math sequences tools.test unicode ; -IN: armstrong.tests - -"Armstrong numbers" print - -"Zero is an Armstrong number" print -{ t } [ 0 armstrong? ] unit-test - -"Single digit numbers are Armstrong numbers" print -{ t } [ 5 armstrong? ] unit-test - -"There are no 2 digit Armstrong numbers" print -{ f } [ 10 armstrong? ] unit-test - -"Three digit number that is an Armstrong number" print -{ t } [ 153 armstrong? ] unit-test - -"Three digit number that is not an Armstrong number" print -{ f } [ 100 armstrong? ] unit-test - -"Four digit number that is an Armstrong number" print -{ t } [ 9474 armstrong? ] unit-test - -"Four digit number that is not an Armstrong number" print -{ f } [ 9475 armstrong? ] unit-test - -"Seven digit number that is an Armstrong number" print -{ t } [ 9926315 armstrong? ] unit-test - -"Seven digit number that is not an Armstrong number" print -{ f } [ 9926314 armstrong? ] unit-test - diff --git a/exercises/practice/armstrong-numbers/armstrong-numbers/armstrong-numbers-tests.factor b/exercises/practice/armstrong-numbers/armstrong-numbers/armstrong-numbers-tests.factor new file mode 100644 index 00000000..de7c8893 --- /dev/null +++ b/exercises/practice/armstrong-numbers/armstrong-numbers/armstrong-numbers-tests.factor @@ -0,0 +1,35 @@ +USING: armstrong-numbers io kernel lexer tools.test unicode ; +IN: armstrong-numbers.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Armstrong Numbers:" print + +"Zero is an Armstrong number" print +{ t } [ 0 armstrong? ] unit-test + +STOP-HERE + +"Single-digit numbers are Armstrong numbers" print +{ t } [ 5 armstrong? ] unit-test + +"There are no two-digit Armstrong numbers" print +{ f } [ 10 armstrong? ] unit-test + +"Three-digit number that is an Armstrong number" print +{ t } [ 153 armstrong? ] unit-test + +"Three-digit number that is not an Armstrong number" print +{ f } [ 100 armstrong? ] unit-test + +"Four-digit number that is an Armstrong number" print +{ t } [ 9474 armstrong? ] unit-test + +"Four-digit number that is not an Armstrong number" print +{ f } [ 9475 armstrong? ] unit-test + +"Seven-digit number that is an Armstrong number" print +{ t } [ 9926315 armstrong? ] unit-test + +"Seven-digit number that is not an Armstrong number" print +{ f } [ 9926314 armstrong? ] unit-test diff --git a/exercises/practice/armstrong-numbers/armstrong-numbers.factor b/exercises/practice/armstrong-numbers/armstrong-numbers/armstrong-numbers.factor similarity index 69% rename from exercises/practice/armstrong-numbers/armstrong-numbers.factor rename to exercises/practice/armstrong-numbers/armstrong-numbers/armstrong-numbers.factor index ceaf0bb1..1597cf3b 100644 --- a/exercises/practice/armstrong-numbers/armstrong-numbers.factor +++ b/exercises/practice/armstrong-numbers/armstrong-numbers/armstrong-numbers.factor @@ -1,6 +1,5 @@ USING: kernel ; IN: armstrong -: armstrong? ( x -- ? ) +: armstrong? ( n -- ? ) "unimplemented" throw ; - diff --git a/exercises/practice/hello-world/.meta/config.json b/exercises/practice/hello-world/.meta/config.json index dd5da699..4259604d 100644 --- a/exercises/practice/hello-world/.meta/config.json +++ b/exercises/practice/hello-world/.meta/config.json @@ -8,13 +8,13 @@ ], "files": { "solution": [ - "hello-world.factor" + "hello-world/hello-world.factor" ], "test": [ - "hello-world-tests.factor" + "hello-world/hello-world-tests.factor" ], "example": [ - ".meta/hello-world-example.factor" + ".meta/example.factor" ] }, "blurb": "The classical introductory exercise. Just say \"Hello, World!\"", diff --git a/exercises/practice/hello-world/.meta/hello-world-example.factor b/exercises/practice/hello-world/.meta/example.factor similarity index 74% rename from exercises/practice/hello-world/.meta/hello-world-example.factor rename to exercises/practice/hello-world/.meta/example.factor index 93363337..d831165c 100644 --- a/exercises/practice/hello-world/.meta/hello-world-example.factor +++ b/exercises/practice/hello-world/.meta/example.factor @@ -1,5 +1,5 @@ USING: formatting kernel sequences ; IN: hello-world -: say-hello ( -- string ) +: say-hello ( -- str ) "Hello, World!" ; diff --git a/exercises/practice/hello-world/.meta/generator.jl b/exercises/practice/hello-world/.meta/generator.jl index 50341239..5213d406 100644 --- a/exercises/practice/hello-world/.meta/generator.jl +++ b/exercises/practice/hello-world/.meta/generator.jl @@ -1,10 +1,8 @@ module HelloWorld -const HEADER = "USING: hello-world tools.test ;" - function gen_test_case(case) expected = case["expected"] - return """{ "$(expected)" } [ hello ] unit-test""" + return """{ "$(expected)" } [ say-hello ] unit-test""" end end diff --git a/exercises/practice/hello-world/hello-world-tests.factor b/exercises/practice/hello-world/hello-world-tests.factor deleted file mode 100644 index b15a6622..00000000 --- a/exercises/practice/hello-world/hello-world-tests.factor +++ /dev/null @@ -1,4 +0,0 @@ -USING: hello-world tools.test ; -IN: hello-world.tests - -{ "Hello, World!" } [ say-hello ] unit-test \ No newline at end of file diff --git a/exercises/practice/hello-world/hello-world/hello-world-tests.factor b/exercises/practice/hello-world/hello-world/hello-world-tests.factor new file mode 100644 index 00000000..7cd98dc3 --- /dev/null +++ b/exercises/practice/hello-world/hello-world/hello-world-tests.factor @@ -0,0 +1,8 @@ +USING: hello-world io kernel lexer tools.test unicode ; +IN: hello-world.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +{ "Hello, World!" } [ say-hello ] unit-test + +STOP-HERE diff --git a/exercises/practice/hello-world/hello-world.factor b/exercises/practice/hello-world/hello-world/hello-world.factor similarity index 74% rename from exercises/practice/hello-world/hello-world.factor rename to exercises/practice/hello-world/hello-world/hello-world.factor index c3cf2599..b0722629 100644 --- a/exercises/practice/hello-world/hello-world.factor +++ b/exercises/practice/hello-world/hello-world/hello-world.factor @@ -1,5 +1,5 @@ USING: formatting kernel sequences ; IN: hello-world -: say-hello ( -- string ) +: say-hello ( -- str ) "Goodbye, Mars!" ; diff --git a/exercises/practice/isogram/.meta/config.json b/exercises/practice/isogram/.meta/config.json index 81767b30..84864094 100644 --- a/exercises/practice/isogram/.meta/config.json +++ b/exercises/practice/isogram/.meta/config.json @@ -5,13 +5,13 @@ ], "files": { "solution": [ - "isogram.factor" + "isogram/isogram.factor" ], "test": [ - "isogram-tests.factor" + "isogram/isogram-tests.factor" ], "example": [ - ".meta/isogram-example.factor" + ".meta/example.factor" ] }, "blurb": "Determine if a word or phrase is an isogram.", diff --git a/exercises/practice/isogram/.meta/example.factor b/exercises/practice/isogram/.meta/example.factor new file mode 100644 index 00000000..4fae1ac9 --- /dev/null +++ b/exercises/practice/isogram/.meta/example.factor @@ -0,0 +1,5 @@ +USING: kernel sequences sets unicode ; +IN: isogram + +: isogram? ( phrase -- ? ) + >lower [ Letter? ] filter all-unique? ; diff --git a/exercises/practice/isogram/.meta/generator.jl b/exercises/practice/isogram/.meta/generator.jl index 3f9c9df2..25792cb1 100644 --- a/exercises/practice/isogram/.meta/generator.jl +++ b/exercises/practice/isogram/.meta/generator.jl @@ -1,7 +1,5 @@ module Isogram -const HEADER = "USING: isogram tools.test ;" - function gen_test_case(case) phrase = case["input"]["phrase"] expected = case["expected"] ? "t" : "f" diff --git a/exercises/practice/isogram/.meta/isogram-example.factor b/exercises/practice/isogram/.meta/isogram-example.factor deleted file mode 100644 index be80ffaf..00000000 --- a/exercises/practice/isogram/.meta/isogram-example.factor +++ /dev/null @@ -1,5 +0,0 @@ -USING: kernel sets ; -IN: isogram - -: isogram? ( str -- ? ) - dup "" set-like = ; diff --git a/exercises/practice/isogram/isogram-tests.factor b/exercises/practice/isogram/isogram/isogram-tests.factor similarity index 56% rename from exercises/practice/isogram/isogram-tests.factor rename to exercises/practice/isogram/isogram/isogram-tests.factor index e284c460..c7bff408 100644 --- a/exercises/practice/isogram/isogram-tests.factor +++ b/exercises/practice/isogram/isogram/isogram-tests.factor @@ -1,28 +1,41 @@ -USING: io isogram tools.test ; +USING: io isogram kernel lexer tools.test unicode ; IN: isogram.tests +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + "Isogram:" print "empty string" print { t } [ "" isogram? ] unit-test +STOP-HERE + "isogram with only lower case characters" print { t } [ "isogram" isogram? ] unit-test "word with one duplicated character" print { f } [ "eleven" isogram? ] unit-test +"word with one duplicated character from the end of the alphabet" print +{ f } [ "zzyzx" isogram? ] unit-test + "longest reported english isogram" print { t } [ "subdermatoglyphic" isogram? ] unit-test "word with duplicated character in mixed case" print { f } [ "Alphabet" isogram? ] unit-test +"word with duplicated character in mixed case, lowercase first" print +{ f } [ "alphAbet" isogram? ] unit-test + "hypothetical isogrammic word with hyphen" print { t } [ "thumbscrew-japingly" isogram? ] unit-test -"isogram with duplicated non letter character" print -{ t } [ "Hjelmqvist-Gryb-Zock-Pfund-Wax" isogram? ] unit-test +"hypothetical word with duplicated character following hyphen" print +{ f } [ "thumbscrew-jappingly" isogram? ] unit-test + +"isogram with duplicated hyphen" print +{ t } [ "six-year-old" isogram? ] unit-test "made-up name that is an isogram" print { t } [ "Emily Jung Schwartzkopf" isogram? ] unit-test @@ -30,6 +43,8 @@ IN: isogram.tests "duplicated character in the middle" print { f } [ "accentor" isogram? ] unit-test +"same first and last characters" print +{ f } [ "angola" isogram? ] unit-test + "word with duplicated character and with two hyphens" print { f } [ "up-to-date" isogram? ] unit-test - diff --git a/exercises/practice/isogram/isogram.factor b/exercises/practice/isogram/isogram/isogram.factor similarity index 67% rename from exercises/practice/isogram/isogram.factor rename to exercises/practice/isogram/isogram/isogram.factor index 6e8e5f5f..339d0820 100644 --- a/exercises/practice/isogram/isogram.factor +++ b/exercises/practice/isogram/isogram/isogram.factor @@ -1,6 +1,5 @@ USING: kernel ; IN: isogram -: isogram? ( str -- ? ) +: isogram? ( phrase -- ? ) "unimplemented" throw ; - diff --git a/exercises/practice/leap/.meta/config.json b/exercises/practice/leap/.meta/config.json index b7bf8bec..5a4988f6 100644 --- a/exercises/practice/leap/.meta/config.json +++ b/exercises/practice/leap/.meta/config.json @@ -7,13 +7,13 @@ ], "files": { "solution": [ - "leap.factor" + "leap/leap.factor" ], "test": [ - "leap-tests.factor" + "leap/leap-tests.factor" ], "example": [ - ".meta/leap-example.factor" + ".meta/example.factor" ] }, "blurb": "Determine whether a given year is a leap year.", diff --git a/exercises/practice/leap/.meta/leap-example.factor b/exercises/practice/leap/.meta/example.factor similarity index 78% rename from exercises/practice/leap/.meta/leap-example.factor rename to exercises/practice/leap/.meta/example.factor index b9578b2e..6d2361e1 100644 --- a/exercises/practice/leap/.meta/leap-example.factor +++ b/exercises/practice/leap/.meta/example.factor @@ -1,7 +1,7 @@ USING: formatting kernel math ; IN: leap -: my-leap-year? ( year -- leap-year? ) +: leap-year? ( year -- ? ) dup odd? [ drop f ] [ [ 4 mod zero? ] [ 100 mod 0 > ] [ 400 mod zero? ] tri or and ] if ; diff --git a/exercises/practice/leap/.meta/generator.jl b/exercises/practice/leap/.meta/generator.jl index 39aa6aa0..7f04375e 100644 --- a/exercises/practice/leap/.meta/generator.jl +++ b/exercises/practice/leap/.meta/generator.jl @@ -1,7 +1,5 @@ module Leap -const HEADER = "USING: leap tools.test ;" - function gen_test_case(case) year = case["input"]["year"] expected = case["expected"] ? "t" : "f" diff --git a/exercises/practice/leap/leap-tests.factor b/exercises/practice/leap/leap-tests.factor deleted file mode 100644 index 2f1d4f7d..00000000 --- a/exercises/practice/leap/leap-tests.factor +++ /dev/null @@ -1,32 +0,0 @@ -USING: io leap tools.test ; -IN: leap.tests - -"Leap year:" print - -"not very common" print -{ f } [ 2015 my-leap-year? ] unit-test - -"introduced every 4 years to adjust about a day" print -{ t } [ 2016 my-leap-year? ] unit-test - -"skipped every 100 years to remove an extra day" print -{ f } [ 1900 my-leap-year? ] unit-test - -"reintroduced every 400 years to adjust yet another day" print -{ t } [ 2000 my-leap-year? ] unit-test - - - -"Additional corner cases of leap years:" print - -"not a leap year" print -{ f } [ 1978 my-leap-year? ] unit-test - -"a common leap year" print -{ t } [ 1992 my-leap-year? ] unit-test - -"is skipped every 100 years" print -{ f } [ 2100 my-leap-year? ] unit-test - -"reintroduced every 400 years" print -{ f } [ 2100 my-leap-year? ] unit-test diff --git a/exercises/practice/leap/leap/leap-tests.factor b/exercises/practice/leap/leap/leap-tests.factor new file mode 100644 index 00000000..8b8089fd --- /dev/null +++ b/exercises/practice/leap/leap/leap-tests.factor @@ -0,0 +1,35 @@ +USING: io kernel leap lexer tools.test unicode ; +IN: leap.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Leap:" print + +"year not divisible by 4 in common year" print +{ f } [ 2015 leap-year? ] unit-test + +STOP-HERE + +"year divisible by 2, not divisible by 4 in common year" print +{ f } [ 1970 leap-year? ] unit-test + +"year divisible by 4, not divisible by 100 in leap year" print +{ t } [ 1996 leap-year? ] unit-test + +"year divisible by 4 and 5 is still a leap year" print +{ t } [ 1960 leap-year? ] unit-test + +"year divisible by 100, not divisible by 400 in common year" print +{ f } [ 2100 leap-year? ] unit-test + +"year divisible by 100 but not by 3 is still not a leap year" print +{ f } [ 1900 leap-year? ] unit-test + +"year divisible by 400 is leap year" print +{ t } [ 2000 leap-year? ] unit-test + +"year divisible by 400 but not by 125 is still a leap year" print +{ t } [ 2400 leap-year? ] unit-test + +"year divisible by 200, not divisible by 400 in common year" print +{ f } [ 1800 leap-year? ] unit-test diff --git a/exercises/practice/leap/leap.factor b/exercises/practice/leap/leap/leap.factor similarity index 56% rename from exercises/practice/leap/leap.factor rename to exercises/practice/leap/leap/leap.factor index c01483f9..6f52c2e9 100644 --- a/exercises/practice/leap/leap.factor +++ b/exercises/practice/leap/leap/leap.factor @@ -1,6 +1,5 @@ USING: kernel ; IN: leap -: my-leap-year? ( year -- leap-year? ) +: leap-year? ( year -- ? ) "unimplemented" throw ; - diff --git a/exercises/practice/raindrops/.meta/config.json b/exercises/practice/raindrops/.meta/config.json index a4f82a9b..68249a39 100644 --- a/exercises/practice/raindrops/.meta/config.json +++ b/exercises/practice/raindrops/.meta/config.json @@ -4,13 +4,13 @@ ], "files": { "solution": [ - "raindrops.factor" + "raindrops/raindrops.factor" ], "test": [ - "raindrops-tests.factor" + "raindrops/raindrops-tests.factor" ], "example": [ - ".meta/raindrops-example.factor" + ".meta/example.factor" ] }, "blurb": "Convert a number to a string, the content of which depends on the number's factors.", diff --git a/exercises/practice/raindrops/.meta/raindrops-example.factor b/exercises/practice/raindrops/.meta/example.factor similarity index 90% rename from exercises/practice/raindrops/.meta/raindrops-example.factor rename to exercises/practice/raindrops/.meta/example.factor index bfcfeb8c..0b9d303f 100644 --- a/exercises/practice/raindrops/.meta/raindrops-example.factor +++ b/exercises/practice/raindrops/.meta/example.factor @@ -1,11 +1,10 @@ USING: combinators kernel math.functions math.parser sequences ; IN: raindrops -: raindrops ( n -- str ) +: convert ( n -- str ) dup [ 3 divisor? "Pling" and ] [ 5 divisor? "Plang" and ] [ 7 divisor? "Plong" and ] tri { } 3sequence sift [ number>string ] [ concat swap drop ] if-empty ; - diff --git a/exercises/practice/raindrops/.meta/generator.jl b/exercises/practice/raindrops/.meta/generator.jl index dbbbf4a6..ee17af04 100644 --- a/exercises/practice/raindrops/.meta/generator.jl +++ b/exercises/practice/raindrops/.meta/generator.jl @@ -1,7 +1,5 @@ module Raindrops -const HEADER = "USING: raindrops tools.test ;" - function gen_test_case(case) number = case["input"]["number"] expected = case["expected"] diff --git a/exercises/practice/raindrops/raindrops-tests.factor b/exercises/practice/raindrops/raindrops/raindrops-tests.factor similarity index 53% rename from exercises/practice/raindrops/raindrops-tests.factor rename to exercises/practice/raindrops/raindrops/raindrops-tests.factor index 96faa55e..e991d360 100644 --- a/exercises/practice/raindrops/raindrops-tests.factor +++ b/exercises/practice/raindrops/raindrops/raindrops-tests.factor @@ -1,59 +1,62 @@ -USING: io kernel raindrops tools.test ; -IN: raindrops.test +USING: io kernel lexer raindrops tools.test unicode ; +IN: raindrops.tests -"Raindrops" print +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Raindrops:" print "the sound for 1 is 1" print -{ "1" } [ 1 raindrops ] unit-test +{ "1" } [ 1 convert ] unit-test + +STOP-HERE "the sound for 3 is Pling" print -{ "Pling" } [ 3 raindrops ] unit-test +{ "Pling" } [ 3 convert ] unit-test "the sound for 5 is Plang" print -{ "Plang" } [ 5 raindrops ] unit-test +{ "Plang" } [ 5 convert ] unit-test "the sound for 7 is Plong" print -{ "Plong" } [ 7 raindrops ] unit-test +{ "Plong" } [ 7 convert ] unit-test "the sound for 6 is Pling as it has a factor 3" print -{ "Pling" } [ 6 raindrops ] unit-test +{ "Pling" } [ 6 convert ] unit-test "2 to the power 3 does not make a raindrop sound as 3 is the exponent not the base" print -{ "8" } [ 8 raindrops ] unit-test +{ "8" } [ 8 convert ] unit-test "the sound for 9 is Pling as it has a factor 3" print -{ "Pling" } [ 9 raindrops ] unit-test +{ "Pling" } [ 9 convert ] unit-test "the sound for 10 is Plang as it has a factor 5" print -{ "Plang" } [ 10 raindrops ] unit-test +{ "Plang" } [ 10 convert ] unit-test "the sound for 14 is Plong as it has a factor of 7" print -{ "Plong" } [ 14 raindrops ] unit-test +{ "Plong" } [ 14 convert ] unit-test "the sound for 15 is PlingPlang as it has factors 3 and 5" print -{ "PlingPlang" } [ 15 raindrops ] unit-test +{ "PlingPlang" } [ 15 convert ] unit-test "the sound for 21 is PlingPlong as it has factors 3 and 7" print -{ "PlingPlong" } [ 21 raindrops ] unit-test +{ "PlingPlong" } [ 21 convert ] unit-test "the sound for 25 is Plang as it has a factor 5" print -{ "Plang" } [ 25 raindrops ] unit-test +{ "Plang" } [ 25 convert ] unit-test "the sound for 27 is Pling as it has a factor 3" print -{ "Pling" } [ 27 raindrops ] unit-test +{ "Pling" } [ 27 convert ] unit-test "the sound for 35 is PlangPlong as it has factors 5 and 7" print -{ "PlangPlong" } [ 35 raindrops ] unit-test +{ "PlangPlong" } [ 35 convert ] unit-test "the sound for 49 is Plong as it has a factor 7" print -{ "Plong" } [ 49 raindrops ] unit-test +{ "Plong" } [ 49 convert ] unit-test "the sound for 52 is 52" print -{ "52" } [ 52 raindrops ] unit-test +{ "52" } [ 52 convert ] unit-test "the sound for 105 is PlingPlangPlong as it has factors 3, 5 and 7" print -{ "PlingPlangPlong" } [ 105 raindrops ] unit-test +{ "PlingPlangPlong" } [ 105 convert ] unit-test "the sound for 3125 is Plang as it has a factor 5" print -{ "Plang" } [ 3125 raindrops ] unit-test - +{ "Plang" } [ 3125 convert ] unit-test diff --git a/exercises/practice/raindrops/raindrops.factor b/exercises/practice/raindrops/raindrops/raindrops.factor similarity index 68% rename from exercises/practice/raindrops/raindrops.factor rename to exercises/practice/raindrops/raindrops/raindrops.factor index 50a9e9a3..a4a1074e 100644 --- a/exercises/practice/raindrops/raindrops.factor +++ b/exercises/practice/raindrops/raindrops/raindrops.factor @@ -1,6 +1,5 @@ USING: kernel ; IN: raindrops -: raindrops ( n -- str ) +: convert ( n -- str ) "unimplemented" throw ; - diff --git a/exercises/practice/resistor-color/.meta/config.json b/exercises/practice/resistor-color/.meta/config.json index 033179c6..bf0a7668 100644 --- a/exercises/practice/resistor-color/.meta/config.json +++ b/exercises/practice/resistor-color/.meta/config.json @@ -4,13 +4,13 @@ ], "files": { "solution": [ - "resistor-color.factor" + "resistor-color/resistor-color.factor" ], "test": [ - "resistor-color-tests.factor" + "resistor-color/resistor-color-tests.factor" ], "example": [ - ".meta/resistor-color-example.factor" + ".meta/example.factor" ] }, "blurb": "Convert a resistor band's color to its numeric representation.", diff --git a/exercises/practice/resistor-color/.meta/resistor-color-example.factor b/exercises/practice/resistor-color/.meta/example.factor similarity index 85% rename from exercises/practice/resistor-color/.meta/resistor-color-example.factor rename to exercises/practice/resistor-color/.meta/example.factor index 32704e48..93f6dc81 100644 --- a/exercises/practice/resistor-color/.meta/resistor-color-example.factor +++ b/exercises/practice/resistor-color/.meta/example.factor @@ -3,6 +3,5 @@ IN: resistor-color CONSTANT: colors { "black" "brown" "red" "orange" "yellow" "green" "blue" "violet" "grey" "white" } -: color>code ( x -- str ) +: color>code ( color -- n ) colors index ; - diff --git a/exercises/practice/resistor-color/.meta/generator.jl b/exercises/practice/resistor-color/.meta/generator.jl index f702e443..94806f79 100644 --- a/exercises/practice/resistor-color/.meta/generator.jl +++ b/exercises/practice/resistor-color/.meta/generator.jl @@ -1,16 +1,14 @@ module ResistorColor -const HEADER = "USING: resistor-color tools.test ;" - function gen_test_case(case) prop = case["property"] if prop == "colorCode" color = case["input"]["color"] expected = Int(case["expected"]) - return """{ $(expected) } [ "$(color)" color-code ] unit-test""" + return """{ $(expected) } [ "$(color)" color>code ] unit-test""" else expected = format_string_array(case["expected"]) - return "{ $(expected) } [ colors ] unit-test" + return "{ $(expected) }\n[ colors ] unit-test" end end diff --git a/exercises/practice/resistor-color/resistor-color-tests.factor b/exercises/practice/resistor-color/resistor-color/resistor-color-tests.factor similarity index 70% rename from exercises/practice/resistor-color/resistor-color-tests.factor rename to exercises/practice/resistor-color/resistor-color/resistor-color-tests.factor index fb62f812..afc0cc89 100644 --- a/exercises/practice/resistor-color/resistor-color-tests.factor +++ b/exercises/practice/resistor-color/resistor-color/resistor-color-tests.factor @@ -1,12 +1,15 @@ -USING: io kernel resistor-color tools.test ; +USING: io kernel lexer resistor-color tools.test unicode ; IN: resistor-color.tests +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + "Resistor Color:" print -"Color codes" print "Black" print { 0 } [ "black" color>code ] unit-test +STOP-HERE + "White" print { 9 } [ "white" color>code ] unit-test @@ -16,4 +19,3 @@ IN: resistor-color.tests "Colors" print { { "black" "brown" "red" "orange" "yellow" "green" "blue" "violet" "grey" "white" } } [ colors ] unit-test - diff --git a/exercises/practice/resistor-color/resistor-color.factor b/exercises/practice/resistor-color/resistor-color/resistor-color.factor similarity index 79% rename from exercises/practice/resistor-color/resistor-color.factor rename to exercises/practice/resistor-color/resistor-color/resistor-color.factor index 4acbc8b8..afde56b1 100644 --- a/exercises/practice/resistor-color/resistor-color.factor +++ b/exercises/practice/resistor-color/resistor-color/resistor-color.factor @@ -4,6 +4,5 @@ IN: resistor-color : colors ( -- seq ) "unimplemented" throw ; -: color>code ( x -- str ) +: color>code ( color -- n ) "unimplemented" throw ; - diff --git a/exercises/practice/reverse-string/.meta/config.json b/exercises/practice/reverse-string/.meta/config.json index c16dac18..5d7b4fe0 100644 --- a/exercises/practice/reverse-string/.meta/config.json +++ b/exercises/practice/reverse-string/.meta/config.json @@ -4,13 +4,13 @@ ], "files": { "solution": [ - "reverse-string.factor" + "reverse-string/reverse-string.factor" ], "test": [ - "reverse-string-tests.factor" + "reverse-string/reverse-string-tests.factor" ], "example": [ - ".meta/reverse-string-example.factor" + ".meta/example.factor" ] }, "blurb": "Reverse a given string.", diff --git a/exercises/practice/reverse-string/.meta/reverse-string-example.factor b/exercises/practice/reverse-string/.meta/example.factor similarity index 72% rename from exercises/practice/reverse-string/.meta/reverse-string-example.factor rename to exercises/practice/reverse-string/.meta/example.factor index 71a2a30b..ea8b07ca 100644 --- a/exercises/practice/reverse-string/.meta/reverse-string-example.factor +++ b/exercises/practice/reverse-string/.meta/example.factor @@ -1,6 +1,5 @@ USING: kernel sequences ; IN: reverse-string -: reverse-string ( str -- newstr ) +: reverse-string ( str -- str ) dup empty? [ "" [ prefix ] reduce ] unless ; - diff --git a/exercises/practice/reverse-string/.meta/generator.jl b/exercises/practice/reverse-string/.meta/generator.jl index c0a9acc2..e5cbb5ed 100644 --- a/exercises/practice/reverse-string/.meta/generator.jl +++ b/exercises/practice/reverse-string/.meta/generator.jl @@ -1,8 +1,5 @@ module ReverseString -const HEADER = "USING: reverse-string tools.test ;" - - function gen_test_case(case) value = escape_factor(case["input"]["value"]) expected = escape_factor(case["expected"]) diff --git a/exercises/practice/reverse-string/.meta/tests.toml b/exercises/practice/reverse-string/.meta/tests.toml index 2113a533..bd49db4c 100644 --- a/exercises/practice/reverse-string/.meta/tests.toml +++ b/exercises/practice/reverse-string/.meta/tests.toml @@ -19,3 +19,15 @@ description = "a palindrome" [b9e7dec1-c6df-40bd-9fa3-cd7ded010c4c] description = "an even-sized word" + +[1bed0f8a-13b0-4bd3-9d59-3d0593326fa2] +description = "wide characters" +include = false + +[93d7e1b8-f60f-4f3c-9559-4056e10d2ead] +description = "grapheme cluster with pre-combined form" +include = false + +[1028b2c1-6763-4459-8540-2da47ca512d9] +description = "grapheme clusters" +include = false diff --git a/exercises/practice/reverse-string/reverse-string-tests.factor b/exercises/practice/reverse-string/reverse-string/reverse-string-tests.factor similarity index 74% rename from exercises/practice/reverse-string/reverse-string-tests.factor rename to exercises/practice/reverse-string/reverse-string/reverse-string-tests.factor index 7ccfdcd4..7921a11f 100644 --- a/exercises/practice/reverse-string/reverse-string-tests.factor +++ b/exercises/practice/reverse-string/reverse-string/reverse-string-tests.factor @@ -1,11 +1,15 @@ -USING: reverse-string io kernel tools.test ; +USING: io kernel lexer reverse-string tools.test unicode ; IN: reverse-string.tests -"Reverse string:" print +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Reverse String:" print "an empty string" print { "" } [ "" reverse-string ] unit-test +STOP-HERE + "a word" print { "tobor" } [ "robot" reverse-string ] unit-test @@ -20,4 +24,3 @@ IN: reverse-string.tests "an even-sized word" print { "reward" } [ "drawer" reverse-string ] unit-test - diff --git a/exercises/practice/reverse-string/reverse-string.factor b/exercises/practice/reverse-string/reverse-string/reverse-string.factor similarity index 65% rename from exercises/practice/reverse-string/reverse-string.factor rename to exercises/practice/reverse-string/reverse-string/reverse-string.factor index b47dd2b4..0dc2929b 100644 --- a/exercises/practice/reverse-string/reverse-string.factor +++ b/exercises/practice/reverse-string/reverse-string/reverse-string.factor @@ -1,6 +1,5 @@ USING: kernel ; IN: reverse-string -: reverse ( str -- newstr ) +: reverse-string ( str -- str ) "unimplemented" throw ; - diff --git a/exercises/practice/two-fer/.meta/config.json b/exercises/practice/two-fer/.meta/config.json index 907b516d..b1548108 100644 --- a/exercises/practice/two-fer/.meta/config.json +++ b/exercises/practice/two-fer/.meta/config.json @@ -7,13 +7,13 @@ ], "files": { "solution": [ - "two-fer.factor" + "two-fer/two-fer.factor" ], "test": [ - "two-fer-tests.factor" + "two-fer/two-fer-tests.factor" ], "example": [ - ".meta/two-fer-example.factor" + ".meta/example.factor" ] }, "blurb": "Create a sentence of the form \"One for X, one for me.\".", diff --git a/exercises/practice/two-fer/.meta/two-fer-example.factor b/exercises/practice/two-fer/.meta/example.factor similarity index 100% rename from exercises/practice/two-fer/.meta/two-fer-example.factor rename to exercises/practice/two-fer/.meta/example.factor diff --git a/exercises/practice/two-fer/.meta/generator.jl b/exercises/practice/two-fer/.meta/generator.jl index c3fb4c4d..8ff24de8 100644 --- a/exercises/practice/two-fer/.meta/generator.jl +++ b/exercises/practice/two-fer/.meta/generator.jl @@ -1,14 +1,12 @@ module TwoFer -const HEADER = "USING: two-fer tools.test ;" - function gen_test_case(case) name = case["input"]["name"] expected = case["expected"] if isnothing(name) - return """{ "$(expected)" } [ f two-fer ] unit-test""" + return """{ "$(expected)" } [ f 2-for-1 ] unit-test""" else - return """{ "$(expected)" } [ "$(name)" two-fer ] unit-test""" + return """{ "$(expected)" } [ "$(name)" 2-for-1 ] unit-test""" end end diff --git a/exercises/practice/two-fer/two-fer-tests.factor b/exercises/practice/two-fer/two-fer-tests.factor deleted file mode 100644 index 4def7bc8..00000000 --- a/exercises/practice/two-fer/two-fer-tests.factor +++ /dev/null @@ -1,8 +0,0 @@ -USING: hello-world tools.test ; -IN: two-fer.tests - -! There are no variadic functions in Factor, due to the nature of the stack. -! Usually, if you need var args, use an array. Here, we'll just use an empty string. -{ "One for you, one for me." } [ "" 2-for-1 ] unit-test -{ "One for Alice, one for me." } [ "Alice" 2-for-1 ] unit-test -{ "One for Bob, one for me." } [ "Bob" 2-for-1 ] unit-test \ No newline at end of file diff --git a/exercises/practice/two-fer/two-fer/two-fer-tests.factor b/exercises/practice/two-fer/two-fer/two-fer-tests.factor new file mode 100644 index 00000000..ef4d9b0d --- /dev/null +++ b/exercises/practice/two-fer/two-fer/two-fer-tests.factor @@ -0,0 +1,17 @@ +USING: io kernel lexer tools.test two-fer unicode ; +IN: two-fer.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Two Fer:" print + +"no name given" print +{ "One for you, one for me." } [ f 2-for-1 ] unit-test + +STOP-HERE + +"a name given" print +{ "One for Alice, one for me." } [ "Alice" 2-for-1 ] unit-test + +"another name given" print +{ "One for Bob, one for me." } [ "Bob" 2-for-1 ] unit-test diff --git a/exercises/practice/two-fer/two-fer.factor b/exercises/practice/two-fer/two-fer/two-fer.factor similarity index 51% rename from exercises/practice/two-fer/two-fer.factor rename to exercises/practice/two-fer/two-fer/two-fer.factor index 39d2a19c..8263df60 100644 --- a/exercises/practice/two-fer/two-fer.factor +++ b/exercises/practice/two-fer/two-fer/two-fer.factor @@ -1,6 +1,7 @@ USING: kernel ; IN: two-fer +! There are no variadic functions in Factor, due to the nature of the stack. + : 2-for-1 ( name -- str ) "unimplemented" throw ; - diff --git a/generator/generate.jl b/generator/generate.jl index 005059e8..c1a0241b 100644 --- a/generator/generate.jl +++ b/generator/generate.jl @@ -71,7 +71,7 @@ function filter_by_toml(cases, exercise) return filter(cases) do case uuid = case["uuid"] entry = get(toml, uuid, nothing) - entry === nothing && return true + entry === nothing && return false return get(entry, "include", true) end end @@ -97,28 +97,31 @@ end const STOP_HERE_DEF = ": STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing" -function render_test_file(mod, cases) - header = mod.HEADER - if length(cases) > 1 - header = replace(header, "tools.test" => "kernel tools.test lexer") - else - header = replace(header, "tools.test" => "kernel tools.test") - end - lines = String[header] +title_of(slug) = join((uppercasefirst(w) for w in split(slug, '-')), ' ') + +function gen_header(cases, slug) + vocabs = [slug, "lexer", "kernel", "io", "tools.test", "unicode"] + sort!(vocabs) + return "USING: $(join(vocabs, " ")) ;\nIN: $(slug).tests" +end + +function render_test_file(mod, cases, slug) + lines = String[gen_header(cases, slug), ""] + push!(lines, STOP_HERE_DEF, "") if length(cases) > 1 - push!(lines, "") - push!(lines, STOP_HERE_DEF) + push!(lines, """"$(title_of(slug)):" print""", "") end - push!(lines, "") for (i, case) in enumerate(cases) + if length(cases) > 1 + push!(lines, """"$(case["description"])" print""") + end push!(lines, Base.invokelatest(mod.gen_test_case, case)) - if i == 1 && length(cases) > 1 - push!(lines, "") - push!(lines, "STOP-HERE") + if i == 1 + push!(lines, "", "STOP-HERE", "") + else push!(lines, "") end end - push!(lines, "") return join(lines, "\n") end @@ -129,7 +132,7 @@ function generate(exercise) extra = load_extra_cases(exercise) append!(cases, extra) mod = load_exercise_module(exercise) - content = render_test_file(mod, cases) + content = render_test_file(mod, cases, exercise) test_path = joinpath(TRACK_DIR, "exercises", "practice", exercise, exercise, "$(exercise)-tests.factor") mkpath(dirname(test_path)) write(test_path, content)