1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <basic/sbx.hxx>
22 #include <com/sun/star/script/ModuleType.hpp>
24 struct SbiParseStack
{ // "Stack" for statement-blocks
25 SbiParseStack
* pNext
; // Chain
26 SbiExprNode
* pWithVar
;
28 sal_uInt32 nChain
; // JUMP-Chain
33 void( SbiParser::*Func
)();
34 sal_Bool bMain
; // sal_True: OK outside the SUB
35 sal_Bool bSubr
; // sal_True: OK inside the SUB
41 static SbiStatement StmntTable
[] = {
42 { ATTRIBUTE
, &SbiParser::Attribute
, Y
, Y
, }, // ATTRIBUTE
43 { CALL
, &SbiParser::Call
, N
, Y
, }, // CALL
44 { CLOSE
, &SbiParser::Close
, N
, Y
, }, // CLOSE
45 { _CONST_
, &SbiParser::Dim
, Y
, Y
, }, // CONST
46 { DECLARE
, &SbiParser::Declare
, Y
, N
, }, // DECLARE
47 { DEFBOOL
, &SbiParser::DefXXX
, Y
, N
, }, // DEFBOOL
48 { DEFCUR
, &SbiParser::DefXXX
, Y
, N
, }, // DEFCUR
49 { DEFDATE
, &SbiParser::DefXXX
, Y
, N
, }, // DEFDATE
50 { DEFDBL
, &SbiParser::DefXXX
, Y
, N
, }, // DEFDBL
51 { DEFERR
, &SbiParser::DefXXX
, Y
, N
, }, // DEFERR
52 { DEFINT
, &SbiParser::DefXXX
, Y
, N
, }, // DEFINT
53 { DEFLNG
, &SbiParser::DefXXX
, Y
, N
, }, // DEFLNG
54 { DEFOBJ
, &SbiParser::DefXXX
, Y
, N
, }, // DEFOBJ
55 { DEFSNG
, &SbiParser::DefXXX
, Y
, N
, }, // DEFSNG
56 { DEFSTR
, &SbiParser::DefXXX
, Y
, N
, }, // DEFSTR
57 { DEFVAR
, &SbiParser::DefXXX
, Y
, N
, }, // DEFVAR
58 { DIM
, &SbiParser::Dim
, Y
, Y
, }, // DIM
59 { DO
, &SbiParser::DoLoop
, N
, Y
, }, // DO
60 { ELSE
, &SbiParser::NoIf
, N
, Y
, }, // ELSE
61 { ELSEIF
, &SbiParser::NoIf
, N
, Y
, }, // ELSEIF
62 { ENDIF
, &SbiParser::NoIf
, N
, Y
, }, // ENDIF
63 { END
, &SbiParser::Stop
, N
, Y
, }, // END
64 { ENUM
, &SbiParser::Enum
, Y
, N
, }, // TYPE
65 { ERASE
, &SbiParser::Erase
, N
, Y
, }, // ERASE
66 { _ERROR_
, &SbiParser::ErrorStmnt
, N
, Y
, }, // ERROR
67 { EXIT
, &SbiParser::Exit
, N
, Y
, }, // EXIT
68 { FOR
, &SbiParser::For
, N
, Y
, }, // FOR
69 { FUNCTION
, &SbiParser::SubFunc
, Y
, N
, }, // FUNCTION
70 { GOSUB
, &SbiParser::Goto
, N
, Y
, }, // GOSUB
71 { GLOBAL
, &SbiParser::Dim
, Y
, N
, }, // GLOBAL
72 { GOTO
, &SbiParser::Goto
, N
, Y
, }, // GOTO
73 { IF
, &SbiParser::If
, N
, Y
, }, // IF
74 { IMPLEMENTS
, &SbiParser::Implements
, Y
, N
, }, // IMPLEMENTS
75 { INPUT
, &SbiParser::Input
, N
, Y
, }, // INPUT
76 { LET
, &SbiParser::Assign
, N
, Y
, }, // LET
77 { LINE
, &SbiParser::Line
, N
, Y
, }, // LINE, -> LINE INPUT (#i92642)
78 { LINEINPUT
,&SbiParser::LineInput
, N
, Y
, }, // LINE INPUT
79 { LOOP
, &SbiParser::BadBlock
, N
, Y
, }, // LOOP
80 { LSET
, &SbiParser::LSet
, N
, Y
, }, // LSET
81 { NAME
, &SbiParser::Name
, N
, Y
, }, // NAME
82 { NEXT
, &SbiParser::BadBlock
, N
, Y
, }, // NEXT
83 { ON
, &SbiParser::On
, N
, Y
, }, // ON
84 { OPEN
, &SbiParser::Open
, N
, Y
, }, // OPEN
85 { OPTION
, &SbiParser::Option
, Y
, N
, }, // OPTION
86 { PRINT
, &SbiParser::Print
, N
, Y
, }, // PRINT
87 { PRIVATE
, &SbiParser::Dim
, Y
, N
, }, // PRIVATE
88 { PROPERTY
, &SbiParser::SubFunc
, Y
, N
, }, // FUNCTION
89 { PUBLIC
, &SbiParser::Dim
, Y
, N
, }, // PUBLIC
90 { REDIM
, &SbiParser::ReDim
, N
, Y
, }, // DIM
91 { RESUME
, &SbiParser::Resume
, N
, Y
, }, // RESUME
92 { RETURN
, &SbiParser::Return
, N
, Y
, }, // RETURN
93 { RSET
, &SbiParser::RSet
, N
, Y
, }, // RSET
94 { SELECT
, &SbiParser::Select
, N
, Y
, }, // SELECT
95 { SET
, &SbiParser::Set
, N
, Y
, }, // SET
96 { STATIC
, &SbiParser::Static
, Y
, Y
, }, // STATIC
97 { STOP
, &SbiParser::Stop
, N
, Y
, }, // STOP
98 { SUB
, &SbiParser::SubFunc
, Y
, N
, }, // SUB
99 { TYPE
, &SbiParser::Type
, Y
, N
, }, // TYPE
100 { UNTIL
, &SbiParser::BadBlock
, N
, Y
, }, // UNTIL
101 { WHILE
, &SbiParser::While
, N
, Y
, }, // WHILE
102 { WEND
, &SbiParser::BadBlock
, N
, Y
, }, // WEND
103 { WITH
, &SbiParser::With
, N
, Y
, }, // WITH
104 { WRITE
, &SbiParser::Write
, N
, Y
, }, // WRITE
111 // 'this' : used in base member initializer list
112 #pragma warning( disable: 4355 )
115 SbiParser::SbiParser( StarBASIC
* pb
, SbModule
* pm
)
116 : SbiTokenizer( pm
->GetSource32(), pb
),
119 aGlobals( aGblStrings
, SbGLOBAL
),
120 aPublics( aGblStrings
, SbPUBLIC
),
121 aRtlSyms( aGblStrings
, SbRTL
),
122 aGen( *pm
, this, 1024 )
136 bClassModule
= ( pm
->GetModuleType() == com::sun::star::script::ModuleType::CLASS
);
137 OSL_TRACE("Parser - %s, bClassModule %d", rtl::OUStringToOString( pm
->GetName(), RTL_TEXTENCODING_UTF8
).getStr(), bClassModule
);
139 for( short i
= 0; i
< 26; i
++ )
140 eDefTypes
[ i
] = SbxVARIANT
; // no explicit default type
142 aPublics
.SetParent( &aGlobals
);
143 aGlobals
.SetParent( &aRtlSyms
);
146 nGblChain
= aGen
.Gen( _JUMP
, 0 );
148 rTypeArray
= new SbxArray
; // array for user defined types
149 rEnumArray
= new SbxArray
; // array for Enum types
150 bVBASupportOn
= pm
->IsVBACompat();
152 EnableCompatibility();
157 // part of the runtime-library?
158 SbiSymDef
* SbiParser::CheckRTLForSym( const OUString
& rSym
, SbxDataType eType
)
160 SbxVariable
* pVar
= GetBasic()->GetRtl()->Find( rSym
, SbxCLASS_DONTCARE
);
161 SbiSymDef
* pDef
= NULL
;
164 if( pVar
->IsA( TYPE(SbxMethod
) ) )
166 SbiProcDef
* pProc_
= aRtlSyms
.AddProc( rSym
);
167 pProc_
->SetType( pVar
->GetType() );
172 pDef
= aRtlSyms
.AddSym( rSym
);
173 pDef
->SetType( eType
);
179 // close global chain
181 bool SbiParser::HasGlobalCode()
183 if( bGblDefs
&& nGblChain
)
185 aGen
.BackChain( nGblChain
);
192 void SbiParser::OpenBlock( SbiToken eTok
, SbiExprNode
* pVar
)
194 SbiParseStack
* p
= new SbiParseStack
;
197 p
->pWithVar
= pWithVar
;
202 // #29955 service the for-loop level
207 void SbiParser::CloseBlock()
211 SbiParseStack
* p
= pStack
;
213 // #29955 service the for-loop level
214 if( p
->eExitTok
== FOR
)
217 aGen
.BackChain( p
->nChain
);
219 pWithVar
= p
->pWithVar
;
226 void SbiParser::Exit()
228 SbiToken eTok
= Next();
229 for( SbiParseStack
* p
= pStack
; p
; p
= p
->pNext
)
231 SbiToken eExitTok
= p
->eExitTok
;
232 if( eTok
== eExitTok
||
233 (eTok
== PROPERTY
&& (eExitTok
== GET
|| eExitTok
== LET
) ) ) // #i109051
235 p
->nChain
= aGen
.Gen( _JUMP
, p
->nChain
);
240 Error( SbERR_EXPECTED
, pStack
->eExitTok
);
242 Error( SbERR_BAD_EXIT
);
245 bool SbiParser::TestSymbol( bool bKwdOk
)
248 if( eCurTok
== SYMBOL
|| ( bKwdOk
&& IsKwd( eCurTok
) ) )
252 Error( SbERR_SYMBOL_EXPECTED
);
258 bool SbiParser::TestToken( SbiToken t
)
266 Error( SbERR_EXPECTED
, t
);
273 bool SbiParser::TestComma()
275 SbiToken eTok
= Peek();
281 else if( eTok
!= COMMA
)
283 Error( SbERR_EXPECTED
, COMMA
);
292 void SbiParser::TestEoln()
294 if( !IsEoln( Next() ) )
296 Error( SbERR_EXPECTED
, EOLN
);
297 while( !IsEoln( Next() ) ) {}
303 void SbiParser::StmntBlock( SbiToken eEnd
)
305 SbiToken xe
= eEndTok
;
307 while( !bAbort
&& Parse() ) {}
311 Error( SbERR_BAD_BLOCK
, eEnd
);
318 bool SbiParser::Parse()
320 if( bAbort
) return false;
324 bErrorIsSymbol
= false;
326 bErrorIsSymbol
= true;
330 // AB #33133: If no sub has been created before,
331 // the global chain must be closed here!
332 // AB #40689: Due to the new static-handling there
333 // can be another nGblChain, so ask for it before.
334 if( bNewGblDefs
&& nGblChain
== 0 )
335 nGblChain
= aGen
.Gen( _JUMP
, 0 );
340 if( IsEoln( eCurTok
) )
345 if( !bSingleLineIf
&& MayBeLabel( true ) )
349 Error( SbERR_NOT_IN_MAIN
, aSym
);
351 pProc
->GetLabels().Define( aSym
);
354 if( IsEoln( eCurTok
) )
361 if( eCurTok
== eEndTok
||
362 ( bVBASupportOn
&& // #i109075
363 (eCurTok
== ENDFUNC
|| eCurTok
== ENDPROPERTY
|| eCurTok
== ENDSUB
) &&
364 (eEndTok
== ENDFUNC
|| eEndTok
== ENDPROPERTY
|| eEndTok
== ENDSUB
) ) )
378 // In vba it's possible to do Error.foobar ( even if it results in
380 if ( eCurTok
== _ERROR_
&& IsVBASupportOn() ) // we probably need to define a subset of keywords where this madness applies e.g. if ( IsVBASupportOn() && SymbolCanBeRedined( eCurTok ) )
382 SbiTokenizer
tokens( *(SbiTokenizer
*)this );
384 if ( tokens
.Peek() == DOT
)
390 // if there's a symbol, it's either a variable (LET)
391 // or a SUB-procedure (CALL without brackets)
392 // DOT for assignments in the WITH-block: .A=5
393 if( eCurTok
== SYMBOL
|| eCurTok
== DOT
)
396 Error( SbERR_EXPECTED
, SUB
);
399 // for correct line and column...
413 for( p
= StmntTable
; p
->eTok
!= NIL
; p
++ )
414 if( p
->eTok
== eCurTok
)
418 if( !pProc
&& !p
->bMain
)
419 Error( SbERR_NOT_IN_MAIN
, eCurTok
);
420 else if( pProc
&& !p
->bSubr
)
421 Error( SbERR_NOT_IN_SUBR
, eCurTok
);
424 // AB #41606/#40689: Due to the new static-handling there
425 // can be another nGblChain, so ask for it before.
426 if( bNewGblDefs
&& nGblChain
== 0 &&
427 ( eCurTok
== SUB
|| eCurTok
== FUNCTION
|| eCurTok
== PROPERTY
) )
429 nGblChain
= aGen
.Gen( _JUMP
, 0 );
432 // statement-opcode at the beginning of a sub, too, please
433 if( ( p
->bSubr
&& (eCurTok
!= STATIC
|| Peek() == SUB
|| Peek() == FUNCTION
) ) ||
434 eCurTok
== SUB
|| eCurTok
== FUNCTION
)
436 (this->*( p
->Func
) )();
437 SbxError nSbxErr
= SbxBase::GetError();
439 SbxBase::ResetError(), Error( (SbError
)nSbxErr
);
443 Error( SbERR_UNEXPECTED
, eCurTok
);
446 // test for the statement's end -
447 // might also be an ELSE, as there must not neccessary be a : before the ELSE!
452 if( !IsEos() && eCurTok
!= ELSE
)
454 // if the parsing has been aborted, jump over to the ":"
455 Error( SbERR_UNEXPECTED
, eCurTok
);
456 while( !IsEos() ) Next();
459 // The parser aborts at the end, the
460 // next token has not been fetched yet!
465 SbiExprNode
* SbiParser::GetWithVar()
470 SbiParseStack
* p
= pStack
;
473 // LoopVar can at the moment only be for with
482 // assignment or subroutine call
484 void SbiParser::Symbol( const KeywordSymbolInfo
* pKeywordSymbolInfo
)
486 SbiExprMode eMode
= bVBASupportOn
? EXPRMODE_STANDALONE
: EXPRMODE_STANDARD
;
487 SbiExpression
aVar( this, SbSYMBOL
, eMode
, pKeywordSymbolInfo
);
489 bool bEQ
= ( Peek() == EQ
);
490 if( !bEQ
&& bVBASupportOn
&& aVar
.IsBracket() )
491 Error( SbERR_EXPECTED
, "=" );
493 RecursiveMode eRecMode
= ( bEQ
? PREVENT_CALL
: FORCE_CALL
);
494 bool bSpecialMidHandling
= false;
495 SbiSymDef
* pDef
= aVar
.GetRealVar();
496 if( bEQ
&& pDef
&& pDef
->GetScope() == SbRTL
)
498 OUString aRtlName
= pDef
->GetName();
499 if( aRtlName
.equalsIgnoreAsciiCase("Mid") )
501 SbiExprNode
* pExprNode
= aVar
.GetExprNode();
502 if( pExprNode
&& pExprNode
->GetNodeType() == SbxVARVAL
)
504 SbiExprList
* pPar
= pExprNode
->GetParameters();
505 short nParCount
= pPar
? pPar
->GetSize() : 0;
506 if( nParCount
== 2 || nParCount
== 3 )
509 pPar
->addExpression( new SbiExpression( this, -1, SbxLONG
) );
512 pPar
->addExpression( new SbiExpression( this ) );
514 bSpecialMidHandling
= true;
519 aVar
.Gen( eRecMode
);
520 if( !bSpecialMidHandling
)
528 // so it must be an assignment!
529 if( !aVar
.IsLvalue() )
530 Error( SbERR_LVALUE_EXPECTED
);
532 SbiExpression
aExpr( this );
534 SbiOpcode eOp
= _PUT
;
537 if( pDef
->GetConstDef() )
538 Error( SbERR_DUPLICATE_DEF
, pDef
->GetName() );
539 if( pDef
->GetType() == SbxOBJECT
)
542 if( pDef
->GetTypeId() )
544 aGen
.Gen( _SETCLASS
, pDef
->GetTypeId() );
555 void SbiParser::Assign()
557 SbiExpression
aLvalue( this, SbLVALUE
);
559 SbiExpression
aExpr( this );
563 SbiSymDef
* pDef
= aLvalue
.GetRealVar();
565 if( pDef
->GetConstDef() )
566 Error( SbERR_DUPLICATE_DEF
, pDef
->GetName() );
567 nLen
= aLvalue
.GetRealVar()->GetLen();
570 aGen
.Gen( _PAD
, nLen
);
574 // assignments of an object-variable
576 void SbiParser::Set()
578 SbiExpression
aLvalue( this, SbLVALUE
);
579 SbxDataType eType
= aLvalue
.GetType();
580 if( eType
!= SbxOBJECT
&& eType
!= SbxEMPTY
&& eType
!= SbxVARIANT
)
581 Error( SbERR_INVALID_OBJECT
);
583 SbiSymDef
* pDef
= aLvalue
.GetRealVar();
584 if( pDef
&& pDef
->GetConstDef() )
585 Error( SbERR_DUPLICATE_DEF
, pDef
->GetName() );
587 SbiToken eTok
= Peek();
592 SbiSymDef
* pTypeDef
= new SbiSymDef( aStr
);
593 TypeDecl( *pTypeDef
, sal_True
);
596 aGen
.Gen( _CREATE
, pDef
->GetId(), pTypeDef
->GetTypeId() );
597 aGen
.Gen( _SETCLASS
, pDef
->GetTypeId() );
601 SbiExpression
aExpr( this );
604 // Its a good idea to distinguish between
605 // set someting = another &
606 // someting = another
607 // ( its necessary for vba objects where set is object
608 // specific and also doesn't involve processing default params )
609 if( pDef
->GetTypeId() )
612 aGen
.Gen( _VBASETCLASS
, pDef
->GetTypeId() );
614 aGen
.Gen( _SETCLASS
, pDef
->GetTypeId() );
627 void SbiParser::LSet()
629 SbiExpression
aLvalue( this, SbLVALUE
);
630 if( aLvalue
.GetType() != SbxSTRING
)
632 Error( SbERR_INVALID_OBJECT
);
635 SbiSymDef
* pDef
= aLvalue
.GetRealVar();
636 if( pDef
&& pDef
->GetConstDef() )
638 Error( SbERR_DUPLICATE_DEF
, pDef
->GetName() );
640 SbiExpression
aExpr( this );
647 void SbiParser::RSet()
649 SbiExpression
aLvalue( this, SbLVALUE
);
650 if( aLvalue
.GetType() != SbxSTRING
)
652 Error( SbERR_INVALID_OBJECT
);
655 SbiSymDef
* pDef
= aLvalue
.GetRealVar();
656 if( pDef
&& pDef
->GetConstDef() )
657 Error( SbERR_DUPLICATE_DEF
, pDef
->GetName() );
658 SbiExpression
aExpr( this );
664 // DEFINT, DEFLNG, DEFSNG, DEFDBL, DEFSTR and so on
666 void SbiParser::DefXXX()
668 sal_Unicode ch1
, ch2
;
669 SbxDataType t
= SbxDataType( eCurTok
- DEFINT
+ SbxINTEGER
);
673 if( Next() != SYMBOL
) break;
674 ch1
= aSym
.toAsciiUpperCase()[0];
676 if( Peek() == MINUS
)
679 if( Next() != SYMBOL
) Error( SbERR_SYMBOL_EXPECTED
);
682 ch2
= aSym
.toAsciiUpperCase()[0];
683 if( ch2
< ch1
) Error( SbERR_SYNTAX
), ch2
= 0;
687 ch1
-= 'A'; ch2
-= 'A';
688 for (; ch1
<= ch2
; ch1
++) eDefTypes
[ ch1
] = t
;
689 if( !TestComma() ) break;
695 void SbiParser::Stop()
698 Peek(); // #35694: only Peek(), so that EOL is recognized in Single-Line-If
703 void SbiParser::Implements()
707 Error( SbERR_UNEXPECTED
, IMPLEMENTS
);
712 if( eCurTok
!= SYMBOL
)
714 Error( SbERR_SYMBOL_EXPECTED
);
718 OUString aImplementedIface
= aSym
;
722 OUString
aDotStr( '.' );
723 while( Peek() == DOT
)
725 aImplementedIface
+= aDotStr
;
727 SbiToken ePeekTok
= Peek();
728 if( ePeekTok
== SYMBOL
|| IsKwd( ePeekTok
) )
731 aImplementedIface
+= aSym
;
736 Error( SbERR_SYMBOL_EXPECTED
);
741 aIfaceVector
.push_back( aImplementedIface
);
744 void SbiParser::EnableCompatibility()
748 bCompatible
= sal_True
;
753 void SbiParser::Option()
758 bExplicit
= true; break;
760 if( Next() == NUMBER
)
762 if( nVal
== 0 || nVal
== 1 )
764 nBase
= (short) nVal
;
768 Error( SbERR_EXPECTED
, "0/1" );
772 OUString aString
= SbiTokenizer::Symbol(Next());
773 if( !aString
.equalsIgnoreAsciiCase("Module") )
775 Error( SbERR_EXPECTED
, "Module" );
781 SbiToken eTok
= Next();
786 else if( eTok
== SYMBOL
&& GetSym().equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("text")) )
792 Error( SbERR_EXPECTED
, "Text/Binary" );
797 EnableCompatibility();
802 aGen
.GetModule().SetModuleType( com::sun::star::script::ModuleType::CLASS
);
804 case VBASUPPORT
: // Option VBASupport used to override the module mode ( in fact this must reset the mode
805 if( Next() == NUMBER
)
807 if ( nVal
== 1 || nVal
== 0 )
809 bVBASupportOn
= ( nVal
== 1 );
812 EnableCompatibility();
814 // if the module setting is different
815 // reset it to what the Option tells us
816 if ( bVBASupportOn
!= aGen
.GetModule().IsVBACompat() )
818 aGen
.GetModule().SetVBACompat( bVBASupportOn
);
823 Error( SbERR_EXPECTED
, "0/1" );
826 Error( SbERR_BAD_OPTION
, eCurTok
);
830 void addStringConst( SbiSymPool
& rPool
, const char* pSym
, const OUString
& rStr
)
832 SbiConstDef
* pConst
= new SbiConstDef( OUString::createFromAscii( pSym
) );
833 pConst
->SetType( SbxSTRING
);
838 inline void addStringConst( SbiSymPool
& rPool
, const char* pSym
, const char* pStr
)
840 addStringConst( rPool
, pSym
, rtl::OUString::createFromAscii( pStr
) );
843 void SbiParser::AddConstants( void )
845 // #113063 Create constant RTL symbols
846 addStringConst( aPublics
, "vbCr", "\x0D" );
847 addStringConst( aPublics
, "vbCrLf", "\x0D\x0A" );
848 addStringConst( aPublics
, "vbFormFeed", "\x0C" );
849 addStringConst( aPublics
, "vbLf", "\x0A" );
851 addStringConst( aPublics
, "vbNewLine", "\x0A" );
853 addStringConst( aPublics
, "vbNewLine", "\x0D\x0A" );
855 addStringConst( aPublics
, "vbNullString", "" );
856 addStringConst( aPublics
, "vbTab", "\x09" );
857 addStringConst( aPublics
, "vbVerticalTab", "\x0B" );
859 // Force length 1 and make char 0 afterwards
860 OUString
aNullCharStr((sal_Unicode
)0);
861 addStringConst( aPublics
, "vbNullChar", aNullCharStr
);
866 void SbiParser::ErrorStmnt()
868 SbiExpression
aPar( this );
873 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */