diff --git a/apps/regression/regression_context.cpp b/apps/regression/regression_context.cpp index 4e28c0769..da8443c20 100644 --- a/apps/regression/regression_context.cpp +++ b/apps/regression/regression_context.cpp @@ -9,8 +9,8 @@ using namespace Shared; namespace Regression { -const Expression RegressionContext::expressionForSymbol(const Symbol & symbol) { - if (Symbol::isRegressionSymbol(symbol.name())) { +const Expression RegressionContext::expressionForSymbol(const SymbolAbstract & symbol) { + if (symbol.type() == ExpressionNode::Type::Symbol && Symbol::isRegressionSymbol(symbol.name())) { const char * seriesName = symbol.name(); assert(strlen(seriesName) == 2); @@ -23,9 +23,9 @@ const Expression RegressionContext::expressionForSymbol(const Symbol & symbol) { assert(m_seriesPairIndex >= 0); assert(m_seriesPairIndex < m_store->numberOfPairsOfSeries(series)); return Float(m_store->get(series, storeI, m_seriesPairIndex)); - } else { - return m_parentContext->expressionForSymbol(symbol); } +} else { + return m_parentContext->expressionForSymbol(symbol); } } diff --git a/apps/sequence/cache_context.cpp b/apps/sequence/cache_context.cpp index 1e284a751..878e6d82b 100644 --- a/apps/sequence/cache_context.cpp +++ b/apps/sequence/cache_context.cpp @@ -15,10 +15,12 @@ CacheContext::CacheContext(Context * parentContext) : } template -const Expression CacheContext::expressionForSymbol(const Symbol & symbol) { +const Expression CacheContext::expressionForSymbol(const SymbolAbstract & symbol) { // [u|v](n(+1)?) - if ((symbol.name()[0] == SequenceStore::k_sequenceNames[0][0] || symbol.name()[0] == SequenceStore::k_sequenceNames[1][0]) - && (strcmp(symbol.name()+1, "(n)") == 0 || strcmp(symbol.name()+1, "(n+1)") == 0)) { + if (symbol.type() == ExpressionNode::Type::Symbol + && (symbol.name()[0] == SequenceStore::k_sequenceNames[0][0] || symbol.name()[0] == SequenceStore::k_sequenceNames[1][0]) + && (strcmp(symbol.name()+1, "(n)") == 0 || strcmp(symbol.name()+1, "(n+1)") == 0)) + { return Float(m_values[nameIndexForSymbol(symbol)][rankIndexForSymbol(symbol)]); } return VariableContext::expressionForSymbol(symbol); diff --git a/apps/statistics/statistics_context.cpp b/apps/statistics/statistics_context.cpp index 88f351610..46f744c00 100644 --- a/apps/statistics/statistics_context.cpp +++ b/apps/statistics/statistics_context.cpp @@ -9,8 +9,8 @@ using namespace Shared; namespace Statistics { -const Expression StatisticsContext::expressionForSymbol(const Symbol & symbol) { - if (Symbol::isSeriesSymbol(symbol.name())) { +const Expression StatisticsContext::expressionForSymbol(const SymbolAbstract & symbol) { + if (symbol.type() == ExpressionNode::Type::Symbol && Symbol::isSeriesSymbol(symbol.name())) { const char * seriesName = symbol.name(); assert(strlen(seriesName) == 2); diff --git a/poincare/Makefile b/poincare/Makefile index 9c06e04c4..cea4501fe 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -120,6 +120,7 @@ objs += $(addprefix poincare/src/,\ subtraction.o\ sum.o\ symbol.o\ + symbol_abstract.o\ tangent.o\ tree_handle.o\ tree_node.o\ diff --git a/poincare/include/poincare/context.h b/poincare/include/poincare/context.h index c53b34de9..41cb9a0ad 100644 --- a/poincare/include/poincare/context.h +++ b/poincare/include/poincare/context.h @@ -4,11 +4,12 @@ namespace Poincare { class Expression; -class Symbol; +class SymbolAbstract; +class Symbol; //TODO remove? class Context { public: - virtual const Expression expressionForSymbol(const Symbol & symbol) = 0; + virtual const Expression expressionForSymbol(const SymbolAbstract & symbol) = 0; virtual void setExpressionForSymbol(const Expression & expression, const Symbol & symbol, Context & context) = 0; }; diff --git a/poincare/include/poincare/function.h b/poincare/include/poincare/function.h index 06c5e30ed..47d51e848 100644 --- a/poincare/include/poincare/function.h +++ b/poincare/include/poincare/function.h @@ -1,14 +1,18 @@ #ifndef POINCARE_FUNCTION_H #define POINCARE_FUNCTION_H -#include +#include #include namespace Poincare { -class FunctionNode : public SymbolNode { +class FunctionNode : public SymbolAbstractNode { public: + // SymbolAbstractNode + const char * name() const override { return m_name; } + // TreeNode + void initToMatchSize(size_t goalSize) override; size_t size() const override; int numberOfChildren() const override { return 1; } //TODO allow any number of children? Needs templating #if POINCARE_TREE_LOG @@ -19,6 +23,7 @@ public: // Properties Type type() const override { return Type::Function; } + Sign sign() const override { return Sign::Unknown; } Expression replaceSymbolWithExpression(const Symbol & symbol, const Expression & expression) override; int polynomialDegree(Context & context, const char * symbolName) const override; int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const override; @@ -26,6 +31,8 @@ public: float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const override; private: + char m_name[0]; // MUST be the last member variable + VariableContext xContext(Context & parentContext) const; // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; @@ -37,10 +44,10 @@ private: Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override; }; -class Function : public Symbol { +class Function : public SymbolAbstract { public: explicit Function(const char * name); - Function(const FunctionNode * n) : Symbol(n) {} + Function(const FunctionNode * n) : SymbolAbstract(n) {} explicit Function(const char * name, Expression operand) : Function(name) { replaceChildAtIndexInPlace(0, operand); } diff --git a/poincare/include/poincare/global_context.h b/poincare/include/poincare/global_context.h index b0840d12b..a0eefef1b 100644 --- a/poincare/include/poincare/global_context.h +++ b/poincare/include/poincare/global_context.h @@ -19,7 +19,7 @@ public: static constexpr char expExtension[] = "exp"; /* The expression recorded in global context is already a expression. * Otherwise, we would need the context and the angle unit to evaluate it */ - const Expression expressionForSymbol(const Symbol & symbol) override; + const Expression expressionForSymbol(const SymbolAbstract & symbol) override; void setExpressionForSymbol(const Expression & expression, const Symbol & symbol, Context & context) override; static Ion::Storage::Record RecordWithName(const char * name); static bool storageMemoryFull(); diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index 0c55fbd8e..201609acb 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -1,30 +1,16 @@ #ifndef POINCARE_SYMBOL_H #define POINCARE_SYMBOL_H -#include +#include namespace Poincare { -/* TODO: should we keep the size of SymbolNode as a member to speed up TreePool - * scan? */ - -/* Symbol Node contains an empty member variable "m_name", which is used to get - * the string that follows. This means that a SymbolNode's size is sizeo - * (SymbolNode) + strlen(string). - * - * Seen by TreePool: |SymbolNode | - * SymbolNode layout: |ExpressionNode|m_name| | - * Memory content: |ExpressionNode|S |y|m|b|o|l|N|a|m|e|0| - * */ - -class SymbolNode final : public ExpressionNode { - friend class Store; +class SymbolNode final : public SymbolAbstractNode { public: - void initToMatchSize(size_t goalSize) override; - void setName(const char * name, int length) { strlcpy(m_name, name, length+1); } - const char * name() const { return m_name; } + const char * name() const override { return m_name; } // TreeNode + void initToMatchSize(size_t goalSize) override; size_t size() const override; int numberOfChildren() const override { return 0; } #if POINCARE_TREE_LOG @@ -45,10 +31,7 @@ public: int getVariables(Context & context, isVariableTest isVariable, char * variables, int maxSizeVariable) const override; float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const override; - /* Comparison */ - int simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const override; - -/* Layout */ + /* Layout */ Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; @@ -64,14 +47,14 @@ public: bool isPi() const { return isSymbolChar(Ion::Charset::SmallPi); } bool isExponential() const { return isSymbolChar(Ion::Charset::Exponential); } bool isIComplex() const { return isSymbolChar(Ion::Charset::IComplex); } -protected: - char m_name[0]; // MUST be the last member variable private: + char m_name[0]; // MUST be the last member variable + template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; bool isSymbolChar(char c) const { const char symbolName[2] = {c, 0}; return strcmp(m_name, symbolName) == 0; } }; -class Symbol final : public Expression { +class Symbol final : public SymbolAbstract { friend class Expression; friend class Store; public: @@ -83,7 +66,7 @@ public: }; Symbol(const char * name, int length); Symbol(char name); - Symbol(const SymbolNode * node) : Expression(node) {} + Symbol(const SymbolNode * node) : SymbolAbstract(node) {} // Symbol properties bool isPi() const { return node()->isPi(); } @@ -97,9 +80,6 @@ public: Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, bool replaceSymbols = true); Expression replaceSymbolWithExpression(const Symbol & symbol, const Expression & expression); int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const; - - // Symbol - const char * name() const { return node()->name(); } private: SymbolNode * node() const { return static_cast(Expression::node()); } }; diff --git a/poincare/include/poincare/symbol_abstract.h b/poincare/include/poincare/symbol_abstract.h new file mode 100644 index 000000000..32812f698 --- /dev/null +++ b/poincare/include/poincare/symbol_abstract.h @@ -0,0 +1,53 @@ +#ifndef POINCARE_ABSTRACT_SYMBOL_H +#define POINCARE_ABSTRACT_SYMBOL_H + +#include + +namespace Poincare { + +/* TODO: should we keep the size of SymbolAbstractNode as a member to speed up + * TreePool scan? */ + +/* SymbolAbstract derived classes must have a char[0] member variable as their + * last member variable, so they can access their name, which is the string that + * follows the node in memory. + * This means that a DerivedSymbolNode's size is sizeof(DerivedSymbolNode) + + * strlen(string). + * + * For instance: + * Seen by TreePool: |SymbolNode | + * SymbolNode layout: |ExpressionNode|m_name| | + * Memory content: |ExpressionNode|S |y|m|b|o|l|N|a|m|e|0| + * */ + +class SymbolAbstractNode : public ExpressionNode { + friend class Store; +public: + virtual const char * name() const = 0; + void setName(const char * newName, int length); + + // ExpressionNode + int simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const override; + + // TreeNode +#if POINCARE_TREE_LOG + virtual void logNodeName(std::ostream & stream) const override { + stream << "SymbolAbstract"; + } +#endif + +protected: + void initName(size_t nameSize); +}; + +class SymbolAbstract : public Expression { +public: + const char * name() const { return node()->name(); } +protected: + SymbolAbstract(const SymbolAbstractNode * node) : Expression(node) {} + SymbolAbstractNode * node() const { return static_cast(Expression::node()); } +}; + +} + +#endif diff --git a/poincare/include/poincare/variable_context.h b/poincare/include/poincare/variable_context.h index 435928ab7..d4d8da0aa 100644 --- a/poincare/include/poincare/variable_context.h +++ b/poincare/include/poincare/variable_context.h @@ -14,7 +14,7 @@ public: // Context void setExpressionForSymbol(const Expression & expression, const Symbol & symbol, Context & context) override; - const Expression expressionForSymbol(const Symbol & symbol) override; + const Expression expressionForSymbol(const SymbolAbstract & symbol) override; private: const char * m_name; diff --git a/poincare/src/function.cpp b/poincare/src/function.cpp index ef0c6fc35..e2984e2ff 100644 --- a/poincare/src/function.cpp +++ b/poincare/src/function.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace Poincare { @@ -15,6 +16,15 @@ static size_t NodeSize(size_t nameLength) { return Helpers::AlignedSize(sizeof(FunctionNode)+nameLength+1, 4); } +void FunctionNode::initToMatchSize(size_t goalSize) { + // TODO Factorize with symbol + assert(goalSize != sizeof(FunctionNode)); + assert(goalSize > sizeof(FunctionNode)); + size_t nameSize = goalSize - sizeof(FunctionNode); + SymbolAbstractNode::initName(nameSize); + assert(size() == goalSize); +} + size_t FunctionNode::size() const { return NodeSize(strlen(m_name)); } @@ -109,7 +119,7 @@ Expression Function::replaceSymbolWithExpression(const Symbol & symbol, const Ex if (symbol.type() == ExpressionNode::Type::Function && strcmp(name(), symbol.name()) == 0) { Expression value = expression.clone(); // Replace the unknown in the new expression by the function's child - const char x[2] = {SpecialSymbols::UnknownX, 0}; + const char x[2] = {Symbol::SpecialSymbols::UnknownX, 0}; Symbol xSymbol = Symbol(x, 1); Expression xValue = childAtIndex(0); value.replaceSymbolWithExpression(xSymbol, xValue); diff --git a/poincare/src/global_context.cpp b/poincare/src/global_context.cpp index 3f2940baf..8640562fe 100644 --- a/poincare/src/global_context.cpp +++ b/poincare/src/global_context.cpp @@ -22,13 +22,16 @@ bool GlobalContext::storageMemoryFull() { /**/ -const Expression GlobalContext::expressionForSymbol(const Symbol & symbol) { - // Constant symbols - if (symbol.isPi()) { - return Float(M_PI); - } - if (symbol.isExponential()) { - return Float(M_E); +const Expression GlobalContext::expressionForSymbol(const SymbolAbstract & symbol) { + if (symbol.type() == ExpressionNode::Type::Symbol) { + // Constant symbols + Symbol s = static_cast(symbol); + if (s.isPi()) { + return Float(M_PI); + } + if (s.isExponential()) { + return Float(M_E); + } } // Look up the file system for symbol return Expression::ExpressionFromRecord(RecordWithName(symbol.name())); diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index ffb4f9224..fcee778f9 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -17,11 +17,9 @@ constexpr char Symbol::k_ans[]; void SymbolNode::initToMatchSize(size_t goalSize) { assert(goalSize != sizeof(SymbolNode)); - int nameSize = goalSize - sizeof(SymbolNode); - for (int i = 0; i < nameSize - 1; i++) { - m_name[i] = 'a'; - } - m_name[nameSize-1] = 0; + assert(goalSize > sizeof(SymbolNode)); + size_t nameSize = goalSize - sizeof(SymbolNode); + SymbolAbstractNode::initName(nameSize); assert(size() == goalSize); } @@ -99,11 +97,6 @@ float SymbolNode::characteristicXRange(Context & context, Preferences::AngleUnit return 0.0; } -int SymbolNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { - assert(e->type() == Type::Symbol); - return strcmp(m_name, static_cast(e)->name()); -} - Layout SymbolNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { if (m_name[0] == Symbol::SpecialSymbols::UnknownX) { assert(m_name[1] == 0); @@ -172,7 +165,7 @@ Evaluation SymbolNode::templatedApproximate(Context& context, Preferences::An return e.approximateToEvaluation(context, angleUnit); } -Symbol::Symbol(const char * name, int length) : Expression(TreePool::sharedPool()->createTreeNode(NodeSize(length))) { +Symbol::Symbol(const char * name, int length) : SymbolAbstract(TreePool::sharedPool()->createTreeNode(NodeSize(length))) { node()->setName(name, length); } diff --git a/poincare/src/symbol_abstract.cpp b/poincare/src/symbol_abstract.cpp new file mode 100644 index 000000000..f809dc1a0 --- /dev/null +++ b/poincare/src/symbol_abstract.cpp @@ -0,0 +1,23 @@ +#include +#include + +namespace Poincare { + +void SymbolAbstractNode::setName(const char * newName, int length) { + strlcpy(const_cast(name()), newName, length+1); +} + +int SymbolAbstractNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { + assert(e->type() == Type::Symbol || e->type() == Type::Function); + return strcmp(name(), static_cast(e)->name()); +} + +void SymbolAbstractNode::initName(size_t nameSize) { + char * modifiableName = const_cast(name()); + for (int i = 0; i < nameSize - 1; i++) { + modifiableName[i] = 'a'; + } + modifiableName[nameSize-1] = 0; +} + +} diff --git a/poincare/src/variable_context.cpp b/poincare/src/variable_context.cpp index ab364d15d..65ca57eec 100644 --- a/poincare/src/variable_context.cpp +++ b/poincare/src/variable_context.cpp @@ -29,7 +29,7 @@ void VariableContext::setExpressionForSymbol(const Expression & expression, cons } } -const Expression VariableContext::expressionForSymbol(const Symbol & symbol) { +const Expression VariableContext::expressionForSymbol(const SymbolAbstract & symbol) { if (strcmp(symbol.name(), m_name) == 0) { return m_value; } else {