Compare commits

...

11 Commits

34 changed files with 1007 additions and 109 deletions

View File

@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.15)
project(imp LANGUAGES C) project(imp LANGUAGES C)
option(TESTS "Build tests" ON) option(TESTS "Build tests" ON)
option(DEMO "Build demo" ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
@@ -23,3 +24,7 @@ if (${TESTS})
add_subdirectory(dep/unity) add_subdirectory(dep/unity)
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
if (${DEMO})
add_subdirectory(demo)
endif()

5
demo/CMakeLists.txt Normal file
View File

@@ -0,0 +1,5 @@
add_executable(repl
main.c
)
configure_target(repl)
target_link_libraries(repl PRIVATE imp)

9
demo/main.c Normal file
View File

@@ -0,0 +1,9 @@
#include "repl.h"
static repl_t repl;
int main(void)
{
repl_init(&repl);
return repl_run(&repl);
}

View File

@@ -1,8 +1,14 @@
add_library(imp add_library(imp
am.c am.c
env.c env.c
eval.c
expr.c
memory_stream.c memory_stream.c
parse.c parse.c
prim.c
print.c
read.c
repl.c
store.c store.c
token.c token.c
) )

View File

@@ -1,5 +1,8 @@
#include "am.h" #include "am.h"
#include "env.h"
#include "store.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@@ -7,6 +10,8 @@ void am_init(am_t *am)
{ {
memset(am, 0, sizeof(am_t)); memset(am, 0, sizeof(am_t));
am->sp = am->stack + AM_STACK_SIZE - 1; am->sp = am->stack + AM_STACK_SIZE - 1;
store_init(am);
env_init(am);
} }
void am_push(am_t *am) void am_push(am_t *am)

View File

@@ -1,5 +1,7 @@
#include "env.h" #include "env.h"
#include "prim.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@@ -47,11 +49,10 @@ static expr_t **lookup(am_t *am, bool *found)
return &prev->pair.cdr; return &prev->pair.cdr;
} }
void env_init(am_t *am, store_t *store) void env_init(am_t *am)
{ {
am->env = store_alloc(store); am->env = expr_empty_list(am);
am->env->is_atom = true; prim_load(am);
am->env->atom.type = ATOM_TYPE_EMPTY_LIST;
} }
void env_fetch(am_t *am) void env_fetch(am_t *am)
@@ -61,7 +62,7 @@ void env_fetch(am_t *am)
am->val = found ? val : NULL; am->val = found ? val : NULL;
} }
void env_set(am_t *am, store_t *store) void env_set(am_t *am)
{ {
bool found; bool found;
expr_t **loc = lookup(am, &found); expr_t **loc = lookup(am, &found);
@@ -69,12 +70,7 @@ void env_set(am_t *am, store_t *store)
*loc = am->val; *loc = am->val;
} else { } else {
(*loc)->is_atom = false; (*loc)->is_atom = false;
(*loc)->pair.cdr = store_alloc(store); (*loc)->pair.cdr = expr_empty_list(am);
(*loc)->pair.cdr->is_atom = true; (*loc)->pair.car = expr_pair(am, am->expr, am->val);
(*loc)->pair.cdr->atom.type = ATOM_TYPE_EMPTY_LIST;
expr_t *entry = (*loc)->pair.car = store_alloc(store);
entry->pair.car = am->expr;
entry->pair.cdr = am->val;
} }
} }

42
lib/eval.c Normal file
View File

@@ -0,0 +1,42 @@
#include "eval.h"
#include "env.h"
#include <assert.h>
static void eval_atom(am_t *am)
{
switch (am->expr->atom.type) {
case ATOM_TYPE_EMPTY_LIST:
case ATOM_TYPE_INTEGER:
case ATOM_TYPE_PRIM_PROC:
am->val = am->expr;
break;
case ATOM_TYPE_SYMBOL:
env_fetch(am);
break;
}
}
static void eval_list(am_t *am)
{
am->argl = am->expr->pair.cdr;
am->expr = am->expr->pair.car;
assert(am->expr->is_atom);
assert(am->expr->atom.type == ATOM_TYPE_SYMBOL);
env_fetch(am);
assert(am->val->is_atom);
assert(am->val->atom.type == ATOM_TYPE_PRIM_PROC);
assert(am->val->atom.prim_proc != NULL);
am->val->atom.prim_proc(am);
}
void eval(am_t *am)
{
if (am->expr->is_atom)
eval_atom(am);
else
eval_list(am);
}

56
lib/expr.c Normal file
View File

@@ -0,0 +1,56 @@
#include "expr.h"
#include "am.h"
#include <string.h>
expr_t *expr_integer(am_t *am, int64_t value)
{
expr_t *expr = store_alloc(am);
expr->is_atom = true;
expr->atom.type = ATOM_TYPE_INTEGER;
expr->atom.integer = value;
return expr;
}
expr_t *expr_symbol(am_t *am, const symbol_t *symbol)
{
expr_t *expr = store_alloc(am);
expr->is_atom = true;
expr->atom.type = ATOM_TYPE_SYMBOL;
memcpy(&expr->atom.symbol, symbol, sizeof(symbol_t));
return expr;
}
expr_t *expr_str_symbol(am_t *am, const char *str)
{
symbol_t symbol = { .len = strlen(str) };
memcpy(symbol.buf, str, symbol.len);
return expr_symbol(am, &symbol);
}
expr_t *expr_empty_list(am_t *am)
{
expr_t *expr = store_alloc(am);
expr->is_atom = true;
expr->atom.type = ATOM_TYPE_EMPTY_LIST;
return expr;
}
expr_t *expr_pair(am_t *am, expr_t *car, expr_t *cdr)
{
expr_t *expr = store_alloc(am);
expr->is_atom = false;
expr->pair.car = car;
expr->pair.cdr = cdr;
return expr;
}
expr_t *expr_prim_proc(am_t *am, prim_proc_t prim_proc)
{
expr_t *expr = store_alloc(am);
expr->is_atom = true;
expr->atom.type = ATOM_TYPE_PRIM_PROC;
expr->atom.prim_proc = prim_proc;
return expr;
}

