Bump version to 6.4-15
[LibreOffice.git] / slideshow / source / engine / smilfunctionparser.cxx
blob9a6673e7db2aab057781be2c35c39ffd794edf2b
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 #include <tools/diagnose_ex.h>
23 #include <rtl/math.hxx>
25 #include <smilfunctionparser.hxx>
26 #include <expressionnodefactory.hxx>
28 #include <rtl/ustring.hxx>
29 #include <sal/log.hxx>
31 #include <basegfx/matrix/b2dhommatrix.hxx>
32 #include <basegfx/point/b2dpoint.hxx>
34 // Makes parser a static resource,
35 // we're synchronized externally.
36 // But watch out, the parser might have
37 // state not visible to this code!
38 #define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
40 #if defined(DBG_UTIL)
41 #include <typeinfo>
42 #define BOOST_SPIRIT_DEBUG
43 #endif
44 #include <boost/spirit/include/classic_core.hpp>
46 #include <iostream>
47 #include <functional>
48 #include <algorithm>
49 #include <stack>
52 /* Implementation of SmilFunctionParser class */
54 namespace slideshow
56 namespace internal
58 namespace
60 typedef const sal_Char* StringIteratorT;
62 struct ParserContext
64 typedef ::std::stack< std::shared_ptr<ExpressionNode> > OperandStack;
66 // stores a stack of not-yet-evaluated operands. This is used
67 // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
68 // arguments from. If all arguments to an operator are constant,
69 // the operator pushes a precalculated result on the stack, and
70 // a composite ExpressionNode otherwise.
71 OperandStack maOperandStack;
73 // bounds of the shape this expression is associated with
74 ::basegfx::B2DRectangle maShapeBounds;
76 // when true, enable usage of time-dependent variable '$'
77 // in expressions
78 bool mbParseAnimationFunction;
81 typedef ::std::shared_ptr< ParserContext > ParserContextSharedPtr;
84 template< typename Generator > class ShapeBoundsFunctor
86 public:
87 ShapeBoundsFunctor( Generator aGenerator,
88 const ParserContextSharedPtr& rContext ) :
89 maGenerator( aGenerator ),
90 mpContext( rContext )
92 ENSURE_OR_THROW( mpContext,
93 "ShapeBoundsFunctor::ShapeBoundsFunctor(): Invalid context" );
96 void operator()( StringIteratorT, StringIteratorT ) const
98 mpContext->maOperandStack.push(
99 ExpressionNodeFactory::createConstantValueExpression(
100 maGenerator( mpContext->maShapeBounds ) ) );
103 private:
104 Generator maGenerator;
105 ParserContextSharedPtr mpContext;
108 template< typename Generator > ShapeBoundsFunctor< Generator >
109 makeShapeBoundsFunctor( const Generator& rGenerator,
110 const ParserContextSharedPtr& rContext )
112 return ShapeBoundsFunctor<Generator>(rGenerator, rContext);
115 /** Generate apriori constant value
117 class ConstantFunctor
119 public:
120 ConstantFunctor( double rValue,
121 const ParserContextSharedPtr& rContext ) :
122 mnValue( rValue ),
123 mpContext( rContext )
125 ENSURE_OR_THROW( mpContext,
126 "ConstantFunctor::ConstantFunctor(): Invalid context" );
129 void operator()( StringIteratorT, StringIteratorT ) const
131 mpContext->maOperandStack.push(
132 ExpressionNodeFactory::createConstantValueExpression( mnValue ) );
135 private:
136 const double mnValue;
137 ParserContextSharedPtr mpContext;
140 /** Generate parse-dependent-but-then-constant value
142 class DoubleConstantFunctor
144 public:
145 explicit DoubleConstantFunctor( const ParserContextSharedPtr& rContext ) :
146 mpContext( rContext )
148 ENSURE_OR_THROW( mpContext,
149 "DoubleConstantFunctor::DoubleConstantFunctor(): Invalid context" );
152 void operator()( double n ) const
154 // push constant value expression to the stack
155 mpContext->maOperandStack.push(
156 ExpressionNodeFactory::createConstantValueExpression( n ) );
159 private:
160 ParserContextSharedPtr mpContext;
163 /** Generate special t value expression node
165 class ValueTFunctor
167 public:
168 explicit ValueTFunctor( const ParserContextSharedPtr& rContext ) :
169 mpContext( rContext )
171 ENSURE_OR_THROW( mpContext,
172 "ValueTFunctor::ValueTFunctor(): Invalid context" );
175 void operator()( StringIteratorT, StringIteratorT ) const
177 if( !mpContext->mbParseAnimationFunction )
179 SAL_WARN("slideshow", "ValueTFunctor::operator(): variable encountered, but we're not parsing a function here" );
180 throw ParseError();
183 // push special t value expression to the stack
184 mpContext->maOperandStack.push(
185 ExpressionNodeFactory::createValueTExpression() );
188 private:
189 ParserContextSharedPtr mpContext;
192 template< typename Functor > class UnaryFunctionFunctor
194 private:
195 /** ExpressionNode implementation for unary
196 function over one ExpressionNode
198 class UnaryFunctionExpression : public ExpressionNode
200 public:
201 UnaryFunctionExpression( const Functor& rFunctor,
202 const std::shared_ptr<ExpressionNode>& rArg ) :
203 maFunctor( rFunctor ),
204 mpArg( rArg )
208 virtual double operator()( double t ) const override
210 return maFunctor( (*mpArg)(t) );
213 virtual bool isConstant() const override
215 return mpArg->isConstant();
218 private:
219 Functor maFunctor;
220 std::shared_ptr<ExpressionNode> mpArg;
223 public:
224 UnaryFunctionFunctor( const Functor& rFunctor,
225 const ParserContextSharedPtr& rContext ) :
226 maFunctor( rFunctor ),
227 mpContext( rContext )
229 ENSURE_OR_THROW( mpContext,
230 "UnaryFunctionFunctor::UnaryFunctionFunctor(): Invalid context" );
233 void operator()( StringIteratorT, StringIteratorT ) const
235 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
237 if( rNodeStack.empty() )
238 throw ParseError( "Not enough arguments for unary operator" );
240 // retrieve arguments
241 std::shared_ptr<ExpressionNode> pArg( std::move(rNodeStack.top()) );
242 rNodeStack.pop();
244 // check for constness
245 if( pArg->isConstant() )
247 rNodeStack.push(
248 ExpressionNodeFactory::createConstantValueExpression(
249 maFunctor( (*pArg)(0.0) ) ) );
251 else
253 // push complex node, that calcs the value on demand
254 rNodeStack.push(
255 std::shared_ptr<ExpressionNode>(
256 new UnaryFunctionExpression(
257 maFunctor,
258 pArg ) ) );
262 private:
263 Functor maFunctor;
264 ParserContextSharedPtr mpContext;
267 // TODO(Q2): Refactor makeUnaryFunctionFunctor,
268 // makeBinaryFunctionFunctor and the whole
269 // ExpressionNodeFactory, to use a generic
270 // makeFunctionFunctor template, which is overloaded for
271 // unary, binary, ternary, etc. function pointers.
272 template< typename Functor > UnaryFunctionFunctor<Functor>
273 makeUnaryFunctionFunctor( const Functor& rFunctor,
274 const ParserContextSharedPtr& rContext )
276 return UnaryFunctionFunctor<Functor>( rFunctor, rContext );
279 // MSVC has problems instantiating above template function with plain function
280 // pointers (doesn't like the const reference there). Thus, provide it with
281 // a dedicated overload here.
282 UnaryFunctionFunctor< double (*)(double) >
283 makeUnaryFunctionFunctor( double (*pFunc)(double),
284 const ParserContextSharedPtr& rContext )
286 return UnaryFunctionFunctor< double (*)(double) >( pFunc, rContext );
289 /** Implements a binary function over two ExpressionNodes
291 @tpl Generator
292 Generator functor, to generate an ExpressionNode of
293 appropriate type
296 template< class Generator > class BinaryFunctionFunctor
298 public:
299 BinaryFunctionFunctor( const Generator& rGenerator,
300 const ParserContextSharedPtr& rContext ) :
301 maGenerator( rGenerator ),
302 mpContext( rContext )
304 ENSURE_OR_THROW( mpContext,
305 "BinaryFunctionFunctor::BinaryFunctionFunctor(): Invalid context" );
308 void operator()( StringIteratorT, StringIteratorT ) const
310 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
312 if( rNodeStack.size() < 2 )
313 throw ParseError( "Not enough arguments for binary operator" );
315 // retrieve arguments
316 std::shared_ptr<ExpressionNode> pSecondArg( std::move(rNodeStack.top()) );
317 rNodeStack.pop();
318 std::shared_ptr<ExpressionNode> pFirstArg( std::move(rNodeStack.top()) );
319 rNodeStack.pop();
321 // create combined ExpressionNode
322 std::shared_ptr<ExpressionNode> pNode( maGenerator( pFirstArg,
323 pSecondArg ) );
324 // check for constness
325 if( pFirstArg->isConstant() &&
326 pSecondArg->isConstant() )
328 // call the operator() at pNode, store result
329 // in constant value ExpressionNode.
330 rNodeStack.push(
331 ExpressionNodeFactory::createConstantValueExpression(
332 (*pNode)( 0.0 ) ) );
334 else
336 // push complex node, that calcs the value on demand
337 rNodeStack.push( pNode );
341 private:
342 Generator maGenerator;
343 ParserContextSharedPtr mpContext;
346 template< typename Generator > BinaryFunctionFunctor<Generator>
347 makeBinaryFunctionFunctor( const Generator& rGenerator,
348 const ParserContextSharedPtr& rContext )
350 return BinaryFunctionFunctor<Generator>( rGenerator, rContext );
354 // Workaround for MSVC compiler anomaly (stack trashing)
356 // The default ureal_parser_policies implementation of parse_exp
357 // triggers a really weird error in MSVC7 (Version 13.00.9466), in
358 // that the real_parser_impl::parse_main() call of parse_exp()
359 // overwrites the frame pointer _on the stack_ (EBP of the calling
360 // function gets overwritten while lying on the stack).
362 // For the time being, our parser thus can only read the 1.0E10
363 // notation, not the 1.0e10 one.
365 // TODO(F1): Also handle the 1.0e10 case here.
366 template< typename T > struct custom_real_parser_policies : public ::boost::spirit::ureal_parser_policies<T>
368 template< typename ScannerT >
369 static typename ::boost::spirit::parser_result< ::boost::spirit::chlit<>, ScannerT >::type
370 parse_exp(ScannerT& scan)
372 // as_lower_d somehow breaks MSVC7
373 return ::boost::spirit::ch_p('E').parse(scan);
377 /* This class implements the following grammar (more or
378 less literally written down below, only slightly
379 obfuscated by the parser actions):
381 identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
383 function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
385 basic_expression =
386 number |
387 identifier |
388 function '(' additive_expression ')' |
389 '(' additive_expression ')'
391 unary_expression =
392 '-' basic_expression |
393 basic_expression
395 multiplicative_expression =
396 unary_expression ( ( '*' unary_expression )* |
397 ( '/' unary_expression )* )
399 additive_expression =
400 multiplicative_expression ( ( '+' multiplicative_expression )* |
401 ( '-' multiplicative_expression )* )
404 class ExpressionGrammar : public ::boost::spirit::grammar< ExpressionGrammar >
406 public:
407 /** Create an arithmetic expression grammar
409 @param rParserContext
410 Contains context info for the parser
412 explicit ExpressionGrammar( const ParserContextSharedPtr& rParserContext ) :
413 mpParserContext( rParserContext )
417 template< typename ScannerT > class definition
419 public:
420 // grammar definition
421 explicit definition( const ExpressionGrammar& self )
423 using ::boost::spirit::str_p;
424 using ::boost::spirit::real_parser;
426 identifier =
427 str_p( "$" )[ ValueTFunctor( self.getContext()) ]
428 | str_p( "pi" )[ ConstantFunctor(M_PI, self.getContext()) ]
429 | str_p( "e" )[ ConstantFunctor(M_E, self.getContext()) ]
430 | str_p( "x" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getCenterX),self.getContext()) ]
431 | str_p( "y" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getCenterY),self.getContext()) ]
432 | str_p( "width" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getWidth), self.getContext()) ]
433 | str_p( "height" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getHeight), self.getContext()) ]
436 unaryFunction =
437 (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&fabs, self.getContext()) ]
438 | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sqrt, self.getContext()) ]
439 | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sin, self.getContext()) ]
440 | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&cos, self.getContext()) ]
441 | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&tan, self.getContext()) ]
442 | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&atan, self.getContext()) ]
443 | (str_p( "acos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&acos, self.getContext()) ]
444 | (str_p( "asin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&asin, self.getContext()) ]
445 | (str_p( "exp" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&exp, self.getContext()) ]
446 | (str_p( "log" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&log, self.getContext()) ]
449 binaryFunction =
450 (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinExpression, self.getContext()) ]
451 | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMaxExpression, self.getContext()) ]
454 basicExpression =
455 real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
456 | identifier
457 | unaryFunction
458 | binaryFunction
459 | '(' >> additiveExpression >> ')'
462 unaryExpression =
463 ('-' >> basicExpression)[ makeUnaryFunctionFunctor(::std::negate<double>(), self.getContext()) ]
464 | basicExpression
467 multiplicativeExpression =
468 unaryExpression
469 >> *( ('*' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMultipliesExpression, self.getContext()) ]
470 | ('/' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createDividesExpression, self.getContext()) ]
474 additiveExpression =
475 multiplicativeExpression
476 >> *( ('+' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createPlusExpression, self.getContext()) ]
477 | ('-' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinusExpression, self.getContext()) ]
481 BOOST_SPIRIT_DEBUG_RULE(additiveExpression);
482 BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression);
483 BOOST_SPIRIT_DEBUG_RULE(unaryExpression);
484 BOOST_SPIRIT_DEBUG_RULE(basicExpression);
485 BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
486 BOOST_SPIRIT_DEBUG_RULE(binaryFunction);
487 BOOST_SPIRIT_DEBUG_RULE(identifier);
490 const ::boost::spirit::rule< ScannerT >& start() const
492 return additiveExpression;
495 private:
496 // the constituents of the Spirit arithmetic expression grammar.
497 // For the sake of readability, without 'ma' prefix.
498 ::boost::spirit::rule< ScannerT > additiveExpression;
499 ::boost::spirit::rule< ScannerT > multiplicativeExpression;
500 ::boost::spirit::rule< ScannerT > unaryExpression;
501 ::boost::spirit::rule< ScannerT > basicExpression;
502 ::boost::spirit::rule< ScannerT > unaryFunction;
503 ::boost::spirit::rule< ScannerT > binaryFunction;
504 ::boost::spirit::rule< ScannerT > identifier;
507 const ParserContextSharedPtr& getContext() const
509 return mpParserContext;
512 private:
513 ParserContextSharedPtr const mpParserContext; // might get modified during parsing
516 const ParserContextSharedPtr& getParserContext()
518 static ParserContextSharedPtr lcl_parserContext( new ParserContext );
520 // clear node stack (since we reuse the static object, that's
521 // the whole point here)
522 while( !lcl_parserContext->maOperandStack.empty() )
523 lcl_parserContext->maOperandStack.pop();
525 return lcl_parserContext;
529 std::shared_ptr<ExpressionNode> const & SmilFunctionParser::parseSmilValue( const OUString& rSmilValue,
530 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
532 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
533 // gives better conversion robustness here (we might want to map space
534 // etc. to ASCII space here)
535 const OString& rAsciiSmilValue(
536 OUStringToOString( rSmilValue, RTL_TEXTENCODING_ASCII_US ) );
538 StringIteratorT aStart( rAsciiSmilValue.getStr() );
539 StringIteratorT aEnd( rAsciiSmilValue.getStr()+rAsciiSmilValue.getLength() );
541 // static parser context, because the actual
542 // Spirit parser is also a static object
543 ParserContextSharedPtr pContext = getParserContext();
545 pContext->maShapeBounds = rRelativeShapeBounds;
546 pContext->mbParseAnimationFunction = false; // parse with '$' disabled
549 ExpressionGrammar aExpressionGrammer( pContext );
550 const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
551 ::boost::spirit::parse( aStart,
552 aEnd,
553 aExpressionGrammer,
554 ::boost::spirit::space_p ) );
556 #if OSL_DEBUG_LEVEL > 0
557 ::std::cout.flush(); // needed to keep stdout and cout in sync
558 #endif
560 // input fully congested by the parser?
561 if( !aParseInfo.full )
562 throw ParseError( "SmilFunctionParser::parseSmilValue(): string not fully parseable" );
564 // parser's state stack now must contain exactly _one_ ExpressionNode,
565 // which represents our formula.
566 if( pContext->maOperandStack.size() != 1 )
567 throw ParseError( "SmilFunctionParser::parseSmilValue(): incomplete or empty expression" );
569 return pContext->maOperandStack.top();
572 std::shared_ptr<ExpressionNode> const & SmilFunctionParser::parseSmilFunction( const OUString& rSmilFunction,
573 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
575 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
576 // gives better conversion robustness here (we might want to map space
577 // etc. to ASCII space here)
578 const OString& rAsciiSmilFunction(
579 OUStringToOString( rSmilFunction, RTL_TEXTENCODING_ASCII_US ) );
581 StringIteratorT aStart( rAsciiSmilFunction.getStr() );
582 StringIteratorT aEnd( rAsciiSmilFunction.getStr()+rAsciiSmilFunction.getLength() );
584 // static parser context, because the actual
585 // Spirit parser is also a static object
586 ParserContextSharedPtr pContext = getParserContext();
588 pContext->maShapeBounds = rRelativeShapeBounds;
589 pContext->mbParseAnimationFunction = true; // parse with '$' enabled
592 ExpressionGrammar aExpressionGrammer( pContext );
593 const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
594 ::boost::spirit::parse( aStart,
595 aEnd,
596 aExpressionGrammer >> ::boost::spirit::end_p,
597 ::boost::spirit::space_p ) );
599 #if OSL_DEBUG_LEVEL > 0
600 ::std::cout.flush(); // needed to keep stdout and cout in sync
601 #endif
602 // input fully congested by the parser?
603 if( !aParseInfo.full )
604 throw ParseError( "SmilFunctionParser::parseSmilFunction(): string not fully parseable" );
606 // parser's state stack now must contain exactly _one_ ExpressionNode,
607 // which represents our formula.
608 if( pContext->maOperandStack.size() != 1 )
609 throw ParseError( "SmilFunctionParser::parseSmilFunction(): incomplete or empty expression" );
611 return pContext->maOperandStack.top();
616 #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
618 // debug hooks
620 namespace boost
623 void sp_scalar_constructor_hook(void *)
627 void sp_scalar_destructor_hook(void *)
633 #endif
635 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */