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
58 for (
int i = getNumInputs(); --i >= 0;)
66 virtual void useSymbol (
const Symbol&) = 0;
71 for (
int i = getNumInputs(); --i >= 0;)
76 JUCE_DECLARE_NON_COPYABLE (
Term)
85 static void checkRecursionDepth (
int depth)
100 DBG (
"Expression::EvaluationError: " + description);
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);
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;
165 s <<
'(' << left->toString() <<
')';
167 s = left->toString();
172 s <<
'(' << right->toString() <<
')';
174 s << right->toString();
184 jassert (input == left || input == right);
185 if (input != left && input != right)
188 if (
auto dest = findDestinationFor (
topLevelTerm,
this))
207 Type getType()
const noexcept {
return symbolType; }
209 String toString()
const {
return symbol; }
210 String getName()
const {
return symbol; }
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; }
264 return *
new Constant (result,
false);
269 for (
int i = 0; i < parameters.
size(); ++i)
278 if (parameters.
size() == 0)
279 return functionName +
"()";
281 String s (functionName +
" (");
283 for (
int i = 0; i < parameters.
size(); ++i)
287 if (i < parameters.
size() - 1)
295 const String functionName;
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; }
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)
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; }
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()); }
424 String getName()
const {
return "-"; }
425 TermPtr negated() {
return input; }
430 jassert (
t == input);
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 <<
" + "; }
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 <<
" - "; }
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; }
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; }
551 JUCE_DECLARE_NON_COPYABLE (
Divide)
561 for (
int i =
topLevel->getNumInputs(); --i >= 0;)
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);
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;
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';
700 bool readOperator (
const char*
ops,
char*
const opType =
nullptr)
noexcept
720 bool readIdentifier (
String& identifier)
noexcept
726 if (
t.isLetter() || *
t ==
'_')
731 while (
t.isLetterOrDigit() || *
t ==
'_')
748 Term* readNumber()
noexcept
752 bool isResolutionTarget = (*
t ==
'@');
754 if (isResolutionTarget)
757 t =
t.findEndOfWhitespace();
764 t =
t.findEndOfWhitespace();
767 if (isDecimalDigit (*
t) || (*
t ==
'.' && isDecimalDigit (
t[1])))
775 auto lhs = readMultiplyOrDivideExpression();
778 while (lhs !=
nullptr && readOperator (
"+-", &
opType))
780 auto rhs = readMultiplyOrDivideExpression();
786 lhs = *
new Add (lhs, rhs);
794 TermPtr readMultiplyOrDivideExpression()
796 auto lhs = readUnaryExpression();
799 while (lhs !=
nullptr && readOperator (
"*/", &
opType))
801 TermPtr rhs (readUnaryExpression());
809 lhs = *
new Divide (lhs, rhs);
818 if (readOperator (
"+-", &
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();
1019 std::unique_ptr<Term>
newTerm (term->clone());
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];
1135 v = jmin (
v, parameters[i]);
1140 if (functionName ==
"max")
1142 double v = parameters[0];
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.
int size() const noexcept
Returns the current number of elements in the array.
ElementType & getReference(int index) const noexcept
Returns a direct reference to one of the elements in the array, without checking the index passed in.
bool addIfNotAlreadyThere(ParameterType newElement)
Appends a new element at the end of the array as long as the array doesn't already contain it.
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.
Adds reference-counting to an object.
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.