View File

@@ -2,12 +2,14 @@
#define AM_H #define AM_H
#include "expr.h" #include "expr.h"
#include "store.h"
#define AM_STACK_SIZE 128U #define AM_STACK_SIZE 128U
typedef struct { typedef struct am {
expr_t *env, *expr, *val; expr_t *argl, *env, *expr, *val;
expr_t **sp, *stack[AM_STACK_SIZE]; expr_t **sp, *stack[AM_STACK_SIZE];
store_t store;
} am_t; } am_t;
void am_init(am_t *am); void am_init(am_t *am);

View File

@@ -2,10 +2,9 @@
#define ENV_H #define ENV_H
#include "am.h" #include "am.h"
#include "store.h"
void env_init(am_t *am, store_t *store); void env_init(am_t *am);
void env_fetch(am_t *am); void env_fetch(am_t *am);
void env_set(am_t *am, store_t *store); void env_set(am_t *am);
#endif #endif

8
lib/include/eval.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef EVAL_H
#define EVAL_H
#include "am.h"
void eval(am_t *am);
#endif

View File

@@ -7,15 +7,20 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
struct am;
typedef struct { typedef struct {
char buf[MAX_SYMBOL_LEN]; char buf[MAX_SYMBOL_LEN];
size_t len; size_t len;
} symbol_t; } symbol_t;
typedef void (*prim_proc_t)(struct am *am);
typedef enum { typedef enum {
ATOM_TYPE_EMPTY_LIST, ATOM_TYPE_EMPTY_LIST,
ATOM_TYPE_INTEGER, ATOM_TYPE_INTEGER,
ATOM_TYPE_SYMBOL, ATOM_TYPE_SYMBOL,
ATOM_TYPE_PRIM_PROC,
} atom_type_t; } atom_type_t;
typedef struct { typedef struct {
@@ -23,6 +28,7 @@ typedef struct {
union { union {
int64_t integer; int64_t integer;
symbol_t symbol; symbol_t symbol;
prim_proc_t prim_proc;
}; };
} atom_t; } atom_t;
@@ -40,4 +46,11 @@ typedef struct expr {
}; };
} expr_t; } expr_t;
expr_t *expr_integer(struct am *am, int64_t value);
expr_t *expr_symbol(struct am *am, const symbol_t *symbol);
expr_t *expr_str_symbol(struct am *am, const char *str);
expr_t *expr_empty_list(struct am *am);
expr_t *expr_pair(struct am *am, expr_t *car, expr_t *cdr);
expr_t *expr_prim_proc(struct am *am, prim_proc_t prim_proc);
#endif #endif

View File

@@ -2,7 +2,6 @@
#define PARSE_H #define PARSE_H
#include "am.h" #include "am.h"
#include "store.h"
#include "token.h" #include "token.h"
#define PARSE_MAX_DEPTH 128U #define PARSE_MAX_DEPTH 128U
@@ -21,7 +20,7 @@ typedef struct {
parse_state_t *sp, stack[PARSE_MAX_DEPTH]; parse_state_t *sp, stack[PARSE_MAX_DEPTH];
} parse_ctx_t; } parse_ctx_t;
void parse_init(am_t *am, store_t *store, parse_ctx_t *out); void parse_init(am_t *am, parse_ctx_t *out);
parse_state_t parse_proc(parse_ctx_t *ctx, const token_t *token); parse_state_t parse_proc(parse_ctx_t *ctx, const token_t *token);
#endif #endif

9
lib/include/prim.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef PRIM_H
#define PRIM_H
#include "am.h"
#include "expr.h"
void prim_load(am_t *am);
#endif

8
lib/include/print.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef PRINT_H
#define PRINT_H
#include "am.h"
size_t print(am_t *am, char *buffer, size_t buffer_size);
#endif

9
lib/include/read.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef READ_H
#define READ_H
#include "am.h"
#include "stream.h"
void read(am_t *am, stream_t *stream);
#endif

16
lib/include/repl.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef REPL_H
#define REPL_H
#include "am.h"
#define REPL_IO_BUFFER_SIZE 256U
typedef struct {
am_t am;
char io_buffer[REPL_IO_BUFFER_SIZE];
} repl_t;
void repl_init(repl_t *repl);
int repl_run(repl_t *repl);
#endif

View File

@@ -5,12 +5,14 @@
#define STORE_SIZE 256U #define STORE_SIZE 256U
typedef struct { typedef struct store {
expr_t *free; expr_t *free;
expr_t buffer[STORE_SIZE]; expr_t buffer[STORE_SIZE];
} store_t; } store_t;
void store_init(store_t *store); struct am;
expr_t *store_alloc(store_t *store);
void store_init(struct am *am);
expr_t *store_alloc(struct am *am);
#endif #endif

