nss: upgrade to release 3.73
[LibreOffice.git] / slideshow / source / engine / smilfunctionparser.cxx
blob3576393751edce73a071ddd3f46c7184d60148ab
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 <slideshowexceptions.hxx>
24 #include <smilfunctionparser.hxx>
25 #include <expressionnodefactory.hxx>
27 #include <rtl/ustring.hxx>
28 #include <sal/log.hxx>
30 // Makes parser a static resource,
31 // we're synchronized externally.
32 // But watch out, the parser might have
33 // state not visible to this code!
34 #define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
36 #if defined(DBG_UTIL)
37 #define BOOST_SPIRIT_DEBUG
38 #endif
39 #include <boost/spirit/include/classic_core.hpp>
41 #include <iostream>
42 #include <functional>
43 #include <algorithm>
44 #include <stack>
47 /* Implementation of SmilFunctionParser class */
49 namespace slideshow::internal
51 namespace
53 typedef const char* StringIteratorT;
55 struct ParserContext
57 typedef ::std::stack< std::shared_ptr<ExpressionNode> > OperandStack;
59 // stores a stack of not-yet-evaluated operands. This is used
60 // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
61 // arguments from. If all arguments to an operator are constant,
62 // the operator pushes a precalculated result on the stack, and
63 // a composite ExpressionNode otherwise.
64 OperandStack maOperandStack;
66 // bounds of the shape this expression is associated with
67 ::basegfx::B2DRectangle maShapeBounds;
69 // when true, enable usage of time-dependent variable '$'
70 // in expressions
71 bool mbParseAnimationFunction;
74 typedef ::std::shared_ptr< ParserContext > ParserContextSharedPtr;
77 template< typename Generator > class ShapeBoundsFunctor
79 public:
80 ShapeBoundsFunctor( Generator aGenerator,
81 const ParserContextSharedPtr& rContext ) :
82 maGenerator( aGenerator ),
83 mpContext( rContext )
85 ENSURE_OR_THROW( mpContext,
86 "ShapeBoundsFunctor::ShapeBoundsFunctor(): Invalid context" );
89 void operator()( StringIteratorT, StringIteratorT ) const
91 mpContext->maOperandStack.push(
92 ExpressionNodeFactory::createConstantValueExpression(
93 maGenerator( mpContext->maShapeBounds ) ) );
96 private:
97 Generator maGenerator;
98 ParserContextSharedPtr mpContext;
101 template< typename Generator > ShapeBoundsFunctor< Generator >
102 makeShapeBoundsFunctor( const Generator& rGenerator,
103 const ParserContextSharedPtr& rContext )
105 return ShapeBoundsFunctor<Generator>(rGenerator, rContext);
108 /** Generate apriori constant value
110 class ConstantFunctor
112 public:
113 ConstantFunctor( double rValue,
114 const ParserContextSharedPtr& rContext ) :
115 mnValue( rValue ),
116 mpContext( rContext )
118 ENSURE_OR_THROW( mpContext,
119 "ConstantFunctor::ConstantFunctor(): Invalid context" );
122 void operator()( StringIteratorT, StringIteratorT ) const
124 mpContext->maOperandStack.push(
125 ExpressionNodeFactory::createConstantValueExpression( mnValue ) );
128 private:
129 const double mnValue;
130 ParserContextSharedPtr mpContext;
133 /** Generate parse-dependent-but-then-constant value
135 class DoubleConstantFunctor
137 public:
138 explicit DoubleConstantFunctor( const ParserContextSharedPtr& rContext ) :
139 mpContext( rContext )
141 ENSURE_OR_THROW( mpContext,
142 "DoubleConstantFunctor::DoubleConstantFunctor(): Invalid context" );
145 void operator()( double n ) const
147 // push constant value expression to the stack
148 mpContext->maOperandStack.push(
149 ExpressionNodeFactory::createConstantValueExpression( n ) );
152 private:
153 ParserContextSharedPtr mpContext;
156 /** Generate special t value expression node
158 class ValueTFunctor
160 public:
161 explicit ValueTFunctor( const ParserContextSharedPtr& rContext ) :
162 mpContext( rContext )
164 ENSURE_OR_THROW( mpContext,
165 "ValueTFunctor::ValueTFunctor(): Invalid context" );
168 void operator()( StringIteratorT, StringIteratorT ) const
170 if( !mpContext->mbParseAnimationFunction )
172 SAL_WARN("slideshow", "ValueTFunctor::operator(): variable encountered, but we're not parsing a function here" );
173 throw ParseError();
176 // push special t value expression to the stack
177 mpContext->maOperandStack.push(
178 ExpressionNodeFactory::createValueTExpression() );
181 private:
182 ParserContextSharedPtr mpContext;
185 template< typename Functor > class UnaryFunctionFunctor
187 private:
188 /** ExpressionNode implementation for unary
189 function over one ExpressionNode
191 class UnaryFunctionExpression : public ExpressionNode
193 public:
194 UnaryFunctionExpression( const Functor& rFunctor,
195 const std::shared_ptr<ExpressionNode>& rArg ) :
196 maFunctor( rFunctor ),
197 mpArg( rArg )
201 virtual double operator()( double t ) const override
203 return maFunctor( (*mpArg)(t) );
206 virtual bool isConstant() const override
208 return mpArg->isConstant();
211 private:
212 Functor maFunctor;
213 std::shared_ptr<ExpressionNode> mpArg;
216 public:
217 UnaryFunctionFunctor( const Functor& rFunctor,
218 const ParserContextSharedPtr& rContext ) :
219 maFunctor( rFunctor ),
220 mpContext( rContext )
222 ENSURE_OR_THROW( mpContext,
223 "UnaryFunctionFunctor::UnaryFunctionFunctor(): Invalid context" );
226 void operator()( StringIteratorT, StringIteratorT ) const
228 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
230 if( rNodeStack.empty() )
231 throw ParseError( "Not enough arguments for unary operator" );
233 // retrieve arguments
234 std::shared_ptr<ExpressionNode> pArg( std::move(rNodeStack.top()) );
235 rNodeStack.pop();
237 // check for constness
238 if( pArg->isConstant() )
240 rNodeStack.push(
241 ExpressionNodeFactory::createConstantValueExpression(
242 maFunctor( (*pArg)(0.0) ) ) );
244 else
246 // push complex node, that calcs the value on demand
247 rNodeStack.push(
248 std::make_shared<UnaryFunctionExpression>(
249 maFunctor,
250 pArg ) );
254 private:
255 Functor maFunctor;
256 ParserContextSharedPtr mpContext;
259 // TODO(Q2): Refactor makeUnaryFunctionFunctor,
260 // makeBinaryFunctionFunctor and the whole
261 // ExpressionNodeFactory, to use a generic
262 // makeFunctionFunctor template, which is overloaded for
263 // unary, binary, ternary, etc. function pointers.
264 template< typename Functor > UnaryFunctionFunctor<Functor>
265 makeUnaryFunctionFunctor( const Functor& rFunctor,
266 const ParserContextSharedPtr& rContext )
268 return UnaryFunctionFunctor<Functor>( rFunctor, rContext );
271 // MSVC has problems instantiating above template function with plain function
272 // pointers (doesn't like the const reference there). Thus, provide it with
273 // a dedicated overload here.
274 UnaryFunctionFunctor< double (*)(double) >
275 makeUnaryFunctionFunctor( double (*pFunc)(double),
276 const ParserContextSharedPtr& rContext )
278 return UnaryFunctionFunctor< double (*)(double) >( pFunc, rContext );
281 /** Implements a binary function over two ExpressionNodes
283 @tpl Generator
284 Generator functor, to generate an ExpressionNode of
285 appropriate type
288 template< class Generator > class BinaryFunctionFunctor
290 public:
291 BinaryFunctionFunctor( const Generator& rGenerator,
292 const ParserContextSharedPtr& rContext ) :
293 maGenerator( rGenerator ),
294 mpContext( rContext )
296 ENSURE_OR_THROW( mpContext,
297 "BinaryFunctionFunctor::BinaryFunctionFunctor(): Invalid context" );
300 void operator()( StringIteratorT, StringIteratorT ) const
302 ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
304 if( rNodeStack.size() < 2 )
305 throw ParseError( "Not enough arguments for binary operator" );
307 // retrieve arguments
308 std::shared_ptr<ExpressionNode> pSecondArg( std::move(rNodeStack.top()) );
309 rNodeStack.pop();
310 std::shared_ptr<ExpressionNode> pFirstArg( std::move(rNodeStack.top()) );
311 rNodeStack.pop();
313 // create combined ExpressionNode
314 std::shared_ptr<ExpressionNode> pNode( maGenerator( pFirstArg,
315 pSecondArg ) );
316 // check for constness
317 if( pFirstArg->isConstant() &&
318 pSecondArg->isConstant() )
320 // call the operator() at pNode, store result
321 // in constant value ExpressionNode.
322 rNodeStack.push(
323 ExpressionNodeFactory::createConstantValueExpression(
324 (*pNode)( 0.0 ) ) );
326 else
328 // push complex node, that calcs the value on demand
329 rNodeStack.push( pNode );
333 private:
334 Generator maGenerator;
335 ParserContextSharedPtr mpContext;
338 template< typename Generator > BinaryFunctionFunctor<Generator>
339 makeBinaryFunctionFunctor( const Generator& rGenerator,
340 const ParserContextSharedPtr& rContext )
342 return BinaryFunctionFunctor<Generator>( rGenerator, rContext );
346 // Workaround for MSVC compiler anomaly (stack trashing)
348 // The default ureal_parser_policies implementation of parse_exp
349 // triggers a really weird error in MSVC7 (Version 13.00.9466), in
350 // that the real_parser_impl::parse_main() call of parse_exp()
351 // overwrites the frame pointer _on the stack_ (EBP of the calling
352 // function gets overwritten while lying on the stack).
354 // For the time being, our parser thus can only read the 1.0E10
355 // notation, not the 1.0e10 one.
357 // TODO(F1): Also handle the 1.0e10 case here.
358 template< typename T > struct custom_real_parser_policies : public ::boost::spirit::classic::ureal_parser_policies<T>
360 template< typename ScannerT >
361 static typename ::boost::spirit::classic::parser_result< ::boost::spirit::classic::chlit<>, ScannerT >::type
362 parse_exp(ScannerT& scan)
364 // as_lower_d somehow breaks MSVC7
365 return ::boost::spirit::classic::ch_p('E').parse(scan);
369 /* This class implements the following grammar (more or
370 less literally written down below, only slightly
371 obfuscated by the parser actions):
373 identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
375 function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
377 basic_expression =
378 number |
379 identifier |
380 function '(' additive_expression ')' |
381 '(' additive_expression ')'
383 unary_expression =
384 '-' basic_expression |
385 basic_expression
387 multiplicative_expression =
388 unary_expression ( ( '*' unary_expression )* |
389 ( '/' unary_expression )* )
391 additive_expression =
392 multiplicative_expression ( ( '+' multiplicative_expression )* |
393 ( '-' multiplicative_expression )* )
396 class ExpressionGrammar : public ::boost::spirit::classic::grammar< ExpressionGrammar >
398 public:
399 /** Create an arithmetic expression grammar
401 @param rParserContext
402 Contains context info for the parser
404 explicit ExpressionGrammar( const ParserContextSharedPtr& rParserContext ) :
405 mpParserContext( rParserContext )
409 template< typename ScannerT > class definition
411 public:
412 // grammar definition
413 explicit definition( const ExpressionGrammar& self )
415 using ::boost::spirit::classic::str_p;
416 using ::boost::spirit::classic::real_parser;
418 identifier =
419 str_p( "$" )[ ValueTFunctor( self.getContext()) ]
420 | str_p( "pi" )[ ConstantFunctor(M_PI, self.getContext()) ]
421 | str_p( "e" )[ ConstantFunctor(M_E, self.getContext()) ]
422 | str_p( "x" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getCenterX),self.getContext()) ]
423 | str_p( "y" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getCenterY),self.getContext()) ]
424 | str_p( "width" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getWidth), self.getContext()) ]
425 | str_p( "height" )[ makeShapeBoundsFunctor(::std::mem_fn(&::basegfx::B2DRange::getHeight), self.getContext()) ]
428 unaryFunction =
429 (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&fabs, self.getContext()) ]
430 | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sqrt, self.getContext()) ]
431 | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sin, self.getContext()) ]
432 | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&cos, self.getContext()) ]
433 | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&tan, self.getContext()) ]
434 | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&atan, self.getContext()) ]
435 | (str_p( "acos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&acos, self.getContext()) ]
436 | (str_p( "asin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&asin, self.getContext()) ]
437 | (str_p( "exp" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&exp, self.getContext()) ]
438 | (str_p( "log" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&log, self.getContext()) ]
441 binaryFunction =
442 (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinExpression, self.getContext()) ]
443 | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMaxExpression, self.getContext()) ]
446 basicExpression =
447 real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
448 | identifier
449 | unaryFunction
450 | binaryFunction
451 | '(' >> additiveExpression >> ')'
454 unaryExpression =
455 ('-' >> basicExpression)[ makeUnaryFunctionFunctor(::std::negate<double>(), self.getContext()) ]
456 | basicExpression
459 multiplicativeExpression =
460 unaryExpression
461 >> *( ('*' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMultipliesExpression, self.getContext()) ]
462 | ('/' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createDividesExpression, self.getContext()) ]
466 additiveExpression =
467 multiplicativeExpression
468 >> *( ('+' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createPlusExpression, self.getContext()) ]
469 | ('-' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinusExpression, self.getContext()) ]
473 BOOST_SPIRIT_DEBUG_RULE(additiveExpression);
474 BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression);
475 BOOST_SPIRIT_DEBUG_RULE(unaryExpression);
476 BOOST_SPIRIT_DEBUG_RULE(basicExpression);
477 BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
478 BOOST_SPIRIT_DEBUG_RULE(binaryFunction);
479 BOOST_SPIRIT_DEBUG_RULE(identifier);
482 const ::boost::spirit::classic::rule< ScannerT >& start() const
484 return additiveExpression;
487 private:
488 // the constituents of the Spirit arithmetic expression grammar.
489 // For the sake of readability, without 'ma' prefix.
490 ::boost::spirit::classic::rule< ScannerT > additiveExpression;
491 ::boost::spirit::classic::rule< ScannerT > multiplicativeExpression;
492 ::boost::spirit::classic::rule< ScannerT > unaryExpression;
493 ::boost::spirit::classic::rule< ScannerT > basicExpression;
494 ::boost::spirit::classic::rule< ScannerT > unaryFunction;
495 ::boost::spirit::classic::rule< ScannerT > binaryFunction;
496 ::boost::spirit::classic::rule< ScannerT > identifier;
499 const ParserContextSharedPtr& getContext() const
501 return mpParserContext;
504 private:
505 ParserContextSharedPtr mpParserContext; // might get modified during parsing
508 const ParserContextSharedPtr& getParserContext()
510 static ParserContextSharedPtr lcl_parserContext = std::make_shared<ParserContext>();
512 // clear node stack (since we reuse the static object, that's
513 // the whole point here)
514 while( !lcl_parserContext->maOperandStack.empty() )
515 lcl_parserContext->maOperandStack.pop();
517 return lcl_parserContext;
521 std::shared_ptr<ExpressionNode> const & SmilFunctionParser::parseSmilValue( const OUString& rSmilValue,
522 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
524 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
525 // gives better conversion robustness here (we might want to map space
526 // etc. to ASCII space here)
527 const OString& rAsciiSmilValue(
528 OUStringToOString( rSmilValue, RTL_TEXTENCODING_ASCII_US ) );
530 StringIteratorT aStart( rAsciiSmilValue.getStr() );
531 StringIteratorT aEnd( rAsciiSmilValue.getStr()+rAsciiSmilValue.getLength() );
533 // static parser context, because the actual
534 // Spirit parser is also a static object
535 ParserContextSharedPtr pContext = getParserContext();
537 pContext->maShapeBounds = rRelativeShapeBounds;
538 pContext->mbParseAnimationFunction = false; // parse with '$' disabled
541 ExpressionGrammar aExpressionGrammer( pContext );
542 const ::boost::spirit::classic::parse_info<StringIteratorT> aParseInfo(
543 ::boost::spirit::classic::parse( aStart,
544 aEnd,
545 aExpressionGrammer,
546 ::boost::spirit::classic::space_p ) );
548 #if OSL_DEBUG_LEVEL > 0
549 ::std::cout.flush(); // needed to keep stdout and cout in sync
550 #endif
552 // input fully congested by the parser?
553 if( !aParseInfo.full )
554 throw ParseError( "SmilFunctionParser::parseSmilValue(): string not fully parseable" );
556 // parser's state stack now must contain exactly _one_ ExpressionNode,
557 // which represents our formula.
558 if( pContext->maOperandStack.size() != 1 )
559 throw ParseError( "SmilFunctionParser::parseSmilValue(): incomplete or empty expression" );
561 return pContext->maOperandStack.top();
564 std::shared_ptr<ExpressionNode> const & SmilFunctionParser::parseSmilFunction( const OUString& rSmilFunction,
565 const ::basegfx::B2DRectangle& rRelativeShapeBounds )
567 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
568 // gives better conversion robustness here (we might want to map space
569 // etc. to ASCII space here)
570 const OString& rAsciiSmilFunction(
571 OUStringToOString( rSmilFunction, RTL_TEXTENCODING_ASCII_US ) );
573 StringIteratorT aStart( rAsciiSmilFunction.getStr() );
574 StringIteratorT aEnd( rAsciiSmilFunction.getStr()+rAsciiSmilFunction.getLength() );
576 // static parser context, because the actual
577 // Spirit parser is also a static object
578 ParserContextSharedPtr pContext = getParserContext();
580 pContext->maShapeBounds = rRelativeShapeBounds;
581 pContext->mbParseAnimationFunction = true; // parse with '$' enabled
584 ExpressionGrammar aExpressionGrammer( pContext );
585 const ::boost::spirit::classic::parse_info<StringIteratorT> aParseInfo(
586 ::boost::spirit::classic::parse( aStart,
587 aEnd,
588 aExpressionGrammer >> ::boost::spirit::classic::end_p,
589 ::boost::spirit::classic::space_p ) );
591 #if OSL_DEBUG_LEVEL > 0
592 ::std::cout.flush(); // needed to keep stdout and cout in sync
593 #endif
594 // input fully congested by the parser?
595 if( !aParseInfo.full )
596 throw ParseError( "SmilFunctionParser::parseSmilFunction(): string not fully parseable" );
598 // parser's state stack now must contain exactly _one_ ExpressionNode,
599 // which represents our formula.
600 if( pContext->maOperandStack.size() != 1 )
601 throw ParseError( "SmilFunctionParser::parseSmilFunction(): incomplete or empty expression" );
603 return pContext->maOperandStack.top();
607 #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
609 // debug hooks
611 namespace boost
614 void sp_scalar_constructor_hook(void *)
618 void sp_scalar_destructor_hook(void *)
624 #endif
626 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */