diff options
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 178 |
1 files changed, 142 insertions, 36 deletions
diff --git a/main.c b/main.c index dd2e824..80706e5 100644 --- a/main.c +++ b/main.c @@ -8,80 +8,186 @@ static void show_version(void) { - fprintf(stderr, "lith version %s: a small lisp-like language interpreter\n", LITH_VERSION_STRING); + fprintf(stderr, + "lith version %s: a small lisp-like language interpreter\n", + LITH_VERSION_STRING); } static void show_help(char *progname) { show_version(); - fprintf(stderr, "usage: %s [OPTIONS] [FILES] ...\n", progname); + fprintf(stderr, + "usage: \n" + " %s [-h | --help] [-v | --version] [-i | --interactive]\n" + " %s [(-e | --evaluate) expr ...]\n" + " %s [--] FILE [ARGS] ...\n\n", + progname, progname, progname); fprintf(stderr, "Available options: \n\n" - " -e <expr>\n" - " --evaluate <expr>\n" - " evaluate the <expr>\n\n" + " -e expr ...\n" + " --evaluate expr ...\n" + " evaluate the expression(s)\n\n" " -h, --help\n" " show this help\n\n" + " -i, --interactive\n" + " run an interactive session (REPL)\n\n" " -v, --version\n" " show version\n\n" ""); } +static lith_value *get_list_of_arguments(lith_st *L, char **arg) +{ + lith_value *arguments, *cur, *str; + arguments = cur = L->nil; + if (!cur) + return NULL; + for (; *arg; arg++) { + str = lith_make_string(L, *arg, strlen(*arg)); + if (!str || LITH_IS_ERR(L)) { + lith_free_value(arguments); + return NULL; + } + if (LITH_IS_NIL(cur)) { + arguments = LITH_CONS(L, str, L->nil); + cur = arguments; + } else { + LITH_CDR(cur) = LITH_CONS(L, str, L->nil); + cur = LITH_CDR(cur); + } + } + return arguments; +} + +static char *read_line(int *line_empty) +{ + size_t length = 0, capacity = 0; + int c; + char *start = NULL, *cur = NULL, *tmp; + while (((c = getchar()) != EOF) && (c != '\n')) { + if ((length + 1) >= capacity) { + tmp = realloc(start, capacity += BUFSIZ); + if (!tmp) { + free(start); + return NULL; + } + start = tmp; + cur = start + length; + } + *cur++ = c; + ++length; + } + if (cur) *cur = 0; + *line_empty = !start && (c == '\n'); + return start; +} + int main(int argc, char **argv) { - int ret; + int ret, empty_line; + size_t len; lith_st T, *L; - lith_env *V, *W; - char **arg, *a; + lith_env *V; + lith_value *arguments; + char **args, *opt, **expr, *filename, *line; + + enum { LITH__REPL, LITH__EXPR, LITH__RUN_FILE } state; if (argc < 2) { show_help(argv[0]); - return 8; + return 2; } - - a = argv[1]; - if (a[0] == '-') { - if (!strcmp(a, "-v") || !strcmp(a, "--version")) { + + opt = argv[1]; + #define OPT(short_form, long_form) \ + ((strcmp(opt, short_form) == 0) \ + || (strcmp(opt, long_form) == 0)) + if (opt[0] == '-') { + if (OPT("-v", "--version")) { show_version(); return 0; - } else if (!strcmp(a, "-h") || !strcmp(a, "--help")) { + } else if (OPT("-h", "--help")) { show_help(argv[0]); return 0; + } else if (OPT("-i", "--interactive")) { + state = LITH__REPL; + } else if (OPT("-e", "--evaluate")) { + state = LITH__EXPR; + if (!argv[2]) { + fprintf(stderr, + "lith: expecting at least one argument for '%s'\n", argv[1]); + return 3; + } + expr = argv+2; + } else if (!strcmp(opt, "--")) { + if (!argv[2]) { + fprintf(stderr, "lith: expecting filename after '--'\n"); + return 4; + } + state = LITH__RUN_FILE; + filename = argv[2]; + args = argv+3; + } else { + fprintf(stderr, + "lith: invalid option '%s': " + "try '%s --help' for available options\n", + argv[1], argv[0]); + return 5; } + } else { + state = LITH__RUN_FILE; + filename = argv[1]; + args = argv+2; } + #undef OPT - ret = 0; L = &T; lith_init(L); - W = lith_new_env(L, L->global); + V = lith_new_env(L, L->global); lith_run_file(L, L->global, "lib.lith"); - if (LITH_IS_ERR(L)) ret |= 16; + if (LITH_IS_ERR(L)) + return 6; - for (arg = argv+1; arg < argv+argc; arg++) { - if ((*arg)[0] != '-') { - V = lith_new_env(L, W); - lith_run_file(L, V, *arg); - lith_free_env(V); - if (LITH_IS_ERR(L)) ret |= 64; - lith_clear_error_state(L); - } else if (!strcmp(*arg, "-e") || !strcmp(*arg, "--evaluate")) { - if (!*++arg) { - fprintf(stderr, "lith: expecting an argument for '%s'\n", *--arg); + switch (state) { + case LITH__EXPR: + for (; *expr; expr++) { + lith_run_string(L, V, *expr, 0); + if (LITH_IS_ERR(L)) { + ret |= 8; break; } - V = lith_new_env(L, W); - lith_run_string(L, V, *arg); - lith_free_env(V); - if (LITH_IS_ERR(L)) ret |= 32; - lith_clear_error_state(L); - } else { - fprintf(stderr, "lith: invalid option '%s': try '%s --help' for available options\n", *arg, argv[0]); + } + break; + case LITH__RUN_FILE: + arguments = get_list_of_arguments(L, args); + if (!arguments) { + ret |= 16; break; } + lith_env_put(L, V, lith_get_symbol(L, "arguments"), arguments); + lith_run_file(L, V, filename); + break; + case LITH__REPL: + show_version(); + for (;;) { + printf("lith> "); + line = read_line(&empty_line); + if (empty_line) continue; + if (!line) { + printf("\nBye!\n"); + break; + } + lith_run_string(L, V, line, 1); + free(line); + if (LITH_IS_ERR(L)) + lith_clear_error_state(L); + } + break; } - - lith_free_env(W); + + lith_free_env(V); lith_free(L); return ret; } + |