Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
21 : // must be first
22 : #include <canvas/debug.hxx>
23 : #include <tools/diagnose_ex.h>
24 :
25 : #include <rtl/math.hxx>
26 :
27 : #include <smilfunctionparser.hxx>
28 : #include <expressionnodefactory.hxx>
29 :
30 : #include <rtl/ustring.hxx>
31 : #include <canvas/verbosetrace.hxx>
32 :
33 : #include <basegfx/matrix/b2dhommatrix.hxx>
34 : #include <basegfx/point/b2dpoint.hxx>
35 :
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>
46 :
47 : #if OSL_DEBUG_LEVEL > 0
48 : #include <iostream>
49 : #endif
50 : #include <functional>
51 : #include <algorithm>
52 : #include <stack>
53 :
54 :
55 :
56 : /* Implementation of SmilFunctionParser class */
57 :
58 : namespace slideshow
59 : {
60 : namespace internal
61 : {
62 : namespace
63 : {
64 : typedef const sal_Char* StringIteratorT;
65 :
66 0 : struct ParserContext
67 : {
68 : typedef ::std::stack< ExpressionNodeSharedPtr > OperandStack;
69 :
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;
76 :
77 : // bounds of the shape this expression is associated with
78 : ::basegfx::B2DRectangle maShapeBounds;
79 :
80 : // when true, enable usage of time-dependent variable '$'
81 : // in expressions
82 : bool mbParseAnimationFunction;
83 : };
84 :
85 : typedef ::boost::shared_ptr< ParserContext > ParserContextSharedPtr;
86 :
87 :
88 0 : template< typename Generator > class ShapeBoundsFunctor
89 : {
90 : public:
91 0 : ShapeBoundsFunctor( Generator aGenerator,
92 : const ParserContextSharedPtr& rContext ) :
93 : maGenerator( aGenerator ),
94 0 : mpContext( rContext )
95 : {
96 0 : ENSURE_OR_THROW( mpContext,
97 : "ShapeBoundsFunctor::ShapeBoundsFunctor(): Invalid context" );
98 0 : }
99 :
100 0 : void operator()( StringIteratorT, StringIteratorT ) const
101 : {
102 0 : mpContext->maOperandStack.push(
103 : ExpressionNodeFactory::createConstantValueExpression(
104 0 : maGenerator( mpContext->maShapeBounds ) ) );
105 0 : }
106 :
107 : private:
108 : Generator maGenerator;
109 : ParserContextSharedPtr mpContext;
110 : };
111 :
112 : template< typename Generator > ShapeBoundsFunctor< Generator >
113 0 : makeShapeBoundsFunctor( const Generator& rGenerator,
114 : const ParserContextSharedPtr& rContext )
115 : {
116 0 : return ShapeBoundsFunctor<Generator>(rGenerator, rContext);
117 : }
118 :
119 : /** Generate apriori constant value
120 : */
121 0 : class ConstantFunctor
122 : {
123 : public:
124 0 : ConstantFunctor( double rValue,
125 : const ParserContextSharedPtr& rContext ) :
126 : mnValue( rValue ),
127 0 : mpContext( rContext )
128 : {
129 0 : ENSURE_OR_THROW( mpContext,
130 : "ConstantFunctor::ConstantFunctor(): Invalid context" );
131 0 : }
132 :
133 0 : void operator()( StringIteratorT, StringIteratorT ) const
134 : {
135 0 : mpContext->maOperandStack.push(
136 0 : ExpressionNodeFactory::createConstantValueExpression( mnValue ) );
137 0 : }
138 :
139 : private:
140 : const double mnValue;
141 : ParserContextSharedPtr mpContext;
142 : };
143 :
144 : /** Generate parse-dependent-but-then-constant value
145 : */
146 0 : class DoubleConstantFunctor
147 : {
148 : public:
149 0 : DoubleConstantFunctor( const ParserContextSharedPtr& rContext ) :
150 0 : mpContext( rContext )
151 : {
152 0 : ENSURE_OR_THROW( mpContext,
153 : "DoubleConstantFunctor::DoubleConstantFunctor(): Invalid context" );
154 0 : }
155 :
156 0 : void operator()( double n ) const
157 : {
158 : // push constant value expression to the stack
159 0 : mpContext->maOperandStack.push(
160 0 : ExpressionNodeFactory::createConstantValueExpression( n ) );
161 0 : }
162 :
163 : private:
164 : ParserContextSharedPtr mpContext;
165 : };
166 :
167 : /** Generate special t value expression node
168 : */
169 0 : class ValueTFunctor
170 : {
171 : public:
172 0 : ValueTFunctor( const ParserContextSharedPtr& rContext ) :
173 0 : mpContext( rContext )
174 : {
175 0 : ENSURE_OR_THROW( mpContext,
176 : "ValueTFunctor::ValueTFunctor(): Invalid context" );
177 0 : }
178 :
179 0 : void operator()( StringIteratorT, StringIteratorT ) const
180 : {
181 0 : if( !mpContext->mbParseAnimationFunction )
182 : {
183 : OSL_FAIL( "ValueTFunctor::operator(): variable encountered, but we're not parsing a function here" );
184 0 : throw ParseError();
185 : }
186 :
187 : // push special t value expression to the stack
188 0 : mpContext->maOperandStack.push(
189 0 : ExpressionNodeFactory::createValueTExpression() );
190 0 : }
191 :
192 : private:
193 : ParserContextSharedPtr mpContext;
194 : };
195 :
196 0 : template< typename Functor > class UnaryFunctionFunctor
197 : {
198 : private:
199 : /** ExpressionNode implementation for unary
200 : function over one ExpressionNode
201 : */
202 0 : class UnaryFunctionExpression : public ExpressionNode
203 : {
204 : public:
205 0 : UnaryFunctionExpression( const Functor& rFunctor,
206 : const ExpressionNodeSharedPtr& rArg ) :
207 : maFunctor( rFunctor ),
208 0 : mpArg( rArg )
209 : {
210 0 : }
211 :
212 0 : virtual double operator()( double t ) const SAL_OVERRIDE
213 : {
214 0 : return maFunctor( (*mpArg)(t) );
215 : }
216 :
217 0 : virtual bool isConstant() const SAL_OVERRIDE
218 : {
219 0 : return mpArg->isConstant();
220 : }
221 :
222 : private:
223 : Functor maFunctor;
224 : ExpressionNodeSharedPtr mpArg;
225 : };
226 :
227 : public:
228 0 : UnaryFunctionFunctor( const Functor& rFunctor,
229 : const ParserContextSharedPtr& rContext ) :
230 : maFunctor( rFunctor ),
231 0 : mpContext( rContext )
232 : {
233 0 : ENSURE_OR_THROW( mpContext,
234 : "UnaryFunctionFunctor::UnaryFunctionFunctor(): Invalid context" );
235 0 : }
236 :
237 0 : void operator()( StringIteratorT, StringIteratorT ) const
238 : {
239 0 : ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
240 :
241 0 : if( rNodeStack.size() < 1 )
242 0 : throw ParseError( "Not enough arguments for unary operator" );
243 :
244 : // retrieve arguments
245 0 : ExpressionNodeSharedPtr pArg( rNodeStack.top() );
246 0 : rNodeStack.pop();
247 :
248 : // check for constness
249 0 : if( pArg->isConstant() )
250 : {
251 0 : rNodeStack.push(
252 : ExpressionNodeFactory::createConstantValueExpression(
253 0 : maFunctor( (*pArg)(0.0) ) ) );
254 : }
255 : else
256 : {
257 : // push complex node, that calcs the value on demand
258 0 : rNodeStack.push(
259 : ExpressionNodeSharedPtr(
260 : new UnaryFunctionExpression(
261 : maFunctor,
262 0 : pArg ) ) );
263 0 : }
264 0 : }
265 :
266 : private:
267 : Functor maFunctor;
268 : ParserContextSharedPtr mpContext;
269 : };
270 :
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 0 : makeUnaryFunctionFunctor( const Functor& rFunctor,
278 : const ParserContextSharedPtr& rContext )
279 : {
280 0 : return UnaryFunctionFunctor<Functor>( rFunctor, rContext );
281 : }
282 :
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 0 : makeUnaryFunctionFunctor( double (*pFunc)(double),
288 : const ParserContextSharedPtr& rContext )
289 : {
290 0 : return UnaryFunctionFunctor< double (*)(double) >( pFunc, rContext );
291 : }
292 :
293 : /** Implements a binary function over two ExpressionNodes
294 :
295 : @tpl Generator
296 : Generator functor, to generate an ExpressionNode of
297 : appropriate type
298 :
299 : */
300 0 : template< class Generator > class BinaryFunctionFunctor
301 : {
302 : public:
303 0 : BinaryFunctionFunctor( const Generator& rGenerator,
304 : const ParserContextSharedPtr& rContext ) :
305 : maGenerator( rGenerator ),
306 0 : mpContext( rContext )
307 : {
308 0 : ENSURE_OR_THROW( mpContext,
309 : "BinaryFunctionFunctor::BinaryFunctionFunctor(): Invalid context" );
310 0 : }
311 :
312 0 : void operator()( StringIteratorT, StringIteratorT ) const
313 : {
314 0 : ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
315 :
316 0 : if( rNodeStack.size() < 2 )
317 0 : throw ParseError( "Not enough arguments for binary operator" );
318 :
319 : // retrieve arguments
320 0 : ExpressionNodeSharedPtr pSecondArg( rNodeStack.top() );
321 0 : rNodeStack.pop();
322 0 : ExpressionNodeSharedPtr pFirstArg( rNodeStack.top() );
323 0 : rNodeStack.pop();
324 :
325 : // create combined ExpressionNode
326 : ExpressionNodeSharedPtr pNode( maGenerator( pFirstArg,
327 0 : pSecondArg ) );
328 : // check for constness
329 0 : if( pFirstArg->isConstant() &&
330 0 : pSecondArg->isConstant() )
331 : {
332 : // call the operator() at pNode, store result
333 : // in constant value ExpressionNode.
334 0 : rNodeStack.push(
335 : ExpressionNodeFactory::createConstantValueExpression(
336 0 : (*pNode)( 0.0 ) ) );
337 : }
338 : else
339 : {
340 : // push complex node, that calcs the value on demand
341 0 : rNodeStack.push( pNode );
342 0 : }
343 0 : }
344 :
345 : private:
346 : Generator maGenerator;
347 : ParserContextSharedPtr mpContext;
348 : };
349 :
350 : template< typename Generator > BinaryFunctionFunctor<Generator>
351 0 : makeBinaryFunctionFunctor( const Generator& rGenerator,
352 : const ParserContextSharedPtr& rContext )
353 : {
354 0 : return BinaryFunctionFunctor<Generator>( rGenerator, rContext );
355 : }
356 :
357 :
358 : // Workaround for MSVC compiler anomaly (stack trashing)
359 :
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).
365 :
366 : // For the time being, our parser thus can only read the 1.0E10
367 : // notation, not the 1.0e10 one.
368 :
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>
371 : {
372 : template< typename ScannerT >
373 : static typename ::boost::spirit::parser_result< ::boost::spirit::chlit<>, ScannerT >::type
374 0 : parse_exp(ScannerT& scan)
375 : {
376 : // as_lower_d somehow breaks MSVC7
377 0 : return ::boost::spirit::ch_p('E').parse(scan);
378 : }
379 : };
380 :
381 : /* This class implements the following grammar (more or
382 : less literally written down below, only slightly
383 : obfuscated by the parser actions):
384 :
385 : identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
386 :
387 : function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
388 :
389 : basic_expression =
390 : number |
391 : identifier |
392 : function '(' additive_expression ')' |
393 : '(' additive_expression ')'
394 :
395 : unary_expression =
396 : '-' basic_expression |
397 : basic_expression
398 :
399 : multiplicative_expression =
400 : unary_expression ( ( '*' unary_expression )* |
401 : ( '/' unary_expression )* )
402 :
403 : additive_expression =
404 : multiplicative_expression ( ( '+' multiplicative_expression )* |
405 : ( '-' multiplicative_expression )* )
406 :
407 : */
408 0 : class ExpressionGrammar : public ::boost::spirit::grammar< ExpressionGrammar >
409 : {
410 : public:
411 : /** Create an arithmetic expression grammar
412 :
413 : @param rParserContext
414 : Contains context info for the parser
415 : */
416 0 : ExpressionGrammar( const ParserContextSharedPtr& rParserContext ) :
417 0 : mpParserContext( rParserContext )
418 : {
419 0 : }
420 :
421 0 : template< typename ScannerT > class definition
422 : {
423 : public:
424 : // grammar definition
425 0 : definition( const ExpressionGrammar& self )
426 0 : {
427 : using ::boost::spirit::str_p;
428 : using ::boost::spirit::real_parser;
429 :
430 0 : identifier =
431 0 : str_p( "$" )[ ValueTFunctor( self.getContext()) ]
432 0 : | str_p( "pi" )[ ConstantFunctor(M_PI, self.getContext()) ]
433 0 : | str_p( "e" )[ ConstantFunctor(M_E, self.getContext()) ]
434 0 : | str_p( "x" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterX),self.getContext()) ]
435 0 : | str_p( "y" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterY),self.getContext()) ]
436 0 : | str_p( "width" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getWidth), self.getContext()) ]
437 0 : | str_p( "height" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getHeight), self.getContext()) ]
438 : ;
439 :
440 0 : unaryFunction =
441 0 : (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&fabs, self.getContext()) ]
442 0 : | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sqrt, self.getContext()) ]
443 0 : | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sin, self.getContext()) ]
444 0 : | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&cos, self.getContext()) ]
445 0 : | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&tan, self.getContext()) ]
446 0 : | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&atan, self.getContext()) ]
447 0 : | (str_p( "acos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&acos, self.getContext()) ]
448 0 : | (str_p( "asin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&asin, self.getContext()) ]
449 0 : | (str_p( "exp" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&exp, self.getContext()) ]
450 0 : | (str_p( "log" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&log, self.getContext()) ]
451 : ;
452 :
453 0 : binaryFunction =
454 0 : (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinExpression, self.getContext()) ]
455 0 : | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMaxExpression, self.getContext()) ]
456 : ;
457 :
458 0 : basicExpression =
459 0 : real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
460 : | identifier
461 : | unaryFunction
462 : | binaryFunction
463 0 : | '(' >> additiveExpression >> ')'
464 : ;
465 :
466 0 : unaryExpression =
467 0 : ('-' >> basicExpression)[ makeUnaryFunctionFunctor(::std::negate<double>(), self.getContext()) ]
468 : | basicExpression
469 : ;
470 :
471 0 : multiplicativeExpression =
472 : unaryExpression
473 0 : >> *( ('*' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMultipliesExpression, self.getContext()) ]
474 0 : | ('/' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createDividesExpression, self.getContext()) ]
475 : )
476 : ;
477 :
478 0 : additiveExpression =
479 : multiplicativeExpression
480 0 : >> *( ('+' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createPlusExpression, self.getContext()) ]
481 0 : | ('-' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinusExpression, self.getContext()) ]
482 : )
483 : ;
484 :
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);
492 0 : }
493 :
494 0 : const ::boost::spirit::rule< ScannerT >& start() const
495 : {
496 0 : return additiveExpression;
497 : }
498 :
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;
509 : };
510 :
511 0 : const ParserContextSharedPtr& getContext() const
512 : {
513 0 : return mpParserContext;
514 : }
515 :
516 : private:
517 : ParserContextSharedPtr mpParserContext; // might get modified during parsing
518 : };
519 :
520 : #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
521 0 : const ParserContextSharedPtr& getParserContext()
522 : {
523 0 : static ParserContextSharedPtr lcl_parserContext( new ParserContext() );
524 :
525 : // clear node stack (since we reuse the static object, that's
526 : // the whole point here)
527 0 : while( !lcl_parserContext->maOperandStack.empty() )
528 0 : lcl_parserContext->maOperandStack.pop();
529 :
530 0 : return lcl_parserContext;
531 : }
532 : #endif
533 : }
534 :
535 0 : ExpressionNodeSharedPtr SmilFunctionParser::parseSmilValue( const OUString& rSmilValue,
536 : const ::basegfx::B2DRectangle& rRelativeShapeBounds )
537 : {
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 0 : OUStringToOString( rSmilValue, RTL_TEXTENCODING_ASCII_US ) );
543 :
544 0 : StringIteratorT aStart( rAsciiSmilValue.getStr() );
545 0 : StringIteratorT aEnd( rAsciiSmilValue.getStr()+rAsciiSmilValue.getLength() );
546 :
547 0 : ParserContextSharedPtr pContext;
548 :
549 : #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
550 : // static parser context, because the actual
551 : // Spirit parser is also a static object
552 0 : pContext = getParserContext();
553 : #else
554 : pContext.reset( new ParserContext() );
555 : #endif
556 :
557 0 : pContext->maShapeBounds = rRelativeShapeBounds;
558 0 : pContext->mbParseAnimationFunction = false; // parse with '$' disabled
559 :
560 :
561 0 : ExpressionGrammar aExpressionGrammer( pContext );
562 : const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
563 : ::boost::spirit::parse( aStart,
564 : aEnd,
565 : aExpressionGrammer,
566 0 : ::boost::spirit::space_p ) );
567 :
568 : #if OSL_DEBUG_LEVEL > 0
569 : ::std::cout.flush(); // needed to keep stdout and cout in sync
570 : #endif
571 :
572 : // input fully congested by the parser?
573 0 : if( !aParseInfo.full )
574 0 : throw ParseError( "SmilFunctionParser::parseSmilValue(): string not fully parseable" );
575 :
576 : // parser's state stack now must contain exactly _one_ ExpressionNode,
577 : // which represents our formula.
578 0 : if( pContext->maOperandStack.size() != 1 )
579 0 : throw ParseError( "SmilFunctionParser::parseSmilValue(): incomplete or empty expression" );
580 :
581 0 : return pContext->maOperandStack.top();
582 : }
583 :
584 0 : ExpressionNodeSharedPtr SmilFunctionParser::parseSmilFunction( const OUString& rSmilFunction,
585 : const ::basegfx::B2DRectangle& rRelativeShapeBounds )
586 : {
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 0 : OUStringToOString( rSmilFunction, RTL_TEXTENCODING_ASCII_US ) );
592 :
593 0 : StringIteratorT aStart( rAsciiSmilFunction.getStr() );
594 0 : StringIteratorT aEnd( rAsciiSmilFunction.getStr()+rAsciiSmilFunction.getLength() );
595 :
596 0 : ParserContextSharedPtr pContext;
597 :
598 : #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
599 : // static parser context, because the actual
600 : // Spirit parser is also a static object
601 0 : pContext = getParserContext();
602 : #else
603 : pContext.reset( new ParserContext() );
604 : #endif
605 :
606 0 : pContext->maShapeBounds = rRelativeShapeBounds;
607 0 : pContext->mbParseAnimationFunction = true; // parse with '$' enabled
608 :
609 :
610 0 : ExpressionGrammar aExpressionGrammer( pContext );
611 : const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
612 : ::boost::spirit::parse( aStart,
613 : aEnd,
614 0 : aExpressionGrammer >> ::boost::spirit::end_p,
615 0 : ::boost::spirit::space_p ) );
616 :
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 0 : if( !aParseInfo.full )
622 0 : throw ParseError( "SmilFunctionParser::parseSmilFunction(): string not fully parseable" );
623 :
624 : // parser's state stack now must contain exactly _one_ ExpressionNode,
625 : // which represents our formula.
626 0 : if( pContext->maOperandStack.size() != 1 )
627 0 : throw ParseError( "SmilFunctionParser::parseSmilFunction(): incomplete or empty expression" );
628 :
629 0 : return pContext->maOperandStack.top();
630 : }
631 : }
632 3 : }
633 :
634 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|