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 .
20 #include <sal/config.h>
22 #include <svx/EnhancedCustomShape2d.hxx>
23 #include <rtl/ustring.hxx>
24 #include <sal/log.hxx>
25 #include <tools/fract.hxx>
27 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
29 // Makes parser a static resource,
30 // we're synchronized externally.
31 // But watch out, the parser might have
32 // state not visible to this code!
34 #define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
36 #if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
37 #define BOOST_SPIRIT_DEBUG
39 #include <boost/spirit/include/classic_core.hpp>
46 using namespace EnhancedCustomShape
;
47 using namespace com::sun::star
;
48 using namespace com::sun::star::drawing
;
50 void EnhancedCustomShape::FillEquationParameter( const EnhancedCustomShapeParameter
& rSource
, const sal_Int32 nDestPara
, EnhancedCustomShapeEquation
& rDest
)
53 if ( rSource
.Value
.getValueTypeClass() == uno::TypeClass_DOUBLE
)
56 if ( rSource
.Value
>>= fValue
)
57 nValue
= static_cast<sal_Int32
>(fValue
);
60 rSource
.Value
>>= nValue
;
62 switch( rSource
.Type
)
64 case css::drawing::EnhancedCustomShapeParameterType::EQUATION
:
66 if ( nValue
& 0x40000000 )
69 rDest
.nOperation
|= 0x20000000 << nDestPara
; // the bit is indicating that this value has to be adjusted later
74 case css::drawing::EnhancedCustomShapeParameterType::ADJUSTMENT
: nValue
+= DFF_Prop_adjustValue
; break;
75 case css::drawing::EnhancedCustomShapeParameterType::BOTTOM
: nValue
= DFF_Prop_geoBottom
; break;
76 case css::drawing::EnhancedCustomShapeParameterType::RIGHT
: nValue
= DFF_Prop_geoRight
; break;
77 case css::drawing::EnhancedCustomShapeParameterType::TOP
: nValue
= DFF_Prop_geoTop
; break;
78 case css::drawing::EnhancedCustomShapeParameterType::LEFT
: nValue
= DFF_Prop_geoLeft
; break;
80 if ( rSource
.Type
!= css::drawing::EnhancedCustomShapeParameterType::NORMAL
)
81 rDest
.nOperation
|= ( 0x2000 << nDestPara
);
82 rDest
.nPara
[ nDestPara
] = nValue
;
85 ExpressionNode::~ExpressionNode()
95 class ConstantValueExpression
: public ExpressionNode
101 explicit ConstantValueExpression( double rValue
) :
105 virtual double operator()() const override
109 virtual bool isConstant() const override
113 virtual ExpressionFunct
getType() const override
115 return ExpressionFunct::Const
;
117 virtual EnhancedCustomShapeParameter
fillNode( std::vector
< EnhancedCustomShapeEquation
>& rEquations
, ExpressionNode
* /* pOptionalArg */, sal_uInt32
/* nFlags */ ) override
119 EnhancedCustomShapeParameter aRet
;
120 Fraction
aFract( maValue
);
121 if ( aFract
.GetDenominator() == 1 )
123 aRet
.Type
= EnhancedCustomShapeParameterType::NORMAL
;
124 aRet
.Value
<<= aFract
.GetNumerator();
128 EnhancedCustomShapeEquation aEquation
;
129 aEquation
.nOperation
= 1;
130 aEquation
.nPara
[ 0 ] = 1;
131 aEquation
.nPara
[ 1 ] = static_cast<sal_Int16
>(aFract
.GetNumerator());
132 aEquation
.nPara
[ 2 ] = static_cast<sal_Int16
>(aFract
.GetDenominator());
133 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
134 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
135 rEquations
.push_back( aEquation
);
141 class AdjustmentExpression
: public ExpressionNode
144 const EnhancedCustomShape2d
& mrCustoShape
;
148 AdjustmentExpression( const EnhancedCustomShape2d
& rCustoShape
, sal_Int32 nIndex
)
150 , mrCustoShape( rCustoShape
)
154 virtual double operator()() const override
158 "$" << mnIndex
<< " --> "
159 << mrCustoShape
.GetAdjustValueAsDouble(mnIndex
) << " (angle: "
160 << 180.0*mrCustoShape
.GetAdjustValueAsDouble(mnIndex
)/10800000.0
162 return mrCustoShape
.GetAdjustValueAsDouble( mnIndex
);
164 virtual bool isConstant() const override
168 virtual ExpressionFunct
getType() const override
170 return ExpressionFunct::EnumAdjustment
;
172 virtual EnhancedCustomShapeParameter
fillNode( std::vector
< EnhancedCustomShapeEquation
>& /*rEquations*/, ExpressionNode
* /*pOptionalArg*/, sal_uInt32
/*nFlags*/ ) override
174 EnhancedCustomShapeParameter aRet
;
175 aRet
.Type
= EnhancedCustomShapeParameterType::ADJUSTMENT
;
176 aRet
.Value
<<= mnIndex
;
181 class EquationExpression
: public ExpressionNode
183 const sal_Int32 mnIndex
;
184 const EnhancedCustomShape2d
& mrCustoShape
;
185 mutable bool mbGettingValueGuard
;
189 EquationExpression( const EnhancedCustomShape2d
& rCustoShape
, sal_Int32 nIndex
)
191 , mrCustoShape( rCustoShape
)
192 , mbGettingValueGuard(false)
195 virtual double operator()() const override
197 if (mbGettingValueGuard
)
198 throw ParseError("Loop in Expression");
199 mbGettingValueGuard
= true;
200 double fRet
= mrCustoShape
.GetEquationValueAsDouble(mnIndex
);
201 mbGettingValueGuard
= false;
204 virtual bool isConstant() const override
208 virtual ExpressionFunct
getType() const override
210 return ExpressionFunct::EnumEquation
;
212 virtual EnhancedCustomShapeParameter
fillNode( std::vector
< EnhancedCustomShapeEquation
>& /*rEquations*/, ExpressionNode
* /*pOptionalArg*/, sal_uInt32
/*nFlags*/ ) override
214 EnhancedCustomShapeParameter aRet
;
215 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
216 aRet
.Value
<<= mnIndex
| 0x40000000; // the bit is indicating that this equation needs to be adjusted later
221 class EnumValueExpression
: public ExpressionNode
223 const ExpressionFunct meFunct
;
224 const EnhancedCustomShape2d
& mrCustoShape
;
228 EnumValueExpression( const EnhancedCustomShape2d
& rCustoShape
, const ExpressionFunct eFunct
)
230 , mrCustoShape ( rCustoShape
)
233 virtual double operator()() const override
235 SAL_INFO("svx", meFunct
<< " --> " << mrCustoShape
.GetEnumFunc(meFunct
) << "(angle: " <<
236 180.0 * mrCustoShape
.GetEnumFunc(meFunct
) / 10800000.0 << ")");
238 return mrCustoShape
.GetEnumFunc( meFunct
);
240 virtual bool isConstant() const override
244 virtual ExpressionFunct
getType() const override
248 virtual EnhancedCustomShapeParameter
fillNode( std::vector
< EnhancedCustomShapeEquation
>& rEquations
, ExpressionNode
* /*pOptionalArg*/, sal_uInt32 nFlags
) override
250 EnhancedCustomShapeParameter aRet
;
252 aRet
.Value
<<= sal_Int32(1);
256 case ExpressionFunct::EnumWidth
: // TODO: do not use this as constant value
257 case ExpressionFunct::EnumHeight
:
258 case ExpressionFunct::EnumLogWidth
:
259 case ExpressionFunct::EnumLogHeight
:
260 case ExpressionFunct::EnumPi
:
262 ConstantValueExpression
aConstantValue( mrCustoShape
.GetEnumFunc( meFunct
) );
263 aRet
= aConstantValue
.fillNode( rEquations
, nullptr, nFlags
);
266 case ExpressionFunct::EnumLeft
: aRet
.Type
= EnhancedCustomShapeParameterType::LEFT
; break;
267 case ExpressionFunct::EnumTop
: aRet
.Type
= EnhancedCustomShapeParameterType::TOP
; break;
268 case ExpressionFunct::EnumRight
: aRet
.Type
= EnhancedCustomShapeParameterType::RIGHT
; break;
269 case ExpressionFunct::EnumBottom
: aRet
.Type
= EnhancedCustomShapeParameterType::BOTTOM
; break;
271 // not implemented so far
272 case ExpressionFunct::EnumXStretch
:
273 case ExpressionFunct::EnumYStretch
:
274 case ExpressionFunct::EnumHasStroke
:
275 case ExpressionFunct::EnumHasFill
: aRet
.Type
= EnhancedCustomShapeParameterType::NORMAL
; break;
284 /** ExpressionNode implementation for unary
285 function over one ExpressionNode
287 class UnaryFunctionExpression
: public ExpressionNode
289 const ExpressionFunct meFunct
;
290 std::shared_ptr
<ExpressionNode
> mpArg
;
293 UnaryFunctionExpression( const ExpressionFunct eFunct
, const std::shared_ptr
<ExpressionNode
>& rArg
) :
298 static double getValue( const ExpressionFunct eFunct
, const std::shared_ptr
<ExpressionNode
>& rArg
)
303 case ExpressionFunct::UnaryAbs
: fRet
= fabs( (*rArg
)() ); break;
304 case ExpressionFunct::UnarySqrt
: fRet
= sqrt( (*rArg
)() ); break;
305 case ExpressionFunct::UnarySin
: fRet
= sin( (*rArg
)() ); break;
306 case ExpressionFunct::UnaryCos
: fRet
= cos( (*rArg
)() ); break;
307 case ExpressionFunct::UnaryTan
: fRet
= tan( (*rArg
)() ); break;
308 case ExpressionFunct::UnaryAtan
: fRet
= atan( (*rArg
)() ); break;
309 case ExpressionFunct::UnaryNeg
: fRet
= ::std::negate
<double>()( (*rArg
)() ); break;
315 virtual double operator()() const override
317 return getValue( meFunct
, mpArg
);
319 virtual bool isConstant() const override
321 return mpArg
->isConstant();
323 virtual ExpressionFunct
getType() const override
327 virtual EnhancedCustomShapeParameter
fillNode( std::vector
< EnhancedCustomShapeEquation
>& rEquations
, ExpressionNode
* pOptionalArg
, sal_uInt32 nFlags
) override
329 EnhancedCustomShapeParameter aRet
;
332 case ExpressionFunct::UnaryAbs
:
334 EnhancedCustomShapeEquation aEquation
;
335 aEquation
.nOperation
|= 3;
336 FillEquationParameter( mpArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
337 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
338 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
339 rEquations
.push_back( aEquation
);
342 case ExpressionFunct::UnarySqrt
:
344 EnhancedCustomShapeEquation aEquation
;
345 aEquation
.nOperation
|= 13;
346 FillEquationParameter( mpArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
347 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
348 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
349 rEquations
.push_back( aEquation
);
352 case ExpressionFunct::UnarySin
:
354 EnhancedCustomShapeEquation aEquation
;
355 aEquation
.nOperation
|= 9;
357 FillEquationParameter( pOptionalArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
359 aEquation
.nPara
[ 0 ] = 1;
361 EnhancedCustomShapeParameter
aSource( mpArg
->fillNode( rEquations
, nullptr, nFlags
| EXPRESSION_FLAG_SUMANGLE_MODE
) );
362 if ( aSource
.Type
== EnhancedCustomShapeParameterType::NORMAL
)
363 { // sumangle needed :-(
364 EnhancedCustomShapeEquation _aEquation
;
365 _aEquation
.nOperation
|= 0xe; // sumangle
366 FillEquationParameter( aSource
, 1, _aEquation
);
367 aSource
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
368 aSource
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
369 rEquations
.push_back( _aEquation
);
371 FillEquationParameter( aSource
, 1, aEquation
);
372 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
373 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
374 rEquations
.push_back( aEquation
);
377 case ExpressionFunct::UnaryCos
:
379 EnhancedCustomShapeEquation aEquation
;
380 aEquation
.nOperation
|= 10;
382 FillEquationParameter( pOptionalArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
384 aEquation
.nPara
[ 0 ] = 1;
386 EnhancedCustomShapeParameter
aSource( mpArg
->fillNode( rEquations
, nullptr, nFlags
| EXPRESSION_FLAG_SUMANGLE_MODE
) );
387 if ( aSource
.Type
== EnhancedCustomShapeParameterType::NORMAL
)
388 { // sumangle needed :-(
389 EnhancedCustomShapeEquation aTmpEquation
;
390 aTmpEquation
.nOperation
|= 0xe; // sumangle
391 FillEquationParameter( aSource
, 1, aTmpEquation
);
392 aSource
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
393 aSource
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
394 rEquations
.push_back( aTmpEquation
);
396 FillEquationParameter( aSource
, 1, aEquation
);
397 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
398 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
399 rEquations
.push_back( aEquation
);
402 case ExpressionFunct::UnaryTan
:
404 EnhancedCustomShapeEquation aEquation
;
405 aEquation
.nOperation
|= 16;
407 FillEquationParameter( pOptionalArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
409 aEquation
.nPara
[ 0 ] = 1;
411 EnhancedCustomShapeParameter
aSource( mpArg
->fillNode( rEquations
, nullptr, nFlags
| EXPRESSION_FLAG_SUMANGLE_MODE
) );
412 if ( aSource
.Type
== EnhancedCustomShapeParameterType::NORMAL
)
413 { // sumangle needed :-(
414 EnhancedCustomShapeEquation aTmpEquation
;
415 aTmpEquation
.nOperation
|= 0xe; // sumangle
416 FillEquationParameter( aSource
, 1, aTmpEquation
);
417 aSource
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
418 aSource
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
419 rEquations
.push_back( aTmpEquation
);
421 FillEquationParameter( aSource
, 1, aEquation
);
422 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
423 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
424 rEquations
.push_back( aEquation
);
427 case ExpressionFunct::UnaryAtan
:
430 aRet
.Type
= EnhancedCustomShapeParameterType::NORMAL
;
433 case ExpressionFunct::UnaryNeg
:
435 EnhancedCustomShapeEquation aEquation
;
436 aEquation
.nOperation
|= 1;
437 aEquation
.nPara
[ 1 ] = -1;
438 aEquation
.nPara
[ 2 ] = 1;
439 FillEquationParameter( mpArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
440 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
441 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
442 rEquations
.push_back( aEquation
);
452 /** ExpressionNode implementation for unary
453 function over two ExpressionNodes
455 class BinaryFunctionExpression
: public ExpressionNode
457 const ExpressionFunct meFunct
;
458 std::shared_ptr
<ExpressionNode
> mpFirstArg
;
459 std::shared_ptr
<ExpressionNode
> mpSecondArg
;
463 BinaryFunctionExpression( const ExpressionFunct eFunct
, const std::shared_ptr
<ExpressionNode
>& rFirstArg
, const std::shared_ptr
<ExpressionNode
>& rSecondArg
) :
465 mpFirstArg( rFirstArg
),
466 mpSecondArg( rSecondArg
)
469 #if defined(__clang__) || (defined (__GNUC__) && __GNUC__ >= 8)
470 //GetEquationValueAsDouble calls isFinite on the result
471 __attribute__((no_sanitize("float-divide-by-zero")))
473 static double getValue( const ExpressionFunct eFunct
, const std::shared_ptr
<ExpressionNode
>& rFirstArg
, const std::shared_ptr
<ExpressionNode
>& rSecondArg
)
478 case ExpressionFunct::BinaryPlus
: fRet
= (*rFirstArg
)() + (*rSecondArg
)(); break;
479 case ExpressionFunct::BinaryMinus
: fRet
= (*rFirstArg
)() - (*rSecondArg
)(); break;
480 case ExpressionFunct::BinaryMul
: fRet
= (*rFirstArg
)() * (*rSecondArg
)(); break;
481 case ExpressionFunct::BinaryDiv
: fRet
= (*rFirstArg
)() / (*rSecondArg
)(); break;
482 case ExpressionFunct::BinaryMin
: fRet
= ::std::min( (*rFirstArg
)(), (*rSecondArg
)() ); break;
483 case ExpressionFunct::BinaryMax
: fRet
= ::std::max( (*rFirstArg
)(), (*rSecondArg
)() ); break;
484 case ExpressionFunct::BinaryAtan2
: fRet
= atan2( (*rFirstArg
)(), (*rSecondArg
)() ); break;
490 virtual double operator()() const override
492 return getValue( meFunct
, mpFirstArg
, mpSecondArg
);
494 virtual bool isConstant() const override
496 return mpFirstArg
->isConstant() && mpSecondArg
->isConstant();
498 virtual ExpressionFunct
getType() const override
502 virtual EnhancedCustomShapeParameter
fillNode( std::vector
< EnhancedCustomShapeEquation
>& rEquations
, ExpressionNode
* /*pOptionalArg*/, sal_uInt32 nFlags
) override
504 EnhancedCustomShapeParameter aRet
;
507 case ExpressionFunct::BinaryPlus
:
509 if ( nFlags
& EXPRESSION_FLAG_SUMANGLE_MODE
)
511 if ( mpFirstArg
->getType() == ExpressionFunct::EnumAdjustment
)
513 EnhancedCustomShapeEquation aEquation
;
514 aEquation
.nOperation
|= 0xe; // sumangle
515 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
516 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 1, aEquation
);
517 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
518 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
519 rEquations
.push_back( aEquation
);
521 else if ( mpSecondArg
->getType() == ExpressionFunct::EnumAdjustment
)
523 EnhancedCustomShapeEquation aEquation
;
524 aEquation
.nOperation
|= 0xe; // sumangle
525 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
526 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 1, aEquation
);
527 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
528 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
529 rEquations
.push_back( aEquation
);
533 EnhancedCustomShapeEquation aSumangle1
;
534 aSumangle1
.nOperation
|= 0xe; // sumangle
535 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
&~EXPRESSION_FLAG_SUMANGLE_MODE
), 1, aSumangle1
);
536 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
537 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
538 rEquations
.push_back( aSumangle1
);
540 EnhancedCustomShapeEquation aSumangle2
;
541 aSumangle2
.nOperation
|= 0xe; // sumangle
542 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
&~EXPRESSION_FLAG_SUMANGLE_MODE
), 1, aSumangle2
);
543 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
544 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
545 rEquations
.push_back( aSumangle2
);
547 EnhancedCustomShapeEquation aEquation
;
548 aEquation
.nOperation
|= 0;
549 aEquation
.nPara
[ 0 ] = ( rEquations
.size() - 2 ) | 0x400;
550 aEquation
.nPara
[ 1 ] = ( rEquations
.size() - 1 ) | 0x400;
551 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
552 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
553 rEquations
.push_back( aEquation
);
558 bool bFirstIsEmpty
= mpFirstArg
->isConstant() && ( (*mpFirstArg
)() == 0 );
559 bool bSecondIsEmpty
= mpSecondArg
->isConstant() && ( (*mpSecondArg
)() == 0 );
562 aRet
= mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
);
563 else if ( bSecondIsEmpty
)
564 aRet
= mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
);
567 EnhancedCustomShapeEquation aEquation
;
568 aEquation
.nOperation
|= 0;
569 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
570 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 1, aEquation
);
571 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
572 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
573 rEquations
.push_back( aEquation
);
578 case ExpressionFunct::BinaryMinus
:
580 EnhancedCustomShapeEquation aEquation
;
581 aEquation
.nOperation
|= 0;
582 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
583 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 2, aEquation
);
584 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
585 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
586 rEquations
.push_back( aEquation
);
589 case ExpressionFunct::BinaryMul
:
591 // in the dest. format the cos function is using integer as result :-(
592 // so we can't use the generic algorithm
593 if ( ( mpFirstArg
->getType() == ExpressionFunct::UnarySin
) || ( mpFirstArg
->getType() == ExpressionFunct::UnaryCos
) || ( mpFirstArg
->getType() == ExpressionFunct::UnaryTan
) )
594 aRet
= mpFirstArg
->fillNode( rEquations
, mpSecondArg
.get(), nFlags
);
595 else if ( ( mpSecondArg
->getType() == ExpressionFunct::UnarySin
) || ( mpSecondArg
->getType() == ExpressionFunct::UnaryCos
) || ( mpSecondArg
->getType() == ExpressionFunct::UnaryTan
) )
596 aRet
= mpSecondArg
->fillNode( rEquations
, mpFirstArg
.get(), nFlags
);
599 if ( mpFirstArg
->isConstant() && (*mpFirstArg
)() == 1 )
600 aRet
= mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
);
601 else if ( mpSecondArg
->isConstant() && (*mpSecondArg
)() == 1 )
602 aRet
= mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
);
603 else if ( ( mpFirstArg
->getType() == ExpressionFunct::BinaryDiv
) // don't care of (pi/180)
604 && ( static_cast<BinaryFunctionExpression
*>(mpFirstArg
.get())->mpFirstArg
->getType() == ExpressionFunct::EnumPi
)
605 && ( static_cast<BinaryFunctionExpression
*>(mpFirstArg
.get())->mpSecondArg
->getType() == ExpressionFunct::Const
) )
607 aRet
= mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
);
609 else if ( ( mpSecondArg
->getType() == ExpressionFunct::BinaryDiv
) // don't care of (pi/180)
610 && ( static_cast<BinaryFunctionExpression
*>(mpSecondArg
.get())->mpFirstArg
->getType() == ExpressionFunct::EnumPi
)
611 && ( static_cast<BinaryFunctionExpression
*>(mpSecondArg
.get())->mpSecondArg
->getType() == ExpressionFunct::Const
) )
613 aRet
= mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
);
617 EnhancedCustomShapeEquation aEquation
;
618 aEquation
.nOperation
|= 1;
619 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
620 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 1, aEquation
);
621 aEquation
.nPara
[ 2 ] = 1;
622 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
623 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
624 rEquations
.push_back( aEquation
);
629 case ExpressionFunct::BinaryDiv
:
631 EnhancedCustomShapeEquation aEquation
;
632 aEquation
.nOperation
|= 1;
633 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
634 aEquation
.nPara
[ 1 ] = 1;
635 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 2, aEquation
);
636 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
637 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
638 rEquations
.push_back( aEquation
);
641 case ExpressionFunct::BinaryMin
:
643 EnhancedCustomShapeEquation aEquation
;
644 aEquation
.nOperation
|= 4;
645 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
646 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 1, aEquation
);
647 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
648 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
649 rEquations
.push_back( aEquation
);
652 case ExpressionFunct::BinaryMax
:
654 EnhancedCustomShapeEquation aEquation
;
655 aEquation
.nOperation
|= 5;
656 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
657 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 1, aEquation
);
658 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
659 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
660 rEquations
.push_back( aEquation
);
663 case ExpressionFunct::BinaryAtan2
:
665 EnhancedCustomShapeEquation aEquation
;
666 aEquation
.nOperation
|= 8;
667 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
668 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 1, aEquation
);
669 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
670 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
671 rEquations
.push_back( aEquation
);
681 class IfExpression
: public ExpressionNode
683 std::shared_ptr
<ExpressionNode
> mpFirstArg
;
684 std::shared_ptr
<ExpressionNode
> mpSecondArg
;
685 std::shared_ptr
<ExpressionNode
> mpThirdArg
;
689 IfExpression( const std::shared_ptr
<ExpressionNode
>& rFirstArg
,
690 const std::shared_ptr
<ExpressionNode
>& rSecondArg
,
691 const std::shared_ptr
<ExpressionNode
>& rThirdArg
) :
692 mpFirstArg( rFirstArg
),
693 mpSecondArg( rSecondArg
),
694 mpThirdArg( rThirdArg
)
697 virtual bool isConstant() const override
700 mpFirstArg
->isConstant() &&
701 mpSecondArg
->isConstant() &&
702 mpThirdArg
->isConstant();
704 virtual double operator()() const override
706 return (*mpFirstArg
)() > 0 ? (*mpSecondArg
)() : (*mpThirdArg
)();
708 virtual ExpressionFunct
getType() const override
710 return ExpressionFunct::TernaryIf
;
712 virtual EnhancedCustomShapeParameter
fillNode( std::vector
< EnhancedCustomShapeEquation
>& rEquations
, ExpressionNode
* /*pOptionalArg*/, sal_uInt32 nFlags
) override
714 EnhancedCustomShapeParameter aRet
;
715 aRet
.Type
= EnhancedCustomShapeParameterType::EQUATION
;
716 aRet
.Value
<<= static_cast<sal_Int32
>(rEquations
.size());
718 EnhancedCustomShapeEquation aEquation
;
719 aEquation
.nOperation
|= 6;
720 FillEquationParameter( mpFirstArg
->fillNode( rEquations
, nullptr, nFlags
), 0, aEquation
);
721 FillEquationParameter( mpSecondArg
->fillNode( rEquations
, nullptr, nFlags
), 1, aEquation
);
722 FillEquationParameter( mpThirdArg
->fillNode( rEquations
, nullptr, nFlags
), 2, aEquation
);
723 rEquations
.push_back( aEquation
);
733 typedef const char* StringIteratorT
;
737 typedef ::std::stack
< std::shared_ptr
<ExpressionNode
> > OperandStack
;
739 // stores a stack of not-yet-evaluated operands. This is used
740 // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
741 // arguments from. If all arguments to an operator are constant,
742 // the operator pushes a precalculated result on the stack, and
743 // a composite ExpressionNode otherwise.
744 OperandStack maOperandStack
;
746 const EnhancedCustomShape2d
* mpCustoShape
;
750 typedef std::shared_ptr
< ParserContext
> ParserContextSharedPtr
;
752 /** Generate parse-dependent-but-then-constant value
754 class DoubleConstantFunctor
756 ParserContextSharedPtr mxContext
;
759 explicit DoubleConstantFunctor( const ParserContextSharedPtr
& rContext
) :
760 mxContext( rContext
)
763 void operator()( double n
) const
765 mxContext
->maOperandStack
.push( std::make_shared
<ConstantValueExpression
>( n
) );
771 const ExpressionFunct meFunct
;
772 ParserContextSharedPtr mxContext
;
776 EnumFunctor( const ExpressionFunct eFunct
, const ParserContextSharedPtr
& rContext
)
778 , mxContext( rContext
)
781 void operator()( StringIteratorT rFirst
, StringIteratorT rSecond
) const
783 /*double nVal = mnValue;*/
786 case ExpressionFunct::EnumAdjustment
:
788 OUString
aVal( rFirst
+ 1, rSecond
- rFirst
, RTL_TEXTENCODING_UTF8
);
789 mxContext
->maOperandStack
.push( std::make_shared
<AdjustmentExpression
>( *mxContext
->mpCustoShape
, aVal
.toInt32() ) );
792 case ExpressionFunct::EnumEquation
:
794 OUString
aVal( rFirst
+ 1, rSecond
- rFirst
, RTL_TEXTENCODING_UTF8
);
795 mxContext
->maOperandStack
.push( std::make_shared
<EquationExpression
>( *mxContext
->mpCustoShape
, aVal
.toInt32() ) );
799 mxContext
->maOperandStack
.push( std::make_shared
<EnumValueExpression
>( *mxContext
->mpCustoShape
, meFunct
) );
804 class UnaryFunctionFunctor
806 const ExpressionFunct meFunct
;
807 ParserContextSharedPtr mxContext
;
811 UnaryFunctionFunctor( const ExpressionFunct eFunct
, const ParserContextSharedPtr
& rContext
) :
813 mxContext( rContext
)
816 void operator()( StringIteratorT
, StringIteratorT
) const
818 ParserContext::OperandStack
& rNodeStack( mxContext
->maOperandStack
);
820 if( rNodeStack
.empty() )
821 throw ParseError( "Not enough arguments for unary operator" );
823 // retrieve arguments
824 std::shared_ptr
<ExpressionNode
> pArg( std::move(rNodeStack
.top()) );
827 if( pArg
->isConstant() ) // check for constness
828 rNodeStack
.push( std::make_shared
<ConstantValueExpression
>( UnaryFunctionExpression::getValue( meFunct
, pArg
) ) );
829 else // push complex node, that calcs the value on demand
830 rNodeStack
.push( std::make_shared
<UnaryFunctionExpression
>( meFunct
, pArg
) );
834 /** Implements a binary function over two ExpressionNodes
837 Generator functor, to generate an ExpressionNode of
841 class BinaryFunctionFunctor
843 const ExpressionFunct meFunct
;
844 ParserContextSharedPtr mxContext
;
848 BinaryFunctionFunctor( const ExpressionFunct eFunct
, const ParserContextSharedPtr
& rContext
) :
850 mxContext( rContext
)
854 void operator()( StringIteratorT
, StringIteratorT
) const
856 ParserContext::OperandStack
& rNodeStack( mxContext
->maOperandStack
);
858 if( rNodeStack
.size() < 2 )
859 throw ParseError( "Not enough arguments for binary operator" );
861 // retrieve arguments
862 std::shared_ptr
<ExpressionNode
> pSecondArg( std::move(rNodeStack
.top()) );
864 std::shared_ptr
<ExpressionNode
> pFirstArg( std::move(rNodeStack
.top()) );
867 // create combined ExpressionNode
868 auto pNode
= std::make_shared
<BinaryFunctionExpression
>( meFunct
, pFirstArg
, pSecondArg
);
869 // check for constness
870 if( pFirstArg
->isConstant() && pSecondArg
->isConstant() ) // call the operator() at pNode, store result in constant value ExpressionNode.
871 rNodeStack
.push( std::make_shared
<ConstantValueExpression
>( (*pNode
)() ) );
872 else // push complex node, that calcs the value on demand
873 rNodeStack
.push( pNode
);
879 ParserContextSharedPtr mxContext
;
883 explicit IfFunctor( const ParserContextSharedPtr
& rContext
) :
884 mxContext( rContext
)
887 void operator()( StringIteratorT
, StringIteratorT
) const
889 ParserContext::OperandStack
& rNodeStack( mxContext
->maOperandStack
);
891 if( rNodeStack
.size() < 3 )
892 throw ParseError( "Not enough arguments for ternary operator" );
894 // retrieve arguments
895 std::shared_ptr
<ExpressionNode
> pThirdArg( std::move(rNodeStack
.top()) );
897 std::shared_ptr
<ExpressionNode
> pSecondArg( std::move(rNodeStack
.top()) );
899 std::shared_ptr
<ExpressionNode
> pFirstArg( std::move(rNodeStack
.top()) );
902 // create combined ExpressionNode
903 auto pNode
= std::make_shared
<IfExpression
>( pFirstArg
, pSecondArg
, pThirdArg
);
904 // check for constness
905 if( pFirstArg
->isConstant() && pSecondArg
->isConstant() && pThirdArg
->isConstant() )
906 rNodeStack
.push( std::make_shared
<ConstantValueExpression
>( (*pNode
)() ) ); // call the operator() at pNode, store result in constant value ExpressionNode.
908 rNodeStack
.push( pNode
); // push complex node, that calcs the value on demand
912 // Workaround for MSVC compiler anomaly (stack trashing)
914 // The default ureal_parser_policies implementation of parse_exp
915 // triggers a really weird error in MSVC7 (Version 13.00.9466), in
916 // that the real_parser_impl::parse_main() call of parse_exp()
917 // overwrites the frame pointer _on the stack_ (EBP of the calling
918 // function gets overwritten while lying on the stack).
920 // For the time being, our parser thus can only read the 1.0E10
921 // notation, not the 1.0e10 one.
923 // TODO(F1): Also handle the 1.0e10 case here.
924 template< typename T
> struct custom_real_parser_policies
: public ::boost::spirit::classic::ureal_parser_policies
<T
>
926 template< typename ScannerT
>
927 static typename ::boost::spirit::classic::parser_result
< ::boost::spirit::classic::chlit
<>, ScannerT
>::type
928 parse_exp(ScannerT
& scan
)
930 // as_lower_d somehow breaks MSVC7
931 return ::boost::spirit::classic::ch_p('E').parse(scan
);
935 /* This class implements the following grammar (more or
936 less literally written down below, only slightly
937 obfuscated by the parser actions):
939 identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
941 function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
946 function '(' additive_expression ')' |
947 '(' additive_expression ')'
950 '-' basic_expression |
953 multiplicative_expression =
954 unary_expression ( ( '*' unary_expression )* |
955 ( '/' unary_expression )* )
957 additive_expression =
958 multiplicative_expression ( ( '+' multiplicative_expression )* |
959 ( '-' multiplicative_expression )* )
963 class ExpressionGrammar
: public ::boost::spirit::classic::grammar
< ExpressionGrammar
>
966 /** Create an arithmetic expression grammar
968 @param rParserContext
969 Contains context info for the parser
971 explicit ExpressionGrammar( const ParserContextSharedPtr
& rParserContext
) :
972 mpParserContext( rParserContext
)
976 template< typename ScannerT
> class definition
979 // grammar definition
980 explicit definition( const ExpressionGrammar
& self
)
982 using ::boost::spirit::classic::str_p
;
983 using ::boost::spirit::classic::range_p
;
984 using ::boost::spirit::classic::lexeme_d
;
985 using ::boost::spirit::classic::real_parser
;
988 str_p( "pi" )[ EnumFunctor(ExpressionFunct::EnumPi
, self
.getContext() ) ]
989 | str_p( "left" )[ EnumFunctor(ExpressionFunct::EnumLeft
, self
.getContext() ) ]
990 | str_p( "top" )[ EnumFunctor(ExpressionFunct::EnumTop
, self
.getContext() ) ]
991 | str_p( "right" )[ EnumFunctor(ExpressionFunct::EnumRight
, self
.getContext() ) ]
992 | str_p( "bottom" )[ EnumFunctor(ExpressionFunct::EnumBottom
, self
.getContext() ) ]
993 | str_p( "xstretch" )[ EnumFunctor(ExpressionFunct::EnumXStretch
, self
.getContext() ) ]
994 | str_p( "ystretch" )[ EnumFunctor(ExpressionFunct::EnumYStretch
, self
.getContext() ) ]
995 | str_p( "hasstroke" )[ EnumFunctor(ExpressionFunct::EnumHasStroke
, self
.getContext() ) ]
996 | str_p( "hasfill" )[ EnumFunctor(ExpressionFunct::EnumHasFill
, self
.getContext() ) ]
997 | str_p( "width" )[ EnumFunctor(ExpressionFunct::EnumWidth
, self
.getContext() ) ]
998 | str_p( "height" )[ EnumFunctor(ExpressionFunct::EnumHeight
, self
.getContext() ) ]
999 | str_p( "logwidth" )[ EnumFunctor(ExpressionFunct::EnumLogWidth
, self
.getContext() ) ]
1000 | str_p( "logheight" )[ EnumFunctor(ExpressionFunct::EnumLogHeight
, self
.getContext() ) ]
1004 (str_p( "abs" ) >> '(' >> additiveExpression
>> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryAbs
, self
.getContext()) ]
1005 | (str_p( "sqrt" ) >> '(' >> additiveExpression
>> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnarySqrt
, self
.getContext()) ]
1006 | (str_p( "sin" ) >> '(' >> additiveExpression
>> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnarySin
, self
.getContext()) ]
1007 | (str_p( "cos" ) >> '(' >> additiveExpression
>> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryCos
, self
.getContext()) ]
1008 | (str_p( "tan" ) >> '(' >> additiveExpression
>> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryTan
, self
.getContext()) ]
1009 | (str_p( "atan" ) >> '(' >> additiveExpression
>> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryAtan
, self
.getContext()) ]
1013 (str_p( "min" ) >> '(' >> additiveExpression
>> ',' >> additiveExpression
>> ')' )[ BinaryFunctionFunctor( ExpressionFunct::BinaryMin
, self
.getContext()) ]
1014 | (str_p( "max" ) >> '(' >> additiveExpression
>> ',' >> additiveExpression
>> ')' )[ BinaryFunctionFunctor( ExpressionFunct::BinaryMax
, self
.getContext()) ]
1015 | (str_p( "atan2") >> '(' >> additiveExpression
>> ',' >> additiveExpression
>> ')' )[ BinaryFunctionFunctor( ExpressionFunct::BinaryAtan2
,self
.getContext()) ]
1019 (str_p( "if" ) >> '(' >> additiveExpression
>> ',' >> additiveExpression
>> ',' >> additiveExpression
>> ')' )[ IfFunctor( self
.getContext() ) ]
1023 lexeme_d
[ +( range_p('a','z') | range_p('A','Z') | range_p('0','9') ) ];
1026 (str_p( "?" ) >> funcRef_decl
)[ EnumFunctor( ExpressionFunct::EnumEquation
, self
.getContext() ) ];
1029 lexeme_d
[ +( range_p('0','9') ) ];
1032 (str_p( "$" ) >> modRef_decl
)[ EnumFunctor( ExpressionFunct::EnumAdjustment
, self
.getContext() ) ];
1035 real_parser
<double, custom_real_parser_policies
<double> >()[ DoubleConstantFunctor(self
.getContext()) ]
1042 | '(' >> additiveExpression
>> ')'
1046 ('-' >> basicExpression
)[ UnaryFunctionFunctor( ExpressionFunct::UnaryNeg
, self
.getContext()) ]
1050 multiplicativeExpression
=
1052 >> *( ('*' >> unaryExpression
)[ BinaryFunctionFunctor( ExpressionFunct::BinaryMul
, self
.getContext()) ]
1053 | ('/' >> unaryExpression
)[ BinaryFunctionFunctor( ExpressionFunct::BinaryDiv
, self
.getContext()) ]
1057 additiveExpression
=
1058 multiplicativeExpression
1059 >> *( ('+' >> multiplicativeExpression
)[ BinaryFunctionFunctor( ExpressionFunct::BinaryPlus
, self
.getContext()) ]
1060 | ('-' >> multiplicativeExpression
)[ BinaryFunctionFunctor( ExpressionFunct::BinaryMinus
, self
.getContext()) ]
1064 BOOST_SPIRIT_DEBUG_RULE(additiveExpression
);
1065 BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression
);
1066 BOOST_SPIRIT_DEBUG_RULE(unaryExpression
);
1067 BOOST_SPIRIT_DEBUG_RULE(basicExpression
);
1068 BOOST_SPIRIT_DEBUG_RULE(unaryFunction
);
1069 BOOST_SPIRIT_DEBUG_RULE(binaryFunction
);
1070 BOOST_SPIRIT_DEBUG_RULE(ternaryFunction
);
1071 BOOST_SPIRIT_DEBUG_RULE(identifier
);
1074 const ::boost::spirit::classic::rule
< ScannerT
>& start() const
1076 return additiveExpression
;
1080 // the constituents of the Spirit arithmetic expression grammar.
1081 // For the sake of readability, without 'ma' prefix.
1082 ::boost::spirit::classic::rule
< ScannerT
> additiveExpression
;
1083 ::boost::spirit::classic::rule
< ScannerT
> multiplicativeExpression
;
1084 ::boost::spirit::classic::rule
< ScannerT
> unaryExpression
;
1085 ::boost::spirit::classic::rule
< ScannerT
> basicExpression
;
1086 ::boost::spirit::classic::rule
< ScannerT
> unaryFunction
;
1087 ::boost::spirit::classic::rule
< ScannerT
> binaryFunction
;
1088 ::boost::spirit::classic::rule
< ScannerT
> ternaryFunction
;
1089 ::boost::spirit::classic::rule
< ScannerT
> funcRef_decl
;
1090 ::boost::spirit::classic::rule
< ScannerT
> functionReference
;
1091 ::boost::spirit::classic::rule
< ScannerT
> modRef_decl
;
1092 ::boost::spirit::classic::rule
< ScannerT
> modifierReference
;
1093 ::boost::spirit::classic::rule
< ScannerT
> identifier
;
1096 const ParserContextSharedPtr
& getContext() const
1098 return mpParserContext
;
1102 ParserContextSharedPtr mpParserContext
; // might get modified during parsing
1105 const ParserContextSharedPtr
& getParserContext()
1107 static ParserContextSharedPtr lcl_parserContext
= std::make_shared
<ParserContext
>();
1109 // clear node stack (since we reuse the static object, that's
1110 // the whole point here)
1111 while( !lcl_parserContext
->maOperandStack
.empty() )
1112 lcl_parserContext
->maOperandStack
.pop();
1114 return lcl_parserContext
;
1119 namespace EnhancedCustomShape
{
1122 std::shared_ptr
<ExpressionNode
> const & FunctionParser::parseFunction( const OUString
& rFunction
, const EnhancedCustomShape2d
& rCustoShape
)
1124 // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
1125 // gives better conversion robustness here (we might want to map space
1126 // etc. to ASCII space here)
1127 const OString
& rAsciiFunction(
1128 OUStringToOString( rFunction
, RTL_TEXTENCODING_ASCII_US
) );
1130 StringIteratorT
aStart( rAsciiFunction
.getStr() );
1131 StringIteratorT
aEnd( rAsciiFunction
.getStr()+rAsciiFunction
.getLength() );
1133 // static parser context, because the actual
1134 // Spirit parser is also a static object
1135 ParserContextSharedPtr pContext
= getParserContext();
1136 pContext
->mpCustoShape
= &rCustoShape
;
1138 ExpressionGrammar
aExpressionGrammer( pContext
);
1139 const ::boost::spirit::classic::parse_info
<StringIteratorT
> aParseInfo(
1140 ::boost::spirit::classic::parse( aStart
,
1142 aExpressionGrammer
>> ::boost::spirit::classic::end_p
,
1143 ::boost::spirit::classic::space_p
) );
1145 // input fully congested by the parser?
1146 if( !aParseInfo
.full
)
1147 throw ParseError( "EnhancedCustomShapeFunctionParser::parseFunction(): string not fully parseable" );
1149 // parser's state stack now must contain exactly _one_ ExpressionNode,
1150 // which represents our formula.
1151 if( pContext
->maOperandStack
.size() != 1 )
1152 throw ParseError( "EnhancedCustomShapeFunctionParser::parseFunction(): incomplete or empty expression" );
1155 return pContext
->maOperandStack
.top();
1160 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */