Files
libreoffice/basic/source/comp/scanner.cxx

494 lines
14 KiB
C++
Raw Normal View History

2000-09-18 15:18:56 +00:00
/*************************************************************************
*
* $RCSfile: scanner.cxx,v $
*
* $Revision: 1.2 $
2000-09-18 15:18:56 +00:00
*
* last change: $Author: ab $ $Date: 2001-05-17 09:43:07 $
2000-09-18 15:18:56 +00:00
*
* The Contents of this file are made available subject to the terms of
* either of the following licenses
*
* - GNU Lesser General Public License Version 2.1
* - Sun Industry Standards Source License Version 1.1
*
* Sun Microsystems Inc., October, 2000
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2000 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*
* Sun Industry Standards Source License Version 1.1
* =================================================
* The contents of this file are subject to the Sun Industry Standards
* Source License Version 1.1 (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.openoffice.org/license.html.
*
* Software provided under this License is provided on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
* See the License for the specific provisions governing your rights and
* obligations concerning the Software.
*
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
*
* Copyright: 2000 by Sun Microsystems, Inc.
*
* All Rights Reserved.
*
* Contributor(s): _______________________________________
*
*
************************************************************************/
#include "sbcomp.hxx"
#pragma hdrstop
#include <ctype.h>
#include <stdio.h> // sprintf()
#include <string.h>
#if defined (ICC) || defined (WTC) || defined(__powerc) || defined ( MAC ) || defined UNX
#include <stdlib.h>
#else
#include <math.h> // atof()
#endif
#ifndef _SOLMATH_HXX //autogen wg. SolarMath
#include <tools/solmath.hxx>
#endif
#ifndef _TOOLS_INTN_HXX
#include <tools/intn.hxx>
#endif
#include "segmentc.hxx"
#pragma SW_SEGMENT_CLASS( SBCOMP, SBCOMP_CODE )
SbiScanner::SbiScanner( const String& rBuf, StarBASIC* p ) : aBuf( rBuf )
{
pBasic = p;
pLine = NULL;
nVal = 0;
eScanType = SbxVARIANT;
nErrors =
nBufPos =
nCurCol1 =
nSavedCol1 =
nColLock =
nLine =
nCol1 =
nCol2 =
nCol = 0;
bError =
bAbort =
bSpaces =
bNumber =
bSymbol =
bUsedForHilite = FALSE;
bHash =
bErrors = TRUE;
}
SbiScanner::~SbiScanner()
{}
void SbiScanner::LockColumn()
{
if( !nColLock++ )
nSavedCol1 = nCol1;
}
void SbiScanner::UnlockColumn()
{
if( nColLock )
nColLock--;
}
void SbiScanner::GenError( SbError code )
{
if( !bError && bErrors )
{
BOOL bRes = TRUE;
// Nur einen Fehler pro Statement reporten
bError = TRUE;
if( pBasic )
{
// Falls EXPECTED oder UNEXPECTED kommen sollte, bezieht es sich
// immer auf das letzte Token, also die Col1 uebernehmen
USHORT nc = nColLock ? nSavedCol1 : nCol1;
switch( code )
{
case SbERR_EXPECTED:
case SbERR_UNEXPECTED:
case SbERR_SYMBOL_EXPECTED:
case SbERR_LABEL_EXPECTED:
nc = nCol1;
if( nc > nCol2 ) nCol2 = nc;
break;
}
bRes = pBasic->CError( code, aError, nLine, nc, nCol2 );
}
bAbort |= !bRes |
( code == SbERR_NO_MEMORY || code == SbERR_PROG_TOO_LARGE );
}
if( bErrors )
nErrors++;
}
// Falls sofort ein Doppelpunkt folgt, wird TRUE zurueckgeliefert.
// Wird von SbiTokenizer::MayBeLabel() verwendet, um einen Label zu erkennen
BOOL SbiScanner::DoesColonFollow()
{
if( pLine && *pLine == ':' )
{
pLine++; nCol++; return TRUE;
}
else return FALSE;
}
// Testen auf ein legales Suffix
static SbxDataType GetSuffixType( sal_Unicode c )
{
static String aSuffixesStr = String::CreateFromAscii( "%&!#@ $" );
if( c )
{
sal_uInt32 n = aSuffixesStr.Search( c );
if( STRING_NOTFOUND != n && c != ' ' )
return SbxDataType( (USHORT) n + SbxINTEGER );
}
return SbxVARIANT;
}
// Einlesen des naechsten Symbols in die Variablen aSym, nVal und eType
// Returnwert ist FALSE bei EOF oder Fehlern
#define BUF_SIZE 80
BOOL SbiScanner::NextSym()
{
static International aEnglischIntn( LANGUAGE_ENGLISH_US, LANGUAGE_ENGLISH_US );
// Fuer den EOLN-Fall merken
USHORT nOldLine = nLine;
USHORT nOldCol1 = nCol1;
USHORT nOldCol2 = nCol2;
sal_Unicode buf[ BUF_SIZE ], *p = buf;
bHash = FALSE;
eScanType = SbxVARIANT;
aSym.Erase();
bSymbol =
bNumber = bSpaces = FALSE;
// Zeile einlesen?
if( !pLine )
{
USHORT n = nBufPos;
USHORT nLen = aBuf.Len();
if( nBufPos >= nLen )
return FALSE;
const sal_Unicode* p = aBuf.GetBuffer();
p += n;
while( ( n < nLen ) && ( *p != '\n' ) && ( *p != '\r' ) )
p++, n++;
aLine = aBuf.Copy( nBufPos, n - nBufPos );
if( ( n < nLen ) && *p == '\r' && *( p+1 ) == '\n' )
n += 2;
else
n++;
nBufPos = n;
pLine = aLine.GetBuffer();
nOldLine = ++nLine;
nCol = nCol1 = nCol2 = nOldCol1 = nOldCol2 = 0;
nColLock = 0;
}
// Leerstellen weg:
while( *pLine && ( *pLine == ' ' ) || ( *pLine == '\t' ) || ( *pLine == '\f' ) )
pLine++, nCol++, bSpaces = TRUE;
nCol1 = nCol;
// nur Leerzeile?
if( !*pLine ) goto eoln;
if( *pLine == '#' ) pLine++, nCol++, bHash = TRUE;
// Symbol? Dann Zeichen kopieren.
if( isalpha( *pLine & 0xFF ) || *pLine == '_' )
{
// Wenn nach '_' nichts kommt, ist es ein Zeilenabschluss!
if( *pLine == '_' && !*(pLine+1) )
{ pLine++;
goto eoln; }
bSymbol = TRUE;
short n = nCol;
for ( ; (isalnum( *pLine & 0xFF ) || ( *pLine == '_' ) ); pLine++ )
nCol++;
aSym = aLine.Copy( n, nCol - n );
// Abschliessendes '_' durch Space ersetzen, wenn Zeilenende folgt
// (sonst falsche Zeilenfortsetzung)
if( !bUsedForHilite && !*pLine && *(pLine-1) == '_' )
*((sal_Unicode*)(pLine-1)) = ' '; // cast wegen const
// Typkennung?
// Das Ausrufezeichen bitte nicht testen, wenn
// danach noch ein Symbol anschliesst
else if( *pLine != '!' || !isalpha( pLine[ 1 ] & 0xFF ) )
{
SbxDataType t = GetSuffixType( *pLine );
if( t != SbxVARIANT )
{
eScanType = t;
pLine++;
nCol++;
}
}
}
// Zahl? Dann einlesen und konvertieren.
else if( isdigit( *pLine & 0xFF )
|| ( *pLine == '.' && isdigit( *(pLine+1) & 0xFF ) ) )
{
short exp = 0;
short comma = 0;
short ndig = 0;
short ncdig = 0;
eScanType = SbxDOUBLE;
BOOL bBufOverflow = FALSE;
while( strchr( "0123456789.DEde", *pLine ) && *pLine )
{
// AB 4.1.1996: Buffer voll? -> leer weiter scannen
if( (p-buf) == (BUF_SIZE-1) )
{
bBufOverflow = TRUE;
pLine++, nCol++;
continue;
}
// Komma oder Exponent?
if( *pLine == '.' )
{
if( ++comma > 1 )
{
pLine++; nCol++; continue;
}
else *p++ = *pLine++, nCol++;
}
else if( strchr( "DdEe", *pLine ) )
{
if (++exp > 1)
{
pLine++; nCol++; continue;
}
// if( toupper( *pLine ) == 'D' )
// eScanType = SbxDOUBLE;
*p++ = 'E'; pLine++; nCol++;
// Vorzeichen hinter Exponent?
if( *pLine == '+' )
pLine++, nCol++;
else
if( *pLine == '-' )
*p++ = *pLine++, nCol++;
}
else
{
*p++ = *pLine++, nCol++;
if( comma && !exp ) ncdig++;
}
if (!exp) ndig++;
}
*p = 0;
aSym = p; bNumber = TRUE;
// Komma, Exponent mehrfach vorhanden?
if( comma > 1 || exp > 1 )
{ aError = '.';
GenError( SbERR_BAD_CHAR_IN_NUMBER ); }
// #57844 Lokalisierte Funktion benutzen
int nErrno;
nVal = SolarMath::StringToDouble( buf, aEnglischIntn, nErrno );
// ATL: nVal = atof( buf );
ndig -= comma;
if( !comma && !exp )
{
if( nVal >= SbxMININT && nVal <= SbxMAXINT )
eScanType = SbxINTEGER;
else
if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
eScanType = SbxLONG;
}
if( bBufOverflow )
GenError( SbERR_MATH_OVERFLOW );
// zu viele Zahlen fuer SINGLE?
// if (ndig > 15 || ncdig > 6)
// eScanType = SbxDOUBLE;
// else
// if( nVal > SbxMAXSNG || nVal < SbxMINSNG )
// eScanType = SbxDOUBLE;
// Typkennung?
SbxDataType t = GetSuffixType( *pLine );
if( t != SbxVARIANT )
{
eScanType = t;
pLine++;
nCol++;
}
}
// Hex/Oktalzahl? Einlesen und konvertieren:
else if( *pLine == '&' )
{
pLine++; nCol++;
sal_Unicode cmp1[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
sal_Unicode cmp2[] = { '0', '1', '2', '3', '4', '5', '6', '7' };
sal_Unicode *cmp = cmp1;
//char *cmp = "0123456789ABCDEF";
sal_Unicode base = 16;
sal_Unicode ndig = 8;
sal_Unicode xch = *pLine++ & 0xFF; nCol++;
switch( toupper( xch ) )
{
case 'O':
cmp = cmp2; base = 8; ndig = 11; break;
//cmp = "01234567"; base = 8; ndig = 11; break;
case 'H':
break;
default :
// Wird als Operator angesehen
pLine--; nCol--; nCol1 = nCol-1; aSym = '&'; return SYMBOL;
}
bNumber = TRUE;
long l = 0;
int i;
BOOL bBufOverflow = FALSE;
while( isalnum( *pLine & 0xFF ) )
{
sal_Unicode ch = toupper( *pLine & 0xFF );
pLine++; nCol++;
// AB 4.1.1996: Buffer voll, leer weiter scannen
if( (p-buf) == (BUF_SIZE-1) )
bBufOverflow = TRUE;
else if( String( cmp ).Search( ch ) != STRING_NOTFOUND )
//else if( strchr( cmp, ch ) )
*p++ = ch;
else
{
aError = ch;
GenError( SbERR_BAD_CHAR_IN_NUMBER );
}
}
*p = 0;
for( p = buf; *p; p++ )
{
i = (*p & 0xFF) - '0';
if( i > 9 ) i -= 7;
l = ( l * base ) + i;
if( !ndig-- )
{
GenError( SbERR_MATH_OVERFLOW ); break;
}
}
if( *pLine == '&' ) pLine++, nCol++;
nVal = (double) l;
eScanType = ( l >= SbxMININT && l <= SbxMAXINT ) ? SbxINTEGER : SbxLONG;
if( bBufOverflow )
GenError( SbERR_MATH_OVERFLOW );
}
// Strings:
else if( *pLine == '"' || *pLine == '[' )
{
sal_Unicode cSep = *pLine;
if( cSep == '[' )
bSymbol = TRUE, cSep = ']';
short n = nCol+1;
while( *pLine )
{
do pLine++, nCol++;
while( *pLine && ( *pLine != cSep ) );
if( *pLine == cSep )
{
pLine++; nCol++;
if( *pLine != cSep || cSep == ']' ) break;
} else aError = cSep, GenError( SbERR_EXPECTED );
}
aSym = aLine.Copy( n, nCol - n - 1 );
// Doppelte Stringbegrenzer raus
String s( cSep );
s += cSep;
USHORT nIdx;
do {
nIdx = aSym.Search( s );
aSym.Erase( nIdx, 1 );
} while( nIdx != STRING_NOTFOUND );
if( cSep != ']' )
eScanType = ( cSep == '#' ) ? SbxDATE : SbxSTRING;
}
// ungueltige Zeichen:
else if( ( *pLine & 0xFF ) >= 0x7F )
{
GenError( SbERR_SYNTAX ); pLine++; nCol++;
}
// andere Gruppen:
else
{
short n = 1;
switch( *pLine++ )
{
case '<': if( *pLine == '>' || *pLine == '=' ) n = 2; break;
case '>': if( *pLine == '=' ) n = 2; break;
case ':': if( *pLine == '=' ) n = 2; break;
}
aSym = aLine.Copy( nCol, n );
pLine += n-1; nCol += n;
}
nCol2 = nCol-1;
// Kommentar?
if( eScanType != SbxSTRING &&
( aSym.GetBuffer()[0] == '\'' || aSym.EqualsIgnoreCaseAscii( "REM" ) ) )
{
aSym = String::CreateFromAscii( "REM" );
nCol2 += String( pLine ).Len();
pLine = NULL;
}
return TRUE;
// Sonst Zeilen-Ende: aber bitte auf '_' testen, ob die
// Zeile nicht weitergeht!
eoln:
if( nCol && *--pLine == '_' )
{
pLine = NULL; return NextSym();
}
else
{
pLine = NULL;
nLine = nOldLine;
nCol1 = nOldCol1;
nCol2 = nOldCol2;
aSym = '\n';
nColLock = 0;
return TRUE;
}
}