xref: /Universal-ctags/extra-cmds/optscript-repl.c (revision 949ffdd12289052d6ebaedba8ca86ca01569327d) !
1 /*
2 *   Copyright (c) 2020, Masatake YAMATO
3 *   Copyright (c) 2020, Red Hat, Inc.
4 *
5 *   This source code is released for free distribution under the terms of the
6 *   GNU General Public License version 2 or (at your option) any later version.
7 */
8 
9 #include "general.h"
10 
11 #include "optscript.h"
12 #include "mio.h"
13 #include "routines.h"
14 
15 #include <string.h>
16 #include <stdlib.h>
17 
18 static void
help(const char * progname,int exit_status)19 help (const char* progname, int exit_status)
20 {
21 	FILE *out = stderr;
22 	if (exit_status == 0)
23 		out = stdout;
24 
25 	fprintf (out, "Usage: %s [options] [file|-]\n\n", progname);
26 	fputs   (     "  -h|--help             Print this option summary\n", out);
27 	fputs   (     "  -l|--list-operators   List built-in operators\n", out);
28 	fputs   (     "  -e|--eval CODE        Evaluate the CODE\n", out);
29 	fputc   ('\n', out);
30 	exit (exit_status);
31 }
32 
33 static EsObject*
op_renew(OptVM * vm,EsObject * name)34 op_renew (OptVM *vm, EsObject *name)
35 {
36 	bool *app_data = opt_vm_get_app_data (vm);
37 	*app_data = true;
38 	return OPT_ERR_QUIT;
39 }
40 
41 int
optscript_run(OptVM * vm,char * prompt,void * app_data)42 optscript_run (OptVM *vm, char *prompt, void *app_data)
43 {
44 	int r = 0;
45 	void *old_app_data = opt_vm_set_app_data (vm, app_data);
46 	char *old_prompt = opt_vm_set_prompt (vm, prompt);
47 
48 	opt_vm_print_prompt (vm);
49 
50 	while (true)
51 	{
52 		EsObject *o = opt_vm_read (vm, NULL);
53 		if (es_object_equal (o, ES_READER_EOF))
54 		{
55 			es_object_unref (o);
56 			break;
57 		}
58 		EsObject *e = opt_vm_eval (vm, o);
59 		es_object_unref (o);
60 
61 		if (es_error_p (e))
62 		{
63 			if (!es_object_equal (e, OPT_ERR_QUIT))
64 			{
65 				opt_vm_report_error (vm, e, NULL);
66 				r = 1;
67 			}
68 			break;
69 		}
70 	}
71 
72 	opt_vm_set_prompt (vm, old_prompt);
73 	opt_vm_set_app_data (vm, old_app_data);
74 	return r;
75 }
76 
77 int
main(int argc,char ** argv)78 main(int argc, char **argv)
79 {
80 	MIO *in = NULL;
81 	const char *file = NULL;
82 	bool help_ops = false;
83 
84 	for (int i = 1; i < argc; i++)
85 	{
86 		if (strcmp (argv[i], "-h") == 0
87 			|| strcmp (argv[i], "--help") == 0)
88 			help (argv[0], 0);
89 		else if (strcmp (argv[i], "-l") == 0
90 				 || strcmp (argv[i], "--list-operators") == 0)
91 			help_ops = true;
92 		else if (strcmp (argv[i], "-e") == 0
93 				 || strcmp (argv[i], "--eval") == 0)
94 		{
95 			if (file)
96 			{
97 				fprintf (stderr, "Don't specify multiple input: %s, %s\n",
98 						 file, argv[i]);
99 				exit (2);
100 			}
101 
102 			if (i == argc -1)
103 			{
104 				fprintf (stderr, "no code for %s\n", argv[i]);
105 				exit (2);
106 			}
107 			file = "<cmdline>";
108 			i++;
109 			in = mio_new_memory ((unsigned char *)eStrdup(argv[i]), strlen (argv[i]), eRealloc, eFree);
110 			if (!in)
111 			{
112 				fprintf (stderr, "failed to create mio from memory\n");
113 				exit (1);
114 			}
115 		}
116 		else if (argv[i][0] == '-' && argv[i][1] == '\0')
117 		{
118 			if (file)
119 			{
120 				fprintf (stderr, "Don't specify multiple input: %s, %s\n",
121 						 file, argv[i]);
122 				exit (2);
123 			}
124 
125 			file = "<stdin>";
126 			in = mio_new_fp (stdin, NULL);
127 			if (!in)
128 			{
129 				fprintf (stderr, "failed to use <stdin>\n");
130 				exit (1);
131 			}
132 		}
133 		else if (argv[i][0] == '-')
134 		{
135 			fprintf (stderr, "unknown option: %s\n", argv[i]);
136 			exit (2);
137 		}
138 		else if (in)
139 		{
140 			fprintf (stderr, "too many arugments\n");
141 			mio_unref (in);
142 			exit (2);
143 		}
144 		else
145 		{
146 			if (file)
147 			{
148 				fprintf (stderr, "Don't specify multiple input: %s, %s\n",
149 						 file, argv[i]);
150 				exit (2);
151 			}
152 
153 			file = argv[i];
154 			in = mio_new_file (file, "r");
155 			if (!in)
156 			{
157 				fprintf (stderr, "failed to open: %s\n", file);
158 				exit (1);
159 			}
160 		}
161 	}
162 
163 	opt_init ();
164 
165 	if (!in)
166 		in = mio_new_fp (stdin, NULL);
167 	if (in == NULL)
168 	{
169 		fprintf (stderr, "failed to open input stream\n");
170 		exit (1);
171 	}
172 
173 	MIO *out = mio_new_fp (stdout, NULL);
174 	if (!out)
175 	{
176 		mio_unref (in);
177 		fprintf (stderr, "failed to open output stream\n");
178 		exit (1);
179 	}
180 	MIO *err = mio_new_fp (stderr, NULL);
181 	if (!err)
182 	{
183 		mio_unref (out);
184 		mio_unref (in);
185 		fprintf (stderr, "failed to open error stream\n");
186 		exit (1);
187 	}
188 
189 	OptVM *vm = opt_vm_new (in, out, err);
190 
191 	int r;
192 
193 	EsObject *dict = opt_dict_new (47);
194 	{
195 		EsObject *op;
196 		EsObject *sym;
197 
198 		op = opt_operator_new (op_renew, "_renew", 0, ":clear the state of vm%- _RENEW -");
199 		sym = es_symbol_intern ("_renew");
200 		opt_dict_def (dict, sym, op);
201 		es_object_unref (op);
202 	}
203 
204 	opt_vm_dstack_push (vm, dict);
205 	if (help_ops)
206 		r = opt_vm_help (vm, NULL, NULL, NULL);
207 	else
208 	{
209 		bool renew = false;
210 	renew:
211 		r = optscript_run (vm, file? NULL: "OPT", &renew);
212 		if (renew)
213 		{
214 			opt_vm_clear (vm);
215 			opt_vm_dstack_push (vm, dict);
216 			renew = false;
217 			goto renew;
218 		}
219 	}
220 	es_object_unref (dict);
221 
222 	opt_vm_delete (vm);
223 
224 	mio_unref (err);
225 	mio_unref (out);
226 	mio_unref (in);
227 
228 	return r;
229 }
230