Compare commits
22 Commits
fade9395fa
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c26c723eea | |||
| 068692e163 | |||
| 167e91d816 | |||
| 426d270318 | |||
| eaef6f3cd3 | |||
| 76efcef953 | |||
| 57c39fd00b | |||
| 52a42d2937 | |||
| ba0a543a4c | |||
| 662b99a40f | |||
| 624311d04f | |||
| 6077cf571d | |||
| 9df53c63c5 | |||
| 5c585ad35c | |||
| e25975e29d | |||
| d90f91cda0 | |||
| d5173243ba | |||
| d8e51b0aa0 | |||
| b20a6749f7 | |||
| 472a682757 | |||
| 3f871d2b43 | |||
| 03bd0ff597 |
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.15)
|
||||
project(imp LANGUAGES C)
|
||||
|
||||
option(TESTS "Build tests" ON)
|
||||
option(DEMO "Build demo" ON)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
@@ -23,3 +24,7 @@ if (${TESTS})
|
||||
add_subdirectory(dep/unity)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if (${DEMO})
|
||||
add_subdirectory(demo)
|
||||
endif()
|
||||
|
||||
5
demo/CMakeLists.txt
Normal file
5
demo/CMakeLists.txt
Normal 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
9
demo/main.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "repl.h"
|
||||
|
||||
static repl_t repl;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
repl_init(&repl);
|
||||
return repl_run(&repl);
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
add_library(imp
|
||||
am.c
|
||||
env.c
|
||||
eval.c
|
||||
expr.c
|
||||
memory_stream.c
|
||||
parse.c
|
||||
prim.c
|
||||
print.c
|
||||
read.c
|
||||
repl.c
|
||||
store.c
|
||||
token.c
|
||||
)
|
||||
|
||||
49
lib/am.c
49
lib/am.c
@@ -1,5 +1,8 @@
|
||||
#include "am.h"
|
||||
|
||||
#include "env.h"
|
||||
#include "store.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -7,16 +10,54 @@ void am_init(am_t *am)
|
||||
{
|
||||
memset(am, 0, sizeof(am_t));
|
||||
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, am_reg_t reg)
|
||||
{
|
||||
assert(am->sp >= am->stack);
|
||||
*am->sp-- = am->expr;
|
||||
*am->sp-- = am->regs[reg];
|
||||
}
|
||||
|
||||
void am_pop(am_t *am)
|
||||
void am_pop(am_t *am, am_reg_t reg)
|
||||
{
|
||||
assert(am->sp < am->stack + AM_STACK_SIZE - 1);
|
||||
am->expr = *++am->sp;
|
||||
am->regs[reg] = *++am->sp;
|
||||
}
|
||||
|
||||
void am_append(am_t *am, am_reg_t list_reg, am_reg_t item_reg)
|
||||
{
|
||||
expr_t *list = am->regs[list_reg];
|
||||
while (!list->is_atom)
|
||||
list = list->pair.cdr;
|
||||
|
||||
list->is_atom = false;
|
||||
list->pair.car = am->regs[item_reg];
|
||||
list->pair.cdr = expr_empty_list(am);
|
||||
}
|
||||
|
||||
void am_car(am_t *am, am_reg_t dest, am_reg_t src)
|
||||
{
|
||||
assert(!am->regs[src]->is_atom);
|
||||
am->regs[dest] = am->regs[src]->pair.car;
|
||||
}
|
||||
|
||||
void am_cdr(am_t *am, am_reg_t dest, am_reg_t src)
|
||||
{
|
||||
assert(!am->regs[src]->is_atom);
|
||||
am->regs[dest] = am->regs[src]->pair.cdr;
|
||||
}
|
||||
|
||||
void am_assign(am_t *am, am_reg_t dest, am_reg_t src)
|
||||
{
|
||||
am->regs[dest] = am->regs[src];
|
||||
}
|
||||
|
||||
void am_call(am_t *am, am_reg_t proc)
|
||||
{
|
||||
assert(am->regs[proc]->is_atom);
|
||||
assert(am->regs[proc]->atom.type == ATOM_TYPE_PRIM_PROC);
|
||||
assert(am->regs[proc]->atom.prim_proc != NULL);
|
||||
am->regs[proc]->atom.prim_proc(am);
|
||||
}
|
||||
|
||||
40
lib/env.c
40
lib/env.c
@@ -1,5 +1,7 @@
|
||||
#include "env.h"
|
||||
|
||||
#include "prim.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -10,18 +12,18 @@ static bool symbol_eq(symbol_t *s, symbol_t *t)
|
||||
|
||||
static expr_t **lookup(am_t *am, bool *found)
|
||||
{
|
||||
assert(am->expr != NULL);
|
||||
assert(am->expr->is_atom);
|
||||
assert(am->expr->atom.type == ATOM_TYPE_SYMBOL);
|
||||
assert(AM_EXPR(am) != NULL);
|
||||
assert(AM_EXPR(am)->is_atom);
|
||||
assert(AM_EXPR(am)->atom.type == ATOM_TYPE_SYMBOL);
|
||||
|
||||
if (am->env->is_atom) {
|
||||
assert(am->env->atom.type == ATOM_TYPE_EMPTY_LIST);
|
||||
if (AM_ENV(am)->is_atom) {
|
||||
assert(AM_ENV(am)->atom.type == ATOM_TYPE_EMPTY_LIST);
|
||||
*found = false;
|
||||
return &am->env;
|
||||
return &AM_ENV(am);
|
||||
}
|
||||
|
||||
expr_t *prev;
|
||||
for (expr_t *list = am->env; !list->is_atom; list = list->pair.cdr) {
|
||||
for (expr_t *list = AM_ENV(am); !list->is_atom; list = list->pair.cdr) {
|
||||
assert(list != NULL);
|
||||
|
||||
expr_t *entry = list->pair.car;
|
||||
@@ -32,7 +34,7 @@ static expr_t **lookup(am_t *am, bool *found)
|
||||
assert(key->is_atom);
|
||||
assert(key->atom.type == ATOM_TYPE_SYMBOL);
|
||||
|
||||
if (symbol_eq(&am->expr->atom.symbol, &key->atom.symbol)) {
|
||||
if (symbol_eq(&AM_EXPR(am)->atom.symbol, &key->atom.symbol)) {
|
||||
*found = true;
|
||||
return &entry->pair.cdr;
|
||||
}
|
||||
@@ -47,34 +49,28 @@ static expr_t **lookup(am_t *am, bool *found)
|
||||
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->is_atom = true;
|
||||
am->env->atom.type = ATOM_TYPE_EMPTY_LIST;
|
||||
AM_ENV(am) = expr_empty_list(am);
|
||||
prim_load(am);
|
||||
}
|
||||
|
||||
void env_fetch(am_t *am)
|
||||
{
|
||||
bool found;
|
||||
expr_t *val = *lookup(am, &found);
|
||||
am->val = found ? val : NULL;
|
||||
AM_VAL(am) = found ? val : NULL;
|
||||
}
|
||||
|
||||
void env_set(am_t *am, store_t *store)
|
||||
void env_set(am_t *am)
|
||||
{
|
||||
bool found;
|
||||
expr_t **loc = lookup(am, &found);
|
||||
if (found) {
|
||||
*loc = am->val;
|
||||
*loc = AM_VAL(am);
|
||||
} else {
|
||||
(*loc)->is_atom = false;
|
||||
(*loc)->pair.cdr = store_alloc(store);
|
||||
(*loc)->pair.cdr->is_atom = true;
|
||||
(*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;
|
||||
(*loc)->pair.cdr = expr_empty_list(am);
|
||||
(*loc)->pair.car = expr_pair(am, AM_EXPR(am), AM_VAL(am));
|
||||
}
|
||||
}
|
||||
|
||||
53
lib/eval.c
Normal file
53
lib/eval.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "eval.h"
|
||||
|
||||
#include "env.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static void eval_atom(am_t *am)
|
||||
{
|
||||
switch (AM_EXPR(am)->atom.type) {
|
||||
case ATOM_TYPE_EMPTY_LIST:
|
||||
case ATOM_TYPE_INTEGER:
|
||||
case ATOM_TYPE_PRIM_PROC:
|
||||
am_assign(am, VAL, EXPR);
|
||||
break;
|
||||
case ATOM_TYPE_SYMBOL:
|
||||
env_fetch(am);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void eval_list(am_t *am)
|
||||
{
|
||||
AM_ARGL(am) = expr_empty_list(am);
|
||||
am_cdr(am, UNEV, EXPR);
|
||||
am_car(am, EXPR, EXPR);
|
||||
|
||||
env_fetch(am);
|
||||
am_push(am, VAL);
|
||||
|
||||
while (!AM_UNEV(am)->is_atom) {
|
||||
am_push(am, ARGL);
|
||||
am_car(am, EXPR, UNEV);
|
||||
am_cdr(am, UNEV, UNEV);
|
||||
am_push(am, UNEV);
|
||||
|
||||
eval(am);
|
||||
|
||||
am_pop(am, UNEV);
|
||||
am_pop(am, ARGL);
|
||||
am_append(am, ARGL, VAL);
|
||||
}
|
||||
|
||||
am_pop(am, VAL);
|
||||
am_call(am, VAL);
|
||||
}
|
||||
|
||||
void eval(am_t *am)
|
||||
{
|
||||
if (AM_EXPR(am)->is_atom)
|
||||
eval_atom(am);
|
||||
else
|
||||
eval_list(am);
|
||||
}
|
||||
56
lib/expr.c
Normal file
56
lib/expr.c
Normal 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;
|
||||
}
|
||||
@@ -2,16 +2,39 @@
|
||||
#define AM_H
|
||||
|
||||
#include "expr.h"
|
||||
#include "store.h"
|
||||
|
||||
#define AM_STACK_SIZE 128U
|
||||
|
||||
typedef struct {
|
||||
expr_t *env, *expr, *val;
|
||||
#define AM_ARGL(am) am->regs[ARGL]
|
||||
#define AM_ENV(am) am->regs[ENV]
|
||||
#define AM_EXPR(am) am->regs[EXPR]
|
||||
#define AM_UNEV(am) am->regs[UNEV]
|
||||
#define AM_VAL(am) am->regs[VAL]
|
||||
|
||||
typedef enum {
|
||||
ARGL,
|
||||
ENV,
|
||||
EXPR,
|
||||
UNEV,
|
||||
VAL,
|
||||
|
||||
AM_REG_COUNT,
|
||||
} am_reg_t;
|
||||
|
||||
typedef struct am {
|
||||
expr_t *regs[AM_REG_COUNT];
|
||||
expr_t **sp, *stack[AM_STACK_SIZE];
|
||||
store_t store;
|
||||
} am_t;
|
||||
|
||||
void am_init(am_t *am);
|
||||
void am_push(am_t *am);
|
||||
void am_pop(am_t *am);
|
||||
void am_push(am_t *am, am_reg_t reg);
|
||||
void am_pop(am_t *am, am_reg_t reg);
|
||||
void am_append(am_t *am, am_reg_t list_reg, am_reg_t item_reg);
|
||||
void am_car(am_t *am, am_reg_t dest, am_reg_t src);
|
||||
void am_cdr(am_t *am, am_reg_t dest, am_reg_t src);
|
||||
void am_assign(am_t *am, am_reg_t dest, am_reg_t src);
|
||||
void am_call(am_t *am, am_reg_t proc);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
#define ENV_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_set(am_t *am, store_t *store);
|
||||
void env_set(am_t *am);
|
||||
|
||||
#endif
|
||||
|
||||
8
lib/include/eval.h
Normal file
8
lib/include/eval.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef EVAL_H
|
||||
#define EVAL_H
|
||||
|
||||
#include "am.h"
|
||||
|
||||
void eval(am_t *am);
|
||||
|
||||
#endif
|
||||
@@ -7,15 +7,20 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct am;
|
||||
|
||||
typedef struct {
|
||||
char buf[MAX_SYMBOL_LEN];
|
||||
size_t len;
|
||||
} symbol_t;
|
||||
|
||||
typedef void (*prim_proc_t)(struct am *am);
|
||||
|
||||
typedef enum {
|
||||
ATOM_TYPE_EMPTY_LIST,
|
||||
ATOM_TYPE_INTEGER,
|
||||
ATOM_TYPE_SYMBOL,
|
||||
ATOM_TYPE_PRIM_PROC,
|
||||
} atom_type_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -23,6 +28,7 @@ typedef struct {
|
||||
union {
|
||||
int64_t integer;
|
||||
symbol_t symbol;
|
||||
prim_proc_t prim_proc;
|
||||
};
|
||||
} atom_t;
|
||||
|
||||
@@ -40,4 +46,11 @@ typedef struct expr {
|
||||
};
|
||||
} 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
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define PARSE_H
|
||||
|
||||
#include "am.h"
|
||||
#include "store.h"
|
||||
#include "token.h"
|
||||
|
||||
#define PARSE_MAX_DEPTH 128U
|
||||
@@ -21,7 +20,7 @@ typedef struct {
|
||||
parse_state_t *sp, stack[PARSE_MAX_DEPTH];
|
||||
} 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);
|
||||
|
||||
#endif
|
||||
|
||||
9
lib/include/prim.h
Normal file
9
lib/include/prim.h
Normal 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
8
lib/include/print.h
Normal 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
9
lib/include/read.h
Normal 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
16
lib/include/repl.h
Normal 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
|
||||
@@ -5,12 +5,14 @@
|
||||
|
||||
#define STORE_SIZE 256U
|
||||
|
||||
typedef struct {
|
||||
typedef struct store {
|
||||
expr_t *free;
|
||||
expr_t buffer[STORE_SIZE];
|
||||
} store_t;
|
||||
|
||||
void store_init(store_t *store);
|
||||
expr_t *store_alloc(store_t *store);
|
||||
struct am;
|
||||
|
||||
void store_init(struct am *am);
|
||||
expr_t *store_alloc(struct am *am);
|
||||
|
||||
#endif
|
||||
|
||||
62
lib/parse.c
62
lib/parse.c
@@ -3,10 +3,9 @@
|
||||
#include <assert.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->store = store;
|
||||
out->state = PARSE_STATE_INIT;
|
||||
out->sp = out->stack + PARSE_MAX_DEPTH - 1;
|
||||
}
|
||||
@@ -23,53 +22,22 @@ static parse_state_t pop_state(parse_ctx_t *ctx)
|
||||
return *++ctx->sp;
|
||||
}
|
||||
|
||||
static void load_integer(parse_ctx_t *ctx, expr_t **expr, int64_t integer)
|
||||
{
|
||||
*expr = store_alloc(ctx->store);
|
||||
(*expr)->is_atom = true;
|
||||
(*expr)->atom.type = ATOM_TYPE_INTEGER;
|
||||
(*expr)->atom.integer = integer;
|
||||
}
|
||||
|
||||
static void
|
||||
load_symbol(parse_ctx_t *ctx, expr_t **expr, const symbol_t *symbol)
|
||||
{
|
||||
*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)
|
||||
{
|
||||
switch (ctx->state) {
|
||||
case PARSE_STATE_INIT:
|
||||
switch (token->type) {
|
||||
case TOKEN_TYPE_INTEGER:
|
||||
load_integer(ctx, &ctx->am->expr, token->integer);
|
||||
AM_EXPR(ctx->am) = expr_integer(ctx->am, token->integer);
|
||||
ctx->state = PARSE_STATE_DONE;
|
||||
break;
|
||||
case TOKEN_TYPE_SYMBOL:
|
||||
load_symbol(ctx, &ctx->am->expr, &token->symbol);
|
||||
AM_EXPR(ctx->am) = expr_symbol(ctx->am, &token->symbol);
|
||||
ctx->state = PARSE_STATE_DONE;
|
||||
break;
|
||||
case TOKEN_TYPE_OPEN_PAREN:
|
||||
push_state(ctx, PARSE_STATE_DONE);
|
||||
ctx->am->expr = store_alloc(ctx->store);
|
||||
ctx->am->expr->is_atom = true;
|
||||
ctx->am->expr->atom.type = ATOM_TYPE_EMPTY_LIST;
|
||||
AM_EXPR(ctx->am) = expr_empty_list(ctx->am);
|
||||
ctx->state = PARSE_STATE_LIST;
|
||||
break;
|
||||
case TOKEN_TYPE_CLOSE_PAREN:
|
||||
@@ -80,30 +48,26 @@ parse_state_t parse_proc(parse_ctx_t *ctx, const token_t *token)
|
||||
|
||||
case PARSE_STATE_LIST:
|
||||
switch (token->type) {
|
||||
expr_t **end_car;
|
||||
case TOKEN_TYPE_INTEGER:
|
||||
end_car = append(ctx, ctx->am->expr);
|
||||
load_integer(ctx, end_car, token->integer);
|
||||
AM_VAL(ctx->am) = expr_integer(ctx->am, token->integer);
|
||||
am_append(ctx->am, EXPR, VAL);
|
||||
break;
|
||||
case TOKEN_TYPE_SYMBOL:
|
||||
end_car = append(ctx, ctx->am->expr);
|
||||
load_symbol(ctx, end_car, &token->symbol);
|
||||
AM_VAL(ctx->am) = expr_symbol(ctx->am, &token->symbol);
|
||||
am_append(ctx->am, EXPR, VAL);
|
||||
break;
|
||||
case TOKEN_TYPE_OPEN_PAREN:
|
||||
am_push(ctx->am);
|
||||
am_push(ctx->am, EXPR);
|
||||
push_state(ctx, PARSE_STATE_LIST);
|
||||
ctx->am->expr = store_alloc(ctx->store);
|
||||
ctx->am->expr->is_atom = true;
|
||||
ctx->am->expr->atom.type = ATOM_TYPE_EMPTY_LIST;
|
||||
AM_EXPR(ctx->am) = expr_empty_list(ctx->am);
|
||||
ctx->state = PARSE_STATE_LIST;
|
||||
break;
|
||||
case TOKEN_TYPE_CLOSE_PAREN:
|
||||
ctx->state = pop_state(ctx);
|
||||
if (ctx->state == PARSE_STATE_LIST) {
|
||||
expr_t *expr = ctx->am->expr;
|
||||
am_pop(ctx->am);
|
||||
end_car = append(ctx, ctx->am->expr);
|
||||
*end_car = expr;
|
||||
AM_VAL(ctx->am) = AM_EXPR(ctx->am);
|
||||
am_pop(ctx->am, EXPR);
|
||||
am_append(ctx->am, EXPR, VAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
74
lib/prim.c
Normal file
74
lib/prim.c
Normal 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(am));
|
||||
|
||||
int64_t total = 0;
|
||||
for (expr_t *list = AM_ARGL(am); !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(am) = expr_integer(am, total);
|
||||
}
|
||||
|
||||
static void mul(am_t *am)
|
||||
{
|
||||
assert(AM_ARGL(am));
|
||||
|
||||
int64_t total = 1;
|
||||
for (expr_t *list = AM_ARGL(am); !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(am) = expr_integer(am, total);
|
||||
}
|
||||
|
||||
static void sub(am_t *am)
|
||||
{
|
||||
assert(AM_ARGL(am));
|
||||
assert(!AM_ARGL(am)->is_atom);
|
||||
assert(AM_ARGL(am)->pair.car->is_atom);
|
||||
assert(AM_ARGL(am)->pair.car->atom.type == ATOM_TYPE_INTEGER);
|
||||
|
||||
int64_t total = AM_ARGL(am)->pair.car->atom.integer;
|
||||
if (!AM_ARGL(am)->is_atom && AM_ARGL(am)->pair.cdr->is_atom) {
|
||||
total *= -1;
|
||||
} else {
|
||||
for (expr_t *list = AM_ARGL(am)->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(am) = 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(am) = expr_str_symbol(am, prim_table[i].name);
|
||||
AM_VAL(am) = expr_prim_proc(am, prim_table[i].prim_proc);
|
||||
env_set(am);
|
||||
}
|
||||
}
|
||||
32
lib/print.c
Normal file
32
lib/print.c
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "print.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
size_t print(am_t *am, char *buffer, size_t buffer_size)
|
||||
{
|
||||
assert(AM_VAL(am)->is_atom);
|
||||
assert(AM_VAL(am)->atom.type == ATOM_TYPE_INTEGER);
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
int64_t value = AM_VAL(am)->atom.integer;
|
||||
if (value < 0) {
|
||||
buffer[i++] = '-';
|
||||
value *= -1;
|
||||
}
|
||||
|
||||
int divisor = 10;
|
||||
while (divisor <= value)
|
||||
divisor *= 10;
|
||||
|
||||
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
22
lib/read.c
Normal 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
58
lib/repl.c
Normal 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);
|
||||
}
|
||||
}
|
||||
12
lib/store.c
12
lib/store.c
@@ -1,14 +1,16 @@
|
||||
#include "store.h"
|
||||
|
||||
#include "am.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void store_init(store_t *store)
|
||||
void store_init(am_t *am)
|
||||
{
|
||||
memset(store, 0, sizeof(store_t));
|
||||
store->free = store->buffer;
|
||||
memset(&am->store, 0, sizeof(store_t));
|
||||
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++;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,13 @@ endfunction()
|
||||
add_test_suites(
|
||||
am_tests.c
|
||||
env_tests.c
|
||||
eval_tests.c
|
||||
expr_tests.c
|
||||
integration_tests.c
|
||||
parse_tests.c
|
||||
prim_tests.c
|
||||
print_tests.c
|
||||
read_tests.c
|
||||
store_tests.c
|
||||
token_tests.c
|
||||
)
|
||||
|
||||
134
tests/am_tests.c
134
tests/am_tests.c
@@ -1,11 +1,23 @@
|
||||
#include "am.h"
|
||||
#include "unity.h"
|
||||
|
||||
#define CAR(expr) (expr->pair.car)
|
||||
#define CDR(expr) (expr->pair.cdr)
|
||||
#define CADR(expr) CAR(CDR(expr))
|
||||
|
||||
static am_t am;
|
||||
static bool test_prim_proc_called;
|
||||
|
||||
static void test_prim_proc(am_t *am)
|
||||
{
|
||||
(void)am;
|
||||
test_prim_proc_called = true;
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
am_init(&am);
|
||||
test_prim_proc_called = false;
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
@@ -15,16 +27,128 @@ void tearDown(void)
|
||||
static void test_expr_value_restored_after_push_modify_pop(void)
|
||||
{
|
||||
expr_t a, b;
|
||||
am.expr = &a;
|
||||
am_push(&am);
|
||||
am.expr = &b;
|
||||
am_pop(&am);
|
||||
TEST_ASSERT_EQUAL(&a, am.expr);
|
||||
|
||||
am.regs[EXPR] = &a;
|
||||
am_push(&am, EXPR);
|
||||
am.regs[EXPR] = &b;
|
||||
am_pop(&am, EXPR);
|
||||
|
||||
TEST_ASSERT_EQUAL(&a, am.regs[EXPR]);
|
||||
}
|
||||
|
||||
static void test_argl_value_restored_after_push_modify_pop(void)
|
||||
{
|
||||
expr_t a, b;
|
||||
|
||||
am.regs[ARGL] = &a;
|
||||
am_push(&am, ARGL);
|
||||
am.regs[ARGL] = &b;
|
||||
am_pop(&am, ARGL);
|
||||
|
||||
TEST_ASSERT_EQUAL(&a, am.regs[ARGL]);
|
||||
}
|
||||
|
||||
static void test_append_val_42_to_empty_argl(void)
|
||||
{
|
||||
am.regs[ARGL] = expr_empty_list(&am);
|
||||
am.regs[VAL] = expr_integer(&am, 42);
|
||||
am_append(&am, ARGL, VAL);
|
||||
|
||||
TEST_ASSERT_FALSE(am.regs[ARGL]->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(CAR(am.regs[ARGL]));
|
||||
|
||||
TEST_ASSERT_TRUE(CAR(am.regs[ARGL])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, CAR(am.regs[ARGL])->atom.type);
|
||||
TEST_ASSERT_EQUAL(42, CAR(am.regs[ARGL])->atom.integer);
|
||||
|
||||
TEST_ASSERT_TRUE(CDR(am.regs[ARGL])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_EMPTY_LIST, CDR(am.regs[ARGL])->atom.type);
|
||||
}
|
||||
|
||||
static void test_append_unev_2_to_expr_list_1(void)
|
||||
{
|
||||
am.regs[EXPR]
|
||||
= expr_pair(&am, expr_integer(&am, 1), expr_empty_list(&am));
|
||||
am.regs[UNEV] = expr_integer(&am, 2);
|
||||
am_append(&am, EXPR, UNEV);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_FALSE(am.regs[EXPR]->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CAR(am.regs[EXPR]));
|
||||
TEST_ASSERT_TRUE(CAR(am.regs[EXPR])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, CAR(am.regs[EXPR])->atom.type);
|
||||
TEST_ASSERT_EQUAL(1, CAR(am.regs[EXPR])->atom.integer);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_FALSE(CDR(am.regs[EXPR])->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CADR(am.regs[EXPR]));
|
||||
TEST_ASSERT_TRUE(CADR(am.regs[EXPR])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, CADR(am.regs[EXPR])->atom.type);
|
||||
TEST_ASSERT_EQUAL(2, CADR(am.regs[EXPR])->atom.integer);
|
||||
}
|
||||
|
||||
static void test_car_of_expr_pair_1_2(void)
|
||||
{
|
||||
am.regs[EXPR]
|
||||
= expr_pair(&am, expr_integer(&am, 1), expr_integer(&am, 2));
|
||||
am_car(&am, EXPR, EXPR);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[EXPR]->atom.type);
|
||||
TEST_ASSERT_EQUAL(1, am.regs[EXPR]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_cdr_of_expr_pair_1_2(void)
|
||||
{
|
||||
am.regs[EXPR]
|
||||
= expr_pair(&am, expr_integer(&am, 1), expr_integer(&am, 2));
|
||||
am_cdr(&am, EXPR, EXPR);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[EXPR]->atom.type);
|
||||
TEST_ASSERT_EQUAL(2, am.regs[EXPR]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_assign_expr_to_val(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_integer(&am, 42);
|
||||
am_assign(&am, VAL, EXPR);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[EXPR]->atom.type);
|
||||
TEST_ASSERT_EQUAL(42, am.regs[EXPR]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_call_prim_proc_val(void)
|
||||
{
|
||||
am.regs[VAL] = expr_prim_proc(&am, test_prim_proc);
|
||||
am_call(&am, VAL);
|
||||
TEST_ASSERT_TRUE(test_prim_proc_called);
|
||||
}
|
||||
|
||||
static void test_call_prim_proc_expr(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_prim_proc(&am, test_prim_proc);
|
||||
am_call(&am, EXPR);
|
||||
TEST_ASSERT_TRUE(test_prim_proc_called);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_expr_value_restored_after_push_modify_pop);
|
||||
RUN_TEST(test_argl_value_restored_after_push_modify_pop);
|
||||
RUN_TEST(test_append_val_42_to_empty_argl);
|
||||
RUN_TEST(test_append_unev_2_to_expr_list_1);
|
||||
RUN_TEST(test_car_of_expr_pair_1_2);
|
||||
RUN_TEST(test_cdr_of_expr_pair_1_2);
|
||||
RUN_TEST(test_assign_expr_to_val);
|
||||
RUN_TEST(test_call_prim_proc_val);
|
||||
RUN_TEST(test_call_prim_proc_expr);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
@@ -4,31 +4,10 @@
|
||||
#include <string.h>
|
||||
|
||||
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)
|
||||
{
|
||||
am_init(&am);
|
||||
store_init(&store);
|
||||
env_init(&am, &store);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
@@ -37,36 +16,36 @@ void tearDown(void)
|
||||
|
||||
static void test_set_foo_to_42_then_fetch(void)
|
||||
{
|
||||
am.expr = symbol("foo");
|
||||
am.val = integer(42);
|
||||
env_set(&am, &store);
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "foo");
|
||||
am.regs[VAL] = expr_integer(&am, 42);
|
||||
env_set(&am);
|
||||
|
||||
am.expr = symbol("foo");
|
||||
am.val = NULL;
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "foo");
|
||||
am.regs[VAL] = NULL;
|
||||
env_fetch(&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);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(42, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_update_foo_from_123_to_456_then_fetch(void)
|
||||
{
|
||||
am.expr = symbol("foo");
|
||||
am.val = integer(123);
|
||||
env_set(&am, &store);
|
||||
am.val = integer(456);
|
||||
env_set(&am, &store);
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "foo");
|
||||
am.regs[VAL] = expr_integer(&am, 123);
|
||||
env_set(&am);
|
||||
am.regs[VAL] = expr_integer(&am, 456);
|
||||
env_set(&am);
|
||||
|
||||
am.expr = symbol("foo");
|
||||
am.val = NULL;
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "foo");
|
||||
am.regs[VAL] = NULL;
|
||||
env_fetch(&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(456, am.val->atom.integer);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(456, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
||||
126
tests/eval_tests.c
Normal file
126
tests/eval_tests.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#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.regs[EXPR] = expr_integer(&am, 42);
|
||||
|
||||
eval(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(42, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_empty_list_self_evals(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_empty_list(&am);
|
||||
|
||||
eval(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_EMPTY_LIST, am.regs[VAL]->atom.type);
|
||||
}
|
||||
|
||||
static void test_prim_proc_self_evals(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_prim_proc(&am, test_prim_proc);
|
||||
|
||||
eval(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(test_prim_proc, am.regs[VAL]->atom.prim_proc);
|
||||
}
|
||||
|
||||
static void test_foo_evals_to_42_when_set_in_env(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "foo");
|
||||
am.regs[VAL] = expr_integer(&am, 42);
|
||||
env_set(&am);
|
||||
am.regs[VAL] = NULL;
|
||||
|
||||
eval(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(42, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_add_1_2_3_evals_to_6(void)
|
||||
{
|
||||
am.regs[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.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(6, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_add_1_mul_2_3_evals_to_7(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_pair(
|
||||
&am, expr_str_symbol(&am, "+"),
|
||||
expr_pair(
|
||||
&am, expr_integer(&am, 1),
|
||||
expr_pair(
|
||||
&am,
|
||||
expr_pair(
|
||||
&am, expr_str_symbol(&am, "*"),
|
||||
expr_pair(
|
||||
&am, expr_integer(&am, 2),
|
||||
expr_pair(
|
||||
&am, expr_integer(&am, 3),
|
||||
expr_empty_list(&am)))),
|
||||
expr_empty_list(&am))));
|
||||
|
||||
eval(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(7, am.regs[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);
|
||||
RUN_TEST(test_add_1_mul_2_3_evals_to_7);
|
||||
return UNITY_END();
|
||||
}
|
||||
123
tests/expr_tests.c
Normal file
123
tests/expr_tests.c
Normal 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();
|
||||
}
|
||||
55
tests/integration_tests.c
Normal file
55
tests/integration_tests.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#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)"));
|
||||
}
|
||||
|
||||
static void test_nested_arithmetic(void)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_STRING("20", read_eval_print("(+ 1 (* 2 (+ 3 4)) 5)"));
|
||||
TEST_ASSERT_EQUAL_STRING("-6", read_eval_print("(+ 1 (* 2 2 (- 3)) 5)"));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_unnested_arithmetic);
|
||||
RUN_TEST(test_nested_arithmetic);
|
||||
return UNITY_END();
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "parse.h"
|
||||
#include "unity.h"
|
||||
|
||||
static store_t store;
|
||||
static am_t am;
|
||||
static parse_ctx_t ctx;
|
||||
|
||||
@@ -9,9 +8,8 @@ static parse_ctx_t ctx;
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
store_init(&store);
|
||||
am_init(&am);
|
||||
parse_init(&am, &store, &ctx);
|
||||
parse_init(&am, &ctx);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
@@ -25,10 +23,10 @@ static void test_integer_123(void)
|
||||
const parse_state_t state = parse_proc(&ctx, &token);
|
||||
TEST_ASSERT_EQUAL(PARSE_STATE_DONE, state);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr);
|
||||
TEST_ASSERT_TRUE(am.expr->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.expr->atom.type);
|
||||
TEST_ASSERT_EQUAL(123, am.expr->atom.integer);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[EXPR]->atom.type);
|
||||
TEST_ASSERT_EQUAL(123, am.regs[EXPR]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_integer_321(void)
|
||||
@@ -38,10 +36,10 @@ static void test_integer_321(void)
|
||||
const parse_state_t state = parse_proc(&ctx, &token);
|
||||
TEST_ASSERT_EQUAL(PARSE_STATE_DONE, state);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr);
|
||||
TEST_ASSERT_TRUE(am.expr->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.expr->atom.type);
|
||||
TEST_ASSERT_EQUAL(321, am.expr->atom.integer);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[EXPR]->atom.type);
|
||||
TEST_ASSERT_EQUAL(321, am.regs[EXPR]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_symbol_foo(void)
|
||||
@@ -54,11 +52,11 @@ static void test_symbol_foo(void)
|
||||
const parse_state_t state = parse_proc(&ctx, &token);
|
||||
TEST_ASSERT_EQUAL(PARSE_STATE_DONE, state);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr);
|
||||
TEST_ASSERT_TRUE(am.expr->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, am.expr->atom.type);
|
||||
TEST_ASSERT_EQUAL(3, am.expr->atom.symbol.len);
|
||||
TEST_ASSERT_EQUAL_MEMORY("foo", am.expr->atom.symbol.buf, 3);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, am.regs[EXPR]->atom.type);
|
||||
TEST_ASSERT_EQUAL(3, am.regs[EXPR]->atom.symbol.len);
|
||||
TEST_ASSERT_EQUAL_MEMORY("foo", am.regs[EXPR]->atom.symbol.buf, 3);
|
||||
}
|
||||
|
||||
static void test_symbol_quux(void)
|
||||
@@ -71,11 +69,11 @@ static void test_symbol_quux(void)
|
||||
const parse_state_t state = parse_proc(&ctx, &token);
|
||||
TEST_ASSERT_EQUAL(PARSE_STATE_DONE, state);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr);
|
||||
TEST_ASSERT_TRUE(am.expr->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, am.expr->atom.type);
|
||||
TEST_ASSERT_EQUAL(4, am.expr->atom.symbol.len);
|
||||
TEST_ASSERT_EQUAL_MEMORY("quux", am.expr->atom.symbol.buf, 4);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, am.regs[EXPR]->atom.type);
|
||||
TEST_ASSERT_EQUAL(4, am.regs[EXPR]->atom.symbol.len);
|
||||
TEST_ASSERT_EQUAL_MEMORY("quux", am.regs[EXPR]->atom.symbol.buf, 4);
|
||||
}
|
||||
|
||||
static void test_open_paren_close_paren(void)
|
||||
@@ -92,9 +90,9 @@ static void test_open_paren_close_paren(void)
|
||||
state = parse_proc(&ctx, tokens + 1);
|
||||
TEST_ASSERT_EQUAL(PARSE_STATE_DONE, state);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr);
|
||||
TEST_ASSERT_TRUE(am.expr->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_EMPTY_LIST, am.expr->atom.type);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_EMPTY_LIST, am.regs[EXPR]->atom.type);
|
||||
}
|
||||
|
||||
static void test_open_paren_foo_42_close_paren(void)
|
||||
@@ -118,28 +116,29 @@ static void test_open_paren_foo_42_close_paren(void)
|
||||
state = parse_proc(&ctx, tokens + NELEMS(tokens) - 1);
|
||||
TEST_ASSERT_EQUAL(PARSE_STATE_DONE, state);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr);
|
||||
TEST_ASSERT_FALSE(am.expr->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_FALSE(am.regs[EXPR]->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.car);
|
||||
TEST_ASSERT_TRUE(am.expr->pair.car->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, am.expr->pair.car->atom.type);
|
||||
TEST_ASSERT_EQUAL(3, am.expr->pair.car->atom.symbol.len);
|
||||
TEST_ASSERT_EQUAL_MEMORY("foo", am.expr->pair.car->atom.symbol.buf, 3);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.car);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->pair.car->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, am.regs[EXPR]->pair.car->atom.type);
|
||||
TEST_ASSERT_EQUAL(3, am.regs[EXPR]->pair.car->atom.symbol.len);
|
||||
TEST_ASSERT_EQUAL_MEMORY(
|
||||
"foo", am.regs[EXPR]->pair.car->atom.symbol.buf, 3);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr);
|
||||
TEST_ASSERT_FALSE(am.expr->pair.cdr->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr);
|
||||
TEST_ASSERT_FALSE(am.regs[EXPR]->pair.cdr->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr->pair.car);
|
||||
TEST_ASSERT_TRUE(am.expr->pair.cdr->pair.car->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr->pair.car);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->pair.cdr->pair.car->is_atom);
|
||||
TEST_ASSERT_EQUAL(
|
||||
ATOM_TYPE_INTEGER, am.expr->pair.cdr->pair.car->atom.type);
|
||||
TEST_ASSERT_EQUAL(42, am.expr->pair.cdr->pair.car->atom.integer);
|
||||
ATOM_TYPE_INTEGER, am.regs[EXPR]->pair.cdr->pair.car->atom.type);
|
||||
TEST_ASSERT_EQUAL(42, am.regs[EXPR]->pair.cdr->pair.car->atom.integer);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr->pair.cdr);
|
||||
TEST_ASSERT_TRUE(am.expr->pair.cdr->pair.cdr->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr->pair.cdr);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->pair.cdr->pair.cdr->is_atom);
|
||||
TEST_ASSERT_EQUAL(
|
||||
ATOM_TYPE_EMPTY_LIST, am.expr->pair.cdr->pair.cdr->atom.type);
|
||||
ATOM_TYPE_EMPTY_LIST, am.regs[EXPR]->pair.cdr->pair.cdr->atom.type);
|
||||
}
|
||||
|
||||
static void test_open_paren_1_open_paren_2_close_paren_3_close_paren(void)
|
||||
@@ -163,48 +162,50 @@ static void test_open_paren_1_open_paren_2_close_paren_3_close_paren(void)
|
||||
state = parse_proc(&ctx, tokens + NELEMS(tokens) - 1);
|
||||
TEST_ASSERT_EQUAL(PARSE_STATE_DONE, state);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr);
|
||||
TEST_ASSERT_FALSE(am.expr->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]);
|
||||
TEST_ASSERT_FALSE(am.regs[EXPR]->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.car);
|
||||
TEST_ASSERT_TRUE(am.expr->pair.car->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.expr->pair.car->atom.type);
|
||||
TEST_ASSERT_EQUAL(1, am.expr->pair.car->atom.integer);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.car);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->pair.car->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[EXPR]->pair.car->atom.type);
|
||||
TEST_ASSERT_EQUAL(1, am.regs[EXPR]->pair.car->atom.integer);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr);
|
||||
TEST_ASSERT_FALSE(am.expr->pair.cdr->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr);
|
||||
TEST_ASSERT_FALSE(am.regs[EXPR]->pair.cdr->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr->pair.car);
|
||||
TEST_ASSERT_FALSE(am.expr->pair.cdr->pair.car->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr->pair.car);
|
||||
TEST_ASSERT_FALSE(am.regs[EXPR]->pair.cdr->pair.car->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr->pair.car->pair.car);
|
||||
TEST_ASSERT_TRUE(am.expr->pair.cdr->pair.car->pair.car->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr->pair.car->pair.car);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->pair.cdr->pair.car->pair.car->is_atom);
|
||||
TEST_ASSERT_EQUAL(
|
||||
ATOM_TYPE_INTEGER, am.expr->pair.cdr->pair.car->pair.car->atom.type);
|
||||
ATOM_TYPE_INTEGER,
|
||||
am.regs[EXPR]->pair.cdr->pair.car->pair.car->atom.type);
|
||||
TEST_ASSERT_EQUAL(
|
||||
2, am.expr->pair.cdr->pair.car->pair.car->atom.integer);
|
||||
2, am.regs[EXPR]->pair.cdr->pair.car->pair.car->atom.integer);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr->pair.car->pair.cdr);
|
||||
TEST_ASSERT_TRUE(am.expr->pair.cdr->pair.car->pair.cdr->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr->pair.car->pair.cdr);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->pair.cdr->pair.car->pair.cdr->is_atom);
|
||||
TEST_ASSERT_EQUAL(
|
||||
ATOM_TYPE_EMPTY_LIST,
|
||||
am.expr->pair.cdr->pair.car->pair.cdr->atom.type);
|
||||
am.regs[EXPR]->pair.cdr->pair.car->pair.cdr->atom.type);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr->pair.cdr);
|
||||
TEST_ASSERT_FALSE(am.expr->pair.cdr->pair.cdr->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr->pair.cdr);
|
||||
TEST_ASSERT_FALSE(am.regs[EXPR]->pair.cdr->pair.cdr->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr->pair.cdr->pair.car);
|
||||
TEST_ASSERT_TRUE(am.expr->pair.cdr->pair.cdr->pair.car->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr->pair.cdr->pair.car);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->pair.cdr->pair.cdr->pair.car->is_atom);
|
||||
TEST_ASSERT_EQUAL(
|
||||
ATOM_TYPE_INTEGER, am.expr->pair.cdr->pair.cdr->pair.car->atom.type);
|
||||
ATOM_TYPE_INTEGER,
|
||||
am.regs[EXPR]->pair.cdr->pair.cdr->pair.car->atom.type);
|
||||
TEST_ASSERT_EQUAL(
|
||||
3, am.expr->pair.cdr->pair.cdr->pair.car->atom.integer);
|
||||
3, am.regs[EXPR]->pair.cdr->pair.cdr->pair.car->atom.integer);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.expr->pair.cdr->pair.cdr->pair.cdr);
|
||||
TEST_ASSERT_TRUE(am.expr->pair.cdr->pair.cdr->pair.cdr->is_atom);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[EXPR]->pair.cdr->pair.cdr->pair.cdr);
|
||||
TEST_ASSERT_TRUE(am.regs[EXPR]->pair.cdr->pair.cdr->pair.cdr->is_atom);
|
||||
TEST_ASSERT_EQUAL(
|
||||
ATOM_TYPE_EMPTY_LIST,
|
||||
am.expr->pair.cdr->pair.cdr->pair.cdr->atom.type);
|
||||
am.regs[EXPR]->pair.cdr->pair.cdr->pair.cdr->atom.type);
|
||||
}
|
||||
|
||||
static void test_close_paren(void)
|
||||
|
||||
141
tests/prim_tests.c
Normal file
141
tests/prim_tests.c
Normal file
@@ -0,0 +1,141 @@
|
||||
#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.regs[EXPR] = expr_str_symbol(&am, "+");
|
||||
env_fetch(&am);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.regs[VAL]->atom.type);
|
||||
|
||||
am.regs[ARGL] = expr_empty_list(&am);
|
||||
am.regs[VAL]->atom.prim_proc(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(0, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_add_1_2_3_is_6(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "+");
|
||||
env_fetch(&am);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.regs[VAL]->atom.type);
|
||||
|
||||
am.regs[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.regs[VAL]->atom.prim_proc(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(6, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_mul_empty_list_is_1(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "*");
|
||||
env_fetch(&am);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.regs[VAL]->atom.type);
|
||||
|
||||
am.regs[ARGL] = expr_empty_list(&am);
|
||||
am.regs[VAL]->atom.prim_proc(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(1, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_mul_2_3_4_is_24(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "*");
|
||||
env_fetch(&am);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.regs[VAL]->atom.type);
|
||||
|
||||
am.regs[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.regs[VAL]->atom.prim_proc(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(24, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_sub_1_is_minus_1(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "-");
|
||||
env_fetch(&am);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.regs[VAL]->atom.type);
|
||||
|
||||
am.regs[ARGL]
|
||||
= expr_pair(&am, expr_integer(&am, 1), expr_empty_list(&am));
|
||||
am.regs[VAL]->atom.prim_proc(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(-1, am.regs[VAL]->atom.integer);
|
||||
}
|
||||
|
||||
static void test_sub_5_4_3_is_minus_2(void)
|
||||
{
|
||||
am.regs[EXPR] = expr_str_symbol(&am, "-");
|
||||
env_fetch(&am);
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_PRIM_PROC, am.regs[VAL]->atom.type);
|
||||
|
||||
am.regs[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.regs[VAL]->atom.prim_proc(&am);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(am.regs[VAL]);
|
||||
TEST_ASSERT_TRUE(am.regs[VAL]->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, am.regs[VAL]->atom.type);
|
||||
TEST_ASSERT_EQUAL(-2, am.regs[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();
|
||||
}
|
||||
70
tests/print_tests.c
Normal file
70
tests/print_tests.c
Normal file
@@ -0,0 +1,70 @@
|
||||
#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.regs[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.regs[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.regs[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.regs[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);
|
||||
}
|
||||
|
||||
static void test_integer_minus_4321(void)
|
||||
{
|
||||
am.regs[VAL] = expr_integer(&am, -4321);
|
||||
const size_t len = print(&am, buffer, BUFFER_SIZE);
|
||||
TEST_ASSERT_EQUAL(5, len);
|
||||
TEST_ASSERT_EQUAL_MEMORY("-4321", buffer, 5);
|
||||
}
|
||||
|
||||
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);
|
||||
RUN_TEST(test_integer_minus_4321);
|
||||
return UNITY_END();
|
||||
}
|
||||
99
tests/read_tests.c
Normal file
99
tests/read_tests.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#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.regs[EXPR]);
|
||||
TEST_ASSERT_FALSE(am.regs[EXPR]->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CAR(am.regs[EXPR]));
|
||||
TEST_ASSERT_TRUE(CAR(am.regs[EXPR])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, CAR(am.regs[EXPR])->atom.type);
|
||||
TEST_ASSERT_EQUAL(1, CAR(am.regs[EXPR])->atom.symbol.len);
|
||||
TEST_ASSERT_EQUAL_MEMORY("+", CAR(am.regs[EXPR])->atom.symbol.buf, 1);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_FALSE(CDR(am.regs[EXPR])->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CADR(am.regs[EXPR]));
|
||||
TEST_ASSERT_TRUE(CADR(am.regs[EXPR])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, CADR(am.regs[EXPR])->atom.type);
|
||||
TEST_ASSERT_EQUAL(1, CADR(am.regs[EXPR])->atom.integer);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CDDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_FALSE(CDDR(am.regs[EXPR])->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CADDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_FALSE(CADDR(am.regs[EXPR])->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CAADDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_TRUE(CAADDR(am.regs[EXPR])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_SYMBOL, CAADDR(am.regs[EXPR])->atom.type);
|
||||
TEST_ASSERT_EQUAL(1, CAADDR(am.regs[EXPR])->atom.symbol.len);
|
||||
TEST_ASSERT_EQUAL_MEMORY("*", CAADDR(am.regs[EXPR])->atom.symbol.buf, 1);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CDADDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_FALSE(CDADDR(am.regs[EXPR])->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CADADDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_TRUE(CADADDR(am.regs[EXPR])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, CADADDR(am.regs[EXPR])->atom.type);
|
||||
TEST_ASSERT_EQUAL(2, CADADDR(am.regs[EXPR])->atom.integer);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CDDADDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_FALSE(CDDADDR(am.regs[EXPR])->is_atom);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CADDADDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_TRUE(CADDADDR(am.regs[EXPR])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_INTEGER, CADDADDR(am.regs[EXPR])->atom.type);
|
||||
TEST_ASSERT_EQUAL(3, CADDADDR(am.regs[EXPR])->atom.integer);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CDDDADDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_TRUE(CDDDADDR(am.regs[EXPR])->is_atom);
|
||||
TEST_ASSERT_EQUAL(
|
||||
ATOM_TYPE_EMPTY_LIST, CDDDADDR(am.regs[EXPR])->atom.type);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(CDDDR(am.regs[EXPR]));
|
||||
TEST_ASSERT_TRUE(CDDDR(am.regs[EXPR])->is_atom);
|
||||
TEST_ASSERT_EQUAL(ATOM_TYPE_EMPTY_LIST, CDDDR(am.regs[EXPR])->atom.type);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_nested_expression);
|
||||
return UNITY_END();
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "store.h"
|
||||
#include "am.h"
|
||||
#include "unity.h"
|
||||
|
||||
static store_t store;
|
||||
static am_t am;
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
store_init(&store);
|
||||
am_init(&am);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
@@ -14,14 +14,14 @@ void tearDown(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);
|
||||
}
|
||||
|
||||
static void test_two_calls_to_alloc_return_distinct(void)
|
||||
{
|
||||
const expr_t *const a = store_alloc(&store);
|
||||
const expr_t *const b = store_alloc(&store);
|
||||
const expr_t *const a = store_alloc(&am);
|
||||
const expr_t *const b = store_alloc(&am);
|
||||
TEST_ASSERT_NOT_EQUAL(a, b);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user