~ubuntu-branches/ubuntu/edgy/koffice/edgy-updates

« back to all changes in this revision

Viewing changes to kformula/fsparser.cc

  • Committer: Bazaar Package Importer
  • Author(s): Ben Burton
  • Date: 2004-05-09 11:33:00 UTC
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20040509113300-xi5t1z4yxe7n03x7
Tags: upstream-1.3.1
ImportĀ upstreamĀ versionĀ 1.3.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of the KDE project
 
2
   Copyright (C) 2002 Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
 
3
 
 
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.
 
8
 
 
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.
 
13
 
 
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.
 
18
*/
 
19
 
 
20
#include <qptrlist.h>
 
21
 
 
22
#include <kdebug.h>
 
23
#include <klocale.h>
 
24
 
 
25
#include <kformuladefs.h>
 
26
#include <kformuladocument.h>
 
27
#include <symboltable.h>
 
28
 
 
29
#include "fsparser.h"
 
30
 
 
31
 
 
32
using namespace std;
 
33
 
 
34
class ParserNode {
 
35
public:
 
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; }
 
41
 
 
42
    static int debugCount;
 
43
};
 
44
 
 
45
int ParserNode::debugCount = 0;
 
46
 
 
47
class PrimaryNode : public ParserNode {
 
48
public:
 
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; }
 
56
private:
 
57
    QString m_primary;
 
58
    QChar m_unicode;
 
59
    bool m_functionName;
 
60
};
 
61
 
 
62
void PrimaryNode::buildXML( QDomDocument doc, QDomElement element )
 
63
{
 
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 );
 
69
    }
 
70
    else {
 
71
        if ( m_functionName ) {
 
72
            QDomElement namesequence = doc.createElement( "NAMESEQUENCE" );
 
73
            element.appendChild( namesequence );
 
74
            element = namesequence;
 
75
        }
 
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 );
 
80
        }
 
81
    }
 
82
}
 
83
 
 
84
class UnaryMinus : public ParserNode {
 
85
public:
 
86
    UnaryMinus( ParserNode* primary ) : m_primary( primary ) {}
 
87
    ~UnaryMinus() { delete m_primary; }
 
88
    virtual void buildXML( QDomDocument doc, QDomElement element );
 
89
private:
 
90
    ParserNode* m_primary;
 
91
};
 
92
 
 
93
void UnaryMinus::buildXML( QDomDocument doc, QDomElement element )
 
94
{
 
95
    QDomElement de = doc.createElement( "TEXT" );
 
96
    de.setAttribute( "CHAR", "-" );
 
97
    element.appendChild( de );
 
98
    m_primary->buildXML( doc, element );
 
99
}
 
100
 
 
101
class OperatorNode : public ParserNode {
 
102
public:
 
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; }
 
110
protected:
 
111
    QString m_type;
 
112
    ParserNode* m_lhs;
 
113
    ParserNode* m_rhs;
 
114
};
 
115
 
 
116
class AssignNode : public OperatorNode {
 
117
public:
 
118
    AssignNode( QString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
 
119
    virtual void buildXML( QDomDocument doc, QDomElement element );
 
120
};
 
121
 
 
122
void AssignNode::buildXML( QDomDocument doc, QDomElement element )
 
123
{
 
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 );
 
129
}
 
130
 
 
131
class ExprNode : public OperatorNode {
 
132
public:
 
133
    ExprNode( QString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
 
134
    virtual void buildXML( QDomDocument doc, QDomElement element );
 
135
};
 
136
 
 
137
void ExprNode::buildXML( QDomDocument doc, QDomElement element )
 
138
{
 
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 );
 
144
}
 
145
 
 
146
class TermNode : public OperatorNode {
 
147
public:
 
148
    TermNode( QString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
 
149
    virtual void buildXML( QDomDocument doc, QDomElement element );
 
150
};
 
151
 
 
152
void TermNode::buildXML( QDomDocument doc, QDomElement element )
 
153
{
 
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 );
 
160
    }
 
161
    else {
 
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 );
 
174
    }
 
175
}
 
176
 
 
177
 
 
178
class PowerNode : public OperatorNode {
 
179
public:
 
180
    PowerNode( QString type, ParserNode* lhs, ParserNode* rhs ) : OperatorNode( type, lhs, rhs ) {}
 
181
    virtual void buildXML( QDomDocument doc, QDomElement element );
 
182
};
 
183
 
 
184
void PowerNode::buildXML( QDomDocument doc, QDomElement element )
 
185
{
 
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 );
 
