/* $Id$ */ /* * Copyright (C) 2006 Richard Braun * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "mtx.h" #include "mtxsymbol.h" #include "mtxlanguage.h" #include "mtxexception.h" #include "mtxexpression.h" #include "mtxinteger.h" #include using namespace std; const char *MtxLanguage::lexical_groups[] = { "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.", "=+-*/", "(", ")", "[", "]", ",", ";", NULL }; MtxLanguage::MtxLanguage() { } size_t MtxLanguage::findGroup(char c) const { const char *ptr; size_t i; i = 0; while (lexical_groups[i] != NULL && (ptr = std::strchr(lexical_groups[i], c)) == NULL) i++; if (lexical_groups[i] == NULL) throw MtxException("invalid character in expression"); return i; } void MtxLanguage::checkLexicalElement(const string &lexical_element) const { if (lexical_element.length() == 0) throw MtxException("bogus lexical_element, please report bug"); else if (isdigit(lexical_element[0]) || lexical_element[0] == '.') { size_t i, length; length = lexical_element.length(); for (i = 1; i < length; i++) if (!(isdigit(lexical_element[i]) || lexical_element[i] == '.')) throw MtxException("invalid numeric value"); } else if (strpbrk(lexical_element.c_str(), lexical_groups[MTXLANGUAGE_OPERATORS_INDEX]) != NULL) { if (lexical_element != "=" && lexical_element != "+" && lexical_element != "-" && lexical_element != "*" && lexical_element != "/" && lexical_element != "+=" && lexical_element != "-=" && lexical_element != "*=" && lexical_element != "/=") throw MtxException("invalid operator"); } } mtx_expression_t MtxLanguage::parseLexicalElements(const string &expression) const { size_t i, str_length, prev_group = 0, group; mtx_expression_t lexical_elements; string lexical_element; bool prev_set; char c; prev_set = false; str_length = expression.length(); for (i = 0; i < str_length; i++) { c = expression[i]; if (!isspace(c)) { group = findGroup(c); if (!prev_set || prev_group == group) { if (group != MTXLANGUAGE_VALUES_INDEX && group != MTXLANGUAGE_OPERATORS_INDEX && lexical_element.length() > 0) { lexical_elements.push_back(lexical_element); lexical_element = c; } else if (group == MTXLANGUAGE_OPERATORS_INDEX) { if(lexical_element == "=" && (c == '+' || c == '-')) { lexical_elements.push_back(lexical_element); lexical_element = c; } else if(lexical_element.length() > MTXLANGUAGE_OPERATORS_MAX_LENGTH) { checkLexicalElement(lexical_element); lexical_elements.push_back(lexical_element); lexical_element = c; } else lexical_element += c; } else lexical_element += c; } else { checkLexicalElement(lexical_element); lexical_elements.push_back(lexical_element); lexical_element = c; } prev_group = group; prev_set = true; } else { if (!lexical_element.empty()) { checkLexicalElement(lexical_element); lexical_elements.push_back(lexical_element); lexical_element = ""; } prev_set = false; } } if (!lexical_element.empty()) { checkLexicalElement(lexical_element); lexical_elements.push_back(lexical_element); lexical_element = ""; } return lexical_elements; } vector MtxLanguage::parseExpressions(const mtx_expression_t &lexical_elements) { mtx_expression_t::const_iterator iterator; vector expressions; size_t p_level, b_level; mtx_expression_t tmp; p_level = 0; b_level = 0; for (iterator = lexical_elements.begin(); iterator != lexical_elements.end(); iterator++) { if (p_level == 0 && b_level == 0 && (*iterator == "," || *iterator == ";")) { if (!tmp.empty()) { if (*iterator == ",") expressions.push_back(new MtxExpression(this, tmp)); else if (*iterator == ";") expressions.push_back(new MtxExpression(this, tmp, true)); tmp.clear(); } } else { if (*iterator == "(") p_level++; else if (*iterator == ")") { if (p_level == 0) throw MtxException("syntax error near )"); p_level--; } else if (*iterator == "[") b_level++; else if (*iterator == "]") { if (b_level == 0) throw MtxException("syntax error near ]"); b_level--; } tmp.push_back(*iterator); } } if (p_level != 0) throw MtxException("syntax error, missing )"); else if (b_level != 0) throw MtxException("syntax error, missing ]"); if (!tmp.empty()) expressions.push_back(new MtxExpression(this, tmp)); return expressions; } void MtxLanguage::freeExpressions(vector &expressions) { vector::iterator iterator; for (iterator = expressions.begin(); iterator != expressions.end(); iterator++) delete *iterator; } vector MtxLanguage::parse(const string &expression) { vector::iterator expression_iterator; mtx_expression_t lexical_elements; vector expressions; vector answers; MtxSymbol answer; lexical_elements = parseLexicalElements(expression); if (lexical_elements.empty()) return answers; expressions = parseExpressions(lexical_elements); for (expression_iterator = expressions.begin(); expression_iterator != expressions.end(); expression_iterator++) { try { answer = (*expression_iterator)->getSymbol(false, false); if (answer.getName().length() == 0) { answer.setName(MTXLANGUAGE_ANS_VARIABLE); setSymbol(answer); if (!(*expression_iterator)->isSilent()) answers.push_back(answer); } else if (!(*expression_iterator)->isSilent()) answers.push_back(answer); } catch (MtxException &exception) { freeExpressions(expressions); throw exception; } } freeExpressions(expressions); return answers; } void MtxLanguage::setSymbol(const MtxSymbol &symbol) { vector::iterator iterator; for (iterator = symbols.begin(); iterator != symbols.end(); iterator++) if ((*iterator).getName() == symbol.getName()) break; if (iterator == symbols.end()) symbols.push_back(symbol); else *iterator = symbol; } MtxSymbol MtxLanguage::getSymbol(const string &name) const { vector::const_iterator iterator; for (iterator = symbols.begin(); iterator != symbols.end(); iterator++) if ((*iterator).getName() == name) return *iterator; throw MtxException("undefined variable"); } vector MtxLanguage::getSymbols() const { return symbols; }