Commit 4663ffef authored by Changrong XUE's avatar Changrong XUE

commit

parent b04edc03
# cproject
# Lixp - A lisp implementation by C
## Description
This is a lisp implementation guided by [buildyourownlisp](http://www.buildyourownlisp.com/contents).
Video on [Youtube](https://youtu.be/tpd2qf2y1Ko).
## Compile
Just use `make`, but `libeditline` is required.
```
$ make
```
## Launch
```
$ ./lixp
```
## Usage
Type in the interactive console. You can careate and interact the variables, use some built-in functions and define your own functions.
### arthmetic operation
```
lixp> + 1 (* 2 8)
17
```
### define a function
```
(def {hello} "Hello World")
```
### some built-in function
`list`,`head`, `join`, `tail`
```
lixp> list 1 2 3 4
{1 2 3 4}
lixp> head {1 2 4}
{1}
lixp> join {1 2} {3 4}
{1 2 3 4}
lixp> tail {1 2 3 4}
{2 3 4}
```
### if statement
```
lixp> def {x y} 100 200
ok
lixp> if (== x y) {+ x y} {- x y}
-100
```
### comment
```
; this is a commment.
```
## Description of the files
### util.c/h
Defined some utilities
### shared.c/h
Global shared variables for setting up the parser.
### mpc.c/h
3rd-party lib for tokenizing input, defining a parser.
### lval.c/h
Data structres and functions for `lval`, which represents the minimal unit in the process of the computation of lixp.
### lang.c/h
implementation of the tokenizer and the parser of the lixp.
### functions.c/h
implementation of the built-in functions.
## TODO
* enable user to define a function
* garbage collector
* native type, not using `long ` in C
* user defined type
* OS interaction, i.e. file system
\ No newline at end of file
This diff is collapsed.
#ifndef FUNCTIONS_H
#define FUNCTIONS_H
#include "lang.h"
// define and implement built-in fuctions
typedef enum BUILTIN_OP_TYPE BUILTIN_OP_TYPE;
enum BUILTIN_OP_TYPE {
BUILTIN_OP_ADD, BUILTIN_OP_SUB, BUILTIN_OP_DIV, BUILTIN_OP_MUL, BUILTIN_OP_POW,
BUILTIN_COMP_GT, BUILTIN_COMP_LT, BUILTIN_COMP_GE, BUILTIN_COMP_LE,
BUILTIN_COMP_EQ, BUILTIN_COMP_NEQ,
BUILTIN_LOGICAL_OR, BUILTIN_LOGICAL_AND, BUILTIN_LOGICAL_NOT
};
char* builtin_op_strname(BUILTIN_OP_TYPE op);
void lenv_add_builtin(lenv* env, char* sym, lbuiltin func);
void lenv_add_builtin_funcs(lenv* env);
//Math functions
lval* builtin_op(lenv* env, lval* val, BUILTIN_OP_TYPE op);
lval* builtin_add(lenv* env, lval* val);
lval* builtin_sub(lenv* env, lval* val);
lval* builtin_div(lenv* env, lval* val);
lval* builtin_mul(lenv* env, lval* val);
lval* builtin_pow(lenv* env, lval* val);
//Comparison Functions
lval* builtin_comp_num(lenv* env, lval* val, BUILTIN_OP_TYPE op);
lval* builtin_comp_gt(lenv* env, lval* val);
lval* builtin_comp_lt(lenv* env, lval* val);
lval* builtin_comp_ge(lenv* env, lval* val);
lval* builtin_comp_le(lenv* env, lval* val);
lval* builtin_comp_value(lenv* env, lval* val, BUILTIN_OP_TYPE op);
lval* builtin_comp_eq(lenv* env, lval* val);
lval* builtin_comp_neq(lenv* env, lval* val);
lval* builtin_logical(lenv* env, lval* val, BUILTIN_OP_TYPE op);
lval* builtin_logical_and(lenv* env, lval* val);
lval* builtin_logical_or(lenv* env, lval* val);
lval* builtin_logical_not(lenv* env, lval* val);
//List/Util functions
lval* builtin_list(lenv* env, lval* val);
lval* builtin_eval(lenv* env, lval* val);
lval* builtin_join(lenv* env, lval* val);
lval* builtin_head(lenv* env, lval* val);
lval* builtin_tail(lenv* env, lval* val);
lval* builtin_if(lenv* env, lval* val);
lval* builtin_print(lenv* env, lval* val);
//ENV Functions
lval* builtin_envdef(lenv* env, lval* val, char* type);
lval* builtin_def(lenv* env, lval* val);
lval* builtin_var(lenv* env, lval* val);
lval* builtin_listenv(lenv* env, lval* val);
lval* builtin_exit(lenv* env, lval* val);
lval* builtin_lambda(lenv* env, lval* val);
lval* builtin_load(lenv* env, lval* val);
lval* builtin_loadonce(lenv* env, lval* val);
lval* builtin_do_load(lenv* env, lval* val, BOOL loadonce);
lval* builtin_error(lenv* env, lval* val);
#endif /* FUNCTIONS_H */
#include <float.h>
#include <stdio.h>
#include "mpc.h"
#include "lang.h"
mpc_ast_t* tokenize(char *input) {
mpc_result_t result;
int success = 1;
if (!mpc_parse("<stdin>", input,gLispy, &result)) {
success = 0;
mpc_err_print_to(result.error, stderr);
mpc_err_delete(result.error);
}
return success ? (mpc_ast_t*)result.output : NULL;
}
void setup_parsers() {
mpc_parser_t* Number = mpc_new("number");
mpc_parser_t* Ok = mpc_new("ok");
mpc_parser_t* Symbol = mpc_new("symbol");
mpc_parser_t* String = mpc_new("string");
mpc_parser_t* Comment = mpc_new("comment");
mpc_parser_t* S_Expr = mpc_new("s_expr");
mpc_parser_t* Q_Expr = mpc_new("q_expr");
mpc_parser_t* Expr = mpc_new("expr");
mpc_parser_t* Lispy = mpc_new("lispy");
mpca_lang(MPCA_LANG_DEFAULT,
" \
number : /-?[0-9]+(\\.[0-9]+)?/ ; \
ok : /ok/ ; \
symbol : /[a-zA-Z0-9_+\\-*\\/\\\\=<>!&\\|]+/ ; \
string : /\"(\\\\.|[^\"])*\"/ ; \
comment : /;[^\\r?\\n]*/ ; \
s_expr : '(' <expr>* ')' ; \
q_expr : '{' <expr>* '}' ; \
expr : <number> | <ok> | <symbol> | <string> \
| <comment> | <s_expr> | <q_expr> ; \
lispy : /^/ <expr>+ /$/ ; \
",
Number, Ok, Symbol, String, Comment, Expr, S_Expr, Q_Expr, Lispy);
gLispy = Lispy;
gParsers = calloc(20, sizeof(mpc_parser_t*));
int i = 0;
gParsers[i++] = Number;
gParsers[i++] = Ok;
gParsers[i++] = Symbol;
gParsers[i++] = String;
gParsers[i++] = Comment;
gParsers[i++] = Q_Expr;
gParsers[i++] = S_Expr;
gParsers[i++] = Expr;
gParsers[i++] = Lispy;
gParsers = realloc(gParsers, sizeof(mpc_parser_t*)*i);
gParserCount = i;
}
void cleanup_parsers() {
for (int i = 0; i < gParserCount; i++) { mpc_undefine(gParsers[i]); }
for (int i = 0; i < gParserCount; i++) { mpc_delete(gParsers[i]); }
free(gParsers);
gParsers = NULL;
gParserCount = 0;
gLispy = NULL;
}
lval* parse(mpc_ast_t *t) {
if (strstr(t->tag, "number")) {
errno = 0;
double_t d = strtod(t->contents, NULL);
return errno != 0 ? lval_err_detail(LERR_BAD_NUM, strerror(errno)) : lval_num(d);
}
if (strstr(t->tag, "ok")) {
return lval_ok();
}
if (strstr(t->tag, "symbol")) {
return lval_sym(t->contents);
}
if (strstr(t->tag, "string")) {
return parse_read_string(t);
}
lval* result = NULL;
if (strcmp(t->tag, ">") == 0) { result = lval_s_expr(); }
if (result == NULL && strstr(t->tag, "s_expr") != NULL) { result = lval_s_expr(); }
if (result == NULL && strstr(t->tag, "q_expr") != NULL) { result = lval_q_expr(); }
for (int i = 0; i < t->children_num; i++) {
if (strcmp(t->children[i]->contents, "(") == 0) { continue; }
if (strcmp(t->children[i]->contents, ")") == 0) { continue; }
if (strcmp(t->children[i]->contents, "}") == 0) { continue; }
if (strcmp(t->children[i]->contents, "{") == 0) { continue; }
if (strcmp(t->children[i]->tag, "regex") == 0) { continue; }
if (strstr(t->children[i]->tag, "comment") != NULL) { continue; }
result = lval_add(result, parse(t->children[i]));
}
return result;
}
lval* parse_read_string(mpc_ast_t* t) {
size_t tLen = strlen(t->contents);
//Copy string, excluding quote marks
char* unescaped = calloc(tLen, sizeof(char));
memcpy(unescaped, t->contents+sizeof(char), (tLen-2) * sizeof(char));
//evaluate entered escape characters. ie \n -> new line
unescaped = mpcf_unescape(unescaped);
//return new string
lval* val = lval_str(unescaped);
free(unescaped);
return val;
}
lval* eval(lenv* env, lval* val) {
if (val->type == LVAL_SYM) {
lval* x = lenv_get(env, val);
lval_delete(val);
return x;
}
if (val->type == LVAL_S_EXPR) {
return eval_s_expr(env, val);
}
return val;
}
lval* eval_s_expr(lenv* env, lval* val) {
//Eval children
for(int i = 0; i < val->cell_count; i++) {
val->cell_list[i] = eval(env, val->cell_list[i]);
}
//Check for errors or exit
for(int i = 0; i < val->cell_count; i++) {
if (val->cell_list[i]->type == LVAL_ERR) {
return lval_take(val, i);
}
if (val->cell_list[i]->type == LVAL_EXIT) {
lval* exit = lval_copy(val->cell_list[i]);
lval_delete(val);
return exit;
}
}
//empty
if (val->cell_count == 0) {
return val;
}
//single
if (val->cell_count == 1) {
return lval_take(val, 0);
}
//Ensure first is symbol
lval* func = lval_pop(val, 0);
if (func->type != LVAL_FUNC) {
VAL_TYPE type = func->type;
lval_delete(func);
lval_delete(val);
return lval_err_detail(LERR_BAD_OP, "First element is not %s got %s", lval_str_name(LVAL_FUNC), lval_str_name(type));
}
//Call builtin
lval* result = lval_call(env, func, val);
lval_delete(func);
return result;
}
\ No newline at end of file
#ifndef LANG_H
#define LANG_H
// Set up the parser and related fuction
#include "mpc.h"
#include "lval.h"
#include "lenv.h"
#include "functions.h"
#include "shared.h"
#define LASSERT(val, cond, errnum, errdetail, ...) \
if (!(cond)) { \
lval * err = lval_err_detail(errnum, errdetail, ##__VA_ARGS__); \
lval_delete(val); \
return err; \
}
#define LASSERT_TYPE(name, val, subject, expectedType) \
LASSERT(val, subject->type == expectedType, \
LERR_SYNTAX, "%s Expected type %s got %s", name, lval_str_name(expectedType), lval_str_name(subject->type))
#define LASSERT_ARG_COUNT(name, val, subject, expectedNum) \
LASSERT(val, subject->cell_count == expectedNum, \
LERR_SYNTAX, "%s Expected %ld arguments got %ld", name, expectedNum, subject->cell_count )
#define LASSERT_MIN_ARG_COUNT(name, val, subject, expectedNum) \
LASSERT(val, subject->cell_count >= expectedNum, \
LERR_SYNTAX, "%s Expected %ld or more arguments got %ld", name, expectedNum, subject->cell_count )
mpc_ast_t* tokenize(char *input);
void setup_parsers();
void cleanup_parsers();
lval* parse(mpc_ast_t *t);
lval* parse_read_string(mpc_ast_t* t);
lval* eval(lenv* env, lval* val);
lval* eval_s_expr(lenv* env, lval* val);
#endif /* LANG_H */
#include <stdlib.h>
#include "lang.h"
#include "util.h"
lenv* lenv_new() {
lenv* env = calloc(1, sizeof(lenv));
env->count = 0;
env->parent = NULL;
env->syms = NULL;
env->loaded_files_count = 0;
env->loaded_files = NULL;
return env;
}
void lenv_delete(lenv* env) {
for(int i = 0; i < env->count; i++) {
symtab_delete(env->syms[i]);
}
for(int i = 0; i < env->loaded_files_count; i++) {
free(env->loaded_files[i]);
}
if (env->loaded_files_count > 0) {
free(env->loaded_files);
}
if (env->syms != NULL) {
free(env->syms);
}
env->parent = NULL;
free(env);
}
lenv* lenv_copy(lenv* env) {
lenv* new = lenv_new();
new->parent = env;
new->syms = calloc(env->count, sizeof(symtab*));
for(int i = 0; i < env->count; i++) {
new->syms[i] = symtab_copy(env->syms[i]);
}
return new;
}
int lenv_compare_symtabs(const void *lhs, const void *rhs) {
const struct symtab* l = *(const struct symtab**)lhs;
const struct symtab* r = *(const struct symtab**)rhs;
return strcmp(l->sym, r->sym);
}
void lenv_sort(lenv* env) {
qsort(env->syms, env->count, sizeof(symtab*), lenv_compare_symtabs);
}
symtab* lenv_search(lenv* env, char* sym) {
if (env->count == 0) {
return NULL;
}
symtab* searchElem = symtab_new(sym, NULL);
symtab** searchComp = calloc(1, sizeof(symtab*));
searchComp[0] = searchElem;
symtab** result = bsearch(searchComp, env->syms, env->count, sizeof(symtab*), lenv_compare_symtabs);
free(searchComp);
symtab_delete(searchElem);
if (result == NULL) {
return NULL;
}
return *result;
}
lenv* lenv_get_root(lenv* env) {
if (env->parent == NULL) {
return env;
} else {
return lenv_get_root(env->parent);
}
}
lval* lenv_get(lenv* env, lval* sym) {
LASSERT(sym, sym->type == LVAL_SYM, LERR_BAD_OP, "Expected symbol");
symtab* result = lenv_search(env, sym->data.sym);
if (result != NULL) {
return lval_copy(result->lval);
} else {
if (env->parent != NULL) {
return lenv_get(env->parent, sym);
} else {
return lval_err_detail(LERR_BAD_SYM, "Unbound Symbol '%s'", sym->data.sym);
}
}
}
void lenv_put(lenv* env, lval* key, lval* val) {
symtab* result = lenv_search(env, key->data.sym);
if (result != NULL) {
lval_delete(result->lval);
result->lval = lval_copy(val);
lenv_sort(env);
return;
}
env->count ++;
env->syms = realloc(env->syms, sizeof(symtab*) * env->count);
env->syms[env->count-1] = symtab_new(key->data.sym, val);
lenv_sort(env);
}
void lenv_def(lenv* env, lval* key, lval* val) {
while(env->parent != NULL) {
env = env->parent;
}
lenv_put(env, key, val);
}
symtab* symtab_new(char* sym, lval* lval) {
symtab* new = calloc(1, sizeof(symtab));
new->lval = lval == NULL ? NULL : lval_copy(lval);
new->sym = strdup(sym);
return new;
}
void symtab_delete(symtab* symtab) {
if (symtab->lval != NULL) {
lval_delete(symtab->lval);
}
free(symtab->sym);
free(symtab);
}
symtab* symtab_copy(symtab* symtab) {
if (symtab == NULL) {
return NULL;
}
return symtab_new(symtab->sym, symtab->lval);
}
\ No newline at end of file
#ifndef LENV_H
#define LENV_H
// define data structures and functions used for setting up environment
struct lenv;
typedef struct lenv lenv;
struct symtab;
typedef struct symtab symtab;
struct symtab {
char* sym;
lval* lval;
};
struct lenv {
size_t count;
struct lenv* parent;
char** loaded_files;
size_t loaded_files_count;
struct symtab** syms;
};
lenv* lenv_new();
void lenv_delete(lenv* env);
lenv* lenv_copy(lenv* env);
int lenv_compare_symtabs(const void *a, const void *b);
void lenv_sort(lenv* env);
symtab* lenv_search(lenv* env, char* sym);
lenv* lenv_get_root(lenv* env);
lval* lenv_get(lenv* env, lval* sym);
void lenv_put(lenv* env, lval* key, lval* val);
void lenv_def(lenv* env, lval* key, lval* val);
void lenv_var(lenv* env, lval* key, lval* val);
symtab* symtab_new(char* sym, lval* lval);
void symtab_delete(symtab* symtab);
symtab* symtab_copy(symtab* symtab);
#endif /* LENV_H */
File added
#include <stdlib.h>
#include <float.h>
#include <stdbool.h>
#include "lang.h"
#include "util.h"
lval* lval_new(int type) {
lval* val = calloc(1,sizeof(lval));
val->type = type;
return val;
}
lval* lval_num(double_t x) {
lval* val = lval_new(LVAL_NUM);
val->data.num = x;
return val;
}
lval* lval_sym(char* x) {
lval* val = lval_new(LVAL_SYM);
val->data.sym = strdup(x);
return val;
}
lval* lval_s_expr() {
lval* val = lval_new(LVAL_S_EXPR);
val->cell_count = 0;
val->cell_list = NULL;
return val;
}
lval* lval_q_expr() {
lval* val = lval_new(LVAL_Q_EXPR);
val->cell_count = 0;
val->cell_list = NULL;
return val;
}
lval* lval_builtin(lbuiltin func, char* name) {
lval* val = lval_new(LVAL_FUNC);
val->data.func = calloc(1, sizeof(lval_func));
val->data.func->builtin = func;
val->data.func->name = strdup(name);
return val;
}
lval* lval_lambda(lval* formals, lval* body) {
lval* val = lval_new(LVAL_FUNC);
val->data.func = calloc(1, sizeof(lval_func));
val->data.func->builtin = NULL;
val->data.func->name = NULL;
val->data.func->env = lenv_new();
val->data.func->formals = formals;
val->data.func->body = body;
return val;
}
lval* lval_exit(short exitcode) {
lval* val = lval_new(LVAL_EXIT);
val->data.exitcode = exitcode;
return val;
}
lval* lval_str(char* str) {
lval* val = lval_new(LVAL_STR);
val->data.str = strdup(str);
return val;
}
lval* lval_ok() {
lval* val = lval_new(LVAL_OK);
return val;
}
lval* lval_add(lval* val, lval* x) {
val->cell_count++;
val->cell_list = realloc(val->cell_list, sizeof(lval*)*val->cell_count);
val->cell_list[val->cell_count-1] = x;
return val;
}
lval* lval_pop(lval* val, int index) {
//Get the item
lval* x = val->cell_list[index];
//Re-create the list ignoring the index we are extracting
lval** newList = NULL;
if (val->cell_count-1 > 0) {
newList = calloc(val->cell_count-1, sizeof(lval*));
int k = 0;
for(int i=0; i < val->cell_count; i++) {
if (i == index) {
continue;
}
newList[k++] = val->cell_list[i];
}
}
val->cell_count--;
free(val->cell_list);
val->cell_list = newList;
return x;
}
lval* lval_take(lval* val, int i) {
lval* x = lval_pop(val, i);
lval_delete(val);
return x;
}
lval* lval_join(lval* a, lval* b) {
while(b->cell_count > 0) {
lval_add(a, lval_pop(b,0));
}
lval_delete(b);
return a;
}
lval* lval_call(lenv* env, lval* function, lval* args) {
lval_func* func = function->data.func;
if (func->builtin != NULL) {
return func->builtin(env, args);
}
//Check arg counts
LASSERT(args, func->formals->cell_count <= args->cell_count, LERR_SYNTAX,
"lambda: insufficient arguments. Expected %ld got %ld", func->formals->cell_count, args->cell_count);
for(int i = 0; i < func->formals->cell_count; i++) {
lenv_put(func->env, func->formals->cell_list[i], args->cell_list[i]);
}
if (func->va != NULL) {
lval* vaArgs = lval_q_expr();
for(int i = func->formals->cell_count; i < args->cell_count; i ++ ) {
lval_add(vaArgs, lval_copy(args->cell_list[i]));
}
lenv_put(func->env, func->va, vaArgs);
lval_delete(vaArgs);
}
lval_delete(args);
func->env->parent = env;
return eval(func->env, lval_add(lval_s_expr(), lval_copy(func->body)));
}
bool lval_equal(lval* a, lval* b) {
if (a->type != b->type) {
return FALSE;
}
switch(a->type) {
case LVAL_ERR: return a->data.err.num == b->data.err.num;
case LVAL_NUM: return fabs(a->data.num - b->data.num) <= DBL_EPSILON;
case LVAL_SYM: return strcmp(a->data.sym, b->data.sym) == 0;
case LVAL_STR: return strcmp(a->data.str, b->data.str) == 0;
case LVAL_OK:
case LVAL_EXIT:
return TRUE;
case LVAL_FUNC:
if (a->data.func->builtin != NULL) {
if (b->data.func->builtin != NULL) {
return a->data.func->builtin == b->data.func->builtin;
} else {
return FALSE;
}
} else {
if (b->data.func->builtin == NULL) {
return FALSE;
} else {
return lval_equal(b->data.func->formals, b->data.func->formals)
&& lval_equal(b->data.func->body, b->data.func->body);
}
}
case LVAL_Q_EXPR:
case LVAL_S_EXPR:
if (a->cell_count != b->cell_count) { return 0; }
for (int i = 0; i < a->cell_count; i++) {
if (!lval_equal(a->cell_list[i], b->cell_list[i])) {
return FALSE;
}
}
return TRUE;
default: return FALSE;
}
}
void lval_delete(lval* val) {
switch(val->type) {
case LVAL_NUM: break;
case LVAL_EXIT: break;
case LVAL_OK: break;
case LVAL_FUNC:
if (val->data.func != NULL) {
if (val->data.func->builtin == NULL) {
lenv_delete(val->data.func->env);
lval_delete(val->data.func->formals);
lval_delete(val->data.func->body);
} else {
free(val->data.func->name);
}
}
break;
case LVAL_SYM: free(val->data.sym); break;
case LVAL_STR: free(val->data.str); break;
case LVAL_ERR:
if (val->data.err.detail != NULL) {
free(val->data.err.detail);
}
break;
case LVAL_Q_EXPR:
case LVAL_S_EXPR:
for (int i = 0; i < val->cell_count; i++) {
lval_delete(val->cell_list[i]);
}
if (val->cell_count > 0) {
free(val->cell_list);
}
break;
}
free(val);
}
lval* lval_copy(lval* current) {
lval* new = lval_new(current->type);
switch(current->type) {
case LVAL_FUNC:
new->data.func = calloc(1, sizeof(lval_func));
lval_func* funcNew = new->data.func;
lval_func* funcCurrent = current->data.func;
if (funcCurrent->builtin == NULL) {
funcNew->env = lenv_copy(funcCurrent->env);
funcNew->body = lval_copy(funcCurrent->body);
funcNew->formals = lval_copy(funcCurrent->formals);
} else {
funcNew->builtin = funcCurrent->builtin;
funcNew->name = strdup(funcCurrent->name);
}
break;
case LVAL_NUM: new->data.num = current->data.num; break;
case LVAL_EXIT: break;
case LVAL_OK: break;
case LVAL_SYM: new->data.sym = strdup(current->data.sym); break;
case LVAL_STR: new->data.str = strdup(current->data.str); break;
case LVAL_ERR:
new->data.err.num = current->data.err.num;
new->data.err.detail = current->data.err.detail == NULL ? NULL : strdup(current->data.err.detail);
break;
case LVAL_Q_EXPR:
case LVAL_S_EXPR:
new->cell_count = current->cell_count;
if (new->cell_count == 0) {
new->cell_list = NULL;
} else {
new->cell_list = calloc(new->cell_count, sizeof(lval*));
for(int i=0; i < new->cell_count; i++) {
new->cell_list[i] = lval_copy(current->cell_list[i]);
}
}
break;
}
return new;
}
lval* lval_err(VAL_ERROR err){
return lval_err_detail(err, NULL);
}
lval* lval_err_detail(VAL_ERROR err, char* format, ...){
lval* val = lval_new(LVAL_ERR);
val->data.err.num = err;
va_list va;
va_start(va, format);
val->data.err.detail = calloc(512, sizeof(char));
vsnprintf(val->data.err.detail, 511, format, va);
if (strlen(val->data.err.detail) == 0) {
free(val->data.err.detail);
val->data.err.detail = NULL;
} else {
val->data.err.detail = realloc(val->data.err.detail, strlen(val->data.err.detail)+1);
}
va_end(va);
return val;
}
char* lval_str_name(VAL_TYPE type) {
switch(type) {
case LVAL_FUNC: return "Function";
case LVAL_NUM: return "Numeric";
case LVAL_STR: return "String";
case LVAL_SYM: return "Symbol";
case LVAL_Q_EXPR: return "Q-Expression";
case LVAL_S_EXPR: return "S-Expression";
case LVAL_EXIT: return "Exit";
case LVAL_ERR: return "Error";
case LVAL_OK: return "Ok/Success";
default: return "UNKNOWN";
}
}
\ No newline at end of file
#ifndef LVAL_H
#define LVAL_H
// Define structs representing lisp values and related functions.
// Forward declarations (if thats what they are called)
struct lval;
typedef struct lval lval;
struct lval_func;
typedef struct lval_func lval_func;
#include <stdbool.h>
#include "main.h"
#include "lenv.h"
#define LVAL_IS_TRUE(val) (val->type == LVAL_NUM && fabs(val->data.num) > DBL_EPSILON)
#define LVAL_IS_FALSE(val) (val->type == LVAL_NUM && fabs(val->data.num) <= DBL_EPSILON)
// LAVAL_SYM => symbol like '+'
enum VAL_TYPE { LVAL_ERR, LVAL_NUM, LVAL_SYM, LVAL_FUNC, LVAL_S_EXPR, LVAL_Q_EXPR, LVAL_EXIT, LVAL_STR, LVAL_OK };
enum VAL_ERROR { LERR_DIV_ZERO, LERR_BAD_OP, LERR_BAD_NUM, LERR_BAD_SYM, LERR_OTHER, LERR_SYNTAX, LERR_USER };
typedef enum VAL_TYPE VAL_TYPE;
typedef enum VAL_ERROR VAL_ERROR;
typedef lval*(*lbuiltin)(lenv*, lval*);
struct lval_func {
char* name;
lbuiltin builtin;
lenv* env;
lval* formals;
lval* body;
lval* va;
};
struct lval {
enum VAL_TYPE type;
union {
double_t num;
char* sym;
char* str;
short exitcode;
struct lval_func* func;
struct {
enum VAL_ERROR num;
char* detail;
} err;
} data;
int cell_count;
struct lval** cell_list;
};
// created corresponding lval
lval* lval_new(int type);
lval* lval_num(double_t x);
lval* lval_sym(char* x);
lval* lval_s_expr();
lval* lval_q_expr();
lval* lval_builtin(lbuiltin func, char* name);
lval* lval_lambda(lval* formals, lval* body);
lval* lval_exit(short exitcode);
lval* lval_str(char* str);
lval* lval_ok();
lval* lval_add(lval* val, lval* x);
lval* lval_pop(lval* val, int i);
lval* lval_take(lval* val, int i);
lval* lval_join(lval* a, lval* b);
lval* lval_call(lenv* env, lval* function, lval* args);
bool lval_equal(lval* a, lval* b);
void lval_delete(lval* val);
lval* lval_copy(lval* current);
lval* lval_err(VAL_ERROR x);
lval* lval_err_detail(VAL_ERROR x, char* format, ...);
char* lval_str_name(VAL_TYPE type);
#endif /* LVAL_H */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <editline/readline.h>
#include "mpc.h"
#include "lang.h"
#include "main.h"
#include "util.h"
int main(int argc, char** argv) {
printf("lixp Version %s\n", VERSION);
// Init environment, add built-in
lenv* env = lenv_new();
lenv_add_builtin_funcs(env);
// Using shared variable in shared.c/h represents the parser
setup_parsers();
//Attempt to import/run files specified on the command line
if (argc > 1) {
for(int i = 1; i < argc; i++) {
lval* loadargs = lval_add(lval_s_expr(), lval_str(argv[i]));
lval* result = builtin_load(env, loadargs);
if (result->type == LVAL_ERR) {
lval_println(result);
}
lval_delete(result);
}
}
int exitcode = EXIT_SUCCESS;
while(1) {
char *input = readline("lixp> ");
if (NULL == input) {
break;
}
add_history(input);
// tokenize the input
mpc_ast_t* ast_result = tokenize(input);
free(input);
if (ast_result != NULL) {
//Parse the ast
lval* result = parse(ast_result);
if (result == NULL) {
result = lval_err(LERR_OTHER);
}
//Evaluate
result = eval(env, result);
BOOL exit = FALSE;
if (result != NULL && result->type == LVAL_EXIT) {
exit = TRUE;
exitcode = result->data.exitcode;
} else {
//print the result
lval_println(result);
}
//Cleanup
lval_delete(result);
mpc_ast_delete(ast_result);
if (exit == TRUE) {;
break;
}
}
}
lenv_delete(env);
cleanup_parsers();
return (exitcode);
}
void lval_expr_print(lval* val, char* open, char* close) {
printf("%s", open);
for(int i = 0; i < val->cell_count ; i++) {
lval_print(val->cell_list[i]);
if (i != val->cell_count-1) {
putchar(' ');
}
}
printf("%s", close);
}
void lval_print(lval* val) {
switch(val->type) {
case LVAL_NUM: printf("%g", val->data.num); break;
case LVAL_SYM: printf("%s", val->data.sym); break;
case LVAL_STR: lval_print_str(val); break;
case LVAL_S_EXPR: lval_expr_print(val, "(", ")"); break;
case LVAL_Q_EXPR: lval_expr_print(val, "{", "}"); break;
case LVAL_EXIT: printf("exit"); break;
case LVAL_OK: printf("ok"); break;
case LVAL_FUNC: ;
lval_func* func = val->data.func;
if (func->builtin != NULL) {
printf("<%s>", func->name);
} else {
printf("(<lambda> ");
lval_print(func->formals);
putchar(' ');
lval_print(func->body);
putchar(')');
}
break;
case LVAL_ERR:
switch(val->data.err.num) {
case LERR_DIV_ZERO: fprintf(stderr, "Divide By Zero"); break;
case LERR_BAD_NUM: fprintf(stderr,"Bad Number"); break;
case LERR_BAD_OP: fprintf(stderr,"Invalid Operator"); break;
case LERR_BAD_SYM: fprintf(stderr,"Unknown/Invalid Symbol"); break;
case LERR_OTHER: fprintf(stderr,"Unknown/Other Error"); break;
case LERR_SYNTAX: fprintf(stderr,"Syntax Error"); break;
case LERR_USER: fprintf(stderr,"Runtime Error"); break;
default: fprintf(stderr,"Unknown Error"); break;
}
if (val->data.err.detail != NULL) {
fprintf(stderr,": %s", val->data.err.detail);
}
break;
}
}
void lval_println(lval* val) {
lval_print(val);
putchar('\n');
}
void lval_print_str(lval* val) {
char* escaped = strdup(val->data.str);
escaped = mpcf_escape(escaped);
printf("\"%s\"", escaped);
free(escaped);
}
\ No newline at end of file
#ifndef MAIN_H
#define MAIN_H
#define VERSION "0.0.0.1"
typedef unsigned char BOOL;
#define TRUE 1
#define FALSE 0
int main(int argc, char** argv);
void lval_expr_print(lval* val, char* open, char* close);
void lval_print(lval* val);
void lval_println(lval* val);
void lval_print_str(lval* val);
#endif /* MAIN_H */
CC = gcc
lixp: main.c functions.c lang.c lenv.c lval.c mpc.c shared.c util.c
$(CC) -Wall --std=c99 main.c functions.c lang.c lenv.c lval.c mpc.c shared.c util.c -ledit -lm -o lixp
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
#include <stdlib.h>
#include "shared.h"
#include "mpc.h"
mpc_parser_t* gLispy = NULL;
size_t gParserCount = 0;
mpc_parser_t** gParsers = NULL;
\ No newline at end of file
#ifndef SHARED_H
#define SHARED_H
#include "mpc.h"
// shared variables
extern mpc_parser_t* gLispy;
extern size_t gParserCount;
extern mpc_parser_t** gParsers;
#endif /* SHARED_H */
(def {hello} "Hello World")
hello
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "util.h"
char * strdup(const char* s) {
if (s == NULL) return NULL;
char *d = calloc(strlen (s) + 1, sizeof(char));
if (d == NULL) return NULL;
strcpy (d,s);
return d;
}
char* vstrcat(int n, ...) {
va_list va;
char** strings = calloc(n, sizeof(char*));
size_t stringLength = 0;
va_start(va, n);
for(int i = 0; i<n; i++) {
strings[i] = va_arg(va, char*);
stringLength += strlen(strings[i]);
}
va_end(va);
char* newStr = calloc(stringLength+1, sizeof(char));
for(int i = 0; i<n; i++) {
strcat(newStr, strings[i]);
}
free(strings);
return newStr;
}
\ No newline at end of file
#ifndef UTIL_H
#define UTIL_H
char* strdup(const char* s);
char* vstrcat(int n, ...);
#endif /* UTIL_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment