Split REPL and tests
This commit is contained in:
parent
dfab67c3b4
commit
90d373944b
|
@ -1,3 +1,4 @@
|
|||
/src/*.o
|
||||
|
||||
/lisp
|
||||
/lisp-test
|
||||
|
|
16
Makefile
16
Makefile
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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))")));
|
||||
}
|
785
src/main.c
785
src/main.c
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue