/*
* lali (Lali Another Lisp Implementation)
*
* Author: Daniel Cerqueira (dan.git@lispclub.com)
* Maintainer: Daniel Cerqueira (dan.git@lispclub.com)
* Version: 0.0
* Keywords: lali, lisp, implementation, interpreter, lisp1.5,
* computer programming language
* Homepage: https://gitlab.com/alexandre1985/lali
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Copyright (C) 2025 Daniel Cerqueira
*
* This file is part of lali.
*
* lali 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 .
*
*
* lali software is based on tiny-lisp
* version from 2016, written by Matthias Pirstitz, which is released to public
* domain under Unlicense .
*/
#include "liblali.h"
#define YEARS "2025"
// MAIN ///////////////////////////////////////////////////////////////////////
void runFile(int infd, Object **env, GC_PARAM) {
Stream stream = { STREAM_TYPE_FILE, .fd = infd };
GC_TRACE(gcObject, nil);
if (setjmp(exceptionEnv))
return;
// deal with shebang
if (peekForward(&stream, true) == EOF)
return;
do {
*gcObject = nil;
*gcObject = readExpr(&stream, GC_ROOTS);
evalExpr(gcObject, env, GC_ROOTS);
} while (peekNext(&stream) != EOF);
}
void runREPL(int infd, Object **env, GC_PARAM) {
Stream stream = { STREAM_TYPE_FILE, .fd = infd };
GC_TRACE(gcObject, nil);
for (;;) {
if (setjmp(exceptionEnv))
continue;
for (;;) {
*gcObject = nil;
fputs("lali> ", stdout);
fflush(stdout);
if (peekNext(&stream) == EOF) {
fputc('\n', stdout);
return;
}
*gcObject = readExpr(&stream, GC_ROOTS);
*gcObject = evalExpr(gcObject, env, GC_ROOTS);
writeObject(*gcObject, true, stdout);
fputc('\n', stdout);
}
}
}
int main(int argc, char *argv[]) {
int options = 0;
bool repl = false;
bool close = false;
bool quiet = false;
if (argc >= 2) {
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "basic usages:\nnormal: %s [file]...\nclose stdin: %s -c [file]...\nrun repl: %s -r [-q] [file]...\n\nin normal mode, reads from standard input.\nwhen present, load file(s) at startup. can be used as library file(s).\n", argv[0], argv[0], argv[0]);
return EXIT_SUCCESS;
} else if (!strcmp(argv[1], "-c")) {
options++;
close = true;
} else if (!strcmp(argv[1], "-r")) {
options++;
repl = true;
if (argc > 2 && !strcmp(argv[2], "-q")) {
options++;
quiet = true;
}
} else if (!strcmp(argv[1], "-q")) {
options++;
quiet = true;
if (argc > 2 && !strcmp(argv[2], "-r")) {
options++;
repl = true;
}
} else if (argv[1][0] == '-') {
fprintf(stderr, "%s: unrecognized option, `%s'\n", argv[0], argv[1]);
return EXIT_FAILURE;
}
}
GC_PARAM = nil;
if (setjmp(exceptionEnv))
return EXIT_FAILURE;
symbols = nil;
symbols = newCons(&nil, &symbols, GC_ROOTS);
symbols = newCons(&n, &symbols, GC_ROOTS);
symbols = newCons(&t, &symbols, GC_ROOTS);
symbols = newCons(&f, &symbols, GC_ROOTS);
GC_TRACE(gcEnv, newRootEnv(GC_ROOTS));
int fd = 0;
if (argc > 1) {
for (int i = ++options; i < argc; i++) {
if ((fd = open(argv[i], O_RDONLY)) == -1) {
fprintf(stderr, "%s: open() failed, %s\n", argv[0], strerror(errno));
return EXIT_FAILURE;
}
runFile(fd, gcEnv, GC_ROOTS);
}
}
if (repl) {
if(!isatty(STDIN_FILENO)) {
fprintf(stderr, "%s: repl does not accept piping\n", argv[0]);
return EXIT_FAILURE;
}
if (!quiet)
fprintf(stdout, "lali Copyright (C) %s Daniel Cerqueira\n\nThis is free software: you are free to change and redistribute it,\nunder certain conditions.\nThis program comes with NO WARRANTY, to the extent permitted by law.\n", YEARS);
runREPL(STDIN_FILENO, gcEnv, GC_ROOTS);
}
else if (!close)
runFile(STDIN_FILENO, gcEnv, GC_ROOTS);
return EXIT_SUCCESS;
}