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