Create basic REPL module
This commit is contained in:
parent
810feee55e
commit
8d5f5e4ede
35
lib/include/repl.h
Normal file
35
lib/include/repl.h
Normal file
@ -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
|
30
lib/repl.c
Normal file
30
lib/repl.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include "repl.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
107
tests/repl_tests.c
Normal file
107
tests/repl_tests.c
Normal file
@ -0,0 +1,107 @@
|
||||
#include "repl.h"
|
||||
#include "testing.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
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();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user