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