klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

commit 0d5eff2c98f48e483cc1326c7f47674e520f20fe
parent 12d6b247b8b03719fec82111802030ce2c8552d5
Author: Oto Havle <havleoto@gmail.com>
Date:   Thu, 20 Oct 2011 14:58:26 +0200

Added noninteractive script execution. Script name can be specified as a command line argument. Additional command line arguments and exit codes are handled according to SRFI-22.

Diffstat:
Msrc/Makefile | 9++++++---
Msrc/kground.c | 5+++++
Msrc/klisp.c | 24+++++++++++++++---------
Asrc/kscript.c | 244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/kscript.h | 24++++++++++++++++++++++++
Msrc/kstate.c | 4++++
Msrc/kstate.h | 3+++
7 files changed, 301 insertions(+), 12 deletions(-)

diff --git a/src/Makefile b/src/Makefile @@ -27,7 +27,7 @@ PLATS= generic mingw posix KRN_A= libklisp.a CORE_O= kobject.o ktoken.o kpair.o kstring.o ksymbol.o kread.o \ kwrite.o kstate.o kmem.o kerror.o kauxlib.o kenvironment.o \ - kcontinuation.o koperative.o kapplicative.o keval.o krepl.o \ + kcontinuation.o koperative.o kapplicative.o keval.o krepl.o kscript.o \ kencapsulation.o kpromise.o kport.o kinteger.o krational.o \ kreal.o ktable.o kgc.o imath.o imrat.o kblob.o \ kground.o kghelpers.o kgbooleans.o kgeqp.o kgequalp.o \ @@ -199,7 +199,7 @@ kground.o: kground.c kstate.h klimits.h klisp.h kobject.h klispconf.h \ kgequalp.h kgsymbols.h kgcontrol.h kgpairs_lists.h kgpair_mut.h \ kgenvironments.h kgenv_mut.h kgcombiners.h kgcontinuations.h \ kgencapsulations.h kgpromises.h kgkd_vars.h kgks_vars.h kgnumbers.h \ - kgstrings.h kgchars.h kgports.h kgblobs.h ktable.h keval.h krepl.h kgsystem.h + kgstrings.h kgchars.h kgports.h kgblobs.h ktable.h keval.h krepl.h kscript.h kgsystem.h kgstrings.o: kgstrings.c kstate.h klimits.h klisp.h kobject.h klispconf.h \ ktoken.h kmem.h kapplicative.h koperative.h kcontinuation.h kerror.h \ ksymbol.h kstring.h kghelpers.h kpair.h kgc.h kenvironment.h kgchars.h \ @@ -237,9 +237,12 @@ kreal.o: kreal.c kreal.h kobject.h klimits.h klisp.h klispconf.h kstate.h \ krepl.o: krepl.c klisp.h kobject.h klimits.h klispconf.h kstate.h \ ktoken.h kmem.h kcontinuation.h kenvironment.h kerror.h kread.h kwrite.h \ kstring.h krepl.h ksymbol.h kport.h kpair.h kgc.h ktable.h +kscript.o: kscript.c klisp.h kobject.h klimits.h klispconf.h kstate.h \ + ktoken.h kmem.h kcontinuation.h kenvironment.h kerror.h kread.h kwrite.h \ + kstring.h krepl.h kscript.h ksymbol.h kport.h kpair.h kgc.h ktable.h kgcontrol.h kstate.o: kstate.c klisp.h kobject.h klimits.h klispconf.h kstate.h \ ktoken.h kmem.h kstring.h kpair.h kgc.h keval.h koperative.h \ - kapplicative.h kcontinuation.h kenvironment.h kground.h krepl.h \ + kapplicative.h kcontinuation.h kenvironment.h kground.h krepl.h kscript.h \ ksymbol.h kport.h ktable.h kblob.h kgpairs_lists.h kghelpers.h kerror.h kstring.o: kstring.c kstring.h kobject.h klimits.h klisp.h klispconf.h \ kstate.h ktoken.h kmem.h kgc.h diff --git a/src/kground.c b/src/kground.c @@ -43,6 +43,7 @@ #include "kstring.h" #include "keval.h" #include "krepl.h" +#include "kscript.h" /* for init_cont_names */ #define add_cont_name(K_, t_, c_, n_) \ @@ -67,6 +68,10 @@ void kinit_cont_names(klisp_State *K) add_cont_name(K, t, do_repl_loop, "repl-loop"); add_cont_name(K, t, do_repl_error, "repl-report-error"); + /* SCRIPT, root-continuation & error-continuation */ + add_cont_name(K, t, do_script_exit, "script-exit"); + add_cont_name(K, t, do_script_error, "script-report-error"); + /* GROUND ENV */ add_cont_name(K, t, do_eval_ls, "eval-list"); add_cont_name(K, t, do_combine, "eval-combine"); diff --git a/src/klisp.c b/src/klisp.c @@ -15,16 +15,22 @@ #include "klisp.h" #include "kstate.h" #include "kauxlib.h" +#include "kscript.h" int main(int argc, char *argv[]) { - printf("REPL Test\n"); - - klisp_State *K = klispL_newstate(); - klispS_init_repl(K); - klispS_run(K); - klisp_close(K); - - printf("Done!\n"); - return 0; + if (argc <= 1) { + klisp_State *K = klispL_newstate(); + klispS_init_repl(K); + klispS_run(K); + klisp_close(K); + return 0; + } else { + klisp_State *K = klispL_newstate(); + kinit_script(K, argc - 1, argv + 1); + klispS_run(K); + int exit_code = K->script_exit_code; + klisp_close(K); + return exit_code; + } } diff --git a/src/kscript.c b/src/kscript.c @@ -0,0 +1,244 @@ +/* +** kscript.c +** klisp noninteractive script execution +** See Copyright Notice in klisp.h +*/ +#include <stdio.h> +#include <setjmp.h> + +#include "klisp.h" +#include "kstate.h" +#include "kobject.h" +#include "kcontinuation.h" +#include "kenvironment.h" +#include "kerror.h" +#include "kread.h" +#include "kwrite.h" +#include "kstring.h" +#include "krepl.h" +#include "kscript.h" +#include "ksymbol.h" +#include "kport.h" +#include "kpair.h" +#include "kgcontrol.h" +/* for names */ +#include "ktable.h" + +/* Push (v) in GC roots and return (v). */ +static inline TValue krooted_tvs_pass(klisp_State *K, TValue v) +{ + krooted_tvs_push(K, v); + return v; +} + +#if KTRACK_SI +static inline TValue krooted_tvs_pass_si(klisp_State *K, TValue v, TValue si) +{ + krooted_tvs_push(K, v); + kset_source_info(K, v, si); + return v; +} +#endif + +/* the exit continuation, it exits the loop */ +void do_script_exit(klisp_State *K, TValue *xparams, TValue obj) +{ + UNUSED(xparams); + + /* save exit code */ + + switch(ttype(obj)) { + case K_TINERT: + K->script_exit_code = 0; + break; + case K_TFIXINT: + K->script_exit_code = (int) ivalue(obj); + break; + default: + K->script_exit_code = KSCRIPT_DEFAULT_ERROR_EXIT_CODE; + /* TODO: print error message here ? */ + break; + } + + /* force the loop to terminate */ + K->next_func = NULL; + return; +} + + +/* the underlying function of the error cont */ +void do_script_error(klisp_State *K, TValue *xparams, TValue obj) +{ + /* + ** xparams[0]: dynamic environment + */ + + /* FOR NOW used only for irritant list */ + TValue port = kcdr(K->kd_error_port_key); + klisp_assert(kport_file(port) == stderr); + + /* TEMP: obj should be an error obj */ + if (ttiserror(obj)) { + Error *err_obj = tv2error(obj); + TValue who = err_obj->who; + char *who_str; + /* TEMP? */ + if (ttiscontinuation(who)) + who = tv2cont(who)->comb; + + if (ttisstring(who)) { + who_str = kstring_buf(who); +#if KTRACK_NAMES + } else if (khas_name(who)) { + TValue name = kget_name(K, who); + who_str = ksymbol_buf(name); +#endif + } else { + who_str = "?"; + } + char *msg = kstring_buf(err_obj->msg); + fprintf(stderr, "\n*ERROR*: \n"); + fprintf(stderr, "%s: %s", who_str, msg); + + krooted_tvs_push(K, obj); + + /* Msg + irritants */ + /* TODO move to a new function */ + if (!ttisnil(err_obj->irritants)) { + fprintf(stderr, ": "); + kwrite_display_to_port(K, port, err_obj->irritants, false); + } + kwrite_newline_to_port(K, port); + +#if KTRACK_NAMES +#if KTRACK_SI + /* Location */ + /* TODO move to a new function */ + /* MAYBE: remove */ + if (khas_name(who) || khas_si(who)) { + fprintf(stderr, "Location: "); + kwrite_display_to_port(K, port, who, false); + kwrite_newline_to_port(K, port); + } + + /* Backtrace */ + /* TODO move to a new function */ + TValue tv_cont = err_obj->cont; + fprintf(stderr, "Backtrace: \n"); + while(ttiscontinuation(tv_cont)) { + kwrite_display_to_port(K, port, tv_cont, false); + kwrite_newline_to_port(K, port); + Continuation *cont = tv2cont(tv_cont); + tv_cont = cont->parent; + } + /* add extra newline at the end */ + kwrite_newline_to_port(K, port); +#endif +#endif + krooted_tvs_pop(K); + } else { + fprintf(stderr, "\n*ERROR*: not an error object passed to " + "error continuation"); + } + + /* Save the exit code to be returned from interpreter + main(). Terminate the interpreter loop. */ + + K->script_exit_code = KSCRIPT_DEFAULT_ERROR_EXIT_CODE; + K->next_func = NULL; +} + +/* convert C style argc-argv pair to list of strings */ +static TValue argv2value(klisp_State *K, int argc, char *argv[]) +{ + TValue dummy = kcons_g(K, false, KINERT, KNIL); + krooted_tvs_push(K, dummy); + TValue tail = dummy; + for (int i = 0; i < argc; i++) { + TValue next_car = kstring_new_b_imm(K, argv[i]); + krooted_tvs_push(K, next_car); + TValue np = kcons_g(K, false, next_car, KNIL); + krooted_tvs_pop(K); + kset_cdr_unsafe(K, tail, np); + tail = np; + } + krooted_tvs_pop(K); + return kcdr(dummy); +} + +/* loader_body(K, ARGV, DENV) returns the value + * + * ((load (car ARGV)) + * ($if ($binds? DENV main) (main ARGV) #inert) + * + */ +static TValue loader_body(klisp_State *K, TValue argv, TValue denv) +{ + int32_t rooted_tvs_mark = K->rooted_tvs_top; +# define S(z) (krooted_tvs_pass(K, ksymbol_new(K, (z), KNIL))) +# define C(car, cdr) (krooted_tvs_pass(K, kcons_g(K, false, (car), (cdr)))) +# define L(n, ...) (krooted_tvs_pass(K, klist_g(K, false, (n), __VA_ARGS__))) + TValue main_sym = S("main"); + TValue script_name = krooted_tvs_pass(K, kcar(argv)); + TValue body = + L(2, L(2, S("load"), script_name), + L(4, S("$if"), L(3, S("$binds?"), denv, main_sym), + L(2, main_sym, C(S("list"), argv)), + KINERT)); +# undef S +# undef L + K->rooted_tvs_top = rooted_tvs_mark; + return body; +} + +/* call this to init the noninteractive mode */ + +void kinit_script(klisp_State *K, int argc, char *argv[]) +{ +# define R(z) (krooted_tvs_pass(K, (z))) +# define G(z, sym) \ + do { TValue symbol = ksymbol_new(K, (sym), KNIL); \ + krooted_tvs_push(K, symbol); \ + kadd_binding(K, K->ground_env, symbol, (z)); \ + krooted_tvs_pop(K); \ + } while (0) + +#if KTRACK_SI + TValue str = R(kstring_new_b_imm(K, __FILE__)); + TValue tail = R(kcons(K, i2tv(__LINE__), i2tv(0))); + TValue si = kcons(K, str, tail); + krooted_tvs_pop(K); + krooted_tvs_pop(K); + krooted_tvs_push(K, si); +# define RSI(z) (krooted_tvs_pass_si(K, (z), si)) +#else +# define RSI(z) R(z) +#endif + + TValue std_env = RSI(kmake_environment(K, K->ground_env)); + TValue root_cont = RSI(kmake_continuation(K, KNIL, do_script_exit, 0)); + TValue error_cont = RSI(kmake_continuation(K, root_cont, do_script_error, 1, std_env)); + G(root_cont, "root-continuation"); + G(error_cont, "error-continuation"); + K->root_cont = root_cont; + K->error_cont = error_cont; + krooted_tvs_pop(K); + krooted_tvs_pop(K); + + TValue argv_value = RSI(argv2value(K, argc, argv)); + TValue loader = RSI(loader_body(K, argv_value, std_env)); + TValue loader_cont = RSI(kmake_continuation(K, root_cont, do_seq, 2, loader, std_env)); + kset_cc(K, loader_cont); + krooted_tvs_pop(K); + krooted_tvs_pop(K); + krooted_tvs_pop(K); + krooted_tvs_pop(K); +#if KTRACK_SI + krooted_tvs_pop(K); +#endif + kapply_cc(K, KINERT); + +#undef R +#undef RSI +#undef G +} diff --git a/src/kscript.h b/src/kscript.h @@ -0,0 +1,24 @@ +/* +** krepl.h +** klisp noninteractive script execution +** See Copyright Notice in klisp.h +*/ + +#ifndef kscript_h +#define kscript_h + +#include "klisp.h" +#include "kstate.h" +#include "kobject.h" + +void kinit_script(klisp_State *K, int argc, char *argv[]); + +/* continuation functions */ +void do_script_exit(klisp_State *K, TValue *xparams, TValue obj); +void do_script_error(klisp_State *K, TValue *xparams, TValue obj); + +/* default exit code in case of error according to SRFI-22 */ + +#define KSCRIPT_DEFAULT_ERROR_EXIT_CODE 70 + +#endif diff --git a/src/kstate.c b/src/kstate.c @@ -30,6 +30,7 @@ #include "kenvironment.h" #include "kground.h" #include "krepl.h" +#include "kscript.h" #include "ksymbol.h" #include "kstring.h" #include "kport.h" @@ -187,6 +188,9 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { /* initialize writer */ K->write_displayp = false; /* set on each call to write */ + /* initialize script */ + K->script_exit_code = KSCRIPT_DEFAULT_ERROR_EXIT_CODE; + /* initialize temp stack */ K->ssize = KS_ISSIZE; K->stop = 0; /* stack is empty */ diff --git a/src/kstate.h b/src/kstate.h @@ -139,6 +139,9 @@ struct klisp_State { /* writer */ bool write_displayp; + /* script */ + int script_exit_code; + /* auxiliary stack */ int32_t ssize; /* total size of array */ int32_t stop; /* top of the stack (all elements are below this index) */