LibreOffice Module slideshow (master) 1
smilfunctionparser.cxx
Go to the documentation of this file.
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
22
26
27#include <rtl/ustring.hxx>
28#include <sal/log.hxx>
29
30// Makes parser a static resource,
31// we're synchronized externally.
32// But watch out, the parser might have
33// state not visible to this code!
34#define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
35
36#if defined(DBG_UTIL)
37#define BOOST_SPIRIT_DEBUG
38#endif
39#include <boost/spirit/include/classic_core.hpp>
40
41#include <iostream>
42#include <functional>
43#include <algorithm>
44#include <stack>
45#include <utility>
46
47
48/* Implementation of SmilFunctionParser class */
49
50namespace slideshow::internal
51{
52 namespace
53 {
54 typedef const char* StringIteratorT;
55
56 struct ParserContext
57 {
58 typedef ::std::stack< std::shared_ptr<ExpressionNode> > OperandStack;
59
60 // stores a stack of not-yet-evaluated operands. This is used
61 // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
62 // arguments from. If all arguments to an operator are constant,
63 // the operator pushes a precalculated result on the stack, and
64 // a composite ExpressionNode otherwise.
65 OperandStack maOperandStack;
66
67 // bounds of the shape this expression is associated with
69
70 // when true, enable usage of time-dependent variable '$'
71 // in expressions
73 };
74
75 typedef ::std::shared_ptr< ParserContext > ParserContextSharedPtr;
76
77
78 template< typename Generator > class ShapeBoundsFunctor
79 {
80 public:
81 ShapeBoundsFunctor( Generator aGenerator,
82 ParserContextSharedPtr xContext ) :
83 maGenerator( aGenerator ),
84 mpContext(std::move( xContext ))
85 {
87 "ShapeBoundsFunctor::ShapeBoundsFunctor(): Invalid context" );
88 }
89
90 void operator()( StringIteratorT, StringIteratorT ) const
91 {
92 mpContext->maOperandStack.push(
94 maGenerator( mpContext->maShapeBounds ) ) );
95 }
96
97 private:
98 Generator maGenerator;
99 ParserContextSharedPtr mpContext;
100 };
101
102 template< typename Generator > ShapeBoundsFunctor< Generator >
103 makeShapeBoundsFunctor( const Generator& rGenerator,
104 const ParserContextSharedPtr& rContext )
105 {
106 return ShapeBoundsFunctor<Generator>(rGenerator, rContext);
107 }
108
111 class ConstantFunctor
112 {
113 public:
114 ConstantFunctor( double rValue,
115 ParserContextSharedPtr xContext ) :
116 mnValue( rValue ),
117 mpContext(std::move( xContext ))
118 {
120 "ConstantFunctor::ConstantFunctor(): Invalid context" );
121 }
122
123 void operator()( StringIteratorT, StringIteratorT ) const
124 {
125 mpContext->maOperandStack.push(
127 }
128
129 private:
130 const double mnValue;
131 ParserContextSharedPtr mpContext;
132 };
133
136 class DoubleConstantFunctor
137 {
138 public:
139 explicit DoubleConstantFunctor( ParserContextSharedPtr xContext ) :
140 mpContext(std::move( xContext ))
141 {
143 "DoubleConstantFunctor::DoubleConstantFunctor(): Invalid context" );
144 }
145
146 void operator()( double n ) const
147 {
148 // push constant value expression to the stack
149 mpContext->maOperandStack.push(
151 }
152
153 private:
154 ParserContextSharedPtr mpContext;
155 };
156
159 class ValueTFunctor
160 {
161 public:
162 explicit ValueTFunctor( ParserContextSharedPtr xContext ) :
163 mpContext(std::move( xContext ))
164 {
166 "ValueTFunctor::ValueTFunctor(): Invalid context" );
167 }
168
169 void operator()( StringIteratorT, StringIteratorT ) const
170 {
171 if( !mpContext->mbParseAnimationFunction )
172 {
173 SAL_WARN("slideshow", "ValueTFunctor::operator(): variable encountered, but we're not parsing a function here" );
174 throw ParseError();
175 }
176
177 // push special t value expression to the stack
178 mpContext->maOperandStack.push(
180 }
181
182 private:
183 ParserContextSharedPtr mpContext;
184 };
185
186 template< typename Functor > class UnaryFunctionFunctor
187 {
188 private:
192 class UnaryFunctionExpression : public ExpressionNode
193 {
194 public:
195 UnaryFunctionExpression( const Functor& rFunctor,
196 std::shared_ptr<ExpressionNode> xArg ) :
197 maFunctor( rFunctor ),
198 mpArg(std::move( xArg ))
199 {
200 }
201
202 virtual double operator()( double t ) const override
203 {
204 return maFunctor( (*mpArg)(t) );
205 }
206
207 virtual bool isConstant() const override
208 {
209 return mpArg->isConstant();
210 }
211
212 private:
213 Functor maFunctor;
214 std::shared_ptr<ExpressionNode> mpArg;
215 };
216
217 public:
218 UnaryFunctionFunctor( const Functor& rFunctor,
219 ParserContextSharedPtr xContext ) :
220 maFunctor( rFunctor ),
221 mpContext(std::move( xContext ))
222 {
224 "UnaryFunctionFunctor::UnaryFunctionFunctor(): Invalid context" );
225 }
226
227 void operator()( StringIteratorT, StringIteratorT ) const
228 {
229 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
230
231 if( rNodeStack.empty() )
232 throw ParseError( "Not enough arguments for unary operator" );
233
234 // retrieve arguments
235 std::shared_ptr<ExpressionNode> pArg( std::move(rNodeStack.top()) );
236 rNodeStack.pop();
237
238 // check for constness
239 if( pArg->isConstant() )
240 {
241 rNodeStack.push(
243 maFunctor( (*pArg)(0.0) ) ) );
244 }
245 else
246 {
247 // push complex node, that calcs the value on demand
248 rNodeStack.push(
249 std::make_shared<UnaryFunctionExpression>(
250 maFunctor,
251 pArg ) );
252 }
253 }
254
255 private:
256 Functor maFunctor;
257 ParserContextSharedPtr mpContext;
258 };
259
260 // TODO(Q2): Refactor makeUnaryFunctionFunctor,
261 // makeBinaryFunctionFunctor and the whole
262 // ExpressionNodeFactory, to use a generic
263 // makeFunctionFunctor template, which is overloaded for
264 // unary, binary, ternary, etc. function pointers.
265 template< typename Functor > UnaryFunctionFunctor<Functor>
266 makeUnaryFunctionFunctor( const Functor& rFunctor,
267 const ParserContextSharedPtr& rContext )
268 {
269 return UnaryFunctionFunctor<Functor>( rFunctor, rContext );
270 }
271
272 // MSVC has problems instantiating above template function with plain function
273 // pointers (doesn't like the const reference there). Thus, provide it with
274 // a dedicated overload here.
275 UnaryFunctionFunctor< double (*)(double) >
276 makeUnaryFunctionFunctor( double (*pFunc)(double),
277 const ParserContextSharedPtr& rContext )
278 {
279 return UnaryFunctionFunctor< double (*)(double) >( pFunc, rContext );
280 }
281
289 template< class Generator > class BinaryFunctionFunctor
290 {
291 public:
292 BinaryFunctionFunctor( const Generator& rGenerator,
293 ParserContextSharedPtr xContext ) :
294 maGenerator( rGenerator ),
295 mpContext(std::move( xContext ))
296 {
298 "BinaryFunctionFunctor::BinaryFunctionFunctor(): Invalid context" );
299 }
300
301 void operator()( StringIteratorT, StringIteratorT ) const
302 {
303 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
304
305 if( rNodeStack.size() < 2 )
306 throw ParseError( "Not enough arguments for binary operator" );
307
308 // retrieve arguments
309 std::shared_ptr<ExpressionNode> pSecondArg( std::move(rNodeStack.top()) );
310 rNodeStack.pop();
311 std::shared_ptr<ExpressionNode> pFirstArg( std::move(rNodeStack.top()) );
312 rNodeStack.pop();
313
314 // create combined ExpressionNode
315 std::shared_ptr<ExpressionNode> pNode( maGenerator( pFirstArg,
316 pSecondArg ) );
317
318 assert(pSecondArg && pFirstArg);
319
320 // check for constness
321 if (pFirstArg->isConstant() && pSecondArg->isConstant())
322 {
323 // call the operator() at pNode, store result
324 // in constant value ExpressionNode.
325 rNodeStack.push(
327 (*pNode)( 0.0 ) ) );
328 }
329 else
330 {
331 // push complex node, that calcs the value on demand
332 rNodeStack.push( pNode );
333 }
334 }
335
336 private:
337 Generator maGenerator;
338 ParserContextSharedPtr mpContext;
339 };
340
341 template< typename Generator > BinaryFunctionFunctor<Generator>
342 makeBinaryFunctionFunctor( const Generator& rGenerator,
343 const ParserContextSharedPtr& rContext )
344 {
345 return BinaryFunctionFunctor<Generator>( rGenerator, rContext );
346 }
347
348
349 // Workaround for MSVC compiler anomaly (stack trashing)
350
351 // The default ureal_parser_policies implementation of parse_exp
352 // triggers a really weird error in MSVC7 (Version 13.00.9466), in
353 // that the real_parser_impl::parse_main() call of parse_exp()
354 // overwrites the frame pointer _on the stack_ (EBP of the calling
355 // function gets overwritten while lying on the stack).
356
357 // For the time being, our parser thus can only read the 1.0E10
358 // notation, not the 1.0e10 one.
359
360 // TODO(F1): Also handle the 1.0e10 case here.
361 template< typename T > struct custom_real_parser_policies : public ::boost::spirit::classic::ureal_parser_policies<T>
362 {
363 template< typename ScannerT >
364 static typename ::boost::spirit::classic::parser_result< ::boost::spirit::classic::chlit<>, ScannerT >::type
365 parse_exp(ScannerT& scan)
366 {
367 // as_lower_d somehow breaks MSVC7
368 return ::boost::spirit::classic::ch_p('E').parse(scan);
369 }
370 };
371
372 /* This class implements the following grammar (more or
373 less literally written down below, only slightly
374 obfuscated by the parser actions):
375
376 identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
377
378 function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
379
380 basic_expression =
381 number |
382 identifier |
383 function '(' additive_expression ')' |
384 '(' additive_expression ')'
385
386 unary_expression =
387 '-' basic_expression |
388 basic_expression
389
390 multiplicative_expression =
391 unary_expression ( ( '*' unary_expression )* |
392 ( '/' unary_expression )* )
393
394 additive_expression =
395 multiplicative_expression ( ( '+' multiplicative_expression )* |
396 ( '-' multiplicative_expression )* )
397
398 */
399 class ExpressionGrammar : public ::boost::spirit::classic::grammar< ExpressionGrammar >
400 {
401 public:
407 explicit ExpressionGrammar( ParserContextSharedPtr xParserContext ) :
408 mpParserContext(std::move( xParserContext ))
409 {
410 }
411
412 template< typename ScannerT > class definition
413 {
414 public:
415 // grammar definition
416 explicit definition( const ExpressionGrammar& self )
417 {
418 using ::boost::spirit::classic::str_p;
419 using ::boost::spirit::classic::real_parser;
420
421 identifier =
422 str_p( "$" )[ ValueTFunctor( self.getContext()) ]
423 | str_p( "pi" )[ ConstantFunctor(M_PI, self.getContext()) ]
424 | str_p( "e" )[ ConstantFunctor(M_E, self.getContext()) ]
425 | str_p( "x" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getCenterX),self.getContext()) ]
426 | str_p( "y" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getCenterY),self.getContext()) ]
427 | str_p( "width" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getWidth), self.getContext()) ]
428 | str_p( "height" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getHeight), self.getContext()) ]
429 ;
430
432 (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&fabs, self.getContext()) ]
433 | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sqrt, self.getContext()) ]
434 | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sin, self.getContext()) ]
435 | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&cos, self.getContext()) ]
436 | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&tan, self.getContext()) ]
437 | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&atan, self.getContext()) ]
438 | (str_p( "acos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&acos, self.getContext()) ]
439 | (str_p( "asin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&asin, self.getContext()) ]
440 | (str_p( "exp" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&exp, self.getContext()) ]
441 | (str_p( "log" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&log, self.getContext()) ]
442 ;
443
445 (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinExpression, self.getContext()) ]
446 | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMaxExpression, self.getContext()) ]
447 ;
448
450 real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
451 | identifier
454 | '(' >> additiveExpression >> ')'
455 ;
456
458 ('-' >> basicExpression)[ makeUnaryFunctionFunctor(::std::negate<double>(), self.getContext()) ]
460 ;
461
464 >> *( ('*' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMultipliesExpression, self.getContext()) ]
465 | ('/' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createDividesExpression, self.getContext()) ]
466 )
467 ;
468
471 >> *( ('+' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createPlusExpression, self.getContext()) ]
472 | ('-' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinusExpression, self.getContext()) ]
473 )
474 ;
475
476 BOOST_SPIRIT_DEBUG_RULE(additiveExpression);
477 BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression);
478 BOOST_SPIRIT_DEBUG_RULE(unaryExpression);
479 BOOST_SPIRIT_DEBUG_RULE(basicExpression);
480 BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
481 BOOST_SPIRIT_DEBUG_RULE(binaryFunction);
482 BOOST_SPIRIT_DEBUG_RULE(identifier);
483 }
484
485 const ::boost::spirit::classic::rule< ScannerT >& start() const
486 {
487 return additiveExpression;
488 }
489
490 private:
491 // the constituents of the Spirit arithmetic expression grammar.
492 // For the sake of readability, without 'ma' prefix.
493 ::boost::spirit::classic::rule< ScannerT > additiveExpression;
494 ::boost::spirit::classic::rule< ScannerT > multiplicativeExpression;
495 ::boost::spirit::classic::rule< ScannerT > unaryExpression;
496 ::boost::spirit::classic::rule< ScannerT > basicExpression;
497 ::boost::spirit::classic::rule< ScannerT > unaryFunction;
498 ::boost::spirit::classic::rule< ScannerT > binaryFunction;
499 ::boost::spirit::classic::rule< ScannerT > identifier;
500 };
501
502 const ParserContextSharedPtr& getContext() const
503 {
504 return mpParserContext;
505 }
506
507 private:
508 ParserContextSharedPtr mpParserContext; // might get modified during parsing
509 };
510
511 const ParserContextSharedPtr& getParserContext()
512 {
513 static ParserContextSharedPtr lcl_parserContext = std::make_shared<ParserContext>();
514
515 // clear node stack (since we reuse the static object, that's
516 // the whole point here)
517 while( !lcl_parserContext->maOperandStack.empty() )
518 lcl_parserContext->maOperandStack.pop();
519
520 return lcl_parserContext;
521 }
522 }
523
524 std::shared_ptr<ExpressionNode> const & SmilFunctionParser::parseSmilValue( const OUString& rSmilValue,
525 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
526 {
527 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
528 // gives better conversion robustness here (we might want to map space
529 // etc. to ASCII space here)
530 const OString& rAsciiSmilValue(
531 OUStringToOString( rSmilValue, RTL_TEXTENCODING_ASCII_US ) );
532
533 StringIteratorT aStart( rAsciiSmilValue.getStr() );
534 StringIteratorT aEnd( rAsciiSmilValue.getStr()+rAsciiSmilValue.getLength() );
535
536 // static parser context, because the actual
537 // Spirit parser is also a static object
538 ParserContextSharedPtr pContext = getParserContext();
539
540 pContext->maShapeBounds = rRelativeShapeBounds;
541 pContext->mbParseAnimationFunction = false; // parse with '$' disabled
542
543
544 ExpressionGrammar aExpressionGrammer( pContext );
545 const ::boost::spirit::classic::parse_info<StringIteratorT> aParseInfo(
546 ::boost::spirit::classic::parse( aStart,
547 aEnd,
548 aExpressionGrammer,
549 ::boost::spirit::classic::space_p ) );
550
551#if OSL_DEBUG_LEVEL > 0
552 ::std::cout.flush(); // needed to keep stdout and cout in sync
553#endif
554
555 // input fully congested by the parser?
556 if( !aParseInfo.full )
557 throw ParseError( "SmilFunctionParser::parseSmilValue(): string not fully parseable" );
558
559 // parser's state stack now must contain exactly _one_ ExpressionNode,
560 // which represents our formula.
561 if( pContext->maOperandStack.size() != 1 )
562 throw ParseError( "SmilFunctionParser::parseSmilValue(): incomplete or empty expression" );
563
564 return pContext->maOperandStack.top();
565 }
566
567 std::shared_ptr<ExpressionNode> const & SmilFunctionParser::parseSmilFunction( const OUString& rSmilFunction,
568 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
569 {
570 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
571 // gives better conversion robustness here (we might want to map space
572 // etc. to ASCII space here)
573 const OString& rAsciiSmilFunction(
574 OUStringToOString( rSmilFunction, RTL_TEXTENCODING_ASCII_US ) );
575
576 StringIteratorT aStart( rAsciiSmilFunction.getStr() );
577 StringIteratorT aEnd( rAsciiSmilFunction.getStr()+rAsciiSmilFunction.getLength() );
578
579 // static parser context, because the actual
580 // Spirit parser is also a static object
581 ParserContextSharedPtr pContext = getParserContext();
582
583 pContext->maShapeBounds = rRelativeShapeBounds;
584 pContext->mbParseAnimationFunction = true; // parse with '$' enabled
585
586
587 ExpressionGrammar aExpressionGrammer( pContext );
588 const ::boost::spirit::classic::parse_info<StringIteratorT> aParseInfo(
589 ::boost::spirit::classic::parse( aStart,
590 aEnd,
591 aExpressionGrammer >> ::boost::spirit::classic::end_p,
592 ::boost::spirit::classic::space_p ) );
593
594#if OSL_DEBUG_LEVEL > 0
595 ::std::cout.flush(); // needed to keep stdout and cout in sync
596#endif
597 // input fully congested by the parser?
598 if( !aParseInfo.full )
599 throw ParseError( "SmilFunctionParser::parseSmilFunction(): string not fully parseable" );
600
601 // parser's state stack now must contain exactly _one_ ExpressionNode,
602 // which represents our formula.
603 if( pContext->maOperandStack.size() != 1 )
604 throw ParseError( "SmilFunctionParser::parseSmilFunction(): incomplete or empty expression" );
605
606 return pContext->maOperandStack.top();
607 }
608}
609
610#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
611
612// debug hooks
613
614namespace boost
615{
616
617void sp_scalar_constructor_hook(void *)
618{
619}
620
621void sp_scalar_destructor_hook(void *)
622{
623}
624
625}
626
627#endif
628
629/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static std::shared_ptr< ExpressionNode > createMultipliesExpression(const std::shared_ptr< ExpressionNode > &rLHS, const std::shared_ptr< ExpressionNode > &rRHS)
static std::shared_ptr< ExpressionNode > createConstantValueExpression(double rConstantValue)
static std::shared_ptr< ExpressionNode > createPlusExpression(const std::shared_ptr< ExpressionNode > &rLHS, const std::shared_ptr< ExpressionNode > &rRHS)
static std::shared_ptr< ExpressionNode > createMinExpression(const std::shared_ptr< ExpressionNode > &rOuterFunction, const std::shared_ptr< ExpressionNode > &rInnerFunction)
Composes two ExpressionNode function.
static std::shared_ptr< ExpressionNode > createValueTExpression()
static std::shared_ptr< ExpressionNode > createMaxExpression(const std::shared_ptr< ExpressionNode > &rOuterFunction, const std::shared_ptr< ExpressionNode > &rInnerFunction)
static std::shared_ptr< ExpressionNode > createDividesExpression(const std::shared_ptr< ExpressionNode > &rLHS, const std::shared_ptr< ExpressionNode > &rRHS)
static std::shared_ptr< ExpressionNode > createMinusExpression(const std::shared_ptr< ExpressionNode > &rLHS, const std::shared_ptr< ExpressionNode > &rRHS)
static std::shared_ptr< ExpressionNode > const & parseSmilValue(const OUString &rSmilValue, const ::basegfx::B2DRectangle &rRelativeShapeBounds)
Parse a string containing a SMIL value.
static std::shared_ptr< ExpressionNode > const & parseSmilFunction(const OUString &rSmilFunction, const ::basegfx::B2DRectangle &rRelativeShapeBounds)
Parse a string containing a SMIL function.
#define ENSURE_OR_THROW(c, m)
#define SAL_WARN(area, stream)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
::boost::spirit::classic::rule< ScannerT > unaryExpression
Generator maGenerator
bool mbParseAnimationFunction
::boost::spirit::classic::rule< ScannerT > additiveExpression
const double mnValue
ParserContextSharedPtr mpParserContext
::basegfx::B2DRectangle maShapeBounds
OperandStack maOperandStack
::boost::spirit::classic::rule< ScannerT > binaryFunction
Functor maFunctor
ParserContextSharedPtr mpContext
::boost::spirit::classic::rule< ScannerT > multiplicativeExpression
std::shared_ptr< ExpressionNode > mpArg
::boost::spirit::classic::rule< ScannerT > basicExpression
::boost::spirit::classic::rule< ScannerT > identifier
::boost::spirit::classic::rule< ScannerT > unaryFunction
This exception is thrown, when the SMIL arithmetic expression parser failed to parse a string.