diff --git a/CHANGELOG.org b/CHANGELOG.org index 91184ab..3bea072 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -19,6 +19,8 @@ Added support for \f, \v and \e Added refresh to bootstrap.sh to build seed from cc_x86.s Added missing license headers +Added support for ~expressions +Added prototype for Slow_Lisp build test ** Changed Converted M2-Planet to use GNU style error message diff --git a/cc_core.c b/cc_core.c index c04c368..0d77f8d 100644 --- a/cc_core.c +++ b/cc_core.c @@ -575,6 +575,11 @@ void primary_expr() common_recursion(postfix_expr); emit_out("XOR_ebx_eax_into_eax\n"); } + else if('~' == global_token->s[0]) + { + common_recursion(postfix_expr); + emit_out("NOT_eax\n"); + } else if(global_token->s[0] == '(') { global_token = global_token->next; diff --git a/test/test.answers b/test/test.answers index 14e69a5..bbf2dbb 100644 --- a/test/test.answers +++ b/test/test.answers @@ -9,7 +9,7 @@ a9a3e332d13ded5f80d7431f8717f26527b3722b33ea57760a9a5723dffc099c test/results/t f1c01feb865c4d552033186d9ce50dd39468a7e8aebf762886c13ad3e03b5011 test/results/test08-binary 3b39e72f3de90ed690adfaf6145af46157cef2ec5e72867ac577fa27a0229894 test/results/test09-binary 020e86020819cc4963e6185b22e534fcf8306b6cb116f12643f254a24688ff0a test/results/test10-binary -a9708d6e40a4c00d7afe1163a9d149d46525071e9b8a0f283d82f76b443da182 test/results/test100-binary +0c5850abb4ea2c4ba62005d6783b7b076baf86e2b960a806e10ee25dfc30b620 test/results/test100-binary 3fd11bad4a426ce1ff8fd9c6d7d2b943effae9f3f5740b7376e426e9b0555851 test/results/test11-binary f98ab8e4bb35580e0dde96126d7a56aff66bda208d02c8d89390b40d6cff591c test/results/test12-binary 5051ffca2615144419f8ec1a5d4999486ae81e7781428f59e47e866af97cef92 test/results/test13-binary diff --git a/test/test100/proof.answer b/test/test100/proof.answer index 3046896..ed47876 100644 --- a/test/test100/proof.answer +++ b/test/test100/proof.answer @@ -1 +1 @@ -b882976ea4fb2ede866851a57fd3be0653ac1918b87e183e4c1cd6cb5c60d2b4 test/test100/proof +b39435961127c9f5096e07a54c1aff13b02444d0ca07c61477558c1b1ff999a1 test/test100/proof diff --git a/test/test26/.gitignore b/test/test26/.gitignore new file mode 100644 index 0000000..5b7810f --- /dev/null +++ b/test/test26/.gitignore @@ -0,0 +1,23 @@ +## Copyright (C) 2017 Jeremiah Orians +## This file is part of M2-Planet. +## +## M2-Planet 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 3 of the License, or +## (at your option) any later version. +## +## M2-Planet 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 M2-Planet. If not, see . + +# Ignore the files created by script +*.M1 +*.hex2 +proof + +# A place to put a good run for comparison +actual.M1 diff --git a/test/test26/cleanup.sh b/test/test26/cleanup.sh new file mode 100755 index 0000000..b60c815 --- /dev/null +++ b/test/test26/cleanup.sh @@ -0,0 +1,22 @@ +#! /bin/sh +## Copyright (C) 2017 Jeremiah Orians +## This file is part of M2-Planet. +## +## M2-Planet 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 3 of the License, or +## (at your option) any later version. +## +## M2-Planet 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 M2-Planet. If not, see . + +rm -f test/test26/lisp.M1 +rm -f test/test26/lisp-footer.M1 +rm -f test/test26/lisp.hex2 +rm -f test/test26/proof +exit 0 diff --git a/test/test26/gcc_req.h b/test/test26/gcc_req.h new file mode 100644 index 0000000..4d499f8 --- /dev/null +++ b/test/test26/gcc_req.h @@ -0,0 +1,21 @@ +/* Copyright (C) 2016 Jeremiah Orians + * This file is part of stage0. + * + * stage0 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 3 of the License, or + * (at your option) any later version. + * + * stage0 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 stage0. If not, see . + */ + +#include +#include + +typedef struct cell* (FUNCTION)(struct cell *); diff --git a/test/test26/hello.sh b/test/test26/hello.sh new file mode 100755 index 0000000..128132f --- /dev/null +++ b/test/test26/hello.sh @@ -0,0 +1,58 @@ +#! /bin/sh +## Copyright (C) 2017 Jeremiah Orians +## This file is part of M2-Planet. +## +## M2-Planet 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 3 of the License, or +## (at your option) any later version. +## +## M2-Planet 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 M2-Planet. If not, see . + +set -x +# Build the test +./bin/M2-Planet -f test/test26/lisp.h \ + -f functions/malloc.c \ + -f functions/calloc.c \ + -f functions/numerate_number.c \ + -f functions/match.c \ + -f functions/file.c \ + -f functions/file_print.c \ + -f functions/exit.c \ + -f test/test26/lisp.c \ + -f test/test26/lisp_cell.c \ + -f test/test26/lisp_eval.c \ + -f test/test26/lisp_print.c \ + -f test/test26/lisp_read.c \ + --debug \ + -o test/test26/lisp.M1 || exit 1 + +# Build debug footer +blood-elf -f test/test26/lisp.M1 \ + -o test/test26/lisp-footer.M1 || exit 2 + +# Macro assemble with libc written in M1-Macro +M1 -f test/common_x86/x86_defs.M1 \ + -f functions/libc-core.M1 \ + -f test/test26/lisp.M1 \ + -f test/test26/lisp-footer.M1 \ + --LittleEndian \ + --Architecture 1 \ + -o test/test26/lisp.hex2 || exit 3 + +# Resolve all linkages +hex2 -f test/common_x86/ELF-i386-debug.hex2 \ + -f test/test26/lisp.hex2 \ + --LittleEndian \ + --Architecture 1 \ + --BaseAddress 0x8048000 \ + -o test/results/test26-binary \ + --exec_enable || exit 4 + +exit 0 diff --git a/test/test26/lisp.c b/test/test26/lisp.c new file mode 100644 index 0000000..833aa45 --- /dev/null +++ b/test/test26/lisp.c @@ -0,0 +1,161 @@ +/* Copyright (C) 2016 Jeremiah Orians + * This file is part of stage0. + * + * stage0 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 3 of the License, or + * (at your option) any later version. + * + * stage0 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 stage0. If not, see . + */ + +#include "lisp.h" + +struct file_list +{ + struct file_list* next; + FILE* file; +}; + +/* Prototypes */ +struct cell* eval(struct cell* exp, struct cell* env); +void init_sl3(); +int Readline(FILE* source_file, char* temp); +struct cell* parse(char* program, int size); +void writeobj(FILE *ofp, struct cell* op); +void garbage_init(int number_of_cells); +void garbage_collect(); + +/* Read Eval Print Loop*/ +int REPL(FILE* in, FILE *out) +{ + int read; + input = in; + char* message = calloc(MAX_STRING + 2, sizeof(char)); + read = Readline(in, message); + if(0 == read) + { + return TRUE; + } + struct cell* temp = parse(message, read); + current = temp; + temp = eval(temp, top_env); + writeobj(out, temp); + current = nil; + if(echo) fputc('\n', out); + return FALSE; +} + +void recursively_evaluate(struct file_list* a) +{ + if(NULL == a) return; + recursively_evaluate(a->next); + int Reached_EOF = FALSE; + while(!Reached_EOF) + { + garbage_collect(); + Reached_EOF = REPL(a->file, console_output); + } +} + +/*** Main Driver ***/ +int main(int argc, char **argv) +{ + int number_of_cells = 1000000; + file_output = fopen("/dev/null", "w"); + console_output = stdout; + struct file_list* essential = NULL; + + int i = 1; + while(i <= argc) + { + if(NULL == argv[i]) + { + i = i + 1; + } + else if(match(argv[i], "-c") || match(argv[i], "--console")) + { + console_output = fopen(argv[i + 1], "w"); + if(NULL == console_output) + { + file_print("The file: ", stderr); + file_print(argv[i + 1], stderr); + file_print(" does not appear writable\n", stderr); + exit(EXIT_FAILURE); + } + i = i + 2; + } + else if(match(argv[i], "-f") || match(argv[i], "--file")) + { + struct file_list* new = calloc(1, sizeof(struct file_list)); + new->file = fopen(argv[i + 1], "r"); + if(NULL == new->file) + { + file_print("The file: ", stderr); + file_print(argv[i + 1], stderr); + file_print(" does not appear readable\n", stderr); + exit(EXIT_FAILURE); + } + new->next = essential; + essential = new; + i = i + 2; + } + else if(match(argv[i], "-h") || match(argv[i], "--help")) + { + file_print("Usage: ", stdout); + file_print(argv[0], stdout); + file_print(" -f FILENAME1 {-f FILENAME2}\n", stdout); + exit(EXIT_SUCCESS); + } + else if(match(argv[i], "-m") || match(argv[i], "--memory")) + { + number_of_cells = numerate_string(argv[i + 1]); + i = i + 2; + } + else if(match(argv[i], "-o") || match(argv[i], "--output")) + { + file_output = fopen(argv[i + 1], "w"); + if(NULL == file_output) + { + file_print("The file: ", stderr); + file_print(argv[i + 1], stderr); + file_print(" does not appear writable\n", stderr); + exit(EXIT_FAILURE); + } + i = i + 2; + } + else if(match(argv[i], "-v") || match(argv[i], "--version")) + { + file_print("Slow_Lisp 0.1\n", stdout); + exit(EXIT_SUCCESS); + } + else + { + file_print("Unknown option\n", stderr); + exit(EXIT_FAILURE); + } + } + + /* Our most important initializations */ + garbage_init(number_of_cells); + init_sl3(); + int Reached_EOF; + echo = TRUE; + + recursively_evaluate(essential); + + Reached_EOF = FALSE; + while(!Reached_EOF) + { + garbage_collect(); + Reached_EOF = REPL(stdin, stdout); + } + fclose(file_output); + return 0; +} diff --git a/test/test26/lisp.h b/test/test26/lisp.h new file mode 100644 index 0000000..b2dd5bf --- /dev/null +++ b/test/test26/lisp.h @@ -0,0 +1,87 @@ +/* Copyright (C) 2016 Jeremiah Orians + * This file is part of stage0. + * + * stage0 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 3 of the License, or + * (at your option) any later version. + * + * stage0 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 stage0. If not, see . + */ + +#include "gcc_req.h" + +//CONSTANT FREE 1 +#define FREE 1 +//CONSTANT MARKED 2 +#define MARKED 2 +//CONSTANT INT 4 +#define INT 4 +//CONSTANT SYM 8 +#define SYM 8 +//CONSTANT CONS 16 +#define CONS 16 +//CONSTANT PROC 32 +#define PROC 32 +//CONSTANT PRIMOP 64 +#define PRIMOP 64 +//CONSTANT CHAR 128 +#define CHAR 128 +//CONSTANT STRING 256 +#define STRING 256 + +// CONSTANT FALSE 0 +#define FALSE 0 +// CONSTANT TRUE 1 +#define TRUE 1 + +struct cell +{ + int type; + union + { + struct cell* car; + int value; + char* string; + FUNCTION* function; + }; + struct cell* cdr; + struct cell* env; +}; + +// CONSTANT MAX_STRING 4096 +#define MAX_STRING 4096 + +/* Common functions */ +struct cell* make_cons(struct cell* a, struct cell* b); +int numerate_string(char *a); +char* numerate_number(int a); +int match(char* a, char* b); +void file_print(char* s, FILE* f); + +/* Global objects */ +struct cell *all_symbols; +struct cell *top_env; +struct cell *nil; +struct cell *tee; +struct cell *quote; +struct cell *s_if; +struct cell *s_lambda; +struct cell *s_define; +struct cell *s_setb; +struct cell *s_cond; +struct cell *s_begin; +struct cell *s_let; +struct cell *s_while; +struct cell *current; +FILE* input; +FILE* file_output; +FILE* console_output; +int echo; +int left_to_take; diff --git a/test/test26/lisp_cell.c b/test/test26/lisp_cell.c new file mode 100644 index 0000000..6260ec5 --- /dev/null +++ b/test/test26/lisp_cell.c @@ -0,0 +1,261 @@ +/* Copyright (C) 2016 Jeremiah Orians + * This file is part of stage0. + * + * stage0 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 3 of the License, or + * (at your option) any later version. + * + * stage0 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 stage0. If not, see . + */ + +#include "lisp.h" + +/* Deal with the fact GCC converts the 1 to the size of the structs being iterated over */ +#define CELL_SIZE 1 +//CONSTANT CELL_SIZE 16 + +struct cell *free_cells; +struct cell *gc_block_start; +struct cell *top_allocated; + +void update_remaining() +{ + int count = 0; + struct cell* i = free_cells; + while(NULL != i) + { + count = count + 1; + i = i->cdr; + } + left_to_take = count; +} + +struct cell* insert_ordered(struct cell* i, struct cell* list) +{ + if(NULL == list) + { + return i; + } + + if(i < list) + { + i->cdr = list; + return i; + } + + list->cdr = insert_ordered(i, list->cdr); + return list; +} + +void reclaim_marked() +{ + struct cell* i; + for(i= top_allocated; i >= gc_block_start ; i = i - CELL_SIZE) + { + if(i->type & MARKED) + { + i->type = FREE; + i->car = NULL; + i->cdr = NULL; + i->env = NULL; + free_cells = insert_ordered(i, free_cells); + } + } +} + +void relocate_cell(struct cell* current, struct cell* target, struct cell* list) +{ + for(; NULL != list; list = list->cdr) + { + if(list->car == current) + { + list->car = target; + } + + if(list->cdr == current) + { + list->cdr = target; + } + + if(list->env == current) + { + list->env = target; + } + + if((list->type & CONS)|| list->type & PROC ) + { + relocate_cell(current, target, list->car); + } + } +} + +struct cell* pop_cons(); +void compact(struct cell* list) +{ + for(; NULL != list; list = list->cdr) + { + if((FREE != list->type) && (list > free_cells )) + { + struct cell* temp = pop_cons(); + temp->type = list->type; + temp->car = list->car; + temp->cdr = list->cdr; + temp->env = list->env; + relocate_cell(list, temp, all_symbols); + relocate_cell(list, temp, top_env); + } + + if((list->type & CONS)|| list->type & PROC ) + { + compact(list->car); + } + } +} + + +void mark_all_cells() +{ + struct cell* i; + for(i= gc_block_start; i < top_allocated; i = i + CELL_SIZE) + { + /* if not in the free list */ + if(!(i->type & FREE)) + { + /* Mark it */ + i->type = i->type | MARKED; + } + } +} + +void unmark_cells(struct cell* list, struct cell* stop, int count) +{ + if(count > 1) return; + + for(; NULL != list; list = list->cdr) + { + if(list == stop) count = count + 1; + list->type = list->type & ~MARKED; + + if(list->type & PROC) + { + unmark_cells(list->car, stop, count); + if(NULL != list->env) + { + unmark_cells(list->env, stop, count); + } + } + + if(list->type & CONS) + { + unmark_cells(list->car, stop, count); + } + } +} + +void garbage_collect() +{ + mark_all_cells(); + unmark_cells(current, current, 0); + unmark_cells(all_symbols, all_symbols, 0); + unmark_cells(top_env, top_env, 0); + reclaim_marked(); + update_remaining(); + compact(all_symbols); + compact(top_env); + top_allocated = NULL; +} + +void garbage_init(int number_of_cells) +{ + gc_block_start = calloc(number_of_cells + 1, sizeof(struct cell)); + top_allocated = gc_block_start + number_of_cells; + free_cells = NULL; + garbage_collect(); + top_allocated = NULL; +} + +struct cell* pop_cons() +{ + if(NULL == free_cells) + { + file_print("OOOPS we ran out of cells", stderr); + exit(EXIT_FAILURE); + } + struct cell* i; + i = free_cells; + free_cells = i->cdr; + i->cdr = NULL; + if(i > top_allocated) + { + top_allocated = i; + } + left_to_take = left_to_take - 1; + return i; +} + +struct cell* make_cell(int type, struct cell* a, struct cell* b, struct cell* env) +{ + struct cell* c = pop_cons(); + c->type = type; + c->car = a; + c->cdr = b; + c->env = env; + return c; +} + +struct cell* make_int(int a) +{ + struct cell* c = pop_cons(); + c->type = INT; + c->value = a; + return c; +} + +struct cell* make_char(int a) +{ + struct cell* c = pop_cons(); + c->type = CHAR; + c->value = a; + return c; +} + +struct cell* make_string(char* a) +{ + struct cell* c = pop_cons(); + c->type = STRING; + c->string = a; + return c; +} + +struct cell* make_sym(char* name) +{ + struct cell* c = pop_cons(); + c->type = SYM; + c->string = name; + return c; +} + +struct cell* make_cons(struct cell* a, struct cell* b) +{ + return make_cell(CONS, a, b, nil); +} + +struct cell* make_proc(struct cell* a, struct cell* b, struct cell* env) +{ + return make_cell(PROC, a, b, env); +} + +struct cell* make_prim(void* fun) +{ + struct cell* c = pop_cons(); + c->type = PRIMOP; + c->function = fun; + return c; +} diff --git a/test/test26/lisp_eval.c b/test/test26/lisp_eval.c new file mode 100644 index 0000000..bb9b7de --- /dev/null +++ b/test/test26/lisp_eval.c @@ -0,0 +1,643 @@ +/* Copyright (C) 2016 Jeremiah Orians + * This file is part of stage0. + * + * stage0 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 3 of the License, or + * (at your option) any later version. + * + * stage0 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 stage0. If not, see . + */ + +#include "lisp.h" + +/* Support functions */ +struct cell* findsym(char *name) +{ + struct cell* symlist; + for(symlist = all_symbols; nil != symlist; symlist = symlist->cdr) + { + if(match(name, symlist->car->string)) + { + return symlist; + } + } + return nil; +} + +struct cell* make_sym(char* name); + +struct cell* intern(char *name) +{ + struct cell* op = findsym(name); + if(nil != op) return op->car; + op = make_sym(name); + all_symbols = make_cons(op, all_symbols); + return op; +} + +/*** Environment ***/ +struct cell* extend(struct cell* env, struct cell* symbol, struct cell* value) +{ + return make_cons(make_cons(symbol, value), env); +} + +struct cell* multiple_extend(struct cell* env, struct cell* syms, struct cell* vals) +{ + if(nil == syms) + { + return env; + } + return multiple_extend(extend(env, syms->car, vals->car), syms->cdr, vals->cdr); +} + +struct cell* extend_env(struct cell* sym, struct cell* val, struct cell* env) +{ + env->cdr = make_cons(env->car, env->cdr); + env->car = make_cons(sym, val); + return val; +} + +struct cell* assoc(struct cell* key, struct cell* alist) +{ + if(nil == alist) return nil; + for(; nil != alist; alist = alist->cdr) + { + if(alist->car->car->string == key->string) return alist->car; + } + return nil; +} + +/*** Evaluator (Eval/Apply) ***/ +struct cell* eval(struct cell* exp, struct cell* env); +struct cell* make_proc(struct cell* a, struct cell* b, struct cell* env); +struct cell* evlis(struct cell* exps, struct cell* env) +{ + if(exps == nil) return nil; + return make_cons(eval(exps->car, env), evlis(exps->cdr, env)); +} + +struct cell* progn(struct cell* exps, struct cell* env) +{ + if(exps == nil) return nil; + + struct cell* result; +progn_reset: + result = eval(exps->car, env); + if(exps->cdr == nil) return result; + exps = exps->cdr; + goto progn_reset; +} + +struct cell* exec_func(FUNCTION * func, struct cell* vals) +{ + return func(vals); +} + +struct cell* apply(struct cell* proc, struct cell* vals) +{ + struct cell* temp = nil; + if(proc->type == PRIMOP) + { + temp = exec_func(proc->function, vals); + } + else if(proc->type == PROC) + { + struct cell* env = make_cons(proc->env->car, proc->env->cdr); + temp = progn(proc->cdr, multiple_extend(env, proc->car, vals)); + } + else + { + file_print("Bad argument to apply\n", stderr); + exit(EXIT_FAILURE); + } + return temp; +} + +struct cell* evcond(struct cell* exp, struct cell* env) +{ + /* Return nil but the result is technically undefined per the standard */ + if(nil == exp) + { + return nil; + } + + if(tee == eval(exp->car->car, env)) + { + return eval(exp->car->cdr->car, env); + } + + return evcond(exp->cdr, env); +} + +void garbage_collect(); +struct cell* evwhile(struct cell* exp, struct cell* env) +{ + if(nil == exp) return nil; + struct cell* conditional = eval(exp->cdr->car, env); + + while(tee == conditional) + { + eval(exp->cdr->cdr->car, env); + conditional = eval(exp->cdr->car, env); + if((tee == exp->cdr->car) && (left_to_take < 1000)) garbage_collect(); + } + + return conditional; +} + +struct cell* process_sym(struct cell* exp, struct cell* env); +struct cell* process_cons(struct cell* exp, struct cell* env); + +struct cell* eval(struct cell* exp, struct cell* env) +{ + if(exp == nil) return nil; + if(SYM == exp->type) return process_sym(exp, env); + if(CONS == exp->type) return process_cons(exp, env); + return exp; +} + +struct cell* process_sym(struct cell* exp, struct cell* env) +{ + struct cell* tmp = assoc(exp, env); + if(tmp == nil) + { + file_print("Unbound symbol:", stderr); + file_print(exp->string, stderr); + fputc('\n', stderr); + exit(EXIT_FAILURE); + } + return tmp->cdr; +} + +struct cell* process_if(struct cell* exp, struct cell* env) +{ + if(eval(exp->cdr->car, env) != nil) + { + return eval(exp->cdr->cdr->car, env); + } + return eval(exp->cdr->cdr->cdr->car, env); + +} + +struct cell* process_setb(struct cell* exp, struct cell* env) +{ + struct cell* newval = eval(exp->cdr->cdr->car, env); + struct cell* pair = assoc(exp->cdr->car, env); + pair->cdr = newval; + return newval; +} + +struct cell* process_let(struct cell* exp, struct cell* env) +{ + struct cell* lets; + for(lets = exp->cdr->car; lets != nil; lets = lets->cdr) + { + env = make_cons(make_cons(lets->car->car, eval(lets->car->cdr->car, env)), env); + } + return progn(exp->cdr->cdr, env); +} + +struct cell* process_cons(struct cell* exp, struct cell* env) +{ + if(exp->car == s_if) return process_if(exp, env); + if(exp->car == s_cond) return evcond(exp->cdr, env); + if(exp->car == s_begin) return progn(exp->cdr, env); + if(exp->car == s_lambda) return make_proc(exp->cdr->car, exp->cdr->cdr, env); + if(exp->car == quote) return exp->cdr->car; + if(exp->car == s_define) return(extend_env(exp->cdr->car, eval(exp->cdr->cdr->car, env), env)); + if(exp->car == s_setb) return process_setb(exp, env); + if(exp->car == s_let) return process_let(exp, env); + if(exp->car == s_while) return evwhile(exp, env); + return apply(eval(exp->car, env), evlis(exp->cdr, env)); +} + + +/*** Primitives ***/ +struct cell* prim_apply(struct cell* args) +{ + return apply(args->car, args->cdr->car); +} + +struct cell* nullp(struct cell* args) +{ + if(nil == args->car) return tee; + return nil; +} + +struct cell* make_int(int a); +struct cell* prim_sum(struct cell* args) +{ + if(nil == args) return nil; + + int sum; + for(sum = 0; nil != args; args = args->cdr) + { + sum = sum + args->car->value; + } + return make_int(sum); +} + +struct cell* prim_sub(struct cell* args) +{ + if(nil == args) return nil; + + int sum = args->car->value; + for(args = args->cdr; nil != args; args = args->cdr) + { + sum = sum - args->car->value; + } + return make_int(sum); +} + +struct cell* prim_prod(struct cell* args) +{ + if(nil == args) return nil; + + int prod; + for(prod = 1; nil != args; args = args->cdr) + { + prod = prod * args->car->value; + } + return make_int(prod); +} + +struct cell* prim_div(struct cell* args) +{ + if(nil == args) return make_int(1); + + int div = args->car->value; + for(args = args->cdr; nil != args; args = args->cdr) + { + div = div / args->car->value; + } + return make_int(div); +} + +struct cell* prim_mod(struct cell* args) +{ + if(nil == args) return nil; + + int mod = args->car->value % args->cdr->car->value; + if(nil != args->cdr->cdr) + { + file_print("wrong number of arguments to mod\n", stderr); + exit(EXIT_FAILURE); + } + return make_int(mod); +} + +struct cell* prim_and(struct cell* args) +{ + if(nil == args) return nil; + + for(; nil != args; args = args->cdr) + { + if(tee != args->car) return nil; + } + return tee; +} + +struct cell* prim_or(struct cell* args) +{ + if(nil == args) return nil; + + for(; nil != args; args = args->cdr) + { + if(tee == args->car) return tee; + } + return nil; +} + +struct cell* prim_not(struct cell* args) +{ + if(nil == args) return nil; + + if(tee != args->car) return tee; + return nil; +} + +struct cell* prim_numgt(struct cell* args) +{ + if(nil == args) return nil; + + int temp = args->car->value; + for(args = args->cdr; nil != args; args = args->cdr) + { + if(temp <= args->car->value) + { + return nil; + } + temp = args->car->value; + } + return tee; +} + +struct cell* prim_numge(struct cell* args) +{ + if(nil == args) return nil; + + int temp = args->car->value; + for(args = args->cdr; nil != args; args = args->cdr) + { + if(temp < args->car->value) + { + return nil; + } + temp = args->car->value; + } + return tee; +} + +struct cell* prim_numeq(struct cell* args) +{ + if(nil == args) return nil; + + int temp = args->car->value; + for(args = args->cdr; nil != args; args = args->cdr) + { + if(temp != args->car->value) + { + return nil; + } + } + return tee; +} + +struct cell* prim_numle(struct cell* args) +{ + if(nil == args) return nil; + + int temp = args->car->value; + for(args = args->cdr; nil != args; args = args->cdr) + { + if(temp > args->car->value) + { + return nil; + } + temp = args->car->value; + } + return tee; +} + +struct cell* prim_numlt(struct cell* args) +{ + if(nil == args) return nil; + + int temp = args->car->value; + for(args = args->cdr; nil != args; args = args->cdr) + { + if(temp >= args->car->value) + { + return nil; + } + temp = args->car->value; + } + return tee; +} + +struct cell* prim_listp(struct cell* args) +{ + if(nil == args) return nil; + + if(CONS == args->car->type) + { + return tee; + } + return nil; +} + +struct cell* prim_get_type(struct cell* args) +{ + if(nil == args) return nil; + return make_int(args->car->type); +} + +struct cell* make_cell(int type, struct cell* a, struct cell* b, struct cell* env); +struct cell* prim_set_type(struct cell* args) +{ + if(nil == args) return nil; + return make_cell(args->cdr->car->value, args->car->car, args->car->cdr, args->car->env); +} + +struct cell* prim_output(struct cell* args, FILE* out) +{ + for(; nil != args; args = args->cdr) + { + if(INT == args->car->type) + { + file_print(numerate_number(args->car->value), out); + } + else if(CHAR == args->car->type) + { + fputc(args->car->value, out); + } + else if(CONS == args->car->type) + { + prim_output(args->car, out); + } + else + { + file_print(args->car->string, out); + } + } + return tee; +} + +struct cell* prim_stringeq(struct cell* args) +{ + if(nil == args) return nil; + + char* temp = args->car->string; + for(args = args->cdr; nil != args; args = args->cdr) + { + if(!match(temp, args->car->string)) + { + return nil; + } + } + return tee; +} + +struct cell* prim_display(struct cell* args) +{ + return prim_output(args, console_output); +} + +struct cell* prim_write(struct cell* args) +{ + return prim_output(args, file_output); +} + +struct cell* prim_freecell(struct cell* args) +{ + if(nil == args) + { + file_print("Remaining Cells: ", stdout); + file_print(numerate_number(left_to_take), stdout); + return nil; + } + return make_int(left_to_take); +} + +struct cell* make_char(int a); +struct cell* string_to_list(char* string) +{ + if(NULL == string) return nil; + if(0 == string[0]) return nil; + struct cell* result = make_char(string[0]); + struct cell* tail = string_to_list(string + 1); + return make_cons(result, tail); +} + +struct cell* prim_string_to_list(struct cell* args) +{ + if(nil == args) return nil; + + if(STRING == args->car->type) + { + return string_to_list(args->car->string); + } + return nil; +} + +struct cell* make_string(char* a); +int list_to_string(int index, char* string, struct cell* args) +{ + struct cell* i; + for(i = args; nil != i; i = i->cdr) + { + if(CHAR == i->car->type) + { + string[index] = i->car->value; + index = index + 1; + } + if(CONS == i->car->type) + { + index = list_to_string(index, string, i->car); + } + } + return index; +} + +struct cell* prim_list_to_string(struct cell* args) +{ + if(nil == args) return nil; + char* string = calloc(MAX_STRING + 2, sizeof(char)); + list_to_string(0, string, args); + return make_string(string); +} + +struct cell* prim_echo(struct cell* args) +{ + if(nil == args) return nil; + if(nil == args->car) echo = FALSE; + if(tee == args->car) + { + echo = TRUE; + return make_string(""); + } + return args->car; +} + +struct cell* prim_read_byte(struct cell* args) +{ + if(nil == args) return make_char(fgetc(input)); + return nil; +} + +struct cell* prim_halt(struct cell* args) +{ + /* Cleanup */ + free(args); + fclose(file_output); + + /* Actual important part */ + exit(EXIT_SUCCESS); +} + +struct cell* prim_list(struct cell* args) {return args;} +struct cell* prim_cons(struct cell* args) { return make_cons(args->car, args->cdr->car); } +struct cell* prim_car(struct cell* args) { return args->car->car; } +struct cell* prim_cdr(struct cell* args) { return args->car->cdr; } + +void spinup(struct cell* sym, struct cell* prim) +{ + all_symbols = make_cons(sym, all_symbols); + top_env = extend(top_env, sym, prim); +} + +/*** Initialization ***/ +struct cell* intern(char *name); +struct cell* make_prim(void* fun); +struct cell* make_sym(char* name); +void init_sl3() +{ + /* Special symbols */ + nil = make_sym("nil"); + tee = make_sym("#t"); + quote = make_sym("quote"); + s_if = make_sym("if"); + s_cond = make_sym("cond"); + s_lambda = make_sym("lambda"); + s_define = make_sym("define"); + s_setb = make_sym("set!"); + s_begin = make_sym("begin"); + s_let = make_sym("let"); + s_while = make_sym("while"); + + /* Globals of interest */ + all_symbols = make_cons(nil, nil); + top_env = extend(nil, nil, nil); + + /* Add Eval Specials */ + spinup(tee, tee); + spinup(quote, quote); + spinup(s_if, s_if); + spinup(s_cond, s_cond); + spinup(s_lambda, s_lambda); + spinup(s_define, s_define); + spinup(s_setb, s_setb); + spinup(s_begin, s_begin); + spinup(s_let, s_let); + spinup(s_while, s_while); + + /* Add Primitive Specials */ + spinup(make_sym("apply"), make_prim(prim_apply)); + spinup(make_sym("null?"), make_prim(nullp)); + spinup(make_sym("+"), make_prim(prim_sum)); + spinup(make_sym("-"), make_prim(prim_sub)); + spinup(make_sym("*"), make_prim(prim_prod)); + spinup(make_sym("/"), make_prim(prim_div)); + spinup(make_sym("mod"), make_prim(prim_mod)); + spinup(make_sym("and"), make_prim(prim_and)); + spinup(make_sym("or"), make_prim(prim_or)); + spinup(make_sym("not"), make_prim(prim_not)); + spinup(make_sym(">"), make_prim(prim_numgt)); + spinup(make_sym(">="), make_prim(prim_numge)); + spinup(make_sym("="), make_prim(prim_numeq)); + spinup(make_sym("<="), make_prim(prim_numle)); + spinup(make_sym("<"), make_prim(prim_numlt)); + spinup(make_sym("display"), make_prim(prim_display)); + spinup(make_sym("write"), make_prim(prim_write)); + spinup(make_sym("free_mem"), make_prim(prim_freecell)); + spinup(make_sym("get-type"), make_prim(prim_get_type)); + spinup(make_sym("set-type!"), make_prim(prim_set_type)); + spinup(make_sym("list?"), make_prim(prim_listp)); + spinup(make_sym("list"), make_prim(prim_list)); + spinup(make_sym("list->string"), make_prim(prim_list_to_string)); + spinup(make_sym("string->list"), make_prim(prim_string_to_list)); + spinup(make_sym("string=?"), make_prim(prim_stringeq)); + spinup(make_sym("cons"), make_prim(prim_cons)); + spinup(make_sym("car"), make_prim(prim_car)); + spinup(make_sym("cdr"), make_prim(prim_cdr)); + spinup(make_sym("echo"), make_prim(prim_echo)); + spinup(make_sym("read-byte"), make_prim(prim_read_byte)); + spinup(make_sym("HALT"), make_prim(prim_halt)); +} diff --git a/test/test26/lisp_print.c b/test/test26/lisp_print.c new file mode 100644 index 0000000..7db570c --- /dev/null +++ b/test/test26/lisp_print.c @@ -0,0 +1,77 @@ +/* Copyright (C) 2016 Jeremiah Orians + * This file is part of stage0. + * + * stage0 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 3 of the License, or + * (at your option) any later version. + * + * stage0 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 stage0. If not, see . + */ + +#include "lisp.h" + +void writeobj(FILE *output_file, struct cell* op) +{ + if(!echo) return; + + if(INT == op->type) + { + file_print(numerate_number(op->value), output_file); + } + else if(CONS == op->type) + { + fputc('(', output_file); + do + { + writeobj(output_file, op->car); + if(nil == op->cdr) + { + fputc(')', output_file); + break; + } + op = op->cdr; + if(op->type != CONS) + { + file_print(" . ", output_file); + writeobj(output_file, op); + fputc(')', output_file); + break; + } + fputc(' ', output_file); + } while(TRUE); + } + else if(SYM == op->type) + { + file_print(op->string, output_file); + } + else if(PRIMOP == op->type) + { + file_print("#", output_file); + } + else if(PROC == op->type) + { + file_print("#", output_file); + } + else if(CHAR == op->type) + { + fputc(op->value, output_file); + } + else if(STRING == op->type) + { + file_print(op->string, output_file); + } + else + { + file_print("Type ", stderr); + file_print(numerate_number(op->type), stderr); + file_print(" is unknown\nPrint aborting hard\n", stderr); + exit(EXIT_FAILURE); + } +} diff --git a/test/test26/lisp_read.c b/test/test26/lisp_read.c new file mode 100644 index 0000000..be13950 --- /dev/null +++ b/test/test26/lisp_read.c @@ -0,0 +1,290 @@ +/* Copyright (C) 2016 Jeremiah Orians + * This file is part of stage0. + * + * stage0 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 3 of the License, or + * (at your option) any later version. + * + * stage0 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 stage0. If not, see . + */ + +#include "lisp.h" +#include +#include + +FILE* source_file; +int Reached_EOF; + +struct cell* token_stack; +struct cell* make_sym(char* name); +struct cell* intern(char *name); +struct cell* findsym(char *name); + +/**************************************************************** + * "Convert a string into a list of tokens." * + ****************************************************************/ +struct cell* tokenize(struct cell* head, char* fullstring, int size) +{ + int i = 0; + int done = FALSE; + if((0 >= size) || (0 == fullstring[0])) + { + return head; + } + + char *store = calloc(MAX_STRING + 1, sizeof(char)); + + do + { + int c = fullstring[i]; + if((i > size) || (MAX_STRING <= i)) + { + done = TRUE; + } + else if(34 == c) + { + store[i] = c; + i = i + 1; + while(34 != fullstring[i]) + { + store[i] = fullstring[i]; + i = i + 1; + } + i = i + 1; + done = TRUE; + } + else + { + if((' ' == c) || ('\t' == c) || ('\n' == c) | ('\r' == c)) + { + i = i + 1; + done = TRUE; + } + else + { + store[i] = c; + i = i + 1; + } + } + } while(!done); + + if(i > 1) + { + struct cell* temp = make_sym(store); + temp->cdr = head; + head = temp; + } + else + { + free(store); + } + head = tokenize(head, (fullstring+i), (size - i)); + return head; +} + + +int is_integer(char* a) +{ + if(('0' <= a[0]) && ('9' >= a[0])) + { + return TRUE; + } + + if('-' == a[0]) + { + if(('0' <= a[1]) && ('9' >= a[1])) + { + return TRUE; + } + } + + return FALSE; +} + + +/******************************************************************** + * Numbers become numbers * + * Strings become strings * + * Functions become functions * + * quoted things become quoted * + * Everything is treated like a symbol * + ********************************************************************/ +struct cell* atom(struct cell* a) +{ + /* Check for quotes */ + if('\'' == a->string[0]) + { + a->string = a->string + 1; + return make_cons(quote, make_cons(a, nil)); + } + + /* Check for strings */ + if(34 == a->string[0]) + { + a->type = STRING; + a->string = a->string + 1; + return a; + } + + /* Check for integer */ + if(is_integer(a->string)) + { + a->type = INT; + a->value = numerate_string(a->string); + return a; + } + + /* Check for functions */ + struct cell* op = findsym(a->string); + if(nil != op) + { + return op->car; + } + + /* Assume new symbol */ + all_symbols = make_cons(a, all_symbols); + return a; +} + +/**************************************************************** + * "Read an expression from a sequence of tokens." * + ****************************************************************/ +struct cell* readlist(); +struct cell* readobj() +{ + struct cell* head = token_stack; + token_stack = head->cdr; + head->cdr = NULL; + if (match("(", head->string)) + { + return readlist(); + } + + return atom(head); +} + +struct cell* readlist() +{ + struct cell* head = token_stack; + if (match(")", head->string)) + { + token_stack = head->cdr; + return nil; + } + + struct cell* tmp = readobj(); +/* token_stack = head->cdr; */ + return make_cons(tmp,readlist()); +} + +/**************************************************** + * Put list of tokens in correct order * + ****************************************************/ +struct cell* reverse_list(struct cell* head) +{ + struct cell* root = NULL; + while(NULL != head) + { + struct cell* next = head->cdr; + head->cdr = root; + root = head; + head = next; + } + return root; +} + +/**************************************************** + * "Read a Scheme expression from a string." * + ****************************************************/ +struct cell* parse(char* program, int size) +{ + token_stack = tokenize(NULL, program, size); + if(NULL == token_stack) + { + return nil; + } + token_stack = reverse_list(token_stack); + return readobj(); +} + +/**************************************************** + * Do the heavy lifting of reading an s-expreesion * + ****************************************************/ +unsigned Readline(FILE* source_file, char* temp) +{ + int c; + unsigned i; + unsigned depth = 0; + + for(i = 0; i < MAX_STRING; i = i + 1) + { +restart_comment: + c = fgetc(source_file); + if((-1 == c) || (4 == c)) + { + return i; + } + else if(';' == c) + { + /* drop everything until we hit newline */ + while('\n' != c) + { + c = fgetc(source_file); + } + goto restart_comment; + } + else if('"' == c) + { /* Deal with strings */ + temp[i] = c; + i = i + 1; + c = fgetc(source_file); + while('"' != c) + { + temp[i] = c; + i = i + 1; + c = fgetc(source_file); + } + temp[i] = c; + } + else if((0 == depth) && (('\n' == c) || ('\r' == c) || (' ' == c) || ('\t' == c))) + { + goto Line_complete; + } + else if(('(' == c) || (')' == c)) + { + if('(' == c) + { + depth = depth + 1; + } + + if(')' == c) + { + depth = depth - 1; + } + + temp[i] = ' '; + temp[i+1] = c; + temp[i+2] = ' '; + i = i + 2; + } + else + { + temp[i] = c; + } + } + +Line_complete: + if(1 > i) + { + return Readline(source_file, temp); + } + + return i; +}