1 
2 /******************************************************************************
3 * MODULE     : drd_info.cpp
4 * DESCRIPTION: data relation descriptions
5 * COPYRIGHT  : (C) 2003  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "vars.hpp"
13 #include "drd_info.hpp"
14 #include "drd_std.hpp"
15 #include "drd_mode.hpp"
16 #include "iterator.hpp"
17 #include "analyze.hpp"
18 
19 /******************************************************************************
20 * Constructors and basic operations
21 ******************************************************************************/
22 
drd_info_rep(string name2)23 drd_info_rep::drd_info_rep (string name2):
24   name (name2), info (tag_info ()), env (UNINIT) {}
drd_info_rep(string name2,drd_info base)25 drd_info_rep::drd_info_rep (string name2, drd_info base):
26   name (name2), info (tag_info (), base->info), env (UNINIT) {}
drd_info(string name)27 drd_info::drd_info (string name):
28   rep (tm_new<drd_info_rep> (name)) {}
drd_info(string name,drd_info base)29 drd_info::drd_info (string name, drd_info base):
30   rep (tm_new<drd_info_rep> (name, base)) {}
31 
32 tree
get_locals()33 drd_info_rep::get_locals () {
34   tree t (COLLECTION);
35   iterator<tree_label> it= iterate (info->item);
36   while (it->busy()) {
37     tree_label l= it->next();
38     tree v= (tree) info->item[l];
39     t << tree (ASSOCIATE, as_string (l), v);
40   }
41   return t;
42 }
43 
44 bool
set_locals(tree t)45 drd_info_rep::set_locals (tree t) {
46   if (!is_func (t, COLLECTION))
47     return false;
48   int i, n= N(t);
49   for (i=0; i<n; i++)
50     if (is_func (t[i], ASSOCIATE, 2) && is_atomic (t[i][0]))
51       info (make_tree_label (t[i][0]->label))= tag_info (t[i][1]);
52   return true;
53 }
54 
55 bool
contains(string l)56 drd_info_rep::contains (string l) {
57   return existing_tree_label (l) && info->contains (as_tree_label (l));
58 }
59 
60 tm_ostream&
operator <<(tm_ostream & out,drd_info drd)61 operator << (tm_ostream& out, drd_info drd) {
62   return out << "drd [" << drd->name << "]";
63 }
64 
65 /******************************************************************************
66 * Tag types
67 ******************************************************************************/
68 
69 void
set_type(tree_label l,int tp)70 drd_info_rep::set_type (tree_label l, int tp) {
71   if (info[l]->pi.freeze_type) return;
72   if (!info->contains (l)) info(l)= copy (info[l]);
73   tag_info& ti= info(l);
74   ti->pi.type= tp;
75 }
76 
77 int
get_type(tree_label l)78 drd_info_rep::get_type (tree_label l) {
79   return info[l]->pi.type;
80 }
81 
82 void
freeze_type(tree_label l)83 drd_info_rep::freeze_type (tree_label l) {
84   if (!info->contains (l)) info(l)= copy (info[l]);
85   tag_info& ti= info(l);
86   ti->pi.freeze_type= true;
87 }
88 
89 int
get_type(tree t)90 drd_info_rep::get_type (tree t) {
91   return info[L(t)]->pi.type;
92 }
93 
94 /******************************************************************************
95 * Arity related methods
96 ******************************************************************************/
97 
98 void
set_arity(tree_label l,int arity,int extra,int am,int cm)99 drd_info_rep::set_arity (tree_label l, int arity, int extra, int am, int cm) {
100   if (info[l]->pi.freeze_arity) return;
101   if (!info->contains (l)) info(l)= copy (info[l]);
102   tag_info& ti= info(l);
103   ti->pi.arity_mode= am;
104   ti->pi.child_mode= cm;
105   if (am != ARITY_VAR_REPEAT) {
106     ti->pi.arity_base = arity;
107     ti->pi.arity_extra= extra;
108   }
109   else {
110     ti->pi.arity_base = extra;
111     ti->pi.arity_extra= arity;
112   }
113   int n;
114   if (arity+extra == 0) n= 0;
115   else if (cm == CHILD_UNIFORM) n= 1;
116   else if (cm == CHILD_BIFORM) n= 2;
117   else n= arity+extra;
118   if (N(ti->ci) != n) ti->ci= array<child_info> (n);
119 }
120 
121 int
get_arity_mode(tree_label l)122 drd_info_rep::get_arity_mode (tree_label l) {
123   return info[l]->pi.arity_mode;
124 }
125 
126 int
get_child_mode(tree_label l)127 drd_info_rep::get_child_mode (tree_label l) {
128   return info[l]->pi.child_mode;
129 }
130 
131 int
get_arity_base(tree_label l)132 drd_info_rep::get_arity_base (tree_label l) {
133   return info[l]->pi.arity_base;
134 }
135 
136 int
get_arity_extra(tree_label l)137 drd_info_rep::get_arity_extra (tree_label l) {
138   return info[l]->pi.arity_extra;
139 }
140 
141 int
get_nr_indices(tree_label l)142 drd_info_rep::get_nr_indices (tree_label l) {
143   return N(info[l]->ci);
144 }
145 
146 void
freeze_arity(tree_label l)147 drd_info_rep::freeze_arity (tree_label l) {
148   if (!info->contains (l)) info(l)= copy (info[l]);
149   tag_info& ti= info(l);
150   ti->pi.freeze_arity= true;
151 }
152 
153 int
get_old_arity(tree_label l)154 drd_info_rep::get_old_arity (tree_label l) {
155   tag_info ti= info[l];
156   if (ti->pi.arity_mode != ARITY_NORMAL) return -1;
157   else return ((int) ti->pi.arity_base) + ((int) ti->pi.arity_extra);
158 }
159 
160 int
get_minimal_arity(tree_label l)161 drd_info_rep::get_minimal_arity (tree_label l) {
162   parent_info pi= info[l]->pi;
163   switch (pi.arity_mode) {
164   case ARITY_NORMAL:
165     return ((int) pi.arity_base) + ((int) pi.arity_extra);
166   case ARITY_OPTIONS:
167   case ARITY_REPEAT:
168   case ARITY_VAR_REPEAT:
169     return ((int) pi.arity_base);
170   }
171   return 0; // NOT REACHED
172 }
173 
174 int
get_maximal_arity(tree_label l)175 drd_info_rep::get_maximal_arity (tree_label l) {
176   parent_info pi= info[l]->pi;
177   switch (pi.arity_mode) {
178   case ARITY_NORMAL:
179   case ARITY_OPTIONS:
180     return ((int) pi.arity_base) + ((int) pi.arity_extra);
181   case ARITY_REPEAT:
182   case ARITY_VAR_REPEAT:
183     return 0x7fffffff;
184   }
185   return 0; // NOT REACHED
186 }
187 
188 bool
correct_arity(tree_label l,int i)189 drd_info_rep::correct_arity (tree_label l, int i) {
190   parent_info pi= info[l]->pi;
191   switch (pi.arity_mode) {
192   case ARITY_NORMAL:
193     return i == ((int) pi.arity_base) + ((int) pi.arity_extra);
194   case ARITY_OPTIONS:
195     return (i >= ((int) pi.arity_base)) &&
196            (i <= ((int) pi.arity_base) + ((int) pi.arity_extra));
197   case ARITY_REPEAT:
198   case ARITY_VAR_REPEAT:
199     return (i >= ((int) pi.arity_base)) &&
200            (((i-pi.arity_base) % pi.arity_extra) == 0);
201   }
202   return false; // NOT REACHED
203 }
204 
205 bool
insert_point(tree_label l,int i,int n)206 drd_info_rep::insert_point (tree_label l, int i, int n) {
207   parent_info pi= info[l]->pi;
208   switch (pi.arity_mode) {
209   case ARITY_NORMAL:
210     return false;
211   case ARITY_OPTIONS:
212     return (i >= ((int) pi.arity_base)) && (i <= n) &&
213            (n < ((int) pi.arity_base) + ((int) pi.arity_extra));
214   case ARITY_REPEAT:
215     return (i >= 0) &&
216            ((i < ((int) pi.arity_base)) ||
217 	    ((i - pi.arity_base) % pi.arity_extra) == 0);
218   case ARITY_VAR_REPEAT:
219     return (i >= 0) &&
220            ((i > (n - ((int) pi.arity_base))) ||
221 	    (i % pi.arity_extra == 0));
222   }
223   return false; // NOT REACHED
224 }
225 
226 bool
is_dynamic(tree t,bool hack)227 drd_info_rep::is_dynamic (tree t, bool hack) {
228   if (hack && L(t) >= START_EXTENSIONS) return true; // FIXME: temporary fix
229   if (is_atomic (t)) return false;
230   if (is_func (t, DOCUMENT) || is_func (t, PARA) || is_func (t, CONCAT) ||
231       is_func (t, TABLE) || is_func (t, ROW)) return false;
232   return info[L(t)]->pi.arity_mode != ARITY_NORMAL;
233 }
234 
235 /******************************************************************************
236 * Border accessability related methods
237 ******************************************************************************/
238 
239 void
set_border(tree_label l,int mode)240 drd_info_rep::set_border (tree_label l, int mode) {
241   if (info[l]->pi.freeze_border) return;
242   if (!info->contains (l)) info(l)= copy (info[l]);
243   tag_info& ti= info(l);
244   ti->pi.border_mode= mode;
245 }
246 
247 int
get_border(tree_label l)248 drd_info_rep::get_border (tree_label l) {
249   return info[l]->pi.border_mode;
250 }
251 
252 void
freeze_border(tree_label l)253 drd_info_rep::freeze_border (tree_label l) {
254   if (!info->contains (l)) info(l)= copy (info[l]);
255   tag_info& ti= info(l);
256   ti->pi.freeze_border= true;
257 }
258 
259 bool
is_child_enforcing(tree t)260 drd_info_rep::is_child_enforcing (tree t) {
261   return ((info[L(t)]->pi.border_mode & BORDER_INNER) != 0) &&
262          (N(t) != 0);
263 }
264 
265 bool
is_parent_enforcing(tree t)266 drd_info_rep::is_parent_enforcing (tree t) {
267   return ((info[L(t)]->pi.border_mode & BORDER_OUTER) != 0) &&
268          (N(t) != 0);
269 }
270 
271 bool
var_without_border(tree_label l)272 drd_info_rep::var_without_border (tree_label l) {
273   return ((info[l]->pi.border_mode & BORDER_INNER) != 0) &&
274          (!std_contains (as_string (l)));
275 }
276 
277 /******************************************************************************
278 * With-like structures correspond to macros which just modify
279 * the current environment, such as the 'strong' tag
280 ******************************************************************************/
281 
282 void
set_with_like(tree_label l,bool is_with_like)283 drd_info_rep::set_with_like (tree_label l, bool is_with_like) {
284   if (info[l]->pi.freeze_with) return;
285   if (!info->contains (l)) info(l)= copy (info[l]);
286   tag_info& ti= info(l);
287   ti->pi.with_like= is_with_like;
288 }
289 
290 bool
get_with_like(tree_label l)291 drd_info_rep::get_with_like (tree_label l) {
292   return info[l]->pi.with_like;
293 }
294 
295 void
freeze_with_like(tree_label l)296 drd_info_rep::freeze_with_like (tree_label l) {
297   if (!info->contains (l)) info(l)= copy (info[l]);
298   tag_info& ti= info(l);
299   ti->pi.freeze_with= true;
300 }
301 
302 bool
is_with_like(tree t)303 drd_info_rep::is_with_like (tree t) {
304   return info[L(t)]->pi.with_like && N(t) > 0;
305 }
306 
307 /******************************************************************************
308 * The var_type determines whether the tag is a regular macro,
309 * or rather an environment variable (which could be a macro).
310 ******************************************************************************/
311 
312 void
set_var_type(tree_label l,int vt)313 drd_info_rep::set_var_type (tree_label l, int vt) {
314   if (info[l]->pi.freeze_with) return;
315   if (!info->contains (l)) info(l)= copy (info[l]);
316   tag_info& ti= info(l);
317   ti->pi.var_type= vt;
318 }
319 
320 int
get_var_type(tree_label l)321 drd_info_rep::get_var_type (tree_label l) {
322   return info[l]->pi.var_type;
323 }
324 
325 void
freeze_var_type(tree_label l)326 drd_info_rep::freeze_var_type (tree_label l) {
327   if (!info->contains (l)) info(l)= copy (info[l]);
328   tag_info& ti= info(l);
329   ti->pi.freeze_with= true;
330 }
331 
332 /******************************************************************************
333 * Other attributes
334 ******************************************************************************/
335 
336 void
set_attribute(tree_label l,string which,tree val)337 drd_info_rep::set_attribute (tree_label l, string which, tree val) {
338   if (!info->contains (l)) info(l)= copy (info[l]);
339   tag_info& ti= info(l);
340   ti->set_attribute (which, val);
341 }
342 
343 tree
get_attribute(tree_label l,string which)344 drd_info_rep::get_attribute (tree_label l, string which) {
345   tree val= info[l]->get_attribute (which);
346   if ((which == "name") && (val == ""))
347     return as_string (l);
348   return val;
349 }
350 
351 void
set_name(tree_label l,string val)352 drd_info_rep::set_name (tree_label l, string val) {
353   set_attribute (l, "name", val);
354 }
355 
356 void
set_long_name(tree_label l,string val)357 drd_info_rep::set_long_name (tree_label l, string val) {
358   set_attribute (l, "long-name", val);
359 }
360 
361 void
set_syntax(tree_label l,tree val)362 drd_info_rep::set_syntax (tree_label l, tree val) {
363   set_attribute (l, "syntax", val);
364 }
365 
366 string
get_name(tree_label l)367 drd_info_rep::get_name (tree_label l) {
368   return as_string (get_attribute (l, "name"));
369 }
370 
371 string
get_long_name(tree_label l)372 drd_info_rep::get_long_name (tree_label l) {
373   string r= as_string (get_attribute (l, "long-name"));
374   if (r != "") return r;
375   return as_string (get_attribute (l, "name"));
376 }
377 
378 tree
get_syntax(tree_label l)379 drd_info_rep::get_syntax (tree_label l) {
380   tree r= get_attribute (l, "syntax");
381   if (r != "") return r;
382   if (env->contains (as_string (l))) return env[as_string (l)];
383   return UNINIT;
384 }
385 
386 static tree
replace(tree t,hashmap<tree,tree> h)387 replace (tree t, hashmap<tree,tree> h) {
388   if (h->contains (t)) return h[t];
389   else if (is_atomic (t)) return t;
390   else {
391     int i, n= N(t);
392     tree r (t, n);
393     for (i=0; i<n; i++)
394       r[i]= replace (t[i], h);
395     return r;
396   }
397 }
398 
399 tree
get_syntax(tree t,path p)400 drd_info_rep::get_syntax (tree t, path p) {
401   if (is_func (t, VALUE, 1) && is_atomic (t[0])) {
402     string s= t[0]->label;
403     if (!existing_tree_label (s)) return UNINIT;
404     return get_syntax (as_tree_label (s));
405   }
406   else if (L(t) < START_EXTENSIONS || is_atomic (t))
407     return UNINIT;
408   else {
409     tree fun= the_drd->get_syntax (L(t));
410     if (fun == UNINIT) return UNINIT;
411     else if (N(t) == 0 && !is_func (fun, MACRO)) return fun;
412     else if (!is_func (fun, MACRO)) return UNINIT;
413     else {
414       int i, n= N(fun)-1;
415       hashmap<tree,tree> tab (UNINIT);
416       for (i=0; i<n; i++) {
417         tree var= tree (ARG, fun[i]);
418         tree val= "";
419         if (i < N(t)) {
420           if (p == path (-1)) val= t[i];
421           else val= tree (QUASI, t[i], (tree) (p * i));
422         }
423         tab (var)= val;
424       }
425       return replace (fun[n], tab);
426     }
427   }
428 }
429 
430 /******************************************************************************
431 * Children's mode
432 ******************************************************************************/
433 
434 void
set_type(tree_label l,int nr,int tp)435 drd_info_rep::set_type (tree_label l, int nr, int tp) {
436   if (!info->contains (l)) info(l)= copy (info[l]);
437   tag_info  & ti= info(l);
438   if (nr >= N(ti->ci)) return;
439   child_info& ci= ti->ci[nr];
440   if (ci.freeze_type) return;
441   ci.type= tp;
442 }
443 
444 int
get_type(tree_label l,int nr)445 drd_info_rep::get_type (tree_label l, int nr) {
446   if (nr >= N(info[l]->ci)) return TYPE_ADHOC;
447   return info[l]->ci[nr].type;
448 }
449 
450 void
freeze_type(tree_label l,int nr)451 drd_info_rep::freeze_type (tree_label l, int nr) {
452   if (!info->contains (l)) info(l)= copy (info[l]);
453   tag_info  & ti= info(l);
454   if (nr >= N(ti->ci)) return;
455   child_info& ci= ti->ci[nr];
456   ci.freeze_type= true;
457 }
458 
459 int
get_type_child(tree t,int i)460 drd_info_rep::get_type_child (tree t, int i) {
461   tag_info ti= info[L(t)];
462   if (is_func (t, EXTERN) && N(t)>0 && is_atomic (t[0])) {
463     tree_label lab= make_tree_label ("extern:" * t[0]->label);
464     if (info->contains(lab)) { ti= info[lab]; }
465     else { ti = info(EXTERN); info(lab)= ti; }
466   }
467   int index= ti->get_index (i, N(t));
468   if ((index<0) || (index>=N(ti->ci))) return TYPE_INVALID;
469   int r= ti->ci[index].type;
470   if (r != TYPE_BINDING) return r;
471   if ((i & 1) == 0) return TYPE_VARIABLE;
472   return TYPE_REGULAR;
473 }
474 
475 /******************************************************************************
476 * Children's accessability related methods
477 ******************************************************************************/
478 
479 void
set_accessible(tree_label l,int nr,int is_accessible)480 drd_info_rep::set_accessible (tree_label l, int nr, int is_accessible) {
481   if (!info->contains (l)) info(l)= copy (info[l]);
482   tag_info  & ti= info(l);
483   if (nr >= N(ti->ci)) return;
484   child_info& ci= ti->ci[nr];
485   if (ci.freeze_accessible) return;
486   ci.accessible= is_accessible;
487 }
488 
489 int
get_accessible(tree_label l,int nr)490 drd_info_rep::get_accessible (tree_label l, int nr) {
491   if (nr >= N(info[l]->ci)) return ACCESSIBLE_NEVER;
492   return info[l]->ci[nr].accessible;
493 }
494 
495 void
freeze_accessible(tree_label l,int nr)496 drd_info_rep::freeze_accessible (tree_label l, int nr) {
497   if (!info->contains (l)) info(l)= copy (info[l]);
498   tag_info  & ti= info(l);
499   if (nr >= N(ti->ci)) return;
500   child_info& ci= ti->ci[nr];
501   ci.freeze_accessible= true;
502 }
503 
504 bool
all_accessible(tree_label l)505 drd_info_rep::all_accessible (tree_label l) {
506   int i, n= N(info[l]->ci);
507   for (i=0; i<n; i++)
508     if (info[l]->ci[i].accessible != ACCESSIBLE_ALWAYS)
509       return false;
510   return n>0;
511 }
512 
513 bool
none_accessible(tree_label l)514 drd_info_rep::none_accessible (tree_label l) {
515   int i, n= N(info[l]->ci);
516   for (i=0; i<n; i++)
517     if (info[l]->ci[i].accessible != ACCESSIBLE_NEVER)
518       return false;
519   return true;
520 }
521 
522 bool
is_accessible_child(tree t,int i)523 drd_info_rep::is_accessible_child (tree t, int i) {
524   //cout << "l= " << as_string (L(t)) << "\n";
525   tag_info ti= info[L(t)];
526   if (is_func (t, EXTERN) && N(t)>0 && is_atomic (t[0])) {
527     tree_label lab= make_tree_label ("extern:" * t[0]->label);
528     if (info->contains(lab)) { ti= info[lab]; }
529     else { ti = info(EXTERN); info(lab)= ti; }
530   }
531   int index= ti->get_index (i, N(t));
532   if ((index<0) || (index>=N(ti->ci))) {
533     if (get_access_mode () == DRD_ACCESS_SOURCE)
534       return !is_atomic (t) && i >= 0 && i < N(t);
535     else return false;
536   }
537   switch (get_access_mode ()) {
538   case DRD_ACCESS_NORMAL:
539     return ti->ci[index].accessible == ACCESSIBLE_ALWAYS;
540   case DRD_ACCESS_HIDDEN:
541     return ti->ci[index].accessible == ACCESSIBLE_ALWAYS ||
542            ti->ci[index].accessible == ACCESSIBLE_HIDDEN;
543   case DRD_ACCESS_SOURCE:
544     return true;
545   }
546   return true; // NOT REACHED
547 }
548 
549 bool
is_accessible_path(tree t,path p)550 drd_info_rep::is_accessible_path (tree t, path p) {
551   if (is_nil (p)) return true;
552   return
553     is_accessible_child (t, p->item) &&
554     (p->item < N(t)) &&
555     is_accessible_path (t[p->item], p->next);
556 }
557 
558 /******************************************************************************
559 * Children's writability
560 ******************************************************************************/
561 
562 void
set_writability(tree_label l,int nr,int writability)563 drd_info_rep::set_writability (tree_label l, int nr, int writability) {
564   if (!info->contains (l)) info(l)= copy (info[l]);
565   tag_info  & ti= info(l);
566   if (nr >= N(ti->ci)) return;
567   child_info& ci= ti->ci[nr];
568   if (ci.freeze_writability) return;
569   ci.writability= writability;
570 }
571 
572 int
get_writability(tree_label l,int nr)573 drd_info_rep::get_writability (tree_label l, int nr) {
574   if (nr >= N(info[l]->ci)) return WRITABILITY_NORMAL;
575   return info[l]->ci[nr].writability;
576 }
577 
578 void
freeze_writability(tree_label l,int nr)579 drd_info_rep::freeze_writability (tree_label l, int nr) {
580   if (!info->contains (l)) info(l)= copy (info[l]);
581   tag_info  & ti= info(l);
582   if (nr >= N(ti->ci)) return;
583   child_info& ci= ti->ci[nr];
584   ci.freeze_writability= true;
585 }
586 
587 int
get_writability_child(tree t,int i)588 drd_info_rep::get_writability_child (tree t, int i) {
589   tag_info ti= info[L(t)];
590   if (is_func (t, EXTERN) && N(t)>0 && is_atomic (t[0])) {
591     tree_label lab= make_tree_label ("extern:" * t[0]->label);
592     if (info->contains(lab)) { ti= info[lab]; }
593     else { ti = info(EXTERN); info(lab)= ti; }
594   }
595   int index= ti->get_index (i, N(t));
596   if ((index<0) || (index>=N(ti->ci))) return WRITABILITY_DISABLE;
597   return ti->ci[index].writability;
598 }
599 
600 /******************************************************************************
601 * Child names, based on set/get attribute for parent
602 ******************************************************************************/
603 
604 void
set_child_name(tree_label l,int nr,string val)605 drd_info_rep::set_child_name (tree_label l, int nr, string val) {
606   set_attribute (l, "name-" * as_string (nr), val);
607 }
608 
609 void
set_child_long_name(tree_label l,int nr,string val)610 drd_info_rep::set_child_long_name (tree_label l, int nr, string val) {
611   set_attribute (l, "long-name-" * as_string (nr), val);
612 }
613 
614 string
get_child_name(tree_label l,int nr)615 drd_info_rep::get_child_name (tree_label l, int nr) {
616   return as_string (get_attribute (l, "name-" * as_string (nr)));
617 }
618 
619 string
get_child_long_name(tree_label l,int nr)620 drd_info_rep::get_child_long_name (tree_label l, int nr) {
621   return as_string (get_attribute (l, "long-name-" * as_string (nr)));
622 }
623 
624 string
get_child_name(tree t,int i)625 drd_info_rep::get_child_name (tree t, int i) {
626   tag_info ti= info[L(t)];
627   if (is_func (t, EXTERN) && N(t)>0 && is_atomic (t[0])) {
628     tree_label lab= make_tree_label ("extern:" * t[0]->label);
629     if (info->contains(lab)) { ti= info[lab]; }
630     else { ti = info(EXTERN); info(lab)= ti; }
631   }
632   int index= ti->get_index (i, N(t));
633   if ((index<0) || (index>=N(ti->ci))) return "";
634   return get_child_name (L(t), index);
635 }
636 
637 string
get_child_long_name(tree t,int i)638 drd_info_rep::get_child_long_name (tree t, int i) {
639   tag_info ti= info[L(t)];
640   if (is_func (t, EXTERN) && N(t)>0 && is_atomic (t[0])) {
641     tree_label lab= make_tree_label ("extern:" * t[0]->label);
642     if (info->contains(lab)) { ti= info[lab]; }
643     else { ti = info(EXTERN); info(lab)= ti; }
644   }
645   int index= ti->get_index (i, N(t));
646   if ((index<0) || (index>=N(ti->ci))) return "";
647   string r= get_child_long_name (L(t), index);
648   if (r != "") return r;
649   return get_child_name (L(t), index);
650 }
651 
652 /******************************************************************************
653 * Environment determination
654 ******************************************************************************/
655 
656 tree
drd_env_write(tree env,string var,tree val)657 drd_env_write (tree env, string var, tree val) {
658   for (int i=0; i<=N(env); i+=2)
659     if (i == N(env))
660       return env * tree (ATTR, var, val);
661     else if (var <= env[i]->label) {
662       if (var == env[i]->label)
663 	return env (0, i) * tree (ATTR, var, val) * env (i+2, N(env));
664       return env (0, i) * tree (ATTR, var, val) * env (i, N(env));
665     }
666   return env;
667 }
668 
669 tree
drd_env_merge(tree env,tree t)670 drd_env_merge (tree env, tree t) {
671   int i, n= N(t);
672   for (i=0; (i+1)<n; i+=2)
673     if (is_atomic (t[i]))
674       env= drd_env_write (env, t[i]->label, t[i+1]);
675   return env;
676 }
677 
678 tree
drd_env_read(tree env,string var,tree val)679 drd_env_read (tree env, string var, tree val) {
680   int i, n= N(env);
681   for (i=0; i<n; i+=2)
682     if (env[i] == var)
683       return env[i+1];
684   return val;
685 }
686 
687 void
set_env(tree_label l,int nr,tree env)688 drd_info_rep::set_env (tree_label l, int nr, tree env) {
689   //if (as_string (l) == "section")
690   //cout << as_string (l) << ", " << nr << " -> " << env << "\n";
691   //if (as_string (l) == "session")
692   //cout << as_string (l) << ", " << nr << " -> " << env << "\n";
693   if (!info->contains (l)) info(l)= copy (info[l]);
694   tag_info  & ti= info(l);
695   if (nr >= N(ti->ci)) return;
696   child_info& ci= ti->ci[nr];
697   if (ci.freeze_env) return;
698   ci.env= drd_encode (env);
699 }
700 
701 tree
get_env(tree_label l,int nr)702 drd_info_rep::get_env (tree_label l, int nr) {
703   if (nr >= N(info[l]->ci)) return tree (ATTR);
704   return drd_decode (info[l]->ci[nr].env);
705 }
706 
707 void
freeze_env(tree_label l,int nr)708 drd_info_rep::freeze_env (tree_label l, int nr) {
709   if (!info->contains (l)) info(l)= copy (info[l]);
710   tag_info  & ti= info(l);
711   if (nr >= N(ti->ci)) return;
712   child_info& ci= ti->ci[nr];
713   ci.freeze_env= true;
714 }
715 
716 tree
get_env_child(tree t,int i,tree env)717 drd_info_rep::get_env_child (tree t, int i, tree env) {
718   if (L(t) == WITH && i == N(t)-1)
719     return drd_env_merge (env, t (0, N(t)-1));
720   else {
721     /* makes cursor movement (is_accessible_cursor) slow for large preambles
722     if (L(t) == DOCUMENT && N(t) > 0 &&
723 	(is_compound (t[0], "hide-preamble", 1) ||
724 	 is_compound (t[0], "show-preamble", 1)))
725       {
726 	tree u= t[0][0];
727 	if (!is_func (u, DOCUMENT)) u= tree (DOCUMENT, u);
728 	tree cenv (ATTR);
729 	for (int i=0; i<N(u); i++)
730 	  if (is_func (u[i], ASSIGN, 2))
731 	    cenv << copy (u[i][0]) << copy (u[i][1]);
732 	env= drd_env_merge (env, cenv);
733       }
734     */
735 
736     tag_info ti= info[L(t)];
737     int index= ti->get_index (i, N(t));
738     if ((index<0) || (index>=N(ti->ci))) return "";
739     tree cenv= drd_decode (ti->ci[index].env);
740     for (int i=1; i<N(cenv); i+=2)
741       if (is_func (cenv[i], ARG, 1) && is_int (cenv[i][0])) {
742 	cenv= copy (cenv);
743 	int j= as_int (cenv[i][0]);
744 	if (j>=0 && j<N(t)) cenv[i]= copy (t[j]);
745       }
746     return drd_env_merge (env, cenv);
747   }
748 }
749 
750 tree
get_env_child(tree t,int i,string var,tree val)751 drd_info_rep::get_env_child (tree t, int i, string var, tree val) {
752   tree env= get_env_child (t, i, tree (ATTR));
753   return drd_env_read (env, var, val);
754 }
755 
756 tree
get_env_descendant(tree t,path p,tree env)757 drd_info_rep::get_env_descendant (tree t, path p, tree env) {
758   if (is_nil (p) || env == "") return env;
759   int  i= p->item;
760   path q= p->next;
761   if (is_compound (t) && i >= 0 && i < N(t))
762     return get_env_descendant (t[i], q, get_env_child (t, i, env));
763   return "";
764 }
765 
766 /******************************************************************************
767 * Heuristic initialization of DRD
768 ******************************************************************************/
769 
770 void
set_environment(hashmap<string,tree> env2)771 drd_info_rep::set_environment (hashmap<string,tree> env2) {
772   env= env2;
773 }
774 
775 tree
arg_access(tree t,tree arg,tree env,int & type)776 drd_info_rep::arg_access (tree t, tree arg, tree env, int& type) {
777   // returns "" if unaccessible and the env if accessible
778   //cout << "  arg_access " << t << ", " << arg << ", " << env << "\n";
779   if (is_atomic (t)) return "";
780   else if (t == arg) return env;
781   else if (is_func (t, QUOTE_ARG, 1) && N(arg) == 1 && t[0] == arg[0])
782     return env;
783   else if (is_func (t, MAP_ARGS) && (t[2] == arg[0])) {
784     if ((N(t) >= 4) && (N(arg) >= 2) && (as_int (t[3]) > as_int (arg[1])))
785       return "";
786     if ((N(t) == 5) && (N(arg) >= 2) && (as_int (t[3]) <= as_int (arg[1])))
787       return "";
788     tree_label inner= make_tree_label (as_string (t[0]));
789     tree_label outer= make_tree_label (as_string (t[1]));
790     if (get_nr_indices (inner) > 0)
791       type= get_type_child (tree (inner, arg), 0);
792     if ((get_nr_indices (inner) > 0) &&
793 	(get_accessible (inner, 0) == ACCESSIBLE_ALWAYS) &&
794 	all_accessible (outer))
795       return env;
796     return "";
797   }
798   else if (is_func (t, MACRO)) return "";
799   else if (is_func (t, WITH)) {
800     int n= N(t)-1;
801     //cout << "env= " << drd_env_merge (env, t (0, n)) << "\n";
802     return arg_access (t[n], arg, drd_env_merge (env, t (0, n)), type);
803   }
804   else if (is_func (t, TFORMAT)) {
805     int n= N(t)-1;
806     tree oldf= drd_env_read (env, CELL_FORMAT, tree (TFORMAT));
807     tree newf= oldf * tree (TFORMAT, A (t (0, n)));
808     tree w   = tree (ATTR, CELL_FORMAT, newf);
809     tree cenv= get_env_child (t, n, drd_env_merge (env, w));
810     return arg_access (t[n], arg, cenv, type);
811   }
812   else if (is_func (t, COMPOUND) && N(t) >= 1 && is_atomic (t[0]))
813     return arg_access (compound (t[0]->label, A (t (1, N(t)))),
814 		       arg, env, type);
815   else if ((is_func (t, IF) || is_func (t, VAR_IF)) && N(t) >= 2)
816     return arg_access (t[1], arg, env, type);
817   else {
818     int i, n= N(t);
819     for (i=0; i<n; i++) {
820       int  ctype= get_type_child (t, i);
821       tree cenv = get_env_child (t, i, env);
822       tree aenv = arg_access (t[i], arg, cenv, ctype);
823       if (aenv != "") {
824 	if (ctype != TYPE_INVALID) type= ctype;
825 	if (is_accessible_child (t, i)) return aenv;
826       }
827       else if (type == TYPE_UNKNOWN &&
828                ctype != TYPE_INVALID &&
829                ctype != TYPE_UNKNOWN) {
830         type= ctype;
831         //cout << "  found type " << t << ", " << arg << ", " << type << "\n";
832       }
833     }
834     return "";
835   }
836 }
837 
838 static void
rewrite_symbolic_arguments(tree macro,tree & env)839 rewrite_symbolic_arguments (tree macro, tree& env) {
840   if (!is_func (env, ATTR)) return;
841   for (int i=1; i<N(env); i+=2)
842     if (is_func (env[i], ARG, 1)) {
843       for (int j=0; j+1<N(macro); j++)
844 	if (macro[j] == env[i][0])
845 	  env[i]= tree (ARG, as_tree (j));
846     }
847 }
848 
849 static bool
is_length(string s)850 is_length (string s) {
851   int i;
852   for (i=0; (i<N(s)) && ((s[i]<'a') || (s[i]>'z')); i++) {}
853   return is_double (s (0, i)) && is_locase_alpha (s (i, N(s)));
854 }
855 
856 bool
heuristic_with_like(tree t,tree arg)857 drd_info_rep::heuristic_with_like (tree t, tree arg) {
858   if (arg == "") {
859     if (!is_func (t, MACRO) || N(t) < 2) return false;
860     return heuristic_with_like (t[N(t)-1], t[N(t)-2]);
861   }
862   else if (t == tree (ARG, arg))
863     return true;
864   else if (is_with_like (t))
865     return heuristic_with_like (t[N(t)-1], arg);
866   else return false;
867 }
868 
869 bool
heuristic_init_macro(string var,tree macro)870 drd_info_rep::heuristic_init_macro (string var, tree macro) {
871   //cout << "init_macro " << var << " -> " << macro << "\n";
872   tree_label l = make_tree_label (var);
873   tag_info old_ti= copy (info[l]);
874   int i, n= N(macro)-1;
875   set_arity (l, n, 0, ARITY_NORMAL, CHILD_DETAILED);
876   if (n == 0 && is_compound (macro[0], "localize", 1) &&
877       is_atomic (macro[0][0])) {
878     set_type (l, TYPE_STRING);
879     set_var_type (l, VAR_MACRO_PARAMETER);
880   }
881   else if (n == 0 && is_atomic (macro[0]) &&
882            is_length (macro[0]->label)) {
883     set_type (l, TYPE_LENGTH);
884     set_var_type (l, VAR_MACRO_PARAMETER);
885   }
886   else set_type (l, get_type (macro[n]));
887   set_with_like (l, heuristic_with_like (macro, ""));
888   //if (heuristic_with_like (macro, ""))
889   //cout << "With-like: " << var << LF;
890   for (i=0; i<n; i++) {
891     if (is_atomic (macro[i]))
892       if (l >= START_EXTENSIONS || get_child_name (l, i) == "")
893         set_child_name (l, i, macro[i]->label);
894     int  type= TYPE_UNKNOWN;
895     tree arg (ARG, macro[i]);
896     tree env= arg_access (macro[n], arg, tree (ATTR), type);
897     //if (var == "section" || var == "section-title")
898     //cout << var << " -> " << env << ", " << macro << "\n";
899     //if (var == "math")
900     //cout << var << ", " << i << " -> " << type << ", " << env << ", " << macro << "\n";
901     set_type (l, i, type);
902     if (env != "") {
903       //if (var == "eqnarray*")
904       //cout << var << " -> " << env << "\n";
905       //if (var == "session")
906       //cout << var << " = " << macro << ", " << i << " -> " << env << "\n";
907       rewrite_symbolic_arguments (macro, env);
908       set_accessible (l, i, ACCESSIBLE_ALWAYS);
909       set_env (l, i, env);
910     }
911   }
912   //if (old_ti != info[l])
913   //cout << var << ": " << old_ti << " -> " << info[l] << "\n";
914   return (old_ti != info[l]);
915 }
916 
917 static int
minimal_arity(tree t,tree var)918 minimal_arity (tree t, tree var) {
919   if (is_atomic (t)) return 0;
920   else if (is_func (t, ARG, 2) && (t[0] == var))
921     return as_int (t[1]) + 1;
922   else if (is_func (t, MAP_ARGS) && (N(t)>=4) && (t[2] == var))
923     return as_int (t[3]);
924   else {
925     int i, n= N(t), m= 0;
926     for (i=0; i<n; i++)
927       m= max (m, minimal_arity (t[i], var));
928     return m;
929   }
930 }
931 
932 bool
heuristic_init_xmacro(string var,tree xmacro)933 drd_info_rep::heuristic_init_xmacro (string var, tree xmacro) {
934   tree_label l = make_tree_label (var);
935   tag_info old_ti= copy (info[l]);
936   int i, m= minimal_arity (xmacro[1], xmacro[0]);
937   set_arity (l, m, 1, ARITY_REPEAT, CHILD_DETAILED);
938   set_type (l, get_type (xmacro[1]));
939   for (i=0; i<=m; i++) {
940     int type= TYPE_UNKNOWN;
941     tree arg (ARG, xmacro[0], as_string (i));
942     tree env= arg_access (xmacro[1], arg, tree (ATTR), type);
943     //cout << var << ", " << xmacro << ", " << i << " -> " << type << "\n";
944     set_type (l, i, type);
945     if (env != "") {
946       set_accessible (l, i, ACCESSIBLE_ALWAYS);
947       set_env (l, i, env);
948     }
949   }
950   // if (old_ti != info[l])
951   //   cout << var << ": " << old_ti << " -> " << info[l] << "\n";
952   return (old_ti != info[l]);
953 }
954 
955 bool
heuristic_init_parameter(string var,string val)956 drd_info_rep::heuristic_init_parameter (string var, string val) {
957   tree_label l = make_tree_label (var);
958   tag_info old_ti= copy (info[l]);
959   set_arity (l, 0, 0, ARITY_NORMAL, CHILD_UNIFORM);
960   set_var_type (l, VAR_PARAMETER);
961   if (ends (var, "-color")) set_type (l, TYPE_COLOR);
962   else if (ends (var, "-length")) set_type (l, TYPE_LENGTH);
963   else if (ends (var, "-width")) set_type (l, TYPE_LENGTH);
964   else if (val == "true" || val == "false") set_type (l, TYPE_BOOLEAN);
965   else if (is_int (val)) set_type (l, TYPE_INTEGER);
966   else if (is_double (val)) set_type (l, TYPE_NUMERIC);
967   else if (is_length (val)) set_type (l, TYPE_LENGTH);
968   else set_type (l, TYPE_STRING);
969   return (old_ti != info[l]);
970 }
971 
972 bool
heuristic_init_parameter(string var,tree val)973 drd_info_rep::heuristic_init_parameter (string var, tree val) {
974   tree_label l = make_tree_label (var);
975   tag_info old_ti= copy (info[l]);
976   set_arity (l, 0, 0, ARITY_NORMAL, CHILD_UNIFORM);
977   set_var_type (l, VAR_PARAMETER);
978   if (ends (var, "-color")) set_type (l, TYPE_COLOR);
979   else if (ends (var, "-length")) set_type (l, TYPE_LENGTH);
980   else if (ends (var, "-width")) set_type (l, TYPE_LENGTH);
981   set_type (l, get_type (val));
982   return (old_ti != info[l]);
983 }
984 
985 void
heuristic_init(hashmap<string,tree> env2)986 drd_info_rep::heuristic_init (hashmap<string,tree> env2) {
987   // time_t tt= texmacs_time ();
988   set_environment (env2);
989   bool flag= true;
990   int round= 0;
991   while (flag) {
992     // cout << HRULE;
993     flag= false;
994     iterator<string> it= iterate (env);
995     while (it->busy()) {
996       string var= it->next();
997       tree   val= env[var];
998       if (is_atomic (val))
999         flag= heuristic_init_parameter (var, val->label) | flag;
1000       else if (is_func (val, MACRO))
1001 	flag= heuristic_init_macro (var, val) | flag;
1002       else if (is_func (val, XMACRO))
1003 	flag= heuristic_init_xmacro (var, val) | flag;
1004       else
1005         flag= heuristic_init_parameter (var, val) | flag;
1006     }
1007     if ((round++) == 10) {
1008       cout << "TeXmacs] Warning: bad heuristic drd convergence\n";
1009       flag= false;
1010     }
1011   }
1012   // cout << "--> " << (texmacs_time ()-tt) << "ms\n";
1013 }
1014