klisp

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

commit 07844d71887f047c789d68e131e4c8bff8561f64
parent 657ec61d59d3e342024e7fa4ebcef0bb52bad7f0
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Mon,  5 Dec 2011 21:09:19 -0300

Added file searching in require.

Diffstat:
Msrc/kgc.c | 3+++
Msrc/kgports.c | 112++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/kgsystem.c | 1-
Msrc/klimits.h | 5+++++
Msrc/klispconf.h | 87++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/kstate.c | 18+++++++++++++++++-
Msrc/kstate.h | 4++++
7 files changed, 217 insertions(+), 13 deletions(-)

diff --git a/src/kgc.c b/src/kgc.c @@ -627,6 +627,9 @@ static void markroot (klisp_State *K) { markvalue(K, K->curr_port); + markvalue(K, K->require_path); + markvalue(K, K->require_table); + /* Mark all objects in the auxiliary stack, (all valid indexes are below top) and all the objects in the two protected areas */ diff --git a/src/kgports.c b/src/kgports.c @@ -4,11 +4,11 @@ ** See Copyright Notice in klisp.h */ -#include <assert.h> #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <stdint.h> +#include <string.h> #include "kstate.h" #include "kobject.h" @@ -770,8 +770,98 @@ void load(klisp_State *K) } } -/* ?.? require, it's like load except in a standard environment */ -/* TODO check to see if the files was required before! */ +/* Helpers for require */ +static bool readable(const char *filename) { + FILE *f = fopen(filename, "r"); /* try to open file */ + if (f == NULL) return false; /* open failed */ + fclose(f); + return true; +} + +/* Path can't/shouldn't contain embedded zeros */ +static const char *get_next_template(klisp_State *K, const char *path, + TValue *next) { + const char *l; + while (*path == *KLISP_PATHSEP) path++; /* skip separators */ + if (*path == '\0') return NULL; /* no more templates */ + l = strchr(path, *KLISP_PATHSEP); /* find next separator */ + if (l == NULL) l = path + strlen(path); + *next = kstring_new_bs(K, path, l-path); /* template */ + return l; /* pointer to the end of the template */ +} + +/* no strings should contains embedded zeroes */ +static TValue str_sub(klisp_State *K, TValue s, TValue p, TValue r) +{ + const char *sp = kstring_buf(s); + const char *pp = kstring_buf(p); + const char *rp = kstring_buf(r); + + uint32_t size = kstring_size(s); + uint32_t psize = kstring_size(p); + uint32_t rsize = kstring_size(r); + int32_t diff_size = rsize - psize; + + const char *wild; + + /* first calculate needed size */ + while ((wild = strstr(sp, pp)) != NULL) { + size += diff_size; + sp = wild + psize; + } + + /* now construct result buffer and fill it */ + TValue res = kstring_new_s(K, size); + char *resp = kstring_buf(res); + sp = kstring_buf(s); + while ((wild = strstr(sp, pp)) != NULL) { + ptrdiff_t l = wild - sp; + memcpy(resp, sp, l); + resp += l; + memcpy(resp, rp, rsize); + resp += rsize; + sp = wild + psize; + } + strcpy(resp, sp); /* the size was calculated beforehand */ + return res; +} + +static TValue find_file (klisp_State *K, TValue name, TValue pname) { + /* not used in klisp */ + /* name = luaL_gsub(L, name, ".", LUA_DIRSEP); */ + /* lua_getfield(L, LUA_ENVIRONINDEX, pname); */ + klisp_assert(ttisstring(name) && !kstring_emptyp(name)); + const char *path = kstring_buf(pname); + TValue next = K->empty_string; + krooted_vars_push(K, &next); + TValue wild = kstring_new_b(K, KLISP_PATH_MARK); + krooted_tvs_push(K, wild); + + while ((path = get_next_template(K, path, &next)) != NULL) { + next = str_sub(K, next, wild, name); + if (readable(kstring_buf(next))) { /* does file exist and is readable? */ + krooted_tvs_pop(K); + krooted_vars_pop(K); + return next; /* return that file name */ + } + } + + krooted_tvs_pop(K); + krooted_vars_pop(K); + return K->empty_string; /* return empty_string */ +} + +/* ?.? require */ +/* +** require is like load except that: +** - require first checks to see if the file was already required +** and if so, doesnt' do anything +** - require looks for the named file in a number of locations +** configurable via env var KLISP_PATH +** - When/if the file is found, evaluation happens in an initially +** standard environment +*/ +/* TODO check if file was required */ void require(klisp_State *K) { TValue *xparams = K->next_xparams; @@ -780,13 +870,27 @@ void require(klisp_State *K) klisp_assert(ttisenvironment(K->next_env)); UNUSED(denv); UNUSED(xparams); - bind_1tp(K, ptree, "string", ttisstring, filename); + bind_1tp(K, ptree, "string", ttisstring, name); + + if (kstring_emptyp(name)) { + klispE_throw_simple(K, "Empty name"); + return; + } + TValue filename = K->empty_string; + krooted_vars_push(K, &filename); + filename = find_file(K, name, K->require_path); + + if (kstring_emptyp(filename)) { + klispE_throw_simple_with_irritants(K, "Not found", 1, name); + return; + } /* the reads must be guarded to close the file if there is some error this continuation also will return inert after the evaluation of the last expression is done */ TValue port = kmake_fport(K, filename, false, false); krooted_tvs_push(K, port); + krooted_vars_pop(K); /* filename already rooted */ TValue inert_cont = kmake_continuation(K, kget_cc(K), do_return_value, 1, KINERT); diff --git a/src/kgsystem.c b/src/kgsystem.c @@ -4,7 +4,6 @@ ** See Copyright Notice in klisp.h */ -#include <assert.h> #include <string.h> #include <stdlib.h> #include <stdbool.h> diff --git a/src/klimits.h b/src/klimits.h @@ -63,6 +63,11 @@ #define MINCONTNAMETABSIZE 32 #endif +/* minimum size for the require table (must be power of 2) */ +#ifndef MINREQUIRETABSIZE +#define MINREQUIRETABSIZE 32 +#endif + /* starting size for ground environment hashtable */ /* at last count, there were about 200 bindings in ground env */ #define ENVTABSIZE 512 diff --git a/src/klispconf.h b/src/klispconf.h @@ -25,6 +25,7 @@ ** non-ansi feature or library. */ #if defined(__STRICT_ANSI__) +/* XXX currently unused */ #define KLISP_ANSI #endif @@ -36,11 +37,13 @@ #if defined(KLISP_USE_LINUX) #define KLISP_USE_POSIX #define KLISP_USE_DLOPEN /* needs an extra library: -ldl */ +/* XXX currently unused */ #define KLISP_USE_READLINE /* needs some extra libraries */ #endif #if defined(KLISP_USE_MACOSX) #define KLISP_USE_POSIX +/* XXX currently unused */ #define KLISP_DL_DYLD /* does not need extra library */ #endif @@ -57,7 +60,6 @@ */ #define KLISP_QL(x) "'" x "'" #define KLISP_QS KLISP_QL("%s") -/* /TODO */ /* @@ KLISP_USE_POSIX includes all functionallity listed as X/Open System @@ -72,16 +74,87 @@ #endif /* -@@ LUA_PATH and LUA_CPATH are the names of the environment variables that -@* Lua check to set its paths. -@@ KLISP_INIT is the name of the environment variable that klisp +@@ KLISP_PATH and KLISP_CPATH are the names of the environment variables that +@* Klisp check to set its paths. +@@ KLISP_INIT is the name of the environment variable that Klisp @* checks for initialization code. ** CHANGE them if you want different names. */ -//#define LUA_PATH "LUA_PATH" -//#define LUA_CPATH "LUA_CPATH" +#define KLISP_PATH "KLISP_PATH" +#define KLISP_CPATH "KLISP_CPATH" #define KLISP_INIT "KLISP_INIT" + +/* +@@ KLISP_PATH_DEFAULT is the default path that Klisp uses to look for +@* Klisp libraries. +@@ KLISP_CPATH_DEFAULT is the default path that Klisp uses to look for +@* C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ +#if defined(_WIN32) +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define KLISP_LDIR "!\\klisp\\" +#define KLISP_CDIR "!\\" +#define KLISP_PATH_DEFAULT \ + ".\\?.k;" ".\\?" \ + KLISP_LDIR"?.k;" KLISP_LDIR"?;" \ + KLISP_CDIR"?.k;" KLISP_CDIR"?;" +/* XXX Not used for now */ +#define KLISP_CPATH_DEFAULT \ + ".\\?.dll;" KLISP_CDIR"?.dll;" KLISP_CDIR"loadall.dll" + +#else +#define KLISP_ROOT "/usr/local/" +#define KLISP_LDIR KLISP_ROOT "share/klisp/0.3/" +#define KLISP_CDIR KLISP_ROOT "lib/klisp/0.3/" +#define KLISP_PATH_DEFAULT \ + "./?.k;./?;" \ + KLISP_LDIR"?.k;" KLISP_LDIR"?;" \ + KLISP_CDIR"?;" KLISP_CDIR"?.k" +/* XXX Not used for now */ +#define KLISP_CPATH_DEFAULT \ + "./?.so;" KLISP_CDIR"?.so;" KLISP_CDIR"loadall.so" +#endif + + +/* +@@ KLISP_DIRSEP is the directory separator (for submodules). +** XXX KLISP_DIRSEP is not currently used +** This allows naturally looking paths in windows while still using +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Klisp automatically uses "\".) +*/ +#if defined(_WIN32) +#define KLISP_DIRSEP "\\" +#else +#define KLISP_DIRSEP "/" +#endif + + +/* +@@ KLISP_PATHSEP is the character that separates templates in a path. +@@ KLISP_PATH_MARK is the string that marks the substitution points in a +@* template. +@@ KLISP_EXECDIR in a Windows path is replaced by the executable's +@* directory. +@@ XXX KLISP_IGMARK is not currently used in klisp. +@@ KLISP_IGMARK is a mark to ignore all before it when bulding the +@* klispopen_ function name. +** CHANGE them if for some reason your system cannot use those +** characters. (E.g., if one of those characters is a common character +** in file/directory names.) Probably you do not need to change them. +*/ +#define KLISP_PATHSEP ";" +#define KLISP_PATH_MARK "?" +#define KLISP_EXECDIR "!" +#define KLISP_IGMARK "-" + /* @@ klisp_stdin_is_tty detects whether the standard input is a 'tty' (that @* is, whether we're running klisp interactively). @@ -117,7 +190,7 @@ #define KTRACK_MARKS true */ -/* TODO use this defines */ +/* TODO use this defines everywhere */ #define KTRACK_NAMES true #define KTRACK_SI true diff --git a/src/kstate.c b/src/kstate.c @@ -13,14 +13,15 @@ ** problem. ASK John. */ +#include <stdlib.h> #include <stddef.h> #include <setjmp.h> +#include <string.h> #include "klisp.h" #include "klimits.h" #include "kstate.h" #include "kobject.h" -#include "kstring.h" #include "kpair.h" #include "kmem.h" #include "keval.h" @@ -191,6 +192,21 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { /* initialize writer */ K->write_displayp = false; /* set on each call to write */ + /* initialize require facilities */ + { + char *str = getenv(KLISP_PATH); + if (str == NULL) + str = KLISP_PATH_DEFAULT; + + K->require_path = kstring_new_b_imm(K, str); + /* replace dirsep with forward slashes, + windows will happily accept forward slashes */ + str = kstring_buf(K->require_path); + while ((str = strchr(str, *KLISP_DIRSEP)) != NULL) + *str++ = '/'; + } + K->require_table = klispH_new(K, 0, MINREQUIRETABSIZE, 0); + /* initialize temp stack */ K->ssize = KS_ISSIZE; K->stop = 0; /* stack is empty */ diff --git a/src/kstate.h b/src/kstate.h @@ -155,6 +155,10 @@ struct klisp_State { /* writer */ bool write_displayp; + /* require */ + TValue require_path; + TValue require_table; + /* auxiliary stack (XXX this could be a vector) */ int32_t ssize; /* total size of array */ int32_t stop; /* top of the stack (all elements are below this index) */