32 virtual Type getType()
const noexcept = 0;
33 virtual Term* clone()
const = 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; }
44 double ,
Term* )
const
50 virtual String getName()
const
56 virtual void renameSymbol (
const Symbol& oldSymbol,
const String& newName,
const Scope& scope,
int recursionDepth)
58 for (
int i = getNumInputs(); --i >= 0;)
59 getInput (i)->renameSymbol (oldSymbol, newName, scope, recursionDepth);
66 virtual void useSymbol (
const Symbol&) = 0;
69 virtual void visitAllSymbols (
SymbolVisitor& visitor,
const Scope& scope,
int recursionDepth)
71 for (
int i = getNumInputs(); --i >= 0;)
72 getInput(i)->visitAllSymbols (visitor, scope, recursionDepth);
76 JUCE_DECLARE_NON_COPYABLE (
Term)
85 static void checkRecursionDepth (
int depth)
100 DBG (
"Expression::EvaluationError: " + description);
110 Constant (
double val,
bool resolutionTarget)
111 : value (val), isResolutionTarget (resolutionTarget) {}
113 Type getType()
const noexcept {
return constantType; }
114 Term* clone()
const {
return new Constant (value, isResolutionTarget); }
116 double toDouble()
const {
return value; }
117 TermPtr negated() {
return *
new Constant (-value, isResolutionTarget); }
122 if (isResolutionTarget)
129 bool isResolutionTarget;
138 jassert (left !=
nullptr && right !=
nullptr);
141 int getInputIndexFor (
const Term* possibleInput)
const
143 return possibleInput == left ? 0 : (possibleInput == right ? 1 : -1);
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); }
150 virtual double performFunction (
double left,
double right)
const = 0;
151 virtual void writeOperator (
String& dest)
const = 0;
153 TermPtr resolve (
const Scope& scope,
int recursionDepth)
155 return *
new Constant (performFunction (left ->resolve (scope, recursionDepth)->toDouble(),
156 right->resolve (scope, recursionDepth)->toDouble()),
false);
162 auto ourPrecendence = getOperatorPrecedence();
164 if (left->getOperatorPrecedence() > ourPrecendence)
165 s <<
'(' << left->toString() <<
')';
167 s = left->toString();
171 if (right->getOperatorPrecedence() >= ourPrecendence)
172 s <<
'(' << right->toString() <<
')';
174 s << right->toString();
182 TermPtr createDestinationTerm (
const Scope& scope,
const Term* input,
double overallTarget,
Term* topLevelTerm)
const
184 jassert (input == left || input == right);
185 if (input != left && input != right)
188 if (
auto dest = findDestinationFor (topLevelTerm,
this))
189 return dest->createTermToEvaluateInput (scope,
this, overallTarget, topLevelTerm);
191 return *
new Constant (overallTarget,
false);
201 TermPtr resolve (
const Scope& scope,
int recursionDepth)
203 checkRecursionDepth (recursionDepth);
204 return scope.
getSymbolValue (symbol).term->resolve (scope, recursionDepth + 1);
207 Type getType()
const noexcept {
return symbolType; }
209 String toString()
const {
return symbol; }
210 String getName()
const {
return symbol; }
214 checkRecursionDepth (recursionDepth);
216 scope.
getSymbolValue (symbol).term->visitAllSymbols (visitor, scope, recursionDepth + 1);
219 void renameSymbol (
const Symbol& oldSymbol,
const String& newName,
const Scope& scope,
int )
235 : functionName (name), parameters (params)
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; }
244 TermPtr resolve (
const Scope& scope,
int recursionDepth)
246 checkRecursionDepth (recursionDepth);
248 auto numParams = parameters.size();
254 for (
int i = 0; i < numParams; ++i)
255 params[i] = parameters.getReference(i).term->resolve (scope, recursionDepth + 1)->toDouble();
264 return *
new Constant (result,
false);
267 int getInputIndexFor (
const Term* possibleInput)
const
269 for (
int i = 0; i < parameters.size(); ++i)
270 if (parameters.getReference(i).term == possibleInput)
278 if (parameters.size() == 0)
279 return functionName +
"()";
281 String s (functionName +
" (");
283 for (
int i = 0; i < parameters.size(); ++i)
285 s << parameters.getReference(i).term->toString();
287 if (i < parameters.size() - 1)
295 const String functionName;
305 TermPtr resolve (
const Scope& scope,
int recursionDepth)
307 checkRecursionDepth (recursionDepth);
309 EvaluationVisitor visitor (right, recursionDepth + 1);
311 return visitor.output;
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; }
322 checkRecursionDepth (recursionDepth);
325 SymbolVisitingVisitor v (right, visitor, recursionDepth + 1);
334 void renameSymbol (
const Symbol& oldSymbol,
const String& newName,
const Scope& scope,
int recursionDepth)
336 checkRecursionDepth (recursionDepth);
337 getSymbol()->renameSymbol (oldSymbol, newName, scope, recursionDepth);
339 SymbolRenamingVisitor visitor (right, oldSymbol, newName, recursionDepth + 1);
353 EvaluationVisitor (
const TermPtr& t,
const int recursion)
354 : input (t), output (t), recursionCount (recursion) {}
356 void visit (
const Scope& scope) { output = input->resolve (scope, recursionCount); }
360 const int recursionCount;
363 JUCE_DECLARE_NON_COPYABLE (EvaluationVisitor)
370 : input (t), visitor (v), recursionCount (recursion) {}
372 void visit (
const Scope& scope) { input->visitAllSymbols (visitor, scope, recursionCount); }
377 const int recursionCount;
379 JUCE_DECLARE_NON_COPYABLE (SymbolVisitingVisitor)
386 : input (t),
symbol (symbol_), newName (newName_), recursionCount (recursionCount_) {}
388 void visit (
const Scope& scope) { input->renameSymbol (
symbol, newName, scope, recursionCount); }
394 const int recursionCount;
396 JUCE_DECLARE_NON_COPYABLE (SymbolRenamingVisitor)
410 jassert (t !=
nullptr);
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()); }
419 TermPtr resolve (
const Scope& scope,
int recursionDepth)
421 return *
new Constant (-input->resolve (scope, recursionDepth)->toDouble(),
false);
424 String getName()
const {
return "-"; }
425 TermPtr negated() {
return input; }
427 TermPtr createTermToEvaluateInput (
const Scope& scope,
const Term* t,
double overallTarget,
Term* topLevelTerm)
const
430 jassert (t == input);
432 const Term*
const dest = findDestinationFor (topLevelTerm,
this);
435 : dest->createTermToEvaluateInput (scope,
this, overallTarget, topLevelTerm));
440 if (input->getOperatorPrecedence() > 0)
441 return "-(" + input->toString() +
")";
443 return "-" + input->toString();
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 <<
" + "; }
462 TermPtr createTermToEvaluateInput (
const Scope& scope,
const Term* input,
double overallTarget,
Term* topLevelTerm)
const
464 if (
auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm))
465 return *
new Subtract (newDest, *(input == left ? right : left)->clone());
471 JUCE_DECLARE_NON_COPYABLE (
Add)
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 <<
" - "; }
486 TermPtr createTermToEvaluateInput (
const Scope& scope,
const Term* input,
double overallTarget,
Term* topLevelTerm)
const
488 if (
auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm))
491 return *
new Add (*newDest, *right->clone());
493 return *
new Subtract (*left->clone(), *newDest);
500 JUCE_DECLARE_NON_COPYABLE (
Subtract)
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; }
515 TermPtr createTermToEvaluateInput (
const Scope& scope,
const Term* input,
double overallTarget,
Term* topLevelTerm)
const
517 if (
auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm))
518 return *
new Divide (newDest, *(input == left ? right : left)->clone());
523 JUCE_DECLARE_NON_COPYABLE (
Multiply)
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; }
538 TermPtr createTermToEvaluateInput (
const Scope& scope,
const Term* input,
double overallTarget,
Term* topLevelTerm)
const
540 auto newDest = createDestinationTerm (scope, input, overallTarget, topLevelTerm);
542 if (newDest ==
nullptr)
546 return *
new Multiply (*newDest, *right->clone());
548 return *
new Divide (*left->clone(), *newDest);
551 JUCE_DECLARE_NON_COPYABLE (
Divide)
555 static Term* findDestinationFor (
Term*
const topLevel,
const Term*
const inputTerm)
557 const int inputIndex = topLevel->getInputIndexFor (inputTerm);
561 for (
int i = topLevel->getNumInputs(); --i >= 0;)
563 Term*
const t = findDestinationFor (topLevel->getInput (i), inputTerm);
572 static Constant* findTermToAdjust (Term*
const term,
const bool mustBeFlagged)
574 jassert (term !=
nullptr);
576 if (term->getType() == constantType)
578 Constant*
const c =
static_cast<Constant*
> (term);
579 if (c->isResolutionTarget || ! mustBeFlagged)
583 if (term->getType() == functionType)
586 const int numIns = term->getNumInputs();
588 for (
int i = 0; i < numIns; ++i)
590 Term*
const input = term->getInput (i);
592 if (input->getType() == constantType)
594 Constant*
const c =
static_cast<Constant*
> (input);
596 if (c->isResolutionTarget || ! mustBeFlagged)
601 for (
int i = 0; i < numIns; ++i)
602 if (
auto c = findTermToAdjust (term->getInput (i), mustBeFlagged))
608 static bool containsAnySymbols (
const Term& t)
610 if (t.getType() == Expression::symbolType)
613 for (
int i = t.getNumInputs(); --i >= 0;)
614 if (containsAnySymbols (*t.getInput (i)))
625 void useSymbol (
const Symbol& s) { wasFound = wasFound || s == symbol; }
627 bool wasFound =
false;
640 void useSymbol (
const Symbol& s) { list.addIfNotAlreadyThere (s); }
662 auto e = readExpression();
664 if (e ==
nullptr || ((! readOperator (
",")) && ! text.
isEmpty()))
665 return parseError (
"Syntax error: \"" +
String (text) +
"\"");
684 static inline bool isDecimalDigit (
const juce_wchar c)
noexcept
686 return c >=
'0' && c <=
'9';
689 bool readChar (
const juce_wchar required)
noexcept
691 if (*text == required)
700 bool readOperator (
const char* ops,
char*
const opType =
nullptr)
noexcept
706 if (readChar ((juce_wchar) (uint8) *ops))
708 if (opType !=
nullptr)
720 bool readIdentifier (
String& identifier)
noexcept
726 if (t.isLetter() || *t ==
'_')
731 while (t.isLetterOrDigit() || *t ==
'_')
740 identifier =
String (text, (
size_t) numChars);
748 Term* readNumber()
noexcept
752 bool isResolutionTarget = (*t ==
'@');
754 if (isResolutionTarget)
767 if (isDecimalDigit (*t) || (*t ==
'.' && isDecimalDigit (t[1])))
775 auto lhs = readMultiplyOrDivideExpression();
778 while (lhs !=
nullptr && readOperator (
"+-", &opType))
780 auto rhs = readMultiplyOrDivideExpression();
783 return parseError (
"Expected expression after \"" +
String::charToString ((juce_wchar) (uint8) opType) +
"\"");
786 lhs = *
new Add (lhs, rhs);
794 TermPtr readMultiplyOrDivideExpression()
796 auto lhs = readUnaryExpression();
799 while (lhs !=
nullptr && readOperator (
"*/", &opType))
801 TermPtr rhs (readUnaryExpression());
804 return parseError (
"Expected expression after \"" +
String::charToString ((juce_wchar) (uint8) opType) +
"\"");
809 lhs = *
new Divide (lhs, rhs);
818 if (readOperator (
"+-", &opType))
820 TermPtr e (readUnaryExpression());
823 return parseError (
"Expected expression after \"" +
String::charToString ((juce_wchar) (uint8) opType) +
"\"");
831 return readPrimaryExpression();
834 TermPtr readPrimaryExpression()
836 if (
auto e = readParenthesisedExpression())
839 if (
auto e = readNumber())
842 return readSymbolOrFunction();
849 if (readIdentifier (identifier))
851 if (readOperator (
"("))
854 std::unique_ptr<Term> func (f);
856 auto param = readExpression();
858 if (param ==
nullptr)
860 if (readOperator (
")"))
861 return TermPtr (func.release());
863 return parseError (
"Expected parameters after \"" + identifier +
" (\"");
868 while (readOperator (
","))
870 param = readExpression();
872 if (param ==
nullptr)
873 return parseError (
"Expected expression after \",\"");
878 if (readOperator (
")"))
879 return TermPtr (func.release());
881 return parseError (
"Expected \")\"");
884 if (readOperator (
"."))
886 TermPtr rhs (readSymbolOrFunction());
889 return parseError (
"Expected symbol or function after \".\"");
891 if (identifier ==
"this")
898 jassert (identifier.
trim() == identifier);
905 TermPtr readParenthesisedExpression()
907 if (! readOperator (
"("))
910 auto e = readExpression();
912 if (e ==
nullptr || ! readOperator (
")"))
918 JUCE_DECLARE_NON_COPYABLE (
Parser)
934 jassert (term !=
nullptr);
954 : term (std::move (other.term))
960 term = std::move (other.term);
968 term = parser.readUpToComma();
969 parseError = parser.error;
976 parseError = parser.error;
995 return term->resolve (scope, 0)->toDouble();
999 evaluationError = e.description;
1019 std::unique_ptr<Term> newTerm (term->clone());
1021 auto termToAdjust = Helpers::findTermToAdjust (newTerm.get(),
true);
1023 if (termToAdjust ==
nullptr)
1024 termToAdjust = Helpers::findTermToAdjust (newTerm.get(),
false);
1026 if (termToAdjust ==
nullptr)
1029 termToAdjust = Helpers::findTermToAdjust (newTerm.get(),
false);
1032 jassert (termToAdjust !=
nullptr);
1034 if (
const Term* parent = Helpers::findDestinationFor (newTerm.get(), termToAdjust))
1036 if (
Helpers::TermPtr reverseTerm = parent->createTermToEvaluateInput (scope, termToAdjust, targetValue, newTerm.get()))
1037 termToAdjust->value =
Expression (reverseTerm.get()).evaluate (scope);
1043 termToAdjust->value = targetValue;
1057 e.term->renameSymbol (oldSymbol, newName, scope, 0);
1067 term->visitAllSymbols (visitor, scope, 0);
1072 return visitor.wasFound;
1080 term->visitAllSymbols (visitor, scope, 0);
1096 return *
new Helpers::Negate (*
this);
1100Expression::Symbol::Symbol (
const String& scope,
const String&
symbol)
1101 : scopeUID (scope), symbolName (
symbol)
1105bool Expression::Symbol::operator== (
const Symbol& other)
const noexcept
1107 return symbolName == other.symbolName && scopeUID == other.scopeUID;
1110bool Expression::Symbol::operator!= (
const Symbol& other)
const noexcept
1112 return ! operator== (other);
1116Expression::Scope::Scope() {}
1117Expression::Scope::~Scope() {}
1131 if (functionName ==
"min")
1133 double v = parameters[0];
1134 for (
int i = 1; i < numParams; ++i)
1135 v = jmin (v, parameters[i]);
1140 if (functionName ==
"max")
1142 double v = parameters[0];
1143 for (
int i = 1; i < numParams; ++i)
1144 v = jmax (v, parameters[i]);
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]);
Holds a resizable array of primitive or copy-by-value objects.
Wraps a pointer to a null-terminated UTF-8 character string, and provides various methods to operate ...
bool isEmpty() const noexcept
Returns true if this pointer is pointing to a null character.
CharPointer_UTF8 findEndOfWhitespace() const noexcept
Returns the first non-whitespace character in the string.
static double readDoubleValue(CharPointerType &text) noexcept
Parses a character string to read a floating-point number.
An exception that can be thrown by Expression::evaluate().
Used as a callback by the Scope::visitRelativeScope() method.
When evaluating an Expression object, this class is used to resolve symbols and perform functions tha...
virtual Expression getSymbolValue(const String &symbol) const
Returns the value of a symbol.
virtual void visitRelativeScope(const String &scopeName, Visitor &visitor) const
Creates a Scope object for a named scope, and then calls a visitor to do some kind of processing with...
virtual String getScopeUID() const
Returns some kind of globally unique ID that identifies this scope.
virtual double evaluateFunction(const String &functionName, const double *parameters, int numParameters) const
Executes a named function.
A class for dynamically evaluating simple numeric expressions.
Expression operator*(const Expression &) const
Returns an expression which is a multiplication operation of two existing expressions.
Expression adjustedToGiveNewResult(double targetValue, const Scope &scope) const
Attempts to return an expression which is a copy of this one, but with a constant adjusted to make th...
void findReferencedSymbols(Array< Symbol > &results, const Scope &scope) const
Returns a list of all symbols that may be needed to resolve this expression in the given scope.
Expression operator+(const Expression &) const
Returns an expression which is an addition operation of two existing expressions.
static Expression function(const String &functionName, const Array< Expression > ¶meters)
Returns an Expression which is a function call.
String getSymbolOrFunction() const
If this expression is a symbol, function or operator, this returns its identifier.
Expression()
Creates a simple expression with a value of 0.
bool usesAnySymbols() const
Returns true if this expression contains any symbols.
Expression withRenamedSymbol(const Symbol &oldSymbol, const String &newName, const Scope &scope) const
Returns a copy of this expression in which all instances of a given symbol have been renamed.
Expression operator/(const Expression &) const
Returns an expression which is a division operation of two existing expressions.
Type getType() const noexcept
Returns the type of this expression.
double evaluate() const
Evaluates this expression, without using a Scope.
static Expression parse(String::CharPointerType &stringToParse, String &parseError)
Returns an Expression which parses a string from a character pointer, and updates the pointer to indi...
int getNumInputs() const
Returns the number of inputs to this expression.
Expression getInput(int index) const
Retrieves one of the inputs to this expression.
bool referencesSymbol(const Symbol &symbol, const Scope &scope) const
Returns true if this expression makes use of the specified symbol.
static Expression symbol(const String &symbol)
Returns an Expression which is an identifier reference.
Expression operator-() const
Returns an expression which performs a negation operation on an existing expression.
String toString() const
Returns a string version of the expression.
Expression & operator=(const Expression &)
Copies another expression.
ReferencedType * get() const noexcept
Returns the object that this pointer references.
Adds reference-counting to an object.
CharPointerType getCharPointer() const noexcept
Returns the character pointer currently being used to store this string.
String trim() const
Returns a copy of this string with any whitespace characters removed from the start and end.
bool isEmpty() const noexcept
Returns true if the string contains no characters.
String toLowerCase() const
Returns an lower-case version of this string.
static String charToString(juce_wchar character)
Creates a string from a single character.
bool containsOnly(StringRef charactersItMightContain) const noexcept
Looks for a set of characters in the string.
Represents a symbol that is used in an Expression.
String symbolName
The name of the symbol.
String scopeUID
The unique ID of the Scope that contains this symbol.