From 19f75b98e63c274db842a6e1193a9d7ae2854ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Wed, 7 Jun 2006 22:05:03 +0000 Subject: [PATCH] I hope Daniel is not upset, but Ingo and I have written a new parser: * it supports white spaces * it supports , and . for the decimal point * it gives parse errors and where the error occured * it supports more functions I also added evaluating an expression given on the command line, it doesn't show the UI then. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17774 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/deskcalc/CalcView.cpp | 19 +- src/apps/deskcalc/DeskCalc.cpp | 30 +- src/apps/deskcalc/DeskCalc.cpp_cli | 281 -------- src/apps/deskcalc/ExpressionParser.cpp | 474 +++++++++++++ src/apps/deskcalc/ExpressionParser.h | 61 ++ src/apps/deskcalc/Jamfile | 2 +- src/apps/deskcalc/Parser.cpp | 919 ------------------------- src/apps/deskcalc/Parser.h | 69 -- 8 files changed, 569 insertions(+), 1286 deletions(-) delete mode 100644 src/apps/deskcalc/DeskCalc.cpp_cli create mode 100644 src/apps/deskcalc/ExpressionParser.cpp create mode 100644 src/apps/deskcalc/ExpressionParser.h delete mode 100644 src/apps/deskcalc/Parser.cpp delete mode 100644 src/apps/deskcalc/Parser.h diff --git a/src/apps/deskcalc/CalcView.cpp b/src/apps/deskcalc/CalcView.cpp index a9fabf7d6f..7e65c74f99 100644 --- a/src/apps/deskcalc/CalcView.cpp +++ b/src/apps/deskcalc/CalcView.cpp @@ -27,8 +27,8 @@ #include "CalcApplication.h" #include "CalcOptionsWindow.h" +#include "ExpressionParser.h" #include "ExpressionTextView.h" -#include "Parser.h" const uint8 K_COLOR_OFFSET = 32; @@ -701,7 +701,6 @@ CalcView::Evaluate() const double EXP_SWITCH_LO = 1e-12; BString expression = fExpressionTextView->Text(); - expression << "\n"; if (expression.Length() == 0) { beep(); @@ -719,22 +718,18 @@ CalcView::Evaluate() //printf("evaluate: %s\n", expression.String()); // evaluate expression - Expression parser; - - char* tmpstr = strdup(expression.String()); - char* start = tmpstr; - char* end = start + expression.Length() - 1; double value = 0.0; try { - value = parser.Evaluate(start, end, true); - } catch (const char* error) { - fExpressionTextView->SetText(error); + ExpressionParser parser; + value = parser.Evaluate(expression.String()); + } catch (ParseException e) { + BString error(e.message.String()); + error << " at " << (e.position + 1); + fExpressionTextView->SetText(error.String()); return; } - free(tmpstr); - //printf(" -> value: %f\n", value); // beautify the expression diff --git a/src/apps/deskcalc/DeskCalc.cpp b/src/apps/deskcalc/DeskCalc.cpp index e46648bb61..8b9cf5c596 100644 --- a/src/apps/deskcalc/DeskCalc.cpp +++ b/src/apps/deskcalc/DeskCalc.cpp @@ -8,15 +8,37 @@ * Stephan Aßmus */ +#include + #include "CalcApplication.h" +#include "ExpressionParser.h" int main(int argc, char* argv[]) { - CalcApplication* app = new CalcApplication(); - - app->Run(); - delete app; + if (argc == 1) { + // run GUI + CalcApplication* app = new CalcApplication(); + + app->Run(); + delete app; + } else { + // evaluate expression from command line + BString expression; + int32 i = 1; + while (i < argc) { + expression << argv[i]; + i++; + } + + try { + ExpressionParser parser; + printf("%f\n", parser.Evaluate(expression.String())); + } catch (ParseException e) { + printf("%s at %ld\n", e.message.String(), e.position + 1); + return 1; + } + } return 0; } diff --git a/src/apps/deskcalc/DeskCalc.cpp_cli b/src/apps/deskcalc/DeskCalc.cpp_cli deleted file mode 100644 index 8edc1a5ac1..0000000000 --- a/src/apps/deskcalc/DeskCalc.cpp_cli +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2006 Haiku, Inc. All Rights Reserved. - * Copyright 2004 Daniel Wallner. All Rights Reserved. - * Copyright 1997, 1998 R3 Software Ltd. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Daniel Wallner - * Timothy Wayper - * Stephan Aßmus - */ - -#include -#include -#include -#include -#include -#include - -//#include -//#include -//#include - -#include "CalcApplication.h" - -using namespace std; -//using namespace xlds; - -void Usage() -{ - cerr << "Expression calculator" << endl << endl; - cerr << " usage: expcalc [expression] ... [options and arguments]" << endl << endl; - cerr << " -h print this message" << endl; - cerr << " -e even thousands exponent output" << endl; - cerr << " -p SI prefix output" << endl; - cerr << " -n newline after output" << endl << endl; - cerr << " If no arguments are given, expcalc enters interactive mode." << endl; - cerr << " You can combine short flags, so `-e -n' means the same as -en or -ne." << endl; -} - -int GetString(string &instring) -{ - instring.assign(""); - - char cchar; - - cchar = getc(stdin); - - while (cchar != EOF && cchar != 10) - { - instring.append(1, cchar); - cchar = getc(stdin); - } - - return 0; -} - -int main(int argc, char *argv[]) -{ - if (argc == 1) { - CalcApplication* app = new CalcApplication(); - - app->Run(); - delete app; - - return 0; - } - - - try - { - char *expression = NULL; - char outputMode = 0; - bool newline = false; - - while (doptind < argc) - { - if (argv[doptind][0] != '-' || strlen(argv[doptind]) <= 1) - { - // File argument - if (expression) - throw "Multiple expressions not supported"; - - expression = argv[doptind]; - - doptind++; - } - else - { - // Option - int opt = dgetopt(argc, argv, ":hepn"); - if (opt != -1) - { - switch (opt) - { - case 'h': - Usage(); - return 1; - break; - - case 'e': - outputMode = 1; - break; - - case 'p': - outputMode = 2; - break; - - case 'n': - newline = true; - break; - - case ':': // No argument - cerr << "Option -" << (char)doptopt << " requires an argument" << endl; - throw ""; - - case '?': - default: - cerr << "Unknown option -" << (char)doptopt << endl; - throw ""; - } - } - } - } - - string instring; - string laststring; - string stored; - - Expression exp; - - if (expression) - { - double temp = exp.Eval(expression, expression + strlen(expression)); - - if (!exp.Error()) - { - cout << exp.dtostr(temp, 14, outputMode).c_str(); - if (newline) - cout << endl; - } - else - { - char *i; - cout << expression << endl; - for (i = expression; i < exp.Error(); i++) - cout << " "; - cout << "^\nError in input\n\n"; - } - } - else - { - cout << "Expression Calculator\n"; - cout << "Copyright (c) Daniel Wallner 2004.\n"; - cout << "Type exit to end program or ? for help.\n\n"; - - cout << "ec> "; - - if (GetString(instring)) - return 0; - - bool rec = false; - - while (strcmp(instring.c_str(), "exit")) - { - if (!strcmp(instring.c_str(), "?")) - { - cout << "\nCommands l : repeat last calculation (used with ans)\n"; - cout << " sto : store last expression\n"; - cout << " rec : recall stored expression\n"; - cout << " d : set trigonometric functions to degrees\n"; - cout << " r : set trigonometric functions to radians (default)\n"; - cout << " e : toggle exponent output mode\n"; - cout << " p : set prefix output mode\n"; - cout << " exit : exit program\n"; - cout << "\nFunctions sin( : sinus\n"; - cout << " cos( : cosinus\n"; - cout << " tan( : tangens\n"; - cout << " asin( : arcsin\n"; - cout << " acos( : arccos\n"; - cout << " atan( : arctan\n"; - cout << " lg( : logarithm with base 10\n"; - cout << " ln( : logarithm with base e\n"; - cout << " sqrt( : sqare root\n"; - cout << " ans : last answer\n"; - cout << " ^,*,+,-,/ : as usual :-)\n"; - cout << "\nConstants e : the constant e\n"; - cout << " pi : the constant pi\n\n"; - - rec=1; - instring = laststring; - } - - if (!strcmp(instring.c_str(), "l")) - { - instring = laststring; - } - - if (!strcmp(instring.c_str(), "sto")) - { - stored = laststring; - cout << "\nLast expression stored!\n\n"; - rec = 1; - instring = laststring; - } - - if (!strcmp(instring.c_str(), "rec")) - { - instring = stored; - } - - if (!strcmp(instring.c_str(), "d")) - { - exp.SetAglf(M_PI / 180.); - rec = 1; - cout << "\nDegree mode!\n\n"; - instring = laststring; - } - - if (!strcmp(instring.c_str(), "r")) - { - exp.SetAglf(1.); - rec = 1; - cout << "\nRadian mode!\n\n"; - instring = laststring; - } - - if (!strcmp(instring.c_str(), "e")) - { - outputMode = (outputMode + 1) % 2; - cout << "\nExponent output mode" << outputMode << "!\n\n"; - instring = laststring; - } - - if (!strcmp(instring.c_str(), "p")) - { - outputMode = 2; - cout << "\nPrefix output mode!\n\n"; - instring = laststring; - } - - if (!instring.size()) - rec = 1; - - if (rec == 0) - { - double temp = exp.Eval(instring.c_str(), instring.c_str() + instring.size()); - - if (!exp.Error()) - { - cout << "\n" << instring << " = "; - cout << exp.dtostr(temp, 14, outputMode).c_str(); - cout << "\n\n"; - exp.SetConstant("ans", temp); - } - else - { - const char *i; - for (i = instring.c_str(); i < exp.Error(); i++) - cout << " "; - cout << " "; - cout << "^\nError in input\n\n"; - } - } - - cout << "ec> "; - laststring = instring; - if (GetString(instring)) - return 0; - rec = 0; - } - } - } - catch (const char * str) - { - cerr << str << endl; - return 1; - } - - return 0; -} diff --git a/src/apps/deskcalc/ExpressionParser.cpp b/src/apps/deskcalc/ExpressionParser.cpp new file mode 100644 index 0000000000..ca812cf8bc --- /dev/null +++ b/src/apps/deskcalc/ExpressionParser.cpp @@ -0,0 +1,474 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + * Stephan Aßmus + */ + +#include +#include +#include +#include + +#include "ExpressionParser.h" + +enum { + TOKEN_IDENTIFIER = 'a', + + TOKEN_CONSTANT = '0', + + TOKEN_PLUS = '+', + TOKEN_MINUS = '-', + + TOKEN_STAR = '*', + TOKEN_SLASH = '/', + TOKEN_MODULO = '%', + + TOKEN_POWER = '^', + + TOKEN_OPENING_BRACKET = '(', + TOKEN_CLOSING_BRACKET = ')', + + TOKEN_AND = '&', + TOKEN_OR = '|', + TOKEN_NOT = '~', + + TOKEN_NONE = ' ', + TOKEN_END_OF_LINE = '\n', +}; + + +struct Token { + Token() + : string(""), + type(TOKEN_NONE), + value(0.0), + position(0) + { + } + + Token(const Token& other) + : string(other.string), + type(other.type), + value(other.value), + position(other.position) + { + } + + Token(const char* string, int32 length, int32 position, int32 type) + : string(string, length), + type(type), + value(0.0), + position(position) + { + } + + Token& operator=(const Token& other) + { + string = other.string; + type = other.type; + value = other.value; + position = other.position; + return *this; + } + + BString string; + int32 type; + double value; + + int32 position; +}; + + +class Tokenizer { + public: + Tokenizer() + : fString(""), + fCurrentChar(NULL), + fCurrentToken(), + fReuseToken(false) + { + } + + void SetTo(const char* string) + { + fString = string; + fCurrentChar = fString.String(); + fCurrentToken = Token(); + fReuseToken = false; + } + + const Token& NextToken() + { + if (fCurrentToken.type == TOKEN_END_OF_LINE) + return fCurrentToken; + + if (fReuseToken) { + fReuseToken = false; +//printf("next token (recycled): '%s'\n", fCurrentToken.string.String()); + return fCurrentToken; + } + + while (*fCurrentChar != 0 && isspace(*fCurrentChar)) + fCurrentChar++; + + if (*fCurrentChar == 0) + return fCurrentToken = Token("", 0, _CurrentPos(), TOKEN_END_OF_LINE); + + bool decimal = *fCurrentChar == '.' || *fCurrentChar == ','; + + if (decimal || isdigit(*fCurrentChar)) { + + BString temp; + + const char* begin = fCurrentChar; + while (*fCurrentChar != 0) { + if (!isdigit(*fCurrentChar)) { + if (!(*fCurrentChar == '.' || *fCurrentChar == ',' + || *fCurrentChar == 'e' || *fCurrentChar == 'E')) + break; + } + if (*fCurrentChar == ',') + temp << '.'; + else + temp << *fCurrentChar; + fCurrentChar++; + } + int32 length = fCurrentChar - begin; + temp << "&_"; + double value; + char t[2]; + int32 matches = sscanf(temp.String(), "%lf&%s", &value, t); + if (matches != 2) + throw ParseException("error in constant", _CurrentPos() - length); + + fCurrentToken = Token(begin, length, _CurrentPos() - length, TOKEN_CONSTANT); + fCurrentToken.value = value;; + + } else if (isalpha(*fCurrentChar)) { + + const char* begin = fCurrentChar; + while (*fCurrentChar != 0 && (isalpha(*fCurrentChar) || isdigit(*fCurrentChar))) + fCurrentChar++; + int32 length = fCurrentChar - begin; + fCurrentToken = Token(begin, length, _CurrentPos() - length, TOKEN_IDENTIFIER); + + } else { + + switch (*fCurrentChar) { + case '+': + case '-': + case '*': + case '/': + + case '^': + case '%': + + case '(': + case ')': + + case '&': + case '|': + case '~': + fCurrentToken = Token(fCurrentChar, 1, _CurrentPos(), *fCurrentChar); + fCurrentChar++; + break; + + default: + throw ParseException("unexpected character", _CurrentPos()); + } + } + +//printf("next token: '%s'\n", fCurrentToken.string.String()); + return fCurrentToken; + } + + void RewindToken() + { + fReuseToken = true; + } + + private: + int32 _CurrentPos() const + { + return fCurrentChar - fString.String(); + } + + BString fString; + const char* fCurrentChar; + Token fCurrentToken; + bool fReuseToken; +}; + + +ExpressionParser::ExpressionParser() + : fTokenizer(new Tokenizer()) +{ +} + + +ExpressionParser::~ExpressionParser() +{ + delete fTokenizer; +} + + +double +ExpressionParser::Evaluate(const char* expressionString) +{ + fTokenizer->SetTo(expressionString); + + double value = _ParseBinary(); + Token token = fTokenizer->NextToken(); + if (token.type != TOKEN_END_OF_LINE) + throw ParseException("parse error", token.position); + + return value; +} + + +double +ExpressionParser::_ParseBinary() +{ + double value = _ParseSum(); + + while (true) { + Token token = fTokenizer->NextToken(); + switch (token.type) { + case TOKEN_AND: + value = (uint64)value & (uint64)_ParseSum(); + break; + case TOKEN_OR: + value = (uint64)value | (uint64)_ParseSum(); + break; + + default: + fTokenizer->RewindToken(); + return value; + } + } +} + + +double +ExpressionParser::_ParseSum() +{ + // TODO: check isnan()... + double value = _ParseProduct(); + + while (true) { + Token token = fTokenizer->NextToken(); + switch (token.type) { + case TOKEN_PLUS: + value = value + _ParseProduct(); + break; + case TOKEN_MINUS: + value = value - _ParseProduct(); + break; + + default: + fTokenizer->RewindToken(); + return value; + } + } +} + + +double +ExpressionParser::_ParseProduct() +{ + // TODO: check isnan()... + double value = _ParsePower(); + + while (true) { + Token token = fTokenizer->NextToken(); + switch (token.type) { + case TOKEN_STAR: + value = value * _ParsePower(); + break; + case TOKEN_SLASH: { + double rhs = _ParsePower(); + if (rhs == 0.0) + throw ParseException("division by zero", token.position); + value = value / rhs; + break; + } + case TOKEN_MODULO: { + double rhs = _ParsePower(); + if (rhs == 0.0) + throw ParseException("modulo by zero", token.position); + value = fmod(value, rhs); + break; + } + + default: + fTokenizer->RewindToken(); + return value; + } + } +} + + +double +ExpressionParser::_ParsePower() +{ + double value = _ParseUnary(); + + while (true) { + Token token = fTokenizer->NextToken(); + if (token.type != TOKEN_POWER) { + fTokenizer->RewindToken(); + return value; + } + value = pow(value, _ParseUnary()); + } +} + + +double +ExpressionParser::_ParseUnary() +{ + Token token = fTokenizer->NextToken(); + if (token.type == TOKEN_END_OF_LINE) + throw ParseException("unexpected end of expression", token.position); + + switch (token.type) { + case TOKEN_PLUS: + return _ParseUnary(); + case TOKEN_MINUS: + return -_ParseUnary(); + case TOKEN_NOT: + return ~(uint64)_ParseUnary(); + + case TOKEN_IDENTIFIER: + return _ParseFunction(token); + + default: + fTokenizer->RewindToken(); + return _ParseAtom(); + } + + return 0.0; +} + + +struct Function { + const char* name; + int argumentCount; + void* function; + double value; +}; + +static const Function kFunctions[] = { + { "e", 0, NULL, M_E }, + { "pi", 0, NULL, M_PI }, + + { "abs", 1, &fabs }, + { "acos", 1, &acos }, + { "asin", 1, &asin }, + { "atan", 1, &atan }, + { "atan2", 2, &atan2 }, + { "ceil", 1, &ceil }, + { "cos", 1, &cos }, + { "cosh", 1, &cosh }, + { "exp", 1, &exp }, + { "fabs", 1, &fabs }, + { "floor", 1, &floor }, + { "fmod", 2, &fmod }, + { "log", 1, &log }, + { "log10", 1, &log10 }, + { "pow", 2, &pow }, + { "sin", 1, &sin }, + { "sinh", 1, &sinh }, + { "sqrt", 1, &sqrt }, + { "tan", 1, &tan }, + { "tanh", 1, &tanh }, + { "hypot", 2, &hypot }, + + { NULL }, +}; + +double +ExpressionParser::_ParseFunction(const Token& token) +{ + const Function* function = _FindFunction(token.string.String()); + if (!function) + throw ParseException("unknown identifier", token.position); + + if (function->argumentCount == 0) + return function->value; + + _EatToken(TOKEN_OPENING_BRACKET); + + double values[function->argumentCount]; + for (int32 i = 0; i < function->argumentCount; i++) { + values[i] = _ParseBinary(); + } + + _EatToken(TOKEN_CLOSING_BRACKET); + + // hard coded cases for different count of arguments + switch (function->argumentCount) { + case 1: + return ((double (*)(double))function->function)(values[0]); + case 2: + return ((double (*)(double, double))function->function)(values[0], + values[1]); + case 3: + return ((double (*)(double, double, double))function->function)( + values[0], values[1], values[2]); + + default: + throw ParseException("unsupported function argument count", + token.position); + } +} + + +double +ExpressionParser::_ParseAtom() +{ + Token token = fTokenizer->NextToken(); + if (token.type == TOKEN_END_OF_LINE) + throw ParseException("unexpected end of expression", token.position); + + if (token.type == TOKEN_CONSTANT) + return token.value; + + fTokenizer->RewindToken(); + + _EatToken(TOKEN_OPENING_BRACKET); + + double value = _ParseBinary(); + + _EatToken(TOKEN_CLOSING_BRACKET); + + return value; +} + + +void +ExpressionParser::_EatToken(int32 type) +{ + Token token = fTokenizer->NextToken(); + if (token.type != type) { + BString temp("expected '"); + temp << (char)type << "' got '" << token.string << "'"; + throw ParseException(temp.String(), token.position); + } +} + + +const Function* +ExpressionParser::_FindFunction(const char* name) const +{ + for (int32 i = 0; kFunctions[i].name; i++) { + if (strcasecmp(kFunctions[i].name, name) == 0) + return &kFunctions[i]; + } + + return NULL; +} + diff --git a/src/apps/deskcalc/ExpressionParser.h b/src/apps/deskcalc/ExpressionParser.h new file mode 100644 index 0000000000..d5f855dfad --- /dev/null +++ b/src/apps/deskcalc/ExpressionParser.h @@ -0,0 +1,61 @@ +/* + * Copyright 2006 Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + * Stephan Aßmus + */ + +#ifndef EXPRESSION_PARSER_H +#define EXPRESSION_PARSER_H + +#include + +class Tokenizer; + +class ParseException { + public: + ParseException(const char* message, int32 position) + : message(message), + position(position) + { + } + + ParseException(const ParseException& other) + : message(other.message), + position(other.position) + { + } + + BString message; + int32 position; +}; + +struct Function; +struct Token; + +class ExpressionParser { + public: + ExpressionParser(); + ~ExpressionParser(); + + double Evaluate(const char* expressionString); + + private: + + double _ParseBinary(); + double _ParseSum(); + double _ParseProduct(); + double _ParsePower(); + double _ParseUnary(); + double _ParseFunction(const Token& token); + double _ParseAtom(); + + void _EatToken(int32 type); + const Function* _FindFunction(const char* name) const; + + Tokenizer* fTokenizer; +}; + +#endif // EXPRESSION_PARSER_H diff --git a/src/apps/deskcalc/Jamfile b/src/apps/deskcalc/Jamfile index f45d4ee05f..58897cf56f 100644 --- a/src/apps/deskcalc/Jamfile +++ b/src/apps/deskcalc/Jamfile @@ -10,9 +10,9 @@ Application DeskCalc : CalcView.cpp CalcWindow.cpp DeskCalc.cpp + ExpressionParser.cpp ExpressionTextView.cpp InputTextView.cpp - Parser.cpp : be $(TARGET_LIBSTDC++) media : DeskCalc.rdef ; diff --git a/src/apps/deskcalc/Parser.cpp b/src/apps/deskcalc/Parser.cpp deleted file mode 100644 index 889b3c849c..0000000000 --- a/src/apps/deskcalc/Parser.cpp +++ /dev/null @@ -1,919 +0,0 @@ -/* - * Copyright 2006 Haiku, Inc. All Rights Reserved. - * Copyright 2004 Daniel Wallner. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Daniel Wallner - */ - -#include "Parser.h" - -#include -#include -#include -#include - -#define NOLLF 5e-16 - -using namespace std; - -//LineParser::LineParser(istream *stream): -// m_stream(stream), -// m_currentLine(0) -//{ -//} -// -//void LineParser::AddSeparator(const char c) -//{ -// m_separators.push_back(c); -//} -// -//size_t LineParser::SetToNext(bool toUpperCase) -//{ -// m_line.erase(m_line.begin(), m_line.end()); -// m_unparsed.assign(""); -// -// // Skip newlines -// while (m_stream->peek() == 0xA || m_stream->peek() == 0xD) -// m_stream->get(); -// -// while (!m_stream->eof() && !m_stream->fail() && m_stream->peek() != 0xA && m_stream->peek() != 0xD) -// { -// string param; -// -// // Skip whitespaces -// while (m_stream->peek() == ' ' || m_stream->peek() == '\t') -// m_unparsed.append(1, char(m_stream->get())); -//// m_separators.push_back(c); add check for separators !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// if (m_stream->peek() == '"') -// { -// // Skip first " -// m_unparsed.append(1, char(m_stream->get())); -// -// int c; -// // Read until " -// while (!m_stream->fail() && m_stream->peek() != 0xA && m_stream->peek() != 0xD && -// (c = m_stream->get()) != '"' && !m_stream->eof()) -// { -// m_unparsed.append(1, char(c)); -// if (toUpperCase) -// param.append(1, char(toupper(c))); -// else -// param.append(1, char(c)); -// } -// if (c == '"') -// m_unparsed.append(1, char(c)); -// } -// else -// { -// int c; -// // Read until whitespace -//// m_separators.push_back(c); add check for separators !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// while (!m_stream->fail() && m_stream->peek() != 0xA && m_stream->peek() != 0xD && -// (c = m_stream->get()) != ' ' && c != '\t' && !m_stream->eof()) -// { -// m_unparsed.append(1, char(c)); -// if (toUpperCase) -// param.append(1, char(toupper(c))); -// else -// param.append(1, char(c)); -// } -// if (c == ' ') -// m_unparsed.append(1, char(c)); -// } -// -// // Store parameter -// if (param.size()) -// m_line.push_back(param); -// } -// -// // Skip newlines -// while (m_stream->peek() == 0xA || m_stream->peek() == 0xD) -// m_stream->get(); -// -// return m_line.size(); -//} -// -//void LineParser::WriteCompressed(string *s) const -//{ -// size_t i; -// for (i = 0; i < m_line.size(); i++) -// { -// if (i != 0) -// { -// *s += " "; -// } -// *s += m_line[i]; -// } -//} -// -//void LineParser::WriteCompressed(ostream *stream) const -//{ -// size_t i; -// for (i = 0; i < m_line.size(); i++) -// { -// if (i != 0) -// { -// *stream << " "; -// } -// *stream << m_line[i]; -// } -// *stream << endl; -//} -// -//size_t LineParser::ParsedLines() const -//{ -// return m_currentLine; -//} -// -//const char *LineParser::Param(const size_t param) const -//{ -// return m_line[param].c_str(); -//} -// -//const char *LineParser::Line() const -//{ -// return m_unparsed.c_str(); -//} - - -// NOTE: commented because unused - -//template -//class MemBlock { -// public: -// MemBlock() -// : fSize(0), -// fBuffer(NULL) -// {} -// -// MemBlock(const MemBlock& src) -// : fSize(0), -// fBuffer(NULL) -// { -// if (src.fBuffer) { -// SetSize(src.fSize); -// memcpy(fBuffer, src.fBuffer, src.fSize); -// } -// } -// -// MemBlock& operator=(const MemBlock& src) -// { -// if (this != &src) { -// SetSize(src.fSize); -// if (fBuffer) -// memcpy(fBuffer, src.fBuffer, src.fSize); -// } -// return (*this); -// } -// -// ~MemBlock() -// { -// free(fBuffer); -// } -// -// void SetSize(const size_t size) -// { -// size_t oldsize = Size(); -// if (oldsize != size) { -// if (!fBuffer && size) { -// fBuffer = malloc(size); -// if (!fBuffer) -// throw "Out of memory"; -// } else if (fBuffer && size) { -// void *newBuf = realloc(fBuffer, size); -// if (newBuf) -// fBuffer = newBuf; -// else -// throw "Out of memory"; -// } else if (fBuffer && !size) { -// free(fBuffer); -// fBuffer = 0; -// } -// fSize = size; -// } -// } -// void* Buffer() const { return fBuffer; } -// T* Access() const { return (T*)fBuffer; } -// T& operator[](size_t i) const -// { -// return Access()[i]; -// } -// size_t Size() const { return fSize; } -// -// protected: -// size_t fSize; -// void* fBuffer; -//}; -// -// -//static int -//strprintf(string &str, const char * format, ...) -//{ -// va_list argptr; -// -// char strfix[128]; -// -// va_start(argptr, format); -// int ret = vsnprintf(strfix, 128, format, argptr); -// va_end(argptr); -// -// if (ret == -1) { -// MemBlock strbuf; -// strbuf.SetSize(128); -// while (ret == -1) { -// strbuf.SetSize(strbuf.Size() * 2); -// va_start(argptr, format); -// ret = vsnprintf(strbuf.Access(), strbuf.Size(), format, argptr); -// va_end(argptr); -// } -// str.assign(strbuf.Access()); -// } else if (ret >= 128) { -// MemBlock strbuf; -// strbuf.SetSize(ret + 1); -// va_start(argptr, format); -// ret = vsnprintf(strbuf.Access(), ret + 1, format, argptr); -// va_end(argptr); -// str.assign(strbuf.Access()); -// } else { -// str.assign(strfix); -// } -// -// return ret; -//} - - -static double -valtod(const char* exp, const char** end) -{ - // http://en.wikipedia.org/wiki/SI_prefix - - char *endptr; - double temp = strtod(exp, &endptr); - if (endptr == exp || !*endptr) { - if (end) - *end = endptr; - return temp; - } - - if (end) - *end = endptr + 1; - - switch (*endptr) { - /* case 'Y': // yotta - return temp * 1.E24; - case 'Z': // zetta - return temp * 1.E21; - case 'E': // exa, incompatible with exponent! - return temp * 1.E18;*/ - case 'P': // peta - return temp * 1.E15; - case 'T': // tera - return temp * 1.E12; - case 'G': // giga - return temp * 1.E9; - case 'M': // mega - return temp * 1.E6; - case 'k': // kilo - return temp * 1.E3; - case 'h': // hecto - return temp * 1.E2; - case 'd': - if (endptr[1] == 'a') { - if (end) - *end = endptr + 2; - return temp * 1.E1; // deca - } else - return temp * 1.E-1; // deci - case 'c': // centi - return temp * 1.E-2; - case 'm': // milli - return temp * 1.E-3; - case 'u': // micro - return temp * 1.E-6; - case 'n': // nano - return temp * 1.E-9; - case 'p': // pico - return temp * 1.E-12; - case 'f': // femto - return temp * 1.E-15; - case 'a': // atto - return temp * 1.E-18; - case 'z': // zepto - return temp * 1.E-21; - case 'y': // yocto - return temp * 1.E-24; - } - if (end) - *end = endptr; - - return temp; -} - -// NOTE: commented because unused - -//static double -//exptod(const char* str, const char** end) -//{ -// Expression exp; -// -// const char* evalEnd = str + strlen(str); -// double temp = exp.Evaluate(str, evalEnd); -// -// const char* err = exp.Error(); -// if (end) -// *end = err ? err : evalEnd; -// -// return temp; -//} -// -// -//static string -//dtostr(const double value, const unsigned int precision, const char mode) -//{ -// if (value == 0.0) -// return "0"; -// -// string str; -// char prefix[2]; -// prefix[0] = 0; -// prefix[1] = 0; -// -// if (mode == 1) { -// // exp3 -// double exp = floor(log10(fabs(value)) / 3.) * 3; -// string format; -// strprintf(format, "%%.%dGE%%ld", precision); -// strprintf(str, format.c_str(), value / pow(10., exp), exp); -// } else if (mode == 2) { -// // prefix -// double exp = floor(log10(fabs(value)) / 3.) * 3; -// string format; -// strprintf(format, "%%.%dG", precision); -// strprintf(str, format.c_str(), value / pow(10., exp)); -// switch (int(exp)) { -// case 24: // yotta -// prefix[0] = 'Y'; -// break; -// case 21: // zetta -// prefix[0] = 'Z'; -// break; -// case 18: // exa -// prefix[0] = 'E'; -// break; -// case 15: // peta -// prefix[0] = 'P'; -// break; -// case 12: // tera -// prefix[0] = 'T'; -// break; -// case 9: // giga -// prefix[0] = 'G'; -// break; -// case 6: // mega -// prefix[0] = 'M'; -// break; -// case 3: // kilo -// prefix[0] = 'k'; -// break; -// case -3: // milli -// prefix[0] = 'm'; -// break; -// case -6: // micro -// prefix[0] = 'u'; -// break; -// case -9: // nano -// prefix[0] = 'n'; -// break; -// case -12: // pico -// prefix[0] = 'p'; -// break; -// case -15: // femto -// prefix[0] = 'f'; -// break; -// case -18: // atto -// prefix[0] = 'a'; -// break; -// case -21: // zepto -// prefix[0] = 'z'; -// break; -// case -24: // yocto -// prefix[0] = 'y'; -// break; -// default: -// if (exp) { -// strprintf(format, "%%.%dGE%%ld", precision); -// strprintf(str, format.c_str(), value / pow(10., exp), exp); -// } else { -// strprintf(format, "%%.%dG", precision); -// strprintf(str, format.c_str(), value); -// } -// break; -// } -// } else { -// // exp -// string format; -// strprintf(format, "%%.%dG", precision); -// strprintf(str, format.c_str(), value); -// } -// -// return string(str) + prefix; -//} - - -// #pragma mark - - - -Expression::Expression() - : fAglf(1.), - fErrorPtr(NULL), - fTrig(false) -{ - // NOTE: these seem to be hard coded anyways... - fFunctionNames.push_back("sin("); // 0 - fFunctionTypes.push_back(1); - fFunctionNames.push_back("asin("); // 1 - fFunctionTypes.push_back(2); - fFunctionNames.push_back("cos("); // 2 - fFunctionTypes.push_back(1); - fFunctionNames.push_back("acos("); // 3 - fFunctionTypes.push_back(2); - fFunctionNames.push_back("tan("); // 4 - fFunctionTypes.push_back(1); - fFunctionNames.push_back("atan("); // 5 - fFunctionTypes.push_back(2); - fFunctionNames.push_back("ln("); // 6 - fFunctionTypes.push_back(0); - fFunctionNames.push_back("lg("); // 7 - fFunctionTypes.push_back(0); - fFunctionNames.push_back("sqrt("); // 8 - fFunctionTypes.push_back(0); - fFunctionNames.push_back("("); // 9 - fFunctionTypes.push_back(0); - - SetConstant("e", M_E); - SetConstant("pi", M_PI); -} - - -void -Expression::SetAglf(const double value) -{ - fAglf = value; -} - - -bool -Expression::SetConstant(const char* name, const double value) -{ - for (size_t i = 0; name[i]; i++) { - if (!isalnum(name[i])) { - throw "Illegal character in constant name"; - } - } - for (size_t i = 0; i < fConstantNames.size(); i++) { - if (!strcmp(name, fConstantNames[i].c_str())) { - fConstantValues[i] = value; - return true; - } - } - - fConstantNames.push_back(name); - fConstantValues.push_back(value); - - return false; -} - - -double -Expression::Evaluate(const char* exp) -{ - const char* evalEnd = exp + strlen(exp); - return Evaluate(exp, evalEnd); -} - - -double -Expression::Evaluate(const char* begin, const char* end, const bool allowWhiteSpaces) -{ - const char* curptr = begin; - int state = 1; - double operand; - double sum = 0.; - double prod = 0.0; - double power = 0.0; - - if (begin[0] == 0) { - fErrorPtr = begin; - return 0.; - } - - if (fErrorPtr) { - return 0.; - } - - for (;;) { - const char *operandEnd; - bool minusBeforeOperand = false; - bool numberOperand = true; - - if (allowWhiteSpaces) { - while (curptr < end && isspace(curptr[0])) - curptr++; - } - - if (curptr[0] == '-') - minusBeforeOperand = true; - - operand = valtod(curptr, &operandEnd); - - if (operandEnd == curptr) { - // operand not a number - const char *operandStart = curptr; - double operandSign = 1.; - - numberOperand = false; - - if (curptr[0] == '+') { - ++operandStart; - } else if (curptr[0] == '-') { - ++operandStart; - operandSign = -1.; - } - - // Iterate through functions - size_t i; - for (i = 0; i < fFunctionNames.size(); i++) { - if (!strncmp(operandStart, fFunctionNames[i].c_str(), fFunctionNames[i].size())) { - const char *functionArg = operandStart + fFunctionNames[i].size(); - const char *chptr = functionArg - 1; - const char *prptr = functionArg - 1; - - if (fFunctionTypes[i]) - fTrig = true; - - // Find closing paranthesis - while (prptr) { - chptr = strchr(chptr + 1, ')'); - prptr = strchr(prptr + 1, '('); - if (prptr > chptr) - break; - } - - if (chptr) { - // Closing paranthesis - if (fFunctionTypes[i] == 1) { - // Trig - operand = operandSign * _ApplyFunction(fAglf * Evaluate(functionArg, chptr), i); - if (fabs(operand) < NOLLF) - operand = 0; - } else if (fFunctionTypes[i] == 2) { - // Inv trig - operand = operandSign / fAglf * _ApplyFunction(Evaluate(functionArg, chptr), i); - } else { - // Normal - operand = operandSign * _ApplyFunction(Evaluate(functionArg, chptr), i); - } - operandEnd = chptr + 1; - } else { - // No closing paranthesis - if (fFunctionTypes[i] == 1) { - operand = operandSign * _ApplyFunction(fAglf * Evaluate(functionArg, end), i); - if (fabs(operand) < NOLLF) - operand = 0; - } else if (fFunctionTypes[i] == 2) { - operand = operandSign / fAglf * _ApplyFunction(Evaluate(functionArg, end), i); - } else { - operand = operandSign * _ApplyFunction(Evaluate(functionArg, end), i); - } - operandEnd = end; - } - - if (fErrorPtr) { - return 0.; - } - - break; - } - } - - // Iterate through constants - if (i == fFunctionNames.size()) { - // Only search if no function found - for (i = 0; i < fConstantNames.size(); i++) { - if (!strncmp(operandStart, fConstantNames[i].c_str(), fConstantNames[i].size()) && !isalnum(operandStart[fConstantNames[i].size()])) { - operandEnd = operandStart + fConstantNames[i].size(); - operand = operandSign * fConstantValues[i]; - break; - } - } - } - } - - if (operandEnd > end) { - fErrorPtr = end; - return 0.; - } - - // Calculate expression in correct order - - // state == 1 => term => sum + operand - // state == 2 => product => sum + prod * operand - // state == 3 => quotient => sum + prod / operand - // state == 4 => plus & power => sum + power ^ operand - // state == 5 => minus & power => sum - power ^ operand - // state == 6 => product & power => sum + prod * power ^ operand - // state == 7 => quotient & power => sum + prod / power ^ operand - - if (curptr != operandEnd) { - // number? - switch (state) { - case 1: // term - if (operandEnd == end) { - return sum + operand; - } - switch (operandEnd[0]) { - // Check next - case '+': - case '-': - curptr = operandEnd; - sum = sum + operand; - break; - case '*': - curptr = operandEnd + 1; - prod = operand; - state = 2; - break; - case '/': - curptr = operandEnd + 1; - prod = operand; - state = 3; - break; - case '^': - curptr = operandEnd + 1; - power = operand; - if (minusBeforeOperand) { - power = power * -1.; - state = 5; - } else { - state = 4; - } - break; - default: - if (numberOperand && isalpha(operandEnd[0])) { - // Assume multiplication of function/constant without * - curptr = operandEnd; - prod = operand; - state = 2; - } else { - fErrorPtr = operandEnd; - return 0.; - } - } - break; - - case 2: // product - if (operandEnd == end) { - return sum + prod * operand; - } - switch (operandEnd[0]) { - // Check next - case '+': - case '-': - curptr = operandEnd; - sum = sum + prod * operand; - state = 1; - break; - case '*': - curptr = operandEnd + 1; - prod = prod * operand; - break; - case '/': - curptr = operandEnd + 1; - prod = prod * operand; - state = 3; - break; - case '^': - curptr = operandEnd + 1; - power = operand; - state = 6; - break; - default: - fErrorPtr = operandEnd; - return 0.; - } - break; - - case 3: // quotient - if (operandEnd == end) { - return sum + prod / operand; - } - switch (operandEnd[0]) { - // Check next - case '+': - case '-': - curptr = operandEnd; - sum = sum + prod / operand; - state = 1; - break; - case '*': - curptr = operandEnd + 1; - prod = prod / operand; - state = 2; - break; - case '/': - curptr = operandEnd + 1; - prod = prod / operand; - break; - case '^': - curptr = operandEnd + 1; - power = operand; - state = 7; - break; - default: - fErrorPtr = operandEnd; - return 0.; - } - break; - - case 4: // plus&power - if (operandEnd == end) { - return sum + pow(power, operand); - } - switch (operandEnd[0]) { - // Check next - case '+': - case '-': - curptr = operandEnd; - sum = sum + pow(power, operand); - state = 1; - break; - case '*': - curptr = operandEnd + 1; - prod = pow(power, operand); - state = 2; - break; - case '/': - curptr = operandEnd + 1; - prod = pow(power, operand); - state = 3; - break; - case '^': - curptr = operandEnd + 1; - power = pow(power, operand); - break; - default: - fErrorPtr = operandEnd; - return 0.; - } - break; - - case 5: // minus&power - if (operandEnd == end) { - return sum - pow(power, operand); - } - switch (operandEnd[0]) { - // Check next - case '+': - case '-': - curptr = operandEnd; - sum = sum - pow(power, operand); - state = 1; - break; - case '*': - curptr = operandEnd + 1; - prod = pow(power, operand); - state = 2; - break; - case '/': - curptr = operandEnd + 1; - prod = pow(power, operand); - state = 3; - break; - case '^': - curptr = operandEnd + 1; - power = pow(power, operand); - break; - default: - fErrorPtr = operandEnd; - return 0.; - } - break; - - case 6: // product&power - if (operandEnd == end) { - return sum + prod * pow(power, operand); - } - switch (operandEnd[0]) { - // Check next - case '+': - case '-': - curptr = operandEnd; - sum = sum + prod * pow(power, operand); - state = 1; - break; - case '*': - curptr = operandEnd + 1; - prod = prod * pow(power, operand); - state = 2; - break; - case '/': - curptr = operandEnd + 1; - prod = prod * pow(power, operand); - state = 3; - break; - case '^': - curptr = operandEnd + 1; - power = pow(power, operand); - break; - default: - fErrorPtr = operandEnd; - return 0.; - } - break; - - case 7: // quotient&power - if (operandEnd == end) { - return sum + prod / pow(power, operand); - } - switch (operandEnd[0]) { - // Check next - case '+': - case '-': - curptr = operandEnd; - sum = sum + prod / pow(power, operand); - state = 1; - break; - case '*': - curptr = operandEnd + 1; - prod = prod / pow(power, operand); - state = 2; - break; - case '/': - curptr = operandEnd + 1; - prod = prod / pow(power, operand); - state = 3; - break; - case '^': - curptr = operandEnd + 1; - power = pow(power, operand); - break; - default: - fErrorPtr = operandEnd; - return 0.; - } - break; - - default: - fErrorPtr = operandEnd; - return 0.; - } - } else { - fErrorPtr = curptr; - return 0.; - } - } -} - - -const char* -Expression::Error() -{ - const char *retval = fErrorPtr; - fErrorPtr = NULL; - fTrig = false; - return retval; -} - - -// #pragma mark - - - -double -Expression::_ApplyFunction(const double x, const size_t number) const -{ - switch (number) { - case 0: - return sin(x); - case 1: - return asin(x); - case 2: - return cos(x); - case 3: - return acos(x); - case 4: - return tan(x); - case 5: - return atan(x); - case 6: - return log(x); - case 7: - return log10(x); - case 8: - return sqrt(x); - default: - return x; - } -} diff --git a/src/apps/deskcalc/Parser.h b/src/apps/deskcalc/Parser.h deleted file mode 100644 index c63557846c..0000000000 --- a/src/apps/deskcalc/Parser.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2006 Haiku, Inc. All Rights Reserved. - * Copyright 2004 Daniel Wallner. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Daniel Wallner - */ - -#ifndef PARSER_H -#define PARSER_H - -#include -#include -#include - -//class LineParser { -// public: -// LineParser(std::istream *stream); -// -// void AddSeparator(const char c); -// -// size_t SetToNext(bool toUpperCase = false); -// void WriteCompressed(std::string *s) const; -// void WriteCompressed(std::ostream *stream) const; -// -// size_t ParsedLines() const; -// const char *Param(const size_t param) const; -// const char *Line() const; -// -// private: -// LineParser(); -// -// std::istream *m_stream; -// size_t m_currentLine; -// std::string m_unparsed; -// std::vector m_line; -// std::vector m_separators; -//}; - -class Expression { - public: - Expression(); - - void SetAglf(const double value); - bool SetConstant(const char* name, - const double value); - - double Evaluate(const char* exp); - double Evaluate(const char* begin, const char* end, - const bool allowWhiteSpaces = false); - const char* Error(); - - private: - double _ApplyFunction(const double x, - const size_t number) const; - - double fAglf; - const char* fErrorPtr; - bool fTrig; - - std::vector fFunctionNames; - std::vector fFunctionTypes; - - std::vector fConstantNames; - std::vector fConstantValues; -}; - -#endif // PARSER_H