fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / slideshow / source / engine / smilfunctionparser.cxx
blob5b1922de91d026933a032a8074221dec96f85b0f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 // must be first
22 #include <canvas/debug.hxx>
23 #include <tools/diagnose_ex.h>
25 #include <rtl/math.hxx>
27 #include <smilfunctionparser.hxx>
28 #include <expressionnodefactory.hxx>
30 #include <rtl/ustring.hxx>
31 #include <canvas/verbosetrace.hxx>
33 #include <basegfx/matrix/b2dhommatrix.hxx>
34 #include <basegfx/point/b2dpoint.hxx>
36 // Makes parser a static resource,
37 // we're synchronized externally.
38 // But watch out, the parser might have
39 // state not visible to this code!
40 #define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
41 #if defined(VERBOSE) && defined(DBG_UTIL)
42 #include <typeinfo>
43 #define BOOST_SPIRIT_DEBUG
44 #endif
45 #include <boost/spirit/include/classic_core.hpp>
47 #if OSL_DEBUG_LEVEL > 0
48 #include <iostream>
49 #endif
50 #include <functional>
51 #include <algorithm>
52 #include <stack>
56 /* Implementation of SmilFunctionParser class */
58 namespace slideshow
60 namespace internal
62 namespace
64 typedef const sal_Char* StringIteratorT;
66 struct ParserContext
68 typedef ::std::stack< ExpressionNodeSharedPtr > OperandStack;
70 // stores a stack of not-yet-evaluated operands. This is used
71 // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
72 // arguments from. If all arguments to an operator are constant,
73 // the operator pushes a precalculated result on the stack, and
74 // a composite ExpressionNode otherwise.
75 OperandStack maOperandStack;
77 // bounds of the shape this expression is associated with
78 ::basegfx::B2DRectangle maShapeBounds;
80 // when true, enable usage of time-dependent variable '$'
81 // in expressions
82 bool mbParseAnimationFunction;
85 typedef ::boost::shared_ptr< ParserContext > ParserContextSharedPtr;
88 template< typename Generator > class ShapeBoundsFunctor
90 public:
91 ShapeBoundsFunctor( Generator aGenerator,
92 const ParserContextSharedPtr& rContext ) :
93 maGenerator( aGenerator ),
94 mpContext( rContext )
96 ENSURE_OR_THROW( mpContext,
97 "ShapeBoundsFunctor::ShapeBoundsFunctor(): Invalid context" );
100 void operator()( StringIteratorT, StringIteratorT ) const
102 mpContext->maOperandStack.push(
103 ExpressionNodeFactory::createConstantValueExpression(
104 maGenerator( mpContext->maShapeBounds ) ) );
107 private:
108 Generator maGenerator;
109 ParserContextSharedPtr mpContext;
112 template< typename Generator > ShapeBoundsFunctor< Generator >
113 makeShapeBoundsFunctor( const Generator& rGenerator,
114 const ParserContextSharedPtr& rContext )
116 return ShapeBoundsFunctor<Generator>(rGenerator, rContext);
119 /** Generate apriori constant value
121 class ConstantFunctor
123 public:
124 ConstantFunctor( double rValue,
125 const ParserContextSharedPtr& rContext ) :
126 mnValue( rValue ),
127 mpContext( rContext )
129 ENSURE_OR_THROW( mpContext,
130 "ConstantFunctor::ConstantFunctor(): Invalid context" );
133 void operator()( StringIteratorT, StringIteratorT ) const
135 mpContext->maOperandStack.push(
136 ExpressionNodeFactory::createConstantValueExpression( mnValue ) );
139 private:
140 const double mnValue;
141 ParserContextSharedPtr mpContext;
144 /** Generate parse-dependent-but-then-constant value
146 class DoubleConstantFunctor
148 public:
149 DoubleConstantFunctor( const ParserContextSharedPtr& rContext ) :
150 mpContext( rContext )
152 ENSURE_OR_THROW( mpContext,
153 "DoubleConstantFunctor::DoubleConstantFunctor(): Invalid context" );
156 void operator()( double n ) const
158 // push constant value expression to the stack
159 mpContext->maOperandStack.push(
160 ExpressionNodeFactory::createConstantValueExpression( n ) );
163 private:
164 ParserContextSharedPtr mpContext;
167 /** Generate special t value expression node
169 class ValueTFunctor
171 public:
172 ValueTFunctor( const ParserContextSharedPtr& rContext ) :
173 mpContext( rContext )
175 ENSURE_OR_THROW( mpContext,
176 "ValueTFunctor::ValueTFunctor(): Invalid context" );
179 void operator()( StringIteratorT, StringIteratorT ) const
181 if( !mpContext->mbParseAnimationFunction )
183 OSL_FAIL( "ValueTFunctor::operator(): variable encountered, but we're not parsing a function here" );
184 throw ParseError();
187 // push special t value expression to the stack
188 mpContext->maOperandStack.push(
189 ExpressionNodeFactory::createValueTExpression() );
192 private:
193 ParserContextSharedPtr mpContext;
196 template< typename Functor > class UnaryFunctionFunctor
198 private:
199 /** ExpressionNode implementation for unary
200 function over one ExpressionNode
202 class UnaryFunctionExpression : public ExpressionNode
204 public:
205 UnaryFunctionExpression( const Functor& rFunctor,
206 const ExpressionNodeSharedPtr& rArg ) :
207 maFunctor( rFunctor ),
208 mpArg( rArg )
212 virtual double operator()( double t ) const SAL_OVERRIDE
214 return maFunctor( (*mpArg)(t) );
217 virtual bool isConstant() const SAL_OVERRIDE
219 return mpArg->isConstant();
222 private:
223 Functor maFunctor;
224 ExpressionNodeSharedPtr mpArg;
227 public:
228 UnaryFunctionFunctor( const Functor& rFunctor,
229 const ParserContextSharedPtr& rContext ) :
230 maFunctor( rFunctor ),
231 mpContext( rContext )
233 ENSURE_OR_THROW( mpContext,
234 "UnaryFunctionFunctor::UnaryFunctionFunctor(): Invalid context" );
237 void operator()( StringIteratorT, StringIteratorT ) const
239 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
241 if( rNodeStack.size() < 1 )
242 throw ParseError( "Not enough arguments for unary operator" );
244 // retrieve arguments
245 ExpressionNodeSharedPtr pArg( rNodeStack.top() );
246 rNodeStack.pop();
248 // check for constness
249 if( pArg->isConstant() )
251 rNodeStack.push(
252 ExpressionNodeFactory::createConstantValueExpression(
253 maFunctor( (*pArg)(0.0) ) ) );
255 else
257 // push complex node, that calcs the value on demand
258 rNodeStack.push(
259 ExpressionNodeSharedPtr(
260 new UnaryFunctionExpression(
261 maFunctor,
262 pArg ) ) );
266 private:
267 Functor maFunctor;
268 ParserContextSharedPtr mpContext;
271 // TODO(Q2): Refactor makeUnaryFunctionFunctor,
272 // makeBinaryFunctionFunctor and the whole
273 // ExpressionNodeFactory, to use a generic
274 // makeFunctionFunctor template, which is overloaded for
275 // unary, binary, ternary, etc. function pointers.
276 template< typename Functor > UnaryFunctionFunctor<Functor>
277 makeUnaryFunctionFunctor( const Functor& rFunctor,
278 const ParserContextSharedPtr& rContext )
280 return UnaryFunctionFunctor<Functor>( rFunctor, rContext );
283 // MSVC has problems instantiating above template function with plain function
284 // pointers (doesn't like the const reference there). Thus, provide it with
285 // a dedicated overload here.
286 UnaryFunctionFunctor< double (*)(double) >
287 makeUnaryFunctionFunctor( double (*pFunc)(double),
288 const ParserContextSharedPtr& rContext )
290 return UnaryFunctionFunctor< double (*)(double) >( pFunc, rContext );
293 /** Implements a binary function over two ExpressionNodes
295 @tpl Generator
296 Generator functor, to generate an ExpressionNode of
297 appropriate type
300 template< class Generator > class BinaryFunctionFunctor
302 public:
303 BinaryFunctionFunctor( const Generator& rGenerator,
304 const ParserContextSharedPtr& rContext ) :
305 maGenerator( rGenerator ),
306 mpContext( rContext )
308 ENSURE_OR_THROW( mpContext,
309 "BinaryFunctionFunctor::BinaryFunctionFunctor(): Invalid context" );
312 void operator()( StringIteratorT, StringIteratorT ) const
314 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
316 if( rNodeStack.size() < 2 )
317 throw ParseError( "Not enough arguments for binary operator" );
319 // retrieve arguments
320 ExpressionNodeSharedPtr pSecondArg( rNodeStack.top() );
321 rNodeStack.pop();
322 ExpressionNodeSharedPtr pFirstArg( rNodeStack.top() );
323 rNodeStack.pop();
325 // create combined ExpressionNode
326 ExpressionNodeSharedPtr pNode( maGenerator( pFirstArg,
327 pSecondArg ) );
328 // check for constness
329 if( pFirstArg->isConstant() &&
330 pSecondArg->isConstant() )
332 // call the operator() at pNode, store result
333 // in constant value ExpressionNode.
334 rNodeStack.push(
335 ExpressionNodeFactory::createConstantValueExpression(
336 (*pNode)( 0.0 ) ) );
338 else
340 // push complex node, that calcs the value on demand
341 rNodeStack.push( pNode );
345 private:
346 Generator maGenerator;
347 ParserContextSharedPtr mpContext;
350 template< typename Generator > BinaryFunctionFunctor<Generator>
351 makeBinaryFunctionFunctor( const Generator& rGenerator,
352 const ParserContextSharedPtr& rContext )
354 return BinaryFunctionFunctor<Generator>( rGenerator, rContext );
358 // Workaround for MSVC compiler anomaly (stack trashing)
360 // The default ureal_parser_policies implementation of parse_exp
361 // triggers a really weird error in MSVC7 (Version 13.00.9466), in
362 // that the real_parser_impl::parse_main() call of parse_exp()
363 // overwrites the frame pointer _on the stack_ (EBP of the calling
364 // function gets overwritten while lying on the stack).
366 // For the time being, our parser thus can only read the 1.0E10
367 // notation, not the 1.0e10 one.
369 // TODO(F1): Also handle the 1.0e10 case here.
370 template< typename T > struct custom_real_parser_policies : public ::boost::spirit::ureal_parser_policies<T>
372 template< typename ScannerT >
373 static typename ::boost::spirit::parser_result< ::boost::spirit::chlit<>, ScannerT >::type
374 parse_exp(ScannerT& scan)
376 // as_lower_d somehow breaks MSVC7
377 return ::boost::spirit::ch_p('E').parse(scan);
381 /* This class implements the following grammar (more or
382 less literally written down below, only slightly
383 obfuscated by the parser actions):
385 identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
387 function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
389 basic_expression =
390 number |
391 identifier |
392 function '(' additive_expression ')' |
393 '(' additive_expression ')'
395 unary_expression =
396 '-' basic_expression |
397 basic_expression
399 multiplicative_expression =
400 unary_expression ( ( '*' unary_expression )* |
401 ( '/' unary_expression )* )
403 additive_expression =
404 multiplicative_expression ( ( '+' multiplicative_expression )* |
405 ( '-' multiplicative_expression )* )
408 class ExpressionGrammar : public ::boost::spirit::grammar< ExpressionGrammar >
410 public:
411 /** Create an arithmetic expression grammar
413 @param rParserContext
414 Contains context info for the parser
416 ExpressionGrammar( const ParserContextSharedPtr& rParserContext ) :
417 mpParserContext( rParserContext )
421 template< typename ScannerT > class definition
423 public:
424 // grammar definition
425 definition( const ExpressionGrammar& self )
427 using ::boost::spirit::str_p;
428 using ::boost::spirit::real_parser;
430 identifier =
431 str_p( "$" )[ ValueTFunctor( self.getContext()) ]
432 | str_p( "pi" )[ ConstantFunctor(M_PI, self.getContext()) ]
433 | str_p( "e" )[ ConstantFunctor(M_E, self.getContext()) ]
434 | str_p( "x" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterX),self.getContext()) ]
435 | str_p( "y" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterY),self.getContext()) ]
436 | str_p( "width" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getWidth), self.getContext()) ]
437 | str_p( "height" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getHeight), self.getContext()) ]
440 unaryFunction =
441 (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&fabs, self.getContext()) ]
442 | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sqrt, self.getContext()) ]
443 | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sin, self.getContext()) ]
444 | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&cos, self.getContext()) ]
445 | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&tan, self.getContext()) ]
446 | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&atan, self.getContext()) ]
447 | (str_p( "acos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&acos, self.getContext()) ]
448 | (str_p( "asin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&asin, self.getContext()) ]
449 | (str_p( "exp" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&exp, self.getContext()) ]
450 | (str_p( "log" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&log, self.getContext()) ]
453 binaryFunction =
454 (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinExpression, self.getContext()) ]
455 | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMaxExpression, self.getContext()) ]
458 basicExpression =
459 real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
460 | identifier
461 | unaryFunction
462 | binaryFunction
463 | '(' >> additiveExpression >> ')'
466 unaryExpression =
467 ('-' >> basicExpression)[ makeUnaryFunctionFunctor(::std::negate<double>(), self.getContext()) ]
468 | basicExpression
471 multiplicativeExpression =
472 unaryExpression
473 >> *( ('*' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMultipliesExpression, self.getContext()) ]
474 | ('/' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createDividesExpression, self.getContext()) ]
478 additiveExpression =
479 multiplicativeExpression
480 >> *( ('+' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createPlusExpression, self.getContext()) ]
481 | ('-' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinusExpression, self.getContext()) ]
485 BOOST_SPIRIT_DEBUG_RULE(additiveExpression);
486 BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression);
487 BOOST_SPIRIT_DEBUG_RULE(unaryExpression);
488 BOOST_SPIRIT_DEBUG_RULE(basicExpression);
489 BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
490 BOOST_SPIRIT_DEBUG_RULE(binaryFunction);
491 BOOST_SPIRIT_DEBUG_RULE(identifier);
494 const ::boost::spirit::rule< ScannerT >& start() const
496 return additiveExpression;
499 private:
500 // the constituents of the Spirit arithmetic expression grammar.
501 // For the sake of readability, without 'ma' prefix.
502 ::boost::spirit::rule< ScannerT > additiveExpression;
503 ::boost::spirit::rule< ScannerT > multiplicativeExpression;
504 ::boost::spirit::rule< ScannerT > unaryExpression;
505 ::boost::spirit::rule< ScannerT > basicExpression;
506 ::boost::spirit::rule< ScannerT > unaryFunction;
507 ::boost::spirit::rule< ScannerT > binaryFunction;
508 ::boost::spirit::rule< ScannerT > identifier;
511 const ParserContextSharedPtr& getContext() const
513 return mpParserContext;
516 private:
517 ParserContextSharedPtr mpParserContext; // might get modified during parsing
520 #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
521 const ParserContextSharedPtr& getParserContext()
523 static ParserContextSharedPtr lcl_parserContext( new ParserContext() );
525 // clear node stack (since we reuse the static object, that's
526 // the whole point here)
527 while( !lcl_parserContext->maOperandStack.empty() )
528 lcl_parserContext->maOperandStack.pop();
530 return lcl_parserContext;
532 #endif
535 ExpressionNodeSharedPtr SmilFunctionParser::parseSmilValue( const OUString& rSmilValue,
536 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
538 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
539 // gives better conversion robustness here (we might want to map space
540 // etc. to ASCII space here)
541 const OString& rAsciiSmilValue(
542 OUStringToOString( rSmilValue, RTL_TEXTENCODING_ASCII_US ) );
544 StringIteratorT aStart( rAsciiSmilValue.getStr() );
545 StringIteratorT aEnd( rAsciiSmilValue.getStr()+rAsciiSmilValue.getLength() );
547 ParserContextSharedPtr pContext;
549 #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
550 // static parser context, because the actual
551 // Spirit parser is also a static object
552 pContext = getParserContext();
553 #else
554 pContext.reset( new ParserContext() );
555 #endif
557 pContext->maShapeBounds = rRelativeShapeBounds;
558 pContext->mbParseAnimationFunction = false; // parse with '$' disabled
561 ExpressionGrammar aExpressionGrammer( pContext );
562 const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
563 ::boost::spirit::parse( aStart,
564 aEnd,
565 aExpressionGrammer,
566 ::boost::spirit::space_p ) );
568 #if OSL_DEBUG_LEVEL > 0
569 ::std::cout.flush(); // needed to keep stdout and cout in sync
570 #endif
572 // input fully congested by the parser?
573 if( !aParseInfo.full )
574 throw ParseError( "SmilFunctionParser::parseSmilValue(): string not fully parseable" );
576 // parser's state stack now must contain exactly _one_ ExpressionNode,
577 // which represents our formula.
578 if( pContext->maOperandStack.size() != 1 )
579 throw ParseError( "SmilFunctionParser::parseSmilValue(): incomplete or empty expression" );
581 return pContext->maOperandStack.top();
584 ExpressionNodeSharedPtr SmilFunctionParser::parseSmilFunction( const OUString& rSmilFunction,
585 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
587 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
588 // gives better conversion robustness here (we might want to map space
589 // etc. to ASCII space here)
590 const OString& rAsciiSmilFunction(
591 OUStringToOString( rSmilFunction, RTL_TEXTENCODING_ASCII_US ) );
593 StringIteratorT aStart( rAsciiSmilFunction.getStr() );
594 StringIteratorT aEnd( rAsciiSmilFunction.getStr()+rAsciiSmilFunction.getLength() );
596 ParserContextSharedPtr pContext;
598 #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
599 // static parser context, because the actual
600 // Spirit parser is also a static object
601 pContext = getParserContext();
602 #else
603 pContext.reset( new ParserContext() );
604 #endif
606 pContext->maShapeBounds = rRelativeShapeBounds;
607 pContext->mbParseAnimationFunction = true; // parse with '$' enabled
610 ExpressionGrammar aExpressionGrammer( pContext );
611 const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
612 ::boost::spirit::parse( aStart,
613 aEnd,
614 aExpressionGrammer >> ::boost::spirit::end_p,
615 ::boost::spirit::space_p ) );
617 #if OSL_DEBUG_LEVEL > 0
618 ::std::cout.flush(); // needed to keep stdout and cout in sync
619 #endif
620 // input fully congested by the parser?
621 if( !aParseInfo.full )
622 throw ParseError( "SmilFunctionParser::parseSmilFunction(): string not fully parseable" );
624 // parser's state stack now must contain exactly _one_ ExpressionNode,
625 // which represents our formula.
626 if( pContext->maOperandStack.size() != 1 )
627 throw ParseError( "SmilFunctionParser::parseSmilFunction(): incomplete or empty expression" );
629 return pContext->maOperandStack.top();
634 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */