Bump version to 6.0-36
[LibreOffice.git] / slideshow / source / engine / smilfunctionparser.cxx
blobd7c5e8befb9f6eeab02ac2640fa91969ee3b74be
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>
30 #include <basegfx/matrix/b2dhommatrix.hxx>
31 #include <basegfx/point/b2dpoint.hxx>
33 // Makes parser a static resource,
34 // we're synchronized externally.
35 // But watch out, the parser might have
36 // state not visible to this code!
37 #define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
39 #if defined(DBG_UTIL)
40 #include <typeinfo>
41 #define BOOST_SPIRIT_DEBUG
42 #endif
43 #include <boost/spirit/include/classic_core.hpp>
45 #include <iostream>
46 #include <functional>
47 #include <algorithm>
48 #include <stack>
51 /* Implementation of SmilFunctionParser class */
53 namespace slideshow
55 namespace internal
57 namespace
59 typedef const sal_Char* StringIteratorT;
61 struct ParserContext
63 typedef ::std::stack< std::shared_ptr<ExpressionNode> > OperandStack;
65 // stores a stack of not-yet-evaluated operands. This is used
66 // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
67 // arguments from. If all arguments to an operator are constant,
68 // the operator pushes a precalculated result on the stack, and
69 // a composite ExpressionNode otherwise.
70 OperandStack maOperandStack;
72 // bounds of the shape this expression is associated with
73 ::basegfx::B2DRectangle maShapeBounds;
75 // when true, enable usage of time-dependent variable '$'
76 // in expressions
77 bool mbParseAnimationFunction;
80 typedef ::std::shared_ptr< ParserContext > ParserContextSharedPtr;
83 template< typename Generator > class ShapeBoundsFunctor
85 public:
86 ShapeBoundsFunctor( Generator aGenerator,
87 const ParserContextSharedPtr& rContext ) :
88 maGenerator( aGenerator ),
89 mpContext( rContext )
91 ENSURE_OR_THROW( mpContext,
92 "ShapeBoundsFunctor::ShapeBoundsFunctor(): Invalid context" );
95 void operator()( StringIteratorT, StringIteratorT ) const
97 mpContext->maOperandStack.push(
98 ExpressionNodeFactory::createConstantValueExpression(
99 maGenerator( mpContext->maShapeBounds ) ) );
102 private:
103 Generator maGenerator;
104 ParserContextSharedPtr mpContext;
107 template< typename Generator > ShapeBoundsFunctor< Generator >
108 makeShapeBoundsFunctor( const Generator& rGenerator,
109 const ParserContextSharedPtr& rContext )
111 return ShapeBoundsFunctor<Generator>(rGenerator, rContext);
114 /** Generate apriori constant value
116 class ConstantFunctor
118 public:
119 ConstantFunctor( double rValue,
120 const ParserContextSharedPtr& rContext ) :
121 mnValue( rValue ),
122 mpContext( rContext )
124 ENSURE_OR_THROW( mpContext,
125 "ConstantFunctor::ConstantFunctor(): Invalid context" );
128 void operator()( StringIteratorT, StringIteratorT ) const
130 mpContext->maOperandStack.push(
131 ExpressionNodeFactory::createConstantValueExpression( mnValue ) );
134 private:
135 const double mnValue;
136 ParserContextSharedPtr mpContext;
139 /** Generate parse-dependent-but-then-constant value
141 class DoubleConstantFunctor
143 public:
144 explicit DoubleConstantFunctor( const ParserContextSharedPtr& rContext ) :
145 mpContext( rContext )
147 ENSURE_OR_THROW( mpContext,
148 "DoubleConstantFunctor::DoubleConstantFunctor(): Invalid context" );
151 void operator()( double n ) const
153 // push constant value expression to the stack
154 mpContext->maOperandStack.push(
155 ExpressionNodeFactory::createConstantValueExpression( n ) );
158 private:
159 ParserContextSharedPtr mpContext;
162 /** Generate special t value expression node
164 class ValueTFunctor
166 public:
167 explicit ValueTFunctor( const ParserContextSharedPtr& rContext ) :
168 mpContext( rContext )
170 ENSURE_OR_THROW( mpContext,
171 "ValueTFunctor::ValueTFunctor(): Invalid context" );
174 void operator()( StringIteratorT, StringIteratorT ) const
176 if( !mpContext->mbParseAnimationFunction )
178 SAL_WARN("slideshow", "ValueTFunctor::operator(): variable encountered, but we're not parsing a function here" );
179 throw ParseError();
182 // push special t value expression to the stack
183 mpContext->maOperandStack.push(
184 ExpressionNodeFactory::createValueTExpression() );
187 private:
188 ParserContextSharedPtr mpContext;
191 template< typename Functor > class UnaryFunctionFunctor
193 private:
194 /** ExpressionNode implementation for unary
195 function over one ExpressionNode
197 class UnaryFunctionExpression : public ExpressionNode
199 public:
200 UnaryFunctionExpression( const Functor& rFunctor,
201 const std::shared_ptr<ExpressionNode>& rArg ) :
202 maFunctor( rFunctor ),
203 mpArg( rArg )
207 virtual double operator()( double t ) const override
209 return maFunctor( (*mpArg)(t) );
212 virtual bool isConstant() const override
214 return mpArg->isConstant();
217 private:
218 Functor maFunctor;
219 std::shared_ptr<ExpressionNode> mpArg;
222 public:
223 UnaryFunctionFunctor( const Functor& rFunctor,
224 const ParserContextSharedPtr& rContext ) :
225 maFunctor( rFunctor ),
226 mpContext( rContext )
228 ENSURE_OR_THROW( mpContext,
229 "UnaryFunctionFunctor::UnaryFunctionFunctor(): Invalid context" );
232 void operator()( StringIteratorT, StringIteratorT ) const
234 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
236 if( rNodeStack.size() < 1 )
237 throw ParseError( "Not enough arguments for unary operator" );
239 // retrieve arguments
240 std::shared_ptr<ExpressionNode> pArg( rNodeStack.top() );
241 rNodeStack.pop();
243 // check for constness
244 if( pArg->isConstant() )
246 rNodeStack.push(
247 ExpressionNodeFactory::createConstantValueExpression(
248 maFunctor( (*pArg)(0.0) ) ) );
250 else
252 // push complex node, that calcs the value on demand
253 rNodeStack.push(
254 std::shared_ptr<ExpressionNode>(
255 new UnaryFunctionExpression(
256 maFunctor,
257 pArg ) ) );
261 private:
262 Functor maFunctor;
263 ParserContextSharedPtr mpContext;
266 // TODO(Q2): Refactor makeUnaryFunctionFunctor,
267 // makeBinaryFunctionFunctor and the whole
268 // ExpressionNodeFactory, to use a generic
269 // makeFunctionFunctor template, which is overloaded for
270 // unary, binary, ternary, etc. function pointers.
271 template< typename Functor > UnaryFunctionFunctor<Functor>
272 makeUnaryFunctionFunctor( const Functor& rFunctor,
273 const ParserContextSharedPtr& rContext )
275 return UnaryFunctionFunctor<Functor>( rFunctor, rContext );
278 // MSVC has problems instantiating above template function with plain function
279 // pointers (doesn't like the const reference there). Thus, provide it with
280 // a dedicated overload here.
281 UnaryFunctionFunctor< double (*)(double) >
282 makeUnaryFunctionFunctor( double (*pFunc)(double),
283 const ParserContextSharedPtr& rContext )
285 return UnaryFunctionFunctor< double (*)(double) >( pFunc, rContext );
288 /** Implements a binary function over two ExpressionNodes
290 @tpl Generator
291 Generator functor, to generate an ExpressionNode of
292 appropriate type
295 template< class Generator > class BinaryFunctionFunctor
297 public:
298 BinaryFunctionFunctor( const Generator& rGenerator,
299 const ParserContextSharedPtr& rContext ) :
300 maGenerator( rGenerator ),
301 mpContext( rContext )
303 ENSURE_OR_THROW( mpContext,
304 "BinaryFunctionFunctor::BinaryFunctionFunctor(): Invalid context" );
307 void operator()( StringIteratorT, StringIteratorT ) const
309 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
311 if( rNodeStack.size() < 2 )
312 throw ParseError( "Not enough arguments for binary operator" );
314 // retrieve arguments
315 std::shared_ptr<ExpressionNode> pSecondArg( rNodeStack.top() );
316 rNodeStack.pop();
317 std::shared_ptr<ExpressionNode> pFirstArg( rNodeStack.top() );
318 rNodeStack.pop();
320 // create combined ExpressionNode
321 std::shared_ptr<ExpressionNode> pNode( maGenerator( pFirstArg,
322 pSecondArg ) );
323 // check for constness
324 if( pFirstArg->isConstant() &&
325 pSecondArg->isConstant() )
327 // call the operator() at pNode, store result
328 // in constant value ExpressionNode.
329 rNodeStack.push(
330 ExpressionNodeFactory::createConstantValueExpression(
331 (*pNode)( 0.0 ) ) );
333 else
335 // push complex node, that calcs the value on demand
336 rNodeStack.push( pNode );
340 private:
341 Generator maGenerator;
342 ParserContextSharedPtr mpContext;
345 template< typename Generator > BinaryFunctionFunctor<Generator>
346 makeBinaryFunctionFunctor( const Generator& rGenerator,
347 const ParserContextSharedPtr& rContext )
349 return BinaryFunctionFunctor<Generator>( rGenerator, rContext );
353 // Workaround for MSVC compiler anomaly (stack trashing)
355 // The default ureal_parser_policies implementation of parse_exp
356 // triggers a really weird error in MSVC7 (Version 13.00.9466), in
357 // that the real_parser_impl::parse_main() call of parse_exp()
358 // overwrites the frame pointer _on the stack_ (EBP of the calling
359 // function gets overwritten while lying on the stack).
361 // For the time being, our parser thus can only read the 1.0E10
362 // notation, not the 1.0e10 one.
364 // TODO(F1): Also handle the 1.0e10 case here.
365 template< typename T > struct custom_real_parser_policies : public ::boost::spirit::ureal_parser_policies<T>
367 template< typename ScannerT >
368 static typename ::boost::spirit::parser_result< ::boost::spirit::chlit<>, ScannerT >::type
369 parse_exp(ScannerT& scan)
371 // as_lower_d somehow breaks MSVC7
372 return ::boost::spirit::ch_p('E').parse(scan);
376 /* This class implements the following grammar (more or
377 less literally written down below, only slightly
378 obfuscated by the parser actions):
380 identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
382 function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
384 basic_expression =
385 number |
386 identifier |
387 function '(' additive_expression ')' |
388 '(' additive_expression ')'
390 unary_expression =
391 '-' basic_expression |
392 basic_expression
394 multiplicative_expression =
395 unary_expression ( ( '*' unary_expression )* |
396 ( '/' unary_expression )* )
398 additive_expression =
399 multiplicative_expression ( ( '+' multiplicative_expression )* |
400 ( '-' multiplicative_expression )* )
403 class ExpressionGrammar : public ::boost::spirit::grammar< ExpressionGrammar >
405 public:
406 /** Create an arithmetic expression grammar
408 @param rParserContext
409 Contains context info for the parser
411 explicit ExpressionGrammar( const ParserContextSharedPtr& rParserContext ) :
412 mpParserContext( rParserContext )
416 template< typename ScannerT > class definition
418 public:
419 // grammar definition
420 explicit definition( const ExpressionGrammar& self )
422 using ::boost::spirit::str_p;
423 using ::boost::spirit::real_parser;
425 identifier =
426 str_p( "$" )[ ValueTFunctor( self.getContext()) ]
427 | str_p( "pi" )[ ConstantFunctor(M_PI, self.getContext()) ]
428 | str_p( "e" )[ ConstantFunctor(M_E, self.getContext()) ]
429 | str_p( "x" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterX),self.getContext()) ]
430 | str_p( "y" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterY),self.getContext()) ]
431 | str_p( "width" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getWidth), self.getContext()) ]
432 | str_p( "height" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getHeight), self.getContext()) ]
435 unaryFunction =
436 (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&fabs, self.getContext()) ]
437 | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sqrt, self.getContext()) ]
438 | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sin, self.getContext()) ]
439 | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&cos, self.getContext()) ]
440 | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&tan, self.getContext()) ]
441 | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&atan, self.getContext()) ]
442 | (str_p( "acos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&acos, self.getContext()) ]
443 | (str_p( "asin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&asin, self.getContext()) ]
444 | (str_p( "exp" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&exp, self.getContext()) ]
445 | (str_p( "log" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&log, self.getContext()) ]
448 binaryFunction =
449 (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinExpression, self.getContext()) ]
450 | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMaxExpression, self.getContext()) ]
453 basicExpression =
454 real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
455 | identifier
456 | unaryFunction
457 | binaryFunction
458 | '(' >> additiveExpression >> ')'
461 unaryExpression =
462 ('-' >> basicExpression)[ makeUnaryFunctionFunctor(::std::negate<double>(), self.getContext()) ]
463 | basicExpression
466 multiplicativeExpression =
467 unaryExpression
468 >> *( ('*' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMultipliesExpression, self.getContext()) ]
469 | ('/' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createDividesExpression, self.getContext()) ]
473 additiveExpression =
474 multiplicativeExpression
475 >> *( ('+' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createPlusExpression, self.getContext()) ]
476 | ('-' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinusExpression, self.getContext()) ]
480 BOOST_SPIRIT_DEBUG_RULE(additiveExpression);
481 BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression);
482 BOOST_SPIRIT_DEBUG_RULE(unaryExpression);
483 BOOST_SPIRIT_DEBUG_RULE(basicExpression);
484 BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
485 BOOST_SPIRIT_DEBUG_RULE(binaryFunction);
486 BOOST_SPIRIT_DEBUG_RULE(identifier);
489 const ::boost::spirit::rule< ScannerT >& start() const
491 return additiveExpression;
494 private:
495 // the constituents of the Spirit arithmetic expression grammar.
496 // For the sake of readability, without 'ma' prefix.
497 ::boost::spirit::rule< ScannerT > additiveExpression;
498 ::boost::spirit::rule< ScannerT > multiplicativeExpression;
499 ::boost::spirit::rule< ScannerT > unaryExpression;
500 ::boost::spirit::rule< ScannerT > basicExpression;
501 ::boost::spirit::rule< ScannerT > unaryFunction;
502 ::boost::spirit::rule< ScannerT > binaryFunction;
503 ::boost::spirit::rule< ScannerT > identifier;
506 const ParserContextSharedPtr& getContext() const
508 return mpParserContext;
511 private:
512 ParserContextSharedPtr mpParserContext; // might get modified during parsing
515 const ParserContextSharedPtr& getParserContext()
517 static ParserContextSharedPtr lcl_parserContext( new ParserContext );
519 // clear node stack (since we reuse the static object, that's
520 // the whole point here)
521 while( !lcl_parserContext->maOperandStack.empty() )
522 lcl_parserContext->maOperandStack.pop();
524 return lcl_parserContext;
528 std::shared_ptr<ExpressionNode> SmilFunctionParser::parseSmilValue( const OUString& rSmilValue,
529 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
531 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
532 // gives better conversion robustness here (we might want to map space
533 // etc. to ASCII space here)
534 const OString& rAsciiSmilValue(
535 OUStringToOString( rSmilValue, RTL_TEXTENCODING_ASCII_US ) );
537 StringIteratorT aStart( rAsciiSmilValue.getStr() );
538 StringIteratorT aEnd( rAsciiSmilValue.getStr()+rAsciiSmilValue.getLength() );
540 ParserContextSharedPtr pContext;
542 // static parser context, because the actual
543 // Spirit parser is also a static object
544 pContext = getParserContext();
546 pContext->maShapeBounds = rRelativeShapeBounds;
547 pContext->mbParseAnimationFunction = false; // parse with '$' disabled
550 ExpressionGrammar aExpressionGrammer( pContext );
551 const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
552 ::boost::spirit::parse( aStart,
553 aEnd,
554 aExpressionGrammer,
555 ::boost::spirit::space_p ) );
557 #if OSL_DEBUG_LEVEL > 0
558 ::std::cout.flush(); // needed to keep stdout and cout in sync
559 #endif
561 // input fully congested by the parser?
562 if( !aParseInfo.full )
563 throw ParseError( "SmilFunctionParser::parseSmilValue(): string not fully parseable" );
565 // parser's state stack now must contain exactly _one_ ExpressionNode,
566 // which represents our formula.
567 if( pContext->maOperandStack.size() != 1 )
568 throw ParseError( "SmilFunctionParser::parseSmilValue(): incomplete or empty expression" );
570 return pContext->maOperandStack.top();
573 std::shared_ptr<ExpressionNode> SmilFunctionParser::parseSmilFunction( const OUString& rSmilFunction,
574 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
576 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
577 // gives better conversion robustness here (we might want to map space
578 // etc. to ASCII space here)
579 const OString& rAsciiSmilFunction(
580 OUStringToOString( rSmilFunction, RTL_TEXTENCODING_ASCII_US ) );
582 StringIteratorT aStart( rAsciiSmilFunction.getStr() );
583 StringIteratorT aEnd( rAsciiSmilFunction.getStr()+rAsciiSmilFunction.getLength() );
585 ParserContextSharedPtr pContext;
587 // static parser context, because the actual
588 // Spirit parser is also a static object
589 pContext = getParserContext();
591 pContext->maShapeBounds = rRelativeShapeBounds;
592 pContext->mbParseAnimationFunction = true; // parse with '$' enabled
595 ExpressionGrammar aExpressionGrammer( pContext );
596 const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
597 ::boost::spirit::parse( aStart,
598 aEnd,
599 aExpressionGrammer >> ::boost::spirit::end_p,
600 ::boost::spirit::space_p ) );
602 #if OSL_DEBUG_LEVEL > 0
603 ::std::cout.flush(); // needed to keep stdout and cout in sync
604 #endif
605 // input fully congested by the parser?
606 if( !aParseInfo.full )
607 throw ParseError( "SmilFunctionParser::parseSmilFunction(): string not fully parseable" );
609 // parser's state stack now must contain exactly _one_ ExpressionNode,
610 // which represents our formula.
611 if( pContext->maOperandStack.size() != 1 )
612 throw ParseError( "SmilFunctionParser::parseSmilFunction(): incomplete or empty expression" );
614 return pContext->maOperandStack.top();
619 #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
621 // debug hooks
623 namespace boost
626 void sp_scalar_constructor_hook(void *)
630 void sp_scalar_destructor_hook(void *)
636 #endif
638 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */