mirror of
https://github.com/samsonjs/lake.git
synced 2026-03-25 08:55:49 +00:00
182 lines
4.2 KiB
C
182 lines
4.2 KiB
C
/**
|
|
* primitive.c
|
|
* Lake Scheme
|
|
*
|
|
* Copyright 2011 Sami Samhuri
|
|
* MIT License
|
|
*
|
|
*/
|
|
|
|
#include <glib.h>
|
|
#include <stdlib.h>
|
|
#include "env.h"
|
|
#include "lake.h"
|
|
#include "primitive.h"
|
|
|
|
static LakePrimitive *prim_alloc(void)
|
|
{
|
|
LakePrimitive *prim = malloc(sizeof(LakePrimitive));
|
|
VAL(prim)->type = TYPE_PRIM;
|
|
VAL(prim)->size = sizeof(LakePrimitive);
|
|
return prim;
|
|
}
|
|
|
|
LakePrimitive *prim_make(char *name, int arity, lake_fn fn)
|
|
{
|
|
LakePrimitive *prim = prim_alloc();
|
|
prim->name = g_strdup(name);
|
|
prim->arity = arity;
|
|
prim->fn = fn;
|
|
return prim;
|
|
}
|
|
|
|
char *prim_repr(LakePrimitive *prim)
|
|
{
|
|
return g_strdup_printf("<#primitive:%s(%d)>", prim->name, prim->arity);
|
|
}
|
|
|
|
static LakeVal *prim_nullP(LakeList *args)
|
|
{
|
|
LakeVal *val = list_shift(args);
|
|
LakeBool *is_null = IS(TYPE_LIST, val) && LIST_N(LIST(val)) == 0 ? T : F;
|
|
return VAL(is_null);
|
|
}
|
|
|
|
static LakeVal *prim_pairP(LakeList *args)
|
|
{
|
|
LakeVal *val = list_shift(args);
|
|
LakeBool *is_pair = IS(TYPE_LIST, val) && LIST_N(LIST(val)) > 0 ? T : F;
|
|
return VAL(is_pair);
|
|
}
|
|
|
|
static LakeVal *prim_not(LakeList *args)
|
|
{
|
|
LakeVal *val = list_shift(args);
|
|
LakeBool *not = IS_FALSE(val) ? T : F;
|
|
return VAL(not);
|
|
}
|
|
|
|
#define ENSURE_INT(x, i) do { \
|
|
if (!IS(TYPE_INT, x)) { \
|
|
ERR("argument %zu is not an integer: %s", i, repr(x)); \
|
|
return NULL; \
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
static LakeVal *prim_add(LakeList *args)
|
|
{
|
|
int result = 0;
|
|
size_t n = LIST_N(args);
|
|
size_t i;
|
|
for (i = 0; i < n; ++i) {
|
|
LakeVal *v = LIST_VAL(args, i);
|
|
ENSURE_INT(v, i);
|
|
result += INT_VAL(INT(v));
|
|
}
|
|
return VAL(int_from_c(result));
|
|
}
|
|
|
|
static LakeVal *prim_sub(LakeList *args)
|
|
{
|
|
size_t n = LIST_N(args);
|
|
|
|
if (n < 1) {
|
|
ERR("- requires at least one argument");
|
|
return NULL;
|
|
}
|
|
|
|
int result = 0;
|
|
size_t i;
|
|
for (i = 0; i < n; ++i) {
|
|
LakeVal *v = LIST_VAL(args, i);
|
|
ENSURE_INT(v, i);
|
|
result -= INT_VAL(INT(v));
|
|
}
|
|
return VAL(int_from_c(result));
|
|
}
|
|
|
|
static LakeVal *prim_mul(LakeList *args)
|
|
{
|
|
int result = 1;
|
|
size_t n = LIST_N(args);
|
|
size_t i;
|
|
for (i = 0; i < n; ++i) {
|
|
LakeVal *v = LIST_VAL(args, i);
|
|
ENSURE_INT(v, i);
|
|
result *= INT_VAL(INT(v));
|
|
}
|
|
return VAL(int_from_c(result));
|
|
}
|
|
|
|
#define DIVIDE_BY_ZERO() ERR("divide by zero")
|
|
|
|
static LakeVal *prim_div(LakeList *args)
|
|
{
|
|
size_t n = LIST_N(args);
|
|
|
|
if (n < 1) {
|
|
ERR("/ requires at least one argument");
|
|
return NULL;
|
|
}
|
|
|
|
LakeVal *v = LIST_VAL(args, 0);
|
|
ENSURE_INT(v, (size_t)0);
|
|
int result = INT_VAL(INT(v));
|
|
|
|
if (n == 1) {
|
|
if (result == 0) {
|
|
DIVIDE_BY_ZERO();
|
|
return NULL;
|
|
}
|
|
result = 1 / result;
|
|
}
|
|
else {
|
|
size_t i;
|
|
for (i = 1; i < n; ++i) {
|
|
v = LIST_VAL(args, i);
|
|
ENSURE_INT(v, i);
|
|
int val = INT_VAL(INT(v));
|
|
if (val == 0) {
|
|
DIVIDE_BY_ZERO();
|
|
return NULL;
|
|
}
|
|
result /= val;
|
|
}
|
|
}
|
|
return VAL(int_from_c(result));
|
|
}
|
|
|
|
static LakeVal *prim_int_eq(LakeList *args)
|
|
{
|
|
gboolean result = TRUE;
|
|
size_t n = LIST_N(args);
|
|
size_t i;
|
|
int curr, prev;
|
|
for (i = 0; i < n; ++i) {
|
|
LakeVal *v = LIST_VAL(args, i);
|
|
ENSURE_INT(v, i);
|
|
curr = INT_VAL(INT(v));
|
|
if (i > 0) {
|
|
result = result && curr == prev;
|
|
}
|
|
prev = INT_VAL(INT(v));
|
|
}
|
|
return VAL(bool_from_int(result));
|
|
}
|
|
|
|
Env *primitive_bindings(void)
|
|
{
|
|
#define DEFINE(name, fn, arity) env_define(env, sym_intern(name), VAL(prim_make(name, arity, fn)))
|
|
|
|
Env *env = env_toplevel();
|
|
DEFINE("null?", prim_nullP, 1);
|
|
DEFINE("pair?", prim_pairP, 1);
|
|
DEFINE("not", prim_not, 1);
|
|
DEFINE("+", prim_add, ARITY_VARARGS);
|
|
DEFINE("-", prim_sub, ARITY_VARARGS);
|
|
DEFINE("*", prim_mul, ARITY_VARARGS);
|
|
DEFINE("/", prim_div, ARITY_VARARGS);
|
|
DEFINE("=", prim_int_eq, ARITY_VARARGS);
|
|
return env;
|
|
}
|