/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include // 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 OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL) #define BOOST_SPIRIT_DEBUG #endif #include #include #include #include #include #include using namespace EnhancedCustomShape; using namespace com::sun::star; using namespace com::sun::star::drawing; void EnhancedCustomShape::FillEquationParameter( const EnhancedCustomShapeParameter& rSource, const sal_Int32 nDestPara, EnhancedCustomShapeEquation& rDest ) { sal_Int32 nValue = 0; if ( rSource.Value.getValueTypeClass() == uno::TypeClass_DOUBLE ) { double fValue(0.0); if ( rSource.Value >>= fValue ) nValue = static_cast(fValue); } else rSource.Value >>= nValue; switch( rSource.Type ) { case css::drawing::EnhancedCustomShapeParameterType::EQUATION : { if ( nValue & 0x40000000 ) { nValue ^= 0x40000000; rDest.nOperation |= 0x20000000 << nDestPara; // the bit is indicating that this value has to be adjusted later } nValue |= 0x400; } break; case css::drawing::EnhancedCustomShapeParameterType::ADJUSTMENT : nValue += DFF_Prop_adjustValue; break; case css::drawing::EnhancedCustomShapeParameterType::BOTTOM : nValue = DFF_Prop_geoBottom; break; case css::drawing::EnhancedCustomShapeParameterType::RIGHT : nValue = DFF_Prop_geoRight; break; case css::drawing::EnhancedCustomShapeParameterType::TOP : nValue = DFF_Prop_geoTop; break; case css::drawing::EnhancedCustomShapeParameterType::LEFT : nValue = DFF_Prop_geoLeft; break; } if ( rSource.Type != css::drawing::EnhancedCustomShapeParameterType::NORMAL ) rDest.nOperation |= ( 0x2000 << nDestPara ); rDest.nPara[ nDestPara ] = nValue; } ExpressionNode::~ExpressionNode() {} namespace { // EXPRESSION NODES class ConstantValueExpression : public ExpressionNode { double maValue; public: explicit ConstantValueExpression( double rValue ) : maValue( rValue ) { } virtual double operator()() const override { return maValue; } virtual bool isConstant() const override { return true; } virtual ExpressionFunct getType() const override { return ExpressionFunct::Const; } virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /* pOptionalArg */, sal_uInt32 /* nFlags */ ) override { EnhancedCustomShapeParameter aRet; Fraction aFract( maValue ); if ( aFract.GetDenominator() == 1 ) { aRet.Type = EnhancedCustomShapeParameterType::NORMAL; aRet.Value <<= aFract.GetNumerator(); } else { EnhancedCustomShapeEquation aEquation; aEquation.nOperation = 1; aEquation.nPara[ 0 ] = 1; aEquation.nPara[ 1 ] = static_cast(aFract.GetNumerator()); aEquation.nPara[ 2 ] = static_cast(aFract.GetDenominator()); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } return aRet; } }; class AdjustmentExpression : public ExpressionNode { sal_Int32 mnIndex; const EnhancedCustomShape2d& mrCustoShape; public: AdjustmentExpression( const EnhancedCustomShape2d& rCustoShape, sal_Int32 nIndex ) : mnIndex ( nIndex ) , mrCustoShape( rCustoShape ) { } virtual double operator()() const override { SAL_INFO( "svx", "$" << mnIndex << " --> " << mrCustoShape.GetAdjustValueAsDouble(mnIndex) << " (angle: " << 180.0*mrCustoShape.GetAdjustValueAsDouble(mnIndex)/10800000.0 << ")"); return mrCustoShape.GetAdjustValueAsDouble( mnIndex ); } virtual bool isConstant() const override { return false; } virtual ExpressionFunct getType() const override { return ExpressionFunct::EnumAdjustment; } virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& /*rEquations*/, ExpressionNode* /*pOptionalArg*/, sal_uInt32 /*nFlags*/ ) override { EnhancedCustomShapeParameter aRet; aRet.Type = EnhancedCustomShapeParameterType::ADJUSTMENT; aRet.Value <<= mnIndex; return aRet; } }; class EquationExpression : public ExpressionNode { const sal_Int32 mnIndex; const EnhancedCustomShape2d& mrCustoShape; mutable bool mbGettingValueGuard; public: EquationExpression( const EnhancedCustomShape2d& rCustoShape, sal_Int32 nIndex ) : mnIndex ( nIndex ) , mrCustoShape( rCustoShape ) , mbGettingValueGuard(false) { } virtual double operator()() const override { if (mbGettingValueGuard) throw ParseError("Loop in Expression"); mbGettingValueGuard = true; double fRet = mrCustoShape.GetEquationValueAsDouble(mnIndex); mbGettingValueGuard = false; return fRet; } virtual bool isConstant() const override { return false; } virtual ExpressionFunct getType() const override { return ExpressionFunct::EnumEquation; } virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& /*rEquations*/, ExpressionNode* /*pOptionalArg*/, sal_uInt32 /*nFlags*/ ) override { EnhancedCustomShapeParameter aRet; aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= mnIndex | 0x40000000; // the bit is indicating that this equation needs to be adjusted later return aRet; } }; class EnumValueExpression : public ExpressionNode { const ExpressionFunct meFunct; const EnhancedCustomShape2d& mrCustoShape; public: EnumValueExpression( const EnhancedCustomShape2d& rCustoShape, const ExpressionFunct eFunct ) : meFunct ( eFunct ) , mrCustoShape ( rCustoShape ) { } virtual double operator()() const override { SAL_INFO("svx", meFunct << " --> " << mrCustoShape.GetEnumFunc(meFunct) << "(angle: " << 180.0 * mrCustoShape.GetEnumFunc(meFunct) / 10800000.0 << ")"); return mrCustoShape.GetEnumFunc( meFunct ); } virtual bool isConstant() const override { return false; } virtual ExpressionFunct getType() const override { return meFunct; } virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /*pOptionalArg*/, sal_uInt32 nFlags ) override { EnhancedCustomShapeParameter aRet; aRet.Value <<= sal_Int32(1); switch( meFunct ) { case ExpressionFunct::EnumWidth : // TODO: do not use this as constant value case ExpressionFunct::EnumHeight : case ExpressionFunct::EnumLogWidth : case ExpressionFunct::EnumLogHeight : case ExpressionFunct::EnumPi : { ConstantValueExpression aConstantValue( mrCustoShape.GetEnumFunc( meFunct ) ); aRet = aConstantValue.fillNode( rEquations, nullptr, nFlags ); } break; case ExpressionFunct::EnumLeft : aRet.Type = EnhancedCustomShapeParameterType::LEFT; break; case ExpressionFunct::EnumTop : aRet.Type = EnhancedCustomShapeParameterType::TOP; break; case ExpressionFunct::EnumRight : aRet.Type = EnhancedCustomShapeParameterType::RIGHT; break; case ExpressionFunct::EnumBottom : aRet.Type = EnhancedCustomShapeParameterType::BOTTOM; break; // not implemented so far case ExpressionFunct::EnumXStretch : case ExpressionFunct::EnumYStretch : case ExpressionFunct::EnumHasStroke : case ExpressionFunct::EnumHasFill : aRet.Type = EnhancedCustomShapeParameterType::NORMAL; break; default: break; } return aRet; } }; /** ExpressionNode implementation for unary function over one ExpressionNode */ class UnaryFunctionExpression : public ExpressionNode { const ExpressionFunct meFunct; std::shared_ptr mpArg; public: UnaryFunctionExpression( const ExpressionFunct eFunct, std::shared_ptr aArg ) : meFunct( eFunct ), mpArg(std::move( aArg )) { } static double getValue( const ExpressionFunct eFunct, const std::shared_ptr& rArg ) { double fRet = 0; switch( eFunct ) { case ExpressionFunct::UnaryAbs : fRet = fabs( (*rArg)() ); break; case ExpressionFunct::UnarySqrt: fRet = sqrt( (*rArg)() ); break; case ExpressionFunct::UnarySin : fRet = sin( (*rArg)() ); break; case ExpressionFunct::UnaryCos : fRet = cos( (*rArg)() ); break; case ExpressionFunct::UnaryTan : fRet = tan( (*rArg)() ); break; case ExpressionFunct::UnaryAtan: fRet = atan( (*rArg)() ); break; case ExpressionFunct::UnaryNeg : fRet = ::std::negate()( (*rArg)() ); break; default: break; } return fRet; } virtual double operator()() const override { return getValue( meFunct, mpArg ); } virtual bool isConstant() const override { return mpArg->isConstant(); } virtual ExpressionFunct getType() const override { return meFunct; } virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* pOptionalArg, sal_uInt32 nFlags ) override { EnhancedCustomShapeParameter aRet; switch( meFunct ) { case ExpressionFunct::UnaryAbs : { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 3; FillEquationParameter( mpArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; case ExpressionFunct::UnarySqrt: { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 13; FillEquationParameter( mpArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; case ExpressionFunct::UnarySin : { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 9; if ( pOptionalArg ) FillEquationParameter( pOptionalArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); else aEquation.nPara[ 0 ] = 1; EnhancedCustomShapeParameter aSource( mpArg->fillNode( rEquations, nullptr, nFlags | EXPRESSION_FLAG_SUMANGLE_MODE ) ); if ( aSource.Type == EnhancedCustomShapeParameterType::NORMAL ) { // sumangle needed :-( EnhancedCustomShapeEquation _aEquation; _aEquation.nOperation |= 0xe; // sumangle FillEquationParameter( aSource, 1, _aEquation ); aSource.Type = EnhancedCustomShapeParameterType::EQUATION; aSource.Value <<= static_cast(rEquations.size()); rEquations.push_back( _aEquation ); } FillEquationParameter( aSource, 1, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; case ExpressionFunct::UnaryCos : { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 10; if ( pOptionalArg ) FillEquationParameter( pOptionalArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); else aEquation.nPara[ 0 ] = 1; EnhancedCustomShapeParameter aSource( mpArg->fillNode( rEquations, nullptr, nFlags | EXPRESSION_FLAG_SUMANGLE_MODE ) ); if ( aSource.Type == EnhancedCustomShapeParameterType::NORMAL ) { // sumangle needed :-( EnhancedCustomShapeEquation aTmpEquation; aTmpEquation.nOperation |= 0xe; // sumangle FillEquationParameter( aSource, 1, aTmpEquation ); aSource.Type = EnhancedCustomShapeParameterType::EQUATION; aSource.Value <<= static_cast(rEquations.size()); rEquations.push_back( aTmpEquation ); } FillEquationParameter( aSource, 1, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; case ExpressionFunct::UnaryTan : { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 16; if ( pOptionalArg ) FillEquationParameter( pOptionalArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); else aEquation.nPara[ 0 ] = 1; EnhancedCustomShapeParameter aSource( mpArg->fillNode( rEquations, nullptr, nFlags | EXPRESSION_FLAG_SUMANGLE_MODE ) ); if ( aSource.Type == EnhancedCustomShapeParameterType::NORMAL ) { // sumangle needed :-( EnhancedCustomShapeEquation aTmpEquation; aTmpEquation.nOperation |= 0xe; // sumangle FillEquationParameter( aSource, 1, aTmpEquation ); aSource.Type = EnhancedCustomShapeParameterType::EQUATION; aSource.Value <<= static_cast(rEquations.size()); rEquations.push_back( aTmpEquation ); } FillEquationParameter( aSource, 1, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; case ExpressionFunct::UnaryAtan: { // TODO: aRet.Type = EnhancedCustomShapeParameterType::NORMAL; } break; case ExpressionFunct::UnaryNeg: { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 1; aEquation.nPara[ 1 ] = -1; aEquation.nPara[ 2 ] = 1; FillEquationParameter( mpArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; default: break; } return aRet; } }; /** ExpressionNode implementation for unary function over two ExpressionNodes */ class BinaryFunctionExpression : public ExpressionNode { const ExpressionFunct meFunct; std::shared_ptr mpFirstArg; std::shared_ptr mpSecondArg; public: BinaryFunctionExpression( const ExpressionFunct eFunct, std::shared_ptr xFirstArg, std::shared_ptr xSecondArg ) : meFunct( eFunct ), mpFirstArg(std::move( xFirstArg )), mpSecondArg(std::move( xSecondArg )) { } #if defined(__clang__) || (defined (__GNUC__) && __GNUC__ >= 8) //GetEquationValueAsDouble calls isFinite on the result __attribute__((no_sanitize("float-divide-by-zero"))) #endif static double getValue( const ExpressionFunct eFunct, const std::shared_ptr& rFirstArg, const std::shared_ptr& rSecondArg ) { double fRet = 0; switch( eFunct ) { case ExpressionFunct::BinaryPlus : fRet = (*rFirstArg)() + (*rSecondArg)(); break; case ExpressionFunct::BinaryMinus: fRet = (*rFirstArg)() - (*rSecondArg)(); break; case ExpressionFunct::BinaryMul : fRet = (*rFirstArg)() * (*rSecondArg)(); break; case ExpressionFunct::BinaryDiv : fRet = (*rFirstArg)() / (*rSecondArg)(); break; case ExpressionFunct::BinaryMin : fRet = ::std::min( (*rFirstArg)(), (*rSecondArg)() ); break; case ExpressionFunct::BinaryMax : fRet = ::std::max( (*rFirstArg)(), (*rSecondArg)() ); break; case ExpressionFunct::BinaryAtan2: fRet = atan2( (*rFirstArg)(), (*rSecondArg)() ); break; default: break; } return fRet; } virtual double operator()() const override { return getValue( meFunct, mpFirstArg, mpSecondArg ); } virtual bool isConstant() const override { return mpFirstArg->isConstant() && mpSecondArg->isConstant(); } virtual ExpressionFunct getType() const override { return meFunct; } virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /*pOptionalArg*/, sal_uInt32 nFlags ) override { EnhancedCustomShapeParameter aRet; switch( meFunct ) { case ExpressionFunct::BinaryPlus : { if ( nFlags & EXPRESSION_FLAG_SUMANGLE_MODE ) { if ( mpFirstArg->getType() == ExpressionFunct::EnumAdjustment ) { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 0xe; // sumangle FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } else if ( mpSecondArg->getType() == ExpressionFunct::EnumAdjustment ) { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 0xe; // sumangle FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } else { EnhancedCustomShapeEquation aSumangle1; aSumangle1.nOperation |= 0xe; // sumangle FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags &~EXPRESSION_FLAG_SUMANGLE_MODE ), 1, aSumangle1 ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aSumangle1 ); EnhancedCustomShapeEquation aSumangle2; aSumangle2.nOperation |= 0xe; // sumangle FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags &~EXPRESSION_FLAG_SUMANGLE_MODE ), 1, aSumangle2 ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aSumangle2 ); EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 0; aEquation.nPara[ 0 ] = ( rEquations.size() - 2 ) | 0x400; aEquation.nPara[ 1 ] = ( rEquations.size() - 1 ) | 0x400; aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } } else { bool bFirstIsEmpty = mpFirstArg->isConstant() && ( (*mpFirstArg)() == 0 ); bool bSecondIsEmpty = mpSecondArg->isConstant() && ( (*mpSecondArg)() == 0 ); if ( bFirstIsEmpty ) aRet = mpSecondArg->fillNode( rEquations, nullptr, nFlags ); else if ( bSecondIsEmpty ) aRet = mpFirstArg->fillNode( rEquations, nullptr, nFlags ); else { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 0; FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } } } break; case ExpressionFunct::BinaryMinus: { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 0; FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 2, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; case ExpressionFunct::BinaryMul : { // in the dest. format the cos function is using integer as result :-( // so we can't use the generic algorithm if ( ( mpFirstArg->getType() == ExpressionFunct::UnarySin ) || ( mpFirstArg->getType() == ExpressionFunct::UnaryCos ) || ( mpFirstArg->getType() == ExpressionFunct::UnaryTan ) ) aRet = mpFirstArg->fillNode( rEquations, mpSecondArg.get(), nFlags ); else if ( ( mpSecondArg->getType() == ExpressionFunct::UnarySin ) || ( mpSecondArg->getType() == ExpressionFunct::UnaryCos ) || ( mpSecondArg->getType() == ExpressionFunct::UnaryTan ) ) aRet = mpSecondArg->fillNode( rEquations, mpFirstArg.get(), nFlags ); else { if ( mpFirstArg->isConstant() && (*mpFirstArg)() == 1 ) aRet = mpSecondArg->fillNode( rEquations, nullptr, nFlags ); else if ( mpSecondArg->isConstant() && (*mpSecondArg)() == 1 ) aRet = mpFirstArg->fillNode( rEquations, nullptr, nFlags ); else if ( ( mpFirstArg->getType() == ExpressionFunct::BinaryDiv ) // don't care of (pi/180) && ( static_cast(mpFirstArg.get())->mpFirstArg->getType() == ExpressionFunct::EnumPi ) && ( static_cast(mpFirstArg.get())->mpSecondArg->getType() == ExpressionFunct::Const ) ) { aRet = mpSecondArg->fillNode( rEquations, nullptr, nFlags ); } else if ( ( mpSecondArg->getType() == ExpressionFunct::BinaryDiv ) // don't care of (pi/180) && ( static_cast(mpSecondArg.get())->mpFirstArg->getType() == ExpressionFunct::EnumPi ) && ( static_cast(mpSecondArg.get())->mpSecondArg->getType() == ExpressionFunct::Const ) ) { aRet = mpFirstArg->fillNode( rEquations, nullptr, nFlags ); } else { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 1; FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation ); aEquation.nPara[ 2 ] = 1; aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } } } break; case ExpressionFunct::BinaryDiv : { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 1; FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); aEquation.nPara[ 1 ] = 1; FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 2, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; case ExpressionFunct::BinaryMin : { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 4; FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; case ExpressionFunct::BinaryMax : { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 5; FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; case ExpressionFunct::BinaryAtan2: { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 8; FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation ); aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); rEquations.push_back( aEquation ); } break; default: break; } return aRet; } }; class IfExpression : public ExpressionNode { std::shared_ptr mpFirstArg; std::shared_ptr mpSecondArg; std::shared_ptr mpThirdArg; public: IfExpression( std::shared_ptr xFirstArg, std::shared_ptr xSecondArg, std::shared_ptr xThirdArg ) : mpFirstArg(std::move( xFirstArg )), mpSecondArg(std::move(xSecondArg )), mpThirdArg(std::move( xThirdArg )) { } virtual bool isConstant() const override { return mpFirstArg->isConstant() && mpSecondArg->isConstant() && mpThirdArg->isConstant(); } virtual double operator()() const override { return (*mpFirstArg)() > 0 ? (*mpSecondArg)() : (*mpThirdArg)(); } virtual ExpressionFunct getType() const override { return ExpressionFunct::TernaryIf; } virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /*pOptionalArg*/, sal_uInt32 nFlags ) override { EnhancedCustomShapeParameter aRet; aRet.Type = EnhancedCustomShapeParameterType::EQUATION; aRet.Value <<= static_cast(rEquations.size()); { EnhancedCustomShapeEquation aEquation; aEquation.nOperation |= 6; FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation ); FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation ); FillEquationParameter( mpThirdArg->fillNode( rEquations, nullptr, nFlags ), 2, aEquation ); rEquations.push_back( aEquation ); } return aRet; } }; // FUNCTION PARSER typedef const char* StringIteratorT; struct ParserContext { typedef ::std::stack< std::shared_ptr > 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; const EnhancedCustomShape2d* mpCustoShape; }; typedef std::shared_ptr< ParserContext > ParserContextSharedPtr; /** Generate parse-dependent-but-then-constant value */ class DoubleConstantFunctor { ParserContextSharedPtr mxContext; public: explicit DoubleConstantFunctor( ParserContextSharedPtr xContext ) : mxContext(std::move( xContext )) { } void operator()( double n ) const { mxContext->maOperandStack.push( std::make_shared( n ) ); } }; class EnumFunctor { const ExpressionFunct meFunct; ParserContextSharedPtr mxContext; public: EnumFunctor( const ExpressionFunct eFunct, ParserContextSharedPtr xContext ) : meFunct( eFunct ) , mxContext(std::move( xContext )) { } void operator()( StringIteratorT rFirst, StringIteratorT rSecond ) const { /*double nVal = mnValue;*/ switch( meFunct ) { case ExpressionFunct::EnumAdjustment : { OUString aVal( rFirst + 1, rSecond - rFirst, RTL_TEXTENCODING_UTF8 ); mxContext->maOperandStack.push( std::make_shared( *mxContext->mpCustoShape, aVal.toInt32() ) ); } break; case ExpressionFunct::EnumEquation : { OUString aVal( rFirst + 1, rSecond - rFirst, RTL_TEXTENCODING_UTF8 ); mxContext->maOperandStack.push( std::make_shared( *mxContext->mpCustoShape, aVal.toInt32() ) ); } break; default: mxContext->maOperandStack.push( std::make_shared( *mxContext->mpCustoShape, meFunct ) ); } } }; class UnaryFunctionFunctor { const ExpressionFunct meFunct; ParserContextSharedPtr mxContext; public: UnaryFunctionFunctor( const ExpressionFunct eFunct, ParserContextSharedPtr xContext ) : meFunct( eFunct ), mxContext(std::move( xContext )) { } void operator()( StringIteratorT, StringIteratorT ) const { ParserContext::OperandStack& rNodeStack( mxContext->maOperandStack ); if( rNodeStack.empty() ) throw ParseError( "Not enough arguments for unary operator" ); // retrieve arguments std::shared_ptr pArg( std::move(rNodeStack.top()) ); rNodeStack.pop(); if( pArg->isConstant() ) // check for constness rNodeStack.push( std::make_shared( UnaryFunctionExpression::getValue( meFunct, pArg ) ) ); else // push complex node, that calcs the value on demand rNodeStack.push( std::make_shared( meFunct, pArg ) ); } }; /** Implements a binary function over two ExpressionNodes @tpl Generator Generator functor, to generate an ExpressionNode of appropriate type */ class BinaryFunctionFunctor { const ExpressionFunct meFunct; ParserContextSharedPtr mxContext; public: BinaryFunctionFunctor( const ExpressionFunct eFunct, ParserContextSharedPtr xContext ) : meFunct( eFunct ), mxContext(std::move( xContext )) { } void operator()( StringIteratorT, StringIteratorT ) const { ParserContext::OperandStack& rNodeStack( mxContext->maOperandStack ); if( rNodeStack.size() < 2 ) throw ParseError( "Not enough arguments for binary operator" ); // retrieve arguments std::shared_ptr pSecondArg( std::move(rNodeStack.top()) ); rNodeStack.pop(); std::shared_ptr pFirstArg( std::move(rNodeStack.top()) ); rNodeStack.pop(); assert(pSecondArg && pFirstArg && "count of arg checked before we get here"); // create combined ExpressionNode auto pNode = std::make_shared( meFunct, pFirstArg, pSecondArg ); // check for constness if( pFirstArg->isConstant() && pSecondArg->isConstant() ) // call the operator() at pNode, store result in constant value ExpressionNode. rNodeStack.push( std::make_shared( (*pNode)() ) ); else // push complex node, that calcs the value on demand rNodeStack.push( pNode ); } }; class IfFunctor { ParserContextSharedPtr mxContext; public: explicit IfFunctor( ParserContextSharedPtr xContext ) : mxContext(std::move( xContext )) { } void operator()( StringIteratorT, StringIteratorT ) const { ParserContext::OperandStack& rNodeStack( mxContext->maOperandStack ); if( rNodeStack.size() < 3 ) throw ParseError( "Not enough arguments for ternary operator" ); // retrieve arguments std::shared_ptr pThirdArg( std::move(rNodeStack.top()) ); rNodeStack.pop(); std::shared_ptr pSecondArg( std::move(rNodeStack.top()) ); rNodeStack.pop(); std::shared_ptr pFirstArg( std::move(rNodeStack.top()) ); rNodeStack.pop(); assert(pThirdArg && pSecondArg && pFirstArg); // create combined ExpressionNode auto pNode = std::make_shared( pFirstArg, pSecondArg, pThirdArg ); // check for constness if( pFirstArg->isConstant() && pSecondArg->isConstant() && pThirdArg->isConstant() ) rNodeStack.push( std::make_shared( (*pNode)() ) ); // call the operator() at pNode, store result in constant value ExpressionNode. else rNodeStack.push( pNode ); // push complex node, that calcs the value on demand } }; // 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::classic::ureal_parser_policies { template< typename ScannerT > static typename ::boost::spirit::classic::parser_result< ::boost::spirit::classic::chlit<>, ScannerT >::type parse_exp(ScannerT& scan) { // as_lower_d somehow breaks MSVC7 return ::boost::spirit::classic::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::classic::grammar< ExpressionGrammar > { public: /** Create an arithmetic expression grammar @param rParserContext Contains context info for the parser */ explicit ExpressionGrammar( ParserContextSharedPtr xParserContext ) : mpParserContext(std::move( xParserContext )) { } template< typename ScannerT > class definition { public: // grammar definition explicit definition( const ExpressionGrammar& self ) { using ::boost::spirit::classic::str_p; using ::boost::spirit::classic::range_p; using ::boost::spirit::classic::lexeme_d; using ::boost::spirit::classic::real_parser; identifier = str_p( "pi" )[ EnumFunctor(ExpressionFunct::EnumPi, self.getContext() ) ] | str_p( "left" )[ EnumFunctor(ExpressionFunct::EnumLeft, self.getContext() ) ] | str_p( "top" )[ EnumFunctor(ExpressionFunct::EnumTop, self.getContext() ) ] | str_p( "right" )[ EnumFunctor(ExpressionFunct::EnumRight, self.getContext() ) ] | str_p( "bottom" )[ EnumFunctor(ExpressionFunct::EnumBottom, self.getContext() ) ] | str_p( "xstretch" )[ EnumFunctor(ExpressionFunct::EnumXStretch, self.getContext() ) ] | str_p( "ystretch" )[ EnumFunctor(ExpressionFunct::EnumYStretch, self.getContext() ) ] | str_p( "hasstroke" )[ EnumFunctor(ExpressionFunct::EnumHasStroke, self.getContext() ) ] | str_p( "hasfill" )[ EnumFunctor(ExpressionFunct::EnumHasFill, self.getContext() ) ] | str_p( "width" )[ EnumFunctor(ExpressionFunct::EnumWidth, self.getContext() ) ] | str_p( "height" )[ EnumFunctor(ExpressionFunct::EnumHeight, self.getContext() ) ] | str_p( "logwidth" )[ EnumFunctor(ExpressionFunct::EnumLogWidth, self.getContext() ) ] | str_p( "logheight" )[ EnumFunctor(ExpressionFunct::EnumLogHeight, self.getContext() ) ] ; unaryFunction = (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryAbs, self.getContext()) ] | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnarySqrt, self.getContext()) ] | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnarySin, self.getContext()) ] | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryCos, self.getContext()) ] | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryTan, self.getContext()) ] | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryAtan, self.getContext()) ] ; binaryFunction = (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ BinaryFunctionFunctor( ExpressionFunct::BinaryMin, self.getContext()) ] | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ BinaryFunctionFunctor( ExpressionFunct::BinaryMax, self.getContext()) ] | (str_p( "atan2") >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ BinaryFunctionFunctor( ExpressionFunct::BinaryAtan2,self.getContext()) ] ; ternaryFunction = (str_p( "if" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ IfFunctor( self.getContext() ) ] ; funcRef_decl = lexeme_d[ +( range_p('a','z') | range_p('A','Z') | range_p('0','9') ) ]; functionReference = (str_p( "?" ) >> funcRef_decl )[ EnumFunctor( ExpressionFunct::EnumEquation, self.getContext() ) ]; modRef_decl = lexeme_d[ +( range_p('0','9') ) ]; modifierReference = (str_p( "$" ) >> modRef_decl )[ EnumFunctor( ExpressionFunct::EnumAdjustment, self.getContext() ) ]; basicExpression = real_parser >()[ DoubleConstantFunctor(self.getContext()) ] | identifier | functionReference | modifierReference | unaryFunction | binaryFunction | ternaryFunction | '(' >> additiveExpression >> ')' ; unaryExpression = ('-' >> basicExpression)[ UnaryFunctionFunctor( ExpressionFunct::UnaryNeg, self.getContext()) ] | basicExpression ; multiplicativeExpression = unaryExpression >> *( ('*' >> unaryExpression)[ BinaryFunctionFunctor( ExpressionFunct::BinaryMul, self.getContext()) ] | ('/' >> unaryExpression)[ BinaryFunctionFunctor( ExpressionFunct::BinaryDiv, self.getContext()) ] ) ; additiveExpression = multiplicativeExpression >> *( ('+' >> multiplicativeExpression)[ BinaryFunctionFunctor( ExpressionFunct::BinaryPlus, self.getContext()) ] | ('-' >> multiplicativeExpression)[ BinaryFunctionFunctor( ExpressionFunct::BinaryMinus, 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(ternaryFunction); BOOST_SPIRIT_DEBUG_RULE(identifier); } const ::boost::spirit::classic::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::classic::rule< ScannerT > additiveExpression; ::boost::spirit::classic::rule< ScannerT > multiplicativeExpression; ::boost::spirit::classic::rule< ScannerT > unaryExpression; ::boost::spirit::classic::rule< ScannerT > basicExpression; ::boost::spirit::classic::rule< ScannerT > unaryFunction; ::boost::spirit::classic::rule< ScannerT > binaryFunction; ::boost::spirit::classic::rule< ScannerT > ternaryFunction; ::boost::spirit::classic::rule< ScannerT > funcRef_decl; ::boost::spirit::classic::rule< ScannerT > functionReference; ::boost::spirit::classic::rule< ScannerT > modRef_decl; ::boost::spirit::classic::rule< ScannerT > modifierReference; ::boost::spirit::classic::rule< ScannerT > identifier; }; const ParserContextSharedPtr& getContext() const { return mpParserContext; } private: ParserContextSharedPtr mpParserContext; // might get modified during parsing }; const ParserContextSharedPtr& getParserContext() { static ParserContextSharedPtr lcl_parserContext = std::make_shared(); // 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; } } namespace EnhancedCustomShape { std::shared_ptr const & FunctionParser::parseFunction( std::u16string_view rFunction, const EnhancedCustomShape2d& rCustoShape ) { // 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 OString& rAsciiFunction( OUStringToOString( rFunction, RTL_TEXTENCODING_ASCII_US ) ); StringIteratorT aStart( rAsciiFunction.getStr() ); StringIteratorT aEnd( rAsciiFunction.getStr()+rAsciiFunction.getLength() ); // static parser context, because the actual // Spirit parser is also a static object ParserContextSharedPtr pContext = getParserContext(); pContext->mpCustoShape = &rCustoShape; ExpressionGrammar aExpressionGrammer( pContext ); const ::boost::spirit::classic::parse_info aParseInfo( ::boost::spirit::classic::parse( aStart, aEnd, aExpressionGrammer >> ::boost::spirit::classic::end_p, ::boost::spirit::classic::space_p ) ); // input fully congested by the parser? if( !aParseInfo.full ) throw ParseError( "EnhancedCustomShapeFunctionParser::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( "EnhancedCustomShapeFunctionParser::parseFunction(): incomplete or empty expression" ); return pContext->maOperandStack.top(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */