From bc3f5d704b67d5eb7cdfa546e33fbec0d1c0de44 Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Tue, 14 May 2024 20:39:46 +0200 Subject: [PATCH] Add `phone-number` exercise --- .../phone-number/.docs/instructions.md | 34 ++++ .../practice/phone-number/.meta/config.json | 19 ++ .../practice/phone-number/.meta/example.8th | 17 ++ .../practice/phone-number/.meta/tests.toml | 84 +++++++++ .../practice/phone-number/libs/exercism/test | 173 ++++++++++++++++++ .../practice/phone-number/phone-number.8th | 3 + exercises/practice/phone-number/test.8th | 86 +++++++++ 7 files changed, 416 insertions(+) create mode 100644 exercises/practice/phone-number/.docs/instructions.md create mode 100644 exercises/practice/phone-number/.meta/config.json create mode 100644 exercises/practice/phone-number/.meta/example.8th create mode 100644 exercises/practice/phone-number/.meta/tests.toml create mode 100644 exercises/practice/phone-number/libs/exercism/test create mode 100644 exercises/practice/phone-number/phone-number.8th create mode 100644 exercises/practice/phone-number/test.8th diff --git a/exercises/practice/phone-number/.docs/instructions.md b/exercises/practice/phone-number/.docs/instructions.md new file mode 100644 index 00000000..62ba48e9 --- /dev/null +++ b/exercises/practice/phone-number/.docs/instructions.md @@ -0,0 +1,34 @@ +# Instructions + +Clean up user-entered phone numbers so that they can be sent SMS messages. + +The **North American Numbering Plan (NANP)** is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda. +All NANP-countries share the same international country code: `1`. + +NANP numbers are ten-digit numbers consisting of a three-digit Numbering Plan Area code, commonly known as _area code_, followed by a seven-digit local number. +The first three digits of the local number represent the _exchange code_, followed by the unique four-digit number which is the _subscriber number_. + +The format is usually represented as + +```text +NXX NXX-XXXX +``` + +where `N` is any digit from 2 through 9 and `X` is any digit from 0 through 9. + +Sometimes they also have the country code (represented as `1` or `+1`) prefixed. + +Your task is to clean up differently formatted telephone numbers by removing punctuation and the country code if present. + +For example, the inputs + +- `+1 (613)-995-0253` +- `613-995-0253` +- `1 613 995 0253` +- `613.995.0253` + +should all produce the output + +`6139950253` + +**Note:** As this exercise only deals with telephone numbers used in NANP-countries, only 1 is considered a valid country code. diff --git a/exercises/practice/phone-number/.meta/config.json b/exercises/practice/phone-number/.meta/config.json new file mode 100644 index 00000000..bbd32a44 --- /dev/null +++ b/exercises/practice/phone-number/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "erikschierboom" + ], + "files": { + "solution": [ + "phone-number.8th" + ], + "test": [ + "test.8th" + ], + "example": [ + ".meta/example.8th" + ] + }, + "blurb": "Clean up user-entered phone numbers so that they can be sent SMS messages.", + "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://turing.edu" +} diff --git a/exercises/practice/phone-number/.meta/example.8th b/exercises/practice/phone-number/.meta/example.8th new file mode 100644 index 00000000..2d94b915 --- /dev/null +++ b/exercises/practice/phone-number/.meta/example.8th @@ -0,0 +1,17 @@ +: remove-punctuation \ s -- s + /[+()\-\s.]/ "" s:replace! +; + +: digits \ s -- a + null s:/ ( >n null? if nip break ;then a:push ) [] a:reduce +; + +: clean \ s -- s + remove-punctuation + digits null? if ;then + a:len 11 n:= if a:shift 1 n:= !if drop null ;then then + a:len 10 n:= !if drop null ;then + 0 a:@ 2 n:< if drop null ;then + 3 a:@ 2 n:< if drop null ;then + ' >s a:map "" a:join +; diff --git a/exercises/practice/phone-number/.meta/tests.toml b/exercises/practice/phone-number/.meta/tests.toml new file mode 100644 index 00000000..24dbf07a --- /dev/null +++ b/exercises/practice/phone-number/.meta/tests.toml @@ -0,0 +1,84 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[79666dce-e0f1-46de-95a1-563802913c35] +description = "cleans the number" + +[c360451f-549f-43e4-8aba-fdf6cb0bf83f] +description = "cleans numbers with dots" + +[08f94c34-9a37-46a2-a123-2a8e9727395d] +description = "cleans numbers with multiple spaces" + +[598d8432-0659-4019-a78b-1c6a73691d21] +description = "invalid when 9 digits" +include = false + +[2de74156-f646-42b5-8638-0ef1d8b58bc2] +description = "invalid when 9 digits" +reimplements = "598d8432-0659-4019-a78b-1c6a73691d21" + +[57061c72-07b5-431f-9766-d97da7c4399d] +description = "invalid when 11 digits does not start with a 1" + +[9962cbf3-97bb-4118-ba9b-38ff49c64430] +description = "valid when 11 digits and starting with 1" + +[fa724fbf-054c-4d91-95da-f65ab5b6dbca] +description = "valid when 11 digits and starting with 1 even with punctuation" + +[c6a5f007-895a-4fc5-90bc-a7e70f9b5cad] +description = "invalid when more than 11 digits" +include = false + +[4a1509b7-8953-4eec-981b-c483358ff531] +description = "invalid when more than 11 digits" +reimplements = "c6a5f007-895a-4fc5-90bc-a7e70f9b5cad" + +[63f38f37-53f6-4a5f-bd86-e9b404f10a60] +description = "invalid with letters" +include = false + +[eb8a1fc0-64e5-46d3-b0c6-33184208e28a] +description = "invalid with letters" +reimplements = "63f38f37-53f6-4a5f-bd86-e9b404f10a60" + +[4bd97d90-52fd-45d3-b0db-06ab95b1244e] +description = "invalid with punctuations" +include = false + +[065f6363-8394-4759-b080-e6c8c351dd1f] +description = "invalid with punctuations" +reimplements = "4bd97d90-52fd-45d3-b0db-06ab95b1244e" + +[d77d07f8-873c-4b17-8978-5f66139bf7d7] +description = "invalid if area code starts with 0" + +[c7485cfb-1e7b-4081-8e96-8cdb3b77f15e] +description = "invalid if area code starts with 1" + +[4d622293-6976-413d-b8bf-dd8a94d4e2ac] +description = "invalid if exchange code starts with 0" + +[4cef57b4-7d8e-43aa-8328-1e1b89001262] +description = "invalid if exchange code starts with 1" + +[9925b09c-1a0d-4960-a197-5d163cbe308c] +description = "invalid if area code starts with 0 on valid 11-digit number" + +[3f809d37-40f3-44b5-ad90-535838b1a816] +description = "invalid if area code starts with 1 on valid 11-digit number" + +[e08e5532-d621-40d4-b0cc-96c159276b65] +description = "invalid if exchange code starts with 0 on valid 11-digit number" + +[57b32f3d-696a-455c-8bf1-137b6d171cdf] +description = "invalid if exchange code starts with 1 on valid 11-digit number" diff --git a/exercises/practice/phone-number/libs/exercism/test b/exercises/practice/phone-number/libs/exercism/test new file mode 100644 index 00000000..1d08eb25 --- /dev/null +++ b/exercises/practice/phone-number/libs/exercism/test @@ -0,0 +1,173 @@ +needs console/loaded + +\ ----------------------------------------------------------------- + +ns: test + +-1 var, test-count +var tests-passed +var tests-failed +var tests-skipped +true var, run-test + +\ Some utility words + + +: test-passed \ s x x -- \\ test name, expected value, actual value + 2drop + 1 tests-passed n:+! + con:green con:onBlack . space " ... OK" . con:white con:onBlack cr +; + +: test-skipped \ s -- + 1 tests-skipped n:+! + con:cyan con:onBlack . space " ... SKIPPED" . con:white con:onBlack cr +; + +: test-failed \ s x x -- \\ test name, expected value, actual value + 1 tests-failed n:+! + rot + con:red con:onBlack . space " ... FAIL" . con:white con:onBlack cr + " Actual: «" . . "»" . cr + " Expected: «" . . "»" . cr cr +; + +: isword? \ x -- x f + dup >kind ns:w n:= +; + +: run-test? \ -- T + run-test @ if true else "RUN_ALL_TESTS" getenv n:>bool then +; + +\ Num passed + num skipped + num failed should == num tests +: all-tests-run? \ -- T + tests-passed @ tests-skipped @ tests-failed @ n:+ n:+ + test-count @ n:= +; + +\ returns true if x is a date, false otherwise +: date? \ x -- x T + dup >kind ns:d n:= +; + +\ adapted from 8th forum -- https://8th-dev.com/forum/index.php/topic,2745.0.html +: eq? \ x x -- T + \ are the items the same kind? + 2dup >kind swap >kind n:= + !if 2drop false ;then + + \ same kind: try different comparators + number? if n:= ;then + string? if s:= ;then + array? if ' eq? a:= 2nip ;then + map? if ' eq? m:= 2nip ;then + date? if d:= ;then + + \ otherwise fall back to 'lazy evaluation' + l: = +; + +: eps_eq? \ n x x -- T + \ are the items the same kind? + 2dup >kind swap >kind n:= + !if 2drop false ;then + number? !if 2drop false ;then + rot n:~= +; + +: check-depth \ ... n -- ... + dup>r + n:1+ depth n:= + !if + con:red con:onBlack + "PANIC: expected stack depth to be " . r> . cr + "Stack is:" . cr + .s cr + 255 die + then + rdrop +; + +\ ----------------------------------------------------------------- + +\ status report at end of run +( all-tests-run? + !if con:red con:onBlack "... FAIL - not all tests completed" . con:white con:onBlack cr then +) onexit + +\ Print a summary of the tests run +( con:white con:onBlack + test-count @ . space "tests planned - " . + tests-passed @ . space "passed - " . + tests-skipped @ . space "skipped - " . + tests-failed @ . space "failed" . cr +) onexit + +\ ----------------------------------------------------------------- +\ The public-facing words +\ ----------------------------------------------------------------- + +: equal? \ s x w -- | s w x -- + run-test? !if 2drop test-skipped ;; then + isword? !if swap then + w:exec + 3 check-depth + 2dup \ so test-failed can show actual and expected + eq? if test-passed else test-failed then +; + +: approx_equal? \ s x w n -- | s w x n -- + run-test? !if 3drop test-skipped ;; then + -rot isword? !if swap then + w:exec + 4 check-depth + 3dup \ so test-failed can show actual and expected + eps_eq? + if rot drop test-passed else rot drop test-failed then +; + +: true? \ s w -- + run-test? !if drop test-skipped ;; then + w:exec + 2 check-depth + true swap dup \ so test-failed can show actual and expected + if test-passed else test-failed then +; + +: false? \ s w -- + run-test? !if drop test-skipped ;; then + w:exec + 2 check-depth + false swap dup \ so test-failed can show actual and expected + !if test-passed else test-failed then +; + +: null? \ s w -- + run-test? !if drop test-skipped ;; then + w:exec + 2 check-depth + null swap dup \ so test-failed can show actual and expected + G:null? nip if test-passed else test-failed then +; + +: SKIP-REST-OF-TESTS false run-test ! ; + +: tests \ n -- + test-count ! +; + +\ Set the exit status: +\ 0 = all OK +\ 1 = not all tests were run (some error occurred) +\ 2 = some tests failed +: end-of-tests \ -- + all-tests-run? + if + tests-failed @ 0 n:= if 0 else 2 then + else + 1 + then + die +; + diff --git a/exercises/practice/phone-number/phone-number.8th b/exercises/practice/phone-number/phone-number.8th new file mode 100644 index 00000000..70ad1dee --- /dev/null +++ b/exercises/practice/phone-number/phone-number.8th @@ -0,0 +1,3 @@ +: clean \ s -- s + +; diff --git a/exercises/practice/phone-number/test.8th b/exercises/practice/phone-number/test.8th new file mode 100644 index 00000000..612f7cfa --- /dev/null +++ b/exercises/practice/phone-number/test.8th @@ -0,0 +1,86 @@ +"phone-number.8th" f:include +needs exercism/test +with: test +18 tests + +"cleans the number" + ( "(223) 456-7890" clean ) + "2234567890" + equal? + +SKIP-REST-OF-TESTS + +"cleans numbers with dots" + ( "223.456.7890" clean ) + "2234567890" + equal? + +"cleans numbers with multiple spaces" + ( "223 456 7890 " clean ) + "2234567890" + equal? + +"invalid when 9 digits" + ( "123456789" clean ) + null? + +"invalid when 11 digits does not start with a 1" + ( "22234567890" clean ) + null? + +"valid when 11 digits and starting with 1" + ( "12234567890" clean ) + "2234567890" + equal? + +"valid when 11 digits and starting with 1 even with punctuation" + ( "+1 (223) 456-7890" clean ) + "2234567890" + equal? + +"invalid when more than 11 digits" + ( "321234567890" clean ) + null? + +"invalid with letters" + ( "523-abc-7890" clean ) + null? + +"invalid with punctuations" + ( "523-@:!-7890" clean ) + null? + +"invalid if area code starts with 0" + ( "(023) 456-7890" clean ) + null? + +"invalid if area code starts with 1" + ( "(123) 456-7890" clean ) + null? + +"invalid if exchange code starts with 0" + ( "(223) 056-7890" clean ) + null? + +"invalid if exchange code starts with 1" + ( "(223) 156-7890" clean ) + null? + +"invalid if area code starts with 0 on valid 11-digit number" + ( "1 (023) 456-7890" clean ) + null? + +"invalid if area code starts with 1 on valid 11-digit number" + ( "1 (123) 456-7890" clean ) + null? + +"invalid if exchange code starts with 0 on valid 11-digit number" + ( "1 (223) 056-7890" clean ) + null? + +"invalid if exchange code starts with 1 on valid 11-digit number" + ( "1 (223) 156-7890" clean ) + null? + +end-of-tests +;with