NEURON
parsact.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 
3 /*
4  * some parse actions to reduce size of parse.ypp the installation routines can
5  * also be used, e.g. in sens to automattically construct variables
6  */
7 
8 #include <stdlib.h>
9 #include "modl.h"
10 #include "parse1.hpp"
11 
12 Symbol* scop_indep; /* independent used by SCoP */
13 Symbol* indepsym; /* only one independent variable */
14 Symbol* stepsym; /* one or fewer stepped variables */
15 List* indeplist; /* FROM TO WITH START UNITS */
16 List* watch_alloc; /* text of the void _watch_alloc(Datum*) function */
17 extern List* syminorder; /* Order in which variables are output to
18  * .var file */
19 
20 extern List* state_discon_list_;
21 extern int net_send_seen_;
22 extern int net_event_seen_;
23 extern int watch_seen_;
24 
28 extern int artificial_cell;
29 extern int vectorize;
30 extern int assert_threadsafe;
31 
33  // give an error message if a variable is explicitly declared
34  // more than once.
35 
36  Symbol* sym = SYM(q);
37 
38  if (sym->usage & EXPLICIT_DECL) {
39  diag("Multiple declaration of ", sym->name);
40  }
41  sym->usage |= EXPLICIT_DECL;
42  /* this ensures that declared PRIMES will appear in .var file */
43  sym->usage |= DEP;
44 }
45 
46 void parm_array_install(Symbol* n, const char* num, char* units, char* limits, int index) {
47  char buf[NRN_BUFSIZE];
48 
49  if (n->u.str == (char*) 0)
51  n->subtype |= PARM;
52  n->subtype |= ARRAY;
53  n->araydim = index;
54  Sprintf(buf, "[%d]\n%s\n%s\n%s\n", index, num, units, limits);
55  n->u.str = stralloc(buf, (char*) 0);
56 }
57 
58 void parminstall(Symbol* n, const char* num, const char* units, const char* limits) {
59  char buf[NRN_BUFSIZE];
60 
61  if (n->u.str == (char*) 0)
63  n->subtype |= PARM;
64  Sprintf(buf, "\n%s\n%s\n%s\n", num, units, limits);
65  n->u.str = stralloc(buf, (char*) 0);
66 }
67 
68 /* often we want to install a parameter by default but only
69 if the user hasn't declared it herself.
70 */
72  const char* num,
73  const char* units,
74  const char* limits) {
75  Symbol* s;
76 
77  if ((s = lookup(name)) == SYM0) {
78  s = install(name, NAME);
79  parminstall(s, num, units, limits);
80  }
81  if (!(s->subtype)) {
82  /* can happen when PRIME used in MATCH */
83  parminstall(s, num, units, limits);
84  }
85  if (!(s->subtype & PARM)) {
86  /* special case is scop_indep can be a PARM but not indepsym */
87  if (scop_indep == indepsym || s != scop_indep) {
88  diag(s->name, "can't be declared a parameter by default");
89  }
90  }
91  return s;
92 }
93 
94 static const char* indepunits = "";
95 
97  const char* from,
98  const char* to,
99  const char* with,
100  const char* units) {
101  char buf[NRN_BUFSIZE];
102 
103  /* scop_indep may turn out to be different from indepsym. If this is the case
104  then indepsym will be a constant in the .var file (see parout.c).
105  If they are the same, then u.str gets the info from SCOP.
106  */
107  if (indepsym) {
108  diag("Only one independent variable can be defined", (char*) 0);
109  }
110  indeplist = newlist();
111  Lappendstr(indeplist, from);
112  Lappendstr(indeplist, to);
113  Lappendstr(indeplist, with);
114  Lappendstr(indeplist, from);
116  n->subtype |= INDEP;
117  indepunits = stralloc(units, (char*) 0);
118  if (n != scop_indep) {
119  Sprintf(buf, "\n%s*%s(%s)\n%s\n", from, to, with, units);
120  n->u.str = stralloc(buf, (char*) 0);
121  }
122  indepsym = n;
123  if (!scop_indep) {
125  }
126 }
127 
128 /*
129  * installation of dependent and state variables type 0 -- dependent; 1 --
130  * state index 0 -- scalar; otherwise -- array qs -- item pointer to START
131  * const string makeconst 0 -- do not make a default constant for state 1 --
132  * make sure name0 exists For states Dname and name0 are normally created.
133  * However Dname will not appear in the .var file unless it is used -- see
134  * parout.c.
135  */
136 void depinstall(int type,
137  Symbol* n,
138  int index,
139  const char* from,
140  const char* to,
141  const char* units,
142  Item* qs,
143  int makeconst,
144  const char* abstol) {
145  char buf[NRN_BUFSIZE];
146  int c;
147 
148  if (!type && strlen(abstol) > 0) {
149  printf("abstol = |%s|\n", abstol);
150  diag(n->name, "tolerance can be specified only for a STATE");
151  }
152  if (n->u.str == (char*) 0)
154  if (type) {
155  n->subtype |= STAT;
156  c = ':';
157  statdefault(n, index, units, qs, makeconst);
158  } else {
159  n->subtype |= DEP;
160  c = ';';
161  if (qs) {
162  diag("START not legal except in STATE block", (char*) 0);
163  }
164  }
165  if (index) {
166  Sprintf(buf, "[%d]\n%s%c%s\n%s\n%s\n", index, from, c, to, units, abstol);
167  n->araydim = index;
168  n->subtype |= ARRAY;
169  } else {
170  Sprintf(buf, "\n%s%c%s\n%s\n%s\n", from, c, to, units, abstol);
171  }
172  n->u.str = stralloc(buf, (char*) 0);
173 }
174 
175 void statdefault(Symbol* n, int index, const char* units, Item* qs, int makeconst) {
176  char nam[256], *un;
177  Symbol* s;
178 
179  if (n->type != NAME && n->type != PRIME) {
180  diag(n->name, "can't be a STATE");
181  }
182  if (makeconst) {
183  Sprintf(nam, "%s0", n->name);
184  s = ifnew_parminstall(nam, "0", units, "");
185  if (qs) { /*replace with proper default*/
186  parminstall(s, STR(qs), units, "");
187  }
188  }
189  Sprintf(nam, "%s/%s", units, indepunits);
190  un = stralloc(nam, (char*) 0);
191  Sprintf(nam, "D%s", n->name);
192  if ((s = lookup(nam)) == SYM0) { /* install the prime as a DEP */
193  s = install(nam, PRIME);
194  depinstall(0, s, index, "0", "1", un, ITEM0, 0, "");
195  }
196 }
197 
198 /* the problem is that qpar->next may already have a _p, ..., _nt
199 vectorize_substitute, and qpar->next is often normally "" instead of ')'
200 for the no arg case.
201 */
202 static int func_arg_examine(Item* qpar, Item* qend) {
203  Item* q;
204  int b = 1; /* real args exist case */
205  q = qpar->next;
206  if (q->itemtype == SYMBOL && strcmp(SYM(q)->name, ")") == 0) {
207  b = 0; /* definitely no arg */
208  }
209  if (q->itemtype == STRING && strcmp(STR(q), "") == 0) {
211  b = 2; /* _p,..._nt already there */
212  } else if (q->next->itemtype == SYMBOL && strcmp(SYM(q->next)->name, ")") == 0) {
213  b = 0; /* definitely no arg */
214  }
215  }
216  return b;
217 }
218 
220  Item* q;
221  return;
222  for (q = q1; q != q2; q = q->next) {
223  if (q->itemtype == SYMBOL) {
224  Symbol* s = SYM(q);
225  if ((s->usage & FUNCT) && !(s->subtype & (EXTDEF | EXTDEF_RANDOM))) {
226  if (q->next->itemtype == SYMBOL && strcmp(SYM(q->next)->name, "(") == 0) {
227  int b = func_arg_examine(q->next, q2);
228  if (b == 0) { /* no args */
229  vectorize_substitute(q->next, "(_threadargs_");
230  } else if (b == 1) { /* real args */
231  vectorize_substitute(q->next, "(_threadargscomma_");
232  } /* else no _p.._nt already there */
233  }
234  }
235  }
236  }
237 }
238 
239 void defarg(Item* q1, Item* q2) /* copy arg list and define as doubles */
240 {
241  Item* q;
242 
243  if (q1->next == q2) {
244  vectorize_substitute(insertstr(q2, ""), "_internalthreadargsproto_");
245  return;
246  }
247  for (q = q1->next; q != q2; q = q->next) {
248  if (strcmp(SYM(q)->name, ",") != 0) {
249  insertstr(q, "double");
250  }
251  }
252  vectorize_substitute(insertstr(q1->next, ""), "_internalthreadargsprotocomma_");
253 }
254 
255 void lag_stmt(Item* q1, int blocktype) /* LAG name1 BY name2 */
256 {
257  Symbol *name1, *name2, *lagval;
258 
259  /*ARGSUSED*/
260  /* parse */
261  name1 = SYM(q1->next);
262  remove(q1->next);
263  remove(q1->next);
264  name2 = SYM(q1->next);
265  remove(q1->next);
266  name1->usage |= DEP;
267  name2->usage |= DEP;
268  /* check */
269  if (!indepsym) {
270  diag("INDEPENDENT variable must be declared to process", "the LAG statement");
271  }
272  if (!(name1->subtype & (DEP | STAT))) {
273  diag(name1->name, "not a STATE or DEPENDENT variable");
274  }
275  if (!(name2->subtype & (PARM | nmodlCONST))) {
276  diag(name2->name, "not a CONSTANT or PARAMETER");
277  }
278  Sprintf(buf, "lag_%s_%s", name1->name, name2->name);
279  if (lookup(buf)) {
280  diag(buf, "already in use");
281  }
282  /* create */
283  lagval = install(buf, NAME);
284  lagval->usage |= DEP;
285  lagval->subtype |= DEP;
286  if (name1->subtype & ARRAY) {
287  lagval->subtype |= ARRAY;
288  lagval->araydim = name1->araydim;
289  }
290  if (lagval->subtype & ARRAY) {
291  Sprintf(buf, "static double *%s;\n", lagval->name);
293  Sprintf(buf,
294  "%s = lag(%s, %s, %s, %d);\n",
295  lagval->name,
296  name1->name,
297  indepsym->name,
298  name2->name,
299  lagval->araydim);
300  } else {
301  Sprintf(buf, "static double %s;\n", lagval->name);
303  Sprintf(buf,
304  "%s = *lag(&(%s), %s, %s, 0);\n",
305  lagval->name,
306  name1->name,
307  indepsym->name,
308  name2->name);
309  }
310  replacstr(q1, buf);
311 }
312 
314  static int reset_fun_cnt = 0;
315 
316  reset_fun_cnt++;
317  Sprintf(buf, "&_reset, &_freset%d,", reset_fun_cnt);
318  Insertstr(q->next, buf);
319  Sprintf(buf, "static double _freset%d;\n", reset_fun_cnt);
321 }
322 
324  vectorize_substitute(insertstr(q->next, "nrn_threads,"), "_nt,");
325 }
326 
327 /* table manipulation */
328 /* arglist must have exactly one argument
329  tablist contains 1) list of names to be looked up (must be empty if
330  qtype is FUNCTION and nonempty if qtype is PROCEDURE).
331  2) From expression list
332  3) To expression list
333  4) With integer string
334  5) DEPEND list as list of names
335  The qname does not have a _l if a function. The arg names all have _l
336  prefixes.
337 */
338 /* checking and creation of table has been moved to separate function called
339  static _check_func.
340 */
341 /* to allow vectorization the table functions are separated into
342  name robust function. makes sure
343  table is uptodate (calls check_name)
344  _check_name if table not up to date then builds table
345  _f_name analytic
346  _n_name table lookup with no checking if usetable=1
347  otherwise calls _f_name.
348 */
349 
352 
353 void check_tables() {
354  /* for threads do this differently */
356  fprintf(fcout, "\n#if %d\n", 0);
358  fprintf(fcout, "#endif\n");
359  }
360 }
361 
362 /* this way we can make sure the tables are up to date in the main thread
363 at critical points in the finitialize, nrn_fixed_step, etc. The only
364 requirement is that the function that generates the table not use
365 any except GLOBAL parameters and assigned vars not requiring
366 an initial value, because we are probably going to
367 call this with nonsense _p, _ppvar, and _thread
368 */
371  Item* q;
374  Sprintf(buf, "\nstatic void %s(_internalthreadargsproto_);", STR(q));
375  lappendstr(p, buf);
376  }
377  lappendstr(
378  p,
379  "\n"
380  "static void _check_table_thread(_threadargsprotocomma_ int _type, "
381  "_nrn_model_sorted_token const& _sorted_token) {\n"
382  " if (gind != 0 && _thread != nullptr) { _globals = _thread[_gth].get<double*>(); } \n"
383  " _nrn_mechanism_cache_range _lmr{_sorted_token, *_nt, *_ml, _type};\n"
384  " {\n"
385  " auto* const _ml = &_lmr;\n");
387  Sprintf(buf, " %s(_threadargs_);\n", STR(q));
388  lappendstr(p, buf);
389  }
390  lappendstr(p,
391  " }\n"
392  "}\n");
393  return 1;
394  }
395  return 0;
396 }
397 
398 void table_massage(List* tablist, Item* qtype, Item* qname, List* arglist) {
399  Symbol *fsym, *s, *arg = 0;
400  char* fname;
401  List *table, *from, *to, *depend;
402  int type, ntab;
403  Item* q;
404 
405  if (!tablist) {
406  return;
407  }
408  fsym = SYM(qname);
409  last_func_using_table = fsym;
410  fname = fsym->name;
411  table = LST(q = tablist->next);
412  from = LST(q = q->next);
413  to = LST(q = q->next);
414  ntab = atoi(STR(q = q->next));
415  depend = LST(q = q->next);
416  type = SYM(qtype)->type;
417 
418  ifnew_parminstall("usetable", "1", "", "0 1");
419  if (!check_table_statements) {
421  }
422  Sprintf(buf, "_check_%s();\n", fname);
424  Sprintf(buf, "_check_%s(_threadargs_);\n", fname);
426  /*checking*/
427  if (type == FUNCTION1) {
428  if (table) {
429  diag("TABLE stmt in FUNCTION cannot have a table name list", (char*) 0);
430  }
431  table = newlist();
432  Lappendsym(table, fsym);
433  } else {
434  if (!table) {
435  diag("TABLE stmt in PROCEDURE must have a table name list", (char*) 0);
436  }
437  }
438  if (arglist->next == arglist || arglist->next->next != arglist) {
439  diag("FUNCTION or PROCEDURE containing a TABLE stmt\n", "must have exactly one argument");
440  } else {
441  arg = SYM(arglist->next);
442  }
443  if (!depend) {
444  depend = newlist();
445  }
446 
447  /*translation*/
448  /* new name for original function */
449  Sprintf(buf, "_f_%s", fname);
450  SYM(qname) = install(buf, fsym->type);
451  SYM(qname)->subtype = fsym->subtype;
452  SYM(qname)->varnum = fsym->varnum;
453  if (type == FUNCTION1) {
454  fsym->subtype |= FUNCT;
455  Sprintf(buf, "static double _n_%s(double);\n", fname);
456  q = linsertstr(procfunc, buf);
457  Sprintf(buf, "static double _n_%s(_internalthreadargsprotocomma_ double _lv);\n", fname);
459  } else {
460  fsym->subtype |= PROCED;
461  Sprintf(buf, "static void _n_%s(double);\n", fname);
462  q = linsertstr(procfunc, buf);
463  Sprintf(buf, "static void _n_%s(_internalthreadargsprotocomma_ double _lv);\n", fname);
465  }
466  fsym->usage |= FUNCT;
467 
468  /* declare communication between func and check_func */
469  Sprintf(buf, "static double _mfac_%s, _tmin_%s;\n", fname, fname);
471 
472  /* create the check function */
475  }
476  Sprintf(buf, "_check_%s", fname);
478  Sprintf(buf, "static void _check_%s();\n", fname);
479  q = insertstr(procfunc, buf);
480  vectorize_substitute(q, "");
481  Sprintf(buf, "static void _check_%s() {\n", fname);
482  q = lappendstr(procfunc, buf);
483  Sprintf(buf, "static void _check_%s(_internalthreadargsproto_) {\n", fname);
485  Lappendstr(procfunc, " static int _maktable=1; int _i, _j, _ix = 0;\n");
486  Lappendstr(procfunc, " double _xi, _tmax;\n");
487  ITERATE(q, depend) {
488  Sprintf(buf, " static double _sav_%s;\n", SYM(q)->name);
490  }
491  lappendstr(procfunc, " if (!usetable) {return;}\n");
492  /*allocation*/
493  ITERATE(q, table) {
494  s = SYM(q);
495  if (s->subtype & ARRAY) {
496  Sprintf(buf,
497  " for (_i=0; _i < %d; _i++) {\
498  _t_%s[_i] = makevector(%d*sizeof(double)); }\n",
499  s->araydim,
500  s->name,
501  ntab + 1);
502  } else {
503  Sprintf(buf, " _t_%s = makevector(%d*sizeof(double));\n", s->name, ntab + 1);
504  }
506  }
507  /* check dependency */
508  ITERATE(q, depend) {
509  Sprintf(buf, " if (_sav_%s != %s) { _maktable = 1;}\n", SYM(q)->name, SYM(q)->name);
511  }
512  /* make the table */
513  Lappendstr(procfunc, " if (_maktable) { double _x, _dx; _maktable=0;\n");
514  Sprintf(buf, " _tmin_%s = ", fname);
516  move(from->next, from->prev, procfunc);
517  Sprintf(buf, ";\n _tmax = ");
519  move(to->next, to->prev, procfunc);
520  Lappendstr(procfunc, ";\n");
521  Sprintf(buf, " _dx = (_tmax - _tmin_%s)/%d.; _mfac_%s = 1./_dx;\n", fname, ntab, fname);
523  Sprintf(buf, " for (_i=0, _x=_tmin_%s; _i < %d; _x += _dx, _i++) {\n", fname, ntab + 1);
525  if (type == FUNCTION1) {
526  ITERATE(q, table) {
527  s = SYM(q);
528  Sprintf(buf, " _t_%s[_i] = _f_%s(_x);\n", s->name, fname);
530  Sprintf(buf, " _t_%s[_i] = _f_%s(_threadargscomma_ _x);\n", s->name, fname);
532  }
533  } else {
534  Sprintf(buf, " _f_%s(_x);\n", fname);
536  Sprintf(buf, " _f_%s(_threadargscomma_ _x);\n", fname);
538  ITERATE(q, table) {
539  s = SYM(q);
540  if (s->subtype & ARRAY) {
541  Sprintf(buf,
542  " for (_j = 0; _j < %d; _j++) { _t_%s[_j][_i] = %s[_j];\n}",
543  s->araydim,
544  s->name,
545  s->name);
546  } else {
547  Sprintf(buf, " _t_%s[_i] = %s;\n", s->name, s->name);
548  }
550  }
551  }
552  Lappendstr(procfunc, " }\n"); /*closes loop over _i index*/
553  /* save old dependency values */
554  ITERATE(q, depend) {
555  s = SYM(q);
556  Sprintf(buf, " _sav_%s = %s;\n", s->name, s->name);
558  }
559  Lappendstr(procfunc, " }\n"); /* closes if(maktable)) */
560  Lappendstr(procfunc, "}\n\n");
561 
562 
563  /* create the new function (steers to analytic or table) */
564  /*declaration*/
565  if (type == FUNCTION1) {
566 #define GLOBFUNC 1
567  Lappendstr(procfunc, "double");
568  } else {
569  Lappendstr(procfunc, "static int");
570  }
571  Sprintf(buf, "%s(double %s){", fname, arg->name);
573  Sprintf(buf, "%s(_internalthreadargsprotocomma_ double %s) {", fname, arg->name);
575  /* check the table */
576  Sprintf(buf, "_check_%s();\n", fname);
577  q = lappendstr(procfunc, buf);
578  Sprintf(buf, "\n#if 0\n_check_%s(_threadargs_);\n#endif\n", fname);
580  if (type == FUNCTION1) {
581  Lappendstr(procfunc, "return");
582  }
583  Sprintf(buf, "_n_%s(%s);\n", fname, arg->name);
585  Sprintf(buf, "_n_%s(_threadargscomma_ %s);\n", fname, arg->name);
587  if (type != FUNCTION1) {
588  Lappendstr(procfunc, "return 0;\n");
589  }
590  Lappendstr(procfunc, "}\n\n"); /* end of new function */
591 
592  /* _n_name function for table lookup with no checking */
593  if (type == FUNCTION1) {
594  Lappendstr(procfunc, "static double");
595  } else {
596  Lappendstr(procfunc, "static void");
597  }
598  Sprintf(buf, "_n_%s(double %s){", fname, arg->name);
600  Sprintf(buf, "_n_%s(_internalthreadargsprotocomma_ double %s){", fname, arg->name);
602  Lappendstr(procfunc, "int _i, _j;\n");
603  Lappendstr(procfunc, "double _xi, _theta;\n");
604 
605  /* usetable */
606  Lappendstr(procfunc, "if (!usetable) {\n");
607  if (type == FUNCTION1) {
608  Lappendstr(procfunc, "return");
609  }
610  Sprintf(buf, "_f_%s(%s);", fname, arg->name);
612  Sprintf(buf, "_f_%s(_threadargscomma_ %s);", fname, arg->name);
614  if (type != FUNCTION1) {
615  Lappendstr(procfunc, "return;");
616  }
617  Lappendstr(procfunc, "\n}\n");
618 
619  /* table lookup */
620  Sprintf(buf, "_xi = _mfac_%s * (%s - _tmin_%s);\n", fname, arg->name, fname);
622  Lappendstr(procfunc, "if (std::isnan(_xi)) {\n");
623  if (type == FUNCTION1) {
624  Lappendstr(procfunc, " return _xi; }\n");
625  } else {
626  ITERATE(q, table) {
627  s = SYM(q);
628  if (s->subtype & ARRAY) {
629  Sprintf(buf,
630  " for (_j = 0; _j < %d; _j++) { %s[_j] = _xi;\n}",
631  s->araydim,
632  s->name);
633  } else {
634  Sprintf(buf, " %s = _xi;\n", s->name);
635  }
637  }
638  Lappendstr(procfunc, " return;\n }\n");
639  }
640  Lappendstr(procfunc, "if (_xi <= 0.) {\n");
641  if (type == FUNCTION1) {
642  Sprintf(buf, "return _t_%s[0];\n", SYM(table->next)->name);
644  } else {
645  ITERATE(q, table) {
646  s = SYM(q);
647  if (s->subtype & ARRAY) {
648  Sprintf(buf,
649  "for (_j = 0; _j < %d; _j++) { %s[_j] = _t_%s[_j][0];\n}",
650  s->araydim,
651  s->name,
652  s->name);
653  } else {
654  Sprintf(buf, "%s = _t_%s[0];\n", s->name, s->name);
655  }
657  }
658  Lappendstr(procfunc, "return;");
659  }
660  Lappendstr(procfunc, "}\n");
661  Sprintf(buf, "if (_xi >= %d.) {\n", ntab);
663  if (type == FUNCTION1) {
664  Sprintf(buf, "return _t_%s[%d];\n", SYM(table->next)->name, ntab);
666  } else {
667  ITERATE(q, table) {
668  s = SYM(q);
669  if (s->subtype & ARRAY) {
670  Sprintf(buf,
671  "for (_j = 0; _j < %d; _j++) { %s[_j] = _t_%s[_j][%d];\n}",
672  s->araydim,
673  s->name,
674  s->name,
675  ntab);
676  } else {
677  Sprintf(buf, "%s = _t_%s[%d];\n", s->name, s->name, ntab);
678  }
680  }
681  Lappendstr(procfunc, "return;");
682  }
683  Lappendstr(procfunc, "}\n");
684  /* table interpolation */
685  Lappendstr(procfunc, "_i = (int) _xi;\n");
686  if (type == FUNCTION1) {
687  s = SYM(table->next);
688  Sprintf(buf,
689  "return _t_%s[_i] + (_xi - (double)_i)*(_t_%s[_i+1] - _t_%s[_i]);\n",
690  s->name,
691  s->name,
692  s->name);
694  } else {
695  Lappendstr(procfunc, "_theta = _xi - (double)_i;\n");
696  ITERATE(q, table) {
697  s = SYM(q);
698  if (s->subtype & ARRAY) {
699  Sprintf(buf,
700  "for (_j = 0; _j < %d; _j++) {double *_t = _t_%s[_j];",
701  s->araydim,
702  s->name);
704  Sprintf(buf, "%s[_j] = _t[_i] + _theta*(_t[_i+1] - _t[_i]);}\n", s->name);
705  } else {
706  Sprintf(buf,
707  "%s = _t_%s[_i] + _theta*(_t_%s[_i+1] - _t_%s[_i]);\n",
708  s->name,
709  s->name,
710  s->name,
711  s->name);
712  }
714  }
715  }
716  Lappendstr(procfunc, "}\n\n"); /* end of new function */
717 
718  /* table declaration */
719  ITERATE(q, table) {
720  s = SYM(q);
721  if (s->subtype & ARRAY) {
722  Sprintf(buf, "static double *_t_%s[%d];\n", s->name, s->araydim);
723  } else {
724  Sprintf(buf, "static double *_t_%s;\n", s->name);
725  }
727  }
728 
729  /*cleanup*/
730  freelist(&table);
731  freelist(&depend);
732  freelist(&from);
733  freelist(&to);
734 }
735 
736 extern int point_process;
737 
738 // Original hocfunchack modified to handle _npy_name definitions.
739 static void funchack(Symbol* n, bool ishoc, int hack) {
740  Item* q;
741  int i;
742  Item* qp = 0;
743 
744  if (point_process) {
745  Sprintf(buf, "\nstatic double _hoc_%s(void* _vptr) {\n double _r;\n", n->name);
746  } else if (ishoc) {
747  Sprintf(buf, "\nstatic void _hoc_%s(void) {\n double _r;\n", n->name);
748  } else { // _npy_...
749  Sprintf(buf,
750  "\nstatic double _npy_%s(Prop* _prop) {\n"
751  " double _r{0.0};\n",
752  n->name);
753  }
756  "Datum* _ppvar; Datum* _thread; NrnThread* _nt;\n");
757  if (point_process) {
759  " auto* const _pnt = static_cast<Point_process*>(_vptr);\n"
760  " auto* const _p = _pnt->_prop;\n"
761  " if (!_p) {\n"
762  " hoc_execerror(\"POINT_PROCESS data instance not valid\", NULL);\n"
763  " }\n");
764  q = lappendstr(procfunc, " _setdata(_p);\n");
766  q,
767  " _nrn_mechanism_cache_instance _ml_real{_p};\n"
768  " auto* const _ml = &_ml_real;\n"
769  " size_t const _iml{};\n"
770  " _ppvar = _nrn_mechanism_access_dparam(_p);\n"
771  " _thread = _extcall_thread.data();\n"
772  " double* _globals = nullptr;\n"
773  " if (gind != 0 && _thread != nullptr) { _globals = _thread[_gth].get<double*>(); }\n"
774  " _nt = static_cast<NrnThread*>(_pnt->_vnt);\n");
775  } else if (ishoc) {
778  lappendstr(procfunc, ""),
779  "_nrn_mechanism_cache_instance _ml_real{_local_prop};\n"
780  "auto* const _ml = &_ml_real;\n"
781  "size_t const _iml{};\n"
782  "_ppvar = _local_prop ? _nrn_mechanism_access_dparam(_local_prop) : nullptr;\n"
783  "_thread = _extcall_thread.data();\n"
784  "double* _globals = nullptr;\n"
785  "if (gind != 0 && _thread != nullptr) { _globals = _thread[_gth].get<double*>(); }\n"
786  "_nt = nrn_threads;\n");
787  } else { // _npy_...
789  " neuron::legacy::set_globals_from_prop(_prop, _ml_real, _ml, _iml);\n"
790  " _ppvar = _nrn_mechanism_access_dparam(_prop);\n");
792  q,
793  "_nrn_mechanism_cache_instance _ml_real{_prop};\n"
794  "auto* const _ml = &_ml_real;\n"
795  "size_t const _iml{};\n"
796  "_ppvar = _nrn_mechanism_access_dparam(_prop);\n"
797  "_thread = _extcall_thread.data();\n"
798  "double* _globals = nullptr;\n"
799  "if (gind != 0 && _thread != nullptr) { _globals = _thread[_gth].get<double*>(); }\n"
800  "_nt = nrn_threads;\n");
801  }
802  if (n == last_func_using_table) {
803  qp = lappendstr(procfunc, "");
804  Sprintf(buf, "\n#if 1\n _check_%s(_threadargs_);\n#endif\n", n->name);
806  }
807  if (n->subtype & FUNCT) {
808  Lappendstr(procfunc, "_r = ");
809  } else {
810  Lappendstr(procfunc, "_r = 1.;\n");
811  }
813  lappendstr(procfunc, "(");
814  qp = lappendstr(procfunc, "");
815  for (i = 0; i < n->varnum; ++i) {
816  Sprintf(buf, "*getarg(%d)", i + 1);
818  if (i + 1 < n->varnum) {
819  Lappendstr(procfunc, ",");
820  }
821  }
822  if (point_process || !ishoc) {
823  Lappendstr(procfunc, ");\n return(_r);\n}\n");
824  } else if (ishoc) {
825  Lappendstr(procfunc, ");\n hoc_retpushx(_r);\n}\n");
826  }
827  if (i) {
828  vectorize_substitute(qp, "_threadargscomma_");
829  } else if (!hack) {
830  vectorize_substitute(qp, "_threadargs_");
831  }
832 }
833 
834 void hocfunchack(Symbol* n, Item* qpar1, Item* qpar2, int hack) {
835  funchack(n, true, hack);
836 }
837 
838 static void npyfunc(Symbol* n, int hack) { // supports seg.mech.n(...)
839  if (point_process) {
840  return;
841  } // direct calls from python from hocfunchack
842  funchack(n, false, hack); // direct calls from python via _npy_.... wrapper.
843 }
844 
845 void hocfunc(Symbol* n, Item* qpar1, Item* qpar2) /*interface between modl and hoc for proc and func
846  */
847 {
848  /* Hack prevents FUNCTION_TABLE bug of 'double table_name()' extra args
849  replacing the double in 'double name(...) */
850  hocfunchack(n, qpar1, qpar2, 0);
851  // wrapper for direct call from python
852  npyfunc(n, 0); // shares most of hocfunchack code (factored out).
853 }
854 
855 /* ARGSUSED */
856 void vectorize_use_func(Item* qname, Item* qpar1, Item* qexpr, Item* qpar2, int blocktype) {
857  Item* q;
858  if (SYM(qname)->subtype & (EXTDEF | EXTDEF_RANDOM)) {
859  if (strcmp(SYM(qname)->name, "nrn_pointing") == 0) {
860  // TODO: this relies on undefined behaviour in C++. &*foo is not
861  // guaranteed to be equivalent to foo if foo is null. See
862  // https://stackoverflow.com/questions/51691273/is-null-well-defined-in-c,
863  // https://en.cppreference.com/w/cpp/language/operator_member_access#Built-in_address-of_operator
864  // also confirms that the special case here in C does not apply to
865  // C++. All of that said, neither GCC nor Clang even produces a
866  // warning and it seems to work.
867  Insertstr(qpar1->next, "&");
868  } else if (strcmp(SYM(qname)->name, "state_discontinuity") == 0) {
869  if (blocktype == NETRECEIVE) {
870  Item* qeq = NULL;
871  /* convert to state = expr form and process with netrec_discon(...) */
872  replacstr(qname, "");
873  replacstr(qpar1, "");
874  replacstr(qpar2, "");
875  /* qexpr begins state, expr */
876  /* find the first , and replace by = */
877  for (q = qexpr; q != qpar2; q = q->next) {
878  if (q->itemtype == SYMBOL && strcmp(SYM(q)->name, ",") == 0) {
879  qeq = q;
880  replacstr(qeq, "=");
881  break;
882  }
883  }
884  assert(qeq);
885  netrec_asgn(qexpr, qeq, qeq->next, qpar2);
886  } else {
887  fprintf(stderr,
888  "Notice: Use of state_discontinuity is not thread safe except in a "
889  "NET_RECEIVE block");
890  vectorize = 0;
891  if (!state_discon_list_) {
893  Linsertstr(procfunc, "extern int state_discon_flag_;\n");
894  }
896  Insertstr(qpar1->next, "-1, &");
897  }
898  } else if (strcmp(SYM(qname)->name, "net_send") == 0) {
899  net_send_seen_ = 1;
900  if (artificial_cell) {
901  replacstr(qname, "artcell_net_send");
902  }
903  Insertstr(qexpr, "t + ");
904  if (blocktype == NETRECEIVE) {
905  Insertstr(qpar1->next, "_tqitem, _args, _pnt,");
906  } else if (blocktype == INITIAL1) {
907  Insertstr(qpar1->next, "_tqitem, nullptr, _ppvar[1].get<Point_process*>(),");
908  } else {
909  diag("net_send allowed only in INITIAL and NET_RECEIVE blocks", (char*) 0);
910  }
911  } else if (strcmp(SYM(qname)->name, "net_event") == 0) {
912  net_event_seen_ = 1;
913  if (blocktype == NETRECEIVE) {
914  Insertstr(qpar1->next, "_pnt,");
915  } else {
916  diag("net_event", "only allowed in NET_RECEIVE block");
917  }
918  } else if (strcmp(SYM(qname)->name, "net_move") == 0) {
919  if (artificial_cell) {
920  replacstr(qname, "artcell_net_move");
921  }
922  if (blocktype == NETRECEIVE) {
923  Insertstr(qpar1->next, "_tqitem, _pnt,");
924  } else {
925  diag("net_move", "only allowed in NET_RECEIVE block");
926  }
927  } else if (SYM(qname)->subtype & EXTDEF_RANDOM) {
928  replacstr(qname, extdef_rand[SYM(qname)->name]);
929  }
930  return;
931  }
932  if (qexpr) {
933  q = insertstr(qpar1->next, "_threadargscomma_");
934  } else {
935  q = insertstr(qpar1->next, "_threadargs_");
936  }
937 }
938 
939 
940 void function_table(Symbol* s, Item* qpar1, Item* qpar2, Item* qb1, Item* qb2) /* s ( ... ) { ... }
941  */
942 {
943  Symbol* t;
944  int i;
945  Item *q, *q1, *q2;
946  for (i = 0, q = qpar1->next; q != qpar2; q = q->next) {
947  if (q->itemtype == STRING || SYM(q)->name[0] != '_') {
948  continue;
949  }
950  Sprintf(buf, "_arg[%d] = %s;\n", i, SYM(q)->name);
951  insertstr(qb2, buf);
952  ++i;
953  }
954  if (i == 0) {
955  diag("FUNCTION_TABLE declaration must have one or more arguments:", s->name);
956  }
957  Sprintf(buf, "double _arg[%d];\n", i);
958  insertstr(qb1->next, buf);
959  Sprintf(buf, "return hoc_func_table(_ptable_%s, %d, _arg);\n", s->name, i);
960  insertstr(qb2, buf);
961  insertstr(qb2, "}\n/* "); /* kludge to avoid a bad vectorize_substitute */
962  insertstr(qb2->next, " */\n");
963 
964  Sprintf(buf, "table_%s", s->name);
965  t = install(buf, NAME);
966  t->subtype |= FUNCT;
967  t->usage |= FUNCT;
968  t->no_threadargs = 1;
969  t->varnum = 0;
970 
971  Sprintf(buf, "double %s", t->name);
973  q1 = lappendsym(procfunc, SYM(qpar1));
974  q2 = lappendsym(procfunc, SYM(qpar2));
975  Sprintf(buf, "{\n\thoc_spec_table(&_ptable_%s, %d);\n\treturn 0.;\n}\n", s->name, i);
977  Sprintf(buf, "\nstatic void* _ptable_%s = (void*)0;\n", s->name);
979  hocfunchack(t, q1, q2, 1);
980  npyfunc(t, 1);
981 }
982 
983 void watchstmt(Item* par1, Item* dir, Item* par2, Item* flag, int blocktype) {
984  if (!watch_seen_) {
985  ++watch_seen_;
986  }
987  if (blocktype != NETRECEIVE) {
988  diag("\"WATCH\" statement only allowed in NET_RECEIVE block", (char*) 0);
989  }
990  Sprintf(buf, "\nstatic double _watch%d_cond(Point_process* _pnt) {\n", watch_seen_);
993  " Datum* _ppvar; Datum* _thread{};\n"
994  " NrnThread* _nt{static_cast<NrnThread*>(_pnt->_vnt)};\n");
995  Sprintf(buf,
996  " auto* const _prop = _pnt->_prop;\n"
997  " _nrn_mechanism_cache_instance _ml_real{_prop};\n"
998  " auto* const _ml = &_ml_real;\n"
999  " size_t _iml{};\n"
1000  " _ppvar = _nrn_mechanism_access_dparam(_prop);\n"
1001  " v = NODEV(_pnt->node);\n"
1002  " return ");
1004  movelist(par1, par2, procfunc);
1005  movelist(dir->next, par2, procfunc);
1006  if (SYM(dir)->name[0] == '<') {
1007  insertstr(par1, "-(");
1008  insertstr(par2->next, ")");
1009  }
1010  replacstr(dir, ") - (");
1011  lappendstr(procfunc, ";\n}\n");
1012 
1013  // nrn_watch_allocate function called from core2nrn_watch_activate.
1014  if (!watch_alloc) {
1015  watch_alloc = newlist();
1017  "\nstatic void _watch_alloc(Datum* _ppvar) {\n"
1018  " auto* _pnt = _ppvar[1].get<Point_process*>();\n");
1019  }
1020  Sprintf(buf,
1021  " _nrn_watch_allocate(_watch_array, _watch%d_cond, %d, _pnt, %s);\n",
1022  watch_seen_,
1023  watch_seen_,
1024  STR(flag));
1026 
1027  Sprintf(buf,
1028  " _nrn_watch_activate(_watch_array, _watch%d_cond, %d, _pnt, _watch_rm++, %s);\n",
1029  watch_seen_,
1030  watch_seen_,
1031  STR(flag));
1032  replacstr(flag, buf);
1033 
1034  ++watch_seen_;
1035 }
1036 
1037 void threadsafe(const char* s) {
1038  if (!assert_threadsafe) {
1039  fprintf(stderr, "Notice: %s\n", s);
1040  vectorize = 0;
1041  }
1042 }
1043 
1044 
1045 Item* protect_astmt(Item* q1, Item* q2) { /* PROTECT, ';' */
1046  Item* q;
1047  replacstr(q1, "/* PROTECT */_NMODLMUTEXLOCK\n");
1048  q = insertstr(q2->next, "\n _NMODLMUTEXUNLOCK /* end PROTECT */\n");
1049  protect_include_ = 1;
1050  return q;
1051 }
1052 
1053 void nrnmutex(int on, Item* q) { /* MUTEXLOCK or MUTEXUNLOCK */
1054  static int toggle = 0;
1055  if (on == 1) {
1056  if (toggle != 0) {
1057  diag("MUTEXLOCK invoked after MUTEXLOCK", (char*) 0);
1058  }
1059  toggle = 1;
1060  replacstr(q, "_NMODLMUTEXLOCK\n");
1061  protect_include_ = 1;
1062  } else if (on == 0) {
1063  if (toggle != 1) {
1064  diag("MUTEXUNLOCK invoked with no earlier MUTEXLOCK", (char*) 0);
1065  }
1066  toggle = 0;
1067  replacstr(q, "_NMODLMUTEXUNLOCK\n");
1068  protect_include_ = 1;
1069  } else {
1070  if (toggle != 0) {
1071  diag("MUTEXUNLOCK not invoked after MUTEXLOCK", (char*) 0);
1072  }
1073  toggle = 0;
1074  }
1075 }
#define STRING
Definition: bbslsrv.cpp:9
#define i
Definition: md1redef.h:19
static double abstol(void *v)
Definition: cvodeobj.cpp:165
static HocParmLimits limits[]
Definition: extcelln.cpp:36
std::map< std::string, const char * > extdef_rand
Definition: init.cpp:158
#define PARM
Definition: modl.h:183
Symbol * ifnew_parminstall(const char *name, const char *num, const char *units, const char *limits)
Definition: parsact.cpp:71
void printlist(List *list)
Definition: model.cpp:141
char buf[512]
Definition: init.cpp:13
#define nmodlCONST
Definition: modl.h:203
#define EXTDEF_RANDOM
Definition: modl.h:208
static int c
Definition: hoc.cpp:169
#define assert(ex)
Definition: hocassrt.h:24
#define DEP
Definition: membfunc.hpp:64
FILE * fcout
Definition: model.cpp:36
#define STR(q)
Definition: model.h:76
#define FUNCT
Definition: model.h:108
#define SYM0
Definition: model.h:63
#define STAT
Definition: model.h:106
#define ITERATE(itm, lst)
Definition: model.h:18
#define SYMBOL
Definition: model.h:91
#define EXPLICIT_DECL
Definition: model.h:126
#define Linsertstr
Definition: model.h:220
#define INDEP
Definition: model.h:104
#define Lappendstr
Definition: model.h:222
#define ITEM0
Definition: model.h:15
#define Insertstr
Definition: model.h:217
#define SYM(q)
Definition: model.h:75
#define EXTDEF
Definition: model.h:119
#define LST(q)
Definition: model.h:79
#define Lappendsym
Definition: model.h:221
Symbol * lookup(const char *)
#define NRN_BUFSIZE
Definition: model.h:6
#define ARRAY
Definition: model.h:107
Symbol * install(const char *, int)
#define PROCED
Definition: model.h:109
printf
Definition: extdef.h:5
List * procfunc
Definition: init.cpp:9
List * firstlist
Definition: init.cpp:8
const char * name
Definition: init.cpp:16
List * initlist
Definition: init.cpp:8
long subtype
Definition: init.cpp:107
char * stralloc(const char *buf, char *rel)
Definition: list.cpp:178
void movelist(Item *q1, Item *q2, List *s)
Definition: list.cpp:214
void move(Item *q1, Item *q2, Item *q3)
Definition: list.cpp:200
Item * linsertstr(List *list, const char *str)
Definition: list.cpp:131
void replacstr(Item *q, const char *s)
Definition: list.cpp:219
Item * lappenditem(List *list, Item *item)
Definition: list.cpp:147
Item * insertstr(Item *item, const char *str)
Definition: list.cpp:99
Item * lappendsym(List *list, Symbol *sym)
Definition: list.cpp:143
Item * lappendstr(List *list, const char *str)
Definition: list.cpp:135
List * newlist()
The following routines support the concept of a list.
int Sprintf(char(&buf)[N], const char *fmt, Args &&... args)
Redirect sprintf to snprintf if the buffer size can be deduced.
Definition: wrap_sprintf.h:14
void netrec_asgn(Item *varname, Item *equal, Item *expr, Item *lastok)
void hocfunc_setdata_item(Symbol *, Item *)
Definition: nocpout.cpp:3438
void vectorize_substitute(Item *q, const char *str)
Definition: noccout.cpp:748
NMODL parser global flags / functions.
#define diag(s)
Definition: nonlin.cpp:19
int const size_t const size_t n
Definition: nrngsl.h:10
size_t q
size_t p
s
Definition: multisend.cpp:521
short index
Definition: cabvars.h:11
short type
Definition: cabvars.h:10
static double remove(void *v)
Definition: ocdeck.cpp:205
Symbol * stepsym
Definition: parsact.cpp:14
void depinstall(int type, Symbol *n, int index, const char *from, const char *to, const char *units, Item *qs, int makeconst, const char *abstol)
Definition: parsact.cpp:136
int net_event_seen_
Definition: nocpout.cpp:169
void add_reset_args(Item *q)
Definition: parsact.cpp:313
static Symbol * last_func_using_table
Definition: parsact.cpp:351
void add_nrnthread_arg(Item *q)
Definition: parsact.cpp:323
static List * check_table_statements
Definition: parsact.cpp:350
int watch_seen_
Definition: nocpout.cpp:170
static void npyfunc(Symbol *n, int hack)
Definition: parsact.cpp:838
int vectorize
Definition: nocpout.cpp:78
void parm_array_install(Symbol *n, const char *num, char *units, char *limits, int index)
Definition: parsact.cpp:46
static int func_arg_examine(Item *qpar, Item *qend)
Definition: parsact.cpp:202
List * state_discon_list_
Definition: nocpout.cpp:160
void defarg(Item *q1, Item *q2)
Definition: parsact.cpp:239
Item * protect_astmt(Item *q1, Item *q2)
Definition: parsact.cpp:1045
List * indeplist
Definition: parsact.cpp:15
void hocfunc(Symbol *n, Item *qpar1, Item *qpar2)
Definition: parsact.cpp:845
void threadsafe(const char *s)
Definition: parsact.cpp:1037
int check_tables_threads(List *p)
Definition: parsact.cpp:370
int protect_include_
Definition: parsact.cpp:26
void vectorize_scan_for_func(Item *q1, Item *q2)
Definition: parsact.cpp:219
void statdefault(Symbol *n, int index, const char *units, Item *qs, int makeconst)
Definition: parsact.cpp:175
int protect_
Definition: parsact.cpp:25
void hocfunchack(Symbol *n, Item *qpar1, Item *qpar2, int hack)
Definition: parsact.cpp:834
void parminstall(Symbol *n, const char *num, const char *units, const char *limits)
Definition: parsact.cpp:58
List * syminorder
Definition: init.cpp:10
Item * vectorize_replacement_item(Item *)
Definition: noccout.cpp:756
static const char * indepunits
Definition: parsact.cpp:94
static List * check_table_thread_list
Definition: parsact.cpp:369
void function_table(Symbol *s, Item *qpar1, Item *qpar2, Item *qb1, Item *qb2)
Definition: parsact.cpp:940
void explicit_decl(Item *q)
Definition: parsact.cpp:32
int assert_threadsafe
void table_massage(List *tablist, Item *qtype, Item *qname, List *arglist)
Definition: parsact.cpp:398
void indepinstall(Symbol *n, const char *from, const char *to, const char *with, const char *units)
Definition: parsact.cpp:96
int point_process
Definition: nocpout.cpp:138
static void funchack(Symbol *n, bool ishoc, int hack)
Definition: parsact.cpp:739
int artificial_cell
Definition: nocpout.cpp:139
void lag_stmt(Item *q1, int blocktype)
Definition: parsact.cpp:255
void vectorize_use_func(Item *qname, Item *qpar1, Item *qexpr, Item *qpar2, int blocktype)
Definition: parsact.cpp:856
Symbol * scop_indep
Definition: parsact.cpp:12
List * watch_alloc
Definition: parsact.cpp:16
void nrnmutex(int on, Item *q)
Definition: parsact.cpp:1053
int net_send_seen_
Definition: nocpout.cpp:168
Symbol * indepsym
Definition: parsact.cpp:13
void watchstmt(Item *par1, Item *dir, Item *par2, Item *flag, int blocktype)
Definition: parsact.cpp:983
void check_tables()
Definition: parsact.cpp:353
#define NULL
Definition: spdefs.h:105
void units(unit *)
Definition: units.cpp:641
static struct table * table
Definition: model.h:8
struct Item * prev
Definition: model.h:13
struct Item * next
Definition: model.h:12
Definition: model.h:47
int usage
Definition: model.h:56
short type
Definition: model.h:48
int araydim
Definition: model.h:57
long subtype
Definition: model.h:49
char * name
Definition: model.h:61
int varnum
Definition: model.h:59
Definition: units.cpp:71
static Item * qexpr[3]
Definition: units1.cpp:66