OpenShot Audio Library | OpenShotAudio 0.3.2
Loading...
Searching...
No Matches
juce_Expression.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26class Expression::Term : public SingleThreadedReferenceCountedObject
27{
28public:
29 Term() {}
30 virtual ~Term() {}
31
32 virtual Type getType() const noexcept = 0;
33 virtual Term* clone() const = 0;
34 virtual ReferenceCountedObjectPtr<Term> resolve (const Scope&, int recursionDepth) = 0;
35 virtual String toString() const = 0;
36 virtual double toDouble() const { return 0; }
37 virtual int getInputIndexFor (const Term*) const { return -1; }
38 virtual int getOperatorPrecedence() const { return 0; }
39 virtual int getNumInputs() const { return 0; }
40 virtual Term* getInput (int) const { return nullptr; }
41 virtual ReferenceCountedObjectPtr<Term> negated();
42
43 virtual ReferenceCountedObjectPtr<Term> createTermToEvaluateInput (const Scope&, const Term* /*inputTerm*/,
44 double /*overallTarget*/, Term* /*topLevelTerm*/) const
45 {
46 jassertfalse;
47 return ReferenceCountedObjectPtr<Term>();
48 }
49
50 virtual String getName() const
51 {
52 jassertfalse; // You shouldn't call this for an expression that's not actually a function!
53 return {};
54 }
55
56 virtual void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth)
57 {
58 for (int i = getNumInputs(); --i >= 0;)
59 getInput (i)->renameSymbol (oldSymbol, newName, scope, recursionDepth);
60 }
61
62 class SymbolVisitor
63 {
64 public:
65 virtual ~SymbolVisitor() {}
66 virtual void useSymbol (const Symbol&) = 0;
67 };
68
69 virtual void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth)
70 {
71 for (int i = getNumInputs(); --i >= 0;)
72 getInput(i)->visitAllSymbols (visitor, scope, recursionDepth);
73 }
74
75private:
76 JUCE_DECLARE_NON_COPYABLE (Term)
77};
78
79
80//==============================================================================
81struct Expression::Helpers
82{
83 using TermPtr = ReferenceCountedObjectPtr<Term>;
84
85 static void checkRecursionDepth (int depth)
86 {
87 if (depth > 256)
88 throw EvaluationError ("Recursive symbol references");
89 }
90
91 friend class Expression::Term;
92
93 //==============================================================================
95 class EvaluationError : public std::exception
96 {
97 public:
98 EvaluationError (const String& desc) : description (desc)
99 {
100 DBG ("Expression::EvaluationError: " + description);
101 }
102
103 String description;
104 };
105
106 //==============================================================================
107 class Constant : public Term
108 {
109 public:
110 Constant (double val, bool resolutionTarget)
111 : value (val), isResolutionTarget (resolutionTarget) {}
112
113 Type getType() const noexcept { return constantType; }
114 Term* clone() const { return new Constant (value, isResolutionTarget); }
115 TermPtr resolve (const Scope&, int) { return *this; }
116 double toDouble() const { return value; }
117 TermPtr negated() { return *new Constant (-value, isResolutionTarget); }
118
119 String toString() const
120 {
121 String s (value);
122 if (isResolutionTarget)
123 s = "@" + s;
124
125 return s;
126 }
127
128 double value;
129 bool isResolutionTarget;
130 };
131
132 //==============================================================================
133 class BinaryTerm : public Term
134 {
135 public:
136 BinaryTerm (TermPtr l, TermPtr r) : left (std::move (l)), right (std::move (r))
137 {
138 jassert (left != nullptr && right != nullptr);
139 }
140
141 int getInputIndexFor (const Term* possibleInput) const
142 {
143 return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1);
144 }
145
146 Type getType() const noexcept { return operatorType; }
147 int getNumInputs() const { return 2; }
148 Term* getInput (int index) const { return index == 0 ? left.get() : (index == 1 ? right.get() : nullptr); }
149
150 virtual double performFunction (double left, double right) const = 0;
151 virtual void writeOperator (String& dest) const = 0;
152
153 TermPtr resolve (const Scope& scope, int recursionDepth)
154 {
155 return *new Constant (performFunction (left ->resolve (scope, recursionDepth)->toDouble(),
156 right->resolve (scope, recursionDepth)->toDouble()), false);
157 }
158
159 String toString() const
160 {
161 String s;
162 auto ourPrecendence = getOperatorPrecedence();
163
164 if (left->getOperatorPrecedence() > ourPrecendence)
165 s << '(' << left->toString() << ')';
166 else
167 s = left->toString();
168
169 writeOperator (s);
170
171 if (right->getOperatorPrecedence() >= ourPrecendence)
172 s << '(' << right->toString() << ')';
173 else
174 s << right->toString();
175
176 return s;
177 }
178
179 protected:
180 const TermPtr left, right;
181
182 TermPtr createDestinationTerm (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const
183 {
184 jassert (input == left || input == right);
185 if (input != left && input != right)
186 return {};
187
188 if (auto dest = findDestinationFor (topLevelTerm, this))
189 return dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm);
190
191 return *new Constant (overallTarget, false);
192 }
193 };
194
195 //==============================================================================
196 class SymbolTerm : public Term
197 {
198 public:
199 explicit SymbolTerm (const String& sym) : symbol (sym) {}
200
201 TermPtr resolve (const Scope& scope, int recursionDepth)
202 {
203 checkRecursionDepth (recursionDepth);
204 return scope.getSymbolValue (symbol).term->resolve (scope, recursionDepth + 1);
205 }
206
207 Type getType() const noexcept { return symbolType; }
208 Term* clone() const { return new SymbolTerm (symbol); }
209 String toString() const { return symbol; }
210 String getName() const { return symbol; }
211
212 void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth)
213 {
214 checkRecursionDepth (recursionDepth);
215 visitor.useSymbol (Symbol (scope.getScopeUID(), symbol));
216 scope.getSymbolValue (symbol).term->visitAllSymbols (visitor, scope, recursionDepth + 1);
217 }
218
219 void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int /*recursionDepth*/)
220 {
221 if (oldSymbol.symbolName == symbol && scope.getScopeUID() == oldSymbol.scopeUID)
222 symbol = newName;
223 }
224
225 String symbol;
226 };
227
228 //==============================================================================
229 class Function : public Term
230 {
231 public:
232 explicit Function (const String& name) : functionName (name) {}
233
234 Function (const String& name, const Array<Expression>& params)
235 : functionName (name), parameters (params)
236 {}
237
238 Type getType() const noexcept { return functionType; }
239 Term* clone() const { return new Function (functionName, parameters); }
240 int getNumInputs() const { return parameters.size(); }
241 Term* getInput (int i) const { return parameters.getReference(i).term.get(); }
242 String getName() const { return functionName; }
243
244 TermPtr resolve (const Scope& scope, int recursionDepth)
245 {
246 checkRecursionDepth (recursionDepth);
247 double result = 0;
248 auto numParams = parameters.size();
249
250 if (numParams > 0)
251 {
252 HeapBlock<double> params (numParams);
253
254 for (int i = 0; i < numParams; ++i)
255 params[i] = parameters.getReference(i).term->resolve (scope, recursionDepth + 1)->toDouble();
256
257 result = scope.evaluateFunction (functionName, params, numParams);
258 }
259 else
260 {
261 result = scope.evaluateFunction (functionName, nullptr, 0);
262 }
263
264 return *new Constant (result, false);
265 }
266
267 int getInputIndexFor (const Term* possibleInput) const
268 {
269 for (int i = 0; i < parameters.size(); ++i)
270 if (parameters.getReference(i).term == possibleInput)
271 return i;
272
273 return -1;
274 }
275
276 String toString() const
277 {
278 if (parameters.size() == 0)
279 return functionName + "()";
280
281 String s (functionName + " (");
282
283 for (int i = 0; i < parameters.size(); ++i)
284 {
285 s << parameters.getReference(i).term->toString();
286
287 if (i < parameters.size() - 1)
288 s << ", ";
289 }
290
291 s << ')';
292 return s;
293 }
294
295 const String functionName;
296 Array<Expression> parameters;
297 };
298
299 //==============================================================================
300 class DotOperator : public BinaryTerm
301 {
302 public:
303 DotOperator (SymbolTerm* l, TermPtr r) : BinaryTerm (TermPtr (l), r) {}
304
305 TermPtr resolve (const Scope& scope, int recursionDepth)
306 {
307 checkRecursionDepth (recursionDepth);
308
309 EvaluationVisitor visitor (right, recursionDepth + 1);
310 scope.visitRelativeScope (getSymbol()->symbol, visitor);
311 return visitor.output;
312 }
313
314 Term* clone() const { return new DotOperator (getSymbol(), *right); }
315 String getName() const { return "."; }
316 int getOperatorPrecedence() const { return 1; }
317 void writeOperator (String& dest) const { dest << '.'; }
318 double performFunction (double, double) const { return 0.0; }
319
320 void visitAllSymbols (SymbolVisitor& visitor, const Scope& scope, int recursionDepth)
321 {
322 checkRecursionDepth (recursionDepth);
323 visitor.useSymbol (Symbol (scope.getScopeUID(), getSymbol()->symbol));
324
325 SymbolVisitingVisitor v (right, visitor, recursionDepth + 1);
326
327 try
328 {
329 scope.visitRelativeScope (getSymbol()->symbol, v);
330 }
331 catch (...) {}
332 }
333
334 void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth)
335 {
336 checkRecursionDepth (recursionDepth);
337 getSymbol()->renameSymbol (oldSymbol, newName, scope, recursionDepth);
338
339 SymbolRenamingVisitor visitor (right, oldSymbol, newName, recursionDepth + 1);
340
341 try
342 {
343 scope.visitRelativeScope (getSymbol()->symbol, visitor);
344 }
345 catch (...) {}
346 }
347
348 private:
349 //==============================================================================
350 class EvaluationVisitor : public Scope::Visitor
351 {
352 public:
353 EvaluationVisitor (const TermPtr& t, const int recursion)
354 : input (t), output (t), recursionCount (recursion) {}
355
356 void visit (const Scope& scope) { output = input->resolve (scope, recursionCount); }
357
358 const TermPtr input;
359 TermPtr output;
360 const int recursionCount;
361
362 private:
363 JUCE_DECLARE_NON_COPYABLE (EvaluationVisitor)
364 };
365
366 class SymbolVisitingVisitor : public Scope::Visitor
367 {
368 public:
369 SymbolVisitingVisitor (const TermPtr& t, SymbolVisitor& v, const int recursion)
370 : input (t), visitor (v), recursionCount (recursion) {}
371
372 void visit (const Scope& scope) { input->visitAllSymbols (visitor, scope, recursionCount); }
373
374 private:
375 const TermPtr input;
376 SymbolVisitor& visitor;
377 const int recursionCount;
378
379 JUCE_DECLARE_NON_COPYABLE (SymbolVisitingVisitor)
380 };
381
382 class SymbolRenamingVisitor : public Scope::Visitor
383 {
384 public:
385 SymbolRenamingVisitor (const TermPtr& t, const Expression::Symbol& symbol_, const String& newName_, const int recursionCount_)
386 : input (t), symbol (symbol_), newName (newName_), recursionCount (recursionCount_) {}
387
388 void visit (const Scope& scope) { input->renameSymbol (symbol, newName, scope, recursionCount); }
389
390 private:
391 const TermPtr input;
392 const Symbol& symbol;
393 const String newName;
394 const int recursionCount;
395
396 JUCE_DECLARE_NON_COPYABLE (SymbolRenamingVisitor)
397 };
398
399 SymbolTerm* getSymbol() const { return static_cast<SymbolTerm*> (left.get()); }
400
401 JUCE_DECLARE_NON_COPYABLE (DotOperator)
402 };
403
404 //==============================================================================
405 class Negate : public Term
406 {
407 public:
408 explicit Negate (const TermPtr& t) : input (t)
409 {
410 jassert (t != nullptr);
411 }
412
413 Type getType() const noexcept { return operatorType; }
414 int getInputIndexFor (const Term* possibleInput) const { return possibleInput == input ? 0 : -1; }
415 int getNumInputs() const { return 1; }
416 Term* getInput (int index) const { return index == 0 ? input.get() : nullptr; }
417 Term* clone() const { return new Negate (*input->clone()); }
418
419 TermPtr resolve (const Scope& scope, int recursionDepth)
420 {
421 return *new Constant (-input->resolve (scope, recursionDepth)->toDouble(), false);
422 }
423
424 String getName() const { return "-"; }
425 TermPtr negated() { return input; }
426
427 TermPtr createTermToEvaluateInput (const Scope& scope, const Term* t, double overallTarget, Term* topLevelTerm) const
428 {
429 ignoreUnused (t);
430 jassert (t == input);
431
432 const Term* const dest = findDestinationFor (topLevelTerm, this);
433
434 return *new Negate (dest == nullptr ? TermPtr (*new Constant (overallTarget, false))
435 : dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm));
436 }
437
438 String toString() const
439 {
440 if (input->getOperatorPrecedence() > 0)
441 return "-(" + input->toString() + ")";
442
443 return "-" + input->toString();
444 }
445
446 private:
447 const TermPtr input;
448 };
449
450 //==============================================================================
451 class Add : public BinaryTerm
452 {
453 public:
454 Add (TermPtr l, TermPtr r) : BinaryTerm (l, r) {}
455
456 Term* clone() const { return new Add (*left->clone(), *right->clone()); }
457 double performFunction (double lhs, double rhs) const { return lhs + rhs; }
458 int getOperatorPrecedence() const { return 3; }
459 String getName() const { return "+"; }
460 void writeOperator (String& dest) const { dest << " + "; }
461
462 TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const
463 {
464 if (auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm))
465 return *new Subtract (newDest, *(input == left ? right : left)->clone());
466
467 return {};
468 }
469
470 private:
471 JUCE_DECLARE_NON_COPYABLE (Add)
472 };
473
474 //==============================================================================
475 class Subtract : public BinaryTerm
476 {
477 public:
478 Subtract (TermPtr l, TermPtr r) : BinaryTerm (l, r) {}
479
480 Term* clone() const { return new Subtract (*left->clone(), *right->clone()); }
481 double performFunction (double lhs, double rhs) const { return lhs - rhs; }
482 int getOperatorPrecedence() const { return 3; }
483 String getName() const { return "-"; }
484 void writeOperator (String& dest) const { dest << " - "; }
485
486 TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const
487 {
488 if (auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm))
489 {
490 if (input == left)
491 return *new Add (*newDest, *right->clone());
492
493 return *new Subtract (*left->clone(), *newDest);
494 }
495
496 return {};
497 }
498
499 private:
500 JUCE_DECLARE_NON_COPYABLE (Subtract)
501 };
502
503 //==============================================================================
504 class Multiply : public BinaryTerm
505 {
506 public:
507 Multiply (TermPtr l, TermPtr r) : BinaryTerm (l, r) {}
508
509 Term* clone() const { return new Multiply (*left->clone(), *right->clone()); }
510 double performFunction (double lhs, double rhs) const { return lhs * rhs; }
511 String getName() const { return "*"; }
512 void writeOperator (String& dest) const { dest << " * "; }
513 int getOperatorPrecedence() const { return 2; }
514
515 TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const
516 {
517 if (auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm))
518 return *new Divide (newDest, *(input == left ? right : left)->clone());
519
520 return {};
521 }
522
523 JUCE_DECLARE_NON_COPYABLE (Multiply)
524 };
525
526 //==============================================================================
527 class Divide : public BinaryTerm
528 {
529 public:
530 Divide (TermPtr l, TermPtr r) : BinaryTerm (l, r) {}
531
532 Term* clone() const { return new Divide (*left->clone(), *right->clone()); }
533 double performFunction (double lhs, double rhs) const { return lhs / rhs; }
534 String getName() const { return "/"; }
535 void writeOperator (String& dest) const { dest << " / "; }
536 int getOperatorPrecedence() const { return 2; }
537
538 TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input, double overallTarget, Term* topLevelTerm) const
539 {
540 auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm);
541
542 if (newDest == nullptr)
543 return {};
544
545 if (input == left)
546 return *new Multiply (*newDest, *right->clone());
547
548 return *new Divide (*left->clone(), *newDest);
549 }
550
551 JUCE_DECLARE_NON_COPYABLE (Divide)
552 };
553
554 //==============================================================================
555 static Term* findDestinationFor (Term* const topLevel, const Term* const inputTerm)
556 {
557 const int inputIndex = topLevel->getInputIndexFor (inputTerm);
558 if (inputIndex >= 0)
559 return topLevel;
560
561 for (int i = topLevel->getNumInputs(); --i >= 0;)
562 {
563 Term* const t = findDestinationFor (topLevel->getInput (i), inputTerm);
564
565 if (t != nullptr)
566 return t;
567 }
568
569 return nullptr;
570 }
571
572 static Constant* findTermToAdjust (Term* const term, const bool mustBeFlagged)
573 {
574 jassert (term != nullptr);
575
576 if (term->getType() == constantType)
577 {
578 Constant* const c = static_cast<Constant*> (term);
579 if (c->isResolutionTarget || ! mustBeFlagged)
580 return c;
581 }
582
583 if (term->getType() == functionType)
584 return nullptr;
585
586 const int numIns = term->getNumInputs();
587
588 for (int i = 0; i < numIns; ++i)
589 {
590 Term* const input = term->getInput (i);
591
592 if (input->getType() == constantType)
593 {
594 Constant* const c = static_cast<Constant*> (input);
595
596 if (c->isResolutionTarget || ! mustBeFlagged)
597 return c;
598 }
599 }
600
601 for (int i = 0; i < numIns; ++i)
602 if (auto c = findTermToAdjust (term->getInput (i), mustBeFlagged))
603 return c;
604
605 return nullptr;
606 }
607
608 static bool containsAnySymbols (const Term& t)
609 {
610 if (t.getType() == Expression::symbolType)
611 return true;
612
613 for (int i = t.getNumInputs(); --i >= 0;)
614 if (containsAnySymbols (*t.getInput (i)))
615 return true;
616
617 return false;
618 }
619
620 //==============================================================================
621 class SymbolCheckVisitor : public Term::SymbolVisitor
622 {
623 public:
624 SymbolCheckVisitor (const Symbol& s) : symbol (s) {}
625 void useSymbol (const Symbol& s) { wasFound = wasFound || s == symbol; }
626
627 bool wasFound = false;
628
629 private:
630 const Symbol& symbol;
631
632 JUCE_DECLARE_NON_COPYABLE (SymbolCheckVisitor)
633 };
634
635 //==============================================================================
636 class SymbolListVisitor : public Term::SymbolVisitor
637 {
638 public:
639 SymbolListVisitor (Array<Symbol>& list_) : list (list_) {}
640 void useSymbol (const Symbol& s) { list.addIfNotAlreadyThere (s); }
641
642 private:
643 Array<Symbol>& list;
644
645 JUCE_DECLARE_NON_COPYABLE (SymbolListVisitor)
646 };
647
648 //==============================================================================
649 class Parser
650 {
651 public:
652 //==============================================================================
653 Parser (String::CharPointerType& stringToParse) : text (stringToParse)
654 {
655 }
656
657 TermPtr readUpToComma()
658 {
659 if (text.isEmpty())
660 return *new Constant (0.0, false);
661
662 auto e = readExpression();
663
664 if (e == nullptr || ((! readOperator (",")) && ! text.isEmpty()))
665 return parseError ("Syntax error: \"" + String (text) + "\"");
666
667 return e;
668 }
669
670 String error;
671
672 private:
673 String::CharPointerType& text;
674
675 TermPtr parseError (const String& message)
676 {
677 if (error.isEmpty())
678 error = message;
679
680 return {};
681 }
682
683 //==============================================================================
684 static inline bool isDecimalDigit (const juce_wchar c) noexcept
685 {
686 return c >= '0' && c <= '9';
687 }
688
689 bool readChar (const juce_wchar required) noexcept
690 {
691 if (*text == required)
692 {
693 ++text;
694 return true;
695 }
696
697 return false;
698 }
699
700 bool readOperator (const char* ops, char* const opType = nullptr) noexcept
701 {
702 text = text.findEndOfWhitespace();
703
704 while (*ops != 0)
705 {
706 if (readChar ((juce_wchar) (uint8) *ops))
707 {
708 if (opType != nullptr)
709 *opType = *ops;
710
711 return true;
712 }
713
714 ++ops;
715 }
716
717 return false;
718 }
719
720 bool readIdentifier (String& identifier) noexcept
721 {
722 text = text.findEndOfWhitespace();
723 auto t = text;
724 int numChars = 0;
725
726 if (t.isLetter() || *t == '_')
727 {
728 ++t;
729 ++numChars;
730
731 while (t.isLetterOrDigit() || *t == '_')
732 {
733 ++t;
734 ++numChars;
735 }
736 }
737
738 if (numChars > 0)
739 {
740 identifier = String (text, (size_t) numChars);
741 text = t;
742 return true;
743 }
744
745 return false;
746 }
747
748 Term* readNumber() noexcept
749 {
750 text = text.findEndOfWhitespace();
751 auto t = text;
752 bool isResolutionTarget = (*t == '@');
753
754 if (isResolutionTarget)
755 {
756 ++t;
757 t = t.findEndOfWhitespace();
758 text = t;
759 }
760
761 if (*t == '-')
762 {
763 ++t;
764 t = t.findEndOfWhitespace();
765 }
766
767 if (isDecimalDigit (*t) || (*t == '.' && isDecimalDigit (t[1])))
768 return new Constant (CharacterFunctions::readDoubleValue (text), isResolutionTarget);
769
770 return nullptr;
771 }
772
773 TermPtr readExpression()
774 {
775 auto lhs = readMultiplyOrDivideExpression();
776 char opType;
777
778 while (lhs != nullptr && readOperator ("+-", &opType))
779 {
780 auto rhs = readMultiplyOrDivideExpression();
781
782 if (rhs == nullptr)
783 return parseError ("Expected expression after \"" + String::charToString ((juce_wchar) (uint8) opType) + "\"");
784
785 if (opType == '+')
786 lhs = *new Add (lhs, rhs);
787 else
788 lhs = *new Subtract (lhs, rhs);
789 }
790
791 return lhs;
792 }
793
794 TermPtr readMultiplyOrDivideExpression()
795 {
796 auto lhs = readUnaryExpression();
797 char opType;
798
799 while (lhs != nullptr && readOperator ("*/", &opType))
800 {
801 TermPtr rhs (readUnaryExpression());
802
803 if (rhs == nullptr)
804 return parseError ("Expected expression after \"" + String::charToString ((juce_wchar) (uint8) opType) + "\"");
805
806 if (opType == '*')
807 lhs = *new Multiply (lhs, rhs);
808 else
809 lhs = *new Divide (lhs, rhs);
810 }
811
812 return lhs;
813 }
814
815 TermPtr readUnaryExpression()
816 {
817 char opType;
818 if (readOperator ("+-", &opType))
819 {
820 TermPtr e (readUnaryExpression());
821
822 if (e == nullptr)
823 return parseError ("Expected expression after \"" + String::charToString ((juce_wchar) (uint8) opType) + "\"");
824
825 if (opType == '-')
826 e = e->negated();
827
828 return e;
829 }
830
831 return readPrimaryExpression();
832 }
833
834 TermPtr readPrimaryExpression()
835 {
836 if (auto e = readParenthesisedExpression())
837 return e;
838
839 if (auto e = readNumber())
840 return e;
841
842 return readSymbolOrFunction();
843 }
844
845 TermPtr readSymbolOrFunction()
846 {
847 String identifier;
848
849 if (readIdentifier (identifier))
850 {
851 if (readOperator ("(")) // method call...
852 {
853 auto f = new Function (identifier);
854 std::unique_ptr<Term> func (f); // (can't use std::unique_ptr<Function> in MSVC)
855
856 auto param = readExpression();
857
858 if (param == nullptr)
859 {
860 if (readOperator (")"))
861 return TermPtr (func.release());
862
863 return parseError ("Expected parameters after \"" + identifier + " (\"");
864 }
865
866 f->parameters.add (Expression (param.get()));
867
868 while (readOperator (","))
869 {
870 param = readExpression();
871
872 if (param == nullptr)
873 return parseError ("Expected expression after \",\"");
874
875 f->parameters.add (Expression (param.get()));
876 }
877
878 if (readOperator (")"))
879 return TermPtr (func.release());
880
881 return parseError ("Expected \")\"");
882 }
883
884 if (readOperator ("."))
885 {
886 TermPtr rhs (readSymbolOrFunction());
887
888 if (rhs == nullptr)
889 return parseError ("Expected symbol or function after \".\"");
890
891 if (identifier == "this")
892 return rhs;
893
894 return *new DotOperator (new SymbolTerm (identifier), rhs);
895 }
896
897 // just a symbol..
898 jassert (identifier.trim() == identifier);
899 return *new SymbolTerm (identifier);
900 }
901
902 return {};
903 }
904
905 TermPtr readParenthesisedExpression()
906 {
907 if (! readOperator ("("))
908 return {};
909
910 auto e = readExpression();
911
912 if (e == nullptr || ! readOperator (")"))
913 return {};
914
915 return e;
916 }
917
918 JUCE_DECLARE_NON_COPYABLE (Parser)
919 };
920};
921
922//==============================================================================
924 : term (new Expression::Helpers::Constant (0, false))
925{
926}
927
931
932Expression::Expression (Term* t) : term (t)
933{
934 jassert (term != nullptr);
935}
936
938 : term (new Expression::Helpers::Constant (constant, false))
939{
940}
941
943 : term (other.term)
944{
945}
946
948{
949 term = other.term;
950 return *this;
951}
952
954 : term (std::move (other.term))
955{
956}
957
959{
960 term = std::move (other.term);
961 return *this;
962}
963
965{
966 auto text = stringToParse.getCharPointer();
967 Helpers::Parser parser (text);
968 term = parser.readUpToComma();
969 parseError = parser.error;
970}
971
972Expression Expression::parse (String::CharPointerType& stringToParse, String& parseError)
973{
974 Helpers::Parser parser (stringToParse);
975 Expression e (parser.readUpToComma().get());
976 parseError = parser.error;
977 return e;
978}
979
981{
982 return evaluate (Expression::Scope());
983}
984
985double Expression::evaluate (const Expression::Scope& scope) const
986{
987 String err;
988 return evaluate (scope, err);
989}
990
992{
993 try
994 {
995 return term->resolve (scope, 0)->toDouble();
996 }
998 {
999 evaluationError = e.description;
1000 }
1001
1002 return 0;
1003}
1004
1005Expression Expression::operator+ (const Expression& other) const { return Expression (new Helpers::Add (term, other.term)); }
1006Expression Expression::operator- (const Expression& other) const { return Expression (new Helpers::Subtract (term, other.term)); }
1007Expression Expression::operator* (const Expression& other) const { return Expression (new Helpers::Multiply (term, other.term)); }
1008Expression Expression::operator/ (const Expression& other) const { return Expression (new Helpers::Divide (term, other.term)); }
1009Expression Expression::operator-() const { return Expression (term->negated().get()); }
1010Expression Expression::symbol (const String& symbol) { return Expression (new Helpers::SymbolTerm (symbol)); }
1011
1012Expression Expression::function (const String& functionName, const Array<Expression>& parameters)
1013{
1014 return Expression (new Helpers::Function (functionName, parameters));
1015}
1016
1018{
1019 std::unique_ptr<Term> newTerm (term->clone());
1020
1021 auto termToAdjust = Helpers::findTermToAdjust (newTerm.get(), true);
1022
1023 if (termToAdjust == nullptr)
1024 termToAdjust = Helpers::findTermToAdjust (newTerm.get(), false);
1025
1026 if (termToAdjust == nullptr)
1027 {
1028 newTerm.reset (new Helpers::Add (*newTerm.release(), *new Helpers::Constant (0, false)));
1029 termToAdjust = Helpers::findTermToAdjust (newTerm.get(), false);
1030 }
1031
1032 jassert (termToAdjust != nullptr);
1033
1034 if (const Term* parent = Helpers::findDestinationFor (newTerm.get(), termToAdjust))
1035 {
1036 if (Helpers::TermPtr reverseTerm = parent->createTermToEvaluateInput (scope, termToAdjust, targetValue, newTerm.get()))
1037 termToAdjust->value = Expression (reverseTerm.get()).evaluate (scope);
1038 else
1039 return Expression (targetValue);
1040 }
1041 else
1042 {
1043 termToAdjust->value = targetValue;
1044 }
1045
1046 return Expression (newTerm.release());
1047}
1048
1050{
1051 jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_"));
1052
1053 if (oldSymbol.symbolName == newName)
1054 return *this;
1055
1056 Expression e (term->clone());
1057 e.term->renameSymbol (oldSymbol, newName, scope, 0);
1058 return e;
1059}
1060
1062{
1063 Helpers::SymbolCheckVisitor visitor (symbolToCheck);
1064
1065 try
1066 {
1067 term->visitAllSymbols (visitor, scope, 0);
1068 }
1070 {}
1071
1072 return visitor.wasFound;
1073}
1074
1075void Expression::findReferencedSymbols (Array<Symbol>& results, const Scope& scope) const
1076{
1077 try
1078 {
1079 Helpers::SymbolListVisitor visitor (results);
1080 term->visitAllSymbols (visitor, scope, 0);
1081 }
1083 {}
1084}
1085
1086String Expression::toString() const { return term->toString(); }
1087bool Expression::usesAnySymbols() const { return Helpers::containsAnySymbols (*term); }
1089String Expression::getSymbolOrFunction() const { return term->getName(); }
1090int Expression::getNumInputs() const { return term->getNumInputs(); }
1091Expression Expression::getInput (int index) const { return Expression (term->getInput (index)); }
1092
1093//==============================================================================
1094ReferenceCountedObjectPtr<Expression::Term> Expression::Term::negated()
1095{
1096 return *new Helpers::Negate (*this);
1097}
1098
1099//==============================================================================
1100Expression::Symbol::Symbol (const String& scope, const String& symbol)
1101 : scopeUID (scope), symbolName (symbol)
1102{
1103}
1104
1105bool Expression::Symbol::operator== (const Symbol& other) const noexcept
1106{
1107 return symbolName == other.symbolName && scopeUID == other.scopeUID;
1108}
1109
1110bool Expression::Symbol::operator!= (const Symbol& other) const noexcept
1111{
1112 return ! operator== (other);
1113}
1114
1115//==============================================================================
1116Expression::Scope::Scope() {}
1117Expression::Scope::~Scope() {}
1118
1120{
1121 if (symbol.isNotEmpty())
1122 throw Helpers::EvaluationError ("Unknown symbol: " + symbol);
1123
1124 return Expression();
1125}
1126
1127double Expression::Scope::evaluateFunction (const String& functionName, const double* parameters, int numParams) const
1128{
1129 if (numParams > 0)
1130 {
1131 if (functionName == "min")
1132 {
1133 double v = parameters[0];
1134 for (int i = 1; i < numParams; ++i)
1135 v = jmin (v, parameters[i]);
1136
1137 return v;
1138 }
1139
1140 if (functionName == "max")
1141 {
1142 double v = parameters[0];
1143 for (int i = 1; i < numParams; ++i)
1144 v = jmax (v, parameters[i]);
1145
1146 return v;
1147 }
1148
1149 if (numParams == 1)
1150 {
1151 if (functionName == "sin") return std::sin (parameters[0]);
1152 if (functionName == "cos") return std::cos (parameters[0]);
1153 if (functionName == "tan") return std::tan (parameters[0]);
1154 if (functionName == "abs") return std::abs (parameters[0]);
1155 }
1156 }
1157
1158 throw Helpers::EvaluationError ("Unknown function: \"" + functionName + "\"");
1159}
1160
1162{
1163 throw Helpers::EvaluationError ("Unknown symbol: " + scopeName);
1164}
1165
1167{
1168 return {};
1169}
1170
1171} // namespace juce
static double readDoubleValue(CharPointerType &text) noexcept
virtual Expression getSymbolValue(const String &symbol) const
virtual void visitRelativeScope(const String &scopeName, Visitor &visitor) const
virtual String getScopeUID() const
virtual double evaluateFunction(const String &functionName, const double *parameters, int numParameters) const
Expression operator*(const Expression &) const
Expression adjustedToGiveNewResult(double targetValue, const Scope &scope) const
void findReferencedSymbols(Array< Symbol > &results, const Scope &scope) const
Expression operator+(const Expression &) const
static Expression function(const String &functionName, const Array< Expression > &parameters)
String getSymbolOrFunction() const
bool usesAnySymbols() const
Expression withRenamedSymbol(const Symbol &oldSymbol, const String &newName, const Scope &scope) const
Expression operator/(const Expression &) const
Type getType() const noexcept
double evaluate() const
static Expression parse(String::CharPointerType &stringToParse, String &parseError)
Expression getInput(int index) const
bool referencesSymbol(const Symbol &symbol, const Scope &scope) const
static Expression symbol(const String &symbol)
Expression operator-() const
String toString() const
Expression & operator=(const Expression &)
String toLowerCase() const
static String charToString(juce_wchar character)
bool containsOnly(StringRef charactersItMightContain) const noexcept