blob: a0ac993c82ef2f6509d442232289aca12ff76fc4 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basic.hxx"
#include <ctype.h>
#include "sbcomp.hxx"
struct TokenTable { SbiToken t; const char *s; };
static short nToken; // Anzahl der Tokens
static TokenTable* pTokTable;
static TokenTable aTokTable_Basic [] = { // Token-Tabelle:
{ CAT, "&" },
{ MUL, "*" },
{ PLUS, "+" },
{ MINUS, "-" },
{ DIV, "/" },
{ EOS, ":" },
{ ASSIGN, ":=" },
{ LT, "<" },
{ LE, "<=" },
{ NE, "<>" },
{ EQ, "=" },
{ GT, ">" },
{ GE, ">=" },
{ ACCESS, "Access" },
{ ALIAS, "Alias" },
{ AND, "And" },
{ ANY, "Any" },
{ APPEND, "Append" },
{ AS, "As" },
{ BASE, "Base" },
{ BINARY, "Binary" },
{ TBOOLEAN, "Boolean" },
{ BYREF, "ByRef", },
{ TBYTE, "Byte", },
{ BYVAL, "ByVal", },
{ CALL, "Call" },
{ CASE, "Case" },
{ _CDECL_, "Cdecl" },
{ CLASSMODULE, "ClassModule" },
{ CLOSE, "Close" },
{ COMPARE, "Compare" },
{ COMPATIBLE,"Compatible" },
{ _CONST_, "Const" },
{ TCURRENCY,"Currency" },
{ TDATE, "Date" },
{ DECLARE, "Declare" },
{ DEFBOOL, "DefBool" },
{ DEFCUR, "DefCur" },
{ DEFDATE, "DefDate" },
{ DEFDBL, "DefDbl" },
{ DEFERR, "DefErr" },
{ DEFINT, "DefInt" },
{ DEFLNG, "DefLng" },
{ DEFOBJ, "DefObj" },
{ DEFSNG, "DefSng" },
{ DEFSTR, "DefStr" },
{ DEFVAR, "DefVar" },
{ DIM, "Dim" },
{ DO, "Do" },
{ TDOUBLE, "Double" },
{ EACH, "Each" },
{ ELSE, "Else" },
{ ELSEIF, "ElseIf" },
{ END, "End" },
{ ENDENUM, "End Enum" },
{ ENDFUNC, "End Function" },
{ ENDIF, "End If" },
{ ENDPROPERTY, "End Property" },
{ ENDSELECT,"End Select" },
{ ENDSUB, "End Sub" },
{ ENDTYPE, "End Type" },
{ ENDIF, "EndIf" },
{ ENUM, "Enum" },
{ EQV, "Eqv" },
{ ERASE, "Erase" },
{ _ERROR_, "Error" },
{ EXIT, "Exit" },
{ BASIC_EXPLICIT, "Explicit" },
{ FOR, "For" },
{ FUNCTION, "Function" },
{ GET, "Get" },
{ GLOBAL, "Global" },
{ GOSUB, "GoSub" },
{ GOTO, "GoTo" },
{ IF, "If" },
{ IMP, "Imp" },
{ IMPLEMENTS, "Implements" },
{ _IN_, "In" },
{ INPUT, "Input" }, // auch INPUT #
{ TINTEGER, "Integer" },
{ IS, "Is" },
{ LET, "Let" },
{ LIB, "Lib" },
{ LIKE, "Like" },
{ LINE, "Line" },
{ LINEINPUT,"Line Input" },
{ LOCAL, "Local" },
{ LOCK, "Lock" },
{ TLONG, "Long" },
{ LOOP, "Loop" },
{ LPRINT, "LPrint" },
{ LSET, "LSet" }, // JSM
{ MOD, "Mod" },
{ NAME, "Name" },
{ NEW, "New" },
{ NEXT, "Next" },
{ NOT, "Not" },
{ TOBJECT, "Object" },
{ ON, "On" },
{ OPEN, "Open" },
{ OPTION, "Option" },
{ _OPTIONAL_, "Optional" },
{ OR, "Or" },
{ OUTPUT, "Output" },
{ PARAMARRAY, "ParamArray" },
{ PRESERVE, "Preserve" },
{ PRINT, "Print" },
{ PRIVATE, "Private" },
{ PROPERTY, "Property" },
{ PUBLIC, "Public" },
{ RANDOM, "Random" },
{ READ, "Read" },
{ REDIM, "ReDim" },
{ REM, "Rem" },
{ RESUME, "Resume" },
{ RETURN, "Return" },
{ RSET, "RSet" }, // JSM
{ SELECT, "Select" },
{ SET, "Set" },
#ifdef SHARED
#undef SHARED
#define tmpSHARED
#endif
{ SHARED, "Shared" },
#ifdef tmpSHARED
#define SHARED
#undef tmpSHARED
#endif
{ TSINGLE, "Single" },
{ STATIC, "Static" },
{ STEP, "Step" },
{ STOP, "Stop" },
{ TSTRING, "String" },
{ SUB, "Sub" },
{ STOP, "System" },
{ TEXT, "Text" },
{ THEN, "Then" },
{ TO, "To", },
{ TYPE, "Type" },
{ TYPEOF, "TypeOf" },
{ UNTIL, "Until" },
{ TVARIANT, "Variant" },
{ VBASUPPORT, "VbaSupport" },
{ WEND, "Wend" },
{ WHILE, "While" },
{ WITH, "With" },
{ WITHEVENTS, "WithEvents" },
{ WRITE, "Write" }, // auch WRITE #
{ XOR, "Xor" },
{ NIL, "" }
};
/*
TokenTable aTokTable_Java [] = { // Token-Tabelle:
{ JS_LOG_NOT, "!" },
{ JS_NE, "!=" },
{ JS_MOD, "%" },
{ JS_ASS_MOD, "%=" },
{ JS_BIT_AND, "&" },
{ JS_LOG_AND, "&&" },
{ JS_ASS_AND, "&=" },
{ JS_LPAREN, "(" },
{ JS_RPAREN, ")" },
{ JS_MUL, "*" },
{ JS_ASS_MUL, "*=" },
{ JS_PLUS, "+" },
{ JS_INC, "++" },
{ JS_ASS_PLUS, "+=" },
{ JS_COMMA, "," },
{ JS_MINUS, "-" },
{ JS_DEC, "--" },
{ JS_ASS_MINUS, "-=" },
{ JS_DIV, "/" },
{ JS_ASS_DIV, "/=" },
{ JS_COND_SEL, ":" },
{ JS_LT, "<" },
{ JS_LSHIFT, "<<" },
{ JS_ASS_LSHIFT,"<<=" },
{ JS_LE, "<=" },
{ JS_NE, "<>" },
{ JS_ASSIGNMENT,"=" },
{ JS_EQ, "==" },
{ JS_GT, ">" },
{ JS_RSHIFT, ">>" },
{ JS_ASS_RSHIFT,">>=" },
{ JS_RSHIFT_Z, ">>>" },
{ JS_ASS_RSHIFT_Z,">>>=" },
{ JS_GE, ">=" },
{ JS_COND_QUEST,"?" },
{ ACCESS, "Access" },
{ ALIAS, "Alias" },
{ AND, "And" },
{ ANY, "Any" },
{ APPEND, "Append" },
{ AS, "As" },
{ BASE, "Base" },
{ BINARY, "Binary" },
{ TBOOLEAN, "Boolean" },
{ BYVAL, "ByVal", },
{ CALL, "Call" },
{ CASE, "Case" },
{ _CDECL_, "Cdecl" },
{ CLOSE, "Close" },
{ COMPARE, "Compare" },
{ _CONST_, "Const" },
{ TCURRENCY,"Currency" },
{ TDATE, "Date" },
{ DECLARE, "Declare" },
{ DEFBOOL, "DefBool" },
{ DEFCUR, "DefCur" },
{ DEFDATE, "DefDate" },
{ DEFDBL, "DefDbl" },
{ DEFERR, "DefErr" },
{ DEFINT, "DefInt" },
{ DEFLNG, "DefLng" },
{ DEFOBJ, "DefObj" },
{ DEFSNG, "DefSng" },
{ DEFSTR, "DefStr" },
{ DEFVAR, "DefVar" },
{ DIM, "Dim" },
{ DO, "Do" },
{ TDOUBLE, "Double" },
{ EACH, "Each" },
{ ELSE, "Else" },
{ ELSEIF, "ElseIf" },
{ END, "End" },
{ ENDFUNC, "End Function" },
{ ENDIF, "End If" },
{ ENDSELECT,"End Select" },
{ ENDSUB, "End Sub" },
{ ENDTYPE, "End Type" },
{ ENDIF, "EndIf" },
{ EQV, "Eqv" },
{ ERASE, "Erase" },
{ _ERROR_, "Error" },
{ EXIT, "Exit" },
{ BASIC_EXPLICIT, "Explicit" },
{ FOR, "For" },
{ FUNCTION, "Function" },
{ GLOBAL, "Global" },
{ GOSUB, "GoSub" },
{ GOTO, "GoTo" },
{ IF, "If" },
{ IMP, "Imp" },
{ _IN_, "In" },
{ INPUT, "Input" }, // auch INPUT #
{ TINTEGER, "Integer" },
{ IS, "Is" },
{ LET, "Let" },
{ LIB, "Lib" },
{ LINE, "Line" },
{ LINEINPUT,"Line Input" },
{ LOCAL, "Local" },
{ LOCK, "Lock" },
{ TLONG, "Long" },
{ LOOP, "Loop" },
{ LPRINT, "LPrint" },
{ LSET, "LSet" }, // JSM
{ MOD, "Mod" },
{ NAME, "Name" },
{ NEW, "New" },
{ NEXT, "Next" },
{ NOT, "Not" },
{ TOBJECT, "Object" },
{ ON, "On" },
{ OPEN, "Open" },
{ OPTION, "Option" },
{ _OPTIONAL_, "Optional" },
{ OR, "Or" },
{ OUTPUT, "Output" },
{ PRESERVE, "Preserve" },
{ PRINT, "Print" },
{ PRIVATE, "Private" },
{ PUBLIC, "Public" },
{ RANDOM, "Random" },
{ READ, "Read" },
{ REDIM, "ReDim" },
{ REM, "Rem" },
{ RESUME, "Resume" },
{ RETURN, "Return" },
{ RSET, "RSet" }, // JSM
{ SELECT, "Select" },
{ SET, "Set" },
{ SHARED, "Shared" },
{ TSINGLE, "Single" },
{ STATIC, "Static" },
{ STEP, "Step" },
{ STOP, "Stop" },
{ TSTRING, "String" },
{ SUB, "Sub" },
{ STOP, "System" },
{ TEXT, "Text" },
{ THEN, "Then" },
{ TO, "To", },
{ TYPE, "Type" },
{ UNTIL, "Until" },
{ TVARIANT, "Variant" },
{ WEND, "Wend" },
{ WHILE, "While" },
{ WITH, "With" },
{ WRITE, "Write" }, // auch WRITE #
{ XOR, "Xor" },
{ JS_LINDEX, "[" },
{ JS_RINDEX, "]" },
{ JS_BIT_XOR, "^" },
{ JS_ASS_XOR, "^=" },
{ JS_BIT_OR, "|" },
{ JS_ASS_OR, "|=" },
{ JS_LOG_OR, "||" },
{ JS_BIT_NOT, "~" },
{ NIL }
};
*/
// #i109076
TokenLabelInfo::TokenLabelInfo( void )
{
m_pTokenCanBeLabelTab = new bool[VBASUPPORT+1];
for( int i = 0 ; i <= VBASUPPORT ; ++i )
m_pTokenCanBeLabelTab[i] = false;
// Token accepted as label by VBA
SbiToken eLabelToken[] = { ACCESS, ALIAS, APPEND, BASE, BINARY, CLASSMODULE,
COMPARE, COMPATIBLE, DEFERR, _ERROR_, BASIC_EXPLICIT, LIB, LINE, LPRINT, NAME,
TOBJECT, OUTPUT, PROPERTY, RANDOM, READ, STEP, STOP, TEXT, VBASUPPORT, NIL };
SbiToken* pTok = eLabelToken;
SbiToken eTok;
for( pTok = eLabelToken ; (eTok = *pTok) != NIL ; ++pTok )
m_pTokenCanBeLabelTab[eTok] = true;
}
TokenLabelInfo::~TokenLabelInfo()
{
delete[] m_pTokenCanBeLabelTab;
}
// Der Konstruktor ermittelt die Laenge der Token-Tabelle.
SbiTokenizer::SbiTokenizer( const ::rtl::OUString& rSrc, StarBASIC* pb )
: SbiScanner( rSrc, pb )
{
pTokTable = aTokTable_Basic;
//if( StarBASIC::GetGlobalLanguageMode() == SB_LANG_JAVASCRIPT )
// pTokTable = aTokTable_Java;
TokenTable *tp;
bEof = bAs = sal_False;
eCurTok = NIL;
ePush = NIL;
bEos = bKeywords = bErrorIsSymbol = sal_True;
if( !nToken )
for( nToken = 0, tp = pTokTable; tp->t; nToken++, tp++ ) {}
}
SbiTokenizer::~SbiTokenizer()
{
}
// Wiederablage (Pushback) eines Tokens. (Bis zu 2 Tokens)
void SbiTokenizer::Push( SbiToken t )
{
if( ePush != NIL )
Error( SbERR_INTERNAL_ERROR, "PUSH" );
else ePush = t;
}
void SbiTokenizer::Error( SbError code, const char* pMsg )
{
aError = String::CreateFromAscii( pMsg );
Error( code );
}
void SbiTokenizer::Error( SbError code, String aMsg )
{
aError = aMsg;
Error( code );
}
void SbiTokenizer::Error( SbError code, SbiToken tok )
{
aError = Symbol( tok );
Error( code );
}
// Einlesen des naechsten Tokens, ohne dass das Token geschluckt wird
SbiToken SbiTokenizer::Peek()
{
if( ePush == NIL )
{
sal_uInt16 nOldLine = nLine;
sal_uInt16 nOldCol1 = nCol1;
sal_uInt16 nOldCol2 = nCol2;
ePush = Next();
nPLine = nLine; nLine = nOldLine;
nPCol1 = nCol1; nCol1 = nOldCol1;
nPCol2 = nCol2; nCol2 = nOldCol2;
}
return eCurTok = ePush;
}
// Dies ist fuer die Decompilation.
// Zahlen und Symbole liefern einen Leerstring zurueck.
const String& SbiTokenizer::Symbol( SbiToken t )
{
// Zeichen-Token?
if( t < FIRSTKWD )
{
aSym = (char) t;
return aSym;
}
switch( t )
{
case NEG : aSym = '-'; return aSym;
case EOS : aSym = String::CreateFromAscii( ":/CRLF" ); return aSym;
case EOLN : aSym = String::CreateFromAscii( "CRLF" ); return aSym;
default: break;
}
TokenTable* tp = pTokTable;
for( short i = 0; i < nToken; i++, tp++ )
{
if( tp->t == t )
{
aSym = String::CreateFromAscii( tp->s );
return aSym;
}
}
const sal_Unicode *p = aSym.GetBuffer();
if (*p <= ' ') aSym = String::CreateFromAscii( "???" );
return aSym;
}
// Einlesen des naechsten Tokens und Ablage desselben
// Tokens, die nicht in der Token-Tabelle vorkommen, werden
// direkt als Zeichen zurueckgeliefert.
// Einige Worte werden gesondert behandelt.
SbiToken SbiTokenizer::Next()
{
if (bEof) return EOLN;
// Schon eines eingelesen?
if( ePush != NIL )
{
eCurTok = ePush;
ePush = NIL;
nLine = nPLine;
nCol1 = nPCol1;
nCol2 = nPCol2;
bEos = IsEoln( eCurTok );
return eCurTok;
}
TokenTable *tp;
// Sonst einlesen:
if( !NextSym() )
{
bEof = bEos = sal_True;
return eCurTok = EOLN;
}
// Zeilenende?
if( aSym.GetBuffer()[0] == '\n' )
{
bEos = sal_True; return eCurTok = EOLN;
}
bEos = sal_False;
// Zahl?
if( bNumber )
return eCurTok = NUMBER;
// String?
else if( ( eScanType == SbxDATE || eScanType == SbxSTRING ) && !bSymbol )
return eCurTok = FIXSTRING;
// Sonderfaelle von Zeichen, die zwischen "Z" und "a" liegen. ICompare()
// wertet die Position dieser Zeichen unterschiedlich aus.
else if( aSym.GetBuffer()[0] == '^' )
return eCurTok = EXPON;
else if( aSym.GetBuffer()[0] == '\\' )
return eCurTok = IDIV;
else
{
// Mit Typkennung oder ein Symbol und keine Keyword-Erkennung?
// Dann kein Token-Test
if( eScanType != SbxVARIANT
|| ( !bKeywords && bSymbol ) )
return eCurTok = SYMBOL;
// Gueltiges Token?
short lb = 0;
short ub = nToken-1;
short delta;
do
{
delta = (ub - lb) >> 1;
tp = &pTokTable[ lb + delta ];
StringCompare res = aSym.CompareIgnoreCaseToAscii( tp->s );
// Gefunden?
if( res == COMPARE_EQUAL )
goto special;
// Groesser? Dann untere Haelfte
if( res == COMPARE_LESS )
{
if ((ub - lb) == 2) ub = lb;
else ub = ub - delta;
}
// Kleiner? Dann obere Haelfte
else
{
if ((ub -lb) == 2) lb = ub;
else lb = lb + delta;
}
} while( delta );
// Symbol? Wenn nicht >= Token
sal_Unicode ch = aSym.GetBuffer()[0];
if( !BasicSimpleCharClass::isAlpha( ch, bCompatible ) && !bSymbol )
return eCurTok = (SbiToken) (ch & 0x00FF);
return eCurTok = SYMBOL;
}
special:
// #i92642
bool bStartOfLine = (eCurTok == NIL || eCurTok == REM || eCurTok == EOLN);
if( !bStartOfLine && (tp->t == NAME || tp->t == LINE) )
return eCurTok = SYMBOL;
else if( tp->t == TEXT )
return eCurTok = SYMBOL;
// #i92642: Special LINE token handling -> SbiParser::Line()
// END IF, CASE, SUB, DEF, FUNCTION, TYPE, CLASS, WITH
if( tp->t == END )
{
// AB, 15.3.96, Spezialbehandlung fuer END, beim Peek() geht die
// aktuelle Zeile verloren, daher alles merken und danach restaurieren
sal_uInt16 nOldLine = nLine;
sal_uInt16 nOldCol = nCol;
sal_uInt16 nOldCol1 = nCol1;
sal_uInt16 nOldCol2 = nCol2;
String aOldSym = aSym;
SaveLine(); // pLine im Scanner sichern
eCurTok = Peek();
switch( eCurTok )
{
case IF: Next(); eCurTok = ENDIF; break;
case SELECT: Next(); eCurTok = ENDSELECT; break;
case SUB: Next(); eCurTok = ENDSUB; break;
case FUNCTION: Next(); eCurTok = ENDFUNC; break;
case PROPERTY: Next(); eCurTok = ENDPROPERTY; break;
case TYPE: Next(); eCurTok = ENDTYPE; break;
case ENUM: Next(); eCurTok = ENDENUM; break;
case WITH: Next(); eCurTok = ENDWITH; break;
default : eCurTok = END;
}
nCol1 = nOldCol1;
if( eCurTok == END )
{
// Alles zuruecksetzen, damit Token nach END ganz neu gelesen wird
ePush = NIL;
nLine = nOldLine;
nCol = nOldCol;
nCol2 = nOldCol2;
aSym = aOldSym;
RestoreLine(); // pLine im Scanner restaurieren
}
return eCurTok;
}
// Sind Datentypen Keywords?
// Nur nach AS, sonst sind es Symbole!
// Es gibt ja ERROR(), DATA(), STRING() etc.
eCurTok = tp->t;
// AS: Datentypen sind Keywords
if( tp->t == AS )
bAs = sal_True;
else
{
if( bAs )
bAs = sal_False;
else if( eCurTok >= DATATYPE1 && eCurTok <= DATATYPE2 && (bErrorIsSymbol || eCurTok != _ERROR_) )
eCurTok = SYMBOL;
}
// CLASSMODULE, PROPERTY, GET, ENUM token only visible in compatible mode
SbiToken eTok = tp->t;
if( bCompatible )
{
// #129904 Suppress system
if( eTok == STOP && aSym.CompareIgnoreCaseToAscii( "system" ) == COMPARE_EQUAL )
eCurTok = SYMBOL;
if( eTok == GET && bStartOfLine )
eCurTok = SYMBOL;
}
else
{
if( eTok == CLASSMODULE ||
eTok == IMPLEMENTS ||
eTok == PARAMARRAY ||
eTok == ENUM ||
eTok == PROPERTY ||
eTok == GET ||
eTok == TYPEOF )
{
eCurTok = SYMBOL;
}
}
bEos = IsEoln( eCurTok );
return eCurTok;
}
#ifdef _MSC_VER
#pragma optimize("",off)
#endif
// Kann das aktuell eingelesene Token ein Label sein?
sal_Bool SbiTokenizer::MayBeLabel( sal_Bool bNeedsColon )
{
if( eCurTok == SYMBOL || m_aTokenLabelInfo.canTokenBeLabel( eCurTok ) )
return bNeedsColon ? DoesColonFollow() : sal_True;
else
return sal_Bool( eCurTok == NUMBER
&& eScanType == SbxINTEGER
&& nVal >= 0 );
}
#ifdef _MSC_VER
#pragma optimize("",off)
#endif
void SbiTokenizer::Hilite( SbTextPortions& rList )
{
bErrors = sal_False;
bUsedForHilite = sal_True;
SbiToken eLastTok = NIL;
for( ;; )
{
Next();
if( IsEof() )
break;
SbTextPortion aRes;
aRes.nLine = nLine;
aRes.nStart = nCol1;
aRes.nEnd = nCol2;
switch( eCurTok )
{
case REM:
aRes.eType = SB_COMMENT; break;
case SYMBOL:
aRes.eType = SB_SYMBOL; break;
case FIXSTRING:
aRes.eType = SB_STRING; break;
case NUMBER:
aRes.eType = SB_NUMBER; break;
default:
if( ( eCurTok >= FIRSTKWD && eCurTok <= LASTKWD )
|| (eCurTok >= _CDECL_ ) )
aRes.eType = SB_KEYWORD;
else
aRes.eType = SB_PUNCTUATION;
}
// Die Folge xxx.Keyword sollte nicht als Kwd geflagt werden
if( aRes.eType == SB_KEYWORD
&& ( eLastTok == DOT|| eLastTok == EXCLAM ) )
aRes.eType = SB_SYMBOL;
if( eCurTok != EOLN && aRes.nStart <= aRes.nEnd )
rList.Insert( aRes, rList.Count() );
if( aRes.eType == SB_COMMENT )
break;
eLastTok = eCurTok;
}
bUsedForHilite = sal_False;
}