diff options
Diffstat (limited to 'basic/source/comp/exprnode.cxx')
-rw-r--r-- | basic/source/comp/exprnode.cxx | 480 |
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 0000000000..ade1d5832b --- /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: */ |