diff --git a/functions.c b/functions.c index 60d2cf2..37252d7 100644 --- a/functions.c +++ b/functions.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "lang.h" #include "main.h" @@ -48,6 +50,7 @@ void lenv_add_builtin_funcs(lenv* env) { 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); } char* builtin_op_strname(BUILTIN_OP_TYPE op) { @@ -347,17 +350,20 @@ lval* builtin_listenv(lenv* env, lval* val) { } lval* builtin_exit(lenv* env, lval* val) { - lval* args = lval_q_expr(); - for(int i=0; icell_count; i++) { - lval_add(args, lval_copy(val->cell_list[i])); + 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* sym = lval_sym("exitcode"); - lenv_put(env, sym, args); - lval_delete(args); - lval_delete(sym); lval_delete(val); - return lval_exit(); + return lval_exit((short)exitcode); } lval* builtin_lambda(lenv* env, lval* val) { @@ -385,4 +391,45 @@ lval* builtin_lambda(lenv* env, lval* val) { lval_delete(val); return lambda; } +lval* builtin_load(lenv* env, lval* val) { + LASSERT_ARG_COUNT("load", val, val, 1); + LASSERT_TYPE("load", val, val->cell_list[0], LVAL_STR); + + char* filename = val->cell_list[0]->data.str; + + 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); + lval_println(resultLval); + while(resultLval->cell_count > 0) { + lval* x = eval(env, lval_pop(resultLval, 0)); + if (x->type == LVAL_ERR) { + lval_println(x); + } + lval_delete(x); + } + + lval_delete(resultLval); + lval_delete(val); + + return lval_s_expr(); + } else { + //Parse error + char* errorMessage = mpc_err_string(result.error); + mpc_err_delete(result.error); + + char cwd[1024]; + getcwd(cwd, sizeof(cwd)-1); + printf("dir: %s\n", cwd); + + lval* err = lval_err_detail(LERR_OTHER,"Load: %s", errorMessage); + free(errorMessage); + lval_delete(val); + + return err; + } +} //End ENV Functions \ No newline at end of file diff --git a/functions.h b/functions.h index 77739d1..3a65e3d 100644 --- a/functions.h +++ b/functions.h @@ -66,6 +66,7 @@ extern "C" { 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); #ifdef __cplusplus } diff --git a/lang.c b/lang.c index 5115081..325a813 100644 --- a/lang.c +++ b/lang.c @@ -5,9 +5,22 @@ #include "lang.h" mpc_ast_t* tokenize(char *input) { + mpc_result_t result; + int success = 1; + if (!mpc_parse("", input,gLispy, &result)) { + success = 0; + mpc_err_print(result.error); + 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* 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"); @@ -16,26 +29,38 @@ mpc_ast_t* tokenize(char *input) { mpca_lang(MPCA_LANG_DEFAULT, " \ number : /-?[0-9]+(\\.[0-9]+)?/ ; \ - symbol : /[a-zA-Z0-9_+\\-*\\/\\\\=<>!&\\|]+/ ; \ + symbol : /[a-zA-Z0-9_+\\-*\\/\\\\=<>!&\\|]+/ ; \ + string : /\"(\\\\.|[^\"])*\"/ ; \ + comment : /;[^\\r?\\n]*/ ; \ s_expr : '(' * ')' ; \ q_expr : '{' * '}' ; \ - expr : | | | ; \ + expr : | | \ + | | | ; \ lispy : /^/ + /$/ ; \ ", - Number, Symbol, Expr, S_Expr, Q_Expr, Lispy); + Number, Symbol, String, Comment, Expr, S_Expr, Q_Expr, Lispy); - mpc_result_t result; - int success = 1; - - if (!mpc_parse("", input,Lispy, &result)) { - success = 0; - mpc_err_print(result.error); - mpc_err_delete(result.error); - } - - mpc_cleanup(6, Number, Symbol, S_Expr, Expr, Lispy, Q_Expr); - - return success ? (mpc_ast_t*)result.output : NULL; + gLispy = Lispy; + gParsers = calloc(20, sizeof(mpc_parser_t*)); + int i = 0; + gParsers[i++] = Number; + 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) { @@ -47,11 +72,14 @@ lval* parse(mpc_ast_t *t) { 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")) { result = lval_s_expr(); } - if (result == NULL && strstr(t->tag, "q_expr")) { result = lval_q_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; } @@ -59,10 +87,36 @@ lval* parse(mpc_ast_t *t) { 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) { @@ -107,15 +161,4 @@ lval* eval_s_expr(lenv* env, lval* val) { lval_delete(func); return result; -} -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; } \ No newline at end of file diff --git a/lang.h b/lang.h index f0e9691..eb82902 100644 --- a/lang.h +++ b/lang.h @@ -16,6 +16,7 @@ extern "C" { #include "lval.h" #include "lenv.h" #include "functions.h" +#include "shared.h" #define LASSERT(val, cond, errnum, errdetail, ...) \ if (!(cond)) { \ @@ -34,14 +35,13 @@ extern "C" { 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* eval_builtin_op(lval* val, char* op); - lval* eval_s_expr(lenv* env, lval* val); + lval* parse_read_string(mpc_ast_t* t); lval* eval(lenv* env, lval* val); - + lval* eval_s_expr(lenv* env, lval* val); #ifdef __cplusplus } diff --git a/lisp/test.lisp b/lisp/test.lisp new file mode 100644 index 0000000..eb140fc --- /dev/null +++ b/lisp/test.lisp @@ -0,0 +1 @@ +(def {hello} "Hello World!") \ No newline at end of file diff --git a/lval.c b/lval.c index 872e72d..4a6d485 100644 --- a/lval.c +++ b/lval.c @@ -53,8 +53,14 @@ lval* lval_lambda(lval* formals, lval* body) { val->data.func->body = body; return val; } -lval* lval_exit() { +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; } @@ -146,6 +152,7 @@ BOOL lval_equal(lval* a, lval* b) { case LVAL_EXIT: return TRUE; 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_FUNC: if (a->data.func->builtin != NULL) { if (b->data.func->builtin != NULL) { @@ -192,6 +199,7 @@ void lval_delete(lval* val) { 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); @@ -232,6 +240,7 @@ lval* lval_copy(lval* current) { case LVAL_EXIT: 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); @@ -283,12 +292,14 @@ lval* lval_err_detail(VAL_ERROR err, char* format, ...){ char* lval_str_name(VAL_TYPE type) { switch(type) { - case LVAL_ERR: return "Error"; case LVAL_FUNC: return "Function"; case LVAL_NUM: return "Numeric"; - case LVAL_Q_EXPR: return "Q-Expression"; + 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"; default: return "UNKNOWN"; } } \ No newline at end of file diff --git a/lval.h b/lval.h index 09e8ef3..134d7f0 100644 --- a/lval.h +++ b/lval.h @@ -25,7 +25,7 @@ typedef struct lval_func lval_func; #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) -enum VAL_TYPE { LVAL_ERR, LVAL_NUM, LVAL_SYM, LVAL_FUNC, LVAL_S_EXPR, LVAL_Q_EXPR, LVAL_EXIT }; +enum VAL_TYPE { LVAL_ERR, LVAL_NUM, LVAL_SYM, LVAL_FUNC, LVAL_S_EXPR, LVAL_Q_EXPR, LVAL_EXIT, LVAL_STR }; enum VAL_ERROR { LERR_DIV_ZERO, LERR_BAD_OP, LERR_BAD_NUM, LERR_BAD_SYM, LERR_OTHER, LERR_SYNTAX }; typedef enum VAL_TYPE VAL_TYPE; typedef enum VAL_ERROR VAL_ERROR; @@ -46,11 +46,13 @@ struct lval { union { double_t num; char* sym; + char* str; + short exitcode; + struct lval_func* func; struct { enum VAL_ERROR num; char* detail; } err; - struct lval_func* func; } data; int cell_count; @@ -65,7 +67,8 @@ 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(); +lval* lval_exit(short exitcode); +lval* lval_str(char* str); lval* lval_add(lval* val, lval* x); lval* lval_pop(lval* val, int i); diff --git a/main.c b/main.c index 3f1cd64..8ce9f3d 100644 --- a/main.c +++ b/main.c @@ -14,6 +14,7 @@ #include "mpc.h" #include "lang.h" #include "main.h" +#include "util.h" /* * @@ -25,6 +26,25 @@ int main(int argc, char** argv) { //Init environment lenv* env = lenv_new(); lenv_add_builtin_funcs(env); + setup_parsers(); + + //Attempt to import/run files specified on the command line + if (argc > 1) { + for(int i = 1; i < argc; i++) { + printf("Loading File \"%s\"\n", argv[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("> "); @@ -48,9 +68,10 @@ int main(int argc, char** argv) { //Evaluate result = eval(env, result); - int exit = 0; + BOOL exit = FALSE; if (result != NULL && result->type == LVAL_EXIT) { - exit = 1; + exit = TRUE; + exitcode = result->data.exitcode; } else { //print the result lval_println(result); @@ -60,16 +81,7 @@ int main(int argc, char** argv) { lval_delete(result); mpc_ast_delete(ast_result); - if (exit == 1) { - printf("Program Terminated: "); - - lval* sym = lval_sym("exitcode"); - lval* exitcode = lenv_get(env, sym); - lval_println(exitcode); - lval_delete(exitcode); - lval_delete(sym); - - fflush(stdout); + if (exit == TRUE) {; break; } } @@ -77,8 +89,9 @@ int main(int argc, char** argv) { } lenv_delete(env); + cleanup_parsers(); - return (EXIT_SUCCESS); + return (exitcode); } void lval_expr_print(lval* val, char* open, char* close) { @@ -99,6 +112,7 @@ 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; @@ -131,7 +145,15 @@ void lval_print(lval* val) { 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 diff --git a/main.h b/main.h index ffa64d8..98e4820 100644 --- a/main.h +++ b/main.h @@ -24,6 +24,7 @@ extern "C" { 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); #ifdef __cplusplus } diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk index a950798..119f6f1 100644 --- a/nbproject/Makefile-Debug.mk +++ b/nbproject/Makefile-Debug.mk @@ -41,6 +41,7 @@ OBJECTFILES= \ ${OBJECTDIR}/lib/mpc/mpc.o \ ${OBJECTDIR}/lval.o \ ${OBJECTDIR}/main.o \ + ${OBJECTDIR}/shared.o \ ${OBJECTDIR}/util.o @@ -98,6 +99,11 @@ ${OBJECTDIR}/main.o: main.c ${RM} "$@.d" $(COMPILE.c) -g -Wall -Ilib/mpc `pkg-config --cflags libedit` -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/main.o main.c +${OBJECTDIR}/shared.o: shared.c + ${MKDIR} -p ${OBJECTDIR} + ${RM} "$@.d" + $(COMPILE.c) -g -Wall -Ilib/mpc `pkg-config --cflags libedit` -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/shared.o shared.c + ${OBJECTDIR}/util.o: util.c ${MKDIR} -p ${OBJECTDIR} ${RM} "$@.d" diff --git a/nbproject/Makefile-Release.mk b/nbproject/Makefile-Release.mk index fb11923..646d182 100644 --- a/nbproject/Makefile-Release.mk +++ b/nbproject/Makefile-Release.mk @@ -41,6 +41,7 @@ OBJECTFILES= \ ${OBJECTDIR}/lib/mpc/mpc.o \ ${OBJECTDIR}/lval.o \ ${OBJECTDIR}/main.o \ + ${OBJECTDIR}/shared.o \ ${OBJECTDIR}/util.o @@ -98,6 +99,11 @@ ${OBJECTDIR}/main.o: main.c ${RM} "$@.d" $(COMPILE.c) -O3 -w -Ilib/mpc `pkg-config --cflags libedit` -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/main.o main.c +${OBJECTDIR}/shared.o: shared.c + ${MKDIR} -p ${OBJECTDIR} + ${RM} "$@.d" + $(COMPILE.c) -O3 -w -Ilib/mpc `pkg-config --cflags libedit` -std=c99 -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/shared.o shared.c + ${OBJECTDIR}/util.o: util.c ${MKDIR} -p ${OBJECTDIR} ${RM} "$@.d" diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index 2b91b0e..c777f6d 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -10,6 +10,7 @@ lval.h main.h lib/mpc/mpc.h + shared.h util.h lval.c main.c lib/mpc/mpc.c + shared.c util.c + + + + @@ -151,6 +157,10 @@ + + + + diff --git a/shared.c b/shared.c new file mode 100644 index 0000000..3c9208b --- /dev/null +++ b/shared.c @@ -0,0 +1,8 @@ +#include +#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 diff --git a/shared.h b/shared.h new file mode 100644 index 0000000..0a1099b --- /dev/null +++ b/shared.h @@ -0,0 +1,26 @@ +/* + * File: shared.h + * Author: sam + * + * Created on 31 May 2014, 15:50 + */ + +#ifndef SHARED_H +#define SHARED_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mpc.h" + + extern mpc_parser_t* gLispy; + extern size_t gParserCount; + extern mpc_parser_t** gParsers; + +#ifdef __cplusplus +} +#endif + +#endif /* SHARED_H */ +