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
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <unistd.h>
#include "lang.h"
#include "main.h"
#include "util.h"
void lenv_add_builtin(lenv* env, char* sym, lbuiltin func) {
lval* symval = lval_sym(sym);
lval* funcval = lval_builtin(func, sym);
lenv_put(env, symval, funcval);
lval_delete(symval);
lval_delete(funcval);
}
void lenv_add_builtin_funcs(lenv* env) {
//Math functions
lenv_add_builtin(env, "+", builtin_add);
lenv_add_builtin(env, "-", builtin_sub);
lenv_add_builtin(env, "/", builtin_div);
lenv_add_builtin(env, "*", builtin_mul);
lenv_add_builtin(env, "^", builtin_pow);
//Comparison functions
lenv_add_builtin(env, ">", builtin_comp_gt);
lenv_add_builtin(env, "<", builtin_comp_lt);
lenv_add_builtin(env, ">=", builtin_comp_ge);
lenv_add_builtin(env, "<=", builtin_comp_le);
lenv_add_builtin(env, "==", builtin_comp_eq);
lenv_add_builtin(env, "!=", builtin_comp_neq);
lenv_add_builtin(env, "&&", builtin_logical_and);
lenv_add_builtin(env, "||", builtin_logical_or);
lenv_add_builtin(env, "!", builtin_logical_not);
//List/Util functions
lenv_add_builtin(env, "list", builtin_list);
lenv_add_builtin(env, "eval", builtin_eval);
lenv_add_builtin(env, "join", builtin_join);
lenv_add_builtin(env, "head", builtin_head);
lenv_add_builtin(env, "tail", builtin_tail);
lenv_add_builtin(env, "if", builtin_if);
lenv_add_builtin(env, "print", builtin_print);
//ENV Functions
lenv_add_builtin(env, "def", builtin_def);
lenv_add_builtin(env, "var", builtin_var);
lenv_add_builtin(env, "listenv", builtin_listenv);
lenv_add_builtin(env, "exit", builtin_exit);
lenv_add_builtin(env, "lambda", builtin_lambda);
lenv_add_builtin(env, "\\", builtin_lambda);
lenv_add_builtin(env, "load", builtin_load);
lenv_add_builtin(env, "loadonce", builtin_loadonce);
lenv_add_builtin(env, "error", builtin_error);
}
char* builtin_op_strname(BUILTIN_OP_TYPE op) {
switch(op) {
// Operators
case BUILTIN_OP_ADD: return "+";
case BUILTIN_OP_SUB: return "-";
case BUILTIN_OP_MUL: return "*";
case BUILTIN_OP_DIV: return "/";
case BUILTIN_OP_POW: return "^";
// Comparisons
case BUILTIN_COMP_GT: return ">";
case BUILTIN_COMP_LT: return "<";
case BUILTIN_COMP_GE: return ">=";
case BUILTIN_COMP_LE: return "<=";
case BUILTIN_COMP_EQ: return "==";
case BUILTIN_COMP_NEQ: return "!=";
//Logical Operators
case BUILTIN_LOGICAL_AND: return "&&";
case BUILTIN_LOGICAL_OR: return "||";
case BUILTIN_LOGICAL_NOT: return "!";
default: return "UNKNOWN";
}
}
//Start Math Functions
lval* builtin_op(lenv* env, lval* val, BUILTIN_OP_TYPE op) {
//Ensure numbers only
for(int i = 0; i < val->cell_count; i++) {
LASSERT_TYPE(builtin_op_strname(op), val, val->cell_list[i], LVAL_NUM);
}
//Get the first element
lval* x = lval_pop(val, 0);
if (op == BUILTIN_OP_SUB && val->cell_count == 0) {
x->data.num = -x->data.num;
}
while(val->cell_count > 0) {
//Get next to process
lval* y = lval_pop(val, 0);
switch(op) {
case BUILTIN_OP_ADD: x->data.num += y->data.num; break;
case BUILTIN_OP_SUB: x->data.num -= y->data.num; break;
case BUILTIN_OP_MUL: x->data.num *= y->data.num; break;
case BUILTIN_OP_POW: x->data.num = pow(x->data.num,y->data.num); break;
case BUILTIN_OP_DIV: ;
short divZero = 0;
if (y->type == LVAL_NUM && LVAL_IS_FALSE(y)) {divZero = 1;}
if (divZero) {
lval_delete(x);
lval_delete(y);
x = lval_err(LERR_DIV_ZERO);
break;
} else {
x->data.num /= y->data.num;
}
break;
default:
lval_delete(val);
return lval_err_detail(LERR_BAD_OP, "expected operator got %s", builtin_op_strname(op));
break;
}
lval_delete(y);
}
lval_delete(val);
return x;
}
lval* builtin_add(lenv* env, lval* val) {
return builtin_op(env, val, BUILTIN_OP_ADD);
}
lval* builtin_sub(lenv* env, lval* val) {
return builtin_op(env, val, BUILTIN_OP_SUB);
}
lval* builtin_div(lenv* env, lval* val) {
return builtin_op(env, val, BUILTIN_OP_DIV);
}
lval* builtin_mul(lenv* env, lval* val) {
return builtin_op(env, val, BUILTIN_OP_MUL);
}
lval* builtin_pow(lenv* env, lval* val){
return builtin_op(env, val, BUILTIN_OP_POW);
}
//End Math Functions
//Start Comparison Functions
lval* builtin_comp_num(lenv* env, lval* val, BUILTIN_OP_TYPE op) {
char* opName = builtin_op_strname(op);
LASSERT_ARG_COUNT(opName, val, val, 2);
LASSERT_TYPE(opName, val, val->cell_list[0], LVAL_NUM);
LASSERT_TYPE(opName, val, val->cell_list[1], LVAL_NUM);
int r = 0;
switch(op) {
case BUILTIN_COMP_GT: r = val->cell_list[0]->data.num > val->cell_list[1]->data.num; break;
case BUILTIN_COMP_LT: r = val->cell_list[0]->data.num < val->cell_list[1]->data.num; break;
case BUILTIN_COMP_GE: r = val->cell_list[0]->data.num >= val->cell_list[1]->data.num; break;
case BUILTIN_COMP_LE: r = val->cell_list[0]->data.num <= val->cell_list[1]->data.num; break;
default:
lval_delete(val);
return lval_err_detail(LERR_BAD_OP, "Invalid comparison got %s", builtin_op_strname(op));
break;
}
lval_delete(val);
return lval_num(r);
}
lval* builtin_comp_gt(lenv* env, lval* val) {
return builtin_comp_num(env, val, BUILTIN_COMP_GT);
}
lval* builtin_comp_lt(lenv* env, lval* val) {
return builtin_comp_num(env, val, BUILTIN_COMP_LT);
}
lval* builtin_comp_ge(lenv* env, lval* val) {
return builtin_comp_num(env, val, BUILTIN_COMP_GE);
}
lval* builtin_comp_le(lenv* env, lval* val) {
return builtin_comp_num(env, val, BUILTIN_COMP_LE);
}
lval* builtin_comp_value(lenv* env, lval* val, BUILTIN_OP_TYPE op) {
LASSERT_ARG_COUNT(builtin_op_strname(op), val, val, 2);
BOOL result = FALSE;
result = lval_equal(val->cell_list[0], val->cell_list[1]);
if (op == BUILTIN_COMP_NEQ) {
result = !result;
}
return lval_num((int)result);
}
lval* builtin_comp_eq(lenv* env, lval* val) {
return builtin_comp_value(env, val, BUILTIN_COMP_EQ);
}
lval* builtin_comp_neq(lenv* env, lval* val) {
return builtin_comp_value(env, val, BUILTIN_COMP_NEQ);
}
lval* builtin_logical(lenv* env, lval* val, BUILTIN_OP_TYPE op) {
int expectedArgs = op == BUILTIN_LOGICAL_NOT ? 1 : 2;
char* opName = builtin_op_strname(op);
LASSERT_ARG_COUNT(opName, val, val, expectedArgs);
LASSERT_TYPE(opName, val, val->cell_list[0], LVAL_NUM);
if (expectedArgs == 2) {
LASSERT_TYPE(opName, val, val->cell_list[1], LVAL_NUM);
}
BOOL result = FALSE;
switch(op) {
case BUILTIN_LOGICAL_AND:
if (LVAL_IS_TRUE(val->cell_list[0]) && LVAL_IS_TRUE(val->cell_list[1])) {
result = TRUE;
}
break;
case BUILTIN_LOGICAL_OR:
if (LVAL_IS_TRUE(val->cell_list[0]) || LVAL_IS_TRUE(val->cell_list[1])) {
result = TRUE;
}
break;
case BUILTIN_LOGICAL_NOT:
if (LVAL_IS_TRUE(val->cell_list[0])) {
result = FALSE;
} else {
result = TRUE;
}
break;
default:
lval_delete(val);
return lval_err_detail(LERR_BAD_OP, "Expected logical operator, got %s", opName);
}
lval_delete(val);
return lval_num((int)result);
}
lval* builtin_logical_and(lenv* env, lval* val) {
return builtin_logical(env, val, BUILTIN_LOGICAL_AND);
}
lval* builtin_logical_or(lenv* env, lval* val) {
return builtin_logical(env, val, BUILTIN_LOGICAL_OR);
}
lval* builtin_logical_not(lenv* env, lval* val) {
return builtin_logical(env, val, BUILTIN_LOGICAL_NOT);
}
//End Comparison Functions
//Start List/Util functions
lval* builtin_list(lenv* env, lval* val){
val->type = LVAL_Q_EXPR;
return val;
}
lval* builtin_eval(lenv* env, lval* val){
LASSERT_ARG_COUNT("eval", val, val, 1);
LASSERT_TYPE("eval", val, val->cell_list[0], LVAL_Q_EXPR);
lval* x = lval_take(val, 0);
x->type = LVAL_S_EXPR;
return eval(env, x);
}
lval* builtin_join(lenv* env, lval* val){
LASSERT_MIN_ARG_COUNT("join", val, val, 1);
BOOL string = TRUE;
size_t totalStringLength = 0;
for(int i = 0; i < val->cell_count; i++) {
if (val->cell_list[i]->type != LVAL_STR) {
string = FALSE;
break;
} else {
totalStringLength += strlen(val->cell_list[i]->data.str);
}
}
if (string) {
char* newStr = calloc(totalStringLength+1, sizeof(char));
for(int i = 0; i < val->cell_count; i++) {
strcat(newStr, val->cell_list[i]->data.str);
}
lval* newVal = lval_str(newStr);
lval_delete(val);
return newVal;
} else { //Not string, join lists
for(int i = 0; i < val->cell_count; i++) {
LASSERT_TYPE("join", val, val->cell_list[i], LVAL_Q_EXPR);
}
lval* x = lval_pop(val,0);
while(val->cell_count > 0) {
x = lval_join(x, lval_pop(val, 0));
}
return x;
}
}
lval* builtin_head(lenv* env, lval* val){
LASSERT_ARG_COUNT("head", val, val, 1);
if (val->cell_list[0]->type == LVAL_STR) { //Return first character
char* strVal = val->cell_list[0]->data.str;
char* result = calloc(1, sizeof(char));
if (strVal != NULL && strlen(strVal) > 0) {
result = calloc(2, sizeof(char));
result[0] = strVal[0];
}
lval_delete(val);
lval* resultLval = lval_str(result);
free(result);
return resultLval;
}
LASSERT_TYPE("head", val, val->cell_list[0], LVAL_Q_EXPR);
LASSERT_MIN_ARG_COUNT("head", val, val->cell_list[0], 1);
lval* x = lval_take(val, 0);
while(x->cell_count > 1) { lval_delete(lval_pop(x, 1)); }
return x;
}
lval* builtin_tail(lenv* env, lval* val){
LASSERT_ARG_COUNT("tail",val , val, 1);
if (val->cell_list[0]->type == LVAL_STR) { //Return last character
char* strVal = val->cell_list[0]->data.str;
size_t strLength = strlen(strVal);
char* result = calloc(1, sizeof(char));
if (strVal != NULL && strLength > 0) {
result = calloc(2, sizeof(char));
result[0] = strVal[strLength-1];
}
lval_delete(val);
lval* resultLval = lval_str(result);
free(result);
return resultLval;
}
LASSERT_TYPE("tail", val, val->cell_list[0], LVAL_Q_EXPR);
LASSERT_MIN_ARG_COUNT("tail", val, val->cell_list[0], 1);
lval* x = lval_take(val, 0);
lval_delete(lval_pop(x, 0));
return x;
}
lval* builtin_if(lenv* env, lval* val) {
LASSERT_ARG_COUNT("if", val, val, 3);
LASSERT_TYPE("if", val, val->cell_list[0], LVAL_NUM);
LASSERT_TYPE("if", val, val->cell_list[1], LVAL_Q_EXPR);
LASSERT_TYPE("if", val, val->cell_list[2], LVAL_Q_EXPR);
lval* result = NULL;
val->cell_list[1]->type = LVAL_S_EXPR;
val->cell_list[2]->type = LVAL_S_EXPR;
if (LVAL_IS_TRUE(val->cell_list[0])) {
result = eval(env, lval_pop(val, 1));
} else {
result = eval(env, lval_pop(val, 2));
}
lval_delete(val);
return result;
}
lval* builtin_print(lenv* env, lval* val) {
for(int i = 0; i < val->cell_count; i++) {
lval_print(val->cell_list[i]);
putchar(' ');
}
putchar('\n');
return lval_ok();
}
//End List/Util functions
//Start ENV Functions
lval* builtin_envdef(lenv* env, lval* val, char* type){
LASSERT_MIN_ARG_COUNT(type, val, val, 1);
LASSERT_TYPE(type, val, val->cell_list[0], LVAL_Q_EXPR);
lval* symbols = val->cell_list[0];
for(int i = 0; i < symbols->cell_count; i++) {
LASSERT_TYPE(type, val, symbols->cell_list[i], LVAL_SYM);
}
LASSERT(val, symbols->cell_count == val->cell_count -1,
LERR_OTHER, "%s: incorrect number of definitions for symbols", type);
for(int i = 0; i < symbols->cell_count; i++) {
if (strcmp(type, "def") == 0) {
lenv_def(env, symbols->cell_list[i], val->cell_list[i+1]);
} else if (strcmp(type, "var") == 0) {
lenv_put(env, symbols->cell_list[i], val->cell_list[i+1]);
}
}
lval_delete(val);
return lval_ok();
}
lval* builtin_var(lenv* env, lval* val) {
return builtin_envdef(env, val, "var");
}
lval* builtin_def(lenv* env, lval* val) {
return builtin_envdef(env, val, "def");
}
lval* builtin_listenv(lenv* env, lval* val) {
for(int i=0; i< env->count; i++) {
printf("%s: ", env->syms[i]->sym);
lval_println(env->syms[i]->lval);
}
lval_delete(val);
return lval_ok();
}
lval* builtin_exit(lenv* env, lval* val) {
LASSERT_ARG_COUNT("exit", val, val, 1);
LASSERT_TYPE("exit", val, val->cell_list[0], LVAL_NUM);
double exitcode = val->cell_list[0]->data.num;
exitcode = floor(exitcode >= 0 ? exitcode+0.5 : exitcode-0.5);
if (exitcode < SHRT_MIN) {
exitcode = SHRT_MIN;
} else if (exitcode > SHRT_MAX) {
exitcode = SHRT_MAX;
}
lval_delete(val);
return lval_exit((short)exitcode);
}
lval* builtin_lambda(lenv* env, lval* val) {
LASSERT_ARG_COUNT("lambda", val, val, 2);
LASSERT_TYPE("lambda", val, val->cell_list[0], LVAL_Q_EXPR);
LASSERT_TYPE("lambda", val, val->cell_list[1], LVAL_Q_EXPR);
lval* symbols = val->cell_list[0];
lval* va = NULL;
for(int i = 0; i < symbols->cell_count; i++) {
LASSERT_TYPE("lambda args", val, symbols->cell_list[i], LVAL_SYM);
if (strcmp("&", symbols->cell_list[i]->data.sym) == 0 && i+1 == symbols->cell_count) {
va = lval_pop(symbols, i);
break;
}
}
lval* formals = lval_pop(val, 0);
lval* body = lval_pop(val, 0);
body->type = LVAL_S_EXPR;
lval* lambda = lval_lambda(formals, body);
lambda->data.func->va = va;
lval_delete(val);
return lambda;
}
lval* builtin_loadonce(lenv* env, lval* val) {
return builtin_do_load(env, val, TRUE);
}
lval* builtin_load(lenv* env, lval* val) {
return builtin_do_load(env, val, FALSE);
}
lval* builtin_do_load(lenv* env, lval* val, BOOL loadonce) {
LASSERT_ARG_COUNT("load", val, val, 1);
LASSERT_TYPE("load", val, val->cell_list[0], LVAL_STR);
lval* fileval = val->cell_list[0];
lenv* rootenv = lenv_get_root(env);
char* filename = fileval->data.str;
BOOL file_loaded = FALSE;
for(int i =0 ; i < rootenv->loaded_files_count; i++) {
if (strcmp(filename, rootenv->loaded_files[i]) == 0) {
file_loaded = TRUE;
break;
}
}
if (loadonce == TRUE && file_loaded == TRUE) {
lval_delete(val);
return lval_ok();
}
mpc_result_t result;
if (mpc_parse_contents(filename, gLispy, &result)) {
//Evaluate the read lisp file
lval* resultLval = parse(result.output);
mpc_ast_delete(result.output);
while(resultLval->cell_count > 0) {
lval* x = eval(env, lval_pop(resultLval, 0));
if (x->type == LVAL_ERR) {
lval_println(x);
}
lval_delete(x);
}
if (file_loaded == FALSE) {
rootenv->loaded_files = realloc(rootenv->loaded_files, sizeof(char*) * rootenv->loaded_files_count+1);
rootenv->loaded_files[rootenv->loaded_files_count] = strdup(filename);
rootenv->loaded_files_count++;
}
lval_delete(resultLval);
lval_delete(val);
return lval_ok();
} else {
//Parse error
char* errorMessage = mpc_err_string(result.error);
mpc_err_delete(result.error);
lval* err = lval_err_detail(LERR_OTHER,"Load: %s", errorMessage);
free(errorMessage);
lval_delete(val);
return err;
}
}
lval* builtin_error(lenv* env, lval* val) {
LASSERT_ARG_COUNT("error", val, val, 1);
LASSERT_TYPE("error", val, val->cell_list[0], LVAL_STR);
lval* errorVal = lval_err_detail(LERR_USER, "%s", val->cell_list[0]->data.str);
lval_delete(val);
return errorVal;
}
//End ENV Functions
\ No newline at end of file
#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 source diff could not be displayed because it is too large. You can view the blob instead.
/*
** mpc - Micro Parser Combinator library for C
**
** https://github.com/orangeduck/mpc
**
** Daniel Holden - contact@daniel-holden.com
** Licensed under BSD3
*/
#ifndef mpc_h
#define mpc_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <ctype.h>
/*
** State Type
*/
typedef struct {
long pos;
long row;
long col;
int term;
} mpc_state_t;
/*
** Error Type
*/
typedef struct {
mpc_state_t state;
int expected_num;
char *filename;
char *failure;
char **expected;
char received;
} mpc_err_t;
void mpc_err_delete(mpc_err_t *e);
char *mpc_err_string(mpc_err_t *e);
void mpc_err_print(mpc_err_t *e);
void mpc_err_print_to(mpc_err_t *e, FILE *f);
/*
** Parsing
*/
typedef void mpc_val_t;
typedef union {
mpc_err_t *error;
mpc_val_t *output;
} mpc_result_t;
struct mpc_parser_t;
typedef struct mpc_parser_t mpc_parser_t;
int mpc_parse(const char *filename, const char *string, mpc_parser_t *p, mpc_result_t *r);
int mpc_nparse(const char *filename, const char *string, size_t length, mpc_parser_t *p, mpc_result_t *r);
int mpc_parse_file(const char *filename, FILE *file, mpc_parser_t *p, mpc_result_t *r);
int mpc_parse_pipe(const char *filename, FILE *pipe, mpc_parser_t *p, mpc_result_t *r);
int mpc_parse_contents(const char *filename, mpc_parser_t *p, mpc_result_t *r);
/*
** Function Types
*/
typedef void(*mpc_dtor_t)(mpc_val_t*);
typedef mpc_val_t*(*mpc_ctor_t)(void);
typedef mpc_val_t*(*mpc_apply_t)(mpc_val_t*);
typedef mpc_val_t*(*mpc_apply_to_t)(mpc_val_t*,void*);
typedef mpc_val_t*(*mpc_fold_t)(int,mpc_val_t**);
typedef int(*mpc_check_t)(mpc_val_t**);
typedef int(*mpc_check_with_t)(mpc_val_t**,void*);
/*
** Building a Parser
*/
mpc_parser_t *mpc_new(const char *name);
mpc_parser_t *mpc_copy(mpc_parser_t *a);
mpc_parser_t *mpc_define(mpc_parser_t *p, mpc_parser_t *a);
mpc_parser_t *mpc_undefine(mpc_parser_t *p);
void mpc_delete(mpc_parser_t *p);
void mpc_cleanup(int n, ...);
/*
** Basic Parsers
*/
mpc_parser_t *mpc_any(void);
mpc_parser_t *mpc_char(char c);
mpc_parser_t *mpc_range(char s, char e);
mpc_parser_t *mpc_oneof(const char *s);
mpc_parser_t *mpc_noneof(const char *s);
mpc_parser_t *mpc_satisfy(int(*f)(char));
mpc_parser_t *mpc_string(const char *s);
/*
** Other Parsers
*/
mpc_parser_t *mpc_pass(void);
mpc_parser_t *mpc_fail(const char *m);
mpc_parser_t *mpc_failf(const char *fmt, ...);
mpc_parser_t *mpc_lift(mpc_ctor_t f);
mpc_parser_t *mpc_lift_val(mpc_val_t *x);
mpc_parser_t *mpc_anchor(int(*f)(char,char));
mpc_parser_t *mpc_state(void);
/*
** Combinator Parsers
*/
mpc_parser_t *mpc_expect(mpc_parser_t *a, const char *e);
mpc_parser_t *mpc_expectf(mpc_parser_t *a, const char *fmt, ...);
mpc_parser_t *mpc_apply(mpc_parser_t *a, mpc_apply_t f);
mpc_parser_t *mpc_apply_to(mpc_parser_t *a, mpc_apply_to_t f, void *x);
mpc_parser_t *mpc_check(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *e);
mpc_parser_t *mpc_check_with(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *e);
mpc_parser_t *mpc_checkf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_t f, const char *fmt, ...);
mpc_parser_t *mpc_check_withf(mpc_parser_t *a, mpc_dtor_t da, mpc_check_with_t f, void *x, const char *fmt, ...);
mpc_parser_t *mpc_not(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_not_lift(mpc_parser_t *a, mpc_dtor_t da, mpc_ctor_t lf);
mpc_parser_t *mpc_maybe(mpc_parser_t *a);
mpc_parser_t *mpc_maybe_lift(mpc_parser_t *a, mpc_ctor_t lf);
mpc_parser_t *mpc_many(mpc_fold_t f, mpc_parser_t *a);
mpc_parser_t *mpc_many1(mpc_fold_t f, mpc_parser_t *a);
mpc_parser_t *mpc_count(int n, mpc_fold_t f, mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_or(int n, ...);
mpc_parser_t *mpc_and(int n, mpc_fold_t f, ...);
mpc_parser_t *mpc_predictive(mpc_parser_t *a);
/*
** Common Parsers
*/
mpc_parser_t *mpc_eoi(void);
mpc_parser_t *mpc_soi(void);
mpc_parser_t *mpc_boundary(void);
mpc_parser_t *mpc_boundary_newline(void);
mpc_parser_t *mpc_whitespace(void);
mpc_parser_t *mpc_whitespaces(void);
mpc_parser_t *mpc_blank(void);
mpc_parser_t *mpc_newline(void);
mpc_parser_t *mpc_tab(void);
mpc_parser_t *mpc_escape(void);
mpc_parser_t *mpc_digit(void);
mpc_parser_t *mpc_hexdigit(void);
mpc_parser_t *mpc_octdigit(void);
mpc_parser_t *mpc_digits(void);
mpc_parser_t *mpc_hexdigits(void);
mpc_parser_t *mpc_octdigits(void);
mpc_parser_t *mpc_lower(void);
mpc_parser_t *mpc_upper(void);
mpc_parser_t *mpc_alpha(void);
mpc_parser_t *mpc_underscore(void);
mpc_parser_t *mpc_alphanum(void);
mpc_parser_t *mpc_int(void);
mpc_parser_t *mpc_hex(void);
mpc_parser_t *mpc_oct(void);
mpc_parser_t *mpc_number(void);
mpc_parser_t *mpc_real(void);
mpc_parser_t *mpc_float(void);
mpc_parser_t *mpc_char_lit(void);
mpc_parser_t *mpc_string_lit(void);
mpc_parser_t *mpc_regex_lit(void);
mpc_parser_t *mpc_ident(void);
/*
** Useful Parsers
*/
mpc_parser_t *mpc_startwith(mpc_parser_t *a);
mpc_parser_t *mpc_endwith(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_whole(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_stripl(mpc_parser_t *a);
mpc_parser_t *mpc_stripr(mpc_parser_t *a);
mpc_parser_t *mpc_strip(mpc_parser_t *a);
mpc_parser_t *mpc_tok(mpc_parser_t *a);
mpc_parser_t *mpc_sym(const char *s);
mpc_parser_t *mpc_total(mpc_parser_t *a, mpc_dtor_t da);
mpc_parser_t *mpc_between(mpc_parser_t *a, mpc_dtor_t ad, const char *o, const char *c);
mpc_parser_t *mpc_parens(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_braces(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_brackets(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_squares(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_tok_between(mpc_parser_t *a, mpc_dtor_t ad, const char *o, const char *c);
mpc_parser_t *mpc_tok_parens(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_tok_braces(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_tok_brackets(mpc_parser_t *a, mpc_dtor_t ad);
mpc_parser_t *mpc_tok_squares(mpc_parser_t *a, mpc_dtor_t ad);
/*
** Common Function Parameters
*/
void mpcf_dtor_null(mpc_val_t *x);
mpc_val_t *mpcf_ctor_null(void);
mpc_val_t *mpcf_ctor_str(void);
mpc_val_t *mpcf_free(mpc_val_t *x);
mpc_val_t *mpcf_int(mpc_val_t *x);
mpc_val_t *mpcf_hex(mpc_val_t *x);
mpc_val_t *mpcf_oct(mpc_val_t *x);
mpc_val_t *mpcf_float(mpc_val_t *x);
mpc_val_t *mpcf_strtriml(mpc_val_t *x);
mpc_val_t *mpcf_strtrimr(mpc_val_t *x);
mpc_val_t *mpcf_strtrim(mpc_val_t *x);
mpc_val_t *mpcf_escape(mpc_val_t *x);
mpc_val_t *mpcf_escape_regex(mpc_val_t *x);
mpc_val_t *mpcf_escape_string_raw(mpc_val_t *x);
mpc_val_t *mpcf_escape_char_raw(mpc_val_t *x);
mpc_val_t *mpcf_unescape(mpc_val_t *x);
mpc_val_t *mpcf_unescape_regex(mpc_val_t *x);
mpc_val_t *mpcf_unescape_string_raw(mpc_val_t *x);
mpc_val_t *mpcf_unescape_char_raw(mpc_val_t *x);
mpc_val_t *mpcf_null(int n, mpc_val_t** xs);
mpc_val_t *mpcf_fst(int n, mpc_val_t** xs);
mpc_val_t *mpcf_snd(int n, mpc_val_t** xs);
mpc_val_t *mpcf_trd(int n, mpc_val_t** xs);
mpc_val_t *mpcf_fst_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_snd_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_trd_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_all_free(int n, mpc_val_t** xs);
mpc_val_t *mpcf_freefold(int n, mpc_val_t** xs);
mpc_val_t *mpcf_strfold(int n, mpc_val_t** xs);
mpc_val_t *mpcf_maths(int n, mpc_val_t** xs);
/*
** Regular Expression Parsers
*/
enum {
MPC_RE_DEFAULT = 0,
MPC_RE_M = 1,
MPC_RE_S = 2,
MPC_RE_MULTILINE = 1,
MPC_RE_DOTALL = 2
};
mpc_parser_t *mpc_re(const char *re);
mpc_parser_t *mpc_re_mode(const char *re, int mode);
/*
** AST
*/
typedef struct mpc_ast_t {
char *tag;
char *contents;
mpc_state_t state;
int children_num;
struct mpc_ast_t** children;
} mpc_ast_t;
mpc_ast_t *mpc_ast_new(const char *tag, const char *contents);
mpc_ast_t *mpc_ast_build(int n, const char *tag, ...);
mpc_ast_t *mpc_ast_add_root(mpc_ast_t *a);
mpc_ast_t *mpc_ast_add_child(mpc_ast_t *r, mpc_ast_t *a);
mpc_ast_t *mpc_ast_add_tag(mpc_ast_t *a, const char *t);
mpc_ast_t *mpc_ast_add_root_tag(mpc_ast_t *a, const char *t);
mpc_ast_t *mpc_ast_tag(mpc_ast_t *a, const char *t);
mpc_ast_t *mpc_ast_state(mpc_ast_t *a, mpc_state_t s);
void mpc_ast_delete(mpc_ast_t *a);
void mpc_ast_print(mpc_ast_t *a);
void mpc_ast_print_to(mpc_ast_t *a, FILE *fp);
int mpc_ast_get_index(mpc_ast_t *ast, const char *tag);
int mpc_ast_get_index_lb(mpc_ast_t *ast, const char *tag, int lb);
mpc_ast_t *mpc_ast_get_child(mpc_ast_t *ast, const char *tag);
mpc_ast_t *mpc_ast_get_child_lb(mpc_ast_t *ast, const char *tag, int lb);
typedef enum {
mpc_ast_trav_order_pre,
mpc_ast_trav_order_post
} mpc_ast_trav_order_t;
typedef struct mpc_ast_trav_t {
mpc_ast_t *curr_node;
struct mpc_ast_trav_t *parent;
int curr_child;
mpc_ast_trav_order_t order;
} mpc_ast_trav_t;
mpc_ast_trav_t *mpc_ast_traverse_start(mpc_ast_t *ast,
mpc_ast_trav_order_t order);
mpc_ast_t *mpc_ast_traverse_next(mpc_ast_trav_t **trav);
void mpc_ast_traverse_free(mpc_ast_trav_t **trav);
/*
** Warning: This function currently doesn't test for equality of the `state` member!
*/
int mpc_ast_eq(mpc_ast_t *a, mpc_ast_t *b);
mpc_val_t *mpcf_fold_ast(int n, mpc_val_t **as);
mpc_val_t *mpcf_str_ast(mpc_val_t *c);
mpc_val_t *mpcf_state_ast(int n, mpc_val_t **xs);
mpc_parser_t *mpca_tag(mpc_parser_t *a, const char *t);
mpc_parser_t *mpca_add_tag(mpc_parser_t *a, const char *t);
mpc_parser_t *mpca_root(mpc_parser_t *a);
mpc_parser_t *mpca_state(mpc_parser_t *a);
mpc_parser_t *mpca_total(mpc_parser_t *a);
mpc_parser_t *mpca_not(mpc_parser_t *a);
mpc_parser_t *mpca_maybe(mpc_parser_t *a);
mpc_parser_t *mpca_many(mpc_parser_t *a);
mpc_parser_t *mpca_many1(mpc_parser_t *a);
mpc_parser_t *mpca_count(int n, mpc_parser_t *a);
mpc_parser_t *mpca_or(int n, ...);
mpc_parser_t *mpca_and(int n, ...);
enum {
MPCA_LANG_DEFAULT = 0,
MPCA_LANG_PREDICTIVE = 1,
MPCA_LANG_WHITESPACE_SENSITIVE = 2
};
mpc_parser_t *mpca_grammar(int flags, const char *grammar, ...);
mpc_err_t *mpca_lang(int flags, const char *language, ...);
mpc_err_t *mpca_lang_file(int flags, FILE *f, ...);
mpc_err_t *mpca_lang_pipe(int flags, FILE *f, ...);
mpc_err_t *mpca_lang_contents(int flags, const char *filename, ...);
/*
** Misc
*/
void mpc_print(mpc_parser_t *p);
void mpc_optimise(mpc_parser_t *p);
void mpc_stats(mpc_parser_t *p);
int mpc_test_pass(mpc_parser_t *p, const char *s, const void *d,
int(*tester)(const void*, const void*),
mpc_dtor_t destructor,
void(*printer)(const void*));
int mpc_test_fail(mpc_parser_t *p, const char *s, const void *d,
int(*tester)(const void*, const void*),
mpc_dtor_t destructor,
void(*printer)(const void*));
#ifdef __cplusplus
}
#endif
#endif
#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