191
 
 
192
    if ( !m_lhs->isSimple() ) {
 
193
        QDomElement bracket = doc.createElement( "BRACKET" );
 
194
        bracket.setAttribute( "LEFT", '(' );
 
195
        bracket.setAttribute( "RIGHT", ')' );
 
196
        sequence.appendChild( bracket );
 
197
 
 
198
        content = doc.createElement( "CONTENT" );
 
199
        bracket.appendChild( content );
 
200
 
 
201
        sequence = doc.createElement( "SEQUENCE" );
 
202
        content.appendChild( sequence );
 
203
    }
 
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 );
 
211
    }
 
212
    else {
 
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 );
 
218
    }
 
219
    element.appendChild( index );
 
220
}
 
221
 
 
222
 
 
223
class FunctionNode : public ParserNode {
 
224
public:
 
225
    FunctionNode( PrimaryNode* name, QPtrList<ParserNode>& args ) : m_name( name ), m_args( args ) {
 
226
        m_args.setAutoDelete( true );
 
227
    }
 
228
    ~FunctionNode() { delete m_name; }
 
229
    //virtual void output( ostream& stream );
 
230
    virtual void buildXML( QDomDocument doc, QDomElement element );
 
231
private:
 
232
    void buildSymbolXML( QDomDocument doc, QDomElement element, KFormula::SymbolType type );
 
233
    PrimaryNode* m_name;
 
234
    QPtrList<ParserNode> m_args;
 
235
};
 
236
 
 
237
void FunctionNode::buildSymbolXML( QDomDocument doc, QDomElement element, KFormula::SymbolType type )
 
238
{
 
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 );
 
249
 
 
250
        QDomElement lower = doc.createElement( "LOWER" );
 
251
        sequence = doc.createElement( "SEQUENCE" );
 
252
        lowerLimit->buildXML( doc, sequence );
 
253
        lower.appendChild( sequence );
 
254
        symbol.appendChild( lower );
 
255
 
 
256
        QDomElement upper = doc.createElement( "UPPER" );
 
257
        sequence = doc.createElement( "SEQUENCE" );
 
258
        upperLimit->buildXML( doc, sequence );
 
259
        upper.appendChild( sequence );
 
260
        symbol.appendChild( upper );
 
261
    }
 
262
    element.appendChild( symbol );
 
263
}
 
264
 
 
265
void FunctionNode::buildXML( QDomDocument doc, QDomElement element )
 
266
{
 
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 );
 
275
    }
 
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 );
 
289
    }
 
290
    else if ( ( m_name->primary() == "sum" ) && ( m_args.count() > 0 ) ) {
 
291
        buildSymbolXML( doc, element, KFormula::Sum );
 
292
    }
 
293
    else if ( ( m_name->primary() == "prod" ) && ( m_args.count() > 0 ) ) {
 
294
        buildSymbolXML( doc, element, KFormula::Product );
 
295
    }
 
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 );
 
301
    }
 
302
    else {
 
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" );
 
309
 
 
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 );
 
316
            }
 
317
        }
 
318
 
 
319
        content.appendChild( sequence );
 
320
        bracket.appendChild( content );
 
321
        element.appendChild( bracket );
 
322
    }
 
323
}
 
324
 
 
325
// void FunctionNode::output( ostream& stream )
 
326
// {
 
327
//     m_name->output( stream );
 
328
//     for ( uint i = 0; i < m_args.count(); i++ ) {
 
329
//         m_args.at( i )->output( stream );
 
330
//     }
 
331
// }
 
332
 
 
333
class RowNode : public ParserNode {
 
334
public:
 
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; }
 
340
private:
 
341
    QPtrList<ParserNode> m_row;
 
342
    uint m_requiredColumns;
 
343
};
 
344
 
 
345
void RowNode::buildXML( QDomDocument doc, QDomElement element )
 
346
{
 
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 );
 
351
        }
 
352
        else {
 
353
            QDomElement de = doc.createElement( "TEXT" );
 
354
            de.setAttribute( "CHAR", "0" );
 
355
            sequence.appendChild( de );
 
356
        }
 
357
        element.appendChild( sequence );
 
358
    }
 
359
}
 
360
 
 
361
// void RowNode::output( ostream& stream )
 
362
// {
 
363
//     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 ) {
 
367
//             stream << ", ";
 
368
//         }
 
369
//     }
 
370
//     stream << "]";
 
371
// }
 
372
 
 
373
class MatrixNode : public ParserNode {
 
374
public:
 
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; }
 
379
    uint columns();
 
380
    uint rows() { return m_rows.count(); }
 
381
private:
 
382
    QPtrList<RowNode> m_rows;
 
383
};
 
384
 
 
385
uint MatrixNode::columns()
 
386
{
 
387
    uint columns = 0;
 
388
    for ( uint i = 0; i < m_rows.count(); i++ ) {
 
389
        columns = QMAX( columns, m_rows.at( i )->columns() );
 
390
    }
 
391
    return columns;
 
392
}
 
393
 
 
394
void MatrixNode::buildXML( QDomDocument doc, QDomElement element )
 
395
{
 
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" );
 
401
 
 
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" ) );
 
410
    }
 
411
    sequence.appendChild( matrix );
 
412
    content.appendChild( sequence );
 
413
    bracket.appendChild( content );
 
414
    element.appendChild( bracket );
 
415
}
 
416
 
 
417
// void MatrixNode::output( ostream& stream )
 
418
// {
 
419
//     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 ) {
 
423
//             stream << ", ";
 
424
//         }
 
425
//     }
 
426
//     stream << "]";
 
427
// }
 
428
 
 
429
 
 
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 )
 
433
{
 
434
}
 
435
 
 
436
FormulaStringParser::~FormulaStringParser()
 
437
{
 
438
    delete head;
 
439
    if ( ParserNode::debugCount != 0 ) {
 
440
        kdDebug( KFormula::DEBUGID ) << "ParserNode::debugCount = " << ParserNode::debugCount << endl;
 
441
    }
 
442
}
 
443
 
 
444
QDomDocument FormulaStringParser::parse()
 
445
{
 
446
    nextToken();
 
447
    head = parseAssign();
 
448
    //head->output( cout );
 
449
    if ( !eol() ) {
 
450
        error( QString( i18n( "Aborted parsing at %1:%2" ) ).arg( line ).arg( column ) );
 
451
    }
 
452
 
 
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);
 
460
 
 
461
    kdDebug( 39001 ) << doc.toString() << endl;
 
462
    return doc;
 
463
}
 
464
 
 
465
ParserNode* FormulaStringParser::parseAssign()
 
466
{
 
467
    ParserNode* lhs = parseExpr();
 
468
    for ( ;; ) {
 
469
        switch ( currentType ) {
 
470
        case ASSIGN: {
 
471
            QString c = current;
 
472
            nextToken();
 
473
            lhs = new AssignNode( c, lhs, parseExpr() );
 
474
            break;
 
475
        }
 
476
        default:
 
477
            return lhs;
 
478
        }
 
479
    }
 
480
}
 
481
 
 
482
ParserNode* FormulaStringParser::parseExpr()
 
483
{
 
484
    ParserNode* lhs = parseTerm();
 
485
    for ( ;; ) {
 
486
        switch ( currentType ) {
 
487
        case PLUS:
 
488
        case SUB: {
 
489
            QString c = current;
 
490
            nextToken();
 
491
            lhs = new ExprNode( c, lhs, parseTerm() );
 
492
            break;
 
493
        }
 
494
        default:
 
495
            return lhs;
 
496
        }
 
497
    }
 
498
}
 
499
 
 
500
ParserNode* FormulaStringParser::parseTerm()
 
501
{
 
502
    ParserNode* lhs = parsePower();
 
503
    for ( ;; ) {
 
504
        switch ( currentType ) {
 
505
        case MUL:
 
506
        case DIV: {
 
507
            QString c = current;
 
508
            nextToken();
 
509
            lhs = new TermNode( c, lhs, parsePower() );
 
510
            break;
 
511
        }
 
512
        default:
 
513
            return lhs;
 
514
        }
 
515
    }
 
516
}
 
517
 
 
518
ParserNode* FormulaStringParser::parsePower()
 
519
{
 
520
    ParserNode* lhs = parsePrimary();
 
521
    for ( ;; ) {
 
522
        switch ( currentType ) {
 
523
        case INDEX:
 
524
        case POW: {
 
525
            QString c = current;
 
526
            nextToken();
 
527
            lhs = new PowerNode( c, lhs, parsePrimary() );
 
528
            break;
 
529
        }
 
530
        default:
 
531
            return lhs;
 
532
        }
 
533
    }
 
534
}
 
535
 
 
536
ParserNode* FormulaStringParser::parsePrimary()
 
