1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
22 #include <canvas/debug.hxx>
23 #include <tools/diagnose_ex.h>
25 #include <rtl/math.hxx>
27 #include <smilfunctionparser.hxx>
28 #include <expressionnodefactory.hxx>
30 #include <rtl/ustring.hxx>
31 #include <canvas/verbosetrace.hxx>
33 #include <basegfx/matrix/b2dhommatrix.hxx>
34 #include <basegfx/point/b2dpoint.hxx>
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)
43 #define BOOST_SPIRIT_DEBUG
45 #include <boost/spirit/include/classic_core.hpp>
47 #if OSL_DEBUG_LEVEL > 0
56 /* Implementation of SmilFunctionParser class */
64 typedef const sal_Char
* StringIteratorT
;
68 typedef ::std::stack
< ExpressionNodeSharedPtr
> OperandStack
;
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
;
77 // bounds of the shape this expression is associated with
78 ::basegfx::B2DRectangle maShapeBounds
;
80 // when true, enable usage of time-dependent variable '$'
82 bool mbParseAnimationFunction
;
85 typedef ::boost::shared_ptr
< ParserContext
> ParserContextSharedPtr
;
88 template< typename Generator
> class ShapeBoundsFunctor
91 ShapeBoundsFunctor( Generator aGenerator
,
92 const ParserContextSharedPtr
& rContext
) :
93 maGenerator( aGenerator
),
96 ENSURE_OR_THROW( mpContext
,
97 "ShapeBoundsFunctor::ShapeBoundsFunctor(): Invalid context" );
100 void operator()( StringIteratorT
, StringIteratorT
) const
102 mpContext
->maOperandStack
.push(
103 ExpressionNodeFactory::createConstantValueExpression(
104 maGenerator( mpContext
->maShapeBounds
) ) );
108 Generator maGenerator
;
109 ParserContextSharedPtr mpContext
;
112 template< typename Generator
> ShapeBoundsFunctor
< Generator
>
113 makeShapeBoundsFunctor( const Generator
& rGenerator
,
114 const ParserContextSharedPtr
& rContext
)
116 return ShapeBoundsFunctor
<Generator
>(rGenerator
, rContext
);
119 /** Generate apriori constant value
121 class ConstantFunctor
124 ConstantFunctor( double rValue
,
125 const ParserContextSharedPtr
& rContext
) :
127 mpContext( rContext
)
129 ENSURE_OR_THROW( mpContext
,
130 "ConstantFunctor::ConstantFunctor(): Invalid context" );
133 void operator()( StringIteratorT
, StringIteratorT
) const
135 mpContext
->maOperandStack
.push(
136 ExpressionNodeFactory::createConstantValueExpression( mnValue
) );
140 const double mnValue
;
141 ParserContextSharedPtr mpContext
;
144 /** Generate parse-dependent-but-then-constant value
146 class DoubleConstantFunctor
149 DoubleConstantFunctor( const ParserContextSharedPtr
& rContext
) :
150 mpContext( rContext
)
152 ENSURE_OR_THROW( mpContext
,
153 "DoubleConstantFunctor::DoubleConstantFunctor(): Invalid context" );
156 void operator()( double n
) const
158 // push constant value expression to the stack
159 mpContext
->maOperandStack
.push(
160 ExpressionNodeFactory::createConstantValueExpression( n
) );
164 ParserContextSharedPtr mpContext
;
167 /** Generate special t value expression node
172 ValueTFunctor( const ParserContextSharedPtr
& rContext
) :
173 mpContext( rContext
)
175 ENSURE_OR_THROW( mpContext
,
176 "ValueTFunctor::ValueTFunctor(): Invalid context" );
179 void operator()( StringIteratorT
, StringIteratorT
) const
181 if( !mpContext
->mbParseAnimationFunction
)
183 OSL_FAIL( "ValueTFunctor::operator(): variable encountered, but we're not parsing a function here" );
187 // push special t value expression to the stack
188 mpContext
->maOperandStack
.push(
189 ExpressionNodeFactory::createValueTExpression() );
193 ParserContextSharedPtr mpContext
;
196 template< typename Functor
> class UnaryFunctionFunctor
199 /** ExpressionNode implementation for unary
200 function over one ExpressionNode
202 class UnaryFunctionExpression
: public ExpressionNode
205 UnaryFunctionExpression( const Functor
& rFunctor
,
206 const ExpressionNodeSharedPtr
& rArg
) :
207 maFunctor( rFunctor
),
212 virtual double operator()( double t
) const SAL_OVERRIDE
214 return maFunctor( (*mpArg
)(t
) );
217 virtual bool isConstant() const SAL_OVERRIDE
219 return mpArg
->isConstant();
224 ExpressionNodeSharedPtr mpArg
;
228 UnaryFunctionFunctor( const Functor
& rFunctor
,
229 const ParserContextSharedPtr
& rContext
) :
230 maFunctor( rFunctor
),
231 mpContext( rContext
)
233 ENSURE_OR_THROW( mpContext
,
234 "UnaryFunctionFunctor::UnaryFunctionFunctor(): Invalid context" );
237 void operator()( StringIteratorT
, StringIteratorT
) const
239 ParserContext::OperandStack
& rNodeStack( mpContext
->maOperandStack
);
241 if( rNodeStack
.size() < 1 )
242 throw ParseError( "Not enough arguments for unary operator" );
244 // retrieve arguments
245 ExpressionNodeSharedPtr
pArg( rNodeStack
.top() );
248 // check for constness
249 if( pArg
->isConstant() )
252 ExpressionNodeFactory::createConstantValueExpression(
253 maFunctor( (*pArg
)(0.0) ) ) );
257 // push complex node, that calcs the value on demand
259 ExpressionNodeSharedPtr(
260 new UnaryFunctionExpression(
268 ParserContextSharedPtr mpContext
;
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 makeUnaryFunctionFunctor( const Functor
& rFunctor
,
278 const ParserContextSharedPtr
& rContext
)
280 return UnaryFunctionFunctor
<Functor
>( rFunctor
, rContext
);
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 makeUnaryFunctionFunctor( double (*pFunc
)(double),
288 const ParserContextSharedPtr
& rContext
)
290 return UnaryFunctionFunctor
< double (*)(double) >( pFunc
, rContext
);
293 /** Implements a binary function over two ExpressionNodes
296 Generator functor, to generate an ExpressionNode of
300 template< class Generator
> class BinaryFunctionFunctor
303 BinaryFunctionFunctor( const Generator
& rGenerator
,
304 const ParserContextSharedPtr
& rContext
) :
305 maGenerator( rGenerator
),
306 mpContext( rContext
)
308 ENSURE_OR_THROW( mpContext
,
309 "BinaryFunctionFunctor::BinaryFunctionFunctor(): Invalid context" );
312 void operator()( StringIteratorT
, StringIteratorT
) const
314 ParserContext::OperandStack
& rNodeStack( mpContext
->maOperandStack
);
316 if( rNodeStack
.size() < 2 )
317 throw ParseError( "Not enough arguments for binary operator" );
319 // retrieve arguments
320 ExpressionNodeSharedPtr
pSecondArg( rNodeStack
.top() );
322 ExpressionNodeSharedPtr
pFirstArg( rNodeStack
.top() );
325 // create combined ExpressionNode
326 ExpressionNodeSharedPtr
pNode( maGenerator( pFirstArg
,
328 // check for constness
329 if( pFirstArg
->isConstant() &&
330 pSecondArg
->isConstant() )
332 // call the operator() at pNode, store result
333 // in constant value ExpressionNode.
335 ExpressionNodeFactory::createConstantValueExpression(
340 // push complex node, that calcs the value on demand
341 rNodeStack
.push( pNode
);
346 Generator maGenerator
;
347 ParserContextSharedPtr mpContext
;
350 template< typename Generator
> BinaryFunctionFunctor
<Generator
>
351 makeBinaryFunctionFunctor( const Generator
& rGenerator
,
352 const ParserContextSharedPtr
& rContext
)
354 return BinaryFunctionFunctor
<Generator
>( rGenerator
, rContext
);
358 // Workaround for MSVC compiler anomaly (stack trashing)
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).
366 // For the time being, our parser thus can only read the 1.0E10
367 // notation, not the 1.0e10 one.
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
>
372 template< typename ScannerT
>
373 static typename ::boost::spirit::parser_result
< ::boost::spirit::chlit
<>, ScannerT
>::type
374 parse_exp(ScannerT
& scan
)
376 // as_lower_d somehow breaks MSVC7
377 return ::boost::spirit::ch_p('E').parse(scan
);
381 /* This class implements the following grammar (more or
382 less literally written down below, only slightly
383 obfuscated by the parser actions):
385 identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
387 function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
392 function '(' additive_expression ')' |
393 '(' additive_expression ')'
396 '-' basic_expression |
399 multiplicative_expression =
400 unary_expression ( ( '*' unary_expression )* |
401 ( '/' unary_expression )* )
403 additive_expression =
404 multiplicative_expression ( ( '+' multiplicative_expression )* |
405 ( '-' multiplicative_expression )* )
408 class ExpressionGrammar
: public ::boost::spirit::grammar
< ExpressionGrammar
>
411 /** Create an arithmetic expression grammar
413 @param rParserContext
414 Contains context info for the parser
416 ExpressionGrammar( const ParserContextSharedPtr
& rParserContext
) :
417 mpParserContext( rParserContext
)
421 template< typename ScannerT
> class definition
424 // grammar definition
425 definition( const ExpressionGrammar
& self
)
427 using ::boost::spirit::str_p
;
428 using ::boost::spirit::real_parser
;
431 str_p( "$" )[ ValueTFunctor( self
.getContext()) ]
432 | str_p( "pi" )[ ConstantFunctor(M_PI
, self
.getContext()) ]
433 | str_p( "e" )[ ConstantFunctor(M_E
, self
.getContext()) ]
434 | str_p( "x" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterX
),self
.getContext()) ]
435 | str_p( "y" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterY
),self
.getContext()) ]
436 | str_p( "width" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getWidth
), self
.getContext()) ]
437 | str_p( "height" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getHeight
), self
.getContext()) ]
441 (str_p( "abs" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&fabs
, self
.getContext()) ]
442 | (str_p( "sqrt" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&sqrt
, self
.getContext()) ]
443 | (str_p( "sin" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&sin
, self
.getContext()) ]
444 | (str_p( "cos" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&cos
, self
.getContext()) ]
445 | (str_p( "tan" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&tan
, self
.getContext()) ]
446 | (str_p( "atan" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&atan
, self
.getContext()) ]
447 | (str_p( "acos" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&acos
, self
.getContext()) ]
448 | (str_p( "asin" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&asin
, self
.getContext()) ]
449 | (str_p( "exp" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&exp
, self
.getContext()) ]
450 | (str_p( "log" ) >> '(' >> additiveExpression
>> ')' )[ makeUnaryFunctionFunctor(&log
, self
.getContext()) ]
454 (str_p( "min" ) >> '(' >> additiveExpression
>> ',' >> additiveExpression
>> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinExpression
, self
.getContext()) ]
455 | (str_p( "max" ) >> '(' >> additiveExpression
>> ',' >> additiveExpression
>> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMaxExpression
, self
.getContext()) ]
459 real_parser
<double, custom_real_parser_policies
<double> >()[ DoubleConstantFunctor(self
.getContext()) ]
463 | '(' >> additiveExpression
>> ')'
467 ('-' >> basicExpression
)[ makeUnaryFunctionFunctor(::std::negate
<double>(), self
.getContext()) ]
471 multiplicativeExpression
=
473 >> *( ('*' >> unaryExpression
)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMultipliesExpression
, self
.getContext()) ]
474 | ('/' >> unaryExpression
)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createDividesExpression
, self
.getContext()) ]
479 multiplicativeExpression
480 >> *( ('+' >> multiplicativeExpression
)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createPlusExpression
, self
.getContext()) ]
481 | ('-' >> multiplicativeExpression
)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinusExpression
, self
.getContext()) ]
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
);
494 const ::boost::spirit::rule
< ScannerT
>& start() const
496 return additiveExpression
;
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
;
511 const ParserContextSharedPtr
& getContext() const
513 return mpParserContext
;
517 ParserContextSharedPtr mpParserContext
; // might get modified during parsing
520 #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
521 const ParserContextSharedPtr
& getParserContext()
523 static ParserContextSharedPtr
lcl_parserContext( new ParserContext() );
525 // clear node stack (since we reuse the static object, that's
526 // the whole point here)
527 while( !lcl_parserContext
->maOperandStack
.empty() )
528 lcl_parserContext
->maOperandStack
.pop();
530 return lcl_parserContext
;
535 ExpressionNodeSharedPtr
SmilFunctionParser::parseSmilValue( const OUString
& rSmilValue
,
536 const ::basegfx::B2DRectangle
& rRelativeShapeBounds
)
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 OUStringToOString( rSmilValue
, RTL_TEXTENCODING_ASCII_US
) );
544 StringIteratorT
aStart( rAsciiSmilValue
.getStr() );
545 StringIteratorT
aEnd( rAsciiSmilValue
.getStr()+rAsciiSmilValue
.getLength() );
547 ParserContextSharedPtr pContext
;
549 #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
550 // static parser context, because the actual
551 // Spirit parser is also a static object
552 pContext
= getParserContext();
554 pContext
.reset( new ParserContext() );
557 pContext
->maShapeBounds
= rRelativeShapeBounds
;
558 pContext
->mbParseAnimationFunction
= false; // parse with '$' disabled
561 ExpressionGrammar
aExpressionGrammer( pContext
);
562 const ::boost::spirit::parse_info
<StringIteratorT
> aParseInfo(
563 ::boost::spirit::parse( aStart
,
566 ::boost::spirit::space_p
) );
568 #if OSL_DEBUG_LEVEL > 0
569 ::std::cout
.flush(); // needed to keep stdout and cout in sync
572 // input fully congested by the parser?
573 if( !aParseInfo
.full
)
574 throw ParseError( "SmilFunctionParser::parseSmilValue(): string not fully parseable" );
576 // parser's state stack now must contain exactly _one_ ExpressionNode,
577 // which represents our formula.
578 if( pContext
->maOperandStack
.size() != 1 )
579 throw ParseError( "SmilFunctionParser::parseSmilValue(): incomplete or empty expression" );
581 return pContext
->maOperandStack
.top();
584 ExpressionNodeSharedPtr
SmilFunctionParser::parseSmilFunction( const OUString
& rSmilFunction
,
585 const ::basegfx::B2DRectangle
& rRelativeShapeBounds
)
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 OUStringToOString( rSmilFunction
, RTL_TEXTENCODING_ASCII_US
) );
593 StringIteratorT
aStart( rAsciiSmilFunction
.getStr() );
594 StringIteratorT
aEnd( rAsciiSmilFunction
.getStr()+rAsciiSmilFunction
.getLength() );
596 ParserContextSharedPtr pContext
;
598 #ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
599 // static parser context, because the actual
600 // Spirit parser is also a static object
601 pContext
= getParserContext();
603 pContext
.reset( new ParserContext() );
606 pContext
->maShapeBounds
= rRelativeShapeBounds
;
607 pContext
->mbParseAnimationFunction
= true; // parse with '$' enabled
610 ExpressionGrammar
aExpressionGrammer( pContext
);
611 const ::boost::spirit::parse_info
<StringIteratorT
> aParseInfo(
612 ::boost::spirit::parse( aStart
,
614 aExpressionGrammer
>> ::boost::spirit::end_p
,
615 ::boost::spirit::space_p
) );
617 #if OSL_DEBUG_LEVEL > 0
618 ::std::cout
.flush(); // needed to keep stdout and cout in sync
620 // input fully congested by the parser?
621 if( !aParseInfo
.full
)
622 throw ParseError( "SmilFunctionParser::parseSmilFunction(): string not fully parseable" );
624 // parser's state stack now must contain exactly _one_ ExpressionNode,
625 // which represents our formula.
626 if( pContext
->maOperandStack
.size() != 1 )
627 throw ParseError( "SmilFunctionParser::parseSmilFunction(): incomplete or empty expression" );
629 return pContext
->maOperandStack
.top();
634 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */