summaryrefslogtreecommitdiff
path: root/slideshow/source/engine/smilfunctionparser.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'slideshow/source/engine/smilfunctionparser.cxx')
-rw-r--r--slideshow/source/engine/smilfunctionparser.cxx638
1 files changed, 638 insertions, 0 deletions
diff --git a/slideshow/source/engine/smilfunctionparser.cxx b/slideshow/source/engine/smilfunctionparser.cxx
new file mode 100644
index 000000000000..aad23327719d
--- /dev/null
+++ b/slideshow/source/engine/smilfunctionparser.cxx
@@ -0,0 +1,638 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_slideshow.hxx"
+
+// must be first
+#include <canvas/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <rtl/math.hxx>
+
+#include <smilfunctionparser.hxx>
+#include <expressionnodefactory.hxx>
+
+#include <rtl/ustring.hxx>
+#include <canvas/verbosetrace.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+
+// Makes parser a static resource,
+// we're synchronized externally.
+// But watch out, the parser might have
+// state not visible to this code!
+#define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
+#if defined(VERBOSE) && defined(DBG_UTIL)
+#include <typeinfo>
+#define BOOST_SPIRIT_DEBUG
+#endif
+#include <boost/spirit/include/classic_core.hpp>
+
+#if OSL_DEBUG_LEVEL > 0
+#include <iostream>
+#endif
+#include <functional>
+#include <algorithm>
+#include <stack>
+
+
+
+/* Implementation of SmilFunctionParser class */
+
+namespace slideshow
+{
+ namespace internal
+ {
+ namespace
+ {
+ typedef const sal_Char* StringIteratorT;
+
+ struct ParserContext
+ {
+ typedef ::std::stack< ExpressionNodeSharedPtr > OperandStack;
+
+ // stores a stack of not-yet-evaluated operands. This is used
+ // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
+ // arguments from. If all arguments to an operator are constant,
+ // the operator pushes a precalculated result on the stack, and
+ // a composite ExpressionNode otherwise.
+ OperandStack maOperandStack;
+
+ // bounds of the shape this expression is associated with
+ ::basegfx::B2DRectangle maShapeBounds;
+
+ // when true, enable usage of time-dependent variable '$'
+ // in expressions
+ bool mbParseAnimationFunction;
+ };
+
+ typedef ::boost::shared_ptr< ParserContext > ParserContextSharedPtr;
+
+
+ template< typename Generator > class ShapeBoundsFunctor
+ {
+ public:
+ ShapeBoundsFunctor( Generator aGenerator,
+ const ParserContextSharedPtr& rContext ) :
+ maGenerator( aGenerator ),
+ mpContext( rContext )
+ {
+ ENSURE_OR_THROW( mpContext,
+ "ShapeBoundsFunctor::ShapeBoundsFunctor(): Invalid context" );
+ }
+
+ void operator()( StringIteratorT, StringIteratorT ) const
+ {
+ mpContext->maOperandStack.push(
+ ExpressionNodeFactory::createConstantValueExpression(
+ maGenerator( mpContext->maShapeBounds ) ) );
+ }
+
+ private:
+ Generator maGenerator;
+ ParserContextSharedPtr mpContext;
+ };
+
+ template< typename Generator > ShapeBoundsFunctor< Generator >
+ makeShapeBoundsFunctor( const Generator& rGenerator,
+ const ParserContextSharedPtr& rContext )
+ {
+ return ShapeBoundsFunctor<Generator>(rGenerator, rContext);
+ }
+
+ /** Generate apriori constant value
+ */
+ class ConstantFunctor
+ {
+ public:
+ ConstantFunctor( double rValue,
+ const ParserContextSharedPtr& rContext ) :
+ mnValue( rValue ),
+ mpContext( rContext )
+ {
+ ENSURE_OR_THROW( mpContext,
+ "ConstantFunctor::ConstantFunctor(): Invalid context" );
+ }
+
+ void operator()( StringIteratorT, StringIteratorT ) const
+ {
+ mpContext->maOperandStack.push(
+ ExpressionNodeFactory::createConstantValueExpression( mnValue ) );
+ }
+
+ private:
+ const double mnValue;
+ ParserContextSharedPtr mpContext;
+ };
+
+ /** Generate parse-dependent-but-then-constant value
+ */
+ class DoubleConstantFunctor
+ {
+ public:
+ DoubleConstantFunctor( const ParserContextSharedPtr& rContext ) :
+ mpContext( rContext )
+ {
+ ENSURE_OR_THROW( mpContext,
+ "DoubleConstantFunctor::DoubleConstantFunctor(): Invalid context" );
+ }
+
+ void operator()( double n ) const
+ {
+ // push constant value expression to the stack
+ mpContext->maOperandStack.push(
+ ExpressionNodeFactory::createConstantValueExpression( n ) );
+ }
+
+ private:
+ ParserContextSharedPtr mpContext;
+ };
+
+ /** Generate special t value expression node
+ */
+ class ValueTFunctor
+ {
+ public:
+ ValueTFunctor( const ParserContextSharedPtr& rContext ) :
+ mpContext( rContext )
+ {
+ ENSURE_OR_THROW( mpContext,
+ "ValueTFunctor::ValueTFunctor(): Invalid context" );
+ }
+
+ void operator()( StringIteratorT, StringIteratorT ) const
+ {
+ if( !mpContext->mbParseAnimationFunction )
+ {
+ OSL_ENSURE( false,
+ "ValueTFunctor::operator(): variable encountered, but we're not parsing a function here" );
+ throw ParseError();
+ }
+
+ // push special t value expression to the stack
+ mpContext->maOperandStack.push(
+ ExpressionNodeFactory::createValueTExpression() );
+ }
+
+ private:
+ ParserContextSharedPtr mpContext;
+ };
+
+ template< typename Functor > class UnaryFunctionFunctor
+ {
+ private:
+ /** ExpressionNode implementation for unary
+ function over one ExpressionNode
+ */
+ class UnaryFunctionExpression : public ExpressionNode
+ {
+ public:
+ UnaryFunctionExpression( const Functor& rFunctor,
+ const ExpressionNodeSharedPtr& rArg ) :
+ maFunctor( rFunctor ),
+ mpArg( rArg )
+ {
+ }
+
+ virtual double operator()( double t ) const
+ {
+ return maFunctor( (*mpArg)(t) );
+ }
+
+ virtual bool isConstant() const
+ {
+ return mpArg->isConstant();
+ }
+
+ private:
+ Functor maFunctor;
+ ExpressionNodeSharedPtr mpArg;
+ };
+
+ public:
+ UnaryFunctionFunctor( const Functor& rFunctor,
+ const ParserContextSharedPtr& rContext ) :
+ maFunctor( rFunctor ),
+ mpContext( rContext )
+ {
+ ENSURE_OR_THROW( mpContext,
+ "UnaryFunctionFunctor::UnaryFunctionFunctor(): Invalid context" );
+ }
+
+ void operator()( StringIteratorT, StringIteratorT ) const
+ {
+ ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
+
+ if( rNodeStack.size() < 1 )
+ throw ParseError( "Not enough arguments for unary operator" );
+
+ // retrieve arguments
+ ExpressionNodeSharedPtr pArg( rNodeStack.top() );
+ rNodeStack.pop();
+
+ // check for constness
+ if( pArg->isConstant() )
+ {
+ rNodeStack.push(
+ ExpressionNodeFactory::createConstantValueExpression(
+ maFunctor( (*pArg)(0.0) ) ) );
+ }
+ else
+ {
+ // push complex node, that calcs the value on demand
+ rNodeStack.push(
+ ExpressionNodeSharedPtr(
+ new UnaryFunctionExpression(
+ maFunctor,
+ pArg ) ) );
+ }
+ }
+
+ private:
+ Functor maFunctor;
+ ParserContextSharedPtr mpContext;
+ };
+
+ // TODO(Q2): Refactor makeUnaryFunctionFunctor,
+ // makeBinaryFunctionFunctor and the whole
+ // ExpressionNodeFactory, to use a generic
+ // makeFunctionFunctor template, which is overloaded for
+ // unary, binary, ternary, etc. function pointers.
+ template< typename Functor > UnaryFunctionFunctor<Functor>
+ makeUnaryFunctionFunctor( const Functor& rFunctor,
+ const ParserContextSharedPtr& rContext )
+ {
+ return UnaryFunctionFunctor<Functor>( rFunctor, rContext );
+ }
+
+ // MSVC has problems instantiating above template function with plain function
+ // pointers (doesn't like the const reference there). Thus, provide it with
+ // a dedicated overload here.
+ UnaryFunctionFunctor< double (*)(double) >
+ makeUnaryFunctionFunctor( double (*pFunc)(double),
+ const ParserContextSharedPtr& rContext )
+ {
+ return UnaryFunctionFunctor< double (*)(double) >( pFunc, rContext );
+ }
+
+ /** Implements a binary function over two ExpressionNodes
+
+ @tpl Generator
+ Generator functor, to generate an ExpressionNode of
+ appropriate type
+
+ */
+ template< class Generator > class BinaryFunctionFunctor
+ {
+ public:
+ BinaryFunctionFunctor( const Generator& rGenerator,
+ const ParserContextSharedPtr& rContext ) :
+ maGenerator( rGenerator ),
+ mpContext( rContext )
+ {
+ ENSURE_OR_THROW( mpContext,
+ "BinaryFunctionFunctor::BinaryFunctionFunctor(): Invalid context" );
+ }
+
+ void operator()( StringIteratorT, StringIteratorT ) const
+ {
+ ParserContext::OperandStack& rNodeStack( mpContext->maOperandStack );
+
+ if( rNodeStack.size() < 2 )
+ throw ParseError( "Not enough arguments for binary operator" );
+
+ // retrieve arguments
+ ExpressionNodeSharedPtr pSecondArg( rNodeStack.top() );
+ rNodeStack.pop();
+ ExpressionNodeSharedPtr pFirstArg( rNodeStack.top() );
+ rNodeStack.pop();
+
+ // create combined ExpressionNode
+ ExpressionNodeSharedPtr pNode( maGenerator( pFirstArg,
+ pSecondArg ) );
+ // check for constness
+ if( pFirstArg->isConstant() &&
+ pSecondArg->isConstant() )
+ {
+ // call the operator() at pNode, store result
+ // in constant value ExpressionNode.
+ rNodeStack.push(
+ ExpressionNodeFactory::createConstantValueExpression(
+ (*pNode)( 0.0 ) ) );
+ }
+ else
+ {
+ // push complex node, that calcs the value on demand
+ rNodeStack.push( pNode );
+ }
+ }
+
+ private:
+ Generator maGenerator;
+ ParserContextSharedPtr mpContext;
+ };
+
+ template< typename Generator > BinaryFunctionFunctor<Generator>
+ makeBinaryFunctionFunctor( const Generator& rGenerator,
+ const ParserContextSharedPtr& rContext )
+ {
+ return BinaryFunctionFunctor<Generator>( rGenerator, rContext );
+ }
+
+
+ // Workaround for MSVC compiler anomaly (stack trashing)
+ //
+ // The default ureal_parser_policies implementation of parse_exp
+ // triggers a really weird error in MSVC7 (Version 13.00.9466), in
+ // that the real_parser_impl::parse_main() call of parse_exp()
+ // overwrites the frame pointer _on the stack_ (EBP of the calling
+ // function gets overwritten while lying on the stack).
+ //
+ // For the time being, our parser thus can only read the 1.0E10
+ // notation, not the 1.0e10 one.
+ //
+ // TODO(F1): Also handle the 1.0e10 case here.
+ template< typename T > struct custom_real_parser_policies : public ::boost::spirit::ureal_parser_policies<T>
+ {
+ template< typename ScannerT >
+ static typename ::boost::spirit::parser_result< ::boost::spirit::chlit<>, ScannerT >::type
+ parse_exp(ScannerT& scan)
+ {
+ // as_lower_d somehow breaks MSVC7
+ return ::boost::spirit::ch_p('E').parse(scan);
+ }
+ };
+
+ /* This class implements the following grammar (more or
+ less literally written down below, only slightly
+ obfuscated by the parser actions):
+
+ identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
+
+ function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
+
+ basic_expression =
+ number |
+ identifier |
+ function '(' additive_expression ')' |
+ '(' additive_expression ')'
+
+ unary_expression =
+ '-' basic_expression |
+ basic_expression
+
+ multiplicative_expression =
+ unary_expression ( ( '*' unary_expression )* |
+ ( '/' unary_expression )* )
+
+ additive_expression =
+ multiplicative_expression ( ( '+' multiplicative_expression )* |
+ ( '-' multiplicative_expression )* )
+
+ */
+ class ExpressionGrammar : public ::boost::spirit::grammar< ExpressionGrammar >
+ {
+ public:
+ /** Create an arithmetic expression grammar
+
+ @param rParserContext
+ Contains context info for the parser
+ */
+ ExpressionGrammar( const ParserContextSharedPtr& rParserContext ) :
+ mpParserContext( rParserContext )
+ {
+ }
+
+ template< typename ScannerT > class definition
+ {
+ public:
+ // grammar definition
+ definition( const ExpressionGrammar& self )
+ {
+ using ::boost::spirit::str_p;
+ using ::boost::spirit::real_parser;
+
+ identifier =
+ str_p( "$" )[ ValueTFunctor( self.getContext()) ]
+ | str_p( "pi" )[ ConstantFunctor(M_PI, self.getContext()) ]
+ | str_p( "e" )[ ConstantFunctor(M_E, self.getContext()) ]
+ | str_p( "x" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterX),self.getContext()) ]
+ | str_p( "y" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getCenterY),self.getContext()) ]
+ | str_p( "width" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getWidth), self.getContext()) ]
+ | str_p( "height" )[ makeShapeBoundsFunctor(::std::mem_fun_ref(&::basegfx::B2DRange::getHeight), self.getContext()) ]
+ ;
+
+ unaryFunction =
+ (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&fabs, self.getContext()) ]
+ | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sqrt, self.getContext()) ]
+ | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&sin, self.getContext()) ]
+ | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&cos, self.getContext()) ]
+ | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&tan, self.getContext()) ]
+ | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&atan, self.getContext()) ]
+ | (str_p( "acos" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&acos, self.getContext()) ]
+ | (str_p( "asin" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&asin, self.getContext()) ]
+ | (str_p( "exp" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&exp, self.getContext()) ]
+ | (str_p( "log" ) >> '(' >> additiveExpression >> ')' )[ makeUnaryFunctionFunctor(&log, self.getContext()) ]
+ ;
+
+ binaryFunction =
+ (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinExpression, self.getContext()) ]
+ | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMaxExpression, self.getContext()) ]
+ ;
+
+ basicExpression =
+ real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
+ | identifier
+ | unaryFunction
+ | binaryFunction
+ | '(' >> additiveExpression >> ')'
+ ;
+
+ unaryExpression =
+ ('-' >> basicExpression)[ makeUnaryFunctionFunctor(::std::negate<double>(), self.getContext()) ]
+ | basicExpression
+ ;
+
+ multiplicativeExpression =
+ unaryExpression
+ >> *( ('*' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMultipliesExpression, self.getContext()) ]
+ | ('/' >> unaryExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createDividesExpression, self.getContext()) ]
+ )
+ ;
+
+ additiveExpression =
+ multiplicativeExpression
+ >> *( ('+' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createPlusExpression, self.getContext()) ]
+ | ('-' >> multiplicativeExpression)[ makeBinaryFunctionFunctor(&ExpressionNodeFactory::createMinusExpression, self.getContext()) ]
+ )
+ ;
+
+ BOOST_SPIRIT_DEBUG_RULE(additiveExpression);
+ BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression);
+ BOOST_SPIRIT_DEBUG_RULE(unaryExpression);
+ BOOST_SPIRIT_DEBUG_RULE(basicExpression);
+ BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
+ BOOST_SPIRIT_DEBUG_RULE(binaryFunction);
+ BOOST_SPIRIT_DEBUG_RULE(identifier);
+ }
+
+ const ::boost::spirit::rule< ScannerT >& start() const
+ {
+ return additiveExpression;
+ }
+
+ private:
+ // the constituents of the Spirit arithmetic expression grammar.
+ // For the sake of readability, without 'ma' prefix.
+ ::boost::spirit::rule< ScannerT > additiveExpression;
+ ::boost::spirit::rule< ScannerT > multiplicativeExpression;
+ ::boost::spirit::rule< ScannerT > unaryExpression;
+ ::boost::spirit::rule< ScannerT > basicExpression;
+ ::boost::spirit::rule< ScannerT > unaryFunction;
+ ::boost::spirit::rule< ScannerT > binaryFunction;
+ ::boost::spirit::rule< ScannerT > identifier;
+ };
+
+ const ParserContextSharedPtr& getContext() const
+ {
+ return mpParserContext;
+ }
+
+ private:
+ ParserContextSharedPtr mpParserContext; // might get modified during parsing
+ };
+
+#ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
+ const ParserContextSharedPtr& getParserContext()
+ {
+ static ParserContextSharedPtr lcl_parserContext( new ParserContext() );
+
+ // clear node stack (since we reuse the static object, that's
+ // the whole point here)
+ while( !lcl_parserContext->maOperandStack.empty() )
+ lcl_parserContext->maOperandStack.pop();
+
+ return lcl_parserContext;
+ }
+#endif
+ }
+
+ ExpressionNodeSharedPtr SmilFunctionParser::parseSmilValue( const ::rtl::OUString& rSmilValue,
+ const ::basegfx::B2DRectangle& rRelativeShapeBounds )
+ {
+ // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
+ // gives better conversion robustness here (we might want to map space
+ // etc. to ASCII space here)
+ const ::rtl::OString& rAsciiSmilValue(
+ rtl::OUStringToOString( rSmilValue, RTL_TEXTENCODING_ASCII_US ) );
+
+ StringIteratorT aStart( rAsciiSmilValue.getStr() );
+ StringIteratorT aEnd( rAsciiSmilValue.getStr()+rAsciiSmilValue.getLength() );
+
+ ParserContextSharedPtr pContext;
+
+#ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
+ // static parser context, because the actual
+ // Spirit parser is also a static object
+ pContext = getParserContext();
+#else
+ pContext.reset( new ParserContext() );
+#endif
+
+ pContext->maShapeBounds = rRelativeShapeBounds;
+ pContext->mbParseAnimationFunction = false; // parse with '$' disabled
+
+
+ ExpressionGrammar aExpressionGrammer( pContext );
+ const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
+ ::boost::spirit::parse( aStart,
+ aEnd,
+ aExpressionGrammer,
+ ::boost::spirit::space_p ) );
+ OSL_DEBUG_ONLY(::std::cout.flush()); // needed to keep stdout and cout in sync
+
+ // input fully congested by the parser?
+ if( !aParseInfo.full )
+ throw ParseError( "SmilFunctionParser::parseSmilValue(): string not fully parseable" );
+
+ // parser's state stack now must contain exactly _one_ ExpressionNode,
+ // which represents our formula.
+ if( pContext->maOperandStack.size() != 1 )
+ throw ParseError( "SmilFunctionParser::parseSmilValue(): incomplete or empty expression" );
+
+ return pContext->maOperandStack.top();
+ }
+
+ ExpressionNodeSharedPtr SmilFunctionParser::parseSmilFunction( const ::rtl::OUString& rSmilFunction,
+ const ::basegfx::B2DRectangle& rRelativeShapeBounds )
+ {
+ // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
+ // gives better conversion robustness here (we might want to map space
+ // etc. to ASCII space here)
+ const ::rtl::OString& rAsciiSmilFunction(
+ rtl::OUStringToOString( rSmilFunction, RTL_TEXTENCODING_ASCII_US ) );
+
+ StringIteratorT aStart( rAsciiSmilFunction.getStr() );
+ StringIteratorT aEnd( rAsciiSmilFunction.getStr()+rAsciiSmilFunction.getLength() );
+
+ ParserContextSharedPtr pContext;
+
+#ifdef BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
+ // static parser context, because the actual
+ // Spirit parser is also a static object
+ pContext = getParserContext();
+#else
+ pContext.reset( new ParserContext() );
+#endif
+
+ pContext->maShapeBounds = rRelativeShapeBounds;
+ pContext->mbParseAnimationFunction = true; // parse with '$' enabled
+
+
+ ExpressionGrammar aExpressionGrammer( pContext );
+ const ::boost::spirit::parse_info<StringIteratorT> aParseInfo(
+ ::boost::spirit::parse( aStart,
+ aEnd,
+ aExpressionGrammer >> ::boost::spirit::end_p,
+ ::boost::spirit::space_p ) );
+ OSL_DEBUG_ONLY(::std::cout.flush()); // needed to keep stdout and cout in sync
+
+ // input fully congested by the parser?
+ if( !aParseInfo.full )
+ throw ParseError( "SmilFunctionParser::parseSmilFunction(): string not fully parseable" );
+
+ // parser's state stack now must contain exactly _one_ ExpressionNode,
+ // which represents our formula.
+ if( pContext->maOperandStack.size() != 1 )
+ throw ParseError( "SmilFunctionParser::parseSmilFunction(): incomplete or empty expression" );
+
+ return pContext->maOperandStack.top();
+ }
+ }
+}