537
{
 
538
    switch ( currentType ) {
 
539
    case NUMBER: {
 
540
        PrimaryNode* node = new PrimaryNode( current );
 
541
        nextToken();
 
542
        return node;
 
543
    }
 
544
    case NAME: {
 
545
        PrimaryNode* node = new PrimaryNode( current );
 
546
        node->setUnicode( m_symbolTable.unicode( current ) );
 
547
        nextToken();
 
548
        if ( currentType == LP ) {
 
549
            nextToken();
 
550
            QPtrList<ParserNode> args;
 
551
            args.setAutoDelete( false );
 
552
            while ( ( currentType != EOL ) && ( currentType != RP ) ) {
 
553
                ParserNode* node = parseExpr();
 
554
                args.append( node );
 
555
                if ( currentType == COMMA ) {
 
556
                    nextToken();
 
557
                }
 
558
            }
 
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 );
 
562
        }
 
563
        return node;
 
564
    }
 
565
    case SUB: {
 
566
        nextToken();
 
567
        //ParserNode* node = new UnaryMinus( parsePrimary() );
 
568
        ParserNode* node = new UnaryMinus( parseTerm() );
 
569
        return node;
 
570
    }
 
571
    case LP: {
 
572
        nextToken();
 
573
        ParserNode* node = parseExpr();
 
574
        expect( RP, QString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( ")" ) );
 
575
        return node;
 
576
    }
 
577
    case LB: {
 
578
        nextToken();
 
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( "[" ) );
 
586
            }
 
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 ) {
 
593
                    nextToken();
 
594
                }
 
595
            }
 
596
            if ( innerBrackets ) {
 
597
                expect( RB, QString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( "]" ) );
 
598
                if ( currentType == COMMA ) {
 
599
                    nextToken();
 
600
                }
 
601
            }
 
602
            else {
 
603
                if ( currentType != RB ) {
 
604
                    if ( currentType == NEWLINE ) {
 
605
                        nextToken();
 
606
                    }
 
607
                    else {
 
608
                        expect( SEMIC, QString( i18n( "'%3' expected at %1:%2" ) ).arg( line ).arg( column ).arg( ";" ) );
 
609
                    }
 
610
                }
 
611
            }
 
612
            rows.append( new RowNode( row ) );
 
613
        }
 
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 ) );
 
619
        }
 
620
        if ( node->rows() == 0 ) {
 
621
            error( QString( i18n( "Null rows in Matrix at %1:%2" ) ).arg( line ).arg( column ) );
 
622
        }
 
623
        return node;
 
624
    }
 
625
    case OTHER: {
 
626
        ParserNode* node = new PrimaryNode( current );
 
627
        nextToken();
 
628
        return node;
 
629
    }
 
630
    default:
 
631
        error( QString( i18n( "Unexpected token at %1:%2" ) ).arg( line ).arg( column ) );
 
632
        return new PrimaryNode( "?" );
 
633
    }
 
634
}
 
635
 
 
636
void FormulaStringParser::expect( TokenType type, QString msg )
 
637
{
 
638
    if ( currentType == type ) {
 
639
        nextToken();
 
640
    }
 
641
    else {
 
642
        error( msg );
 
643
    }
 
644
}
 
645
 
 
646
QString FormulaStringParser::nextToken()
 
647
{
 
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' ) {
 
653
            line++;
 
654
            if ( m_newlineIsSpace ) {
 
655
                column = 0;
 
656
            }
 
657
            else {
 
658
                pos++;
 
659
                column = 1;
 
660
                currentType = NEWLINE;
 
661
                return current = "\n";
 
662
            }
 
663
        }
 
664
        pos++; column++;
 
665
    }
 
666
    if ( eol() ) {
 
667
        currentType = EOL;
 
668
        return QString::null;
 
669
    }
 
670
    if ( m_formula[pos].isDigit() || m_formula[pos] == '.' ) {
 
671
        uint begin = pos;
 
672
        readNumber();
 
673
        currentType = NUMBER;
 
674
        current = m_formula.mid( begin, pos-begin );
 
675
        if ( current[0] == '.' ) {
 
676
            current = "0" + current;
 
677
        }
 
678
        if ( current[current.length()-1] == '.' ) {
 
679
            current = current + "0";
 
680
        }
 
681
        return current;
 
682
    }
 
683
    else if ( m_formula[pos].isLetter() ) {
 
684
        uint begin = pos;
 
685
        pos++; column++;
 
686
        while ( !eol() && m_formula[pos].isLetter() ) {
 
687
            pos++; column++;
 
688
        }
 
689
        currentType = NAME;
 
690
        return current = m_formula.mid( begin, pos-begin );
 
691
    }
 
