first commit

This commit is contained in:
yoyo 2024-08-29 19:56:05 +09:00
commit a9d27325c5
11 changed files with 941 additions and 0 deletions

53
bltin.c Normal file
View File

@ -0,0 +1,53 @@
#include "dat.h"
#include "fn.h"
Object Nil = (Object){.type=OSYMBOL, .sym="nil"};
Object True = (Object){.type=OSYMBOL, .sym="true"};
Object False= (Object){.type=OSYMBOL, .sym="false"};
Object Minus= (Object){.type=OBLTIN, .sym="-"};
Object Plus = (Object){.type=OBLTIN, .sym="+"};
Object Lambda= (Object){.type=OBLTIN, .sym="lambda"};
Object Car = (Object){.type=OBLTIN, .sym="car"};
Object Cdr = (Object){.type=OBLTIN, .sym="cdr"};
Object Quote= (Object){.type=OBLTIN, .sym="'"};
Object Cons = (Object){.type=OBLTIN, .sym="cons"};
Object Define= (Object){.type=OBLTIN, .sym="define"};
Object Setq = (Object){.type=OBLTIN, .sym="setq"};
extern Object* fnplus(Object *, Object *);
extern Object* fnlambda(Object *, Object *);
extern Object* fndefine(Object *, Object *);
extern Object* fnsetq(Object *, Object *);
extern Object* fnundef(Object *, Object *);
/*extern Object* fnminus(Object *, Object *);*/
/*extern Object* fncons(Object *, Object *);*/
/*extern Object* fnquote(Object *, Object *);*/
/*extern Object* fncar(Object *, Object *);*/
/*extern Object* fncdr(Object *, Object *);*/
Bltinfn
bltinlookup(Object *obj)
{
static struct
{
Object *sym;
Bltinfn fn;
}bltins[] = {
{&Lambda , fnlambda},
{&Plus , fnplus},
{&Define ,fndefine},
{&Setq ,fnsetq},
{&Minus ,0},
{&Cons ,0},
{&Quote ,0},
{&Car ,0},
{&Cdr ,0},
0,
};
for(int i = 0; bltins[i].sym; ++i){
if(obj == bltins[i].sym)
return bltins[i].fn;
}
return 0;
}

65
dat.h Normal file
View File

@ -0,0 +1,65 @@
typedef struct Object Object;
typedef Object* (*Bltinfn)(Object *env, Object *args);
typedef struct Object Object;
enum OType
{
OERROR,
OCELL,
OSYMBOL,
OIDENT,
OINT,
OSTRING,
OLAMBDA,
OBLTIN,
OFUNC,
OENV,
};
struct Object
{
enum OType type; /* type */
int flag; /* flag */
Object *next; /* for gc */
union{
/* int */
long num;
/* cell */
struct{
Object *car;
Object *cdr;
};
/* string & ident */
char *sym;
struct{
char *beg;
char *ptr;
char *end;
};
/* function */
struct{
Object *params;
Object *body;
Object *env;
};
/* env */
struct{
Object *name;
Object *up;
Object *vars;
};
};
};
extern Object Nil;
extern Object True;
extern Object False;
extern Object Minus;
extern Object Plus;
extern Object Lambda;
extern Object Car;
extern Object Cdr;
extern Object Quote;
extern Object Cons;
extern Object Define;
extern Object Setq;

32
error.c Normal file
View File

@ -0,0 +1,32 @@
#include "dat.h"
#include "fn.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <setjmp.h>
void
panic(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(1);
}
void
error(char *fmt, ...)
{
extern jmp_buf err;
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "ERROR => ");
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
longjmp(err, 1);
exit(1);
}

167
eval.c Normal file
View File

@ -0,0 +1,167 @@
#include "dat.h"
#include "fn.h"
static Object* evallist(Object *env, Object *list);
static int
exprlen(Object *expr)
{
int l = 0;
for(;expr->type==OCELL; expr=expr->cdr)
++l;
if(expr != &Nil)
error("Not list type");
return l;
}
static int
islist(Object *obj)
{
return obj == &Nil || obj->type == OCELL;
}
static Object*
find(Object *env, Object *obj)
{
for(Object *cur=env; cur!=&Nil; cur=cur->up){
for(Object *p=cur->vars; p!=&Nil; p=p->cdr)
if(strequal(obj, p->car->car))
return p->car;
}
return 0;
}
static Object*
enter(Object *env, Object *vars, Object *args)
{
Object *map = &Nil;
for(;vars->type==OCELL; vars=vars->cdr,args=args->cdr){
if(args->type!=OCELL)
error("Cna't apply function argment dose not match");
Object *id = vars->car;
Object *val = args->car;
map = newacons(id, val, map);
}
return newenv(&Nil, map, env);
}
Object*
fnlambda(Object *env, Object *l)
{
if(l->type!=OCELL || islist(l->car)==0 || l->cdr->type!=OCELL)
error("malformed function");
for(Object *p=l->car; p->type==OCELL; p=p->cdr){
if(p->car->type!=OIDENT)
error("parameter is not IDNET");
}
Object *params = l->car;
Object *body = l->cdr;
return newfn(env, params, body);
}
Object*
fnsetq(Object *env, Object *list)
{
if(exprlen(list)!=2 || list->car->type!=OIDENT)
error("Malformed setq");
Object *obj = find(env, list->car);
if(obj == 0)
error("Not exist variable");
return obj->cdr = eval(env, list->cdr->car);
}
Object*
fndefine(Object *env, Object *list)
{
if(exprlen(list)!=2 || list->car->type!=OIDENT)
error("Malformed define");
Object *val = eval(env, list->cdr->car);
Object *obj = find(env, list->car);
if(obj)
return obj->cdr = val;
return env->vars = newacons(list->car, val, env->vars);
}
Object*
fnplus(Object *env, Object *list)
{
long sum = 0;
for(Object *p=evallist(env, list); p!=&Nil; p=p->cdr){
if(p->car->type != OINT)
error("+ take only number");
sum += p->car->num;
}
return newint(sum);
}
static Object*
evallist(Object *env, Object *list)
{
if(list == &Nil)
return &Nil;
if(list->type != OCELL)
error("type is not list");
Object *car = eval(env, list->car);
Object *cdr = evallist(env, list->cdr);
return newcons(car, cdr);
}
static Object*
applyfn(Object *fn, Object *args)
{
Object *env = enter(fn->env, fn->params, args);
Object *r = 0;
for(Object *p=fn->body; p!=&Nil; p=p->cdr){
r = p->car;
r = eval(env, r);
}
return r;
}
static Object*
apply(Object *env, Object *fn, Object *args)
{
if(islist(args) == 0)
error("args is not list type");
switch(fn->type){
case OBLTIN:
Bltinfn blt = bltinlookup(fn);
if(blt==0)
error("not builtin type!");
return blt(env, args);
case OFUNC:{
Object *elist = evallist(env, args);
Object*res = applyfn(fn, elist);
return res;
}
}
error("can't apply");
return 0;
}
Object*
eval(Object *env, Object *obj)
{
switch(obj->type){
case OINT:
case OSTRING:
case OBLTIN:
case OSYMBOL:
return obj;
case OIDENT:{
Object* val = find(env, obj);
if(val == 0)
error("not exist '%s'", obj->beg);
return val->cdr;
}
case OCELL:{
Object *fn = eval(env, obj->car);
if(fn->type!=OFUNC&&fn->type!=OBLTIN)
error("expected function type");
Object *res = apply(env, fn, obj->cdr);
return res;
}
}
error("can't apply");
return 0;
}

37
fn.h Normal file
View File

@ -0,0 +1,37 @@
/* parser.c */
Object* nextexpr(void);
void skipline(void);
/* eval.c */
Object* eval(Object *env, Object *expr);
/* new */
Object* newint(long);
Object* newcons(Object*,Object*);
Object* newenv(Object*name, Object *vars, Object *up);
Object* newacons(Object*, Object*, Object*);
Object* newsymbol(char*, int);
Object* newstr(int);
Object* newfn(Object *env, Object *params, Object *body);
/* gc.c */
void gcstatus(void);
Object* newobj(enum OType);
void* xalloc(int);
void* xralloc(void*, int);
void gcinit(void *top, int cap);
void gcrun(void);
/* str.c */
void strputc(Object*, int);
void strputs(Object*, char*);
int strequal(Object*, Object*);
/* error.c */
void panic(char *fmt, ...);
void error(char *fmt, ...);
/* builtin */
Bltinfn bltinlookup(Object *obj);
void printexpr(Object *obj);

182
gc.c Normal file
View File

@ -0,0 +1,182 @@
#include "dat.h"
#include "fn.h"
#include <stdlib.h>
#include <stdint.h>
#include <setjmp.h>
#include <stdint.h>
#include <string.h>
enum
{
USING = 1 << 1,
};
typedef struct
{
int total;
int using;
uintptr_t top;
uintptr_t beg;
uintptr_t end;
Object objs;
Object freed;
}GC;
GC gc = {0};
static void
pushobj(Object *list, Object *obj)
{
Object *l = list;
Object *c = l->next;
while(c){
l = c;
c=c->next;
}
l->next = obj;
}
static void
mark(Object *obj)
{
if(obj == 0 || obj->flag & USING)
return;
obj->flag = USING;
switch(obj->type){
case OCELL:
mark(obj->car);
mark(obj->cdr);
break;
case OENV:
mark(obj->name);
mark(obj->vars);
mark(obj->up);
break;
case OFUNC:
mark(obj->params);
mark(obj->body);
mark(obj->env);
break;
}
}
static int
isobj(uintptr_t val)
{
if(val < gc.beg || val >= gc.end)
return 0;
val -= gc.beg;
uintptr_t mod = val % sizeof(Object);
return mod == 0;
}
static void
freeobj(Object *obj)
{
switch(obj->type){
case OSTRING:
case OIDENT:
printf("freed => '%s'\n", obj->beg);
free(obj->beg);
break;
}
memset(obj, 0, sizeof(*obj));
pushobj(&gc.freed, obj);
gc.using -= sizeof(Object);
}
static void
gcsweep(void)
{
Object *l = &gc.objs;
Object *c = l->next;
while(c){
if(c->flag&USING){
c->flag = 0;
l = c;
c = c->next;
continue;
}
Object *t = c;
l->next = c->next;
c = c->next;
freeobj(t);
}
}
static void
gcmark(void)
{
void *_ = 0;
uintptr_t bot = (uintptr_t)&_;
for(; bot < gc.top; bot += sizeof(bot)){
uintptr_t val = (uintptr_t)*(void**)bot;
if(isobj(val))
mark((Object*)val);
}
}
void
gcrun(void)
{
jmp_buf reg;
setjmp(reg);
gcmark();
gcsweep();
gcstatus();
}
void
gcstatus(void)
{
printf("curren=> total:%d using:%d remain:%d\n", gc.total, gc.using, gc.total-gc.using);
}
void*
xalloc(int sz)
{
int *res = calloc(1, sz);
if(res == 0)
panic("Can't allocated %d byte", sz);
return res;
}
void*
xralloc(void *src, int sz)
{
int *p = realloc(src, sz);
if(p == 0)
panic("Can't allocated %d byte", sz);
return p;
}
Object*
newobj(enum OType type)
{
gcrun();
Object *obj = 0;
if(gc.freed.next){
obj = gc.freed.next;
gc.freed.next = obj->next;
obj->next = 0;
}else
panic("not impl yet");
obj->type = type;
pushobj(&gc.objs, obj);
gc.using += sizeof(Object);
return obj;
}
void
gcinit(void *top, int cap)
{
gc.total = cap;
gc.using = 0;
gc.top = (uintptr_t)top;
gc.beg = (uintptr_t)xalloc(cap);
gc.end = gc.beg + cap;
Object *p = &gc.freed;
for(uintptr_t i = gc.beg; i < gc.end; i+=sizeof(Object)){
p = p->next = (Object*)i;
}
}

79
main.c Normal file
View File

@ -0,0 +1,79 @@
#include "dat.h"
#include "fn.h"
#include <setjmp.h>
#include <stdio.h>
jmp_buf err;
static void
SExprint(Object *obj)
{
if(obj == 0)
return;
switch(obj->type){
case OCELL:
printf("(");
SExprint(obj->car);
printf(" . ");
SExprint(obj->cdr);
printf(")");
break;
case OINT:
printf("%ld", obj->num);
break;
case OIDENT:
printf("%s", obj->beg);
break;
case OSTRING:
printf("\"%s\"", obj->beg);
break;
case OBLTIN:
case OSYMBOL:
printf("%s", obj->sym);
break;
case OENV:
printf("<env>");
break;
case OLAMBDA:
printf("<lambda>");
goto func;
case OFUNC:
printf("<func>");
func:
printf("<");
SExprint(obj->params);
SExprint(obj->body);
printf(">");
break;
}
}
void
printexpr(Object *obj)
{
SExprint(obj);
printf("\n");
}
static void
loop(void)
{
Object *root = newenv(&Nil, &Nil, &Nil);
if(setjmp(err) == 1){
skipline();
gcrun();
}
while(1){
gcstatus();
Object *res = nextexpr();
res = eval(root, res);
printexpr(res);
}
}
int
main(int argc, char *argv[])
{
gcinit(&argc, 3000);
loop();
}

26
makefile Normal file
View File

@ -0,0 +1,26 @@
NAME=lisp
OFILES=\
bltin.o\
error.o\
eval.o\
main.o\
gc.o\
obj.o\
str.o\
parser.o
AS=$(CC) -c
CFLAGS=-c -g -O2
all: $(NAME)
%.o: %.c
$(CC) $(CFLAGS) $*.c
$(NAME): $(OFILES)
$(CC) -o $(NAME) $(OFILES)
$(OFILES): dat.h fn.h
clean:
rm -f $(NAME) $(OFILES)

76
obj.c Normal file
View File

@ -0,0 +1,76 @@
#include "dat.h"
#include "fn.h"
#include <stdlib.h>
#include <string.h>
Object*
newint(long val)
{
Object *obj = newobj(OINT);
obj->num = val;
return obj;
}
Object*
newcons(Object *car, Object *cdr)
{
Object *obj = newobj(OCELL);
obj->car = car;
obj->cdr = cdr;
return obj;
}
Object*
newenv(Object* name, Object *vars, Object *up)
{
Object *obj = newobj(OENV);
obj->name = name;
obj->up = up;
obj->vars = vars;
return obj;
}
Object*
newacons(Object *x, Object *y, Object *z)
{
Object *cons = newcons(x, y);
return newcons(cons, z);
}
Object*
newfn(Object *env, Object *params, Object *body)
{
Object *fn = newobj(OFUNC);
fn->params = params;
fn->body = body;
fn->env = env;
return fn;
}
Object*
newsymbol(char *str, int len)
{
static Object *syms[] = {
&Nil, &True, &False, &Minus, &Plus,
&Lambda, &Car, &Cdr, &Quote, &Cons, &Define, &Setq,
};
for(int i = 0; i < sizeof(syms)/sizeof(syms[0]); ++i){
Object *c = syms[i];
if(strlen(c->sym)==len && memcmp(c->sym, str, len) == 0)
return c;
}
Object *obj = newobj(OIDENT);
obj->beg = xalloc(len + 1);
obj->end = obj->ptr = obj->beg + len;
memcpy(obj->beg, str, len+1);
return obj;
}
Object*
newstr(int len)
{
Object *obj = newobj(OSTRING);
obj->ptr = obj->beg = xalloc(len + 1);
obj->end = obj->beg + len;
return obj;
}

