summaryrefslogtreecommitdiffstats
path: root/basic/source/comp/exprnode.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--basic/source/comp/exprnode.cxx480
1 files changed, 480 insertions, 0 deletions
diff --git a/basic/source/comp/exprnode.cxx b/basic/source/comp/exprnode.cxx
new file mode 100644
index 000000000..ade1d5832
--- /dev/null
+++ b/basic/source/comp/exprnode.cxx
@@ -0,0 +1,480 @@
+/* -*- 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 <cmath>
+
+#include <o3tl/temporary.hxx>
+#include <parser.hxx>
+#include <expr.hxx>
+#include <tools/long.hxx>
+
+#include <basic/sberrors.hxx>
+
+#include <rtl/math.hxx>
+#include <utility>
+
+SbiExprNode::SbiExprNode( std::unique_ptr<SbiExprNode> l, SbiToken t, std::unique_ptr<SbiExprNode> r ) :
+ pLeft(std::move(l)),
+ pRight(std::move(r)),
+ pWithParent(nullptr),
+ eNodeType(SbxNODE),
+ eType(SbxVARIANT), // Nodes are always Variant
+ eTok(t),
+ bError(false)
+{
+}
+
+SbiExprNode::SbiExprNode( double n, SbxDataType t ):
+ nVal(n),
+ pWithParent(nullptr),
+ eNodeType(SbxNUMVAL),
+ eType(t),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+SbiExprNode::SbiExprNode( OUString aVal ):
+ aStrVal(std::move(aVal)),
+ pWithParent(nullptr),
+ eNodeType(SbxSTRVAL),
+ eType(SbxSTRING),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+SbiExprNode::SbiExprNode( const SbiSymDef& r, SbxDataType t, SbiExprListPtr l ) :
+ pWithParent(nullptr),
+ eNodeType(SbxVARVAL),
+ eTok(NIL),
+ bError(false)
+{
+ eType = ( t == SbxVARIANT ) ? r.GetType() : t;
+ aVar.pDef = const_cast<SbiSymDef*>(&r);
+ aVar.pPar = l.release();
+ aVar.pvMorePar = nullptr;
+ aVar.pNext= nullptr;
+}
+
+// #120061 TypeOf
+SbiExprNode::SbiExprNode( std::unique_ptr<SbiExprNode> l, sal_uInt16 nId ) :
+ nTypeStrId(nId),
+ pLeft(std::move(l)),
+ pWithParent(nullptr),
+ eNodeType(SbxTYPEOF),
+ eType(SbxBOOL),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+// new <type>
+SbiExprNode::SbiExprNode( sal_uInt16 nId ) :
+ nTypeStrId(nId),
+ pWithParent(nullptr),
+ eNodeType(SbxNEW),
+ eType(SbxOBJECT),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+SbiExprNode::SbiExprNode() :
+ pWithParent(nullptr),
+ eNodeType(SbxDUMMY),
+ eType(SbxVARIANT),
+ eTok(NIL),
+ bError(false)
+{
+}
+
+SbiExprNode::~SbiExprNode()
+{
+ if( IsVariable() )
+ {
+ delete aVar.pPar;
+ delete aVar.pNext;
+ delete aVar.pvMorePar;
+ }
+}
+
+SbiSymDef* SbiExprNode::GetVar()
+{
+ if( eNodeType == SbxVARVAL )
+ return aVar.pDef;
+ else
+ return nullptr;
+}
+
+SbiSymDef* SbiExprNode::GetRealVar()
+{
+ SbiExprNode* p = GetRealNode();
+ if( p )
+ return p->GetVar();
+ else
+ return nullptr;
+}
+
+// From 1995-12-18
+SbiExprNode* SbiExprNode::GetRealNode()
+{
+ if( eNodeType == SbxVARVAL )
+ {
+ SbiExprNode* p = this;
+ while( p->aVar.pNext )
+ p = p->aVar.pNext;
+ return p;
+ }
+ else
+ return nullptr;
+}
+
+// This method transform the type, if it fits into the Integer range
+
+void SbiExprNode::ConvertToIntConstIfPossible()
+{
+ if( eNodeType == SbxNUMVAL )
+ {
+ if( eType >= SbxINTEGER && eType <= SbxDOUBLE )
+ {
+ if( nVal >= SbxMININT && nVal <= SbxMAXINT && modf( nVal, &o3tl::temporary(double()) ) == 0 )
+ {
+ eType = SbxINTEGER;
+ }
+ }
+ }
+}
+
+bool SbiExprNode::IsNumber() const
+{
+ return eNodeType == SbxNUMVAL;
+}
+
+bool SbiExprNode::IsVariable() const
+{
+ return eNodeType == SbxVARVAL;
+}
+
+bool SbiExprNode::IsLvalue() const
+{
+ return IsVariable();
+}
+
+// Adjustment of a tree:
+// 1. Constant Folding
+// 2. Type-Adjustment
+// 3. Conversion of the operands into Strings
+// 4. Lifting of the composite- and error-bits
+
+void SbiExprNode::Optimize(SbiParser* pParser)
+{
+ FoldConstants(pParser);
+ CollectBits();
+}
+
+// Lifting of the error-bits
+
+void SbiExprNode::CollectBits()
+{
+ if( pLeft )
+ {
+ pLeft->CollectBits();
+ bError = bError || pLeft->bError;
+ }
+ if( pRight )
+ {
+ pRight->CollectBits();
+ bError = bError || pRight->bError;
+ }
+}
+
+// If a twig can be converted, True will be returned. In this case
+// the result is in the left twig.
+void SbiExprNode::FoldConstants(SbiParser* pParser)
+{
+ if( IsOperand() || eTok == LIKE ) return;
+
+ if (pLeft && !pRight)
+ FoldConstantsUnaryNode(pParser);
+ else if (pLeft && pRight)
+ FoldConstantsBinaryNode(pParser);
+
+ if( eNodeType == SbxNUMVAL )
+ {
+ // Potentially convolve in INTEGER (because of better opcode)?
+ if( eType == SbxSINGLE || eType == SbxDOUBLE )
+ {
+ if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG
+ && !modf( nVal, &o3tl::temporary(double()) ) )
+ eType = SbxLONG;
+ }
+ if( eType == SbxLONG && nVal >= SbxMININT && nVal <= SbxMAXINT )
+ eType = SbxINTEGER;
+ }
+}
+
+void SbiExprNode::FoldConstantsBinaryNode(SbiParser* pParser)
+{
+ pLeft->FoldConstants(pParser);
+ pRight->FoldConstants(pParser);
+ if( !(pLeft->IsConstant() && pRight->IsConstant()
+ && pLeft->eNodeType == pRight->eNodeType) )
+ return;
+
+ CollectBits();
+ if( eTok == CAT )
+ // CAT affiliate also two numbers!
+ eType = SbxSTRING;
+ if( pLeft->eType == SbxSTRING )
+ // No Type Mismatch!
+ eType = SbxSTRING;
+ if( eType == SbxSTRING )
+ {
+ OUString rl( pLeft->GetString() );
+ OUString rr( pRight->GetString() );
+ pLeft.reset();
+ pRight.reset();
+ if( eTok == PLUS || eTok == CAT )
+ {
+ eTok = CAT;
+ // Linking:
+ aStrVal = rl;
+ aStrVal += rr;
+ eType = SbxSTRING;
+ eNodeType = SbxSTRVAL;
+ }
+ else
+ {
+ eType = SbxBOOL;
+ eNodeType = SbxNUMVAL;
+ int eRes = rr.compareTo( rl );
+ switch( eTok )
+ {
+ case EQ:
+ nVal = ( eRes == 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case NE:
+ nVal = ( eRes != 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case LT:
+ nVal = ( eRes > 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case GT:
+ nVal = ( eRes < 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case LE:
+ nVal = ( eRes >= 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ case GE:
+ nVal = ( eRes <= 0 ) ? SbxTRUE : SbxFALSE;
+ break;
+ default:
+ pParser->Error( ERRCODE_BASIC_CONVERSION );
+ bError = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ double nl = pLeft->nVal;
+ double nr = pRight->nVal;
+ // tdf#141201, tdf#147089 - round MOD/IDIV literals to Integer values
+ if (eTok == MOD || eTok == IDIV)
+ {
+ nl = rtl::math::round(nl);
+ nr = rtl::math::round(nr);
+ }
+ tools::Long ll = 0, lr = 0;
+ if( ( eTok >= AND && eTok <= IMP )
+ || eTok == IDIV || eTok == MOD )
+ {
+ // Integer operations
+ bool bErr = false;
+ if( nl > SbxMAXLNG )
+ {
+ bErr = true;
+ nl = SbxMAXLNG;
+ }
+ else if( nl < SbxMINLNG )
+ {
+ bErr = true;
+ nl = SbxMINLNG;
+ }
+ if( nr > SbxMAXLNG )
+ {
+ bErr = true;
+ nr = SbxMAXLNG;
+ }
+ else if( nr < SbxMINLNG )
+ {
+ bErr = true;
+ nr = SbxMINLNG;
+ }
+ ll = static_cast<tools::Long>(nl); lr = static_cast<tools::Long>(nr);
+ if( bErr )
+ {
+ pParser->Error( ERRCODE_BASIC_MATH_OVERFLOW );
+ bError = true;
+ }
+ }
+ bool bBothInt = ( pLeft->eType < SbxSINGLE
+ && pRight->eType < SbxSINGLE );
+ pLeft.reset();
+ pRight.reset();
+ nVal = 0;
+ eType = SbxDOUBLE;
+ eNodeType = SbxNUMVAL;
+ bool bCheckType = false;
+ switch( eTok )
+ {
+ case EXPON:
+ nVal = pow( nl, nr ); break;
+ case MUL:
+ bCheckType = true;
+ nVal = nl * nr; break;
+ case DIV:
+ if( !nr )
+ {
+ pParser->Error( ERRCODE_BASIC_ZERODIV ); nVal = HUGE_VAL;
+ bError = true;
+ } else nVal = nl / nr;
+ break;
+ case PLUS:
+ bCheckType = true;
+ nVal = nl + nr; break;
+ case MINUS:
+ bCheckType = true;
+ nVal = nl - nr; break;
+ case EQ:
+ nVal = ( nl == nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case NE:
+ nVal = ( nl != nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case LT:
+ nVal = ( nl < nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case GT:
+ nVal = ( nl > nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case LE:
+ nVal = ( nl <= nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case GE:
+ nVal = ( nl >= nr ) ? SbxTRUE : SbxFALSE;
+ eType = SbxBOOL; break;
+ case IDIV:
+ if( !lr )
+ {
+ pParser->Error( ERRCODE_BASIC_ZERODIV ); nVal = HUGE_VAL;
+ bError = true;
+ } else nVal = ll / lr;
+ eType = SbxLONG; break;
+ case MOD:
+ if( !lr )
+ {
+ pParser->Error( ERRCODE_BASIC_ZERODIV ); nVal = HUGE_VAL;
+ bError = true;
+ } else nVal = ll - lr * (ll/lr);
+ eType = SbxLONG; break;
+ case AND:
+ nVal = static_cast<double>( ll & lr ); eType = SbxLONG; break;
+ case OR:
+ nVal = static_cast<double>( ll | lr ); eType = SbxLONG; break;
+ case XOR:
+ nVal = static_cast<double>( ll ^ lr ); eType = SbxLONG; break;
+ case EQV:
+ nVal = static_cast<double>( ~ll ^ lr ); eType = SbxLONG; break;
+ case IMP:
+ nVal = static_cast<double>( ~ll | lr ); eType = SbxLONG; break;
+ default: break;
+ }
+
+ if( !std::isfinite( nVal ) )
+ pParser->Error( ERRCODE_BASIC_MATH_OVERFLOW );
+
+ // Recover the data type to kill rounding error
+ if( bCheckType && bBothInt
+ && nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
+ {
+ // Decimal place away
+ tools::Long n = static_cast<tools::Long>(nVal);
+ nVal = n;
+ eType = ( n >= SbxMININT && n <= SbxMAXINT )
+ ? SbxINTEGER : SbxLONG;
+ }
+ }
+
+}
+void SbiExprNode::FoldConstantsUnaryNode(SbiParser* pParser)
+{
+ pLeft->FoldConstants(pParser);
+ if (pLeft->IsNumber())
+ {
+ nVal = pLeft->nVal;
+ pLeft.reset();
+ eType = SbxDOUBLE;
+ eNodeType = SbxNUMVAL;
+ switch( eTok )
+ {
+ case NEG:
+ nVal = -nVal; break;
+ case NOT: {
+ // Integer operation!
+ bool bErr = false;
+ if( nVal > SbxMAXLNG )
+ {
+ bErr = true;
+ nVal = SbxMAXLNG;
+ }
+ else if( nVal < SbxMINLNG )
+ {
+ bErr = true;
+ nVal = SbxMINLNG;
+ }
+ if( bErr )
+ {
+ pParser->Error( ERRCODE_BASIC_MATH_OVERFLOW );
+ bError = true;
+ }
+ nVal = static_cast<double>(~static_cast<tools::Long>(nVal));
+ eType = SbxLONG;
+ } break;
+ default: break;
+ }
+ }
+ if( eNodeType == SbxNUMVAL )
+ {
+ // Potentially convolve in INTEGER (because of better opcode)?
+ if( eType == SbxSINGLE || eType == SbxDOUBLE )
+ {
+ if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG
+ && !modf( nVal, &o3tl::temporary(double()) ) )
+ eType = SbxLONG;
+ }
+ if( eType == SbxLONG && nVal >= SbxMININT && nVal <= SbxMAXINT )
+ eType = SbxINTEGER;
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */