-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathrepl.c
246 lines (233 loc) · 7.42 KB
/
repl.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/** @file repl.c
* @brief An example REPL for the liblisp interpreter
* @author Richard Howe (2015)
* @license LGPL v2.1 or Later
* @email howe.r.j.89@gmail.com **/
#include "liblisp.h"
#include "private.h"
#include <stdlib.h>
#include <string.h>
#include <locale.h>
/**** The need putting here by the build system / version control system ****/
#ifndef VERSION
#define VERSION unknown /**< Version of the interpreter*/
#endif
#ifndef VCS_COMMIT
#define VCS_COMMIT unknown /**< Version control commit of this interpreter*/
#endif
#ifndef VCS_ORIGIN
#define VCS_ORIGIN unknown /**< Version control repository origin*/
#endif
/****************************************************************************/
static const char *usage = /**< command line options for example interpreter*/
"(-[hcpvVEHL])* (-[i\\-] file)* (-e string)* (-o file)* file* -";
static const char *help =
"The liblisp library and interpreter. For more information on usage\n\
consult the man pages 'lisp' and 'liblisp'. Alternatively, consult:\n\
\n\
https://github.com/howerj/liblisp\n\
http://work.anapnea.net/html/html/projects.html\n\
";
static unsigned lisp_verbosity = LISP_LOG_LEVEL_ERROR;
enum { OPTS_ERROR = -1, /**< there's been an error processing the options*/
OPTS_SWITCH, /**< current argument was a valid flag*/
OPTS_IN_FILE, /**< current argument is file input to eval*/
OPTS_IN_FILE_NEXT_ARG, /**< process the next argument as file input*/
OPTS_OUT_FILE, /**< next argument is an output file*/
OPTS_IN_STRING, /**< next argument is a string to eval*/
OPTS_IN_STDIN, /**< read input from stdin*/
}; /**< getoptions enum*/
static int getoptions(lisp_t * l, char *arg, char *arg_0) { /**@brief simple parser for command line options**/
int c = 0;
if ('-' != *arg++)
return OPTS_IN_FILE;
if (!arg[0])
return OPTS_IN_STDIN;
while ((c = *arg++))
switch (c) {
case 'i':
case '-':
return OPTS_IN_FILE_NEXT_ARG;
case 'h':
printf("usage %s %s\n\n", arg_0, usage);
puts(help);
exit(0);
break;
case 'c':
lisp_log_note(l, "'color-on");
l->color_on = 1;
break; /*colorize output */
case 'L':
lisp_log_note(l, "'local 'default");
if (!setlocale(LC_ALL, ""))
FATAL("failed to default locale");
break;
case 'p':
lisp_log_note(l, "'prompt-on");
l->prompt_on = 1;
break; /*turn standard prompt when reading stdin */
case 'E':
lisp_log_note(l, "'line-editor-on");
l->editor_on = 1;
break; /*turn line editor on when reading stdin */
case 'H':
lisp_log_note(l, "'halt-on-error");
l->errors_halt = 1;
break;
case 'v':
lisp_verbosity++;
if (lisp_verbosity < LISP_LOG_LEVEL_LAST_INVALID)
lisp_set_log_level(l, lisp_verbosity);
else
lisp_log_note(l, "'verbosity \"already set to maximum\"");
break;
case 'V':
puts("program: liblisp");
puts("version: " XSTRINGIFY(VERSION));
puts("commit: " XSTRINGIFY(VCS_COMMIT));
puts("origin: " XSTRINGIFY(VCS_ORIGIN));
exit(0);
break;
case 'e':
return OPTS_IN_STRING;
case 'o':
return OPTS_OUT_FILE;
default:
fprintf(stderr, "unknown option '%c'\n", c);
fprintf(stderr, "usage %s %s\n", arg_0, usage);
return OPTS_ERROR;
}
return OPTS_SWITCH; /*this argument was a valid flag, nothing more */
}
int lisp_repl(lisp_t * l, char *prompt, int editor_on) {
lisp_cell_t *ret;
io_t *ofp, *efp;
char *line = NULL;
int r = 0;
ofp = lisp_get_output(l);
efp = lisp_get_logging(l);
ofp->pretty = efp->pretty = 1;
ofp->color = efp->color = l->color_on;
if ((r = setjmp(l->recover)) < 0) { /*catch errors and "sig" */
l->recover_init = 0;
return r;
}
l->recover_init = 1;
if (editor_on && l->editor) { /*handle line editing functionality */
while ((line = l->editor(prompt))) {
lisp_cell_t *prn;
if (!line[strspn(line, " \t\r\n")]) {
free(line);
continue;
}
if (!(prn = lisp_eval_string(l, line))) {
free(line);
LISP_RECOVER(l, "\"%s\"", "invalid or incomplete line");
}
lisp_print(l, prn);
free(line);
line = NULL;
}
} else { /*read from input with no special handling, or a file */
for (;;) {
/**@bug this should exit with a failure if not reading
* from stdin and an error occurs, otherwise the
* parser looses track, this is mainly a concern
* for string input, where is makes for very
* confusing behavior*/
lisp_printf(l, ofp, 0, "%s", prompt);
if (!(ret = reader(l, lisp_get_input(l))))
break;
if (!(ret = eval(l, 0, ret, l->top_env)))
break;
lisp_printf(l, ofp, 0, "%S\n", ret);
l->gc_stack_used = 0;
}
}
l->gc_stack_used = 0;
l->recover_init = 0;
return r;
}
int main_lisp_env(lisp_t * l, int argc, char **argv) {
int i = 0, stdin_off = 0;
lisp_cell_t *ob = l->nil;
if (!l)
return -1;
for (i = argc - 1; i + 1; i--) /*add command line args to list */
if (!(ob = cons(l, mk_str(l, lstrdup_or_abort(argv[i])), ob)))
return -1;
if (!lisp_extend_top(l, lisp_intern(l, lstrdup_or_abort("args")), ob))
return -1;
lisp_add_cell(l, "*version*", mk_str(l, lstrdup_or_abort(XSTRINGIFY(VERSION))));
lisp_add_cell(l, "*commit*", mk_str(l, lstrdup_or_abort(XSTRINGIFY(VCS_COMMIT))));
lisp_add_cell(l, "*repository-origin*", mk_str(l, lstrdup_or_abort(XSTRINGIFY(VCS_ORIGIN))));
for (i = 1; i < argc; i++)
switch (getoptions(l, argv[i], argv[0])) {
case OPTS_SWITCH:
break;
case OPTS_IN_STDIN: /*read from standard input */
lisp_log_note(l, "'input-file 'stdin");
io_close(lisp_get_input(l));
if (lisp_set_input(l, io_fin(stdin)) < 0)
return perror("stdin"), -1;
if (lisp_repl(l, l->prompt_on ? "> " : "", l->editor_on) < 0)
return -1;
io_close(lisp_get_input(l));
lisp_set_input(l, NULL);
stdin_off = 1;
break;
case OPTS_IN_FILE_NEXT_ARG:
if (!(++i < argc))
return fprintf(stderr, "-i and -- expects file\n"), -1;
/* fall-through */
case OPTS_IN_FILE: /*read from a file */
lisp_log_note(l, "'input-file \"%s\"", argv[i]);
io_close(lisp_get_input(l));
if (lisp_set_input(l, io_fin(fopen(argv[i], "rb"))) < 0)
return perror(argv[i]), -1;
if (lisp_repl(l, "", 0) < 0)
return -1;
io_close(lisp_get_input(l));
lisp_set_input(l, NULL);
stdin_off = 1;
break;
case OPTS_IN_STRING: /*evaluate a string */
lisp_log_note(l, "'input-string \"%s\"", argv[i]);
io_close(lisp_get_input(l));
if (!(++i < argc))
return fprintf(stderr, "-e expects arg\n"), -1;
if (lisp_set_input(l, io_sin(argv[i], strlen(argv[i]))) < 0)
return perror(argv[i]), -1;
if (lisp_repl(l, "", 0) < 0)
return -1;
io_close(lisp_get_input(l));
lisp_set_input(l, NULL);
stdin_off = 1;
break;
case OPTS_OUT_FILE: /*change the file to write to */
lisp_log_note(l, "'output-file \"%s\"", argv[i]);
if (!(++i < argc))
return fprintf(stderr, "-o expects arg\n"), -1;
io_close(lisp_get_output(l));
lisp_set_output(l, NULL);
if (lisp_set_output(l, io_fout(fopen(argv[i], "wb"))) < 0)
return perror(argv[i]), -1;
break;
case OPTS_ERROR:
default:
exit(-1);
}
if (!stdin_off) {
lisp_log_note(l, "\"%s\"", "reading from stdin");
if (lisp_repl(l, l->prompt_on ? "> " : "", l->editor_on) < 0)
return -1;
}
lisp_destroy(l);
return 0;
}
int main_lisp(int argc, char **argv) {
lisp_t *l = NULL;
if (!(l = lisp_init()))
return -1;
return main_lisp_env(l, argc, argv);
}