Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/connectivity/source/commontools/RowFunctionParser.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
21
// Makes parser a static resource,
22
// we're synchronized externally.
23
// But watch out, the parser might have
24
// state not visible to this code!
25
#define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
26
27
#if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
28
#include <typeinfo>
29
#define BOOST_SPIRIT_DEBUG
30
#endif
31
#include <boost/spirit/include/classic_core.hpp>
32
#include <RowFunctionParser.hxx>
33
#include <rtl/ustring.hxx>
34
35
36
#if (OSL_DEBUG_LEVEL > 0)
37
#include <iostream>
38
#endif
39
#include <stack>
40
#include <utility>
41
42
namespace connectivity
43
{
44
using namespace com::sun::star;
45
46
namespace
47
{
48
49
50
// EXPRESSION NODES
51
52
53
class ConstantValueExpression : public ExpressionNode
54
{
55
    ORowSetValueDecoratorRef maValue;
56
57
public:
58
59
    explicit ConstantValueExpression(ORowSetValueDecoratorRef aValue)
60
0
        : maValue(std::move(aValue))
61
0
    {
62
0
    }
63
    virtual ORowSetValueDecoratorRef evaluate(const ODatabaseMetaDataResultSet::ORow& /*_aRow*/ ) const override
64
0
    {
65
0
        return maValue;
66
0
    }
67
    virtual void fill(const ODatabaseMetaDataResultSet::ORow& /*_aRow*/ ) const override
68
0
    {
69
0
    }
70
};
71
72
73
/** ExpressionNode implementation for unary
74
    function over two ExpressionNodes
75
    */
76
class BinaryFunctionExpression : public ExpressionNode
77
{
78
    const ExpressionFunct   meFunct;
79
    std::shared_ptr<ExpressionNode> mpFirstArg;
80
    std::shared_ptr<ExpressionNode> mpSecondArg;
81
82
public:
83
84
    BinaryFunctionExpression( const ExpressionFunct eFunct, std::shared_ptr<ExpressionNode> xFirstArg, std::shared_ptr<ExpressionNode> xSecondArg ) :
85
0
        meFunct( eFunct ),
86
0
        mpFirstArg(std::move( xFirstArg )),
87
0
        mpSecondArg(std::move( xSecondArg ))
88
0
    {
89
0
    }
90
    virtual ORowSetValueDecoratorRef evaluate(const ODatabaseMetaDataResultSet::ORow& _aRow ) const override
91
0
    {
92
0
        ORowSetValueDecoratorRef aRet;
93
0
        switch(meFunct)
94
0
        {
95
0
            case ExpressionFunct::Equation:
96
0
                aRet = new ORowSetValueDecorator( ORowSetValue(mpFirstArg->evaluate(_aRow )->getValue() == mpSecondArg->evaluate(_aRow )->getValue()) );
97
0
                break;
98
0
            case ExpressionFunct::And:
99
0
                aRet = new ORowSetValueDecorator( ORowSetValue(mpFirstArg->evaluate(_aRow )->getValue().getBool() && mpSecondArg->evaluate(_aRow )->getValue().getBool()) );
100
0
                break;
101
0
            case ExpressionFunct::Or:
102
0
                aRet = new ORowSetValueDecorator( ORowSetValue(mpFirstArg->evaluate(_aRow )->getValue().getBool() || mpSecondArg->evaluate(_aRow )->getValue().getBool()) );
103
0
                break;
104
0
            default:
105
0
                break;
106
0
        }
107
0
        return aRet;
108
0
    }
109
    virtual void fill(const ODatabaseMetaDataResultSet::ORow& _aRow ) const override
110
0
    {
111
0
        switch(meFunct)
112
0
        {
113
0
            case ExpressionFunct::Equation:
114
0
                (*mpFirstArg->evaluate(_aRow )) = mpSecondArg->evaluate(_aRow )->getValue();
115
0
                break;
116
0
            default:
117
0
                break;
118
0
        }
119
0
    }
120
};
121
122
123
// FUNCTION PARSER
124
125
126
typedef const char* StringIteratorT;
127
128
struct ParserContext
129
{
130
    typedef std::stack< std::shared_ptr<ExpressionNode> > OperandStack;
131
132
    // stores a stack of not-yet-evaluated operands. This is used
133
    // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
134
    // arguments from. If all arguments to an operator are constant,
135
    // the operator pushes a precalculated result on the stack, and
136
    // a composite ExpressionNode otherwise.
137
    OperandStack                            maOperandStack;
138
};
139
140
typedef std::shared_ptr< ParserContext > ParserContextSharedPtr;
141
142
/** Generate apriori constant value
143
    */
144
145
class ConstantFunctor
146
{
147
    ParserContextSharedPtr          mpContext;
148
149
public:
150
151
    explicit ConstantFunctor( ParserContextSharedPtr xContext ) :
152
0
        mpContext(std::move( xContext ))
153
0
    {
154
0
    }
155
    void operator()( StringIteratorT rFirst,StringIteratorT rSecond) const
156
0
    {
157
0
        OUString sVal( rFirst, rSecond - rFirst, RTL_TEXTENCODING_UTF8 );
158
0
        mpContext->maOperandStack.push(std::make_shared<ConstantValueExpression>(ORowSetValueDecoratorRef(new ORowSetValueDecorator(sVal))));
159
0
    }
160
};
161
162
/** Generate parse-dependent-but-then-constant value
163
    */
164
class IntConstantFunctor
165
{
166
    ParserContextSharedPtr  mpContext;
167
168
public:
169
    explicit IntConstantFunctor( ParserContextSharedPtr xContext ) :
170
0
        mpContext(std::move( xContext ))
171
0
    {
172
0
    }
173
    void operator()( sal_Int32 n ) const
174
0
    {
175
0
        mpContext->maOperandStack.push(std::make_shared<ConstantValueExpression>(ORowSetValueDecoratorRef(new ORowSetValueDecorator(n))));
176
0
    }
177
};
178
179
/** Implements a binary function over two ExpressionNodes
180
181
    @tpl Generator
182
    Generator functor, to generate an ExpressionNode of
183
    appropriate type
184
185
    */
186
class BinaryFunctionFunctor
187
{
188
    const ExpressionFunct   meFunct;
189
    ParserContextSharedPtr  mpContext;
190
191
public:
192
193
    BinaryFunctionFunctor( const ExpressionFunct eFunct, ParserContextSharedPtr xContext ) :
194
0
        meFunct( eFunct ),
195
0
        mpContext(std::move( xContext ))
196
0
    {
197
0
    }
198
199
    void operator()( StringIteratorT, StringIteratorT ) const
200
0
    {
201
0
        ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
202
203
0
        if( rNodeStack.size() < 2 )
204
0
            throw ParseError( "Not enough arguments for binary operator" );
205
206
        // retrieve arguments
207
0
        std::shared_ptr<ExpressionNode> pSecondArg( std::move(rNodeStack.top()) );
208
0
        rNodeStack.pop();
209
0
        std::shared_ptr<ExpressionNode> pFirstArg( std::move(rNodeStack.top()) );
210
0
        rNodeStack.pop();
211
212
        // create combined ExpressionNode
213
0
        auto pNode = std::make_shared<BinaryFunctionExpression>( meFunct, pFirstArg, pSecondArg );
214
        // check for constness
215
0
        rNodeStack.push( pNode );
216
0
    }
217
};
218
/** ExpressionNode implementation for unary
219
    function over one ExpressionNode
220
    */
221
class UnaryFunctionExpression : public ExpressionNode
222
{
223
    std::shared_ptr<ExpressionNode> mpArg;
224
225
public:
226
    explicit UnaryFunctionExpression( std::shared_ptr<ExpressionNode> xArg ) :
227
0
        mpArg(std::move( xArg ))
228
0
    {
229
0
    }
230
    virtual ORowSetValueDecoratorRef evaluate(const ODatabaseMetaDataResultSet::ORow& _aRow ) const override
231
0
    {
232
0
        return _aRow[mpArg->evaluate(_aRow )->getValue().getUInt32()];
233
0
    }
234
    virtual void fill(const ODatabaseMetaDataResultSet::ORow& /*_aRow*/ ) const override
235
0
    {
236
0
    }
237
};
238
239
class UnaryFunctionFunctor
240
{
241
    ParserContextSharedPtr  mpContext;
242
243
public:
244
245
    explicit UnaryFunctionFunctor(ParserContextSharedPtr xContext)
246
0
        : mpContext(std::move(xContext))
247
0
    {
248
0
    }
249
    void operator()( StringIteratorT, StringIteratorT ) const
250
0
    {
251
252
0
        ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
253
254
0
        if( rNodeStack.empty() )
255
0
            throw ParseError( "Not enough arguments for unary operator" );
256
257
        // retrieve arguments
258
0
        std::shared_ptr<ExpressionNode> pArg( std::move(rNodeStack.top()) );
259
0
        rNodeStack.pop();
260
261
0
        rNodeStack.push( std::make_shared<UnaryFunctionExpression>( pArg ) );
262
0
    }
263
};
264
265
/* This class implements the following grammar (more or
266
    less literally written down below, only slightly
267
    obfuscated by the parser actions):
268
269
    basic_expression =
270
                       number |
271
                       '(' additive_expression ')'
272
273
    unary_expression =
274
                    basic_expression
275
276
    multiplicative_expression =
277
                       unary_expression ( ( '*' unary_expression )* |
278
                                        ( '/' unary_expression )* )
279
280
    additive_expression =
281
                       multiplicative_expression ( ( '+' multiplicative_expression )* |
282
                                                   ( '-' multiplicative_expression )* )
283
284
    */
285
class ExpressionGrammar : public ::boost::spirit::classic::grammar< ExpressionGrammar >
286
{
287
public:
288
    /** Create an arithmetic expression grammar
289
290
        @param rParserContext
291
        Contains context info for the parser
292
        */
293
    explicit ExpressionGrammar( ParserContextSharedPtr xParserContext ) :
294
0
        mpParserContext(std::move( xParserContext ))
295
0
    {
296
0
    }
297
298
    template< typename ScannerT > class definition
299
    {
300
    public:
301
        // grammar definition
302
        explicit definition( const ExpressionGrammar& self )
303
0
        {
304
0
            using ::boost::spirit::classic::space_p;
305
0
            using ::boost::spirit::classic::range_p;
306
0
            using ::boost::spirit::classic::lexeme_d;
307
0
            using ::boost::spirit::classic::ch_p;
308
0
            using ::boost::spirit::classic::int_p;
309
0
            using ::boost::spirit::classic::as_lower_d;
310
0
            using ::boost::spirit::classic::strlit;
311
0
            using ::boost::spirit::classic::inhibit_case;
312
313
314
0
            typedef inhibit_case<strlit<> > token_t;
315
0
            token_t COLUMN  = as_lower_d[ "column" ];
316
0
            token_t OR_     = as_lower_d[ "or" ];
317
0
            token_t AND_    = as_lower_d[ "and" ];
318
319
0
            integer =
320
0
                    int_p
321
0
                                [IntConstantFunctor(self.getContext())];
322
323
0
            argument =
324
0
                    integer
325
0
                |    lexeme_d[ +( range_p('a','z') | range_p('A','Z') | range_p('0','9') ) ]
326
0
                                [ ConstantFunctor(self.getContext()) ]
327
0
               ;
328
329
0
            unaryFunction =
330
0
                    (COLUMN >> '(' >> integer >> ')' )
331
0
                                [ UnaryFunctionFunctor( self.getContext()) ]
332
0
                ;
333
334
0
            assignment =
335
0
                    unaryFunction >> ch_p('=') >> argument
336
0
                                [ BinaryFunctionFunctor( ExpressionFunct::Equation,  self.getContext()) ]
337
0
               ;
338
339
0
            andExpression =
340
0
                    assignment
341
0
                |   ( '(' >> orExpression >> ')' )
342
0
                |   ( assignment >> AND_ >> assignment )  [ BinaryFunctionFunctor( ExpressionFunct::And,  self.getContext()) ]
343
0
                ;
344
345
0
            orExpression =
346
0
                    andExpression
347
0
                |   ( orExpression >> OR_ >> andExpression ) [ BinaryFunctionFunctor( ExpressionFunct::Or,  self.getContext()) ]
348
0
                ;
349
350
0
            basicExpression =
351
0
                    orExpression
352
0
                ;
353
354
0
            BOOST_SPIRIT_DEBUG_RULE(basicExpression);
355
0
            BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
356
0
            BOOST_SPIRIT_DEBUG_RULE(assignment);
357
0
            BOOST_SPIRIT_DEBUG_RULE(argument);
358
0
            BOOST_SPIRIT_DEBUG_RULE(integer);
359
0
            BOOST_SPIRIT_DEBUG_RULE(orExpression);
360
0
            BOOST_SPIRIT_DEBUG_RULE(andExpression);
361
0
        }
362
363
        const ::boost::spirit::classic::rule< ScannerT >& start() const
364
0
        {
365
0
            return basicExpression;
366
0
        }
367
368
    private:
369
        // the constituents of the Spirit arithmetic expression grammar.
370
        // For the sake of readability, without 'ma' prefix.
371
        ::boost::spirit::classic::rule< ScannerT >   basicExpression;
372
        ::boost::spirit::classic::rule< ScannerT >   unaryFunction;
373
        ::boost::spirit::classic::rule< ScannerT >   assignment;
374
        ::boost::spirit::classic::rule< ScannerT >   integer,argument;
375
        ::boost::spirit::classic::rule< ScannerT >   orExpression,andExpression;
376
    };
377
378
    const ParserContextSharedPtr& getContext() const
379
0
    {
380
0
        return mpParserContext;
381
0
    }
382
383
private:
384
    ParserContextSharedPtr          mpParserContext; // might get modified during parsing
385
};
386
387
const ParserContextSharedPtr& getParserContext()
388
0
{
389
0
    static ParserContextSharedPtr lcl_parserContext = std::make_shared<ParserContext>();
390
391
    // clear node stack (since we reuse the static object, that's
392
    // the whole point here)
393
0
    while( !lcl_parserContext->maOperandStack.empty() )
394
0
        lcl_parserContext->maOperandStack.pop();
395
396
0
    return lcl_parserContext;
397
0
}
398
399
}
400
401
std::shared_ptr<ExpressionNode> const & FunctionParser::parseFunction( const OUString& _sFunction)
402
0
{
403
    // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
404
    // gives better conversion robustness here (we might want to map space
405
    // etc. to ASCII space here)
406
0
    const OString aAsciiFunction(
407
0
        OUStringToOString( _sFunction, RTL_TEXTENCODING_ASCII_US ) );
408
409
0
    StringIteratorT aStart( aAsciiFunction.getStr() );
410
0
    StringIteratorT aEnd( aAsciiFunction.getStr()+aAsciiFunction.getLength() );
411
412
    // static parser context, because the actual
413
    // Spirit parser is also a static object
414
0
    const ParserContextSharedPtr& pContext = getParserContext();
415
416
0
    ExpressionGrammar aExpressionGrammer( pContext );
417
418
0
    const ::boost::spirit::classic::parse_info<StringIteratorT> aParseInfo(
419
0
            ::boost::spirit::classic::parse( aStart,
420
0
                                    aEnd,
421
0
                                    aExpressionGrammer,
422
0
                                    ::boost::spirit::classic::space_p ) );
423
424
#if (OSL_DEBUG_LEVEL > 0)
425
    std::cout.flush(); // needed to keep stdout and cout in sync
426
#endif
427
428
    // input fully congested by the parser?
429
0
    if( !aParseInfo.full )
430
0
        throw ParseError( "RowFunctionParser::parseFunction(): string not fully parseable" );
431
432
    // parser's state stack now must contain exactly _one_ ExpressionNode,
433
    // which represents our formula.
434
0
    if( pContext->maOperandStack.size() != 1 )
435
0
        throw ParseError( "RowFunctionParser::parseFunction(): incomplete or empty expression" );
436
437
0
    return pContext->maOperandStack.top();
438
0
}
439
}
440
441
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */