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
This commit is contained in:
Stephan Aßmus 2006-06-07 22:05:03 +00:00
parent 1fa515a650
commit 19f75b98e6
8 changed files with 569 additions and 1286 deletions

View File

@ -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

View File

@ -8,15 +8,37 @@
* Stephan Aßmus <superstippi@gmx.de>
*/
#include <stdio.h>
#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;
}

View File

@ -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 <daniel.wallner@bredband.net>
* Timothy Wayper <timmy@wunderbear.com>
* Stephan Aßmus <superstippi@gmx.de>
*/
#include <iostream.h>
#include <fstream.h>
#include <complex>
#include <vector>
#include <math.h>
#include <string>
//#include <libxlds/xlds.h>
//#include <libdsph/getopt.h>
//#include <libdsph/const.h>
#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;
}

View File

@ -0,0 +1,474 @@
/*
* Copyright 2006 Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
* Stephan Aßmus <superstippi@gmx.de>
*/
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#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;
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2006 Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef EXPRESSION_PARSER_H
#define EXPRESSION_PARSER_H
#include <String.h>
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

View File

@ -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
;

View File

@ -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 <daniel.wallner@bredband.net>
*/
#include "Parser.h"
#include <iostream.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#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 T>
//class MemBlock {
// public:
// MemBlock()
// : fSize(0),
// fBuffer(NULL)
// {}
//
// MemBlock(const MemBlock<T>& src)
// : fSize(0),
// fBuffer(NULL)
// {
// if (src.fBuffer) {
// SetSize(src.fSize);
// memcpy(fBuffer, src.fBuffer, src.fSize);
// }
// }
//
// MemBlock<T>& operator=(const MemBlock<T>& 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<char> 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<char> 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;
}
}

View File

@ -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 <daniel.wallner@bredband.net>
*/
#ifndef PARSER_H
#define PARSER_H
#include <vector.h>
#include <string>
#include <stdio.h>
//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<std::string> m_line;
// std::vector<char> 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<std::string> fFunctionNames;
std::vector<char> fFunctionTypes;
std::vector<std::string> fConstantNames;
std::vector<double> fConstantValues;
};
#endif // PARSER_H