527741d528
Change-Id: I97c09d82699621d5a43a525b5f2cf75ff5d131c0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147353 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
391 lines
11 KiB
C++
391 lines
11 KiB
C++
/* -*- 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 <sal/config.h>
|
|
|
|
#include <basic/sbx.hxx>
|
|
#include <basic/sberrors.hxx>
|
|
#include <rtl/character.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
|
|
#include <basiccharclass.hxx>
|
|
|
|
static SbxVariableRef Element
|
|
( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf,
|
|
SbxClassType, bool bCompatible );
|
|
|
|
static const sal_Unicode* SkipWhitespace( const sal_Unicode* p )
|
|
{
|
|
while( BasicCharClass::isWhitespace(*p) )
|
|
p++;
|
|
return p;
|
|
}
|
|
|
|
// Scanning of a symbol. The symbol were inserted in rSym, the return value
|
|
// is the new scan position. The symbol is at errors empty.
|
|
|
|
static const sal_Unicode* Symbol( const sal_Unicode* p, OUString& rSym, bool bCompatible )
|
|
{
|
|
sal_uInt16 nLen = 0;
|
|
// Did we have a nonstandard symbol?
|
|
if( *p == '[' )
|
|
{
|
|
rSym = ++p;
|
|
while( *p && *p != ']' )
|
|
{
|
|
p++;
|
|
nLen++;
|
|
}
|
|
p++;
|
|
}
|
|
else
|
|
{
|
|
// A symbol had to begin with an alphabetic character or an underline
|
|
if( !BasicCharClass::isAlpha( *p, bCompatible ) && *p != '_' )
|
|
{
|
|
SbxBase::SetError( ERRCODE_BASIC_SYNTAX );
|
|
}
|
|
else
|
|
{
|
|
rSym = p;
|
|
// The it can contain alphabetic characters, numbers or underlines
|
|
while( *p && (BasicCharClass::isAlphaNumeric( *p, bCompatible ) || *p == '_') )
|
|
{
|
|
p++;
|
|
nLen++;
|
|
}
|
|
// Ignore standard BASIC suffixes
|
|
if( *p && (*p == '%' || *p == '&' || *p == '!' || *p == '#' || *p == '$' ) )
|
|
{
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
rSym = rSym.copy( 0, nLen );
|
|
return p;
|
|
}
|
|
|
|
// Qualified name. Element.Element...
|
|
|
|
static SbxVariableRef QualifiedName
|
|
( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, SbxClassType t, bool bCompatible )
|
|
{
|
|
|
|
SbxVariableRef refVar;
|
|
const sal_Unicode* p = SkipWhitespace( *ppBuf );
|
|
if( BasicCharClass::isAlpha( *p, bCompatible ) || *p == '_' || *p == '[' )
|
|
{
|
|
// Read in the element
|
|
refVar = Element( pObj, pGbl, &p, t, bCompatible );
|
|
while( refVar.is() && (*p == '.' || *p == '!') )
|
|
{
|
|
// It follows still an objectelement. The current element
|
|
// had to be a SBX-Object or had to deliver such an object!
|
|
pObj = dynamic_cast<SbxObject*>( refVar.get() );
|
|
if( !pObj )
|
|
// Then it had to deliver an object
|
|
pObj = dynamic_cast<SbxObject*>( refVar->GetObject() );
|
|
refVar.clear();
|
|
if( !pObj )
|
|
break;
|
|
p++;
|
|
// And the next element please
|
|
refVar = Element( pObj, pGbl, &p, t, bCompatible );
|
|
}
|
|
}
|
|
else
|
|
SbxBase::SetError( ERRCODE_BASIC_SYNTAX );
|
|
*ppBuf = p;
|
|
return refVar;
|
|
}
|
|
|
|
// Read in of an operand. This could be a number, a string or
|
|
// a function (with optional parameters).
|
|
|
|
static SbxVariableRef Operand
|
|
( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bVar, bool bCompatible )
|
|
{
|
|
SbxVariableRef refVar( new SbxVariable );
|
|
const sal_Unicode* p = SkipWhitespace( *ppBuf );
|
|
if( !bVar && ( rtl::isAsciiDigit( *p )
|
|
|| ( *p == '.' && rtl::isAsciiDigit( *( p+1 ) ) )
|
|
|| *p == '-'
|
|
|| *p == '&' ) )
|
|
{
|
|
// A number could be scanned in directly!
|
|
sal_uInt16 nLen;
|
|
if( !refVar->Scan( OUString( p ), &nLen ) )
|
|
{
|
|
refVar.clear();
|
|
}
|
|
else
|
|
{
|
|
p += nLen;
|
|
}
|
|
}
|
|
else if( !bVar && *p == '"' )
|
|
{
|
|
// A string
|
|
OUStringBuffer aString;
|
|
p++;
|
|
for( ;; )
|
|
{
|
|
// This is perhaps an error
|
|
if( !*p )
|
|
{
|
|
return nullptr;
|
|
}
|
|
// Double quotes are OK
|
|
if( *p == '"' && (*++p) != '"' )
|
|
{
|
|
break;
|
|
}
|
|
aString.append(*p++);
|
|
}
|
|
refVar->PutString( aString.makeStringAndClear() );
|
|
}
|
|
else
|
|
{
|
|
refVar = QualifiedName( pObj, pGbl, &p, SbxClassType::DontCare, bCompatible );
|
|
}
|
|
*ppBuf = p;
|
|
return refVar;
|
|
}
|
|
|
|
// Read in of a simple term. The operands +, -, * and /
|
|
// are supported.
|
|
|
|
static SbxVariableRef MulDiv( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible )
|
|
{
|
|
const sal_Unicode* p = *ppBuf;
|
|
SbxVariableRef refVar( Operand( pObj, pGbl, &p, false, bCompatible ) );
|
|
p = SkipWhitespace( p );
|
|
while( refVar.is() && ( *p == '*' || *p == '/' ) )
|
|
{
|
|
sal_Unicode cOp = *p++;
|
|
SbxVariableRef refVar2( Operand( pObj, pGbl, &p, false, bCompatible ) );
|
|
if( refVar2.is() )
|
|
{
|
|
// temporary variable!
|
|
SbxVariable* pVar = refVar.get();
|
|
pVar = new SbxVariable( *pVar );
|
|
refVar = pVar;
|
|
if( cOp == '*' )
|
|
*refVar *= *refVar2;
|
|
else
|
|
*refVar /= *refVar2;
|
|
}
|
|
else
|
|
{
|
|
refVar.clear();
|
|
break;
|
|
}
|
|
}
|
|
*ppBuf = p;
|
|
return refVar;
|
|
}
|
|
|
|
static SbxVariableRef PlusMinus( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible )
|
|
{
|
|
const sal_Unicode* p = *ppBuf;
|
|
SbxVariableRef refVar( MulDiv( pObj, pGbl, &p, bCompatible ) );
|
|
p = SkipWhitespace( p );
|
|
while( refVar.is() && ( *p == '+' || *p == '-' ) )
|
|
{
|
|
sal_Unicode cOp = *p++;
|
|
SbxVariableRef refVar2( MulDiv( pObj, pGbl, &p, bCompatible ) );
|
|
if( refVar2.is() )
|
|
{
|
|
// temporary Variable!
|
|
SbxVariable* pVar = refVar.get();
|
|
pVar = new SbxVariable( *pVar );
|
|
refVar = pVar;
|
|
if( cOp == '+' )
|
|
*refVar += *refVar2;
|
|
else
|
|
*refVar -= *refVar2;
|
|
}
|
|
else
|
|
{
|
|
refVar.clear();
|
|
break;
|
|
}
|
|
}
|
|
*ppBuf = p;
|
|
return refVar;
|
|
}
|
|
|
|
static SbxVariableRef Assign( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible )
|
|
{
|
|
const sal_Unicode* p = *ppBuf;
|
|
SbxVariableRef refVar( Operand( pObj, pGbl, &p, true, bCompatible ) );
|
|
p = SkipWhitespace( p );
|
|
if( refVar.is() )
|
|
{
|
|
if( *p == '=' )
|
|
{
|
|
// Assign only onto properties!
|
|
if( refVar->GetClass() != SbxClassType::Property )
|
|
{
|
|
SbxBase::SetError( ERRCODE_BASIC_BAD_ACTION );
|
|
refVar.clear();
|
|
}
|
|
else
|
|
{
|
|
p++;
|
|
SbxVariableRef refVar2( PlusMinus( pObj, pGbl, &p, bCompatible ) );
|
|
if( refVar2.is() )
|
|
{
|
|
SbxVariable* pVar = refVar.get();
|
|
SbxVariable* pVar2 = refVar2.get();
|
|
*pVar = *pVar2;
|
|
pVar->SetParameters( nullptr );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
// Simple call: once activating
|
|
refVar->Broadcast( SfxHintId::BasicDataWanted );
|
|
}
|
|
*ppBuf = p;
|
|
return refVar;
|
|
}
|
|
|
|
// Read in of an element. This is a symbol, optional followed
|
|
// by a parameter list. The symbol will be searched in the
|
|
// specified object and the parameter list will be attached if necessary.
|
|
|
|
static SbxVariableRef Element
|
|
( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf,
|
|
SbxClassType t, bool bCompatible )
|
|
{
|
|
OUString aSym;
|
|
const sal_Unicode* p = Symbol( *ppBuf, aSym, bCompatible );
|
|
SbxVariableRef refVar;
|
|
if( !aSym.isEmpty() )
|
|
{
|
|
SbxFlagBits nOld = pObj->GetFlags();
|
|
if( pObj == pGbl )
|
|
{
|
|
pObj->SetFlag( SbxFlagBits::GlobalSearch );
|
|
}
|
|
refVar = pObj->Find( aSym, t );
|
|
pObj->SetFlags( nOld );
|
|
if( refVar.is() )
|
|
{
|
|
refVar->SetParameters( nullptr );
|
|
// Follow still parameter?
|
|
p = SkipWhitespace( p );
|
|
if( *p == '(' )
|
|
{
|
|
p++;
|
|
auto refPar = tools::make_ref<SbxArray>();
|
|
sal_uInt32 nArg = 0;
|
|
// We are once relaxed and accept as well
|
|
// the line- or command end as delimiter
|
|
// Search parameter always global!
|
|
while( *p && *p != ')' && *p != ']' )
|
|
{
|
|
SbxVariableRef refArg = PlusMinus( pGbl, pGbl, &p, bCompatible );
|
|
if( !refArg.is() )
|
|
{
|
|
// Error during the parsing
|
|
refVar.clear(); break;
|
|
}
|
|
else
|
|
{
|
|
// One copies the parameter, so that
|
|
// one have the current status (triggers also
|
|
// the call per access)
|
|
refPar->Put(new SbxVariable(*refArg), ++nArg);
|
|
}
|
|
p = SkipWhitespace( p );
|
|
if( *p == ',' )
|
|
p++;
|
|
}
|
|
if( *p == ')' )
|
|
p++;
|
|
if( refVar.is() )
|
|
refVar->SetParameters( refPar.get() );
|
|
}
|
|
}
|
|
else
|
|
SbxBase::SetError( ERRCODE_BASIC_NO_METHOD, aSym );
|
|
}
|
|
*ppBuf = p;
|
|
return refVar;
|
|
}
|
|
|
|
// Mainroutine
|
|
|
|
SbxVariable* SbxObject::Execute( const OUString& rTxt )
|
|
{
|
|
SbxVariableRef pVar;
|
|
const sal_Unicode* p = rTxt.getStr();
|
|
for( ;; )
|
|
{
|
|
p = SkipWhitespace( p );
|
|
if( !*p )
|
|
{
|
|
break;
|
|
}
|
|
if( *p++ != '[' )
|
|
{
|
|
SetError( ERRCODE_BASIC_SYNTAX ); break;
|
|
}
|
|
pVar = Assign( this, this, &p, IsOptionCompatible() );
|
|
if( !pVar.is() )
|
|
{
|
|
break;
|
|
}
|
|
p = SkipWhitespace( p );
|
|
if( *p++ != ']' )
|
|
{
|
|
SetError( ERRCODE_BASIC_SYNTAX ); break;
|
|
}
|
|
}
|
|
return pVar.get();
|
|
}
|
|
|
|
SbxVariable* SbxObject::FindQualified( const OUString& rName, SbxClassType t )
|
|
{
|
|
SbxVariableRef pVar;
|
|
const sal_Unicode* p = rName.getStr();
|
|
p = SkipWhitespace( p );
|
|
if( !*p )
|
|
{
|
|
return nullptr;
|
|
}
|
|
pVar = QualifiedName( this, this, &p, t, IsOptionCompatible() );
|
|
p = SkipWhitespace( p );
|
|
if( *p )
|
|
{
|
|
SetError( ERRCODE_BASIC_SYNTAX );
|
|
}
|
|
return pVar.get();
|
|
}
|
|
|
|
bool SbxObject::IsOptionCompatible() const
|
|
{
|
|
if (const SbxObject* pObj = GetParent())
|
|
return pObj->IsOptionCompatible();
|
|
return false;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|