diff --git a/lib/include/repl.h b/lib/include/repl.h new file mode 100644 index 0000000..25368f6 --- /dev/null +++ b/lib/include/repl.h @@ -0,0 +1,35 @@ +#ifndef REPL_H +#define REPL_H + +#include "memory_pool.h" + +#define REPL_LINE_BUFFER_SIZE 128 +#define REPL_RESULT_BUFFER_SIZE 128 + +typedef enum { + REPL_OK, + REPL_ERROR, + REPL_EXIT, +} repl_status_t; + +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); + +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]; +} repl_t; + +void init_repl(repl_t *repl); +void step_repl(repl_t *repl); +void run_repl(repl_t *repl); + +#endif diff --git a/lib/repl.c b/lib/repl.c new file mode 100644 index 0000000..35dd60a --- /dev/null +++ b/lib/repl.c @@ -0,0 +1,30 @@ +#include "repl.h" + +#include + +void init_repl(repl_t *repl) +{ + init_memory_pool(&repl->pool); +} + +void step_repl(repl_t *repl) +{ + int len; + for (len = 0; len < REPL_LINE_BUFFER_SIZE; ++len) { + const int byte = repl->get_byte(); + if ('\n' == byte) + break; + repl->line_buffer[len] = (char)byte; + } + const expression_t *e = repl->read(&repl->pool, repl->line_buffer, len); + 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); +} + +void run_repl(repl_t *repl) +{ + while (1) + step_repl(repl); +} diff --git a/scripts/build.sh b/scripts/build.sh index 83f6da8..eabd2a6 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -10,7 +10,9 @@ mkdir -p build clang $CFLAGS -c -o build/evaluator.o lib/evaluator.c clang $CFLAGS -c -o build/memory_pool.o lib/memory_pool.c clang $CFLAGS -c -o build/reader.o lib/reader.c -ar -crs build/lib.a build/evaluator.o build/memory_pool.o build/reader.o +clang $CFLAGS -c -o build/repl.o lib/repl.c +ar -crs build/lib.a \ + build/evaluator.o build/memory_pool.o build/reader.o build/repl.o # Build tests clang $CFLAGS -Itests -c -o build/testing.o tests/testing.c @@ -20,6 +22,9 @@ 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 5ebae56..55fa04b 100644 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -4,5 +4,6 @@ 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/repl_tests.c b/tests/repl_tests.c new file mode 100644 index 0000000..515451e --- /dev/null +++ b/tests/repl_tests.c @@ -0,0 +1,107 @@ +#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 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; + init_repl(&repl); +} + +static void read_is_called_on_first_line_line_in_input(void) +{ + 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) +{ + 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) +{ + set_up_valid_state(); + step_repl(&repl); + ASSERT_TRUE(print_called); + ASSERT_EQUAL(5, print_len); + ASSERT_MEM_EQUAL("4321\n", print_output, 5); +} + +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); + TESTING_END(); +}