// This is a minimal wrapper for s7, intended to be usable as a // bootstrapper when assembling the code. // // If there are no arguments, then read from stdin. // If there is at least one argument, it is the name of a .scm file to be loaded. // That argument can be "-", to indicate reading from stdin. // No options are recognised here. // // Any other arguments (which are allowed to look like options) are put into *command-line*. // // This file is part of Beastie // SPDX-FileCopyrightText: 2023 Norman Gray // SPDX-License-Identifier: BSD-2-Clause #include #include // for exit() #include // for strcmp #include "s7.h" #include "util.h" #include "version.h" static const char version_string[] = "version " BEASTIE_VERSION ", " BEASTIE_RELEASEDATE " (s7 " S7_VERSION ")"; #include "readermacros.inc" void initialise_runtime(s7_scheme*); // from core.c static const char* progname; void beastie0_exit(void) { error_exit("beastie bootstrap program, %s\n" "Usage:\n" " %s [prog.scm arg...] (program can be \"-\")", version_string, progname); } void scheme_abort_s7(int lineno) { fprintf(stderr, "s7 abort: line %d\n", lineno); exit(1); } s7_scheme* S7; int main(int argc, char** argv) { S7 = s7_init(); s7_load_c_string(S7, readermacros_scm, readermacros_scm_len); const char* def_reader_macros = "(set! *#readers*" " `((#\\\" . ,dquotes-reader*)" " (#\\; . ,srfi62-comments)" " . ,*#readers*))"; s7_load_c_string(S7, def_reader_macros, strlen(def_reader_macros)); initialise_runtime(S7); progname = argv[0]; const char* scm_file = NULL; s7_pointer command_line_arguments = s7_nil(S7); for (int i=argc-1; i>=1; i--) { if (i == 1) { // treat argument one specially: this can be // * a file name to be read (saved into scm_file) // * the string "-" to indicate reading from stdin // * nothing else: if any option appears here, print an error message if (strcmp(argv[1], "-") == 0) { scm_file = NULL; // same as initial value } else if (argv[1][0] == '-') { beastie0_exit(); } else { scm_file = argv[1]; } } command_line_arguments = s7_cons(S7, s7_make_string(S7, argv[i]), command_line_arguments); } s7_define_constant(S7, "*command-line*", command_line_arguments); s7_pointer result; if (scm_file) { // The more obvious thing to do here is // int status = s7_load(S7, scm_file) ? 0 : 1; // but when evaluating scm_file, this results in (current-input-port) // being that file, which isn't useful when that file wants to use // (current-input-port) to refer to the stdin _here_. // // Return #f on success, or an error message if anything goes wrong. s7_define_constant(S7, "*input-file*", s7_make_string(S7, scm_file)); result = s7_eval_c_string(S7, "(catch #t" " (lambda ()" " (call-with-input-file *input-file*" " (let ((outenv (curlet)))" " (lambda (p)" " (let loop ((e (read p)))" " (unless (eof-object? e)" " (eval e outenv)" " (loop (read p)))))))" " #f)" " (lambda (tag msg)" " (apply format #f msg)))"); } else { s7_define_constant(S7, "*input-file*", s7_make_string(S7, "-")); result = s7_eval_c_string(S7, "(catch #t" " (lambda ()" " (let ((outenv (curlet)))" " (let loop ((e (read)))" " (cond ((eof-object? e) #f)" " (else" " (eval e outenv)" " (loop (read)))))))" " (lambda (tag msg)" " (apply format #f msg)))"); } int status; if (s7_boolean(S7, result)) { s7_display(S7, result, s7_current_error_port(S7)); fprintf(stderr, "\n"); status = 1; } else { status = 0; } exit(status); }