summaryrefslogtreecommitdiff
path: root/connectivity/source/commontools/RowFunctionParser.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'connectivity/source/commontools/RowFunctionParser.cxx')
-rw-r--r--connectivity/source/commontools/RowFunctionParser.cxx499
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();
+}
+}
+