View File

@@ -3,10 +3,9 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
void parse_init(am_t *am, store_t *store, parse_ctx_t *out) void parse_init(am_t *am, parse_ctx_t *out)
{ {
out->am = am; out->am = am;
out->store = store;
out->state = PARSE_STATE_INIT; out->state = PARSE_STATE_INIT;
out->sp = out->stack + PARSE_MAX_DEPTH - 1; out->sp = out->stack + PARSE_MAX_DEPTH - 1;
} }
@@ -23,33 +22,16 @@ static parse_state_t pop_state(parse_ctx_t *ctx)
return *++ctx->sp; return *++ctx->sp;
} }
static void load_integer(parse_ctx_t *ctx, expr_t **expr, int64_t integer) static void append(parse_ctx_t *ctx, expr_t *expr)
{ {
*expr = store_alloc(ctx->store); expr_t *list = ctx->am->expr;
(*expr)->is_atom = true; while (!list->is_atom)
(*expr)->atom.type = ATOM_TYPE_INTEGER; list = list->pair.cdr;
(*expr)->atom.integer = integer; assert(list->atom.type == ATOM_TYPE_EMPTY_LIST);
}
static void list->is_atom = false;
load_symbol(parse_ctx_t *ctx, expr_t **expr, const symbol_t *symbol) list->pair.car = expr;
{ list->pair.cdr = expr_empty_list(ctx->am);
*expr = store_alloc(ctx->store);
(*expr)->is_atom = true;
(*expr)->atom.type = ATOM_TYPE_SYMBOL;
memcpy(&(*expr)->atom.symbol, symbol, sizeof(symbol_t));
}
static expr_t **append(parse_ctx_t *ctx, expr_t *expr)
{
while (!expr->is_atom)
expr = expr->pair.cdr;
assert(expr->atom.type == ATOM_TYPE_EMPTY_LIST);
expr->is_atom = false;
expr->pair.cdr = store_alloc(ctx->store);
expr->pair.cdr->is_atom = true;
expr->pair.cdr->atom.type = ATOM_TYPE_EMPTY_LIST;
return &expr->pair.car;
} }
parse_state_t parse_proc(parse_ctx_t *ctx, const token_t *token) parse_state_t parse_proc(parse_ctx_t *ctx, const token_t *token)
@@ -58,18 +40,16 @@ parse_state_t parse_proc(parse_ctx_t *ctx, const token_t *token)
case PARSE_STATE_INIT: case PARSE_STATE_INIT:
switch (token->type) { switch (token->type) {
case TOKEN_TYPE_INTEGER: case TOKEN_TYPE_INTEGER:
load_integer(ctx, &ctx->am->expr, token->integer); ctx->am->expr = expr_integer(ctx->am, token->integer);
ctx->state = PARSE_STATE_DONE; ctx->state = PARSE_STATE_DONE;
break; break;
case TOKEN_TYPE_SYMBOL: case TOKEN_TYPE_SYMBOL:
load_symbol(ctx, &ctx->am->expr, &token->symbol); ctx->am->expr = expr_symbol(ctx->am, &token->symbol);
ctx->state = PARSE_STATE_DONE; ctx->state = PARSE_STATE_DONE;
break; break;
case TOKEN_TYPE_OPEN_PAREN: case TOKEN_TYPE_OPEN_PAREN:
push_state(ctx, PARSE_STATE_DONE); push_state(ctx, PARSE_STATE_DONE);
ctx->am->expr = store_alloc(ctx->store); ctx->am->expr = expr_empty_list(ctx->am);
ctx->am->expr->is_atom = true;
ctx->am->expr->atom.type = ATOM_TYPE_EMPTY_LIST;
ctx->state = PARSE_STATE_LIST; ctx->state = PARSE_STATE_LIST;
break; break;
case TOKEN_TYPE_CLOSE_PAREN: case TOKEN_TYPE_CLOSE_PAREN:
@@ -80,21 +60,16 @@ parse_state_t parse_proc(parse_ctx_t *ctx, const token_t *token)
case PARSE_STATE_LIST: case PARSE_STATE_LIST:
switch (token->type) { switch (token->type) {
expr_t **end_car;
case TOKEN_TYPE_INTEGER: case TOKEN_TYPE_INTEGER:
end_car = append(ctx, ctx->am->expr); append(ctx, expr_integer(ctx->am, token->integer));
load_integer(ctx, end_car, token->integer);
break; break;
case TOKEN_TYPE_SYMBOL: case TOKEN_TYPE_SYMBOL:
end_car = append(ctx, ctx->am->expr); append(ctx, expr_symbol(ctx->am, &token->symbol));
load_symbol(ctx, end_car, &token->symbol);
break; break;
case TOKEN_TYPE_OPEN_PAREN: case TOKEN_TYPE_OPEN_PAREN:
am_push(ctx->am); am_push(ctx->am);
push_state(ctx, PARSE_STATE_LIST); push_state(ctx, PARSE_STATE_LIST);
ctx->am->expr = store_alloc(ctx->store); ctx->am->expr = expr_empty_list(ctx->am);
ctx->am->expr->is_atom = true;
ctx->am->expr->atom.type = ATOM_TYPE_EMPTY_LIST;
ctx->state = PARSE_STATE_LIST; ctx->state = PARSE_STATE_LIST;
break; break;
case TOKEN_TYPE_CLOSE_PAREN: case TOKEN_TYPE_CLOSE_PAREN:
@@ -102,8 +77,7 @@ parse_state_t parse_proc(parse_ctx_t *ctx, const token_t *token)
if (ctx->state == PARSE_STATE_LIST) { if (ctx->state == PARSE_STATE_LIST) {
expr_t *expr = ctx->am->expr; expr_t *expr = ctx->am->expr;
am_pop(ctx->am); am_pop(ctx->am);
end_car = append(ctx, ctx->am->expr); append(ctx, expr);
*end_car = expr;
} }
break; break;
} }

74
lib/prim.c Normal file
View File

@@ -0,0 +1,74 @@
#include "prim.h"
#include "env.h"
#include <assert.h>
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
typedef struct {
const char *name;
prim_proc_t prim_proc;
} prim_table_entry_t;
static void add(am_t *am)
{
assert(am->argl);
int64_t total = 0;
for (expr_t *list = am->argl; !list->is_atom; list = list->pair.cdr) {
assert(list->pair.car->is_atom);
assert(list->pair.car->atom.type == ATOM_TYPE_INTEGER);
total += list->pair.car->atom.integer;
}
am->val = expr_integer(am, total);
}
static void mul(am_t *am)
{
assert(am->argl);
int64_t total = 1;
for (expr_t *list = am->argl; !list->is_atom; list = list->pair.cdr) {
assert(list->pair.car->is_atom);
assert(list->pair.car->atom.type == ATOM_TYPE_INTEGER);
total *= list->pair.car->atom.integer;
}
am->val = expr_integer(am, total);
}
static void sub(am_t *am)
{
assert(am->argl);
assert(!am->argl->is_atom);
assert(am->argl->pair.car->is_atom);
assert(am->argl->pair.car->atom.type == ATOM_TYPE_INTEGER);
int64_t total = am->argl->pair.car->atom.integer;
if (!am->argl->is_atom && am->argl->pair.cdr->is_atom) {
total *= -1;
} else {
for (expr_t *list = am->argl->pair.cdr; !list->is_atom;
list = list->pair.cdr) {
assert(list->pair.car->is_atom);
assert(list->pair.car->atom.type == ATOM_TYPE_INTEGER);
total -= list->pair.car->atom.integer;
}
}
am->val = expr_integer(am, total);
}
static const prim_table_entry_t prim_table[] = {
{ "+", add },
{ "*", mul },
{ "-", sub },
};
void prim_load(am_t *am)
{
for (unsigned i = 0; i < NELEMS(prim_table); ++i) {
am->expr = expr_str_symbol(am, prim_table[i].name);
am->val = expr_prim_proc(am, prim_table[i].prim_proc);
env_set(am);
}
}

26
lib/print.c Normal file
View File

@@ -0,0 +1,26 @@
#include "print.h"
#include <assert.h>
size_t print(am_t *am, char *buffer, size_t buffer_size)
{
assert(am->val->is_atom);
assert(am->val->atom.type == ATOM_TYPE_INTEGER);
int64_t value = am->val->atom.integer;
int divisor = 10;
while (divisor <= value)
divisor *= 10;
size_t i = 0;
do {
divisor /= 10;
const int digit = value / divisor;
value = value % divisor;
assert(i < buffer_size);
buffer[i++] = '0' + (char)digit;
} while (divisor > 1);
return i;
}

22
lib/read.c Normal file
View File

@@ -0,0 +1,22 @@
#include "read.h"
#include "parse.h"
#include "token.h"
#include <assert.h>
void read(am_t *am, stream_t *stream)
{
parse_ctx_t ctx;
token_t token;
parse_init(am, &ctx);
while (ctx.state != PARSE_STATE_DONE) {
const token_status_t token_status = token_read(stream, &token);
assert(token_status == TOKEN_OK);
parse_proc(&ctx, &token);
assert(ctx.state != PARSE_STATE_ERROR);
}
}

58
lib/repl.c Normal file
View File

@@ -0,0 +1,58 @@
#include "repl.h"
#include "eval.h"
#include "memory_stream.h"
#include "print.h"
#include "read.h"
#include <assert.h>
#include <stdio.h>
#define PROMPT "> "
static int read_line(char buffer[REPL_IO_BUFFER_SIZE])
{
for (unsigned i = 0; i < REPL_IO_BUFFER_SIZE; ++i) {
const int c = getchar();
switch (c) {
case EOF:
return EOF;
case '\n':
return (int)i;
default:
buffer[i] = (char)c;
break;
}
}
assert(false);
return -2;
}
void repl_init(repl_t *repl)
{
am_init(&repl->am);
}
int repl_run(repl_t *repl)
{
int len;
memory_stream_t input;
while (true) {
fputs(PROMPT, stdout);
len = read_line(repl->io_buffer);
if (len < 0) {
fputc('\n', stdout);
return 0;
}
memory_stream_init(&input, (const uint8_t *)repl->io_buffer, len);
read(&repl->am, (stream_t *)&input);
eval(&repl->am);
len = print(&repl->am, repl->io_buffer, REPL_IO_BUFFER_SIZE - 1);
repl->io_buffer[len] = '\n';
fwrite(repl->io_buffer, 1, len + 1, stdout);
}
}

View File

@@ -1,14 +1,16 @@
#include "store.h" #include "store.h"
#include "am.h"
#include <string.h> #include <string.h>
void store_init(store_t *store) void store_init(am_t *am)
{ {
memset(store, 0, sizeof(store_t)); memset(&am->store, 0, sizeof(store_t));
store->free = store->buffer; am->store.free = am->store.buffer;
} }
expr_t *store_alloc(store_t *store) expr_t *store_alloc(am_t *am)
{ {
return store->free++; return am->store.free++;
} }

View File

@@ -11,7 +11,13 @@ endfunction()
add_test_suites( add_test_suites(
am_tests.c am_tests.c
env_tests.c env_tests.c
eval_tests.c
expr_tests.c
integration_tests.c
parse_tests.c parse_tests.c
prim_tests.c
print_tests.c
read_tests.c
store_tests.c store_tests.c
token_tests.c token_tests.c
) )

View File

@@ -4,31 +4,10 @@
#include <string.h> #include <string.h>
static am_t am; static am_t am;
static store_t store;
static expr_t *integer(int64_t value)
{
expr_t *expr = store_alloc(&store);
expr->is_atom = true;
expr->atom.type = ATOM_TYPE_INTEGER;
expr->atom.integer = value;
return expr;
}
static expr_t *symbol(const char *s)
{
expr_t *expr = store_alloc(&store);
expr->is_atom = true;
expr->atom.type = ATOM_TYPE_SYMBOL;
memcpy(expr->atom.symbol.buf, s, strlen(s));
return expr;
}
void setUp(void) void setUp(void)
{ {
am_init(&am); am_init(&am);
store_init(&store);
env_init(&am, &store);
} }
void tearDown(void) void tearDown(void)
@@ -37,11 +16,11 @@ void tearDown(void)
static void test_set_foo_to_42_then_fetch(void) static void test_set_foo_to_42_then_fetch(void)
{ {
am.expr = symbol("foo"); am.expr = expr_str_symbol(&am, "foo");
am.val = integer(42); am.val = expr_integer(&am, 42);
env_set(&am, &store); env_set(&am);
am.expr = symbol("foo"); am.expr = expr_str_symbol(&am, "foo");
am.val = NULL; am.val = NULL;
env_fetch(&am); env_fetch(&am);
@@ -53,13 +32,13 @@ static void test_set_foo_to_42_then_fetch(void)
static void test_update_foo_from_123_to_456_then_fetch(void) static void test_update_foo_from_123_to_456_then_fetch(void)
{ {
am.expr = symbol("foo"); am.expr = expr_str_symbol(&am, "foo");
am.val = integer(123); am.val = expr_integer(&am, 123);
env_set(&am, &store); env_set(&am);
am.val = integer(456); am.val = expr_integer(&am, 456);
env_set(&am, &store); env_set(&am);
am.expr = symbol("foo"); am.expr = expr_str_symbol(&am, "foo");
am.val = NULL; am.val = NULL;
env_fetch(&am); env_fetch(&am);

100
tests/eval_tests.c Normal file
View File

@@ -0,0 +1,100 @@
#include "am.h"
#include "env.h"
#include "eval.h"
#include "unity.h"
static am_t am;
void test_prim_proc(am_t *am)
{
(void)am;
}
void setUp(void)
{
am_init(&am);
}
void tearDown(void)
{
}
static void test_42_self_evals(void)
{
am.expr = expr_integer(&am, 42);
eval(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.val->atom.type);
TEST_ASSERT_EQUAL(42, am.val->atom.integer);
}
static void test_empty_list_self_evals(void)
{
am.expr = expr_empty_list(&am);
eval(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_EMPTY_LIST, am.val->atom.type);
}
static void test_prim_proc_self_evals(void)
{
am.expr = expr_prim_proc(&am, test_prim_proc);
eval(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.val->atom.type);
TEST_ASSERT_EQUAL(test_prim_proc, am.val->atom.prim_proc);
}
static void test_foo_evals_to_42_when_set_in_env(void)
{
am.expr = expr_str_symbol(&am, "foo");
am.val = expr_integer(&am, 42);
env_set(&am);
am.val = NULL;
eval(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.val->atom.type);
TEST_ASSERT_EQUAL(42, am.val->atom.integer);
}
static void test_add_1_2_3_evals_to_6(void)
{
am.expr = expr_pair(
&am, expr_str_symbol(&am, "+"),
expr_pair(
&am, expr_integer(&am, 1),
expr_pair(
&am, expr_integer(&am, 2),
expr_pair(
&am, expr_integer(&am, 3), expr_empty_list(&am)))));
eval(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.val->atom.type);
TEST_ASSERT_EQUAL(6, am.val->atom.integer);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_42_self_evals);
RUN_TEST(test_empty_list_self_evals);
RUN_TEST(test_prim_proc_self_evals);
RUN_TEST(test_foo_evals_to_42_when_set_in_env);
RUN_TEST(test_add_1_2_3_evals_to_6);
return UNITY_END();
}

123
tests/expr_tests.c Normal file
View File

@@ -0,0 +1,123 @@
#include "am.h"
#include "expr.h"
#include "unity.h"
static am_t am;
void test_prim_proc(am_t *am)
{
(void)am;
}
void setUp(void)
{
am_init(&am);
}
void tearDown(void)
{
}
static void test_expr_integer_123(void)
{
expr_t *expr = expr_integer(&am, 123);
TEST_ASSERT_NOT_NULL(expr);
TEST_ASSERT_TRUE(expr->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, expr->atom.type);
TEST_ASSERT_EQUAL(123, expr->atom.integer);
}
static void test_expr_integer_456(void)
{
expr_t *expr = expr_integer(&am, 456);
TEST_ASSERT_NOT_NULL(expr);
TEST_ASSERT_TRUE(expr->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, expr->atom.type);
TEST_ASSERT_EQUAL(456, expr->atom.integer);
}
static void test_expr_symbol_foo(void)
{
const symbol_t symbol = { .buf = "foo", .len = 3 };
expr_t *expr = expr_symbol(&am, &symbol);
TEST_ASSERT_NOT_NULL(expr);
TEST_ASSERT_TRUE(expr->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, expr->atom.type);
TEST_ASSERT_EQUAL(3, expr->atom.symbol.len);
TEST_ASSERT_EQUAL_MEMORY("foo", expr->atom.symbol.buf, 3);
}
static void test_expr_symbol_quux(void)
{
const symbol_t symbol = { .buf = "quux", .len = 4 };
expr_t *expr = expr_symbol(&am, &symbol);
TEST_ASSERT_NOT_NULL(expr);
TEST_ASSERT_TRUE(expr->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, expr->atom.type);
TEST_ASSERT_EQUAL(4, expr->atom.symbol.len);
TEST_ASSERT_EQUAL_MEMORY("quux", expr->atom.symbol.buf, 4);
}
static void test_expr_str_symbol_foo(void)
{
expr_t *expr = expr_str_symbol(&am, "foo");
TEST_ASSERT_NOT_NULL(expr);
TEST_ASSERT_TRUE(expr->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, expr->atom.type);
TEST_ASSERT_EQUAL(3, expr->atom.symbol.len);
TEST_ASSERT_EQUAL_MEMORY("foo", expr->atom.symbol.buf, 3);
}
static void test_expr_str_symbol_quux(void)
{
expr_t *expr = expr_str_symbol(&am, "quux");
TEST_ASSERT_NOT_NULL(expr);
TEST_ASSERT_TRUE(expr->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, expr->atom.type);
TEST_ASSERT_EQUAL(4, expr->atom.symbol.len);
TEST_ASSERT_EQUAL_MEMORY("quux", expr->atom.symbol.buf, 4);
}
static void test_expr_empty_list(void)
{
expr_t *expr = expr_empty_list(&am);
TEST_ASSERT_NOT_NULL(expr);
TEST_ASSERT_TRUE(expr->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_EMPTY_LIST, expr->atom.type);
}
static void test_expr_pair(void)
{
expr_t *car = expr_integer(&am, 123);
expr_t *cdr = expr_integer(&am, 456);
expr_t *expr = expr_pair(&am, car, cdr);
TEST_ASSERT_NOT_NULL(expr);
TEST_ASSERT_FALSE(expr->is_atom);
TEST_ASSERT_EQUAL(car, expr->pair.car);
TEST_ASSERT_EQUAL(cdr, expr->pair.cdr);
}
static void test_expr_prim_proc(void)
{
expr_t *expr = expr_prim_proc(&am, test_prim_proc);
TEST_ASSERT_NOT_NULL(expr);
TEST_ASSERT_TRUE(expr->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, expr->atom.type);
TEST_ASSERT_EQUAL(test_prim_proc, expr->atom.prim_proc);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_expr_integer_123);
RUN_TEST(test_expr_integer_456);
RUN_TEST(test_expr_symbol_foo);
RUN_TEST(test_expr_symbol_quux);
RUN_TEST(test_expr_str_symbol_foo);
RUN_TEST(test_expr_str_symbol_quux);
RUN_TEST(test_expr_empty_list);
RUN_TEST(test_expr_pair);
RUN_TEST(test_expr_prim_proc);
return UNITY_END();
}

48
tests/integration_tests.c Normal file
View File

@@ -0,0 +1,48 @@
#include "eval.h"
#include "memory_stream.h"
#include "print.h"
#include "read.h"
#include "unity.h"
#include <string.h>
#define BUFFER_SIZE 256U
static am_t am;
static char buffer[BUFFER_SIZE];
static const char *read_eval_print(const char *input)
{
memory_stream_t stream;
memory_stream_init(&stream, (const uint8_t *)input, strlen(input));
read(&am, (stream_t *)&stream);
eval(&am);
memset(buffer, 0, sizeof(buffer));
print(&am, buffer, BUFFER_SIZE - 1);
return buffer;
}
void setUp(void)
{
am_init(&am);
}
void tearDown(void)
{
}
static void test_unnested_arithmetic(void)
{
TEST_ASSERT_EQUAL_STRING("10", read_eval_print("(+ 1 2 3 4)"));
TEST_ASSERT_EQUAL_STRING("42", read_eval_print("(* 2 21)"));
TEST_ASSERT_EQUAL_STRING("64", read_eval_print("(- 100 36)"));
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_unnested_arithmetic);
return UNITY_END();
}

View File

@@ -1,7 +1,6 @@
#include "parse.h" #include "parse.h"
#include "unity.h" #include "unity.h"
static store_t store;
static am_t am; static am_t am;
static parse_ctx_t ctx; static parse_ctx_t ctx;
@@ -9,9 +8,8 @@ static parse_ctx_t ctx;
void setUp(void) void setUp(void)
{ {
store_init(&store);
am_init(&am); am_init(&am);
parse_init(&am, &store, &ctx); parse_init(&am, &ctx);
} }
void tearDown(void) void tearDown(void)

140
tests/prim_tests.c Normal file
View File

@@ -0,0 +1,140 @@
#include "env.h"
#include "prim.h"
#include "unity.h"
static am_t am;
void setUp(void)
{
am_init(&am);
}
void tearDown(void)
{
}
static void test_add_empty_list_is_0(void)
{
am.expr = expr_str_symbol(&am, "+");
env_fetch(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.val->atom.type);
am.argl = expr_empty_list(&am);
am.val->atom.prim_proc(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.val->atom.type);
TEST_ASSERT_EQUAL(0, am.val->atom.integer);
}
static void test_add_1_2_3_is_6(void)
{
am.expr = expr_str_symbol(&am, "+");
env_fetch(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.val->atom.type);
am.argl = expr_pair(
&am, expr_integer(&am, 1),
expr_pair(
&am, expr_integer(&am, 2),
expr_pair(&am, expr_integer(&am, 3), expr_empty_list(&am))));
am.val->atom.prim_proc(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.val->atom.type);
TEST_ASSERT_EQUAL(6, am.val->atom.integer);
}
static void test_mul_empty_list_is_1(void)
{
am.expr = expr_str_symbol(&am, "*");
env_fetch(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.val->atom.type);
am.argl = expr_empty_list(&am);
am.val->atom.prim_proc(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.val->atom.type);
TEST_ASSERT_EQUAL(1, am.val->atom.integer);
}
static void test_mul_2_3_4_is_24(void)
{
am.expr = expr_str_symbol(&am, "*");
env_fetch(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.val->atom.type);
am.argl = expr_pair(
&am, expr_integer(&am, 2),
expr_pair(
&am, expr_integer(&am, 3),
expr_pair(&am, expr_integer(&am, 4), expr_empty_list(&am))));
am.val->atom.prim_proc(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.val->atom.type);
TEST_ASSERT_EQUAL(24, am.val->atom.integer);
}
static void test_sub_1_is_minus_1(void)
{
am.expr = expr_str_symbol(&am, "-");
env_fetch(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.val->atom.type);
am.argl = expr_pair(&am, expr_integer(&am, 1), expr_empty_list(&am));
am.val->atom.prim_proc(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.val->atom.type);
TEST_ASSERT_EQUAL(-1, am.val->atom.integer);
}
static void test_sub_5_4_3_is_minus_2(void)
{
am.expr = expr_str_symbol(&am, "-");
env_fetch(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.val->atom.type);
am.argl = expr_pair(
&am, expr_integer(&am, 5),
expr_pair(
&am, expr_integer(&am, 4),
expr_pair(&am, expr_integer(&am, 3), expr_empty_list(&am))));
am.val->atom.prim_proc(&am);
TEST_ASSERT_NOT_NULL(am.val);
TEST_ASSERT_TRUE(am.val->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.val->atom.type);
TEST_ASSERT_EQUAL(-2, am.val->atom.integer);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_add_empty_list_is_0);
RUN_TEST(test_add_1_2_3_is_6);
RUN_TEST(test_mul_empty_list_is_1);
RUN_TEST(test_mul_2_3_4_is_24);
RUN_TEST(test_sub_1_is_minus_1);
RUN_TEST(test_sub_5_4_3_is_minus_2);
return UNITY_END();
}

61
tests/print_tests.c Normal file
View File

@@ -0,0 +1,61 @@
#include "print.h"
#include "unity.h"
#include <string.h>
#define BUFFER_SIZE 256U
static am_t am;
static char buffer[BUFFER_SIZE];
void setUp(void)
{
memset(buffer, 0, BUFFER_SIZE);
am_init(&am);
}
void tearDown(void)
{
}
static void test_integer_5(void)
{
am.val = expr_integer(&am, 5);
const size_t len = print(&am, buffer, BUFFER_SIZE);
TEST_ASSERT_EQUAL(1, len);
TEST_ASSERT_EQUAL_MEMORY("5", buffer, 1);
}
static void test_integer_1234(void)
{
am.val = expr_integer(&am, 1234);
const size_t len = print(&am, buffer, BUFFER_SIZE);
TEST_ASSERT_EQUAL(4, len);
TEST_ASSERT_EQUAL_MEMORY("1234", buffer, 4);
}
static void test_integer_0(void)
{
am.val = expr_integer(&am, 0);
const size_t len = print(&am, buffer, BUFFER_SIZE);
TEST_ASSERT_EQUAL(1, len);
TEST_ASSERT_EQUAL_MEMORY("0", buffer, 1);
}
static void test_integer_10(void)
{
am.val = expr_integer(&am, 10);
const size_t len = print(&am, buffer, BUFFER_SIZE);
TEST_ASSERT_EQUAL(2, len);
TEST_ASSERT_EQUAL_MEMORY("10", buffer, 2);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_integer_5);
RUN_TEST(test_integer_1234);
RUN_TEST(test_integer_0);
RUN_TEST(test_integer_10);
return UNITY_END();
}

98
tests/read_tests.c Normal file
View File

@@ -0,0 +1,98 @@
#include "memory_stream.h"
#include "read.h"
#include "unity.h"
#include <string.h>
#define CAR(expr) (expr->pair.car)
#define CDR(expr) (expr->pair.cdr)
#define CADR(expr) CAR(CDR(expr))
#define CDDR(expr) CDR(CDR(expr))
#define CADDR(expr) CAR(CDDR(expr))
#define CDDDR(expr) CDR(CDDR(expr))
#define CAADDR(expr) CAR(CADDR(expr))
#define CDADDR(expr) CDR(CADDR(expr))
#define CADADDR(expr) CAR(CDADDR(expr))
#define CDDADDR(expr) CDR(CDADDR(expr))
#define CADDADDR(expr) CAR(CDDADDR(expr))
#define CDDDADDR(expr) CDR(CDDADDR(expr))
static am_t am;
void setUp(void)
{
am_init(&am);
}
void tearDown(void)
{
}
static void test_nested_expression(void)
{
const char *input = "(+ 1 (* 2 3))";
memory_stream_t stream;
memory_stream_init(&stream, (const uint8_t *)input, strlen(input));
read(&am, (stream_t *)&stream);
TEST_ASSERT_NOT_NULL(am.expr);
TEST_ASSERT_FALSE(am.expr->is_atom);
TEST_ASSERT_NOT_NULL(CAR(am.expr));
TEST_ASSERT_TRUE(CAR(am.expr)->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, CAR(am.expr)->atom.type);
TEST_ASSERT_EQUAL(1, CAR(am.expr)->atom.symbol.len);
TEST_ASSERT_EQUAL_MEMORY("+", CAR(am.expr)->atom.symbol.buf, 1);
TEST_ASSERT_NOT_NULL(CDR(am.expr));
TEST_ASSERT_FALSE(CDR(am.expr)->is_atom);
TEST_ASSERT_NOT_NULL(CADR(am.expr));
TEST_ASSERT_TRUE(CADR(am.expr)->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, CADR(am.expr)->atom.type);
TEST_ASSERT_EQUAL(1, CADR(am.expr)->atom.integer);
TEST_ASSERT_NOT_NULL(CDDR(am.expr));
TEST_ASSERT_FALSE(CDDR(am.expr)->is_atom);
TEST_ASSERT_NOT_NULL(CADDR(am.expr));
TEST_ASSERT_FALSE(CADDR(am.expr)->is_atom);
TEST_ASSERT_NOT_NULL(CAADDR(am.expr));
TEST_ASSERT_TRUE(CAADDR(am.expr)->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, CAADDR(am.expr)->atom.type);
TEST_ASSERT_EQUAL(1, CAADDR(am.expr)->atom.symbol.len);
TEST_ASSERT_EQUAL_MEMORY("*", CAADDR(am.expr)->atom.symbol.buf, 1);
TEST_ASSERT_NOT_NULL(CDADDR(am.expr));
TEST_ASSERT_FALSE(CDADDR(am.expr)->is_atom);
TEST_ASSERT_NOT_NULL(CADADDR(am.expr));
TEST_ASSERT_TRUE(CADADDR(am.expr)->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, CADADDR(am.expr)->atom.type);
TEST_ASSERT_EQUAL(2, CADADDR(am.expr)->atom.integer);
TEST_ASSERT_NOT_NULL(CDDADDR(am.expr));
TEST_ASSERT_FALSE(CDDADDR(am.expr)->is_atom);
TEST_ASSERT_NOT_NULL(CADDADDR(am.expr));
TEST_ASSERT_TRUE(CADDADDR(am.expr)->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, CADDADDR(am.expr)->atom.type);
TEST_ASSERT_EQUAL(3, CADDADDR(am.expr)->atom.integer);
TEST_ASSERT_NOT_NULL(CDDDADDR(am.expr));
TEST_ASSERT_TRUE(CDDDADDR(am.expr)->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_EMPTY_LIST, CDDDADDR(am.expr)->atom.type);
TEST_ASSERT_NOT_NULL(CDDDR(am.expr));
TEST_ASSERT_TRUE(CDDDR(am.expr)->is_atom);
TEST_ASSERT_EQUAL(ATOM_TYPE_EMPTY_LIST, CDDDR(am.expr)->atom.type);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_nested_expression);
return UNITY_END();
}

View File

@@ -1,11 +1,11 @@
#include "store.h" #include "am.h"
#include "unity.h" #include "unity.h"
static store_t store; static am_t am;
void setUp(void) void setUp(void)
{ {
store_init(&store); am_init(&am);
} }
void tearDown(void) void tearDown(void)
@@ -14,14 +14,14 @@ void tearDown(void)
static void test_alloc_returns_non_null_after_init(void) static void test_alloc_returns_non_null_after_init(void)
{ {
const expr_t *const expr = store_alloc(&store); const expr_t *const expr = store_alloc(&am);
TEST_ASSERT_NOT_NULL(expr); TEST_ASSERT_NOT_NULL(expr);
} }
static void test_two_calls_to_alloc_return_distinct(void) static void test_two_calls_to_alloc_return_distinct(void)
{ {
const expr_t *const a = store_alloc(&store); const expr_t *const a = store_alloc(&am);
const expr_t *const b = store_alloc(&store); const expr_t *const b = store_alloc(&am);
TEST_ASSERT_NOT_EQUAL(a, b); TEST_ASSERT_NOT_EQUAL(a, b);
} }