diff options
Diffstat (limited to 'connectivity/source/commontools/RowFunctionParser.cxx')
-rw-r--r-- | connectivity/source/commontools/RowFunctionParser.cxx | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/connectivity/source/commontools/RowFunctionParser.cxx b/connectivity/source/commontools/RowFunctionParser.cxx new file mode 100644 index 000000000000..3804ddfb259f --- /dev/null +++ b/connectivity/source/commontools/RowFunctionParser.cxx @@ -0,0 +1,499 @@ +/************************************************************************* + * + * 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_connectivity.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> +#include "RowFunctionParser.hxx" +#include <rtl/ustring.hxx> +#include <tools/fract.hxx> + + + +#if (OSL_DEBUG_LEVEL > 0) +#include <iostream> +#endif +#include <functional> +#include <algorithm> +#include <stack> + +namespace connectivity +{ +using namespace com::sun::star; + +namespace +{ +////////////////////// +////////////////////// +// EXPRESSION NODES +////////////////////// +////////////////////// +class ConstantValueExpression : public ExpressionNode +{ + ORowSetValueDecoratorRef maValue; + +public: + + ConstantValueExpression( ORowSetValueDecoratorRef rValue ) : + maValue( rValue ) + { + } + virtual ORowSetValueDecoratorRef evaluate(const ODatabaseMetaDataResultSet::ORow& /*_aRow*/ ) const + { + return maValue; + } + virtual void fill(const ODatabaseMetaDataResultSet::ORow& /*_aRow*/ ) const + { + } + virtual ExpressionFunct getType() const + { + return FUNC_CONST; + } + virtual ODatabaseMetaDataResultSet::ORow fillNode( std::vector< RowEquation >& /*rEquations*/, ExpressionNode* /* pOptionalArg */, sal_uInt32 /* nFlags */ ) + { + ODatabaseMetaDataResultSet::ORow aRet; + return aRet; + } +}; + + +/** ExpressionNode implementation for unary + function over two ExpressionNodes + */ +class BinaryFunctionExpression : public ExpressionNode +{ + const ExpressionFunct meFunct; + ExpressionNodeSharedPtr mpFirstArg; + ExpressionNodeSharedPtr mpSecondArg; + +public: + + BinaryFunctionExpression( const ExpressionFunct eFunct, const ExpressionNodeSharedPtr& rFirstArg, const ExpressionNodeSharedPtr& rSecondArg ) : + meFunct( eFunct ), + mpFirstArg( rFirstArg ), + mpSecondArg( rSecondArg ) + { + } + virtual ORowSetValueDecoratorRef evaluate(const ODatabaseMetaDataResultSet::ORow& _aRow ) const + { + ORowSetValueDecoratorRef aRet; + switch(meFunct) + { + case ENUM_FUNC_EQUATION: + aRet = new ORowSetValueDecorator(sal_Bool(mpFirstArg->evaluate(_aRow )->getValue() == mpSecondArg->evaluate(_aRow )->getValue()) ); + break; + case ENUM_FUNC_AND: + aRet = new ORowSetValueDecorator( sal_Bool(mpFirstArg->evaluate(_aRow )->getValue().getBool() && mpSecondArg->evaluate(_aRow )->getValue().getBool()) ); + break; + case ENUM_FUNC_OR: + aRet = new ORowSetValueDecorator( sal_Bool(mpFirstArg->evaluate(_aRow )->getValue().getBool() || mpSecondArg->evaluate(_aRow )->getValue().getBool()) ); + break; + default: + break; + } + return aRet; + } + virtual void fill(const ODatabaseMetaDataResultSet::ORow& _aRow ) const + { + switch(meFunct) + { + case ENUM_FUNC_EQUATION: + (*mpFirstArg->evaluate(_aRow )) = mpSecondArg->evaluate(_aRow )->getValue(); + break; + default: + break; + } + } + virtual ExpressionFunct getType() const + { + return meFunct; + } + virtual ODatabaseMetaDataResultSet::ORow fillNode( std::vector< RowEquation >& /*rEquations*/, ExpressionNode* /*pOptionalArg*/, sal_uInt32 /*nFlags*/ ) + { + ODatabaseMetaDataResultSet::ORow aRet; + return aRet; + } +}; + + +//////////////////////// +//////////////////////// +// FUNCTION PARSER +//////////////////////// +//////////////////////// + +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; +}; + +typedef ::boost::shared_ptr< ParserContext > ParserContextSharedPtr; + +/** Generate apriori constant value + */ + +class ConstantFunctor +{ + ParserContextSharedPtr mpContext; + +public: + + ConstantFunctor( const ParserContextSharedPtr& rContext ) : + mpContext( rContext ) + { + } + void operator()( StringIteratorT rFirst,StringIteratorT rSecond) const + { + rtl::OUString sVal( rFirst, rSecond - rFirst, RTL_TEXTENCODING_UTF8 ); + mpContext->maOperandStack.push( ExpressionNodeSharedPtr( new ConstantValueExpression( new ORowSetValueDecorator( sVal ) ) ) ); + } +}; + +/** Generate parse-dependent-but-then-constant value + */ +class IntConstantFunctor +{ + ParserContextSharedPtr mpContext; + +public: + IntConstantFunctor( const ParserContextSharedPtr& rContext ) : + mpContext( rContext ) + { + } + void operator()( sal_Int32 n ) const + { + mpContext->maOperandStack.push( ExpressionNodeSharedPtr( new ConstantValueExpression( new ORowSetValueDecorator( n ) ) ) ); + } + void operator()( StringIteratorT rFirst,StringIteratorT rSecond) const + { + rtl::OUString sVal( rFirst, rSecond - rFirst, RTL_TEXTENCODING_UTF8 ); + (void)sVal; + } +}; + +/** Implements a binary function over two ExpressionNodes + + @tpl Generator + Generator functor, to generate an ExpressionNode of + appropriate type + + */ +class BinaryFunctionFunctor +{ + const ExpressionFunct meFunct; + ParserContextSharedPtr mpContext; + +public: + + BinaryFunctionFunctor( const ExpressionFunct eFunct, const ParserContextSharedPtr& rContext ) : + meFunct( eFunct ), + mpContext( rContext ) + { + } + + 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 = ExpressionNodeSharedPtr( new BinaryFunctionExpression( meFunct, pFirstArg, pSecondArg ) ); + // check for constness + rNodeStack.push( pNode ); + } +}; +/** ExpressionNode implementation for unary + function over one ExpressionNode + */ +class UnaryFunctionExpression : public ExpressionNode +{ + const ExpressionFunct meFunct; + ExpressionNodeSharedPtr mpArg; + +public: + UnaryFunctionExpression( const ExpressionFunct eFunct, const ExpressionNodeSharedPtr& rArg ) : + meFunct( eFunct ), + mpArg( rArg ) + { + } + virtual ORowSetValueDecoratorRef evaluate(const ODatabaseMetaDataResultSet::ORow& _aRow ) const + { + return _aRow[mpArg->evaluate(_aRow )->getValue().getInt32()]; + } + virtual void fill(const ODatabaseMetaDataResultSet::ORow& /*_aRow*/ ) const + { + } + virtual ExpressionFunct getType() const + { + return meFunct; + } + virtual ODatabaseMetaDataResultSet::ORow fillNode( std::vector< RowEquation >& /*rEquations*/, ExpressionNode* /* pOptionalArg */, sal_uInt32 /* nFlags */ ) + { + ODatabaseMetaDataResultSet::ORow aRet; + return aRet; + } +}; + +class UnaryFunctionFunctor +{ + const ExpressionFunct meFunct; + ParserContextSharedPtr mpContext; + +public : + + UnaryFunctionFunctor( const ExpressionFunct eFunct, const ParserContextSharedPtr& rContext ) : + meFunct( eFunct ), + mpContext( rContext ) + { + } + 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(); + + rNodeStack.push( ExpressionNodeSharedPtr( new UnaryFunctionExpression( meFunct, pArg ) ) ); + } +}; + +/* This class implements the following grammar (more or + less literally written down below, only slightly + obfuscated by the parser actions): + + basic_expression = + number | + '(' additive_expression ')' + + unary_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::space_p; + using ::boost::spirit::range_p; + using ::boost::spirit::lexeme_d; + using ::boost::spirit::real_parser; + using ::boost::spirit::chseq_p; + using ::boost::spirit::ch_p; + using ::boost::spirit::int_p; + using ::boost::spirit::as_lower_d; + using ::boost::spirit::strlit; + using ::boost::spirit::inhibit_case; + + + typedef inhibit_case<strlit<> > token_t; + token_t COLUMN = as_lower_d[ "column" ]; + token_t OR_ = as_lower_d[ "or" ]; + token_t AND_ = as_lower_d[ "and" ]; + + integer = + int_p + [IntConstantFunctor(self.getContext())]; + + argument = + integer + | lexeme_d[ +( range_p('a','z') | range_p('A','Z') | range_p('0','9') ) ] + [ ConstantFunctor(self.getContext()) ] + ; + + unaryFunction = + (COLUMN >> '(' >> integer >> ')' ) + [ UnaryFunctionFunctor( UNARY_FUNC_COLUMN, self.getContext()) ] + ; + + assignment = + unaryFunction >> ch_p('=') >> argument + [ BinaryFunctionFunctor( ENUM_FUNC_EQUATION, self.getContext()) ] + ; + + andExpression = + assignment + | ( '(' >> orExpression >> ')' ) + | ( assignment >> AND_ >> assignment ) [ BinaryFunctionFunctor( ENUM_FUNC_AND, self.getContext()) ] + ; + + orExpression = + andExpression + | ( orExpression >> OR_ >> andExpression ) [ BinaryFunctionFunctor( ENUM_FUNC_OR, self.getContext()) ] + ; + + basicExpression = + orExpression + ; + + BOOST_SPIRIT_DEBUG_RULE(basicExpression); + BOOST_SPIRIT_DEBUG_RULE(unaryFunction); + BOOST_SPIRIT_DEBUG_RULE(assignment); + BOOST_SPIRIT_DEBUG_RULE(argument); + BOOST_SPIRIT_DEBUG_RULE(integer); + BOOST_SPIRIT_DEBUG_RULE(orExpression); + BOOST_SPIRIT_DEBUG_RULE(andExpression); + } + + const ::boost::spirit::rule< ScannerT >& start() const + { + return basicExpression; + } + + private: + // the constituents of the Spirit arithmetic expression grammar. + // For the sake of readability, without 'ma' prefix. + ::boost::spirit::rule< ScannerT > basicExpression; + ::boost::spirit::rule< ScannerT > unaryFunction; + ::boost::spirit::rule< ScannerT > assignment; + ::boost::spirit::rule< ScannerT > integer,argument; + ::boost::spirit::rule< ScannerT > orExpression,andExpression; + }; + + 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 FunctionParser::parseFunction( const ::rtl::OUString& _sFunction) +{ + // 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& rAsciiFunction( + rtl::OUStringToOString( _sFunction, RTL_TEXTENCODING_ASCII_US ) ); + + StringIteratorT aStart( rAsciiFunction.getStr() ); + StringIteratorT aEnd( rAsciiFunction.getStr()+rAsciiFunction.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 + + 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( "RowFunctionParser::parseFunction(): 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( "RowFunctionParser::parseFunction(): incomplete or empty expression" ); + + return pContext->maOperandStack.top(); +} +} + |