mirror of
https://github.com/samsonjs/lake.git
synced 2026-04-27 14:57:43 +00:00
first commit
This commit is contained in:
commit
70fe7a1a58
20 changed files with 1513 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*.o
|
||||||
|
lake
|
||||||
8
Makefile
Normal file
8
Makefile
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
CC=gcc -Wall -ansi -G
|
||||||
|
|
||||||
|
all: lake
|
||||||
|
|
||||||
|
lake: lake.o env.o hashtab.o int.o string.o sym.o parse.o bool.o list.o
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm *.o lake
|
||||||
28
bool.c
Normal file
28
bool.c
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include "bool.h"
|
||||||
|
#include "lake.h"
|
||||||
|
#include "sym.h"
|
||||||
|
|
||||||
|
LakeSym *bool_from_int(int n)
|
||||||
|
{
|
||||||
|
return n ? sym_intern("#t") : sym_intern("#f");
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_true(LakeVal *val)
|
||||||
|
{
|
||||||
|
return (val->type == TYPE_SYM && sym_eq(sym_intern("#t"), SYM(val)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_false(LakeVal *val)
|
||||||
|
{
|
||||||
|
return (val->type == TYPE_SYM && sym_eq(sym_intern("#f"), SYM(val)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_truthy(LakeVal *val)
|
||||||
|
{
|
||||||
|
return is_true(val); /* TODO */
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_falsy(LakeVal *val)
|
||||||
|
{
|
||||||
|
return is_false(val); /* TODO */
|
||||||
|
}
|
||||||
12
bool.h
Normal file
12
bool.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef _BOOL_H
|
||||||
|
#define _BOOL_H 1
|
||||||
|
|
||||||
|
#include "lake.h"
|
||||||
|
|
||||||
|
LakeSym *bool_from_int(int b);
|
||||||
|
int is_true(LakeVal *val);
|
||||||
|
int is_false(LakeVal *val);
|
||||||
|
int is_truthy(LakeVal *val);
|
||||||
|
int is_falsy(LakeVal *val);
|
||||||
|
|
||||||
|
#endif
|
||||||
107
env.c
Normal file
107
env.c
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "lake.h"
|
||||||
|
#include "env.h"
|
||||||
|
#include "hashtab.h"
|
||||||
|
|
||||||
|
#define ENV_TABLE_SIZE 64
|
||||||
|
#define SYM_TABLE_SIZE 1024
|
||||||
|
|
||||||
|
static Env *_shared = NULL;
|
||||||
|
|
||||||
|
void set_shared_env(Env *env)
|
||||||
|
{
|
||||||
|
_shared = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
Env *shared_env(void)
|
||||||
|
{
|
||||||
|
if (!_shared) {
|
||||||
|
_shared = env_make(NULL);
|
||||||
|
}
|
||||||
|
return _shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
Env *env_make(Env *parent)
|
||||||
|
{
|
||||||
|
Env *env;
|
||||||
|
env = malloc(sizeof(Env));
|
||||||
|
if (!env) oom();
|
||||||
|
env->parent = parent;
|
||||||
|
env->bindings = ht_init(ENV_TABLE_SIZE, NULL);
|
||||||
|
env->symbols = ht_init(SYM_TABLE_SIZE, NULL);
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeVal *env_define(Env *env, char *key, LakeVal *val)
|
||||||
|
{
|
||||||
|
size_t keylen = strlen(key);
|
||||||
|
if (ht_get(env->bindings, key, keylen) != NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return VAL_OR_NIL(ht_put(env->bindings, key, keylen, val, VAL_SIZE(val)));
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeVal *env_set(Env *env, char *key, LakeVal *val)
|
||||||
|
{
|
||||||
|
size_t keylen = strlen(key);
|
||||||
|
if (ht_get(env->bindings, key, keylen) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return VAL_OR_NIL(ht_put(env->bindings, key, keylen, val, VAL_SIZE(val)));
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeVal *env_get(Env *env, char *key)
|
||||||
|
{
|
||||||
|
return VAL_OR_NIL(ht_get(env->bindings, key, strlen(key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int env_is_bound(Env *env, char *key)
|
||||||
|
{
|
||||||
|
return (ht_get(env->bindings, key, strlen(key)) != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
LakeVal *env_apply(Env *env, LakeFn *fn, LakeList *args)
|
||||||
|
{
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
LakeVal *env_eval(Env *env, LakeVal *expr)
|
||||||
|
{
|
||||||
|
LakeVal *result;
|
||||||
|
LakeList *list;
|
||||||
|
|
||||||
|
switch (expr->type) {
|
||||||
|
|
||||||
|
/* self evaluating types */
|
||||||
|
case TYPE_NIL:
|
||||||
|
case TYPE_SYM:
|
||||||
|
case TYPE_BOOL:
|
||||||
|
case TYPE_INT:
|
||||||
|
case TYPE_STR:
|
||||||
|
result = expr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_LIST:
|
||||||
|
list = LIST(expr);
|
||||||
|
if (LIST_N(list) == 0) {
|
||||||
|
result = NIL;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
LakeFn *fn = list_shift(list);
|
||||||
|
result = env_apply(env, fn, list);
|
||||||
|
*/
|
||||||
|
printf("%s:%d TODO apply functions\n", __FILE__, __LINE__);
|
||||||
|
result = expr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("unrecognized value, type %d, size %lu bytes", expr->type, expr->size);
|
||||||
|
die("we don't eval that around here!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
25
env.h
Normal file
25
env.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef _ENV_H
|
||||||
|
#define _ENV_H 1
|
||||||
|
|
||||||
|
#include "hashtab.h"
|
||||||
|
|
||||||
|
struct env {
|
||||||
|
struct env *parent;
|
||||||
|
hashtab_t *bindings;
|
||||||
|
hashtab_t *symbols;
|
||||||
|
};
|
||||||
|
typedef struct env Env;
|
||||||
|
|
||||||
|
#include "lake.h"
|
||||||
|
|
||||||
|
void set_shared_env(Env *env);
|
||||||
|
Env *shared_env(void);
|
||||||
|
|
||||||
|
Env *env_make(Env *parent);
|
||||||
|
LakeVal *env_define(Env *env, char *key, LakeVal *val);
|
||||||
|
LakeVal *env_set(Env *env, char *key, LakeVal *val);
|
||||||
|
LakeVal *env_get(Env *env, char *key);
|
||||||
|
int env_is_bound(Env *env, char *key);
|
||||||
|
LakeVal *env_eval(Env *env, LakeVal *expr);
|
||||||
|
|
||||||
|
#endif
|
||||||
310
hashtab.c
Normal file
310
hashtab.c
Normal file
|
|
@ -0,0 +1,310 @@
|
||||||
|
/* hashtab.c - Simple, Reliable C Hashtable
|
||||||
|
* Copyright (C) 2007 Christopher Wellons <mosquitopsu@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "hashtab.h"
|
||||||
|
|
||||||
|
hashtab_t *ht_init (size_t size, int (*hash_func) (void *, size_t, size_t))
|
||||||
|
{
|
||||||
|
hashtab_t *new_ht = (hashtab_t *) malloc (sizeof (hashtab_t));
|
||||||
|
|
||||||
|
new_ht->arr = (hashtab_node_t **) malloc (sizeof (hashtab_node_t *) * size);
|
||||||
|
|
||||||
|
new_ht->size = size;
|
||||||
|
new_ht->count = 0;
|
||||||
|
|
||||||
|
/* all entries are empty */
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; i < (int) size; i++)
|
||||||
|
{
|
||||||
|
new_ht->arr[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash_func == NULL)
|
||||||
|
new_ht->hash_func = &ht_hash;
|
||||||
|
else
|
||||||
|
new_ht->hash_func = hash_func;
|
||||||
|
|
||||||
|
return new_ht;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ht_get (hashtab_t * hashtable, void *key, size_t keylen)
|
||||||
|
{
|
||||||
|
int index = ht_hash (key, keylen, hashtable->size);
|
||||||
|
|
||||||
|
if (hashtable->arr[index] == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hashtab_node_t *last_node = hashtable->arr[index];
|
||||||
|
while (last_node != NULL)
|
||||||
|
{
|
||||||
|
/* only compare matching keylens */
|
||||||
|
if (last_node->keylen == keylen)
|
||||||
|
{
|
||||||
|
/* compare keys */
|
||||||
|
if (memcmp (key, last_node->key, keylen) == 0)
|
||||||
|
{
|
||||||
|
return last_node->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_node = last_node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ht_put (hashtab_t * hashtable,
|
||||||
|
void *key, size_t keylen, void *value, size_t vallen)
|
||||||
|
{
|
||||||
|
int index = ht_hash (key, keylen, hashtable->size);
|
||||||
|
|
||||||
|
hashtab_node_t *next_node, *last_node;
|
||||||
|
next_node = hashtable->arr[index];
|
||||||
|
last_node = NULL;
|
||||||
|
|
||||||
|
/* Search for an existing key. */
|
||||||
|
while (next_node != NULL)
|
||||||
|
{
|
||||||
|
/* only compare matching keylens */
|
||||||
|
if (next_node->keylen == keylen)
|
||||||
|
{
|
||||||
|
/* compare keys */
|
||||||
|
if (memcmp (key, next_node->key, keylen) == 0)
|
||||||
|
{
|
||||||
|
/* this key already exists, replace it */
|
||||||
|
if (next_node->vallen != vallen)
|
||||||
|
{
|
||||||
|
/* new value is a different size */
|
||||||
|
free (next_node->value);
|
||||||
|
next_node->value = malloc (vallen);
|
||||||
|
if (next_node->value == NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy (next_node->value, value, vallen);
|
||||||
|
next_node->vallen = vallen;
|
||||||
|
return next_node->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_node = next_node;
|
||||||
|
next_node = next_node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create a new node */
|
||||||
|
hashtab_node_t *new_node;
|
||||||
|
new_node = (hashtab_node_t *) malloc (sizeof (hashtab_node_t));
|
||||||
|
if (new_node == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* get some memory for the new node data */
|
||||||
|
new_node->key = malloc (keylen);
|
||||||
|
new_node->value = malloc (vallen);
|
||||||
|
if (new_node->key == NULL || new_node->key == NULL)
|
||||||
|
{
|
||||||
|
free (new_node->key);
|
||||||
|
free (new_node->value);
|
||||||
|
free (new_node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy over the value and key */
|
||||||
|
memcpy (new_node->key, key, keylen);
|
||||||
|
memcpy (new_node->value, value, vallen);
|
||||||
|
new_node->keylen = keylen;
|
||||||
|
new_node->vallen = vallen;
|
||||||
|
|
||||||
|
/* no next node */
|
||||||
|
new_node->next = NULL;
|
||||||
|
|
||||||
|
/* Tack the new node on the end or right on the table. */
|
||||||
|
if (last_node != NULL)
|
||||||
|
last_node->next = new_node;
|
||||||
|
else
|
||||||
|
hashtable->arr[index] = new_node;
|
||||||
|
|
||||||
|
hashtable->count++;
|
||||||
|
return new_node->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* delete the given key from the hashtable */
|
||||||
|
void ht_remove (hashtab_t * hashtable, void *key, size_t keylen)
|
||||||
|
{
|
||||||
|
hashtab_node_t *last_node, *next_node;
|
||||||
|
int index = ht_hash (key, keylen, hashtable->size);
|
||||||
|
next_node = hashtable->arr[index];
|
||||||
|
last_node = NULL;
|
||||||
|
|
||||||
|
while (next_node != NULL)
|
||||||
|
{
|
||||||
|
if (next_node->keylen == keylen)
|
||||||
|
{
|
||||||
|
/* compare keys */
|
||||||
|
if (memcmp (key, next_node->key, keylen) == 0)
|
||||||
|
{
|
||||||
|
/* free node memory */
|
||||||
|
free (next_node->value);
|
||||||
|
free (next_node->key);
|
||||||
|
|
||||||
|
/* adjust the list pointers */
|
||||||
|
if (last_node != NULL)
|
||||||
|
last_node->next = next_node->next;
|
||||||
|
else
|
||||||
|
hashtable->arr[index] = next_node->next;
|
||||||
|
|
||||||
|
/* free the node */
|
||||||
|
free (next_node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_node = next_node;
|
||||||
|
next_node = next_node->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* grow the hashtable */
|
||||||
|
void *ht_grow (hashtab_t * old_ht, size_t new_size)
|
||||||
|
{
|
||||||
|
/* create new hashtable */
|
||||||
|
hashtab_t *new_ht = ht_init (new_size, old_ht->hash_func);
|
||||||
|
if (new_ht == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
void *ret; /* captures return values */
|
||||||
|
|
||||||
|
/* Iterate through the old hashtable. */
|
||||||
|
hashtab_iter_t ii;
|
||||||
|
ht_iter_init (old_ht, &ii);
|
||||||
|
for (; ii.key != NULL; ht_iter_inc (&ii))
|
||||||
|
{
|
||||||
|
ret = ht_put (new_ht, ii.key, ii.keylen, ii.value, ii.vallen);
|
||||||
|
if (ret == NULL)
|
||||||
|
{
|
||||||
|
/* Insert failed. Destroy new hashtable and return. */
|
||||||
|
ht_destroy (new_ht);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destroy the old hashtable. */
|
||||||
|
ht_destroy (old_ht);
|
||||||
|
|
||||||
|
return new_ht;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free all resources used by the hashtable */
|
||||||
|
void ht_destroy (hashtab_t * hashtable)
|
||||||
|
{
|
||||||
|
hashtab_node_t *next_node, *last_node;
|
||||||
|
|
||||||
|
/* Free each linked list in hashtable. */
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < (int) hashtable->size; i++)
|
||||||
|
{
|
||||||
|
next_node = hashtable->arr[i];
|
||||||
|
while (next_node != NULL)
|
||||||
|
{
|
||||||
|
/* destroy node */
|
||||||
|
free (next_node->key);
|
||||||
|
free (next_node->value);
|
||||||
|
last_node = next_node;
|
||||||
|
next_node = next_node->next;
|
||||||
|
free (last_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free (hashtable->arr);
|
||||||
|
free (hashtable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterator initilaize */
|
||||||
|
void ht_iter_init (hashtab_t * hashtable, hashtab_iter_t * ii)
|
||||||
|
{
|
||||||
|
/* stick in initial bookeeping data */
|
||||||
|
ii->internal.hashtable = hashtable;
|
||||||
|
ii->internal.node = NULL;
|
||||||
|
ii->internal.index = -1;
|
||||||
|
|
||||||
|
/* have iterator point to first element */
|
||||||
|
ht_iter_inc (ii);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterator increment */
|
||||||
|
void ht_iter_inc (hashtab_iter_t * ii)
|
||||||
|
{
|
||||||
|
hashtab_t *hashtable = ii->internal.hashtable;
|
||||||
|
int index = ii->internal.index;
|
||||||
|
|
||||||
|
/* attempt to grab the next node */
|
||||||
|
if (ii->internal.node == NULL || ii->internal.node->next == NULL)
|
||||||
|
index++;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* next node in the list */
|
||||||
|
ii->internal.node = ii->internal.node->next;
|
||||||
|
ii->key = ii->internal.node->key;
|
||||||
|
ii->value = ii->internal.node->value;
|
||||||
|
ii->keylen = ii->internal.node->keylen;
|
||||||
|
ii->vallen = ii->internal.node->vallen;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find next node */
|
||||||
|
while (hashtable->arr[index] == NULL && index < (int) hashtable->size)
|
||||||
|
index++;
|
||||||
|
|
||||||
|
if (index >= (int) hashtable->size)
|
||||||
|
{
|
||||||
|
/* end of hashtable */
|
||||||
|
ii->internal.node = NULL;
|
||||||
|
ii->internal.index = (int) hashtable->size;
|
||||||
|
|
||||||
|
ii->key = NULL;
|
||||||
|
ii->value = NULL;
|
||||||
|
ii->keylen = 0;
|
||||||
|
ii->vallen = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* point to the next item in the hashtable */
|
||||||
|
ii->internal.node = hashtable->arr[index];
|
||||||
|
ii->internal.index = index;
|
||||||
|
ii->key = ii->internal.node->key;
|
||||||
|
ii->value = ii->internal.node->value;
|
||||||
|
ii->keylen = ii->internal.node->keylen;
|
||||||
|
ii->vallen = ii->internal.node->vallen;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ht_hash (void *key, size_t keylen, size_t hashtab_size)
|
||||||
|
{
|
||||||
|
int sum = 0;
|
||||||
|
|
||||||
|
/* very simple hash function for now */
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < (int) keylen; i++)
|
||||||
|
{
|
||||||
|
sum += ((unsigned char *) key)[i] * (i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (sum % (int) hashtab_size);
|
||||||
|
}
|
||||||
120
hashtab.h
Normal file
120
hashtab.h
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* hashtab.h - Simple, Reliable C Hashtable
|
||||||
|
* Copyright (C) 2007 Christopher Wellons <mosquitopsu@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* I needed a hashtable for a project and wanted to code my own. This
|
||||||
|
* hashtable compromises speed for reliability, specifically with
|
||||||
|
* growing the hashtable.
|
||||||
|
*
|
||||||
|
* The hashtable does not grow automatically, but when the hashtable
|
||||||
|
* grow function is called. Growing the hashtable is a safe operation:
|
||||||
|
* if growing the hashtable fails, the existing hashtable is not
|
||||||
|
* destroyed or modified.
|
||||||
|
*
|
||||||
|
* This hashtable is not thread-safe.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HASHTAB_H
|
||||||
|
#define HASHTAB_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct hashtab_node_t
|
||||||
|
{
|
||||||
|
void *key; /* key for the node */
|
||||||
|
size_t keylen; /* length of the key */
|
||||||
|
void *value; /* value for this node */
|
||||||
|
size_t vallen; /* length of the value */
|
||||||
|
|
||||||
|
struct hashtab_node_t *next; /* next node (open hashtable) */
|
||||||
|
} hashtab_node_t;
|
||||||
|
|
||||||
|
typedef struct hashtab_t
|
||||||
|
{
|
||||||
|
hashtab_node_t **arr;
|
||||||
|
size_t size; /* size of the hash */
|
||||||
|
int count; /* number if items in this table */
|
||||||
|
int (*hash_func) (void *, size_t, size_t); /* hash function */
|
||||||
|
} hashtab_t;
|
||||||
|
|
||||||
|
/* Iterator type for iterating through the hashtable. */
|
||||||
|
typedef struct hashtab_iter_t
|
||||||
|
{
|
||||||
|
/* key and value of current item */
|
||||||
|
void *key;
|
||||||
|
void *value;
|
||||||
|
size_t keylen;
|
||||||
|
size_t vallen;
|
||||||
|
|
||||||
|
/* bookkeeping data */
|
||||||
|
struct hashtab_internal_t
|
||||||
|
{
|
||||||
|
hashtab_t *hashtable;
|
||||||
|
hashtab_node_t *node;
|
||||||
|
int index;
|
||||||
|
} internal;
|
||||||
|
|
||||||
|
} hashtab_iter_t;
|
||||||
|
|
||||||
|
/* Initialize a new hashtable (set bookingkeeping data) and return a
|
||||||
|
* pointer to the hashtable. A hash function may be provided. If no
|
||||||
|
* function pointer is given (a NULL pointer), then the built in hash
|
||||||
|
* function is used. A NULL pointer returned if the creation of the
|
||||||
|
* hashtable failed due to a failed malloc(). */
|
||||||
|
hashtab_t *ht_init (size_t size,
|
||||||
|
int (*hash_func)
|
||||||
|
(void *key, size_t keylen, size_t ht_size));
|
||||||
|
|
||||||
|
/* Fetch a value from table matching the key. Returns a pointer to
|
||||||
|
* the value matching the given key. */
|
||||||
|
void *ht_get (hashtab_t * hashtable, void *key, size_t keylen);
|
||||||
|
|
||||||
|
/* Put a value into the table with the given key. Returns NULL if
|
||||||
|
* malloc() fails to allocate memory for the new node. */
|
||||||
|
void *ht_put (hashtab_t * hashtable,
|
||||||
|
void *key, size_t keylen, void *value, size_t vallen);
|
||||||
|
|
||||||
|
/* Delete the given key and value pair from the hashtable. If the key
|
||||||
|
* does not exist, no error is given. */
|
||||||
|
void ht_remove (hashtab_t * hashtable, void *key, size_t keylen);
|
||||||
|
|
||||||
|
/* Change the size of the hashtable. It will allocate a new hashtable
|
||||||
|
* and move all keys and values over. The pointer to the new hashtable
|
||||||
|
* is returned. Will return NULL if the new hashtable fails to be
|
||||||
|
* allocated. If this happens, the old hashtable will not be altered
|
||||||
|
* in any way. The old hashtable is destroyed upon a successful
|
||||||
|
* grow. */
|
||||||
|
void *ht_grow (hashtab_t * hashtable, size_t new_size);
|
||||||
|
|
||||||
|
/* Free all resources used by the hashtable. */
|
||||||
|
void ht_destroy (hashtab_t * hashtable);
|
||||||
|
|
||||||
|
/* Initialize the given iterator. It will point to the first element
|
||||||
|
* in the hashtable. */
|
||||||
|
void ht_iter_init (hashtab_t * hashtable, hashtab_iter_t * ii);
|
||||||
|
|
||||||
|
/* Increment the iterator to the next element. The iterator key and
|
||||||
|
* value will point to NULL values when the iterator has reached the
|
||||||
|
* end of the hashtable. */
|
||||||
|
void ht_iter_inc (hashtab_iter_t * ii);
|
||||||
|
|
||||||
|
/* Default hashtable hash function. */
|
||||||
|
int ht_hash (void *key, size_t key_size, size_t hashtab_size);
|
||||||
|
|
||||||
|
#endif
|
||||||
60
int.c
Normal file
60
int.c
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "bool.h"
|
||||||
|
#include "int.h"
|
||||||
|
|
||||||
|
static LakeInt *int_alloc(void)
|
||||||
|
{
|
||||||
|
LakeInt *i = malloc(sizeof(LakeInt));
|
||||||
|
i->base.type = TYPE_INT;
|
||||||
|
i->base.size = sizeof(LakeInt);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeInt *int_make(void)
|
||||||
|
{
|
||||||
|
LakeInt *i = int_alloc();
|
||||||
|
i->val = 0;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeInt *int_copy(LakeInt *i)
|
||||||
|
{
|
||||||
|
LakeInt *copy = int_alloc();
|
||||||
|
copy->val = i->val;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeInt *int_from_c(int n)
|
||||||
|
{
|
||||||
|
LakeInt *i = int_alloc();
|
||||||
|
i->val = n;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int int_val(LakeInt *i)
|
||||||
|
{
|
||||||
|
return i->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeInt *int_cmp(LakeInt *a, LakeInt *b)
|
||||||
|
{
|
||||||
|
int aN = a->val, bN = b->val;
|
||||||
|
LakeInt *result = int_alloc();
|
||||||
|
result->val = aN < bN ? -1 : (aN == bN ? 0 : 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeSym *int_eq(LakeInt *a, LakeInt *b)
|
||||||
|
{
|
||||||
|
return bool_from_int(a->val == b->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeStr *int_to_str(LakeInt *i)
|
||||||
|
{
|
||||||
|
static int size = 32;
|
||||||
|
char s[size];
|
||||||
|
snprintf(s, size, "%d", i->val);
|
||||||
|
LakeStr *str = str_from_c(s);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
14
int.h
Normal file
14
int.h
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef _INT_H
|
||||||
|
#define _INT_H 1
|
||||||
|
|
||||||
|
#include "lake.h"
|
||||||
|
|
||||||
|
LakeInt *int_make(void);
|
||||||
|
LakeInt *int_copy(LakeInt *i);
|
||||||
|
LakeInt *int_from_c(int n);
|
||||||
|
int int_val(LakeInt *i);
|
||||||
|
LakeInt *int_cmp(LakeInt *a, LakeInt *b);
|
||||||
|
LakeSym *int_eq(LakeInt *a, LakeInt *b);
|
||||||
|
LakeStr *int_to_str(LakeInt *i);
|
||||||
|
|
||||||
|
#endif
|
||||||
167
lake.c
Normal file
167
lake.c
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
/**
|
||||||
|
* 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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "env.h"
|
||||||
|
#include "lake.h"
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
static LakeVal _NIL = { TYPE_NIL, sizeof(LakeVal) };
|
||||||
|
LakeVal *NIL = &_NIL;
|
||||||
|
|
||||||
|
void die(char *msg)
|
||||||
|
{
|
||||||
|
printf("error: %s\n", msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void oom(void)
|
||||||
|
{
|
||||||
|
die("out of memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
Env *primitive_bindings(void)
|
||||||
|
{
|
||||||
|
Env *env = shared_env();
|
||||||
|
/* TODO */
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(LakeVal *val)
|
||||||
|
{
|
||||||
|
puts(repr(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeVal *prompt_read(char *prompt)
|
||||||
|
{
|
||||||
|
static int n = 1024;
|
||||||
|
printf("%s", prompt);
|
||||||
|
char buf[n];
|
||||||
|
if (!fgets(buf, n, stdin)) {
|
||||||
|
if (ferror(stdin)) {
|
||||||
|
printf("error: cannot read from stdin");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return parse_expr(buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_repl_with_env(Env *env)
|
||||||
|
{
|
||||||
|
puts("Lake Scheme v" LAKE_VERSION);
|
||||||
|
LakeVal *expr;
|
||||||
|
LakeVal *result;
|
||||||
|
for (;;) {
|
||||||
|
expr = prompt_read("> ");
|
||||||
|
if (!expr) return;
|
||||||
|
result = env_eval(env, expr);
|
||||||
|
print(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_repl(void)
|
||||||
|
{
|
||||||
|
run_repl_with_env(primitive_bindings());
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_one_then_repl(int argc, char const *args[])
|
||||||
|
{
|
||||||
|
/* create a top level environment */
|
||||||
|
Env *env = primitive_bindings();
|
||||||
|
|
||||||
|
/* bind args */
|
||||||
|
/*
|
||||||
|
LakeList *lakeArgs = list_of_strings_from_array(argc, args);
|
||||||
|
env_bind(env, "args", lakeArgs);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eval (load args[0]) in env */
|
||||||
|
|
||||||
|
run_repl_with_env(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *list_repr(LakeList *list)
|
||||||
|
{
|
||||||
|
char s[1024];
|
||||||
|
size_t n = 0;
|
||||||
|
s[n++] = '(';
|
||||||
|
int i;
|
||||||
|
char *s2;
|
||||||
|
size_t len;
|
||||||
|
for (i = 0; i < LIST_N(list); ++i) {
|
||||||
|
s2 = repr(LIST_VAL(list, i));
|
||||||
|
len = strlen(s2);
|
||||||
|
memcpy(s + n, s2, len);
|
||||||
|
n += len;
|
||||||
|
free(s2);
|
||||||
|
if (i != LIST_N(list) - 1) s[n++] = ' ';
|
||||||
|
}
|
||||||
|
s[n++] = ')';
|
||||||
|
s[n] = 0;
|
||||||
|
return strdup(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *repr(LakeVal *expr)
|
||||||
|
{
|
||||||
|
char *s = NULL;
|
||||||
|
LakeStr *str;
|
||||||
|
|
||||||
|
switch (expr->type) {
|
||||||
|
|
||||||
|
case TYPE_NIL:
|
||||||
|
s = strdup("nil");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_SYM:
|
||||||
|
s = sym_repr(SYM(expr));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_BOOL:
|
||||||
|
printf("%s:%d TODO: repr for bools", __FILE__, __LINE__);
|
||||||
|
s = strdup("[bool]");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_INT:
|
||||||
|
str = int_to_str(INT(expr));
|
||||||
|
s = str_val(str);
|
||||||
|
str_free(str);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_STR:
|
||||||
|
str = STR(expr);
|
||||||
|
s = malloc(STR_N(str) + 3);
|
||||||
|
sprintf(s, "\"%s\"", STR_S(str));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_LIST:
|
||||||
|
s = list_repr(LIST(expr));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("unrecognized value, type %d, size %lu bytes", expr->type, expr->size);
|
||||||
|
s = strdup("unrecognized value");
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char const *argv[])
|
||||||
|
{
|
||||||
|
if (argc == 1) {
|
||||||
|
run_repl();
|
||||||
|
} else {
|
||||||
|
run_one_then_repl(argc, argv);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
81
lake.h
Normal file
81
lake.h
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#ifndef _LAKE_H
|
||||||
|
#define _LAKE_H 1
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define LAKE_VERSION "0.1"
|
||||||
|
|
||||||
|
typedef int LakeType;
|
||||||
|
|
||||||
|
#define TYPE_NIL 0
|
||||||
|
#define TYPE_SYM 1
|
||||||
|
#define TYPE_BOOL 2
|
||||||
|
#define TYPE_INT 3
|
||||||
|
#define TYPE_STR 4
|
||||||
|
#define TYPE_LIST 5
|
||||||
|
|
||||||
|
#define VAL(x) ((LakeVal *)x)
|
||||||
|
#define SYM(x) ((LakeSym *)x)
|
||||||
|
#define BOOL(x) ((LakeBool *)x)
|
||||||
|
#define INT(x) ((LakeInt *)x)
|
||||||
|
#define STR(x) ((LakeStr *)x)
|
||||||
|
#define LIST(x) ((LakeList *)x)
|
||||||
|
|
||||||
|
struct lake_val {
|
||||||
|
LakeType type;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
typedef struct lake_val LakeVal;
|
||||||
|
|
||||||
|
LakeVal *NIL;
|
||||||
|
typedef LakeVal *NILP;
|
||||||
|
|
||||||
|
#define VAL_SIZE(x) (x != NULL ? x->size : NIL->size)
|
||||||
|
#define VAL_OR_NIL(x) (x != NULL ? (LakeVal *)x : NIL)
|
||||||
|
|
||||||
|
struct lake_sym {
|
||||||
|
LakeVal base;
|
||||||
|
size_t n;
|
||||||
|
char *s;
|
||||||
|
unsigned long hash;
|
||||||
|
};
|
||||||
|
typedef struct lake_sym LakeSym;
|
||||||
|
|
||||||
|
struct lake_int {
|
||||||
|
LakeVal base;
|
||||||
|
int val;
|
||||||
|
};
|
||||||
|
typedef struct lake_int LakeInt;
|
||||||
|
|
||||||
|
struct lake_str {
|
||||||
|
LakeVal base;
|
||||||
|
size_t n;
|
||||||
|
char *s;
|
||||||
|
};
|
||||||
|
typedef struct lake_str LakeStr;
|
||||||
|
|
||||||
|
#define STR_N(str) (str->n)
|
||||||
|
#define STR_S(str) (str->s)
|
||||||
|
|
||||||
|
struct lake_list {
|
||||||
|
LakeVal base;
|
||||||
|
size_t cap;
|
||||||
|
size_t n;
|
||||||
|
LakeVal **vals;
|
||||||
|
};
|
||||||
|
typedef struct lake_list LakeList;
|
||||||
|
|
||||||
|
#define LIST_N(list) (list->n)
|
||||||
|
#define LIST_VAL(list, i) (list->vals[i])
|
||||||
|
|
||||||
|
void die(char *msg);
|
||||||
|
void oom();
|
||||||
|
char *repr(LakeVal *val);
|
||||||
|
|
||||||
|
#include "sym.h"
|
||||||
|
#include "bool.h"
|
||||||
|
#include "int.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
109
list.c
Normal file
109
list.c
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "int.h"
|
||||||
|
#include "lake.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#define LIST_INIT_CAP 2
|
||||||
|
|
||||||
|
static LakeList *list_alloc(void)
|
||||||
|
{
|
||||||
|
LakeList *list = malloc(sizeof(LakeList));
|
||||||
|
list->base.type = TYPE_LIST;
|
||||||
|
list->base.size = sizeof(LakeList);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeList *list_make(void)
|
||||||
|
{
|
||||||
|
LakeList *list = list_make_with_cap(LIST_INIT_CAP);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < list->cap; ++i) {
|
||||||
|
list->vals[i] = NULL;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeList *list_make_with_cap(size_t cap)
|
||||||
|
{
|
||||||
|
LakeList *list = list_alloc();
|
||||||
|
list->cap = cap;
|
||||||
|
list->n = 0;
|
||||||
|
list->vals = malloc(cap * sizeof(LakeVal *));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeList *list_from_array(size_t n, LakeVal *vals[])
|
||||||
|
{
|
||||||
|
LakeList *list = list_make_with_cap(n);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
list->vals[i] = vals[i];
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeInt *list_len(LakeList *list)
|
||||||
|
{
|
||||||
|
return int_from_c(list->n);
|
||||||
|
}
|
||||||
|
|
||||||
|
NILP list_grow(LakeList *list)
|
||||||
|
{
|
||||||
|
list->cap *= 2;
|
||||||
|
LakeVal **new_vals;
|
||||||
|
new_vals = malloc(list->cap * sizeof(LakeVal *));
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < list->n; ++i) {
|
||||||
|
new_vals[i] = list->vals[i];
|
||||||
|
}
|
||||||
|
free(list->vals);
|
||||||
|
list->vals = new_vals;
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NILP list_append(LakeList *list, LakeVal *val)
|
||||||
|
{
|
||||||
|
if (list->n >= list->cap) {
|
||||||
|
list_grow(list);
|
||||||
|
}
|
||||||
|
list->vals[list->n++] = val;
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NILP list_set(LakeList *list, size_t i, LakeVal *val)
|
||||||
|
{
|
||||||
|
if (i < 0 || i >= list->n) {
|
||||||
|
/* TODO error */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
list->vals[i] = val;
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeVal *list_get(LakeList *list, LakeInt *li)
|
||||||
|
{
|
||||||
|
int i = int_val(li);
|
||||||
|
if (i < 0 || i >= list->n) {
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
return VAL_OR_NIL(list->vals[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeInt *list_cmp(LakeList *a, LakeList *b)
|
||||||
|
{
|
||||||
|
/* TODO */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeSym *list_eq(LakeList *a, LakeList *b)
|
||||||
|
{
|
||||||
|
/* TODO */
|
||||||
|
return bool_from_int(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeStr *list_to_str(LakeList *list)
|
||||||
|
{
|
||||||
|
/* TODO */
|
||||||
|
return str_from_c("[TODO]");
|
||||||
|
}
|
||||||
20
list.h
Normal file
20
list.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef _LIST_H
|
||||||
|
#define _LIST_H 1
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "lake.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
LakeList *list_make(void);
|
||||||
|
LakeList *list_make_with_cap(size_t cap);
|
||||||
|
LakeList *list_from_array(size_t n, LakeVal *vals[]);
|
||||||
|
LakeInt *list_len(LakeList *list);
|
||||||
|
NILP list_grow(LakeList *list);
|
||||||
|
NILP list_append(LakeList *list, LakeVal *val);
|
||||||
|
NILP list_set(LakeList *list, size_t i, LakeVal *val);
|
||||||
|
LakeVal *list_get(LakeList *list, LakeInt *li);
|
||||||
|
LakeInt *list_cmp(LakeList *a, LakeList *b);
|
||||||
|
LakeSym *list_eq(LakeList *a, LakeList *b);
|
||||||
|
LakeStr *list_to_str(LakeList *list);
|
||||||
|
|
||||||
|
#endif
|
||||||
281
parse.c
Normal file
281
parse.c
Normal file
|
|
@ -0,0 +1,281 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "lake.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
struct context {
|
||||||
|
char *s;
|
||||||
|
size_t n;
|
||||||
|
size_t i;
|
||||||
|
};
|
||||||
|
typedef struct context Ctx;
|
||||||
|
|
||||||
|
static LakeVal *_parse_expr(Ctx *ctx);
|
||||||
|
|
||||||
|
LakeVal *parse_expr(char *s, size_t n)
|
||||||
|
{
|
||||||
|
Ctx ctx = { s, n, 0 };
|
||||||
|
LakeVal *result = _parse_expr(&ctx);
|
||||||
|
if (ctx.i < ctx.n) {
|
||||||
|
printf("ignoring %lu trailing chars: %s\n", ctx.n - ctx.i - 1, ctx.s + ctx.i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PARSE_EOF -1
|
||||||
|
|
||||||
|
static char peek(Ctx *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->i < ctx->n) return ctx->s[ctx->i];
|
||||||
|
return PARSE_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char peek2(Ctx *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->i + 1 < ctx->n) return ctx->s[ctx->i + 1];
|
||||||
|
return PARSE_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void consume(Ctx *ctx, size_t n)
|
||||||
|
{
|
||||||
|
if (ctx->i + n > ctx->n) {
|
||||||
|
die("ERROR: cannot consume, no more input");
|
||||||
|
}
|
||||||
|
ctx->i += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char consume1(Ctx *ctx)
|
||||||
|
{
|
||||||
|
char c = peek(ctx);
|
||||||
|
consume(ctx, 1);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char ch(Ctx *ctx, char expected)
|
||||||
|
{
|
||||||
|
char c = peek(ctx);
|
||||||
|
if (c == expected) {
|
||||||
|
consume1(ctx);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
char *msg = malloc(64);
|
||||||
|
sprintf(msg, "parse error, expected '%c' got '%c'", expected, c);
|
||||||
|
die(msg);
|
||||||
|
return 0; /* placate the compiler */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int maybe_spaces(Ctx *ctx)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
while ((p = strchr(" \r\n\t", peek(ctx))) != NULL) {
|
||||||
|
consume1(ctx);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
static int whitespace(Ctx *ctx)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
char *p;
|
||||||
|
while ((p = strchr(" \r\n\t", peek(ctx))) != NULL) {
|
||||||
|
consume1(ctx);
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
static int is_letter(char c)
|
||||||
|
{
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_symbol(char c)
|
||||||
|
{
|
||||||
|
return strchr("!$%&|*+-/:<=>?@^_~#", c) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_digit(char c)
|
||||||
|
{
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *parse_while(Ctx *ctx, int (*is_valid)(char))
|
||||||
|
{
|
||||||
|
size_t n = 8;
|
||||||
|
size_t i = 0;
|
||||||
|
char *s = malloc(n);
|
||||||
|
char c;
|
||||||
|
while ((c = peek(ctx)) != PARSE_EOF && is_valid(c)) {
|
||||||
|
s[i++] = c;
|
||||||
|
consume1(ctx);
|
||||||
|
/* grow if necessary */
|
||||||
|
if (i >= n) {
|
||||||
|
n *= 2;
|
||||||
|
char *t = realloc(s, n);
|
||||||
|
if (!t) oom();
|
||||||
|
s = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s[i] = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LakeInt *parse_int(Ctx *ctx)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
char c = peek(ctx);
|
||||||
|
char sign = c == '-' ? -1 : 1;
|
||||||
|
if (c == '-' || c == '+') {
|
||||||
|
consume1(ctx);
|
||||||
|
}
|
||||||
|
while (is_digit(c = peek(ctx))) {
|
||||||
|
n *= 10;
|
||||||
|
n += c - '0';
|
||||||
|
consume1(ctx);
|
||||||
|
}
|
||||||
|
return int_from_c(sign * n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_sym_char(char c)
|
||||||
|
{
|
||||||
|
return is_letter(c) || is_symbol(c) || is_digit(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LakeSym *parse_sym(Ctx *ctx)
|
||||||
|
{
|
||||||
|
static int size = 1024;
|
||||||
|
char s[size];
|
||||||
|
char c;
|
||||||
|
int i = 0;
|
||||||
|
while (is_sym_char(c = peek(ctx)) && i < size - 1) {
|
||||||
|
s[i++] = c;
|
||||||
|
consume1(ctx);
|
||||||
|
}
|
||||||
|
s[i] = 0;
|
||||||
|
/* TODO: check for #t and #f and return true boolean values (LakeBool *) */
|
||||||
|
return sym_intern(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char escape_char(char c)
|
||||||
|
{
|
||||||
|
switch (c) {
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
c = '\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
c = '\r';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
c = '\t';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* noop */
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LakeStr *parse_str(Ctx *ctx)
|
||||||
|
{
|
||||||
|
size_t n = 8;
|
||||||
|
size_t i = 0;
|
||||||
|
char *s = malloc(n);
|
||||||
|
char c;
|
||||||
|
ch(ctx, '"');
|
||||||
|
while ((c = peek(ctx)) != PARSE_EOF && c != '"') {
|
||||||
|
/* handle backslash escapes */
|
||||||
|
if (c == '\\') {
|
||||||
|
consume1(ctx);
|
||||||
|
c = escape_char(peek(ctx));
|
||||||
|
if (c == PARSE_EOF) break;
|
||||||
|
}
|
||||||
|
s[i++] = c;
|
||||||
|
consume1(ctx);
|
||||||
|
/* grow if necessary */
|
||||||
|
if (i >= n) {
|
||||||
|
n *= 2;
|
||||||
|
char *t = realloc(s, n);
|
||||||
|
if (!t) oom();
|
||||||
|
s = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s[i] = 0;
|
||||||
|
ch(ctx, '"');
|
||||||
|
LakeStr *str = str_from_c(s);
|
||||||
|
free(s);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LakeList* parse_list(Ctx *ctx)
|
||||||
|
{
|
||||||
|
LakeList *list = list_make();
|
||||||
|
ch(ctx, '(');
|
||||||
|
char c;
|
||||||
|
while ((c = peek(ctx)) != ')') {
|
||||||
|
if (c == PARSE_EOF) {
|
||||||
|
printf("error: end of input while parsing list");
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
list_append(list, _parse_expr(ctx));
|
||||||
|
}
|
||||||
|
ch(ctx, ')');
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_not_newline(char c)
|
||||||
|
{
|
||||||
|
return !(c == '\n' || c == '\r');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_comment(Ctx *ctx)
|
||||||
|
{
|
||||||
|
free(parse_while(ctx, is_not_newline));
|
||||||
|
}
|
||||||
|
|
||||||
|
static LakeVal *_parse_expr(Ctx *ctx)
|
||||||
|
{
|
||||||
|
LakeVal *result = NIL;
|
||||||
|
char c = peek(ctx);
|
||||||
|
/*char d =*/ peek2(ctx);
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
result = (LakeVal *)parse_int(ctx);
|
||||||
|
}
|
||||||
|
/* TODO: chars
|
||||||
|
else if (c == '#' && d == '\\') {
|
||||||
|
result = (LakeVal *)parse_char(ctx);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
else if (is_letter(c) || is_symbol(c)) {
|
||||||
|
result = (LakeVal *)parse_sym(ctx);
|
||||||
|
}
|
||||||
|
else if (c == '"') {
|
||||||
|
result = (LakeVal *)parse_str(ctx);
|
||||||
|
}
|
||||||
|
/* TODO: quote
|
||||||
|
else if (c == '\'') {
|
||||||
|
result = (LakeVal *)parse_quoted(ctx);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
else if (c == '(') {
|
||||||
|
result = (LakeVal *)parse_list(ctx);
|
||||||
|
}
|
||||||
|
else if (c == ';') {
|
||||||
|
parse_comment(ctx);
|
||||||
|
}
|
||||||
|
else if (c == PARSE_EOF) {
|
||||||
|
printf("error: end of input, expected an expression");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char msg[32];
|
||||||
|
sprintf(msg, "unexpected char '%c'", c);
|
||||||
|
die(msg);
|
||||||
|
}
|
||||||
|
maybe_spaces(ctx);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
9
parse.h
Normal file
9
parse.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _PARSE_H
|
||||||
|
#define _PARSE_H 1
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "lake.h"
|
||||||
|
|
||||||
|
LakeVal *parse_expr(char *s, size_t n);
|
||||||
|
|
||||||
|
#endif
|
||||||
70
string.c
Normal file
70
string.c
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "int.h"
|
||||||
|
#include "lake.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
static LakeStr *str_alloc(void)
|
||||||
|
{
|
||||||
|
LakeStr *str = malloc(sizeof(LakeStr));
|
||||||
|
str->base.type = TYPE_STR;
|
||||||
|
str->base.size = sizeof(LakeStr);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void str_free(LakeStr *str)
|
||||||
|
{
|
||||||
|
free(str->s);
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeStr *str_make(void)
|
||||||
|
{
|
||||||
|
return str_from_c("");
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeInt *str_len(LakeStr *str)
|
||||||
|
{
|
||||||
|
return int_from_c(str->n);
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeStr *str_copy(LakeStr *str)
|
||||||
|
{
|
||||||
|
return str_from_c(str->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeStr *str_from_c(char *s)
|
||||||
|
{
|
||||||
|
LakeStr *str = str_alloc();
|
||||||
|
str_set(str, s);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *str_val(LakeStr *str)
|
||||||
|
{
|
||||||
|
return strdup(str->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
NILP str_set(LakeStr *str, char *s)
|
||||||
|
{
|
||||||
|
str->n = strlen(s);
|
||||||
|
str->s = strdup(s);
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MIN(a, b) (a < b ? a : b)
|
||||||
|
|
||||||
|
LakeInt *str_cmp(LakeStr *a, LakeStr *b)
|
||||||
|
{
|
||||||
|
return int_from_c(strncmp(a->s, b->s, MIN(a->n, b->n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeSym *str_eq(LakeStr *a, LakeStr *b)
|
||||||
|
{
|
||||||
|
return bool_from_int(strncmp(a->s, b->s, MIN(a->n, b->n)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeStr *str_to_str(LakeStr *str)
|
||||||
|
{
|
||||||
|
return str_copy(str);
|
||||||
|
}
|
||||||
17
string.h
Normal file
17
string.h
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef _STRING_H
|
||||||
|
#define _STRING_H 1
|
||||||
|
|
||||||
|
#include "lake.h"
|
||||||
|
|
||||||
|
LakeStr *str_make(void);
|
||||||
|
void str_free(LakeStr *str);
|
||||||
|
LakeStr *str_copy(LakeStr *str);
|
||||||
|
LakeStr *str_from_c(char *s);
|
||||||
|
char *str_val(LakeStr *str);
|
||||||
|
LakeInt *str_len(LakeStr *str);
|
||||||
|
NILP str_set(LakeStr *str, char *s);
|
||||||
|
LakeInt *str_cmp(LakeStr *a, LakeStr *b);
|
||||||
|
LakeSym *str_eq(LakeStr *a, LakeStr *b);
|
||||||
|
LakeStr *str_to_str(LakeStr *str);
|
||||||
|
|
||||||
|
#endif
|
||||||
60
sym.c
Normal file
60
sym.c
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "env.h"
|
||||||
|
#include "lake.h"
|
||||||
|
#include "hashtab.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "sym.h"
|
||||||
|
|
||||||
|
#define SYM_TABLE_SIZE 1024
|
||||||
|
|
||||||
|
static LakeSym *sym_alloc(void)
|
||||||
|
{
|
||||||
|
LakeSym *sym = malloc(sizeof(LakeSym));
|
||||||
|
sym->base.type = TYPE_SYM;
|
||||||
|
sym->base.size = sizeof(LakeSym);
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeSym *sym_intern(char *s)
|
||||||
|
{
|
||||||
|
size_t n = strlen(s);
|
||||||
|
hashtab_t *symbols = shared_env()->symbols;
|
||||||
|
LakeSym *sym = ht_get(symbols, s, n);
|
||||||
|
if (!sym) {
|
||||||
|
sym = sym_alloc();
|
||||||
|
sym->n = n;
|
||||||
|
sym->s = strdup(s);
|
||||||
|
sym->hash = ht_hash(s, n, symbols->size);
|
||||||
|
ht_put(symbols, sym->s, sym->n, sym, sizeof(LakeSym));
|
||||||
|
}
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeStr *sym_to_str(LakeSym *sym)
|
||||||
|
{
|
||||||
|
return str_from_c(sym->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
LakeSym *sym_from_str(LakeStr *str)
|
||||||
|
{
|
||||||
|
return sym_intern(str->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *sym_repr(LakeSym *sym)
|
||||||
|
{
|
||||||
|
return strdup(sym->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long sym_val(LakeSym *sym)
|
||||||
|
{
|
||||||
|
return sym->hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MIN(a, b) (a < b ? a : b)
|
||||||
|
|
||||||
|
LakeSym *sym_eq(LakeSym *a, LakeSym *b)
|
||||||
|
{
|
||||||
|
return bool_from_int(strncmp(a->s, b->s, MIN(a->n, b->n)) == 0);
|
||||||
|
}
|
||||||
13
sym.h
Normal file
13
sym.h
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef _SYM_H
|
||||||
|
#define _SYM_H 1
|
||||||
|
|
||||||
|
#include "lake.h"
|
||||||
|
|
||||||
|
LakeSym *sym_intern(char *s);
|
||||||
|
LakeStr *sym_to_str(LakeSym *sym);
|
||||||
|
LakeSym *sym_from_str(LakeStr *str);
|
||||||
|
char *sym_repr(LakeSym *sym);
|
||||||
|
unsigned long sym_val(LakeSym *sym);
|
||||||
|
LakeSym *sym_eq(LakeSym *a, LakeSym *b);
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Reference in a new issue