Update ooo320-m1
[ooovba.git] / slideshow / source / engine / smilfunctionparser.cxx
blob7a06548f1da1249ddbd3d2aaa9adc37d64f6dd7c
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: smilfunctionparser.cxx,v $
10 * $Revision: 1.10 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_slideshow.hxx"
34 // must be first
35 #include <canvas/debug.hxx>
36 #include <tools/diagnose_ex.h>
38 #include <rtl/math.hxx>
40 #include <smilfunctionparser.hxx>
41 #include <expressionnodefactory.hxx>
43 #include <rtl/ustring.hxx>
44 #include <canvas/verbosetrace.hxx>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <basegfx/point/b2dpoint.hxx>
49 // Makes parser a static resource,
50 // we're synchronized externally.
51 // But watch out, the parser might have
52 // state not visible to this code!
53 #define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
54 #if defined(VERBOSE) && defined(DBG_UTIL)
55 #include <typeinfo>
56 #define BOOST_SPIRIT_DEBUG
57 #endif
58 #include <boost/spirit/include/classic_core.hpp>
60 #if OSL_DEBUG_LEVEL > 0
61 #include <iostream>
62 #endif
63 #include <functional>
64 #include <algorithm>
65 #include <stack>
69 /* Implementation of SmilFunctionParser class */
71 namespace slideshow
73 namespace internal
75 namespace
77 typedef const sal_Char* StringIteratorT;
79 struct ParserContext
81 typedef ::std::stack< ExpressionNodeSharedPtr > OperandStack;
83 // stores a stack of not-yet-evaluated operands. This is used
84 // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
85 // arguments from. If all arguments to an operator are constant,
86 // the operator pushes a precalculated result on the stack, and
87 // a composite ExpressionNode otherwise.
88 OperandStack maOperandStack;
90 // bounds of the shape this expression is associated with
91 ::basegfx::B2DRectangle maShapeBounds;
93 // when true, enable usage of time-dependent variable '$'
94 // in expressions
95 bool mbParseAnimationFunction;
98 typedef ::boost::shared_ptr< ParserContext > ParserContextSharedPtr;
101 template< typename Generator > class ShapeBoundsFunctor
103 public:
104 ShapeBoundsFunctor( Generator aGenerator,
105 const ParserContextSharedPtr& rContext ) :
106 maGenerator( aGenerator ),
107 mpContext( rContext )
109 ENSURE_OR_THROW( mpContext,
110 "ShapeBoundsFunctor::ShapeBoundsFunctor(): Invalid context" );
113 void operator()( StringIteratorT, StringIteratorT ) const
115 mpContext->maOperandStack.push(
116 ExpressionNodeFactory::createConstantValueExpression(
117 maGenerator( mpContext->maShapeBounds ) ) );
120 private:
121 Generator maGenerator;
122 ParserContextSharedPtr mpContext;
125 template< typename Generator > ShapeBoundsFunctor< Generator >
126 makeShapeBoundsFunctor( const Generator& rGenerator,
127 const ParserContextSharedPtr& rContext )
129 return ShapeBoundsFunctor<Generator>(rGenerator, rContext);
132 /** Generate apriori constant value
134 class ConstantFunctor
136 public:
137 ConstantFunctor( double rValue,
138 const ParserContextSharedPtr& rContext ) :
139 mnValue( rValue ),
140 mpContext( rContext )
142 ENSURE_OR_THROW( mpContext,
143 "ConstantFunctor::ConstantFunctor(): Invalid context" );
146 void operator()( StringIteratorT, StringIteratorT ) const
148 mpContext->maOperandStack.push(
149 ExpressionNodeFactory::createConstantValueExpression( mnValue ) );
152 private:
153 const double mnValue;
154 ParserContextSharedPtr mpContext;
157 /** Generate parse-dependent-but-then-constant value
159 class DoubleConstantFunctor
161 public:
162 DoubleConstantFunctor( const ParserContextSharedPtr& rContext ) :
163 mpContext( rContext )
165 ENSURE_OR_THROW( mpContext,
166 "DoubleConstantFunctor::DoubleConstantFunctor(): Invalid context" );
169 void operator()( double n ) const
171 // push constant value expression to the stack
172 mpContext->maOperandStack.push(
173 ExpressionNodeFactory::createConstantValueExpression( n ) );
176 private:
177 ParserContextSharedPtr mpContext;
180 /** Generate special t value expression node
182 class ValueTFunctor
184 public:
185 ValueTFunctor( const ParserContextSharedPtr& rContext ) :
186 mpContext( rContext )
188 ENSURE_OR_THROW( mpContext,
189 "ValueTFunctor::ValueTFunctor(): Invalid context" );
192 void operator()( StringIteratorT, StringIteratorT ) const
194 if( !mpContext->mbParseAnimationFunction )
196 OSL_ENSURE( false,
197 "ValueTFunctor::operator(): variable encountered, but we're not parsing a function here" );
198 throw ParseError();
201 // push special t value expression to the stack
202 mpContext->maOperandStack.push(
203 ExpressionNodeFactory::createValueTExpression() );
206 private:
207 ParserContextSharedPtr mpContext;
210 template< typename Functor > class UnaryFunctionFunctor
212 private:
213 /** ExpressionNode implementation for unary
214 function over one ExpressionNode
216 class UnaryFunctionExpression : public ExpressionNode
218 public:
219 UnaryFunctionExpression( const Functor& rFunctor,
220 const ExpressionNodeSharedPtr& rArg ) :
221 maFunctor( rFunctor ),
222 mpArg( rArg )
226 virtual double operator()( double t ) const
228 return maFunctor( (*mpArg)(t) );
231 virtual bool isConstant() const
233 return mpArg->isConstant();
236 private:
237 Functor maFunctor;
238 ExpressionNodeSharedPtr mpArg;
241 public:
242 UnaryFunctionFunctor( const Functor& rFunctor,
243 const ParserContextSharedPtr& rContext ) :
244 maFunctor( rFunctor ),
245 mpContext( rContext )
247 ENSURE_OR_THROW( mpContext,
248 "UnaryFunctionFunctor::UnaryFunctionFunctor(): Invalid context" );
251 void operator()( StringIteratorT, StringIteratorT ) const
253 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
255 if( rNodeStack.size() < 1 )
256 throw ParseError( "Not enough arguments for unary operator" );
258 // retrieve arguments
259 ExpressionNodeSharedPtr pArg( rNodeStack.top() );
260 rNodeStack.pop();
262 // check for constness
263 if( pArg->isConstant() )
265 rNodeStack.push(
266 ExpressionNodeFactory::createConstantValueExpression(
267 maFunctor( (*pArg)(0.0) ) ) );
269 else
271 // push complex node, that calcs the value on demand
272 rNodeStack.push(
273 ExpressionNodeSharedPtr(
274 new UnaryFunctionExpression(
275 maFunctor,
276 pArg ) ) );
280 private:
281 Functor maFunctor;
282 ParserContextSharedPtr mpContext;
285 // TODO(Q2): Refactor makeUnaryFunctionFunctor,
286 // makeBinaryFunctionFunctor and the whole
287 // ExpressionNodeFactory, to use a generic
288 // makeFunctionFunctor template, which is overloaded for
289 // unary, binary, ternary, etc. function pointers.
290 template< typename Functor > UnaryFunctionFunctor<Functor>
291 makeUnaryFunctionFunctor( const Functor& rFunctor,
292 const ParserContextSharedPtr& rContext )
294 return UnaryFunctionFunctor<Functor>( rFunctor, rContext );
297 // MSVC has problems instantiating above template function with plain function
298 // pointers (doesn't like the const reference there). Thus, provide it with
299 // a dedicated overload here.
300 UnaryFunctionFunctor< double (*)(double) >
301 makeUnaryFunctionFunctor( double (*pFunc)(double),
302 const ParserContextSharedPtr& rContext )
304 return UnaryFunctionFunctor< double (*)(double) >( pFunc, rContext );
307 /** Implements a binary function over two ExpressionNodes
309 @tpl Generator
310 Generator functor, to generate an ExpressionNode of
311 appropriate type
314 template< class Generator > class BinaryFunctionFunctor
316 public:
317 BinaryFunctionFunctor( const Generator& rGenerator,
318 const ParserContextSharedPtr& rContext ) :
319 maGenerator( rGenerator ),
320 mpContext( rContext )
322 ENSURE_OR_THROW( mpContext,
323 "BinaryFunctionFunctor::BinaryFunctionFunctor(): Invalid context" );
326 void operator()( StringIteratorT, StringIteratorT ) const
328 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
330 if( rNodeStack.size() < 2 )
331 throw ParseError( "Not enough arguments for binary operator" );
333 // retrieve arguments
334 ExpressionNodeSharedPtr pSecondArg( rNodeStack.top() );
335 rNodeStack.pop();
336 ExpressionNodeSharedPtr pFirstArg( rNodeStack.top() );
337 rNodeStack.pop();
339 // create combined ExpressionNode
340 ExpressionNodeSharedPtr pNode( maGenerator( pFirstArg,
341 pSecondArg ) );
342 // check for constness
343 if( pFirstArg->isConstant() &&
344 pSecondArg->isConstant() )
346 // call the operator() at pNode, store result
347 // in constant value ExpressionNode.
348 rNodeStack.push(
349 ExpressionNodeFactory::createConstantValueExpression(
350 (*pNode)( 0.0 ) ) );
352 else
354 // push complex node, that calcs the value on demand
355 rNodeStack.push( pNode );
359 private:
360 Generator maGenerator;
361 ParserContextSharedPtr mpContext;
364 template< typename Generator > BinaryFunctionFunctor<Generator>
365 makeBinaryFunctionFunctor( const Generator& rGenerator,
366 const ParserContextSharedPtr& rContext )
368 return BinaryFunctionFunctor<Generator>( rGenerator, rContext );
372 // Workaround for MSVC compiler anomaly (stack trashing)
374 // The default ureal_parser_policies implementation of parse_exp
375 // triggers a really weird error in MSVC7 (Version 13.00.9466), in
376 // that the real_parser_impl::parse_main() call of parse_exp()
377 // overwrites the frame pointer _on the stack_ (EBP of the calling
378 // function gets overwritten while lying on the stack).
380 // For the time being, our parser thus can only read the 1.0E10
381 // notation, not the 1.0e10 one.
383 // TODO(F1): Also handle the 1.0e10 case here.
384 template< typename T > struct custom_real_parser_policies : public ::boost::spirit::ureal_parser_policies<T>
386 template< typename ScannerT >
387 static typename ::boost::spirit::parser_result< ::boost::spirit::chlit<>, ScannerT >::type
388 parse_exp(ScannerT& scan)
390 // as_lower_d somehow breaks MSVC7
391 return ::boost::spirit::ch_p('E').parse(scan);
395 /* This class implements the following grammar (more or
396 less literally written down below, only slightly
397 obfuscated by the parser actions):
399 identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
401 function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
403 basic_expression =
404 number |
405 identifier |
406 function '(' additive_expression ')' |
407 '(' additive_expression ')'
409 unary_expression =
410 '-' basic_expression |
411 basic_expression
413 multiplicative_expression =
414 unary_expression ( ( '*' unary_expression )* |
415 ( '/' unary_expression )* )
417 additive_expression =
418 multiplicative_expression ( ( '+' multiplicative_expression )* |
419 ( '-' multiplicative_expression )* )
422 class ExpressionGrammar : public ::boost::spirit::grammar< ExpressionGrammar >
424 public:
425 /** Create an arithmetic expression grammar
427 @param rParserContext
428 Contains context info for the parser
430 ExpressionGrammar( const ParserContextSharedPtr& rParserContext ) :
431 mpParserContext( rParserContext )
435 template< typename ScannerT > class definition
437 public:
438 // grammar definition
439 definition( const ExpressionGrammar& self )
441 using ::boost::spirit::str_p;
442 using ::boost::spirit::real_parser;
444 identifier =
445 str_p( "$" )[ ValueTFunctor( self.getContext()) ]
446 | str_p( "pi" )[ ConstantFunctor(M_PI, self.getContext()) ]
447 | str_p( "e" )[ ConstantFunctor(M_E, self.getContext()) ]
448 | str_p( "x" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterX),self.getContext()) ]
449 | str_p( "y" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterY),self.getContext()) ]
450 | str_p( "width" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getWidth), self.getContext()) ]
451 | str_p( "height" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getHeight), self.getContext()) ]
454 unaryFunction =
455 (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&fabs, self.getContext()) ]
456 | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sqrt, self.getContext()) ]
457 | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sin, self.getContext()) ]
458 | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&cos, self.getContext()) ]
459 | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&tan, self.getContext()) ]
460 | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&atan, self.getContext()) ]
461 | (str_p( "acos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&acos, self.getContext()) ]
462 | (str_p( "asin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&asin, self.getContext()) ]
463 | (str_p( "exp" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&exp, self.getContext()) ]
464 | (str_p( "log" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&log, self.getContext()) ]
467 binaryFunction =
468 (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinExpression, self.getContext()) ]
469 | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMaxExpression, self.getContext()) ]
472 basicExpression =
473 real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
474 | identifier
475 | unaryFunction
476 | binaryFunction
477 | '(' >> additiveExpression >> ')'
480 unaryExpression =
481 ('-' >> basicExpression)[ makeUnaryFunctionFunctor(::std::negate<double>(), self.getContext()) ]
482 | basicExpression
485 multiplicativeExpression =
486 unaryExpression
487 >> *( ('*' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMultipliesExpression, self.getContext()) ]
488 | ('/' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createDividesExpression, self.getContext()) ]
492 additiveExpression =
493 multiplicativeExpression
494 >> *( ('+' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createPlusExpression, self.getContext()) ]
495 | ('-' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinusExpression, self.getContext()) ]
499 BOOST_SPIRIT_DEBUG_RULE(additiveExpression);
500 BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression);
501 BOOST_SPIRIT_DEBUG_RULE(unaryExpression);
502 BOOST_SPIRIT_DEBUG_RULE(basicExpression);
503 BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
504 BOOST_SPIRIT_DEBUG_RULE(binaryFunction);
505 BOOST_SPIRIT_DEBUG_RULE(identifier);
508 const ::boost::spirit::rule< ScannerT >& start() const
510 return additiveExpression;
513 private:
514 // the constituents of the Spirit arithmetic expression grammar.
515 // For the sake of readability, without 'ma' prefix.
516 ::boost::spirit::rule< ScannerT > additiveExpression;
517 ::boost::spirit::rule< ScannerT > multiplicativeExpression;
518 ::boost::spirit::rule< ScannerT > unaryExpression;
519 ::boost::spirit::rule< ScannerT > basicExpression;
520 ::boost::spirit::rule< ScannerT > unaryFunction;
521 ::boost::spirit::rule< ScannerT > binaryFunction;
522 ::boost::spirit::rule< ScannerT > identifier;
525 const ParserContextSharedPtr& getContext() const
527 return mpParserContext;
530 private:
531 ParserContextSharedPtr mpParserContext; // might get modified during parsing
534 #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
535 const ParserContextSharedPtr& getParserContext()
537 static ParserContextSharedPtr lcl_parserContext( new ParserContext() );
539 // clear node stack (since we reuse the static object, that's
540 // the whole point here)
541 while( !lcl_parserContext->maOperandStack.empty() )
542 lcl_parserContext->maOperandStack.pop();
544 return lcl_parserContext;
546 #endif
549 ExpressionNodeSharedPtr SmilFunctionParser::parseSmilValue( const ::rtl::OUString& rSmilValue,
550 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
552 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
553 // gives better conversion robustness here (we might want to map space
554 // etc. to ASCII space here)
555 const ::rtl::OString& rAsciiSmilValue(
556 rtl::OUStringToOString( rSmilValue, RTL_TEXTENCODING_ASCII_US ) );
558 StringIteratorT aStart( rAsciiSmilValue.getStr() );
559 StringIteratorT aEnd( rAsciiSmilValue.getStr()+rAsciiSmilValue.getLength() );
561 ParserContextSharedPtr pContext;
563 #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
564 // static parser context, because the actual
565 // Spirit parser is also a static object
566 pContext = getParserContext();
567 #else
568 pContext.reset( new ParserContext() );
569 #endif
571 pContext->maShapeBounds = rRelativeShapeBounds;
572 pContext->mbParseAnimationFunction = false; // parse with '$' disabled
575 ExpressionGrammar aExpressionGrammer( pContext );
576 const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
577 ::boost::spirit::parse( aStart,
578 aEnd,
579 aExpressionGrammer,
580 ::boost::spirit::space_p ) );
581 OSL_DEBUG_ONLY(::std::cout.flush()); // needed to keep stdout and cout in sync
583 // input fully congested by the parser?
584 if( !aParseInfo.full )
585 throw ParseError( "SmilFunctionParser::parseSmilValue(): string not fully parseable" );
587 // parser's state stack now must contain exactly _one_ ExpressionNode,
588 // which represents our formula.
589 if( pContext->maOperandStack.size() != 1 )
590 throw ParseError( "SmilFunctionParser::parseSmilValue(): incomplete or empty expression" );
592 return pContext->maOperandStack.top();
595 ExpressionNodeSharedPtr SmilFunctionParser::parseSmilFunction( const ::rtl::OUString& rSmilFunction,
596 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
598 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
599 // gives better conversion robustness here (we might want to map space
600 // etc. to ASCII space here)
601 const ::rtl::OString& rAsciiSmilFunction(
602 rtl::OUStringToOString( rSmilFunction, RTL_TEXTENCODING_ASCII_US ) );
604 StringIteratorT aStart( rAsciiSmilFunction.getStr() );
605 StringIteratorT aEnd( rAsciiSmilFunction.getStr()+rAsciiSmilFunction.getLength() );
607 ParserContextSharedPtr pContext;
609 #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
610 // static parser context, because the actual
611 // Spirit parser is also a static object
612 pContext = getParserContext();
613 #else
614 pContext.reset( new ParserContext() );
615 #endif
617 pContext->maShapeBounds = rRelativeShapeBounds;
618 pContext->mbParseAnimationFunction = true; // parse with '$' enabled
621 ExpressionGrammar aExpressionGrammer( pContext );
622 const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
623 ::boost::spirit::parse( aStart,
624 aEnd,
625 aExpressionGrammer >> ::boost::spirit::end_p,
626 ::boost::spirit::space_p ) );
627 OSL_DEBUG_ONLY(::std::cout.flush()); // needed to keep stdout and cout in sync
629 // input fully congested by the parser?
630 if( !aParseInfo.full )
631 throw ParseError( "SmilFunctionParser::parseSmilFunction(): string not fully parseable" );
633 // parser's state stack now must contain exactly _one_ ExpressionNode,
634 // which represents our formula.
635 if( pContext->maOperandStack.size() != 1 )
636 throw ParseError( "SmilFunctionParser::parseSmilFunction(): incomplete or empty expression" );
638 return pContext->maOperandStack.top();