xref: /trunk/main/basic/source/sbx/sbxexec.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basic.hxx"
30 #include <tools/errcode.hxx>
31 #ifndef _APP_HXX //autogen
32 #include <vcl/svapp.hxx>
33 #endif
34 #include <basic/sbx.hxx>
35 
36 
37 class SbxSimpleCharClass
38 {
39 public:
40     sal_Bool isAlpha( sal_Unicode c ) const
41     {
42         sal_Bool bRet = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
43         return bRet;
44     }
45 
46     sal_Bool isDigit( sal_Unicode c ) const
47     {
48         sal_Bool bRet = (c >= '0' && c <= '9');
49         return bRet;
50     }
51 
52     sal_Bool isAlphaNumeric( sal_Unicode c ) const
53     {
54         sal_Bool bRet = isDigit( c ) || isAlpha( c );
55         return bRet;
56     }
57 };
58 
59 
60 static SbxVariable* Element
61     ( SbxObject* pObj, SbxObject* pGbl, const xub_Unicode** ppBuf,
62       SbxClassType, const SbxSimpleCharClass& rCharClass );
63 
64 static const xub_Unicode* SkipWhitespace( const xub_Unicode* p )
65 {
66     while( *p && ( *p == ' ' || *p == '\t' ) )
67         p++;
68     return p;
69 }
70 
71 // Scannen eines Symbol. Das Symbol wird in rSym eingetragen, der Returnwert
72 // ist die neue Scanposition. Bei Fehlern ist das Symbol leer.
73 
74 static const xub_Unicode* Symbol( const xub_Unicode* p, XubString& rSym, const SbxSimpleCharClass& rCharClass )
75 {
76     sal_uInt16 nLen = 0;
77     // Haben wir ein Sondersymbol?
78     if( *p == '[' )
79     {
80         rSym = ++p;
81         while( *p && *p != ']' )
82             p++, nLen++;
83         p++;
84     }
85     else
86     {
87         // Ein Symbol muss mit einem Buchstaben oder einem Underline beginnen
88         if( !rCharClass.isAlpha( *p ) && *p != '_' )
89             SbxBase::SetError( SbxERR_SYNTAX );
90         else
91         {
92             rSym = p;
93             // Dann darf es Buchstaben, Zahlen oder Underlines enthalten
94             while( *p && (rCharClass.isAlphaNumeric( *p ) || *p == '_') )
95                 p++, nLen++;
96             // BASIC-Standard-Suffixe werden ignoriert
97             if( *p && (*p == '%' || *p == '&' || *p == '!' || *p == '#' || *p == '$' ) )
98                 p++;
99         }
100     }
101     rSym.Erase( nLen );
102     return p;
103 }
104 
105 // Qualifizierter Name. Element.Element....
106 
107 static SbxVariable* QualifiedName
108     ( SbxObject* pObj, SbxObject* pGbl, const xub_Unicode** ppBuf, SbxClassType t )
109 {
110     static SbxSimpleCharClass aCharClass;
111 
112     SbxVariableRef refVar;
113     const xub_Unicode* p = SkipWhitespace( *ppBuf );
114     if( aCharClass.isAlpha( *p ) || *p == '_' || *p == '[' )
115     {
116         // Element einlesen
117         refVar = Element( pObj, pGbl, &p, t, aCharClass );
118         while( refVar.Is() && (*p == '.' || *p == '!') )
119         {
120             // Es folgt noch ein Objektelement. Das aktuelle Element
121             // muss also ein SBX-Objekt sein oder liefern!
122             pObj = PTR_CAST(SbxObject,(SbxVariable*) refVar);
123             if( !pObj )
124                 // Dann muss es ein Objekt liefern
125                 pObj = PTR_CAST(SbxObject,refVar->GetObject());
126             refVar.Clear();
127             if( !pObj )
128                 break;
129             p++;
130             // Und das naechste Element bitte
131             refVar = Element( pObj, pGbl, &p, t, aCharClass );
132         }
133     }
134     else
135         SbxBase::SetError( SbxERR_SYNTAX );
136     *ppBuf = p;
137     if( refVar.Is() )
138         refVar->AddRef();
139     return refVar;
140 }
141 
142 // Einlesen eines Operanden. Dies kann eine Zahl, ein String oder
143 // eine Funktion (mit optionalen Parametern) sein.
144 
145 static SbxVariable* Operand
146     ( SbxObject* pObj, SbxObject* pGbl, const xub_Unicode** ppBuf, sal_Bool bVar )
147 {
148     static SbxSimpleCharClass aCharClass;
149 
150     SbxVariableRef refVar( new SbxVariable );
151     const xub_Unicode* p = SkipWhitespace( *ppBuf );
152     if( !bVar && ( aCharClass.isDigit( *p )
153      || ( *p == '.' && aCharClass.isDigit( *( p+1 ) ) )
154      || *p == '-'
155      || *p == '&' ) )
156     {
157         // Eine Zahl kann direkt eingescant werden!
158         sal_uInt16 nLen;
159         if( !refVar->Scan( XubString( p ), &nLen ) )
160             refVar.Clear();
161         else
162             p += nLen;
163     }
164     else if( !bVar && *p == '"' )
165     {
166         // Ein String
167         XubString aString;
168         p++;
169         for( ;; )
170         {
171             // Das ist wohl ein Fehler
172             if( !*p )
173                 return NULL;
174             // Doppelte Quotes sind OK
175             if( *p == '"' )
176                 if( *++p != '"' )
177                     break;
178             aString += *p++;
179         }
180         refVar->PutString( aString );
181     }
182     else
183         refVar = QualifiedName( pObj, pGbl, &p, SbxCLASS_DONTCARE );
184     *ppBuf = p;
185     if( refVar.Is() )
186         refVar->AddRef();
187     return refVar;
188 }
189 
190 // Einlesen einer einfachen Term. Die Operatoren +, -, * und /
191 // werden unterstuetzt.
192 
193 static SbxVariable* MulDiv( SbxObject* pObj, SbxObject* pGbl, const xub_Unicode** ppBuf )
194 {
195     const xub_Unicode* p = *ppBuf;
196     SbxVariableRef refVar( Operand( pObj, pGbl, &p, sal_False ) );
197     p = SkipWhitespace( p );
198     while( refVar.Is() && ( *p == '*' || *p == '/' ) )
199     {
200         xub_Unicode cOp = *p++;
201         SbxVariableRef refVar2( Operand( pObj, pGbl, &p, sal_False ) );
202         if( refVar2.Is() )
203         {
204             // temporaere Variable!
205             SbxVariable* pVar = refVar;
206             pVar = new SbxVariable( *pVar );
207             refVar = pVar;
208             if( cOp == '*' )
209                 *refVar *= *refVar2;
210             else
211                 *refVar /= *refVar2;
212         }
213         else
214         {
215             refVar.Clear();
216             break;
217         }
218     }
219     *ppBuf = p;
220     if( refVar.Is() )
221         refVar->AddRef();
222     return refVar;
223 }
224 
225 static SbxVariable* PlusMinus( SbxObject* pObj, SbxObject* pGbl, const xub_Unicode** ppBuf )
226 {
227     const xub_Unicode* p = *ppBuf;
228     SbxVariableRef refVar( MulDiv( pObj, pGbl, &p ) );
229     p = SkipWhitespace( p );
230     while( refVar.Is() && ( *p == '+' || *p == '-' ) )
231     {
232         xub_Unicode cOp = *p++;
233         SbxVariableRef refVar2( MulDiv( pObj, pGbl, &p ) );
234         if( refVar2.Is() )
235         {
236             // temporaere Variable!
237             SbxVariable* pVar = refVar;
238             pVar = new SbxVariable( *pVar );
239             refVar = pVar;
240             if( cOp == '+' )
241                 *refVar += *refVar2;
242             else
243                 *refVar -= *refVar2;
244         }
245         else
246         {
247             refVar.Clear();
248             break;
249         }
250     }
251     *ppBuf = p;
252     if( refVar.Is() )
253         refVar->AddRef();
254     return refVar;
255 }
256 
257 static SbxVariable* Assign( SbxObject* pObj, SbxObject* pGbl, const xub_Unicode** ppBuf )
258 {
259     const xub_Unicode* p = *ppBuf;
260     SbxVariableRef refVar( Operand( pObj, pGbl, &p, sal_True ) );
261     p = SkipWhitespace( p );
262     if( refVar.Is() )
263     {
264         if( *p == '=' )
265         {
266             // Nur auf Props zuweisen!
267             if( refVar->GetClass() != SbxCLASS_PROPERTY )
268             {
269                 SbxBase::SetError( SbxERR_BAD_ACTION );
270                 refVar.Clear();
271             }
272             else
273             {
274                 p++;
275                 SbxVariableRef refVar2( PlusMinus( pObj, pGbl, &p ) );
276                 if( refVar2.Is() )
277                 {
278                     SbxVariable* pVar = refVar;
279                     SbxVariable* pVar2 = refVar2;
280                     *pVar = *pVar2;
281                     pVar->SetParameters( NULL );
282                 }
283             }
284         }
285         else
286             // Einfacher Aufruf: einmal aktivieren
287             refVar->Broadcast( SBX_HINT_DATAWANTED );
288     }
289     *ppBuf = p;
290     if( refVar.Is() )
291         refVar->AddRef();
292     return refVar;
293 }
294 
295 // Einlesen eines Elements. Dies ist ein Symbol, optional gefolgt
296 // von einer Parameterliste. Das Symbol wird im angegebenen Objekt
297 // gesucht und die Parameterliste wird ggf. angefuegt.
298 
299 static SbxVariable* Element
300     ( SbxObject* pObj, SbxObject* pGbl, const xub_Unicode** ppBuf,
301       SbxClassType t, const SbxSimpleCharClass& rCharClass )
302 {
303     XubString aSym;
304     const xub_Unicode* p = Symbol( *ppBuf, aSym, rCharClass );
305     SbxVariableRef refVar;
306     if( aSym.Len() )
307     {
308         sal_uInt16 nOld = pObj->GetFlags();
309         if( pObj == pGbl )
310             pObj->SetFlag( SBX_GBLSEARCH );
311         refVar = pObj->Find( aSym, t );
312         pObj->SetFlags( nOld );
313         if( refVar.Is() )
314         {
315             refVar->SetParameters( NULL );
316             // folgen noch Parameter?
317             p = SkipWhitespace( p );
318             if( *p == '(' )
319             {
320                 p++;
321                 SbxArrayRef refPar = new SbxArray;
322                 sal_uInt16 nArg = 0;
323                 // Wird sind mal relaxed und akzeptieren auch
324                 // das Zeilen- oder Komandoende als Begrenzer
325                 // Parameter immer global suchen!
326                 while( *p && *p != ')' && *p != ']' )
327                 {
328                     SbxVariableRef refArg = PlusMinus( pGbl, pGbl, &p );
329                     if( !refArg )
330                     {
331                         // Fehler beim Parsing
332                         refVar.Clear(); break;
333                     }
334                     else
335                     {
336                         // Man kopiere den Parameter, damit
337                         // man den aktuellen Zustand hat (loest auch
338                         // den Aufruf per Zugriff aus)
339                         SbxVariable* pArg = refArg;
340                         refPar->Put( new SbxVariable( *pArg ), ++nArg );
341                     }
342                     p = SkipWhitespace( p );
343                     if( *p == ',' )
344                         p++;
345                 }
346                 if( *p == ')' )
347                     p++;
348                 if( refVar.Is() )
349                     refVar->SetParameters( refPar );
350             }
351         }
352         else
353             SbxBase::SetError( SbxERR_NO_METHOD );
354     }
355     *ppBuf = p;
356     if( refVar.Is() )
357         refVar->AddRef();
358     return refVar;
359 }
360 
361 // Hauptroutine
362 
363 SbxVariable* SbxObject::Execute( const XubString& rTxt )
364 {
365     SbxVariable* pVar = NULL;
366     const xub_Unicode* p = rTxt.GetBuffer();
367     for( ;; )
368     {
369         p = SkipWhitespace( p );
370         if( !*p )
371             break;
372         if( *p++ != '[' )
373         {
374             SetError( SbxERR_SYNTAX ); break;
375         }
376         pVar = Assign( this, this, &p );
377         if( !pVar )
378             break;
379         p = SkipWhitespace( p );
380         if( *p++ != ']' )
381         {
382             SetError( SbxERR_SYNTAX ); break;
383         }
384     }
385     return pVar;
386 }
387 
388 SbxVariable* SbxObject::FindQualified( const XubString& rName, SbxClassType t )
389 {
390     SbxVariable* pVar = NULL;
391     const xub_Unicode* p = rName.GetBuffer();
392     p = SkipWhitespace( p );
393     if( !*p )
394         return NULL;;
395     pVar = QualifiedName( this, this, &p, t );
396     p = SkipWhitespace( p );
397     if( *p )
398         SetError( SbxERR_SYNTAX );
399     return pVar;
400 }
401 
402