7443 lines
176 KiB
C
7443 lines
176 KiB
C
/* newlisp.c --- enrty point and main functions for newLISP
|
|
|
|
|
|
Copyright (C) 2016 Lutz Mueller
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "newlisp.h"
|
|
#include "protos.h"
|
|
#include "primes.h"
|
|
|
|
#ifdef WINDOWS
|
|
#include <winsock2.h>
|
|
#else
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#ifdef READLINE
|
|
#include <readline/readline.h>
|
|
/* take following line out on Slackware Linux */
|
|
#include <readline/history.h>
|
|
#endif /* end READLINE */
|
|
|
|
#ifdef SUPPORT_UTF8
|
|
#include <wctype.h>
|
|
#endif
|
|
|
|
#define freeMemory free
|
|
|
|
#define INIT_FILE "init.lsp"
|
|
|
|
#ifdef WINDOWS
|
|
#define fprintf win_fprintf
|
|
#define fgets win_fgets
|
|
#define fclose win_fclose
|
|
#endif
|
|
|
|
#ifdef LIBRARY
|
|
extern STREAM libStrStream;
|
|
int newlispLibConsoleFlag = 0;
|
|
#endif
|
|
|
|
#ifdef LINUX
|
|
#ifdef ANDROID
|
|
int opsys = 11;
|
|
#else
|
|
int opsys = 1;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef _BSD
|
|
int opsys = 2;
|
|
#endif
|
|
|
|
#ifdef MAC_OSX
|
|
#ifdef EMSCRIPTEN
|
|
int opsys = 11;
|
|
#else
|
|
int opsys = 3;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef SOLARIS
|
|
int opsys = 4;
|
|
#endif
|
|
|
|
#ifdef WINDOWS
|
|
int opsys = 6;
|
|
#endif
|
|
|
|
#ifdef OS2
|
|
int opsys = 7;
|
|
#endif
|
|
|
|
#ifdef CYGWIN
|
|
int opsys = 8;
|
|
#endif
|
|
|
|
#ifdef TRU64
|
|
int opsys = 9;
|
|
#endif
|
|
|
|
#ifdef AIX
|
|
int opsys = 10;
|
|
#endif
|
|
|
|
|
|
/* opsys = 11 taken for ANDROID; see LINUX */
|
|
|
|
int bigEndian = 1; /* gets set in main() */
|
|
|
|
int version = 10700;
|
|
|
|
char copyright[]=
|
|
"\nnewLISP v.10.7.0 Copyright (c) 2016 Lutz Mueller. All rights reserved.\n\n%s\n\n";
|
|
|
|
#ifndef NEWLISP64
|
|
#ifdef SUPPORT_UTF8
|
|
char banner[]=
|
|
"newLISP v.10.7.0 32-bit on %s IPv4/6 UTF-8%s%s\n\n";
|
|
#else
|
|
char banner[]=
|
|
"newLISP v.10.7.0 32-bit on %s IPv4/6%s%s\n\n";
|
|
#endif
|
|
#else /* NEWLISP64 */
|
|
#ifdef SUPPORT_UTF8
|
|
char banner[]=
|
|
"newLISP v.10.7.0 64-bit on %s IPv4/6 UTF-8%s%s\n\n";
|
|
#else
|
|
char banner[]=
|
|
"newLISP v.10.7.0 64-bit on %s IPv4/6%s%s\n\n";
|
|
#endif
|
|
#endif /* NEWLISP64 */
|
|
|
|
char banner2[]= ", options: newlisp -h";
|
|
|
|
void linkSource(char *, char *, char *);
|
|
char linkOffset[] = "&&&&@@@@";
|
|
char preLoad[] =
|
|
#ifdef EMSCRIPTEN
|
|
"(set (global 'module) (fn ($x) (load (append {/newlisp-js/} $x))))"
|
|
#else
|
|
"(set (global 'module) (fn ($x) (load (append (env {NEWLISPDIR}) {/modules/} $x))))"
|
|
#endif
|
|
"(context 'Tree) (constant 'Tree:Tree) (context MAIN)"
|
|
"(define (Class:Class) (cons (context) (args)))";
|
|
void printHelpText(void);
|
|
#ifdef READLINE
|
|
char ** newlisp_completion (char * text, int start, int end);
|
|
#endif
|
|
/* --------------------- globals -------------------------------------- */
|
|
|
|
/* interactive command line */
|
|
|
|
int isTTY = FALSE;
|
|
int daemonMode = 0;
|
|
|
|
int noPromptMode = 0;
|
|
int forcePromptMode = 0;
|
|
int httpMode = 0;
|
|
int evalSilent = 0;
|
|
|
|
|
|
#ifdef WINDOWS
|
|
int IOchannelIsSocketStream = 0;
|
|
#endif
|
|
FILE * IOchannel;
|
|
char * IOdomain = NULL;
|
|
int IOport = 0;
|
|
int connectionTimeout = 0;
|
|
|
|
int logTraffic = 0;
|
|
#define LOG_LESS 1
|
|
#define LOG_MORE 2
|
|
|
|
/* initialization */
|
|
int MAX_CPU_STACK = 0x800;
|
|
int MAX_ENV_STACK;
|
|
int MAX_RESULT_STACK;
|
|
#define MAX_OBJECT_STACK 64
|
|
#ifndef NEWLISP64
|
|
INT MAX_CELL_COUNT = 0x10000000;
|
|
#else
|
|
INT MAX_CELL_COUNT = 0x800000000000000LL;
|
|
#endif
|
|
INT blockCount = 0;
|
|
|
|
CELL * firstFreeCell = NULL;
|
|
|
|
CELL * nilCell;
|
|
CELL * trueCell;
|
|
CELL * lastCellCopied;
|
|
CELL * countCell;
|
|
SYMBOL * nilSymbol;
|
|
SYMBOL * trueSymbol;
|
|
SYMBOL * starSymbol;
|
|
SYMBOL * plusSymbol;
|
|
SYMBOL * questionSymbol;
|
|
SYMBOL * atSymbol;
|
|
SYMBOL * currentFunc;
|
|
SYMBOL * argsSymbol;
|
|
SYMBOL * mainArgsSymbol;
|
|
SYMBOL * listIdxSymbol;
|
|
SYMBOL * itSymbol;
|
|
SYMBOL * sysxSymbol;
|
|
SYMBOL * countSymbol;
|
|
SYMBOL * beginSymbol;
|
|
SYMBOL * expandSymbol;
|
|
|
|
SYMBOL * sysSymbol[MAX_REGEX_EXP];
|
|
|
|
SYMBOL * currentContext = NULL;
|
|
SYMBOL * mainContext = NULL;
|
|
SYMBOL * errorEvent;
|
|
SYMBOL * timerEvent;
|
|
SYMBOL * promptEvent;
|
|
SYMBOL * commandEvent;
|
|
SYMBOL * transferEvent;
|
|
SYMBOL * readerEvent;
|
|
|
|
SYMBOL * symHandler[32];
|
|
int currentSignal = 0;
|
|
|
|
SYMBOL * symbolCheck = NULL;
|
|
CELL * stringCell = NULL;
|
|
void * stringIndexPtr = NULL;
|
|
|
|
jmp_buf errorJump;
|
|
|
|
char lc_decimal_point;
|
|
|
|
/* error and exception handling */
|
|
|
|
#define EXCEPTION_THROW -1
|
|
int errorReg = 0;
|
|
CELL * throwResult;
|
|
|
|
/* buffers for read-line and error reporting */
|
|
STREAM readLineStream;
|
|
STREAM errorStream;
|
|
|
|
/* compiler */
|
|
|
|
size_t cellCount = 0;
|
|
size_t symbolCount = 0;
|
|
|
|
int parStackCounter = 0;
|
|
|
|
/* expression evaluation */
|
|
|
|
static CELL * (*evalFunc)(CELL *) = NULL;
|
|
|
|
UINT * envStack = NULL;
|
|
UINT * envStackIdx;
|
|
UINT * envStackTop;
|
|
UINT * resultStack = NULL;
|
|
UINT * resultStackIdx;
|
|
UINT * resultStackTop;
|
|
UINT * lambdaStack = NULL;
|
|
UINT * lambdaStackIdx;
|
|
|
|
/* internal dummy to carry FOOP object */
|
|
SYMBOL objSymbol = {SYMBOL_GLOBAL | SYMBOL_BUILTIN,
|
|
0, "container of (self)", 0, NULL, NULL, NULL, NULL};
|
|
CELL * objCell;
|
|
|
|
extern PRIMITIVE primitive[];
|
|
|
|
/* debugger in nl-debug.c */
|
|
extern char debugPreStr[];
|
|
extern char debugPostStr[];
|
|
extern CELL * debugPrintCell;
|
|
|
|
int traceFlag = 0;
|
|
int evalCatchFlag = 0;
|
|
int recursionCount = 0;
|
|
|
|
int prettyPrintPars = 0;
|
|
int prettyPrintCurrent = 0;
|
|
int prettyPrintFlags = 0;
|
|
int prettyPrintLength = 0;
|
|
char * prettyPrintTab = " ";
|
|
char * prettyPrintFloat = "%1.16g";
|
|
#define MAX_PRETTY_PRINT_LENGTH 80
|
|
UINT prettyPrintMaxLength = MAX_PRETTY_PRINT_LENGTH;
|
|
int stringOutputRaw = TRUE;
|
|
|
|
#define pushLambda(A) (*(lambdaStackIdx++) = (UINT)(A))
|
|
|
|
int pushResultFlag = TRUE;
|
|
|
|
char startupDir[PATH_MAX]; /* start up directory, if defined via -w */
|
|
char * tempDir; /* /tmp on unix or geten("TMP") on Windows */
|
|
char logFile[PATH_MAX]; /* logFile, is define with -l, -L */
|
|
|
|
/* nl-filesys.c */
|
|
int pagesize;
|
|
|
|
/* ============================== MAIN ================================ */
|
|
|
|
#ifndef EMSCRIPTEN
|
|
/*
|
|
void setupSignalHandler(int sig, void (* handler)(int))
|
|
{
|
|
static struct sigaction sig_act;
|
|
sig_act.sa_handler = handler;
|
|
sigemptyset(&sig_act.sa_mask);
|
|
sig_act.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
|
if(sigaction(sig, &sig_act, 0) != 0)
|
|
printf("Error setting signal:%d handler\n", sig);
|
|
}
|
|
*/
|
|
void setupSignalHandler(int sig, void (* handler)(int))
|
|
{
|
|
if(signal(sig, handler) == SIG_ERR)
|
|
printf("Error setting signal:%d handler\n", sig);
|
|
}
|
|
|
|
#if defined(SOLARIS) || defined(TRU64) || defined(AIX)
|
|
void sigpipe_handler(int sig)
|
|
{
|
|
setupSignalHandler(SIGPIPE, sigpipe_handler);
|
|
}
|
|
|
|
void sigchld_handler(int sig)
|
|
{
|
|
waitpid(-1, (int *)0, WNOHANG);
|
|
}
|
|
|
|
void ctrlC_handler(int sig)
|
|
{
|
|
char chr;
|
|
|
|
setupSignalHandler(SIGINT, ctrlC_handler);
|
|
|
|
traceFlag |= TRACE_SIGINT;
|
|
|
|
printErrorMessage(ERR_SIGINT, NULL, 0);
|
|
printf("%s", "(c)ontinue, e(x)it, (r)eset:");
|
|
fflush(NULL);
|
|
chr = getchar();
|
|
if(chr == 'x') exit(1);
|
|
if(chr == 'c') traceFlag &= ~TRACE_SIGINT;
|
|
}
|
|
|
|
|
|
void sigalrm_handler(int sig)
|
|
{
|
|
setupSignalHandler(sig, sigalrm_handler);
|
|
/* check if not sitting idle */
|
|
if(recursionCount)
|
|
traceFlag |= TRACE_TIMER;
|
|
else /* if idle */
|
|
executeSymbol(timerEvent, NULL, NULL);
|
|
}
|
|
|
|
#endif /* SOLARIS, TRUE64, AIX */
|
|
|
|
|
|
void setupAllSignals(void)
|
|
{
|
|
#if defined(SOLARIS) || defined(TRU64) || defined(AIX)
|
|
setupSignalHandler(SIGINT, ctrlC_handler);
|
|
#else
|
|
setupSignalHandler(SIGINT, signal_handler);
|
|
#endif
|
|
|
|
#ifndef WINDOWS
|
|
|
|
#if defined(SOLARIS) || defined(TRU64) || defined(AIX)
|
|
setupSignalHandler(SIGALRM, sigalrm_handler);
|
|
setupSignalHandler(SIGVTALRM, sigalrm_handler);
|
|
setupSignalHandler(SIGPROF, sigalrm_handler);
|
|
setupSignalHandler(SIGPIPE, sigpipe_handler);
|
|
setupSignalHandler(SIGCHLD, sigchld_handler);
|
|
#else
|
|
setupSignalHandler(SIGALRM, signal_handler);
|
|
setupSignalHandler(SIGVTALRM, signal_handler);
|
|
setupSignalHandler(SIGPROF, signal_handler);
|
|
setupSignalHandler(SIGPIPE, signal_handler);
|
|
setupSignalHandler(SIGCHLD, signal_handler);
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
void signal_handler(int sig)
|
|
{
|
|
#ifndef WINDOWS
|
|
char chr;
|
|
#endif
|
|
|
|
if(sig > 32 || sig < 1) return;
|
|
|
|
#if defined(SOLARIS) || defined(TRU64) || defined(AIX)
|
|
switch(sig)
|
|
{
|
|
case SIGALRM:
|
|
case SIGVTALRM:
|
|
case SIGPROF:
|
|
setupSignalHandler(sig, sigalrm_handler);
|
|
break;
|
|
case SIGPIPE:
|
|
setupSignalHandler(SIGPIPE, sigpipe_handler);
|
|
break;
|
|
case SIGCHLD:
|
|
setupSignalHandler(SIGCHLD, sigchld_handler);
|
|
break;
|
|
}
|
|
#else
|
|
setupSignalHandler(sig, signal_handler);
|
|
#endif
|
|
|
|
if(symHandler[sig - 1] != nilSymbol)
|
|
{
|
|
if(recursionCount)
|
|
{
|
|
currentSignal = sig;
|
|
traceFlag |= TRACE_SIGNAL;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
executeSymbol(symHandler[sig-1], stuffInteger(sig), NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch(sig)
|
|
{
|
|
case SIGINT:
|
|
printErrorMessage(ERR_SIGINT, NULL, 0);
|
|
|
|
#ifdef WINDOWS
|
|
traceFlag |= TRACE_SIGINT;
|
|
#else
|
|
printf("%s", "\n(c)ontinue, (d)ebug, e(x)it, (r)eset:");
|
|
fflush(NULL);
|
|
chr = getchar();
|
|
if(chr == 'x') exit(1);
|
|
if(chr == 'd')
|
|
{
|
|
traceFlag &= ~TRACE_SIGINT;
|
|
openTrace();
|
|
}
|
|
if(chr == 'r') traceFlag |= TRACE_SIGINT;
|
|
break;
|
|
case SIGPIPE:
|
|
break;
|
|
case SIGALRM:
|
|
case SIGVTALRM:
|
|
case SIGPROF:
|
|
/* check if not sitting idle */
|
|
if(recursionCount)
|
|
traceFlag |= TRACE_TIMER;
|
|
else /* if idle */
|
|
executeSymbol(timerEvent, NULL, NULL);
|
|
break;
|
|
case SIGCHLD:
|
|
waitpid(-1, (int *)0, WNOHANG);
|
|
#endif
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
#endif /* no EMSCRIPTEN */
|
|
|
|
char * which(char * name, char * buff)
|
|
{
|
|
char *path_list, *test, *tmp, *path_parsed;
|
|
struct stat filestat;
|
|
int count = 1;
|
|
int i, len, nlen;
|
|
int found = FALSE;
|
|
|
|
path_list = getenv("PATH");
|
|
if (!path_list) path_list = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin";
|
|
|
|
len = strlen(path_list);
|
|
nlen = strlen(name);
|
|
path_parsed = alloca(len + 1);
|
|
strncpy(path_parsed, path_list, len + 1);
|
|
|
|
test = path_parsed;
|
|
while (TRUE)
|
|
{
|
|
#ifdef WINDOWS
|
|
tmp = strchr(test, ';');
|
|
#else
|
|
tmp = strchr(test, ':');
|
|
#endif
|
|
if (tmp == NULL) break;
|
|
*tmp = 0;
|
|
test = tmp + 1;
|
|
count++;
|
|
}
|
|
|
|
test = path_parsed;
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
len = strlen(test);
|
|
if((len + nlen + 2) > PATH_MAX)
|
|
return(NULL);
|
|
strncpy(buff, test, len + 1);
|
|
buff[len] = '/';
|
|
memcpy(buff + len + 1, name, nlen);
|
|
buff[len + 1 + nlen] = 0;
|
|
if (stat (buff, &filestat) == 0 && filestat.st_mode & S_IXUSR)
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
test += (len + 1);
|
|
}
|
|
|
|
if(!found) return(NULL);
|
|
errno = 0;
|
|
return(buff);
|
|
}
|
|
|
|
#ifndef LIBRARY
|
|
void loadStartup(char * name)
|
|
{
|
|
char initFile[PATH_MAX];
|
|
char * envPtr;
|
|
int len;
|
|
|
|
/* normal newLISP start up */
|
|
if(strncmp(linkOffset + 4, "@@@@", 4) == 0)
|
|
{
|
|
if(getenv("HOME"))
|
|
strncpy(initFile, getenv("HOME"), PATH_MAX - 16);
|
|
else if(getenv("USERPROFILE"))
|
|
strncpy(initFile, getenv("USERPROFILE"), PATH_MAX - 16);
|
|
else if(getenv("DOCUMENT_ROOT"))
|
|
strncpy(initFile, getenv("DOCUMENT_ROOT"), PATH_MAX - 16);
|
|
|
|
len = strlen(initFile);
|
|
memcpy(initFile + len, "/.", 2);
|
|
memcpy(initFile + len + 2, INIT_FILE, 8);
|
|
initFile[len + 2 + 8] = 0;
|
|
if(loadFile(initFile, 0, 0, mainContext) == NULL)
|
|
{
|
|
envPtr = getenv("NEWLISPDIR");
|
|
if(envPtr)
|
|
{
|
|
strncpy(initFile, envPtr, PATH_MAX - 16);
|
|
len = strlen(envPtr);
|
|
memcpy(initFile + len, "/", 1);
|
|
memcpy(initFile + len + 1, INIT_FILE, 8);
|
|
initFile[len + 1 + 8] = 0;
|
|
loadFile(initFile, 0, 0, mainContext);
|
|
}
|
|
}
|
|
}
|
|
/* load part at offset no init.lsp or .init.lsp is loaded */
|
|
else
|
|
{
|
|
#ifdef WINDOWS
|
|
name = win_getExePath(alloca(MAX_PATH));
|
|
loadFile(name, *(unsigned int *)linkOffset, 1, mainContext);
|
|
#else /* if not Win32 get full pathname of file in name */
|
|
if(strchr(name, '/') == NULL)
|
|
if((name = which(name, alloca(PATH_MAX))) == NULL)
|
|
{
|
|
printf("%s: %s\n", strerror(ENOENT), name);
|
|
exit(ENOENT);
|
|
}
|
|
loadFile(name, *(unsigned int *)linkOffset, 1, mainContext);
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* LIBRARY */
|
|
|
|
#ifdef _BSD
|
|
struct lconv *localeconv(void);
|
|
char *setlocale(int, const char *);
|
|
#endif
|
|
|
|
void initLocale(void)
|
|
{
|
|
#ifndef ANDROID
|
|
struct lconv * lc;
|
|
#endif
|
|
char * locale;
|
|
|
|
#ifndef SUPPORT_UTF8
|
|
locale = setlocale(LC_ALL, "C");
|
|
#else
|
|
locale = setlocale(LC_ALL, "");
|
|
#endif
|
|
|
|
if (locale != NULL)
|
|
stringOutputRaw = (strcmp(locale, "C") == 0);
|
|
|
|
#ifdef ANDROID
|
|
lc_decimal_point = '.';
|
|
#else
|
|
lc = localeconv();
|
|
lc_decimal_point = *lc->decimal_point;
|
|
#endif
|
|
}
|
|
|
|
/* set NEWLISPDIR only if not set already */
|
|
void initNewlispDir(void)
|
|
{
|
|
#ifdef WINDOWS
|
|
char * varValue;
|
|
char * newlispDir;
|
|
int len;
|
|
|
|
if(getenv("NEWLISPDIR") == NULL)
|
|
{
|
|
newlispDir = alloca(MAX_PATH);
|
|
varValue = getenv("PROGRAMFILES");
|
|
if(varValue != NULL)
|
|
{
|
|
len = strlen(varValue);
|
|
strncpy(newlispDir, varValue, MAX_PATH - 12);
|
|
memcpy(newlispDir + len, "/newlisp", 8);
|
|
newlispDir[len + 8] = 0;
|
|
setenv("NEWLISPDIR", newlispDir, TRUE);
|
|
}
|
|
else setenv("NEWLISPDIR", "newlisp", TRUE);
|
|
}
|
|
#else
|
|
if(getenv("NEWLISPDIR") == NULL)
|
|
setenv("NEWLISPDIR", NEWLISPDIR, TRUE);
|
|
#endif
|
|
}
|
|
|
|
void initTempDir()
|
|
{
|
|
#ifdef WINDOWS
|
|
if((tempDir = getenv("TMP")) == NULL)
|
|
{
|
|
printf("Environment variable TMP not set, assuming /tmp .");
|
|
tempDir = "/tmp";
|
|
}
|
|
#else
|
|
#ifdef ANDROID
|
|
tempDir = "/data/tmp";
|
|
#else /* all UNIX */
|
|
tempDir = "/tmp";
|
|
#endif
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
#ifndef LIBRARY
|
|
char * getArg(char * * arg, int argc, int * index)
|
|
{
|
|
if(strlen(arg[*index]) > 2)
|
|
return(arg[*index] + 2);
|
|
|
|
if(*index >= (argc - 1))
|
|
{
|
|
printf("missing parameter for %s\n", arg[*index]);
|
|
exit(-1);
|
|
}
|
|
|
|
*index = *index + 1;
|
|
|
|
return(arg[*index]);
|
|
}
|
|
|
|
#ifndef WINDOWS
|
|
char ** MainArgs;
|
|
#endif
|
|
|
|
CELL * getMainArgs(char * mainArgs[])
|
|
{
|
|
CELL * argList;
|
|
int idx = 0;
|
|
|
|
#ifndef WINDOWS
|
|
MainArgs = mainArgs;
|
|
#endif
|
|
|
|
argList = getCell(CELL_EXPRESSION);
|
|
|
|
while(mainArgs[idx] != NULL)
|
|
addList(argList, stuffString(mainArgs[idx++]));
|
|
|
|
return(argList);
|
|
}
|
|
|
|
char * getCommandLine(int batchMode, int * length);
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
char command[MAX_COMMAND_LINE];
|
|
STREAM cmdStream = {NULL, NULL, 0, 0, 0};
|
|
char * cmd;
|
|
int idx;
|
|
|
|
#ifdef WINDOWS
|
|
WSADATA WSAData;
|
|
if(WSAStartup(MAKEWORD(2,2), &WSAData) != 0)
|
|
{
|
|
printf("Winsocket initialization failed\n");
|
|
exit(-1);
|
|
}
|
|
pagesize = 4096;
|
|
|
|
/* replace '_CRT_fmode = _O_BINARY' in nl-filesys.c for 10.4.8, thanks to Kosh */
|
|
_setmode(_fileno(stdin), _O_BINARY);
|
|
_setmode(_fileno(stdout), _O_BINARY);
|
|
_setmode(_fileno(stderr), _O_BINARY);
|
|
#endif
|
|
|
|
#ifdef SUPPORT_UTF8
|
|
opsys += 128;
|
|
#endif
|
|
|
|
#ifdef NEWLISP64
|
|
opsys += 256;
|
|
#endif
|
|
|
|
#ifdef FFI
|
|
opsys += 1024;
|
|
initFFI();
|
|
#endif
|
|
|
|
#ifndef WINDOWS
|
|
#ifndef OS2
|
|
pagesize = getpagesize();
|
|
#endif
|
|
tzset();
|
|
#endif
|
|
|
|
#ifdef OS2
|
|
/* Reset the floating point coprocessor */
|
|
_fpreset();
|
|
#endif
|
|
|
|
initLocale();
|
|
initNewlispDir();
|
|
initTempDir();
|
|
|
|
IOchannel = stdin;
|
|
bigEndian = (*((char *)&bigEndian) == 0);
|
|
|
|
initStacks();
|
|
initialize();
|
|
initDefaultInAddr();
|
|
|
|
#ifdef WINDOWS
|
|
#ifdef SUPPORT_UTF8
|
|
{
|
|
/*
|
|
command line parameter is MBCS.
|
|
MBCS -> Unicode(UTF-16) -> UTF-8
|
|
*/
|
|
char **argv_utf8 = allocMemory((argc + 1)* sizeof(char *)) ;
|
|
{
|
|
for(idx = 0 ; idx<argc ; idx++)
|
|
{
|
|
WCHAR *p_argvW = ansi_mbcs_to_utf16(argv[idx]) ;
|
|
char *p_argvU = utf16_to_utf8(p_argvW) ;
|
|
argv_utf8[idx] = p_argvU ;
|
|
}
|
|
argv_utf8[idx] = NULL ;
|
|
argv = argv_utf8 ;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
mainArgsSymbol->contents = (UINT)getMainArgs(argv);
|
|
|
|
if((errorReg = setjmp(errorJump)) != 0)
|
|
{
|
|
if((errorEvent != nilSymbol) || (errorReg == ERR_USER_RESET))
|
|
executeSymbol(errorEvent, NULL, NULL);
|
|
else exit(-1);
|
|
goto AFTER_ERROR_ENTRY;
|
|
}
|
|
|
|
setupAllSignals();
|
|
|
|
sysEvalString(preLoad, mainContext, nilCell, EVAL_STRING);
|
|
|
|
/* loading of init.lsp can be suppressed with -n as first option
|
|
but is never done when program is link.lsp'd */
|
|
|
|
if(argc < 2 || strncmp(argv[1], "-n", 2))
|
|
{
|
|
if(!(argc >= 2 && strcmp(argv[1], "-x") == 0))
|
|
loadStartup(argv[0]);
|
|
}
|
|
|
|
errno = 0;
|
|
|
|
if(realpath(".", startupDir) == NULL)
|
|
fatalError(ERR_IO_ERROR, 0, 0);
|
|
|
|
for(idx = 1; idx < argc; idx++)
|
|
{
|
|
if(strncmp(argv[idx], "-c", 2) == 0)
|
|
{
|
|
noPromptMode = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-C", 2) == 0)
|
|
{
|
|
forcePromptMode = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-http", 5) == 0)
|
|
{
|
|
noPromptMode = TRUE;
|
|
httpMode = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-s", 2) == 0)
|
|
{
|
|
MAX_CPU_STACK = atoi(getArg(argv, argc, &idx));
|
|
|
|
if(MAX_CPU_STACK < 1024) MAX_CPU_STACK = 1024;
|
|
initStacks();
|
|
continue;
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-p", 2) == 0 || strncmp(argv[idx], "-d", 2) == 0 )
|
|
{
|
|
if(strncmp(argv[idx], "-d", 2) == 0)
|
|
daemonMode = TRUE;
|
|
|
|
IOdomain = getArg(argv, argc, &idx);
|
|
IOport = atoi(IOdomain);
|
|
|
|
setupServer(0);
|
|
continue;
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-t", 2) == 0)
|
|
{
|
|
connectionTimeout = atoi(getArg(argv, argc, &idx));
|
|
continue;
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-l", 2) == 0 || strncmp(argv[idx], "-L", 2) == 0)
|
|
{
|
|
logTraffic = (strncmp(argv[idx], "-L", 2) == 0) ? LOG_MORE : LOG_LESS;
|
|
if(realpath(getArg(argv, argc, &idx), logFile) == NULL)
|
|
close(openFile(logFile, "w", 0));
|
|
|
|
continue;
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-m", 2) == 0)
|
|
{
|
|
#ifndef NEWLISP64
|
|
MAX_CELL_COUNT = abs(0x0010000 * atoi(getArg(argv, argc, &idx)));
|
|
#else
|
|
MAX_CELL_COUNT = abs(0x0008000 * atoi(getArg(argv, argc, &idx)));
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-w", 2) == 0)
|
|
{
|
|
if(realpath(getArg(argv, argc, &idx), startupDir) == NULL
|
|
|| chdir(startupDir) < 0)
|
|
fatalError(ERR_WORKING_DIR, 0, 0);
|
|
continue;
|
|
}
|
|
|
|
if(strcmp(argv[idx], "-6") == 0)
|
|
{
|
|
ADDR_FAMILY = AF_INET6;
|
|
initDefaultInAddr();
|
|
continue;
|
|
}
|
|
|
|
if(strcmp(argv[idx], "-v") == 0)
|
|
{
|
|
varPrintf(OUT_CONSOLE, banner, OSTYPE, LIBFFI, ".");
|
|
exit(0);
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-e", 2) == 0)
|
|
{
|
|
executeCommandLine(getArg(argv, argc, &idx), OUT_CONSOLE, &cmdStream);
|
|
exit(0);
|
|
}
|
|
|
|
if(strncmp(argv[idx], "-x", 2) == 0)
|
|
{
|
|
if(argc == 4)
|
|
linkSource(argv[0], argv[idx + 1], argv[idx + 2]);
|
|
exit(0);
|
|
}
|
|
|
|
if(strcmp(argv[idx], "-h") == 0)
|
|
{
|
|
printHelpText();
|
|
exit(0);
|
|
}
|
|
|
|
loadFile(argv[idx], 0, 0, mainContext);
|
|
}
|
|
|
|
AFTER_ERROR_ENTRY:
|
|
|
|
if(isatty(fileno(IOchannel)))
|
|
{
|
|
isTTY = TRUE;
|
|
if(!noPromptMode)
|
|
varPrintf(OUT_CONSOLE, banner, OSTYPE, LIBFFI, banner2);
|
|
}
|
|
else
|
|
{
|
|
#ifdef WINDOWS
|
|
if(!IOchannelIsSocketStream)
|
|
#endif
|
|
setbuf(IOchannel,0);
|
|
if(forcePromptMode)
|
|
varPrintf(OUT_CONSOLE, banner, OSTYPE, LIBFFI, banner2);
|
|
}
|
|
|
|
/* ======================= main entry on reset ====================== */
|
|
|
|
|
|
errorReg = setjmp(errorJump);
|
|
|
|
setupAllSignals();
|
|
reset();
|
|
initStacks();
|
|
|
|
if(errorReg && !isNil((CELL*)errorEvent->contents) )
|
|
executeSymbol(errorEvent, NULL, NULL);
|
|
|
|
|
|
#ifdef READLINE
|
|
rl_readline_name = "newlisp";
|
|
rl_attempted_completion_function = (char ** (*) (const char *, int, int))newlisp_completion;
|
|
#if defined(LINUX) || defined(_BSD)
|
|
/* in Bash .inputrc put 'set blink-matching-paren on' */
|
|
rl_set_paren_blink_timeout(300000); /* 300 ms */
|
|
#endif
|
|
#endif
|
|
|
|
while(TRUE)
|
|
{
|
|
cleanupResults(resultStack);
|
|
if(isTTY)
|
|
{
|
|
cmd = getCommandLine(FALSE, NULL);
|
|
executeCommandLine(cmd, OUT_CONSOLE, &cmdStream);
|
|
free(cmd);
|
|
continue;
|
|
}
|
|
|
|
if(IOchannel != stdin || forcePromptMode)
|
|
varPrintf(OUT_CONSOLE, "%s", prompt());
|
|
|
|
/* daemon mode timeout if nothing read after accepting connection */
|
|
if(connectionTimeout && IOchannel && daemonMode)
|
|
{
|
|
#ifdef WINDOWS
|
|
if(IOchannelIsSocketStream)
|
|
if(wait_ready(getSocket(IOchannel), connectionTimeout, 0) == 0)
|
|
#else
|
|
if(wait_ready(fileno(IOchannel), connectionTimeout, 0) == 0)
|
|
#endif
|
|
{
|
|
fclose(IOchannel);
|
|
setupServer(1);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if(IOchannel == NULL || fgets(command, MAX_COMMAND_LINE - 1, IOchannel) == NULL)
|
|
{
|
|
if(!daemonMode) exit(1);
|
|
if(IOchannel != NULL) fclose(IOchannel);
|
|
setupServer(1);
|
|
continue;
|
|
}
|
|
|
|
executeCommandLine(command, OUT_CONSOLE, &cmdStream);
|
|
}
|
|
|
|
#ifndef WINDOWS
|
|
return 0;
|
|
#endif
|
|
}
|
|
#endif /* not LIBRARY */
|
|
|
|
#ifdef READLINE
|
|
char * command_generator(char * text, int state)
|
|
{
|
|
static int list_index, len, clen;
|
|
char * name;
|
|
|
|
if (!state)
|
|
{
|
|
list_index = 0;
|
|
len = strlen (text);
|
|
}
|
|
|
|
while((name = primitive[list_index].name))
|
|
{
|
|
list_index++;
|
|
|
|
if (strncmp (name, text, len) == 0)
|
|
{
|
|
clen = strlen(name) + 1;
|
|
return(strncpy(malloc(clen), name, clen));
|
|
}
|
|
}
|
|
|
|
return ((char *)NULL);
|
|
}
|
|
|
|
char ** completion_matches(const char * text, char * (*commands)(const char *, int));
|
|
|
|
char ** newlisp_completion (char * text, int start, int end)
|
|
{
|
|
return(completion_matches(text, (char * (*) (const char *, int) )command_generator));
|
|
}
|
|
|
|
#endif /* READLINE */
|
|
|
|
|
|
char * getCommandLine(int batchMode, int * length)
|
|
{
|
|
char * cmd;
|
|
int len;
|
|
|
|
#ifndef READLINE
|
|
if(!batchMode) varPrintf(OUT_CONSOLE, "%s", prompt());
|
|
cmd = calloc(MAX_COMMAND_LINE + 4, 1);
|
|
if(fgets(cmd, MAX_COMMAND_LINE - 1, IOchannel) == NULL)
|
|
{
|
|
puts("");
|
|
exit(0);
|
|
}
|
|
len = strlen(cmd);
|
|
/* cut off line terminators left by fgets */
|
|
*(cmd + len - LINE_FEED_LEN) = 0;
|
|
len -= LINE_FEED_LEN; /* v.10.6.2 */
|
|
#else /* READLINE */
|
|
int errnoSave = errno;
|
|
if((cmd = readline(batchMode ? "" : prompt())) == NULL)
|
|
{
|
|
puts("");
|
|
exit(0);
|
|
}
|
|
errno = errnoSave; /* reset errno, set by readline() */
|
|
len = strlen(cmd);
|
|
if(len > 0)
|
|
add_history(cmd);
|
|
#endif
|
|
|
|
if(length != NULL) *length = len;
|
|
return(cmd);
|
|
}
|
|
|
|
#ifndef LIBRARY
|
|
|
|
void printHelpText(void)
|
|
{
|
|
varPrintf(OUT_CONSOLE, copyright,
|
|
"usage: newlisp [file | url ...] [options ...] [file | url ...]\n\noptions:");
|
|
varPrintf(OUT_CONSOLE,
|
|
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n\n",
|
|
" -h this help",
|
|
" -n no init.lsp (must be first)",
|
|
" -x <source> <target> link",
|
|
" -v version",
|
|
" -s <stacksize>",
|
|
" -m <max-mem-MB> cell memory",
|
|
" -e <quoted lisp expression>",
|
|
" -l <path-file> log connections",
|
|
" -L <path-file> log all",
|
|
" -w <working dir>",
|
|
" -c no prompts, net-eval, HTTP",
|
|
" -C force prompts",
|
|
" -t <usec-server-timeout>",
|
|
" -p <port-no>",
|
|
" -d <port-no> daemon mode",
|
|
" -http only mode",
|
|
" -6 IPv6 mode",
|
|
"\nmore info at http://newlisp.org");
|
|
}
|
|
|
|
void setupServer(int reconnect)
|
|
{
|
|
if((IOchannel = serverFD(IOport, IOdomain, reconnect)) == NULL)
|
|
{
|
|
printf("newLISP server setup on %s failed.\n", IOdomain);
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef WINDOWS
|
|
else IOchannelIsSocketStream = TRUE;
|
|
|
|
if(!IOchannelIsSocketStream)
|
|
#endif
|
|
setbuf(IOchannel,0);
|
|
|
|
if(!reconnect && !noPromptMode)
|
|
varPrintf(OUT_CONSOLE, banner, OSTYPE, LIBFFI, ".");
|
|
}
|
|
|
|
#endif /* ifndef LIBRARY */
|
|
|
|
char * prompt(void)
|
|
{
|
|
char * contextName = "";
|
|
CELL * result;
|
|
static char string[64];
|
|
|
|
if(evalSilent || noPromptMode)
|
|
{
|
|
evalSilent = 0;
|
|
return("");
|
|
}
|
|
|
|
if(promptEvent != nilSymbol)
|
|
{
|
|
if(executeSymbol(promptEvent, stuffSymbol(currentContext), &result) == CELL_STRING)
|
|
{
|
|
strncpy(string, (char *)result->contents, 64);
|
|
string[63] = 0;
|
|
deleteList(result);
|
|
return(string);
|
|
}
|
|
deleteList(result);
|
|
}
|
|
|
|
if(currentContext != mainContext)
|
|
contextName = currentContext->name;
|
|
|
|
if(traceFlag & TRACE_SIGINT)
|
|
{
|
|
traceFlag &= ~TRACE_SIGINT;
|
|
longjmp(errorJump, errorReg);
|
|
}
|
|
|
|
if(traceFlag && !(traceFlag & TRACE_PRINT_EVAL))
|
|
snprintf(string, 63, "%d %s> ", recursionCount, contextName);
|
|
else
|
|
snprintf(string, 63, "%s> ", contextName);
|
|
|
|
return(string);
|
|
}
|
|
|
|
|
|
void reset()
|
|
{
|
|
recoverEnvironment(envStack);
|
|
|
|
while(resultStackIdx > resultStack)
|
|
deleteList(popResult());
|
|
|
|
envStackIdx = envStack;
|
|
lambdaStackIdx = lambdaStack;
|
|
|
|
freeCellBlocks();
|
|
|
|
if(printDevice) close((int)printDevice);
|
|
printDevice = recursionCount = traceFlag = prettyPrintFlags = 0;
|
|
evalFunc = NULL;
|
|
#ifdef XML_SUPPORT
|
|
xmlTags = NULL; /* force recreation */
|
|
#endif
|
|
pushResultFlag = TRUE;
|
|
currentContext = mainContext;
|
|
itSymbol->contents = (UINT)nilCell;
|
|
}
|
|
|
|
|
|
void recoverEnvironment(UINT * index)
|
|
{
|
|
SYMBOL * symbol;
|
|
CELL * cell;
|
|
|
|
while(envStackIdx > index)
|
|
{
|
|
symbol = (SYMBOL *)popEnvironment();
|
|
cell = (CELL*)popEnvironment();
|
|
if(cell != (CELL*)symbol->contents)
|
|
{
|
|
deleteList((CELL*)symbol->contents);
|
|
symbol->contents = (UINT)cell;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
char * processCommandEvent(char * command);
|
|
|
|
void executeCommandLine(char * command, UINT outDevice, STREAM * cmdStream)
|
|
{
|
|
STREAM stream;
|
|
char buff[MAX_COMMAND_LINE];
|
|
char * cmd;
|
|
int batchMode = 0;
|
|
int len;
|
|
|
|
memset(buff + MAX_COMMAND_LINE -2, 0, 2);
|
|
|
|
if(memcmp(command, "[cmd]", 5) == 0)
|
|
batchMode = 2;
|
|
else if(isTTY && (*command == '\n' || *command == '\r' || *command == 0))
|
|
batchMode = 1;
|
|
|
|
#ifndef LIBRARY
|
|
if(!batchMode && commandEvent != nilSymbol)
|
|
command = processCommandEvent(command);
|
|
#endif
|
|
|
|
if(!isTTY && (*command == '\n' || *command == '\r' || *command == 0)) return;
|
|
|
|
if(!batchMode)
|
|
{
|
|
if(logTraffic == LOG_MORE)
|
|
writeLog(command, TRUE);
|
|
#ifndef LIBRARY
|
|
if(strncmp(command, "GET /", 5) == 0)
|
|
executeHTTPrequest(command + 5, HTTP_GET);
|
|
else if(strncmp(command, "HEAD /", 6) == 0)
|
|
executeHTTPrequest(command + 6, HTTP_HEAD);
|
|
else if(strncmp(command, "PUT /", 5) == 0)
|
|
executeHTTPrequest(command + 5, HTTP_PUT);
|
|
else if(strncmp(command, "POST /", 6) == 0)
|
|
executeHTTPrequest(command + 6, HTTP_POST);
|
|
else if(strncmp(command, "DELETE /", 8) == 0)
|
|
executeHTTPrequest(command + 8, HTTP_DELETE);
|
|
#endif
|
|
else if(!httpMode) goto EXEC_COMMANDLINE;
|
|
return;
|
|
}
|
|
|
|
if(httpMode) goto RETURN_BATCHMODE;
|
|
|
|
EXEC_COMMANDLINE:
|
|
if(noPromptMode == FALSE && *command == '!' && *(command + 1) != ' ' && strlen(command) > 1)
|
|
{
|
|
if(system(command + 1)) return; /* avoid stupid compiler warning */
|
|
return;
|
|
}
|
|
|
|
if(cmdStream != NULL && batchMode)
|
|
{
|
|
openStrStream(cmdStream, 1024, TRUE);
|
|
for(;;)
|
|
{
|
|
if(isTTY)
|
|
{
|
|
cmd = getCommandLine(TRUE, &len);
|
|
if(len > (MAX_COMMAND_LINE - 3))
|
|
len = MAX_COMMAND_LINE - 3;
|
|
memcpy(buff, cmd, len);
|
|
memcpy(buff + len, LINE_FEED, LINE_FEED_LEN);
|
|
buff[len + LINE_FEED_LEN] = 0;
|
|
free(cmd);
|
|
}
|
|
else
|
|
if(fgets(buff, MAX_COMMAND_LINE - 1, IOchannel) == NULL) break;
|
|
if( (memcmp(buff, "[/cmd]", 6) == 0 && batchMode == 2) ||
|
|
(batchMode == 1 && (*buff == '\n' || *buff == '\r' || *buff == 0)))
|
|
{
|
|
if(logTraffic)
|
|
writeLog(cmdStream->buffer, 0);
|
|
makeStreamFromString(&stream, cmdStream->buffer);
|
|
evaluateStream(&stream, outDevice, 0);
|
|
return;
|
|
}
|
|
writeStreamStr(cmdStream, buff, 0);
|
|
}
|
|
closeStrStream(cmdStream);
|
|
RETURN_BATCHMODE:
|
|
if(!daemonMode) exit(1);
|
|
if(IOchannel != NULL) fclose(IOchannel);
|
|
#ifndef LIBRARY
|
|
setupServer(1);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if(logTraffic == LOG_LESS) writeLog(command, TRUE);
|
|
prettyPrintLength = 0;
|
|
|
|
makeStreamFromString(&stream, command);
|
|
evaluateStream(&stream, outDevice, 0);
|
|
}
|
|
|
|
char * processCommandEvent(char * command)
|
|
{
|
|
CELL * result;
|
|
|
|
if(executeSymbol(commandEvent, stuffString(command), &result) == CELL_STRING)
|
|
{
|
|
pushResult(result);
|
|
command = (char *)result->contents;
|
|
}
|
|
|
|
return(command);
|
|
}
|
|
|
|
/*
|
|
void printResultStack()
|
|
{
|
|
printf("result stack:\n");
|
|
while(resultStackIdx > resultStack)
|
|
{
|
|
--resultStackIdx;
|
|
printCell(*resultStackIdx, TRUE, OUT_CONSOLE);
|
|
printf("\n");
|
|
}
|
|
printf("\n");
|
|
}
|
|
*/
|
|
|
|
/* used for loadFile() and and executeCommandLine() */
|
|
CELL * evaluateStream(STREAM * stream, UINT outDevice, int flag)
|
|
{
|
|
CELL * program;
|
|
CELL * eval = nilCell;
|
|
CELL * xlate;
|
|
UINT * resultIdxSave = resultStackIdx;
|
|
int result = TRUE;
|
|
|
|
while(result)
|
|
{
|
|
pushResult(program = getCell(CELL_QUOTE));
|
|
result = compileExpression(stream, program);
|
|
if(readerEvent != nilSymbol && result)
|
|
{
|
|
--resultStackIdx; /* program cell consumed by executeSymbol() */
|
|
executeSymbol(readerEvent, program, &xlate);
|
|
pushResult(program = makeCell(CELL_QUOTE, (UINT)xlate));
|
|
}
|
|
if(result)
|
|
{
|
|
if(flag && eval != nilCell) deleteList(eval);
|
|
eval = evaluateExpression((CELL *)program->contents);
|
|
if(outDevice != 0 && !evalSilent)
|
|
{
|
|
printCell(eval, TRUE, outDevice);
|
|
varPrintf(outDevice, "\n");
|
|
if(logTraffic == LOG_MORE)
|
|
{
|
|
writeLog("-> ", 0);
|
|
printCell(eval, TRUE, OUT_LOG);
|
|
writeLog("", TRUE);
|
|
}
|
|
#ifdef EMSCRIPTEN
|
|
if(outDevice) fflush(NULL);
|
|
#endif
|
|
}
|
|
if(flag) eval = copyCell(eval);
|
|
}
|
|
cleanupResults(resultIdxSave);
|
|
}
|
|
|
|
if(flag) return(eval);
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
int executeSymbol(SYMBOL * symbol, CELL * params, CELL * * result)
|
|
{
|
|
CELL * program;
|
|
CELL * cell;
|
|
UINT * resultIdxSave = resultStackIdx;
|
|
|
|
if(symbol == nilSymbol || symbol == trueSymbol || symbol == NULL) return(0);
|
|
pushResult(program = getCell(CELL_EXPRESSION));
|
|
cell = makeCell(CELL_SYMBOL, (UINT)symbol);
|
|
program->contents = (UINT)cell;
|
|
if(params != NULL) cell->next = params;
|
|
|
|
if(result == NULL)
|
|
{
|
|
evaluateExpression(program);
|
|
cleanupResults(resultIdxSave);
|
|
return(0);
|
|
}
|
|
|
|
*result = copyCell(evaluateExpression(program));
|
|
cleanupResults(resultIdxSave);
|
|
|
|
return((*result)->type);
|
|
}
|
|
|
|
|
|
/* -------------------------- initialization -------------------- */
|
|
|
|
void initialize()
|
|
{
|
|
SYMBOL * symbol;
|
|
CELL * pCell;
|
|
char symName[8];
|
|
int i;
|
|
|
|
/* build true and false cells */
|
|
nilCell = getCell(CELL_NIL);
|
|
nilCell->aux = nilCell->contents = (UINT)nilCell;
|
|
nilCell->next = nilCell;
|
|
|
|
trueCell = getCell(CELL_TRUE);
|
|
trueCell->contents = (UINT)trueCell;
|
|
|
|
/* make first symbol, which is context MAIN */
|
|
currentContext = createRootContext("MAIN");
|
|
|
|
/* build symbols for primitives */
|
|
for(i = 0; primitive[i].name != NULL; i++)
|
|
{
|
|
pCell = getCell(CELL_PRIMITIVE);
|
|
symbol = translateCreateSymbol(
|
|
primitive[i].name, CELL_PRIMITIVE, mainContext, TRUE);
|
|
symbol->contents = (UINT)pCell;
|
|
symbol->flags = primitive[i].flags | SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
pCell->contents = (UINT)primitive[i].function;
|
|
pCell->aux = (UINT)symbol->name;
|
|
}
|
|
|
|
/* build nil, true, * and ? symbols and others */
|
|
nilSymbol = translateCreateSymbol("nil", CELL_NIL, mainContext, TRUE);
|
|
nilSymbol->contents = (UINT)nilCell;
|
|
trueSymbol = translateCreateSymbol("true", CELL_TRUE, mainContext, TRUE);
|
|
trueSymbol->contents = (UINT)trueCell;
|
|
starSymbol = translateCreateSymbol("*", CELL_PRIMITIVE, mainContext, TRUE);
|
|
plusSymbol = translateCreateSymbol("+", CELL_PRIMITIVE, mainContext, TRUE);
|
|
questionSymbol = translateCreateSymbol("?", CELL_NIL, mainContext, TRUE);
|
|
atSymbol = translateCreateSymbol("@", CELL_NIL, mainContext, TRUE);
|
|
argsSymbol = translateCreateSymbol("$args", CELL_NIL, mainContext, TRUE);
|
|
mainArgsSymbol = translateCreateSymbol("$main-args", CELL_NIL, mainContext, TRUE);
|
|
listIdxSymbol = translateCreateSymbol("$idx", CELL_NIL, mainContext, TRUE);
|
|
itSymbol = translateCreateSymbol("$it", CELL_NIL, mainContext, TRUE);
|
|
countSymbol = translateCreateSymbol("$count", CELL_NIL, mainContext, TRUE);
|
|
sysxSymbol = translateCreateSymbol("$x", CELL_NIL, mainContext, TRUE);
|
|
beginSymbol = translateCreateSymbol("begin", CELL_NIL, mainContext, TRUE);
|
|
expandSymbol = translateCreateSymbol("expand", CELL_NIL, mainContext, TRUE);
|
|
|
|
symbol = translateCreateSymbol("ostype", CELL_STRING, mainContext, TRUE);
|
|
symbol->flags = SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
symbol->contents = (UINT)stuffString(OSTYPE);
|
|
|
|
for(i = 0; i < MAX_REGEX_EXP; i++)
|
|
{
|
|
snprintf(symName, 8, "$%d", i);
|
|
sysSymbol[i] = translateCreateSymbol(symName, CELL_NIL, mainContext, TRUE);
|
|
sysSymbol[i]->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN;
|
|
}
|
|
|
|
currentFunc = errorEvent = timerEvent = promptEvent = commandEvent = transferEvent = readerEvent = nilSymbol;
|
|
|
|
trueSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
nilSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
questionSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
atSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
argsSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
mainArgsSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
listIdxSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
itSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
countSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN | SYMBOL_PROTECTED;
|
|
sysxSymbol->flags |= SYMBOL_GLOBAL | SYMBOL_BUILTIN;
|
|
|
|
countCell = stuffInteger(0);
|
|
countSymbol->contents = (UINT)countCell ;
|
|
argsSymbol->contents = (UINT)getCell(CELL_EXPRESSION);
|
|
objSymbol.contents = (UINT)nilCell;
|
|
objSymbol.context = mainContext;
|
|
objCell = nilCell;
|
|
|
|
/* init signal handlers */
|
|
for(i = 0; i < 32; i++)
|
|
symHandler[i] = nilSymbol;
|
|
|
|
/* init system wide string streams */
|
|
openStrStream(&readLineStream, 16, 0);
|
|
openStrStream(&errorStream, MAX_LINE, 0);
|
|
}
|
|
|
|
void initStacks()
|
|
{
|
|
MAX_ENV_STACK = (MAX_CPU_STACK * 8 * 2);
|
|
MAX_RESULT_STACK = (MAX_CPU_STACK * 2);
|
|
if(envStack != NULL) freeMemory(envStack);
|
|
if(resultStack != NULL) freeMemory(resultStack);
|
|
if(lambdaStack != NULL) freeMemory(lambdaStack);
|
|
envStackIdx = envStack = (UINT *)allocMemory((MAX_ENV_STACK + 16) * sizeof(UINT));
|
|
envStackTop = envStack + MAX_ENV_STACK;
|
|
resultStackIdx = resultStack = (UINT *)allocMemory((MAX_RESULT_STACK + 16) * sizeof(UINT));
|
|
resultStackTop = resultStack + MAX_RESULT_STACK;
|
|
lambdaStackIdx = lambdaStack = (UINT *)allocMemory((MAX_RESULT_STACK + 16) * sizeof(UINT));
|
|
}
|
|
|
|
|
|
/* ------------------------- evaluate s-expression --------------------- */
|
|
|
|
|
|
CELL * evaluateExpression(CELL * cell)
|
|
{
|
|
#ifdef ISO_C90
|
|
CELL * result;
|
|
UINT * resultIdxSave = resultStackIdx;
|
|
CELL * args = NULL;
|
|
CELL * pCell = NULL;
|
|
SYMBOL * newContext = NULL;
|
|
SYMBOL * sPtr = NULL;
|
|
#endif
|
|
|
|
symbolCheck = NULL;
|
|
stringCell = NULL;
|
|
|
|
if(isSelfEval(cell->type))
|
|
return(cell);
|
|
|
|
if(cell->type == CELL_SYMBOL || cell->type == CELL_CONTEXT)
|
|
{
|
|
symbolCheck = (SYMBOL *)cell->contents;
|
|
return((CELL *)symbolCheck->contents);
|
|
}
|
|
|
|
#ifndef ISO_C90
|
|
CELL * result;
|
|
UINT * resultIdxSave = resultStackIdx;
|
|
CELL * args = NULL;
|
|
CELL * pCell = NULL;
|
|
SYMBOL * newContext = NULL;
|
|
SYMBOL * sPtr = NULL;
|
|
#endif
|
|
|
|
switch(cell->type)
|
|
{
|
|
case CELL_QUOTE:
|
|
return((CELL *)cell->contents);
|
|
|
|
case CELL_EXPRESSION:
|
|
args = (CELL *)cell->contents;
|
|
if(++recursionCount > (int)MAX_CPU_STACK)
|
|
fatalError(ERR_OUT_OF_CALL_STACK, args, 0);
|
|
|
|
if(args->type == CELL_SYMBOL) /* precheck for speedup */
|
|
{
|
|
sPtr = (SYMBOL *)args->contents;
|
|
newContext = sPtr->context;
|
|
pCell = (CELL*)sPtr->contents;
|
|
}
|
|
else if(args->type == CELL_DYN_SYMBOL)
|
|
{
|
|
sPtr = getDynamicSymbol(args);
|
|
newContext = sPtr->context;
|
|
pCell = (CELL *)sPtr->contents;
|
|
}
|
|
else
|
|
{
|
|
pCell = evaluateExpression(args);
|
|
newContext = currentContext;
|
|
}
|
|
|
|
if(traceFlag) traceEntry(cell, pCell, args);
|
|
|
|
/* check for 'default' functor
|
|
* allow function call with context name, i.e: (ctx)
|
|
* assumes that a ctx:ctx contains a function
|
|
*/
|
|
if(pCell->type == CELL_CONTEXT)
|
|
{
|
|
newContext = (SYMBOL *)pCell->contents;
|
|
sPtr= translateCreateSymbol(newContext->name, CELL_NIL, newContext, TRUE);
|
|
pCell = (CELL *)sPtr->contents;
|
|
|
|
/* if the default functor contains nil, it works like a hash function */
|
|
if(isNil(pCell))
|
|
{
|
|
result = evaluateNamespaceHash(args->next, newContext);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* pCell is evaluated op element */
|
|
if(pCell->type == CELL_PRIMITIVE)
|
|
{
|
|
evalFunc = (CELL *(*)(CELL*))pCell->contents;
|
|
result = evalFunc(args->next);
|
|
evalFunc = NULL;
|
|
break;
|
|
}
|
|
|
|
if(pCell->type == CELL_LAMBDA)
|
|
{
|
|
pushLambda(cell);
|
|
result = evaluateLambda((CELL *)pCell->contents, args->next, newContext);
|
|
--lambdaStackIdx;
|
|
break;
|
|
}
|
|
|
|
if(pCell->type == CELL_FEXPR)
|
|
{
|
|
pushLambda(cell);
|
|
result = evaluateLambdaMacro((CELL *)pCell->contents, args->next, newContext);
|
|
--lambdaStackIdx;
|
|
break;
|
|
}
|
|
#ifndef EMSCRIPTEN
|
|
/* simple ffi with CDECL or DLL and extended libffi */
|
|
if(pCell->type & IMPORT_MASK)
|
|
{
|
|
result = executeLibfunction(pCell, args->next);
|
|
break;
|
|
}
|
|
#endif
|
|
/* implicit indexing or resting for list, array or string
|
|
*/
|
|
if(args->next != nilCell)
|
|
{
|
|
/* implicit indexing array */
|
|
if(pCell->type == CELL_EXPRESSION)
|
|
{
|
|
if(!sPtr) sPtr = symbolCheck;
|
|
result = implicitIndexList(pCell, args->next);
|
|
symbolCheck = sPtr;
|
|
pushResultFlag = FALSE;
|
|
}
|
|
|
|
/* implicit indexing array */
|
|
else if(pCell->type == CELL_ARRAY)
|
|
{
|
|
if(!sPtr) sPtr = symbolCheck;
|
|
result = implicitIndexArray(pCell, args->next);
|
|
symbolCheck = sPtr;
|
|
pushResultFlag = FALSE;
|
|
}
|
|
|
|
/* implicit indexing string */
|
|
else if(pCell->type == CELL_STRING)
|
|
{
|
|
if(sPtr || (sPtr = symbolCheck))
|
|
{
|
|
result = implicitIndexString(pCell, args->next);
|
|
/* result is always a copy */
|
|
pushResult(result);
|
|
symbolCheck = sPtr;
|
|
--recursionCount;
|
|
return(result);
|
|
}
|
|
else
|
|
result = implicitIndexString(pCell, args->next);
|
|
}
|
|
|
|
/* implicit resting for lists and strings */
|
|
else if(isNumber(pCell->type))
|
|
result = implicitNrestSlice(pCell, args->next);
|
|
|
|
else
|
|
result = errorProcExt(ERR_INVALID_FUNCTION, cell);
|
|
} /* implixit indexing, resting on lists and strings */
|
|
else
|
|
result = errorProcExt(ERR_INVALID_FUNCTION, cell);
|
|
break;
|
|
|
|
case CELL_DYN_SYMBOL:
|
|
symbolCheck = getDynamicSymbol(cell);
|
|
return((CELL *)symbolCheck->contents);
|
|
|
|
default:
|
|
result = nilCell;
|
|
}
|
|
|
|
if(pushResultFlag)
|
|
{
|
|
if(resultStackIdx > resultStackTop)
|
|
fatalError(ERR_OUT_OF_CALL_STACK, pCell, 0);
|
|
|
|
while(resultStackIdx > resultIdxSave)
|
|
deleteList(popResult());
|
|
|
|
pushResult(result);
|
|
}
|
|
else
|
|
pushResultFlag = TRUE;
|
|
|
|
if(traceFlag) traceExit(result, cell, pCell, args);
|
|
--recursionCount;
|
|
return(result);
|
|
}
|
|
|
|
|
|
CELL * evaluateExpressionSafe(CELL * cell, int * errNo)
|
|
{
|
|
jmp_buf errorJumpSave;
|
|
CELL * result;
|
|
|
|
memcpy(errorJumpSave, errorJump, sizeof(jmp_buf));
|
|
if((*errNo = setjmp(errorJump)) != 0)
|
|
{
|
|
memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
|
|
return(NULL);
|
|
}
|
|
|
|
result = evaluateExpression(cell);
|
|
memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
|
|
return(result);
|
|
}
|
|
|
|
|
|
CELL * evaluateNamespaceHash(CELL * args, SYMBOL * newContext)
|
|
{
|
|
SYMBOL * sPtr;
|
|
CELL * pCell;
|
|
|
|
pCell = evaluateExpression(args);
|
|
if(pCell->type == CELL_STRING || isNumber(pCell->type))
|
|
{
|
|
/* set contents */
|
|
if(args->next != nilCell)
|
|
{
|
|
sPtr = makeSafeSymbol(pCell, newContext, TRUE);
|
|
|
|
itSymbol->contents = sPtr->contents;
|
|
/* itSymbol may occur in evaluateExpression() */
|
|
itSymbol->contents = (UINT)copyCell(evaluateExpression(args->next));
|
|
deleteList((CELL *)sPtr->contents);
|
|
sPtr->contents = itSymbol->contents;
|
|
itSymbol->contents = (UINT)nilCell;
|
|
|
|
if(isNil((CELL *)sPtr->contents))
|
|
{
|
|
deleteAndFreeSymbol(sPtr, FALSE);
|
|
return(nilCell);
|
|
}
|
|
|
|
symbolCheck = sPtr;
|
|
pushResultFlag = FALSE;
|
|
return((CELL *)sPtr->contents);
|
|
}
|
|
/* get contents */
|
|
else
|
|
{
|
|
sPtr = makeSafeSymbol(pCell, newContext, FALSE);
|
|
if(sPtr == NULL)
|
|
return(nilCell);
|
|
else
|
|
{
|
|
symbolCheck = sPtr;
|
|
pushResultFlag = FALSE;
|
|
return((CELL *)sPtr->contents);
|
|
}
|
|
}
|
|
}
|
|
/* create Tree from association list */
|
|
else if(pCell->type == CELL_EXPRESSION)
|
|
{
|
|
args = (CELL *)pCell->contents;
|
|
while(args->type == CELL_EXPRESSION)
|
|
{
|
|
pCell = (CELL *)args->contents;
|
|
if(pCell->type == CELL_STRING || isNumber(pCell->type))
|
|
{
|
|
sPtr = makeSafeSymbol(pCell, newContext, TRUE);
|
|
deleteList((CELL *)sPtr->contents);
|
|
sPtr->contents = (UINT)copyCell(pCell->next);
|
|
}
|
|
args = args->next;
|
|
}
|
|
return(stuffSymbol(newContext));
|
|
}
|
|
/* return association list */
|
|
else if(pCell->type == CELL_NIL)
|
|
return(associationsFromTree(newContext));
|
|
|
|
return(errorProcExt(ERR_INVALID_PARAMETER, pCell));
|
|
}
|
|
|
|
|
|
/* a symbol belonging to a dynamic context
|
|
the parent context symbol points to the real context
|
|
cell->contents -> name str of this symbol
|
|
cell->aux -> symbol var which holds context (dynamic)
|
|
((SYMBOL*)cell->aux)->contents -> context cell
|
|
*/
|
|
SYMBOL * getDynamicSymbol(CELL * cell)
|
|
{
|
|
CELL * contextCell;
|
|
|
|
contextCell = (CELL *)((SYMBOL *)cell->aux)->contents;
|
|
if(contextCell->type != CELL_CONTEXT)
|
|
fatalError(ERR_CONTEXT_EXPECTED, stuffSymbol((SYMBOL*)cell->aux), TRUE);
|
|
|
|
return(translateCreateSymbol(
|
|
(char*)cell->contents, /* name of dyn symbol */
|
|
CELL_NIL,
|
|
(SYMBOL*)contextCell->contents, /* contextPtr */
|
|
TRUE));
|
|
}
|
|
|
|
|
|
void cleanupResults(UINT * from)
|
|
{
|
|
while(resultStackIdx > from)
|
|
deleteList(popResult());
|
|
}
|
|
|
|
/* -------------------- evaluate lambda function ----------------------- */
|
|
|
|
CELL * evaluateLambda(CELL * localLst, CELL * arg, SYMBOL * newContext)
|
|
{
|
|
CELL * local;
|
|
CELL * result = nilCell;
|
|
CELL * cell;
|
|
SYMBOL * symbol;
|
|
SYMBOL * contextSave;
|
|
UINT * resultIdxSave;
|
|
int localCount = 1; /* 1 for $args */
|
|
|
|
if(envStackIdx > envStackTop)
|
|
return(errorProc(ERR_OUT_OF_ENV_STACK));
|
|
|
|
if(localLst->type != CELL_EXPRESSION)
|
|
return(errorProcExt(ERR_INVALID_LAMBDA, localLst));
|
|
|
|
/* evaluate arguments */
|
|
if(arg != nilCell)
|
|
{
|
|
/* this symbol precheck does 10% speed improvement on lambdas */
|
|
if(arg->type == CELL_SYMBOL)
|
|
cell = result = copyCell((CELL*)((SYMBOL *)arg->contents)->contents);
|
|
else
|
|
cell = result = copyCell(evaluateExpression(arg));
|
|
|
|
while((arg = arg->next) != nilCell)
|
|
{
|
|
if(arg->type == CELL_SYMBOL)
|
|
cell = cell->next = copyCell((CELL*)((SYMBOL *)arg->contents)->contents);
|
|
else
|
|
cell = cell->next = copyCell(evaluateExpression(arg));
|
|
}
|
|
}
|
|
|
|
/* change to new context */
|
|
contextSave = currentContext;
|
|
currentContext = newContext;
|
|
|
|
/* save environment and get parameters */
|
|
local = (CELL*)localLst->contents;
|
|
for(;;)
|
|
{
|
|
if(local->type == CELL_SYMBOL)
|
|
symbol = (SYMBOL *)local->contents;
|
|
/* get default parameters */
|
|
else if(local->type == CELL_EXPRESSION)
|
|
{
|
|
cell = (CELL *)local->contents;
|
|
if(cell->type == CELL_SYMBOL)
|
|
{
|
|
symbol = (SYMBOL *)cell->contents;
|
|
if(result == nilCell)
|
|
result = copyCell(evaluateExpression(cell->next));
|
|
}
|
|
else break;
|
|
}
|
|
else break;
|
|
|
|
if(isProtected(symbol->flags))
|
|
return(errorProcExt(ERR_SYMBOL_PROTECTED, local));
|
|
|
|
/* save symbol environment */
|
|
pushEnvironment(symbol->contents);
|
|
pushEnvironment((UINT)symbol);
|
|
|
|
/* fill local symbols */
|
|
if(result == nilCell) result = copyCell(nilCell);
|
|
symbol->contents = (UINT)result;
|
|
|
|
cell = result;
|
|
result = result->next;
|
|
|
|
/* unlink list */
|
|
cell->next = nilCell;
|
|
|
|
local = local->next;
|
|
localCount++;
|
|
}
|
|
|
|
/* put unassigned args in protected $args */
|
|
pushEnvironment(argsSymbol->contents);
|
|
pushEnvironment((UINT)argsSymbol);
|
|
argsSymbol->contents = (UINT)makeCell(CELL_EXPRESSION, (UINT)result);
|
|
|
|
/* get contents for (self), is nil if no ancestor caller is colon : */
|
|
objSymbol.contents = (UINT)objCell;
|
|
|
|
#ifdef FOOP_DEBUG
|
|
printf("objCell in lambda:");
|
|
printCell(objCell, TRUE, OUT_CONSOLE);
|
|
printf(" context:%s\n", currentContext->name);
|
|
#endif
|
|
|
|
/* evaluate body expressions */
|
|
resultIdxSave = resultStackIdx;
|
|
result = nilCell;
|
|
while( (localLst = localLst->next) != nilCell)
|
|
{
|
|
while(resultStackIdx > resultIdxSave)
|
|
deleteList(popResult());
|
|
result = evaluateExpression(localLst);
|
|
}
|
|
result = copyCell(result);
|
|
|
|
/* restore symbols used as locals */
|
|
while(localCount--)
|
|
{
|
|
symbol = (SYMBOL *)popEnvironment();
|
|
deleteList((CELL *)symbol->contents);
|
|
symbol->contents = popEnvironment();
|
|
}
|
|
|
|
currentContext = contextSave;
|
|
symbolCheck = NULL;
|
|
stringCell = NULL;
|
|
return(result);
|
|
}
|
|
|
|
|
|
CELL * evaluateLambdaMacro(CELL * localLst, CELL * arg, SYMBOL * newContext)
|
|
{
|
|
CELL * local;
|
|
CELL * result = nilCell;
|
|
CELL * cell;
|
|
SYMBOL * symbol;
|
|
SYMBOL * contextSave;
|
|
UINT * resultIdxSave;
|
|
int localCount = 1; /* for $args */
|
|
|
|
if(envStackIdx > envStackTop)
|
|
return(errorProc(ERR_OUT_OF_ENV_STACK));
|
|
|
|
if(localLst->type != CELL_EXPRESSION)
|
|
return(errorProcExt(ERR_INVALID_MACRO, localLst));
|
|
|
|
local = (CELL *)localLst->contents;
|
|
|
|
contextSave = currentContext;
|
|
currentContext = newContext;
|
|
|
|
/* save environment and get parameters */
|
|
while (TRUE)
|
|
{
|
|
if(local->type == CELL_SYMBOL)
|
|
symbol = (SYMBOL *)local->contents;
|
|
/* get default parameters */
|
|
else if(local->type == CELL_EXPRESSION)
|
|
{
|
|
if(((CELL*)local->contents)->type == CELL_SYMBOL)
|
|
{
|
|
cell = (CELL *)local->contents;
|
|
if(cell->type == CELL_SYMBOL)
|
|
{
|
|
symbol = (SYMBOL *)cell->contents;
|
|
if(arg == nilCell)
|
|
arg = evaluateExpression(cell->next);
|
|
}
|
|
else break;
|
|
}
|
|
else break;
|
|
}
|
|
else break;
|
|
|
|
if(isProtected(symbol->flags))
|
|
return(errorProcExt(ERR_SYMBOL_PROTECTED, local));
|
|
|
|
pushEnvironment(symbol->contents);
|
|
pushEnvironment((UINT)symbol);
|
|
symbol->contents = (UINT)copyCell(arg);
|
|
local = local->next;
|
|
arg = arg->next;
|
|
localCount++;
|
|
}
|
|
|
|
/* put unassigned args in $args */
|
|
pushEnvironment(argsSymbol->contents);
|
|
pushEnvironment((UINT)argsSymbol);
|
|
argsSymbol->contents = (UINT)makeCell(CELL_EXPRESSION, (UINT)copyList(arg));
|
|
|
|
/* evaluate body expressions */
|
|
resultIdxSave = resultStackIdx;
|
|
while((localLst = localLst->next) != nilCell)
|
|
{
|
|
while(resultStackIdx > resultIdxSave)
|
|
deleteList(popResult());
|
|
result = evaluateExpression(localLst);
|
|
}
|
|
result = copyCell(result);
|
|
|
|
/* restore symbols used as locals */
|
|
while(localCount--)
|
|
{
|
|
symbol = (SYMBOL *)popEnvironment();
|
|
deleteList((CELL *)symbol->contents);
|
|
symbol->contents = popEnvironment();
|
|
}
|
|
|
|
currentContext = contextSave;
|
|
symbolCheck = NULL;
|
|
stringCell = NULL;
|
|
return(result);
|
|
}
|
|
|
|
|
|
/* -------------- list/cell creation/deletion routines ---------------- */
|
|
|
|
|
|
CELL * stuffInteger(UINT contents)
|
|
{
|
|
CELL * cell;
|
|
|
|
if(firstFreeCell == NULL) allocBlock();
|
|
cell = firstFreeCell;
|
|
firstFreeCell = cell->next;
|
|
++cellCount;
|
|
|
|
cell->type = CELL_LONG;
|
|
cell->next = nilCell;
|
|
cell->aux = (UINT)nilCell;
|
|
cell->contents = contents;
|
|
|
|
return(cell);
|
|
}
|
|
|
|
#ifndef NEWLISP64
|
|
CELL * stuffInteger64(INT64 contents)
|
|
{
|
|
CELL * cell;
|
|
|
|
if(firstFreeCell == NULL) allocBlock();
|
|
cell = firstFreeCell;
|
|
firstFreeCell = cell->next;
|
|
++cellCount;
|
|
|
|
cell->type = CELL_INT64;
|
|
cell->next = nilCell;
|
|
|
|
*(INT64 *)&cell->aux = contents;
|
|
return(cell);
|
|
}
|
|
#endif
|
|
|
|
CELL * stuffIntegerList(int argc, ...)
|
|
{
|
|
CELL * cell;
|
|
CELL * list;
|
|
va_list ap;
|
|
|
|
va_start(ap, argc);
|
|
|
|
list = makeCell(CELL_EXPRESSION, (UINT)stuffInteger(va_arg(ap, UINT)));
|
|
cell = (CELL *)list->contents;
|
|
|
|
while(--argc)
|
|
cell = cell->next = stuffInteger(va_arg(ap, UINT));
|
|
|
|
va_end(ap);
|
|
|
|
return(list);
|
|
}
|
|
|
|
#ifdef BIGINT
|
|
CELL * stuffBigint(char * token)
|
|
{
|
|
int len;
|
|
CELL * cell;
|
|
|
|
cell = getCell(CELL_BIGINT);
|
|
cell->contents = (UINT)strToBigint(token, strlen(token), &len);
|
|
cell->aux = len + 1;
|
|
|
|
return(cell);
|
|
}
|
|
#endif
|
|
|
|
/* only safe for text content */
|
|
CELL * stuffString(char * string)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = getCell(CELL_STRING);
|
|
cell->aux = strlen(string) + 1;
|
|
cell->contents = (UINT)allocMemory((UINT)cell->aux);
|
|
|
|
memcpy((void *)cell->contents, string, (UINT)cell->aux);
|
|
return(cell);
|
|
}
|
|
|
|
/* safe for binary content */
|
|
CELL * stuffStringN(char * string, int len)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = getCell(CELL_STRING);
|
|
cell->aux = len + 1;
|
|
cell->contents = (UINT)allocMemory((UINT)cell->aux);
|
|
memcpy((void *)cell->contents, string, len);
|
|
*(char*)(cell->contents + len) = 0;
|
|
return(cell);
|
|
}
|
|
|
|
CELL * stuffFloat(double floatVal)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = getCell(CELL_FLOAT);
|
|
#ifndef NEWLISP64
|
|
*(double *)&cell->aux = floatVal;
|
|
#else
|
|
*(double *)&cell->contents = floatVal;
|
|
#endif
|
|
return(cell);
|
|
}
|
|
|
|
CELL * stuffSymbol(SYMBOL * sPtr)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = getCell(CELL_SYMBOL);
|
|
cell->contents = (UINT)sPtr;
|
|
return(cell);
|
|
}
|
|
|
|
/* appends to a list, the list must have be either optimized
|
|
with list->aux pointing to the last cell, or list->aux must
|
|
contain nilCell and be empty
|
|
*/
|
|
|
|
void addList(CELL * list, CELL * new)
|
|
{
|
|
if(list->aux == (UINT)nilCell)
|
|
list->contents = (UINT)new;
|
|
else
|
|
((CELL *)list->aux)->next = new;
|
|
list->aux = (UINT)new;
|
|
}
|
|
|
|
ssize_t convertNegativeOffset(ssize_t offset, CELL * list)
|
|
{
|
|
int len = 0;
|
|
|
|
while(list != nilCell)
|
|
{
|
|
++len;
|
|
list = list->next;
|
|
}
|
|
offset = len + offset;
|
|
if(offset < 0)
|
|
errorProc(ERR_LIST_INDEX_INVALID);
|
|
|
|
return(offset);
|
|
}
|
|
|
|
/* ------------------------ creating and freeing cells ------------------- */
|
|
|
|
CELL * getCell(int type)
|
|
{
|
|
CELL * cell;
|
|
|
|
if(firstFreeCell == NULL) allocBlock();
|
|
cell = firstFreeCell;
|
|
firstFreeCell = cell->next;
|
|
++cellCount;
|
|
|
|
cell->type = type;
|
|
cell->next = nilCell;
|
|
cell->aux = (UINT)nilCell;
|
|
cell->contents = (UINT)nilCell;
|
|
|
|
return(cell);
|
|
}
|
|
|
|
|
|
CELL * makeCell(int type, UINT contents)
|
|
{
|
|
CELL * cell;
|
|
|
|
if(firstFreeCell == NULL) allocBlock();
|
|
cell = firstFreeCell;
|
|
firstFreeCell = cell->next;
|
|
++cellCount;
|
|
|
|
cell->type = type;
|
|
cell->next = nilCell;
|
|
cell->aux = (UINT)nilCell;
|
|
cell->contents = contents;
|
|
|
|
return(cell);
|
|
}
|
|
|
|
|
|
CELL * makeStringCell(char * contents, size_t size)
|
|
{
|
|
CELL * cell;
|
|
|
|
if(firstFreeCell == NULL) allocBlock();
|
|
cell = firstFreeCell;
|
|
firstFreeCell = cell->next;
|
|
++cellCount;
|
|
|
|
cell->type = CELL_STRING;
|
|
cell->next = nilCell;
|
|
cell->aux = (UINT)size + 1;
|
|
cell->contents = (UINT)contents;
|
|
|
|
return(cell);
|
|
}
|
|
|
|
CELL * copyCell(CELL * cell)
|
|
{
|
|
#ifdef ISO_C90
|
|
CELL * newCell;
|
|
CELL * list;
|
|
UINT len;
|
|
#endif
|
|
|
|
/* avoids copy if cell on resultStack */
|
|
if(cell == (CELL *)*(resultStackIdx))
|
|
{
|
|
if(cell != nilCell && cell != trueCell)
|
|
return(popResult());
|
|
}
|
|
|
|
#ifndef ISO_C90
|
|
CELL * newCell;
|
|
CELL * list;
|
|
UINT len;
|
|
#endif
|
|
|
|
if(firstFreeCell == NULL) allocBlock();
|
|
newCell = firstFreeCell;
|
|
firstFreeCell = newCell->next;
|
|
++cellCount;
|
|
|
|
newCell->type = cell->type;
|
|
newCell->next = nilCell;
|
|
newCell->aux = cell->aux;
|
|
newCell->contents = cell->contents;
|
|
|
|
if(isEnvelope(cell->type))
|
|
{
|
|
if(cell->type == CELL_ARRAY)
|
|
newCell->contents = (UINT)copyArray(cell);
|
|
else /* normal list expression */
|
|
{
|
|
if(cell->contents != (UINT)nilCell)
|
|
{
|
|
newCell->contents = (UINT)copyCell((CELL *)cell->contents);
|
|
list = (CELL *)newCell->contents;
|
|
cell = (CELL *)cell->contents;
|
|
while((cell = cell->next) != nilCell)
|
|
list = list->next = copyCell(cell);
|
|
newCell->aux = (UINT)list; /* last element optimization */
|
|
}
|
|
}
|
|
}
|
|
else if(cell->type == CELL_STRING)
|
|
{
|
|
newCell->contents = (UINT)allocMemory((UINT)cell->aux);
|
|
memcpy((void *)newCell->contents, (void*)cell->contents, (UINT)cell->aux);
|
|
}
|
|
else if(cell->type == CELL_DYN_SYMBOL)
|
|
{
|
|
len = strlen((char *)cell->contents);
|
|
newCell->contents = (UINT)allocMemory(len + 1);
|
|
memcpy((char *)newCell->contents, (char *)cell->contents, len + 1);
|
|
}
|
|
#ifdef BIGINT
|
|
else if(cell->type == CELL_BIGINT)
|
|
{
|
|
newCell->contents = (UINT)allocMemory((UINT)cell->aux * sizeof(int));
|
|
memcpy((void *)newCell->contents, (void*)cell->contents, (UINT)cell->aux * sizeof(int));
|
|
}
|
|
#endif
|
|
|
|
return(newCell);
|
|
}
|
|
|
|
|
|
/* this routine must be called with the list head
|
|
if copying with envelope call copyCell() instead */
|
|
CELL * copyList(CELL * cell)
|
|
{
|
|
#ifdef ISO_C90
|
|
CELL * firstCell;
|
|
CELL * newCell;
|
|
#endif
|
|
|
|
if(cell == nilCell)
|
|
{
|
|
lastCellCopied = nilCell;
|
|
return(cell);
|
|
}
|
|
|
|
#ifndef ISO_C90
|
|
CELL * firstCell;
|
|
CELL * newCell;
|
|
#endif
|
|
|
|
firstCell = newCell = copyCell(cell);
|
|
|
|
while((cell = cell->next) != nilCell)
|
|
newCell = newCell->next = copyCell(cell);
|
|
|
|
lastCellCopied = newCell;
|
|
return(firstCell);
|
|
}
|
|
|
|
|
|
/* for deleting lists _and_ cells */
|
|
void deleteList(CELL * cell)
|
|
{
|
|
CELL * next;
|
|
|
|
while(cell != nilCell)
|
|
{
|
|
if(isEnvelope(cell->type))
|
|
{
|
|
if(cell->type == CELL_ARRAY)
|
|
deleteArray(cell);
|
|
else
|
|
deleteList((CELL *)cell->contents);
|
|
}
|
|
|
|
else if(cell->type == CELL_STRING || cell->type == CELL_DYN_SYMBOL
|
|
#ifdef BIGINT
|
|
|| cell->type == CELL_BIGINT
|
|
#endif
|
|
)
|
|
freeMemory( (void *)cell->contents);
|
|
|
|
/* free cell changes in 10.6.3 */
|
|
if(cell == nilCell || cell == trueCell)
|
|
cell = cell->next;
|
|
else
|
|
{
|
|
next = cell->next;
|
|
cell->type = CELL_FREE;
|
|
cell->next = firstFreeCell;
|
|
firstFreeCell = cell;
|
|
--cellCount;
|
|
cell = next;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* --------------- cell / memory allocation and deallocation -------------
|
|
|
|
allthough (MAC_BLOCK + 1) are allocated only MAX_BLOCK cells
|
|
are used. The last cell only serves as a pointer to the next block
|
|
*/
|
|
|
|
CELL * cellMemory = NULL;/* start of cell memory */
|
|
CELL * cellBlock = NULL; /* the last block allocated */
|
|
|
|
void allocBlock()
|
|
{
|
|
int i;
|
|
|
|
if(cellCount > MAX_CELL_COUNT - MAX_BLOCK)
|
|
{
|
|
printErrorMessage(ERR_NOT_ENOUGH_MEMORY, NULL, 0);
|
|
exit(ERR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
if(cellMemory == NULL)
|
|
{
|
|
cellMemory = (CELL *)allocMemory((MAX_BLOCK + 1) * sizeof(CELL));
|
|
cellBlock = cellMemory;
|
|
}
|
|
else
|
|
{
|
|
(cellBlock + MAX_BLOCK)->next =
|
|
(CELL *)allocMemory((MAX_BLOCK + 1) * sizeof(CELL));
|
|
cellBlock = (cellBlock + MAX_BLOCK)->next;
|
|
}
|
|
|
|
for(i = 0; i < MAX_BLOCK; i++)
|
|
{
|
|
(cellBlock + i)->type = CELL_FREE;
|
|
(cellBlock + i)->next = (cellBlock + i + 1);
|
|
}
|
|
(cellBlock + MAX_BLOCK - 1)->next = NULL;
|
|
(cellBlock + MAX_BLOCK)->next = NULL;
|
|
firstFreeCell = cellBlock;
|
|
++ blockCount;
|
|
}
|
|
|
|
|
|
/* Return unused blocks to OS, this is normally only called under error
|
|
conditions but can also be forced issuing a (reset nil)
|
|
|
|
Older versions also did a complete cell mark and sweep. Now all
|
|
error conditons clean out allocated cells and memory before doing
|
|
the longjmp().
|
|
*/
|
|
|
|
/* not used, not tested
|
|
void freeAllCells()
|
|
{
|
|
CELL * blockPtr = cellMemory;
|
|
int i, j;
|
|
|
|
for(i = 0; i < blockCount; i++)
|
|
{
|
|
for(j = 0; j < MAX_BLOCK; j++)
|
|
{
|
|
if(*(UINT *)blockPtr != CELL_FREE)
|
|
{
|
|
deleteList(blockPtr);
|
|
}
|
|
blockPtr++;
|
|
}
|
|
blockPtr = blockPtr->next;
|
|
}
|
|
}
|
|
*/
|
|
|
|
void freeCellBlocks()
|
|
{
|
|
CELL * blockPtr;
|
|
CELL * lastBlockPtr = NULL;
|
|
CELL * lastFreeCell = NULL;
|
|
CELL * prevLastFreeCell;
|
|
CELL * prevCellBlock;
|
|
int i, freeCount;
|
|
|
|
cellBlock = blockPtr = cellMemory;
|
|
firstFreeCell = NULL;
|
|
while(blockPtr != NULL)
|
|
{
|
|
prevLastFreeCell = lastFreeCell;
|
|
prevCellBlock = cellBlock;
|
|
cellBlock = blockPtr;
|
|
for(i = freeCount = 0; i < MAX_BLOCK; i++)
|
|
{
|
|
if(*(UINT *)blockPtr == CELL_FREE)
|
|
{
|
|
if(firstFreeCell == NULL)
|
|
firstFreeCell = lastFreeCell = blockPtr;
|
|
else
|
|
{
|
|
lastFreeCell->next = blockPtr;
|
|
lastFreeCell = blockPtr;
|
|
}
|
|
freeCount++;
|
|
}
|
|
blockPtr++;
|
|
}
|
|
if(freeCount == MAX_BLOCK)
|
|
{
|
|
lastFreeCell = prevLastFreeCell;
|
|
cellBlock = prevCellBlock;
|
|
blockPtr = blockPtr->next;
|
|
freeMemory(lastBlockPtr->next);
|
|
--blockCount;
|
|
lastBlockPtr->next = blockPtr;
|
|
}
|
|
else
|
|
{
|
|
lastBlockPtr = blockPtr;
|
|
blockPtr = blockPtr->next;
|
|
}
|
|
}
|
|
lastFreeCell->next = NULL;
|
|
}
|
|
|
|
|
|
/* OS memory allocation */
|
|
|
|
void * allocMemory(size_t nbytes)
|
|
{
|
|
void * ptr;
|
|
|
|
if( (ptr = (void *)malloc(nbytes)) == NULL)
|
|
fatalError(ERR_NOT_ENOUGH_MEMORY, NULL, 0);
|
|
|
|
return(ptr);
|
|
}
|
|
|
|
void * callocMemory(size_t nbytes)
|
|
{
|
|
void * ptr;
|
|
|
|
if( (ptr = (void *)calloc(nbytes, 1)) == NULL)
|
|
fatalError(ERR_NOT_ENOUGH_MEMORY, NULL, 0);
|
|
|
|
return(ptr);
|
|
}
|
|
|
|
void * reallocMemory(void * prevPtr, UINT size)
|
|
{
|
|
void * ptr;
|
|
|
|
if( (ptr = realloc(prevPtr, size)) == NULL)
|
|
fatalError(ERR_NOT_ENOUGH_MEMORY, NULL, 0);
|
|
|
|
return(ptr);
|
|
}
|
|
|
|
/* -------------------------- I/O routines ------------------------------ */
|
|
|
|
UINT printDevice;
|
|
void prettyPrint(UINT device);
|
|
|
|
void varPrintf(UINT device, char * format, ...)
|
|
{
|
|
char * buffer;
|
|
va_list argptr;
|
|
|
|
va_start(argptr,format);
|
|
|
|
/* defined in nl-filesys.c if not in libc */
|
|
vasprintf(&buffer, format, argptr);
|
|
|
|
prettyPrintLength += strlen(buffer);
|
|
switch(device)
|
|
{
|
|
case OUT_NULL:
|
|
return;
|
|
|
|
case OUT_DEVICE:
|
|
if(printDevice != 0)
|
|
{
|
|
if(write(printDevice, buffer, strlen(buffer)) < 0)
|
|
fatalError(ERR_IO_ERROR, 0, 0);
|
|
break;
|
|
}
|
|
case OUT_CONSOLE:
|
|
#ifdef LIBRARY
|
|
if(!newlispLibConsoleFlag)
|
|
{
|
|
writeStreamStr(&libStrStream, buffer, 0);
|
|
freeMemory(buffer);
|
|
fflush(NULL);
|
|
return;
|
|
}
|
|
else
|
|
#endif
|
|
if(IOchannel == stdin)
|
|
{
|
|
printf("%s", buffer);
|
|
if(!isTTY) fflush(NULL);
|
|
}
|
|
else if(IOchannel != NULL)
|
|
fprintf(IOchannel, "%s", buffer);
|
|
break;
|
|
case OUT_LOG:
|
|
writeLog(buffer, 0);
|
|
break;
|
|
default:
|
|
writeStreamStr((STREAM *)device, buffer, 0);
|
|
break;
|
|
}
|
|
|
|
freeMemory(buffer);
|
|
|
|
va_end(argptr);
|
|
}
|
|
|
|
|
|
void printCell(CELL * cell, UINT printFlag, UINT device)
|
|
{
|
|
SYMBOL * sPtr;
|
|
SYMBOL * sp;
|
|
#ifdef BIGINT
|
|
char * ptr;
|
|
#endif
|
|
|
|
if(cell == debugPrintCell)
|
|
varPrintf(device, "%s", debugPreStr);
|
|
|
|
switch(cell->type)
|
|
{
|
|
case CELL_NIL:
|
|
varPrintf(device, "nil"); break;
|
|
|
|
case CELL_TRUE:
|
|
varPrintf(device, "true"); break;
|
|
|
|
case CELL_LONG:
|
|
varPrintf(device,"%"PRIdPTR, cell->contents); break;
|
|
#ifndef NEWLISP64
|
|
case CELL_INT64:
|
|
varPrintf(device,"%"PRId64, *(INT64 *)&cell->aux); break;
|
|
#endif /* NEWLISP64 */
|
|
#ifdef BIGINT
|
|
case CELL_BIGINT:
|
|
ptr = bigintToDigits((int *)cell->contents, cell->aux - 1, 48, NULL);
|
|
varPrintf(device, "%sL", ptr);
|
|
free(ptr);
|
|
break;
|
|
#endif
|
|
case CELL_FLOAT:
|
|
#ifndef NEWLISP64
|
|
varPrintf(device, prettyPrintFloat ,*(double *)&cell->aux);
|
|
#else
|
|
varPrintf(device, prettyPrintFloat ,*(double *)&cell->contents);
|
|
#endif
|
|
break;
|
|
|
|
case CELL_STRING:
|
|
if(printFlag)
|
|
printString((char *)cell->contents, device, cell->aux - 1);
|
|
else
|
|
varPrintf(device,"%s",cell->contents);
|
|
break;
|
|
|
|
case CELL_SYMBOL:
|
|
case CELL_CONTEXT:
|
|
sPtr = (SYMBOL *)cell->contents;
|
|
if(sPtr->context != currentContext
|
|
/* if not global or global overwritten in current context */
|
|
&& (!(sPtr->flags & SYMBOL_GLOBAL) || (lookupSymbol(sPtr->name, currentContext)))
|
|
&& (symbolType(sPtr) != CELL_CONTEXT ||
|
|
(SYMBOL *)((CELL*)sPtr->contents)->contents != sPtr)) /* context var */
|
|
{
|
|
varPrintf(device,"%s:%s", (char*)((SYMBOL*)sPtr->context)->name, sPtr->name);
|
|
break;
|
|
}
|
|
/* overwriting global in MAIN */
|
|
if(sPtr->context == currentContext
|
|
&& currentContext != mainContext
|
|
&& ((sp = lookupSymbol(sPtr->name, mainContext)) != NULL)
|
|
&& (sp->flags & SYMBOL_GLOBAL) )
|
|
{
|
|
varPrintf(device,"%s:%s", currentContext->name, sPtr->name);
|
|
break;
|
|
}
|
|
|
|
varPrintf(device,"%s",sPtr->name);
|
|
break;
|
|
|
|
case CELL_PRIMITIVE:
|
|
varPrintf(device,"%s@%lX", (char *)cell->aux, cell->contents);
|
|
break;
|
|
case CELL_IMPORT_CDECL:
|
|
case CELL_IMPORT_FFI:
|
|
#if defined(WINDOWS) || defined(CYGWIN)
|
|
case CELL_IMPORT_DLL:
|
|
#endif
|
|
|
|
#ifdef FFI
|
|
if(cell->type == CELL_IMPORT_FFI)
|
|
varPrintf(device,"%s@%lX", (char *)((FFIMPORT *)cell->aux)->name,
|
|
cell->contents);
|
|
else
|
|
varPrintf(device,"%s@%lX", (char *)cell->aux, cell->contents);
|
|
#else
|
|
varPrintf(device,"%s@%lX", (char *)cell->aux, cell->contents);
|
|
#endif
|
|
break;
|
|
|
|
case CELL_QUOTE:
|
|
varPrintf(device, "'");
|
|
prettyPrintFlags |= PRETTYPRINT_DOUBLE;
|
|
printCell((CELL *)cell->contents, printFlag, device);
|
|
break;
|
|
|
|
case CELL_EXPRESSION:
|
|
case CELL_LAMBDA:
|
|
case CELL_FEXPR:
|
|
printExpression(cell, device);
|
|
break;
|
|
|
|
case CELL_DYN_SYMBOL:
|
|
varPrintf(device, "%s:%s", ((SYMBOL*)cell->aux)->name, (char*)cell->contents);
|
|
break;
|
|
case CELL_ARRAY:
|
|
printArray(cell, device);
|
|
break;
|
|
|
|
default:
|
|
varPrintf(device,"?");
|
|
}
|
|
|
|
if(cell == debugPrintCell)
|
|
varPrintf(device, "%s", debugPostStr);
|
|
|
|
prettyPrintFlags &= ~PRETTYPRINT_DOUBLE;
|
|
}
|
|
|
|
|
|
void printString(char * str, UINT device, int size)
|
|
{
|
|
char chr;
|
|
|
|
if(size >= MAX_STRING)
|
|
{
|
|
varPrintf(device, "[text]");
|
|
while(size--) varPrintf(device, "%c", *str++);
|
|
varPrintf(device, "[/text]");
|
|
return;
|
|
}
|
|
|
|
varPrintf(device,"\"");
|
|
while(size--)
|
|
{
|
|
switch(chr = *str++)
|
|
{
|
|
case '\b': varPrintf(device,"\\b"); break;
|
|
case '\f': varPrintf(device,"\\f"); break;
|
|
case '\n': varPrintf(device,"\\n"); break;
|
|
case '\r': varPrintf(device,"\\r"); break;
|
|
case '\t': varPrintf(device,"\\t"); break;
|
|
case '\\': varPrintf(device,"\\\\"); break;
|
|
case '"': varPrintf(device,"\\%c",'"'); break;
|
|
default:
|
|
if((unsigned char)chr < 32 || (stringOutputRaw && (unsigned char)chr > 126))
|
|
varPrintf(device,"\\%03u", (unsigned char)chr);
|
|
else
|
|
varPrintf(device,"%c",chr); break;
|
|
}
|
|
}
|
|
varPrintf(device,"\"");
|
|
}
|
|
|
|
|
|
void printExpression(CELL * cell, UINT device)
|
|
{
|
|
CELL * item;
|
|
int i, pFlags;
|
|
|
|
item = (CELL *)cell->contents;
|
|
|
|
|
|
if(prettyPrintPars <= prettyPrintCurrent ||
|
|
prettyPrintLength > prettyPrintMaxLength)
|
|
prettyPrint(device);
|
|
|
|
if(cell->type == CELL_LAMBDA)
|
|
{
|
|
varPrintf(device, "(lambda ");
|
|
++prettyPrintPars;
|
|
}
|
|
else if(cell->type == CELL_FEXPR)
|
|
{
|
|
varPrintf(device, "(lambda-macro ");
|
|
++prettyPrintPars;
|
|
}
|
|
else
|
|
{
|
|
if(isSymbol(item->type))
|
|
{
|
|
if(item->type == CELL_SYMBOL)
|
|
pFlags = ((SYMBOL *)item->contents)->flags;
|
|
else
|
|
pFlags = 0;
|
|
|
|
if((pFlags & PRINT_TYPE_MASK) != 0)
|
|
{
|
|
prettyPrint(device);
|
|
varPrintf(device, "(");
|
|
++prettyPrintPars;
|
|
for(i = 0; i < (pFlags & PRINT_TYPE_MASK); i++)
|
|
{
|
|
if(item == nilCell)
|
|
{prettyPrintFlags |= PRETTYPRINT_DOUBLE; break;}
|
|
printCell(item, TRUE, device);
|
|
item = item->next;
|
|
if(item != nilCell) varPrintf(device," ");
|
|
else prettyPrintFlags |= PRETTYPRINT_DOUBLE;
|
|
}
|
|
prettyPrint(device);
|
|
}
|
|
else
|
|
{
|
|
varPrintf(device, "(");
|
|
++prettyPrintPars;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
varPrintf(device, "(");
|
|
++prettyPrintPars;
|
|
}
|
|
}
|
|
|
|
|
|
while(item != nilCell)
|
|
{
|
|
if(prettyPrintLength > prettyPrintMaxLength) prettyPrint(device);
|
|
printCell(item, TRUE, device);
|
|
item = item->next;
|
|
if(item != nilCell) varPrintf(device," ");
|
|
}
|
|
|
|
varPrintf(device,")");
|
|
--prettyPrintPars;
|
|
}
|
|
|
|
|
|
void prettyPrint(UINT device)
|
|
{
|
|
int i;
|
|
|
|
if(prettyPrintFlags) return;
|
|
|
|
if(prettyPrintPars > 0)
|
|
varPrintf(device, LINE_FEED);
|
|
|
|
for(i = 0; i < prettyPrintPars; i++)
|
|
varPrintf(device, "%s", prettyPrintTab);
|
|
|
|
prettyPrintLength = prettyPrintCurrent = prettyPrintPars;
|
|
prettyPrintFlags |= PRETTYPRINT_DOUBLE;
|
|
}
|
|
|
|
|
|
void printSymbol(SYMBOL * sPtr, UINT device)
|
|
{
|
|
CELL * cell;
|
|
CELL * list = NULL;
|
|
char * setStr;
|
|
size_t offset, len;
|
|
|
|
prettyPrintCurrent = prettyPrintPars = 1;
|
|
prettyPrintLength = 0;
|
|
prettyPrintFlags &= ~PRETTYPRINT_DOUBLE;
|
|
|
|
if(sPtr->flags & SYMBOL_PROTECTED)
|
|
setStr = "(constant ";
|
|
else
|
|
setStr = "(set ";
|
|
|
|
switch(symbolType(sPtr))
|
|
{
|
|
case CELL_PRIMITIVE:
|
|
case CELL_IMPORT_CDECL:
|
|
case CELL_IMPORT_FFI:
|
|
#if defined(WINDOWS) || defined(CYGWIN)
|
|
case CELL_IMPORT_DLL:
|
|
#endif
|
|
break;
|
|
case CELL_SYMBOL:
|
|
case CELL_DYN_SYMBOL:
|
|
varPrintf(device, "%s", setStr);
|
|
printSymbolNameExt(device, sPtr);
|
|
varPrintf(device,"'");
|
|
printCell((CELL *)sPtr->contents, TRUE, device);
|
|
varPrintf(device, ")");
|
|
break;
|
|
case CELL_ARRAY:
|
|
case CELL_EXPRESSION:
|
|
varPrintf(device, "%s", setStr);
|
|
printSymbolNameExt(device, sPtr);
|
|
cell = (CELL *)sPtr->contents;
|
|
|
|
if(symbolType(sPtr) == CELL_ARRAY)
|
|
{
|
|
varPrintf(device, "(array ");
|
|
printArrayDimensions(cell, device);
|
|
varPrintf(device, "(flat ");
|
|
list = cell = arrayList(cell, TRUE);
|
|
}
|
|
|
|
cell = (CELL *)cell->contents;
|
|
|
|
varPrintf(device,"'(");
|
|
prettyPrintPars = 2;
|
|
if(cell->type == CELL_EXPRESSION) prettyPrint(device);
|
|
while(cell != nilCell)
|
|
{
|
|
if(prettyPrintLength > prettyPrintMaxLength)
|
|
prettyPrint(device);
|
|
printCell(cell, TRUE, device);
|
|
cell = cell->next;
|
|
if(cell != nilCell) varPrintf(device, " ");
|
|
}
|
|
varPrintf(device, "))");
|
|
if(symbolType(sPtr) == CELL_ARRAY)
|
|
{
|
|
deleteList(list);
|
|
varPrintf(device ,"))");
|
|
}
|
|
break;
|
|
case CELL_LAMBDA:
|
|
case CELL_FEXPR:
|
|
if(isProtected(sPtr->flags))
|
|
{
|
|
varPrintf(device, "%s%s%s", LINE_FEED, LINE_FEED, setStr);
|
|
printSymbolNameExt(device, sPtr);
|
|
printExpression((CELL *)sPtr->contents, device);
|
|
varPrintf(device, ")");
|
|
}
|
|
else if (isGlobal(sPtr->flags))
|
|
{
|
|
printLambda(sPtr, device);
|
|
varPrintf(device, "%s%s", LINE_FEED, LINE_FEED);
|
|
printSymbolNameExt(device, sPtr);
|
|
}
|
|
else printLambda(sPtr, device);
|
|
break;
|
|
default:
|
|
varPrintf(device, "%s", setStr);
|
|
printSymbolNameExt(device, sPtr);
|
|
cell = (CELL *)sPtr->contents;
|
|
if(cell->type == CELL_STRING && cell->aux > MAX_STRING) /* size > 2047 */
|
|
{
|
|
varPrintf(device, "%s ", "(append ");
|
|
offset = 0;
|
|
while(offset < cell->aux - 1)
|
|
{
|
|
varPrintf(device, "%s ", LINE_FEED);
|
|
len = (cell->aux - 1 - offset);
|
|
len = len > 72 ? 72 : len;
|
|
printString((char *)(cell->contents + offset), device, len);
|
|
offset += len;
|
|
}
|
|
varPrintf(device, "))");
|
|
break;
|
|
}
|
|
printCell(cell, TRUE, device);
|
|
varPrintf(device, ")");
|
|
break;
|
|
}
|
|
|
|
varPrintf(device, "%s%s", LINE_FEED, LINE_FEED);
|
|
|
|
prettyPrintLength = prettyPrintPars = 0;
|
|
}
|
|
|
|
|
|
void printLambda(SYMBOL * sPtr, UINT device)
|
|
{
|
|
CELL * lambda;
|
|
CELL * cell;
|
|
|
|
lambda = (CELL *)sPtr->contents;
|
|
cell = (CELL *)lambda->contents;
|
|
if(cell->type == CELL_EXPRESSION)
|
|
cell = (CELL *)cell->contents;
|
|
|
|
if(!isLegalSymbol(sPtr->name))
|
|
{
|
|
varPrintf(device, "(set (sym ");
|
|
printString(sPtr->name, device, strlen(sPtr->name));
|
|
varPrintf(device, " %s) ", ((SYMBOL*)sPtr->context)->name);
|
|
printExpression((CELL *)sPtr->contents, device);
|
|
varPrintf(device, ")");
|
|
return;
|
|
}
|
|
|
|
if(symbolType(sPtr) == CELL_LAMBDA)
|
|
varPrintf(device, "(define (");
|
|
else
|
|
varPrintf(device, "(define-macro (");
|
|
prettyPrintPars += 2;
|
|
|
|
printSymbolName(device, sPtr);
|
|
varPrintf(device, " ");
|
|
|
|
while(cell != nilCell)
|
|
{
|
|
printCell(cell, TRUE, device);
|
|
cell = cell->next;
|
|
if(cell != nilCell) varPrintf(device, " ");
|
|
}
|
|
varPrintf(device, ")");
|
|
--prettyPrintPars;
|
|
prettyPrint(device);
|
|
|
|
cell = (CELL *)lambda->contents;
|
|
while((cell = cell->next) != nilCell)
|
|
{
|
|
if(prettyPrintLength > prettyPrintMaxLength) prettyPrint(device);
|
|
printCell(cell, TRUE, device);
|
|
if(!(cell->type & ENVELOPE_TYPE_MASK) && cell->next != nilCell) varPrintf(device, " ");
|
|
}
|
|
|
|
varPrintf(device, ")");
|
|
--prettyPrintPars;
|
|
}
|
|
|
|
|
|
void printSymbolName(UINT device, SYMBOL * sPtr)
|
|
{
|
|
SYMBOL * sp;
|
|
|
|
if(sPtr->context == currentContext)
|
|
{
|
|
if(*sPtr->name == *currentContext->name && strcmp(sPtr->name, currentContext->name) == 0)
|
|
varPrintf(device, "%s:%s", sPtr->name, sPtr->name);
|
|
|
|
else if(currentContext != mainContext
|
|
&& ((sp = lookupSymbol(sPtr->name, mainContext)) != NULL)
|
|
&& (sp->flags & SYMBOL_GLOBAL) )
|
|
varPrintf(device, "%s:%s", currentContext->name, sPtr->name);
|
|
else
|
|
varPrintf(device,"%s", sPtr->name);
|
|
}
|
|
else
|
|
varPrintf(device,"%s:%s",
|
|
(char *)((SYMBOL*)sPtr->context)->name, sPtr->name);
|
|
}
|
|
|
|
|
|
void printSymbolNameExt(UINT device, SYMBOL * sPtr)
|
|
{
|
|
if(isGlobal(sPtr->flags))
|
|
{
|
|
varPrintf(device, "(global '");
|
|
printSymbolName(device, sPtr);
|
|
if(symbolType(sPtr) == CELL_LAMBDA || symbolType(sPtr) == CELL_FEXPR)
|
|
varPrintf(device, ")");
|
|
else varPrintf(device, ") ");
|
|
}
|
|
else
|
|
{
|
|
if(!isLegalSymbol(sPtr->name))
|
|
{
|
|
varPrintf(device, " (sym ");
|
|
printString(sPtr->name, device, strlen(sPtr->name));
|
|
varPrintf(device, " MAIN:%s) ", ((SYMBOL*)sPtr->context)->name);
|
|
}
|
|
else
|
|
{
|
|
varPrintf(device, "'");
|
|
printSymbolName(device, sPtr);
|
|
}
|
|
varPrintf(device, " ");
|
|
}
|
|
}
|
|
|
|
|
|
CELL * p_prettyPrint(CELL * params)
|
|
{
|
|
CELL * result;
|
|
char * str;
|
|
size_t len;
|
|
|
|
if(params != nilCell)
|
|
params = getInteger(params, &prettyPrintMaxLength);
|
|
if(params != nilCell)
|
|
{
|
|
params = getStringSize(params, &str, &len, TRUE);
|
|
prettyPrintTab = allocMemory(len + 1);
|
|
memcpy(prettyPrintTab, str, len + 1);
|
|
}
|
|
if(params != nilCell)
|
|
{
|
|
getStringSize(params, &str, &len, TRUE);
|
|
prettyPrintFloat = allocMemory(len + 1);
|
|
memcpy(prettyPrintFloat, str, len + 1);
|
|
}
|
|
|
|
result = getCell(CELL_EXPRESSION);
|
|
addList(result, stuffInteger(prettyPrintMaxLength));
|
|
addList(result, stuffString(prettyPrintTab));
|
|
addList(result, stuffString(prettyPrintFloat));
|
|
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------- error handling --------------------------- */
|
|
|
|
char * errorMessage[] =
|
|
{
|
|
"", /* 0 */
|
|
"not enough memory", /* 1 */
|
|
"environment stack overflow", /* 2 */
|
|
"call or result stack overflow",/* 3 */
|
|
"problem accessing file", /* 4 */
|
|
"illegal token or expression", /* 5 */
|
|
"missing parenthesis", /* 6 */
|
|
"string token too long", /* 7 */
|
|
"missing argument", /* 8 */
|
|
"number or string expected", /* 9 */
|
|
"value expected", /* 10 */
|
|
"string expected", /* 11 */
|
|
"symbol expected", /* 12 */
|
|
"context expected", /* 13 */
|
|
"symbol or context expected", /* 14 */
|
|
"list expected", /* 15 */
|
|
"list or array expected", /* 15 */
|
|
"list or symbol expected", /* 17 */
|
|
"list or string expected", /* 18 */
|
|
"list or number expected", /* 19 */
|
|
"array expected", /* 20 */
|
|
"array, list or string expected", /* 21 */
|
|
"lambda expected", /* 22 */
|
|
"lambda-macro expected", /* 23 */
|
|
"invalid function", /* 24 */
|
|
"invalid lambda expression", /* 25 */
|
|
"invalid macro expression", /* 26 */
|
|
"invalid let parameter list", /* 27 */
|
|
"problem saving file", /* 28 */
|
|
"division by zero", /* 29 */
|
|
"matrix expected", /* 30 */
|
|
"wrong dimensions", /* 31 */
|
|
"matrix is singular", /* 32 */
|
|
"invalid option", /* 33 */
|
|
"throw without catch", /* 34 */
|
|
"problem loading library", /* 35 */
|
|
"import function not found", /* 36 */
|
|
"symbol is protected", /* 37 */
|
|
"number out of range", /* 38 */
|
|
"regular expression", /* 39 */
|
|
"end of text [/text] tag", /* 40 */
|
|
"mismatch in number of arguments", /* 41 */
|
|
"problem in format string", /* 42 */
|
|
"data type and format don't match", /* 43 */
|
|
"invalid parameter", /* 44 */
|
|
"invalid parameter: 0.0", /* 45 */
|
|
"invalid parameter: NaN", /* 46 */
|
|
"invalid UTF8 string", /* 47 */
|
|
"illegal parameter type", /* 48 */
|
|
"symbol not in MAIN context", /* 49 */
|
|
"symbol not in current context", /* 50 */
|
|
"target cannot be MAIN", /* 51 */
|
|
"invalid list index", /* 52 */
|
|
"array index out of bounds", /* 53 */
|
|
"invalid string index", /* 54 */
|
|
"nesting level to deep", /* 55 */
|
|
"list reference changed", /* 56 */
|
|
"invalid syntax", /* 57 */
|
|
"user error", /* 58 */
|
|
"user reset -", /* 59 */
|
|
"received SIGINT -", /* 60 */
|
|
"function is not reentrant", /* 61 */
|
|
"not allowed on local symbol", /* 62 */
|
|
"no reference found", /* 63 */
|
|
"list is empty", /* 64 */
|
|
"I/O error", /* 65 */
|
|
"no working directory found", /* 66 */
|
|
"invalid PID", /* 67 */
|
|
"cannot open socket pair", /* 68 */
|
|
"cannot fork process", /* 69 */
|
|
"no comm channel found", /* 70 */
|
|
"ffi preparation failed", /* 71 */
|
|
"invalid ffi type", /* 72 */
|
|
"ffi struct expected", /* 73 */
|
|
"bigint type not applicable", /* 74 */
|
|
"not a number or infinite", /* 75 */
|
|
"cannot convert NULL to string",/* 76 */
|
|
NULL
|
|
};
|
|
|
|
|
|
void errorMissingPar(STREAM * stream)
|
|
{
|
|
char str[48];
|
|
snprintf(str, 40, "...%.40s", ((char *)((stream->ptr - stream->buffer) > 40 ? stream->ptr - 40 : stream->buffer)));
|
|
errorProcExt2(ERR_MISSING_PAR, stuffString(str));
|
|
}
|
|
|
|
CELL * errorProcAll(int errorNumber, CELL * expr, int deleteFlag)
|
|
{
|
|
if(!traceFlag) fatalError(errorNumber, expr, deleteFlag);
|
|
printErrorMessage(errorNumber, expr, deleteFlag);
|
|
return(nilCell);
|
|
}
|
|
|
|
CELL * errorProc(int errorNumber)
|
|
{
|
|
return(errorProcAll(errorNumber, NULL, 0));
|
|
}
|
|
|
|
/* extended error info in expr */
|
|
CELL * errorProcExt(int errorNumber, CELL * expr)
|
|
{
|
|
return(errorProcAll(errorNumber, expr, 0));
|
|
}
|
|
|
|
/* extended error info in expr, which has to be discarded after printing */
|
|
CELL * errorProcExt2(int errorNumber, CELL * expr)
|
|
{
|
|
return(errorProcAll(errorNumber, expr, 1));
|
|
}
|
|
|
|
CELL * errorProcArgs(int errorNumber, CELL * expr)
|
|
{
|
|
if(expr == nilCell)
|
|
return(errorProcExt(ERR_MISSING_ARGUMENT, NULL));
|
|
|
|
return(errorProcExt(errorNumber, expr));
|
|
}
|
|
|
|
void fatalError(int errorNumber, CELL * expr, int deleteFlag)
|
|
{
|
|
printErrorMessage(errorNumber, expr, deleteFlag);
|
|
#ifndef LIBRARY
|
|
closeTrace();
|
|
#endif
|
|
longjmp(errorJump, errorReg);
|
|
}
|
|
|
|
void printErrorMessage(UINT errorNumber, CELL * expr, int deleteFlag)
|
|
{
|
|
CELL * lambdaFunc;
|
|
CELL * lambdaExpr;
|
|
UINT * stackIdx = lambdaStackIdx;
|
|
SYMBOL * context;
|
|
int i;
|
|
|
|
if(errorNumber == EXCEPTION_THROW)
|
|
errorNumber = ERR_THROW_WO_CATCH;
|
|
|
|
errorReg = errorNumber;
|
|
|
|
if(!errorNumber) return;
|
|
|
|
openStrStream(&errorStream, MAX_STRING, 1);
|
|
writeStreamStr(&errorStream, "ERR: ", 5);
|
|
writeStreamStr(&errorStream, errorMessage[errorReg], 0);
|
|
|
|
for(i = 0; primitive[i].name != NULL; i++)
|
|
{
|
|
if(evalFunc == primitive[i].function)
|
|
{
|
|
writeStreamStr(&errorStream, " in function ", 0);
|
|
writeStreamStr(&errorStream, primitive[i].name, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(expr != NULL)
|
|
{
|
|
writeStreamStr(&errorStream, " : ", 3);
|
|
printCell(expr, (errorNumber != ERR_USER_ERROR), (UINT)&errorStream);
|
|
if(deleteFlag) deleteList(expr);
|
|
}
|
|
|
|
while(stackIdx > lambdaStack)
|
|
{
|
|
lambdaExpr = (CELL *)*(--stackIdx);
|
|
lambdaFunc = (CELL *)lambdaExpr->contents;
|
|
if(lambdaFunc->type == CELL_SYMBOL)
|
|
{
|
|
writeStreamStr(&errorStream, LINE_FEED, 0);
|
|
writeStreamStr(&errorStream, "called from user function ", 0);
|
|
context = ((SYMBOL *)lambdaFunc->contents)->context;
|
|
if(context != mainContext)
|
|
{
|
|
writeStreamStr(&errorStream, context->name, 0);
|
|
writeStreamStr(&errorStream, ":", 0);
|
|
}
|
|
/* writeStreamStr(&errorStream, ((SYMBOL *)lambdaFunc->contents)->name, 0); */
|
|
printCell(lambdaExpr, (errorNumber != ERR_USER_ERROR), (UINT)&errorStream); /* 10.6.3 */
|
|
}
|
|
}
|
|
|
|
if(!(traceFlag & TRACE_SIGINT)) evalFunc = NULL;
|
|
parStackCounter = prettyPrintPars = 0;
|
|
|
|
if(evalCatchFlag && !((traceFlag & TRACE_SIGINT)
|
|
|| (traceFlag & TRACE_IN_DEBUG))) return;
|
|
|
|
if(errorEvent == nilSymbol)
|
|
{
|
|
if(errorNumber == ERR_SIGINT)
|
|
printf("%s", errorStream.buffer);
|
|
else
|
|
{
|
|
varPrintf(OUT_CONSOLE, "\n%.1024s\n", errorStream.buffer);
|
|
if(logTraffic == LOG_MORE) writeLog(errorStream.buffer, TRUE);
|
|
}
|
|
}
|
|
|
|
if(traceFlag & TRACE_PRINT_EVAL) tracePrint(errorStream.buffer, NULL);
|
|
}
|
|
|
|
|
|
/* --------------------------- load source file ------------------------- */
|
|
|
|
|
|
CELL * loadFile(char * fileName, UINT offset, int linkFlag, SYMBOL * context)
|
|
{
|
|
CELL * result;
|
|
STREAM stream;
|
|
int errNo, sourceLen;
|
|
jmp_buf errorJumpSave;
|
|
SYMBOL * contextSave;
|
|
#ifdef LOAD_DEBUG
|
|
int i;
|
|
#endif
|
|
|
|
contextSave = currentContext;
|
|
currentContext = context;
|
|
if(linkFlag)
|
|
sourceLen = *((int *) (linkOffset + 4));
|
|
else sourceLen = MAX_FILE_BUFFER;
|
|
|
|
#ifndef EMSCRIPTEN
|
|
if(my_strnicmp(fileName, "http://", 7) == 0)
|
|
{
|
|
result = getPutPostDeleteUrl(fileName, nilCell, HTTP_GET, CONNECT_TIMEOUT);
|
|
pushResult(result);
|
|
if(memcmp((char *)result->contents, "ERR:", 4) == 0)
|
|
return(errorProcExt2(ERR_ACCESSING_FILE, stuffString((char *)result->contents)));
|
|
result = copyCell(sysEvalString((char *)result->contents, context, nilCell, EVAL_STRING));
|
|
currentContext = contextSave;
|
|
return(result);
|
|
}
|
|
#endif
|
|
|
|
if(makeStreamFromFile(&stream, fileName, sourceLen + 4 * MAX_STRING, offset) == 0)
|
|
return(NULL);
|
|
|
|
memcpy(errorJumpSave, errorJump, sizeof(jmp_buf));
|
|
if((errNo = setjmp(errorJump)) != 0)
|
|
{
|
|
closeStrStream(&stream);
|
|
memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
|
|
currentContext = contextSave;
|
|
longjmp(errorJump, errNo);
|
|
}
|
|
|
|
#ifdef LOAD_DEBUG
|
|
for(i = 0; i<recursionCount; i++) printf(" ");
|
|
printf("load: %s\n", fileName);
|
|
#endif
|
|
|
|
result = evaluateStream(&stream, 0, TRUE);
|
|
currentContext = contextSave;
|
|
|
|
#ifdef LOAD_DEBUG
|
|
for(i = 0; i<recursionCount; i++) printf(" ");
|
|
printf("finish load: %s\n", fileName);
|
|
#endif
|
|
|
|
memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
|
|
closeStrStream(&stream);
|
|
return(result);
|
|
}
|
|
|
|
|
|
void linkSource(char * pathname, char * source, char * target)
|
|
{
|
|
int sourceLen;
|
|
char * buffer;
|
|
int size, offset = 0;
|
|
char * ptr;
|
|
|
|
#ifdef WINDOWS
|
|
/* gets full path of currently executing newlisp.exe */
|
|
pathname = win_getExePath(alloca(PATH_MAX));
|
|
#else /* Unix */
|
|
if(strchr(pathname, '/') == NULL)
|
|
pathname = which(pathname, alloca(PATH_MAX));
|
|
#endif
|
|
|
|
size = readFile(pathname, &buffer);
|
|
sourceLen = (size_t)fileSize(source);
|
|
|
|
if(errno)
|
|
{
|
|
printf("%s\n", strerror(errno));
|
|
exit(errno);
|
|
}
|
|
|
|
ptr = buffer;
|
|
|
|
if(strncmp(linkOffset + 4, "@@@@", 4) != 0) return; /* already linked */
|
|
do {
|
|
offset = searchBuffer(ptr, size - (ptr - buffer) , "@@@@", 4, 1);
|
|
ptr = ptr + offset + 4;
|
|
} while (strncmp(ptr - 8, "&&&&", 4) != 0); /* the linkOffset */
|
|
|
|
offset = (ptr - buffer - 8);
|
|
*(int *)(buffer + offset) = (int)size;
|
|
*(int *)(buffer + offset + 4) = (int)sourceLen;
|
|
writeFile(target, buffer, size, "w");
|
|
readFile(source, &buffer);
|
|
writeFile(target, buffer, sourceLen, "a");
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
/* -------------------------- parse / compile -----------------------------
|
|
|
|
Takes source in a string stream and and envelope cell and compiles
|
|
newLISP source into an internal LISP cell structure tree. The tree
|
|
can be decompiled to source at any time and is processed by the
|
|
evaluateExpression() function.
|
|
|
|
*/
|
|
int references(SYMBOL *, int);
|
|
int compileExpression(STREAM * stream, CELL * cell)
|
|
{
|
|
char token[MAX_STRING + 4];
|
|
double floatNumber;
|
|
CELL * newCell;
|
|
CELL * contextCell;
|
|
CELL * preCell;
|
|
SYMBOL * contextPtr;
|
|
SYMBOL * sPtr;
|
|
int listFlag, tklen;
|
|
char * lastPtr;
|
|
int errnoSave;
|
|
INT64 number;
|
|
|
|
listFlag = TRUE; /* cell is either quote or list envelope */
|
|
|
|
GETNEXT:
|
|
lastPtr = stream->ptr;
|
|
switch(getToken(stream, token, &tklen))
|
|
{
|
|
case TKN_ERROR:
|
|
errorProcExt2(ERR_EXPRESSION, stuffStringN(lastPtr,
|
|
(strlen(lastPtr) < 60) ? strlen(lastPtr) : 60));
|
|
return(FALSE);
|
|
|
|
case TKN_EMPTY:
|
|
if(parStackCounter != 0) errorMissingPar(stream);
|
|
return(FALSE);
|
|
|
|
case TKN_CHARACTER:
|
|
newCell = stuffInteger((UINT)token[0]);
|
|
break;
|
|
|
|
case TKN_HEX:
|
|
newCell = stuffInteger64((INT64)strtoull(token,NULL,0));
|
|
break;
|
|
|
|
case TKN_BINARY:
|
|
newCell = stuffInteger64((INT64)strtoull(&token[2],NULL,2));
|
|
break;
|
|
|
|
case TKN_OCTAL:
|
|
newCell = stuffInteger64(strtoll(token,NULL,0));
|
|
break;
|
|
|
|
case TKN_DECIMAL:
|
|
errnoSave = errno;
|
|
errno = 0;
|
|
#ifdef BIGINT
|
|
if(*(token + tklen - 1) == 'L')
|
|
{
|
|
newCell = stuffBigint(token);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#ifndef NEWLISP64
|
|
number = (INT64)strtoll(token, NULL, 0);
|
|
#else
|
|
number = strtoll(token, NULL, 0);
|
|
#endif
|
|
|
|
#ifdef BIGINT
|
|
if(errno == ERANGE)
|
|
{
|
|
newCell = stuffBigint(token);
|
|
errno = errnoSave;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
newCell = stuffInteger64(number);
|
|
errno = errnoSave;
|
|
break;
|
|
|
|
case TKN_FLOAT:
|
|
floatNumber = (double)atof(token);
|
|
newCell = stuffFloat(floatNumber);
|
|
break;
|
|
|
|
case TKN_STRING:
|
|
newCell = stuffStringN(token, tklen);
|
|
break;
|
|
|
|
case TKN_SYMBOL:
|
|
if(strcmp(token, "lambda") == 0 || strcmp(token, "fn") == 0)
|
|
{
|
|
if(cell->type != CELL_EXPRESSION)
|
|
{
|
|
errorProcExt2(ERR_INVALID_LAMBDA, stuffString(lastPtr));
|
|
return(FALSE);
|
|
}
|
|
cell->type = CELL_LAMBDA;
|
|
cell->aux = (UINT)nilCell;
|
|
goto GETNEXT;
|
|
}
|
|
else if(strcmp(token, "lambda-macro") == 0 || strcmp(token, "fn-macro") == 0)
|
|
{
|
|
if(cell->type != CELL_EXPRESSION)
|
|
{
|
|
errorProcExt2(ERR_INVALID_LAMBDA, stuffString(lastPtr));
|
|
return(FALSE);
|
|
}
|
|
cell->type = CELL_FEXPR;
|
|
cell->aux = (UINT)nilCell;
|
|
goto GETNEXT;
|
|
}
|
|
else if(strncmp(token, "[text]", 6) == 0)
|
|
{
|
|
newCell = makeCell(CELL_STRING, (UINT)readStreamText(stream, &tklen));
|
|
if(newCell->contents == 0)
|
|
{
|
|
deleteList(newCell);
|
|
errorProc(ERR_TEXT_END_TAG);
|
|
}
|
|
newCell->aux = tklen + 1;
|
|
break;
|
|
}
|
|
newCell = getCell(CELL_SYMBOL);
|
|
if(*token == '$')
|
|
{
|
|
newCell->contents = (UINT)translateCreateSymbol(
|
|
token, CELL_NIL, mainContext, TRUE);
|
|
((SYMBOL *)newCell->contents)->flags |= SYMBOL_GLOBAL;
|
|
}
|
|
else
|
|
newCell->contents = (UINT)translateCreateSymbol(
|
|
token, CELL_NIL, currentContext, 0);
|
|
break;
|
|
|
|
case TKN_CONTEXT:
|
|
contextPtr = NULL;
|
|
if(currentContext != mainContext)
|
|
{
|
|
if(strcmp(currentContext->name, token) == 0)
|
|
contextPtr = currentContext;
|
|
else
|
|
contextPtr = lookupSymbol(token, currentContext);
|
|
}
|
|
|
|
if(contextPtr == NULL)
|
|
{
|
|
contextPtr = translateCreateSymbol(
|
|
token, CELL_CONTEXT, mainContext, TRUE);
|
|
}
|
|
|
|
contextCell = (CELL *)contextPtr->contents;
|
|
if(getToken(stream, token, &tklen) != TKN_SYMBOL)
|
|
errorProcExt2(ERR_SYMBOL_EXPECTED, stuffString(lastPtr));
|
|
|
|
/* context does not exist */
|
|
if(contextCell->type != CELL_CONTEXT
|
|
|| contextPtr != (SYMBOL*)contextCell->contents)
|
|
{
|
|
newCell = getCell(CELL_DYN_SYMBOL);
|
|
newCell->aux = (UINT)contextPtr;
|
|
newCell->contents = (UINT)allocMemory(tklen + 1);
|
|
strncpy((char *)newCell->contents, token, tklen + 1);
|
|
break;
|
|
}
|
|
|
|
/* context exists make a symbol for it */
|
|
newCell = getCell(CELL_SYMBOL);
|
|
newCell->contents = (UINT)translateCreateSymbol(
|
|
token, CELL_NIL, contextPtr, TRUE);
|
|
break;
|
|
|
|
case TKN_QUOTE:
|
|
newCell = getCell(CELL_QUOTE);
|
|
compileExpression(stream, newCell);
|
|
break;
|
|
|
|
case TKN_LEFT_PAR:
|
|
++parStackCounter;
|
|
newCell = getCell(CELL_EXPRESSION);
|
|
compileExpression(stream, newCell);
|
|
if(((CELL *)newCell->contents)->type == CELL_SYMBOL)
|
|
{
|
|
sPtr = (SYMBOL *)((CELL *)newCell->contents)->contents;
|
|
/* macro expansion */
|
|
if(sPtr->flags & SYMBOL_MACRO)
|
|
{
|
|
preCell = copyCell(evaluateExpression(newCell));
|
|
deleteList(newCell);
|
|
newCell = preCell;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TKN_RIGHT_PAR:
|
|
if(parStackCounter == 0) errorMissingPar(stream);
|
|
--parStackCounter;
|
|
cell->next = nilCell;
|
|
return(TRUE);
|
|
|
|
default:
|
|
errorProcExt2(ERR_EXPRESSION, stuffString(lastPtr));
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
linkCell(cell, newCell, listFlag);
|
|
|
|
if(cell->type == CELL_QUOTE && listFlag == TRUE)
|
|
return(TRUE);
|
|
|
|
listFlag = FALSE;
|
|
cell = newCell;
|
|
|
|
if(parStackCounter != 0)
|
|
{
|
|
if(*(stream->ptr) != 0) goto GETNEXT;
|
|
else errorMissingPar(stream);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
void linkCell(CELL * left, CELL * right, int linkFlag)
|
|
{
|
|
if(linkFlag == 0) left->next = right;
|
|
else left->contents = (UINT)right;
|
|
}
|
|
|
|
int getToken(STREAM * stream, char * token, int * ptr_len)
|
|
{
|
|
char *tkn;
|
|
char chr;
|
|
int tknLen;
|
|
#ifdef SUPPORT_UTF8
|
|
int len;
|
|
#endif
|
|
int floatFlag;
|
|
int bracketBalance;
|
|
char buff[8];
|
|
|
|
tkn = token;
|
|
tknLen = floatFlag = 0;
|
|
*tkn = 0;
|
|
|
|
STRIP:
|
|
if(stream->ptr > (stream->buffer + stream->size - 4 * MAX_STRING))
|
|
{
|
|
if(stream->handle == 0)
|
|
{
|
|
/* coming from commmand line or p_evalString */
|
|
stream->buffer = stream->ptr;
|
|
}
|
|
else
|
|
{
|
|
stream->position += (stream->ptr - stream->buffer);
|
|
lseek((int)stream->handle, stream->position, SEEK_SET);
|
|
memset(stream->buffer, 0, stream->size + 1);
|
|
|
|
if(read(stream->handle, stream->buffer, stream->size) > 0)
|
|
stream->ptr = stream->buffer;
|
|
else
|
|
{
|
|
*stream->ptr = 0;
|
|
return(TKN_EMPTY);
|
|
}
|
|
}
|
|
}
|
|
|
|
while((unsigned char)*stream->ptr <= ' ' && (unsigned char)*stream->ptr != 0)
|
|
++stream->ptr;
|
|
|
|
if(*stream->ptr == 0) return(TKN_EMPTY);
|
|
|
|
/* check for comments */
|
|
if(*stream->ptr == ';' || *stream->ptr == '#')
|
|
{
|
|
stream->ptr++;
|
|
for(;;)
|
|
{
|
|
if(*stream->ptr == 0) return(TKN_EMPTY);
|
|
if(*stream->ptr == '\n' || *stream->ptr == '\r')
|
|
break;
|
|
stream->ptr++;
|
|
}
|
|
stream->ptr++;
|
|
goto STRIP;
|
|
}
|
|
|
|
|
|
if( *stream->ptr == '-' || *stream->ptr == '+')
|
|
{
|
|
if(isDigit((unsigned char)*(stream->ptr + 1))
|
|
|| *(stream->ptr + 1) == lc_decimal_point ) /* added 10.4.8 to allow -.9 */
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
}
|
|
|
|
|
|
if(isDigit((unsigned char)*stream->ptr) ||
|
|
(*stream->ptr == lc_decimal_point &&
|
|
isDigit((unsigned char)*(stream->ptr + 1))))
|
|
{
|
|
if(*stream->ptr == '0' && isDigit((unsigned char)*(stream->ptr + 1)))
|
|
{
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
while(*stream->ptr < '8' && *stream->ptr >= '0' && *stream->ptr != 0)
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
*tkn = 0;
|
|
return(TKN_OCTAL);
|
|
}
|
|
|
|
while(isDigit((unsigned char)*stream->ptr) && tknLen < MAX_DIGITS)
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
|
|
if(toupper(*stream->ptr) == 'X' && token[0] == '0')
|
|
{
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
while(isxdigit((unsigned char)*stream->ptr) && tknLen < MAX_HEX_NO)
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
*tkn = 0;
|
|
return(TKN_HEX);
|
|
}
|
|
|
|
if(toupper(*stream->ptr) == 'B' && token[0] == '0')
|
|
{
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
while((*stream->ptr == '0' || *stream->ptr == '1') && tknLen < MAX_BIN_NO)
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
*tkn = 0;
|
|
return(TKN_BINARY);
|
|
}
|
|
|
|
if(*stream->ptr == lc_decimal_point)
|
|
{
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
while(isDigit((unsigned char)*stream->ptr) && tknLen < MAX_DECIMALS)
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
floatFlag = TRUE;
|
|
}
|
|
else if(toupper(*stream->ptr) != 'E')
|
|
{
|
|
if(*stream->ptr == 'L') *(tkn++) = *(stream->ptr++), tknLen++;
|
|
*tkn = 0;
|
|
*ptr_len = tknLen;
|
|
return(TKN_DECIMAL);
|
|
}
|
|
|
|
if(toupper(*stream->ptr) == 'E')
|
|
{
|
|
if(isDigit((unsigned char)*(stream->ptr+2))
|
|
&& ( *(stream->ptr+1) == '-' || *(stream->ptr+1) == '+') )
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
if(isDigit((unsigned char)*(stream->ptr+1)))
|
|
{
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
while(isDigit((unsigned char)*stream->ptr) && tknLen < MAX_SYMBOL)
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
}
|
|
else
|
|
{
|
|
*tkn = 0;
|
|
if(floatFlag == TRUE) return(TKN_FLOAT);
|
|
else
|
|
{
|
|
*ptr_len = tknLen;
|
|
return(TKN_DECIMAL);
|
|
}
|
|
}
|
|
}
|
|
*tkn = 0;
|
|
return(TKN_FLOAT);
|
|
}
|
|
else
|
|
{
|
|
chr = *stream->ptr;
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
switch(chr)
|
|
{
|
|
case '"':
|
|
--tkn; --tknLen;
|
|
while(*stream->ptr != '"' && *stream->ptr != 0
|
|
&& tknLen < MAX_STRING)
|
|
{
|
|
if(*stream->ptr == '\\')
|
|
{
|
|
stream->ptr++;
|
|
if(isDigit((unsigned char)*stream->ptr) &&
|
|
isDigit((unsigned char)*(stream->ptr+1)) &&
|
|
isDigit((unsigned char)*(stream->ptr+2)))
|
|
{
|
|
memcpy(buff, stream->ptr, 3);
|
|
buff[3] = 0;
|
|
*(tkn++) = atoi(buff);
|
|
tknLen++;
|
|
stream->ptr += 3;
|
|
continue;
|
|
}
|
|
|
|
switch(*stream->ptr)
|
|
{
|
|
case 0:
|
|
goto SRING_TO_LONG_ERROR;
|
|
break;
|
|
case 'n':
|
|
*(tkn++) = '\n'; break;
|
|
case '\\':
|
|
*(tkn++) = '\\'; break;
|
|
case 'b':
|
|
*(tkn++) = '\b'; break;
|
|
case 'f':
|
|
*(tkn++) = '\f'; break;
|
|
case 'r':
|
|
*(tkn++) = '\r'; break;
|
|
case 't':
|
|
*(tkn++) = '\t'; break;
|
|
case '"':
|
|
*(tkn++) = '"'; break;
|
|
case 'x':
|
|
if(isxdigit((unsigned char)*(stream->ptr + 1)) &&
|
|
isxdigit((unsigned char)*(stream->ptr + 2)))
|
|
{
|
|
buff[0] = '0';
|
|
buff[1] = (unsigned char)*(stream->ptr + 1);
|
|
buff[2] = (unsigned char)*(stream->ptr + 2);
|
|
buff[3] = 0;
|
|
*(tkn++) = strtol(buff, NULL, 16);
|
|
stream->ptr += 2;
|
|
break;
|
|
}
|
|
case 'u':
|
|
if(isxdigit((unsigned char)*(stream->ptr + 1)) &&
|
|
isxdigit((unsigned char)*(stream->ptr + 2)) &&
|
|
isxdigit((unsigned char)*(stream->ptr + 3)) &&
|
|
isxdigit((unsigned char)*(stream->ptr + 4)))
|
|
{
|
|
#ifdef SUPPORT_UTF8
|
|
buff[0] = '0';
|
|
buff[1] = 'x';
|
|
memcpy(buff + 2, stream->ptr + 1, 4);
|
|
buff[6] = 0;
|
|
len = wchar_utf8(strtol(buff, NULL, 16), tkn);
|
|
stream->ptr += 4;
|
|
tkn += len;
|
|
tknLen += len -1;
|
|
#else
|
|
*(tkn++) = '\\';
|
|
memcpy(tkn, stream->ptr, 5);
|
|
tknLen = 5;
|
|
tkn += 5;
|
|
stream->ptr += 4;
|
|
#endif
|
|
break;
|
|
}
|
|
default:
|
|
*(tkn++) = *stream->ptr;
|
|
}
|
|
stream->ptr++;
|
|
tknLen++;
|
|
}
|
|
else *(tkn++) = *(stream->ptr++), tknLen++;
|
|
}
|
|
if(*stream->ptr == '\"')
|
|
{
|
|
*tkn = 0;
|
|
stream->ptr++;
|
|
*ptr_len = tknLen;
|
|
return(TKN_STRING);
|
|
}
|
|
else
|
|
{
|
|
goto SRING_TO_LONG_ERROR;
|
|
}
|
|
break;
|
|
|
|
case '\'':
|
|
case '(':
|
|
case ')':
|
|
*tkn = 0;
|
|
return(chr);
|
|
case '{':
|
|
--tkn; --tknLen;
|
|
bracketBalance = 1;
|
|
while(*stream->ptr != 0 && tknLen < MAX_STRING)
|
|
{
|
|
if(*stream->ptr == '{') ++bracketBalance;
|
|
if(*stream->ptr == '}') --bracketBalance;
|
|
if(bracketBalance == 0) break;
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
}
|
|
if(*stream->ptr == '}')
|
|
{
|
|
*tkn = 0;
|
|
stream->ptr++;
|
|
*ptr_len = tknLen;
|
|
return(TKN_STRING);
|
|
}
|
|
else
|
|
{
|
|
goto SRING_TO_LONG_ERROR;
|
|
}
|
|
break;
|
|
|
|
|
|
case ',':
|
|
case ':':
|
|
*tkn = 0;
|
|
*ptr_len = tknLen;
|
|
return(TKN_SYMBOL);
|
|
|
|
case '[':
|
|
while( tknLen < MAX_SYMBOL && *stream->ptr != 0 && *stream->ptr != ']')
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
if(*stream->ptr == 0) return(TKN_ERROR);
|
|
*tkn++ = ']';
|
|
*tkn = 0;
|
|
*ptr_len = ++tknLen;
|
|
stream->ptr++;
|
|
|
|
return(TKN_SYMBOL);
|
|
|
|
default:
|
|
while( tknLen < MAX_SYMBOL
|
|
&& (unsigned char)*stream->ptr > ' '
|
|
&& *stream->ptr != '"' && *stream->ptr != '\''
|
|
&& *stream->ptr != '(' && *stream->ptr != ')'
|
|
&& *stream->ptr != ':' && *stream->ptr != ','
|
|
&& *stream->ptr != 0)
|
|
*(tkn++) = *(stream->ptr++), tknLen++;
|
|
*tkn = 0;
|
|
*ptr_len = tknLen;
|
|
if(*stream->ptr == ':')
|
|
{
|
|
stream->ptr++;
|
|
return(TKN_CONTEXT);
|
|
}
|
|
return(TKN_SYMBOL);
|
|
}
|
|
}
|
|
*tkn=0;
|
|
return(TKN_ERROR);
|
|
|
|
SRING_TO_LONG_ERROR:
|
|
*tkn = 0;
|
|
errorProcExt2(ERR_STRING_TOO_LONG,
|
|
stuffStringN(token, strlen(token) < 40 ? strlen(token) : 40));
|
|
return(TKN_ERROR);
|
|
}
|
|
|
|
/* -------------------------- utilities ------------------------------------ */
|
|
|
|
size_t listlen(CELL * listHead)
|
|
{
|
|
size_t len = 0;
|
|
|
|
while(listHead != nilCell)
|
|
{
|
|
len++;
|
|
listHead = listHead->next;
|
|
}
|
|
|
|
return(len);
|
|
}
|
|
|
|
/* -------------------------- functions to get parameters ------------------ */
|
|
|
|
int getFlag(CELL * params)
|
|
{
|
|
params = evaluateExpression(params);
|
|
return(!isNil(params));
|
|
}
|
|
|
|
CELL * getInteger(CELL * params, UINT * number)
|
|
{
|
|
CELL * cell;
|
|
#ifdef BIGINT
|
|
INT64 longNum;
|
|
#endif
|
|
|
|
cell = evaluateExpression(params);
|
|
|
|
#ifndef NEWLISP64
|
|
if(cell->type == CELL_INT64)
|
|
{
|
|
if(*(INT64 *)&cell->aux > 0xFFFFFFFF) *number = 0xFFFFFFFF;
|
|
else if(*(INT64 *)&cell->aux < INT32_MIN_AS_INT64) *number = 0x80000000;
|
|
else *number = *(INT64 *)&cell->aux;
|
|
}
|
|
else if(cell->type == CELL_LONG)
|
|
*number = cell->contents;
|
|
else if(cell->type == CELL_FLOAT)
|
|
{
|
|
#ifdef WINDOWS
|
|
if(isnan(*(double *)&cell->aux) || !_finite(*(double *)&cell->aux)) *number = 0;
|
|
#else
|
|
if(isnan(*(double *)&cell->aux)) *number = 0;
|
|
#endif
|
|
else if(*(double *)&cell->aux > 4294967295.0) *number = 0xFFFFFFFF;
|
|
else if(*(double *)&cell->aux < -2147483648.0) *number = 0x80000000;
|
|
else *number = *(double *)&cell->aux;
|
|
}
|
|
#else /* NEWLISP64 */
|
|
if(cell->type == CELL_LONG)
|
|
*number = cell->contents;
|
|
else if(cell->type == CELL_FLOAT)
|
|
{
|
|
if(isnan(*(double *)&cell->contents)) *number = 0;
|
|
else if(*(double *)&cell->contents > 9223372036854775807.0) *number = 0x7FFFFFFFFFFFFFFFLL;
|
|
else if(*(double *)&cell->contents < -9223372036854775808.0) *number = 0x8000000000000000LL;
|
|
else *number = *(double *)&cell->contents;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
#ifdef BIGINT
|
|
if(cell->type == CELL_BIGINT)
|
|
{
|
|
longNum = bigintToInt64(cell);
|
|
*number = longNum;
|
|
#ifndef NEWLISP64
|
|
if(longNum > 2147483647LL || longNum < -2147483648LL)
|
|
return(errorProcExt(ERR_NUMBER_OUT_OF_RANGE, cell));
|
|
#endif
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
*number = 0;
|
|
return(errorProcArgs(ERR_NUMBER_EXPECTED, params));
|
|
}
|
|
}
|
|
|
|
return(params->next);
|
|
}
|
|
|
|
#ifndef NEWLISP64
|
|
CELL * getInteger64Ext(CELL * params, INT64 * number, int evalFlag)
|
|
{
|
|
CELL * cell;
|
|
|
|
if(evalFlag)
|
|
cell = evaluateExpression(params);
|
|
else
|
|
cell = params;
|
|
|
|
if(cell->type == CELL_INT64)
|
|
*number = *(INT64 *)&cell->aux;
|
|
else if(cell->type == CELL_LONG)
|
|
*number = (int)cell->contents;
|
|
else if(cell->type == CELL_FLOAT)
|
|
{
|
|
if(isnan(*(double *)&cell->aux)) *number = 0;
|
|
else if(*(double *)&cell->aux > 9223372036854775807.0) *number = 0x7FFFFFFFFFFFFFFFLL;
|
|
else if(*(double *)&cell->aux < -9223372036854775808.0) *number = 0x8000000000000000LL;
|
|
else *number = *(double *)&cell->aux;
|
|
}
|
|
else /* check for bigint if size * != NULL, then return bigint address in number */
|
|
{
|
|
#ifdef BIGINT
|
|
if(cell->type == CELL_BIGINT)
|
|
*number = bigintToInt64(cell);
|
|
else
|
|
#endif
|
|
{
|
|
*number = 0;
|
|
return(errorProcExt(ERR_NUMBER_EXPECTED, params));
|
|
}
|
|
}
|
|
|
|
return(params->next);
|
|
}
|
|
|
|
#else /* NEWLISP64 */
|
|
CELL * getInteger64Ext(CELL * params, INT64 * number, int evalFlag)
|
|
{
|
|
CELL * cell;
|
|
|
|
if(evalFlag)
|
|
cell = evaluateExpression(params);
|
|
else
|
|
cell = params;
|
|
|
|
if(cell->type == CELL_LONG)
|
|
*number = cell->contents;
|
|
else if(cell->type == CELL_FLOAT)
|
|
{
|
|
if(isnan(*(double *)&cell->contents)) *number = 0;
|
|
else if(*(double *)&cell->contents > 9223372036854775807.0) *number = 0x7FFFFFFFFFFFFFFFLL;
|
|
else if(*(double *)&cell->contents < -9223372036854775808.0) *number = 0x8000000000000000LL;
|
|
else *number = *(double *)&cell->contents;
|
|
}
|
|
else
|
|
{
|
|
#ifdef BIGINT
|
|
if(cell->type == CELL_BIGINT)
|
|
*number = bigintToInt64(cell);
|
|
else
|
|
#endif
|
|
{
|
|
*number = 0;
|
|
return(errorProcArgs(ERR_NUMBER_EXPECTED, params));
|
|
}
|
|
}
|
|
|
|
return(params->next);
|
|
}
|
|
#endif
|
|
|
|
CELL * getIntegerExt(CELL * params, UINT * number, int evalFlag)
|
|
{
|
|
CELL * cell;
|
|
#ifdef BIGINT
|
|
INT64 longNum;
|
|
#endif
|
|
|
|
if(evalFlag)
|
|
cell = evaluateExpression(params);
|
|
else cell = params;
|
|
|
|
#ifndef NEWLISP64
|
|
if(cell->type == CELL_INT64)
|
|
{
|
|
if(*(INT64 *)&cell->aux > 0xFFFFFFFF) *number = 0xFFFFFFFF;
|
|
else if(*(INT64 *)&cell->aux < INT32_MIN_AS_INT64) *number = 0x80000000;
|
|
else *number = *(INT64 *)&cell->aux;
|
|
}
|
|
else if(cell->type == CELL_LONG)
|
|
*number = cell->contents;
|
|
else if(cell->type == CELL_FLOAT)
|
|
{
|
|
#ifdef WINDOWS
|
|
if(isnan(*(double *)&cell->aux) || !_finite(*(double *)&cell->aux)) *number = 0;
|
|
#else
|
|
if(isnan(*(double *)&cell->aux)) *number = 0;
|
|
#endif
|
|
else if(*(double *)&cell->aux > 4294967295.0) *number = 0xFFFFFFFF;
|
|
else if(*(double *)&cell->aux < -2147483648.0) *number = 0x80000000;
|
|
else *number = *(double *)&cell->aux;
|
|
}
|
|
#else /* NEWLISP64 */
|
|
if(cell->type == CELL_LONG)
|
|
*number = cell->contents;
|
|
else if(cell->type == CELL_FLOAT)
|
|
{
|
|
if(isnan(*(double *)&cell->contents)) *number = 0;
|
|
else if(*(double *)&cell->contents > 9223372036854775807.0) *number = 0x7FFFFFFFFFFFFFFFLL;
|
|
else if(*(double *)&cell->contents < -9223372036854775808.0) *number = 0x8000000000000000LL;
|
|
else *number = *(double *)&cell->contents;
|
|
}
|
|
#endif
|
|
else /* if BIGNUM type throw ERR_NUMBER_OUT_OF_RANGE */
|
|
{
|
|
#ifdef BIGINT
|
|
if(cell->type == CELL_BIGINT)
|
|
{
|
|
longNum = bigintToInt64(cell);
|
|
*number = longNum;
|
|
#ifndef NEWLISP64
|
|
if(longNum > 2147483647LL || longNum < -2147483648LL)
|
|
return(errorProcExt(ERR_NUMBER_OUT_OF_RANGE, cell));
|
|
#endif
|
|
}
|
|
else
|
|
|
|
#endif /* BIGINT */
|
|
{
|
|
*number = 0;
|
|
return(errorProcArgs(ERR_NUMBER_EXPECTED, params));
|
|
}
|
|
}
|
|
|
|
return(params->next);
|
|
}
|
|
|
|
|
|
CELL * getFloat(CELL * params, double * floatNumber)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = evaluateExpression(params);
|
|
|
|
#ifndef NEWLISP64
|
|
if(cell->type == CELL_FLOAT)
|
|
*floatNumber = *(double *)&cell->aux;
|
|
else if(cell->type == CELL_INT64)
|
|
*floatNumber = *(INT64 *)&cell->aux;
|
|
#else
|
|
if(cell->type == CELL_FLOAT)
|
|
*floatNumber = *(double *)&cell->contents;
|
|
#endif
|
|
else if(cell->type == CELL_LONG)
|
|
*floatNumber = (INT)cell->contents;
|
|
else
|
|
{
|
|
#ifdef BIGINT
|
|
if(cell->type == CELL_BIGINT)
|
|
*floatNumber = bigintCellToFloat(cell);
|
|
else
|
|
#endif
|
|
{
|
|
*floatNumber = 0.0;
|
|
return(errorProcArgs(ERR_NUMBER_EXPECTED, params));
|
|
}
|
|
}
|
|
|
|
return(params->next);
|
|
}
|
|
|
|
|
|
CELL * getString(CELL * params, char * * stringPtr)
|
|
{
|
|
CELL * cell;
|
|
SYMBOL * sPtr;
|
|
|
|
cell = evaluateExpression(params);
|
|
|
|
if(cell->type == CELL_CONTEXT)
|
|
{
|
|
sPtr = translateCreateSymbol( ((SYMBOL*)cell->contents)->name, CELL_NIL,
|
|
(SYMBOL*)cell->contents, TRUE);
|
|
cell = (CELL *)sPtr->contents;
|
|
}
|
|
|
|
if(cell->type != CELL_STRING)
|
|
{
|
|
*stringPtr = "";
|
|
return(errorProcArgs(ERR_STRING_EXPECTED, params));
|
|
}
|
|
|
|
*stringPtr = (char *)cell->contents;
|
|
return(params->next);
|
|
}
|
|
|
|
|
|
CELL * getStringSize(CELL * params, char * * stringPtr, size_t * size, int evalFlag)
|
|
{
|
|
CELL * cell;
|
|
SYMBOL * sPtr;
|
|
|
|
if(evalFlag)
|
|
cell = evaluateExpression(params);
|
|
else
|
|
cell = params;
|
|
|
|
if(cell->type == CELL_CONTEXT)
|
|
{
|
|
sPtr = translateCreateSymbol( ((SYMBOL*)cell->contents)->name, CELL_NIL,
|
|
(SYMBOL*)cell->contents, TRUE);
|
|
symbolCheck = sPtr;
|
|
cell = (CELL *)sPtr->contents;
|
|
}
|
|
|
|
if(cell->type != CELL_STRING)
|
|
{
|
|
*stringPtr = "";
|
|
return(errorProcArgs(ERR_STRING_EXPECTED, params));
|
|
}
|
|
|
|
*stringPtr = (char *)cell->contents;
|
|
|
|
if(size) *size = cell->aux - 1;
|
|
return(params->next);
|
|
}
|
|
|
|
|
|
CELL * getSymbol(CELL * params, SYMBOL * * symbol)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = evaluateExpression(params);
|
|
|
|
if(cell->type != CELL_SYMBOL)
|
|
{
|
|
if(cell->type == CELL_DYN_SYMBOL)
|
|
{
|
|
*symbol = getDynamicSymbol(cell);
|
|
return(params->next);
|
|
}
|
|
*symbol = nilSymbol;
|
|
return(errorProcArgs(ERR_SYMBOL_EXPECTED, params));
|
|
}
|
|
|
|
*symbol = (SYMBOL *)cell->contents;
|
|
return(params->next);
|
|
}
|
|
|
|
/* only used for internal syms: $timer, $error-event, $prompt-event, $command-event
|
|
$transfer-event, and $signal-1-> $signal-32
|
|
If a quoted symbols hasn't been passed take the evaluated params as contents
|
|
of the system event symbols starting with $ */
|
|
CELL * getCreateSymbol(CELL * params, SYMBOL * * symbol, char * name)
|
|
{
|
|
CELL * cell;
|
|
CELL * cellForDelete;
|
|
|
|
cell = evaluateExpression(params);
|
|
|
|
if(cell->type != CELL_SYMBOL)
|
|
{
|
|
if(cell->type == CELL_DYN_SYMBOL)
|
|
{
|
|
*symbol = getDynamicSymbol(cell);
|
|
return(params->next);
|
|
}
|
|
*symbol = translateCreateSymbol(name, CELL_NIL, mainContext, TRUE);
|
|
(*symbol)->flags |= SYMBOL_PROTECTED | SYMBOL_GLOBAL;
|
|
cellForDelete = (CELL *)(*symbol)->contents;
|
|
if(isNil(cell))
|
|
*symbol = nilSymbol;
|
|
else if(cell->type != CELL_LAMBDA && cell->type != CELL_FEXPR && cell->type != CELL_PRIMITIVE)
|
|
{
|
|
*symbol = nilSymbol;
|
|
deleteList(cellForDelete);
|
|
return(errorProcExt(ERR_INVALID_PARAMETER, params));
|
|
}
|
|
else if(compareCells(cellForDelete, cell) != 0)
|
|
{
|
|
(*symbol)->contents = (UINT)copyCell(cell);
|
|
deleteList(cellForDelete);
|
|
}
|
|
}
|
|
else
|
|
*symbol = (SYMBOL *)cell->contents;
|
|
|
|
return(params->next);
|
|
}
|
|
|
|
|
|
CELL * getContext(CELL * params, SYMBOL * * context)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = evaluateExpression(params);
|
|
|
|
if(cell->type == CELL_CONTEXT || cell->type == CELL_SYMBOL)
|
|
*context = (SYMBOL *)cell->contents;
|
|
else
|
|
{
|
|
*context = NULL;
|
|
return(errorProcArgs(ERR_CONTEXT_EXPECTED, params));
|
|
}
|
|
|
|
if(symbolType(*context) != CELL_CONTEXT)
|
|
return(errorProcExt(ERR_CONTEXT_EXPECTED, params));
|
|
|
|
return(params->next);
|
|
}
|
|
|
|
|
|
CELL * getEvalDefault(CELL * params, CELL * * result)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = evaluateExpression(params);
|
|
|
|
if(cell->type == CELL_CONTEXT)
|
|
{
|
|
symbolCheck = translateCreateSymbol( ((SYMBOL*)cell->contents)->name, CELL_NIL,
|
|
(SYMBOL*)cell->contents, TRUE);
|
|
cell = (CELL *)symbolCheck->contents;
|
|
}
|
|
|
|
*result = cell;
|
|
|
|
return(params->next);
|
|
}
|
|
|
|
/* gets the first element, without list envelope in head
|
|
and return the list with envelope
|
|
*/
|
|
CELL * getListHead(CELL * params, CELL * * head)
|
|
{
|
|
CELL * cell;
|
|
SYMBOL * sPtr;
|
|
|
|
cell = evaluateExpression(params);
|
|
|
|
if(cell->type == CELL_CONTEXT)
|
|
{
|
|
sPtr = translateCreateSymbol( ((SYMBOL*)cell->contents)->name, CELL_NIL,
|
|
(SYMBOL*)cell->contents, TRUE);
|
|
cell = (CELL *)sPtr->contents;
|
|
}
|
|
|
|
if(!isList(cell->type))
|
|
{
|
|
*head = nilCell;
|
|
return(errorProcExt(ERR_LIST_EXPECTED, params));
|
|
}
|
|
|
|
*head = (CELL *)cell->contents;
|
|
|
|
return(params->next);
|
|
}
|
|
|
|
/* ------------------------------- core predicates ------------------------ */
|
|
|
|
CELL * p_setLocale(CELL * params)
|
|
{
|
|
#ifndef ANDROID
|
|
struct lconv * lc;
|
|
#endif
|
|
char * locale;
|
|
UINT category;
|
|
CELL * cell;
|
|
|
|
if(params != nilCell)
|
|
params = getString(params, &locale);
|
|
else locale = NULL;
|
|
|
|
getEvalDefault(params, &cell);
|
|
if(isNumber(cell->type)) /* second parameter */
|
|
getIntegerExt(cell, &category, FALSE);
|
|
else category = LC_ALL;
|
|
|
|
locale = setlocale(category, locale);
|
|
|
|
if(locale == NULL)
|
|
return(nilCell);
|
|
|
|
stringOutputRaw = (strcmp(locale, "C") == 0);
|
|
|
|
#ifndef ANDROID
|
|
lc = localeconv();
|
|
lc_decimal_point = *lc->decimal_point;
|
|
#endif
|
|
cell = getCell(CELL_EXPRESSION);
|
|
addList(cell, stuffString(locale));
|
|
#ifdef ANDROID
|
|
addList(cell, stuffStringN(".", 1));
|
|
#else
|
|
addList(cell, stuffStringN(lc->decimal_point, 1));
|
|
#endif
|
|
return(cell);
|
|
}
|
|
|
|
CELL * p_quote(CELL * params)
|
|
{
|
|
return(copyCell(params));
|
|
}
|
|
|
|
|
|
CELL * p_eval(CELL * params)
|
|
{
|
|
CELL * result;
|
|
|
|
params = evaluateExpression(params);
|
|
result = evaluateExpression(params);
|
|
pushResultFlag = FALSE;
|
|
return(result);
|
|
}
|
|
|
|
|
|
CELL * p_catch(CELL * params)
|
|
{
|
|
jmp_buf errorJumpSave;
|
|
UINT * envStackIdxSave;
|
|
UINT * lambdaStackIdxSave;
|
|
int recursionCountSave;
|
|
int value;
|
|
CELL * expr;
|
|
CELL * result;
|
|
SYMBOL * symbol = NULL;
|
|
SYMBOL * contextSave;
|
|
CELL * objSave;
|
|
CELL * objCellSave;
|
|
|
|
expr = params;
|
|
if(params->next != nilCell)
|
|
{
|
|
getSymbol(params->next, &symbol);
|
|
if(isProtected(symbol->flags))
|
|
return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbol)));
|
|
}
|
|
|
|
memcpy(errorJumpSave, errorJump, sizeof(jmp_buf));
|
|
/* save general environment */
|
|
envStackIdxSave = envStackIdx;
|
|
recursionCountSave = recursionCount;
|
|
lambdaStackIdxSave = lambdaStackIdx;
|
|
contextSave = currentContext;
|
|
/* save FOOP environment */
|
|
objSave = (CELL *)objSymbol.contents;
|
|
objCellSave = objCell;
|
|
itSymbol->contents = (UINT)nilCell;
|
|
|
|
if((value = setjmp(errorJump)) != 0)
|
|
{
|
|
memcpy(errorJump, errorJumpSave, (sizeof(jmp_buf)));
|
|
/* restore general environment */
|
|
recoverEnvironment(envStackIdxSave);
|
|
recursionCount = recursionCountSave;
|
|
lambdaStackIdx = lambdaStackIdxSave;
|
|
currentContext = contextSave;
|
|
/* restore FOOP environment */
|
|
objSymbol.contents = (UINT)objSave;
|
|
objCell = objCellSave;
|
|
|
|
evalCatchFlag--;
|
|
if(value == EXCEPTION_THROW)
|
|
{
|
|
if(symbol == NULL) return(throwResult);
|
|
deleteList((CELL*)symbol->contents);
|
|
symbol->contents = (UINT)throwResult;
|
|
return(trueCell);
|
|
}
|
|
|
|
if(errorStream.buffer != NULL)
|
|
{
|
|
if(symbol == NULL)
|
|
{
|
|
if(errorEvent == nilSymbol && evalCatchFlag == 0)
|
|
varPrintf(OUT_CONSOLE, "\n%.1024s\n", errorStream.buffer);
|
|
longjmp(errorJump, value);
|
|
}
|
|
deleteList((CELL*)symbol->contents);
|
|
symbol->contents = (UINT)stuffString(errorStream.buffer);
|
|
}
|
|
|
|
return(nilCell);
|
|
}
|
|
|
|
evalCatchFlag++;
|
|
result = copyCell(evaluateExpression(expr));
|
|
evalCatchFlag--;
|
|
memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
|
|
|
|
if(symbol == NULL) return(result);
|
|
|
|
deleteList((CELL*)symbol->contents);
|
|
symbol->contents = (UINT)result;
|
|
|
|
return(trueCell);
|
|
}
|
|
|
|
|
|
CELL * p_throw(CELL * params)
|
|
{
|
|
if(evalCatchFlag == 0)
|
|
return(errorProc(ERR_THROW_WO_CATCH));
|
|
|
|
throwResult = copyCell(evaluateExpression(params));
|
|
longjmp(errorJump, EXCEPTION_THROW);
|
|
|
|
return(trueCell);
|
|
}
|
|
|
|
CELL * p_throwError(CELL * params)
|
|
{
|
|
evalFunc = NULL;
|
|
errorProcExt(ERR_USER_ERROR, evaluateExpression(params));
|
|
return(nilCell);
|
|
}
|
|
|
|
CELL * evalString(CELL * params, int mode);
|
|
|
|
CELL * p_evalString(CELL * params) { return(evalString(params, EVAL_STRING)); }
|
|
CELL * p_readExpr(CELL * params) { return(evalString(params, READ_EXPR)); }
|
|
|
|
CELL * evalString(CELL * params, int mode)
|
|
{
|
|
SYMBOL * context = currentContext;
|
|
char * evalStr;
|
|
|
|
params = getString(params, &evalStr);
|
|
|
|
if(params != nilCell)
|
|
{
|
|
if((context = getCreateContext(params, TRUE)) == NULL)
|
|
return(errorProcExt(ERR_SYMBOL_OR_CONTEXT_EXPECTED, params));
|
|
}
|
|
|
|
if(mode == EVAL_STRING)
|
|
return(copyCell(sysEvalString(evalStr, context, params->next, mode)));
|
|
|
|
/* returns a new object not yet marked for deletion */
|
|
return(sysEvalString(evalStr, context, params->next, mode));
|
|
}
|
|
|
|
/* modes:
|
|
EVAL_STRING
|
|
the classic eval-string: read the string, compile to s-expression , evaluate
|
|
READ_EXPR_SYNC
|
|
used by p_sync() in nl-filesys.c
|
|
READ_EXPR
|
|
used by p_readExpr
|
|
READ_EXPR_NET
|
|
used by p_netEval introduces in 10.6.3, before READ_EXPR_SYNC was used
|
|
*/
|
|
|
|
|
|
CELL * sysEvalString(char * evalString, SYMBOL * context, CELL * proc, int mode)
|
|
{
|
|
CELL * program;
|
|
STREAM stream;
|
|
CELL * resultCell = nilCell;
|
|
SYMBOL * contextSave = NULL;
|
|
UINT * resultIdxSave;
|
|
jmp_buf errorJumpSave;
|
|
int recursionCountSave;
|
|
UINT * envStackIdxSave;
|
|
UINT offset;
|
|
CELL * xlate;
|
|
|
|
makeStreamFromString(&stream, evalString);
|
|
if(proc->next != nilCell)
|
|
{
|
|
getInteger(proc->next, &offset);
|
|
stream.ptr += offset;
|
|
}
|
|
|
|
resultIdxSave = resultStackIdx;
|
|
contextSave = currentContext;
|
|
currentContext = context;
|
|
|
|
if(proc != nilCell)
|
|
{
|
|
recursionCountSave = recursionCount;
|
|
envStackIdxSave = envStackIdx;
|
|
evalCatchFlag++;
|
|
memcpy(errorJumpSave, errorJump, sizeof(jmp_buf));
|
|
|
|
if(setjmp(errorJump) != 0)
|
|
{
|
|
memcpy(errorJump, errorJumpSave, (sizeof(jmp_buf)));
|
|
recoverEnvironment(envStackIdxSave);
|
|
evalCatchFlag--;
|
|
recursionCount = recursionCountSave;
|
|
currentContext = contextSave;
|
|
if(mode == READ_EXPR)
|
|
return(copyCell(evaluateExpression(proc)));
|
|
return(evaluateExpression(proc));
|
|
}
|
|
}
|
|
|
|
while(TRUE)
|
|
{
|
|
pushResult(program = getCell(CELL_QUOTE));
|
|
if(compileExpression(&stream, program) == 0)
|
|
break;
|
|
if(readerEvent != nilSymbol)
|
|
{
|
|
--resultStackIdx;
|
|
executeSymbol(readerEvent, program, &xlate);
|
|
pushResult(program = makeCell(CELL_QUOTE, (UINT)xlate));
|
|
}
|
|
if(mode == EVAL_STRING)
|
|
resultCell = evaluateExpression((CELL *)program->contents);
|
|
else /* READ_EXPR, READ_EXPR_SYNC, READ_EXPR_NET */
|
|
{
|
|
if(resultCell != nilCell) pushResult(resultCell); /* 10.6.3 */
|
|
countCell->contents = (UINT)(stream.ptr - stream.buffer);
|
|
resultCell = (CELL *)program->contents;
|
|
program->contents = (UINT)nilCell; /* de-couple */
|
|
if(mode == READ_EXPR_SYNC || mode == READ_EXPR) /* 10.6.3 */
|
|
break; /* only do first expression */
|
|
}
|
|
|
|
if(resultStackIdx > resultStackTop - 256)
|
|
{
|
|
program = popResult(); /* leave last result */
|
|
cleanupResults(resultIdxSave);
|
|
pushResult(program);
|
|
}
|
|
}
|
|
|
|
currentContext = contextSave;
|
|
|
|
if(proc != nilCell)
|
|
{
|
|
memcpy(errorJump, errorJumpSave, (sizeof(jmp_buf)));
|
|
evalCatchFlag--;
|
|
}
|
|
|
|
return(resultCell);
|
|
}
|
|
|
|
#ifdef EMSCRIPTEN
|
|
extern char *emscripten_run_script_string(const char *script);
|
|
char * evalJSbuff = NULL;
|
|
|
|
char * evalStringJS(char * cmd, size_t len)
|
|
{
|
|
if(evalJSbuff != NULL) free(evalJSbuff);
|
|
|
|
evalJSbuff = callocMemory(len + 1);
|
|
memcpy(evalJSbuff, cmd, len);
|
|
|
|
return(emscripten_run_script_string(evalJSbuff));
|
|
}
|
|
|
|
|
|
CELL * p_evalStringJS(CELL * params)
|
|
{
|
|
char * cmd;
|
|
size_t len;
|
|
char * result;
|
|
|
|
getStringSize(params, &cmd, &len, TRUE);
|
|
result = evalStringJS(cmd, len);
|
|
|
|
return(stuffString(result));
|
|
}
|
|
#endif
|
|
|
|
|
|
CELL * p_curry(CELL * params)
|
|
{
|
|
CELL * lambda;
|
|
CELL * cell;
|
|
|
|
cell = makeCell(CELL_EXPRESSION, (UINT)stuffSymbol(sysxSymbol));
|
|
lambda = makeCell(CELL_LAMBDA, (UINT)cell);
|
|
cell->next = getCell(CELL_EXPRESSION);
|
|
cell = cell->next;
|
|
cell->contents = (UINT)copyCell(params);
|
|
cell = (CELL *)cell->contents;
|
|
/* take left parameter */
|
|
cell->next = copyCell(params->next);
|
|
cell = cell->next;
|
|
cell->next = stuffSymbol(sysxSymbol);
|
|
/* take right parameter
|
|
cell->next = stuffSymbol(sysxSymbol);
|
|
cell = cell->next;
|
|
cell->next = copyCell(params->next);
|
|
*/
|
|
return(lambda);
|
|
}
|
|
|
|
|
|
CELL * p_apply(CELL * params)
|
|
{
|
|
CELL * expr;
|
|
CELL * args;
|
|
CELL * cell;
|
|
CELL * result;
|
|
CELL * func;
|
|
ssize_t count = 0, cnt;
|
|
UINT * resultIdxSave;
|
|
|
|
func = evaluateExpression(params);
|
|
params = getEvalDefault(params->next, &args);
|
|
cell = copyCell(func);
|
|
expr = makeCell(CELL_EXPRESSION, (UINT)cell);
|
|
|
|
if(args->type == CELL_ARRAY)
|
|
{
|
|
args = arrayList(args, FALSE);
|
|
pushResult(args);
|
|
}
|
|
|
|
if(args->type != CELL_EXPRESSION)
|
|
{
|
|
pushResult(expr);
|
|
if(isNil(args))
|
|
return(copyCell(evaluateExpression(expr)));
|
|
else
|
|
return(errorProcExt(ERR_LIST_EXPECTED, args));
|
|
}
|
|
|
|
args = (CELL *)args->contents;
|
|
|
|
if(params != nilCell)
|
|
getInteger(params, (UINT *)&count);
|
|
if(count < 2) count = MAX_LONG;
|
|
cnt = count;
|
|
|
|
resultIdxSave = resultStackIdx + 2;
|
|
for(;;)
|
|
{
|
|
while(args != nilCell && cnt--)
|
|
{
|
|
if(isSelfEval(args->type))
|
|
cell->next = copyCell(args);
|
|
else
|
|
cell->next = makeCell(CELL_QUOTE, (UINT)copyCell(args));
|
|
cell = cell->next;
|
|
args = args->next;
|
|
}
|
|
pushResult(expr);
|
|
if(args == nilCell)
|
|
{
|
|
result = evaluateExpression(expr);
|
|
if(symbolCheck)
|
|
{
|
|
pushResultFlag = FALSE;
|
|
return(result);
|
|
}
|
|
else return(copyCell(result));
|
|
}
|
|
result = copyCell(evaluateExpression(expr));
|
|
cell = copyCell(func);
|
|
expr = makeCell(CELL_EXPRESSION, (UINT)cell);
|
|
cell->next = makeCell(CELL_QUOTE, (UINT)result);
|
|
cell = cell->next;
|
|
cnt = count - 1;
|
|
cleanupResults(resultIdxSave);
|
|
}
|
|
}
|
|
|
|
CELL * p_args(CELL * params)
|
|
{
|
|
if(params != nilCell)
|
|
return(copyCell(implicitIndexList((CELL*)argsSymbol->contents, params)));
|
|
return(copyCell((CELL*)argsSymbol->contents));
|
|
}
|
|
|
|
/* in-place expansion, if symbol==NULL all uppercase, non-nil vars are expanded */
|
|
CELL * expand(CELL * expr, SYMBOL * symbol)
|
|
{
|
|
CELL * cell = nilCell;
|
|
SYMBOL * sPtr;
|
|
int enable = 1;
|
|
CELL * cont;
|
|
int wchar;
|
|
|
|
if(isList(expr->type) || expr->type == CELL_QUOTE)
|
|
cell = (CELL*)expr->contents;
|
|
else if(expr->type == CELL_SYMBOL && expr->contents == (UINT)symbol)
|
|
expandExprSymbol(expr, symbol);
|
|
|
|
while(cell != nilCell)
|
|
{
|
|
if(cell->type == CELL_SYMBOL && (cell->contents == (UINT)symbol || symbol == NULL) )
|
|
{
|
|
sPtr = (SYMBOL *)cell->contents;
|
|
if(symbol == NULL)
|
|
{
|
|
#ifndef SUPPORT_UTF8
|
|
wchar = *sPtr->name;
|
|
#else
|
|
utf8_wchar(sPtr->name, &wchar);
|
|
#endif
|
|
enable = (wchar > 64 && wchar < 91);
|
|
cont = (CELL*)sPtr->contents;
|
|
enable = (enable && cont->contents != (UINT)nilCell
|
|
&& cont->contents != (UINT)nilSymbol);
|
|
}
|
|
|
|
if(symbol || enable)
|
|
expandExprSymbol(cell, sPtr);
|
|
}
|
|
|
|
else if(isEnvelope(cell->type)) expand(cell, symbol);
|
|
cell = cell->next;
|
|
}
|
|
|
|
return(expr);
|
|
}
|
|
|
|
|
|
void expandExprSymbol(CELL * cell, SYMBOL * sPtr)
|
|
{
|
|
CELL * rep;
|
|
|
|
rep = copyCell((CELL*)sPtr->contents);
|
|
/* check for and undo copyCell optimization */
|
|
while((UINT)rep == sPtr->contents)
|
|
rep = copyCell((CELL*)sPtr->contents);
|
|
|
|
cell->type = rep->type;
|
|
cell->aux = rep->aux;
|
|
cell->contents = rep->contents;
|
|
rep->type = CELL_LONG;
|
|
deleteList(rep);
|
|
}
|
|
|
|
|
|
/* expands one or a chain of expressions */
|
|
|
|
CELL * blockExpand(CELL * block, SYMBOL * symbol)
|
|
{
|
|
CELL * expanded = nilCell;
|
|
CELL * next = nilCell;
|
|
|
|
while(block != nilCell)
|
|
{
|
|
if(expanded == nilCell)
|
|
{
|
|
next = expand(copyCell(block), symbol);
|
|
expanded = next;
|
|
}
|
|
else
|
|
{
|
|
next->next = expand(copyCell(block), symbol);
|
|
next = next->next;
|
|
}
|
|
block = block->next;
|
|
}
|
|
|
|
return(expanded);
|
|
}
|
|
|
|
|
|
CELL * p_expand(CELL * params)
|
|
{
|
|
SYMBOL * symbol;
|
|
CELL * expr;
|
|
CELL * next;
|
|
CELL * list;
|
|
CELL * cell;
|
|
int evalFlag;
|
|
|
|
params = getEvalDefault(params, &expr);
|
|
|
|
if((next = params) == nilCell)
|
|
return(expand(copyCell(expr), NULL));
|
|
|
|
while((params = next) != nilCell)
|
|
{
|
|
next = params->next;
|
|
params = evaluateExpression(params);
|
|
if(params->type == CELL_SYMBOL)
|
|
symbol = (SYMBOL*)params->contents;
|
|
else if(params->type == CELL_DYN_SYMBOL)
|
|
symbol = getDynamicSymbol(params);
|
|
else if(params->type == CELL_EXPRESSION)
|
|
{
|
|
evalFlag = getFlag(next);
|
|
list = (CELL*)params->contents; /* expansion assoc list */
|
|
while(list != nilCell)
|
|
{
|
|
if(list->type != CELL_EXPRESSION)
|
|
return(errorProcExt(ERR_LIST_EXPECTED, list));
|
|
cell = (CELL *)list->contents;
|
|
if(cell->type != CELL_SYMBOL)
|
|
return(errorProcExt(ERR_SYMBOL_EXPECTED, cell));
|
|
symbol = (SYMBOL*)cell->contents;
|
|
pushEnvironment(symbol->contents);
|
|
pushEnvironment(symbol);
|
|
if(evalFlag)
|
|
symbol->contents = (UINT)copyCell(evaluateExpression(cell->next));
|
|
else
|
|
symbol->contents = (UINT)cell->next;
|
|
expr = expand(copyCell(expr), symbol);
|
|
if(evalFlag) deleteList((CELL *)symbol->contents);
|
|
symbol = (SYMBOL*)popEnvironment();
|
|
symbol->contents = popEnvironment();
|
|
pushResult(expr);
|
|
list = list->next;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
return(errorProcExt(ERR_LIST_OR_SYMBOL_EXPECTED, params));
|
|
expr = expand(copyCell(expr), symbol);
|
|
pushResult(expr);
|
|
}
|
|
|
|
return(copyCell(expr));
|
|
}
|
|
|
|
|
|
CELL * defineOrMacro(CELL * params, UINT cellType, int flag)
|
|
{
|
|
SYMBOL * symbol;
|
|
CELL * argsPtr;
|
|
CELL * lambda;
|
|
CELL * args;
|
|
CELL * body;
|
|
CELL * cell;
|
|
|
|
if(params->type != CELL_EXPRESSION)
|
|
return(errorProcExt(ERR_LIST_OR_SYMBOL_EXPECTED, params));
|
|
|
|
/* symbol to be defined */
|
|
argsPtr = (CELL *)params->contents;
|
|
if(argsPtr->type != CELL_SYMBOL)
|
|
{
|
|
if(argsPtr->type == CELL_DYN_SYMBOL)
|
|
symbol = getDynamicSymbol(argsPtr);
|
|
else
|
|
return(errorProcExt(ERR_SYMBOL_EXPECTED, params));
|
|
}
|
|
else symbol = (SYMBOL *)argsPtr->contents;
|
|
|
|
if(isProtected(symbol->flags))
|
|
return(errorProc(ERR_SYMBOL_PROTECTED));
|
|
|
|
/* local symbols */
|
|
argsPtr = copyList(argsPtr->next);
|
|
|
|
args = getCell(CELL_EXPRESSION);
|
|
args->contents = (UINT)argsPtr;
|
|
/* body expressions */
|
|
body = copyList(params->next);
|
|
|
|
/* if expansion macro insert expand symbol for body expansion
|
|
(expand 'body) */
|
|
if(flag)
|
|
{
|
|
if(body->next != nilCell)
|
|
{
|
|
/* body has multiple expressions (expand '(begin ...)) */
|
|
cell = stuffSymbol(beginSymbol);
|
|
cell->next = body;
|
|
body = makeCell(CELL_EXPRESSION, (UINT)cell);
|
|
}
|
|
cell = stuffSymbol(expandSymbol);
|
|
cell->next = makeCell(CELL_QUOTE, (UINT)body);
|
|
body = makeCell(CELL_EXPRESSION, (UINT)cell);
|
|
symbol->flags |= SYMBOL_MACRO;
|
|
}
|
|
|
|
args->next = body;
|
|
lambda = makeCell(cellType, (UINT)args);
|
|
|
|
deleteList((CELL *)symbol->contents);
|
|
symbol->contents = (UINT)lambda;
|
|
|
|
pushResultFlag = FALSE;
|
|
return(lambda);
|
|
}
|
|
|
|
|
|
CELL * p_define(CELL * params)
|
|
{
|
|
if(params->type != CELL_SYMBOL)
|
|
{
|
|
if(params->type != CELL_DYN_SYMBOL)
|
|
return(defineOrMacro(params, CELL_LAMBDA, FALSE));
|
|
return(setDefine(getDynamicSymbol(params), params->next, SET_SET));
|
|
}
|
|
|
|
return(setDefine((SYMBOL *)params->contents, params->next, SET_SET));
|
|
}
|
|
|
|
CELL * p_defineMacro(CELL * params)
|
|
{
|
|
return(defineOrMacro(params, CELL_FEXPR, FALSE));
|
|
}
|
|
|
|
CELL * p_macro(CELL * params)
|
|
{
|
|
return(defineOrMacro(params, CELL_FEXPR, TRUE));
|
|
}
|
|
|
|
/* also called from setq */
|
|
CELL * p_setf(CELL *params)
|
|
{
|
|
SYMBOL * symbolRef = NULL;
|
|
CELL * cell;
|
|
CELL * new;
|
|
CELL * stringRef;
|
|
char * indexRefPtr;
|
|
|
|
SETF_BEGIN:
|
|
if(params->next == nilCell)
|
|
return(errorProc(ERR_MISSING_ARGUMENT));
|
|
|
|
cell = evaluateExpression(params);
|
|
|
|
if(cell == nilCell || cell == trueCell)
|
|
errorProcExt(ERR_IS_NOT_REFERENCED, cell);
|
|
|
|
symbolRef = symbolCheck;
|
|
stringRef = stringCell;
|
|
indexRefPtr = stringIndexPtr;
|
|
|
|
if(symbolRef && isProtected(symbolRef->flags))
|
|
return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbolRef)));
|
|
|
|
itSymbol->contents = (UINT)cell;
|
|
new = copyCell(evaluateExpression(params->next));
|
|
itSymbol->contents = (UINT)nilCell;
|
|
|
|
params = params->next;
|
|
params = params->next;
|
|
|
|
|
|
if(stringRef && indexRefPtr)
|
|
{
|
|
cell = setNthStr((CELL *)stringRef, new, indexRefPtr);
|
|
if(params != nilCell) goto SETF_BEGIN;
|
|
return(cell);
|
|
}
|
|
|
|
/* delete contents of original cell */
|
|
if(isEnvelope(cell->type))
|
|
{
|
|
if(cell->type == CELL_ARRAY)
|
|
deleteArray(cell);
|
|
else
|
|
deleteList((CELL *)cell->contents);
|
|
}
|
|
else if(cell->type == CELL_STRING || cell->type == CELL_DYN_SYMBOL
|
|
#ifdef BIGINT
|
|
|| cell->type == CELL_BIGINT
|
|
#endif
|
|
)
|
|
freeMemory( (void *)cell->contents);
|
|
|
|
|
|
/* get new contents */
|
|
cell->type = new->type;
|
|
cell->aux = new->aux;
|
|
cell->contents = new->contents;
|
|
|
|
/* free cell */
|
|
new->type = CELL_FREE;
|
|
new->aux = 0;
|
|
new->contents = 0;
|
|
new->next = firstFreeCell;
|
|
firstFreeCell = new;
|
|
--cellCount;
|
|
|
|
if(params != nilCell) goto SETF_BEGIN;
|
|
|
|
/* return modified cell */
|
|
symbolCheck = symbolRef;
|
|
pushResultFlag = FALSE;
|
|
return(cell);
|
|
}
|
|
|
|
|
|
CELL * p_set(CELL *params)
|
|
{
|
|
SYMBOL * symbol;
|
|
CELL * next;
|
|
|
|
for(;;)
|
|
{
|
|
params = getSymbol(params, &symbol);
|
|
next = params->next;
|
|
if(params == nilCell)
|
|
return(errorProc(ERR_MISSING_ARGUMENT));
|
|
if(next == nilCell) return(setDefine(symbol, params, SET_SET));
|
|
setDefine(symbol, params, SET_SET);
|
|
params = next;
|
|
}
|
|
}
|
|
|
|
|
|
CELL * p_constant(CELL *params)
|
|
{
|
|
SYMBOL * symbol;
|
|
CELL * next;
|
|
UINT * idx = envStackIdx;
|
|
|
|
for(;;)
|
|
{
|
|
params = getSymbol(params, &symbol);
|
|
/* make sure symbol is not used as local in call hierachy */
|
|
while(idx > envStack)
|
|
{
|
|
if(symbol == (SYMBOL *)*(--idx))
|
|
errorProcExt2(ERR_CANNOT_PROTECT_LOCAL, stuffSymbol(symbol));
|
|
--idx;
|
|
}
|
|
|
|
/* protect contexts from being set, but not vars holding contexts */
|
|
if((symbolType(symbol) == CELL_CONTEXT && (SYMBOL *)((CELL *)symbol->contents)->contents == symbol)
|
|
|| symbol == countSymbol)
|
|
return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbol)));
|
|
next = params->next;
|
|
if(symbol->context != currentContext)
|
|
return(errorProcExt2(ERR_NOT_CURRENT_CONTEXT, stuffSymbol(symbol)));
|
|
symbol->flags |= SYMBOL_PROTECTED;
|
|
if(params == nilCell)
|
|
return(copyCell((CELL*)symbol->contents));
|
|
if(next == nilCell)
|
|
{
|
|
next = setDefine(symbol, params, SET_CONSTANT);
|
|
pushResultFlag = TRUE;
|
|
return(copyCell(next));
|
|
}
|
|
setDefine(symbol, params, SET_CONSTANT);
|
|
pushResultFlag = TRUE;
|
|
params = next;
|
|
}
|
|
}
|
|
|
|
|
|
CELL * setDefine(SYMBOL * symbol, CELL * params, int type)
|
|
{
|
|
CELL * cell;
|
|
|
|
if(isProtected(symbol->flags))
|
|
{
|
|
if(type == SET_CONSTANT)
|
|
{
|
|
if(symbol == nilSymbol || symbol == trueSymbol)
|
|
return(errorProcExt2(ERR_SYMBOL_EXPECTED, stuffSymbol(symbol)));
|
|
}
|
|
else
|
|
return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbol)));
|
|
}
|
|
|
|
cell = copyCell(evaluateExpression(params));
|
|
|
|
deleteList((CELL *)symbol->contents);
|
|
symbol->contents = (UINT)(cell);
|
|
|
|
symbolCheck = symbol;
|
|
pushResultFlag = FALSE;
|
|
|
|
return(cell);
|
|
}
|
|
|
|
|
|
CELL * p_global(CELL * params)
|
|
{
|
|
SYMBOL * sPtr;
|
|
|
|
do
|
|
{
|
|
params = getSymbol(params, &sPtr);
|
|
if(sPtr->context != mainContext || currentContext != mainContext)
|
|
return(errorProcExt2(ERR_NOT_IN_MAIN, stuffSymbol(sPtr)));
|
|
else
|
|
sPtr->flags |= SYMBOL_GLOBAL;
|
|
} while (params != nilCell);
|
|
|
|
return(stuffSymbol(sPtr));
|
|
}
|
|
|
|
#define LET_STD 0
|
|
#define LET_NEST 1
|
|
#define LET_EXPAND 2
|
|
#define LET_LOCAL 3
|
|
|
|
CELL * let(CELL * params, int type);
|
|
|
|
CELL * p_let(CELL * params) { return(let(params, LET_STD)); }
|
|
CELL * p_letn(CELL * params) { return(let(params, LET_NEST)); }
|
|
CELL * p_letExpand(CELL * params) { return(let(params, LET_EXPAND)); }
|
|
CELL * p_local(CELL * params) { return(let(params, LET_LOCAL)); }
|
|
|
|
CELL * let(CELL * params, int type)
|
|
{
|
|
CELL * inits;
|
|
CELL * cell;
|
|
CELL * result = nilCell;
|
|
CELL * args = NULL, * list = NULL;
|
|
CELL * body;
|
|
SYMBOL * symbol;
|
|
int localCount = 0;
|
|
|
|
if(params->type != CELL_EXPRESSION)
|
|
return(errorProcExt(ERR_INVALID_LET, params));
|
|
|
|
/* evaluate symbol assignments in parameter list
|
|
handle double syntax classic: (let ((s1 e1) (s2 e2) ...) ...)
|
|
and: (let (s1 e1 s2 e2 ...) ...)
|
|
*/
|
|
inits = (CELL*)params->contents;
|
|
body = params->next;
|
|
|
|
if(type == LET_LOCAL)
|
|
{
|
|
while(inits != nilCell)
|
|
{
|
|
if(inits->type != CELL_SYMBOL)
|
|
return(errorProcExt(ERR_SYMBOL_EXPECTED, inits));
|
|
symbol = (SYMBOL *)inits->contents;
|
|
if(isProtected(symbol->flags))
|
|
return(errorProcExt(ERR_SYMBOL_PROTECTED, inits));
|
|
pushEnvironment(symbol->contents);
|
|
pushEnvironment(symbol);
|
|
symbol->contents = (UINT)copyCell(nilCell);
|
|
localCount++;
|
|
inits = inits->next;
|
|
}
|
|
goto EVAL_LET_BODY;
|
|
}
|
|
|
|
while(inits != nilCell)
|
|
{
|
|
if(inits->type != CELL_EXPRESSION)
|
|
{
|
|
if(inits->type != CELL_SYMBOL)
|
|
return(errorProcExt(ERR_INVALID_LET, inits));
|
|
cell = inits;
|
|
inits = ((CELL*)cell->next)->next;
|
|
}
|
|
else
|
|
{
|
|
cell = (CELL *)inits->contents;
|
|
if(cell->type != CELL_SYMBOL)
|
|
return(errorProcExt(ERR_SYMBOL_EXPECTED, inits));
|
|
inits = inits->next;
|
|
}
|
|
|
|
if(type == LET_STD || type == LET_EXPAND)
|
|
{
|
|
if(localCount == 0)
|
|
list = args = copyCell(evaluateExpression(cell->next));
|
|
else
|
|
{
|
|
args->next = copyCell(evaluateExpression(cell->next));
|
|
args = args->next;
|
|
}
|
|
}
|
|
else /* LET_NEST */
|
|
{
|
|
symbol = (SYMBOL *)cell->contents;
|
|
if(isProtected(symbol->flags))
|
|
return(errorProcExt(ERR_SYMBOL_PROTECTED, cell));
|
|
args = copyCell(evaluateExpression(cell->next));
|
|
pushEnvironment((CELL *)symbol->contents);
|
|
pushEnvironment((UINT)symbol);
|
|
symbol->contents = (UINT)args;
|
|
}
|
|
|
|
localCount++;
|
|
}
|
|
|
|
/* save symbols and get new bindings */
|
|
if(type == LET_STD || type == LET_EXPAND)
|
|
{
|
|
inits = (CELL*)params->contents;
|
|
while(inits != nilCell)
|
|
{
|
|
if(inits->type == CELL_EXPRESSION)
|
|
{
|
|
cell = (CELL *)inits->contents;
|
|
inits = inits->next;
|
|
}
|
|
else
|
|
{
|
|
cell = inits;
|
|
inits = ((CELL*)cell->next)->next;
|
|
}
|
|
|
|
symbol = (SYMBOL *)cell->contents;
|
|
|
|
if(isProtected(symbol->flags))
|
|
return(errorProcExt(ERR_SYMBOL_PROTECTED, cell));
|
|
|
|
pushEnvironment((CELL *)symbol->contents);
|
|
pushEnvironment((UINT)symbol);
|
|
symbol->contents = (UINT)list;
|
|
|
|
args = list;
|
|
list = list->next;
|
|
args->next = nilCell; /* decouple */
|
|
|
|
/* hook in LET_EXPAND mode here */
|
|
if(type == LET_EXPAND)
|
|
{
|
|
body = blockExpand(body, symbol);
|
|
pushResult(body);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
EVAL_LET_BODY:
|
|
/* evaluate body expressions */
|
|
while(body != nilCell)
|
|
{
|
|
if(result != nilCell) deleteList(result);
|
|
result = copyCell(evaluateExpression(body));
|
|
body = body->next;
|
|
}
|
|
|
|
/* restore environment */
|
|
while(localCount--)
|
|
{
|
|
symbol = (SYMBOL *)popEnvironment();
|
|
deleteList((CELL *)symbol->contents);
|
|
symbol->contents = popEnvironment();
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
CELL * p_first(CELL * params)
|
|
{
|
|
char str[2];
|
|
CELL * cell;
|
|
CELL * result;
|
|
#ifdef SUPPORT_UTF8
|
|
size_t len;
|
|
#endif
|
|
|
|
getEvalDefault(params, &cell);
|
|
|
|
if(cell->type == CELL_STRING)
|
|
{
|
|
stringCell = cell;
|
|
if((str[0] = *(char *)cell->contents) == 0)
|
|
return(stuffString(""));
|
|
|
|
#ifndef SUPPORT_UTF8
|
|
str[1] = 0;
|
|
result = stuffString(str);
|
|
#else
|
|
len = utf8_1st_len((char*)cell->contents);
|
|
if(len > cell->aux -1)
|
|
return(errorProc(ERR_INVALID_UTF8));
|
|
result = stuffStringN((char*)cell->contents, len);
|
|
#endif
|
|
|
|
stringIndexPtr = (char *)cell->contents;
|
|
if(symbolCheck)
|
|
{
|
|
pushResult(result);
|
|
pushResultFlag = FALSE;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
else if(isList(cell->type))
|
|
{
|
|
if(cell->contents == (UINT)nilCell)
|
|
return(errorProcExt(ERR_LIST_EMPTY, params));
|
|
|
|
pushResultFlag = FALSE;
|
|
return((CELL *)cell->contents);
|
|
}
|
|
|
|
else if(cell->type == CELL_ARRAY)
|
|
{
|
|
pushResultFlag = FALSE;
|
|
return(*(CELL * *)cell->contents);
|
|
}
|
|
|
|
return(errorProcExt(ERR_ARRAY_LIST_OR_STRING_EXPECTED, params));
|
|
}
|
|
|
|
|
|
CELL * p_rest(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
CELL * tail;
|
|
#ifdef SUPPORT_UTF8
|
|
size_t size;
|
|
#endif
|
|
|
|
/* cell = evaluateExpression(params); */
|
|
getEvalDefault(params, &cell);
|
|
|
|
if(isList(cell->type))
|
|
{
|
|
tail = makeCell(CELL_EXPRESSION, (UINT)copyList(((CELL*)cell->contents)->next));
|
|
return(tail);
|
|
}
|
|
else if(cell->type == CELL_ARRAY)
|
|
return(subarray(cell, 1, MAX_LONG));
|
|
|
|
else if(cell->type == CELL_STRING)
|
|
{
|
|
if(*(char *)cell->contents == 0)
|
|
return(stuffString(""));
|
|
#ifndef SUPPORT_UTF8
|
|
return(stuffString((char *)(cell->contents + 1)));
|
|
#else
|
|
size = utf8_1st_len((char *)cell->contents);
|
|
if(size > cell->aux - 1)
|
|
return(errorProc(ERR_INVALID_UTF8));
|
|
return(stuffString((char *)(cell->contents + size)));
|
|
#endif
|
|
}
|
|
|
|
return(errorProcExt(ERR_ARRAY_LIST_OR_STRING_EXPECTED, params));
|
|
}
|
|
|
|
CELL * implicitNrestSlice(CELL * num, CELL * params)
|
|
{
|
|
CELL * list;
|
|
ssize_t n, len;
|
|
|
|
getIntegerExt(num, (UINT *)&n, FALSE);
|
|
list = evaluateExpression(params);
|
|
|
|
if(list->type == CELL_CONTEXT)
|
|
list = (CELL *)(translateCreateSymbol(
|
|
((SYMBOL*)list->contents)->name,
|
|
CELL_NIL,
|
|
(SYMBOL*)list->contents,
|
|
TRUE))->contents;
|
|
|
|
/* slice */
|
|
if(isNumber(list->type))
|
|
{
|
|
getIntegerExt(list, (UINT*)&len, FALSE);
|
|
list = evaluateExpression(params->next);
|
|
|
|
if(list->type == CELL_CONTEXT)
|
|
list = (CELL *)(translateCreateSymbol(
|
|
((SYMBOL*)list->contents)->name,
|
|
CELL_NIL,
|
|
(SYMBOL*)list->contents,
|
|
TRUE))->contents;
|
|
|
|
if(isList(list->type))
|
|
return(sublist((CELL *)list->contents, n, len));
|
|
else if(list->type == CELL_STRING)
|
|
return(substring((char *)list->contents, list->aux-1, n, len));
|
|
else if(list->type == CELL_ARRAY)
|
|
return(subarray(list, n, len));
|
|
}
|
|
|
|
/* nrest lists */
|
|
else if(isList(list->type))
|
|
{
|
|
list = (CELL *)list->contents;
|
|
|
|
if(n < 0) n = convertNegativeOffset(n, list);
|
|
|
|
while(n-- && list != nilCell)
|
|
list = list->next;
|
|
|
|
return(makeCell(CELL_EXPRESSION, (UINT)copyList(list)));
|
|
}
|
|
|
|
/* nrest strings
|
|
this was UTF-8 sensitive before 9.1.11, but only the
|
|
explicit first/last/rest should be UTF8-sensitive
|
|
*/
|
|
else if(list->type == CELL_STRING)
|
|
return(substring((char *)list->contents, list->aux - 1, n, MAX_LONG));
|
|
|
|
else if(list->type == CELL_ARRAY)
|
|
return(subarray(list, n, MAX_LONG));
|
|
|
|
return(errorProcExt(ERR_ILLEGAL_TYPE, params));
|
|
}
|
|
|
|
|
|
CELL * p_cons(CELL * params)
|
|
{
|
|
CELL * cons;
|
|
CELL * head;
|
|
CELL * tail;
|
|
|
|
if(params == nilCell)
|
|
return(getCell(CELL_EXPRESSION));
|
|
|
|
head = copyCell(evaluateExpression(params));
|
|
cons = makeCell(CELL_EXPRESSION, (UINT)head);
|
|
params = params->next;
|
|
|
|
if(params != nilCell)
|
|
{
|
|
tail = evaluateExpression(params);
|
|
|
|
if(isList(tail->type))
|
|
{
|
|
head->next = copyList((CELL *)tail->contents);
|
|
cons->type = tail->type;
|
|
}
|
|
else
|
|
head->next = copyCell(tail);
|
|
}
|
|
|
|
return(cons);
|
|
}
|
|
|
|
|
|
CELL * p_list(CELL * params)
|
|
{
|
|
CELL * list;
|
|
CELL * lastCopy = NULL;
|
|
CELL * copy;
|
|
CELL * cell;
|
|
UINT * resultIdxSave;
|
|
|
|
list = getCell(CELL_EXPRESSION);
|
|
|
|
resultIdxSave = resultStackIdx;
|
|
while(params != nilCell)
|
|
{
|
|
cell = evaluateExpression(params);
|
|
if(cell->type == CELL_ARRAY)
|
|
copy = arrayList(cell, TRUE);
|
|
else
|
|
copy = copyCell(cell);
|
|
if(lastCopy == NULL)
|
|
list->contents = (UINT)copy;
|
|
else lastCopy->next = copy;
|
|
lastCopy = copy;
|
|
cleanupResults(resultIdxSave);
|
|
params = params->next;
|
|
}
|
|
|
|
return(list);
|
|
}
|
|
|
|
|
|
CELL * p_last(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
CELL * listPtr;
|
|
CELL * result;
|
|
char * str;
|
|
|
|
getEvalDefault(params, &cell);
|
|
|
|
if(cell->type == CELL_STRING)
|
|
{
|
|
stringCell = cell;
|
|
str = (char *)cell->contents;
|
|
if(*str == 0) return(copyCell(cell));
|
|
#ifndef SUPPORT_UTF8
|
|
str += (cell->aux - 2);
|
|
result = stuffString(str);
|
|
#else
|
|
str = utf8_index(str, utf8_wlen(str, str + cell->aux) -1);
|
|
result = stuffString(str);
|
|
#endif
|
|
stringIndexPtr = (char *)str;
|
|
if(symbolCheck)
|
|
{
|
|
pushResult(result);
|
|
pushResultFlag = FALSE;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
else if(isList(cell->type))
|
|
{
|
|
if(cell->contents == (UINT)nilCell)
|
|
return(errorProcExt(ERR_LIST_EMPTY, params));
|
|
|
|
if(cell->aux != (UINT)nilCell)
|
|
{
|
|
pushResultFlag = FALSE;
|
|
return((CELL *)cell->aux);
|
|
}
|
|
|
|
listPtr = (CELL *)cell->contents;
|
|
while(listPtr->next != nilCell) listPtr = listPtr->next;
|
|
cell->aux = (UINT)listPtr;
|
|
pushResultFlag = FALSE;
|
|
return(listPtr);
|
|
}
|
|
|
|
else if(cell->type == CELL_ARRAY)
|
|
{
|
|
pushResultFlag = FALSE;
|
|
return(*((CELL * *)cell->contents + (cell->aux - 1) / sizeof(UINT) - 1));
|
|
}
|
|
|
|
return(errorProcExt(ERR_ARRAY_LIST_OR_STRING_EXPECTED, params));
|
|
}
|
|
|
|
|
|
/* -------------------------- program flow and logical ------------------ */
|
|
|
|
CELL * evaluateBlock(CELL * cell)
|
|
{
|
|
CELL * result;
|
|
|
|
result = nilCell;
|
|
|
|
while(cell != nilCell)
|
|
{
|
|
result = evaluateExpression(cell);
|
|
cell = cell->next;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
|
|
CELL * p_if(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = evaluateExpression(params);
|
|
itSymbol->contents = (UINT)cell;
|
|
while(isNil(cell) || isEmpty(cell))
|
|
{
|
|
params = params->next;
|
|
if(params->next == nilCell)
|
|
goto IF_RETURN;
|
|
params = params->next;
|
|
cell = evaluateExpression(params);
|
|
}
|
|
|
|
if(params->next != nilCell)
|
|
cell = evaluateExpression(params->next);
|
|
|
|
IF_RETURN:
|
|
itSymbol->contents = (UINT)nilCell;
|
|
pushResultFlag = FALSE;
|
|
return(cell);
|
|
}
|
|
|
|
|
|
CELL * p_ifNot(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = evaluateExpression(params);
|
|
if(!isNil(cell) && !isEmpty(cell))
|
|
params = params->next;
|
|
|
|
cell = evaluateExpression(params->next);
|
|
|
|
pushResultFlag = FALSE;
|
|
return(cell);
|
|
}
|
|
|
|
|
|
CELL * p_when(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = evaluateExpression(params);
|
|
if(isNil(cell) || isEmpty(cell)) goto WHEN_END;
|
|
|
|
while((params = params->next) != nilCell)
|
|
cell = evaluateExpression(params);
|
|
|
|
WHEN_END:
|
|
pushResultFlag = FALSE;
|
|
return(cell);
|
|
}
|
|
|
|
CELL * p_unless(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = evaluateExpression(params);
|
|
if(!isNil(cell) && !isEmpty(cell)) goto UNLESS_END;
|
|
|
|
while((params = params->next) != nilCell)
|
|
cell = evaluateExpression(params);
|
|
|
|
UNLESS_END:
|
|
pushResultFlag = FALSE;
|
|
return(cell);
|
|
}
|
|
|
|
|
|
CELL * p_condition(CELL * params)
|
|
{
|
|
CELL * condition;
|
|
CELL * eval = nilCell;
|
|
|
|
while(params != nilCell)
|
|
{
|
|
if(params->type == CELL_EXPRESSION)
|
|
{
|
|
condition = (CELL *)params->contents;
|
|
eval = evaluateExpression(condition);
|
|
if(!isNil(eval) && !isEmpty(eval))
|
|
{
|
|
if(condition->next != nilCell)
|
|
eval = evaluateBlock(condition->next);
|
|
break;
|
|
}
|
|
params = params->next;
|
|
}
|
|
else return(errorProc(ERR_LIST_EXPECTED));
|
|
}
|
|
|
|
pushResultFlag = FALSE;
|
|
return(eval);
|
|
}
|
|
|
|
|
|
CELL * p_case(CELL * params)
|
|
{
|
|
CELL * cases;
|
|
CELL * cond;
|
|
CELL * eval;
|
|
|
|
cases = params->next;
|
|
params = evaluateExpression(params);
|
|
while(cases != nilCell)
|
|
{
|
|
if(cases->type == CELL_EXPRESSION)
|
|
{
|
|
cond = (CELL *)cases->contents;
|
|
if(compareCells(params, cond) == 0
|
|
|| (cond->type == CELL_SYMBOL && symbolType((SYMBOL *)cond->contents) == CELL_TRUE)
|
|
|| cond->type == CELL_TRUE)
|
|
{
|
|
eval = evaluateBlock(cond->next);
|
|
pushResultFlag = FALSE;
|
|
return(eval);
|
|
}
|
|
}
|
|
cases = cases->next;
|
|
}
|
|
|
|
return(nilCell);
|
|
}
|
|
|
|
#define REPEAT_WHILE 0
|
|
#define REPEAT_DOWHILE 1
|
|
#define REPEAT_UNTIL 2
|
|
#define REPEAT_DOUNTIL 3
|
|
|
|
CELL * p_while(CELL * params) { return(repeat(params, REPEAT_WHILE)); }
|
|
CELL * p_doWhile(CELL * params) { return(repeat(params, REPEAT_DOWHILE)); }
|
|
CELL * p_until(CELL * params) { return(repeat(params, REPEAT_UNTIL)); }
|
|
CELL * p_doUntil(CELL * params) { return(repeat(params, REPEAT_DOUNTIL)); }
|
|
|
|
|
|
CELL * repeat(CELL * params, int type)
|
|
{
|
|
CELL * result;
|
|
CELL * cell;
|
|
CELL * cellIdx;
|
|
UINT * resultIdxSave;
|
|
SYMBOL * symbolRef = NULL;
|
|
|
|
cellIdx = initIteratorIndex();
|
|
|
|
resultIdxSave = resultStackIdx;
|
|
result = nilCell;
|
|
while(TRUE)
|
|
{
|
|
switch(type)
|
|
{
|
|
case REPEAT_WHILE:
|
|
cell = evaluateExpression(params);
|
|
if(isNil(cell) || isEmpty(cell)) goto END_REPEAT;
|
|
cleanupResults(resultIdxSave);
|
|
result = evaluateBlock(params->next);
|
|
symbolRef = symbolCheck;
|
|
break;
|
|
case REPEAT_DOWHILE:
|
|
result = evaluateBlock(params->next);
|
|
symbolRef = symbolCheck;
|
|
cell = evaluateExpression(params);
|
|
if(isNil(cell) || isEmpty(cell)) goto END_REPEAT;
|
|
cleanupResults(resultIdxSave);
|
|
break;
|
|
case REPEAT_UNTIL:
|
|
cell = evaluateExpression(params);
|
|
if(!isNil(cell) && !isEmpty(cell))
|
|
{
|
|
if(params->next == nilCell)
|
|
result = cell;
|
|
goto END_REPEAT;
|
|
}
|
|
cleanupResults(resultIdxSave);
|
|
result = evaluateBlock(params->next);
|
|
symbolRef = symbolCheck;
|
|
break;
|
|
case REPEAT_DOUNTIL:
|
|
result = evaluateBlock(params->next);
|
|
symbolRef = symbolCheck;
|
|
cell = evaluateExpression(params);
|
|
if(!isNil(cell) && !isEmpty(cell))
|
|
{
|
|
if(params->next == nilCell)
|
|
result = cell;
|
|
goto END_REPEAT;
|
|
}
|
|
cleanupResults(resultIdxSave);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(cellIdx->type == CELL_LONG) cellIdx->contents += 1;
|
|
}
|
|
|
|
END_REPEAT:
|
|
recoverIteratorIndex(cellIdx);
|
|
|
|
symbolCheck = symbolRef;
|
|
pushResultFlag = FALSE;
|
|
return(result);
|
|
}
|
|
|
|
|
|
CELL * getPushSymbolParam(CELL * params, SYMBOL * * sym)
|
|
{
|
|
SYMBOL * symbol;
|
|
CELL * cell;
|
|
|
|
if(params->type != CELL_EXPRESSION)
|
|
return(errorProcExt(ERR_LIST_EXPECTED, params));
|
|
|
|
cell = (CELL *)params->contents;
|
|
if(cell->type != CELL_SYMBOL)
|
|
return(errorProcExt(ERR_SYMBOL_EXPECTED, cell));
|
|
|
|
*sym = symbol = (SYMBOL *)cell->contents;
|
|
if(isProtected(symbol->flags))
|
|
return(errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(symbol)));
|
|
|
|
pushEnvironment((CELL *)symbol->contents);
|
|
pushEnvironment((UINT)symbol);
|
|
symbol->contents = (UINT)nilCell;
|
|
|
|
return(cell->next);
|
|
}
|
|
|
|
|
|
CELL * initIteratorIndex(void)
|
|
{
|
|
CELL * cell = stuffInteger(0);
|
|
|
|
pushEnvironment(listIdxSymbol->contents);
|
|
pushEnvironment(listIdxSymbol);
|
|
listIdxSymbol->contents = (UINT)cell;
|
|
|
|
return(cell);
|
|
}
|
|
|
|
void recoverIteratorIndex(CELL * cellIdx)
|
|
{
|
|
deleteList(cellIdx);
|
|
listIdxSymbol = (SYMBOL*)popEnvironment();
|
|
listIdxSymbol->contents = (UINT)popEnvironment();
|
|
}
|
|
|
|
CELL * loop(CELL * params, int forFlag)
|
|
{
|
|
CELL * cell;
|
|
CELL * cond = nilCell;
|
|
CELL * block;
|
|
SYMBOL * symbol = NULL;
|
|
double fromFlt, toFlt, interval, step, cntFlt;
|
|
INT64 stepCnt, i;
|
|
INT64 fromInt64 = 0, toInt64 = 0;
|
|
int intFlag;
|
|
UINT * resultIdxSave;
|
|
|
|
cell = getPushSymbolParam(params, &symbol);
|
|
|
|
/* integer loops for dotimes and (for (i from to) ...) */
|
|
if((intFlag = ((CELL *)cell->next)->next == nilCell))
|
|
{
|
|
if(forFlag)
|
|
{
|
|
cell = getInteger64Ext(cell, &fromInt64, TRUE);
|
|
getInteger64Ext(cell, &toInt64, TRUE);
|
|
stepCnt = (toInt64 > fromInt64) ? toInt64 - fromInt64 : fromInt64 - toInt64;
|
|
}
|
|
else /* dotimes */
|
|
{
|
|
fromInt64 = toInt64 = 0;
|
|
cond = getInteger64Ext(cell, &stepCnt, TRUE);
|
|
}
|
|
}
|
|
else /* float (for (i from to step) ...) */
|
|
{
|
|
cell = getFloat(cell, &fromFlt);
|
|
cell = getFloat(cell, &toFlt);
|
|
cond = getFloat(cell, &step);
|
|
if(isnan(fromFlt) || isnan(toFlt) || isnan(step))
|
|
return(errorProc(ERR_INVALID_PARAMETER_NAN));
|
|
if(step < 0) step = -step;
|
|
if(fromFlt > toFlt) step = -step;
|
|
cntFlt = (fromFlt < toFlt) ? (toFlt - fromFlt)/step : (fromFlt - toFlt)/step;
|
|
stepCnt = (cntFlt > 0.0) ? floor(cntFlt + 0.0000000001) : floor(-cntFlt + 0.0000000001);
|
|
}
|
|
|
|
block = params->next;
|
|
resultIdxSave = resultStackIdx;
|
|
cell = nilCell;
|
|
for(i = 0; i <= stepCnt; i++)
|
|
{
|
|
if(!forFlag && i == stepCnt) break;
|
|
deleteList((CELL *)symbol->contents);
|
|
if(intFlag)
|
|
{
|
|
symbol->contents =
|
|
(UINT)stuffInteger64((fromInt64 > toInt64) ? fromInt64 - i:
|
|
fromInt64 + i);
|
|
}
|
|
else
|
|
{
|
|
interval = fromFlt + i * step;
|
|
symbol->contents = (UINT)stuffFloat(interval);
|
|
}
|
|
/* cleanupResults(resultIdxSave);*/
|
|
while(resultStackIdx > resultIdxSave) deleteList(popResult());
|
|
if(cond != nilCell)
|
|
{
|
|
cell = evaluateExpression(cond);
|
|
if(!isNil(cell)) break;
|
|
}
|
|
cell = evaluateBlock(block);
|
|
}
|
|
|
|
|
|
if(symbolCheck && cell != (CELL *)symbol->contents && symbol != symbolCheck)
|
|
pushResultFlag = FALSE;
|
|
else
|
|
cell = copyCell(cell);
|
|
|
|
deleteList((CELL *)symbol->contents);
|
|
symbol = (SYMBOL*)popEnvironment();
|
|
symbol->flags &= ~SYMBOL_PROTECTED;
|
|
symbol->contents = (UINT)popEnvironment();
|
|
|
|
return(cell);
|
|
}
|
|
|
|
|
|
CELL * p_dotimes(CELL * params)
|
|
{
|
|
return(loop(params, 0));
|
|
}
|
|
|
|
CELL * p_for(CELL * params)
|
|
{
|
|
return(loop(params, 1));
|
|
}
|
|
|
|
|
|
#define DOLIST 0
|
|
#define DOTREE 1
|
|
#define DOARGS 2
|
|
#define DOSTRING 3
|
|
|
|
CELL * p_dolist(CELL * params)
|
|
{
|
|
return(dolist(params, DOLIST));
|
|
}
|
|
|
|
CELL * p_dotree(CELL * params)
|
|
{
|
|
return(dolist(params, DOTREE));
|
|
}
|
|
|
|
CELL * p_doargs(CELL * params)
|
|
{
|
|
return(dolist(params, DOARGS));
|
|
}
|
|
|
|
CELL * p_dostring(CELL * params)
|
|
{
|
|
return(dolist(params, DOSTRING));
|
|
}
|
|
|
|
CELL * dolist(CELL * params, int doType)
|
|
{
|
|
CELL * cell;
|
|
CELL * list = nilCell;
|
|
char * str;
|
|
#ifdef SUPPORT_UTF8
|
|
int chr;
|
|
#endif
|
|
CELL * cond = nilCell;
|
|
SYMBOL * symbol = NULL;
|
|
SYMBOL * sPtr;
|
|
CELL * cellIdx;
|
|
UINT * resultIdxSave;
|
|
|
|
cell = getPushSymbolParam(params, &symbol);
|
|
cellIdx = initIteratorIndex();
|
|
|
|
switch(doType)
|
|
{
|
|
case DOLIST:
|
|
/* list = copyCell(evaluateExpression(cell)); */
|
|
getEvalDefault(cell, &list);
|
|
if(isList(list->type)) list = copyCell(list);
|
|
else if(list->type == CELL_ARRAY) list = arrayList(list, FALSE);
|
|
else return(errorProcExt(ERR_LIST_EXPECTED, cell));
|
|
cond = cell->next;
|
|
break;
|
|
case DOTREE:
|
|
getContext(cell, &sPtr);
|
|
list = getCell(CELL_EXPRESSION);
|
|
collectSymbols((SYMBOL *)((CELL *)sPtr->contents)->aux, list);
|
|
cond = (getFlag(cell->next) == 1) ? trueCell : nilCell;
|
|
break;
|
|
case DOARGS:
|
|
list = copyCell((CELL *)argsSymbol->contents);
|
|
cond = cell;
|
|
break;
|
|
case DOSTRING:
|
|
getString(cell, &str);
|
|
resultIdxSave = resultStackIdx;
|
|
cond = cell->next;
|
|
while(*str)
|
|
{
|
|
cleanupResults(resultIdxSave);
|
|
deleteList((CELL *)symbol->contents);
|
|
#ifdef SUPPORT_UTF8
|
|
str = utf8_wchar(str, &chr);
|
|
symbol->contents = (UINT)stuffInteger(chr);
|
|
#else
|
|
symbol->contents = (UINT)stuffInteger((int)*str++);
|
|
#endif
|
|
if(cond != nilCell)
|
|
{
|
|
cell = evaluateExpression(cond);
|
|
if(!isNil(cell)) break;
|
|
}
|
|
cell = evaluateBlock(params->next);
|
|
if(cellIdx->type == CELL_LONG) cellIdx->contents += 1;
|
|
}
|
|
goto FINISH_DO;
|
|
break;
|
|
}
|
|
|
|
/* make sure worklist gets destroyed */
|
|
pushResult(list);
|
|
list = (CELL *)list->contents;
|
|
|
|
resultIdxSave = resultStackIdx;
|
|
cell = nilCell;
|
|
while(list!= nilCell)
|
|
{
|
|
cleanupResults(resultIdxSave);
|
|
deleteList((CELL *)symbol->contents);
|
|
symbol->contents = (UINT)copyCell(list);
|
|
if(cond != nilCell)
|
|
{
|
|
if(doType == DOTREE)
|
|
{
|
|
sPtr = (SYMBOL *)list->contents;
|
|
if(*sPtr->name != '_')
|
|
{
|
|
cell = nilCell;
|
|
goto DO_CONTINUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cell = evaluateExpression(cond);
|
|
if(!isNil(cell)) break;
|
|
}
|
|
}
|
|
cell = evaluateBlock(params->next);
|
|
if(cellIdx->type == CELL_LONG) cellIdx->contents += 1;
|
|
DO_CONTINUE:
|
|
list = list->next;
|
|
}
|
|
|
|
FINISH_DO:
|
|
if(symbolCheck && cell != (CELL *)symbol->contents && symbol != symbolCheck)
|
|
pushResultFlag = FALSE;
|
|
else
|
|
cell = copyCell(cell);
|
|
|
|
recoverIteratorIndex(cellIdx);
|
|
|
|
deleteList((CELL *)symbol->contents);
|
|
symbol = (SYMBOL*)popEnvironment();
|
|
symbol->contents = (UINT)popEnvironment();
|
|
|
|
return(cell);
|
|
}
|
|
|
|
|
|
CELL * p_evalBlock(CELL * params)
|
|
{
|
|
CELL * result = nilCell;
|
|
|
|
while(params != nilCell)
|
|
{
|
|
result = evaluateExpression(params);
|
|
params = params->next;
|
|
}
|
|
|
|
pushResultFlag = FALSE;
|
|
return(result);
|
|
}
|
|
|
|
extern UINT getAddress(CELL * params);
|
|
|
|
CELL * p_copy(CELL * params)
|
|
{
|
|
CELL * copy;
|
|
|
|
/* experimental: copy a cell from address, from:
|
|
http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=5&t=4548
|
|
June 14, 2014 "get-cell function patch"
|
|
*/
|
|
if(params->next != nilCell && getFlag(params->next))
|
|
return(copyCell((CELL *)getAddress(params)));
|
|
|
|
copy = copyCell(evaluateExpression(params));
|
|
symbolCheck = NULL;
|
|
return(copy);
|
|
}
|
|
|
|
CELL * p_silent(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
evalSilent = TRUE;
|
|
|
|
cell = evaluateBlock(params);
|
|
if(symbolCheck)
|
|
{
|
|
pushResultFlag = FALSE;
|
|
return(cell);
|
|
}
|
|
|
|
return(copyCell(cell));
|
|
}
|
|
|
|
|
|
CELL * p_and(CELL * params)
|
|
{
|
|
CELL * result = trueCell;
|
|
|
|
while(params != nilCell)
|
|
{
|
|
result = evaluateExpression(params);
|
|
if(isNil(result) || isEmpty(result)) return(copyCell(result));
|
|
params = params->next;
|
|
}
|
|
|
|
if(symbolCheck)
|
|
{
|
|
pushResultFlag = FALSE;
|
|
return(result);
|
|
}
|
|
|
|
return(copyCell(result));
|
|
}
|
|
|
|
|
|
CELL * p_or(CELL * params)
|
|
{
|
|
CELL * result = nilCell;
|
|
|
|
while(params != nilCell)
|
|
{
|
|
result = evaluateExpression(params);
|
|
if(!isNil(result) && !isEmpty(result))
|
|
{
|
|
if(symbolCheck)
|
|
{
|
|
pushResultFlag = FALSE;
|
|
return(result);
|
|
}
|
|
return(copyCell(result));
|
|
}
|
|
params = params->next;
|
|
}
|
|
|
|
return(copyCell(result));
|
|
}
|
|
|
|
|
|
CELL * p_not(CELL * params)
|
|
{
|
|
CELL * eval;
|
|
|
|
eval = evaluateExpression(params);
|
|
if(isNil(eval) || isEmpty(eval))
|
|
return(trueCell);
|
|
return(nilCell);
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------ I / O --------------------------------- */
|
|
|
|
CELL * p_print(CELL * params)
|
|
{
|
|
return println(params, FALSE);
|
|
}
|
|
|
|
|
|
CELL * p_println(CELL * params)
|
|
{
|
|
return println(params, TRUE);
|
|
}
|
|
|
|
|
|
CELL * println(CELL * params, int lineFeed)
|
|
{
|
|
CELL * result;
|
|
|
|
result = nilCell;
|
|
while(params != nilCell)
|
|
{
|
|
result = evaluateExpression(params);
|
|
printCell(result, 0, OUT_DEVICE);
|
|
params = params->next;
|
|
}
|
|
|
|
if(lineFeed) varPrintf(OUT_DEVICE, LINE_FEED);
|
|
|
|
return(copyCell(result));
|
|
}
|
|
|
|
|
|
CELL * p_device(CELL * params)
|
|
{
|
|
if(params != nilCell)
|
|
getInteger(params, &printDevice);
|
|
return(stuffInteger(printDevice));
|
|
}
|
|
|
|
|
|
CELL * p_load(CELL * params)
|
|
{
|
|
char * fileName;
|
|
CELL * result = nilCell;
|
|
CELL * next;
|
|
SYMBOL * context;
|
|
int count = 0;
|
|
|
|
/* get last parameter */
|
|
if((next = params) == nilCell)
|
|
errorProc(ERR_MISSING_ARGUMENT);
|
|
while(next->next != nilCell)
|
|
{
|
|
count++;
|
|
next = next->next;
|
|
}
|
|
|
|
next = evaluateExpression(next);
|
|
if(next->type == CELL_STRING)
|
|
{
|
|
count++;
|
|
context = mainContext;
|
|
}
|
|
else
|
|
{
|
|
if(count == 0)
|
|
errorProcExt(ERR_STRING_EXPECTED, next);
|
|
if((context = getCreateContext(next, FALSE)) == NULL)
|
|
errorProcExt(ERR_SYMBOL_OR_CONTEXT_EXPECTED, next);
|
|
next = NULL;
|
|
}
|
|
|
|
while(count--)
|
|
{
|
|
/* if last arg was a string, avoid double evaluation */
|
|
if(count == 0 && next != NULL)
|
|
getStringSize(next, &fileName, NULL, FALSE);
|
|
else
|
|
params = getString(params, &fileName);
|
|
|
|
result = loadFile(fileName, 0, 0, context);
|
|
|
|
if(result == NULL)
|
|
return(errorProcExt2(ERR_ACCESSING_FILE, stuffString(fileName)));
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
|
|
void saveContext(SYMBOL * sPtr, UINT device)
|
|
{
|
|
SYMBOL * contextSave;
|
|
|
|
contextSave = currentContext;
|
|
|
|
currentContext = sPtr;
|
|
|
|
if(currentContext != mainContext)
|
|
{
|
|
varPrintf(device, "%s(context '%s)%s%s",
|
|
LINE_FEED, sPtr->name, LINE_FEED, LINE_FEED);
|
|
/* make sure 'set' is not overwritten */
|
|
if((sPtr = lookupSymbol("set", currentContext)) != NULL)
|
|
{
|
|
deleteList((CELL *)sPtr->contents);
|
|
sPtr->contents = (UINT)copyCell(nilCell);
|
|
}
|
|
}
|
|
|
|
|
|
saveSymbols((SYMBOL *)((CELL*)currentContext->contents)->aux, device);
|
|
|
|
if(currentContext != mainContext)
|
|
varPrintf(device, "%s(context MAIN)%s%s",
|
|
LINE_FEED, LINE_FEED, LINE_FEED);
|
|
|
|
currentContext = contextSave;
|
|
}
|
|
|
|
|
|
void saveSymbols(SYMBOL * sPtr, UINT device)
|
|
{
|
|
int type;
|
|
|
|
if(sPtr != NIL_SYM && sPtr != NULL)
|
|
{
|
|
saveSymbols(sPtr->left, device);
|
|
type = symbolType(sPtr);
|
|
if(type == CELL_CONTEXT)
|
|
{
|
|
if(sPtr == (SYMBOL *)((CELL *)sPtr->contents)->contents)
|
|
{
|
|
if(sPtr != currentContext && *sPtr->name != '$') saveContext(sPtr, device);
|
|
}
|
|
else printSymbol(sPtr, device);
|
|
}
|
|
/* don't save primitives, symbols containing nil and the trueSymbol */
|
|
else if(type != CELL_PRIMITIVE && type != CELL_NIL
|
|
&& sPtr != trueSymbol && type != CELL_IMPORT_CDECL && type != CELL_IMPORT_FFI
|
|
#if defined(WINDOWS) || defined(CYGWIN)
|
|
&& type != CELL_IMPORT_DLL
|
|
#endif
|
|
)
|
|
if(*sPtr->name != '$') printSymbol(sPtr, device);
|
|
saveSymbols(sPtr->right, device);
|
|
}
|
|
}
|
|
|
|
|
|
CELL * p_save(CELL * params)
|
|
{
|
|
char * fileName;
|
|
STREAM strStream = {NULL, NULL, 0, 0, 0};
|
|
SYMBOL * contextSave;
|
|
#ifndef EMSCRIPTEN
|
|
CELL * result;
|
|
CELL * dataCell;
|
|
#endif
|
|
int errorFlag = 0;
|
|
|
|
contextSave = currentContext;
|
|
currentContext = mainContext;
|
|
|
|
params = getString(params, &fileName);
|
|
|
|
openStrStream(&strStream, MAX_STRING, 0);
|
|
serializeSymbols(params, (UINT)&strStream);
|
|
|
|
#ifndef EMSCRIPTEN
|
|
/* check for URL format */
|
|
if(my_strnicmp(fileName, "http://", 7) == 0)
|
|
{
|
|
dataCell = stuffString(strStream.buffer);
|
|
result = getPutPostDeleteUrl(fileName, dataCell, HTTP_PUT, CONNECT_TIMEOUT);
|
|
pushResult(result);
|
|
deleteList(dataCell);
|
|
errorFlag = (strncmp((char *)result->contents, "ERR:", 4) == 0);
|
|
}
|
|
else
|
|
#endif
|
|
errorFlag = writeFile(fileName, strStream.buffer, strStream.position, "w");
|
|
|
|
closeStrStream(&strStream);
|
|
|
|
currentContext = contextSave;
|
|
|
|
if(errorFlag)
|
|
return(errorProcExt2(ERR_SAVING_FILE, stuffString(fileName)));
|
|
|
|
return(trueCell);
|
|
}
|
|
|
|
void serializeSymbols(CELL * params, UINT device)
|
|
{
|
|
SYMBOL * sPtr;
|
|
|
|
if(params->type == CELL_NIL)
|
|
saveSymbols((SYMBOL *)((CELL*)currentContext->contents)->aux, device);
|
|
else
|
|
while(params != nilCell)
|
|
{
|
|
params = getSymbol(params, &sPtr);
|
|
if(symbolType(sPtr) == CELL_CONTEXT)
|
|
saveContext((SYMBOL*)((CELL *)sPtr->contents)->contents, device);
|
|
else
|
|
printSymbol(sPtr, device);
|
|
}
|
|
}
|
|
|
|
/* ----------------------- copy a context with 'new' -------------- */
|
|
static SYMBOL * fromContext;
|
|
static SYMBOL * toContext;
|
|
static int overWriteFlag;
|
|
|
|
CELL * copyContextList(CELL * cell);
|
|
UINT * copyContextArray(CELL * array);
|
|
|
|
|
|
CELL * copyContextCell(CELL * cell)
|
|
{
|
|
CELL * newCell;
|
|
SYMBOL * sPtr;
|
|
SYMBOL * newSptr;
|
|
|
|
if(firstFreeCell == NULL) allocBlock();
|
|
newCell = firstFreeCell;
|
|
firstFreeCell = newCell->next;
|
|
++cellCount;
|
|
|
|
newCell->type = cell->type;
|
|
newCell->next = nilCell;
|
|
newCell->aux = cell->aux;
|
|
newCell->contents = cell->contents;
|
|
|
|
if(cell->type == CELL_DYN_SYMBOL)
|
|
{
|
|
sPtr = (SYMBOL*)cell->aux;
|
|
if(sPtr->context == fromContext)
|
|
newCell->aux =
|
|
(UINT)translateCreateSymbol(sPtr->name, 0, toContext, TRUE);
|
|
newCell->contents = (UINT)allocMemory(strlen((char *)cell->contents) + 1);
|
|
memcpy((void *)newCell->contents,
|
|
(void*)cell->contents, strlen((char *)cell->contents) + 1);
|
|
}
|
|
|
|
if(cell->type == CELL_SYMBOL)
|
|
{
|
|
/* if the cell copied, itself contains a symbol copy it recursevely,
|
|
if new, if not done here it might not been seen as new later and left
|
|
without contents */
|
|
sPtr = (SYMBOL *)cell->contents;
|
|
/* don't copy symbols of builtins and libffi */
|
|
if(sPtr->context == fromContext && !(sPtr->flags & (SYMBOL_BUILTIN | SYMBOL_FFI)))
|
|
{
|
|
if((newSptr = lookupSymbol(sPtr->name, toContext)) == NULL)
|
|
{
|
|
newSptr = translateCreateSymbol(sPtr->name, symbolType(sPtr), toContext, TRUE);
|
|
deleteList((CELL *)newSptr->contents);
|
|
newSptr->contents = (UINT)copyContextCell((CELL*)sPtr->contents);
|
|
}
|
|
newCell->contents = (UINT)newSptr;
|
|
newSptr->flags = sPtr->flags;
|
|
}
|
|
}
|
|
|
|
if(isEnvelope(cell->type))
|
|
{
|
|
if(cell->type == CELL_ARRAY)
|
|
newCell->contents = (UINT)copyContextArray(cell);
|
|
else
|
|
{
|
|
/* undo push last optimization */
|
|
newCell->aux = (UINT)nilCell;
|
|
newCell->contents = (UINT)copyContextList((CELL *)cell->contents);
|
|
}
|
|
}
|
|
else if(cell->type == CELL_STRING)
|
|
{
|
|
newCell->contents = (UINT)allocMemory((UINT)cell->aux);
|
|
memcpy((void *)newCell->contents, (void*)cell->contents, (UINT)cell->aux);
|
|
}
|
|
|
|
return(newCell);
|
|
}
|
|
|
|
|
|
CELL * copyContextList(CELL * cell)
|
|
{
|
|
CELL * firstCell;
|
|
CELL * newCell;
|
|
|
|
if(cell == nilCell || cell == trueCell) return(cell);
|
|
|
|
firstCell = newCell = copyContextCell(cell);
|
|
|
|
while((cell = cell->next) != nilCell)
|
|
{
|
|
newCell->next = copyContextCell(cell);
|
|
newCell = newCell->next;
|
|
}
|
|
|
|
return(firstCell);
|
|
}
|
|
|
|
|
|
UINT * copyContextArray(CELL * array)
|
|
{
|
|
CELL * * newAddr;
|
|
CELL * * orgAddr;
|
|
CELL * * addr;
|
|
size_t size;
|
|
|
|
addr = newAddr = (CELL * *)callocMemory(array->aux);
|
|
|
|
size = (array->aux - 1) / sizeof(UINT);
|
|
orgAddr = (CELL * *)array->contents;
|
|
|
|
while(size--)
|
|
*(newAddr++) = copyContextCell(*(orgAddr++));
|
|
|
|
return((UINT*)addr);
|
|
}
|
|
|
|
|
|
void iterateCopyCreateSymbols(SYMBOL * sPtr)
|
|
{
|
|
int type, newFlag = FALSE;
|
|
SYMBOL * newPtr = NULL;
|
|
|
|
if(sPtr != NIL_SYM && sPtr != NULL && !(sPtr->flags & SYMBOL_BUILTIN))
|
|
{
|
|
iterateCopyCreateSymbols(sPtr->left);
|
|
type = symbolType(sPtr);
|
|
|
|
/* optimized check for default symbol, translate default symbol to default symbol */
|
|
if(*sPtr->name == *fromContext->name && strcmp(sPtr->name, fromContext->name) == 0)
|
|
{
|
|
if((newPtr = lookupSymbol(toContext->name, toContext)) == NULL)
|
|
{
|
|
newPtr = translateCreateSymbol(toContext->name, type, toContext, TRUE);
|
|
newFlag = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((newPtr = lookupSymbol(sPtr->name, toContext)) == NULL)
|
|
{
|
|
newPtr = translateCreateSymbol(sPtr->name, type, toContext, TRUE);
|
|
newFlag = TRUE;
|
|
}
|
|
}
|
|
|
|
if(overWriteFlag == TRUE || newFlag == TRUE)
|
|
{
|
|
deleteList((CELL *)newPtr->contents);
|
|
newPtr->contents = (UINT)copyContextCell((CELL*)sPtr->contents);
|
|
}
|
|
|
|
newPtr->flags |= sPtr->flags & SYMBOL_PROTECTED;
|
|
iterateCopyCreateSymbols(sPtr->right);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CELL * p_new(CELL * params)
|
|
{
|
|
CELL * next;
|
|
|
|
overWriteFlag = FALSE;
|
|
|
|
params = getContext(params, &fromContext);
|
|
if(!fromContext) return(nilCell); /* for debug mode */
|
|
|
|
next = params->next;
|
|
|
|
if(params == nilCell)
|
|
toContext = currentContext;
|
|
else
|
|
{
|
|
params = evaluateExpression(params);
|
|
if(params->type == CELL_CONTEXT || params->type == CELL_SYMBOL)
|
|
toContext = (SYMBOL *)params->contents;
|
|
else
|
|
return(errorProcExt(ERR_CONTEXT_EXPECTED, params));
|
|
|
|
overWriteFlag = (evaluateExpression(next)->type != CELL_NIL);
|
|
|
|
/* allow symbols to be converted to contexts */
|
|
if(symbolType(toContext) != CELL_CONTEXT)
|
|
{
|
|
if(isProtected(toContext->flags))
|
|
return(errorProcExt(ERR_SYMBOL_PROTECTED, params));
|
|
|
|
if(toContext->context != mainContext)
|
|
return(errorProcExt2(ERR_NOT_IN_MAIN, stuffSymbol(toContext)));
|
|
|
|
deleteList((CELL *)toContext->contents);
|
|
makeContextFromSymbol(toContext, NULL);
|
|
}
|
|
}
|
|
|
|
if(toContext == mainContext)
|
|
return(errorProc(ERR_TARGET_NO_MAIN));
|
|
|
|
iterateCopyCreateSymbols((SYMBOL *)((CELL*)fromContext->contents)->aux);
|
|
|
|
return(copyCell((CELL*)toContext->contents));
|
|
}
|
|
|
|
|
|
CELL * p_defineNew(CELL * params)
|
|
{
|
|
SYMBOL * sourcePtr;
|
|
SYMBOL * targetPtr;
|
|
char * name;
|
|
|
|
params = getSymbol(params, &sourcePtr);
|
|
if(params != nilCell)
|
|
{
|
|
getSymbol(params, &targetPtr);
|
|
name = targetPtr->name;
|
|
toContext = targetPtr->context;
|
|
}
|
|
else
|
|
{
|
|
name = sourcePtr->name;
|
|
toContext = currentContext;
|
|
}
|
|
|
|
if(toContext == mainContext)
|
|
return(errorProc(ERR_TARGET_NO_MAIN));
|
|
|
|
fromContext = sourcePtr->context;
|
|
targetPtr = translateCreateSymbol(name, symbolType(sourcePtr), toContext, TRUE);
|
|
|
|
deleteList((CELL *)targetPtr->contents);
|
|
targetPtr->contents = (UINT)copyContextCell((CELL*)sourcePtr->contents);
|
|
|
|
targetPtr->flags = sourcePtr->flags;
|
|
|
|
return(stuffSymbol(targetPtr));
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------ system ------------------------------ */
|
|
|
|
CELL * isType(CELL *, int);
|
|
|
|
CELL * p_isNil(CELL * params)
|
|
{
|
|
params = evaluateExpression(params);
|
|
|
|
if(isNil(params))
|
|
return(trueCell);
|
|
|
|
return(nilCell);
|
|
}
|
|
|
|
CELL * p_isEmpty(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
|
|
getEvalDefault(params, &cell);
|
|
return(isEmptyFunc(cell));
|
|
}
|
|
|
|
CELL * isEmptyFunc(CELL * cell)
|
|
{
|
|
if(cell->type == CELL_STRING)
|
|
{
|
|
if(*(char*)cell->contents == 0)
|
|
return(trueCell);
|
|
else return(nilCell);
|
|
}
|
|
|
|
if(!isList(cell->type))
|
|
return(errorProcExt(ERR_LIST_OR_STRING_EXPECTED, cell));
|
|
if(cell->contents == (UINT)nilCell)
|
|
return(trueCell);
|
|
return(nilCell);
|
|
}
|
|
|
|
CELL * isZero(CELL * cell)
|
|
{
|
|
#ifdef BIGINT
|
|
int * numPtr;
|
|
#endif
|
|
|
|
switch(cell->type)
|
|
{
|
|
#ifndef NEWLISP64
|
|
case CELL_INT64:
|
|
if(*(INT64 *)&cell->aux == 0)
|
|
return(trueCell);
|
|
break;
|
|
#endif
|
|
case CELL_FLOAT:
|
|
#ifndef NEWLISP64
|
|
if(*(double *)&cell->aux == 0.0)
|
|
#else
|
|
if(*(double *)&cell->contents == 0.0)
|
|
#endif
|
|
return(trueCell);
|
|
break;
|
|
case CELL_LONG:
|
|
if(cell->contents == 0)
|
|
return(trueCell);
|
|
break;
|
|
#ifdef BIGINT
|
|
case CELL_BIGINT:
|
|
numPtr = (int *)(UINT)cell->contents;
|
|
if(cell->aux == 2 && numPtr[1] == 0)
|
|
return(trueCell);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(nilCell);
|
|
}
|
|
|
|
|
|
CELL * p_isNull(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
|
|
cell = evaluateExpression(params);
|
|
if(isNil(cell))
|
|
return(trueCell);
|
|
|
|
if( (cell->type == CELL_STRING || isList(cell->type)))
|
|
return(isEmptyFunc(cell));
|
|
|
|
#ifndef NEWLISP64
|
|
if(cell->type == CELL_FLOAT && (isnan(*(double *)&cell->aux)) )
|
|
#else
|
|
if(cell->type == CELL_FLOAT && (isnan(*(double *)&cell->contents)))
|
|
#endif
|
|
return(trueCell);
|
|
|
|
return(isZero(cell));
|
|
}
|
|
|
|
CELL * p_isZero(CELL * params)
|
|
{
|
|
return(isZero(evaluateExpression(params)));
|
|
}
|
|
|
|
|
|
CELL * p_isTrue(CELL * params)
|
|
{
|
|
params = evaluateExpression(params);
|
|
if(!isNil(params) && !isEmpty(params))
|
|
return(trueCell);
|
|
|
|
return(nilCell);
|
|
}
|
|
|
|
CELL * p_isInteger(CELL * params)
|
|
{
|
|
params = evaluateExpression(params);
|
|
if((params->type & COMPARE_TYPE_MASK) == CELL_INT)
|
|
return(trueCell);
|
|
return(nilCell);
|
|
}
|
|
|
|
#ifdef BIGINT
|
|
CELL * p_isBigInteger(CELL * params)
|
|
{ return(isType(params, CELL_BIGINT)); }
|
|
#endif
|
|
|
|
CELL * p_isFloat(CELL * params)
|
|
{ return(isType(params, CELL_FLOAT)); }
|
|
|
|
CELL * p_isNumber(CELL * params)
|
|
{
|
|
params = evaluateExpression(params);
|
|
if(isNumber(params->type)) return(trueCell);
|
|
return(nilCell);
|
|
}
|
|
|
|
CELL * p_isString(CELL * params)
|
|
{ return(isType(params, CELL_STRING)); }
|
|
|
|
CELL * p_isSymbol(CELL * params)
|
|
{ return(isType(params, CELL_SYMBOL)); }
|
|
|
|
CELL * p_isContext(CELL * params)
|
|
{
|
|
char * symStr;
|
|
SYMBOL * ctx;
|
|
|
|
/* check type */
|
|
if(params->next == nilCell)
|
|
return(isType(params, CELL_CONTEXT));
|
|
|
|
/* check for existense of symbol */
|
|
params = getContext(params, &ctx);
|
|
if(!ctx) return(nilCell); /* for debug mode */
|
|
getString(params, &symStr);
|
|
|
|
return (lookupSymbol(symStr, ctx) ? trueCell : nilCell);
|
|
}
|
|
|
|
CELL * p_isPrimitive(CELL * params)
|
|
{ return(isType(params, CELL_PRIMITIVE)); }
|
|
|
|
|
|
CELL * p_isGlobal(CELL * params)
|
|
{
|
|
params = evaluateExpression(params);
|
|
if(isSymbol(params->type) && isGlobal(((SYMBOL *)params->contents)->flags))
|
|
return(trueCell);
|
|
return(nilCell);
|
|
}
|
|
|
|
CELL * p_isProtected(CELL * params)
|
|
{
|
|
params = evaluateExpression(params);
|
|
if(isSymbol(params->type) && isProtected(((SYMBOL *)params->contents)->flags))
|
|
return(trueCell);
|
|
return(nilCell);
|
|
}
|
|
|
|
CELL * p_isAtom(CELL * params)
|
|
{
|
|
if(params == nilCell)
|
|
return(errorProc(ERR_MISSING_ARGUMENT));
|
|
params = evaluateExpression(params);
|
|
if(params->type & ENVELOPE_TYPE_MASK) return(nilCell);
|
|
return(trueCell);
|
|
}
|
|
|
|
CELL * p_isQuote(CELL *params)
|
|
{ return(isType(params, CELL_QUOTE)); }
|
|
|
|
CELL * p_isList(CELL * params)
|
|
{ return(isType(params, CELL_EXPRESSION)); }
|
|
|
|
CELL * p_isLambda(CELL * params)
|
|
{ return(isType(params, CELL_LAMBDA)); }
|
|
|
|
CELL * p_isMacro(CELL * params)
|
|
{
|
|
SYMBOL * sPtr;
|
|
|
|
if(params == nilCell)
|
|
return(errorProc(ERR_MISSING_ARGUMENT));
|
|
params = evaluateExpression(params);
|
|
if(params->type == CELL_FEXPR) /* lambda-macro */
|
|
return(trueCell);
|
|
if(params->type == CELL_SYMBOL)
|
|
{
|
|
sPtr = (SYMBOL *)params->contents;
|
|
if(sPtr->flags & SYMBOL_MACRO)
|
|
return(trueCell);
|
|
}
|
|
return(nilCell);
|
|
}
|
|
|
|
CELL * p_isArray(CELL * params)
|
|
{ return(isType(params, CELL_ARRAY)); }
|
|
|
|
CELL * isType(CELL * params, int operand)
|
|
{
|
|
CELL * contextCell;
|
|
|
|
if(params == nilCell)
|
|
return(errorProc(ERR_MISSING_ARGUMENT));
|
|
params = evaluateExpression(params);
|
|
if((UINT)operand == params->type) return(trueCell);
|
|
switch(operand)
|
|
{
|
|
case CELL_PRIMITIVE:
|
|
if(params->type == CELL_IMPORT_CDECL
|
|
|| params->type == CELL_IMPORT_FFI
|
|
#if defined(WINDOWS) || defined(CYGWIN)
|
|
|| params->type == CELL_IMPORT_DLL
|
|
#endif
|
|
)
|
|
return(trueCell);
|
|
break;
|
|
case CELL_EXPRESSION:
|
|
if(isList(params->type)) return(trueCell);
|
|
break;
|
|
case CELL_SYMBOL:
|
|
if(params->type == CELL_DYN_SYMBOL) /* check if already created */
|
|
{
|
|
contextCell = (CELL *)((SYMBOL *)params->aux)->contents;
|
|
if(contextCell->type != CELL_CONTEXT)
|
|
fatalError(ERR_CONTEXT_EXPECTED,
|
|
stuffSymbol((SYMBOL*)params->aux), TRUE);
|
|
if(lookupSymbol((char *)params->contents, (SYMBOL*)contextCell->contents))
|
|
return(trueCell);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(nilCell);
|
|
}
|
|
|
|
|
|
CELL * p_isLegal(CELL * params)
|
|
{
|
|
char * symStr;
|
|
|
|
getString(params, &symStr);
|
|
|
|
if(isLegalSymbol(symStr)) return(trueCell);
|
|
|
|
return(nilCell);
|
|
}
|
|
|
|
|
|
int isLegalSymbol(char * source)
|
|
{
|
|
STREAM stream;
|
|
char token[MAX_SYMBOL + 1];
|
|
int tklen;
|
|
|
|
if(*source == (char)'"' || *source == (char)'{'
|
|
|| (unsigned char)*source <= (unsigned char)' ' || *source == (char)';' || *source == (char)'#')
|
|
return(0);
|
|
|
|
makeStreamFromString(&stream, source);
|
|
|
|
return(getToken(&stream, token, &tklen) == TKN_SYMBOL && tklen == stream.size - 4 * MAX_STRING);
|
|
}
|
|
|
|
CELL * p_exit(CELL * params)
|
|
{
|
|
UINT result;
|
|
|
|
#ifndef EMSCRIPTEN
|
|
if(daemonMode)
|
|
{
|
|
fclose(IOchannel);
|
|
#ifndef WINDOWS
|
|
IOchannel = NULL;
|
|
#endif
|
|
longjmp(errorJump, ERR_USER_RESET);
|
|
}
|
|
#else
|
|
return(nilCell);
|
|
#endif
|
|
|
|
if(params != nilCell) getInteger(params, &result);
|
|
else result = 0;
|
|
|
|
#ifdef HAVE_FORK
|
|
/* release spawn resources */
|
|
purgeSpawnList(TRUE);
|
|
#endif
|
|
|
|
exit(result);
|
|
return(trueCell);
|
|
}
|
|
|
|
|
|
#ifdef EMSCRIPTEN
|
|
void emscriptenReload(void)
|
|
{
|
|
char * cmd = "location.reload();";
|
|
printf("# newLISP is reloading ...\n");
|
|
evalStringJS(cmd, strlen(cmd));
|
|
}
|
|
#endif
|
|
|
|
|
|
CELL * p_reset(CELL * params)
|
|
{
|
|
int blockCountBefore = blockCount;
|
|
|
|
if(params != nilCell)
|
|
{
|
|
params = evaluateExpression(params);
|
|
if(isNumber(params->type))
|
|
{
|
|
getIntegerExt(params, (UINT*)&MAX_CELL_COUNT, FALSE);
|
|
if(MAX_CELL_COUNT < MAX_BLOCK) MAX_CELL_COUNT = MAX_BLOCK;
|
|
return(stuffInteger(MAX_CELL_COUNT));
|
|
}
|
|
else if(isNil(params))
|
|
{
|
|
freeCellBlocks();
|
|
return(stuffIntegerList(2, blockCountBefore, blockCount)); /* 10.3.3 */
|
|
}
|
|
#ifndef LIBRARY
|
|
#ifndef WINDOWS
|
|
else
|
|
execv(MainArgs[0], MainArgs);
|
|
#endif
|
|
#endif
|
|
#ifdef EMSCRIPTEN
|
|
emscriptenReload();
|
|
#endif
|
|
}
|
|
else
|
|
#ifndef EMSCRIPTEN
|
|
longjmp(errorJump, ERR_USER_RESET);
|
|
#else
|
|
return(nilCell);
|
|
#endif
|
|
|
|
return(trueCell);
|
|
}
|
|
|
|
CELL * setEvent(CELL * params, SYMBOL * * eventSymPtr, char * sysSymName)
|
|
{
|
|
if(params != nilCell) getCreateSymbol(params, eventSymPtr, sysSymName);
|
|
return(makeCell(CELL_SYMBOL, (UINT)*eventSymPtr));
|
|
}
|
|
|
|
CELL * p_errorEvent(CELL * params)
|
|
{
|
|
return(setEvent(params, &errorEvent, "$error-event"));
|
|
}
|
|
|
|
CELL * p_promptEvent(CELL * params)
|
|
{
|
|
return(setEvent(params, &promptEvent, "$prompt-event"));
|
|
}
|
|
|
|
CELL * p_commandEvent(CELL * params)
|
|
{
|
|
return(setEvent(params, &commandEvent, "$command-event"));
|
|
}
|
|
|
|
CELL * p_transferEvent(CELL * params)
|
|
{
|
|
return(setEvent(params, &transferEvent, "$transfer-event"));
|
|
}
|
|
|
|
CELL * p_readerEvent(CELL * params)
|
|
{
|
|
return(setEvent(params, &readerEvent, "$reader-event"));
|
|
}
|
|
|
|
|
|
#ifndef WINDOWS
|
|
|
|
CELL * p_timerEvent(CELL * params)
|
|
{
|
|
double seconds;
|
|
UINT timerOption = 0;
|
|
struct itimerval timerVal;
|
|
struct itimerval outVal;
|
|
static double duration;
|
|
|
|
if(params != nilCell)
|
|
{
|
|
params = getCreateSymbol(params, &timerEvent, "$timer");
|
|
|
|
if(params != nilCell)
|
|
{
|
|
params = getFloat(params, &seconds);
|
|
duration = seconds;
|
|
if(params != nilCell)
|
|
getInteger(params, &timerOption);
|
|
memset(&timerVal, 0, sizeof(timerVal));
|
|
timerVal.it_value.tv_sec = seconds;
|
|
timerVal.it_value.tv_usec = (seconds - timerVal.it_value.tv_sec) * 1000000;
|
|
if(setitimer((int)timerOption, &timerVal, &outVal) == -1)
|
|
return(nilCell);
|
|
return(stuffInteger(0));
|
|
}
|
|
else
|
|
getitimer(timerOption, &outVal);
|
|
|
|
seconds = duration - (outVal.it_value.tv_sec + outVal.it_value.tv_usec / 1000000.0);
|
|
return(stuffFloat(seconds));
|
|
}
|
|
|
|
return(makeCell(CELL_SYMBOL, (UINT)timerEvent));
|
|
}
|
|
#endif
|
|
|
|
#ifndef EMSCRIPTEN
|
|
#define IGNORE_S 0
|
|
#define DEFAULT_S 1
|
|
#define RESET_S 2
|
|
CELL * p_signal(CELL * params)
|
|
{
|
|
SYMBOL * signalEvent;
|
|
UINT sig;
|
|
char sigStr[12];
|
|
char mode;
|
|
|
|
params = getInteger(params, &sig);
|
|
if(sig > 32 || sig < 1) return(nilCell);
|
|
|
|
if(params->type == CELL_STRING)
|
|
{
|
|
mode = toupper(*(char *)params->contents);
|
|
symHandler[sig - 1] = nilSymbol;
|
|
if(mode == 'I') /* "ignore" */
|
|
return(signal(sig, SIG_IGN) == SIG_ERR ? nilCell: trueCell);
|
|
else if(mode == 'D') /* "default" */
|
|
return(signal(sig, SIG_DFL) == SIG_ERR ? nilCell: trueCell);
|
|
else if(mode == 'R') /* "reset" */
|
|
return(signal(sig, signal_handler) == SIG_ERR ? nilCell: trueCell);
|
|
}
|
|
else if(params != nilCell)
|
|
{
|
|
snprintf(sigStr, 11, "$signal-%d", (int)sig);
|
|
getCreateSymbol(params, &signalEvent, sigStr);
|
|
symHandler[sig - 1] = signalEvent;
|
|
if(signal(sig, signal_handler) == SIG_ERR) return(nilCell);
|
|
}
|
|
|
|
return(makeCell(CELL_SYMBOL, (UINT)symHandler[sig - 1]));
|
|
}
|
|
#endif
|
|
|
|
CELL * p_lastError(CELL * params)
|
|
{
|
|
CELL * result;
|
|
char * sPtr;
|
|
UINT errNum = errorReg;
|
|
|
|
if(params != nilCell)
|
|
getInteger(params, &errNum);
|
|
|
|
if(!errNum) return(nilCell);
|
|
|
|
result = makeCell(CELL_EXPRESSION, (UINT)stuffInteger(errNum));
|
|
if(params != nilCell)
|
|
sPtr = (errNum > MAX_ERROR_NUMBER) ? UNKNOWN_ERROR : errorMessage[errNum];
|
|
else
|
|
sPtr = errorStream.buffer;
|
|
|
|
((CELL *)result->contents)->next = stuffString(sPtr);
|
|
|
|
return(result);
|
|
}
|
|
|
|
|
|
CELL * p_dump(CELL * params)
|
|
{
|
|
CELL * blockPtr;
|
|
CELL * cell;
|
|
UINT count = 0;
|
|
int i;
|
|
|
|
if(params != nilCell)
|
|
{
|
|
cell = evaluateExpression(params);
|
|
return(stuffIntegerList
|
|
(5, cell, cell->type, cell->next, cell->aux, cell->contents));
|
|
}
|
|
|
|
blockPtr = cellMemory;
|
|
while(blockPtr != NULL)
|
|
{
|
|
for(i = 0; i < MAX_BLOCK; i++)
|
|
{
|
|
if(*(UINT *)blockPtr != CELL_FREE)
|
|
{
|
|
varPrintf(OUT_DEVICE, "address=%lX type=%d contents=", blockPtr, blockPtr->type);
|
|
printCell(blockPtr, TRUE, OUT_DEVICE);
|
|
varPrintf(OUT_DEVICE, LINE_FEED);
|
|
++count;
|
|
}
|
|
++blockPtr;
|
|
}
|
|
blockPtr = blockPtr->next;
|
|
}
|
|
|
|
return(stuffInteger(count));
|
|
}
|
|
|
|
|
|
CELL * p_mainArgs(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
ssize_t idx;
|
|
|
|
cell = (CELL*)mainArgsSymbol->contents;
|
|
if(params != nilCell && cell->type == CELL_EXPRESSION)
|
|
{
|
|
getInteger(params, (UINT *)&idx);
|
|
cell = (CELL *)cell->contents;
|
|
if(idx < 0) idx = convertNegativeOffset(idx, (CELL *)cell);
|
|
while(idx--) cell = cell->next;
|
|
}
|
|
|
|
return(copyCell(cell));
|
|
}
|
|
|
|
|
|
CELL * p_context(CELL * params)
|
|
{
|
|
CELL * cell;
|
|
SYMBOL * sPtr;
|
|
SYMBOL * cPtr;
|
|
char * newSymStr;
|
|
|
|
if(params->type == CELL_NIL)
|
|
return(copyCell((CELL *)currentContext->contents));
|
|
|
|
if((cPtr = getCreateContext(params, TRUE)) == NULL)
|
|
return(errorProcExt(ERR_SYMBOL_OR_CONTEXT_EXPECTED, params));
|
|
|
|
if(params->next == nilCell)
|
|
{
|
|
currentContext = cPtr;
|
|
return(copyCell( (CELL *)currentContext->contents));
|
|
}
|
|
|
|
params = params->next;
|
|
cell = evaluateExpression(params);
|
|
if(cell->type == CELL_STRING)
|
|
newSymStr = (char *)cell->contents;
|
|
else if(cell->type == CELL_SYMBOL)
|
|
newSymStr = ((SYMBOL *)cell->contents)->name;
|
|
else if(cell->type == CELL_DYN_SYMBOL)
|
|
{
|
|
sPtr = getDynamicSymbol(cell);
|
|
newSymStr = sPtr->name;
|
|
}
|
|
else
|
|
return(errorProcExt(ERR_ILLEGAL_TYPE, cell));
|
|
|
|
|
|
if(params->next == nilCell)
|
|
{
|
|
pushResultFlag = FALSE;
|
|
sPtr = lookupSymbol(newSymStr, cPtr);
|
|
if(sPtr == NULL)
|
|
return(nilCell);
|
|
else
|
|
return((CELL *)sPtr->contents);
|
|
}
|
|
|
|
|
|
sPtr = translateCreateSymbol(newSymStr, CELL_NIL, cPtr, TRUE);
|
|
|
|
return(setDefine(sPtr, params->next, SET_SET));
|
|
}
|
|
|
|
|
|
SYMBOL * getCreateContext(CELL * cell, int evaluate)
|
|
{
|
|
SYMBOL * contextSymbol;
|
|
|
|
if(evaluate)
|
|
cell = evaluateExpression(cell);
|
|
if(cell->type == CELL_SYMBOL || cell->type == CELL_CONTEXT)
|
|
contextSymbol = (SYMBOL *)cell->contents;
|
|
else
|
|
return(NULL);
|
|
|
|
|
|
if(symbolType(contextSymbol) != CELL_CONTEXT)
|
|
{
|
|
if(contextSymbol->context != mainContext)
|
|
{
|
|
contextSymbol= translateCreateSymbol(
|
|
contextSymbol->name, CELL_CONTEXT, mainContext, 1);
|
|
}
|
|
|
|
if(symbolType(contextSymbol) != CELL_CONTEXT)
|
|
{
|
|
if(isProtected(contextSymbol->flags))
|
|
errorProcExt2(ERR_SYMBOL_PROTECTED, stuffSymbol(contextSymbol));
|
|
|
|
deleteList((CELL *)contextSymbol->contents);
|
|
makeContextFromSymbol(contextSymbol, NULL);
|
|
}
|
|
}
|
|
|
|
/* if this is a context var retrieve the real context symbol */
|
|
return((SYMBOL *)((CELL *)contextSymbol->contents)->contents);
|
|
}
|
|
|
|
|
|
CELL * p_default(CELL * params)
|
|
{
|
|
SYMBOL * contextSymbol;
|
|
|
|
getContext(params, &contextSymbol);
|
|
|
|
symbolCheck = translateCreateSymbol(contextSymbol->name, CELL_NIL, contextSymbol, TRUE);
|
|
pushResultFlag = FALSE;
|
|
return((CELL *)symbolCheck->contents);
|
|
}
|
|
|
|
|
|
/* FOOP fuctions */
|
|
|
|
/* filled in colon, only used internally and by obj function */
|
|
/* need stack for objSymbol.contents */
|
|
/* what happens to stack when using catch/throw */
|
|
|
|
CELL * p_colon(CELL * params)
|
|
{
|
|
SYMBOL * contextSymbol = NULL;
|
|
SYMBOL * methodSymbol;
|
|
SYMBOL * sPtr;
|
|
CELL * proc;
|
|
CELL * cell;
|
|
CELL * obj;
|
|
CELL * objSave;
|
|
CELL * objCellSave;
|
|
SYMBOL * objSymbolContextSave;
|
|
int objSymbolFlagsSave;
|
|
|
|
if(params->type != CELL_SYMBOL)
|
|
return(errorProcExt(ERR_SYMBOL_EXPECTED, params));
|
|
|
|
methodSymbol = (SYMBOL *)params->contents;
|
|
params = getEvalDefault(params->next, &obj);
|
|
|
|
objSymbolFlagsSave = objSymbol.flags;
|
|
objSymbolContextSave = objSymbol.context;
|
|
if(symbolCheck)
|
|
{
|
|
objSymbol.flags = symbolCheck->flags;
|
|
objSymbol.context = symbolCheck->context;
|
|
}
|
|
|
|
objSave = (CELL *)objSymbol.contents;
|
|
objCellSave = objCell;
|
|
objCell = obj;
|
|
|
|
#ifdef FOOP_DEBUG
|
|
printf("entering colon, saving in objSave:");
|
|
printCell(objSave, TRUE, OUT_CONSOLE);
|
|
puts("");
|
|
#endif
|
|
|
|
cell = (CELL *)obj->contents;
|
|
if(obj->type != CELL_EXPRESSION)
|
|
return(errorProcExt(ERR_LIST_EXPECTED, obj));
|
|
|
|
if(cell->type == CELL_SYMBOL || cell->type == CELL_CONTEXT)
|
|
contextSymbol = (SYMBOL *)cell->contents;
|
|
if(contextSymbol == NULL || symbolType(contextSymbol) != CELL_CONTEXT)
|
|
return(errorProcExt(ERR_CONTEXT_EXPECTED, cell));
|
|
|
|
sPtr = methodSymbol;
|
|
if((methodSymbol = lookupSymbol(sPtr->name, contextSymbol)) == NULL)
|
|
return(errorProcExt2(ERR_INVALID_FUNCTION, stuffSymbol(sPtr)));
|
|
|
|
cell = stuffSymbol(methodSymbol);
|
|
proc = makeCell(CELL_EXPRESSION, (UINT)cell);
|
|
|
|
while(params != nilCell)
|
|
{
|
|
cell->next = copyCell(params);
|
|
cell = cell->next;
|
|
params = params->next;
|
|
}
|
|
|
|
pushResult(proc);
|
|
|
|
#ifdef FOOP_DEBUG
|
|
printf("colon calling %s in %s with objCell:", methodSymbol->name, contextSymbol->name);
|
|
printCell(objCell, TRUE, OUT_CONSOLE);
|
|
puts("");
|
|
#endif
|
|
|
|
cell = copyCell(evaluateExpression(proc));
|
|
|
|
objSymbol.flags = objSymbolFlagsSave;
|
|
objSymbol.context = objSymbolContextSave;
|
|
|
|
objSymbol.contents = (UINT)objSave;
|
|
objCell = objCellSave;
|
|
|
|
#ifdef FOOP_DEBUG
|
|
printf("leavin colon, objCell restored to:");
|
|
printCell(obj, TRUE, OUT_CONSOLE);
|
|
puts("");
|
|
#endif
|
|
|
|
|
|
return(cell);
|
|
}
|
|
|
|
CELL * p_self(CELL * params)
|
|
{
|
|
CELL * result;
|
|
|
|
if(objSymbol.contents == (UINT)nilCell)
|
|
return(nilCell);
|
|
|
|
if(params == nilCell)
|
|
{
|
|
symbolCheck = &objSymbol;
|
|
pushResultFlag = FALSE;
|
|
return((CELL *)objSymbol.contents);
|
|
}
|
|
|
|
result = implicitIndexList((CELL*)objSymbol.contents, params);
|
|
|
|
symbolCheck = &objSymbol;
|
|
pushResultFlag = FALSE;
|
|
|
|
return(result);
|
|
}
|
|
|
|
|
|
CELL * p_systemSymbol(CELL * params)
|
|
{
|
|
UINT idx;
|
|
|
|
getInteger(params, &idx);
|
|
|
|
if(idx > 15) return(nilCell);
|
|
|
|
return(copyCell((CELL*)sysSymbol[idx]->contents));
|
|
}
|
|
|
|
|
|
/* end of file */
|