mirror of
https://github.com/samsonjs/lake.git
synced 2026-03-25 08:55:49 +00:00
168 lines
3.6 KiB
C
168 lines
3.6 KiB
C
/**
|
|
* lake.c
|
|
* Lake Scheme
|
|
*
|
|
* Copyright 2011 Sami Samhuri
|
|
* MIT License
|
|
*
|
|
* A quick and dirty scheme written in C, for fun and to use while
|
|
* reading The Little Schemer.
|
|
*
|
|
*/
|
|
|
|
#include <glib.h>
|
|
#include "bool.h"
|
|
#include "comment.h"
|
|
#include "common.h"
|
|
#include "env.h"
|
|
#include "eval.h"
|
|
#include "lake.h"
|
|
#include "list.h"
|
|
#include "primitive.h"
|
|
#include "str.h"
|
|
#include "symtable.h"
|
|
|
|
int lk_val_size(void *x)
|
|
{
|
|
return VAL(x)->size;
|
|
}
|
|
|
|
int lk_is_type(LakeType t, void *x)
|
|
{
|
|
return VAL(x)->type == t;
|
|
}
|
|
|
|
char *lake_repr(void *expr)
|
|
{
|
|
if (expr == NULL) return g_strdup("(null)");
|
|
|
|
char *s = NULL;
|
|
|
|
LakeVal *e = VAL(expr);
|
|
switch (e->type) {
|
|
|
|
case TYPE_SYM:
|
|
s = sym_repr(SYM(e));
|
|
break;
|
|
|
|
case TYPE_BOOL:
|
|
s = lk_bool_repr(BOOL(e));
|
|
break;
|
|
|
|
case TYPE_INT:
|
|
s = int_repr(INT(e));
|
|
break;
|
|
|
|
case TYPE_STR:
|
|
s = g_strdup_printf("\"%s\"", STR_S(STR(e)));
|
|
break;
|
|
|
|
case TYPE_LIST:
|
|
s = list_repr(LIST(e));
|
|
break;
|
|
|
|
case TYPE_DLIST:
|
|
s = dlist_repr(DLIST(e));
|
|
break;
|
|
|
|
case TYPE_PRIM:
|
|
s = prim_repr(PRIM(e));
|
|
break;
|
|
|
|
case TYPE_FN:
|
|
s = fn_repr(FN(e));
|
|
break;
|
|
|
|
case TYPE_COMM:
|
|
s = comment_repr(COMM(e));
|
|
break;
|
|
|
|
default:
|
|
// If it wasn't a LakeVal we already crashed at the beginning of the switch,
|
|
// so go ahead and print out the size too.
|
|
fprintf(stderr, "error: unrecognized value, type %d, size %zu bytes",
|
|
e->type, e->size);
|
|
s = g_strdup("(unknown)");
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
bool lk_is_nil(LakeVal *x)
|
|
{
|
|
return lk_is_type(TYPE_LIST, x) && LIST_N(LIST(x)) == 0;
|
|
}
|
|
|
|
bool lake_is(LakeVal *a, LakeVal *b)
|
|
{
|
|
if (lk_is_type(TYPE_INT, a) && lk_is_type(TYPE_INT, b)) {
|
|
return INT_VAL(INT(a)) == INT_VAL(INT(b));
|
|
}
|
|
if (lk_is_nil(a) && lk_is_nil(b)) return TRUE;
|
|
return a == b;
|
|
}
|
|
|
|
static char *type_name(LakeVal *expr)
|
|
{
|
|
static char *type_names[9] = { "nil", "symbol", "boolean", "integer", "string", "list",
|
|
"dotted-list", "primitive", "function"
|
|
};
|
|
|
|
LakeType t = expr->type;
|
|
return t >= 0 && t <= 8 ? type_names[t] : "(not a LakeVal)";
|
|
}
|
|
|
|
bool lake_equal(LakeVal *a, LakeVal *b)
|
|
{
|
|
if (a->type != b->type) return FALSE;
|
|
switch (a->type) {
|
|
|
|
/* singletons can be compared directly */
|
|
case TYPE_SYM:
|
|
case TYPE_BOOL:
|
|
case TYPE_PRIM:
|
|
case TYPE_FN:
|
|
return a == b;
|
|
|
|
case TYPE_INT:
|
|
return INT_VAL(INT(a)) == INT_VAL(INT(b));
|
|
|
|
case TYPE_STR:
|
|
return str_equal(STR(a), STR(b));
|
|
|
|
case TYPE_LIST:
|
|
return list_equal(LIST(a), LIST(b));
|
|
|
|
case TYPE_DLIST:
|
|
return dlist_equal(DLIST(a), DLIST(b));
|
|
|
|
case TYPE_COMM:
|
|
return comment_equal(COMM(a), COMM(b));
|
|
|
|
default:
|
|
ERR("unknown type %d (%s)", a->type, type_name(a));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static LakeBool *bool_make(bool val)
|
|
{
|
|
LakeBool *b = g_malloc(sizeof(LakeBool));
|
|
VAL(b)->type = TYPE_BOOL;
|
|
VAL(b)->size = sizeof(LakeBool);
|
|
b->val = val;
|
|
return b;
|
|
}
|
|
|
|
LakeCtx *lake_init(void)
|
|
{
|
|
LakeCtx *ctx = g_malloc(sizeof(LakeCtx));
|
|
ctx->toplevel = env_make(NULL);
|
|
ctx->symbols = g_hash_table_new(g_str_hash, g_str_equal);
|
|
ctx->special_form_handlers = symtable_make();
|
|
ctx->T = bool_make(TRUE);
|
|
ctx->F = bool_make(FALSE);
|
|
bind_primitives(ctx);
|
|
init_special_form_handlers(ctx);
|
|
return ctx;
|
|
}
|