C: change phone-number to exercism V3

This commit is contained in:
2021-09-03 15:50:08 +02:00
parent 215629a19c
commit 77a8072c73
6 changed files with 489 additions and 0 deletions

63
c/phone-number/HELP.md Normal file
View File

@@ -0,0 +1,63 @@
# Help
## Running the tests
Get the first test compiling, linking and passing by following the [three rules of test-driven development][3-tdd-rules].
The included makefile can be used to create and run the tests using the `test` task.
```console
$ make test
```
Create just the functions you need to satisfy any compiler errors and get the test to fail.
Then write just enough code to get the test to pass.
Once you've done that, move onto the next test.
As you progress through the tests, take the time to refactor your implementation for readability and expressiveness and then go on to the next test.
Try to use standard C99 facilities in preference to writing your own low-level algorithms or facilities by hand.
[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
## Submitting your solution
You can submit your solution using the `exercism submit phone_number.c phone_number.h` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [C track's documentation](https://exercism.org/docs/tracks/c)
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
Make sure you have read the "Guides" section of the [C track][c-track] on the Exercism site.
This covers the basic information on setting up the development environment expected by the exercises.
## Submitting Incomplete Solutions
If you are struggling with a particular exercise, it is possible to submit an incomplete solution so you can see how others have completed the exercise.
## Resources
To get help if having trouble, you can use the following resources:
- [StackOverflow][] can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
- [CPPReference][] can be used to look up information on C concepts, operators, types, standard library functions and more.
- [TutorialsPoint][] has similar content as CPPReference in its C programming section.
- [The C Programming][K&R] book by K&R is the original source of the language and is still useful today.
[c-track]: https://exercism.io/my/tracks/c
[stackoverflow]: http://stackoverflow.com/questions/tagged/c
[cppreference]: https://en.cppreference.com/w/c
[tutorialspoint]: https://www.tutorialspoint.com/cprogramming/
[K&R]: https://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628/

View File

@@ -0,0 +1,45 @@
#ifndef __BR_COMMON_H
#define __BR_COMMON_H
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 30
*
* __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17.
* For other compilers, simple implementation (for __falltrough__ only)
*/
#ifndef __has_attribute
# define __has_attribute(x) __GCC4_has_attribute_##x
# define __GCC4_has_attribute___assume_aligned__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___copy__ 0
# define __GCC4_has_attribute___designated_init__ 0
# define __GCC4_has_attribute___externally_visible__ 1
# define __GCC4_has_attribute___no_caller_saved_registers__ 0
# define __GCC4_has_attribute___noclone__ 1
# define __GCC4_has_attribute___nonstring__ 0
# define __GCC4_has_attribute___no_sanitize_address__ (__GNUC_MINOR__ >= 8)
# define __GCC4_has_attribute___no_sanitize_undefined__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___fallthrough__ 0
# define __GCC4_has_attribute___fallthrough__ 0
#endif
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 200
*/
#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#else
# define fallthrough do {} while (0); /* fallthrough */
#endif
/* See GNUmakefile below for explanation
* https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile
*/
#if defined UNIT_TEST || defined DEBUG
# include <stdio.h>
# include <stdlib.h>
#endif
#ifdef TESTALL
# undef TEST_IGNORE
# define TEST_IGNORE() {}
#endif
#endif /* __BR_COMMON_H */

View File

@@ -0,0 +1,80 @@
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <stdint.h>
#include "phone_number.h"
#define LEN_NUM 10
#define LPAREN '('
/* this version is likely not very stable, due to poor scanf() capabilities
* I made it to offer an option to traditional strtok() or manual string
* parsing.
*/
char *phone_number_clean(const char *input)
{
char *scan="%m[+(0-9]%*[()-. ]%m[0-9]%*[()-. ]%m[0-9]%*[-. ]%m[0-9]";
char *sn[4];
uint64_t num[4];
uint64_t *p = &num[0];
int nmatch;
char *res;
if (!(res = malloc(LEN_NUM+1)))
return NULL;
memset(res, '0', LEN_NUM);
*(res+LEN_NUM) = 0;
nmatch = sscanf(input, scan, &sn[0], &sn[1], &sn[2], &sn[3]);
for (int i=0; i<nmatch; ++i) {
*(p+i) = atol(*sn[i] == LPAREN? sn[i]+1: sn[i]);
free(sn[i]);
}
switch (nmatch) {
case 2:
case 0:
return res;
case 1:
if (*p > 10000000000) /* 1 000 000 0000 */
*p -= 10000000000;
if (*p > 9999999999 || /* 999 999 9999 */
*p < 2000000000) /* 200 000 0000 */
return res;
break;
case 4: /* area */
if (*p != 1)
return res;
p++;
fallthrough; /* only gcc>=7 & clang>=12 */
case 3: /* last 3 numbers */
if (*p < 200 || *p > 999 ||
*(p+1) < 200 || *(p+1) > 999 ||
*(p+2) > 9999)
return res;
break;
}
snprintf(res, LEN_NUM+1, "%ld%ld%ld", *p, *(p+1), *(p+2));
return res;
}
/* See GNUmakefile below for explanation
* https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile
*/
#ifdef UNIT_TEST
int main(int ac, char **av)
{
int arg;
char *res;
for (arg=1; arg<ac; ++arg) {
res=phone_number_clean(av[arg]);
printf("orig = [%s]\n", av[arg]);
printf("\t-> [%s]\n", res);
free(res);
}
}
#endif

View File

@@ -0,0 +1,10 @@
#ifndef PHONE_NUMBER_H
#define PHONE_NUMBER_H
#include "br-common.h"
#define NUMBER_LENGTH 10
char *phone_number_clean(const char *input);
#endif

View File

@@ -0,0 +1,246 @@
#include "test-framework/unity.h"
#include "phone_number.h"
#include <stdlib.h>
static char *result = NULL;
void setUp(void)
{
}
void tearDown(void)
{
free(result);
result = NULL;
}
static void test_cleans_the_number(void)
{
const char input[] = "(223) 456-7890";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_cleans_numbers_with_dots(void)
{
TEST_IGNORE(); // delete this line to run test
const char input[] = "223.456.7890";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_cleans_numbers_with_multiple_spaces(void)
{
TEST_IGNORE();
const char input[] = "223 456 7890 ";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_when_9_digits(void)
{
TEST_IGNORE();
const char input[] = "123456789";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_when_11_digits_does_not_start_with_a_1(void)
{
TEST_IGNORE();
const char input[] = "22234567890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_valid_when_11_digits_and_starting_with_1(void)
{
TEST_IGNORE();
const char input[] = "12234567890";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_valid_when_11_digits_and_starting_with_1_even_with_punctuation(void)
{
TEST_IGNORE();
const char input[] = "+1 (223) 456-7890";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_when_more_than_11_digits(void)
{
TEST_IGNORE();
const char input[] = "321234567890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_with_letters(void)
{
TEST_IGNORE();
const char input[] = "123-abc-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_with_punctuations(void)
{
TEST_IGNORE();
const char input[] = "123-@:!-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_if_area_code_starts_with_0(void)
{
TEST_IGNORE();
const char input[] = "(023) 456-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_if_area_code_starts_with_1(void)
{
TEST_IGNORE();
const char input[] = "(123) 456-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_if_exchange_code_starts_with_0(void)
{
TEST_IGNORE();
const char input[] = "(223) 056-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_if_exchange_code_starts_with_1(void)
{
TEST_IGNORE();
const char input[] = "(223) 156-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_invalid_if_area_code_starts_with_0_on_valid_11_digit_number(void)
{
TEST_IGNORE();
const char input[] = "1 (023) 456-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_invalid_if_area_code_starts_with_1_on_valid_11_digit_number(void)
{
TEST_IGNORE();
const char input[] = "1 (123) 456-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_invalid_if_exchange_code_starts_with_0_on_valid_11_digit_number(void)
{
TEST_IGNORE();
const char input[] = "1 (223) 056-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_invalid_if_exchange_code_starts_with_1_on_valid_11_digit_number(void)
{
TEST_IGNORE();
const char input[] = "1 (123) 156-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
int main(void)
{
UnityBegin("test_phone_number.c");
RUN_TEST(test_cleans_the_number);
RUN_TEST(test_cleans_numbers_with_dots);
RUN_TEST(test_cleans_numbers_with_multiple_spaces);
RUN_TEST(test_invalid_when_9_digits);
RUN_TEST(test_invalid_when_11_digits_does_not_start_with_a_1);
RUN_TEST(test_valid_when_11_digits_and_starting_with_1);
RUN_TEST
(test_valid_when_11_digits_and_starting_with_1_even_with_punctuation);
RUN_TEST(test_invalid_when_more_than_11_digits);
RUN_TEST(test_invalid_with_letters);
RUN_TEST(test_invalid_with_punctuations);
RUN_TEST(test_invalid_if_area_code_starts_with_0);
RUN_TEST(test_invalid_if_area_code_starts_with_1);
RUN_TEST(test_invalid_if_exchange_code_starts_with_0);
RUN_TEST(test_invalid_if_exchange_code_starts_with_1);
RUN_TEST(test_invalid_if_area_code_starts_with_0_on_valid_11_digit_number);
RUN_TEST(test_invalid_if_area_code_starts_with_1_on_valid_11_digit_number);
RUN_TEST
(test_invalid_if_exchange_code_starts_with_0_on_valid_11_digit_number);
RUN_TEST
(test_invalid_if_exchange_code_starts_with_1_on_valid_11_digit_number);
return UnityEnd();
}

45
c/templates/br-common.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef __BR_COMMON_H
#define __BR_COMMON_H
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 30
*
* __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17.
* For other compilers, simple implementation (for __falltrough__ only)
*/
#ifndef __has_attribute
# define __has_attribute(x) __GCC4_has_attribute_##x
# define __GCC4_has_attribute___assume_aligned__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___copy__ 0
# define __GCC4_has_attribute___designated_init__ 0
# define __GCC4_has_attribute___externally_visible__ 1
# define __GCC4_has_attribute___no_caller_saved_registers__ 0
# define __GCC4_has_attribute___noclone__ 1
# define __GCC4_has_attribute___nonstring__ 0
# define __GCC4_has_attribute___no_sanitize_address__ (__GNUC_MINOR__ >= 8)
# define __GCC4_has_attribute___no_sanitize_undefined__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___fallthrough__ 0
# define __GCC4_has_attribute___fallthrough__ 0
#endif
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 200
*/
#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#else
# define fallthrough do {} while (0); /* fallthrough */
#endif
/* See GNUmakefile below for explanation
* https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile
*/
#if defined UNIT_TEST || defined DEBUG
# include <stdio.h>
# include <stdlib.h>
#endif
#ifdef TESTALL
# undef TEST_IGNORE
# define TEST_IGNORE() {}
#endif
#endif /* __BR_COMMON_H */