diff options
-rw-r--r-- | mtxexpression.cc | 211 | ||||
-rw-r--r-- | mtxexpression.h | 63 |
2 files changed, 264 insertions, 10 deletions
diff --git a/mtxexpression.cc b/mtxexpression.cc index a6bc695..f18e7be 100644 --- a/mtxexpression.cc +++ b/mtxexpression.cc @@ -35,6 +35,18 @@ using namespace std; /* * See mtxexpression.h. */ +struct mtx_function MtxExpression::functions[] = +{ + {"det", MTXEXPRESSION_FUNCTION_DETERMINANT, 1}, + {"inverse", MTXEXPRESSION_FUNCTION_INVERSE, 1}, + {"transpose", MTXEXPRESSION_FUNCTION_TRANSPOSE, 1}, + {"augment", MTXEXPRESSION_FUNCTION_AUGMENT, 2}, + {NULL, MTXEXPRESSION_FUNCTION_NULL} +}; + +/* + * See mtxexpression.h. + */ struct mtx_operator MtxExpression::operators[][6] = { { @@ -107,8 +119,11 @@ MtxExpression::MtxExpression(MtxLanguage *language, MtxInteger(element.c_str()))); } + else if (isVariableName(element)) + expressions.push_back(new MtxSymbol(language, element)); + else - expressions.push_back(new MtxSymbol(language, *lexical_elements.begin())); + throw MtxException("invalid variable name"); function = MTXEXPRESSION_FUNCTION_NULL; } @@ -218,7 +233,8 @@ MtxExpression::MtxExpression(MtxLanguage *language, expressions.push_back(new MtxExpression(language, second_operand)); } - else if (lexical_elements.size() > 1 && lexical_elements.back() == ")") + else if (lexical_elements.size() > 2 && lexical_elements[0] == "(" + && lexical_elements.back() == ")") { lexical_elements.erase(lexical_elements.begin()); lexical_elements.erase(lexical_elements.end() - 1); @@ -252,8 +268,13 @@ MtxExpression::MtxExpression(MtxLanguage *language, integer = dynamic_cast<MtxInteger *>(value); if (integer == NULL) - throw MtxException("matrix cell not integer or " - "rational"); + { + delete symbols[i]; + delete value; + delete[] rationals; + throw MtxException("matrix cell not integer or " + "rational"); + } else rationals[i] = *integer; @@ -273,6 +294,38 @@ MtxExpression::MtxExpression(MtxLanguage *language, function = MTXEXPRESSION_FUNCTION_NULL; } + else if (lexical_elements.size() > 3 && lexical_elements[1] == "(" + && lexical_elements.back() == ")") + { + vector<MtxSymbol *> symbols; + size_t i; + + i = 0; + + while (functions[i].str != NULL) + { + if (lexical_elements[0] == functions[i].str) + break; + + i++; + } + + if (functions[i].str == NULL) + throw MtxException("call to unknown function"); + + function = functions[i].id; + lexical_elements.erase(lexical_elements.begin()); + lexical_elements.erase(lexical_elements.begin()); + lexical_elements.erase(lexical_elements.end() - 1); + parseArguments(lexical_elements, symbols); + + if (symbols.size() != functions[i].argc) + throw MtxException("wrong number of arguments in function call"); + + for (i = 0; i < symbols.size(); i++) + expressions.push_back(symbols[i]); + } + else throw MtxException("syntax error"); } @@ -290,12 +343,12 @@ MtxExpression::~MtxExpression() #define MATRIX_DIMENSIONS_ERROR(symbols) \ { \ - freeMatrix(symbols); \ + freeSymbols(symbols); \ throw MtxException("number of columns mismatch at matrix creation"); \ } void -MtxExpression::freeMatrix(vector<MtxSymbol *> &symbols) const +MtxExpression::freeSymbols(vector<MtxSymbol *> &symbols) const { vector<MtxSymbol *>::iterator iterator; @@ -397,6 +450,63 @@ MtxExpression::parseMatrix(const mtx_expression_t &lexical_elements, MATRIX_DIMENSIONS_ERROR(symbols); } +void +MtxExpression::parseArguments(const mtx_expression_t &lexical_elements, + vector<MtxSymbol *> &symbols) const +{ + mtx_expression_t::const_iterator iterator; + size_t p_level, b_level; + mtx_expression_t tmp; + + if (lexical_elements.empty()) + throw MtxException("syntax error"); + + p_level = 0; + b_level = 0; + + for (iterator = lexical_elements.begin(); + iterator != lexical_elements.end(); + iterator++) + { + if (p_level == 0 && b_level == 0 && *iterator == ",") + { + if (tmp.empty()) + { + freeSymbols(symbols); + throw MtxException("syntax error"); + } + + symbols.push_back(new MtxExpression(language, tmp)); + tmp.clear(); + } + + else + { + if (*iterator == "(") + p_level++; + + else if (*iterator == ")") + p_level--; + + else if (*iterator == "[") + b_level++; + + else if (*iterator == "]") + b_level--; + + tmp.push_back(*iterator); + } + } + + if (tmp.empty()) + { + freeSymbols(symbols); + throw MtxException("syntax error"); + } + + symbols.push_back(new MtxExpression(language, tmp)); +} + bool MtxExpression::isOperator(const string &str) { @@ -422,6 +532,24 @@ MtxExpression::isOperator(const string &str) return false; } +bool +MtxExpression::isVariableName(const string &str) +{ + size_t i; + + if (str.length() == 0) + return false; + + if (isdigit(str[0])) + return false; + + for (i = 0; i < str.length(); i++) + if (!(isalnum(str[i]) || str[i] == '_')) + return false; + + return true; +} + mtx_expression_t::iterator MtxExpression::findOperator(mtx_expression_t &lexical_elements, const char *str) { @@ -502,9 +630,13 @@ MtxExpression::getSymbol(bool create, bool fail_if_special) const else { + MtxMatrix *matrix, *matrix2; MtxSymbol symbol; MtxValue *value; + /* + * XXX I usually never do this, and was a little short on time. + */ switch (function) { case MTXEXPRESSION_FUNCTION_NULL: @@ -596,6 +728,73 @@ MtxExpression::getSymbol(bool create, bool fail_if_special) const symbol.setName(expressions[0]->getSymbol(true).getName()); language->setSymbol(symbol); return symbol; + + case MTXEXPRESSION_FUNCTION_DETERMINANT: + value = expressions[0]->getSymbol().getValue()->clone(); + matrix = dynamic_cast<MtxMatrix *>(value); + + if (matrix == NULL) + { + delete value; + throw MtxException("argument is not a matrix"); + } + + symbol = MtxSymbol(language, matrix->determinant()); + delete matrix; + return symbol; + + case MTXEXPRESSION_FUNCTION_INVERSE: + value = expressions[0]->getSymbol().getValue()->clone(); + matrix = dynamic_cast<MtxMatrix *>(value); + + if (matrix == NULL) + { + delete value; + throw MtxException("argument is not a matrix"); + } + + symbol = MtxSymbol(language, matrix->inverted()); + delete matrix; + return symbol; + + case MTXEXPRESSION_FUNCTION_TRANSPOSE: + value = expressions[0]->getSymbol().getValue()->clone(); + matrix = dynamic_cast<MtxMatrix *>(value); + + if (matrix == NULL) + { + delete value; + throw MtxException("argument is not a matrix"); + } + + symbol = MtxSymbol(language, matrix->transpose()); + delete matrix; + return symbol; + + case MTXEXPRESSION_FUNCTION_AUGMENT: + value = expressions[0]->getSymbol().getValue()->clone(); + matrix = dynamic_cast<MtxMatrix *>(value); + + if (matrix == NULL) + { + delete value; + throw MtxException("argument is not a matrix"); + } + + value = expressions[1]->getSymbol().getValue()->clone(); + matrix2 = dynamic_cast<MtxMatrix *>(value); + + if (matrix2 == NULL) + { + delete matrix; + delete value; + throw MtxException("argument is not a matrix"); + } + + symbol = MtxSymbol(language, matrix->augmented(*matrix2)); + delete matrix; + delete matrix2; + return symbol; } throw MtxException("operator/function not yet implemented"); diff --git a/mtxexpression.h b/mtxexpression.h index 16051b6..e22dcfe 100644 --- a/mtxexpression.h +++ b/mtxexpression.h @@ -34,7 +34,7 @@ /** * \enum mtx_function_id - * \brief ID of a function/operator (e.g. assignment, addition). + * \brief ID of a function/operator. E.g. assignment, addition. */ enum mtx_function_id { @@ -47,7 +47,11 @@ enum mtx_function_id MTXEXPRESSION_FUNCTION_MULTIPLY, MTXEXPRESSION_FUNCTION_MULTIPLY_ASSIGN, MTXEXPRESSION_FUNCTION_DIVIDE, - MTXEXPRESSION_FUNCTION_DIVIDE_ASSIGN + MTXEXPRESSION_FUNCTION_DIVIDE_ASSIGN, + MTXEXPRESSION_FUNCTION_DETERMINANT, + MTXEXPRESSION_FUNCTION_INVERSE, + MTXEXPRESSION_FUNCTION_TRANSPOSE, + MTXEXPRESSION_FUNCTION_AUGMENT }; /** @@ -62,7 +66,30 @@ enum mtx_operator_associativity }; /** + * A function. + */ +struct mtx_function +{ + /** + * Function string as entered by the user. + */ + char *str; + + /** + * ID of the function. + */ + enum mtx_function_id id; + + /** + * Number of arguments. + */ + size_t argc; +}; + +/** * An operator. + * + * XXX Inheritence from struct mtx_function seems to cause trouble. Why ? */ struct mtx_operator { @@ -141,9 +168,10 @@ class MtxExpression: public MtxSymbol /** * Free the pointers stored in the given vector of symbols. * - * Mainly used after parsing a matrix is done or failed. + * Mainly used after parsing a matrix or function arguments is done or + * failed. */ - void freeMatrix(std::vector<MtxSymbol *> &symbols) const; + void freeSymbols(std::vector<MtxSymbol *> &symbols) const; /** * Parse the given lexical elements and extract symbols and expressions @@ -160,6 +188,21 @@ class MtxExpression: public MtxSymbol std::vector<MtxSymbol *> &symbols, size_t &rows, size_t &columns) const; + /** + * Parse the given lexical elements and extract symbols and expressions + * from it to create the list of arguments of a function. An exception is + * thrown if the syntax is invalid. + * + * @param lexical_elements vector of strings to parse + * @param symbols vector of symbols/expressions updated by this method + */ + void parseArguments(const mtx_expression_t &lexical_elements, + std::vector<MtxSymbol *> &symbols) const; + + /** + * Array of builtin functions. + */ + static struct mtx_function functions[]; /** * Multidimensional array of supported operators. @@ -171,14 +214,26 @@ class MtxExpression: public MtxSymbol * * XXX There are at most 5 operators for the same precedence (+ a null * entry to indicate end of list), so the second dimension is 6. + * + * @see isOperator() */ static struct mtx_operator operators[][6]; /** * Return true if the given string matches an operator. + * + * @see operators() */ static bool isOperator(const std::string &str); + /** + * Return true if the given string is a valid variable name. + * + * A valid variable name consist of [a-zA-Z0-9_] characters only, + * and cannot begin with a digit. + */ + static bool isVariableName(const std::string &str); + public: /** * Create an expression from a set of lexical elements. |