183
parser.c Normal file
View File

@ -0,0 +1,183 @@
#include "dat.h"
#include "fn.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define SYMBOL_LEN 64
const char symbolchars[] = "*-=+<>";
static Object* lparlist(void);
static Object* list(void);
static char
get(void)
{
char c = getchar();
if(c == EOF)
panic("EOF");
return c;
}
static void
expect(char x)
{
char y = get();
if(x != y)
error("expected '%c', actual '%c'", x, y);
}
static char
lookup(void)
{
char c = get();
ungetc(c, stdin);
return c;
}
/* skip space */
static char
slookup(void)
{
char c = -1;
while(1){
c = get();
if(isspace(c) == 0)
break;
}
ungetc(c, stdin);
return c;
}
static Object*
symbol(char c)
{
char buf[SYMBOL_LEN+1] = {0,};
int len = 0;
buf[len++] = c;
while(isalnum(lookup()) || strchr(symbolchars, lookup())){
if(len >= sizeof(buf)-1)
error("Symbol too long");
buf[len++] = get();
}
buf[len] = 0;
return newsymbol(buf, len);
}
static long
number(void)
{
long val = get() - '0';
while(isdigit(lookup()))
val = val * 10 + (get() - '0');
return val;
}
static Object*
quote(void)
{
Object *car = &Quote;
Object *ccdr = list();
Object *cdr = newcons(ccdr, &Nil);
return newcons(car, cdr);
}
static Object*
string(void)
{
Object *str = newstr(16);
while(lookup() != '\"'){
strputc(str, get());
}
expect('\"');
return str;
}
static Object*
atom(char c)
{
if(isdigit(c))
return newint(number());
get();
if(c == '-'){
if(isdigit(lookup()))
return newint(-number());
else
return symbol('-');
}
if(c == '"'){
return string();
}
if(isalpha(c) || strchr(symbolchars, c)){
return symbol(c);
}
error("bad char in list '%c'", c);
}
static Object*
lparlist(void)
{
Object *car = 0;
Object *cdr = 0;
char c = slookup();
switch(c){
case '\'':
get();
car = quote();
cdr = lparlist();
return newcons(car, cdr);
case '.':
get();
return list();
case '(':
car = list();
cdr = lparlist();
return newcons(car, cdr);
case ')':
return &Nil;
}
car = atom(c);
cdr = lparlist();
return newcons(car ,cdr);
}
static Object*
list(void)
{
char c = slookup();
switch(c){
case '\'':
get();
return quote();
case '(':{
get();
Object *obj = lparlist();
slookup();
expect(')');
return obj;
}
}
return atom(c);
}
void
skipline(void)
{
for(;;){
switch(get()){
case '\n':
return;
case '\r':
if(lookup() == '\n')
get();
return;
}
}
}
Object*
nextexpr(void)
{
return list();
}

41
str.c Normal file
View File

@ -0,0 +1,41 @@
#include "dat.h"
#include "fn.h"
#include <string.h>
static void
raise(Object *s, int ns)
{
int pos = s->ptr - s->beg;
char *ptr = xralloc(s->beg, ns + 1);
s->beg = ptr;
s->ptr = s->beg + pos;
s->end = s->beg + ns;
}
void
strputc(Object *s, int c)
{
if(s->ptr >= s->end)
raise(s, (s->end - s->beg) * 2);
*s->ptr++ = c;
*s->ptr = 0;
}
void
strputs(Object *s, char *ptr)
{
int l = strlen(ptr);
if(s->ptr + l >= s->end)
raise(s, s->end - s->beg + l);
memcpy(s->ptr, ptr, l);
s->ptr += l;
s->ptr[0] = 0;
}
int
strequal(Object *a, Object *b)
{
int la = a->ptr - a->beg;
int lb = b->ptr - b->beg;
return la == lb && memcmp(a->beg, b->beg, la) == 0;
}