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