| For all practical purposes, any interpreter is pretty useless if it only works interactively. I have added a `source' built-in command to `sic_builtin.c' which takes lines of input from a file and evaluates them using `sic_repl.c' in much the same way as lines typed at the prompt are evaluated otherwise . Here is the built-in handler: | /* List of built in functions. */ #define builtin_functions \ BUILTIN(exit, 0, 1) \ BUILTIN(load, 1, 1) \ BUILTIN(source, 1, -1) \ BUILTIN(unload, 1, -1) BUILTIN_DECLARATION (source) { int status = SIC_OKAY; int i; for (i = 1; status == SIC_OKAY && argv[i]; ++i) status = source (sic, argv[i]); return status; } | And the source function from `sic_repl.c' : | int source (Sic *sic, const char *path) { FILE *stream; int result = SIC_OKAY; int save_interactive = is_interactive; SIC_ASSERT (sic && path); is_interactive = 0; if ((stream = fopen (path, "rt")) == NULL) { sic_result_clear (sic); sic_result_append (sic, "cannot source \"", path, "\": ", strerror (errno), NULL); result = SIC_ERROR; } else result = evalstream (sic, stream); is_interactive = save_interactive; return result; } | The reason for separating the source function in this way, is that it makes it easy for the startup sequence in main to evaluate a startup file. In traditional Unix fashion, the startup file is named `.sicrc' , and is evaluated if it is present in the user 's home directory: | static int evalsicrc (Sic *sic) { int result = SIC_OKAY; char *home = getenv ("HOME"); char *sicrcpath, *separator = ""; int len; if (!home) home = ""; len = strlen (home); if (len && home[len -1] != '/') separator = "/"; len += strlen (separator) + strlen (SICRCFILE) + 1; sicrcpath = XMALLOC (char, len); sprintf (sicrcpath, "%s%s%s", home, separator, SICRCFILE); if (access (sicrcpath, R_OK) == 0) result = source (sic, sicrcpath); return result; } | |