1 /************************************************************************
2  ************************************************************************
3     FAUST compiler
4     Copyright (C) 2003-2018 GRAME, Centre National de Creation Musicale
5     ---------------------------------------------------------------------
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  ************************************************************************
20  ************************************************************************/
21 #undef TRACE
22 
23 /**
24  * \file eval.cpp
25  * Implementation of the Block diagram evaluator.
26  *
27  * A strict lambda-calculus evaluator for block diagram expressions.
28  *
29  **/
30 
31 #include <stdio.h>
32 #include <cstdlib>
33 
34 #include "compatibility.hh"
35 #include "errormsg.hh"
36 #include "eval.hh"
37 #include "environment.hh"
38 #include "exception.hh"
39 #include "global.hh"
40 #include "names.hh"
41 #include "patternmatcher.hh"
42 #include "ppbox.hh"
43 #include "propagate.hh"
44 #include "property.hh"
45 #include "simplify.hh"
46 #include "xtended.hh"
47 
48 // History
49 // 23/05/2005 : New environment management
50 
51 //-------------- prototypes ---------------------------------------------------------
52 static Tree   a2sb(Tree exp);
53 static Tree   eval(Tree exp, Tree visited, Tree localValEnv);
54 static Tree   realeval(Tree exp, Tree visited, Tree localValEnv);
55 static Tree   revEvalList(Tree lexp, Tree visited, Tree localValEnv);
56 static Tree   applyList(Tree fun, Tree larg);
57 static Tree   iteratePar(Tree var, int num, Tree body, Tree visited, Tree localValEnv);
58 static Tree   iterateSeq(Tree id, int num, Tree body, Tree visited, Tree localValEnv);
59 static Tree   iterateSum(Tree id, int num, Tree body, Tree visited, Tree localValEnv);
60 static Tree   iterateProd(Tree id, int num, Tree body, Tree visited, Tree localValEnv);
61 static Tree   larg2par(Tree larg);
62 static int    eval2int(Tree exp, Tree visited, Tree localValEnv);
63 static double eval2double(Tree exp, Tree visited, Tree localValEnv);
64 static string evalLabel(const char* l, Tree visited, Tree localValEnv);
65 
66 static Tree evalIdDef(Tree id, Tree visited, Tree env);
67 
68 static Tree evalCase(Tree rules, Tree env);
69 static Tree evalRuleList(Tree rules, Tree env);
70 static Tree evalRule(Tree rule, Tree env);
71 static Tree evalPatternList(Tree patterns, Tree env);
72 static Tree evalPattern(Tree pattern, Tree env);
73 
74 static Tree patternSimplification(Tree pattern);
75 static bool isBoxNumeric(Tree in, Tree& out);
76 
77 static Tree vec2list(const vector<Tree>& v);
78 static void list2vec(Tree l, vector<Tree>& v);
79 static Tree listn(int n, Tree e);
80 
81 static Tree boxSimplification(Tree box);
82 
83 static void setNumericProperty(Tree t, Tree num);
84 static bool getNumericProperty(Tree t, Tree& num);
85 
86 // Public Interface
87 //----------------------
88 
89 /**
90  * Eval "process" from a list of definitions.
91  *
92  * Strict evaluation of a block diagram expression by applying beta reduction.
93  * @param eqlist a list of Faust definitions forming the global environment
94  * @return the process block diagram in normal form
95  */
evalprocess(Tree eqlist)96 Tree evalprocess(Tree eqlist)
97 {
98     Tree b = a2sb(eval(boxIdent(gGlobal->gProcessName.c_str()), gGlobal->nil,
99                        pushMultiClosureDefs(eqlist, gGlobal->nil, gGlobal->nil)));
100 
101     if (gGlobal->gSimplifyDiagrams) {
102         b = boxSimplification(b);
103     }
104 
105     return b;
106 }
107 
108 /* Eval a documentation expression. */
109 
evaldocexpr(Tree docexpr,Tree eqlist)110 Tree evaldocexpr(Tree docexpr, Tree eqlist)
111 {
112     return a2sb(eval(docexpr, gGlobal->nil, pushMultiClosureDefs(eqlist, gGlobal->nil, gGlobal->nil)));
113 }
114 
115 /**
116  * Simplify a block-diagram pattern by computing its numerical sub-expressions
117  * \param pattern an evaluated block-diagram
118  * \return a simplified pattern
119  *
120  */
121 /* uncomment for debugging output */
122 //#define DEBUG
simplifyPattern(Tree value)123 Tree simplifyPattern(Tree value)
124 {
125     Tree num;
126     if (!getNumericProperty(value, num)) {
127         if (!isBoxNumeric(value, num)) {
128             num = value;
129         }
130         setNumericProperty(value, num);
131     }
132     return num;
133 }
134 
135 // Private Implementation
136 //------------------------
137 
138 /**
139  * Transform unused (unapplied) closures into symbolic boxes
140  *
141  * @param exp the expression to transform
142  * @return an expression where abstractions have been replaced by symbolic boxes
143  */
144 
145 static Tree real_a2sb(Tree exp);
146 
a2sb(Tree exp)147 static Tree a2sb(Tree exp)
148 {
149     Tree result;
150     Tree id;
151 
152     if (gGlobal->gSymbolicBoxProperty->get(exp, result)) {
153         return result;
154     }
155 
156     result = real_a2sb(exp);
157     if (result != exp && getDefNameProperty(exp, id)) {
158         setDefNameProperty(result, id);  // propagate definition name property when needed
159     }
160     gGlobal->gSymbolicBoxProperty->set(exp, result);
161     return result;
162 }
163 
real_a2sb(Tree exp)164 static Tree real_a2sb(Tree exp)
165 {
166     Tree abstr, visited, unusedEnv, localValEnv, var, name, body;
167 
168     if (isClosure(exp, abstr, unusedEnv, visited, localValEnv)) {
169         if (isBoxIdent(abstr)) {
170             // special case introduced with access and components
171             Tree result = a2sb(eval(abstr, visited, localValEnv));
172 
173             // propagate definition name property when needed
174             if (getDefNameProperty(exp, name)) setDefNameProperty(result, name);
175             return result;
176 
177         } else if (isBoxAbstr(abstr, var, body)) {
178             // Here we have remaining abstraction that we will try to
179             // transform in a symbolic box by applying it to a slot
180 
181             Tree         slot = boxSlot(++gGlobal->gBoxSlotNumber);
182             stringstream s;
183             s << boxpp(var);
184             setDefNameProperty(slot, s.str());  // ajout YO
185 
186             // Apply the abstraction to the slot
187             Tree result = boxSymbolic(slot, a2sb(eval(body, visited, pushValueDef(var, slot, localValEnv))));
188 
189             // propagate definition name property when needed
190             if (getDefNameProperty(exp, name)) setDefNameProperty(result, name);
191             return result;
192 
193         } else if (isBoxEnvironment(abstr)) {
194             return abstr;
195 
196         } else {
197             evalerror(yyfilename, -1, "a2sb : internal error : not an abstraction inside closure (1)", exp);
198             // Never reached since evalerror throws an exception
199             return 0;
200         }
201 
202     } else if (isBoxPatternMatcher(exp)) {
203         // Here we have remaining PM rules that we will try to
204         // transform in a symbolic box by applying it to a slot
205 
206         Tree         slot = boxSlot(++gGlobal->gBoxSlotNumber);
207         stringstream s;
208         s << "PM" << gGlobal->gBoxSlotNumber;
209         setDefNameProperty(slot, s.str());
210 
211         // apply the PM rules to the slot and transfoms the result in a symbolic box
212         Tree result = boxSymbolic(slot, a2sb(applyList(exp, cons(slot, gGlobal->nil))));
213 
214         // propagate definition name property when needed
215         if (getDefNameProperty(exp, name)) setDefNameProperty(result, name);
216         return result;
217 
218     } else if (isBoxWaveform(exp)) {
219         // A waveform is always in Normal Form, nothing to evaluate
220         return exp;
221 
222     } else {
223         // it is a constructor : transform each branches
224         unsigned int ar = exp->arity();
225         tvec         B(ar);
226         bool         modified = false;
227         for (unsigned int i = 0; i < ar; i++) {
228             Tree b = exp->branch(i);
229             Tree m = a2sb(b);
230             B[i]   = m;
231             if (b != m) modified = true;
232         }
233         Tree r = (modified) ? CTree::make(exp->node(), B) : exp;
234         return r;
235     }
236 }
237 
autoName(Tree exp,Tree & id)238 static bool autoName(Tree exp, Tree& id)
239 {
240     stringstream s;
241     s << boxpp(exp);
242     string res = s.str();
243     id         = tree(res.c_str());
244     return true;
245 }
246 
getArgName(Tree t,Tree & id)247 static bool getArgName(Tree t, Tree& id)
248 {
249     // return getDefNameProperty(t, id) || autoName(t, id) ;
250     return autoName(t, id);
251 }
252 
253 /**
254  * Set the value of box in the environment env
255  * @param box the block diagram we have evaluated
256  * @param env the evaluation environment
257  * @param value the evaluated block diagram
258  */
setEvalProperty(Tree box,Tree env,Tree value)259 static void setEvalProperty(Tree box, Tree env, Tree value)
260 {
261     setProperty(box, tree(gGlobal->EVALPROPERTY, env), value);
262 }
263 
264 /**
265  * Retrieve the value of box in the environment env
266  * @param box the expression we want to retrieve the value
267  * @param env the lexical environment
268  * @param value the returned value if any
269  * @return true if a value already exist
270  */
getEvalProperty(Tree box,Tree env,Tree & value)271 static bool getEvalProperty(Tree box, Tree env, Tree& value)
272 {
273     return getProperty(box, tree(gGlobal->EVALPROPERTY, env), value);
274 }
275 
276 /**
277  * Eval a block diagram expression.
278  *
279  * Wrap the realeval function in order to propagate the name property
280  * @param exp the expression to evaluate
281  * @param visited list of visited definitions to detect recursive definitions
282  * @param localValEnv the local environment
283  * @return a block diagram in normal form
284  */
eval(Tree exp,Tree visited,Tree localValEnv)285 static Tree eval(Tree exp, Tree visited, Tree localValEnv)
286 {
287     Tree id;
288     Tree result;
289 
290     if (!getEvalProperty(exp, localValEnv, result)) {
291         gGlobal->gLoopDetector.detect(cons(exp, localValEnv));
292         gGlobal->gStackOverflowDetector.detect();
293         // cerr << "ENTER eval("<< *exp << ") with env " << *localValEnv << endl;
294         result = realeval(exp, visited, localValEnv);
295         setEvalProperty(exp, localValEnv, result);
296         // cerr << "EXIT eval(" << *exp << ") IS " << *result << " with env " << *localValEnv << endl;
297         if (getDefNameProperty(exp, id)) {
298             setDefNameProperty(result, id);  // propagate definition name property
299         }
300     }
301     return result;
302 }
303 
304 /**
305  * Check numerical tuple.
306  *
307  * Check if a box is a parallel construction of numbers
308  * @param box the expression to analyse
309  * @param L the resulting flat list of numbers
310  * @return true if box is a parallel construction of numbers
311  */
isNumericalTuple(Tree box,siglist & L)312 static bool isNumericalTuple(Tree box, siglist& L)
313 {
314     Tree l, r;
315 
316     if (isBoxInt(box) || isBoxReal(box)) {
317         L.push_back(box);
318         return true;
319     } else if (isBoxPar(box, l, r) && isNumericalTuple(l, L)) {
320         return isNumericalTuple(r, L);
321     } else {
322         return false;
323     }
324 }
325 
326 /**
327  * Eval a block diagram expression.
328  *
329  * Strict evaluation of a block diagram expression by applying beta reduction.
330  * @param exp the expression to evaluate
331  * @param visited list of visited definition to detect recursive definitions
332  * @param localValEnv the local environment
333  * @return a block diagram in normal form
334  */
335 
realeval(Tree exp,Tree visited,Tree localValEnv)336 static Tree realeval(Tree exp, Tree visited, Tree localValEnv)
337 {
338     Tree fun;
339     Tree arg;
340     Tree var, num, chan, body, ldef, ins, outs, routes;
341     Tree label;
342     Tree cur, lo, hi, step;
343     Tree e1, e2, exp2, notused, visited2, lenv2;
344     Tree rules;
345     Tree id;
346 
347     // cerr << "EVAL " << *exp << " (visited : " << *visited << ")" << endl;
348     // cerr << "REALEVAL of " << *exp << endl;
349 
350     xtended* xt = (xtended*)getUserData(exp);
351 
352     // constants
353     //-----------
354 
355     if (xt || isBoxInt(exp) || isBoxReal(exp) || isBoxWire(exp) || isBoxCut(exp) || isBoxPrim0(exp) ||
356         isBoxPrim1(exp) || isBoxPrim2(exp) || isBoxPrim3(exp) || isBoxPrim4(exp) || isBoxPrim5(exp) || isBoxFFun(exp) ||
357         isBoxFConst(exp) || isBoxFVar(exp) || isBoxWaveform(exp)) {
358         return exp;
359 
360         // block-diagram constructors
361         //---------------------------
362 
363     } else if (isBoxSeq(exp, e1, e2)) {
364         Tree a1 = eval(e1, visited, localValEnv);
365         Tree a2 = eval(e2, visited, localValEnv);
366         Tree re = boxSeq(a1, a2);
367 
368         xtended* xxt = (xtended*)getUserData(a2);
369         siglist  lsig;
370         // try a numerical simplification of expressions of type 2,3:+
371         if (isNumericalTuple(a1, lsig) && (xxt || isBoxWire(a2) || isBoxPrim1(a2) || isBoxPrim2(a2))) {
372             // check that re is well typed before trying to simplify it
373             int n, m;
374             getBoxType(re, &n, &m);
375 
376             Tree lres = boxPropagateSig(gGlobal->nil, a2, lsig);
377             if (isList(lres) && isNil(tl(lres))) {
378                 // cerr << "simplify 355" << endl;
379                 Tree r = simplify(hd(lres));
380                 if (isNum(r)) {
381                     return r;
382                 }
383             }
384         }
385         // no numerical simplification
386         return re;
387 
388     } else if (isBoxPar(exp, e1, e2)) {
389         return boxPar(eval(e1, visited, localValEnv), eval(e2, visited, localValEnv));
390 
391     } else if (isBoxRec(exp, e1, e2)) {
392         return boxRec(eval(e1, visited, localValEnv), eval(e2, visited, localValEnv));
393 
394     } else if (isBoxSplit(exp, e1, e2)) {
395         return boxSplit(eval(e1, visited, localValEnv), eval(e2, visited, localValEnv));
396 
397     } else if (isBoxMerge(exp, e1, e2)) {
398         return boxMerge(eval(e1, visited, localValEnv), eval(e2, visited, localValEnv));
399 
400         // Modules
401         //--------
402 
403     } else if (isBoxAccess(exp, body, var)) {
404         Tree val = eval(body, visited, localValEnv);
405         if (isClosure(val, exp2, notused, visited2, lenv2)) {
406             // it is a closure, we have an environment to access
407             return eval(closure(var, notused, visited2, lenv2), visited, localValEnv);
408         } else {
409             evalerror(getDefFileProp(exp), getDefLineProp(exp), "no environment to access", exp);
410         }
411 
412         //////////////////////en chantier////////////////////////////
413 
414     } else if (isBoxModifLocalDef(exp, body, ldef)) {
415         Tree val = eval(body, visited, localValEnv);
416         if (isClosure(val, exp2, notused, visited2, lenv2)) {
417             // we rebuild the closure using a copy of the original environment
418             // modified with some new definitions
419             Tree lenv3 = copyEnvReplaceDefs(lenv2, ldef, visited2, localValEnv);
420             return eval(closure(exp2, notused, visited2, lenv3), visited, localValEnv);
421         } else {
422             evalerror(getDefFileProp(exp), getDefLineProp(exp), "not a closure", val);
423             evalerror(getDefFileProp(exp), getDefLineProp(exp), "no environment to access", exp);
424         }
425 
426         ///////////////////////////////////////////////////////////////////
427 
428     } else if (isBoxComponent(exp, label)) {
429         const char* fname = tree2str(label);
430         Tree        eqlst = gGlobal->gReader.expandList(gGlobal->gReader.getList(fname));
431         Tree        res   = closure(boxIdent("process"), gGlobal->nil, gGlobal->nil,
432                            pushMultiClosureDefs(eqlst, gGlobal->nil, gGlobal->nil));
433         setDefNameProperty(res, label);
434         // cerr << "component is " << boxpp(res) << endl;
435         return res;
436 
437     } else if (isBoxLibrary(exp, label)) {
438         const char* fname = tree2str(label);
439         Tree        eqlst = gGlobal->gReader.expandList(gGlobal->gReader.getList(fname));
440         Tree        res   = closure(boxEnvironment(), gGlobal->nil, gGlobal->nil,
441                            pushMultiClosureDefs(eqlst, gGlobal->nil, gGlobal->nil));
442         setDefNameProperty(res, label);
443         // cerr << "component is " << boxpp(res) << endl;
444         return res;
445 
446         // user interface elements
447         //------------------------
448 
449     } else if (isBoxButton(exp, label)) {
450         const char* l1 = tree2str(label);
451         string      l2 = evalLabel(l1, visited, localValEnv);
452         // cout << "button label : " << l1 << " become " << l2 << endl;
453         return boxButton(tree(l2.c_str()));
454 
455     } else if (isBoxCheckbox(exp, label)) {
456         const char* l1 = tree2str(label);
457         string      l2 = evalLabel(l1, visited, localValEnv);
458         // cout << "check box label : " << l1 << " become " << l2 << endl;
459         return boxCheckbox(tree(l2.c_str()));
460 
461     } else if (isBoxVSlider(exp, label, cur, lo, hi, step)) {
462         const char* l1 = tree2str(label);
463         string      l2 = evalLabel(l1, visited, localValEnv);
464         return (boxVSlider(tree(l2.c_str()), tree(eval2double(cur, visited, localValEnv)),
465                            tree(eval2double(lo, visited, localValEnv)), tree(eval2double(hi, visited, localValEnv)),
466                            tree(eval2double(step, visited, localValEnv))));
467 
468     } else if (isBoxHSlider(exp, label, cur, lo, hi, step)) {
469         const char* l1 = tree2str(label);
470         string      l2 = evalLabel(l1, visited, localValEnv);
471         return (boxHSlider(tree(l2.c_str()), tree(eval2double(cur, visited, localValEnv)),
472                            tree(eval2double(lo, visited, localValEnv)), tree(eval2double(hi, visited, localValEnv)),
473                            tree(eval2double(step, visited, localValEnv))));
474 
475     } else if (isBoxNumEntry(exp, label, cur, lo, hi, step)) {
476         const char* l1 = tree2str(label);
477         string      l2 = evalLabel(l1, visited, localValEnv);
478         return (boxNumEntry(tree(l2.c_str()), tree(eval2double(cur, visited, localValEnv)),
479                             tree(eval2double(lo, visited, localValEnv)), tree(eval2double(hi, visited, localValEnv)),
480                             tree(eval2double(step, visited, localValEnv))));
481 
482     } else if (isBoxSoundfile(exp, label, chan)) {
483         const char* l1 = tree2str(label);
484         string      l2 = evalLabel(l1, visited, localValEnv);
485         return boxSoundfile(tree(l2.c_str()), tree(eval2int(chan, visited, localValEnv)));
486 
487     } else if (isBoxVGroup(exp, label, arg)) {
488         const char* l1 = tree2str(label);
489         string      l2 = evalLabel(l1, visited, localValEnv);
490         return boxVGroup(tree(l2.c_str()), eval(arg, visited, localValEnv));
491 
492     } else if (isBoxHGroup(exp, label, arg)) {
493         const char* l1 = tree2str(label);
494         string      l2 = evalLabel(l1, visited, localValEnv);
495         return boxHGroup(tree(l2.c_str()), eval(arg, visited, localValEnv));
496 
497     } else if (isBoxTGroup(exp, label, arg)) {
498         const char* l1 = tree2str(label);
499         string      l2 = evalLabel(l1, visited, localValEnv);
500         return boxTGroup(tree(l2.c_str()), eval(arg, visited, localValEnv));
501 
502     } else if (isBoxHBargraph(exp, label, lo, hi)) {
503         const char* l1 = tree2str(label);
504         string      l2 = evalLabel(l1, visited, localValEnv);
505         return boxHBargraph(tree(l2.c_str()), tree(eval2double(lo, visited, localValEnv)),
506                             tree(eval2double(hi, visited, localValEnv)));
507 
508     } else if (isBoxMetadata(exp, e1, e2)) {
509         gGlobal->gMetaDataSet[hd(e2)].insert(tl(e2));
510         return eval(e1, visited, localValEnv);
511 
512     } else if (isBoxVBargraph(exp, label, lo, hi)) {
513         const char* l1 = tree2str(label);
514         string      l2 = evalLabel(l1, visited, localValEnv);
515         return boxVBargraph(tree(l2.c_str()), tree(eval2double(lo, visited, localValEnv)),
516                             tree(eval2double(hi, visited, localValEnv)));
517 
518         // lambda calculus
519         //----------------
520 
521     } else if (isBoxIdent(exp)) {
522         return evalIdDef(exp, visited, localValEnv);
523 
524     } else if (isBoxWithLocalDef(exp, body, ldef)) {
525         Tree expandedldef = gGlobal->gReader.expandList(ldef);
526         return eval(body, visited, pushMultiClosureDefs(expandedldef, visited, localValEnv));
527 
528     } else if (isBoxAppl(exp, fun, arg)) {
529         return applyList(eval(fun, visited, localValEnv), revEvalList(arg, visited, localValEnv));
530 
531     } else if (isBoxAbstr(exp)) {
532         // it is an abstraction : return a closure
533         return closure(exp, gGlobal->nil, visited, localValEnv);
534 
535     } else if (isBoxEnvironment(exp)) {
536         // environment : return also a closure
537         return closure(exp, gGlobal->nil, visited, localValEnv);
538 
539     } else if (isClosure(exp, exp2, notused, visited2, lenv2)) {
540         if (isBoxAbstr(exp2)) {
541             // a 'real' closure
542             return closure(exp2, gGlobal->nil, setUnion(visited, visited2), lenv2);
543         } else if (isBoxEnvironment(exp2)) {
544             // a 'real' closure
545             return closure(exp2, gGlobal->nil, setUnion(visited, visited2), lenv2);
546         } else {
547             // it was a suspended evaluation
548             return eval(exp2, setUnion(visited, visited2), lenv2);
549         }
550 
551         // Algorithmic constructions
552         //--------------------------
553 
554     } else if (isBoxIPar(exp, var, num, body)) {
555         int n = eval2int(num, visited, localValEnv);
556         return iteratePar(var, n, body, visited, localValEnv);
557 
558     } else if (isBoxISeq(exp, var, num, body)) {
559         int n = eval2int(num, visited, localValEnv);
560         return iterateSeq(var, n, body, visited, localValEnv);
561 
562     } else if (isBoxISum(exp, var, num, body)) {
563         int n = eval2int(num, visited, localValEnv);
564         return iterateSum(var, n, body, visited, localValEnv);
565 
566     } else if (isBoxIProd(exp, var, num, body)) {
567         int n = eval2int(num, visited, localValEnv);
568         return iterateProd(var, n, body, visited, localValEnv);
569 
570         // static
571     } else if (isBoxInputs(exp, body)) {
572         int  ins1, outs1;
573         Tree b = a2sb(eval(body, visited, localValEnv));
574         if (getBoxType(b, &ins1, &outs1)) {
575             return boxInt(ins1);
576         } else {
577             stringstream error;
578             error << "ERROR : can't evaluate ' : " << *exp << endl;
579             throw faustexception(error.str());
580         }
581 
582     } else if (isBoxOutputs(exp, body)) {
583         int  ins1, outs1;
584         Tree b = a2sb(eval(body, visited, localValEnv));
585         if (getBoxType(b, &ins1, &outs1)) {
586             return boxInt(outs1);
587         } else {
588             stringstream error;
589             error << "ERROR : can't evaluate ' : " << *exp << endl;
590             throw faustexception(error.str());
591         }
592 
593     } else if (isBoxSlot(exp)) {
594         return exp;
595 
596     } else if (isBoxSymbolic(exp)) {
597         return exp;
598 
599         // Pattern matching extension
600         //---------------------------
601 
602     } else if (isBoxCase(exp, rules)) {
603         return evalCase(rules, localValEnv);
604 
605     } else if (isBoxPatternVar(exp, id)) {
606         return exp;
607         // return evalIdDef(id, visited, localValEnv);
608 
609     } else if (isBoxPatternMatcher(exp)) {
610         return exp;
611 
612     } else if (isBoxRoute(exp, ins, outs, routes)) {
613         // evaluate the route description
614         Tree v1 = a2sb(eval(ins, visited, localValEnv));
615         Tree v2 = a2sb(eval(outs, visited, localValEnv));
616         Tree vr = a2sb(eval(routes, visited, localValEnv));
617 
618         // check that we have constant numerical descriptions
619         int i1, o1, i2, o2, i3, o3;
620 
621         getBoxType(v1, &i1, &o1);
622         getBoxType(v2, &i2, &o2);
623         getBoxType(vr, &i3, &o3);
624 
625         if ((i1 == 0) & (o1 == 1) & (i2 == 0) & (o2 == 1) & (i3 == 0) & (o3 > 1) & ((o3 % 2) == 0)) {
626             // we are in good shape
627             Tree ls1 = boxPropagateSig(gGlobal->nil, v1, makeSigInputList(0));
628             Tree ls2 = boxPropagateSig(gGlobal->nil, v2, makeSigInputList(0));
629             Tree lsr = boxPropagateSig(gGlobal->nil, vr, makeSigInputList(0));
630 
631             // all these lists should be list of constant numerical signals
632             // that we need to convert back to box expressions
633             vector<int> w1, w2, wr;
634             if (sigList2vecInt(ls1, w1) && sigList2vecInt(ls2, w2) && sigList2vecInt(lsr, wr)) {
635                 // convert wr into a parallel boxes of ints b
636                 Tree b = boxInt(wr[o3 - 1]);
637                 for (int j = o3 - 2; j >= 0; j--) b = boxPar(boxInt(wr[j]), b);
638 
639                 return boxRoute(boxInt(w1[0]), boxInt(w2[0]), b);
640             } else {
641                 stringstream error;
642                 error << "ERROR : eval not a valid route expression (1) : " << boxpp(exp) << endl;
643                 throw faustexception(error.str());
644             }
645         } else {
646             stringstream error;
647             error << "ERROR : eval not a valid route expression (2) : " << boxpp(exp) << endl;
648             throw faustexception(error.str());
649         }
650 
651     } else {
652         stringstream error;
653         error << "ERROR : eval doesn't intercept : " << *exp << endl;
654         throw faustexception(error.str());
655     }
656 
657     return nullptr;
658 }
659 
660 /* Deconstruct a (BDA) op pattern (YO). */
661 
isBoxPatternOp(Tree box,Node & n,Tree & t1,Tree & t2)662 static inline bool isBoxPatternOp(Tree box, Node& n, Tree& t1, Tree& t2)
663 {
664     if (isBoxPar(box, t1, t2) || isBoxSeq(box, t1, t2) || isBoxSplit(box, t1, t2) || isBoxMerge(box, t1, t2) ||
665         isBoxRec(box, t1, t2)) {
666         n = box->node();
667         return true;
668     } else {
669         return false;
670     }
671 }
672 
setNumericProperty(Tree t,Tree num)673 static void setNumericProperty(Tree t, Tree num)
674 {
675     setProperty(t, gGlobal->NUMERICPROPERTY, num);
676 }
677 
getNumericProperty(Tree t,Tree & num)678 static bool getNumericProperty(Tree t, Tree& num)
679 {
680     return getProperty(t, gGlobal->NUMERICPROPERTY, num);
681 }
682 
isBoxNumeric(Tree in,Tree & out)683 static bool isBoxNumeric(Tree in, Tree& out)
684 {
685     int    numInputs, numOutputs;
686     double x;
687     int    i;
688     Tree   v, abstr, genv, vis, lenv, var, body;
689 
690     if (isBoxInt(in, &i) || isBoxReal(in, &x)) {
691         out = in;
692         return true;
693     } else if (isClosure(in, abstr, genv, vis, lenv) && isBoxAbstr(abstr, var, body)) {
694         return false;
695     } else {
696         v = a2sb(in);
697         if (getBoxType(v, &numInputs, &numOutputs) && (numInputs == 0) && (numOutputs == 1)) {
698             // potential numerical expression
699             Tree lsignals = boxPropagateSig(gGlobal->nil, v, makeSigInputList(numInputs));
700             // cerr << "simplify 658" << endl;
701             Tree res = simplify(hd(lsignals));
702             if (isSigReal(res, &x)) {
703                 out = boxReal(x);
704                 return true;
705             }
706             if (isSigInt(res, &i)) {
707                 out = boxInt(i);
708                 return true;
709             }
710         }
711         return false;
712     }
713 }
714 
patternSimplification(Tree pattern)715 static Tree patternSimplification(Tree pattern)
716 {
717     Node n(0);
718     Tree v, t1, t2;
719 
720     if (isBoxNumeric(pattern, v)) {
721         return v;
722     } else if (isBoxPatternOp(pattern, n, t1, t2)) {
723         return tree(n, patternSimplification(t1), patternSimplification(t2));
724     } else {
725         return pattern;
726     }
727 }
728 
729 /**
730  * Eval a block diagram to a double.
731  *
732  * Eval a block diagram that represent a double constant. This function first eval
733  * a block diagram to its normal form, then check it represent a numerical value (a
734  * block diagram of type : 0->1) then do a symbolic propagation and try to convert the
735  * resulting signal to a double.
736  * @param exp the expression to evaluate
737  * @param globalDefEnv the global environment
738  * @param visited list of visited definition to detect recursive definitions
739  * @param localValEnv the local environment
740  * @return a block diagram in normal form
741  */
eval2double(Tree exp,Tree visited,Tree localValEnv)742 static double eval2double(Tree exp, Tree visited, Tree localValEnv)
743 {
744     Tree diagram = a2sb(eval(exp, visited, localValEnv));  // For getBoxType
745     int  numInputs, numOutputs;
746     getBoxType(diagram, &numInputs, &numOutputs);
747     if ((numInputs > 0) || (numOutputs != 1)) {
748         evalerror(yyfilename, yylineno, "not a constant expression of type : (0->1)", exp);
749         // Never reached since evalerror throws an exception
750         return 1;
751     } else {
752         Tree lsignals = boxPropagateSig(gGlobal->nil, diagram, makeSigInputList(numInputs));
753         // cerr << "simplify 710" << endl;
754         Tree val = simplify(hd(lsignals));
755         return tree2float(val);
756     }
757 }
758 
759 /**
760  * Eval a block diagram to an int.
761  *
762  * Eval a block diagram that represent an integer constant. This function first eval
763  * a block diagram to its normal form, then check it represent a numerical value (a
764  * block diagram of type : 0->1) then do a symbolic propagation and try to convert the
765  * resulting signal to an int.
766  * @param exp the expression to evaluate
767  * @param globalDefEnv the global environment
768  * @param visited list of visited definition to detect recursive definitions
769  * @param localValEnv the local environment
770  * @return a block diagram in normal form
771  */
eval2int(Tree exp,Tree visited,Tree localValEnv)772 static int eval2int(Tree exp, Tree visited, Tree localValEnv)
773 {
774     Tree diagram = a2sb(eval(exp, visited, localValEnv));  // pour getBoxType()
775     int  numInputs, numOutputs;
776     getBoxType(diagram, &numInputs, &numOutputs);
777     if ((numInputs > 0) || (numOutputs != 1)) {
778         evalerror(yyfilename, yylineno, "not a constant expression of type : (0->1)", exp);
779         // Never reached since evalerror throws an exception
780         return 1;
781     } else {
782         Tree lsignals = boxPropagateSig(gGlobal->nil, diagram, makeSigInputList(numInputs));
783         // cerr << "simplify 739" << endl;
784         Tree val = simplify(hd(lsignals));
785         return tree2int(val);
786     }
787 }
788 
isDigitChar(char c)789 static bool isDigitChar(char c)
790 {
791     return (c >= '0') & (c <= '9');
792 }
793 
isIdentChar(char c)794 static bool isIdentChar(char c)
795 {
796     return ((c >= 'a') & (c <= 'z')) || ((c >= 'A') & (c <= 'Z')) || ((c >= '0') & (c <= '9')) || (c == '_');
797 }
798 
799 static const char* Formats[] = {"%d", "%1d", "%2d", "%3d", "%4d"};
800 
writeIdentValue(string & dst,const string & format,const string & ident,Tree visited,Tree localValEnv)801 static void writeIdentValue(string& dst, const string& format, const string& ident, Tree visited, Tree localValEnv)
802 {
803     int  f = std::atoi(format.c_str());
804     int  n = eval2int(boxIdent(ident.c_str()), visited, localValEnv);
805     int  i = min(4, max(f, 0));
806     char val[256];
807 
808     snprintf(val, 250, Formats[i], n);
809     dst += val;
810 }
811 
812 /**
813  * evallabel replace "...%2i..." occurences in label with value of i
814  */
evalLabel(const char * src,Tree visited,Tree localValEnv)815 static string evalLabel(const char* src, Tree visited, Tree localValEnv)
816 {
817     // std::cerr << "Eval Label : " << src;
818     int    state = 0;  // current state
819     string dst;        // label once evaluated
820     string ident;      // current identifier
821     string format;     // current format
822 
823     while (state != -1) {
824         if (state == 0) {
825             if (*src == 0) {
826                 state = -1;
827             } else if (*src == '%') {
828                 ident  = "";
829                 format = "";
830                 state  = 1;
831                 src++;
832             } else {
833                 dst += *src++;
834                 state = 0;
835             }
836 
837         } else if (state == 1) {
838             if (*src == 0) {
839                 // end and no identifier, stops
840                 dst += '%';
841                 dst += format;
842                 state = -1;
843             } else if (isDigitChar(*src)) {
844                 format += *src++;
845                 state = 1;
846             } else if (isIdentChar(*src)) {
847                 ident += *src++;
848                 state = 2;
849             } else {
850                 // punctuation character and no identifier, stops
851                 dst += '%';
852                 dst += format;
853                 state = 0;
854             }
855 
856         } else if (state == 2) {
857             if (isIdentChar(*src)) {
858                 ident += *src++;
859                 state = 2;
860             } else {
861                 writeIdentValue(dst, format, ident, visited, localValEnv);
862                 state = 0;
863             }
864 
865         } else {
866             stringstream error;
867             error << "ERROR in evallabel : undefined state " << state << std::endl;
868             throw faustexception(error.str());
869         }
870     }
871 
872     return dst;
873 }
874 
875 /**
876  * Iterate a parallel construction
877  *
878  * Iterate a parallel construction such that :
879  * par(i,10,E) --> E(i<-0),(E(i<-1),...,E(i<-9))
880  * @param id the formal parameter of the iteration
881  * @param num the number of iterations
882  * @param body the body expression of the iteration
883  * @param globalDefEnv the global environment
884  * @param visited list of visited definition to detect recursive definitions
885  * @param localValEnv the local environment
886  * @return a block diagram in normal form
887  */
iteratePar(Tree id,int num,Tree body,Tree visited,Tree localValEnv)888 static Tree iteratePar(Tree id, int num, Tree body, Tree visited, Tree localValEnv)
889 {
890     if (num == 0) {
891         evalerror(yyfilename, -1, "iteratePar called with 0 iteration", id);
892     }
893 
894     Tree res = eval(body, visited, pushValueDef(id, tree(num - 1), localValEnv));
895     for (int i = num - 2; i >= 0; i--) {
896         res = boxPar(eval(body, visited, pushValueDef(id, tree(i), localValEnv)), res);
897     }
898 
899     return res;
900 }
901 
902 /**
903  * Iterate a sequential construction
904  *
905  * Iterate a sequential construction such that :
906  * seq(i,10,E) --> E(i<-0):(E(i<-1):...:E(i<-9))
907  * @param id the formal parameter of the iteration
908  * @param num the number of iterations
909  * @param body the body expression of the iteration
910  * @param globalDefEnv the global environment
911  * @param visited list of visited definition to detect recursive definitions
912  * @return a block diagram in normal form
913  */
iterateSeq(Tree id,int num,Tree body,Tree visited,Tree localValEnv)914 static Tree iterateSeq(Tree id, int num, Tree body, Tree visited, Tree localValEnv)
915 {
916     if (num == 0) {
917         evalerror(yyfilename, -1, "iterateSeq called with 0 iteration", id);
918     }
919 
920     Tree res = eval(body, visited, pushValueDef(id, tree(num - 1), localValEnv));
921     for (int i = num - 2; i >= 0; i--) {
922         res = boxSeq(eval(body, visited, pushValueDef(id, tree(i), localValEnv)), res);
923     }
924 
925     return res;
926 }
927 
928 /**
929  * Iterate an addition construction
930  *
931  * Iterate an addition construction such that :
932  * par(i,10,E) --> E(i<-0)+E(i<-1)+...+E(i<-9)
933  * @param id the formal parameter of the iteration
934  * @param num the number of iterations
935  * @param body the body expression of the iteration
936  * @param globalDefEnv the global environment
937  * @param visited list of visited definition to detect recursive definitions
938  * @param localValEnv the local environment
939  * @return a block diagram in normal form
940  */
iterateSum(Tree id,int num,Tree body,Tree visited,Tree localValEnv)941 static Tree iterateSum(Tree id, int num, Tree body, Tree visited, Tree localValEnv)
942 {
943     if (num == 0) {
944         evalerror(yyfilename, -1, "iterateSum called with 0 iterations", id);
945     }
946 
947     Tree res = eval(body, visited, pushValueDef(id, tree(0), localValEnv));
948     for (int i = 1; i < num; i++) {
949         res = boxSeq(boxPar(res, eval(body, visited, pushValueDef(id, tree(i), localValEnv))), boxPrim2(sigAdd));
950     }
951 
952     return res;
953 }
954 
955 /**
956  * Iterate a product construction
957  *
958  * Iterate a product construction such that :
959  * par(i,10,E) --> E(i<-0)*E(i<-1)*...*E(i<-9)
960  * @param id the formal parameter of the iteration
961  * @param num the number of iterations
962  * @param body the body expression of the iteration
963  * @param globalDefEnv the global environment
964  * @param visited list of visited definition to detect recursive definitions
965  * @param localValEnv the local environment
966  * @return a block diagram in normal form
967  */
iterateProd(Tree id,int num,Tree body,Tree visited,Tree localValEnv)968 static Tree iterateProd(Tree id, int num, Tree body, Tree visited, Tree localValEnv)
969 {
970     if (num == 0) {
971         evalerror(yyfilename, -1, "iterateProd called with 0 iterations", id);
972     }
973 
974     Tree res = eval(body, visited, pushValueDef(id, tree(0), localValEnv));
975     for (int i = 1; i < num; i++) {
976         res = boxSeq(boxPar(res, eval(body, visited, pushValueDef(id, tree(i), localValEnv))), boxPrim2(sigMul));
977     }
978 
979     return res;
980 }
981 
982 /**
983  * Compute the sum of outputs of a list of boxes. The sum is
984  * valid if all the boxes have a valid boxType
985  *
986  * @param boxlist the list of boxes
987  * @param outputs sum of outputs of the boxes
988  * @return true if outputs is valid, false otherwise
989  */
990 #if 1
boxlistOutputs(Tree boxlist,int * outputs)991 static bool boxlistOutputs(Tree boxlist, int* outputs)
992 {
993     int ins, outs;
994 
995     *outputs = 0;
996     while (!isNil(boxlist)) {
997         Tree b = a2sb(hd(boxlist));  // for getBoxType, suppose list of evaluated boxes
998         if (getBoxType(b, &ins, &outs)) {
999             *outputs += outs;
1000         } else {
1001             // arbitrary output arity set to 1
1002             // when can't be determined
1003             *outputs += 1;
1004         }
1005         boxlist = tl(boxlist);
1006     }
1007     return isNil(boxlist);
1008 }
1009 #else
boxlistOutputs(Tree boxlist,int * outputs)1010 static bool boxlistOutputs(Tree boxlist, int* outputs)
1011 {
1012     int ins, outs;
1013 
1014     *outputs = 0;
1015     while (!isNil(boxlist) && getBoxType(hd(boxlist), &ins, &outs)) {
1016         *outputs += outs;
1017         boxlist = tl(boxlist);
1018     }
1019     return isNil(boxlist);
1020 }
1021 #endif
1022 
1023 /**
1024  * Repeat a wire n times
1025  */
nwires(int n)1026 static Tree nwires(int n)
1027 {
1028     Tree l = gGlobal->nil;
1029     while (n--) {
1030         l = cons(boxWire(), l);
1031     }
1032     return l;
1033 }
1034 
1035 /**
1036  * Apply a function to a list of arguments.
1037  * Apply a function F to a list of arguments (a,b,c,...).
1038  * F can be either a closure over an abstraction, or a
1039  * pattern matcher. If it is not the case then we have :
1040  * F(a,b,c,...) ==> (a,b,c,...):F
1041  *
1042  * @param fun the function to apply
1043  * @param larg the list of arguments
1044  * @return the resulting expression in normal form
1045  */
applyList(Tree fun,Tree larg)1046 static Tree applyList(Tree fun, Tree larg)
1047 {
1048     Tree abstr;
1049     Tree globalDefEnv;
1050     Tree visited;
1051     Tree localValEnv;
1052     Tree envList;
1053     Tree originalRules;
1054     Tree revParamList;
1055 
1056     Tree id;
1057     Tree body;
1058 
1059     Automaton* automat;
1060     int        state;
1061 
1062     prim2 p2;
1063 
1064     // cerr << "applyList (" << *fun << ", " << *larg << ")" << endl;
1065 
1066     if (isNil(larg)) return fun;
1067 
1068     if (isBoxError(fun) || isBoxError(larg)) {
1069         return boxError();
1070     }
1071 
1072     if (isBoxPatternMatcher(fun, automat, state, envList, originalRules, revParamList)) {
1073         Tree         result;
1074         int          state2;
1075         vector<Tree> envVect;
1076 
1077         list2vec(envList, envVect);
1078         // cerr << "applyList/apply_pattern_matcher(" << automat << "," << state << "," << *hd(larg) << ")" << endl;
1079         state2 = apply_pattern_matcher(automat, state, hd(larg), result, envVect);
1080         // cerr << "state2 = " << state2 << "; result = " << *result << endl;
1081         if (state2 >= 0 && isNil(result)) {
1082             // we need to continue the pattern matching
1083             return applyList(
1084                 boxPatternMatcher(automat, state2, vec2list(envVect), originalRules, cons(hd(larg), revParamList)),
1085                 tl(larg));
1086         } else if (state2 < 0) {
1087             stringstream error;
1088             error << "ERROR : pattern matching failed, no rule of " << boxpp(boxCase(originalRules))
1089                   << " matches argument list " << boxpp(reverse(cons(hd(larg), revParamList))) << endl;
1090             throw faustexception(error.str());
1091         } else {
1092             // Pattern Matching was succesful
1093             // the result is a closure that we need to evaluate.
1094             if (isClosure(result, body, globalDefEnv, visited, localValEnv)) {
1095                 // why ??? return simplifyPattern(eval(body, nil, localValEnv));
1096                 // return eval(body, nil, localValEnv);
1097                 return applyList(eval(body, gGlobal->nil, localValEnv), tl(larg));
1098             } else {
1099                 cerr << "wrong result from pattern matching (not a closure) : " << boxpp(result) << endl;
1100                 return boxError();
1101             }
1102         }
1103     }
1104     if (!isClosure(fun, abstr, globalDefEnv, visited, localValEnv)) {
1105         // principle : f(a,b,c,...) ==> (a,b,c,...):f
1106         int ins, outs;
1107 
1108         // check arity of function
1109         Tree efun = a2sb(fun);
1110         // cerr << "TRACEPOINT 1 : " << boxpp(efun) << endl;
1111         if (!getBoxType(efun, &ins, &outs)) {  // on laisse comme ca pour le moment
1112             // we can't determine the input arity of the expression
1113             // hope for the best
1114             return boxSeq(larg2par(larg), fun);
1115         }
1116 
1117         // check arity of arg list
1118         if (!boxlistOutputs(larg, &outs)) {
1119             // we don't know yet the output arity of larg. Therefore we can't
1120             // do any arity checking nor add _ to reach the required number of arguments
1121             // cerr << "warning : can't infere the type of : " << boxpp(larg) << endl;
1122             return boxSeq(larg2par(larg), fun);
1123         }
1124 
1125         if (outs > ins) {
1126             stringstream error;
1127             error << "ERROR : too much arguments : " << outs << ", instead of : " << ins << endl;
1128             error << "when applying : " << boxpp(fun) << endl << "to : " << boxpp(larg) << endl;
1129             throw faustexception(error.str());
1130         }
1131 
1132         if ((outs == 1) && ((isBoxPrim2(fun, &p2) && (p2 != sigPrefix)) ||
1133                             (getUserData(fun) && ((xtended*)getUserData(fun))->isSpecialInfix()))) {
1134             // special case : /(3) ==> _,3 : /
1135             Tree larg2 = concat(nwires(ins - outs), larg);
1136             return boxSeq(larg2par(larg2), fun);
1137 
1138         } else {
1139             Tree larg2 = concat(larg, nwires(ins - outs));
1140             return boxSeq(larg2par(larg2), fun);
1141         }
1142     }
1143 
1144     // Here fun is a closure, we can test the content of abstr
1145 
1146     if (isBoxEnvironment(abstr)) {
1147         evalerrorbox(yyfilename, -1, "an environment can't be used as a function", fun);
1148     }
1149 
1150     if (isBoxIdent(abstr)) {
1151         // We have an unevaluated Ident inside a closure
1152         // std::cerr << "applyList we are apply an ident inside a closure: " << boxpp(abstr) << std::endl;
1153         Tree fff = eval(abstr, visited, localValEnv);
1154         // std::cerr << "applyList it's value is : " << boxpp(fff) << std::endl;
1155 
1156         return applyList(fff, larg);
1157     }
1158 
1159     if (!isBoxAbstr(abstr, id, body)) {
1160         evalerror(yyfilename, -1, "(internal) not an abstraction inside closure (2)", fun);
1161     }
1162 
1163     // Here abstr is an abstraction, we can test the content of abstr
1164 
1165     // try to synthetise a name from the function name and the argument name
1166     {
1167         Tree arg = eval(hd(larg), visited, localValEnv);
1168         Tree narg;
1169         if (isBoxNumeric(arg, narg)) {
1170             arg = narg;
1171         }
1172         Tree f = eval(body, visited, pushValueDef(id, arg, localValEnv));
1173 
1174         Tree fname;
1175         if (getDefNameProperty(fun, fname)) {
1176             stringstream s;
1177             s << tree2str(fname);
1178             if (!gGlobal->gSimpleNames) s << "(" << boxpp(arg) << ")";
1179             setDefNameProperty(f, s.str());
1180         }
1181         return applyList(f, tl(larg));
1182     }
1183 }
1184 
1185 /**
1186  * Eval a list of expressions returning the list of results in reverse order.
1187  *
1188  * @param lexp list of expressions to evaluate
1189  * @param globalDefEnv the global environment
1190  * @param visited list of visited definition to detect recursive definitions
1191  * @param localValEnv the local environment
1192  * @return list of evaluated expressions in reverse order
1193  */
revEvalList(Tree lexp,Tree visited,Tree localValEnv)1194 static Tree revEvalList(Tree lexp, Tree visited, Tree localValEnv)
1195 {
1196     Tree result = gGlobal->nil;
1197     // Tree lexp_orig = lexp;
1198     // cerr << "ENTER revEvalList(" << *lexp_orig << ", env:" << *localValEnv << ")" << endl;
1199     while (!isNil(lexp)) {
1200         result = cons(eval(hd(lexp), visited, localValEnv), result);
1201         lexp   = tl(lexp);
1202     }
1203 
1204     // cerr << "EXIT revEvalList(" << *lexp_orig << ", env:" << *localValEnv << ") -> " << *result << endl;
1205     return result;
1206 }
1207 
1208 /**
1209  * Transform a list of expressions in a parallel construction
1210  *
1211  * @param larg list of expressions
1212  * @return parallel construction
1213  */
larg2par(Tree larg)1214 static Tree larg2par(Tree larg)
1215 {
1216     if (isNil(larg)) {
1217         evalerror(yyfilename, -1, "empty list of arguments", larg);
1218     }
1219     if (isNil(tl(larg))) {
1220         return hd(larg);
1221     }
1222     return boxPar(hd(larg), larg2par(tl(larg)));
1223 }
1224 
1225 /**
1226  * Search the environment for the definition of a symbol
1227  * ID and evaluate it. Detects recursive definitions using
1228  * a set of visited IDxENV. Associates the symbol as a definition name
1229  * property of the definition.
1230  * @param id the symbol ID t-o search
1231  * @param visited set of visited symbols (used for recursive definition detection)
1232  * @param lenv the environment where to search
1233  * @return the evaluated definition of ID
1234  */
evalIdDef(Tree id,Tree visited,Tree lenv)1235 static Tree evalIdDef(Tree id, Tree visited, Tree lenv)
1236 {
1237     Tree def  = nullptr;
1238     Tree name = nullptr;
1239 
1240     // search the environment env for a definition of symbol id
1241     while (!isNil(lenv) && !getProperty(lenv, id, def)) {
1242         faustassert(lenv->arity() > 0);
1243         lenv = lenv->branch(0);
1244     }
1245 
1246     // check that the definition exists
1247     if (isNil(lenv)) {
1248         if (hasDefProp(id)) {
1249             stringstream error;
1250             error << "ERROR : " << *id << " is defined here : " << getDefFileProp(id) << ":" << getDefLineProp(id)
1251                   << endl;
1252             throw faustexception(error.str());
1253         } else {
1254             evalerror(getUseFileProp(id), getUseLineProp(id), "undefined symbol", id);
1255         }
1256     }
1257 
1258     // cerr << "Id definition is " << *def << endl;
1259     // check that it is not a recursive definition
1260     Tree p = cons(id, lenv);
1261     // set the definition name property
1262     faustassert(def);
1263     if (!getDefNameProperty(def, name)) {
1264         // if the definition has no name use the identifier
1265         stringstream s;
1266         s << boxpp(id);
1267         // XXXXXX setDefNameProperty(def, s.str());
1268     }
1269 
1270     // return the evaluated definition
1271     return eval(def, addElement(p, visited), gGlobal->nil);
1272 }
1273 
1274 /**
1275  * Creates a list of n elements.
1276  * @param n number of elements
1277  * @param e element to be repeated
1278  * @return [e e e ...] n times
1279  */
1280 
listn(int n,Tree e)1281 static Tree listn(int n, Tree e)
1282 {
1283     return (n <= 0) ? gGlobal->nil : cons(e, listn(n - 1, e));
1284 }
1285 
1286 /**
1287  * A property to store the pattern matcher corresponding to a set of rules
1288  * in a specific environement
1289  */
1290 
setPMProperty(Tree t,Tree env,Tree pm)1291 static void setPMProperty(Tree t, Tree env, Tree pm)
1292 {
1293     setProperty(t, tree(gGlobal->PMPROPERTYNODE, env), pm);
1294 }
1295 
getPMProperty(Tree t,Tree env,Tree & pm)1296 static bool getPMProperty(Tree t, Tree env, Tree& pm)
1297 {
1298     return getProperty(t, tree(gGlobal->PMPROPERTYNODE, env), pm);
1299 }
1300 
1301 /**
1302  * Eval a case expression containing a list of pattern matching rules.
1303  * Creates a boxPatternMatcher containing a pm autamaton a state
1304  * and a list of environments.
1305  * @param rules the list of rules
1306  * @param env the environment uused to evaluate the patterns and closure the rhs
1307  * @return a boxPatternMatcher ready to be applied
1308  */
1309 
evalCase(Tree rules,Tree env)1310 static Tree evalCase(Tree rules, Tree env)
1311 {
1312     Tree pm;
1313     if (!getPMProperty(rules, env, pm)) {
1314         Automaton* a = make_pattern_matcher(evalRuleList(rules, env));
1315         pm           = boxPatternMatcher(a, 0, listn(len(rules), pushEnvBarrier(env)), rules, gGlobal->nil);
1316         setPMProperty(rules, env, pm);
1317     }
1318     return pm;
1319 }
1320 
1321 /**
1322  * Evaluates each rule of the list
1323  */
evalRuleList(Tree rules,Tree env)1324 static Tree evalRuleList(Tree rules, Tree env)
1325 {
1326     // cerr << "evalRuleList "<< *rules << " in " << *env << endl;
1327     if (isNil(rules))
1328         return gGlobal->nil;
1329     else
1330         return cons(evalRule(hd(rules), env), evalRuleList(tl(rules), env));
1331 }
1332 
1333 /**
1334  * Evaluates the list of patterns and closure the rhs
1335  */
evalRule(Tree rule,Tree env)1336 static Tree evalRule(Tree rule, Tree env)
1337 {
1338     // cerr << "evalRule "<< *rule << " in " << *env << endl;
1339     return cons(evalPatternList(left(rule), env), right(rule));
1340 }
1341 
1342 /**
1343  * Evaluates each pattern of the list
1344  */
evalPatternList(Tree patterns,Tree env)1345 static Tree evalPatternList(Tree patterns, Tree env)
1346 {
1347     if (isNil(patterns)) {
1348         return gGlobal->nil;
1349     } else {
1350         return cons(evalPattern(hd(patterns), env), evalPatternList(tl(patterns), env));
1351     }
1352 }
1353 
1354 /**
1355  * Evaluates a pattern and simplify it to numerical value
1356  * if possible
1357  */
evalPattern(Tree pattern,Tree env)1358 static Tree evalPattern(Tree pattern, Tree env)
1359 {
1360     Tree p = eval(pattern, gGlobal->nil, env);
1361     return patternSimplification(p);
1362 }
1363 
list2vec(Tree l,vector<Tree> & v)1364 static void list2vec(Tree l, vector<Tree>& v)
1365 {
1366     while (!isNil(l)) {
1367         v.push_back(hd(l));
1368         l = tl(l);
1369     }
1370 }
1371 
vec2list(const vector<Tree> & v)1372 static Tree vec2list(const vector<Tree>& v)
1373 {
1374     Tree l = gGlobal->nil;
1375     int  n = (int)v.size();
1376 
1377     while (n--) {
1378         l = cons(v[n], l);
1379     }
1380     return l;
1381 }
1382 
1383 /////////////////////////////////////////////////////////////////////////////////////////////////////////
1384 // Further simplification : replace bloc-diagrams that denote constant number by this number
1385 /////////////////////////////////////////////////////////////////////////////////////////////////////////
1386 
1387 static Tree numericBoxSimplification(Tree box);
1388 static Tree insideBoxSimplification(Tree box);
1389 
1390 /**
1391  * boxSimplification(box) : simplify a block-diagram by replacing expressions
1392  * denoting a constant number by this number.
1393  */
boxSimplification(Tree box)1394 static Tree boxSimplification(Tree box)
1395 {
1396     Tree simplified;
1397 
1398     if (gGlobal->gSimplifiedBoxProperty->get(box, simplified)) {
1399         return simplified;
1400 
1401     } else {
1402         simplified = numericBoxSimplification(box);
1403 
1404         // transfers name property if any
1405         Tree name;
1406         if (getDefNameProperty(box, name)) setDefNameProperty(simplified, name);
1407 
1408         // attach simplified expression as a property of original box
1409         gGlobal->gSimplifiedBoxProperty->set(box, simplified);
1410 
1411         return simplified;
1412     }
1413 }
1414 
1415 /**
1416  * Try to do a numeric simplification of a block-diagram
1417  */
numericBoxSimplification(Tree box)1418 static Tree numericBoxSimplification(Tree box)
1419 {
1420     int    ins, outs;
1421     Tree   result;
1422     int    i;
1423     double x;
1424 
1425     if (!getBoxType(box, &ins, &outs)) {
1426         stringstream error;
1427         error << "ERROR in file " << __FILE__ << ':' << __LINE__ << ", can't compute the box type of : ";
1428         error << *box << endl;
1429         throw faustexception(error.str());
1430     }
1431 
1432     if (ins == 0 && outs == 1) {
1433         // this box can potentially denote a number
1434         if (isBoxInt(box, &i) || isBoxReal(box, &x)) {
1435             result = box;
1436         } else {
1437             // propagate signals to discover if it simplifies to a number
1438             int    i1;
1439             double x1;
1440             Tree   lsignals = boxPropagateSig(gGlobal->nil, box, makeSigInputList(0));
1441             // cerr << "simplify 1389" << endl;
1442             Tree s = simplify(hd(lsignals));
1443 
1444             if (isSigReal(s, &x1)) {
1445                 result = boxReal(x1);
1446             } else if (isSigInt(s, &i1)) {
1447                 result = boxInt(i1);
1448             } else {
1449                 result = insideBoxSimplification(box);
1450             }
1451         }
1452     } else {
1453         // this box can't denote a number
1454         result = insideBoxSimplification(box);
1455     }
1456     return result;
1457 }
1458 
1459 /**
1460  *  Simplify inside a block-diagram : S[A*B] => S[A]*S[B]
1461  */
insideBoxSimplification(Tree box)1462 static Tree insideBoxSimplification(Tree box)
1463 {
1464     int    i;
1465     double r;
1466     prim0  p0;
1467     prim1  p1;
1468     prim2  p2;
1469     prim3  p3;
1470     prim4  p4;
1471     prim5  p5;
1472 
1473     Tree t1, t2, ff, label, cur, min, max, step, type, name, file, slot, body;
1474 
1475     xtended* xt = (xtended*)getUserData(box);
1476 
1477     // Extended Primitives
1478 
1479     if (xt) {
1480         return box;
1481     }
1482 
1483     // Numbers and Constants
1484 
1485     else if (isBoxInt(box, &i)) {
1486         return box;
1487     } else if (isBoxReal(box, &r)) {
1488         return box;
1489     }
1490 
1491     else if (isBoxFConst(box, type, name, file)) {
1492         return box;
1493     }
1494 
1495     else if (isBoxFVar(box, type, name, file)) {
1496         return box;
1497     }
1498 
1499     // Wire and Cut
1500 
1501     else if (isBoxCut(box)) {
1502         return box;
1503     }
1504 
1505     else if (isBoxWire(box)) {
1506         return box;
1507     }
1508 
1509     // Primitives
1510 
1511     else if (isBoxPrim0(box, &p0)) {
1512         return box;
1513     }
1514 
1515     else if (isBoxPrim1(box, &p1)) {
1516         return box;
1517     }
1518 
1519     else if (isBoxPrim2(box, &p2)) {
1520         return box;
1521     }
1522 
1523     else if (isBoxPrim3(box, &p3)) {
1524         return box;
1525     }
1526 
1527     else if (isBoxPrim4(box, &p4)) {
1528         return box;
1529     }
1530 
1531     else if (isBoxPrim5(box, &p5)) {
1532         return box;
1533     }
1534 
1535     else if (isBoxFFun(box, ff)) {
1536         return box;
1537     }
1538 
1539     // User Interface Widgets
1540 
1541     else if (isBoxButton(box, label)) {
1542         return box;
1543     }
1544 
1545     else if (isBoxCheckbox(box, label)) {
1546         return box;
1547     }
1548 
1549     else if (isBoxVSlider(box, label, cur, min, max, step)) {
1550         return box;
1551     }
1552 
1553     else if (isBoxHSlider(box, label, cur, min, max, step)) {
1554         return box;
1555     }
1556 
1557     else if (isBoxNumEntry(box, label, cur, min, max, step)) {
1558         return box;
1559     }
1560 
1561     else if (isBoxVBargraph(box, label, min, max)) {
1562         return box;
1563     }
1564 
1565     else if (isBoxHBargraph(box, label, min, max)) {
1566         return box;
1567     }
1568 
1569     // User Interface Groups
1570 
1571     else if (isBoxVGroup(box, label, t1)) {
1572         return boxVGroup(label, boxSimplification(t1));
1573     }
1574 
1575     else if (isBoxHGroup(box, label, t1)) {
1576         return boxHGroup(label, boxSimplification(t1));
1577     }
1578 
1579     else if (isBoxTGroup(box, label, t1)) {
1580         return boxTGroup(label, boxSimplification(t1));
1581     }
1582 
1583     // Slots and Symbolic Boxes
1584 
1585     else if (isBoxSlot(box)) {
1586         return box;
1587     }
1588 
1589     else if (isBoxSymbolic(box, slot, body)) {
1590         Tree b = boxSimplification(body);
1591         return boxSymbolic(slot, b);
1592     }
1593 
1594     // Block Diagram Composition Algebra
1595 
1596     else if (isBoxSeq(box, t1, t2)) {
1597         Tree s1 = boxSimplification(t1);
1598         Tree s2 = boxSimplification(t2);
1599         return boxSeq(s1, s2);
1600     }
1601 
1602     else if (isBoxPar(box, t1, t2)) {
1603         Tree s1 = boxSimplification(t1);
1604         Tree s2 = boxSimplification(t2);
1605         return boxPar(s1, s2);
1606     }
1607 
1608     else if (isBoxSplit(box, t1, t2)) {
1609         Tree s1 = boxSimplification(t1);
1610         Tree s2 = boxSimplification(t2);
1611         return boxSplit(s1, s2);
1612     }
1613 
1614     else if (isBoxMerge(box, t1, t2)) {
1615         Tree s1 = boxSimplification(t1);
1616         Tree s2 = boxSimplification(t2);
1617         return boxMerge(s1, s2);
1618     } else if (isBoxRec(box, t1, t2)) {
1619         Tree s1 = boxSimplification(t1);
1620         Tree s2 = boxSimplification(t2);
1621         return boxRec(s1, s2);
1622     } else if (isBoxMetadata(box, t1, t2)) {
1623         Tree s1 = boxSimplification(t1);
1624         cout << "is this right?" << endl;
1625         return boxMetadata(s1, t2);
1626     }
1627 
1628     stringstream error;
1629     error << "ERROR in file " << __FILE__ << ':' << __LINE__ << ", unrecognised box expression : " << *box << endl;
1630     throw faustexception(error.str());
1631     return nullptr;
1632 }
1633