1
/* This file is part of the KDE project
2
Copyright (C) 2002 Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
4
This library is free software; you can redistribute it and/or
5
modify it under the terms of the GNU Library General Public
6
License as published by the Free Software Foundation; either
7
version 2 of the License, or (at your option) any later version.
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
Library General Public License for more details.
14
You should have received a copy of the GNU Library General Public License
15
along with this library; see the file COPYING.LIB. If not, write to
16
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
Boston, MA 02111-1307, USA.
25
#include <kformuladefs.h>
26
#include <kformuladocument.h>
27
#include <symboltable.h>
36
ParserNode() { debugCount++; }
37
virtual ~ParserNode() { debugCount--; }
38
//virtual void output( ostream& ) = 0;
39
virtual void buildXML( QDomDocument doc, QDomElement element ) = 0;
40
virtual bool isSimple() { return false; }
42
static int debugCount;
45
int ParserNode::debugCount = 0;
47
class PrimaryNode : public ParserNode {
49
PrimaryNode( QString primary ) : m_primary( primary ), m_functionName( false ) {}
50
//virtual void output( ostream& stream ) { stream << "PrimaryNode {" << m_primary << "}" << endl; }
51
virtual void buildXML( QDomDocument doc, QDomElement element );
52
virtual bool isSimple() { return true; }
53
void setUnicode( QChar unicode ) { m_unicode = unicode; }
54
void setFunctionName( bool functionName ) { m_functionName = functionName; }
55
QString primary() const { return m_primary; }
62
void PrimaryNode::buildXML( QDomDocument doc, QDomElement element )
64
if ( m_unicode != QChar::null ) {
65
QDomElement de = doc.createElement( "TEXT" );
66
de.setAttribute( "CHAR", QString( m_unicode ) );
67
de.setAttribute( "SYMBOL", "3" );
68
element.appendChild( de );
71
if ( m_functionName ) {
72
QDomElement namesequence = doc.createElement( "NAMESEQUENCE" );
73
element.appendChild( namesequence );
74
element = namesequence;
76
for ( uint i = 0; i < m_primary.length(); i++ ) {
77
QDomElement de = doc.createElement( "TEXT" );
78
de.setAttribute( "CHAR", QString( m_primary[i] ) );
79
element.appendChild( de );
84
class UnaryMinus : public ParserNode {
86
UnaryMinus( ParserNode* primary ) : m_primary( primary ) {}
87
~UnaryMinus() { delete m_primary; }
88
virtual void buildXML( QDomDocument doc, QDomElement element );
90
ParserNode* m_primary;
93
void UnaryMinus::buildXML( QDomDocument doc, QDomElement element )
95
QDomElement de = doc.createElement( "TEXT" );
96
de.setAttribute( "CHAR", "-" );
97
element.appendChild( de );
98
m_primary->buildXML( doc, element );
101
class OperatorNode : public ParserNode {
103
OperatorNode( QString type, ParserNode* lhs, ParserNode* rhs )
104
: m_type( type ), m_lhs( lhs ), m_rhs( rhs ) {}
105
~OperatorNode() { delete m_rhs; delete m_lhs; }
106
// virtual void output( ostream& stream ) {
107
// stream << "OperatorNode {";
108
// m_lhs->output( stream ); stream << m_type; m_rhs->output( stream );
109
// stream << "}" << endl; }
116
class AssignNode : public OperatorNode {
118
AssignNode( QString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
119
virtual void buildXML( QDomDocument doc, QDomElement element );
122
void AssignNode::buildXML( QDomDocument doc, QDomElement element )
124
m_lhs->buildXML( doc, element );
125
QDomElement de = doc.createElement( "TEXT" );
126
de.setAttribute( "CHAR", QString( m_type ) );
127
element.appendChild( de );
128
m_rhs->buildXML( doc, element );
131
class ExprNode : public OperatorNode {
133
ExprNode( QString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
134
virtual void buildXML( QDomDocument doc, QDomElement element );
137
void ExprNode::buildXML( QDomDocument doc, QDomElement element )
139
m_lhs->buildXML( doc, element );
140
QDomElement de = doc.createElement( "TEXT" );
141
de.setAttribute( "CHAR", QString( m_type ) );
142
element.appendChild( de );
143
m_rhs->buildXML( doc, element );
146
class TermNode : public OperatorNode {
148
TermNode( QString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
149
virtual void buildXML( QDomDocument doc, QDomElement element );
152
void TermNode::buildXML( QDomDocument doc, QDomElement element )
154
if ( m_type == "*" ) {
155
m_lhs->buildXML( doc, element );
156
QDomElement de = doc.createElement( "TEXT" );
157
de.setAttribute( "CHAR", QString( m_type ) );
158
element.appendChild( de );
159
m_rhs->buildXML( doc, element );
162
QDomElement fraction = doc.createElement( "FRACTION" );
163
QDomElement numerator = doc.createElement( "NUMERATOR" );
164
QDomElement sequence = doc.createElement( "SEQUENCE" );
165
m_lhs->buildXML( doc, sequence );
166
numerator.appendChild( sequence );
167
fraction.appendChild( numerator );
168
QDomElement denominator = doc.createElement( "DENOMINATOR" );
169
sequence = doc.createElement( "SEQUENCE" );
170
m_rhs->buildXML( doc, sequence );
171
denominator.appendChild( sequence );
172
fraction.appendChild( denominator );
173
element.appendChild( fraction );
178
class PowerNode : public OperatorNode {
180
PowerNode( QString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
181
virtual void buildXML( QDomDocument doc, QDomElement element );
184
void PowerNode::buildXML( QDomDocument doc, QDomElement element )
186
QDomElement index = doc.createElement( "INDEX" );
187
QDomElement content = doc.createElement( "CONTENT" );
188
QDomElement sequence = doc.createElement( "SEQUENCE" );
189
content.appendChild( sequence );
190
index.appendChild( content );
192
if ( !m_lhs->isSimple() ) {
193
QDomElement bracket = doc.createElement( "BRACKET" );
194
bracket.setAttribute( "LEFT", '(' );
195
bracket.setAttribute( "RIGHT", ')' );
196
sequence.appendChild( bracket );
198
content = doc.createElement( "CONTENT" );
199
bracket.appendChild( content );
201
sequence = doc.createElement( "SEQUENCE" );
202
content.appendChild( sequence );
204
m_lhs->buildXML( doc, sequence );
205
if ( m_type == "_" ) {
206
QDomElement lowerRight = doc.createElement( "LOWERRIGHT" );
207
sequence = doc.createElement( "SEQUENCE" );
208
m_rhs->buildXML( doc, sequence );
209
lowerRight.appendChild( sequence );
210
index.appendChild( lowerRight );
213
QDomElement upperRight = doc.createElement( "UPPERRIGHT" );
214
sequence = doc.createElement( "SEQUENCE" );
215
m_rhs->buildXML( doc, sequence );
216
upperRight.appendChild( sequence );
217
index.appendChild( upperRight );
219
element.appendChild( index );
223
class FunctionNode : public ParserNode {
225
FunctionNode( PrimaryNode* name, QPtrList<ParserNode>& args ) : m_name( name ), m_args( args ) {
226
m_args.setAutoDelete( true );
228
~FunctionNode() { delete m_name; }
229
//virtual void output( ostream& stream );
230
virtual void buildXML( QDomDocument doc, QDomElement element );
232
void buildSymbolXML( QDomDocument doc, QDomElement element, KFormula::SymbolType type );
234
QPtrList<ParserNode> m_args;
237
void FunctionNode::buildSymbolXML( QDomDocument doc, QDomElement element, KFormula::SymbolType type )
239
QDomElement symbol = doc.createElement( "SYMBOL" );
240
symbol.setAttribute( "TYPE", type );
241
QDomElement content = doc.createElement( "CONTENT" );
242
QDomElement sequence = doc.createElement( "SEQUENCE" );
243
m_args.at( 0 )->buildXML( doc, sequence );
244
content.appendChild( sequence );
245
symbol.appendChild( content );
246
if ( m_args.count() > 2 ) {
247
ParserNode* lowerLimit = m_args.at( m_args.count()-2 );
248
ParserNode* upperLimit = m_args.at( m_args.count()-1 );
250
QDomElement lower = doc.createElement( "LOWER" );
251
sequence = doc.createElement( "SEQUENCE" );
252
lowerLimit->buildXML( doc, sequence );
253
lower.appendChild( sequence );
254
symbol.appendChild( lower );
256
QDomElement upper = doc.createElement( "UPPER" );
257
sequence = doc.createElement( "SEQUENCE" );
258
upperLimit->buildXML( doc, sequence );
259
upper.appendChild( sequence );
260
symbol.appendChild( upper );
262
element.appendChild( symbol );
265
void FunctionNode::buildXML( QDomDocument doc, QDomElement element )
267
if ( ( m_name->primary() == "sqrt" ) && ( m_args.count() == 1 ) ) {
268
QDomElement root = doc.createElement( "ROOT" );
269
QDomElement content = doc.createElement( "CONTENT" );
270
QDomElement sequence = doc.createElement( "SEQUENCE" );
271
m_args.at( 0 )->buildXML( doc, sequence );
272
content.appendChild( sequence );
273
root.appendChild( content );
274
element.appendChild( root );
276
else if ( ( m_name->primary() == "pow" ) && ( m_args.count() == 2 ) ) {
277
QDomElement index = doc.createElement( "INDEX" );
278
QDomElement content = doc.createElement( "CONTENT" );
279
QDomElement sequence = doc.createElement( "SEQUENCE" );
280
m_args.at( 0 )->buildXML( doc, sequence );
281
content.appendChild( sequence );
282
index.appendChild( content );
283
QDomElement upperRight = doc.createElement( "UPPERRIGHT" );
284
sequence = doc.createElement( "SEQUENCE" );
285
m_args.at( 1 )->buildXML( doc, sequence );
286
upperRight.appendChild( sequence );
287
index.appendChild( upperRight );
288
element.appendChild( index );
290
else if ( ( m_name->primary() == "sum" ) && ( m_args.count() > 0 ) ) {
291
buildSymbolXML( doc, element, KFormula::Sum );
293
else if ( ( m_name->primary() == "prod" ) && ( m_args.count() > 0 ) ) {
294
buildSymbolXML( doc, element, KFormula::Product );
296
else if ( ( ( m_name->primary() == "int" ) ||
297
( m_name->primary() == "integrate" ) ||
298
( m_name->primary() == "quad" ) )
299
&& ( m_args.count() > 0 ) ) {
300
buildSymbolXML( doc, element, KFormula::Integral );
303
m_name->buildXML( doc, element );
304
QDomElement bracket = doc.createElement( "BRACKET" );
305
bracket.setAttribute( "LEFT", '(' );
306
bracket.setAttribute( "RIGHT", ')' );
307
QDomElement content = doc.createElement( "CONTENT" );
308
QDomElement sequence = doc.createElement( "SEQUENCE" );
310
for ( uint i = 0; i < m_args.count(); i++ ) {
311
m_args.at( i )->buildXML( doc, sequence );
312
if ( i < m_args.count()-1 ) {
313
QDomElement de = doc.createElement( "TEXT" );
314
de.setAttribute( "CHAR", "," );
315
sequence.appendChild( de );
319
content.appendChild( sequence );
320
bracket.appendChild( content );
321
element.appendChild( bracket );
325
// void FunctionNode::output( ostream& stream )
327
// m_name->output( stream );
328
// for ( uint i = 0; i < m_args.count(); i++ ) {
329
// m_args.at( i )->output( stream );
333
class RowNode : public ParserNode {
335
RowNode( QPtrList<ParserNode> row ) : m_row( row ) { m_row.setAutoDelete( true ); }
336
//virtual void output( ostream& stream );
337
virtual void buildXML( QDomDocument doc, QDomElement element );
338
uint columns() const { return m_row.count(); }
339
void setRequiredColumns( uint requiredColumns ) { m_requiredColumns = requiredColumns; }
341
QPtrList<ParserNode> m_row;
342
uint m_requiredColumns;
345
void RowNode::buildXML( QDomDocument doc, QDomElement element )
347
for ( uint i = 0; i < m_requiredColumns; i++ ) {
348
QDomElement sequence = doc.createElement( "SEQUENCE" );
349
if ( i < m_row.count() ) {
350
m_row.at( i )->buildXML( doc, sequence );
353
QDomElement de = doc.createElement( "TEXT" );
354
de.setAttribute( "CHAR", "0" );
355
sequence.appendChild( de );
357
element.appendChild( sequence );
361
// void RowNode::output( ostream& stream )
364
// for ( uint i = 0; i < m_row.count(); i++ ) {
365
// m_row.at( i )->output( stream );
366
// if ( i < m_row.count()-1 ) {
373
class MatrixNode : public ParserNode {
375
MatrixNode( QPtrList<RowNode> rows ) : m_rows( rows ) { m_rows.setAutoDelete( true ); }
376
//virtual void output( ostream& stream );
377
virtual void buildXML( QDomDocument doc, QDomElement element );
378
virtual bool isSimple() { return true; }
380
uint rows() { return m_rows.count(); }
382
QPtrList<RowNode> m_rows;
385
uint MatrixNode::columns()
388
for ( uint i = 0; i < m_rows.count(); i++ ) {
389
columns = QMAX( columns, m_rows.at( i )->columns() );
394
void MatrixNode::buildXML( QDomDocument doc, QDomElement element )
396
QDomElement bracket = doc.createElement( "BRACKET" );
397
bracket.setAttribute( "LEFT", '(' );
398
bracket.setAttribute( "RIGHT", ')' );
399
QDomElement content = doc.createElement( "CONTENT" );
400
QDomElement sequence = doc.createElement( "SEQUENCE" );
402
uint cols = columns();
403
QDomElement matrix = doc.createElement( "MATRIX" );
404
matrix.setAttribute( "ROWS", m_rows.count() );
405
matrix.setAttribute( "COLUMNS", cols );
406
for ( uint i = 0; i < m_rows.count(); i++ ) {
407
m_rows.at( i )->setRequiredColumns( cols );
408
m_rows.at( i )->buildXML( doc, matrix );
409
matrix.appendChild( doc.createComment( "end of row" ) );
411
sequence.appendChild( matrix );
412
content.appendChild( sequence );
413
bracket.appendChild( content );
414
element.appendChild( bracket );
417
// void MatrixNode::output( ostream& stream )
420
// for ( uint i = 0; i < m_rows.count(); i++ ) {
421
// m_rows.at( i )->output( stream );
422
// if ( i < m_rows.count()-1 ) {
430
FormulaStringParser::FormulaStringParser( const KFormula::SymbolTable& symbolTable, QString formula )
431
: m_symbolTable( symbolTable ), m_formula( formula ),
432
pos( 0 ), line( 1 ), column( 1 ), m_newlineIsSpace( true )
436
FormulaStringParser::~FormulaStringParser()
439
if ( ParserNode::debugCount != 0 ) {
440
kdDebug( KFormula::DEBUGID ) << "ParserNode::debugCount = " << ParserNode::debugCount << endl;
444
QDomDocument FormulaStringParser::parse()
447
head = parseAssign();
448
//head->output( cout );
450
error( QString( i18n( "Aborted parsing at %1:%2" ) ).arg( line ).arg( column ) );
453
QDomDocument doc = KFormula::Document::createDomDocument();
454
QDomElement root = doc.documentElement();
455
QDomElement de = doc.createElement( "FORMULA" );
456
// here comes the current version of FormulaElement
457
//de.setAttribute( "VERSION", "4" );
458
head->buildXML( doc, de );
459
root.appendChild(de);
461
kdDebug( 39001 ) << doc.toString() << endl;
465
ParserNode* FormulaStringParser::parseAssign()
467
ParserNode* lhs = parseExpr();
469
switch ( currentType ) {
473
lhs = new AssignNode( c, lhs, parseExpr() );
482
ParserNode* FormulaStringParser::parseExpr()
484
ParserNode* lhs = parseTerm();
486
switch ( currentType ) {
491
lhs = new ExprNode( c, lhs, parseTerm() );
500
ParserNode* FormulaStringParser::parseTerm()
502
ParserNode* lhs = parsePower();
504
switch ( currentType ) {
509
lhs = new TermNode( c, lhs, parsePower() );
518
ParserNode* FormulaStringParser::parsePower()
520
ParserNode* lhs = parsePrimary();
522
switch ( currentType ) {
527
lhs = new PowerNode( c, lhs, parsePrimary() );
536
ParserNode* FormulaStringParser::parsePrimary()
538
switch ( currentType ) {
540
PrimaryNode* node = new PrimaryNode( current );
545
PrimaryNode* node = new PrimaryNode( current );
546
node->setUnicode( m_symbolTable.unicode( current ) );
548
if ( currentType == LP ) {
550
QPtrList<ParserNode> args;
551
args.setAutoDelete( false );
552
while ( ( currentType != EOL ) && ( currentType != RP ) ) {
553
ParserNode* node = parseExpr();
555
if ( currentType == COMMA ) {
559
expect( RP, QString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( ")" ) );
560
node->setFunctionName( true );
561
return new FunctionNode( node, args );
567
//ParserNode* node = new UnaryMinus( parsePrimary() );
568
ParserNode* node = new UnaryMinus( parseTerm() );
573
ParserNode* node = parseExpr();
574
expect( RP, QString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( ")" ) );
579
QPtrList<RowNode> rows;
580
rows.setAutoDelete( false );
581
bool innerBrackets = currentType == LB;
582
m_newlineIsSpace = innerBrackets;
583
while ( ( currentType != EOL ) && ( currentType != RB ) ) {
584
if ( innerBrackets ) {
585
expect( LB, QString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( "[" ) );
587
QPtrList<ParserNode> row;
588
row.setAutoDelete( false );
589
while ( ( currentType != EOL ) && ( currentType != RB ) &&
590
( innerBrackets || ( currentType != SEMIC && currentType != NEWLINE ) ) ) {
591
row.append( parseExpr() );
592
if ( currentType == COMMA ) {
596
if ( innerBrackets ) {
597
expect( RB, QString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( "]" ) );
598
if ( currentType == COMMA ) {
603
if ( currentType != RB ) {
604
if ( currentType == NEWLINE ) {
608
expect( SEMIC, QString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( ";" ) );
612
rows.append( new RowNode( row ) );
614
m_newlineIsSpace = true;
615
expect( RB, QString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( "]" ) );
616
MatrixNode* node = new MatrixNode( rows );
617
if ( node->columns() == 0 ) {
618
error( QString( i18n( "Null columns in Matrix at %1:%2" ) ).arg( line ).arg( column ) );
620
if ( node->rows() == 0 ) {
621
error( QString( i18n( "Null rows in Matrix at %1:%2" ) ).arg( line ).arg( column ) );
626
ParserNode* node = new PrimaryNode( current );
631
error( QString( i18n( "Unexpected token at %1:%2" ) ).arg( line ).arg( column ) );
632
return new PrimaryNode( "?" );
636
void FormulaStringParser::expect( TokenType type, QString msg )
638
if ( currentType == type ) {
646
QString FormulaStringParser::nextToken()
648
// We skip any ' or " so that we can parse string literals.
649
while ( !eol() && ( m_formula[pos].isSpace() ||
650
( m_formula[pos] == '"' ) ||
651
( m_formula[pos] == '\'' ) ) ) {
652
if ( m_formula[pos] == '\n' ) {
654
if ( m_newlineIsSpace ) {
660
currentType = NEWLINE;
661
return current = "\n";
668
return QString::null;
670
if ( m_formula[pos].isDigit() || m_formula[pos] == '.' ) {
673
currentType = NUMBER;
674
current = m_formula.mid( begin, pos-begin );
675
if ( current[0] == '.' ) {
676
current = "0" + current;
678
if ( current[current.length()-1] == '.' ) {
679
current = current + "0";
683
else if ( m_formula[pos].isLetter() ) {
686
while ( !eol() && m_formula[pos].isLetter() ) {
690
return current = m_formula.mid( begin, pos-begin );
693
switch ( m_formula[pos].latin1() ) {
697
return current = "+";
701
return current = "-";
704
if ( !eol() && m_formula[pos] == '*' ) {
707
return current = "**";
710
return current = "*";
714
return current = "/";
718
return current = "**";
722
return current = "_";
726
return current = "(";
730
return current = ")";
734
return current = "[";
738
return current = "]";
742
return current = ",";
746
return current = ";";
749
currentType = ASSIGN;
750
return current = "=";
754
return current = m_formula.mid( pos-1, 1 );
759
void FormulaStringParser::readNumber()
761
bool digitsBeforeDot = m_formula[pos] != '.';
764
if ( pos < m_formula.length()-1 ) {
765
QChar ch = m_formula[pos];
772
if ( ch.isDigit() ) {
775
else if ( !digitsBeforeDot ) {
776
error( QString( i18n( "A single '.' is not a number at %1:%2" ) ).arg( line ).arg( column ) );
781
// there might as well be an exponent
782
if ( pos < m_formula.length()-1 ) {
784
if ( ( ch == 'E' ) || ( ch == 'e' ) ) {
789
// signs are allowed after the exponent
790
if ( ( ( ch == '+' ) || ( ch == '-' ) ) &&
791
( pos < m_formula.length()-1 ) ) {
795
if ( ch.isDigit() ) {
804
else if ( ch.isDigit() ) {
817
void FormulaStringParser::readDigits()
819
while ( !eol() && m_formula[pos].isDigit() ) {
825
void FormulaStringParser::error( QString err )
827
kdDebug( KFormula::DEBUGID ) << err << " (" << currentType << "; " << current << ")" << endl;
828
m_errorList.push_back( err );