1 /*
2 This file is part of GNU APL, a free implementation of the
3 ISO/IEC Standard 13751, "Programming Language APL, Extended"
4
5 Copyright (C) 2008-2015 Dr. Jürgen Sauermann
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <string.h>
22 #include <strings.h>
23
24 #include "CDR.hh"
25 #include "Command.hh"
26 #include "Function.hh"
27 #include "IndexExpr.hh"
28 #include "IntCell.hh"
29 #include "Output.hh"
30 #include "PrintOperator.hh"
31 #include "Symbol.hh"
32 #include "SymbolTable.hh"
33 #include "SystemVariable.hh"
34 #include "Tokenizer.hh"
35 #include "UserFunction.hh"
36 #include "Value.hh"
37 #include "Workspace.hh"
38
39 //-----------------------------------------------------------------------------
40 Symbol *
lookup_symbol(const UCS_string & sym_name)41 SymbolTable::lookup_symbol(const UCS_string & sym_name)
42 {
43 if (sym_name.size() == 0) return 0;
44 if (Avec::is_quad(sym_name[0])) // should not be called for ⎕xx
45 {
46 CERR << "Symbol is: '" << sym_name << "' at " << LOC << endl;
47 FIXME;
48 }
49
50 const uint32_t hash = compute_hash(sym_name);
51
52 if (symbol_table[hash] == 0) // unused hash value
53 {
54 // sym_name is the first symbol with this hash.
55 // create a new symbol, insert it into symbol_table, and return it.
56 //
57 Log(LOG_SYMBOL_lookup_symbol)
58 {
59 CERR << "Symbol " << sym_name << " has hash " << HEX(hash) << endl;
60 }
61
62 Symbol * new_symbol = new Symbol(sym_name, ID_USER_SYMBOL);
63 symbol_table[hash] = new_symbol;
64 return new_symbol;
65 }
66
67 // One or more symbols with this hash already exist. The number of symbols
68 // with the same name is usually very short, so we walk the list twice.
69 // The first walk checks for a (possibly erased) symbol with the
70 // same name and return it if found.
71 //
72 for (Symbol * sym = symbol_table[hash]; sym; sym = sym->next)
73 {
74 if (sym->equal(sym_name)) // found
75 {
76 return sym;
77 }
78 }
79
80 // no symbol with name sym_name exists. The second walk:
81 //
82 Symbol * sym = new Symbol(sym_name, ID_USER_SYMBOL);
83 add_symbol(sym);
84 return sym;
85 }
86 //-----------------------------------------------------------------------------
87 UCS_string
find_lambda_name(const UserFunction * lambda)88 SymbolTable::find_lambda_name(const UserFunction * lambda)
89 {
90 loop(s, SYMBOL_HASH_TABLE_SIZE)
91 {
92 for (Symbol * sym = symbol_table[s]; sym; sym = sym->next)
93 {
94 if (sym->is_erased()) continue;
95 if (sym->get_ufun_depth(lambda) != -1) return sym->get_name();
96 }
97 }
98
99 return UCS_string();
100 }
101 //-----------------------------------------------------------------------------
102 ostream &
list_symbol(ostream & out,const UCS_string & buf1) const103 SymbolTable::list_symbol(ostream & out, const UCS_string & buf1) const
104 {
105 UCS_string buf(buf1);
106 buf.remove_leading_and_trailing_whitespaces();
107 if (buf1.size() == 1) switch(buf1[0])
108 {
109 case UNI_ALPHA: return Workspace::get_v_ALPHA().print_verbose(out);
110 case UNI_ALPHA_UNDERBAR:
111 return Workspace::get_v_ALPHA_U().print_verbose(out);
112 case UNI_OMEGA: return Workspace::get_v_OMEGA().print_verbose(out);
113 case UNI_CHI: return Workspace::get_v_CHI().print_verbose(out);
114 case UNI_OMEGA_UNDERBAR:
115 return Workspace::get_v_OMEGA_U().print_verbose(out);
116 case UNI_LAMBDA: return Workspace::get_v_LAMBDA().print_verbose(out);
117 default: break;
118 }
119
120 const Symbol * sym = Workspace::lookup_existing_symbol(buf);
121
122 if (sym) return sym->print_verbose(out);
123
124 if (buf1[0] == UNI_Quad_Quad) return out << "System Function" << endl;
125
126 return out << "no symbol '" << buf1 << "'" << endl;
127 }
128 //-----------------------------------------------------------------------------
129 void
list(ostream & out,ListCategory which,UCS_string from_to) const130 SymbolTable::list(ostream & out, ListCategory which, UCS_string from_to) const
131 {
132 UCS_string from;
133 UCS_string to;
134 {
135 const bool bad_from_to = Command::parse_from_to(from, to, from_to);
136 if (bad_from_to)
137 {
138 CERR << "bad range argument" << endl;
139 MORE_ERROR() << "bad range argument " << from_to
140 << ", expecting from-to";
141 return;
142 }
143 }
144
145 // put those symbols into 'list' that satisfy 'which'
146 //
147 std::vector<Symbol *> list;
148 int symbol_count = 0;
149 loop(s, SYMBOL_HASH_TABLE_SIZE)
150 {
151 for (Symbol * sym = symbol_table[s]; sym; sym = sym->next)
152 {
153 if (sym->get_name()[0] == UNI_MUE) continue; // macro
154 ++symbol_count;
155
156 // check range
157 //
158 if (from.size() && sym->get_name().lexical_before(from))
159 {
160 // CERR << "'" << sym->get_name() << "' comes before '"
161 // << from << "'" << endl;
162 continue;
163 }
164 if (to.size() && to.lexical_before(sym->get_name()))
165 {
166 // CERR << "'" << to << "' comes before '"
167 // << sym->get_name() << "'" << endl;
168 continue;
169 }
170
171 if (sym->value_stack.size() == 0)
172 {
173 if (which == LIST_ALL) list.push_back(sym);
174 continue;
175 }
176
177 if (sym->is_erased() && !(which & LIST_ERASED)) continue;
178
179 const NameClass nc = sym->value_stack.back().name_class;
180 if (((nc == NC_VARIABLE) && (which & LIST_VARS)) ||
181 ((nc == NC_FUNCTION) && (which & LIST_FUNS)) ||
182 ((nc == NC_OPERATOR) && (which & LIST_OPERS)) ||
183 ((nc == NC_LABEL) && (which & LIST_LABELS)) ||
184 ((nc == NC_LABEL) && (which & LIST_VARS)) ||
185 ((nc == NC_INVALID) && (which & LIST_INVALID)) ||
186 ((nc == NC_UNUSED_USER_NAME) && (which & LIST_UNUSED)))
187 {
188 list.push_back(sym);
189 }
190 }
191 }
192
193 if (which == LIST_NONE) // )SYMBOLS: display total symbol count
194 {
195 // this could be:
196 //
197 // 1. )SYMBOLS or (show symbol count)
198 // 2. )SYMBOLS N (set symbol count, ignored by GNU APL)
199 //
200 if (from_to.size()) return; // case 2
201 out << "IS " << symbol_count << endl;
202 return;
203 }
204
205 const int count = list.size();
206 UCS_string_vector names;
207 loop(l, count)
208 {
209 UCS_string name = list[l]->get_name();
210 if (which == LIST_NAMES) // append .NC
211 {
212 name.append(UNI_ASCII_FULLSTOP);
213 name.append_number(list[l]->value_stack.back().name_class);
214 }
215 names.push_back(name);
216 }
217
218 names.sort();
219
220 // figure column widths
221 //
222 enum { tabsize = 4 };
223 std::vector<int> col_widths;
224 names.compute_column_width(tabsize, col_widths);
225
226 loop(c, count)
227 {
228 const size_t col = c % col_widths.size();
229 out << names[c];
230 if (col == (col_widths.size() - 1) || c == (count - 1))
231 {
232 // last column or last item: print newline
233 //
234 out << endl;
235 }
236 else
237 {
238 // intermediate column: print spaces
239 //
240 const int len = tabsize*col_widths[col] - names[c].size();
241 Assert(len > 0);
242 loop(l, len) out << " ";
243 }
244 }
245 }
246 //-----------------------------------------------------------------------------
247 void
unmark_all_values() const248 SymbolTable::unmark_all_values() const
249 {
250 loop(s, SYMBOL_HASH_TABLE_SIZE)
251 {
252 for (Symbol * sym = symbol_table[s]; sym; sym = sym->next)
253 {
254 sym->unmark_all_values();
255 }
256 }
257 }
258 //-----------------------------------------------------------------------------
259 int
show_owners(ostream & out,const Value & value) const260 SymbolTable::show_owners(ostream & out, const Value & value) const
261 {
262 int count = 0;
263 loop(s, SYMBOL_HASH_TABLE_SIZE)
264 {
265 for (Symbol * sym = symbol_table[s]; sym; sym = sym->next)
266 {
267 count += sym->show_owners(out, value);
268 }
269 }
270 return count;
271 }
272 //-----------------------------------------------------------------------------
273 void
write_all_symbols(FILE * out,uint64_t & seq) const274 SymbolTable::write_all_symbols(FILE * out, uint64_t & seq) const
275 {
276 loop(s, SYMBOL_HASH_TABLE_SIZE)
277 {
278 for (Symbol * sym = symbol_table[s]; sym; sym = sym->next)
279 {
280 sym->write_OUT(out, seq);
281 }
282 }
283 }
284 //-----------------------------------------------------------------------------
285 void
erase_symbols(ostream & out,const UCS_string_vector & symbols)286 SymbolTable::erase_symbols(ostream & out, const UCS_string_vector & symbols)
287 {
288 int error_count = 0;
289 loop(s, symbols.size())
290 {
291 const bool error = erase_one_symbol(symbols[s]);
292 if (error)
293 {
294 ++error_count;
295 if (error_count == 1) // first error
296 out << "NOT ERASED:";
297 out << " " << symbols[s];
298 }
299 }
300
301 if (error_count) out << endl;
302 }
303 //-----------------------------------------------------------------------------
304 void
clear(ostream & out)305 SymbolTable::clear(ostream & out)
306 {
307 // SymbolTable::clear() should only be called after Workspace::clear_SI()
308 //
309 Assert(Workspace::SI_entry_count() == 0);
310
311 loop(hash, max_symbol_count) clear_slot(out, hash);
312 }
313 //-----------------------------------------------------------------------------
314 void
clear_slot(ostream & out,int hash)315 SymbolTable::clear_slot(ostream & out, int hash)
316 {
317 Symbol * sym = symbol_table[hash];
318 if (sym == 0) return; // no symbol with this hash
319
320 symbol_table[hash] = 0;
321
322 Symbol * next; // the symbol after sym
323 for (; sym; sym = next)
324 {
325 next = sym->next;
326
327 // keep system-defined symbols
328 //
329 if (sym->is_user_defined() && sym->get_name()[0] != UNI_MUE)
330 {
331 sym->call_monitor_callback(SEV_ERASED);
332 delete sym;
333 }
334 else
335 {
336 sym->next = symbol_table[hash];
337 symbol_table[hash] = sym;
338 }
339 }
340 }
341 //-----------------------------------------------------------------------------
342 bool
erase_one_symbol(const UCS_string & sym)343 SymbolTable::erase_one_symbol(const UCS_string & sym)
344 {
345 Symbol * symbol = lookup_existing_symbol(sym);
346
347 if (symbol == 0)
348 {
349 MORE_ERROR() << "Can't )ERASE symbol '" << sym << "': unknown symbol ";
350 return true;
351 }
352
353 if (symbol->is_erased())
354 {
355 if (symbol->value_stack.size() == 1)
356 {
357 MORE_ERROR() << "Can't )ERASE symbol '"
358 << sym << "': already erased";
359 return true;
360 }
361 else // still holding a value
362 {
363 symbol->clear_vs();
364 return false;
365 }
366 }
367
368 if (symbol->value_stack.size() != 1)
369 {
370 MORE_ERROR() << "Can't )ERASE symbol '" << sym
371 << "': symbol is pushed (localized)";
372 return true;
373 }
374
375 if (Workspace::is_called(sym))
376 {
377 MORE_ERROR() << "Can't )ERASE symbol '" << sym
378 << "': symbol is called (is on SI)";
379 return true;
380 }
381
382 ValueStackItem & tos = symbol->value_stack[0];
383
384 switch(tos.name_class)
385 {
386 case NC_LABEL:
387 Assert(0 && "should not happen since stack height == 1");
388 return true;
389
390 case NC_VARIABLE:
391 symbol->expunge();
392 return false;
393
394 case NC_UNUSED_USER_NAME:
395 return true;
396
397 case NC_FUNCTION:
398 case NC_OPERATOR:
399 Assert(tos.sym_val.function);
400 if (tos.sym_val.function->is_native())
401 {
402 symbol->expunge();
403 return false;
404 }
405
406 if (tos.sym_val.function->is_lambda())
407 {
408 symbol->expunge();
409 return false;
410 }
411
412 {
413 const UserFunction * ufun = tos.sym_val.function->get_ufun1();
414 Assert(ufun);
415 if (Workspace::oldest_exec(ufun))
416 {
417 MORE_ERROR() << "Can't )ERASE symbol '" << sym
418 << "': pushed on SI-stack";
419 return true;
420 }
421 }
422
423 delete tos.sym_val.function;
424 tos.sym_val.function = 0;
425 tos.name_class = NC_UNUSED_USER_NAME;
426 return false;
427
428 default: break;
429 }
430
431 Assert(0 && "Bad name_class in SymbolTable::erase_one_symbol()");
432 return true;
433 }
434 //-----------------------------------------------------------------------------
435 std::vector<const Symbol *>
get_all_symbols() const436 SymbolTable::get_all_symbols() const
437 {
438 std::vector<const Symbol *> ret;
439 ret.reserve(1000);
440
441 loop(hash, SYMBOL_HASH_TABLE_SIZE)
442 {
443 for (const Symbol * sym = symbol_table[hash]; sym; sym = sym->next)
444 {
445 ret.push_back(sym);
446 }
447 }
448
449 return ret;
450 }
451 //-----------------------------------------------------------------------------
452 void
dump(ostream & out,int & fcount,int & vcount) const453 SymbolTable::dump(ostream & out, int & fcount, int & vcount) const
454 {
455 std::vector<const Symbol *> symbols;
456 loop(hash, SYMBOL_HASH_TABLE_SIZE)
457 {
458 for (const Symbol * sym = symbol_table[hash]; sym; sym = sym->next)
459 {
460 if (sym->is_erased()) continue;
461 if (sym->value_stack_size() < 1) continue;
462 symbols.push_back(sym);
463 }
464 }
465
466 // sort symbols by name
467 //
468 loop(d, symbols.size())
469 {
470 for (ShapeItem j = d + 1; j < ShapeItem(symbols.size()); ++j)
471 {
472 if (symbols[d]->get_name().compare(symbols[j]->get_name()) > 0)
473 {
474 const Symbol * ss = symbols[d];
475 symbols[d] = symbols[j];
476 symbols[j] = ss;
477 }
478 }
479 }
480
481 // pass 1: functions
482 //
483 loop(s, symbols.size())
484 {
485 const Symbol & sym = *symbols[s];
486 const ValueStackItem & vs = sym[0];
487 if (vs.name_class == NC_FUNCTION) { ++fcount; sym.dump(out); }
488 else if (vs.name_class == NC_OPERATOR) { ++fcount; sym.dump(out); }
489 }
490
491 // pass 2: variables
492 //
493 loop(s, symbols.size())
494 {
495 const Symbol & sym = *symbols[s];
496 const ValueStackItem & vs = sym[0];
497 if (vs.name_class == NC_VARIABLE) { ++vcount; sym.dump(out); }
498 }
499 }
500 //=============================================================================
501 void
clear(ostream & out)502 SystemSymTab::clear(ostream & out)
503 {
504 // SymbolTable::clear() should only be called after Workspace::clear_SI()
505 //
506 Assert(Workspace::SI_entry_count() == 0);
507
508 loop(hash, max_symbol_count) clear_slot(out, hash);
509 }
510 //-----------------------------------------------------------------------------
511 void
clear_slot(ostream & out,int hash)512 SystemSymTab::clear_slot(ostream & out, int hash)
513 {
514 SystemName * sym = symbol_table[hash];
515 symbol_table[hash] = 0;
516
517 while (sym)
518 {
519 // remember sym->next (since sym is being deleted)
520 //
521 SystemName * next = sym->next;
522
523 delete sym;
524 sym = next;
525 }
526 }
527 //-----------------------------------------------------------------------------
528 void
add_fun_or_var(const UCS_string & name,Id id,QuadFunction * function,SystemVariable * variable)529 SystemSymTab::add_fun_or_var(const UCS_string & name, Id id,
530 QuadFunction * function, SystemVariable * variable)
531 {
532 // name should not yet exist
533 //
534 if (lookup_existing_symbol(name))
535 {
536 Q1(name); FIXME;
537 }
538
539 SystemName * dist_name = new SystemName(name, id, function, variable);
540 add_symbol(dist_name);
541 if (max_name_len < name.size()) max_name_len = name.size();
542 }
543 //-----------------------------------------------------------------------------
544