diff --git a/app/main.c b/app/main.c index 690a5b3..dc5ed95 100644 --- a/app/main.c +++ b/app/main.c @@ -1,21 +1,9 @@ #include #include -#include "evaluator.h" -#include "reader.h" #include "repl.h" -static void print(const char *output, int len) -{ - fwrite(output, 1, len, stdout); -} - -static repl_t repl = { - .get_byte = getchar, - .read = read_expression, - .evaluate = evaluate, - .print = print, -}; +static repl_t repl; int main(void) { diff --git a/lib/include/reader.h b/lib/include/reader.h index 5fc99d8..5853d06 100644 --- a/lib/include/reader.h +++ b/lib/include/reader.h @@ -3,6 +3,8 @@ #include "memory_pool.h" +int read_line(int (*get_byte)(void), char *buffer, int buffer_size); + const expression_t * read_expression(memory_pool_t *pool, const char *input, int len); diff --git a/lib/include/repl.h b/lib/include/repl.h index 7a99b8c..260267e 100644 --- a/lib/include/repl.h +++ b/lib/include/repl.h @@ -3,23 +3,11 @@ #include "memory_pool.h" -#define REPL_LINE_BUFFER_SIZE 128 -#define REPL_RESULT_BUFFER_SIZE 128 - -typedef int (*get_byte_fn_t)(void); -typedef const expression_t *(*read_fn_t)( - memory_pool_t *pool, const char *input, int len); -typedef int (*evaluate_fn_t)(const expression_t *expression); -typedef void (*print_fn_t)(const char *output, int len); +#define REPL_BUFFER_SIZE 128 typedef struct { - get_byte_fn_t get_byte; - read_fn_t read; - evaluate_fn_t evaluate; - print_fn_t print; memory_pool_t pool; - char line_buffer[REPL_LINE_BUFFER_SIZE]; - char result_buffer[REPL_RESULT_BUFFER_SIZE]; + char buffer[REPL_BUFFER_SIZE]; } repl_t; bool step_repl(repl_t *repl); diff --git a/lib/reader.c b/lib/reader.c index f86592e..20cf78e 100644 --- a/lib/reader.c +++ b/lib/reader.c @@ -2,6 +2,7 @@ #include #include +#include static int parse_expression( memory_pool_t *pool, const char *input, int len, expression_t **out); @@ -152,6 +153,19 @@ static int parse_expression( return used; } +int read_line(int (*get_byte)(void), char *buffer, int buffer_size) +{ + for (int len = 0; len < buffer_size; ++len) { + const int byte = get_byte(); + if (EOF == byte) + return -1; + if ('\n' == byte) + return len; + buffer[len] = (char)byte; + } + return buffer_size; +} + const expression_t * read_expression(memory_pool_t *pool, const char *input, int len) { diff --git a/lib/repl.c b/lib/repl.c index af3e77c..ad7e914 100644 --- a/lib/repl.c +++ b/lib/repl.c @@ -1,5 +1,8 @@ #include "repl.h" +#include "evaluator.h" +#include "reader.h" + #include #include @@ -7,26 +10,17 @@ bool step_repl(repl_t *repl) { init_memory_pool(&repl->pool); - int len; - for (len = 0; len < REPL_LINE_BUFFER_SIZE; ++len) { - const int byte = repl->get_byte(); - if (EOF == byte) - return false; - else if ('\n' == byte) - break; - repl->line_buffer[len] = (char)byte; - } - const expression_t *e = repl->read(&repl->pool, repl->line_buffer, len); + const int len = read_line(getchar, repl->buffer, REPL_BUFFER_SIZE); + if (len < 0) + return false; + const expression_t *e = read_expression(&repl->pool, repl->buffer, len); if (NULL == e) { - const char *msg = "Invalid expression\n"; - repl->print(msg, strlen(msg)); + puts("Invalid expression\n"); return true; } - const int result = repl->evaluate(e); - const int result_len = snprintf( - repl->result_buffer, REPL_RESULT_BUFFER_SIZE, "%d\n", result); - repl->print(repl->result_buffer, result_len); + const int result = evaluate(e); + printf("%d\n", result); return true; } diff --git a/scripts/build.sh b/scripts/build.sh index fc1e7d2..0ca929a 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -22,9 +22,6 @@ clang $CFLAGS -o build/evaluator_tests \ clang $CFLAGS -Itests -c -o build/reader_tests.o tests/reader_tests.c clang $CFLAGS -o build/reader_tests \ build/reader_tests.o build/lib.a build/testing.o -clang $CFLAGS -Itests -c -o build/repl_tests.o tests/repl_tests.c -clang $CFLAGS -o build/repl_tests \ - build/repl_tests.o build/lib.a build/testing.o # Build application clang $CFLAGS -c -o build/main.o app/main.c diff --git a/scripts/test.sh b/scripts/test.sh index 55fa04b..5ebae56 100644 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -4,6 +4,5 @@ fails=0 build/evaluator_tests || fails=`expr $fails + 1` build/reader_tests || fails=`expr $fails + 1` -build/repl_tests || fails=`expr $fails + 1` if [ $fails -eq 0 ]; then echo Tests OK; fi diff --git a/tests/reader_tests.c b/tests/reader_tests.c index 1df4e42..68240fe 100644 --- a/tests/reader_tests.c +++ b/tests/reader_tests.c @@ -1,8 +1,21 @@ #include "reader.h" #include "testing.h" +#include + static memory_pool_t pool; +static char *test_input; +static int test_input_len; + +static int test_get_byte(void) +{ + if (test_input_len < 0) + return EOF; + --test_input_len; + return *test_input++; +} + static void input_1234_is_read_as_number_with_expected_value(void) { init_memory_pool(&pool); @@ -234,6 +247,41 @@ static void trailing_spaces_are_ignored(void) ASSERT_EQUAL(14, result->number); } +static void read_line_copies_line_into_buffer(void) +{ + test_input = "foobar\nbazquux"; + test_input_len = strlen(test_input); + char buffer[10]; + read_line(test_get_byte, buffer, sizeof(buffer)); + ASSERT_MEM_EQUAL("foobar", buffer, 6); +} + +static void read_line_returns_length_of_line(void) +{ + test_input = "foobar\nbazquux"; + test_input_len = strlen(test_input); + char buffer[10]; + const int len = read_line(test_get_byte, buffer, sizeof(buffer)); + ASSERT_EQUAL(6, len); +} + +static void read_line_returns_minus_1_on_eof(void) +{ + test_input_len = 0; + char buffer[10]; + const int len = read_line(test_get_byte, buffer, sizeof(buffer)); + ASSERT_EQUAL(-1, len); +} + +static void read_line_returns_buffer_size_if_no_newline(void) +{ + test_input = "foobarbazquux"; + test_input_len = strlen(test_input); + char buffer[10]; + const int len = read_line(test_get_byte, buffer, sizeof(buffer)); + ASSERT_EQUAL(10, len); +} + int main(void) { TESTING_BEGIN(); @@ -256,5 +304,9 @@ int main(void) RUN_TEST(spaces_inside_parens_are_ignored); RUN_TEST(leading_spaces_are_ignored); RUN_TEST(trailing_spaces_are_ignored); + RUN_TEST(read_line_copies_line_into_buffer); + RUN_TEST(read_line_returns_length_of_line); + RUN_TEST(read_line_returns_minus_1_on_eof); + RUN_TEST(read_line_returns_buffer_size_if_no_newline); TESTING_END(); } diff --git a/tests/repl_tests.c b/tests/repl_tests.c deleted file mode 100644 index 69d39a1..0000000 --- a/tests/repl_tests.c +++ /dev/null @@ -1,145 +0,0 @@ -#include "repl.h" -#include "testing.h" - -#include -#include - -static const char *input; -static int input_len; - -static bool read_called; -static const char *read_input; -static int read_len; -static const expression_t *read_result; - -static bool evaluate_called; -static const expression_t *evaluate_expression; -static int evaluate_result; - -static bool print_called; -static const char *print_output; -static int print_len; - -static void reset_fixtures(void) -{ - read_called = false; - evaluate_called = false; - print_called = false; -} - -static int test_get_byte(void) -{ - if (0 == input_len) - return EOF; - --input_len; - return *input++; -} - -static const expression_t * -test_read(memory_pool_t *pool, const char *input, int len) -{ - (void)pool; - read_called = true; - read_input = input; - read_len = len; - return read_result; -} - -static int test_evaluate(const expression_t *expression) -{ - evaluate_called = true; - evaluate_expression = expression; - return evaluate_result; -} - -static void test_print(const char *output, int len) -{ - print_called = true; - print_output = output; - print_len = len; -} - -static repl_t repl = { - .get_byte = test_get_byte, - .read = test_read, - .evaluate = test_evaluate, - .print = test_print, -}; - -static void set_up_valid_state(void) -{ - static const expression_t expression - = { .is_number = true, .number = 1234 }; - input = "foobar\nbarquux"; - input_len = 14; - read_result = &expression; - evaluate_result = 4321; -} - -static void read_is_called_on_first_line_line_in_input(void) -{ - reset_fixtures(); - set_up_valid_state(); - step_repl(&repl); - ASSERT_TRUE(read_called); - ASSERT_EQUAL(6, read_len); - ASSERT_MEM_EQUAL("foobar", read_input, 6); -} - -static void read_result_is_passed_to_evaluate(void) -{ - reset_fixtures(); - set_up_valid_state(); - step_repl(&repl); - ASSERT_TRUE(evaluate_called); - ASSERT_TRUE(evaluate_expression->is_number); - ASSERT_EQUAL(1234, evaluate_expression->number); -} - -static void result_of_evaluation_is_printed_with_a_newline(void) -{ - reset_fixtures(); - set_up_valid_state(); - step_repl(&repl); - ASSERT_TRUE(print_called); - ASSERT_EQUAL(5, print_len); - ASSERT_MEM_EQUAL("4321\n", print_output, 5); -} - -static void true_is_returned_on_successful_step(void) -{ - reset_fixtures(); - set_up_valid_state(); - const bool result = step_repl(&repl); - ASSERT_TRUE(result); -} - -static void false_is_returned_on_end_of_input(void) -{ - reset_fixtures(); - set_up_valid_state(); - input_len = 0; - const bool result = step_repl(&repl); - ASSERT_FALSE(result); -} - -static void evaluate_is_not_called_if_read_returns_null(void) -{ - reset_fixtures(); - set_up_valid_state(); - read_result = NULL; - step_repl(&repl); - ASSERT_FALSE(evaluate_called); -} - -int main(void) -{ - TESTING_BEGIN(); - RUN_TEST(read_is_called_on_first_line_line_in_input); - RUN_TEST(read_result_is_passed_to_evaluate); - RUN_TEST(result_of_evaluation_is_printed_with_a_newline); - RUN_TEST(true_is_returned_on_successful_step); - RUN_TEST(false_is_returned_on_end_of_input); - RUN_TEST(evaluate_is_not_called_if_read_returns_null); - TESTING_END(); -}