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
#include "paripriv.h"
22
23
/**************************************************************************/
24
static entree *current_ep = NULL;
25
26
/* do we add () at the end of completed word? (is it a function?) */
27
static int
28
add_paren(pari_rl_interface *rl, int end)
29
{
30
entree *ep;
31
const char *s;
32
33
if (end < 0 || (*rl->line_buffer)[end] == '(')
34
return 0; /* not from command_generator or already there */
35
ep = do_alias(current_ep); /* current_ep set in command_generator */
36
if (EpVALENCE(ep) < EpNEW)
37
{ /* is it a constant masked as a function (e.g Pi)? */
38
s = ep->help; if (!s) return 1;
39
while (is_keyword_char(*s)) s++;
40
return (*s != '=');
41
}
42
switch(EpVALENCE(ep))
43
{
44
case EpVAR: return typ((GEN)ep->value) == t_CLOSURE;
45
case EpINSTALL: return 1;
46
}
47
return 0;
48
}
49
50
static void
51
match_concat(char **matches, const char *s)
52
{
53
pari_realloc_ip((void**)matches, strlen(matches[0])+strlen(s)+1);
54
strcat(matches[0],s);
55
}
56
57
#define add_comma(x) (x==-2) /* from default_generator */
58
59
/* a single match, possibly modify matches[0] in place */
60
static void
61
treat_single(pari_rl_interface *rl, int code, char **matches)
62
{
63
if (add_paren(rl, code))
64
{
65
match_concat(matches,"()");
66
rl->back = 1;
67
if (*rl->point == *rl->end)
68
*rl->completion_append_character = '\0'; /* Do not append space. */
69
}
70
else if (add_comma(code))
71
match_concat(matches,",");
72
}
73
#undef add_comma
74
75
static char **
76
matches_for_emacs(const char *text, char **matches)
77
{
78
if (!matches) printf("@");
79
else
80
{
81
int i;
82
printf("%s@", matches[0] + strlen(text));
83
if (matches[1]) print_fun_list(matches+1,0);
84
85
/* we don't want readline to do anything, but insert some junk
86
* which will be erased by emacs.
87
*/
88
for (i=0; matches[i]; i++) pari_free(matches[i]);
89
pari_free(matches);
90
}
91
matches = (char **) pari_malloc(2*sizeof(char *));
92
matches[0] = (char*)pari_malloc(2); sprintf(matches[0],"_");
93
matches[1] = NULL; printf("@E_N_D"); pari_flush();
94
return matches;
95
}
96
97
/* Attempt to complete on the contents of TEXT. 'code' is used to
98
* differentiate between callers when a single match is found.
99
* Return the array of matches, NULL if there are none. */
100
static char **
101
get_matches(pari_rl_interface *rl, int code, const char *text, char *(*f)(const char*, int))
102
{
103
char **matches = rl->completion_matches(text, f);
104
if (matches && !matches[1]) treat_single(rl, code, matches);
105
if (GP_DATA->flags & gpd_EMACS) matches = matches_for_emacs(text,matches);
106
return matches;
107
}
108
109
static char *
110
add_prefix(const char *name, const char *text, long junk)
111
{
112
char *s = strncpy((char*)pari_malloc(strlen(name)+1+junk),text,junk);
113
strcpy(s+junk,name); return s;
114
}
115
static void
116
init_prefix(const char *text, int *len, int *junk, char **TEXT)
117
{
118
long l = strlen(text), j = l-1;
119
while (j >= 0 && is_keyword_char(text[j])) j--;
120
if (j >= 7 && text[j] == '-' && !strncmp(text+(j-7),"refcard",7)) j -= 8;
121
j++;
122
*TEXT = (char*)text + j;
123
*junk = j;
124
*len = l - j;
125
}
126
127
static int
128
is_internal(entree *ep) { return *ep->name == '_'; }
129
130
/* Generator function for command completion. STATE lets us know whether
131
* to start from scratch; without any state (i.e. STATE == 0), then we
132
* start at the top of the list. */
133
static char *
134
hashtable_generator(const char *text, int state, entree **hash)
135
{
136
static int hashpos, len, junk;
137
static entree* ep;
138
static char *TEXT;
139
140
/* If this is a new word to complete, initialize now:
141
* + indexes hashpos (GP hash list) and n (keywords specific to long help).
142
* + file completion and keyword completion use different word boundaries,
143
* have TEXT point to the keyword start.
144
* + save the length of TEXT for efficiency.
145
*/
146
if (!state)
147
{
148
hashpos = 0; ep = hash[hashpos];
149
init_prefix(text, &len, &junk, &TEXT);
150
}
151
152
/* Return the next name which partially matches from the command list. */
153
for(;;)
154
if (!ep)
155
{
156
if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
157
ep = hash[hashpos];
158
}
159
else if (is_internal(ep) || strncmp(ep->name,TEXT,len))
160
ep = ep->next;
161
else
162
break;
163
current_ep = ep; ep = ep->next;
164
return add_prefix(current_ep->name,text,junk);
165
}
166
/* Generator function for member completion. STATE lets us know whether
167
* to start from scratch; without any state (i.e. STATE == 0), then we
168
* start at the top of the list. */
169
static char *
170
member_generator(const char *text, int state)
171
{
172
static int hashpos, len, junk;
173
static entree* ep;
174
static char *TEXT;
175
entree **hash=functions_hash;
176
177
/* If this is a new word to complete, initialize now:
178
* + indexes hashpos (GP hash list) and n (keywords specific to long help).
179
* + file completion and keyword completion use different word boundaries,
180
* have TEXT point to the keyword start.
181
* + save the length of TEXT for efficiency.
182
*/
183
if (!state)
184
{
185
hashpos = 0; ep = hash[hashpos];
186
init_prefix(text, &len, &junk, &TEXT);
187
}
188
189
/* Return the next name which partially matches from the command list. */
190
for(;;)
191
if (!ep)
192
{
193
if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
194
ep = hash[hashpos];
195
}
196
else if (ep->name[0]=='_' && ep->name[1]=='.'
197
&& !strncmp(ep->name+2,TEXT,len))
198
break;
199
else
200
ep = ep->next;
201
current_ep = ep; ep = ep->next;
202
return add_prefix(current_ep->name+2,text,junk);
203
}
204
static char *
205
command_generator(const char *text, int state)
206
{ return hashtable_generator(text,state, functions_hash); }
207
static char *
208
default_generator(const char *text,int state)
209
{ return hashtable_generator(text,state, defaults_hash); }
210
211
static char *
212
ext_help_generator(const char *text, int state)
213
{
214
static int len, junk, n, def, key;
215
static char *TEXT;
216
if (!state) {
217
n = 0;
218
def = key = 1;
219
init_prefix(text, &len, &junk, &TEXT);
220
}
221
if (def)
222
{
223
char *s = default_generator(TEXT, state);
224
if (s) return add_prefix(s, text, junk);
225
def = 0;
226
}
227
if (key)
228
{
229
const char **L = gphelp_keyword_list();
230
for ( ; L[n]; n++)
231
if (!strncmp(L[n],TEXT,len))
232
return add_prefix(L[n++], text, junk);
233
key = 0; state = 0;
234
}
235
return command_generator(text, state);
236
}
237
238
/* add a space between \<char> and following text. Attempting completion now
239
* would delete char. Hitting <TAB> again will complete properly */
240
static char **
241
add_space(pari_rl_interface *rl, int start)
242
{
243
char **m;
244
int p = *rl->point + 1;
245
*rl->point = start + 2;
246
rl->insert(1, ' '); *rl->point = p;
247
/*better: fake an empty completion, but don't append ' ' after it! */
248
*rl->completion_append_character = '\0';
249
m = (char**)pari_malloc(2 * sizeof(char*));
250
m[0] = (char*)pari_malloc(1); *(m[0]) = 0;
251
m[1] = NULL; return m;
252
}
253
254
char **
255
pari_completion(pari_rl_interface *rl, char *text, int START, int END)
256
{
257
int i, first=0, start=START;
258
char *line = *rl->line_buffer;
259
260
*rl->completion_append_character = ' ';
261
current_ep = NULL;
262
while (line[first] && isspace((int)line[first])) first++;
263
if (line[first] == '?')
264
{
265
if (line[first+1] == '?')
266
return get_matches(rl, -1, text, ext_help_generator);
267
return get_matches(rl, -1, text, command_generator);
268
}
269
270
/* If the line does not begin by a backslash, then it is:
271
* . an old command ( if preceded by "whatnow(" ).
272
* . a default ( if preceded by "default(" ).
273
* . a member function ( if preceded by "." + keyword_chars )
274
* . a file name (in current directory) ( if preceded by 'read' or 'writexx' )
275
* . a command */
276
if (start >=1 && line[start] != '~') start--;
277
while (start && is_keyword_char(line[start])) start--;
278
if (line[start] == '~')
279
{
280
char *(*f)(const char*, int);
281
f = rl->username_completion_function;
282
for(i=start+1;i<=END;i++)
283
if (line[i] == '/') { f = rl->filename_completion_function; break; }
284
return get_matches(rl, -1, text, f);
285
}
286
if (line[first] == '\\')
287
{
288
if (first == start) return add_space(rl, start);
289
return get_matches(rl, -1, text, rl->filename_completion_function);
290
}
291
292
while (start && line[start] != '('
293
&& line[start] != ',') start--;
294
if (line[start] == '(' && start)
295
{
296
int iend, j,k;
297
entree *ep;
298
char buf[200];
299
300
i = start;
301
302
while (i && isspace((int)line[i-1])) i--;
303
iend = i;
304
while (i && is_keyword_char(line[i-1])) i--;
305
306
if (strncmp(line + i,"default",7) == 0)
307
return get_matches(rl, -2, text, default_generator);
308
if ( strncmp(line + i,"read",4) == 0
309
|| strncmp(line + i,"write",5) == 0)
310
return get_matches(rl, -1, text, rl->filename_completion_function);
311
312
j = start + 1;
313
while (j <= END && isspace((int)line[j])) j++;
314
k = END;
315
while (k > j && isspace((int)line[k])) k--;
316
/* If we are in empty parens, insert the default arguments */
317
if ((GP_DATA->readline_state & DO_ARGS_COMPLETE) && k == j
318
&& (line[j] == ')' || !line[j])
319
&& (iend - i < (long)sizeof(buf))
320
&& ( strncpy(buf, line + i, iend - i),
321
buf[iend - i] = 0, 1)
322
&& (ep = is_entry(buf)) && ep->help)
323
{
324
const char *s = ep->help;
325
while (is_keyword_char(*s)) s++;
326
if (*s++ == '(')
327
{ /* function call: insert arguments */
328
const char *e = s;
329
while (*e && *e != ')' && *e != '(') e++;
330
if (*e == ')')
331
{ /* we just skipped over the arguments in short help text */
332
char *str = strncpy((char*)pari_malloc(e-s + 1), s, e-s);
333
char **ret = (char**)pari_malloc(sizeof(char*)*2);
334
str[e-s] = 0;
335
ret[0] = str; ret[1] = NULL;
336
if (GP_DATA->flags & gpd_EMACS) ret = matches_for_emacs("",ret);
337
return ret;
338
}
339
}
340
}
341
}
342
for(i = END-1; i >= start; i--)
343
if (!is_keyword_char(line[i]))
344
{
345
if (line[i] == '.')
346
return get_matches(rl, -1, text, member_generator);
347
break;
348
}
349
return get_matches(rl, END, text, command_generator);
350
}
351
352
static char *
353
pari_completion_word(char *line, long end)
354
{
355
char *s = line + end, *found_quote = NULL;
356
long i;
357
*s = 0; /* truncate at cursor position */
358
for (i=0; i < end; i++)
359
{ /* first look for unclosed string */
360
switch(line[i])
361
{
362
case '"':
363
found_quote = found_quote? NULL: line + i;
364
break;
365
case '\\': i++; break;
366
}
367
}
368
if (found_quote) return found_quote + 1; /* return next char after quote */
369
/* else find beginning of word */
370
while (s > line && is_keyword_char(s[-1])) s--;
371
return s;
372
}
373
374
char **
375
pari_completion_matches(pari_rl_interface *rl, const char *s, long pos, long *wordpos)
376
{
377
char *text, *b;
378
long w;
379
380
if (*rl->line_buffer) pari_free(*rl->line_buffer);
381
*rl->line_buffer = b = pari_strdup(s);
382
text = pari_completion_word(b, pos);
383
w = text - b; if (wordpos) *wordpos = w;
384
/* text = start of expression we complete */
385
*rl->end = strlen(b)-1;
386
*rl->point = pos;
387
return pari_completion(rl, text, w, pos);
388
}
389
390