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