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 <basic/sberrors.hxx>
21 #include <basic/sbxmeth.hxx>
22 #include <basic/sbmod.hxx>
23 #include <basic/sbstar.hxx>
24 #include <basic/sbx.hxx>
25 #include <parser.hxx>
26 #include <com/sun/star/script/ModuleType.hpp>
27 #include <rtl/character.hxx>
28 
29 struct SbiParseStack {              // "Stack" for statement-blocks
30     SbiParseStack* pNext;           // Chain
31     SbiExprNode* pWithVar;
32     SbiToken eExitTok;
33     sal_uInt32  nChain;                 // JUMP-Chain
34 };
35 
36 namespace {
37 
38 struct SbiStatement {
39     SbiToken eTok;
40     void( SbiParser::*Func )();
41     bool  bMain;                    // true: OK outside the SUB
42     bool  bSubr;                    // true: OK inside the SUB
43 };
44 
45 }
46 
47 #define Y   true
48 #define N   false
49 
50 const SbiStatement StmntTable [] = {
51 { ATTRIBUTE, &SbiParser::Attribute, Y, Y, }, // ATTRIBUTE
52 { CALL,     &SbiParser::Call,       N, Y, }, // CALL
53 { CLOSE,    &SbiParser::Close,      N, Y, }, // CLOSE
54 { CONST_,   &SbiParser::Dim,        Y, Y, }, // CONST
55 { DECLARE,  &SbiParser::Declare,    Y, N, }, // DECLARE
56 { DEFBOOL,  &SbiParser::DefXXX,     Y, N, }, // DEFBOOL
57 { DEFCUR,   &SbiParser::DefXXX,     Y, N, }, // DEFCUR
58 { DEFDATE,  &SbiParser::DefXXX,     Y, N, }, // DEFDATE
59 { DEFDBL,   &SbiParser::DefXXX,     Y, N, }, // DEFDBL
60 { DEFERR,   &SbiParser::DefXXX,     Y, N, }, // DEFERR
61 { DEFINT,   &SbiParser::DefXXX,     Y, N, }, // DEFINT
62 { DEFLNG,   &SbiParser::DefXXX,     Y, N, }, // DEFLNG
63 { DEFOBJ,   &SbiParser::DefXXX,     Y, N, }, // DEFOBJ
64 { DEFSNG,   &SbiParser::DefXXX,     Y, N, }, // DEFSNG
65 { DEFSTR,   &SbiParser::DefXXX,     Y, N, }, // DEFSTR
66 { DEFVAR,   &SbiParser::DefXXX,     Y, N, }, // DEFVAR
67 { DIM,      &SbiParser::Dim,        Y, Y, }, // DIM
68 { DO,       &SbiParser::DoLoop,     N, Y, }, // DO
69 { ELSE,     &SbiParser::NoIf,       N, Y, }, // ELSE
70 { ELSEIF,   &SbiParser::NoIf,       N, Y, }, // ELSEIF
71 { ENDIF,    &SbiParser::NoIf,       N, Y, }, // ENDIF
72 { END,      &SbiParser::Stop,       N, Y, }, // END
73 { ENUM,     &SbiParser::Enum,       Y, N, }, // TYPE
74 { ERASE,    &SbiParser::Erase,      N, Y, }, // ERASE
75 { ERROR_,   &SbiParser::ErrorStmnt, N, Y, }, // ERROR
76 { EXIT,     &SbiParser::Exit,       N, Y, }, // EXIT
77 { FOR,      &SbiParser::For,        N, Y, }, // FOR
78 { FUNCTION, &SbiParser::SubFunc,    Y, N, }, // FUNCTION
79 { GOSUB,    &SbiParser::Goto,       N, Y, }, // GOSUB
80 { GLOBAL,   &SbiParser::Dim,        Y, N, }, // GLOBAL
81 { GOTO,     &SbiParser::Goto,       N, Y, }, // GOTO
82 { IF,       &SbiParser::If,         N, Y, }, // IF
83 { IMPLEMENTS, &SbiParser::Implements, Y, N, }, // IMPLEMENTS
84 { INPUT,    &SbiParser::Input,      N, Y, }, // INPUT
85 { LET,      &SbiParser::Assign,     N, Y, }, // LET
86 { LINE,     &SbiParser::Line,       N, Y, }, // LINE, -> LINE INPUT (#i92642)
87 { LINEINPUT,&SbiParser::LineInput,  N, Y, }, // LINE INPUT
88 { LOOP,     &SbiParser::BadBlock,   N, Y, }, // LOOP
89 { LSET,     &SbiParser::LSet,       N, Y, }, // LSET
90 { NAME,     &SbiParser::Name,       N, Y, }, // NAME
91 { NEXT,     &SbiParser::BadBlock,   N, Y, }, // NEXT
92 { ON,       &SbiParser::On,         N, Y, }, // ON
93 { OPEN,     &SbiParser::Open,       N, Y, }, // OPEN
94 { OPTION,   &SbiParser::Option,     Y, N, }, // OPTION
95 { PRINT,    &SbiParser::Print,      N, Y, }, // PRINT
96 { PRIVATE,  &SbiParser::Dim,        Y, N, }, // PRIVATE
97 { PROPERTY, &SbiParser::SubFunc,    Y, N, }, // FUNCTION
98 { PUBLIC,   &SbiParser::Dim,        Y, N, }, // PUBLIC
99 { REDIM,    &SbiParser::ReDim,      N, Y, }, // DIM
100 { RESUME,   &SbiParser::Resume,     N, Y, }, // RESUME
101 { RETURN,   &SbiParser::Return,     N, Y, }, // RETURN
102 { RSET,     &SbiParser::RSet,       N, Y, }, // RSET
103 { SELECT,   &SbiParser::Select,     N, Y, }, // SELECT
104 { SET,      &SbiParser::Set,        N, Y, }, // SET
105 { STATIC,   &SbiParser::Static,     Y, Y, }, // STATIC
106 { STOP,     &SbiParser::Stop,       N, Y, }, // STOP
107 { SUB,      &SbiParser::SubFunc,    Y, N, }, // SUB
108 { TYPE,     &SbiParser::Type,       Y, N, }, // TYPE
109 { UNTIL,    &SbiParser::BadBlock,   N, Y, }, // UNTIL
110 { WHILE,    &SbiParser::While,      N, Y, }, // WHILE
111 { WEND,     &SbiParser::BadBlock,   N, Y, }, // WEND
112 { WITH,     &SbiParser::With,       N, Y, }, // WITH
113 { WRITE,    &SbiParser::Write,      N, Y, }, // WRITE
114 
115 { NIL, nullptr, N, N }
116 };
117 
118 
SbiParser(StarBASIC * pb,SbModule * pm)119 SbiParser::SbiParser( StarBASIC* pb, SbModule* pm )
120         : SbiTokenizer( pm->GetSource32(), pb ),
121           aGlobals( aGblStrings, SbGLOBAL, this ),
122           aPublics( aGblStrings, SbPUBLIC, this ),
123           aRtlSyms( aGblStrings, SbRTL, this ),
124           aGen( *pm, this )
125 {
126     eEndTok  = NIL;
127     pProc    = nullptr;
128     pStack   = nullptr;
129     pWithVar = nullptr;
130     nBase    = 0;
131     bGblDefs =
132     bNewGblDefs =
133     bSingleLineIf =
134     bCodeCompleting =
135     bExplicit = false;
136     bClassModule = ( pm->GetModuleType() == css::script::ModuleType::CLASS );
137     pPool    = &aPublics;
138     for(SbxDataType & eDefType : eDefTypes)
139         eDefType = SbxVARIANT;    // no explicit default type
140 
141     aPublics.SetParent( &aGlobals );
142     aGlobals.SetParent( &aRtlSyms );
143 
144 
145     nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
146 
147     rTypeArray = new SbxArray; // array for user defined types
148     rEnumArray = new SbxArray; // array for Enum types
149     bVBASupportOn = pm->IsVBACompat();
150     if ( bVBASupportOn )
151         EnableCompatibility();
152 
153 }
154 
~SbiParser()155 SbiParser::~SbiParser() { }
156 
157 // part of the runtime-library?
CheckRTLForSym(const OUString & rSym,SbxDataType eType)158 SbiSymDef* SbiParser::CheckRTLForSym(const OUString& rSym, SbxDataType eType)
159 {
160     SbxVariable* pVar = GetBasic()->GetRtl()->Find(rSym, SbxClassType::DontCare);
161     if (!pVar)
162         return nullptr;
163 
164     if (SbxMethod* pMethod = dynamic_cast<SbxMethod*>(pVar))
165     {
166         SbiProcDef* pProc_ = aRtlSyms.AddProc( rSym );
167         if (pMethod->IsRuntimeFunction())
168         {
169             pProc_->SetType( pMethod->GetRuntimeFunctionReturnType() );
170         }
171         else
172         {
173             pProc_->SetType( pVar->GetType() );
174         }
175         return pProc_;
176     }
177 
178 
179     SbiSymDef* pDef = aRtlSyms.AddSym(rSym);
180     pDef->SetType(eType);
181     return pDef;
182 }
183 
184 // close global chain
185 
HasGlobalCode()186 bool SbiParser::HasGlobalCode()
187 {
188     if( bGblDefs && nGblChain )
189     {
190         aGen.BackChain( nGblChain );
191         aGen.Gen( SbiOpcode::LEAVE_ );
192         nGblChain = 0;
193     }
194     return bGblDefs;
195 }
196 
OpenBlock(SbiToken eTok,SbiExprNode * pVar)197 void SbiParser::OpenBlock( SbiToken eTok, SbiExprNode* pVar )
198 {
199     SbiParseStack* p = new SbiParseStack;
200     p->eExitTok = eTok;
201     p->nChain   = 0;
202     p->pWithVar = pWithVar;
203     p->pNext    = pStack;
204     pStack      = p;
205     pWithVar    = pVar;
206 
207     // #29955 service the for-loop level
208     if( eTok == FOR )
209         aGen.IncForLevel();
210 }
211 
CloseBlock()212 void SbiParser::CloseBlock()
213 {
214     if( !pStack )
215         return;
216 
217     SbiParseStack* p = pStack;
218 
219     // #29955 service the for-loop level
220     if( p->eExitTok == FOR )
221         aGen.DecForLevel();
222 
223     aGen.BackChain( p->nChain );
224     pStack = p->pNext;
225     pWithVar = p->pWithVar;
226     delete p;
227 }
228 
229 // EXIT ...
230 
Exit()231 void SbiParser::Exit()
232 {
233     SbiToken eTok = Next();
234     for( SbiParseStack* p = pStack; p; p = p->pNext )
235     {
236         SbiToken eExitTok = p->eExitTok;
237         if( eTok == eExitTok ||
238             (eTok == PROPERTY && (eExitTok == GET || eExitTok == LET) ) )   // #i109051
239         {
240             p->nChain = aGen.Gen( SbiOpcode::JUMP_, p->nChain );
241             return;
242         }
243     }
244     if( pStack )
245         Error( ERRCODE_BASIC_EXPECTED, pStack->eExitTok );
246     else
247         Error( ERRCODE_BASIC_BAD_EXIT );
248 }
249 
TestSymbol()250 bool SbiParser::TestSymbol()
251 {
252     Peek();
253     if( eCurTok == SYMBOL )
254     {
255         Next(); return true;
256     }
257     Error( ERRCODE_BASIC_SYMBOL_EXPECTED );
258     return false;
259 }
260 
261 
TestToken(SbiToken t)262 bool SbiParser::TestToken( SbiToken t )
263 {
264     if( Peek() == t )
265     {
266         Next(); return true;
267     }
268     else
269     {
270         Error( ERRCODE_BASIC_EXPECTED, t );
271         return false;
272     }
273 }
274 
275 
TestComma()276 bool SbiParser::TestComma()
277 {
278     SbiToken eTok = Peek();
279     if( IsEoln( eTok ) )
280     {
281         Next();
282         return false;
283     }
284     else if( eTok != COMMA )
285     {
286         Error( ERRCODE_BASIC_EXPECTED, COMMA );
287         return false;
288     }
289     Next();
290     return true;
291 }
292 
293 
TestEoln()294 void SbiParser::TestEoln()
295 {
296     if( !IsEoln( Next() ) )
297     {
298         Error( ERRCODE_BASIC_EXPECTED, EOLN );
299         while( !IsEoln( Next() ) ) {}
300     }
301 }
302 
303 
StmntBlock(SbiToken eEnd)304 void SbiParser::StmntBlock( SbiToken eEnd )
305 {
306     SbiToken xe = eEndTok;
307     eEndTok = eEnd;
308     while( !bAbort && Parse() ) {}
309     eEndTok = xe;
310     if( IsEof() )
311     {
312         Error( ERRCODE_BASIC_BAD_BLOCK, eEnd );
313         bAbort = true;
314     }
315 }
316 
SetCodeCompleting(bool b)317 void SbiParser::SetCodeCompleting( bool b )
318 {
319     bCodeCompleting = b;
320 }
321 
322 
Parse()323 bool SbiParser::Parse()
324 {
325     if( bAbort ) return false;
326 
327     EnableErrors();
328 
329     bErrorIsSymbol = false;
330     Peek();
331     bErrorIsSymbol = true;
332 
333     if( IsEof() )
334     {
335         // AB #33133: If no sub has been created before,
336         // the global chain must be closed here!
337         // AB #40689: Due to the new static-handling there
338         // can be another nGblChain, so ask for it before.
339         if( bNewGblDefs && nGblChain == 0 )
340             nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
341         return false;
342     }
343 
344 
345     if( IsEoln( eCurTok ) )
346     {
347         Next(); return true;
348     }
349 
350     if( !bSingleLineIf && MayBeLabel( true ) )
351     {
352         // is a label
353         if( !pProc )
354             Error( ERRCODE_BASIC_NOT_IN_MAIN, aSym );
355         else
356             pProc->GetLabels().Define( aSym );
357         Next(); Peek();
358 
359         if( IsEoln( eCurTok ) )
360         {
361             Next(); return true;
362         }
363     }
364 
365     // end of parsing?
366     if( eCurTok == eEndTok ||
367         ( bVBASupportOn &&      // #i109075
368           (eCurTok == ENDFUNC || eCurTok == ENDPROPERTY || eCurTok == ENDSUB) &&
369           (eEndTok == ENDFUNC || eEndTok == ENDPROPERTY || eEndTok == ENDSUB) ) )
370     {
371         Next();
372         if( eCurTok != NIL )
373             aGen.Statement();
374         return false;
375     }
376 
377     // comment?
378     if( eCurTok == REM )
379     {
380         Next(); return true;
381     }
382 
383     // In vba it's possible to do Error.foobar ( even if it results in
384     // a runtime error
385     if ( eCurTok == ERROR_ && IsVBASupportOn() ) // we probably need to define a subset of keywords where this madness applies e.g. if ( IsVBASupportOn() && SymbolCanBeRedined( eCurTok ) )
386     {
387         SbiTokenizer tokens( *this );
388         tokens.Next();
389         if ( tokens.Peek()  == DOT )
390         {
391             eCurTok = SYMBOL;
392             ePush = eCurTok;
393         }
394     }
395     // if there's a symbol, it's either a variable (LET)
396     // or a SUB-procedure (CALL without brackets)
397     // DOT for assignments in the WITH-block: .A=5
398     if( eCurTok == SYMBOL || eCurTok == DOT )
399     {
400         if( !pProc )
401             Error( ERRCODE_BASIC_EXPECTED, SUB );
402         else
403         {
404             // for correct line and column...
405             Next();
406             Push( eCurTok );
407             aGen.Statement();
408             Symbol(nullptr);
409         }
410     }
411     else
412     {
413         Next();
414 
415         // statement parsers
416 
417         const SbiStatement* p;
418         for( p = StmntTable; p->eTok != NIL; p++ )
419             if( p->eTok == eCurTok )
420                 break;
421         if( p->eTok != NIL )
422         {
423             if( !pProc && !p->bMain )
424                 Error( ERRCODE_BASIC_NOT_IN_MAIN, eCurTok );
425             else if( pProc && !p->bSubr )
426                 Error( ERRCODE_BASIC_NOT_IN_SUBR, eCurTok );
427             else
428             {
429                 // AB #41606/#40689: Due to the new static-handling there
430                 // can be another nGblChain, so ask for it before.
431                 if( bNewGblDefs && nGblChain == 0 &&
432                     ( eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY ) )
433                 {
434                     nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 );
435                     bNewGblDefs = false;
436                 }
437                 // statement-opcode at the beginning of a sub, too, please
438                 if( ( p->bSubr && (eCurTok != STATIC || Peek() == SUB || Peek() == FUNCTION ) ) ||
439                         eCurTok == SUB || eCurTok == FUNCTION )
440                     aGen.Statement();
441                 (this->*( p->Func ) )();
442                 ErrCode nSbxErr = SbxBase::GetError();
443                 if( nSbxErr )
444                 {
445                     SbxBase::ResetError();
446                     Error( nSbxErr );
447                 }
448             }
449         }
450         else
451             Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
452     }
453 
454     // test for the statement's end -
455     // might also be an ELSE, as there must not necessary be a : before the ELSE!
456 
457     if( !IsEos() )
458     {
459         Peek();
460         if( !IsEos() && eCurTok != ELSE )
461         {
462             // if the parsing has been aborted, jump over to the ":"
463             Error( ERRCODE_BASIC_UNEXPECTED, eCurTok );
464             while( !IsEos() ) Next();
465         }
466     }
467     // The parser aborts at the end, the
468     // next token has not been fetched yet!
469     return true;
470 }
471 
472 
GetWithVar()473 SbiExprNode* SbiParser::GetWithVar()
474 {
475     if( pWithVar )
476         return pWithVar;
477 
478     SbiParseStack* p = pStack;
479     while( p )
480     {
481         // LoopVar can at the moment only be for with
482         if( p->pWithVar )
483             return p->pWithVar;
484         p = p->pNext;
485     }
486     return nullptr;
487 }
488 
489 
490 // assignment or subroutine call
491 
Symbol(const KeywordSymbolInfo * pKeywordSymbolInfo)492 void SbiParser::Symbol( const KeywordSymbolInfo* pKeywordSymbolInfo )
493 {
494     SbiExprMode eMode = bVBASupportOn ? EXPRMODE_STANDALONE : EXPRMODE_STANDARD;
495     SbiExpression aVar( this, SbSYMBOL, eMode, pKeywordSymbolInfo );
496 
497     bool bEQ = ( Peek() == EQ );
498     if( !bEQ && bVBASupportOn && aVar.IsBracket() )
499         Error( ERRCODE_BASIC_EXPECTED, "=" );
500 
501     RecursiveMode eRecMode = ( bEQ ? PREVENT_CALL : FORCE_CALL );
502     bool bSpecialMidHandling = false;
503     SbiSymDef* pDef = aVar.GetRealVar();
504     if( bEQ && pDef && pDef->GetScope() == SbRTL )
505     {
506         OUString aRtlName = pDef->GetName();
507         if( aRtlName.equalsIgnoreAsciiCase("Mid") )
508         {
509             SbiExprNode* pExprNode = aVar.GetExprNode();
510             if( pExprNode && pExprNode->GetNodeType() == SbxVARVAL )
511             {
512                 SbiExprList* pPar = pExprNode->GetParameters();
513                 short nParCount = pPar ? pPar->GetSize() : 0;
514                 if( nParCount == 2 || nParCount == 3 )
515                 {
516                     if( nParCount == 2 )
517                         pPar->addExpression( std::make_unique<SbiExpression>( this, -1, SbxLONG ) );
518 
519                     TestToken( EQ );
520                     pPar->addExpression( std::make_unique<SbiExpression>( this ) );
521 
522                     bSpecialMidHandling = true;
523                 }
524             }
525         }
526     }
527     aVar.Gen( eRecMode );
528     if( bSpecialMidHandling )
529         return;
530 
531     if( !bEQ )
532     {
533         aGen.Gen( SbiOpcode::GET_ );
534     }
535     else
536     {
537         // so it must be an assignment!
538         if( !aVar.IsLvalue() )
539             Error( ERRCODE_BASIC_LVALUE_EXPECTED );
540         TestToken( EQ );
541         SbiExpression aExpr( this );
542         aExpr.Gen();
543         SbiOpcode eOp = SbiOpcode::PUT_;
544         if( pDef )
545         {
546             if( pDef->GetConstDef() )
547                 Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
548             if( pDef->GetType() == SbxOBJECT )
549             {
550                 eOp = SbiOpcode::SET_;
551                 if( pDef->GetTypeId() )
552                 {
553                     aGen.Gen( SbiOpcode::SETCLASS_, pDef->GetTypeId() );
554                     return;
555                 }
556             }
557         }
558         aGen.Gen( eOp );
559     }
560 }
561 
562 
Assign()563 void SbiParser::Assign()
564 {
565     SbiExpression aLvalue( this, SbLVALUE );
566     TestToken( EQ );
567     SbiExpression aExpr( this );
568     aLvalue.Gen();
569     aExpr.Gen();
570     sal_uInt16 nLen = 0;
571     SbiSymDef* pDef = aLvalue.GetRealVar();
572     {
573         if( pDef->GetConstDef() )
574             Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
575         nLen = aLvalue.GetRealVar()->GetLen();
576     }
577     if( nLen )
578         aGen.Gen( SbiOpcode::PAD_, nLen );
579     aGen.Gen( SbiOpcode::PUT_ );
580 }
581 
582 // assignments of an object-variable
583 
Set()584 void SbiParser::Set()
585 {
586     SbiExpression aLvalue( this, SbLVALUE );
587     SbxDataType eType = aLvalue.GetType();
588     if( eType != SbxOBJECT && eType != SbxEMPTY && eType != SbxVARIANT )
589         Error( ERRCODE_BASIC_INVALID_OBJECT );
590     TestToken( EQ );
591     SbiSymDef* pDef = aLvalue.GetRealVar();
592     if( pDef->GetConstDef() )
593         Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
594 
595     SbiToken eTok = Peek();
596     if( eTok == NEW )
597     {
598         Next();
599         auto pTypeDef = std::make_unique<SbiSymDef>( OUString() );
600         TypeDecl( *pTypeDef, true );
601 
602         aLvalue.Gen();
603         aGen.Gen( SbiOpcode::CREATE_, pDef->GetId(), pTypeDef->GetTypeId() );
604         aGen.Gen( SbiOpcode::SETCLASS_, pDef->GetTypeId() );
605     }
606     else
607     {
608         SbiExpression aExpr( this );
609         aLvalue.Gen();
610         aExpr.Gen();
611         // It's a good idea to distinguish between
612         // set something = another &
613         // something = another
614         // ( it's necessary for vba objects where set is object
615         // specific and also doesn't involve processing default params )
616         if( pDef->GetTypeId() )
617         {
618             if ( bVBASupportOn )
619                 aGen.Gen( SbiOpcode::VBASETCLASS_, pDef->GetTypeId() );
620             else
621                 aGen.Gen( SbiOpcode::SETCLASS_, pDef->GetTypeId() );
622         }
623         else
624         {
625             if ( bVBASupportOn )
626                 aGen.Gen( SbiOpcode::VBASET_ );
627             else
628                 aGen.Gen( SbiOpcode::SET_ );
629         }
630     }
631 }
632 
633 // JSM 07.10.95
LSet()634 void SbiParser::LSet()
635 {
636     SbiExpression aLvalue( this, SbLVALUE );
637     if( aLvalue.GetType() != SbxSTRING )
638     {
639         Error( ERRCODE_BASIC_INVALID_OBJECT );
640     }
641     TestToken( EQ );
642     SbiSymDef* pDef = aLvalue.GetRealVar();
643     if( pDef && pDef->GetConstDef() )
644     {
645         Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
646     }
647     SbiExpression aExpr( this );
648     aLvalue.Gen();
649     aExpr.Gen();
650     aGen.Gen( SbiOpcode::LSET_ );
651 }
652 
653 // JSM 07.10.95
RSet()654 void SbiParser::RSet()
655 {
656     SbiExpression aLvalue( this, SbLVALUE );
657     if( aLvalue.GetType() != SbxSTRING )
658     {
659         Error( ERRCODE_BASIC_INVALID_OBJECT );
660     }
661     TestToken( EQ );
662     SbiSymDef* pDef = aLvalue.GetRealVar();
663     if( pDef && pDef->GetConstDef() )
664         Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
665     SbiExpression aExpr( this );
666     aLvalue.Gen();
667     aExpr.Gen();
668     aGen.Gen( SbiOpcode::RSET_ );
669 }
670 
671 // DEFINT, DEFLNG, DEFSNG, DEFDBL, DEFSTR and so on
672 
DefXXX()673 void SbiParser::DefXXX()
674 {
675     sal_Unicode ch1, ch2;
676     SbxDataType t = SbxDataType( eCurTok - DEFINT + SbxINTEGER );
677 
678     while( !bAbort )
679     {
680         if( Next() != SYMBOL ) break;
681         ch1 = rtl::toAsciiUpperCase(aSym[0]);
682         ch2 = 0;
683         if( Peek() == MINUS )
684         {
685             Next();
686             if( Next() != SYMBOL ) Error( ERRCODE_BASIC_SYMBOL_EXPECTED );
687             else
688             {
689                 ch2 = rtl::toAsciiUpperCase(aSym[0]);
690                 if( ch2 < ch1 )
691                 {
692                     Error( ERRCODE_BASIC_SYNTAX );
693                     ch2 = 0;
694                 }
695             }
696         }
697         if (!ch2) ch2 = ch1;
698         ch1 -= 'A'; ch2 -= 'A';
699         for (; ch1 <= ch2; ch1++) eDefTypes[ ch1 ] = t;
700         if( !TestComma() ) break;
701     }
702 }
703 
704 // STOP/SYSTEM
705 
Stop()706 void SbiParser::Stop()
707 {
708     aGen.Gen( SbiOpcode::STOP_ );
709     Peek();     // #35694: only Peek(), so that EOL is recognized in Single-Line-If
710 }
711 
712 // IMPLEMENTS
713 
Implements()714 void SbiParser::Implements()
715 {
716     if( !bClassModule )
717     {
718         Error( ERRCODE_BASIC_UNEXPECTED, IMPLEMENTS );
719         return;
720     }
721 
722     Peek();
723     if( eCurTok != SYMBOL )
724     {
725         Error( ERRCODE_BASIC_SYMBOL_EXPECTED );
726         return;
727     }
728 
729     OUString aImplementedIface = aSym;
730     Next();
731     if( Peek() == DOT )
732     {
733         OUString aDotStr( '.' );
734         while( Peek() == DOT )
735         {
736             aImplementedIface += aDotStr;
737             Next();
738             SbiToken ePeekTok = Peek();
739             if( ePeekTok == SYMBOL || IsKwd( ePeekTok ) )
740             {
741                 Next();
742                 aImplementedIface += aSym;
743             }
744             else
745             {
746                 Next();
747                 Error( ERRCODE_BASIC_SYMBOL_EXPECTED );
748                 break;
749             }
750         }
751     }
752     aIfaceVector.push_back( aImplementedIface );
753 }
754 
EnableCompatibility()755 void SbiParser::EnableCompatibility()
756 {
757     if( !bCompatible )
758         AddConstants();
759     bCompatible = true;
760 }
761 
762 // OPTION
763 
Option()764 void SbiParser::Option()
765 {
766     switch( Next() )
767     {
768         case BASIC_EXPLICIT:
769             bExplicit = true; break;
770         case BASE:
771             if( Next() == NUMBER && ( nVal == 0 || nVal == 1 ) )
772             {
773                 nBase = static_cast<short>(nVal);
774                 break;
775             }
776             Error( ERRCODE_BASIC_EXPECTED, "0/1" );
777             break;
778         case PRIVATE:
779         {
780             OUString aString = SbiTokenizer::Symbol(Next());
781             if( !aString.equalsIgnoreAsciiCase("Module") )
782             {
783                 Error( ERRCODE_BASIC_EXPECTED, "Module" );
784             }
785             break;
786         }
787         case COMPARE:
788         {
789             SbiToken eTok = Next();
790             if( eTok == BINARY )
791             {
792             }
793             else if( eTok == SYMBOL && GetSym().equalsIgnoreAsciiCase("text") )
794             {
795             }
796             else
797             {
798                 Error( ERRCODE_BASIC_EXPECTED, "Text/Binary" );
799             }
800             break;
801         }
802         case COMPATIBLE:
803             EnableCompatibility();
804             break;
805 
806         case CLASSMODULE:
807             bClassModule = true;
808             aGen.GetModule().SetModuleType( css::script::ModuleType::CLASS );
809             break;
810         case VBASUPPORT: // Option VBASupport used to override the module mode ( in fact this must reset the mode
811             if( Next() == NUMBER )
812             {
813                 if ( nVal == 1 || nVal == 0 )
814                 {
815                     bVBASupportOn = ( nVal == 1 );
816                     if ( bVBASupportOn )
817                     {
818                         EnableCompatibility();
819                     }
820                     // if the module setting is different
821                     // reset it to what the Option tells us
822                     if ( bVBASupportOn != aGen.GetModule().IsVBACompat() )
823                     {
824                         aGen.GetModule().SetVBACompat( bVBASupportOn );
825                     }
826                     break;
827                 }
828             }
829             Error( ERRCODE_BASIC_EXPECTED, "0/1" );
830             break;
831         default:
832             Error( ERRCODE_BASIC_BAD_OPTION, eCurTok );
833     }
834 }
835 
addStringConst(SbiSymPool & rPool,const OUString & pSym,const OUString & rStr)836 static void addStringConst( SbiSymPool& rPool, const OUString& pSym, const OUString& rStr )
837 {
838     SbiConstDef* pConst = new SbiConstDef( pSym );
839     pConst->SetType( SbxSTRING );
840     pConst->Set( rStr );
841     rPool.Add( pConst );
842 }
843 
addNumericConst(SbiSymPool & rPool,const OUString & pSym,double nVal)844 static void addNumericConst(SbiSymPool& rPool, const OUString& pSym, double nVal)
845 {
846     SbiConstDef* pConst = new SbiConstDef(pSym);
847     pConst->Set(nVal, SbxDOUBLE);
848     rPool.Add(pConst);
849 }
850 
AddConstants()851 void SbiParser::AddConstants()
852 {
853     // tdf#131563 - add vba color constants
854     // See https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/color-constants
855     addNumericConst(aPublics, "vbBlack", 0x0);
856     addNumericConst(aPublics, "vbRed", 0xFF);
857     addNumericConst(aPublics, "vbGreen", 0xFF00);
858     addNumericConst(aPublics, "vbYellow", 0xFFFF);
859     addNumericConst(aPublics, "vbBlue", 0xFF0000);
860     addNumericConst(aPublics, "vbMagenta", 0xFF00FF);
861     addNumericConst(aPublics, "vbCyan", 0xFFFF00);
862     addNumericConst(aPublics, "vbWhite", 0xFFFFFF);
863 
864     // #113063 Create constant RTL symbols
865     addStringConst( aPublics, "vbCr", "\x0D" );
866     addStringConst( aPublics, "vbCrLf", "\x0D\x0A" );
867     addStringConst( aPublics, "vbFormFeed", "\x0C" );
868     addStringConst( aPublics, "vbLf", "\x0A" );
869 #ifdef _WIN32
870     addStringConst( aPublics, "vbNewLine", "\x0D\x0A" );
871 #else
872     addStringConst( aPublics, "vbNewLine", "\x0A" );
873 #endif
874     addStringConst( aPublics, "vbNullString", "" );
875     addStringConst( aPublics, "vbTab", "\x09" );
876     addStringConst( aPublics, "vbVerticalTab", "\x0B" );
877 
878     addStringConst( aPublics, "vbNullChar", OUString(u'\0') );
879 }
880 
881 // ERROR n
882 
ErrorStmnt()883 void SbiParser::ErrorStmnt()
884 {
885     SbiExpression aPar( this );
886     aPar.Gen();
887     aGen.Gen( SbiOpcode::ERROR_ );
888 }
889 
890 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
891