/src/libreoffice/basic/source/sbx/sbxexec.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <sal/config.h> |
21 | | |
22 | | #include <basic/sbx.hxx> |
23 | | #include <basic/sberrors.hxx> |
24 | | #include <rtl/character.hxx> |
25 | | #include <rtl/ustrbuf.hxx> |
26 | | |
27 | | #include <basiccharclass.hxx> |
28 | | |
29 | | static SbxVariableRef Element |
30 | | ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, |
31 | | SbxClassType, bool bCompatible ); |
32 | | |
33 | | static const sal_Unicode* SkipWhitespace( const sal_Unicode* p ) |
34 | 0 | { |
35 | 0 | while( BasicCharClass::isWhitespace(*p) ) |
36 | 0 | p++; |
37 | 0 | return p; |
38 | 0 | } |
39 | | |
40 | | // Scanning of a symbol. The symbol were inserted in rSym, the return value |
41 | | // is the new scan position. The symbol is at errors empty. |
42 | | |
43 | | static const sal_Unicode* Symbol( const sal_Unicode* p, OUString& rSym, bool bCompatible ) |
44 | 0 | { |
45 | 0 | sal_uInt16 nLen = 0; |
46 | | // Did we have a nonstandard symbol? |
47 | 0 | if( *p == '[' ) |
48 | 0 | { |
49 | 0 | rSym = ++p; |
50 | 0 | while( *p && *p != ']' ) |
51 | 0 | { |
52 | 0 | p++; |
53 | 0 | nLen++; |
54 | 0 | } |
55 | 0 | p++; |
56 | 0 | } |
57 | 0 | else |
58 | 0 | { |
59 | | // A symbol had to begin with an alphabetic character or an underline |
60 | 0 | if( !BasicCharClass::isAlpha( *p, bCompatible ) && *p != '_' ) |
61 | 0 | { |
62 | 0 | SbxBase::SetError( ERRCODE_BASIC_SYNTAX ); |
63 | 0 | } |
64 | 0 | else |
65 | 0 | { |
66 | 0 | rSym = p; |
67 | | // The it can contain alphabetic characters, numbers or underlines |
68 | 0 | while( *p && (BasicCharClass::isAlphaNumeric( *p, bCompatible ) || *p == '_') ) |
69 | 0 | { |
70 | 0 | p++; |
71 | 0 | nLen++; |
72 | 0 | } |
73 | | // Ignore standard BASIC suffixes |
74 | 0 | if( *p && (*p == '%' || *p == '&' || *p == '!' || *p == '#' || *p == '$' ) ) |
75 | 0 | { |
76 | 0 | p++; |
77 | 0 | } |
78 | 0 | } |
79 | 0 | } |
80 | 0 | rSym = rSym.copy( 0, nLen ); |
81 | 0 | return p; |
82 | 0 | } |
83 | | |
84 | | // Qualified name. Element.Element... |
85 | | |
86 | | static SbxVariableRef QualifiedName |
87 | | ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, SbxClassType t, bool bCompatible ) |
88 | 0 | { |
89 | |
|
90 | 0 | SbxVariableRef refVar; |
91 | 0 | const sal_Unicode* p = SkipWhitespace( *ppBuf ); |
92 | 0 | if( BasicCharClass::isAlpha( *p, bCompatible ) || *p == '_' || *p == '[' ) |
93 | 0 | { |
94 | | // Read in the element |
95 | 0 | refVar = Element( pObj, pGbl, &p, t, bCompatible ); |
96 | 0 | while( refVar.is() && (*p == '.' || *p == '!') ) |
97 | 0 | { |
98 | | // It follows still an objectelement. The current element |
99 | | // had to be a SBX-Object or had to deliver such an object! |
100 | 0 | pObj = dynamic_cast<SbxObject*>( refVar.get() ); |
101 | 0 | if( !pObj ) |
102 | | // Then it had to deliver an object |
103 | 0 | pObj = dynamic_cast<SbxObject*>( refVar->GetObject() ); |
104 | 0 | refVar.clear(); |
105 | 0 | if( !pObj ) |
106 | 0 | break; |
107 | 0 | p++; |
108 | | // And the next element please |
109 | 0 | refVar = Element( pObj, pGbl, &p, t, bCompatible ); |
110 | 0 | } |
111 | 0 | } |
112 | 0 | else |
113 | 0 | SbxBase::SetError( ERRCODE_BASIC_SYNTAX ); |
114 | 0 | *ppBuf = p; |
115 | 0 | return refVar; |
116 | 0 | } |
117 | | |
118 | | // Read in of an operand. This could be a number, a string or |
119 | | // a function (with optional parameters). |
120 | | |
121 | | static SbxVariableRef Operand |
122 | | ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bVar, bool bCompatible ) |
123 | 0 | { |
124 | 0 | SbxVariableRef refVar( new SbxVariable ); |
125 | 0 | const sal_Unicode* p = SkipWhitespace( *ppBuf ); |
126 | 0 | if( !bVar && ( rtl::isAsciiDigit( *p ) |
127 | 0 | || ( *p == '.' && rtl::isAsciiDigit( *( p+1 ) ) ) |
128 | 0 | || *p == '-' |
129 | 0 | || *p == '&' ) ) |
130 | 0 | { |
131 | | // A number could be scanned in directly! |
132 | 0 | sal_Int32 nLen; |
133 | 0 | if (!refVar->Scan(p, &nLen)) |
134 | 0 | { |
135 | 0 | refVar.clear(); |
136 | 0 | } |
137 | 0 | else |
138 | 0 | { |
139 | 0 | p += nLen; |
140 | 0 | } |
141 | 0 | } |
142 | 0 | else if( !bVar && *p == '"' ) |
143 | 0 | { |
144 | | // A string |
145 | 0 | OUStringBuffer aString; |
146 | 0 | p++; |
147 | 0 | for( ;; ) |
148 | 0 | { |
149 | | // This is perhaps an error |
150 | 0 | if( !*p ) |
151 | 0 | { |
152 | 0 | return nullptr; |
153 | 0 | } |
154 | | // Double quotes are OK |
155 | 0 | if( *p == '"' && (*++p) != '"' ) |
156 | 0 | { |
157 | 0 | break; |
158 | 0 | } |
159 | 0 | aString.append(*p++); |
160 | 0 | } |
161 | 0 | refVar->PutString( aString.makeStringAndClear() ); |
162 | 0 | } |
163 | 0 | else |
164 | 0 | { |
165 | 0 | refVar = QualifiedName( pObj, pGbl, &p, SbxClassType::DontCare, bCompatible ); |
166 | 0 | } |
167 | 0 | *ppBuf = p; |
168 | 0 | return refVar; |
169 | 0 | } |
170 | | |
171 | | // Read in of a simple term. The operands +, -, * and / |
172 | | // are supported. |
173 | | |
174 | | static SbxVariableRef MulDiv( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible ) |
175 | 0 | { |
176 | 0 | const sal_Unicode* p = *ppBuf; |
177 | 0 | SbxVariableRef refVar( Operand( pObj, pGbl, &p, false, bCompatible ) ); |
178 | 0 | p = SkipWhitespace( p ); |
179 | 0 | while( refVar.is() && ( *p == '*' || *p == '/' ) ) |
180 | 0 | { |
181 | 0 | sal_Unicode cOp = *p++; |
182 | 0 | SbxVariableRef refVar2( Operand( pObj, pGbl, &p, false, bCompatible ) ); |
183 | 0 | if( refVar2.is() ) |
184 | 0 | { |
185 | | // temporary variable! |
186 | 0 | SbxVariable* pVar = refVar.get(); |
187 | 0 | pVar = new SbxVariable( *pVar ); |
188 | 0 | refVar = pVar; |
189 | 0 | if( cOp == '*' ) |
190 | 0 | *refVar *= *refVar2; |
191 | 0 | else |
192 | 0 | *refVar /= *refVar2; |
193 | 0 | } |
194 | 0 | else |
195 | 0 | { |
196 | 0 | refVar.clear(); |
197 | 0 | break; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | *ppBuf = p; |
201 | 0 | return refVar; |
202 | 0 | } |
203 | | |
204 | | static SbxVariableRef PlusMinus( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible ) |
205 | 0 | { |
206 | 0 | const sal_Unicode* p = *ppBuf; |
207 | 0 | SbxVariableRef refVar( MulDiv( pObj, pGbl, &p, bCompatible ) ); |
208 | 0 | p = SkipWhitespace( p ); |
209 | 0 | while( refVar.is() && ( *p == '+' || *p == '-' ) ) |
210 | 0 | { |
211 | 0 | sal_Unicode cOp = *p++; |
212 | 0 | SbxVariableRef refVar2( MulDiv( pObj, pGbl, &p, bCompatible ) ); |
213 | 0 | if( refVar2.is() ) |
214 | 0 | { |
215 | | // temporary Variable! |
216 | 0 | SbxVariable* pVar = refVar.get(); |
217 | 0 | pVar = new SbxVariable( *pVar ); |
218 | 0 | refVar = pVar; |
219 | 0 | if( cOp == '+' ) |
220 | 0 | *refVar += *refVar2; |
221 | 0 | else |
222 | 0 | *refVar -= *refVar2; |
223 | 0 | } |
224 | 0 | else |
225 | 0 | { |
226 | 0 | refVar.clear(); |
227 | 0 | break; |
228 | 0 | } |
229 | 0 | } |
230 | 0 | *ppBuf = p; |
231 | 0 | return refVar; |
232 | 0 | } |
233 | | |
234 | | static SbxVariableRef Assign( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, bool bCompatible ) |
235 | 0 | { |
236 | 0 | const sal_Unicode* p = *ppBuf; |
237 | 0 | SbxVariableRef refVar( Operand( pObj, pGbl, &p, true, bCompatible ) ); |
238 | 0 | p = SkipWhitespace( p ); |
239 | 0 | if( refVar.is() ) |
240 | 0 | { |
241 | 0 | if( *p == '=' ) |
242 | 0 | { |
243 | | // Assign only onto properties! |
244 | 0 | if( refVar->GetClass() != SbxClassType::Property ) |
245 | 0 | { |
246 | 0 | SbxBase::SetError( ERRCODE_BASIC_BAD_ACTION ); |
247 | 0 | refVar.clear(); |
248 | 0 | } |
249 | 0 | else |
250 | 0 | { |
251 | 0 | p++; |
252 | 0 | SbxVariableRef refVar2( PlusMinus( pObj, pGbl, &p, bCompatible ) ); |
253 | 0 | if( refVar2.is() ) |
254 | 0 | { |
255 | 0 | SbxVariable* pVar = refVar.get(); |
256 | 0 | SbxVariable* pVar2 = refVar2.get(); |
257 | 0 | *pVar = *pVar2; |
258 | 0 | pVar->SetParameters( nullptr ); |
259 | 0 | } |
260 | 0 | } |
261 | 0 | } |
262 | 0 | else |
263 | | // Simple call: once activating |
264 | 0 | refVar->Broadcast( SfxHintId::BasicDataWanted ); |
265 | 0 | } |
266 | 0 | *ppBuf = p; |
267 | 0 | return refVar; |
268 | 0 | } |
269 | | |
270 | | // Read in of an element. This is a symbol, optional followed |
271 | | // by a parameter list. The symbol will be searched in the |
272 | | // specified object and the parameter list will be attached if necessary. |
273 | | |
274 | | static SbxVariableRef Element |
275 | | ( SbxObject* pObj, SbxObject* pGbl, const sal_Unicode** ppBuf, |
276 | | SbxClassType t, bool bCompatible ) |
277 | 0 | { |
278 | 0 | OUString aSym; |
279 | 0 | const sal_Unicode* p = Symbol( *ppBuf, aSym, bCompatible ); |
280 | 0 | SbxVariableRef refVar; |
281 | 0 | if( !aSym.isEmpty() ) |
282 | 0 | { |
283 | 0 | SbxFlagBits nOld = pObj->GetFlags(); |
284 | 0 | if( pObj == pGbl ) |
285 | 0 | { |
286 | 0 | pObj->SetFlag( SbxFlagBits::GlobalSearch ); |
287 | 0 | } |
288 | 0 | refVar = pObj->Find( aSym, t ); |
289 | 0 | pObj->SetFlags( nOld ); |
290 | 0 | if( refVar.is() ) |
291 | 0 | { |
292 | 0 | refVar->SetParameters( nullptr ); |
293 | | // Follow still parameter? |
294 | 0 | p = SkipWhitespace( p ); |
295 | 0 | if( *p == '(' ) |
296 | 0 | { |
297 | 0 | p++; |
298 | 0 | auto refPar = tools::make_ref<SbxArray>(); |
299 | 0 | sal_uInt32 nArg = 0; |
300 | | // We are once relaxed and accept as well |
301 | | // the line- or command end as delimiter |
302 | | // Search parameter always global! |
303 | 0 | while( *p && *p != ')' && *p != ']' ) |
304 | 0 | { |
305 | 0 | SbxVariableRef refArg = PlusMinus( pGbl, pGbl, &p, bCompatible ); |
306 | 0 | if( !refArg.is() ) |
307 | 0 | { |
308 | | // Error during the parsing |
309 | 0 | refVar.clear(); break; |
310 | 0 | } |
311 | 0 | else |
312 | 0 | { |
313 | | // One copies the parameter, so that |
314 | | // one have the current status (triggers also |
315 | | // the call per access) |
316 | 0 | refPar->Put(new SbxVariable(*refArg), ++nArg); |
317 | 0 | } |
318 | 0 | p = SkipWhitespace( p ); |
319 | 0 | if( *p == ',' ) |
320 | 0 | p++; |
321 | 0 | } |
322 | 0 | if( *p == ')' ) |
323 | 0 | p++; |
324 | 0 | if( refVar.is() ) |
325 | 0 | refVar->SetParameters( refPar.get() ); |
326 | 0 | } |
327 | 0 | } |
328 | 0 | else |
329 | 0 | SbxBase::SetError( ERRCODE_BASIC_NO_METHOD, aSym ); |
330 | 0 | } |
331 | 0 | *ppBuf = p; |
332 | 0 | return refVar; |
333 | 0 | } |
334 | | |
335 | | // Mainroutine |
336 | | |
337 | | SbxVariable* SbxObject::Execute( const OUString& rTxt ) |
338 | 0 | { |
339 | 0 | SbxVariableRef pVar; |
340 | 0 | const sal_Unicode* p = rTxt.getStr(); |
341 | 0 | for( ;; ) |
342 | 0 | { |
343 | 0 | p = SkipWhitespace( p ); |
344 | 0 | if( !*p ) |
345 | 0 | { |
346 | 0 | break; |
347 | 0 | } |
348 | 0 | if( *p++ != '[' ) |
349 | 0 | { |
350 | 0 | SetError( ERRCODE_BASIC_SYNTAX ); break; |
351 | 0 | } |
352 | 0 | pVar = Assign( this, this, &p, IsOptionCompatible() ); |
353 | 0 | if( !pVar.is() ) |
354 | 0 | { |
355 | 0 | break; |
356 | 0 | } |
357 | 0 | p = SkipWhitespace( p ); |
358 | 0 | if( *p++ != ']' ) |
359 | 0 | { |
360 | 0 | SetError( ERRCODE_BASIC_SYNTAX ); break; |
361 | 0 | } |
362 | 0 | } |
363 | 0 | return pVar.get(); |
364 | 0 | } |
365 | | |
366 | | SbxVariable* SbxObject::FindQualified( const OUString& rName, SbxClassType t ) |
367 | 0 | { |
368 | 0 | SbxVariableRef pVar; |
369 | 0 | const sal_Unicode* p = rName.getStr(); |
370 | 0 | p = SkipWhitespace( p ); |
371 | 0 | if( !*p ) |
372 | 0 | { |
373 | 0 | return nullptr; |
374 | 0 | } |
375 | 0 | pVar = QualifiedName( this, this, &p, t, IsOptionCompatible() ); |
376 | 0 | p = SkipWhitespace( p ); |
377 | 0 | if( *p ) |
378 | 0 | { |
379 | 0 | SetError( ERRCODE_BASIC_SYNTAX ); |
380 | 0 | } |
381 | 0 | return pVar.get(); |
382 | 0 | } |
383 | | |
384 | | bool SbxObject::IsOptionCompatible() const |
385 | 0 | { |
386 | 0 | if (const SbxObject* pObj = GetParent()) |
387 | 0 | return pObj->IsOptionCompatible(); |
388 | 0 | return false; |
389 | 0 | } |
390 | | |
391 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |