You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
830 lines
26 KiB
830 lines
26 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2002 Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <tqptrlist.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
|
|
#include <kformuladefs.h>
|
|
#include <kformuladocument.h>
|
|
#include <symboltable.h>
|
|
|
|
#include "fsparser.h"
|
|
|
|
|
|
using namespace std;
|
|
|
|
class ParserNode {
|
|
public:
|
|
ParserNode() { debugCount++; }
|
|
virtual ~ParserNode() { debugCount--; }
|
|
//virtual void output( ostream& ) = 0;
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element ) = 0;
|
|
virtual bool isSimple() { return false; }
|
|
|
|
static int debugCount;
|
|
};
|
|
|
|
int ParserNode::debugCount = 0;
|
|
|
|
class PrimaryNode : public ParserNode {
|
|
public:
|
|
PrimaryNode( TQString primary ) : m_primary( primary ), m_functionName( false ) {}
|
|
//virtual void output( ostream& stream ) { stream << "PrimaryNode {" << m_primary << "}" << endl; }
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element );
|
|
virtual bool isSimple() { return true; }
|
|
void setUnicode( TQChar unicode ) { m_unicode = unicode; }
|
|
void setFunctionName( bool functionName ) { m_functionName = functionName; }
|
|
TQString primary() const { return m_primary; }
|
|
private:
|
|
TQString m_primary;
|
|
TQChar m_unicode;
|
|
bool m_functionName;
|
|
};
|
|
|
|
void PrimaryNode::buildXML( TQDomDocument& doc, TQDomElement element )
|
|
{
|
|
if ( m_unicode != TQChar::null ) {
|
|
TQDomElement de = doc.createElement( "TEXT" );
|
|
de.setAttribute( "CHAR", TQString( m_unicode ) );
|
|
de.setAttribute( "SYMBOL", "3" );
|
|
element.appendChild( de );
|
|
}
|
|
else {
|
|
if ( m_functionName ) {
|
|
TQDomElement namesequence = doc.createElement( "NAMESEQUENCE" );
|
|
element.appendChild( namesequence );
|
|
element = namesequence;
|
|
}
|
|
for ( uint i = 0; i < m_primary.length(); i++ ) {
|
|
TQDomElement de = doc.createElement( "TEXT" );
|
|
de.setAttribute( "CHAR", TQString( m_primary[i] ) );
|
|
element.appendChild( de );
|
|
}
|
|
}
|
|
}
|
|
|
|
class UnaryMinus : public ParserNode {
|
|
public:
|
|
UnaryMinus( ParserNode* primary ) : m_primary( primary ) {}
|
|
~UnaryMinus() { delete m_primary; }
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element );
|
|
private:
|
|
ParserNode* m_primary;
|
|
};
|
|
|
|
void UnaryMinus::buildXML( TQDomDocument& doc, TQDomElement element )
|
|
{
|
|
TQDomElement de = doc.createElement( "TEXT" );
|
|
de.setAttribute( "CHAR", "-" );
|
|
element.appendChild( de );
|
|
m_primary->buildXML( doc, element );
|
|
}
|
|
|
|
class OperatorNode : public ParserNode {
|
|
public:
|
|
OperatorNode( TQString type, ParserNode* lhs, ParserNode* rhs )
|
|
: m_type( type ), m_lhs( lhs ), m_rhs( rhs ) {}
|
|
~OperatorNode() { delete m_rhs; delete m_lhs; }
|
|
// virtual void output( ostream& stream ) {
|
|
// stream << "OperatorNode {";
|
|
// m_lhs->output( stream ); stream << m_type; m_rhs->output( stream );
|
|
// stream << "}" << endl; }
|
|
protected:
|
|
TQString m_type;
|
|
ParserNode* m_lhs;
|
|
ParserNode* m_rhs;
|
|
};
|
|
|
|
class AssignNode : public OperatorNode {
|
|
public:
|
|
AssignNode( TQString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element );
|
|
};
|
|
|
|
void AssignNode::buildXML( TQDomDocument& doc, TQDomElement element )
|
|
{
|
|
m_lhs->buildXML( doc, element );
|
|
TQDomElement de = doc.createElement( "TEXT" );
|
|
de.setAttribute( "CHAR", TQString( m_type ) );
|
|
element.appendChild( de );
|
|
m_rhs->buildXML( doc, element );
|
|
}
|
|
|
|
class ExprNode : public OperatorNode {
|
|
public:
|
|
ExprNode( TQString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element );
|
|
};
|
|
|
|
void ExprNode::buildXML( TQDomDocument& doc, TQDomElement element )
|
|
{
|
|
m_lhs->buildXML( doc, element );
|
|
TQDomElement de = doc.createElement( "TEXT" );
|
|
de.setAttribute( "CHAR", TQString( m_type ) );
|
|
element.appendChild( de );
|
|
m_rhs->buildXML( doc, element );
|
|
}
|
|
|
|
class TermNode : public OperatorNode {
|
|
public:
|
|
TermNode( TQString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element );
|
|
};
|
|
|
|
void TermNode::buildXML( TQDomDocument& doc, TQDomElement element )
|
|
{
|
|
if ( m_type == "*" ) {
|
|
m_lhs->buildXML( doc, element );
|
|
TQDomElement de = doc.createElement( "TEXT" );
|
|
de.setAttribute( "CHAR", TQString( m_type ) );
|
|
element.appendChild( de );
|
|
m_rhs->buildXML( doc, element );
|
|
}
|
|
else {
|
|
TQDomElement fraction = doc.createElement( "FRACTION" );
|
|
TQDomElement numerator = doc.createElement( "NUMERATOR" );
|
|
TQDomElement sequence = doc.createElement( "SEQUENCE" );
|
|
m_lhs->buildXML( doc, sequence );
|
|
numerator.appendChild( sequence );
|
|
fraction.appendChild( numerator );
|
|
TQDomElement denominator = doc.createElement( "DENOMINATOR" );
|
|
sequence = doc.createElement( "SEQUENCE" );
|
|
m_rhs->buildXML( doc, sequence );
|
|
denominator.appendChild( sequence );
|
|
fraction.appendChild( denominator );
|
|
element.appendChild( fraction );
|
|
}
|
|
}
|
|
|
|
|
|
class PowerNode : public OperatorNode {
|
|
public:
|
|
PowerNode( TQString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element );
|
|
};
|
|
|
|
void PowerNode::buildXML( TQDomDocument& doc, TQDomElement element )
|
|
{
|
|
TQDomElement index = doc.createElement( "INDEX" );
|
|
TQDomElement content = doc.createElement( "CONTENT" );
|
|
TQDomElement sequence = doc.createElement( "SEQUENCE" );
|
|
content.appendChild( sequence );
|
|
index.appendChild( content );
|
|
|
|
if ( !m_lhs->isSimple() ) {
|
|
TQDomElement bracket = doc.createElement( "BRACKET" );
|
|
bracket.setAttribute( "LEFT", '(' );
|
|
bracket.setAttribute( "RIGHT", ')' );
|
|
sequence.appendChild( bracket );
|
|
|
|
content = doc.createElement( "CONTENT" );
|
|
bracket.appendChild( content );
|
|
|
|
sequence = doc.createElement( "SEQUENCE" );
|
|
content.appendChild( sequence );
|
|
}
|
|
m_lhs->buildXML( doc, sequence );
|
|
if ( m_type == "_" ) {
|
|
TQDomElement lowerRight = doc.createElement( "LOWERRIGHT" );
|
|
sequence = doc.createElement( "SEQUENCE" );
|
|
m_rhs->buildXML( doc, sequence );
|
|
lowerRight.appendChild( sequence );
|
|
index.appendChild( lowerRight );
|
|
}
|
|
else {
|
|
TQDomElement upperRight = doc.createElement( "UPPERRIGHT" );
|
|
sequence = doc.createElement( "SEQUENCE" );
|
|
m_rhs->buildXML( doc, sequence );
|
|
upperRight.appendChild( sequence );
|
|
index.appendChild( upperRight );
|
|
}
|
|
element.appendChild( index );
|
|
}
|
|
|
|
|
|
class FunctionNode : public ParserNode {
|
|
public:
|
|
FunctionNode( PrimaryNode* name, TQPtrList<ParserNode>& args ) : m_name( name ), m_args( args ) {
|
|
m_args.setAutoDelete( true );
|
|
}
|
|
~FunctionNode() { delete m_name; }
|
|
//virtual void output( ostream& stream );
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element );
|
|
private:
|
|
void buildSymbolXML( TQDomDocument& doc, TQDomElement element, KFormula::SymbolType type );
|
|
PrimaryNode* m_name;
|
|
TQPtrList<ParserNode> m_args;
|
|
};
|
|
|
|
void FunctionNode::buildSymbolXML( TQDomDocument& doc, TQDomElement element, KFormula::SymbolType type )
|
|
{
|
|
TQDomElement symbol = doc.createElement( "SYMBOL" );
|
|
symbol.setAttribute( "TYPE", type );
|
|
TQDomElement content = doc.createElement( "CONTENT" );
|
|
TQDomElement sequence = doc.createElement( "SEQUENCE" );
|
|
m_args.at( 0 )->buildXML( doc, sequence );
|
|
content.appendChild( sequence );
|
|
symbol.appendChild( content );
|
|
if ( m_args.count() > 2 ) {
|
|
ParserNode* lowerLimit = m_args.at( m_args.count()-2 );
|
|
ParserNode* upperLimit = m_args.at( m_args.count()-1 );
|
|
|
|
TQDomElement lower = doc.createElement( "LOWER" );
|
|
sequence = doc.createElement( "SEQUENCE" );
|
|
lowerLimit->buildXML( doc, sequence );
|
|
lower.appendChild( sequence );
|
|
symbol.appendChild( lower );
|
|
|
|
TQDomElement upper = doc.createElement( "UPPER" );
|
|
sequence = doc.createElement( "SEQUENCE" );
|
|
upperLimit->buildXML( doc, sequence );
|
|
upper.appendChild( sequence );
|
|
symbol.appendChild( upper );
|
|
}
|
|
element.appendChild( symbol );
|
|
}
|
|
|
|
void FunctionNode::buildXML( TQDomDocument& doc, TQDomElement element )
|
|
{
|
|
if ( ( m_name->primary() == "sqrt" ) && ( m_args.count() == 1 ) ) {
|
|
TQDomElement root = doc.createElement( "ROOT" );
|
|
TQDomElement content = doc.createElement( "CONTENT" );
|
|
TQDomElement sequence = doc.createElement( "SEQUENCE" );
|
|
m_args.at( 0 )->buildXML( doc, sequence );
|
|
content.appendChild( sequence );
|
|
root.appendChild( content );
|
|
element.appendChild( root );
|
|
}
|
|
else if ( ( m_name->primary() == "pow" ) && ( m_args.count() == 2 ) ) {
|
|
TQDomElement index = doc.createElement( "INDEX" );
|
|
TQDomElement content = doc.createElement( "CONTENT" );
|
|
TQDomElement sequence = doc.createElement( "SEQUENCE" );
|
|
m_args.at( 0 )->buildXML( doc, sequence );
|
|
content.appendChild( sequence );
|
|
index.appendChild( content );
|
|
TQDomElement upperRight = doc.createElement( "UPPERRIGHT" );
|
|
sequence = doc.createElement( "SEQUENCE" );
|
|
m_args.at( 1 )->buildXML( doc, sequence );
|
|
upperRight.appendChild( sequence );
|
|
index.appendChild( upperRight );
|
|
element.appendChild( index );
|
|
}
|
|
else if ( ( m_name->primary() == "sum" ) && ( m_args.count() > 0 ) ) {
|
|
buildSymbolXML( doc, element, KFormula::Sum );
|
|
}
|
|
else if ( ( m_name->primary() == "prod" ) && ( m_args.count() > 0 ) ) {
|
|
buildSymbolXML( doc, element, KFormula::Product );
|
|
}
|
|
else if ( ( ( m_name->primary() == "int" ) ||
|
|
( m_name->primary() == "integrate" ) ||
|
|
( m_name->primary() == "quad" ) )
|
|
&& ( m_args.count() > 0 ) ) {
|
|
buildSymbolXML( doc, element, KFormula::Integral );
|
|
}
|
|
else {
|
|
m_name->buildXML( doc, element );
|
|
TQDomElement bracket = doc.createElement( "BRACKET" );
|
|
bracket.setAttribute( "LEFT", '(' );
|
|
bracket.setAttribute( "RIGHT", ')' );
|
|
TQDomElement content = doc.createElement( "CONTENT" );
|
|
TQDomElement sequence = doc.createElement( "SEQUENCE" );
|
|
|
|
for ( uint i = 0; i < m_args.count(); i++ ) {
|
|
m_args.at( i )->buildXML( doc, sequence );
|
|
if ( i < m_args.count()-1 ) {
|
|
TQDomElement de = doc.createElement( "TEXT" );
|
|
de.setAttribute( "CHAR", "," );
|
|
sequence.appendChild( de );
|
|
}
|
|
}
|
|
|
|
content.appendChild( sequence );
|
|
bracket.appendChild( content );
|
|
element.appendChild( bracket );
|
|
}
|
|
}
|
|
|
|
// void FunctionNode::output( ostream& stream )
|
|
// {
|
|
// m_name->output( stream );
|
|
// for ( uint i = 0; i < m_args.count(); i++ ) {
|
|
// m_args.at( i )->output( stream );
|
|
// }
|
|
// }
|
|
|
|
class RowNode : public ParserNode {
|
|
public:
|
|
RowNode( TQPtrList<ParserNode> row ) : m_row( row ) { m_row.setAutoDelete( true ); }
|
|
//virtual void output( ostream& stream );
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element );
|
|
uint columns() const { return m_row.count(); }
|
|
void setRequiredColumns( uint requiredColumns ) { m_requiredColumns = requiredColumns; }
|
|
private:
|
|
TQPtrList<ParserNode> m_row;
|
|
uint m_requiredColumns;
|
|
};
|
|
|
|
void RowNode::buildXML( TQDomDocument& doc, TQDomElement element )
|
|
{
|
|
for ( uint i = 0; i < m_requiredColumns; i++ ) {
|
|
TQDomElement sequence = doc.createElement( "SEQUENCE" );
|
|
if ( i < m_row.count() ) {
|
|
m_row.at( i )->buildXML( doc, sequence );
|
|
}
|
|
else {
|
|
TQDomElement de = doc.createElement( "TEXT" );
|
|
de.setAttribute( "CHAR", "0" );
|
|
sequence.appendChild( de );
|
|
}
|
|
element.appendChild( sequence );
|
|
}
|
|
}
|
|
|
|
// void RowNode::output( ostream& stream )
|
|
// {
|
|
// stream << "[";
|
|
// for ( uint i = 0; i < m_row.count(); i++ ) {
|
|
// m_row.at( i )->output( stream );
|
|
// if ( i < m_row.count()-1 ) {
|
|
// stream << ", ";
|
|
// }
|
|
// }
|
|
// stream << "]";
|
|
// }
|
|
|
|
class MatrixNode : public ParserNode {
|
|
public:
|
|
MatrixNode( TQPtrList<RowNode> rows ) : m_rows( rows ) { m_rows.setAutoDelete( true ); }
|
|
//virtual void output( ostream& stream );
|
|
virtual void buildXML( TQDomDocument& doc, TQDomElement element );
|
|
virtual bool isSimple() { return true; }
|
|
uint columns();
|
|
uint rows() { return m_rows.count(); }
|
|
private:
|
|
TQPtrList<RowNode> m_rows;
|
|
};
|
|
|
|
uint MatrixNode::columns()
|
|
{
|
|
uint columns = 0;
|
|
for ( uint i = 0; i < m_rows.count(); i++ ) {
|
|
columns = TQMAX( columns, m_rows.at( i )->columns() );
|
|
}
|
|
return columns;
|
|
}
|
|
|
|
void MatrixNode::buildXML( TQDomDocument& doc, TQDomElement element )
|
|
{
|
|
TQDomElement bracket = doc.createElement( "BRACKET" );
|
|
bracket.setAttribute( "LEFT", '(' );
|
|
bracket.setAttribute( "RIGHT", ')' );
|
|
TQDomElement content = doc.createElement( "CONTENT" );
|
|
TQDomElement sequence = doc.createElement( "SEQUENCE" );
|
|
|
|
uint cols = columns();
|
|
TQDomElement matrix = doc.createElement( "MATRIX" );
|
|
matrix.setAttribute( "ROWS", m_rows.count() );
|
|
matrix.setAttribute( "COLUMNS", cols );
|
|
for ( uint i = 0; i < m_rows.count(); i++ ) {
|
|
m_rows.at( i )->setRequiredColumns( cols );
|
|
m_rows.at( i )->buildXML( doc, matrix );
|
|
matrix.appendChild( doc.createComment( "end of row" ) );
|
|
}
|
|
sequence.appendChild( matrix );
|
|
content.appendChild( sequence );
|
|
bracket.appendChild( content );
|
|
element.appendChild( bracket );
|
|
}
|
|
|
|
// void MatrixNode::output( ostream& stream )
|
|
// {
|
|
// stream << "[";
|
|
// for ( uint i = 0; i < m_rows.count(); i++ ) {
|
|
// m_rows.at( i )->output( stream );
|
|
// if ( i < m_rows.count()-1 ) {
|
|
// stream << ", ";
|
|
// }
|
|
// }
|
|
// stream << "]";
|
|
// }
|
|
|
|
|
|
FormulaStringParser::FormulaStringParser( const KFormula::SymbolTable& symbolTable, TQString formula )
|
|
: m_symbolTable( symbolTable ), m_formula( formula ),
|
|
pos( 0 ), line( 1 ), column( 1 ), m_newlineIsSpace( true )
|
|
{
|
|
}
|
|
|
|
FormulaStringParser::~FormulaStringParser()
|
|
{
|
|
delete head;
|
|
if ( ParserNode::debugCount != 0 ) {
|
|
kdDebug( KFormula::DEBUGID ) << "ParserNode::debugCount = " << ParserNode::debugCount << endl;
|
|
}
|
|
}
|
|
|
|
TQDomDocument FormulaStringParser::parse()
|
|
{
|
|
nextToken();
|
|
head = parseAssign();
|
|
//head->output( cout );
|
|
if ( !eol() ) {
|
|
error( TQString( i18n( "Aborted parsing at %1:%2" ) ).arg( line ).arg( column ) );
|
|
}
|
|
|
|
TQDomDocument doc = KFormula::Document::createDomDocument();
|
|
TQDomElement root = doc.documentElement();
|
|
TQDomElement de = doc.createElement( "FORMULA" );
|
|
// here comes the current version of FormulaElement
|
|
//de.setAttribute( "VERSION", "4" );
|
|
head->buildXML( doc, de );
|
|
root.appendChild(de);
|
|
|
|
kdDebug( 39001 ) << doc.toString() << endl;
|
|
return doc;
|
|
}
|
|
|
|
ParserNode* FormulaStringParser::parseAssign()
|
|
{
|
|
ParserNode* lhs = parseExpr();
|
|
for ( ;; ) {
|
|
switch ( currentType ) {
|
|
case ASSIGN: {
|
|
TQString c = current;
|
|
nextToken();
|
|
lhs = new AssignNode( c, lhs, parseExpr() );
|
|
break;
|
|
}
|
|
default:
|
|
return lhs;
|
|
}
|
|
}
|
|
}
|
|
|
|
ParserNode* FormulaStringParser::parseExpr()
|
|
{
|
|
ParserNode* lhs = parseTerm();
|
|
for ( ;; ) {
|
|
switch ( currentType ) {
|
|
case PLUS:
|
|
case SUB: {
|
|
TQString c = current;
|
|
nextToken();
|
|
lhs = new ExprNode( c, lhs, parseTerm() );
|
|
break;
|
|
}
|
|
default:
|
|
return lhs;
|
|
}
|
|
}
|
|
}
|
|
|
|
ParserNode* FormulaStringParser::parseTerm()
|
|
{
|
|
ParserNode* lhs = parsePower();
|
|
for ( ;; ) {
|
|
switch ( currentType ) {
|
|
case MUL:
|
|
case DIV: {
|
|
TQString c = current;
|
|
nextToken();
|
|
lhs = new TermNode( c, lhs, parsePower() );
|
|
break;
|
|
}
|
|
default:
|
|
return lhs;
|
|
}
|
|
}
|
|
}
|
|
|
|
ParserNode* FormulaStringParser::parsePower()
|
|
{
|
|
ParserNode* lhs = parsePrimary();
|
|
for ( ;; ) {
|
|
switch ( currentType ) {
|
|
case INDEX:
|
|
case POW: {
|
|
TQString c = current;
|
|
nextToken();
|
|
lhs = new PowerNode( c, lhs, parsePrimary() );
|
|
break;
|
|
}
|
|
default:
|
|
return lhs;
|
|
}
|
|
}
|
|
}
|
|
|
|
ParserNode* FormulaStringParser::parsePrimary()
|
|
{
|
|
switch ( currentType ) {
|
|
case NUMBER: {
|
|
PrimaryNode* node = new PrimaryNode( current );
|
|
nextToken();
|
|
return node;
|
|
}
|
|
case NAME: {
|
|
PrimaryNode* node = new PrimaryNode( current );
|
|
node->setUnicode( m_symbolTable.unicode( current ) );
|
|
nextToken();
|
|
if ( currentType == LP ) {
|
|
nextToken();
|
|
TQPtrList<ParserNode> args;
|
|
args.setAutoDelete( false );
|
|
while ( ( currentType != EOL ) && ( currentType != RP ) ) {
|
|
ParserNode* node = parseExpr();
|
|
args.append( node );
|
|
if ( currentType == COMMA ) {
|
|
nextToken();
|
|
}
|
|
}
|
|
expect( RP, TQString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( ")" ) );
|
|
node->setFunctionName( true );
|
|
return new FunctionNode( node, args );
|
|
}
|
|
return node;
|
|
}
|
|
case SUB: {
|
|
nextToken();
|
|
//ParserNode* node = new UnaryMinus( parsePrimary() );
|
|
ParserNode* node = new UnaryMinus( parseTerm() );
|
|
return node;
|
|
}
|
|
case LP: {
|
|
nextToken();
|
|
ParserNode* node = parseExpr();
|
|
expect( RP, TQString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( ")" ) );
|
|
return node;
|
|
}
|
|
case LB: {
|
|
nextToken();
|
|
TQPtrList<RowNode> rows;
|
|
rows.setAutoDelete( false );
|
|
bool innerBrackets = currentType == LB;
|
|
m_newlineIsSpace = innerBrackets;
|
|
while ( ( currentType != EOL ) && ( currentType != RB ) ) {
|
|
if ( innerBrackets ) {
|
|
expect( LB, TQString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( "[" ) );
|
|
}
|
|
TQPtrList<ParserNode> row;
|
|
row.setAutoDelete( false );
|
|
while ( ( currentType != EOL ) && ( currentType != RB ) &&
|
|
( innerBrackets || ( currentType != SEMIC && currentType != NEWLINE ) ) ) {
|
|
row.append( parseExpr() );
|
|
if ( currentType == COMMA ) {
|
|
nextToken();
|
|
}
|
|
}
|
|
if ( innerBrackets ) {
|
|
expect( RB, TQString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( "]" ) );
|
|
if ( currentType == COMMA ) {
|
|
nextToken();
|
|
}
|
|
}
|
|
else {
|
|
if ( currentType != RB ) {
|
|
if ( currentType == NEWLINE ) {
|
|
nextToken();
|
|
}
|
|
else {
|
|
expect( SEMIC, TQString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( ";" ) );
|
|
}
|
|
}
|
|
}
|
|
rows.append( new RowNode( row ) );
|
|
}
|
|
m_newlineIsSpace = true;
|
|
expect( RB, TQString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( "]" ) );
|
|
MatrixNode* node = new MatrixNode( rows );
|
|
if ( node->columns() == 0 ) {
|
|
error( TQString( i18n( "Null columns in Matrix at %1:%2" ) ).arg( line ).arg( column ) );
|
|
}
|
|
if ( node->rows() == 0 ) {
|
|
error( TQString( i18n( "Null rows in Matrix at %1:%2" ) ).arg( line ).arg( column ) );
|
|
}
|
|
return node;
|
|
}
|
|
case OTHER: {
|
|
ParserNode* node = new PrimaryNode( current );
|
|
nextToken();
|
|
return node;
|
|
}
|
|
default:
|
|
error( TQString( i18n( "Unexpected token at %1:%2" ) ).arg( line ).arg( column ) );
|
|
return new PrimaryNode( "?" );
|
|
}
|
|
}
|
|
|
|
void FormulaStringParser::expect( TokenType type, TQString msg )
|
|
{
|
|
if ( currentType == type ) {
|
|
nextToken();
|
|
}
|
|
else {
|
|
error( msg );
|
|
}
|
|
}
|
|
|
|
TQString FormulaStringParser::nextToken()
|
|
{
|
|
// We skip any ' or " so that we can parse string literals.
|
|
while ( !eol() && ( m_formula[pos].isSpace() ||
|
|
( m_formula[pos] == '"' ) ||
|
|
( m_formula[pos] == '\'' ) ) ) {
|
|
if ( m_formula[pos] == '\n' ) {
|
|
line++;
|
|
if ( m_newlineIsSpace ) {
|
|
column = 0;
|
|
}
|
|
else {
|
|
pos++;
|
|
column = 1;
|
|
currentType = NEWLINE;
|
|
return current = "\n";
|
|
}
|
|
}
|
|
pos++; column++;
|
|
}
|
|
if ( eol() ) {
|
|
currentType = EOL;
|
|
return TQString();
|
|
}
|
|
if ( m_formula[pos].isDigit() || m_formula[pos] == '.' ) {
|
|
uint begin = pos;
|
|
readNumber();
|
|
currentType = NUMBER;
|
|
current = m_formula.mid( begin, pos-begin );
|
|
if ( current[0] == '.' ) {
|
|
current = "0" + current;
|
|
}
|
|
if ( current[current.length()-1] == '.' ) {
|
|
current = current + "0";
|
|
}
|
|
return current;
|
|
}
|
|
else if ( m_formula[pos].isLetter() ) {
|
|
uint begin = pos;
|
|
pos++; column++;
|
|
while ( !eol() && m_formula[pos].isLetter() ) {
|
|
pos++; column++;
|
|
}
|
|
currentType = NAME;
|
|
return current = m_formula.mid( begin, pos-begin );
|
|
}
|
|
else {
|
|
switch ( m_formula[pos].latin1() ) {
|
|
case '+':
|
|
pos++; column++;
|
|
currentType = PLUS;
|
|
return current = "+";
|
|
case '-':
|
|
pos++; column++;
|
|
currentType = SUB;
|
|
return current = "-";
|
|
case '*':
|
|
pos++; column++;
|
|
if ( !eol() && m_formula[pos] == '*' ) {
|
|
pos++; column++;
|
|
currentType = POW;
|
|
return current = "**";
|
|
}
|
|
currentType = MUL;
|
|
return current = "*";
|
|
case '/':
|
|
pos++; column++;
|
|
currentType = DIV;
|
|
return current = "/";
|
|
case '^':
|
|
pos++; column++;
|
|
currentType = POW;
|
|
return current = "**";
|
|
case '_':
|
|
pos++; column++;
|
|
currentType = INDEX;
|
|
return current = "_";
|
|
case '(':
|
|
pos++; column++;
|
|
currentType = LP;
|
|
return current = "(";
|
|
case ')':
|
|
pos++; column++;
|
|
currentType = RP;
|
|
return current = ")";
|
|
case '[':
|
|
pos++; column++;
|
|
currentType = LB;
|
|
return current = "[";
|
|
case ']':
|
|
pos++; column++;
|
|
currentType = RB;
|
|
return current = "]";
|
|
case ',':
|
|
pos++; column++;
|
|
currentType = COMMA;
|
|
return current = ",";
|
|
case ';':
|
|
pos++; column++;
|
|
currentType = SEMIC;
|
|
return current = ";";
|
|
case '=':
|
|
pos++; column++;
|
|
currentType = ASSIGN;
|
|
return current = "=";
|
|
default:
|
|
pos++; column++;
|
|
currentType = OTHER;
|
|
return current = m_formula.mid( pos-1, 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FormulaStringParser::readNumber()
|
|
{
|
|
bool digitsBeforeDot = m_formula[pos] != '.';
|
|
|
|
readDigits();
|
|
if ( pos < m_formula.length()-1 ) {
|
|
TQChar ch = m_formula[pos];
|
|
|
|
// Look for a dot.
|
|
if ( ch == '.' ) {
|
|
pos++;
|
|
column++;
|
|
ch = m_formula[pos];
|
|
if ( ch.isDigit() ) {
|
|
readDigits();
|
|
}
|
|
else if ( !digitsBeforeDot ) {
|
|
error( TQString( i18n( "A single '.' is not a number at %1:%2" ) ).arg( line ).arg( column ) );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// there might as well be an exponent
|
|
if ( pos < m_formula.length()-1 ) {
|
|
ch = m_formula[pos];
|
|
if ( ( ch == 'E' ) || ( ch == 'e' ) ) {
|
|
pos++;
|
|
column++;
|
|
ch = m_formula[pos];
|
|
|
|
// signs are allowed after the exponent
|
|
if ( ( ( ch == '+' ) || ( ch == '-' ) ) &&
|
|
( pos < m_formula.length()-1 ) ) {
|
|
pos++;
|
|
column++;
|
|
ch = m_formula[pos];
|
|
if ( ch.isDigit() ) {
|
|
readDigits();
|
|
}
|
|
else {
|
|
pos -= 2;
|
|
column -= 2;
|
|
return;
|
|
}
|
|
}
|
|
else if ( ch.isDigit() ) {
|
|
readDigits();
|
|
}
|
|
else {
|
|
pos--;
|
|
column--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FormulaStringParser::readDigits()
|
|
{
|
|
while ( !eol() && m_formula[pos].isDigit() ) {
|
|
pos++;
|
|
column++;
|
|
}
|
|
}
|
|
|
|
void FormulaStringParser::error( TQString err )
|
|
{
|
|
kdDebug( KFormula::DEBUGID ) << err << " (" << currentType << "; " << current << ")" << endl;
|
|
m_errorList.push_back( err );
|
|
}
|