692
    else {
 
693
        switch ( m_formula[pos].latin1() ) {
 
694
        case '+':
 
695
            pos++; column++;
 
696
            currentType = PLUS;
 
697
            return current = "+";
 
698
        case '-':
 
699
            pos++; column++;
 
700
            currentType = SUB;
 
701
            return current = "-";
 
702
        case '*':
 
703
            pos++; column++;
 
704
            if ( !eol() && m_formula[pos] == '*' ) {
 
705
                pos++; column++;
 
706
                currentType = POW;
 
707
                return current = "**";
 
708
            }
 
709
            currentType = MUL;
 
710
            return current = "*";
 
711
        case '/':
 
712
            pos++; column++;
 
713
            currentType = DIV;
 
714
            return current = "/";
 
715
        case '^':
 
716
            pos++; column++;
 
717
            currentType = POW;
 
718
            return current = "**";
 
719
        case '_':
 
720
            pos++; column++;
 
721
            currentType = INDEX;
 
722
            return current = "_";
 
723
        case '(':
 
724
            pos++; column++;
 
725
            currentType = LP;
 
726
            return current = "(";
 
727
        case ')':
 
728
            pos++; column++;
 
729
            currentType = RP;
 
730
            return current = ")";
 
731
        case '[':
 
732
            pos++; column++;
 
733
            currentType = LB;
 
734
            return current = "[";
 
735
        case ']':
 
736
            pos++; column++;
 
737
            currentType = RB;
 
738
            return current = "]";
 
739
        case ',':
 
740
            pos++; column++;
 
741
            currentType = COMMA;
 
742
            return current = ",";
 
743
        case ';':
 
744
            pos++; column++;
 
745
            currentType = SEMIC;
 
746
            return current = ";";
 
747
        case '=':
 
748
            pos++; column++;
 
749
            currentType = ASSIGN;
 
750
            return current = "=";
 
751
        default:
 
752
            pos++; column++;
 
753
            currentType = OTHER;
 
754
            return current = m_formula.mid( pos-1, 1 );
 
755
        }
 
756
    }
 
757
}
 
758
 
 
759
void FormulaStringParser::readNumber()
 
760
{
 
761
    bool digitsBeforeDot = m_formula[pos] != '.';
 
762
 
 
763
    readDigits();
 
764
    if ( pos < m_formula.length()-1 ) {
 
765
        QChar ch = m_formula[pos];
 
766
 
 
767
        // Look for a dot.
 
768
        if ( ch == '.' ) {
 
769
            pos++;
 
770
            column++;
 
771
            ch = m_formula[pos];
 
772
            if ( ch.isDigit() ) {
 
773
                readDigits();
 
774
            }
 
775
            else if ( !digitsBeforeDot ) {
 
776
                error( QString( i18n( "A single '.' is not a number at %1:%2" ) ).arg( line ).arg( column ) );
 
777
                return;
 
778
            }
 
779
        }
 
780
 
 
781
        // there might as well be an exponent
 
782
        if ( pos < m_formula.length()-1 ) {
 
783
            ch = m_formula[pos];
 
784
            if ( ( ch == 'E' ) || ( ch == 'e' ) ) {
 
785
                pos++;
 
786
                column++;
 
787
                ch = m_formula[pos];
 
788
 
 
789
                // signs are allowed after the exponent
 
790
                if ( ( ( ch == '+' ) || ( ch == '-' ) ) &&
 
791
                     ( pos < m_formula.length()-1 ) ) {
 
792
                    pos++;
 
793
                    column++;
 
794
                    ch = m_formula[pos];
 
795
                    if ( ch.isDigit() ) {
 
796
                        readDigits();
 
797
                    }
 
798
                    else {
 
799
                        pos -= 2;
 
800
                        column -= 2;
 
801
                        return;
 
802
                    }
 
803
                }
 
804
                else if ( ch.isDigit() ) {
 
805
                    readDigits();
 
806
                }
 
807
                else {
 
808
                    pos--;
 
809
                    column--;
 
810
                }
 
811
            }
 
812
        }
 
813
    }
 
814
}
 
815
 
 
816
 
 
817
void FormulaStringParser::readDigits()
 
818
{
 
819
    while ( !eol() && m_formula[pos].isDigit() ) {
 
820
        pos++;
 
821
        column++;
 
822
    }
 
823
}
 
824
 
 
825
void FormulaStringParser::error( QString err )
 
826
{
 
827
    kdDebug( KFormula::DEBUGID ) << err << " (" << currentType << "; " << current << ")" << endl;
 
828
    m_errorList.push_back( err );
 
829
}