1
0
Fork 0

Split REPL and tests

This commit is contained in:
Alex Kotov 2023-05-05 15:35:34 +04:00
parent dfab67c3b4
commit 90d373944b
Signed by: kotovalexarian
GPG Key ID: 553C0EBBEB5D5F08
6 changed files with 801 additions and 786 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/src/*.o
/lisp
/lisp-test

View File

@ -6,22 +6,28 @@ CFLAGS = -Wall -Wextra
OBJS = \
src/builtins.c.o \
src/ctype.c.o \
src/eval.c.o \
src/lexer.c.o \
src/main.c.o \
src/object.c.o \
src/parser.c.o \
src/tokens.c.o
MAIN_OBJS = $(OBJS) src/main.c.o
TEST_OBJS = $(OBJS) src/main-test.c.o
repl: lisp
./lisp
test: lisp
./lisp --test
test: lisp-test
./lisp-test
clean:
rm -f lisp $(OBJS)
rm -f lisp $(MAIN_OBJS) $(TEST_OBJS)
lisp: $(OBJS)
lisp: $(MAIN_OBJS)
$(CC) -o $@ $^ $(CFLAGS)
lisp-test: $(TEST_OBJS)
$(CC) -o $@ $^ $(CFLAGS)
%.c.o: %.c

112
src/eval.c Normal file
View File

@ -0,0 +1,112 @@
#include "eval.h"
#include "builtins.h"
#include "lexer.h"
#include "parser.h"
#include <assert.h>
#include <stddef.h>
#include <string.h>
static struct Object *eval_list(struct Object *object);
struct Object *eval_str(const char *const str)
{
assert(str);
Tokens tokens = Tokens_new();
assert(tokens);
Lexer lexer = Lexer_new(tokens);
assert(lexer);
for (const char *chr = str; *chr; ++chr) {
Lexer_lex(lexer, *chr);
}
Lexer_lex(lexer, '\n');
LEXER_DELETE(lexer);
assert(Tokens_top(tokens));
struct Object *const program = parse(tokens);
TOKENS_DELETE(tokens);
return eval(program);
}
struct Object *eval(struct Object *const object)
{
// NULL is an empty list, can't eval
assert(object);
// SYMBOL performs lookup
if (object->type == TYPE_SYMBOL) {
struct Object *const procedure = builtins_get(object->s);
assert(procedure);
return procedure;
}
// Almost everything evaluates to itself
if (object->type != TYPE_PAIR) return object;
struct Object *const func_expr = object->pair.a;
struct Object *const args = object->pair.b;
if (func_expr && func_expr->type == TYPE_SYMBOL) {
if (strcmp(func_expr->s, "quote") == 0) {
assert(args);
assert(args->type == TYPE_PAIR);
assert(args->pair.b == NULL);
return args->pair.a;
}
if (strcmp(func_expr->s, "if") == 0) {
assert(args);
assert(args->type == TYPE_PAIR);
struct Object *const cond = args->pair.a;
struct Object *const then_else_list = args->pair.b;
assert(then_else_list);
assert(then_else_list->type == TYPE_PAIR);
struct Object *const then_branch = then_else_list->pair.a;
struct Object *const else_list = then_else_list->pair.b;
assert(else_list);
assert(else_list->type == TYPE_PAIR);
struct Object *const else_branch = else_list->pair.a;
assert(else_list->pair.b == NULL);
if (Object_is_false(cond)) {
return eval(else_branch);
} else {
return eval(then_branch);
}
}
}
struct Object *const func = eval(func_expr);
assert(func);
if (func->type == TYPE_PROCEDURE) {
return func->procedure.func(eval_list(args));
} else {
assert(0);
}
}
struct Object *eval_list(struct Object *const object)
{
if (!object) {
return NULL;
} else if (object->type != TYPE_PAIR) {
return eval(object);
} else if (!object->pair.a) {
return Object_new_pair(
NULL,
eval_list(object->pair.b)
);
} else {
return Object_new_pair(
eval(object->pair.a),
eval_list(object->pair.b)
);
}
}

9
src/eval.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __LISP_EVAL_H__
#define __LISP_EVAL_H__
#include "object.h"
struct Object *eval_str(const char * str);
struct Object *eval(struct Object *object);
#endif

664
src/main-test.c Normal file
View File

@ -0,0 +1,664 @@
#include "eval.h"
#include "object.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
// Macros
static void test_if();
// Basic data structures
static void test_car();
static void test_cdr();
static void test_list();
// Type predicates
static void test_booleanQN();
static void test_charQN();
static void test_nullQN();
static void test_numberQN();
static void test_pairQN();
static void test_procedureQN();
static void test_stringQN();
static void test_symbolQN();
// Type conversion
static void test_numberTOstring();
// Logical operators
static void test_not();
int main()
{
// Macros
test_if();
// Basic data structures
test_car();
test_cdr();
test_list();
// Type predicates
test_booleanQN();
test_charQN();
test_nullQN();
test_numberQN();
test_pairQN();
test_procedureQN();
test_stringQN();
test_symbolQN();
// Type conversion
test_numberTOstring();
// Logical operators
test_not();
struct Object *const sym_foo = Object_new_symbol("foo");
struct Object *const sharp_true = Object_new_boolean(true);
struct Object *const num_123 = Object_new_number(123);
// #true
// #true
assert(eval(sharp_true) == sharp_true);
// 123
// 123
assert(eval(num_123) == num_123);
// (quote foo)
// 'foo
assert(
eval(
Object_build_list(2, Object_new_symbol("quote"), sym_foo)
) == sym_foo
);
// (quote #true)
// #true
assert(
eval(
Object_build_list(2, Object_new_symbol("quote"), sharp_true)
) == sharp_true
);
// (quote ())
// '()
assert(eval_str("(quote ())") == NULL);
// (quote 123)
// 123
assert(
eval(
Object_build_list(2, Object_new_symbol("quote"), num_123)
) == num_123
);
exit(EXIT_SUCCESS);
}
/**********
* Macros *
**********/
void test_if()
{
struct Object *const num_123 = Object_new_number(123);
struct Object *const num_456 = Object_new_number(456);
// (if #true 123 456)
assert(eval(Object_build_list(
4,
Object_new_symbol("if"),
Object_new_boolean(true),
num_123,
num_456
)) == num_123);
// (if "foo" 123 456)
assert(eval(Object_build_list(
4,
Object_new_symbol("if"),
Object_new_string("foo"),
num_123,
num_456
)) == num_123);
// (if #false 123 456)
assert(eval(Object_build_list(
4,
Object_new_symbol("if"),
Object_new_boolean(false),
num_123,
num_456
)) == num_456);
}
/*************************
* Basic data structures *
*************************/
void test_car()
{
struct Object *const num_123 = Object_new_number(123);
struct Object *const num_456 = Object_new_number(456);
// (car (cons 123 456))
// 123
assert(eval(Object_build_list(
2,
Object_new_symbol("car"),
Object_build_list(
3,
Object_new_symbol("cons"),
num_123,
num_456
)
)) == num_123);
}
void test_cdr()
{
struct Object *const num_123 = Object_new_number(123);
struct Object *const num_456 = Object_new_number(456);
// (cdr (cons 123 456))
// 456
assert(eval(Object_build_list(
2,
Object_new_symbol("cdr"),
Object_build_list(
3,
Object_new_symbol("cons"),
num_123,
num_456
)
)) == num_456);
}
void test_list()
{
struct Object *result = NULL;
struct Object *const num_123 = Object_new_number(123);
struct Object *const num_456 = Object_new_number(456);
// (list)
// '()
assert(eval_str("(list)") == NULL);
// (list 123)
// '(123)
result = eval(Object_build_list(
2,
Object_new_symbol("list"),
num_123
));
assert(Object_is_pair(result));
assert(result->pair.a == num_123);
assert(OBJECT_IS_NULL(result->pair.b));
// (list 123 456)
// '(123 456)
result = eval(Object_build_list(
3,
Object_new_symbol("list"),
num_123,
num_456
));
assert(Object_is_pair(result));
assert(result->pair.a == num_123);
assert(Object_is_pair(result->pair.b));
assert(result->pair.b->pair.a == num_456);
assert(OBJECT_IS_NULL(result->pair.b->pair.b));
}
/*******************
* Type predicates *
*******************/
void test_booleanQN()
{
// (boolean? +)
// #false
assert(Object_is_false(eval_str("(boolean? +)")));
// (boolean? '())
// #false
assert(Object_is_false(eval_str("(boolean? '())")));
// (boolean? #true)
// #true
assert(Object_is_true(eval_str("(boolean? #true)")));
// (boolean? #false)
// #true
assert(Object_is_true(eval_str("(boolean? #false)")));
// (boolean? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("boolean?"),
Object_new_char('\n')
))));
// (boolean? 'foo)
// #false
assert(Object_is_false(eval_str("(boolean? 'foo)")));
// (boolean? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("boolean?"),
Object_new_string("foo")
))));
// (boolean? 123)
// #false
assert(Object_is_false(eval_str("(boolean? 123)")));
// (boolean? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(boolean? (cons 123 456))")));
}
void test_charQN()
{
// (char? +)
// #false
assert(Object_is_false(eval_str("(char? +)")));
// (char? '())
// #false
assert(Object_is_false(eval_str("(char? '())")));
// (char? #true)
// #false
assert(Object_is_false(eval_str("(char? #true)")));
// (char? #false)
// #false
assert(Object_is_false(eval_str("(char? #false)")));
// (char? #\n)
// #true
assert(Object_is_true(eval(Object_build_list(
2,
Object_new_symbol("char?"),
Object_new_char('\n')
))));
// (char? 'foo)
// #false
assert(Object_is_false(eval_str("(char? 'foo)")));
// (char? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("char?"),
Object_new_string("foo")
))));
// (char? 123)
// #false
assert(Object_is_false(eval_str("(char? 123)")));
// (char? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(char? (cons 123 456))")));
}
void test_nullQN()
{
// (null? +)
// #false
assert(Object_is_false(eval_str("(null? +)")));
// (null? '())
// #true
assert(Object_is_true(eval_str("(null? '())")));
// (null? #true)
// #false
assert(Object_is_false(eval_str("(null? #true)")));
// (null? #false)
// #false
assert(Object_is_false(eval_str("(null? #false)")));
// (null? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("null?"),
Object_new_char('\n')
))));
// (null? 'foo)
// #false
assert(Object_is_false(eval_str("(null? 'foo)")));
// (null? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("null?"),
Object_new_string("foo")
))));
// (null? 123)
// #false
assert(Object_is_false(eval_str("(null? 123)")));
// (null? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(null? (cons 123 456))")));
}
void test_numberQN()
{
// (number? +)
// #false
assert(Object_is_false(eval_str("(number? +)")));
// (number? '())
// #false
assert(Object_is_false(eval_str("(number? '())")));
// (number? #true)
// #false
assert(Object_is_false(eval_str("(number? #true)")));
// (number? #false)
// #false
assert(Object_is_false(eval_str("(number? #false)")));
// (number? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("number?"),
Object_new_char('\n')
))));
// (number? 'foo)
// #false
assert(Object_is_false(eval_str("(number? 'foo)")));
// (number? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("number?"),
Object_new_string("foo")
))));
// (number? 123)
// #true
assert(Object_is_true(eval_str("(number? 123)")));
// (number? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(number? (cons 123 456))")));
}
void test_pairQN()
{
// (pair? +)
// #false
assert(Object_is_false(eval_str("(pair? +)")));
// (pair? '())
// #false
assert(Object_is_false(eval_str("(pair? '())")));
// (pair? #true)
// #false
assert(Object_is_false(eval_str("(pair? #true)")));
// (pair? #false)
// #false
assert(Object_is_false(eval_str("(pair? #false)")));
// (pair? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("pair?"),
Object_new_char('\n')
))));
// (pair? 'foo)
// #false
assert(Object_is_false(eval_str("(pair? 'foo)")));
// (pair? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("pair?"),
Object_new_string("foo")
))));
// (pair? 123)
// #false
assert(Object_is_false(eval_str("(pair? 123)")));
// (pair? (cons 123 456))
// #true
assert(Object_is_true(eval_str("(pair? (cons 123 456))")));
}
void test_procedureQN()
{
// (procedure? +)
// #true
assert(Object_is_true(eval_str("(procedure? +)")));
// (procedure? '())
// #false
assert(Object_is_false(eval_str("(procedure? '())")));
// (procedure? #true)
// #false
assert(Object_is_false(eval_str("(procedure? #true)")));
// (procedure? #false)
// #false
assert(Object_is_false(eval_str("(procedure? #false)")));
// (procedure? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("procedure?"),
Object_new_char('\n')
))));
// (procedure? 'foo)
// #false
assert(Object_is_false(eval_str("(procedure? 'foo)")));
// (procedure? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("procedure?"),
Object_new_string("foo")
))));
// (procedure? 123)
// #false
assert(Object_is_false(eval_str("(procedure? 123)")));
// (procedure? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(procedure? (cons 123 456))")));
}
void test_stringQN()
{
// (string? +)
// #false
assert(Object_is_false(eval_str("(string? +)")));
// (string? '())
// #false
assert(Object_is_false(eval_str("(string? '())")));
// (string? #true)
// #false
assert(Object_is_false(eval_str("(string? #true)")));
// (string? #false)
// #false
assert(Object_is_false(eval_str("(string? #false)")));
// (string? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("string?"),
Object_new_char('\n')
))));
// (string? 'foo)
// #false
assert(Object_is_false(eval_str("(string? 'foo)")));
// (string? "foo")
// #true
assert(Object_is_true(eval(Object_build_list(
2,
Object_new_symbol("string?"),
Object_new_string("foo")
))));
// (string? 123)
// #false
assert(Object_is_false(eval_str("(string? 123)")));
// (string? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(string? (cons 123 456))")));
}
void test_symbolQN()
{
// (symbol? +)
// #false
assert(Object_is_false(eval_str("(symbol? +)")));
// (symbol? '())
// #false
assert(Object_is_false(eval_str("(symbol? '())")));
// (symbol? #true)
// #false
assert(Object_is_false(eval_str("(symbol? #true)")));
// (symbol? #false)
// #false
assert(Object_is_false(eval_str("(symbol? #false)")));
// (symbol? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("symbol?"),
Object_new_char('\n')
))));
// (symbol? 'foo)
// #true
assert(Object_is_true(eval_str("(symbol? 'foo)")));
// (symbol? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("symbol?"),
Object_new_string("foo")
))));
// (symbol? 123)
// #false
assert(Object_is_false(eval_str("(symbol? 123)")));
// (symbol? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(symbol? (cons 123 456))")));
}
/*******************
* Type conversion *
*******************/
void test_numberTOstring()
{
struct Object *result = NULL;
// (number->string 123)
// "123"
result = eval_str("(number->string 123)");
assert(Object_is_string(result));
assert(strcmp(result->s, "123") == 0);
// (number->string -123)
// "-123"
result = eval(Object_build_list(
2,
Object_new_symbol("number->string"),
Object_new_number(-123)
));
assert(Object_is_string(result));
assert(strcmp(result->s, "-123") == 0);
// (number->string 123456 16)
// "1e240"
result = eval_str("(number->string 123456 16)");
assert(Object_is_string(result));
assert(strcmp(result->s, "1e240") == 0);
}
/*********************
* Logical operators *
*********************/
void test_not()
{
// (not '())
// #false
assert(Object_is_false(eval_str("(not '())")));
// (not #true)
// #false
assert(Object_is_false(eval_str("(not #true)")));
// (not #false)
// #true
assert(Object_is_true(eval_str("(not #false)")));
// (not #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("not"),
Object_new_char('\n')
))));
// (not 'foo)
// #false
assert(Object_is_false(eval_str("(not 'foo)")));
// (not "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("not"),
Object_new_string("foo")
))));
// (not 123)
// #false
assert(Object_is_false(eval_str("(not 123)")));
// (not (cons 123 456))
// #false
assert(Object_is_false(eval_str("(not (cons 123 456))")));
}

View File

@ -1,4 +1,4 @@
#include "builtins.h"
#include "eval.h"
#include "lexer.h"
#include "object.h"
#include "parser.h"
@ -11,130 +11,7 @@
#include <stdlib.h>
#include <string.h>
static void repl();
static void test();
int main(int argc, char **argv)
{
assert(argc == 1 || argc == 2);
if (argc == 1) {
repl();
} else if (argc == 2 && strcmp(argv[1], "--test") == 0) {
test();
} else {
abort();
}
exit(EXIT_SUCCESS);
}
static struct Object *eval_str(const char * str);
static struct Object *eval(struct Object *object);
static struct Object *eval_list(struct Object *object);
struct Object *eval_str(const char *const str)
{
assert(str);
Tokens tokens = Tokens_new();
assert(tokens);
Lexer lexer = Lexer_new(tokens);
assert(lexer);
for (const char *chr = str; *chr; ++chr) {
Lexer_lex(lexer, *chr);
}
Lexer_lex(lexer, '\n');
LEXER_DELETE(lexer);
assert(Tokens_top(tokens));
struct Object *const program = parse(tokens);
TOKENS_DELETE(tokens);
return eval(program);
}
struct Object *eval(struct Object *const object)
{
// NULL is an empty list, can't eval
assert(object);
// SYMBOL performs lookup
if (object->type == TYPE_SYMBOL) {
struct Object *const procedure = builtins_get(object->s);
assert(procedure);
return procedure;
}
// Almost everything evaluates to itself
if (object->type != TYPE_PAIR) return object;
struct Object *const func_expr = object->pair.a;
struct Object *const args = object->pair.b;
if (func_expr && func_expr->type == TYPE_SYMBOL) {
if (strcmp(func_expr->s, "quote") == 0) {
assert(args);
assert(args->type == TYPE_PAIR);
assert(args->pair.b == NULL);
return args->pair.a;
}
if (strcmp(func_expr->s, "if") == 0) {
assert(args);
assert(args->type == TYPE_PAIR);
struct Object *const cond = args->pair.a;
struct Object *const then_else_list = args->pair.b;
assert(then_else_list);
assert(then_else_list->type == TYPE_PAIR);
struct Object *const then_branch = then_else_list->pair.a;
struct Object *const else_list = then_else_list->pair.b;
assert(else_list);
assert(else_list->type == TYPE_PAIR);
struct Object *const else_branch = else_list->pair.a;
assert(else_list->pair.b == NULL);
if (Object_is_false(cond)) {
return eval(else_branch);
} else {
return eval(then_branch);
}
}
}
struct Object *const func = eval(func_expr);
assert(func);
if (func->type == TYPE_PROCEDURE) {
return func->procedure.func(eval_list(args));
} else {
assert(0);
}
}
struct Object *eval_list(struct Object *const object)
{
if (!object) {
return NULL;
} else if (object->type != TYPE_PAIR) {
return eval(object);
} else if (!object->pair.a) {
return Object_new_pair(
NULL,
eval_list(object->pair.b)
);
} else {
return Object_new_pair(
eval(object->pair.a),
eval_list(object->pair.b)
);
}
}
void repl()
int main()
{
while (true) {
Tokens tokens = Tokens_new();
@ -170,660 +47,6 @@ void repl()
printf("=> ");
eval(Object_build_list(2, Object_new_symbol("displayln"), result));
}
}
// Macros
static void test_if();
// Basic data structures
static void test_car();
static void test_cdr();
static void test_list();
// Type predicates
static void test_booleanQN();
static void test_charQN();
static void test_nullQN();
static void test_numberQN();
static void test_pairQN();
static void test_procedureQN();
static void test_stringQN();
static void test_symbolQN();
// Type conversion
static void test_numberTOstring();
// Logical operators
static void test_not();
void test()
{
// Macros
test_if();
// Basic data structures
test_car();
test_cdr();
test_list();
// Type predicates
test_booleanQN();
test_charQN();
test_nullQN();
test_numberQN();
test_pairQN();
test_procedureQN();
test_stringQN();
test_symbolQN();
// Type conversion
test_numberTOstring();
// Logical operators
test_not();
struct Object *const sym_foo = Object_new_symbol("foo");
struct Object *const sharp_true = Object_new_boolean(true);
struct Object *const num_123 = Object_new_number(123);
// #true
// #true
assert(eval(sharp_true) == sharp_true);
// 123
// 123
assert(eval(num_123) == num_123);
// (quote foo)
// 'foo
assert(
eval(
Object_build_list(2, Object_new_symbol("quote"), sym_foo)
) == sym_foo
);
// (quote #true)
// #true
assert(
eval(
Object_build_list(2, Object_new_symbol("quote"), sharp_true)
) == sharp_true
);
// (quote ())
// '()
assert(eval_str("(quote ())") == NULL);
// (quote 123)
// 123
assert(
eval(
Object_build_list(2, Object_new_symbol("quote"), num_123)
) == num_123
);
}
/**********
* Macros *
**********/
void test_if()
{
struct Object *const num_123 = Object_new_number(123);
struct Object *const num_456 = Object_new_number(456);
// (if #true 123 456)
assert(eval(Object_build_list(
4,
Object_new_symbol("if"),
Object_new_boolean(true),
num_123,
num_456
)) == num_123);
// (if "foo" 123 456)
assert(eval(Object_build_list(
4,
Object_new_symbol("if"),
Object_new_string("foo"),
num_123,
num_456
)) == num_123);
// (if #false 123 456)
assert(eval(Object_build_list(
4,
Object_new_symbol("if"),
Object_new_boolean(false),
num_123,
num_456
)) == num_456);
}
/*************************
* Basic data structures *
*************************/
void test_car()
{
struct Object *const num_123 = Object_new_number(123);
struct Object *const num_456 = Object_new_number(456);
// (car (cons 123 456))
// 123
assert(eval(Object_build_list(
2,
Object_new_symbol("car"),
Object_build_list(
3,
Object_new_symbol("cons"),
num_123,
num_456
)
)) == num_123);
}
void test_cdr()
{
struct Object *const num_123 = Object_new_number(123);
struct Object *const num_456 = Object_new_number(456);
// (cdr (cons 123 456))
// 456
assert(eval(Object_build_list(
2,
Object_new_symbol("cdr"),
Object_build_list(
3,
Object_new_symbol("cons"),
num_123,
num_456
)
)) == num_456);
}
void test_list()
{
struct Object *result = NULL;
struct Object *const num_123 = Object_new_number(123);
struct Object *const num_456 = Object_new_number(456);
// (list)
// '()
assert(eval_str("(list)") == NULL);
// (list 123)
// '(123)
result = eval(Object_build_list(
2,
Object_new_symbol("list"),
num_123
));
assert(Object_is_pair(result));
assert(result->pair.a == num_123);
assert(OBJECT_IS_NULL(result->pair.b));
// (list 123 456)
// '(123 456)
result = eval(Object_build_list(
3,
Object_new_symbol("list"),
num_123,
num_456
));
assert(Object_is_pair(result));
assert(result->pair.a == num_123);
assert(Object_is_pair(result->pair.b));
assert(result->pair.b->pair.a == num_456);
assert(OBJECT_IS_NULL(result->pair.b->pair.b));
}
/*******************
* Type predicates *
*******************/
void test_booleanQN()
{
// (boolean? +)
// #false
assert(Object_is_false(eval_str("(boolean? +)")));
// (boolean? '())
// #false
assert(Object_is_false(eval_str("(boolean? '())")));
// (boolean? #true)
// #true
assert(Object_is_true(eval_str("(boolean? #true)")));
// (boolean? #false)
// #true
assert(Object_is_true(eval_str("(boolean? #false)")));
// (boolean? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("boolean?"),
Object_new_char('\n')
))));
// (boolean? 'foo)
// #false
assert(Object_is_false(eval_str("(boolean? 'foo)")));
// (boolean? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("boolean?"),
Object_new_string("foo")
))));
// (boolean? 123)
// #false
assert(Object_is_false(eval_str("(boolean? 123)")));
// (boolean? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(boolean? (cons 123 456))")));
}
void test_charQN()
{
// (char? +)
// #false
assert(Object_is_false(eval_str("(char? +)")));
// (char? '())
// #false
assert(Object_is_false(eval_str("(char? '())")));
// (char? #true)
// #false
assert(Object_is_false(eval_str("(char? #true)")));
// (char? #false)
// #false
assert(Object_is_false(eval_str("(char? #false)")));
// (char? #\n)
// #true
assert(Object_is_true(eval(Object_build_list(
2,
Object_new_symbol("char?"),
Object_new_char('\n')
))));
// (char? 'foo)
// #false
assert(Object_is_false(eval_str("(char? 'foo)")));
// (char? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("char?"),
Object_new_string("foo")
))));
// (char? 123)
// #false
assert(Object_is_false(eval_str("(char? 123)")));
// (char? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(char? (cons 123 456))")));
}
void test_nullQN()
{
// (null? +)
// #false
assert(Object_is_false(eval_str("(null? +)")));
// (null? '())
// #true
assert(Object_is_true(eval_str("(null? '())")));
// (null? #true)
// #false
assert(Object_is_false(eval_str("(null? #true)")));
// (null? #false)
// #false
assert(Object_is_false(eval_str("(null? #false)")));
// (null? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("null?"),
Object_new_char('\n')
))));
// (null? 'foo)
// #false
assert(Object_is_false(eval_str("(null? 'foo)")));
// (null? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("null?"),
Object_new_string("foo")
))));
// (null? 123)
// #false
assert(Object_is_false(eval_str("(null? 123)")));
// (null? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(null? (cons 123 456))")));
}
void test_numberQN()
{
// (number? +)
// #false
assert(Object_is_false(eval_str("(number? +)")));
// (number? '())
// #false
assert(Object_is_false(eval_str("(number? '())")));
// (number? #true)
// #false
assert(Object_is_false(eval_str("(number? #true)")));
// (number? #false)
// #false
assert(Object_is_false(eval_str("(number? #false)")));
// (number? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("number?"),
Object_new_char('\n')
))));
// (number? 'foo)
// #false
assert(Object_is_false(eval_str("(number? 'foo)")));
// (number? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("number?"),
Object_new_string("foo")
))));
// (number? 123)
// #true
assert(Object_is_true(eval_str("(number? 123)")));
// (number? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(number? (cons 123 456))")));
}
void test_pairQN()
{
// (pair? +)
// #false
assert(Object_is_false(eval_str("(pair? +)")));
// (pair? '())
// #false
assert(Object_is_false(eval_str("(pair? '())")));
// (pair? #true)
// #false
assert(Object_is_false(eval_str("(pair? #true)")));
// (pair? #false)
// #false
assert(Object_is_false(eval_str("(pair? #false)")));
// (pair? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("pair?"),
Object_new_char('\n')
))));
// (pair? 'foo)
// #false
assert(Object_is_false(eval_str("(pair? 'foo)")));
// (pair? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("pair?"),
Object_new_string("foo")
))));
// (pair? 123)
// #false
assert(Object_is_false(eval_str("(pair? 123)")));
// (pair? (cons 123 456))
// #true
assert(Object_is_true(eval_str("(pair? (cons 123 456))")));
}
void test_procedureQN()
{
// (procedure? +)
// #true
assert(Object_is_true(eval_str("(procedure? +)")));
// (procedure? '())
// #false
assert(Object_is_false(eval_str("(procedure? '())")));
// (procedure? #true)
// #false
assert(Object_is_false(eval_str("(procedure? #true)")));
// (procedure? #false)
// #false
assert(Object_is_false(eval_str("(procedure? #false)")));
// (procedure? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("procedure?"),
Object_new_char('\n')
))));
// (procedure? 'foo)
// #false
assert(Object_is_false(eval_str("(procedure? 'foo)")));
// (procedure? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("procedure?"),
Object_new_string("foo")
))));
// (procedure? 123)
// #false
assert(Object_is_false(eval_str("(procedure? 123)")));
// (procedure? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(procedure? (cons 123 456))")));
}
void test_stringQN()
{
// (string? +)
// #false
assert(Object_is_false(eval_str("(string? +)")));
// (string? '())
// #false
assert(Object_is_false(eval_str("(string? '())")));
// (string? #true)
// #false
assert(Object_is_false(eval_str("(string? #true)")));
// (string? #false)
// #false
assert(Object_is_false(eval_str("(string? #false)")));
// (string? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("string?"),
Object_new_char('\n')
))));
// (string? 'foo)
// #false
assert(Object_is_false(eval_str("(string? 'foo)")));
// (string? "foo")
// #true
assert(Object_is_true(eval(Object_build_list(
2,
Object_new_symbol("string?"),
Object_new_string("foo")
))));
// (string? 123)
// #false
assert(Object_is_false(eval_str("(string? 123)")));
// (string? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(string? (cons 123 456))")));
}
void test_symbolQN()
{
// (symbol? +)
// #false
assert(Object_is_false(eval_str("(symbol? +)")));
// (symbol? '())
// #false
assert(Object_is_false(eval_str("(symbol? '())")));
// (symbol? #true)
// #false
assert(Object_is_false(eval_str("(symbol? #true)")));
// (symbol? #false)
// #false
assert(Object_is_false(eval_str("(symbol? #false)")));
// (symbol? #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("symbol?"),
Object_new_char('\n')
))));
// (symbol? 'foo)
// #true
assert(Object_is_true(eval_str("(symbol? 'foo)")));
// (symbol? "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("symbol?"),
Object_new_string("foo")
))));
// (symbol? 123)
// #false
assert(Object_is_false(eval_str("(symbol? 123)")));
// (symbol? (cons 123 456))
// #false
assert(Object_is_false(eval_str("(symbol? (cons 123 456))")));
}
/*******************
* Type conversion *
*******************/
void test_numberTOstring()
{
struct Object *result = NULL;
// (number->string 123)
// "123"
result = eval_str("(number->string 123)");
assert(Object_is_string(result));
assert(strcmp(result->s, "123") == 0);
// (number->string -123)
// "-123"
result = eval(Object_build_list(
2,
Object_new_symbol("number->string"),
Object_new_number(-123)
));
assert(Object_is_string(result));
assert(strcmp(result->s, "-123") == 0);
// (number->string 123456 16)
// "1e240"
result = eval_str("(number->string 123456 16)");
assert(Object_is_string(result));
assert(strcmp(result->s, "1e240") == 0);
}
/*********************
* Logical operators *
*********************/
void test_not()
{
// (not '())
// #false
assert(Object_is_false(eval_str("(not '())")));
// (not #true)
// #false
assert(Object_is_false(eval_str("(not #true)")));
// (not #false)
// #true
assert(Object_is_true(eval_str("(not #false)")));
// (not #\n)
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("not"),
Object_new_char('\n')
))));
// (not 'foo)
// #false
assert(Object_is_false(eval_str("(not 'foo)")));
// (not "foo")
// #false
assert(Object_is_false(eval(Object_build_list(
2,
Object_new_symbol("not"),
Object_new_string("foo")
))));
// (not 123)
// #false
assert(Object_is_false(eval_str("(not 123)")));
// (not (cons 123 456))
// #false
assert(Object_is_false(eval_str("(not (cons 123 456))")));
exit(EXIT_SUCCESS);
}