Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

Testing latest pari + WASM + node.js... and it works?! Wow.

28485 views
License: GPL3
ubuntu2004
1
/* Copyright (C) 2000 The PARI group.
2
3
This file is part of the PARI/GP package.
4
5
PARI/GP is free software; you can redistribute it and/or modify it under the
6
terms of the GNU General Public License as published by the Free Software
7
Foundation; either version 2 of the License, or (at your option) any later
8
version. It is distributed in the hope that it will be useful, but WITHOUT
9
ANY WARRANTY WHATSOEVER.
10
11
Check the License for details. You should have received a copy of it, along
12
with the package; see the file 'COPYING'. If not, write to the Free Software
13
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
14
15
/*******************************************************************/
16
/* */
17
/* INTERFACE TO READLINE COMPLETION */
18
/* */
19
/*******************************************************************/
20
#include "pari.h"
21
22
#ifdef READLINE
23
24
#include "paripriv.h"
25
#include "gp.h"
26
27
typedef int (*RLCI)(int, int); /* rl_complete and rl_insert functions */
28
29
BEGINEXTERN
30
/* otherwise C++ compilers will choke on rl_message() prototype */
31
#define USE_VARARGS
32
#define PREFER_STDARG
33
#include <readline/readline.h>
34
#include <readline/history.h>
35
ENDEXTERN
36
37
/**************************************************************************/
38
static pari_rl_interface pari_rl;
39
static int did_init_matched = 0;
40
41
static int
42
change_state(const char *msg, ulong flag, int count)
43
{
44
int c = (GP_DATA->readline_state & flag) != 0;
45
ulong state = GP_DATA->readline_state;
46
47
switch(count)
48
{
49
default: c = 0; break; /* off */
50
case -1: c = 1; break; /* on */
51
case -2: c = 1 - c; /* toggle */
52
}
53
if (c)
54
GP_DATA->readline_state |= flag;
55
else {
56
GP_DATA->readline_state &= ~flag;
57
if (!GP_DATA->readline_state && state) GP_DATA->readline_state = 1;
58
}
59
rl_save_prompt();
60
rl_message("[%s: %s] ", msg, c? "on": "off");
61
c = rl_read_key();
62
rl_restore_prompt();
63
rl_clear_message();
64
rl_stuff_char(c); return 1;
65
}
66
67
/* Wrapper around rl_complete to allow toggling insertion of arguments */
68
static int
69
pari_rl_complete(int count, int key)
70
{
71
int ret;
72
73
pari_rl.back = 0;
74
if (count <= 0)
75
return change_state("complete args", DO_ARGS_COMPLETE, count);
76
77
rl_begin_undo_group();
78
if (rl_last_func == pari_rl_complete)
79
rl_last_func = (RLCI) rl_complete; /* Make repeated TABs different */
80
ret = ((RLCI)rl_complete)(count,key);
81
if (pari_rl.back && (pari_rl.back <= rl_point))
82
rl_point -= pari_rl.back;
83
rl_end_undo_group(); return ret;
84
}
85
86
static int did_matched_insert;
87
88
static int
89
pari_rl_matched_insert_suspend(int count, int key)
90
{
91
ulong state = GP_DATA->readline_state;
92
(void)count; (void)key;
93
94
did_matched_insert = (GP_DATA->readline_state & DO_MATCHED_INSERT);
95
GP_DATA->readline_state &= ~DO_MATCHED_INSERT;
96
if (!GP_DATA->readline_state && state) GP_DATA->readline_state = 1;
97
return 1;
98
}
99
100
static int
101
pari_rl_matched_insert_restore(int count, int key)
102
{
103
(void)count; (void)key;
104
if (did_matched_insert)
105
GP_DATA->readline_state |= DO_MATCHED_INSERT;
106
return 1;
107
}
108
109
static const char paropen[] = "([{";
110
static const char parclose[] = ")]}";
111
112
/* To allow insertion of () with a point in between. */
113
static int
114
pari_rl_matched_insert(int count, int key)
115
{
116
int i = 0, ret;
117
118
if (count <= 0)
119
return change_state("electric parens", DO_MATCHED_INSERT, count);
120
while (paropen[i] && paropen[i] != key) i++;
121
if (!paropen[i] || !(GP_DATA->readline_state & DO_MATCHED_INSERT) || GP_DATA->flags & gpd_EMACS)
122
return ((RLCI)rl_insert)(count,key);
123
rl_begin_undo_group();
124
((RLCI)rl_insert)(count,key);
125
ret = ((RLCI)rl_insert)(count,parclose[i]);
126
rl_point -= count;
127
rl_end_undo_group(); return ret;
128
}
129
130
static int
131
pari_rl_default_matched_insert(int count, int key)
132
{
133
if (!did_init_matched) {
134
did_init_matched = 1;
135
GP_DATA->readline_state |= DO_MATCHED_INSERT;
136
}
137
return pari_rl_matched_insert(count, key);
138
}
139
140
static int
141
pari_rl_forward_sexp(int count, int key)
142
{
143
int deep = 0, dir = 1, move_point = 0, lfail;
144
145
(void)key;
146
if (count < 0)
147
{
148
count = -count; dir = -1;
149
if (!rl_point) goto fail;
150
rl_point--;
151
}
152
while (count || deep)
153
{
154
move_point = 1; /* Need to move point if moving left. */
155
lfail = 0; /* Do not need to fail left movement yet. */
156
while ( !is_keyword_char(rl_line_buffer[rl_point])
157
&& !strchr("\"([{}])",rl_line_buffer[rl_point])
158
&& !( (dir == 1)
159
? (rl_point >= rl_end)
160
: (rl_point <= 0 && (lfail = 1))))
161
rl_point += dir;
162
if (lfail || !rl_line_buffer[rl_point]) goto fail;
163
164
if (is_keyword_char(rl_line_buffer[rl_point]))
165
{
166
while ( is_keyword_char(rl_line_buffer[rl_point])
167
&& (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0 && (lfail = 1)))
168
|| (move_point = 0)))
169
rl_point += dir;
170
if (deep && lfail) goto fail;
171
if (!deep) count--;
172
}
173
else if (strchr(paropen,rl_line_buffer[rl_point]))
174
{
175
if (deep == 0 && dir == -1) goto fail; /* We are already out of pars. */
176
rl_point += dir;
177
deep++; if (!deep) count--;
178
}
179
else if (strchr(parclose,rl_line_buffer[rl_point]))
180
{
181
if (deep == 0 && dir == 1)
182
{
183
rl_point++; goto fail; /* Get out of pars. */
184
}
185
rl_point += dir;
186
deep--; if (!deep) count--;
187
}
188
else if (rl_line_buffer[rl_point] == '\"')
189
{
190
int bad = 1;
191
192
rl_point += dir;
193
while ( ((rl_line_buffer[rl_point] != '\"') || (bad = 0))
194
&& (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0))
195
|| (move_point = 0)) )
196
rl_point += dir;
197
if (bad) goto fail;
198
rl_point += dir; /* Skip the other delimiter */
199
if (!deep) count--;
200
}
201
else
202
{
203
fail: rl_ding(); return 1;
204
}
205
}
206
if (dir != 1 && move_point) rl_point++;
207
return 1;
208
}
209
210
static int
211
pari_rl_backward_sexp(int count, int key)
212
{
213
return pari_rl_forward_sexp(-count, key);
214
}
215
216
static void
217
rl_print_aide(char *s, int flag)
218
{
219
int p = rl_point, e = rl_end;
220
FILE *save = pari_outfile;
221
222
rl_point = 0; rl_end = 0; pari_outfile = rl_outstream;
223
rl_save_prompt();
224
rl_message("%s",""); /* rl_message("") ==> "zero length format" warning */
225
gp_help(s, flag);
226
rl_restore_prompt();
227
rl_point = p; rl_end = e; pari_outfile = save;
228
rl_clear_message();
229
rl_refresh_line(0,0);
230
}
231
232
/* long help if count < 0 */
233
static int
234
rl_short_help(int count, int key)
235
{
236
int flag = h_RL;
237
char *s = rl_line_buffer + rl_point;
238
239
(void)key;
240
/* func() with cursor on ')', e.g. following completion */
241
if (s > rl_line_buffer && *s == ')' && s[-1] == '(') s--;
242
while (s > rl_line_buffer && is_keyword_char(s[-1])) s--;
243
/* check for '\c' */
244
if (s > rl_line_buffer && s[-1] == '\\') s--;
245
246
if (count < 0 || rl_last_func == rl_short_help) flag |= h_LONG;
247
rl_print_aide(s, flag); return 0;
248
}
249
250
static int
251
rl_long_help(int count, int key) { (void)count; return rl_short_help(-1,key); }
252
253
static void
254
init_histfile(void)
255
{
256
char *h = GP_DATA->histfile;
257
if (h && read_history(h)) write_history(h);
258
}
259
260
/*******************************************************************/
261
/* */
262
/* GET LINE FROM READLINE */
263
/* */
264
/*******************************************************************/
265
static int
266
history_is_new(char *s)
267
{
268
HIST_ENTRY *e;
269
if (!*s) return 0;
270
if (!history_length) return 1;
271
e = history_get(history_length);
272
/* paranoia: e != NULL, unless readline is in a weird state */
273
return e? strcmp(s, e->line): 0;
274
}
275
276
static void
277
gp_add_history(char *s)
278
{
279
if (history_is_new(s)) { add_history(s); append_history(1,GP_DATA->histfile); }
280
}
281
282
/* Read line; returns a malloc()ed string of the user input or NULL on EOF.
283
Increments the buffer size appropriately if needed; fix *endp if so. */
284
static char *
285
gprl_input(char **endp, int first, input_method *IM, filtre_t *F)
286
{
287
pari_sp av = avma;
288
Buffer *b = F->buf;
289
ulong used = *endp - b->buf;
290
ulong left = b->len - used, l;
291
const char *p;
292
char *s, *t;
293
294
if (first) p = IM->prompt;
295
else {
296
p = F->in_comment ? GP_DATA->prompt_comment: IM->prompt_cont;
297
p = gp_format_prompt(p);
298
}
299
if (! (s = readline(p)) ) { set_avma(av); return NULL; } /* EOF */
300
gp_add_history(s); /* Makes a copy */
301
l = strlen(s) + 1;
302
/* put back \n that readline stripped. This is needed for
303
* { print("a
304
* b"); }
305
* and conforms with the other input methods anyway. */
306
t = (char*)pari_malloc(l + 1);
307
memcpy(t, s, l-1);
308
t[l-1] = '\n';
309
t[l] = 0; /* equivalent to sprintf(t,"%s\n", s) */
310
if (left < l)
311
{
312
ulong incr = b->len;
313
if (incr < l) incr = l;
314
fix_buffer(b, b->len + incr);
315
*endp = b->buf + used;
316
}
317
set_avma(av); return t;
318
}
319
320
/* request one line interactively.
321
* Return 0: EOF
322
* 1: got one line from readline or pari_infile */
323
int
324
get_line_from_readline(const char *prompt, const char *prompt_cont, filtre_t *F)
325
{
326
const int index = history_length;
327
char *s;
328
input_method IM;
329
330
if (!GP_DATA->use_readline)
331
{
332
pari_puts(prompt); pari_flush();
333
return get_line_from_file(prompt, F, pari_infile);
334
}
335
336
IM.prompt = prompt;
337
IM.prompt_cont = prompt_cont;
338
IM.getline = &gprl_input;
339
IM.free = 1;
340
if (! input_loop(F,&IM)) { pari_puts("\n"); return 0; }
341
342
s = F->buf->buf;
343
if (*s)
344
{
345
if (history_length > index+1)
346
{ /* Multi-line input. Remove incomplete lines */
347
int i = history_length;
348
while (i > index) {
349
HIST_ENTRY *e = remove_history(--i);
350
pari_free(e->line); pari_free(e);
351
}
352
gp_add_history(s);
353
}
354
gp_echo_and_log(prompt, s);
355
}
356
return 1;
357
}
358
359
static char**
360
gp_completion(char *text, int START, int END)
361
{
362
return pari_completion(&pari_rl, text, START, END);
363
}
364
365
void
366
init_readline(void)
367
{
368
static int init_done = 0;
369
370
if (init_done) return;
371
372
pari_use_readline(pari_rl);
373
374
if (! GP_DATA->use_readline) GP_DATA->readline_state = 0;
375
init_done = 1;
376
init_histfile();
377
cb_pari_init_histfile = init_histfile;
378
cb_pari_get_line_interactive = get_line_from_readline;
379
380
/* Allow conditional parsing of the ~/.inputrc file. */
381
rl_readline_name = "Pari-GP";
382
383
/* added ~, ? and , */
384
rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(?~";
385
rl_special_prefixes = "~";
386
387
/* custom completer */
388
rl_attempted_completion_function = (rl_completion_func_t*) gp_completion;
389
390
/* we always want the whole list of completions under emacs */
391
if (GP_DATA->flags & gpd_EMACS) rl_completion_query_items = 0x8fff;
392
393
rl_add_defun("short-help", rl_short_help, -1);
394
rl_add_defun("long-help", rl_long_help, -1);
395
rl_add_defun("pari-complete", pari_rl_complete, '\t');
396
rl_add_defun("pari-matched-insert", pari_rl_default_matched_insert, -1);
397
rl_add_defun("pari-matched-insert-suspend", pari_rl_matched_insert_suspend, -1);
398
rl_add_defun("pari-matched-insert-restore", pari_rl_matched_insert_restore, -1);
399
rl_add_defun("pari-forward-sexp", pari_rl_forward_sexp, -1);
400
rl_add_defun("pari-backward-sexp", pari_rl_backward_sexp, -1);
401
402
rl_bind_key_in_map('h', rl_short_help, emacs_meta_keymap);
403
rl_bind_key_in_map('H', rl_long_help, emacs_meta_keymap);
404
405
#define KSbind(s,f,k) rl_generic_bind(ISFUNC, (s), (char*)(f), (k))
406
407
KSbind("OP", rl_short_help, emacs_meta_keymap); /* f1, vt100 */
408
KSbind("[11~", rl_short_help, emacs_meta_keymap); /* f1, xterm */
409
KSbind("OP", rl_short_help, vi_movement_keymap); /* f1, vt100 */
410
KSbind("[11~", rl_short_help, vi_movement_keymap); /* f1, xterm */
411
/* XTerm may signal start/end of paste by emitting F200/F201
412
* TODO: check to what extent this patch has been applied */
413
/* FIXME: For vi mode something more intelligent is needed - to switch to the
414
insert mode - and back when restoring. */
415
KSbind("[200~", pari_rl_matched_insert_suspend, emacs_meta_keymap); /* pre-paste xterm */
416
KSbind("[200~", pari_rl_matched_insert_suspend, vi_movement_keymap); /* pre-paste xterm */
417
KSbind("[201~", pari_rl_matched_insert_restore, emacs_meta_keymap); /* post-paste xterm */
418
KSbind("[201~", pari_rl_matched_insert_restore, vi_movement_keymap); /* post-paste xterm */
419
rl_bind_key_in_map('(', pari_rl_matched_insert, emacs_standard_keymap);
420
rl_bind_key_in_map('[', pari_rl_matched_insert, emacs_standard_keymap);
421
rl_bind_key_in_map(6, pari_rl_forward_sexp, emacs_meta_keymap); /* M-C-f */
422
rl_bind_key_in_map(2, pari_rl_backward_sexp, emacs_meta_keymap); /* M-C-b */
423
}
424
#endif
425
426